@almadar/runtime 2.1.2 → 2.2.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-MBjDCrt8.d.ts → OrbitalServerRuntime-YLqyxdjz.d.ts} +2 -1
- package/dist/OrbitalServerRuntime.d.ts +2 -2
- package/dist/OrbitalServerRuntime.js +268 -1
- package/dist/OrbitalServerRuntime.js.map +1 -1
- package/dist/ServerBridge.d.ts +1 -1
- package/dist/{chunk-UBXKXCSM.js → chunk-54P2HYHQ.js} +70 -3
- package/dist/chunk-54P2HYHQ.js.map +1 -0
- package/dist/index.d.ts +27 -4
- package/dist/index.js +1 -1
- package/dist/{types-9hbY6RWC.d.ts → types-E5o2Jqe6.d.ts} +16 -0
- package/package.json +6 -1
- package/dist/chunk-UBXKXCSM.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
|
-
import { I as IEventBus, i as RuntimeEvent, h as EventListener, U as Unsubscribe, E as EffectHandlers, g as Effect, T as TraitState } from './types-
|
|
2
|
+
import { I as IEventBus, i as RuntimeEvent, h as EventListener, U as Unsubscribe, E as EffectHandlers, g as Effect, T as TraitState } from './types-E5o2Jqe6.js';
|
|
3
3
|
import { OrbitalSchema, Orbital, Trait } from '@almadar/core';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -602,6 +602,7 @@ declare class OrbitalServerRuntime {
|
|
|
602
602
|
private preprocessedCache;
|
|
603
603
|
private entitySharingMap;
|
|
604
604
|
private eventNamespaceMap;
|
|
605
|
+
private osHandlers;
|
|
605
606
|
constructor(config?: OrbitalServerRuntimeConfig);
|
|
606
607
|
/**
|
|
607
608
|
* Register an OrbitalSchema for execution.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import 'express';
|
|
2
|
-
export { n as EffectResult, L as LoaderConfig, O as OrbitalEventRequest, c as OrbitalEventResponse, o as OrbitalServerRuntime, d as OrbitalServerRuntimeConfig, P as PersistenceAdapter, R as RuntimeOrbital, h as RuntimeOrbitalSchema, i as RuntimeTrait, q as RuntimeTraitTick, r as createOrbitalServerRuntime } from './OrbitalServerRuntime-
|
|
3
|
-
import './types-
|
|
2
|
+
export { n as EffectResult, L as LoaderConfig, O as OrbitalEventRequest, c as OrbitalEventResponse, o as OrbitalServerRuntime, d as OrbitalServerRuntimeConfig, P as PersistenceAdapter, R as RuntimeOrbital, h as RuntimeOrbitalSchema, i as RuntimeTrait, q as RuntimeTraitTick, r as createOrbitalServerRuntime } from './OrbitalServerRuntime-YLqyxdjz.js';
|
|
3
|
+
import './types-E5o2Jqe6.js';
|
|
4
4
|
import '@almadar/core';
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import { EventBus, createUnifiedLoader, preprocessSchema, StateMachineManager, createContextFromBindings, EffectExecutor } from './chunk-
|
|
1
|
+
import { EventBus, createUnifiedLoader, preprocessSchema, StateMachineManager, createContextFromBindings, EffectExecutor } from './chunk-54P2HYHQ.js';
|
|
2
2
|
import { Router } from 'express';
|
|
3
3
|
import { evaluateGuard } from '@almadar/evaluator';
|
|
4
4
|
import { faker } from '@faker-js/faker';
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import * as net from 'net';
|
|
7
|
+
import { execSync } from 'child_process';
|
|
5
8
|
|
|
6
9
|
var MockPersistenceAdapter = class {
|
|
7
10
|
stores = /* @__PURE__ */ new Map();
|
|
@@ -263,6 +266,258 @@ var MockPersistenceAdapter = class {
|
|
|
263
266
|
return store.size;
|
|
264
267
|
}
|
|
265
268
|
};
|
|
269
|
+
function globToRegex(glob) {
|
|
270
|
+
let regex = "";
|
|
271
|
+
let i = 0;
|
|
272
|
+
while (i < glob.length) {
|
|
273
|
+
const c = glob[i];
|
|
274
|
+
if (c === "*") {
|
|
275
|
+
if (glob[i + 1] === "*") {
|
|
276
|
+
regex += ".*";
|
|
277
|
+
i += 2;
|
|
278
|
+
if (glob[i] === "/") i++;
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
regex += "[^/]*";
|
|
282
|
+
} else if (c === "?") {
|
|
283
|
+
regex += "[^/]";
|
|
284
|
+
} else if (c === ".") {
|
|
285
|
+
regex += "\\.";
|
|
286
|
+
} else if (c === "/" || c === "-" || c === "_") {
|
|
287
|
+
regex += c;
|
|
288
|
+
} else if (/[{}()[\]^$+|\\]/.test(c)) {
|
|
289
|
+
regex += "\\" + c;
|
|
290
|
+
} else {
|
|
291
|
+
regex += c;
|
|
292
|
+
}
|
|
293
|
+
i++;
|
|
294
|
+
}
|
|
295
|
+
return new RegExp("^" + regex + "$");
|
|
296
|
+
}
|
|
297
|
+
function parseCronField(field, min, max) {
|
|
298
|
+
const values = /* @__PURE__ */ new Set();
|
|
299
|
+
for (const part of field.split(",")) {
|
|
300
|
+
if (part === "*") {
|
|
301
|
+
for (let i = min; i <= max; i++) values.add(i);
|
|
302
|
+
} else if (part.includes("/")) {
|
|
303
|
+
const [range, stepStr] = part.split("/");
|
|
304
|
+
const step = parseInt(stepStr, 10);
|
|
305
|
+
const start = range === "*" ? min : parseInt(range, 10);
|
|
306
|
+
for (let i = start; i <= max; i += step) values.add(i);
|
|
307
|
+
} else if (part.includes("-")) {
|
|
308
|
+
const [lo, hi] = part.split("-").map(Number);
|
|
309
|
+
for (let i = lo; i <= hi; i++) values.add(i);
|
|
310
|
+
} else {
|
|
311
|
+
values.add(parseInt(part, 10));
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return values;
|
|
315
|
+
}
|
|
316
|
+
function parseCron(expression) {
|
|
317
|
+
const parts = expression.trim().split(/\s+/);
|
|
318
|
+
if (parts.length !== 5) {
|
|
319
|
+
throw new Error(`Invalid cron expression (expected 5 fields): ${expression}`);
|
|
320
|
+
}
|
|
321
|
+
return {
|
|
322
|
+
minute: parseCronField(parts[0], 0, 59),
|
|
323
|
+
hour: parseCronField(parts[1], 0, 23),
|
|
324
|
+
day: parseCronField(parts[2], 1, 31),
|
|
325
|
+
month: parseCronField(parts[3], 1, 12),
|
|
326
|
+
weekday: parseCronField(parts[4], 0, 6)
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
function cronMatches(fields, date) {
|
|
330
|
+
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());
|
|
331
|
+
}
|
|
332
|
+
function createOsHandlers(ctx) {
|
|
333
|
+
const cwd = ctx.cwd ?? process.cwd();
|
|
334
|
+
const watchers = [];
|
|
335
|
+
const intervals = [];
|
|
336
|
+
const signalHandlers = [];
|
|
337
|
+
let httpWatchActive = false;
|
|
338
|
+
const debounceConfig = /* @__PURE__ */ new Map();
|
|
339
|
+
const debounceTimers = /* @__PURE__ */ new Map();
|
|
340
|
+
function debouncedEmit(eventType, payload) {
|
|
341
|
+
const ms = debounceConfig.get(eventType);
|
|
342
|
+
if (ms !== void 0 && ms > 0) {
|
|
343
|
+
const existing = debounceTimers.get(eventType);
|
|
344
|
+
if (existing) clearTimeout(existing);
|
|
345
|
+
debounceTimers.set(
|
|
346
|
+
eventType,
|
|
347
|
+
setTimeout(() => {
|
|
348
|
+
debounceTimers.delete(eventType);
|
|
349
|
+
ctx.emitEvent(eventType, payload);
|
|
350
|
+
}, ms)
|
|
351
|
+
);
|
|
352
|
+
} else {
|
|
353
|
+
ctx.emitEvent(eventType, payload);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
const handlers = {
|
|
357
|
+
osWatchFiles: (glob, options) => {
|
|
358
|
+
const recursive = options.recursive !== false;
|
|
359
|
+
const pattern = globToRegex(glob);
|
|
360
|
+
try {
|
|
361
|
+
const watcher = fs.watch(cwd, { recursive }, (_event, filename) => {
|
|
362
|
+
if (filename && pattern.test(filename)) {
|
|
363
|
+
debouncedEmit("OS_FILE_MODIFIED", {
|
|
364
|
+
file: filename,
|
|
365
|
+
glob,
|
|
366
|
+
cwd
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
watchers.push(watcher);
|
|
371
|
+
} catch (err) {
|
|
372
|
+
console.warn("[os/watch-files] Failed to start watcher:", err);
|
|
373
|
+
}
|
|
374
|
+
},
|
|
375
|
+
osWatchProcess: (name, subcommand) => {
|
|
376
|
+
const searchTerm = subcommand ? `${name} ${subcommand}` : name;
|
|
377
|
+
let wasRunning = false;
|
|
378
|
+
const interval = setInterval(() => {
|
|
379
|
+
let isRunning = false;
|
|
380
|
+
try {
|
|
381
|
+
const result = execSync(`pgrep -f "${searchTerm}" 2>/dev/null`, {
|
|
382
|
+
encoding: "utf-8",
|
|
383
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
384
|
+
});
|
|
385
|
+
isRunning = result.trim().length > 0;
|
|
386
|
+
} catch {
|
|
387
|
+
isRunning = false;
|
|
388
|
+
}
|
|
389
|
+
if (isRunning && !wasRunning) {
|
|
390
|
+
debouncedEmit("OS_PROCESS_STARTED", { process: name, subcommand: subcommand ?? null });
|
|
391
|
+
} else if (!isRunning && wasRunning) {
|
|
392
|
+
debouncedEmit("OS_PROCESS_EXITED", { process: name, subcommand: subcommand ?? null });
|
|
393
|
+
}
|
|
394
|
+
wasRunning = isRunning;
|
|
395
|
+
}, 2e3);
|
|
396
|
+
intervals.push(interval);
|
|
397
|
+
},
|
|
398
|
+
osWatchPort: (port, protocol) => {
|
|
399
|
+
if (protocol !== "tcp") {
|
|
400
|
+
console.warn(`[os/watch-port] Only TCP is supported, got: ${protocol}`);
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
let wasOpen = false;
|
|
404
|
+
const interval = setInterval(() => {
|
|
405
|
+
const socket = new net.Socket();
|
|
406
|
+
socket.setTimeout(1e3);
|
|
407
|
+
socket.on("connect", () => {
|
|
408
|
+
socket.destroy();
|
|
409
|
+
if (!wasOpen) {
|
|
410
|
+
wasOpen = true;
|
|
411
|
+
debouncedEmit("OS_PORT_OPENED", { port, protocol });
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
socket.on("error", () => {
|
|
415
|
+
socket.destroy();
|
|
416
|
+
if (wasOpen) {
|
|
417
|
+
wasOpen = false;
|
|
418
|
+
debouncedEmit("OS_PORT_CLOSED", { port, protocol });
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
socket.on("timeout", () => {
|
|
422
|
+
socket.destroy();
|
|
423
|
+
if (wasOpen) {
|
|
424
|
+
wasOpen = false;
|
|
425
|
+
debouncedEmit("OS_PORT_CLOSED", { port, protocol });
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
socket.connect(port, "127.0.0.1");
|
|
429
|
+
}, 3e3);
|
|
430
|
+
intervals.push(interval);
|
|
431
|
+
},
|
|
432
|
+
osWatchHttp: (urlPattern, method) => {
|
|
433
|
+
if (!httpWatchActive) {
|
|
434
|
+
httpWatchActive = true;
|
|
435
|
+
console.warn(
|
|
436
|
+
`[os/watch-http] HTTP interception is only supported in compiled mode. Pattern: ${urlPattern}${method ? `, method: ${method}` : ""}`
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
},
|
|
440
|
+
osWatchCron: (expression) => {
|
|
441
|
+
let fields;
|
|
442
|
+
try {
|
|
443
|
+
fields = parseCron(expression);
|
|
444
|
+
} catch (err) {
|
|
445
|
+
console.warn("[os/watch-cron] Invalid expression:", err);
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
let lastFired = -1;
|
|
449
|
+
const interval = setInterval(() => {
|
|
450
|
+
const now = /* @__PURE__ */ new Date();
|
|
451
|
+
const minuteKey = now.getFullYear() * 1e8 + now.getMonth() * 1e6 + now.getDate() * 1e4 + now.getHours() * 100 + now.getMinutes();
|
|
452
|
+
if (minuteKey !== lastFired && cronMatches(fields, now)) {
|
|
453
|
+
lastFired = minuteKey;
|
|
454
|
+
debouncedEmit("OS_CRON_FIRE", {
|
|
455
|
+
expression,
|
|
456
|
+
firedAt: now.toISOString()
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
}, 1e3);
|
|
460
|
+
intervals.push(interval);
|
|
461
|
+
},
|
|
462
|
+
osWatchSignal: (signal) => {
|
|
463
|
+
const sig = signal.toUpperCase();
|
|
464
|
+
const handler = () => {
|
|
465
|
+
debouncedEmit(`OS_SIGNAL_${sig}`, { signal: sig });
|
|
466
|
+
};
|
|
467
|
+
try {
|
|
468
|
+
process.on(sig, handler);
|
|
469
|
+
signalHandlers.push({ signal: sig, handler });
|
|
470
|
+
} catch (err) {
|
|
471
|
+
console.warn(`[os/watch-signal] Cannot listen for ${sig}:`, err);
|
|
472
|
+
}
|
|
473
|
+
},
|
|
474
|
+
osWatchEnv: (variable) => {
|
|
475
|
+
let lastValue = process.env[variable];
|
|
476
|
+
const interval = setInterval(() => {
|
|
477
|
+
const current = process.env[variable];
|
|
478
|
+
if (current !== lastValue) {
|
|
479
|
+
const previous = lastValue;
|
|
480
|
+
lastValue = current;
|
|
481
|
+
debouncedEmit("OS_ENV_CHANGED", {
|
|
482
|
+
variable,
|
|
483
|
+
value: current ?? null,
|
|
484
|
+
previous: previous ?? null
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
}, 1e3);
|
|
488
|
+
intervals.push(interval);
|
|
489
|
+
},
|
|
490
|
+
osDebounce: (ms, eventType) => {
|
|
491
|
+
debounceConfig.set(eventType, ms);
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
function cleanup() {
|
|
495
|
+
for (const w of watchers) {
|
|
496
|
+
try {
|
|
497
|
+
w.close();
|
|
498
|
+
} catch {
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
watchers.length = 0;
|
|
502
|
+
for (const i of intervals) {
|
|
503
|
+
clearInterval(i);
|
|
504
|
+
}
|
|
505
|
+
intervals.length = 0;
|
|
506
|
+
for (const { signal, handler } of signalHandlers) {
|
|
507
|
+
try {
|
|
508
|
+
process.removeListener(signal, handler);
|
|
509
|
+
} catch {
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
signalHandlers.length = 0;
|
|
513
|
+
httpWatchActive = false;
|
|
514
|
+
for (const timer of debounceTimers.values()) {
|
|
515
|
+
clearTimeout(timer);
|
|
516
|
+
}
|
|
517
|
+
debounceTimers.clear();
|
|
518
|
+
}
|
|
519
|
+
return { handlers, cleanup };
|
|
520
|
+
}
|
|
266
521
|
|
|
267
522
|
// src/OrbitalServerRuntime.ts
|
|
268
523
|
var InMemoryPersistence = class {
|
|
@@ -305,6 +560,7 @@ var OrbitalServerRuntime = class {
|
|
|
305
560
|
preprocessedCache = /* @__PURE__ */ new Map();
|
|
306
561
|
entitySharingMap = {};
|
|
307
562
|
eventNamespaceMap = {};
|
|
563
|
+
osHandlers = null;
|
|
308
564
|
constructor(config = {}) {
|
|
309
565
|
this.config = {
|
|
310
566
|
mode: "mock",
|
|
@@ -333,6 +589,13 @@ var OrbitalServerRuntime = class {
|
|
|
333
589
|
} else {
|
|
334
590
|
this.persistence = config.persistence || new InMemoryPersistence();
|
|
335
591
|
}
|
|
592
|
+
this.osHandlers = createOsHandlers({
|
|
593
|
+
emitEvent: (type, payload) => this.eventBus.emit(type, payload)
|
|
594
|
+
});
|
|
595
|
+
this.config.effectHandlers = {
|
|
596
|
+
...this.osHandlers.handlers,
|
|
597
|
+
...this.config.effectHandlers
|
|
598
|
+
};
|
|
336
599
|
}
|
|
337
600
|
// ==========================================================================
|
|
338
601
|
// Schema Registration
|
|
@@ -760,6 +1023,10 @@ var OrbitalServerRuntime = class {
|
|
|
760
1023
|
this.listenerCleanups = [];
|
|
761
1024
|
this.orbitals.clear();
|
|
762
1025
|
this.eventBus.clear();
|
|
1026
|
+
if (this.osHandlers) {
|
|
1027
|
+
this.osHandlers.cleanup();
|
|
1028
|
+
this.osHandlers = null;
|
|
1029
|
+
}
|
|
763
1030
|
}
|
|
764
1031
|
// ==========================================================================
|
|
765
1032
|
// Event Processing
|