@almadar/runtime 6.19.0 → 6.21.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/{OrbitalServerRuntime-TewGACFH.d.ts → OrbitalServerRuntime-CO0rEx3p.d.ts} +0 -5
- package/dist/OrbitalServerRuntime.d.ts +1 -1
- package/dist/OrbitalServerRuntime.js +19 -28
- package/dist/OrbitalServerRuntime.js.map +1 -1
- package/dist/{chunk-JZ4IDIUU.js → chunk-53VRCHPK.js} +81 -4
- package/dist/chunk-53VRCHPK.js.map +1 -0
- package/dist/chunk-U4PL237A.js +51 -0
- package/dist/chunk-U4PL237A.js.map +1 -0
- package/dist/createOsHandlers.js +2 -36
- package/dist/createOsHandlers.js.map +1 -1
- package/dist/index.d.ts +56 -3
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-JZ4IDIUU.js.map +0 -1
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// src/cron.ts
|
|
2
|
+
function parseCronField(field, min, max) {
|
|
3
|
+
const values = /* @__PURE__ */ new Set();
|
|
4
|
+
for (const part of field.split(",")) {
|
|
5
|
+
if (part === "*") {
|
|
6
|
+
for (let i = min; i <= max; i++) values.add(i);
|
|
7
|
+
} else if (part.includes("/")) {
|
|
8
|
+
const [range, stepStr] = part.split("/");
|
|
9
|
+
const step = parseInt(stepStr, 10);
|
|
10
|
+
const start = range === "*" ? min : parseInt(range, 10);
|
|
11
|
+
for (let i = start; i <= max; i += step) values.add(i);
|
|
12
|
+
} else if (part.includes("-")) {
|
|
13
|
+
const [lo, hi] = part.split("-").map(Number);
|
|
14
|
+
for (let i = lo; i <= hi; i++) values.add(i);
|
|
15
|
+
} else {
|
|
16
|
+
values.add(parseInt(part, 10));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return values;
|
|
20
|
+
}
|
|
21
|
+
function parseCron(expression) {
|
|
22
|
+
const parts = expression.trim().split(/\s+/);
|
|
23
|
+
if (parts.length !== 5) {
|
|
24
|
+
throw new Error(`Invalid cron expression (expected 5 fields): ${expression}`);
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
minute: parseCronField(parts[0], 0, 59),
|
|
28
|
+
hour: parseCronField(parts[1], 0, 23),
|
|
29
|
+
day: parseCronField(parts[2], 1, 31),
|
|
30
|
+
month: parseCronField(parts[3], 1, 12),
|
|
31
|
+
weekday: parseCronField(parts[4], 0, 6)
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function isValidCronExpression(expression) {
|
|
35
|
+
try {
|
|
36
|
+
parseCron(expression);
|
|
37
|
+
return true;
|
|
38
|
+
} catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function cronMatches(fields, date) {
|
|
43
|
+
return fields.minute.has(date.getMinutes()) && fields.hour.has(date.getHours()) && fields.day.has(date.getDate()) && fields.month.has(date.getMonth() + 1) && fields.weekday.has(date.getDay());
|
|
44
|
+
}
|
|
45
|
+
function cronMinuteKey(date) {
|
|
46
|
+
return date.getFullYear() * 1e8 + date.getMonth() * 1e6 + date.getDate() * 1e4 + date.getHours() * 100 + date.getMinutes();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export { cronMatches, cronMinuteKey, isValidCronExpression, parseCron, parseCronField };
|
|
50
|
+
//# sourceMappingURL=chunk-U4PL237A.js.map
|
|
51
|
+
//# sourceMappingURL=chunk-U4PL237A.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cron.ts"],"names":[],"mappings":"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","file":"chunk-U4PL237A.js","sourcesContent":["/**\n * Cron expression parsing and matching — the ONE canonical implementation\n * every engine that schedules a cron-shaped interval delegates to (the\n * `os/watch-cron` effect handler in `createOsHandlers.ts`, `OrbitalServerRuntime`,\n * `TickScheduler`, and generated client codegen). No Node.js dependencies, so\n * it's safe to import client-side too.\n *\n * @packageDocumentation\n */\n\nexport interface CronFields {\n minute: Set<number>;\n hour: Set<number>;\n day: Set<number>;\n month: Set<number>;\n weekday: Set<number>;\n}\n\nexport function parseCronField(field: string, min: number, max: number): Set<number> {\n const values = new Set<number>();\n for (const part of field.split(\",\")) {\n if (part === \"*\") {\n for (let i = min; i <= max; i++) values.add(i);\n } else if (part.includes(\"/\")) {\n const [range, stepStr] = part.split(\"/\");\n const step = parseInt(stepStr, 10);\n const start = range === \"*\" ? min : parseInt(range, 10);\n for (let i = start; i <= max; i += step) values.add(i);\n } else if (part.includes(\"-\")) {\n const [lo, hi] = part.split(\"-\").map(Number);\n for (let i = lo; i <= hi; i++) values.add(i);\n } else {\n values.add(parseInt(part, 10));\n }\n }\n return values;\n}\n\nexport function parseCron(expression: string): CronFields {\n const parts = expression.trim().split(/\\s+/);\n if (parts.length !== 5) {\n throw new Error(`Invalid cron expression (expected 5 fields): ${expression}`);\n }\n return {\n minute: parseCronField(parts[0], 0, 59),\n hour: parseCronField(parts[1], 0, 23),\n day: parseCronField(parts[2], 1, 31),\n month: parseCronField(parts[3], 1, 12),\n weekday: parseCronField(parts[4], 0, 6),\n };\n}\n\n/** Returns true if `expression` is a syntactically valid 5-field cron expression. */\nexport function isValidCronExpression(expression: string): boolean {\n try {\n parseCron(expression);\n return true;\n } catch {\n return false;\n }\n}\n\nexport function cronMatches(fields: CronFields, date: Date): boolean {\n return (\n fields.minute.has(date.getMinutes()) &&\n fields.hour.has(date.getHours()) &&\n fields.day.has(date.getDate()) &&\n fields.month.has(date.getMonth() + 1) &&\n fields.weekday.has(date.getDay())\n );\n}\n\n/** Calendar-minute key for once-per-minute dedup — the same shape `os/watch-cron` uses. */\nexport function cronMinuteKey(date: Date): number {\n return (\n date.getFullYear() * 1e8 +\n date.getMonth() * 1e6 +\n date.getDate() * 1e4 +\n date.getHours() * 100 +\n date.getMinutes()\n );\n}\n"]}
|
package/dist/createOsHandlers.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { parseCron, cronMinuteKey, cronMatches } from './chunk-U4PL237A.js';
|
|
1
2
|
import './chunk-PZ5AY32C.js';
|
|
2
3
|
import * as fs from 'fs';
|
|
3
4
|
import * as net from 'net';
|
|
@@ -33,41 +34,6 @@ function globToRegex(glob) {
|
|
|
33
34
|
}
|
|
34
35
|
return new RegExp("^" + regex + "$");
|
|
35
36
|
}
|
|
36
|
-
function parseCronField(field, min, max) {
|
|
37
|
-
const values = /* @__PURE__ */ new Set();
|
|
38
|
-
for (const part of field.split(",")) {
|
|
39
|
-
if (part === "*") {
|
|
40
|
-
for (let i = min; i <= max; i++) values.add(i);
|
|
41
|
-
} else if (part.includes("/")) {
|
|
42
|
-
const [range, stepStr] = part.split("/");
|
|
43
|
-
const step = parseInt(stepStr, 10);
|
|
44
|
-
const start = range === "*" ? min : parseInt(range, 10);
|
|
45
|
-
for (let i = start; i <= max; i += step) values.add(i);
|
|
46
|
-
} else if (part.includes("-")) {
|
|
47
|
-
const [lo, hi] = part.split("-").map(Number);
|
|
48
|
-
for (let i = lo; i <= hi; i++) values.add(i);
|
|
49
|
-
} else {
|
|
50
|
-
values.add(parseInt(part, 10));
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return values;
|
|
54
|
-
}
|
|
55
|
-
function parseCron(expression) {
|
|
56
|
-
const parts = expression.trim().split(/\s+/);
|
|
57
|
-
if (parts.length !== 5) {
|
|
58
|
-
throw new Error(`Invalid cron expression (expected 5 fields): ${expression}`);
|
|
59
|
-
}
|
|
60
|
-
return {
|
|
61
|
-
minute: parseCronField(parts[0], 0, 59),
|
|
62
|
-
hour: parseCronField(parts[1], 0, 23),
|
|
63
|
-
day: parseCronField(parts[2], 1, 31),
|
|
64
|
-
month: parseCronField(parts[3], 1, 12),
|
|
65
|
-
weekday: parseCronField(parts[4], 0, 6)
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
function cronMatches(fields, date) {
|
|
69
|
-
return fields.minute.has(date.getMinutes()) && fields.hour.has(date.getHours()) && fields.day.has(date.getDate()) && fields.month.has(date.getMonth() + 1) && fields.weekday.has(date.getDay());
|
|
70
|
-
}
|
|
71
37
|
function createOsHandlers(ctx) {
|
|
72
38
|
const cwd = ctx.cwd ?? process.cwd();
|
|
73
39
|
const watchers = [];
|
|
@@ -209,7 +175,7 @@ function createOsHandlers(ctx) {
|
|
|
209
175
|
const eventName = resolveOnMessage(emit, "OS_CRON_FIRE");
|
|
210
176
|
const interval = setInterval(() => {
|
|
211
177
|
const now = /* @__PURE__ */ new Date();
|
|
212
|
-
const minuteKey =
|
|
178
|
+
const minuteKey = cronMinuteKey(now);
|
|
213
179
|
if (minuteKey !== lastFired && cronMatches(fields, now)) {
|
|
214
180
|
lastFired = minuteKey;
|
|
215
181
|
debouncedEmit(eventName, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/createOsHandlers.ts"],"names":[],"mappings":"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","file":"createOsHandlers.js","sourcesContent":["/**\n * OS Trigger Handlers — Server-Side Only\n *\n * Provides Node.js implementations for all 8 os/* operators.\n * Used by OrbitalServerRuntime (interpreted path).\n *\n * NOT exported from the main index.ts because it imports Node.js-only modules.\n * Import directly: import { createOsHandlers } from '@almadar/runtime/createOsHandlers';\n *\n * @packageDocumentation\n */\n\nimport * as fs from \"fs\";\nimport * as net from \"net\";\nimport { execFileSync } from \"child_process\";\nimport { createLogger } from '@almadar/logger';\nimport type { EventPayload, OsEmitConfig } from './types.js';\nimport type { EffectHandlers } from \"./types.js\";\n\nconst log = createLogger('almadar:runtime:os-handlers');\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface OsHandlerContext {\n /** Emit an event on the EventBus */\n emitEvent: (type: string, payload: EventPayload) => void;\n /** Working directory for file watching (defaults to process.cwd()) */\n cwd?: string;\n}\n\nexport interface OsHandlerResult {\n handlers: Partial<EffectHandlers>;\n cleanup: () => void;\n}\n\n// ============================================================================\n// Glob Matching (minimal, no external dependency)\n// ============================================================================\n\nfunction globToRegex(glob: string): RegExp {\n let regex = \"\";\n let i = 0;\n while (i < glob.length) {\n const c = glob[i];\n if (c === \"*\") {\n if (glob[i + 1] === \"*\") {\n // ** matches any path segment\n regex += \".*\";\n i += 2;\n if (glob[i] === \"/\") i++; // skip trailing slash\n continue;\n }\n // * matches anything except /\n regex += \"[^/]*\";\n } else if (c === \"?\") {\n regex += \"[^/]\";\n } else if (c === \".\") {\n regex += \"\\\\.\";\n } else if (c === \"/\" || c === \"-\" || c === \"_\") {\n regex += c;\n } else if (/[{}()[\\]^$+|\\\\]/.test(c)) {\n regex += \"\\\\\" + c;\n } else {\n regex += c;\n }\n i++;\n }\n return new RegExp(\"^\" + regex + \"$\");\n}\n\n// ============================================================================\n// Cron Parsing (5-field standard: min hour day month weekday)\n// ============================================================================\n\ninterface CronFields {\n minute: Set<number>;\n hour: Set<number>;\n day: Set<number>;\n month: Set<number>;\n weekday: Set<number>;\n}\n\nfunction parseCronField(field: string, min: number, max: number): Set<number> {\n const values = new Set<number>();\n for (const part of field.split(\",\")) {\n if (part === \"*\") {\n for (let i = min; i <= max; i++) values.add(i);\n } else if (part.includes(\"/\")) {\n const [range, stepStr] = part.split(\"/\");\n const step = parseInt(stepStr, 10);\n const start = range === \"*\" ? min : parseInt(range, 10);\n for (let i = start; i <= max; i += step) values.add(i);\n } else if (part.includes(\"-\")) {\n const [lo, hi] = part.split(\"-\").map(Number);\n for (let i = lo; i <= hi; i++) values.add(i);\n } else {\n values.add(parseInt(part, 10));\n }\n }\n return values;\n}\n\nfunction parseCron(expression: string): CronFields {\n const parts = expression.trim().split(/\\s+/);\n if (parts.length !== 5) {\n throw new Error(`Invalid cron expression (expected 5 fields): ${expression}`);\n }\n return {\n minute: parseCronField(parts[0], 0, 59),\n hour: parseCronField(parts[1], 0, 23),\n day: parseCronField(parts[2], 1, 31),\n month: parseCronField(parts[3], 1, 12),\n weekday: parseCronField(parts[4], 0, 6),\n };\n}\n\nfunction cronMatches(fields: CronFields, date: Date): boolean {\n return (\n fields.minute.has(date.getMinutes()) &&\n fields.hour.has(date.getHours()) &&\n fields.day.has(date.getDate()) &&\n fields.month.has(date.getMonth() + 1) &&\n fields.weekday.has(date.getDay())\n );\n}\n\n// ============================================================================\n// Factory\n// ============================================================================\n\nexport function createOsHandlers(ctx: OsHandlerContext): OsHandlerResult {\n const cwd = ctx.cwd ?? process.cwd();\n\n // Resource tracking for cleanup\n const watchers: fs.FSWatcher[] = [];\n const intervals: ReturnType<typeof setInterval>[] = [];\n const signalHandlers: Array<{ signal: NodeJS.Signals; handler: () => void }> = [];\n let httpWatchActive = false;\n\n // Debounce configuration: { eventType: ms }\n const debounceConfig = new Map<string, number>();\n const debounceTimers = new Map<string, ReturnType<typeof setTimeout>>();\n\n function debouncedEmit(eventType: string, payload: EventPayload): void {\n const ms = debounceConfig.get(eventType);\n if (ms !== undefined && ms > 0) {\n const existing = debounceTimers.get(eventType);\n if (existing) clearTimeout(existing);\n debounceTimers.set(\n eventType,\n setTimeout(() => {\n debounceTimers.delete(eventType);\n ctx.emitEvent(eventType, payload);\n }, ms),\n );\n } else {\n ctx.emitEvent(eventType, payload);\n }\n }\n\n // ============================================================================\n // Handler Implementations\n // ============================================================================\n\n // When an author sets `emit: { on_message: \"X\" }` on an os/watch-* effect,\n // we swap the hardcoded default event name for X. Null-safe: absent emit\n // config preserves the legacy names so existing schemas keep working.\n const resolveOnMessage = (emit: OsEmitConfig | undefined, fallback: string): string =>\n emit?.on_message ?? fallback;\n\n const handlers: Partial<EffectHandlers> = {\n osWatchFiles: (\n glob: string,\n options: { recursive?: boolean; debounce?: number },\n emit?: OsEmitConfig,\n ) => {\n const recursive = (options.recursive as boolean) !== false;\n const pattern = globToRegex(glob);\n const eventName = resolveOnMessage(emit, \"OS_FILE_MODIFIED\");\n\n try {\n const watcher = fs.watch(cwd, { recursive }, (_event, filename) => {\n if (filename && pattern.test(filename)) {\n debouncedEmit(eventName, {\n file: filename,\n glob,\n cwd,\n });\n }\n });\n watchers.push(watcher);\n } catch (err) {\n if (emit?.failure) {\n ctx.emitEvent(emit.failure, {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n log.warn('watch-files-failed-to-start', { error: err instanceof Error ? err : String(err) });\n }\n },\n\n osWatchProcess: (name: string, subcommand?: string, emit?: OsEmitConfig) => {\n const searchTerm = subcommand ? `${name} ${subcommand}` : name;\n // execFileSync below runs pgrep with no shell, so searchTerm can never inject\n // a command; this guard additionally rejects pathological / metacharacter input.\n if (!/^[\\w .\\-/:@]+$/.test(searchTerm)) {\n log.warn('watch-process-invalid-name', { searchTerm });\n return;\n }\n let wasRunning = false;\n // Both start + exit transitions share one event name when emit.on_message\n // is configured (consumers discriminate on the payload's `process` field).\n const startEvent = resolveOnMessage(emit, \"OS_PROCESS_STARTED\");\n const exitEvent = resolveOnMessage(emit, \"OS_PROCESS_EXITED\");\n\n const interval = setInterval(() => {\n let isRunning = false;\n try {\n const result = execFileSync(\"pgrep\", [\"-f\", searchTerm], {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n isRunning = result.trim().length > 0;\n } catch {\n isRunning = false;\n }\n\n if (isRunning && !wasRunning) {\n debouncedEmit(startEvent, { process: name, subcommand: subcommand ?? null });\n } else if (!isRunning && wasRunning) {\n debouncedEmit(exitEvent, { process: name, subcommand: subcommand ?? null });\n }\n wasRunning = isRunning;\n }, 2000);\n\n intervals.push(interval);\n },\n\n osWatchPort: (port: number, protocol: string, emit?: OsEmitConfig) => {\n if (protocol !== \"tcp\") {\n log.warn('watch-port-only-tcp-supported', { protocol });\n return;\n }\n\n let wasOpen = false;\n const openEvent = resolveOnMessage(emit, \"OS_PORT_OPENED\");\n const closeEvent = resolveOnMessage(emit, \"OS_PORT_CLOSED\");\n\n const interval = setInterval(() => {\n const socket = new net.Socket();\n socket.setTimeout(1000);\n\n socket.on(\"connect\", () => {\n socket.destroy();\n if (!wasOpen) {\n wasOpen = true;\n debouncedEmit(openEvent, { port, protocol });\n }\n });\n\n socket.on(\"error\", () => {\n socket.destroy();\n if (wasOpen) {\n wasOpen = false;\n debouncedEmit(closeEvent, { port, protocol });\n }\n });\n\n socket.on(\"timeout\", () => {\n socket.destroy();\n if (wasOpen) {\n wasOpen = false;\n debouncedEmit(closeEvent, { port, protocol });\n }\n });\n\n socket.connect(port, \"127.0.0.1\");\n }, 3000);\n\n intervals.push(interval);\n },\n\n osWatchHttp: (urlPattern: string, method?: string, _emit?: OsEmitConfig) => {\n // HTTP interception requires monkey-patching Node.js module exports (read-only in TS types).\n // The compiled path (backend.rs) generates untyped inline code that handles this.\n // For the interpreted runtime, log a warning.\n if (!httpWatchActive) {\n httpWatchActive = true;\n log.warn('watch-http-compiled-only', {\n pattern: urlPattern,\n method: method ?? null,\n });\n }\n },\n\n osWatchCron: (expression: string, emit?: OsEmitConfig) => {\n let fields: CronFields;\n try {\n fields = parseCron(expression);\n } catch (err) {\n if (emit?.failure) {\n ctx.emitEvent(emit.failure, {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n log.warn('watch-cron-invalid-expression', { error: err instanceof Error ? err : String(err) });\n return;\n }\n\n let lastFired = -1;\n const eventName = resolveOnMessage(emit, \"OS_CRON_FIRE\");\n\n const interval = setInterval(() => {\n const now = new Date();\n const minuteKey = now.getFullYear() * 1e8 + now.getMonth() * 1e6 +\n now.getDate() * 1e4 + now.getHours() * 100 + now.getMinutes();\n\n if (minuteKey !== lastFired && cronMatches(fields, now)) {\n lastFired = minuteKey;\n debouncedEmit(eventName, {\n expression,\n firedAt: now.toISOString(),\n });\n }\n }, 1000);\n\n intervals.push(interval);\n },\n\n osWatchSignal: (signal: string, emit?: OsEmitConfig) => {\n const sig = signal.toUpperCase() as NodeJS.Signals;\n // Default name keeps the per-signal suffix (OS_SIGNAL_TERM); a\n // configured on_message drops that convention in favor of the single\n // author-chosen event.\n const handler = () => {\n const eventName = emit?.on_message ?? `OS_SIGNAL_${sig}`;\n debouncedEmit(eventName, { signal: sig });\n };\n\n try {\n process.on(sig, handler);\n signalHandlers.push({ signal: sig, handler });\n } catch (err) {\n if (emit?.failure) {\n ctx.emitEvent(emit.failure, {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n log.warn('watch-signal-cannot-listen', { signal: sig, error: err instanceof Error ? err : String(err) });\n }\n },\n\n osWatchEnv: (variable: string, emit?: OsEmitConfig) => {\n let lastValue = process.env[variable];\n const eventName = resolveOnMessage(emit, \"OS_ENV_CHANGED\");\n\n const interval = setInterval(() => {\n const current = process.env[variable];\n if (current !== lastValue) {\n const previous = lastValue;\n lastValue = current;\n debouncedEmit(eventName, {\n variable,\n value: current ?? null,\n previous: previous ?? null,\n });\n }\n }, 1000);\n\n intervals.push(interval);\n },\n\n osDebounce: (ms: number, eventType: string) => {\n debounceConfig.set(eventType, ms);\n },\n };\n\n // ============================================================================\n // Cleanup\n // ============================================================================\n\n function cleanup(): void {\n for (const w of watchers) {\n try { w.close(); } catch { /* already closed */ }\n }\n watchers.length = 0;\n\n for (const i of intervals) {\n clearInterval(i);\n }\n intervals.length = 0;\n\n for (const { signal, handler } of signalHandlers) {\n try { process.removeListener(signal, handler); } catch { /* noop */ }\n }\n signalHandlers.length = 0;\n\n httpWatchActive = false;\n\n // Clear pending debounce timers\n for (const timer of debounceTimers.values()) {\n clearTimeout(timer);\n }\n debounceTimers.clear();\n }\n\n return { handlers, cleanup };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/createOsHandlers.ts"],"names":[],"mappings":"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","file":"createOsHandlers.js","sourcesContent":["/**\n * OS Trigger Handlers — Server-Side Only\n *\n * Provides Node.js implementations for all 8 os/* operators.\n * Used by OrbitalServerRuntime (interpreted path).\n *\n * NOT exported from the main index.ts because it imports Node.js-only modules.\n * Import directly: import { createOsHandlers } from '@almadar/runtime/createOsHandlers';\n *\n * @packageDocumentation\n */\n\nimport * as fs from \"fs\";\nimport * as net from \"net\";\nimport { execFileSync } from \"child_process\";\nimport { createLogger } from '@almadar/logger';\nimport type { EventPayload, OsEmitConfig } from './types.js';\nimport type { EffectHandlers } from \"./types.js\";\nimport { parseCron, cronMatches, cronMinuteKey, type CronFields } from './cron.js';\n\nconst log = createLogger('almadar:runtime:os-handlers');\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface OsHandlerContext {\n /** Emit an event on the EventBus */\n emitEvent: (type: string, payload: EventPayload) => void;\n /** Working directory for file watching (defaults to process.cwd()) */\n cwd?: string;\n}\n\nexport interface OsHandlerResult {\n handlers: Partial<EffectHandlers>;\n cleanup: () => void;\n}\n\n// ============================================================================\n// Glob Matching (minimal, no external dependency)\n// ============================================================================\n\nfunction globToRegex(glob: string): RegExp {\n let regex = \"\";\n let i = 0;\n while (i < glob.length) {\n const c = glob[i];\n if (c === \"*\") {\n if (glob[i + 1] === \"*\") {\n // ** matches any path segment\n regex += \".*\";\n i += 2;\n if (glob[i] === \"/\") i++; // skip trailing slash\n continue;\n }\n // * matches anything except /\n regex += \"[^/]*\";\n } else if (c === \"?\") {\n regex += \"[^/]\";\n } else if (c === \".\") {\n regex += \"\\\\.\";\n } else if (c === \"/\" || c === \"-\" || c === \"_\") {\n regex += c;\n } else if (/[{}()[\\]^$+|\\\\]/.test(c)) {\n regex += \"\\\\\" + c;\n } else {\n regex += c;\n }\n i++;\n }\n return new RegExp(\"^\" + regex + \"$\");\n}\n\n// ============================================================================\n// Factory\n// ============================================================================\n\nexport function createOsHandlers(ctx: OsHandlerContext): OsHandlerResult {\n const cwd = ctx.cwd ?? process.cwd();\n\n // Resource tracking for cleanup\n const watchers: fs.FSWatcher[] = [];\n const intervals: ReturnType<typeof setInterval>[] = [];\n const signalHandlers: Array<{ signal: NodeJS.Signals; handler: () => void }> = [];\n let httpWatchActive = false;\n\n // Debounce configuration: { eventType: ms }\n const debounceConfig = new Map<string, number>();\n const debounceTimers = new Map<string, ReturnType<typeof setTimeout>>();\n\n function debouncedEmit(eventType: string, payload: EventPayload): void {\n const ms = debounceConfig.get(eventType);\n if (ms !== undefined && ms > 0) {\n const existing = debounceTimers.get(eventType);\n if (existing) clearTimeout(existing);\n debounceTimers.set(\n eventType,\n setTimeout(() => {\n debounceTimers.delete(eventType);\n ctx.emitEvent(eventType, payload);\n }, ms),\n );\n } else {\n ctx.emitEvent(eventType, payload);\n }\n }\n\n // ============================================================================\n // Handler Implementations\n // ============================================================================\n\n // When an author sets `emit: { on_message: \"X\" }` on an os/watch-* effect,\n // we swap the hardcoded default event name for X. Null-safe: absent emit\n // config preserves the legacy names so existing schemas keep working.\n const resolveOnMessage = (emit: OsEmitConfig | undefined, fallback: string): string =>\n emit?.on_message ?? fallback;\n\n const handlers: Partial<EffectHandlers> = {\n osWatchFiles: (\n glob: string,\n options: { recursive?: boolean; debounce?: number },\n emit?: OsEmitConfig,\n ) => {\n const recursive = (options.recursive as boolean) !== false;\n const pattern = globToRegex(glob);\n const eventName = resolveOnMessage(emit, \"OS_FILE_MODIFIED\");\n\n try {\n const watcher = fs.watch(cwd, { recursive }, (_event, filename) => {\n if (filename && pattern.test(filename)) {\n debouncedEmit(eventName, {\n file: filename,\n glob,\n cwd,\n });\n }\n });\n watchers.push(watcher);\n } catch (err) {\n if (emit?.failure) {\n ctx.emitEvent(emit.failure, {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n log.warn('watch-files-failed-to-start', { error: err instanceof Error ? err : String(err) });\n }\n },\n\n osWatchProcess: (name: string, subcommand?: string, emit?: OsEmitConfig) => {\n const searchTerm = subcommand ? `${name} ${subcommand}` : name;\n // execFileSync below runs pgrep with no shell, so searchTerm can never inject\n // a command; this guard additionally rejects pathological / metacharacter input.\n if (!/^[\\w .\\-/:@]+$/.test(searchTerm)) {\n log.warn('watch-process-invalid-name', { searchTerm });\n return;\n }\n let wasRunning = false;\n // Both start + exit transitions share one event name when emit.on_message\n // is configured (consumers discriminate on the payload's `process` field).\n const startEvent = resolveOnMessage(emit, \"OS_PROCESS_STARTED\");\n const exitEvent = resolveOnMessage(emit, \"OS_PROCESS_EXITED\");\n\n const interval = setInterval(() => {\n let isRunning = false;\n try {\n const result = execFileSync(\"pgrep\", [\"-f\", searchTerm], {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n isRunning = result.trim().length > 0;\n } catch {\n isRunning = false;\n }\n\n if (isRunning && !wasRunning) {\n debouncedEmit(startEvent, { process: name, subcommand: subcommand ?? null });\n } else if (!isRunning && wasRunning) {\n debouncedEmit(exitEvent, { process: name, subcommand: subcommand ?? null });\n }\n wasRunning = isRunning;\n }, 2000);\n\n intervals.push(interval);\n },\n\n osWatchPort: (port: number, protocol: string, emit?: OsEmitConfig) => {\n if (protocol !== \"tcp\") {\n log.warn('watch-port-only-tcp-supported', { protocol });\n return;\n }\n\n let wasOpen = false;\n const openEvent = resolveOnMessage(emit, \"OS_PORT_OPENED\");\n const closeEvent = resolveOnMessage(emit, \"OS_PORT_CLOSED\");\n\n const interval = setInterval(() => {\n const socket = new net.Socket();\n socket.setTimeout(1000);\n\n socket.on(\"connect\", () => {\n socket.destroy();\n if (!wasOpen) {\n wasOpen = true;\n debouncedEmit(openEvent, { port, protocol });\n }\n });\n\n socket.on(\"error\", () => {\n socket.destroy();\n if (wasOpen) {\n wasOpen = false;\n debouncedEmit(closeEvent, { port, protocol });\n }\n });\n\n socket.on(\"timeout\", () => {\n socket.destroy();\n if (wasOpen) {\n wasOpen = false;\n debouncedEmit(closeEvent, { port, protocol });\n }\n });\n\n socket.connect(port, \"127.0.0.1\");\n }, 3000);\n\n intervals.push(interval);\n },\n\n osWatchHttp: (urlPattern: string, method?: string, _emit?: OsEmitConfig) => {\n // HTTP interception requires monkey-patching Node.js module exports (read-only in TS types).\n // The compiled path (backend.rs) generates untyped inline code that handles this.\n // For the interpreted runtime, log a warning.\n if (!httpWatchActive) {\n httpWatchActive = true;\n log.warn('watch-http-compiled-only', {\n pattern: urlPattern,\n method: method ?? null,\n });\n }\n },\n\n osWatchCron: (expression: string, emit?: OsEmitConfig) => {\n let fields: CronFields;\n try {\n fields = parseCron(expression);\n } catch (err) {\n if (emit?.failure) {\n ctx.emitEvent(emit.failure, {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n log.warn('watch-cron-invalid-expression', { error: err instanceof Error ? err : String(err) });\n return;\n }\n\n let lastFired = -1;\n const eventName = resolveOnMessage(emit, \"OS_CRON_FIRE\");\n\n const interval = setInterval(() => {\n const now = new Date();\n const minuteKey = cronMinuteKey(now);\n\n if (minuteKey !== lastFired && cronMatches(fields, now)) {\n lastFired = minuteKey;\n debouncedEmit(eventName, {\n expression,\n firedAt: now.toISOString(),\n });\n }\n }, 1000);\n\n intervals.push(interval);\n },\n\n osWatchSignal: (signal: string, emit?: OsEmitConfig) => {\n const sig = signal.toUpperCase() as NodeJS.Signals;\n // Default name keeps the per-signal suffix (OS_SIGNAL_TERM); a\n // configured on_message drops that convention in favor of the single\n // author-chosen event.\n const handler = () => {\n const eventName = emit?.on_message ?? `OS_SIGNAL_${sig}`;\n debouncedEmit(eventName, { signal: sig });\n };\n\n try {\n process.on(sig, handler);\n signalHandlers.push({ signal: sig, handler });\n } catch (err) {\n if (emit?.failure) {\n ctx.emitEvent(emit.failure, {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n log.warn('watch-signal-cannot-listen', { signal: sig, error: err instanceof Error ? err : String(err) });\n }\n },\n\n osWatchEnv: (variable: string, emit?: OsEmitConfig) => {\n let lastValue = process.env[variable];\n const eventName = resolveOnMessage(emit, \"OS_ENV_CHANGED\");\n\n const interval = setInterval(() => {\n const current = process.env[variable];\n if (current !== lastValue) {\n const previous = lastValue;\n lastValue = current;\n debouncedEmit(eventName, {\n variable,\n value: current ?? null,\n previous: previous ?? null,\n });\n }\n }, 1000);\n\n intervals.push(interval);\n },\n\n osDebounce: (ms: number, eventType: string) => {\n debounceConfig.set(eventType, ms);\n },\n };\n\n // ============================================================================\n // Cleanup\n // ============================================================================\n\n function cleanup(): void {\n for (const w of watchers) {\n try { w.close(); } catch { /* already closed */ }\n }\n watchers.length = 0;\n\n for (const i of intervals) {\n clearInterval(i);\n }\n intervals.length = 0;\n\n for (const { signal, handler } of signalHandlers) {\n try { process.removeListener(signal, handler); } catch { /* noop */ }\n }\n signalHandlers.length = 0;\n\n httpWatchActive = false;\n\n // Clear pending debounce timers\n for (const timer of debounceTimers.values()) {\n clearTimeout(timer);\n }\n debounceTimers.clear();\n }\n\n return { handlers, cleanup };\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { B as BindingContext, E as EvaluationContextExtensions, P as PatternProps, a as EffectHandlers, b as EffectContext, c as ExecutionEnvironment, d as EffectResult, T as TraitDefinition } from './types-BA7GoDni.js';
|
|
2
2
|
export { e as Effect, f as EventListener, H as HANDLER_MANIFEST, I as IEventBus, R as RuntimeConfig, g as RuntimeEvent, h as TraitState, i as TransitionObserver, j as TransitionResult, U as Unsubscribe } from './types-BA7GoDni.js';
|
|
3
|
-
import { U as UnifiedLoaderOptions, S as SchemaLoader, I as ImportChainLike, L as LoadResult, a as LoadedSchema, b as LoadedOrbital } from './OrbitalServerRuntime-
|
|
4
|
-
export { E as EntitySharingMap, c as EventBus, d as EventNamespaceMap, O as OrbitalEventRequest, e as OrbitalEventResponse, f as OrbitalServerRuntimeConfig, P as PreprocessOptions, g as PreprocessResult, h as PreprocessedSchema, i as ProcessEventOptions, R as RegisteredOrbital, j as RuntimeOrbital, k as RuntimeOrbitalSchema, l as RuntimeTrait, m as StateMachineManager, n as collectDeclaredConfigDefaults, o as collectDeclaredEntityDefaults, p as createInitialTraitState, q as findInitialState, r as findTransition, s as getIsolatedCollectionName, t as getNamespacedEvent, u as isBrowser, v as isElectron, w as isNamespacedEvent, x as isNode, y as normalizeEventKey, z as parseNamespacedEvent, A as preprocessSchema, B as processEvent } from './OrbitalServerRuntime-
|
|
3
|
+
import { U as UnifiedLoaderOptions, S as SchemaLoader, I as ImportChainLike, L as LoadResult, a as LoadedSchema, b as LoadedOrbital } from './OrbitalServerRuntime-CO0rEx3p.js';
|
|
4
|
+
export { E as EntitySharingMap, c as EventBus, d as EventNamespaceMap, O as OrbitalEventRequest, e as OrbitalEventResponse, f as OrbitalServerRuntimeConfig, P as PreprocessOptions, g as PreprocessResult, h as PreprocessedSchema, i as ProcessEventOptions, R as RegisteredOrbital, j as RuntimeOrbital, k as RuntimeOrbitalSchema, l as RuntimeTrait, m as StateMachineManager, n as collectDeclaredConfigDefaults, o as collectDeclaredEntityDefaults, p as createInitialTraitState, q as findInitialState, r as findTransition, s as getIsolatedCollectionName, t as getNamespacedEvent, u as isBrowser, v as isElectron, w as isNamespacedEvent, x as isNode, y as normalizeEventKey, z as parseNamespacedEvent, A as preprocessSchema, B as processEvent } from './OrbitalServerRuntime-CO0rEx3p.js';
|
|
5
5
|
import { EvaluationContext } from '@almadar/evaluator';
|
|
6
6
|
export { EvaluationContext, createMinimalContext } from '@almadar/evaluator';
|
|
7
7
|
import { EventPayload, EntityField, EntityRow, ServiceParams, PayloadField, OrbitalDefinition, OrbitalSchema } from '@almadar/core';
|
|
@@ -171,15 +171,68 @@ declare class TickScheduler {
|
|
|
171
171
|
* just this tick.
|
|
172
172
|
*/
|
|
173
173
|
add(intervalMs: number, onDue: () => void): TickHandle;
|
|
174
|
+
/**
|
|
175
|
+
* Register a cron-scheduled tick (`ticks { ... every "0 9 * * *" }`).
|
|
176
|
+
* Fires at most once per matching calendar minute — the same dedup shape
|
|
177
|
+
* `os/watch-cron` uses. Throws if `expression` isn't a valid 5-field cron
|
|
178
|
+
* string; callers on a path the compiler has already validated (e.g.
|
|
179
|
+
* generated code) won't hit this, but a raw runtime value might.
|
|
180
|
+
*/
|
|
181
|
+
addCron(expression: string, onDue: () => void): TickHandle;
|
|
174
182
|
/** Stop every tick and the shared loop. */
|
|
175
183
|
stopAll(): void;
|
|
176
184
|
private ensureRunning;
|
|
177
185
|
private maybeStop;
|
|
178
186
|
private advance;
|
|
187
|
+
/** Cron ticks check calendar-match at most once a second, deduped per matching minute. */
|
|
188
|
+
private advanceCron;
|
|
179
189
|
}
|
|
180
190
|
/** Construct a new, independent `TickScheduler`. */
|
|
181
191
|
declare function createTickScheduler(): TickScheduler;
|
|
182
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Cron expression parsing and matching — the ONE canonical implementation
|
|
195
|
+
* every engine that schedules a cron-shaped interval delegates to (the
|
|
196
|
+
* `os/watch-cron` effect handler in `createOsHandlers.ts`, `OrbitalServerRuntime`,
|
|
197
|
+
* `TickScheduler`, and generated client codegen). No Node.js dependencies, so
|
|
198
|
+
* it's safe to import client-side too.
|
|
199
|
+
*
|
|
200
|
+
* @packageDocumentation
|
|
201
|
+
*/
|
|
202
|
+
interface CronFields {
|
|
203
|
+
minute: Set<number>;
|
|
204
|
+
hour: Set<number>;
|
|
205
|
+
day: Set<number>;
|
|
206
|
+
month: Set<number>;
|
|
207
|
+
weekday: Set<number>;
|
|
208
|
+
}
|
|
209
|
+
declare function parseCronField(field: string, min: number, max: number): Set<number>;
|
|
210
|
+
declare function parseCron(expression: string): CronFields;
|
|
211
|
+
/** Returns true if `expression` is a syntactically valid 5-field cron expression. */
|
|
212
|
+
declare function isValidCronExpression(expression: string): boolean;
|
|
213
|
+
declare function cronMatches(fields: CronFields, date: Date): boolean;
|
|
214
|
+
/** Calendar-minute key for once-per-minute dedup — the same shape `os/watch-cron` uses. */
|
|
215
|
+
declare function cronMinuteKey(date: Date): number;
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Duration-string parsing — the one canonical parser for a tick interval
|
|
219
|
+
* written as `'5ms'`/`'5s'`/`'1m'`/`'1h'` (as opposed to a cron expression;
|
|
220
|
+
* see `./cron.js`). Shared by `OrbitalServerRuntime` and any client consumer
|
|
221
|
+
* (`@almadar/ui`'s `useTraitStateMachine`) so both agree on the same
|
|
222
|
+
* milliseconds for the same string.
|
|
223
|
+
*
|
|
224
|
+
* @packageDocumentation
|
|
225
|
+
*/
|
|
226
|
+
/**
|
|
227
|
+
* Parse a duration string to milliseconds. Supports `'5ms'`/`'5s'`/`'1m'`/`'1h'`,
|
|
228
|
+
* and a bare number-as-string (assumed ms). Throws on anything else — a
|
|
229
|
+
* caller should check `isValidCronExpression` first if the string might be
|
|
230
|
+
* cron-shaped instead.
|
|
231
|
+
*/
|
|
232
|
+
declare function parseDurationString(interval: string): number;
|
|
233
|
+
/** Returns true if `interval` matches the duration-string shape `parseDurationString` accepts. */
|
|
234
|
+
declare function isValidDurationString(interval: string): boolean;
|
|
235
|
+
|
|
183
236
|
/**
|
|
184
237
|
* BindingResolver - Platform-Agnostic Binding Resolution
|
|
185
238
|
*
|
|
@@ -960,4 +1013,4 @@ declare namespace index {
|
|
|
960
1013
|
export { type index_ComposeBehaviorsInput as ComposeBehaviorsInput, type index_ComposeBehaviorsResult as ComposeBehaviorsResult, type index_EventWiringEntry as EventWiringEntry, type index_LayoutStrategy as LayoutStrategy, type index_PipeStep as PipeStep, index_applyEventWiring as applyEventWiring, index_composeBehaviors as composeBehaviors, index_detectLayoutStrategy as detectLayoutStrategy, index_pipeBehaviors as pipeBehaviors };
|
|
961
1014
|
}
|
|
962
1015
|
|
|
963
|
-
export { BindingContext, type ClientEventBus, type ComposeBehaviorsInput, type ComposeBehaviorsResult, type CreateClientEffectHandlersOptions, type CreateServerEffectHandlersOptions, EffectContext, EffectExecutor, type EffectExecutorOptions, EffectHandlers, EffectResult, type EntitySchema, type EventWiringEntry, ExecutionEnvironment, ImportChainLike, type LayoutStrategy, LoadResult, LoadedOrbital, LoadedSchema, MockPersistenceAdapter, type MockPersistenceConfig, type PayloadMismatch, type PayloadValidationFailure, PersistenceAdapter, type PipeStep, SchemaLoader, type ServerEffectResult, type SlotSetter, type TickHandle, TickScheduler, TraitDefinition, UnifiedLoaderOptions, applyEventWiring, buildEmitsFromTraits, composeBehaviors, index as composition, containsBindings, createClientEffectHandlers, createContextFromBindings, createMockPersistence, createServerEffectHandlers, createTestExecutor, createTickScheduler, createUnifiedLoader, detectLayoutStrategy, extractBindings, formatPayloadValidationError, interpolateProps, interpolateValue, pipeBehaviors, validateEventPayload, validatePayloadShapes };
|
|
1016
|
+
export { BindingContext, type ClientEventBus, type ComposeBehaviorsInput, type ComposeBehaviorsResult, type CreateClientEffectHandlersOptions, type CreateServerEffectHandlersOptions, type CronFields, EffectContext, EffectExecutor, type EffectExecutorOptions, EffectHandlers, EffectResult, type EntitySchema, type EventWiringEntry, ExecutionEnvironment, ImportChainLike, type LayoutStrategy, LoadResult, LoadedOrbital, LoadedSchema, MockPersistenceAdapter, type MockPersistenceConfig, type PayloadMismatch, type PayloadValidationFailure, PersistenceAdapter, type PipeStep, SchemaLoader, type ServerEffectResult, type SlotSetter, type TickHandle, TickScheduler, TraitDefinition, UnifiedLoaderOptions, applyEventWiring, buildEmitsFromTraits, composeBehaviors, index as composition, containsBindings, createClientEffectHandlers, createContextFromBindings, createMockPersistence, createServerEffectHandlers, createTestExecutor, createTickScheduler, createUnifiedLoader, cronMatches, cronMinuteKey, detectLayoutStrategy, extractBindings, formatPayloadValidationError, interpolateProps, interpolateValue, isValidCronExpression, isValidDurationString, parseCron, parseCronField, parseDurationString, pipeBehaviors, validateEventPayload, validatePayloadShapes };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { EffectExecutor, createContextFromBindings } from './chunk-
|
|
2
|
-
export { EffectExecutor, EventBus, HANDLER_MANIFEST, InMemoryPersistence, MockPersistenceAdapter, StateMachineManager, TickScheduler, buildEmitsFromTraits, collectDeclaredConfigDefaults, collectDeclaredEntityDefaults, containsBindings, createContextFromBindings, createInitialTraitState, createMinimalContext, createMockPersistence, createTestExecutor, createTickScheduler, createUnifiedLoader, extractBindings, findInitialState, findTransition, formatPayloadValidationError, getIsolatedCollectionName, getNamespacedEvent, interpolateProps, interpolateValue, isBrowser, isElectron, isNamespacedEvent, isNode, normalizeEventKey, parseNamespacedEvent, preprocessSchema, processEvent, validateEventPayload, validatePayloadShapes } from './chunk-
|
|
1
|
+
import { EffectExecutor, createContextFromBindings } from './chunk-53VRCHPK.js';
|
|
2
|
+
export { EffectExecutor, EventBus, HANDLER_MANIFEST, InMemoryPersistence, MockPersistenceAdapter, StateMachineManager, TickScheduler, buildEmitsFromTraits, collectDeclaredConfigDefaults, collectDeclaredEntityDefaults, containsBindings, createContextFromBindings, createInitialTraitState, createMinimalContext, createMockPersistence, createTestExecutor, createTickScheduler, createUnifiedLoader, extractBindings, findInitialState, findTransition, formatPayloadValidationError, getIsolatedCollectionName, getNamespacedEvent, interpolateProps, interpolateValue, isBrowser, isElectron, isNamespacedEvent, isNode, isValidDurationString, normalizeEventKey, parseDurationString, parseNamespacedEvent, preprocessSchema, processEvent, validateEventPayload, validatePayloadShapes } from './chunk-53VRCHPK.js';
|
|
3
|
+
export { cronMatches, cronMinuteKey, isValidCronExpression, parseCron, parseCronField } from './chunk-U4PL237A.js';
|
|
3
4
|
import { __export } from './chunk-PZ5AY32C.js';
|
|
4
5
|
import { createLogger } from '@almadar/logger';
|
|
5
6
|
import { evaluate } from '@almadar/evaluator';
|