@workglow/indexeddb 0.2.36 → 0.2.37

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 (29) hide show
  1. package/dist/job-queue/IndexedDbJobStore.d.ts +29 -0
  2. package/dist/job-queue/IndexedDbJobStore.d.ts.map +1 -0
  3. package/dist/job-queue/IndexedDbMessageQueue.d.ts +38 -0
  4. package/dist/job-queue/IndexedDbMessageQueue.d.ts.map +1 -0
  5. package/dist/job-queue/IndexedDbQueueStorage.d.ts +38 -7
  6. package/dist/job-queue/IndexedDbQueueStorage.d.ts.map +1 -1
  7. package/dist/job-queue/IndexedDbRateLimiterStorage.d.ts +1 -2
  8. package/dist/job-queue/IndexedDbRateLimiterStorage.d.ts.map +1 -1
  9. package/dist/job-queue/browser.js +415 -60
  10. package/dist/job-queue/browser.js.map +10 -7
  11. package/dist/job-queue/common.d.ts +3 -0
  12. package/dist/job-queue/common.d.ts.map +1 -1
  13. package/dist/job-queue/createIndexedDbQueue.d.ts +20 -0
  14. package/dist/job-queue/createIndexedDbQueue.d.ts.map +1 -0
  15. package/dist/job-queue/node.js +415 -60
  16. package/dist/job-queue/node.js.map +10 -7
  17. package/dist/migrations/indexedDbQueueMigrations.d.ts +11 -5
  18. package/dist/migrations/indexedDbQueueMigrations.d.ts.map +1 -1
  19. package/dist/storage/IndexedDbKvStorage.d.ts +1 -1
  20. package/dist/storage/IndexedDbKvStorage.d.ts.map +1 -1
  21. package/dist/storage/IndexedDbTabularStorage.d.ts +1 -1
  22. package/dist/storage/IndexedDbTabularStorage.d.ts.map +1 -1
  23. package/dist/storage/IndexedDbVectorStorage.d.ts +2 -2
  24. package/dist/storage/IndexedDbVectorStorage.d.ts.map +1 -1
  25. package/dist/storage/browser.js +9 -9
  26. package/dist/storage/browser.js.map +5 -5
  27. package/dist/storage/node.js +9 -9
  28. package/dist/storage/node.js.map +5 -5
  29. package/package.json +7 -7
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Steven Roussey <sroussey@gmail.com>
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { IndexedDbJobStore } from "./IndexedDbJobStore";
7
+ import { IndexedDbMessageQueue } from "./IndexedDbMessageQueue";
8
+ import { IndexedDbQueueStorage, type IndexedDbQueueStorageOptions } from "./IndexedDbQueueStorage";
9
+ /**
10
+ * Factory for the paired IndexedDB message queue and job store. Both
11
+ * facades share a single underlying {@link IndexedDbQueueStorage} so writes
12
+ * through one are observable through the other.
13
+ */
14
+ export declare function createIndexedDbQueue<Input, Output>(queueName: string, opts?: IndexedDbQueueStorageOptions): {
15
+ messageQueue: IndexedDbMessageQueue<Input, Output>;
16
+ jobStore: IndexedDbJobStore<Input, Output>;
17
+ /** @internal — exposed for callers that still need the legacy storage object. */
18
+ core: IndexedDbQueueStorage<Input, Output>;
19
+ };
20
+ //# sourceMappingURL=createIndexedDbQueue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createIndexedDbQueue.d.ts","sourceRoot":"","sources":["../../src/job-queue/createIndexedDbQueue.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAA8B,MAAM,yBAAyB,CAAC;AAC5F,OAAO,EAAE,qBAAqB,EAAE,KAAK,4BAA4B,EAAE,MAAM,yBAAyB,CAAC;AAEnG;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAChD,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE,4BAA4B,GAClC;IACD,YAAY,EAAE,qBAAqB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACnD,QAAQ,EAAE,iBAAiB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAC3C,iFAAiF;IACjF,IAAI,EAAE,qBAAqB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;CAC5C,CAQA"}
@@ -1,28 +1,7 @@
1
1
  // src/job-queue/IndexedDbQueueStorage.ts
2
- import { createServiceToken, deepEqual, makeFingerprint, uuid4 } from "@workglow/util";
2
+ import { JobStatus, validateLeaseMs } from "@workglow/job-queue";
3
3
  import { HybridSubscriptionManager } from "@workglow/storage";
