@workglow/indexeddb 0.3.13 → 0.3.15
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.
- package/dist/job-queue/IndexedDbRateLimiterStorage.d.ts +1 -3
- package/dist/job-queue/IndexedDbRateLimiterStorage.d.ts.map +1 -1
- package/dist/job-queue/browser.js +1 -20
- package/dist/job-queue/browser.js.map +3 -3
- package/dist/job-queue/node.js +1 -20
- package/dist/job-queue/node.js.map +3 -3
- package/dist/storage/IndexedDbTabularStorage.d.ts +34 -1
- package/dist/storage/IndexedDbTabularStorage.d.ts.map +1 -1
- package/dist/storage/IndexedDbVectorStorage.d.ts +20 -2
- package/dist/storage/IndexedDbVectorStorage.d.ts.map +1 -1
- package/dist/storage/browser.js +135 -42
- package/dist/storage/browser.js.map +4 -4
- package/dist/storage/node.js +135 -42
- package/dist/storage/node.js.map +4 -4
- package/package.json +12 -8
package/dist/storage/node.js
CHANGED
|
@@ -323,7 +323,8 @@ import {
|
|
|
323
323
|
BaseTabularStorage,
|
|
324
324
|
HybridSubscriptionManager,
|
|
325
325
|
isSearchCondition,
|
|
326
|
-
pickCoveringIndex
|
|
326
|
+
pickCoveringIndex,
|
|
327
|
+
safeEmit
|
|
327
328
|
} from "@workglow/storage";
|
|
328
329
|
import { createServiceToken, deepEqual as deepEqual2, makeFingerprint, uuid4 } from "@workglow/util";
|
|
329
330
|
|
|
@@ -743,8 +744,8 @@ class IndexedDbTabularStorage extends BaseTabularStorage {
|
|
|
743
744
|
hybridManager = null;
|
|
744
745
|
hybridOptions;
|
|
745
746
|
cursorSafeIndexes;
|
|
746
|
-
constructor(table = "tabular_store", schema, primaryKeyNames, indexes = [], migrationOptions = {}, clientProvidedKeys = "if-missing", tabularMigrations) {
|
|
747
|
-
super(schema, primaryKeyNames, indexes, clientProvidedKeys, tabularMigrations, table);
|
|
747
|
+
constructor(table = "tabular_store", schema, primaryKeyNames, indexes = [], migrationOptions = {}, clientProvidedKeys = "if-missing", tabularMigrations, uniqueIndexes = []) {
|
|
748
|
+
super(schema, primaryKeyNames, indexes, clientProvidedKeys, tabularMigrations, table, uniqueIndexes);
|
|
748
749
|
this.table = table;
|
|
749
750
|
this.migrationOptions = migrationOptions;
|
|
750
751
|
this.hybridOptions = {
|
|
@@ -824,6 +825,15 @@ class IndexedDbTabularStorage extends BaseTabularStorage {
|
|
|
824
825
|
options: { unique: false }
|
|
825
826
|
});
|
|
826
827
|
}
|
|
828
|
+
for (const columns of this.uniqueIndexes) {
|
|
829
|
+
const columnNames = columns.map((col) => String(col));
|
|
830
|
+
const indexName = columnNames.join("_");
|
|
831
|
+
expectedIndexes.push({
|
|
832
|
+
name: indexName,
|
|
833
|
+
keyPath: columnNames.length === 1 ? columnNames[0] : columnNames,
|
|
834
|
+
options: { unique: true }
|
|
835
|
+
});
|
|
836
|
+
}
|
|
827
837
|
const primaryKey = pkColumns.length === 1 ? pkColumns[0] : pkColumns;
|
|
828
838
|
const useAutoIncrement = this.hasAutoGeneratedKey() && this.autoGeneratedKeyStrategy === "autoincrement" && pkColumns.length === 1;
|
|
829
839
|
return await ensureIndexedDbTable(this.table, primaryKey, expectedIndexes, this.migrationOptions, useAutoIncrement);
|
|
@@ -841,39 +851,43 @@ class IndexedDbTabularStorage extends BaseTabularStorage {
|
|
|
841
851
|
}
|
|
842
852
|
throw new Error(`IndexedDB autoincrement keys are generated by the database, not client-side. Column: ${columnName}`);
|
|
843
853
|
}
|
|
844
|
-
|
|
845
|
-
const db = await this.getDb();
|
|
854
|
+
prepareRecordForPut(record) {
|
|
846
855
|
let recordToStore = record;
|
|
847
|
-
if (this.hasAutoGeneratedKey()
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
}
|
|
859
|
-
shouldGenerate = false;
|
|
860
|
-
} else {
|
|
861
|
-
shouldGenerate = !hasClientValue;
|
|
862
|
-
}
|
|
863
|
-
if (shouldGenerate) {
|
|
864
|
-
const generatedValue = this.generateKeyValue(keyName, "uuid");
|
|
865
|
-
recordToStore = { ...record, [keyName]: generatedValue };
|
|
866
|
-
}
|
|
867
|
-
} else if (this.autoGeneratedKeyStrategy === "autoincrement") {
|
|
868
|
-
if (this.clientProvidedKeys === "always" && !hasClientValue) {
|
|
856
|
+
if (!this.hasAutoGeneratedKey() || !this.autoGeneratedKeyName)
|
|
857
|
+
return recordToStore;
|
|
858
|
+
const keyName = String(this.autoGeneratedKeyName);
|
|
859
|
+
const clientProvidedValue = record[keyName];
|
|
860
|
+
const hasClientValue = clientProvidedValue !== undefined && clientProvidedValue !== null;
|
|
861
|
+
if (this.autoGeneratedKeyStrategy === "uuid") {
|
|
862
|
+
let shouldGenerate = false;
|
|
863
|
+
if (this.clientProvidedKeys === "never") {
|
|
864
|
+
shouldGenerate = true;
|
|
865
|
+
} else if (this.clientProvidedKeys === "always") {
|
|
866
|
+
if (!hasClientValue) {
|
|
869
867
|
throw new Error(`Auto-generated key "${keyName}" is required when clientProvidedKeys is "always"`);
|
|
870
868
|
}
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
869
|
+
shouldGenerate = false;
|
|
870
|
+
} else {
|
|
871
|
+
shouldGenerate = !hasClientValue;
|
|
872
|
+
}
|
|
873
|
+
if (shouldGenerate) {
|
|
874
|
+
const generatedValue = this.generateKeyValue(keyName, "uuid");
|
|
875
|
+
recordToStore = { ...record, [keyName]: generatedValue };
|
|
876
|
+
}
|
|
877
|
+
} else if (this.autoGeneratedKeyStrategy === "autoincrement") {
|
|
878
|
+
if (this.clientProvidedKeys === "always" && !hasClientValue) {
|
|
879
|
+
throw new Error(`Auto-generated key "${keyName}" is required when clientProvidedKeys is "always"`);
|
|
880
|
+
}
|
|
881
|
+
if (this.clientProvidedKeys === "never") {
|
|
882
|
+
const { [keyName]: _, ...rest } = record;
|
|
883
|
+
recordToStore = rest;
|
|
875
884
|
}
|
|
876
885
|
}
|
|
886
|
+
return recordToStore;
|
|
887
|
+
}
|
|
888
|
+
async put(record) {
|
|
889
|
+
const db = await this.getDb();
|
|
890
|
+
let recordToStore = this.prepareRecordForPut(record);
|
|
877
891
|
return new Promise((resolve, reject) => {
|
|
878
892
|
const transaction = db.transaction(this.table, "readwrite");
|
|
879
893
|
const store = transaction.objectStore(this.table);
|
|
@@ -899,6 +913,78 @@ class IndexedDbTabularStorage extends BaseTabularStorage {
|
|
|
899
913
|
async putBulk(records) {
|
|
900
914
|
return await Promise.all(records.map((record) => this.put(record)));
|
|
901
915
|
}
|
|
916
|
+
async putBulkInTransaction(records) {
|
|
917
|
+
if (records.length === 0)
|
|
918
|
+
return [];
|
|
919
|
+
const prepared = records.map((r) => this.prepareRecordForPut(r));
|
|
920
|
+
const run = async () => {
|
|
921
|
+
const db = await this.getDb();
|
|
922
|
+
return new Promise((resolve, reject) => {
|
|
923
|
+
let tx;
|
|
924
|
+
try {
|
|
925
|
+
tx = db.transaction(this.table, "readwrite");
|
|
926
|
+
} catch (err) {
|
|
927
|
+
reject(err);
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
930
|
+
const store = tx.objectStore(this.table);
|
|
931
|
+
const results = new Array(prepared.length);
|
|
932
|
+
const useAutoincrement = this.hasAutoGeneratedKey() && this.autoGeneratedKeyName !== undefined && this.autoGeneratedKeyStrategy === "autoincrement";
|
|
933
|
+
const keyName = useAutoincrement ? String(this.autoGeneratedKeyName) : undefined;
|
|
934
|
+
tx.oncomplete = () => {
|
|
935
|
+
for (const r of results)
|
|
936
|
+
safeEmit(this.events, "put", r);
|
|
937
|
+
this.hybridManager?.notifyLocalChange();
|
|
938
|
+
resolve(results);
|
|
939
|
+
};
|
|
940
|
+
tx.onerror = () => reject(tx.error);
|
|
941
|
+
tx.onabort = () => reject(tx.error ?? new Error("putBulk transaction aborted"));
|
|
942
|
+
try {
|
|
943
|
+
for (let i = 0;i < prepared.length; i++) {
|
|
944
|
+
const recordToStore = prepared[i];
|
|
945
|
+
const request = store.put(recordToStore);
|
|
946
|
+
request.onsuccess = () => {
|
|
947
|
+
let finalRecord = recordToStore;
|
|
948
|
+
if (useAutoincrement && keyName !== undefined) {
|
|
949
|
+
if (finalRecord[keyName] === undefined) {
|
|
950
|
+
finalRecord = { ...finalRecord, [keyName]: request.result };
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
results[i] = finalRecord;
|
|
954
|
+
};
|
|
955
|
+
request.onerror = () => {};
|
|
956
|
+
}
|
|
957
|
+
} catch (err) {
|
|
958
|
+
try {
|
|
959
|
+
tx.abort();
|
|
960
|
+
} catch {}
|
|
961
|
+
reject(err);
|
|
962
|
+
}
|
|
963
|
+
});
|
|
964
|
+
};
|
|
965
|
+
try {
|
|
966
|
+
return await run();
|
|
967
|
+
} catch (err) {
|
|
968
|
+
if (err instanceof DOMException && err.name === "InvalidStateError") {
|
|
969
|
+
try {
|
|
970
|
+
this.db?.close();
|
|
971
|
+
} catch {}
|
|
972
|
+
this.db = undefined;
|
|
973
|
+
try {
|
|
974
|
+
return await run();
|
|
975
|
+
} catch (retryErr) {
|
|
976
|
+
safeEmit(this.events, "rollback", {
|
|
977
|
+
op: "putBulkInTransaction",
|
|
978
|
+
error: retryErr,
|
|
979
|
+
ids: []
|
|
980
|
+
});
|
|
981
|
+
throw retryErr;
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
safeEmit(this.events, "rollback", { op: "putBulkInTransaction", error: err, ids: [] });
|
|
985
|
+
throw err;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
902
988
|
getPrimaryKeyAsOrderedArray(key) {
|
|
903
989
|
return super.getPrimaryKeyAsOrderedArray(key).map((value) => typeof value === "bigint" ? value.toString() : value);
|
|
904
990
|
}
|
|
@@ -1013,7 +1099,7 @@ class IndexedDbTabularStorage extends BaseTabularStorage {
|
|
|
1013
1099
|
if (this.cursorSafeIndexes)
|
|
1014
1100
|
return this.cursorSafeIndexes;
|
|
1015
1101
|
const required = new Set(this.schema.required ?? []);
|
|
1016
|
-
this.cursorSafeIndexes = this.indexes.filter((columns) => columns.every((column) => required.has(String(column))));
|
|
1102
|
+
this.cursorSafeIndexes = [...this.indexes, ...this.uniqueIndexes].filter((columns) => columns.every((column) => required.has(String(column))));
|
|
1017
1103
|
return this.cursorSafeIndexes;
|
|
1018
1104
|
}
|
|
1019
1105
|
createIndexedRange(store, criteria) {
|
|
@@ -1506,18 +1592,16 @@ class IndexedDbKvStorage extends KvViaTabularStorage {
|
|
|
1506
1592
|
}
|
|
1507
1593
|
}
|
|
1508
1594
|
// src/storage/IndexedDbVectorStorage.ts
|
|
1509
|
-
import {
|
|
1595
|
+
import {
|
|
1596
|
+
assertVectorShape,
|
|
1597
|
+
getMetadataProperty,
|
|
1598
|
+
getVectorProperty,
|
|
1599
|
+
matchesFilter,
|
|
1600
|
+
validateVectorEntities
|
|
1601
|
+
} from "@workglow/storage";
|
|
1510
1602
|
import { createServiceToken as createServiceToken3 } from "@workglow/util";
|
|
1511
1603
|
import { cosineSimilarity } from "@workglow/util/schema";
|
|
1512
1604
|
var IDB_VECTOR_REPOSITORY = createServiceToken3("storage.vectorRepository.indexedDb");
|
|
1513
|
-
function matchesFilter(metadata, filter) {
|
|
1514
|
-
for (const [key, value] of Object.entries(filter)) {
|
|
1515
|
-
if (metadata[key] !== value) {
|
|
1516
|
-
return false;
|
|
1517
|
-
}
|
|
1518
|
-
}
|
|
1519
|
-
return true;
|
|
1520
|
-
}
|
|
1521
1605
|
|
|
1522
1606
|
class IndexedDbVectorStorage extends IndexedDbTabularStorage {
|
|
1523
1607
|
vectorDimensions;
|
|
@@ -1536,7 +1620,16 @@ class IndexedDbVectorStorage extends IndexedDbTabularStorage {
|
|
|
1536
1620
|
getVectorDimensions() {
|
|
1537
1621
|
return this.vectorDimensions;
|
|
1538
1622
|
}
|
|
1623
|
+
async put(record) {
|
|
1624
|
+
assertVectorShape(record[this.vectorPropertyName], this.vectorDimensions, "write");
|
|
1625
|
+
return super.put(record);
|
|
1626
|
+
}
|
|
1627
|
+
async putBulk(records) {
|
|
1628
|
+
validateVectorEntities(records, this.vectorPropertyName, this.vectorDimensions);
|
|
1629
|
+
return this.putBulkInTransaction(records);
|
|
1630
|
+
}
|
|
1539
1631
|
async similaritySearch(query, options = {}) {
|
|
1632
|
+
assertVectorShape(query, this.vectorDimensions, "query");
|
|
1540
1633
|
const { topK = 10, filter, scoreThreshold = 0 } = options;
|
|
1541
1634
|
const results = [];
|
|
1542
1635
|
const allEntities = await this.getAll() || [];
|
|
@@ -1577,4 +1670,4 @@ export {
|
|
|
1577
1670
|
IDB_KV_REPOSITORY
|
|
1578
1671
|
};
|
|
1579
1672
|
|
|
1580
|
-
//# debugId=
|
|
1673
|
+
//# debugId=43B855CF3D1C120364756E2164756E21
|