delaykit 0.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.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +264 -0
  3. package/dist/delaykit.d.ts +77 -0
  4. package/dist/delaykit.d.ts.map +1 -0
  5. package/dist/delaykit.js +525 -0
  6. package/dist/delaykit.js.map +1 -0
  7. package/dist/duration.d.ts +7 -0
  8. package/dist/duration.d.ts.map +1 -0
  9. package/dist/duration.js +38 -0
  10. package/dist/duration.js.map +1 -0
  11. package/dist/emitter.d.ts +9 -0
  12. package/dist/emitter.d.ts.map +1 -0
  13. package/dist/emitter.js +41 -0
  14. package/dist/emitter.js.map +1 -0
  15. package/dist/executor.d.ts +41 -0
  16. package/dist/executor.d.ts.map +1 -0
  17. package/dist/executor.js +98 -0
  18. package/dist/executor.js.map +1 -0
  19. package/dist/index.d.ts +10 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +6 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/result-handler.d.ts +23 -0
  24. package/dist/result-handler.d.ts.map +1 -0
  25. package/dist/result-handler.js +155 -0
  26. package/dist/result-handler.js.map +1 -0
  27. package/dist/schedulers/polling.d.ts +46 -0
  28. package/dist/schedulers/polling.d.ts.map +1 -0
  29. package/dist/schedulers/polling.js +148 -0
  30. package/dist/schedulers/polling.js.map +1 -0
  31. package/dist/schedulers/posthook.d.ts +29 -0
  32. package/dist/schedulers/posthook.d.ts.map +1 -0
  33. package/dist/schedulers/posthook.js +49 -0
  34. package/dist/schedulers/posthook.js.map +1 -0
  35. package/dist/stores/memory.d.ts +28 -0
  36. package/dist/stores/memory.d.ts.map +1 -0
  37. package/dist/stores/memory.js +282 -0
  38. package/dist/stores/memory.js.map +1 -0
  39. package/dist/stores/postgres-migrations.d.ts +6 -0
  40. package/dist/stores/postgres-migrations.d.ts.map +1 -0
  41. package/dist/stores/postgres-migrations.js +65 -0
  42. package/dist/stores/postgres-migrations.js.map +1 -0
  43. package/dist/stores/postgres.d.ts +32 -0
  44. package/dist/stores/postgres.d.ts.map +1 -0
  45. package/dist/stores/postgres.js +382 -0
  46. package/dist/stores/postgres.js.map +1 -0
  47. package/dist/types.d.ts +192 -0
  48. package/dist/types.d.ts.map +1 -0
  49. package/dist/types.js +4 -0
  50. package/dist/types.js.map +1 -0
  51. package/package.json +80 -0