4
-
5
- // src/storage/openIdb.ts
6
- function openIdb(dbName, options = {}) {
7
- return new Promise((resolve, reject) => {
8
- const req = indexedDB.open(dbName, options.version);
9
- req.onsuccess = () => {
10
- const db = req.result;
11
- db.onversionchange = () => db.close();
12
- resolve(db);
13
- };
14
- req.onupgradeneeded = (ev) => options.onUpgradeNeeded?.(ev);
15
- req.onerror = () => {
16
- const err = req.error;
17
- if (err && err.name === "VersionError") {
18
- reject(new Error(`IndexedDB ${dbName} exists at a higher version than ${options.version ?? "current"}`));
19
- return;
20
- }
21
- reject(err);
22
- };
23
- req.onblocked = () => reject(new Error(`IndexedDB ${dbName} is blocked — close other tabs using this database.`));
24
- });
25
- }
4
+ import { createServiceToken, deepEqual, makeFingerprint, uuid4 } from "@workglow/util";
26
5
 
27
6
  // src/migrations/IndexedDbMigrationRunner.ts
28
7
  import {
@@ -317,6 +296,62 @@ function indexedDbQueueMigrations(tableName, prefixes) {
317
296
  });
318
297
  }
319
298
  }
299
+ },
300
+ {
301
+ component,
302
+ version: 2,
303
+ description: "Rename queue_status_run_after → queue_status_visible_at; backfill five legacy field renames (run_after → visible_at, run_attempts → attempts, last_ran_at → last_attempted_at, max_retries → max_attempts, worker_id → lease_owner)",
304
+ up({ tx }) {
305
+ const store = tx.objectStore(tableName);
306
+ const indexNames = Array.from(store.indexNames);
307
+ if (indexNames.includes("queue_status_run_after")) {
308
+ store.deleteIndex("queue_status_run_after");
309
+ }
310
+ const cursorReq = store.openCursor();
311
+ cursorReq.onsuccess = () => {
312
+ const cursor = cursorReq.result;
313
+ if (!cursor) {
314
+ if (!Array.from(store.indexNames).includes("queue_status_visible_at")) {
315
+ store.createIndex("queue_status_visible_at", k(["queue", "status", "visible_at"]), {
316
+ unique: false
317
+ });
318
+ }
319
+ return;
320
+ }
321
+ const row = cursor.value;
322
+ if (row) {
323
+ let dirty = false;
324
+ if (row.run_after !== undefined && row.visible_at === undefined) {
325
+ row.visible_at = row.run_after;
326
+ delete row.run_after;
327
+ dirty = true;
328
+ }
329
+ if (row.run_attempts !== undefined && row.attempts === undefined) {
330
+ row.attempts = row.run_attempts;
331
+ delete row.run_attempts;
332
+ dirty = true;
333
+ }
334
+ if (row.last_ran_at !== undefined && row.last_attempted_at === undefined) {
335
+ row.last_attempted_at = row.last_ran_at;
336
+ delete row.last_ran_at;
337
+ dirty = true;
338
+ }
339
+ if (row.max_retries !== undefined && row.max_attempts === undefined) {
340
+ row.max_attempts = row.max_retries;
341
+ delete row.max_retries;
342
+ dirty = true;
343
+ }
344
+ if (row.worker_id !== undefined && row.lease_owner === undefined) {
345
+ row.lease_owner = row.worker_id;
346
+ delete row.worker_id;
347
+ dirty = true;
348
+ }
349
+ if (dirty)
350
+ cursor.update(row);
351
+ }
352
+ cursor.continue();
353
+ };
354
+ }
320
355
  }
321
356
  ];
322
357
  }
@@ -327,8 +362,29 @@ function indexedDbQueueMigrationGroup(tableName, prefixes) {
327
362
  };
328
363
  }
329
364
 
365
+ // src/storage/openIdb.ts
366
+ function openIdb(dbName, options = {}) {
367
+ return new Promise((resolve, reject) => {
368
+ const req = indexedDB.open(dbName, options.version);
369
+ req.onsuccess = () => {
370
+ const db = req.result;
371
+ db.onversionchange = () => db.close();
372
+ resolve(db);
373
+ };
374
+ req.onupgradeneeded = (ev) => options.onUpgradeNeeded?.(ev);
375
+ req.onerror = () => {
376
+ const err = req.error;
377
+ if (err && err.name === "VersionError") {
378
+ reject(new Error(`IndexedDB ${dbName} exists at a higher version than ${options.version ?? "current"}`));
379
+ return;
380
+ }
381
+ reject(err);
382
+ };
383
+ req.onblocked = () => reject(new Error(`IndexedDB ${dbName} is blocked — close other tabs using this database.`));
384
+ });
385
+ }
386
+
330
387
  // src/job-queue/IndexedDbQueueStorage.ts
