@xyo-network/schema-cache 7.0.0 → 7.0.3

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.
@@ -13,14 +13,17 @@ export declare class SchemaCache<T extends SchemaNameToValidatorMap = SchemaName
13
13
  */
14
14
  protected static readonly NULL: SchemaCacheEntry;
15
15
  private static _instance?;
16
- onSchemaCached?: (name: string, entry: SchemaCacheEntry) => void;
17
- proxy?: string;
18
16
  private _cache;
19
17
  private _resolutions;
20
18
  private _validators;
21
19
  private getDebounce;
20
+ onSchemaCached?: (name: string, entry: SchemaCacheEntry) => void;
21
+ proxy?: string;
22
22
  private constructor();
23
23
  static get instance(): SchemaCache<SchemaNameToValidatorMap>;
24
+ private cacheSchemaIfValid;
25
+ private cacheSchemas;
26
+ private fetchSchema;
24
27
  /**
25
28
  * A map of cached schema (by name) to payload validators for the schema. A schema
26
29
  * must be cached via `get('schema.name')` before it's validator can be used as
@@ -36,8 +39,5 @@ export declare class SchemaCache<T extends SchemaNameToValidatorMap = SchemaName
36
39
  * retryable: they are not cached as permanent negatives.
37
40
  */
38
41
  resolve(schema?: string): Promise<SchemaResolution | undefined>;
39
- private cacheSchemaIfValid;
40
- private cacheSchemas;
41
- private fetchSchema;
42
42
  }
43
43
  //# sourceMappingURL=SchemaCache.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SchemaCache.d.ts","sourceRoot":"","sources":["../../src/SchemaCache.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAA;AAElG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAA;AAMvE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAA;AAS7E,MAAM,WAAW,gBAAiB,SAAQ,cAAc,CAAC,aAAa,CAAC;IACrE,wFAAwF;IACxF,MAAM,CAAC,EAAE,sBAAsB,CAAA;CAChC;AAED,qBAAa,WAAW,CAAC,CAAC,SAAS,wBAAwB,GAAG,wBAAwB;IACpF;;;OAGG;IACH,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAwD;IAExG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAa;IAEtC,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAA;IAChE,KAAK,CAAC,EAAE,MAAM,CAAA;IAEd,OAAO,CAAC,MAAM,CAA8E;IAC5F,OAAO,CAAC,YAAY,CAA8E;IAClG,OAAO,CAAC,WAAW,CAAa;IAGhC,OAAO,CAAC,WAAW,CAAiB;IAEpC,OAAO;IAIP,MAAM,KAAK,QAAQ,0CAGlB;IAED;;;;OAIG;IACH,IAAI,UAAU,IAAI,CAAC,CAElB;IAEK,GAAG,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,GAAG,IAAI,CAAC;IAcxE;;;;;;OAMG;IACG,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC;IAerE,OAAO,CAAC,kBAAkB;IAgB1B,OAAO,CAAC,YAAY;YAMN,WAAW;CA4B1B"}
