@warlock.js/cache 5.30.0 → 5.31.0
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 +20 -8
- package/README.md +12 -21
- package/cjs/index.cjs +46 -27
- package/cjs/index.cjs.map +1 -1
- package/esm/cache-manager.d.mts +3 -1
- package/esm/cache-manager.d.mts.map +1 -1
- package/esm/cache-manager.mjs +2 -2
- package/esm/cache-manager.mjs.map +1 -1
- package/esm/drivers/base-cache-driver.d.mts +3 -1
- package/esm/drivers/base-cache-driver.d.mts.map +1 -1
- package/esm/drivers/base-cache-driver.mjs +4 -3
- package/esm/drivers/base-cache-driver.mjs.map +1 -1
- package/esm/drivers/file-cache-driver.d.mts.map +1 -1
- package/esm/drivers/file-cache-driver.mjs.map +1 -1
- package/esm/drivers/lru-memory-cache-driver.d.mts +3 -1
- package/esm/drivers/lru-memory-cache-driver.d.mts.map +1 -1
- package/esm/drivers/lru-memory-cache-driver.mjs +6 -4
- package/esm/drivers/lru-memory-cache-driver.mjs.map +1 -1
- package/esm/drivers/memory-cache-driver.d.mts +3 -1
- package/esm/drivers/memory-cache-driver.d.mts.map +1 -1
- package/esm/drivers/memory-cache-driver.mjs +9 -3
- package/esm/drivers/memory-cache-driver.mjs.map +1 -1
- package/esm/drivers/mock-cache-driver.d.mts +3 -1
- package/esm/drivers/mock-cache-driver.d.mts.map +1 -1
- package/esm/drivers/mock-cache-driver.mjs +11 -4
- package/esm/drivers/mock-cache-driver.mjs.map +1 -1
- package/esm/drivers/pg-cache-driver.d.mts +3 -1
- package/esm/drivers/pg-cache-driver.d.mts.map +1 -1
- package/esm/drivers/pg-cache-driver.mjs +6 -3
- package/esm/drivers/pg-cache-driver.mjs.map +1 -1
- package/esm/drivers/redis-cache-driver.d.mts +4 -2
- package/esm/drivers/redis-cache-driver.d.mts.map +1 -1
- package/esm/drivers/redis-cache-driver.mjs +4 -3
- package/esm/drivers/redis-cache-driver.mjs.map +1 -1
- package/esm/list/memory-cache-list.mjs.map +1 -1
- package/esm/metrics.d.mts.map +1 -1
- package/esm/metrics.mjs.map +1 -1
- package/esm/scoped-cache.d.mts +3 -1
- package/esm/scoped-cache.d.mts.map +1 -1
- package/esm/scoped-cache.mjs +2 -2
- package/esm/scoped-cache.mjs.map +1 -1
- package/esm/tagged-cache.d.mts +3 -1
- package/esm/tagged-cache.d.mts.map +1 -1
- package/esm/tagged-cache.mjs +2 -2
- package/esm/tagged-cache.mjs.map +1 -1
- package/esm/tagged-scoped-cache.d.mts +3 -1
- package/esm/tagged-scoped-cache.d.mts.map +1 -1
- package/esm/tagged-scoped-cache.mjs +2 -2
- package/esm/tagged-scoped-cache.mjs.map +1 -1
- package/esm/types.d.mts +16 -5
- package/esm/types.d.mts.map +1 -1
- package/esm/types.mjs.map +1 -1
- package/esm/utils.d.mts.map +1 -1
- package/esm/utils.mjs.map +1 -1
- package/llms-full.txt +229 -224
- package/llms.txt +1 -1
- package/package.json +3 -3
- package/skills/apply-cache-patterns/SKILL.md +3 -2
- package/skills/cache-basics/SKILL.md +24 -24
- package/skills/configure-pg-cache/SKILL.md +2 -2
- package/skills/configure-set-options/SKILL.md +14 -14
- package/skills/handle-cache-errors/SKILL.md +8 -8
- package/skills/index.md +4 -1
- package/skills/observe-cache/SKILL.md +8 -8
- package/skills/pick-cache-driver/SKILL.md +24 -23
- package/skills/test-cache-code/SKILL.md +15 -10
- package/skills/use-cache-atomic/SKILL.md +17 -11
- package/skills/use-cache-bulk/SKILL.md +8 -5
- package/skills/use-cache-list/SKILL.md +13 -13
- package/skills/use-cache-lock/SKILL.md +11 -17
- package/skills/use-cache-namespace/SKILL.md +11 -13
- package/skills/use-cache-similarity/SKILL.md +9 -9
- package/skills/use-cache-tags/SKILL.md +13 -13
- package/skills/use-cache-update-merge/SKILL.md +10 -10
- package/skills/use-cache-utils/SKILL.md +17 -17
- package/skills/use-cached-hof/SKILL.md +8 -7
- package/skills/use-swr/SKILL.md +14 -18
package/esm/scoped-cache.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scoped-cache.mjs","names":[],"sources":["../../../../../../../packages/cache/src/scoped-cache.ts"],"sourcesContent":["import { TaggedScopedCache } from \"./tagged-scoped-cache\";\nimport type {\n CacheDriver,\n CacheKey,\n CacheListAccessor,\n CacheNamespaceOptions,\n CacheSetOptions,\n CacheSimilarHit,\n CacheSimilarOptions,\n CacheSwrOptions,\n CacheTtl,\n LockOptions,\n LockOutcome,\n RememberOptions,\n ScopedCacheContract,\n TaggedScopedCacheContract,\n} from \"./types\";\nimport {\n mergeTagSets,\n normalizeToOptions,\n normalizeToRememberOptions,\n parseCacheKey,\n parseTtl,\n} from \"./utils\";\n\n/**\n * Scoped view over a cache source. Returned by `cache.namespace(prefix, options?)`.\n *\n * **Role.** A `ScopedCache` is a stateless wrapper that prepends a fixed\n * prefix to every key and applies optional default `ttl` / `tags` to every\n * write. Stores nothing itself — every call forwards to the underlying\n * `source` (typically the `CacheManager`, but any `CacheDriver` works).\n *\n * **Responsibility.**\n * - Owns: prefix-prepending of keys, normalization of nested-scope prefixes,\n * merging scope defaults into write options (`ttl`, `tags`), filtering\n * `similar()` hits to its own scope, and exposing `.clear()` as a sugar\n * for `removeNamespace(prefix)`.\n * - Does NOT own: actual storage, connection lifecycle, event listeners,\n * driver selection, or tag-index bookkeeping (delegated to the source's\n * tagged-cache machinery).\n *\n * Per-call options always win over scope defaults; tags merge additively\n * across (scope defaults + per-call) layers. Nested scopes inherit and may\n * override the parent's defaults — see {@link ScopedCache.namespace}.\n *\n * @example\n * const chat = cache.namespace(`chats.${id}`, { ttl: \"30d\" });\n *\n * await chat.set(\"messages.10\", msg); // 30d default\n * await chat.set(\"draft\", d, { ttl: \"1h\" }); // per-call override\n * await chat.namespace(\"typing\", { ttl: \"5s\" }).set(\"user.42\", true);\n * await chat.clear();\n */\nexport class ScopedCache implements ScopedCacheContract {\n /**\n * Fully-qualified prefix prepended to every key handled by this scope.\n * Normalized through {@link parseCacheKey} on construction so colon-form\n * input (`\"chats:10\"`) and trailing dots compose cleanly with nested scopes.\n */\n public readonly prefix: string;\n\n /**\n * Underlying cache source. Public-readonly so co-located helpers\n * (`TaggedScopedCache`) can delegate without ceremony — not part of the\n * stable consumer API.\n *\n * @internal\n */\n public readonly source: CacheDriver<any, any>;\n\n /**\n * Defaults applied to every write through this scope. Per-call options\n * override `ttl`; `tags` merge additively across layers.\n *\n * @internal\n */\n public readonly defaults: CacheNamespaceOptions;\n\n /**\n * Build a scope. Constructed via `cache.namespace(prefix, options)` —\n * users never call this directly.\n */\n public constructor(\n source: CacheDriver<any, any>,\n prefix: string,\n defaults: CacheNamespaceOptions = {},\n ) {\n this.source = source;\n this.prefix = parseCacheKey(prefix);\n this.defaults = {\n ttl: defaults.ttl,\n tags: defaults.tags && defaults.tags.length > 0 ? [...defaults.tags] : undefined,\n };\n }\n\n /**\n * Build a nested scope. The child's prefix is `parent.child`; child options\n * override the parent's `ttl` and union into `tags`.\n *\n * @example\n * const chat = cache.namespace(\"chats.10\", { ttl: \"30d\" });\n * const typing = chat.namespace(\"typing\", { ttl: \"5s\" });\n * // typing.prefix === \"chats.10.typing\"\n */\n public namespace(prefix: string, options: CacheNamespaceOptions = {}): ScopedCacheContract {\n const childPrefix = `${this.prefix}.${parseCacheKey(prefix)}`;\n\n return new ScopedCache(this.source, childPrefix, {\n ttl: options.ttl ?? this.defaults.ttl,\n tags: mergeTagSets(this.defaults.tags, options.tags),\n });\n }\n\n /**\n * Return a one-shot tagged write handle. The handle's tags merge additively\n * with scope-level defaults — final tag list per write is the union of\n * (scope tags + handle tags + per-call tags), deduped.\n */\n public tags(tags: string[]): TaggedScopedCacheContract {\n return new TaggedScopedCache(this, tags);\n }\n\n /**\n * Wipe every entry under this scope's prefix. Sugar over\n * `source.removeNamespace(prefix)` — siblings outside the scope are\n * untouched.\n */\n public clear(): Promise<void> {\n return this.source.removeNamespace(this.prefix);\n }\n\n /**\n * Read the value at the scoped key. Forwards to the source after prefixing.\n */\n public get<T = any>(key: CacheKey): Promise<T | null> {\n return this.source.get<T>(this.scopedKey(key));\n }\n\n /**\n * Check presence of the scoped key without fetching the value.\n */\n public has(key: CacheKey): Promise<boolean> {\n return this.source.has(this.scopedKey(key));\n }\n\n /**\n * Batch-read scoped keys. Each input key is prefixed before forwarding.\n */\n public many(keys: CacheKey[]): Promise<any[]> {\n return this.source.many(keys.map((key) => this.scopedKey(key)));\n }\n\n /**\n * Read-and-remove. Returns the value or `null`; the entry is gone after.\n */\n public pull<T = any>(key: CacheKey): Promise<T | null> {\n return this.source.pull<T>(this.scopedKey(key));\n }\n\n /**\n * Write the scoped key. Per-call `ttl`/`tags` win over scope defaults;\n * `expiresAt` is preserved as-is (absolute deadlines are never overridden\n * by the scope's relative-ttl default).\n */\n public set(\n key: CacheKey,\n value: any,\n ttlOrOptions?: CacheTtl | CacheSetOptions,\n ): Promise<any> {\n return this.source.set(this.scopedKey(key), value, this.mergeSetOptions(ttlOrOptions));\n }\n\n /**\n * Batch-write under the scope. Caller's positional `ttl` wins; otherwise\n * the scope default is parsed to seconds (since `setMany` accepts only a\n * numeric ttl).\n */\n public setMany(items: Record<string, any>, ttl?: number): Promise<void> {\n const scoped: Record<string, any> = {};\n\n for (const [key, value] of Object.entries(items)) {\n scoped[this.scopedKey(key)] = value;\n }\n\n return this.source.setMany(scoped, ttl ?? this.scopeTtlSeconds());\n }\n\n /**\n * Atomic create-or-skip on the scoped key. Throws when the underlying\n * source has no `setNX` (driver-specific — Redis-only today).\n */\n public setNX(key: CacheKey, value: any, ttl?: number): Promise<boolean> {\n if (!this.source.setNX) {\n throw new Error(\n `setNX is not supported by the underlying cache source: ${this.source.name}`,\n );\n }\n\n return this.source.setNX(this.scopedKey(key), value, ttl ?? this.scopeTtlSeconds());\n }\n\n /**\n * Permanent write (no expiration). Bypasses the scope's `ttl` default —\n * `forever` always means forever, regardless of scope policy.\n */\n public forever<T = any>(key: CacheKey, value: T): Promise<T> {\n return this.source.forever<T>(this.scopedKey(key), value);\n }\n\n /**\n * Remove a single scoped key.\n */\n public remove(key: CacheKey): Promise<void> {\n return this.source.remove(this.scopedKey(key));\n }\n\n /**\n * Read-or-compute. Cache-miss writes pick up the scope's default `ttl`\n * and `tags` unless the caller passed an options object that overrides.\n */\n public remember<T = any>(\n key: CacheKey,\n ttlOrOptions: CacheTtl | RememberOptions,\n callback: () => Promise<T>,\n ): Promise<T> {\n return this.source.remember<T>(\n this.scopedKey(key),\n this.mergeRememberOptions(ttlOrOptions),\n callback,\n );\n }\n\n /**\n * Stale-while-revalidate on the scoped key. Scope-level `tags` merge\n * additively with `options.tags`; `freshTtl`/`staleTtl` always come from\n * the caller (no scope-default precedence — the SWR shape is too\n * specific to the call site to inherit).\n */\n public swr<T = any>(\n key: CacheKey,\n options: CacheSwrOptions,\n callback: () => Promise<T>,\n ): Promise<T> {\n const merged: CacheSwrOptions = {\n ...options,\n tags: mergeTagSets(this.defaults.tags, options.tags),\n };\n\n return this.source.swr<T>(this.scopedKey(key), merged, callback);\n }\n\n /**\n * Atomic counter increment on the scoped key. TTL is preserved by the\n * underlying driver — scope ttl is only applied on first write via `set`.\n */\n public increment(key: CacheKey, value?: number): Promise<number> {\n return this.source.increment(this.scopedKey(key), value);\n }\n\n /**\n * Atomic counter decrement on the scoped key. See {@link increment} for\n * TTL semantics.\n */\n public decrement(key: CacheKey, value?: number): Promise<number> {\n return this.source.decrement(this.scopedKey(key), value);\n }\n\n /**\n * Atomic read-modify-write. An explicit `options.ttl` always wins. Otherwise\n * the scope's default `ttl` is applied only when the key does not exist yet;\n * for an existing key no ttl is passed so the source preserves the entry's\n * remaining TTL.\n */\n public async update<T = any>(\n key: CacheKey,\n fn: (current: T | null) => T | null | Promise<T | null>,\n options?: { ttl?: CacheTtl },\n ): Promise<T | null> {\n const scopedKey = this.scopedKey(key);\n\n return this.source.update<T>(scopedKey, fn, await this.writeOptions(scopedKey, options));\n }\n\n /**\n * Shallow-merge a partial object into the scoped entry. Same TTL semantics\n * as {@link update}.\n */\n public async merge<T extends Record<string, any> = Record<string, any>>(\n key: CacheKey,\n partial: Partial<T>,\n options?: { ttl?: CacheTtl },\n ): Promise<T> {\n const scopedKey = this.scopedKey(key);\n\n return this.source.merge<T>(scopedKey, partial, await this.writeOptions(scopedKey, options));\n }\n\n /**\n * Resolve the ttl options for update/merge: explicit ttl, else the scope\n * default only for keys that don't exist yet, else nothing (keep remaining).\n */\n private async writeOptions(\n scopedKey: CacheKey,\n options?: { ttl?: CacheTtl },\n ): Promise<{ ttl?: CacheTtl }> {\n if (options?.ttl !== undefined) {\n return { ttl: options.ttl };\n }\n\n if (this.defaults.ttl === undefined) {\n return {};\n }\n\n return (await this.source.has(scopedKey)) ? {} : { ttl: this.defaults.ttl };\n }\n\n /**\n * Return a list accessor bound to the scoped key. The accessor itself\n * does its own read-mutate-write under the prefixed entry.\n */\n public list<T = any>(key: CacheKey): CacheListAccessor<T> {\n return this.source.list<T>(this.scopedKey(key));\n }\n\n /**\n * Acquire a distributed lock on the scoped key. Caller's TTL wins; when\n * the options form omits `ttl`, the scope default fills in.\n */\n public lock<T>(\n key: CacheKey,\n ttlOrOptions: CacheTtl | Omit<LockOptions, \"driver\">,\n fn: () => Promise<T>,\n ): Promise<LockOutcome<T>> {\n if (typeof ttlOrOptions === \"object\" && ttlOrOptions !== null) {\n const merged: Omit<LockOptions, \"driver\"> = {\n ...ttlOrOptions,\n ttl: ttlOrOptions.ttl ?? this.defaults.ttl ?? ttlOrOptions.ttl,\n };\n\n // The third coalesce is intentional — when both caller and scope omit\n // the TTL, we still pass the original (undefined) through so the source\n // raises its own validation error rather than us swallowing it silently.\n return this.source.lock<T>(this.scopedKey(key), merged, fn);\n }\n\n return this.source.lock<T>(this.scopedKey(key), ttlOrOptions, fn);\n }\n\n /**\n * Similarity retrieval, scope-isolated. Hits whose keys fall outside this\n * scope are filtered out before the result is returned. `topK` applies to\n * the underlying retrieval — when the scope contains fewer than `topK`\n * matches but other scopes do, the caller will see fewer hits than `topK`.\n */\n public async similar<T = any>(\n vector: number[],\n options: CacheSimilarOptions,\n ): Promise<CacheSimilarHit<T>[]> {\n const hits = await this.source.similar<T>(vector, options);\n const parsedPrefix = this.source.parseKey(this.prefix);\n\n return hits.filter(\n (hit) => hit.key === parsedPrefix || hit.key.startsWith(parsedPrefix + \".\"),\n );\n }\n\n /**\n * Build the source-side key by prepending the scope prefix. Object keys\n * are normalized via {@link parseCacheKey} first so they compose with the\n * prefix as plain dot-strings.\n */\n protected scopedKey(key: CacheKey): string {\n const keyString = typeof key === \"string\" ? key : parseCacheKey(key);\n\n if (!keyString) {\n return this.prefix;\n }\n\n return `${this.prefix}.${keyString}`;\n }\n\n /**\n * Coerce the polymorphic 3rd `set` argument into a {@link CacheSetOptions}\n * with scope defaults filled in. Per-call values always win; tags merge\n * additively. `expiresAt` is preserved without injecting the scope's `ttl`\n * default (absolute deadlines override relative ones).\n */\n protected mergeSetOptions(\n input?: CacheTtl | CacheSetOptions,\n ): CacheSetOptions | undefined {\n const options = normalizeToOptions(input);\n const ttl =\n options.ttl ?? (options.expiresAt === undefined ? this.defaults.ttl : undefined);\n const tags = mergeTagSets(this.defaults.tags, options.tags);\n\n const merged: CacheSetOptions = { ...options };\n\n if (ttl !== undefined) {\n merged.ttl = ttl;\n }\n\n if (tags !== undefined) {\n merged.tags = tags;\n }\n\n return merged;\n }\n\n /**\n * Same merge as {@link mergeSetOptions} but for the `remember()` shape\n * ({@link RememberOptions} — no `expiresAt`).\n */\n protected mergeRememberOptions(\n input: CacheTtl | RememberOptions,\n ): CacheTtl | RememberOptions {\n const options = normalizeToRememberOptions(input);\n const ttl = options.ttl ?? this.defaults.ttl;\n const tags = mergeTagSets(this.defaults.tags, options.tags);\n\n const merged: RememberOptions = { ...options };\n\n if (ttl !== undefined) {\n merged.ttl = ttl;\n }\n\n if (tags !== undefined) {\n merged.tags = tags;\n }\n\n return merged;\n }\n\n /**\n * Convert the scope's default `ttl` (which may be a duration string) into\n * seconds, for the few methods (`setMany`, `setNX`) that accept only a\n * numeric ttl.\n */\n protected scopeTtlSeconds(): number | undefined {\n if (this.defaults.ttl === undefined) {\n return undefined;\n }\n\n return parseTtl(this.defaults.ttl);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"scoped-cache.mjs","names":[],"sources":["../../../../../../../packages/cache/src/scoped-cache.ts"],"sourcesContent":["import { TaggedScopedCache } from \"./tagged-scoped-cache\";\nimport type {\n CacheDriver,\n CacheKey,\n CacheListAccessor,\n CacheNamespaceOptions,\n CacheSetOptions,\n CacheSimilarHit,\n CacheSimilarOptions,\n CacheSwrOptions,\n CacheTtl,\n LockOptions,\n LockOutcome,\n RememberOptions,\n ScopedCacheContract,\n TaggedScopedCacheContract,\n} from \"./types\";\nimport {\n mergeTagSets,\n normalizeToOptions,\n normalizeToRememberOptions,\n parseCacheKey,\n parseTtl,\n} from \"./utils\";\n\n/**\n * Scoped view over a cache source. Returned by `cache.namespace(prefix, options?)`.\n *\n * **Role.** A `ScopedCache` is a stateless wrapper that prepends a fixed\n * prefix to every key and applies optional default `ttl` / `tags` to every\n * write. Stores nothing itself — every call forwards to the underlying\n * `source` (typically the `CacheManager`, but any `CacheDriver` works).\n *\n * **Responsibility.**\n * - Owns: prefix-prepending of keys, normalization of nested-scope prefixes,\n * merging scope defaults into write options (`ttl`, `tags`), filtering\n * `similar()` hits to its own scope, and exposing `.clear()` as a sugar\n * for `removeNamespace(prefix)`.\n * - Does NOT own: actual storage, connection lifecycle, event listeners,\n * driver selection, or tag-index bookkeeping (delegated to the source's\n * tagged-cache machinery).\n *\n * Per-call options always win over scope defaults; tags merge additively\n * across (scope defaults + per-call) layers. Nested scopes inherit and may\n * override the parent's defaults — see {@link ScopedCache.namespace}.\n *\n * @example\n * const chat = cache.namespace(`chats.${id}`, { ttl: \"30d\" });\n *\n * await chat.set(\"messages.10\", msg); // 30d default\n * await chat.set(\"draft\", d, { ttl: \"1h\" }); // per-call override\n * await chat.namespace(\"typing\", { ttl: \"5s\" }).set(\"user.42\", true);\n * await chat.clear();\n */\nexport class ScopedCache implements ScopedCacheContract {\n /**\n * Fully-qualified prefix prepended to every key handled by this scope.\n * Normalized through {@link parseCacheKey} on construction so colon-form\n * input (`\"chats:10\"`) and trailing dots compose cleanly with nested scopes.\n */\n public readonly prefix: string;\n\n /**\n * Underlying cache source. Public-readonly so co-located helpers\n * (`TaggedScopedCache`) can delegate without ceremony — not part of the\n * stable consumer API.\n *\n * @internal\n */\n public readonly source: CacheDriver<any, any>;\n\n /**\n * Defaults applied to every write through this scope. Per-call options\n * override `ttl`; `tags` merge additively across layers.\n *\n * @internal\n */\n public readonly defaults: CacheNamespaceOptions;\n\n /**\n * Build a scope. Constructed via `cache.namespace(prefix, options)` —\n * users never call this directly.\n */\n public constructor(\n source: CacheDriver<any, any>,\n prefix: string,\n defaults: CacheNamespaceOptions = {},\n ) {\n this.source = source;\n this.prefix = parseCacheKey(prefix);\n this.defaults = {\n ttl: defaults.ttl,\n tags: defaults.tags && defaults.tags.length > 0 ? [...defaults.tags] : undefined,\n };\n }\n\n /**\n * Build a nested scope. The child's prefix is `parent.child`; child options\n * override the parent's `ttl` and union into `tags`.\n *\n * @example\n * const chat = cache.namespace(\"chats.10\", { ttl: \"30d\" });\n * const typing = chat.namespace(\"typing\", { ttl: \"5s\" });\n * // typing.prefix === \"chats.10.typing\"\n */\n public namespace(prefix: string, options: CacheNamespaceOptions = {}): ScopedCacheContract {\n const childPrefix = `${this.prefix}.${parseCacheKey(prefix)}`;\n\n return new ScopedCache(this.source, childPrefix, {\n ttl: options.ttl ?? this.defaults.ttl,\n tags: mergeTagSets(this.defaults.tags, options.tags),\n });\n }\n\n /**\n * Return a one-shot tagged write handle. The handle's tags merge additively\n * with scope-level defaults — final tag list per write is the union of\n * (scope tags + handle tags + per-call tags), deduped.\n */\n public tags(tags: string[]): TaggedScopedCacheContract {\n return new TaggedScopedCache(this, tags);\n }\n\n /**\n * Wipe every entry under this scope's prefix. Sugar over\n * `source.removeNamespace(prefix)` — siblings outside the scope are\n * untouched.\n */\n public clear(): Promise<void> {\n return this.source.removeNamespace(this.prefix);\n }\n\n /**\n * Read the value at the scoped key. Forwards to the source after prefixing.\n */\n public get<T = any>(key: CacheKey): Promise<T | null> {\n return this.source.get<T>(this.scopedKey(key));\n }\n\n /**\n * Check presence of the scoped key without fetching the value.\n */\n public has(key: CacheKey): Promise<boolean> {\n return this.source.has(this.scopedKey(key));\n }\n\n /**\n * Batch-read scoped keys. Each input key is prefixed before forwarding.\n */\n public many(keys: CacheKey[]): Promise<any[]> {\n return this.source.many(keys.map((key) => this.scopedKey(key)));\n }\n\n /**\n * Read-and-remove. Returns the value or `null`; the entry is gone after.\n */\n public pull<T = any>(key: CacheKey): Promise<T | null> {\n return this.source.pull<T>(this.scopedKey(key));\n }\n\n /**\n * Write the scoped key. Per-call `ttl`/`tags` win over scope defaults;\n * `expiresAt` is preserved as-is (absolute deadlines are never overridden\n * by the scope's relative-ttl default).\n */\n public set(key: CacheKey, value: any, ttlOrOptions?: CacheTtl | CacheSetOptions): Promise<any> {\n return this.source.set(this.scopedKey(key), value, this.mergeSetOptions(ttlOrOptions));\n }\n\n /**\n * Batch-write under the scope. Caller's positional `ttl` wins; otherwise\n * the scope default is parsed to seconds (since `setMany` accepts only a\n * numeric ttl).\n */\n public setMany(items: Record<string, any>, ttl?: number): Promise<void> {\n const scoped: Record<string, any> = {};\n\n for (const [key, value] of Object.entries(items)) {\n scoped[this.scopedKey(key)] = value;\n }\n\n return this.source.setMany(scoped, ttl ?? this.scopeTtlSeconds());\n }\n\n /**\n * Atomic create-or-skip on the scoped key. Throws when the underlying\n * source has no `setNX` (driver-specific — Redis-only today).\n */\n public setNX(key: CacheKey, value: any, ttl?: number): Promise<boolean> {\n if (!this.source.setNX) {\n throw new Error(`setNX is not supported by the underlying cache source: ${this.source.name}`);\n }\n\n return this.source.setNX(this.scopedKey(key), value, ttl ?? this.scopeTtlSeconds());\n }\n\n /**\n * Permanent write (no expiration). Bypasses the scope's `ttl` default —\n * `forever` always means forever, regardless of scope policy.\n */\n public forever<T = any>(key: CacheKey, value: T): Promise<T> {\n return this.source.forever<T>(this.scopedKey(key), value);\n }\n\n /**\n * Remove a single scoped key.\n */\n public remove(key: CacheKey): Promise<void> {\n return this.source.remove(this.scopedKey(key));\n }\n\n /**\n * Read-or-compute. Cache-miss writes pick up the scope's default `ttl`\n * and `tags` unless the caller passed an options object that overrides.\n */\n public remember<T = any>(\n key: CacheKey,\n ttlOrOptions: CacheTtl | RememberOptions,\n callback: () => Promise<T>,\n ): Promise<T> {\n return this.source.remember<T>(\n this.scopedKey(key),\n this.mergeRememberOptions(ttlOrOptions),\n callback,\n );\n }\n\n /**\n * Stale-while-revalidate on the scoped key. Scope-level `tags` merge\n * additively with `options.tags`; `freshTtl`/`staleTtl` always come from\n * the caller (no scope-default precedence — the SWR shape is too\n * specific to the call site to inherit).\n */\n public swr<T = any>(\n key: CacheKey,\n options: CacheSwrOptions,\n callback: () => Promise<T>,\n ): Promise<T> {\n const merged: CacheSwrOptions = {\n ...options,\n tags: mergeTagSets(this.defaults.tags, options.tags),\n };\n\n return this.source.swr<T>(this.scopedKey(key), merged, callback);\n }\n\n /**\n * Atomic counter increment on the scoped key. TTL is preserved by the\n * underlying driver — scope ttl is only applied on first write via `set`.\n */\n public increment(key: CacheKey, value?: number, options?: { ttl?: CacheTtl }): Promise<number> {\n return this.source.increment(this.scopedKey(key), value, {\n ttl: options?.ttl ?? this.defaults.ttl,\n });\n }\n\n /**\n * Atomic counter decrement on the scoped key. See {@link increment} for\n * TTL semantics.\n */\n public decrement(key: CacheKey, value?: number): Promise<number> {\n return this.source.decrement(this.scopedKey(key), value);\n }\n\n /**\n * Atomic read-modify-write. An explicit `options.ttl` always wins. Otherwise\n * the scope's default `ttl` is applied only when the key does not exist yet;\n * for an existing key no ttl is passed so the source preserves the entry's\n * remaining TTL.\n */\n public async update<T = any>(\n key: CacheKey,\n fn: (current: T | null) => T | null | Promise<T | null>,\n options?: { ttl?: CacheTtl },\n ): Promise<T | null> {\n const scopedKey = this.scopedKey(key);\n\n return this.source.update<T>(scopedKey, fn, await this.writeOptions(scopedKey, options));\n }\n\n /**\n * Shallow-merge a partial object into the scoped entry. Same TTL semantics\n * as {@link update}.\n */\n public async merge<T extends Record<string, any> = Record<string, any>>(\n key: CacheKey,\n partial: Partial<T>,\n options?: { ttl?: CacheTtl },\n ): Promise<T> {\n const scopedKey = this.scopedKey(key);\n\n return this.source.merge<T>(scopedKey, partial, await this.writeOptions(scopedKey, options));\n }\n\n /**\n * Resolve the ttl options for update/merge: explicit ttl, else the scope\n * default only for keys that don't exist yet, else nothing (keep remaining).\n */\n private async writeOptions(\n scopedKey: CacheKey,\n options?: { ttl?: CacheTtl },\n ): Promise<{ ttl?: CacheTtl }> {\n if (options?.ttl !== undefined) {\n return { ttl: options.ttl };\n }\n\n if (this.defaults.ttl === undefined) {\n return {};\n }\n\n return (await this.source.has(scopedKey)) ? {} : { ttl: this.defaults.ttl };\n }\n\n /**\n * Return a list accessor bound to the scoped key. The accessor itself\n * does its own read-mutate-write under the prefixed entry.\n */\n public list<T = any>(key: CacheKey): CacheListAccessor<T> {\n return this.source.list<T>(this.scopedKey(key));\n }\n\n /**\n * Acquire a distributed lock on the scoped key. Caller's TTL wins; when\n * the options form omits `ttl`, the scope default fills in.\n */\n public lock<T>(\n key: CacheKey,\n ttlOrOptions: CacheTtl | Omit<LockOptions, \"driver\">,\n fn: () => Promise<T>,\n ): Promise<LockOutcome<T>> {\n if (typeof ttlOrOptions === \"object\" && ttlOrOptions !== null) {\n const merged: Omit<LockOptions, \"driver\"> = {\n ...ttlOrOptions,\n ttl: ttlOrOptions.ttl ?? this.defaults.ttl ?? ttlOrOptions.ttl,\n };\n\n // The third coalesce is intentional — when both caller and scope omit\n // the TTL, we still pass the original (undefined) through so the source\n // raises its own validation error rather than us swallowing it silently.\n return this.source.lock<T>(this.scopedKey(key), merged, fn);\n }\n\n return this.source.lock<T>(this.scopedKey(key), ttlOrOptions, fn);\n }\n\n /**\n * Similarity retrieval, scope-isolated. Hits whose keys fall outside this\n * scope are filtered out before the result is returned. `topK` applies to\n * the underlying retrieval — when the scope contains fewer than `topK`\n * matches but other scopes do, the caller will see fewer hits than `topK`.\n */\n public async similar<T = any>(\n vector: number[],\n options: CacheSimilarOptions,\n ): Promise<CacheSimilarHit<T>[]> {\n const hits = await this.source.similar<T>(vector, options);\n const parsedPrefix = this.source.parseKey(this.prefix);\n\n return hits.filter((hit) => hit.key === parsedPrefix || hit.key.startsWith(parsedPrefix + \".\"));\n }\n\n /**\n * Build the source-side key by prepending the scope prefix. Object keys\n * are normalized via {@link parseCacheKey} first so they compose with the\n * prefix as plain dot-strings.\n */\n protected scopedKey(key: CacheKey): string {\n const keyString = typeof key === \"string\" ? key : parseCacheKey(key);\n\n if (!keyString) {\n return this.prefix;\n }\n\n return `${this.prefix}.${keyString}`;\n }\n\n /**\n * Coerce the polymorphic 3rd `set` argument into a {@link CacheSetOptions}\n * with scope defaults filled in. Per-call values always win; tags merge\n * additively. `expiresAt` is preserved without injecting the scope's `ttl`\n * default (absolute deadlines override relative ones).\n */\n protected mergeSetOptions(input?: CacheTtl | CacheSetOptions): CacheSetOptions | undefined {\n const options = normalizeToOptions(input);\n const ttl = options.ttl ?? (options.expiresAt === undefined ? this.defaults.ttl : undefined);\n const tags = mergeTagSets(this.defaults.tags, options.tags);\n\n const merged: CacheSetOptions = { ...options };\n\n if (ttl !== undefined) {\n merged.ttl = ttl;\n }\n\n if (tags !== undefined) {\n merged.tags = tags;\n }\n\n return merged;\n }\n\n /**\n * Same merge as {@link mergeSetOptions} but for the `remember()` shape\n * ({@link RememberOptions} — no `expiresAt`).\n */\n protected mergeRememberOptions(input: CacheTtl | RememberOptions): CacheTtl | RememberOptions {\n const options = normalizeToRememberOptions(input);\n const ttl = options.ttl ?? this.defaults.ttl;\n const tags = mergeTagSets(this.defaults.tags, options.tags);\n\n const merged: RememberOptions = { ...options };\n\n if (ttl !== undefined) {\n merged.ttl = ttl;\n }\n\n if (tags !== undefined) {\n merged.tags = tags;\n }\n\n return merged;\n }\n\n /**\n * Convert the scope's default `ttl` (which may be a duration string) into\n * seconds, for the few methods (`setMany`, `setNX`) that accept only a\n * numeric ttl.\n */\n protected scopeTtlSeconds(): number | undefined {\n if (this.defaults.ttl === undefined) {\n return undefined;\n }\n\n return parseTtl(this.defaults.ttl);\n }\n}\n"],"mappings":"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"}
|
package/esm/tagged-cache.d.mts
CHANGED
|
@@ -111,7 +111,9 @@ declare class TaggedCache implements TaggedCacheDriver {
|
|
|
111
111
|
/**
|
|
112
112
|
* {@inheritdoc}
|
|
113
113
|
*/
|
|
114
|
-
increment(key: CacheKey, value?: number
|
|
114
|
+
increment(key: CacheKey, value?: number, options?: {
|
|
115
|
+
ttl?: CacheTtl;
|
|
116
|
+
}): Promise<number>;
|
|
115
117
|
/**
|
|
116
118
|
* {@inheritdoc}
|
|
117
119
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tagged-cache.d.mts","names":[],"sources":["../../../../../../../packages/cache/src/tagged-cache.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"tagged-cache.d.mts","names":[],"sources":["../../../../../../../packages/cache/src/tagged-cache.ts"],"mappings":";;;;;AAYA;;cAAa,WAAA,YAAuB,iBAAA;EAShB;;;EAAA,UALR,SAAA;EAsC6B;;;EAAA,UAjC7B,MAAA,EAAQ,WAAA;EAqGyB;;;cAhGxB,IAAA,YAAgB,MAAA,EAAQ,WAAA;EAqJF;;;EAAA,UA7I/B,MAAA,CAAO,GAAA;EA+JW;;;EAAA,UAxJZ,cAAA,CAAe,GAAA,EAAK,QAAA,GAAW,OAAA;EA2KtB;;;;;;;;;EA9JZ,oBAAA,CAAqB,GAAA,EAAK,QAAA,GAAW,OAAA;EAqP3B;;;;EAAA,0BAtOG,sBAAA;EA+PN;;;;;;EAAA,UAvPJ,QAAA,CAAS,MAAA,UAAgB,WAAA,WAAsB,OAAA;EAAA,UAgC/C,QAAA,CAAS,MAAA,UAAgB,OAAA,aAAoB,OAAA;EAAA,UAa7C,SAAA,CAAU,MAAA,WAAiB,OAAA;EAAA,UAQ3B,aAAA,CAAc,MAAA,UAAgB,OAAA,aAAoB,OAAA;EAAA,UAoBlD,aAAA,CAAc,MAAA,WAAiB,OAAA;;;;;;;;;;;YAkBrC,UAAA,CAAW,GAAA,EAAK,QAAA;EAlHa;;;EAAA,UAyHvB,aAAA,IAAiB,OAAA,CAAQ,GAAA;EAlGzB;;;EAiHH,GAAA,CACX,GAAA,EAAK,QAAA,EACL,KAAA,OACA,YAAA,GAAe,QAAA,GAAW,eAAA,GACzB,OAAA;EArFa;;;EAgGH,GAAA,CAAI,GAAA,EAAK,QAAA,GAAW,OAAA;EAnFjB;;;EA0FH,MAAA,CAAO,GAAA,EAAK,QAAA,GAAW,OAAA;EAlFN;;;EA+FjB,UAAA,IAAc,OAAA;EA3EG;;;EAAA,UA6Gd,cAAA,CAAe,UAAA,WAAqB,OAAA;EA3F/B;;;;EAqGR,KAAA,IAAS,OAAA;EA9Ef;;;EAqFM,GAAA,CAAI,GAAA,EAAK,QAAA,GAAW,OAAA;EAnFL;;;EA0Ff,QAAA,CAAS,GAAA,EAAK,QAAA,EAAU,GAAA,UAAa,QAAA,QAAgB,OAAA,QAAe,OAAA;EA9E3D;;;EA8FT,IAAA,CAAK,GAAA,EAAK,QAAA,GAAW,OAAA;EAvFT;;;EAsGZ,OAAA,CAAQ,GAAA,EAAK,QAAA,EAAU,KAAA,QAAa,OAAA;EAzFtB;;;EAgGd,SAAA,CACX,GAAA,EAAK,QAAA,EACL,KAAA,WACA,OAAA;IAAY,GAAA,GAAM,QAAA;EAAA,IACjB,OAAA;EAjDU;;;EA8DA,SAAA,CAAU,GAAA,EAAK,QAAA,EAAU,KAAA,YAAoB,OAAA;AAAA"}
|
package/esm/tagged-cache.mjs
CHANGED
|
@@ -194,8 +194,8 @@ var TaggedCache = class TaggedCache {
|
|
|
194
194
|
/**
|
|
195
195
|
* {@inheritdoc}
|
|
196
196
|
*/
|
|
197
|
-
async increment(key, value = 1) {
|
|
198
|
-
const newValue = await this.driver.increment(key, value);
|
|
197
|
+
async increment(key, value = 1, options) {
|
|
198
|
+
const newValue = await this.driver.increment(key, value, options);
|
|
199
199
|
await this.storeTaggedKey(key);
|
|
200
200
|
return newValue;
|
|
201
201
|
}
|
package/esm/tagged-cache.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tagged-cache.mjs","names":[],"sources":["../../../../../../../packages/cache/src/tagged-cache.ts"],"sourcesContent":["import type {\n CacheDriver,\n CacheKey,\n CacheSetOptions,\n CacheTtl,\n TaggedCacheDriver,\n} from \"./types\";\nimport { parseCacheKey } from \"./utils\";\n\n/** Implemented by `BaseCacheDriver`; a custom driver may not have it. */\ntype TagsInvalidatedEmitter = {\n emitTagsInvalidated(data: { tags: string[]; keys: string[]; durationMs: number }): Promise<void>;\n};\n\n/**\n * Tagged Cache Wrapper\n * Wraps a cache driver to automatically manage tag relationships\n */\nexport class TaggedCache implements TaggedCacheDriver {\n /**\n * The tags associated with this tagged cache instance\n */\n protected cacheTags: string[];\n\n /**\n * The underlying cache driver\n */\n protected driver: CacheDriver<any, any>;\n\n /**\n * Constructor\n */\n public constructor(tags: string[], driver: CacheDriver<any, any>) {\n this.cacheTags = tags;\n this.driver = driver;\n }\n\n /**\n * Get the tag key prefix for storing tag-key relationships\n */\n protected tagKey(tag: string): string {\n return `cache:tags:${tag}`;\n }\n\n /**\n * Store tag-key relationship\n */\n protected async storeTaggedKey(key: CacheKey): Promise<void> {\n await this.storeTagRelationship(key);\n }\n\n /**\n * Public alias of the tag-index writer. Called by `BaseCacheDriver.applyTags`\n * when tags are passed inline through `CacheSetOptions.tags`.\n *\n * Takes the caller's key, never the driver-parsed one — see {@link indexedKey}.\n *\n * @internal — public for cross-class use within this package; not part of the\n * stable consumer API.\n */\n public async storeTagRelationship(key: CacheKey): Promise<void> {\n const indexedKey = this.indexedKey(key);\n\n for (const tag of this.cacheTags) {\n const tagKey = this.tagKey(tag);\n\n await this.addToTag(tagKey, [indexedKey]);\n await this.pruneTag(tagKey, indexedKey);\n }\n }\n\n /**\n * Upper bound on member-existence checks per tagged write, so pruning stays\n * O(1) per write no matter how large an index grows.\n */\n protected static readonly PRUNE_CHECKS_PER_WRITE = 20;\n\n /**\n * Drop index members whose entry no longer exists (expired, evicted, or\n * removed outside a tagged handle). Checks a random window of at most\n * {@link PRUNE_CHECKS_PER_WRITE} members, so a long-lived index converges\n * to its live members without any write paying for a full scan.\n */\n protected async pruneTag(tagKey: string, justWritten: string): Promise<void> {\n const members = (await this.membersOf(tagKey)).filter((member) => member !== justWritten);\n\n if (members.length === 0) {\n return;\n }\n\n const limit = TaggedCache.PRUNE_CHECKS_PER_WRITE;\n const start = members.length > limit ? Math.floor(Math.random() * members.length) : 0;\n const window = Array.from(\n { length: Math.min(limit, members.length) },\n (_, index) => members[(start + index) % members.length]!,\n );\n\n const missing: string[] = [];\n\n for (const member of window) {\n if (!(await this.driver.has(member))) {\n missing.push(member);\n }\n }\n\n if (missing.length > 0) {\n await this.removeFromTag(tagKey, missing);\n }\n }\n\n // ------------------------------------------------------------\n // Tag-index access: the driver's primitives when it has them, else\n // the legacy get/set array (third-party drivers without primitives).\n // ------------------------------------------------------------\n\n protected async addToTag(tagKey: string, members: string[]): Promise<void> {\n if (typeof this.driver.tagAdd === \"function\") {\n return this.driver.tagAdd(tagKey, members);\n }\n\n const current = await this.legacyMembers(tagKey);\n const merged = [...new Set([...current, ...members])];\n\n if (merged.length !== current.length) {\n await this.driver.set(tagKey, merged, Infinity);\n }\n }\n\n protected async membersOf(tagKey: string): Promise<string[]> {\n if (typeof this.driver.tagMembers === \"function\") {\n return this.driver.tagMembers(tagKey);\n }\n\n return this.legacyMembers(tagKey);\n }\n\n protected async removeFromTag(tagKey: string, members: string[]): Promise<void> {\n if (typeof this.driver.tagRemove === \"function\") {\n return this.driver.tagRemove(tagKey, members);\n }\n\n const dropped = new Set(members);\n const current = await this.legacyMembers(tagKey);\n const kept = current.filter((member) => !dropped.has(member));\n\n if (kept.length === current.length) {\n return;\n }\n\n if (kept.length === 0) {\n await this.driver.remove(tagKey);\n } else {\n await this.driver.set(tagKey, kept, Infinity);\n }\n }\n\n protected async legacyMembers(tagKey: string): Promise<string[]> {\n const value = await this.driver.get(tagKey);\n\n return Array.isArray(value)\n ? value.filter((member): member is string => typeof member === \"string\")\n : [];\n }\n\n /**\n * The form a key takes inside a tag index: normalized but un-prefixed.\n *\n * The index is read back and deleted through `driver.remove(key)`, which\n * applies `globalPrefix` itself — so the index must hold the key *before*\n * the prefix, or invalidation would prefix it twice and delete nothing.\n * With a function prefix, the current prefix is re-applied at invalidation\n * time; the tag index key is resolved under that same prefix, so the two\n * agree as long as the prefix is stable for a given request/tenant.\n */\n protected indexedKey(key: CacheKey): string {\n return parseCacheKey(key);\n }\n\n /**\n * Get all keys associated with tags\n */\n protected async getTaggedKeys(): Promise<Set<string>> {\n const allKeys = new Set<string>();\n\n for (const tag of this.cacheTags) {\n for (const key of await this.membersOf(this.tagKey(tag))) {\n allKeys.add(key);\n }\n }\n\n return allKeys;\n }\n\n /**\n * {@inheritdoc}\n */\n public async set(\n key: CacheKey,\n value: any,\n ttlOrOptions?: CacheTtl | CacheSetOptions,\n ): Promise<any> {\n await this.driver.set(key, value, ttlOrOptions);\n\n await this.storeTaggedKey(key);\n\n return value;\n }\n\n /**\n * {@inheritdoc}\n */\n public async get(key: CacheKey): Promise<any | null> {\n return this.driver.get(key);\n }\n\n /**\n * {@inheritdoc}\n */\n public async remove(key: CacheKey): Promise<void> {\n const indexedKey = this.indexedKey(key);\n\n // Remove the value\n await this.driver.remove(key);\n\n // Remove from all tag relationships\n await this.detachFromTags(indexedKey);\n }\n\n /**\n * Invalidate (clear) all keys associated with the current tags\n */\n public async invalidate(): Promise<void> {\n const startedAt = performance.now();\n const removed = new Set<string>();\n\n for (const tag of this.cacheTags) {\n const tagKey = this.tagKey(tag);\n const members = await this.membersOf(tagKey);\n\n // Remove the tagged entries — indexed un-prefixed, so `remove()` prefixes once\n for (const member of members) {\n await this.driver.remove(member);\n removed.add(member);\n }\n\n // Remove exactly the members read above rather than dropping the whole\n // index: a key tagged concurrently (after the read) keeps its entry in\n // the index, so a later invalidate still reaches it.\n await this.removeFromTag(tagKey, members);\n }\n\n // Drivers built on `BaseCacheDriver` emit `invalidated`; a custom driver\n // without the method simply emits nothing.\n const driver = this.driver as Partial<TagsInvalidatedEmitter>;\n\n await driver.emitTagsInvalidated?.({\n tags: [...this.cacheTags],\n keys: [...removed],\n durationMs: performance.now() - startedAt,\n });\n }\n\n /**\n * Remove `indexedKey` from every tag of this handle.\n */\n protected async detachFromTags(indexedKey: string): Promise<void> {\n for (const tag of this.cacheTags) {\n await this.removeFromTag(this.tagKey(tag), [indexedKey]);\n }\n }\n\n /**\n * Flush all keys associated with the current tags\n * @deprecated Use invalidate() instead for better semantics\n */\n public async flush(): Promise<void> {\n return this.invalidate();\n }\n\n /**\n * {@inheritdoc}\n */\n public async has(key: CacheKey): Promise<boolean> {\n return this.driver.has(key);\n }\n\n /**\n * {@inheritdoc}\n */\n public async remember(\n key: CacheKey,\n ttl: number,\n callback: () => Promise<any>,\n ): Promise<any> {\n const value = await this.get(key);\n\n if (value !== null) {\n return value;\n }\n\n const result = await callback();\n await this.set(key, result, ttl);\n\n return result;\n }\n\n /**\n * {@inheritdoc}\n */\n public async pull(key: CacheKey): Promise<any | null> {\n // The driver's own `pull` (atomic on redis / pg / in-process drivers)\n // hands the value out once; then the key leaves this handle's tags.\n const value = await this.driver.pull(key);\n\n if (value !== null) {\n await this.detachFromTags(this.indexedKey(key));\n }\n\n return value;\n }\n\n /**\n * {@inheritdoc}\n */\n public async forever(key: CacheKey, value: any): Promise<any> {\n return this.set(key, value, Infinity);\n }\n\n /**\n * {@inheritdoc}\n */\n public async increment(key: CacheKey, value: number = 1): Promise<number> {\n // The driver's atomic increment (INCRBY / pg upsert / synchronous\n // in-process), then record the tag relationship.\n const newValue = await this.driver.increment(key, value);\n\n await this.storeTaggedKey(key);\n\n return newValue;\n }\n\n /**\n * {@inheritdoc}\n */\n public async decrement(key: CacheKey, value: number = 1): Promise<number> {\n return this.increment(key, -value);\n }\n}\n"],"mappings":";;;;;;;AAkBA,IAAa,cAAb,MAAa,YAAyC;;;;CAcpD,AAAO,YAAY,MAAgB,QAA+B;EAChE,KAAK,YAAY;EACjB,KAAK,SAAS;CAChB;;;;CAKA,AAAU,OAAO,KAAqB;EACpC,OAAO,cAAc;CACvB;;;;CAKA,MAAgB,eAAe,KAA8B;EAC3D,MAAM,KAAK,qBAAqB,GAAG;CACrC;;;;;;;;;;CAWA,MAAa,qBAAqB,KAA8B;EAC9D,MAAM,aAAa,KAAK,WAAW,GAAG;EAEtC,KAAK,MAAM,OAAO,KAAK,WAAW;GAChC,MAAM,SAAS,KAAK,OAAO,GAAG;GAE9B,MAAM,KAAK,SAAS,QAAQ,CAAC,UAAU,CAAC;GACxC,MAAM,KAAK,SAAS,QAAQ,UAAU;EACxC;CACF;;gCAMmD;;;;;;;;CAQnD,MAAgB,SAAS,QAAgB,aAAoC;EAC3E,MAAM,WAAW,MAAM,KAAK,UAAU,MAAM,EAAC,CAAE,QAAQ,WAAW,WAAW,WAAW;EAExF,IAAI,QAAQ,WAAW,GACrB;EAGF,MAAM,QAAQ,YAAY;EAC1B,MAAM,QAAQ,QAAQ,SAAS,QAAQ,KAAK,MAAM,KAAK,OAAO,IAAI,QAAQ,MAAM,IAAI;EACpF,MAAM,SAAS,MAAM,KACnB,EAAE,QAAQ,KAAK,IAAI,OAAO,QAAQ,MAAM,EAAE,IACzC,GAAG,UAAU,SAAS,QAAQ,SAAS,QAAQ,OAClD;EAEA,MAAM,UAAoB,CAAC;EAE3B,KAAK,MAAM,UAAU,QACnB,IAAI,CAAE,MAAM,KAAK,OAAO,IAAI,MAAM,GAChC,QAAQ,KAAK,MAAM;EAIvB,IAAI,QAAQ,SAAS,GACnB,MAAM,KAAK,cAAc,QAAQ,OAAO;CAE5C;CAOA,MAAgB,SAAS,QAAgB,SAAkC;EACzE,IAAI,OAAO,KAAK,OAAO,WAAW,YAChC,OAAO,KAAK,OAAO,OAAO,QAAQ,OAAO;EAG3C,MAAM,UAAU,MAAM,KAAK,cAAc,MAAM;EAC/C,MAAM,SAAS,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,SAAS,GAAG,OAAO,CAAC,CAAC;EAEpD,IAAI,OAAO,WAAW,QAAQ,QAC5B,MAAM,KAAK,OAAO,IAAI,QAAQ,QAAQ,QAAQ;CAElD;CAEA,MAAgB,UAAU,QAAmC;EAC3D,IAAI,OAAO,KAAK,OAAO,eAAe,YACpC,OAAO,KAAK,OAAO,WAAW,MAAM;EAGtC,OAAO,KAAK,cAAc,MAAM;CAClC;CAEA,MAAgB,cAAc,QAAgB,SAAkC;EAC9E,IAAI,OAAO,KAAK,OAAO,cAAc,YACnC,OAAO,KAAK,OAAO,UAAU,QAAQ,OAAO;EAG9C,MAAM,UAAU,IAAI,IAAI,OAAO;EAC/B,MAAM,UAAU,MAAM,KAAK,cAAc,MAAM;EAC/C,MAAM,OAAO,QAAQ,QAAQ,WAAW,CAAC,QAAQ,IAAI,MAAM,CAAC;EAE5D,IAAI,KAAK,WAAW,QAAQ,QAC1B;EAGF,IAAI,KAAK,WAAW,GAClB,MAAM,KAAK,OAAO,OAAO,MAAM;OAE/B,MAAM,KAAK,OAAO,IAAI,QAAQ,MAAM,QAAQ;CAEhD;CAEA,MAAgB,cAAc,QAAmC;EAC/D,MAAM,QAAQ,MAAM,KAAK,OAAO,IAAI,MAAM;EAE1C,OAAO,MAAM,QAAQ,KAAK,IACtB,MAAM,QAAQ,WAA6B,OAAO,WAAW,QAAQ,IACrE,CAAC;CACP;;;;;;;;;;;CAYA,AAAU,WAAW,KAAuB;EAC1C,OAAO,cAAc,GAAG;CAC1B;;;;CAKA,MAAgB,gBAAsC;EACpD,MAAM,0BAAU,IAAI,IAAY;EAEhC,KAAK,MAAM,OAAO,KAAK,WACrB,KAAK,MAAM,OAAO,MAAM,KAAK,UAAU,KAAK,OAAO,GAAG,CAAC,GACrD,QAAQ,IAAI,GAAG;EAInB,OAAO;CACT;;;;CAKA,MAAa,IACX,KACA,OACA,cACc;EACd,MAAM,KAAK,OAAO,IAAI,KAAK,OAAO,YAAY;EAE9C,MAAM,KAAK,eAAe,GAAG;EAE7B,OAAO;CACT;;;;CAKA,MAAa,IAAI,KAAoC;EACnD,OAAO,KAAK,OAAO,IAAI,GAAG;CAC5B;;;;CAKA,MAAa,OAAO,KAA8B;EAChD,MAAM,aAAa,KAAK,WAAW,GAAG;EAGtC,MAAM,KAAK,OAAO,OAAO,GAAG;EAG5B,MAAM,KAAK,eAAe,UAAU;CACtC;;;;CAKA,MAAa,aAA4B;EACvC,MAAM,YAAY,YAAY,IAAI;EAClC,MAAM,0BAAU,IAAI,IAAY;EAEhC,KAAK,MAAM,OAAO,KAAK,WAAW;GAChC,MAAM,SAAS,KAAK,OAAO,GAAG;GAC9B,MAAM,UAAU,MAAM,KAAK,UAAU,MAAM;GAG3C,KAAK,MAAM,UAAU,SAAS;IAC5B,MAAM,KAAK,OAAO,OAAO,MAAM;IAC/B,QAAQ,IAAI,MAAM;GACpB;GAKA,MAAM,KAAK,cAAc,QAAQ,OAAO;EAC1C;EAMA,MAFe,KAAK,OAEP,sBAAsB;GACjC,MAAM,CAAC,GAAG,KAAK,SAAS;GACxB,MAAM,CAAC,GAAG,OAAO;GACjB,YAAY,YAAY,IAAI,IAAI;EAClC,CAAC;CACH;;;;CAKA,MAAgB,eAAe,YAAmC;EAChE,KAAK,MAAM,OAAO,KAAK,WACrB,MAAM,KAAK,cAAc,KAAK,OAAO,GAAG,GAAG,CAAC,UAAU,CAAC;CAE3D;;;;;CAMA,MAAa,QAAuB;EAClC,OAAO,KAAK,WAAW;CACzB;;;;CAKA,MAAa,IAAI,KAAiC;EAChD,OAAO,KAAK,OAAO,IAAI,GAAG;CAC5B;;;;CAKA,MAAa,SACX,KACA,KACA,UACc;EACd,MAAM,QAAQ,MAAM,KAAK,IAAI,GAAG;EAEhC,IAAI,UAAU,MACZ,OAAO;EAGT,MAAM,SAAS,MAAM,SAAS;EAC9B,MAAM,KAAK,IAAI,KAAK,QAAQ,GAAG;EAE/B,OAAO;CACT;;;;CAKA,MAAa,KAAK,KAAoC;EAGpD,MAAM,QAAQ,MAAM,KAAK,OAAO,KAAK,GAAG;EAExC,IAAI,UAAU,MACZ,MAAM,KAAK,eAAe,KAAK,WAAW,GAAG,CAAC;EAGhD,OAAO;CACT;;;;CAKA,MAAa,QAAQ,KAAe,OAA0B;EAC5D,OAAO,KAAK,IAAI,KAAK,OAAO,QAAQ;CACtC;;;;CAKA,MAAa,UAAU,KAAe,QAAgB,GAAoB;EAGxE,MAAM,WAAW,MAAM,KAAK,OAAO,UAAU,KAAK,KAAK;EAEvD,MAAM,KAAK,eAAe,GAAG;EAE7B,OAAO;CACT;;;;CAKA,MAAa,UAAU,KAAe,QAAgB,GAAoB;EACxE,OAAO,KAAK,UAAU,KAAK,CAAC,KAAK;CACnC;AACF"}
|
|
1
|
+
{"version":3,"file":"tagged-cache.mjs","names":[],"sources":["../../../../../../../packages/cache/src/tagged-cache.ts"],"sourcesContent":["import type { CacheDriver, CacheKey, CacheSetOptions, CacheTtl, TaggedCacheDriver } from \"./types\";\nimport { parseCacheKey } from \"./utils\";\n\n/** Implemented by `BaseCacheDriver`; a custom driver may not have it. */\ntype TagsInvalidatedEmitter = {\n emitTagsInvalidated(data: { tags: string[]; keys: string[]; durationMs: number }): Promise<void>;\n};\n\n/**\n * Tagged Cache Wrapper\n * Wraps a cache driver to automatically manage tag relationships\n */\nexport class TaggedCache implements TaggedCacheDriver {\n /**\n * The tags associated with this tagged cache instance\n */\n protected cacheTags: string[];\n\n /**\n * The underlying cache driver\n */\n protected driver: CacheDriver<any, any>;\n\n /**\n * Constructor\n */\n public constructor(tags: string[], driver: CacheDriver<any, any>) {\n this.cacheTags = tags;\n this.driver = driver;\n }\n\n /**\n * Get the tag key prefix for storing tag-key relationships\n */\n protected tagKey(tag: string): string {\n return `cache:tags:${tag}`;\n }\n\n /**\n * Store tag-key relationship\n */\n protected async storeTaggedKey(key: CacheKey): Promise<void> {\n await this.storeTagRelationship(key);\n }\n\n /**\n * Public alias of the tag-index writer. Called by `BaseCacheDriver.applyTags`\n * when tags are passed inline through `CacheSetOptions.tags`.\n *\n * Takes the caller's key, never the driver-parsed one — see {@link indexedKey}.\n *\n * @internal — public for cross-class use within this package; not part of the\n * stable consumer API.\n */\n public async storeTagRelationship(key: CacheKey): Promise<void> {\n const indexedKey = this.indexedKey(key);\n\n for (const tag of this.cacheTags) {\n const tagKey = this.tagKey(tag);\n\n await this.addToTag(tagKey, [indexedKey]);\n await this.pruneTag(tagKey, indexedKey);\n }\n }\n\n /**\n * Upper bound on member-existence checks per tagged write, so pruning stays\n * O(1) per write no matter how large an index grows.\n */\n protected static readonly PRUNE_CHECKS_PER_WRITE = 20;\n\n /**\n * Drop index members whose entry no longer exists (expired, evicted, or\n * removed outside a tagged handle). Checks a random window of at most\n * {@link PRUNE_CHECKS_PER_WRITE} members, so a long-lived index converges\n * to its live members without any write paying for a full scan.\n */\n protected async pruneTag(tagKey: string, justWritten: string): Promise<void> {\n const members = (await this.membersOf(tagKey)).filter((member) => member !== justWritten);\n\n if (members.length === 0) {\n return;\n }\n\n const limit = TaggedCache.PRUNE_CHECKS_PER_WRITE;\n const start = members.length > limit ? Math.floor(Math.random() * members.length) : 0;\n const window = Array.from(\n { length: Math.min(limit, members.length) },\n (_, index) => members[(start + index) % members.length]!,\n );\n\n const missing: string[] = [];\n\n for (const member of window) {\n if (!(await this.driver.has(member))) {\n missing.push(member);\n }\n }\n\n if (missing.length > 0) {\n await this.removeFromTag(tagKey, missing);\n }\n }\n\n // ------------------------------------------------------------\n // Tag-index access: the driver's primitives when it has them, else\n // the legacy get/set array (third-party drivers without primitives).\n // ------------------------------------------------------------\n\n protected async addToTag(tagKey: string, members: string[]): Promise<void> {\n if (typeof this.driver.tagAdd === \"function\") {\n return this.driver.tagAdd(tagKey, members);\n }\n\n const current = await this.legacyMembers(tagKey);\n const merged = [...new Set([...current, ...members])];\n\n if (merged.length !== current.length) {\n await this.driver.set(tagKey, merged, Infinity);\n }\n }\n\n protected async membersOf(tagKey: string): Promise<string[]> {\n if (typeof this.driver.tagMembers === \"function\") {\n return this.driver.tagMembers(tagKey);\n }\n\n return this.legacyMembers(tagKey);\n }\n\n protected async removeFromTag(tagKey: string, members: string[]): Promise<void> {\n if (typeof this.driver.tagRemove === \"function\") {\n return this.driver.tagRemove(tagKey, members);\n }\n\n const dropped = new Set(members);\n const current = await this.legacyMembers(tagKey);\n const kept = current.filter((member) => !dropped.has(member));\n\n if (kept.length === current.length) {\n return;\n }\n\n if (kept.length === 0) {\n await this.driver.remove(tagKey);\n } else {\n await this.driver.set(tagKey, kept, Infinity);\n }\n }\n\n protected async legacyMembers(tagKey: string): Promise<string[]> {\n const value = await this.driver.get(tagKey);\n\n return Array.isArray(value)\n ? value.filter((member): member is string => typeof member === \"string\")\n : [];\n }\n\n /**\n * The form a key takes inside a tag index: normalized but un-prefixed.\n *\n * The index is read back and deleted through `driver.remove(key)`, which\n * applies `globalPrefix` itself — so the index must hold the key *before*\n * the prefix, or invalidation would prefix it twice and delete nothing.\n * With a function prefix, the current prefix is re-applied at invalidation\n * time; the tag index key is resolved under that same prefix, so the two\n * agree as long as the prefix is stable for a given request/tenant.\n */\n protected indexedKey(key: CacheKey): string {\n return parseCacheKey(key);\n }\n\n /**\n * Get all keys associated with tags\n */\n protected async getTaggedKeys(): Promise<Set<string>> {\n const allKeys = new Set<string>();\n\n for (const tag of this.cacheTags) {\n for (const key of await this.membersOf(this.tagKey(tag))) {\n allKeys.add(key);\n }\n }\n\n return allKeys;\n }\n\n /**\n * {@inheritdoc}\n */\n public async set(\n key: CacheKey,\n value: any,\n ttlOrOptions?: CacheTtl | CacheSetOptions,\n ): Promise<any> {\n await this.driver.set(key, value, ttlOrOptions);\n\n await this.storeTaggedKey(key);\n\n return value;\n }\n\n /**\n * {@inheritdoc}\n */\n public async get(key: CacheKey): Promise<any | null> {\n return this.driver.get(key);\n }\n\n /**\n * {@inheritdoc}\n */\n public async remove(key: CacheKey): Promise<void> {\n const indexedKey = this.indexedKey(key);\n\n // Remove the value\n await this.driver.remove(key);\n\n // Remove from all tag relationships\n await this.detachFromTags(indexedKey);\n }\n\n /**\n * Invalidate (clear) all keys associated with the current tags\n */\n public async invalidate(): Promise<void> {\n const startedAt = performance.now();\n const removed = new Set<string>();\n\n for (const tag of this.cacheTags) {\n const tagKey = this.tagKey(tag);\n const members = await this.membersOf(tagKey);\n\n // Remove the tagged entries — indexed un-prefixed, so `remove()` prefixes once\n for (const member of members) {\n await this.driver.remove(member);\n removed.add(member);\n }\n\n // Remove exactly the members read above rather than dropping the whole\n // index: a key tagged concurrently (after the read) keeps its entry in\n // the index, so a later invalidate still reaches it.\n await this.removeFromTag(tagKey, members);\n }\n\n // Drivers built on `BaseCacheDriver` emit `invalidated`; a custom driver\n // without the method simply emits nothing.\n const driver = this.driver as Partial<TagsInvalidatedEmitter>;\n\n await driver.emitTagsInvalidated?.({\n tags: [...this.cacheTags],\n keys: [...removed],\n durationMs: performance.now() - startedAt,\n });\n }\n\n /**\n * Remove `indexedKey` from every tag of this handle.\n */\n protected async detachFromTags(indexedKey: string): Promise<void> {\n for (const tag of this.cacheTags) {\n await this.removeFromTag(this.tagKey(tag), [indexedKey]);\n }\n }\n\n /**\n * Flush all keys associated with the current tags\n * @deprecated Use invalidate() instead for better semantics\n */\n public async flush(): Promise<void> {\n return this.invalidate();\n }\n\n /**\n * {@inheritdoc}\n */\n public async has(key: CacheKey): Promise<boolean> {\n return this.driver.has(key);\n }\n\n /**\n * {@inheritdoc}\n */\n public async remember(key: CacheKey, ttl: number, callback: () => Promise<any>): Promise<any> {\n const value = await this.get(key);\n\n if (value !== null) {\n return value;\n }\n\n const result = await callback();\n await this.set(key, result, ttl);\n\n return result;\n }\n\n /**\n * {@inheritdoc}\n */\n public async pull(key: CacheKey): Promise<any | null> {\n // The driver's own `pull` (atomic on redis / pg / in-process drivers)\n // hands the value out once; then the key leaves this handle's tags.\n const value = await this.driver.pull(key);\n\n if (value !== null) {\n await this.detachFromTags(this.indexedKey(key));\n }\n\n return value;\n }\n\n /**\n * {@inheritdoc}\n */\n public async forever(key: CacheKey, value: any): Promise<any> {\n return this.set(key, value, Infinity);\n }\n\n /**\n * {@inheritdoc}\n */\n public async increment(\n key: CacheKey,\n value: number = 1,\n options?: { ttl?: CacheTtl },\n ): Promise<number> {\n // The driver's atomic increment (INCRBY / pg upsert / synchronous\n // in-process), then record the tag relationship.\n const newValue = await this.driver.increment(key, value, options);\n\n await this.storeTaggedKey(key);\n\n return newValue;\n }\n\n /**\n * {@inheritdoc}\n */\n public async decrement(key: CacheKey, value: number = 1): Promise<number> {\n return this.increment(key, -value);\n }\n}\n"],"mappings":"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"}
|
|
@@ -92,7 +92,9 @@ declare class TaggedScopedCache implements TaggedScopedCacheContract {
|
|
|
92
92
|
* Atomic counter increment on the scoped key. Tags aren't applied to
|
|
93
93
|
* subsequent increments — they're attached at first-write time.
|
|
94
94
|
*/
|
|
95
|
-
increment(key: CacheKey, value?: number
|
|
95
|
+
increment(key: CacheKey, value?: number, options?: {
|
|
96
|
+
ttl?: CacheTtl;
|
|
97
|
+
}): Promise<number>;
|
|
96
98
|
/**
|
|
97
99
|
* Atomic counter decrement on the scoped key. See {@link increment}.
|
|
98
100
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tagged-scoped-cache.d.mts","names":[],"sources":["../../../../../../../packages/cache/src/tagged-scoped-cache.ts"],"mappings":";;;;;;AA4CA;;;;;;;;;;;;;;;;;;;;;;;;;;cAAa,iBAAA,YAA6B,yBAAA;
|
|
1
|
+
{"version":3,"file":"tagged-scoped-cache.d.mts","names":[],"sources":["../../../../../../../packages/cache/src/tagged-scoped-cache.ts"],"mappings":";;;;;;AA4CA;;;;;;;;;;;;;;;;;;;;;;;;;;cAAa,iBAAA,YAA6B,yBAAA;EAoIjC;;;;;EAAA,mBA9HY,KAAA,EAAO,WAAA;EAiIvB;;;;EAAA,mBA3HgB,UAAA;EA4I8B;;;;cAtI9B,KAAA,EAAO,WAAA,EAAa,UAAA;EAlB0B;;;;EA2B1D,GAAA,CAAI,GAAA,EAAK,QAAA,EAAU,KAAA,OAAY,YAAA,GAAe,QAAA,GAAW,eAAA,GAAkB,OAAA;;;;EAS3E,GAAA,UAAa,GAAA,EAAK,QAAA,GAAW,OAAA,CAAQ,CAAA;EATrC;;;EAgBA,GAAA,CAAI,GAAA,EAAK,QAAA,GAAW,OAAA;EAhB0B;;;;EAwBxC,MAAA,CAAO,GAAA,EAAK,QAAA,GAAW,OAAA;EAfzB;;;EAuBE,IAAA,UAAc,GAAA,EAAK,QAAA,GAAW,OAAA,CAAQ,CAAA;EAvBP;;;EAAA,UAoC5B,cAAA,CAAe,GAAA,EAAK,QAAA,GAAW,OAAA;EA7BpB;;;;EAgDpB,OAAA,UAAiB,GAAA,EAAK,QAAA,EAAU,KAAA,EAAO,CAAA,GAAI,OAAA,CAAQ,CAAA;EAhC7C;;;;;EA4CA,KAAA,CAAM,GAAA,EAAK,QAAA,EAAU,KAAA,OAAY,GAAA,YAAe,OAAA;EA/B7C;;;EA2DT,QAAA,UACL,GAAA,EAAK,QAAA,EACL,YAAA,EAAc,QAAA,GAAW,eAAA,EACzB,QAAA,QAAgB,OAAA,CAAQ,CAAA,IACvB,OAAA,CAAQ,CAAA;EA5CJ;;;;EAsDA,SAAA,CAAU,GAAA,EAAK,QAAA,EAAU,KAAA,WAAgB,OAAA;IAAY,GAAA,GAAM,QAAA;EAAA,IAAa,OAAA;EAtDrB;;;EA6DnD,SAAA,CAAU,GAAA,EAAK,QAAA,EAAU,KAAA,YAAiB,OAAA;EAjDf;;;;;EA0DrB,UAAA,IAAc,OAAA;EA7BzB;;;;;EAAA,UA4CQ,cAAA,CAAe,GAAA,EAAK,QAAA;AAAA"}
|
|
@@ -117,8 +117,8 @@ var TaggedScopedCache = class {
|
|
|
117
117
|
* Atomic counter increment on the scoped key. Tags aren't applied to
|
|
118
118
|
* subsequent increments — they're attached at first-write time.
|
|
119
119
|
*/
|
|
120
|
-
increment(key, value) {
|
|
121
|
-
return this.scope.increment(key, value);
|
|
120
|
+
increment(key, value, options) {
|
|
121
|
+
return this.scope.increment(key, value, options);
|
|
122
122
|
}
|
|
123
123
|
/**
|
|
124
124
|
* Atomic counter decrement on the scoped key. See {@link increment}.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tagged-scoped-cache.mjs","names":[],"sources":["../../../../../../../packages/cache/src/tagged-scoped-cache.ts"],"sourcesContent":["import type { ScopedCache } from \"./scoped-cache\";\nimport type {\n CacheKey,\n CacheSetOptions,\n CacheTtl,\n RememberOptions,\n TaggedScopedCacheContract,\n} from \"./types\";\nimport {\n injectTags,\n mergeTagSets,\n normalizeToOptions,\n normalizeToRememberOptions,\n parseCacheKey,\n} from \"./utils\";\n\n/**\n * One-shot tagged write handle on top of a {@link ScopedCache}.\n *\n * **Role.** Returned by `scope.tags([...])`. Adds a fixed list of tags to\n * every write produced through this handle, on top of any tags the parent\n * scope already contributes. Stateless except for the captured tag list.\n *\n * **Responsibility.**\n * - Owns: appending the handle's tags to writes, delegating tag-index\n * bookkeeping for `setNX` (which lacks an inline `tags` knob on the driver\n * contract), and computing the union for `invalidate()` calls.\n * - Does NOT own: storage, prefix-prepending (delegated to the scope),\n * default `ttl` (delegated to the scope), or any kind of long-lived state.\n *\n * Tags compose additively: scope tags + handle tags + per-call tags, all\n * unioned and deduped. The handle never replaces scope tags — `invalidate()`\n * always sees the full union.\n *\n * @example\n * // Inside application code — scope provides the per-user tag automatically:\n * const feed = cache.namespace(`feed.${userId}`, { tags: [`user.${userId}`] });\n *\n * await feed.tags([\"unread\"]).set(\"messages.1\", message);\n * // → tagged with [user.<id>, unread]\n *\n * await feed.tags([\"unread\"]).invalidate();\n * // → wipes everything tagged with user.<id> OR unread.\n */\nexport class TaggedScopedCache implements TaggedScopedCacheContract {\n /**\n * The {@link ScopedCache} this handle delegates to. Held by reference so\n * scope-default changes (none today, but the option is preserved for the\n * future) are visible without rebuilding the handle.\n */\n protected readonly scope: ScopedCache;\n\n /**\n * Tags this handle contributes to every write. Cloned at the call site so\n * later mutation of the input array doesn't leak in.\n */\n protected readonly handleTags: string[];\n\n /**\n * Build a tagged handle. Constructed via `scope.tags([...])` — users never\n * call this directly.\n */\n public constructor(scope: ScopedCache, handleTags: string[]) {\n this.scope = scope;\n this.handleTags = [...handleTags];\n }\n\n /**\n * Write the scoped key with the handle's tags appended to whatever the\n * caller passed. Scope-level tags are added on top by the scope itself.\n */\n public set(
|
|
1
|
+
{"version":3,"file":"tagged-scoped-cache.mjs","names":[],"sources":["../../../../../../../packages/cache/src/tagged-scoped-cache.ts"],"sourcesContent":["import type { ScopedCache } from \"./scoped-cache\";\nimport type {\n CacheKey,\n CacheSetOptions,\n CacheTtl,\n RememberOptions,\n TaggedScopedCacheContract,\n} from \"./types\";\nimport {\n injectTags,\n mergeTagSets,\n normalizeToOptions,\n normalizeToRememberOptions,\n parseCacheKey,\n} from \"./utils\";\n\n/**\n * One-shot tagged write handle on top of a {@link ScopedCache}.\n *\n * **Role.** Returned by `scope.tags([...])`. Adds a fixed list of tags to\n * every write produced through this handle, on top of any tags the parent\n * scope already contributes. Stateless except for the captured tag list.\n *\n * **Responsibility.**\n * - Owns: appending the handle's tags to writes, delegating tag-index\n * bookkeeping for `setNX` (which lacks an inline `tags` knob on the driver\n * contract), and computing the union for `invalidate()` calls.\n * - Does NOT own: storage, prefix-prepending (delegated to the scope),\n * default `ttl` (delegated to the scope), or any kind of long-lived state.\n *\n * Tags compose additively: scope tags + handle tags + per-call tags, all\n * unioned and deduped. The handle never replaces scope tags — `invalidate()`\n * always sees the full union.\n *\n * @example\n * // Inside application code — scope provides the per-user tag automatically:\n * const feed = cache.namespace(`feed.${userId}`, { tags: [`user.${userId}`] });\n *\n * await feed.tags([\"unread\"]).set(\"messages.1\", message);\n * // → tagged with [user.<id>, unread]\n *\n * await feed.tags([\"unread\"]).invalidate();\n * // → wipes everything tagged with user.<id> OR unread.\n */\nexport class TaggedScopedCache implements TaggedScopedCacheContract {\n /**\n * The {@link ScopedCache} this handle delegates to. Held by reference so\n * scope-default changes (none today, but the option is preserved for the\n * future) are visible without rebuilding the handle.\n */\n protected readonly scope: ScopedCache;\n\n /**\n * Tags this handle contributes to every write. Cloned at the call site so\n * later mutation of the input array doesn't leak in.\n */\n protected readonly handleTags: string[];\n\n /**\n * Build a tagged handle. Constructed via `scope.tags([...])` — users never\n * call this directly.\n */\n public constructor(scope: ScopedCache, handleTags: string[]) {\n this.scope = scope;\n this.handleTags = [...handleTags];\n }\n\n /**\n * Write the scoped key with the handle's tags appended to whatever the\n * caller passed. Scope-level tags are added on top by the scope itself.\n */\n public set(key: CacheKey, value: any, ttlOrOptions?: CacheTtl | CacheSetOptions): Promise<any> {\n const options = injectTags(normalizeToOptions(ttlOrOptions), this.handleTags);\n\n return this.scope.set(key, value, options);\n }\n\n /**\n * Read the scoped key. Tags don't affect reads — pass-through.\n */\n public get<T = any>(key: CacheKey): Promise<T | null> {\n return this.scope.get<T>(key);\n }\n\n /**\n * Check presence of the scoped key.\n */\n public has(key: CacheKey): Promise<boolean> {\n return this.scope.has(key);\n }\n\n /**\n * Remove the scoped key and take it out of every tag index this handle\n * writes to (scope tags + handle tags).\n */\n public async remove(key: CacheKey): Promise<void> {\n await this.scope.remove(key);\n await this.detachFromTags(key);\n }\n\n /**\n * Read-and-remove the scoped key, then take it out of this handle's tags.\n */\n public async pull<T = any>(key: CacheKey): Promise<T | null> {\n const value = await this.scope.pull<T>(key);\n\n if (value !== null) {\n await this.detachFromTags(key);\n }\n\n return value;\n }\n\n /**\n * Remove the un-prefixed scoped key from every tag index of this handle.\n */\n protected async detachFromTags(key: CacheKey): Promise<void> {\n const allTags = mergeTagSets(this.scope.defaults.tags, this.handleTags);\n\n if (!allTags || allTags.length === 0) {\n return;\n }\n\n const scopedKey = this.buildScopedKey(key);\n const tagged = this.scope.source.tags(allTags) as unknown as {\n detachFromTags: (indexedKey: string) => Promise<void>;\n };\n\n await tagged.detachFromTags(parseCacheKey(scopedKey));\n }\n\n /**\n * Permanent write with handle tags applied. Bypasses both the scope's and\n * the caller's TTL (forever means forever) — only tags get injected.\n */\n public forever<T = any>(key: CacheKey, value: T): Promise<T> {\n return this.scope.set(key, value, {\n ttl: Infinity,\n tags: this.handleTags,\n }) as Promise<T>;\n }\n\n /**\n * Atomic create-or-skip with the handle's tags applied on success. The\n * driver contract has no inline `tags` knob on `setNX`, so we register the\n * tag relationship manually after a successful write.\n */\n public async setNX(key: CacheKey, value: any, ttl?: number): Promise<boolean> {\n const wasSet = await this.scope.setNX(key, value, ttl);\n\n if (!wasSet) {\n return false;\n }\n\n const allTags = mergeTagSets(this.scope.defaults.tags, this.handleTags);\n\n if (!allTags || allTags.length === 0) {\n return true;\n }\n\n // Register the un-prefixed scoped key — the tag index never holds the\n // driver-parsed form, so `invalidate()` can remove it via `remove(key)`.\n const scopedKey = this.buildScopedKey(key);\n const tagged = this.scope.source.tags(allTags) as unknown as {\n storeTagRelationship: (key: CacheKey) => Promise<void>;\n };\n\n await tagged.storeTagRelationship(scopedKey);\n\n return true;\n }\n\n /**\n * Read-or-compute with handle tags appended on the cache-miss write.\n */\n public remember<T = any>(\n key: CacheKey,\n ttlOrOptions: CacheTtl | RememberOptions,\n callback: () => Promise<T>,\n ): Promise<T> {\n const options = injectTags(normalizeToRememberOptions(ttlOrOptions), this.handleTags);\n\n return this.scope.remember<T>(key, options, callback);\n }\n\n /**\n * Atomic counter increment on the scoped key. Tags aren't applied to\n * subsequent increments — they're attached at first-write time.\n */\n public increment(key: CacheKey, value?: number, options?: { ttl?: CacheTtl }): Promise<number> {\n return this.scope.increment(key, value, options);\n }\n\n /**\n * Atomic counter decrement on the scoped key. See {@link increment}.\n */\n public decrement(key: CacheKey, value?: number): Promise<number> {\n return this.scope.decrement(key, value);\n }\n\n /**\n * Wipe every entry tagged with the union of (scope tags + handle tags).\n * Tags are global across the package, so this reaches outside the scope's\n * prefix when scope tags are also used elsewhere.\n */\n public async invalidate(): Promise<void> {\n const allTags = mergeTagSets(this.scope.defaults.tags, this.handleTags);\n\n if (!allTags || allTags.length === 0) {\n return;\n }\n\n await this.scope.source.tags(allTags).invalidate();\n }\n\n /**\n * Compute the source-side key the same way `ScopedCache.scopedKey` does —\n * needed for `setNX`, where we have to register the tag relationship by\n * hand because the driver contract doesn't accept inline tags there.\n */\n protected buildScopedKey(key: CacheKey): string {\n const keyString = typeof key === \"string\" ? key : parseCacheKey(key);\n\n if (!keyString) {\n return this.scope.prefix;\n }\n\n return `${this.scope.prefix}.${keyString}`;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CA,IAAa,oBAAb,MAAoE;;;;;CAkBlE,AAAO,YAAY,OAAoB,YAAsB;EAC3D,KAAK,QAAQ;EACb,KAAK,aAAa,CAAC,GAAG,UAAU;CAClC;;;;;CAMA,AAAO,IAAI,KAAe,OAAY,cAAyD;EAC7F,MAAM,UAAU,WAAW,mBAAmB,YAAY,GAAG,KAAK,UAAU;EAE5E,OAAO,KAAK,MAAM,IAAI,KAAK,OAAO,OAAO;CAC3C;;;;CAKA,AAAO,IAAa,KAAkC;EACpD,OAAO,KAAK,MAAM,IAAO,GAAG;CAC9B;;;;CAKA,AAAO,IAAI,KAAiC;EAC1C,OAAO,KAAK,MAAM,IAAI,GAAG;CAC3B;;;;;CAMA,MAAa,OAAO,KAA8B;EAChD,MAAM,KAAK,MAAM,OAAO,GAAG;EAC3B,MAAM,KAAK,eAAe,GAAG;CAC/B;;;;CAKA,MAAa,KAAc,KAAkC;EAC3D,MAAM,QAAQ,MAAM,KAAK,MAAM,KAAQ,GAAG;EAE1C,IAAI,UAAU,MACZ,MAAM,KAAK,eAAe,GAAG;EAG/B,OAAO;CACT;;;;CAKA,MAAgB,eAAe,KAA8B;EAC3D,MAAM,UAAU,aAAa,KAAK,MAAM,SAAS,MAAM,KAAK,UAAU;EAEtE,IAAI,CAAC,WAAW,QAAQ,WAAW,GACjC;EAGF,MAAM,YAAY,KAAK,eAAe,GAAG;EAKzC,MAJe,KAAK,MAAM,OAAO,KAAK,OAI3B,CAAC,CAAC,eAAe,cAAc,SAAS,CAAC;CACtD;;;;;CAMA,AAAO,QAAiB,KAAe,OAAsB;EAC3D,OAAO,KAAK,MAAM,IAAI,KAAK,OAAO;GAChC,KAAK;GACL,MAAM,KAAK;EACb,CAAC;CACH;;;;;;CAOA,MAAa,MAAM,KAAe,OAAY,KAAgC;EAG5E,IAAI,CAAC,MAFgB,KAAK,MAAM,MAAM,KAAK,OAAO,GAAG,GAGnD,OAAO;EAGT,MAAM,UAAU,aAAa,KAAK,MAAM,SAAS,MAAM,KAAK,UAAU;EAEtE,IAAI,CAAC,WAAW,QAAQ,WAAW,GACjC,OAAO;EAKT,MAAM,YAAY,KAAK,eAAe,GAAG;EAKzC,MAJe,KAAK,MAAM,OAAO,KAAK,OAI3B,CAAC,CAAC,qBAAqB,SAAS;EAE3C,OAAO;CACT;;;;CAKA,AAAO,SACL,KACA,cACA,UACY;EACZ,MAAM,UAAU,WAAW,2BAA2B,YAAY,GAAG,KAAK,UAAU;EAEpF,OAAO,KAAK,MAAM,SAAY,KAAK,SAAS,QAAQ;CACtD;;;;;CAMA,AAAO,UAAU,KAAe,OAAgB,SAA+C;EAC7F,OAAO,KAAK,MAAM,UAAU,KAAK,OAAO,OAAO;CACjD;;;;CAKA,AAAO,UAAU,KAAe,OAAiC;EAC/D,OAAO,KAAK,MAAM,UAAU,KAAK,KAAK;CACxC;;;;;;CAOA,MAAa,aAA4B;EACvC,MAAM,UAAU,aAAa,KAAK,MAAM,SAAS,MAAM,KAAK,UAAU;EAEtE,IAAI,CAAC,WAAW,QAAQ,WAAW,GACjC;EAGF,MAAM,KAAK,MAAM,OAAO,KAAK,OAAO,CAAC,CAAC,WAAW;CACnD;;;;;;CAOA,AAAU,eAAe,KAAuB;EAC9C,MAAM,YAAY,OAAO,QAAQ,WAAW,MAAM,cAAc,GAAG;EAEnE,IAAI,CAAC,WACH,OAAO,KAAK,MAAM;EAGpB,OAAO,GAAG,KAAK,MAAM,OAAO,GAAG;CACjC;AACF"}
|
package/esm/types.d.mts
CHANGED
|
@@ -429,7 +429,9 @@ interface TaggedCacheDriver {
|
|
|
429
429
|
/**
|
|
430
430
|
* Increment with tags
|
|
431
431
|
*/
|
|
432
|
-
increment(key: CacheKey, value?: number
|
|
432
|
+
increment(key: CacheKey, value?: number, options?: {
|
|
433
|
+
ttl?: CacheTtl;
|
|
434
|
+
}): Promise<number>;
|
|
433
435
|
/**
|
|
434
436
|
* Decrement with tags
|
|
435
437
|
*/
|
|
@@ -832,7 +834,10 @@ interface CacheDriver<ClientType, Options> {
|
|
|
832
834
|
lock<T>(key: CacheKey, ttlOrOptions: CacheTtl | Omit<LockOptions, "driver">, fn: () => Promise<T>): Promise<LockOutcome<T>>;
|
|
833
835
|
/**
|
|
834
836
|
* Increment a numeric value in cache. A missing key starts from 0 with the
|
|
835
|
-
* driver default
|
|
837
|
+
* per-call `options.ttl` (or driver default); an existing expiry is preserved.
|
|
838
|
+
* An explicit TTL also repairs a live counter with no expiry.
|
|
839
|
+
* Supply the TTL on every call for expiring counters: a separate seed may
|
|
840
|
+
* expire before the atomic increment recreates the key.
|
|
836
841
|
*
|
|
837
842
|
* Atomicity: across servers on `redis` (`INCRBY`) and `pg` (single
|
|
838
843
|
* upsert statement); within the process on `memory`, `memoryExtended`,
|
|
@@ -841,7 +846,9 @@ interface CacheDriver<ClientType, Options> {
|
|
|
841
846
|
* @param key The cache key
|
|
842
847
|
* @param value The value to increment by (default 1)
|
|
843
848
|
*/
|
|
844
|
-
increment(key: CacheKey, value?: number
|
|
849
|
+
increment(key: CacheKey, value?: number, options?: {
|
|
850
|
+
ttl?: CacheTtl;
|
|
851
|
+
}): Promise<number>;
|
|
845
852
|
/**
|
|
846
853
|
* Decrement a numeric value in cache
|
|
847
854
|
*
|
|
@@ -978,7 +985,9 @@ interface TaggedScopedCacheContract {
|
|
|
978
985
|
forever<T = any>(key: CacheKey, value: T): Promise<T>;
|
|
979
986
|
setNX(key: CacheKey, value: any, ttl?: number): Promise<boolean>;
|
|
980
987
|
remember<T = any>(key: CacheKey, ttlOrOptions: CacheTtl | RememberOptions, callback: () => Promise<T>): Promise<T>;
|
|
981
|
-
increment(key: CacheKey, value?: number
|
|
988
|
+
increment(key: CacheKey, value?: number, options?: {
|
|
989
|
+
ttl?: CacheTtl;
|
|
990
|
+
}): Promise<number>;
|
|
982
991
|
decrement(key: CacheKey, value?: number): Promise<number>;
|
|
983
992
|
/**
|
|
984
993
|
* Invalidate every entry tagged with the union of scope tags + handle tags.
|
|
@@ -1028,7 +1037,9 @@ interface ScopedCacheContract {
|
|
|
1028
1037
|
remove(key: CacheKey): Promise<void>;
|
|
1029
1038
|
remember<T = any>(key: CacheKey, ttlOrOptions: CacheTtl | RememberOptions, callback: () => Promise<T>): Promise<T>;
|
|
1030
1039
|
swr<T = any>(key: CacheKey, options: CacheSwrOptions, callback: () => Promise<T>): Promise<T>;
|
|
1031
|
-
increment(key: CacheKey, value?: number
|
|
1040
|
+
increment(key: CacheKey, value?: number, options?: {
|
|
1041
|
+
ttl?: CacheTtl;
|
|
1042
|
+
}): Promise<number>;
|
|
1032
1043
|
decrement(key: CacheKey, value?: number): Promise<number>;
|
|
1033
1044
|
update<T = any>(key: CacheKey, fn: (current: T | null) => T | null | Promise<T | null>, options?: {
|
|
1034
1045
|
ttl?: CacheTtl;
|
package/esm/types.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../../packages/cache/src/types.ts"],"mappings":";;;;;;;;;;;;;;;;cAiBa,UAAA,SAAmB,KAAK;cAChB,OAAA;AAAA;;;;cASR,oBAAA,SAA6B,UAAU;cAC/B,OAAA;AAAA;;;;cASR,uBAAA,SAAgC,UAAU;cAClC,OAAA;AAAA;;AApBe;AASpC;cAoBa,8BAAA,SAAuC,UAAU;cAE1D,OAAA;AAAA;;;;;AArBgC;AASpC;cAyBa,qBAAA,SAA8B,UAAU;cAChC,OAAA;AAAA;;;;cASR,qBAAA,SAA8B,UAAU;cAChC,OAAA;AAAA;;;;;;;;KAaT,QAAA;AAxBZ;;;;;;;AAAA,KAiCY,mBAAA;AAhCwB;AASpC;;AAToC,KAqCxB,cAAA;EA5ByC;;;EAgCnD,MAAA;EA/BkC;AAAA;AAapC;;EAuBE,QAAA,EAAU,CAAC;AAAA;AAvBO;AASpB;;;;AAA+B;AAK/B;;;;AAdoB,KAoCR,WAAA;EAlBV;;;;EAuBA,GAAA,EAAK,QAAQ;EALH;;;;EAUV,KAAA;EALK;;;EASL,MAAA;AAAA;AAgBF;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../../packages/cache/src/types.ts"],"mappings":"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"}
|