@adonis-agora/durable 0.41.0 → 0.43.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 (71) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/dist/assets/spa/assets/index-BkHCh-H3.js +16 -0
  3. package/dist/assets/spa/assets/index-BkHCh-H3.js.map +1 -0
  4. package/dist/assets/spa/index.html +1 -1
  5. package/dist/providers/durable_provider.d.ts.map +1 -1
  6. package/dist/providers/durable_provider.js +10 -0
  7. package/dist/providers/durable_provider.js.map +1 -1
  8. package/dist/src/codec-state-store.d.ts +13 -1
  9. package/dist/src/codec-state-store.d.ts.map +1 -1
  10. package/dist/src/codec-state-store.js +32 -0
  11. package/dist/src/codec-state-store.js.map +1 -1
  12. package/dist/src/commands/worker.d.ts.map +1 -1
  13. package/dist/src/commands/worker.js +4 -0
  14. package/dist/src/commands/worker.js.map +1 -1
  15. package/dist/src/concurrency.d.ts +55 -0
  16. package/dist/src/concurrency.d.ts.map +1 -0
  17. package/dist/src/concurrency.js +52 -0
  18. package/dist/src/concurrency.js.map +1 -0
  19. package/dist/src/dashboard/index.d.ts +1 -1
  20. package/dist/src/dashboard/index.js +1 -1
  21. package/dist/src/define_config.d.ts +17 -1
  22. package/dist/src/define_config.d.ts.map +1 -1
  23. package/dist/src/define_config.js.map +1 -1
  24. package/dist/src/engine.d.ts +73 -2
  25. package/dist/src/engine.d.ts.map +1 -1
  26. package/dist/src/engine.js +195 -26
  27. package/dist/src/engine.js.map +1 -1
  28. package/dist/src/errors.d.ts +13 -0
  29. package/dist/src/errors.d.ts.map +1 -1
  30. package/dist/src/errors.js +20 -0
  31. package/dist/src/errors.js.map +1 -1
  32. package/dist/src/index.d.ts +3 -1
  33. package/dist/src/index.d.ts.map +1 -1
  34. package/dist/src/index.js +3 -1
  35. package/dist/src/index.js.map +1 -1
  36. package/dist/src/interfaces.d.ts +111 -0
  37. package/dist/src/interfaces.d.ts.map +1 -1
  38. package/dist/src/interfaces.js +18 -1
  39. package/dist/src/interfaces.js.map +1 -1
  40. package/dist/src/run-value-facets.d.ts +1 -1
  41. package/dist/src/run-value-facets.d.ts.map +1 -1
  42. package/dist/src/run-value-facets.js +1 -1
  43. package/dist/src/run-value-facets.js.map +1 -1
  44. package/dist/src/schedules.d.ts +172 -0
  45. package/dist/src/schedules.d.ts.map +1 -0
  46. package/dist/src/schedules.js +362 -0
  47. package/dist/src/schedules.js.map +1 -0
  48. package/dist/src/stores/lucid-schema.d.ts +1 -0
  49. package/dist/src/stores/lucid-schema.d.ts.map +1 -1
  50. package/dist/src/stores/lucid-schema.js +21 -0
  51. package/dist/src/stores/lucid-schema.js.map +1 -1
  52. package/dist/src/stores/lucid.d.ts +9 -1
  53. package/dist/src/stores/lucid.d.ts.map +1 -1
  54. package/dist/src/stores/lucid.js +107 -0
  55. package/dist/src/stores/lucid.js.map +1 -1
  56. package/dist/src/testing/in-memory-state-store.d.ts +8 -1
  57. package/dist/src/testing/in-memory-state-store.d.ts.map +1 -1
  58. package/dist/src/testing/in-memory-state-store.js +38 -0
  59. package/dist/src/testing/in-memory-state-store.js.map +1 -1
  60. package/dist/src/testing-kit/state-store-conformance.d.ts.map +1 -1
  61. package/dist/src/testing-kit/state-store-conformance.js +101 -0
  62. package/dist/src/testing-kit/state-store-conformance.js.map +1 -1
  63. package/dist/src/workflow-discovery.d.ts.map +1 -1
  64. package/dist/src/workflow-discovery.js +1 -0
  65. package/dist/src/workflow-discovery.js.map +1 -1
  66. package/dist/src/workflow-ref.d.ts +9 -0
  67. package/dist/src/workflow-ref.d.ts.map +1 -1
  68. package/dist/src/workflow-ref.js.map +1 -1
  69. package/package.json +6 -6
  70. package/dist/assets/spa/assets/index-CeHc6GHq.js +0 -16
  71. package/dist/assets/spa/assets/index-CeHc6GHq.js.map +0 -1
