@azlib/scheduler 1.0.4 → 1.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 (48) hide show
  1. package/Dockerfile +14 -0
  2. package/README.md +40 -0
  3. package/compose.yaml +28 -0
  4. package/dist/cli-config-7F2U8qPM.mjs +462 -0
  5. package/dist/cli-config-7F2U8qPM.mjs.map +1 -0
  6. package/dist/cli-config-Cz8QP_4I.cjs +512 -0
  7. package/dist/dashboard/cli.cjs +30 -0
  8. package/dist/dashboard/cli.d.cts +6 -0
  9. package/dist/dashboard/cli.d.cts.map +1 -0
  10. package/dist/dashboard/cli.d.mts +6 -0
  11. package/dist/dashboard/cli.d.mts.map +1 -0
  12. package/dist/dashboard/cli.mjs +31 -0
  13. package/dist/dashboard/cli.mjs.map +1 -0
  14. package/dist/dashboard/public/assets/index-CbmpG6_w.css +2 -0
  15. package/dist/dashboard/public/assets/index-eWhp41cY.js +12 -0
  16. package/dist/dashboard/public/index.html +13 -0
  17. package/dist/dashboard-types-BHjRLcxJ.d.cts +127 -0
  18. package/dist/dashboard-types-BHjRLcxJ.d.cts.map +1 -0
  19. package/dist/dashboard-types-BHjRLcxJ.d.mts +127 -0
  20. package/dist/dashboard-types-BHjRLcxJ.d.mts.map +1 -0
  21. package/dist/dashboard.cjs +9 -0
  22. package/dist/dashboard.d.cts +20 -0
  23. package/dist/dashboard.d.cts.map +1 -0
  24. package/dist/dashboard.d.mts +20 -0
  25. package/dist/dashboard.d.mts.map +1 -0
  26. package/dist/dashboard.mjs +4 -0
  27. package/dist/http-server-DN6Lg464.d.cts +19 -0
  28. package/dist/http-server-DN6Lg464.d.cts.map +1 -0
  29. package/dist/http-server-DPQ9HIK-.d.mts +19 -0
  30. package/dist/http-server-DPQ9HIK-.d.mts.map +1 -0
  31. package/dist/index.cjs +49 -442
  32. package/dist/index.d.cts +3 -158
  33. package/dist/index.d.cts.map +1 -1
  34. package/dist/index.d.mts +3 -158
  35. package/dist/index.d.mts.map +1 -1
  36. package/dist/index.mjs +44 -439
  37. package/dist/index.mjs.map +1 -1
  38. package/dist/queue-dashboard-service-BFXcnfUr.cjs +55 -0
  39. package/dist/queue-dashboard-service-C0QapgOg.mjs +52 -0
  40. package/dist/queue-dashboard-service-C0QapgOg.mjs.map +1 -0
  41. package/dist/store-dashboard-C44d7sdh.d.cts +59 -0
  42. package/dist/store-dashboard-C44d7sdh.d.cts.map +1 -0
  43. package/dist/store-dashboard-Cyf_JILa.mjs +515 -0
  44. package/dist/store-dashboard-Cyf_JILa.mjs.map +1 -0
  45. package/dist/store-dashboard-D-yIxXxL.d.mts +59 -0
  46. package/dist/store-dashboard-D-yIxXxL.d.mts.map +1 -0
  47. package/dist/store-dashboard-Sg4BBWiJ.cjs +554 -0
  48. package/package.json +56 -8
package/dist/index.mjs CHANGED
@@ -1,3 +1,5 @@
1
+ import { a as dispatchJobExecution, i as retryFailedExecutionById, n as createSchedulerDashboardFromStores, o as computeNextRunAt, r as createSchedulerPersistence, s as parseSchedule, t as createSchedulerDashboardFromPersistence } from "./store-dashboard-Cyf_JILa.mjs";
2
+ import { t as createSchedulerDashboardService } from "./queue-dashboard-service-C0QapgOg.mjs";
1
3
  import { unique } from "@azlib/std";
2
4
  import { randomUUID } from "node:crypto";
3
5
  import { createCache } from "@azlib/cache";
@@ -240,42 +242,6 @@ function createSchedulerCache(namespace = "scheduler-core") {
240
242
  });
241
243
  }
