@almadar/runtime 2.2.2 → 2.4.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,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-E5o2Jqe6.js';
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-BrbvZxzX.js';
3
3
  import { OrbitalSchema, Orbital, Trait } from '@almadar/core';
4
4
 
5
5
  /**
@@ -517,10 +517,10 @@ interface OrbitalEventResponse {
517
517
  */
518
518
  interface EffectResult {
519
519
  /** Effect type that was executed */
520
- effect: 'persist' | 'call-service' | 'set';
520
+ effect: 'persist' | 'call-service' | 'set' | 'ref' | 'deref' | 'swap' | 'atomic';
521
521
  /** Action performed (e.g., 'create', 'update', 'delete' for persist) */
522
522
  action?: string;
523
- /** Entity type affected (for persist/set) */
523
+ /** Entity type affected (for persist/set/ref/deref/swap) */
524
524
  entityType?: string;
525
525
  /** Result data from the effect */
526
526
  data?: Record<string, unknown>;
@@ -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-YLqyxdjz.js';
3
- import './types-E5o2Jqe6.js';
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-Bel-jQ_o.js';
3
+ import './types-BrbvZxzX.js';
4
4
  import '@almadar/core';
@@ -1,6 +1,6 @@
1
- import { EventBus, createUnifiedLoader, preprocessSchema, StateMachineManager, createContextFromBindings, EffectExecutor } from './chunk-57PUFAKH.js';
1
+ import { EventBus, createUnifiedLoader, preprocessSchema, StateMachineManager, createContextFromBindings, EffectExecutor } from './chunk-GCRRQAGZ.js';
2
2
  import { Router } from 'express';
3
- import { evaluateGuard } from '@almadar/evaluator';
3
+ import { evaluateGuard, evaluate } from '@almadar/evaluator';
4
4
  import { faker } from '@faker-js/faker';
5
5
  import * as fs from 'fs';
6
6
  import * as net from 'net';
@@ -1087,6 +1087,33 @@ var OrbitalServerRuntime = class {
1087
1087
  );
1088
1088
  }
1089
1089
  }
1090
+ const persistedTypes = /* @__PURE__ */ new Set();
1091
+ for (const er of effectResults) {
1092
+ if ((er.effect === "persist" || er.effect === "set" || er.effect === "swap") && er.success && er.entityType) {
1093
+ persistedTypes.add(er.entityType);
1094
+ }
1095
+ }
1096
+ const refTypes = /* @__PURE__ */ new Set();
1097
+ for (const trait of registered.schema.traits || []) {
1098
+ const sm = trait.stateMachine;
1099
+ const transitions = sm?.transitions ?? trait.transitions ?? [];
1100
+ for (const trans of transitions) {
1101
+ for (const eff of trans.effects ?? []) {
1102
+ if (Array.isArray(eff) && eff[0] === "ref" && typeof eff[1] === "string") {
1103
+ refTypes.add(eff[1]);
1104
+ }
1105
+ }
1106
+ }
1107
+ }
1108
+ for (const mutatedEntityType of persistedTypes) {
1109
+ if (refTypes.has(mutatedEntityType)) {
1110
+ try {
1111
+ const fresh = await this.persistence.list(mutatedEntityType);
1112
+ fetchedData[mutatedEntityType] = fresh;
1113
+ } catch {
1114
+ }
1115
+ }
1116
+ }
1090
1117
  const states = {};
