@vielzeug/postmaster 2.0.0 → 2.1.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 +13 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.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/types.d.ts +13 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -45,6 +45,11 @@ const postmaster = createPostmaster({ jobs, store });
|
|
|
45
45
|
await postmaster.enqueue('createTodo', { id: crypto.randomUUID(), title: 'Buy milk' });
|
|
46
46
|
await postmaster.start();
|
|
47
47
|
|
|
48
|
+
// Delayed eligibility — the job persists now but cannot be claimed until `availableAt`:
|
|
49
|
+
await postmaster.enqueue('createTodo', { id: crypto.randomUUID(), title: 'Buy milk' }, {
|
|
50
|
+
availableAt: Date.now() + 60_000,
|
|
51
|
+
});
|
|
52
|
+
|
|
48
53
|
// On page unload:
|
|
49
54
|
await postmaster.dispose();
|
|
50
55
|
await store.dispose();
|
|
@@ -54,6 +59,14 @@ await store.dispose();
|
|
|
54
59
|
|
|
55
60
|
Postmaster provides at-least-once delivery. Every job must derive a stable idempotency key, and handlers must send or otherwise enforce that key. Never assume exactly-once execution.
|
|
56
61
|
|
|
62
|
+
## Delayed eligibility
|
|
63
|
+
|
|
64
|
+
`enqueue()` accepts an optional `availableAt` timestamp. The job persists immediately but cannot be claimed before that time. Postmaster does not guarantee execution at `availableAt` — only that the job will not be claimed earlier. A live processor (`start()` or `flush()`) is required for execution; in a browser, a closed page or suspended service worker will run the job when the processor next becomes active. Past timestamps remain immediately eligible.
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
await postmaster.enqueue('sendDigest', { userId }, { availableAt: Date.now() + 60_000 });
|
|
68
|
+
```
|
|
69
|
+
|
|
57
70
|
## Entry points
|
|
58
71
|
|
|
59
72
|
| Import | Purpose |
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { defineJobs } from './definitions.ts';
|
|
2
2
|
export { PostmasterDisposedError, PostmasterError, PostmasterJobError } from './errors.ts';
|
|
3
3
|
export { createPostmaster } from './postmaster.ts';
|
|
4
|
-
export type { CreatePostmasterOptions, EntryFilter, EntryStatus, FlushResult, InferJobPayload, JobContext, JobDefinition, JobDefinitions, JsonPrimitive, JsonValue, Postmaster, PostmasterEntry, PostmasterEvent, PostmasterStats, PostmasterStore, RemoveResult, RetryPolicy, RetryResult, StoredFailure, StoredJob, StoreTx, Validate, } from './types.ts';
|
|
4
|
+
export type { CreatePostmasterOptions, EnqueueOptions, EntryFilter, EntryStatus, FlushResult, InferJobPayload, JobContext, JobDefinition, JobDefinitions, JsonPrimitive, JsonValue, Postmaster, PostmasterEntry, PostmasterEvent, PostmasterStats, PostmasterStore, RemoveResult, RetryPolicy, RetryResult, StoredFailure, StoredJob, StoreTx, Validate, } from './types.ts';
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,uBAAuB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAC3F,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,YAAY,EACV,uBAAuB,EACvB,WAAW,EACX,WAAW,EACX,WAAW,EACX,eAAe,EACf,UAAU,EACV,aAAa,EACb,cAAc,EACd,aAAa,EACb,SAAS,EACT,UAAU,EACV,eAAe,EACf,eAAe,EACf,eAAe,EACf,eAAe,EACf,YAAY,EACZ,WAAW,EACX,WAAW,EACX,aAAa,EACb,SAAS,EACT,OAAO,EACP,QAAQ,GACT,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,uBAAuB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAC3F,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,YAAY,EACV,uBAAuB,EACvB,cAAc,EACd,WAAW,EACX,WAAW,EACX,WAAW,EACX,eAAe,EACf,UAAU,EACV,aAAa,EACb,cAAc,EACd,aAAa,EACb,SAAS,EACT,UAAU,EACV,eAAe,EACf,eAAe,EACf,eAAe,EACf,eAAe,EACf,YAAY,EACZ,WAAW,EACX,WAAW,EACX,aAAa,EACb,SAAS,EACT,OAAO,EACP,QAAQ,GACT,MAAM,YAAY,CAAC"}
|
package/dist/postmaster.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},t=class extends e{},n=class extends e{};function r(t,n){if(t&&(!Number.isInteger(t.maxAttempts)||t.maxAttempts<1))throw new e(`job "${n}" retry.maxAttempts must be a positive integer`)}function i(t,n){if(!Number.isInteger(n.version)||n.version<1)throw new e(`job "${t}" version must be a positive integer`);if(typeof n.execute!=`function`||typeof n.key!=`function`)throw new e(`job "${t}" must define execute and key`);r(n.retry,t)}function a(e){for(let[t,n]of Object.entries(e))i(t,n);return e}function o(e,t=3e4){return Math.min(1e3*2**Math.max(0,Number.isNaN(e)?0:Math.floor(e)),t)}function s(e,t){return e?typeof e==`function`?e(t):e.parse(t):t}function c(t){let n;try{n=JSON.stringify(t)}catch(t){throw new e(`job payload must be JSON-serializable`,{cause:t})}if(n===void 0)throw new e(`job payload must be JSON-serializable`);return JSON.parse(n)}function l(e,t){return e instanceof Error?{message:e.message,name:e.name,occurredAt:t}:{message:String(e),name:`Error`,occurredAt:t}}function u(e){return e instanceof DOMException&&e.name===`AbortError`}async function d(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 f(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 p(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 m(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 h(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 g(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 _(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:b(i),status:`retried`}}async function v(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 y(e,t){if(await e.get(t.id))throw Error(`Postmaster entry "${t.id}" already exists`);await e.put(t)}function b(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 x=()=>({completed:0,deadLettered:0,processed:0,retryScheduled:0});function S(e){let t=new AbortController,n=e.filter(e=>e!==void 0),r=()=>t.abort();for(let e of n){if(e.aborted){r();break}e.addEventListener(`abort`,r,{once:!0})}return t.signal}function C(t){if(!Number.isInteger(t)||t<1e3)throw new e(`leaseDuration must be a positive integer of at least 1000ms`)}function w(r){let{clock:i=Date.now,jobs:a,leaseDuration:
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},t=class extends e{},n=class extends e{};function r(t,n){if(t&&(!Number.isInteger(t.maxAttempts)||t.maxAttempts<1))throw new e(`job "${n}" retry.maxAttempts must be a positive integer`)}function i(t,n){if(!Number.isInteger(n.version)||n.version<1)throw new e(`job "${t}" version must be a positive integer`);if(typeof n.execute!=`function`||typeof n.key!=`function`)throw new e(`job "${t}" must define execute and key`);r(n.retry,t)}function a(e){for(let[t,n]of Object.entries(e))i(t,n);return e}function o(e,t=3e4){return Math.min(1e3*2**Math.max(0,Number.isNaN(e)?0:Math.floor(e)),t)}function s(e,t){return e?typeof e==`function`?e(t):e.parse(t):t}function c(t){let n;try{n=JSON.stringify(t)}catch(t){throw new e(`job payload must be JSON-serializable`,{cause:t})}if(n===void 0)throw new e(`job payload must be JSON-serializable`);return JSON.parse(n)}function l(e,t){return e instanceof Error?{message:e.message,name:e.name,occurredAt:t}:{message:String(e),name:`Error`,occurredAt:t}}function u(e){return e instanceof DOMException&&e.name===`AbortError`}async function d(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 f(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 p(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 m(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 h(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 g(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 _(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:b(i),status:`retried`}}async function v(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 y(e,t){if(await e.get(t.id))throw Error(`Postmaster entry "${t.id}" already exists`);await e.put(t)}function b(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 x=()=>({completed:0,deadLettered:0,processed:0,retryScheduled:0});function S(e){let t=new AbortController,n=e.filter(e=>e!==void 0),r=()=>t.abort();for(let e of n){if(e.aborted){r();break}e.addEventListener(`abort`,r,{once:!0})}return t.signal}function C(t){if(!Number.isInteger(t)||t<1e3)throw new e(`leaseDuration must be a positive integer of at least 1000ms`)}function w(t,n){if(t===void 0)return n;if(!Number.isFinite(t)||t<0||!Number.isSafeInteger(t))throw new e(`availableAt must be a finite non-negative safe integer`);return t}function T(r){let{clock:i=Date.now,jobs:a,leaseDuration:T=3e4,signal:E,store:D}=r;C(T);let O=new AbortController,k=crypto.randomUUID(),A=new Set,j=!1,M=!1,N,P,F=new Map,I,L=e=>{let t=e instanceof Error?e:Error(String(e));R({error:t,type:`processor-error`}),queueMicrotask(()=>{throw t})},R=e=>{if(A.size!==0)for(let t of A)try{t(e)}catch{}},z=()=>{if(j)throw new t(`Postmaster is disposed`)},B=()=>{P!==void 0&&clearTimeout(P),P=void 0},V=()=>{B(),M&&!j&&q()},H=async()=>{if(!M||j)return;let e=await D.transact(e=>e.findNextWake(i()));if(e===void 0)return;let t=Math.max(0,Math.min(e-i(),2147483647));P=setTimeout(V,t)},U=e=>{let t=a[e.name];if(!t)throw new n(`no job definition is registered for "${e.name}"`);return t},W=async(e,t)=>await D.transact(n=>h(n,{failure:l(t,i()),id:e.id,now:i(),ownerId:k}))?(R({entry:b({...e,status:`dead-letter`}),type:`dead-lettered`}),{completed:0,deadLettered:1,processed:!0,retryScheduled:0}):(R({id:e.id,type:`lease-lost`}),{completed:0,deadLettered:0,processed:!0,retryScheduled:0}),G=async(t,r)=>{let a,c;try{if(a=U(t),t.version>a.version)throw new n(`job "${t.name}" version ${t.version} is newer than the registered version`);let e=t.version===a.version?t.payload:a.migrate?.(t.payload,t.version);if(e===void 0)throw new n(`job "${t.name}" version ${t.version} cannot be migrated`);c=s(a.validate,e)}catch(e){return W(t,e)}let d=new AbortController,h=S([O.signal,r,d.signal]),_=setInterval(()=>{D.transact(e=>g(e,{id:t.id,leaseExpiresAt:i()+T,now:i(),ownerId:k})).then(e=>{e||(d.abort(),R({id:t.id,type:`lease-lost`}))}).catch(L)},Math.max(1e3,Math.floor(T/2)));R({entry:b(t),type:`started`});try{return await a.execute(c,{attempt:t.attempts,entryId:t.id,key:t.key,signal:h}),h.aborted?(await D.transact(e=>p(e,{id:t.id,now:i(),ownerId:k})),{completed:0,deadLettered:0,processed:!0,retryScheduled:0}):await D.transact(e=>f(e,{id:t.id,now:i(),ownerId:k}))?(R({entry:b(t),type:`completed`}),{completed:1,deadLettered:0,processed:!0,retryScheduled:0}):(R({id:t.id,type:`lease-lost`}),{completed:0,deadLettered:0,processed:!0,retryScheduled:0})}catch(n){if(h.aborted||u(n))return await D.transact(e=>p(e,{id:t.id,now:i(),ownerId:k})),{completed:0,deadLettered:0,processed:!0,retryScheduled:0};let r=!1,s=0;try{if(r=t.attempts<(a.retry?.maxAttempts??1)&&(a.retry?.shouldRetry(n,t.attempts)??!1),!r)return W(t,n);if(s=a.retry?.delay?.(t.attempts-1)??o(t.attempts-1),!Number.isFinite(s)||s<0)throw new e(`job "${t.name}" retry delay must be finite and non-negative`)}catch(e){return W(t,e)}let c=i()+s;return await D.transact(e=>m(e,{availableAt:c,failure:l(n,i()),id:t.id,now:i(),ownerId:k}))?(R({entry:b({...t,availableAt:c,status:`queued`}),type:`retry-scheduled`}),{completed:0,deadLettered:0,processed:!0,retryScheduled:1}):(R({id:t.id,type:`lease-lost`}),{completed:0,deadLettered:0,processed:!0,retryScheduled:0})}finally{clearInterval(_)}},K=async e=>{if(e?.aborted)throw new DOMException(`The operation was aborted`,`AbortError`);if(O.signal.aborted)return{completed:0,deadLettered:0,processed:!1,retryScheduled:0};let t=await D.transact(e=>d(e,{leaseDuration:T,now:i(),ownerId:k}));if(!t)return{completed:0,deadLettered:0,processed:!1,retryScheduled:0};F.set(t.id,t);try{return await G(t,e)}finally{F.delete(t.id)}},q=()=>{N||j||(N=(async()=>{for(;!(!(await K()).processed||j););await H()})().catch(L).finally(()=>{N=void 0}))},J=D.subscribe(V),Y=()=>{X()};E?.addEventListener(`abort`,Y,{once:!0});let X=async()=>{if(j)return;j=!0,R({type:`dispose`}),A.clear(),B(),E?.removeEventListener(`abort`,Y),J(),O.abort();let e=[...F.values()];F.clear(),await Promise.all(e.map(e=>D.transact(t=>p(t,{id:e.id,now:i(),ownerId:k}))))};return{get disposalSignal(){return O.signal},async dispose(){await X()},get disposed(){return j},async enqueue(t,r,o){z();let l=a[t];if(!l)throw new n(`no job definition is registered for "${t}"`);let u=i(),d=w(o?.availableAt,u),f=s(l.validate,r),p=l.key(f);if(typeof p!=`string`||p.length===0)throw new e(`job "${t}" returned an empty key`);let m={attempts:0,availableAt:d,createdAt:u,id:crypto.randomUUID(),key:p,name:t,payload:c(f),status:`queued`,updatedAt:u,version:l.version};await D.transact(e=>y(e,m));let h=b(m);return R({entry:h,type:`enqueued`}),V(),h},flush(e){if(z(),I)return I;B();let t=(async()=>{await N;let t=x();try{for(;;){let n=await K(e?.signal);if(!n.processed||(t.processed+=1,t.completed+=n.completed,t.deadLettered+=n.deadLettered,t.retryScheduled+=n.retryScheduled,j))return t}}finally{I=void 0,V()}})();return I=t,t},async list(e){return z(),(await D.list(e)).map(b)},async remove(e){z();let t=await D.transact(t=>v(t,e));return t.status===`removed`&&R({id:e,type:`removed`}),t},async retry(e){z();let t=await D.transact(t=>_(t,e,i()));return t.status===`retried`&&V(),t},async start(){z(),!M&&(M=!0,V())},async stats(){return z(),D.transact(e=>e.countByStatus())},tap(e,t){if(z(),A.add(e),t?.signal){if(t.signal.aborted)return A.delete(e),()=>{};let n=()=>A.delete(e);return t.signal.addEventListener(`abort`,n,{once:!0}),()=>{A.delete(e),t.signal?.removeEventListener(`abort`,n)}}return()=>A.delete(e)},async[Symbol.asyncDispose](){await X()}}}exports.PostmasterDisposedError=t,exports.PostmasterError=e,exports.PostmasterJobError=n,exports.createPostmaster=T,exports.defineJobs=a;
|
|
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 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\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>(name: K, payload: InferJobPayload<J[K]>): Promise<PostmasterEntry> {\n assertLive();\n const job = jobs[name];\n if (!job) throw new PostmasterJobError(`no job definition is registered for \"${name}\"`);\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 now = clock();\n const entry: StoredJob = {\n attempts: 0,\n availableAt: now,\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","../../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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"}
|
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,
|
|
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;AAqCpB,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,cAAc,EAAE,OAAO,EAAE,uBAAuB,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAoV7G"}
|
package/dist/postmaster.iife.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var Postmaster=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});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){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)}function o(e){for(let[t,n]of Object.entries(e))a(t,n);return e}function s(e,t=3e4){return Math.min(1e3*2**Math.max(0,Number.isNaN(e)?0:Math.floor(e)),t)}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 Error(`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){let t=new AbortController,n=e.filter(e=>e!==void 0),r=()=>t.abort();for(let e of n){if(e.aborted){r();break}e.addEventListener(`abort`,r,{once:!0})}return t.signal}function w(e){if(!Number.isInteger(e)||e<1e3)throw new t(`leaseDuration must be a positive integer of at least 1000ms`)}function T(e){let{clock:i=Date.now,jobs:a,leaseDuration:o=3e4,signal:
|
|
1
|
+
var Postmaster=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});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){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)}function o(e){for(let[t,n]of Object.entries(e))a(t,n);return e}function s(e,t=3e4){return Math.min(1e3*2**Math.max(0,Number.isNaN(e)?0:Math.floor(e)),t)}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 Error(`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){let t=new AbortController,n=e.filter(e=>e!==void 0),r=()=>t.abort();for(let e of n){if(e.aborted){r();break}e.addEventListener(`abort`,r,{once:!0})}return t.signal}function w(e){if(!Number.isInteger(e)||e<1e3)throw new t(`leaseDuration must be a positive integer of at least 1000ms`)}function T(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 E(e){let{clock:i=Date.now,jobs:a,leaseDuration:o=3e4,signal:E,store:D}=e;w(o);let O=new AbortController,k=crypto.randomUUID(),A=new Set,j=!1,M=!1,N,P,F=new Map,I,L=e=>{let t=e instanceof Error?e:Error(String(e));R({error:t,type:`processor-error`}),queueMicrotask(()=>{throw t})},R=e=>{if(A.size!==0)for(let t of A)try{t(e)}catch{}},z=()=>{if(j)throw new n(`Postmaster is disposed`)},B=()=>{P!==void 0&&clearTimeout(P),P=void 0},V=()=>{B(),M&&!j&&q()},H=async()=>{if(!M||j)return;let e=await D.transact(e=>e.findNextWake(i()));if(e===void 0)return;let t=Math.max(0,Math.min(e-i(),2147483647));P=setTimeout(V,t)},U=e=>{let t=a[e.name];if(!t)throw new r(`no job definition is registered for "${e.name}"`);return t},W=async(e,t)=>await D.transact(n=>g(n,{failure:u(t,i()),id:e.id,now:i(),ownerId:k}))?(R({entry:x({...e,status:`dead-letter`}),type:`dead-lettered`}),{completed:0,deadLettered:1,processed:!0,retryScheduled:0}):(R({id:e.id,type:`lease-lost`}),{completed:0,deadLettered:0,processed:!0,retryScheduled:0}),G=async(e,n)=>{let a,l;try{if(a=U(e),e.version>a.version)throw new r(`job "${e.name}" version ${e.version} is newer than the registered version`);let t=e.version===a.version?e.payload:a.migrate?.(e.payload,e.version);if(t===void 0)throw new r(`job "${e.name}" version ${e.version} cannot be migrated`);l=c(a.validate,t)}catch(t){return W(e,t)}let f=new AbortController,g=C([O.signal,n,f.signal]),v=setInterval(()=>{D.transact(t=>_(t,{id:e.id,leaseExpiresAt:i()+o,now:i(),ownerId:k})).then(t=>{t||(f.abort(),R({id:e.id,type:`lease-lost`}))}).catch(L)},Math.max(1e3,Math.floor(o/2)));R({entry:x(e),type:`started`});try{return await a.execute(l,{attempt:e.attempts,entryId:e.id,key:e.key,signal:g}),g.aborted?(await D.transact(t=>m(t,{id:e.id,now:i(),ownerId:k})),{completed:0,deadLettered:0,processed:!0,retryScheduled:0}):await D.transact(t=>p(t,{id:e.id,now:i(),ownerId:k}))?(R({entry:x(e),type:`completed`}),{completed:1,deadLettered:0,processed:!0,retryScheduled:0}):(R({id:e.id,type:`lease-lost`}),{completed:0,deadLettered:0,processed:!0,retryScheduled:0})}catch(n){if(g.aborted||d(n))return await D.transact(t=>m(t,{id:e.id,now:i(),ownerId:k})),{completed:0,deadLettered:0,processed:!0,retryScheduled:0};let r=!1,o=0;try{if(r=e.attempts<(a.retry?.maxAttempts??1)&&(a.retry?.shouldRetry(n,e.attempts)??!1),!r)return W(e,n);if(o=a.retry?.delay?.(e.attempts-1)??s(e.attempts-1),!Number.isFinite(o)||o<0)throw new t(`job "${e.name}" retry delay must be finite and non-negative`)}catch(t){return W(e,t)}let c=i()+o;return await D.transact(t=>h(t,{availableAt:c,failure:u(n,i()),id:e.id,now:i(),ownerId:k}))?(R({entry:x({...e,availableAt:c,status:`queued`}),type:`retry-scheduled`}),{completed:0,deadLettered:0,processed:!0,retryScheduled:1}):(R({id:e.id,type:`lease-lost`}),{completed:0,deadLettered:0,processed:!0,retryScheduled:0})}finally{clearInterval(v)}},K=async e=>{if(e?.aborted)throw new DOMException(`The operation was aborted`,`AbortError`);if(O.signal.aborted)return{completed:0,deadLettered:0,processed:!1,retryScheduled:0};let t=await D.transact(e=>f(e,{leaseDuration:o,now:i(),ownerId:k}));if(!t)return{completed:0,deadLettered:0,processed:!1,retryScheduled:0};F.set(t.id,t);try{return await G(t,e)}finally{F.delete(t.id)}},q=()=>{N||j||(N=(async()=>{for(;!(!(await K()).processed||j););await H()})().catch(L).finally(()=>{N=void 0}))},J=D.subscribe(V),Y=()=>{X()};E?.addEventListener(`abort`,Y,{once:!0});let X=async()=>{if(j)return;j=!0,R({type:`dispose`}),A.clear(),B(),E?.removeEventListener(`abort`,Y),J(),O.abort();let e=[...F.values()];F.clear(),await Promise.all(e.map(e=>D.transact(t=>m(t,{id:e.id,now:i(),ownerId:k}))))};return{get disposalSignal(){return O.signal},async dispose(){await X()},get disposed(){return j},async enqueue(e,n,o){z();let s=a[e];if(!s)throw new r(`no job definition is registered for "${e}"`);let u=i(),d=T(o?.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 D.transact(e=>b(e,m));let h=x(m);return R({entry:h,type:`enqueued`}),V(),h},flush(e){if(z(),I)return I;B();let t=(async()=>{await N;let t=S();try{for(;;){let n=await K(e?.signal);if(!n.processed||(t.processed+=1,t.completed+=n.completed,t.deadLettered+=n.deadLettered,t.retryScheduled+=n.retryScheduled,j))return t}}finally{I=void 0,V()}})();return I=t,t},async list(e){return z(),(await D.list(e)).map(x)},async remove(e){z();let t=await D.transact(t=>y(t,e));return t.status===`removed`&&R({id:e,type:`removed`}),t},async retry(e){z();let t=await D.transact(t=>v(t,e,i()));return t.status===`retried`&&V(),t},async start(){z(),!M&&(M=!0,V())},async stats(){return z(),D.transact(e=>e.countByStatus())},tap(e,t){if(z(),A.add(e),t?.signal){if(t.signal.aborted)return A.delete(e),()=>{};let n=()=>A.delete(e);return t.signal.addEventListener(`abort`,n,{once:!0}),()=>{A.delete(e),t.signal?.removeEventListener(`abort`,n)}}return()=>A.delete(e)},async[Symbol.asyncDispose](){await X()}}}return e.PostmasterDisposedError=n,e.PostmasterError=t,e.PostmasterJobError=r,e.createPostmaster=E,e.defineJobs=o,e})({});
|
|
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 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\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>(name: K, payload: InferJobPayload<J[K]>): Promise<PostmasterEntry> {\n assertLive();\n const job = jobs[name];\n if (!job) throw new PostmasterJobError(`no job definition is registered for \"${name}\"`);\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 now = clock();\n const entry: StoredJob = {\n attempts: 0,\n availableAt: now,\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","../../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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"}
|
package/dist/postmaster.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},t=class extends e{},n=class extends e{};function r(t,n){if(t&&(!Number.isInteger(t.maxAttempts)||t.maxAttempts<1))throw new e(`job "${n}" retry.maxAttempts must be a positive integer`)}function i(t,n){if(!Number.isInteger(n.version)||n.version<1)throw new e(`job "${t}" version must be a positive integer`);if(typeof n.execute!=`function`||typeof n.key!=`function`)throw new e(`job "${t}" must define execute and key`);r(n.retry,t)}function a(e){for(let[t,n]of Object.entries(e))i(t,n);return e}function o(e,t=3e4){return Math.min(1e3*2**Math.max(0,Number.isNaN(e)?0:Math.floor(e)),t)}function s(e,t){return e?typeof e==`function`?e(t):e.parse(t):t}function c(t){let n;try{n=JSON.stringify(t)}catch(t){throw new e(`job payload must be JSON-serializable`,{cause:t})}if(n===void 0)throw new e(`job payload must be JSON-serializable`);return JSON.parse(n)}function l(e,t){return e instanceof Error?{message:e.message,name:e.name,occurredAt:t}:{message:String(e),name:`Error`,occurredAt:t}}function u(e){return e instanceof DOMException&&e.name===`AbortError`}async function d(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 f(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 p(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 m(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 h(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 g(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 _(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:b(i),status:`retried`}}async function v(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 y(e,t){if(await e.get(t.id))throw Error(`Postmaster entry "${t.id}" already exists`);await e.put(t)}function b(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 x=()=>({completed:0,deadLettered:0,processed:0,retryScheduled:0});function S(e){let t=new AbortController,n=e.filter(e=>e!==void 0),r=()=>t.abort();for(let e of n){if(e.aborted){r();break}e.addEventListener(`abort`,r,{once:!0})}return t.signal}function C(t){if(!Number.isInteger(t)||t<1e3)throw new e(`leaseDuration must be a positive integer of at least 1000ms`)}function w(r){let{clock:i=Date.now,jobs:a,leaseDuration:
|
|
1
|
+
var e=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},t=class extends e{},n=class extends e{};function r(t,n){if(t&&(!Number.isInteger(t.maxAttempts)||t.maxAttempts<1))throw new e(`job "${n}" retry.maxAttempts must be a positive integer`)}function i(t,n){if(!Number.isInteger(n.version)||n.version<1)throw new e(`job "${t}" version must be a positive integer`);if(typeof n.execute!=`function`||typeof n.key!=`function`)throw new e(`job "${t}" must define execute and key`);r(n.retry,t)}function a(e){for(let[t,n]of Object.entries(e))i(t,n);return e}function o(e,t=3e4){return Math.min(1e3*2**Math.max(0,Number.isNaN(e)?0:Math.floor(e)),t)}function s(e,t){return e?typeof e==`function`?e(t):e.parse(t):t}function c(t){let n;try{n=JSON.stringify(t)}catch(t){throw new e(`job payload must be JSON-serializable`,{cause:t})}if(n===void 0)throw new e(`job payload must be JSON-serializable`);return JSON.parse(n)}function l(e,t){return e instanceof Error?{message:e.message,name:e.name,occurredAt:t}:{message:String(e),name:`Error`,occurredAt:t}}function u(e){return e instanceof DOMException&&e.name===`AbortError`}async function d(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 f(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 p(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 m(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 h(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 g(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 _(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:b(i),status:`retried`}}async function v(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 y(e,t){if(await e.get(t.id))throw Error(`Postmaster entry "${t.id}" already exists`);await e.put(t)}function b(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 x=()=>({completed:0,deadLettered:0,processed:0,retryScheduled:0});function S(e){let t=new AbortController,n=e.filter(e=>e!==void 0),r=()=>t.abort();for(let e of n){if(e.aborted){r();break}e.addEventListener(`abort`,r,{once:!0})}return t.signal}function C(t){if(!Number.isInteger(t)||t<1e3)throw new e(`leaseDuration must be a positive integer of at least 1000ms`)}function w(t,n){if(t===void 0)return n;if(!Number.isFinite(t)||t<0||!Number.isSafeInteger(t))throw new e(`availableAt must be a finite non-negative safe integer`);return t}function T(r){let{clock:i=Date.now,jobs:a,leaseDuration:T=3e4,signal:E,store:D}=r;C(T);let O=new AbortController,k=crypto.randomUUID(),A=new Set,j=!1,M=!1,N,P,F=new Map,I,L=e=>{let t=e instanceof Error?e:Error(String(e));R({error:t,type:`processor-error`}),queueMicrotask(()=>{throw t})},R=e=>{if(A.size!==0)for(let t of A)try{t(e)}catch{}},z=()=>{if(j)throw new t(`Postmaster is disposed`)},B=()=>{P!==void 0&&clearTimeout(P),P=void 0},V=()=>{B(),M&&!j&&q()},H=async()=>{if(!M||j)return;let e=await D.transact(e=>e.findNextWake(i()));if(e===void 0)return;let t=Math.max(0,Math.min(e-i(),2147483647));P=setTimeout(V,t)},U=e=>{let t=a[e.name];if(!t)throw new n(`no job definition is registered for "${e.name}"`);return t},W=async(e,t)=>await D.transact(n=>h(n,{failure:l(t,i()),id:e.id,now:i(),ownerId:k}))?(R({entry:b({...e,status:`dead-letter`}),type:`dead-lettered`}),{completed:0,deadLettered:1,processed:!0,retryScheduled:0}):(R({id:e.id,type:`lease-lost`}),{completed:0,deadLettered:0,processed:!0,retryScheduled:0}),G=async(t,r)=>{let a,c;try{if(a=U(t),t.version>a.version)throw new n(`job "${t.name}" version ${t.version} is newer than the registered version`);let e=t.version===a.version?t.payload:a.migrate?.(t.payload,t.version);if(e===void 0)throw new n(`job "${t.name}" version ${t.version} cannot be migrated`);c=s(a.validate,e)}catch(e){return W(t,e)}let d=new AbortController,h=S([O.signal,r,d.signal]),_=setInterval(()=>{D.transact(e=>g(e,{id:t.id,leaseExpiresAt:i()+T,now:i(),ownerId:k})).then(e=>{e||(d.abort(),R({id:t.id,type:`lease-lost`}))}).catch(L)},Math.max(1e3,Math.floor(T/2)));R({entry:b(t),type:`started`});try{return await a.execute(c,{attempt:t.attempts,entryId:t.id,key:t.key,signal:h}),h.aborted?(await D.transact(e=>p(e,{id:t.id,now:i(),ownerId:k})),{completed:0,deadLettered:0,processed:!0,retryScheduled:0}):await D.transact(e=>f(e,{id:t.id,now:i(),ownerId:k}))?(R({entry:b(t),type:`completed`}),{completed:1,deadLettered:0,processed:!0,retryScheduled:0}):(R({id:t.id,type:`lease-lost`}),{completed:0,deadLettered:0,processed:!0,retryScheduled:0})}catch(n){if(h.aborted||u(n))return await D.transact(e=>p(e,{id:t.id,now:i(),ownerId:k})),{completed:0,deadLettered:0,processed:!0,retryScheduled:0};let r=!1,s=0;try{if(r=t.attempts<(a.retry?.maxAttempts??1)&&(a.retry?.shouldRetry(n,t.attempts)??!1),!r)return W(t,n);if(s=a.retry?.delay?.(t.attempts-1)??o(t.attempts-1),!Number.isFinite(s)||s<0)throw new e(`job "${t.name}" retry delay must be finite and non-negative`)}catch(e){return W(t,e)}let c=i()+s;return await D.transact(e=>m(e,{availableAt:c,failure:l(n,i()),id:t.id,now:i(),ownerId:k}))?(R({entry:b({...t,availableAt:c,status:`queued`}),type:`retry-scheduled`}),{completed:0,deadLettered:0,processed:!0,retryScheduled:1}):(R({id:t.id,type:`lease-lost`}),{completed:0,deadLettered:0,processed:!0,retryScheduled:0})}finally{clearInterval(_)}},K=async e=>{if(e?.aborted)throw new DOMException(`The operation was aborted`,`AbortError`);if(O.signal.aborted)return{completed:0,deadLettered:0,processed:!1,retryScheduled:0};let t=await D.transact(e=>d(e,{leaseDuration:T,now:i(),ownerId:k}));if(!t)return{completed:0,deadLettered:0,processed:!1,retryScheduled:0};F.set(t.id,t);try{return await G(t,e)}finally{F.delete(t.id)}},q=()=>{N||j||(N=(async()=>{for(;!(!(await K()).processed||j););await H()})().catch(L).finally(()=>{N=void 0}))},J=D.subscribe(V),Y=()=>{X()};E?.addEventListener(`abort`,Y,{once:!0});let X=async()=>{if(j)return;j=!0,R({type:`dispose`}),A.clear(),B(),E?.removeEventListener(`abort`,Y),J(),O.abort();let e=[...F.values()];F.clear(),await Promise.all(e.map(e=>D.transact(t=>p(t,{id:e.id,now:i(),ownerId:k}))))};return{get disposalSignal(){return O.signal},async dispose(){await X()},get disposed(){return j},async enqueue(t,r,o){z();let l=a[t];if(!l)throw new n(`no job definition is registered for "${t}"`);let u=i(),d=w(o?.availableAt,u),f=s(l.validate,r),p=l.key(f);if(typeof p!=`string`||p.length===0)throw new e(`job "${t}" returned an empty key`);let m={attempts:0,availableAt:d,createdAt:u,id:crypto.randomUUID(),key:p,name:t,payload:c(f),status:`queued`,updatedAt:u,version:l.version};await D.transact(e=>y(e,m));let h=b(m);return R({entry:h,type:`enqueued`}),V(),h},flush(e){if(z(),I)return I;B();let t=(async()=>{await N;let t=x();try{for(;;){let n=await K(e?.signal);if(!n.processed||(t.processed+=1,t.completed+=n.completed,t.deadLettered+=n.deadLettered,t.retryScheduled+=n.retryScheduled,j))return t}}finally{I=void 0,V()}})();return I=t,t},async list(e){return z(),(await D.list(e)).map(b)},async remove(e){z();let t=await D.transact(t=>v(t,e));return t.status===`removed`&&R({id:e,type:`removed`}),t},async retry(e){z();let t=await D.transact(t=>_(t,e,i()));return t.status===`retried`&&V(),t},async start(){z(),!M&&(M=!0,V())},async stats(){return z(),D.transact(e=>e.countByStatus())},tap(e,t){if(z(),A.add(e),t?.signal){if(t.signal.aborted)return A.delete(e),()=>{};let n=()=>A.delete(e);return t.signal.addEventListener(`abort`,n,{once:!0}),()=>{A.delete(e),t.signal?.removeEventListener(`abort`,n)}}return()=>A.delete(e)},async[Symbol.asyncDispose](){await X()}}}export{t as PostmasterDisposedError,e as PostmasterError,n as PostmasterJobError,T as createPostmaster,a as defineJobs};
|
|
2
2
|
//# sourceMappingURL=postmaster.js.map
|
package/dist/postmaster.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postmaster.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 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\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>(name: K, payload: InferJobPayload<J[K]>): Promise<PostmasterEntry> {\n assertLive();\n const job = jobs[name];\n if (!job) throw new PostmasterJobError(`no job definition is registered for \"${name}\"`);\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 now = clock();\n const entry: StoredJob = {\n attempts: 0,\n availableAt: now,\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.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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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -116,11 +116,23 @@ export interface CreatePostmasterOptions<J extends JobDefinitions> {
|
|
|
116
116
|
readonly signal?: AbortSignal;
|
|
117
117
|
readonly store: PostmasterStore;
|
|
118
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Options for {@link Postmaster.enqueue}.
|
|
121
|
+
*
|
|
122
|
+
* `availableAt` is the earliest epoch timestamp (ms) at which the job may be
|
|
123
|
+
* claimed. Defaults to the Postmaster clock at enqueue time. Past timestamps
|
|
124
|
+
* remain immediately eligible. Postmaster does not guarantee execution at the
|
|
125
|
+
* requested time — only that the job will not be claimed before it. A live
|
|
126
|
+
* processor (`start()` or `flush()`) is required for execution.
|
|
127
|
+
*/
|
|
128
|
+
export interface EnqueueOptions {
|
|
129
|
+
readonly availableAt?: number;
|
|
130
|
+
}
|
|
119
131
|
export interface Postmaster<J extends JobDefinitions> {
|
|
120
132
|
readonly disposalSignal: AbortSignal;
|
|
121
133
|
dispose(): Promise<void>;
|
|
122
134
|
readonly disposed: boolean;
|
|
123
|
-
enqueue<K extends keyof J & string>(name: K, payload: InferJobPayload<J[K]
|
|
135
|
+
enqueue<K extends keyof J & string>(name: K, payload: InferJobPayload<J[K]>, options?: EnqueueOptions): Promise<PostmasterEntry>;
|
|
124
136
|
flush(options?: {
|
|
125
137
|
signal?: AbortSignal;
|
|
126
138
|
}): Promise<FlushResult>;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vielzeug/postmaster",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Typed durable job outbox with leased processing, retries, and dead-letter recovery",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
67
|
"@types/node": "^26.2.0",
|
|
68
|
+
"@vielzeug/courier": "2.3.0",
|
|
68
69
|
"fake-indexeddb": "^6.2.5",
|
|
69
70
|
"typescript": "^6.0.3",
|
|
70
71
|
"vite": "^8.2.1",
|