242
244
  //#endregion
243
- //#region src/core/schedule-parser.ts
244
- const CRON_FIELD_PATTERN = /^\*|\*\/\d+|\d+|\d+-\d+|\d+(?:,\d+)*$/;
245
- function assertValidTimezone(timezone) {
246
- try {
247
- new Intl.DateTimeFormat("en-US", { timeZone: timezone }).format(/* @__PURE__ */ new Date());
248
- } catch {
249
- throw new Error(`Invalid timezone: ${timezone}`);
250
- }
251
- }
252
- function assertValidCronExpression(expression) {
253
- const fields = expression.trim().split(/\s+/);
254
- if (fields.length !== 5) throw new Error("Cron expression must contain 5 fields");
255
- for (const field of fields) if (!CRON_FIELD_PATTERN.test(field)) throw new Error(`Invalid cron field: ${field}`);
256
- }
257
- function assertValidOnceExpression(expression) {
258
- const date = new Date(expression);
259
- if (Number.isNaN(date.getTime())) throw new Error("Once schedule expression must be a valid ISO datetime");
260
- }
261
- function parseSchedule(scheduleType, expression, timezone) {
262
- assertValidTimezone(timezone);
263
- if (scheduleType === "cron") assertValidCronExpression(expression);
264
- else assertValidOnceExpression(expression);
265
- return {
266
- scheduleType,
267
- expression: expression.trim(),
268
- timezone
269
- };
270
- }
271
- function computeNextRunAt(parsed, now = /* @__PURE__ */ new Date()) {
272
- if (parsed.scheduleType === "once") return new Date(parsed.expression).toISOString();
273
- const next = new Date(now);
274
- next.setUTCSeconds(0, 0);
275
- next.setUTCMinutes(next.getUTCMinutes() + 1);
276
- return next.toISOString();
277
- }
278
- //#endregion
279
245
  //#region src/core/scheduler-engine.ts
