@almadar/runtime 5.7.0 → 5.8.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/LocalPersistenceAdapter.d.ts +46 -0
- package/dist/LocalPersistenceAdapter.js +79 -0
- package/dist/LocalPersistenceAdapter.js.map +1 -0
- package/dist/{OrbitalServerRuntime-BMOr7miw.d.ts → OrbitalServerRuntime-BP5sz5Bn.d.ts} +2 -107
- package/dist/OrbitalServerRuntime.d.ts +2 -1
- package/dist/OrbitalServerRuntime.js +37 -365
- package/dist/OrbitalServerRuntime.js.map +1 -1
- package/dist/PersistenceAdapter-B6dQCbbU.d.ts +67 -0
- package/dist/createOsHandlers.d.ts +28 -0
- package/dist/createOsHandlers.js +285 -0
- package/dist/createOsHandlers.js.map +1 -0
- package/dist/index.d.ts +6 -26
- package/package.json +1 -1
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { EntityRow } from '@almadar/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PersistenceAdapter — the storage contract for runtime effect handlers.
|
|
5
|
+
*
|
|
6
|
+
* The server-side runtime and the in-browser mock runtime both invoke
|
|
7
|
+
* `fetch` / `persist` / `ref` / `deref` / `swap!` effects against an
|
|
8
|
+
* implementation of this interface. Extracted from
|
|
9
|
+
* `OrbitalServerRuntime.ts` so it can be imported by browser code that
|
|
10
|
+
* cannot depend on the server module (which pulls in express).
|
|
11
|
+
*
|
|
12
|
+
* @packageDocumentation
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Storage contract for CRUD operations on runtime entity rows.
|
|
17
|
+
*
|
|
18
|
+
* Implementations:
|
|
19
|
+
* - `InMemoryPersistence` (this file) — simple Map-backed store, used by
|
|
20
|
+
* the browser mock runtime and as the default when an adapter is not
|
|
21
|
+
* supplied to `OrbitalServerRuntime`.
|
|
22
|
+
* - `MockPersistenceAdapter` — in-memory with faker-generated seed data
|
|
23
|
+
* for realistic preview content.
|
|
24
|
+
* - `LocalPersistenceAdapter` — localStorage-backed, browser-safe.
|
|
25
|
+
* - Consumer-provided (e.g. Firestore, Postgres) for production servers.
|
|
26
|
+
*/
|
|
27
|
+
interface PersistenceAdapter {
|
|
28
|
+
create(entityType: string, data: EntityRow): Promise<{
|
|
29
|
+
id: string;
|
|
30
|
+
}>;
|
|
31
|
+
update(entityType: string, id: string, data: EntityRow): Promise<void>;
|
|
32
|
+
delete(entityType: string, id: string): Promise<void>;
|
|
33
|
+
getById(entityType: string, id: string): Promise<EntityRow | null>;
|
|
34
|
+
list(entityType: string): Promise<EntityRow[]>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Simple in-memory persistence for dev/testing and offline previews.
|
|
38
|
+
* Keys each entity collection by type, rows by generated string id.
|
|
39
|
+
*/
|
|
40
|
+
declare class InMemoryPersistence implements PersistenceAdapter {
|
|
41
|
+
private data;
|
|
42
|
+
private idCounter;
|
|
43
|
+
/**
|
|
44
|
+
* Seed the store with pre-existing rows.
|
|
45
|
+
*
|
|
46
|
+
* Accepts either a plain `Record<entityType, EntityRow[]>` or an iterable
|
|
47
|
+
* of `[entityType, EntityRow[]]` entries. Rows without an `id` get one
|
|
48
|
+
* generated at insert time; rows with an `id` keep it (so re-seeding
|
|
49
|
+
* after a schema rebuild preserves identities used in render bindings).
|
|
50
|
+
*/
|
|
51
|
+
seed(seedData: Record<string, EntityRow[]> | Iterable<[string, EntityRow[]]>): void;
|
|
52
|
+
create(entityType: string, data: EntityRow): Promise<{
|
|
53
|
+
id: string;
|
|
54
|
+
}>;
|
|
55
|
+
update(entityType: string, id: string, data: EntityRow): Promise<void>;
|
|
56
|
+
delete(entityType: string, id: string): Promise<void>;
|
|
57
|
+
getById(entityType: string, id: string): Promise<EntityRow | null>;
|
|
58
|
+
list(entityType: string): Promise<EntityRow[]>;
|
|
59
|
+
/**
|
|
60
|
+
* Snapshot the entire store as a plain object (entityType → rows).
|
|
61
|
+
* Useful for feeding a fresh render-time binding layer with the
|
|
62
|
+
* current persistence view.
|
|
63
|
+
*/
|
|
64
|
+
snapshot(): Record<string, EntityRow[]>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { InMemoryPersistence as I, type PersistenceAdapter as P };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { a as EffectHandlers } from './types-ByLpy6yj.js';
|
|
2
|
+
import { EventPayload } from '@almadar/core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* OS Trigger Handlers — Server-Side Only
|
|
6
|
+
*
|
|
7
|
+
* Provides Node.js implementations for all 8 os/* operators.
|
|
8
|
+
* Used by OrbitalServerRuntime (interpreted path).
|
|
9
|
+
*
|
|
10
|
+
* NOT exported from the main index.ts because it imports Node.js-only modules.
|
|
11
|
+
* Import directly: import { createOsHandlers } from '@almadar/runtime/createOsHandlers';
|
|
12
|
+
*
|
|
13
|
+
* @packageDocumentation
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
interface OsHandlerContext {
|
|
17
|
+
/** Emit an event on the EventBus */
|
|
18
|
+
emitEvent: (type: string, payload: EventPayload) => void;
|
|
19
|
+
/** Working directory for file watching (defaults to process.cwd()) */
|
|
20
|
+
cwd?: string;
|
|
21
|
+
}
|
|
22
|
+
interface OsHandlerResult {
|
|
23
|
+
handlers: Partial<EffectHandlers>;
|
|
24
|
+
cleanup: () => void;
|
|
25
|
+
}
|
|
26
|
+
declare function createOsHandlers(ctx: OsHandlerContext): OsHandlerResult;
|
|
27
|
+
|
|
28
|
+
export { type OsHandlerContext, type OsHandlerResult, createOsHandlers };
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import './chunk-PZ5AY32C.js';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as net from 'net';
|
|
4
|
+
import { execSync } from 'child_process';
|
|
5
|
+
|
|
6
|
+
function globToRegex(glob) {
|
|
7
|
+
let regex = "";
|
|
8
|
+
let i = 0;
|
|
9
|
+
while (i < glob.length) {
|
|
10
|
+
const c = glob[i];
|
|
11
|
+
if (c === "*") {
|
|
12
|
+
if (glob[i + 1] === "*") {
|
|
13
|
+
regex += ".*";
|
|
14
|
+
i += 2;
|
|
15
|
+
if (glob[i] === "/") i++;
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
regex += "[^/]*";
|
|
19
|
+
} else if (c === "?") {
|
|
20
|
+
regex += "[^/]";
|
|
21
|
+
} else if (c === ".") {
|
|
22
|
+
regex += "\\.";
|
|
23
|
+
} else if (c === "/" || c === "-" || c === "_") {
|
|
24
|
+
regex += c;
|
|
25
|
+
} else if (/[{}()[\]^$+|\\]/.test(c)) {
|
|
26
|
+
regex += "\\" + c;
|
|
27
|
+
} else {
|
|
28
|
+
regex += c;
|
|
29
|
+
}
|
|
30
|
+
i++;
|
|
31
|
+
}
|
|
32
|
+
return new RegExp("^" + regex + "$");
|
|
33
|
+
}
|
|
34
|
+
function parseCronField(field, min, max) {
|
|
35
|
+
const values = /* @__PURE__ */ new Set();
|
|
36
|
+
for (const part of field.split(",")) {
|
|
37
|
+
if (part === "*") {
|
|
38
|
+
for (let i = min; i <= max; i++) values.add(i);
|
|
39
|
+
} else if (part.includes("/")) {
|
|
40
|
+
const [range, stepStr] = part.split("/");
|
|
41
|
+
const step = parseInt(stepStr, 10);
|
|
42
|
+
const start = range === "*" ? min : parseInt(range, 10);
|
|
43
|
+
for (let i = start; i <= max; i += step) values.add(i);
|
|
44
|
+
} else if (part.includes("-")) {
|
|
45
|
+
const [lo, hi] = part.split("-").map(Number);
|
|
46
|
+
for (let i = lo; i <= hi; i++) values.add(i);
|
|
47
|
+
} else {
|
|
48
|
+
values.add(parseInt(part, 10));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return values;
|
|
52
|
+
}
|
|
53
|
+
function parseCron(expression) {
|
|
54
|
+
const parts = expression.trim().split(/\s+/);
|
|
55
|
+
if (parts.length !== 5) {
|
|
56
|
+
throw new Error(`Invalid cron expression (expected 5 fields): ${expression}`);
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
minute: parseCronField(parts[0], 0, 59),
|
|
60
|
+
hour: parseCronField(parts[1], 0, 23),
|
|
61
|
+
day: parseCronField(parts[2], 1, 31),
|
|
62
|
+
month: parseCronField(parts[3], 1, 12),
|
|
63
|
+
weekday: parseCronField(parts[4], 0, 6)
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function cronMatches(fields, date) {
|
|
67
|
+
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());
|
|
68
|
+
}
|
|
69
|
+
function createOsHandlers(ctx) {
|
|
70
|
+
const cwd = ctx.cwd ?? process.cwd();
|
|
71
|
+
const watchers = [];
|
|
72
|
+
const intervals = [];
|
|
73
|
+
const signalHandlers = [];
|
|
74
|
+
let httpWatchActive = false;
|
|
75
|
+
const debounceConfig = /* @__PURE__ */ new Map();
|
|
76
|
+
const debounceTimers = /* @__PURE__ */ new Map();
|
|
77
|
+
function debouncedEmit(eventType, payload) {
|
|
78
|
+
const ms = debounceConfig.get(eventType);
|
|
79
|
+
if (ms !== void 0 && ms > 0) {
|
|
80
|
+
const existing = debounceTimers.get(eventType);
|
|
81
|
+
if (existing) clearTimeout(existing);
|
|
82
|
+
debounceTimers.set(
|
|
83
|
+
eventType,
|
|
84
|
+
setTimeout(() => {
|
|
85
|
+
debounceTimers.delete(eventType);
|
|
86
|
+
ctx.emitEvent(eventType, payload);
|
|
87
|
+
}, ms)
|
|
88
|
+
);
|
|
89
|
+
} else {
|
|
90
|
+
ctx.emitEvent(eventType, payload);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const resolveOnMessage = (emit, fallback) => emit?.on_message ?? fallback;
|
|
94
|
+
const handlers = {
|
|
95
|
+
osWatchFiles: (glob, options, emit) => {
|
|
96
|
+
const recursive = options.recursive !== false;
|
|
97
|
+
const pattern = globToRegex(glob);
|
|
98
|
+
const eventName = resolveOnMessage(emit, "OS_FILE_MODIFIED");
|
|
99
|
+
try {
|
|
100
|
+
const watcher = fs.watch(cwd, { recursive }, (_event, filename) => {
|
|
101
|
+
if (filename && pattern.test(filename)) {
|
|
102
|
+
debouncedEmit(eventName, {
|
|
103
|
+
file: filename,
|
|
104
|
+
glob,
|
|
105
|
+
cwd
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
watchers.push(watcher);
|
|
110
|
+
} catch (err) {
|
|
111
|
+
if (emit?.failure) {
|
|
112
|
+
ctx.emitEvent(emit.failure, {
|
|
113
|
+
error: err instanceof Error ? err.message : String(err)
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
console.warn("[os/watch-files] Failed to start watcher:", err);
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
osWatchProcess: (name, subcommand, emit) => {
|
|
120
|
+
const searchTerm = subcommand ? `${name} ${subcommand}` : name;
|
|
121
|
+
let wasRunning = false;
|
|
122
|
+
const startEvent = resolveOnMessage(emit, "OS_PROCESS_STARTED");
|
|
123
|
+
const exitEvent = resolveOnMessage(emit, "OS_PROCESS_EXITED");
|
|
124
|
+
const interval = setInterval(() => {
|
|
125
|
+
let isRunning = false;
|
|
126
|
+
try {
|
|
127
|
+
const result = execSync(`pgrep -f "${searchTerm}" 2>/dev/null`, {
|
|
128
|
+
encoding: "utf-8",
|
|
129
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
130
|
+
});
|
|
131
|
+
isRunning = result.trim().length > 0;
|
|
132
|
+
} catch {
|
|
133
|
+
isRunning = false;
|
|
134
|
+
}
|
|
135
|
+
if (isRunning && !wasRunning) {
|
|
136
|
+
debouncedEmit(startEvent, { process: name, subcommand: subcommand ?? null });
|
|
137
|
+
} else if (!isRunning && wasRunning) {
|
|
138
|
+
debouncedEmit(exitEvent, { process: name, subcommand: subcommand ?? null });
|
|
139
|
+
}
|
|
140
|
+
wasRunning = isRunning;
|
|
141
|
+
}, 2e3);
|
|
142
|
+
intervals.push(interval);
|
|
143
|
+
},
|
|
144
|
+
osWatchPort: (port, protocol, emit) => {
|
|
145
|
+
if (protocol !== "tcp") {
|
|
146
|
+
console.warn(`[os/watch-port] Only TCP is supported, got: ${protocol}`);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
let wasOpen = false;
|
|
150
|
+
const openEvent = resolveOnMessage(emit, "OS_PORT_OPENED");
|
|
151
|
+
const closeEvent = resolveOnMessage(emit, "OS_PORT_CLOSED");
|
|
152
|
+
const interval = setInterval(() => {
|
|
153
|
+
const socket = new net.Socket();
|
|
154
|
+
socket.setTimeout(1e3);
|
|
155
|
+
socket.on("connect", () => {
|
|
156
|
+
socket.destroy();
|
|
157
|
+
if (!wasOpen) {
|
|
158
|
+
wasOpen = true;
|
|
159
|
+
debouncedEmit(openEvent, { port, protocol });
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
socket.on("error", () => {
|
|
163
|
+
socket.destroy();
|
|
164
|
+
if (wasOpen) {
|
|
165
|
+
wasOpen = false;
|
|
166
|
+
debouncedEmit(closeEvent, { port, protocol });
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
socket.on("timeout", () => {
|
|
170
|
+
socket.destroy();
|
|
171
|
+
if (wasOpen) {
|
|
172
|
+
wasOpen = false;
|
|
173
|
+
debouncedEmit(closeEvent, { port, protocol });
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
socket.connect(port, "127.0.0.1");
|
|
177
|
+
}, 3e3);
|
|
178
|
+
intervals.push(interval);
|
|
179
|
+
},
|
|
180
|
+
osWatchHttp: (urlPattern, method, _emit) => {
|
|
181
|
+
if (!httpWatchActive) {
|
|
182
|
+
httpWatchActive = true;
|
|
183
|
+
console.warn(
|
|
184
|
+
`[os/watch-http] HTTP interception is only supported in compiled mode. Pattern: ${urlPattern}${method ? `, method: ${method}` : ""}`
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
osWatchCron: (expression, emit) => {
|
|
189
|
+
let fields;
|
|
190
|
+
try {
|
|
191
|
+
fields = parseCron(expression);
|
|
192
|
+
} catch (err) {
|
|
193
|
+
if (emit?.failure) {
|
|
194
|
+
ctx.emitEvent(emit.failure, {
|
|
195
|
+
error: err instanceof Error ? err.message : String(err)
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
console.warn("[os/watch-cron] Invalid expression:", err);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
let lastFired = -1;
|
|
202
|
+
const eventName = resolveOnMessage(emit, "OS_CRON_FIRE");
|
|
203
|
+
const interval = setInterval(() => {
|
|
204
|
+
const now = /* @__PURE__ */ new Date();
|
|
205
|
+
const minuteKey = now.getFullYear() * 1e8 + now.getMonth() * 1e6 + now.getDate() * 1e4 + now.getHours() * 100 + now.getMinutes();
|
|
206
|
+
if (minuteKey !== lastFired && cronMatches(fields, now)) {
|
|
207
|
+
lastFired = minuteKey;
|
|
208
|
+
debouncedEmit(eventName, {
|
|
209
|
+
expression,
|
|
210
|
+
firedAt: now.toISOString()
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}, 1e3);
|
|
214
|
+
intervals.push(interval);
|
|
215
|
+
},
|
|
216
|
+
osWatchSignal: (signal, emit) => {
|
|
217
|
+
const sig = signal.toUpperCase();
|
|
218
|
+
const handler = () => {
|
|
219
|
+
const eventName = emit?.on_message ?? `OS_SIGNAL_${sig}`;
|
|
220
|
+
debouncedEmit(eventName, { signal: sig });
|
|
221
|
+
};
|
|
222
|
+
try {
|
|
223
|
+
process.on(sig, handler);
|
|
224
|
+
signalHandlers.push({ signal: sig, handler });
|
|
225
|
+
} catch (err) {
|
|
226
|
+
if (emit?.failure) {
|
|
227
|
+
ctx.emitEvent(emit.failure, {
|
|
228
|
+
error: err instanceof Error ? err.message : String(err)
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
console.warn(`[os/watch-signal] Cannot listen for ${sig}:`, err);
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
osWatchEnv: (variable, emit) => {
|
|
235
|
+
let lastValue = process.env[variable];
|
|
236
|
+
const eventName = resolveOnMessage(emit, "OS_ENV_CHANGED");
|
|
237
|
+
const interval = setInterval(() => {
|
|
238
|
+
const current = process.env[variable];
|
|
239
|
+
if (current !== lastValue) {
|
|
240
|
+
const previous = lastValue;
|
|
241
|
+
lastValue = current;
|
|
242
|
+
debouncedEmit(eventName, {
|
|
243
|
+
variable,
|
|
244
|
+
value: current ?? null,
|
|
245
|
+
previous: previous ?? null
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}, 1e3);
|
|
249
|
+
intervals.push(interval);
|
|
250
|
+
},
|
|
251
|
+
osDebounce: (ms, eventType) => {
|
|
252
|
+
debounceConfig.set(eventType, ms);
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
function cleanup() {
|
|
256
|
+
for (const w of watchers) {
|
|
257
|
+
try {
|
|
258
|
+
w.close();
|
|
259
|
+
} catch {
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
watchers.length = 0;
|
|
263
|
+
for (const i of intervals) {
|
|
264
|
+
clearInterval(i);
|
|
265
|
+
}
|
|
266
|
+
intervals.length = 0;
|
|
267
|
+
for (const { signal, handler } of signalHandlers) {
|
|
268
|
+
try {
|
|
269
|
+
process.removeListener(signal, handler);
|
|
270
|
+
} catch {
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
signalHandlers.length = 0;
|
|
274
|
+
httpWatchActive = false;
|
|
275
|
+
for (const timer of debounceTimers.values()) {
|
|
276
|
+
clearTimeout(timer);
|
|
277
|
+
}
|
|
278
|
+
debounceTimers.clear();
|
|
279
|
+
}
|
|
280
|
+
return { handlers, cleanup };
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export { createOsHandlers };
|
|
284
|
+
//# sourceMappingURL=createOsHandlers.js.map
|
|
285
|
+
//# sourceMappingURL=createOsHandlers.js.map
|
|
@@ -0,0 +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 { execSync } from \"child_process\";\nimport type { EventPayload, OsEmitConfig } from './types.js';\nimport type { EffectHandlers } from \"./types.js\";\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 console.warn(\"[os/watch-files] Failed to start watcher:\", err);\n }\n },\n\n osWatchProcess: (name: string, subcommand?: string, emit?: OsEmitConfig) => {\n const searchTerm = subcommand ? `${name} ${subcommand}` : name;\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 = execSync(`pgrep -f \"${searchTerm}\" 2>/dev/null`, {\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 console.warn(`[os/watch-port] Only TCP is supported, got: ${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 console.warn(\n `[os/watch-http] HTTP interception is only supported in compiled mode. ` +\n `Pattern: ${urlPattern}${method ? `, method: ${method}` : \"\"}`,\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 console.warn(\"[os/watch-cron] Invalid expression:\", 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 console.warn(`[os/watch-signal] Cannot listen for ${sig}:`, 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,11 +1,14 @@
|
|
|
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-ByLpy6yj.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-ByLpy6yj.js';
|
|
3
|
-
import { U as UnifiedLoaderOptions, S as SchemaLoader, I as ImportChainLike, L as LoadResult, a as LoadedSchema, b as LoadedOrbital
|
|
4
|
-
export { E as EntitySharingMap, c as EventBus, d as EventNamespaceMap,
|
|
3
|
+
import { U as UnifiedLoaderOptions, S as SchemaLoader, I as ImportChainLike, L as LoadResult, a as LoadedSchema, b as LoadedOrbital } from './OrbitalServerRuntime-BP5sz5Bn.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 createInitialTraitState, o as findInitialState, p as findTransition, q as getIsolatedCollectionName, r as getNamespacedEvent, s as isBrowser, t as isElectron, u as isNamespacedEvent, v as isNode, w as normalizeEventKey, x as parseNamespacedEvent, y as preprocessSchema, z as processEvent } from './OrbitalServerRuntime-BP5sz5Bn.js';
|
|
5
5
|
import { EvaluationContext } from '@almadar/evaluator';
|
|
6
6
|
export { EvaluationContext, createMinimalContext } from '@almadar/evaluator';
|
|
7
7
|
import { EventPayload, EntityRow, PayloadField, OrbitalDefinition, OrbitalSchema } from '@almadar/core';
|
|
8
|
+
import { P as PersistenceAdapter } from './PersistenceAdapter-B6dQCbbU.js';
|
|
9
|
+
export { I as InMemoryPersistence } from './PersistenceAdapter-B6dQCbbU.js';
|
|
8
10
|
export { ServerBridgeConfig, ServerBridgeState } from './ServerBridge.js';
|
|
11
|
+
export { OsHandlerContext, OsHandlerResult } from './createOsHandlers.js';
|
|
9
12
|
import 'express';
|
|
10
13
|
|
|
11
14
|
/**
|
|
@@ -588,29 +591,6 @@ interface CreateServerEffectHandlersOptions {
|
|
|
588
591
|
*/
|
|
589
592
|
declare function createServerEffectHandlers(opts: CreateServerEffectHandlersOptions): EffectHandlers;
|
|
590
593
|
|
|
591
|
-
/**
|
|
592
|
-
* OS Trigger Handlers — Server-Side Only
|
|
593
|
-
*
|
|
594
|
-
* Provides Node.js implementations for all 8 os/* operators.
|
|
595
|
-
* Used by OrbitalServerRuntime (interpreted path).
|
|
596
|
-
*
|
|
597
|
-
* NOT exported from the main index.ts because it imports Node.js-only modules.
|
|
598
|
-
* Import directly: import { createOsHandlers } from '@almadar/runtime/createOsHandlers';
|
|
599
|
-
*
|
|
600
|
-
* @packageDocumentation
|
|
601
|
-
*/
|
|
602
|
-
|
|
603
|
-
interface OsHandlerContext {
|
|
604
|
-
/** Emit an event on the EventBus */
|
|
605
|
-
emitEvent: (type: string, payload: EventPayload) => void;
|
|
606
|
-
/** Working directory for file watching (defaults to process.cwd()) */
|
|
607
|
-
cwd?: string;
|
|
608
|
-
}
|
|
609
|
-
interface OsHandlerResult {
|
|
610
|
-
handlers: Partial<EffectHandlers>;
|
|
611
|
-
cleanup: () => void;
|
|
612
|
-
}
|
|
613
|
-
|
|
614
594
|
/**
|
|
615
595
|
* PayloadValidator - Cross-Trait Payload Shape Validation (RCG-10)
|
|
616
596
|
*
|
|
@@ -883,4 +863,4 @@ declare namespace index {
|
|
|
883
863
|
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 };
|
|
884
864
|
}
|
|
885
865
|
|
|
886
|
-
export { BindingContext, type ClientEventBus, type ComposeBehaviorsInput, type ComposeBehaviorsResult, type CreateClientEffectHandlersOptions, type CreateServerEffectHandlersOptions, EffectContext, EffectExecutor, type EffectExecutorOptions, EffectHandlers, EffectResult, type EntityField, type EntitySchema, type EventWiringEntry, ExecutionEnvironment, ImportChainLike, type LayoutStrategy, LoadResult, LoadedOrbital, LoadedSchema, MockPersistenceAdapter, type MockPersistenceConfig, type
|
|
866
|
+
export { BindingContext, type ClientEventBus, type ComposeBehaviorsInput, type ComposeBehaviorsResult, type CreateClientEffectHandlersOptions, type CreateServerEffectHandlersOptions, EffectContext, EffectExecutor, type EffectExecutorOptions, EffectHandlers, EffectResult, type EntityField, 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, TraitDefinition, UnifiedLoaderOptions, applyEventWiring, buildEmitsFromTraits, composeBehaviors, index as composition, containsBindings, createClientEffectHandlers, createContextFromBindings, createMockPersistence, createServerEffectHandlers, createTestExecutor, createUnifiedLoader, detectLayoutStrategy, extractBindings, formatPayloadValidationError, interpolateProps, interpolateValue, pipeBehaviors, validateEventPayload, validatePayloadShapes };
|