@workglow/indexeddb 0.5.0 → 0.6.0

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 (43) hide show
  1. package/dist/job-queue/IndexedDbQueueStorage.d.ts +0 -1
  2. package/dist/job-queue/IndexedDbRateLimiterStorage.d.ts +2 -3
  3. package/dist/job-queue/browser.d.ts +0 -1
  4. package/dist/job-queue/browser.js.map +3 -3
  5. package/dist/job-queue/common.d.ts +0 -1
  6. package/dist/job-queue/createIndexedDbQueue.d.ts +0 -1
  7. package/dist/job-queue/node.d.ts +0 -1
  8. package/dist/job-queue/node.js.map +3 -3
  9. package/dist/migrations/IndexedDbMigrationRunner.d.ts +0 -1
  10. package/dist/migrations/indexedDbQueueMigrations.d.ts +0 -1
  11. package/dist/migrations/indexedDbRateLimiterMigrations.d.ts +0 -1
  12. package/dist/storage/IndexedDbKvStorage.d.ts +0 -1
  13. package/dist/storage/IndexedDbTable.d.ts +1 -2
  14. package/dist/storage/IndexedDbTabularMigrationApplier.d.ts +0 -1
  15. package/dist/storage/IndexedDbTabularStorage.d.ts +0 -1
  16. package/dist/storage/IndexedDbVectorStorage.d.ts +0 -1
  17. package/dist/storage/browser.d.ts +0 -1
  18. package/dist/storage/browser.js +5 -5
  19. package/dist/storage/browser.js.map +3 -3
  20. package/dist/storage/common.d.ts +0 -1
  21. package/dist/storage/node.d.ts +0 -1
  22. package/dist/storage/node.js +5 -5
  23. package/dist/storage/node.js.map +3 -3
  24. package/dist/storage/openIdb.d.ts +0 -1
  25. package/package.json +7 -7
  26. package/dist/job-queue/IndexedDbQueueStorage.d.ts.map +0 -1
  27. package/dist/job-queue/IndexedDbRateLimiterStorage.d.ts.map +0 -1
  28. package/dist/job-queue/browser.d.ts.map +0 -1
  29. package/dist/job-queue/common.d.ts.map +0 -1
  30. package/dist/job-queue/createIndexedDbQueue.d.ts.map +0 -1
  31. package/dist/job-queue/node.d.ts.map +0 -1
  32. package/dist/migrations/IndexedDbMigrationRunner.d.ts.map +0 -1
  33. package/dist/migrations/indexedDbQueueMigrations.d.ts.map +0 -1
  34. package/dist/migrations/indexedDbRateLimiterMigrations.d.ts.map +0 -1
  35. package/dist/storage/IndexedDbKvStorage.d.ts.map +0 -1
  36. package/dist/storage/IndexedDbTable.d.ts.map +0 -1
  37. package/dist/storage/IndexedDbTabularMigrationApplier.d.ts.map +0 -1
  38. package/dist/storage/IndexedDbTabularStorage.d.ts.map +0 -1
  39. package/dist/storage/IndexedDbVectorStorage.d.ts.map +0 -1
  40. package/dist/storage/browser.d.ts.map +0 -1
  41. package/dist/storage/common.d.ts.map +0 -1
  42. package/dist/storage/node.d.ts.map +0 -1
  43. package/dist/storage/openIdb.d.ts.map +0 -1
@@ -130,4 +130,3 @@ export declare class IndexedDbQueueStorage<Input, Output> implements IQueueStora
130
130
  subscribeToChanges(callback: (change: QueueChangePayload<Input, Output>) => void, options?: QueueSubscribeOptions): () => void;
131
131
  destroy(): void;
132
132
  }
133
- //# sourceMappingURL=IndexedDbQueueStorage.d.ts.map
@@ -3,7 +3,7 @@
3
3
  * Copyright 2025 Steven Roussey <sroussey@gmail.com>
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
- import type { IRateLimiterStorage, PrefixColumn, RateLimiterStorageOptions, RateLimiterStorageScope } from "@workglow/job-queue";
6
+ import type { IRateLimiterStorage, LimiterToken, PrefixColumn, RateLimiterStorageOptions, RateLimiterStorageScope } from "@workglow/job-queue";
7
7
  export declare const INDEXED_DB_RATE_LIMITER_STORAGE: import("@workglow/util").ServiceToken<IRateLimiterStorage>;
8
8
  export type IndexedDbRateLimiterStorageOptions = RateLimiterStorageOptions;
9
9
  /**
@@ -62,7 +62,7 @@ export declare class IndexedDbRateLimiterStorage implements IRateLimiterStorage
62
62
  * tab serial execution, and the stricter count check is what protects the
63
63
  * primary maxExecutions invariant.
64
64
  */
65
- tryReserveExecution(queueName: string, maxExecutions: number, windowMs: number): Promise<unknown | null>;
65
+ tryReserveExecution(queueName: string, maxExecutions: number, windowMs: number): Promise<LimiterToken | null>;
66
66
  releaseExecution(queueName: string, token: unknown): Promise<void>;
67
67
  getExecutionCount(queueName: string, windowStartTime: string): Promise<number>;
68
68
  getOldestExecutionAtOffset(queueName: string, offset: number): Promise<string | undefined>;
@@ -70,4 +70,3 @@ export declare class IndexedDbRateLimiterStorage implements IRateLimiterStorage
70
70
  setNextAvailableTime(queueName: string, nextAvailableAt: string): Promise<void>;
71
71
  clear(queueName: string): Promise<void>;
72
72
  }
73
- //# sourceMappingURL=IndexedDbRateLimiterStorage.d.ts.map
@@ -4,4 +4,3 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
  export * from "./common";
7
- //# sourceMappingURL=browser.d.ts.map
@@ -2,15 +2,15 @@
2
2
  "version": 3,
