@workglow/storage 0.0.85 → 0.0.87
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/README.md +185 -53
- package/dist/browser.js +451 -188
- package/dist/browser.js.map +21 -18
- package/dist/bun.js +1069 -356
- package/dist/bun.js.map +29 -24
- package/dist/common-server.d.ts +17 -15
- package/dist/common-server.d.ts.map +1 -1
- package/dist/common.d.ts +13 -10
- package/dist/common.d.ts.map +1 -1
- package/dist/kv/{FsFolderJsonKvRepository.d.ts → FsFolderJsonKvStorage.d.ts} +8 -8
- package/dist/kv/FsFolderJsonKvStorage.d.ts.map +1 -0
- package/dist/kv/{FsFolderKvRepository.d.ts → FsFolderKvStorage.d.ts} +7 -7
- package/dist/kv/FsFolderKvStorage.d.ts.map +1 -0
- package/dist/kv/{IKvRepository.d.ts → IKvStorage.d.ts} +3 -3
- package/dist/kv/IKvStorage.d.ts.map +1 -0
- package/dist/kv/{InMemoryKvRepository.d.ts → InMemoryKvStorage.d.ts} +8 -8
- package/dist/kv/InMemoryKvStorage.d.ts.map +1 -0
- package/dist/kv/{IndexedDbKvRepository.d.ts → IndexedDbKvStorage.d.ts} +8 -8
- package/dist/kv/IndexedDbKvStorage.d.ts.map +1 -0
- package/dist/kv/{KvRepository.d.ts → KvStorage.d.ts} +7 -7
- package/dist/kv/KvStorage.d.ts.map +1 -0
- package/dist/kv/{KvViaTabularRepository.d.ts → KvViaTabularStorage.d.ts} +7 -7
- package/dist/kv/KvViaTabularStorage.d.ts.map +1 -0
- package/dist/kv/{PostgresKvRepository.d.ts → PostgresKvStorage.d.ts} +8 -8
- package/dist/kv/PostgresKvStorage.d.ts.map +1 -0
- package/dist/kv/{SqliteKvRepository.d.ts → SqliteKvStorage.d.ts} +8 -8
- package/dist/kv/SqliteKvStorage.d.ts.map +1 -0
- package/dist/kv/{SupabaseKvRepository.d.ts → SupabaseKvStorage.d.ts} +9 -9
- package/dist/kv/SupabaseKvStorage.d.ts.map +1 -0
- package/dist/node.js +1069 -356
- package/dist/node.js.map +29 -24
- package/dist/queue-limiter/IRateLimiterStorage.d.ts.map +1 -0
- package/dist/queue-limiter/InMemoryRateLimiterStorage.d.ts.map +1 -0
- package/dist/queue-limiter/IndexedDbRateLimiterStorage.d.ts.map +1 -0
- package/dist/queue-limiter/PostgresRateLimiterStorage.d.ts.map +1 -0
- package/dist/queue-limiter/SqliteRateLimiterStorage.d.ts.map +1 -0
- package/dist/queue-limiter/SupabaseRateLimiterStorage.d.ts.map +1 -0
- package/dist/tabular/{BaseSqlTabularRepository.d.ts → BaseSqlTabularStorage.d.ts} +8 -7
- package/dist/tabular/BaseSqlTabularStorage.d.ts.map +1 -0
- package/dist/tabular/{TabularRepository.d.ts → BaseTabularStorage.d.ts} +52 -10
- package/dist/tabular/BaseTabularStorage.d.ts.map +1 -0
- package/dist/tabular/{CachedTabularRepository.d.ts → CachedTabularStorage.d.ts} +15 -14
- package/dist/tabular/CachedTabularStorage.d.ts.map +1 -0
- package/dist/tabular/{FsFolderTabularRepository.d.ts → FsFolderTabularStorage.d.ts} +22 -12
- package/dist/tabular/FsFolderTabularStorage.d.ts.map +1 -0
- package/dist/tabular/{ITabularRepository.d.ts → ITabularStorage.d.ts} +29 -6
- package/dist/tabular/ITabularStorage.d.ts.map +1 -0
- package/dist/tabular/{InMemoryTabularRepository.d.ts → InMemoryTabularStorage.d.ts} +24 -14
- package/dist/tabular/InMemoryTabularStorage.d.ts.map +1 -0
- package/dist/tabular/{IndexedDbTabularRepository.d.ts → IndexedDbTabularStorage.d.ts} +20 -11
- package/dist/tabular/IndexedDbTabularStorage.d.ts.map +1 -0
- package/dist/tabular/{PostgresTabularRepository.d.ts → PostgresTabularStorage.d.ts} +37 -15
- package/dist/tabular/PostgresTabularStorage.d.ts.map +1 -0
- package/dist/tabular/{SharedInMemoryTabularRepository.d.ts → SharedInMemoryTabularStorage.d.ts} +14 -13
- package/dist/tabular/SharedInMemoryTabularStorage.d.ts.map +1 -0
- package/dist/tabular/{SqliteTabularRepository.d.ts → SqliteTabularStorage.d.ts} +25 -11
- package/dist/tabular/SqliteTabularStorage.d.ts.map +1 -0
- package/dist/tabular/{SupabaseTabularRepository.d.ts → SupabaseTabularStorage.d.ts} +17 -15
- package/dist/tabular/SupabaseTabularStorage.d.ts.map +1 -0
- package/dist/tabular/TabularStorageRegistry.d.ts +29 -0
- package/dist/tabular/TabularStorageRegistry.d.ts.map +1 -0
- package/dist/util/IndexedDbTable.d.ts +1 -1
- package/dist/util/IndexedDbTable.d.ts.map +1 -1
- package/dist/vector/IVectorStorage.d.ts +83 -0
- package/dist/vector/IVectorStorage.d.ts.map +1 -0
- package/dist/vector/InMemoryVectorStorage.d.ts +41 -0
- package/dist/vector/InMemoryVectorStorage.d.ts.map +1 -0
- package/dist/vector/PostgresVectorStorage.d.ts +57 -0
- package/dist/vector/PostgresVectorStorage.d.ts.map +1 -0
- package/dist/vector/SqliteVectorStorage.d.ts +45 -0
- package/dist/vector/SqliteVectorStorage.d.ts.map +1 -0
- package/package.json +7 -7
- package/src/kv/README.md +3 -3
- package/src/tabular/README.md +186 -23
- package/src/vector/README.md +393 -0
- package/dist/kv/FsFolderJsonKvRepository.d.ts.map +0 -1
- package/dist/kv/FsFolderKvRepository.d.ts.map +0 -1
- package/dist/kv/IKvRepository.d.ts.map +0 -1
- package/dist/kv/InMemoryKvRepository.d.ts.map +0 -1
- package/dist/kv/IndexedDbKvRepository.d.ts.map +0 -1
- package/dist/kv/KvRepository.d.ts.map +0 -1
- package/dist/kv/KvViaTabularRepository.d.ts.map +0 -1
- package/dist/kv/PostgresKvRepository.d.ts.map +0 -1
- package/dist/kv/SqliteKvRepository.d.ts.map +0 -1
- package/dist/kv/SupabaseKvRepository.d.ts.map +0 -1
- package/dist/limiter/IRateLimiterStorage.d.ts.map +0 -1
- package/dist/limiter/InMemoryRateLimiterStorage.d.ts.map +0 -1
- package/dist/limiter/IndexedDbRateLimiterStorage.d.ts.map +0 -1
- package/dist/limiter/PostgresRateLimiterStorage.d.ts.map +0 -1
- package/dist/limiter/SqliteRateLimiterStorage.d.ts.map +0 -1
- package/dist/limiter/SupabaseRateLimiterStorage.d.ts.map +0 -1
- package/dist/tabular/BaseSqlTabularRepository.d.ts.map +0 -1
- package/dist/tabular/CachedTabularRepository.d.ts.map +0 -1
- package/dist/tabular/FsFolderTabularRepository.d.ts.map +0 -1
- package/dist/tabular/ITabularRepository.d.ts.map +0 -1
- package/dist/tabular/InMemoryTabularRepository.d.ts.map +0 -1
- package/dist/tabular/IndexedDbTabularRepository.d.ts.map +0 -1
- package/dist/tabular/PostgresTabularRepository.d.ts.map +0 -1
- package/dist/tabular/SharedInMemoryTabularRepository.d.ts.map +0 -1
- package/dist/tabular/SqliteTabularRepository.d.ts.map +0 -1
- package/dist/tabular/SupabaseTabularRepository.d.ts.map +0 -1
- package/dist/tabular/TabularRepository.d.ts.map +0 -1
- /package/dist/{limiter → queue-limiter}/IRateLimiterStorage.d.ts +0 -0
- /package/dist/{limiter → queue-limiter}/InMemoryRateLimiterStorage.d.ts +0 -0
- /package/dist/{limiter → queue-limiter}/IndexedDbRateLimiterStorage.d.ts +0 -0
- /package/dist/{limiter → queue-limiter}/PostgresRateLimiterStorage.d.ts +0 -0
- /package/dist/{limiter → queue-limiter}/SqliteRateLimiterStorage.d.ts +0 -0
- /package/dist/{limiter → queue-limiter}/SupabaseRateLimiterStorage.d.ts +0 -0
package/dist/node.js
CHANGED
|
@@ -1,18 +1,4 @@
|
|
|
1
|
-
// src/tabular/
|
|
2
|
-
import { createServiceToken as createServiceToken3 } from "@workglow/util";
|
|
3
|
-
|
|
4
|
-
// src/tabular/InMemoryTabularRepository.ts
|
|
5
|
-
import {
|
|
6
|
-
createServiceToken as createServiceToken2,
|
|
7
|
-
makeFingerprint as makeFingerprint2
|
|
8
|
-
} from "@workglow/util";
|
|
9
|
-
|
|
10
|
-
// src/tabular/ITabularRepository.ts
|
|
11
|
-
function isSearchCondition(value) {
|
|
12
|
-
return typeof value === "object" && value !== null && "value" in value && "operator" in value && typeof value.operator === "string";
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// src/tabular/TabularRepository.ts
|
|
1
|
+
// src/tabular/BaseTabularStorage.ts
|
|
16
2
|
import {
|
|
17
3
|
createServiceToken,
|
|
18
4
|
EventEmitter,
|
|
@@ -20,16 +6,20 @@ import {
|
|
|
20
6
|
} from "@workglow/util";
|
|
21
7
|
var TABULAR_REPOSITORY = createServiceToken("storage.tabularRepository");
|
|
22
8
|
|
|
23
|
-
class
|
|
9
|
+
class BaseTabularStorage {
|
|
24
10
|
schema;
|
|
25
11
|
primaryKeyNames;
|
|
26
12
|
events = new EventEmitter;
|
|
27
13
|
indexes;
|
|
28
14
|
primaryKeySchema;
|
|
29
15
|
valueSchema;
|
|
30
|
-
|
|
16
|
+
autoGeneratedKeyName = null;
|
|
17
|
+
autoGeneratedKeyStrategy = null;
|
|
18
|
+
clientProvidedKeys;
|
|
19
|
+
constructor(schema, primaryKeyNames, indexes = [], clientProvidedKeys = "if-missing") {
|
|
31
20
|
this.schema = schema;
|
|
32
21
|
this.primaryKeyNames = primaryKeyNames;
|
|
22
|
+
this.clientProvidedKeys = clientProvidedKeys;
|
|
33
23
|
const primaryKeyProps = {};
|
|
34
24
|
const valueProps = {};
|
|
35
25
|
const primaryKeySet = new Set(primaryKeyNames);
|
|
@@ -72,6 +62,28 @@ class TabularRepository {
|
|
|
72
62
|
}
|
|
73
63
|
}
|
|
74
64
|
}
|
|
65
|
+
const autoGeneratedKeys = [];
|
|
66
|
+
for (const key of primaryKeyNames) {
|
|
67
|
+
const keyStr = String(key);
|
|
68
|
+
const propDef = schema.properties[keyStr];
|
|
69
|
+
if (propDef && typeof propDef === "object" && "x-auto-generated" in propDef) {
|
|
70
|
+
if (propDef["x-auto-generated"] === true) {
|
|
71
|
+
autoGeneratedKeys.push(keyStr);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (autoGeneratedKeys.length > 1) {
|
|
76
|
+
throw new Error(`Multiple auto-generated keys detected: ${autoGeneratedKeys.join(", ")}. ` + `Only the first primary key column can be auto-generated.`);
|
|
77
|
+
}
|
|
78
|
+
if (autoGeneratedKeys.length > 0) {
|
|
79
|
+
const autoGenKeyName = autoGeneratedKeys[0];
|
|
80
|
+
const firstPrimaryKey = String(primaryKeyNames[0]);
|
|
81
|
+
if (autoGenKeyName !== firstPrimaryKey) {
|
|
82
|
+
throw new Error(`Auto-generated key "${autoGenKeyName}" must be the first primary key column. ` + `Current first primary key is "${firstPrimaryKey}".`);
|
|
83
|
+
}
|
|
84
|
+
this.autoGeneratedKeyName = autoGenKeyName;
|
|
85
|
+
this.autoGeneratedKeyStrategy = this.determineGenerationStrategy(autoGenKeyName, schema.properties[autoGenKeyName]);
|
|
86
|
+
}
|
|
75
87
|
}
|
|
76
88
|
filterCompoundKeys(primaryKey, potentialKeys) {
|
|
77
89
|
const isPrefix = (prefix, arr) => {
|
|
@@ -193,6 +205,32 @@ class TabularRepository {
|
|
|
193
205
|
}
|
|
194
206
|
return bestMatch;
|
|
195
207
|
}
|
|
208
|
+
hasAutoGeneratedKey() {
|
|
209
|
+
return this.autoGeneratedKeyName !== null;
|
|
210
|
+
}
|
|
211
|
+
isAutoGeneratedKey(name) {
|
|
212
|
+
return this.autoGeneratedKeyName !== null && String(this.autoGeneratedKeyName) === name;
|
|
213
|
+
}
|
|
214
|
+
determineGenerationStrategy(columnName, typeDef) {
|
|
215
|
+
let actualType = typeDef;
|
|
216
|
+
if (typeDef && typeof typeDef === "object") {
|
|
217
|
+
if (typeDef.anyOf && Array.isArray(typeDef.anyOf)) {
|
|
218
|
+
actualType = typeDef.anyOf.find((t) => t.type !== "null") || typeDef;
|
|
219
|
+
} else if (typeDef.oneOf && Array.isArray(typeDef.oneOf)) {
|
|
220
|
+
actualType = typeDef.oneOf.find((t) => t.type !== "null") || typeDef;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (typeof actualType !== "object") {
|
|
224
|
+
return "uuid";
|
|
225
|
+
}
|
|
226
|
+
if (actualType.type === "integer") {
|
|
227
|
+
return "autoincrement";
|
|
228
|
+
}
|
|
229
|
+
return "uuid";
|
|
230
|
+
}
|
|
231
|
+
generateKeyValue(columnName, strategy) {
|
|
232
|
+
throw new Error(`generateKeyValue not implemented for ${this.constructor.name}. ` + `Column: ${columnName}, Strategy: ${strategy}`);
|
|
233
|
+
}
|
|
196
234
|
async setupDatabase() {}
|
|
197
235
|
destroy() {}
|
|
198
236
|
async[Symbol.asyncDispose]() {
|
|
@@ -202,22 +240,67 @@ class TabularRepository {
|
|
|
202
240
|
this.destroy();
|
|
203
241
|
}
|
|
204
242
|
}
|
|
243
|
+
// src/tabular/CachedTabularStorage.ts
|
|
244
|
+
import {
|
|
245
|
+
createServiceToken as createServiceToken3
|
|
246
|
+
} from "@workglow/util";
|
|
205
247
|
|
|
206
|
-
// src/tabular/
|
|
248
|
+
// src/tabular/InMemoryTabularStorage.ts
|
|
249
|
+
import {
|
|
250
|
+
createServiceToken as createServiceToken2,
|
|
251
|
+
makeFingerprint as makeFingerprint2,
|
|
252
|
+
uuid4
|
|
253
|
+
} from "@workglow/util";
|
|
254
|
+
|
|
255
|
+
// src/tabular/ITabularStorage.ts
|
|
256
|
+
function isSearchCondition(value) {
|
|
257
|
+
return typeof value === "object" && value !== null && "value" in value && "operator" in value && typeof value.operator === "string";
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// src/tabular/InMemoryTabularStorage.ts
|
|
207
261
|
var MEMORY_TABULAR_REPOSITORY = createServiceToken2("storage.tabularRepository.inMemory");
|
|
208
262
|
|
|
209
|
-
class
|
|
263
|
+
class InMemoryTabularStorage extends BaseTabularStorage {
|
|
210
264
|
values = new Map;
|
|
211
|
-
|
|
212
|
-
|
|
265
|
+
autoIncrementCounter = 0;
|
|
266
|
+
constructor(schema, primaryKeyNames, indexes = [], clientProvidedKeys = "if-missing") {
|
|
267
|
+
super(schema, primaryKeyNames, indexes, clientProvidedKeys);
|
|
213
268
|
}
|
|
214
269
|
async setupDatabase() {}
|
|
270
|
+
generateKeyValue(columnName, strategy) {
|
|
271
|
+
if (strategy === "autoincrement") {
|
|
272
|
+
return ++this.autoIncrementCounter;
|
|
273
|
+
} else {
|
|
274
|
+
return uuid4();
|
|
275
|
+
}
|
|
276
|
+
}
|
|
215
277
|
async put(value) {
|
|
216
|
-
|
|
278
|
+
let entityToStore = value;
|
|
279
|
+
if (this.hasAutoGeneratedKey() && this.autoGeneratedKeyName) {
|
|
280
|
+
const keyName = this.autoGeneratedKeyName;
|
|
281
|
+
const clientProvidedValue = value[keyName];
|
|
282
|
+
const hasClientValue = clientProvidedValue !== undefined && clientProvidedValue !== null;
|
|
283
|
+
let shouldGenerate = false;
|
|
284
|
+
if (this.clientProvidedKeys === "never") {
|
|
285
|
+
shouldGenerate = true;
|
|
286
|
+
} else if (this.clientProvidedKeys === "always") {
|
|
287
|
+
if (!hasClientValue) {
|
|
288
|
+
throw new Error(`Auto-generated key "${keyName}" is required when clientProvidedKeys is "always"`);
|
|
289
|
+
}
|
|
290
|
+
shouldGenerate = false;
|
|
291
|
+
} else {
|
|
292
|
+
shouldGenerate = !hasClientValue;
|
|
293
|
+
}
|
|
294
|
+
if (shouldGenerate) {
|
|
295
|
+
const generatedValue = this.generateKeyValue(keyName, this.autoGeneratedKeyStrategy);
|
|
296
|
+
entityToStore = { ...value, [keyName]: generatedValue };
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
const { key } = this.separateKeyValueFromCombined(entityToStore);
|
|
217
300
|
const id = await makeFingerprint2(key);
|
|
218
|
-
this.values.set(id,
|
|
219
|
-
this.events.emit("put",
|
|
220
|
-
return
|
|
301
|
+
this.values.set(id, entityToStore);
|
|
302
|
+
this.events.emit("put", entityToStore);
|
|
303
|
+
return entityToStore;
|
|
221
304
|
}
|
|
222
305
|
async putBulk(values) {
|
|
223
306
|
return await Promise.all(values.map(async (value) => this.put(value)));
|
|
@@ -338,23 +421,23 @@ class InMemoryTabularRepository extends TabularRepository {
|
|
|
338
421
|
}
|
|
339
422
|
}
|
|
340
423
|
|
|
341
|
-
// src/tabular/
|
|
424
|
+
// src/tabular/CachedTabularStorage.ts
|
|
342
425
|
var CACHED_TABULAR_REPOSITORY = createServiceToken3("storage.tabularRepository.cached");
|
|
343
426
|
|
|
344
|
-
class
|
|
427
|
+
class CachedTabularStorage extends BaseTabularStorage {
|
|
345
428
|
cache;
|
|
346
429
|
durable;
|
|
347
430
|
cacheInitialized = false;
|
|
348
|
-
constructor(durable, cache, schema, primaryKeyNames, indexes) {
|
|
431
|
+
constructor(durable, cache, schema, primaryKeyNames, indexes, clientProvidedKeys = "if-missing") {
|
|
349
432
|
if (!schema || !primaryKeyNames) {
|
|
350
|
-
throw new Error("Schema and primaryKeyNames must be provided when creating
|
|
433
|
+
throw new Error("Schema and primaryKeyNames must be provided when creating CachedTabularStorage");
|
|
351
434
|
}
|
|
352
|
-
super(schema, primaryKeyNames, indexes || []);
|
|
435
|
+
super(schema, primaryKeyNames, indexes || [], clientProvidedKeys);
|
|
353
436
|
this.durable = durable;
|
|
354
437
|
if (cache) {
|
|
355
438
|
this.cache = cache;
|
|
356
439
|
} else {
|
|
357
|
-
this.cache = new
|
|
440
|
+
this.cache = new InMemoryTabularStorage(schema, primaryKeyNames, indexes || [], clientProvidedKeys);
|
|
358
441
|
}
|
|
359
442
|
this.setupEventForwarding();
|
|
360
443
|
}
|
|
@@ -481,7 +564,36 @@ class CachedTabularRepository extends TabularRepository {
|
|
|
481
564
|
this.cache.destroy();
|
|
482
565
|
}
|
|
483
566
|
}
|
|
484
|
-
// src/
|
|
567
|
+
// src/tabular/TabularStorageRegistry.ts
|
|
568
|
+
import {
|
|
569
|
+
createServiceToken as createServiceToken4,
|
|
570
|
+
globalServiceRegistry,
|
|
571
|
+
registerInputResolver
|
|
572
|
+
} from "@workglow/util";
|
|
573
|
+
var TABULAR_REPOSITORIES = createServiceToken4("storage.tabular.repositories");
|
|
574
|
+
if (!globalServiceRegistry.has(TABULAR_REPOSITORIES)) {
|
|
575
|
+
globalServiceRegistry.register(TABULAR_REPOSITORIES, () => new Map, true);
|
|
576
|
+
}
|
|
577
|
+
function getGlobalTabularRepositories() {
|
|
578
|
+
return globalServiceRegistry.get(TABULAR_REPOSITORIES);
|
|
579
|
+
}
|
|
580
|
+
function registerTabularRepository(id, repository) {
|
|
581
|
+
const repos = getGlobalTabularRepositories();
|
|
582
|
+
repos.set(id, repository);
|
|
583
|
+
}
|
|
584
|
+
function getTabularRepository(id) {
|
|
585
|
+
return getGlobalTabularRepositories().get(id);
|
|
586
|
+
}
|
|
587
|
+
function resolveRepositoryFromRegistry(id, format, registry) {
|
|
588
|
+
const repos = registry.has(TABULAR_REPOSITORIES) ? registry.get(TABULAR_REPOSITORIES) : getGlobalTabularRepositories();
|
|
589
|
+
const repo = repos.get(id);
|
|
590
|
+
if (!repo) {
|
|
591
|
+
throw new Error(`Tabular storage "${id}" not found in registry`);
|
|
592
|
+
}
|
|
593
|
+
return repo;
|
|
594
|
+
}
|
|
595
|
+
registerInputResolver("storage:tabular", resolveRepositoryFromRegistry);
|
|
596
|
+
// src/kv/IKvStorage.ts
|
|
485
597
|
var DefaultKeyValueSchema = {
|
|
486
598
|
type: "object",
|
|
487
599
|
properties: {
|
|
@@ -491,14 +603,14 @@ var DefaultKeyValueSchema = {
|
|
|
491
603
|
additionalProperties: false
|
|
492
604
|
};
|
|
493
605
|
var DefaultKeyValueKey = ["key"];
|
|
494
|
-
// src/kv/
|
|
495
|
-
import { createServiceToken as
|
|
606
|
+
// src/kv/InMemoryKvStorage.ts
|
|
607
|
+
import { createServiceToken as createServiceToken6 } from "@workglow/util";
|
|
496
608
|
|
|
497
|
-
// src/kv/
|
|
498
|
-
import { createServiceToken as
|
|
499
|
-
var KV_REPOSITORY =
|
|
609
|
+
// src/kv/KvStorage.ts
|
|
610
|
+
import { createServiceToken as createServiceToken5, EventEmitter as EventEmitter2, makeFingerprint as makeFingerprint3 } from "@workglow/util";
|
|
611
|
+
var KV_REPOSITORY = createServiceToken5("storage.kvRepository");
|
|
500
612
|
|
|
501
|
-
class
|
|
613
|
+
class KvStorage {
|
|
502
614
|
keySchema;
|
|
503
615
|
valueSchema;
|
|
504
616
|
events = new EventEmitter2;
|
|
@@ -526,8 +638,8 @@ class KvRepository {
|
|
|
526
638
|
}
|
|
527
639
|
}
|
|
528
640
|
|
|
529
|
-
// src/kv/
|
|
530
|
-
class
|
|
641
|
+
// src/kv/KvViaTabularStorage.ts
|
|
642
|
+
class KvViaTabularStorage extends KvStorage {
|
|
531
643
|
async setupDatabase() {
|
|
532
644
|
await this.tabularRepository.setupDatabase?.();
|
|
533
645
|
}
|
|
@@ -602,22 +714,22 @@ class KvViaTabularRepository extends KvRepository {
|
|
|
602
714
|
}
|
|
603
715
|
}
|
|
604
716
|
|
|
605
|
-
// src/kv/
|
|
606
|
-
var MEMORY_KV_REPOSITORY =
|
|
717
|
+
// src/kv/InMemoryKvStorage.ts
|
|
718
|
+
var MEMORY_KV_REPOSITORY = createServiceToken6("storage.kvRepository.inMemory");
|
|
607
719
|
|
|
608
|
-
class
|
|
720
|
+
class InMemoryKvStorage extends KvViaTabularStorage {
|
|
609
721
|
tabularRepository;
|
|
610
722
|
constructor(keySchema = { type: "string" }, valueSchema = {}) {
|
|
611
723
|
super(keySchema, valueSchema);
|
|
612
|
-
this.tabularRepository = new
|
|
724
|
+
this.tabularRepository = new InMemoryTabularStorage(DefaultKeyValueSchema, DefaultKeyValueKey);
|
|
613
725
|
}
|
|
614
726
|
}
|
|
615
727
|
// src/queue/InMemoryQueueStorage.ts
|
|
616
|
-
import { createServiceToken as
|
|
728
|
+
import { createServiceToken as createServiceToken8, EventEmitter as EventEmitter3, makeFingerprint as makeFingerprint4, sleep, uuid4 as uuid42 } from "@workglow/util";
|
|
617
729
|
|
|
618
730
|
// src/queue/IQueueStorage.ts
|
|
619
|
-
import { createServiceToken as
|
|
620
|
-
var QUEUE_STORAGE =
|
|
731
|
+
import { createServiceToken as createServiceToken7 } from "@workglow/util";
|
|
732
|
+
var QUEUE_STORAGE = createServiceToken7("jobqueue.storage");
|
|
621
733
|
var JobStatus = {
|
|
622
734
|
PENDING: "PENDING",
|
|
623
735
|
PROCESSING: "PROCESSING",
|
|
@@ -628,7 +740,7 @@ var JobStatus = {
|
|
|
628
740
|
};
|
|
629
741
|
|
|
630
742
|
// src/queue/InMemoryQueueStorage.ts
|
|
631
|
-
var IN_MEMORY_QUEUE_STORAGE =
|
|
743
|
+
var IN_MEMORY_QUEUE_STORAGE = createServiceToken8("jobqueue.storage.inMemory");
|
|
632
744
|
|
|
633
745
|
class InMemoryQueueStorage {
|
|
634
746
|
queueName;
|
|
@@ -656,8 +768,8 @@ class InMemoryQueueStorage {
|
|
|
656
768
|
await sleep(0);
|
|
657
769
|
const now = new Date().toISOString();
|
|
658
770
|
const jobWithPrefixes = job;
|
|
659
|
-
jobWithPrefixes.id = jobWithPrefixes.id ??
|
|
660
|
-
jobWithPrefixes.job_run_id = jobWithPrefixes.job_run_id ??
|
|
771
|
+
jobWithPrefixes.id = jobWithPrefixes.id ?? uuid42();
|
|
772
|
+
jobWithPrefixes.job_run_id = jobWithPrefixes.job_run_id ?? uuid42();
|
|
661
773
|
jobWithPrefixes.queue = this.queueName;
|
|
662
774
|
jobWithPrefixes.fingerprint = await makeFingerprint4(jobWithPrefixes.input);
|
|
663
775
|
jobWithPrefixes.status = JobStatus.PENDING;
|
|
@@ -804,9 +916,9 @@ class InMemoryQueueStorage {
|
|
|
804
916
|
return this.events.subscribe("change", filteredCallback);
|
|
805
917
|
}
|
|
806
918
|
}
|
|
807
|
-
// src/limiter/InMemoryRateLimiterStorage.ts
|
|
808
|
-
import { createServiceToken as
|
|
809
|
-
var IN_MEMORY_RATE_LIMITER_STORAGE =
|
|
919
|
+
// src/queue-limiter/InMemoryRateLimiterStorage.ts
|
|
920
|
+
import { createServiceToken as createServiceToken9, sleep as sleep2 } from "@workglow/util";
|
|
921
|
+
var IN_MEMORY_RATE_LIMITER_STORAGE = createServiceToken9("ratelimiter.storage.inMemory");
|
|
810
922
|
|
|
811
923
|
class InMemoryRateLimiterStorage {
|
|
812
924
|
prefixValues;
|
|
@@ -863,9 +975,9 @@ class InMemoryRateLimiterStorage {
|
|
|
863
975
|
this.nextAvailableTimes.delete(key);
|
|
864
976
|
}
|
|
865
977
|
}
|
|
866
|
-
// src/limiter/IRateLimiterStorage.ts
|
|
867
|
-
import { createServiceToken as
|
|
868
|
-
var RATE_LIMITER_STORAGE =
|
|
978
|
+
// src/queue-limiter/IRateLimiterStorage.ts
|
|
979
|
+
import { createServiceToken as createServiceToken10 } from "@workglow/util";
|
|
980
|
+
var RATE_LIMITER_STORAGE = createServiceToken10("ratelimiter.storage");
|
|
869
981
|
// src/util/HybridSubscriptionManager.ts
|
|
870
982
|
class HybridSubscriptionManager {
|
|
871
983
|
subscribers = new Set;
|
|
@@ -1139,21 +1251,141 @@ class PollingSubscriptionManager {
|
|
|
1139
1251
|
this.initialized = false;
|
|
1140
1252
|
}
|
|
1141
1253
|
}
|
|
1142
|
-
// src/
|
|
1254
|
+
// src/vector/InMemoryVectorStorage.ts
|
|
1255
|
+
import { cosineSimilarity } from "@workglow/util";
|
|
1256
|
+
|
|
1257
|
+
// src/vector/IVectorStorage.ts
|
|
1258
|
+
function getVectorProperty(schema) {
|
|
1259
|
+
for (const [key, value] of Object.entries(schema.properties)) {
|
|
1260
|
+
if (typeof value !== "boolean" && value.type === "array" && (value.format === "TypedArray" || value.format?.startsWith("TypedArray:"))) {
|
|
1261
|
+
return key;
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
return;
|
|
1265
|
+
}
|
|
1266
|
+
function getMetadataProperty(schema) {
|
|
1267
|
+
for (const [key, value] of Object.entries(schema.properties)) {
|
|
1268
|
+
if (typeof value !== "boolean" && value.type === "object" && value.format === "metadata") {
|
|
1269
|
+
return key;
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
return;
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
// src/vector/InMemoryVectorStorage.ts
|
|
1276
|
+
function matchesFilter(metadata, filter) {
|
|
1277
|
+
for (const [key, value] of Object.entries(filter)) {
|
|
1278
|
+
if (metadata[key] !== value) {
|
|
1279
|
+
return false;
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
return true;
|
|
1283
|
+
}
|
|
1284
|
+
function textRelevance(text, query) {
|
|
1285
|
+
const textLower = text.toLowerCase();
|
|
1286
|
+
const queryLower = query.toLowerCase();
|
|
1287
|
+
const queryWords = queryLower.split(/\s+/).filter((w) => w.length > 0);
|
|
1288
|
+
if (queryWords.length === 0) {
|
|
1289
|
+
return 0;
|
|
1290
|
+
}
|
|
1291
|
+
let matches = 0;
|
|
1292
|
+
for (const word of queryWords) {
|
|
1293
|
+
if (textLower.includes(word)) {
|
|
1294
|
+
matches++;
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
return matches / queryWords.length;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
class InMemoryVectorStorage extends InMemoryTabularStorage {
|
|
1301
|
+
vectorDimensions;
|
|
1302
|
+
VectorType;
|
|
1303
|
+
vectorPropertyName;
|
|
1304
|
+
metadataPropertyName;
|
|
1305
|
+
constructor(schema, primaryKeyNames, indexes = [], dimensions, VectorType = Float32Array) {
|
|
1306
|
+
super(schema, primaryKeyNames, indexes);
|
|
1307
|
+
this.vectorDimensions = dimensions;
|
|
1308
|
+
this.VectorType = VectorType;
|
|
1309
|
+
const vectorProp = getVectorProperty(schema);
|
|
1310
|
+
if (!vectorProp) {
|
|
1311
|
+
throw new Error("Schema must have a property with type array and format TypedArray");
|
|
1312
|
+
}
|
|
1313
|
+
this.vectorPropertyName = vectorProp;
|
|
1314
|
+
this.metadataPropertyName = getMetadataProperty(schema);
|
|
1315
|
+
}
|
|
1316
|
+
getVectorDimensions() {
|
|
1317
|
+
return this.vectorDimensions;
|
|
1318
|
+
}
|
|
1319
|
+
async similaritySearch(query, options = {}) {
|
|
1320
|
+
const { topK = 10, filter, scoreThreshold = 0 } = options;
|
|
1321
|
+
const results = [];
|
|
1322
|
+
const allEntities = await this.getAll() || [];
|
|
1323
|
+
for (const entity of allEntities) {
|
|
1324
|
+
const vector = entity[this.vectorPropertyName];
|
|
1325
|
+
const metadata = this.metadataPropertyName ? entity[this.metadataPropertyName] : {};
|
|
1326
|
+
if (filter && !matchesFilter(metadata, filter)) {
|
|
1327
|
+
continue;
|
|
1328
|
+
}
|
|
1329
|
+
const score = cosineSimilarity(query, vector);
|
|
1330
|
+
if (score < scoreThreshold) {
|
|
1331
|
+
continue;
|
|
1332
|
+
}
|
|
1333
|
+
results.push({
|
|
1334
|
+
...entity,
|
|
1335
|
+
score
|
|
1336
|
+
});
|
|
1337
|
+
}
|
|
1338
|
+
results.sort((a, b) => b.score - a.score);
|
|
1339
|
+
const topResults = results.slice(0, topK);
|
|
1340
|
+
return topResults;
|
|
1341
|
+
}
|
|
1342
|
+
async hybridSearch(query, options) {
|
|
1343
|
+
const { topK = 10, filter, scoreThreshold = 0, textQuery, vectorWeight = 0.7 } = options;
|
|
1344
|
+
if (!textQuery || textQuery.trim().length === 0) {
|
|
1345
|
+
return this.similaritySearch(query, { topK, filter, scoreThreshold });
|
|
1346
|
+
}
|
|
1347
|
+
const results = [];
|
|
1348
|
+
const allEntities = await this.getAll() || [];
|
|
1349
|
+
for (const entity of allEntities) {
|
|
1350
|
+
const vector = entity[this.vectorPropertyName];
|
|
1351
|
+
const metadata = this.metadataPropertyName ? entity[this.metadataPropertyName] : {};
|
|
1352
|
+
if (filter && !matchesFilter(metadata, filter)) {
|
|
1353
|
+
continue;
|
|
1354
|
+
}
|
|
1355
|
+
const vectorScore = cosineSimilarity(query, vector);
|
|
1356
|
+
const metadataText = Object.values(metadata).join(" ").toLowerCase();
|
|
1357
|
+
const textScore = textRelevance(metadataText, textQuery);
|
|
1358
|
+
const combinedScore = vectorWeight * vectorScore + (1 - vectorWeight) * textScore;
|
|
1359
|
+
if (combinedScore < scoreThreshold) {
|
|
1360
|
+
continue;
|
|
1361
|
+
}
|
|
1362
|
+
results.push({
|
|
1363
|
+
...entity,
|
|
1364
|
+
score: combinedScore
|
|
1365
|
+
});
|
|
1366
|
+
}
|
|
1367
|
+
results.sort((a, b) => b.score - a.score);
|
|
1368
|
+
const topResults = results.slice(0, topK);
|
|
1369
|
+
return topResults;
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
// src/tabular/FsFolderTabularStorage.ts
|
|
1143
1373
|
import {
|
|
1144
|
-
createServiceToken as
|
|
1374
|
+
createServiceToken as createServiceToken11,
|
|
1145
1375
|
makeFingerprint as makeFingerprint5,
|
|
1146
|
-
sleep as sleep3
|
|
1376
|
+
sleep as sleep3,
|
|
1377
|
+
uuid4 as uuid43
|
|
1147
1378
|
} from "@workglow/util";
|
|
1148
1379
|
import { mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
1149
1380
|
import path from "node:path";
|
|
1150
|
-
var FS_FOLDER_TABULAR_REPOSITORY =
|
|
1381
|
+
var FS_FOLDER_TABULAR_REPOSITORY = createServiceToken11("storage.tabularRepository.fsFolder");
|
|
1151
1382
|
|
|
1152
|
-
class
|
|
1383
|
+
class FsFolderTabularStorage extends BaseTabularStorage {
|
|
1153
1384
|
folderPath;
|
|
1385
|
+
autoIncrementCounter = 0;
|
|
1154
1386
|
pollingManager = null;
|
|
1155
|
-
constructor(folderPath, schema, primaryKeyNames, indexes = []) {
|
|
1156
|
-
super(schema, primaryKeyNames, indexes);
|
|
1387
|
+
constructor(folderPath, schema, primaryKeyNames, indexes = [], clientProvidedKeys = "if-missing") {
|
|
1388
|
+
super(schema, primaryKeyNames, indexes, clientProvidedKeys);
|
|
1157
1389
|
this.folderPath = path.join(folderPath);
|
|
1158
1390
|
}
|
|
1159
1391
|
async setupDirectory() {
|
|
@@ -1166,11 +1398,39 @@ class FsFolderTabularRepository extends TabularRepository {
|
|
|
1166
1398
|
} catch {}
|
|
1167
1399
|
}
|
|
1168
1400
|
}
|
|
1401
|
+
generateKeyValue(columnName, strategy) {
|
|
1402
|
+
if (strategy === "autoincrement") {
|
|
1403
|
+
return ++this.autoIncrementCounter;
|
|
1404
|
+
} else {
|
|
1405
|
+
return uuid43();
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1169
1408
|
async put(entity) {
|
|
1409
|
+
let entityToStore = entity;
|
|
1410
|
+
if (this.hasAutoGeneratedKey() && this.autoGeneratedKeyName) {
|
|
1411
|
+
const keyName = this.autoGeneratedKeyName;
|
|
1412
|
+
const clientProvidedValue = entity[keyName];
|
|
1413
|
+
const hasClientValue = clientProvidedValue !== undefined && clientProvidedValue !== null;
|
|
1414
|
+
let shouldGenerate = false;
|
|
1415
|
+
if (this.clientProvidedKeys === "never") {
|
|
1416
|
+
shouldGenerate = true;
|
|
1417
|
+
} else if (this.clientProvidedKeys === "always") {
|
|
1418
|
+
if (!hasClientValue) {
|
|
1419
|
+
throw new Error(`Auto-generated key "${keyName}" is required when clientProvidedKeys is "always"`);
|
|
1420
|
+
}
|
|
1421
|
+
shouldGenerate = false;
|
|
1422
|
+
} else {
|
|
1423
|
+
shouldGenerate = !hasClientValue;
|
|
1424
|
+
}
|
|
1425
|
+
if (shouldGenerate) {
|
|
1426
|
+
const generatedValue = this.generateKeyValue(keyName, this.autoGeneratedKeyStrategy);
|
|
1427
|
+
entityToStore = { ...entity, [keyName]: generatedValue };
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1170
1430
|
await this.setupDirectory();
|
|
1171
|
-
const filePath = await this.getFilePath(
|
|
1431
|
+
const filePath = await this.getFilePath(entityToStore);
|
|
1172
1432
|
try {
|
|
1173
|
-
await writeFile(filePath, JSON.stringify(
|
|
1433
|
+
await writeFile(filePath, JSON.stringify(entityToStore));
|
|
1174
1434
|
} catch (error) {
|
|
1175
1435
|
try {
|
|
1176
1436
|
await sleep3(1);
|
|
@@ -1179,8 +1439,8 @@ class FsFolderTabularRepository extends TabularRepository {
|
|
|
1179
1439
|
console.error("Error writing file", filePath, error2);
|
|
1180
1440
|
}
|
|
1181
1441
|
}
|
|
1182
|
-
this.events.emit("put",
|
|
1183
|
-
return
|
|
1442
|
+
this.events.emit("put", entityToStore);
|
|
1443
|
+
return entityToStore;
|
|
1184
1444
|
}
|
|
1185
1445
|
async putBulk(entities) {
|
|
1186
1446
|
await this.setupDirectory();
|
|
@@ -1244,7 +1504,7 @@ class FsFolderTabularRepository extends TabularRepository {
|
|
|
1244
1504
|
return jsonFiles.length;
|
|
1245
1505
|
}
|
|
1246
1506
|
async search(key) {
|
|
1247
|
-
throw new Error("Search not supported for
|
|
1507
|
+
throw new Error("Search not supported for FsFolderTabularStorage");
|
|
1248
1508
|
}
|
|
1249
1509
|
async getFilePath(value) {
|
|
1250
1510
|
const { key } = this.separateKeyValueFromCombined(value);
|
|
@@ -1253,7 +1513,7 @@ class FsFolderTabularRepository extends TabularRepository {
|
|
|
1253
1513
|
return fullPath;
|
|
1254
1514
|
}
|
|
1255
1515
|
async deleteSearch(_criteria) {
|
|
1256
|
-
throw new Error("deleteSearch is not supported for
|
|
1516
|
+
throw new Error("deleteSearch is not supported for FsFolderTabularStorage");
|
|
1257
1517
|
}
|
|
1258
1518
|
getPollingManager() {
|
|
1259
1519
|
if (!this.pollingManager) {
|
|
@@ -1287,14 +1547,16 @@ class FsFolderTabularRepository extends TabularRepository {
|
|
|
1287
1547
|
super.destroy();
|
|
1288
1548
|
}
|
|
1289
1549
|
}
|
|
1290
|
-
// src/tabular/
|
|
1291
|
-
import {
|
|
1550
|
+
// src/tabular/PostgresTabularStorage.ts
|
|
1551
|
+
import {
|
|
1552
|
+
createServiceToken as createServiceToken12
|
|
1553
|
+
} from "@workglow/util";
|
|
1292
1554
|
|
|
1293
|
-
// src/tabular/
|
|
1294
|
-
class
|
|
1555
|
+
// src/tabular/BaseSqlTabularStorage.ts
|
|
1556
|
+
class BaseSqlTabularStorage extends BaseTabularStorage {
|
|
1295
1557
|
table;
|
|
1296
|
-
constructor(table = "tabular_store", schema, primaryKeyNames, indexes = []) {
|
|
1297
|
-
super(schema, primaryKeyNames, indexes);
|
|
1558
|
+
constructor(table = "tabular_store", schema, primaryKeyNames, indexes = [], clientProvidedKeys = "if-missing") {
|
|
1559
|
+
super(schema, primaryKeyNames, indexes, clientProvidedKeys);
|
|
1298
1560
|
this.table = table;
|
|
1299
1561
|
this.validateTableAndSchema();
|
|
1300
1562
|
}
|
|
@@ -1473,13 +1735,13 @@ class BaseSqlTabularRepository extends TabularRepository {
|
|
|
1473
1735
|
}
|
|
1474
1736
|
}
|
|
1475
1737
|
|
|
1476
|
-
// src/tabular/
|
|
1477
|
-
var POSTGRES_TABULAR_REPOSITORY =
|
|
1738
|
+
// src/tabular/PostgresTabularStorage.ts
|
|
1739
|
+
var POSTGRES_TABULAR_REPOSITORY = createServiceToken12("storage.tabularRepository.postgres");
|
|
1478
1740
|
|
|
1479
|
-
class
|
|
1741
|
+
class PostgresTabularStorage extends BaseSqlTabularStorage {
|
|
1480
1742
|
db;
|
|
1481
|
-
constructor(db, table = "tabular_store", schema, primaryKeyNames, indexes = []) {
|
|
1482
|
-
super(table, schema, primaryKeyNames, indexes);
|
|
1743
|
+
constructor(db, table = "tabular_store", schema, primaryKeyNames, indexes = [], clientProvidedKeys = "if-missing") {
|
|
1744
|
+
super(table, schema, primaryKeyNames, indexes, clientProvidedKeys);
|
|
1483
1745
|
this.db = db;
|
|
1484
1746
|
}
|
|
1485
1747
|
async setupDatabase() {
|
|
@@ -1490,6 +1752,7 @@ class PostgresTabularRepository extends BaseSqlTabularRepository {
|
|
|
1490
1752
|
)
|
|
1491
1753
|
`;
|
|
1492
1754
|
await this.db.query(sql);
|
|
1755
|
+
await this.createVectorIndexes();
|
|
1493
1756
|
const pkColumns = this.primaryKeyColumns();
|
|
1494
1757
|
const createdIndexes = new Set;
|
|
1495
1758
|
for (const columns of this.indexes) {
|
|
@@ -1513,6 +1776,14 @@ class PostgresTabularRepository extends BaseSqlTabularRepository {
|
|
|
1513
1776
|
}
|
|
1514
1777
|
}
|
|
1515
1778
|
}
|
|
1779
|
+
isVectorFormat(format) {
|
|
1780
|
+
if (!format)
|
|
1781
|
+
return false;
|
|
1782
|
+
return format.startsWith("TypedArray:") || format === "TypedArray";
|
|
1783
|
+
}
|
|
1784
|
+
getVectorDimensions(typeDef) {
|
|
1785
|
+
return;
|
|
1786
|
+
}
|
|
1516
1787
|
mapTypeToSQL(typeDef) {
|
|
1517
1788
|
const actualType = this.getNonNullType(typeDef);
|
|
1518
1789
|
if (typeof actualType === "boolean") {
|
|
@@ -1532,6 +1803,12 @@ class PostgresTabularRepository extends BaseSqlTabularRepository {
|
|
|
1532
1803
|
return "VARCHAR(2048)";
|
|
1533
1804
|
if (actualType.format === "uuid")
|
|
1534
1805
|
return "UUID";
|
|
1806
|
+
if (this.isVectorFormat(actualType.format)) {
|
|
1807
|
+
const dimension = this.getVectorDimensions(actualType);
|
|
1808
|
+
if (typeof dimension === "number") {
|
|
1809
|
+
return `vector(${dimension})`;
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1535
1812
|
if (typeof actualType.maxLength === "number") {
|
|
1536
1813
|
return `VARCHAR(${actualType.maxLength})`;
|
|
1537
1814
|
}
|
|
@@ -1599,6 +1876,17 @@ class PostgresTabularRepository extends BaseSqlTabularRepository {
|
|
|
1599
1876
|
}
|
|
1600
1877
|
constructPrimaryKeyColumns($delimiter = "") {
|
|
1601
1878
|
const cols = Object.entries(this.primaryKeySchema.properties).map(([key, typeDef]) => {
|
|
1879
|
+
if (this.isAutoGeneratedKey(key)) {
|
|
1880
|
+
if (this.autoGeneratedKeyStrategy === "autoincrement") {
|
|
1881
|
+
const sqlType2 = this.mapTypeToSQL(typeDef);
|
|
1882
|
+
const isSmallInt = sqlType2.includes("SMALLINT");
|
|
1883
|
+
const isBigInt = sqlType2.includes("BIGINT");
|
|
1884
|
+
const serialType = isBigInt ? "BIGSERIAL" : isSmallInt ? "SMALLSERIAL" : "SERIAL";
|
|
1885
|
+
return `${$delimiter}${key}${$delimiter} ${serialType}`;
|
|
1886
|
+
} else if (this.autoGeneratedKeyStrategy === "uuid") {
|
|
1887
|
+
return `${$delimiter}${key}${$delimiter} UUID DEFAULT gen_random_uuid()`;
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1602
1890
|
const sqlType = this.mapTypeToSQL(typeDef);
|
|
1603
1891
|
let constraints = "NOT NULL";
|
|
1604
1892
|
if (this.shouldBeUnsigned(typeDef)) {
|
|
@@ -1626,6 +1914,22 @@ class PostgresTabularRepository extends BaseSqlTabularRepository {
|
|
|
1626
1914
|
return "";
|
|
1627
1915
|
}
|
|
1628
1916
|
}
|
|
1917
|
+
jsToSqlValue(column, value) {
|
|
1918
|
+
const typeDef = this.schema.properties[column];
|
|
1919
|
+
if (typeDef) {
|
|
1920
|
+
const actualType = this.getNonNullType(typeDef);
|
|
1921
|
+
if (typeof actualType !== "boolean" && this.isVectorFormat(actualType.format)) {
|
|
1922
|
+
if (value && ArrayBuffer.isView(value) && !(value instanceof DataView)) {
|
|
1923
|
+
const array = Array.from(value);
|
|
1924
|
+
return `[${array.join(",")}]`;
|
|
1925
|
+
}
|
|
1926
|
+
if (typeof value === "string") {
|
|
1927
|
+
return value;
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
return super.jsToSqlValue(column, value);
|
|
1932
|
+
}
|
|
1629
1933
|
sqlToJsValue(column, value) {
|
|
1630
1934
|
const typeDef = this.schema.properties[column];
|
|
1631
1935
|
if (typeDef) {
|
|
@@ -1633,6 +1937,19 @@ class PostgresTabularRepository extends BaseSqlTabularRepository {
|
|
|
1633
1937
|
return null;
|
|
1634
1938
|
}
|
|
1635
1939
|
const actualType = this.getNonNullType(typeDef);
|
|
1940
|
+
if (typeof actualType !== "boolean" && this.isVectorFormat(actualType.format)) {
|
|
1941
|
+
if (typeof value === "string") {
|
|
1942
|
+
try {
|
|
1943
|
+
const array = JSON.parse(value);
|
|
1944
|
+
return new Float32Array(array);
|
|
1945
|
+
} catch (e) {
|
|
1946
|
+
console.warn(`Failed to parse vector for column ${column}:`, e);
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
if (value && typeof value === "object") {
|
|
1950
|
+
return value;
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1636
1953
|
if (typeof actualType !== "boolean" && (actualType.type === "number" || actualType.type === "integer")) {
|
|
1637
1954
|
const v = value;
|
|
1638
1955
|
if (typeof v === "number")
|
|
@@ -1656,30 +1973,105 @@ class PostgresTabularRepository extends BaseSqlTabularRepository {
|
|
|
1656
1973
|
}
|
|
1657
1974
|
return false;
|
|
1658
1975
|
}
|
|
1976
|
+
getVectorColumns() {
|
|
1977
|
+
const vectorColumns = [];
|
|
1978
|
+
for (const [key, typeDef] of Object.entries(this.schema.properties)) {
|
|
1979
|
+
const actualType = this.getNonNullType(typeDef);
|
|
1980
|
+
if (typeof actualType !== "boolean" && this.isVectorFormat(actualType.format)) {
|
|
1981
|
+
const dimension = this.getVectorDimensions(actualType);
|
|
1982
|
+
if (typeof dimension === "number") {
|
|
1983
|
+
vectorColumns.push({ column: key, dimension });
|
|
1984
|
+
} else {
|
|
1985
|
+
console.warn(`Invalid vector format for column ${key}: ${actualType.format}, skipping`);
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
return vectorColumns;
|
|
1990
|
+
}
|
|
1991
|
+
async createVectorIndexes() {
|
|
1992
|
+
const vectorColumns = this.getVectorColumns();
|
|
1993
|
+
if (vectorColumns.length === 0) {
|
|
1994
|
+
return;
|
|
1995
|
+
}
|
|
1996
|
+
try {
|
|
1997
|
+
await this.db.query("CREATE EXTENSION IF NOT EXISTS vector");
|
|
1998
|
+
} catch (error) {
|
|
1999
|
+
console.warn("pgvector extension not available, vector columns will use TEXT fallback:", error);
|
|
2000
|
+
return;
|
|
2001
|
+
}
|
|
2002
|
+
for (const { column } of vectorColumns) {
|
|
2003
|
+
const indexName = `${this.table}_${column}_hnsw_idx`;
|
|
2004
|
+
try {
|
|
2005
|
+
await this.db.query(`
|
|
2006
|
+
CREATE INDEX IF NOT EXISTS "${indexName}"
|
|
2007
|
+
ON "${this.table}"
|
|
2008
|
+
USING hnsw ("${column}" vector_cosine_ops)
|
|
2009
|
+
`);
|
|
2010
|
+
} catch (error) {
|
|
2011
|
+
console.warn(`Failed to create HNSW index on ${column}:`, error);
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
1659
2015
|
async put(entity) {
|
|
1660
2016
|
const db = this.db;
|
|
1661
|
-
const
|
|
1662
|
-
const
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
2017
|
+
const columnsToInsert = [];
|
|
2018
|
+
const paramsToInsert = [];
|
|
2019
|
+
let paramIndex = 1;
|
|
2020
|
+
const pkColumns = this.primaryKeyColumns();
|
|
2021
|
+
for (const col of pkColumns) {
|
|
2022
|
+
const colStr = String(col);
|
|
2023
|
+
if (this.isAutoGeneratedKey(colStr)) {
|
|
2024
|
+
const clientProvidedValue = entity[col];
|
|
2025
|
+
const hasClientValue = clientProvidedValue !== undefined && clientProvidedValue !== null;
|
|
2026
|
+
let shouldUseClientValue = false;
|
|
2027
|
+
if (this.clientProvidedKeys === "never") {
|
|
2028
|
+
shouldUseClientValue = false;
|
|
2029
|
+
} else if (this.clientProvidedKeys === "always") {
|
|
2030
|
+
if (!hasClientValue) {
|
|
2031
|
+
throw new Error(`Auto-generated key "${colStr}" is required when clientProvidedKeys is "always"`);
|
|
2032
|
+
}
|
|
2033
|
+
shouldUseClientValue = true;
|
|
2034
|
+
} else {
|
|
2035
|
+
shouldUseClientValue = hasClientValue;
|
|
2036
|
+
}
|
|
2037
|
+
if (shouldUseClientValue) {
|
|
2038
|
+
columnsToInsert.push(colStr);
|
|
2039
|
+
paramsToInsert.push(this.jsToSqlValue(colStr, clientProvidedValue));
|
|
2040
|
+
}
|
|
2041
|
+
continue;
|
|
2042
|
+
}
|
|
2043
|
+
columnsToInsert.push(colStr);
|
|
2044
|
+
const value = entity[col];
|
|
2045
|
+
paramsToInsert.push(this.jsToSqlValue(colStr, value));
|
|
2046
|
+
}
|
|
2047
|
+
const valueColumns = this.valueColumns();
|
|
2048
|
+
for (const col of valueColumns) {
|
|
2049
|
+
const colStr = String(col);
|
|
2050
|
+
columnsToInsert.push(colStr);
|
|
2051
|
+
const value = entity[col];
|
|
2052
|
+
paramsToInsert.push(this.jsToSqlValue(colStr, value));
|
|
2053
|
+
}
|
|
2054
|
+
const columnList = columnsToInsert.map((c) => `"${c}"`).join(", ");
|
|
2055
|
+
const placeholders = columnsToInsert.map((_, i) => `$${i + 1}`).join(", ");
|
|
2056
|
+
const conflictClause = valueColumns.length > 0 ? `
|
|
1670
2057
|
ON CONFLICT (${this.primaryKeyColumnList('"')}) DO UPDATE
|
|
1671
2058
|
SET
|
|
1672
|
-
${
|
|
1673
|
-
|
|
2059
|
+
${valueColumns.map((col) => {
|
|
2060
|
+
const colIdx = columnsToInsert.indexOf(String(col));
|
|
2061
|
+
return `"${col}" = $${colIdx + 1}`;
|
|
2062
|
+
}).join(", ")}
|
|
2063
|
+
` : "";
|
|
2064
|
+
const sql = `
|
|
2065
|
+
INSERT INTO "${this.table}" (${columnList})
|
|
2066
|
+
VALUES (${placeholders})
|
|
2067
|
+
${conflictClause}
|
|
1674
2068
|
RETURNING *
|
|
1675
2069
|
`;
|
|
1676
|
-
const
|
|
1677
|
-
const valueParams = this.getValueAsOrderedArray(value);
|
|
1678
|
-
const params = [...primaryKeyParams, ...valueParams];
|
|
2070
|
+
const params = paramsToInsert;
|
|
1679
2071
|
const result = await db.query(sql, params);
|
|
1680
2072
|
const updatedEntity = result.rows[0];
|
|
1681
|
-
for (const
|
|
1682
|
-
updatedEntity[
|
|
2073
|
+
for (const key in this.schema.properties) {
|
|
2074
|
+
updatedEntity[key] = this.sqlToJsValue(key, updatedEntity[key]);
|
|
1683
2075
|
}
|
|
1684
2076
|
this.events.emit("put", updatedEntity);
|
|
1685
2077
|
return updatedEntity;
|
|
@@ -1687,45 +2079,7 @@ class PostgresTabularRepository extends BaseSqlTabularRepository {
|
|
|
1687
2079
|
async putBulk(entities) {
|
|
1688
2080
|
if (entities.length === 0)
|
|
1689
2081
|
return [];
|
|
1690
|
-
|
|
1691
|
-
const allParams = [];
|
|
1692
|
-
const valuesPerRow = this.primaryKeyColumns().length + this.valueColumns().length;
|
|
1693
|
-
let paramIndex = 1;
|
|
1694
|
-
const valuesClauses = entities.map((entity) => {
|
|
1695
|
-
const { key, value } = this.separateKeyValueFromCombined(entity);
|
|
1696
|
-
const primaryKeyParams = this.getPrimaryKeyAsOrderedArray(key);
|
|
1697
|
-
const valueParams = this.getValueAsOrderedArray(value);
|
|
1698
|
-
const entityParams = [...primaryKeyParams, ...valueParams];
|
|
1699
|
-
allParams.push(...entityParams);
|
|
1700
|
-
const placeholders = Array(valuesPerRow).fill(0).map(() => `$${paramIndex++}`).join(", ");
|
|
1701
|
-
return `(${placeholders})`;
|
|
1702
|
-
}).join(", ");
|
|
1703
|
-
const sql = `
|
|
1704
|
-
INSERT INTO "${this.table}" (
|
|
1705
|
-
${this.primaryKeyColumnList('"')} ${this.valueColumnList() ? ", " + this.valueColumnList('"') : ""}
|
|
1706
|
-
)
|
|
1707
|
-
VALUES ${valuesClauses}
|
|
1708
|
-
${!this.valueColumnList() ? "" : `
|
|
1709
|
-
ON CONFLICT (${this.primaryKeyColumnList('"')}) DO UPDATE
|
|
1710
|
-
SET
|
|
1711
|
-
${this.valueColumns().map((col) => {
|
|
1712
|
-
return `"${col}" = EXCLUDED."${col}"`;
|
|
1713
|
-
}).join(", ")}
|
|
1714
|
-
`}
|
|
1715
|
-
RETURNING *
|
|
1716
|
-
`;
|
|
1717
|
-
const result = await db.query(sql, allParams);
|
|
1718
|
-
const updatedEntities = result.rows.map((row) => {
|
|
1719
|
-
const entity = row;
|
|
1720
|
-
for (const key in this.schema.properties) {
|
|
1721
|
-
entity[key] = this.sqlToJsValue(key, entity[key]);
|
|
1722
|
-
}
|
|
1723
|
-
return entity;
|
|
1724
|
-
});
|
|
1725
|
-
for (const entity of updatedEntities) {
|
|
1726
|
-
this.events.emit("put", entity);
|
|
1727
|
-
}
|
|
1728
|
-
return updatedEntities;
|
|
2082
|
+
return await Promise.all(entities.map((entity) => this.put(entity)));
|
|
1729
2083
|
}
|
|
1730
2084
|
async get(key) {
|
|
1731
2085
|
const db = this.db;
|
|
@@ -1846,35 +2200,53 @@ class PostgresTabularRepository extends BaseSqlTabularRepository {
|
|
|
1846
2200
|
this.events.emit("delete", criteriaKeys[0]);
|
|
1847
2201
|
}
|
|
1848
2202
|
subscribeToChanges(callback, options) {
|
|
1849
|
-
throw new Error("subscribeToChanges is not supported for
|
|
2203
|
+
throw new Error("subscribeToChanges is not supported for PostgresTabularStorage");
|
|
1850
2204
|
}
|
|
1851
2205
|
destroy() {
|
|
1852
2206
|
super.destroy();
|
|
1853
2207
|
}
|
|
1854
2208
|
}
|
|
1855
|
-
// src/tabular/
|
|
2209
|
+
// src/tabular/SqliteTabularStorage.ts
|
|
1856
2210
|
import { Sqlite } from "@workglow/sqlite";
|
|
1857
|
-
import {
|
|
1858
|
-
|
|
2211
|
+
import {
|
|
2212
|
+
createServiceToken as createServiceToken13,
|
|
2213
|
+
uuid4 as uuid44
|
|
2214
|
+
} from "@workglow/util";
|
|
2215
|
+
var SQLITE_TABULAR_REPOSITORY = createServiceToken13("storage.tabularRepository.sqlite");
|
|
1859
2216
|
var Database = Sqlite.Database;
|
|
1860
2217
|
|
|
1861
|
-
class
|
|
2218
|
+
class SqliteTabularStorage extends BaseSqlTabularStorage {
|
|
1862
2219
|
db;
|
|
1863
|
-
constructor(dbOrPath, table = "tabular_store", schema, primaryKeyNames, indexes = []) {
|
|
1864
|
-
super(table, schema, primaryKeyNames, indexes);
|
|
2220
|
+
constructor(dbOrPath, table = "tabular_store", schema, primaryKeyNames, indexes = [], clientProvidedKeys = "if-missing") {
|
|
2221
|
+
super(table, schema, primaryKeyNames, indexes, clientProvidedKeys);
|
|
1865
2222
|
if (typeof dbOrPath === "string") {
|
|
1866
2223
|
this.db = new Database(dbOrPath);
|
|
1867
2224
|
} else {
|
|
1868
2225
|
this.db = dbOrPath;
|
|
1869
2226
|
}
|
|
1870
2227
|
}
|
|
2228
|
+
constructPrimaryKeyColumns($delimiter = "") {
|
|
2229
|
+
const cols = Object.entries(this.primaryKeySchema.properties).map(([key, typeDef]) => {
|
|
2230
|
+
if (this.isAutoGeneratedKey(key) && this.autoGeneratedKeyStrategy === "autoincrement") {
|
|
2231
|
+
return `${$delimiter}${key}${$delimiter} INTEGER PRIMARY KEY AUTOINCREMENT`;
|
|
2232
|
+
}
|
|
2233
|
+
const sqlType = this.mapTypeToSQL(typeDef);
|
|
2234
|
+
return `${$delimiter}${key}${$delimiter} ${sqlType} NOT NULL`;
|
|
2235
|
+
}).join(", ");
|
|
2236
|
+
return cols;
|
|
2237
|
+
}
|
|
1871
2238
|
async setupDatabase() {
|
|
1872
|
-
const
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
2239
|
+
const hasAutoIncrementKey = this.hasAutoGeneratedKey() && this.autoGeneratedKeyStrategy === "autoincrement";
|
|
2240
|
+
const sql = hasAutoIncrementKey ? `
|
|
2241
|
+
CREATE TABLE IF NOT EXISTS \`${this.table}\` (
|
|
2242
|
+
${this.constructPrimaryKeyColumns()} ${this.constructValueColumns()}
|
|
2243
|
+
)
|
|
2244
|
+
` : `
|
|
2245
|
+
CREATE TABLE IF NOT EXISTS \`${this.table}\` (
|
|
2246
|
+
${this.constructPrimaryKeyColumns()} ${this.constructValueColumns()},
|
|
2247
|
+
PRIMARY KEY (${this.primaryKeyColumnList()})
|
|
2248
|
+
)
|
|
2249
|
+
`;
|
|
1878
2250
|
this.db.exec(sql);
|
|
1879
2251
|
const pkColumns = this.primaryKeyColumns();
|
|
1880
2252
|
const createdIndexes = new Set;
|
|
@@ -2015,21 +2387,71 @@ class SqliteTabularRepository extends BaseSqlTabularRepository {
|
|
|
2015
2387
|
return "TEXT /* unknown type */";
|
|
2016
2388
|
}
|
|
2017
2389
|
}
|
|
2390
|
+
generateKeyValue(columnName, strategy) {
|
|
2391
|
+
if (strategy === "uuid") {
|
|
2392
|
+
return uuid44();
|
|
2393
|
+
}
|
|
2394
|
+
throw new Error(`SQLite autoincrement keys are generated by the database, not client-side. Column: ${columnName}`);
|
|
2395
|
+
}
|
|
2018
2396
|
async put(entity) {
|
|
2019
2397
|
const db = this.db;
|
|
2020
|
-
|
|
2398
|
+
let entityToInsert = entity;
|
|
2399
|
+
if (this.hasAutoGeneratedKey() && this.autoGeneratedKeyName) {
|
|
2400
|
+
const keyName = String(this.autoGeneratedKeyName);
|
|
2401
|
+
const clientProvidedValue = entity[keyName];
|
|
2402
|
+
const hasClientValue = clientProvidedValue !== undefined && clientProvidedValue !== null;
|
|
2403
|
+
let shouldUseClientValue = false;
|
|
2404
|
+
if (this.clientProvidedKeys === "never") {
|
|
2405
|
+
shouldUseClientValue = false;
|
|
2406
|
+
} else if (this.clientProvidedKeys === "always") {
|
|
2407
|
+
if (!hasClientValue) {
|
|
2408
|
+
throw new Error(`Auto-generated key "${keyName}" is required when clientProvidedKeys is "always"`);
|
|
2409
|
+
}
|
|
2410
|
+
shouldUseClientValue = true;
|
|
2411
|
+
} else {
|
|
2412
|
+
shouldUseClientValue = hasClientValue;
|
|
2413
|
+
}
|
|
2414
|
+
if (this.autoGeneratedKeyStrategy === "uuid" && !shouldUseClientValue) {
|
|
2415
|
+
const generatedValue = this.generateKeyValue(keyName, "uuid");
|
|
2416
|
+
entityToInsert = { ...entity, [keyName]: generatedValue };
|
|
2417
|
+
} else if (this.autoGeneratedKeyStrategy === "uuid" && shouldUseClientValue) {
|
|
2418
|
+
entityToInsert = entity;
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2421
|
+
let columnsToInsert = [];
|
|
2422
|
+
let paramsToInsert = [];
|
|
2423
|
+
const pkColumns = this.primaryKeyColumns();
|
|
2424
|
+
for (const col of pkColumns) {
|
|
2425
|
+
const colStr = String(col);
|
|
2426
|
+
if (this.isAutoGeneratedKey(colStr) && this.autoGeneratedKeyStrategy === "autoincrement" && this.clientProvidedKeys !== "always") {
|
|
2427
|
+
const clientProvidedValue = entityToInsert[colStr];
|
|
2428
|
+
const hasClientValue = clientProvidedValue !== undefined && clientProvidedValue !== null;
|
|
2429
|
+
if (this.clientProvidedKeys === "if-missing" && hasClientValue) {
|
|
2430
|
+
columnsToInsert.push(colStr);
|
|
2431
|
+
paramsToInsert.push(this.jsToSqlValue(colStr, clientProvidedValue));
|
|
2432
|
+
}
|
|
2433
|
+
continue;
|
|
2434
|
+
}
|
|
2435
|
+
columnsToInsert.push(colStr);
|
|
2436
|
+
const value = entityToInsert[colStr];
|
|
2437
|
+
paramsToInsert.push(this.jsToSqlValue(colStr, value));
|
|
2438
|
+
}
|
|
2439
|
+
const valueColumns = this.valueColumns();
|
|
2440
|
+
for (const col of valueColumns) {
|
|
2441
|
+
const colStr = String(col);
|
|
2442
|
+
columnsToInsert.push(colStr);
|
|
2443
|
+
const value = entityToInsert[colStr];
|
|
2444
|
+
paramsToInsert.push(this.jsToSqlValue(colStr, value));
|
|
2445
|
+
}
|
|
2446
|
+
const columnList = columnsToInsert.map((c) => `\`${c}\``).join(", ");
|
|
2447
|
+
const placeholders = columnsToInsert.map(() => "?").join(", ");
|
|
2021
2448
|
const sql = `
|
|
2022
|
-
INSERT OR REPLACE INTO \`${this.table}\` (${
|
|
2023
|
-
VALUES (
|
|
2024
|
-
${this.primaryKeyColumns().map((i) => "?")}
|
|
2025
|
-
${this.valueColumns().length > 0 ? ", " + this.valueColumns().map((i) => "?") : ""}
|
|
2026
|
-
)
|
|
2449
|
+
INSERT OR REPLACE INTO \`${this.table}\` (${columnList})
|
|
2450
|
+
VALUES (${placeholders})
|
|
2027
2451
|
RETURNING *
|
|
2028
2452
|
`;
|
|
2029
2453
|
const stmt = db.prepare(sql);
|
|
2030
|
-
const
|
|
2031
|
-
const valueParams = this.getValueAsOrderedArray(value);
|
|
2032
|
-
const params = [...primaryKeyParams, ...valueParams];
|
|
2454
|
+
const params = paramsToInsert;
|
|
2033
2455
|
for (let i = 0;i < params.length; i++) {
|
|
2034
2456
|
let param = params[i];
|
|
2035
2457
|
if (param === undefined) {
|
|
@@ -2084,46 +2506,7 @@ class SqliteTabularRepository extends BaseSqlTabularRepository {
|
|
|
2084
2506
|
async putBulk(entities) {
|
|
2085
2507
|
if (entities.length === 0)
|
|
2086
2508
|
return [];
|
|
2087
|
-
|
|
2088
|
-
const updatedEntities = [];
|
|
2089
|
-
const transaction = db.transaction((entitiesToInsert) => {
|
|
2090
|
-
for (const entity of entitiesToInsert) {
|
|
2091
|
-
const { key, value } = this.separateKeyValueFromCombined(entity);
|
|
2092
|
-
const sql = `
|
|
2093
|
-
INSERT OR REPLACE INTO \`${this.table}\` (${this.primaryKeyColumnList()} ${this.valueColumnList() ? ", " + this.valueColumnList() : ""})
|
|
2094
|
-
VALUES (
|
|
2095
|
-
${this.primaryKeyColumns().map(() => "?").join(", ")}
|
|
2096
|
-
${this.valueColumns().length > 0 ? ", " + this.valueColumns().map(() => "?").join(", ") : ""}
|
|
2097
|
-
)
|
|
2098
|
-
RETURNING *
|
|
2099
|
-
`;
|
|
2100
|
-
const stmt = db.prepare(sql);
|
|
2101
|
-
const primaryKeyParams = this.getPrimaryKeyAsOrderedArray(key);
|
|
2102
|
-
const valueParams = this.getValueAsOrderedArray(value);
|
|
2103
|
-
const params = [...primaryKeyParams, ...valueParams];
|
|
2104
|
-
for (let i = 0;i < params.length; i++) {
|
|
2105
|
-
let param = params[i];
|
|
2106
|
-
if (param === undefined) {
|
|
2107
|
-
params[i] = null;
|
|
2108
|
-
} else if (param !== null && typeof param === "object") {
|
|
2109
|
-
const paramObj = param;
|
|
2110
|
-
if (!(paramObj instanceof Uint8Array) && (typeof Buffer === "undefined" || !(paramObj instanceof Buffer))) {
|
|
2111
|
-
params[i] = JSON.stringify(paramObj);
|
|
2112
|
-
}
|
|
2113
|
-
}
|
|
2114
|
-
}
|
|
2115
|
-
const updatedEntity = stmt.get(...params);
|
|
2116
|
-
for (const k in this.schema.properties) {
|
|
2117
|
-
updatedEntity[k] = this.sqlToJsValue(k, updatedEntity[k]);
|
|
2118
|
-
}
|
|
2119
|
-
updatedEntities.push(updatedEntity);
|
|
2120
|
-
}
|
|
2121
|
-
});
|
|
2122
|
-
transaction(entities);
|
|
2123
|
-
for (const entity of updatedEntities) {
|
|
2124
|
-
this.events.emit("put", entity);
|
|
2125
|
-
}
|
|
2126
|
-
return updatedEntities;
|
|
2509
|
+
return await Promise.all(entities.map((entity) => this.put(entity)));
|
|
2127
2510
|
}
|
|
2128
2511
|
async get(key) {
|
|
2129
2512
|
const db = this.db;
|
|
@@ -2248,21 +2631,23 @@ class SqliteTabularRepository extends BaseSqlTabularRepository {
|
|
|
2248
2631
|
this.events.emit("delete", criteriaKeys[0]);
|
|
2249
2632
|
}
|
|
2250
2633
|
subscribeToChanges(callback, options) {
|
|
2251
|
-
throw new Error("subscribeToChanges is not supported for
|
|
2634
|
+
throw new Error("subscribeToChanges is not supported for SqliteTabularStorage");
|
|
2252
2635
|
}
|
|
2253
2636
|
destroy() {
|
|
2254
2637
|
super.destroy();
|
|
2255
2638
|
}
|
|
2256
2639
|
}
|
|
2257
|
-
// src/tabular/
|
|
2258
|
-
import {
|
|
2259
|
-
|
|
2640
|
+
// src/tabular/SupabaseTabularStorage.ts
|
|
2641
|
+
import {
|
|
2642
|
+
createServiceToken as createServiceToken14
|
|
2643
|
+
} from "@workglow/util";
|
|
2644
|
+
var SUPABASE_TABULAR_REPOSITORY = createServiceToken14("storage.tabularRepository.supabase");
|
|
2260
2645
|
|
|
2261
|
-
class
|
|
2646
|
+
class SupabaseTabularStorage extends BaseSqlTabularStorage {
|
|
2262
2647
|
client;
|
|
2263
2648
|
realtimeChannel = null;
|
|
2264
|
-
constructor(client, table = "tabular_store", schema, primaryKeyNames, indexes = []) {
|
|
2265
|
-
super(table, schema, primaryKeyNames, indexes);
|
|
2649
|
+
constructor(client, table = "tabular_store", schema, primaryKeyNames, indexes = [], clientProvidedKeys = "if-missing") {
|
|
2650
|
+
super(table, schema, primaryKeyNames, indexes, clientProvidedKeys);
|
|
2266
2651
|
this.client = client;
|
|
2267
2652
|
}
|
|
2268
2653
|
async setupDatabase() {
|
|
@@ -2389,6 +2774,17 @@ class SupabaseTabularRepository extends BaseSqlTabularRepository {
|
|
|
2389
2774
|
}
|
|
2390
2775
|
constructPrimaryKeyColumns($delimiter = "") {
|
|
2391
2776
|
const cols = Object.entries(this.primaryKeySchema.properties).map(([key, typeDef]) => {
|
|
2777
|
+
if (this.isAutoGeneratedKey(key)) {
|
|
2778
|
+
if (this.autoGeneratedKeyStrategy === "autoincrement") {
|
|
2779
|
+
const sqlType2 = this.mapTypeToSQL(typeDef);
|
|
2780
|
+
const isSmallInt = sqlType2.includes("SMALLINT");
|
|
2781
|
+
const isBigInt = sqlType2.includes("BIGINT");
|
|
2782
|
+
const serialType = isBigInt ? "BIGSERIAL" : isSmallInt ? "SMALLSERIAL" : "SERIAL";
|
|
2783
|
+
return `${$delimiter}${key}${$delimiter} ${serialType}`;
|
|
2784
|
+
} else if (this.autoGeneratedKeyStrategy === "uuid") {
|
|
2785
|
+
return `${$delimiter}${key}${$delimiter} UUID DEFAULT gen_random_uuid()`;
|
|
2786
|
+
}
|
|
2787
|
+
}
|
|
2392
2788
|
const sqlType = this.mapTypeToSQL(typeDef);
|
|
2393
2789
|
let constraints = "NOT NULL";
|
|
2394
2790
|
if (this.shouldBeUnsigned(typeDef)) {
|
|
@@ -2448,7 +2844,27 @@ class SupabaseTabularRepository extends BaseSqlTabularRepository {
|
|
|
2448
2844
|
return false;
|
|
2449
2845
|
}
|
|
2450
2846
|
async put(entity) {
|
|
2451
|
-
|
|
2847
|
+
let entityToInsert = { ...entity };
|
|
2848
|
+
if (this.hasAutoGeneratedKey() && this.autoGeneratedKeyName) {
|
|
2849
|
+
const keyName = String(this.autoGeneratedKeyName);
|
|
2850
|
+
const clientProvidedValue = entity[keyName];
|
|
2851
|
+
const hasClientValue = clientProvidedValue !== undefined && clientProvidedValue !== null;
|
|
2852
|
+
let shouldOmitKey = false;
|
|
2853
|
+
if (this.clientProvidedKeys === "never") {
|
|
2854
|
+
shouldOmitKey = true;
|
|
2855
|
+
} else if (this.clientProvidedKeys === "always") {
|
|
2856
|
+
if (!hasClientValue) {
|
|
2857
|
+
throw new Error(`Auto-generated key "${keyName}" is required when clientProvidedKeys is "always"`);
|
|
2858
|
+
}
|
|
2859
|
+
shouldOmitKey = false;
|
|
2860
|
+
} else {
|
|
2861
|
+
shouldOmitKey = !hasClientValue;
|
|
2862
|
+
}
|
|
2863
|
+
if (shouldOmitKey) {
|
|
2864
|
+
delete entityToInsert[keyName];
|
|
2865
|
+
}
|
|
2866
|
+
}
|
|
2867
|
+
const normalizedEntity = { ...entityToInsert };
|
|
2452
2868
|
const requiredSet = new Set(this.valueSchema.required ?? []);
|
|
2453
2869
|
for (const key in this.valueSchema.properties) {
|
|
2454
2870
|
if (!(key in normalizedEntity) || normalizedEntity[key] === undefined) {
|
|
@@ -2470,29 +2886,7 @@ class SupabaseTabularRepository extends BaseSqlTabularRepository {
|
|
|
2470
2886
|
async putBulk(entities) {
|
|
2471
2887
|
if (entities.length === 0)
|
|
2472
2888
|
return [];
|
|
2473
|
-
|
|
2474
|
-
const normalizedEntities = entities.map((entity) => {
|
|
2475
|
-
const normalized = { ...entity };
|
|
2476
|
-
for (const key in this.valueSchema.properties) {
|
|
2477
|
-
if (!(key in normalized) || normalized[key] === undefined) {
|
|
2478
|
-
if (!requiredSet.has(key)) {
|
|
2479
|
-
normalized[key] = null;
|
|
2480
|
-
}
|
|
2481
|
-
}
|
|
2482
|
-
}
|
|
2483
|
-
return normalized;
|
|
2484
|
-
});
|
|
2485
|
-
const { data, error } = await this.client.from(this.table).upsert(normalizedEntities, { onConflict: this.primaryKeyColumnList() }).select();
|
|
2486
|
-
if (error)
|
|
2487
|
-
throw error;
|
|
2488
|
-
const updatedEntities = data;
|
|
2489
|
-
for (const entity of updatedEntities) {
|
|
2490
|
-
for (const key in this.schema.properties) {
|
|
2491
|
-
entity[key] = this.sqlToJsValue(key, entity[key]);
|
|
2492
|
-
}
|
|
2493
|
-
this.events.emit("put", entity);
|
|
2494
|
-
}
|
|
2495
|
-
return updatedEntities;
|
|
2889
|
+
return await Promise.all(entities.map((entity) => this.put(entity)));
|
|
2496
2890
|
}
|
|
2497
2891
|
async get(key) {
|
|
2498
2892
|
let query = this.client.from(this.table).select("*");
|
|
@@ -2665,26 +3059,26 @@ class SupabaseTabularRepository extends BaseSqlTabularRepository {
|
|
|
2665
3059
|
}
|
|
2666
3060
|
}
|
|
2667
3061
|
}
|
|
2668
|
-
// src/kv/
|
|
2669
|
-
import { createServiceToken as
|
|
2670
|
-
var FS_FOLDER_JSON_KV_REPOSITORY =
|
|
3062
|
+
// src/kv/FsFolderJsonKvStorage.ts
|
|
3063
|
+
import { createServiceToken as createServiceToken15 } from "@workglow/util";
|
|
3064
|
+
var FS_FOLDER_JSON_KV_REPOSITORY = createServiceToken15("storage.kvRepository.fsFolderJson");
|
|
2671
3065
|
|
|
2672
|
-
class
|
|
3066
|
+
class FsFolderJsonKvStorage extends KvViaTabularStorage {
|
|
2673
3067
|
folderPath;
|
|
2674
3068
|
tabularRepository;
|
|
2675
3069
|
constructor(folderPath, keySchema = { type: "string" }, valueSchema = {}) {
|
|
2676
3070
|
super(keySchema, valueSchema);
|
|
2677
3071
|
this.folderPath = folderPath;
|
|
2678
|
-
this.tabularRepository = new
|
|
3072
|
+
this.tabularRepository = new FsFolderTabularStorage(folderPath, DefaultKeyValueSchema, DefaultKeyValueKey);
|
|
2679
3073
|
}
|
|
2680
3074
|
}
|
|
2681
|
-
// src/kv/
|
|
2682
|
-
import { createServiceToken as
|
|
3075
|
+
// src/kv/FsFolderKvStorage.ts
|
|
3076
|
+
import { createServiceToken as createServiceToken16 } from "@workglow/util";
|
|
2683
3077
|
import { mkdir as mkdir2, readFile as readFile2, rm as rm2, unlink, writeFile as writeFile2 } from "fs/promises";
|
|
2684
3078
|
import path2 from "path";
|
|
2685
|
-
var FS_FOLDER_KV_REPOSITORY =
|
|
3079
|
+
var FS_FOLDER_KV_REPOSITORY = createServiceToken16("storage.kvRepository.fsFolder");
|
|
2686
3080
|
|
|
2687
|
-
class
|
|
3081
|
+
class FsFolderKvStorage extends KvStorage {
|
|
2688
3082
|
folderPath;
|
|
2689
3083
|
pathWriter;
|
|
2690
3084
|
constructor(folderPath, pathWriter, keySchema = { type: "string" }, valueSchema = { contentEncoding: "blob" }) {
|
|
@@ -2758,11 +3152,11 @@ class FsFolderKvRepository extends KvRepository {
|
|
|
2758
3152
|
throw new Error("Not implemented");
|
|
2759
3153
|
}
|
|
2760
3154
|
}
|
|
2761
|
-
// src/kv/
|
|
2762
|
-
import { createServiceToken as
|
|
2763
|
-
var POSTGRES_KV_REPOSITORY =
|
|
3155
|
+
// src/kv/PostgresKvStorage.ts
|
|
3156
|
+
import { createServiceToken as createServiceToken17 } from "@workglow/util";
|
|
3157
|
+
var POSTGRES_KV_REPOSITORY = createServiceToken17("storage.kvRepository.postgres");
|
|
2764
3158
|
|
|
2765
|
-
class
|
|
3159
|
+
class PostgresKvStorage extends KvViaTabularStorage {
|
|
2766
3160
|
db;
|
|
2767
3161
|
dbName;
|
|
2768
3162
|
tabularRepository;
|
|
@@ -2770,14 +3164,14 @@ class PostgresKvRepository extends KvViaTabularRepository {
|
|
|
2770
3164
|
super(keySchema, valueSchema);
|
|
2771
3165
|
this.db = db;
|
|
2772
3166
|
this.dbName = dbName;
|
|
2773
|
-
this.tabularRepository = new
|
|
3167
|
+
this.tabularRepository = new PostgresTabularStorage(db, dbName, DefaultKeyValueSchema, DefaultKeyValueKey);
|
|
2774
3168
|
}
|
|
2775
3169
|
}
|
|
2776
|
-
// src/kv/
|
|
2777
|
-
import { createServiceToken as
|
|
2778
|
-
var SQLITE_KV_REPOSITORY =
|
|
3170
|
+
// src/kv/SqliteKvStorage.ts
|
|
3171
|
+
import { createServiceToken as createServiceToken18 } from "@workglow/util";
|
|
3172
|
+
var SQLITE_KV_REPOSITORY = createServiceToken18("storage.kvRepository.sqlite");
|
|
2779
3173
|
|
|
2780
|
-
class
|
|
3174
|
+
class SqliteKvStorage extends KvViaTabularStorage {
|
|
2781
3175
|
db;
|
|
2782
3176
|
dbName;
|
|
2783
3177
|
tabularRepository;
|
|
@@ -2785,14 +3179,14 @@ class SqliteKvRepository extends KvViaTabularRepository {
|
|
|
2785
3179
|
super(keySchema, valueSchema);
|
|
2786
3180
|
this.db = db;
|
|
2787
3181
|
this.dbName = dbName;
|
|
2788
|
-
this.tabularRepository = new
|
|
3182
|
+
this.tabularRepository = new SqliteTabularStorage(db, dbName, DefaultKeyValueSchema, DefaultKeyValueKey);
|
|
2789
3183
|
}
|
|
2790
3184
|
}
|
|
2791
|
-
// src/kv/
|
|
2792
|
-
import { createServiceToken as
|
|
2793
|
-
var SUPABASE_KV_REPOSITORY =
|
|
3185
|
+
// src/kv/SupabaseKvStorage.ts
|
|
3186
|
+
import { createServiceToken as createServiceToken19 } from "@workglow/util";
|
|
3187
|
+
var SUPABASE_KV_REPOSITORY = createServiceToken19("storage.kvRepository.supabase");
|
|
2794
3188
|
|
|
2795
|
-
class
|
|
3189
|
+
class SupabaseKvStorage extends KvViaTabularStorage {
|
|
2796
3190
|
client;
|
|
2797
3191
|
tableName;
|
|
2798
3192
|
tabularRepository;
|
|
@@ -2800,12 +3194,12 @@ class SupabaseKvRepository extends KvViaTabularRepository {
|
|
|
2800
3194
|
super(keySchema, valueSchema);
|
|
2801
3195
|
this.client = client;
|
|
2802
3196
|
this.tableName = tableName;
|
|
2803
|
-
this.tabularRepository = tabularRepository ?? new
|
|
3197
|
+
this.tabularRepository = tabularRepository ?? new SupabaseTabularStorage(client, tableName, DefaultKeyValueSchema, DefaultKeyValueKey);
|
|
2804
3198
|
}
|
|
2805
3199
|
}
|
|
2806
3200
|
// src/queue/PostgresQueueStorage.ts
|
|
2807
|
-
import { createServiceToken as
|
|
2808
|
-
var POSTGRES_QUEUE_STORAGE =
|
|
3201
|
+
import { createServiceToken as createServiceToken20, makeFingerprint as makeFingerprint6, uuid4 as uuid45 } from "@workglow/util";
|
|
3202
|
+
var POSTGRES_QUEUE_STORAGE = createServiceToken20("jobqueue.storage.postgres");
|
|
2809
3203
|
|
|
2810
3204
|
class PostgresQueueStorage {
|
|
2811
3205
|
db;
|
|
@@ -2902,7 +3296,7 @@ class PostgresQueueStorage {
|
|
|
2902
3296
|
async add(job) {
|
|
2903
3297
|
const now = new Date().toISOString();
|
|
2904
3298
|
job.queue = this.queueName;
|
|
2905
|
-
job.job_run_id = job.job_run_id ??
|
|
3299
|
+
job.job_run_id = job.job_run_id ?? uuid45();
|
|
2906
3300
|
job.fingerprint = await makeFingerprint6(job.input);
|
|
2907
3301
|
job.status = JobStatus.PENDING;
|
|
2908
3302
|
job.progress = 0;
|
|
@@ -3134,8 +3528,8 @@ class PostgresQueueStorage {
|
|
|
3134
3528
|
}
|
|
3135
3529
|
}
|
|
3136
3530
|
// src/queue/SqliteQueueStorage.ts
|
|
3137
|
-
import { createServiceToken as
|
|
3138
|
-
var SQLITE_QUEUE_STORAGE =
|
|
3531
|
+
import { createServiceToken as createServiceToken21, makeFingerprint as makeFingerprint7, sleep as sleep4, uuid4 as uuid46 } from "@workglow/util";
|
|
3532
|
+
var SQLITE_QUEUE_STORAGE = createServiceToken21("jobqueue.storage.sqlite");
|
|
3139
3533
|
|
|
3140
3534
|
class SqliteQueueStorage {
|
|
3141
3535
|
db;
|
|
@@ -3217,7 +3611,7 @@ class SqliteQueueStorage {
|
|
|
3217
3611
|
}
|
|
3218
3612
|
async add(job) {
|
|
3219
3613
|
const now = new Date().toISOString();
|
|
3220
|
-
job.job_run_id = job.job_run_id ??
|
|
3614
|
+
job.job_run_id = job.job_run_id ?? uuid46();
|
|
3221
3615
|
job.queue = this.queueName;
|
|
3222
3616
|
job.fingerprint = await makeFingerprint7(job.input);
|
|
3223
3617
|
job.status = JobStatus.PENDING;
|
|
@@ -3470,8 +3864,8 @@ class SqliteQueueStorage {
|
|
|
3470
3864
|
}
|
|
3471
3865
|
}
|
|
3472
3866
|
// src/queue/SupabaseQueueStorage.ts
|
|
3473
|
-
import { createServiceToken as
|
|
3474
|
-
var SUPABASE_QUEUE_STORAGE =
|
|
3867
|
+
import { createServiceToken as createServiceToken22, makeFingerprint as makeFingerprint8, uuid4 as uuid47 } from "@workglow/util";
|
|
3868
|
+
var SUPABASE_QUEUE_STORAGE = createServiceToken22("jobqueue.storage.supabase");
|
|
3475
3869
|
|
|
3476
3870
|
class SupabaseQueueStorage {
|
|
3477
3871
|
client;
|
|
@@ -3587,7 +3981,7 @@ class SupabaseQueueStorage {
|
|
|
3587
3981
|
async add(job) {
|
|
3588
3982
|
const now = new Date().toISOString();
|
|
3589
3983
|
job.queue = this.queueName;
|
|
3590
|
-
job.job_run_id = job.job_run_id ??
|
|
3984
|
+
job.job_run_id = job.job_run_id ?? uuid47();
|
|
3591
3985
|
job.fingerprint = await makeFingerprint8(job.input);
|
|
3592
3986
|
job.status = JobStatus.PENDING;
|
|
3593
3987
|
job.progress = 0;
|
|
@@ -3969,9 +4363,9 @@ class SupabaseQueueStorage {
|
|
|
3969
4363
|
return manager.subscribe(callback, { intervalMs });
|
|
3970
4364
|
}
|
|
3971
4365
|
}
|
|
3972
|
-
// src/limiter/PostgresRateLimiterStorage.ts
|
|
3973
|
-
import { createServiceToken as
|
|
3974
|
-
var POSTGRES_RATE_LIMITER_STORAGE =
|
|
4366
|
+
// src/queue-limiter/PostgresRateLimiterStorage.ts
|
|
4367
|
+
import { createServiceToken as createServiceToken23 } from "@workglow/util";
|
|
4368
|
+
var POSTGRES_RATE_LIMITER_STORAGE = createServiceToken23("ratelimiter.storage.postgres");
|
|
3975
4369
|
|
|
3976
4370
|
class PostgresRateLimiterStorage {
|
|
3977
4371
|
db;
|
|
@@ -4107,9 +4501,9 @@ class PostgresRateLimiterStorage {
|
|
|
4107
4501
|
await this.db.query(`DELETE FROM ${this.nextAvailableTableName} WHERE queue_name = $1${prefixConditions}`, [queueName, ...prefixParams]);
|
|
4108
4502
|
}
|
|
4109
4503
|
}
|
|
4110
|
-
// src/limiter/SqliteRateLimiterStorage.ts
|
|
4111
|
-
import { createServiceToken as
|
|
4112
|
-
var SQLITE_RATE_LIMITER_STORAGE =
|
|
4504
|
+
// src/queue-limiter/SqliteRateLimiterStorage.ts
|
|
4505
|
+
import { createServiceToken as createServiceToken24, sleep as sleep5, toSQLiteTimestamp } from "@workglow/util";
|
|
4506
|
+
var SQLITE_RATE_LIMITER_STORAGE = createServiceToken24("ratelimiter.storage.sqlite");
|
|
4113
4507
|
|
|
4114
4508
|
class SqliteRateLimiterStorage {
|
|
4115
4509
|
db;
|
|
@@ -4246,9 +4640,9 @@ class SqliteRateLimiterStorage {
|
|
|
4246
4640
|
this.db.prepare(`DELETE FROM ${this.nextAvailableTableName} WHERE queue_name = ?${prefixConditions}`).run(queueName, ...prefixParams);
|
|
4247
4641
|
}
|
|
4248
4642
|
}
|
|
4249
|
-
// src/limiter/SupabaseRateLimiterStorage.ts
|
|
4250
|
-
import { createServiceToken as
|
|
4251
|
-
var SUPABASE_RATE_LIMITER_STORAGE =
|
|
4643
|
+
// src/queue-limiter/SupabaseRateLimiterStorage.ts
|
|
4644
|
+
import { createServiceToken as createServiceToken25 } from "@workglow/util";
|
|
4645
|
+
var SUPABASE_RATE_LIMITER_STORAGE = createServiceToken25("ratelimiter.storage.supabase");
|
|
4252
4646
|
|
|
4253
4647
|
class SupabaseRateLimiterStorage {
|
|
4254
4648
|
client;
|
|
@@ -4399,13 +4793,311 @@ class SupabaseRateLimiterStorage {
|
|
|
4399
4793
|
throw nextError;
|
|
4400
4794
|
}
|
|
4401
4795
|
}
|
|
4402
|
-
// src/
|
|
4403
|
-
import {
|
|
4796
|
+
// src/vector/PostgresVectorStorage.ts
|
|
4797
|
+
import { cosineSimilarity as cosineSimilarity2 } from "@workglow/util";
|
|
4798
|
+
class PostgresVectorStorage extends PostgresTabularStorage {
|
|
4799
|
+
vectorDimensions;
|
|
4800
|
+
VectorType;
|
|
4801
|
+
vectorPropertyName;
|
|
4802
|
+
metadataPropertyName;
|
|
4803
|
+
constructor(db, table, schema, primaryKeyNames, indexes = [], dimensions, VectorType = Float32Array) {
|
|
4804
|
+
super(db, table, schema, primaryKeyNames, indexes);
|
|
4805
|
+
this.vectorDimensions = dimensions;
|
|
4806
|
+
this.VectorType = VectorType;
|
|
4807
|
+
const vectorProp = getVectorProperty(schema);
|
|
4808
|
+
if (!vectorProp) {
|
|
4809
|
+
throw new Error("Schema must have a property with type array and format TypedArray");
|
|
4810
|
+
}
|
|
4811
|
+
this.vectorPropertyName = vectorProp;
|
|
4812
|
+
this.metadataPropertyName = getMetadataProperty(schema);
|
|
4813
|
+
}
|
|
4814
|
+
getVectorDimensions() {
|
|
4815
|
+
return this.vectorDimensions;
|
|
4816
|
+
}
|
|
4817
|
+
async similaritySearch(query, options = {}) {
|
|
4818
|
+
const { topK = 10, filter, scoreThreshold = 0 } = options;
|
|
4819
|
+
try {
|
|
4820
|
+
const queryVector = `[${Array.from(query).join(",")}]`;
|
|
4821
|
+
const vectorCol = String(this.vectorPropertyName);
|
|
4822
|
+
const metadataCol = this.metadataPropertyName ? String(this.metadataPropertyName) : null;
|
|
4823
|
+
let sql = `
|
|
4824
|
+
SELECT
|
|
4825
|
+
*,
|
|
4826
|
+
1 - (${vectorCol} <=> $1::vector) as score
|
|
4827
|
+
FROM "${this.table}"
|
|
4828
|
+
`;
|
|
4829
|
+
const params = [queryVector];
|
|
4830
|
+
let paramIndex = 2;
|
|
4831
|
+
if (filter && Object.keys(filter).length > 0 && metadataCol) {
|
|
4832
|
+
const conditions = [];
|
|
4833
|
+
for (const [key, value] of Object.entries(filter)) {
|
|
4834
|
+
conditions.push(`${metadataCol}->>'${key}' = $${paramIndex}`);
|
|
4835
|
+
params.push(String(value));
|
|
4836
|
+
paramIndex++;
|
|
4837
|
+
}
|
|
4838
|
+
sql += ` WHERE ${conditions.join(" AND ")}`;
|
|
4839
|
+
}
|
|
4840
|
+
if (scoreThreshold > 0) {
|
|
4841
|
+
sql += filter ? " AND" : " WHERE";
|
|
4842
|
+
sql += ` (1 - (${vectorCol} <=> $1::vector)) >= $${paramIndex}`;
|
|
4843
|
+
params.push(scoreThreshold);
|
|
4844
|
+
paramIndex++;
|
|
4845
|
+
}
|
|
4846
|
+
sql += ` ORDER BY ${vectorCol} <=> $1::vector LIMIT $${paramIndex}`;
|
|
4847
|
+
params.push(topK);
|
|
4848
|
+
const result = await this.db.query(sql, params);
|
|
4849
|
+
const results = [];
|
|
4850
|
+
for (const row of result.rows) {
|
|
4851
|
+
const vectorResult = await this.db.query(`SELECT ${vectorCol}::text FROM "${this.table}" WHERE ${this.getPrimaryKeyWhereClause(row)}`, this.getPrimaryKeyValues(row));
|
|
4852
|
+
const vectorStr = vectorResult.rows[0]?.[vectorCol] || "[]";
|
|
4853
|
+
const vectorArray = JSON.parse(vectorStr);
|
|
4854
|
+
results.push({
|
|
4855
|
+
...row,
|
|
4856
|
+
[this.vectorPropertyName]: new this.VectorType(vectorArray),
|
|
4857
|
+
score: parseFloat(row.score)
|
|
4858
|
+
});
|
|
4859
|
+
}
|
|
4860
|
+
return results;
|
|
4861
|
+
} catch (error) {
|
|
4862
|
+
console.warn("pgvector query failed, falling back to in-memory search:", error);
|
|
4863
|
+
return this.searchFallback(query, options);
|
|
4864
|
+
}
|
|
4865
|
+
}
|
|
4866
|
+
async hybridSearch(query, options) {
|
|
4867
|
+
const { topK = 10, filter, scoreThreshold = 0, textQuery, vectorWeight = 0.7 } = options;
|
|
4868
|
+
if (!textQuery || textQuery.trim().length === 0) {
|
|
4869
|
+
return this.similaritySearch(query, { topK, filter, scoreThreshold });
|
|
4870
|
+
}
|
|
4871
|
+
try {
|
|
4872
|
+
const queryVector = `[${Array.from(query).join(",")}]`;
|
|
4873
|
+
const tsQuery = textQuery.split(/\s+/).join(" & ");
|
|
4874
|
+
const vectorCol = String(this.vectorPropertyName);
|
|
4875
|
+
const metadataCol = this.metadataPropertyName ? String(this.metadataPropertyName) : null;
|
|
4876
|
+
let sql = `
|
|
4877
|
+
SELECT
|
|
4878
|
+
*,
|
|
4879
|
+
(
|
|
4880
|
+
$2 * (1 - (${vectorCol} <=> $1::vector)) +
|
|
4881
|
+
$3 * ts_rank(to_tsvector('english', ${metadataCol || "''"}::text), to_tsquery('english', $4))
|
|
4882
|
+
) as score
|
|
4883
|
+
FROM "${this.table}"
|
|
4884
|
+
`;
|
|
4885
|
+
const params = [queryVector, vectorWeight, 1 - vectorWeight, tsQuery];
|
|
4886
|
+
let paramIndex = 5;
|
|
4887
|
+
if (filter && Object.keys(filter).length > 0 && metadataCol) {
|
|
4888
|
+
const conditions = [];
|
|
4889
|
+
for (const [key, value] of Object.entries(filter)) {
|
|
4890
|
+
conditions.push(`${metadataCol}->>'${key}' = $${paramIndex}`);
|
|
4891
|
+
params.push(String(value));
|
|
4892
|
+
paramIndex++;
|
|
4893
|
+
}
|
|
4894
|
+
sql += ` WHERE ${conditions.join(" AND ")}`;
|
|
4895
|
+
}
|
|
4896
|
+
if (scoreThreshold > 0) {
|
|
4897
|
+
sql += filter ? " AND" : " WHERE";
|
|
4898
|
+
sql += ` (
|
|
4899
|
+
$2 * (1 - (${vectorCol} <=> $1::vector)) +
|
|
4900
|
+
$3 * ts_rank(to_tsvector('english', ${metadataCol || "''"}::text), to_tsquery('english', $4))
|
|
4901
|
+
) >= $${paramIndex}`;
|
|
4902
|
+
params.push(scoreThreshold);
|
|
4903
|
+
paramIndex++;
|
|
4904
|
+
}
|
|
4905
|
+
sql += ` ORDER BY score DESC LIMIT $${paramIndex}`;
|
|
4906
|
+
params.push(topK);
|
|
4907
|
+
const result = await this.db.query(sql, params);
|
|
4908
|
+
const results = [];
|
|
4909
|
+
for (const row of result.rows) {
|
|
4910
|
+
const vectorResult = await this.db.query(`SELECT ${vectorCol}::text FROM "${this.table}" WHERE ${this.getPrimaryKeyWhereClause(row)}`, this.getPrimaryKeyValues(row));
|
|
4911
|
+
const vectorStr = vectorResult.rows[0]?.[vectorCol] || "[]";
|
|
4912
|
+
const vectorArray = JSON.parse(vectorStr);
|
|
4913
|
+
results.push({
|
|
4914
|
+
...row,
|
|
4915
|
+
[this.vectorPropertyName]: new this.VectorType(vectorArray),
|
|
4916
|
+
score: parseFloat(row.score)
|
|
4917
|
+
});
|
|
4918
|
+
}
|
|
4919
|
+
return results;
|
|
4920
|
+
} catch (error) {
|
|
4921
|
+
console.warn("pgvector hybrid query failed, falling back to in-memory search:", error);
|
|
4922
|
+
return this.hybridSearchFallback(query, options);
|
|
4923
|
+
}
|
|
4924
|
+
}
|
|
4925
|
+
async searchFallback(query, options) {
|
|
4926
|
+
const { topK = 10, filter, scoreThreshold = 0 } = options;
|
|
4927
|
+
const allRows = await this.getAll() || [];
|
|
4928
|
+
const results = [];
|
|
4929
|
+
for (const row of allRows) {
|
|
4930
|
+
const vector = row[this.vectorPropertyName];
|
|
4931
|
+
const metadata = this.metadataPropertyName ? row[this.metadataPropertyName] : {};
|
|
4932
|
+
if (filter && !this.matchesFilter(metadata, filter)) {
|
|
4933
|
+
continue;
|
|
4934
|
+
}
|
|
4935
|
+
const score = cosineSimilarity2(query, vector);
|
|
4936
|
+
if (score >= scoreThreshold) {
|
|
4937
|
+
results.push({ ...row, score });
|
|
4938
|
+
}
|
|
4939
|
+
}
|
|
4940
|
+
results.sort((a, b) => b.score - a.score);
|
|
4941
|
+
const topResults = results.slice(0, topK);
|
|
4942
|
+
return topResults;
|
|
4943
|
+
}
|
|
4944
|
+
async hybridSearchFallback(query, options) {
|
|
4945
|
+
const { topK = 10, filter, scoreThreshold = 0, textQuery, vectorWeight = 0.7 } = options;
|
|
4946
|
+
const allRows = await this.getAll() || [];
|
|
4947
|
+
const results = [];
|
|
4948
|
+
const queryLower = textQuery.toLowerCase();
|
|
4949
|
+
const queryWords = queryLower.split(/\s+/).filter((w) => w.length > 0);
|
|
4950
|
+
for (const row of allRows) {
|
|
4951
|
+
const vector = row[this.vectorPropertyName];
|
|
4952
|
+
const metadata = this.metadataPropertyName ? row[this.metadataPropertyName] : {};
|
|
4953
|
+
if (filter && !this.matchesFilter(metadata, filter)) {
|
|
4954
|
+
continue;
|
|
4955
|
+
}
|
|
4956
|
+
const vectorScore = cosineSimilarity2(query, vector);
|
|
4957
|
+
const metadataText = JSON.stringify(metadata).toLowerCase();
|
|
4958
|
+
let textScore = 0;
|
|
4959
|
+
if (queryWords.length > 0) {
|
|
4960
|
+
let matches = 0;
|
|
4961
|
+
for (const word of queryWords) {
|
|
4962
|
+
if (metadataText.includes(word)) {
|
|
4963
|
+
matches++;
|
|
4964
|
+
}
|
|
4965
|
+
}
|
|
4966
|
+
textScore = matches / queryWords.length;
|
|
4967
|
+
}
|
|
4968
|
+
const combinedScore = vectorWeight * vectorScore + (1 - vectorWeight) * textScore;
|
|
4969
|
+
if (combinedScore >= scoreThreshold) {
|
|
4970
|
+
results.push({ ...row, score: combinedScore });
|
|
4971
|
+
}
|
|
4972
|
+
}
|
|
4973
|
+
results.sort((a, b) => b.score - a.score);
|
|
4974
|
+
const topResults = results.slice(0, topK);
|
|
4975
|
+
return topResults;
|
|
4976
|
+
}
|
|
4977
|
+
getPrimaryKeyWhereClause(row) {
|
|
4978
|
+
const conditions = this.primaryKeyNames.map((key, idx) => `${String(key)} = $${idx + 1}`);
|
|
4979
|
+
return conditions.join(" AND ");
|
|
4980
|
+
}
|
|
4981
|
+
getPrimaryKeyValues(row) {
|
|
4982
|
+
return this.primaryKeyNames.map((key) => row[key]);
|
|
4983
|
+
}
|
|
4984
|
+
matchesFilter(metadata, filter) {
|
|
4985
|
+
for (const [key, value] of Object.entries(filter)) {
|
|
4986
|
+
if (metadata[key] !== value) {
|
|
4987
|
+
return false;
|
|
4988
|
+
}
|
|
4989
|
+
}
|
|
4990
|
+
return true;
|
|
4991
|
+
}
|
|
4992
|
+
}
|
|
4993
|
+
// src/vector/SqliteVectorStorage.ts
|
|
4994
|
+
import { cosineSimilarity as cosineSimilarity3 } from "@workglow/util";
|
|
4995
|
+
function matchesFilter2(metadata, filter) {
|
|
4996
|
+
for (const [key, value] of Object.entries(filter)) {
|
|
4997
|
+
if (metadata[key] !== value) {
|
|
4998
|
+
return false;
|
|
4999
|
+
}
|
|
5000
|
+
}
|
|
5001
|
+
return true;
|
|
5002
|
+
}
|
|
4404
5003
|
|
|
4405
|
-
|
|
5004
|
+
class SqliteVectorStorage extends SqliteTabularStorage {
|
|
5005
|
+
vectorDimensions;
|
|
5006
|
+
VectorType;
|
|
5007
|
+
vectorPropertyName;
|
|
5008
|
+
metadataPropertyName;
|
|
5009
|
+
constructor(dbOrPath, table = "vectors", schema, primaryKeyNames, indexes = [], dimensions, VectorType = Float32Array) {
|
|
5010
|
+
super(dbOrPath, table, schema, primaryKeyNames, indexes);
|
|
5011
|
+
this.vectorDimensions = dimensions;
|
|
5012
|
+
this.VectorType = VectorType;
|
|
5013
|
+
const vectorProp = getVectorProperty(schema);
|
|
5014
|
+
if (!vectorProp) {
|
|
5015
|
+
throw new Error("Schema must have a property with type array and format TypedArray");
|
|
5016
|
+
}
|
|
5017
|
+
this.vectorPropertyName = vectorProp;
|
|
5018
|
+
this.metadataPropertyName = getMetadataProperty(schema);
|
|
5019
|
+
}
|
|
5020
|
+
getVectorDimensions() {
|
|
5021
|
+
return this.vectorDimensions;
|
|
5022
|
+
}
|
|
5023
|
+
deserializeVector(vectorJson) {
|
|
5024
|
+
const array = JSON.parse(vectorJson);
|
|
5025
|
+
return new this.VectorType(array);
|
|
5026
|
+
}
|
|
5027
|
+
async similaritySearch(query, options = {}) {
|
|
5028
|
+
const { topK = 10, filter, scoreThreshold = 0 } = options;
|
|
5029
|
+
const results = [];
|
|
5030
|
+
const allEntities = await this.getAll() || [];
|
|
5031
|
+
for (const entity of allEntities) {
|
|
5032
|
+
const vectorRaw = entity[this.vectorPropertyName];
|
|
5033
|
+
const vector = this.deserializeVector(vectorRaw);
|
|
5034
|
+
const metadata = this.metadataPropertyName ? entity[this.metadataPropertyName] : {};
|
|
5035
|
+
if (filter && !matchesFilter2(metadata, filter)) {
|
|
5036
|
+
continue;
|
|
5037
|
+
}
|
|
5038
|
+
const score = cosineSimilarity3(query, vector);
|
|
5039
|
+
if (score < scoreThreshold) {
|
|
5040
|
+
continue;
|
|
5041
|
+
}
|
|
5042
|
+
results.push({
|
|
5043
|
+
...entity,
|
|
5044
|
+
score
|
|
5045
|
+
});
|
|
5046
|
+
}
|
|
5047
|
+
results.sort((a, b) => b.score - a.score);
|
|
5048
|
+
const topResults = results.slice(0, topK);
|
|
5049
|
+
return topResults;
|
|
5050
|
+
}
|
|
5051
|
+
async hybridSearch(query, options) {
|
|
5052
|
+
const { topK = 10, filter, scoreThreshold = 0, textQuery, vectorWeight = 0.7 } = options;
|
|
5053
|
+
if (!textQuery || textQuery.trim().length === 0) {
|
|
5054
|
+
return this.similaritySearch(query, { topK, filter, scoreThreshold });
|
|
5055
|
+
}
|
|
5056
|
+
const results = [];
|
|
5057
|
+
const allEntities = await this.getAll() || [];
|
|
5058
|
+
const queryLower = textQuery.toLowerCase();
|
|
5059
|
+
const queryWords = queryLower.split(/\s+/).filter((w) => w.length > 0);
|
|
5060
|
+
for (const entity of allEntities) {
|
|
5061
|
+
const vectorRaw = entity[this.vectorPropertyName];
|
|
5062
|
+
const vector = this.deserializeVector(vectorRaw);
|
|
5063
|
+
const metadata = this.metadataPropertyName ? entity[this.metadataPropertyName] : {};
|
|
5064
|
+
if (filter && !matchesFilter2(metadata, filter)) {
|
|
5065
|
+
continue;
|
|
5066
|
+
}
|
|
5067
|
+
const vectorScore = cosineSimilarity3(query, vector);
|
|
5068
|
+
const metadataText = JSON.stringify(metadata).toLowerCase();
|
|
5069
|
+
let textScore = 0;
|
|
5070
|
+
if (queryWords.length > 0) {
|
|
5071
|
+
let matches = 0;
|
|
5072
|
+
for (const word of queryWords) {
|
|
5073
|
+
if (metadataText.includes(word)) {
|
|
5074
|
+
matches++;
|
|
5075
|
+
}
|
|
5076
|
+
}
|
|
5077
|
+
textScore = matches / queryWords.length;
|
|
5078
|
+
}
|
|
5079
|
+
const combinedScore = vectorWeight * vectorScore + (1 - vectorWeight) * textScore;
|
|
5080
|
+
if (combinedScore < scoreThreshold) {
|
|
5081
|
+
continue;
|
|
5082
|
+
}
|
|
5083
|
+
results.push({
|
|
5084
|
+
...entity,
|
|
5085
|
+
score: combinedScore
|
|
5086
|
+
});
|
|
5087
|
+
}
|
|
5088
|
+
results.sort((a, b) => b.score - a.score);
|
|
5089
|
+
const topResults = results.slice(0, topK);
|
|
5090
|
+
return topResults;
|
|
5091
|
+
}
|
|
5092
|
+
}
|
|
5093
|
+
// src/kv/IndexedDbKvStorage.ts
|
|
5094
|
+
import { createServiceToken as createServiceToken27 } from "@workglow/util";
|
|
5095
|
+
|
|
5096
|
+
// src/tabular/IndexedDbTabularStorage.ts
|
|
4406
5097
|
import {
|
|
4407
|
-
createServiceToken as
|
|
4408
|
-
makeFingerprint as makeFingerprint9
|
|
5098
|
+
createServiceToken as createServiceToken26,
|
|
5099
|
+
makeFingerprint as makeFingerprint9,
|
|
5100
|
+
uuid4 as uuid48
|
|
4409
5101
|
} from "@workglow/util";
|
|
4410
5102
|
|
|
4411
5103
|
// src/util/IndexedDbTable.ts
|
|
@@ -4554,7 +5246,7 @@ async function performIncrementalMigration(db, tableName, diff, options = {}) {
|
|
|
4554
5246
|
options.onMigrationProgress?.(`Migration complete`, 1);
|
|
4555
5247
|
});
|
|
4556
5248
|
}
|
|
4557
|
-
async function performDestructiveMigration(db, tableName, primaryKey, expectedIndexes, options = {}) {
|
|
5249
|
+
async function performDestructiveMigration(db, tableName, primaryKey, expectedIndexes, options = {}, autoIncrement = false) {
|
|
4558
5250
|
if (!options.allowDestructiveMigration) {
|
|
4559
5251
|
throw new Error(`Destructive migration required for ${tableName} but not allowed. ` + `Primary key has changed. Set allowDestructiveMigration=true to proceed with data loss, ` + `or provide a dataTransformer to migrate data.`);
|
|
4560
5252
|
}
|
|
@@ -4599,7 +5291,7 @@ async function performDestructiveMigration(db, tableName, primaryKey, expectedIn
|
|
|
4599
5291
|
if (db2.objectStoreNames.contains(tableName)) {
|
|
4600
5292
|
db2.deleteObjectStore(tableName);
|
|
4601
5293
|
}
|
|
4602
|
-
const store = db2.createObjectStore(tableName, { keyPath: primaryKey });
|
|
5294
|
+
const store = db2.createObjectStore(tableName, { keyPath: primaryKey, autoIncrement });
|
|
4603
5295
|
for (const idx of expectedIndexes) {
|
|
4604
5296
|
store.createIndex(idx.name, idx.keyPath, idx.options);
|
|
4605
5297
|
}
|
|
@@ -4617,7 +5309,7 @@ async function performDestructiveMigration(db, tableName, primaryKey, expectedIn
|
|
|
4617
5309
|
options.onMigrationProgress?.(`Destructive migration complete`, 1);
|
|
4618
5310
|
return newDb;
|
|
4619
5311
|
}
|
|
4620
|
-
async function createNewDatabase(tableName, primaryKey, expectedIndexes, options = {}) {
|
|
5312
|
+
async function createNewDatabase(tableName, primaryKey, expectedIndexes, options = {}, autoIncrement = false) {
|
|
4621
5313
|
options.onMigrationProgress?.(`Creating new database: ${tableName}`, 0);
|
|
4622
5314
|
try {
|
|
4623
5315
|
await deleteIndexedDbTable(tableName);
|
|
@@ -4629,7 +5321,7 @@ async function createNewDatabase(tableName, primaryKey, expectedIndexes, options
|
|
|
4629
5321
|
if (!db2.objectStoreNames.contains(METADATA_STORE_NAME)) {
|
|
4630
5322
|
db2.createObjectStore(METADATA_STORE_NAME, { keyPath: "tableName" });
|
|
4631
5323
|
}
|
|
4632
|
-
const store = db2.createObjectStore(tableName, { keyPath: primaryKey });
|
|
5324
|
+
const store = db2.createObjectStore(tableName, { keyPath: primaryKey, autoIncrement });
|
|
4633
5325
|
for (const idx of expectedIndexes) {
|
|
4634
5326
|
store.createIndex(idx.name, idx.keyPath, idx.options);
|
|
4635
5327
|
}
|
|
@@ -4645,7 +5337,7 @@ async function createNewDatabase(tableName, primaryKey, expectedIndexes, options
|
|
|
4645
5337
|
options.onMigrationProgress?.(`Database created successfully`, 1);
|
|
4646
5338
|
return db;
|
|
4647
5339
|
}
|
|
4648
|
-
async function ensureIndexedDbTable(tableName, primaryKey, expectedIndexes = [], options = {}) {
|
|
5340
|
+
async function ensureIndexedDbTable(tableName, primaryKey, expectedIndexes = [], options = {}, autoIncrement = false) {
|
|
4649
5341
|
try {
|
|
4650
5342
|
let db;
|
|
4651
5343
|
let wasJustCreated = false;
|
|
@@ -4657,7 +5349,7 @@ async function ensureIndexedDbTable(tableName, primaryKey, expectedIndexes = [],
|
|
|
4657
5349
|
}
|
|
4658
5350
|
} catch (err) {
|
|
4659
5351
|
options.onMigrationProgress?.(`Database ${tableName} does not exist or has version conflict, creating...`, 0);
|
|
4660
|
-
return await createNewDatabase(tableName, primaryKey, expectedIndexes, options);
|
|
5352
|
+
return await createNewDatabase(tableName, primaryKey, expectedIndexes, options, autoIncrement);
|
|
4661
5353
|
}
|
|
4662
5354
|
if (wasJustCreated) {
|
|
4663
5355
|
options.onMigrationProgress?.(`Creating new database: ${tableName}`, 0);
|
|
@@ -4670,7 +5362,7 @@ async function ensureIndexedDbTable(tableName, primaryKey, expectedIndexes = [],
|
|
|
4670
5362
|
if (!db2.objectStoreNames.contains(METADATA_STORE_NAME)) {
|
|
4671
5363
|
db2.createObjectStore(METADATA_STORE_NAME, { keyPath: "tableName" });
|
|
4672
5364
|
}
|
|
4673
|
-
const store2 = db2.createObjectStore(tableName, { keyPath: primaryKey });
|
|
5365
|
+
const store2 = db2.createObjectStore(tableName, { keyPath: primaryKey, autoIncrement });
|
|
4674
5366
|
for (const idx of expectedIndexes) {
|
|
4675
5367
|
store2.createIndex(idx.name, idx.keyPath, idx.options);
|
|
4676
5368
|
}
|
|
@@ -4700,7 +5392,7 @@ async function ensureIndexedDbTable(tableName, primaryKey, expectedIndexes = [],
|
|
|
4700
5392
|
if (!db.objectStoreNames.contains(tableName)) {
|
|
4701
5393
|
options.onMigrationProgress?.(`Object store ${tableName} does not exist, creating...`, 0);
|
|
4702
5394
|
db.close();
|
|
4703
|
-
return await createNewDatabase(tableName, primaryKey, expectedIndexes, options);
|
|
5395
|
+
return await createNewDatabase(tableName, primaryKey, expectedIndexes, options, autoIncrement);
|
|
4704
5396
|
}
|
|
4705
5397
|
const transaction = db.transaction(tableName, "readonly");
|
|
4706
5398
|
const store = transaction.objectStore(tableName);
|
|
@@ -4723,7 +5415,7 @@ async function ensureIndexedDbTable(tableName, primaryKey, expectedIndexes = [],
|
|
|
4723
5415
|
}
|
|
4724
5416
|
if (diff.needsObjectStoreRecreation) {
|
|
4725
5417
|
options.onMigrationProgress?.(`Schema change requires object store recreation for ${tableName}`, 0);
|
|
4726
|
-
db = await performDestructiveMigration(db, tableName, primaryKey, expectedIndexes, options);
|
|
5418
|
+
db = await performDestructiveMigration(db, tableName, primaryKey, expectedIndexes, options, autoIncrement);
|
|
4727
5419
|
} else {
|
|
4728
5420
|
options.onMigrationProgress?.(`Performing incremental migration for ${tableName}`, 0);
|
|
4729
5421
|
db = await performIncrementalMigration(db, tableName, diff, options);
|
|
@@ -4745,18 +5437,18 @@ async function dropIndexedDbTable(tableName) {
|
|
|
4745
5437
|
return deleteIndexedDbTable(tableName);
|
|
4746
5438
|
}
|
|
4747
5439
|
|
|
4748
|
-
// src/tabular/
|
|
4749
|
-
var IDB_TABULAR_REPOSITORY =
|
|
5440
|
+
// src/tabular/IndexedDbTabularStorage.ts
|
|
5441
|
+
var IDB_TABULAR_REPOSITORY = createServiceToken26("storage.tabularRepository.indexedDb");
|
|
4750
5442
|
|
|
4751
|
-
class
|
|
5443
|
+
class IndexedDbTabularStorage extends BaseTabularStorage {
|
|
4752
5444
|
table;
|
|
4753
5445
|
db;
|
|
4754
5446
|
setupPromise = null;
|
|
4755
5447
|
migrationOptions;
|
|
4756
5448
|
hybridManager = null;
|
|
4757
5449
|
hybridOptions;
|
|
4758
|
-
constructor(table = "tabular_store", schema, primaryKeyNames, indexes = [], migrationOptions = {}) {
|
|
4759
|
-
super(schema, primaryKeyNames, indexes);
|
|
5450
|
+
constructor(table = "tabular_store", schema, primaryKeyNames, indexes = [], migrationOptions = {}, clientProvidedKeys = "if-missing") {
|
|
5451
|
+
super(schema, primaryKeyNames, indexes, clientProvidedKeys);
|
|
4760
5452
|
this.table = table;
|
|
4761
5453
|
this.migrationOptions = migrationOptions;
|
|
4762
5454
|
this.hybridOptions = {
|
|
@@ -4803,21 +5495,64 @@ class IndexedDbTabularRepository extends TabularRepository {
|
|
|
4803
5495
|
});
|
|
4804
5496
|
}
|
|
4805
5497
|
const primaryKey = pkColumns.length === 1 ? pkColumns[0] : pkColumns;
|
|
4806
|
-
|
|
5498
|
+
const useAutoIncrement = this.hasAutoGeneratedKey() && this.autoGeneratedKeyStrategy === "autoincrement" && pkColumns.length === 1;
|
|
5499
|
+
return await ensureIndexedDbTable(this.table, primaryKey, expectedIndexes, this.migrationOptions, useAutoIncrement);
|
|
5500
|
+
}
|
|
5501
|
+
generateKeyValue(columnName, strategy) {
|
|
5502
|
+
if (strategy === "uuid") {
|
|
5503
|
+
return uuid48();
|
|
5504
|
+
}
|
|
5505
|
+
throw new Error(`IndexedDB autoincrement keys are generated by the database, not client-side. Column: ${columnName}`);
|
|
4807
5506
|
}
|
|
4808
5507
|
async put(record) {
|
|
4809
5508
|
const db = await this.getDb();
|
|
4810
|
-
|
|
5509
|
+
let recordToStore = record;
|
|
5510
|
+
if (this.hasAutoGeneratedKey() && this.autoGeneratedKeyName) {
|
|
5511
|
+
const keyName = String(this.autoGeneratedKeyName);
|
|
5512
|
+
const clientProvidedValue = record[keyName];
|
|
5513
|
+
const hasClientValue = clientProvidedValue !== undefined && clientProvidedValue !== null;
|
|
5514
|
+
if (this.autoGeneratedKeyStrategy === "uuid") {
|
|
5515
|
+
let shouldGenerate = false;
|
|
5516
|
+
if (this.clientProvidedKeys === "never") {
|
|
5517
|
+
shouldGenerate = true;
|
|
5518
|
+
} else if (this.clientProvidedKeys === "always") {
|
|
5519
|
+
if (!hasClientValue) {
|
|
5520
|
+
throw new Error(`Auto-generated key "${keyName}" is required when clientProvidedKeys is "always"`);
|
|
5521
|
+
}
|
|
5522
|
+
shouldGenerate = false;
|
|
5523
|
+
} else {
|
|
5524
|
+
shouldGenerate = !hasClientValue;
|
|
5525
|
+
}
|
|
5526
|
+
if (shouldGenerate) {
|
|
5527
|
+
const generatedValue = this.generateKeyValue(keyName, "uuid");
|
|
5528
|
+
recordToStore = { ...record, [keyName]: generatedValue };
|
|
5529
|
+
}
|
|
5530
|
+
} else if (this.autoGeneratedKeyStrategy === "autoincrement") {
|
|
5531
|
+
if (this.clientProvidedKeys === "always" && !hasClientValue) {
|
|
5532
|
+
throw new Error(`Auto-generated key "${keyName}" is required when clientProvidedKeys is "always"`);
|
|
5533
|
+
}
|
|
5534
|
+
if (this.clientProvidedKeys === "never") {
|
|
5535
|
+
const { [keyName]: _, ...rest } = record;
|
|
5536
|
+
recordToStore = rest;
|
|
5537
|
+
}
|
|
5538
|
+
}
|
|
5539
|
+
}
|
|
4811
5540
|
return new Promise((resolve, reject) => {
|
|
4812
5541
|
const transaction = db.transaction(this.table, "readwrite");
|
|
4813
5542
|
const store = transaction.objectStore(this.table);
|
|
4814
|
-
const request = store.put(
|
|
5543
|
+
const request = store.put(recordToStore);
|
|
4815
5544
|
request.onerror = () => {
|
|
4816
5545
|
reject(request.error);
|
|
4817
5546
|
};
|
|
4818
5547
|
request.onsuccess = () => {
|
|
4819
|
-
this.
|
|
4820
|
-
|
|
5548
|
+
if (this.hasAutoGeneratedKey() && this.autoGeneratedKeyName && this.autoGeneratedKeyStrategy === "autoincrement") {
|
|
5549
|
+
const keyName = String(this.autoGeneratedKeyName);
|
|
5550
|
+
if (recordToStore[keyName] === undefined) {
|
|
5551
|
+
recordToStore = { ...recordToStore, [keyName]: request.result };
|
|
5552
|
+
}
|
|
5553
|
+
}
|
|
5554
|
+
this.events.emit("put", recordToStore);
|
|
5555
|
+
resolve(recordToStore);
|
|
4821
5556
|
};
|
|
4822
5557
|
transaction.oncomplete = () => {
|
|
4823
5558
|
this.hybridManager?.notifyLocalChange();
|
|
@@ -4825,38 +5560,7 @@ class IndexedDbTabularRepository extends TabularRepository {
|
|
|
4825
5560
|
});
|
|
4826
5561
|
}
|
|
4827
5562
|
async putBulk(records) {
|
|
4828
|
-
|
|
4829
|
-
return new Promise((resolve, reject) => {
|
|
4830
|
-
const transaction = db.transaction(this.table, "readwrite");
|
|
4831
|
-
const store = transaction.objectStore(this.table);
|
|
4832
|
-
let completed = 0;
|
|
4833
|
-
let hasError = false;
|
|
4834
|
-
transaction.onerror = () => {
|
|
4835
|
-
if (!hasError) {
|
|
4836
|
-
hasError = true;
|
|
4837
|
-
reject(transaction.error);
|
|
4838
|
-
}
|
|
4839
|
-
};
|
|
4840
|
-
transaction.oncomplete = () => {
|
|
4841
|
-
if (!hasError) {
|
|
4842
|
-
this.hybridManager?.notifyLocalChange();
|
|
4843
|
-
resolve(records);
|
|
4844
|
-
}
|
|
4845
|
-
};
|
|
4846
|
-
for (const record of records) {
|
|
4847
|
-
const request = store.put(record);
|
|
4848
|
-
request.onsuccess = () => {
|
|
4849
|
-
this.events.emit("put", record);
|
|
4850
|
-
completed++;
|
|
4851
|
-
};
|
|
4852
|
-
request.onerror = () => {
|
|
4853
|
-
if (!hasError) {
|
|
4854
|
-
hasError = true;
|
|
4855
|
-
reject(request.error);
|
|
4856
|
-
}
|
|
4857
|
-
};
|
|
4858
|
-
}
|
|
4859
|
-
});
|
|
5563
|
+
return await Promise.all(records.map((record) => this.put(record)));
|
|
4860
5564
|
}
|
|
4861
5565
|
getPrimaryKeyAsOrderedArray(key) {
|
|
4862
5566
|
return super.getPrimaryKeyAsOrderedArray(key).map((value) => typeof value === "bigint" ? value.toString() : value);
|
|
@@ -5149,21 +5853,21 @@ class IndexedDbTabularRepository extends TabularRepository {
|
|
|
5149
5853
|
}
|
|
5150
5854
|
}
|
|
5151
5855
|
|
|
5152
|
-
// src/kv/
|
|
5153
|
-
var IDB_KV_REPOSITORY =
|
|
5856
|
+
// src/kv/IndexedDbKvStorage.ts
|
|
5857
|
+
var IDB_KV_REPOSITORY = createServiceToken27("storage.kvRepository.indexedDb");
|
|
5154
5858
|
|
|
5155
|
-
class
|
|
5859
|
+
class IndexedDbKvStorage extends KvViaTabularStorage {
|
|
5156
5860
|
dbName;
|
|
5157
5861
|
tabularRepository;
|
|
5158
5862
|
constructor(dbName, keySchema = { type: "string" }, valueSchema = {}) {
|
|
5159
5863
|
super(keySchema, valueSchema);
|
|
5160
5864
|
this.dbName = dbName;
|
|
5161
|
-
this.tabularRepository = new
|
|
5865
|
+
this.tabularRepository = new IndexedDbTabularStorage(dbName, DefaultKeyValueSchema, DefaultKeyValueKey);
|
|
5162
5866
|
}
|
|
5163
5867
|
}
|
|
5164
|
-
// src/limiter/IndexedDbRateLimiterStorage.ts
|
|
5165
|
-
import { createServiceToken as
|
|
5166
|
-
var INDEXED_DB_RATE_LIMITER_STORAGE =
|
|
5868
|
+
// src/queue-limiter/IndexedDbRateLimiterStorage.ts
|
|
5869
|
+
import { createServiceToken as createServiceToken28 } from "@workglow/util";
|
|
5870
|
+
var INDEXED_DB_RATE_LIMITER_STORAGE = createServiceToken28("ratelimiter.storage.indexedDb");
|
|
5167
5871
|
|
|
5168
5872
|
class IndexedDbRateLimiterStorage {
|
|
5169
5873
|
executionDb;
|
|
@@ -5383,8 +6087,8 @@ class IndexedDbRateLimiterStorage {
|
|
|
5383
6087
|
}
|
|
5384
6088
|
}
|
|
5385
6089
|
// src/queue/IndexedDbQueueStorage.ts
|
|
5386
|
-
import { createServiceToken as
|
|
5387
|
-
var INDEXED_DB_QUEUE_STORAGE =
|
|
6090
|
+
import { createServiceToken as createServiceToken29, makeFingerprint as makeFingerprint10, uuid4 as uuid49 } from "@workglow/util";
|
|
6091
|
+
var INDEXED_DB_QUEUE_STORAGE = createServiceToken29("jobqueue.storage.indexedDb");
|
|
5388
6092
|
|
|
5389
6093
|
class IndexedDbQueueStorage {
|
|
5390
6094
|
queueName;
|
|
@@ -5464,8 +6168,8 @@ class IndexedDbQueueStorage {
|
|
|
5464
6168
|
const db = await this.getDb();
|
|
5465
6169
|
const now = new Date().toISOString();
|
|
5466
6170
|
const jobWithPrefixes = job;
|
|
5467
|
-
jobWithPrefixes.id = jobWithPrefixes.id ??
|
|
5468
|
-
jobWithPrefixes.job_run_id = jobWithPrefixes.job_run_id ??
|
|
6171
|
+
jobWithPrefixes.id = jobWithPrefixes.id ?? uuid49();
|
|
6172
|
+
jobWithPrefixes.job_run_id = jobWithPrefixes.job_run_id ?? uuid49();
|
|
5469
6173
|
jobWithPrefixes.queue = this.queueName;
|
|
5470
6174
|
jobWithPrefixes.fingerprint = await makeFingerprint10(jobWithPrefixes.input);
|
|
5471
6175
|
jobWithPrefixes.status = JobStatus.PENDING;
|
|
@@ -5952,19 +6656,25 @@ class IndexedDbQueueStorage {
|
|
|
5952
6656
|
}
|
|
5953
6657
|
}
|
|
5954
6658
|
export {
|
|
6659
|
+
registerTabularRepository,
|
|
5955
6660
|
isSearchCondition,
|
|
6661
|
+
getVectorProperty,
|
|
6662
|
+
getTabularRepository,
|
|
6663
|
+
getMetadataProperty,
|
|
6664
|
+
getGlobalTabularRepositories,
|
|
5956
6665
|
ensureIndexedDbTable,
|
|
5957
6666
|
dropIndexedDbTable,
|
|
5958
|
-
TabularRepository,
|
|
5959
6667
|
TABULAR_REPOSITORY,
|
|
5960
|
-
|
|
6668
|
+
TABULAR_REPOSITORIES,
|
|
6669
|
+
SupabaseTabularStorage,
|
|
5961
6670
|
SupabaseRateLimiterStorage,
|
|
5962
6671
|
SupabaseQueueStorage,
|
|
5963
|
-
|
|
5964
|
-
|
|
6672
|
+
SupabaseKvStorage,
|
|
6673
|
+
SqliteVectorStorage,
|
|
6674
|
+
SqliteTabularStorage,
|
|
5965
6675
|
SqliteRateLimiterStorage,
|
|
5966
6676
|
SqliteQueueStorage,
|
|
5967
|
-
|
|
6677
|
+
SqliteKvStorage,
|
|
5968
6678
|
SUPABASE_TABULAR_REPOSITORY,
|
|
5969
6679
|
SUPABASE_RATE_LIMITER_STORAGE,
|
|
5970
6680
|
SUPABASE_QUEUE_STORAGE,
|
|
@@ -5975,10 +6685,11 @@ export {
|
|
|
5975
6685
|
SQLITE_KV_REPOSITORY,
|
|
5976
6686
|
RATE_LIMITER_STORAGE,
|
|
5977
6687
|
QUEUE_STORAGE,
|
|
5978
|
-
|
|
6688
|
+
PostgresVectorStorage,
|
|
6689
|
+
PostgresTabularStorage,
|
|
5979
6690
|
PostgresRateLimiterStorage,
|
|
5980
6691
|
PostgresQueueStorage,
|
|
5981
|
-
|
|
6692
|
+
PostgresKvStorage,
|
|
5982
6693
|
PollingSubscriptionManager,
|
|
5983
6694
|
POSTGRES_TABULAR_REPOSITORY,
|
|
5984
6695
|
POSTGRES_RATE_LIMITER_STORAGE,
|
|
@@ -5986,18 +6697,19 @@ export {
|
|
|
5986
6697
|
POSTGRES_KV_REPOSITORY,
|
|
5987
6698
|
MEMORY_TABULAR_REPOSITORY,
|
|
5988
6699
|
MEMORY_KV_REPOSITORY,
|
|
5989
|
-
|
|
5990
|
-
|
|
6700
|
+
KvViaTabularStorage,
|
|
6701
|
+
KvStorage,
|
|
5991
6702
|
KV_REPOSITORY,
|
|
5992
6703
|
JobStatus,
|
|
5993
|
-
|
|
6704
|
+
IndexedDbTabularStorage,
|
|
5994
6705
|
IndexedDbRateLimiterStorage,
|
|
5995
6706
|
IndexedDbQueueStorage,
|
|
5996
|
-
|
|
5997
|
-
|
|
6707
|
+
IndexedDbKvStorage,
|
|
6708
|
+
InMemoryVectorStorage,
|
|
6709
|
+
InMemoryTabularStorage,
|
|
5998
6710
|
InMemoryRateLimiterStorage,
|
|
5999
6711
|
InMemoryQueueStorage,
|
|
6000
|
-
|
|
6712
|
+
InMemoryKvStorage,
|
|
6001
6713
|
IN_MEMORY_RATE_LIMITER_STORAGE,
|
|
6002
6714
|
IN_MEMORY_QUEUE_STORAGE,
|
|
6003
6715
|
INDEXED_DB_RATE_LIMITER_STORAGE,
|
|
@@ -6005,16 +6717,17 @@ export {
|
|
|
6005
6717
|
IDB_TABULAR_REPOSITORY,
|
|
6006
6718
|
IDB_KV_REPOSITORY,
|
|
6007
6719
|
HybridSubscriptionManager,
|
|
6008
|
-
|
|
6009
|
-
|
|
6010
|
-
|
|
6720
|
+
FsFolderTabularStorage,
|
|
6721
|
+
FsFolderKvStorage,
|
|
6722
|
+
FsFolderJsonKvStorage,
|
|
6011
6723
|
FS_FOLDER_TABULAR_REPOSITORY,
|
|
6012
6724
|
FS_FOLDER_KV_REPOSITORY,
|
|
6013
6725
|
FS_FOLDER_JSON_KV_REPOSITORY,
|
|
6014
6726
|
DefaultKeyValueSchema,
|
|
6015
6727
|
DefaultKeyValueKey,
|
|
6016
|
-
|
|
6017
|
-
CACHED_TABULAR_REPOSITORY
|
|
6728
|
+
CachedTabularStorage,
|
|
6729
|
+
CACHED_TABULAR_REPOSITORY,
|
|
6730
|
+
BaseTabularStorage
|
|
6018
6731
|
};
|
|
6019
6732
|
|
|
6020
|
-
//# debugId=
|
|
6733
|
+
//# debugId=CDE752B27E94EEDB64756E2164756E21
|