@almadar/runtime 5.7.0 → 5.8.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.
@@ -1,364 +1,29 @@
1
- import { createLogger, EventBus, createUnifiedLoader, MockPersistenceAdapter, InMemoryPersistence, preprocessSchema, StateMachineManager, createContextFromBindings, validateEventPayload, formatPayloadValidationError, EffectExecutor } from './chunk-54YR5TKO.js';
2
- export { InMemoryPersistence } from './chunk-54YR5TKO.js';
1
+ import { createLogger, EventBus, createUnifiedLoader, MockPersistenceAdapter, InMemoryPersistence, preprocessSchema, StateMachineManager, createContextFromBindings, validateEventPayload, formatPayloadValidationError, EffectExecutor } from './chunk-GW5OOIRO.js';
2
+ export { InMemoryPersistence } from './chunk-GW5OOIRO.js';
3
3
  import './chunk-PZ5AY32C.js';
4
- import { Router } from 'express';
5
- import * as fs from 'fs';
6
- import fs__default from 'fs';
7
- import path from 'path';
4
+ import * as nodeModule from 'module';
8
5
  import { evaluateGuard, evaluate } from '@almadar/evaluator';
9
6
  import { isInlineTrait, isEntityCall } from '@almadar/core';
10
- import * as net from 'net';
11
- import { execSync } from 'child_process';
12
7
 
