@aigne/afs-scheduler 1.12.0-beta.5

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 (75) hide show
  1. package/LICENSE.md +26 -0
  2. package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.cjs +11 -0
  3. package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs +10 -0
  4. package/dist/cron-parser.cjs +202 -0
  5. package/dist/cron-parser.d.cts +65 -0
  6. package/dist/cron-parser.d.cts.map +1 -0
  7. package/dist/cron-parser.d.mts +65 -0
  8. package/dist/cron-parser.d.mts.map +1 -0
  9. package/dist/cron-parser.mjs +200 -0
  10. package/dist/cron-parser.mjs.map +1 -0
  11. package/dist/do-executor.cjs +59 -0
  12. package/dist/do-executor.d.cts +30 -0
  13. package/dist/do-executor.d.cts.map +1 -0
  14. package/dist/do-executor.d.mts +30 -0
  15. package/dist/do-executor.d.mts.map +1 -0
  16. package/dist/do-executor.mjs +59 -0
  17. package/dist/do-executor.mjs.map +1 -0
  18. package/dist/do-job-executor.cjs +53 -0
  19. package/dist/do-job-executor.d.cts +28 -0
  20. package/dist/do-job-executor.d.cts.map +1 -0
  21. package/dist/do-job-executor.d.mts +28 -0
  22. package/dist/do-job-executor.d.mts.map +1 -0
  23. package/dist/do-job-executor.mjs +53 -0
  24. package/dist/do-job-executor.mjs.map +1 -0
  25. package/dist/do-types.d.cts +66 -0
  26. package/dist/do-types.d.cts.map +1 -0
  27. package/dist/do-types.d.mts +66 -0
  28. package/dist/do-types.d.mts.map +1 -0
  29. package/dist/index.cjs +34 -0
  30. package/dist/index.d.cts +11 -0
  31. package/dist/index.d.mts +11 -0
  32. package/dist/index.mjs +11 -0
  33. package/dist/job-coordinator.cjs +216 -0
  34. package/dist/job-coordinator.d.cts +8 -0
  35. package/dist/job-coordinator.d.cts.map +1 -0
  36. package/dist/job-coordinator.d.mts +8 -0
  37. package/dist/job-coordinator.d.mts.map +1 -0
  38. package/dist/job-coordinator.mjs +216 -0
  39. package/dist/job-coordinator.mjs.map +1 -0
  40. package/dist/local-executor.cjs +25 -0
  41. package/dist/local-executor.d.cts +12 -0
  42. package/dist/local-executor.d.cts.map +1 -0
  43. package/dist/local-executor.d.mts +12 -0
  44. package/dist/local-executor.d.mts.map +1 -0
  45. package/dist/local-executor.mjs +25 -0
  46. package/dist/local-executor.mjs.map +1 -0
  47. package/dist/scheduler-provider.cjs +1236 -0
  48. package/dist/scheduler-provider.d.cts +95 -0
  49. package/dist/scheduler-provider.d.cts.map +1 -0
  50. package/dist/scheduler-provider.d.mts +95 -0
  51. package/dist/scheduler-provider.d.mts.map +1 -0
  52. package/dist/scheduler-provider.mjs +1237 -0
  53. package/dist/scheduler-provider.mjs.map +1 -0
  54. package/dist/task-runner.cjs +70 -0
  55. package/dist/task-runner.d.cts +25 -0
  56. package/dist/task-runner.d.cts.map +1 -0
  57. package/dist/task-runner.d.mts +25 -0
  58. package/dist/task-runner.d.mts.map +1 -0
  59. package/dist/task-runner.mjs +70 -0
  60. package/dist/task-runner.mjs.map +1 -0
  61. package/dist/task-store.cjs +186 -0
  62. package/dist/task-store.d.cts +60 -0
  63. package/dist/task-store.d.cts.map +1 -0
  64. package/dist/task-store.d.mts +60 -0
  65. package/dist/task-store.d.mts.map +1 -0
  66. package/dist/task-store.mjs +187 -0
  67. package/dist/task-store.mjs.map +1 -0
  68. package/dist/types.cjs +185 -0
  69. package/dist/types.d.cts +384 -0
  70. package/dist/types.d.cts.map +1 -0
  71. package/dist/types.d.mts +384 -0
  72. package/dist/types.d.mts.map +1 -0
  73. package/dist/types.mjs +176 -0
  74. package/dist/types.mjs.map +1 -0
  75. package/package.json +59 -0