3
3
  "sources": ["../../src/job-queue/IndexedDbQueueStorage.ts", "../../src/migrations/IndexedDbMigrationRunner.ts", "../../src/migrations/indexedDbQueueMigrations.ts", "../../src/storage/openIdb.ts", "../../src/job-queue/createIndexedDbQueue.ts", "../../src/job-queue/IndexedDbRateLimiterStorage.ts", "../../src/migrations/indexedDbRateLimiterMigrations.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type {\n IQueueStorage,\n JobStorageFormat,\n PrefixColumn,\n QueueChangePayload,\n QueueStorageOptions,\n QueueSubscribeOptions,\n} from \"@workglow/job-queue\";\nimport { JobStatus, validateLeaseMs } from \"@workglow/job-queue\";\nimport { HybridSubscriptionManager } from \"@workglow/storage\";\nimport { createServiceToken, deepEqual, getLogger, makeFingerprint, uuid4 } from \"@workglow/util\";\nimport { IndexedDbMigrationRunner } from \"../migrations/IndexedDbMigrationRunner\";\nimport { indexedDbQueueMigrations } from \"../migrations/indexedDbQueueMigrations\";\nimport { openIdb } from \"../storage/openIdb\";\n\nexport const INDEXED_DB_QUEUE_STORAGE = createServiceToken<IQueueStorage<any, any>>(\n \"jobqueue.storage.indexedDb\"\n);\n\nexport interface IndexedDbQueueStorageOptions extends QueueStorageOptions {\n /** Enable BroadcastChannel notifications (default: true) */\n readonly useBroadcastChannel?: boolean;\n /** Backup polling interval in ms (default: 5000, 0 to disable) */\n readonly backupPollingIntervalMs?: number;\n}\n\nexport class IndexedDbQueueStorage<Input, Output> implements IQueueStorage<Input, Output> {\n public readonly scope = \"process\" as const;\n private db: IDBDatabase | undefined;\n private readonly tableName: string;\n protected readonly prefixes: readonly PrefixColumn[];\n protected readonly prefixValues: Readonly<Record<string, string | number>>;\n private hybridManager: HybridSubscriptionManager<\n JobStorageFormat<Input, Output>,\n unknown,\n QueueChangePayload<Input, Output>\n > | null = null;\n private readonly hybridOptions: {\n readonly useBroadcastChannel: boolean;\n readonly backupPollingIntervalMs: number;\n };\n\n constructor(\n public readonly queueName: string,\n options: IndexedDbQueueStorageOptions = {}\n ) {\n this.prefixes = options.prefixes ?? [];\n this.prefixValues = options.prefixValues ?? {};\n this.hybridOptions = {\n useBroadcastChannel: options.useBroadcastChannel ?? true,\n backupPollingIntervalMs: options.backupPollingIntervalMs ?? 5000,\n };\n // Table name varies by prefix configuration to avoid conflicts\n if (this.prefixes.length > 0) {\n const prefixNames = this.prefixes.map((p) => p.name).join(\"_\");\n this.tableName = `jobs_${prefixNames}`;\n } else {\n this.tableName = \"jobs\";\n }\n }\n\n private matchesPrefixes(job: JobStorageFormat<Input, Output> & Record<string, unknown>): boolean {\n for (const [key, value] of Object.entries(this.prefixValues)) {\n if (job[key] !== value) {\n return false;\n }\n }\n return true;\n }\n\n /** Prefix values as an array in column order for index key construction. */\n private getPrefixKeyValues(): Array<string | number> {\n return this.prefixes.map((p) => this.prefixValues[p.name]);\n }\n\n private async getDb(): Promise<IDBDatabase> {\n if (this.db) return this.db;\n await this.migrate();\n return this.db!;\n }\n\n /**\n * Returns the versioned migrations that this storage's object store +\n * indexes depend on. Callers can compose them with other storages'\n * migrations under a shared {@link IndexedDbMigrationRunner}; otherwise\n * call {@link migrate}.\n */\n public getMigrations() {\n return indexedDbQueueMigrations(this.tableName, this.prefixes);\n }\n\n /**\n * Applies any pending migrations for this queue's IndexedDB database, then\n * opens a long-lived connection at the migrated version. Idempotent — a\n * second call closes any prior handle (so it doesn't pin the\n * pre-migration version or block another tab's upgrade), reruns the\n * runner (no-op if the bookkeeping store says everything is applied), and\n * reopens the connection.\n */\n public async migrate(): Promise<void> {\n if (this.db) {\n try {\n this.db.close();\n } catch {\n // ignore — close is best-effort\n }\n this.db = undefined;\n }\n const runner = new IndexedDbMigrationRunner(this.tableName);\n await runner.run(this.getMigrations());\n this.db = await openIdb(this.tableName);\n }\n\n public async add(job: JobStorageFormat<Input, Output>): Promise<unknown> {\n const db = await this.getDb();\n const now = new Date().toISOString();\n const jobWithPrefixes = job as JobStorageFormat<Input, Output> & Record<string, unknown>;\n jobWithPrefixes.id = jobWithPrefixes.id ?? uuid4();\n jobWithPrefixes.job_run_id = jobWithPrefixes.job_run_id ?? uuid4();\n jobWithPrefixes.queue = this.queueName;\n jobWithPrefixes.fingerprint =\n jobWithPrefixes.fingerprint ?? (await makeFingerprint(jobWithPrefixes.input));\n jobWithPrefixes.status = JobStatus.PENDING;\n jobWithPrefixes.progress = 0;\n jobWithPrefixes.progress_message = \"\";\n jobWithPrefixes.progress_details = null;\n jobWithPrefixes.created_at = now;\n // A caller-set future visible_at is a delayed send (delaySeconds) — keep it.\n jobWithPrefixes.visible_at = jobWithPrefixes.visible_at ?? now;\n\n for (const [key, value] of Object.entries(this.prefixValues)) {\n jobWithPrefixes[key] = value;\n }\n\n const tx = db.transaction(this.tableName, \"readwrite\");\n const store = tx.objectStore(this.tableName);\n\n return new Promise((resolve, reject) => {\n const request = store.add(jobWithPrefixes);\n\n tx.oncomplete = () => {\n this.hybridManager?.notifyLocalChange();\n resolve(jobWithPrefixes.id);\n };\n tx.onerror = () => reject(tx.error);\n request.onerror = () => reject(request.error);\n });\n }\n\n async get(id: unknown): Promise<JobStorageFormat<Input, Output> | undefined> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readonly\");\n const store = tx.objectStore(this.tableName);\n const request = store.get(id as string);\n return new Promise((resolve, reject) => {\n request.onsuccess = () => {\n const job = request.result as\n | (JobStorageFormat<Input, Output> & Record<string, unknown>)\n | undefined;\n if (job && job.queue === this.queueName && this.matchesPrefixes(job)) {\n resolve(job);\n } else {\n resolve(undefined);\n }\n };\n request.onerror = () => reject(request.error);\n tx.onerror = () => reject(tx.error);\n });\n }\n\n public async peek(\n status: JobStatus = JobStatus.PENDING,\n num: number = 100\n ): Promise<JobStorageFormat<Input, Output>[]> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readonly\");\n const store = tx.objectStore(this.tableName);\n const index = store.index(\"queue_status_visible_at\");\n const prefixKeyValues = this.getPrefixKeyValues();\n\n return new Promise((resolve, reject) => {\n const ret = new Map<unknown, JobStorageFormat<Input, Output>>();\n const keyRange = IDBKeyRange.bound(\n [...prefixKeyValues, this.queueName, status, \"\"],\n [...prefixKeyValues, this.queueName, status, \"\\uffff\"]\n );\n const cursorRequest = index.openCursor(keyRange);\n\n const handleCursor = (e: Event) => {\n const cursor = (e.target as IDBRequest<IDBCursorWithValue>).result;\n if (!cursor || ret.size >= num) {\n resolve(Array.from(ret.values()));\n return;\n }\n const job = cursor.value as JobStorageFormat<Input, Output> & Record<string, unknown>;\n // Map keyed by id ensures no duplicates\n if (this.matchesPrefixes(job)) {\n ret.set(cursor.value.id, cursor.value);\n }\n cursor.continue();\n };\n\n cursorRequest.onsuccess = handleCursor;\n cursorRequest.onerror = () => reject(cursorRequest.error);\n tx.onerror = () => reject(tx.error);\n });\n }\n\n /**\n * Retrieves the next job from the queue using optimistic locking.\n * Claims PENDING jobs ready to run, and also reclaims PROCESSING jobs whose\n * lease has expired (crash recovery). Sets lease_expires_at on the claimed row.\n *\n * IndexedDB uses snapshot isolation, so concurrent transactions can both see the same\n * PENDING job. To prevent processing the same job multiple times, this method:\n * 1. Claims a job by setting it to PROCESSING with a unique claim token\n * 2. After the transaction completes, re-reads the job to verify the claim succeeded\n * 3. If another worker claimed it first (different claim token), returns undefined\n *\n * @param workerId - Worker ID to associate with the job (required)\n * @param opts - Optional options including leaseMs (default 30000)\n * @returns A promise that resolves to the next job or undefined if the queue is empty.\n */\n public async next(\n workerId: string,\n opts?: { leaseMs?: number }\n ): Promise<JobStorageFormat<Input, Output> | undefined> {\n const leaseMs = opts?.leaseMs ?? 30000;\n validateLeaseMs(leaseMs, \"leaseMs\");\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readwrite\");\n const store = tx.objectStore(this.tableName);\n const now = new Date().toISOString();\n const leaseExpiry = new Date(Date.now() + leaseMs).toISOString();\n const prefixKeyValues = this.getPrefixKeyValues();\n\n // Used after the tx to verify we actually won the race to claim this job.\n const claimToken = workerId;\n\n const jobToReturn = await new Promise<JobStorageFormat<Input, Output> | undefined>(\n (resolve, reject) => {\n let claimedJob: JobStorageFormat<Input, Output> | undefined;\n\n const tryClaimJob = (job: JobStorageFormat<Input, Output> & Record<string, unknown>) => {\n // Lease-expiry reclaim consumes one attempt against max_attempts;\n // a fresh PENDING claim does not (the worker's validateJobState\n // FAILs the job when attempts >= max_attempts on the next step).\n const isLeaseExpiryReclaim = job.status === JobStatus.PROCESSING;\n job.status = JobStatus.PROCESSING;\n job.last_attempted_at = now;\n job.lease_owner = claimToken;\n job.lease_expires_at = leaseExpiry;\n if (isLeaseExpiryReclaim) {\n job.attempts = ((job.attempts as number | undefined) ?? 0) + 1;\n }\n // Always clear stale abort_requested_at on (re)claim. A PROCESSING\n // row may have had abort_requested_at set before the previous\n // worker crashed; the new owner must start with a clean slate.\n job.abort_requested_at = null;\n\n try {\n const updateRequest = store.put(job);\n updateRequest.onsuccess = () => {\n claimedJob = job;\n };\n updateRequest.onerror = () => {};\n } catch {\n // ignore\n }\n };\n\n const pendingIndex = store.index(\"queue_status_visible_at\");\n const pendingRequest = pendingIndex.openCursor(\n IDBKeyRange.bound(\n [...prefixKeyValues, this.queueName, JobStatus.PENDING, \"\"],\n [...prefixKeyValues, this.queueName, JobStatus.PENDING, now],\n false,\n false\n )\n );\n\n pendingRequest.onsuccess = (e) => {\n const cursor = (e.target as IDBRequest<IDBCursorWithValue>).result;\n if (!cursor) {\n // No PENDING job found — try expired-lease PROCESSING job\n if (!claimedJob) {\n tryExpiredLeaseScan();\n }\n return;\n }\n const job = cursor.value as JobStorageFormat<Input, Output> & Record<string, unknown>;\n if (\n job.queue !== this.queueName ||\n job.status !== JobStatus.PENDING ||\n !this.matchesPrefixes(job)\n ) {\n cursor.continue();\n return;\n }\n tryClaimJob(job);\n // Don't continue cursor — claim attempted\n };\n\n pendingRequest.onerror = () => reject(pendingRequest.error);\n\n const tryExpiredLeaseScan = () => {\n const processingIndex = store.index(\"queue_status_visible_at\");\n const processingRequest = processingIndex.openCursor(\n IDBKeyRange.bound(\n [...prefixKeyValues, this.queueName, JobStatus.PROCESSING, \"\"],\n [...prefixKeyValues, this.queueName, JobStatus.PROCESSING, \"￿\"],\n false,\n false\n )\n );\n\n processingRequest.onsuccess = (e) => {\n const cursor = (e.target as IDBRequest<IDBCursorWithValue>).result;\n if (!cursor) return; // none found, tx.oncomplete will resolve with undefined\n const job = cursor.value as JobStorageFormat<Input, Output> & Record<string, unknown>;\n if (\n job.queue !== this.queueName ||\n job.status !== JobStatus.PROCESSING ||\n !this.matchesPrefixes(job)\n ) {\n cursor.continue();\n return;\n }\n // null lease_expires_at = expired per spec\n if (job.lease_expires_at && job.lease_expires_at >= now) {\n cursor.continue();\n return;\n }\n tryClaimJob(job);\n };\n\n processingRequest.onerror = () => reject(processingRequest.error);\n };\n\n tx.oncomplete = () => {\n if (claimedJob) {\n this.hybridManager?.notifyLocalChange();\n }\n resolve(claimedJob);\n };\n tx.onerror = () => reject(tx.error);\n }\n );\n\n if (!jobToReturn) {\n return undefined;\n }\n\n // Verify we actually won the race by re-reading the job\n const verifiedJob = await this.get(jobToReturn.id);\n\n if (!verifiedJob) {\n return undefined;\n }\n\n if (verifiedJob.lease_owner !== claimToken) {\n return undefined;\n }\n\n if (verifiedJob.status !== JobStatus.PROCESSING) {\n return undefined;\n }\n\n return verifiedJob;\n }\n\n /**\n * Extend the lease on a currently PROCESSING job.\n * @param id - The ID of the job to extend the lease for\n * @param workerId - Worker ID that must match the current lease owner (lease_owner)\n * @param ms - Number of milliseconds to extend the lease by\n */\n public async extendLease(id: unknown, workerId: string, ms: number): Promise<void> {\n validateLeaseMs(ms, \"ms\");\n const job = await this.get(id);\n if (!job || job.status !== JobStatus.PROCESSING || job.lease_owner !== workerId) {\n throw new Error(\n `extendLease failed: job ${String(id)} is not PROCESSING or lease is not owned by worker ${workerId}`\n );\n }\n job.lease_expires_at = new Date(Date.now() + ms).toISOString();\n await this.put(job);\n }\n\n public async size(status = JobStatus.PENDING): Promise<number> {\n const db = await this.getDb();\n const prefixKeyValues = this.getPrefixKeyValues();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(this.tableName, \"readonly\");\n const store = tx.objectStore(this.tableName);\n const index = store.index(\"queue_status\");\n const keyRange = IDBKeyRange.only([...prefixKeyValues, this.queueName, status]);\n const request = index.count(keyRange);\n\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n tx.onerror = () => reject(tx.error);\n });\n }\n\n public async complete(job: JobStorageFormat<Input, Output>): Promise<void> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readwrite\");\n const store = tx.objectStore(this.tableName);\n\n return new Promise((resolve, reject) => {\n const getReq = store.get(job.id as string);\n getReq.onsuccess = () => {\n const existing = getReq.result as\n | (JobStorageFormat<Input, Output> & Record<string, unknown>)\n | undefined;\n if (!existing || existing.queue !== this.queueName || !this.matchesPrefixes(existing)) {\n // Contract: complete() must silently no-op (not throw) when the row is\n // missing or belongs to another queue. Lease-expiry races can legitimately\n // make the target row disappear between claim and complete, so the\n // transaction is allowed to commit with no write and resolve.\n return;\n }\n const currentAttempts = existing.attempts ?? 0;\n job.attempts = currentAttempts + 1;\n // PENDING-retry / terminal completion: always clear\n // abort_requested_at so a flag set DURING the attempt does not\n // survive into the next retry and immediately cancel it.\n const jobAsRecord = job as JobStorageFormat<Input, Output> & Record<string, unknown>;\n jobAsRecord.abort_requested_at = null;\n job.queue = this.queueName;\n\n const jobWithPrefixes = jobAsRecord;\n for (const [key, value] of Object.entries(this.prefixValues)) {\n jobWithPrefixes[key] = value;\n }\n\n const putReq = store.put(jobWithPrefixes);\n putReq.onsuccess = () => {};\n putReq.onerror = () => reject(putReq.error);\n };\n getReq.onerror = () => reject(getReq.error);\n\n tx.oncomplete = () => {\n this.hybridManager?.notifyLocalChange();\n resolve();\n };\n tx.onerror = () => reject(tx.error);\n });\n }\n\n public async releaseClaim(id: unknown): Promise<void> {\n const job = await this.get(id);\n if (!job) return;\n\n job.status = JobStatus.PENDING;\n job.lease_owner = null;\n job.progress = 0;\n job.progress_message = \"\";\n job.progress_details = null;\n // Clear stale abort_requested_at — an abort flag set during the previous\n // claim must not immediately cancel the next worker that picks up the row.\n (job as unknown as Record<string, unknown>).abort_requested_at = null;\n\n await this.put(job);\n }\n\n /**\n * Aborts a job.\n * - If PENDING: immediately mark as FAILED with abort_requested_at set.\n * - If PROCESSING: set abort_requested_at only (leave status as PROCESSING).\n * - Otherwise: no-op.\n */\n public async abort(id: unknown): Promise<void> {\n const job = await this.get(id);\n if (!job) return;\n const now = new Date().toISOString();\n if (job.status === JobStatus.PENDING) {\n job.status = JobStatus.FAILED;\n job.abort_requested_at = now;\n job.completed_at = now;\n // Use put() (not complete()) so attempts is NOT bumped — the worker\n // never actually attempted this job. Matches the cross-backend\n // contract verified in InMemory/Postgres.\n await this.put(job);\n } else if (job.status === JobStatus.PROCESSING) {\n job.abort_requested_at = now;\n await this.put(job);\n }\n }\n\n /** Force-overwrite status without touching attempts (used to persist DISABLED after lease release). */\n public async saveStatus(id: unknown, status: string): Promise<void> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readwrite\");\n const store = tx.objectStore(this.tableName);\n const getRequest = store.get(id as IDBValidKey);\n return new Promise((resolve, reject) => {\n getRequest.onsuccess = () => {\n const record = getRequest.result;\n if (!record) {\n resolve();\n return;\n }\n const putRequest = store.put({ ...record, status });\n putRequest.onsuccess = () => resolve();\n putRequest.onerror = () => reject(putRequest.error);\n };\n getRequest.onerror = () => reject(getRequest.error);\n });\n }\n\n public async getByRunId(job_run_id: string): Promise<JobStorageFormat<Input, Output>[]> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readonly\");\n const store = tx.objectStore(this.tableName);\n const index = store.index(\"queue_job_run_id\");\n const prefixKeyValues = this.getPrefixKeyValues();\n const keyRange = IDBKeyRange.only([...prefixKeyValues, this.queueName, job_run_id]);\n const request = index.getAll(keyRange);\n\n return new Promise((resolve, reject) => {\n request.onsuccess = () => {\n const results = (request.result || []).filter(\n (job: JobStorageFormat<Input, Output> & Record<string, unknown>) =>\n this.matchesPrefixes(job)\n );\n resolve(results);\n };\n request.onerror = () => reject(request.error);\n tx.onerror = () => reject(tx.error);\n });\n }\n\n /**\n * Terminal write that does NOT bump `attempts`. See IQueueStorage.finalize\n * for the rationale (avoids double-counting on ack/fail).\n */\n public async finalize(\n id: unknown,\n fields: {\n output?: Output | null;\n error?: string | null;\n error_code?: string | null;\n status?: JobStatus;\n completed_at?: string | null;\n abort_requested_at?: string | null;\n lease_owner?: string | null;\n progress?: number;\n progress_message?: string;\n progress_details?: Record<string, any> | null;\n visible_at?: string | null;\n }\n ): Promise<void> {\n const existing = await this.get(id);\n if (!existing) return;\n const updated = existing as JobStorageFormat<Input, Output> & Record<string, unknown>;\n if (\"output\" in fields) updated.output = fields.output ?? null;\n if (\"error\" in fields) updated.error = fields.error ?? null;\n if (\"error_code\" in fields) updated.error_code = fields.error_code ?? null;\n if (\"status\" in fields) updated.status = fields.status;\n if (\"completed_at\" in fields) updated.completed_at = fields.completed_at ?? null;\n if (\"abort_requested_at\" in fields) {\n updated.abort_requested_at = fields.abort_requested_at ?? null;\n }\n if (\"lease_owner\" in fields) updated.lease_owner = fields.lease_owner ?? null;\n if (\"progress\" in fields) updated.progress = fields.progress ?? 0;\n if (\"progress_message\" in fields) updated.progress_message = fields.progress_message ?? \"\";\n if (\"progress_details\" in fields) updated.progress_details = fields.progress_details ?? null;\n if (\"visible_at\" in fields) updated.visible_at = fields.visible_at ?? null;\n await this.put(updated);\n }\n\n /**\n * Atomically writes status=DISABLED, releases the lease, clears progress\n * fields, and stamps completed_at (preserving an existing value). IDB\n * is single-threaded per-origin within a JS context, so the get + finalize\n * pair is observably atomic — no other transaction can interleave between\n * them inside this microtask chain.\n */\n public async markDisabled(id: unknown): Promise<void> {\n const existing = await this.get(id);\n if (!existing) return;\n const completedAt = existing.completed_at ?? new Date().toISOString();\n await this.finalize(id, {\n status: JobStatus.DISABLED,\n completed_at: completedAt,\n lease_owner: null,\n progress: 0,\n progress_message: \"\",\n progress_details: null,\n });\n }\n\n public async deleteAll(): Promise<void> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readwrite\");\n const store = tx.objectStore(this.tableName);\n const index = store.index(\"queue_status\");\n const prefixKeyValues = this.getPrefixKeyValues();\n\n return new Promise((resolve, reject) => {\n const keyRange = IDBKeyRange.bound(\n [...prefixKeyValues, this.queueName, \"\"],\n [...prefixKeyValues, this.queueName, \"\\uffff\"]\n );\n const request = index.openCursor(keyRange);\n\n request.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;\n if (cursor) {\n const job = cursor.value as JobStorageFormat<Input, Output> & Record<string, unknown>;\n if (job.queue === this.queueName && this.matchesPrefixes(job)) {\n const deleteRequest = cursor.delete();\n deleteRequest.onsuccess = () => {\n cursor.continue();\n };\n deleteRequest.onerror = () => {\n cursor.continue();\n };\n } else {\n cursor.continue();\n }\n }\n };\n\n tx.oncomplete = () => {\n this.hybridManager?.notifyLocalChange();\n resolve();\n };\n tx.onerror = () => reject(tx.error);\n request.onerror = () => reject(request.error);\n });\n }\n\n public async outputForInput(input: Input): Promise<Output | null> {\n const fingerprint = await makeFingerprint(input);\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readonly\");\n const store = tx.objectStore(this.tableName);\n const index = store.index(\"queue_fingerprint_status\");\n const prefixKeyValues = this.getPrefixKeyValues();\n const request = index.get([\n ...prefixKeyValues,\n this.queueName,\n fingerprint,\n JobStatus.COMPLETED,\n ]);\n\n return new Promise((resolve, reject) => {\n request.onsuccess = () => {\n const job = request.result as\n | (JobStorageFormat<Input, Output> & Record<string, unknown>)\n | undefined;\n if (job && this.matchesPrefixes(job)) {\n resolve(job.output ?? null);\n } else {\n resolve(null);\n }\n };\n request.onerror = () => reject(request.error);\n tx.onerror = () => reject(tx.error);\n });\n }\n\n public async saveProgress(\n id: unknown,\n progress: number,\n message: string,\n details: Record<string, any> | null\n ): Promise<void> {\n const job = await this.get(id);\n if (!job) {\n // Contract: progress updates for a missing job silently no-op — the job may\n // have already completed/been removed, or a lease-expiry race removed it.\n getLogger().warn(\"Job not found for progress update\", { id });\n return;\n }\n\n job.progress = progress;\n job.progress_message = message;\n job.progress_details = details;\n\n await this.put(job);\n }\n\n /**\n * Persists a job to the store without modifying attempts or other completion logic.\n */\n private async put(job: JobStorageFormat<Input, Output>): Promise<void> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readwrite\");\n const store = tx.objectStore(this.tableName);\n\n job.queue = this.queueName;\n\n const jobWithPrefixes = job as JobStorageFormat<Input, Output> & Record<string, unknown>;\n for (const [key, value] of Object.entries(this.prefixValues)) {\n jobWithPrefixes[key] = value;\n }\n\n return new Promise((resolve, reject) => {\n const putReq = store.put(jobWithPrefixes);\n putReq.onerror = () => reject(putReq.error);\n tx.oncomplete = () => {\n this.hybridManager?.notifyLocalChange();\n resolve();\n };\n tx.onerror = () => reject(tx.error);\n });\n }\n\n public async delete(id: unknown): Promise<void> {\n const job = await this.get(id);\n if (!job) return;\n\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readwrite\");\n const store = tx.objectStore(this.tableName);\n const request = store.delete(id as string);\n\n return new Promise((resolve, reject) => {\n request.onsuccess = () => resolve();\n request.onerror = () => reject(request.error);\n tx.oncomplete = () => {\n this.hybridManager?.notifyLocalChange();\n };\n tx.onerror = () => reject(tx.error);\n });\n }\n\n public async deleteJobsByStatusAndAge(status: JobStatus, olderThanMs: number): Promise<void> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readwrite\");\n const store = tx.objectStore(this.tableName);\n const index = store.index(\"queue_status\");\n const cutoffDate = new Date(Date.now() - olderThanMs).toISOString();\n const prefixKeyValues = this.getPrefixKeyValues();\n const keyRange = IDBKeyRange.only([...prefixKeyValues, this.queueName, status]);\n\n return new Promise((resolve, reject) => {\n const request = index.openCursor(keyRange);\n\n request.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;\n if (cursor) {\n const job = cursor.value as JobStorageFormat<Input, Output> & Record<string, unknown>;\n if (\n job.queue === this.queueName &&\n this.matchesPrefixes(job) &&\n job.status === status &&\n job.completed_at &&\n job.completed_at <= cutoffDate\n ) {\n cursor.delete();\n }\n cursor.continue();\n }\n };\n\n tx.oncomplete = () => {\n this.hybridManager?.notifyLocalChange();\n resolve();\n };\n tx.onerror = () => reject(tx.error);\n request.onerror = () => reject(request.error);\n });\n }\n\n private async getAllJobs(): Promise<Array<JobStorageFormat<Input, Output>>> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readonly\");\n const store = tx.objectStore(this.tableName);\n const index = store.index(\"queue_status\");\n const prefixKeyValues = this.getPrefixKeyValues();\n\n return new Promise((resolve, reject) => {\n const jobs: Array<JobStorageFormat<Input, Output>> = [];\n const keyRange = IDBKeyRange.bound(\n [...prefixKeyValues, this.queueName, \"\"],\n [...prefixKeyValues, this.queueName, \"\\uffff\"]\n );\n const request = index.openCursor(keyRange);\n\n request.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;\n if (cursor) {\n const job = cursor.value as JobStorageFormat<Input, Output> & Record<string, unknown>;\n if (job.queue === this.queueName && this.matchesPrefixes(job)) {\n jobs.push(job);\n }\n cursor.continue();\n }\n };\n\n tx.oncomplete = () => resolve(jobs);\n tx.onerror = () => reject(tx.error);\n request.onerror = () => reject(request.error);\n });\n }\n\n private async getAllJobsWithFilter(\n prefixFilter: Readonly<Record<string, string | number>>\n ): Promise<Array<JobStorageFormat<Input, Output>>> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readonly\");\n const store = tx.objectStore(this.tableName);\n\n return new Promise((resolve, reject) => {\n const jobs: Array<JobStorageFormat<Input, Output>> = [];\n const request = store.openCursor();\n\n request.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;\n if (cursor) {\n const job = cursor.value as JobStorageFormat<Input, Output> & Record<string, unknown>;\n if (job.queue !== this.queueName) {\n cursor.continue();\n return;\n }\n if (Object.keys(prefixFilter).length === 0) {\n jobs.push(job);\n } else {\n let matches = true;\n for (const [key, value] of Object.entries(prefixFilter)) {\n if (job[key] !== value) {\n matches = false;\n break;\n }\n }\n if (matches) {\n jobs.push(job);\n }\n }\n cursor.continue();\n }\n };\n\n tx.oncomplete = () => resolve(jobs);\n tx.onerror = () => reject(tx.error);\n request.onerror = () => reject(request.error);\n });\n }\n\n private isCustomPrefixFilter(prefixFilter?: Readonly<Record<string, string | number>>): boolean {\n if (prefixFilter === undefined) {\n return false;\n }\n if (Object.keys(prefixFilter).length === 0) {\n return true;\n }\n const instanceKeys = Object.keys(this.prefixValues);\n const filterKeys = Object.keys(prefixFilter);\n if (instanceKeys.length !== filterKeys.length) {\n return true;\n }\n for (const key of instanceKeys) {\n if (this.prefixValues[key] !== prefixFilter[key]) {\n return true;\n }\n }\n return false;\n }\n\n private getHybridManager(): HybridSubscriptionManager<\n JobStorageFormat<Input, Output>,\n unknown,\n QueueChangePayload<Input, Output>\n > {\n if (!this.hybridManager) {\n const channelName = `indexeddb-queue-${this.tableName}-${this.queueName}`;\n\n this.hybridManager = new HybridSubscriptionManager<\n JobStorageFormat<Input, Output>,\n unknown,\n QueueChangePayload<Input, Output>\n >(\n channelName,\n async () => {\n const jobs = await this.getAllJobs();\n return new Map(jobs.map((j) => [j.id, j]));\n },\n (a, b) => deepEqual(a, b),\n {\n insert: (item) => ({ type: \"INSERT\" as const, new: item }),\n update: (oldItem, newItem) => ({ type: \"UPDATE\" as const, old: oldItem, new: newItem }),\n delete: (item) => ({ type: \"DELETE\" as const, old: item }),\n },\n {\n defaultIntervalMs: 1000,\n useBroadcastChannel: this.hybridOptions.useBroadcastChannel,\n backupPollingIntervalMs: this.hybridOptions.backupPollingIntervalMs,\n }\n );\n }\n return this.hybridManager;\n }\n\n private subscribeWithCustomPrefixFilter(\n callback: (change: QueueChangePayload<Input, Output>) => void,\n prefixFilter: Readonly<Record<string, string | number>>,\n intervalMs: number\n ): () => void {\n let lastKnownJobs = new Map<unknown, JobStorageFormat<Input, Output>>();\n let cancelled = false;\n\n const poll = async () => {\n if (cancelled) return;\n try {\n const currentJobs = await this.getAllJobsWithFilter(prefixFilter);\n if (cancelled) return;\n const currentMap = new Map(currentJobs.map((j) => [j.id, j]));\n\n for (const [id, job] of currentMap) {\n const old = lastKnownJobs.get(id);\n if (!old) {\n callback({ type: \"INSERT\", new: job });\n } else if (!deepEqual(old, job)) {\n callback({ type: \"UPDATE\", old, new: job });\n }\n }\n\n for (const [id, job] of lastKnownJobs) {\n if (!currentMap.has(id)) {\n callback({ type: \"DELETE\", old: job });\n }\n }\n\n lastKnownJobs = currentMap;\n } catch {\n // ignore polling errors\n }\n };\n\n const intervalId = setInterval(poll, intervalMs);\n poll();\n\n return () => {\n cancelled = true;\n clearInterval(intervalId);\n };\n }\n\n /**\n * Uses polling since IndexedDB has no native cross-tab change notifications.\n * Normal subscriptions share a single polling loop; custom prefix filter\n * subscriptions get a dedicated loop with DB-level filtering.\n */\n public subscribeToChanges(\n callback: (change: QueueChangePayload<Input, Output>) => void,\n options?: QueueSubscribeOptions\n ): () => void {\n const intervalMs = options?.pollingIntervalMs ?? 1000;\n\n if (this.isCustomPrefixFilter(options?.prefixFilter)) {\n return this.subscribeWithCustomPrefixFilter(callback, options!.prefixFilter!, intervalMs);\n }\n\n const manager = this.getHybridManager();\n return manager.subscribe(callback, { intervalMs });\n }\n\n destroy(): void {\n if (this.hybridManager) {\n this.hybridManager.destroy();\n this.hybridManager = null;\n }\n }\n}\n",
5
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type {\n IQueueStorage,\n JobStorageFormat,\n PrefixColumn,\n QueueChangePayload,\n QueueStorageOptions,\n QueueSubscribeOptions,\n} from \"@workglow/job-queue\";\nimport { JobStatus, validateLeaseMs } from \"@workglow/job-queue\";\nimport { HybridSubscriptionManager } from \"@workglow/storage\";\nimport { createServiceToken, deepEqual, getLogger, makeFingerprint, uuid4 } from \"@workglow/util\";\nimport { IndexedDbMigrationRunner } from \"../migrations/IndexedDbMigrationRunner\";\nimport { indexedDbQueueMigrations } from \"../migrations/indexedDbQueueMigrations\";\nimport { openIdb } from \"../storage/openIdb\";\n\nexport const INDEXED_DB_QUEUE_STORAGE = createServiceToken<IQueueStorage<any, any>>(\n \"jobqueue.storage.indexedDb\"\n);\n\nexport interface IndexedDbQueueStorageOptions extends QueueStorageOptions {\n /** Enable BroadcastChannel notifications (default: true) */\n readonly useBroadcastChannel?: boolean;\n /** Backup polling interval in ms (default: 5000, 0 to disable) */\n readonly backupPollingIntervalMs?: number;\n}\n\nexport class IndexedDbQueueStorage<Input, Output> implements IQueueStorage<Input, Output> {\n public readonly scope = \"process\" as const;\n private db: IDBDatabase | undefined;\n private readonly tableName: string;\n protected readonly prefixes: readonly PrefixColumn[];\n protected readonly prefixValues: Readonly<Record<string, string | number>>;\n private hybridManager: HybridSubscriptionManager<\n JobStorageFormat<Input, Output>,\n unknown,\n QueueChangePayload<Input, Output>\n > | null = null;\n private readonly hybridOptions: {\n readonly useBroadcastChannel: boolean;\n readonly backupPollingIntervalMs: number;\n };\n\n constructor(\n public readonly queueName: string,\n options: IndexedDbQueueStorageOptions = {}\n ) {\n this.prefixes = options.prefixes ?? [];\n this.prefixValues = options.prefixValues ?? {};\n this.hybridOptions = {\n useBroadcastChannel: options.useBroadcastChannel ?? true,\n backupPollingIntervalMs: options.backupPollingIntervalMs ?? 5000,\n };\n // Table name varies by prefix configuration to avoid conflicts\n if (this.prefixes.length > 0) {\n const prefixNames = this.prefixes.map((p) => p.name).join(\"_\");\n this.tableName = `jobs_${prefixNames}`;\n } else {\n this.tableName = \"jobs\";\n }\n }\n\n private matchesPrefixes(job: JobStorageFormat<Input, Output> & Record<string, unknown>): boolean {\n for (const [key, value] of Object.entries(this.prefixValues)) {\n if (job[key] !== value) {\n return false;\n }\n }\n return true;\n }\n\n /** Prefix values as an array in column order for index key construction. */\n private getPrefixKeyValues(): Array<string | number> {\n return this.prefixes.map((p) => this.prefixValues[p.name]);\n }\n\n private async getDb(): Promise<IDBDatabase> {\n if (this.db) return this.db;\n await this.migrate();\n return this.db!;\n }\n\n /**\n * Returns the versioned migrations that this storage's object store +\n * indexes depend on. Callers can compose them with other storages'\n * migrations under a shared {@link IndexedDbMigrationRunner}; otherwise\n * call {@link migrate}.\n */\n public getMigrations() {\n return indexedDbQueueMigrations(this.tableName, this.prefixes);\n }\n\n /**\n * Applies any pending migrations for this queue's IndexedDB database, then\n * opens a long-lived connection at the migrated version. Idempotent — a\n * second call closes any prior handle (so it doesn't pin the\n * pre-migration version or block another tab's upgrade), reruns the\n * runner (no-op if the bookkeeping store says everything is applied), and\n * reopens the connection.\n */\n public async migrate(): Promise<void> {\n if (this.db) {\n try {\n this.db.close();\n } catch {\n // ignore — close is best-effort\n }\n this.db = undefined;\n }\n const runner = new IndexedDbMigrationRunner(this.tableName);\n await runner.run(this.getMigrations());\n this.db = await openIdb(this.tableName);\n }\n\n public async add(job: JobStorageFormat<Input, Output>): Promise<unknown> {\n const db = await this.getDb();\n const now = new Date().toISOString();\n const jobWithPrefixes = job as JobStorageFormat<Input, Output> & Record<string, unknown>;\n jobWithPrefixes.id = jobWithPrefixes.id ?? uuid4();\n jobWithPrefixes.job_run_id = jobWithPrefixes.job_run_id ?? uuid4();\n jobWithPrefixes.queue = this.queueName;\n jobWithPrefixes.fingerprint =\n jobWithPrefixes.fingerprint ?? (await makeFingerprint(jobWithPrefixes.input));\n jobWithPrefixes.status = JobStatus.PENDING;\n jobWithPrefixes.progress = 0;\n jobWithPrefixes.progress_message = \"\";\n jobWithPrefixes.progress_details = null;\n jobWithPrefixes.created_at = now;\n // A caller-set future visible_at is a delayed send (delaySeconds) — keep it.\n jobWithPrefixes.visible_at = jobWithPrefixes.visible_at ?? now;\n\n for (const [key, value] of Object.entries(this.prefixValues)) {\n jobWithPrefixes[key] = value;\n }\n\n const tx = db.transaction(this.tableName, \"readwrite\");\n const store = tx.objectStore(this.tableName);\n\n return new Promise((resolve, reject) => {\n const request = store.add(jobWithPrefixes);\n\n tx.oncomplete = () => {\n this.hybridManager?.notifyLocalChange();\n resolve(jobWithPrefixes.id);\n };\n tx.onerror = () => reject(tx.error);\n request.onerror = () => reject(request.error);\n });\n }\n\n async get(id: unknown): Promise<JobStorageFormat<Input, Output> | undefined> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readonly\");\n const store = tx.objectStore(this.tableName);\n const request = store.get(id as string);\n return new Promise((resolve, reject) => {\n request.onsuccess = () => {\n const job = request.result as\n | (JobStorageFormat<Input, Output> & Record<string, unknown>)\n | undefined;\n if (job && job.queue === this.queueName && this.matchesPrefixes(job)) {\n resolve(job);\n } else {\n resolve(undefined);\n }\n };\n request.onerror = () => reject(request.error);\n tx.onerror = () => reject(tx.error);\n });\n }\n\n public async peek(\n status: JobStatus = JobStatus.PENDING,\n num: number = 100\n ): Promise<JobStorageFormat<Input, Output>[]> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readonly\");\n const store = tx.objectStore(this.tableName);\n const index = store.index(\"queue_status_visible_at\");\n const prefixKeyValues = this.getPrefixKeyValues();\n\n return new Promise((resolve, reject) => {\n const ret = new Map<unknown, JobStorageFormat<Input, Output>>();\n const keyRange = IDBKeyRange.bound(\n [...prefixKeyValues, this.queueName, status, \"\"],\n [...prefixKeyValues, this.queueName, status, \"\\uffff\"]\n );\n const cursorRequest = index.openCursor(keyRange);\n\n const handleCursor = (e: Event) => {\n const cursor = (e.target as IDBRequest<IDBCursorWithValue>).result;\n if (!cursor || ret.size >= num) {\n resolve(Array.from(ret.values()));\n return;\n }\n const job = cursor.value as JobStorageFormat<Input, Output> & Record<string, unknown>;\n // Map keyed by id ensures no duplicates\n if (this.matchesPrefixes(job)) {\n ret.set(cursor.value.id, cursor.value);\n }\n cursor.continue();\n };\n\n cursorRequest.onsuccess = handleCursor;\n cursorRequest.onerror = () => reject(cursorRequest.error);\n tx.onerror = () => reject(tx.error);\n });\n }\n\n /**\n * Retrieves the next job from the queue using optimistic locking.\n * Claims PENDING jobs ready to run, and also reclaims PROCESSING jobs whose\n * lease has expired (crash recovery). Sets lease_expires_at on the claimed row.\n *\n * IndexedDB uses snapshot isolation, so concurrent transactions can both see the same\n * PENDING job. To prevent processing the same job multiple times, this method:\n * 1. Claims a job by setting it to PROCESSING with a unique claim token\n * 2. After the transaction completes, re-reads the job to verify the claim succeeded\n * 3. If another worker claimed it first (different claim token), returns undefined\n *\n * @param workerId - Worker ID to associate with the job (required)\n * @param opts - Optional options including leaseMs (default 30000)\n * @returns A promise that resolves to the next job or undefined if the queue is empty.\n */\n public async next(\n workerId: string,\n opts?: { leaseMs?: number }\n ): Promise<JobStorageFormat<Input, Output> | undefined> {\n const leaseMs = opts?.leaseMs ?? 30000;\n validateLeaseMs(leaseMs, \"leaseMs\");\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readwrite\");\n const store = tx.objectStore(this.tableName);\n const now = new Date().toISOString();\n const leaseExpiry = new Date(Date.now() + leaseMs).toISOString();\n const prefixKeyValues = this.getPrefixKeyValues();\n\n // Used after the tx to verify we actually won the race to claim this job.\n const claimToken = workerId;\n\n const jobToReturn = await new Promise<JobStorageFormat<Input, Output> | undefined>(\n (resolve, reject) => {\n let claimedJob: JobStorageFormat<Input, Output> | undefined;\n\n const tryClaimJob = (job: JobStorageFormat<Input, Output> & Record<string, unknown>) => {\n // Lease-expiry reclaim consumes one attempt against max_attempts;\n // a fresh PENDING claim does not (the worker's validateJobState\n // FAILs the job when attempts >= max_attempts on the next step).\n const isLeaseExpiryReclaim = job.status === JobStatus.PROCESSING;\n job.status = JobStatus.PROCESSING;\n job.last_attempted_at = now;\n job.lease_owner = claimToken;\n job.lease_expires_at = leaseExpiry;\n if (isLeaseExpiryReclaim) {\n job.attempts = ((job.attempts as number | undefined) ?? 0) + 1;\n }\n // Always clear stale abort_requested_at on (re)claim. A PROCESSING\n // row may have had abort_requested_at set before the previous\n // worker crashed; the new owner must start with a clean slate.\n job.abort_requested_at = null;\n\n try {\n const updateRequest = store.put(job);\n updateRequest.onsuccess = () => {\n claimedJob = job;\n };\n updateRequest.onerror = () => {};\n } catch {\n // ignore\n }\n };\n\n const pendingIndex = store.index(\"queue_status_visible_at\");\n const pendingRequest = pendingIndex.openCursor(\n IDBKeyRange.bound(\n [...prefixKeyValues, this.queueName, JobStatus.PENDING, \"\"],\n [...prefixKeyValues, this.queueName, JobStatus.PENDING, now],\n false,\n false\n )\n );\n\n pendingRequest.onsuccess = (e) => {\n const cursor = (e.target as IDBRequest<IDBCursorWithValue>).result;\n if (!cursor) {\n // No PENDING job found — try expired-lease PROCESSING job\n if (!claimedJob) {\n tryExpiredLeaseScan();\n }\n return;\n }\n const job = cursor.value as JobStorageFormat<Input, Output> & Record<string, unknown>;\n if (\n job.queue !== this.queueName ||\n job.status !== JobStatus.PENDING ||\n !this.matchesPrefixes(job)\n ) {\n cursor.continue();\n return;\n }\n tryClaimJob(job);\n // Don't continue cursor — claim attempted\n };\n\n pendingRequest.onerror = () => reject(pendingRequest.error);\n\n const tryExpiredLeaseScan = () => {\n const processingIndex = store.index(\"queue_status_visible_at\");\n const processingRequest = processingIndex.openCursor(\n IDBKeyRange.bound(\n [...prefixKeyValues, this.queueName, JobStatus.PROCESSING, \"\"],\n [...prefixKeyValues, this.queueName, JobStatus.PROCESSING, \"￿\"],\n false,\n false\n )\n );\n\n processingRequest.onsuccess = (e) => {\n const cursor = (e.target as IDBRequest<IDBCursorWithValue>).result;\n if (!cursor) return; // none found, tx.oncomplete will resolve with undefined\n const job = cursor.value as JobStorageFormat<Input, Output> & Record<string, unknown>;\n if (\n job.queue !== this.queueName ||\n job.status !== JobStatus.PROCESSING ||\n !this.matchesPrefixes(job)\n ) {\n cursor.continue();\n return;\n }\n // null lease_expires_at = expired per spec\n if (job.lease_expires_at && job.lease_expires_at >= now) {\n cursor.continue();\n return;\n }\n tryClaimJob(job);\n };\n\n processingRequest.onerror = () => reject(processingRequest.error);\n };\n\n tx.oncomplete = () => {\n if (claimedJob) {\n this.hybridManager?.notifyLocalChange();\n }\n resolve(claimedJob);\n };\n tx.onerror = () => reject(tx.error);\n }\n );\n\n if (!jobToReturn) {\n return undefined;\n }\n\n // Verify we actually won the race by re-reading the job\n const verifiedJob = await this.get(jobToReturn.id);\n\n if (!verifiedJob) {\n return undefined;\n }\n\n if (verifiedJob.lease_owner !== claimToken) {\n return undefined;\n }\n\n if (verifiedJob.status !== JobStatus.PROCESSING) {\n return undefined;\n }\n\n return verifiedJob;\n }\n\n /**\n * Extend the lease on a currently PROCESSING job.\n * @param id - The ID of the job to extend the lease for\n * @param workerId - Worker ID that must match the current lease owner (lease_owner)\n * @param ms - Number of milliseconds to extend the lease by\n */\n public async extendLease(id: unknown, workerId: string, ms: number): Promise<void> {\n validateLeaseMs(ms, \"ms\");\n const job = await this.get(id);\n if (!job || job.status !== JobStatus.PROCESSING || job.lease_owner !== workerId) {\n throw new Error(\n `extendLease failed: job ${String(id)} is not PROCESSING or lease is not owned by worker ${workerId}`\n );\n }\n job.lease_expires_at = new Date(Date.now() + ms).toISOString();\n await this.put(job);\n }\n\n public async size(status = JobStatus.PENDING): Promise<number> {\n const db = await this.getDb();\n const prefixKeyValues = this.getPrefixKeyValues();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(this.tableName, \"readonly\");\n const store = tx.objectStore(this.tableName);\n const index = store.index(\"queue_status\");\n const keyRange = IDBKeyRange.only([...prefixKeyValues, this.queueName, status]);\n const request = index.count(keyRange);\n\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n tx.onerror = () => reject(tx.error);\n });\n }\n\n public async complete(job: JobStorageFormat<Input, Output>): Promise<void> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readwrite\");\n const store = tx.objectStore(this.tableName);\n\n return new Promise((resolve, reject) => {\n const getReq = store.get(job.id as string);\n getReq.onsuccess = () => {\n const existing = getReq.result as\n | (JobStorageFormat<Input, Output> & Record<string, unknown>)\n | undefined;\n if (!existing || existing.queue !== this.queueName || !this.matchesPrefixes(existing)) {\n // Contract: complete() must silently no-op (not throw) when the row is\n // missing or belongs to another queue. Lease-expiry races can legitimately\n // make the target row disappear between claim and complete, so the\n // transaction is allowed to commit with no write and resolve.\n return;\n }\n const currentAttempts = existing.attempts ?? 0;\n job.attempts = currentAttempts + 1;\n // PENDING-retry / terminal completion: always clear\n // abort_requested_at so a flag set DURING the attempt does not\n // survive into the next retry and immediately cancel it.\n const jobAsRecord = job as JobStorageFormat<Input, Output> & Record<string, unknown>;\n jobAsRecord.abort_requested_at = null;\n job.queue = this.queueName;\n\n const jobWithPrefixes = jobAsRecord;\n for (const [key, value] of Object.entries(this.prefixValues)) {\n jobWithPrefixes[key] = value;\n }\n\n const putReq = store.put(jobWithPrefixes);\n putReq.onsuccess = () => {};\n putReq.onerror = () => reject(putReq.error);\n };\n getReq.onerror = () => reject(getReq.error);\n\n tx.oncomplete = () => {\n this.hybridManager?.notifyLocalChange();\n resolve();\n };\n tx.onerror = () => reject(tx.error);\n });\n }\n\n public async releaseClaim(id: unknown): Promise<void> {\n const job = await this.get(id);\n if (!job) return;\n\n job.status = JobStatus.PENDING;\n job.lease_owner = null;\n job.progress = 0;\n job.progress_message = \"\";\n job.progress_details = null;\n // Clear stale abort_requested_at — an abort flag set during the previous\n // claim must not immediately cancel the next worker that picks up the row.\n (job as unknown as Record<string, unknown>).abort_requested_at = null;\n\n await this.put(job);\n }\n\n /**\n * Aborts a job.\n * - If PENDING: immediately mark as FAILED with abort_requested_at set.\n * - If PROCESSING: set abort_requested_at only (leave status as PROCESSING).\n * - Otherwise: no-op.\n */\n public async abort(id: unknown): Promise<void> {\n const job = await this.get(id);\n if (!job) return;\n const now = new Date().toISOString();\n if (job.status === JobStatus.PENDING) {\n job.status = JobStatus.FAILED;\n job.abort_requested_at = now;\n job.completed_at = now;\n // Use put() (not complete()) so attempts is NOT bumped — the worker\n // never actually attempted this job. Matches the cross-backend\n // contract verified in InMemory/Postgres.\n await this.put(job);\n } else if (job.status === JobStatus.PROCESSING) {\n job.abort_requested_at = now;\n await this.put(job);\n }\n }\n\n /** Force-overwrite status without touching attempts (used to persist DISABLED after lease release). */\n public async saveStatus(id: unknown, status: string): Promise<void> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readwrite\");\n const store = tx.objectStore(this.tableName);\n const getRequest = store.get(id as IDBValidKey);\n return new Promise((resolve, reject) => {\n getRequest.onsuccess = () => {\n const record = getRequest.result;\n if (!record) {\n resolve();\n return;\n }\n const putRequest = store.put({ ...record, status });\n putRequest.onsuccess = () => resolve();\n putRequest.onerror = () => reject(putRequest.error);\n };\n getRequest.onerror = () => reject(getRequest.error);\n });\n }\n\n public async getByRunId(job_run_id: string): Promise<JobStorageFormat<Input, Output>[]> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readonly\");\n const store = tx.objectStore(this.tableName);\n const index = store.index(\"queue_job_run_id\");\n const prefixKeyValues = this.getPrefixKeyValues();\n const keyRange = IDBKeyRange.only([...prefixKeyValues, this.queueName, job_run_id]);\n const request = index.getAll(keyRange);\n\n return new Promise((resolve, reject) => {\n request.onsuccess = () => {\n const results = (request.result || []).filter(\n (job: JobStorageFormat<Input, Output> & Record<string, unknown>) =>\n this.matchesPrefixes(job)\n );\n resolve(results);\n };\n request.onerror = () => reject(request.error);\n tx.onerror = () => reject(tx.error);\n });\n }\n\n /**\n * Terminal write that does NOT bump `attempts`. See IQueueStorage.finalize\n * for the rationale (avoids double-counting on ack/fail).\n */\n public async finalize(\n id: unknown,\n fields: {\n output?: Output | null;\n error?: string | null;\n error_code?: string | null;\n status?: JobStatus;\n completed_at?: string | null;\n abort_requested_at?: string | null;\n lease_owner?: string | null;\n progress?: number;\n progress_message?: string;\n progress_details?: Record<string, any> | null;\n visible_at?: string | null;\n }\n ): Promise<void> {\n const existing = await this.get(id);\n if (!existing) return;\n const updated = existing as JobStorageFormat<Input, Output> & Record<string, unknown>;\n if (\"output\" in fields) updated.output = fields.output ?? null;\n if (\"error\" in fields) updated.error = fields.error ?? null;\n if (\"error_code\" in fields) updated.error_code = fields.error_code ?? null;\n if (\"status\" in fields) updated.status = fields.status;\n if (\"completed_at\" in fields) updated.completed_at = fields.completed_at ?? null;\n if (\"abort_requested_at\" in fields) {\n updated.abort_requested_at = fields.abort_requested_at ?? null;\n }\n if (\"lease_owner\" in fields) updated.lease_owner = fields.lease_owner ?? null;\n if (\"progress\" in fields) updated.progress = fields.progress ?? 0;\n if (\"progress_message\" in fields) updated.progress_message = fields.progress_message ?? \"\";\n if (\"progress_details\" in fields) updated.progress_details = fields.progress_details ?? null;\n if (\"visible_at\" in fields) updated.visible_at = fields.visible_at ?? null;\n await this.put(updated);\n }\n\n /**\n * Atomically writes status=DISABLED, releases the lease, clears progress\n * fields, and stamps completed_at (preserving an existing value). IDB\n * is single-threaded per-origin within a JS context, so the get + finalize\n * pair is observably atomic — no other transaction can interleave between\n * them inside this microtask chain.\n */\n public async markDisabled(id: unknown): Promise<void> {\n const existing = await this.get(id);\n if (!existing) return;\n const completedAt = existing.completed_at ?? new Date().toISOString();\n await this.finalize(id, {\n status: JobStatus.DISABLED,\n completed_at: completedAt,\n lease_owner: null,\n progress: 0,\n progress_message: \"\",\n progress_details: null,\n });\n }\n\n public async deleteAll(): Promise<void> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readwrite\");\n const store = tx.objectStore(this.tableName);\n const index = store.index(\"queue_status\");\n const prefixKeyValues = this.getPrefixKeyValues();\n\n return new Promise((resolve, reject) => {\n const keyRange = IDBKeyRange.bound(\n [...prefixKeyValues, this.queueName, \"\"],\n [...prefixKeyValues, this.queueName, \"\\uffff\"]\n );\n const request = index.openCursor(keyRange);\n\n request.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;\n if (cursor) {\n const job = cursor.value as JobStorageFormat<Input, Output> & Record<string, unknown>;\n if (job.queue === this.queueName && this.matchesPrefixes(job)) {\n const deleteRequest = cursor.delete();\n deleteRequest.onsuccess = () => {\n cursor.continue();\n };\n deleteRequest.onerror = () => {\n cursor.continue();\n };\n } else {\n cursor.continue();\n }\n }\n };\n\n tx.oncomplete = () => {\n this.hybridManager?.notifyLocalChange();\n resolve();\n };\n tx.onerror = () => reject(tx.error);\n request.onerror = () => reject(request.error);\n });\n }\n\n public async outputForInput(input: Input): Promise<Output | null> {\n const fingerprint = await makeFingerprint(input);\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readonly\");\n const store = tx.objectStore(this.tableName);\n const index = store.index(\"queue_fingerprint_status\");\n const prefixKeyValues = this.getPrefixKeyValues();\n const request = index.get([\n ...prefixKeyValues,\n this.queueName,\n fingerprint,\n JobStatus.COMPLETED,\n ]);\n\n return new Promise((resolve, reject) => {\n request.onsuccess = () => {\n const job = request.result as\n | (JobStorageFormat<Input, Output> & Record<string, unknown>)\n | undefined;\n if (job && this.matchesPrefixes(job)) {\n resolve(job.output ?? null);\n } else {\n resolve(null);\n }\n };\n request.onerror = () => reject(request.error);\n tx.onerror = () => reject(tx.error);\n });\n }\n\n public async saveProgress(\n id: unknown,\n progress: number,\n message: string,\n details: Record<string, any> | null\n ): Promise<void> {\n const job = await this.get(id);\n if (!job) {\n // Contract: progress updates for a missing job silently no-op — the job may\n // have already completed/been removed, or a lease-expiry race removed it.\n getLogger().warn(\"Job not found for progress update\", { id });\n return;\n }\n\n job.progress = progress;\n job.progress_message = message;\n job.progress_details = details;\n\n await this.put(job);\n }\n\n /**\n * Persists a job to the store without modifying attempts or other completion logic.\n */\n private async put(job: JobStorageFormat<Input, Output>): Promise<void> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readwrite\");\n const store = tx.objectStore(this.tableName);\n\n job.queue = this.queueName;\n\n const jobWithPrefixes = job as JobStorageFormat<Input, Output> & Record<string, unknown>;\n for (const [key, value] of Object.entries(this.prefixValues)) {\n jobWithPrefixes[key] = value;\n }\n\n return new Promise((resolve, reject) => {\n const putReq = store.put(jobWithPrefixes);\n putReq.onerror = () => reject(putReq.error);\n tx.oncomplete = () => {\n this.hybridManager?.notifyLocalChange();\n resolve();\n };\n tx.onerror = () => reject(tx.error);\n });\n }\n\n public async delete(id: unknown): Promise<void> {\n const job = await this.get(id);\n if (!job) return;\n\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readwrite\");\n const store = tx.objectStore(this.tableName);\n const request = store.delete(id as string);\n\n return new Promise((resolve, reject) => {\n request.onsuccess = () => resolve();\n request.onerror = () => reject(request.error);\n tx.oncomplete = () => {\n this.hybridManager?.notifyLocalChange();\n };\n tx.onerror = () => reject(tx.error);\n });\n }\n\n public async deleteJobsByStatusAndAge(status: JobStatus, olderThanMs: number): Promise<void> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readwrite\");\n const store = tx.objectStore(this.tableName);\n const index = store.index(\"queue_status\");\n const cutoffDate = new Date(Date.now() - olderThanMs).toISOString();\n const prefixKeyValues = this.getPrefixKeyValues();\n const keyRange = IDBKeyRange.only([...prefixKeyValues, this.queueName, status]);\n\n return new Promise((resolve, reject) => {\n const request = index.openCursor(keyRange);\n\n request.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;\n if (cursor) {\n const job = cursor.value as JobStorageFormat<Input, Output> & Record<string, unknown>;\n if (\n job.queue === this.queueName &&\n this.matchesPrefixes(job) &&\n job.status === status &&\n job.completed_at &&\n job.completed_at <= cutoffDate\n ) {\n cursor.delete();\n }\n cursor.continue();\n }\n };\n\n tx.oncomplete = () => {\n this.hybridManager?.notifyLocalChange();\n resolve();\n };\n tx.onerror = () => reject(tx.error);\n request.onerror = () => reject(request.error);\n });\n }\n\n private async getAllJobs(): Promise<Array<JobStorageFormat<Input, Output>>> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readonly\");\n const store = tx.objectStore(this.tableName);\n const index = store.index(\"queue_status\");\n const prefixKeyValues = this.getPrefixKeyValues();\n\n return new Promise((resolve, reject) => {\n const jobs: Array<JobStorageFormat<Input, Output>> = [];\n const keyRange = IDBKeyRange.bound(\n [...prefixKeyValues, this.queueName, \"\"],\n [...prefixKeyValues, this.queueName, \"\\uffff\"]\n );\n const request = index.openCursor(keyRange);\n\n request.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;\n if (cursor) {\n const job = cursor.value as JobStorageFormat<Input, Output> & Record<string, unknown>;\n if (job.queue === this.queueName && this.matchesPrefixes(job)) {\n jobs.push(job);\n }\n cursor.continue();\n }\n };\n\n tx.oncomplete = () => resolve(jobs);\n tx.onerror = () => reject(tx.error);\n request.onerror = () => reject(request.error);\n });\n }\n\n private async getAllJobsWithFilter(\n prefixFilter: Readonly<Record<string, string | number>>\n ): Promise<Array<JobStorageFormat<Input, Output>>> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readonly\");\n const store = tx.objectStore(this.tableName);\n\n return new Promise((resolve, reject) => {\n const jobs: Array<JobStorageFormat<Input, Output>> = [];\n const request = store.openCursor();\n\n request.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;\n if (cursor) {\n const job = cursor.value as JobStorageFormat<Input, Output> & Record<string, unknown>;\n if (job.queue !== this.queueName) {\n cursor.continue();\n return;\n }\n if (Object.keys(prefixFilter).length === 0) {\n jobs.push(job);\n } else {\n let matches = true;\n for (const [key, value] of Object.entries(prefixFilter)) {\n if (job[key] !== value) {\n matches = false;\n break;\n }\n }\n if (matches) {\n jobs.push(job);\n }\n }\n cursor.continue();\n }\n };\n\n tx.oncomplete = () => resolve(jobs);\n tx.onerror = () => reject(tx.error);\n request.onerror = () => reject(request.error);\n });\n }\n\n private isCustomPrefixFilter(prefixFilter?: Readonly<Record<string, string | number>>): boolean {\n if (prefixFilter === undefined) {\n return false;\n }\n if (Object.keys(prefixFilter).length === 0) {\n return true;\n }\n const instanceKeys = Object.keys(this.prefixValues);\n const filterKeys = Object.keys(prefixFilter);\n if (instanceKeys.length !== filterKeys.length) {\n return true;\n }\n for (const key of instanceKeys) {\n if (this.prefixValues[key] !== prefixFilter[key]) {\n return true;\n }\n }\n return false;\n }\n\n private getHybridManager(): HybridSubscriptionManager<\n JobStorageFormat<Input, Output>,\n unknown,\n QueueChangePayload<Input, Output>\n > {\n if (!this.hybridManager) {\n const channelName = `indexeddb-queue-${this.tableName}-${this.queueName}`;\n\n this.hybridManager = new HybridSubscriptionManager<\n JobStorageFormat<Input, Output>,\n unknown,\n QueueChangePayload<Input, Output>\n >(\n channelName,\n async () => {\n const jobs = await this.getAllJobs();\n return new Map(jobs.map((j) => [j.id, j]));\n },\n (a, b) => deepEqual(a, b),\n {\n insert: (item) => ({ type: \"INSERT\" as const, new: item }),\n update: (oldItem, newItem) => ({ type: \"UPDATE\" as const, old: oldItem, new: newItem }),\n delete: (item) => ({ type: \"DELETE\" as const, old: item }),\n },\n {\n defaultIntervalMs: 1000,\n useBroadcastChannel: this.hybridOptions.useBroadcastChannel,\n backupPollingIntervalMs: this.hybridOptions.backupPollingIntervalMs,\n }\n );\n }\n return this.hybridManager;\n }\n\n private subscribeWithCustomPrefixFilter(\n callback: (change: QueueChangePayload<Input, Output>) => void,\n prefixFilter: Readonly<Record<string, string | number>>,\n intervalMs: number\n ): () => void {\n let lastKnownJobs = new Map<unknown, JobStorageFormat<Input, Output>>();\n let cancelled = false;\n\n const poll = async () => {\n if (cancelled) return;\n try {\n const currentJobs = await this.getAllJobsWithFilter(prefixFilter);\n if (cancelled) return;\n const currentMap = new Map(currentJobs.map((j) => [j.id, j]));\n\n for (const [id, job] of currentMap) {\n const old = lastKnownJobs.get(id);\n if (!old) {\n callback({ type: \"INSERT\", new: job });\n } else if (!deepEqual(old, job)) {\n callback({ type: \"UPDATE\", old, new: job });\n }\n }\n\n for (const [id, job] of lastKnownJobs) {\n if (!currentMap.has(id)) {\n callback({ type: \"DELETE\", old: job });\n }\n }\n\n lastKnownJobs = currentMap;\n } catch {\n // ignore polling errors\n }\n };\n\n const intervalId = setInterval(poll, intervalMs);\n void poll();\n\n return () => {\n cancelled = true;\n clearInterval(intervalId);\n };\n }\n\n /**\n * Uses polling since IndexedDB has no native cross-tab change notifications.\n * Normal subscriptions share a single polling loop; custom prefix filter\n * subscriptions get a dedicated loop with DB-level filtering.\n */\n public subscribeToChanges(\n callback: (change: QueueChangePayload<Input, Output>) => void,\n options?: QueueSubscribeOptions\n ): () => void {\n const intervalMs = options?.pollingIntervalMs ?? 1000;\n\n if (this.isCustomPrefixFilter(options?.prefixFilter)) {\n return this.subscribeWithCustomPrefixFilter(callback, options!.prefixFilter!, intervalMs);\n }\n\n const manager = this.getHybridManager();\n return manager.subscribe(callback, { intervalMs });\n }\n\n destroy(): void {\n if (this.hybridManager) {\n this.hybridManager.destroy();\n this.hybridManager = null;\n }\n }\n}\n",
6
6
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport {\n type IMigration,\n type IMigrationRunner,\n type RunMigrationsOptions,\n MIGRATIONS_TABLE,\n sortMigrations,\n} from \"@workglow/storage\";\n\n/**\n * Context handed to an IndexedDB migration's `up()` body. IDB's spec only\n * permits schema changes (createObjectStore, createIndex, etc.) inside an\n * `onupgradeneeded` callback, so migrations receive both the {@link IDBDatabase}\n * and the version-change {@link IDBTransaction} active during that callback.\n *\n * `up()` MUST be synchronous — IDB upgrade transactions auto-commit as soon as\n * the callback returns control to the event loop, so any awaited Promise\n * between IDB requests would silently lose the rest of the migration.\n */\nexport interface IndexedDbUpgradeContext {\n readonly db: IDBDatabase;\n readonly tx: IDBTransaction;\n readonly oldVersion: number;\n readonly newVersion: number;\n}\n\n/** Convenience alias for IndexedDB-flavoured migrations. */\nexport type IndexedDbMigration = IMigration<IndexedDbUpgradeContext>;\n\nfunction getIndexedDb(): IDBFactory {\n // Node test environments (vitest + fake-indexeddb) install a global; browsers\n // expose it natively. We don't pick a fallback because callers in unsupported\n // runtimes (Bun without polyfill, Node without setup) need a clear failure.\n const idb = (globalThis as { indexedDB?: IDBFactory }).indexedDB;\n if (!idb) {\n throw new Error(\n \"indexedDB is not available in this environment. Provide one via the IndexedDbMigrationRunner constructor or polyfill globalThis.indexedDB.\"\n );\n }\n return idb;\n}\n\n/**\n * Thrown when an in-flight migration is interrupted by another tab opening\n * the same database at a higher version. Surfaces as the `run()` rejection\n * so callers can distinguish a tab-coordination failure from a programming\n * error in `up()`.\n */\nexport class MigrationAbortedByOtherTabError extends Error {\n constructor(dbName: string) {\n super(`IndexedDB ${dbName} migration aborted: another tab requested a higher version`);\n this.name = \"MigrationAbortedByOtherTabError\";\n }\n}\n\n/**\n * Per-DB serialization for `run()`. IDB's open-with-higher-version semantics\n * mean two concurrent migrations of the same database can deadlock each other\n * (each holds an open connection that blocks the other's upgrade). We\n * serialize at the JS layer per-dbName so back-to-back `runIndexedDbMigrationGroups`\n * calls (which construct fresh runner instances) cooperate.\n *\n * Module-scoped so it spans runner instances; safe because the only state is\n * a Promise chain keyed by `dbName`.\n */\nconst RUN_LOCKS = new Map<string, Promise<unknown>>();\n\n/**\n * Runs versioned migrations against a single IndexedDB database.\n *\n * Bookkeeping-driven dispatch (NOT IDB-version-ordinal driven). The runner:\n * 1. Probes the DB to read the `_storage_migrations` object store, building\n * a set of already-applied `(component, version)` pairs. The probe\n * registers an `onupgradeneeded` handler so that if the DB doesn't yet\n * exist, IDB's auto-creation path also creates the bookkeeping store\n * atomically (rather than leaving an empty v1 DB with no schema).\n * 2. Computes the pending migrations as `sorted \\ alreadyApplied` —\n * identifying each migration by `(component, version)`, NOT by its\n * position in the sorted array. This means inserting a new migration\n * that sorts BEFORE existing ones (e.g. a new component name that's\n * lexicographically earlier) is still correctly detected as pending.\n * 3. If pending is empty, returns without bumping the IDB version.\n * 4. Otherwise reopens at `currentVersion + 1` and runs every pending\n * migration inside the upgrade transaction, recording each in the\n * bookkeeping store as it goes.\n *\n * Concurrent `run()` calls against the same dbName (across instances) are\n * serialized via {@link RUN_LOCKS} so that two callers don't deadlock each\n * other on the open-at-higher-version handshake.\n *\n * The IDB database version is treated purely as a monotonic trigger for\n * `onupgradeneeded`; we never use it to decide which migrations to apply.\n *\n * Caveats:\n * - `up()` must be synchronous. IDB upgrade transactions auto-commit as\n * soon as control returns to the event loop, so awaited Promises between\n * IDB requests would silently lose the rest of the migration. The runner\n * throws if `up()` returns a Promise.\n */\nexport class IndexedDbMigrationRunner implements IMigrationRunner<IndexedDbUpgradeContext> {\n constructor(\n private readonly dbName: string,\n private readonly idb: IDBFactory = getIndexedDb()\n ) {}\n\n /**\n * Probe the DB without forcing a schema change. Reads the bookkeeping\n * store (creating it on first run via the auto-fired upgrade callback)\n * and returns the current IDB version + the set of applied migrations\n * keyed by `${component}@${version}`.\n */\n private async probe(): Promise<{ currentVersion: number; applied: Set<string> }> {\n return new Promise((resolve, reject) => {\n // Multiple IDB callbacks (onupgradeneeded, onsuccess + nested getAll\n // success/error, onerror, onblocked) can fire in surprising orders —\n // e.g. an orphaned `onerror` after we've already returned a result.\n // Gate every settlement through `finalize` so the Promise resolves or\n // rejects exactly once, and so the open connection is closed exactly\n // once on the way out.\n let settled = false;\n const finalize = (\n db: IDBDatabase | undefined,\n outcome:\n | { ok: true; value: { currentVersion: number; applied: Set<string> } }\n | { ok: false; error: unknown }\n ): void => {\n if (settled) return;\n settled = true;\n if (db) {\n try {\n db.close();\n } catch {\n // ignore — close is best-effort\n }\n }\n if (outcome.ok) resolve(outcome.value);\n else reject(outcome.error);\n };\n\n const req = this.idb.open(this.dbName);\n req.onupgradeneeded = () => {\n if (settled) return;\n // Fires only when the DB doesn't already exist (oldVersion = 0).\n // We seed the bookkeeping store atomically with DB creation so a\n // fresh DB never lacks it.\n const u = req.result;\n if (!u.objectStoreNames.contains(MIGRATIONS_TABLE)) {\n u.createObjectStore(MIGRATIONS_TABLE, { keyPath: [\"component\", \"version\"] });\n }\n };\n req.onsuccess = () => {\n if (settled) return;\n const db = req.result;\n const currentVersion = db.version;\n if (!db.objectStoreNames.contains(MIGRATIONS_TABLE)) {\n // Existed prior to this runner ever being used — no bookkeeping yet.\n finalize(db, { ok: true, value: { currentVersion, applied: new Set() } });\n return;\n }\n const tx = db.transaction(MIGRATIONS_TABLE, \"readonly\");\n const store = tx.objectStore(MIGRATIONS_TABLE);\n const getAll = store.getAll();\n getAll.onsuccess = () => {\n if (settled) return;\n const rows = getAll.result as Array<{ component: string; version: number }>;\n finalize(db, {\n ok: true,\n value: {\n currentVersion,\n applied: new Set(rows.map((r) => `${r.component}@${r.version}`)),\n },\n });\n };\n getAll.onerror = () => {\n if (settled) return;\n finalize(db, { ok: false, error: getAll.error });\n };\n };\n req.onerror = () => {\n if (settled) return;\n finalize(undefined, { ok: false, error: req.error });\n };\n req.onblocked = () => {\n if (settled) return;\n finalize(undefined, {\n ok: false,\n error: new Error(`IndexedDB ${this.dbName} blocked while probing for bookkeeping store`),\n });\n };\n });\n }\n\n /** Ensures the bookkeeping object store exists (created lazily by {@link probe}). */\n async ensureBookkeepingTable(): Promise<void> {\n await this.probe();\n }\n\n async appliedVersions(component: string): Promise<Set<number>> {\n const { applied } = await this.probe();\n const versions = new Set<number>();\n for (const key of applied) {\n const at = key.lastIndexOf(\"@\");\n if (at < 0) continue;\n const c = key.slice(0, at);\n const v = Number(key.slice(at + 1));\n if (c === component && Number.isFinite(v)) versions.add(v);\n }\n return versions;\n }\n\n async run(\n migrations: ReadonlyArray<IndexedDbMigration>,\n options: RunMigrationsOptions = {}\n ): Promise<ReadonlyArray<IndexedDbMigration>> {\n const prev = RUN_LOCKS.get(this.dbName) ?? Promise.resolve();\n const next = prev.catch(() => undefined).then(() => this.runLocked(migrations, options));\n RUN_LOCKS.set(this.dbName, next);\n try {\n return await next;\n } finally {\n if (RUN_LOCKS.get(this.dbName) === next) RUN_LOCKS.delete(this.dbName);\n }\n }\n\n /**\n * Body of `run()` that executes under the per-dbName lock. Probes the\n * bookkeeping store *after* acquiring the lock so each serialized caller\n * sees rows committed by the previous one — without re-probing, a queued\n * caller would re-run already-applied migrations from a stale snapshot.\n */\n private async runLocked(\n migrations: ReadonlyArray<IndexedDbMigration>,\n options: RunMigrationsOptions\n ): Promise<ReadonlyArray<IndexedDbMigration>> {\n const sorted = sortMigrations(migrations);\n if (sorted.length === 0) return [];\n\n const { currentVersion, applied: alreadyApplied } = await this.probe();\n const pending = sorted.filter((m) => !alreadyApplied.has(`${m.component}@${m.version}`));\n if (pending.length === 0) return [];\n\n // Bump the IDB version monotonically — its only role is to trigger\n // onupgradeneeded. The bookkeeping store decides what actually runs.\n const targetVersion = currentVersion + 1;\n const applied: IndexedDbMigration[] = [];\n // Buffer events emitted from inside `onupgradeneeded` and flush after the\n // open request settles. Calling user code (the listener) inside the IDB\n // upgrade transaction can let microtasks slip in and auto-commit the\n // transaction before our migrations are done.\n const onProgress = options.onProgress;\n const buffered: Parameters<NonNullable<typeof onProgress>>[0][] = [];\n const emitLater = onProgress\n ? (ev: Parameters<NonNullable<typeof onProgress>>[0]) => buffered.push(ev)\n : undefined;\n\n await new Promise<void>((resolve, reject) => {\n // Same orphan-callback hazard as `probe()`, plus an extra one: aborting\n // the upgrade transaction in our catch path queues an `onerror` that\n // would otherwise re-reject after we've already settled with the real\n // cause. Funnel everything through `finalize`.\n let settled = false;\n let upgradeDb: IDBDatabase | undefined;\n const finalize = (outcome: { ok: true } | { ok: false; error: unknown }): void => {\n if (settled) return;\n settled = true;\n if (upgradeDb) {\n try {\n upgradeDb.close();\n } catch {\n // ignore — close is best-effort\n }\n }\n if (outcome.ok) resolve();\n else reject(outcome.error);\n };\n\n const upreq = this.idb.open(this.dbName, targetVersion);\n upreq.onupgradeneeded = (ev) => {\n if (settled) return;\n try {\n const db = upreq.result;\n upgradeDb = db;\n const tx = upreq.transaction!;\n const oldVersion = ev.oldVersion;\n const newVersion = ev.newVersion ?? targetVersion;\n\n // Another tab opening at a still-higher version mid-upgrade: abort\n // our work and surface a typed error rather than blocking forever.\n db.onversionchange = () => {\n try {\n tx.abort();\n } catch {\n // ignore — tx may already be settling\n }\n finalize({ ok: false, error: new MigrationAbortedByOtherTabError(this.dbName) });\n };\n\n if (!db.objectStoreNames.contains(MIGRATIONS_TABLE)) {\n db.createObjectStore(MIGRATIONS_TABLE, { keyPath: [\"component\", \"version\"] });\n }\n const meta = tx.objectStore(MIGRATIONS_TABLE);\n\n for (const m of pending) {\n emitLater?.({\n component: m.component,\n version: m.version,\n phase: \"starting\",\n description: m.description,\n });\n const ctx: IndexedDbUpgradeContext = { db, tx, oldVersion, newVersion };\n const result = m.up(ctx, (fraction) => {\n emitLater?.({\n component: m.component,\n version: m.version,\n phase: \"running\",\n description: m.description,\n fraction,\n });\n });\n if (result instanceof Promise) {\n throw new Error(\n `IndexedDB migration \"${m.component}@${m.version}\" returned a Promise; ` +\n `IDB upgrade transactions cannot span async work.`\n );\n }\n meta.add({\n component: m.component,\n version: m.version,\n description: m.description ?? null,\n applied_at: new Date().toISOString(),\n });\n applied.push(m);\n emitLater?.({\n component: m.component,\n version: m.version,\n phase: \"completed\",\n description: m.description,\n fraction: 1,\n });\n }\n } catch (err) {\n // Force the open request to fail by aborting the upgrade transaction.\n // The orphan `onerror` it triggers will see `settled` and bail.\n try {\n upreq.transaction?.abort();\n } catch {\n // ignore\n }\n // Tag which migration failed (best-effort: the one whose `starting`\n // we last buffered without a matching `completed`).\n const lastStart = [...buffered].reverse().find((e) => e.phase === \"starting\");\n if (lastStart) {\n emitLater?.({\n component: lastStart.component,\n version: lastStart.version,\n phase: \"failed\",\n description: lastStart.description,\n error: err,\n });\n }\n finalize({ ok: false, error: err });\n }\n };\n upreq.onsuccess = () => {\n if (settled) return;\n upgradeDb = upreq.result;\n finalize({ ok: true });\n };\n upreq.onerror = () => {\n if (settled) return;\n finalize({ ok: false, error: upreq.error });\n };\n upreq.onblocked = () => {\n if (settled) return;\n finalize({\n ok: false,\n error: new Error(`IndexedDB ${this.dbName} upgrade blocked — close other tabs.`),\n });\n };\n }).finally(() => {\n // Flush buffered events after the upgrade settles, regardless of\n // success/failure. Listeners always see a consistent \"starting →\n // {completed | failed}\" sequence.\n if (onProgress) {\n for (const ev of buffered) onProgress(ev);\n }\n });\n\n return applied;\n }\n}\n\n/**\n * Convenience helper used by storage classes — runs the supplied migration\n * groups (one per IDB database) against fresh runners and returns an array\n * of the applied migrations across all groups.\n *\n * IndexedDB groups exist because some storages (e.g. the rate limiter) span\n * multiple databases and expose them as separate `(dbName, migrations)` pairs\n * via `getMigrations()`.\n */\nexport interface IndexedDbMigrationGroup {\n readonly dbName: string;\n readonly migrations: ReadonlyArray<IndexedDbMigration>;\n}\n\nexport async function runIndexedDbMigrationGroups(\n groups: ReadonlyArray<IndexedDbMigrationGroup>,\n options: RunMigrationsOptions & { idb?: IDBFactory } = {}\n): Promise<ReadonlyArray<IndexedDbMigration>> {\n const { idb, onProgress } = options;\n const all: IndexedDbMigration[] = [];\n for (const group of groups) {\n const runner = idb\n ? new IndexedDbMigrationRunner(group.dbName, idb)\n : new IndexedDbMigrationRunner(group.dbName);\n const applied = await runner.run(group.migrations, { onProgress });\n all.push(...applied);\n }\n return all;\n}\n",
7
7
  "/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { PrefixColumn } from \"@workglow/job-queue\";\nimport type { IndexedDbMigration, IndexedDbMigrationGroup } from \"./IndexedDbMigrationRunner\";\n\n/**\n * Migration set for the IndexedDB queue object store identified by\n * `tableName`.\n *\n * Component name is `queue:indexeddb:<tableName>` so two queues with\n * different table names get tracked independently in `_storage_migrations`.\n *\n * Schema: a single object store keyed by `id` plus four compound indexes\n * (queue/status, queue/status/visible_at, queue/job_run_id,\n * queue/fingerprint/status). When `prefixes` is non-empty the prefix\n * columns are prepended to every index key path so per-tenant queries can\n * be served directly by the index.\n */\nexport function indexedDbQueueMigrations(\n tableName: string,\n prefixes: readonly PrefixColumn[]\n): IndexedDbMigration[] {\n const component = `queue:indexeddb:${tableName}`;\n const prefixCols = prefixes.map((p) => p.name);\n const k = (cols: string[]): string[] => [...prefixCols, ...cols];\n\n return [\n {\n component,\n version: 1,\n description: \"Create queue object store + indexes\",\n up({ db }) {\n if (!db.objectStoreNames.contains(tableName)) {\n const store = db.createObjectStore(tableName, { keyPath: \"id\" });\n store.createIndex(\"queue_status\", k([\"queue\", \"status\"]), { unique: false });\n store.createIndex(\"queue_status_visible_at\", k([\"queue\", \"status\", \"visible_at\"]), {\n unique: false,\n });\n store.createIndex(\"queue_job_run_id\", k([\"queue\", \"job_run_id\"]), { unique: false });\n store.createIndex(\"queue_fingerprint_status\", k([\"queue\", \"fingerprint\", \"status\"]), {\n unique: false,\n });\n }\n },\n },\n ];\n}\n\n/** Returns the queue migrations packaged with their target IDB database name. */\nexport function indexedDbQueueMigrationGroup(\n tableName: string,\n prefixes: readonly PrefixColumn[]\n): IndexedDbMigrationGroup {\n return {\n dbName: tableName,\n migrations: indexedDbQueueMigrations(tableName, prefixes),\n };\n}\n",
8
8
  "/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Opens an IDB connection and wires `onversionchange` so a future schema\n * bump in another tab can close this connection rather than blocking the\n * upgrade indefinitely.\n *\n * Pass `version` + `onUpgradeNeeded` only when the call site is itself\n * running a schema upgrade. Most callers want the no-arg form: open at\n * the database's current version and use it for reads/writes.\n */\nexport function openIdb(\n dbName: string,\n options: {\n readonly version?: number;\n readonly onUpgradeNeeded?: (event: IDBVersionChangeEvent) => void;\n } = {}\n): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const req = indexedDB.open(dbName, options.version);\n req.onsuccess = () => {\n const db = req.result;\n db.onversionchange = () => db.close();\n resolve(db);\n };\n req.onupgradeneeded = (ev) => options.onUpgradeNeeded?.(ev);\n req.onerror = () => {\n const err = req.error;\n if (err && err.name === \"VersionError\") {\n reject(\n new Error(\n `IndexedDB ${dbName} exists at a higher version than ${options.version ?? \"current\"}`\n )\n );\n return;\n }\n reject(err);\n };\n req.onblocked = () =>\n reject(new Error(`IndexedDB ${dbName} is blocked — close other tabs using this database.`));\n });\n}\n",
9
9
  "/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { QueuePair } from \"@workglow/job-queue\";\nimport { wrapQueueStorage } from \"@workglow/job-queue\";\nimport { IndexedDbQueueStorage, type IndexedDbQueueStorageOptions } from \"./IndexedDbQueueStorage\";\n\n/**\n * Factory for the paired IndexedDB message queue and job store. Both\n * facades share a single underlying {@link IndexedDbQueueStorage} so writes\n * through one are observable through the other.\n */\nexport function createIndexedDbQueue<Input, Output>(\n queueName: string,\n opts?: IndexedDbQueueStorageOptions\n): QueuePair<Input, Output> {\n return wrapQueueStorage<Input, Output>(new IndexedDbQueueStorage<Input, Output>(queueName, opts));\n}\n",
10
- "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type {\n IRateLimiterStorage,\n PrefixColumn,\n RateLimiterStorageOptions,\n RateLimiterStorageScope,\n} from \"@workglow/job-queue\";\nimport { createServiceToken } from \"@workglow/util\";\nimport { runIndexedDbMigrationGroups } from \"../migrations/IndexedDbMigrationRunner\";\nimport { indexedDbRateLimiterMigrationGroups } from \"../migrations/indexedDbRateLimiterMigrations\";\nimport { openIdb } from \"../storage/openIdb\";\n\nexport const INDEXED_DB_RATE_LIMITER_STORAGE = createServiceToken<IRateLimiterStorage>(\n \"ratelimiter.storage.indexedDb\"\n);\n\nexport type IndexedDbRateLimiterStorageOptions = RateLimiterStorageOptions;\n\ninterface ExecutionRecord {\n readonly id?: string;\n readonly queue_name: string;\n readonly executed_at: string;\n readonly [key: string]: unknown;\n}\n\ninterface NextAvailableRecord {\n readonly queue_name: string;\n readonly next_available_at: string;\n readonly [key: string]: unknown;\n}\n\n/**\n * IndexedDB implementation of rate limiter storage.\n * Manages execution records and next available times for rate limiting.\n *\n * Atomicity is `\"process\"`-scoped (browser tab) and the `next_available_at`\n * pre-check inside {@link tryReserveExecution} runs in a separate IDB\n * transaction from the count-and-insert. See {@link tryReserveExecution} for\n * the full caveat — do not assume cross-store atomicity.\n */\nexport class IndexedDbRateLimiterStorage implements IRateLimiterStorage {\n /**\n * `\"process\"` — IndexedDB is per-origin and shared across tabs but not across\n * processes (different machines, server processes). Within a single tab the\n * `readwrite` transaction below provides atomicity.\n */\n public readonly scope: RateLimiterStorageScope = \"process\";\n private executionDb: IDBDatabase | undefined;\n private nextAvailableDb: IDBDatabase | undefined;\n private readonly executionTableName: string;\n private readonly nextAvailableTableName: string;\n protected readonly prefixes: readonly PrefixColumn[];\n protected readonly prefixValues: Readonly<Record<string, string | number>>;\n\n constructor(options: IndexedDbRateLimiterStorageOptions = {}) {\n this.prefixes = options.prefixes ?? [];\n this.prefixValues = options.prefixValues ?? {};\n\n if (this.prefixes.length > 0) {\n const prefixNames = this.prefixes.map((p) => p.name).join(\"_\");\n this.executionTableName = `rate_limit_executions_${prefixNames}`;\n this.nextAvailableTableName = `rate_limit_next_available_${prefixNames}`;\n } else {\n this.executionTableName = \"rate_limit_executions\";\n this.nextAvailableTableName = \"rate_limit_next_available\";\n }\n }\n\n private getPrefixColumnNames(): string[] {\n return this.prefixes.map((p) => p.name);\n }\n\n private matchesPrefixes(record: Record<string, unknown>): boolean {\n for (const [key, value] of Object.entries(this.prefixValues)) {\n if (record[key] !== value) {\n return false;\n }\n }\n return true;\n }\n\n private getPrefixKeyValues(): Array<string | number> {\n return this.prefixes.map((p) => this.prefixValues[p.name]);\n }\n\n private async getExecutionDb(): Promise<IDBDatabase> {\n if (this.executionDb) return this.executionDb;\n await this.migrate();\n return this.executionDb!;\n }\n\n private async getNextAvailableDb(): Promise<IDBDatabase> {\n if (this.nextAvailableDb) return this.nextAvailableDb;\n await this.migrate();\n return this.nextAvailableDb!;\n }\n\n /**\n * Returns the versioned migration groups (one per IndexedDB database) that\n * the rate-limiter's tables depend on. The execution and next-available\n * stores live in separate databases, so this returns two groups.\n */\n public getMigrations() {\n return indexedDbRateLimiterMigrationGroups(\n this.executionTableName,\n this.nextAvailableTableName,\n this.prefixes\n );\n }\n\n /**\n * Applies any pending migrations for the rate limiter's two IndexedDB\n * databases, then opens long-lived connections at the migrated versions.\n * Idempotent — a second call closes any prior handles (so they don't pin\n * the pre-migration versions or block another tab's upgrade), reruns the\n * runner (no-op if everything is recorded as applied), and reopens.\n */\n public async migrate(): Promise<void> {\n if (this.executionDb) {\n try {\n this.executionDb.close();\n } catch {\n // ignore — close is best-effort\n }\n this.executionDb = undefined;\n }\n if (this.nextAvailableDb) {\n try {\n this.nextAvailableDb.close();\n } catch {\n // ignore — close is best-effort\n }\n this.nextAvailableDb = undefined;\n }\n await runIndexedDbMigrationGroups(this.getMigrations());\n this.executionDb = await openIdb(this.executionTableName);\n this.nextAvailableDb = await openIdb(this.nextAvailableTableName);\n }\n\n /**\n * Atomically reserves an execution slot in IndexedDB.\n *\n * Atomicity caveat: this implementation only serializes the `count + insert`\n * pair (under a single `readwrite` IDB transaction over the executions\n * store). The `next_available_at` lookup is a soft pre-check performed\n * BEFORE the tx, because the executions and next-available object stores\n * live in separate `IDBDatabase` instances by design (one\n * `ensureIndexedDbTable` call each) and IDB transaction scope cannot cross\n * databases. This is acceptable for IndexedDB's `\"process\"` scope: rate-\n * limit overshoot from a stale next-available read is bounded by single-\n * tab serial execution, and the stricter count check is what protects the\n * primary maxExecutions invariant.\n */\n public async tryReserveExecution(\n queueName: string,\n maxExecutions: number,\n windowMs: number\n ): Promise<unknown | null> {\n // Soft pre-check against external backoff. Done before opening the tx\n // because next-available lives in a separate IDBDatabase.\n const nextIso = await this.getNextAvailableTime(queueName);\n if (nextIso && new Date(nextIso).getTime() > Date.now()) {\n return null;\n }\n\n const execDb = await this.getExecutionDb();\n const prefixKeyValues = this.getPrefixKeyValues();\n const windowStartIso = new Date(Date.now() - windowMs).toISOString();\n const execTx = execDb.transaction(this.executionTableName, \"readwrite\");\n const execStore = execTx.objectStore(this.executionTableName);\n const insertedId = crypto.randomUUID();\n\n return new Promise<unknown | null>((resolve, reject) => {\n let liveCount = 0;\n let didInsert = false;\n const liveRange = IDBKeyRange.bound(\n [...prefixKeyValues, queueName, windowStartIso],\n [...prefixKeyValues, queueName, \"￿\"],\n true,\n false\n );\n const cursorReq = execStore.index(\"queue_executed_at\").openCursor(liveRange);\n\n cursorReq.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;\n if (cursor) {\n const record = cursor.value as ExecutionRecord;\n if (this.matchesPrefixes(record)) {\n liveCount++;\n }\n cursor.continue();\n return;\n }\n\n if (liveCount >= maxExecutions) {\n // Aborting fires execTx.onabort below, which resolves with `false`.\n // No INSERT happened, so capacity is preserved.\n execTx.abort();\n return;\n }\n\n const record: ExecutionRecord = {\n id: insertedId,\n queue_name: queueName,\n executed_at: new Date().toISOString(),\n };\n for (const [k, v] of Object.entries(this.prefixValues)) {\n (record as Record<string, unknown>)[k] = v;\n }\n const addReq = execStore.add(record);\n didInsert = true;\n addReq.onerror = () => {\n try {\n execTx.abort();\n } catch {\n // already aborted\n }\n reject(addReq.error);\n };\n };\n\n cursorReq.onerror = () => reject(cursorReq.error);\n execTx.oncomplete = () => resolve(didInsert ? insertedId : null);\n execTx.onerror = () => reject(execTx.error);\n execTx.onabort = () => resolve(null);\n });\n }\n\n public async releaseExecution(queueName: string, token: unknown): Promise<void> {\n if (token === null || token === undefined) return;\n const db = await this.getExecutionDb();\n const tx = db.transaction(this.executionTableName, \"readwrite\");\n const store = tx.objectStore(this.executionTableName);\n return new Promise<void>((resolve, reject) => {\n // Delete by id — NEVER by recency. Two concurrent acquirers can hold\n // tokens for different rows; deleting \"the most recent\" would release\n // the other worker's slot.\n const req = store.delete(token as IDBValidKey);\n req.onerror = () => reject(req.error);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n\n public async getExecutionCount(queueName: string, windowStartTime: string): Promise<number> {\n const db = await this.getExecutionDb();\n const tx = db.transaction(this.executionTableName, \"readonly\");\n const store = tx.objectStore(this.executionTableName);\n const index = store.index(\"queue_executed_at\");\n const prefixKeyValues = this.getPrefixKeyValues();\n\n return new Promise((resolve, reject) => {\n let count = 0;\n const keyRange = IDBKeyRange.bound(\n [...prefixKeyValues, queueName, windowStartTime],\n [...prefixKeyValues, queueName, \"\\uffff\"],\n true, // exclude lower bound (windowStartTime)\n false\n );\n const request = index.openCursor(keyRange);\n\n request.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;\n if (cursor) {\n const record = cursor.value as ExecutionRecord;\n if (this.matchesPrefixes(record)) {\n count++;\n }\n cursor.continue();\n }\n };\n\n tx.oncomplete = () => resolve(count);\n tx.onerror = () => reject(tx.error);\n request.onerror = () => reject(request.error);\n });\n }\n\n public async getOldestExecutionAtOffset(\n queueName: string,\n offset: number\n ): Promise<string | undefined> {\n const db = await this.getExecutionDb();\n const tx = db.transaction(this.executionTableName, \"readonly\");\n const store = tx.objectStore(this.executionTableName);\n const index = store.index(\"queue_executed_at\");\n const prefixKeyValues = this.getPrefixKeyValues();\n\n return new Promise((resolve, reject) => {\n const executions: string[] = [];\n const keyRange = IDBKeyRange.bound(\n [...prefixKeyValues, queueName, \"\"],\n [...prefixKeyValues, queueName, \"\\uffff\"]\n );\n const request = index.openCursor(keyRange);\n\n request.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;\n if (cursor) {\n const record = cursor.value as ExecutionRecord;\n if (this.matchesPrefixes(record)) {\n executions.push(record.executed_at);\n }\n cursor.continue();\n }\n };\n\n tx.oncomplete = () => {\n executions.sort();\n resolve(executions[offset]);\n };\n tx.onerror = () => reject(tx.error);\n request.onerror = () => reject(request.error);\n });\n }\n\n public async getNextAvailableTime(queueName: string): Promise<string | undefined> {\n const db = await this.getNextAvailableDb();\n const tx = db.transaction(this.nextAvailableTableName, \"readonly\");\n const store = tx.objectStore(this.nextAvailableTableName);\n const prefixKeyValues = this.getPrefixKeyValues();\n const key = [...prefixKeyValues, queueName].join(\"_\");\n\n return new Promise((resolve, reject) => {\n const request = store.get(key);\n request.onsuccess = () => {\n const record = request.result as NextAvailableRecord | undefined;\n if (record && this.matchesPrefixes(record)) {\n resolve(record.next_available_at);\n } else {\n resolve(undefined);\n }\n };\n request.onerror = () => reject(request.error);\n tx.onerror = () => reject(tx.error);\n });\n }\n\n public async setNextAvailableTime(queueName: string, nextAvailableAt: string): Promise<void> {\n const db = await this.getNextAvailableDb();\n const tx = db.transaction(this.nextAvailableTableName, \"readwrite\");\n const store = tx.objectStore(this.nextAvailableTableName);\n const prefixKeyValues = this.getPrefixKeyValues();\n const key = [...prefixKeyValues, queueName].join(\"_\");\n\n const record: NextAvailableRecord & { [key: string]: unknown } = {\n queue_name: queueName,\n next_available_at: nextAvailableAt,\n };\n\n for (const [k, value] of Object.entries(this.prefixValues)) {\n record[k] = value;\n }\n\n (record as Record<string, unknown>)[\n this.getPrefixColumnNames().concat([\"queue_name\"]).join(\"_\")\n ] = key;\n\n return new Promise((resolve, reject) => {\n const request = store.put(record);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n request.onerror = () => reject(request.error);\n });\n }\n\n public async clear(queueName: string): Promise<void> {\n const execDb = await this.getExecutionDb();\n const execTx = execDb.transaction(this.executionTableName, \"readwrite\");\n const execStore = execTx.objectStore(this.executionTableName);\n const execIndex = execStore.index(\"queue_executed_at\");\n const prefixKeyValues = this.getPrefixKeyValues();\n\n await new Promise<void>((resolve, reject) => {\n const keyRange = IDBKeyRange.bound(\n [...prefixKeyValues, queueName, \"\"],\n [...prefixKeyValues, queueName, \"\\uffff\"]\n );\n const request = execIndex.openCursor(keyRange);\n\n request.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;\n if (cursor) {\n const record = cursor.value as ExecutionRecord;\n if (this.matchesPrefixes(record)) {\n cursor.delete();\n }\n cursor.continue();\n }\n };\n\n execTx.oncomplete = () => resolve();\n execTx.onerror = () => reject(execTx.error);\n request.onerror = () => reject(request.error);\n });\n\n const nextDb = await this.getNextAvailableDb();\n const nextTx = nextDb.transaction(this.nextAvailableTableName, \"readwrite\");\n const nextStore = nextTx.objectStore(this.nextAvailableTableName);\n const key = [...prefixKeyValues, queueName].join(\"_\");\n\n await new Promise<void>((resolve, reject) => {\n const request = nextStore.delete(key);\n nextTx.oncomplete = () => resolve();\n nextTx.onerror = () => reject(nextTx.error);\n request.onerror = () => reject(request.error);\n });\n }\n}\n",
10
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type {\n IRateLimiterStorage,\n LimiterToken,\n PrefixColumn,\n RateLimiterStorageOptions,\n RateLimiterStorageScope,\n} from \"@workglow/job-queue\";\nimport { createServiceToken } from \"@workglow/util\";\nimport { runIndexedDbMigrationGroups } from \"../migrations/IndexedDbMigrationRunner\";\nimport { indexedDbRateLimiterMigrationGroups } from \"../migrations/indexedDbRateLimiterMigrations\";\nimport { openIdb } from \"../storage/openIdb\";\n\nexport const INDEXED_DB_RATE_LIMITER_STORAGE = createServiceToken<IRateLimiterStorage>(\n \"ratelimiter.storage.indexedDb\"\n);\n\nexport type IndexedDbRateLimiterStorageOptions = RateLimiterStorageOptions;\n\ninterface ExecutionRecord {\n readonly id?: string;\n readonly queue_name: string;\n readonly executed_at: string;\n readonly [key: string]: unknown;\n}\n\ninterface NextAvailableRecord {\n readonly queue_name: string;\n readonly next_available_at: string;\n readonly [key: string]: unknown;\n}\n\n/**\n * IndexedDB implementation of rate limiter storage.\n * Manages execution records and next available times for rate limiting.\n *\n * Atomicity is `\"process\"`-scoped (browser tab) and the `next_available_at`\n * pre-check inside {@link tryReserveExecution} runs in a separate IDB\n * transaction from the count-and-insert. See {@link tryReserveExecution} for\n * the full caveat — do not assume cross-store atomicity.\n */\nexport class IndexedDbRateLimiterStorage implements IRateLimiterStorage {\n /**\n * `\"process\"` — IndexedDB is per-origin and shared across tabs but not across\n * processes (different machines, server processes). Within a single tab the\n * `readwrite` transaction below provides atomicity.\n */\n public readonly scope: RateLimiterStorageScope = \"process\";\n private executionDb: IDBDatabase | undefined;\n private nextAvailableDb: IDBDatabase | undefined;\n private readonly executionTableName: string;\n private readonly nextAvailableTableName: string;\n protected readonly prefixes: readonly PrefixColumn[];\n protected readonly prefixValues: Readonly<Record<string, string | number>>;\n\n constructor(options: IndexedDbRateLimiterStorageOptions = {}) {\n this.prefixes = options.prefixes ?? [];\n this.prefixValues = options.prefixValues ?? {};\n\n if (this.prefixes.length > 0) {\n const prefixNames = this.prefixes.map((p) => p.name).join(\"_\");\n this.executionTableName = `rate_limit_executions_${prefixNames}`;\n this.nextAvailableTableName = `rate_limit_next_available_${prefixNames}`;\n } else {\n this.executionTableName = \"rate_limit_executions\";\n this.nextAvailableTableName = \"rate_limit_next_available\";\n }\n }\n\n private getPrefixColumnNames(): string[] {\n return this.prefixes.map((p) => p.name);\n }\n\n private matchesPrefixes(record: Record<string, unknown>): boolean {\n for (const [key, value] of Object.entries(this.prefixValues)) {\n if (record[key] !== value) {\n return false;\n }\n }\n return true;\n }\n\n private getPrefixKeyValues(): Array<string | number> {\n return this.prefixes.map((p) => this.prefixValues[p.name]);\n }\n\n private async getExecutionDb(): Promise<IDBDatabase> {\n if (this.executionDb) return this.executionDb;\n await this.migrate();\n return this.executionDb!;\n }\n\n private async getNextAvailableDb(): Promise<IDBDatabase> {\n if (this.nextAvailableDb) return this.nextAvailableDb;\n await this.migrate();\n return this.nextAvailableDb!;\n }\n\n /**\n * Returns the versioned migration groups (one per IndexedDB database) that\n * the rate-limiter's tables depend on. The execution and next-available\n * stores live in separate databases, so this returns two groups.\n */\n public getMigrations() {\n return indexedDbRateLimiterMigrationGroups(\n this.executionTableName,\n this.nextAvailableTableName,\n this.prefixes\n );\n }\n\n /**\n * Applies any pending migrations for the rate limiter's two IndexedDB\n * databases, then opens long-lived connections at the migrated versions.\n * Idempotent — a second call closes any prior handles (so they don't pin\n * the pre-migration versions or block another tab's upgrade), reruns the\n * runner (no-op if everything is recorded as applied), and reopens.\n */\n public async migrate(): Promise<void> {\n if (this.executionDb) {\n try {\n this.executionDb.close();\n } catch {\n // ignore — close is best-effort\n }\n this.executionDb = undefined;\n }\n if (this.nextAvailableDb) {\n try {\n this.nextAvailableDb.close();\n } catch {\n // ignore — close is best-effort\n }\n this.nextAvailableDb = undefined;\n }\n await runIndexedDbMigrationGroups(this.getMigrations());\n this.executionDb = await openIdb(this.executionTableName);\n this.nextAvailableDb = await openIdb(this.nextAvailableTableName);\n }\n\n /**\n * Atomically reserves an execution slot in IndexedDB.\n *\n * Atomicity caveat: this implementation only serializes the `count + insert`\n * pair (under a single `readwrite` IDB transaction over the executions\n * store). The `next_available_at` lookup is a soft pre-check performed\n * BEFORE the tx, because the executions and next-available object stores\n * live in separate `IDBDatabase` instances by design (one\n * `ensureIndexedDbTable` call each) and IDB transaction scope cannot cross\n * databases. This is acceptable for IndexedDB's `\"process\"` scope: rate-\n * limit overshoot from a stale next-available read is bounded by single-\n * tab serial execution, and the stricter count check is what protects the\n * primary maxExecutions invariant.\n */\n public async tryReserveExecution(\n queueName: string,\n maxExecutions: number,\n windowMs: number\n ): Promise<LimiterToken | null> {\n // Soft pre-check against external backoff. Done before opening the tx\n // because next-available lives in a separate IDBDatabase.\n const nextIso = await this.getNextAvailableTime(queueName);\n if (nextIso && new Date(nextIso).getTime() > Date.now()) {\n return null;\n }\n\n const execDb = await this.getExecutionDb();\n const prefixKeyValues = this.getPrefixKeyValues();\n const windowStartIso = new Date(Date.now() - windowMs).toISOString();\n const execTx = execDb.transaction(this.executionTableName, \"readwrite\");\n const execStore = execTx.objectStore(this.executionTableName);\n const insertedId = crypto.randomUUID();\n\n return new Promise<LimiterToken | null>((resolve, reject) => {\n let liveCount = 0;\n let didInsert = false;\n const liveRange = IDBKeyRange.bound(\n [...prefixKeyValues, queueName, windowStartIso],\n [...prefixKeyValues, queueName, \"￿\"],\n true,\n false\n );\n const cursorReq = execStore.index(\"queue_executed_at\").openCursor(liveRange);\n\n cursorReq.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;\n if (cursor) {\n const record = cursor.value as ExecutionRecord;\n if (this.matchesPrefixes(record)) {\n liveCount++;\n }\n cursor.continue();\n return;\n }\n\n if (liveCount >= maxExecutions) {\n // Aborting fires execTx.onabort below, which resolves with `false`.\n // No INSERT happened, so capacity is preserved.\n execTx.abort();\n return;\n }\n\n const record: ExecutionRecord = {\n id: insertedId,\n queue_name: queueName,\n executed_at: new Date().toISOString(),\n };\n for (const [k, v] of Object.entries(this.prefixValues)) {\n (record as Record<string, unknown>)[k] = v;\n }\n const addReq = execStore.add(record);\n didInsert = true;\n addReq.onerror = () => {\n try {\n execTx.abort();\n } catch {\n // already aborted\n }\n reject(addReq.error);\n };\n };\n\n cursorReq.onerror = () => reject(cursorReq.error);\n execTx.oncomplete = () => resolve(didInsert ? insertedId : null);\n execTx.onerror = () => reject(execTx.error);\n execTx.onabort = () => resolve(null);\n });\n }\n\n public async releaseExecution(queueName: string, token: unknown): Promise<void> {\n if (token === null || token === undefined) return;\n const db = await this.getExecutionDb();\n const tx = db.transaction(this.executionTableName, \"readwrite\");\n const store = tx.objectStore(this.executionTableName);\n return new Promise<void>((resolve, reject) => {\n // Delete by id — NEVER by recency. Two concurrent acquirers can hold\n // tokens for different rows; deleting \"the most recent\" would release\n // the other worker's slot.\n const req = store.delete(token as IDBValidKey);\n req.onerror = () => reject(req.error);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n\n public async getExecutionCount(queueName: string, windowStartTime: string): Promise<number> {\n const db = await this.getExecutionDb();\n const tx = db.transaction(this.executionTableName, \"readonly\");\n const store = tx.objectStore(this.executionTableName);\n const index = store.index(\"queue_executed_at\");\n const prefixKeyValues = this.getPrefixKeyValues();\n\n return new Promise((resolve, reject) => {\n let count = 0;\n const keyRange = IDBKeyRange.bound(\n [...prefixKeyValues, queueName, windowStartTime],\n [...prefixKeyValues, queueName, \"\\uffff\"],\n true, // exclude lower bound (windowStartTime)\n false\n );\n const request = index.openCursor(keyRange);\n\n request.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;\n if (cursor) {\n const record = cursor.value as ExecutionRecord;\n if (this.matchesPrefixes(record)) {\n count++;\n }\n cursor.continue();\n }\n };\n\n tx.oncomplete = () => resolve(count);\n tx.onerror = () => reject(tx.error);\n request.onerror = () => reject(request.error);\n });\n }\n\n public async getOldestExecutionAtOffset(\n queueName: string,\n offset: number\n ): Promise<string | undefined> {\n const db = await this.getExecutionDb();\n const tx = db.transaction(this.executionTableName, \"readonly\");\n const store = tx.objectStore(this.executionTableName);\n const index = store.index(\"queue_executed_at\");\n const prefixKeyValues = this.getPrefixKeyValues();\n\n return new Promise((resolve, reject) => {\n const executions: string[] = [];\n const keyRange = IDBKeyRange.bound(\n [...prefixKeyValues, queueName, \"\"],\n [...prefixKeyValues, queueName, \"\\uffff\"]\n );\n const request = index.openCursor(keyRange);\n\n request.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;\n if (cursor) {\n const record = cursor.value as ExecutionRecord;\n if (this.matchesPrefixes(record)) {\n executions.push(record.executed_at);\n }\n cursor.continue();\n }\n };\n\n tx.oncomplete = () => {\n executions.sort();\n resolve(executions[offset]);\n };\n tx.onerror = () => reject(tx.error);\n request.onerror = () => reject(request.error);\n });\n }\n\n public async getNextAvailableTime(queueName: string): Promise<string | undefined> {\n const db = await this.getNextAvailableDb();\n const tx = db.transaction(this.nextAvailableTableName, \"readonly\");\n const store = tx.objectStore(this.nextAvailableTableName);\n const prefixKeyValues = this.getPrefixKeyValues();\n const key = [...prefixKeyValues, queueName].join(\"_\");\n\n return new Promise((resolve, reject) => {\n const request = store.get(key);\n request.onsuccess = () => {\n const record = request.result as NextAvailableRecord | undefined;\n if (record && this.matchesPrefixes(record)) {\n resolve(record.next_available_at);\n } else {\n resolve(undefined);\n }\n };\n request.onerror = () => reject(request.error);\n tx.onerror = () => reject(tx.error);\n });\n }\n\n public async setNextAvailableTime(queueName: string, nextAvailableAt: string): Promise<void> {\n const db = await this.getNextAvailableDb();\n const tx = db.transaction(this.nextAvailableTableName, \"readwrite\");\n const store = tx.objectStore(this.nextAvailableTableName);\n const prefixKeyValues = this.getPrefixKeyValues();\n const key = [...prefixKeyValues, queueName].join(\"_\");\n\n const record: NextAvailableRecord & { [key: string]: unknown } = {\n queue_name: queueName,\n next_available_at: nextAvailableAt,\n };\n\n for (const [k, value] of Object.entries(this.prefixValues)) {\n record[k] = value;\n }\n\n (record as Record<string, unknown>)[\n this.getPrefixColumnNames().concat([\"queue_name\"]).join(\"_\")\n ] = key;\n\n return new Promise((resolve, reject) => {\n const request = store.put(record);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n request.onerror = () => reject(request.error);\n });\n }\n\n public async clear(queueName: string): Promise<void> {\n const execDb = await this.getExecutionDb();\n const execTx = execDb.transaction(this.executionTableName, \"readwrite\");\n const execStore = execTx.objectStore(this.executionTableName);\n const execIndex = execStore.index(\"queue_executed_at\");\n const prefixKeyValues = this.getPrefixKeyValues();\n\n await new Promise<void>((resolve, reject) => {\n const keyRange = IDBKeyRange.bound(\n [...prefixKeyValues, queueName, \"\"],\n [...prefixKeyValues, queueName, \"\\uffff\"]\n );\n const request = execIndex.openCursor(keyRange);\n\n request.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;\n if (cursor) {\n const record = cursor.value as ExecutionRecord;\n if (this.matchesPrefixes(record)) {\n cursor.delete();\n }\n cursor.continue();\n }\n };\n\n execTx.oncomplete = () => resolve();\n execTx.onerror = () => reject(execTx.error);\n request.onerror = () => reject(request.error);\n });\n\n const nextDb = await this.getNextAvailableDb();\n const nextTx = nextDb.transaction(this.nextAvailableTableName, \"readwrite\");\n const nextStore = nextTx.objectStore(this.nextAvailableTableName);\n const key = [...prefixKeyValues, queueName].join(\"_\");\n\n await new Promise<void>((resolve, reject) => {\n const request = nextStore.delete(key);\n nextTx.oncomplete = () => resolve();\n nextTx.onerror = () => reject(nextTx.error);\n request.onerror = () => reject(request.error);\n });\n }\n}\n",
11
11
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { PrefixColumn } from \"@workglow/job-queue\";\nimport type { IndexedDbMigration, IndexedDbMigrationGroup } from \"./IndexedDbMigrationRunner\";\n\n/**\n * Initial migrations for the IndexedDB rate-limiter execution table.\n *\n * Schema: an object store keyed by `id` (autoincrement-style; we generate\n * UUIDs at the storage layer) plus a compound index `queue_executed_at` for\n * windowed counts. Prefix columns are prepended to every index key path.\n */\nexport function indexedDbRateLimiterExecutionMigrations(\n executionTableName: string,\n prefixes: readonly PrefixColumn[]\n): IndexedDbMigration[] {\n const component = `rate-limiter:indexeddb:${executionTableName}`;\n const prefixCols = prefixes.map((p) => p.name);\n const k = (cols: string[]): string[] => [...prefixCols, ...cols];\n\n return [\n {\n component,\n version: 1,\n description: \"Create rate-limiter execution object store + indexes\",\n up({ db }) {\n if (!db.objectStoreNames.contains(executionTableName)) {\n const store = db.createObjectStore(executionTableName, { keyPath: \"id\" });\n store.createIndex(\"queue_executed_at\", k([\"queue_name\", \"executed_at\"]), {\n unique: false,\n });\n }\n },\n },\n ];\n}\n\n/**\n * Initial migrations for the IndexedDB rate-limiter `next_available` table.\n *\n * Schema: object store keyed by a single synthetic field whose name is\n * `prefixCols.concat([\"queue_name\"]).join(\"_\")`. The storage layer writes\n * a record where this field holds `prefixValues.concat([queueName]).join(\"_\")`\n * — i.e. tenant-qualified — so the same physical store can hold rows for\n * multiple tenants without collisions. Without this, two tenants with the\n * same queue name would silently overwrite each other's row (the\n * `next_available_at` of one would be returned for the other).\n *\n * No `queue_name = \"myqueue\"` index is created because the storage's read\n * path (`store.get(syntheticKey)`) goes through the primary key directly.\n */\nexport function indexedDbRateLimiterNextAvailableMigrations(\n nextAvailableTableName: string,\n prefixes: readonly PrefixColumn[]\n): IndexedDbMigration[] {\n const component = `rate-limiter:indexeddb:${nextAvailableTableName}`;\n const prefixCols = prefixes.map((p) => p.name);\n // The keyPath is a single field whose name encodes the prefix-qualified\n // identifier. With no prefixes this collapses to `\"queue_name\"`, matching\n // the legacy single-tenant layout.\n const keyField = [...prefixCols, \"queue_name\"].join(\"_\");\n\n return [\n {\n component,\n version: 1,\n description: \"Create rate-limiter next_available object store\",\n up({ db }) {\n if (!db.objectStoreNames.contains(nextAvailableTableName)) {\n db.createObjectStore(nextAvailableTableName, { keyPath: keyField });\n }\n },\n },\n ];\n}\n\n/**\n * Returns the rate-limiter migrations packaged as IDB migration groups —\n * one group per database, since `IndexedDbRateLimiterStorage` keeps the\n * execution and next-available tables in separate databases.\n */\nexport function indexedDbRateLimiterMigrationGroups(\n executionTableName: string,\n nextAvailableTableName: string,\n prefixes: readonly PrefixColumn[]\n): IndexedDbMigrationGroup[] {\n return [\n {\n dbName: executionTableName,\n migrations: indexedDbRateLimiterExecutionMigrations(executionTableName, prefixes),\n },\n {\n dbName: nextAvailableTableName,\n migrations: indexedDbRateLimiterNextAvailableMigrations(nextAvailableTableName, prefixes),\n },\n ];\n}\n"
12
12
  ],
13
- "mappings": "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",
13
+ "mappings": "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",
14
14
  "debugId": "4D4D60834872C89964756E2164756E21",
15
15
  "names": []
16
16
  }
@@ -9,4 +9,3 @@ export * from "./IndexedDbRateLimiterStorage";
9
9
  export * from "../migrations/IndexedDbMigrationRunner";
10
10
  export * from "../migrations/indexedDbQueueMigrations";
11
11
  export * from "../migrations/indexedDbRateLimiterMigrations";
12
- //# sourceMappingURL=common.d.ts.map
@@ -11,4 +11,3 @@ import { type IndexedDbQueueStorageOptions } from "./IndexedDbQueueStorage";
11
11
  * through one are observable through the other.
12
12
  */
13
13
  export declare function createIndexedDbQueue<Input, Output>(queueName: string, opts?: IndexedDbQueueStorageOptions): QueuePair<Input, Output>;
14
- //# sourceMappingURL=createIndexedDbQueue.d.ts.map
@@ -4,4 +4,3 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
  export * from "./common";
7
- //# sourceMappingURL=node.d.ts.map