@apisurf/ngn-core 0.2.9 → 0.3.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.
package/dist/core.d.ts CHANGED
@@ -607,6 +607,8 @@ interface ExecuteSchedule {
607
607
  /** When the next tick is due, epoch ms. */
608
608
  nextAt?: number | null;
609
609
  }
610
+ /** The `task_runs.id` of the run that threw `error`, when a task run did. */
611
+ declare function failedRunId(error: unknown): number | null;
610
612
  declare class Task {
611
613
  private taskCallbacks;
612
614
  private startRun;
@@ -703,12 +705,26 @@ declare class TaskLibrary {
703
705
 
704
706
  type Options$1 = {
705
707
  taskLibrary: TaskLibrary;
708
+ /** Called when a scheduled run throws, with the run it was recorded as when there is one. */
709
+ onRunError?: (path: string, error: unknown, runId: number | null) => void;
706
710
  };
711
+ /** What `start()` did with each task: scheduled on its pattern, or left out and why. */
712
+ interface ScheduleReport {
713
+ scheduled: {
714
+ path: string;
715
+ pattern: string;
716
+ }[];
717
+ failed: {
718
+ path: string;
719
+ reason: string;
720
+ }[];
721
+ }
707
722
  declare class TaskLibraryScheduler {
708
723
  private taskLibrary;
724
+ private onRunError;
709
725
  private scheduledTasks;
710
726
  constructor(options: Options$1);
711
- start(): Promise<void>;
727
+ start(): Promise<ScheduleReport>;
712
728
  stop(): Promise<void>;
713
729
  scheduleTask(task: Task): Promise<void>;
714
730
  unscheduleTask(task: Task): void;
@@ -739,4 +755,4 @@ declare function createControlsGenerator({ dbClient, env, }: {
739
755
  env: TaskContext["env"];
740
756
  }): BuildCompiledTaskConfigFn;
741
757
 
742
- export { type AddJob, type Backoff, type ClaimOptions, Compiler, type DBInstance, type Duration, type InitDBOptions, type Job, type JobFilter, type JobStatus, LockBusyError, type LockOptions, type Migration, type OverlapPolicy, Queue, type QueueOptions, type QueueStats, Task, type TaskContext, TaskLibrary, TaskLibraryScheduler, TaskOnceRunner, type TaskRun, type TaskSqlite, type WorkHandler, type WorkOptions, type WorkSummary, type WorkerHandle, closeTaskDatabases, createControlsGenerator, executeEsm, getDbClient, isCronPatternValid, scheduleTask, setupDbClient };
758
+ export { type AddJob, type Backoff, type ClaimOptions, Compiler, type DBInstance, type Duration, type InitDBOptions, type Job, type JobFilter, type JobStatus, LockBusyError, type LockOptions, type Migration, type OverlapPolicy, Queue, type QueueOptions, type QueueStats, type ScheduleReport, Task, type TaskContext, TaskLibrary, TaskLibraryScheduler, TaskOnceRunner, type TaskRun, type TaskSqlite, type WorkHandler, type WorkOptions, type WorkSummary, type WorkerHandle, closeTaskDatabases, createControlsGenerator, executeEsm, failedRunId, getDbClient, isCronPatternValid, scheduleTask, setupDbClient };
package/dist/core.js CHANGED
@@ -1170,6 +1170,10 @@ function breakerSkips(failedInARow, breaker) {
1170
1170
 
1171
1171
  // src/runtime/task.ts
1172
1172
  var sleep2 = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
1173
+ var failedRuns = /* @__PURE__ */ new WeakMap();
1174
+ function failedRunId(error) {
1175
+ return error && typeof error === "object" ? failedRuns.get(error) ?? null : null;
1176
+ }
1173
1177
  var Task = class {
1174
1178
  taskCallbacks;
1175
1179
  startRun;
@@ -1304,6 +1308,7 @@ var Task = class {
1304
1308
  await hooks.onSuccess?.(taskContext);
1305
1309
  return taskReturnValue;
1306
1310
  } catch (err) {
1311
+ if (err && typeof err === "object") failedRuns.set(err, run.id);
1307
1312
  await run.callbacks.onFailure?.({ path });
1308
1313
  await hooks.onError?.(err, taskContext);
1309
1314
  throw err;
@@ -1553,23 +1558,30 @@ function scheduleTask(task, pattern, onError) {
1553
1558
  // src/runtime/taskLibraryScheduler.ts
1554
1559
  var TaskLibraryScheduler = class {
1555
1560
  taskLibrary;
1561
+ onRunError;
1556
1562
  scheduledTasks = /* @__PURE__ */ new Map();
1557
1563
  constructor(options) {
1558
1564
  this.taskLibrary = options.taskLibrary;
1565
+ this.onRunError = options.onRunError;
1559
1566
  }
1560
1567
  async start() {
1561
1568
  logTaskLibraryScheduler("Starting scheduler");
1569
+ const report = { scheduled: [], failed: [] };
1562
1570
  const scheduledTasks = this.taskLibrary.getTasks().map(async (task) => {
1563
1571
  try {
1564
- const res = await this.scheduleTask(task);
1565
- return res;
1572
+ await this.scheduleTask(task);
1573
+ report.scheduled.push({ path: task.taskPath, pattern: task.timingPattern ?? "" });
1566
1574
  } catch (error) {
1567
1575
  logTaskLibrarySchedulerError(`Error scheduling task: ${task.taskPath}`);
1568
1576
  logTaskLibrarySchedulerError(error);
1577
+ report.failed.push({ path: task.taskPath, reason: reasonOf(error, task.taskPath) });
1569
1578
  }
1570
1579
  });
1571
1580
  await Promise.all(scheduledTasks);
1572
1581
  logTaskLibraryScheduler("Scheduler started");
1582
+ report.scheduled.sort(byPath);
1583
+ report.failed.sort(byPath);
1584
+ return report;
1573
1585
  }
1574
1586
  async stop() {
1575
1587
  logTaskLibraryScheduler("Stopping scheduler");
@@ -1591,6 +1603,7 @@ var TaskLibraryScheduler = class {
1591
1603
  const scheduledTask = scheduleTask(task, task.timingPattern, (error) => {
1592
1604
  logTaskLibrarySchedulerError(`Run failed: ${task.taskPath}`);
1593
1605
  logTaskLibrarySchedulerError(error);
1606
+ this.onRunError?.(task.taskPath, error, failedRunId(error));
1594
1607
  });
1595
1608
  this.scheduledTasks.set(task.taskPath, scheduledTask);
1596
1609
  logTaskLibraryScheduler(`Scheduled: ${task.taskPath}`);
@@ -1623,6 +1636,13 @@ var TaskLibraryScheduler = class {
1623
1636
  // task.stop();
1624
1637
  // }
1625
1638
  };
1639
+ function byPath(a, b) {
1640
+ return a.path.localeCompare(b.path);
1641
+ }
1642
+ function reasonOf(error, path) {
1643
+ const message = error instanceof Error ? error.message : String(error);
1644
+ return message.replace(/^Invariant failed: /, "").replace(` for ${path}`, "").replace(`: ${path}`, "");
1645
+ }
1626
1646
 
1627
1647
  // src/runtime/taskOnceRunner.ts
1628
1648
  import invariant9 from "tiny-invariant";
@@ -2209,6 +2229,7 @@ export {
2209
2229
  closeTaskDatabases,
2210
2230
  createControlsGenerator,
2211
2231
  executeEsm,
2232
+ failedRunId,
2212
2233
  getDbClient,
2213
2234
  initDbFileIfNotExists,
2214
2235
  isCronPatternValid,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apisurf/ngn-core",
3
- "version": "0.2.9",
3
+ "version": "0.3.0",
4
4
  "description": "ngn runtime: task compilation, scheduling and the run/log/timing services.",
5
5
  "type": "module",
6
6
  "author": "Luka Vidakovic",
@@ -38,9 +38,9 @@
38
38
  "esbuild": "^0.27.1",
39
39
  "tiny-invariant": "^1.3.3",
40
40
  "zod": "^3.24.2",
41
- "@apisurf/ngn-schema": "0.2.9",
42
- "@apisurf/ngn-os": "0.2.9",
43
- "@apisurf/ngn-persistence": "0.2.9"
41
+ "@apisurf/ngn-os": "0.3.0",
42
+ "@apisurf/ngn-persistence": "0.3.0",
43
+ "@apisurf/ngn-schema": "0.3.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/node": "^22.8.1",