@runium/core 0.0.9 → 0.1.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.
package/index.js ADDED
@@ -0,0 +1,594 @@
1
+ import { readFile as x, writeFile as M, mkdir as j } from "node:fs/promises";
2
+ import { EventEmitter as v } from "node:events";
3
+ import q from "bcx-expression-evaluator";
4
+ import K from "ajv/dist/2020.js";
5
+ import U from "ajv-keywords";
6
+ import { spawn as F } from "node:child_process";
7
+ import { createWriteStream as w } from "node:fs";
8
+ import { resolve as I, dirname as P } from "node:path";
9
+ class p extends Error {
10
+ code;
11
+ payload;
12
+ constructor(t, s, r = null) {
13
+ super(t), this.code = s, this.payload = r;
14
+ }
15
+ }
16
+ function At(e) {
17
+ return !!e && e instanceof p;
18
+ }
19
+ var Y = ((e) => (e.READ_JSON = "file-read-json", e.WRITE_JSON = "file-write-json", e))(Y || {});
20
+ async function _t(e) {
21
+ try {
22
+ const t = await x(e, { encoding: "utf-8" });
23
+ return JSON.parse(t);
24
+ } catch (t) {
25
+ throw new p(`Can not read JSON file ${e}`, "file-read-json", { path: e, original: t });
26
+ }
27
+ }
28
+ async function It(e, t) {
29
+ try {
30
+ await M(e, JSON.stringify(t, null, 2), { encoding: "utf-8" });
31
+ } catch (s) {
32
+ throw new p(`Can not write JSON file ${e}`, "file-write-json", { path: e, data: t, original: s });
33
+ }
34
+ }
35
+ const V = String.raw`"\$unwrap\((.*)\)"`;
36
+ var J = ((e) => (e.MACRO_APPLY_ERROR = "macro-apply-error", e))(J || {});
37
+ function $t(e, t) {
38
+ const s = Object.keys(t), r = String.raw`\$(${s.join("|")})\(([^()]*(?:\([^()]*\)[^()]*)*)\)`, i = (o) => {
39
+ const a = new RegExp(r, "g");
40
+ let c = o, n = !0;
41
+ for (; n; ) n = !1, c = c.replace(a, (u, A, N) => {
42
+ n = !0;
43
+ const y = [];
44
+ let _ = 0, S = "";
45
+ for (const g of N) g === "," && _ === 0 ? (y.push(S.trim()), S = "") : (g === "(" && _++, g === ")" && _--, S += g);
46
+ y.push(S.trim());
47
+ const G = y.map((g) => g ? i(g) : "");
48
+ try {
49
+ return t[A](...G);
50
+ } catch (g) {
51
+ throw new p(`Can not apply macro "${A}"`, "macro-apply-error", { type: A, args: y, original: g });
52
+ }
53
+ });
54
+ return c;
55
+ };
56
+ return (function(o) {
57
+ const a = new RegExp(V, "g");
58
+ return o.replace(a, (c, n) => n.trim());
59
+ })(s.length ? i(e) : e);
60
+ }
61
+ var f = ((e) => (e.IMMEDIATE = "immediate", e.DEFERRED = "deferred", e.IGNORE = "ignore", e))(f || {}), d = ((e) => (e.EMIT_EVENT = "emit-event", e.START_TASK = "start-task", e.RESTART_TASK = "restart-task", e.STOP_TASK = "stop-task", e.STOP_PROJECT = "stop-project", e.ENABLE_TRIGGER = "enable-trigger", e.DISABLE_TRIGGER = "disable-trigger", e))(d || {}), T = ((e) => (e.DEFAULT = "default", e))(T || {}), l = ((e) => (e.EVENT = "event", e.TIMEOUT = "timeout", e.INTERVAL = "interval", e))(l || {}), m = ((e) => (e.ALWAYS = "always", e.ON_FAILURE = "on-failure", e))(m || {}), B = ((e) => (e.INCORRECT_DATA = "project-config-incorrect-data", e.TASKS_CIRCULAR_DEPENDENCY = "project-config-tasks-circular-dependency", e.TASK_NOT_EXISTS = "project-config-task-not-exists", e.TRIGGER_NOT_EXISTS = "project-config-trigger-not-exists", e))(B || {});
62
+ const H = /* @__PURE__ */ new Set(["start-task", "restart-task", "stop-task"]), W = /* @__PURE__ */ new Set(["enable-trigger", "disable-trigger"]), X = new Set(Object.values(d)), z = new Set(Object.values(l));
63
+ function b(e) {
64
+ return H.has(e.type);
65
+ }
66
+ function O(e) {
67
+ return W.has(e.type);
68
+ }
69
+ function Z(e) {
70
+ return !X.has(e.type);
71
+ }
72
+ function Q(e) {
73
+ return !z.has(e.type);
74
+ }
75
+ const C = new K({ allowUnionTypes: !0, allErrors: !0, verbose: !0 });
76
+ function tt(e) {
77
+ const t = /* @__PURE__ */ new Map(), s = /* @__PURE__ */ new Set();
78
+ for (const i of e.tasks) s.add(i.id), t.set(i.id, []);
79
+ const r = new Set(e.triggers?.map((i) => i.id) || []);
80
+ for (const i of e.tasks) {
81
+ for (const o of i.dependencies || []) {
82
+ if (!s.has(o.taskId)) throw new p(`Task "${i.id}" depends on not existing task "${o.taskId}"`, "project-config-task-not-exists", { taskId: i.id, dependency: { ...o }, scope: "dependencies" });
83
+ t.get(o.taskId).push(i.id);
84
+ }
85
+ for (const o of i.handlers || []) {
86
+ if (b(o.action) && !s.has(o.action.taskId)) throw new p(`Task "${i.id}" handler action uses not existing task "${o.action.taskId}"`, "project-config-task-not-exists", { taskId: i.id, handler: { ...o }, scope: "handlers" });
87
+ if (O(o.action) && !r.has(o.action.triggerId)) throw new p(`Task "${i.id}" handler action uses not existing trigger "${o.action.triggerId}"`, "project-config-trigger-not-exists", { taskId: i.id, handler: { ...o }, scope: "handlers" });
88
+ }
89
+ }
90
+ for (const i of e.triggers || []) {
91
+ if (b(i.action) && !s.has(i.action.taskId)) throw new p(`Trigger "${i.id}" action uses not existing task "${i.action.taskId}"`, "project-config-task-not-exists", { trigger: { ...i }, scope: "triggers" });
92
+ if (O(i.action) && !r.has(i.action.triggerId)) throw new p(`Trigger "${i.id}" action uses not existing trigger "${i.action.triggerId}"`, "project-config-trigger-not-exists", { trigger: { ...i }, scope: "triggers" });
93
+ }
94
+ (function(i) {
95
+ const o = /* @__PURE__ */ new Set(), a = /* @__PURE__ */ new Set(), c = (n) => {
96
+ if (a.has(n)) {
97
+ const u = [...a, n].join(" -> ");
98
+ throw new p(`Project config tasks circular dependency detected ${u}`, "project-config-tasks-circular-dependency", { task: n, dependencies: [...a] });
99
+ }
100
+ if (!o.has(n)) {
101
+ a.add(n);
102
+ for (const u of i.get(n) || []) c(u);
103
+ a.delete(n), o.add(n);
104
+ }
105
+ };
106
+ for (const n of i.keys()) o.has(n) || c(n);
107
+ })(t);
108
+ }
109
+ function et(e, t) {
110
+ (function(s, r) {
111
+ const i = C.compile(r || {});
112
+ if (!i(s)) throw new p("Incorrect project config data", "project-config-incorrect-data", { errors: i.errors });
113
+ })(e, t), tt(e);
114
+ }
115
+ U(C, ["uniqueItemProperties"]);
116
+ var h = ((e) => (e.IDLE = "idle", e.STARTING = "starting", e.STARTED = "started", e.COMPLETED = "completed", e.FAILED = "failed", e.STOPPING = "stopping", e.STOPPED = "stopped", e))(h || {}), k = ((e) => (e.STATE_CHANGE = "state-change", e.STDOUT = "stdout", e.STDERR = "stderr", e))(k || {});
117
+ const jt = -1;
118
+ class D extends v {
119
+ constructor(t) {
120
+ super(), this.options = t;
121
+ }
122
+ }
123
+ class st extends D {
124
+ constructor(t) {
125
+ super(t), this.options = t, this.setMaxListeners(50), this.options.env = { ...process.env, ...t.env || {} }, this.options.cwd = I(process.cwd(), t.cwd || "");
126
+ }
127
+ state = { status: "idle", pid: -1, timestamp: Date.now(), iteration: 0 };
128
+ process = null;
129
+ stdoutStream = null;
130
+ stderrStream = null;
131
+ ttlTimer = null;
132
+ getState() {
133
+ return { ...this.state };
134
+ }
135
+ getOptions() {
136
+ return { ...this.options };
137
+ }
138
+ canStart() {
139
+ const { status: t } = this.state;
140
+ return t !== "started" && t !== "starting" && t !== "stopping";
141
+ }
142
+ async start() {
143
+ if (this.canStart()) {
144
+ this.updateState({ status: "starting", iteration: this.state.iteration + 1, pid: -1, exitCode: void 0, error: void 0 });
145
+ try {
146
+ await this.initLogStreams();
147
+ const { cwd: t, command: s, arguments: r = [], env: i, shell: o = !0 } = this.options;
148
+ this.process = F(s, r, { cwd: t, env: i, shell: o, stdio: ["ignore", "pipe", "pipe"] }), this.addProcessListeners(), this.setTTLTimer(), this.updateState({ status: "started", pid: this.process.pid });
149
+ } catch (t) {
150
+ this.onError(t);
151
+ }
152
+ }
153
+ }
154
+ canStop() {
155
+ const { status: t } = this.state;
156
+ return !!this.process && t === "started";
157
+ }
158
+ async stop(t = "") {
159
+ if (this.canStop()) return this.updateState({ status: "stopping", reason: t }), new Promise((s) => {
160
+ const r = this.options.stopSignal || "SIGTERM";
161
+ this.process.kill(r), this.process.on("exit", () => {
162
+ s();
163
+ });
164
+ });
165
+ }
166
+ async restart() {
167
+ await this.stop("restart"), this.start();
168
+ }
169
+ updateState(t) {
170
+ const s = { ...this.state, ...t, timestamp: Date.now() };
171
+ this.state = Object.fromEntries(Object.entries(s).filter(([r, i]) => i !== void 0)), this.emit("state-change", this.getState()), this.emit(this.state.status, this.getState());
172
+ }
173
+ async initLogStreams() {
174
+ const { stdout: t = null, stderr: s = null } = this.options.log || {};
175
+ if (t) {
176
+ const r = I(t);
177
+ await j(P(r), { recursive: !0 }), this.stdoutStream = w(t, { flags: "w" });
178
+ }
179
+ if (s) {
180
+ const r = I(s);
181
+ await j(P(r), { recursive: !0 }), this.stderrStream = w(s, { flags: "w" });
182
+ }
183
+ }
184
+ setTTLTimer() {
185
+ const { ttl: t } = this.options;
186
+ t && this.process && (this.ttlTimer = setTimeout(() => {
187
+ this.stop("ttl");
188
+ }, t));
189
+ }
190
+ addProcessListeners() {
191
+ this.process && (this.process.stdout?.on("data", (t) => this.onStdOutData(t)), this.process.stderr?.on("data", (t) => this.onStdErrData(t)), this.process.on("exit", (t) => this.onExit(t)), this.process.on("error", (t) => this.onError(t)));
192
+ }
193
+ onStdOutData(t) {
194
+ const s = t.toString();
195
+ this.emit("stdout", s), this.stdoutStream && this.stdoutStream.write(s);
196
+ }
197
+ onStdErrData(t) {
198
+ const s = t.toString();
199
+ this.emit("stderr", s), this.stderrStream && this.stderrStream.write(s);
200
+ }
201
+ onExit(t) {
202
+ const s = t !== null ? t : -1;
203
+ this.updateState({ status: s === 0 ? "completed" : s === -1 ? "stopped" : "failed", exitCode: s }), this.cleanup();
204
+ }
205
+ onError(t) {
206
+ this.updateState({ status: "failed", error: t }), this.cleanup();
207
+ }
208
+ cleanup() {
209
+ this.ttlTimer && (clearTimeout(this.ttlTimer), this.ttlTimer = null), this.process && (this.process.removeAllListeners(), this.process.stdout?.removeAllListeners(), this.process.stderr?.removeAllListeners(), this.process = null), this.stdoutStream && (this.stdoutStream.end(), this.stdoutStream = null), this.stderrStream && (this.stderrStream.end(), this.stderrStream = null);
210
+ }
211
+ }
212
+ var rt = ((e) => (e.ACTION_TYPE_ALREADY_USED = "project-schema-action-type-already-used", e.TASK_TYPE_ALREADY_USED = "project-schema-task-type-already-used", e.TRIGGER_TYPE_ALREADY_USED = "project-schema-trigger-type-already-used", e))(rt || {});
213
+ const $ = /^[a-zA-Z0-9_-]+$/, it = { id: { type: "string", pattern: $.source }, name: { type: "string" }, type: { type: "string" }, mode: { $ref: "#/$defs/Runium_TaskStartMode" }, dependencies: { type: "array", items: { $ref: "#/$defs/Runium_TaskDependency" } }, handlers: { type: "array", items: { $ref: "#/$defs/Runium_TaskHandler" } }, restart: { $ref: "#/$defs/Runium_TaskRestartPolicy" } }, E = { id: { type: "string", pattern: $.source }, action: { $ref: "#/$defs/Runium_Action" }, disabled: { type: "boolean" } };
214
+ function L(e, t) {
215
+ return { type: "object", properties: { ...structuredClone(it), type: { const: e }, options: { ...structuredClone(t) } }, required: ["id", "options"], additionalProperties: !1 };
216
+ }
217
+ function ot(e, t) {
218
+ return { type: "object", properties: { type: { type: "string", const: e }, ...t ? { payload: t } : {} }, required: ["type", ...t ? ["payload"] : []], additionalProperties: !1 };
219
+ }
220
+ function nt(e, t) {
221
+ const s = t ? { payload: t } : {}, r = t ? ["payload"] : [];
222
+ return { type: "object", properties: { ...structuredClone(E), type: { type: "string", const: e }, ...s }, required: ["id", "type", "action", ...r], additionalProperties: !1 };
223
+ }
224
+ function at() {
225
+ const e = { $schema: "https://json-schema.org/draft/2020-12/schema", $id: "https://example.com/schemas/project.json", title: "Project", type: "object", properties: { id: { type: "string", pattern: $.source }, name: { type: "string" }, tasks: { type: "array", items: { oneOf: [{ $ref: "#/$defs/Runium_TaskConfig" }] }, minItems: 1, uniqueItemProperties: ["id"] }, triggers: { type: "array", items: { $ref: "#/$defs/Runium_Trigger" }, uniqueItemProperties: ["id"] } }, required: ["id", "tasks"], additionalProperties: !1, $defs: { Runium_EnvValue: { type: ["string", "number", "boolean"] }, Runium_Env: { type: "object", additionalProperties: { $ref: "#/$defs/Runium_EnvValue" } }, Runium_TaskStartMode: { type: "string", enum: Object.values(f) }, Runium_TaskHandler: { type: "object", properties: { action: { $ref: "#/$defs/Runium_Action" }, condition: { $ref: "#/$defs/Runium_TaskStateCondition" } }, required: ["action", "condition"], additionalProperties: !1 }, Runium_TaskStateCondition: { oneOf: [{ type: "string" }, { type: "boolean" }, { $ref: "#/$defs/Runium_TaskState" }] }, Runium_TaskState: { type: "object", properties: { status: { $ref: "#/$defs/Runium_TaskStatus" }, iteration: { type: "number", minimum: 0 }, exitCode: { type: "number", minimum: 0 }, reason: { type: "string" } }, additionalProperties: !1 }, Runium_TaskStatus: { type: "string", enum: Object.values(h) }, Runium_TaskDependency: { type: "object", properties: { taskId: { type: "string" }, condition: { $ref: "#/$defs/Runium_TaskStateCondition" } }, required: ["taskId", "condition"], additionalProperties: !1 }, Runium_Trigger: { oneOf: [{ $ref: "#/$defs/Runium_TriggerEvent" }, { $ref: "#/$defs/Runium_TriggerInterval" }, { $ref: "#/$defs/Runium_TriggerTimeout" }] }, Runium_TriggerEvent: { type: "object", properties: { ...structuredClone(E), type: { const: l.EVENT }, event: { type: "string" } }, required: ["id", "type", "event", "action"], additionalProperties: !1 }, Runium_TriggerInterval: { type: "object", properties: { ...structuredClone(E), type: { const: l.INTERVAL }, interval: { type: "number", minimum: 0 } }, required: ["id", "type", "interval", "action"], additionalProperties: !1 }, Runium_TriggerTimeout: { type: "object", properties: { ...structuredClone(E), type: { const: l.TIMEOUT }, timeout: { type: "number", minimum: 0 } }, required: ["id", "type", "timeout", "action"], additionalProperties: !1 }, Runium_Action: { oneOf: [{ $ref: "#/$defs/Runium_ActionEmitEvent" }, { $ref: "#/$defs/Runium_ActionProcessTask" }, { $ref: "#/$defs/Runium_ActionStopProject" }, { $ref: "#/$defs/Runium_ActionToggleTrigger" }] }, Runium_ActionEmitEvent: { type: "object", properties: { type: { type: "string", const: d.EMIT_EVENT }, event: { type: "string" } }, required: ["type", "event"], additionalProperties: !1 }, Runium_ActionProcessTask: { type: "object", properties: { type: { type: "string", enum: [d.START_TASK, d.RESTART_TASK, d.STOP_TASK] }, taskId: { type: "string" } }, required: ["type", "taskId"], additionalProperties: !1 }, Runium_ActionStopProject: { type: "object", properties: { type: { type: "string", const: d.STOP_PROJECT } }, required: ["type"], additionalProperties: !1 }, Runium_ActionToggleTrigger: { type: "object", properties: { type: { type: "string", enum: [d.ENABLE_TRIGGER, d.DISABLE_TRIGGER] }, triggerId: { type: "string" } }, required: ["type", "triggerId"], additionalProperties: !1 }, Runium_TaskConfig: { ...L(T.DEFAULT, { $ref: "#/$defs/Runium_TaskOptions" }) }, Runium_TaskOptions: { type: "object", properties: { command: { type: "string" }, arguments: { type: "array", items: { type: "string" } }, shell: { type: "boolean" }, cwd: { type: "string" }, env: { $ref: "#/$defs/Runium_Env" }, ttl: { type: "number" }, log: { $ref: "#/$defs/Runium_TaskLog" }, stopSignal: { type: "string" } }, required: ["command"], additionalProperties: !1 }, Runium_TaskRestartPolicy: { oneOf: [{ $ref: "#/$defs/Runium_TaskRestartPolicyAlways" }, { $ref: "#/$defs/Runium_TaskRestartPolicyOnFailure" }] }, Runium_TaskRestartPolicyAlways: { type: "object", required: ["policy"], properties: { policy: { const: m.ALWAYS }, delay: { type: "number" } }, additionalProperties: !1 }, Runium_TaskRestartPolicyOnFailure: { type: "object", required: ["policy"], properties: { policy: { const: m.ON_FAILURE }, delay: { type: "number" }, maxRetries: { type: "number" } }, additionalProperties: !1 }, Runium_TaskLog: { type: "object", properties: { stdout: { type: ["string", "null"] }, stderr: { type: ["string", "null"] } }, additionalProperties: !1 } } };
226
+ return Object.freeze(e);
227
+ }
228
+ function ct(e, t) {
229
+ let s = structuredClone(e);
230
+ return t.project && (s = (function(r, i) {
231
+ return i && (r.properties = { ...i.properties || {}, ...r.properties }, r.required = Array.from(/* @__PURE__ */ new Set([...r.required ?? [], ...i.required ?? []]))), r;
232
+ })(s, t.project)), t.definitions && (s = (function(r, i) {
233
+ return i && (r.$defs = { ...i, ...r.$defs }), r;
234
+ })(s, t.definitions)), t.tasks && (s = (function(r, i) {
235
+ if (i) {
236
+ const o = new Set(r.properties.tasks.items.oneOf.map((c) => {
237
+ const n = c.$ref.split("/").pop() || "";
238
+ return r.$defs[n]?.properties?.type?.const || T.DEFAULT;
239
+ })), a = {};
240
+ for (const [c, n] of Object.entries(i)) {
241
+ if (o.has(n.type)) throw new p(`Task type "${n.type}" already used in project schema`, "project-schema-task-type-already-used", { type: n.type });
242
+ a[c] = L(n.type, n.options), r.properties.tasks.items.oneOf.push({ $ref: `#/$defs/${c}` }), o.add(n.type);
243
+ }
244
+ r.$defs = { ...a, ...r.$defs };
245
+ }
246
+ return r;
247
+ })(s, t.tasks)), t.actions && (s = (function(r, i) {
248
+ if (i) {
249
+ const o = new Set(r.$defs.Runium_Action.oneOf.map((c) => {
250
+ const n = c.$ref.split("/").pop() || "", u = r.$defs[n]?.properties?.type;
251
+ return u?.enum || [u?.const];
252
+ }).flat()), a = {};
253
+ for (const [c, n] of Object.entries(i)) {
254
+ if (o.has(n.type)) throw new p(`Action type "${n.type}" already used in project schema`, "project-schema-action-type-already-used", { type: n.type });
255
+ a[c] = ot(n.type, n.payload), r.$defs.Runium_Action.oneOf.push({ $ref: `#/$defs/${c}` }), o.add(n.type);
256
+ }
257
+ r.$defs = { ...a, ...r.$defs };
258
+ }
259
+ return r;
260
+ })(s, t.actions)), t.triggers && (s = (function(r, i) {
261
+ if (i) {
262
+ const o = new Set(r.$defs.Runium_Trigger.oneOf.map((c) => {
263
+ const n = c.$ref.split("/").pop() || "";
264
+ return r.$defs[n]?.properties?.type?.const;
265
+ })), a = {};
266
+ for (const [c, n] of Object.entries(i)) {
267
+ if (o.has(n.type)) throw new p(`Trigger type "${n.type}" already used in project schema`, "project-schema-trigger-type-already-used", { type: n.type });
268
+ a[c] = nt(n.type, n.payload), r.$defs.Runium_Trigger.oneOf.push({ $ref: `#/$defs/${c}` }), o.add(n.type);
269
+ }
270
+ r.$defs = { ...a, ...r.$defs };
271
+ }
272
+ return r;
273
+ })(s, t.triggers)), Object.freeze(s);
274
+ }
275
+ class R {
276
+ project;
277
+ id;
278
+ action;
279
+ disabled;
280
+ constructor(t, s) {
281
+ this.id = t.id, this.action = t.action, this.disabled = t.disabled ?? !1, this.project = s;
282
+ }
283
+ getId() {
284
+ return this.id;
285
+ }
286
+ isDisabled() {
287
+ return this.disabled;
288
+ }
289
+ }
290
+ class pt extends R {
291
+ event;
292
+ constructor(t, s) {
293
+ super(t, s), this.event = t.event;
294
+ }
295
+ enable() {
296
+ this.project.on(this.event, this.handler), this.disabled = !1;
297
+ }
298
+ disable() {
299
+ this.project.off(this.event, this.handler), this.disabled = !0;
300
+ }
301
+ handler = () => {
302
+ this.project.processAction(this.action);
303
+ };
304
+ }
305
+ class dt extends R {
306
+ interval;
307
+ intervalId = null;
308
+ constructor(t, s) {
309
+ super(t, s), this.interval = t.interval;
310
+ }
311
+ enable() {
312
+ this.intervalId = setInterval(() => {
313
+ this.project.processAction(this.action);
314
+ }, this.interval), this.disabled = !1;
315
+ }
316
+ disable() {
317
+ this.intervalId && clearInterval(this.intervalId), this.disabled = !0, this.intervalId = null;
318
+ }
319
+ }
320
+ class ut extends R {
321
+ timeout;
322
+ timeoutId = null;
323
+ constructor(t, s) {
324
+ super(t, s), this.timeout = t.timeout;
325
+ }
326
+ enable() {
327
+ this.timeoutId = setTimeout(() => {
328
+ this.project.processAction(this.action);
329
+ }, this.timeout), this.disabled = !1;
330
+ }
331
+ disable() {
332
+ this.timeoutId && clearTimeout(this.timeoutId), this.disabled = !0, this.timeoutId = null;
333
+ }
334
+ }
335
+ var gt = ((e) => (e.STATE_CHANGE = "state-change", e.START_TASK = "start-task", e.RESTART_TASK = "restart-task", e.STOP_TASK = "stop-task", e.PROCESS_ACTION = "process-action", e.ENABLE_TRIGGER = "enable-trigger", e.DISABLE_TRIGGER = "disable-trigger", e.TASK_STATE_CHANGE = "task-state-change", e.TASK_STDOUT = "task-stdout", e.TASK_STDERR = "task-stderr", e))(gt || {}), ht = ((e) => (e.IDLE = "idle", e.STARTING = "starting", e.STARTED = "started", e.STOPPING = "stopping", e.STOPPED = "stopped", e))(ht || {}), lt = ((e) => (e.ACTION_PROCESSOR_ALREADY_REGISTERED = "project-action-processor-already-registered", e.ACTION_PROCESSOR_INCORRECT = "project-action-processor-incorrect", e.TASK_PROCESSOR_NOT_FOUND = "project-task-processor-not-found", e.TASK_PROCESSOR_ALREADY_REGISTERED = "project-task-processor-already-registered", e.TASK_PROCESSOR_INCORRECT = "project-task-processor-incorrect", e.TRIGGER_PROCESSOR_ALREADY_REGISTERED = "project-trigger-processor-already-registered", e.TRIGGER_PROCESSOR_INCORRECT = "project-trigger-processor-incorrect", e))(lt || {});
336
+ class wt extends v {
337
+ constructor(t) {
338
+ super(), this.config = t;
339
+ }
340
+ schema = at();
341
+ taskProcessors = /* @__PURE__ */ new Map([[T.DEFAULT, st]]);
342
+ actionProcessors = /* @__PURE__ */ new Map();
343
+ triggerProcessors = /* @__PURE__ */ new Map();
344
+ tasks = /* @__PURE__ */ new Map();
345
+ triggers = /* @__PURE__ */ new Map();
346
+ state = { timestamp: Date.now(), status: "idle" };
347
+ validate() {
348
+ et(this.config, this.schema);
349
+ }
350
+ getConfig() {
351
+ return { ...this.config };
352
+ }
353
+ setConfig(t) {
354
+ this.config = { ...t };
355
+ }
356
+ getState() {
357
+ return { ...this.state };
358
+ }
359
+ async start() {
360
+ if (this.state.status !== "idle" && this.state.status !== "stopped") return;
361
+ this.validate(), this.initTasks(), this.initTriggers(), this.updateState({ status: "starting" });
362
+ const t = this.getTasksStartOrder();
363
+ for (const s of t) if (this.tasks.has(s)) {
364
+ const { instance: r, config: i } = this.tasks.get(s), { mode: o = f.IMMEDIATE, dependencies: a = [] } = i;
365
+ o === f.IMMEDIATE && a.length === 0 && r.start();
366
+ }
367
+ this.updateState({ status: "started" });
368
+ }
369
+ async stop(t = "") {
370
+ if (this.state.status !== "started" && this.state.status !== "starting") return;
371
+ this.updateState({ status: "stopping", reason: t }), this.cleanupTriggers();
372
+ const s = this.getTasksStartOrder().reverse(), r = [];
373
+ for (const i of s) if (this.tasks.has(i)) {
374
+ const { instance: o } = this.tasks.get(i), { status: a } = o.getState();
375
+ a === h.STARTED && r.push(o.stop("project-stop"));
376
+ }
377
+ await Promise.allSettled(r), this.updateState({ status: "stopped" });
378
+ }
379
+ extendValidationSchema(t) {
380
+ this.schema = ct(this.schema, t);
381
+ }
382
+ registerAction(t, s) {
383
+ if (this.actionProcessors.has(t)) throw new p(`Action processor for type "${t}" already registered`, "project-action-processor-already-registered", { type: t });
384
+ if (typeof s != "function") throw new p(`Action processor for type "${t}" must be a function`, "project-action-processor-incorrect", { type: t });
385
+ this.actionProcessors.set(t, s);
386
+ }
387
+ registerTask(t, s) {
388
+ if (this.taskProcessors.has(t)) throw new p(`Task processor for type "${t}" already registered`, "project-task-processor-already-registered", { type: t });
389
+ if (!(s.prototype instanceof D)) throw new p(`Task processor for type "${t}" must be a subclass of "RuniumTask"`, "project-task-processor-incorrect", { type: t });
390
+ this.taskProcessors.set(t, s);
391
+ }
392
+ registerTrigger(t, s) {
393
+ if (this.triggerProcessors.has(t)) throw new p(`Trigger processor for type "${t}" already registered`, "project-trigger-processor-already-registered", { type: t });
394
+ if (!(s.prototype instanceof R)) throw new p(`Trigger processor for type "${t}" must be a subclass of "RuniumTrigger"`, "project-trigger-processor-incorrect", { type: t });
395
+ this.triggerProcessors.set(t, s);
396
+ }
397
+ updateState(t) {
398
+ this.state = { ...this.state, ...t, timestamp: Date.now() }, this.emit("state-change", this.getState()), this.emit(this.state.status, this.getState());
399
+ }
400
+ initTasks() {
401
+ this.tasks.clear();
402
+ for (const t of this.config.tasks) {
403
+ const s = t.type || T.DEFAULT, r = this.taskProcessors.get(s);
404
+ if (!r) throw new p(`Task processor for type "${s}" not found`, "project-task-processor-not-found", { type: s });
405
+ const i = new r(t.options);
406
+ i.on(k.STATE_CHANGE, (o) => {
407
+ this.emit("task-state-change", t.id, o), this.onTaskStateChange(t.id, o);
408
+ }), i.on(k.STDOUT, (o) => {
409
+ this.emit("task-stdout", t.id, o);
410
+ }), i.on(k.STDERR, (o) => {
411
+ this.emit("task-stderr", t.id, o);
412
+ }), this.tasks.set(t.id, { instance: i, config: t, dependencies: [...t.dependencies || []], dependents: null });
413
+ }
414
+ }
415
+ getTasksStartOrder() {
416
+ const t = [], s = /* @__PURE__ */ new Set(), r = (i) => {
417
+ if (s.has(i)) return;
418
+ s.add(i);
419
+ const { dependencies: o = [] } = this.tasks.get(i) || {};
420
+ for (const a of o) this.tasks.has(a.taskId) && r(a.taskId);
421
+ t.push(i);
422
+ };
423
+ for (const i of this.tasks.keys()) r(i);
424
+ return t;
425
+ }
426
+ onTaskStateChange(t, s) {
427
+ const { config: r, instance: i } = this.tasks.get(t), o = this.getDependentTasks(t);
428
+ for (const a of o) this.isDependentTaskReady(a) && this.startTask(a);
429
+ if (s.status === h.COMPLETED || s.status === h.FAILED) {
430
+ const { restart: a } = r;
431
+ if (a && s.exitCode !== -1) {
432
+ const { policy: c } = a;
433
+ if (c === m.ALWAYS || c === m.ON_FAILURE && s.exitCode !== 0) {
434
+ const { maxRetries: n = 1 / 0, delay: u = 0 } = a;
435
+ s.iteration <= n && setTimeout(i.restart.bind(i), u);
436
+ }
437
+ }
438
+ }
439
+ (r.handlers || []).forEach((a) => {
440
+ this.checkTaskStateCondition(a.condition, s) && this.processAction(a.action);
441
+ });
442
+ }
443
+ checkTaskStateCondition(t, s) {
444
+ return typeof t == "string" ? q.evaluate(t, s) === !0 : typeof t == "object" ? Object.entries(t).every(([r, i]) => s[r] === i) : typeof t == "boolean" && t;
445
+ }
446
+ async startTask(t) {
447
+ if (this.tasks.has(t)) {
448
+ const { instance: s } = this.tasks.get(t), { status: r } = s.getState();
449
+ if (r !== h.STARTED && r !== h.STARTING && r !== h.STOPPING) return this.emit("start-task", t), s.start();
450
+ }
451
+ }
452
+ async stopTask(t) {
453
+ if (this.tasks.has(t)) {
454
+ const { instance: s } = this.tasks.get(t), { status: r } = s.getState();
455
+ if (r === h.STARTED) return this.emit("stop-task", t), s.stop("action-stop");
456
+ }
457
+ }
458
+ async restartTask(t) {
459
+ if (this.tasks.has(t)) {
460
+ const { instance: s } = this.tasks.get(t);
461
+ return this.emit("restart-task", t), s.restart();
462
+ }
463
+ }
464
+ getTaskState(t) {
465
+ if (this.tasks.has(t)) {
466
+ const { instance: s } = this.tasks.get(t);
467
+ return s.getState();
468
+ }
469
+ return null;
470
+ }
471
+ isDependentTaskReady(t) {
472
+ if (this.tasks.has(t)) {
473
+ const { dependencies: s = [], config: r } = this.tasks.get(t);
474
+ if (r.mode === f.IGNORE) return !1;
475
+ for (const { taskId: i, condition: o } of s) {
476
+ const { instance: a } = this.tasks.get(i);
477
+ if (!this.checkTaskStateCondition(o, a.getState())) return !1;
478
+ }
479
+ return !0;
480
+ }
481
+ return !1;
482
+ }
483
+ getDependentTasks(t) {
484
+ let s = [];
485
+ if (this.tasks.has(t)) {
486
+ const { dependents: r } = this.tasks.get(t);
487
+ if (r) s = r;
488
+ else {
489
+ for (const [i, { dependencies: o }] of this.tasks) for (const a of o) a.taskId === t && s.push(i);
490
+ this.tasks.get(t).dependents = s;
491
+ }
492
+ }
493
+ return s;
494
+ }
495
+ processAction(t) {
496
+ if (this.emit("process-action", t), Z(t)) {
497
+ const s = this.actionProcessors.get(t.type);
498
+ s && s(t.payload || {});
499
+ } else switch (t.type) {
500
+ case d.START_TASK:
501
+ this.startTask(t.taskId);
502
+ break;
503
+ case d.RESTART_TASK:
504
+ this.restartTask(t.taskId);
505
+ break;
506
+ case d.STOP_TASK:
507
+ this.stopTask(t.taskId);
508
+ break;
509
+ case d.EMIT_EVENT:
510
+ this.emit(t.event);
511
+ break;
512
+ case d.STOP_PROJECT:
513
+ this.stop("action");
514
+ break;
515
+ case d.ENABLE_TRIGGER:
516
+ this.enableTrigger(t.triggerId);
517
+ break;
518
+ case d.DISABLE_TRIGGER:
519
+ this.disableTrigger(t.triggerId);
520
+ }
521
+ }
522
+ initTriggers() {
523
+ const t = { processAction: this.processAction.bind(this), on: this.on.bind(this), off: this.off.bind(this) };
524
+ let s = null;
525
+ for (const r of this.config.triggers || []) {
526
+ if (Q(r)) {
527
+ const i = this.triggerProcessors.get(r.type);
528
+ i && (s = new i(r, t));
529
+ } else switch (r.type) {
530
+ case l.EVENT:
531
+ s = new pt(r, t);
532
+ break;
533
+ case l.INTERVAL:
534
+ s = new dt(r, t);
535
+ break;
536
+ case l.TIMEOUT:
537
+ s = new ut(r, t);
538
+ }
539
+ s && (this.triggers.set(r.id, s), r.disabled !== !0 && s.enable());
540
+ }
541
+ }
542
+ cleanupTriggers() {
543
+ for (const t in this.triggers) {
544
+ const s = this.triggers.get(t);
545
+ s && s.disable();
546
+ }
547
+ this.triggers.clear();
548
+ }
549
+ enableTrigger(t) {
550
+ const s = this.triggers.get(t);
551
+ s && (this.emit("enable-trigger", t), s.enable());
552
+ }
553
+ disableTrigger(t) {
554
+ const s = this.triggers.get(t);
555
+ s && (this.emit("disable-trigger", t), s.disable());
556
+ }
557
+ }
558
+ export {
559
+ pt as EventTrigger,
560
+ Y as FileErrorCode,
561
+ $ as ID_REGEX,
562
+ dt as IntervalTrigger,
563
+ J as MacrosErrorCode,
564
+ wt as Project,
565
+ d as ProjectActionType,
566
+ B as ProjectConfigErrorCode,
567
+ lt as ProjectErrorCode,
568
+ gt as ProjectEvent,
569
+ rt as ProjectSchemaErrorCode,
570
+ ht as ProjectStatus,
571
+ m as ProjectTaskRestartPolicyType,
572
+ f as ProjectTaskStartMode,
573
+ T as ProjectTaskType,
574
+ l as ProjectTriggerType,
575
+ p as RuniumError,
576
+ D as RuniumTask,
577
+ R as RuniumTrigger,
578
+ jt as SILENT_EXIT_CODE,
579
+ st as Task,
580
+ k as TaskEvent,
581
+ h as TaskStatus,
582
+ ut as TimeoutTrigger,
583
+ $t as applyMacros,
584
+ ct as extendProjectSchema,
585
+ at as getProjectSchema,
586
+ Z as isCustomAction,
587
+ Q as isCustomTrigger,
588
+ b as isProcessTaskAction,
589
+ At as isRuniumError,
590
+ O as isToggleTriggerAction,
591
+ _t as readJsonFile,
592
+ et as validateProject,
593
+ It as writeJsonFile
594
+ };
package/package.json CHANGED
@@ -1,35 +1,23 @@
1
1
  {
2
2
  "name": "@runium/core",
3
- "version": "0.0.9",
3
+ "version": "0.1.1",
4
4
  "description": "Runium Core",
5
5
  "author": "TheBeastApp",
6
6
  "license": "MIT",
7
7
  "type": "module",
8
- "scripts": {
9
- "build": "vite build",
10
- "lint": "eslint ./src --ext .ts",
11
- "lint:fix": "eslint ./src --ext .ts --fix",
12
- "format": "prettier --write \"src/**/*.ts\""
8
+ "main": "./index.js",
9
+ "module": "./index.js",
10
+ "files": [
11
+ "index.js"
12
+ ],
13
+ "exports": {
14
+ ".": {
15
+ "import": "./index.js"
16
+ }
13
17
  },
14
18
  "dependencies": {
15
19
  "ajv": "^8.17.1",
16
20
  "ajv-keywords": "^5.1.0",
17
21
  "bcx-expression-evaluator": "^1.2.1"
18
- },
19
- "devDependencies": {
20
- "@types/node": "^22.18.0",
21
- "@typescript-eslint/eslint-plugin": "^7.0.0",
22
- "@typescript-eslint/parser": "^7.0.0",
23
- "eslint": "^8.56.0",
24
- "eslint-config-prettier": "^9.1.0",
25
- "eslint-plugin-prettier": "^5.1.3",
26
- "prettier": "^3.2.5",
27
- "rollup-plugin-terser": "^7.0.2",
28
- "terser": "^5.44.0",
29
- "ts-node": "^10.9.2",
30
- "typescript": "^5.3.3",
31
- "vite": "^7.1.12",
32
- "vite-plugin-dts": "^4.5.4",
33
- "vite-plugin-externalize-deps": "^0.10.0"
34
22
  }
35
- }
23
+ }
package/.eslintrc.json DELETED
@@ -1,30 +0,0 @@
1
- {
2
- "env": {
3
- "node": true,
4
- "es2022": true
5
- },
6
- "extends": [
7
- "eslint:recommended",
8
- "plugin:@typescript-eslint/recommended",
9
- "plugin:prettier/recommended"
10
- ],
11
- "parser": "@typescript-eslint/parser",
12
- "parserOptions": {
13
- "ecmaVersion": 2022,
14
- "sourceType": "module"
15
- },
16
- "plugins": ["@typescript-eslint", "prettier"],
17
- "rules": {
18
- "prettier/prettier": "error",
19
- "@typescript-eslint/no-unused-vars": [
20
- "error",
21
- {
22
- "argsIgnorePattern": "^_",
23
- "varsIgnorePattern": "^_"
24
- }
25
- ],
26
- "@typescript-eslint/no-explicit-any": "warn",
27
- "no-console": "warn"
28
- },
29
- "ignorePatterns": ["dist/", "node_modules/"]
30
- }