@conduit-client/service-renewable-resource-manager 3.22.0 → 3.23.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.
@@ -2,8 +2,8 @@ import type { NamedService, ServiceDescriptor } from '@conduit-client/utils';
2
2
  import type { RenewableResourceManager } from './renewable-resource-manager';
3
3
  export type { RenewableResourceManager } from './renewable-resource-manager';
4
4
  export { BasicRenewableResourceManager, type BasicRenewableResourceManagerConfig, type ResourceStorage, } from './basic-renewable-resource-manager';
5
- export type NamedRenewableResourceManagerService<Name extends string> = NamedService<Name, RenewableResourceManager<any>>;
6
- export type RenewableResourceManagerServiceDescriptor<Name extends string> = ServiceDescriptor<RenewableResourceManager<any>, Name, '1.0'>;
5
+ export type NamedRenewableResourceManagerService<Name extends string> = NamedService<Name, RenewableResourceManager<string | number | boolean>>;
6
+ export type RenewableResourceManagerServiceDescriptor<Name extends string> = ServiceDescriptor<RenewableResourceManager<string | number | boolean>, Name, '1.0'>;
7
7
  /**
8
8
  * Builds a versioned service descriptor wrapping a {@link RenewableResourceManager}.
9
9
  *
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/v1/basic-renewable-resource-manager.ts","../../src/v1/index.ts"],"sourcesContent":["import type { RenewableResourceManager } from './renewable-resource-manager';\n\n/**\n * Storage adapter supplied by the runtime. Preconditions the manager relies on\n * but cannot enforce:\n *\n * - **Durable before resolution**: the `PromiseLike` returned by `set`/`clear`\n * resolves only after the write is observable by a subsequent `get`.\n * - **Read-your-writes**: a `get` after a settled `set` observes that value.\n * - **Sole writer / monotonic replacement**: the manager is the only writer of\n * this slot. The storm-dedup in {@link BasicRenewableResourceManager.refresh}\n * treats \"stored value differs from the stale value\" as \"someone already\n * refreshed\"; a third party mutating storage out from under the manager\n * (e.g. a server-pushed value) breaks that assumption.\n */\nexport interface ResourceStorage<T> {\n get: () => PromiseLike<T | undefined>;\n set: (value: T) => PromiseLike<void>;\n clear: () => PromiseLike<void>;\n}\n\nexport interface BasicRenewableResourceManagerConfig<T extends string | number | boolean> {\n /** Fetches a fresh value from the source of truth (\"go to the network\"). */\n fetch: () => PromiseLike<T | undefined>;\n storage: ResourceStorage<T>;\n}\n\n/**\n * Configurable building block implementing {@link RenewableResourceManager}.\n *\n * The runtime supplies `fetch` and `storage`; the building block adds\n * single-flight fetch coalescing and refresh-storm deduplication. It holds no\n * opinion on storage backend, TTL, or initialization strategy — a consumer with\n * a preloaded value seeds `storage` before constructing the manager, so the\n * first `get` finds it and skips `fetch`.\n *\n * The implementation chains `PromiseLike` directly rather than using\n * `async`/`await` so a synchronous source stays synchronous and avoids a\n * microtask hop.\n */\nexport class BasicRenewableResourceManager<T extends string | number | boolean>\n implements RenewableResourceManager<T>\n{\n private inFlightFetch: PromiseLike<T | undefined> | undefined;\n\n constructor(private readonly config: BasicRenewableResourceManagerConfig<T>) {}\n\n get(): PromiseLike<T | undefined> {\n return this.config.storage\n .get()\n .then((cached) => (cached !== undefined ? cached : this.fetchAndStore()));\n }\n\n refresh(staleValue?: T): PromiseLike<T | undefined> {\n if (staleValue === undefined) {\n return this.fetchAndStore();\n }\n // Storm dedup: if storage already holds a value other than the one this\n // caller saw fail, another caller refreshed — return it, skip the network.\n return this.config.storage\n .get()\n .then((current) =>\n current !== undefined && current !== staleValue ? current : this.fetchAndStore()\n );\n }\n\n clear(): PromiseLike<void> {\n return this.config.storage.clear();\n }\n\n /**\n * Single in-flight fetch shared by all concurrent `get`/`refresh` callers.\n * Overwrites storage on a successful fetch of a defined value only; a\n * rejected fetch or a resolved-`undefined` leaves the cached value intact.\n */\n private fetchAndStore(): PromiseLike<T | undefined> {\n if (this.inFlightFetch) return this.inFlightFetch;\n\n const pending = this.config\n .fetch()\n .then((fetched) =>\n fetched === undefined\n ? fetched\n : this.config.storage.set(fetched).then(() => fetched)\n );\n\n // Assign the slot BEFORE attaching the release handler. PromiseLike\n // resolves synchronously when fetch + set are synchronous, so the\n // handler can run inline; if the assignment came after, releaseSlot\n // would see `inFlightFetch === undefined`, no-op, and the slot set on\n // the next line would leak forever.\n this.inFlightFetch = pending;\n\n const releaseSlot = () => {\n if (this.inFlightFetch === pending) this.inFlightFetch = undefined;\n };\n pending.then(releaseSlot, releaseSlot);\n\n return pending;\n }\n}\n","import type { NamedService, ServiceDescriptor } from '@conduit-client/utils';\nimport type { RenewableResourceManager } from './renewable-resource-manager';\n\nexport type { RenewableResourceManager } from './renewable-resource-manager';\nexport {\n BasicRenewableResourceManager,\n type BasicRenewableResourceManagerConfig,\n type ResourceStorage,\n} from './basic-renewable-resource-manager';\n\nexport type NamedRenewableResourceManagerService<Name extends string> = NamedService<\n Name,\n RenewableResourceManager<any>\n>;\n\nexport type RenewableResourceManagerServiceDescriptor<Name extends string> = ServiceDescriptor<\n RenewableResourceManager<any>,\n Name,\n '1.0'\n>;\n\n/**\n * Builds a versioned service descriptor wrapping a {@link RenewableResourceManager}.\n *\n * The `type` is the resource name (e.g. `'csrfToken'`), so a runtime can\n * register multiple independent managers — one per renewable resource — each\n * resolvable by name through the provisioner.\n */\nexport function buildRenewableResourceManagerDescriptor<\n Name extends string,\n T extends string | number | boolean,\n>(\n type: Name,\n service: RenewableResourceManager<T>\n): RenewableResourceManagerServiceDescriptor<Name> {\n return {\n version: '1.0' as const,\n service: service as RenewableResourceManager<any>,\n type,\n };\n}\n"],"names":[],"mappings":"AAwCO,MAAM,8BAEb;AAAA,EAGI,YAA6B,QAAgD;AAAhD,SAAA,SAAA;AAAA,EAAiD;AAAA,EAE9E,MAAkC;AAC9B,WAAO,KAAK,OAAO,QACd,IAAA,EACA,KAAK,CAAC,WAAY,WAAW,SAAY,SAAS,KAAK,eAAgB;AAAA,EAChF;AAAA,EAEA,QAAQ,YAA4C;AAChD,QAAI,eAAe,QAAW;AAC1B,aAAO,KAAK,cAAA;AAAA,IAChB;AAGA,WAAO,KAAK,OAAO,QACd,IAAA,EACA;AAAA,MAAK,CAAC,YACH,YAAY,UAAa,YAAY,aAAa,UAAU,KAAK,cAAA;AAAA,IAAc;AAAA,EAE3F;AAAA,EAEA,QAA2B;AACvB,WAAO,KAAK,OAAO,QAAQ,MAAA;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,gBAA4C;AAChD,QAAI,KAAK,cAAe,QAAO,KAAK;AAEpC,UAAM,UAAU,KAAK,OAChB,MAAA,EACA;AAAA,MAAK,CAAC,YACH,YAAY,SACN,UACA,KAAK,OAAO,QAAQ,IAAI,OAAO,EAAE,KAAK,MAAM,OAAO;AAAA,IAAA;AAQjE,SAAK,gBAAgB;AAErB,UAAM,cAAc,MAAM;AACtB,UAAI,KAAK,kBAAkB,QAAS,MAAK,gBAAgB;AAAA,IAC7D;AACA,YAAQ,KAAK,aAAa,WAAW;AAErC,WAAO;AAAA,EACX;AACJ;ACxEO,SAAS,wCAIZ,MACA,SAC+C;AAC/C,SAAO;AAAA,IACH,SAAS;AAAA,IACT;AAAA,IACA;AAAA,EAAA;AAER;"}
1
+ {"version":3,"file":"index.js","sources":["../../src/v1/basic-renewable-resource-manager.ts","../../src/v1/index.ts"],"sourcesContent":["import type { RenewableResourceManager } from './renewable-resource-manager';\n\n/**\n * Storage adapter supplied by the runtime. Preconditions the manager relies on\n * but cannot enforce:\n *\n * - **Durable before resolution**: the `PromiseLike` returned by `set`/`clear`\n * resolves only after the write is observable by a subsequent `get`.\n * - **Read-your-writes**: a `get` after a settled `set` observes that value.\n * - **Sole writer / monotonic replacement**: the manager is the only writer of\n * this slot. The storm-dedup in {@link BasicRenewableResourceManager.refresh}\n * treats \"stored value differs from the stale value\" as \"someone already\n * refreshed\"; a third party mutating storage out from under the manager\n * (e.g. a server-pushed value) breaks that assumption.\n */\nexport interface ResourceStorage<T> {\n get: () => PromiseLike<T | undefined>;\n set: (value: T) => PromiseLike<void>;\n clear: () => PromiseLike<void>;\n}\n\nexport interface BasicRenewableResourceManagerConfig<T extends string | number | boolean> {\n /** Fetches a fresh value from the source of truth (\"go to the network\"). */\n fetch: () => PromiseLike<T | undefined>;\n storage: ResourceStorage<T>;\n}\n\n/**\n * Configurable building block implementing {@link RenewableResourceManager}.\n *\n * The runtime supplies `fetch` and `storage`; the building block adds\n * single-flight fetch coalescing and refresh-storm deduplication. It holds no\n * opinion on storage backend, TTL, or initialization strategy — a consumer with\n * a preloaded value seeds `storage` before constructing the manager, so the\n * first `get` finds it and skips `fetch`.\n *\n * The implementation chains `PromiseLike` directly rather than using\n * `async`/`await` so a synchronous source stays synchronous and avoids a\n * microtask hop.\n */\nexport class BasicRenewableResourceManager<T extends string | number | boolean>\n implements RenewableResourceManager<T>\n{\n private inFlightFetch: PromiseLike<T | undefined> | undefined;\n\n constructor(private readonly config: BasicRenewableResourceManagerConfig<T>) {}\n\n get(): PromiseLike<T | undefined> {\n return this.config.storage\n .get()\n .then((cached) => (cached !== undefined ? cached : this.fetchAndStore()));\n }\n\n refresh(staleValue?: T): PromiseLike<T | undefined> {\n if (staleValue === undefined) {\n return this.fetchAndStore();\n }\n // Storm dedup: if storage already holds a value other than the one this\n // caller saw fail, another caller refreshed — return it, skip the network.\n return this.config.storage\n .get()\n .then((current) =>\n current !== undefined && current !== staleValue ? current : this.fetchAndStore()\n );\n }\n\n clear(): PromiseLike<void> {\n return this.config.storage.clear();\n }\n\n /**\n * Single in-flight fetch shared by all concurrent `get`/`refresh` callers.\n * Overwrites storage on a successful fetch of a defined value only; a\n * rejected fetch or a resolved-`undefined` leaves the cached value intact.\n */\n private fetchAndStore(): PromiseLike<T | undefined> {\n if (this.inFlightFetch) return this.inFlightFetch;\n\n const pending = this.config\n .fetch()\n .then((fetched) =>\n fetched === undefined\n ? fetched\n : this.config.storage.set(fetched).then(() => fetched)\n );\n\n // Assign the slot BEFORE attaching the release handler. PromiseLike\n // resolves synchronously when fetch + set are synchronous, so the\n // handler can run inline; if the assignment came after, releaseSlot\n // would see `inFlightFetch === undefined`, no-op, and the slot set on\n // the next line would leak forever.\n this.inFlightFetch = pending;\n\n const releaseSlot = () => {\n if (this.inFlightFetch === pending) this.inFlightFetch = undefined;\n };\n pending.then(releaseSlot, releaseSlot);\n\n return pending;\n }\n}\n","import type { NamedService, ServiceDescriptor } from '@conduit-client/utils';\nimport type { RenewableResourceManager } from './renewable-resource-manager';\n\nexport type { RenewableResourceManager } from './renewable-resource-manager';\nexport {\n BasicRenewableResourceManager,\n type BasicRenewableResourceManagerConfig,\n type ResourceStorage,\n} from './basic-renewable-resource-manager';\n\nexport type NamedRenewableResourceManagerService<Name extends string> = NamedService<\n Name,\n RenewableResourceManager<string | number | boolean>\n>;\n\nexport type RenewableResourceManagerServiceDescriptor<Name extends string> = ServiceDescriptor<\n RenewableResourceManager<string | number | boolean>,\n Name,\n '1.0'\n>;\n\n/**\n * Builds a versioned service descriptor wrapping a {@link RenewableResourceManager}.\n *\n * The `type` is the resource name (e.g. `'csrfToken'`), so a runtime can\n * register multiple independent managers — one per renewable resource — each\n * resolvable by name through the provisioner.\n */\nexport function buildRenewableResourceManagerDescriptor<\n Name extends string,\n T extends string | number | boolean,\n>(\n type: Name,\n service: RenewableResourceManager<T>\n): RenewableResourceManagerServiceDescriptor<Name> {\n return {\n version: '1.0' as const,\n service,\n type,\n };\n}\n"],"names":[],"mappings":"AAwCO,MAAM,8BAEb;AAAA,EAGI,YAA6B,QAAgD;AAAhD,SAAA,SAAA;AAAA,EAAiD;AAAA,EAE9E,MAAkC;AAC9B,WAAO,KAAK,OAAO,QACd,IAAA,EACA,KAAK,CAAC,WAAY,WAAW,SAAY,SAAS,KAAK,eAAgB;AAAA,EAChF;AAAA,EAEA,QAAQ,YAA4C;AAChD,QAAI,eAAe,QAAW;AAC1B,aAAO,KAAK,cAAA;AAAA,IAChB;AAGA,WAAO,KAAK,OAAO,QACd,IAAA,EACA;AAAA,MAAK,CAAC,YACH,YAAY,UAAa,YAAY,aAAa,UAAU,KAAK,cAAA;AAAA,IAAc;AAAA,EAE3F;AAAA,EAEA,QAA2B;AACvB,WAAO,KAAK,OAAO,QAAQ,MAAA;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,gBAA4C;AAChD,QAAI,KAAK,cAAe,QAAO,KAAK;AAEpC,UAAM,UAAU,KAAK,OAChB,MAAA,EACA;AAAA,MAAK,CAAC,YACH,YAAY,SACN,UACA,KAAK,OAAO,QAAQ,IAAI,OAAO,EAAE,KAAK,MAAM,OAAO;AAAA,IAAA;AAQjE,SAAK,gBAAgB;AAErB,UAAM,cAAc,MAAM;AACtB,UAAI,KAAK,kBAAkB,QAAS,MAAK,gBAAgB;AAAA,IAC7D;AACA,YAAQ,KAAK,aAAa,WAAW;AAErC,WAAO;AAAA,EACX;AACJ;ACxEO,SAAS,wCAIZ,MACA,SAC+C;AAC/C,SAAO;AAAA,IACH,SAAS;AAAA,IACT;AAAA,IACA;AAAA,EAAA;AAER;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@conduit-client/service-renewable-resource-manager",
3
- "version": "3.22.0",
3
+ "version": "3.23.0",
4
4
  "private": false,
5
5
  "description": "Generic renewable-resource manager service (lazy cache + single-flight fetch + refresh-storm dedup)",
6
6
  "type": "module",