@workglow/storage 0.2.26 → 0.2.27

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 (38) hide show
  1. package/dist/browser.js +218 -12
  2. package/dist/browser.js.map +13 -13
  3. package/dist/bun.js +703 -24
  4. package/dist/bun.js.map +18 -17
  5. package/dist/common-server.d.ts +1 -0
  6. package/dist/common-server.d.ts.map +1 -1
  7. package/dist/node.js +703 -24
  8. package/dist/node.js.map +18 -17
  9. package/dist/queue/IQueueStorage.d.ts +25 -2
  10. package/dist/queue/IQueueStorage.d.ts.map +1 -1
  11. package/dist/queue/InMemoryQueueStorage.d.ts +1 -0
  12. package/dist/queue/InMemoryQueueStorage.d.ts.map +1 -1
  13. package/dist/queue/IndexedDbQueueStorage.d.ts +1 -0
  14. package/dist/queue/IndexedDbQueueStorage.d.ts.map +1 -1
  15. package/dist/queue/PostgresQueueStorage.d.ts +24 -3
  16. package/dist/queue/PostgresQueueStorage.d.ts.map +1 -1
  17. package/dist/queue/SqliteQueueStorage.d.ts +8 -0
  18. package/dist/queue/SqliteQueueStorage.d.ts.map +1 -1
  19. package/dist/queue/SupabaseQueueStorage.d.ts +1 -0
  20. package/dist/queue/SupabaseQueueStorage.d.ts.map +1 -1
  21. package/dist/queue/TelemetryQueueStorage.d.ts +2 -1
  22. package/dist/queue/TelemetryQueueStorage.d.ts.map +1 -1
  23. package/dist/queue-limiter/IRateLimiterStorage.d.ts +46 -0
  24. package/dist/queue-limiter/IRateLimiterStorage.d.ts.map +1 -1
  25. package/dist/queue-limiter/InMemoryRateLimiterStorage.d.ts +12 -1
  26. package/dist/queue-limiter/InMemoryRateLimiterStorage.d.ts.map +1 -1
  27. package/dist/queue-limiter/IndexedDbRateLimiterStorage.d.ts +28 -1
  28. package/dist/queue-limiter/IndexedDbRateLimiterStorage.d.ts.map +1 -1
  29. package/dist/queue-limiter/PostgresRateLimiterStorage.d.ts +4 -1
  30. package/dist/queue-limiter/PostgresRateLimiterStorage.d.ts.map +1 -1
  31. package/dist/queue-limiter/SqliteRateLimiterStorage.d.ts +10 -1
  32. package/dist/queue-limiter/SqliteRateLimiterStorage.d.ts.map +1 -1
  33. package/dist/queue-limiter/SupabaseRateLimiterStorage.d.ts +6 -1
  34. package/dist/queue-limiter/SupabaseRateLimiterStorage.d.ts.map +1 -1
  35. package/dist/tabular/IndexedDbTabularStorage.d.ts.map +1 -1
  36. package/dist/tabular/SharedInMemoryTabularStorage.d.ts +2 -1
  37. package/dist/tabular/SharedInMemoryTabularStorage.d.ts.map +1 -1
  38. package/package.json +3 -3
package/dist/node.js CHANGED
@@ -1577,6 +1577,7 @@ var IN_MEMORY_QUEUE_STORAGE = createServiceToken9("jobqueue.storage.inMemory");
1577
1577
 
