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