@@ -0,0 +1,172 @@
1
+ import type { ConcurrencyQuota } from './concurrency.js';
2
+ import type { StartOptions } from './engine.js';
3
+ import type { RunResult, ScheduleQuery, SearchAttributes, StateStore, WorkflowRun } from './interfaces.js';
4
+ import { type WorkflowRef } from './workflow-ref.js';
5
+ /**
6
+ * A schedule definition — what `engine.schedules.create/upsert` take. Temporal-style: a persisted,
7
+ * runtime-managed recurring start of `workflow` with `input`, fired by every driving engine's timer
8
+ * poll (safe with many workers: each window's run id is deterministic, `start` is idempotent, and
9
+ * advancing the schedule is a compare-and-set).
10
+ */
11
+ export interface ScheduleOptions {
12
+ /** Stable id, unique per store (e.g. `digest:user-42`). Part of every run id it starts. */
13
+ id: string;
14
+ /** The workflow to start — a registered name or `@Workflow` class. */
15
+ workflow: WorkflowRef;
16
+ /** The input every run starts with. */
17
+ input?: unknown;
18
+ /** Cron expression (5 fields, or 6 with leading seconds). Needs the `cron-parser` peer. */
19
+ cron?: string | undefined;
20
+ /** Fixed interval (`'15m'`, or ms), windows aligned to the epoch. Exclusive with `cron`. */
21
+ every?: string | number | undefined;
22
+ /** IANA timezone the cron is evaluated in (e.g. `America/Sao_Paulo`). Default UTC. */
23
+ timezone?: string | undefined;
24
+ /**
25
+ * Delay each fire by up to this much (`'30s'` or ms) — a stable per-(schedule, window) offset, so
26
+ * thousands of schedules on the same cron don't all start in the same second.
27
+ */
28
+ jitter?: string | number | undefined;
29
+ /**
30
+ * The previous run of this schedule is still in flight when the next window comes due:
31
+ * `'allow'` (default) starts the new one anyway; `'skip'` skips that window.
32
+ */
33
+ overlap?: 'allow' | 'skip' | undefined;
34
+ /**
35
+ * Windows missed while nothing polled (all schedulers down): `'latest'` (default) fires the most
36
+ * recent missed window once; `'skip'` drops them and waits for the next window. Never more than one
37
+ * catch-up run per schedule.
38
+ */
39
+ catchup?: 'latest' | 'skip' | undefined;
40
+ /** Create paused (never due until `resume`). On `upsert`, omit to keep the current paused state. */
41
+ paused?: boolean | undefined;
42
+ /** Free-form note shown with the schedule (why it exists / why it's paused). */
43
+ note?: string | undefined;
44
+ /** Namespace of the schedule and of the runs it starts. Defaults to the engine's own namespace. */
45
+ namespace?: string | undefined;
46
+ /** Labels to find the schedule by — also stamped on every run it starts (plus `schedule:<id>`). */
47
+ tags?: string[] | undefined;
48
+ /** Search attributes stamped on every run it starts. */
49
+ searchAttributes?: SearchAttributes | undefined;
50
+ /** Dispatch priority for the runs it starts. */
51
+ priority?: number | undefined;
52
+ /**
53
+ * A start-time concurrency quota applied to every run it starts (see `StartOptions.concurrency`):
54
+ * a window whose start would exceed it is recorded as the schedule's `lastError` and skipped, and
55
+ * the schedule moves on to its next window.
56
+ */
57
+ concurrency?: ConcurrencyQuota | undefined;
58
+ }
59
+ /** A schedule as `engine.schedules.get/list` describe it. */
60
+ export interface ScheduleDescription {
61
+ id: string;
62
+ workflow: string;
63
+ input: unknown;
64
+ namespace: string;
65
+ cron?: string | undefined;
66
+ everyMs?: number | undefined;
67
+ timezone?: string | undefined;
68
+ jitterMs?: number | undefined;
69
+ overlap: 'allow' | 'skip';
70
+ catchup: 'latest' | 'skip';
71
+ paused: boolean;
72
+ note?: string | undefined;
73
+ tags?: string[] | undefined;
74
+ searchAttributes?: SearchAttributes | undefined;
75
+ priority?: number | undefined;
76
+ concurrency?: ConcurrencyQuota | undefined;
77
+ /** When it is next due (jitter included), or null if it never fires again / paused. */
78
+ nextFireAt: Date | null;
79
+ /** The last window it fired for, and the run that window started. */
80
+ lastFireAt?: Date | undefined;
81
+ lastRunId?: string | undefined;
82
+ /** How many windows it has fired (manual triggers excluded). */
83
+ fires: number;
84
+ /** The last start failure (e.g. a concurrency limit), cleared by the next successful start. */
85
+ lastError?: string | undefined;
86
+ createdAt: Date;
87
+ updatedAt: Date;
88
+ }
89
+ /** Filter for `engine.schedules.list`. */
90
+ export type ScheduleListQuery = Omit<ScheduleQuery, 'dueBy'>;
91
+ /** Thrown by `engine.schedules.create` when the id is taken (use `upsert` to replace). */
92
+ export declare class ScheduleAlreadyExistsError extends Error {
93
+ readonly scheduleId: string;
94
+ constructor(scheduleId: string);
95
+ }
96
+ /** Thrown by `engine.schedules.*` on an id that doesn't exist. */
97
+ export declare class ScheduleNotFoundError extends Error {
98
+ readonly scheduleId: string;
99
+ constructor(scheduleId: string);
100
+ }
101
+ /** The engine surface the schedule client drives. */
102
+ export interface ScheduleHost {
103
+ start(workflow: string, input: unknown, runId: string, opts?: StartOptions): Promise<RunResult>;
104
+ getRun(runId: string): Promise<WorkflowRun | null>;
105
+ }
106
+ /** Engine-level knobs for the client. */
107
+ export interface ScheduleClientOptions {
108
+ /** The engine's namespace: the default for new schedules, and which ones `tick` fires. */
109
+ namespace?: string | undefined;
110
+ clock?: (() => number) | undefined;
111
+ }
112
+ /**
113
+ * Persisted, runtime-managed schedules — `engine.schedules`. Unlike the code-registered
114
+ * `schedules` config / `static schedule`, these live in the store: create/update/pause/delete them at runtime
115
+ * (one per user, per tenant, per automation…), and every driving engine's timer poll fires the due
116
+ * ones via {@link tick}.
117
+ *
118
+ * Multi-worker safety comes from two properties, not from a lock: a window's run id is deterministic
119
+ * (`sched:<id>:<windowMs>`), so racing starts of the same window collapse into one run; and a
120
+ * schedule is advanced with a compare-and-set on its `nextFireAt`, so exactly one poller moves it on.
121
+ * A crash between starting the run and advancing the schedule just re-fires the (idempotent) start.
122
+ */
123
+ export declare class ScheduleClient {
124
+ private readonly store;
125
+ private readonly host;
126
+ private readonly options;
127
+ constructor(store: StateStore, host: ScheduleHost, options?: ScheduleClientOptions);
128
+ private now;
129
+ /** Whether the store persists schedules (implements the schedule methods). */
130
+ get supported(): boolean;
131
+ private requireStore;
132
+ /** Create a schedule. Throws {@link ScheduleAlreadyExistsError} if the id is taken. */
133
+ create(opts: ScheduleOptions): Promise<ScheduleDescription>;
134
+ /**
135
+ * Create the schedule, or replace the definition of an existing one (its history — last fire, last
136
+ * run, fire count — is kept). The next fire is recomputed from now under the new definition, so a
137
+ * window that already fired is never fired again by an upsert.
138
+ */
139
+ upsert(opts: ScheduleOptions): Promise<ScheduleDescription>;
140
+ /** The schedule `id`, or null. */
141
+ get(id: string): Promise<ScheduleDescription | null>;
142
+ /** Schedules matching `query`, soonest-due first. */
143
+ list(query?: ScheduleListQuery): Promise<ScheduleDescription[]>;
144
+ /** Delete the schedule. Runs it already started are left alone. Returns whether it existed. */
145
+ delete(id: string): Promise<boolean>;
146
+ /** Stop firing until {@link resume}. `note` records why (shown by `get`/`list`). */
147
+ pause(id: string, note?: string): Promise<ScheduleDescription>;
148
+ /**
149
+ * Fire again from the next window after now — the windows skipped while paused are NOT caught up.
150
+ */
151
+ resume(id: string): Promise<ScheduleDescription>;
152
+ /**
153
+ * Start one run of the schedule right now, outside its cadence (paused or not). The run id is
154
+ * `sched:<id>:manual:<nowMs>`; it doesn't move the schedule's next fire.
155
+ */
156
+ trigger(id: string): Promise<RunResult>;
157
+ /**
158
+ * Fire every due schedule (in this engine's namespace; all namespaces for an operator engine):
159
+ * start its window's run and advance it to the next window. Called by the worker tick (`durable:work`
160
+ * / the embedded worker) when `persistedSchedules` is on; call it yourself from any other driver.
161
+ * Returns the
162
+ * run ids started (or re-found, for an idempotent re-fire).
163
+ */
164
+ tick(nowMs?: number): Promise<string[]>;
165
+ /** Fire one due schedule and advance it. Returns the run id started, if any. */
166
+ private fire;
167
+ /** `overlap: 'skip'` and the previous run of this schedule is still in flight. */
168
+ private overlapping;
169
+ private mustGet;
170
+ private write;
171
+ }
172
+ //# sourceMappingURL=schedules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedules.d.ts","sourceRoot":"","sources":["../../src/schedules.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,362 @@
1
+ import { parseDuration } from './duration.js';
2
+ import { nextCronFireMs, prevCronFireMs } from './scheduler.js';
3
+ import { workflowName } from './workflow-ref.js';
4
+ /** Statuses a run never leaves on its own — an `overlap: 'skip'` schedule may fire again. */
5
+ const TERMINAL_RUN_STATUSES = ['completed', 'failed', 'cancelled', 'dead'];
6
+ /** Thrown by `engine.schedules.create` when the id is taken (use `upsert` to replace). */
7
+ export class ScheduleAlreadyExistsError extends Error {
8
+ scheduleId;
9
+ constructor(scheduleId) {
10
+ super(`schedule "${scheduleId}" already exists`);
11
+ this.scheduleId = scheduleId;
12
+ this.name = 'ScheduleAlreadyExistsError';
13
+ }
14
+ }
15
+ /** Thrown by `engine.schedules.*` on an id that doesn't exist. */
16
+ export class ScheduleNotFoundError extends Error {
17
+ scheduleId;
18
+ constructor(scheduleId) {
19
+ super(`schedule "${scheduleId}" not found`);
20
+ this.scheduleId = scheduleId;
21
+ this.name = 'ScheduleNotFoundError';
22
+ }
23
+ }
24
+ const DEFAULT_NAMESPACE = 'default';
25
+ /** How late a window may fire under `catchup: 'skip'` and still count as on time (plus jitter). */
26
+ const ON_TIME_GRACE_MS = 60_000;
27
+ const TICK_BATCH = 100;
28
+ /**
29
+ * Persisted, runtime-managed schedules — `engine.schedules`. Unlike the code-registered
30
+ * `schedules` config / `static schedule`, these live in the store: create/update/pause/delete them at runtime
31
+ * (one per user, per tenant, per automation…), and every driving engine's timer poll fires the due
32
+ * ones via {@link tick}.
33
+ *
34
+ * Multi-worker safety comes from two properties, not from a lock: a window's run id is deterministic
35
+ * (`sched:<id>:<windowMs>`), so racing starts of the same window collapse into one run; and a
36
+ * schedule is advanced with a compare-and-set on its `nextFireAt`, so exactly one poller moves it on.
37
+ * A crash between starting the run and advancing the schedule just re-fires the (idempotent) start.
38
+ */
39
+ export class ScheduleClient {
40
+ store;
41
+ host;
42
+ options;
43
+ constructor(store, host, options = {}) {
44
+ this.store = store;
45
+ this.host = host;
46
+ this.options = options;
47
+ }
48
+ now() {
49
+ return (this.options.clock ?? Date.now)();
50
+ }
51
+ /** Whether the store persists schedules (implements the schedule methods). */
52
+ get supported() {
53
+ const s = this.store;
54
+ return !!(s.saveSchedule &&
55
+ s.getSchedule &&
56
+ s.updateSchedule &&
57
+ s.deleteSchedule &&
58
+ s.listSchedules);
59
+ }
60
+ requireStore() {
61
+ if (!this.supported) {
62
+ throw new Error('persisted schedules need a store that implements saveSchedule/getSchedule/updateSchedule/deleteSchedule/listSchedules — every bundled store adapter does; this one does not');
63
+ }
64
+ const s = this.store;
65
+ return {
66
+ saveSchedule: (r) => s.saveSchedule.call(s, r),
67
+ getSchedule: (id) => s.getSchedule.call(s, id),
68
+ updateSchedule: (id, patch, expected) => s.updateSchedule.call(s, id, patch, expected),
69
+ deleteSchedule: (id) => s.deleteSchedule.call(s, id),
70
+ listSchedules: (q) => s.listSchedules.call(s, q),
71
+ };
72
+ }
73
+ /** Create a schedule. Throws {@link ScheduleAlreadyExistsError} if the id is taken. */
74
+ async create(opts) {
75
+ const store = this.requireStore();
76
+ if (await store.getSchedule(opts.id))
77
+ throw new ScheduleAlreadyExistsError(opts.id);
78
+ return this.write(opts, null);
79
+ }
80
+ /**
81
+ * Create the schedule, or replace the definition of an existing one (its history — last fire, last
82
+ * run, fire count — is kept). The next fire is recomputed from now under the new definition, so a
83
+ * window that already fired is never fired again by an upsert.
84
+ */
85
+ async upsert(opts) {
86
+ const store = this.requireStore();
87
+ return this.write(opts, await store.getSchedule(opts.id));
88
+ }
89
+ /** The schedule `id`, or null. */
90
+ async get(id) {
91
+ const record = await this.requireStore().getSchedule(id);
92
+ return record ? describe(record) : null;
93
+ }
94
+ /** Schedules matching `query`, soonest-due first. */
95
+ async list(query = {}) {
96
+ return (await this.requireStore().listSchedules(query)).map(describe);
97
+ }
98
+ /** Delete the schedule. Runs it already started are left alone. Returns whether it existed. */
99
+ delete(id) {
100
+ return this.requireStore().deleteSchedule(id);
101
+ }
102
+ /** Stop firing until {@link resume}. `note` records why (shown by `get`/`list`). */
103
+ async pause(id, note) {
104
+ const store = this.requireStore();
105
+ const record = await this.mustGet(id);
106
+ const spec = { ...record.spec };
107
+ if (note !== undefined)
108
+ spec.note = note;
109
+ await store.updateSchedule(id, {
110
+ paused: true,
111
+ spec: spec,
112
+ updatedAt: new Date(this.now()),
113
+ });
114
+ return describe(await this.mustGet(id));
115
+ }
116
+ /**
117
+ * Fire again from the next window after now — the windows skipped while paused are NOT caught up.
118
+ */
119
+ async resume(id) {
120
+ const store = this.requireStore();
121
+ const record = await this.mustGet(id);
122
+ const spec = record.spec;
123
+ const window = nextWindowAfter(spec, this.now());
124
+ await store.updateSchedule(id, {
125
+ paused: false,
126
+ nextFireAt: window === null ? null : window + jitterFor(record.id, window, spec),
127
+ state: { ...record.state, window },
128
+ updatedAt: new Date(this.now()),
129
+ });
130
+ return describe(await this.mustGet(id));
131
+ }
132
+ /**
133
+ * Start one run of the schedule right now, outside its cadence (paused or not). The run id is
134
+ * `sched:<id>:manual:<nowMs>`; it doesn't move the schedule's next fire.
135
+ */
136
+ async trigger(id) {
137
+ const record = await this.mustGet(id);
138
+ const runId = `sched:${id}:manual:${this.now()}`;
139
+ return this.host.start(record.workflow, specOf(record).input, runId, startOptions(record));
140
+ }
141
+ /**
142
+ * Fire every due schedule (in this engine's namespace; all namespaces for an operator engine):
143
+ * start its window's run and advance it to the next window. Called by the worker tick (`durable:work`
144
+ * / the embedded worker) when `persistedSchedules` is on; call it yourself from any other driver.
145
+ * Returns the
146
+ * run ids started (or re-found, for an idempotent re-fire).
147
+ */
148
+ async tick(nowMs = this.now()) {
149
+ const store = this.requireStore();
150
+ const started = [];
151
+ for (let round = 0; round < 50; round++) {
152
+ const due = await store.listSchedules({
153
+ dueBy: nowMs,
154
+ namespace: this.options.namespace,
155
+ limit: TICK_BATCH,
156
+ });
157
+ for (const record of due) {
158
+ const runId = await this.fire(record, nowMs);
159
+ if (runId)
160
+ started.push(runId);
161
+ }
162
+ if (due.length < TICK_BATCH)
163
+ break;
164
+ }
165
+ return started;
166
+ }
167
+ /** Fire one due schedule and advance it. Returns the run id started, if any. */
168
+ async fire(record, nowMs) {
169
+ const store = this.requireStore();
170
+ const spec = specOf(record);
171
+ const state = record.state;
172
+ let latest;
173
+ let next;
174
+ try {
175
+ // The most recent window at or before now — the one (and only one) a late poll fires.
176
+ latest = latestWindowAtOrBefore(spec, nowMs);
177
+ next = nextWindowAfter(spec, latest);
178
+ }
179
+ catch (error) {
180
+ // A definition that no longer evaluates (bad cron/timezone): stop it, loudly, instead of
181
+ // failing on every tick forever.
182
+ await store.updateSchedule(record.id, {
183
+ paused: true,
184
+ nextFireAt: null,
185
+ state: { ...state, lastError: `invalid schedule: ${errorMessage(error)}` },
186
+ updatedAt: new Date(nowMs),
187
+ }, record.nextFireAt);
188
+ return undefined;
189
+ }
190
+ const window = state.window ?? latest;
191
+ const jitter = spec.jitterMs ?? 0;
192
+ const onTime = nowMs - latest <= ON_TIME_GRACE_MS + jitter;
193
+ let runId;
194
+ let lastError;
195
+ let fired = false;
196
+ const skipMissed = spec.catchup === 'skip' && !onTime;
197
+ const beforeWindow = latest < window; // clock skew: not actually due yet
198
+ if (!skipMissed && !beforeWindow && !(await this.overlapping(spec, state))) {
199
+ runId = `sched:${record.id}:${latest}`;
200
+ try {
201
+ await this.host.start(record.workflow, spec.input, runId, startOptions(record));
202
+ fired = true;
203
+ }
204
+ catch (error) {
205
+ // A racing poller may have created the same (deterministic) run first: that's a success.
206
+ if (await this.host.getRun(runId))
207
+ fired = true;
208
+ else
209
+ lastError = errorMessage(error);
210
+ }
211
+ }
212
+ const nextState = {
213
+ ...state,
214
+ window: next,
215
+ ...(fired ? { lastFireAt: latest, lastRunId: runId, fires: (state.fires ?? 0) + 1 } : {}),
216
+ lastError: fired ? undefined : (lastError ?? state.lastError),
217
+ };
218
+ await store.updateSchedule(record.id, {
219
+ nextFireAt: next === null ? null : next + jitterFor(record.id, next, spec),
220
+ state: stripUndefined(nextState),
221
+ updatedAt: new Date(nowMs),
222
+ }, record.nextFireAt);
223
+ return fired ? runId : undefined;
224
+ }
225
+ /** `overlap: 'skip'` and the previous run of this schedule is still in flight. */
226
+ async overlapping(spec, state) {
227
+ if (spec.overlap !== 'skip' || !state.lastRunId)
228
+ return false;
229
+ const prev = await this.host.getRun(state.lastRunId);
230
+ return !!prev && !TERMINAL_RUN_STATUSES.includes(prev.status);
231
+ }
232
+ async mustGet(id) {
233
+ const record = await this.requireStore().getSchedule(id);
234
+ if (!record)
235
+ throw new ScheduleNotFoundError(id);
236
+ return record;
237
+ }
238
+ async write(opts, existing) {
239
+ const store = this.requireStore();
240
+ const spec = toSpec(opts);
241
+ const now = this.now();
242
+ // An upsert that doesn't say otherwise keeps a paused schedule paused.
243
+ const paused = opts.paused ?? existing?.paused ?? false;
244
+ const window = nextWindowAfter(spec, now); // also validates the cron/timezone up front
245
+ const record = {
246
+ id: opts.id,
247
+ namespace: opts.namespace ?? this.options.namespace ?? DEFAULT_NAMESPACE,
248
+ workflow: workflowName(opts.workflow),
249
+ paused,
250
+ nextFireAt: window === null ? null : window + jitterFor(opts.id, window, spec),
251
+ tags: opts.tags,
252
+ spec: spec,
253
+ state: stripUndefined({
254
+ ...(existing?.state ?? {}),
255
+ window,
256
+ }),
257
+ createdAt: existing?.createdAt ?? new Date(now),
258
+ updatedAt: new Date(now),
259
+ };
260
+ await store.saveSchedule(record);
261
+ return describe(record);
262
+ }
263
+ }
264
+ function toSpec(opts) {
265
+ if ((opts.cron == null) === (opts.every == null)) {
266
+ throw new Error(`schedule "${opts.id}" needs exactly one of "cron" or "every"`);
267
+ }
268
+ const everyMs = opts.every != null ? parseDuration(opts.every) : undefined;
269
+ if (everyMs !== undefined && !(everyMs > 0)) {
270
+ throw new Error(`schedule "${opts.id}": "every" must be a positive duration`);
271
+ }
272
+ const jitterMs = opts.jitter != null ? parseDuration(opts.jitter) : undefined;
273
+ return stripUndefined({
274
+ v: 1,
275
+ input: opts.input,
276
+ cron: opts.cron,
277
+ everyMs,
278
+ timezone: opts.timezone,
279
+ jitterMs: jitterMs && jitterMs > 0 ? jitterMs : undefined,
280
+ overlap: opts.overlap ?? 'allow',
281
+ catchup: opts.catchup ?? 'latest',
282
+ note: opts.note,
283
+ searchAttributes: opts.searchAttributes,
284
+ priority: opts.priority,
285
+ concurrency: opts.concurrency,
286
+ });
287
+ }
288
+ function specOf(record) {
289
+ return record.spec;
290
+ }
291
+ /** The latest window at or before `atMs`. */
292
+ function latestWindowAtOrBefore(spec, atMs) {
293
+ if (spec.cron != null)
294
+ return prevCronFireMs(spec.cron, atMs, spec.timezone);
295
+ const every = spec.everyMs;
296
+ return Math.floor(atMs / every) * every;
297
+ }
298
+ /** The first window strictly after `afterMs`. */
299
+ function nextWindowAfter(spec, afterMs) {
300
+ if (spec.cron != null)
301
+ return nextCronFireMs(spec.cron, afterMs, spec.timezone);
302
+ const every = spec.everyMs;
303
+ return (Math.floor(afterMs / every) + 1) * every;
304
+ }
305
+ /** A stable offset in `[0, jitterMs)` per (schedule, window): FNV-1a over the pair. */
306
+ function jitterFor(id, window, spec) {
307
+ const max = spec.jitterMs ?? 0;
308
+ if (max <= 0)
309
+ return 0;
310
+ let h = 0x811c9dc5;
311
+ for (const ch of `${id}@${window}`) {
312
+ h ^= ch.charCodeAt(0);
313
+ h = Math.imul(h, 0x01000193) >>> 0;
314
+ }
315
+ return h % max;
316
+ }
317
+ function startOptions(record) {
318
+ const spec = specOf(record);
319
+ return stripUndefined({
320
+ tags: [...(record.tags ?? []), `schedule:${record.id}`],
321
+ namespace: record.namespace,
322
+ searchAttributes: spec.searchAttributes,
323
+ priority: spec.priority,
324
+ concurrency: spec.concurrency,
325
+ });
326
+ }
327
+ function describe(record) {
328
+ const spec = specOf(record);
329
+ const state = record.state;
330
+ return stripUndefined({
331
+ id: record.id,
332
+ workflow: record.workflow,
333
+ input: spec.input,
334
+ namespace: record.namespace,
335
+ cron: spec.cron,
336
+ everyMs: spec.everyMs,
337
+ timezone: spec.timezone,
338
+ jitterMs: spec.jitterMs,
339
+ overlap: spec.overlap ?? 'allow',
340
+ catchup: spec.catchup ?? 'latest',
341
+ paused: record.paused,
342
+ note: spec.note,
343
+ tags: record.tags,
344
+ searchAttributes: spec.searchAttributes,
345
+ priority: spec.priority,
346
+ concurrency: spec.concurrency,
347
+ nextFireAt: record.paused || record.nextFireAt == null ? null : new Date(record.nextFireAt),
348
+ lastFireAt: state.lastFireAt != null ? new Date(state.lastFireAt) : undefined,
349
+ lastRunId: state.lastRunId,
350
+ fires: state.fires ?? 0,
351
+ lastError: state.lastError,
352
+ createdAt: record.createdAt,
353
+ updatedAt: record.updatedAt,
354
+ });
355
+ }
356
+ function stripUndefined(o) {
357
+ return Object.fromEntries(Object.entries(o).filter(([, v]) => v !== undefined));
358
+ }
359
+ function errorMessage(error) {
360
+ return error instanceof Error ? error.message : String(error);
361
+ }
362
+ //# sourceMappingURL=schedules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedules.js","sourceRoot":"","sources":["../../src/schedules.ts"],"names":[],"mappings":"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"}
@@ -11,6 +11,7 @@ export declare const DURABLE_TABLES: {
11
11
  readonly signalWaiters: 'durable_signal_waiters';
12
12
  readonly bufferedSignals: 'durable_buffered_signals';
13
13
  readonly bufferedEvents: 'durable_buffered_events';
14
+ readonly schedules: 'durable_schedules';
14
15
  };
