@xyo-network/diviner-boundwitness-indexeddb 3.5.2 → 3.6.0-rc.10

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,4 +1,4 @@
1
- import type { BoundWitness } from '@xyo-network/boundwitness-model';
1
+ import { type BoundWitness } from '@xyo-network/boundwitness-model';
2
2
  import { BoundWitnessDiviner } from '@xyo-network/diviner-boundwitness-abstract';
3
3
  import type { BoundWitnessDivinerQueryPayload } from '@xyo-network/diviner-boundwitness-model';
4
4
  import type { Schema } from '@xyo-network/payload-model';
@@ -15,7 +15,7 @@ export declare class IndexedDbBoundWitnessDiviner<TParams extends IndexedDbBound
15
15
  */
16
16
  get dbName(): string;
17
17
  /**
18
- * The database version. If not supplied via config, it defaults to 1.
18
+ * The database version. If not supplied via config, it defaults to the archivist default version.
19
19
  */
20
20
  get dbVersion(): number;
21
21
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../src/Diviner.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAA;AAEnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,4CAA4C,CAAA;AAChF,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,yCAAyC,CAAA;AAG9F,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAA;AAKxD,OAAO,KAAK,EAAE,kCAAkC,EAAE,MAAM,aAAa,CAAA;AAmBrE,qBAAa,4BAA4B,CACvC,OAAO,SAAS,kCAAkC,GAAG,kCAAkC,EACvF,GAAG,SAAS,+BAA+B,GAAG,+BAA+B,EAC7E,IAAI,SAAS,YAAY,GAAG,YAAY,CACxC,SAAQ,mBAAmB,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC;IAC/C,gBAAyB,aAAa,EAAE,MAAM,EAAE,CAAqE;IACrH,gBAAyB,mBAAmB,EAAE,MAAM,CAA2C;IAE/F;;;;;;OAMG;IACH,IAAI,MAAM,WAET;IAED;;OAEG;IACH,IAAI,SAAS,WAEZ;IAED;;;OAGG;IACH,IAAI,SAAS,WAEZ;cAEwB,aAAa,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;cAgEhD,YAAY;IAQrC;;;OAGG;YACW,mBAAmB;IAwBjC;;;;OAIG;YACW,QAAQ;CAcvB"}
1
+ {"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../src/Diviner.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,YAAY,EAAkB,MAAM,iCAAiC,CAAA;AACnF,OAAO,EAAE,mBAAmB,EAAE,MAAM,4CAA4C,CAAA;AAChF,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,yCAAyC,CAAA;AAE9F,OAAO,KAAK,EACV,MAAM,EACP,MAAM,4BAA4B,CAAA;AAKnC,OAAO,KAAK,EAAE,kCAAkC,EAAE,MAAM,aAAa,CAAA;AAmBrE,qBAAa,4BAA4B,CACvC,OAAO,SAAS,kCAAkC,GAAG,kCAAkC,EACvF,GAAG,SAAS,+BAA+B,GAAG,+BAA+B,EAC7E,IAAI,SAAS,YAAY,GAAG,YAAY,CACxC,SAAQ,mBAAmB,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC;IAC/C,gBAAyB,aAAa,EAAE,MAAM,EAAE,CAAqE;IACrH,gBAAyB,mBAAmB,EAAE,MAAM,CAA2C;IAE/F;;;;;;OAMG;IACH,IAAI,MAAM,WAET;IAED;;OAEG;IACH,IAAI,SAAS,WAEZ;IAED;;;OAGG;IACH,IAAI,SAAS,WAEZ;cAEwB,aAAa,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;cAmEhD,YAAY;IAQrC;;;OAGG;YACW,mBAAmB;IAwBjC;;;;OAIG;YACW,QAAQ;CAcvB"}
@@ -10,12 +10,12 @@ var IndexedDbBoundWitnessDivinerConfigSchema = `${IndexedDbBoundWitnessDivinerSc
10
10
 
11
11
  // src/Diviner.ts
12
12
  import { containsAll } from "@xylabs/array";
13
+ import { assertEx } from "@xylabs/assert";
13
14
  import { exists } from "@xylabs/exists";
14
15
  import { IndexedDbArchivist } from "@xyo-network/archivist-indexeddb";
15
- import { BoundWitnessSchema, isBoundWitness } from "@xyo-network/boundwitness-model";
16
+ import { isBoundWitness } from "@xyo-network/boundwitness-model";
16
17
  import { BoundWitnessDiviner } from "@xyo-network/diviner-boundwitness-abstract";
17
18
  import { isBoundWitnessDivinerQueryPayload } from "@xyo-network/diviner-boundwitness-model";
18
- import { PayloadBuilder } from "@xyo-network/payload-builder";
19
19
  import { openDB } from "idb";
20
20
  var bwValueFilter = /* @__PURE__ */ __name((key, values) => {
21
21
  if (!values || values?.length === 0) return void 0;
@@ -44,7 +44,7 @@ var IndexedDbBoundWitnessDiviner = class extends BoundWitnessDiviner {
44
44
  return this.config?.dbName ?? this.config?.archivist ?? IndexedDbArchivist.defaultDbName;
45
45
  }
46
46
  /**
47
- * The database version. If not supplied via config, it defaults to 1.
47
+ * The database version. If not supplied via config, it defaults to the archivist default version.
48
48
  */
49
49
  get dbVersion() {
50
50
  return this.config?.dbVersion ?? IndexedDbArchivist.defaultDbVersion;
@@ -57,30 +57,34 @@ var IndexedDbBoundWitnessDiviner = class extends BoundWitnessDiviner {
57
57
  return this.config?.storeName ?? IndexedDbArchivist.defaultStoreName;
58
58
  }
59
59
  async divineHandler(payloads) {
60
- const query = payloads?.filter(isBoundWitnessDivinerQueryPayload)?.pop();
60
+ const query = payloads?.find(isBoundWitnessDivinerQueryPayload);
61
61
  if (!query) return [];
62
62
  const result = await this.tryUseDb(async (db) => {
63
- const { addresses, payload_hashes, payload_schemas, limit, offset, order } = query;
63
+ const { addresses, payload_hashes, payload_schemas, limit, cursor, order } = query;
64
64
  const tx = db.transaction(this.storeName, "readonly");
65
65
  const store = tx.objectStore(this.storeName);
66
66
  const results = [];
67
- let parsedOffset = offset ?? 0;
67
+ const parsedCursor = cursor;
68
68
  const parsedLimit = limit ?? 10;
69
- const direction = order === "desc" ? "prev" : "next";
70
69
  const valueFilters = [
70
+ isBoundWitness,
71
71
  bwValueFilter("addresses", addresses),
72
72
  bwValueFilter("payload_hashes", payload_hashes),
73
73
  bwValueFilter("payload_schemas", payload_schemas)
74
74
  ].filter(exists);
75
- let cursor = await store.index(IndexedDbArchivist.schemaIndexName).openCursor(IDBKeyRange.only(BoundWitnessSchema), direction);
76
- if (valueFilters.length === 0) {
77
- while (cursor && parsedOffset > 0) {
78
- cursor = await cursor.advance(parsedOffset);
79
- parsedOffset = 0;
75
+ const direction = order === "desc" ? "prev" : "next";
76
+ const sequenceIndex = assertEx(store.index(IndexedDbArchivist.sequenceIndexName), () => "Failed to get sequence index");
77
+ let dbCursor = assertEx(await sequenceIndex.openCursor(null, direction), () => `Failed to get cursor [${parsedCursor}, ${cursor}]`);
78
+ if (parsedCursor !== void 0) {
79
+ let currentSequence;
80
+ while (dbCursor && currentSequence !== parsedCursor) {
81
+ const current = dbCursor.value;
82
+ currentSequence = current?._sequence;
83
+ dbCursor = await dbCursor.advance(1);
80
84
  }
81
85
  }
82
- while (cursor && results.length < parsedLimit) {
83
- const value = cursor.value;
86
+ while (dbCursor && results.length < parsedLimit) {
87
+ const value = dbCursor.value;
84
88
  if (value) {
85
89
  if (valueFilters.length > 0) {
86
90
  if (valueFilters.every((filter) => filter(value))) {
@@ -91,15 +95,13 @@ var IndexedDbBoundWitnessDiviner = class extends BoundWitnessDiviner {
91
95
  }
92
96
  }
93
97
  try {
94
- cursor = await cursor.continue();
98
+ dbCursor = await dbCursor.continue();
95
99
  } catch {
96
100
  break;
97
101
  }
98
102
  }
99
103
  await tx.done;
100
- return await Promise.all(results.filter(isBoundWitness).map((bw) => {
101
- return PayloadBuilder.build(bw);
102
- }));
104
+ return results;
103
105
  });
104
106
  return result ?? [];
105
107
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/Schema.ts","../../src/Config.ts","../../src/Diviner.ts"],"sourcesContent":["import { BoundWitnessDivinerSchema } from '@xyo-network/diviner-boundwitness-model'\n\nexport const IndexedDbBoundWitnessDivinerSchema = `${BoundWitnessDivinerSchema}.indexeddb` as const\nexport type IndexedDbBoundWitnessDivinerSchema = typeof IndexedDbBoundWitnessDivinerSchema\n","import type { IndexDescription } from '@xyo-network/archivist-model'\nimport type { DivinerConfig } from '@xyo-network/diviner-model'\n\nimport { IndexedDbBoundWitnessDivinerSchema } from './Schema.ts'\n\nexport const IndexedDbBoundWitnessDivinerConfigSchema = `${IndexedDbBoundWitnessDivinerSchema}.config` as const\nexport type IndexedDbBoundWitnessDivinerConfigSchema = typeof IndexedDbBoundWitnessDivinerConfigSchema\n\nexport type IndexedDbBoundWitnessDivinerConfig = DivinerConfig<{\n /**\n * The database name\n */\n dbName?: string\n /**\n * The version of the DB, defaults to 1\n */\n dbVersion?: number\n schema: IndexedDbBoundWitnessDivinerConfigSchema\n /**\n * The storage configuration\n * // TODO: Hoist to main diviner config\n */\n storage?: {\n /**\n * The indexes to create on the object store\n */\n indexes?: IndexDescription[]\n }\n /**\n * The name of the object store\n */\n storeName?: string\n}>\n","import { containsAll } from '@xylabs/array'\nimport { exists } from '@xylabs/exists'\nimport { IndexedDbArchivist } from '@xyo-network/archivist-indexeddb'\nimport type { BoundWitness } from '@xyo-network/boundwitness-model'\nimport { BoundWitnessSchema, isBoundWitness } from '@xyo-network/boundwitness-model'\nimport { BoundWitnessDiviner } from '@xyo-network/diviner-boundwitness-abstract'\nimport type { BoundWitnessDivinerQueryPayload } from '@xyo-network/diviner-boundwitness-model'\nimport { isBoundWitnessDivinerQueryPayload } from '@xyo-network/diviner-boundwitness-model'\nimport { PayloadBuilder } from '@xyo-network/payload-builder'\nimport type { Schema } from '@xyo-network/payload-model'\nimport type { IDBPDatabase } from 'idb'\nimport { openDB } from 'idb'\n\nimport { IndexedDbBoundWitnessDivinerConfigSchema } from './Config.ts'\nimport type { IndexedDbBoundWitnessDivinerParams } from './Params.ts'\n\ninterface BoundWitnessStore {\n [s: string]: BoundWitness\n}\n\ntype ValueFilter = (bw?: BoundWitness | null) => boolean\n\nconst bwValueFilter = (\n key: keyof Pick<BoundWitness, 'addresses' | 'payload_hashes' | 'payload_schemas'>,\n values?: string[],\n): ValueFilter | undefined => {\n if (!values || values?.length === 0) return undefined\n return (bw) => {\n if (!bw) return false\n return containsAll(bw[key], values)\n }\n}\n\nexport class IndexedDbBoundWitnessDiviner<\n TParams extends IndexedDbBoundWitnessDivinerParams = IndexedDbBoundWitnessDivinerParams,\n TIn extends BoundWitnessDivinerQueryPayload = BoundWitnessDivinerQueryPayload,\n TOut extends BoundWitness = BoundWitness,\n> extends BoundWitnessDiviner<TParams, TIn, TOut> {\n static override readonly configSchemas: Schema[] = [...super.configSchemas, IndexedDbBoundWitnessDivinerConfigSchema]\n static override readonly defaultConfigSchema: Schema = IndexedDbBoundWitnessDivinerConfigSchema\n\n /**\n * The database name. If not supplied via config, it defaults\n * to the archivist's name and if archivist's name is not supplied,\n * it defaults to `archivist`. This behavior\n * biases towards a single, isolated DB per archivist which seems to\n * make the most sense for 99% of use cases.\n */\n get dbName() {\n return this.config?.dbName ?? this.config?.archivist ?? IndexedDbArchivist.defaultDbName\n }\n\n /**\n * The database version. If not supplied via config, it defaults to 1.\n */\n get dbVersion() {\n return this.config?.dbVersion ?? IndexedDbArchivist.defaultDbVersion\n }\n\n /**\n * The name of the object store. If not supplied via config, it defaults\n * to `payloads`.\n */\n get storeName() {\n return this.config?.storeName ?? IndexedDbArchivist.defaultStoreName\n }\n\n protected override async divineHandler(payloads?: TIn[]): Promise<TOut[]> {\n const query = payloads?.filter(isBoundWitnessDivinerQueryPayload)?.pop()\n if (!query) return []\n\n const result = await this.tryUseDb(async (db) => {\n const {\n addresses, payload_hashes, payload_schemas, limit, offset, order,\n } = query\n const tx = db.transaction(this.storeName, 'readonly')\n const store = tx.objectStore(this.storeName)\n const results: TOut[] = []\n let parsedOffset = offset ?? 0\n const parsedLimit = limit ?? 10\n const direction: IDBCursorDirection = order === 'desc' ? 'prev' : 'next'\n const valueFilters: ValueFilter[] = [\n bwValueFilter('addresses', addresses),\n bwValueFilter('payload_hashes', payload_hashes),\n bwValueFilter('payload_schemas', payload_schemas),\n ].filter(exists)\n // Only iterate over BWs\n let cursor = await store.index(IndexedDbArchivist.schemaIndexName).openCursor(IDBKeyRange.only(BoundWitnessSchema), direction)\n\n // If we're filtering on more than just the schema, we need to\n // iterate through all the results\n if (valueFilters.length === 0) {\n // Skip records until the offset is reached\n while (cursor && parsedOffset > 0) {\n cursor = await cursor.advance(parsedOffset)\n parsedOffset = 0 // Reset offset after skipping\n }\n }\n // Collect results up to the limit\n while (cursor && results.length < parsedLimit) {\n const value = cursor.value\n if (value) {\n // If we're filtering on more than just the schema\n if (valueFilters.length > 0) {\n // Ensure all filters pass\n if (valueFilters.every(filter => filter(value))) {\n // Then save the value\n results.push(value)\n }\n } else {\n // Otherwise just save the value\n results.push(value)\n }\n }\n try {\n cursor = await cursor.continue()\n } catch {\n break\n }\n }\n await tx.done\n // Remove any metadata before returning to the client\n return await Promise.all(\n results.filter(isBoundWitness).map((bw) => {\n return PayloadBuilder.build(bw)\n }),\n )\n })\n return result ?? []\n }\n\n protected override async startHandler() {\n await super.startHandler()\n // NOTE: Do not eager initialize the DB here. It will cause the\n // DB to be created by this process and then the DB will be\n // in a bad state for other processes that need to create the DB.\n return true\n }\n\n /**\n * Checks that the desired DB/objectStore exists and is initialized to the correct version\n * @returns The initialized DB or undefined if it does not exist in the desired state\n */\n private async tryGetInitializedDb(): Promise<IDBPDatabase<BoundWitnessStore> | undefined> {\n // Enumerate the DBs\n const dbs = await indexedDB.databases()\n // Check that the DB exists at the desired version\n const dbExists = dbs.some((db) => {\n return db.name === this.dbName && db.version === this.dbVersion\n })\n // If the DB exists at the desired version\n if (dbExists) {\n // If the db does exist, open it\n const db = await openDB<BoundWitnessStore>(this.dbName, this.dbVersion)\n // Check that the desired objectStore exists\n const storeExists = db.objectStoreNames.contains(this.storeName)\n // If the correct db/version/objectStore exists\n if (storeExists) {\n return db\n } else {\n // Otherwise close the db so the process that is going to update the\n // db can open it\n db.close()\n }\n }\n }\n\n /**\n * Executes a callback with the initialized DB and then closes the db\n * @param callback The method to execute with the initialized DB\n * @returns\n */\n private async tryUseDb<T>(callback: (db: IDBPDatabase<BoundWitnessStore>) => Promise<T> | T): Promise<T | undefined> {\n // Get the initialized DB\n const db = await this.tryGetInitializedDb()\n if (db) {\n try {\n // Perform the callback\n return await callback(db)\n } finally {\n // Close the DB\n db.close()\n }\n }\n return undefined\n }\n}\n"],"mappings":"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","names":["BoundWitnessDivinerSchema","IndexedDbBoundWitnessDivinerSchema","IndexedDbBoundWitnessDivinerConfigSchema","IndexedDbBoundWitnessDivinerSchema","containsAll","exists","IndexedDbArchivist","BoundWitnessSchema","isBoundWitness","BoundWitnessDiviner","isBoundWitnessDivinerQueryPayload","PayloadBuilder","openDB","bwValueFilter","key","values","length","undefined","bw","containsAll","IndexedDbBoundWitnessDiviner","BoundWitnessDiviner","configSchemas","IndexedDbBoundWitnessDivinerConfigSchema","defaultConfigSchema","dbName","config","archivist","IndexedDbArchivist","defaultDbName","dbVersion","defaultDbVersion","storeName","defaultStoreName","divineHandler","payloads","query","filter","isBoundWitnessDivinerQueryPayload","pop","result","tryUseDb","db","addresses","payload_hashes","payload_schemas","limit","offset","order","tx","transaction","store","objectStore","results","parsedOffset","parsedLimit","direction","valueFilters","exists","cursor","index","schemaIndexName","openCursor","IDBKeyRange","only","BoundWitnessSchema","advance","value","every","push","continue","done","Promise","all","isBoundWitness","map","PayloadBuilder","build","startHandler","tryGetInitializedDb","dbs","indexedDB","databases","dbExists","some","name","version","openDB","storeExists","objectStoreNames","contains","close","callback"]}
1
+ {"version":3,"sources":["../../src/Schema.ts","../../src/Config.ts","../../src/Diviner.ts"],"sourcesContent":["import { BoundWitnessDivinerSchema } from '@xyo-network/diviner-boundwitness-model'\n\nexport const IndexedDbBoundWitnessDivinerSchema = `${BoundWitnessDivinerSchema}.indexeddb` as const\nexport type IndexedDbBoundWitnessDivinerSchema = typeof IndexedDbBoundWitnessDivinerSchema\n","import type { IndexDescription } from '@xyo-network/archivist-model'\nimport type { DivinerConfig } from '@xyo-network/diviner-model'\n\nimport { IndexedDbBoundWitnessDivinerSchema } from './Schema.ts'\n\nexport const IndexedDbBoundWitnessDivinerConfigSchema = `${IndexedDbBoundWitnessDivinerSchema}.config` as const\nexport type IndexedDbBoundWitnessDivinerConfigSchema = typeof IndexedDbBoundWitnessDivinerConfigSchema\n\nexport type IndexedDbBoundWitnessDivinerConfig = DivinerConfig<{\n /**\n * The database name\n */\n dbName?: string\n /**\n * The version of the DB, defaults to 1\n */\n dbVersion?: number\n schema: IndexedDbBoundWitnessDivinerConfigSchema\n /**\n * The storage configuration\n * // TODO: Hoist to main diviner config\n */\n storage?: {\n /**\n * The indexes to create on the object store\n */\n indexes?: IndexDescription[]\n }\n /**\n * The name of the object store\n */\n storeName?: string\n}>\n","import { containsAll } from '@xylabs/array'\nimport { assertEx } from '@xylabs/assert'\nimport { exists } from '@xylabs/exists'\nimport { IndexedDbArchivist } from '@xyo-network/archivist-indexeddb'\nimport { type BoundWitness, isBoundWitness } from '@xyo-network/boundwitness-model'\nimport { BoundWitnessDiviner } from '@xyo-network/diviner-boundwitness-abstract'\nimport type { BoundWitnessDivinerQueryPayload } from '@xyo-network/diviner-boundwitness-model'\nimport { isBoundWitnessDivinerQueryPayload } from '@xyo-network/diviner-boundwitness-model'\nimport type {\n Schema, Sequence, WithStorageMeta,\n} from '@xyo-network/payload-model'\nimport type { IDBPCursorWithValue, IDBPDatabase } from 'idb'\nimport { openDB } from 'idb'\n\nimport { IndexedDbBoundWitnessDivinerConfigSchema } from './Config.ts'\nimport type { IndexedDbBoundWitnessDivinerParams } from './Params.ts'\n\ninterface BoundWitnessStore {\n [s: string]: WithStorageMeta<BoundWitness>\n}\n\ntype ValueFilter = (bw?: BoundWitness | null) => boolean\n\nconst bwValueFilter = (\n key: keyof Pick<BoundWitness, 'addresses' | 'payload_hashes' | 'payload_schemas'>,\n values?: string[],\n): ValueFilter | undefined => {\n if (!values || values?.length === 0) return undefined\n return (bw) => {\n if (!bw) return false\n return containsAll(bw[key], values)\n }\n}\n\nexport class IndexedDbBoundWitnessDiviner<\n TParams extends IndexedDbBoundWitnessDivinerParams = IndexedDbBoundWitnessDivinerParams,\n TIn extends BoundWitnessDivinerQueryPayload = BoundWitnessDivinerQueryPayload,\n TOut extends BoundWitness = BoundWitness,\n> extends BoundWitnessDiviner<TParams, TIn, TOut> {\n static override readonly configSchemas: Schema[] = [...super.configSchemas, IndexedDbBoundWitnessDivinerConfigSchema]\n static override readonly defaultConfigSchema: Schema = IndexedDbBoundWitnessDivinerConfigSchema\n\n /**\n * The database name. If not supplied via config, it defaults\n * to the archivist's name and if archivist's name is not supplied,\n * it defaults to `archivist`. This behavior\n * biases towards a single, isolated DB per archivist which seems to\n * make the most sense for 99% of use cases.\n */\n get dbName() {\n return this.config?.dbName ?? this.config?.archivist ?? IndexedDbArchivist.defaultDbName\n }\n\n /**\n * The database version. If not supplied via config, it defaults to the archivist default version.\n */\n get dbVersion() {\n return this.config?.dbVersion ?? IndexedDbArchivist.defaultDbVersion\n }\n\n /**\n * The name of the object store. If not supplied via config, it defaults\n * to `payloads`.\n */\n get storeName() {\n return this.config?.storeName ?? IndexedDbArchivist.defaultStoreName\n }\n\n protected override async divineHandler(payloads?: TIn[]): Promise<TOut[]> {\n const query = payloads?.find(isBoundWitnessDivinerQueryPayload)\n if (!query) return []\n const result = await this.tryUseDb(async (db) => {\n const {\n addresses, payload_hashes, payload_schemas, limit, cursor, order,\n } = query\n const tx = db.transaction(this.storeName, 'readonly')\n const store = tx.objectStore(this.storeName)\n const results: TOut[] = []\n const parsedCursor = cursor\n const parsedLimit = limit ?? 10\n const valueFilters: ValueFilter[] = [\n isBoundWitness,\n bwValueFilter('addresses', addresses),\n bwValueFilter('payload_hashes', payload_hashes),\n bwValueFilter('payload_schemas', payload_schemas),\n ].filter(exists)\n const direction: IDBCursorDirection = order === 'desc' ? 'prev' : 'next'\n\n // Iterate all records using the sequence index\n const sequenceIndex = assertEx(store.index(IndexedDbArchivist.sequenceIndexName), () => 'Failed to get sequence index')\n let dbCursor: IDBPCursorWithValue<BoundWitnessStore, [string], string, string, 'readonly'> | null\n = assertEx(await sequenceIndex.openCursor(null, direction), () => `Failed to get cursor [${parsedCursor}, ${cursor}]`)\n\n // If a cursor was supplied\n if (parsedCursor !== undefined) {\n let currentSequence: Sequence | undefined\n // Skip records until the supplied cursor offset is reached\n while (dbCursor && currentSequence !== parsedCursor) {\n // Find the sequence of the current record\n const current: WithStorageMeta<BoundWitness> = dbCursor.value\n currentSequence = current?._sequence\n // Advance one record beyond the cursor\n dbCursor = await dbCursor.advance(1)\n }\n }\n\n // Collect results up to the limit\n while (dbCursor && results.length < parsedLimit) {\n const value = dbCursor.value\n if (value) {\n // If we're filtering on more than just the schema\n if (valueFilters.length > 0) {\n // Ensure all filters pass\n if (valueFilters.every(filter => filter(value))) {\n // Then save the value\n results.push(value)\n }\n } else {\n // Otherwise just save the value\n results.push(value)\n }\n }\n try {\n dbCursor = await dbCursor.continue()\n } catch {\n break\n }\n }\n await tx.done\n // Remove any metadata before returning to the client\n return results\n })\n return result ?? []\n }\n\n protected override async startHandler() {\n await super.startHandler()\n // NOTE: Do not eager initialize the DB here. It will cause the\n // DB to be created by this process and then the DB will be\n // in a bad state for other processes that need to create the DB.\n return true\n }\n\n /**\n * Checks that the desired DB/objectStore exists and is initialized to the correct version\n * @returns The initialized DB or undefined if it does not exist in the desired state\n */\n private async tryGetInitializedDb(): Promise<IDBPDatabase<BoundWitnessStore> | undefined> {\n // Enumerate the DBs\n const dbs = await indexedDB.databases()\n // Check that the DB exists at the desired version\n const dbExists = dbs.some((db) => {\n return db.name === this.dbName && db.version === this.dbVersion\n })\n // If the DB exists at the desired version\n if (dbExists) {\n // If the db does exist, open it\n const db = await openDB<BoundWitnessStore>(this.dbName, this.dbVersion)\n // Check that the desired objectStore exists\n const storeExists = db.objectStoreNames.contains(this.storeName)\n // If the correct db/version/objectStore exists\n if (storeExists) {\n return db\n } else {\n // Otherwise close the db so the process that is going to update the\n // db can open it\n db.close()\n }\n }\n }\n\n /**\n * Executes a callback with the initialized DB and then closes the db\n * @param callback The method to execute with the initialized DB\n * @returns\n */\n private async tryUseDb<T>(callback: (db: IDBPDatabase<BoundWitnessStore>) => Promise<T> | T): Promise<T | undefined> {\n // Get the initialized DB\n const db = await this.tryGetInitializedDb()\n if (db) {\n try {\n // Perform the callback\n return await callback(db)\n } finally {\n // Close the DB\n db.close()\n }\n }\n return undefined\n }\n}\n"],"mappings":"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","names":["BoundWitnessDivinerSchema","IndexedDbBoundWitnessDivinerSchema","IndexedDbBoundWitnessDivinerConfigSchema","IndexedDbBoundWitnessDivinerSchema","containsAll","assertEx","exists","IndexedDbArchivist","isBoundWitness","BoundWitnessDiviner","isBoundWitnessDivinerQueryPayload","openDB","bwValueFilter","key","values","length","undefined","bw","containsAll","IndexedDbBoundWitnessDiviner","BoundWitnessDiviner","configSchemas","IndexedDbBoundWitnessDivinerConfigSchema","defaultConfigSchema","dbName","config","archivist","IndexedDbArchivist","defaultDbName","dbVersion","defaultDbVersion","storeName","defaultStoreName","divineHandler","payloads","query","find","isBoundWitnessDivinerQueryPayload","result","tryUseDb","db","addresses","payload_hashes","payload_schemas","limit","cursor","order","tx","transaction","store","objectStore","results","parsedCursor","parsedLimit","valueFilters","isBoundWitness","filter","exists","direction","sequenceIndex","assertEx","index","sequenceIndexName","dbCursor","openCursor","currentSequence","current","value","_sequence","advance","every","push","continue","done","startHandler","tryGetInitializedDb","dbs","indexedDB","databases","dbExists","some","name","version","openDB","storeExists","objectStoreNames","contains","close","callback"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xyo-network/diviner-boundwitness-indexeddb",
3
- "version": "3.5.2",
3
+ "version": "3.6.0-rc.10",
4
4
  "description": "Primary SDK for using XYO Protocol 2.0",
5
5
  "homepage": "https://xyo.network",
6
6
  "bugs": {
@@ -29,32 +29,34 @@
29
29
  "module": "dist/browser/index.mjs",
30
30
  "types": "dist/browser/index.d.ts",
31
31
  "dependencies": {
32
- "@xylabs/array": "^4.4.9",
33
- "@xylabs/exists": "^4.4.9",
34
- "@xyo-network/archivist-indexeddb": "^3.5.2",
35
- "@xyo-network/archivist-model": "^3.5.2",
36
- "@xyo-network/boundwitness-model": "^3.5.2",
37
- "@xyo-network/diviner-boundwitness-abstract": "^3.5.2",
38
- "@xyo-network/diviner-boundwitness-model": "^3.5.2",
39
- "@xyo-network/diviner-model": "^3.5.2",
40
- "@xyo-network/module-model": "^3.5.2",
41
- "@xyo-network/payload-builder": "^3.5.2",
42
- "@xyo-network/payload-model": "^3.5.2",
43
- "idb": "^8.0.0"
32
+ "@xylabs/array": "^4.4.21",
33
+ "@xylabs/assert": "^4.4.21",
34
+ "@xylabs/exists": "^4.4.21",
35
+ "@xyo-network/archivist-indexeddb": "^3.6.0-rc.10",
36
+ "@xyo-network/archivist-model": "^3.6.0-rc.10",
37
+ "@xyo-network/boundwitness-model": "^3.6.0-rc.10",
38
+ "@xyo-network/diviner-boundwitness-abstract": "^3.6.0-rc.10",
39
+ "@xyo-network/diviner-boundwitness-model": "^3.6.0-rc.10",
40
+ "@xyo-network/diviner-model": "^3.6.0-rc.10",
41
+ "@xyo-network/module-model": "^3.6.0-rc.10",
42
+ "@xyo-network/payload-model": "^3.6.0-rc.10",
43
+ "idb": "^8.0.1"
44
44
  },
45
45
  "devDependencies": {
46
- "@xylabs/ts-scripts-yarn3": "^4.2.4",
47
- "@xylabs/tsconfig": "^4.2.4",
48
- "@xylabs/vitest-extended": "^4.4.9",
49
- "@xyo-network/archivist-indexeddb": "^3.5.2",
50
- "@xyo-network/boundwitness-builder": "^3.5.2",
51
- "@xyo-network/node-memory": "^3.5.2",
52
- "@xyo-network/payload-builder": "^3.5.2",
46
+ "@xylabs/delay": "^4.4.21",
47
+ "@xylabs/ts-scripts-yarn3": "^4.2.6",
48
+ "@xylabs/tsconfig": "^4.2.6",
49
+ "@xylabs/vitest-extended": "^4.4.21",
50
+ "@xyo-network/archivist-indexeddb": "^3.6.0-rc.10",
51
+ "@xyo-network/boundwitness-builder": "^3.6.0-rc.10",
52
+ "@xyo-network/node-memory": "^3.6.0-rc.10",
53
+ "@xyo-network/payload-builder": "^3.6.0-rc.10",
53
54
  "fake-indexeddb": "^6.0.0",
54
55
  "typescript": "^5.7.2",
55
- "vitest": "^2.1.5"
56
+ "vitest": "^2.1.8"
56
57
  },
57
58
  "publishConfig": {
58
59
  "access": "public"
59
- }
60
+ },
61
+ "stableVersion": "3.5.2"
60
62
  }
package/src/Diviner.ts CHANGED
@@ -1,21 +1,22 @@
1
1
  import { containsAll } from '@xylabs/array'
2
+ import { assertEx } from '@xylabs/assert'
2
3
  import { exists } from '@xylabs/exists'
3
4
  import { IndexedDbArchivist } from '@xyo-network/archivist-indexeddb'
4
- import type { BoundWitness } from '@xyo-network/boundwitness-model'
5
- import { BoundWitnessSchema, isBoundWitness } from '@xyo-network/boundwitness-model'
5
+ import { type BoundWitness, isBoundWitness } from '@xyo-network/boundwitness-model'
6
6
  import { BoundWitnessDiviner } from '@xyo-network/diviner-boundwitness-abstract'
7
7
  import type { BoundWitnessDivinerQueryPayload } from '@xyo-network/diviner-boundwitness-model'
8
8
  import { isBoundWitnessDivinerQueryPayload } from '@xyo-network/diviner-boundwitness-model'
9
- import { PayloadBuilder } from '@xyo-network/payload-builder'
10
- import type { Schema } from '@xyo-network/payload-model'
11
- import type { IDBPDatabase } from 'idb'
9
+ import type {
10
+ Schema, Sequence, WithStorageMeta,
11
+ } from '@xyo-network/payload-model'
12
+ import type { IDBPCursorWithValue, IDBPDatabase } from 'idb'
12
13
  import { openDB } from 'idb'
13
14
 
14
15
  import { IndexedDbBoundWitnessDivinerConfigSchema } from './Config.ts'
15
16
  import type { IndexedDbBoundWitnessDivinerParams } from './Params.ts'
16
17
 
17
18
  interface BoundWitnessStore {
18
- [s: string]: BoundWitness
19
+ [s: string]: WithStorageMeta<BoundWitness>
19
20
  }
20
21
 
21
22
  type ValueFilter = (bw?: BoundWitness | null) => boolean
@@ -51,7 +52,7 @@ export class IndexedDbBoundWitnessDiviner<
51
52
  }
52
53
 
53
54
  /**
54
- * The database version. If not supplied via config, it defaults to 1.
55
+ * The database version. If not supplied via config, it defaults to the archivist default version.
55
56
  */
56
57
  get dbVersion() {
57
58
  return this.config?.dbVersion ?? IndexedDbArchivist.defaultDbVersion
@@ -66,39 +67,46 @@ export class IndexedDbBoundWitnessDiviner<
66
67
  }
67
68
 
68
69
  protected override async divineHandler(payloads?: TIn[]): Promise<TOut[]> {
69
- const query = payloads?.filter(isBoundWitnessDivinerQueryPayload)?.pop()
70
+ const query = payloads?.find(isBoundWitnessDivinerQueryPayload)
70
71
  if (!query) return []
71
-
72
72
  const result = await this.tryUseDb(async (db) => {
73
73
  const {
74
- addresses, payload_hashes, payload_schemas, limit, offset, order,
74
+ addresses, payload_hashes, payload_schemas, limit, cursor, order,
75
75
  } = query
76
76
  const tx = db.transaction(this.storeName, 'readonly')
77
77
  const store = tx.objectStore(this.storeName)
78
78
  const results: TOut[] = []
79
- let parsedOffset = offset ?? 0
79
+ const parsedCursor = cursor
80
80
  const parsedLimit = limit ?? 10
81
- const direction: IDBCursorDirection = order === 'desc' ? 'prev' : 'next'
82
81
  const valueFilters: ValueFilter[] = [
82
+ isBoundWitness,
83
83
  bwValueFilter('addresses', addresses),
84
84
  bwValueFilter('payload_hashes', payload_hashes),
85
85
  bwValueFilter('payload_schemas', payload_schemas),
86
86
  ].filter(exists)
87
- // Only iterate over BWs
88
- let cursor = await store.index(IndexedDbArchivist.schemaIndexName).openCursor(IDBKeyRange.only(BoundWitnessSchema), direction)
87
+ const direction: IDBCursorDirection = order === 'desc' ? 'prev' : 'next'
89
88
 
90
- // If we're filtering on more than just the schema, we need to
91
- // iterate through all the results
92
- if (valueFilters.length === 0) {
93
- // Skip records until the offset is reached
94
- while (cursor && parsedOffset > 0) {
95
- cursor = await cursor.advance(parsedOffset)
96
- parsedOffset = 0 // Reset offset after skipping
89
+ // Iterate all records using the sequence index
90
+ const sequenceIndex = assertEx(store.index(IndexedDbArchivist.sequenceIndexName), () => 'Failed to get sequence index')
91
+ let dbCursor: IDBPCursorWithValue<BoundWitnessStore, [string], string, string, 'readonly'> | null
92
+ = assertEx(await sequenceIndex.openCursor(null, direction), () => `Failed to get cursor [${parsedCursor}, ${cursor}]`)
93
+
94
+ // If a cursor was supplied
95
+ if (parsedCursor !== undefined) {
96
+ let currentSequence: Sequence | undefined
97
+ // Skip records until the supplied cursor offset is reached
98
+ while (dbCursor && currentSequence !== parsedCursor) {
99
+ // Find the sequence of the current record
100
+ const current: WithStorageMeta<BoundWitness> = dbCursor.value
101
+ currentSequence = current?._sequence
102
+ // Advance one record beyond the cursor
103
+ dbCursor = await dbCursor.advance(1)
97
104
  }
98
105
  }
106
+
99
107
  // Collect results up to the limit
100
- while (cursor && results.length < parsedLimit) {
101
- const value = cursor.value
108
+ while (dbCursor && results.length < parsedLimit) {
109
+ const value = dbCursor.value
102
110
  if (value) {
103
111
  // If we're filtering on more than just the schema
104
112
  if (valueFilters.length > 0) {
@@ -113,18 +121,14 @@ export class IndexedDbBoundWitnessDiviner<
113
121
  }
114
122
  }
115
123
  try {
116
- cursor = await cursor.continue()
124
+ dbCursor = await dbCursor.continue()
117
125
  } catch {
118
126
  break
119
127
  }
120
128
  }
121
129
  await tx.done
122
130
  // Remove any metadata before returning to the client
123
- return await Promise.all(
124
- results.filter(isBoundWitness).map((bw) => {
125
- return PayloadBuilder.build(bw)
126
- }),
127
- )
131
+ return results
128
132
  })
129
133
  return result ?? []
130
134
  }