@@ -0,0 +1,41 @@
1
+ export class JobEventEmitter {
2
+ listeners = new Map();
3
+ /** Bound emit function — safe to pass as a dependency. Single instance, no closure per access. */
4
+ emit = (event) => {
5
+ const set = this.listeners.get(event.type);
6
+ if (!set || set.size === 0)
7
+ return;
8
+ for (const listener of set) {
9
+ try {
10
+ const result = listener(event);
11
+ if (result instanceof Promise) {
12
+ result.catch((err) => {
13
+ console.error(`[delaykit] Async event listener error for "${event.type}":`, err);
14
+ });
15
+ }
16
+ }
17
+ catch (err) {
18
+ console.error(`[delaykit] Event listener error for "${event.type}":`, err);
19
+ }
20
+ }
21
+ };
22
+ on(event, listener) {
23
+ let set = this.listeners.get(event);
24
+ if (!set) {
25
+ set = new Set();
26
+ this.listeners.set(event, set);
27
+ }
28
+ set.add(listener);
29
+ return () => { set.delete(listener); };
30
+ }
31
+ }
32
+ export function emitStalled(emit, job, stalledMs) {
33
+ emit?.({
34
+ type: "job:stalled",
35
+ job: { ...job },
36
+ timestamp: new Date(),
37
+ stalledMs,
38
+ reclaimed: true,
39
+ });
40
+ }
41
+ //# sourceMappingURL=emitter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emitter.js","sourceRoot":"","sources":["../src/emitter.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,eAAe;IAClB,SAAS,GAAG,IAAI,GAAG,EAA4C,CAAC;IAExE,kGAAkG;IACzF,IAAI,GAAW,CAAC,KAAK,EAAE,EAAE;QAChC,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3C,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC;YAAE,OAAO;QACnC,KAAK,MAAM,QAAQ,IAAI,GAAG,EAAE,CAAC;YAC3B,IAAI,CAAC;gBACH,MAAM,MAAM,GAAyB,QAAQ,CAAC,KAAK,CAAC,CAAC;gBACrD,IAAI,MAAM,YAAY,OAAO,EAAE,CAAC;oBAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;wBAC5B,OAAO,CAAC,KAAK,CAAC,8CAA8C,KAAK,CAAC,IAAI,IAAI,EAAE,GAAG,CAAC,CAAC;oBACnF,CAAC,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,wCAAwC,KAAK,CAAC,IAAI,IAAI,EAAE,GAAG,CAAC,CAAC;YAC7E,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IAEF,EAAE,CAAyB,KAAQ,EAAE,QAA6B;QAChE,IAAI,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;YAChB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACjC,CAAC;QACD,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAClB,OAAO,GAAG,EAAE,GAAG,GAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC;CACF;AAED,MAAM,UAAU,WAAW,CAAC,IAAwB,EAAE,GAAQ,EAAE,SAAiB;IAC/E,IAAI,EAAE,CAAC;QACL,IAAI,EAAE,aAAa;QACnB,GAAG,EAAE,EAAE,GAAG,GAAG,EAAE;QACf,SAAS,EAAE,IAAI,IAAI,EAAE;QACrB,SAAS;QACT,SAAS,EAAE,IAAI;KAChB,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,41 @@
1
+ import type { HandlerContext, Job, Store, EmitFn } from "./types.js";
2
+ export interface HandlerEntry {
3
+ fn: (ctx: HandlerContext) => Promise<void>;
4
+ timeoutMs: number;
5
+ }
6
+ export type ExecutionResult = {
7
+ status: "completed";
8
+ job: Job;
9
+ startedAt: number;
10
+ } | {
11
+ status: "handler_succeeded";
12
+ job: Job;
13
+ startedAt: number;
14
+ } | {
15
+ status: "handler_error";
16
+ error: Error;
17
+ job: Job;
18
+ startedAt: number;
19
+ } | {
20
+ status: "needs_reschedule";
21
+ job: Job;
22
+ } | {
23
+ status: "skipped";
24
+ };
25
+ export interface TriggerPayload {
26
+ jobId: string;
27
+ version: number;
28
+ }
29
+ /**
30
+ * Claims a job and runs the handler. Returns a decision signal
31
+ * for the scheduler to act on.
32
+ *
33
+ * For kind='once': marks completed on success, returns "completed".
34
+ * For patterns: returns "handler_succeeded" — the scheduler handles
35
+ * the markCompleted/requeueForNextWindow decision.
36
+ * For debounce not settled: returns "needs_reschedule" — the scheduler
37
+ * does scheduler-first + rescheduleDueAt.
38
+ * On failure: returns "handler_error" — the scheduler decides retry vs terminal.
39
+ */
40
+ export declare function executeJob(trigger: TriggerPayload, store: Store, handlers: Map<string, HandlerEntry>, emit?: EmitFn): Promise<ExecutionResult>;
41
+ //# sourceMappingURL=executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAIrE,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,eAAe,GACvB;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,GAAG,EAAE,GAAG,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACpD;IAAE,MAAM,EAAE,mBAAmB,CAAC;IAAC,GAAG,EAAE,GAAG,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC5D;IAAE,MAAM,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,KAAK,CAAC;IAAC,GAAG,EAAE,GAAG,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACtE;IAAE,MAAM,EAAE,kBAAkB,CAAC;IAAC,GAAG,EAAE,GAAG,CAAA;CAAE,GACxC;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,CAAC;AAE1B,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EACnC,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,eAAe,CAAC,CAgF1B"}
@@ -0,0 +1,98 @@
1
+ import { DEFAULT_TIMEOUT_MS, STALLED_GRACE_MS } from "./types.js";
2
+ import { emitStalled } from "./emitter.js";
3
+ /**
4
+ * Claims a job and runs the handler. Returns a decision signal
5
+ * for the scheduler to act on.
6
+ *
7
+ * For kind='once': marks completed on success, returns "completed".
8
+ * For patterns: returns "handler_succeeded" — the scheduler handles
9
+ * the markCompleted/requeueForNextWindow decision.
10
+ * For debounce not settled: returns "needs_reschedule" — the scheduler
11
+ * does scheduler-first + rescheduleDueAt.
12
+ * On failure: returns "handler_error" — the scheduler decides retry vs terminal.
13
+ */
14
+ export async function executeJob(trigger, store, handlers, emit) {
15
+ let job = await store.getJob(trigger.jobId);
16
+ if (!job)
17
+ return { status: "skipped" };
18
+ if (job.version !== trigger.version)
19
+ return { status: "skipped" };
20
+ // Inline stalled recovery: if the row is running with an expired lease,
21
+ // reclaim it before the pending check. This handles the case where a
22
+ // previous process died mid-handler and a redelivery arrives.
23
+ if (job.status === "running" && job.startedAt) {
24
+ const entry = handlers.get(job.handler);
25
+ const leaseMs = (entry?.timeoutMs ?? DEFAULT_TIMEOUT_MS) + STALLED_GRACE_MS;
26
+ const stalledMs = Date.now() - job.startedAt.getTime();
27
+ const reclaimed = await store.reclaimStalled(job.id, leaseMs);
28
+ if (reclaimed) {
29
+ emitStalled(emit, job, stalledMs);
30
+ // Check if retry budget is exhausted after reclaim
31
+ if (reclaimed.attempt >= reclaimed.maxAttempts) {
32
+ await store.markRunning(reclaimed.id, reclaimed.version);
33
+ const error = new Error("Job stalled (process crash or timeout)");
34
+ await store.markFailed(reclaimed.id, reclaimed.version, error);
35
+ return { status: "handler_error", error, job: reclaimed, startedAt: Date.now() };
36
+ }
37
+ job = reclaimed;
38
+ }
39
+ }
40
+ if (job.status !== "pending")
41
+ return { status: "skipped" };
42
+ // Debounce settlement check (before claiming)
43
+ if (job.kind === "debounce") {
44
+ const now = Date.now();
45
+ const settled = job.lastAt != null && (now - job.lastAt.getTime()) >= (job.waitMs ?? 0);
46
+ const maxWaitExceeded = job.maxWaitMs != null && job.firstAt != null &&
47
+ (now - job.firstAt.getTime()) >= job.maxWaitMs;
48
+ if (!settled && !maxWaitExceeded) {
49
+ return { status: "needs_reschedule", job };
50
+ }
51
+ }
52
+ const entry = handlers.get(job.handler);
53
+ if (!entry) {
54
+ console.error(`[delaykit] No handler registered for "${job.handler}". Job ${job.id} marked failed.`);
55
+ await store.markRunning(job.id, job.version);
56
+ await store.markFailed(job.id, job.version, new Error(`No handler registered for "${job.handler}"`));
57
+ return { status: "handler_error", error: new Error(`No handler registered for "${job.handler}"`), job, startedAt: Date.now() };
58
+ }
59
+ const claimed = await store.markRunning(job.id, job.version);
60
+ if (!claimed)
61
+ return { status: "skipped" };
62
+ const startedAt = Date.now();
63
+ const startedDate = new Date(startedAt);
64
+ emit?.({
65
+ type: "job:started",
66
+ job: { ...job, status: "running", claimedVersion: job.version, startedAt: startedDate },
67
+ timestamp: startedDate,
68
+ attempt: job.attempt,
69
+ });
70
+ const ac = new AbortController();
71
+ const ctx = { key: job.key, job, signal: ac.signal };
72
+ try {
73
+ await executeWithTimeout(entry.fn, ctx, ac, entry.timeoutMs);
74
+ if (job.kind === "once") {
75
+ await store.markCompleted(job.id, job.version);
76
+ return { status: "completed", job, startedAt };
77
+ }
78
+ // Pattern: return decision signal for scheduler to handle
79
+ return { status: "handler_succeeded", job, startedAt };
80
+ }
81
+ catch (err) {
82
+ const error = err instanceof Error ? err : new Error(String(err));
83
+ return { status: "handler_error", error, job, startedAt };
84
+ }
85
+ }
86
+ function executeWithTimeout(fn, ctx, ac, timeoutMs) {
87
+ return new Promise((resolve, reject) => {
88
+ const timer = setTimeout(() => {
89
+ ac.abort();
90
+ reject(new Error(`Handler "${ctx.job.handler}" timed out after ${timeoutMs}ms`));
91
+ }, timeoutMs);
92
+ fn(ctx)
93
+ .then(resolve)
94
+ .catch(reject)
95
+ .finally(() => clearTimeout(timer));
96
+ });
97
+ }
98
+ //# sourceMappingURL=executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.js","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export { DelayKit } from "./delaykit.js";
2
+ export type { DelayKitOptions } from "./delaykit.js";
3
+ export { executeJob } from "./executor.js";
4
+ export type { HandlerEntry, ExecutionResult, TriggerPayload } from "./executor.js";
5
+ export { ACTIVE_STATUSES } from "./types.js";
6
+ export type { Job, JobStatus, Store, Scheduler, ScheduleOptions, DebounceOptions, ThrottleOptions, HandlerFn, HandlerConfig, HandlerContext, RetryConfig, ScheduleRequest, SchedulerRetryConfig, } from "./types.js";
7
+ export { parseDuration, delayToDate } from "./duration.js";
8
+ export { JobEventEmitter } from "./emitter.js";
9
+ export type { JobEvent, JobEventType, JobEventMap, JobEventListener, EmitFn, JobScheduledEvent, JobStartedEvent, JobCompletedEvent, JobFailedEvent, JobRetryingEvent, JobCancelledEvent, JobStalledEvent, } from "./types.js";
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,YAAY,EACV,GAAG,EACH,SAAS,EACT,KAAK,EACL,SAAS,EACT,eAAe,EACf,eAAe,EACf,eAAe,EACf,SAAS,EACT,aAAa,EACb,cAAc,EACd,WAAW,EACX,eAAe,EACf,oBAAoB,GACrB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,YAAY,EACV,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,MAAM,EACN,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,GAChB,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export { DelayKit } from "./delaykit.js";
2
+ export { executeJob } from "./executor.js";
3
+ export { ACTIVE_STATUSES } from "./types.js";
4
+ export { parseDuration, delayToDate } from "./duration.js";
5
+ export { JobEventEmitter } from "./emitter.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAgB7C,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { ExecutionResult } from "./executor.js";
2
+ import type { Store, EmitFn, ScheduleRequest } from "./types.js";
3
+ import type { PollingHandlerEntry, RetryConfig } from "./schedulers/polling.js";
4
+ export interface ResultHandlerDeps {
5
+ store: Store;
6
+ handlers: Map<string, PollingHandlerEntry>;
7
+ schedule: (req: ScheduleRequest) => Promise<string | null>;
8
+ cancel?: (schedulerRef: string) => Promise<void>;
9
+ /** When true, the external scheduler owns retry timing (PosthookScheduler).
10
+ * Handler failures just return "retry" without calling retryJob(). */
11
+ externalRetries?: boolean;
12
+ emit?: EmitFn;
13
+ }
14
+ /**
15
+ * Shared post-execution result handling for both PollingScheduler and webhook delivery.
16
+ *
17
+ * Returns:
18
+ * - "ok" — completed, requeued, or skipped (caller returns 200)
19
+ * - "retry" — handler failed with retries remaining (caller returns 500 for Posthook retry)
20
+ */
21
+ export declare function handleResult(result: ExecutionResult, deps: ResultHandlerDeps): Promise<"ok" | "retry">;
22
+ export declare function calculateRetryDelay(retry: RetryConfig, attempt: number): number;
23
+ //# sourceMappingURL=result-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result-handler.d.ts","sourceRoot":"","sources":["../src/result-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EAAE,KAAK,EAAO,MAAM,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,KAAK,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEhF,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAC3C,QAAQ,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC3D,MAAM,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD;2EACuE;IACvE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,CA2GzB;AAwBD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAsB/E"}
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Shared post-execution result handling for both PollingScheduler and webhook delivery.
3
+ *
4
+ * Returns:
5
+ * - "ok" — completed, requeued, or skipped (caller returns 200)
6
+ * - "retry" — handler failed with retries remaining (caller returns 500 for Posthook retry)
7
+ */
8
+ export async function handleResult(result, deps) {
9
+ if (result.status === "skipped") {
10
+ return "ok";
11
+ }
12
+ if (result.status === "completed") {
13
+ const now = Date.now();
14
+ deps.emit?.({
15
+ type: "job:completed",
16
+ job: { ...result.job, status: "completed" },
17
+ timestamp: new Date(now),
18
+ durationMs: now - result.startedAt,
19
+ });
20
+ return "ok";
21
+ }
22
+ if (result.status === "needs_reschedule") {
23
+ const updated = await deps.store.rescheduleDueAt(result.job.id, result.job.version);
24
+ if (updated)
25
+ await materializeWake(updated, deps);
26
+ return "ok";
27
+ }
28
+ if (result.status === "handler_succeeded") {
29
+ const completed = await deps.store.markCompleted(result.job.id, result.job.version);
30
+ if (completed) {
31
+ const now = Date.now();
32
+ deps.emit?.({
33
+ type: "job:completed",
34
+ job: { ...result.job, status: "completed" },
35
+ timestamp: new Date(now),
36
+ durationMs: now - result.startedAt,
37
+ });
38
+ }
39
+ else if (result.job.kind !== "once") {
40
+ const requeued = await deps.store.requeueForNextWindow(result.job.id);
41
+ if (requeued)
42
+ await materializeWake(requeued, deps);
43
+ }
44
+ return "ok";
45
+ }
46
+ if (result.status === "handler_error") {
47
+ const entry = deps.handlers.get(result.job.handler);
48
+ if (!entry) {
49
+ await deps.store.markFailed(result.job.id, result.job.version, result.error);
50
+ return "ok";
51
+ }
52
+ if (result.job.attempt + 1 < entry.retry.maxAttempts) {
53
+ const scheduledFor = deps.externalRetries
54
+ ? new Date()
55
+ : new Date(Date.now() + calculateRetryDelay(entry.retry, result.job.attempt));
56
+ const retried = await deps.store.retryJob(result.job.id, result.job.version, result.job.attempt + 1, scheduledFor, result.error.message);
57
+ if (retried) {
58
+ deps.emit?.({
59
+ type: "job:retrying",
60
+ job: { ...result.job },
61
+ timestamp: new Date(),
62
+ error: result.error,
63
+ attempt: result.job.attempt,
64
+ nextAttempt: result.job.attempt + 1,
65
+ scheduledFor,
66
+ });
67
+ }
68
+ else if (result.job.kind !== "once") {
69
+ const requeued = await deps.store.requeueForNextWindow(result.job.id);
70
+ if (requeued)
71
+ await materializeWake(requeued, deps);
72
+ return "ok";
73
+ }
74
+ return deps.externalRetries ? "retry" : "ok";
75
+ }
76
+ // Exhausted: terminal failure
77
+ const failed = await deps.store.markFailed(result.job.id, result.job.version, result.error);
78
+ if (failed) {
79
+ const now = Date.now();
80
+ deps.emit?.({
81
+ type: "job:failed",
82
+ job: { ...result.job, status: "failed" },
83
+ timestamp: new Date(now),
84
+ error: result.error,
85
+ attempts: result.job.attempt + 1,
86
+ durationMs: now - result.startedAt,
87
+ });
88
+ }
89
+ else if (result.job.kind !== "once") {
90
+ const requeued = await deps.store.requeueForNextWindow(result.job.id);
91
+ if (requeued)
92
+ await materializeWake(requeued, deps);
93
+ }
94
+ if (entry.retry.onFailure) {
95
+ try {
96
+ await entry.retry.onFailure({
97
+ key: result.job.key,
98
+ error: result.error,
99
+ attempts: result.job.attempt + 1,
100
+ });
101
+ }
102
+ catch (e) {
103
+ console.error(`[delaykit] onFailure handler threw for job ${result.job.id}:`, e);
104
+ }
105
+ }
106
+ return "ok";
107
+ }
108
+ return "ok";
109
+ }
110
+ /**
111
+ * Schedule an external wake and store the ref (version-guarded).
112
+ * If the version advanced while we were creating the hook (e.g., a new
113
+ * pattern event arrived during the network round-trip), the CAS fails.
114
+ * Cancel the orphaned hook so it doesn't deliver and get silently ignored.
115
+ */
116
+ async function materializeWake(job, deps) {
117
+ const entry = deps.handlers.get(job.handler);
118
+ const retry = entry && entry.retry.maxAttempts > 1
119
+ ? { ...entry.retry, attempts: entry.retry.maxAttempts }
120
+ : undefined;
121
+ const ref = await deps.schedule({
122
+ id: job.id, version: job.version, at: job.scheduledFor,
123
+ handler: job.handler, key: job.key, retry,
124
+ });
125
+ if (!ref)
126
+ return;
127
+ const stored = await deps.store.updateSchedulerRef(job.id, job.version, ref);
128
+ if (!stored && deps.cancel) {
129
+ try {
130
+ await deps.cancel(ref);
131
+ }
132
+ catch { /* best-effort cleanup */ }
133
+ }
134
+ }
135
+ export function calculateRetryDelay(retry, attempt) {
136
+ let delay;
137
+ switch (retry.backoff) {
138
+ case "exponential":
139
+ delay = retry.initialDelayMs * Math.pow(2, attempt);
140
+ break;
141
+ case "linear":
142
+ delay = retry.initialDelayMs * (attempt + 1);
143
+ break;
144
+ case "fixed":
145
+ delay = retry.initialDelayMs;
146
+ break;
147
+ }
148
+ delay = Math.min(delay, retry.maxDelayMs);
149
+ if (retry.jitter) {
150
+ const jitterRange = delay * 0.25;
151
+ delay += (Math.random() * 2 - 1) * jitterRange;
152
+ }
153
+ return Math.max(delay, 0);
154
+ }
155
+ //# sourceMappingURL=result-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result-handler.js","sourceRoot":"","sources":["../src/result-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import type { HandlerEntry } from "../executor.js";
2
+ import type { Scheduler, SchedulerContext } from "../types.js";
3
+ export interface RetryConfig {
4
+ maxAttempts: number;
5
+ initialDelayMs: number;
6
+ maxDelayMs: number;
7
+ backoff: "exponential" | "linear" | "fixed";
8
+ jitter: boolean;
9
+ onFailure?: (ctx: {
10
+ key: string;
11
+ error: Error;
12
+ attempts: number;
13
+ }) => Promise<void>;
14
+ }
15
+ export interface PollingHandlerEntry extends HandlerEntry {
16
+ retry: RetryConfig;
17
+ }
18
+ export interface PollingSchedulerOptions {
19
+ /** Polling interval in milliseconds. Default: 1000 (1 second). */
20
+ interval?: number;
21
+ /** Stalled job check interval in milliseconds. Default: 30000 (30 seconds). */
22
+ stalledCheckInterval?: number;
23
+ }
24
+ export declare class PollingScheduler implements Scheduler {
25
+ private interval;
26
+ private stalledCheckInterval;
27
+ private store;
28
+ private handlers;
29
+ private _emit;
30
+ private timer;
31
+ private stalledTimer;
32
+ private running;
33
+ constructor(options?: PollingSchedulerOptions);
34
+ init(ctx: SchedulerContext): void;
35
+ schedule(_req: unknown): Promise<string | null>;
36
+ cancel(_schedulerRef: string): Promise<void>;
37
+ start(): Promise<void>;
38
+ stop(): Promise<void>;
39
+ private scheduleNextPoll;
40
+ private poll;
41
+ private scheduleNextStalledCheck;
42
+ private sweepStalled;
43
+ private handleJob;
44
+ private detectServerless;
45
+ }
46
+ //# sourceMappingURL=polling.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"polling.d.ts","sourceRoot":"","sources":["../../src/schedulers/polling.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,gBAAgB,CAAC;AACnE,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAiB,MAAM,aAAa,CAAC;AAK9E,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,aAAa,GAAG,QAAQ,GAAG,OAAO,CAAC;IAC5C,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,KAAK,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrF;AAED,MAAM,WAAW,mBAAoB,SAAQ,YAAY;IACvD,KAAK,EAAE,WAAW,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACtC,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,qBAAa,gBAAiB,YAAW,SAAS;IAChD,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,oBAAoB,CAAS;IACrC,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,QAAQ,CAAiD;IACjE,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,KAAK,CAA8C;IAC3D,OAAO,CAAC,YAAY,CAA8C;IAClE,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,CAAC,EAAE,uBAAuB;IAK7C,IAAI,CAAC,GAAG,EAAE,gBAAgB,GAAG,IAAI;IAM3B,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAI/C,MAAM,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAE5C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAY3B,OAAO,CAAC,gBAAgB;YAQV,IAAI;IAgBlB,OAAO,CAAC,wBAAwB;YAQlB,YAAY;YA8CZ,SAAS;IAYvB,OAAO,CAAC,gBAAgB;CAiBzB"}
@@ -0,0 +1,148 @@
1
+ import { executeJob } from "../executor.js";
2
+ import { DEFAULT_TIMEOUT_MS, STALLED_GRACE_MS } from "../types.js";
3
+ import { emitStalled } from "../emitter.js";
4
+ import { handleResult, calculateRetryDelay } from "../result-handler.js";
5
+ export class PollingScheduler {
6
+ interval;
7
+ stalledCheckInterval;
8
+ store = null;
9
+ handlers = null;
10
+ _emit = null;
11
+ timer = null;
12
+ stalledTimer = null;
13
+ running = false;
14
+ constructor(options) {
15
+ this.interval = options?.interval ?? 1_000;
16
+ this.stalledCheckInterval = options?.stalledCheckInterval ?? 30_000;
17
+ }
18
+ init(ctx) {
19
+ this.store = ctx.store;
20
+ this.handlers = ctx.handlers;
21
+ this._emit = ctx.emit;
22
+ }
23
+ async schedule(_req) {
24
+ return null;
25
+ }
26
+ async cancel(_schedulerRef) { }
27
+ async start() {
28
+ if (this.running)
29
+ return;
30
+ this.detectServerless();
31
+ this.running = true;
32
+ this.scheduleNextPoll();
33
+ this.scheduleNextStalledCheck();
34
+ }
35
+ async stop() {
36
+ this.running = false;
37
+ if (this.timer) {
38
+ clearTimeout(this.timer);
39
+ this.timer = null;
40
+ }
41
+ if (this.stalledTimer) {
42
+ clearTimeout(this.stalledTimer);
43
+ this.stalledTimer = null;
44
+ }
45
+ }
46
+ scheduleNextPoll() {
47
+ if (!this.running)
48
+ return;
49
+ this.timer = setTimeout(async () => {
50
+ await this.poll();
51
+ this.scheduleNextPoll();
52
+ }, this.interval);
53
+ }
54
+ async poll() {
55
+ if (!this.handlers || !this.store)
56
+ return;
57
+ try {
58
+ const dueJobs = await this.store.getDueJobs(100);
59
+ for (const job of dueJobs) {
60
+ const trigger = { jobId: job.id, version: job.version };
61
+ this.handleJob(trigger).catch((err) => {
62
+ console.error(`[delaykit] Unhandled error processing job ${job.id}:`, err);
63
+ });
64
+ }
65
+ }
66
+ catch (err) {
67
+ console.error("[delaykit] PollingScheduler poll error:", err);
68
+ }
69
+ }
70
+ scheduleNextStalledCheck() {
71
+ if (!this.running)
72
+ return;
73
+ this.stalledTimer = setTimeout(async () => {
74
+ await this.sweepStalled();
75
+ this.scheduleNextStalledCheck();
76
+ }, this.stalledCheckInterval);
77
+ }
78
+ async sweepStalled() {
79
+ if (!this.handlers || !this.store)
80
+ return;
81
+ try {
82
+ const timeouts = new Map();
83
+ for (const [name, entry] of this.handlers) {
84
+ timeouts.set(name, entry.timeoutMs);
85
+ }
86
+ const reclaimed = await this.store.reclaimStalledJobs(timeouts);
87
+ for (const job of reclaimed) {
88
+ const entry = this.handlers.get(job.handler);
89
+ if (!entry)
90
+ continue;
91
+ const timeout = timeouts.get(job.handler) ?? DEFAULT_TIMEOUT_MS;
92
+ emitStalled(this._emit ?? undefined, job, timeout + STALLED_GRACE_MS);
93
+ if (job.attempt >= entry.retry.maxAttempts) {
94
+ // Exhausted: mark failed + call onFailure
95
+ await this.store.markRunning(job.id, job.version);
96
+ await this.store.markFailed(job.id, job.version, new Error("Job stalled (process crash or timeout)"));
97
+ if (entry.retry.onFailure) {
98
+ try {
99
+ await entry.retry.onFailure({
100
+ key: job.key,
101
+ error: new Error("Job stalled (process crash or timeout)"),
102
+ attempts: job.attempt,
103
+ });
104
+ }
105
+ catch (e) {
106
+ console.error(`[delaykit] onFailure threw for stalled job ${job.id}:`, e);
107
+ }
108
+ }
109
+ }
110
+ else if (job.attempt > 0) {
111
+ // Retry reclaim: apply backoff delay.
112
+ // Pattern requeues (attempt=0) already have correct scheduledFor from the store.
113
+ const delay = calculateRetryDelay(entry.retry, job.attempt - 1);
114
+ const nextAt = new Date(Date.now() + delay);
115
+ await this.store.updateScheduledFor(job.id, nextAt);
116
+ }
117
+ }
118
+ }
119
+ catch (err) {
120
+ console.error("[delaykit] PollingScheduler stalled sweep error:", err);
121
+ }
122
+ }
123
+ async handleJob(trigger) {
124
+ if (!this.handlers || !this.store)
125
+ return;
126
+ const result = await executeJob(trigger, this.store, this.handlers, this._emit ?? undefined);
127
+ await handleResult(result, {
128
+ store: this.store,
129
+ handlers: this.handlers,
130
+ schedule: this.schedule.bind(this),
131
+ emit: this._emit ?? undefined,
132
+ });
133
+ }
134
+ detectServerless() {
135
+ const isVercel = typeof process !== "undefined" && process.env.VERCEL === "1";
136
+ const isLambda = typeof process !== "undefined" && !!process.env.AWS_LAMBDA_FUNCTION_NAME;
137
+ if (isVercel || isLambda) {
138
+ throw new Error([
139
+ "PollingScheduler cannot run in serverless environments (no long-running process).",
140
+ "",
141
+ "Options:",
142
+ " 1. Use PosthookScheduler for managed scheduling: https://delaykit.dev/vercel",
143
+ " 2. Run PollingScheduler on a separate VPS with triggerUrl: https://delaykit.dev/split-deploy",
144
+ ].join("\n"));
145
+ }
146
+ }
147
+ }
148
+ //# sourceMappingURL=polling.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"polling.js","sourceRoot":"","sources":["../../src/schedulers/polling.ts"],"names":[],"mappings":"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"}