@xyo-network/diviner-payload-generic 4.3.0 → 5.0.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.
@@ -1 +1 @@
1
- {"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../src/Diviner.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AACtC,OAAO,KAAK,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAA;AAC/F,OAAO,KAAK,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAA;AACzF,OAAO,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAA;AACtE,OAAO,KAAK,EACV,KAAK,EACL,oBAAoB,EACpB,oBAAoB,EACpB,0BAA0B,EAC3B,MAAM,oCAAoC,CAAA;AAI3C,OAAO,KAAK,EACV,OAAO,EAAE,MAAM,EACf,eAAe,EAChB,MAAM,4BAA4B,CAAA;AAMnC,eAAO,MAAM,iCAAiC,EAAG,4CAAqD,CAAA;AACtG,MAAM,MAAM,iCAAiC,GAAG,OAAO,iCAAiC,CAAA;AAExF,MAAM,MAAM,2BAA2B,GAAG,oBAAoB,CAC5D;IACE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CACnB,EACD,iCAAiC,CAClC,CAAA;AAED,qBACa,qBAAqB,CAChC,OAAO,SAAS,oBAAoB,CAAC,2BAA2B,CAAC,GAAG,oBAAoB,CAAC,2BAA2B,CAAC,EACrH,GAAG,SAAS,0BAA0B,GAAG,0BAA0B,EACnE,IAAI,SAAS,eAAe,CAAC,OAAO,CAAC,GAAG,eAAe,CAAC,OAAO,CAAC,EAChE,UAAU,SAAS,sBAAsB,CAAC,eAAe,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,sBAAsB,CAChH,eAAe,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,EACnC,GAAG,EACH,IAAI,CACL,CACD,SAAQ,cAAc,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,CAAC;IACtD,gBAAyB,aAAa,EAAE,MAAM,EAAE,CAA8D;IAC9G,gBAAyB,mBAAmB,EAAE,MAAM,CAAoC;IAExF,SAAS,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAK;IACjE,SAAS,CAAC,gBAAgB,EAAE,eAAe,CAAC,IAAI,CAAC,EAAE,CAAK;IAExD,OAAO,CAAC,kBAAkB,CAAC,CAAmB;IAC9C,OAAO,CAAC,OAAO,CAAC,CAAK;IACrB,OAAO,CAAC,wBAAwB,CAAc;IAE9C,SAAS,KAAK,cAAc,WAE3B;IAED,SAAS,KAAK,OAAO,IAAI,MAAM,EAAE,CAEhC;IAED,SAAS,KAAK,YAAY,WAEzB;IAED,SAAS,CAAC,GAAG,CAAC,KAAK,GAAE,KAAc,EAAE,MAAM,CAAC,EAAE,GAAG;cAOxB,iBAAiB,IAAI,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC;cAC3D,iBAAiB,CAAC,QAAQ,EAAE,IAAI,GAAG,OAAO,CAAC,iBAAiB,CAAC;cAetE,UAAU;cAQD,aAAa,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAkCzE,SAAS,CAAC,kBAAkB,EAAE,aAAa,CAAC,wBAAwB,CAAC,SAAS,CAAC,CAAC,CAO/E;IAGD,SAAS,CAAC,kBAAkB,EAAE,aAAa,CAAC,wBAAwB,CAAC,SAAS,CAAC,CAAC,CAO/E;IAED,SAAS,CAAC,mBAAmB,EAAE,aAAa,CAAC,wBAAwB,CAAC,UAAU,CAAC,CAAC,CAEjF;cAEwB,WAAW,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS;cASlD,WAAW;IAa3B,OAAO,CAAC,aAAa;CAYtB"}
1
+ {"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../src/Diviner.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AACtC,OAAO,KAAK,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAA;AAC/F,OAAO,KAAK,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAA;AACzF,OAAO,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAA;AACtE,OAAO,KAAK,EACV,KAAK,EACL,oBAAoB,EACpB,oBAAoB,EACpB,0BAA0B,EAC3B,MAAM,oCAAoC,CAAA;AAI3C,OAAO,KAAK,EACV,OAAO,EAAE,MAAM,EAEf,eAAe,EAChB,MAAM,4BAA4B,CAAA;AAMnC,eAAO,MAAM,iCAAiC,EAAG,4CAAqD,CAAA;AACtG,MAAM,MAAM,iCAAiC,GAAG,OAAO,iCAAiC,CAAA;AAExF,MAAM,MAAM,2BAA2B,GAAG,oBAAoB,CAC5D;IACE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CACnB,EACD,iCAAiC,CAClC,CAAA;AAED,qBACa,qBAAqB,CAChC,OAAO,SAAS,oBAAoB,CAAC,2BAA2B,CAAC,GAAG,oBAAoB,CAAC,2BAA2B,CAAC,EACrH,GAAG,SAAS,0BAA0B,GAAG,0BAA0B,EACnE,IAAI,SAAS,eAAe,CAAC,OAAO,CAAC,GAAG,eAAe,CAAC,OAAO,CAAC,EAChE,UAAU,SAAS,sBAAsB,CAAC,eAAe,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,sBAAsB,CAChH,eAAe,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,EACnC,GAAG,EACH,IAAI,CACL,CACD,SAAQ,cAAc,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,CAAC;IACtD,gBAAyB,aAAa,EAAE,MAAM,EAAE,CAA8D;IAC9G,gBAAyB,mBAAmB,EAAE,MAAM,CAAoC;IAExF,SAAS,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAK;IACjE,SAAS,CAAC,gBAAgB,EAAE,eAAe,CAAC,IAAI,CAAC,EAAE,CAAK;IAExD,OAAO,CAAC,kBAAkB,CAAC,CAAmB;IAC9C,OAAO,CAAC,OAAO,CAAC,CAAU;IAC1B,OAAO,CAAC,wBAAwB,CAAc;IAE9C,SAAS,KAAK,cAAc,WAE3B;IAED,SAAS,KAAK,OAAO,IAAI,MAAM,EAAE,CAEhC;IAED,SAAS,KAAK,YAAY,WAEzB;IAED,SAAS,CAAC,GAAG,CAAC,KAAK,GAAE,KAAc,EAAE,MAAM,CAAC,EAAE,GAAG;cAOxB,iBAAiB,IAAI,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC;cAC3D,iBAAiB,CAAC,QAAQ,EAAE,IAAI,GAAG,OAAO,CAAC,iBAAiB,CAAC;cAetE,UAAU;cAQD,aAAa,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAkCzE,SAAS,CAAC,kBAAkB,EAAE,aAAa,CAAC,wBAAwB,CAAC,SAAS,CAAC,CAAC,CAO/E;IAGD,SAAS,CAAC,kBAAkB,EAAE,aAAa,CAAC,wBAAwB,CAAC,SAAS,CAAC,CAAC,CAO/E;IAED,SAAS,CAAC,mBAAmB,EAAE,aAAa,CAAC,wBAAwB,CAAC,UAAU,CAAC,CAAC,CAEjF;cAEwB,WAAW,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS;cASlD,WAAW;IAa3B,OAAO,CAAC,aAAa;CAYtB"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/Diviner.ts"],"sourcesContent":["import { filterAs } from '@xylabs/array'\nimport { assertEx } from '@xylabs/assert'\nimport type { EventListener } from '@xylabs/events'\nimport { forget } from '@xylabs/forget'\nimport type { Hex } from '@xylabs/hex'\nimport type { ArchivistInstance, ArchivistModuleEventData } from '@xyo-network/archivist-model'\nimport type { DivinerInstance, DivinerModuleEventData } from '@xyo-network/diviner-model'\nimport { PayloadDiviner } from '@xyo-network/diviner-payload-abstract'\nimport type {\n Order,\n PayloadDivinerConfig,\n PayloadDivinerParams,\n PayloadDivinerQueryPayload,\n} from '@xyo-network/diviner-payload-model'\nimport { asPayloadDivinerQueryPayload } from '@xyo-network/diviner-payload-model'\nimport { creatableModule } from '@xyo-network/module-model'\nimport { PayloadBuilder } from '@xyo-network/payload-builder'\nimport type {\n Payload, Schema,\n WithStorageMeta,\n} from '@xyo-network/payload-model'\nimport { Mutex } from 'async-mutex'\n\nconst DEFAULT_INDEX_BATCH_SIZE = 100 as const\nconst DEFAULT_MAX_INDEX_SIZE = 8000 as const\n\nexport const GenericPayloadDivinerConfigSchema = 'network.xyo.diviner.payload.generic.config' as const\nexport type GenericPayloadDivinerConfigSchema = typeof GenericPayloadDivinerConfigSchema\n\nexport type GenericPayloadDivinerConfig = PayloadDivinerConfig<\n {\n indexes?: string[]\n },\n GenericPayloadDivinerConfigSchema\n>\n\n@creatableModule()\nexport class GenericPayloadDiviner<\n TParams extends PayloadDivinerParams<GenericPayloadDivinerConfig> = PayloadDivinerParams<GenericPayloadDivinerConfig>,\n TIn extends PayloadDivinerQueryPayload = PayloadDivinerQueryPayload,\n TOut extends WithStorageMeta<Payload> = WithStorageMeta<Payload>,\n TEventData extends DivinerModuleEventData<DivinerInstance<TParams, TIn, TOut>, TIn, TOut> = DivinerModuleEventData<\n DivinerInstance<TParams, TIn, TOut>,\n TIn,\n TOut\n >,\n> extends PayloadDiviner<TParams, TIn, TOut, TEventData> {\n static override readonly configSchemas: Schema[] = [...super.configSchemas, GenericPayloadDivinerConfigSchema]\n static override readonly defaultConfigSchema: Schema = GenericPayloadDivinerConfigSchema\n\n protected indexMaps: Record<string, WithStorageMeta<TOut>[]> = {}\n protected payloadsWithMeta: WithStorageMeta<TOut>[] = []\n\n private _archivistInstance?: ArchivistInstance\n private _cursor?: Hex\n private _updatePayloadPairsMutex = new Mutex()\n\n protected get indexBatchSize() {\n return this.config.indexBatchSize ?? DEFAULT_INDEX_BATCH_SIZE\n }\n\n protected get indexes(): string[] {\n return ['schema', ...(this.config.indexes ?? [])]\n }\n\n protected get maxIndexSize() {\n return this.config.maxIndexSize ?? DEFAULT_MAX_INDEX_SIZE\n }\n\n protected all(order: Order = 'desc', cursor?: Hex) {\n const payloads = this.payloadsWithMeta.toSorted(PayloadBuilder.compareStorageMeta)\n if (order === 'desc') payloads.reverse()\n const startIndex = (cursor ? (payloads.findIndex(payload => payload._sequence === cursor) ?? -1) : -1) + 1\n return payloads.slice(startIndex)\n }\n\n protected override async archivistInstance(): Promise<ArchivistInstance | undefined>\n protected override async archivistInstance(required: true): Promise<ArchivistInstance>\n protected override async archivistInstance(required = false): Promise<ArchivistInstance | undefined> {\n if (!this._archivistInstance) {\n const archivist = await super.archivistInstance()\n if (required && !archivist) {\n throw new Error('Failed to find archivist')\n }\n archivist?.on('inserted', this.onArchivistInserted)\n archivist?.on('cleared', this.onArchivistCleared)\n archivist?.on('deleted', this.onArchivistDeleted)\n this._archivistInstance = archivist\n }\n return this._archivistInstance\n }\n\n protected async clearIndex() {\n await this._updatePayloadPairsMutex.runExclusive(() => {\n this._cursor = undefined\n this.payloadsWithMeta = []\n this.indexMaps = {}\n })\n }\n\n protected override async divineHandler(payloads?: TIn[]): Promise<TOut[]> {\n const filters = filterAs(payloads ?? [], asPayloadDivinerQueryPayload)\n assertEx(filters.length < 2, () => 'Multiple PayloadDivinerQuery payloads may not be specified')\n const filter = assertEx(filters.shift(), () => 'No PayloadDivinerQuery specified') as PayloadDivinerQueryPayload\n await this.updateIndex()\n\n const {\n\n schema, schemas, order, limit, cursor, ...props\n } = filter\n let all: TOut[] = this.all(order, cursor)\n if (all) {\n if (schemas?.length) all = all.filter(payload => schemas.includes(payload.schema))\n if (Object.keys(props).length > 0) {\n const additionalFilterCriteria = Object.entries(props)\n for (const [prop, filter] of additionalFilterCriteria) {\n const property = prop as keyof TOut\n all\n = Array.isArray(filter)\n ? all.filter(payload =>\n filter.every((value) => {\n const prop = payload?.[property]\n // TODO: This seems to be written just to check arrays, and now that $meta is there, need to check type?\n return Array.isArray(prop) && prop.includes?.(value)\n }))\n : all.filter(payload => payload?.[property] === filter)\n }\n }\n return limit ? all.slice(0, limit) : all\n } else {\n throw new Error('Archivist does not support \"all\"')\n }\n }\n\n protected onArchivistCleared: EventListener<ArchivistModuleEventData['cleared']> = () => {\n forget(\n (async () => {\n await this.clearIndex()\n await this.updateIndex()\n })(),\n )\n }\n\n // we are just rebuilding the entire index at this point on delete since large archivists do not support delete\n protected onArchivistDeleted: EventListener<ArchivistModuleEventData['deleted']> = () => {\n forget(\n (async () => {\n await this.clearIndex()\n await this.updateIndex()\n })(),\n )\n }\n\n protected onArchivistInserted: EventListener<ArchivistModuleEventData['inserted']> = () => {\n forget(this.updateIndex())\n }\n\n protected override async stopHandler(_timeout?: number | undefined) {\n await super.stopHandler()\n const archivist = await this.archivistInstance(true)\n archivist.off('inserted', this.onArchivistInserted)\n archivist.off('deleted', this.onArchivistDeleted)\n archivist.off('cleared', this.onArchivistCleared)\n }\n\n // index any new payloads\n protected async updateIndex() {\n await this._updatePayloadPairsMutex.runExclusive(async () => {\n const archivist = await this.archivistInstance(true)\n let newPayloads = await archivist.next({ limit: 100, cursor: this._cursor }) as TOut[]\n while (newPayloads.length > 0) {\n this._cursor = newPayloads.at(-1)?._sequence\n assertEx(this.payloadsWithMeta.length + newPayloads.length <= this.maxIndexSize, () => 'maxIndexSize exceeded')\n this.indexPayloads(newPayloads)\n newPayloads = await archivist.next({ limit: 100, cursor: this._cursor }) as TOut[]\n }\n })\n }\n\n private indexPayloads(payloads: WithStorageMeta<TOut>[]): Hex {\n this.payloadsWithMeta.push(...payloads)\n\n // update the custom indexes\n for (const index of this.indexes ?? []) {\n this.indexMaps[index] = this.indexMaps[index] ?? []\n for (const payload of payloads) {\n this.indexMaps[index].push(payload)\n }\n }\n return assertEx(payloads.at(-1), () => 'No payloads to index')._sequence\n }\n}\n"],"mappings":"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","names":["filter","prop"]}
1
+ {"version":3,"sources":["../../src/Diviner.ts"],"sourcesContent":["import { filterAs } from '@xylabs/array'\nimport { assertEx } from '@xylabs/assert'\nimport type { EventListener } from '@xylabs/events'\nimport { forget } from '@xylabs/forget'\nimport type { Hex } from '@xylabs/hex'\nimport type { ArchivistInstance, ArchivistModuleEventData } from '@xyo-network/archivist-model'\nimport type { DivinerInstance, DivinerModuleEventData } from '@xyo-network/diviner-model'\nimport { PayloadDiviner } from '@xyo-network/diviner-payload-abstract'\nimport type {\n Order,\n PayloadDivinerConfig,\n PayloadDivinerParams,\n PayloadDivinerQueryPayload,\n} from '@xyo-network/diviner-payload-model'\nimport { asPayloadDivinerQueryPayload } from '@xyo-network/diviner-payload-model'\nimport { creatableModule } from '@xyo-network/module-model'\nimport { PayloadBuilder } from '@xyo-network/payload-builder'\nimport type {\n Payload, Schema,\n Sequence,\n WithStorageMeta,\n} from '@xyo-network/payload-model'\nimport { Mutex } from 'async-mutex'\n\nconst DEFAULT_INDEX_BATCH_SIZE = 100 as const\nconst DEFAULT_MAX_INDEX_SIZE = 8000 as const\n\nexport const GenericPayloadDivinerConfigSchema = 'network.xyo.diviner.payload.generic.config' as const\nexport type GenericPayloadDivinerConfigSchema = typeof GenericPayloadDivinerConfigSchema\n\nexport type GenericPayloadDivinerConfig = PayloadDivinerConfig<\n {\n indexes?: string[]\n },\n GenericPayloadDivinerConfigSchema\n>\n\n@creatableModule()\nexport class GenericPayloadDiviner<\n TParams extends PayloadDivinerParams<GenericPayloadDivinerConfig> = PayloadDivinerParams<GenericPayloadDivinerConfig>,\n TIn extends PayloadDivinerQueryPayload = PayloadDivinerQueryPayload,\n TOut extends WithStorageMeta<Payload> = WithStorageMeta<Payload>,\n TEventData extends DivinerModuleEventData<DivinerInstance<TParams, TIn, TOut>, TIn, TOut> = DivinerModuleEventData<\n DivinerInstance<TParams, TIn, TOut>,\n TIn,\n TOut\n >,\n> extends PayloadDiviner<TParams, TIn, TOut, TEventData> {\n static override readonly configSchemas: Schema[] = [...super.configSchemas, GenericPayloadDivinerConfigSchema]\n static override readonly defaultConfigSchema: Schema = GenericPayloadDivinerConfigSchema\n\n protected indexMaps: Record<string, WithStorageMeta<TOut>[]> = {}\n protected payloadsWithMeta: WithStorageMeta<TOut>[] = []\n\n private _archivistInstance?: ArchivistInstance\n private _cursor?: Sequence\n private _updatePayloadPairsMutex = new Mutex()\n\n protected get indexBatchSize() {\n return this.config.indexBatchSize ?? DEFAULT_INDEX_BATCH_SIZE\n }\n\n protected get indexes(): string[] {\n return ['schema', ...(this.config.indexes ?? [])]\n }\n\n protected get maxIndexSize() {\n return this.config.maxIndexSize ?? DEFAULT_MAX_INDEX_SIZE\n }\n\n protected all(order: Order = 'desc', cursor?: Hex) {\n const payloads = this.payloadsWithMeta.toSorted(PayloadBuilder.compareStorageMeta)\n if (order === 'desc') payloads.reverse()\n const startIndex = (cursor ? (payloads.findIndex(payload => payload._sequence === cursor) ?? -1) : -1) + 1\n return payloads.slice(startIndex)\n }\n\n protected override async archivistInstance(): Promise<ArchivistInstance | undefined>\n protected override async archivistInstance(required: true): Promise<ArchivistInstance>\n protected override async archivistInstance(required = false): Promise<ArchivistInstance | undefined> {\n if (!this._archivistInstance) {\n const archivist = await super.archivistInstance()\n if (required && !archivist) {\n throw new Error('Failed to find archivist')\n }\n archivist?.on('inserted', this.onArchivistInserted)\n archivist?.on('cleared', this.onArchivistCleared)\n archivist?.on('deleted', this.onArchivistDeleted)\n this._archivistInstance = archivist\n }\n return this._archivistInstance\n }\n\n protected async clearIndex() {\n await this._updatePayloadPairsMutex.runExclusive(() => {\n this._cursor = undefined\n this.payloadsWithMeta = []\n this.indexMaps = {}\n })\n }\n\n protected override async divineHandler(payloads?: TIn[]): Promise<TOut[]> {\n const filters = filterAs(payloads ?? [], asPayloadDivinerQueryPayload)\n assertEx(filters.length < 2, () => 'Multiple PayloadDivinerQuery payloads may not be specified')\n const filter = assertEx(filters.shift(), () => 'No PayloadDivinerQuery specified') as PayloadDivinerQueryPayload\n await this.updateIndex()\n\n const {\n\n schema, schemas, order, limit, cursor, ...props\n } = filter\n let all: TOut[] = this.all(order, cursor)\n if (all) {\n if (schemas?.length) all = all.filter(payload => schemas.includes(payload.schema))\n if (Object.keys(props).length > 0) {\n const additionalFilterCriteria = Object.entries(props)\n for (const [prop, filter] of additionalFilterCriteria) {\n const property = prop as keyof TOut\n all\n = Array.isArray(filter)\n ? all.filter(payload =>\n filter.every((value) => {\n const prop = payload?.[property]\n // TODO: This seems to be written just to check arrays, and now that $meta is there, need to check type?\n return Array.isArray(prop) && prop.includes?.(value)\n }))\n : all.filter(payload => payload?.[property] === filter)\n }\n }\n return limit ? all.slice(0, limit) : all\n } else {\n throw new Error('Archivist does not support \"all\"')\n }\n }\n\n protected onArchivistCleared: EventListener<ArchivistModuleEventData['cleared']> = () => {\n forget(\n (async () => {\n await this.clearIndex()\n await this.updateIndex()\n })(),\n )\n }\n\n // we are just rebuilding the entire index at this point on delete since large archivists do not support delete\n protected onArchivistDeleted: EventListener<ArchivistModuleEventData['deleted']> = () => {\n forget(\n (async () => {\n await this.clearIndex()\n await this.updateIndex()\n })(),\n )\n }\n\n protected onArchivistInserted: EventListener<ArchivistModuleEventData['inserted']> = () => {\n forget(this.updateIndex())\n }\n\n protected override async stopHandler(_timeout?: number | undefined) {\n await super.stopHandler()\n const archivist = await this.archivistInstance(true)\n archivist.off('inserted', this.onArchivistInserted)\n archivist.off('deleted', this.onArchivistDeleted)\n archivist.off('cleared', this.onArchivistCleared)\n }\n\n // index any new payloads\n protected async updateIndex() {\n await this._updatePayloadPairsMutex.runExclusive(async () => {\n const archivist = await this.archivistInstance(true)\n let newPayloads = await archivist.next({ limit: 100, cursor: this._cursor }) as TOut[]\n while (newPayloads.length > 0) {\n this._cursor = newPayloads.at(-1)?._sequence\n assertEx(this.payloadsWithMeta.length + newPayloads.length <= this.maxIndexSize, () => 'maxIndexSize exceeded')\n this.indexPayloads(newPayloads)\n newPayloads = await archivist.next({ limit: 100, cursor: this._cursor }) as TOut[]\n }\n })\n }\n\n private indexPayloads(payloads: WithStorageMeta<TOut>[]): Hex {\n this.payloadsWithMeta.push(...payloads)\n\n // update the custom indexes\n for (const index of this.indexes ?? []) {\n this.indexMaps[index] = this.indexMaps[index] ?? []\n for (const payload of payloads) {\n this.indexMaps[index].push(payload)\n }\n }\n return assertEx(payloads.at(-1), () => 'No payloads to index')._sequence\n }\n}\n"],"mappings":"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","names":["filter","prop"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xyo-network/diviner-payload-generic",
3
- "version": "4.3.0",
3
+ "version": "5.0.0",
4
4
  "description": "Primary SDK for using XYO Protocol 2.0",
5
5
  "homepage": "https://xyo.network",
6
6
  "bugs": {
@@ -28,30 +28,34 @@
28
28
  },
29
29
  "module": "dist/neutral/index.mjs",
30
30
  "types": "dist/neutral/index.d.ts",
31
+ "files": [
32
+ "dist",
33
+ "src"
34
+ ],
31
35
  "dependencies": {
32
- "@xylabs/array": "^4.15.0",
33
- "@xylabs/assert": "^4.15.0",
34
- "@xylabs/events": "^4.15.0",
35
- "@xylabs/forget": "^4.15.0",
36
- "@xylabs/hex": "^4.15.0",
37
- "@xyo-network/archivist-model": "^4.3.0",
38
- "@xyo-network/diviner-model": "^4.3.0",
39
- "@xyo-network/diviner-payload-abstract": "^4.3.0",
40
- "@xyo-network/diviner-payload-model": "^4.3.0",
41
- "@xyo-network/module-model": "^4.3.0",
42
- "@xyo-network/payload-builder": "^4.3.0",
43
- "@xyo-network/payload-model": "^4.3.0",
36
+ "@xylabs/array": "^5.0.0",
37
+ "@xylabs/assert": "^5.0.0",
38
+ "@xylabs/events": "^5.0.0",
39
+ "@xylabs/forget": "^5.0.0",
40
+ "@xylabs/hex": "^5.0.0",
41
+ "@xyo-network/archivist-model": "^5.0.0",
42
+ "@xyo-network/diviner-model": "^5.0.0",
43
+ "@xyo-network/diviner-payload-abstract": "^5.0.0",
44
+ "@xyo-network/diviner-payload-model": "^5.0.0",
45
+ "@xyo-network/module-model": "^5.0.0",
46
+ "@xyo-network/payload-builder": "^5.0.0",
47
+ "@xyo-network/payload-model": "^5.0.0",
44
48
  "async-mutex": "^0.5.0"
45
49
  },
46
50
  "devDependencies": {
47
- "@xylabs/delay": "^4.15.0",
48
- "@xylabs/ts-scripts-yarn3": "^7.0.1",
49
- "@xylabs/tsconfig": "^7.0.1",
50
- "@xylabs/vitest-extended": "^4.15.0",
51
- "@xyo-network/archivist-indexeddb": "^4.3.0",
52
- "@xyo-network/archivist-memory": "^4.3.0",
53
- "@xyo-network/node-memory": "^4.3.0",
54
- "@xyo-network/payload-builder": "^4.3.0",
51
+ "@xylabs/delay": "^5.0.0",
52
+ "@xylabs/ts-scripts-yarn3": "^7.0.2",
53
+ "@xylabs/tsconfig": "^7.0.2",
54
+ "@xylabs/vitest-extended": "^5.0.0",
55
+ "@xyo-network/archivist-indexeddb": "^5.0.0",
56
+ "@xyo-network/archivist-memory": "^5.0.0",
57
+ "@xyo-network/node-memory": "^5.0.0",
58
+ "@xyo-network/payload-builder": "^5.0.0",
55
59
  "fake-indexeddb": "^6.0.1",
56
60
  "typescript": "^5.8.3",
57
61
  "vitest": "^3.2.4"
package/src/Diviner.ts CHANGED
@@ -17,6 +17,7 @@ import { creatableModule } from '@xyo-network/module-model'
17
17
  import { PayloadBuilder } from '@xyo-network/payload-builder'
18
18
  import type {
19
19
  Payload, Schema,
20
+ Sequence,
20
21
  WithStorageMeta,
21
22
  } from '@xyo-network/payload-model'
22
23
  import { Mutex } from 'async-mutex'
@@ -52,7 +53,7 @@ export class GenericPayloadDiviner<
52
53
  protected payloadsWithMeta: WithStorageMeta<TOut>[] = []
53
54
 
54
55
  private _archivistInstance?: ArchivistInstance
55
- private _cursor?: Hex
56
+ private _cursor?: Sequence
56
57
  private _updatePayloadPairsMutex = new Mutex()
57
58
 
58
59
  protected get indexBatchSize() {
@@ -0,0 +1,274 @@
1
+ import '@xylabs/vitest-extended'
2
+
3
+ import { delay } from '@xylabs/delay'
4
+ import { IndexedDbArchivist } from '@xyo-network/archivist-indexeddb'
5
+ import type { PayloadDivinerQueryPayload } from '@xyo-network/diviner-payload-model'
6
+ import { PayloadDivinerQuerySchema } from '@xyo-network/diviner-payload-model'
7
+ import { MemoryNode } from '@xyo-network/node-memory'
8
+ import { PayloadBuilder } from '@xyo-network/payload-builder'
9
+ import type { Payload, WithStorageMeta } from '@xyo-network/payload-model'
10
+ import {
11
+ IDBCursor,
12
+ IDBCursorWithValue,
13
+ IDBDatabase,
14
+ IDBFactory,
15
+ IDBIndex,
16
+ IDBKeyRange,
17
+ IDBObjectStore,
18
+ IDBOpenDBRequest,
19
+ IDBRequest,
20
+ IDBTransaction,
21
+ IDBVersionChangeEvent,
22
+ indexedDB,
23
+ } from 'fake-indexeddb'
24
+ import {
25
+ beforeAll, describe, expect, it,
26
+ } from 'vitest'
27
+
28
+ import { GenericPayloadDiviner, GenericPayloadDivinerConfigSchema } from '../Diviner.ts'
29
+
30
+ /**
31
+ * @group module
32
+ * @group diviner
33
+ */
34
+
35
+ describe('GenericPayloadDiviner', () => {
36
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
37
+ const globalThisAny = globalThis as any
38
+
39
+ // Augment window with prototypes to ensure instance of comparisons work
40
+ globalThisAny.IDBCursor = IDBCursor
41
+ globalThisAny.IDBCursorWithValue = IDBCursorWithValue
42
+ globalThisAny.IDBDatabase = IDBDatabase
43
+ globalThisAny.IDBFactory = IDBFactory
44
+ globalThisAny.IDBIndex = IDBIndex
45
+ globalThisAny.IDBKeyRange = IDBKeyRange
46
+ globalThisAny.IDBObjectStore = IDBObjectStore
47
+ globalThisAny.IDBOpenDBRequest = IDBOpenDBRequest
48
+ globalThisAny.IDBRequest = IDBRequest
49
+ globalThisAny.IDBTransaction = IDBTransaction
50
+ globalThisAny.IDBVersionChangeEvent = IDBVersionChangeEvent
51
+ globalThisAny.indexedDB = indexedDB
52
+
53
+ let archivist: IndexedDbArchivist
54
+ let sut: GenericPayloadDiviner
55
+ let node: MemoryNode
56
+ let payloadA: Payload<{ schema: string; url: string }>
57
+ let payloadB: Payload<{ foo: string[]; schema: string }>
58
+ let payloadC: Payload<{ foo: string[]; schema: string }>
59
+ let payloadD: Payload<{ foo: string[]; schema: string }>
60
+ let insertedPayloads: WithStorageMeta<Payload>[]
61
+
62
+ beforeAll(async () => {
63
+ payloadA = {
64
+ schema: 'network.xyo.test',
65
+ url: 'https://xyo.network',
66
+ }
67
+ payloadB = {
68
+ foo: ['bar', 'baz'],
69
+ schema: 'network.xyo.debug',
70
+ }
71
+ payloadC = {
72
+ foo: ['one', 'two'],
73
+ schema: 'network.xyo.debug',
74
+ }
75
+ payloadD = {
76
+ foo: ['aaa', 'bbb'],
77
+ schema: 'network.xyo.debug',
78
+ }
79
+
80
+ archivist = await IndexedDbArchivist.create({
81
+ account: 'random',
82
+ config: { name: 'test', schema: IndexedDbArchivist.defaultConfigSchema },
83
+ })
84
+ const [insertedPayloadA] = await archivist.insert([payloadA])
85
+ await delay(1)
86
+ const [insertedPayloadB] = await archivist.insert([payloadB])
87
+ await delay(1)
88
+ const [insertedPayloadC] = await archivist.insert([payloadC])
89
+ await delay(1)
90
+ const [insertedPayloadD] = await archivist.insert([payloadD])
91
+ await delay(1)
92
+ insertedPayloads = [insertedPayloadA, insertedPayloadB, insertedPayloadC, insertedPayloadD]
93
+ const all = await archivist.all()
94
+ console.log(all)
95
+ sut = await GenericPayloadDiviner.create({
96
+ account: 'random',
97
+ config: {
98
+ archivist: archivist.address,
99
+ schema: GenericPayloadDivinerConfigSchema,
100
+ },
101
+ })
102
+ node = await MemoryNode.create({
103
+ account: 'random',
104
+ config: { schema: MemoryNode.defaultConfigSchema },
105
+ })
106
+ const modules = [archivist, sut]
107
+ await node.start()
108
+ await Promise.all(
109
+ modules.map(async (mod) => {
110
+ await node.register(mod)
111
+ await node.attach(mod.address, true)
112
+ }),
113
+ )
114
+ await sut.start()
115
+ })
116
+ describe('with filter for', () => {
117
+ describe('schema', () => {
118
+ describe('single', () => {
119
+ it.each(['network.xyo.test', 'network.xyo.debug'])('only returns payloads of that schema', async (schema) => {
120
+ const schemas = [schema]
121
+ const query = new PayloadBuilder<PayloadDivinerQueryPayload>({ schema: PayloadDivinerQuerySchema })
122
+ .fields({ schemas })
123
+ .build()
124
+ const results = await sut.divine([query])
125
+ expect(results.length).toBeGreaterThan(0)
126
+ expect(results.every(result => result.schema === schema)).toBe(true)
127
+ })
128
+ it('only return single payload of that schema', async () => {
129
+ const schemas = ['network.xyo.debug']
130
+ const query = new PayloadBuilder<PayloadDivinerQueryPayload>({ schema: PayloadDivinerQuerySchema })
131
+ .fields({ limit: 1, schemas })
132
+ .build()
133
+ const results = await sut.divine([query])
134
+ expect(results.length).toBe(1)
135
+ expect(await PayloadBuilder.dataHash(results[0])).toBe(await PayloadBuilder.dataHash(payloadD))
136
+ expect(results.every(result => result.schema === 'network.xyo.debug')).toBe(true)
137
+ })
138
+ it('only return single payload of that schema (desc)', async () => {
139
+ const schemas = ['network.xyo.debug']
140
+ const query = new PayloadBuilder<PayloadDivinerQueryPayload>({ schema: PayloadDivinerQuerySchema })
141
+ .fields({
142
+ limit: 1, order: 'desc', schemas,
143
+ })
144
+ .build()
145
+ const results = await sut.divine([query])
146
+ expect(results.length).toBe(1)
147
+ expect(await PayloadBuilder.dataHash(results[0])).toBe(await PayloadBuilder.dataHash(payloadD))
148
+ expect(results.every(result => result.schema === 'network.xyo.debug')).toBe(true)
149
+ })
150
+ it('only return single payload of that schema (asc)', async () => {
151
+ const schemas = ['network.xyo.debug']
152
+ const query = new PayloadBuilder<PayloadDivinerQueryPayload>({ schema: PayloadDivinerQuerySchema })
153
+ .fields({
154
+ limit: 1, order: 'asc', schemas,
155
+ })
156
+ .build()
157
+ const results = await sut.divine([query])
158
+ expect(results.length).toBe(1)
159
+ expect(await PayloadBuilder.dataHash(results[0])).toBe(await PayloadBuilder.dataHash(payloadB))
160
+ expect(results.every(result => result.schema === 'network.xyo.debug')).toBe(true)
161
+ })
162
+ })
163
+ describe('multiple', () => {
164
+ it('only returns payloads of that schema', async () => {
165
+ const schemas = ['network.xyo.test', 'network.xyo.debug']
166
+ const query = new PayloadBuilder<PayloadDivinerQueryPayload>({ schema: PayloadDivinerQuerySchema })
167
+ .fields({ schemas })
168
+ .build()
169
+ const results = await sut.divine([query])
170
+ expect(results.length).toBeGreaterThan(0)
171
+ expect(results.every(result => schemas.includes(result.schema))).toBe(true)
172
+ })
173
+ })
174
+ describe('paging', () => {
175
+ it('test paging with multiple calls (asc)', async () => {
176
+ const schemas = ['network.xyo.test', 'network.xyo.debug']
177
+ const query = new PayloadBuilder<PayloadDivinerQueryPayload>({ schema: PayloadDivinerQuerySchema })
178
+ .fields({
179
+ limit: 2, order: 'asc', schemas,
180
+ })
181
+ .build()
182
+ const results = await sut.divine([query])
183
+ const resultSequences = results.map(result => result._sequence)
184
+ expect(PayloadBuilder.omitStorageMeta(results)).toStrictEqual([payloadA, payloadB])
185
+ expect(results.length).toBe(2)
186
+ expect(resultSequences[0]).toBe(insertedPayloads[0]._sequence)
187
+ expect(resultSequences[1]).toBe(insertedPayloads[1]._sequence)
188
+
189
+ const cursor = resultSequences[1]
190
+ const query2 = new PayloadBuilder<PayloadDivinerQueryPayload>({ schema: PayloadDivinerQuerySchema })
191
+ .fields({
192
+ limit: 2, cursor, order: 'asc', schemas,
193
+ })
194
+ .build()
195
+ const results2 = await sut.divine([query2])
196
+ const resultSequences2 = results2.map(result => result._sequence)
197
+ expect(results2.length).toBe(2)
198
+ expect(resultSequences2[0]).toBe(insertedPayloads[2]._sequence)
199
+ expect(resultSequences2[1]).toBe(insertedPayloads[3]._sequence)
200
+ const cursor2 = resultSequences2[1]
201
+
202
+ const query3 = new PayloadBuilder<PayloadDivinerQueryPayload>({ schema: PayloadDivinerQuerySchema })
203
+ .fields({
204
+ limit: 2, cursor: cursor2, order: 'asc', schemas,
205
+ })
206
+ .build()
207
+ const results3 = await sut.divine([query3])
208
+ expect(results3).toBeArrayOfSize(0)
209
+ })
210
+ it('test paging with multiple calls (desc)', async () => {
211
+ const schemas = ['network.xyo.test', 'network.xyo.debug']
212
+ const query = new PayloadBuilder<PayloadDivinerQueryPayload>({ schema: PayloadDivinerQuerySchema })
213
+ .fields({
214
+ limit: 2, order: 'desc', schemas,
215
+ })
216
+ .build()
217
+ const results = await sut.divine([query])
218
+ const resultSequences = results.map(result => result._sequence)
219
+ expect(results.length).toBe(2)
220
+ expect(resultSequences[0]).toBe(insertedPayloads[3]._sequence)
221
+ expect(resultSequences[1]).toBe(insertedPayloads[2]._sequence)
222
+
223
+ const cursor = resultSequences[1]
224
+ const query2 = new PayloadBuilder<PayloadDivinerQueryPayload>({ schema: PayloadDivinerQuerySchema })
225
+ .fields({
226
+ limit: 2, cursor, order: 'desc', schemas,
227
+ })
228
+ .build()
229
+ const results2 = await sut.divine([query2])
230
+ const resultSequences2 = results2.map(result => result._sequence)
231
+ expect(results2.length).toBe(2)
232
+ expect(resultSequences2[0]).toBe(insertedPayloads[1]._sequence)
233
+ expect(resultSequences2[1]).toBe(insertedPayloads[0]._sequence)
234
+ const cursor2 = resultSequences2[1]
235
+
236
+ const query3 = new PayloadBuilder<PayloadDivinerQueryPayload>({ schema: PayloadDivinerQuerySchema })
237
+ .fields({
238
+ limit: 2, cursor: cursor2, order: 'desc', schemas,
239
+ })
240
+ .build()
241
+ const results3 = await sut.divine([query3])
242
+ expect(results3).toBeArrayOfSize(0)
243
+ })
244
+ })
245
+ })
246
+ describe('custom field', () => {
247
+ describe('property', () => {
248
+ it('only returns payloads with that property', async () => {
249
+ type WithUrl = { url?: string }
250
+ const url = payloadA.url
251
+ const query = new PayloadBuilder<PayloadDivinerQueryPayload<{ url?: string }>>({ schema: PayloadDivinerQuerySchema })
252
+ .fields({ url })
253
+ .build()
254
+ const results = await sut.divine([query])
255
+ expect(results.length).toBeGreaterThan(0)
256
+ expect(results.every(result => (result as WithUrl)?.url === url)).toBe(true)
257
+ })
258
+ })
259
+ describe('array', () => {
260
+ const cases: string[][] = [['bar'], ['baz'], ['bar', 'baz']]
261
+ it.each(cases)('only returns payloads that have an array containing all the values supplied', async (...foo) => {
262
+ type WithFoo = { foo?: string[] }
263
+ const query = new PayloadBuilder<PayloadDivinerQueryPayload<{ foo?: string[] }>>({ schema: PayloadDivinerQuerySchema })
264
+ .fields({ foo })
265
+ .build()
266
+ const results = await sut.divine([query])
267
+ expect(results.length).toBeGreaterThan(0)
268
+ // eslint-disable-next-line max-nested-callbacks
269
+ expect(results.every(result => foo.every(v => (result as unknown as WithFoo)?.foo?.includes(v)))).toBe(true)
270
+ })
271
+ })
272
+ })
273
+ })
274
+ })
@@ -0,0 +1,251 @@
1
+ /* eslint-disable max-nested-callbacks */
2
+
3
+ import '@xylabs/vitest-extended'
4
+
5
+ import { delay } from '@xylabs/delay'
6
+ import { MemoryArchivist } from '@xyo-network/archivist-memory'
7
+ import type { PayloadDivinerQueryPayload } from '@xyo-network/diviner-payload-model'
8
+ import { PayloadDivinerQuerySchema } from '@xyo-network/diviner-payload-model'
9
+ import { MemoryNode } from '@xyo-network/node-memory'
10
+ import { PayloadBuilder } from '@xyo-network/payload-builder'
11
+ import type { Payload, WithStorageMeta } from '@xyo-network/payload-model'
12
+ import {
13
+ beforeAll, describe, expect, it,
14
+ } from 'vitest'
15
+
16
+ import { GenericPayloadDiviner, GenericPayloadDivinerConfigSchema } from '../Diviner.ts'
17
+
18
+ /**
19
+ * @group module
20
+ * @group diviner
21
+ */
22
+
23
+ describe('GenericPayloadDiviner', () => {
24
+ let archivist: MemoryArchivist
25
+ let sut: GenericPayloadDiviner
26
+ let node: MemoryNode
27
+ const payloadA: Payload<{ schema: string; url: string }> = {
28
+ schema: 'network.xyo.test',
29
+ url: 'https://xyo.network',
30
+ }
31
+ const payloadB: Payload<{ foo: string[]; schema: string }> = {
32
+ foo: ['bar', 'baz'],
33
+ schema: 'network.xyo.debug',
34
+ }
35
+ const payloadC: Payload<{ foo: string[]; schema: string }> = {
36
+ foo: ['one', 'two'],
37
+ schema: 'network.xyo.debug',
38
+ }
39
+ const payloadD: Payload<{ foo: string[]; schema: string }> = {
40
+ foo: ['aaa', 'bbb'],
41
+ schema: 'network.xyo.debug',
42
+ }
43
+ let insertedPayloads: WithStorageMeta<Payload>[] = []
44
+ beforeAll(async () => {
45
+ archivist = await MemoryArchivist.create({
46
+ account: 'random',
47
+ config: { name: 'test', schema: MemoryArchivist.defaultConfigSchema },
48
+ })
49
+ for (const payload of [payloadA, payloadB, payloadC, payloadD]) {
50
+ await delay(2)
51
+ const [insertedPayload] = await archivist.insert([payload])
52
+ insertedPayloads.push(insertedPayload)
53
+ }
54
+ sut = await GenericPayloadDiviner.create({
55
+ account: 'random',
56
+ config: {
57
+ archivist: archivist.address,
58
+ schema: GenericPayloadDivinerConfigSchema,
59
+ },
60
+ })
61
+ node = await MemoryNode.create({
62
+ account: 'random',
63
+ config: { schema: MemoryNode.defaultConfigSchema },
64
+ })
65
+ const modules = [archivist, sut]
66
+ await node.start()
67
+ await Promise.all(
68
+ modules.map(async (mod) => {
69
+ await node.register(mod)
70
+ await node.attach(mod.address, true)
71
+ }),
72
+ )
73
+ await sut.start()
74
+ })
75
+ describe('with filter for', () => {
76
+ describe('schema', () => {
77
+ describe('single', () => {
78
+ it.each(['network.xyo.test', 'network.xyo.debug'])('only returns payloads of that schema', async (schema) => {
79
+ const schemas = [schema]
80
+ const query = new PayloadBuilder<PayloadDivinerQueryPayload>({ schema: PayloadDivinerQuerySchema }).fields({ schemas }).build()
81
+ const results = await sut.divine([query])
82
+ expect(results.length).toBeGreaterThan(0)
83
+ expect(results.every(result => result.schema === schema)).toBe(true)
84
+ })
85
+ it('only return single payload of that schema', async () => {
86
+ const schemas = ['network.xyo.debug']
87
+ const query = new PayloadBuilder<PayloadDivinerQueryPayload>({ schema: PayloadDivinerQuerySchema })
88
+ .fields({ limit: 1, schemas })
89
+ .build()
90
+ const results = await sut.divine([query])
91
+ expect(results.length).toBe(1)
92
+ expect(await PayloadBuilder.dataHash(results[0])).toBe(await PayloadBuilder.dataHash(insertedPayloads[3]))
93
+ expect(results.every(result => result.schema === 'network.xyo.debug')).toBe(true)
94
+ })
95
+ it('only return single payload of that schema (desc)', async () => {
96
+ const schemas = ['network.xyo.debug']
97
+ const query = new PayloadBuilder<PayloadDivinerQueryPayload>({ schema: PayloadDivinerQuerySchema })
98
+ .fields({
99
+ limit: 1, order: 'desc', schemas,
100
+ })
101
+ .build()
102
+ const results = await sut.divine([query])
103
+ expect(results.length).toBe(1)
104
+ expect(PayloadBuilder.omitStorageMeta(results[0])).toStrictEqual(payloadD)
105
+ expect(await PayloadBuilder.dataHash(results[0])).toBe(await PayloadBuilder.dataHash(insertedPayloads[3]))
106
+ expect(results.every(result => result.schema === 'network.xyo.debug')).toBe(true)
107
+ })
108
+ it('only return single payload of that schema (asc)', async () => {
109
+ const schemas = ['network.xyo.debug']
110
+ const query = new PayloadBuilder<PayloadDivinerQueryPayload>({ schema: PayloadDivinerQuerySchema })
111
+ .fields({
112
+ limit: 1, order: 'asc', schemas,
113
+ })
114
+ .build()
115
+ const results = await sut.divine([query])
116
+ expect(results.length).toBe(1)
117
+ expect(PayloadBuilder.omitStorageMeta(results[0])).toStrictEqual(payloadB)
118
+ expect(await PayloadBuilder.dataHash(results[0])).toBe(await PayloadBuilder.dataHash(payloadB))
119
+ expect(results.every(result => result.schema === 'network.xyo.debug')).toBe(true)
120
+ })
121
+ })
122
+ describe('multiple', () => {
123
+ it('only returns payloads of that schema', async () => {
124
+ const schemas = ['network.xyo.test', 'network.xyo.debug']
125
+ const query = new PayloadBuilder<PayloadDivinerQueryPayload>({ schema: PayloadDivinerQuerySchema })
126
+ .fields({ schemas })
127
+ .build()
128
+ const results = await sut.divine([query])
129
+ expect(results.length).toBeGreaterThan(0)
130
+ expect(results.every(result => schemas.includes(result.schema))).toBe(true)
131
+ })
132
+ })
133
+ describe('paging', () => {
134
+ it('test paging with multiple calls (asc)', async () => {
135
+ const schemas = ['network.xyo.test', 'network.xyo.debug']
136
+ const query = new PayloadBuilder<PayloadDivinerQueryPayload>({ schema: PayloadDivinerQuerySchema })
137
+ .fields({
138
+ limit: 2, order: 'asc', schemas,
139
+ })
140
+ .build()
141
+ const results = await sut.divine([query])
142
+ console.warn('results', results)
143
+ const resultSequences = results.map(result => result._sequence)
144
+ expect(PayloadBuilder.omitStorageMeta(results)).toStrictEqual([payloadA, payloadB])
145
+ expect(results.length).toBe(2)
146
+ expect(resultSequences[0]).toBe(insertedPayloads[0]._sequence)
147
+ expect(resultSequences[1]).toBe(insertedPayloads[1]._sequence)
148
+
149
+ const cursor = resultSequences[1]
150
+ const query2 = new PayloadBuilder<PayloadDivinerQueryPayload>({ schema: PayloadDivinerQuerySchema })
151
+ .fields({
152
+ limit: 2, cursor, order: 'asc', schemas,
153
+ })
154
+ .build()
155
+ const results2 = await sut.divine([query2])
156
+ const resultSequences2 = results2.map(result => result._sequence)
157
+ expect(results2.length).toBe(2)
158
+ expect(resultSequences2[0]).toBe(insertedPayloads[2]._sequence)
159
+ expect(resultSequences2[1]).toBe(insertedPayloads[3]._sequence)
160
+ const cursor2 = resultSequences2[1]
161
+
162
+ const query3 = new PayloadBuilder<PayloadDivinerQueryPayload>({ schema: PayloadDivinerQuerySchema })
163
+ .fields({
164
+ limit: 2, cursor: cursor2, order: 'asc', schemas,
165
+ })
166
+ .build()
167
+ const results3 = await sut.divine([query3])
168
+ expect(results3).toBeArrayOfSize(0)
169
+ })
170
+ it('test paging with multiple calls (desc)', async () => {
171
+ const schemas = ['network.xyo.test', 'network.xyo.debug']
172
+ const query = new PayloadBuilder<PayloadDivinerQueryPayload>({ schema: PayloadDivinerQuerySchema })
173
+ .fields({
174
+ limit: 2, order: 'desc', schemas,
175
+ })
176
+ .build()
177
+ const results = await sut.divine([query])
178
+ const resultSequences = results.map(result => result._sequence)
179
+ expect(results.length).toBe(2)
180
+ expect(resultSequences[0]).toBe(insertedPayloads[3]._sequence)
181
+ expect(resultSequences[1]).toBe(insertedPayloads[2]._sequence)
182
+
183
+ const cursor = resultSequences[1]
184
+ const query2 = new PayloadBuilder<PayloadDivinerQueryPayload>({ schema: PayloadDivinerQuerySchema })
185
+ .fields({
186
+ limit: 2, cursor, order: 'desc', schemas,
187
+ })
188
+ .build()
189
+ const results2 = await sut.divine([query2])
190
+ const resultSequences2 = results2.map(result => result._sequence)
191
+ expect(results2.length).toBe(2)
192
+ expect(resultSequences2[0]).toBe(insertedPayloads[1]._sequence)
193
+ expect(resultSequences2[1]).toBe(insertedPayloads[0]._sequence)
194
+ const cursor2 = resultSequences2[1]
195
+
196
+ const query3 = new PayloadBuilder<PayloadDivinerQueryPayload>({ schema: PayloadDivinerQuerySchema })
197
+ .fields({
198
+ limit: 2, cursor: cursor2, order: 'desc', schemas,
199
+ })
200
+ .build()
201
+ const results3 = await sut.divine([query3])
202
+ expect(results3).toBeArrayOfSize(0)
203
+ })
204
+ })
205
+ })
206
+ describe('custom field', () => {
207
+ describe('property', () => {
208
+ it('only returns payloads with that property', async () => {
209
+ type WithUrl = { url?: string }
210
+ const url = payloadA.url
211
+ const query = new PayloadBuilder<PayloadDivinerQueryPayload & WithUrl>({ schema: PayloadDivinerQuerySchema })
212
+ .fields({ url })
213
+ .build()
214
+ const results = await sut.divine([query])
215
+ expect(results.length).toBeGreaterThan(0)
216
+ expect(results.every(result => (result as WithUrl)?.url === url)).toBe(true)
217
+ })
218
+ })
219
+ describe('array', () => {
220
+ const cases: string[][] = [['bar'], ['baz'], ['bar', 'baz']]
221
+ it.each(cases)('only returns payloads that have an array containing all the values supplied', async (...foo) => {
222
+ type WithFoo = { foo?: string[] }
223
+ const query = new PayloadBuilder<PayloadDivinerQueryPayload & WithFoo>({ schema: PayloadDivinerQuerySchema })
224
+ .fields({ foo })
225
+ .build()
226
+ const results = await sut.divine([query])
227
+ expect(results.length).toBeGreaterThan(0)
228
+ expect(results.every(result => foo.every(v => (result as unknown as WithFoo)?.foo?.includes(v)))).toBe(true)
229
+ })
230
+ })
231
+ })
232
+ })
233
+ describe('on events', () => {
234
+ it ('should listen to clear event', async () => {
235
+ // clear the memory archivist before we test
236
+ await archivist.clear()
237
+ // insert a single payload
238
+ const payload: Payload = { schema: 'network.xyo.test' }
239
+ await archivist.insert([payload])
240
+ // build query for a single schema
241
+ const query = new PayloadBuilder<PayloadDivinerQueryPayload>({ schema: PayloadDivinerQuerySchema })
242
+ .fields({ schemas: ['network.xyo.test'] })
243
+ .build()
244
+ // divine the payload
245
+ const results = await sut.divine([query])
246
+ // should be one but has two because of some payload inserted before cleared the archivist
247
+ // but not the diviner cache
248
+ expect(results).toBeArrayOfSize(1)
249
+ })
250
+ })
251
+ })
package/xy.config.ts DELETED
@@ -1,10 +0,0 @@
1
- import type { XyTsupConfig } from '@xylabs/ts-scripts-yarn3'
2
- const config: XyTsupConfig = {
3
- compile: {
4
- browser: {},
5
- neutral: { src: true },
6
- node: {},
7
- },
8
- }
9
-
10
- export default config