1578
1578
  class InMemoryQueueStorage {
1579
1579
  queueName;
1580
+ scope = "process";
1580
1581
  prefixValues;
1581
1582
  events = new EventEmitter3;
1582
1583
  constructor(queueName, options) {
@@ -1786,6 +1787,9 @@ class TelemetryQueueStorage {
1786
1787
  this.storageName = storageName;
1787
1788
  this.inner = inner;
1788
1789
  }
1790
+ get scope() {
1791
+ return this.inner.scope;
1792
+ }
1789
1793
  add(job) {
1790
1794
  return traced("workglow.storage.queue.add", this.storageName, () => this.inner.add(job));
1791
1795
  }
@@ -1836,13 +1840,15 @@ class TelemetryQueueStorage {
1836
1840
  }
1837
1841
  }
1838
1842
  // src/queue-limiter/InMemoryRateLimiterStorage.ts
1839
- import { createServiceToken as createServiceToken10, sleep as sleep2 } from "@workglow/util";
1843
+ import { createServiceToken as createServiceToken10, sleep as sleep2, uuid4 as uuid43 } from "@workglow/util";
1840
1844
  var IN_MEMORY_RATE_LIMITER_STORAGE = createServiceToken10("ratelimiter.storage.inMemory");
1841
1845
 
1842
1846
  class InMemoryRateLimiterStorage {
1847
+ scope = "process";
1843
1848
  prefixValues;
1844
1849
  executions = new Map;
1845
1850
  nextAvailableTimes = new Map;
1851
+ reserveChains = new Map;
1846
1852
  constructor(options) {
1847
1853
  this.prefixValues = options?.prefixValues ?? {};
1848
1854
  }
@@ -1851,11 +1857,67 @@ class InMemoryRateLimiterStorage {
1851
1857
  return prefixPart ? `${prefixPart}|${queueName}` : queueName;
1852
1858
  }
1853
1859
  async setupDatabase() {}
1860
+ async withKeyLock(key, fn) {
1861
+ const previous = this.reserveChains.get(key) ?? Promise.resolve();
1862
+ let release;
1863
+ const next = new Promise((resolve) => {
1864
+ release = resolve;
1865
+ });
1866
+ this.reserveChains.set(key, next);
1867
+ try {
1868
+ await previous;
1869
+ return await fn();
1870
+ } finally {
1871
+ release(undefined);
1872
+ if (this.reserveChains.get(key) === next) {
1873
+ this.reserveChains.delete(key);
1874
+ }
1875
+ }
1876
+ }
1877
+ async tryReserveExecution(queueName, maxExecutions, windowMs) {
1878
+ const key = this.makeKey(queueName);
1879
+ return this.withKeyLock(key, () => {
1880
+ const now = Date.now();
1881
+ const windowStart = new Date(now - windowMs);
1882
+ const executions = this.executions.get(key) ?? [];
1883
+ const live = executions.filter((e) => e.executedAt > windowStart);
1884
+ if (live.length >= maxExecutions) {
1885
+ if (live.length !== executions.length) {
1886
+ this.executions.set(key, live);
1887
+ }
1888
+ return null;
1889
+ }
1890
+ const next = this.nextAvailableTimes.get(key);
1891
+ if (next && next.getTime() > now) {
1892
+ return null;
1893
+ }
1894
+ const id = uuid43();
1895
+ live.push({ id, queueName, executedAt: new Date(now) });
1896
+ this.executions.set(key, live);
1897
+ return id;
1898
+ });
1899
+ }
1900
+ async releaseExecution(queueName, token) {
1901
+ if (token === null || token === undefined)
1902
+ return;
1903
+ const key = this.makeKey(queueName);
1904
+ await this.withKeyLock(key, () => {
1905
+ const executions = this.executions.get(key);
1906
+ if (!executions || executions.length === 0)
1907
+ return;
1908
+ const idx = executions.findIndex((e) => e.id === token);
1909
+ if (idx === -1)
1910
+ return;
1911
+ executions.splice(idx, 1);
1912
+ this.executions.set(key, executions);
1913
+ });
1914
+ }
1854
1915
  async recordExecution(queueName) {
1855
1916
  await sleep2(0);
1856
1917
  const key = this.makeKey(queueName);
1857
1918
  const executions = this.executions.get(key) ?? [];
1858
1919
  executions.push({
1920
+ id: uuid43(),
1859
1921
  queueName,
1860
1922
  executedAt: new Date
1861
1923
  });
@@ -2443,7 +2505,7 @@ import {
2443
2505
  getLogger as getLogger3,
2444
2506
  makeFingerprint as makeFingerprint5,
2445
2507
  sleep as sleep3,
2446
- uuid4 as uuid43
2508
+ uuid4 as uuid44
2447
2509
  } from "@workglow/util";
2448
2510
  import { mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises";
2449
2511
  import path from "node:path";
@@ -2471,7 +2533,7 @@ class FsFolderTabularStorage extends BaseTabularStorage {
2471
2533
  if (strategy === "autoincrement") {
2472
2534
  return ++this.autoIncrementCounter;
2473
2535
  } else {
2474
- return uuid43();
2536
+ return uuid44();
2475
2537
  }
2476
2538
  }
2477
2539
  async put(entity) {
@@ -3423,7 +3485,7 @@ class PostgresTabularStorage extends BaseSqlTabularStorage {
3423
3485
  }
3424
3486
  // src/tabular/SqliteTabularStorage.ts
3425
3487
  import { Sqlite } from "@workglow/storage/sqlite";
3426
- import { createServiceToken as createServiceToken14, uuid4 as uuid44 } from "@workglow/util";
3488
+ import { createServiceToken as createServiceToken14, uuid4 as uuid45 } from "@workglow/util";
3427
3489
  var SQLITE_TABULAR_REPOSITORY = createServiceToken14("storage.tabularRepository.sqlite");
3428
3490
 
3429
3491
  class SqliteTabularStorage extends BaseSqlTabularStorage {
@@ -3601,7 +3663,7 @@ class SqliteTabularStorage extends BaseSqlTabularStorage {
3601
3663
  }
3602
3664
  generateKeyValue(columnName, strategy) {
3603
3665
  if (strategy === "uuid") {
3604
- return uuid44();
3666
+ return uuid45();
3605
3667
  }
3606
3668
  throw new Error(`SQLite autoincrement keys are generated by the database, not client-side. Column: ${columnName}`);
3607
3669
  }
@@ -4641,13 +4703,15 @@ class SupabaseKvStorage extends KvViaTabularStorage {
4641
4703
  }
4642
4704
  }
4643
4705
  // src/queue/PostgresQueueStorage.ts
4644
- import { createServiceToken as createServiceToken21, makeFingerprint as makeFingerprint6, uuid4 as uuid45 } from "@workglow/util";
4706
+ import { createHash } from "node:crypto";
4707
+ import { createServiceToken as createServiceToken21, getLogger as getLogger4, makeFingerprint as makeFingerprint6, uuid4 as uuid46 } from "@workglow/util";
4645
4708
  var POSTGRES_QUEUE_STORAGE = createServiceToken21("jobqueue.storage.postgres");
4646
4709
  var SAFE_IDENTIFIER = /^[a-zA-Z][a-zA-Z0-9_]*$/;
4647
4710
 
4648
4711
  class PostgresQueueStorage {
4649
4712
  db;
4650
4713
  queueName;
4714
+ scope = "cluster";
4651
4715
  prefixes;
4652
4716
  prefixValues;
4653
4717
  tableName;
@@ -4744,14 +4808,46 @@ class PostgresQueueStorage {
4744
4808
  ON ${this.tableName} (${prefixIndexPrefix}queue, status, run_after)`;
4745
4809
  await this.db.query(sql);
4746
4810
  sql = `
4747
- CREATE INDEX IF NOT EXISTS jobs_fingerprint${indexSuffix}_unique_idx
4811
+ CREATE INDEX IF NOT EXISTS jobs_fingerprint${indexSuffix}_unique_idx
4748
4812
  ON ${this.tableName} (${prefixIndexPrefix}queue, fingerprint, status)`;
4749
4813
  await this.db.query(sql);
4814
+ const fnName = `${this.tableName}_notify`;
4815
+ const trgName = `${this.tableName}_notify_trg`;
4816
+ try {
4817
+ await this.db.query(`
4818
+ CREATE OR REPLACE FUNCTION ${fnName}() RETURNS trigger AS $fn$
4819
+ DECLARE
4820
+ channel TEXT := 'wglw_q_' || md5('${this.tableName}' || COALESCE(NEW.queue, OLD.queue));
4821
+ payload TEXT;
4822
+ BEGIN
4823
+ payload := json_build_object(
4824
+ 'op', TG_OP,
4825
+ 'id', COALESCE(NEW.id, OLD.id),
4826
+ 'queue', COALESCE(NEW.queue, OLD.queue),
4827
+ 'status', COALESCE(NEW.status::text, OLD.status::text)
4828
+ )::text;
4829
+ PERFORM pg_notify(channel, payload);
4830
+ RETURN NULL;
4831
+ END;
4832
+ $fn$ LANGUAGE plpgsql;
4833
+ `);
4834
+ await this.db.query(`DROP TRIGGER IF EXISTS ${trgName} ON ${this.tableName}`);
4835
+ await this.db.query(`
4836
+ CREATE TRIGGER ${trgName}
4837
+ AFTER INSERT OR UPDATE ON ${this.tableName}
4838
+ FOR EACH ROW EXECUTE FUNCTION ${fnName}();
4839
+ `);
4840
+ } catch {}
4841
+ }
4842
+ notifyChannelName() {
4843
+ const tableAndQueue = `${this.tableName}${this.queueName}`;
4844
+ const hash = createHash("md5").update(tableAndQueue).digest("hex");
4845
+ return `wglw_q_${hash}`;
4750
4846
  }
4751
4847
  async add(job) {
4752
4848
  const now = new Date().toISOString();
4753
4849
  job.queue = this.queueName;
4754
- job.job_run_id = job.job_run_id ?? uuid45();
4850
+ job.job_run_id = job.job_run_id ?? uuid46();
4755
4851
  job.fingerprint = await makeFingerprint6(job.input);
4756
4852
  job.status = JobStatus.PENDING;
4757
4853
  job.progress = 0;
@@ -4990,17 +5086,138 @@ class PostgresQueueStorage {
4990
5086
  AND completed_at <= $3${prefixConditions}`, [this.queueName, status, cutoffDate, ...prefixParams]);
4991
5087
  }
4992
5088
  subscribeToChanges(callback, options) {
4993
- throw new Error("subscribeToChanges is not supported for PostgresQueueStorage");
5089
+ const poolMaybe = this.db;
5090
+ if (typeof poolMaybe.connect !== "function") {
5091
+ throw new Error("PostgresQueueStorage.subscribeToChanges requires a pg.Pool (got a single-connection wrapper)");
5092
+ }
5093
+ const pool = poolMaybe;
5094
+ const channel = this.notifyChannelName();
5095
+ const effectivePrefixFilter = options?.prefixFilter === undefined ? this.prefixes.length > 0 ? this.prefixValues : null : Object.keys(options.prefixFilter).length === 0 ? null : options.prefixFilter;
5096
+ let unsubscribed = false;
5097
+ let activeClient = null;
5098
+ let reconnectTimer = null;
5099
+ let backoffMs = 250;
5100
+ const dispatch = (change) => {
5101
+ try {
5102
+ callback(change);
5103
+ } catch (err) {
5104
+ getLogger4().debug("PostgresQueueStorage subscribe callback threw", {
5105
+ channel,
5106
+ changeType: change.type,
5107
+ error: err
5108
+ });
5109
+ }
5110
+ };
5111
+ const matchesPrefix = (change, filter) => {
5112
+ const row = change.new ?? change.old;
5113
+ if (!row)
5114
+ return false;
5115
+ for (const [k, v] of Object.entries(filter)) {
5116
+ if (row[k] !== v)
5117
+ return false;
5118
+ }
5119
+ return true;
5120
+ };
5121
+ const hydrate = async (id) => {
5122
+ try {
5123
+ return await this.get(id);
5124
+ } catch {
5125
+ return;
5126
+ }
5127
+ };
5128
+ const handleNotification = (msg) => {
5129
+ if (msg.channel !== channel || !msg.payload)
5130
+ return;
5131
+ let parsed;
5132
+ try {
5133
+ parsed = JSON.parse(msg.payload);
5134
+ } catch {
5135
+ return;
5136
+ }
5137
+ (async () => {
5138
+ const op = parsed.op;
5139
+ const fallback = {
5140
+ id: parsed.id,
5141
+ queue: parsed.queue,
5142
+ status: parsed.status
5143
+ };
5144
+ const fullRow = op === "DELETE" ? undefined : await hydrate(parsed.id);
5145
+ const change = {
5146
+ type: op === "INSERT" ? "INSERT" : op === "DELETE" ? "DELETE" : "UPDATE",
5147
+ new: op === "DELETE" ? undefined : fullRow ?? fallback,
5148
+ old: op === "DELETE" ? fallback : undefined
5149
+ };
5150
+ if (effectivePrefixFilter && !matchesPrefix(change, effectivePrefixFilter))
5151
+ return;
5152
+ dispatch(change);
5153
+ })();
5154
+ };
5155
+ const connect = async () => {
5156
+ if (unsubscribed)
5157
+ return;
5158
+ try {
5159
+ const client = await pool.connect();
5160
+ if (unsubscribed) {
5161
+ client.release();
5162
+ return;
5163
+ }
5164
+ activeClient = client;
5165
+ client.on("notification", handleNotification);
5166
+ client.on("error", () => scheduleReconnect());
5167
+ await client.query(`LISTEN ${channel}`);
5168
+ backoffMs = 250;
5169
+ dispatch({ type: "RESYNC" });
5170
+ } catch {
5171
+ scheduleReconnect();
5172
+ }
5173
+ };
5174
+ const scheduleReconnect = () => {
5175
+ if (unsubscribed || reconnectTimer)
5176
+ return;
5177
+ const c = activeClient;
5178
+ activeClient = null;
5179
+ try {
5180
+ c?.removeAllListeners?.("notification");
5181
+ c?.removeAllListeners?.("error");
5182
+ c?.release();
5183
+ } catch {}
5184
+ const delay = Math.min(backoffMs, 30000);
5185
+ backoffMs = Math.min(backoffMs * 2, 30000);
5186
+ reconnectTimer = setTimeout(() => {
5187
+ reconnectTimer = null;
5188
+ connect();
5189
+ }, delay);
5190
+ };
5191
+ connect();
5192
+ return () => {
5193
+ unsubscribed = true;
5194
+ if (reconnectTimer) {
5195
+ clearTimeout(reconnectTimer);
5196
+ reconnectTimer = null;
5197
+ }
5198
+ const c = activeClient;
5199
+ activeClient = null;
5200
+ if (c) {
5201
+ c.query(`UNLISTEN ${channel}`).catch(() => {}).finally(() => {
5202
+ try {
5203
+ c.removeAllListeners?.("notification");
5204
+ c.removeAllListeners?.("error");
5205
+ c.release();
5206
+ } catch {}
5207
+ });
5208
+ }
5209
+ };
4994
5210
  }
4995
5211
  }
4996
5212
  // src/queue/SqliteQueueStorage.ts
4997
- import { createServiceToken as createServiceToken22, makeFingerprint as makeFingerprint7, sleep as sleep4, uuid4 as uuid46 } from "@workglow/util";
5213
+ import { createServiceToken as createServiceToken22, makeFingerprint as makeFingerprint7, sleep as sleep4, uuid4 as uuid47 } from "@workglow/util";
4998
5214
  var SQLITE_QUEUE_STORAGE = createServiceToken22("jobqueue.storage.sqlite");
4999
5215
 
5000
5216
  class SqliteQueueStorage {
5001
5217
  db;
5002
5218
  queueName;
5003
5219
  options;
5220
+ scope = "process";
5004
5221
  prefixes;
5005
5222
  prefixValues;
5006
5223
  tableName;
@@ -5077,7 +5294,7 @@ class SqliteQueueStorage {
5077
5294
  }
5078
5295
  async add(job) {
5079
5296
  const now = new Date().toISOString();
5080
- job.job_run_id = job.job_run_id ?? uuid46();
5297
+ job.job_run_id = job.job_run_id ?? uuid47();
5081
5298
  job.queue = this.queueName;
5082
5299
  job.fingerprint = await makeFingerprint7(job.input);
5083
5300
  job.status = JobStatus.PENDING;
@@ -5344,11 +5561,12 @@ class SqliteQueueStorage {
5344
5561
  }
5345
5562
  }
5346
5563
  // src/queue/SupabaseQueueStorage.ts
5347
- import { createServiceToken as createServiceToken23, deepEqual as deepEqual2, makeFingerprint as makeFingerprint8, uuid4 as uuid47 } from "@workglow/util";
5564
+ import { createServiceToken as createServiceToken23, deepEqual as deepEqual2, makeFingerprint as makeFingerprint8, uuid4 as uuid48 } from "@workglow/util";
5348
5565
  var SUPABASE_QUEUE_STORAGE = createServiceToken23("jobqueue.storage.supabase");
5349
5566
 
5350
5567
  class SupabaseQueueStorage {
5351
5568
  queueName;
5569
+ scope = "cluster";
5352
5570
  client;
5353
5571
  prefixes;
5354
5572
  prefixValues;
@@ -5473,7 +5691,7 @@ class SupabaseQueueStorage {
5473
5691
  async add(job) {
5474
5692
  const now = new Date().toISOString();
5475
5693
  job.queue = this.queueName;
5476
- job.job_run_id = job.job_run_id ?? uuid47();
5694
+ job.job_run_id = job.job_run_id ?? uuid48();
5477
5695
  job.fingerprint = await makeFingerprint8(job.input);
5478
5696
  job.status = JobStatus.PENDING;
5479
5697
  job.progress = 0;
@@ -5876,6 +6094,7 @@ var POSTGRES_RATE_LIMITER_STORAGE = createServiceToken24("ratelimiter.storage.po
5876
6094
 
5877
6095
  class PostgresRateLimiterStorage {
5878
6096
  db;
6097
+ scope = "cluster";
5879
6098
  prefixes;
5880
6099
  prefixValues;
5881
6100
  executionTableName;
@@ -5942,6 +6161,80 @@ class PostgresRateLimiterStorage {
5942
6161
  )
5943
6162
  `);
5944
6163
  }
6164
+ async tryReserveExecution(queueName, maxExecutions, windowMs) {
6165
+ const prefixColumnNames = this.getPrefixColumnNames();
6166
+ const prefixParamValues = this.getPrefixParamValues();
6167
+ const prefixCount = prefixColumnNames.length;
6168
+ const queueParam = `$${prefixCount + 1}`;
6169
+ const windowStartParam = `$${prefixCount + 2}`;
6170
+ const lockKeyParts = [`'${this.executionTableName}'`];
6171
+ for (let i = 0;i < prefixCount; i++) {
6172
+ lockKeyParts.push(`$${i + 1}::text`);
6173
+ }
6174
+ lockKeyParts.push(`${queueParam}::text`);
6175
+ const lockKeyExpr = `hashtextextended(${lockKeyParts.join(" || '|' || ")}, 0)`;
6176
+ const prefixWhere = prefixCount > 0 ? " AND " + prefixColumnNames.map((p, i) => `${p} = $${i + 1}`).join(" AND ") : "";
6177
+ const prefixInsertCols = prefixCount > 0 ? prefixColumnNames.join(", ") + ", " : "";
6178
+ const prefixInsertPlaceholders = prefixCount > 0 ? prefixColumnNames.map((_, i) => `$${i + 1}`).join(", ") + ", " : "";
6179
+ const windowStart = new Date(Date.now() - windowMs).toISOString();
6180
+ const supportsConnect = typeof this.db.connect === "function";
6181
+ if (!supportsConnect) {
6182
+ const dbAny = this.db;
6183
+ const ctorName = dbAny.constructor?.name;
6184
+ const looksLikePGlite = typeof dbAny.exec === "function" && dbAny.waitReady !== undefined;
6185
+ const looksLikePGLitePool = ctorName === "PGLitePool";
6186
+ if (!looksLikePGlite && !looksLikePGLitePool) {
6187
+ throw new Error(`PostgresRateLimiterStorage.tryReserveExecution requires a pg.Pool with connect() or a known single-connection wrapper (PGLitePool, PGlite); got ${ctorName ?? typeof this.db}. A multi-connection pool without connect() would dispatch the advisory lock and the INSERT to different sessions, breaking atomicity.`);
6188
+ }
6189
+ }
6190
+ const conn = supportsConnect ? await this.db.connect() : { query: this.db.query.bind(this.db), release: () => {} };
6191
+ try {
6192
+ await conn.query("BEGIN");
6193
+ try {
6194
+ await conn.query(`SELECT pg_advisory_xact_lock(${lockKeyExpr})`, [...prefixParamValues, queueName]);
6195
+ const countResult = await conn.query(`
6196
+ SELECT COUNT(*)::int AS n
6197
+ FROM ${this.executionTableName}
6198
+ WHERE queue_name = ${queueParam} AND executed_at > ${windowStartParam}${prefixWhere}
6199
+ `, [...prefixParamValues, queueName, windowStart]);
6200
+ const n = countResult.rows[0]?.n ?? 0;
6201
+ if (n >= maxExecutions) {
6202
+ await conn.query("COMMIT");
6203
+ return null;
6204
+ }
6205
+ const naResult = await conn.query(`
6206
+ SELECT next_available_at
6207
+ FROM ${this.nextAvailableTableName}
6208
+ WHERE queue_name = ${queueParam}${prefixWhere}
6209
+ `, [...prefixParamValues, queueName]);
6210
+ const nextAvailableAt = naResult.rows[0]?.next_available_at ?? null;
6211
+ if (nextAvailableAt && new Date(nextAvailableAt).getTime() > Date.now()) {
6212
+ await conn.query("COMMIT");
6213
+ return null;
6214
+ }
6215
+ const insertResult = await conn.query(`
6216
+ INSERT INTO ${this.executionTableName} (${prefixInsertCols}queue_name)
6217
+ VALUES (${prefixInsertPlaceholders}${queueParam})
6218
+ RETURNING id
6219
+ `, [...prefixParamValues, queueName]);
6220
+ await conn.query("COMMIT");
6221
+ return insertResult.rows[0]?.id ?? null;
6222
+ } catch (err) {
6223
+ try {
6224
+ await conn.query("ROLLBACK");
6225
+ } catch {}
6226
+ throw err;
6227
+ }
6228
+ } finally {
6229
+ conn.release();
6230
+ }
6231
+ }
6232
+ async releaseExecution(queueName, token) {
6233
+ if (token === null || token === undefined)
6234
+ return;
6235
+ const { conditions: prefixConditions, params: prefixParams } = this.buildPrefixWhereClause(3);
6236
+ await this.db.query(`DELETE FROM ${this.executionTableName} WHERE id = $1 AND queue_name = $2${prefixConditions}`, [token, queueName, ...prefixParams]);
6237
+ }
5945
6238
  async recordExecution(queueName) {
5946
6239
  const prefixColumnNames = this.getPrefixColumnNames();
5947
6240
  const prefixColumnsInsert = prefixColumnNames.length > 0 ? prefixColumnNames.join(", ") + ", " : "";
@@ -6014,6 +6307,7 @@ var SQLITE_RATE_LIMITER_STORAGE = createServiceToken25("ratelimiter.storage.sqli
6014
6307
 
6015
6308
  class SqliteRateLimiterStorage {
6016
6309
  db;
6310
+ scope = "process";
6017
6311
  prefixes;
6018
6312
  prefixValues;
6019
6313
  executionTableName;
@@ -6077,6 +6371,55 @@ class SqliteRateLimiterStorage {
6077
6371
  );
6078
6372
  `);
6079
6373
  }
6374
+ async tryReserveExecution(queueName, maxExecutions, windowMs) {
6375
+ const prefixColumnNames = this.getPrefixColumnNames();
6376
+ const prefixParamValues = this.getPrefixParamValues();
6377
+ const prefixConditions = this.buildPrefixWhereClause();
6378
+ const prefixColumnsInsert = prefixColumnNames.length > 0 ? prefixColumnNames.join(", ") + ", " : "";
6379
+ const prefixPlaceholders = prefixColumnNames.length > 0 ? prefixColumnNames.map(() => "?").join(", ") + ", " : "";
6380
+ const windowStart = toSQLiteTimestamp(new Date(Date.now() - windowMs));
6381
+ let insertedId = null;
6382
+ const txn = this.db.transaction(() => {
6383
+ const countStmt = this.db.prepare(`
6384
+ SELECT COUNT(*) AS count
6385
+ FROM ${this.executionTableName}
6386
+ WHERE queue_name = ? AND executed_at > ?${prefixConditions}
6387
+ `);
6388
+ const countRow = countStmt.get(queueName, windowStart, ...prefixParamValues);
6389
+ if ((countRow?.count ?? 0) >= maxExecutions) {
6390
+ return;
6391
+ }
6392
+ const naStmt = this.db.prepare(`
6393
+ SELECT next_available_at
6394
+ FROM ${this.nextAvailableTableName}
6395
+ WHERE queue_name = ?${prefixConditions}
6396
+ `);
6397
+ const naRow = naStmt.get(queueName, ...prefixParamValues);
6398
+ if (naRow?.next_available_at) {
6399
+ const nextAvailable = new Date(naRow.next_available_at + "Z").getTime();
6400
+ if (nextAvailable > Date.now()) {
6401
+ return;
6402
+ }
6403
+ }
6404
+ const insertStmt = this.db.prepare(`
6405
+ INSERT INTO ${this.executionTableName} (${prefixColumnsInsert}queue_name)
6406
+ VALUES (${prefixPlaceholders}?)
6407
+ `);
6408
+ const info = insertStmt.run(...prefixParamValues, queueName);
6409
+ insertedId = info.lastInsertRowid;
6410
+ });
6411
+ txn();
6412
+ if (insertedId == null)
6413
+ return null;
6414
+ return typeof insertedId === "bigint" ? Number(insertedId) : insertedId;
6415
+ }
6416
+ async releaseExecution(queueName, token) {
6417
+ if (token === null || token === undefined)
6418
+ return;
6419
+ const prefixConditions = this.buildPrefixWhereClause();
6420
+ const prefixParams = this.getPrefixParamValues();
6421
+ this.db.prepare(`DELETE FROM ${this.executionTableName} WHERE id = ? AND queue_name = ?${prefixConditions}`).run(token, queueName, ...prefixParams);
6422
+ }
6080
6423
  async recordExecution(queueName) {
6081
6424
  const prefixColumnNames = this.getPrefixColumnNames();
6082
6425
  const prefixColumnsInsert = prefixColumnNames.length > 0 ? prefixColumnNames.join(", ") + ", " : "";
@@ -6152,6 +6495,7 @@ import { createServiceToken as createServiceToken26 } from "@workglow/util";
6152
6495
  var SUPABASE_RATE_LIMITER_STORAGE = createServiceToken26("ratelimiter.storage.supabase");
6153
6496
 
6154
6497
  class SupabaseRateLimiterStorage {
6498
+ scope = "cluster";
6155
6499
  client;
6156
6500
  prefixes;
6157
6501
  prefixValues;
@@ -6234,6 +6578,75 @@ class SupabaseRateLimiterStorage {
6234
6578
  if (nextTableError && nextTableError.code !== "42P07") {
6235
6579
  throw nextTableError;
6236
6580
  }
6581
+ const fnName = this.atomicReserveFunctionName();
6582
+ const prefixSig = this.prefixes.map((p) => `${p.name} ${this.getPrefixColumnType(p.type)}`).join(", ");
6583
+ const prefixSigPrefix = prefixSig ? prefixSig + ", " : "";
6584
+ const prefixWhere = this.prefixes.length > 0 ? " AND " + this.prefixes.map((p) => `${p.name} = _${p.name}`).join(" AND ") : "";
6585
+ const prefixInsertCols = this.prefixes.length > 0 ? this.prefixes.map((p) => p.name).join(", ") + ", " : "";
6586
+ const prefixInsertVals = this.prefixes.length > 0 ? this.prefixes.map((p) => `_${p.name}`).join(", ") + ", " : "";
6587
+ const lockKeyParts = [`'${this.executionTableName}'`, ...this.prefixes.map((p) => `_${p.name}::text`), `_queue_name::text`];
6588
+ const lockKeyExpr = `hashtextextended(${lockKeyParts.join(" || '|' || ")}, 0)`;
6589
+ const createFnSql = `
6590
+ CREATE OR REPLACE FUNCTION ${fnName}(
6591
+ ${prefixSigPrefix}_queue_name TEXT, _window_start TIMESTAMPTZ, _max_exec INT
6592
+ ) RETURNS BIGINT AS $fn$
6593
+ DECLARE
6594
+ _count INT;
6595
+ _next TIMESTAMPTZ;
6596
+ _new_id BIGINT;
6597
+ BEGIN
6598
+ PERFORM pg_advisory_xact_lock(${lockKeyExpr});
6599
+ SELECT COUNT(*) INTO _count FROM ${this.executionTableName}
6600
+ WHERE queue_name = _queue_name AND executed_at > _window_start${prefixWhere};
6601
+ IF _count >= _max_exec THEN RETURN NULL; END IF;
6602
+ SELECT next_available_at INTO _next FROM ${this.nextAvailableTableName}
6603
+ WHERE queue_name = _queue_name${prefixWhere};
6604
+ IF _next IS NOT NULL AND _next > NOW() THEN RETURN NULL; END IF;
6605
+ INSERT INTO ${this.executionTableName} (${prefixInsertCols}queue_name)
6606
+ VALUES (${prefixInsertVals}_queue_name)
6607
+ RETURNING id INTO _new_id;
6608
+ RETURN _new_id;
6609
+ END;
6610
+ $fn$ LANGUAGE plpgsql;
6611
+ `;
6612
+ const { error: fnError } = await this.client.rpc("exec_sql", { query: createFnSql });
6613
+ if (fnError) {
6614
+ throw fnError;
6615
+ }
6616
+ }
6617
+ atomicReserveFunctionName() {
6618
+ return `${this.executionTableName}_try_reserve`.slice(0, 63);
6619
+ }
6620
+ async tryReserveExecution(queueName, maxExecutions, windowMs) {
6621
+ const args = {
6622
+ _queue_name: queueName,
6623
+ _window_start: new Date(Date.now() - windowMs).toISOString(),
6624
+ _max_exec: maxExecutions
6625
+ };
6626
+ for (const p of this.prefixes) {
6627
+ args[`_${p.name}`] = this.prefixValues[p.name];
6628
+ }
6629
+ const { data, error } = await this.client.rpc(this.atomicReserveFunctionName(), args);
6630
+ if (error)
6631
+ throw error;
6632
+ if (data === null || data === undefined)
6633
+ return null;
6634
+ if (Array.isArray(data)) {
6635
+ if (data.length === 0)
6636
+ return null;
6637
+ const first = Object.values(data[0])[0];
6638
+ return first ?? null;
6639
+ }
6640
+ return data;
6641
+ }
6642
+ async releaseExecution(queueName, token) {
6643
+ if (token === null || token === undefined)
6644
+ return;
6645
+ let del = this.client.from(this.executionTableName).delete().eq("id", token).eq("queue_name", queueName);
6646
+ del = this.applyPrefixFilters(del);
6647
+ const { error: delError } = await del;
6648
+ if (delError)
6649
+ throw delError;
6237
6650
  }
6238
6651
  async recordExecution(queueName) {
6239
6652
  const prefixInsertValues = this.getPrefixInsertValues();
@@ -6999,7 +7412,7 @@ class SqliteAiVectorStorage extends SqliteTabularStorage {
6999
7412
  import { createServiceToken as createServiceToken28 } from "@workglow/util";
7000
7413
 
7001
7414
  // src/tabular/IndexedDbTabularStorage.ts
7002
- import { createServiceToken as createServiceToken27, deepEqual as deepEqual4, makeFingerprint as makeFingerprint9, uuid4 as uuid48 } from "@workglow/util";
7415
+ import { createServiceToken as createServiceToken27, deepEqual as deepEqual4, makeFingerprint as makeFingerprint9, uuid4 as uuid49 } from "@workglow/util";
7003
7416
 
7004
7417
  // src/util/IndexedDbTable.ts
7005
7418
  import { deepEqual as deepEqual3 } from "@workglow/util";
@@ -7390,7 +7803,7 @@ class IndexedDbTabularStorage extends BaseTabularStorage {
7390
7803
  }
7391
7804
  generateKeyValue(columnName, strategy) {
7392
7805
  if (strategy === "uuid") {
7393
- return uuid48();
7806
+ return uuid49();
7394
7807
  }
7395
7808
  throw new Error(`IndexedDB autoincrement keys are generated by the database, not client-side. Column: ${columnName}`);
7396
7809
  }
@@ -7963,13 +8376,15 @@ class IndexedDbTabularStorage extends BaseTabularStorage {
7963
8376
  }
7964
8377
  let matches = true;
7965
8378
  for (const [col, crit] of Object.entries(criteria)) {
7966
- if (!isSearchCondition(crit))
7967
- continue;
7968
8379
  const pos = keyPathPositions.get(col);
7969
8380
  if (pos === undefined)
7970
8381
  continue;
8382
+ if (pos < prefix.length)
8383
+ continue;
7971
8384
  const valFromKey = Array.isArray(key) ? key[pos] : key;
7972
- if (!compareWithOperator(valFromKey, crit.operator, crit.value)) {
8385
+ const op = isSearchCondition(crit) ? crit.operator : "=";
8386
+ const val = isSearchCondition(crit) ? crit.value : crit;
8387
+ if (!compareWithOperator(valFromKey, op, val)) {
7973
8388
  matches = false;
7974
8389
  break;
7975
8390
  }
@@ -8061,6 +8476,7 @@ import { createServiceToken as createServiceToken29 } from "@workglow/util";
8061
8476
  var INDEXED_DB_RATE_LIMITER_STORAGE = createServiceToken29("ratelimiter.storage.indexedDb");
8062
8477
 
8063
8478
  class IndexedDbRateLimiterStorage {
8479
+ scope = "process";
8064
8480
  executionDb;
8065
8481
  nextAvailableDb;
8066
8482
  executionTableName;
@@ -8129,6 +8545,72 @@ class IndexedDbRateLimiterStorage {
8129
8545
  ];
8130
8546
  this.nextAvailableDb = await ensureIndexedDbTable(this.nextAvailableTableName, buildKeyPath(["queue_name"]).join("_"), nextAvailableIndexes, this.migrationOptions);
8131
8547
  }
8548
+ async tryReserveExecution(queueName, maxExecutions, windowMs) {
8549
+ const nextIso = await this.getNextAvailableTime(queueName);
8550
+ if (nextIso && new Date(nextIso).getTime() > Date.now()) {
8551
+ return null;
8552
+ }
8553
+ const execDb = await this.getExecutionDb();
8554
+ const prefixKeyValues = this.getPrefixKeyValues();
8555
+ const windowStartIso = new Date(Date.now() - windowMs).toISOString();
8556
+ const execTx = execDb.transaction(this.executionTableName, "readwrite");
8557
+ const execStore = execTx.objectStore(this.executionTableName);
8558
+ const insertedId = crypto.randomUUID();
8559
+ return new Promise((resolve, reject) => {
8560
+ let liveCount = 0;
8561
+ let didInsert = false;
8562
+ const liveRange = IDBKeyRange.bound([...prefixKeyValues, queueName, windowStartIso], [...prefixKeyValues, queueName, "￿"], true, false);
8563
+ const cursorReq = execStore.index("queue_executed_at").openCursor(liveRange);
8564
+ cursorReq.onsuccess = (event) => {
8565
+ const cursor = event.target.result;
8566
+ if (cursor) {
8567
+ const record2 = cursor.value;
8568
+ if (this.matchesPrefixes(record2)) {
8569
+ liveCount++;
8570
+ }
8571
+ cursor.continue();
8572
+ return;
8573
+ }
8574
+ if (liveCount >= maxExecutions) {
8575
+ execTx.abort();
8576
+ return;
8577
+ }
8578
+ const record = {
8579
+ id: insertedId,
8580
+ queue_name: queueName,
8581
+ executed_at: new Date().toISOString()
8582
+ };
8583
+ for (const [k, v] of Object.entries(this.prefixValues)) {
8584
+ record[k] = v;
8585
+ }
8586
+ const addReq = execStore.add(record);
8587
+ didInsert = true;
8588
+ addReq.onerror = () => {
8589
+ try {
8590
+ execTx.abort();
8591
+ } catch {}
8592
+ reject(addReq.error);
8593
+ };
8594
+ };
8595
+ cursorReq.onerror = () => reject(cursorReq.error);
8596
+ execTx.oncomplete = () => resolve(didInsert ? insertedId : null);
8597
+ execTx.onerror = () => reject(execTx.error);
8598
+ execTx.onabort = () => resolve(null);
8599
+ });
8600
+ }
8601
+ async releaseExecution(queueName, token) {
8602
+ if (token === null || token === undefined)
8603
+ return;
8604
+ const db = await this.getExecutionDb();
8605
+ const tx = db.transaction(this.executionTableName, "readwrite");
8606
+ const store = tx.objectStore(this.executionTableName);
8607
+ return new Promise((resolve, reject) => {
8608
+ const req = store.delete(token);
8609
+ req.onerror = () => reject(req.error);
8610
+ tx.oncomplete = () => resolve();
8611
+ tx.onerror = () => reject(tx.error);
8612
+ });
8613
+ }
8132
8614
  async recordExecution(queueName) {
8133
8615
  const db = await this.getExecutionDb();
8134
8616
  const tx = db.transaction(this.executionTableName, "readwrite");
@@ -8278,11 +8760,12 @@ class IndexedDbRateLimiterStorage {
8278
8760
  }
8279
8761
  }
8280
8762
  // src/queue/IndexedDbQueueStorage.ts
8281
- import { createServiceToken as createServiceToken30, deepEqual as deepEqual5, makeFingerprint as makeFingerprint10, uuid4 as uuid49 } from "@workglow/util";
8763
+ import { createServiceToken as createServiceToken30, deepEqual as deepEqual5, makeFingerprint as makeFingerprint10, uuid4 as uuid410 } from "@workglow/util";
8282
8764
  var INDEXED_DB_QUEUE_STORAGE = createServiceToken30("jobqueue.storage.indexedDb");
8283
8765
 
8284
8766
  class IndexedDbQueueStorage {
8285
8767
  queueName;
8768
+ scope = "process";
8286
8769
  db;
8287
8770
  tableName;
8288
8771
  migrationOptions;
@@ -8359,8 +8842,8 @@ class IndexedDbQueueStorage {
8359
8842
  const db = await this.getDb();
8360
8843
  const now = new Date().toISOString();
8361
8844
  const jobWithPrefixes = job;
8362
- jobWithPrefixes.id = jobWithPrefixes.id ?? uuid49();
8363
- jobWithPrefixes.job_run_id = jobWithPrefixes.job_run_id ?? uuid49();
8845
+ jobWithPrefixes.id = jobWithPrefixes.id ?? uuid410();
8846
+ jobWithPrefixes.job_run_id = jobWithPrefixes.job_run_id ?? uuid410();
8364
8847
  jobWithPrefixes.queue = this.queueName;
8365
8848
  jobWithPrefixes.fingerprint = await makeFingerprint10(jobWithPrefixes.input);
8366
8849
  jobWithPrefixes.status = JobStatus.PENDING;
@@ -8857,10 +9340,204 @@ class IndexedDbQueueStorage {
8857
9340
  }
8858
9341
  }
8859
9342
  }
8860
- // src/vector/IndexedDbVectorStorage.ts
9343
+ // src/tabular/SharedInMemoryTabularStorage.ts
8861
9344
  import { createServiceToken as createServiceToken31 } from "@workglow/util";
9345
+ var SHARED_IN_MEMORY_TABULAR_REPOSITORY = createServiceToken31("storage.tabularRepository.sharedInMemory");
9346
+ var SYNC_TIMEOUT = 1000;
9347
+ var MAX_PENDING_MESSAGES = 1000;
9348
+
9349
+ class SharedInMemoryTabularStorage extends BaseTabularStorage {
9350
+ channel = null;
9351
+ channelName;
9352
+ inMemoryRepo;
9353
+ isInitialized = false;
9354
+ syncInProgress = false;
9355
+ pendingMessages = [];
9356
+ constructor(channelName = "tabular_store", schema, primaryKeyNames, indexes = [], clientProvidedKeys = "if-missing") {
9357
+ super(schema, primaryKeyNames, indexes, clientProvidedKeys);
9358
+ this.channelName = channelName;
9359
+ this.inMemoryRepo = new InMemoryTabularStorage(schema, primaryKeyNames, indexes, clientProvidedKeys);
9360
+ this.setupEventForwarding();
9361
+ this.initializeBroadcastChannel();
9362
+ }
9363
+ isBroadcastChannelAvailable() {
9364
+ return typeof BroadcastChannel !== "undefined";
9365
+ }
9366
+ initializeBroadcastChannel() {
9367
+ if (!this.isBroadcastChannelAvailable()) {
9368
+ console.warn("BroadcastChannel is not available. Tab synchronization will not work.");
9369
+ return;
9370
+ }
9371
+ try {
9372
+ this.channel = new BroadcastChannel(this.channelName);
9373
+ this.channel.onmessage = (event) => {
9374
+ this.handleBroadcastMessage(event.data);
9375
+ };
9376
+ this.syncFromOtherTabs();
9377
+ } catch (error) {
9378
+ console.error("Failed to initialize BroadcastChannel:", error);
9379
+ }
9380
+ }
9381
+ setupEventForwarding() {
9382
+ this.inMemoryRepo.on("put", (entity) => {
9383
+ this.events.emit("put", entity);
9384
+ });
9385
+ this.inMemoryRepo.on("get", (key, entity) => {
9386
+ this.events.emit("get", key, entity);
9387
+ });
9388
+ this.inMemoryRepo.on("query", (key, entities) => {
9389
+ this.events.emit("query", key, entities);
9390
+ });
9391
+ this.inMemoryRepo.on("delete", (key) => {
9392
+ this.events.emit("delete", key);
9393
+ });
9394
+ this.inMemoryRepo.on("clearall", () => {
9395
+ this.events.emit("clearall");
9396
+ });
9397
+ }
9398
+ async handleBroadcastMessage(message) {
9399
+ if (this.syncInProgress && message.type !== "SYNC_RESPONSE") {
9400
+ if (this.pendingMessages.length < MAX_PENDING_MESSAGES) {
9401
+ this.pendingMessages.push(message);
9402
+ }
9403
+ return;
9404
+ }
9405
+ switch (message.type) {
9406
+ case "SYNC_REQUEST":
9407
+ const all = await this.inMemoryRepo.getAll();
9408
+ if (this.channel && all) {
9409
+ this.channel.postMessage({
9410
+ type: "SYNC_RESPONSE",
9411
+ data: all
9412
+ });
9413
+ }
9414
+ break;
9415
+ case "SYNC_RESPONSE":
9416
+ if (message.data && Array.isArray(message.data)) {
9417
+ await this.copyDataFromArray(message.data);
9418
+ }
9419
+ this.syncInProgress = false;
9420
+ await this.drainPendingMessages();
9421
+ break;
9422
+ case "PUT":
9423
+ await this.inMemoryRepo.put(message.entity);
9424
+ break;
9425
+ case "PUT_BULK":
9426
+ await this.inMemoryRepo.putBulk(message.entities);
9427
+ break;
9428
+ case "DELETE":
9429
+ await this.inMemoryRepo.delete(message.key);
9430
+ break;
9431
+ case "DELETE_ALL":
9432
+ await this.inMemoryRepo.deleteAll();
9433
+ break;
9434
+ case "DELETE_SEARCH":
9435
+ await this.inMemoryRepo.deleteSearch(message.criteria);
9436
+ break;
9437
+ }
9438
+ }
9439
+ async drainPendingMessages() {
9440
+ while (!this.syncInProgress && this.pendingMessages.length > 0) {
9441
+ const messages = this.pendingMessages;
9442
+ this.pendingMessages = [];
9443
+ for (const message of messages) {
9444
+ await this.handleBroadcastMessage(message);
9445
+ if (this.syncInProgress) {
9446
+ break;
9447
+ }
9448
+ }
9449
+ }
9450
+ }
9451
+ syncFromOtherTabs() {
9452
+ if (!this.channel)
9453
+ return;
9454
+ this.syncInProgress = true;
9455
+ this.channel.postMessage({ type: "SYNC_REQUEST" });
9456
+ setTimeout(() => {
9457
+ if (this.syncInProgress) {
9458
+ this.syncInProgress = false;
9459
+ this.drainPendingMessages().catch((error) => {
9460
+ console.error("Failed to drain pending messages after sync timeout", error);
9461
+ });
9462
+ }
9463
+ }, SYNC_TIMEOUT);
9464
+ }
9465
+ async copyDataFromArray(entities) {
9466
+ if (entities.length === 0)
9467
+ return;
9468
+ await this.inMemoryRepo.deleteAll();
9469
+ await this.inMemoryRepo.putBulk(entities);
9470
+ }
9471
+ broadcast(message) {
9472
+ if (this.channel) {
9473
+ this.channel.postMessage(message);
9474
+ }
9475
+ }
9476
+ async setupDatabase() {
9477
+ if (this.isInitialized)
9478
+ return;
9479
+ this.isInitialized = true;
9480
+ await this.syncFromOtherTabs();
9481
+ }
9482
+ async put(value) {
9483
+ const result = await this.inMemoryRepo.put(value);
9484
+ this.broadcast({ type: "PUT", entity: result });
9485
+ return result;
9486
+ }
9487
+ async putBulk(values) {
9488
+ const result = await this.inMemoryRepo.putBulk(values);
9489
+ this.broadcast({ type: "PUT_BULK", entities: result });
9490
+ return result;
9491
+ }
9492
+ async get(key) {
9493
+ return await this.inMemoryRepo.get(key);
9494
+ }
9495
+ async delete(value) {
9496
+ await this.inMemoryRepo.delete(value);
9497
+ const { key } = this.separateKeyValueFromCombined(value);
9498
+ this.broadcast({ type: "DELETE", key });
9499
+ }
9500
+ async deleteAll() {
9501
+ await this.inMemoryRepo.deleteAll();
9502
+ this.broadcast({ type: "DELETE_ALL" });
9503
+ }
9504
+ async getAll(options) {
9505
+ return await this.inMemoryRepo.getAll(options);
9506
+ }
9507
+ async size() {
9508
+ return await this.inMemoryRepo.size();
9509
+ }
9510
+ async getBulk(offset, limit) {
9511
+ return await this.inMemoryRepo.getBulk(offset, limit);
9512
+ }
9513
+ async query(criteria, options) {
9514
+ return await this.inMemoryRepo.query(criteria, options);
9515
+ }
9516
+ async queryIndex(criteria, options) {
9517
+ return await this.inMemoryRepo.queryIndex(criteria, options);
9518
+ }
9519
+ async deleteSearch(criteria) {
9520
+ await this.inMemoryRepo.deleteSearch(criteria);
9521
+ this.broadcast({
9522
+ type: "DELETE_SEARCH",
9523
+ criteria
9524
+ });
9525
+ }
9526
+ subscribeToChanges(callback, options) {
9527
+ return this.inMemoryRepo.subscribeToChanges(callback, options);
9528
+ }
9529
+ destroy() {
9530
+ if (this.channel) {
9531
+ this.channel.close();
9532
+ this.channel = null;
9533
+ }
9534
+ this.inMemoryRepo.destroy();
9535
+ }
9536
+ }
9537
+ // src/vector/IndexedDbVectorStorage.ts
9538
+ import { createServiceToken as createServiceToken32 } from "@workglow/util";
8862
9539
  import { cosineSimilarity as cosineSimilarity5 } from "@workglow/util/schema";
8863
- var IDB_VECTOR_REPOSITORY = createServiceToken31("storage.vectorRepository.indexedDb");
9540
+ var IDB_VECTOR_REPOSITORY = createServiceToken32("storage.vectorRepository.indexedDb");
8864
9541
  function matchesFilter4(metadata, filter) {
8865
9542
  for (const [key, value] of Object.entries(filter)) {
8866
9543
  if (metadata[key] !== value) {
@@ -8988,6 +9665,7 @@ export {
8988
9665
  SqliteQueueStorage,
8989
9666
  SqliteKvStorage,
8990
9667
  SqliteAiVectorStorage,
9668
+ SharedInMemoryTabularStorage,
8991
9669
  SUPABASE_TABULAR_REPOSITORY,
8992
9670
  SUPABASE_RATE_LIMITER_STORAGE,
8993
9671
  SUPABASE_QUEUE_STORAGE,
@@ -8996,6 +9674,7 @@ export {
8996
9674
  SQLITE_RATE_LIMITER_STORAGE,
8997
9675
  SQLITE_QUEUE_STORAGE,
8998
9676
  SQLITE_KV_REPOSITORY,
9677
+ SHARED_IN_MEMORY_TABULAR_REPOSITORY,
8999
9678
  RATE_LIMITER_STORAGE,
9000
9679
  QUEUE_STORAGE,
9001
9680
  PostgresVectorStorage,
@@ -9050,4 +9729,4 @@ export {
9050
9729
  BaseTabularStorage
9051
9730
  };
9052
9731
 
9053
- //# debugId=FEAE1DAF1DC5CDCE64756E2164756E21
9732
+ //# debugId=7AF69C59EB644D3464756E2164756E21