@almadar/runtime 3.3.0 → 4.1.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, g as RuntimeEvent, f as EventListener, U as Unsubscribe, T as TraitDefinition, R as RuntimeConfig, i as TransitionObserver, h as TraitState, j as TransitionResult, E as EvaluationContextExtensions, a as EffectHandlers } from './types-B8OfRFfV.js';
2
+ import { I as IEventBus, g as RuntimeEvent, f as EventListener, U as Unsubscribe, T as TraitDefinition, R as RuntimeConfig, i as TransitionObserver, h as TraitState, j as TransitionResult, E as EvaluationContextExtensions, a as EffectHandlers } from './types-CVSBlnzV.js';
3
3
  import { EventPayload, EntityRow, OrbitalSchema, Orbital, Trait, OrbitalDefinition, Entity, TraitTick } from '@almadar/core';
4
4
 
5
5
  /**
@@ -748,14 +748,16 @@ interface OrbitalEventResponse {
748
748
  success: boolean;
749
749
  transitioned: boolean;
750
750
  states: Record<string, string>;
751
+ /**
752
+ * Events emitted during processing, in declaration order. Payloads are
753
+ * typed against `@almadar/core`'s `EventPayload` — the recursive record of
754
+ * primitive + nested EventPayload values the state machine already uses
755
+ * internally. Kept as the single source of truth for emit-payload shape.
756
+ */
751
757
  emittedEvents: Array<{
752
758
  event: string;
753
- payload?: unknown;
759
+ payload?: EventPayload;
754
760
  }>;
755
- /** Entity data fetched by `fetch` effects - keyed by entity type */
756
- data?: {
757
- [entityType: string]: EntityRow | EntityRow[];
758
- };
759
761
  /** Client-side effects to execute (render-ui, navigate, notify) */
760
762
  clientEffects?: Array<unknown>;
761
763
  /**
@@ -883,13 +885,30 @@ declare class OrbitalServerRuntime {
883
885
  private osHandlers;
884
886
  private localPersistence;
885
887
  constructor(config?: OrbitalServerRuntimeConfig);
888
+ /**
889
+ * Lazily construct a default loader when the caller didn't provide one
890
+ * but `register()` needs to preprocess. Looks for `@almadar/std` in the
891
+ * nearest `node_modules` so cross-orbital `std/behaviors/<name>` imports
892
+ * resolve to the tiered registry on disk.
893
+ *
894
+ * Node only — browsers should receive already-preprocessed schemas from
895
+ * their server.
896
+ */
897
+ private ensureLoader;
886
898
  /**
887
899
  * Register an OrbitalSchema for execution.
888
900
  *
889
- * If `autoPreprocess` is enabled in config and schema has `uses` declarations,
890
- * it will be preprocessed first to resolve imports.
901
+ * Auto-preprocesses the schema when it contains `uses` declarations or
902
+ * unresolved cross-orbital trait references (e.g. a trait with
903
+ * `ref: "Modal.traits.ModalRecordModal"` and no inline `stateMachine`).
904
+ * Without preprocessing, those refs arrive empty at the state machine and
905
+ * button clicks silently do nothing — see Phase 9.5.H.
891
906
  *
892
- * For explicit preprocessing control, use `registerWithPreprocess()`.
907
+ * Preprocessing needs a loader. If `loaderConfig` is set, that loader is
908
+ * used. Otherwise, a default loader is constructed that points at
909
+ * `<cwd>` (for `basePath`) and the nearest `node_modules/@almadar/std` (for
910
+ * `stdLibPath`), which matches how every caller in this monorepo has the
911
+ * std registry on disk.
893
912
  */
894
913
  register(schema: OrbitalSchema): Promise<void>;
