@zmdb/jobs 1.0.0-beta.1

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.
@@ -0,0 +1,442 @@
1
+ // @zmdb/jobs/schedule — app-owned cron and interval scheduling.
2
+ //
3
+ // Decorators record declarations only. createScheduler receives the instances
4
+ // one application constructed, parses every expression once, and owns all
5
+ // timers, run state and leases for that application.
6
+ import '@zmdb/app';
7
+ import { createCronPlan } from './cron.js';
8
+ import { createLeaseHolder, createLeaseSession } from './lease.js';
9
+ const SCHEDULES = Symbol('zmdb.jobs.schedules');
10
+ const DEFAULT_TIME_ZONE = 'UTC';
11
+ const DEFAULT_TIMEOUT_MS = 300_000;
12
+ const DEFAULT_LEASE_MS = 60_000;
13
+ const DEFAULT_GRACE_MS = 15_000;
14
+ const MAX_TIMER_MS = 2_147_483_647;
15
+ // boundary: Cron and Interval are the only writers of SCHEDULES, so this typed
16
+ // view of their private metadata slot is sound.
17
+ function scheduleMetadata(metadata) {
18
+ return metadata;
19
+ }
20
+ function pushSchedule(metadata, schedule) {
21
+ const view = scheduleMetadata(metadata);
22
+ const own = Object.hasOwn(metadata, SCHEDULES) ? view[SCHEDULES] : undefined;
23
+ if (own === undefined) {
24
+ view[SCHEDULES] = [schedule];
25
+ }
26
+ else {
27
+ own.push(schedule);
28
+ }
29
+ }
30
+ function ownSchedules(metadata) {
31
+ if (!Object.hasOwn(metadata, SCHEDULES)) {
32
+ return [];
33
+ }
34
+ return scheduleMetadata(metadata)[SCHEDULES] ?? [];
35
+ }
36
+ function composedSchedules(metadata) {
37
+ const baseFirst = [];
38
+ for (let record = metadata; record !== null; record = Object.getPrototypeOf(record)) {
39
+ baseFirst.unshift(record);
40
+ }
41
+ let composed = [];
42
+ for (const record of baseFirst) {
43
+ const own = ownSchedules(record);
44
+ if (own.length === 0) {
45
+ continue;
46
+ }
47
+ const overridden = new Set(own.map(schedule => schedule.method));
48
+ composed = [...composed.filter(schedule => !overridden.has(schedule.method)), ...own];
49
+ }
50
+ return composed;
51
+ }
52
+ /** Declare one cron-triggered method. The expression is parsed at scheduler construction. */
53
+ export function Cron(expression, options) {
54
+ return function (_target, context) {
55
+ const method = String(context.name);
56
+ pushSchedule(context.metadata, {
57
+ method,
58
+ trigger: { kind: 'cron', expression },
59
+ runs: options.runs,
60
+ ...(options.name === undefined ? {} : { name: options.name }),
61
+ ...(options.timeZone === undefined ? {} : { timeZone: options.timeZone }),
62
+ ...(options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }),
63
+ });
64
+ };
65
+ }
66
+ /** Declare one fixed-duration, completion-to-start method. */
67
+ export function Interval(everyMs, options) {
68
+ return function (_target, context) {
69
+ const method = String(context.name);
70
+ if ('timeZone' in options) {
71
+ throw new RangeError(`@zmdb/jobs: interval schedule "${options.name ?? method}" cannot set timeZone; intervals are durations`);
72
+ }
73
+ pushSchedule(context.metadata, {
74
+ method,
75
+ trigger: { kind: 'interval', everyMs },
76
+ runs: options.runs,
77
+ ...(options.name === undefined ? {} : { name: options.name }),
78
+ ...(options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }),
79
+ });
80
+ };
81
+ }
82
+ /** Read and normalize the schedules declared by one class. */
83
+ export function schedulesOf(controller) {
84
+ return schedulesFor(controller, controller.name);
85
+ }
86
+ /** Build one isolated scheduler for the supplied application-owned instances. */
87
+ export function createScheduler(options) {
88
+ const leaseMs = duration(options.leaseMs ?? DEFAULT_LEASE_MS, 'leaseMs');
89
+ const graceMs = duration(options.graceMs ?? DEFAULT_GRACE_MS, 'graceMs');
90
+ const tasks = runtimeTasks(options);
91
+ const clusterTasks = tasks
92
+ .filter(task => task.definition.runs === 'once-per-cluster')
93
+ .map(task => task.definition.name);
94
+ if (clusterTasks.length > 0 && options.leases === undefined) {
95
+ throw new Error(`@zmdb/jobs: once-per-cluster schedules require leases: ${clusterTasks.join(', ')}`);
96
+ }
97
+ return new AppScheduler(options, tasks, leaseMs, graceMs);
98
+ }
99
+ function schedulesFor(controller, className) {
100
+ const carrier = controller;
101
+ const metadata = carrier[Symbol.metadata];
102
+ if (metadata === undefined || metadata === null) {
103
+ return [];
104
+ }
105
+ return composedSchedules(metadata).map(schedule => normalizedSchedule(schedule, className));
106
+ }
107
+ function normalizedSchedule(schedule, className) {
108
+ const fallbackClass = className.length === 0 ? '<anonymous>' : className;
109
+ const name = schedule.name ?? `${fallbackClass}.${schedule.method}`;
110
+ if (name.length === 0) {
111
+ throw new RangeError('@zmdb/jobs: a scheduled task name cannot be empty');
112
+ }
113
+ const timeoutMs = duration(schedule.timeoutMs ?? DEFAULT_TIMEOUT_MS, `${name}.timeoutMs`);
114
+ if (schedule.trigger.kind === 'interval') {
115
+ const everyMs = duration(schedule.trigger.everyMs, `${name}.everyMs`);
116
+ if (everyMs > MAX_TIMER_MS) {
117
+ throw new RangeError(`@zmdb/jobs: interval schedule "${name}" exceeds ${String(MAX_TIMER_MS)}ms; use @Cron for calendar time`);
118
+ }
119
+ return {
120
+ name,
121
+ method: schedule.method,
122
+ trigger: { kind: 'interval', everyMs },
123
+ runs: taskRuns(schedule.runs, name),
124
+ timeZone: DEFAULT_TIME_ZONE,
125
+ timeoutMs,
126
+ };
127
+ }
128
+ return {
129
+ name,
130
+ method: schedule.method,
131
+ trigger: schedule.trigger,
132
+ runs: taskRuns(schedule.runs, name),
133
+ timeZone: schedule.timeZone ?? DEFAULT_TIME_ZONE,
134
+ timeoutMs,
135
+ };
136
+ }
137
+ function taskRuns(value, name) {
138
+ if (value !== 'once-per-replica' && value !== 'once-per-cluster') {
139
+ throw new RangeError(`@zmdb/jobs: schedule "${name}" has invalid runs value "${String(value)}"`);
140
+ }
141
+ return value;
142
+ }
143
+ function duration(value, name) {
144
+ if (!Number.isInteger(value) || value <= 0) {
145
+ throw new RangeError(`@zmdb/jobs: ${name} must be a positive integer`);
146
+ }
147
+ return value;
148
+ }
149
+ function shutdownGrace(value) {
150
+ if (!Number.isInteger(value) || value < 0) {
151
+ throw new RangeError('@zmdb/jobs: graceMs must be a non-negative integer');
152
+ }
153
+ return value;
154
+ }
155
+ function runtimeTasks(options) {
156
+ const result = [];
157
+ const names = new Set();
158
+ const now = options.clock.now();
159
+ for (const instance of options.tasks) {
160
+ for (const definition of schedulesFor(instance.constructor, instance.constructor.name)) {
161
+ if (names.has(definition.name)) {
162
+ throw new Error(`@zmdb/jobs: duplicate scheduled task name "${definition.name}"`);
163
+ }
164
+ names.add(definition.name);
165
+ const value = Reflect.get(instance, definition.method);
166
+ if (typeof value !== 'function') {
167
+ throw new Error(`@zmdb/jobs: scheduled method "${definition.name}" is not callable on its instance`);
168
+ }
169
+ const invoke = async (signal) => {
170
+ await Reflect.apply(value, instance, [signal]);
171
+ };
172
+ if (definition.trigger.kind === 'cron') {
173
+ const plan = createCronPlan(definition.trigger.expression, definition.timeZone, definition.name);
174
+ const initial = plan.nextAtOrAfter(now);
175
+ const initialDue = initial === now ? now : undefined;
176
+ result.push({
177
+ definition,
178
+ trigger: { kind: 'cron', plan },
179
+ invoke,
180
+ initialDue,
181
+ firstTick: true,
182
+ nextAt: initialDue === undefined ? initial : plan.nextAfter(now),
183
+ running: undefined,
184
+ disabled: false,
185
+ });
186
+ }
187
+ else {
188
+ result.push({
189
+ definition,
190
+ trigger: definition.trigger,
191
+ invoke,
192
+ initialDue: undefined,
193
+ firstTick: true,
194
+ nextAt: now + definition.trigger.everyMs,
195
+ running: undefined,
196
+ disabled: false,
197
+ });
198
+ }
199
+ }
200
+ }
201
+ return result;
202
+ }
203
+ class AppScheduler {
204
+ #options;
205
+ #tasks;
206
+ #leaseMs;
207
+ #graceMs;
208
+ #holder = createLeaseHolder();
209
+ #started = false;
210
+ #stopped = false;
211
+ #sleepController;
212
+ #loop = Promise.resolve();
213
+ #shutdown;
214
+ constructor(options, tasks, leaseMs, graceMs) {
215
+ this.#options = options;
216
+ this.#tasks = tasks;
217
+ this.#leaseMs = leaseMs;
218
+ this.#graceMs = graceMs;
219
+ }
220
+ start() {
221
+ if (this.#started || this.#stopped) {
222
+ return;
223
+ }
224
+ this.#started = true;
225
+ void this.tick(this.#options.clock.now()).catch(() => undefined);
226
+ this.#loop = this.#runLoop();
227
+ }
228
+ async tick(now) {
229
+ if (this.#stopped) {
230
+ return;
231
+ }
232
+ await Promise.all(this.#tasks.map(task => this.#tickTask(task, now)));
233
+ }
234
+ onShutdown(options) {
235
+ if (this.#shutdown !== undefined)
236
+ return this.#shutdown;
237
+ const cap = options?.graceMs;
238
+ if (cap !== undefined)
239
+ shutdownGrace(cap);
240
+ const graceMs = Math.min(this.#graceMs, cap ?? this.#graceMs);
241
+ this.#shutdown = this.#stop(graceMs);
242
+ return this.#shutdown;
243
+ }
244
+ async #tickTask(task, now) {
245
+ if (task.disabled || !Number.isFinite(task.nextAt)) {
246
+ return;
247
+ }
248
+ if (task.firstTick) {
249
+ task.firstTick = false;
250
+ if (task.initialDue === now) {
251
+ task.initialDue = undefined;
252
+ await this.#launch(task, now);
253
+ return;
254
+ }
255
+ task.initialDue = undefined;
256
+ }
257
+ if (task.nextAt < now) {
258
+ this.#skip(task, task.nextAt, task.running === undefined ? 'missed' : 'still-running');
259
+ task.nextAt =
260
+ task.trigger.kind === 'cron'
261
+ ? task.trigger.plan.nextAtOrAfter(now)
262
+ : task.running === undefined
263
+ ? now + task.trigger.everyMs
264
+ : Number.POSITIVE_INFINITY;
265
+ }
266
+ if (task.nextAt !== now) {
267
+ return;
268
+ }
269
+ const scheduledFor = task.nextAt;
270
+ task.nextAt = task.trigger.kind === 'cron' ? task.trigger.plan.nextAfter(scheduledFor) : Number.POSITIVE_INFINITY;
271
+ if (task.running !== undefined) {
272
+ this.#skip(task, scheduledFor, 'still-running');
273
+ return;
274
+ }
275
+ await this.#launch(task, scheduledFor);
276
+ }
277
+ async #launch(task, scheduledFor) {
278
+ let complete = () => undefined;
279
+ const completion = new Promise(resolve => {
280
+ complete = resolve;
281
+ });
282
+ const active = {
283
+ controller: new AbortController(),
284
+ completion,
285
+ complete,
286
+ lease: undefined,
287
+ timedOut: false,
288
+ };
289
+ task.running = active;
290
+ if (task.definition.runs === 'once-per-cluster') {
291
+ const leases = this.#options.leases;
292
+ if (leases === undefined) {
293
+ this.#finishWithoutRun(task, active);
294
+ return;
295
+ }
296
+ const session = createLeaseSession({
297
+ store: leases,
298
+ clock: this.#options.clock,
299
+ key: task.definition.name,
300
+ holder: this.#holder,
301
+ ttlMs: this.#leaseMs,
302
+ });
303
+ active.lease = session;
304
+ let acquired = false;
305
+ try {
306
+ acquired = await session.acquire();
307
+ }
308
+ catch (error) {
309
+ this.#error(task, scheduledFor, error);
310
+ }
311
+ if (!acquired) {
312
+ this.#skip(task, scheduledFor, 'lease-not-held');
313
+ await session.release();
314
+ this.#finishWithoutRun(task, active);
315
+ return;
316
+ }
317
+ session.startRenewing(error => {
318
+ task.disabled = true;
319
+ active.controller.abort(error);
320
+ this.#error(task, scheduledFor, error);
321
+ this.#wake();
322
+ });
323
+ }
324
+ const timerController = new AbortController();
325
+ active.controller.signal.addEventListener('abort', () => timerController.abort(active.controller.signal.reason), {
326
+ once: true,
327
+ });
328
+ const invocation = task.invoke(active.controller.signal).then(() => ({ kind: 'resolved' }), error => ({ kind: 'rejected', error }));
329
+ const invocationCompletion = invocation.then(async (settlement) => {
330
+ timerController.abort();
331
+ if (settlement.kind === 'rejected' && !active.timedOut) {
332
+ this.#error(task, scheduledFor, settlement.error);
333
+ }
334
+ await this.#finishRun(task, active);
335
+ });
336
+ const timeout = this.#options.clock.sleep(task.definition.timeoutMs, timerController.signal).then(() => {
337
+ if (task.running === active) {
338
+ active.timedOut = true;
339
+ const error = new Error(`@zmdb/jobs: scheduled task "${task.definition.name}" timed out after ${String(task.definition.timeoutMs)}ms`);
340
+ active.controller.abort(error);
341
+ this.#error(task, scheduledFor, error);
342
+ }
343
+ }, () => undefined);
344
+ await Promise.race([invocationCompletion, timeout]);
345
+ }
346
+ #finishWithoutRun(task, active) {
347
+ if (task.running === active) {
348
+ task.running = undefined;
349
+ if (task.trigger.kind === 'interval') {
350
+ task.nextAt = this.#options.clock.now() + task.trigger.everyMs;
351
+ }
352
+ }
353
+ active.complete();
354
+ this.#wake();
355
+ }
356
+ async #finishRun(task, active) {
357
+ await active.lease?.release();
358
+ if (task.running === active) {
359
+ task.running = undefined;
360
+ if (task.trigger.kind === 'interval' && !task.disabled && !this.#stopped) {
361
+ task.nextAt = this.#options.clock.now() + task.trigger.everyMs;
362
+ }
363
+ }
364
+ active.complete();
365
+ this.#wake();
366
+ }
367
+ #skip(task, scheduledFor, reason) {
368
+ try {
369
+ this.#options.onSkipped({
370
+ task: task.definition.name,
371
+ scheduledFor: new Date(scheduledFor),
372
+ reason,
373
+ });
374
+ }
375
+ catch {
376
+ // Observation cannot stop the scheduler.
377
+ }
378
+ }
379
+ #error(task, scheduledFor, error) {
380
+ try {
381
+ this.#options.onTaskError(task.definition.name, new Date(scheduledFor), error);
382
+ }
383
+ catch {
384
+ // Observation cannot replace the original task failure.
385
+ }
386
+ }
387
+ async #runLoop() {
388
+ while (!this.#stopped) {
389
+ if (this.#tasks.length === 0) {
390
+ return;
391
+ }
392
+ const nextAt = this.#tasks.reduce((earliest, task) => (task.disabled ? earliest : Math.min(earliest, task.nextAt)), Number.POSITIVE_INFINITY);
393
+ const now = this.#options.clock.now();
394
+ const delay = Number.isFinite(nextAt) ? Math.min(MAX_TIMER_MS, Math.max(0, nextAt - now)) : MAX_TIMER_MS;
395
+ const controller = new AbortController();
396
+ this.#sleepController = controller;
397
+ try {
398
+ await this.#options.clock.sleep(delay, controller.signal);
399
+ }
400
+ catch {
401
+ if (this.#stopped) {
402
+ return;
403
+ }
404
+ }
405
+ if (this.#sleepController === controller) {
406
+ this.#sleepController = undefined;
407
+ }
408
+ if (this.#stopped || controller.signal.aborted) {
409
+ continue;
410
+ }
411
+ void this.tick(this.#options.clock.now()).catch(() => undefined);
412
+ await Promise.resolve();
413
+ }
414
+ }
415
+ #wake() {
416
+ this.#sleepController?.abort();
417
+ }
418
+ async #stop(graceMs) {
419
+ this.#stopped = true;
420
+ this.#wake();
421
+ await this.#loop;
422
+ const active = this.#tasks.flatMap(task => (task.running === undefined ? [] : [task.running]));
423
+ if (active.length === 0) {
424
+ return;
425
+ }
426
+ if (graceMs > 0) {
427
+ const graceController = new AbortController();
428
+ const settled = Promise.all(active.map(run => run.completion)).then(() => 'settled');
429
+ const grace = this.#options.clock.sleep(graceMs, graceController.signal).then(() => 'expired', () => 'cancelled');
430
+ const outcome = await Promise.race([settled, grace]);
431
+ if (outcome === 'settled') {
432
+ graceController.abort();
433
+ return;
434
+ }
435
+ }
436
+ for (const run of active) {
437
+ run.controller.abort(new Error('@zmdb/jobs: scheduler shutdown grace expired'));
438
+ await run.lease?.release();
439
+ }
440
+ }
441
+ }
442
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/schedule/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { Clock } from '../queues/index.js';
2
+ /** A renewable lease over one scheduled task name. */
3
+ export interface LeaseStore {
4
+ acquire(key: string, holder: string, ttlMs: number): Promise<boolean>;
5
+ renew(key: string, holder: string, ttlMs: number): Promise<boolean>;
6
+ release(key: string, holder: string): Promise<void>;
7
+ }
8
+ export interface LeaseSession {
9
+ readonly signal: AbortSignal;
10
+ acquire(): Promise<boolean>;
11
+ startRenewing(onLost: (error: Error) => void): void;
12
+ release(): Promise<void>;
13
+ }
14
+ interface LeaseSessionOptions {
15
+ readonly store: LeaseStore;
16
+ readonly clock: Clock;
17
+ readonly key: string;
18
+ readonly holder: string;
19
+ readonly ttlMs: number;
20
+ }
21
+ /** One opaque holder id per scheduler instance, shared by its per-task leases. */
22
+ export declare function createLeaseHolder(): string;
23
+ /** Coordinate one task run without adding a backend dependency to @zmdb/jobs. */
24
+ export declare function createLeaseSession(options: LeaseSessionOptions): LeaseSession;
25
+ export {};
26
+ //# sourceMappingURL=lease.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lease.d.ts","sourceRoot":"","sources":["../../src/schedule/lease.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAEhD,sDAAsD;AACtD,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtE,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACpE,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5B,aAAa,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI,CAAC;IACpD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,UAAU,mBAAmB;IAC3B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,kFAAkF;AAClF,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,iFAAiF;AACjF,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,YAAY,CAoF7E"}
@@ -0,0 +1,90 @@
1
+ /** One opaque holder id per scheduler instance, shared by its per-task leases. */
2
+ export function createLeaseHolder() {
3
+ return globalThis.crypto.randomUUID();
4
+ }
5
+ /** Coordinate one task run without adding a backend dependency to @zmdb/jobs. */
6
+ export function createLeaseSession(options) {
7
+ const controller = new AbortController();
8
+ let held = false;
9
+ let released = false;
10
+ let renewal = Promise.resolve();
11
+ const renew = async (onLost) => {
12
+ const delay = Math.max(1, Math.floor(options.ttlMs / 3));
13
+ while (!controller.signal.aborted) {
14
+ try {
15
+ await options.clock.sleep(delay, controller.signal);
16
+ }
17
+ catch {
18
+ return;
19
+ }
20
+ if (controller.signal.aborted) {
21
+ return;
22
+ }
23
+ let renewed = false;
24
+ try {
25
+ renewed = await options.store.renew(options.key, options.holder, options.ttlMs);
26
+ }
27
+ catch (error) {
28
+ held = false;
29
+ controller.abort(error);
30
+ onLost(error instanceof Error
31
+ ? error
32
+ : new Error(`@zmdb/jobs: lease renewal for "${options.key}" failed: ${String(error)}`));
33
+ return;
34
+ }
35
+ if (!renewed) {
36
+ held = false;
37
+ const error = new Error(`@zmdb/jobs: lease renewal for "${options.key}" was refused`);
38
+ controller.abort(error);
39
+ onLost(error);
40
+ return;
41
+ }
42
+ }
43
+ };
44
+ return {
45
+ signal: controller.signal,
46
+ async acquire() {
47
+ if (released) {
48
+ return false;
49
+ }
50
+ const acquired = await options.store.acquire(options.key, options.holder, options.ttlMs);
51
+ if (released) {
52
+ if (acquired) {
53
+ try {
54
+ await options.store.release(options.key, options.holder);
55
+ }
56
+ catch {
57
+ // The lease still expires if a release after cancellation fails.
58
+ }
59
+ }
60
+ return false;
61
+ }
62
+ held = acquired;
63
+ return held;
64
+ },
65
+ startRenewing(onLost) {
66
+ if (held && !released) {
67
+ renewal = renew(onLost);
68
+ }
69
+ },
70
+ async release() {
71
+ if (released) {
72
+ return;
73
+ }
74
+ released = true;
75
+ controller.abort();
76
+ await renewal;
77
+ if (!held) {
78
+ return;
79
+ }
80
+ held = false;
81
+ try {
82
+ await options.store.release(options.key, options.holder);
83
+ }
84
+ catch {
85
+ // Best effort. A failed release is bounded by the lease TTL.
86
+ }
87
+ },
88
+ };
89
+ }
90
+ //# sourceMappingURL=lease.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lease.js","sourceRoot":"","sources":["../../src/schedule/lease.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@zmdb/jobs",
3
+ "version": "1.0.0-beta.1",
4
+ "description": "Portable typed queues, workers, scheduling, leases, and application lifecycle integration.",
5
+ "keywords": [
6
+ "cron",
7
+ "jobs",
8
+ "queue",
9
+ "scheduler",
10
+ "typescript",
11
+ "worker",
12
+ "zmdb"
13
+ ],
14
+ "homepage": "https://github.com/ambasta/zmdb#readme",
15
+ "bugs": {
16
+ "url": "https://github.com/ambasta/zmdb/issues"
17
+ },
18
+ "license": "GPL-3.0-or-later",
19
+ "author": "zmdb contributors",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/ambasta/zmdb.git",
23
+ "directory": "packages/jobs"
24
+ },
25
+ "type": "module",
26
+ "sideEffects": false,
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "import": "./dist/index.js"
31
+ },
32
+ "./schedule": {
33
+ "types": "./dist/schedule/index.d.ts",
34
+ "import": "./dist/schedule/index.js"
35
+ }
36
+ },
37
+ "publishConfig": {
38
+ "access": "public",
39
+ "tag": "beta"
40
+ },
41
+ "scripts": {
42
+ "build": "node ../../scripts/build-package.mjs",
43
+ "test": "vitest run"
44
+ },
45
+ "dependencies": {
46
+ "@zmdb/app": "1.0.0-beta.1"
47
+ },
48
+ "engines": {
49
+ "node": ">=26"
50
+ },
51
+ "main": "./dist/index.js",
52
+ "types": "./dist/index.d.ts"
53
+ }