@almadar/runtime 5.6.1 → 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.
@@ -1,368 +1,36 @@
1
1
  import { createLogger, EventBus, createUnifiedLoader, MockPersistenceAdapter, InMemoryPersistence, preprocessSchema, StateMachineManager, createContextFromBindings, validateEventPayload, formatPayloadValidationError, EffectExecutor } from './chunk-54YR5TKO.js';
2
2
  export { InMemoryPersistence } from './chunk-54YR5TKO.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;
105
- } 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
- );
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;
167
14
  } 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
- }
339
- }
340
- watchers.length = 0;
341
- for (const i of intervals) {
342
- clearInterval(i);
21
+ _resolvedNodeRequire = createReq(import.meta.url);
343
22
  }
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");
365
30
  var xOrbitalLog = createLogger("almadar:runtime:cross-orbital");
31
+ function isNodeEnv() {
32
+ return typeof process !== "undefined" && Boolean(process.versions?.node);
33
+ }
366
34
  function collectDeclaredConfigDefaults(trait) {
367
35
  if (!trait) return void 0;
368
36
  const schema = trait.config;
@@ -441,12 +109,19 @@ var OrbitalServerRuntime = class {
441
109
  } else {
442
110
  this.persistence = config.persistence || new InMemoryPersistence();
443
111
  }
444
- if (config.localStorageRoot) {
112
+ if (config.localStorageRoot && isNodeEnv()) {
113
+ const { LocalPersistenceAdapter } = nodeRequire(`./LocalPersistenceAdapter${_nodeRequireExt}`);
445
114
  this.localPersistence = new LocalPersistenceAdapter(config.localStorageRoot);
446
115
  }
447
- this.osHandlers = createOsHandlers({
448
- emitEvent: (type, payload) => this.eventBus.emit(type, payload)
449
- });
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
+ }
450
125
  this.config.effectHandlers = {
451
126
  ...this.osHandlers.handlers,
452
127
  ...this.config.effectHandlers
@@ -467,22 +142,22 @@ var OrbitalServerRuntime = class {
467
142
  return;
468
143
  }
469
144
  try {
470
- const [{ fileURLToPath }, path2, fs3] = await Promise.all([
145
+ const [{ fileURLToPath }, path, fs] = await Promise.all([
471
146
  import('url'),
472
147
  import('path'),
473
148
  import('fs')
474
149
  ]);
475
150
  const mainEntryUrl = import.meta.resolve("@almadar/std");
476
151
  const mainEntry = fileURLToPath(mainEntryUrl);
477
- let stdLibPath = path2.dirname(mainEntry);
478
- while (stdLibPath !== path2.dirname(stdLibPath)) {
479
- 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"))) {
480
155
  const pkg = JSON.parse(
481
- fs3.readFileSync(path2.join(stdLibPath, "package.json"), "utf-8")
156
+ fs.readFileSync(path.join(stdLibPath, "package.json"), "utf-8")
482
157
  );
483
158
  if (pkg.name === "@almadar/std") break;
484
159
  }
485
- stdLibPath = path2.dirname(stdLibPath);
160
+ stdLibPath = path.dirname(stdLibPath);
486
161
  }
487
162
  const basePath = this.config.loaderConfig?.basePath ?? process.cwd();
488
163
  this.loader = createUnifiedLoader({
@@ -601,20 +276,20 @@ var OrbitalServerRuntime = class {
601
276
  * Node only. Browsers must receive already-resolved schemas from their
602
277
  * server (see `getResolvedSchema()`).
603
278
  */
604
- async registerFromFile(path2) {
279
+ async registerFromFile(path) {
605
280
  if (typeof process === "undefined" || !process.versions?.node) {
606
281
  throw new Error(
607
282
  "registerFromFile is Node-only. Browsers should receive resolved schemas from their server."
608
283
  );
609
284
  }
610
285
  const { readFile } = await import('fs/promises');
611
- const raw = await readFile(path2, "utf-8");
286
+ const raw = await readFile(path, "utf-8");
612
287
  let schema;
613
288
  try {
614
289
  schema = JSON.parse(raw);
615
290
  } catch (err) {
616
291
  const msg = err instanceof Error ? err.message : String(err);
617
- throw new Error(`registerFromFile: ${path2} is not valid JSON: ${msg}`);
292
+ throw new Error(`registerFromFile: ${path} is not valid JSON: ${msg}`);
618
293
  }
619
294
  await this.register(schema);
620
295
  }
@@ -1654,8 +1329,8 @@ var OrbitalServerRuntime = class {
1654
1329
  });
1655
1330
  pushClientEffect(["render-ui", slot, pattern, props, priority]);
1656
1331
  },
1657
- navigate: (path2, params) => {
1658
- pushClientEffect(["navigate", path2, params]);
1332
+ navigate: (path, params) => {
1333
+ pushClientEffect(["navigate", path, params]);
1659
1334
  },
1660
1335
  notify: (message, type) => {
1661
1336
  if (this.config.debug) {
@@ -1934,6 +1609,12 @@ var OrbitalServerRuntime = class {
1934
1609
  * - POST /:orbital/events - Send event to orbital (includes data from `fetch` effects)
1935
1610
  */
1936
1611
  router() {
1612
+ if (!isNodeEnv()) {
1613
+ throw new Error(
1614
+ "OrbitalServerRuntime.router() is Node-only (uses Express). For in-browser use, mount <BrowserPlayground> from @almadar/ui instead."
1615
+ );
1616
+ }
1617
+ const { Router } = nodeRequire("express");
1937
1618
  const router = Router();
1938
1619
  router.get("/", (_req, res) => {
1939
1620
  const orbitals = Array.from(this.orbitals.entries()).map(
@@ -2092,6 +1773,6 @@ function buildMatcher(src, listenerOrbital) {
2092
1773
  return (source) => !!source && source.orbital === wantedOrbital && source.trait === wantedTrait;
2093
1774
  }
2094
1775
 
2095
- export { LocalPersistenceAdapter, OrbitalServerRuntime, createOrbitalServerRuntime };
1776
+ export { OrbitalServerRuntime, createOrbitalServerRuntime };
2096
1777
  //# sourceMappingURL=OrbitalServerRuntime.js.map
2097
1778
  //# sourceMappingURL=OrbitalServerRuntime.js.map