895
914
  /**
@@ -1,4 +1,4 @@
1
1
  import 'express';
2
- export { B as EffectResult, C as LoaderConfig, D as LocalPersistenceAdapter, O as OrbitalEventRequest, e as OrbitalEventResponse, F as OrbitalServerRuntime, f as OrbitalServerRuntimeConfig, P as PersistenceAdapter, R as RegisteredOrbital, k as RuntimeOrbital, l as RuntimeOrbitalSchema, m as RuntimeTrait, G as RuntimeTraitTick, H as createOrbitalServerRuntime } from './OrbitalServerRuntime-DMRvEfcT.js';
3
- import './types-B8OfRFfV.js';
2
+ export { B as EffectResult, C as LoaderConfig, D as LocalPersistenceAdapter, O as OrbitalEventRequest, e as OrbitalEventResponse, F as OrbitalServerRuntime, f as OrbitalServerRuntimeConfig, P as PersistenceAdapter, R as RegisteredOrbital, k as RuntimeOrbital, l as RuntimeOrbitalSchema, m as RuntimeTrait, G as RuntimeTraitTick, H as createOrbitalServerRuntime } from './OrbitalServerRuntime-DXMHHVZI.js';
3
+ import './types-CVSBlnzV.js';
4
4
  import '@almadar/core';
@@ -1,4 +1,4 @@
1
- import { EventBus, createUnifiedLoader, preprocessSchema, StateMachineManager, createContextFromBindings, EffectExecutor } from './chunk-II3JSETH.js';
1
+ import { EventBus, createUnifiedLoader, preprocessSchema, StateMachineManager, createContextFromBindings, EffectExecutor } from './chunk-Z72NLOSF.js';
2
2
  import './chunk-PZ5AY32C.js';
3
3
  import { Router } from 'express';
4
4
  import * as fs from 'fs';
@@ -649,6 +649,23 @@ var InMemoryPersistence = class {
649
649
  return collection ? Array.from(collection.values()) : [];
650
650
  }
651
651
  };
652
+ function needsPreprocessing(schema) {
653
+ for (const orbital of schema.orbitals) {
654
+ const uses = orbital.uses;
655
+ if (Array.isArray(uses) && uses.length > 0) {
656
+ return true;
657
+ }
658
+ const traits = orbital.traits ?? [];
659
+ for (const t of traits) {
660
+ if (!t || typeof t !== "object") continue;
661
+ const obj = t;
662
+ if (typeof obj.ref === "string" && obj.ref.includes(".") && !obj.stateMachine) {
663
+ return true;
664
+ }
665
+ }
666
+ }
667
+ return false;
668
+ }
652
669
  var OrbitalServerRuntime = class {
653
670
  orbitals = /* @__PURE__ */ new Map();
654
671
  eventBus;
@@ -701,21 +718,102 @@ var OrbitalServerRuntime = class {
701
718
  ...this.config.effectHandlers
702
719
  };
703
720
  }
721
+ /**
722
+ * Lazily construct a default loader when the caller didn't provide one
723
+ * but `register()` needs to preprocess. Looks for `@almadar/std` in the
724
+ * nearest `node_modules` so cross-orbital `std/behaviors/<name>` imports
725
+ * resolve to the tiered registry on disk.
726
+ *
727
+ * Node only — browsers should receive already-preprocessed schemas from
728
+ * their server.
729
+ */
730
+ async ensureLoader() {
731
+ if (this.loader) return;
732
+ if (typeof process === "undefined" || !process.versions?.node) {
733
+ return;
734
+ }
735
+ try {
736
+ const [{ createRequire }, path2] = await Promise.all([
737
+ import('module'),
738
+ import('path')
739
+ ]);
740
+ const require2 = createRequire(import.meta.url);
741
+ const stdPkgJson = require2.resolve("@almadar/std/package.json");
742
+ const stdLibPath = path2.dirname(stdPkgJson);
743
+ const basePath = this.config.loaderConfig?.basePath ?? process.cwd();
744
+ this.loader = createUnifiedLoader({
745
+ basePath,
746
+ stdLibPath,
747
+ scopedPaths: this.config.loaderConfig?.scopedPaths
748
+ });
749
+ if (this.config.debug) {
750
+ console.log(
751
+ `[OrbitalRuntime] Default loader constructed: basePath=${basePath} stdLibPath=${stdLibPath}`
752
+ );
753
+ }
754
+ } catch (err) {
755
+ if (this.config.debug) {
756
+ console.warn(
757
+ `[OrbitalRuntime] Could not auto-construct loader: ${err instanceof Error ? err.message : String(err)}`
758
+ );
759
+ }
760
+ }
761
+ }
704
762
  // ==========================================================================
705
763
  // Schema Registration
706
764
  // ==========================================================================
707
765
  /**
708
766
  * Register an OrbitalSchema for execution.
709
767
  *
710
- * If `autoPreprocess` is enabled in config and schema has `uses` declarations,
711
- * it will be preprocessed first to resolve imports.
768
+ * Auto-preprocesses the schema when it contains `uses` declarations or
769
+ * unresolved cross-orbital trait references (e.g. a trait with
770
+ * `ref: "Modal.traits.ModalRecordModal"` and no inline `stateMachine`).
771
+ * Without preprocessing, those refs arrive empty at the state machine and
772
+ * button clicks silently do nothing — see Phase 9.5.H.
712
773
  *
713
- * For explicit preprocessing control, use `registerWithPreprocess()`.
774
+ * Preprocessing needs a loader. If `loaderConfig` is set, that loader is
775
+ * used. Otherwise, a default loader is constructed that points at
776
+ * `<cwd>` (for `basePath`) and the nearest `node_modules/@almadar/std` (for
777
+ * `stdLibPath`), which matches how every caller in this monorepo has the
778
+ * std registry on disk.
714
779
  */
