@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.
- package/dist/job-queue/IndexedDbJobStore.d.ts +29 -0
- package/dist/job-queue/IndexedDbJobStore.d.ts.map +1 -0
- package/dist/job-queue/IndexedDbMessageQueue.d.ts +38 -0
- package/dist/job-queue/IndexedDbMessageQueue.d.ts.map +1 -0
- package/dist/job-queue/IndexedDbQueueStorage.d.ts +38 -7
- package/dist/job-queue/IndexedDbQueueStorage.d.ts.map +1 -1
- package/dist/job-queue/IndexedDbRateLimiterStorage.d.ts +1 -2
- package/dist/job-queue/IndexedDbRateLimiterStorage.d.ts.map +1 -1
- package/dist/job-queue/browser.js +415 -60
- package/dist/job-queue/browser.js.map +10 -7
- package/dist/job-queue/common.d.ts +3 -0
- package/dist/job-queue/common.d.ts.map +1 -1
- package/dist/job-queue/createIndexedDbQueue.d.ts +20 -0
- package/dist/job-queue/createIndexedDbQueue.d.ts.map +1 -0
- package/dist/job-queue/node.js +415 -60
- package/dist/job-queue/node.js.map +10 -7
- package/dist/migrations/indexedDbQueueMigrations.d.ts +11 -5
- package/dist/migrations/indexedDbQueueMigrations.d.ts.map +1 -1
- package/dist/storage/IndexedDbKvStorage.d.ts +1 -1
- package/dist/storage/IndexedDbKvStorage.d.ts.map +1 -1
- package/dist/storage/IndexedDbTabularStorage.d.ts +1 -1
- package/dist/storage/IndexedDbTabularStorage.d.ts.map +1 -1
- package/dist/storage/IndexedDbVectorStorage.d.ts +2 -2
- package/dist/storage/IndexedDbVectorStorage.d.ts.map +1 -1
- package/dist/storage/browser.js +9 -9
- package/dist/storage/browser.js.map +5 -5
- package/dist/storage/node.js +9 -9
- package/dist/storage/node.js.map +5 -5
- package/package.json +7 -7
|
@@ -1,28 +1,7 @@
|
|
|
1
1
|
// src/job-queue/IndexedDbQueueStorage.ts
|
|
2
|
-
import {
|
|
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.
|
|
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("
|
|
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
|
-
|
|
475
|
-
|
|
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
|
-
|
|
479
|
-
|
|
480
|
-
|
|
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
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
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
|
-
|
|
502
|
-
|
|
503
|
-
|
|
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.
|
|
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.
|
|
557
|
-
job.
|
|
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 =
|
|
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
|
|
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.
|
|
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
|
-
|
|
591
|
-
|
|
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=
|
|
1598
|
+
//# debugId=DD150AE4B3AEA97B64756E2164756E21
|