@@ -0,0 +1,60 @@
1
+ import { BudgetEstimate, JobEntry, ScheduleEntry, SchedulerSnapshot, TaskEntry } from "./types.mjs";
2
+
3
+ //#region src/task-store.d.ts
4
+ /** Extra fields for task creation */
5
+ interface CreateExtras {
6
+ ownerKey?: string;
7
+ scheduleId?: string;
8
+ jobId?: string;
9
+ batchIndex?: number;
10
+ /** Claim-based (pull model) task — see #1067. */
11
+ claimable?: boolean;
12
+ /** Claim constraint: "" = any worker, DID = only that worker. Only meaningful when `claimable`. */
13
+ assignee?: string;
14
+ }
15
+ declare class TaskStore {
16
+ private maxHistory;
17
+ private budgetBlockedTTL;
18
+ private tasks;
19
+ private historyList;
20
+ private schedules;
21
+ constructor(maxHistory?: number, budgetBlockedTTL?: number);
22
+ create(taskPath: string, args?: unknown, procId?: string, estimate?: BudgetEstimate, extras?: CreateExtras): TaskEntry;
23
+ get(id: string): TaskEntry | undefined;
24
+ getFromHistory(id: string): TaskEntry | undefined;
25
+ getAny(id: string): TaskEntry | undefined;
26
+ /** Fields that must never be overwritten via update() */
27
+ private static readonly IMMUTABLE_FIELDS;
28
+ update(id: string, patch: Partial<TaskEntry>): void;
29
+ listActive(): TaskEntry[];
30
+ listHistory(): TaskEntry[];
31
+ moveToHistory(id: string): void;
32
+ restoreFromHistory(id: string): TaskEntry | undefined;
33
+ addSchedule(opts: {
34
+ name: string;
35
+ cron: string;
36
+ taskPath: string;
37
+ args?: unknown;
38
+ enabled?: boolean;
39
+ ownerKey?: string;
40
+ }): ScheduleEntry;
41
+ getSchedule(id: string): ScheduleEntry | undefined;
42
+ getScheduleByName(name: string): ScheduleEntry | undefined;
43
+ listSchedules(): ScheduleEntry[];
44
+ updateSchedule(id: string, patch: Partial<ScheduleEntry>): void;
45
+ removeSchedule(id: string): boolean;
46
+ recordScheduleRun(id: string, now: number): void;
47
+ private jobs;
48
+ addJob(job: JobEntry): void;
49
+ getJob(id: string): JobEntry | undefined;
50
+ listJobs(): JobEntry[];
51
+ updateJob(id: string, patch: Partial<JobEntry>): void;
52
+ removeJob(id: string): boolean;
53
+ /** Get the next pending batch index for a job, or -1 if none */
54
+ nextPendingBatch(jobId: string): number;
55
+ snapshot(): SchedulerSnapshot;
56
+ restore(snapshot: SchedulerSnapshot, now: number): void;
57
+ }
58
+ //#endregion
59
+ export { TaskStore };
60
+ //# sourceMappingURL=task-store.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-store.d.mts","names":[],"sources":["../src/task-store.ts"],"mappings":";;;;UAaU,YAAA;EACR,QAAA;EACA,UAAA;EACA,KAAA;EACA,UAAA;EAHA;EAKA,SAAA;EAHA;EAKA,QAAA;AAAA;AAAA,cAGW,SAAA;EAAA,QAMD,UAAA;EAAA,QACA,gBAAA;EAAA,QANF,KAAA;EAAA,QACA,WAAA;EAAA,QACA,SAAA;cAGE,UAAA,WACA,gBAAA;EAGV,MAAA,CACE,QAAA,UACA,IAAA,YACA,MAAA,WACA,QAAA,GAAW,cAAA,EACX,MAAA,GAAS,YAAA,GACR,SAAA;EAsBH,GAAA,CAAI,EAAA,WAAa,SAAA;EAIjB,cAAA,CAAe,EAAA,WAAa,SAAA;EAI5B,MAAA,CAAO,EAAA,WAAa,SAAA;EAAA;EAAA,wBAKI,gBAAA;EAExB,MAAA,CAAO,EAAA,UAAY,KAAA,EAAO,OAAA,CAAQ,SAAA;EAUlC,UAAA,CAAA,GAAc,SAAA;EAId,WAAA,CAAA,GAAe,SAAA;EAIf,aAAA,CAAc,EAAA;EAUd,kBAAA,CAAmB,EAAA,WAAa,SAAA;EAahC,WAAA,CAAY,IAAA;IACV,IAAA;IACA,IAAA;IACA,QAAA;IACA,IAAA;IACA,OAAA;IACA,QAAA;EAAA,IACE,aAAA;EA2BJ,WAAA,CAAY,EAAA,WAAa,aAAA;EAIzB,iBAAA,CAAkB,IAAA,WAAe,aAAA;EAOjC,aAAA,CAAA,GAAiB,aAAA;EAIjB,cAAA,CAAe,EAAA,UAAY,KAAA,EAAO,OAAA,CAAQ,aAAA;EAgB1C,cAAA,CAAe,EAAA;EAIf,iBAAA,CAAkB,EAAA,UAAY,GAAA;EAAA,QAWtB,IAAA;EAER,MAAA,CAAO,GAAA,EAAK,QAAA;EAIZ,MAAA,CAAO,EAAA,WAAa,QAAA;EAIpB,QAAA,CAAA,GAAY,QAAA;EAIZ,SAAA,CAAU,EAAA,UAAY,KAAA,EAAO,OAAA,CAAQ,QAAA;EAQrC,SAAA,CAAU,EAAA;EA9LA;EAmMV,gBAAA,CAAiB,KAAA;EAejB,QAAA,CAAA,GAAY,iBAAA;EAgBZ,OAAA,CAAQ,QAAA,EAAU,iBAAA,EAAmB,GAAA;AAAA"}
@@ -0,0 +1,187 @@
1
+ import { nextRun, parseCron } from "./cron-parser.mjs";
2
+ import { v7 } from "@aigne/uuid";
3
+
4
+ //#region src/task-store.ts
5
+ const DEFAULT_BUDGET_BLOCKED_TTL = 864e5;
6
+ var TaskStore = class TaskStore {
7
+ tasks = /* @__PURE__ */ new Map();
8
+ historyList = [];
9
+ schedules = /* @__PURE__ */ new Map();
10
+ constructor(maxHistory = 100, budgetBlockedTTL = DEFAULT_BUDGET_BLOCKED_TTL) {
11
+ this.maxHistory = maxHistory;
12
+ this.budgetBlockedTTL = budgetBlockedTTL;
13
+ }
14
+ create(taskPath, args, procId, estimate, extras) {
15
+ const entry = {
16
+ id: v7(),
17
+ status: "pending",
18
+ taskPath,
19
+ args,
20
+ estimate,
21
+ created: Date.now(),
22
+ attempts: 0,
23
+ procId,
24
+ ownerKey: extras?.ownerKey,
25
+ scheduleId: extras?.scheduleId,
26
+ jobId: extras?.jobId,
27
+ batchIndex: extras?.batchIndex,
28
+ claimable: extras?.claimable,
29
+ assignee: extras?.assignee,
30
+ version: v7()
31
+ };
32
+ this.tasks.set(entry.id, entry);
33
+ return entry;
34
+ }
35
+ get(id) {
36
+ return this.tasks.get(id);
37
+ }
38
+ getFromHistory(id) {
39
+ return this.historyList.find((e) => e.id === id);
40
+ }
41
+ getAny(id) {
42
+ return this.tasks.get(id) ?? this.historyList.find((e) => e.id === id);
43
+ }
44
+ /** Fields that must never be overwritten via update() */
45
+ static IMMUTABLE_FIELDS = new Set(["id", "taskPath"]);
46
+ update(id, patch) {
47
+ const entry = this.tasks.get(id);
48
+ if (!entry) throw new Error(`Task ${id} not found in active store`);
49
+ const safePatch = { ...patch };
50
+ for (const key of TaskStore.IMMUTABLE_FIELDS) delete safePatch[key];
51
+ Object.assign(entry, safePatch);
52
+ }
53
+ listActive() {
54
+ return [...this.tasks.values()];
55
+ }
56
+ listHistory() {
57
+ return [...this.historyList];
58
+ }
59
+ moveToHistory(id) {
60
+ const entry = this.tasks.get(id);
61
+ if (!entry) return;
62
+ this.tasks.delete(id);
63
+ this.historyList.push(entry);
64
+ while (this.historyList.length > this.maxHistory) this.historyList.shift();
65
+ }
66
+ restoreFromHistory(id) {
67
+ const idx = this.historyList.findIndex((e) => e.id === id);
68
+ if (idx === -1) return void 0;
69
+ const entry = this.historyList[idx];
70
+ this.historyList.splice(idx, 1);
71
+ this.tasks.set(entry.id, entry);
72
+ return entry;
73
+ }
74
+ addSchedule(opts) {
75
+ parseCron(opts.cron);
76
+ for (const existing of this.schedules.values()) if (existing.name === opts.name) throw new Error(`Schedule "${opts.name}" already exists`);
77
+ const now = /* @__PURE__ */ new Date();
78
+ const entry = {
79
+ id: v7(),
80
+ name: opts.name,
81
+ cron: opts.cron,
82
+ taskPath: opts.taskPath,
83
+ args: opts.args,
84
+ enabled: opts.enabled ?? true,
85
+ created: Date.now(),
86
+ ownerKey: opts.ownerKey,
87
+ nextRun: nextRun(opts.cron, now).getTime()
88
+ };
89
+ this.schedules.set(entry.id, entry);
90
+ return entry;
91
+ }
92
+ getSchedule(id) {
93
+ return this.schedules.get(id);
94
+ }
95
+ getScheduleByName(name) {
96
+ for (const entry of this.schedules.values()) if (entry.name === name) return entry;
97
+ }
98
+ listSchedules() {
99
+ return [...this.schedules.values()];
100
+ }
101
+ updateSchedule(id, patch) {
102
+ const entry = this.schedules.get(id);
103
+ if (!entry) throw new Error(`Schedule ${id} not found`);
104
+ const safePatch = { ...patch };
105
+ delete safePatch.id;
106
+ Object.assign(entry, safePatch);
107
+ if (patch.cron) {
108
+ parseCron(patch.cron);
109
+ entry.nextRun = nextRun(patch.cron, /* @__PURE__ */ new Date()).getTime();
110
+ }
111
+ }
112
+ removeSchedule(id) {
113
+ return this.schedules.delete(id);
114
+ }
115
+ recordScheduleRun(id, now) {
116
+ const entry = this.schedules.get(id);
117
+ if (!entry) return;
118
+ entry.lastRun = now;
119
+ entry.nextRun = nextRun(entry.cron, new Date(now)).getTime();
120
+ }
121
+ jobs = /* @__PURE__ */ new Map();
122
+ addJob(job) {
123
+ this.jobs.set(job.id, job);
124
+ }
125
+ getJob(id) {
126
+ return this.jobs.get(id);
127
+ }
128
+ listJobs() {
129
+ return [...this.jobs.values()];
130
+ }
131
+ updateJob(id, patch) {
132
+ const job = this.jobs.get(id);
133
+ if (!job) throw new Error(`Job ${id} not found`);
134
+ const safePatch = { ...patch };
135
+ delete safePatch.id;
136
+ Object.assign(job, safePatch);
137
+ }
138
+ removeJob(id) {
139
+ return this.jobs.delete(id);
140
+ }
141
+ /** Get the next pending batch index for a job, or -1 if none */
142
+ nextPendingBatch(jobId) {
143
+ const job = this.jobs.get(jobId);
144
+ if (!job) return -1;
145
+ for (let i = 0; i < job.totalBatches; i++) if (job.mapTaskIds[i] === "") return i;
146
+ return -1;
147
+ }
148
+ snapshot() {
149
+ const tasks = {};
150
+ for (const [id, entry] of this.tasks) tasks[id] = { ...entry };
151
+ const schedules = this.listSchedules().map((s) => ({ ...s }));
152
+ const jobs = this.listJobs().map((j) => ({ ...j }));
153
+ return {
154
+ version: 3,
155
+ tasks,
156
+ history: this.historyList.map((e) => ({ ...e })),
157
+ schedules,
158
+ jobs
159
+ };
160
+ }
161
+ restore(snapshot, now) {
162
+ this.tasks.clear();
163
+ this.historyList = [];
164
+ this.schedules.clear();
165
+ this.jobs.clear();
166
+ for (const [_id, entry] of Object.entries(snapshot.tasks)) if (entry.status === "running" || entry.status === "pending") {
167
+ entry.status = "failed";
168
+ entry.error = "Stale: recovered after restart";
169
+ entry.ended = now;
170
+ this.historyList.push(entry);
171
+ } else if (entry.status === "budget-blocked") if (this.budgetBlockedTTL > 0 && now - entry.created > this.budgetBlockedTTL) {
172
+ entry.status = "failed";
173
+ entry.error = "Budget-blocked task evicted: exceeded staleness TTL";
174
+ entry.ended = now;
175
+ this.historyList.push(entry);
176
+ } else this.tasks.set(entry.id, entry);
177
+ else if (entry.status === "done" || entry.status === "failed" || entry.status === "cancelled" || entry.status === "timeout") this.historyList.push(entry);
178
+ for (const entry of snapshot.history) this.historyList.push(entry);
179
+ while (this.historyList.length > this.maxHistory) this.historyList.shift();
180
+ if (snapshot.schedules) for (const schedule of snapshot.schedules) this.schedules.set(schedule.id, { ...schedule });
181
+ if (snapshot.jobs) for (const job of snapshot.jobs) this.jobs.set(job.id, { ...job });
182
+ }
183
+ };
184
+
185
+ //#endregion
186
+ export { TaskStore };
187
+ //# sourceMappingURL=task-store.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-store.mjs","names":["cronNextRun"],"sources":["../src/task-store.ts"],"sourcesContent":["import { v7 } from \"@aigne/uuid\";\nimport { nextRun as cronNextRun, parseCron } from \"./cron-parser.js\";\nimport type {\n BudgetEstimate,\n JobEntry,\n ScheduleEntry,\n SchedulerSnapshot,\n TaskEntry,\n} from \"./types.js\";\n\nconst DEFAULT_BUDGET_BLOCKED_TTL = 86_400_000; // 24 hours\n\n/** Extra fields for task creation */\ninterface CreateExtras {\n ownerKey?: string;\n scheduleId?: string;\n jobId?: string;\n batchIndex?: number;\n /** Claim-based (pull model) task — see #1067. */\n claimable?: boolean;\n /** Claim constraint: \"\" = any worker, DID = only that worker. Only meaningful when `claimable`. */\n assignee?: string;\n}\n\nexport class TaskStore {\n private tasks = new Map<string, TaskEntry>();\n private historyList: TaskEntry[] = [];\n private schedules = new Map<string, ScheduleEntry>();\n\n constructor(\n private maxHistory = 100,\n private budgetBlockedTTL = DEFAULT_BUDGET_BLOCKED_TTL,\n ) {}\n\n create(\n taskPath: string,\n args?: unknown,\n procId?: string,\n estimate?: BudgetEstimate,\n extras?: CreateExtras,\n ): TaskEntry {\n const entry: TaskEntry = {\n id: v7(),\n status: \"pending\",\n taskPath,\n args,\n estimate,\n created: Date.now(),\n attempts: 0,\n procId,\n ownerKey: extras?.ownerKey,\n scheduleId: extras?.scheduleId,\n jobId: extras?.jobId,\n batchIndex: extras?.batchIndex,\n claimable: extras?.claimable,\n assignee: extras?.assignee,\n version: v7(),\n };\n this.tasks.set(entry.id, entry);\n return entry;\n }\n\n get(id: string): TaskEntry | undefined {\n return this.tasks.get(id);\n }\n\n getFromHistory(id: string): TaskEntry | undefined {\n return this.historyList.find((e) => e.id === id);\n }\n\n getAny(id: string): TaskEntry | undefined {\n return this.tasks.get(id) ?? this.historyList.find((e) => e.id === id);\n }\n\n /** Fields that must never be overwritten via update() */\n private static readonly IMMUTABLE_FIELDS = new Set([\"id\", \"taskPath\"]);\n\n update(id: string, patch: Partial<TaskEntry>): void {\n const entry = this.tasks.get(id);\n if (!entry) throw new Error(`Task ${id} not found in active store`);\n const safePatch = { ...patch };\n for (const key of TaskStore.IMMUTABLE_FIELDS) {\n delete (safePatch as Record<string, unknown>)[key];\n }\n Object.assign(entry, safePatch);\n }\n\n listActive(): TaskEntry[] {\n return [...this.tasks.values()];\n }\n\n listHistory(): TaskEntry[] {\n return [...this.historyList];\n }\n\n moveToHistory(id: string): void {\n const entry = this.tasks.get(id);\n if (!entry) return;\n this.tasks.delete(id);\n this.historyList.push(entry);\n while (this.historyList.length > this.maxHistory) {\n this.historyList.shift();\n }\n }\n\n restoreFromHistory(id: string): TaskEntry | undefined {\n const idx = this.historyList.findIndex((e) => e.id === id);\n if (idx === -1) return undefined;\n const entry = this.historyList[idx]!;\n this.historyList.splice(idx, 1);\n this.tasks.set(entry.id, entry);\n return entry;\n }\n\n // ═══════════════════════════════════════════════════════════════\n // Schedule management\n // ═══════════════════════════════════════════════════════════════\n\n addSchedule(opts: {\n name: string;\n cron: string;\n taskPath: string;\n args?: unknown;\n enabled?: boolean;\n ownerKey?: string;\n }): ScheduleEntry {\n // Validate cron expression\n parseCron(opts.cron);\n\n // Check for duplicate name\n for (const existing of this.schedules.values()) {\n if (existing.name === opts.name) {\n throw new Error(`Schedule \"${opts.name}\" already exists`);\n }\n }\n\n const now = new Date();\n const entry: ScheduleEntry = {\n id: v7(),\n name: opts.name,\n cron: opts.cron,\n taskPath: opts.taskPath,\n args: opts.args,\n enabled: opts.enabled ?? true,\n created: Date.now(),\n ownerKey: opts.ownerKey,\n nextRun: cronNextRun(opts.cron, now).getTime(),\n };\n this.schedules.set(entry.id, entry);\n return entry;\n }\n\n getSchedule(id: string): ScheduleEntry | undefined {\n return this.schedules.get(id);\n }\n\n getScheduleByName(name: string): ScheduleEntry | undefined {\n for (const entry of this.schedules.values()) {\n if (entry.name === name) return entry;\n }\n return undefined;\n }\n\n listSchedules(): ScheduleEntry[] {\n return [...this.schedules.values()];\n }\n\n updateSchedule(id: string, patch: Partial<ScheduleEntry>): void {\n const entry = this.schedules.get(id);\n if (!entry) throw new Error(`Schedule ${id} not found`);\n\n const safePatch = { ...patch };\n // Never allow changing id\n delete (safePatch as Record<string, unknown>).id;\n Object.assign(entry, safePatch);\n\n // If cron changed, recalculate nextRun\n if (patch.cron) {\n parseCron(patch.cron); // validate\n entry.nextRun = cronNextRun(patch.cron, new Date()).getTime();\n }\n }\n\n removeSchedule(id: string): boolean {\n return this.schedules.delete(id);\n }\n\n recordScheduleRun(id: string, now: number): void {\n const entry = this.schedules.get(id);\n if (!entry) return;\n entry.lastRun = now;\n entry.nextRun = cronNextRun(entry.cron, new Date(now)).getTime();\n }\n\n // ═══════════════════════════════════════════════════════════════\n // Job management (map-reduce)\n // ═══════════════════════════════════════════════════════════════\n\n private jobs = new Map<string, JobEntry>();\n\n addJob(job: JobEntry): void {\n this.jobs.set(job.id, job);\n }\n\n getJob(id: string): JobEntry | undefined {\n return this.jobs.get(id);\n }\n\n listJobs(): JobEntry[] {\n return [...this.jobs.values()];\n }\n\n updateJob(id: string, patch: Partial<JobEntry>): void {\n const job = this.jobs.get(id);\n if (!job) throw new Error(`Job ${id} not found`);\n const safePatch = { ...patch };\n delete (safePatch as Record<string, unknown>).id;\n Object.assign(job, safePatch);\n }\n\n removeJob(id: string): boolean {\n return this.jobs.delete(id);\n }\n\n /** Get the next pending batch index for a job, or -1 if none */\n nextPendingBatch(jobId: string): number {\n const job = this.jobs.get(jobId);\n if (!job) return -1;\n for (let i = 0; i < job.totalBatches; i++) {\n if (job.mapTaskIds[i] === \"\") {\n return i;\n }\n }\n return -1;\n }\n\n // ═══════════════════════════════════════════════════════════════\n // Snapshot — v3\n // ═══════════════════════════════════════════════════════════════\n\n snapshot(): SchedulerSnapshot {\n const tasks: Record<string, TaskEntry> = {};\n for (const [id, entry] of this.tasks) {\n tasks[id] = { ...entry };\n }\n const schedules = this.listSchedules().map((s) => ({ ...s }));\n const jobs = this.listJobs().map((j) => ({ ...j }));\n return {\n version: 3,\n tasks,\n history: this.historyList.map((e) => ({ ...e })),\n schedules,\n jobs,\n };\n }\n\n restore(snapshot: SchedulerSnapshot, now: number): void {\n this.tasks.clear();\n this.historyList = [];\n this.schedules.clear();\n this.jobs.clear();\n\n // Restore task entries with stale detection\n for (const [_id, entry] of Object.entries(snapshot.tasks)) {\n if (entry.status === \"running\" || entry.status === \"pending\") {\n // Stale: mark as failed and move to history\n entry.status = \"failed\";\n entry.error = \"Stale: recovered after restart\";\n entry.ended = now;\n this.historyList.push(entry);\n } else if (entry.status === \"budget-blocked\") {\n // Evict stale budget-blocked tasks that exceeded TTL\n if (this.budgetBlockedTTL > 0 && now - entry.created > this.budgetBlockedTTL) {\n entry.status = \"failed\";\n entry.error = \"Budget-blocked task evicted: exceeded staleness TTL\";\n entry.ended = now;\n this.historyList.push(entry);\n } else {\n // Budget-blocked tasks stay in active — recoverable via retry\n this.tasks.set(entry.id, entry);\n }\n } else if (\n entry.status === \"done\" ||\n entry.status === \"failed\" ||\n entry.status === \"cancelled\" ||\n entry.status === \"timeout\"\n ) {\n // Terminal entries stuck in active (crash between update and moveToHistory)\n this.historyList.push(entry);\n }\n }\n\n // Restore history\n for (const entry of snapshot.history) {\n this.historyList.push(entry);\n }\n\n // Enforce history cap\n while (this.historyList.length > this.maxHistory) {\n this.historyList.shift();\n }\n\n // Restore schedules (v2+) — missing in v1 snapshots\n if (snapshot.schedules) {\n for (const schedule of snapshot.schedules) {\n this.schedules.set(schedule.id, { ...schedule });\n }\n }\n\n // Restore jobs (v3+) — missing in v1/v2 snapshots\n if (snapshot.jobs) {\n for (const job of snapshot.jobs) {\n this.jobs.set(job.id, { ...job });\n }\n }\n }\n}\n"],"mappings":"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"}
package/dist/types.cjs ADDED
@@ -0,0 +1,185 @@
1
+ let zod_v4 = require("zod/v4");
2
+
3
+ //#region src/types.ts
4
+ const DispatchArgsSchema = zod_v4.z.object({
5
+ task: zod_v4.z.string().min(1),
6
+ args: zod_v4.z.unknown().optional(),
7
+ estimate: zod_v4.z.object({
8
+ tokens: zod_v4.z.number().int().nonnegative().optional(),
9
+ duration: zod_v4.z.number().int().nonnegative().optional(),
10
+ calls: zod_v4.z.number().int().nonnegative().optional()
11
+ }).optional(),
12
+ proc: zod_v4.z.object({
13
+ type: zod_v4.z.enum([
14
+ "realm",
15
+ "blocklet",
16
+ "agent-run",
17
+ "ash-script",
18
+ "resource"
19
+ ]),
20
+ parentId: zod_v4.z.string().optional(),
21
+ appId: zod_v4.z.string().optional(),
22
+ budget: zod_v4.z.record(zod_v4.z.string(), zod_v4.z.number().positive()).optional(),
23
+ timeout: zod_v4.z.number().int().positive().optional()
24
+ }).optional(),
25
+ assignee: zod_v4.z.string().optional()
26
+ });
27
+ const CancelArgsSchema = zod_v4.z.object({ taskId: zod_v4.z.string().min(1) });
28
+ const RetryArgsSchema = zod_v4.z.object({ taskId: zod_v4.z.string().min(1) });
29
+ const ScheduleArgsSchema = zod_v4.z.object({
30
+ name: zod_v4.z.string().min(1),
31
+ cron: zod_v4.z.string().min(1),
32
+ task: zod_v4.z.string().min(1),
33
+ args: zod_v4.z.unknown().optional(),
34
+ enabled: zod_v4.z.boolean().optional()
35
+ });
36
+ const UnscheduleArgsSchema = zod_v4.z.object({
37
+ scheduleId: zod_v4.z.string().min(1).optional(),
38
+ name: zod_v4.z.string().min(1).optional()
39
+ });
40
+ const HeartbeatArgsSchema = zod_v4.z.object({
41
+ taskId: zod_v4.z.string().min(1),
42
+ progress: zod_v4.z.unknown().optional()
43
+ });
44
+ const ClaimArgsSchema = zod_v4.z.object({
45
+ taskId: zod_v4.z.string().min(1),
46
+ workerId: zod_v4.z.string().min(1),
47
+ leaseDuration: zod_v4.z.number().int().positive().optional(),
48
+ ifMatch: zod_v4.z.string().optional()
49
+ });
50
+ const SubmitArgsSchema = zod_v4.z.object({
51
+ taskId: zod_v4.z.string().min(1),
52
+ workerId: zod_v4.z.string().min(1),
53
+ success: zod_v4.z.boolean().optional(),
54
+ result: zod_v4.z.unknown().optional(),
55
+ artifacts: zod_v4.z.unknown().optional(),
56
+ verifyTask: zod_v4.z.string().min(1).optional(),
57
+ verifyArgs: zod_v4.z.unknown().optional()
58
+ });
59
+ const SubmitJobArgsSchema = zod_v4.z.object({
60
+ name: zod_v4.z.string().optional(),
61
+ mapTask: zod_v4.z.string().min(1),
62
+ reduceTask: zod_v4.z.string().min(1).optional(),
63
+ totalItems: zod_v4.z.number().int().positive(),
64
+ batchSize: zod_v4.z.number().int().positive().optional(),
65
+ concurrency: zod_v4.z.number().int().positive().optional(),
66
+ args: zod_v4.z.unknown().optional(),
67
+ timeout: zod_v4.z.number().int().positive().optional()
68
+ });
69
+ const CancelJobArgsSchema = zod_v4.z.object({ jobId: zod_v4.z.string().min(1) });
70
+ /** Zod schema for validating restored snapshots */
71
+ const TaskResultSchema = zod_v4.z.object({
72
+ success: zod_v4.z.boolean(),
73
+ data: zod_v4.z.unknown().optional(),
74
+ error: zod_v4.z.string().optional()
75
+ });
76
+ const BudgetEstimateSchema = zod_v4.z.object({
77
+ tokens: zod_v4.z.number().nonnegative().optional(),
78
+ duration: zod_v4.z.number().nonnegative().optional(),
79
+ calls: zod_v4.z.number().nonnegative().optional()
80
+ });
81
+ const TaskEntrySchema = zod_v4.z.object({
82
+ id: zod_v4.z.string(),
83
+ status: zod_v4.z.enum([
84
+ "pending",
85
+ "running",
86
+ "done",
87
+ "failed",
88
+ "cancelled",
89
+ "budget-blocked",
90
+ "timeout"
91
+ ]),
92
+ taskPath: zod_v4.z.string(),
93
+ created: zod_v4.z.number().nonnegative(),
94
+ attempts: zod_v4.z.number().nonnegative(),
95
+ args: zod_v4.z.unknown().optional(),
96
+ estimate: BudgetEstimateSchema.optional(),
97
+ result: TaskResultSchema.optional(),
98
+ error: zod_v4.z.string().optional(),
99
+ started: zod_v4.z.number().nonnegative().optional(),
100
+ ended: zod_v4.z.number().nonnegative().optional(),
101
+ procId: zod_v4.z.string().optional(),
102
+ ownerKey: zod_v4.z.string().optional(),
103
+ scheduleId: zod_v4.z.string().optional(),
104
+ deadline: zod_v4.z.number().nonnegative().optional(),
105
+ lastHeartbeat: zod_v4.z.number().nonnegative().optional(),
106
+ timeout: zod_v4.z.number().nonnegative().optional(),
107
+ jobId: zod_v4.z.string().optional(),
108
+ batchIndex: zod_v4.z.number().int().nonnegative().optional(),
109
+ claimable: zod_v4.z.boolean().optional(),
110
+ assignee: zod_v4.z.string().optional(),
111
+ claimedBy: zod_v4.z.string().optional(),
112
+ claimedAt: zod_v4.z.number().nonnegative().optional(),
113
+ version: zod_v4.z.string().optional()
114
+ });
115
+ const ScheduleEntrySchema = zod_v4.z.object({
116
+ id: zod_v4.z.string(),
117
+ name: zod_v4.z.string(),
118
+ cron: zod_v4.z.string(),
119
+ taskPath: zod_v4.z.string(),
120
+ args: zod_v4.z.unknown().optional(),
121
+ enabled: zod_v4.z.boolean(),
122
+ created: zod_v4.z.number().nonnegative(),
123
+ ownerKey: zod_v4.z.string().optional(),
124
+ lastRun: zod_v4.z.number().nonnegative().optional(),
125
+ nextRun: zod_v4.z.number().nonnegative().optional()
126
+ });
127
+ const JobProgressSchema = zod_v4.z.object({
128
+ pending: zod_v4.z.number().int().nonnegative(),
129
+ running: zod_v4.z.number().int().nonnegative(),
130
+ done: zod_v4.z.number().int().nonnegative(),
131
+ failed: zod_v4.z.number().int().nonnegative()
132
+ });
133
+ const JobEntrySchema = zod_v4.z.object({
134
+ id: zod_v4.z.string(),
135
+ name: zod_v4.z.string().optional(),
136
+ status: zod_v4.z.enum([
137
+ "pending",
138
+ "mapping",
139
+ "reducing",
140
+ "done",
141
+ "failed",
142
+ "cancelled"
143
+ ]),
144
+ mapTask: zod_v4.z.string(),
145
+ reduceTask: zod_v4.z.string().optional(),
146
+ args: zod_v4.z.unknown().optional(),
147
+ totalItems: zod_v4.z.number().int().positive(),
148
+ batchSize: zod_v4.z.number().int().positive(),
149
+ concurrency: zod_v4.z.number().int().positive(),
150
+ timeout: zod_v4.z.number().int().positive().optional(),
151
+ totalBatches: zod_v4.z.number().int().nonnegative(),
152
+ progress: JobProgressSchema,
153
+ mapTaskIds: zod_v4.z.array(zod_v4.z.string()),
154
+ reduceTaskId: zod_v4.z.string().optional(),
155
+ mapResults: zod_v4.z.array(zod_v4.z.unknown()),
156
+ result: zod_v4.z.unknown().optional(),
157
+ error: zod_v4.z.string().optional(),
158
+ created: zod_v4.z.number().nonnegative(),
159
+ started: zod_v4.z.number().nonnegative().optional(),
160
+ ended: zod_v4.z.number().nonnegative().optional()
161
+ });
162
+ const SchedulerSnapshotSchema = zod_v4.z.object({
163
+ version: zod_v4.z.union([
164
+ zod_v4.z.literal(1),
165
+ zod_v4.z.literal(2),
166
+ zod_v4.z.literal(3)
167
+ ]),
168
+ tasks: zod_v4.z.record(zod_v4.z.string(), TaskEntrySchema),
169
+ history: zod_v4.z.array(TaskEntrySchema),
170
+ schedules: zod_v4.z.array(ScheduleEntrySchema).optional(),
171
+ jobs: zod_v4.z.array(JobEntrySchema).optional()
172
+ });
173
+
174
+ //#endregion
175
+ exports.CancelArgsSchema = CancelArgsSchema;
176
+ exports.CancelJobArgsSchema = CancelJobArgsSchema;
177
+ exports.ClaimArgsSchema = ClaimArgsSchema;
178
+ exports.DispatchArgsSchema = DispatchArgsSchema;
179
+ exports.HeartbeatArgsSchema = HeartbeatArgsSchema;
180
+ exports.RetryArgsSchema = RetryArgsSchema;
181
+ exports.ScheduleArgsSchema = ScheduleArgsSchema;
182
+ exports.SchedulerSnapshotSchema = SchedulerSnapshotSchema;
183
+ exports.SubmitArgsSchema = SubmitArgsSchema;
184
+ exports.SubmitJobArgsSchema = SubmitJobArgsSchema;
185
+ exports.UnscheduleArgsSchema = UnscheduleArgsSchema;