13
- var LocalPersistenceAdapter = class {
14
- root;
15
- constructor(root) {
16
- this.root = root;
17
- fs__default.mkdirSync(root, { recursive: true });
18
- }
19
- entityDir(entityType) {
20
- const dir = path.join(this.root, entityType.toLowerCase());
21
- fs__default.mkdirSync(dir, { recursive: true });
22
- return dir;
23
- }
24
- filePath(entityType, id) {
25
- return path.join(this.entityDir(entityType), `${id}.json`);
26
- }
27
- async create(entityType, data) {
28
- const id = data.id || `${entityType.toLowerCase()}_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
29
- const record = { ...data, id };
30
- const filePath = this.filePath(entityType, id);
31
- fs__default.writeFileSync(filePath, JSON.stringify(record, null, 2), "utf-8");
32
- return { id };
33
- }
34
- async update(entityType, id, data) {
35
- const filePath = this.filePath(entityType, id);
36
- let existing = { id };
37
- if (fs__default.existsSync(filePath)) {
38
- existing = JSON.parse(fs__default.readFileSync(filePath, "utf-8"));
39
- }
40
- const merged = { ...existing, ...data, id };
41
- fs__default.writeFileSync(filePath, JSON.stringify(merged, null, 2), "utf-8");
42
- }
43
- async delete(entityType, id) {
44
- const filePath = this.filePath(entityType, id);
45
- if (fs__default.existsSync(filePath)) {
46
- fs__default.unlinkSync(filePath);
47
- }
48
- }
49
- async getById(entityType, id) {
50
- const filePath = this.filePath(entityType, id);
51
- if (!fs__default.existsSync(filePath)) return null;
52
- return JSON.parse(fs__default.readFileSync(filePath, "utf-8"));
53
- }
54
- async list(entityType) {
55
- const dir = this.entityDir(entityType);
56
- if (!fs__default.existsSync(dir)) return [];
57
- const files = fs__default.readdirSync(dir).filter((f) => f.endsWith(".json"));
58
- const results = [];
59
- for (const file of files) {
60
- const content = fs__default.readFileSync(path.join(dir, file), "utf-8");
61
- results.push(JSON.parse(content));
62
- }
63
- return results;
64
- }
65
- /**
66
- * Remove all data for an entity type.
67
- */
68
- async clear(entityType) {
69
- const dir = path.join(this.root, entityType.toLowerCase());
70
- if (fs__default.existsSync(dir)) {
71
- fs__default.rmSync(dir, { recursive: true, force: true });
72
- }
73
- }
74
- /**
75
- * Remove all local data.
76
- */
77
- async clearAll() {
78
- if (fs__default.existsSync(this.root)) {
79
- fs__default.rmSync(this.root, { recursive: true, force: true });
80
- fs__default.mkdirSync(this.root, { recursive: true });
81
- }
82
- }
83
- };
84
- function globToRegex(glob) {
85
- let regex = "";
86
- let i = 0;
87
- while (i < glob.length) {
88
- const c = glob[i];
89
- if (c === "*") {
90
- if (glob[i + 1] === "*") {
91
- regex += ".*";
92
- i += 2;
93
- if (glob[i] === "/") i++;
94
- continue;
95
- }
96
- regex += "[^/]*";
97
- } else if (c === "?") {
98
- regex += "[^/]";
99
- } else if (c === ".") {
100
- regex += "\\.";
101
- } else if (c === "/" || c === "-" || c === "_") {
102
- regex += c;
103
- } else if (/[{}()[\]^$+|\\]/.test(c)) {
104
- regex += "\\" + c;
8
+ var _resolvedNodeRequire = null;
9
+ function nodeRequire(modulePath) {
10
+ if (!_resolvedNodeRequire) {
11
+ const evalRequire = (0, eval)('typeof require !== "undefined" ? require : null');
12
+ if (evalRequire) {
13
+ _resolvedNodeRequire = evalRequire;
105
14
  } else {
106
- regex += c;
107
- }
108
- i++;
109
- }
110
- return new RegExp("^" + regex + "$");
111
- }
112
- function parseCronField(field, min, max) {
113
- const values = /* @__PURE__ */ new Set();
114
- for (const part of field.split(",")) {
115
- if (part === "*") {
116
- for (let i = min; i <= max; i++) values.add(i);
117
- } else if (part.includes("/")) {
118
- const [range, stepStr] = part.split("/");
119
- const step = parseInt(stepStr, 10);
120
- const start = range === "*" ? min : parseInt(range, 10);
121
- for (let i = start; i <= max; i += step) values.add(i);
122
- } else if (part.includes("-")) {
123
- const [lo, hi] = part.split("-").map(Number);
124
- for (let i = lo; i <= hi; i++) values.add(i);
125
- } else {
126
- values.add(parseInt(part, 10));
127
- }
128
- }
129
- return values;
130
- }
131
- function parseCron(expression) {
132
- const parts = expression.trim().split(/\s+/);
133
- if (parts.length !== 5) {
134
- throw new Error(`Invalid cron expression (expected 5 fields): ${expression}`);
135
- }
136
- return {
137
- minute: parseCronField(parts[0], 0, 59),
138
- hour: parseCronField(parts[1], 0, 23),
139
- day: parseCronField(parts[2], 1, 31),
140
- month: parseCronField(parts[3], 1, 12),
141
- weekday: parseCronField(parts[4], 0, 6)
142
- };
143
- }
144
- function cronMatches(fields, date) {
145
- 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());
146
- }
147
- function createOsHandlers(ctx) {
148
- const cwd = ctx.cwd ?? process.cwd();
149
- const watchers = [];
150
- const intervals = [];
151
- const signalHandlers = [];
152
- let httpWatchActive = false;
153
- const debounceConfig = /* @__PURE__ */ new Map();
154
- const debounceTimers = /* @__PURE__ */ new Map();
155
- function debouncedEmit(eventType, payload) {
156
- const ms = debounceConfig.get(eventType);
157
- if (ms !== void 0 && ms > 0) {
158
- const existing = debounceTimers.get(eventType);
159
- if (existing) clearTimeout(existing);
160
- debounceTimers.set(
161
- eventType,
162
- setTimeout(() => {
163
- debounceTimers.delete(eventType);
164
- ctx.emitEvent(eventType, payload);
165
- }, ms)
166
- );
167
- } else {
168
- ctx.emitEvent(eventType, payload);
169
- }
170
- }
171
- const resolveOnMessage = (emit, fallback) => emit?.on_message ?? fallback;
172
- const handlers = {
173
- osWatchFiles: (glob, options, emit) => {
174
- const recursive = options.recursive !== false;
175
- const pattern = globToRegex(glob);
176
- const eventName = resolveOnMessage(emit, "OS_FILE_MODIFIED");
177
- try {
178
- const watcher = fs.watch(cwd, { recursive }, (_event, filename) => {
179
- if (filename && pattern.test(filename)) {
180
- debouncedEmit(eventName, {
181
- file: filename,
182
- glob,
183
- cwd
184
- });
185
- }
186
- });
187
- watchers.push(watcher);
188
- } catch (err) {
189
- if (emit?.failure) {
190
- ctx.emitEvent(emit.failure, {
191
- error: err instanceof Error ? err.message : String(err)
192
- });
193
- }
194
- console.warn("[os/watch-files] Failed to start watcher:", err);
195
- }
196
- },
197
- osWatchProcess: (name, subcommand, emit) => {
198
- const searchTerm = subcommand ? `${name} ${subcommand}` : name;
199
- let wasRunning = false;
200
- const startEvent = resolveOnMessage(emit, "OS_PROCESS_STARTED");
201
- const exitEvent = resolveOnMessage(emit, "OS_PROCESS_EXITED");
202
- const interval = setInterval(() => {
203
- let isRunning = false;
204
- try {
205
- const result = execSync(`pgrep -f "${searchTerm}" 2>/dev/null`, {
206
- encoding: "utf-8",
207
- stdio: ["pipe", "pipe", "pipe"]
208
- });
209
- isRunning = result.trim().length > 0;
210
- } catch {
211
- isRunning = false;
212
- }
213
- if (isRunning && !wasRunning) {
214
- debouncedEmit(startEvent, { process: name, subcommand: subcommand ?? null });
215
- } else if (!isRunning && wasRunning) {
216
- debouncedEmit(exitEvent, { process: name, subcommand: subcommand ?? null });
217
- }
218
- wasRunning = isRunning;
219
- }, 2e3);
220
- intervals.push(interval);
221
- },
222
- osWatchPort: (port, protocol, emit) => {
223
- if (protocol !== "tcp") {
224
- console.warn(`[os/watch-port] Only TCP is supported, got: ${protocol}`);
225
- return;
226
- }
227
- let wasOpen = false;
228
- const openEvent = resolveOnMessage(emit, "OS_PORT_OPENED");
229
- const closeEvent = resolveOnMessage(emit, "OS_PORT_CLOSED");
230
- const interval = setInterval(() => {
231
- const socket = new net.Socket();
232
- socket.setTimeout(1e3);
233
- socket.on("connect", () => {
234
- socket.destroy();
235
- if (!wasOpen) {
236
- wasOpen = true;
237
- debouncedEmit(openEvent, { port, protocol });
238
- }
239
- });
240
- socket.on("error", () => {
241
- socket.destroy();
242
- if (wasOpen) {
243
- wasOpen = false;
244
- debouncedEmit(closeEvent, { port, protocol });
245
- }
246
- });
247
- socket.on("timeout", () => {
248
- socket.destroy();
249
- if (wasOpen) {
250
- wasOpen = false;
251
- debouncedEmit(closeEvent, { port, protocol });
252
- }
253
- });
254
- socket.connect(port, "127.0.0.1");
255
- }, 3e3);
256
- intervals.push(interval);
257
- },
258
- osWatchHttp: (urlPattern, method, _emit) => {
259
- if (!httpWatchActive) {
260
- httpWatchActive = true;
261
- console.warn(
262
- `[os/watch-http] HTTP interception is only supported in compiled mode. Pattern: ${urlPattern}${method ? `, method: ${method}` : ""}`
15
+ const createReq = nodeModule.createRequire;
16
+ if (typeof createReq !== "function") {
17
+ throw new Error(
18
+ "[OrbitalServerRuntime] No synchronous require available. This branch is Node-only \u2014 invoking it from a browser indicates an isNodeEnv() guard regression upstream."
263
19
  );
264
20
  }
265
- },
266
- osWatchCron: (expression, emit) => {
267
- let fields;
268
- try {
269
- fields = parseCron(expression);
270
- } catch (err) {
271
- if (emit?.failure) {
272
- ctx.emitEvent(emit.failure, {
273
- error: err instanceof Error ? err.message : String(err)
274
- });
275
- }
276
- console.warn("[os/watch-cron] Invalid expression:", err);
277
- return;
278
- }
279
- let lastFired = -1;
280
- const eventName = resolveOnMessage(emit, "OS_CRON_FIRE");
281
- const interval = setInterval(() => {
282
- const now = /* @__PURE__ */ new Date();
283
- const minuteKey = now.getFullYear() * 1e8 + now.getMonth() * 1e6 + now.getDate() * 1e4 + now.getHours() * 100 + now.getMinutes();
284
- if (minuteKey !== lastFired && cronMatches(fields, now)) {
285
- lastFired = minuteKey;
286
- debouncedEmit(eventName, {
287
- expression,
288
- firedAt: now.toISOString()
289
- });
290
- }
291
- }, 1e3);
292
- intervals.push(interval);
293
- },
294
- osWatchSignal: (signal, emit) => {
295
- const sig = signal.toUpperCase();
296
- const handler = () => {
297
- const eventName = emit?.on_message ?? `OS_SIGNAL_${sig}`;
298
- debouncedEmit(eventName, { signal: sig });
299
- };
300
- try {
301
- process.on(sig, handler);
302
- signalHandlers.push({ signal: sig, handler });
303
- } catch (err) {
304
- if (emit?.failure) {
305
- ctx.emitEvent(emit.failure, {
306
- error: err instanceof Error ? err.message : String(err)
307
- });
308
- }
309
- console.warn(`[os/watch-signal] Cannot listen for ${sig}:`, err);
310
- }
311
- },
312
- osWatchEnv: (variable, emit) => {
313
- let lastValue = process.env[variable];
314
- const eventName = resolveOnMessage(emit, "OS_ENV_CHANGED");
315
- const interval = setInterval(() => {
316
- const current = process.env[variable];
317
- if (current !== lastValue) {
318
- const previous = lastValue;
319
- lastValue = current;
320
- debouncedEmit(eventName, {
321
- variable,
322
- value: current ?? null,
323
- previous: previous ?? null
324
- });
325
- }
326
- }, 1e3);
327
- intervals.push(interval);
328
- },
329
- osDebounce: (ms, eventType) => {
330
- debounceConfig.set(eventType, ms);
331
- }
332
- };
333
- function cleanup() {
334
- for (const w of watchers) {
335
- try {
336
- w.close();
337
- } catch {
338
- }
21
+ _resolvedNodeRequire = createReq(import.meta.url);
339
22
  }
340
- watchers.length = 0;
341
- for (const i of intervals) {
342
- clearInterval(i);
343
- }
344
- intervals.length = 0;
345
- for (const { signal, handler } of signalHandlers) {
346
- try {
347
- process.removeListener(signal, handler);
348
- } catch {
349
- }
350
- }
351
- signalHandlers.length = 0;
352
- httpWatchActive = false;
353
- for (const timer of debounceTimers.values()) {
354
- clearTimeout(timer);
355
- }
356
- debounceTimers.clear();
357
23
  }
358
- return { handlers, cleanup };
24
+ return _resolvedNodeRequire(modulePath);
359
25
  }
360
-
361
- // src/OrbitalServerRuntime.ts
26
+ var _nodeRequireExt = import.meta.url.endsWith(".ts") ? ".ts" : ".js";
362
27
  var effectLog = createLogger("almadar:runtime:effects");
363
28
  var busLog = createLogger("almadar:runtime:bus");
364
29
  var renderLog = createLogger("almadar:runtime:render-ui");
@@ -445,12 +110,18 @@ var OrbitalServerRuntime = class {
445
110
  this.persistence = config.persistence || new InMemoryPersistence();
446
111
  }
447
112
  if (config.localStorageRoot && isNodeEnv()) {
113
+ const { LocalPersistenceAdapter } = nodeRequire(`./LocalPersistenceAdapter${_nodeRequireExt}`);
448
114
  this.localPersistence = new LocalPersistenceAdapter(config.localStorageRoot);
449
115
  }
450
- this.osHandlers = isNodeEnv() ? createOsHandlers({
451
- emitEvent: (type, payload) => this.eventBus.emit(type, payload)
452
- }) : { handlers: {}, cleanup: () => {
453
- } };
116
+ if (isNodeEnv()) {
117
+ const { createOsHandlers } = nodeRequire(`./createOsHandlers${_nodeRequireExt}`);
118
+ this.osHandlers = createOsHandlers({
119
+ emitEvent: (type, payload) => this.eventBus.emit(type, payload)
120
+ });
121
+ } else {
122
+ this.osHandlers = { handlers: {}, cleanup: () => {
123
+ } };
124
+ }
454
125
  this.config.effectHandlers = {
455
126
  ...this.osHandlers.handlers,
456
127
  ...this.config.effectHandlers
@@ -471,22 +142,22 @@ var OrbitalServerRuntime = class {
471
142
  return;
472
143
  }
473
144
  try {
474
- const [{ fileURLToPath }, path2, fs3] = await Promise.all([
145
+ const [{ fileURLToPath }, path, fs] = await Promise.all([
475
146
  import('url'),
476
147
  import('path'),
477
148
  import('fs')
478
149
  ]);
479
150
  const mainEntryUrl = import.meta.resolve("@almadar/std");
480
151
  const mainEntry = fileURLToPath(mainEntryUrl);
481
- let stdLibPath = path2.dirname(mainEntry);
482
- while (stdLibPath !== path2.dirname(stdLibPath)) {
483
- if (fs3.existsSync(path2.join(stdLibPath, "package.json"))) {
152
+ let stdLibPath = path.dirname(mainEntry);
153
+ while (stdLibPath !== path.dirname(stdLibPath)) {
154
+ if (fs.existsSync(path.join(stdLibPath, "package.json"))) {
484
155
  const pkg = JSON.parse(
485
- fs3.readFileSync(path2.join(stdLibPath, "package.json"), "utf-8")
156
+ fs.readFileSync(path.join(stdLibPath, "package.json"), "utf-8")
486
157
  );
487
158
  if (pkg.name === "@almadar/std") break;
488
159
  }
489
- stdLibPath = path2.dirname(stdLibPath);
160
+ stdLibPath = path.dirname(stdLibPath);
490
161
  }
491
162
  const basePath = this.config.loaderConfig?.basePath ?? process.cwd();
492
163
  this.loader = createUnifiedLoader({
@@ -605,20 +276,20 @@ var OrbitalServerRuntime = class {
605
276
  * Node only. Browsers must receive already-resolved schemas from their
606
277
  * server (see `getResolvedSchema()`).
607
278
  */
608
- async registerFromFile(path2) {
279
+ async registerFromFile(path) {
609
280
  if (typeof process === "undefined" || !process.versions?.node) {
610
281
  throw new Error(
611
282
  "registerFromFile is Node-only. Browsers should receive resolved schemas from their server."
612
283
  );
613
284
  }
614
285
  const { readFile } = await import('fs/promises');
615
- const raw = await readFile(path2, "utf-8");
286
+ const raw = await readFile(path, "utf-8");
616
287
  let schema;
617
288
  try {
618
289
  schema = JSON.parse(raw);
619
290
  } catch (err) {
620
291
  const msg = err instanceof Error ? err.message : String(err);
621
- throw new Error(`registerFromFile: ${path2} is not valid JSON: ${msg}`);
292
+ throw new Error(`registerFromFile: ${path} is not valid JSON: ${msg}`);
622
293
  }
623
294
  await this.register(schema);
624
295
  }
@@ -1658,8 +1329,8 @@ var OrbitalServerRuntime = class {
1658
1329
  });
1659
1330
  pushClientEffect(["render-ui", slot, pattern, props, priority]);
1660
1331
  },
1661
- navigate: (path2, params) => {
1662
- pushClientEffect(["navigate", path2, params]);
1332
+ navigate: (path, params) => {
1333
+ pushClientEffect(["navigate", path, params]);
1663
1334
  },
1664
1335
  notify: (message, type) => {
1665
1336
  if (this.config.debug) {
@@ -1943,6 +1614,7 @@ var OrbitalServerRuntime = class {
1943
1614
  "OrbitalServerRuntime.router() is Node-only (uses Express). For in-browser use, mount <BrowserPlayground> from @almadar/ui instead."
1944
1615
  );
1945
1616
  }
1617
+ const { Router } = nodeRequire("express");
1946
1618
  const router = Router();
1947
1619
  router.get("/", (_req, res) => {
1948
1620
  const orbitals = Array.from(this.orbitals.entries()).map(
@@ -2101,6 +1773,6 @@ function buildMatcher(src, listenerOrbital) {
2101
1773
  return (source) => !!source && source.orbital === wantedOrbital && source.trait === wantedTrait;
2102
1774
  }
2103
1775
 
2104
- export { LocalPersistenceAdapter, OrbitalServerRuntime, createOrbitalServerRuntime };
1776
+ export { OrbitalServerRuntime, createOrbitalServerRuntime };
2105
1777
  //# sourceMappingURL=OrbitalServerRuntime.js.map
2106
1778
  //# sourceMappingURL=OrbitalServerRuntime.js.map