15
16
  /**
16
17
  * Where {@link createDurableTables} reports an APPLIED repair. Structurally satisfied by `console`
@@ -1 +1 @@
1
- {"version":3,"file":"lucid-schema.d.ts","sourceRoot":"","sources":["../../../src/stores/lucid-schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEzD;;;;GAIG;AACH,eAAO,MAAM,cAAc;aACzB,IAAI,EAAE,uBAAuB;aAC7B,WAAW,EAAE,0BAA0B;aACvC,UAAU,EAAE,wBAAwB;aACpC,aAAa,EAAE,wBAAwB;aACvC,eAAe,EAAE,0BAA0B;aAC3C,cAAc,EAAE,yBAAyB;CACjC,CAAC;AAEX;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,0BAA0B;IACzC,kEAAkE;IAClE,MAAM,CAAC,EAAE,mBAAmB,CAAC;CAC9B;AA8ED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,QAAQ,EACZ,cAAc,CAAC,EAAE,MAAM,EACvB,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,IAAI,CAAC,CAyNf;AAED,uFAAuF;AACvF,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,QAAQ,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ5F"}
1
+ {"version":3,"file":"lucid-schema.d.ts","sourceRoot":"","sources":["../../../src/stores/lucid-schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEzD;;;;GAIG;AACH,eAAO,MAAM,cAAc;aACzB,IAAI,EAAE,uBAAuB;aAC7B,WAAW,EAAE,0BAA0B;aACvC,UAAU,EAAE,wBAAwB;aACpC,aAAa,EAAE,wBAAwB;aACvC,eAAe,EAAE,0BAA0B;aAC3C,cAAc,EAAE,yBAAyB;aACzC,SAAS,EAAE,mBAAmB;CACtB,CAAC;AAEX;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,0BAA0B;IACzC,kEAAkE;IAClE,MAAM,CAAC,EAAE,mBAAmB,CAAC;CAC9B;AA8ED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,QAAQ,EACZ,cAAc,CAAC,EAAE,MAAM,EACvB,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,IAAI,CAAC,CA6Of;AAED,uFAAuF;AACvF,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,QAAQ,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAS5F"}
@@ -10,6 +10,7 @@ export const DURABLE_TABLES = {
10
10
  signalWaiters: 'durable_signal_waiters',
11
11
  bufferedSignals: 'durable_buffered_signals',
12
12
  bufferedEvents: 'durable_buffered_events',
13
+ schedules: 'durable_schedules',
13
14
  };
14
15
  /**
15
16
  * The warning {@link createDurableTables} emits when it actually `ALTER`ed something.
@@ -301,6 +302,25 @@ export async function createDurableTables(db, connectionName, options = {}) {
301
302
  table.index(['name', 'published_at'], 'durable_buffered_events_name_idx');
302
303
  });
303
304
  }
305
+ // Persisted schedules (`engine.schedules`): the store filters on the columns; `spec`/`state` are
306
+ // engine-owned JSON documents it keeps verbatim. `next_fire_at` is epoch ms (NULL = never fires
307
+ // again), exact, so `updateSchedule`'s compare-and-set on it is reliable on every dialect.
308
+ if (!(await conn().hasTable(DURABLE_TABLES.schedules))) {
309
+ await conn().createTable(DURABLE_TABLES.schedules, (table) => {
310
+ table.string('id').primary();
311
+ table.string('namespace').notNullable().defaultTo('default');
312
+ table.string('workflow').notNullable();
313
+ table.boolean('paused').notNullable().defaultTo(false);
314
+ table.bigInteger('next_fire_at');
315
+ table.text('tags');
316
+ table.text('spec').notNullable();
317
+ table.text('state').notNullable();
318
+ table.bigInteger('created_at').notNullable();
319
+ table.bigInteger('updated_at').notNullable();
320
+ // The worker tick's "due by T" scan: `paused = false AND next_fire_at <= T`.
321
+ table.index(['paused', 'next_fire_at'], 'durable_schedules_due_idx');
322
+ });
323
+ }
304
324
  // ONE warning for the whole call, listing every column added, or none at all. Not one per ALTER:
305
325
  // an operator needs to know their schema was behind and by what, and a burst of lines is easier to
306
326
  // lose than a single one.
@@ -311,6 +331,7 @@ export async function createDurableTables(db, connectionName, options = {}) {
311
331
  /** Drop every durable table (reverse FK order). Used by tests and migration `down`. */
312
332
  export async function dropDurableTables(db, connectionName) {
313
333
  const conn = () => db.connection(connectionName).schema;
334
+ await conn().dropTableIfExists(DURABLE_TABLES.schedules);
314
335
  await conn().dropTableIfExists(DURABLE_TABLES.bufferedEvents);
315
336
  await conn().dropTableIfExists(DURABLE_TABLES.bufferedSignals);
316
337
  await conn().dropTableIfExists(DURABLE_TABLES.signalWaiters);