@classytic/repo-core 0.26.1 → 0.27.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1014 -983
- package/dist/cache/engine.mjs +3 -1
- package/dist/cache/index.d.mts +2 -1
- package/dist/cache/index.mjs +2 -1
- package/dist/cache/memo.d.mts +30 -0
- package/dist/cache/memo.mjs +47 -0
- package/package.json +1 -1
package/dist/cache/engine.mjs
CHANGED
|
@@ -140,7 +140,9 @@ var CacheEngine = class {
|
|
|
140
140
|
status: "wait",
|
|
141
141
|
promise: existing.promise
|
|
142
142
|
};
|
|
143
|
-
|
|
143
|
+
const deferred = Promise.withResolvers();
|
|
144
|
+
deferred.promise.catch(() => {});
|
|
145
|
+
this.pending.set(key, deferred);
|
|
144
146
|
return { status: "claimed" };
|
|
145
147
|
}
|
|
146
148
|
/** Resolve an in-flight claim with the fresh result + clear it. */
|
package/dist/cache/index.d.mts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { CacheOptions, CacheReadResult } from "./options.mjs";
|
|
2
2
|
import { CacheAdapter } from "./types.mjs";
|
|
3
3
|
import { CacheAdapterResolver, CacheAdapterSource, CacheEngine, CacheEngineOptions, SingleFlightClaim } from "./engine.mjs";
|
|
4
|
+
import { TtlMemo, TtlMemoOptions, createTtlMemo } from "./memo.mjs";
|
|
4
5
|
import { MemoryCacheAdapterOptions, createMemoryCacheAdapter } from "./memory-adapter.mjs";
|
|
5
6
|
import { DEFAULT_SHAPE_KEYS_BY_OP } from "./plugin/context.mjs";
|
|
6
7
|
import { DEFAULT_CACHEABLE_OPS, DEFAULT_INVALIDATING_OPS, LogCallbacks, RepositoryCacheHandle, RepositoryCachePluginOptions, cachePlugin } from "./plugin/index.mjs";
|
|
7
8
|
import { scheduleBackground } from "./runtime.mjs";
|
|
8
9
|
import { CacheTimeoutError, TimeoutAdapterOptions, withTimeout } from "./timeout-adapter.mjs";
|
|
9
|
-
export { type CacheAdapter, type CacheAdapterResolver, type CacheAdapterSource, CacheEngine, type CacheEngineOptions, type CacheOptions, type CacheReadResult, CacheTimeoutError, DEFAULT_CACHEABLE_OPS, DEFAULT_INVALIDATING_OPS, DEFAULT_SHAPE_KEYS_BY_OP, type LogCallbacks, type MemoryCacheAdapterOptions, type RepositoryCacheHandle, type RepositoryCachePluginOptions, type SingleFlightClaim, type TimeoutAdapterOptions, cachePlugin, createMemoryCacheAdapter, scheduleBackground, withTimeout };
|
|
10
|
+
export { type CacheAdapter, type CacheAdapterResolver, type CacheAdapterSource, CacheEngine, type CacheEngineOptions, type CacheOptions, type CacheReadResult, CacheTimeoutError, DEFAULT_CACHEABLE_OPS, DEFAULT_INVALIDATING_OPS, DEFAULT_SHAPE_KEYS_BY_OP, type LogCallbacks, type MemoryCacheAdapterOptions, type RepositoryCacheHandle, type RepositoryCachePluginOptions, type SingleFlightClaim, type TimeoutAdapterOptions, type TtlMemo, type TtlMemoOptions, cachePlugin, createMemoryCacheAdapter, createTtlMemo, scheduleBackground, withTimeout };
|
package/dist/cache/index.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { CacheEngine } from "./engine.mjs";
|
|
2
2
|
import { createMemoryCacheAdapter } from "./memory-adapter.mjs";
|
|
3
|
+
import { createTtlMemo } from "./memo.mjs";
|
|
3
4
|
import { DEFAULT_SHAPE_KEYS_BY_OP } from "./plugin/context.mjs";
|
|
4
5
|
import { scheduleBackground } from "./runtime.mjs";
|
|
5
6
|
import { DEFAULT_CACHEABLE_OPS, DEFAULT_INVALIDATING_OPS, cachePlugin } from "./plugin/index.mjs";
|
|
6
7
|
import { CacheTimeoutError, withTimeout } from "./timeout-adapter.mjs";
|
|
7
|
-
export { CacheEngine, CacheTimeoutError, DEFAULT_CACHEABLE_OPS, DEFAULT_INVALIDATING_OPS, DEFAULT_SHAPE_KEYS_BY_OP, cachePlugin, createMemoryCacheAdapter, scheduleBackground, withTimeout };
|
|
8
|
+
export { CacheEngine, CacheTimeoutError, DEFAULT_CACHEABLE_OPS, DEFAULT_INVALIDATING_OPS, DEFAULT_SHAPE_KEYS_BY_OP, cachePlugin, createMemoryCacheAdapter, createTtlMemo, scheduleBackground, withTimeout };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//#region src/cache/memo.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* `createTtlMemo` — memoise a keyed async load for `ttlMs`, single-flight.
|
|
4
|
+
*
|
|
5
|
+
* Use this instead of a `Map<key, { at, value }>`. Every hand-rolled one wrote
|
|
6
|
+
* its entry AFTER the await, so N concurrent callers all missed and the cache
|
|
7
|
+
* only ever helped the caller after them. This is `CacheEngine.prefetch` over
|
|
8
|
+
* the memory adapter: concurrent misses share one load, a thrown load caches
|
|
9
|
+
* nothing, and a `null` result is an answer.
|
|
10
|
+
*/
|
|
11
|
+
interface TtlMemoOptions<K> {
|
|
12
|
+
/** Freshness window. `0` disables memoisation — every call loads. */
|
|
13
|
+
readonly ttlMs: number;
|
|
14
|
+
/**
|
|
15
|
+
* Key derivation. REQUIRED for non-primitive keys: an object key would be
|
|
16
|
+
* identity-compared, never hit, and read as a caching bug. Fold anything that
|
|
17
|
+
* changes the answer into it (a revision counter, a tenant).
|
|
18
|
+
*/
|
|
19
|
+
readonly keyOf?: (key: K) => string;
|
|
20
|
+
/** LRU ceiling. Default 10,000. */
|
|
21
|
+
readonly maxEntries?: number;
|
|
22
|
+
}
|
|
23
|
+
interface TtlMemo<K, V> {
|
|
24
|
+
get(key: K): Promise<V>;
|
|
25
|
+
/** Drop one key, or everything. */
|
|
26
|
+
invalidate(key?: K): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
declare function createTtlMemo<K, V>(load: (key: K) => Promise<V>, options: TtlMemoOptions<K>): TtlMemo<K, V>;
|
|
29
|
+
//#endregion
|
|
30
|
+
export { TtlMemo, TtlMemoOptions, createTtlMemo };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { CacheEngine } from "./engine.mjs";
|
|
2
|
+
import { createMemoryCacheAdapter } from "./memory-adapter.mjs";
|
|
3
|
+
//#region src/cache/memo.ts
|
|
4
|
+
/**
|
|
5
|
+
* `createTtlMemo` — memoise a keyed async load for `ttlMs`, single-flight.
|
|
6
|
+
*
|
|
7
|
+
* Use this instead of a `Map<key, { at, value }>`. Every hand-rolled one wrote
|
|
8
|
+
* its entry AFTER the await, so N concurrent callers all missed and the cache
|
|
9
|
+
* only ever helped the caller after them. This is `CacheEngine.prefetch` over
|
|
10
|
+
* the memory adapter: concurrent misses share one load, a thrown load caches
|
|
11
|
+
* nothing, and a `null` result is an answer.
|
|
12
|
+
*/
|
|
13
|
+
const PREFIX = "memo";
|
|
14
|
+
function createTtlMemo(load, options) {
|
|
15
|
+
const { ttlMs, keyOf, maxEntries } = options;
|
|
16
|
+
const adapter = createMemoryCacheAdapter({ maxEntries: maxEntries ?? 1e4 });
|
|
17
|
+
const engine = new CacheEngine(adapter, { prefix: PREFIX });
|
|
18
|
+
const resolved = {
|
|
19
|
+
staleTime: Math.max(0, ttlMs) / 1e3,
|
|
20
|
+
gcTime: 0,
|
|
21
|
+
tags: [],
|
|
22
|
+
bypass: false,
|
|
23
|
+
swr: false,
|
|
24
|
+
enabled: ttlMs > 0
|
|
25
|
+
};
|
|
26
|
+
const toKey = (key) => {
|
|
27
|
+
if (keyOf) return `${PREFIX}:${keyOf(key)}`;
|
|
28
|
+
const t = typeof key;
|
|
29
|
+
if (t === "string" || t === "number" || t === "boolean") return `${PREFIX}:${String(key)}`;
|
|
30
|
+
throw new TypeError(`createTtlMemo: a ${key === null ? "null" : t} key needs keyOf(...) — an identity-compared key never hits`);
|
|
31
|
+
};
|
|
32
|
+
return {
|
|
33
|
+
get(key) {
|
|
34
|
+
const k = toKey(key);
|
|
35
|
+
return engine.prefetch(k, resolved, () => load(key));
|
|
36
|
+
},
|
|
37
|
+
async invalidate(key) {
|
|
38
|
+
if (key === void 0) {
|
|
39
|
+
await engine.clear();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
await adapter.delete(toKey(key));
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
export { createTtlMemo };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@classytic/repo-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.1",
|
|
4
4
|
"description": "Driver-agnostic repository primitives: hooks, Filter IR, operations, pagination, cache contract. Foundation for mongokit, sqlitekit, pgkit, and prismakit. Lean by design — no plugins ship here; each kit owns its own.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|