@almadar/ui 4.6.13 → 4.7.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.
@@ -1116,13 +1116,23 @@ function SlotsProvider({ children }) {
1116
1116
  const setSlotPatterns = React115.useCallback((slot, patterns, source) => {
1117
1117
  const sourceKey = source?.trait ?? DEFAULT_SOURCE_KEY;
1118
1118
  const entityProp = patterns[0]?.pattern && typeof patterns[0].pattern === "object" ? patterns[0].pattern.entity : void 0;
1119
+ const firstPatternType = patterns[0]?.pattern && typeof patterns[0].pattern === "object" ? patterns[0].pattern.type : void 0;
1119
1120
  slotLog.debug("setSlotPatterns", {
1120
1121
  slot,
1121
1122
  sourceKey,
1122
1123
  patternCount: patterns.length,
1123
- firstPatternType: patterns[0]?.pattern && typeof patterns[0].pattern === "object" ? patterns[0].pattern.type : void 0,
1124
+ firstPatternType,
1124
1125
  entityRefId: refId(entityProp)
1125
1126
  });
1127
+ if (source?.trait) {
1128
+ xOrbitalLog.info("slot-set", {
1129
+ slot,
1130
+ sourceTrait: source.trait,
1131
+ patternCount: patterns.length,
1132
+ firstPatternType,
1133
+ state: source.state
1134
+ });
1135
+ }
1126
1136
  setSlots((prev) => {
1127
1137
  const prevSlot = prev[slot] ?? {};
1128
1138
  return {
@@ -1144,6 +1154,7 @@ function SlotsProvider({ children }) {
1144
1154
  });
1145
1155
  }, []);
1146
1156
  const clearSlotForSource = React115.useCallback((slot, sourceTrait) => {
1157
+ xOrbitalLog.info("slot-clear-source", { slot, sourceTrait });
1147
1158
  setSlots((prev) => {
1148
1159
  const existing = prev[slot];
1149
1160
  if (!existing || !(sourceTrait in existing)) return prev;
@@ -1184,11 +1195,12 @@ function useSlotsActions() {
1184
1195
  }
1185
1196
  return actions;
1186
1197
  }
1187
- var slotLog, refIds, nextRefId, DEFAULT_SOURCE_KEY, SlotsStateContext, SlotsActionsContext;
1198
+ var slotLog, xOrbitalLog, refIds, nextRefId, DEFAULT_SOURCE_KEY, SlotsStateContext, SlotsActionsContext;
1188
1199
  var init_SlotsContext = __esm({
1189
1200
  "runtime/ui/SlotsContext.tsx"() {
1190
1201
  init_logger();
1191
1202
  slotLog = createLogger("almadar:ui:slot-render");
1203
+ xOrbitalLog = createLogger("almadar:runtime:cross-orbital");
1192
1204
  refIds = /* @__PURE__ */ new WeakMap();
1193
1205
  nextRefId = 1;
1194
1206
  DEFAULT_SOURCE_KEY = "__default__";
@@ -8182,7 +8194,7 @@ var init_MapView = __esm({
8182
8194
  shadowSize: [41, 41]
8183
8195
  });
8184
8196
  L.Marker.prototype.options.icon = defaultIcon;
8185
- const { useEffect: useEffect68, useRef: useRef65, useCallback: useCallback110, useState: useState103 } = React115__namespace.default;
8197
+ const { useEffect: useEffect68, useRef: useRef65, useCallback: useCallback110, useState: useState102 } = React115__namespace.default;
8186
8198
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
8187
8199
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
8188
8200
  function MapUpdater({ centerLat, centerLng, zoom }) {
@@ -8226,7 +8238,7 @@ var init_MapView = __esm({
8226
8238
  showAttribution = true
8227
8239
  }) {
8228
8240
  const eventBus = useEventBus2();
8229
- const [clickedPosition, setClickedPosition] = useState103(null);
8241
+ const [clickedPosition, setClickedPosition] = useState102(null);
8230
8242
  const handleMapClick = useCallback110((lat, lng) => {
8231
8243
  if (showClickedPin) {
8232
8244
  setClickedPosition({ lat, lng });
@@ -38179,6 +38191,7 @@ function useTraitStateMachine(traitBindings, slotsActions, options) {
38179
38191
  const eventBus = useEventBus();
38180
38192
  const { entities } = useEntitySchema();
38181
38193
  const traitConfigsByName = options?.traitConfigsByName;
38194
+ const orbitalsByTrait = options?.orbitalsByTrait;
38182
38195
  const manager = React115.useMemo(() => {
38183
38196
  const traitDefs = traitBindings.map(toTraitDefinition);
38184
38197
  const m = new runtime.StateMachineManager(traitDefs);
@@ -38391,24 +38404,7 @@ function useTraitStateMachine(traitBindings, slotsActions, options) {
38391
38404
  const actions = slotsActionsRef.current;
38392
38405
  console.log("[TraitStateMachine] Processing event:", normalizedEvent, "payload:", payload);
38393
38406
  const bindingMap = new Map(bindings.map((b) => [b.trait.name, b]));
38394
- for (const traitName of bindingMap.keys()) {
38395
- const traitState = currentManager.getState(traitName);
38396
- eventBus.emit(`${traitName}:DISPATCH`, {
38397
- event: normalizedEvent,
38398
- payload,
38399
- currentState: traitState?.currentState
38400
- });
38401
- }
38402
38407
  const results = currentManager.sendEvent(normalizedEvent, payload);
38403
- for (const { traitName, result } of results) {
38404
- const suffix = result.executed ? "SUCCESS" : "ERROR";
38405
- eventBus.emit(`${traitName}:${normalizedEvent}:${suffix}`, {
38406
- event: normalizedEvent,
38407
- payload,
38408
- newState: result.newState,
38409
- currentState: result.previousState
38410
- });
38411
- }
38412
38408
  const emittedByTrait = /* @__PURE__ */ new Map();
38413
38409
  for (const { traitName, result } of results) {
38414
38410
  const binding = bindingMap.get(traitName);
@@ -38644,7 +38640,10 @@ function useTraitStateMachine(traitBindings, slotsActions, options) {
38644
38640
  if (!LIFECYCLE_EVENTS.has(normalizedEvent)) {
38645
38641
  for (const { traitName, result } of results) {
38646
38642
  if (!result.executed) continue;
38647
- eventBus.emit(normalizedEvent, payload, {
38643
+ const orbitalName = orbitalsByTrait?.[traitName];
38644
+ if (!orbitalName) continue;
38645
+ eventBus.emit(`UI:${orbitalName}.${traitName}.${normalizedEvent}`, payload, {
38646
+ orbital: orbitalName,
38648
38647
  trait: traitName,
38649
38648
  fromBridge: true
38650
38649
  });
@@ -38655,7 +38654,13 @@ function useTraitStateMachine(traitBindings, slotsActions, options) {
38655
38654
  }
38656
38655
  const onEventProcessed = optionsRef.current?.onEventProcessed;
38657
38656
  if (onEventProcessed) {
38658
- await onEventProcessed(normalizedEvent, payload);
38657
+ const dispatchedOrbitals = /* @__PURE__ */ new Set();
38658
+ for (const { traitName, result } of results) {
38659
+ if (!result.executed) continue;
38660
+ const orbital = orbitalsByTrait?.[traitName];
38661
+ if (orbital) dispatchedOrbitals.add(orbital);
38662
+ }
38663
+ await onEventProcessed(normalizedEvent, payload, dispatchedOrbitals);
38659
38664
  }
38660
38665
  }, [entities, eventBus]);
38661
38666
  const drainEventQueue = React115.useCallback(async () => {
@@ -38699,26 +38704,34 @@ function useTraitStateMachine(traitBindings, slotsActions, options) {
38699
38704
  }
38700
38705
  console.log("[TraitStateMachine] Subscribing to events:", Array.from(allEvents));
38701
38706
  const unsubscribes = [];
38702
- for (const eventKey of allEvents) {
38703
- if (eventKey === "INIT" || eventKey === "LOAD" || eventKey === "$MOUNT") {
38704
- continue;
38707
+ for (const binding of traitBindings) {
38708
+ const traitName = binding.trait.name;
38709
+ const orbitalName = orbitalsByTrait?.[traitName];
38710
+ if (!orbitalName) continue;
38711
+ for (const transition of binding.trait.transitions) {
38712
+ const eventKey = transition.event;
38713
+ if (eventKey === "INIT" || eventKey === "LOAD" || eventKey === "$MOUNT") {
38714
+ continue;
38715
+ }
38716
+ const unsub = eventBus.on(`UI:${orbitalName}.${traitName}.${eventKey}`, (event) => {
38717
+ if (event.source && event.source.fromBridge) {
38718
+ return;
38719
+ }
38720
+ enqueueAndDrain(eventKey, event.payload);
38721
+ });
38722
+ unsubscribes.push(unsub);
38705
38723
  }
38706
- const unsub = eventBus.on(`UI:${eventKey}`, (event) => {
38707
- console.log("[TraitStateMachine] Received event:", `UI:${eventKey}`, event);
38708
- enqueueAndDrain(eventKey, event.payload);
38709
- });
38710
- unsubscribes.push(unsub);
38711
38724
  }
38712
38725
  for (const binding of traitBindings) {
38726
+ const ownOrbital = orbitalsByTrait?.[binding.trait.name];
38713
38727
  const listens = binding.trait.listens ?? [];
38714
38728
  for (const listen of listens) {
38715
- const expectedTrait = listen.source?.trait;
38716
- const unsub = eventBus.on(listen.event, (event) => {
38717
- if (expectedTrait) {
38718
- const emitTrait = event.source?.trait;
38719
- if (emitTrait !== expectedTrait) return;
38720
- }
38721
- console.log("[TraitStateMachine] listens", binding.trait.name, listen.event, "\u2192", listen.triggers, "from", event.source?.trait);
38729
+ const sourceTrait = listen.source?.trait;
38730
+ if (!sourceTrait) continue;
38731
+ const sourceOrbital = listen.source?.orbital ?? ownOrbital;
38732
+ if (!sourceOrbital) continue;
38733
+ const busKey = `UI:${sourceOrbital}.${sourceTrait}.${listen.event}`;
38734
+ const unsub = eventBus.on(busKey, (event) => {
38722
38735
  enqueueAndDrain(listen.triggers, event.payload);
38723
38736
  });
38724
38737
  unsubscribes.push(unsub);
@@ -38865,6 +38878,8 @@ init_EntitySchemaContext();
38865
38878
 
38866
38879
  // runtime/ServerBridge.tsx
38867
38880
  init_useEventBus();
38881
+ init_logger();
38882
+ var xOrbitalLog2 = createLogger("almadar:runtime:cross-orbital");
38868
38883
  var ServerBridgeContext = React115.createContext(null);
38869
38884
  function useServerBridge() {
38870
38885
  const ctx = React115.useContext(ServerBridgeContext);
@@ -38942,8 +38957,22 @@ function ServerBridgeProvider({
38942
38957
  }
38943
38958
  if (result.emittedEvents) {
38944
38959
  for (const emitted of result.emittedEvents) {
38945
- eventBus.emit(`UI:${emitted.event}`, emitted.payload);
38946
- eventBus.emit(emitted.event, emitted.payload);
38960
+ const evTrait = emitted.source?.trait;
38961
+ if (!evTrait) {
38962
+ xOrbitalLog2.warn("emit:dropped-no-source", {
38963
+ event: emitted.event,
38964
+ dispatchOrbital: orbitalName
38965
+ });
38966
+ continue;
38967
+ }
38968
+ const key = emitted.source?.orbital ? `UI:${emitted.source.orbital}.${evTrait}.${emitted.event}` : `UI:${evTrait}.${emitted.event}`;
38969
+ xOrbitalLog2.info("emit:rebroadcast", {
38970
+ busKey: key,
38971
+ sourceOrbital: emitted.source?.orbital,
38972
+ sourceTrait: evTrait,
38973
+ dispatchOrbital: orbitalName
38974
+ });
38975
+ eventBus.emit(key, emitted.payload);
38947
38976
  }
38948
38977
  }
38949
38978
  } else if (result.error) {
@@ -39064,6 +39093,10 @@ function prepareSchemaForPreview(input) {
39064
39093
  const schema = adjustSchemaForMockData(parsed, mockData);
39065
39094
  return { schema, mockData };
39066
39095
  }
39096
+
39097
+ // runtime/OrbPreview.tsx
39098
+ init_logger();
39099
+ var xOrbitalLog3 = createLogger("almadar:runtime:cross-orbital");
39067
39100
  function normalizeChild(child) {
39068
39101
  if (typeof child === "string") return child;
39069
39102
  if (child === null || typeof child !== "object" || Array.isArray(child)) {
@@ -39143,6 +39176,11 @@ function applyServerEffects(effects, uiSlots, onNavigate) {
39143
39176
  const patternRecord = eff.pattern;
39144
39177
  const { type: patternType, children, ...inlineProps } = patternRecord;
39145
39178
  const normalizedChildren = Array.isArray(children) ? children.map((c) => normalizeChild(c)) : children;
39179
+ xOrbitalLog3.info("slot-write", {
39180
+ slot: eff.slot,
39181
+ sourceTrait: eff.traitName ?? "server",
39182
+ patternType: typeof patternType === "string" ? patternType : void 0
39183
+ });
39146
39184
  uiSlots.render({
39147
39185
  target: eff.slot,
39148
39186
  pattern: patternType,
@@ -39157,19 +39195,26 @@ function applyServerEffects(effects, uiSlots, onNavigate) {
39157
39195
  }
39158
39196
  }
39159
39197
  }
39160
- function TraitInitializer({ traits: traits2, orbitalNames, onNavigate, onLocalFallback, persistence, traitConfigsByName }) {
39198
+ function TraitInitializer({ traits: traits2, orbitalNames, onNavigate, onLocalFallback, persistence, traitConfigsByName, orbitalsByTrait }) {
39161
39199
  const slotsActions = useSlotsActions();
39162
39200
  const bridge = useServerBridge();
39163
39201
  const uiSlots = context.useUISlots();
39164
- const onEventProcessed = React115.useCallback(async (event, payload) => {
39202
+ const onEventProcessed = React115.useCallback(async (event, payload, dispatchedOrbitals) => {
39165
39203
  if (!bridge.connected || !orbitalNames?.length) return;
39166
- for (const name of orbitalNames) {
39204
+ const targets = dispatchedOrbitals && dispatchedOrbitals.size > 0 ? orbitalNames.filter((n) => dispatchedOrbitals.has(n)) : orbitalNames;
39205
+ xOrbitalLog3.info("TraitInitializer:fanout", {
39206
+ event,
39207
+ sentTo: targets,
39208
+ skipped: orbitalNames.filter((n) => !targets.includes(n)),
39209
+ dispatchedOrbitalsSize: dispatchedOrbitals?.size ?? 0
39210
+ });
39211
+ for (const name of targets) {
39167
39212
  const { effects, meta } = await bridge.sendEvent(name, event, payload);
39168
39213
  recordServerResponse(name, event, meta);
39169
39214
  applyServerEffects(effects, uiSlots, onNavigate);
39170
39215
  }
39171
39216
  }, [bridge.connected, bridge.sendEvent, orbitalNames, uiSlots, onNavigate]);
39172
- const opts = orbitalNames ? { onEventProcessed, navigate: onNavigate, traitConfigsByName } : { navigate: onNavigate, persistence, traitConfigsByName };
39217
+ const opts = orbitalNames ? { onEventProcessed, navigate: onNavigate, traitConfigsByName, orbitalsByTrait } : { navigate: onNavigate, persistence, traitConfigsByName, orbitalsByTrait };
39173
39218
  const { sendEvent } = useTraitStateMachine(traits2, slotsActions, opts);
39174
39219
  const initSentRef = React115.useRef(false);
39175
39220
  React115.useEffect(() => {
@@ -39219,27 +39264,68 @@ function SchemaRunner({ schema, serverUrl, mockData, pageName, onNavigate, onLoc
39219
39264
  const allPageTraits = React115.useMemo(() => {
39220
39265
  if (pageName && traits2.length > 0) return traits2;
39221
39266
  if (!ir?.pages || ir.pages.size <= 1) return traits2;
39222
- const combined = [];
39267
+ const firstPage = ir.pages.values().next().value;
39268
+ if (!firstPage) return traits2;
39269
+ const firstPageTraits = [];
39223
39270
  const seen = /* @__PURE__ */ new Set();
39224
- for (const page of ir.pages.values()) {
39225
- for (const t of page.traits) {
39226
- const binding = t;
39227
- const traitObj = binding.trait;
39228
- const name = traitObj?.name ?? binding.name ?? "";
39229
- if (name && !seen.has(name)) {
39230
- seen.add(name);
39231
- combined.push(t);
39232
- }
39271
+ for (const binding of firstPage.traits) {
39272
+ const name = binding.trait.name;
39273
+ if (name && !seen.has(name)) {
39274
+ seen.add(name);
39275
+ firstPageTraits.push(binding);
39233
39276
  }
39234
39277
  }
39235
- return combined.length > 0 ? combined : traits2;
39278
+ return firstPageTraits.length > 0 ? firstPageTraits : traits2;
39236
39279
  }, [ir, traits2, pageName]);
39237
- const orbitalNames = React115.useMemo(() => {
39280
+ React115.useMemo(() => {
39238
39281
  const parsed = schema;
39239
39282
  const orbitals = parsed?.orbitals;
39240
39283
  if (!orbitals) return [];
39241
39284
  return orbitals.filter((o) => typeof o.name === "string").map((o) => o.name);
39242
39285
  }, [schema]);
39286
+ const orbitalsByTrait = React115.useMemo(() => {
39287
+ const map = {};
39288
+ const parsed = schema;
39289
+ if (!parsed?.orbitals) return map;
39290
+ for (const orb of parsed.orbitals) {
39291
+ for (const traitRef of orb.traits) {
39292
+ let traitName;
39293
+ if (typeof traitRef === "string") {
39294
+ const parts = traitRef.split(".");
39295
+ traitName = parts[parts.length - 1];
39296
+ } else if ("ref" in traitRef && typeof traitRef.ref === "string") {
39297
+ const parts = traitRef.ref.split(".");
39298
+ traitName = traitRef.name ?? parts[parts.length - 1];
39299
+ } else if ("name" in traitRef && typeof traitRef.name === "string") {
39300
+ traitName = traitRef.name;
39301
+ }
39302
+ if (traitName) map[traitName] = orb.name;
39303
+ }
39304
+ }
39305
+ return map;
39306
+ }, [schema]);
39307
+ const pageOrbitalNames = React115.useMemo(() => {
39308
+ const set = /* @__PURE__ */ new Set();
39309
+ for (const binding of allPageTraits) {
39310
+ const orb = orbitalsByTrait[binding.trait.name];
39311
+ if (orb) set.add(orb);
39312
+ }
39313
+ return Array.from(set);
39314
+ }, [allPageTraits, orbitalsByTrait]);
39315
+ React115.useEffect(() => {
39316
+ const traitNames = allPageTraits.map((b) => b.trait.name);
39317
+ const orbitalsByTraitForPage = {};
39318
+ for (const name of traitNames) {
39319
+ const orb = orbitalsByTrait[name];
39320
+ if (orb) orbitalsByTraitForPage[name] = orb;
39321
+ }
39322
+ xOrbitalLog3.info("SchemaRunner:mount", {
39323
+ pageName,
39324
+ traitNames,
39325
+ orbitalsByTraitForPage,
39326
+ pageOrbitalNames: pageOrbitalNames.join(",")
39327
+ });
39328
+ }, [pageName, allPageTraits, orbitalsByTrait, pageOrbitalNames]);
39243
39329
  const traitConfigsByName = React115.useMemo(() => {
39244
39330
  const map = {};
39245
39331
  const parsed = schema;
@@ -39280,8 +39366,9 @@ function SchemaRunner({ schema, serverUrl, mockData, pageName, onNavigate, onLoc
39280
39366
  TraitInitializer,
39281
39367
  {
39282
39368
  traits: allPageTraits,
39283
- orbitalNames: serverUrl ? orbitalNames : void 0,
39369
+ orbitalNames: serverUrl ? pageOrbitalNames : void 0,
39284
39370
  traitConfigsByName,
39371
+ orbitalsByTrait,
39285
39372
  onNavigate,
39286
39373
  onLocalFallback,
39287
39374
  persistence
@@ -39303,7 +39390,8 @@ function OrbPreview({
39303
39390
  autoMock = false,
39304
39391
  height = "400px",
39305
39392
  className,
39306
- serverUrl
39393
+ serverUrl,
39394
+ initialPagePath
39307
39395
  }) {
39308
39396
  const [localFallback, setLocalFallback] = React115.useState(false);
39309
39397
  const eventBus = useEventBus();
@@ -39349,7 +39437,17 @@ function OrbPreview({
39349
39437
  return [];
39350
39438
  }
39351
39439
  }, [parsedSchema]);
39352
- const [currentPage, setCurrentPage] = React115.useState(void 0);
39440
+ const initialPageName = React115.useMemo(() => {
39441
+ if (!initialPagePath) return void 0;
39442
+ const match = pages.find(({ page }) => page.path === initialPagePath);
39443
+ return match?.page.name;
39444
+ }, [pages, initialPagePath]);
39445
+ const [currentPage, setCurrentPage] = React115.useState(initialPageName);
39446
+ React115.useEffect(() => {
39447
+ if (initialPageName && initialPageName !== currentPage) {
39448
+ setCurrentPage(initialPageName);
39449
+ }
39450
+ }, [initialPageName, currentPage]);
39353
39451
  const handleNavigate = React115.useCallback((path) => {
39354
39452
  const match = pages.find(({ page }) => page.path === path);
39355
39453
  if (match) {