331
- import { JobStatus } from "@workglow/job-queue";
332
388
  var INDEXED_DB_QUEUE_STORAGE = createServiceToken("jobqueue.storage.indexedDb");
333
389
 
334
390
  class IndexedDbQueueStorage {
@@ -399,7 +455,7 @@ class IndexedDbQueueStorage {
399
455
  jobWithPrefixes.progress_message = "";
400
456
  jobWithPrefixes.progress_details = null;
401
457
  jobWithPrefixes.created_at = now;
402
- jobWithPrefixes.run_after = now;
458
+ jobWithPrefixes.visible_at = now;
403
459
  for (const [key, value] of Object.entries(this.prefixValues)) {
404
460
  jobWithPrefixes[key] = value;
405
461
  }
@@ -437,7 +493,7 @@ class IndexedDbQueueStorage {
437
493
  const db = await this.getDb();
438
494
  const tx = db.transaction(this.tableName, "readonly");
439
495
  const store = tx.objectStore(this.tableName);
440
- const index = store.index("queue_status_run_after");
496
+ const index = store.index("queue_status_visible_at");
441
497
  const prefixKeyValues = this.getPrefixKeyValues();
442
498
  return new Promise((resolve, reject) => {
443
499
  const ret = new Map;
@@ -460,24 +516,44 @@ class IndexedDbQueueStorage {
460
516
  tx.onerror = () => reject(tx.error);
461
517
  });
462
518
  }
463
- async next(workerId) {
519
+ async next(workerId, opts) {
520
+ const leaseMs = opts?.leaseMs ?? 30000;
521
+ validateLeaseMs(leaseMs, "leaseMs");
464
522
  const db = await this.getDb();
465
523
  const tx = db.transaction(this.tableName, "readwrite");
466
524
  const store = tx.objectStore(this.tableName);
467
- const index = store.index("queue_status_run_after");
468
525
  const now = new Date().toISOString();
526
+ const leaseExpiry = new Date(Date.now() + leaseMs).toISOString();
469
527
  const prefixKeyValues = this.getPrefixKeyValues();
470
528
  const claimToken = workerId;
471
529
  const jobToReturn = await new Promise((resolve, reject) => {
472
- const cursorRequest = index.openCursor(IDBKeyRange.bound([...prefixKeyValues, this.queueName, JobStatus.PENDING, ""], [...prefixKeyValues, this.queueName, JobStatus.PENDING, now], false, false));
473
530
  let claimedJob;
474
- let cursorStopped = false;
475
- cursorRequest.onsuccess = (e) => {
531
+ const tryClaimJob = (job) => {
532
+ const isLeaseExpiryReclaim = job.status === JobStatus.PROCESSING;
533
+ job.status = JobStatus.PROCESSING;
534
+ job.last_attempted_at = now;
535
+ job.lease_owner = claimToken;
536
+ job.lease_expires_at = leaseExpiry;
537
+ if (isLeaseExpiryReclaim) {
538
+ job.attempts = (job.attempts ?? 0) + 1;
539
+ }
540
+ job.abort_requested_at = null;
541
+ try {
542
+ const updateRequest = store.put(job);
543
+ updateRequest.onsuccess = () => {
544
+ claimedJob = job;
545
+ };
546
+ updateRequest.onerror = () => {};
547
+ } catch {}
548
+ };
549
+ const pendingIndex = store.index("queue_status_visible_at");
550
+ const pendingRequest = pendingIndex.openCursor(IDBKeyRange.bound([...prefixKeyValues, this.queueName, JobStatus.PENDING, ""], [...prefixKeyValues, this.queueName, JobStatus.PENDING, now], false, false));
551
+ pendingRequest.onsuccess = (e) => {
476
552
  const cursor = e.target.result;
477
553
  if (!cursor) {
478
- return;
479
- }
480
- if (cursorStopped) {
554
+ if (!claimedJob) {
555
+ tryExpiredLeaseScan();
556
+ }
481
557
  return;
482
558
  }
483
559
  const job = cursor.value;
@@ -485,25 +561,29 @@ class IndexedDbQueueStorage {
485
561
  cursor.continue();
486
562
  return;
487
563
  }
488
- job.status = JobStatus.PROCESSING;
489
- job.last_ran_at = now;
490
- job.worker_id = claimToken;
491
- try {
492
- const updateRequest = store.put(job);
493
- updateRequest.onsuccess = () => {
494
- claimedJob = job;
495
- cursorStopped = true;
496
- };
497
- updateRequest.onerror = (err) => {
498
- console.error("Failed to update job status:", err);
564
+ tryClaimJob(job);
565
+ };
566
+ pendingRequest.onerror = () => reject(pendingRequest.error);
567
+ const tryExpiredLeaseScan = () => {
568
+ const processingIndex = store.index("queue_status_visible_at");
569
+ const processingRequest = processingIndex.openCursor(IDBKeyRange.bound([...prefixKeyValues, this.queueName, JobStatus.PROCESSING, ""], [...prefixKeyValues, this.queueName, JobStatus.PROCESSING, "￿"], false, false));
570
+ processingRequest.onsuccess = (e) => {
571
+ const cursor = e.target.result;
572
+ if (!cursor)
573
+ return;
574
+ const job = cursor.value;
575
+ if (job.queue !== this.queueName || job.status !== JobStatus.PROCESSING || !this.matchesPrefixes(job)) {
499
576
  cursor.continue();
500
- };
501
- } catch (err) {
502
- console.error("Error updating job:", err);
503
- cursor.continue();
504
- }
577
+ return;
578
+ }
579
+ if (job.lease_expires_at && job.lease_expires_at >= now) {
580
+ cursor.continue();
581
+ return;
582
+ }
583
+ tryClaimJob(job);
584
+ };
585
+ processingRequest.onerror = () => reject(processingRequest.error);
505
586
  };
506
- cursorRequest.onerror = () => reject(cursorRequest.error);
507
587
  tx.oncomplete = () => {
508
588
  if (claimedJob) {
509
589
  this.hybridManager?.notifyLocalChange();
@@ -519,7 +599,7 @@ class IndexedDbQueueStorage {
519
599
  if (!verifiedJob) {
520
600
  return;
521
601
  }
522
- if (verifiedJob.worker_id !== claimToken) {
602
+ if (verifiedJob.lease_owner !== claimToken) {
523
603
  return;
524
604
  }
525
605
  if (verifiedJob.status !== JobStatus.PROCESSING) {
@@ -527,6 +607,15 @@ class IndexedDbQueueStorage {
527
607
  }
528
608
  return verifiedJob;
529
609
  }
610
+ async extendLease(id, workerId, ms) {
611
+ validateLeaseMs(ms, "ms");
612
+ const job = await this.get(id);
613
+ if (!job || job.status !== JobStatus.PROCESSING || job.lease_owner !== workerId) {
614
+ throw new Error(`extendLease failed: job ${String(id)} is not PROCESSING or lease is not owned by worker ${workerId}`);
615
+ }
616
+ job.lease_expires_at = new Date(Date.now() + ms).toISOString();
617
+ await this.put(job);
618
+ }
530
619
  async size(status = JobStatus.PENDING) {
531
620
  const db = await this.getDb();
532
621
  const prefixKeyValues = this.getPrefixKeyValues();
@@ -553,10 +642,12 @@ class IndexedDbQueueStorage {
553
642
  reject(new Error(`Job ${job.id} not found or does not belong to queue ${this.queueName}`));
554
643
  return;
555
644
  }
556
- const currentAttempts = existing.run_attempts ?? 0;
557
- job.run_attempts = currentAttempts + 1;
645
+ const currentAttempts = existing.attempts ?? 0;
646
+ job.attempts = currentAttempts + 1;
647
+ const jobAsRecord = job;
648
+ jobAsRecord.abort_requested_at = null;
558
649
  job.queue = this.queueName;
559
- const jobWithPrefixes = job;
650
+ const jobWithPrefixes = jobAsRecord;
560
651
  for (const [key, value] of Object.entries(this.prefixValues)) {
561
652
  jobWithPrefixes[key] = value;
562
653
  }
@@ -572,23 +663,51 @@ class IndexedDbQueueStorage {
572
663
  tx.onerror = () => reject(tx.error);
573
664
  });
574
665
  }
575
- async release(id) {
666
+ async releaseClaim(id) {
576
667
  const job = await this.get(id);
577
668
  if (!job)
578
669
  return;
579
670
  job.status = JobStatus.PENDING;
580
- job.worker_id = null;
671
+ job.lease_owner = null;
581
672
  job.progress = 0;
582
673
  job.progress_message = "";
583
674
  job.progress_details = null;
675
+ job.abort_requested_at = null;
584
676
  await this.put(job);
585
677
  }
586
678
  async abort(id) {
587
679
  const job = await this.get(id);
588
680
  if (!job)
589
681
  return;
590
- job.status = JobStatus.ABORTING;
591
- await this.complete(job);
682
+ const now = new Date().toISOString();
683
+ if (job.status === JobStatus.PENDING) {
684
+ job.status = JobStatus.FAILED;
685
+ job.abort_requested_at = now;
686
+ job.completed_at = now;
687
+ await this.put(job);
688
+ } else if (job.status === JobStatus.PROCESSING) {
689
+ job.abort_requested_at = now;
690
+ await this.put(job);
691
+ }
692
+ }
693
+ async saveStatus(id, status) {
694
+ const db = await this.getDb();
695
+ const tx = db.transaction(this.tableName, "readwrite");
696
+ const store = tx.objectStore(this.tableName);
697
+ const getRequest = store.get(id);
698
+ return new Promise((resolve, reject) => {
699
+ getRequest.onsuccess = () => {
700
+ const record = getRequest.result;
701
+ if (!record) {
702
+ resolve();
703
+ return;
704
+ }
705
+ const putRequest = store.put({ ...record, status });
706
+ putRequest.onsuccess = () => resolve();
707
+ putRequest.onerror = () => reject(putRequest.error);
708
+ };
709
+ getRequest.onerror = () => reject(getRequest.error);
710
+ });
592
711
  }
593
712
  async getByRunId(job_run_id) {
594
713
  const db = await this.getDb();
@@ -607,6 +726,34 @@ class IndexedDbQueueStorage {
607
726
  tx.onerror = () => reject(tx.error);
608
727
  });
609
728
  }
729
+ async finalize(id, fields) {
730
+ const existing = await this.get(id);
731
+ if (!existing)
732
+ return;
733
+ const updated = existing;
734
+ if ("output" in fields)
735
+ updated.output = fields.output ?? null;
736
+ if ("error" in fields)
737
+ updated.error = fields.error ?? null;
738
+ if ("error_code" in fields)
739
+ updated.error_code = fields.error_code ?? null;
740
+ if ("status" in fields)
741
+ updated.status = fields.status;
742
+ if ("completed_at" in fields)
743
+ updated.completed_at = fields.completed_at ?? null;
744
+ if ("abort_requested_at" in fields) {
745
+ updated.abort_requested_at = fields.abort_requested_at ?? null;
746
+ }
747
+ if ("lease_owner" in fields)
748
+ updated.lease_owner = fields.lease_owner ?? null;
749
+ if ("progress" in fields)
750
+ updated.progress = fields.progress ?? 0;
751
+ if ("progress_message" in fields)
752
+ updated.progress_message = fields.progress_message ?? "";
753
+ if ("progress_details" in fields)
754
+ updated.progress_details = fields.progress_details ?? null;
755
+ await this.put(updated);
756
+ }
610
757
  async deleteAll() {
611
758
  const db = await this.getDb();
612
759
  const tx = db.transaction(this.tableName, "readwrite");
@@ -888,6 +1035,211 @@ class IndexedDbQueueStorage {
888
1035
  }
889
1036
  }
890
1037
  }
1038
+ // src/job-queue/IndexedDbMessageQueue.ts
1039
+ class IndexedDbClaim {
1040
+ core;
1041
+ pending;
1042
+ id;
1043
+ body;
1044
+ attempts;
1045
+ workerId;
1046
+ constructor(core, pending, id, body, attempts, workerId) {
1047
+ this.core = core;
1048
+ this.pending = pending;
1049
+ this.id = id;
1050
+ this.body = body;
1051
+ this.attempts = attempts;
1052
+ this.workerId = workerId;
1053
+ }
1054
+ async ack(result) {
1055
+ const buf = this.pending.get(this.id);
1056
+ this.pending.delete(this.id);
1057
+ const current = await this.core.get(this.id) ?? this.body;
1058
+ const output = result !== undefined ? result : buf?.output !== undefined ? buf.output : current.output ?? null;
1059
+ await this.core.finalize(this.id, {
1060
+ output,
1061
+ error: null,
1062
+ error_code: null,
1063
+ status: "COMPLETED",
1064
+ completed_at: current.completed_at ?? new Date().toISOString()
1065
+ });
1066
+ }
1067
+ async retry(opts) {
1068
+ this.pending.delete(this.id);
1069
+ const delay = opts?.delaySeconds ?? 0;
1070
+ const current = await this.core.get(this.id) ?? this.body;
1071
+ await this.core.complete({
1072
+ ...current,
1073
+ status: "PENDING",
1074
+ lease_owner: null,
1075
+ lease_expires_at: null,
1076
+ visible_at: new Date(Date.now() + delay * 1000).toISOString(),
1077
+ progress: 0,
1078
+ progress_message: "",
1079
+ progress_details: null
1080
+ });
1081
+ }
1082
+ async fail(opts) {
1083
+ opts?.permanent;
1084
+ const buf = this.pending.get(this.id);
1085
+ this.pending.delete(this.id);
1086
+ const current = await this.core.get(this.id) ?? this.body;
1087
+ const error = opts?.error !== undefined ? opts.error : buf?.error !== undefined ? buf.error : current.error ?? null;
1088
+ const errorCode = opts?.errorCode !== undefined ? opts.errorCode : buf?.errorCode !== undefined ? buf.errorCode : current.error_code ?? null;
1089
+ const abortRequested = opts?.abortRequested !== undefined ? opts.abortRequested : buf?.abortRequested ?? false;
1090
+ await this.core.finalize(this.id, {
1091
+ error,
1092
+ error_code: errorCode,
1093
+ abort_requested_at: abortRequested ? current.abort_requested_at ?? new Date().toISOString() : current.abort_requested_at ?? null,
1094
+ status: "FAILED",
1095
+ completed_at: current.completed_at ?? new Date().toISOString()
1096
+ });
1097
+ }
1098
+ async extendLease(ms) {
1099
+ await this.core.extendLease(this.id, this.workerId, ms);
1100
+ }
1101
+ async disable() {
1102
+ this.pending.delete(this.id);
1103
+ const current = await this.core.get(this.id);
1104
+ const completedAt = current?.completed_at ?? new Date().toISOString();
1105
+ await this.core.finalize(this.id, {
1106
+ status: "DISABLED",
1107
+ completed_at: completedAt,
1108
+ lease_owner: null,
1109
+ progress: 0,
1110
+ progress_message: "",
1111
+ progress_details: null
1112
+ });
1113
+ }
1114
+ }
1115
+
1116
+ class IndexedDbMessageQueue {
1117
+ scope = "process";
1118
+ core;
1119
+ pending;
1120
+ constructor(core, pending) {
1121
+ this.core = core;
1122
+ this.pending = pending;
1123
+ }
1124
+ async send(body, opts) {
1125
+ return this.core.add(applySendOptions(body, opts));
1126
+ }
1127
+ async sendBatch(bodies, opts) {
1128
+ const ids = [];
1129
+ for (const body of bodies) {
1130
+ ids.push(await this.send(body, opts));
1131
+ }
1132
+ return ids;
1133
+ }
1134
+ async receive(opts) {
1135
+ const max = Math.max(1, opts.max ?? 1);
1136
+ const claims = [];
1137
+ while (claims.length < max) {
1138
+ const job = await this.core.next(opts.workerId, { leaseMs: opts.leaseMs });
1139
+ if (!job)
1140
+ break;
1141
+ claims.push(new IndexedDbClaim(this.core, this.pending, job.id, job, job.attempts ?? 0, opts.workerId));
1142
+ }
1143
+ return claims;
1144
+ }
1145
+ async releaseClaim(id) {
1146
+ this.pending.delete(id);
1147
+ await this.core.releaseClaim(id);
1148
+ }
1149
+ async migrate() {
1150
+ await this.core.migrate();
1151
+ }
1152
+ getMigrations() {
1153
+ return this.core.getMigrations();
1154
+ }
1155
+ subscribeToChanges(callback, options) {
1156
+ return this.core.subscribeToChanges(callback, options);
1157
+ }
1158
+ }
1159
+ function applySendOptions(body, opts) {
1160
+ if (!opts)
1161
+ return body;
1162
+ const out = { ...body };
1163
+ if (opts.delaySeconds != null) {
1164
+ out.visible_at = new Date(Date.now() + opts.delaySeconds * 1000).toISOString();
1165
+ }
1166
+ if (opts.timeoutSeconds != null) {
1167
+ out.deadline_at = new Date(Date.now() + opts.timeoutSeconds * 1000).toISOString();
1168
+ }
1169
+ if (opts.fingerprint != null)
1170
+ out.fingerprint = opts.fingerprint;
1171
+ if (opts.jobRunId != null)
1172
+ out.job_run_id = opts.jobRunId;
1173
+ if (opts.maxAttempts != null)
1174
+ out.max_attempts = opts.maxAttempts;
1175
+ return out;
1176
+ }
1177
+ // src/job-queue/IndexedDbJobStore.ts
1178
+ class IndexedDbJobStore {
1179
+ core;
1180
+ pending;
1181
+ constructor(core, pending) {
1182
+ this.core = core;
1183
+ this.pending = pending;
1184
+ }
1185
+ get(id) {
1186
+ return this.core.get(id);
1187
+ }
1188
+ async peek(status, num) {
1189
+ return this.core.peek(status, num);
1190
+ }
1191
+ size(status) {
1192
+ return this.core.size(status);
1193
+ }
1194
+ async getByRunId(runId) {
1195
+ return this.core.getByRunId(runId);
1196
+ }
1197
+ outputForInput(input) {
1198
+ return this.core.outputForInput(input);
1199
+ }
1200
+ async saveProgress(id, progress, message, details) {
1201
+ await this.core.saveProgress(id, progress, message, details);
1202
+ }
1203
+ async saveResult(id, output) {
1204
+ const buf = this.pending.get(id) ?? {};
1205
+ buf.output = output ?? null;
1206
+ this.pending.set(id, buf);
1207
+ }
1208
+ async saveError(id, error, errorCode, abortRequested) {
1209
+ const buf = this.pending.get(id) ?? {};
1210
+ buf.error = error;
1211
+ buf.errorCode = errorCode;
1212
+ buf.abortRequested = abortRequested;
1213
+ this.pending.set(id, buf);
1214
+ }
1215
+ async deleteByStatusAndAge(status, olderThanMs) {
1216
+ await this.core.deleteJobsByStatusAndAge(status, olderThanMs);
1217
+ }
1218
+ async delete(id) {
1219
+ this.pending.delete(id);
1220
+ await this.core.delete(id);
1221
+ }
1222
+ async deleteAll() {
1223
+ this.pending.clear();
1224
+ await this.core.deleteAll();
1225
+ }
1226
+ async abort(id) {
1227
+ await this.core.abort(id);
1228
+ }
1229
+ async saveStatus(id, status) {
1230
+ await this.core.saveStatus(id, status);
1231
+ }
1232
+ }
1233
+ // src/job-queue/createIndexedDbQueue.ts
1234
+ function createIndexedDbQueue(queueName, opts) {
1235
+ const core = new IndexedDbQueueStorage(queueName, opts);
1236
+ const pending = new Map;
1237
+ return {
1238
+ messageQueue: new IndexedDbMessageQueue(core, pending),
1239
+ jobStore: new IndexedDbJobStore(core, pending),
1240
+ core
1241
+ };
1242
+ }
891
1243
  // src/job-queue/IndexedDbRateLimiterStorage.ts
892
1244
  import { createServiceToken as createServiceToken2 } from "@workglow/util";
893
1245
 
@@ -1232,12 +1584,15 @@ export {
1232
1584
  indexedDbRateLimiterExecutionMigrations,
1233
1585
  indexedDbQueueMigrations,
1234
1586
  indexedDbQueueMigrationGroup,
1587
+ createIndexedDbQueue,
1235
1588
  MigrationAbortedByOtherTabError,
1236
1589
  IndexedDbRateLimiterStorage,
1237
1590
  IndexedDbQueueStorage,
1238
1591
  IndexedDbMigrationRunner,
1592
+ IndexedDbMessageQueue,
1593
+ IndexedDbJobStore,
1239
1594
  INDEXED_DB_RATE_LIMITER_STORAGE,
1240
1595
  INDEXED_DB_QUEUE_STORAGE
1241
1596
  };
1242
1597
 
1243
- //# debugId=676DC1BE327F4D4964756E2164756E21
1598
+ //# debugId=90BAEB2124C483DB64756E2164756E21