280
246
  function createSchedulerEngine(dependencies) {
281
247
  let timer;
@@ -294,26 +260,16 @@ function createSchedulerEngine(dependencies) {
294
260
  for (const job of candidates) {
295
261
  const nextRunAt = await computeNextRun(job, now);
296
262
  if (new Date(nextRunAt).getTime() > now.getTime()) continue;
297
- const executionId = `exec_${randomUUID()}`;
298
- await dependencies.executionStore.create({
299
- executionId,
300
- jobId: job.jobId,
263
+ await dispatchJobExecution({
264
+ job,
265
+ executionStore: dependencies.executionStore,
266
+ queueService: dependencies.queueService,
301
267
  scheduledFor: nextRunAt,
302
- triggeredAt: now.toISOString(),
303
- status: "queued",
304
- attemptCount: 0
268
+ attemptCount: 0,
269
+ idempotencyKey: `scheduler:${job.jobId}:${nextRunAt}`
305
270
  });
306
271
  await dependencies.cursorStore.markTriggered(job.jobId, now.toISOString());
307
- if (dependencies.queueService) await dependencies.queueService.enqueue({
308
- idempotencyKey: `scheduler:${job.jobId}:${nextRunAt}`,
309
- payloadRef: {
310
- executionId,
311
- handlerKey: job.handlerKey,
312
- config: job.config
313
- }
314
- });
315
272
  dependencies.logger.info("scheduler.job.triggered", {
316
- executionId,
317
273
  jobId: job.jobId,
318
274
  scheduledFor: nextRunAt
319
275
  });
@@ -370,6 +326,9 @@ function createSchedulerHandlerRegistry() {
370
326
  },
371
327
  has(handlerKey) {
372
328
  return handlers.has(handlerKey);
329
+ },
330
+ keys() {
331
+ return Array.from(handlers.keys());
373
332
  }
374
333
  };
375
334
  }
@@ -462,7 +421,12 @@ function toJobListItem(input) {
462
421
  name: input.name,
463
422
  enabled: input.enabled,
464
423
  timezone: input.timezone,
465
- nextRunAt: input.nextRunAt
424
+ nextRunAt: input.nextRunAt,
425
+ handlerKey: input.handlerKey,
426
+ schedule: input.schedule,
427
+ config: input.config,
428
+ createdAt: input.createdAt,
429
+ updatedAt: input.updatedAt
466
430
  };
467
431
  }
468
432
  function filterExecutions(items, query) {
@@ -478,358 +442,6 @@ function filterExecutions(items, query) {
478
442
  }).slice(0, query.limit ?? items.length);
479
443
  }
480
444
  //#endregion
481
- //#region src/core/scheduler-service-retry.ts
482
- async function retryFailedExecutionById(allExecutions, executionStore, executionId) {
483
- const target = allExecutions.find((item) => item.executionId === executionId);
484
- if (!target) throw new Error(`Execution not found: ${executionId}`);
485
- if (target.status !== "failed" && target.status !== "dead-letter") throw new Error("Only failed or dead-letter executions can be retried");
486
- const retryExecutionId = `exec_${randomUUID()}`;
487
- const retry = {
488
- ...target,
489
- executionId: retryExecutionId,
490
- status: "queued",
491
- attemptCount: target.attemptCount + 1,
492
- triggeredAt: (/* @__PURE__ */ new Date()).toISOString()
493
- };
494
- await executionStore.create(retry);
495
- return retry;
496
- }
497
- //#endregion
498
- //#region src/core/persistence.ts
499
- function namespaceFor(config) {
500
- return config.namespace?.trim() || "azlib";
501
- }
502
- function tableNames(namespace) {
503
- return {
504
- jobs: `${namespace}__scheduler_jobs`,
505
- cursors: `${namespace}__scheduler_cursors`,
506
- executions: `${namespace}__scheduler_executions`
507
- };
508
- }
509
- function quoted(dialect, tableName) {
510
- return dialect.quoteIdentifier(tableName);
511
- }
512
- async function bootstrapSchedulerPersistence(client, dialect, names) {
513
- await client.transaction(async (tx) => {
514
- await tx.execute(`CREATE TABLE IF NOT EXISTS ${quoted(dialect, names.jobs)} (
515
- job_id TEXT PRIMARY KEY,
516
- name TEXT NOT NULL,
517
- handler_key TEXT NOT NULL,
518
- schedule_type TEXT NOT NULL,
519
- expression TEXT NOT NULL,
520
- timezone TEXT NOT NULL,
521
- overlap_policy TEXT,
522
- missed_run_policy TEXT,
523
- config_json TEXT NOT NULL,
524
- enabled INTEGER NOT NULL,
525
- created_at TEXT NOT NULL,
526
- updated_at TEXT NOT NULL
527
- )`);
528
- await tx.execute(`CREATE TABLE IF NOT EXISTS ${quoted(dialect, names.cursors)} (
529
- job_id TEXT PRIMARY KEY,
530
- last_evaluated_at TEXT NOT NULL,
531
- last_triggered_at TEXT,
532
- next_run_at TEXT NOT NULL,
533
- version INTEGER NOT NULL
534
- )`);
535
- await tx.execute(`CREATE TABLE IF NOT EXISTS ${quoted(dialect, names.executions)} (
536
- execution_id TEXT PRIMARY KEY,
537
- job_id TEXT NOT NULL,
538
- scheduled_for TEXT NOT NULL,
539
- triggered_at TEXT NOT NULL,
540
- status TEXT NOT NULL,
541
- queue_task_id TEXT,
542
- attempt_count INTEGER NOT NULL,
543
- last_error TEXT
544
- )`);
545
- });
546
- }
547
- function toJobRow(jobId, definition) {
548
- const now = (/* @__PURE__ */ new Date()).toISOString();
549
- return {
550
- jobId,
551
- name: definition.name,
552
- handlerKey: definition.handlerKey,
553
- scheduleType: definition.schedule.scheduleType,
554
- expression: definition.schedule.expression,
555
- timezone: definition.schedule.timezone,
556
- overlapPolicy: definition.schedule.overlapPolicy ?? null,
557
- missedRunPolicy: definition.schedule.missedRunPolicy ?? null,
558
- configJson: JSON.stringify(definition.config),
559
- enabled: definition.enabled ?? true ? 1 : 0,
560
- createdAt: now,
561
- updatedAt: now
562
- };
563
- }
564
- function rowToJobRecord(row) {
565
- return {
566
- jobId: row.jobId,
567
- name: row.name,
568
- handlerKey: row.handlerKey,
569
- schedule: {
570
- scheduleType: row.scheduleType,
571
- expression: row.expression,
572
- timezone: row.timezone,
573
- overlapPolicy: row.overlapPolicy === null ? void 0 : row.overlapPolicy,
574
- missedRunPolicy: row.missedRunPolicy === null ? void 0 : row.missedRunPolicy
575
- },
576
- config: JSON.parse(row.configJson),
577
- enabled: Boolean(row.enabled),
578
- createdAt: row.createdAt,
579
- updatedAt: row.updatedAt
580
- };
581
- }
582
- async function readJobRow(client, dialect, names, jobId) {
583
- const rows = await client.query(`SELECT job_id AS jobId, name, handler_key AS handlerKey, schedule_type AS scheduleType, expression, timezone, overlap_policy AS overlapPolicy, missed_run_policy AS missedRunPolicy, config_json AS configJson, enabled, created_at AS createdAt, updated_at AS updatedAt
584
- FROM ${quoted(dialect, names.jobs)} WHERE job_id = ? LIMIT 1`, [jobId]);
585
- return rows[0] ? rowToJobRecord(rows[0]) : null;
586
- }
587
- async function readCursorRow(client, dialect, names, jobId) {
588
- const row = (await client.query(`SELECT job_id AS jobId, last_evaluated_at AS lastEvaluatedAt, last_triggered_at AS lastTriggeredAt, next_run_at AS nextRunAt, version
589
- FROM ${quoted(dialect, names.cursors)} WHERE job_id = ? LIMIT 1`, [jobId]))[0];
590
- return row ? {
591
- jobId: row.jobId,
592
- lastEvaluatedAt: row.lastEvaluatedAt,
593
- lastTriggeredAt: row.lastTriggeredAt ?? void 0,
594
- nextRunAt: row.nextRunAt,
595
- version: row.version
596
- } : null;
597
- }
598
- async function readExecutionRow(client, dialect, names, executionId) {
599
- const row = (await client.query(`SELECT execution_id AS executionId, job_id AS jobId, scheduled_for AS scheduledFor, triggered_at AS triggeredAt, status, queue_task_id AS queueTaskId, attempt_count AS attemptCount, last_error AS lastError
600
- FROM ${quoted(dialect, names.executions)} WHERE execution_id = ? LIMIT 1`, [executionId]))[0];
601
- return row ? {
602
- executionId: row.executionId,
603
- jobId: row.jobId,
604
- scheduledFor: row.scheduledFor,
605
- triggeredAt: row.triggeredAt,
606
- status: row.status,
607
- queueTaskId: row.queueTaskId ?? void 0,
608
- attemptCount: row.attemptCount,
609
- lastError: row.lastError ?? void 0
610
- } : null;
611
- }
612
- function createSchedulerPersistence(config) {
613
- const names = tableNames(namespaceFor(config));
614
- let bootstrapPromise = null;
615
- async function ensureBootstrapped() {
616
- if (!bootstrapPromise) bootstrapPromise = bootstrapSchedulerPersistence(config.client, config.dialect, names);
617
- await bootstrapPromise;
618
- }
619
- return {
620
- jobStore: {
621
- async create(jobId, definition) {
622
- await ensureBootstrapped();
623
- const row = toJobRow(jobId, definition);
624
- await config.client.transaction(async (tx) => {
625
- await tx.execute(`DELETE FROM ${quoted(config.dialect, names.jobs)} WHERE job_id = ?`, [jobId]);
626
- await tx.execute(`INSERT INTO ${quoted(config.dialect, names.jobs)} (job_id, name, handler_key, schedule_type, expression, timezone, overlap_policy, missed_run_policy, config_json, enabled, created_at, updated_at)
627
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
628
- row.jobId,
629
- row.name,
630
- row.handlerKey,
631
- row.scheduleType,
632
- row.expression,
633
- row.timezone,
634
- row.overlapPolicy,
635
- row.missedRunPolicy,
636
- row.configJson,
637
- row.enabled,
638
- row.createdAt,
639
- row.updatedAt
640
- ]);
641
- });
642
- return rowToJobRecord(row);
643
- },
644
- async update(jobId, patch) {
645
- const current = await readJobRow(config.client, config.dialect, names, jobId);
646
- if (!current) return null;
647
- const next = {
648
- ...current,
649
- ...patch,
650
- schedule: {
651
- ...current.schedule,
652
- ...patch.schedule
653
- },
654
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
655
- };
656
- await config.client.transaction(async (tx) => {
657
- await tx.execute(`DELETE FROM ${quoted(config.dialect, names.jobs)} WHERE job_id = ?`, [jobId]);
658
- await tx.execute(`INSERT INTO ${quoted(config.dialect, names.jobs)} (job_id, name, handler_key, schedule_type, expression, timezone, overlap_policy, missed_run_policy, config_json, enabled, created_at, updated_at)
659
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
660
- next.jobId,
661
- next.name,
662
- next.handlerKey,
663
- next.schedule.scheduleType,
664
- next.schedule.expression,
665
- next.schedule.timezone,
666
- next.schedule.overlapPolicy ?? null,
667
- next.schedule.missedRunPolicy ?? null,
668
- JSON.stringify(next.config),
669
- next.enabled ? 1 : 0,
670
- next.createdAt,
671
- next.updatedAt
672
- ]);
673
- });
674
- return next;
675
- },
676
- async setEnabled(jobId, enabled) {
677
- const current = await readJobRow(config.client, config.dialect, names, jobId);
678
- if (!current) return null;
679
- const next = {
680
- ...current,
681
- enabled,
682
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
683
- };
684
- await config.client.transaction(async (tx) => {
685
- await tx.execute(`DELETE FROM ${quoted(config.dialect, names.jobs)} WHERE job_id = ?`, [jobId]);
686
- await tx.execute(`INSERT INTO ${quoted(config.dialect, names.jobs)} (job_id, name, handler_key, schedule_type, expression, timezone, overlap_policy, missed_run_policy, config_json, enabled, created_at, updated_at)
687
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
688
- next.jobId,
689
- next.name,
690
- next.handlerKey,
691
- next.schedule.scheduleType,
692
- next.schedule.expression,
693
- next.schedule.timezone,
694
- next.schedule.overlapPolicy ?? null,
695
- next.schedule.missedRunPolicy ?? null,
696
- JSON.stringify(next.config),
697
- next.enabled ? 1 : 0,
698
- next.createdAt,
699
- next.updatedAt
700
- ]);
701
- });
702
- return next;
703
- },
704
- async get(jobId) {
705
- await ensureBootstrapped();
706
- return readJobRow(config.client, config.dialect, names, jobId);
707
- },
708
- async list() {
709
- await ensureBootstrapped();
710
- return (await config.client.query(`SELECT job_id AS jobId, name, handler_key AS handlerKey, schedule_type AS scheduleType, expression, timezone, overlap_policy AS overlapPolicy, missed_run_policy AS missedRunPolicy, config_json AS configJson, enabled, created_at AS createdAt, updated_at AS updatedAt
711
- FROM ${quoted(config.dialect, names.jobs)} ORDER BY created_at ASC`, [])).map(rowToJobRecord);
712
- },
713
- async remove(jobId) {
714
- await ensureBootstrapped();
715
- await config.client.execute(`DELETE FROM ${quoted(config.dialect, names.jobs)} WHERE job_id = ?`, [jobId]);
716
- return true;
717
- }
718
- },
719
- cursorStore: {
720
- async set(jobId, nextRunAt, now) {
721
- await ensureBootstrapped();
722
- const current = await readCursorRow(config.client, config.dialect, names, jobId);
723
- const next = {
724
- jobId,
725
- lastEvaluatedAt: now,
726
- lastTriggeredAt: current?.lastTriggeredAt ?? null,
727
- nextRunAt,
728
- version: (current?.version ?? 0) + 1
729
- };
730
- await config.client.transaction(async (tx) => {
731
- await tx.execute(`DELETE FROM ${quoted(config.dialect, names.cursors)} WHERE job_id = ?`, [jobId]);
732
- await tx.execute(`INSERT INTO ${quoted(config.dialect, names.cursors)} (job_id, last_evaluated_at, last_triggered_at, next_run_at, version) VALUES (?, ?, ?, ?, ?)`, [
733
- next.jobId,
734
- next.lastEvaluatedAt,
735
- next.lastTriggeredAt,
736
- next.nextRunAt,
737
- next.version
738
- ]);
739
- });
740
- return {
741
- jobId: next.jobId,
742
- lastEvaluatedAt: next.lastEvaluatedAt,
743
- lastTriggeredAt: next.lastTriggeredAt ?? void 0,
744
- nextRunAt: next.nextRunAt,
745
- version: next.version
746
- };
747
- },
748
- async markTriggered(jobId, triggeredAt) {
749
- await ensureBootstrapped();
750
- const current = await readCursorRow(config.client, config.dialect, names, jobId);
751
- if (!current) return null;
752
- const next = {
753
- ...current,
754
- lastTriggeredAt: triggeredAt,
755
- version: current.version + 1
756
- };
757
- await config.client.execute(`UPDATE ${quoted(config.dialect, names.cursors)} SET last_triggered_at = ?, version = ? WHERE job_id = ?`, [
758
- next.lastTriggeredAt,
759
- next.version,
760
- jobId
761
- ]);
762
- return {
763
- jobId: next.jobId,
764
- lastEvaluatedAt: next.lastEvaluatedAt,
765
- lastTriggeredAt: next.lastTriggeredAt ?? void 0,
766
- nextRunAt: next.nextRunAt,
767
- version: next.version
768
- };
769
- },
770
- async get(jobId) {
771
- await ensureBootstrapped();
772
- return readCursorRow(config.client, config.dialect, names, jobId);
773
- },
774
- async remove(jobId) {
775
- await ensureBootstrapped();
776
- await config.client.execute(`DELETE FROM ${quoted(config.dialect, names.cursors)} WHERE job_id = ?`, [jobId]);
777
- return true;
778
- }
779
- },
780
- executionStore: {
781
- async create(record) {
782
- await ensureBootstrapped();
783
- await config.client.execute(`INSERT INTO ${quoted(config.dialect, names.executions)} (execution_id, job_id, scheduled_for, triggered_at, status, queue_task_id, attempt_count, last_error)
784
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
785
- record.executionId,
786
- record.jobId,
787
- record.scheduledFor,
788
- record.triggeredAt,
789
- record.status,
790
- record.queueTaskId ?? null,
791
- record.attemptCount,
792
- record.lastError ?? null
793
- ]);
794
- return record;
795
- },
796
- async update(executionId, patch) {
797
- const current = await readExecutionRow(config.client, config.dialect, names, executionId);
798
- if (!current) return null;
799
- const next = {
800
- ...current,
801
- ...patch
802
- };
803
- await config.client.execute(`UPDATE ${quoted(config.dialect, names.executions)} SET job_id = ?, scheduled_for = ?, triggered_at = ?, status = ?, queue_task_id = ?, attempt_count = ?, last_error = ? WHERE execution_id = ?`, [
804
- next.jobId,
805
- next.scheduledFor,
806
- next.triggeredAt,
807
- next.status,
808
- next.queueTaskId ?? null,
809
- next.attemptCount,
810
- next.lastError ?? null,
811
- executionId
812
- ]);
813
- return next;
814
- },
815
- async listByJob(jobId) {
816
- await ensureBootstrapped();
817
- return (await config.client.query(`SELECT execution_id AS executionId, job_id AS jobId, scheduled_for AS scheduledFor, triggered_at AS triggeredAt, status, queue_task_id AS queueTaskId, attempt_count AS attemptCount, last_error AS lastError
818
- FROM ${quoted(config.dialect, names.executions)} WHERE job_id = ? ORDER BY triggered_at ASC`, [jobId])).map((row) => ({
819
- executionId: row.executionId,
820
- jobId: row.jobId,
821
- scheduledFor: row.scheduledFor,
822
- triggeredAt: row.triggeredAt,
823
- status: row.status,
824
- queueTaskId: row.queueTaskId ?? void 0,
825
- attemptCount: row.attemptCount,
826
- lastError: row.lastError ?? void 0
827
- }));
828
- }
829
- }
830
- };
831
- }
832
- //#endregion
833
445
  //#region src/core/scheduler-service.ts
834
446
  function createSchedulerService$1(options) {
835
447
  assertRuntimeConfig(options);
@@ -910,7 +522,12 @@ function createSchedulerService$1(options) {
910
522
  name: job.name,
911
523
  enabled: job.enabled,
912
524
  timezone: job.schedule.timezone,
913
- nextRunAt: cursor?.nextRunAt
525
+ nextRunAt: cursor?.nextRunAt,
526
+ handlerKey: job.handlerKey,
527
+ schedule: job.schedule,
528
+ config: job.config,
529
+ createdAt: job.createdAt,
530
+ updatedAt: job.updatedAt
914
531
  });
915
532
  }));
