@workglow/storage 0.0.52

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.
Files changed (74) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +1015 -0
  3. package/dist/browser.js +2635 -0
  4. package/dist/browser.js.map +27 -0
  5. package/dist/bun.js +3880 -0
  6. package/dist/bun.js.map +35 -0
  7. package/dist/common-server.d.ts +23 -0
  8. package/dist/common-server.d.ts.map +1 -0
  9. package/dist/common.d.ts +16 -0
  10. package/dist/common.d.ts.map +1 -0
  11. package/dist/kv/FsFolderJsonKvRepository.d.ts +27 -0
  12. package/dist/kv/FsFolderJsonKvRepository.d.ts.map +1 -0
  13. package/dist/kv/FsFolderKvRepository.d.ts +74 -0
  14. package/dist/kv/FsFolderKvRepository.d.ts.map +1 -0
  15. package/dist/kv/IKvRepository.d.ts +65 -0
  16. package/dist/kv/IKvRepository.d.ts.map +1 -0
  17. package/dist/kv/InMemoryKvRepository.d.ts +26 -0
  18. package/dist/kv/InMemoryKvRepository.d.ts.map +1 -0
  19. package/dist/kv/IndexedDbKvRepository.d.ts +27 -0
  20. package/dist/kv/IndexedDbKvRepository.d.ts.map +1 -0
  21. package/dist/kv/KvRepository.d.ts +109 -0
  22. package/dist/kv/KvRepository.d.ts.map +1 -0
  23. package/dist/kv/KvViaTabularRepository.d.ts +64 -0
  24. package/dist/kv/KvViaTabularRepository.d.ts.map +1 -0
  25. package/dist/kv/PostgresKvRepository.d.ts +28 -0
  26. package/dist/kv/PostgresKvRepository.d.ts.map +1 -0
  27. package/dist/kv/SqliteKvRepository.d.ts +28 -0
  28. package/dist/kv/SqliteKvRepository.d.ts.map +1 -0
  29. package/dist/kv/SupabaseKvRepository.d.ts +34 -0
  30. package/dist/kv/SupabaseKvRepository.d.ts.map +1 -0
  31. package/dist/node.js +3879 -0
  32. package/dist/node.js.map +35 -0
  33. package/dist/queue/IQueueStorage.d.ts +125 -0
  34. package/dist/queue/IQueueStorage.d.ts.map +1 -0
  35. package/dist/queue/InMemoryQueueStorage.d.ts +109 -0
  36. package/dist/queue/InMemoryQueueStorage.d.ts.map +1 -0
  37. package/dist/queue/IndexedDbQueueStorage.d.ts +89 -0
  38. package/dist/queue/IndexedDbQueueStorage.d.ts.map +1 -0
  39. package/dist/queue/PostgresQueueStorage.d.ts +92 -0
  40. package/dist/queue/PostgresQueueStorage.d.ts.map +1 -0
  41. package/dist/queue/SqliteQueueStorage.d.ts +116 -0
  42. package/dist/queue/SqliteQueueStorage.d.ts.map +1 -0
  43. package/dist/queue/SupabaseQueueStorage.d.ts +93 -0
  44. package/dist/queue/SupabaseQueueStorage.d.ts.map +1 -0
  45. package/dist/tabular/BaseSqlTabularRepository.d.ts +94 -0
  46. package/dist/tabular/BaseSqlTabularRepository.d.ts.map +1 -0
  47. package/dist/tabular/CachedTabularRepository.d.ts +110 -0
  48. package/dist/tabular/CachedTabularRepository.d.ts.map +1 -0
  49. package/dist/tabular/FsFolderTabularRepository.d.ts +92 -0
  50. package/dist/tabular/FsFolderTabularRepository.d.ts.map +1 -0
  51. package/dist/tabular/ITabularRepository.d.ts +52 -0
  52. package/dist/tabular/ITabularRepository.d.ts.map +1 -0
  53. package/dist/tabular/InMemoryTabularRepository.d.ts +93 -0
  54. package/dist/tabular/InMemoryTabularRepository.d.ts.map +1 -0
  55. package/dist/tabular/IndexedDbTabularRepository.d.ts +100 -0
  56. package/dist/tabular/IndexedDbTabularRepository.d.ts.map +1 -0
  57. package/dist/tabular/PostgresTabularRepository.d.ts +133 -0
  58. package/dist/tabular/PostgresTabularRepository.d.ts.map +1 -0
  59. package/dist/tabular/SharedInMemoryTabularRepository.d.ts +126 -0
  60. package/dist/tabular/SharedInMemoryTabularRepository.d.ts.map +1 -0
  61. package/dist/tabular/SqliteTabularRepository.d.ts +110 -0
  62. package/dist/tabular/SqliteTabularRepository.d.ts.map +1 -0
  63. package/dist/tabular/SupabaseTabularRepository.d.ts +132 -0
  64. package/dist/tabular/SupabaseTabularRepository.d.ts.map +1 -0
  65. package/dist/tabular/TabularRepository.d.ts +123 -0
  66. package/dist/tabular/TabularRepository.d.ts.map +1 -0
  67. package/dist/types.d.ts +7 -0
  68. package/dist/types.d.ts.map +1 -0
  69. package/dist/util/IndexedDbTable.d.ts +40 -0
  70. package/dist/util/IndexedDbTable.d.ts.map +1 -0
  71. package/package.json +60 -0
  72. package/src/kv/README.md +159 -0
  73. package/src/queue/README.md +41 -0
  74. package/src/tabular/README.md +298 -0
