@lunora/scheduler 1.0.0-alpha.7 → 1.0.0-alpha.8
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/dist/index.d.mts +29 -17
- package/dist/index.d.ts +29 -17
- package/dist/index.mjs +4 -4
- package/dist/packem_shared/{CRON_SCHEDULE_KINDS-BMVeCHOu.mjs → CRON_SCHEDULE_KINDS-C2FflEnq.mjs} +13 -3
- package/dist/packem_shared/{SchedulerDO-DVeJrNbs.mjs → SchedulerDO-DbXUK1Qa.mjs} +46 -40
- package/dist/packem_shared/{createScheduler-CcD09oIm.mjs → createScheduler-DGVqs2Ft.mjs} +1 -27
- package/dist/packem_shared/{createWorkpool-C28trhTA.mjs → createWorkpool-Ce0kNM2p.mjs} +1 -27
- package/dist/packem_shared/do-client-CMJtLoHO.mjs +42 -0
- package/package.json +2 -2
- package/dist/packem_shared/jurisdiction-CR2zC3Et.mjs +0 -13
package/dist/index.d.mts
CHANGED
|
@@ -648,15 +648,17 @@ declare class SchedulerDO {
|
|
|
648
648
|
*
|
|
649
649
|
* Claims the job by deleting its time-index entry BEFORE dispatch (an alarm
|
|
650
650
|
* re-fire then won't pick it up again), runs {@link drainRecord}, and on a
|
|
651
|
-
* thrown storage op
|
|
651
|
+
* thrown storage op re-asserts the claim so the job stays re-fireable.
|
|
652
652
|
*
|
|
653
|
-
*
|
|
654
|
-
*
|
|
655
|
-
*
|
|
656
|
-
*
|
|
657
|
-
*
|
|
658
|
-
*
|
|
659
|
-
*
|
|
653
|
+
* A throw reaching here always means the job was NOT dispatched:
|
|
654
|
+
* {@link drainRecord} swallows its own post-dispatch cleanup errors and
|
|
655
|
+
* returns instead of throwing once a kick succeeds, so every escaping throw
|
|
656
|
+
* comes from the pre-dispatch or failed-dispatch paths. We therefore always
|
|
657
|
+
* re-assert the time-index claim so a later alarm re-attempts it
|
|
658
|
+
* (at-least-once): the claim delete may have removed it and
|
|
659
|
+
* recordRetry()/requeuePooled() may not have re-armed it before throwing, and
|
|
660
|
+
* re-inserting the same key is idempotent, so a surviving claim is simply
|
|
661
|
+
* rewritten to its prior value.
|
|
660
662
|
*/
|
|
661
663
|
private drainRecordGuarded;
|
|
662
664
|
/**
|
|
@@ -666,16 +668,18 @@ declare class SchedulerDO {
|
|
|
666
668
|
* free slot is reserved durably before dispatch and released immediately if
|
|
667
669
|
* the kick fails (success holds it until the runtime reports completion).
|
|
668
670
|
* Success clears the `id:`/`retry:` rows; failure routes to
|
|
669
|
-
* {@link recordRetry}.
|
|
670
|
-
*
|
|
671
|
+
* {@link recordRetry}. Pool state is read FRESH from storage per record (see
|
|
672
|
+
* {@link reservePoolSlot}) and never held across the dispatch() await, so a
|
|
673
|
+
* concurrent /complete landing mid-dispatch can't be clobbered.
|
|
674
|
+
* Once a kick succeeds, post-dispatch cleanup (clearing the `id:`/`retry:`
|
|
675
|
+
* rows) is swallowed rather than allowed to throw, so a successful dispatch
|
|
676
|
+
* NEVER propagates an error to {@link drainRecordGuarded}: every throw that
|
|
677
|
+
* escapes comes from the pre-dispatch or failed-dispatch paths, where the job
|
|
678
|
+
* is still re-fireable and the guard safely re-claims the time index.
|
|
671
679
|
* @returns `true` only when the record was successfully dispatched (a 2xx
|
|
672
|
-
* kick)
|
|
673
|
-
*
|
|
674
|
-
*
|
|
675
|
-
* an already-kicked job would break idempotency. A `false` return (pool
|
|
676
|
-
* backpressure or a failed dispatch) means the job is still re-fireable —
|
|
677
|
-
* either already re-armed here, or, if a throw escapes, re-claimed by the
|
|
678
|
-
* guard's catch.
|
|
680
|
+
* kick); `false` on pool backpressure or a failed dispatch (the job is still
|
|
681
|
+
* re-fireable — already re-armed here). The value is informational (the guard
|
|
682
|
+
* branches on throw/no-throw, not on this boolean).
|
|
679
683
|
*/
|
|
680
684
|
private drainRecord;
|
|
681
685
|
/**
|
|
@@ -683,6 +687,14 @@ declare class SchedulerDO {
|
|
|
683
687
|
* job via {@link requeuePooled}) when the pool is at `maxConcurrency`;
|
|
684
688
|
* otherwise reserves a slot durably and returns `true`. Non-pooled records
|
|
685
689
|
* always return `true` without touching any pool state.
|
|
690
|
+
*
|
|
691
|
+
* The pool row is read FRESH from storage on every call — never cached
|
|
692
|
+
* across the drain. Each reservation durably `savePool()`s before the next
|
|
693
|
+
* record runs, so a same-pass reservation is still visible to the next
|
|
694
|
+
* record's fresh read (the budget carries forward); and because dispatch()
|
|
695
|
+
* awaits an outbound fetch between records, a concurrent /complete that
|
|
696
|
+
* decrements the row mid-drain IS reflected here instead of being clobbered
|
|
697
|
+
* by a stale in-memory copy (which would leak a slot permanently).
|
|
686
698
|
*/
|
|
687
699
|
private reservePoolSlot;
|
|
688
700
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -648,15 +648,17 @@ declare class SchedulerDO {
|
|
|
648
648
|
*
|
|
649
649
|
* Claims the job by deleting its time-index entry BEFORE dispatch (an alarm
|
|
650
650
|
* re-fire then won't pick it up again), runs {@link drainRecord}, and on a
|
|
651
|
-
* thrown storage op
|
|
651
|
+
* thrown storage op re-asserts the claim so the job stays re-fireable.
|
|
652
652
|
*
|
|
653
|
-
*
|
|
654
|
-
*
|
|
655
|
-
*
|
|
656
|
-
*
|
|
657
|
-
*
|
|
658
|
-
*
|
|
659
|
-
*
|
|
653
|
+
* A throw reaching here always means the job was NOT dispatched:
|
|
654
|
+
* {@link drainRecord} swallows its own post-dispatch cleanup errors and
|
|
655
|
+
* returns instead of throwing once a kick succeeds, so every escaping throw
|
|
656
|
+
* comes from the pre-dispatch or failed-dispatch paths. We therefore always
|
|
657
|
+
* re-assert the time-index claim so a later alarm re-attempts it
|
|
658
|
+
* (at-least-once): the claim delete may have removed it and
|
|
659
|
+
* recordRetry()/requeuePooled() may not have re-armed it before throwing, and
|
|
660
|
+
* re-inserting the same key is idempotent, so a surviving claim is simply
|
|
661
|
+
* rewritten to its prior value.
|
|
660
662
|
*/
|
|
661
663
|
private drainRecordGuarded;
|
|
662
664
|
/**
|
|
@@ -666,16 +668,18 @@ declare class SchedulerDO {
|
|
|
666
668
|
* free slot is reserved durably before dispatch and released immediately if
|
|
667
669
|
* the kick fails (success holds it until the runtime reports completion).
|
|
668
670
|
* Success clears the `id:`/`retry:` rows; failure routes to
|
|
669
|
-
* {@link recordRetry}.
|
|
670
|
-
*
|
|
671
|
+
* {@link recordRetry}. Pool state is read FRESH from storage per record (see
|
|
672
|
+
* {@link reservePoolSlot}) and never held across the dispatch() await, so a
|
|
673
|
+
* concurrent /complete landing mid-dispatch can't be clobbered.
|
|
674
|
+
* Once a kick succeeds, post-dispatch cleanup (clearing the `id:`/`retry:`
|
|
675
|
+
* rows) is swallowed rather than allowed to throw, so a successful dispatch
|
|
676
|
+
* NEVER propagates an error to {@link drainRecordGuarded}: every throw that
|
|
677
|
+
* escapes comes from the pre-dispatch or failed-dispatch paths, where the job
|
|
678
|
+
* is still re-fireable and the guard safely re-claims the time index.
|
|
671
679
|
* @returns `true` only when the record was successfully dispatched (a 2xx
|
|
672
|
-
* kick)
|
|
673
|
-
*
|
|
674
|
-
*
|
|
675
|
-
* an already-kicked job would break idempotency. A `false` return (pool
|
|
676
|
-
* backpressure or a failed dispatch) means the job is still re-fireable —
|
|
677
|
-
* either already re-armed here, or, if a throw escapes, re-claimed by the
|
|
678
|
-
* guard's catch.
|
|
680
|
+
* kick); `false` on pool backpressure or a failed dispatch (the job is still
|
|
681
|
+
* re-fireable — already re-armed here). The value is informational (the guard
|
|
682
|
+
* branches on throw/no-throw, not on this boolean).
|
|
679
683
|
*/
|
|
680
684
|
private drainRecord;
|
|
681
685
|
/**
|
|
@@ -683,6 +687,14 @@ declare class SchedulerDO {
|
|
|
683
687
|
* job via {@link requeuePooled}) when the pool is at `maxConcurrency`;
|
|
684
688
|
* otherwise reserves a slot durably and returns `true`. Non-pooled records
|
|
685
689
|
* always return `true` without touching any pool state.
|
|
690
|
+
*
|
|
691
|
+
* The pool row is read FRESH from storage on every call — never cached
|
|
692
|
+
* across the drain. Each reservation durably `savePool()`s before the next
|
|
693
|
+
* record runs, so a same-pass reservation is still visible to the next
|
|
694
|
+
* record's fresh read (the budget carries forward); and because dispatch()
|
|
695
|
+
* awaits an outbound fetch between records, a concurrent /complete that
|
|
696
|
+
* decrements the row mid-drain IS reflected here instead of being clobbered
|
|
697
|
+
* by a stale in-memory copy (which would leak a slot permanently).
|
|
686
698
|
*/
|
|
687
699
|
private reservePoolSlot;
|
|
688
700
|
/**
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export { default as createScheduler } from './packem_shared/createScheduler-
|
|
2
|
-
export { default as createWorkpool } from './packem_shared/createWorkpool-
|
|
1
|
+
export { default as createScheduler } from './packem_shared/createScheduler-DGVqs2Ft.mjs';
|
|
2
|
+
export { default as createWorkpool } from './packem_shared/createWorkpool-Ce0kNM2p.mjs';
|
|
3
3
|
export { createCronTrigger } from './packem_shared/createCronTrigger-Dh4VLxD9.mjs';
|
|
4
|
-
export { CRON_SCHEDULE_KINDS, compileCronSchedule, cronJobs } from './packem_shared/CRON_SCHEDULE_KINDS-
|
|
4
|
+
export { CRON_SCHEDULE_KINDS, compileCronSchedule, cronJobs } from './packem_shared/CRON_SCHEDULE_KINDS-C2FflEnq.mjs';
|
|
5
5
|
export { createQueueConsumer, createQueueWorkpool, httpDispatcher } from './packem_shared/createQueueConsumer-Cy-Mp-El.mjs';
|
|
6
|
-
export { SchedulerDO } from './packem_shared/SchedulerDO-
|
|
6
|
+
export { SchedulerDO } from './packem_shared/SchedulerDO-DbXUK1Qa.mjs';
|
|
7
7
|
export { isWorkflowReference } from './packem_shared/isWorkflowReference-C9mQkMXt.mjs';
|
|
8
8
|
export { assertValidCronExpression, isValidCronExpression } from './packem_shared/assertValidCronExpression-B9m75qU0.mjs';
|
package/dist/packem_shared/{CRON_SCHEDULE_KINDS-BMVeCHOu.mjs → CRON_SCHEDULE_KINDS-C2FflEnq.mjs}
RENAMED
|
@@ -20,6 +20,16 @@ const field = (value, label, min, max) => {
|
|
|
20
20
|
}
|
|
21
21
|
return value.toFixed(0);
|
|
22
22
|
};
|
|
23
|
+
const stepField = (value, label, period) => {
|
|
24
|
+
const rendered = field(value, label, 1, period - 1);
|
|
25
|
+
if (period % value !== 0) {
|
|
26
|
+
throw new LunoraError(
|
|
27
|
+
"INTERNAL",
|
|
28
|
+
`@lunora/scheduler: ${label} must evenly divide ${period.toFixed(0)} for a fixed "every ${value.toFixed(0)}" interval — cron "*/${value.toFixed(0)}" means "at values divisible by ${value.toFixed(0)}", which wraps unevenly; pick a divisor of ${period.toFixed(0)}`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
return rendered;
|
|
32
|
+
};
|
|
23
33
|
const compileInterval = (schedule) => {
|
|
24
34
|
const units = ["seconds", "minutes", "hours"].filter((unit2) => schedule[unit2] !== void 0);
|
|
25
35
|
if (units.length !== 1) {
|
|
@@ -28,12 +38,12 @@ const compileInterval = (schedule) => {
|
|
|
28
38
|
const unit = units[0];
|
|
29
39
|
const value = schedule[unit];
|
|
30
40
|
if (unit === "seconds") {
|
|
31
|
-
return `*/${
|
|
41
|
+
return `*/${stepField(value, "interval.seconds", 60)} * * * * *`;
|
|
32
42
|
}
|
|
33
43
|
if (unit === "minutes") {
|
|
34
|
-
return `*/${
|
|
44
|
+
return `*/${stepField(value, "interval.minutes", 60)} * * * *`;
|
|
35
45
|
}
|
|
36
|
-
return `0 */${
|
|
46
|
+
return `0 */${stepField(value, "interval.hours", 24)} * * *`;
|
|
37
47
|
};
|
|
38
48
|
const compileDaily = (schedule) => {
|
|
39
49
|
const minute = field(schedule.minuteUTC, "daily.minuteUTC", 0, 59);
|
|
@@ -7,7 +7,7 @@ const POOL_PREFIX = "pool:";
|
|
|
7
7
|
const MAX_RETRY_ATTEMPTS = 5;
|
|
8
8
|
const RETRY_BASE_DELAY_MS = 3e4;
|
|
9
9
|
const POOL_BACKPRESSURE_DELAY_MS = 1e3;
|
|
10
|
-
const MAX_SCHEDULED_FOR_MS =
|
|
10
|
+
const MAX_SCHEDULED_FOR_MS = 999999999999999;
|
|
11
11
|
const TIME_PAD = 15;
|
|
12
12
|
const padTime = (n) => String(n).padStart(TIME_PAD, "0");
|
|
13
13
|
const generateId = () => {
|
|
@@ -162,13 +162,14 @@ class SchedulerDO {
|
|
|
162
162
|
const record = await this.state.storage.get(`${HEADER_PREFIX}${recordId}`);
|
|
163
163
|
if (record) {
|
|
164
164
|
due.push(record);
|
|
165
|
+
} else {
|
|
166
|
+
await this.state.storage.delete(indexKey);
|
|
165
167
|
}
|
|
166
168
|
}
|
|
167
169
|
}
|
|
168
|
-
const pools = /* @__PURE__ */ new Map();
|
|
169
170
|
try {
|
|
170
171
|
for (const record of due) {
|
|
171
|
-
await this.drainRecordGuarded(record
|
|
172
|
+
await this.drainRecordGuarded(record);
|
|
172
173
|
}
|
|
173
174
|
} finally {
|
|
174
175
|
await this.rescheduleAlarm();
|
|
@@ -237,27 +238,26 @@ class SchedulerDO {
|
|
|
237
238
|
*
|
|
238
239
|
* Claims the job by deleting its time-index entry BEFORE dispatch (an alarm
|
|
239
240
|
* re-fire then won't pick it up again), runs {@link drainRecord}, and on a
|
|
240
|
-
* thrown storage op
|
|
241
|
+
* thrown storage op re-asserts the claim so the job stays re-fireable.
|
|
241
242
|
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
243
|
+
* A throw reaching here always means the job was NOT dispatched:
|
|
244
|
+
* {@link drainRecord} swallows its own post-dispatch cleanup errors and
|
|
245
|
+
* returns instead of throwing once a kick succeeds, so every escaping throw
|
|
246
|
+
* comes from the pre-dispatch or failed-dispatch paths. We therefore always
|
|
247
|
+
* re-assert the time-index claim so a later alarm re-attempts it
|
|
248
|
+
* (at-least-once): the claim delete may have removed it and
|
|
249
|
+
* recordRetry()/requeuePooled() may not have re-armed it before throwing, and
|
|
250
|
+
* re-inserting the same key is idempotent, so a surviving claim is simply
|
|
251
|
+
* rewritten to its prior value.
|
|
249
252
|
*/
|
|
250
|
-
async drainRecordGuarded(record
|
|
251
|
-
let dispatched = false;
|
|
253
|
+
async drainRecordGuarded(record) {
|
|
252
254
|
try {
|
|
253
255
|
await this.state.storage.delete(SchedulerDO.indexKey(record.scheduledFor, record.id));
|
|
254
|
-
|
|
256
|
+
await this.drainRecord(record);
|
|
255
257
|
} catch {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
} catch {
|
|
260
|
-
}
|
|
258
|
+
try {
|
|
259
|
+
await this.state.storage.put(SchedulerDO.indexKey(record.scheduledFor, record.id), record.id);
|
|
260
|
+
} catch {
|
|
261
261
|
}
|
|
262
262
|
}
|
|
263
263
|
}
|
|
@@ -268,30 +268,29 @@ class SchedulerDO {
|
|
|
268
268
|
* free slot is reserved durably before dispatch and released immediately if
|
|
269
269
|
* the kick fails (success holds it until the runtime reports completion).
|
|
270
270
|
* Success clears the `id:`/`retry:` rows; failure routes to
|
|
271
|
-
* {@link recordRetry}.
|
|
272
|
-
*
|
|
271
|
+
* {@link recordRetry}. Pool state is read FRESH from storage per record (see
|
|
272
|
+
* {@link reservePoolSlot}) and never held across the dispatch() await, so a
|
|
273
|
+
* concurrent /complete landing mid-dispatch can't be clobbered.
|
|
274
|
+
* Once a kick succeeds, post-dispatch cleanup (clearing the `id:`/`retry:`
|
|
275
|
+
* rows) is swallowed rather than allowed to throw, so a successful dispatch
|
|
276
|
+
* NEVER propagates an error to {@link drainRecordGuarded}: every throw that
|
|
277
|
+
* escapes comes from the pre-dispatch or failed-dispatch paths, where the job
|
|
278
|
+
* is still re-fireable and the guard safely re-claims the time index.
|
|
273
279
|
* @returns `true` only when the record was successfully dispatched (a 2xx
|
|
274
|
-
* kick)
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
* an already-kicked job would break idempotency. A `false` return (pool
|
|
278
|
-
* backpressure or a failed dispatch) means the job is still re-fireable —
|
|
279
|
-
* either already re-armed here, or, if a throw escapes, re-claimed by the
|
|
280
|
-
* guard's catch.
|
|
280
|
+
* kick); `false` on pool backpressure or a failed dispatch (the job is still
|
|
281
|
+
* re-fireable — already re-armed here). The value is informational (the guard
|
|
282
|
+
* branches on throw/no-throw, not on this boolean).
|
|
281
283
|
*/
|
|
282
|
-
async drainRecord(record
|
|
283
|
-
const reserved = await this.reservePoolSlot(record
|
|
284
|
+
async drainRecord(record) {
|
|
285
|
+
const reserved = await this.reservePoolSlot(record);
|
|
284
286
|
if (!reserved) {
|
|
285
287
|
return false;
|
|
286
288
|
}
|
|
287
289
|
const ok = await this.dispatch(record);
|
|
288
290
|
if (!ok && record.pool !== void 0) {
|
|
289
|
-
const pool =
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
pools.set(record.pool, released);
|
|
293
|
-
await this.savePool(record.pool, released);
|
|
294
|
-
}
|
|
291
|
+
const pool = await this.loadPool(record.pool);
|
|
292
|
+
const released = SchedulerDO.releaseSlot(pool, record.id);
|
|
293
|
+
await this.savePool(record.pool, released);
|
|
295
294
|
}
|
|
296
295
|
if (ok) {
|
|
297
296
|
try {
|
|
@@ -308,13 +307,20 @@ class SchedulerDO {
|
|
|
308
307
|
* job via {@link requeuePooled}) when the pool is at `maxConcurrency`;
|
|
309
308
|
* otherwise reserves a slot durably and returns `true`. Non-pooled records
|
|
310
309
|
* always return `true` without touching any pool state.
|
|
310
|
+
*
|
|
311
|
+
* The pool row is read FRESH from storage on every call — never cached
|
|
312
|
+
* across the drain. Each reservation durably `savePool()`s before the next
|
|
313
|
+
* record runs, so a same-pass reservation is still visible to the next
|
|
314
|
+
* record's fresh read (the budget carries forward); and because dispatch()
|
|
315
|
+
* awaits an outbound fetch between records, a concurrent /complete that
|
|
316
|
+
* decrements the row mid-drain IS reflected here instead of being clobbered
|
|
317
|
+
* by a stale in-memory copy (which would leak a slot permanently).
|
|
311
318
|
*/
|
|
312
|
-
async reservePoolSlot(record
|
|
319
|
+
async reservePoolSlot(record) {
|
|
313
320
|
if (record.pool === void 0) {
|
|
314
321
|
return true;
|
|
315
322
|
}
|
|
316
|
-
const pool =
|
|
317
|
-
pools.set(record.pool, pool);
|
|
323
|
+
const pool = await this.loadPool(record.pool);
|
|
318
324
|
if (pool.inFlight >= pool.maxConcurrency) {
|
|
319
325
|
await this.requeuePooled(record);
|
|
320
326
|
return false;
|
|
@@ -522,7 +528,7 @@ class SchedulerDO {
|
|
|
522
528
|
return SchedulerDO.error(400, "INVALID_INPUT", "functionPath is required");
|
|
523
529
|
}
|
|
524
530
|
if (typeof body.scheduledFor !== "number" || !Number.isInteger(body.scheduledFor) || body.scheduledFor <= 0 || body.scheduledFor > MAX_SCHEDULED_FOR_MS) {
|
|
525
|
-
return SchedulerDO.error(400, "INVALID_INPUT", "scheduledFor must be a positive integer epoch-millisecond number no greater than
|
|
531
|
+
return SchedulerDO.error(400, "INVALID_INPUT", "scheduledFor must be a positive integer epoch-millisecond number no greater than 999999999999999");
|
|
526
532
|
}
|
|
527
533
|
if (typeof this.env.LUNORA_ORIGIN_URL !== "string" || this.env.LUNORA_ORIGIN_URL.length === 0) {
|
|
528
534
|
return SchedulerDO.error(500, "ORIGIN_NOT_CONFIGURED", "LUNORA_ORIGIN_URL env binding must be set on the SchedulerDO");
|
|
@@ -1,32 +1,6 @@
|
|
|
1
1
|
import { LunoraError } from '@lunora/errors';
|
|
2
|
-
import {
|
|
2
|
+
import { c as callDO, g as getDO } from './do-client-CMJtLoHO.mjs';
|
|
3
3
|
|
|
4
|
-
const schedulerStub = (options) => {
|
|
5
|
-
const namespace = applyJurisdiction(options.namespace, options.jurisdiction);
|
|
6
|
-
return namespace.get(namespace.idFromName(options.instanceName ?? "default"));
|
|
7
|
-
};
|
|
8
|
-
const callDO = async (options, path, body) => {
|
|
9
|
-
const stub = schedulerStub(options);
|
|
10
|
-
const response = await stub.fetch(`https://scheduler.internal${path}`, {
|
|
11
|
-
body: JSON.stringify(body),
|
|
12
|
-
headers: { "content-type": "application/json" },
|
|
13
|
-
method: "POST"
|
|
14
|
-
});
|
|
15
|
-
if (!response.ok) {
|
|
16
|
-
const text = await response.text();
|
|
17
|
-
throw new LunoraError("INTERNAL", `@lunora/scheduler: SchedulerDO ${path} failed (${String(response.status)}): ${text}`);
|
|
18
|
-
}
|
|
19
|
-
return await response.json();
|
|
20
|
-
};
|
|
21
|
-
const getDO = async (options, path) => {
|
|
22
|
-
const stub = schedulerStub(options);
|
|
23
|
-
const response = await stub.fetch(`https://scheduler.internal${path}`, { method: "GET" });
|
|
24
|
-
if (!response.ok) {
|
|
25
|
-
const text = await response.text();
|
|
26
|
-
throw new LunoraError("INTERNAL", `@lunora/scheduler: SchedulerDO ${path} failed (${String(response.status)}): ${text}`);
|
|
27
|
-
}
|
|
28
|
-
return await response.json();
|
|
29
|
-
};
|
|
30
4
|
const createScheduler = (options) => {
|
|
31
5
|
if (!options.namespace) {
|
|
32
6
|
throw new LunoraError("INTERNAL", "@lunora/scheduler: `namespace` (SchedulerDO binding) is required");
|
|
@@ -1,32 +1,6 @@
|
|
|
1
1
|
import { LunoraError } from '@lunora/errors';
|
|
2
|
-
import {
|
|
2
|
+
import { g as getDO, c as callDO } from './do-client-CMJtLoHO.mjs';
|
|
3
3
|
|
|
4
|
-
const workpoolStub = (options) => {
|
|
5
|
-
const namespace = applyJurisdiction(options.namespace, options.jurisdiction);
|
|
6
|
-
return namespace.get(namespace.idFromName(options.instanceName ?? "default"));
|
|
7
|
-
};
|
|
8
|
-
const callDO = async (options, path, body) => {
|
|
9
|
-
const stub = workpoolStub(options);
|
|
10
|
-
const response = await stub.fetch(`https://scheduler.internal${path}`, {
|
|
11
|
-
body: JSON.stringify(body),
|
|
12
|
-
headers: { "content-type": "application/json" },
|
|
13
|
-
method: "POST"
|
|
14
|
-
});
|
|
15
|
-
if (!response.ok) {
|
|
16
|
-
const text = await response.text();
|
|
17
|
-
throw new LunoraError("INTERNAL", `@lunora/scheduler: SchedulerDO ${path} failed (${String(response.status)}): ${text}`);
|
|
18
|
-
}
|
|
19
|
-
return await response.json();
|
|
20
|
-
};
|
|
21
|
-
const getDO = async (options, path) => {
|
|
22
|
-
const stub = workpoolStub(options);
|
|
23
|
-
const response = await stub.fetch(`https://scheduler.internal${path}`, { method: "GET" });
|
|
24
|
-
if (!response.ok) {
|
|
25
|
-
const text = await response.text();
|
|
26
|
-
throw new LunoraError("INTERNAL", `@lunora/scheduler: SchedulerDO ${path} failed (${String(response.status)}): ${text}`);
|
|
27
|
-
}
|
|
28
|
-
return await response.json();
|
|
29
|
-
};
|
|
30
4
|
const createWorkpool = (options) => {
|
|
31
5
|
if (!options.namespace) {
|
|
32
6
|
throw new LunoraError("INTERNAL", "@lunora/scheduler: `namespace` (SchedulerDO binding) is required");
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { LunoraError } from '@lunora/errors';
|
|
2
|
+
|
|
3
|
+
const applyJurisdiction = (namespace, jurisdiction) => {
|
|
4
|
+
if (jurisdiction === void 0) {
|
|
5
|
+
return namespace;
|
|
6
|
+
}
|
|
7
|
+
if (typeof namespace.jurisdiction !== "function") {
|
|
8
|
+
throw new TypeError(
|
|
9
|
+
`@lunora/scheduler: Durable Object namespace does not support jurisdiction("${jurisdiction}") — update @cloudflare/workers-types or remove the jurisdiction option`
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
return namespace.jurisdiction(jurisdiction);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const schedulerStub = (options) => {
|
|
16
|
+
const namespace = applyJurisdiction(options.namespace, options.jurisdiction);
|
|
17
|
+
return namespace.get(namespace.idFromName(options.instanceName ?? "default"));
|
|
18
|
+
};
|
|
19
|
+
const callDO = async (options, path, body) => {
|
|
20
|
+
const stub = schedulerStub(options);
|
|
21
|
+
const response = await stub.fetch(`https://scheduler.internal${path}`, {
|
|
22
|
+
body: JSON.stringify(body),
|
|
23
|
+
headers: { "content-type": "application/json" },
|
|
24
|
+
method: "POST"
|
|
25
|
+
});
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
const text = await response.text();
|
|
28
|
+
throw new LunoraError("INTERNAL", `@lunora/scheduler: SchedulerDO ${path} failed (${String(response.status)}): ${text}`);
|
|
29
|
+
}
|
|
30
|
+
return await response.json();
|
|
31
|
+
};
|
|
32
|
+
const getDO = async (options, path) => {
|
|
33
|
+
const stub = schedulerStub(options);
|
|
34
|
+
const response = await stub.fetch(`https://scheduler.internal${path}`, { method: "GET" });
|
|
35
|
+
if (!response.ok) {
|
|
36
|
+
const text = await response.text();
|
|
37
|
+
throw new LunoraError("INTERNAL", `@lunora/scheduler: SchedulerDO ${path} failed (${String(response.status)}): ${text}`);
|
|
38
|
+
}
|
|
39
|
+
return await response.json();
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export { callDO as c, getDO as g };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/scheduler",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.8",
|
|
4
4
|
"description": "Scheduling for Lunora: runAfter / runAt and Cron Triggers via SchedulerDO",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"access": "public"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@lunora/errors": "1.0.0-alpha.
|
|
49
|
+
"@lunora/errors": "1.0.0-alpha.4",
|
|
50
50
|
"cron-parser": "5.6.1"
|
|
51
51
|
},
|
|
52
52
|
"engines": {
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
const applyJurisdiction = (namespace, jurisdiction) => {
|
|
2
|
-
if (jurisdiction === void 0) {
|
|
3
|
-
return namespace;
|
|
4
|
-
}
|
|
5
|
-
if (typeof namespace.jurisdiction !== "function") {
|
|
6
|
-
throw new TypeError(
|
|
7
|
-
`@lunora/scheduler: Durable Object namespace does not support jurisdiction("${jurisdiction}") — update @cloudflare/workers-types or remove the jurisdiction option`
|
|
8
|
-
);
|
|
9
|
-
}
|
|
10
|
-
return namespace.jurisdiction(jurisdiction);
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export { applyJurisdiction as a };
|