916
533
  },
@@ -920,7 +537,7 @@ function createSchedulerService$1(options) {
920
537
  },
921
538
  async retryFailedExecution(executionId) {
922
539
  const jobs = await jobStore.list();
923
- const retry = await retryFailedExecutionById((await Promise.all(jobs.map((job) => executionStore.listByJob(job.jobId)))).flat(), executionStore, executionId);
540
+ const retry = await retryFailedExecutionById((await Promise.all(jobs.map((job) => executionStore.listByJob(job.jobId)))).flat(), jobs, executionStore, queueService, executionId);
924
541
  events.emit({
925
542
  eventType: "retry",
926
543
  jobId: retry.jobId,
@@ -928,6 +545,25 @@ function createSchedulerService$1(options) {
928
545
  metadata: { previousExecutionId: executionId }
929
546
  });
930
547
  },
548
+ async runNow(jobId) {
549
+ const job = await jobStore.get(jobId);
550
+ if (!job) throw new Error(`Job not found: ${jobId}`);
551
+ const scheduledFor = (/* @__PURE__ */ new Date()).toISOString();
552
+ const execution = await dispatchJobExecution({
553
+ job,
554
+ executionStore,
555
+ queueService,
556
+ scheduledFor,
557
+ attemptCount: 0,
558
+ idempotencyKey: `scheduler:${job.jobId}:manual:${scheduledFor}`
559
+ });
560
+ events.emit({
561
+ eventType: "triggered",
562
+ jobId,
563
+ executionId: execution.executionId,
564
+ metadata: { reason: "run-now" }
565
+ });
566
+ },
931
567
  async start() {
932
568
  logger.info("scheduler.service.start", { mode: options.mode });
933
569
  await runtime.start();
@@ -941,42 +577,11 @@ function createSchedulerService$1(options) {
941
577
  };
942
578
  }
943
579
  //#endregion
944
- //#region src/dashboard/queue-queries.ts
945
- async function querySchedulerHealth(scheduler) {
946
- const jobs = await scheduler.listJobs();
947
- const executions = await scheduler.listExecutions({ status: "failed" });
948
- return {
949
- totalJobs: jobs.length,
950
- enabledJobs: jobs.filter((job) => job.enabled).length,
951
- pausedJobs: jobs.filter((job) => !job.enabled).length,
952
- failedExecutions: executions.length
953
- };
954
- }
955
- async function querySchedulerItems(scheduler) {
956
- const jobs = await scheduler.listJobs();
957
- const executions = await scheduler.listExecutions({ limit: 100 });
958
- return jobs.map((job) => ({
959
- ...job,
960
- executions: executions.filter((execution) => execution.jobId === job.jobId)
961
- }));
962
- }
963
- //#endregion
964
- //#region src/dashboard/queue-dashboard-service.ts
965
- function createSchedulerDashboardService(scheduler) {
966
- return {
967
- getHealth: () => querySchedulerHealth(scheduler),
968
- listItems: () => querySchedulerItems(scheduler),
969
- pauseJob: (jobId) => scheduler.pauseJob(jobId),
970
- resumeJob: (jobId) => scheduler.resumeJob(jobId),
971
- retryExecution: (executionId) => scheduler.retryFailedExecution(executionId)
972
- };
973
- }
974
- //#endregion
975
580
  //#region index.ts
976
581
  function createSchedulerService(options) {
977
582
  return createSchedulerService$1(options);
978
583
  }
979
584
  //#endregion
980
- export { CronWeekday, bindSchedulerToHost, createCronExpression, createSchedulerDashboardService, createSchedulerPersistence, createSchedulerService };
585
+ export { CronWeekday, bindSchedulerToHost, createCronExpression, createSchedulerDashboardFromPersistence, createSchedulerDashboardFromStores, createSchedulerDashboardService, createSchedulerPersistence, createSchedulerService };
981
586
 
982
587
  //# sourceMappingURL=index.mjs.map