@vielzeug/postmaster 2.1.0 → 3.0.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.
- package/README.md +15 -56
- package/dist/_store-utils.cjs +2 -0
- package/dist/_store-utils.cjs.map +1 -0
- package/dist/_store-utils.d.ts +7 -0
- package/dist/_store-utils.d.ts.map +1 -0
- package/dist/_store-utils.js +14 -0
- package/dist/_store-utils.js.map +1 -0
- package/dist/definitions.cjs +1 -1
- package/dist/definitions.cjs.map +1 -1
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js +22 -7
- package/dist/definitions.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/indexeddb.cjs +1 -1
- package/dist/indexeddb.cjs.map +1 -1
- package/dist/indexeddb.d.ts +5 -0
- package/dist/indexeddb.d.ts.map +1 -1
- package/dist/indexeddb.js +126 -43
- package/dist/indexeddb.js.map +1 -1
- package/dist/postmaster.cjs +1 -1
- package/dist/postmaster.cjs.map +1 -1
- package/dist/postmaster.d.ts.map +1 -1
- package/dist/postmaster.iife.js +1 -1
- package/dist/postmaster.iife.js.map +1 -1
- package/dist/postmaster.js +1 -1
- package/dist/postmaster.js.map +1 -1
- package/dist/store-ops.cjs +1 -1
- package/dist/store-ops.cjs.map +1 -1
- package/dist/store-ops.d.ts.map +1 -1
- package/dist/store-ops.js +15 -14
- package/dist/store-ops.js.map +1 -1
- package/dist/store.cjs +0 -0
- package/dist/store.d.ts +10 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +0 -0
- package/dist/testing/memory-store.cjs +1 -1
- package/dist/testing/memory-store.cjs.map +1 -1
- package/dist/testing/memory-store.d.ts.map +1 -1
- package/dist/testing/memory-store.js +1 -9
- package/dist/testing/memory-store.js.map +1 -1
- package/dist/types.d.ts +12 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +17 -9
package/dist/indexeddb.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"indexeddb.js","names":[],"sources":["../src/indexeddb.ts"],"sourcesContent":["import { table } from '@vielzeug/vault';\nimport { createIndexedDB } from '@vielzeug/vault/indexeddb';\nimport { PostmasterError } from './errors.ts';\nimport type { EntryFilter, PostmasterStats, PostmasterStore, StoredJob, StoreTx } from './types.ts';\n\ntype PostmasterSchema = {\n jobs: ReturnType<typeof table<StoredJob, 'id'>>;\n};\n\ntype IndexedDbPostmasterStoreOptions = {\n readonly name: string;\n};\n\nfunction normalize(entry: StoredJob): StoredJob {\n const { failure, leaseExpiresAt, ownerId, ...rest } = entry;\n return {\n ...rest,\n ...(failure !== undefined ? { failure } : {}),\n ...(leaseExpiresAt !== undefined ? { leaseExpiresAt } : {}),\n ...(ownerId !== undefined ? { ownerId } : {}),\n };\n}\n\nfunction wrap<T>(operation: string, fn: () => Promise<T>): Promise<T> {\n return fn().catch((cause: unknown) => {\n if (cause instanceof PostmasterError) throw cause;\n throw new PostmasterError(`${operation} failed`, { cause });\n });\n}\n\nexport function createIndexedDbPostmasterStore(options: IndexedDbPostmasterStoreOptions): PostmasterStore {\n const schema: PostmasterSchema = {\n jobs: table<StoredJob, 'id'>('id', {\n indexes: ['availableAt', 'leaseExpiresAt', 'status'],\n }),\n };\n const vault = createIndexedDB({ name: options.name, schema });\n\n const buildTx = (vaultTx: Parameters<Parameters<typeof vault.batch>[1]>[0]): StoreTx => ({\n async countByStatus(): Promise<PostmasterStats> {\n const [queued, running, deadLetter] = await Promise.all([\n vaultTx.query('jobs').equals('status', 'queued').count(),\n vaultTx.query('jobs').equals('status', 'running').count(),\n vaultTx.query('jobs').equals('status', 'dead-letter').count(),\n ]);\n return { deadLetter, queued, running };\n },\n async delete(id: string): Promise<void> {\n await vaultTx.delete('jobs', id);\n },\n async findClaimable(now: number): Promise<StoredJob | undefined> {\n const candidates = await vaultTx\n .query('jobs')\n .filter(\n (entry) =>\n (entry.status === 'queued' && entry.availableAt <= now) ||\n (entry.status === 'running' && (entry.leaseExpiresAt ?? 0) <= now),\n )\n .toArray();\n const entry = candidates.sort((a, b) => a.availableAt - b.availableAt || a.createdAt - b.createdAt)[0];\n return entry ? normalize(entry) : undefined;\n },\n async findNextWake(now: number): Promise<number | undefined> {\n const entries = await vaultTx.getAll('jobs');\n let next: number | undefined;\n for (const entry of entries) {\n let wake: number | undefined;\n if (entry.status === 'queued' && entry.availableAt > now) wake = entry.availableAt;\n else if (entry.status === 'running' && (entry.leaseExpiresAt ?? 0) > now) wake = entry.leaseExpiresAt;\n if (wake !== undefined && (next === undefined || wake < next)) next = wake;\n }\n return next;\n },\n async get(id: string): Promise<StoredJob | undefined> {\n const entry = await vaultTx.get('jobs', id);\n return entry ? normalize(entry) : undefined;\n },\n async put(entry: StoredJob): Promise<void> {\n await vaultTx.put('jobs', normalize(entry));\n },\n });\n\n return {\n get disposalSignal() {\n return vault.disposalSignal;\n },\n dispose(): Promise<void> {\n return wrap('disposing IndexedDB store', () => vault.dispose());\n },\n get disposed() {\n return vault.disposed;\n },\n list(filter?: EntryFilter): Promise<StoredJob[]> {\n return wrap('listing jobs', async () => {\n const query = vault.query('jobs');\n const entries =\n filter?.status === undefined ? await query.toArray() : await query.equals('status', filter.status).toArray();\n return entries.sort((a, b) => a.createdAt - b.createdAt).map(normalize);\n });\n },\n subscribe(listener: () => void): () => void {\n return vault.observe('jobs', () => listener(), { immediate: false });\n },\n transact<T>(fn: (tx: StoreTx) => Promise<T>): Promise<T> {\n return wrap('executing transaction', () => vault.batch(['jobs'], async (vaultTx) => fn(buildTx(vaultTx))));\n },\n async [Symbol.asyncDispose](): Promise<void> {\n await vault.dispose();\n },\n };\n}\n\nexport type { IndexedDbPostmasterStoreOptions };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"indexeddb.js","names":[],"sources":["../src/indexeddb.ts"],"sourcesContent":["import { isPlainObject } from '@vielzeug/arsenal';\nimport { table } from '@vielzeug/vault';\nimport { createIndexedDB } from '@vielzeug/vault/indexeddb';\nimport { normalize } from './_store-utils.ts';\nimport { PostmasterError } from './errors.ts';\nimport type {\n EntryFilter,\n EntryStatus,\n JsonValue,\n PostmasterStats,\n PostmasterStore,\n StoredFailure,\n StoredJob,\n StoreTx,\n} from './types.ts';\n\nconst CORRUPT = Symbol('corrupt');\n\ntype CorruptRecord = {\n readonly [CORRUPT]: CorruptStoredJob;\n readonly availableAt?: number;\n readonly id: string;\n readonly leaseExpiresAt?: number;\n readonly status?: EntryStatus;\n};\n\ntype DatabaseJob = CorruptRecord | StoredJob;\n\ntype PostmasterSchema = {\n jobs: ReturnType<typeof table<DatabaseJob, 'id'>>;\n};\n\nexport interface CorruptStoredJob {\n readonly id?: string;\n readonly reason: string;\n}\n\ntype IndexedDbPostmasterStoreOptions = {\n readonly name: string;\n readonly onCorruptRecord?: (record: CorruptStoredJob) => void;\n};\n\nconst ENTRY_STATUSES: readonly EntryStatus[] = ['dead-letter', 'queued', 'running'];\n\nfunction isEntryStatus(value: unknown): value is EntryStatus {\n return typeof value === 'string' && ENTRY_STATUSES.includes(value as EntryStatus);\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.length > 0;\n}\n\nfunction isNonNegativeInteger(value: unknown): value is number {\n return Number.isInteger(value) && (value as number) >= 0;\n}\n\nfunction isTimestamp(value: unknown): value is number {\n return Number.isSafeInteger(value) && (value as number) >= 0;\n}\n\nfunction isJsonValue(value: unknown, seen = new Set<object>()): value is JsonValue {\n if (value === null || typeof value === 'boolean' || typeof value === 'string') return true;\n if (typeof value === 'number') return Number.isFinite(value);\n if (typeof value !== 'object' || seen.has(value)) return false;\n\n seen.add(value);\n const valid = Array.isArray(value)\n ? value.every((item) => isJsonValue(item, seen))\n : isPlainObject(value) && Object.values(value).every((item) => isJsonValue(item, seen));\n seen.delete(value);\n return valid;\n}\n\nfunction isStoredFailure(value: unknown): value is StoredFailure {\n return (\n isPlainObject(value) &&\n typeof value.message === 'string' &&\n isNonEmptyString(value.name) &&\n isTimestamp(value.occurredAt)\n );\n}\n\n/**\n * Validates that persisted data conforms to the {@link StoredJob} shape before\n * it re-enters Postmaster's in-memory world. Replaces the prior no-op cast and\n * enforces Vault's required-codec boundary on durable records.\n */\nfunction parseStoredJob(value: unknown): StoredJob {\n if (!isPlainObject(value)) {\n throw new PostmasterError('invalid stored job: expected a plain object');\n }\n\n const {\n id,\n name,\n key,\n status,\n version,\n payload,\n attempts,\n availableAt,\n createdAt,\n updatedAt,\n failure,\n leaseExpiresAt,\n ownerId,\n } = value;\n\n if (!isNonEmptyString(id)) throw new PostmasterError('invalid stored job: id must be a non-empty string');\n if (!isNonEmptyString(name)) throw new PostmasterError('invalid stored job: name must be a non-empty string');\n if (!isNonEmptyString(key)) throw new PostmasterError('invalid stored job: key must be a non-empty string');\n if (!isEntryStatus(status)) {\n throw new PostmasterError('invalid stored job: status must be one of dead-letter, queued, running');\n }\n if (!isNonNegativeInteger(version) || version < 1) {\n throw new PostmasterError('invalid stored job: version must be a positive integer');\n }\n if (!isJsonValue(payload)) throw new PostmasterError('invalid stored job: payload must be a JSON value');\n if (!isNonNegativeInteger(attempts)) {\n throw new PostmasterError('invalid stored job: attempts must be a non-negative integer');\n }\n if (!isTimestamp(availableAt)) throw new PostmasterError('invalid stored job: availableAt must be a timestamp');\n if (!isTimestamp(createdAt)) throw new PostmasterError('invalid stored job: createdAt must be a timestamp');\n if (!isTimestamp(updatedAt)) throw new PostmasterError('invalid stored job: updatedAt must be a timestamp');\n if (failure !== undefined && !isStoredFailure(failure)) {\n throw new PostmasterError('invalid stored job: failure must be a StoredFailure');\n }\n\n if (status === 'running') {\n if (attempts < 1) throw new PostmasterError('invalid stored job: running jobs must have an attempt');\n if (!isTimestamp(leaseExpiresAt)) {\n throw new PostmasterError('invalid stored job: running jobs must have a lease expiry');\n }\n if (!isNonEmptyString(ownerId)) {\n throw new PostmasterError('invalid stored job: running jobs must have an owner');\n }\n if (failure !== undefined) throw new PostmasterError('invalid stored job: running jobs must not have a failure');\n } else if (leaseExpiresAt !== undefined || ownerId !== undefined) {\n throw new PostmasterError('invalid stored job: only running jobs may have lease ownership');\n }\n\n return {\n attempts,\n availableAt,\n createdAt,\n id,\n key,\n name,\n payload,\n status,\n updatedAt,\n version,\n ...(failure !== undefined ? { failure } : {}),\n ...(leaseExpiresAt !== undefined ? { leaseExpiresAt } : {}),\n ...(ownerId !== undefined ? { ownerId } : {}),\n };\n}\n\nfunction isValidRecord(record: DatabaseJob): record is StoredJob {\n return !(CORRUPT in record);\n}\n\nfunction wrap<T>(operation: string, fn: () => Promise<T>): Promise<T> {\n return fn().catch((cause: unknown) => {\n if (cause instanceof PostmasterError) throw cause;\n throw new PostmasterError(`${operation} failed`, { cause });\n });\n}\n\nexport function createIndexedDbPostmasterStore(options: IndexedDbPostmasterStoreOptions): PostmasterStore {\n const reported = new Set<string>();\n const decode = (value: unknown): DatabaseJob => {\n try {\n return parseStoredJob(value);\n } catch (cause) {\n const id = isPlainObject(value) && isNonEmptyString(value.id) ? value.id : undefined;\n const reason = cause instanceof Error ? cause.message : String(cause);\n const issue: CorruptStoredJob = { ...(id === undefined ? {} : { id }), reason };\n const signature = `${id ?? ''}\\u0000${reason}`;\n if (!reported.has(signature)) {\n reported.add(signature);\n try {\n options.onCorruptRecord?.(issue);\n } catch {}\n }\n return {\n [CORRUPT]: issue,\n id: id ?? '',\n ...(isPlainObject(value) && isTimestamp(value.availableAt) ? { availableAt: value.availableAt } : {}),\n ...(isPlainObject(value) && isTimestamp(value.leaseExpiresAt) ? { leaseExpiresAt: value.leaseExpiresAt } : {}),\n ...(isPlainObject(value) && isEntryStatus(value.status) ? { status: value.status } : {}),\n };\n }\n };\n const schema: PostmasterSchema = {\n jobs: table<DatabaseJob, 'id'>('id', {\n indexes: ['availableAt', 'leaseExpiresAt', 'status'],\n }),\n };\n const vault = createIndexedDB({\n codecs: {\n jobs: {\n decode,\n encode: (record) => {\n if (!isValidRecord(record)) throw new PostmasterError('cannot persist a corrupt stored job');\n return parseStoredJob(record);\n },\n },\n },\n name: options.name,\n schema,\n });\n\n const buildTx = (vaultTx: Parameters<Parameters<typeof vault.batch>[1]>[0]): StoreTx => ({\n async countByStatus(): Promise<PostmasterStats> {\n const entries = (await vaultTx.getAll('jobs')).filter(isValidRecord);\n let queued = 0;\n let running = 0;\n let deadLetter = 0;\n for (const entry of entries) {\n if (entry.status === 'queued') queued += 1;\n else if (entry.status === 'running') running += 1;\n else if (entry.status === 'dead-letter') deadLetter += 1;\n }\n return { deadLetter, queued, running };\n },\n async delete(id: string): Promise<void> {\n await vaultTx.delete('jobs', id);\n },\n async findClaimable(now: number): Promise<StoredJob | undefined> {\n const entries = (await vaultTx.getAll('jobs')).filter(isValidRecord);\n const candidates = entries.filter(\n (entry) =>\n (entry.status === 'queued' && entry.availableAt <= now) ||\n (entry.status === 'running' && (entry.leaseExpiresAt ?? 0) <= now),\n );\n candidates.sort((a, b) => a.availableAt - b.availableAt || a.createdAt - b.createdAt);\n return candidates[0] ? normalize(candidates[0]) : undefined;\n },\n async findNextWake(now: number): Promise<number | undefined> {\n const entries = (await vaultTx.getAll('jobs')).filter(isValidRecord);\n let next: number | undefined;\n for (const entry of entries) {\n let wake: number | undefined;\n if (entry.status === 'queued' && entry.availableAt > now) wake = entry.availableAt;\n else if (entry.status === 'running' && (entry.leaseExpiresAt ?? 0) > now) wake = entry.leaseExpiresAt;\n if (wake !== undefined && (next === undefined || wake < next)) next = wake;\n }\n return next;\n },\n async get(id: string): Promise<StoredJob | undefined> {\n const entry = await vaultTx.get('jobs', id);\n return entry && isValidRecord(entry) ? normalize(entry) : undefined;\n },\n async put(entry: StoredJob): Promise<void> {\n await vaultTx.put('jobs', normalize(entry));\n },\n });\n\n return {\n get disposalSignal() {\n return vault.disposalSignal;\n },\n dispose(): Promise<void> {\n return wrap('disposing IndexedDB store', () => vault.dispose());\n },\n get disposed() {\n return vault.disposed;\n },\n list(filter?: EntryFilter): Promise<StoredJob[]> {\n return wrap('listing jobs', async () => {\n const entries =\n filter?.status === undefined\n ? await vault.getAll('jobs')\n : await vault.getAllByIndex('jobs', 'status', filter.status);\n return entries\n .filter(isValidRecord)\n .sort((a, b) => a.createdAt - b.createdAt)\n .map(normalize);\n });\n },\n subscribe(listener: () => void): () => void {\n return vault.observe('jobs', () => listener(), { immediate: false });\n },\n transact<T>(fn: (tx: StoreTx) => Promise<T>): Promise<T> {\n return wrap('executing transaction', () => vault.batch(['jobs'], async (vaultTx) => fn(buildTx(vaultTx))));\n },\n async [Symbol.asyncDispose](): Promise<void> {\n await vault.dispose();\n },\n };\n}\n\nexport type { IndexedDbPostmasterStoreOptions };\n"],"mappings":"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"}
|
package/dist/postmaster.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@vielzeug/arsenal");var t=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},n=class extends t{},r=class extends t{};function i(e,n){if(e&&(!Number.isInteger(e.maxAttempts)||e.maxAttempts<1))throw new t(`job "${n}" retry.maxAttempts must be a positive integer`)}function a(e,n){let r=n.migrate;if(!r)return;let i=Object.keys(r);if(i.length===0)throw new t(`job "${e}" migrations must not be empty`);let a=i.map(i=>{let a=Number(i);if(!Number.isInteger(a)||String(a)!==i)throw new t(`job "${e}" has invalid migration key "${i}"`);if(a<1||a>=n.version)throw new t(`job "${e}" has invalid migration key "${a}" (valid range: 1..${n.version-1})`);if(typeof r[a]!=`function`)throw new t(`job "${e}" migration from version ${a} must be a function`);return a}).sort((e,t)=>e-t);for(let n=1;n<a.length;n++)if(a[n]!==a[n-1]+1)throw new t(`job "${e}" missing migration from version ${a[n-1]+1}`);if(a.at(-1)!==n.version-1)throw new t(`job "${e}" migrations must lead into version ${n.version}`)}function o(e,n){if(!Number.isInteger(n.version)||n.version<1)throw new t(`job "${e}" version must be a positive integer`);if(typeof n.execute!=`function`||typeof n.key!=`function`)throw new t(`job "${e}" must define execute and key`);i(n.retry,e),a(e,n)}function s(e){for(let[t,n]of Object.entries(e))o(t,n);return e}function c(e,t){return e?typeof e==`function`?e(t):e.parse(t):t}function l(e){let n;try{n=JSON.stringify(e)}catch(e){throw new t(`job payload must be JSON-serializable`,{cause:e})}if(n===void 0)throw new t(`job payload must be JSON-serializable`);return JSON.parse(n)}function u(e,t){return e instanceof Error?{message:e.message,name:e.name,occurredAt:t}:{message:String(e),name:`Error`,occurredAt:t}}function d(e){return e instanceof DOMException&&e.name===`AbortError`}async function f(e,t){let n=await e.findClaimable(t.now);if(!n)return;let r={...n,attempts:n.attempts+1,failure:void 0,leaseExpiresAt:t.now+t.leaseDuration,ownerId:t.ownerId,status:`running`,updatedAt:t.now};return await e.put(r),r}async function p(e,t){let n=await e.get(t.id);return!n||n.status!==`running`||n.ownerId!==t.ownerId?!1:(await e.delete(t.id),!0)}async function m(e,t){let n=await e.get(t.id);return!n||n.status!==`running`||n.ownerId!==t.ownerId?!1:(await e.put({...n,leaseExpiresAt:void 0,ownerId:void 0,status:`queued`,updatedAt:t.now}),!0)}async function h(e,t){let n=await e.get(t.id);return!n||n.status!==`running`||n.ownerId!==t.ownerId?!1:(await e.put({...n,availableAt:t.availableAt,failure:t.failure,leaseExpiresAt:void 0,ownerId:void 0,status:`queued`,updatedAt:t.now}),!0)}async function g(e,t){let n=await e.get(t.id);return!n||n.status!==`running`||n.ownerId!==t.ownerId?!1:(await e.put({...n,failure:t.failure,leaseExpiresAt:void 0,ownerId:void 0,status:`dead-letter`,updatedAt:t.now}),!0)}async function _(e,t){let n=await e.get(t.id);return!n||n.status!==`running`||n.ownerId!==t.ownerId?!1:(await e.put({...n,leaseExpiresAt:t.leaseExpiresAt,updatedAt:t.now}),!0)}async function v(e,t,n){let r=await e.get(t);if(!r)return{status:`not-found`};if(r.status===`running`)return{status:`running`};if(r.status!==`dead-letter`)return{status:`not-dead-letter`};let i={...r,availableAt:n,failure:void 0,status:`queued`,updatedAt:n};return await e.put(i),{entry:x(i),status:`retried`}}async function y(e,t){let n=await e.get(t);return n?n.status===`running`?{status:`running`}:(await e.delete(t),{id:t,status:`removed`}):{status:`not-found`}}async function b(e,t){if(await e.get(t.id))throw new r(`Postmaster entry "${t.id}" already exists`);await e.put(t)}function x(e){return{attempts:e.attempts,availableAt:e.availableAt,createdAt:e.createdAt,...e.failure?{failure:e.failure}:{},id:e.id,key:e.key,name:e.name,status:e.status,updatedAt:e.updatedAt,version:e.version}}var S=()=>({completed:0,deadLettered:0,processed:0,retryScheduled:0});function C(e){if(!Number.isInteger(e)||e<1e3)throw new t(`leaseDuration must be a positive integer of at least 1000ms`)}function w(e,n){if(e===void 0)return n;if(!Number.isFinite(e)||e<0||!Number.isSafeInteger(e))throw new t(`availableAt must be a finite non-negative safe integer`);return e}function T(i){let{clock:a=Date.now,jobs:o,leaseDuration:s=3e4,signal:T,store:E}=i;C(s);let D=new AbortController,O=crypto.randomUUID(),k=new Set,A=!1,j=!1,M,N,P=new Map,F,I=e=>{let t=e instanceof Error?e:Error(String(e));L({error:t,type:`processor-error`})},L=e=>{if(k.size!==0)for(let t of k)try{t(e)}catch{}},R=()=>{if(A)throw new n(`Postmaster is disposed`)},z=()=>{N!==void 0&&clearTimeout(N),N=void 0},B=()=>{z(),j&&!A&&K()},V=async()=>{if(!j||A)return;let e=await E.transact(e=>e.findNextWake(a()));if(e===void 0)return;let t=Math.max(0,Math.min(e-a(),2147483647));N=setTimeout(B,t)},H=e=>{let t=o[e.name];if(!t)throw new r(`no job definition is registered for "${e.name}"`);return t},U=async(e,t)=>await E.transact(n=>g(n,{failure:u(t,a()),id:e.id,now:a(),ownerId:O}))?(L({entry:x({...e,status:`dead-letter`}),type:`dead-lettered`}),{completed:0,deadLettered:1,processed:!0,retryScheduled:0}):(L({id:e.id,type:`lease-lost`}),{completed:0,deadLettered:0,processed:!0,retryScheduled:0}),W=async(n,i)=>{let o,l;try{if(o=H(n),n.version>o.version)throw new r(`job "${n.name}" version ${n.version} is newer than the registered version`);let e=n.payload;if(n.version<o.version){let t=o.migrate;if(!t)throw new r(`job "${n.name}" has no migrations`);for(let i=n.version;i<o.version;i++){let a=t[i];if(typeof a!=`function`)throw new r(`job "${n.name}" does not support stored version ${n.version}`);if(e=a(e),e===void 0)throw new r(`job "${n.name}" migration from version ${i} returned undefined`)}}l=c(o.validate,e)}catch(e){return U(n,e)}let f=new AbortController,g=AbortSignal.any([D.signal,i,f.signal].filter(e=>e!==void 0)),v=setInterval(()=>{E.transact(e=>_(e,{id:n.id,leaseExpiresAt:a()+s,now:a(),ownerId:O})).then(e=>{e||(f.abort(),L({id:n.id,type:`lease-lost`}))}).catch(I)},Math.max(1e3,Math.floor(s/2)));L({entry:x(n),type:`started`});try{return await o.execute(l,{attempt:n.attempts,entryId:n.id,key:n.key,signal:g}),g.aborted?(await E.transact(e=>m(e,{id:n.id,now:a(),ownerId:O})),{completed:0,deadLettered:0,processed:!0,retryScheduled:0}):await E.transact(e=>p(e,{id:n.id,now:a(),ownerId:O}))?(L({entry:x(n),type:`completed`}),{completed:1,deadLettered:0,processed:!0,retryScheduled:0}):(L({id:n.id,type:`lease-lost`}),{completed:0,deadLettered:0,processed:!0,retryScheduled:0})}catch(r){if(g.aborted||d(r))return await E.transact(e=>m(e,{id:n.id,now:a(),ownerId:O})),{completed:0,deadLettered:0,processed:!0,retryScheduled:0};let i=!1,s=0;try{if(i=n.attempts<(o.retry?.maxAttempts??1)&&(o.retry?.shouldRetry(r,n.attempts)??!1),!i)return U(n,r);if(s=o.retry?.delay?.(n.attempts-1)??(0,e.backoff)(n.attempts-1),!Number.isFinite(s)||s<0)throw new t(`job "${n.name}" retry delay must be finite and non-negative`)}catch(e){return U(n,e)}let c=a()+s;return await E.transact(e=>h(e,{availableAt:c,failure:u(r,a()),id:n.id,now:a(),ownerId:O}))?(L({entry:x({...n,availableAt:c,status:`queued`}),type:`retry-scheduled`}),{completed:0,deadLettered:0,processed:!0,retryScheduled:1}):(L({id:n.id,type:`lease-lost`}),{completed:0,deadLettered:0,processed:!0,retryScheduled:0})}finally{clearInterval(v)}},G=async e=>{if(e?.aborted)throw new DOMException(`The operation was aborted`,`AbortError`);if(D.signal.aborted)return{completed:0,deadLettered:0,processed:!1,retryScheduled:0};let t=await E.transact(e=>f(e,{leaseDuration:s,now:a(),ownerId:O}));if(!t)return{completed:0,deadLettered:0,processed:!1,retryScheduled:0};P.set(t.id,t);try{return await W(t,e)}finally{P.delete(t.id)}},K=()=>{M||A||(M=(async()=>{for(;(await G()).processed&&!A;);await V()})().catch(I).finally(()=>{M=void 0}))},q=E.subscribe(B),J=()=>{Y()};T?.addEventListener(`abort`,J,{once:!0});let Y=async()=>{if(A)return;A=!0,L({type:`dispose`}),k.clear(),z(),T?.removeEventListener(`abort`,J),q(),D.abort();let e=[...P.values()];P.clear(),await Promise.all(e.map(e=>E.transact(t=>m(t,{id:e.id,now:a(),ownerId:O}))))};return{get disposalSignal(){return D.signal},async dispose(){await Y()},get disposed(){return A},async enqueue(e,n,i){R();let s=o[e];if(!s)throw new r(`no job definition is registered for "${e}"`);let u=a(),d=w(i?.availableAt,u),f=c(s.validate,n),p=s.key(f);if(typeof p!=`string`||p.length===0)throw new t(`job "${e}" returned an empty key`);let m={attempts:0,availableAt:d,createdAt:u,id:crypto.randomUUID(),key:p,name:e,payload:l(f),status:`queued`,updatedAt:u,version:s.version};await E.transact(e=>b(e,m));let h=x(m);return L({entry:h,type:`enqueued`}),B(),h},flush(e){if(R(),F)return F;z();let t=(async()=>{await M;let t=S();try{for(;;){let n=await G(e?.signal);if(!n.processed||(t.processed+=1,t.completed+=n.completed,t.deadLettered+=n.deadLettered,t.retryScheduled+=n.retryScheduled,A))return t}}finally{F=void 0,B()}})();return F=t,t},async list(e){return R(),(await E.list(e)).map(x)},async remove(e){R();let t=await E.transact(t=>y(t,e));return t.status===`removed`&&L({id:e,type:`removed`}),t},async retry(e){R();let t=await E.transact(t=>v(t,e,a()));return t.status===`retried`&&B(),t},start(){R(),!j&&(j=!0,B())},async stats(){return R(),E.transact(e=>e.countByStatus())},tap(e,t){if(R(),k.add(e),t?.signal){if(t.signal.aborted)return k.delete(e),()=>{};let n=()=>k.delete(e);return t.signal.addEventListener(`abort`,n,{once:!0}),()=>{k.delete(e),t.signal?.removeEventListener(`abort`,n)}}return()=>k.delete(e)},async[Symbol.asyncDispose](){await Y()}}}exports.PostmasterDisposedError=n,exports.PostmasterError=t,exports.PostmasterJobError=r,exports.createPostmaster=T,exports.defineJobs=s;
|
|
2
2
|
//# sourceMappingURL=postmaster.cjs.map
|
package/dist/postmaster.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postmaster.cjs","names":[],"sources":["../src/errors.ts","../src/definitions.ts","../../arsenal/dist/async/backoff.js","../src/_json.ts","../src/store-ops.ts","../src/postmaster.ts"],"sourcesContent":["export class PostmasterError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class PostmasterDisposedError extends PostmasterError {}\nexport class PostmasterJobError extends PostmasterError {}\n","import { PostmasterError } from './errors.ts';\nimport type { JobDefinition, JobDefinitions, RetryPolicy } from './types.ts';\n\nfunction validateRetry(retry: RetryPolicy | undefined, name: string): void {\n if (!retry) return;\n\n if (!Number.isInteger(retry.maxAttempts) || retry.maxAttempts < 1) {\n throw new PostmasterError(`job \"${name}\" retry.maxAttempts must be a positive integer`);\n }\n}\n\nfunction validateJob(name: string, job: JobDefinition<unknown>): void {\n if (!Number.isInteger(job.version) || job.version < 1) {\n throw new PostmasterError(`job \"${name}\" version must be a positive integer`);\n }\n\n if (typeof job.execute !== 'function' || typeof job.key !== 'function') {\n throw new PostmasterError(`job \"${name}\" must define execute and key`);\n }\n\n validateRetry(job.retry, name);\n}\n\nexport function defineJobs<const J extends JobDefinitions>(jobs: J): J {\n for (const [name, job] of Object.entries(jobs)) validateJob(name, job);\n\n return jobs;\n}\n","//#region src/async/backoff.ts\nfunction e(e, t = 3e4) {\n\treturn Math.min(1e3 * 2 ** Math.max(0, Number.isNaN(e) ? 0 : Math.floor(e)), t);\n}\n//#endregion\nexport { e as backoff };\n\n//# sourceMappingURL=backoff.js.map","import { PostmasterError } from './errors.ts';\nimport type { JsonValue, StoredFailure, Validate } from './types.ts';\n\nexport function runValidate<T>(validate: Validate<T> | undefined, value: unknown): T {\n if (!validate) return value as T;\n if (typeof validate === 'function') return validate(value);\n return validate.parse(value);\n}\n\nexport function asJsonValue(value: unknown): JsonValue {\n let serialized: string | undefined;\n\n try {\n serialized = JSON.stringify(value);\n } catch (error) {\n throw new PostmasterError('job payload must be JSON-serializable', { cause: error });\n }\n\n if (serialized === undefined) {\n throw new PostmasterError('job payload must be JSON-serializable');\n }\n\n return JSON.parse(serialized) as JsonValue;\n}\n\nexport function failureFrom(error: unknown, now: number): StoredFailure {\n if (error instanceof Error) {\n return { message: error.message, name: error.name, occurredAt: now };\n }\n\n return { message: String(error), name: 'Error', occurredAt: now };\n}\n\nexport function isAbortError(error: unknown): boolean {\n return error instanceof DOMException && error.name === 'AbortError';\n}\n","import type { PostmasterEntry, RemoveResult, RetryResult, StoredFailure, StoredJob, StoreTx } from './types.ts';\n\nexport interface ClaimParams {\n readonly leaseDuration: number;\n readonly now: number;\n readonly ownerId: string;\n}\n\nexport interface OwnedParams {\n readonly id: string;\n readonly now: number;\n readonly ownerId: string;\n}\n\nexport interface RescheduleParams extends OwnedParams {\n readonly availableAt: number;\n readonly failure: StoredFailure;\n}\n\nexport interface DeadLetterParams extends OwnedParams {\n readonly failure: StoredFailure;\n}\n\n/** Claim the earliest eligible job atomically. Returns the claimed entry or undefined. */\nexport async function claimJob(tx: StoreTx, params: ClaimParams): Promise<StoredJob | undefined> {\n const entry = await tx.findClaimable(params.now);\n if (!entry) return undefined;\n const claimed: StoredJob = {\n ...entry,\n attempts: entry.attempts + 1,\n failure: undefined,\n leaseExpiresAt: params.now + params.leaseDuration,\n ownerId: params.ownerId,\n status: 'running',\n updatedAt: params.now,\n };\n await tx.put(claimed);\n return claimed;\n}\n\n/** Complete and delete a job. Returns false if the owner is stale or the job is gone. */\nexport async function completeJob(tx: StoreTx, params: OwnedParams): Promise<boolean> {\n const entry = await tx.get(params.id);\n if (!entry || entry.status !== 'running' || entry.ownerId !== params.ownerId) return false;\n await tx.delete(params.id);\n return true;\n}\n\n/** Release a claimed job back to queued without consuming an attempt. Returns false if stale. */\nexport async function releaseJob(tx: StoreTx, params: OwnedParams): Promise<boolean> {\n const entry = await tx.get(params.id);\n if (!entry || entry.status !== 'running' || entry.ownerId !== params.ownerId) return false;\n await tx.put({\n ...entry,\n leaseExpiresAt: undefined,\n ownerId: undefined,\n status: 'queued',\n updatedAt: params.now,\n });\n return true;\n}\n\n/** Reschedule a running job back to queued with a future availableAt. Returns false if stale. */\nexport async function rescheduleJob(tx: StoreTx, params: RescheduleParams): Promise<boolean> {\n const entry = await tx.get(params.id);\n if (!entry || entry.status !== 'running' || entry.ownerId !== params.ownerId) return false;\n await tx.put({\n ...entry,\n availableAt: params.availableAt,\n failure: params.failure,\n leaseExpiresAt: undefined,\n ownerId: undefined,\n status: 'queued',\n updatedAt: params.now,\n });\n return true;\n}\n\n/** Move a running job to dead-letter. Returns false if stale. */\nexport async function deadLetterJob(tx: StoreTx, params: DeadLetterParams): Promise<boolean> {\n const entry = await tx.get(params.id);\n if (!entry || entry.status !== 'running' || entry.ownerId !== params.ownerId) return false;\n await tx.put({\n ...entry,\n failure: params.failure,\n leaseExpiresAt: undefined,\n ownerId: undefined,\n status: 'dead-letter',\n updatedAt: params.now,\n });\n return true;\n}\n\n/** Renew a lease for the current owner. Returns false if stale. */\nexport async function renewLeaseJob(tx: StoreTx, params: OwnedParams & { leaseExpiresAt: number }): Promise<boolean> {\n const entry = await tx.get(params.id);\n if (!entry || entry.status !== 'running' || entry.ownerId !== params.ownerId) return false;\n await tx.put({ ...entry, leaseExpiresAt: params.leaseExpiresAt, updatedAt: params.now });\n return true;\n}\n\n/** Retry a dead-letter job back to queued. Returns discriminated result. */\nexport async function retryJob(tx: StoreTx, id: string, now: number): Promise<RetryResult> {\n const entry = await tx.get(id);\n if (!entry) return { status: 'not-found' };\n if (entry.status === 'running') return { status: 'running' };\n if (entry.status !== 'dead-letter') return { status: 'not-dead-letter' };\n const retried: StoredJob = {\n ...entry,\n availableAt: now,\n failure: undefined,\n status: 'queued',\n updatedAt: now,\n };\n await tx.put(retried);\n return { entry: toEntry(retried), status: 'retried' };\n}\n\n/** Remove a queued or dead-letter job. Running jobs cannot be removed. */\nexport async function removeJob(tx: StoreTx, id: string): Promise<RemoveResult> {\n const entry = await tx.get(id);\n if (!entry) return { status: 'not-found' };\n if (entry.status === 'running') return { status: 'running' };\n await tx.delete(id);\n return { id, status: 'removed' };\n}\n\n/** Enqueue a new job. Rejects duplicate ids. */\nexport async function enqueueJob(tx: StoreTx, entry: StoredJob): Promise<void> {\n const existing = await tx.get(entry.id);\n if (existing) throw new Error(`Postmaster entry \"${entry.id}\" already exists`);\n await tx.put(entry);\n}\n\nexport function toEntry(entry: StoredJob): PostmasterEntry {\n return {\n attempts: entry.attempts,\n availableAt: entry.availableAt,\n createdAt: entry.createdAt,\n ...(entry.failure ? { failure: entry.failure } : {}),\n id: entry.id,\n key: entry.key,\n name: entry.name,\n status: entry.status,\n updatedAt: entry.updatedAt,\n version: entry.version,\n };\n}\n","import { backoff } from '@vielzeug/arsenal/async';\nimport { asJsonValue, failureFrom, isAbortError, runValidate } from './_json.ts';\nimport { PostmasterDisposedError, PostmasterError, PostmasterJobError } from './errors.ts';\nimport {\n claimJob,\n completeJob,\n deadLetterJob,\n enqueueJob,\n releaseJob,\n removeJob,\n renewLeaseJob,\n rescheduleJob,\n retryJob,\n toEntry,\n} from './store-ops.ts';\nimport type {\n CreatePostmasterOptions,\n EnqueueOptions,\n EntryFilter,\n FlushResult,\n InferJobPayload,\n JobContext,\n JobDefinition,\n JobDefinitions,\n Postmaster,\n PostmasterEntry,\n PostmasterEvent,\n PostmasterStats,\n RemoveResult,\n RetryResult,\n StoredJob,\n} from './types.ts';\n\ntype ExecutionResult = Omit<FlushResult, 'processed'> & { processed: boolean };\ntype MutableFlushResult = { -readonly [K in keyof FlushResult]: FlushResult[K] };\n\nconst emptyResult = (): FlushResult => ({ completed: 0, deadLettered: 0, processed: 0, retryScheduled: 0 });\n\nfunction mergeSignals(signals: readonly (AbortSignal | undefined)[]): AbortSignal {\n const controller = new AbortController();\n const active = signals.filter((signal): signal is AbortSignal => signal !== undefined);\n const abort = (): void => controller.abort();\n\n for (const signal of active) {\n if (signal.aborted) {\n abort();\n break;\n }\n signal.addEventListener('abort', abort, { once: true });\n }\n\n return controller.signal;\n}\n\nfunction assertLeaseDuration(value: number): void {\n if (!Number.isInteger(value) || value < 1_000) {\n throw new PostmasterError('leaseDuration must be a positive integer of at least 1000ms');\n }\n}\n\nfunction assertAvailableAt(value: number | undefined, defaultValue: number): number {\n if (value === undefined) return defaultValue;\n if (!Number.isFinite(value) || value < 0 || !Number.isSafeInteger(value)) {\n throw new PostmasterError('availableAt must be a finite non-negative safe integer');\n }\n return value;\n}\n\nexport function createPostmaster<J extends JobDefinitions>(options: CreatePostmasterOptions<J>): Postmaster<J> {\n const { clock = Date.now, jobs, leaseDuration = 30_000, signal, store } = options;\n assertLeaseDuration(leaseDuration);\n\n const controller = new AbortController();\n const ownerId = crypto.randomUUID();\n const tappers = new Set<(event: PostmasterEvent) => void>();\n let disposed = false;\n let started = false;\n let running: Promise<void> | undefined;\n let wakeTimer: ReturnType<typeof setTimeout> | undefined;\n const activeEntries = new Map<string, StoredJob>();\n let flushPromise: Promise<FlushResult> | undefined;\n\n const error = (reason: unknown): void => {\n const value = reason instanceof Error ? reason : new Error(String(reason));\n emitTap({ error: value, type: 'processor-error' });\n queueMicrotask(() => {\n throw value;\n });\n };\n\n const emitTap = (event: PostmasterEvent): void => {\n if (tappers.size === 0) return;\n for (const tapper of tappers) {\n try {\n tapper(event);\n } catch {\n // Observability must not affect postmaster behavior.\n }\n }\n };\n\n const assertLive = (): void => {\n if (disposed) throw new PostmasterDisposedError('Postmaster is disposed');\n };\n\n const clearWakeTimer = (): void => {\n if (wakeTimer !== undefined) clearTimeout(wakeTimer);\n wakeTimer = undefined;\n };\n\n const wake = (): void => {\n clearWakeTimer();\n if (started && !disposed) pump();\n };\n\n const schedule = async (): Promise<void> => {\n if (!started || disposed) return;\n const next = await store.transact((tx) => tx.findNextWake(clock()));\n if (next === undefined) return;\n const delay = Math.max(0, Math.min(next - clock(), 2_147_483_647));\n wakeTimer = setTimeout(wake, delay);\n };\n\n const resolveJob = (entry: StoredJob): JobDefinition<unknown> => {\n const job = jobs[entry.name];\n if (!job) throw new PostmasterJobError(`no job definition is registered for \"${entry.name}\"`);\n return job;\n };\n\n const deadLetter = async (entry: StoredJob, reason: unknown): Promise<ExecutionResult> => {\n const finalized = await store.transact((tx) =>\n deadLetterJob(tx, { failure: failureFrom(reason, clock()), id: entry.id, now: clock(), ownerId }),\n );\n\n if (!finalized) {\n emitTap({ id: entry.id, type: 'lease-lost' });\n return { completed: 0, deadLettered: 0, processed: true, retryScheduled: 0 };\n }\n\n emitTap({ entry: toEntry({ ...entry, status: 'dead-letter' }), type: 'dead-lettered' });\n return { completed: 0, deadLettered: 1, processed: true, retryScheduled: 0 };\n };\n\n const execute = async (entry: StoredJob, externalSignal?: AbortSignal): Promise<ExecutionResult> => {\n let job: JobDefinition<unknown>;\n let payload: unknown;\n\n try {\n job = resolveJob(entry);\n if (entry.version > job.version) {\n throw new PostmasterJobError(\n `job \"${entry.name}\" version ${entry.version} is newer than the registered version`,\n );\n }\n const raw = entry.version === job.version ? entry.payload : job.migrate?.(entry.payload, entry.version);\n if (raw === undefined) {\n throw new PostmasterJobError(`job \"${entry.name}\" version ${entry.version} cannot be migrated`);\n }\n payload = runValidate(job.validate, raw);\n } catch (reason) {\n return deadLetter(entry, reason);\n }\n\n const taskController = new AbortController();\n const taskSignal = mergeSignals([controller.signal, externalSignal, taskController.signal]);\n const heartbeat = setInterval(\n () => {\n void store\n .transact((tx) =>\n renewLeaseJob(tx, { id: entry.id, leaseExpiresAt: clock() + leaseDuration, now: clock(), ownerId }),\n )\n .then((renewed) => {\n if (!renewed) {\n taskController.abort();\n emitTap({ id: entry.id, type: 'lease-lost' });\n }\n })\n .catch(error);\n },\n Math.max(1_000, Math.floor(leaseDuration / 2)),\n );\n\n emitTap({ entry: toEntry(entry), type: 'started' });\n\n try {\n await job.execute(payload, {\n attempt: entry.attempts,\n entryId: entry.id,\n key: entry.key,\n signal: taskSignal,\n } as JobContext);\n\n if (taskSignal.aborted) {\n await store.transact((tx) => releaseJob(tx, { id: entry.id, now: clock(), ownerId }));\n return { completed: 0, deadLettered: 0, processed: true, retryScheduled: 0 };\n }\n\n const completed = await store.transact((tx) => completeJob(tx, { id: entry.id, now: clock(), ownerId }));\n if (!completed) {\n emitTap({ id: entry.id, type: 'lease-lost' });\n return { completed: 0, deadLettered: 0, processed: true, retryScheduled: 0 };\n }\n\n emitTap({ entry: toEntry(entry), type: 'completed' });\n return { completed: 1, deadLettered: 0, processed: true, retryScheduled: 0 };\n } catch (reason) {\n if (taskSignal.aborted || isAbortError(reason)) {\n await store.transact((tx) => releaseJob(tx, { id: entry.id, now: clock(), ownerId }));\n return { completed: 0, deadLettered: 0, processed: true, retryScheduled: 0 };\n }\n\n let shouldRetry = false;\n let delay = 0;\n\n try {\n shouldRetry =\n entry.attempts < (job.retry?.maxAttempts ?? 1) && (job.retry?.shouldRetry(reason, entry.attempts) ?? false);\n if (!shouldRetry) return deadLetter(entry, reason);\n delay = job.retry?.delay?.(entry.attempts - 1) ?? backoff(entry.attempts - 1);\n if (!Number.isFinite(delay) || delay < 0) {\n throw new PostmasterError(`job \"${entry.name}\" retry delay must be finite and non-negative`);\n }\n } catch (retryReason) {\n return deadLetter(entry, retryReason);\n }\n\n const availableAt = clock() + delay;\n const rescheduled = await store.transact((tx) =>\n rescheduleJob(tx, { availableAt, failure: failureFrom(reason, clock()), id: entry.id, now: clock(), ownerId }),\n );\n if (!rescheduled) {\n emitTap({ id: entry.id, type: 'lease-lost' });\n return { completed: 0, deadLettered: 0, processed: true, retryScheduled: 0 };\n }\n emitTap({ entry: toEntry({ ...entry, availableAt, status: 'queued' }), type: 'retry-scheduled' });\n return { completed: 0, deadLettered: 0, processed: true, retryScheduled: 1 };\n } finally {\n clearInterval(heartbeat);\n }\n };\n\n const processNext = async (externalSignal?: AbortSignal): Promise<ExecutionResult> => {\n if (externalSignal?.aborted) throw new DOMException('The operation was aborted', 'AbortError');\n if (controller.signal.aborted) return { completed: 0, deadLettered: 0, processed: false, retryScheduled: 0 };\n const entry = await store.transact((tx) => claimJob(tx, { leaseDuration, now: clock(), ownerId }));\n if (!entry) return { completed: 0, deadLettered: 0, processed: false, retryScheduled: 0 };\n activeEntries.set(entry.id, entry);\n try {\n return await execute(entry, externalSignal);\n } finally {\n activeEntries.delete(entry.id);\n }\n };\n\n const pump = (): void => {\n if (running || disposed) return;\n running = (async () => {\n for (;;) {\n const result = await processNext();\n if (!result.processed || disposed) break;\n }\n await schedule();\n })()\n .catch(error)\n .finally(() => {\n running = undefined;\n });\n };\n\n const unsubscribeStore = store.subscribe(wake);\n const externalAbort = (): void => {\n void dispose();\n };\n signal?.addEventListener('abort', externalAbort, { once: true });\n\n const dispose = async (): Promise<void> => {\n if (disposed) return;\n disposed = true;\n emitTap({ type: 'dispose' });\n tappers.clear();\n clearWakeTimer();\n signal?.removeEventListener('abort', externalAbort);\n unsubscribeStore();\n controller.abort();\n const entries = [...activeEntries.values()];\n activeEntries.clear();\n await Promise.all(\n entries.map((entry) => store.transact((tx) => releaseJob(tx, { id: entry.id, now: clock(), ownerId }))),\n );\n };\n\n return {\n get disposalSignal() {\n return controller.signal;\n },\n async dispose(): Promise<void> {\n await dispose();\n },\n get disposed() {\n return disposed;\n },\n async enqueue<K extends keyof J & string>(\n name: K,\n payload: InferJobPayload<J[K]>,\n options?: EnqueueOptions,\n ): Promise<PostmasterEntry> {\n assertLive();\n const job = jobs[name];\n if (!job) throw new PostmasterJobError(`no job definition is registered for \"${name}\"`);\n const now = clock();\n const availableAt = assertAvailableAt(options?.availableAt, now);\n const parsed = runValidate(job.validate, payload);\n const key = job.key(parsed);\n if (typeof key !== 'string' || key.length === 0) {\n throw new PostmasterError(`job \"${name}\" returned an empty key`);\n }\n const entry: StoredJob = {\n attempts: 0,\n availableAt,\n createdAt: now,\n id: crypto.randomUUID(),\n key,\n name,\n payload: asJsonValue(parsed),\n status: 'queued',\n updatedAt: now,\n version: job.version,\n };\n await store.transact((tx) => enqueueJob(tx, entry));\n const result = toEntry(entry);\n emitTap({ entry: result, type: 'enqueued' });\n wake();\n return result;\n },\n flush(options?: { signal?: AbortSignal }): Promise<FlushResult> {\n assertLive();\n if (flushPromise) return flushPromise;\n clearWakeTimer();\n const promise = (async (): Promise<FlushResult> => {\n await running;\n const result: MutableFlushResult = emptyResult();\n try {\n for (;;) {\n const next = await processNext(options?.signal);\n if (!next.processed) return result;\n result.processed += 1;\n result.completed += next.completed;\n result.deadLettered += next.deadLettered;\n result.retryScheduled += next.retryScheduled;\n if (disposed) return result;\n }\n } finally {\n flushPromise = undefined;\n wake();\n }\n })();\n flushPromise = promise;\n return promise;\n },\n async list(filter?: EntryFilter): Promise<PostmasterEntry[]> {\n assertLive();\n return (await store.list(filter)).map(toEntry);\n },\n async remove(id: string): Promise<RemoveResult> {\n assertLive();\n const result = await store.transact((tx) => removeJob(tx, id));\n if (result.status === 'removed') emitTap({ id, type: 'removed' });\n return result;\n },\n async retry(id: string): Promise<RetryResult> {\n assertLive();\n const result = await store.transact((tx) => retryJob(tx, id, clock()));\n if (result.status === 'retried') wake();\n return result;\n },\n async start(): Promise<void> {\n assertLive();\n if (started) return;\n started = true;\n wake();\n },\n async stats(): Promise<PostmasterStats> {\n assertLive();\n return store.transact((tx) => tx.countByStatus());\n },\n tap(handler: (event: PostmasterEvent) => void, opts?: { readonly signal?: AbortSignal }): () => void {\n assertLive();\n tappers.add(handler);\n\n if (opts?.signal) {\n if (opts.signal.aborted) {\n tappers.delete(handler);\n return () => {};\n }\n const onAbort = () => tappers.delete(handler);\n opts.signal.addEventListener('abort', onAbort, { once: true });\n return () => {\n tappers.delete(handler);\n opts.signal?.removeEventListener('abort', onAbort);\n };\n }\n\n return () => tappers.delete(handler);\n },\n async [Symbol.asyncDispose](): Promise<void> {\n await dispose();\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"postmaster.cjs","names":[],"sources":["../src/errors.ts","../src/definitions.ts","../src/_json.ts","../src/store-ops.ts","../src/postmaster.ts"],"sourcesContent":["export class PostmasterError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class PostmasterDisposedError extends PostmasterError {}\nexport class PostmasterJobError extends PostmasterError {}\n","import { PostmasterError } from './errors.ts';\nimport type { JobDefinition, JobDefinitions, RetryPolicy } from './types.ts';\n\nfunction validateRetry(retry: RetryPolicy | undefined, name: string): void {\n if (!retry) return;\n\n if (!Number.isInteger(retry.maxAttempts) || retry.maxAttempts < 1) {\n throw new PostmasterError(`job \"${name}\" retry.maxAttempts must be a positive integer`);\n }\n}\n\nfunction validateMigrations(name: string, job: JobDefinition<unknown>): void {\n const migrations = job.migrate;\n if (!migrations) return;\n\n const keys = Object.keys(migrations);\n if (keys.length === 0) throw new PostmasterError(`job \"${name}\" migrations must not be empty`);\n\n const versions = keys\n .map((key) => {\n const version = Number(key);\n if (!Number.isInteger(version) || String(version) !== key) {\n throw new PostmasterError(`job \"${name}\" has invalid migration key \"${key}\"`);\n }\n if (version < 1 || version >= job.version) {\n throw new PostmasterError(\n `job \"${name}\" has invalid migration key \"${version}\" (valid range: 1..${job.version - 1})`,\n );\n }\n if (typeof migrations[version] !== 'function') {\n throw new PostmasterError(`job \"${name}\" migration from version ${version} must be a function`);\n }\n return version;\n })\n .sort((a, b) => a - b);\n\n for (let index = 1; index < versions.length; index++) {\n if (versions[index] !== versions[index - 1] + 1) {\n throw new PostmasterError(`job \"${name}\" missing migration from version ${versions[index - 1] + 1}`);\n }\n }\n\n if (versions.at(-1) !== job.version - 1) {\n throw new PostmasterError(`job \"${name}\" migrations must lead into version ${job.version}`);\n }\n}\n\nfunction validateJob(name: string, job: JobDefinition<unknown>): void {\n if (!Number.isInteger(job.version) || job.version < 1) {\n throw new PostmasterError(`job \"${name}\" version must be a positive integer`);\n }\n\n if (typeof job.execute !== 'function' || typeof job.key !== 'function') {\n throw new PostmasterError(`job \"${name}\" must define execute and key`);\n }\n\n validateRetry(job.retry, name);\n validateMigrations(name, job);\n}\n\nexport function defineJobs<const J extends JobDefinitions>(jobs: J): J {\n for (const [name, job] of Object.entries(jobs)) validateJob(name, job);\n\n return jobs;\n}\n","import { PostmasterError } from './errors.ts';\nimport type { JsonValue, StoredFailure, Validate } from './types.ts';\n\nexport function runValidate<T>(validate: Validate<T> | undefined, value: unknown): T {\n if (!validate) return value as T;\n if (typeof validate === 'function') return validate(value);\n return validate.parse(value);\n}\n\nexport function asJsonValue(value: unknown): JsonValue {\n let serialized: string | undefined;\n\n try {\n serialized = JSON.stringify(value);\n } catch (error) {\n throw new PostmasterError('job payload must be JSON-serializable', { cause: error });\n }\n\n if (serialized === undefined) {\n throw new PostmasterError('job payload must be JSON-serializable');\n }\n\n return JSON.parse(serialized) as JsonValue;\n}\n\nexport function failureFrom(error: unknown, now: number): StoredFailure {\n if (error instanceof Error) {\n return { message: error.message, name: error.name, occurredAt: now };\n }\n\n return { message: String(error), name: 'Error', occurredAt: now };\n}\n\nexport function isAbortError(error: unknown): boolean {\n return error instanceof DOMException && error.name === 'AbortError';\n}\n","import { PostmasterJobError } from './errors.ts';\nimport type { PostmasterEntry, RemoveResult, RetryResult, StoredFailure, StoredJob, StoreTx } from './types.ts';\n\nexport interface ClaimParams {\n readonly leaseDuration: number;\n readonly now: number;\n readonly ownerId: string;\n}\n\nexport interface OwnedParams {\n readonly id: string;\n readonly now: number;\n readonly ownerId: string;\n}\n\nexport interface RescheduleParams extends OwnedParams {\n readonly availableAt: number;\n readonly failure: StoredFailure;\n}\n\nexport interface DeadLetterParams extends OwnedParams {\n readonly failure: StoredFailure;\n}\n\n/** Claim the earliest eligible job atomically. Returns the claimed entry or undefined. */\nexport async function claimJob(tx: StoreTx, params: ClaimParams): Promise<StoredJob | undefined> {\n const entry = await tx.findClaimable(params.now);\n if (!entry) return undefined;\n const claimed: StoredJob = {\n ...entry,\n attempts: entry.attempts + 1,\n failure: undefined,\n leaseExpiresAt: params.now + params.leaseDuration,\n ownerId: params.ownerId,\n status: 'running',\n updatedAt: params.now,\n };\n await tx.put(claimed);\n return claimed;\n}\n\n/** Complete and delete a job. Returns false if the owner is stale or the job is gone. */\nexport async function completeJob(tx: StoreTx, params: OwnedParams): Promise<boolean> {\n const entry = await tx.get(params.id);\n if (!entry || entry.status !== 'running' || entry.ownerId !== params.ownerId) return false;\n await tx.delete(params.id);\n return true;\n}\n\n/** Release a claimed job back to queued without consuming an attempt. Returns false if stale. */\nexport async function releaseJob(tx: StoreTx, params: OwnedParams): Promise<boolean> {\n const entry = await tx.get(params.id);\n if (!entry || entry.status !== 'running' || entry.ownerId !== params.ownerId) return false;\n await tx.put({\n ...entry,\n leaseExpiresAt: undefined,\n ownerId: undefined,\n status: 'queued',\n updatedAt: params.now,\n });\n return true;\n}\n\n/** Reschedule a running job back to queued with a future availableAt. Returns false if stale. */\nexport async function rescheduleJob(tx: StoreTx, params: RescheduleParams): Promise<boolean> {\n const entry = await tx.get(params.id);\n if (!entry || entry.status !== 'running' || entry.ownerId !== params.ownerId) return false;\n await tx.put({\n ...entry,\n availableAt: params.availableAt,\n failure: params.failure,\n leaseExpiresAt: undefined,\n ownerId: undefined,\n status: 'queued',\n updatedAt: params.now,\n });\n return true;\n}\n\n/** Move a running job to dead-letter. Returns false if stale. */\nexport async function deadLetterJob(tx: StoreTx, params: DeadLetterParams): Promise<boolean> {\n const entry = await tx.get(params.id);\n if (!entry || entry.status !== 'running' || entry.ownerId !== params.ownerId) return false;\n await tx.put({\n ...entry,\n failure: params.failure,\n leaseExpiresAt: undefined,\n ownerId: undefined,\n status: 'dead-letter',\n updatedAt: params.now,\n });\n return true;\n}\n\n/** Renew a lease for the current owner. Returns false if stale. */\nexport async function renewLeaseJob(tx: StoreTx, params: OwnedParams & { leaseExpiresAt: number }): Promise<boolean> {\n const entry = await tx.get(params.id);\n if (!entry || entry.status !== 'running' || entry.ownerId !== params.ownerId) return false;\n await tx.put({ ...entry, leaseExpiresAt: params.leaseExpiresAt, updatedAt: params.now });\n return true;\n}\n\n/** Retry a dead-letter job back to queued. Returns discriminated result. */\nexport async function retryJob(tx: StoreTx, id: string, now: number): Promise<RetryResult> {\n const entry = await tx.get(id);\n if (!entry) return { status: 'not-found' };\n if (entry.status === 'running') return { status: 'running' };\n if (entry.status !== 'dead-letter') return { status: 'not-dead-letter' };\n const retried: StoredJob = {\n ...entry,\n availableAt: now,\n failure: undefined,\n status: 'queued',\n updatedAt: now,\n };\n await tx.put(retried);\n return { entry: toEntry(retried), status: 'retried' };\n}\n\n/** Remove a queued or dead-letter job. Running jobs cannot be removed. */\nexport async function removeJob(tx: StoreTx, id: string): Promise<RemoveResult> {\n const entry = await tx.get(id);\n if (!entry) return { status: 'not-found' };\n if (entry.status === 'running') return { status: 'running' };\n await tx.delete(id);\n return { id, status: 'removed' };\n}\n\n/** Enqueue a new job. Rejects duplicate ids. */\nexport async function enqueueJob(tx: StoreTx, entry: StoredJob): Promise<void> {\n const existing = await tx.get(entry.id);\n if (existing) throw new PostmasterJobError(`Postmaster entry \"${entry.id}\" already exists`);\n await tx.put(entry);\n}\n\nexport function toEntry(entry: StoredJob): PostmasterEntry {\n return {\n attempts: entry.attempts,\n availableAt: entry.availableAt,\n createdAt: entry.createdAt,\n ...(entry.failure ? { failure: entry.failure } : {}),\n id: entry.id,\n key: entry.key,\n name: entry.name,\n status: entry.status,\n updatedAt: entry.updatedAt,\n version: entry.version,\n };\n}\n","import { backoff } from '@vielzeug/arsenal';\nimport { asJsonValue, failureFrom, isAbortError, runValidate } from './_json.ts';\nimport { PostmasterDisposedError, PostmasterError, PostmasterJobError } from './errors.ts';\nimport {\n claimJob,\n completeJob,\n deadLetterJob,\n enqueueJob,\n releaseJob,\n removeJob,\n renewLeaseJob,\n rescheduleJob,\n retryJob,\n toEntry,\n} from './store-ops.ts';\nimport type {\n CreatePostmasterOptions,\n EnqueueOptions,\n EntryFilter,\n FlushResult,\n InferJobPayload,\n JobContext,\n JobDefinition,\n JobDefinitions,\n Postmaster,\n PostmasterEntry,\n PostmasterEvent,\n PostmasterStats,\n RemoveResult,\n RetryResult,\n StoredJob,\n} from './types.ts';\n\ntype ExecutionResult = Omit<FlushResult, 'processed'> & { processed: boolean };\ntype MutableFlushResult = { -readonly [K in keyof FlushResult]: FlushResult[K] };\n\nconst emptyResult = (): FlushResult => ({ completed: 0, deadLettered: 0, processed: 0, retryScheduled: 0 });\n\nfunction assertLeaseDuration(value: number): void {\n if (!Number.isInteger(value) || value < 1_000) {\n throw new PostmasterError('leaseDuration must be a positive integer of at least 1000ms');\n }\n}\n\nfunction assertAvailableAt(value: number | undefined, defaultValue: number): number {\n if (value === undefined) return defaultValue;\n if (!Number.isFinite(value) || value < 0 || !Number.isSafeInteger(value)) {\n throw new PostmasterError('availableAt must be a finite non-negative safe integer');\n }\n return value;\n}\n\nexport function createPostmaster<J extends JobDefinitions>(options: CreatePostmasterOptions<J>): Postmaster<J> {\n const { clock = Date.now, jobs, leaseDuration = 30_000, signal, store } = options;\n assertLeaseDuration(leaseDuration);\n\n const controller = new AbortController();\n const ownerId = crypto.randomUUID();\n const tappers = new Set<(event: PostmasterEvent) => void>();\n let disposed = false;\n let started = false;\n let running: Promise<void> | undefined;\n let wakeTimer: ReturnType<typeof setTimeout> | undefined;\n const activeEntries = new Map<string, StoredJob>();\n let flushPromise: Promise<FlushResult> | undefined;\n\n const error = (reason: unknown): void => {\n const value = reason instanceof Error ? reason : new Error(String(reason));\n emitTap({ error: value, type: 'processor-error' });\n };\n\n const emitTap = (event: PostmasterEvent): void => {\n if (tappers.size === 0) return;\n for (const tapper of tappers) {\n try {\n tapper(event);\n } catch {\n // Observability must not affect postmaster behavior.\n }\n }\n };\n\n const assertLive = (): void => {\n if (disposed) throw new PostmasterDisposedError('Postmaster is disposed');\n };\n\n const clearWakeTimer = (): void => {\n if (wakeTimer !== undefined) clearTimeout(wakeTimer);\n wakeTimer = undefined;\n };\n\n const wake = (): void => {\n clearWakeTimer();\n if (started && !disposed) pump();\n };\n\n const schedule = async (): Promise<void> => {\n if (!started || disposed) return;\n const next = await store.transact((tx) => tx.findNextWake(clock()));\n if (next === undefined) return;\n const delay = Math.max(0, Math.min(next - clock(), 2_147_483_647));\n wakeTimer = setTimeout(wake, delay);\n };\n\n const resolveJob = (entry: StoredJob): JobDefinition<unknown> => {\n const job = jobs[entry.name];\n if (!job) throw new PostmasterJobError(`no job definition is registered for \"${entry.name}\"`);\n return job;\n };\n\n const deadLetter = async (entry: StoredJob, reason: unknown): Promise<ExecutionResult> => {\n const finalized = await store.transact((tx) =>\n deadLetterJob(tx, { failure: failureFrom(reason, clock()), id: entry.id, now: clock(), ownerId }),\n );\n\n if (!finalized) {\n emitTap({ id: entry.id, type: 'lease-lost' });\n return { completed: 0, deadLettered: 0, processed: true, retryScheduled: 0 };\n }\n\n emitTap({ entry: toEntry({ ...entry, status: 'dead-letter' }), type: 'dead-lettered' });\n return { completed: 0, deadLettered: 1, processed: true, retryScheduled: 0 };\n };\n\n const execute = async (entry: StoredJob, externalSignal?: AbortSignal): Promise<ExecutionResult> => {\n let job: JobDefinition<unknown>;\n let payload: unknown;\n\n try {\n job = resolveJob(entry);\n if (entry.version > job.version) {\n throw new PostmasterJobError(\n `job \"${entry.name}\" version ${entry.version} is newer than the registered version`,\n );\n }\n let raw: unknown = entry.payload;\n if (entry.version < job.version) {\n const migrations = job.migrate;\n if (!migrations) {\n throw new PostmasterJobError(`job \"${entry.name}\" has no migrations`);\n }\n for (let v = entry.version; v < job.version; v++) {\n const step = migrations[v];\n if (typeof step !== 'function') {\n throw new PostmasterJobError(`job \"${entry.name}\" does not support stored version ${entry.version}`);\n }\n raw = step(raw);\n if (raw === undefined) {\n throw new PostmasterJobError(`job \"${entry.name}\" migration from version ${v} returned undefined`);\n }\n }\n }\n payload = runValidate(job.validate, raw);\n } catch (reason) {\n return deadLetter(entry, reason);\n }\n\n const taskController = new AbortController();\n const taskSignal = AbortSignal.any(\n [controller.signal, externalSignal, taskController.signal].filter((s): s is AbortSignal => s !== undefined),\n );\n const heartbeat = setInterval(\n () => {\n void store\n .transact((tx) =>\n renewLeaseJob(tx, { id: entry.id, leaseExpiresAt: clock() + leaseDuration, now: clock(), ownerId }),\n )\n .then((renewed) => {\n if (!renewed) {\n taskController.abort();\n emitTap({ id: entry.id, type: 'lease-lost' });\n }\n })\n .catch(error);\n },\n Math.max(1_000, Math.floor(leaseDuration / 2)),\n );\n\n emitTap({ entry: toEntry(entry), type: 'started' });\n\n try {\n await job.execute(payload, {\n attempt: entry.attempts,\n entryId: entry.id,\n key: entry.key,\n signal: taskSignal,\n } as JobContext);\n\n if (taskSignal.aborted) {\n await store.transact((tx) => releaseJob(tx, { id: entry.id, now: clock(), ownerId }));\n return { completed: 0, deadLettered: 0, processed: true, retryScheduled: 0 };\n }\n\n const completed = await store.transact((tx) => completeJob(tx, { id: entry.id, now: clock(), ownerId }));\n if (!completed) {\n emitTap({ id: entry.id, type: 'lease-lost' });\n return { completed: 0, deadLettered: 0, processed: true, retryScheduled: 0 };\n }\n\n emitTap({ entry: toEntry(entry), type: 'completed' });\n return { completed: 1, deadLettered: 0, processed: true, retryScheduled: 0 };\n } catch (reason) {\n if (taskSignal.aborted || isAbortError(reason)) {\n await store.transact((tx) => releaseJob(tx, { id: entry.id, now: clock(), ownerId }));\n return { completed: 0, deadLettered: 0, processed: true, retryScheduled: 0 };\n }\n\n let shouldRetry = false;\n let delay = 0;\n\n try {\n shouldRetry =\n entry.attempts < (job.retry?.maxAttempts ?? 1) && (job.retry?.shouldRetry(reason, entry.attempts) ?? false);\n if (!shouldRetry) return deadLetter(entry, reason);\n delay = job.retry?.delay?.(entry.attempts - 1) ?? backoff(entry.attempts - 1);\n if (!Number.isFinite(delay) || delay < 0) {\n throw new PostmasterError(`job \"${entry.name}\" retry delay must be finite and non-negative`);\n }\n } catch (retryReason) {\n return deadLetter(entry, retryReason);\n }\n\n const availableAt = clock() + delay;\n const rescheduled = await store.transact((tx) =>\n rescheduleJob(tx, { availableAt, failure: failureFrom(reason, clock()), id: entry.id, now: clock(), ownerId }),\n );\n if (!rescheduled) {\n emitTap({ id: entry.id, type: 'lease-lost' });\n return { completed: 0, deadLettered: 0, processed: true, retryScheduled: 0 };\n }\n emitTap({ entry: toEntry({ ...entry, availableAt, status: 'queued' }), type: 'retry-scheduled' });\n return { completed: 0, deadLettered: 0, processed: true, retryScheduled: 1 };\n } finally {\n clearInterval(heartbeat);\n }\n };\n\n const processNext = async (externalSignal?: AbortSignal): Promise<ExecutionResult> => {\n if (externalSignal?.aborted) throw new DOMException('The operation was aborted', 'AbortError');\n if (controller.signal.aborted) return { completed: 0, deadLettered: 0, processed: false, retryScheduled: 0 };\n const entry = await store.transact((tx) => claimJob(tx, { leaseDuration, now: clock(), ownerId }));\n if (!entry) return { completed: 0, deadLettered: 0, processed: false, retryScheduled: 0 };\n activeEntries.set(entry.id, entry);\n try {\n return await execute(entry, externalSignal);\n } finally {\n activeEntries.delete(entry.id);\n }\n };\n\n const pump = (): void => {\n if (running || disposed) return;\n running = (async () => {\n for (;;) {\n const result = await processNext();\n if (!result.processed || disposed) break;\n }\n await schedule();\n })()\n .catch(error)\n .finally(() => {\n running = undefined;\n });\n };\n\n const unsubscribeStore = store.subscribe(wake);\n const externalAbort = (): void => {\n void dispose();\n };\n signal?.addEventListener('abort', externalAbort, { once: true });\n\n const dispose = async (): Promise<void> => {\n if (disposed) return;\n disposed = true;\n emitTap({ type: 'dispose' });\n tappers.clear();\n clearWakeTimer();\n signal?.removeEventListener('abort', externalAbort);\n unsubscribeStore();\n controller.abort();\n const entries = [...activeEntries.values()];\n activeEntries.clear();\n await Promise.all(\n entries.map((entry) => store.transact((tx) => releaseJob(tx, { id: entry.id, now: clock(), ownerId }))),\n );\n };\n\n return {\n get disposalSignal() {\n return controller.signal;\n },\n async dispose(): Promise<void> {\n await dispose();\n },\n get disposed() {\n return disposed;\n },\n async enqueue<K extends keyof J & string>(\n name: K,\n payload: InferJobPayload<J[K]>,\n options?: EnqueueOptions,\n ): Promise<PostmasterEntry> {\n assertLive();\n const job = jobs[name];\n if (!job) throw new PostmasterJobError(`no job definition is registered for \"${name}\"`);\n const now = clock();\n const availableAt = assertAvailableAt(options?.availableAt, now);\n const parsed = runValidate(job.validate, payload);\n const key = job.key(parsed);\n if (typeof key !== 'string' || key.length === 0) {\n throw new PostmasterError(`job \"${name}\" returned an empty key`);\n }\n const entry: StoredJob = {\n attempts: 0,\n availableAt,\n createdAt: now,\n id: crypto.randomUUID(),\n key,\n name,\n payload: asJsonValue(parsed),\n status: 'queued',\n updatedAt: now,\n version: job.version,\n };\n await store.transact((tx) => enqueueJob(tx, entry));\n const result = toEntry(entry);\n emitTap({ entry: result, type: 'enqueued' });\n wake();\n return result;\n },\n flush(options?: { signal?: AbortSignal }): Promise<FlushResult> {\n assertLive();\n if (flushPromise) return flushPromise;\n clearWakeTimer();\n const promise = (async (): Promise<FlushResult> => {\n await running;\n const result: MutableFlushResult = emptyResult();\n try {\n for (;;) {\n const next = await processNext(options?.signal);\n if (!next.processed) return result;\n result.processed += 1;\n result.completed += next.completed;\n result.deadLettered += next.deadLettered;\n result.retryScheduled += next.retryScheduled;\n if (disposed) return result;\n }\n } finally {\n flushPromise = undefined;\n wake();\n }\n })();\n flushPromise = promise;\n return promise;\n },\n async list(filter?: EntryFilter): Promise<PostmasterEntry[]> {\n assertLive();\n return (await store.list(filter)).map(toEntry);\n },\n async remove(id: string): Promise<RemoveResult> {\n assertLive();\n const result = await store.transact((tx) => removeJob(tx, id));\n if (result.status === 'removed') emitTap({ id, type: 'removed' });\n return result;\n },\n async retry(id: string): Promise<RetryResult> {\n assertLive();\n const result = await store.transact((tx) => retryJob(tx, id, clock()));\n if (result.status === 'retried') wake();\n return result;\n },\n start(): void {\n assertLive();\n if (started) return;\n started = true;\n wake();\n },\n async stats(): Promise<PostmasterStats> {\n assertLive();\n return store.transact((tx) => tx.countByStatus());\n },\n tap(handler: (event: PostmasterEvent) => void, opts?: { readonly signal?: AbortSignal }): () => void {\n assertLive();\n tappers.add(handler);\n\n if (opts?.signal) {\n if (opts.signal.aborted) {\n tappers.delete(handler);\n return () => {};\n }\n const onAbort = () => tappers.delete(handler);\n opts.signal.addEventListener('abort', onAbort, { once: true });\n return () => {\n tappers.delete(handler);\n opts.signal?.removeEventListener('abort', onAbort);\n };\n }\n\n return () => tappers.delete(handler);\n },\n async [Symbol.asyncDispose](): Promise<void> {\n await dispose();\n },\n };\n}\n"],"mappings":"sGAAA,IAAa,EAAb,cAAqC,KAAM,CACzC,YAAY,EAAiB,EAAwB,CACnD,MAAM,EAAS,CAAO,EACtB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAEa,EAAb,cAA6C,CAAgB,CAAC,EACjD,EAAb,cAAwC,CAAgB,CAAC,ECNzD,SAAS,EAAc,EAAgC,EAAoB,CACpE,OAED,CAAC,OAAO,UAAU,EAAM,WAAW,GAAK,EAAM,YAAc,GAC9D,MAAM,IAAI,EAAgB,QAAQ,EAAK,+CAA+C,CAE1F,CAEA,SAAS,EAAmB,EAAc,EAAmC,CAC3E,IAAM,EAAa,EAAI,QACvB,GAAI,CAAC,EAAY,OAEjB,IAAM,EAAO,OAAO,KAAK,CAAU,EACnC,GAAI,EAAK,SAAW,EAAG,MAAM,IAAI,EAAgB,QAAQ,EAAK,+BAA+B,EAE7F,IAAM,EAAW,EACd,IAAK,GAAQ,CACZ,IAAM,EAAU,OAAO,CAAG,EAC1B,GAAI,CAAC,OAAO,UAAU,CAAO,GAAK,OAAO,CAAO,IAAM,EACpD,MAAM,IAAI,EAAgB,QAAQ,EAAK,+BAA+B,EAAI,EAAE,EAE9E,GAAI,EAAU,GAAK,GAAW,EAAI,QAChC,MAAM,IAAI,EACR,QAAQ,EAAK,+BAA+B,EAAQ,qBAAqB,EAAI,QAAU,EAAE,EAC3F,EAEF,GAAI,OAAO,EAAW,IAAa,WACjC,MAAM,IAAI,EAAgB,QAAQ,EAAK,2BAA2B,EAAQ,oBAAoB,EAEhG,OAAO,CACT,CAAC,CAAC,CACD,MAAM,EAAG,IAAM,EAAI,CAAC,EAEvB,IAAK,IAAI,EAAQ,EAAG,EAAQ,EAAS,OAAQ,IAC3C,GAAI,EAAS,KAAW,EAAS,EAAQ,GAAK,EAC5C,MAAM,IAAI,EAAgB,QAAQ,EAAK,mCAAmC,EAAS,EAAQ,GAAK,GAAG,EAIvG,GAAI,EAAS,GAAG,EAAE,IAAM,EAAI,QAAU,EACpC,MAAM,IAAI,EAAgB,QAAQ,EAAK,sCAAsC,EAAI,SAAS,CAE9F,CAEA,SAAS,EAAY,EAAc,EAAmC,CACpE,GAAI,CAAC,OAAO,UAAU,EAAI,OAAO,GAAK,EAAI,QAAU,EAClD,MAAM,IAAI,EAAgB,QAAQ,EAAK,qCAAqC,EAG9E,GAAI,OAAO,EAAI,SAAY,YAAc,OAAO,EAAI,KAAQ,WAC1D,MAAM,IAAI,EAAgB,QAAQ,EAAK,8BAA8B,EAGvE,EAAc,EAAI,MAAO,CAAI,EAC7B,EAAmB,EAAM,CAAG,CAC9B,CAEA,SAAgB,EAA2C,EAAY,CACrE,IAAK,GAAM,CAAC,EAAM,KAAQ,OAAO,QAAQ,CAAI,EAAG,EAAY,EAAM,CAAG,EAErE,OAAO,CACT,CC7DA,SAAgB,EAAe,EAAmC,EAAmB,CAGnF,OAFK,EACD,OAAO,GAAa,WAAmB,EAAS,CAAK,EAClD,EAAS,MAAM,CAAK,EAFL,CAGxB,CAEA,SAAgB,EAAY,EAA2B,CACrD,IAAI,EAEJ,GAAI,CACF,EAAa,KAAK,UAAU,CAAK,CACnC,OAAS,EAAO,CACd,MAAM,IAAI,EAAgB,wCAAyC,CAAE,MAAO,CAAM,CAAC,CACrF,CAEA,GAAI,IAAe,IAAA,GACjB,MAAM,IAAI,EAAgB,uCAAuC,EAGnE,OAAO,KAAK,MAAM,CAAU,CAC9B,CAEA,SAAgB,EAAY,EAAgB,EAA4B,CAKtE,OAJI,aAAiB,MACZ,CAAE,QAAS,EAAM,QAAS,KAAM,EAAM,KAAM,WAAY,CAAI,EAG9D,CAAE,QAAS,OAAO,CAAK,EAAG,KAAM,QAAS,WAAY,CAAI,CAClE,CAEA,SAAgB,EAAa,EAAyB,CACpD,OAAO,aAAiB,cAAgB,EAAM,OAAS,YACzD,CCVA,eAAsB,EAAS,EAAa,EAAqD,CAC/F,IAAM,EAAQ,MAAM,EAAG,cAAc,EAAO,GAAG,EAC/C,GAAI,CAAC,EAAO,OACZ,IAAM,EAAqB,CACzB,GAAG,EACH,SAAU,EAAM,SAAW,EAC3B,QAAS,IAAA,GACT,eAAgB,EAAO,IAAM,EAAO,cACpC,QAAS,EAAO,QAChB,OAAQ,UACR,UAAW,EAAO,GACpB,EAEA,OADA,MAAM,EAAG,IAAI,CAAO,EACb,CACT,CAGA,eAAsB,EAAY,EAAa,EAAuC,CACpF,IAAM,EAAQ,MAAM,EAAG,IAAI,EAAO,EAAE,EAGpC,MAFI,CAAC,GAAS,EAAM,SAAW,WAAa,EAAM,UAAY,EAAO,QAAgB,IACrF,MAAM,EAAG,OAAO,EAAO,EAAE,EAClB,GACT,CAGA,eAAsB,EAAW,EAAa,EAAuC,CACnF,IAAM,EAAQ,MAAM,EAAG,IAAI,EAAO,EAAE,EASpC,MARI,CAAC,GAAS,EAAM,SAAW,WAAa,EAAM,UAAY,EAAO,QAAgB,IACrF,MAAM,EAAG,IAAI,CACX,GAAG,EACH,eAAgB,IAAA,GAChB,QAAS,IAAA,GACT,OAAQ,SACR,UAAW,EAAO,GACpB,CAAC,EACM,GACT,CAGA,eAAsB,EAAc,EAAa,EAA4C,CAC3F,IAAM,EAAQ,MAAM,EAAG,IAAI,EAAO,EAAE,EAWpC,MAVI,CAAC,GAAS,EAAM,SAAW,WAAa,EAAM,UAAY,EAAO,QAAgB,IACrF,MAAM,EAAG,IAAI,CACX,GAAG,EACH,YAAa,EAAO,YACpB,QAAS,EAAO,QAChB,eAAgB,IAAA,GAChB,QAAS,IAAA,GACT,OAAQ,SACR,UAAW,EAAO,GACpB,CAAC,EACM,GACT,CAGA,eAAsB,EAAc,EAAa,EAA4C,CAC3F,IAAM,EAAQ,MAAM,EAAG,IAAI,EAAO,EAAE,EAUpC,MATI,CAAC,GAAS,EAAM,SAAW,WAAa,EAAM,UAAY,EAAO,QAAgB,IACrF,MAAM,EAAG,IAAI,CACX,GAAG,EACH,QAAS,EAAO,QAChB,eAAgB,IAAA,GAChB,QAAS,IAAA,GACT,OAAQ,cACR,UAAW,EAAO,GACpB,CAAC,EACM,GACT,CAGA,eAAsB,EAAc,EAAa,EAAoE,CACnH,IAAM,EAAQ,MAAM,EAAG,IAAI,EAAO,EAAE,EAGpC,MAFI,CAAC,GAAS,EAAM,SAAW,WAAa,EAAM,UAAY,EAAO,QAAgB,IACrF,MAAM,EAAG,IAAI,CAAE,GAAG,EAAO,eAAgB,EAAO,eAAgB,UAAW,EAAO,GAAI,CAAC,EAChF,GACT,CAGA,eAAsB,EAAS,EAAa,EAAY,EAAmC,CACzF,IAAM,EAAQ,MAAM,EAAG,IAAI,CAAE,EAC7B,GAAI,CAAC,EAAO,MAAO,CAAE,OAAQ,WAAY,EACzC,GAAI,EAAM,SAAW,UAAW,MAAO,CAAE,OAAQ,SAAU,EAC3D,GAAI,EAAM,SAAW,cAAe,MAAO,CAAE,OAAQ,iBAAkB,EACvE,IAAM,EAAqB,CACzB,GAAG,EACH,YAAa,EACb,QAAS,IAAA,GACT,OAAQ,SACR,UAAW,CACb,EAEA,OADA,MAAM,EAAG,IAAI,CAAO,EACb,CAAE,MAAO,EAAQ,CAAO,EAAG,OAAQ,SAAU,CACtD,CAGA,eAAsB,EAAU,EAAa,EAAmC,CAC9E,IAAM,EAAQ,MAAM,EAAG,IAAI,CAAE,EAI7B,OAHK,EACD,EAAM,SAAW,UAAkB,CAAE,OAAQ,SAAU,GAC3D,MAAM,EAAG,OAAO,CAAE,EACX,CAAE,KAAI,OAAQ,SAAU,GAHZ,CAAE,OAAQ,WAAY,CAI3C,CAGA,eAAsB,EAAW,EAAa,EAAiC,CAE7E,GAAI,MADmB,EAAG,IAAI,EAAM,EAAE,EACxB,MAAM,IAAI,EAAmB,qBAAqB,EAAM,GAAG,iBAAiB,EAC1F,MAAM,EAAG,IAAI,CAAK,CACpB,CAEA,SAAgB,EAAQ,EAAmC,CACzD,MAAO,CACL,SAAU,EAAM,SAChB,YAAa,EAAM,YACnB,UAAW,EAAM,UACjB,GAAI,EAAM,QAAU,CAAE,QAAS,EAAM,OAAQ,EAAI,CAAC,EAClD,GAAI,EAAM,GACV,IAAK,EAAM,IACX,KAAM,EAAM,KACZ,OAAQ,EAAM,OACd,UAAW,EAAM,UACjB,QAAS,EAAM,OACjB,CACF,CChHA,IAAM,OAAkC,CAAE,UAAW,EAAG,aAAc,EAAG,UAAW,EAAG,eAAgB,CAAE,GAEzG,SAAS,EAAoB,EAAqB,CAChD,GAAI,CAAC,OAAO,UAAU,CAAK,GAAK,EAAQ,IACtC,MAAM,IAAI,EAAgB,6DAA6D,CAE3F,CAEA,SAAS,EAAkB,EAA2B,EAA8B,CAClF,GAAI,IAAU,IAAA,GAAW,OAAO,EAChC,GAAI,CAAC,OAAO,SAAS,CAAK,GAAK,EAAQ,GAAK,CAAC,OAAO,cAAc,CAAK,EACrE,MAAM,IAAI,EAAgB,wDAAwD,EAEpF,OAAO,CACT,CAEA,SAAgB,EAA2C,EAAoD,CAC7G,GAAM,CAAE,QAAQ,KAAK,IAAK,OAAM,gBAAgB,IAAQ,SAAQ,SAAU,EAC1E,EAAoB,CAAa,EAEjC,IAAM,EAAa,IAAI,gBACjB,EAAU,OAAO,WAAW,EAC5B,EAAU,IAAI,IAChB,EAAW,GACX,EAAU,GACV,EACA,EACE,EAAgB,IAAI,IACtB,EAEE,EAAS,GAA0B,CACvC,IAAM,EAAQ,aAAkB,MAAQ,EAAa,MAAM,OAAO,CAAM,CAAC,EACzE,EAAQ,CAAE,MAAO,EAAO,KAAM,iBAAkB,CAAC,CACnD,EAEM,EAAW,GAAiC,CAC5C,KAAQ,OAAS,EACrB,IAAK,IAAM,KAAU,EACnB,GAAI,CACF,EAAO,CAAK,CACd,MAAQ,CAER,CAEJ,EAEM,MAAyB,CAC7B,GAAI,EAAU,MAAM,IAAI,EAAwB,wBAAwB,CAC1E,EAEM,MAA6B,CAC7B,IAAc,IAAA,IAAW,aAAa,CAAS,EACnD,EAAY,IAAA,EACd,EAEM,MAAmB,CACvB,EAAe,EACX,GAAW,CAAC,GAAU,EAAK,CACjC,EAEM,EAAW,SAA2B,CAC1C,GAAI,CAAC,GAAW,EAAU,OAC1B,IAAM,EAAO,MAAM,EAAM,SAAU,GAAO,EAAG,aAAa,EAAM,CAAC,CAAC,EAClE,GAAI,IAAS,IAAA,GAAW,OACxB,IAAM,EAAQ,KAAK,IAAI,EAAG,KAAK,IAAI,EAAO,EAAM,EAAG,UAAa,CAAC,EACjE,EAAY,WAAW,EAAM,CAAK,CACpC,EAEM,EAAc,GAA6C,CAC/D,IAAM,EAAM,EAAK,EAAM,MACvB,GAAI,CAAC,EAAK,MAAM,IAAI,EAAmB,wCAAwC,EAAM,KAAK,EAAE,EAC5F,OAAO,CACT,EAEM,EAAa,MAAO,EAAkB,IAKrC,MAJmB,EAAM,SAAU,GACtC,EAAc,EAAI,CAAE,QAAS,EAAY,EAAQ,EAAM,CAAC,EAAG,GAAI,EAAM,GAAI,IAAK,EAAM,EAAG,SAAQ,CAAC,CAClG,GAOA,EAAQ,CAAE,MAAO,EAAQ,CAAE,GAAG,EAAO,OAAQ,aAAc,CAAC,EAAG,KAAM,eAAgB,CAAC,EAC/E,CAAE,UAAW,EAAG,aAAc,EAAG,UAAW,GAAM,eAAgB,CAAE,IALzE,EAAQ,CAAE,GAAI,EAAM,GAAI,KAAM,YAAa,CAAC,EACrC,CAAE,UAAW,EAAG,aAAc,EAAG,UAAW,GAAM,eAAgB,CAAE,GAOzE,EAAU,MAAO,EAAkB,IAA2D,CAClG,IAAI,EACA,EAEJ,GAAI,CAEF,GADA,EAAM,EAAW,CAAK,EAClB,EAAM,QAAU,EAAI,QACtB,MAAM,IAAI,EACR,QAAQ,EAAM,KAAK,YAAY,EAAM,QAAQ,sCAC/C,EAEF,IAAI,EAAe,EAAM,QACzB,GAAI,EAAM,QAAU,EAAI,QAAS,CAC/B,IAAM,EAAa,EAAI,QACvB,GAAI,CAAC,EACH,MAAM,IAAI,EAAmB,QAAQ,EAAM,KAAK,oBAAoB,EAEtE,IAAK,IAAI,EAAI,EAAM,QAAS,EAAI,EAAI,QAAS,IAAK,CAChD,IAAM,EAAO,EAAW,GACxB,GAAI,OAAO,GAAS,WAClB,MAAM,IAAI,EAAmB,QAAQ,EAAM,KAAK,oCAAoC,EAAM,SAAS,EAGrG,GADA,EAAM,EAAK,CAAG,EACV,IAAQ,IAAA,GACV,MAAM,IAAI,EAAmB,QAAQ,EAAM,KAAK,2BAA2B,EAAE,oBAAoB,CAErG,CACF,CACA,EAAU,EAAY,EAAI,SAAU,CAAG,CACzC,OAAS,EAAQ,CACf,OAAO,EAAW,EAAO,CAAM,CACjC,CAEA,IAAM,EAAiB,IAAI,gBACrB,EAAa,YAAY,IAC7B,CAAC,EAAW,OAAQ,EAAgB,EAAe,MAAM,CAAC,CAAC,OAAQ,GAAwB,IAAM,IAAA,EAAS,CAC5G,EACM,EAAY,gBACV,CACJ,EACG,SAAU,GACT,EAAc,EAAI,CAAE,GAAI,EAAM,GAAI,eAAgB,EAAM,EAAI,EAAe,IAAK,EAAM,EAAG,SAAQ,CAAC,CACpG,CAAC,CACA,KAAM,GAAY,CACZ,IACH,EAAe,MAAM,EACrB,EAAQ,CAAE,GAAI,EAAM,GAAI,KAAM,YAAa,CAAC,EAEhD,CAAC,CAAC,CACD,MAAM,CAAK,CAChB,EACA,KAAK,IAAI,IAAO,KAAK,MAAM,EAAgB,CAAC,CAAC,CAC/C,EAEA,EAAQ,CAAE,MAAO,EAAQ,CAAK,EAAG,KAAM,SAAU,CAAC,EAElD,GAAI,CAoBF,OAnBA,MAAM,EAAI,QAAQ,EAAS,CACzB,QAAS,EAAM,SACf,QAAS,EAAM,GACf,IAAK,EAAM,IACX,OAAQ,CACV,CAAe,EAEX,EAAW,SACb,MAAM,EAAM,SAAU,GAAO,EAAW,EAAI,CAAE,GAAI,EAAM,GAAI,IAAK,EAAM,EAAG,SAAQ,CAAC,CAAC,EAC7E,CAAE,UAAW,EAAG,aAAc,EAAG,UAAW,GAAM,eAAgB,CAAE,GAIxE,MADmB,EAAM,SAAU,GAAO,EAAY,EAAI,CAAE,GAAI,EAAM,GAAI,IAAK,EAAM,EAAG,SAAQ,CAAC,CAAC,GAMvG,EAAQ,CAAE,MAAO,EAAQ,CAAK,EAAG,KAAM,WAAY,CAAC,EAC7C,CAAE,UAAW,EAAG,aAAc,EAAG,UAAW,GAAM,eAAgB,CAAE,IALzE,EAAQ,CAAE,GAAI,EAAM,GAAI,KAAM,YAAa,CAAC,EACrC,CAAE,UAAW,EAAG,aAAc,EAAG,UAAW,GAAM,eAAgB,CAAE,EAK/E,OAAS,EAAQ,CACf,GAAI,EAAW,SAAW,EAAa,CAAM,EAE3C,OADA,MAAM,EAAM,SAAU,GAAO,EAAW,EAAI,CAAE,GAAI,EAAM,GAAI,IAAK,EAAM,EAAG,SAAQ,CAAC,CAAC,EAC7E,CAAE,UAAW,EAAG,aAAc,EAAG,UAAW,GAAM,eAAgB,CAAE,EAG7E,IAAI,EAAc,GACd,EAAQ,EAEZ,GAAI,CAGF,GAFA,EACE,EAAM,UAAY,EAAI,OAAO,aAAe,KAAO,EAAI,OAAO,YAAY,EAAQ,EAAM,QAAQ,GAAK,IACnG,CAAC,EAAa,OAAO,EAAW,EAAO,CAAM,EAEjD,GADA,EAAQ,EAAI,OAAO,QAAQ,EAAM,SAAW,CAAC,IAAA,EAAK,EAAA,QAAA,CAAQ,EAAM,SAAW,CAAC,EACxE,CAAC,OAAO,SAAS,CAAK,GAAK,EAAQ,EACrC,MAAM,IAAI,EAAgB,QAAQ,EAAM,KAAK,8CAA8C,CAE/F,OAAS,EAAa,CACpB,OAAO,EAAW,EAAO,CAAW,CACtC,CAEA,IAAM,EAAc,EAAM,EAAI,EAS9B,OALK,MAHqB,EAAM,SAAU,GACxC,EAAc,EAAI,CAAE,cAAa,QAAS,EAAY,EAAQ,EAAM,CAAC,EAAG,GAAI,EAAM,GAAI,IAAK,EAAM,EAAG,SAAQ,CAAC,CAC/G,GAKA,EAAQ,CAAE,MAAO,EAAQ,CAAE,GAAG,EAAO,cAAa,OAAQ,QAAS,CAAC,EAAG,KAAM,iBAAkB,CAAC,EACzF,CAAE,UAAW,EAAG,aAAc,EAAG,UAAW,GAAM,eAAgB,CAAE,IAJzE,EAAQ,CAAE,GAAI,EAAM,GAAI,KAAM,YAAa,CAAC,EACrC,CAAE,UAAW,EAAG,aAAc,EAAG,UAAW,GAAM,eAAgB,CAAE,EAI/E,QAAU,CACR,cAAc,CAAS,CACzB,CACF,EAEM,EAAc,KAAO,IAA2D,CACpF,GAAI,GAAgB,QAAS,MAAM,IAAI,aAAa,4BAA6B,YAAY,EAC7F,GAAI,EAAW,OAAO,QAAS,MAAO,CAAE,UAAW,EAAG,aAAc,EAAG,UAAW,GAAO,eAAgB,CAAE,EAC3G,IAAM,EAAQ,MAAM,EAAM,SAAU,GAAO,EAAS,EAAI,CAAE,gBAAe,IAAK,EAAM,EAAG,SAAQ,CAAC,CAAC,EACjG,GAAI,CAAC,EAAO,MAAO,CAAE,UAAW,EAAG,aAAc,EAAG,UAAW,GAAO,eAAgB,CAAE,EACxF,EAAc,IAAI,EAAM,GAAI,CAAK,EACjC,GAAI,CACF,OAAO,MAAM,EAAQ,EAAO,CAAc,CAC5C,QAAU,CACR,EAAc,OAAO,EAAM,EAAE,CAC/B,CACF,EAEM,MAAmB,CACnB,GAAW,IACf,GAAW,SAAY,CACrB,MAEO,MADgB,EAAY,EAAA,CACrB,WAAa,KAE3B,MAAM,EAAS,CACjB,EAAA,CAAG,CAAC,CACD,MAAM,CAAK,CAAC,CACZ,YAAc,CACb,EAAU,IAAA,EACZ,CAAC,EACL,EAEM,EAAmB,EAAM,UAAU,CAAI,EACvC,MAA4B,CAChC,EAAa,CACf,EACA,GAAQ,iBAAiB,QAAS,EAAe,CAAE,KAAM,EAAK,CAAC,EAE/D,IAAM,EAAU,SAA2B,CACzC,GAAI,EAAU,OACd,EAAW,GACX,EAAQ,CAAE,KAAM,SAAU,CAAC,EAC3B,EAAQ,MAAM,EACd,EAAe,EACf,GAAQ,oBAAoB,QAAS,CAAa,EAClD,EAAiB,EACjB,EAAW,MAAM,EACjB,IAAM,EAAU,CAAC,GAAG,EAAc,OAAO,CAAC,EAC1C,EAAc,MAAM,EACpB,MAAM,QAAQ,IACZ,EAAQ,IAAK,GAAU,EAAM,SAAU,GAAO,EAAW,EAAI,CAAE,GAAI,EAAM,GAAI,IAAK,EAAM,EAAG,SAAQ,CAAC,CAAC,CAAC,CACxG,CACF,EAEA,MAAO,CACL,IAAI,gBAAiB,CACnB,OAAO,EAAW,MACpB,EACA,MAAM,SAAyB,CAC7B,MAAM,EAAQ,CAChB,EACA,IAAI,UAAW,CACb,OAAO,CACT,EACA,MAAM,QACJ,EACA,EACA,EAC0B,CAC1B,EAAW,EACX,IAAM,EAAM,EAAK,GACjB,GAAI,CAAC,EAAK,MAAM,IAAI,EAAmB,wCAAwC,EAAK,EAAE,EACtF,IAAM,EAAM,EAAM,EACZ,EAAc,EAAkB,GAAS,YAAa,CAAG,EACzD,EAAS,EAAY,EAAI,SAAU,CAAO,EAC1C,EAAM,EAAI,IAAI,CAAM,EAC1B,GAAI,OAAO,GAAQ,UAAY,EAAI,SAAW,EAC5C,MAAM,IAAI,EAAgB,QAAQ,EAAK,wBAAwB,EAEjE,IAAM,EAAmB,CACvB,SAAU,EACV,cACA,UAAW,EACX,GAAI,OAAO,WAAW,EACtB,MACA,OACA,QAAS,EAAY,CAAM,EAC3B,OAAQ,SACR,UAAW,EACX,QAAS,EAAI,OACf,EACA,MAAM,EAAM,SAAU,GAAO,EAAW,EAAI,CAAK,CAAC,EAClD,IAAM,EAAS,EAAQ,CAAK,EAG5B,OAFA,EAAQ,CAAE,MAAO,EAAQ,KAAM,UAAW,CAAC,EAC3C,EAAK,EACE,CACT,EACA,MAAM,EAA0D,CAE9D,GADA,EAAW,EACP,EAAc,OAAO,EACzB,EAAe,EACf,IAAM,GAAW,SAAkC,CACjD,MAAM,EACN,IAAM,EAA6B,EAAY,EAC/C,GAAI,CACF,OAAS,CACP,IAAM,EAAO,MAAM,EAAY,GAAS,MAAM,EAM9C,GALI,CAAC,EAAK,YACV,EAAO,WAAa,EACpB,EAAO,WAAa,EAAK,UACzB,EAAO,cAAgB,EAAK,aAC5B,EAAO,gBAAkB,EAAK,eAC1B,GAAU,OAAO,CACvB,CACF,QAAU,CACR,EAAe,IAAA,GACf,EAAK,CACP,CACF,EAAA,CAAG,EAEH,MADA,GAAe,EACR,CACT,EACA,MAAM,KAAK,EAAkD,CAE3D,OADA,EAAW,GACH,MAAM,EAAM,KAAK,CAAM,EAAA,CAAG,IAAI,CAAO,CAC/C,EACA,MAAM,OAAO,EAAmC,CAC9C,EAAW,EACX,IAAM,EAAS,MAAM,EAAM,SAAU,GAAO,EAAU,EAAI,CAAE,CAAC,EAE7D,OADI,EAAO,SAAW,WAAW,EAAQ,CAAE,KAAI,KAAM,SAAU,CAAC,EACzD,CACT,EACA,MAAM,MAAM,EAAkC,CAC5C,EAAW,EACX,IAAM,EAAS,MAAM,EAAM,SAAU,GAAO,EAAS,EAAI,EAAI,EAAM,CAAC,CAAC,EAErE,OADI,EAAO,SAAW,WAAW,EAAK,EAC/B,CACT,EACA,OAAc,CACZ,EAAW,EACP,KACJ,EAAU,GACV,EAAK,EACP,EACA,MAAM,OAAkC,CAEtC,OADA,EAAW,EACJ,EAAM,SAAU,GAAO,EAAG,cAAc,CAAC,CAClD,EACA,IAAI,EAA2C,EAAsD,CAInG,GAHA,EAAW,EACX,EAAQ,IAAI,CAAO,EAEf,GAAM,OAAQ,CAChB,GAAI,EAAK,OAAO,QAEd,OADA,EAAQ,OAAO,CAAO,MACT,CAAC,EAEhB,IAAM,MAAgB,EAAQ,OAAO,CAAO,EAE5C,OADA,EAAK,OAAO,iBAAiB,QAAS,EAAS,CAAE,KAAM,EAAK,CAAC,MAChD,CACX,EAAQ,OAAO,CAAO,EACtB,EAAK,QAAQ,oBAAoB,QAAS,CAAO,CACnD,CACF,CAEA,UAAa,EAAQ,OAAO,CAAO,CACrC,EACA,MAAO,OAAO,eAA+B,CAC3C,MAAM,EAAQ,CAChB,CACF,CACF"}
|
package/dist/postmaster.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postmaster.d.ts","sourceRoot":"","sources":["../src/postmaster.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACV,uBAAuB,EAOvB,cAAc,EACd,UAAU,EAOX,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"postmaster.d.ts","sourceRoot":"","sources":["../src/postmaster.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACV,uBAAuB,EAOvB,cAAc,EACd,UAAU,EAOX,MAAM,YAAY,CAAC;AAqBpB,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,cAAc,EAAE,OAAO,EAAE,uBAAuB,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAgW7G"}
|
package/dist/postmaster.iife.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var Postmaster=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var
|
|
1
|
+
var Postmaster=(function(e,t){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var n=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},r=class extends n{},i=class extends n{};function a(e,t){if(e&&(!Number.isInteger(e.maxAttempts)||e.maxAttempts<1))throw new n(`job "${t}" retry.maxAttempts must be a positive integer`)}function o(e,t){let r=t.migrate;if(!r)return;let i=Object.keys(r);if(i.length===0)throw new n(`job "${e}" migrations must not be empty`);let a=i.map(i=>{let a=Number(i);if(!Number.isInteger(a)||String(a)!==i)throw new n(`job "${e}" has invalid migration key "${i}"`);if(a<1||a>=t.version)throw new n(`job "${e}" has invalid migration key "${a}" (valid range: 1..${t.version-1})`);if(typeof r[a]!=`function`)throw new n(`job "${e}" migration from version ${a} must be a function`);return a}).sort((e,t)=>e-t);for(let t=1;t<a.length;t++)if(a[t]!==a[t-1]+1)throw new n(`job "${e}" missing migration from version ${a[t-1]+1}`);if(a.at(-1)!==t.version-1)throw new n(`job "${e}" migrations must lead into version ${t.version}`)}function s(e,t){if(!Number.isInteger(t.version)||t.version<1)throw new n(`job "${e}" version must be a positive integer`);if(typeof t.execute!=`function`||typeof t.key!=`function`)throw new n(`job "${e}" must define execute and key`);a(t.retry,e),o(e,t)}function c(e){for(let[t,n]of Object.entries(e))s(t,n);return e}function l(e,t){return e?typeof e==`function`?e(t):e.parse(t):t}function u(e){let t;try{t=JSON.stringify(e)}catch(e){throw new n(`job payload must be JSON-serializable`,{cause:e})}if(t===void 0)throw new n(`job payload must be JSON-serializable`);return JSON.parse(t)}function d(e,t){return e instanceof Error?{message:e.message,name:e.name,occurredAt:t}:{message:String(e),name:`Error`,occurredAt:t}}function f(e){return e instanceof DOMException&&e.name===`AbortError`}async function p(e,t){let n=await e.findClaimable(t.now);if(!n)return;let r={...n,attempts:n.attempts+1,failure:void 0,leaseExpiresAt:t.now+t.leaseDuration,ownerId:t.ownerId,status:`running`,updatedAt:t.now};return await e.put(r),r}async function m(e,t){let n=await e.get(t.id);return!n||n.status!==`running`||n.ownerId!==t.ownerId?!1:(await e.delete(t.id),!0)}async function h(e,t){let n=await e.get(t.id);return!n||n.status!==`running`||n.ownerId!==t.ownerId?!1:(await e.put({...n,leaseExpiresAt:void 0,ownerId:void 0,status:`queued`,updatedAt:t.now}),!0)}async function g(e,t){let n=await e.get(t.id);return!n||n.status!==`running`||n.ownerId!==t.ownerId?!1:(await e.put({...n,availableAt:t.availableAt,failure:t.failure,leaseExpiresAt:void 0,ownerId:void 0,status:`queued`,updatedAt:t.now}),!0)}async function _(e,t){let n=await e.get(t.id);return!n||n.status!==`running`||n.ownerId!==t.ownerId?!1:(await e.put({...n,failure:t.failure,leaseExpiresAt:void 0,ownerId:void 0,status:`dead-letter`,updatedAt:t.now}),!0)}async function v(e,t){let n=await e.get(t.id);return!n||n.status!==`running`||n.ownerId!==t.ownerId?!1:(await e.put({...n,leaseExpiresAt:t.leaseExpiresAt,updatedAt:t.now}),!0)}async function y(e,t,n){let r=await e.get(t);if(!r)return{status:`not-found`};if(r.status===`running`)return{status:`running`};if(r.status!==`dead-letter`)return{status:`not-dead-letter`};let i={...r,availableAt:n,failure:void 0,status:`queued`,updatedAt:n};return await e.put(i),{entry:S(i),status:`retried`}}async function b(e,t){let n=await e.get(t);return n?n.status===`running`?{status:`running`}:(await e.delete(t),{id:t,status:`removed`}):{status:`not-found`}}async function x(e,t){if(await e.get(t.id))throw new i(`Postmaster entry "${t.id}" already exists`);await e.put(t)}function S(e){return{attempts:e.attempts,availableAt:e.availableAt,createdAt:e.createdAt,...e.failure?{failure:e.failure}:{},id:e.id,key:e.key,name:e.name,status:e.status,updatedAt:e.updatedAt,version:e.version}}var C=()=>({completed:0,deadLettered:0,processed:0,retryScheduled:0});function w(e){if(!Number.isInteger(e)||e<1e3)throw new n(`leaseDuration must be a positive integer of at least 1000ms`)}function T(e,t){if(e===void 0)return t;if(!Number.isFinite(e)||e<0||!Number.isSafeInteger(e))throw new n(`availableAt must be a finite non-negative safe integer`);return e}function E(e){let{clock:a=Date.now,jobs:o,leaseDuration:s=3e4,signal:c,store:E}=e;w(s);let D=new AbortController,O=crypto.randomUUID(),k=new Set,A=!1,j=!1,M,N,P=new Map,F,I=e=>{let t=e instanceof Error?e:Error(String(e));L({error:t,type:`processor-error`})},L=e=>{if(k.size!==0)for(let t of k)try{t(e)}catch{}},R=()=>{if(A)throw new r(`Postmaster is disposed`)},z=()=>{N!==void 0&&clearTimeout(N),N=void 0},B=()=>{z(),j&&!A&&K()},V=async()=>{if(!j||A)return;let e=await E.transact(e=>e.findNextWake(a()));if(e===void 0)return;let t=Math.max(0,Math.min(e-a(),2147483647));N=setTimeout(B,t)},H=e=>{let t=o[e.name];if(!t)throw new i(`no job definition is registered for "${e.name}"`);return t},U=async(e,t)=>await E.transact(n=>_(n,{failure:d(t,a()),id:e.id,now:a(),ownerId:O}))?(L({entry:S({...e,status:`dead-letter`}),type:`dead-lettered`}),{completed:0,deadLettered:1,processed:!0,retryScheduled:0}):(L({id:e.id,type:`lease-lost`}),{completed:0,deadLettered:0,processed:!0,retryScheduled:0}),W=async(e,r)=>{let o,c;try{if(o=H(e),e.version>o.version)throw new i(`job "${e.name}" version ${e.version} is newer than the registered version`);let t=e.payload;if(e.version<o.version){let n=o.migrate;if(!n)throw new i(`job "${e.name}" has no migrations`);for(let r=e.version;r<o.version;r++){let a=n[r];if(typeof a!=`function`)throw new i(`job "${e.name}" does not support stored version ${e.version}`);if(t=a(t),t===void 0)throw new i(`job "${e.name}" migration from version ${r} returned undefined`)}}c=l(o.validate,t)}catch(t){return U(e,t)}let u=new AbortController,p=AbortSignal.any([D.signal,r,u.signal].filter(e=>e!==void 0)),_=setInterval(()=>{E.transact(t=>v(t,{id:e.id,leaseExpiresAt:a()+s,now:a(),ownerId:O})).then(t=>{t||(u.abort(),L({id:e.id,type:`lease-lost`}))}).catch(I)},Math.max(1e3,Math.floor(s/2)));L({entry:S(e),type:`started`});try{return await o.execute(c,{attempt:e.attempts,entryId:e.id,key:e.key,signal:p}),p.aborted?(await E.transact(t=>h(t,{id:e.id,now:a(),ownerId:O})),{completed:0,deadLettered:0,processed:!0,retryScheduled:0}):await E.transact(t=>m(t,{id:e.id,now:a(),ownerId:O}))?(L({entry:S(e),type:`completed`}),{completed:1,deadLettered:0,processed:!0,retryScheduled:0}):(L({id:e.id,type:`lease-lost`}),{completed:0,deadLettered:0,processed:!0,retryScheduled:0})}catch(r){if(p.aborted||f(r))return await E.transact(t=>h(t,{id:e.id,now:a(),ownerId:O})),{completed:0,deadLettered:0,processed:!0,retryScheduled:0};let i=!1,s=0;try{if(i=e.attempts<(o.retry?.maxAttempts??1)&&(o.retry?.shouldRetry(r,e.attempts)??!1),!i)return U(e,r);if(s=o.retry?.delay?.(e.attempts-1)??(0,t.backoff)(e.attempts-1),!Number.isFinite(s)||s<0)throw new n(`job "${e.name}" retry delay must be finite and non-negative`)}catch(t){return U(e,t)}let c=a()+s;return await E.transact(t=>g(t,{availableAt:c,failure:d(r,a()),id:e.id,now:a(),ownerId:O}))?(L({entry:S({...e,availableAt:c,status:`queued`}),type:`retry-scheduled`}),{completed:0,deadLettered:0,processed:!0,retryScheduled:1}):(L({id:e.id,type:`lease-lost`}),{completed:0,deadLettered:0,processed:!0,retryScheduled:0})}finally{clearInterval(_)}},G=async e=>{if(e?.aborted)throw new DOMException(`The operation was aborted`,`AbortError`);if(D.signal.aborted)return{completed:0,deadLettered:0,processed:!1,retryScheduled:0};let t=await E.transact(e=>p(e,{leaseDuration:s,now:a(),ownerId:O}));if(!t)return{completed:0,deadLettered:0,processed:!1,retryScheduled:0};P.set(t.id,t);try{return await W(t,e)}finally{P.delete(t.id)}},K=()=>{M||A||(M=(async()=>{for(;(await G()).processed&&!A;);await V()})().catch(I).finally(()=>{M=void 0}))},q=E.subscribe(B),J=()=>{Y()};c?.addEventListener(`abort`,J,{once:!0});let Y=async()=>{if(A)return;A=!0,L({type:`dispose`}),k.clear(),z(),c?.removeEventListener(`abort`,J),q(),D.abort();let e=[...P.values()];P.clear(),await Promise.all(e.map(e=>E.transact(t=>h(t,{id:e.id,now:a(),ownerId:O}))))};return{get disposalSignal(){return D.signal},async dispose(){await Y()},get disposed(){return A},async enqueue(e,t,r){R();let s=o[e];if(!s)throw new i(`no job definition is registered for "${e}"`);let c=a(),d=T(r?.availableAt,c),f=l(s.validate,t),p=s.key(f);if(typeof p!=`string`||p.length===0)throw new n(`job "${e}" returned an empty key`);let m={attempts:0,availableAt:d,createdAt:c,id:crypto.randomUUID(),key:p,name:e,payload:u(f),status:`queued`,updatedAt:c,version:s.version};await E.transact(e=>x(e,m));let h=S(m);return L({entry:h,type:`enqueued`}),B(),h},flush(e){if(R(),F)return F;z();let t=(async()=>{await M;let t=C();try{for(;;){let n=await G(e?.signal);if(!n.processed||(t.processed+=1,t.completed+=n.completed,t.deadLettered+=n.deadLettered,t.retryScheduled+=n.retryScheduled,A))return t}}finally{F=void 0,B()}})();return F=t,t},async list(e){return R(),(await E.list(e)).map(S)},async remove(e){R();let t=await E.transact(t=>b(t,e));return t.status===`removed`&&L({id:e,type:`removed`}),t},async retry(e){R();let t=await E.transact(t=>y(t,e,a()));return t.status===`retried`&&B(),t},start(){R(),!j&&(j=!0,B())},async stats(){return R(),E.transact(e=>e.countByStatus())},tap(e,t){if(R(),k.add(e),t?.signal){if(t.signal.aborted)return k.delete(e),()=>{};let n=()=>k.delete(e);return t.signal.addEventListener(`abort`,n,{once:!0}),()=>{k.delete(e),t.signal?.removeEventListener(`abort`,n)}}return()=>k.delete(e)},async[Symbol.asyncDispose](){await Y()}}}return e.PostmasterDisposedError=r,e.PostmasterError=n,e.PostmasterJobError=i,e.createPostmaster=E,e.defineJobs=c,e})({},Arsenal);
|
|
2
2
|
//# sourceMappingURL=postmaster.iife.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postmaster.iife.js","names":[],"sources":["../src/errors.ts","../src/definitions.ts","../../arsenal/dist/async/backoff.js","../src/_json.ts","../src/store-ops.ts","../src/postmaster.ts"],"sourcesContent":["export class PostmasterError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class PostmasterDisposedError extends PostmasterError {}\nexport class PostmasterJobError extends PostmasterError {}\n","import { PostmasterError } from './errors.ts';\nimport type { JobDefinition, JobDefinitions, RetryPolicy } from './types.ts';\n\nfunction validateRetry(retry: RetryPolicy | undefined, name: string): void {\n if (!retry) return;\n\n if (!Number.isInteger(retry.maxAttempts) || retry.maxAttempts < 1) {\n throw new PostmasterError(`job \"${name}\" retry.maxAttempts must be a positive integer`);\n }\n}\n\nfunction validateJob(name: string, job: JobDefinition<unknown>): void {\n if (!Number.isInteger(job.version) || job.version < 1) {\n throw new PostmasterError(`job \"${name}\" version must be a positive integer`);\n }\n\n if (typeof job.execute !== 'function' || typeof job.key !== 'function') {\n throw new PostmasterError(`job \"${name}\" must define execute and key`);\n }\n\n validateRetry(job.retry, name);\n}\n\nexport function defineJobs<const J extends JobDefinitions>(jobs: J): J {\n for (const [name, job] of Object.entries(jobs)) validateJob(name, job);\n\n return jobs;\n}\n","//#region src/async/backoff.ts\nfunction e(e, t = 3e4) {\n\treturn Math.min(1e3 * 2 ** Math.max(0, Number.isNaN(e) ? 0 : Math.floor(e)), t);\n}\n//#endregion\nexport { e as backoff };\n\n//# sourceMappingURL=backoff.js.map","import { PostmasterError } from './errors.ts';\nimport type { JsonValue, StoredFailure, Validate } from './types.ts';\n\nexport function runValidate<T>(validate: Validate<T> | undefined, value: unknown): T {\n if (!validate) return value as T;\n if (typeof validate === 'function') return validate(value);\n return validate.parse(value);\n}\n\nexport function asJsonValue(value: unknown): JsonValue {\n let serialized: string | undefined;\n\n try {\n serialized = JSON.stringify(value);\n } catch (error) {\n throw new PostmasterError('job payload must be JSON-serializable', { cause: error });\n }\n\n if (serialized === undefined) {\n throw new PostmasterError('job payload must be JSON-serializable');\n }\n\n return JSON.parse(serialized) as JsonValue;\n}\n\nexport function failureFrom(error: unknown, now: number): StoredFailure {\n if (error instanceof Error) {\n return { message: error.message, name: error.name, occurredAt: now };\n }\n\n return { message: String(error), name: 'Error', occurredAt: now };\n}\n\nexport function isAbortError(error: unknown): boolean {\n return error instanceof DOMException && error.name === 'AbortError';\n}\n","import type { PostmasterEntry, RemoveResult, RetryResult, StoredFailure, StoredJob, StoreTx } from './types.ts';\n\nexport interface ClaimParams {\n readonly leaseDuration: number;\n readonly now: number;\n readonly ownerId: string;\n}\n\nexport interface OwnedParams {\n readonly id: string;\n readonly now: number;\n readonly ownerId: string;\n}\n\nexport interface RescheduleParams extends OwnedParams {\n readonly availableAt: number;\n readonly failure: StoredFailure;\n}\n\nexport interface DeadLetterParams extends OwnedParams {\n readonly failure: StoredFailure;\n}\n\n/** Claim the earliest eligible job atomically. Returns the claimed entry or undefined. */\nexport async function claimJob(tx: StoreTx, params: ClaimParams): Promise<StoredJob | undefined> {\n const entry = await tx.findClaimable(params.now);\n if (!entry) return undefined;\n const claimed: StoredJob = {\n ...entry,\n attempts: entry.attempts + 1,\n failure: undefined,\n leaseExpiresAt: params.now + params.leaseDuration,\n ownerId: params.ownerId,\n status: 'running',\n updatedAt: params.now,\n };\n await tx.put(claimed);\n return claimed;\n}\n\n/** Complete and delete a job. Returns false if the owner is stale or the job is gone. */\nexport async function completeJob(tx: StoreTx, params: OwnedParams): Promise<boolean> {\n const entry = await tx.get(params.id);\n if (!entry || entry.status !== 'running' || entry.ownerId !== params.ownerId) return false;\n await tx.delete(params.id);\n return true;\n}\n\n/** Release a claimed job back to queued without consuming an attempt. Returns false if stale. */\nexport async function releaseJob(tx: StoreTx, params: OwnedParams): Promise<boolean> {\n const entry = await tx.get(params.id);\n if (!entry || entry.status !== 'running' || entry.ownerId !== params.ownerId) return false;\n await tx.put({\n ...entry,\n leaseExpiresAt: undefined,\n ownerId: undefined,\n status: 'queued',\n updatedAt: params.now,\n });\n return true;\n}\n\n/** Reschedule a running job back to queued with a future availableAt. Returns false if stale. */\nexport async function rescheduleJob(tx: StoreTx, params: RescheduleParams): Promise<boolean> {\n const entry = await tx.get(params.id);\n if (!entry || entry.status !== 'running' || entry.ownerId !== params.ownerId) return false;\n await tx.put({\n ...entry,\n availableAt: params.availableAt,\n failure: params.failure,\n leaseExpiresAt: undefined,\n ownerId: undefined,\n status: 'queued',\n updatedAt: params.now,\n });\n return true;\n}\n\n/** Move a running job to dead-letter. Returns false if stale. */\nexport async function deadLetterJob(tx: StoreTx, params: DeadLetterParams): Promise<boolean> {\n const entry = await tx.get(params.id);\n if (!entry || entry.status !== 'running' || entry.ownerId !== params.ownerId) return false;\n await tx.put({\n ...entry,\n failure: params.failure,\n leaseExpiresAt: undefined,\n ownerId: undefined,\n status: 'dead-letter',\n updatedAt: params.now,\n });\n return true;\n}\n\n/** Renew a lease for the current owner. Returns false if stale. */\nexport async function renewLeaseJob(tx: StoreTx, params: OwnedParams & { leaseExpiresAt: number }): Promise<boolean> {\n const entry = await tx.get(params.id);\n if (!entry || entry.status !== 'running' || entry.ownerId !== params.ownerId) return false;\n await tx.put({ ...entry, leaseExpiresAt: params.leaseExpiresAt, updatedAt: params.now });\n return true;\n}\n\n/** Retry a dead-letter job back to queued. Returns discriminated result. */\nexport async function retryJob(tx: StoreTx, id: string, now: number): Promise<RetryResult> {\n const entry = await tx.get(id);\n if (!entry) return { status: 'not-found' };\n if (entry.status === 'running') return { status: 'running' };\n if (entry.status !== 'dead-letter') return { status: 'not-dead-letter' };\n const retried: StoredJob = {\n ...entry,\n availableAt: now,\n failure: undefined,\n status: 'queued',\n updatedAt: now,\n };\n await tx.put(retried);\n return { entry: toEntry(retried), status: 'retried' };\n}\n\n/** Remove a queued or dead-letter job. Running jobs cannot be removed. */\nexport async function removeJob(tx: StoreTx, id: string): Promise<RemoveResult> {\n const entry = await tx.get(id);\n if (!entry) return { status: 'not-found' };\n if (entry.status === 'running') return { status: 'running' };\n await tx.delete(id);\n return { id, status: 'removed' };\n}\n\n/** Enqueue a new job. Rejects duplicate ids. */\nexport async function enqueueJob(tx: StoreTx, entry: StoredJob): Promise<void> {\n const existing = await tx.get(entry.id);\n if (existing) throw new Error(`Postmaster entry \"${entry.id}\" already exists`);\n await tx.put(entry);\n}\n\nexport function toEntry(entry: StoredJob): PostmasterEntry {\n return {\n attempts: entry.attempts,\n availableAt: entry.availableAt,\n createdAt: entry.createdAt,\n ...(entry.failure ? { failure: entry.failure } : {}),\n id: entry.id,\n key: entry.key,\n name: entry.name,\n status: entry.status,\n updatedAt: entry.updatedAt,\n version: entry.version,\n };\n}\n","import { backoff } from '@vielzeug/arsenal/async';\nimport { asJsonValue, failureFrom, isAbortError, runValidate } from './_json.ts';\nimport { PostmasterDisposedError, PostmasterError, PostmasterJobError } from './errors.ts';\nimport {\n claimJob,\n completeJob,\n deadLetterJob,\n enqueueJob,\n releaseJob,\n removeJob,\n renewLeaseJob,\n rescheduleJob,\n retryJob,\n toEntry,\n} from './store-ops.ts';\nimport type {\n CreatePostmasterOptions,\n EnqueueOptions,\n EntryFilter,\n FlushResult,\n InferJobPayload,\n JobContext,\n JobDefinition,\n JobDefinitions,\n Postmaster,\n PostmasterEntry,\n PostmasterEvent,\n PostmasterStats,\n RemoveResult,\n RetryResult,\n StoredJob,\n} from './types.ts';\n\ntype ExecutionResult = Omit<FlushResult, 'processed'> & { processed: boolean };\ntype MutableFlushResult = { -readonly [K in keyof FlushResult]: FlushResult[K] };\n\nconst emptyResult = (): FlushResult => ({ completed: 0, deadLettered: 0, processed: 0, retryScheduled: 0 });\n\nfunction mergeSignals(signals: readonly (AbortSignal | undefined)[]): AbortSignal {\n const controller = new AbortController();\n const active = signals.filter((signal): signal is AbortSignal => signal !== undefined);\n const abort = (): void => controller.abort();\n\n for (const signal of active) {\n if (signal.aborted) {\n abort();\n break;\n }\n signal.addEventListener('abort', abort, { once: true });\n }\n\n return controller.signal;\n}\n\nfunction assertLeaseDuration(value: number): void {\n if (!Number.isInteger(value) || value < 1_000) {\n throw new PostmasterError('leaseDuration must be a positive integer of at least 1000ms');\n }\n}\n\nfunction assertAvailableAt(value: number | undefined, defaultValue: number): number {\n if (value === undefined) return defaultValue;\n if (!Number.isFinite(value) || value < 0 || !Number.isSafeInteger(value)) {\n throw new PostmasterError('availableAt must be a finite non-negative safe integer');\n }\n return value;\n}\n\nexport function createPostmaster<J extends JobDefinitions>(options: CreatePostmasterOptions<J>): Postmaster<J> {\n const { clock = Date.now, jobs, leaseDuration = 30_000, signal, store } = options;\n assertLeaseDuration(leaseDuration);\n\n const controller = new AbortController();\n const ownerId = crypto.randomUUID();\n const tappers = new Set<(event: PostmasterEvent) => void>();\n let disposed = false;\n let started = false;\n let running: Promise<void> | undefined;\n let wakeTimer: ReturnType<typeof setTimeout> | undefined;\n const activeEntries = new Map<string, StoredJob>();\n let flushPromise: Promise<FlushResult> | undefined;\n\n const error = (reason: unknown): void => {\n const value = reason instanceof Error ? reason : new Error(String(reason));\n emitTap({ error: value, type: 'processor-error' });\n queueMicrotask(() => {\n throw value;\n });\n };\n\n const emitTap = (event: PostmasterEvent): void => {\n if (tappers.size === 0) return;\n for (const tapper of tappers) {\n try {\n tapper(event);\n } catch {\n // Observability must not affect postmaster behavior.\n }\n }\n };\n\n const assertLive = (): void => {\n if (disposed) throw new PostmasterDisposedError('Postmaster is disposed');\n };\n\n const clearWakeTimer = (): void => {\n if (wakeTimer !== undefined) clearTimeout(wakeTimer);\n wakeTimer = undefined;\n };\n\n const wake = (): void => {\n clearWakeTimer();\n if (started && !disposed) pump();\n };\n\n const schedule = async (): Promise<void> => {\n if (!started || disposed) return;\n const next = await store.transact((tx) => tx.findNextWake(clock()));\n if (next === undefined) return;\n const delay = Math.max(0, Math.min(next - clock(), 2_147_483_647));\n wakeTimer = setTimeout(wake, delay);\n };\n\n const resolveJob = (entry: StoredJob): JobDefinition<unknown> => {\n const job = jobs[entry.name];\n if (!job) throw new PostmasterJobError(`no job definition is registered for \"${entry.name}\"`);\n return job;\n };\n\n const deadLetter = async (entry: StoredJob, reason: unknown): Promise<ExecutionResult> => {\n const finalized = await store.transact((tx) =>\n deadLetterJob(tx, { failure: failureFrom(reason, clock()), id: entry.id, now: clock(), ownerId }),\n );\n\n if (!finalized) {\n emitTap({ id: entry.id, type: 'lease-lost' });\n return { completed: 0, deadLettered: 0, processed: true, retryScheduled: 0 };\n }\n\n emitTap({ entry: toEntry({ ...entry, status: 'dead-letter' }), type: 'dead-lettered' });\n return { completed: 0, deadLettered: 1, processed: true, retryScheduled: 0 };\n };\n\n const execute = async (entry: StoredJob, externalSignal?: AbortSignal): Promise<ExecutionResult> => {\n let job: JobDefinition<unknown>;\n let payload: unknown;\n\n try {\n job = resolveJob(entry);\n if (entry.version > job.version) {\n throw new PostmasterJobError(\n `job \"${entry.name}\" version ${entry.version} is newer than the registered version`,\n );\n }\n const raw = entry.version === job.version ? entry.payload : job.migrate?.(entry.payload, entry.version);\n if (raw === undefined) {\n throw new PostmasterJobError(`job \"${entry.name}\" version ${entry.version} cannot be migrated`);\n }\n payload = runValidate(job.validate, raw);\n } catch (reason) {\n return deadLetter(entry, reason);\n }\n\n const taskController = new AbortController();\n const taskSignal = mergeSignals([controller.signal, externalSignal, taskController.signal]);\n const heartbeat = setInterval(\n () => {\n void store\n .transact((tx) =>\n renewLeaseJob(tx, { id: entry.id, leaseExpiresAt: clock() + leaseDuration, now: clock(), ownerId }),\n )\n .then((renewed) => {\n if (!renewed) {\n taskController.abort();\n emitTap({ id: entry.id, type: 'lease-lost' });\n }\n })\n .catch(error);\n },\n Math.max(1_000, Math.floor(leaseDuration / 2)),\n );\n\n emitTap({ entry: toEntry(entry), type: 'started' });\n\n try {\n await job.execute(payload, {\n attempt: entry.attempts,\n entryId: entry.id,\n key: entry.key,\n signal: taskSignal,\n } as JobContext);\n\n if (taskSignal.aborted) {\n await store.transact((tx) => releaseJob(tx, { id: entry.id, now: clock(), ownerId }));\n return { completed: 0, deadLettered: 0, processed: true, retryScheduled: 0 };\n }\n\n const completed = await store.transact((tx) => completeJob(tx, { id: entry.id, now: clock(), ownerId }));\n if (!completed) {\n emitTap({ id: entry.id, type: 'lease-lost' });\n return { completed: 0, deadLettered: 0, processed: true, retryScheduled: 0 };\n }\n\n emitTap({ entry: toEntry(entry), type: 'completed' });\n return { completed: 1, deadLettered: 0, processed: true, retryScheduled: 0 };\n } catch (reason) {\n if (taskSignal.aborted || isAbortError(reason)) {\n await store.transact((tx) => releaseJob(tx, { id: entry.id, now: clock(), ownerId }));\n return { completed: 0, deadLettered: 0, processed: true, retryScheduled: 0 };\n }\n\n let shouldRetry = false;\n let delay = 0;\n\n try {\n shouldRetry =\n entry.attempts < (job.retry?.maxAttempts ?? 1) && (job.retry?.shouldRetry(reason, entry.attempts) ?? false);\n if (!shouldRetry) return deadLetter(entry, reason);\n delay = job.retry?.delay?.(entry.attempts - 1) ?? backoff(entry.attempts - 1);\n if (!Number.isFinite(delay) || delay < 0) {\n throw new PostmasterError(`job \"${entry.name}\" retry delay must be finite and non-negative`);\n }\n } catch (retryReason) {\n return deadLetter(entry, retryReason);\n }\n\n const availableAt = clock() + delay;\n const rescheduled = await store.transact((tx) =>\n rescheduleJob(tx, { availableAt, failure: failureFrom(reason, clock()), id: entry.id, now: clock(), ownerId }),\n );\n if (!rescheduled) {\n emitTap({ id: entry.id, type: 'lease-lost' });\n return { completed: 0, deadLettered: 0, processed: true, retryScheduled: 0 };\n }\n emitTap({ entry: toEntry({ ...entry, availableAt, status: 'queued' }), type: 'retry-scheduled' });\n return { completed: 0, deadLettered: 0, processed: true, retryScheduled: 1 };\n } finally {\n clearInterval(heartbeat);\n }\n };\n\n const processNext = async (externalSignal?: AbortSignal): Promise<ExecutionResult> => {\n if (externalSignal?.aborted) throw new DOMException('The operation was aborted', 'AbortError');\n if (controller.signal.aborted) return { completed: 0, deadLettered: 0, processed: false, retryScheduled: 0 };\n const entry = await store.transact((tx) => claimJob(tx, { leaseDuration, now: clock(), ownerId }));\n if (!entry) return { completed: 0, deadLettered: 0, processed: false, retryScheduled: 0 };\n activeEntries.set(entry.id, entry);\n try {\n return await execute(entry, externalSignal);\n } finally {\n activeEntries.delete(entry.id);\n }\n };\n\n const pump = (): void => {\n if (running || disposed) return;\n running = (async () => {\n for (;;) {\n const result = await processNext();\n if (!result.processed || disposed) break;\n }\n await schedule();\n })()\n .catch(error)\n .finally(() => {\n running = undefined;\n });\n };\n\n const unsubscribeStore = store.subscribe(wake);\n const externalAbort = (): void => {\n void dispose();\n };\n signal?.addEventListener('abort', externalAbort, { once: true });\n\n const dispose = async (): Promise<void> => {\n if (disposed) return;\n disposed = true;\n emitTap({ type: 'dispose' });\n tappers.clear();\n clearWakeTimer();\n signal?.removeEventListener('abort', externalAbort);\n unsubscribeStore();\n controller.abort();\n const entries = [...activeEntries.values()];\n activeEntries.clear();\n await Promise.all(\n entries.map((entry) => store.transact((tx) => releaseJob(tx, { id: entry.id, now: clock(), ownerId }))),\n );\n };\n\n return {\n get disposalSignal() {\n return controller.signal;\n },\n async dispose(): Promise<void> {\n await dispose();\n },\n get disposed() {\n return disposed;\n },\n async enqueue<K extends keyof J & string>(\n name: K,\n payload: InferJobPayload<J[K]>,\n options?: EnqueueOptions,\n ): Promise<PostmasterEntry> {\n assertLive();\n const job = jobs[name];\n if (!job) throw new PostmasterJobError(`no job definition is registered for \"${name}\"`);\n const now = clock();\n const availableAt = assertAvailableAt(options?.availableAt, now);\n const parsed = runValidate(job.validate, payload);\n const key = job.key(parsed);\n if (typeof key !== 'string' || key.length === 0) {\n throw new PostmasterError(`job \"${name}\" returned an empty key`);\n }\n const entry: StoredJob = {\n attempts: 0,\n availableAt,\n createdAt: now,\n id: crypto.randomUUID(),\n key,\n name,\n payload: asJsonValue(parsed),\n status: 'queued',\n updatedAt: now,\n version: job.version,\n };\n await store.transact((tx) => enqueueJob(tx, entry));\n const result = toEntry(entry);\n emitTap({ entry: result, type: 'enqueued' });\n wake();\n return result;\n },\n flush(options?: { signal?: AbortSignal }): Promise<FlushResult> {\n assertLive();\n if (flushPromise) return flushPromise;\n clearWakeTimer();\n const promise = (async (): Promise<FlushResult> => {\n await running;\n const result: MutableFlushResult = emptyResult();\n try {\n for (;;) {\n const next = await processNext(options?.signal);\n if (!next.processed) return result;\n result.processed += 1;\n result.completed += next.completed;\n result.deadLettered += next.deadLettered;\n result.retryScheduled += next.retryScheduled;\n if (disposed) return result;\n }\n } finally {\n flushPromise = undefined;\n wake();\n }\n })();\n flushPromise = promise;\n return promise;\n },\n async list(filter?: EntryFilter): Promise<PostmasterEntry[]> {\n assertLive();\n return (await store.list(filter)).map(toEntry);\n },\n async remove(id: string): Promise<RemoveResult> {\n assertLive();\n const result = await store.transact((tx) => removeJob(tx, id));\n if (result.status === 'removed') emitTap({ id, type: 'removed' });\n return result;\n },\n async retry(id: string): Promise<RetryResult> {\n assertLive();\n const result = await store.transact((tx) => retryJob(tx, id, clock()));\n if (result.status === 'retried') wake();\n return result;\n },\n async start(): Promise<void> {\n assertLive();\n if (started) return;\n started = true;\n wake();\n },\n async stats(): Promise<PostmasterStats> {\n assertLive();\n return store.transact((tx) => tx.countByStatus());\n },\n tap(handler: (event: PostmasterEvent) => void, opts?: { readonly signal?: AbortSignal }): () => void {\n assertLive();\n tappers.add(handler);\n\n if (opts?.signal) {\n if (opts.signal.aborted) {\n tappers.delete(handler);\n return () => {};\n }\n const onAbort = () => tappers.delete(handler);\n opts.signal.addEventListener('abort', onAbort, { once: true });\n return () => {\n tappers.delete(handler);\n opts.signal?.removeEventListener('abort', onAbort);\n };\n }\n\n return () => tappers.delete(handler);\n },\n async [Symbol.asyncDispose](): Promise<void> {\n await dispose();\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"postmaster.iife.js","names":[],"sources":["../src/errors.ts","../src/definitions.ts","../src/_json.ts","../src/store-ops.ts","../src/postmaster.ts"],"sourcesContent":["export class PostmasterError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class PostmasterDisposedError extends PostmasterError {}\nexport class PostmasterJobError extends PostmasterError {}\n","import { PostmasterError } from './errors.ts';\nimport type { JobDefinition, JobDefinitions, RetryPolicy } from './types.ts';\n\nfunction validateRetry(retry: RetryPolicy | undefined, name: string): void {\n if (!retry) return;\n\n if (!Number.isInteger(retry.maxAttempts) || retry.maxAttempts < 1) {\n throw new PostmasterError(`job \"${name}\" retry.maxAttempts must be a positive integer`);\n }\n}\n\nfunction validateMigrations(name: string, job: JobDefinition<unknown>): void {\n const migrations = job.migrate;\n if (!migrations) return;\n\n const keys = Object.keys(migrations);\n if (keys.length === 0) throw new PostmasterError(`job \"${name}\" migrations must not be empty`);\n\n const versions = keys\n .map((key) => {\n const version = Number(key);\n if (!Number.isInteger(version) || String(version) !== key) {\n throw new PostmasterError(`job \"${name}\" has invalid migration key \"${key}\"`);\n }\n if (version < 1 || version >= job.version) {\n throw new PostmasterError(\n `job \"${name}\" has invalid migration key \"${version}\" (valid range: 1..${job.version - 1})`,\n );\n }\n if (typeof migrations[version] !== 'function') {\n throw new PostmasterError(`job \"${name}\" migration from version ${version} must be a function`);\n }\n return version;\n })\n .sort((a, b) => a - b);\n\n for (let index = 1; index < versions.length; index++) {\n if (versions[index] !== versions[index - 1] + 1) {\n throw new PostmasterError(`job \"${name}\" missing migration from version ${versions[index - 1] + 1}`);\n }\n }\n\n if (versions.at(-1) !== job.version - 1) {\n throw new PostmasterError(`job \"${name}\" migrations must lead into version ${job.version}`);\n }\n}\n\nfunction validateJob(name: string, job: JobDefinition<unknown>): void {\n if (!Number.isInteger(job.version) || job.version < 1) {\n throw new PostmasterError(`job \"${name}\" version must be a positive integer`);\n }\n\n if (typeof job.execute !== 'function' || typeof job.key !== 'function') {\n throw new PostmasterError(`job \"${name}\" must define execute and key`);\n }\n\n validateRetry(job.retry, name);\n validateMigrations(name, job);\n}\n\nexport function defineJobs<const J extends JobDefinitions>(jobs: J): J {\n for (const [name, job] of Object.entries(jobs)) validateJob(name, job);\n\n return jobs;\n}\n","import { PostmasterError } from './errors.ts';\nimport type { JsonValue, StoredFailure, Validate } from './types.ts';\n\nexport function runValidate<T>(validate: Validate<T> | undefined, value: unknown): T {\n if (!validate) return value as T;\n if (typeof validate === 'function') return validate(value);\n return validate.parse(value);\n}\n\nexport function asJsonValue(value: unknown): JsonValue {\n let serialized: string | undefined;\n\n try {\n serialized = JSON.stringify(value);\n } catch (error) {\n throw new PostmasterError('job payload must be JSON-serializable', { cause: error });\n }\n\n if (serialized === undefined) {\n throw new PostmasterError('job payload must be JSON-serializable');\n }\n\n return JSON.parse(serialized) as JsonValue;\n}\n\nexport function failureFrom(error: unknown, now: number): StoredFailure {\n if (error instanceof Error) {\n return { message: error.message, name: error.name, occurredAt: now };\n }\n\n return { message: String(error), name: 'Error', occurredAt: now };\n}\n\nexport function isAbortError(error: unknown): boolean {\n return error instanceof DOMException && error.name === 'AbortError';\n}\n","import { PostmasterJobError } from './errors.ts';\nimport type { PostmasterEntry, RemoveResult, RetryResult, StoredFailure, StoredJob, StoreTx } from './types.ts';\n\nexport interface ClaimParams {\n readonly leaseDuration: number;\n readonly now: number;\n readonly ownerId: string;\n}\n\nexport interface OwnedParams {\n readonly id: string;\n readonly now: number;\n readonly ownerId: string;\n}\n\nexport interface RescheduleParams extends OwnedParams {\n readonly availableAt: number;\n readonly failure: StoredFailure;\n}\n\nexport interface DeadLetterParams extends OwnedParams {\n readonly failure: StoredFailure;\n}\n\n/** Claim the earliest eligible job atomically. Returns the claimed entry or undefined. */\nexport async function claimJob(tx: StoreTx, params: ClaimParams): Promise<StoredJob | undefined> {\n const entry = await tx.findClaimable(params.now);\n if (!entry) return undefined;\n const claimed: StoredJob = {\n ...entry,\n attempts: entry.attempts + 1,\n failure: undefined,\n leaseExpiresAt: params.now + params.leaseDuration,\n ownerId: params.ownerId,\n status: 'running',\n updatedAt: params.now,\n };\n await tx.put(claimed);\n return claimed;\n}\n\n/** Complete and delete a job. Returns false if the owner is stale or the job is gone. */\nexport async function completeJob(tx: StoreTx, params: OwnedParams): Promise<boolean> {\n const entry = await tx.get(params.id);\n if (!entry || entry.status !== 'running' || entry.ownerId !== params.ownerId) return false;\n await tx.delete(params.id);\n return true;\n}\n\n/** Release a claimed job back to queued without consuming an attempt. Returns false if stale. */\nexport async function releaseJob(tx: StoreTx, params: OwnedParams): Promise<boolean> {\n const entry = await tx.get(params.id);\n if (!entry || entry.status !== 'running' || entry.ownerId !== params.ownerId) return false;\n await tx.put({\n ...entry,\n leaseExpiresAt: undefined,\n ownerId: undefined,\n status: 'queued',\n updatedAt: params.now,\n });\n return true;\n}\n\n/** Reschedule a running job back to queued with a future availableAt. Returns false if stale. */\nexport async function rescheduleJob(tx: StoreTx, params: RescheduleParams): Promise<boolean> {\n const entry = await tx.get(params.id);\n if (!entry || entry.status !== 'running' || entry.ownerId !== params.ownerId) return false;\n await tx.put({\n ...entry,\n availableAt: params.availableAt,\n failure: params.failure,\n leaseExpiresAt: undefined,\n ownerId: undefined,\n status: 'queued',\n updatedAt: params.now,\n });\n return true;\n}\n\n/** Move a running job to dead-letter. Returns false if stale. */\nexport async function deadLetterJob(tx: StoreTx, params: DeadLetterParams): Promise<boolean> {\n const entry = await tx.get(params.id);\n if (!entry || entry.status !== 'running' || entry.ownerId !== params.ownerId) return false;\n await tx.put({\n ...entry,\n failure: params.failure,\n leaseExpiresAt: undefined,\n ownerId: undefined,\n status: 'dead-letter',\n updatedAt: params.now,\n });\n return true;\n}\n\n/** Renew a lease for the current owner. Returns false if stale. */\nexport async function renewLeaseJob(tx: StoreTx, params: OwnedParams & { leaseExpiresAt: number }): Promise<boolean> {\n const entry = await tx.get(params.id);\n if (!entry || entry.status !== 'running' || entry.ownerId !== params.ownerId) return false;\n await tx.put({ ...entry, leaseExpiresAt: params.leaseExpiresAt, updatedAt: params.now });\n return true;\n}\n\n/** Retry a dead-letter job back to queued. Returns discriminated result. */\nexport async function retryJob(tx: StoreTx, id: string, now: number): Promise<RetryResult> {\n const entry = await tx.get(id);\n if (!entry) return { status: 'not-found' };\n if (entry.status === 'running') return { status: 'running' };\n if (entry.status !== 'dead-letter') return { status: 'not-dead-letter' };\n const retried: StoredJob = {\n ...entry,\n availableAt: now,\n failure: undefined,\n status: 'queued',\n updatedAt: now,\n };\n await tx.put(retried);\n return { entry: toEntry(retried), status: 'retried' };\n}\n\n/** Remove a queued or dead-letter job. Running jobs cannot be removed. */\nexport async function removeJob(tx: StoreTx, id: string): Promise<RemoveResult> {\n const entry = await tx.get(id);\n if (!entry) return { status: 'not-found' };\n if (entry.status === 'running') return { status: 'running' };\n await tx.delete(id);\n return { id, status: 'removed' };\n}\n\n/** Enqueue a new job. Rejects duplicate ids. */\nexport async function enqueueJob(tx: StoreTx, entry: StoredJob): Promise<void> {\n const existing = await tx.get(entry.id);\n if (existing) throw new PostmasterJobError(`Postmaster entry \"${entry.id}\" already exists`);\n await tx.put(entry);\n}\n\nexport function toEntry(entry: StoredJob): PostmasterEntry {\n return {\n attempts: entry.attempts,\n availableAt: entry.availableAt,\n createdAt: entry.createdAt,\n ...(entry.failure ? { failure: entry.failure } : {}),\n id: entry.id,\n key: entry.key,\n name: entry.name,\n status: entry.status,\n updatedAt: entry.updatedAt,\n version: entry.version,\n };\n}\n","import { backoff } from '@vielzeug/arsenal';\nimport { asJsonValue, failureFrom, isAbortError, runValidate } from './_json.ts';\nimport { PostmasterDisposedError, PostmasterError, PostmasterJobError } from './errors.ts';\nimport {\n claimJob,\n completeJob,\n deadLetterJob,\n enqueueJob,\n releaseJob,\n removeJob,\n renewLeaseJob,\n rescheduleJob,\n retryJob,\n toEntry,\n} from './store-ops.ts';\nimport type {\n CreatePostmasterOptions,\n EnqueueOptions,\n EntryFilter,\n FlushResult,\n InferJobPayload,\n JobContext,\n JobDefinition,\n JobDefinitions,\n Postmaster,\n PostmasterEntry,\n PostmasterEvent,\n PostmasterStats,\n RemoveResult,\n RetryResult,\n StoredJob,\n} from './types.ts';\n\ntype ExecutionResult = Omit<FlushResult, 'processed'> & { processed: boolean };\ntype MutableFlushResult = { -readonly [K in keyof FlushResult]: FlushResult[K] };\n\nconst emptyResult = (): FlushResult => ({ completed: 0, deadLettered: 0, processed: 0, retryScheduled: 0 });\n\nfunction assertLeaseDuration(value: number): void {\n if (!Number.isInteger(value) || value < 1_000) {\n throw new PostmasterError('leaseDuration must be a positive integer of at least 1000ms');\n }\n}\n\nfunction assertAvailableAt(value: number | undefined, defaultValue: number): number {\n if (value === undefined) return defaultValue;\n if (!Number.isFinite(value) || value < 0 || !Number.isSafeInteger(value)) {\n throw new PostmasterError('availableAt must be a finite non-negative safe integer');\n }\n return value;\n}\n\nexport function createPostmaster<J extends JobDefinitions>(options: CreatePostmasterOptions<J>): Postmaster<J> {\n const { clock = Date.now, jobs, leaseDuration = 30_000, signal, store } = options;\n assertLeaseDuration(leaseDuration);\n\n const controller = new AbortController();\n const ownerId = crypto.randomUUID();\n const tappers = new Set<(event: PostmasterEvent) => void>();\n let disposed = false;\n let started = false;\n let running: Promise<void> | undefined;\n let wakeTimer: ReturnType<typeof setTimeout> | undefined;\n const activeEntries = new Map<string, StoredJob>();\n let flushPromise: Promise<FlushResult> | undefined;\n\n const error = (reason: unknown): void => {\n const value = reason instanceof Error ? reason : new Error(String(reason));\n emitTap({ error: value, type: 'processor-error' });\n };\n\n const emitTap = (event: PostmasterEvent): void => {\n if (tappers.size === 0) return;\n for (const tapper of tappers) {\n try {\n tapper(event);\n } catch {\n // Observability must not affect postmaster behavior.\n }\n }\n };\n\n const assertLive = (): void => {\n if (disposed) throw new PostmasterDisposedError('Postmaster is disposed');\n };\n\n const clearWakeTimer = (): void => {\n if (wakeTimer !== undefined) clearTimeout(wakeTimer);\n wakeTimer = undefined;\n };\n\n const wake = (): void => {\n clearWakeTimer();\n if (started && !disposed) pump();\n };\n\n const schedule = async (): Promise<void> => {\n if (!started || disposed) return;\n const next = await store.transact((tx) => tx.findNextWake(clock()));\n if (next === undefined) return;\n const delay = Math.max(0, Math.min(next - clock(), 2_147_483_647));\n wakeTimer = setTimeout(wake, delay);\n };\n\n const resolveJob = (entry: StoredJob): JobDefinition<unknown> => {\n const job = jobs[entry.name];\n if (!job) throw new PostmasterJobError(`no job definition is registered for \"${entry.name}\"`);\n return job;\n };\n\n const deadLetter = async (entry: StoredJob, reason: unknown): Promise<ExecutionResult> => {\n const finalized = await store.transact((tx) =>\n deadLetterJob(tx, { failure: failureFrom(reason, clock()), id: entry.id, now: clock(), ownerId }),\n );\n\n if (!finalized) {\n emitTap({ id: entry.id, type: 'lease-lost' });\n return { completed: 0, deadLettered: 0, processed: true, retryScheduled: 0 };\n }\n\n emitTap({ entry: toEntry({ ...entry, status: 'dead-letter' }), type: 'dead-lettered' });\n return { completed: 0, deadLettered: 1, processed: true, retryScheduled: 0 };\n };\n\n const execute = async (entry: StoredJob, externalSignal?: AbortSignal): Promise<ExecutionResult> => {\n let job: JobDefinition<unknown>;\n let payload: unknown;\n\n try {\n job = resolveJob(entry);\n if (entry.version > job.version) {\n throw new PostmasterJobError(\n `job \"${entry.name}\" version ${entry.version} is newer than the registered version`,\n );\n }\n let raw: unknown = entry.payload;\n if (entry.version < job.version) {\n const migrations = job.migrate;\n if (!migrations) {\n throw new PostmasterJobError(`job \"${entry.name}\" has no migrations`);\n }\n for (let v = entry.version; v < job.version; v++) {\n const step = migrations[v];\n if (typeof step !== 'function') {\n throw new PostmasterJobError(`job \"${entry.name}\" does not support stored version ${entry.version}`);\n }\n raw = step(raw);\n if (raw === undefined) {\n throw new PostmasterJobError(`job \"${entry.name}\" migration from version ${v} returned undefined`);\n }\n }\n }\n payload = runValidate(job.validate, raw);\n } catch (reason) {\n return deadLetter(entry, reason);\n }\n\n const taskController = new AbortController();\n const taskSignal = AbortSignal.any(\n [controller.signal, externalSignal, taskController.signal].filter((s): s is AbortSignal => s !== undefined),\n );\n const heartbeat = setInterval(\n () => {\n void store\n .transact((tx) =>\n renewLeaseJob(tx, { id: entry.id, leaseExpiresAt: clock() + leaseDuration, now: clock(), ownerId }),\n )\n .then((renewed) => {\n if (!renewed) {\n taskController.abort();\n emitTap({ id: entry.id, type: 'lease-lost' });\n }\n })\n .catch(error);\n },\n Math.max(1_000, Math.floor(leaseDuration / 2)),\n );\n\n emitTap({ entry: toEntry(entry), type: 'started' });\n\n try {\n await job.execute(payload, {\n attempt: entry.attempts,\n entryId: entry.id,\n key: entry.key,\n signal: taskSignal,\n } as JobContext);\n\n if (taskSignal.aborted) {\n await store.transact((tx) => releaseJob(tx, { id: entry.id, now: clock(), ownerId }));\n return { completed: 0, deadLettered: 0, processed: true, retryScheduled: 0 };\n }\n\n const completed = await store.transact((tx) => completeJob(tx, { id: entry.id, now: clock(), ownerId }));\n if (!completed) {\n emitTap({ id: entry.id, type: 'lease-lost' });\n return { completed: 0, deadLettered: 0, processed: true, retryScheduled: 0 };\n }\n\n emitTap({ entry: toEntry(entry), type: 'completed' });\n return { completed: 1, deadLettered: 0, processed: true, retryScheduled: 0 };\n } catch (reason) {\n if (taskSignal.aborted || isAbortError(reason)) {\n await store.transact((tx) => releaseJob(tx, { id: entry.id, now: clock(), ownerId }));\n return { completed: 0, deadLettered: 0, processed: true, retryScheduled: 0 };\n }\n\n let shouldRetry = false;\n let delay = 0;\n\n try {\n shouldRetry =\n entry.attempts < (job.retry?.maxAttempts ?? 1) && (job.retry?.shouldRetry(reason, entry.attempts) ?? false);\n if (!shouldRetry) return deadLetter(entry, reason);\n delay = job.retry?.delay?.(entry.attempts - 1) ?? backoff(entry.attempts - 1);\n if (!Number.isFinite(delay) || delay < 0) {\n throw new PostmasterError(`job \"${entry.name}\" retry delay must be finite and non-negative`);\n }\n } catch (retryReason) {\n return deadLetter(entry, retryReason);\n }\n\n const availableAt = clock() + delay;\n const rescheduled = await store.transact((tx) =>\n rescheduleJob(tx, { availableAt, failure: failureFrom(reason, clock()), id: entry.id, now: clock(), ownerId }),\n );\n if (!rescheduled) {\n emitTap({ id: entry.id, type: 'lease-lost' });\n return { completed: 0, deadLettered: 0, processed: true, retryScheduled: 0 };\n }\n emitTap({ entry: toEntry({ ...entry, availableAt, status: 'queued' }), type: 'retry-scheduled' });\n return { completed: 0, deadLettered: 0, processed: true, retryScheduled: 1 };\n } finally {\n clearInterval(heartbeat);\n }\n };\n\n const processNext = async (externalSignal?: AbortSignal): Promise<ExecutionResult> => {\n if (externalSignal?.aborted) throw new DOMException('The operation was aborted', 'AbortError');\n if (controller.signal.aborted) return { completed: 0, deadLettered: 0, processed: false, retryScheduled: 0 };\n const entry = await store.transact((tx) => claimJob(tx, { leaseDuration, now: clock(), ownerId }));\n if (!entry) return { completed: 0, deadLettered: 0, processed: false, retryScheduled: 0 };\n activeEntries.set(entry.id, entry);\n try {\n return await execute(entry, externalSignal);\n } finally {\n activeEntries.delete(entry.id);\n }\n };\n\n const pump = (): void => {\n if (running || disposed) return;\n running = (async () => {\n for (;;) {\n const result = await processNext();\n if (!result.processed || disposed) break;\n }\n await schedule();\n })()\n .catch(error)\n .finally(() => {\n running = undefined;\n });\n };\n\n const unsubscribeStore = store.subscribe(wake);\n const externalAbort = (): void => {\n void dispose();\n };\n signal?.addEventListener('abort', externalAbort, { once: true });\n\n const dispose = async (): Promise<void> => {\n if (disposed) return;\n disposed = true;\n emitTap({ type: 'dispose' });\n tappers.clear();\n clearWakeTimer();\n signal?.removeEventListener('abort', externalAbort);\n unsubscribeStore();\n controller.abort();\n const entries = [...activeEntries.values()];\n activeEntries.clear();\n await Promise.all(\n entries.map((entry) => store.transact((tx) => releaseJob(tx, { id: entry.id, now: clock(), ownerId }))),\n );\n };\n\n return {\n get disposalSignal() {\n return controller.signal;\n },\n async dispose(): Promise<void> {\n await dispose();\n },\n get disposed() {\n return disposed;\n },\n async enqueue<K extends keyof J & string>(\n name: K,\n payload: InferJobPayload<J[K]>,\n options?: EnqueueOptions,\n ): Promise<PostmasterEntry> {\n assertLive();\n const job = jobs[name];\n if (!job) throw new PostmasterJobError(`no job definition is registered for \"${name}\"`);\n const now = clock();\n const availableAt = assertAvailableAt(options?.availableAt, now);\n const parsed = runValidate(job.validate, payload);\n const key = job.key(parsed);\n if (typeof key !== 'string' || key.length === 0) {\n throw new PostmasterError(`job \"${name}\" returned an empty key`);\n }\n const entry: StoredJob = {\n attempts: 0,\n availableAt,\n createdAt: now,\n id: crypto.randomUUID(),\n key,\n name,\n payload: asJsonValue(parsed),\n status: 'queued',\n updatedAt: now,\n version: job.version,\n };\n await store.transact((tx) => enqueueJob(tx, entry));\n const result = toEntry(entry);\n emitTap({ entry: result, type: 'enqueued' });\n wake();\n return result;\n },\n flush(options?: { signal?: AbortSignal }): Promise<FlushResult> {\n assertLive();\n if (flushPromise) return flushPromise;\n clearWakeTimer();\n const promise = (async (): Promise<FlushResult> => {\n await running;\n const result: MutableFlushResult = emptyResult();\n try {\n for (;;) {\n const next = await processNext(options?.signal);\n if (!next.processed) return result;\n result.processed += 1;\n result.completed += next.completed;\n result.deadLettered += next.deadLettered;\n result.retryScheduled += next.retryScheduled;\n if (disposed) return result;\n }\n } finally {\n flushPromise = undefined;\n wake();\n }\n })();\n flushPromise = promise;\n return promise;\n },\n async list(filter?: EntryFilter): Promise<PostmasterEntry[]> {\n assertLive();\n return (await store.list(filter)).map(toEntry);\n },\n async remove(id: string): Promise<RemoveResult> {\n assertLive();\n const result = await store.transact((tx) => removeJob(tx, id));\n if (result.status === 'removed') emitTap({ id, type: 'removed' });\n return result;\n },\n async retry(id: string): Promise<RetryResult> {\n assertLive();\n const result = await store.transact((tx) => retryJob(tx, id, clock()));\n if (result.status === 'retried') wake();\n return result;\n },\n start(): void {\n assertLive();\n if (started) return;\n started = true;\n wake();\n },\n async stats(): Promise<PostmasterStats> {\n assertLive();\n return store.transact((tx) => tx.countByStatus());\n },\n tap(handler: (event: PostmasterEvent) => void, opts?: { readonly signal?: AbortSignal }): () => void {\n assertLive();\n tappers.add(handler);\n\n if (opts?.signal) {\n if (opts.signal.aborted) {\n tappers.delete(handler);\n return () => {};\n }\n const onAbort = () => tappers.delete(handler);\n opts.signal.addEventListener('abort', onAbort, { once: true });\n return () => {\n tappers.delete(handler);\n opts.signal?.removeEventListener('abort', onAbort);\n };\n }\n\n return () => tappers.delete(handler);\n },\n async [Symbol.asyncDispose](): Promise<void> {\n await dispose();\n },\n };\n}\n"],"mappings":"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"}
|