1
+ {"version":3,"file":"SchemaCache.d.ts","sourceRoot":"","sources":["../../src/SchemaCache.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAA;AAElG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAA;AAMvE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAA;AAS7E,MAAM,WAAW,gBAAiB,SAAQ,cAAc,CAAC,aAAa,CAAC;IACrE,wFAAwF;IACxF,MAAM,CAAC,EAAE,sBAAsB,CAAA;CAChC;AAED,qBAAa,WAAW,CAAC,CAAC,SAAS,wBAAwB,GAAG,wBAAwB;IACpF;;;OAGG;IACH,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAwD;IAExG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAa;IAEtC,OAAO,CAAC,MAAM,CAA8E;IAC5F,OAAO,CAAC,YAAY,CAA8E;IAClG,OAAO,CAAC,WAAW,CAAa;IAGhC,OAAO,CAAC,WAAW,CAAiB;IAEpC,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAA;IAChE,KAAK,CAAC,EAAE,MAAM,CAAA;IAEd,OAAO;IAIP,MAAM,KAAK,QAAQ,0CAGlB;IAED,OAAO,CAAC,kBAAkB;IAiB1B,OAAO,CAAC,YAAY;YAMN,WAAW;IA6BzB;;;;OAIG;IACH,IAAI,UAAU,IAAI,CAAC,CAElB;IAEK,GAAG,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,GAAG,IAAI,CAAC;IAcxE;;;;;;OAMG;IACG,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC;CAetE"}
@@ -73,13 +73,13 @@ var SchemaCache = class _SchemaCache {
73
73
  */
74
74
  static NULL = { payload: { definition: {}, schema: SchemaSchema } };
75
75
  static _instance;
76
- onSchemaCached;
77
- proxy;
78
76
  _cache = new TtlLruCache({ max: 500, ttl: 1e3 * 60 * 5 });
79
77
  _resolutions = new TtlLruCache({ max: 500, ttl: 1e3 * 60 * 5 });
80
78
  _validators = {};
81
79
  // prevents double discovery
82
80
  getDebounce = new Debounce();
81
+ onSchemaCached;
82
+ proxy;
83
83
  constructor(proxy) {
84
84
  this.proxy = proxy;
85
85
  }
@@ -87,6 +87,46 @@ var SchemaCache = class _SchemaCache {
87
87
  this._instance ??= new _SchemaCache();
88
88
  return this._instance;
89
89
  }
90
+ cacheSchemaIfValid(entry) {
91
+ if (!isDefined2(entry?.payload?.definition)) {
92
+ return;
93
+ }
94
+ const ajv = new Ajv({ strict: false });
95
+ const validator = ajv.compile(entry.payload.definition);
96
+ const schemaName = getSchemaNameFromSchema(entry.payload.definition);
97
+ if (isString(schemaName)) {
98
+ this._cache.set(schemaName, entry);
99
+ const key = schemaName;
100
+ this._validators[key] = validator;
101
+ this.onSchemaCached?.(schemaName, entry);
102
+ }
103
+ }
104
+ cacheSchemas(aliasEntries) {
105
+ for (const entry of aliasEntries?.filter((entry2) => entry2.payload.schema === SchemaSchema) ?? []) {
106
+ this.cacheSchemaIfValid(entry);
107
+ }
108
+ }
109
+ async fetchSchema(schema) {
110
+ try {
111
+ const resolution = await this.resolve(schema);
112
+ if (resolution?.status === "resolved") {
113
+ return;
114
+ }
115
+ const domain = await DomainPayloadWrapper.discover(schema, this.proxy);
116
+ await domain?.fetch();
117
+ this.cacheSchemas(domain?.aliases);
118
+ if (this._cache.get(schema) === void 0 && resolution?.status !== "unreachable") {
119
+ this._cache.set(schema, _SchemaCache.NULL);
120
+ }
121
+ } catch (error) {
122
+ if (error instanceof FetchClientError) {
123
+ console.log(`Fetch Url: ${error.config.url}`);
124
+ }
125
+ handleError(error, (error2) => {
126
+ console.error(`fetchSchema threw: ${error2.message}`);
127
+ });
128
+ }
129
+ }
90
130
  /**
91
131
  * A map of cached schema (by name) to payload validators for the schema. A schema
92
132
  * must be cached via `get('schema.name')` before it's validator can be used as
@@ -119,7 +159,8 @@ var SchemaCache = class _SchemaCache {
119
159
  await this.getDebounce.one(`resolve:${schema}`, async () => {
120
160
  const existing = this._resolutions.get(schema);
121
161
  if (existing === void 0 || existing.status === "unreachable") {
122
- const resolution = await new DomainSchemaResolver().resolve(schema, { proxy: this.proxy });
162
+ const resolver = new DomainSchemaResolver();
163
+ const resolution = await resolver.resolve(schema, { proxy: this.proxy });
123
164
  this._resolutions.set(schema, resolution);
124
165
  if (resolution.status === "resolved" && isDefined2(resolution.payload)) {
125
166
  this.cacheSchemaIfValid({ payload: resolution.payload, source: resolution.source });
@@ -128,45 +169,6 @@ var SchemaCache = class _SchemaCache {
128
169
  });
129
170
  return this._resolutions.get(schema);
130
171
  }
131
- cacheSchemaIfValid(entry) {
132
- if (isDefined2(entry?.payload?.definition)) {
133
- const ajv = new Ajv({ strict: false });
134
- const validator = ajv.compile(entry.payload.definition);
135
- const schemaName = getSchemaNameFromSchema(entry.payload.definition);
136
- if (isString(schemaName)) {
137
- this._cache.set(schemaName, entry);
138
- const key = schemaName;
139
- this._validators[key] = validator;
140
- this.onSchemaCached?.(schemaName, entry);
141
- }
142
- }
143
- }
144
- cacheSchemas(aliasEntries) {
145
- for (const entry of aliasEntries?.filter((entry2) => entry2.payload.schema === SchemaSchema) ?? []) {
146
- this.cacheSchemaIfValid(entry);
147
- }
148
- }
149
- async fetchSchema(schema) {
150
- try {
151
- const resolution = await this.resolve(schema);
152
- if (resolution?.status === "resolved") {
153
- return;
154
- }
155
- const domain = await DomainPayloadWrapper.discover(schema, this.proxy);
156
- await domain?.fetch();
157
- this.cacheSchemas(domain?.aliases);
158
- if (this._cache.get(schema) === void 0 && resolution?.status !== "unreachable") {
159
- this._cache.set(schema, _SchemaCache.NULL);
160
- }
161
- } catch (error) {
162
- if (error instanceof FetchClientError) {
163
- console.log(`Fetch Url: ${error.config.url}`);
164
- }
165
- handleError(error, (error2) => {
166
- console.error(`fetchSchema threw: ${error2.message}`);
167
- });
168
- }
169
- }
170
172
  };
171
173
  export {
172
174
  Debounce,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/Debounce.ts", "../../src/SchemaCache.ts", "../../src/TtlLruCache.ts"],
4
- "sourcesContent": ["import { delay, isDefined } from '@xylabs/sdk-js'\n\nexport class Debounce<TKey = string> {\n private map = new Map<TKey, number>()\n\n async one<T>(key: TKey, closure: () => Promise<T>, timeout = 10_000) {\n const startTime = Date.now()\n while (isDefined(this.map.get(key))) {\n await delay(100)\n if (Date.now() - startTime > timeout) {\n throw new Error(`Debounce timed out [${String(key)}]`)\n }\n }\n try {\n this.map.set(key, 1)\n return await closure()\n } finally {\n // delete (not set 0): isDefined(0) is true, so a leftover 0 would make every\n // subsequent call with this key spin until timeout\n this.map.delete(key)\n }\n }\n}\n", "import {\n FetchClientError,\n handleError, isDefined, isString,\n} from '@xylabs/sdk-js'\nimport type { SchemaResolution, SchemaResolutionSource } from '@xyo-network/domain-payload-plugin'\nimport { DomainPayloadWrapper, DomainSchemaResolver } from '@xyo-network/domain-payload-plugin'\nimport type { FetchedPayload } from '@xyo-network/huri'\nimport type { SchemaPayload } from '@xyo-network/schema-payload-plugin'\nimport { SchemaSchema } from '@xyo-network/schema-payload-plugin'\nimport type { SchemaObject } from 'ajv'\nimport { Ajv } from 'ajv'\n\nimport { Debounce } from './Debounce.ts'\nimport type { SchemaNameToValidatorMap } from './SchemaNameToValidatorMap.ts'\nimport { TtlLruCache } from './TtlLruCache.ts'\n\nconst getSchemaNameFromSchema = (schema: SchemaObject) => {\n if (isString(schema.$id)) {\n return schema.$id\n }\n}\n\nexport interface SchemaCacheEntry extends FetchedPayload<SchemaPayload> {\n /** Provenance of the schema definition when resolved via the domain schema authority */\n source?: SchemaResolutionSource\n}\n\nexport class SchemaCache<T extends SchemaNameToValidatorMap = SchemaNameToValidatorMap> {\n /**\n * Object representing `null` since LRU Cache types\n * only allow for types that derive from object\n */\n protected static readonly NULL: SchemaCacheEntry = { payload: { definition: {}, schema: SchemaSchema } }\n\n private static _instance?: SchemaCache\n\n onSchemaCached?: (name: string, entry: SchemaCacheEntry) => void\n proxy?: string\n\n private _cache = new TtlLruCache<string, SchemaCacheEntry>({ max: 500, ttl: 1000 * 60 * 5 })\n private _resolutions = new TtlLruCache<string, SchemaResolution>({ max: 500, ttl: 1000 * 60 * 5 })\n private _validators: T = {} as T\n\n // prevents double discovery\n private getDebounce = new Debounce()\n\n private constructor(proxy?: string) {\n this.proxy = proxy\n }\n\n static get instance() {\n this._instance ??= new SchemaCache()\n return this._instance\n }\n\n /**\n * A map of cached schema (by name) to payload validators for the schema. A schema\n * must be cached via `get('schema.name')` before it's validator can be used as\n * they are compiled dynamically at runtime upon retrieval.\n */\n get validators(): T {\n return this._validators\n }\n\n async get(schema?: string): Promise<SchemaCacheEntry | undefined | null> {\n if (isString(schema)) {\n await this.getDebounce.one(schema, async () => {\n // If we've never looked for it before, it will be undefined\n if (this._cache.get(schema) === undefined) {\n await this.fetchSchema(schema)\n }\n })\n const value = this._cache.get(schema)\n return value === SchemaCache.NULL ? null : value\n }\n return undefined\n }\n\n /**\n * Resolve a schema name to its domain-published authoritative definition (SPEC.md in\n * @xyo-network/domain-payload-plugin), with provenance and a richer status than get():\n * 'resolved' | 'not-published' | 'unreachable'. Resolved definitions are cached and\n * their validators compiled, exactly as with get(). 'unreachable' results are\n * retryable: they are not cached as permanent negatives.\n */\n async resolve(schema?: string): Promise<SchemaResolution | undefined> {\n if (!isString(schema)) return undefined\n await this.getDebounce.one(`resolve:${schema}`, async () => {\n const existing = this._resolutions.get(schema)\n if (existing === undefined || existing.status === 'unreachable') {\n const resolution = await new DomainSchemaResolver().resolve(schema, { proxy: this.proxy })\n this._resolutions.set(schema, resolution)\n if (resolution.status === 'resolved' && isDefined(resolution.payload)) {\n this.cacheSchemaIfValid({ payload: resolution.payload, source: resolution.source })\n }\n }\n })\n return this._resolutions.get(schema)\n }\n\n private cacheSchemaIfValid(entry: SchemaCacheEntry) {\n // only store them if they match the schema root\n if (isDefined(entry?.payload?.definition)) {\n const ajv = new Ajv({ strict: false })\n // check if it is a valid schema def\n const validator = ajv.compile(entry.payload.definition)\n const schemaName = getSchemaNameFromSchema(entry.payload.definition)\n if (isString(schemaName)) {\n this._cache.set(schemaName, entry)\n const key = schemaName as keyof T\n this._validators[key] = validator as unknown as T[keyof T]\n this.onSchemaCached?.(schemaName, entry)\n }\n }\n }\n\n private cacheSchemas(aliasEntries?: FetchedPayload[] | null) {\n for (const entry of aliasEntries?.filter(entry => entry.payload.schema === SchemaSchema) ?? []) {\n this.cacheSchemaIfValid(entry as SchemaCacheEntry)\n }\n }\n\n private async fetchSchema(schema: string) {\n try {\n // domain schema authority resolution first (DNS records, xyo.json, xyo. subdomain)\n const resolution = await this.resolve(schema)\n if (resolution?.status === 'resolved') {\n return\n }\n\n // legacy discovery fallback; also opportunistically warms sibling schemas\n const domain = await DomainPayloadWrapper.discover(schema, this.proxy)\n await domain?.fetch()\n this.cacheSchemas(domain?.aliases)\n\n // if it is still undefined, mark it as null (not found) \u2014 but never cache transport\n // failures as permanent negatives; 'unreachable' stays undefined so it is retried\n if (this._cache.get(schema) === undefined && resolution?.status !== 'unreachable') {\n this._cache.set(schema, SchemaCache.NULL)\n }\n } catch (error) {\n // transport failure: leave the entry undefined so a future get() retries\n if (error instanceof FetchClientError) {\n console.log(`Fetch Url: ${error.config.url}`)\n }\n handleError(error, (error) => {\n console.error(`fetchSchema threw: ${error.message}`)\n })\n }\n }\n}\n", "export interface TtlLruCacheOpts {\n max: number\n ttl: number\n}\n\nexport class TtlLruCache<K, V> {\n private readonly map = new Map<K, { expires: number; value: V }>()\n private readonly max: number\n private readonly ttl: number\n\n constructor(opts: TtlLruCacheOpts) {\n this.max = opts.max\n this.ttl = opts.ttl\n }\n\n get(key: K): V | undefined {\n const entry = this.map.get(key)\n if (entry === undefined) return undefined\n if (entry.expires <= Date.now()) {\n this.map.delete(key)\n return undefined\n }\n this.map.delete(key)\n this.map.set(key, entry)\n return entry.value\n }\n\n set(key: K, value: V): void {\n if (this.map.has(key)) this.map.delete(key)\n this.map.set(key, { value, expires: Date.now() + this.ttl })\n if (this.map.size > this.max) {\n const oldest = this.map.keys().next().value\n if (oldest !== undefined) this.map.delete(oldest)\n }\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { delay, isDefined } from '@xylabs/sdk-js'\n\nexport class Debounce<TKey = string> {\n private map = new Map<TKey, number>()\n\n async one<T>(key: TKey, closure: () => Promise<T>, timeout = 10_000) {\n const startTime = Date.now()\n while (isDefined(this.map.get(key))) {\n await delay(100)\n if (Date.now() - startTime > timeout) {\n throw new Error(`Debounce timed out [${String(key)}]`)\n }\n }\n try {\n this.map.set(key, 1)\n return await closure()\n } finally {\n // delete (not set 0): isDefined(0) is true, so a leftover 0 would make every\n // subsequent call with this key spin until timeout\n this.map.delete(key)\n }\n }\n}\n", "import {\n FetchClientError,\n handleError, isDefined, isString,\n} from '@xylabs/sdk-js'\nimport type { SchemaResolution, SchemaResolutionSource } from '@xyo-network/domain-payload-plugin'\nimport { DomainPayloadWrapper, DomainSchemaResolver } from '@xyo-network/domain-payload-plugin'\nimport type { FetchedPayload } from '@xyo-network/huri'\nimport type { SchemaPayload } from '@xyo-network/schema-payload-plugin'\nimport { SchemaSchema } from '@xyo-network/schema-payload-plugin'\nimport type { SchemaObject } from 'ajv'\nimport { Ajv } from 'ajv'\n\nimport { Debounce } from './Debounce.ts'\nimport type { SchemaNameToValidatorMap } from './SchemaNameToValidatorMap.ts'\nimport { TtlLruCache } from './TtlLruCache.ts'\n\nconst getSchemaNameFromSchema = (schema: SchemaObject) => {\n if (isString(schema.$id)) {\n return schema.$id\n }\n}\n\nexport interface SchemaCacheEntry extends FetchedPayload<SchemaPayload> {\n /** Provenance of the schema definition when resolved via the domain schema authority */\n source?: SchemaResolutionSource\n}\n\nexport class SchemaCache<T extends SchemaNameToValidatorMap = SchemaNameToValidatorMap> {\n /**\n * Object representing `null` since LRU Cache types\n * only allow for types that derive from object\n */\n protected static readonly NULL: SchemaCacheEntry = { payload: { definition: {}, schema: SchemaSchema } }\n\n private static _instance?: SchemaCache\n\n private _cache = new TtlLruCache<string, SchemaCacheEntry>({ max: 500, ttl: 1000 * 60 * 5 })\n private _resolutions = new TtlLruCache<string, SchemaResolution>({ max: 500, ttl: 1000 * 60 * 5 })\n private _validators: T = {} as T\n\n // prevents double discovery\n private getDebounce = new Debounce()\n\n onSchemaCached?: (name: string, entry: SchemaCacheEntry) => void\n proxy?: string\n\n private constructor(proxy?: string) {\n this.proxy = proxy\n }\n\n static get instance() {\n this._instance ??= new SchemaCache()\n return this._instance\n }\n\n private cacheSchemaIfValid(entry: SchemaCacheEntry) {\n if (!isDefined(entry?.payload?.definition)) {\n return\n }\n\n const ajv = new Ajv({ strict: false })\n // check if it is a valid schema def\n const validator = ajv.compile(entry.payload.definition)\n const schemaName = getSchemaNameFromSchema(entry.payload.definition)\n if (isString(schemaName)) {\n this._cache.set(schemaName, entry)\n const key = schemaName as keyof T\n this._validators[key] = validator as unknown as T[keyof T]\n this.onSchemaCached?.(schemaName, entry)\n }\n }\n\n private cacheSchemas(aliasEntries?: FetchedPayload[] | null) {\n for (const entry of aliasEntries?.filter(entry => entry.payload.schema === SchemaSchema) ?? []) {\n this.cacheSchemaIfValid(entry as SchemaCacheEntry)\n }\n }\n\n private async fetchSchema(schema: string) {\n try {\n // domain schema authority resolution first (DNS records, xyo.json, xyo. subdomain)\n const resolution = await this.resolve(schema)\n if (resolution?.status === 'resolved') {\n return\n }\n\n // legacy discovery fallback; also opportunistically warms sibling schemas\n const domain = await DomainPayloadWrapper.discover(schema, this.proxy)\n await domain?.fetch()\n this.cacheSchemas(domain?.aliases)\n\n // if it is still undefined, mark it as null (not found) \u2014 but never cache transport\n // failures as permanent negatives; 'unreachable' stays undefined so it is retried\n if (this._cache.get(schema) === undefined && resolution?.status !== 'unreachable') {\n this._cache.set(schema, SchemaCache.NULL)\n }\n } catch (error) {\n // transport failure: leave the entry undefined so a future get() retries\n if (error instanceof FetchClientError) {\n console.log(`Fetch Url: ${error.config.url}`)\n }\n handleError(error, (error) => {\n console.error(`fetchSchema threw: ${error.message}`)\n })\n }\n }\n\n /**\n * A map of cached schema (by name) to payload validators for the schema. A schema\n * must be cached via `get('schema.name')` before it's validator can be used as\n * they are compiled dynamically at runtime upon retrieval.\n */\n get validators(): T {\n return this._validators\n }\n\n async get(schema?: string): Promise<SchemaCacheEntry | undefined | null> {\n if (isString(schema)) {\n await this.getDebounce.one(schema, async () => {\n // If we've never looked for it before, it will be undefined\n if (this._cache.get(schema) === undefined) {\n await this.fetchSchema(schema)\n }\n })\n const value = this._cache.get(schema)\n return value === SchemaCache.NULL ? null : value\n }\n return undefined\n }\n\n /**\n * Resolve a schema name to its domain-published authoritative definition (SPEC.md in\n * @xyo-network/domain-payload-plugin), with provenance and a richer status than get():\n * 'resolved' | 'not-published' | 'unreachable'. Resolved definitions are cached and\n * their validators compiled, exactly as with get(). 'unreachable' results are\n * retryable: they are not cached as permanent negatives.\n */\n async resolve(schema?: string): Promise<SchemaResolution | undefined> {\n if (!isString(schema)) return undefined\n await this.getDebounce.one(`resolve:${schema}`, async () => {\n const existing = this._resolutions.get(schema)\n if (existing === undefined || existing.status === 'unreachable') {\n const resolver = new DomainSchemaResolver()\n const resolution = await resolver.resolve(schema, { proxy: this.proxy })\n this._resolutions.set(schema, resolution)\n if (resolution.status === 'resolved' && isDefined(resolution.payload)) {\n this.cacheSchemaIfValid({ payload: resolution.payload, source: resolution.source })\n }\n }\n })\n return this._resolutions.get(schema)\n }\n}\n", "export interface TtlLruCacheOpts {\n max: number\n ttl: number\n}\n\nexport class TtlLruCache<K, V> {\n private readonly map = new Map<K, { expires: number; value: V }>()\n private readonly max: number\n private readonly ttl: number\n\n constructor(opts: TtlLruCacheOpts) {\n this.max = opts.max\n this.ttl = opts.ttl\n }\n\n get(key: K): V | undefined {\n const entry = this.map.get(key)\n if (entry === undefined) return undefined\n if (entry.expires <= Date.now()) {\n this.map.delete(key)\n return undefined\n }\n this.map.delete(key)\n this.map.set(key, entry)\n return entry.value\n }\n\n set(key: K, value: V): void {\n if (this.map.has(key)) this.map.delete(key)\n this.map.set(key, { value, expires: Date.now() + this.ttl })\n if (this.map.size > this.max) {\n const oldest = this.map.keys().next().value\n if (oldest !== undefined) this.map.delete(oldest)\n }\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["isDefined", "isDefined", "entry", "error"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xyo-network/schema-cache",
3
- "version": "7.0.0",
3
+ "version": "7.0.3",
4
4
  "description": "Primary SDK for using XYO Protocol 2.0",
5
5
  "homepage": "https://xyo.network",
6
6
  "bugs": {
@@ -34,35 +34,35 @@
34
34
  "README.md"
35
35
  ],
36
36
  "dependencies": {
37
- "@xyo-network/domain-payload-plugin": "~7.0.0",
38
- "@xyo-network/huri": "~7.0.0",
39
- "@xyo-network/payload-model": "~7.0.0",
40
- "@xyo-network/schema-payload-plugin": "~7.0.0"
37
+ "@xyo-network/domain-payload-plugin": "~7.0.3",
38
+ "@xyo-network/huri": "~7.0.3",
39
+ "@xyo-network/payload-model": "~7.0.3",
40
+ "@xyo-network/schema-payload-plugin": "~7.0.3"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@bitauth/libauth": "~3.0.0",
44
44
  "@opentelemetry/api": "^1.9.1",
45
45
  "@opentelemetry/sdk-trace-base": "^2.8.0",
46
46
  "@scure/base": "~2.2.0",
47
- "@xylabs/sdk-js": "^6.1.1",
48
- "@xylabs/threads": "^6.1.1",
49
- "@xylabs/toolchain": "~8.1.20",
50
- "@xylabs/tsconfig": "~8.1.20",
51
- "@xylabs/tsconfig-dom": "~8.1.20",
52
- "@xylabs/vitest-extended": "^6.1.1",
47
+ "@xylabs/sdk-js": "^6.1.4",
48
+ "@xylabs/threads": "^6.1.4",
49
+ "@xylabs/toolchain": "~8.3.0",
50
+ "@xylabs/tsconfig": "~8.3.0",
51
+ "@xylabs/tsconfig-dom": "~8.3.0",
52
+ "@xylabs/vitest-extended": "^6.1.4",
53
53
  "ajv": "^8.20.0",
54
54
  "async-mutex": "^0.5.0",
55
55
  "debug": "^4.4.3",
56
- "eslint": "^10.4.1",
57
- "ethers": "^6.16.0",
56
+ "eslint": "^10.5.0",
57
+ "ethers": "^6.17.0",
58
58
  "hash-wasm": "~4.12.0",
59
59
  "observable-fns": "^0.6.1",
60
60
  "typescript": "~6.0.3",
61
- "vite": "^8.0.16",
62
- "vitest": "~4.1.8",
61
+ "vite": "^8.1.0",
62
+ "vitest": "~4.1.9",
63
63
  "zod": "^4.4.3",
64
- "@xyo-network/network": "~7.0.0",
65
- "@xyo-network/payload-builder": "~7.0.0"
64
+ "@xyo-network/network": "~7.0.3",
65
+ "@xyo-network/payload-builder": "~7.0.3"
66
66
  },
67
67
  "peerDependencies": {
68
68
  "@bitauth/libauth": "~3.0",