@almadar/runtime 4.0.1 → 4.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.
@@ -884,14 +884,32 @@ declare class OrbitalServerRuntime {
884
884
  private eventNamespaceMap;
885
885
  private osHandlers;
886
886
  private localPersistence;
887
+ private resolvedSchema;
887
888
  constructor(config?: OrbitalServerRuntimeConfig);
889
+ /**
890
+ * Lazily construct a default loader when the caller didn't provide one
891
+ * but `register()` needs to preprocess. Looks for `@almadar/std` in the
892
+ * nearest `node_modules` so cross-orbital `std/behaviors/<name>` imports
893
+ * resolve to the tiered registry on disk.
894
+ *
895
+ * Node only — browsers should receive already-preprocessed schemas from
896
+ * their server.
897
+ */
898
+ private ensureLoader;
888
899
  /**
889
900
  * Register an OrbitalSchema for execution.
890
901
  *
891
- * If `autoPreprocess` is enabled in config and schema has `uses` declarations,
892
- * it will be preprocessed first to resolve imports.
902
+ * Auto-preprocesses the schema when it contains `uses` declarations or
903
+ * unresolved cross-orbital trait references (e.g. a trait with
904
+ * `ref: "Modal.traits.ModalRecordModal"` and no inline `stateMachine`).
905
+ * Without preprocessing, those refs arrive empty at the state machine and
906
+ * button clicks silently do nothing — see Phase 9.5.H.
893
907
  *
894
- * For explicit preprocessing control, use `registerWithPreprocess()`.
908
+ * Preprocessing needs a loader. If `loaderConfig` is set, that loader is
909
+ * used. Otherwise, a default loader is constructed that points at
910
+ * `<cwd>` (for `basePath`) and the nearest `node_modules/@almadar/std` (for
911
+ * `stdLibPath`), which matches how every caller in this monorepo has the
912
+ * std registry on disk.
895
913
  */
896
914
  register(schema: OrbitalSchema): Promise<void>;
897
915
  /**
@@ -900,6 +918,25 @@ declare class OrbitalServerRuntime {
900
918
  * Use async register() for guaranteed instance seeding.
901
919
  */
902
920
  registerSync(schema: OrbitalSchema): void;
921
+ /**
922
+ * Returns the schema that this runtime is currently executing, post-
923
+ * preprocessing. Safe to expose from an HTTP `/api/schema` endpoint — every
924
+ * cross-orbital trait ref will have an inline `stateMachine` already, which
925
+ * is what the browser's `schema-to-ir` resolver needs to wire button clicks
926
+ * back to state transitions.
927
+ *
928
+ * Returns `null` if `register()` hasn't run yet.
929
+ */
930
+ getResolvedSchema(): OrbitalSchema | null;
931
+ /**
932
+ * One-call entry point: read an `.orb` file from disk, parse it, preprocess
933
+ * cross-orbital imports, and register the result. Callers never touch raw
934
+ * `.orb` bytes — `register()` handles preprocessing internally.
935
+ *
936
+ * Node only. Browsers must receive already-resolved schemas from their
937
+ * server (see `getResolvedSchema()`).
938
+ */
939
+ registerFromFile(path: string): Promise<void>;
903
940
  /**
904
941
  * Register an OrbitalSchema with preprocessing to resolve `uses` imports.
905
942
  *
@@ -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-DMe0zqCc.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-CFMJku6Q.js';
3
3
  import './types-CVSBlnzV.js';
4
4
  import '@almadar/core';
@@ -1,4 +1,4 @@
1
- import { EventBus, createUnifiedLoader, preprocessSchema, StateMachineManager, createContextFromBindings, EffectExecutor } from './chunk-7WP4AE6X.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;
@@ -662,6 +679,7 @@ var OrbitalServerRuntime = class {
662
679
  eventNamespaceMap = {};
663
680
  osHandlers = null;
664
681
  localPersistence = null;
682
+ resolvedSchema = null;
665
683
  constructor(config = {}) {
666
684
  this.config = {
667
685
  mode: "mock",
@@ -701,26 +719,108 @@ var OrbitalServerRuntime = class {
701
719
  ...this.config.effectHandlers
702
720
  };
703
721
  }
722
+ /**
723
+ * Lazily construct a default loader when the caller didn't provide one
724
+ * but `register()` needs to preprocess. Looks for `@almadar/std` in the
725
+ * nearest `node_modules` so cross-orbital `std/behaviors/<name>` imports
726
+ * resolve to the tiered registry on disk.
727
+ *
728
+ * Node only — browsers should receive already-preprocessed schemas from
729
+ * their server.
730
+ */
731
+ async ensureLoader() {
732
+ if (this.loader) return;
733
+ if (typeof process === "undefined" || !process.versions?.node) {
734
+ return;
735
+ }
736
+ try {
737
+ const [{ createRequire }, path2] = await Promise.all([
738
+ import('module'),
739
+ import('path')
740
+ ]);
741
+ const require2 = createRequire(import.meta.url);
742
+ const stdPkgJson = require2.resolve("@almadar/std/package.json");
743
+ const stdLibPath = path2.dirname(stdPkgJson);
744
+ const basePath = this.config.loaderConfig?.basePath ?? process.cwd();
745
+ this.loader = createUnifiedLoader({
746
+ basePath,
747
+ stdLibPath,
748
+ scopedPaths: this.config.loaderConfig?.scopedPaths
749
+ });
750
+ if (this.config.debug) {
751
+ console.log(
752
+ `[OrbitalRuntime] Default loader constructed: basePath=${basePath} stdLibPath=${stdLibPath}`
753
+ );
754
+ }
755
+ } catch (err) {
756
+ if (this.config.debug) {
757
+ console.warn(
758
+ `[OrbitalRuntime] Could not auto-construct loader: ${err instanceof Error ? err.message : String(err)}`
759
+ );
760
+ }
761
+ }
762
+ }
704
763
  // ==========================================================================
705
764
  // Schema Registration
706
765
  // ==========================================================================
707
766
  /**
708
767
  * Register an OrbitalSchema for execution.
709
768
  *
710
- * If `autoPreprocess` is enabled in config and schema has `uses` declarations,
711
- * it will be preprocessed first to resolve imports.
769
+ * Auto-preprocesses the schema when it contains `uses` declarations or
770
+ * unresolved cross-orbital trait references (e.g. a trait with
771
+ * `ref: "Modal.traits.ModalRecordModal"` and no inline `stateMachine`).
772
+ * Without preprocessing, those refs arrive empty at the state machine and
773
+ * button clicks silently do nothing — see Phase 9.5.H.
712
774
  *
713
- * For explicit preprocessing control, use `registerWithPreprocess()`.
775
+ * Preprocessing needs a loader. If `loaderConfig` is set, that loader is
776
+ * used. Otherwise, a default loader is constructed that points at
777
+ * `<cwd>` (for `basePath`) and the nearest `node_modules/@almadar/std` (for
778
+ * `stdLibPath`), which matches how every caller in this monorepo has the
779
+ * std registry on disk.
714
780
  */
715
781
  async register(schema) {
716
782
  if (this.config.debug) {
717
783
  console.log(`[OrbitalRuntime] Registering schema: ${schema.name}`);
718
784
  }
785
+ if (needsPreprocessing(schema)) {
786
+ await this.ensureLoader();
787
+ if (this.loader) {
788
+ if (this.config.debug) {
789
+ console.log(`[OrbitalRuntime] Schema has uses/refs \u2014 auto-preprocessing`);
790
+ }
791
+ const result = await preprocessSchema(schema, {
792
+ basePath: this.config.loaderConfig?.basePath || process.cwd(),
793
+ stdLibPath: this.config.loaderConfig?.stdLibPath,
794
+ scopedPaths: this.config.loaderConfig?.scopedPaths,
795
+ loader: this.loader,
796
+ namespaceEvents: this.config.namespaceEvents
797
+ });
798
+ if (!result.success) {
799
+ throw new Error(
800
+ `Schema preprocessing failed: ${result.errors.join("; ")}`
801
+ );
802
+ }
803
+ schema = result.data.schema;
804
+ this.entitySharingMap = {
805
+ ...this.entitySharingMap,
806
+ ...result.data.entitySharing
807
+ };
808
+ this.eventNamespaceMap = {
809
+ ...this.eventNamespaceMap,
810
+ ...result.data.eventNamespaces
811
+ };
812
+ } else if (this.config.debug) {
813
+ console.warn(
814
+ `[OrbitalRuntime] Schema has uses/refs but no loader available \u2014 proceeding without preprocessing. Cross-orbital trait refs will be empty.`
815
+ );
816
+ }
817
+ }
719
818
  for (const orbital of schema.orbitals) {
720
819
  await this.registerOrbitalAsync(orbital);
721
820
  }
722
821
  this.setupEventListeners();
723
822
  this.setupTicks();
823
+ this.resolvedSchema = schema;
724
824
  }
725
825
  /**
726
826
  * Register an OrbitalSchema synchronously (for backward compatibility).
@@ -736,6 +836,44 @@ var OrbitalServerRuntime = class {
736
836
  }
737
837
  this.setupEventListeners();
738
838
  this.setupTicks();
839
+ this.resolvedSchema = schema;
840
+ }
841
+ /**
842
+ * Returns the schema that this runtime is currently executing, post-
843
+ * preprocessing. Safe to expose from an HTTP `/api/schema` endpoint — every
844
+ * cross-orbital trait ref will have an inline `stateMachine` already, which
845
+ * is what the browser's `schema-to-ir` resolver needs to wire button clicks
846
+ * back to state transitions.
847
+ *
848
+ * Returns `null` if `register()` hasn't run yet.
849
+ */
850
+ getResolvedSchema() {
851
+ return this.resolvedSchema;
852
+ }
853
+ /**
854
+ * One-call entry point: read an `.orb` file from disk, parse it, preprocess
855
+ * cross-orbital imports, and register the result. Callers never touch raw
856
+ * `.orb` bytes — `register()` handles preprocessing internally.
857
+ *
858
+ * Node only. Browsers must receive already-resolved schemas from their
859
+ * server (see `getResolvedSchema()`).
860
+ */
861
+ async registerFromFile(path2) {
862
+ if (typeof process === "undefined" || !process.versions?.node) {
863
+ throw new Error(
864
+ "registerFromFile is Node-only. Browsers should receive resolved schemas from their server."
865
+ );
866
+ }
867
+ const { readFile } = await import('fs/promises');
868
+ const raw = await readFile(path2, "utf-8");
869
+ let schema;
870
+ try {
871
+ schema = JSON.parse(raw);
872
+ } catch (err) {
873
+ const msg = err instanceof Error ? err.message : String(err);
874
+ throw new Error(`registerFromFile: ${path2} is not valid JSON: ${msg}`);
875
+ }
876
+ await this.register(schema);
739
877
  }
740
878
  /**
741
879
  * Register an OrbitalSchema with preprocessing to resolve `uses` imports.