Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Cache instance/class user-configurable via options.cache #615

Merged
merged 9 commits into from
Nov 28, 2023
20 changes: 20 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"typescript": "^5.0.2"
},
"dependencies": {
"@wry/caches": "^1.0.0",
"@wry/context": "^0.7.0",
"@wry/trie": "^0.4.3",
"tslib": "^2.3.0"
Expand Down
115 changes: 0 additions & 115 deletions src/cache.ts

This file was deleted.

32 changes: 26 additions & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Trie } from "@wry/trie";

import { Cache } from "./cache.js";
import { StrongCache } from "@wry/caches";
import { Entry, AnyEntry } from "./entry.js";
import { parentEntrySlot } from "./context.js";

Expand Down Expand Up @@ -88,10 +88,28 @@ export type OptimisticWrapperFunction<
makeCacheKey: (...args: TKeyArgs) => TCacheKey;
};

export interface CommonCache<K,V> {
has(key: K): boolean;
get(key: K): V | undefined;
set(key: K, value: V): V;
delete(key: K): boolean;
clean(): void;
readonly size: number;
}

export interface CommonCacheConstructor<TCacheKey, TResult, TArgs extends any[]> {
new <K extends TCacheKey, V extends Entry<TArgs, TResult>>(max?: number, dispose?: (value: V, key?: K) => void): CommonCache<K,V>;
}

type NoInfer<T> = [T][T extends any ? 0 : never];



export type OptimisticWrapOptions<
TArgs extends any[],
TKeyArgs extends any[] = TArgs,
TCacheKey = any,
TResult = any,
> = {
// The maximum number of cache entries that should be retained before the
// cache begins evicting the oldest ones.
Expand All @@ -102,13 +120,14 @@ export type OptimisticWrapOptions<
// The makeCacheKey function takes the same arguments that were passed to
// the wrapper function and returns a single value that can be used as a key
// in a Map to identify the cached result.
makeCacheKey?: (...args: TKeyArgs) => TCacheKey;
makeCacheKey?: (...args: NoInfer<TKeyArgs>) => TCacheKey;
// If provided, the subscribe function should either return an unsubscribe
// function or return nothing.
subscribe?: (...args: TArgs) => void | (() => any);
Cache?: CommonCacheConstructor<NoInfer<TCacheKey>, NoInfer<TResult>, NoInfer<TArgs>>
benjamn marked this conversation as resolved.
Show resolved Hide resolved
};

const caches = new Set<Cache<any, AnyEntry>>();
const caches = new Set<CommonCache<any, AnyEntry>>();

export function wrap<
TArgs extends any[],
Expand All @@ -117,10 +136,11 @@ export function wrap<
TCacheKey = any,
>(originalFunction: (...args: TArgs) => TResult, {
max = Math.pow(2, 16),
makeCacheKey = defaultMakeCacheKey,
makeCacheKey = (defaultMakeCacheKey as () => TCacheKey),
keyArgs,
subscribe,
}: OptimisticWrapOptions<TArgs, TKeyArgs> = Object.create(null)) {
Cache = StrongCache
}: OptimisticWrapOptions<TArgs, TKeyArgs, TCacheKey, TResult> = Object.create(null)) {
const cache = new Cache<TCacheKey, Entry<TArgs, TResult>>(
max,
entry => entry.dispose(),
Expand Down Expand Up @@ -168,7 +188,7 @@ export function wrap<

Object.defineProperty(optimistic, "size", {
get() {
return cache["map"].size;
return cache.size;
},
configurable: false,
enumerable: false,
Expand Down
36 changes: 36 additions & 0 deletions src/tests/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
wrap,
defaultMakeCacheKey,
OptimisticWrapperFunction,
CommonCache,
} from "../index";
import { wrapYieldingFiberMethods } from '@wry/context';
import { dep } from "../dep";
Expand Down Expand Up @@ -36,6 +37,41 @@ describe("optimism", function () {
assert.strictEqual(test("a"), "aNaCl");
});

it("can manually set the `Cache` implementation", () => {
let cache!: Cache<any, any>;

class Cache<K, V> implements CommonCache<K, V> {
private _cache = new Map<K, V>()
constructor() {
cache = this;
}
has = this._cache.has.bind(this._cache);
get = this._cache.get.bind(this._cache);
delete = this._cache.delete.bind(this._cache);
get size(){ return this._cache.size }
set(key: K, value: V): V {
this._cache.set(key, value);
return value;
}
clean(){};
}

const wrapped = wrap(
(obj: { value: string }) => obj.value + " transformed",
{
makeCacheKey(obj) {
return obj.value;
},
Cache,
}
);
assert.ok(cache instanceof Cache);
assert.strictEqual(wrapped({ value: "test" }), "test transformed");
assert.strictEqual(wrapped({ value: "test" }), "test transformed");
cache.get("test").value[0] = "test modified";
assert.strictEqual(wrapped({ value: "test" }), "test modified");
});

it("works with two layers of functions", function () {
const files: { [key: string]: string } = {
"a.js": "a",
Expand Down