@xyo-network/schema-cache 2.53.18

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.
@@ -0,0 +1,25 @@
1
+ import { delay } from '@xylabs/delay';
2
+ export class Debounce {
3
+ map = new Map();
4
+ async one(key, closure, timeout = 10000) {
5
+ const startTime = Date.now();
6
+ while (this.map.get(key)) {
7
+ await delay(100);
8
+ if (Date.now() - startTime > timeout) {
9
+ throw Error(`Debounce timed out [${key}]`);
10
+ }
11
+ }
12
+ try {
13
+ this.map.set(key, 1);
14
+ return await closure();
15
+ }
16
+ catch (ex) {
17
+ console.error(`Debounce closure threw: ${ex}`);
18
+ throw ex;
19
+ }
20
+ finally {
21
+ this.map.set(key, 0);
22
+ }
23
+ }
24
+ }
25
+ //# sourceMappingURL=Debounce.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Debounce.js","sourceRoot":"","sources":["../../src/Debounce.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AAErC,MAAM,OAAO,QAAQ;IACX,GAAG,GAAG,IAAI,GAAG,EAAgB,CAAA;IAErC,KAAK,CAAC,GAAG,CAAI,GAAS,EAAE,OAAyB,EAAE,OAAO,GAAG,KAAK;QAChE,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QAC5B,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;YACxB,MAAM,KAAK,CAAC,GAAG,CAAC,CAAA;YAChB,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,GAAG,OAAO,EAAE;gBACpC,MAAM,KAAK,CAAC,uBAAuB,GAAG,GAAG,CAAC,CAAA;aAC3C;SACF;QACD,IAAI;YACF,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;YACpB,OAAO,MAAM,OAAO,EAAE,CAAA;SACvB;QAAC,OAAO,EAAE,EAAE;YACX,OAAO,CAAC,KAAK,CAAC,2BAA2B,EAAE,EAAE,CAAC,CAAA;YAC9C,MAAM,EAAE,CAAA;SACT;gBAAS;YACR,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;SACrB;IACH,CAAC;CACF"}
@@ -0,0 +1,86 @@
1
+ import { XyoDomainPayloadWrapper } from '@xyo-network/domain-payload-plugin';
2
+ import { XyoSchemaSchema } from '@xyo-network/schema-payload-plugin';
3
+ import Ajv from 'ajv';
4
+ import LruCache from 'lru-cache';
5
+ import { Debounce } from './Debounce';
6
+ const getSchemaNameFromSchema = (schema) => {
7
+ if (schema.$id) {
8
+ return schema.$id;
9
+ }
10
+ };
11
+ export class SchemaCache {
12
+ /**
13
+ * Object representing `null` since LRU Cache types
14
+ * only allow for types that derive from object
15
+ */
16
+ static NULL = { payload: { definition: {}, schema: XyoSchemaSchema } };
17
+ static _instance;
18
+ onSchemaCached;
19
+ proxy;
20
+ _cache = new LruCache({ max: 500, ttl: 1000 * 60 * 5 });
21
+ _validators = {};
22
+ //prevents double discovery
23
+ getDebounce = new Debounce();
24
+ constructor(proxy) {
25
+ this.proxy = proxy;
26
+ }
27
+ static get instance() {
28
+ if (!this._instance) {
29
+ this._instance = new SchemaCache();
30
+ }
31
+ return this._instance;
32
+ }
33
+ /**
34
+ * A map of cached schema (by name) to payload validators for the schema. A schema
35
+ * must be cached via `get('schema.name')` before it's validator can be used as
36
+ * they are compiled dynamically at runtime upon retrieval.
37
+ */
38
+ get validators() {
39
+ return this._validators;
40
+ }
41
+ async get(schema) {
42
+ if (schema) {
43
+ await this.getDebounce.one(schema, async () => {
44
+ // If we've never looked for it before, it will be undefined
45
+ if (this._cache.get(schema) === undefined) {
46
+ await this.fetchSchema(schema);
47
+ }
48
+ });
49
+ const value = this._cache.get(schema);
50
+ return value === SchemaCache.NULL ? null : value;
51
+ }
52
+ return undefined;
53
+ }
54
+ cacheSchemaIfValid(entry) {
55
+ //only store them if they match the schema root
56
+ if (entry.payload.definition) {
57
+ const ajv = new Ajv({ strict: false });
58
+ //check if it is a valid schema def
59
+ const validator = ajv.compile(entry.payload.definition);
60
+ const schemaName = getSchemaNameFromSchema(entry.payload.definition);
61
+ if (schemaName) {
62
+ this._cache.set(schemaName, entry);
63
+ const key = schemaName;
64
+ this._validators[key] = validator;
65
+ this.onSchemaCached?.(schemaName, entry);
66
+ }
67
+ }
68
+ }
69
+ cacheSchemas(aliasEntries) {
70
+ aliasEntries
71
+ ?.filter((entry) => entry.payload.schema === XyoSchemaSchema)
72
+ .forEach((entry) => {
73
+ this.cacheSchemaIfValid(entry);
74
+ });
75
+ }
76
+ async fetchSchema(schema) {
77
+ const domain = await XyoDomainPayloadWrapper.discover(schema, this.proxy);
78
+ await domain?.fetch();
79
+ this.cacheSchemas(domain?.aliases);
80
+ //if it is still undefined, mark it as null (not found)
81
+ if (this._cache.get(schema) === undefined) {
82
+ this._cache.set(schema, SchemaCache.NULL);
83
+ }
84
+ }
85
+ }
86
+ //# sourceMappingURL=SchemaCache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SchemaCache.js","sourceRoot":"","sources":["../../src/SchemaCache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { XyoDomainSchema } from '@xyo-network/domain-payload-plugin';
2
+ import { PayloadSchema } from '@xyo-network/payload-model';
3
+ import { XyoSchemaSchema } from '@xyo-network/schema-payload-plugin';
4
+ //# sourceMappingURL=SchemaNameToValidatorMap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SchemaNameToValidatorMap.js","sourceRoot":"","sources":["../../src/SchemaNameToValidatorMap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,eAAe,EAAE,MAAM,oCAAoC,CAAA;AACtF,OAAO,EAAW,aAAa,EAAE,MAAM,4BAA4B,CAAA;AACnE,OAAO,EAAoB,eAAe,EAAE,MAAM,oCAAoC,CAAA"}
@@ -0,0 +1,3 @@
1
+ export * from './SchemaCache';
2
+ export * from './SchemaNameToValidatorMap';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAA;AAC7B,cAAc,4BAA4B,CAAA"}
@@ -0,0 +1,5 @@
1
+ export declare class Debounce<TKey = string> {
2
+ private map;
3
+ one<T>(key: TKey, closure: () => Promise<T>, timeout?: number): Promise<T>;
4
+ }
5
+ //# sourceMappingURL=Debounce.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Debounce.d.ts","sourceRoot":"","sources":["../../src/Debounce.ts"],"names":[],"mappings":"AAEA,qBAAa,QAAQ,CAAC,IAAI,GAAG,MAAM;IACjC,OAAO,CAAC,GAAG,CAA0B;IAE/B,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,SAAQ;CAkBnE"}
@@ -0,0 +1,30 @@
1
+ import { FetchedPayload } from '@xyo-network/huri';
2
+ import { XyoSchemaPayload } from '@xyo-network/schema-payload-plugin';
3
+ import { SchemaNameToValidatorMap } from './SchemaNameToValidatorMap';
4
+ export type SchemaCacheEntry = FetchedPayload<XyoSchemaPayload>;
5
+ export declare class SchemaCache<T extends SchemaNameToValidatorMap = SchemaNameToValidatorMap> {
6
+ /**
7
+ * Object representing `null` since LRU Cache types
8
+ * only allow for types that derive from object
9
+ */
10
+ protected static readonly NULL: SchemaCacheEntry;
11
+ private static _instance?;
12
+ onSchemaCached?: (name: string, entry: SchemaCacheEntry) => void;
13
+ proxy?: string;
14
+ private _cache;
15
+ private _validators;
16
+ private getDebounce;
17
+ private constructor();
18
+ static get instance(): SchemaCache<SchemaNameToValidatorMap>;
19
+ /**
20
+ * A map of cached schema (by name) to payload validators for the schema. A schema
21
+ * must be cached via `get('schema.name')` before it's validator can be used as
22
+ * they are compiled dynamically at runtime upon retrieval.
23
+ */
24
+ get validators(): T;
25
+ get(schema?: string): Promise<SchemaCacheEntry | undefined | null>;
26
+ private cacheSchemaIfValid;
27
+ private cacheSchemas;
28
+ private fetchSchema;
29
+ }
30
+ //# sourceMappingURL=SchemaCache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SchemaCache.d.ts","sourceRoot":"","sources":["../../src/SchemaCache.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAClD,OAAO,EAAE,gBAAgB,EAAmB,MAAM,oCAAoC,CAAA;AAKtF,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAA;AAQrE,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC,gBAAgB,CAAC,CAAA;AAE/D,qBAAa,WAAW,CAAC,CAAC,SAAS,wBAAwB,GAAG,wBAAwB;IACpF;;;OAGG;IACH,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAA2D;IAE3G,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,CAA2E;IACzF,OAAO,CAAC,WAAW,CAAa;IAGhC,OAAO,CAAC,WAAW,CAAiB;IAEpC,OAAO;IAIP,MAAM,KAAK,QAAQ,0CAKlB;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,OAAO,CAAC,kBAAkB;IAgB1B,OAAO,CAAC,YAAY;YAQN,WAAW;CAU1B"}
@@ -0,0 +1,17 @@
1
+ import { XyoDomainPayload, XyoDomainSchema } from '@xyo-network/domain-payload-plugin';
2
+ import { Payload, PayloadSchema } from '@xyo-network/payload-model';
3
+ import { XyoSchemaPayload, XyoSchemaSchema } from '@xyo-network/schema-payload-plugin';
4
+ /**
5
+ * Used in conjunction with schema validation to support compile time type assertion
6
+ * for known schema types.
7
+ */
8
+ export type NarrowPayload<T extends Payload = Payload> = ((x: Payload) => x is T) | undefined;
9
+ /**
10
+ * Used to map known schemas (byt their string name) to the validators which assert their types
11
+ */
12
+ export interface SchemaNameToValidatorMap {
13
+ [PayloadSchema]: NarrowPayload<Payload>;
14
+ [XyoDomainSchema]: NarrowPayload<XyoDomainPayload>;
15
+ [XyoSchemaSchema]: NarrowPayload<XyoSchemaPayload>;
16
+ }
17
+ //# sourceMappingURL=SchemaNameToValidatorMap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SchemaNameToValidatorMap.d.ts","sourceRoot":"","sources":["../../src/SchemaNameToValidatorMap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAA;AACtF,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AACnE,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAA;AAEtF;;;GAGG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,CAAA;AAE7F;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAA;IACvC,CAAC,eAAe,CAAC,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAA;IAClD,CAAC,eAAe,CAAC,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAA;CACnD"}
@@ -0,0 +1,3 @@
1
+ export * from './SchemaCache';
2
+ export * from './SchemaNameToValidatorMap';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAA;AAC7B,cAAc,4BAA4B,CAAA"}
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "author": {
3
+ "email": "support@xyo.network",
4
+ "name": "XYO Development Team",
5
+ "url": "https://xyo.network"
6
+ },
7
+ "bugs": {
8
+ "email": "support@xyo.network",
9
+ "url": "https://github.com/XYOracleNetwork/sdk-xyo-client-js/issues"
10
+ },
11
+ "dependencies": {
12
+ "@ethersproject/providers": "^5.7.2",
13
+ "@metamask/providers": "^10.2.1",
14
+ "@xylabs/delay": "^2.7.6",
15
+ "@xyo-network/domain-payload-plugin": "^2.53.18",
16
+ "@xyo-network/huri": "^2.53.18",
17
+ "@xyo-network/payload-model": "^2.53.18",
18
+ "@xyo-network/schema-payload-plugin": "^2.53.18",
19
+ "ajv": "^8.12.0",
20
+ "lru-cache": "^8.0.4"
21
+ },
22
+ "description": "Primary SDK for using XYO Protocol 2.0",
23
+ "devDependencies": {
24
+ "@xylabs/ts-scripts-yarn3": "^2.16.1",
25
+ "@xylabs/tsconfig-dom": "^2.16.1",
26
+ "typescript": "^4.9.5"
27
+ },
28
+ "browser": "dist/esm/index.js",
29
+ "docs": "dist/docs.json",
30
+ "exports": {
31
+ ".": {
32
+ "node": {
33
+ "import": "./dist/esm/index.js",
34
+ "require": "./dist/cjs/index.js"
35
+ },
36
+ "browser": {
37
+ "import": "./dist/esm/index.js",
38
+ "require": "./dist/cjs/index.js"
39
+ },
40
+ "default": "./dist/esm/index.js"
41
+ },
42
+ "./dist/docs.json": {
43
+ "default": "./dist/docs.json"
44
+ },
45
+ "./package.json": "./package.json"
46
+ },
47
+ "main": "dist/cjs/index.js",
48
+ "module": "dist/esm/index.js",
49
+ "homepage": "https://xyo.network",
50
+ "license": "LGPL-3.0",
51
+ "name": "@xyo-network/schema-cache",
52
+ "publishConfig": {
53
+ "access": "public"
54
+ },
55
+ "repository": {
56
+ "type": "git",
57
+ "url": "https://github.com/XYOracleNetwork/sdk-xyo-client-js.git"
58
+ },
59
+ "sideEffects": false,
60
+ "types": "dist/types/index.d.ts",
61
+ "version": "2.53.18"
62
+ }
@@ -0,0 +1,24 @@
1
+ import { delay } from '@xylabs/delay'
2
+
3
+ export class Debounce<TKey = string> {
4
+ private map = new Map<TKey, number>()
5
+
6
+ async one<T>(key: TKey, closure: () => Promise<T>, timeout = 10000) {
7
+ const startTime = Date.now()
8
+ while (this.map.get(key)) {
9
+ await delay(100)
10
+ if (Date.now() - startTime > timeout) {
11
+ throw Error(`Debounce timed out [${key}]`)
12
+ }
13
+ }
14
+ try {
15
+ this.map.set(key, 1)
16
+ return await closure()
17
+ } catch (ex) {
18
+ console.error(`Debounce closure threw: ${ex}`)
19
+ throw ex
20
+ } finally {
21
+ this.map.set(key, 0)
22
+ }
23
+ }
24
+ }
@@ -0,0 +1,104 @@
1
+ import { XyoDomainPayloadWrapper } from '@xyo-network/domain-payload-plugin'
2
+ import { FetchedPayload } from '@xyo-network/huri'
3
+ import { XyoSchemaPayload, XyoSchemaSchema } from '@xyo-network/schema-payload-plugin'
4
+ import Ajv, { SchemaObject } from 'ajv'
5
+ import LruCache from 'lru-cache'
6
+
7
+ import { Debounce } from './Debounce'
8
+ import { SchemaNameToValidatorMap } from './SchemaNameToValidatorMap'
9
+
10
+ const getSchemaNameFromSchema = (schema: SchemaObject) => {
11
+ if (schema.$id) {
12
+ return schema.$id
13
+ }
14
+ }
15
+
16
+ export type SchemaCacheEntry = FetchedPayload<XyoSchemaPayload>
17
+
18
+ export class SchemaCache<T extends SchemaNameToValidatorMap = SchemaNameToValidatorMap> {
19
+ /**
20
+ * Object representing `null` since LRU Cache types
21
+ * only allow for types that derive from object
22
+ */
23
+ protected static readonly NULL: SchemaCacheEntry = { payload: { definition: {}, schema: XyoSchemaSchema } }
24
+
25
+ private static _instance?: SchemaCache
26
+
27
+ onSchemaCached?: (name: string, entry: SchemaCacheEntry) => void
28
+ proxy?: string
29
+
30
+ private _cache = new LruCache<string, SchemaCacheEntry>({ max: 500, ttl: 1000 * 60 * 5 })
31
+ private _validators: T = {} as T
32
+
33
+ //prevents double discovery
34
+ private getDebounce = new Debounce()
35
+
36
+ private constructor(proxy?: string) {
37
+ this.proxy = proxy
38
+ }
39
+
40
+ static get instance() {
41
+ if (!this._instance) {
42
+ this._instance = new SchemaCache()
43
+ }
44
+ return this._instance
45
+ }
46
+
47
+ /**
48
+ * A map of cached schema (by name) to payload validators for the schema. A schema
49
+ * must be cached via `get('schema.name')` before it's validator can be used as
50
+ * they are compiled dynamically at runtime upon retrieval.
51
+ */
52
+ get validators(): T {
53
+ return this._validators
54
+ }
55
+
56
+ async get(schema?: string): Promise<SchemaCacheEntry | undefined | null> {
57
+ if (schema) {
58
+ await this.getDebounce.one(schema, async () => {
59
+ // If we've never looked for it before, it will be undefined
60
+ if (this._cache.get(schema) === undefined) {
61
+ await this.fetchSchema(schema)
62
+ }
63
+ })
64
+ const value = this._cache.get(schema)
65
+ return value === SchemaCache.NULL ? null : value
66
+ }
67
+ return undefined
68
+ }
69
+
70
+ private cacheSchemaIfValid(entry: SchemaCacheEntry) {
71
+ //only store them if they match the schema root
72
+ if (entry.payload.definition) {
73
+ const ajv = new Ajv({ strict: false })
74
+ //check if it is a valid schema def
75
+ const validator = ajv.compile(entry.payload.definition)
76
+ const schemaName = getSchemaNameFromSchema(entry.payload.definition)
77
+ if (schemaName) {
78
+ this._cache.set(schemaName, entry)
79
+ const key = schemaName as keyof T
80
+ this._validators[key] = validator as unknown as T[keyof T]
81
+ this.onSchemaCached?.(schemaName, entry)
82
+ }
83
+ }
84
+ }
85
+
86
+ private cacheSchemas(aliasEntries?: FetchedPayload[] | null) {
87
+ aliasEntries
88
+ ?.filter((entry) => entry.payload.schema === XyoSchemaSchema)
89
+ .forEach((entry) => {
90
+ this.cacheSchemaIfValid(entry as SchemaCacheEntry)
91
+ })
92
+ }
93
+
94
+ private async fetchSchema(schema: string) {
95
+ const domain = await XyoDomainPayloadWrapper.discover(schema, this.proxy)
96
+ await domain?.fetch()
97
+ this.cacheSchemas(domain?.aliases)
98
+
99
+ //if it is still undefined, mark it as null (not found)
100
+ if (this._cache.get(schema) === undefined) {
101
+ this._cache.set(schema, SchemaCache.NULL)
102
+ }
103
+ }
104
+ }
@@ -0,0 +1,18 @@
1
+ import { XyoDomainPayload, XyoDomainSchema } from '@xyo-network/domain-payload-plugin'
2
+ import { Payload, PayloadSchema } from '@xyo-network/payload-model'
3
+ import { XyoSchemaPayload, XyoSchemaSchema } from '@xyo-network/schema-payload-plugin'
4
+
5
+ /**
6
+ * Used in conjunction with schema validation to support compile time type assertion
7
+ * for known schema types.
8
+ */
9
+ export type NarrowPayload<T extends Payload = Payload> = ((x: Payload) => x is T) | undefined
10
+
11
+ /**
12
+ * Used to map known schemas (byt their string name) to the validators which assert their types
13
+ */
14
+ export interface SchemaNameToValidatorMap {
15
+ [PayloadSchema]: NarrowPayload<Payload>
16
+ [XyoDomainSchema]: NarrowPayload<XyoDomainPayload>
17
+ [XyoSchemaSchema]: NarrowPayload<XyoSchemaPayload>
18
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './SchemaCache'
2
+ export * from './SchemaNameToValidatorMap'
package/typedoc.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "intentionallyNotExported": [
3
+ "XyoFetchedPayload",
4
+ "XyoSchemaPayload",
5
+ "PayloadFull",
6
+ "XyoDomainPayload",
7
+ "Payload"
8
+ ]
9
+ }