@almadar/runtime 4.0.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
  /**
@@ -885,13 +885,30 @@ declare class OrbitalServerRuntime {
885
885
  private osHandlers;
886
886
  private localPersistence;
887
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;
888
898
  /**
889
899
  * Register an OrbitalSchema for execution.
890
900
  *
891
- * If `autoPreprocess` is enabled in config and schema has `uses` declarations,
892
- * 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.
893
906
  *
894
- * 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.
895
912
  */
896
913
  register(schema: OrbitalSchema): Promise<void>;
897
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-CXfyKGAS.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-WIWVHE3P.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;
@@ -1256,11 +1356,15 @@ var OrbitalServerRuntime = class {
1256
1356
  let bindingsRef = null;
1257
1357
  let contextRef = null;
1258
1358
  const handlers = {
1259
- emit: (event, eventPayload) => {
1359
+ emit: (event, eventPayload, source) => {
1260
1360
  if (this.config.debug) {
1261
- console.log(`[OrbitalRuntime] Emitting: ${event}`, eventPayload);
1361
+ console.log(`[OrbitalRuntime] Emitting: ${event}`, eventPayload, source);
1262
1362
  }
1263
- 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);
1264
1368
  emittedEvents.push({ event, payload: eventPayload });
1265
1369
  },
1266
1370
  set: async (targetId, field, value) => {
@@ -1594,7 +1698,7 @@ var OrbitalServerRuntime = class {
1594
1698
  const atomicExecutor = new EffectExecutor({
1595
1699
  handlers,
1596
1700
  bindings: bindingsRef ?? {},
1597
- context: contextRef ?? { traitName, state: "unknown", transition: "unknown" },
1701
+ context: contextRef ?? { traitName, orbitalName: registered.schema.name, state: "unknown", transition: "unknown" },
1598
1702
  debug: this.config.debug,
1599
1703
  contextExtensions: this.config.contextExtensions
1600
1704
  });
@@ -1655,6 +1759,7 @@ var OrbitalServerRuntime = class {
1655
1759
  bindingsRef = bindings;
1656
1760
  const context = {
1657
1761
  traitName,
1762
+ orbitalName: registered.schema.name,
1658
1763
  state: state?.currentState || "unknown",
1659
1764
  transition: "unknown",
1660
1765
  entityId
@@ -2000,6 +2105,53 @@ var OrbitalServerRuntime = class {
2000
2105
  function createOrbitalServerRuntime(config) {
2001
2106
  return new OrbitalServerRuntime(config);
2002
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
+ }
2003
2155
 
2004
2156
  export { LocalPersistenceAdapter, OrbitalServerRuntime, createOrbitalServerRuntime };
2005
2157
  //# sourceMappingURL=OrbitalServerRuntime.js.map