package/dist/bun.js ADDED
@@ -0,0 +1,3880 @@
1
+ // @bun
2
+ // src/tabular/CachedTabularRepository.ts
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/TabularRepository.ts
12
+ import {
13
+ createServiceToken,
14
+ EventEmitter,
15
+ makeFingerprint
16
+ } from "@workglow/util";
17
+ var TABULAR_REPOSITORY = createServiceToken("storage.tabularRepository");
18
+
19
+ class TabularRepository {
20
+ schema;
21
+ primaryKeyNames;
22
+ events = new EventEmitter;
23
+ indexes;
24
+ primaryKeySchema;
25
+ valueSchema;
26
+ constructor(schema, primaryKeyNames, indexes = []) {
27
+ this.schema = schema;
28
+ this.primaryKeyNames = primaryKeyNames;
29
+ const primaryKeyProps = {};
30
+ const valueProps = {};
31
+ const primaryKeySet = new Set(primaryKeyNames);
32
+ for (const [key, typeDef] of Object.entries(schema.properties)) {
33
+ if (primaryKeySet.has(key)) {
34
+ primaryKeyProps[key] = Object.assign({}, typeDef);
35
+ } else {
36
+ valueProps[key] = Object.assign({}, typeDef);
37
+ }
38
+ }
39
+ const primaryKeyRequired = schema.required?.filter((key) => primaryKeySet.has(key)) ?? [];
40
+ const valueRequired = schema.required?.filter((key) => !primaryKeySet.has(key)) ?? [];
41
+ this.primaryKeySchema = {
42
+ type: "object",
43
+ properties: primaryKeyProps,
44
+ required: primaryKeyRequired,
45
+ additionalProperties: false
46
+ };
47
+ this.valueSchema = {
48
+ type: "object",
49
+ properties: valueProps,
50
+ required: valueRequired,
51
+ additionalProperties: false
52
+ };
53
+ const combinedColumns = [...this.primaryKeyColumns(), ...this.valueColumns()];
54
+ for (const column of combinedColumns) {
55
+ if (typeof column !== "string") {
56
+ throw new Error("Column names must be strings");
57
+ }
58
+ if (!/^[a-zA-Z][a-zA-Z0-9_]*$/.test(column)) {
59
+ throw new Error("Column names must start with a letter and contain only letters, digits, and underscores");
60
+ }
61
+ }
62
+ this.indexes = indexes.map((spec) => Array.isArray(spec) ? spec : [spec]);
63
+ this.indexes = this.filterCompoundKeys(this.primaryKeyColumns(), this.indexes);
64
+ for (const compoundIndex of this.indexes) {
65
+ for (const column of compoundIndex) {
66
+ if (!(column in this.primaryKeySchema.properties) && !(column in this.valueSchema.properties)) {
67
+ throw new Error(`Searchable column ${String(column)} is not in the primary key schema or value schema`);
68
+ }
69
+ }
70
+ }
71
+ }
72
+ filterCompoundKeys(primaryKey, potentialKeys) {
73
+ const isPrefix = (prefix, arr) => {
74
+ if (prefix.length > arr.length)
75
+ return false;
76
+ return prefix.every((val, index) => val === arr[index]);
77
+ };
78
+ potentialKeys.sort((a, b) => a.length - b.length);
79
+ let filteredKeys = [];
80
+ for (let i = 0;i < potentialKeys.length; i++) {
81
+ let key = potentialKeys[i];
82
+ if (isPrefix(key, primaryKey))
83
+ continue;
84
+ if (key.length === 1) {
85
+ filteredKeys.push(key);
86
+ continue;
87
+ }
88
+ let isRedundant = potentialKeys.some((otherKey, j) => j > i && isPrefix(key, otherKey));
89
+ if (!isRedundant) {
90
+ filteredKeys.push(key);
91
+ }
92
+ }
93
+ return filteredKeys;
94
+ }
95
+ on(name, fn) {
96
+ this.events.on(name, fn);
97
+ }
98
+ off(name, fn) {
99
+ this.events.off(name, fn);
100
+ }
101
+ once(name, fn) {
102
+ this.events.once(name, fn);
103
+ }
104
+ emit(name, ...args) {
105
+ this.events.emit(name, ...args);
106
+ }
107
+ waitOn(name) {
108
+ return this.events.waitOn(name);
109
+ }
110
+ primaryKeyColumns() {
111
+ const columns = [];
112
+ for (const key of Object.keys(this.primaryKeySchema.properties)) {
113
+ columns.push(key);
114
+ }
115
+ return columns;
116
+ }
117
+ valueColumns() {
118
+ const columns = [];
119
+ for (const key of Object.keys(this.valueSchema.properties)) {
120
+ columns.push(key);
121
+ }
122
+ return columns;
123
+ }
124
+ separateKeyValueFromCombined(obj) {
125
+ if (obj === null) {
126
+ console.warn("Key is null");
127
+ return { value: {}, key: {} };
128
+ }
129
+ if (typeof obj !== "object") {
130
+ console.warn("Object is not an object");
131
+ return { value: {}, key: {} };
132
+ }
133
+ const primaryKeyNames = this.primaryKeyColumns();
134
+ const valueNames = this.valueColumns();
135
+ const value = {};
136
+ const key = {};
137
+ for (const k of primaryKeyNames) {
138
+ key[k] = obj[k];
139
+ }
140
+ for (const k of valueNames) {
141
+ value[k] = obj[k];
142
+ }
143
+ return { value, key };
144
+ }
145
+ async getKeyAsIdString(key) {
146
+ return await makeFingerprint(key);
147
+ }
148
+ getPrimaryKeyAsOrderedArray(key) {
149
+ const orderedParams = [];
150
+ const keyObj = key;
151
+ for (const k in this.primaryKeySchema.properties) {
152
+ if (k in keyObj) {
153
+ orderedParams.push(keyObj[k]);
154
+ } else {
155
+ throw new Error(`Missing required primary key field: ${k}`);
156
+ }
157
+ }
158
+ return orderedParams;
159
+ }
160
+ findBestMatchingIndex(unorderedSearchKey) {
161
+ if (!unorderedSearchKey.length)
162
+ return;
163
+ const allKeys = [
164
+ this.primaryKeyColumns(),
165
+ ...this.indexes
166
+ ];
167
+ const searchKeySet = new Set(unorderedSearchKey);
168
+ const hasMatchingPrefix = (index) => {
169
+ return index.length > 0 && searchKeySet.has(index[0]);
170
+ };
171
+ let bestMatch;
172
+ let bestMatchScore = 0;
173
+ for (const index of allKeys) {
174
+ if (hasMatchingPrefix(index)) {
175
+ let score = 0;
176
+ for (const col of index) {
177
+ if (!searchKeySet.has(col))
178
+ break;
179
+ score++;
180
+ }
181
+ if (score > bestMatchScore) {
182
+ bestMatch = index;
183
+ bestMatchScore = score;
184
+ }
185
+ }
186
+ }
187
+ return bestMatch;
188
+ }
189
+ destroy() {}
190
+ async[Symbol.asyncDispose]() {
191
+ this.destroy();
192
+ }
193
+ [Symbol.dispose]() {
194
+ this.destroy();
195
+ }
196
+ }
197
+
198
+ // src/tabular/InMemoryTabularRepository.ts
199
+ var MEMORY_TABULAR_REPOSITORY = createServiceToken2("storage.tabularRepository.inMemory");
200
+
201
+ class InMemoryTabularRepository extends TabularRepository {
202
+ values = new Map;
203
+ constructor(schema, primaryKeyNames, indexes = []) {
204
+ super(schema, primaryKeyNames, indexes);
205
+ }
206
+ async setupDatabase() {}
207
+ async put(value) {
208
+ const { key } = this.separateKeyValueFromCombined(value);
209
+ const id = await makeFingerprint2(key);
210
+ this.values.set(id, value);
211
+ this.events.emit("put", value);
212
+ return value;
213
+ }
214
+ async putBulk(values) {
215
+ return await Promise.all(values.map(async (value) => this.put(value)));
216
+ }
217
+ async get(key) {
218
+ const id = await makeFingerprint2(key);
219
+ const out = this.values.get(id);
220
+ this.events.emit("get", key, out);
221
+ return out;
222
+ }
223
+ async search(key) {
224
+ const searchKeys = Object.keys(key);
225
+ if (searchKeys.length === 0) {
226
+ return;
227
+ }
228
+ const bestIndex = this.findBestMatchingIndex(searchKeys);
229
+ if (!bestIndex) {
230
+ throw new Error(`No suitable index found for the search criteria, searching for ['${searchKeys.join("', '")}'] with pk ['${this.primaryKeyNames.join("', '")}'] and indexes ['${this.indexes.join("', '")}']`);
231
+ }
232
+ const results = Array.from(this.values.values()).filter((item) => Object.entries(key).every(([k, v]) => item[k] === v));
233
+ if (results.length > 0) {
234
+ this.events.emit("search", key, results);
235
+ return results;
236
+ } else {
237
+ this.events.emit("search", key, undefined);
238
+ return;
239
+ }
240
+ }
241
+ async delete(value) {
242
+ const { key } = this.separateKeyValueFromCombined(value);
243
+ const id = await makeFingerprint2(key);
244
+ this.values.delete(id);
245
+ this.events.emit("delete", key);
246
+ }
247
+ async deleteAll() {
248
+ this.values.clear();
249
+ this.events.emit("clearall");
250
+ }
251
+ async getAll() {
252
+ const all = Array.from(this.values.values());
253
+ return all.length > 0 ? all : undefined;
254
+ }
255
+ async size() {
256
+ return this.values.size;
257
+ }
258
+ async deleteSearch(column, value, operator = "=") {
259
+ const entries = this.values.entries();
260
+ const entriesToDelete = entries.filter(([_, entity]) => {
261
+ const columnValue = entity[column];
262
+ switch (operator) {
263
+ case "=":
264
+ return columnValue === value;
265
+ case "<":
266
+ return columnValue !== null && columnValue !== undefined && columnValue < value;
267
+ case "<=":
268
+ return columnValue !== null && columnValue !== undefined && columnValue <= value;
269
+ case ">":
270
+ return columnValue !== null && columnValue !== undefined && columnValue > value;
271
+ case ">=":
272
+ return columnValue !== null && columnValue !== undefined && columnValue >= value;
273
+ default:
274
+ return false;
275
+ }
276
+ });
277
+ for (const [id, _] of entriesToDelete) {
278
+ this.values.delete(id);
279
+ }
280
+ if (Array.from(entriesToDelete).length > 0) {
281
+ this.events.emit("delete", column);
282
+ }
283
+ }
284
+ destroy() {
285
+ this.values.clear();
286
+ }
287
+ }
288
+
289
+ // src/tabular/CachedTabularRepository.ts
290
+ var CACHED_TABULAR_REPOSITORY = createServiceToken3("storage.tabularRepository.cached");
291
+
292
+ class CachedTabularRepository extends TabularRepository {
293
+ cache;
294
+ durable;
295
+ cacheInitialized = false;
296
+ constructor(durable, cache, schema, primaryKeyNames, indexes) {
297
+ if (!schema || !primaryKeyNames) {
298
+ throw new Error("Schema and primaryKeyNames must be provided when creating CachedTabularRepository");
299
+ }
300
+ super(schema, primaryKeyNames, indexes || []);
301
+ this.durable = durable;
302
+ if (cache) {
303
+ this.cache = cache;
304
+ } else {
305
+ this.cache = new InMemoryTabularRepository(schema, primaryKeyNames, indexes || []);
306
+ }
307
+ this.setupEventForwarding();
308
+ }
309
+ setupEventForwarding() {
310
+ this.cache.on("put", (entity) => {
311
+ this.events.emit("put", entity);
312
+ });
313
+ this.cache.on("get", (key, entity) => {
314
+ this.events.emit("get", key, entity);
315
+ });
316
+ this.cache.on("search", (key, entities) => {
317
+ this.events.emit("search", key, entities);
318
+ });
319
+ this.cache.on("delete", (key) => {
320
+ this.events.emit("delete", key);
321
+ });
322
+ this.cache.on("clearall", () => {
323
+ this.events.emit("clearall");
324
+ });
325
+ }
326
+ async initializeCache() {
327
+ if (this.cacheInitialized)
328
+ return;
329
+ try {
330
+ const all = await this.durable.getAll();
331
+ if (all && all.length > 0) {
332
+ await this.cache.putBulk(all);
333
+ }
334
+ this.cacheInitialized = true;
335
+ } catch (error) {
336
+ console.warn("Failed to initialize cache from durable repository:", error);
337
+ this.cacheInitialized = true;
338
+ }
339
+ }
340
+ async put(value) {
341
+ await this.initializeCache();
342
+ const result = await this.durable.put(value);
343
+ await this.cache.put(result);
344
+ return result;
345
+ }
346
+ async putBulk(values) {
347
+ await this.initializeCache();
348
+ const results = await this.durable.putBulk(values);
349
+ await this.cache.putBulk(results);
350
+ return results;
351
+ }
352
+ async get(key) {
353
+ await this.initializeCache();
354
+ let result = await this.cache.get(key);
355
+ if (result === undefined) {
356
+ result = await this.durable.get(key);
357
+ if (result) {
358
+ await this.cache.put(result);
359
+ }
360
+ }
361
+ return result;
362
+ }
363
+ async search(key) {
364
+ await this.initializeCache();
365
+ let results = await this.cache.search(key);
366
+ if (results === undefined) {
367
+ results = await this.durable.search(key);
368
+ if (results && results.length > 0) {
369
+ await this.cache.putBulk(results);
370
+ }
371
+ }
372
+ return results;
373
+ }
374
+ async delete(value) {
375
+ await this.initializeCache();
376
+ await this.durable.delete(value);
377
+ await this.cache.delete(value);
378
+ }
379
+ async deleteAll() {
380
+ await this.initializeCache();
381
+ await this.durable.deleteAll();
382
+ await this.cache.deleteAll();
383
+ }
384
+ async getAll() {
385
+ await this.initializeCache();
386
+ let results = await this.cache.getAll();
387
+ if (!results || results.length === 0) {
388
+ results = await this.durable.getAll();
389
+ if (results && results.length > 0) {
390
+ await this.cache.putBulk(results);
391
+ }
392
+ }
393
+ return results;
394
+ }
395
+ async size() {
396
+ await this.initializeCache();
397
+ return await this.durable.size();
398
+ }
399
+ async deleteSearch(column, value, operator = "=") {
400
+ await this.initializeCache();
401
+ await this.durable.deleteSearch(column, value, operator);
402
+ await this.cache.deleteSearch(column, value, operator);
403
+ }
404
+ async invalidateCache() {
405
+ await this.cache.deleteAll();
406
+ this.cacheInitialized = false;
407
+ }
408
+ async refreshCache() {
409
+ await this.cache.deleteAll();
410
+ this.cacheInitialized = false;
411
+ await this.initializeCache();
412
+ }
413
+ destroy() {
414
+ this.durable.destroy();
415
+ this.cache.destroy();
416
+ }
417
+ }
418
+ // src/kv/IKvRepository.ts
419
+ var DefaultKeyValueSchema = {
420
+ type: "object",
421
+ properties: {
422
+ key: { type: "string" },
423
+ value: {}
424
+ },
425
+ additionalProperties: false
426
+ };
427
+ var DefaultKeyValueKey = ["key"];
428
+ // src/kv/InMemoryKvRepository.ts
429
+ import { createServiceToken as createServiceToken5 } from "@workglow/util";
430
+
431
+ // src/kv/KvRepository.ts
432
+ import { createServiceToken as createServiceToken4, EventEmitter as EventEmitter2, makeFingerprint as makeFingerprint3 } from "@workglow/util";
433
+ var KV_REPOSITORY = createServiceToken4("storage.kvRepository");
434
+
435
+ class KvRepository {
436
+ keySchema;
437
+ valueSchema;
438
+ events = new EventEmitter2;
439
+ constructor(keySchema = { type: "string" }, valueSchema = {}) {
440
+ this.keySchema = keySchema;
441
+ this.valueSchema = valueSchema;
442
+ }
443
+ async getObjectAsIdString(object) {
444
+ return await makeFingerprint3(object);
445
+ }
446
+ on(name, fn) {
447
+ this.events.on(name, fn);
448
+ }
449
+ off(name, fn) {
450
+ this.events.off(name, fn);
451
+ }
452
+ once(name, fn) {
453
+ this.events.once(name, fn);
454
+ }
455
+ emit(name, ...args) {
456
+ this.events.emit(name, ...args);
457
+ }
458
+ waitOn(name) {
459
+ return this.events.waitOn(name);
460
+ }
461
+ }
462
+
463
+ // src/kv/KvViaTabularRepository.ts
464
+ class KvViaTabularRepository extends KvRepository {
465
+ async put(key, value) {
466
+ const schemaType = typeof this.valueSchema === "object" && this.valueSchema !== null && "type" in this.valueSchema ? this.valueSchema.type : undefined;
467
+ const shouldStringify = !["number", "boolean", "string", "blob"].includes(schemaType);
468
+ if (shouldStringify) {
469
+ value = JSON.stringify(value);
470
+ }
471
+ await this.tabularRepository.put({ key, value });
472
+ }
473
+ async putBulk(items) {
474
+ const schemaType = typeof this.valueSchema === "object" && this.valueSchema !== null && "type" in this.valueSchema ? this.valueSchema.type : undefined;
475
+ const shouldStringify = !["number", "boolean", "string", "blob"].includes(schemaType);
476
+ const entities = items.map(({ key, value }) => {
477
+ if (shouldStringify) {
478
+ value = JSON.stringify(value);
479
+ }
480
+ return { key, value };
481
+ });
482
+ await this.tabularRepository.putBulk(entities);
483
+ }
484
+ async get(key) {
485
+ const result = await this.tabularRepository.get({ key });
486
+ if (result) {
487
+ const schemaType = typeof this.valueSchema === "object" && this.valueSchema !== null && "type" in this.valueSchema ? this.valueSchema.type : undefined;
488
+ const shouldParse = !["number", "boolean", "string", "blob"].includes(schemaType);
489
+ if (shouldParse) {
490
+ try {
491
+ return JSON.parse(result.value);
492
+ } catch (e) {
493
+ return result.value;
494
+ }
495
+ } else {
496
+ return result.value;
497
+ }
498
+ } else {
499
+ return;
500
+ }
501
+ }
502
+ async delete(key) {
503
+ return await this.tabularRepository.delete({ key });
504
+ }
505
+ async getAll() {
506
+ const values = await this.tabularRepository.getAll();
507
+ if (values) {
508
+ return values.map((value) => ({
509
+ key: value.key,
510
+ value: (() => {
511
+ const schemaType = typeof this.valueSchema === "object" && this.valueSchema !== null && "type" in this.valueSchema ? this.valueSchema.type : undefined;
512
+ const shouldParse = !["number", "boolean", "string"].includes(schemaType);
513
+ if (shouldParse && typeof value.value === "string") {
514
+ try {
515
+ return JSON.parse(value.value);
516
+ } catch (e) {
517
+ return value.value;
518
+ }
519
+ }
520
+ return value.value;
521
+ })()
522
+ }));
523
+ }
524
+ }
525
+ async deleteAll() {
526
+ return await this.tabularRepository.deleteAll();
527
+ }
528
+ async size() {
529
+ return await this.tabularRepository.size();
530
+ }
531
+ }
532
+
533
+ // src/kv/InMemoryKvRepository.ts
534
+ var MEMORY_KV_REPOSITORY = createServiceToken5("storage.kvRepository.inMemory");
535
+
536
+ class InMemoryKvRepository extends KvViaTabularRepository {
537
+ tabularRepository;
538
+ constructor(keySchema = { type: "string" }, valueSchema = {}) {
539
+ super(keySchema, valueSchema);
540
+ this.tabularRepository = new InMemoryTabularRepository(DefaultKeyValueSchema, DefaultKeyValueKey);
541
+ }
542
+ }
543
+ // src/queue/InMemoryQueueStorage.ts
544
+ import { createServiceToken as createServiceToken7, makeFingerprint as makeFingerprint4, sleep, uuid4 } from "@workglow/util";
545
+
546
+ // src/queue/IQueueStorage.ts
547
+ import { createServiceToken as createServiceToken6 } from "@workglow/util";
548
+ var QUEUE_STORAGE = createServiceToken6("jobqueue.storage");
549
+ var JobStatus;
550
+ ((JobStatus2) => {
551
+ JobStatus2["PENDING"] = "PENDING";
552
+ JobStatus2["PROCESSING"] = "PROCESSING";
553
+ JobStatus2["COMPLETED"] = "COMPLETED";
554
+ JobStatus2["ABORTING"] = "ABORTING";
555
+ JobStatus2["FAILED"] = "FAILED";
556
+ JobStatus2["DISABLED"] = "DISABLED";
557
+ })(JobStatus ||= {});
558
+
559
+ // src/queue/InMemoryQueueStorage.ts
560
+ var IN_MEMORY_QUEUE_STORAGE = createServiceToken7("jobqueue.storage.inMemory");
561
+
562
+ class InMemoryQueueStorage {
563
+ queueName;
564
+ constructor(queueName) {
565
+ this.queueName = queueName;
566
+ this.jobQueue = [];
567
+ }
568
+ jobQueue;
569
+ pendingQueue() {
570
+ const now = new Date().toISOString();
571
+ return this.jobQueue.filter((job) => job.status === "PENDING" /* PENDING */).filter((job) => !job.run_after || job.run_after <= now).sort((a, b) => (a.run_after || "").localeCompare(b.run_after || ""));
572
+ }
573
+ async add(job) {
574
+ await sleep(0);
575
+ const now = new Date().toISOString();
576
+ job.id = job.id ?? uuid4();
577
+ job.job_run_id = job.job_run_id ?? uuid4();
578
+ job.queue = this.queueName;
579
+ job.fingerprint = await makeFingerprint4(job.input);
580
+ job.status = "PENDING" /* PENDING */;
581
+ job.progress = 0;
582
+ job.progress_message = "";
583
+ job.progress_details = null;
584
+ job.created_at = now;
585
+ job.run_after = now;
586
+ this.jobQueue.push(job);
587
+ return job.id;
588
+ }
589
+ async get(id) {
590
+ await sleep(0);
591
+ return this.jobQueue.find((j) => j.id === id);
592
+ }
593
+ async peek(status = "PENDING" /* PENDING */, num = 100) {
594
+ await sleep(0);
595
+ num = Number(num) || 100;
596
+ return this.jobQueue.sort((a, b) => (a.run_after || "").localeCompare(b.run_after || "")).filter((j) => j.status === status).slice(0, num);
597
+ }
598
+ async next() {
599
+ await sleep(0);
600
+ const top = this.pendingQueue();
601
+ const job = top[0];
602
+ if (job) {
603
+ job.status = "PROCESSING" /* PROCESSING */;
604
+ job.last_ran_at = new Date().toISOString();
605
+ return job;
606
+ }
607
+ }
608
+ async size(status = "PENDING" /* PENDING */) {
609
+ await sleep(0);
610
+ return this.jobQueue.filter((j) => j.status === status).length;
611
+ }
612
+ async saveProgress(id, progress, message, details) {
613
+ await sleep(0);
614
+ const job = this.jobQueue.find((j) => j.id === id);
615
+ if (!job) {
616
+ throw new Error(`Job ${id} not found`);
617
+ }
618
+ job.progress = progress;
619
+ job.progress_message = message;
620
+ job.progress_details = details;
621
+ }
622
+ async complete(job) {
623
+ await sleep(0);
624
+ const index = this.jobQueue.findIndex((j) => j.id === job.id);
625
+ if (index !== -1) {
626
+ const existing = this.jobQueue[index];
627
+ const currentAttempts = existing?.run_attempts ?? 0;
628
+ job.run_attempts = currentAttempts + 1;
629
+ this.jobQueue[index] = job;
630
+ }
631
+ }
632
+ async abort(id) {
633
+ await sleep(0);
634
+ const job = this.jobQueue.find((j) => j.id === id);
635
+ if (job) {
636
+ job.status = "ABORTING" /* ABORTING */;
637
+ }
638
+ }
639
+ async getByRunId(runId) {
640
+ await sleep(0);
641
+ return this.jobQueue.filter((job) => job.job_run_id === runId);
642
+ }
643
+ async deleteAll() {
644
+ await sleep(0);
645
+ this.jobQueue = [];
646
+ }
647
+ async outputForInput(input) {
648
+ await sleep(0);
649
+ const fingerprint = await makeFingerprint4(input);
650
+ return this.jobQueue.find((j) => j.fingerprint === fingerprint && j.status === "COMPLETED" /* COMPLETED */)?.output ?? null;
651
+ }
652
+ async delete(id) {
653
+ await sleep(0);
654
+ this.jobQueue = this.jobQueue.filter((job) => job.id !== id);
655
+ }
656
+ async deleteJobsByStatusAndAge(status, olderThanMs) {
657
+ await sleep(0);
658
+ const cutoffDate = new Date(Date.now() - olderThanMs).toISOString();
659
+ this.jobQueue = this.jobQueue.filter((job) => job.status !== status || !job.completed_at || job.completed_at > cutoffDate);
660
+ }
661
+ async setupDatabase() {}
662
+ }
663
+ // src/tabular/FsFolderTabularRepository.ts
664
+ import { createServiceToken as createServiceToken8, sleep as sleep2 } from "@workglow/util";
665
+ import { mkdir, readdir, readFile, rm, writeFile } from "fs/promises";
666
+ import path from "path";
667
+ var FS_FOLDER_TABULAR_REPOSITORY = createServiceToken8("storage.tabularRepository.fsFolder");
668
+
669
+ class FsFolderTabularRepository extends TabularRepository {
670
+ folderPath;
671
+ constructor(folderPath, schema, primaryKeyNames, indexes = []) {
672
+ super(schema, primaryKeyNames, indexes);
673
+ this.folderPath = path.join(folderPath);
674
+ }
675
+ async setupDirectory() {
676
+ try {
677
+ await mkdir(this.folderPath, { recursive: true });
678
+ } catch (error) {
679
+ await new Promise((resolve) => setTimeout(resolve, 0));
680
+ try {
681
+ await mkdir(this.folderPath, { recursive: true });
682
+ } catch {}
683
+ }
684
+ }
685
+ async put(entity) {
686
+ await this.setupDirectory();
687
+ const filePath = await this.getFilePath(entity);
688
+ try {
689
+ await writeFile(filePath, JSON.stringify(entity));
690
+ } catch (error) {
691
+ try {
692
+ await sleep2(1);
693
+ await writeFile(filePath, JSON.stringify(entity));
694
+ } catch (error2) {
695
+ console.error("Error writing file", filePath, error2);
696
+ }
697
+ }
698
+ this.events.emit("put", entity);
699
+ return entity;
700
+ }
701
+ async putBulk(entities) {
702
+ await this.setupDirectory();
703
+ return await Promise.all(entities.map(async (entity) => this.put(entity)));
704
+ }
705
+ async get(key) {
706
+ await this.setupDirectory();
707
+ const filePath = await this.getFilePath(key);
708
+ try {
709
+ const buf = await readFile(filePath);
710
+ const data = buf.toString("utf8");
711
+ const entity = JSON.parse(data);
712
+ this.events.emit("get", key, entity);
713
+ return entity;
714
+ } catch (error) {
715
+ this.events.emit("get", key, undefined);
716
+ return;
717
+ }
718
+ }
719
+ async delete(value) {
720
+ await this.setupDirectory();
721
+ const { key } = this.separateKeyValueFromCombined(value);
722
+ const filePath = await this.getFilePath(key);
723
+ try {
724
+ await rm(filePath);
725
+ } catch (error) {
726
+ console.error("Error deleting file", filePath, error);
727
+ }
728
+ this.events.emit("delete", key);
729
+ }
730
+ async getAll() {
731
+ await this.setupDirectory();
732
+ try {
733
+ const files = await readdir(this.folderPath);
734
+ const jsonFiles = files.filter((file) => file.endsWith(".json"));
735
+ if (jsonFiles.length === 0) {
736
+ return;
737
+ }
738
+ const results = await Promise.allSettled(jsonFiles.map(async (file) => {
739
+ const buf = await readFile(path.join(this.folderPath, file));
740
+ const content = buf.toString("utf8");
741
+ const data = JSON.parse(content);
742
+ return data;
743
+ }));
744
+ const values = results.filter((result) => result.status === "fulfilled").map((result) => result.value);
745
+ return values.length > 0 ? values : undefined;
746
+ } catch (error) {
747
+ console.error("Error in getAll:", error);
748
+ throw error;
749
+ }
750
+ }
751
+ async deleteAll() {
752
+ await this.setupDirectory();
753
+ await rm(this.folderPath, { recursive: true, force: true });
754
+ this.events.emit("clearall");
755
+ }
756
+ async size() {
757
+ await this.setupDirectory();
758
+ const files = await readdir(this.folderPath);
759
+ const jsonFiles = files.filter((file) => file.endsWith(".json"));
760
+ return jsonFiles.length;
761
+ }
762
+ async search(key) {
763
+ throw new Error("Search not supported for FsFolderTabularRepository");
764
+ }
765
+ async getFilePath(value) {
766
+ const { key } = this.separateKeyValueFromCombined(value);
767
+ const filename = await this.getKeyAsIdString(key);
768
+ const fullPath = path.join(this.folderPath, `${filename}.json`);
769
+ return fullPath;
770
+ }
771
+ async deleteSearch(column, value, operator = "=") {
772
+ throw new Error("Search not supported for FsFolderTabularRepository");
773
+ }
774
+ }
775
+ // src/tabular/PostgresTabularRepository.ts
776
+ import { createServiceToken as createServiceToken9 } from "@workglow/util";
777
+
778
+ // src/tabular/BaseSqlTabularRepository.ts
779
+ class BaseSqlTabularRepository extends TabularRepository {
780
+ table;
781
+ constructor(table = "tabular_store", schema, primaryKeyNames, indexes = []) {
782
+ super(schema, primaryKeyNames, indexes);
783
+ this.table = table;
784
+ this.validateTableAndSchema();
785
+ }
786
+ constructPrimaryKeyColumns($delimiter = "") {
787
+ const cols = Object.entries(this.primaryKeySchema.properties).map(([key, typeDef]) => {
788
+ const sqlType = this.mapTypeToSQL(typeDef);
789
+ return `${$delimiter}${key}${$delimiter} ${sqlType} NOT NULL`;
790
+ }).join(", ");
791
+ return cols;
792
+ }
793
+ constructValueColumns($delimiter = "") {
794
+ const requiredSet = new Set(this.valueSchema.required ?? []);
795
+ const cols = Object.entries(this.valueSchema.properties).map(([key, typeDef]) => {
796
+ const sqlType = this.mapTypeToSQL(typeDef);
797
+ const isRequired = requiredSet.has(key);
798
+ const nullable = !isRequired || this.isNullable(typeDef);
799
+ return `${$delimiter}${key}${$delimiter} ${sqlType}${nullable ? " NULL" : " NOT NULL"}`;
800
+ }).join(", ");
801
+ if (cols.length > 0) {
802
+ return `, ${cols}`;
803
+ } else {
804
+ return "";
805
+ }
806
+ }
807
+ isNullable(typeDef) {
808
+ if (typeof typeDef === "boolean")
809
+ return typeDef;
810
+ if (typeDef.type === "null") {
811
+ return true;
812
+ }
813
+ if (Array.isArray(typeDef.type)) {
814
+ return typeDef.type.includes("null");
815
+ }
816
+ if (typeDef.anyOf && Array.isArray(typeDef.anyOf)) {
817
+ return typeDef.anyOf.some((type) => type.type === "null");
818
+ }
819
+ if (typeDef.oneOf && Array.isArray(typeDef.oneOf)) {
820
+ return typeDef.oneOf.some((type) => type.type === "null");
821
+ }
822
+ return false;
823
+ }
824
+ primaryKeyColumnList($delimiter = "") {
825
+ return $delimiter + this.primaryKeyColumns().join(`${$delimiter}, ${$delimiter}`) + $delimiter;
826
+ }
827
+ valueColumnList($delimiter = "") {
828
+ return $delimiter + this.valueColumns().join(`${$delimiter}, ${$delimiter}`) + $delimiter;
829
+ }
830
+ getNonNullType(typeDef) {
831
+ if (typeof typeDef === "boolean")
832
+ return typeDef;
833
+ if (typeDef.anyOf && Array.isArray(typeDef.anyOf)) {
834
+ const nonNullType = typeDef.anyOf.find((t) => t.type !== "null");
835
+ if (nonNullType) {
836
+ return nonNullType;
837
+ }
838
+ }
839
+ if (typeDef.oneOf && Array.isArray(typeDef.oneOf)) {
840
+ const nonNullType = typeDef.oneOf.find((t) => t.type !== "null");
841
+ if (nonNullType) {
842
+ return nonNullType;
843
+ }
844
+ }
845
+ return typeDef;
846
+ }
847
+ getValueAsOrderedArray(value) {
848
+ const orderedParams = [];
849
+ const valueAsRecord = value;
850
+ const requiredSet = new Set(this.valueSchema.required ?? []);
851
+ for (const key in this.valueSchema.properties) {
852
+ if (Object.prototype.hasOwnProperty.call(valueAsRecord, key)) {
853
+ const val = valueAsRecord[key];
854
+ if (val === undefined && !requiredSet.has(key)) {
855
+ orderedParams.push(null);
856
+ } else {
857
+ orderedParams.push(this.jsToSqlValue(key, val));
858
+ }
859
+ } else {
860
+ if (requiredSet.has(key)) {
861
+ throw new Error(`Missing required value field: ${key}`);
862
+ }
863
+ orderedParams.push(null);
864
+ }
865
+ }
866
+ return orderedParams;
867
+ }
868
+ getPrimaryKeyAsOrderedArray(key) {
869
+ const orderedParams = [];
870
+ const keyObj = key;
871
+ for (const k of Object.keys(this.primaryKeySchema.properties)) {
872
+ if (k in keyObj) {
873
+ const value = keyObj[k];
874
+ if (value === null) {
875
+ throw new Error(`Primary key field ${k} cannot be null`);
876
+ }
877
+ orderedParams.push(this.jsToSqlValue(k, value));
878
+ } else {
879
+ throw new Error(`Missing required primary key field: ${k}`);
880
+ }
881
+ }
882
+ return orderedParams;
883
+ }
884
+ jsToSqlValue(column, value) {
885
+ const typeDef = this.schema.properties[column];
886
+ if (!typeDef) {
887
+ return value;
888
+ }
889
+ if (value === null && this.isNullable(typeDef)) {
890
+ return null;
891
+ }
892
+ const actualType = this.getNonNullType(typeDef);
893
+ if (typeof actualType === "boolean") {
894
+ return value;
895
+ }
896
+ if (actualType.contentEncoding === "blob") {
897
+ const v = value;
898
+ if (v instanceof Uint8Array) {
899
+ return v;
900
+ }
901
+ if (typeof Buffer !== "undefined" && v instanceof Buffer) {
902
+ return new Uint8Array(v);
903
+ }
904
+ if (Array.isArray(v)) {
905
+ return new Uint8Array(v);
906
+ }
907
+ return v;
908
+ } else if (value instanceof Date) {
909
+ return value.toISOString();
910
+ } else {
911
+ return value;
912
+ }
913
+ }
914
+ sqlToJsValue(column, value) {
915
+ const typeDef = this.schema.properties[column];
916
+ if (!typeDef) {
917
+ return value;
918
+ }
919
+ if (value === null && this.isNullable(typeDef)) {
920
+ return null;
921
+ }
922
+ const actualType = this.getNonNullType(typeDef);
923
+ if (typeof actualType === "boolean") {
924
+ return value;
925
+ }
926
+ if (actualType.contentEncoding === "blob") {
927
+ const v = value;
928
+ if (typeof Buffer !== "undefined" && v instanceof Buffer) {
929
+ return new Uint8Array(v);
930
+ }
931
+ if (v instanceof Uint8Array) {
932
+ return v;
933
+ }
934
+ return v;
935
+ } else {
936
+ return value;
937
+ }
938
+ }
939
+ validateTableAndSchema() {
940
+ if (!/^[a-zA-Z][a-zA-Z0-9_]*$/.test(this.table)) {
941
+ throw new Error("Table name must start with a letter and contain only letters, digits, and underscores, got: " + this.table);
942
+ }
943
+ const validateSchemaKeys = (schema) => {
944
+ for (const key in schema.properties) {
945
+ if (!/^[a-zA-Z][a-zA-Z0-9_]*$/.test(key)) {
946
+ throw new Error("Schema keys must start with a letter and contain only letters, digits, and underscores, got: " + key);
947
+ }
948
+ }
949
+ };
950
+ validateSchemaKeys(this.primaryKeySchema);
951
+ validateSchemaKeys(this.valueSchema);
952
+ const primaryKeys = new Set(Object.keys(this.primaryKeySchema.properties));
953
+ const valueKeys = Object.keys(this.valueSchema.properties);
954
+ const duplicates = valueKeys.filter((key) => primaryKeys.has(key));
955
+ if (duplicates.length > 0) {
956
+ throw new Error(`Duplicate keys found in schemas: ${duplicates.join(", ")}`);
957
+ }
958
+ }
959
+ }
960
+
961
+ // src/tabular/PostgresTabularRepository.ts
962
+ var POSTGRES_TABULAR_REPOSITORY = createServiceToken9("storage.tabularRepository.postgres");
963
+
964
+ class PostgresTabularRepository extends BaseSqlTabularRepository {
965
+ db;
966
+ constructor(db, table = "tabular_store", schema, primaryKeyNames, indexes = []) {
967
+ super(table, schema, primaryKeyNames, indexes);
968
+ this.db = db;
969
+ }
970
+ #setup = false;
971
+ async setupDatabase() {
972
+ if (this.#setup) {
973
+ return this.db;
974
+ }
975
+ const sql = `
976
+ CREATE TABLE IF NOT EXISTS "${this.table}" (
977
+ ${this.constructPrimaryKeyColumns('"')} ${this.constructValueColumns('"')},
978
+ PRIMARY KEY (${this.primaryKeyColumnList()})
979
+ )
980
+ `;
981
+ await this.db.query(sql);
982
+ const pkColumns = this.primaryKeyColumns();
983
+ const createdIndexes = new Set;
984
+ for (const columns of this.indexes) {
985
+ if (columns.length <= pkColumns.length) {
986
+ const isPkPrefix = columns.every((col, idx) => col === pkColumns[idx]);
987
+ if (isPkPrefix)
988
+ continue;
989
+ }
990
+ const indexName = `${this.table}_${columns.join("_")}`;
991
+ const columnList = columns.map((col) => `"${String(col)}"`).join(", ");
992
+ const columnKey = columns.join(",");
993
+ if (createdIndexes.has(columnKey))
994
+ continue;
995
+ const isRedundant = Array.from(createdIndexes).some((existing) => {
996
+ const existingCols = existing.split(",");
997
+ return existingCols.length >= columns.length && columns.every((col, idx) => col === existingCols[idx]);
998
+ });
999
+ if (!isRedundant) {
1000
+ await this.db.query(`CREATE INDEX IF NOT EXISTS "${indexName}" ON "${this.table}" (${columnList})`);
1001
+ createdIndexes.add(columnKey);
1002
+ }
1003
+ }
1004
+ this.#setup = true;
1005
+ return this.db;
1006
+ }
1007
+ mapTypeToSQL(typeDef) {
1008
+ const actualType = this.getNonNullType(typeDef);
1009
+ if (typeof actualType === "boolean") {
1010
+ return "TEXT /* boolean schema */";
1011
+ }
1012
+ if (actualType.contentEncoding === "blob")
1013
+ return "BYTEA";
1014
+ switch (actualType.type) {
1015
+ case "string":
1016
+ if (actualType.format === "date-time")
1017
+ return "TIMESTAMP";
1018
+ if (actualType.format === "date")
1019
+ return "DATE";
1020
+ if (actualType.format === "email")
1021
+ return "VARCHAR(255)";
1022
+ if (actualType.format === "uri")
1023
+ return "VARCHAR(2048)";
1024
+ if (actualType.format === "uuid")
1025
+ return "UUID";
1026
+ if (typeof actualType.maxLength === "number") {
1027
+ return `VARCHAR(${actualType.maxLength})`;
1028
+ }
1029
+ return "TEXT";
1030
+ case "number":
1031
+ case "integer":
1032
+ if (actualType.multipleOf === 1 || actualType.type === "integer") {
1033
+ if (typeof actualType.minimum === "number") {
1034
+ if (actualType.minimum >= 0) {
1035
+ if (typeof actualType.maximum === "number") {
1036
+ if (actualType.maximum <= 32767)
1037
+ return "SMALLINT";
1038
+ if (actualType.maximum <= 2147483647)
1039
+ return "INTEGER";
1040
+ }
1041
+ return "BIGINT";
1042
+ }
1043
+ }
1044
+ return "INTEGER";
1045
+ }
1046
+ if (actualType.format === "float")
1047
+ return "REAL";
1048
+ if (actualType.format === "double")
1049
+ return "DOUBLE PRECISION";
1050
+ if (typeof actualType.multipleOf === "number") {
1051
+ const decimalPlaces = String(actualType.multipleOf).split(".")[1]?.length || 0;
1052
+ if (decimalPlaces > 0) {
1053
+ return `NUMERIC(38, ${decimalPlaces})`;
1054
+ }
1055
+ }
1056
+ return "NUMERIC";
1057
+ case "boolean":
1058
+ return "BOOLEAN";
1059
+ case "array":
1060
+ if (actualType.items && typeof actualType.items === "object" && !Array.isArray(actualType.items)) {
1061
+ const itemType = this.mapTypeToSQL(actualType.items);
1062
+ const supportedArrayElementTypes = [
1063
+ "TEXT",
1064
+ "VARCHAR",
1065
+ "CHAR",
1066
+ "INTEGER",
1067
+ "SMALLINT",
1068
+ "BIGINT",
1069
+ "REAL",
1070
+ "DOUBLE PRECISION",
1071
+ "NUMERIC",
1072
+ "BOOLEAN",
1073
+ "UUID",
1074
+ "DATE",
1075
+ "TIMESTAMP"
1076
+ ];
1077
+ const isSupported = supportedArrayElementTypes.some((type) => itemType === type || itemType.startsWith(type + "(") && type !== "VARCHAR");
1078
+ if (isSupported) {
1079
+ return `${itemType}[]`;
1080
+ } else {
1081
+ return "JSONB /* complex array */";
1082
+ }
1083
+ }
1084
+ return "JSONB /* generic array */";
1085
+ case "object":
1086
+ return "JSONB /* object */";
1087
+ default:
1088
+ return "TEXT /* unknown type */";
1089
+ }
1090
+ }
1091
+ constructPrimaryKeyColumns($delimiter = "") {
1092
+ const cols = Object.entries(this.primaryKeySchema.properties).map(([key, typeDef]) => {
1093
+ const sqlType = this.mapTypeToSQL(typeDef);
1094
+ let constraints = "NOT NULL";
1095
+ if (this.shouldBeUnsigned(typeDef)) {
1096
+ constraints += ` CHECK (${$delimiter}${key}${$delimiter} >= 0)`;
1097
+ }
1098
+ return `${$delimiter}${key}${$delimiter} ${sqlType} ${constraints}`;
1099
+ }).join(", ");
1100
+ return cols;
1101
+ }
1102
+ constructValueColumns($delimiter = "") {
1103
+ const requiredSet = new Set(this.valueSchema.required ?? []);
1104
+ const cols = Object.entries(this.valueSchema.properties).map(([key, typeDef]) => {
1105
+ const sqlType = this.mapTypeToSQL(typeDef);
1106
+ const isRequired = requiredSet.has(key);
1107
+ const nullable = !isRequired || this.isNullable(typeDef);
1108
+ let constraints = nullable ? "NULL" : "NOT NULL";
1109
+ if (this.shouldBeUnsigned(typeDef)) {
1110
+ constraints += ` CHECK (${$delimiter}${key}${$delimiter} >= 0)`;
1111
+ }
1112
+ return `${$delimiter}${key}${$delimiter} ${sqlType} ${constraints}`;
1113
+ }).join(", ");
1114
+ if (cols.length > 0) {
1115
+ return `, ${cols}`;
1116
+ } else {
1117
+ return "";
1118
+ }
1119
+ }
1120
+ sqlToJsValue(column, value) {
1121
+ const typeDef = this.schema.properties[column];
1122
+ if (typeDef) {
1123
+ if (value === null && this.isNullable(typeDef)) {
1124
+ return null;
1125
+ }
1126
+ const actualType = this.getNonNullType(typeDef);
1127
+ if (typeof actualType !== "boolean" && (actualType.type === "number" || actualType.type === "integer")) {
1128
+ const v = value;
1129
+ if (typeof v === "number")
1130
+ return v;
1131
+ if (typeof v === "string") {
1132
+ const parsed = Number(v);
1133
+ if (!isNaN(parsed))
1134
+ return parsed;
1135
+ }
1136
+ }
1137
+ }
1138
+ return super.sqlToJsValue(column, value);
1139
+ }
1140
+ shouldBeUnsigned(typeDef) {
1141
+ const actualType = this.getNonNullType(typeDef);
1142
+ if (typeof actualType === "boolean") {
1143
+ return false;
1144
+ }
1145
+ if ((actualType.type === "number" || actualType.type === "integer") && typeof actualType.minimum === "number" && actualType.minimum >= 0) {
1146
+ return true;
1147
+ }
1148
+ return false;
1149
+ }
1150
+ async put(entity) {
1151
+ const db = await this.setupDatabase();
1152
+ const { key, value } = this.separateKeyValueFromCombined(entity);
1153
+ const sql = `
1154
+ INSERT INTO "${this.table}" (
1155
+ ${this.primaryKeyColumnList('"')} ${this.valueColumnList() ? ", " + this.valueColumnList('"') : ""}
1156
+ )
1157
+ VALUES (
1158
+ ${[...this.primaryKeyColumns(), ...this.valueColumns()].map((_, i) => `$${i + 1}`).join(", ")}
1159
+ )
1160
+ ${!this.valueColumnList() ? "" : `
1161
+ ON CONFLICT (${this.primaryKeyColumnList('"')}) DO UPDATE
1162
+ SET
1163
+ ${this.valueColumns().map((col, i) => `"${col}" = $${i + this.primaryKeyColumns().length + 1}`).join(", ")}
1164
+ `}
1165
+ RETURNING *
1166
+ `;
1167
+ const primaryKeyParams = this.getPrimaryKeyAsOrderedArray(key);
1168
+ const valueParams = this.getValueAsOrderedArray(value);
1169
+ const params = [...primaryKeyParams, ...valueParams];
1170
+ const result = await db.query(sql, params);
1171
+ const updatedEntity = result.rows[0];
1172
+ for (const key2 in this.schema.properties) {
1173
+ updatedEntity[key2] = this.sqlToJsValue(key2, updatedEntity[key2]);
1174
+ }
1175
+ this.events.emit("put", updatedEntity);
1176
+ return updatedEntity;
1177
+ }
1178
+ async putBulk(entities) {
1179
+ if (entities.length === 0)
1180
+ return [];
1181
+ const db = await this.setupDatabase();
1182
+ const allParams = [];
1183
+ const valuesPerRow = this.primaryKeyColumns().length + this.valueColumns().length;
1184
+ let paramIndex = 1;
1185
+ const valuesClauses = entities.map((entity) => {
1186
+ const { key, value } = this.separateKeyValueFromCombined(entity);
1187
+ const primaryKeyParams = this.getPrimaryKeyAsOrderedArray(key);
1188
+ const valueParams = this.getValueAsOrderedArray(value);
1189
+ const entityParams = [...primaryKeyParams, ...valueParams];
1190
+ allParams.push(...entityParams);
1191
+ const placeholders = Array(valuesPerRow).fill(0).map(() => `$${paramIndex++}`).join(", ");
1192
+ return `(${placeholders})`;
1193
+ }).join(", ");
1194
+ const sql = `
1195
+ INSERT INTO "${this.table}" (
1196
+ ${this.primaryKeyColumnList('"')} ${this.valueColumnList() ? ", " + this.valueColumnList('"') : ""}
1197
+ )
1198
+ VALUES ${valuesClauses}
1199
+ ${!this.valueColumnList() ? "" : `
1200
+ ON CONFLICT (${this.primaryKeyColumnList('"')}) DO UPDATE
1201
+ SET
1202
+ ${this.valueColumns().map((col) => {
1203
+ return `"${col}" = EXCLUDED."${col}"`;
1204
+ }).join(", ")}
1205
+ `}
1206
+ RETURNING *
1207
+ `;
1208
+ const result = await db.query(sql, allParams);
1209
+ const updatedEntities = result.rows.map((row) => {
1210
+ const entity = row;
1211
+ for (const key in this.schema.properties) {
1212
+ entity[key] = this.sqlToJsValue(key, entity[key]);
1213
+ }
1214
+ return entity;
1215
+ });
1216
+ for (const entity of updatedEntities) {
1217
+ this.events.emit("put", entity);
1218
+ }
1219
+ return updatedEntities;
1220
+ }
1221
+ async get(key) {
1222
+ const db = await this.setupDatabase();
1223
+ const whereClauses = this.primaryKeyColumns().map((discriminatorKey, i) => `"${discriminatorKey}" = $${i + 1}`).join(" AND ");
1224
+ const sql = `SELECT * FROM "${this.table}" WHERE ${whereClauses}`;
1225
+ const params = this.getPrimaryKeyAsOrderedArray(key);
1226
+ const result = await db.query(sql, params);
1227
+ let val;
1228
+ if (result.rows.length > 0) {
1229
+ val = result.rows[0];
1230
+ for (const key2 in this.schema.properties) {
1231
+ val[key2] = this.sqlToJsValue(key2, val[key2]);
1232
+ }
1233
+ } else {
1234
+ val = undefined;
1235
+ }
1236
+ this.events.emit("get", key, val);
1237
+ return val;
1238
+ }
1239
+ async search(key) {
1240
+ const db = await this.setupDatabase();
1241
+ const searchKeys = Object.keys(key);
1242
+ if (searchKeys.length === 0) {
1243
+ return;
1244
+ }
1245
+ const bestIndex = this.findBestMatchingIndex(searchKeys);
1246
+ if (!bestIndex) {
1247
+ throw new Error(`No suitable index found for the search criteria, searching for ['${searchKeys.join("', '")}'] with pk ['${this.primaryKeyNames.join("', '")}'] and indexes ['${this.indexes.join("', '")}']`);
1248
+ }
1249
+ const validColumns = [...this.primaryKeyColumns(), ...this.valueColumns()];
1250
+ const invalidColumns = searchKeys.filter((key2) => !validColumns.includes(key2));
1251
+ if (invalidColumns.length > 0) {
1252
+ throw new Error(`Invalid columns in search criteria: ${invalidColumns.join(", ")}`);
1253
+ }
1254
+ const whereClauses = Object.keys(key).map((key2, i) => `"${key2}" = $${i + 1}`).join(" AND ");
1255
+ const whereClauseValues = Object.entries(key).map(([k, v]) => this.jsToSqlValue(k, v));
1256
+ const sql = `SELECT * FROM "${this.table}" WHERE ${whereClauses}`;
1257
+ const result = await db.query(sql, whereClauseValues);
1258
+ if (result.rows.length > 0) {
1259
+ for (const row of result.rows) {
1260
+ for (const k in this.schema.properties) {
1261
+ row[k] = this.sqlToJsValue(k, row[k]);
1262
+ }
1263
+ }
1264
+ this.events.emit("search", key, result.rows);
1265
+ return result.rows;
1266
+ } else {
1267
+ this.events.emit("search", key, undefined);
1268
+ return;
1269
+ }
1270
+ }
1271
+ async delete(value) {
1272
+ const db = await this.setupDatabase();
1273
+ const { key } = this.separateKeyValueFromCombined(value);
1274
+ const whereClauses = this.primaryKeyColumns().map((key2, i) => `${key2} = $${i + 1}`).join(" AND ");
1275
+ const params = this.getPrimaryKeyAsOrderedArray(key);
1276
+ await db.query(`DELETE FROM "${this.table}" WHERE ${whereClauses}`, params);
1277
+ this.events.emit("delete", key);
1278
+ }
1279
+ async getAll() {
1280
+ const db = await this.setupDatabase();
1281
+ const sql = `SELECT * FROM "${this.table}"`;
1282
+ const result = await db.query(sql);
1283
+ if (result.rows.length > 0) {
1284
+ for (const row of result.rows) {
1285
+ for (const key in this.schema.properties) {
1286
+ row[key] = this.sqlToJsValue(key, row[key]);
1287
+ }
1288
+ }
1289
+ return result.rows;
1290
+ }
1291
+ return;
1292
+ }
1293
+ async deleteAll() {
1294
+ const db = await this.setupDatabase();
1295
+ await db.query(`DELETE FROM "${this.table}"`);
1296
+ this.events.emit("clearall");
1297
+ }
1298
+ async size() {
1299
+ const db = await this.setupDatabase();
1300
+ const result = await db.query(`SELECT COUNT(*) FROM "${this.table}"`);
1301
+ return parseInt(result.rows[0].count, 10);
1302
+ }
1303
+ generateWhereClause(column, operator = "=") {
1304
+ if (!(column in this.schema.properties)) {
1305
+ throw new Error(`Schema must have a ${String(column)} field to use deleteSearch`);
1306
+ }
1307
+ return `"${String(column)}" ${operator} $1`;
1308
+ }
1309
+ async deleteSearch(column, value, operator = "=") {
1310
+ const whereClause = this.generateWhereClause(column, operator);
1311
+ const db = await this.setupDatabase();
1312
+ await db.query(`DELETE FROM "${this.table}" WHERE ${whereClause}`, [
1313
+ this.jsToSqlValue(column, value)
1314
+ ]);
1315
+ this.events.emit("delete", column);
1316
+ }
1317
+ }
1318
+ // src/tabular/SqliteTabularRepository.ts
1319
+ import { Sqlite } from "@workglow/sqlite";
1320
+ import { createServiceToken as createServiceToken10 } from "@workglow/util";
1321
+ var SQLITE_TABULAR_REPOSITORY = createServiceToken10("storage.tabularRepository.sqlite");
1322
+ var Database = Sqlite.Database;
1323
+
1324
+ class SqliteTabularRepository extends BaseSqlTabularRepository {
1325
+ db;
1326
+ constructor(dbOrPath, table = "tabular_store", schema, primaryKeyNames, indexes = []) {
1327
+ super(table, schema, primaryKeyNames, indexes);
1328
+ if (typeof dbOrPath === "string") {
1329
+ this.db = new Database(dbOrPath);
1330
+ } else {
1331
+ this.db = dbOrPath;
1332
+ }
1333
+ }
1334
+ #setup = false;
1335
+ async setupDatabase() {
1336
+ if (this.#setup)
1337
+ return this.db;
1338
+ const sql = `
1339
+ CREATE TABLE IF NOT EXISTS \`${this.table}\` (
1340
+ ${this.constructPrimaryKeyColumns()} ${this.constructValueColumns()},
1341
+ PRIMARY KEY (${this.primaryKeyColumnList()})
1342
+ )
1343
+ `;
1344
+ this.db.exec(sql);
1345
+ const pkColumns = this.primaryKeyColumns();
1346
+ const createdIndexes = new Set;
1347
+ for (const searchSpec of this.indexes) {
1348
+ const columns = Array.isArray(searchSpec) ? searchSpec : [searchSpec];
1349
+ if (columns.length <= pkColumns.length) {
1350
+ const isPkPrefix = columns.every((col, idx) => col === pkColumns[idx]);
1351
+ if (isPkPrefix)
1352
+ continue;
1353
+ }
1354
+ const indexName = `${this.table}_${columns.join("_")}`;
1355
+ const columnList = columns.map((col) => `\`${String(col)}\``).join(", ");
1356
+ const columnKey = columns.join(",");
1357
+ if (createdIndexes.has(columnKey))
1358
+ continue;
1359
+ const isRedundant = Array.from(createdIndexes).some((existing) => {
1360
+ const existingCols = existing.split(",");
1361
+ return existingCols.length >= columns.length && columns.every((col, idx) => col === existingCols[idx]);
1362
+ });
1363
+ if (!isRedundant) {
1364
+ this.db.exec(`CREATE INDEX IF NOT EXISTS \`${indexName}\` ON \`${this.table}\` (${columnList})`);
1365
+ createdIndexes.add(columnKey);
1366
+ }
1367
+ }
1368
+ this.#setup = true;
1369
+ return this.db;
1370
+ }
1371
+ jsToSqlValue(column, value) {
1372
+ const typeDef = this.schema.properties[column];
1373
+ if (typeDef) {
1374
+ const actualType = this.getNonNullType(typeDef);
1375
+ if (typeof actualType !== "boolean" && actualType.type === "boolean") {
1376
+ if (value === null && this.isNullable(typeDef)) {
1377
+ return null;
1378
+ }
1379
+ const v = value;
1380
+ if (typeof v === "boolean")
1381
+ return v ? 1 : 0;
1382
+ if (typeof v === "number")
1383
+ return v ? 1 : 0;
1384
+ if (typeof v === "string")
1385
+ return v === "1" || v.toLowerCase() === "true" ? 1 : 0;
1386
+ }
1387
+ }
1388
+ return super.jsToSqlValue(column, value);
1389
+ }
1390
+ sqlToJsValue(column, value) {
1391
+ const typeDef = this.schema.properties[column];
1392
+ if (typeDef) {
1393
+ if (value === null && this.isNullable(typeDef)) {
1394
+ return null;
1395
+ }
1396
+ const actualType = this.getNonNullType(typeDef);
1397
+ if (typeof actualType !== "boolean" && actualType.type === "boolean") {
1398
+ const v = value;
1399
+ if (typeof v === "boolean")
1400
+ return v;
1401
+ if (typeof v === "number")
1402
+ return v !== 0;
1403
+ if (typeof v === "string")
1404
+ return v === "1" || v.toLowerCase() === "true";
1405
+ }
1406
+ }
1407
+ return super.sqlToJsValue(column, value);
1408
+ }
1409
+ mapTypeToSQL(typeDef) {
1410
+ const actualType = this.getNonNullType(typeDef);
1411
+ if (typeof actualType === "boolean") {
1412
+ return "TEXT /* boolean schema */";
1413
+ }
1414
+ if (actualType.contentEncoding === "blob")
1415
+ return "BLOB";
1416
+ switch (actualType.type) {
1417
+ case "string":
1418
+ if (actualType.format === "date-time")
1419
+ return "TEXT";
1420
+ if (actualType.format === "date")
1421
+ return "TEXT";
1422
+ if (typeof actualType.maxLength === "number") {
1423
+ return `TEXT /* VARCHAR(${actualType.maxLength}) */`;
1424
+ }
1425
+ return "TEXT";
1426
+ case "number":
1427
+ case "integer":
1428
+ if (actualType.multipleOf === 1 || actualType.type === "integer") {
1429
+ return "INTEGER";
1430
+ }
1431
+ return "REAL";
1432
+ case "boolean":
1433
+ return "INTEGER";
1434
+ case "array":
1435
+ case "object":
1436
+ return "TEXT /* JSON */";
1437
+ default:
1438
+ return "TEXT /* unknown type */";
1439
+ }
1440
+ }
1441
+ async put(entity) {
1442
+ const db = await this.setupDatabase();
1443
+ const { key, value } = this.separateKeyValueFromCombined(entity);
1444
+ const sql = `
1445
+ INSERT OR REPLACE INTO \`${this.table}\` (${this.primaryKeyColumnList()} ${this.valueColumnList() ? ", " + this.valueColumnList() : ""})
1446
+ VALUES (
1447
+ ${this.primaryKeyColumns().map((i) => "?")}
1448
+ ${this.valueColumns().length > 0 ? ", " + this.valueColumns().map((i) => "?") : ""}
1449
+ )
1450
+ RETURNING *
1451
+ `;
1452
+ const stmt = db.prepare(sql);
1453
+ const primaryKeyParams = this.getPrimaryKeyAsOrderedArray(key);
1454
+ const valueParams = this.getValueAsOrderedArray(value);
1455
+ const params = [...primaryKeyParams, ...valueParams];
1456
+ const updatedEntity = stmt.get(...params);
1457
+ for (const k in this.schema.properties) {
1458
+ updatedEntity[k] = this.sqlToJsValue(k, updatedEntity[k]);
1459
+ }
1460
+ this.events.emit("put", updatedEntity);
1461
+ return updatedEntity;
1462
+ }
1463
+ async putBulk(entities) {
1464
+ if (entities.length === 0)
1465
+ return [];
1466
+ const db = await this.setupDatabase();
1467
+ const updatedEntities = [];
1468
+ const transaction = db.transaction((entitiesToInsert) => {
1469
+ for (const entity of entitiesToInsert) {
1470
+ const { key, value } = this.separateKeyValueFromCombined(entity);
1471
+ const sql = `
1472
+ INSERT OR REPLACE INTO \`${this.table}\` (${this.primaryKeyColumnList()} ${this.valueColumnList() ? ", " + this.valueColumnList() : ""})
1473
+ VALUES (
1474
+ ${this.primaryKeyColumns().map(() => "?").join(", ")}
1475
+ ${this.valueColumns().length > 0 ? ", " + this.valueColumns().map(() => "?").join(", ") : ""}
1476
+ )
1477
+ RETURNING *
1478
+ `;
1479
+ const stmt = db.prepare(sql);
1480
+ const primaryKeyParams = this.getPrimaryKeyAsOrderedArray(key);
1481
+ const valueParams = this.getValueAsOrderedArray(value);
1482
+ const params = [...primaryKeyParams, ...valueParams];
1483
+ const updatedEntity = stmt.get(...params);
1484
+ for (const k in this.schema.properties) {
1485
+ updatedEntity[k] = this.sqlToJsValue(k, updatedEntity[k]);
1486
+ }
1487
+ updatedEntities.push(updatedEntity);
1488
+ }
1489
+ });
1490
+ transaction(entities);
1491
+ for (const entity of updatedEntities) {
1492
+ this.events.emit("put", entity);
1493
+ }
1494
+ return updatedEntities;
1495
+ }
1496
+ async get(key) {
1497
+ const db = await this.setupDatabase();
1498
+ const whereClauses = this.primaryKeyColumns().map((key2) => `\`${key2}\` = ?`).join(" AND ");
1499
+ const sql = `
1500
+ SELECT * FROM \`${this.table}\` WHERE ${whereClauses}
1501
+ `;
1502
+ const stmt = db.prepare(sql);
1503
+ const params = this.getPrimaryKeyAsOrderedArray(key);
1504
+ const value = stmt.get(...params);
1505
+ if (value) {
1506
+ for (const k in this.schema.properties) {
1507
+ value[k] = this.sqlToJsValue(k, value[k]);
1508
+ }
1509
+ this.events.emit("get", key, value);
1510
+ return value;
1511
+ } else {
1512
+ this.events.emit("get", key, undefined);
1513
+ return;
1514
+ }
1515
+ }
1516
+ async search(key) {
1517
+ const db = await this.setupDatabase();
1518
+ const searchKeys = Object.keys(key);
1519
+ if (searchKeys.length === 0) {
1520
+ return;
1521
+ }
1522
+ const bestIndex = super.findBestMatchingIndex(searchKeys);
1523
+ if (!bestIndex) {
1524
+ throw new Error(`No suitable index found for the search criteria, searching for ['${searchKeys.join("', '")}'] with pk ['${this.primaryKeyNames.join("', '")}'] and indexes ['${this.indexes.join("', '")}']`);
1525
+ }
1526
+ const validColumns = [...this.primaryKeyColumns(), ...this.valueColumns()];
1527
+ const invalidColumns = searchKeys.filter((key2) => !validColumns.includes(key2));
1528
+ if (invalidColumns.length > 0) {
1529
+ throw new Error(`Invalid columns in search criteria: ${invalidColumns.join(", ")}`);
1530
+ }
1531
+ const whereClauses = Object.keys(key).map((key2, i) => `"${key2}" = ?`).join(" AND ");
1532
+ const whereClauseValues = Object.entries(key).map(([k, v]) => this.jsToSqlValue(k, v));
1533
+ const sql = `SELECT * FROM \`${this.table}\` WHERE ${whereClauses}`;
1534
+ const stmt = db.prepare(sql);
1535
+ const result = stmt.all(...whereClauseValues);
1536
+ if (result.length > 0) {
1537
+ for (const row of result) {
1538
+ for (const k in this.schema.properties) {
1539
+ row[k] = this.sqlToJsValue(k, row[k]);
1540
+ }
1541
+ }
1542
+ this.events.emit("search", key, result);
1543
+ return result;
1544
+ } else {
1545
+ this.events.emit("search", key, undefined);
1546
+ return;
1547
+ }
1548
+ }
1549
+ async delete(key) {
1550
+ const db = await this.setupDatabase();
1551
+ const whereClauses = this.primaryKeyColumns().map((key2) => `${key2} = ?`).join(" AND ");
1552
+ const params = this.getPrimaryKeyAsOrderedArray(key);
1553
+ const stmt = db.prepare(`DELETE FROM \`${this.table}\` WHERE ${whereClauses}`);
1554
+ stmt.run(...params);
1555
+ this.events.emit("delete", key);
1556
+ }
1557
+ async getAll() {
1558
+ const db = await this.setupDatabase();
1559
+ const sql = `SELECT * FROM \`${this.table}\``;
1560
+ const stmt = db.prepare(sql);
1561
+ const value = stmt.all();
1562
+ if (!value.length)
1563
+ return;
1564
+ for (const row of value) {
1565
+ for (const k in this.schema.properties) {
1566
+ row[k] = this.sqlToJsValue(k, row[k]);
1567
+ }
1568
+ }
1569
+ return value;
1570
+ }
1571
+ async deleteAll() {
1572
+ const db = await this.setupDatabase();
1573
+ db.exec(`DELETE FROM \`${this.table}\``);
1574
+ this.events.emit("clearall");
1575
+ }
1576
+ async size() {
1577
+ const db = await this.setupDatabase();
1578
+ const stmt = db.prepare(`
1579
+ SELECT COUNT(*) AS count FROM \`${this.table}\`
1580
+ `);
1581
+ return stmt.get()?.count || 0;
1582
+ }
1583
+ generateWhereClause(column, operator = "=") {
1584
+ if (!(column in this.schema.properties)) {
1585
+ throw new Error(`Schema must have a ${String(column)} field to use deleteSearch`);
1586
+ }
1587
+ return `${String(column)} ${operator} ?`;
1588
+ }
1589
+ async deleteSearch(column, value, operator = "=") {
1590
+ const db = await this.setupDatabase();
1591
+ const whereClause = this.generateWhereClause(column, operator);
1592
+ const stmt = db.prepare(`DELETE FROM \`${this.table}\` WHERE ${whereClause}`);
1593
+ stmt.run(this.jsToSqlValue(column, value));
1594
+ this.events.emit("delete", column);
1595
+ }
1596
+ }
1597
+ // src/tabular/SupabaseTabularRepository.ts
1598
+ import { createServiceToken as createServiceToken11 } from "@workglow/util";
1599
+ var SUPABASE_TABULAR_REPOSITORY = createServiceToken11("storage.tabularRepository.supabase");
1600
+
1601
+ class SupabaseTabularRepository extends BaseSqlTabularRepository {
1602
+ client;
1603
+ constructor(client, table = "tabular_store", schema, primaryKeyNames, indexes = []) {
1604
+ super(table, schema, primaryKeyNames, indexes);
1605
+ this.client = client;
1606
+ }
1607
+ isSetup = true;
1608
+ async setupDatabase() {
1609
+ if (this.isSetup) {
1610
+ return this.client;
1611
+ }
1612
+ const sql = `
1613
+ CREATE TABLE IF NOT EXISTS "${this.table}" (
1614
+ ${this.constructPrimaryKeyColumns('"')} ${this.constructValueColumns('"')},
1615
+ PRIMARY KEY (${this.primaryKeyColumnList()})
1616
+ )
1617
+ `;
1618
+ const { error } = await this.client.rpc("exec_sql", { query: sql });
1619
+ if (error && !error.message.includes("already exists")) {
1620
+ throw error;
1621
+ }
1622
+ const pkColumns = this.primaryKeyColumns();
1623
+ const createdIndexes = new Set;
1624
+ for (const columns of this.indexes) {
1625
+ if (columns.length <= pkColumns.length) {
1626
+ const isPkPrefix = columns.every((col, idx) => col === pkColumns[idx]);
1627
+ if (isPkPrefix)
1628
+ continue;
1629
+ }
1630
+ const indexName = `${this.table}_${columns.join("_")}`;
1631
+ const columnList = columns.map((col) => `"${String(col)}"`).join(", ");
1632
+ const columnKey = columns.join(",");
1633
+ if (createdIndexes.has(columnKey))
1634
+ continue;
1635
+ const isRedundant = Array.from(createdIndexes).some((existing) => {
1636
+ const existingCols = existing.split(",");
1637
+ return existingCols.length >= columns.length && columns.every((col, idx) => col === existingCols[idx]);
1638
+ });
1639
+ if (!isRedundant) {
1640
+ const indexSql = `CREATE INDEX IF NOT EXISTS "${indexName}" ON "${this.table}" (${columnList})`;
1641
+ const { error: indexError } = await this.client.rpc("exec_sql", { query: indexSql });
1642
+ if (indexError && !indexError.message.includes("already exists")) {
1643
+ console.warn(`Failed to create index ${indexName}:`, indexError);
1644
+ }
1645
+ createdIndexes.add(columnKey);
1646
+ }
1647
+ }
1648
+ this.isSetup = true;
1649
+ return this.client;
1650
+ }
1651
+ mapTypeToSQL(typeDef) {
1652
+ const actualType = this.getNonNullType(typeDef);
1653
+ if (typeof actualType === "boolean") {
1654
+ return "TEXT /* boolean schema */";
1655
+ }
1656
+ if (actualType.contentEncoding === "blob")
1657
+ return "BYTEA";
1658
+ switch (actualType.type) {
1659
+ case "string":
1660
+ if (actualType.format === "date-time")
1661
+ return "TIMESTAMP";
1662
+ if (actualType.format === "date")
1663
+ return "DATE";
1664
+ if (actualType.format === "email")
1665
+ return "VARCHAR(255)";
1666
+ if (actualType.format === "uri")
1667
+ return "VARCHAR(2048)";
1668
+ if (actualType.format === "uuid")
1669
+ return "UUID";
1670
+ if (typeof actualType.maxLength === "number") {
1671
+ return `VARCHAR(${actualType.maxLength})`;
1672
+ }
1673
+ return "TEXT";
1674
+ case "number":
1675
+ case "integer":
1676
+ if (actualType.multipleOf === 1 || actualType.type === "integer") {
1677
+ if (typeof actualType.minimum === "number") {
1678
+ if (actualType.minimum >= 0) {
1679
+ if (typeof actualType.maximum === "number") {
1680
+ if (actualType.maximum <= 32767)
1681
+ return "SMALLINT";
1682
+ if (actualType.maximum <= 2147483647)
1683
+ return "INTEGER";
1684
+ }
1685
+ return "BIGINT";
1686
+ }
1687
+ }
1688
+ return "INTEGER";
1689
+ }
1690
+ if (actualType.format === "float")
1691
+ return "REAL";
1692
+ if (actualType.format === "double")
1693
+ return "DOUBLE PRECISION";
1694
+ if (typeof actualType.multipleOf === "number") {
1695
+ const decimalPlaces = String(actualType.multipleOf).split(".")[1]?.length || 0;
1696
+ if (decimalPlaces > 0) {
1697
+ return `NUMERIC(38, ${decimalPlaces})`;
1698
+ }
1699
+ }
1700
+ return "NUMERIC";
1701
+ case "boolean":
1702
+ return "BOOLEAN";
1703
+ case "array":
1704
+ if (actualType.items && typeof actualType.items === "object" && !Array.isArray(actualType.items)) {
1705
+ const itemType = this.mapTypeToSQL(actualType.items);
1706
+ const supportedArrayElementTypes = [
1707
+ "TEXT",
1708
+ "VARCHAR",
1709
+ "CHAR",
1710
+ "INTEGER",
1711
+ "SMALLINT",
1712
+ "BIGINT",
1713
+ "REAL",
1714
+ "DOUBLE PRECISION",
1715
+ "NUMERIC",
1716
+ "BOOLEAN",
1717
+ "UUID",
1718
+ "DATE",
1719
+ "TIMESTAMP"
1720
+ ];
1721
+ const isSupported = supportedArrayElementTypes.some((type) => itemType === type || itemType.startsWith(type + "(") && type !== "VARCHAR");
1722
+ if (isSupported) {
1723
+ return `${itemType}[]`;
1724
+ } else {
1725
+ return "JSONB /* complex array */";
1726
+ }
1727
+ }
1728
+ return "JSONB /* generic array */";
1729
+ case "object":
1730
+ return "JSONB /* object */";
1731
+ default:
1732
+ return "TEXT /* unknown type */";
1733
+ }
1734
+ }
1735
+ constructPrimaryKeyColumns($delimiter = "") {
1736
+ const cols = Object.entries(this.primaryKeySchema.properties).map(([key, typeDef]) => {
1737
+ const sqlType = this.mapTypeToSQL(typeDef);
1738
+ let constraints = "NOT NULL";
1739
+ if (this.shouldBeUnsigned(typeDef)) {
1740
+ constraints += ` CHECK (${$delimiter}${key}${$delimiter} >= 0)`;
1741
+ }
1742
+ return `${$delimiter}${key}${$delimiter} ${sqlType} ${constraints}`;
1743
+ }).join(", ");
1744
+ return cols;
1745
+ }
1746
+ constructValueColumns($delimiter = "") {
1747
+ const delimiter = $delimiter || '"';
1748
+ const requiredSet = new Set(this.valueSchema.required ?? []);
1749
+ const cols = Object.entries(this.valueSchema.properties).map(([key, typeDef]) => {
1750
+ const sqlType = this.mapTypeToSQL(typeDef);
1751
+ const isRequired = requiredSet.has(key);
1752
+ const nullable = !isRequired || this.isNullable(typeDef);
1753
+ let constraints = nullable ? "NULL" : "NOT NULL";
1754
+ if (this.shouldBeUnsigned(typeDef)) {
1755
+ constraints += ` CHECK (${delimiter}${key}${delimiter} >= 0)`;
1756
+ }
1757
+ return `${delimiter}${key}${delimiter} ${sqlType} ${constraints}`;
1758
+ }).join(", ");
1759
+ if (cols.length > 0) {
1760
+ return `, ${cols}`;
1761
+ } else {
1762
+ return "";
1763
+ }
1764
+ }
1765
+ sqlToJsValue(column, value) {
1766
+ const typeDef = this.schema.properties[column];
1767
+ if (typeDef) {
1768
+ if (value === null && this.isNullable(typeDef)) {
1769
+ return null;
1770
+ }
1771
+ const actualType = this.getNonNullType(typeDef);
1772
+ if (typeof actualType !== "boolean" && (actualType.type === "number" || actualType.type === "integer")) {
1773
+ const v = value;
1774
+ if (typeof v === "number")
1775
+ return v;
1776
+ if (typeof v === "string") {
1777
+ const parsed = Number(v);
1778
+ if (!isNaN(parsed))
1779
+ return parsed;
1780
+ }
1781
+ }
1782
+ }
1783
+ return super.sqlToJsValue(column, value);
1784
+ }
1785
+ shouldBeUnsigned(typeDef) {
1786
+ const actualType = this.getNonNullType(typeDef);
1787
+ if (typeof actualType === "boolean") {
1788
+ return false;
1789
+ }
1790
+ if ((actualType.type === "number" || actualType.type === "integer") && typeof actualType.minimum === "number" && actualType.minimum >= 0) {
1791
+ return true;
1792
+ }
1793
+ return false;
1794
+ }
1795
+ async put(entity) {
1796
+ await this.setupDatabase();
1797
+ const normalizedEntity = { ...entity };
1798
+ const requiredSet = new Set(this.valueSchema.required ?? []);
1799
+ for (const key in this.valueSchema.properties) {
1800
+ if (!(key in normalizedEntity) || normalizedEntity[key] === undefined) {
1801
+ if (!requiredSet.has(key)) {
1802
+ normalizedEntity[key] = null;
1803
+ }
1804
+ }
1805
+ }
1806
+ const { data, error } = await this.client.from(this.table).upsert(normalizedEntity, { onConflict: this.primaryKeyColumnList() }).select().single();
1807
+ if (error)
1808
+ throw error;
1809
+ const updatedEntity = data;
1810
+ for (const key in this.schema.properties) {
1811
+ updatedEntity[key] = this.sqlToJsValue(key, updatedEntity[key]);
1812
+ }
1813
+ this.events.emit("put", updatedEntity);
1814
+ return updatedEntity;
1815
+ }
1816
+ async putBulk(entities) {
1817
+ if (entities.length === 0)
1818
+ return [];
1819
+ await this.setupDatabase();
1820
+ const requiredSet = new Set(this.valueSchema.required ?? []);
1821
+ const normalizedEntities = entities.map((entity) => {
1822
+ const normalized = { ...entity };
1823
+ for (const key in this.valueSchema.properties) {
1824
+ if (!(key in normalized) || normalized[key] === undefined) {
1825
+ if (!requiredSet.has(key)) {
1826
+ normalized[key] = null;
1827
+ }
1828
+ }
1829
+ }
1830
+ return normalized;
1831
+ });
1832
+ const { data, error } = await this.client.from(this.table).upsert(normalizedEntities, { onConflict: this.primaryKeyColumnList() }).select();
1833
+ if (error)
1834
+ throw error;
1835
+ const updatedEntities = data;
1836
+ for (const entity of updatedEntities) {
1837
+ for (const key in this.schema.properties) {
1838
+ entity[key] = this.sqlToJsValue(key, entity[key]);
1839
+ }
1840
+ this.events.emit("put", entity);
1841
+ }
1842
+ return updatedEntities;
1843
+ }
1844
+ async get(key) {
1845
+ await this.setupDatabase();
1846
+ let query = this.client.from(this.table).select("*");
1847
+ for (const pkName of this.primaryKeyNames) {
1848
+ query = query.eq(String(pkName), key[pkName]);
1849
+ }
1850
+ const { data, error } = await query.single();
1851
+ if (error) {
1852
+ if (error.code === "PGRST116") {
1853
+ this.events.emit("get", key, undefined);
1854
+ return;
1855
+ }
1856
+ throw error;
1857
+ }
1858
+ const val = data;
1859
+ if (val) {
1860
+ for (const key2 in this.schema.properties) {
1861
+ val[key2] = this.sqlToJsValue(key2, val[key2]);
1862
+ }
1863
+ }
1864
+ this.events.emit("get", key, val);
1865
+ return val;
1866
+ }
1867
+ async search(searchCriteria) {
1868
+ await this.setupDatabase();
1869
+ const searchKeys = Object.keys(searchCriteria);
1870
+ if (searchKeys.length === 0) {
1871
+ return;
1872
+ }
1873
+ const bestIndex = this.findBestMatchingIndex(searchKeys);
1874
+ if (!bestIndex) {
1875
+ throw new Error(`No suitable index found for the search criteria, searching for ['${searchKeys.join("', '")}'] with pk ['${this.primaryKeyNames.join("', '")}'] and indexes ['${this.indexes.join("', '")}']`);
1876
+ }
1877
+ const validColumns = [...this.primaryKeyColumns(), ...this.valueColumns()];
1878
+ const invalidColumns = searchKeys.filter((key) => !validColumns.includes(key));
1879
+ if (invalidColumns.length > 0) {
1880
+ throw new Error(`Invalid columns in search criteria: ${invalidColumns.join(", ")}`);
1881
+ }
1882
+ let query = this.client.from(this.table).select("*");
1883
+ for (const [key, value] of Object.entries(searchCriteria)) {
1884
+ query = query.eq(key, value);
1885
+ }
1886
+ const { data, error } = await query;
1887
+ if (error)
1888
+ throw error;
1889
+ if (data && data.length > 0) {
1890
+ for (const row of data) {
1891
+ for (const key in this.schema.properties) {
1892
+ row[key] = this.sqlToJsValue(key, row[key]);
1893
+ }
1894
+ }
1895
+ this.events.emit("search", searchCriteria, data);
1896
+ return data;
1897
+ } else {
1898
+ this.events.emit("search", searchCriteria, undefined);
1899
+ return;
1900
+ }
1901
+ }
1902
+ async delete(value) {
1903
+ await this.setupDatabase();
1904
+ const { key } = this.separateKeyValueFromCombined(value);
1905
+ let query = this.client.from(this.table).delete();
1906
+ for (const pkName of this.primaryKeyNames) {
1907
+ query = query.eq(String(pkName), key[pkName]);
1908
+ }
1909
+ const { error } = await query;
1910
+ if (error)
1911
+ throw error;
1912
+ this.events.emit("delete", key);
1913
+ }
1914
+ async getAll() {
1915
+ await this.setupDatabase();
1916
+ const { data, error } = await this.client.from(this.table).select("*");
1917
+ if (error)
1918
+ throw error;
1919
+ if (data && data.length) {
1920
+ for (const row of data) {
1921
+ for (const key in this.schema.properties) {
1922
+ row[key] = this.sqlToJsValue(key, row[key]);
1923
+ }
1924
+ }
1925
+ return data;
1926
+ }
1927
+ return;
1928
+ }
1929
+ async deleteAll() {
1930
+ await this.setupDatabase();
1931
+ const firstPkColumn = this.primaryKeyNames[0];
1932
+ const { error } = await this.client.from(this.table).delete().neq(String(firstPkColumn), null);
1933
+ if (error)
1934
+ throw error;
1935
+ this.events.emit("clearall");
1936
+ }
1937
+ async size() {
1938
+ await this.setupDatabase();
1939
+ const { count, error } = await this.client.from(this.table).select("*", { count: "exact", head: true });
1940
+ if (error)
1941
+ throw error;
1942
+ return count ?? 0;
1943
+ }
1944
+ generateWhereClause(column, operator = "=") {
1945
+ if (!(column in this.schema.properties)) {
1946
+ throw new Error(`Schema must have a ${String(column)} field to use deleteSearch`);
1947
+ }
1948
+ return `${String(column)} ${operator} $1`;
1949
+ }
1950
+ async deleteSearch(column, value, operator = "=") {
1951
+ await this.setupDatabase();
1952
+ let query = this.client.from(this.table).delete();
1953
+ switch (operator) {
1954
+ case "=":
1955
+ query = query.eq(String(column), value);
1956
+ break;
1957
+ case "<":
1958
+ query = query.lt(String(column), value);
1959
+ break;
1960
+ case "<=":
1961
+ query = query.lte(String(column), value);
1962
+ break;
1963
+ case ">":
1964
+ query = query.gt(String(column), value);
1965
+ break;
1966
+ case ">=":
1967
+ query = query.gte(String(column), value);
1968
+ break;
1969
+ }
1970
+ const { error } = await query;
1971
+ if (error)
1972
+ throw error;
1973
+ this.events.emit("delete", column);
1974
+ }
1975
+ }
1976
+ // src/kv/FsFolderJsonKvRepository.ts
1977
+ import { createServiceToken as createServiceToken12 } from "@workglow/util";
1978
+ var FS_FOLDER_JSON_KV_REPOSITORY = createServiceToken12("storage.kvRepository.fsFolderJson");
1979
+
1980
+ class FsFolderJsonKvRepository extends KvViaTabularRepository {
1981
+ folderPath;
1982
+ tabularRepository;
1983
+ constructor(folderPath, keySchema = { type: "string" }, valueSchema = {}) {
1984
+ super(keySchema, valueSchema);
1985
+ this.folderPath = folderPath;
1986
+ this.tabularRepository = new FsFolderTabularRepository(folderPath, DefaultKeyValueSchema, DefaultKeyValueKey);
1987
+ }
1988
+ }
1989
+ // src/kv/FsFolderKvRepository.ts
1990
+ import { createServiceToken as createServiceToken13 } from "@workglow/util";
1991
+ import { mkdir as mkdir2, readFile as readFile2, rm as rm2, unlink, writeFile as writeFile2 } from "fs/promises";
1992
+ import path2 from "path";
1993
+ var FS_FOLDER_KV_REPOSITORY = createServiceToken13("storage.kvRepository.fsFolder");
1994
+
1995
+ class FsFolderKvRepository extends KvRepository {
1996
+ folderPath;
1997
+ pathWriter;
1998
+ constructor(folderPath, pathWriter, keySchema = { type: "string" }, valueSchema = { contentEncoding: "blob" }) {
1999
+ super(keySchema, valueSchema);
2000
+ this.folderPath = folderPath;
2001
+ this.pathWriter = pathWriter;
2002
+ }
2003
+ async setupDirectory() {
2004
+ try {
2005
+ await mkdir2(this.folderPath, { recursive: true });
2006
+ } catch (error) {
2007
+ await new Promise((resolve) => setTimeout(resolve, 0));
2008
+ try {
2009
+ await mkdir2(this.folderPath, { recursive: true });
2010
+ } catch {}
2011
+ }
2012
+ }
2013
+ async put(key, value) {
2014
+ const localPath = path2.join(this.folderPath, this.pathWriter(key).replaceAll("..", "_"));
2015
+ let content;
2016
+ const schemaType = typeof this.valueSchema === "object" && this.valueSchema !== null && "type" in this.valueSchema ? this.valueSchema.type : undefined;
2017
+ if (value === null) {
2018
+ content = "";
2019
+ } else if (schemaType === "object") {
2020
+ content = JSON.stringify(value);
2021
+ } else if (typeof value === "object") {
2022
+ content = JSON.stringify(value);
2023
+ } else {
2024
+ content = String(value);
2025
+ }
2026
+ await mkdir2(path2.dirname(localPath), { recursive: true });
2027
+ await writeFile2(localPath, content);
2028
+ }
2029
+ async putBulk(items) {
2030
+ await this.setupDirectory();
2031
+ await Promise.all(items.map(async ({ key, value }) => this.put(key, value)));
2032
+ }
2033
+ async get(key) {
2034
+ const localPath = path2.join(this.folderPath, this.pathWriter(key).replaceAll("..", "_"));
2035
+ const typeDef = this.valueSchema;
2036
+ try {
2037
+ const encoding = typeof typeDef === "object" && typeDef !== null && "contentEncoding" in typeDef && typeDef.contentEncoding === "blob" ? "binary" : "utf-8";
2038
+ const content = (await readFile2(localPath, { encoding })).toString().trim();
2039
+ if (encoding === "utf-8") {
2040
+ const schemaType = typeof typeDef === "object" && typeDef !== null && "type" in typeDef ? typeDef.type : undefined;
2041
+ if (schemaType === "object" || content.startsWith("{") && content.endsWith("}") || content.startsWith("[") && content.endsWith("]")) {
2042
+ try {
2043
+ return JSON.parse(content);
2044
+ } catch (e) {
2045
+ return content;
2046
+ }
2047
+ }
2048
+ }
2049
+ return content;
2050
+ } catch (error) {
2051
+ return;
2052
+ }
2053
+ }
2054
+ async delete(key) {
2055
+ const localPath = path2.join(this.folderPath, this.pathWriter(key).replaceAll("..", "_"));
2056
+ await unlink(localPath);
2057
+ }
2058
+ async getAll() {
2059
+ throw new Error("Not implemented");
2060
+ }
2061
+ async deleteAll() {
2062
+ const localPath = path2.join(this.folderPath);
2063
+ await rm2(localPath, { recursive: true });
2064
+ }
2065
+ async size() {
2066
+ throw new Error("Not implemented");
2067
+ }
2068
+ }
2069
+ // src/kv/PostgresKvRepository.ts
2070
+ import { createServiceToken as createServiceToken14 } from "@workglow/util";
2071
+ var POSTGRES_KV_REPOSITORY = createServiceToken14("storage.kvRepository.postgres");
2072
+
2073
+ class PostgresKvRepository extends KvViaTabularRepository {
2074
+ db;
2075
+ dbName;
2076
+ tabularRepository;
2077
+ constructor(db, dbName, keySchema = { type: "string" }, valueSchema = {}) {
2078
+ super(keySchema, valueSchema);
2079
+ this.db = db;
2080
+ this.dbName = dbName;
2081
+ this.tabularRepository = new PostgresTabularRepository(db, dbName, DefaultKeyValueSchema, DefaultKeyValueKey);
2082
+ }
2083
+ }
2084
+ // src/kv/SqliteKvRepository.ts
2085
+ import { createServiceToken as createServiceToken15 } from "@workglow/util";
2086
+ var SQLITE_KV_REPOSITORY = createServiceToken15("storage.kvRepository.sqlite");
2087
+
2088
+ class SqliteKvRepository extends KvViaTabularRepository {
2089
+ db;
2090
+ dbName;
2091
+ tabularRepository;
2092
+ constructor(db, dbName, keySchema = { type: "string" }, valueSchema = {}) {
2093
+ super(keySchema, valueSchema);
2094
+ this.db = db;
2095
+ this.dbName = dbName;
2096
+ this.tabularRepository = new SqliteTabularRepository(db, dbName, DefaultKeyValueSchema, DefaultKeyValueKey);
2097
+ }
2098
+ }
2099
+ // src/kv/SupabaseKvRepository.ts
2100
+ import { createServiceToken as createServiceToken16 } from "@workglow/util";
2101
+ var SUPABASE_KV_REPOSITORY = createServiceToken16("storage.kvRepository.supabase");
2102
+
2103
+ class SupabaseKvRepository extends KvViaTabularRepository {
2104
+ client;
2105
+ tableName;
2106
+ tabularRepository;
2107
+ constructor(client, tableName, keySchema = { type: "string" }, valueSchema = {}, tabularRepository) {
2108
+ super(keySchema, valueSchema);
2109
+ this.client = client;
2110
+ this.tableName = tableName;
2111
+ this.tabularRepository = tabularRepository ?? new SupabaseTabularRepository(client, tableName, DefaultKeyValueSchema, DefaultKeyValueKey);
2112
+ }
2113
+ }
2114
+ // src/queue/PostgresQueueStorage.ts
2115
+ import { createServiceToken as createServiceToken17, makeFingerprint as makeFingerprint5, uuid4 as uuid42 } from "@workglow/util";
2116
+ var POSTGRES_QUEUE_STORAGE = createServiceToken17("jobqueue.storage.postgres");
2117
+
2118
+ class PostgresQueueStorage {
2119
+ db;
2120
+ queueName;
2121
+ constructor(db, queueName) {
2122
+ this.db = db;
2123
+ this.queueName = queueName;
2124
+ }
2125
+ async setupDatabase() {
2126
+ let sql;
2127
+ try {
2128
+ sql = `CREATE TYPE job_status AS ENUM (${Object.values(JobStatus).map((v) => `'${v}'`).join(",")})`;
2129
+ await this.db.query(sql);
2130
+ } catch (e) {
2131
+ if (e.code !== "42710")
2132
+ throw e;
2133
+ }
2134
+ sql = `
2135
+ CREATE TABLE IF NOT EXISTS job_queue (
2136
+ id SERIAL NOT NULL,
2137
+ fingerprint text NOT NULL,
2138
+ queue text NOT NULL,
2139
+ job_run_id text NOT NULL,
2140
+ status job_status NOT NULL default 'PENDING',
2141
+ input jsonb NOT NULL,
2142
+ output jsonb,
2143
+ run_attempts integer default 0,
2144
+ max_retries integer default 20,
2145
+ run_after timestamp with time zone DEFAULT now(),
2146
+ last_ran_at timestamp with time zone,
2147
+ created_at timestamp with time zone DEFAULT now(),
2148
+ deadline_at timestamp with time zone,
2149
+ completed_at timestamp with time zone,
2150
+ error text,
2151
+ error_code text,
2152
+ progress real DEFAULT 0,
2153
+ progress_message text DEFAULT '',
2154
+ progress_details jsonb
2155
+ )`;
2156
+ await this.db.query(sql);
2157
+ sql = `
2158
+ CREATE INDEX IF NOT EXISTS job_fetcher_idx
2159
+ ON job_queue (id, status, run_after)`;
2160
+ await this.db.query(sql);
2161
+ sql = `
2162
+ CREATE INDEX IF NOT EXISTS job_queue_fetcher_idx
2163
+ ON job_queue (queue, status, run_after)`;
2164
+ await this.db.query(sql);
2165
+ sql = `
2166
+ CREATE INDEX IF NOT EXISTS jobs_fingerprint_unique_idx
2167
+ ON job_queue (queue, fingerprint, status)`;
2168
+ await this.db.query(sql);
2169
+ }
2170
+ async add(job) {
2171
+ const now = new Date().toISOString();
2172
+ job.queue = this.queueName;
2173
+ job.job_run_id = job.job_run_id ?? uuid42();
2174
+ job.fingerprint = await makeFingerprint5(job.input);
2175
+ job.status = "PENDING" /* PENDING */;
2176
+ job.progress = 0;
2177
+ job.progress_message = "";
2178
+ job.progress_details = null;
2179
+ job.created_at = now;
2180
+ job.run_after = now;
2181
+ const sql = `
2182
+ INSERT INTO job_queue(
2183
+ queue,
2184
+ fingerprint,
2185
+ input,
2186
+ run_after,
2187
+ created_at,
2188
+ deadline_at,
2189
+ max_retries,
2190
+ job_run_id,
2191
+ progress,
2192
+ progress_message,
2193
+ progress_details
2194
+ )
2195
+ VALUES
2196
+ ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11)
2197
+ RETURNING id`;
2198
+ const params = [
2199
+ job.queue,
2200
+ job.fingerprint,
2201
+ JSON.stringify(job.input),
2202
+ job.run_after,
2203
+ job.created_at,
2204
+ job.deadline_at,
2205
+ job.max_retries,
2206
+ job.job_run_id,
2207
+ job.progress,
2208
+ job.progress_message,
2209
+ job.progress_details ? JSON.stringify(job.progress_details) : null
2210
+ ];
2211
+ const result = await this.db.query(sql, params);
2212
+ if (!result)
2213
+ throw new Error("Failed to add to queue");
2214
+ job.id = result.rows[0].id;
2215
+ return job.id;
2216
+ }
2217
+ async get(id) {
2218
+ const result = await this.db.query(`SELECT *
2219
+ FROM job_queue
2220
+ WHERE id = $1 AND queue = $2
2221
+ FOR UPDATE SKIP LOCKED
2222
+ LIMIT 1`, [id, this.queueName]);
2223
+ if (!result || result.rows.length === 0)
2224
+ return;
2225
+ return result.rows[0];
2226
+ }
2227
+ async peek(status = "PENDING" /* PENDING */, num = 100) {
2228
+ num = Number(num) || 100;
2229
+ const result = await this.db.query(`
2230
+ SELECT *
2231
+ FROM job_queue
2232
+ WHERE queue = $1
2233
+ AND status = $2
2234
+ ORDER BY run_after ASC
2235
+ LIMIT $3
2236
+ FOR UPDATE SKIP LOCKED`, [this.queueName, status, num]);
2237
+ if (!result)
2238
+ return [];
2239
+ return result.rows;
2240
+ }
2241
+ async next() {
2242
+ const result = await this.db.query(`
2243
+ UPDATE job_queue
2244
+ SET status = $1, last_ran_at = NOW() AT TIME ZONE 'UTC'
2245
+ WHERE id = (
2246
+ SELECT id
2247
+ FROM job_queue
2248
+ WHERE queue = $2
2249
+ AND status = $3
2250
+ AND run_after <= NOW() AT TIME ZONE 'UTC'
2251
+ ORDER BY run_after ASC
2252
+ FOR UPDATE SKIP LOCKED
2253
+ LIMIT 1
2254
+ )
2255
+ RETURNING *`, ["PROCESSING" /* PROCESSING */, this.queueName, "PENDING" /* PENDING */]);
2256
+ return result?.rows?.[0] ?? undefined;
2257
+ }
2258
+ async size(status = "PENDING" /* PENDING */) {
2259
+ const result = await this.db.query(`
2260
+ SELECT COUNT(*) as count
2261
+ FROM job_queue
2262
+ WHERE queue = $1
2263
+ AND status = $2`, [this.queueName, status]);
2264
+ if (!result)
2265
+ return 0;
2266
+ return parseInt(result.rows[0].count, 10);
2267
+ }
2268
+ async complete(jobDetails) {
2269
+ if (jobDetails.status === "DISABLED" /* DISABLED */) {
2270
+ await this.db.query(`UPDATE job_queue
2271
+ SET
2272
+ status = $1,
2273
+ progress = 100,
2274
+ progress_message = '',
2275
+ progress_details = NULL,
2276
+ completed_at = NOW() AT TIME ZONE 'UTC'
2277
+ WHERE id = $2 AND queue = $3`, [jobDetails.status, jobDetails.id, this.queueName]);
2278
+ } else if (jobDetails.status === "PENDING" /* PENDING */) {
2279
+ await this.db.query(`UPDATE job_queue
2280
+ SET
2281
+ error = $1,
2282
+ error_code = $2,
2283
+ status = $3,
2284
+ run_after = $4,
2285
+ progress = 0,
2286
+ progress_message = '',
2287
+ progress_details = NULL,
2288
+ run_attempts = run_attempts + 1,
2289
+ last_ran_at = NOW() AT TIME ZONE 'UTC'
2290
+ WHERE id = $5 AND queue = $6`, [
2291
+ jobDetails.error,
2292
+ jobDetails.error_code,
2293
+ jobDetails.status,
2294
+ jobDetails.run_after,
2295
+ jobDetails.id,
2296
+ this.queueName
2297
+ ]);
2298
+ } else {
2299
+ await this.db.query(`
2300
+ UPDATE job_queue
2301
+ SET
2302
+ output = $1,
2303
+ error = $2,
2304
+ error_code = $3,
2305
+ status = $4,
2306
+ progress = 100,
2307
+ progress_message = '',
2308
+ progress_details = NULL,
2309
+ run_attempts = run_attempts + 1,
2310
+ completed_at = NOW() AT TIME ZONE 'UTC',
2311
+ last_ran_at = NOW() AT TIME ZONE 'UTC'
2312
+ WHERE id = $5 AND queue = $6`, [
2313
+ jobDetails.output ? JSON.stringify(jobDetails.output) : null,
2314
+ jobDetails.error ?? null,
2315
+ jobDetails.error_code ?? null,
2316
+ jobDetails.status,
2317
+ jobDetails.id,
2318
+ this.queueName
2319
+ ]);
2320
+ }
2321
+ }
2322
+ async deleteAll() {
2323
+ await this.db.query(`
2324
+ DELETE FROM job_queue
2325
+ WHERE queue = $1`, [this.queueName]);
2326
+ }
2327
+ async outputForInput(input) {
2328
+ const fingerprint = await makeFingerprint5(input);
2329
+ const result = await this.db.query(`
2330
+ SELECT output
2331
+ FROM job_queue
2332
+ WHERE fingerprint = $1 AND queue = $2 AND status = 'COMPLETED'`, [fingerprint, this.queueName]);
2333
+ if (!result)
2334
+ return null;
2335
+ return result.rows[0].output;
2336
+ }
2337
+ async abort(jobId) {
2338
+ const result = await this.db.query(`
2339
+ UPDATE job_queue
2340
+ SET status = 'ABORTING'
2341
+ WHERE id = $1 AND queue = $2`, [jobId, this.queueName]);
2342
+ }
2343
+ async getByRunId(job_run_id) {
2344
+ const result = await this.db.query(`
2345
+ SELECT * FROM job_queue WHERE job_run_id = $1 AND queue = $2`, [job_run_id, this.queueName]);
2346
+ if (!result)
2347
+ return [];
2348
+ return result.rows;
2349
+ }
2350
+ async saveProgress(jobId, progress, message, details) {
2351
+ await this.db.query(`
2352
+ UPDATE job_queue
2353
+ SET progress = $1,
2354
+ progress_message = $2,
2355
+ progress_details = $3
2356
+ WHERE id = $4 AND queue = $5`, [progress, message, details ? JSON.stringify(details) : null, jobId, this.queueName]);
2357
+ }
2358
+ async delete(jobId) {
2359
+ await this.db.query("DELETE FROM job_queue WHERE id = $1 AND queue = $2", [
2360
+ jobId,
2361
+ this.queueName
2362
+ ]);
2363
+ }
2364
+ async deleteJobsByStatusAndAge(status, olderThanMs) {
2365
+ const cutoffDate = new Date(Date.now() - olderThanMs).toISOString();
2366
+ await this.db.query(`DELETE FROM job_queue
2367
+ WHERE queue = $1
2368
+ AND status = $2
2369
+ AND completed_at IS NOT NULL
2370
+ AND completed_at <= $3`, [this.queueName, status, cutoffDate]);
2371
+ }
2372
+ }
2373
+ // src/queue/SqliteQueueStorage.ts
2374
+ import { createServiceToken as createServiceToken18, makeFingerprint as makeFingerprint6, sleep as sleep3, uuid4 as uuid43 } from "@workglow/util";
2375
+ var SQLITE_QUEUE_STORAGE = createServiceToken18("jobqueue.storage.sqlite");
2376
+
2377
+ class SqliteQueueStorage {
2378
+ db;
2379
+ queueName;
2380
+ options;
2381
+ constructor(db, queueName, options) {
2382
+ this.db = db;
2383
+ this.queueName = queueName;
2384
+ this.options = options;
2385
+ }
2386
+ async setupDatabase() {
2387
+ await sleep3(0);
2388
+ this.db.exec(`
2389
+ CREATE TABLE IF NOT EXISTS job_queue (
2390
+ id INTEGER PRIMARY KEY,
2391
+ fingerprint text NOT NULL,
2392
+ queue text NOT NULL,
2393
+ job_run_id text NOT NULL,
2394
+ status TEXT NOT NULL default 'PENDING',
2395
+ input TEXT NOT NULL,
2396
+ output TEXT,
2397
+ run_attempts INTEGER default 0,
2398
+ max_retries INTEGER default 23,
2399
+ run_after TEXT NOT NULL,
2400
+ last_ran_at TEXT,
2401
+ created_at TEXT NOT NULL,
2402
+ completed_at TEXT,
2403
+ deadline_at TEXT,
2404
+ error TEXT,
2405
+ error_code TEXT,
2406
+ progress REAL DEFAULT 0,
2407
+ progress_message TEXT DEFAULT '',
2408
+ progress_details TEXT NULL
2409
+ );
2410
+
2411
+ CREATE INDEX IF NOT EXISTS job_queue_fetcher_idx ON job_queue (queue, status, run_after);
2412
+ CREATE INDEX IF NOT EXISTS job_queue_fingerprint_idx ON job_queue (queue, fingerprint, status);
2413
+ CREATE INDEX IF NOT EXISTS job_queue_job_run_id_idx ON job_queue (queue, job_run_id);
2414
+ `);
2415
+ }
2416
+ async add(job) {
2417
+ const now = new Date().toISOString();
2418
+ job.job_run_id = job.job_run_id ?? uuid43();
2419
+ job.queue = this.queueName;
2420
+ job.fingerprint = await makeFingerprint6(job.input);
2421
+ job.status = "PENDING" /* PENDING */;
2422
+ job.progress = 0;
2423
+ job.progress_message = "";
2424
+ job.progress_details = null;
2425
+ job.created_at = now;
2426
+ job.run_after = now;
2427
+ const AddQuery = `
2428
+ INSERT INTO job_queue(
2429
+ queue,
2430
+ fingerprint,
2431
+ input,
2432
+ run_after,
2433
+ deadline_at,
2434
+ max_retries,
2435
+ job_run_id,
2436
+ progress,
2437
+ progress_message,
2438
+ progress_details,
2439
+ created_at
2440
+ )
2441
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2442
+ RETURNING id`;
2443
+ const stmt = this.db.prepare(AddQuery);
2444
+ const result = stmt.get(job.queue, job.fingerprint, JSON.stringify(job.input), job.run_after, job.deadline_at ?? null, job.max_retries, job.job_run_id, job.progress, job.progress_message, job.progress_details ? JSON.stringify(job.progress_details) : null, job.created_at);
2445
+ job.id = result?.id;
2446
+ return result?.id;
2447
+ }
2448
+ async get(id) {
2449
+ const JobQuery = `
2450
+ SELECT *
2451
+ FROM job_queue
2452
+ WHERE id = ? AND queue = ?
2453
+ LIMIT 1`;
2454
+ const stmt = this.db.prepare(JobQuery);
2455
+ const result = stmt.get(id, this.queueName);
2456
+ if (!result)
2457
+ return;
2458
+ if (result.input)
2459
+ result.input = JSON.parse(result.input);
2460
+ if (result.output)
2461
+ result.output = JSON.parse(result.output);
2462
+ if (result.progress_details)
2463
+ result.progress_details = JSON.parse(result.progress_details);
2464
+ return result;
2465
+ }
2466
+ async peek(status = "PENDING" /* PENDING */, num = 100) {
2467
+ num = Number(num) || 100;
2468
+ const FutureJobQuery = `
2469
+ SELECT *
2470
+ FROM job_queue
2471
+ WHERE queue = ?
2472
+ AND status = ?
2473
+ ORDER BY run_after ASC
2474
+ LIMIT ${num}`;
2475
+ const stmt = this.db.prepare(FutureJobQuery);
2476
+ const result = stmt.all(this.queueName, status);
2477
+ return (result || []).map((details) => {
2478
+ if (details.input)
2479
+ details.input = JSON.parse(details.input);
2480
+ if (details.output)
2481
+ details.output = JSON.parse(details.output);
2482
+ if (details.progress_details)
2483
+ details.progress_details = JSON.parse(details.progress_details);
2484
+ return details;
2485
+ });
2486
+ }
2487
+ async abort(jobId) {
2488
+ const AbortQuery = `
2489
+ UPDATE job_queue
2490
+ SET status = ?
2491
+ WHERE id = ? AND queue = ?`;
2492
+ const stmt = this.db.prepare(AbortQuery);
2493
+ stmt.run("ABORTING" /* ABORTING */, jobId, this.queueName);
2494
+ }
2495
+ async getByRunId(job_run_id) {
2496
+ const JobsByRunIdQuery = `
2497
+ SELECT *
2498
+ FROM job_queue
2499
+ WHERE job_run_id = ? AND queue = ?`;
2500
+ const stmt = this.db.prepare(JobsByRunIdQuery);
2501
+ const result = stmt.all(job_run_id, this.queueName);
2502
+ return (result || []).map((details) => {
2503
+ if (details.input)
2504
+ details.input = JSON.parse(details.input);
2505
+ if (details.output)
2506
+ details.output = JSON.parse(details.output);
2507
+ if (details.progress_details)
2508
+ details.progress_details = JSON.parse(details.progress_details);
2509
+ return details;
2510
+ });
2511
+ }
2512
+ async next() {
2513
+ const now = new Date().toISOString();
2514
+ const stmt = this.db.prepare(`
2515
+ UPDATE job_queue
2516
+ SET status = ?, last_ran_at = ?
2517
+ WHERE id = (
2518
+ SELECT id
2519
+ FROM job_queue
2520
+ WHERE queue = ?
2521
+ AND status = ?
2522
+ AND run_after <= ?
2523
+ ORDER BY run_after ASC
2524
+ LIMIT 1
2525
+ )
2526
+ RETURNING *`);
2527
+ const result = stmt.get("PROCESSING" /* PROCESSING */, now, this.queueName, "PENDING" /* PENDING */, now);
2528
+ if (!result)
2529
+ return;
2530
+ if (result.input)
2531
+ result.input = JSON.parse(result.input);
2532
+ if (result.output)
2533
+ result.output = JSON.parse(result.output);
2534
+ if (result.progress_details)
2535
+ result.progress_details = JSON.parse(result.progress_details);
2536
+ return result;
2537
+ }
2538
+ async size(status = "PENDING" /* PENDING */) {
2539
+ const sizeQuery = `
2540
+ SELECT COUNT(*) as count
2541
+ FROM job_queue
2542
+ WHERE queue = ?
2543
+ AND status = ?`;
2544
+ const stmt = this.db.prepare(sizeQuery);
2545
+ const result = stmt.get(this.queueName, status);
2546
+ return result.count;
2547
+ }
2548
+ async complete(job) {
2549
+ const now = new Date().toISOString();
2550
+ let updateQuery;
2551
+ let params;
2552
+ if (job.status === "DISABLED" /* DISABLED */) {
2553
+ updateQuery = `
2554
+ UPDATE job_queue
2555
+ SET
2556
+ status = ?,
2557
+ progress = 100,
2558
+ progress_message = '',
2559
+ progress_details = NULL,
2560
+ completed_at = ?
2561
+ WHERE id = ? AND queue = ?`;
2562
+ params = [job.status, now, job.id, this.queueName];
2563
+ } else {
2564
+ updateQuery = `
2565
+ UPDATE job_queue
2566
+ SET
2567
+ output = ?,
2568
+ error = ?,
2569
+ error_code = ?,
2570
+ status = ?,
2571
+ progress = 100,
2572
+ progress_message = '',
2573
+ progress_details = NULL,
2574
+ last_ran_at = ?,
2575
+ completed_at = ?,
2576
+ run_attempts = run_attempts + 1
2577
+ WHERE id = ? AND queue = ?`;
2578
+ params = [
2579
+ job.output ? JSON.stringify(job.output) : null,
2580
+ job.error ?? null,
2581
+ job.error_code ?? null,
2582
+ job.status,
2583
+ now,
2584
+ now,
2585
+ job.id,
2586
+ this.queueName
2587
+ ];
2588
+ }
2589
+ const stmt = this.db.prepare(updateQuery);
2590
+ stmt.run(...params);
2591
+ }
2592
+ async deleteAll() {
2593
+ const ClearQuery = `
2594
+ DELETE FROM job_queue
2595
+ WHERE queue = ?`;
2596
+ const stmt = this.db.prepare(ClearQuery);
2597
+ stmt.run(this.queueName);
2598
+ }
2599
+ async outputForInput(input) {
2600
+ const fingerprint = await makeFingerprint6(input);
2601
+ const OutputQuery = `
2602
+ SELECT output
2603
+ FROM job_queue
2604
+ WHERE queue = ? AND fingerprint = ? AND status = ?`;
2605
+ const stmt = this.db.prepare(OutputQuery);
2606
+ const result = stmt.get(this.queueName, fingerprint, "COMPLETED" /* COMPLETED */);
2607
+ return result?.output ? JSON.parse(result.output) : null;
2608
+ }
2609
+ async saveProgress(jobId, progress, message, details) {
2610
+ const UpdateProgressQuery = `
2611
+ UPDATE job_queue
2612
+ SET progress = ?,
2613
+ progress_message = ?,
2614
+ progress_details = ?
2615
+ WHERE id = ? AND queue = ?`;
2616
+ const stmt = this.db.prepare(UpdateProgressQuery);
2617
+ stmt.run(progress, message, JSON.stringify(details), String(jobId), this.queueName);
2618
+ }
2619
+ async delete(jobId) {
2620
+ const DeleteQuery = `
2621
+ DELETE FROM job_queue
2622
+ WHERE id = ? AND queue = ?`;
2623
+ const stmt = this.db.prepare(DeleteQuery);
2624
+ stmt.run(String(jobId), this.queueName);
2625
+ }
2626
+ async deleteJobsByStatusAndAge(status, olderThanMs) {
2627
+ const cutoffDate = new Date(Date.now() - olderThanMs).toISOString();
2628
+ const DeleteQuery = `
2629
+ DELETE FROM job_queue
2630
+ WHERE queue = ?
2631
+ AND status = ?
2632
+ AND completed_at IS NOT NULL
2633
+ AND completed_at <= ?`;
2634
+ const stmt = this.db.prepare(DeleteQuery);
2635
+ stmt.run(this.queueName, status, cutoffDate);
2636
+ }
2637
+ }
2638
+ // src/queue/SupabaseQueueStorage.ts
2639
+ import { createServiceToken as createServiceToken19, makeFingerprint as makeFingerprint7, uuid4 as uuid44 } from "@workglow/util";
2640
+ var SUPABASE_QUEUE_STORAGE = createServiceToken19("jobqueue.storage.supabase");
2641
+
2642
+ class SupabaseQueueStorage {
2643
+ client;
2644
+ queueName;
2645
+ constructor(client, queueName) {
2646
+ this.client = client;
2647
+ this.queueName = queueName;
2648
+ }
2649
+ async setupDatabase() {
2650
+ const createTypeSql = `CREATE TYPE job_status AS ENUM (${Object.values(JobStatus).map((v) => `'${v}'`).join(",")})`;
2651
+ const { error: typeError } = await this.client.rpc("exec_sql", { query: createTypeSql });
2652
+ if (typeError && typeError.code !== "42710") {
2653
+ throw typeError;
2654
+ }
2655
+ const createTableSql = `
2656
+ CREATE TABLE IF NOT EXISTS job_queue (
2657
+ id SERIAL NOT NULL,
2658
+ fingerprint text NOT NULL,
2659
+ queue text NOT NULL,
2660
+ job_run_id text NOT NULL,
2661
+ status job_status NOT NULL default 'PENDING',
2662
+ input jsonb NOT NULL,
2663
+ output jsonb,
2664
+ run_attempts integer default 0,
2665
+ max_retries integer default 20,
2666
+ run_after timestamp with time zone DEFAULT now(),
2667
+ last_ran_at timestamp with time zone,
2668
+ created_at timestamp with time zone DEFAULT now(),
2669
+ deadline_at timestamp with time zone,
2670
+ completed_at timestamp with time zone,
2671
+ error text,
2672
+ error_code text,
2673
+ progress real DEFAULT 0,
2674
+ progress_message text DEFAULT '',
2675
+ progress_details jsonb
2676
+ )`;
2677
+ const { error: tableError } = await this.client.rpc("exec_sql", { query: createTableSql });
2678
+ if (tableError) {
2679
+ if (tableError.code !== "42P07") {
2680
+ throw tableError;
2681
+ }
2682
+ }
2683
+ const indexes = [
2684
+ `CREATE INDEX IF NOT EXISTS job_fetcher_idx ON job_queue (id, status, run_after)`,
2685
+ `CREATE INDEX IF NOT EXISTS job_queue_fetcher_idx ON job_queue (queue, status, run_after)`,
2686
+ `CREATE INDEX IF NOT EXISTS jobs_fingerprint_unique_idx ON job_queue (queue, fingerprint, status)`
2687
+ ];
2688
+ for (const indexSql of indexes) {
2689
+ const { error: indexError } = await this.client.rpc("exec_sql", { query: indexSql });
2690
+ }
2691
+ }
2692
+ async add(job) {
2693
+ const now = new Date().toISOString();
2694
+ job.queue = this.queueName;
2695
+ job.job_run_id = job.job_run_id ?? uuid44();
2696
+ job.fingerprint = await makeFingerprint7(job.input);
2697
+ job.status = "PENDING" /* PENDING */;
2698
+ job.progress = 0;
2699
+ job.progress_message = "";
2700
+ job.progress_details = null;
2701
+ job.created_at = now;
2702
+ job.run_after = now;
2703
+ const { data, error } = await this.client.from("job_queue").insert({
2704
+ queue: job.queue,
2705
+ fingerprint: job.fingerprint,
2706
+ input: job.input,
2707
+ run_after: job.run_after,
2708
+ created_at: job.created_at,
2709
+ deadline_at: job.deadline_at,
2710
+ max_retries: job.max_retries,
2711
+ job_run_id: job.job_run_id,
2712
+ progress: job.progress,
2713
+ progress_message: job.progress_message,
2714
+ progress_details: job.progress_details
2715
+ }).select("id").single();
2716
+ if (error)
2717
+ throw error;
2718
+ if (!data)
2719
+ throw new Error("Failed to add to queue");
2720
+ job.id = data.id;
2721
+ return job.id;
2722
+ }
2723
+ async get(id) {
2724
+ const { data, error } = await this.client.from("job_queue").select("*").eq("id", id).eq("queue", this.queueName).single();
2725
+ if (error) {
2726
+ if (error.code === "PGRST116")
2727
+ return;
2728
+ throw error;
2729
+ }
2730
+ return data;
2731
+ }
2732
+ async peek(status = "PENDING" /* PENDING */, num = 100) {
2733
+ num = Number(num) || 100;
2734
+ const { data, error } = await this.client.from("job_queue").select("*").eq("queue", this.queueName).eq("status", status).order("run_after", { ascending: true }).limit(num);
2735
+ if (error)
2736
+ throw error;
2737
+ return data ?? [];
2738
+ }
2739
+ async next() {
2740
+ const { data: jobs, error: selectError } = await this.client.from("job_queue").select("*").eq("queue", this.queueName).eq("status", "PENDING" /* PENDING */).lte("run_after", new Date().toISOString()).order("run_after", { ascending: true }).limit(1);
2741
+ if (selectError)
2742
+ throw selectError;
2743
+ if (!jobs || jobs.length === 0)
2744
+ return;
2745
+ const job = jobs[0];
2746
+ const { data: updatedJob, error: updateError } = await this.client.from("job_queue").update({
2747
+ status: "PROCESSING" /* PROCESSING */,
2748
+ last_ran_at: new Date().toISOString()
2749
+ }).eq("id", job.id).eq("queue", this.queueName).select().single();
2750
+ if (updateError)
2751
+ throw updateError;
2752
+ return updatedJob;
2753
+ }
2754
+ async size(status = "PENDING" /* PENDING */) {
2755
+ const { count, error } = await this.client.from("job_queue").select("*", { count: "exact", head: true }).eq("queue", this.queueName).eq("status", status);
2756
+ if (error)
2757
+ throw error;
2758
+ return count ?? 0;
2759
+ }
2760
+ async complete(jobDetails) {
2761
+ const now = new Date().toISOString();
2762
+ if (jobDetails.status === "DISABLED" /* DISABLED */) {
2763
+ const { error: error2 } = await this.client.from("job_queue").update({
2764
+ status: jobDetails.status,
2765
+ progress: 100,
2766
+ progress_message: "",
2767
+ progress_details: null,
2768
+ completed_at: now,
2769
+ last_ran_at: now
2770
+ }).eq("id", jobDetails.id).eq("queue", this.queueName);
2771
+ if (error2)
2772
+ throw error2;
2773
+ return;
2774
+ }
2775
+ const { data: current, error: getError } = await this.client.from("job_queue").select("run_attempts").eq("id", jobDetails.id).eq("queue", this.queueName).single();
2776
+ if (getError)
2777
+ throw getError;
2778
+ const nextAttempts = (current?.run_attempts ?? 0) + 1;
2779
+ if (jobDetails.status === "PENDING" /* PENDING */) {
2780
+ const { error: error2 } = await this.client.from("job_queue").update({
2781
+ error: jobDetails.error ?? null,
2782
+ error_code: jobDetails.error_code ?? null,
2783
+ status: jobDetails.status,
2784
+ run_after: jobDetails.run_after,
2785
+ progress: 0,
2786
+ progress_message: "",
2787
+ progress_details: null,
2788
+ run_attempts: nextAttempts,
2789
+ last_ran_at: now
2790
+ }).eq("id", jobDetails.id).eq("queue", this.queueName);
2791
+ if (error2)
2792
+ throw error2;
2793
+ return;
2794
+ }
2795
+ if (jobDetails.status === "COMPLETED" /* COMPLETED */ || jobDetails.status === "FAILED" /* FAILED */) {
2796
+ const { error: error2 } = await this.client.from("job_queue").update({
2797
+ output: jobDetails.output ?? null,
2798
+ error: jobDetails.error ?? null,
2799
+ error_code: jobDetails.error_code ?? null,
2800
+ status: jobDetails.status,
2801
+ progress: 100,
2802
+ progress_message: "",
2803
+ progress_details: null,
2804
+ run_attempts: nextAttempts,
2805
+ completed_at: now,
2806
+ last_ran_at: now
2807
+ }).eq("id", jobDetails.id).eq("queue", this.queueName);
2808
+ if (error2)
2809
+ throw error2;
2810
+ return;
2811
+ }
2812
+ const { error } = await this.client.from("job_queue").update({
2813
+ status: jobDetails.status,
2814
+ output: jobDetails.output ?? null,
2815
+ error: jobDetails.error ?? null,
2816
+ error_code: jobDetails.error_code ?? null,
2817
+ run_after: jobDetails.run_after ?? null,
2818
+ run_attempts: nextAttempts,
2819
+ last_ran_at: now
2820
+ }).eq("id", jobDetails.id).eq("queue", this.queueName);
2821
+ if (error)
2822
+ throw error;
2823
+ }
2824
+ async deleteAll() {
2825
+ const { error } = await this.client.from("job_queue").delete().eq("queue", this.queueName);
2826
+ if (error)
2827
+ throw error;
2828
+ }
2829
+ async outputForInput(input) {
2830
+ const fingerprint = await makeFingerprint7(input);
2831
+ const { data, error } = await this.client.from("job_queue").select("output").eq("fingerprint", fingerprint).eq("queue", this.queueName).eq("status", "COMPLETED" /* COMPLETED */).single();
2832
+ if (error) {
2833
+ if (error.code === "PGRST116")
2834
+ return null;
2835
+ throw error;
2836
+ }
2837
+ return data?.output ?? null;
2838
+ }
2839
+ async abort(jobId) {
2840
+ const { error } = await this.client.from("job_queue").update({ status: "ABORTING" /* ABORTING */ }).eq("id", jobId).eq("queue", this.queueName);
2841
+ if (error)
2842
+ throw error;
2843
+ }
2844
+ async getByRunId(job_run_id) {
2845
+ const { data, error } = await this.client.from("job_queue").select("*").eq("job_run_id", job_run_id).eq("queue", this.queueName);
2846
+ if (error)
2847
+ throw error;
2848
+ return data ?? [];
2849
+ }
2850
+ async saveProgress(jobId, progress, message, details) {
2851
+ const { error } = await this.client.from("job_queue").update({
2852
+ progress,
2853
+ progress_message: message,
2854
+ progress_details: details
2855
+ }).eq("id", jobId).eq("queue", this.queueName);
2856
+ if (error)
2857
+ throw error;
2858
+ }
2859
+ async delete(jobId) {
2860
+ const { error } = await this.client.from("job_queue").delete().eq("id", jobId).eq("queue", this.queueName);
2861
+ if (error)
2862
+ throw error;
2863
+ }
2864
+ async deleteJobsByStatusAndAge(status, olderThanMs) {
2865
+ const cutoffDate = new Date(Date.now() - olderThanMs).toISOString();
2866
+ const { error } = await this.client.from("job_queue").delete().eq("queue", this.queueName).eq("status", status).not("completed_at", "is", null).lte("completed_at", cutoffDate);
2867
+ if (error)
2868
+ throw error;
2869
+ }
2870
+ }
2871
+ // src/kv/IndexedDbKvRepository.ts
2872
+ import { createServiceToken as createServiceToken21 } from "@workglow/util";
2873
+
2874
+ // src/tabular/IndexedDbTabularRepository.ts
2875
+ import { createServiceToken as createServiceToken20 } from "@workglow/util";
2876
+
2877
+ // src/util/IndexedDbTable.ts
2878
+ var METADATA_STORE_NAME = "__schema_metadata__";
2879
+ async function saveSchemaMetadata(db, tableName, snapshot) {
2880
+ return new Promise((resolve, reject) => {
2881
+ try {
2882
+ const transaction = db.transaction(METADATA_STORE_NAME, "readwrite");
2883
+ const store = transaction.objectStore(METADATA_STORE_NAME);
2884
+ const request = store.put({ ...snapshot, tableName }, tableName);
2885
+ request.onsuccess = () => resolve();
2886
+ request.onerror = () => reject(request.error);
2887
+ transaction.onerror = () => reject(transaction.error);
2888
+ } catch (err) {
2889
+ resolve();
2890
+ }
2891
+ });
2892
+ }
2893
+ async function loadSchemaMetadata(db, tableName) {
2894
+ return new Promise((resolve) => {
2895
+ try {
2896
+ if (!db.objectStoreNames.contains(METADATA_STORE_NAME)) {
2897
+ resolve(null);
2898
+ return;
2899
+ }
2900
+ const transaction = db.transaction(METADATA_STORE_NAME, "readonly");
2901
+ const store = transaction.objectStore(METADATA_STORE_NAME);
2902
+ const request = store.get(tableName);
2903
+ request.onsuccess = () => resolve(request.result || null);
2904
+ request.onerror = () => resolve(null);
2905
+ transaction.onerror = () => resolve(null);
2906
+ } catch (err) {
2907
+ resolve(null);
2908
+ }
2909
+ });
2910
+ }
2911
+ async function openIndexedDbTable(tableName, version, upgradeNeededCallback) {
2912
+ return new Promise((resolve, reject) => {
2913
+ const openRequest = indexedDB.open(tableName, version);
2914
+ openRequest.onsuccess = (event) => {
2915
+ const db = event.target.result;
2916
+ db.onversionchange = () => {
2917
+ db.close();
2918
+ };
2919
+ resolve(db);
2920
+ };
2921
+ openRequest.onupgradeneeded = (event) => {
2922
+ if (upgradeNeededCallback) {
2923
+ upgradeNeededCallback(event);
2924
+ }
2925
+ };
2926
+ openRequest.onerror = () => {
2927
+ const error = openRequest.error;
2928
+ if (error && error.name === "VersionError") {
2929
+ reject(new Error(`Database ${tableName} exists at a higher version. Cannot open at version ${version || "current"}.`));
2930
+ } else {
2931
+ reject(error);
2932
+ }
2933
+ };
2934
+ openRequest.onblocked = () => {
2935
+ reject(new Error(`Database ${tableName} is blocked. Close all other tabs using this database.`));
2936
+ };
2937
+ });
2938
+ }
2939
+ async function deleteIndexedDbTable(tableName) {
2940
+ return new Promise((resolve, reject) => {
2941
+ const deleteRequest = indexedDB.deleteDatabase(tableName);
2942
+ deleteRequest.onsuccess = () => resolve();
2943
+ deleteRequest.onerror = () => reject(deleteRequest.error);
2944
+ deleteRequest.onblocked = () => {
2945
+ reject(new Error(`Cannot delete database ${tableName}. Close all other tabs using this database.`));
2946
+ };
2947
+ });
2948
+ }
2949
+ function compareSchemas(store, expectedPrimaryKey, expectedIndexes) {
2950
+ const diff = {
2951
+ indexesToAdd: [],
2952
+ indexesToRemove: [],
2953
+ indexesToModify: [],
2954
+ primaryKeyChanged: false,
2955
+ needsObjectStoreRecreation: false
2956
+ };
2957
+ const actualKeyPath = store.keyPath;
2958
+ const normalizedExpected = Array.isArray(expectedPrimaryKey) ? expectedPrimaryKey : expectedPrimaryKey;
2959
+ const normalizedActual = Array.isArray(actualKeyPath) ? actualKeyPath : actualKeyPath;
2960
+ if (JSON.stringify(normalizedExpected) !== JSON.stringify(normalizedActual)) {
2961
+ diff.primaryKeyChanged = true;
2962
+ diff.needsObjectStoreRecreation = true;
2963
+ return diff;
2964
+ }
2965
+ const existingIndexes = new Map;
2966
+ for (let i = 0;i < store.indexNames.length; i++) {
2967
+ const indexName = store.indexNames[i];
2968
+ existingIndexes.set(indexName, store.index(indexName));
2969
+ }
2970
+ for (const expectedIdx of expectedIndexes) {
2971
+ const existingIdx = existingIndexes.get(expectedIdx.name);
2972
+ if (!existingIdx) {
2973
+ diff.indexesToAdd.push(expectedIdx);
2974
+ } else {
2975
+ const expectedKeyPath = Array.isArray(expectedIdx.keyPath) ? expectedIdx.keyPath : [expectedIdx.keyPath];
2976
+ const actualKeyPath2 = Array.isArray(existingIdx.keyPath) ? existingIdx.keyPath : [existingIdx.keyPath];
2977
+ const keyPathChanged = JSON.stringify(expectedKeyPath) !== JSON.stringify(actualKeyPath2);
2978
+ const uniqueChanged = existingIdx.unique !== (expectedIdx.options?.unique ?? false);
2979
+ const multiEntryChanged = existingIdx.multiEntry !== (expectedIdx.options?.multiEntry ?? false);
2980
+ if (keyPathChanged || uniqueChanged || multiEntryChanged) {
2981
+ diff.indexesToModify.push(expectedIdx);
2982
+ }
2983
+ existingIndexes.delete(expectedIdx.name);
2984
+ }
2985
+ }
2986
+ diff.indexesToRemove = Array.from(existingIndexes.keys());
2987
+ return diff;
2988
+ }
2989
+ async function readAllData(store) {
2990
+ return new Promise((resolve, reject) => {
2991
+ const request = store.getAll();
2992
+ request.onsuccess = () => resolve(request.result || []);
2993
+ request.onerror = () => reject(request.error);
2994
+ });
2995
+ }
2996
+ async function performIncrementalMigration(db, tableName, diff, options = {}) {
2997
+ const currentVersion = db.version;
2998
+ const newVersion = currentVersion + 1;
2999
+ db.close();
3000
+ options.onMigrationProgress?.(`Migrating ${tableName} from version ${currentVersion} to ${newVersion}...`, 0);
3001
+ return openIndexedDbTable(tableName, newVersion, (event) => {
3002
+ const db2 = event.target.result;
3003
+ const transaction = event.target.transaction;
3004
+ const store = transaction.objectStore(tableName);
3005
+ for (const indexName of diff.indexesToRemove) {
3006
+ options.onMigrationProgress?.(`Removing index: ${indexName}`, 0.2);
3007
+ store.deleteIndex(indexName);
3008
+ }
3009
+ for (const indexDef of diff.indexesToModify) {
3010
+ options.onMigrationProgress?.(`Updating index: ${indexDef.name}`, 0.4);
3011
+ if (store.indexNames.contains(indexDef.name)) {
3012
+ store.deleteIndex(indexDef.name);
3013
+ }
3014
+ store.createIndex(indexDef.name, indexDef.keyPath, indexDef.options);
3015
+ }
3016
+ for (const indexDef of diff.indexesToAdd) {
3017
+ options.onMigrationProgress?.(`Adding index: ${indexDef.name}`, 0.6);
3018
+ store.createIndex(indexDef.name, indexDef.keyPath, indexDef.options);
3019
+ }
3020
+ options.onMigrationProgress?.(`Migration complete`, 1);
3021
+ });
3022
+ }
3023
+ async function performDestructiveMigration(db, tableName, primaryKey, expectedIndexes, options = {}) {
3024
+ if (!options.allowDestructiveMigration) {
3025
+ 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.`);
3026
+ }
3027
+ const currentVersion = db.version;
3028
+ const newVersion = currentVersion + 1;
3029
+ options.onMigrationProgress?.(`Performing destructive migration of ${tableName}. Reading existing data...`, 0);
3030
+ let existingData = [];
3031
+ try {
3032
+ const transaction = db.transaction(tableName, "readonly");
3033
+ const store = transaction.objectStore(tableName);
3034
+ existingData = await readAllData(store);
3035
+ options.onMigrationProgress?.(`Read ${existingData.length} records`, 0.3);
3036
+ } catch (err) {
3037
+ options.onMigrationWarning?.(`Failed to read existing data during migration: ${err}`, err);
3038
+ }
3039
+ db.close();
3040
+ if (options.dataTransformer && existingData.length > 0) {
3041
+ options.onMigrationProgress?.(`Transforming ${existingData.length} records...`, 0.4);
3042
+ try {
3043
+ const transformed = [];
3044
+ for (let i = 0;i < existingData.length; i++) {
3045
+ const record = existingData[i];
3046
+ const transformedRecord = await options.dataTransformer(record);
3047
+ if (transformedRecord !== undefined && transformedRecord !== null) {
3048
+ transformed.push(transformedRecord);
3049
+ }
3050
+ if (i % 100 === 0) {
3051
+ options.onMigrationProgress?.(`Transformed ${i}/${existingData.length} records`, 0.4 + i / existingData.length * 0.3);
3052
+ }
3053
+ }
3054
+ existingData = transformed;
3055
+ options.onMigrationProgress?.(`Transformation complete: ${existingData.length} records`, 0.7);
3056
+ } catch (err) {
3057
+ options.onMigrationWarning?.(`Data transformation failed: ${err}. Some data may be lost.`, err);
3058
+ existingData = [];
3059
+ }
3060
+ }
3061
+ options.onMigrationProgress?.(`Recreating object store...`, 0.75);
3062
+ const newDb = await openIndexedDbTable(tableName, newVersion, (event) => {
3063
+ const db2 = event.target.result;
3064
+ const transaction = event.target.transaction;
3065
+ if (db2.objectStoreNames.contains(tableName)) {
3066
+ db2.deleteObjectStore(tableName);
3067
+ }
3068
+ const store = db2.createObjectStore(tableName, { keyPath: primaryKey });
3069
+ for (const idx of expectedIndexes) {
3070
+ store.createIndex(idx.name, idx.keyPath, idx.options);
3071
+ }
3072
+ if (existingData.length > 0) {
3073
+ options.onMigrationProgress?.(`Restoring ${existingData.length} records...`, 0.8);
3074
+ for (const record of existingData) {
3075
+ try {
3076
+ store.put(record);
3077
+ } catch (err) {
3078
+ options.onMigrationWarning?.(`Failed to restore record: ${err}`, err);
3079
+ }
3080
+ }
3081
+ }
3082
+ });
3083
+ options.onMigrationProgress?.(`Destructive migration complete`, 1);
3084
+ return newDb;
3085
+ }
3086
+ async function createNewDatabase(tableName, primaryKey, expectedIndexes, options = {}) {
3087
+ options.onMigrationProgress?.(`Creating new database: ${tableName}`, 0);
3088
+ try {
3089
+ await deleteIndexedDbTable(tableName);
3090
+ await new Promise((resolve) => setTimeout(resolve, 50));
3091
+ } catch (err) {}
3092
+ const version = 1;
3093
+ const db = await openIndexedDbTable(tableName, version, (event) => {
3094
+ const db2 = event.target.result;
3095
+ if (!db2.objectStoreNames.contains(METADATA_STORE_NAME)) {
3096
+ db2.createObjectStore(METADATA_STORE_NAME, { keyPath: "tableName" });
3097
+ }
3098
+ const store = db2.createObjectStore(tableName, { keyPath: primaryKey });
3099
+ for (const idx of expectedIndexes) {
3100
+ store.createIndex(idx.name, idx.keyPath, idx.options);
3101
+ }
3102
+ });
3103
+ const snapshot = {
3104
+ version: db.version,
3105
+ primaryKey,
3106
+ indexes: expectedIndexes,
3107
+ recordCount: 0,
3108
+ timestamp: Date.now()
3109
+ };
3110
+ await saveSchemaMetadata(db, tableName, snapshot);
3111
+ options.onMigrationProgress?.(`Database created successfully`, 1);
3112
+ return db;
3113
+ }
3114
+ async function ensureIndexedDbTable(tableName, primaryKey, expectedIndexes = [], options = {}) {
3115
+ try {
3116
+ let db;
3117
+ let wasJustCreated = false;
3118
+ try {
3119
+ db = await openIndexedDbTable(tableName);
3120
+ if (db.version === 1 && !db.objectStoreNames.contains(tableName)) {
3121
+ wasJustCreated = true;
3122
+ db.close();
3123
+ }
3124
+ } catch (err) {
3125
+ options.onMigrationProgress?.(`Database ${tableName} does not exist or has version conflict, creating...`, 0);
3126
+ return await createNewDatabase(tableName, primaryKey, expectedIndexes, options);
3127
+ }
3128
+ if (wasJustCreated) {
3129
+ options.onMigrationProgress?.(`Creating new database: ${tableName}`, 0);
3130
+ try {
3131
+ await deleteIndexedDbTable(tableName);
3132
+ await new Promise((resolve) => setTimeout(resolve, 50));
3133
+ } catch (err) {}
3134
+ db = await openIndexedDbTable(tableName, 1, (event) => {
3135
+ const db2 = event.target.result;
3136
+ if (!db2.objectStoreNames.contains(METADATA_STORE_NAME)) {
3137
+ db2.createObjectStore(METADATA_STORE_NAME, { keyPath: "tableName" });
3138
+ }
3139
+ const store2 = db2.createObjectStore(tableName, { keyPath: primaryKey });
3140
+ for (const idx of expectedIndexes) {
3141
+ store2.createIndex(idx.name, idx.keyPath, idx.options);
3142
+ }
3143
+ });
3144
+ const snapshot2 = {
3145
+ version: db.version,
3146
+ primaryKey,
3147
+ indexes: expectedIndexes,
3148
+ recordCount: 0,
3149
+ timestamp: Date.now()
3150
+ };
3151
+ await saveSchemaMetadata(db, tableName, snapshot2);
3152
+ options.onMigrationProgress?.(`Database created successfully`, 1);
3153
+ return db;
3154
+ }
3155
+ if (!db.objectStoreNames.contains(METADATA_STORE_NAME)) {
3156
+ const currentVersion = db.version;
3157
+ db.close();
3158
+ db = await openIndexedDbTable(tableName, currentVersion + 1, (event) => {
3159
+ const db2 = event.target.result;
3160
+ if (!db2.objectStoreNames.contains(METADATA_STORE_NAME)) {
3161
+ db2.createObjectStore(METADATA_STORE_NAME, { keyPath: "tableName" });
3162
+ }
3163
+ });
3164
+ }
3165
+ const metadata = await loadSchemaMetadata(db, tableName);
3166
+ if (!db.objectStoreNames.contains(tableName)) {
3167
+ options.onMigrationProgress?.(`Object store ${tableName} does not exist, creating...`, 0);
3168
+ db.close();
3169
+ return await createNewDatabase(tableName, primaryKey, expectedIndexes, options);
3170
+ }
3171
+ const transaction = db.transaction(tableName, "readonly");
3172
+ const store = transaction.objectStore(tableName);
3173
+ const diff = compareSchemas(store, primaryKey, expectedIndexes);
3174
+ await new Promise((resolve) => {
3175
+ transaction.oncomplete = () => resolve();
3176
+ transaction.onerror = () => resolve();
3177
+ });
3178
+ const needsMigration = diff.indexesToAdd.length > 0 || diff.indexesToRemove.length > 0 || diff.indexesToModify.length > 0 || diff.needsObjectStoreRecreation;
3179
+ if (!needsMigration) {
3180
+ options.onMigrationProgress?.(`Schema for ${tableName} is up to date`, 1);
3181
+ const snapshot2 = {
3182
+ version: db.version,
3183
+ primaryKey,
3184
+ indexes: expectedIndexes,
3185
+ timestamp: Date.now()
3186
+ };
3187
+ await saveSchemaMetadata(db, tableName, snapshot2);
3188
+ return db;
3189
+ }
3190
+ if (diff.needsObjectStoreRecreation) {
3191
+ options.onMigrationProgress?.(`Schema change requires object store recreation for ${tableName}`, 0);
3192
+ db = await performDestructiveMigration(db, tableName, primaryKey, expectedIndexes, options);
3193
+ } else {
3194
+ options.onMigrationProgress?.(`Performing incremental migration for ${tableName}`, 0);
3195
+ db = await performIncrementalMigration(db, tableName, diff, options);
3196
+ }
3197
+ const snapshot = {
3198
+ version: db.version,
3199
+ primaryKey,
3200
+ indexes: expectedIndexes,
3201
+ timestamp: Date.now()
3202
+ };
3203
+ await saveSchemaMetadata(db, tableName, snapshot);
3204
+ return db;
3205
+ } catch (err) {
3206
+ options.onMigrationWarning?.(`Migration failed for ${tableName}: ${err}`, err);
3207
+ throw err;
3208
+ }
3209
+ }
3210
+ async function dropIndexedDbTable(tableName) {
3211
+ return deleteIndexedDbTable(tableName);
3212
+ }
3213
+
3214
+ // src/tabular/IndexedDbTabularRepository.ts
3215
+ var IDB_TABULAR_REPOSITORY = createServiceToken20("storage.tabularRepository.indexedDb");
3216
+
3217
+ class IndexedDbTabularRepository extends TabularRepository {
3218
+ table;
3219
+ db;
3220
+ migrationOptions;
3221
+ constructor(table = "tabular_store", schema, primaryKeyNames, indexes = [], migrationOptions = {}) {
3222
+ super(schema, primaryKeyNames, indexes);
3223
+ this.table = table;
3224
+ this.migrationOptions = migrationOptions;
3225
+ }
3226
+ async setupDatabase() {
3227
+ if (this.db)
3228
+ return this.db;
3229
+ const pkColumns = super.primaryKeyColumns();
3230
+ const expectedIndexes = [];
3231
+ for (const spec of this.indexes) {
3232
+ const columns = spec;
3233
+ if (columns.length <= pkColumns.length) {
3234
+ const isPkPrefix = columns.every((col, idx) => col === pkColumns[idx]);
3235
+ if (isPkPrefix)
3236
+ continue;
3237
+ }
3238
+ const columnNames = columns.map((col) => String(col));
3239
+ const indexName = columnNames.join("_");
3240
+ expectedIndexes.push({
3241
+ name: indexName,
3242
+ keyPath: columnNames.length === 1 ? columnNames[0] : columnNames,
3243
+ options: { unique: false }
3244
+ });
3245
+ }
3246
+ const primaryKey = pkColumns.length === 1 ? pkColumns[0] : pkColumns;
3247
+ this.db = await ensureIndexedDbTable(this.table, primaryKey, expectedIndexes, this.migrationOptions);
3248
+ return this.db;
3249
+ }
3250
+ async put(record) {
3251
+ const db = await this.setupDatabase();
3252
+ const { key } = this.separateKeyValueFromCombined(record);
3253
+ return new Promise((resolve, reject) => {
3254
+ const transaction = db.transaction(this.table, "readwrite");
3255
+ const store = transaction.objectStore(this.table);
3256
+ const request = store.put(record);
3257
+ request.onerror = () => {
3258
+ reject(request.error);
3259
+ };
3260
+ request.onsuccess = () => {
3261
+ this.events.emit("put", record);
3262
+ resolve(record);
3263
+ };
3264
+ });
3265
+ }
3266
+ async putBulk(records) {
3267
+ const db = await this.setupDatabase();
3268
+ return new Promise((resolve, reject) => {
3269
+ const transaction = db.transaction(this.table, "readwrite");
3270
+ const store = transaction.objectStore(this.table);
3271
+ let completed = 0;
3272
+ let hasError = false;
3273
+ transaction.onerror = () => {
3274
+ if (!hasError) {
3275
+ hasError = true;
3276
+ reject(transaction.error);
3277
+ }
3278
+ };
3279
+ transaction.oncomplete = () => {
3280
+ if (!hasError) {
3281
+ resolve(records);
3282
+ }
3283
+ };
3284
+ for (const record of records) {
3285
+ const request = store.put(record);
3286
+ request.onsuccess = () => {
3287
+ this.events.emit("put", record);
3288
+ completed++;
3289
+ };
3290
+ request.onerror = () => {
3291
+ if (!hasError) {
3292
+ hasError = true;
3293
+ reject(request.error);
3294
+ }
3295
+ };
3296
+ }
3297
+ });
3298
+ }
3299
+ getPrimaryKeyAsOrderedArray(key) {
3300
+ return super.getPrimaryKeyAsOrderedArray(key).map((value) => typeof value === "bigint" ? value.toString() : value);
3301
+ }
3302
+ getIndexedKey(key) {
3303
+ const keys = super.getPrimaryKeyAsOrderedArray(key).map((value) => typeof value === "bigint" ? value.toString() : value);
3304
+ return keys.length === 1 ? keys[0] : keys;
3305
+ }
3306
+ async get(key) {
3307
+ const db = await this.setupDatabase();
3308
+ return new Promise((resolve, reject) => {
3309
+ const transaction = db.transaction(this.table, "readonly");
3310
+ const store = transaction.objectStore(this.table);
3311
+ const request = store.get(this.getIndexedKey(key));
3312
+ request.onerror = () => reject(request.error);
3313
+ request.onsuccess = () => {
3314
+ if (!request.result) {
3315
+ this.events.emit("get", key, undefined);
3316
+ resolve(undefined);
3317
+ return;
3318
+ }
3319
+ this.events.emit("get", key, request.result);
3320
+ resolve(request.result);
3321
+ };
3322
+ });
3323
+ }
3324
+ async getAll() {
3325
+ const db = await this.setupDatabase();
3326
+ const transaction = db.transaction(this.table, "readonly");
3327
+ const store = transaction.objectStore(this.table);
3328
+ const request = store.getAll();
3329
+ return new Promise((resolve, reject) => {
3330
+ request.onerror = () => reject(request.error);
3331
+ request.onsuccess = () => {
3332
+ const values = request.result;
3333
+ resolve(values.length > 0 ? values : undefined);
3334
+ };
3335
+ });
3336
+ }
3337
+ async search(key) {
3338
+ const db = await this.setupDatabase();
3339
+ const searchKeys = Object.keys(key);
3340
+ if (searchKeys.length === 0) {
3341
+ return;
3342
+ }
3343
+ const bestIndex = this.findBestMatchingIndex(searchKeys);
3344
+ if (!bestIndex) {
3345
+ throw new Error("No suitable index found for the search criteria");
3346
+ }
3347
+ return new Promise((resolve, reject) => {
3348
+ const transaction = db.transaction(this.table, "readonly");
3349
+ const store = transaction.objectStore(this.table);
3350
+ const indexName = bestIndex.join("_");
3351
+ const primaryKeyName = this.primaryKeyColumns().join("_");
3352
+ const isPrimaryKey = indexName === primaryKeyName;
3353
+ const indexValues = [];
3354
+ for (const col of bestIndex) {
3355
+ const val = key[col];
3356
+ if (val === undefined)
3357
+ break;
3358
+ if (typeof val !== "string" && typeof val !== "number") {
3359
+ throw new Error(`Invalid value type for indexed column ${String(col)}`);
3360
+ }
3361
+ indexValues.push(val);
3362
+ }
3363
+ if (indexValues.length > 0) {
3364
+ const index = isPrimaryKey ? store : store.index(indexName);
3365
+ const isPartialMatch = indexValues.length < bestIndex.length;
3366
+ if (isPartialMatch) {
3367
+ const allColumnsRequired = bestIndex.every((col) => {
3368
+ const colName = String(col);
3369
+ return this.schema.required?.includes(colName);
3370
+ });
3371
+ if (allColumnsRequired) {
3372
+ const results = [];
3373
+ const keyRange = IDBKeyRange.lowerBound(indexValues);
3374
+ const cursorRequest = index.openCursor(keyRange);
3375
+ cursorRequest.onsuccess = () => {
3376
+ const cursor = cursorRequest.result;
3377
+ if (cursor) {
3378
+ const item = cursor.value;
3379
+ const cursorKey = Array.isArray(cursor.key) ? cursor.key : [cursor.key];
3380
+ const prefixMatches = indexValues.every((val, idx) => cursorKey[idx] === val);
3381
+ if (!prefixMatches) {
3382
+ resolve(results.length > 0 ? results : undefined);
3383
+ return;
3384
+ }
3385
+ const matches = Object.entries(key).every(([k, v]) => item[k] === v);
3386
+ if (matches) {
3387
+ results.push(item);
3388
+ }
3389
+ cursor.continue();
3390
+ } else {
3391
+ resolve(results.length > 0 ? results : undefined);
3392
+ }
3393
+ };
3394
+ cursorRequest.onerror = () => {
3395
+ reject(cursorRequest.error);
3396
+ };
3397
+ } else {
3398
+ const getAllRequest = store.getAll();
3399
+ getAllRequest.onsuccess = () => {
3400
+ const allRecords = getAllRequest.result;
3401
+ const results = allRecords.filter((item) => Object.entries(key).every(([k, v]) => item[k] === v));
3402
+ resolve(results.length > 0 ? results : undefined);
3403
+ };
3404
+ getAllRequest.onerror = () => {
3405
+ reject(getAllRequest.error);
3406
+ };
3407
+ }
3408
+ } else {
3409
+ const request = index.getAll(indexValues.length === 1 ? indexValues[0] : indexValues);
3410
+ request.onsuccess = () => {
3411
+ const results = request.result.filter((item) => Object.entries(key).every(([k, v]) => item[k] === v));
3412
+ resolve(results.length > 0 ? results : undefined);
3413
+ };
3414
+ request.onerror = () => {
3415
+ console.error("Search error:", request.error);
3416
+ reject(request.error);
3417
+ };
3418
+ }
3419
+ } else {
3420
+ throw new Error(`No valid values provided for indexed columns: ${bestIndex.join(", ")}`);
3421
+ }
3422
+ });
3423
+ }
3424
+ async delete(key) {
3425
+ const db = await this.setupDatabase();
3426
+ return new Promise((resolve, reject) => {
3427
+ const transaction = db.transaction(this.table, "readwrite");
3428
+ const store = transaction.objectStore(this.table);
3429
+ const request = store.delete(this.getIndexedKey(key));
3430
+ request.onerror = () => reject(request.error);
3431
+ request.onsuccess = () => {
3432
+ this.events.emit("delete", key);
3433
+ resolve();
3434
+ };
3435
+ });
3436
+ }
3437
+ async deleteAll() {
3438
+ const db = await this.setupDatabase();
3439
+ return new Promise((resolve, reject) => {
3440
+ const transaction = db.transaction(this.table, "readwrite");
3441
+ const store = transaction.objectStore(this.table);
3442
+ const request = store.clear();
3443
+ request.onerror = () => reject(request.error);
3444
+ request.onsuccess = () => {
3445
+ this.events.emit("clearall");
3446
+ resolve();
3447
+ };
3448
+ });
3449
+ }
3450
+ async size() {
3451
+ const db = await this.setupDatabase();
3452
+ return new Promise((resolve, reject) => {
3453
+ const transaction = db.transaction(this.table, "readonly");
3454
+ const store = transaction.objectStore(this.table);
3455
+ const request = store.count();
3456
+ request.onerror = () => reject(request.error);
3457
+ request.onsuccess = () => resolve(request.result);
3458
+ });
3459
+ }
3460
+ async deleteSearch(column, value, operator = "=") {
3461
+ const db = await this.setupDatabase();
3462
+ return new Promise(async (resolve, reject) => {
3463
+ try {
3464
+ if (operator === "=") {
3465
+ const searchKey = { [column]: value };
3466
+ const recordsToDelete = await this.search(searchKey);
3467
+ if (!recordsToDelete || recordsToDelete.length === 0) {
3468
+ this.events.emit("delete", column);
3469
+ resolve();
3470
+ return;
3471
+ }
3472
+ const transaction = db.transaction(this.table, "readwrite");
3473
+ const store = transaction.objectStore(this.table);
3474
+ transaction.oncomplete = () => {
3475
+ this.events.emit("delete", column);
3476
+ resolve();
3477
+ };
3478
+ transaction.onerror = () => {
3479
+ reject(transaction.error);
3480
+ };
3481
+ for (const record of recordsToDelete) {
3482
+ const primaryKey = this.primaryKeyColumns().reduce((key, column2) => {
3483
+ key[column2] = record[column2];
3484
+ return key;
3485
+ }, {});
3486
+ const request = store.delete(this.getIndexedKey(primaryKey));
3487
+ request.onerror = () => {
3488
+ console.error("Error deleting record:", request.error);
3489
+ };
3490
+ }
3491
+ } else {
3492
+ const transaction = db.transaction(this.table, "readwrite");
3493
+ const store = transaction.objectStore(this.table);
3494
+ transaction.oncomplete = () => {
3495
+ this.events.emit("delete", column);
3496
+ resolve();
3497
+ };
3498
+ transaction.onerror = () => {
3499
+ reject(transaction.error);
3500
+ };
3501
+ const getAllRequest = store.getAll();
3502
+ getAllRequest.onsuccess = () => {
3503
+ const allRecords = getAllRequest.result;
3504
+ const recordsToDelete = allRecords.filter((record) => {
3505
+ const recordValue = record[column];
3506
+ if (recordValue === null || recordValue === undefined || value === null || value === undefined) {
3507
+ return false;
3508
+ }
3509
+ switch (operator) {
3510
+ case "<":
3511
+ return recordValue < value;
3512
+ case "<=":
3513
+ return recordValue <= value;
3514
+ case ">":
3515
+ return recordValue > value;
3516
+ case ">=":
3517
+ return recordValue >= value;
3518
+ default:
3519
+ return false;
3520
+ }
3521
+ });
3522
+ if (recordsToDelete.length === 0) {
3523
+ return;
3524
+ }
3525
+ for (const record of recordsToDelete) {
3526
+ const primaryKey = this.primaryKeyColumns().reduce((key, column2) => {
3527
+ key[column2] = record[column2];
3528
+ return key;
3529
+ }, {});
3530
+ const request = store.delete(this.getIndexedKey(primaryKey));
3531
+ request.onerror = () => {
3532
+ console.error("Error deleting record:", request.error);
3533
+ };
3534
+ }
3535
+ };
3536
+ getAllRequest.onerror = () => {
3537
+ reject(getAllRequest.error);
3538
+ };
3539
+ }
3540
+ } catch (error) {
3541
+ reject(error);
3542
+ }
3543
+ });
3544
+ }
3545
+ destroy() {
3546
+ this.db?.close();
3547
+ }
3548
+ }
3549
+
3550
+ // src/kv/IndexedDbKvRepository.ts
3551
+ var IDB_KV_REPOSITORY = createServiceToken21("storage.kvRepository.indexedDb");
3552
+
3553
+ class IndexedDbKvRepository extends KvViaTabularRepository {
3554
+ dbName;
3555
+ tabularRepository;
3556
+ constructor(dbName, keySchema = { type: "string" }, valueSchema = {}) {
3557
+ super(keySchema, valueSchema);
3558
+ this.dbName = dbName;
3559
+ this.tabularRepository = new IndexedDbTabularRepository(dbName, DefaultKeyValueSchema, DefaultKeyValueKey);
3560
+ }
3561
+ }
3562
+ // src/queue/IndexedDbQueueStorage.ts
3563
+ import { createServiceToken as createServiceToken22, makeFingerprint as makeFingerprint8, uuid4 as uuid45 } from "@workglow/util";
3564
+ var INDEXED_DB_QUEUE_STORAGE = createServiceToken22("jobqueue.storage.indexedDb");
3565
+
3566
+ class IndexedDbQueueStorage {
3567
+ queueName;
3568
+ db;
3569
+ tableName;
3570
+ migrationOptions;
3571
+ constructor(queueName, migrationOptions = {}) {
3572
+ this.queueName = queueName;
3573
+ this.tableName = `jobs_${queueName}`;
3574
+ this.migrationOptions = migrationOptions;
3575
+ }
3576
+ async getDb() {
3577
+ if (this.db)
3578
+ return this.db;
3579
+ await this.setupDatabase();
3580
+ return this.db;
3581
+ }
3582
+ async setupDatabase() {
3583
+ const expectedIndexes = [
3584
+ {
3585
+ name: "status",
3586
+ keyPath: `status`,
3587
+ options: { unique: false }
3588
+ },
3589
+ {
3590
+ name: "status_run_after",
3591
+ keyPath: ["status", "run_after"],
3592
+ options: { unique: false }
3593
+ },
3594
+ {
3595
+ name: "job_run_id",
3596
+ keyPath: `job_run_id`,
3597
+ options: { unique: false }
3598
+ },
3599
+ {
3600
+ name: "fingerprint_status",
3601
+ keyPath: ["fingerprint", "status"],
3602
+ options: { unique: false }
3603
+ }
3604
+ ];
3605
+ this.db = await ensureIndexedDbTable(this.tableName, "id", expectedIndexes, this.migrationOptions);
3606
+ }
3607
+ async add(job) {
3608
+ const db = await this.getDb();
3609
+ const now = new Date().toISOString();
3610
+ job.id = job.id ?? uuid45();
3611
+ job.job_run_id = job.job_run_id ?? uuid45();
3612
+ job.queue = this.queueName;
3613
+ job.fingerprint = await makeFingerprint8(job.input);
3614
+ job.status = "PENDING" /* PENDING */;
3615
+ job.progress = 0;
3616
+ job.progress_message = "";
3617
+ job.progress_details = null;
3618
+ job.created_at = now;
3619
+ job.run_after = now;
3620
+ const tx = db.transaction(this.tableName, "readwrite");
3621
+ const store = tx.objectStore(this.tableName);
3622
+ return new Promise((resolve, reject) => {
3623
+ const request = store.add(job);
3624
+ tx.oncomplete = () => resolve(job.id);
3625
+ tx.onerror = () => reject(tx.error);
3626
+ request.onerror = () => reject(request.error);
3627
+ });
3628
+ }
3629
+ async get(id) {
3630
+ const db = await this.getDb();
3631
+ const tx = db.transaction(this.tableName, "readonly");
3632
+ const store = tx.objectStore(this.tableName);
3633
+ const request = store.get(id);
3634
+ return new Promise((resolve, reject) => {
3635
+ request.onsuccess = () => resolve(request.result);
3636
+ request.onerror = () => reject(request.error);
3637
+ tx.onerror = () => reject(tx.error);
3638
+ });
3639
+ }
3640
+ async peek(status = "PENDING" /* PENDING */, num = 100) {
3641
+ const db = await this.getDb();
3642
+ const tx = db.transaction(this.tableName, "readonly");
3643
+ const store = tx.objectStore(this.tableName);
3644
+ const index = store.index("status_run_after");
3645
+ return new Promise((resolve, reject) => {
3646
+ const ret = new Map;
3647
+ const keyRange = IDBKeyRange.bound([status, ""], [status, "\uFFFF"]);
3648
+ const cursorRequest = index.openCursor(keyRange);
3649
+ const handleCursor = (e) => {
3650
+ const cursor = e.target.result;
3651
+ if (!cursor || ret.size >= num) {
3652
+ resolve(Array.from(ret.values()));
3653
+ return;
3654
+ }
3655
+ ret.set(cursor.value.id, cursor.value);
3656
+ cursor.continue();
3657
+ };
3658
+ cursorRequest.onsuccess = handleCursor;
3659
+ cursorRequest.onerror = () => reject(cursorRequest.error);
3660
+ tx.onerror = () => reject(tx.error);
3661
+ });
3662
+ }
3663
+ async next() {
3664
+ const db = await this.getDb();
3665
+ const tx = db.transaction(this.tableName, "readwrite");
3666
+ const store = tx.objectStore(this.tableName);
3667
+ const index = store.index("status_run_after");
3668
+ const now = new Date().toISOString();
3669
+ return new Promise((resolve, reject) => {
3670
+ const cursorRequest = index.openCursor(IDBKeyRange.bound(["PENDING" /* PENDING */, ""], ["PENDING" /* PENDING */, now], false, true));
3671
+ let jobToReturn;
3672
+ cursorRequest.onsuccess = (e) => {
3673
+ const cursor = e.target.result;
3674
+ if (!cursor) {
3675
+ if (jobToReturn) {
3676
+ resolve(jobToReturn);
3677
+ } else {
3678
+ resolve(undefined);
3679
+ }
3680
+ return;
3681
+ }
3682
+ const job = cursor.value;
3683
+ if (job.status !== "PENDING" /* PENDING */) {
3684
+ cursor.continue();
3685
+ return;
3686
+ }
3687
+ job.status = "PROCESSING" /* PROCESSING */;
3688
+ job.last_ran_at = now;
3689
+ try {
3690
+ const updateRequest = store.put(job);
3691
+ updateRequest.onsuccess = () => {
3692
+ jobToReturn = job;
3693
+ };
3694
+ updateRequest.onerror = (err) => {
3695
+ console.error("Failed to update job status:", err);
3696
+ cursor.continue();
3697
+ };
3698
+ } catch (err) {
3699
+ console.error("Error updating job:", err);
3700
+ cursor.continue();
3701
+ }
3702
+ };
3703
+ cursorRequest.onerror = () => reject(cursorRequest.error);
3704
+ tx.oncomplete = () => {
3705
+ resolve(jobToReturn);
3706
+ };
3707
+ tx.onerror = () => reject(tx.error);
3708
+ });
3709
+ }
3710
+ async size(status = "PENDING" /* PENDING */) {
3711
+ const db = await this.getDb();
3712
+ return new Promise((resolve, reject) => {
3713
+ const tx = db.transaction(this.tableName, "readonly");
3714
+ const store = tx.objectStore(this.tableName);
3715
+ const index = store.index("status");
3716
+ const request = index.count(status);
3717
+ request.onsuccess = () => resolve(request.result);
3718
+ request.onerror = () => reject(request.error);
3719
+ tx.onerror = () => reject(tx.error);
3720
+ });
3721
+ }
3722
+ async complete(job) {
3723
+ const db = await this.getDb();
3724
+ const tx = db.transaction(this.tableName, "readwrite");
3725
+ const store = tx.objectStore(this.tableName);
3726
+ return new Promise((resolve, reject) => {
3727
+ const getReq = store.get(job.id);
3728
+ getReq.onsuccess = () => {
3729
+ const existing = getReq.result;
3730
+ const currentAttempts = existing?.run_attempts ?? 0;
3731
+ job.run_attempts = currentAttempts + 1;
3732
+ const putReq = store.put(job);
3733
+ putReq.onsuccess = () => {};
3734
+ putReq.onerror = () => reject(putReq.error);
3735
+ };
3736
+ getReq.onerror = () => reject(getReq.error);
3737
+ tx.oncomplete = () => resolve();
3738
+ tx.onerror = () => reject(tx.error);
3739
+ });
3740
+ }
3741
+ async abort(id) {
3742
+ const job = await this.get(id);
3743
+ if (!job)
3744
+ return;
3745
+ job.status = "ABORTING" /* ABORTING */;
3746
+ await this.complete(job);
3747
+ }
3748
+ async getByRunId(job_run_id) {
3749
+ const db = await this.getDb();
3750
+ const tx = db.transaction(this.tableName, "readonly");
3751
+ const store = tx.objectStore(this.tableName);
3752
+ const index = store.index("job_run_id");
3753
+ const request = index.getAll(job_run_id);
3754
+ return new Promise((resolve, reject) => {
3755
+ request.onsuccess = () => resolve(request.result);
3756
+ request.onerror = () => reject(request.error);
3757
+ tx.onerror = () => reject(tx.error);
3758
+ });
3759
+ }
3760
+ async deleteAll() {
3761
+ const db = await this.getDb();
3762
+ const tx = db.transaction(this.tableName, "readwrite");
3763
+ const store = tx.objectStore(this.tableName);
3764
+ const request = store.clear();
3765
+ return new Promise((resolve, reject) => {
3766
+ request.onsuccess = () => resolve();
3767
+ request.onerror = () => reject(request.error);
3768
+ tx.onerror = () => reject(tx.error);
3769
+ });
3770
+ }
3771
+ async outputForInput(input) {
3772
+ const fingerprint = await makeFingerprint8(input);
3773
+ const db = await this.getDb();
3774
+ const tx = db.transaction(this.tableName, "readonly");
3775
+ const store = tx.objectStore(this.tableName);
3776
+ const index = store.index("fingerprint_status");
3777
+ const request = index.get([fingerprint, "COMPLETED" /* COMPLETED */]);
3778
+ return new Promise((resolve, reject) => {
3779
+ request.onsuccess = () => resolve(request.result?.output ?? null);
3780
+ request.onerror = () => reject(request.error);
3781
+ tx.onerror = () => reject(tx.error);
3782
+ });
3783
+ }
3784
+ async saveProgress(id, progress, message, details) {
3785
+ const job = await this.get(id);
3786
+ if (!job)
3787
+ throw new Error(`Job ${id} not found`);
3788
+ job.progress = progress;
3789
+ job.progress_message = message;
3790
+ job.progress_details = details;
3791
+ await this.complete(job);
3792
+ }
3793
+ async delete(id) {
3794
+ const db = await this.getDb();
3795
+ const tx = db.transaction(this.tableName, "readwrite");
3796
+ const store = tx.objectStore(this.tableName);
3797
+ const request = store.delete(id);
3798
+ return new Promise((resolve, reject) => {
3799
+ request.onsuccess = () => resolve();
3800
+ request.onerror = () => reject(request.error);
3801
+ tx.onerror = () => reject(tx.error);
3802
+ });
3803
+ }
3804
+ async deleteJobsByStatusAndAge(status, olderThanMs) {
3805
+ const db = await this.getDb();
3806
+ const tx = db.transaction(this.tableName, "readwrite");
3807
+ const store = tx.objectStore(this.tableName);
3808
+ const index = store.index("status");
3809
+ const cutoffDate = new Date(Date.now() - olderThanMs).toISOString();
3810
+ return new Promise((resolve, reject) => {
3811
+ const request = index.openCursor();
3812
+ request.onsuccess = (event) => {
3813
+ const cursor = event.target.result;
3814
+ if (cursor) {
3815
+ const job = cursor.value;
3816
+ if (job.status === status && job.completed_at && job.completed_at <= cutoffDate) {
3817
+ cursor.delete();
3818
+ }
3819
+ cursor.continue();
3820
+ }
3821
+ };
3822
+ tx.oncomplete = () => resolve();
3823
+ tx.onerror = () => reject(tx.error);
3824
+ request.onerror = () => reject(request.error);
3825
+ });
3826
+ }
3827
+ }
3828
+ export {
3829
+ ensureIndexedDbTable,
3830
+ dropIndexedDbTable,
3831
+ TabularRepository,
3832
+ TABULAR_REPOSITORY,
3833
+ SupabaseTabularRepository,
3834
+ SupabaseQueueStorage,
3835
+ SupabaseKvRepository,
3836
+ SqliteTabularRepository,
3837
+ SqliteQueueStorage,
3838
+ SqliteKvRepository,
3839
+ SUPABASE_TABULAR_REPOSITORY,
3840
+ SUPABASE_QUEUE_STORAGE,
3841
+ SUPABASE_KV_REPOSITORY,
3842
+ SQLITE_TABULAR_REPOSITORY,
3843
+ SQLITE_QUEUE_STORAGE,
3844
+ SQLITE_KV_REPOSITORY,
3845
+ QUEUE_STORAGE,
3846
+ PostgresTabularRepository,
3847
+ PostgresQueueStorage,
3848
+ PostgresKvRepository,
3849
+ POSTGRES_TABULAR_REPOSITORY,
3850
+ POSTGRES_QUEUE_STORAGE,
3851
+ POSTGRES_KV_REPOSITORY,
3852
+ MEMORY_TABULAR_REPOSITORY,
3853
+ MEMORY_KV_REPOSITORY,
3854
+ KvViaTabularRepository,
3855
+ KvRepository,
3856
+ KV_REPOSITORY,
3857
+ JobStatus,
3858
+ IndexedDbTabularRepository,
3859
+ IndexedDbQueueStorage,
3860
+ IndexedDbKvRepository,
3861
+ InMemoryTabularRepository,
3862
+ InMemoryQueueStorage,
3863
+ InMemoryKvRepository,
3864
+ IN_MEMORY_QUEUE_STORAGE,
3865
+ INDEXED_DB_QUEUE_STORAGE,
3866
+ IDB_TABULAR_REPOSITORY,
3867
+ IDB_KV_REPOSITORY,
3868
+ FsFolderTabularRepository,
3869
+ FsFolderKvRepository,
3870
+ FsFolderJsonKvRepository,
3871
+ FS_FOLDER_TABULAR_REPOSITORY,
3872
+ FS_FOLDER_KV_REPOSITORY,
3873
+ FS_FOLDER_JSON_KV_REPOSITORY,
3874
+ DefaultKeyValueSchema,
3875
+ DefaultKeyValueKey,
3876
+ CachedTabularRepository,
3877
+ CACHED_TABULAR_REPOSITORY
3878
+ };
3879
+
3880
+ //# debugId=9E46692D2791266264756E2164756E21