1091
1118
  for (const [name, state] of registered.manager.getAllStates()) {
1092
1119
  states[name] = state.currentState;
@@ -1114,6 +1141,7 @@ var OrbitalServerRuntime = class {
1114
1141
  async executeEffects(registered, traitName, effects, payload, entityData, entityId, emittedEvents, fetchedData, clientEffects, effectResults, user) {
1115
1142
  const entityType = registered.schema.entity.name;
1116
1143
  let bindingsRef = null;
1144
+ let contextRef = null;
1117
1145
  const handlers = {
1118
1146
  emit: (event, eventPayload) => {
1119
1147
  if (this.config.debug) {
@@ -1336,6 +1364,149 @@ var OrbitalServerRuntime = class {
1336
1364
  return null;
1337
1365
  }
1338
1366
  },
1367
+ // Resource operators: ref, deref, swap, watch, atomic
1368
+ ref: async (refEntityType, options) => {
1369
+ try {
1370
+ return await handlers.fetch(refEntityType, options);
1371
+ } catch (error) {
1372
+ console.error(`[OrbitalRuntime] ref error for ${refEntityType}:`, error);
1373
+ return null;
1374
+ }
1375
+ },
1376
+ deref: async (derefEntityType, options) => {
1377
+ try {
1378
+ let result = null;
1379
+ if (options?.id) {
1380
+ const entity = await this.persistence.getById(derefEntityType, options.id);
1381
+ if (entity) {
1382
+ fetchedData[derefEntityType] = [entity];
1383
+ result = entity;
1384
+ }
1385
+ } else {
1386
+ const entities = await this.persistence.list(derefEntityType);
1387
+ fetchedData[derefEntityType] = entities;
1388
+ result = entities;
1389
+ }
1390
+ if (bindingsRef && result) {
1391
+ const records = Array.isArray(result) ? result : [result];
1392
+ if (records.length > 0) {
1393
+ const merged = Object.assign([...records], records[0]);
1394
+ bindingsRef[derefEntityType] = merged;
1395
+ if (derefEntityType === entityType) {
1396
+ bindingsRef.entity = merged;
1397
+ }
1398
+ }
1399
+ }
1400
+ effectResults.push({
1401
+ effect: "deref",
1402
+ entityType: derefEntityType,
1403
+ success: true
1404
+ });
1405
+ return result;
1406
+ } catch (error) {
1407
+ effectResults.push({
1408
+ effect: "deref",
1409
+ entityType: derefEntityType,
1410
+ success: false,
1411
+ error: error instanceof Error ? error.message : String(error)
1412
+ });
1413
+ return null;
1414
+ }
1415
+ },
1416
+ swap: async (swapEntityType, swapEntityId, transform) => {
1417
+ try {
1418
+ const current = await this.persistence.getById(swapEntityType, swapEntityId);
1419
+ if (!current) {
1420
+ effectResults.push({
1421
+ effect: "swap",
1422
+ entityType: swapEntityType,
1423
+ success: false,
1424
+ error: `Entity ${swapEntityType}/${swapEntityId} not found`
1425
+ });
1426
+ return null;
1427
+ }
1428
+ const ctx = createContextFromBindings({
1429
+ current,
1430
+ entity: entityData,
1431
+ payload
1432
+ });
1433
+ let newData;
1434
+ if (Array.isArray(transform)) {
1435
+ const result = evaluate(
1436
+ transform,
1437
+ ctx
1438
+ );
1439
+ if (result && typeof result === "object" && !Array.isArray(result)) {
1440
+ newData = result;
1441
+ } else {
1442
+ newData = current;
1443
+ }
1444
+ } else if (typeof transform === "object" && transform !== null) {
1445
+ newData = { ...current, ...transform };
1446
+ } else {
1447
+ effectResults.push({
1448
+ effect: "swap",
1449
+ entityType: swapEntityType,
1450
+ success: false,
1451
+ error: "swap! transform must be an S-expression or object"
1452
+ });
1453
+ return null;
1454
+ }
1455
+ await this.persistence.update(swapEntityType, swapEntityId, newData);
1456
+ effectResults.push({
1457
+ effect: "swap",
1458
+ entityType: swapEntityType,
1459
+ data: { id: swapEntityId, ...newData },
1460
+ success: true
1461
+ });
1462
+ return newData;
1463
+ } catch (error) {
1464
+ effectResults.push({
1465
+ effect: "swap",
1466
+ entityType: swapEntityType,
1467
+ success: false,
1468
+ error: error instanceof Error ? error.message : String(error)
1469
+ });
1470
+ return null;
1471
+ }
1472
+ },
1473
+ watch: (_watchEntityType, _watchOptions) => {
1474
+ if (this.config.debug) {
1475
+ console.log(`[OrbitalRuntime] watch is a no-op on server: ${_watchEntityType}`);
1476
+ }
1477
+ },
1478
+ atomic: async (atomicEffects) => {
1479
+ let atomicFailed = false;
1480
+ let atomicError = "";
1481
+ const atomicExecutor = new EffectExecutor({
1482
+ handlers,
1483
+ bindings: bindingsRef ?? {},
1484
+ context: contextRef ?? { traitName, state: "unknown", transition: "unknown" },
1485
+ debug: this.config.debug
1486
+ });
1487
+ for (const innerEffect of atomicEffects) {
1488
+ if (atomicFailed) break;
1489
+ try {
1490
+ await atomicExecutor.execute(innerEffect);
1491
+ } catch (err) {
1492
+ atomicFailed = true;
1493
+ atomicError = err instanceof Error ? err.message : String(err);
1494
+ }
1495
+ }
1496
+ if (atomicFailed) {
1497
+ effectResults.push({
1498
+ effect: "atomic",
1499
+ success: false,
1500
+ error: `Atomic block failed: ${atomicError}`
1501
+ });
1502
+ } else {
1503
+ effectResults.push({
1504
+ effect: "atomic",
1505
+ success: true,
1506
+ data: { innerCount: atomicEffects.length }
1507
+ });
1508
+ }
1509
+ },
1339
1510
  // Client-side effects - collect for forwarding to client
1340
1511
  renderUI: (slot, pattern, props, priority) => {
1341
1512
  clientEffects.push(["render-ui", slot, pattern, props, priority]);
@@ -1374,6 +1545,7 @@ var OrbitalServerRuntime = class {
1374
1545
  transition: "unknown",
1375
1546
  entityId
1376
1547
  };
1548
+ contextRef = context;
1377
1549
  const executor = new EffectExecutor({
1378
1550
  handlers,
1379
1551
  bindings,