@xyo-network/diviner-payload-indexeddb 2.87.0-rc.2 → 2.87.0-rc.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,25 +5,19 @@ import { Payload } from '@xyo-network/payload-model';
5
5
  import { IndexedDbPayloadDivinerParams } from './Params';
6
6
  export declare class IndexedDbPayloadDiviner<TParams extends IndexedDbPayloadDivinerParams = IndexedDbPayloadDivinerParams, TIn extends PayloadDivinerQueryPayload = PayloadDivinerQueryPayload, TOut extends Payload = Payload, TEventData extends DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut> = DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut>> extends PayloadDiviner<TParams, TIn, TOut, TEventData> {
7
7
  static configSchemas: string[];
8
- static defaultDbName: string;
9
- static defaultDbVersion: number;
10
- static defaultStoreName: string;
11
8
  private _db;
12
9
  /**
13
- * The database name. If not supplied via config it defaults to
14
- * `archivist`. This behavior biases towards a single, isolated
15
- * DB per archivist which seems to make the most sense for 99% of
16
- * use cases.
10
+ * The database name. If not supplied via config, it defaults
11
+ * to the archivist's name and if archivist's name is not supplied,
12
+ * it defaults to `archivist`. This behavior
13
+ * biases towards a single, isolated DB per archivist which seems to
14
+ * make the most sense for 99% of use cases.
17
15
  */
18
16
  get dbName(): string;
19
17
  /**
20
- * 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.
21
19
  */
22
20
  get dbVersion(): number;
23
- /**
24
- * The database indexes.
25
- */
26
- get indexes(): import("@xyo-network/archivist-model").IndexDescription[];
27
21
  /**
28
22
  * The name of the object store. If not supplied via config, it defaults
29
23
  * to `payloads`.
@@ -32,7 +26,7 @@ export declare class IndexedDbPayloadDiviner<TParams extends IndexedDbPayloadDiv
32
26
  private get db();
33
27
  protected divineHandler(payloads?: TIn[]): Promise<TOut[]>;
34
28
  protected startHandler(): Promise<boolean>;
35
- private getKeyValuesFromQuery;
29
+ private getKeyRangeValue;
36
30
  private selectBestIndex;
37
31
  }
38
32
  //# sourceMappingURL=Diviner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../src/Diviner.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAA;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAA;AACtE,OAAO,EAAgC,0BAA0B,EAAE,MAAM,oCAAoC,CAAA;AAG7G,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAA;AAIpD,OAAO,EAAE,6BAA6B,EAAE,MAAM,UAAU,CAAA;AAMxD,qBAAa,uBAAuB,CAClC,OAAO,SAAS,6BAA6B,GAAG,6BAA6B,EAC7E,GAAG,SAAS,0BAA0B,GAAG,0BAA0B,EACnE,IAAI,SAAS,OAAO,GAAG,OAAO,EAC9B,UAAU,SAAS,sBAAsB,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,sBAAsB,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,CACxI,SAAQ,cAAc,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,CAAC;IACtD,OAAgB,aAAa,WAAwC;IACrE,MAAM,CAAC,aAAa,SAAc;IAClC,MAAM,CAAC,gBAAgB,SAAI;IAC3B,MAAM,CAAC,gBAAgB,SAAa;IAEpC,OAAO,CAAC,GAAG,CAAwC;IAEnD;;;;;OAKG;IACH,IAAI,MAAM,WAET;IAED;;OAEG;IACH,IAAI,SAAS,WAEZ;IAED;;OAEG;IACH,IAAI,OAAO,8DAEV;IAED;;;OAGG;IACH,IAAI,SAAS,WAEZ;IAED,OAAO,KAAK,EAAE,GAEb;cAEwB,aAAa,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;cAqChD,YAAY;IAQrC,OAAO,CAAC,qBAAqB;IAiB7B,OAAO,CAAC,eAAe;CA2BxB"}
1
+ {"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../src/Diviner.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAA;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAA;AACtE,OAAO,EAAgC,0BAA0B,EAAE,MAAM,oCAAoC,CAAA;AAG7G,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAA;AAIpD,OAAO,EAAE,6BAA6B,EAAE,MAAM,UAAU,CAAA;AAMxD,qBAAa,uBAAuB,CAClC,OAAO,SAAS,6BAA6B,GAAG,6BAA6B,EAC7E,GAAG,SAAS,0BAA0B,GAAG,0BAA0B,EACnE,IAAI,SAAS,OAAO,GAAG,OAAO,EAC9B,UAAU,SAAS,sBAAsB,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,sBAAsB,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,CACxI,SAAQ,cAAc,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,CAAC;IACtD,OAAgB,aAAa,WAAwC;IAErE,OAAO,CAAC,GAAG,CAAwC;IAEnD;;;;;;OAMG;IACH,IAAI,MAAM,WAET;IAED;;OAEG;IACH,IAAI,SAAS,WAEZ;IAED;;;OAGG;IACH,IAAI,SAAS,WAEZ;IAED,OAAO,KAAK,EAAE,GAEb;cAEwB,aAAa,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;cAqChD,YAAY;IAQrC,OAAO,CAAC,gBAAgB;IAkBxB,OAAO,CAAC,eAAe;CA2BxB"}
@@ -5,25 +5,19 @@ import { Payload } from '@xyo-network/payload-model';
5
5
  import { IndexedDbPayloadDivinerParams } from './Params';
6
6
  export declare class IndexedDbPayloadDiviner<TParams extends IndexedDbPayloadDivinerParams = IndexedDbPayloadDivinerParams, TIn extends PayloadDivinerQueryPayload = PayloadDivinerQueryPayload, TOut extends Payload = Payload, TEventData extends DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut> = DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut>> extends PayloadDiviner<TParams, TIn, TOut, TEventData> {
7
7
  static configSchemas: string[];
8
- static defaultDbName: string;
9
- static defaultDbVersion: number;
10
- static defaultStoreName: string;
11
8
  private _db;
12
9
  /**
13
- * The database name. If not supplied via config it defaults to
14
- * `archivist`. This behavior biases towards a single, isolated
15
- * DB per archivist which seems to make the most sense for 99% of
16
- * use cases.
10
+ * The database name. If not supplied via config, it defaults
11
+ * to the archivist's name and if archivist's name is not supplied,
12
+ * it defaults to `archivist`. This behavior
13
+ * biases towards a single, isolated DB per archivist which seems to
14
+ * make the most sense for 99% of use cases.
17
15
  */
18
16
  get dbName(): string;
19
17
  /**
20
- * 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.
21
19
  */
22
20
  get dbVersion(): number;
23
- /**
24
- * The database indexes.
25
- */
26
- get indexes(): import("@xyo-network/archivist-model").IndexDescription[];
27
21
  /**
28
22
  * The name of the object store. If not supplied via config, it defaults
29
23
  * to `payloads`.
@@ -32,7 +26,7 @@ export declare class IndexedDbPayloadDiviner<TParams extends IndexedDbPayloadDiv
32
26
  private get db();
33
27
  protected divineHandler(payloads?: TIn[]): Promise<TOut[]>;
34
28
  protected startHandler(): Promise<boolean>;
35
- private getKeyValuesFromQuery;
29
+ private getKeyRangeValue;
36
30
  private selectBestIndex;
37
31
  }
38
32
  //# sourceMappingURL=Diviner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../src/Diviner.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAA;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAA;AACtE,OAAO,EAAgC,0BAA0B,EAAE,MAAM,oCAAoC,CAAA;AAG7G,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAA;AAIpD,OAAO,EAAE,6BAA6B,EAAE,MAAM,UAAU,CAAA;AAMxD,qBAAa,uBAAuB,CAClC,OAAO,SAAS,6BAA6B,GAAG,6BAA6B,EAC7E,GAAG,SAAS,0BAA0B,GAAG,0BAA0B,EACnE,IAAI,SAAS,OAAO,GAAG,OAAO,EAC9B,UAAU,SAAS,sBAAsB,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,sBAAsB,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,CACxI,SAAQ,cAAc,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,CAAC;IACtD,OAAgB,aAAa,WAAwC;IACrE,MAAM,CAAC,aAAa,SAAc;IAClC,MAAM,CAAC,gBAAgB,SAAI;IAC3B,MAAM,CAAC,gBAAgB,SAAa;IAEpC,OAAO,CAAC,GAAG,CAAwC;IAEnD;;;;;OAKG;IACH,IAAI,MAAM,WAET;IAED;;OAEG;IACH,IAAI,SAAS,WAEZ;IAED;;OAEG;IACH,IAAI,OAAO,8DAEV;IAED;;;OAGG;IACH,IAAI,SAAS,WAEZ;IAED,OAAO,KAAK,EAAE,GAEb;cAEwB,aAAa,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;cAqChD,YAAY;IAQrC,OAAO,CAAC,qBAAqB;IAiB7B,OAAO,CAAC,eAAe;CA2BxB"}
1
+ {"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../src/Diviner.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAA;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAA;AACtE,OAAO,EAAgC,0BAA0B,EAAE,MAAM,oCAAoC,CAAA;AAG7G,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAA;AAIpD,OAAO,EAAE,6BAA6B,EAAE,MAAM,UAAU,CAAA;AAMxD,qBAAa,uBAAuB,CAClC,OAAO,SAAS,6BAA6B,GAAG,6BAA6B,EAC7E,GAAG,SAAS,0BAA0B,GAAG,0BAA0B,EACnE,IAAI,SAAS,OAAO,GAAG,OAAO,EAC9B,UAAU,SAAS,sBAAsB,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,sBAAsB,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,CACxI,SAAQ,cAAc,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,CAAC;IACtD,OAAgB,aAAa,WAAwC;IAErE,OAAO,CAAC,GAAG,CAAwC;IAEnD;;;;;;OAMG;IACH,IAAI,MAAM,WAET;IAED;;OAEG;IACH,IAAI,SAAS,WAEZ;IAED;;;OAGG;IACH,IAAI,SAAS,WAEZ;IAED,OAAO,KAAK,EAAE,GAEb;cAEwB,aAAa,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;cAqChD,YAAY;IAQrC,OAAO,CAAC,gBAAgB;IAkBxB,OAAO,CAAC,eAAe;CA2BxB"}
@@ -5,25 +5,19 @@ import { Payload } from '@xyo-network/payload-model';
5
5
  import { IndexedDbPayloadDivinerParams } from './Params';
6
6
  export declare class IndexedDbPayloadDiviner<TParams extends IndexedDbPayloadDivinerParams = IndexedDbPayloadDivinerParams, TIn extends PayloadDivinerQueryPayload = PayloadDivinerQueryPayload, TOut extends Payload = Payload, TEventData extends DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut> = DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut>> extends PayloadDiviner<TParams, TIn, TOut, TEventData> {
7
7
  static configSchemas: string[];
8
- static defaultDbName: string;
9
- static defaultDbVersion: number;
10
- static defaultStoreName: string;
11
8
  private _db;
12
9
  /**
13
- * The database name. If not supplied via config it defaults to
14
- * `archivist`. This behavior biases towards a single, isolated
15
- * DB per archivist which seems to make the most sense for 99% of
16
- * use cases.
10
+ * The database name. If not supplied via config, it defaults
11
+ * to the archivist's name and if archivist's name is not supplied,
12
+ * it defaults to `archivist`. This behavior
13
+ * biases towards a single, isolated DB per archivist which seems to
14
+ * make the most sense for 99% of use cases.
17
15
  */
18
16
  get dbName(): string;
19
17
  /**
20
- * 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.
21
19
  */
22
20
  get dbVersion(): number;
23
- /**
24
- * The database indexes.
25
- */
26
- get indexes(): import("@xyo-network/archivist-model").IndexDescription[];
27
21
  /**
28
22
  * The name of the object store. If not supplied via config, it defaults
29
23
  * to `payloads`.
@@ -32,7 +26,7 @@ export declare class IndexedDbPayloadDiviner<TParams extends IndexedDbPayloadDiv
32
26
  private get db();
33
27
  protected divineHandler(payloads?: TIn[]): Promise<TOut[]>;
34
28
  protected startHandler(): Promise<boolean>;
35
- private getKeyValuesFromQuery;
29
+ private getKeyRangeValue;
36
30
  private selectBestIndex;
37
31
  }
38
32
  //# sourceMappingURL=Diviner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../src/Diviner.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAA;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAA;AACtE,OAAO,EAAgC,0BAA0B,EAAE,MAAM,oCAAoC,CAAA;AAG7G,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAA;AAIpD,OAAO,EAAE,6BAA6B,EAAE,MAAM,UAAU,CAAA;AAMxD,qBAAa,uBAAuB,CAClC,OAAO,SAAS,6BAA6B,GAAG,6BAA6B,EAC7E,GAAG,SAAS,0BAA0B,GAAG,0BAA0B,EACnE,IAAI,SAAS,OAAO,GAAG,OAAO,EAC9B,UAAU,SAAS,sBAAsB,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,sBAAsB,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,CACxI,SAAQ,cAAc,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,CAAC;IACtD,OAAgB,aAAa,WAAwC;IACrE,MAAM,CAAC,aAAa,SAAc;IAClC,MAAM,CAAC,gBAAgB,SAAI;IAC3B,MAAM,CAAC,gBAAgB,SAAa;IAEpC,OAAO,CAAC,GAAG,CAAwC;IAEnD;;;;;OAKG;IACH,IAAI,MAAM,WAET;IAED;;OAEG;IACH,IAAI,SAAS,WAEZ;IAED;;OAEG;IACH,IAAI,OAAO,8DAEV;IAED;;;OAGG;IACH,IAAI,SAAS,WAEZ;IAED,OAAO,KAAK,EAAE,GAEb;cAEwB,aAAa,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;cAqChD,YAAY;IAQrC,OAAO,CAAC,qBAAqB;IAiB7B,OAAO,CAAC,eAAe;CA2BxB"}
1
+ {"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../src/Diviner.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAA;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAA;AACtE,OAAO,EAAgC,0BAA0B,EAAE,MAAM,oCAAoC,CAAA;AAG7G,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAA;AAIpD,OAAO,EAAE,6BAA6B,EAAE,MAAM,UAAU,CAAA;AAMxD,qBAAa,uBAAuB,CAClC,OAAO,SAAS,6BAA6B,GAAG,6BAA6B,EAC7E,GAAG,SAAS,0BAA0B,GAAG,0BAA0B,EACnE,IAAI,SAAS,OAAO,GAAG,OAAO,EAC9B,UAAU,SAAS,sBAAsB,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,sBAAsB,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,CACxI,SAAQ,cAAc,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,CAAC;IACtD,OAAgB,aAAa,WAAwC;IAErE,OAAO,CAAC,GAAG,CAAwC;IAEnD;;;;;;OAMG;IACH,IAAI,MAAM,WAET;IAED;;OAEG;IACH,IAAI,SAAS,WAEZ;IAED;;;OAGG;IACH,IAAI,SAAS,WAEZ;IAED,OAAO,KAAK,EAAE,GAEb;cAEwB,aAAa,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;cAqChD,YAAY;IAQrC,OAAO,CAAC,gBAAgB;IAkBxB,OAAO,CAAC,eAAe;CA2BxB"}
@@ -36,49 +36,42 @@ var IndexedDbPayloadDivinerConfigSchema = `${IndexedDbPayloadDivinerSchema}.conf
36
36
 
37
37
  // src/Diviner.ts
38
38
  var import_assert = require("@xylabs/assert");
39
+ var import_archivist_indexeddb = require("@xyo-network/archivist-indexeddb");
39
40
  var import_archivist_model = require("@xyo-network/archivist-model");
40
41
  var import_diviner_payload_abstract = require("@xyo-network/diviner-payload-abstract");
41
42
  var import_diviner_payload_model2 = require("@xyo-network/diviner-payload-model");
42
43
  var import_hash = require("@xyo-network/hash");
43
44
  var import_idb = require("idb");
44
- var IndexedDbPayloadDiviner = class _IndexedDbPayloadDiviner extends import_diviner_payload_abstract.PayloadDiviner {
45
+ var IndexedDbPayloadDiviner = class extends import_diviner_payload_abstract.PayloadDiviner {
45
46
  static {
46
47
  __name(this, "IndexedDbPayloadDiviner");
47
48
  }
48
49
  static configSchemas = [
49
50
  IndexedDbPayloadDivinerConfigSchema
50
51
  ];
51
- static defaultDbName = "archivist";
52
- static defaultDbVersion = 1;
53
- static defaultStoreName = "payloads";
54
52
  _db;
55
53
  /**
56
- * The database name. If not supplied via config it defaults to
57
- * `archivist`. This behavior biases towards a single, isolated
58
- * DB per archivist which seems to make the most sense for 99% of
59
- * use cases.
54
+ * The database name. If not supplied via config, it defaults
55
+ * to the archivist's name and if archivist's name is not supplied,
56
+ * it defaults to `archivist`. This behavior
57
+ * biases towards a single, isolated DB per archivist which seems to
58
+ * make the most sense for 99% of use cases.
60
59
  */
61
60
  get dbName() {
62
- return this.config?.dbName ?? _IndexedDbPayloadDiviner.defaultDbName;
61
+ return this.config?.dbName ?? this.config?.archivist ?? import_archivist_indexeddb.IndexedDbArchivist.defaultDbName;
63
62
  }
64
63
  /**
65
- * The database version. If not supplied via config, it defaults to 1.
64
+ * The database version. If not supplied via config, it defaults to the archivist default version.
66
65
  */
67
66
  get dbVersion() {
68
- return this.config?.dbVersion ?? _IndexedDbPayloadDiviner.defaultDbVersion;
69
- }
70
- /**
71
- * The database indexes.
72
- */
73
- get indexes() {
74
- return this.config?.storage?.indexes ?? [];
67
+ return this.config?.dbVersion ?? import_archivist_indexeddb.IndexedDbArchivist.defaultDbVersion;
75
68
  }
76
69
  /**
77
70
  * The name of the object store. If not supplied via config, it defaults
78
71
  * to `payloads`.
79
72
  */
80
73
  get storeName() {
81
- return this.config?.storeName ?? _IndexedDbPayloadDiviner.defaultStoreName;
74
+ return this.config?.storeName ?? import_archivist_indexeddb.IndexedDbArchivist.defaultStoreName;
82
75
  }
83
76
  get db() {
84
77
  return (0, import_assert.assertEx)(this._db, "DB not initialized");
@@ -103,8 +96,8 @@ var IndexedDbPayloadDiviner = class _IndexedDbPayloadDiviner extends import_divi
103
96
  };
104
97
  const direction = order === "desc" ? "prev" : "next";
105
98
  const suggestedIndex = this.selectBestIndex(filter, store);
106
- const filterValues = this.getKeyValuesFromQuery(suggestedIndex, filter);
107
- let cursor = suggestedIndex ? await store.index(suggestedIndex).openCursor(IDBKeyRange.only(filterValues.length === 1 ? filterValues[0] : filterValues), direction) : await store.openCursor(suggestedIndex, direction);
99
+ const keyRangeValue = this.getKeyRangeValue(suggestedIndex, filter);
100
+ let cursor = suggestedIndex ? await store.index(suggestedIndex).openCursor(IDBKeyRange.only(keyRangeValue), direction) : await store.openCursor(suggestedIndex, direction);
108
101
  while (cursor && parsedOffset > 0) {
109
102
  cursor = await cursor.advance(parsedOffset);
110
103
  parsedOffset = 0;
@@ -121,14 +114,15 @@ var IndexedDbPayloadDiviner = class _IndexedDbPayloadDiviner extends import_divi
121
114
  this._db = await (0, import_idb.openDB)(this.dbName, this.dbVersion);
122
115
  return true;
123
116
  }
124
- getKeyValuesFromQuery(indexName, query) {
117
+ getKeyRangeValue(indexName, query) {
125
118
  if (!indexName)
126
119
  return [];
127
120
  const extractFields = /* @__PURE__ */ __name((indexName2) => {
128
121
  return indexName2.slice(3).split(import_archivist_model.IndexSeparator).map((field) => field.toLowerCase());
129
122
  }, "extractFields");
130
123
  const indexFields = extractFields(indexName);
131
- return indexFields.map((field) => query[field]);
124
+ const keyRangeValue = indexFields.map((field) => query[field]);
125
+ return keyRangeValue.length === 1 ? keyRangeValue[0] : keyRangeValue;
132
126
  }
133
127
  selectBestIndex(query, store) {
134
128
  const { indexNames } = store;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/index.ts","../../src/Schema.ts","../../src/Config.ts","../../src/Diviner.ts"],"sourcesContent":["export * from './Config'\nexport * from './Diviner'\nexport * from './Params'\nexport * from './Schema'\n","import { PayloadDivinerSchema } from '@xyo-network/diviner-payload-model'\n\nexport const IndexedDbPayloadDivinerSchema = `${PayloadDivinerSchema}.indexeddb`\nexport type IndexedDbPayloadDivinerSchema = typeof IndexedDbPayloadDivinerSchema\n","import { IndexDescription } from '@xyo-network/archivist-model'\nimport { DivinerConfig } from '@xyo-network/diviner-model'\n\nimport { IndexedDbPayloadDivinerSchema } from './Schema'\n\nexport const IndexedDbPayloadDivinerConfigSchema = `${IndexedDbPayloadDivinerSchema}.config`\nexport type IndexedDbPayloadDivinerConfigSchema = typeof IndexedDbPayloadDivinerConfigSchema\n\nexport type IndexedDbPayloadDivinerConfig = 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: IndexedDbPayloadDivinerConfigSchema\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 { assertEx } from '@xylabs/assert'\nimport { IndexSeparator } from '@xyo-network/archivist-model'\nimport { DivinerModule, DivinerModuleEventData } from '@xyo-network/diviner-model'\nimport { PayloadDiviner } from '@xyo-network/diviner-payload-abstract'\nimport { isPayloadDivinerQueryPayload, PayloadDivinerQueryPayload } from '@xyo-network/diviner-payload-model'\nimport { PayloadHasher } from '@xyo-network/hash'\nimport { AnyObject } from '@xyo-network/object'\nimport { Payload } from '@xyo-network/payload-model'\nimport { IDBPDatabase, IDBPObjectStore, openDB } from 'idb'\n\nimport { IndexedDbPayloadDivinerConfigSchema } from './Config'\nimport { IndexedDbPayloadDivinerParams } from './Params'\n\ninterface PayloadStore {\n [s: string]: Payload\n}\n\nexport class IndexedDbPayloadDiviner<\n TParams extends IndexedDbPayloadDivinerParams = IndexedDbPayloadDivinerParams,\n TIn extends PayloadDivinerQueryPayload = PayloadDivinerQueryPayload,\n TOut extends Payload = Payload,\n TEventData extends DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut> = DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut>,\n> extends PayloadDiviner<TParams, TIn, TOut, TEventData> {\n static override configSchemas = [IndexedDbPayloadDivinerConfigSchema]\n static defaultDbName = 'archivist'\n static defaultDbVersion = 1\n static defaultStoreName = 'payloads'\n\n private _db: IDBPDatabase<PayloadStore> | undefined\n\n /**\n * The database name. If not supplied via config it defaults to\n * `archivist`. This behavior biases towards a single, isolated\n * DB per archivist which seems to make the most sense for 99% of\n * use cases.\n */\n get dbName() {\n return this.config?.dbName ?? IndexedDbPayloadDiviner.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 ?? IndexedDbPayloadDiviner.defaultDbVersion\n }\n\n /**\n * The database indexes.\n */\n get indexes() {\n return this.config?.storage?.indexes ?? []\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 ?? IndexedDbPayloadDiviner.defaultStoreName\n }\n\n private get db(): IDBPDatabase<PayloadStore> {\n return assertEx(this._db, 'DB not initialized')\n }\n\n protected override async divineHandler(payloads?: TIn[]): Promise<TOut[]> {\n const query = assertEx(payloads?.filter(isPayloadDivinerQueryPayload)?.pop(), 'Missing query payload')\n if (!query) return []\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { schemas, limit, offset, hash, order, schema: _schema, sources, ...props } = query as unknown as TIn & { sources?: string[] }\n const tx = this.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 assertEx((schemas?.length ?? 1) === 1, 'IndexedDbPayloadDiviner: Only one filter schema supported')\n const filterSchema = schemas?.[0]\n const filter = filterSchema ? { schema: filterSchema, ...props } : { ...props }\n const direction: IDBCursorDirection = order === 'desc' ? 'prev' : 'next'\n const suggestedIndex = this.selectBestIndex(filter, store)\n const filterValues = this.getKeyValuesFromQuery(suggestedIndex, filter)\n let cursor = suggestedIndex\n ? // Conditionally filter on schemas\n await store.index(suggestedIndex).openCursor(IDBKeyRange.only(filterValues.length === 1 ? filterValues[0] : filterValues), direction)\n : // Just iterate all records\n await store.openCursor(suggestedIndex, direction)\n\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 // Collect results up to the limit\n while (cursor && results.length < parsedLimit) {\n results.push(cursor.value)\n cursor = await cursor.continue()\n }\n await tx.done\n // Remove any metadata before returning to the client\n return results.map((payload) => PayloadHasher.jsonPayload(payload))\n }\n\n protected override async startHandler() {\n await super.startHandler()\n // NOTE: We could defer this creation to first access but we\n // want to fail fast here in case something is wrong\n this._db = await openDB<PayloadStore>(this.dbName, this.dbVersion)\n return true\n }\n\n private getKeyValuesFromQuery(indexName: string | null, query: AnyObject): unknown[] {\n if (!indexName) return []\n // Function to extract fields from an index name\n const extractFields = (indexName: string): string[] => {\n return indexName\n .slice(3)\n .split(IndexSeparator)\n .map((field) => field.toLowerCase())\n }\n\n // Extracting the relevant fields from the index name\n const indexFields = extractFields(indexName)\n\n // Collecting the values for these fields from the query object\n return indexFields.map((field) => query[field as keyof AnyObject])\n }\n\n private selectBestIndex(query: AnyObject, store: IDBPObjectStore<PayloadStore>): string | null {\n // List of available indexes\n const { indexNames } = store\n\n // Function to extract fields from an index name\n const extractFields = (indexName: string): string[] => {\n return indexName\n .slice(3)\n .split(IndexSeparator)\n .map((field) => field.toLowerCase())\n }\n\n // Convert query object keys to a set for easier comparison\n const queryKeys = new Set(Object.keys(query).map((key) => key.toLowerCase()))\n\n // Find the best matching index\n let bestMatch: { indexName: string; matchCount: number } = { indexName: '', matchCount: 0 }\n\n for (const indexName of indexNames) {\n const indexFields = extractFields(indexName)\n const matchCount = indexFields.filter((field) => queryKeys.has(field)).length\n if (matchCount > bestMatch.matchCount) {\n bestMatch = { indexName, matchCount }\n }\n }\n return bestMatch.matchCount > 0 ? bestMatch.indexName : null\n }\n}\n"],"mappings":"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","names":["IndexedDbPayloadDivinerSchema","PayloadDivinerSchema","IndexedDbPayloadDivinerConfigSchema","IndexedDbPayloadDivinerSchema","import_diviner_payload_model","IndexedDbPayloadDiviner","PayloadDiviner","configSchemas","IndexedDbPayloadDivinerConfigSchema","defaultDbName","defaultDbVersion","defaultStoreName","_db","dbName","config","dbVersion","indexes","storage","storeName","db","assertEx","divineHandler","payloads","query","filter","isPayloadDivinerQueryPayload","pop","schemas","limit","offset","hash","order","schema","_schema","sources","props","tx","transaction","store","objectStore","results","parsedOffset","parsedLimit","length","filterSchema","direction","suggestedIndex","selectBestIndex","filterValues","getKeyValuesFromQuery","cursor","index","openCursor","IDBKeyRange","only","advance","push","value","continue","done","map","payload","PayloadHasher","jsonPayload","startHandler","openDB","indexName","extractFields","slice","split","IndexSeparator","field","toLowerCase","indexFields","indexNames","queryKeys","Set","Object","keys","key","bestMatch","matchCount","has"]}
1
+ {"version":3,"sources":["../../src/index.ts","../../src/Schema.ts","../../src/Config.ts","../../src/Diviner.ts"],"sourcesContent":["export * from './Config'\nexport * from './Diviner'\nexport * from './Params'\nexport * from './Schema'\n","import { PayloadDivinerSchema } from '@xyo-network/diviner-payload-model'\n\nexport const IndexedDbPayloadDivinerSchema = `${PayloadDivinerSchema}.indexeddb`\nexport type IndexedDbPayloadDivinerSchema = typeof IndexedDbPayloadDivinerSchema\n","import { IndexDescription } from '@xyo-network/archivist-model'\nimport { DivinerConfig } from '@xyo-network/diviner-model'\n\nimport { IndexedDbPayloadDivinerSchema } from './Schema'\n\nexport const IndexedDbPayloadDivinerConfigSchema = `${IndexedDbPayloadDivinerSchema}.config`\nexport type IndexedDbPayloadDivinerConfigSchema = typeof IndexedDbPayloadDivinerConfigSchema\n\nexport type IndexedDbPayloadDivinerConfig = 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: IndexedDbPayloadDivinerConfigSchema\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 { assertEx } from '@xylabs/assert'\nimport { IndexedDbArchivist } from '@xyo-network/archivist-indexeddb'\nimport { IndexSeparator } from '@xyo-network/archivist-model'\nimport { DivinerModule, DivinerModuleEventData } from '@xyo-network/diviner-model'\nimport { PayloadDiviner } from '@xyo-network/diviner-payload-abstract'\nimport { isPayloadDivinerQueryPayload, PayloadDivinerQueryPayload } from '@xyo-network/diviner-payload-model'\nimport { PayloadHasher } from '@xyo-network/hash'\nimport { AnyObject } from '@xyo-network/object'\nimport { Payload } from '@xyo-network/payload-model'\nimport { IDBPDatabase, IDBPObjectStore, openDB } from 'idb'\n\nimport { IndexedDbPayloadDivinerConfigSchema } from './Config'\nimport { IndexedDbPayloadDivinerParams } from './Params'\n\ninterface PayloadStore {\n [s: string]: Payload\n}\n\nexport class IndexedDbPayloadDiviner<\n TParams extends IndexedDbPayloadDivinerParams = IndexedDbPayloadDivinerParams,\n TIn extends PayloadDivinerQueryPayload = PayloadDivinerQueryPayload,\n TOut extends Payload = Payload,\n TEventData extends DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut> = DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut>,\n> extends PayloadDiviner<TParams, TIn, TOut, TEventData> {\n static override configSchemas = [IndexedDbPayloadDivinerConfigSchema]\n\n private _db: IDBPDatabase<PayloadStore> | undefined\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 private get db(): IDBPDatabase<PayloadStore> {\n return assertEx(this._db, 'DB not initialized')\n }\n\n protected override async divineHandler(payloads?: TIn[]): Promise<TOut[]> {\n const query = assertEx(payloads?.filter(isPayloadDivinerQueryPayload)?.pop(), 'Missing query payload')\n if (!query) return []\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { schemas, limit, offset, hash, order, schema: _schema, sources, ...props } = query as unknown as TIn & { sources?: string[] }\n const tx = this.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 assertEx((schemas?.length ?? 1) === 1, 'IndexedDbPayloadDiviner: Only one filter schema supported')\n const filterSchema = schemas?.[0]\n const filter = filterSchema ? { schema: filterSchema, ...props } : { ...props }\n const direction: IDBCursorDirection = order === 'desc' ? 'prev' : 'next'\n const suggestedIndex = this.selectBestIndex(filter, store)\n const keyRangeValue = this.getKeyRangeValue(suggestedIndex, filter)\n let cursor = suggestedIndex\n ? // Conditionally filter on schemas\n await store.index(suggestedIndex).openCursor(IDBKeyRange.only(keyRangeValue), direction)\n : // Just iterate all records\n await store.openCursor(suggestedIndex, direction)\n\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 // Collect results up to the limit\n while (cursor && results.length < parsedLimit) {\n results.push(cursor.value)\n cursor = await cursor.continue()\n }\n await tx.done\n // Remove any metadata before returning to the client\n return results.map((payload) => PayloadHasher.jsonPayload(payload))\n }\n\n protected override async startHandler() {\n await super.startHandler()\n // NOTE: We could defer this creation to first access but we\n // want to fail fast here in case something is wrong\n this._db = await openDB<PayloadStore>(this.dbName, this.dbVersion)\n return true\n }\n\n private getKeyRangeValue(indexName: string | null, query: AnyObject): unknown | unknown[] {\n if (!indexName) return []\n // Function to extract fields from an index name\n const extractFields = (indexName: string): string[] => {\n return indexName\n .slice(3)\n .split(IndexSeparator)\n .map((field) => field.toLowerCase())\n }\n\n // Extracting the relevant fields from the index name\n const indexFields = extractFields(indexName)\n\n // Collecting the values for these fields from the query object\n const keyRangeValue = indexFields.map((field) => query[field as keyof AnyObject])\n return keyRangeValue.length === 1 ? keyRangeValue[0] : keyRangeValue\n }\n\n private selectBestIndex(query: AnyObject, store: IDBPObjectStore<PayloadStore>): string | null {\n // List of available indexes\n const { indexNames } = store\n\n // Function to extract fields from an index name\n const extractFields = (indexName: string): string[] => {\n return indexName\n .slice(3)\n .split(IndexSeparator)\n .map((field) => field.toLowerCase())\n }\n\n // Convert query object keys to a set for easier comparison\n const queryKeys = new Set(Object.keys(query).map((key) => key.toLowerCase()))\n\n // Find the best matching index\n let bestMatch: { indexName: string; matchCount: number } = { indexName: '', matchCount: 0 }\n\n for (const indexName of indexNames) {\n const indexFields = extractFields(indexName)\n const matchCount = indexFields.filter((field) => queryKeys.has(field)).length\n if (matchCount > bestMatch.matchCount) {\n bestMatch = { indexName, matchCount }\n }\n }\n return bestMatch.matchCount > 0 ? bestMatch.indexName : null\n }\n}\n"],"mappings":"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","names":["IndexedDbPayloadDivinerSchema","PayloadDivinerSchema","IndexedDbPayloadDivinerConfigSchema","IndexedDbPayloadDivinerSchema","import_diviner_payload_model","IndexedDbPayloadDiviner","PayloadDiviner","configSchemas","IndexedDbPayloadDivinerConfigSchema","_db","dbName","config","archivist","IndexedDbArchivist","defaultDbName","dbVersion","defaultDbVersion","storeName","defaultStoreName","db","assertEx","divineHandler","payloads","query","filter","isPayloadDivinerQueryPayload","pop","schemas","limit","offset","hash","order","schema","_schema","sources","props","tx","transaction","store","objectStore","results","parsedOffset","parsedLimit","length","filterSchema","direction","suggestedIndex","selectBestIndex","keyRangeValue","getKeyRangeValue","cursor","index","openCursor","IDBKeyRange","only","advance","push","value","continue","done","map","payload","PayloadHasher","jsonPayload","startHandler","openDB","indexName","extractFields","slice","split","IndexSeparator","field","toLowerCase","indexFields","indexNames","queryKeys","Set","Object","keys","key","bestMatch","matchCount","has"]}
@@ -10,49 +10,42 @@ var IndexedDbPayloadDivinerConfigSchema = `${IndexedDbPayloadDivinerSchema}.conf
10
10
 
11
11
  // src/Diviner.ts
12
12
  import { assertEx } from "@xylabs/assert";
13
+ import { IndexedDbArchivist } from "@xyo-network/archivist-indexeddb";
13
14
  import { IndexSeparator } from "@xyo-network/archivist-model";
14
15
  import { PayloadDiviner } from "@xyo-network/diviner-payload-abstract";
15
16
  import { isPayloadDivinerQueryPayload } from "@xyo-network/diviner-payload-model";
16
17
  import { PayloadHasher } from "@xyo-network/hash";
17
18
  import { openDB } from "idb";
18
- var IndexedDbPayloadDiviner = class _IndexedDbPayloadDiviner extends PayloadDiviner {
19
+ var IndexedDbPayloadDiviner = class extends PayloadDiviner {
19
20
  static {
20
21
  __name(this, "IndexedDbPayloadDiviner");
21
22
  }
22
23
  static configSchemas = [
23
24
  IndexedDbPayloadDivinerConfigSchema
24
25
  ];
25
- static defaultDbName = "archivist";
26
- static defaultDbVersion = 1;
27
- static defaultStoreName = "payloads";
28
26
  _db;
29
27
  /**
30
- * The database name. If not supplied via config it defaults to
31
- * `archivist`. This behavior biases towards a single, isolated
32
- * DB per archivist which seems to make the most sense for 99% of
33
- * use cases.
28
+ * The database name. If not supplied via config, it defaults
29
+ * to the archivist's name and if archivist's name is not supplied,
30
+ * it defaults to `archivist`. This behavior
31
+ * biases towards a single, isolated DB per archivist which seems to
32
+ * make the most sense for 99% of use cases.
34
33
  */
35
34
  get dbName() {
36
- return this.config?.dbName ?? _IndexedDbPayloadDiviner.defaultDbName;
35
+ return this.config?.dbName ?? this.config?.archivist ?? IndexedDbArchivist.defaultDbName;
37
36
  }
38
37
  /**
39
- * The database version. If not supplied via config, it defaults to 1.
38
+ * The database version. If not supplied via config, it defaults to the archivist default version.
40
39
  */
41
40
  get dbVersion() {
42
- return this.config?.dbVersion ?? _IndexedDbPayloadDiviner.defaultDbVersion;
43
- }
44
- /**
45
- * The database indexes.
46
- */
47
- get indexes() {
48
- return this.config?.storage?.indexes ?? [];
41
+ return this.config?.dbVersion ?? IndexedDbArchivist.defaultDbVersion;
49
42
  }
50
43
  /**
51
44
  * The name of the object store. If not supplied via config, it defaults
52
45
  * to `payloads`.
53
46
  */
54
47
  get storeName() {
55
- return this.config?.storeName ?? _IndexedDbPayloadDiviner.defaultStoreName;
48
+ return this.config?.storeName ?? IndexedDbArchivist.defaultStoreName;
56
49
  }
57
50
  get db() {
58
51
  return assertEx(this._db, "DB not initialized");
@@ -77,8 +70,8 @@ var IndexedDbPayloadDiviner = class _IndexedDbPayloadDiviner extends PayloadDivi
77
70
  };
78
71
  const direction = order === "desc" ? "prev" : "next";
79
72
  const suggestedIndex = this.selectBestIndex(filter, store);
80
- const filterValues = this.getKeyValuesFromQuery(suggestedIndex, filter);
81
- let cursor = suggestedIndex ? await store.index(suggestedIndex).openCursor(IDBKeyRange.only(filterValues.length === 1 ? filterValues[0] : filterValues), direction) : await store.openCursor(suggestedIndex, direction);
73
+ const keyRangeValue = this.getKeyRangeValue(suggestedIndex, filter);
74
+ let cursor = suggestedIndex ? await store.index(suggestedIndex).openCursor(IDBKeyRange.only(keyRangeValue), direction) : await store.openCursor(suggestedIndex, direction);
82
75
  while (cursor && parsedOffset > 0) {
83
76
  cursor = await cursor.advance(parsedOffset);
84
77
  parsedOffset = 0;
@@ -95,14 +88,15 @@ var IndexedDbPayloadDiviner = class _IndexedDbPayloadDiviner extends PayloadDivi
95
88
  this._db = await openDB(this.dbName, this.dbVersion);
96
89
  return true;
97
90
  }
98
- getKeyValuesFromQuery(indexName, query) {
91
+ getKeyRangeValue(indexName, query) {
99
92
  if (!indexName)
100
93
  return [];
101
94
  const extractFields = /* @__PURE__ */ __name((indexName2) => {
102
95
  return indexName2.slice(3).split(IndexSeparator).map((field) => field.toLowerCase());
103
96
  }, "extractFields");
104
97
  const indexFields = extractFields(indexName);
105
- return indexFields.map((field) => query[field]);
98
+ const keyRangeValue = indexFields.map((field) => query[field]);
99
+ return keyRangeValue.length === 1 ? keyRangeValue[0] : keyRangeValue;
106
100
  }
107
101
  selectBestIndex(query, store) {
108
102
  const { indexNames } = store;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/Schema.ts","../../src/Config.ts","../../src/Diviner.ts"],"sourcesContent":["import { PayloadDivinerSchema } from '@xyo-network/diviner-payload-model'\n\nexport const IndexedDbPayloadDivinerSchema = `${PayloadDivinerSchema}.indexeddb`\nexport type IndexedDbPayloadDivinerSchema = typeof IndexedDbPayloadDivinerSchema\n","import { IndexDescription } from '@xyo-network/archivist-model'\nimport { DivinerConfig } from '@xyo-network/diviner-model'\n\nimport { IndexedDbPayloadDivinerSchema } from './Schema'\n\nexport const IndexedDbPayloadDivinerConfigSchema = `${IndexedDbPayloadDivinerSchema}.config`\nexport type IndexedDbPayloadDivinerConfigSchema = typeof IndexedDbPayloadDivinerConfigSchema\n\nexport type IndexedDbPayloadDivinerConfig = 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: IndexedDbPayloadDivinerConfigSchema\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 { assertEx } from '@xylabs/assert'\nimport { IndexSeparator } from '@xyo-network/archivist-model'\nimport { DivinerModule, DivinerModuleEventData } from '@xyo-network/diviner-model'\nimport { PayloadDiviner } from '@xyo-network/diviner-payload-abstract'\nimport { isPayloadDivinerQueryPayload, PayloadDivinerQueryPayload } from '@xyo-network/diviner-payload-model'\nimport { PayloadHasher } from '@xyo-network/hash'\nimport { AnyObject } from '@xyo-network/object'\nimport { Payload } from '@xyo-network/payload-model'\nimport { IDBPDatabase, IDBPObjectStore, openDB } from 'idb'\n\nimport { IndexedDbPayloadDivinerConfigSchema } from './Config'\nimport { IndexedDbPayloadDivinerParams } from './Params'\n\ninterface PayloadStore {\n [s: string]: Payload\n}\n\nexport class IndexedDbPayloadDiviner<\n TParams extends IndexedDbPayloadDivinerParams = IndexedDbPayloadDivinerParams,\n TIn extends PayloadDivinerQueryPayload = PayloadDivinerQueryPayload,\n TOut extends Payload = Payload,\n TEventData extends DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut> = DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut>,\n> extends PayloadDiviner<TParams, TIn, TOut, TEventData> {\n static override configSchemas = [IndexedDbPayloadDivinerConfigSchema]\n static defaultDbName = 'archivist'\n static defaultDbVersion = 1\n static defaultStoreName = 'payloads'\n\n private _db: IDBPDatabase<PayloadStore> | undefined\n\n /**\n * The database name. If not supplied via config it defaults to\n * `archivist`. This behavior biases towards a single, isolated\n * DB per archivist which seems to make the most sense for 99% of\n * use cases.\n */\n get dbName() {\n return this.config?.dbName ?? IndexedDbPayloadDiviner.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 ?? IndexedDbPayloadDiviner.defaultDbVersion\n }\n\n /**\n * The database indexes.\n */\n get indexes() {\n return this.config?.storage?.indexes ?? []\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 ?? IndexedDbPayloadDiviner.defaultStoreName\n }\n\n private get db(): IDBPDatabase<PayloadStore> {\n return assertEx(this._db, 'DB not initialized')\n }\n\n protected override async divineHandler(payloads?: TIn[]): Promise<TOut[]> {\n const query = assertEx(payloads?.filter(isPayloadDivinerQueryPayload)?.pop(), 'Missing query payload')\n if (!query) return []\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { schemas, limit, offset, hash, order, schema: _schema, sources, ...props } = query as unknown as TIn & { sources?: string[] }\n const tx = this.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 assertEx((schemas?.length ?? 1) === 1, 'IndexedDbPayloadDiviner: Only one filter schema supported')\n const filterSchema = schemas?.[0]\n const filter = filterSchema ? { schema: filterSchema, ...props } : { ...props }\n const direction: IDBCursorDirection = order === 'desc' ? 'prev' : 'next'\n const suggestedIndex = this.selectBestIndex(filter, store)\n const filterValues = this.getKeyValuesFromQuery(suggestedIndex, filter)\n let cursor = suggestedIndex\n ? // Conditionally filter on schemas\n await store.index(suggestedIndex).openCursor(IDBKeyRange.only(filterValues.length === 1 ? filterValues[0] : filterValues), direction)\n : // Just iterate all records\n await store.openCursor(suggestedIndex, direction)\n\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 // Collect results up to the limit\n while (cursor && results.length < parsedLimit) {\n results.push(cursor.value)\n cursor = await cursor.continue()\n }\n await tx.done\n // Remove any metadata before returning to the client\n return results.map((payload) => PayloadHasher.jsonPayload(payload))\n }\n\n protected override async startHandler() {\n await super.startHandler()\n // NOTE: We could defer this creation to first access but we\n // want to fail fast here in case something is wrong\n this._db = await openDB<PayloadStore>(this.dbName, this.dbVersion)\n return true\n }\n\n private getKeyValuesFromQuery(indexName: string | null, query: AnyObject): unknown[] {\n if (!indexName) return []\n // Function to extract fields from an index name\n const extractFields = (indexName: string): string[] => {\n return indexName\n .slice(3)\n .split(IndexSeparator)\n .map((field) => field.toLowerCase())\n }\n\n // Extracting the relevant fields from the index name\n const indexFields = extractFields(indexName)\n\n // Collecting the values for these fields from the query object\n return indexFields.map((field) => query[field as keyof AnyObject])\n }\n\n private selectBestIndex(query: AnyObject, store: IDBPObjectStore<PayloadStore>): string | null {\n // List of available indexes\n const { indexNames } = store\n\n // Function to extract fields from an index name\n const extractFields = (indexName: string): string[] => {\n return indexName\n .slice(3)\n .split(IndexSeparator)\n .map((field) => field.toLowerCase())\n }\n\n // Convert query object keys to a set for easier comparison\n const queryKeys = new Set(Object.keys(query).map((key) => key.toLowerCase()))\n\n // Find the best matching index\n let bestMatch: { indexName: string; matchCount: number } = { indexName: '', matchCount: 0 }\n\n for (const indexName of indexNames) {\n const indexFields = extractFields(indexName)\n const matchCount = indexFields.filter((field) => queryKeys.has(field)).length\n if (matchCount > bestMatch.matchCount) {\n bestMatch = { indexName, matchCount }\n }\n }\n return bestMatch.matchCount > 0 ? bestMatch.indexName : null\n }\n}\n"],"mappings":"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","names":["PayloadDivinerSchema","IndexedDbPayloadDivinerSchema","IndexedDbPayloadDivinerConfigSchema","IndexedDbPayloadDivinerSchema","assertEx","IndexSeparator","PayloadDiviner","isPayloadDivinerQueryPayload","PayloadHasher","openDB","IndexedDbPayloadDiviner","PayloadDiviner","configSchemas","IndexedDbPayloadDivinerConfigSchema","defaultDbName","defaultDbVersion","defaultStoreName","_db","dbName","config","dbVersion","indexes","storage","storeName","db","assertEx","divineHandler","payloads","query","filter","isPayloadDivinerQueryPayload","pop","schemas","limit","offset","hash","order","schema","_schema","sources","props","tx","transaction","store","objectStore","results","parsedOffset","parsedLimit","length","filterSchema","direction","suggestedIndex","selectBestIndex","filterValues","getKeyValuesFromQuery","cursor","index","openCursor","IDBKeyRange","only","advance","push","value","continue","done","map","payload","PayloadHasher","jsonPayload","startHandler","openDB","indexName","extractFields","slice","split","IndexSeparator","field","toLowerCase","indexFields","indexNames","queryKeys","Set","Object","keys","key","bestMatch","matchCount","has"]}
1
+ {"version":3,"sources":["../../src/Schema.ts","../../src/Config.ts","../../src/Diviner.ts"],"sourcesContent":["import { PayloadDivinerSchema } from '@xyo-network/diviner-payload-model'\n\nexport const IndexedDbPayloadDivinerSchema = `${PayloadDivinerSchema}.indexeddb`\nexport type IndexedDbPayloadDivinerSchema = typeof IndexedDbPayloadDivinerSchema\n","import { IndexDescription } from '@xyo-network/archivist-model'\nimport { DivinerConfig } from '@xyo-network/diviner-model'\n\nimport { IndexedDbPayloadDivinerSchema } from './Schema'\n\nexport const IndexedDbPayloadDivinerConfigSchema = `${IndexedDbPayloadDivinerSchema}.config`\nexport type IndexedDbPayloadDivinerConfigSchema = typeof IndexedDbPayloadDivinerConfigSchema\n\nexport type IndexedDbPayloadDivinerConfig = 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: IndexedDbPayloadDivinerConfigSchema\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 { assertEx } from '@xylabs/assert'\nimport { IndexedDbArchivist } from '@xyo-network/archivist-indexeddb'\nimport { IndexSeparator } from '@xyo-network/archivist-model'\nimport { DivinerModule, DivinerModuleEventData } from '@xyo-network/diviner-model'\nimport { PayloadDiviner } from '@xyo-network/diviner-payload-abstract'\nimport { isPayloadDivinerQueryPayload, PayloadDivinerQueryPayload } from '@xyo-network/diviner-payload-model'\nimport { PayloadHasher } from '@xyo-network/hash'\nimport { AnyObject } from '@xyo-network/object'\nimport { Payload } from '@xyo-network/payload-model'\nimport { IDBPDatabase, IDBPObjectStore, openDB } from 'idb'\n\nimport { IndexedDbPayloadDivinerConfigSchema } from './Config'\nimport { IndexedDbPayloadDivinerParams } from './Params'\n\ninterface PayloadStore {\n [s: string]: Payload\n}\n\nexport class IndexedDbPayloadDiviner<\n TParams extends IndexedDbPayloadDivinerParams = IndexedDbPayloadDivinerParams,\n TIn extends PayloadDivinerQueryPayload = PayloadDivinerQueryPayload,\n TOut extends Payload = Payload,\n TEventData extends DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut> = DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut>,\n> extends PayloadDiviner<TParams, TIn, TOut, TEventData> {\n static override configSchemas = [IndexedDbPayloadDivinerConfigSchema]\n\n private _db: IDBPDatabase<PayloadStore> | undefined\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 private get db(): IDBPDatabase<PayloadStore> {\n return assertEx(this._db, 'DB not initialized')\n }\n\n protected override async divineHandler(payloads?: TIn[]): Promise<TOut[]> {\n const query = assertEx(payloads?.filter(isPayloadDivinerQueryPayload)?.pop(), 'Missing query payload')\n if (!query) return []\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { schemas, limit, offset, hash, order, schema: _schema, sources, ...props } = query as unknown as TIn & { sources?: string[] }\n const tx = this.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 assertEx((schemas?.length ?? 1) === 1, 'IndexedDbPayloadDiviner: Only one filter schema supported')\n const filterSchema = schemas?.[0]\n const filter = filterSchema ? { schema: filterSchema, ...props } : { ...props }\n const direction: IDBCursorDirection = order === 'desc' ? 'prev' : 'next'\n const suggestedIndex = this.selectBestIndex(filter, store)\n const keyRangeValue = this.getKeyRangeValue(suggestedIndex, filter)\n let cursor = suggestedIndex\n ? // Conditionally filter on schemas\n await store.index(suggestedIndex).openCursor(IDBKeyRange.only(keyRangeValue), direction)\n : // Just iterate all records\n await store.openCursor(suggestedIndex, direction)\n\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 // Collect results up to the limit\n while (cursor && results.length < parsedLimit) {\n results.push(cursor.value)\n cursor = await cursor.continue()\n }\n await tx.done\n // Remove any metadata before returning to the client\n return results.map((payload) => PayloadHasher.jsonPayload(payload))\n }\n\n protected override async startHandler() {\n await super.startHandler()\n // NOTE: We could defer this creation to first access but we\n // want to fail fast here in case something is wrong\n this._db = await openDB<PayloadStore>(this.dbName, this.dbVersion)\n return true\n }\n\n private getKeyRangeValue(indexName: string | null, query: AnyObject): unknown | unknown[] {\n if (!indexName) return []\n // Function to extract fields from an index name\n const extractFields = (indexName: string): string[] => {\n return indexName\n .slice(3)\n .split(IndexSeparator)\n .map((field) => field.toLowerCase())\n }\n\n // Extracting the relevant fields from the index name\n const indexFields = extractFields(indexName)\n\n // Collecting the values for these fields from the query object\n const keyRangeValue = indexFields.map((field) => query[field as keyof AnyObject])\n return keyRangeValue.length === 1 ? keyRangeValue[0] : keyRangeValue\n }\n\n private selectBestIndex(query: AnyObject, store: IDBPObjectStore<PayloadStore>): string | null {\n // List of available indexes\n const { indexNames } = store\n\n // Function to extract fields from an index name\n const extractFields = (indexName: string): string[] => {\n return indexName\n .slice(3)\n .split(IndexSeparator)\n .map((field) => field.toLowerCase())\n }\n\n // Convert query object keys to a set for easier comparison\n const queryKeys = new Set(Object.keys(query).map((key) => key.toLowerCase()))\n\n // Find the best matching index\n let bestMatch: { indexName: string; matchCount: number } = { indexName: '', matchCount: 0 }\n\n for (const indexName of indexNames) {\n const indexFields = extractFields(indexName)\n const matchCount = indexFields.filter((field) => queryKeys.has(field)).length\n if (matchCount > bestMatch.matchCount) {\n bestMatch = { indexName, matchCount }\n }\n }\n return bestMatch.matchCount > 0 ? bestMatch.indexName : null\n }\n}\n"],"mappings":"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","names":["PayloadDivinerSchema","IndexedDbPayloadDivinerSchema","IndexedDbPayloadDivinerConfigSchema","IndexedDbPayloadDivinerSchema","assertEx","IndexedDbArchivist","IndexSeparator","PayloadDiviner","isPayloadDivinerQueryPayload","PayloadHasher","openDB","IndexedDbPayloadDiviner","PayloadDiviner","configSchemas","IndexedDbPayloadDivinerConfigSchema","_db","dbName","config","archivist","IndexedDbArchivist","defaultDbName","dbVersion","defaultDbVersion","storeName","defaultStoreName","db","assertEx","divineHandler","payloads","query","filter","isPayloadDivinerQueryPayload","pop","schemas","limit","offset","hash","order","schema","_schema","sources","props","tx","transaction","store","objectStore","results","parsedOffset","parsedLimit","length","filterSchema","direction","suggestedIndex","selectBestIndex","keyRangeValue","getKeyRangeValue","cursor","index","openCursor","IDBKeyRange","only","advance","push","value","continue","done","map","payload","PayloadHasher","jsonPayload","startHandler","openDB","indexName","extractFields","slice","split","IndexSeparator","field","toLowerCase","indexFields","indexNames","queryKeys","Set","Object","keys","key","bestMatch","matchCount","has"]}
@@ -5,25 +5,19 @@ import { Payload } from '@xyo-network/payload-model';
5
5
  import { IndexedDbPayloadDivinerParams } from './Params';
6
6
  export declare class IndexedDbPayloadDiviner<TParams extends IndexedDbPayloadDivinerParams = IndexedDbPayloadDivinerParams, TIn extends PayloadDivinerQueryPayload = PayloadDivinerQueryPayload, TOut extends Payload = Payload, TEventData extends DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut> = DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut>> extends PayloadDiviner<TParams, TIn, TOut, TEventData> {
7
7
  static configSchemas: string[];
8
- static defaultDbName: string;
9
- static defaultDbVersion: number;
10
- static defaultStoreName: string;
11
8
  private _db;
12
9
  /**
13
- * The database name. If not supplied via config it defaults to
14
- * `archivist`. This behavior biases towards a single, isolated
15
- * DB per archivist which seems to make the most sense for 99% of
16
- * use cases.
10
+ * The database name. If not supplied via config, it defaults
11
+ * to the archivist's name and if archivist's name is not supplied,
12
+ * it defaults to `archivist`. This behavior
13
+ * biases towards a single, isolated DB per archivist which seems to
14
+ * make the most sense for 99% of use cases.
17
15
  */
18
16
  get dbName(): string;
19
17
  /**
20
- * 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.
21
19
  */
22
20
  get dbVersion(): number;
23
- /**
24
- * The database indexes.
25
- */
26
- get indexes(): import("@xyo-network/archivist-model").IndexDescription[];
27
21
  /**
28
22
  * The name of the object store. If not supplied via config, it defaults
29
23
  * to `payloads`.
@@ -32,7 +26,7 @@ export declare class IndexedDbPayloadDiviner<TParams extends IndexedDbPayloadDiv
32
26
  private get db();
33
27
  protected divineHandler(payloads?: TIn[]): Promise<TOut[]>;
34
28
  protected startHandler(): Promise<boolean>;
35
- private getKeyValuesFromQuery;
29
+ private getKeyRangeValue;
36
30
  private selectBestIndex;
37
31
  }
38
32
  //# sourceMappingURL=Diviner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../src/Diviner.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAA;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAA;AACtE,OAAO,EAAgC,0BAA0B,EAAE,MAAM,oCAAoC,CAAA;AAG7G,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAA;AAIpD,OAAO,EAAE,6BAA6B,EAAE,MAAM,UAAU,CAAA;AAMxD,qBAAa,uBAAuB,CAClC,OAAO,SAAS,6BAA6B,GAAG,6BAA6B,EAC7E,GAAG,SAAS,0BAA0B,GAAG,0BAA0B,EACnE,IAAI,SAAS,OAAO,GAAG,OAAO,EAC9B,UAAU,SAAS,sBAAsB,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,sBAAsB,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,CACxI,SAAQ,cAAc,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,CAAC;IACtD,OAAgB,aAAa,WAAwC;IACrE,MAAM,CAAC,aAAa,SAAc;IAClC,MAAM,CAAC,gBAAgB,SAAI;IAC3B,MAAM,CAAC,gBAAgB,SAAa;IAEpC,OAAO,CAAC,GAAG,CAAwC;IAEnD;;;;;OAKG;IACH,IAAI,MAAM,WAET;IAED;;OAEG;IACH,IAAI,SAAS,WAEZ;IAED;;OAEG;IACH,IAAI,OAAO,8DAEV;IAED;;;OAGG;IACH,IAAI,SAAS,WAEZ;IAED,OAAO,KAAK,EAAE,GAEb;cAEwB,aAAa,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;cAqChD,YAAY;IAQrC,OAAO,CAAC,qBAAqB;IAiB7B,OAAO,CAAC,eAAe;CA2BxB"}
1
+ {"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../src/Diviner.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAA;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAA;AACtE,OAAO,EAAgC,0BAA0B,EAAE,MAAM,oCAAoC,CAAA;AAG7G,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAA;AAIpD,OAAO,EAAE,6BAA6B,EAAE,MAAM,UAAU,CAAA;AAMxD,qBAAa,uBAAuB,CAClC,OAAO,SAAS,6BAA6B,GAAG,6BAA6B,EAC7E,GAAG,SAAS,0BAA0B,GAAG,0BAA0B,EACnE,IAAI,SAAS,OAAO,GAAG,OAAO,EAC9B,UAAU,SAAS,sBAAsB,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,sBAAsB,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,CACxI,SAAQ,cAAc,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,CAAC;IACtD,OAAgB,aAAa,WAAwC;IAErE,OAAO,CAAC,GAAG,CAAwC;IAEnD;;;;;;OAMG;IACH,IAAI,MAAM,WAET;IAED;;OAEG;IACH,IAAI,SAAS,WAEZ;IAED;;;OAGG;IACH,IAAI,SAAS,WAEZ;IAED,OAAO,KAAK,EAAE,GAEb;cAEwB,aAAa,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;cAqChD,YAAY;IAQrC,OAAO,CAAC,gBAAgB;IAkBxB,OAAO,CAAC,eAAe;CA2BxB"}
@@ -5,25 +5,19 @@ import { Payload } from '@xyo-network/payload-model';
5
5
  import { IndexedDbPayloadDivinerParams } from './Params';
6
6
  export declare class IndexedDbPayloadDiviner<TParams extends IndexedDbPayloadDivinerParams = IndexedDbPayloadDivinerParams, TIn extends PayloadDivinerQueryPayload = PayloadDivinerQueryPayload, TOut extends Payload = Payload, TEventData extends DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut> = DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut>> extends PayloadDiviner<TParams, TIn, TOut, TEventData> {
7
7
  static configSchemas: string[];
8
- static defaultDbName: string;
9
- static defaultDbVersion: number;
10
- static defaultStoreName: string;
11
8
  private _db;
12
9
  /**
13
- * The database name. If not supplied via config it defaults to
14
- * `archivist`. This behavior biases towards a single, isolated
15
- * DB per archivist which seems to make the most sense for 99% of
16
- * use cases.
10
+ * The database name. If not supplied via config, it defaults
11
+ * to the archivist's name and if archivist's name is not supplied,
12
+ * it defaults to `archivist`. This behavior
13
+ * biases towards a single, isolated DB per archivist which seems to
14
+ * make the most sense for 99% of use cases.
17
15
  */
18
16
  get dbName(): string;
19
17
  /**
20
- * 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.
21
19
  */
22
20
  get dbVersion(): number;
23
- /**
24
- * The database indexes.
25
- */
26
- get indexes(): import("@xyo-network/archivist-model").IndexDescription[];
27
21
  /**
28
22
  * The name of the object store. If not supplied via config, it defaults
29
23
  * to `payloads`.
@@ -32,7 +26,7 @@ export declare class IndexedDbPayloadDiviner<TParams extends IndexedDbPayloadDiv
32
26
  private get db();
33
27
  protected divineHandler(payloads?: TIn[]): Promise<TOut[]>;
34
28
  protected startHandler(): Promise<boolean>;
35
- private getKeyValuesFromQuery;
29
+ private getKeyRangeValue;
36
30
  private selectBestIndex;
37
31
  }
38
32
  //# sourceMappingURL=Diviner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../src/Diviner.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAA;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAA;AACtE,OAAO,EAAgC,0BAA0B,EAAE,MAAM,oCAAoC,CAAA;AAG7G,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAA;AAIpD,OAAO,EAAE,6BAA6B,EAAE,MAAM,UAAU,CAAA;AAMxD,qBAAa,uBAAuB,CAClC,OAAO,SAAS,6BAA6B,GAAG,6BAA6B,EAC7E,GAAG,SAAS,0BAA0B,GAAG,0BAA0B,EACnE,IAAI,SAAS,OAAO,GAAG,OAAO,EAC9B,UAAU,SAAS,sBAAsB,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,sBAAsB,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,CACxI,SAAQ,cAAc,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,CAAC;IACtD,OAAgB,aAAa,WAAwC;IACrE,MAAM,CAAC,aAAa,SAAc;IAClC,MAAM,CAAC,gBAAgB,SAAI;IAC3B,MAAM,CAAC,gBAAgB,SAAa;IAEpC,OAAO,CAAC,GAAG,CAAwC;IAEnD;;;;;OAKG;IACH,IAAI,MAAM,WAET;IAED;;OAEG;IACH,IAAI,SAAS,WAEZ;IAED;;OAEG;IACH,IAAI,OAAO,8DAEV;IAED;;;OAGG;IACH,IAAI,SAAS,WAEZ;IAED,OAAO,KAAK,EAAE,GAEb;cAEwB,aAAa,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;cAqChD,YAAY;IAQrC,OAAO,CAAC,qBAAqB;IAiB7B,OAAO,CAAC,eAAe;CA2BxB"}
1
+ {"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../src/Diviner.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAA;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAA;AACtE,OAAO,EAAgC,0BAA0B,EAAE,MAAM,oCAAoC,CAAA;AAG7G,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAA;AAIpD,OAAO,EAAE,6BAA6B,EAAE,MAAM,UAAU,CAAA;AAMxD,qBAAa,uBAAuB,CAClC,OAAO,SAAS,6BAA6B,GAAG,6BAA6B,EAC7E,GAAG,SAAS,0BAA0B,GAAG,0BAA0B,EACnE,IAAI,SAAS,OAAO,GAAG,OAAO,EAC9B,UAAU,SAAS,sBAAsB,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,sBAAsB,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,CACxI,SAAQ,cAAc,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,CAAC;IACtD,OAAgB,aAAa,WAAwC;IAErE,OAAO,CAAC,GAAG,CAAwC;IAEnD;;;;;;OAMG;IACH,IAAI,MAAM,WAET;IAED;;OAEG;IACH,IAAI,SAAS,WAEZ;IAED;;;OAGG;IACH,IAAI,SAAS,WAEZ;IAED,OAAO,KAAK,EAAE,GAEb;cAEwB,aAAa,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;cAqChD,YAAY;IAQrC,OAAO,CAAC,gBAAgB;IAkBxB,OAAO,CAAC,eAAe;CA2BxB"}
@@ -5,25 +5,19 @@ import { Payload } from '@xyo-network/payload-model';
5
5
  import { IndexedDbPayloadDivinerParams } from './Params';
6
6
  export declare class IndexedDbPayloadDiviner<TParams extends IndexedDbPayloadDivinerParams = IndexedDbPayloadDivinerParams, TIn extends PayloadDivinerQueryPayload = PayloadDivinerQueryPayload, TOut extends Payload = Payload, TEventData extends DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut> = DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut>> extends PayloadDiviner<TParams, TIn, TOut, TEventData> {
7
7
  static configSchemas: string[];
8
- static defaultDbName: string;
9
- static defaultDbVersion: number;
10
- static defaultStoreName: string;
11
8
  private _db;
12
9
  /**
13
- * The database name. If not supplied via config it defaults to
14
- * `archivist`. This behavior biases towards a single, isolated
15
- * DB per archivist which seems to make the most sense for 99% of
16
- * use cases.
10
+ * The database name. If not supplied via config, it defaults
11
+ * to the archivist's name and if archivist's name is not supplied,
12
+ * it defaults to `archivist`. This behavior
13
+ * biases towards a single, isolated DB per archivist which seems to
14
+ * make the most sense for 99% of use cases.
17
15
  */
18
16
  get dbName(): string;
19
17
  /**
20
- * 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.
21
19
  */
22
20
  get dbVersion(): number;
23
- /**
24
- * The database indexes.
25
- */
26
- get indexes(): import("@xyo-network/archivist-model").IndexDescription[];
27
21
  /**
28
22
  * The name of the object store. If not supplied via config, it defaults
29
23
  * to `payloads`.
@@ -32,7 +26,7 @@ export declare class IndexedDbPayloadDiviner<TParams extends IndexedDbPayloadDiv
32
26
  private get db();
33
27
  protected divineHandler(payloads?: TIn[]): Promise<TOut[]>;
34
28
  protected startHandler(): Promise<boolean>;
35
- private getKeyValuesFromQuery;
29
+ private getKeyRangeValue;
36
30
  private selectBestIndex;
37
31
  }
38
32
  //# sourceMappingURL=Diviner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../src/Diviner.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAA;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAA;AACtE,OAAO,EAAgC,0BAA0B,EAAE,MAAM,oCAAoC,CAAA;AAG7G,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAA;AAIpD,OAAO,EAAE,6BAA6B,EAAE,MAAM,UAAU,CAAA;AAMxD,qBAAa,uBAAuB,CAClC,OAAO,SAAS,6BAA6B,GAAG,6BAA6B,EAC7E,GAAG,SAAS,0BAA0B,GAAG,0BAA0B,EACnE,IAAI,SAAS,OAAO,GAAG,OAAO,EAC9B,UAAU,SAAS,sBAAsB,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,sBAAsB,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,CACxI,SAAQ,cAAc,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,CAAC;IACtD,OAAgB,aAAa,WAAwC;IACrE,MAAM,CAAC,aAAa,SAAc;IAClC,MAAM,CAAC,gBAAgB,SAAI;IAC3B,MAAM,CAAC,gBAAgB,SAAa;IAEpC,OAAO,CAAC,GAAG,CAAwC;IAEnD;;;;;OAKG;IACH,IAAI,MAAM,WAET;IAED;;OAEG;IACH,IAAI,SAAS,WAEZ;IAED;;OAEG;IACH,IAAI,OAAO,8DAEV;IAED;;;OAGG;IACH,IAAI,SAAS,WAEZ;IAED,OAAO,KAAK,EAAE,GAEb;cAEwB,aAAa,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;cAqChD,YAAY;IAQrC,OAAO,CAAC,qBAAqB;IAiB7B,OAAO,CAAC,eAAe;CA2BxB"}
1
+ {"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../src/Diviner.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAA;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAA;AACtE,OAAO,EAAgC,0BAA0B,EAAE,MAAM,oCAAoC,CAAA;AAG7G,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAA;AAIpD,OAAO,EAAE,6BAA6B,EAAE,MAAM,UAAU,CAAA;AAMxD,qBAAa,uBAAuB,CAClC,OAAO,SAAS,6BAA6B,GAAG,6BAA6B,EAC7E,GAAG,SAAS,0BAA0B,GAAG,0BAA0B,EACnE,IAAI,SAAS,OAAO,GAAG,OAAO,EAC9B,UAAU,SAAS,sBAAsB,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,sBAAsB,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,CACxI,SAAQ,cAAc,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,CAAC;IACtD,OAAgB,aAAa,WAAwC;IAErE,OAAO,CAAC,GAAG,CAAwC;IAEnD;;;;;;OAMG;IACH,IAAI,MAAM,WAET;IAED;;OAEG;IACH,IAAI,SAAS,WAEZ;IAED;;;OAGG;IACH,IAAI,SAAS,WAEZ;IAED,OAAO,KAAK,EAAE,GAEb;cAEwB,aAAa,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;cAqChD,YAAY;IAQrC,OAAO,CAAC,gBAAgB;IAkBxB,OAAO,CAAC,eAAe;CA2BxB"}
@@ -41,6 +41,7 @@ var IndexedDbPayloadDivinerConfigSchema = `${IndexedDbPayloadDivinerSchema}.conf
41
41
 
42
42
  // src/Diviner.ts
43
43
  var import_assert = require("@xylabs/assert");
44
+ var import_archivist_indexeddb = require("@xyo-network/archivist-indexeddb");
44
45
  var import_archivist_model = require("@xyo-network/archivist-model");
45
46
  var import_diviner_payload_abstract = require("@xyo-network/diviner-payload-abstract");
46
47
  var import_diviner_payload_model2 = require("@xyo-network/diviner-payload-model");
@@ -49,28 +50,22 @@ var import_idb = require("idb");
49
50
  var _IndexedDbPayloadDiviner = class _IndexedDbPayloadDiviner extends import_diviner_payload_abstract.PayloadDiviner {
50
51
  _db;
51
52
  /**
52
- * The database name. If not supplied via config it defaults to
53
- * `archivist`. This behavior biases towards a single, isolated
54
- * DB per archivist which seems to make the most sense for 99% of
55
- * use cases.
53
+ * The database name. If not supplied via config, it defaults
54
+ * to the archivist's name and if archivist's name is not supplied,
55
+ * it defaults to `archivist`. This behavior
56
+ * biases towards a single, isolated DB per archivist which seems to
57
+ * make the most sense for 99% of use cases.
56
58
  */
57
59
  get dbName() {
58
- var _a;
59
- return ((_a = this.config) == null ? void 0 : _a.dbName) ?? _IndexedDbPayloadDiviner.defaultDbName;
60
+ var _a, _b;
61
+ return ((_a = this.config) == null ? void 0 : _a.dbName) ?? ((_b = this.config) == null ? void 0 : _b.archivist) ?? import_archivist_indexeddb.IndexedDbArchivist.defaultDbName;
60
62
  }
61
63
  /**
62
- * The database version. If not supplied via config, it defaults to 1.
64
+ * The database version. If not supplied via config, it defaults to the archivist default version.
63
65
  */
64
66
  get dbVersion() {
65
67
  var _a;
66
- return ((_a = this.config) == null ? void 0 : _a.dbVersion) ?? _IndexedDbPayloadDiviner.defaultDbVersion;
67
- }
68
- /**
69
- * The database indexes.
70
- */
71
- get indexes() {
72
- var _a, _b;
73
- return ((_b = (_a = this.config) == null ? void 0 : _a.storage) == null ? void 0 : _b.indexes) ?? [];
68
+ return ((_a = this.config) == null ? void 0 : _a.dbVersion) ?? import_archivist_indexeddb.IndexedDbArchivist.defaultDbVersion;
74
69
  }
75
70
  /**
76
71
  * The name of the object store. If not supplied via config, it defaults
@@ -78,7 +73,7 @@ var _IndexedDbPayloadDiviner = class _IndexedDbPayloadDiviner extends import_div
78
73
  */
79
74
  get storeName() {
80
75
  var _a;
81
- return ((_a = this.config) == null ? void 0 : _a.storeName) ?? _IndexedDbPayloadDiviner.defaultStoreName;
76
+ return ((_a = this.config) == null ? void 0 : _a.storeName) ?? import_archivist_indexeddb.IndexedDbArchivist.defaultStoreName;
82
77
  }
83
78
  get db() {
84
79
  return (0, import_assert.assertEx)(this._db, "DB not initialized");
@@ -104,8 +99,8 @@ var _IndexedDbPayloadDiviner = class _IndexedDbPayloadDiviner extends import_div
104
99
  };
105
100
  const direction = order === "desc" ? "prev" : "next";
106
101
  const suggestedIndex = this.selectBestIndex(filter, store);
107
- const filterValues = this.getKeyValuesFromQuery(suggestedIndex, filter);
108
- let cursor = suggestedIndex ? await store.index(suggestedIndex).openCursor(IDBKeyRange.only(filterValues.length === 1 ? filterValues[0] : filterValues), direction) : await store.openCursor(suggestedIndex, direction);
102
+ const keyRangeValue = this.getKeyRangeValue(suggestedIndex, filter);
103
+ let cursor = suggestedIndex ? await store.index(suggestedIndex).openCursor(IDBKeyRange.only(keyRangeValue), direction) : await store.openCursor(suggestedIndex, direction);
109
104
  while (cursor && parsedOffset > 0) {
110
105
  cursor = await cursor.advance(parsedOffset);
111
106
  parsedOffset = 0;
@@ -122,14 +117,15 @@ var _IndexedDbPayloadDiviner = class _IndexedDbPayloadDiviner extends import_div
122
117
  this._db = await (0, import_idb.openDB)(this.dbName, this.dbVersion);
123
118
  return true;
124
119
  }
125
- getKeyValuesFromQuery(indexName, query) {
120
+ getKeyRangeValue(indexName, query) {
126
121
  if (!indexName)
127
122
  return [];
128
123
  const extractFields = /* @__PURE__ */ __name((indexName2) => {
129
124
  return indexName2.slice(3).split(import_archivist_model.IndexSeparator).map((field) => field.toLowerCase());
130
125
  }, "extractFields");
131
126
  const indexFields = extractFields(indexName);
132
- return indexFields.map((field) => query[field]);
127
+ const keyRangeValue = indexFields.map((field) => query[field]);
128
+ return keyRangeValue.length === 1 ? keyRangeValue[0] : keyRangeValue;
133
129
  }
134
130
  selectBestIndex(query, store) {
135
131
  const { indexNames } = store;
@@ -158,9 +154,6 @@ __name(_IndexedDbPayloadDiviner, "IndexedDbPayloadDiviner");
158
154
  __publicField(_IndexedDbPayloadDiviner, "configSchemas", [
159
155
  IndexedDbPayloadDivinerConfigSchema
160
156
  ]);
161
- __publicField(_IndexedDbPayloadDiviner, "defaultDbName", "archivist");
162
- __publicField(_IndexedDbPayloadDiviner, "defaultDbVersion", 1);
163
- __publicField(_IndexedDbPayloadDiviner, "defaultStoreName", "payloads");
164
157
  var IndexedDbPayloadDiviner = _IndexedDbPayloadDiviner;
165
158
  // Annotate the CommonJS export names for ESM import in node:
166
159
  0 && (module.exports = {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/index.ts","../../src/Schema.ts","../../src/Config.ts","../../src/Diviner.ts"],"sourcesContent":["export * from './Config'\nexport * from './Diviner'\nexport * from './Params'\nexport * from './Schema'\n","import { PayloadDivinerSchema } from '@xyo-network/diviner-payload-model'\n\nexport const IndexedDbPayloadDivinerSchema = `${PayloadDivinerSchema}.indexeddb`\nexport type IndexedDbPayloadDivinerSchema = typeof IndexedDbPayloadDivinerSchema\n","import { IndexDescription } from '@xyo-network/archivist-model'\nimport { DivinerConfig } from '@xyo-network/diviner-model'\n\nimport { IndexedDbPayloadDivinerSchema } from './Schema'\n\nexport const IndexedDbPayloadDivinerConfigSchema = `${IndexedDbPayloadDivinerSchema}.config`\nexport type IndexedDbPayloadDivinerConfigSchema = typeof IndexedDbPayloadDivinerConfigSchema\n\nexport type IndexedDbPayloadDivinerConfig = 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: IndexedDbPayloadDivinerConfigSchema\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 { assertEx } from '@xylabs/assert'\nimport { IndexSeparator } from '@xyo-network/archivist-model'\nimport { DivinerModule, DivinerModuleEventData } from '@xyo-network/diviner-model'\nimport { PayloadDiviner } from '@xyo-network/diviner-payload-abstract'\nimport { isPayloadDivinerQueryPayload, PayloadDivinerQueryPayload } from '@xyo-network/diviner-payload-model'\nimport { PayloadHasher } from '@xyo-network/hash'\nimport { AnyObject } from '@xyo-network/object'\nimport { Payload } from '@xyo-network/payload-model'\nimport { IDBPDatabase, IDBPObjectStore, openDB } from 'idb'\n\nimport { IndexedDbPayloadDivinerConfigSchema } from './Config'\nimport { IndexedDbPayloadDivinerParams } from './Params'\n\ninterface PayloadStore {\n [s: string]: Payload\n}\n\nexport class IndexedDbPayloadDiviner<\n TParams extends IndexedDbPayloadDivinerParams = IndexedDbPayloadDivinerParams,\n TIn extends PayloadDivinerQueryPayload = PayloadDivinerQueryPayload,\n TOut extends Payload = Payload,\n TEventData extends DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut> = DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut>,\n> extends PayloadDiviner<TParams, TIn, TOut, TEventData> {\n static override configSchemas = [IndexedDbPayloadDivinerConfigSchema]\n static defaultDbName = 'archivist'\n static defaultDbVersion = 1\n static defaultStoreName = 'payloads'\n\n private _db: IDBPDatabase<PayloadStore> | undefined\n\n /**\n * The database name. If not supplied via config it defaults to\n * `archivist`. This behavior biases towards a single, isolated\n * DB per archivist which seems to make the most sense for 99% of\n * use cases.\n */\n get dbName() {\n return this.config?.dbName ?? IndexedDbPayloadDiviner.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 ?? IndexedDbPayloadDiviner.defaultDbVersion\n }\n\n /**\n * The database indexes.\n */\n get indexes() {\n return this.config?.storage?.indexes ?? []\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 ?? IndexedDbPayloadDiviner.defaultStoreName\n }\n\n private get db(): IDBPDatabase<PayloadStore> {\n return assertEx(this._db, 'DB not initialized')\n }\n\n protected override async divineHandler(payloads?: TIn[]): Promise<TOut[]> {\n const query = assertEx(payloads?.filter(isPayloadDivinerQueryPayload)?.pop(), 'Missing query payload')\n if (!query) return []\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { schemas, limit, offset, hash, order, schema: _schema, sources, ...props } = query as unknown as TIn & { sources?: string[] }\n const tx = this.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 assertEx((schemas?.length ?? 1) === 1, 'IndexedDbPayloadDiviner: Only one filter schema supported')\n const filterSchema = schemas?.[0]\n const filter = filterSchema ? { schema: filterSchema, ...props } : { ...props }\n const direction: IDBCursorDirection = order === 'desc' ? 'prev' : 'next'\n const suggestedIndex = this.selectBestIndex(filter, store)\n const filterValues = this.getKeyValuesFromQuery(suggestedIndex, filter)\n let cursor = suggestedIndex\n ? // Conditionally filter on schemas\n await store.index(suggestedIndex).openCursor(IDBKeyRange.only(filterValues.length === 1 ? filterValues[0] : filterValues), direction)\n : // Just iterate all records\n await store.openCursor(suggestedIndex, direction)\n\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 // Collect results up to the limit\n while (cursor && results.length < parsedLimit) {\n results.push(cursor.value)\n cursor = await cursor.continue()\n }\n await tx.done\n // Remove any metadata before returning to the client\n return results.map((payload) => PayloadHasher.jsonPayload(payload))\n }\n\n protected override async startHandler() {\n await super.startHandler()\n // NOTE: We could defer this creation to first access but we\n // want to fail fast here in case something is wrong\n this._db = await openDB<PayloadStore>(this.dbName, this.dbVersion)\n return true\n }\n\n private getKeyValuesFromQuery(indexName: string | null, query: AnyObject): unknown[] {\n if (!indexName) return []\n // Function to extract fields from an index name\n const extractFields = (indexName: string): string[] => {\n return indexName\n .slice(3)\n .split(IndexSeparator)\n .map((field) => field.toLowerCase())\n }\n\n // Extracting the relevant fields from the index name\n const indexFields = extractFields(indexName)\n\n // Collecting the values for these fields from the query object\n return indexFields.map((field) => query[field as keyof AnyObject])\n }\n\n private selectBestIndex(query: AnyObject, store: IDBPObjectStore<PayloadStore>): string | null {\n // List of available indexes\n const { indexNames } = store\n\n // Function to extract fields from an index name\n const extractFields = (indexName: string): string[] => {\n return indexName\n .slice(3)\n .split(IndexSeparator)\n .map((field) => field.toLowerCase())\n }\n\n // Convert query object keys to a set for easier comparison\n const queryKeys = new Set(Object.keys(query).map((key) => key.toLowerCase()))\n\n // Find the best matching index\n let bestMatch: { indexName: string; matchCount: number } = { indexName: '', matchCount: 0 }\n\n for (const indexName of indexNames) {\n const indexFields = extractFields(indexName)\n const matchCount = indexFields.filter((field) => queryKeys.has(field)).length\n if (matchCount > bestMatch.matchCount) {\n bestMatch = { indexName, matchCount }\n }\n }\n return bestMatch.matchCount > 0 ? bestMatch.indexName : null\n }\n}\n"],"mappings":"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","names":["IndexedDbPayloadDivinerSchema","PayloadDivinerSchema","IndexedDbPayloadDivinerConfigSchema","IndexedDbPayloadDivinerSchema","import_diviner_payload_model","IndexedDbPayloadDiviner","PayloadDiviner","_db","dbName","config","defaultDbName","dbVersion","defaultDbVersion","indexes","storage","storeName","defaultStoreName","db","assertEx","divineHandler","payloads","query","filter","isPayloadDivinerQueryPayload","pop","schemas","limit","offset","hash","order","schema","_schema","sources","props","tx","transaction","store","objectStore","results","parsedOffset","parsedLimit","length","filterSchema","direction","suggestedIndex","selectBestIndex","filterValues","getKeyValuesFromQuery","cursor","index","openCursor","IDBKeyRange","only","advance","push","value","continue","done","map","payload","PayloadHasher","jsonPayload","startHandler","openDB","indexName","extractFields","slice","split","IndexSeparator","field","toLowerCase","indexFields","indexNames","queryKeys","Set","Object","keys","key","bestMatch","matchCount","has","configSchemas","IndexedDbPayloadDivinerConfigSchema"]}
1
+ {"version":3,"sources":["../../src/index.ts","../../src/Schema.ts","../../src/Config.ts","../../src/Diviner.ts"],"sourcesContent":["export * from './Config'\nexport * from './Diviner'\nexport * from './Params'\nexport * from './Schema'\n","import { PayloadDivinerSchema } from '@xyo-network/diviner-payload-model'\n\nexport const IndexedDbPayloadDivinerSchema = `${PayloadDivinerSchema}.indexeddb`\nexport type IndexedDbPayloadDivinerSchema = typeof IndexedDbPayloadDivinerSchema\n","import { IndexDescription } from '@xyo-network/archivist-model'\nimport { DivinerConfig } from '@xyo-network/diviner-model'\n\nimport { IndexedDbPayloadDivinerSchema } from './Schema'\n\nexport const IndexedDbPayloadDivinerConfigSchema = `${IndexedDbPayloadDivinerSchema}.config`\nexport type IndexedDbPayloadDivinerConfigSchema = typeof IndexedDbPayloadDivinerConfigSchema\n\nexport type IndexedDbPayloadDivinerConfig = 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: IndexedDbPayloadDivinerConfigSchema\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 { assertEx } from '@xylabs/assert'\nimport { IndexedDbArchivist } from '@xyo-network/archivist-indexeddb'\nimport { IndexSeparator } from '@xyo-network/archivist-model'\nimport { DivinerModule, DivinerModuleEventData } from '@xyo-network/diviner-model'\nimport { PayloadDiviner } from '@xyo-network/diviner-payload-abstract'\nimport { isPayloadDivinerQueryPayload, PayloadDivinerQueryPayload } from '@xyo-network/diviner-payload-model'\nimport { PayloadHasher } from '@xyo-network/hash'\nimport { AnyObject } from '@xyo-network/object'\nimport { Payload } from '@xyo-network/payload-model'\nimport { IDBPDatabase, IDBPObjectStore, openDB } from 'idb'\n\nimport { IndexedDbPayloadDivinerConfigSchema } from './Config'\nimport { IndexedDbPayloadDivinerParams } from './Params'\n\ninterface PayloadStore {\n [s: string]: Payload\n}\n\nexport class IndexedDbPayloadDiviner<\n TParams extends IndexedDbPayloadDivinerParams = IndexedDbPayloadDivinerParams,\n TIn extends PayloadDivinerQueryPayload = PayloadDivinerQueryPayload,\n TOut extends Payload = Payload,\n TEventData extends DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut> = DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut>,\n> extends PayloadDiviner<TParams, TIn, TOut, TEventData> {\n static override configSchemas = [IndexedDbPayloadDivinerConfigSchema]\n\n private _db: IDBPDatabase<PayloadStore> | undefined\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 private get db(): IDBPDatabase<PayloadStore> {\n return assertEx(this._db, 'DB not initialized')\n }\n\n protected override async divineHandler(payloads?: TIn[]): Promise<TOut[]> {\n const query = assertEx(payloads?.filter(isPayloadDivinerQueryPayload)?.pop(), 'Missing query payload')\n if (!query) return []\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { schemas, limit, offset, hash, order, schema: _schema, sources, ...props } = query as unknown as TIn & { sources?: string[] }\n const tx = this.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 assertEx((schemas?.length ?? 1) === 1, 'IndexedDbPayloadDiviner: Only one filter schema supported')\n const filterSchema = schemas?.[0]\n const filter = filterSchema ? { schema: filterSchema, ...props } : { ...props }\n const direction: IDBCursorDirection = order === 'desc' ? 'prev' : 'next'\n const suggestedIndex = this.selectBestIndex(filter, store)\n const keyRangeValue = this.getKeyRangeValue(suggestedIndex, filter)\n let cursor = suggestedIndex\n ? // Conditionally filter on schemas\n await store.index(suggestedIndex).openCursor(IDBKeyRange.only(keyRangeValue), direction)\n : // Just iterate all records\n await store.openCursor(suggestedIndex, direction)\n\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 // Collect results up to the limit\n while (cursor && results.length < parsedLimit) {\n results.push(cursor.value)\n cursor = await cursor.continue()\n }\n await tx.done\n // Remove any metadata before returning to the client\n return results.map((payload) => PayloadHasher.jsonPayload(payload))\n }\n\n protected override async startHandler() {\n await super.startHandler()\n // NOTE: We could defer this creation to first access but we\n // want to fail fast here in case something is wrong\n this._db = await openDB<PayloadStore>(this.dbName, this.dbVersion)\n return true\n }\n\n private getKeyRangeValue(indexName: string | null, query: AnyObject): unknown | unknown[] {\n if (!indexName) return []\n // Function to extract fields from an index name\n const extractFields = (indexName: string): string[] => {\n return indexName\n .slice(3)\n .split(IndexSeparator)\n .map((field) => field.toLowerCase())\n }\n\n // Extracting the relevant fields from the index name\n const indexFields = extractFields(indexName)\n\n // Collecting the values for these fields from the query object\n const keyRangeValue = indexFields.map((field) => query[field as keyof AnyObject])\n return keyRangeValue.length === 1 ? keyRangeValue[0] : keyRangeValue\n }\n\n private selectBestIndex(query: AnyObject, store: IDBPObjectStore<PayloadStore>): string | null {\n // List of available indexes\n const { indexNames } = store\n\n // Function to extract fields from an index name\n const extractFields = (indexName: string): string[] => {\n return indexName\n .slice(3)\n .split(IndexSeparator)\n .map((field) => field.toLowerCase())\n }\n\n // Convert query object keys to a set for easier comparison\n const queryKeys = new Set(Object.keys(query).map((key) => key.toLowerCase()))\n\n // Find the best matching index\n let bestMatch: { indexName: string; matchCount: number } = { indexName: '', matchCount: 0 }\n\n for (const indexName of indexNames) {\n const indexFields = extractFields(indexName)\n const matchCount = indexFields.filter((field) => queryKeys.has(field)).length\n if (matchCount > bestMatch.matchCount) {\n bestMatch = { indexName, matchCount }\n }\n }\n return bestMatch.matchCount > 0 ? bestMatch.indexName : null\n }\n}\n"],"mappings":"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","names":["IndexedDbPayloadDivinerSchema","PayloadDivinerSchema","IndexedDbPayloadDivinerConfigSchema","IndexedDbPayloadDivinerSchema","import_diviner_payload_model","IndexedDbPayloadDiviner","PayloadDiviner","_db","dbName","config","archivist","IndexedDbArchivist","defaultDbName","dbVersion","defaultDbVersion","storeName","defaultStoreName","db","assertEx","divineHandler","payloads","query","filter","isPayloadDivinerQueryPayload","pop","schemas","limit","offset","hash","order","schema","_schema","sources","props","tx","transaction","store","objectStore","results","parsedOffset","parsedLimit","length","filterSchema","direction","suggestedIndex","selectBestIndex","keyRangeValue","getKeyRangeValue","cursor","index","openCursor","IDBKeyRange","only","advance","push","value","continue","done","map","payload","PayloadHasher","jsonPayload","startHandler","openDB","indexName","extractFields","slice","split","IndexSeparator","field","toLowerCase","indexFields","indexNames","queryKeys","Set","Object","keys","key","bestMatch","matchCount","has","configSchemas","IndexedDbPayloadDivinerConfigSchema"]}
@@ -15,6 +15,7 @@ var IndexedDbPayloadDivinerConfigSchema = `${IndexedDbPayloadDivinerSchema}.conf
15
15
 
16
16
  // src/Diviner.ts
17
17
  import { assertEx } from "@xylabs/assert";
18
+ import { IndexedDbArchivist } from "@xyo-network/archivist-indexeddb";
18
19
  import { IndexSeparator } from "@xyo-network/archivist-model";
19
20
  import { PayloadDiviner } from "@xyo-network/diviner-payload-abstract";
20
21
  import { isPayloadDivinerQueryPayload } from "@xyo-network/diviner-payload-model";
@@ -23,28 +24,22 @@ import { openDB } from "idb";
23
24
  var _IndexedDbPayloadDiviner = class _IndexedDbPayloadDiviner extends PayloadDiviner {
24
25
  _db;
25
26
  /**
26
- * The database name. If not supplied via config it defaults to
27
- * `archivist`. This behavior biases towards a single, isolated
28
- * DB per archivist which seems to make the most sense for 99% of
29
- * use cases.
27
+ * The database name. If not supplied via config, it defaults
28
+ * to the archivist's name and if archivist's name is not supplied,
29
+ * it defaults to `archivist`. This behavior
30
+ * biases towards a single, isolated DB per archivist which seems to
31
+ * make the most sense for 99% of use cases.
30
32
  */
31
33
  get dbName() {
32
- var _a;
33
- return ((_a = this.config) == null ? void 0 : _a.dbName) ?? _IndexedDbPayloadDiviner.defaultDbName;
34
+ var _a, _b;
35
+ return ((_a = this.config) == null ? void 0 : _a.dbName) ?? ((_b = this.config) == null ? void 0 : _b.archivist) ?? IndexedDbArchivist.defaultDbName;
34
36
  }
35
37
  /**
36
- * The database version. If not supplied via config, it defaults to 1.
38
+ * The database version. If not supplied via config, it defaults to the archivist default version.
37
39
  */
38
40
  get dbVersion() {
39
41
  var _a;
40
- return ((_a = this.config) == null ? void 0 : _a.dbVersion) ?? _IndexedDbPayloadDiviner.defaultDbVersion;
41
- }
42
- /**
43
- * The database indexes.
44
- */
45
- get indexes() {
46
- var _a, _b;
47
- return ((_b = (_a = this.config) == null ? void 0 : _a.storage) == null ? void 0 : _b.indexes) ?? [];
42
+ return ((_a = this.config) == null ? void 0 : _a.dbVersion) ?? IndexedDbArchivist.defaultDbVersion;
48
43
  }
49
44
  /**
50
45
  * The name of the object store. If not supplied via config, it defaults
@@ -52,7 +47,7 @@ var _IndexedDbPayloadDiviner = class _IndexedDbPayloadDiviner extends PayloadDiv
52
47
  */
53
48
  get storeName() {
54
49
  var _a;
55
- return ((_a = this.config) == null ? void 0 : _a.storeName) ?? _IndexedDbPayloadDiviner.defaultStoreName;
50
+ return ((_a = this.config) == null ? void 0 : _a.storeName) ?? IndexedDbArchivist.defaultStoreName;
56
51
  }
57
52
  get db() {
58
53
  return assertEx(this._db, "DB not initialized");
@@ -78,8 +73,8 @@ var _IndexedDbPayloadDiviner = class _IndexedDbPayloadDiviner extends PayloadDiv
78
73
  };
79
74
  const direction = order === "desc" ? "prev" : "next";
80
75
  const suggestedIndex = this.selectBestIndex(filter, store);
81
- const filterValues = this.getKeyValuesFromQuery(suggestedIndex, filter);
82
- let cursor = suggestedIndex ? await store.index(suggestedIndex).openCursor(IDBKeyRange.only(filterValues.length === 1 ? filterValues[0] : filterValues), direction) : await store.openCursor(suggestedIndex, direction);
76
+ const keyRangeValue = this.getKeyRangeValue(suggestedIndex, filter);
77
+ let cursor = suggestedIndex ? await store.index(suggestedIndex).openCursor(IDBKeyRange.only(keyRangeValue), direction) : await store.openCursor(suggestedIndex, direction);
83
78
  while (cursor && parsedOffset > 0) {
84
79
  cursor = await cursor.advance(parsedOffset);
85
80
  parsedOffset = 0;
@@ -96,14 +91,15 @@ var _IndexedDbPayloadDiviner = class _IndexedDbPayloadDiviner extends PayloadDiv
96
91
  this._db = await openDB(this.dbName, this.dbVersion);
97
92
  return true;
98
93
  }
99
- getKeyValuesFromQuery(indexName, query) {
94
+ getKeyRangeValue(indexName, query) {
100
95
  if (!indexName)
101
96
  return [];
102
97
  const extractFields = /* @__PURE__ */ __name((indexName2) => {
103
98
  return indexName2.slice(3).split(IndexSeparator).map((field) => field.toLowerCase());
104
99
  }, "extractFields");
105
100
  const indexFields = extractFields(indexName);
106
- return indexFields.map((field) => query[field]);
101
+ const keyRangeValue = indexFields.map((field) => query[field]);
102
+ return keyRangeValue.length === 1 ? keyRangeValue[0] : keyRangeValue;
107
103
  }
108
104
  selectBestIndex(query, store) {
109
105
  const { indexNames } = store;
@@ -132,9 +128,6 @@ __name(_IndexedDbPayloadDiviner, "IndexedDbPayloadDiviner");
132
128
  __publicField(_IndexedDbPayloadDiviner, "configSchemas", [
133
129
  IndexedDbPayloadDivinerConfigSchema
134
130
  ]);
135
- __publicField(_IndexedDbPayloadDiviner, "defaultDbName", "archivist");
136
- __publicField(_IndexedDbPayloadDiviner, "defaultDbVersion", 1);
137
- __publicField(_IndexedDbPayloadDiviner, "defaultStoreName", "payloads");
138
131
  var IndexedDbPayloadDiviner = _IndexedDbPayloadDiviner;
139
132
  export {
140
133
  IndexedDbPayloadDiviner,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/Schema.ts","../../src/Config.ts","../../src/Diviner.ts"],"sourcesContent":["import { PayloadDivinerSchema } from '@xyo-network/diviner-payload-model'\n\nexport const IndexedDbPayloadDivinerSchema = `${PayloadDivinerSchema}.indexeddb`\nexport type IndexedDbPayloadDivinerSchema = typeof IndexedDbPayloadDivinerSchema\n","import { IndexDescription } from '@xyo-network/archivist-model'\nimport { DivinerConfig } from '@xyo-network/diviner-model'\n\nimport { IndexedDbPayloadDivinerSchema } from './Schema'\n\nexport const IndexedDbPayloadDivinerConfigSchema = `${IndexedDbPayloadDivinerSchema}.config`\nexport type IndexedDbPayloadDivinerConfigSchema = typeof IndexedDbPayloadDivinerConfigSchema\n\nexport type IndexedDbPayloadDivinerConfig = 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: IndexedDbPayloadDivinerConfigSchema\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 { assertEx } from '@xylabs/assert'\nimport { IndexSeparator } from '@xyo-network/archivist-model'\nimport { DivinerModule, DivinerModuleEventData } from '@xyo-network/diviner-model'\nimport { PayloadDiviner } from '@xyo-network/diviner-payload-abstract'\nimport { isPayloadDivinerQueryPayload, PayloadDivinerQueryPayload } from '@xyo-network/diviner-payload-model'\nimport { PayloadHasher } from '@xyo-network/hash'\nimport { AnyObject } from '@xyo-network/object'\nimport { Payload } from '@xyo-network/payload-model'\nimport { IDBPDatabase, IDBPObjectStore, openDB } from 'idb'\n\nimport { IndexedDbPayloadDivinerConfigSchema } from './Config'\nimport { IndexedDbPayloadDivinerParams } from './Params'\n\ninterface PayloadStore {\n [s: string]: Payload\n}\n\nexport class IndexedDbPayloadDiviner<\n TParams extends IndexedDbPayloadDivinerParams = IndexedDbPayloadDivinerParams,\n TIn extends PayloadDivinerQueryPayload = PayloadDivinerQueryPayload,\n TOut extends Payload = Payload,\n TEventData extends DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut> = DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut>,\n> extends PayloadDiviner<TParams, TIn, TOut, TEventData> {\n static override configSchemas = [IndexedDbPayloadDivinerConfigSchema]\n static defaultDbName = 'archivist'\n static defaultDbVersion = 1\n static defaultStoreName = 'payloads'\n\n private _db: IDBPDatabase<PayloadStore> | undefined\n\n /**\n * The database name. If not supplied via config it defaults to\n * `archivist`. This behavior biases towards a single, isolated\n * DB per archivist which seems to make the most sense for 99% of\n * use cases.\n */\n get dbName() {\n return this.config?.dbName ?? IndexedDbPayloadDiviner.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 ?? IndexedDbPayloadDiviner.defaultDbVersion\n }\n\n /**\n * The database indexes.\n */\n get indexes() {\n return this.config?.storage?.indexes ?? []\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 ?? IndexedDbPayloadDiviner.defaultStoreName\n }\n\n private get db(): IDBPDatabase<PayloadStore> {\n return assertEx(this._db, 'DB not initialized')\n }\n\n protected override async divineHandler(payloads?: TIn[]): Promise<TOut[]> {\n const query = assertEx(payloads?.filter(isPayloadDivinerQueryPayload)?.pop(), 'Missing query payload')\n if (!query) return []\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { schemas, limit, offset, hash, order, schema: _schema, sources, ...props } = query as unknown as TIn & { sources?: string[] }\n const tx = this.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 assertEx((schemas?.length ?? 1) === 1, 'IndexedDbPayloadDiviner: Only one filter schema supported')\n const filterSchema = schemas?.[0]\n const filter = filterSchema ? { schema: filterSchema, ...props } : { ...props }\n const direction: IDBCursorDirection = order === 'desc' ? 'prev' : 'next'\n const suggestedIndex = this.selectBestIndex(filter, store)\n const filterValues = this.getKeyValuesFromQuery(suggestedIndex, filter)\n let cursor = suggestedIndex\n ? // Conditionally filter on schemas\n await store.index(suggestedIndex).openCursor(IDBKeyRange.only(filterValues.length === 1 ? filterValues[0] : filterValues), direction)\n : // Just iterate all records\n await store.openCursor(suggestedIndex, direction)\n\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 // Collect results up to the limit\n while (cursor && results.length < parsedLimit) {\n results.push(cursor.value)\n cursor = await cursor.continue()\n }\n await tx.done\n // Remove any metadata before returning to the client\n return results.map((payload) => PayloadHasher.jsonPayload(payload))\n }\n\n protected override async startHandler() {\n await super.startHandler()\n // NOTE: We could defer this creation to first access but we\n // want to fail fast here in case something is wrong\n this._db = await openDB<PayloadStore>(this.dbName, this.dbVersion)\n return true\n }\n\n private getKeyValuesFromQuery(indexName: string | null, query: AnyObject): unknown[] {\n if (!indexName) return []\n // Function to extract fields from an index name\n const extractFields = (indexName: string): string[] => {\n return indexName\n .slice(3)\n .split(IndexSeparator)\n .map((field) => field.toLowerCase())\n }\n\n // Extracting the relevant fields from the index name\n const indexFields = extractFields(indexName)\n\n // Collecting the values for these fields from the query object\n return indexFields.map((field) => query[field as keyof AnyObject])\n }\n\n private selectBestIndex(query: AnyObject, store: IDBPObjectStore<PayloadStore>): string | null {\n // List of available indexes\n const { indexNames } = store\n\n // Function to extract fields from an index name\n const extractFields = (indexName: string): string[] => {\n return indexName\n .slice(3)\n .split(IndexSeparator)\n .map((field) => field.toLowerCase())\n }\n\n // Convert query object keys to a set for easier comparison\n const queryKeys = new Set(Object.keys(query).map((key) => key.toLowerCase()))\n\n // Find the best matching index\n let bestMatch: { indexName: string; matchCount: number } = { indexName: '', matchCount: 0 }\n\n for (const indexName of indexNames) {\n const indexFields = extractFields(indexName)\n const matchCount = indexFields.filter((field) => queryKeys.has(field)).length\n if (matchCount > bestMatch.matchCount) {\n bestMatch = { indexName, matchCount }\n }\n }\n return bestMatch.matchCount > 0 ? bestMatch.indexName : null\n }\n}\n"],"mappings":"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","names":["PayloadDivinerSchema","IndexedDbPayloadDivinerSchema","IndexedDbPayloadDivinerConfigSchema","IndexedDbPayloadDivinerSchema","assertEx","IndexSeparator","PayloadDiviner","isPayloadDivinerQueryPayload","PayloadHasher","openDB","IndexedDbPayloadDiviner","PayloadDiviner","_db","dbName","config","defaultDbName","dbVersion","defaultDbVersion","indexes","storage","storeName","defaultStoreName","db","assertEx","divineHandler","payloads","query","filter","isPayloadDivinerQueryPayload","pop","schemas","limit","offset","hash","order","schema","_schema","sources","props","tx","transaction","store","objectStore","results","parsedOffset","parsedLimit","length","filterSchema","direction","suggestedIndex","selectBestIndex","filterValues","getKeyValuesFromQuery","cursor","index","openCursor","IDBKeyRange","only","advance","push","value","continue","done","map","payload","PayloadHasher","jsonPayload","startHandler","openDB","indexName","extractFields","slice","split","IndexSeparator","field","toLowerCase","indexFields","indexNames","queryKeys","Set","Object","keys","key","bestMatch","matchCount","has","configSchemas","IndexedDbPayloadDivinerConfigSchema"]}
1
+ {"version":3,"sources":["../../src/Schema.ts","../../src/Config.ts","../../src/Diviner.ts"],"sourcesContent":["import { PayloadDivinerSchema } from '@xyo-network/diviner-payload-model'\n\nexport const IndexedDbPayloadDivinerSchema = `${PayloadDivinerSchema}.indexeddb`\nexport type IndexedDbPayloadDivinerSchema = typeof IndexedDbPayloadDivinerSchema\n","import { IndexDescription } from '@xyo-network/archivist-model'\nimport { DivinerConfig } from '@xyo-network/diviner-model'\n\nimport { IndexedDbPayloadDivinerSchema } from './Schema'\n\nexport const IndexedDbPayloadDivinerConfigSchema = `${IndexedDbPayloadDivinerSchema}.config`\nexport type IndexedDbPayloadDivinerConfigSchema = typeof IndexedDbPayloadDivinerConfigSchema\n\nexport type IndexedDbPayloadDivinerConfig = 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: IndexedDbPayloadDivinerConfigSchema\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 { assertEx } from '@xylabs/assert'\nimport { IndexedDbArchivist } from '@xyo-network/archivist-indexeddb'\nimport { IndexSeparator } from '@xyo-network/archivist-model'\nimport { DivinerModule, DivinerModuleEventData } from '@xyo-network/diviner-model'\nimport { PayloadDiviner } from '@xyo-network/diviner-payload-abstract'\nimport { isPayloadDivinerQueryPayload, PayloadDivinerQueryPayload } from '@xyo-network/diviner-payload-model'\nimport { PayloadHasher } from '@xyo-network/hash'\nimport { AnyObject } from '@xyo-network/object'\nimport { Payload } from '@xyo-network/payload-model'\nimport { IDBPDatabase, IDBPObjectStore, openDB } from 'idb'\n\nimport { IndexedDbPayloadDivinerConfigSchema } from './Config'\nimport { IndexedDbPayloadDivinerParams } from './Params'\n\ninterface PayloadStore {\n [s: string]: Payload\n}\n\nexport class IndexedDbPayloadDiviner<\n TParams extends IndexedDbPayloadDivinerParams = IndexedDbPayloadDivinerParams,\n TIn extends PayloadDivinerQueryPayload = PayloadDivinerQueryPayload,\n TOut extends Payload = Payload,\n TEventData extends DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut> = DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut>,\n> extends PayloadDiviner<TParams, TIn, TOut, TEventData> {\n static override configSchemas = [IndexedDbPayloadDivinerConfigSchema]\n\n private _db: IDBPDatabase<PayloadStore> | undefined\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 private get db(): IDBPDatabase<PayloadStore> {\n return assertEx(this._db, 'DB not initialized')\n }\n\n protected override async divineHandler(payloads?: TIn[]): Promise<TOut[]> {\n const query = assertEx(payloads?.filter(isPayloadDivinerQueryPayload)?.pop(), 'Missing query payload')\n if (!query) return []\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { schemas, limit, offset, hash, order, schema: _schema, sources, ...props } = query as unknown as TIn & { sources?: string[] }\n const tx = this.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 assertEx((schemas?.length ?? 1) === 1, 'IndexedDbPayloadDiviner: Only one filter schema supported')\n const filterSchema = schemas?.[0]\n const filter = filterSchema ? { schema: filterSchema, ...props } : { ...props }\n const direction: IDBCursorDirection = order === 'desc' ? 'prev' : 'next'\n const suggestedIndex = this.selectBestIndex(filter, store)\n const keyRangeValue = this.getKeyRangeValue(suggestedIndex, filter)\n let cursor = suggestedIndex\n ? // Conditionally filter on schemas\n await store.index(suggestedIndex).openCursor(IDBKeyRange.only(keyRangeValue), direction)\n : // Just iterate all records\n await store.openCursor(suggestedIndex, direction)\n\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 // Collect results up to the limit\n while (cursor && results.length < parsedLimit) {\n results.push(cursor.value)\n cursor = await cursor.continue()\n }\n await tx.done\n // Remove any metadata before returning to the client\n return results.map((payload) => PayloadHasher.jsonPayload(payload))\n }\n\n protected override async startHandler() {\n await super.startHandler()\n // NOTE: We could defer this creation to first access but we\n // want to fail fast here in case something is wrong\n this._db = await openDB<PayloadStore>(this.dbName, this.dbVersion)\n return true\n }\n\n private getKeyRangeValue(indexName: string | null, query: AnyObject): unknown | unknown[] {\n if (!indexName) return []\n // Function to extract fields from an index name\n const extractFields = (indexName: string): string[] => {\n return indexName\n .slice(3)\n .split(IndexSeparator)\n .map((field) => field.toLowerCase())\n }\n\n // Extracting the relevant fields from the index name\n const indexFields = extractFields(indexName)\n\n // Collecting the values for these fields from the query object\n const keyRangeValue = indexFields.map((field) => query[field as keyof AnyObject])\n return keyRangeValue.length === 1 ? keyRangeValue[0] : keyRangeValue\n }\n\n private selectBestIndex(query: AnyObject, store: IDBPObjectStore<PayloadStore>): string | null {\n // List of available indexes\n const { indexNames } = store\n\n // Function to extract fields from an index name\n const extractFields = (indexName: string): string[] => {\n return indexName\n .slice(3)\n .split(IndexSeparator)\n .map((field) => field.toLowerCase())\n }\n\n // Convert query object keys to a set for easier comparison\n const queryKeys = new Set(Object.keys(query).map((key) => key.toLowerCase()))\n\n // Find the best matching index\n let bestMatch: { indexName: string; matchCount: number } = { indexName: '', matchCount: 0 }\n\n for (const indexName of indexNames) {\n const indexFields = extractFields(indexName)\n const matchCount = indexFields.filter((field) => queryKeys.has(field)).length\n if (matchCount > bestMatch.matchCount) {\n bestMatch = { indexName, matchCount }\n }\n }\n return bestMatch.matchCount > 0 ? bestMatch.indexName : null\n }\n}\n"],"mappings":"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","names":["PayloadDivinerSchema","IndexedDbPayloadDivinerSchema","IndexedDbPayloadDivinerConfigSchema","IndexedDbPayloadDivinerSchema","assertEx","IndexedDbArchivist","IndexSeparator","PayloadDiviner","isPayloadDivinerQueryPayload","PayloadHasher","openDB","IndexedDbPayloadDiviner","PayloadDiviner","_db","dbName","config","archivist","IndexedDbArchivist","defaultDbName","dbVersion","defaultDbVersion","storeName","defaultStoreName","db","assertEx","divineHandler","payloads","query","filter","isPayloadDivinerQueryPayload","pop","schemas","limit","offset","hash","order","schema","_schema","sources","props","tx","transaction","store","objectStore","results","parsedOffset","parsedLimit","length","filterSchema","direction","suggestedIndex","selectBestIndex","keyRangeValue","getKeyRangeValue","cursor","index","openCursor","IDBKeyRange","only","advance","push","value","continue","done","map","payload","PayloadHasher","jsonPayload","startHandler","openDB","indexName","extractFields","slice","split","IndexSeparator","field","toLowerCase","indexFields","indexNames","queryKeys","Set","Object","keys","key","bestMatch","matchCount","has","configSchemas","IndexedDbPayloadDivinerConfigSchema"]}
package/package.json CHANGED
@@ -11,23 +11,23 @@
11
11
  },
12
12
  "dependencies": {
13
13
  "@xylabs/assert": "^2.13.23",
14
- "@xyo-network/diviner-model": "~2.87.0-rc.2",
15
- "@xyo-network/diviner-payload-abstract": "~2.87.0-rc.2",
16
- "@xyo-network/diviner-payload-model": "~2.87.0-rc.2",
17
- "@xyo-network/hash": "~2.87.0-rc.2",
18
- "@xyo-network/payload-model": "~2.87.0-rc.2",
14
+ "@xyo-network/archivist-model": "~2.87.0-rc.3",
15
+ "@xyo-network/diviner-model": "~2.87.0-rc.3",
16
+ "@xyo-network/diviner-payload-abstract": "~2.87.0-rc.3",
17
+ "@xyo-network/diviner-payload-model": "~2.87.0-rc.3",
18
+ "@xyo-network/hash": "~2.87.0-rc.3",
19
+ "@xyo-network/payload-model": "~2.87.0-rc.3",
19
20
  "idb": "^8.0.0"
20
21
  },
21
22
  "devDependencies": {
22
23
  "@xylabs/ts-scripts-yarn3": "^3.2.33",
23
24
  "@xylabs/tsconfig": "^3.2.33",
24
- "@xyo-network/account": "~2.87.0-rc.2",
25
- "@xyo-network/archivist-indexeddb": "~2.87.0-rc.2",
26
- "@xyo-network/archivist-model": "~2.87.0-rc.2",
27
- "@xyo-network/module-model": "~2.87.0-rc.2",
28
- "@xyo-network/node-memory": "~2.87.0-rc.2",
29
- "@xyo-network/object": "~2.87.0-rc.2",
30
- "@xyo-network/payload-builder": "~2.87.0-rc.2",
25
+ "@xyo-network/account": "~2.87.0-rc.3",
26
+ "@xyo-network/archivist-indexeddb": "~2.87.0-rc.3",
27
+ "@xyo-network/module-model": "~2.87.0-rc.3",
28
+ "@xyo-network/node-memory": "~2.87.0-rc.3",
29
+ "@xyo-network/object": "~2.87.0-rc.3",
30
+ "@xyo-network/payload-builder": "~2.87.0-rc.3",
31
31
  "fake-indexeddb": "^5.0.2",
32
32
  "typescript": "^5.3.3"
33
33
  },
@@ -70,7 +70,7 @@
70
70
  "url": "https://github.com/XYOracleNetwork/sdk-xyo-client-js.git"
71
71
  },
72
72
  "sideEffects": false,
73
- "version": "2.87.0-rc.2",
73
+ "version": "2.87.0-rc.3",
74
74
  "type": "module",
75
75
  "stableVersion": "2.86.1"
76
76
  }
package/src/Diviner.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { assertEx } from '@xylabs/assert'
2
+ import { IndexedDbArchivist } from '@xyo-network/archivist-indexeddb'
2
3
  import { IndexSeparator } from '@xyo-network/archivist-model'
3
4
  import { DivinerModule, DivinerModuleEventData } from '@xyo-network/diviner-model'
4
5
  import { PayloadDiviner } from '@xyo-network/diviner-payload-abstract'
@@ -22,34 +23,25 @@ export class IndexedDbPayloadDiviner<
22
23
  TEventData extends DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut> = DivinerModuleEventData<DivinerModule<TParams>, TIn, TOut>,
23
24
  > extends PayloadDiviner<TParams, TIn, TOut, TEventData> {
24
25
  static override configSchemas = [IndexedDbPayloadDivinerConfigSchema]
25
- static defaultDbName = 'archivist'
26
- static defaultDbVersion = 1
27
- static defaultStoreName = 'payloads'
28
26
 
29
27
  private _db: IDBPDatabase<PayloadStore> | undefined
30
28
 
31
29
  /**
32
- * The database name. If not supplied via config it defaults to
33
- * `archivist`. This behavior biases towards a single, isolated
34
- * DB per archivist which seems to make the most sense for 99% of
35
- * use cases.
30
+ * The database name. If not supplied via config, it defaults
31
+ * to the archivist's name and if archivist's name is not supplied,
32
+ * it defaults to `archivist`. This behavior
33
+ * biases towards a single, isolated DB per archivist which seems to
34
+ * make the most sense for 99% of use cases.
36
35
  */
37
36
  get dbName() {
38
- return this.config?.dbName ?? IndexedDbPayloadDiviner.defaultDbName
37
+ return this.config?.dbName ?? this.config?.archivist ?? IndexedDbArchivist.defaultDbName
39
38
  }
40
39
 
41
40
  /**
42
- * The database version. If not supplied via config, it defaults to 1.
41
+ * The database version. If not supplied via config, it defaults to the archivist default version.
43
42
  */
44
43
  get dbVersion() {
45
- return this.config?.dbVersion ?? IndexedDbPayloadDiviner.defaultDbVersion
46
- }
47
-
48
- /**
49
- * The database indexes.
50
- */
51
- get indexes() {
52
- return this.config?.storage?.indexes ?? []
44
+ return this.config?.dbVersion ?? IndexedDbArchivist.defaultDbVersion
53
45
  }
54
46
 
55
47
  /**
@@ -57,7 +49,7 @@ export class IndexedDbPayloadDiviner<
57
49
  * to `payloads`.
58
50
  */
59
51
  get storeName() {
60
- return this.config?.storeName ?? IndexedDbPayloadDiviner.defaultStoreName
52
+ return this.config?.storeName ?? IndexedDbArchivist.defaultStoreName
61
53
  }
62
54
 
63
55
  private get db(): IDBPDatabase<PayloadStore> {
@@ -79,10 +71,10 @@ export class IndexedDbPayloadDiviner<
79
71
  const filter = filterSchema ? { schema: filterSchema, ...props } : { ...props }
80
72
  const direction: IDBCursorDirection = order === 'desc' ? 'prev' : 'next'
81
73
  const suggestedIndex = this.selectBestIndex(filter, store)
82
- const filterValues = this.getKeyValuesFromQuery(suggestedIndex, filter)
74
+ const keyRangeValue = this.getKeyRangeValue(suggestedIndex, filter)
83
75
  let cursor = suggestedIndex
84
76
  ? // Conditionally filter on schemas
85
- await store.index(suggestedIndex).openCursor(IDBKeyRange.only(filterValues.length === 1 ? filterValues[0] : filterValues), direction)
77
+ await store.index(suggestedIndex).openCursor(IDBKeyRange.only(keyRangeValue), direction)
86
78
  : // Just iterate all records
87
79
  await store.openCursor(suggestedIndex, direction)
88
80
 
@@ -109,7 +101,7 @@ export class IndexedDbPayloadDiviner<
109
101
  return true
110
102
  }
111
103
 
112
- private getKeyValuesFromQuery(indexName: string | null, query: AnyObject): unknown[] {
104
+ private getKeyRangeValue(indexName: string | null, query: AnyObject): unknown | unknown[] {
113
105
  if (!indexName) return []
114
106
  // Function to extract fields from an index name
115
107
  const extractFields = (indexName: string): string[] => {
@@ -123,7 +115,8 @@ export class IndexedDbPayloadDiviner<
123
115
  const indexFields = extractFields(indexName)
124
116
 
125
117
  // Collecting the values for these fields from the query object
126
- return indexFields.map((field) => query[field as keyof AnyObject])
118
+ const keyRangeValue = indexFields.map((field) => query[field as keyof AnyObject])
119
+ return keyRangeValue.length === 1 ? keyRangeValue[0] : keyRangeValue
127
120
  }
128
121
 
129
122
  private selectBestIndex(query: AnyObject, store: IDBPObjectStore<PayloadStore>): string | null {