715
780
  async register(schema) {
716
781
  if (this.config.debug) {
717
782
  console.log(`[OrbitalRuntime] Registering schema: ${schema.name}`);
718
783
  }
784
+ if (needsPreprocessing(schema)) {
785
+ await this.ensureLoader();
786
+ if (this.loader) {
787
+ if (this.config.debug) {
788
+ console.log(`[OrbitalRuntime] Schema has uses/refs \u2014 auto-preprocessing`);
789
+ }
790
+ const result = await preprocessSchema(schema, {
791
+ basePath: this.config.loaderConfig?.basePath || process.cwd(),
792
+ stdLibPath: this.config.loaderConfig?.stdLibPath,
793
+ scopedPaths: this.config.loaderConfig?.scopedPaths,
794
+ loader: this.loader,
795
+ namespaceEvents: this.config.namespaceEvents
796
+ });
797
+ if (!result.success) {
798
+ throw new Error(
799
+ `Schema preprocessing failed: ${result.errors.join("; ")}`
800
+ );
801
+ }
802
+ schema = result.data.schema;
803
+ this.entitySharingMap = {
804
+ ...this.entitySharingMap,
805
+ ...result.data.entitySharing
806
+ };
807
+ this.eventNamespaceMap = {
808
+ ...this.eventNamespaceMap,
809
+ ...result.data.eventNamespaces
810
+ };
811
+ } else if (this.config.debug) {
812
+ console.warn(
813
+ `[OrbitalRuntime] Schema has uses/refs but no loader available \u2014 proceeding without preprocessing. Cross-orbital trait refs will be empty.`
814
+ );
815
+ }
816
+ }
719
817
  for (const orbital of schema.orbitals) {
720
818
  await this.registerOrbitalAsync(orbital);
721
819
  }
@@ -953,10 +1051,12 @@ var OrbitalServerRuntime = class {
953
1051
  for (const trait of registered.traits) {
954
1052
  if (!trait.listens) continue;
955
1053
  for (const listener of trait.listens) {
956
- const cleanup = this.eventBus.on(listener.event, async (event) => {
1054
+ const { bareEvent, matcher } = parseListenSource(listener, orbitalName);
1055
+ const cleanup = this.eventBus.on(bareEvent, async (event) => {
1056
+ if (!matcher(event.source)) return;
957
1057
  if (this.config.debug) {
958
1058
  console.log(
959
- `[OrbitalRuntime] ${orbitalName}.${trait.name} received: ${listener.event}`
1059
+ `[OrbitalRuntime] ${orbitalName}.${trait.name} received: ${listener.event} (from ${event.source?.orbital ?? "?"}.${event.source?.trait ?? "?"})`
960
1060
  );
961
1061
  }
962
1062
  let mappedPayload = event.payload;
@@ -1223,32 +1323,6 @@ var OrbitalServerRuntime = class {
1223
1323
  );
1224
1324
  }
1225
1325
  }
1226
- const persistedTypes = /* @__PURE__ */ new Set();
1227
- for (const er of effectResults) {
1228
- if ((er.effect === "persist" || er.effect === "set" || er.effect === "swap") && er.success && er.entityType) {
1229
- persistedTypes.add(er.entityType);
1230
- }
1231
- }
1232
- const refTypes = /* @__PURE__ */ new Set();
1233
- for (const trait of registered.traits) {
1234
- const transitions = trait.stateMachine?.transitions ?? [];
1235
- for (const trans of transitions) {
1236
- for (const eff of trans.effects ?? []) {
1237
- if (Array.isArray(eff) && eff[0] === "ref" && typeof eff[1] === "string") {
1238
- refTypes.add(eff[1]);
1239
- }
1240
- }
1241
- }
1242
- }
1243
- for (const mutatedEntityType of persistedTypes) {
1244
- if (refTypes.has(mutatedEntityType)) {
1245
- try {
1246
- const fresh = await this.persistence.list(mutatedEntityType);
1247
- fetchedData[mutatedEntityType] = fresh;
1248
- } catch {
1249
- }
1250
- }
1251
- }
1252
1326
  const states = {};
1253
1327
  for (const [name, state] of registered.manager.getAllStates()) {
1254
1328
  states[name] = state.currentState;
@@ -1259,9 +1333,6 @@ var OrbitalServerRuntime = class {
1259
1333
  states,
1260
1334
  emittedEvents
1261
1335
  };
1262
- if (Object.keys(fetchedData).length > 0) {
1263
- response.data = fetchedData;
1264
- }
1265
1336
  if (clientEffects.length > 0) {
1266
1337
  response.clientEffects = clientEffects;
1267
1338
  }
@@ -1285,11 +1356,15 @@ var OrbitalServerRuntime = class {
1285
1356
  let bindingsRef = null;
1286
1357
  let contextRef = null;
1287
1358
  const handlers = {
1288
- emit: (event, eventPayload) => {
1359
+ emit: (event, eventPayload, source) => {
1289
1360
  if (this.config.debug) {
1290
- console.log(`[OrbitalRuntime] Emitting: ${event}`, eventPayload);
1361
+ console.log(`[OrbitalRuntime] Emitting: ${event}`, eventPayload, source);
1291
1362
  }
1292
- this.eventBus.emit(event, eventPayload);
1363
+ const stamp = source ?? {
1364
+ orbital: registered.schema.name,
1365
+ trait: traitName
1366
+ };
1367
+ this.eventBus.emit(event, eventPayload, stamp);
1293
1368
  emittedEvents.push({ event, payload: eventPayload });
1294
1369
  },
1295
1370
  set: async (targetId, field, value) => {
@@ -1623,7 +1698,7 @@ var OrbitalServerRuntime = class {
1623
1698
  const atomicExecutor = new EffectExecutor({
1624
1699
  handlers,
1625
1700
  bindings: bindingsRef ?? {},
1626
- context: contextRef ?? { traitName, state: "unknown", transition: "unknown" },
1701
+ context: contextRef ?? { traitName, orbitalName: registered.schema.name, state: "unknown", transition: "unknown" },
1627
1702
  debug: this.config.debug,
1628
1703
  contextExtensions: this.config.contextExtensions
1629
1704
  });
@@ -1684,6 +1759,7 @@ var OrbitalServerRuntime = class {
1684
1759
  bindingsRef = bindings;
1685
1760
  const context = {
1686
1761
  traitName,
1762
+ orbitalName: registered.schema.name,
1687
1763
  state: state?.currentState || "unknown",
1688
1764
  transition: "unknown",
1689
1765
  entityId
@@ -2029,6 +2105,53 @@ var OrbitalServerRuntime = class {
2029
2105
  function createOrbitalServerRuntime(config) {
2030
2106
  return new OrbitalServerRuntime(config);
2031
2107
  }
2108
+ function parseListenSource(listener, listenerOrbital) {
2109
+ const explicit = listener.source;
2110
+ if (explicit && typeof explicit === "object") {
2111
+ return {
2112
+ bareEvent: listener.event,
2113
+ matcher: buildMatcher(explicit, listenerOrbital)
2114
+ };
2115
+ }
2116
+ const key = listener.event;
2117
+ const parts = key.split(".");
2118
+ if (parts.length === 1) {
2119
+ return { bareEvent: key, matcher: () => true };
2120
+ }
2121
+ if (parts.length === 2) {
2122
+ const [sourceOrStar, eventName] = parts;
2123
+ if (sourceOrStar === "*") {
2124
+ return { bareEvent: eventName, matcher: () => true };
2125
+ }
2126
+ return {
2127
+ bareEvent: eventName,
2128
+ matcher: buildMatcher(
2129
+ { kind: "trait", trait: sourceOrStar },
2130
+ listenerOrbital
2131
+ )
2132
+ };
2133
+ }
2134
+ if (parts.length >= 3) {
2135
+ const eventName = parts[parts.length - 1];
2136
+ const trait = parts[parts.length - 2];
2137
+ const orbital = parts.slice(0, parts.length - 2).join(".");
2138
+ return {
2139
+ bareEvent: eventName,
2140
+ matcher: buildMatcher({ kind: "orbital", orbital, trait }, listenerOrbital)
2141
+ };
2142
+ }
2143
+ return { bareEvent: key, matcher: () => true };
2144
+ }
2145
+ function buildMatcher(src, listenerOrbital) {
2146
+ if (src.kind === "any") return () => true;
2147
+ if (src.kind === "trait") {
2148
+ const wantedTrait2 = src.trait;
2149
+ return (source) => !!source && source.orbital === listenerOrbital && source.trait === wantedTrait2;
2150
+ }
2151
+ const wantedOrbital = src.orbital;
2152
+ const wantedTrait = src.trait;
2153
+ return (source) => !!source && source.orbital === wantedOrbital && source.trait === wantedTrait;
2154
+ }
2032
2155
 
2033
2156
  export { LocalPersistenceAdapter, OrbitalServerRuntime, createOrbitalServerRuntime };
2034
2157
  //# sourceMappingURL=OrbitalServerRuntime.js.map