@almadar/ui 4.10.7 → 4.12.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.
@@ -37,6 +37,7 @@ import langGraphql from 'react-syntax-highlighter/dist/esm/languages/prism/graph
37
37
  import { isCircuitEvent, schemaToIR, getPage, clearSchemaCache as clearSchemaCache$1, isEntityCall, isInlineTrait } from '@almadar/core';
38
38
  import '@tanstack/react-query';
39
39
  import { StateMachineManager, createContextFromBindings, interpolateValue, createServerEffectHandlers, EffectExecutor, InMemoryPersistence } from '@almadar/runtime';
40
+ import { OrbitalServerRuntime } from '@almadar/runtime/OrbitalServerRuntime';
40
41
 
41
42
  var __defProp = Object.defineProperty;
42
43
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -8184,13 +8185,13 @@ var init_MapView = __esm({
8184
8185
  shadowSize: [41, 41]
8185
8186
  });
8186
8187
  L.Marker.prototype.options.icon = defaultIcon;
8187
- const { useEffect: useEffect68, useRef: useRef65, useCallback: useCallback110, useState: useState102 } = React115__default;
8188
+ const { useEffect: useEffect69, useRef: useRef65, useCallback: useCallback110, useState: useState103 } = React115__default;
8188
8189
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
8189
8190
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
8190
8191
  function MapUpdater({ centerLat, centerLng, zoom }) {
8191
8192
  const map = useMap();
8192
8193
  const prevRef = useRef65({ centerLat, centerLng, zoom });
8193
- useEffect68(() => {
8194
+ useEffect69(() => {
8194
8195
  const prev = prevRef.current;
8195
8196
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
8196
8197
  map.setView([centerLat, centerLng], zoom);
@@ -8201,7 +8202,7 @@ var init_MapView = __esm({
8201
8202
  }
8202
8203
  function MapClickHandler({ onMapClick }) {
8203
8204
  const map = useMap();
8204
- useEffect68(() => {
8205
+ useEffect69(() => {
8205
8206
  if (!onMapClick) return;
8206
8207
  const handler = (e) => {
8207
8208
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -8228,7 +8229,7 @@ var init_MapView = __esm({
8228
8229
  showAttribution = true
8229
8230
  }) {
8230
8231
  const eventBus = useEventBus2();
8231
- const [clickedPosition, setClickedPosition] = useState102(null);
8232
+ const [clickedPosition, setClickedPosition] = useState103(null);
8232
8233
  const handleMapClick = useCallback110((lat, lng) => {
8233
8234
  if (showClickedPin) {
8234
8235
  setClickedPosition({ lat, lng });
@@ -38928,6 +38929,78 @@ init_Box();
38928
38929
  init_Typography();
38929
38930
  init_UISlotRenderer();
38930
38931
  init_useEventBus();
38932
+
38933
+ // runtime/embedded-traits.ts
38934
+ var TRAIT_BINDING_PREFIX = "@trait.";
38935
+ function collectTraitRefsFromValue(value, into) {
38936
+ if (value === null || value === void 0) return;
38937
+ if (typeof value === "string") {
38938
+ if (value.startsWith(TRAIT_BINDING_PREFIX)) {
38939
+ const rest = value.slice(TRAIT_BINDING_PREFIX.length);
38940
+ const dot = rest.indexOf(".");
38941
+ const traitName = dot === -1 ? rest : rest.slice(0, dot);
38942
+ if (traitName.length > 0) into.add(traitName);
38943
+ }
38944
+ return;
38945
+ }
38946
+ if (Array.isArray(value)) {
38947
+ for (const item of value) collectTraitRefsFromValue(item, into);
38948
+ return;
38949
+ }
38950
+ if (typeof value === "object") {
38951
+ for (const v of Object.values(value)) {
38952
+ collectTraitRefsFromValue(v, into);
38953
+ }
38954
+ }
38955
+ }
38956
+ function collectTraitRefsFromEffects(effects, into) {
38957
+ if (!effects) return;
38958
+ for (const effect of effects) {
38959
+ if (!Array.isArray(effect)) continue;
38960
+ if (effect[0] === "render-ui" && effect.length >= 3) {
38961
+ collectTraitRefsFromValue(effect[2], into);
38962
+ continue;
38963
+ }
38964
+ for (let i = 1; i < effect.length; i++) {
38965
+ const arg = effect[i];
38966
+ if (Array.isArray(arg)) collectTraitRefsFromEffects([arg], into);
38967
+ else collectTraitRefsFromValue(arg, into);
38968
+ }
38969
+ }
38970
+ }
38971
+ function collectEmbeddedTraits(schema) {
38972
+ const out = /* @__PURE__ */ new Set();
38973
+ if (!schema?.orbitals) return out;
38974
+ for (const orbital of schema.orbitals) {
38975
+ if (!orbital || typeof orbital !== "object") continue;
38976
+ const traits2 = orbital.traits;
38977
+ if (!Array.isArray(traits2)) continue;
38978
+ for (const trait of traits2) {
38979
+ if (!trait || typeof trait !== "object") continue;
38980
+ const resolved = trait._resolved;
38981
+ const target = resolved && typeof resolved === "object" ? resolved : trait;
38982
+ const stateMachine = target.stateMachine;
38983
+ const transitions = stateMachine?.transitions;
38984
+ if (!Array.isArray(transitions)) continue;
38985
+ for (const t of transitions) {
38986
+ if (!t || typeof t !== "object") continue;
38987
+ const effects = t.effects;
38988
+ collectTraitRefsFromEffects(effects, out);
38989
+ }
38990
+ const initialEffects = target.initialEffects;
38991
+ collectTraitRefsFromEffects(initialEffects, out);
38992
+ const ticks2 = target.ticks;
38993
+ if (Array.isArray(ticks2)) {
38994
+ for (const tick of ticks2) {
38995
+ collectTraitRefsFromEffects(tick?.effects, out);
38996
+ }
38997
+ }
38998
+ }
38999
+ }
39000
+ return out;
39001
+ }
39002
+
39003
+ // runtime/OrbPreview.tsx
38931
39004
  init_SlotsContext();
38932
39005
  init_EntitySchemaContext();
38933
39006
 
@@ -38935,6 +39008,38 @@ init_EntitySchemaContext();
38935
39008
  init_useEventBus();
38936
39009
  init_logger();
38937
39010
  var xOrbitalLog2 = createLogger("almadar:runtime:cross-orbital");
39011
+ function createHttpTransport(serverUrl) {
39012
+ return {
39013
+ register: async (schema) => {
39014
+ try {
39015
+ const res = await fetch(`${serverUrl}/register`, {
39016
+ method: "POST",
39017
+ headers: { "Content-Type": "application/json" },
39018
+ body: JSON.stringify({ schema })
39019
+ });
39020
+ const result = await res.json();
39021
+ return !!result.success;
39022
+ } catch (err) {
39023
+ console.error("[ServerBridge] Registration failed:", err);
39024
+ return false;
39025
+ }
39026
+ },
39027
+ unregister: async () => {
39028
+ try {
39029
+ await fetch(`${serverUrl}/unregister`, { method: "DELETE" });
39030
+ } catch {
39031
+ }
39032
+ },
39033
+ sendEvent: async (orbitalName, event, payload) => {
39034
+ const res = await fetch(`${serverUrl}/${orbitalName}/events`, {
39035
+ method: "POST",
39036
+ headers: { "Content-Type": "application/json" },
39037
+ body: JSON.stringify({ event, payload })
39038
+ });
39039
+ return res.json();
39040
+ }
39041
+ };
39042
+ }
38938
39043
  var ServerBridgeContext = createContext(null);
38939
39044
  function useServerBridge() {
38940
39045
  const ctx = useContext(ServerBridgeContext);
@@ -38947,40 +39052,34 @@ function useServerBridge() {
38947
39052
  function ServerBridgeProvider({
38948
39053
  schema,
38949
39054
  serverUrl,
39055
+ transport: customTransport,
38950
39056
  children
38951
39057
  }) {
39058
+ if (!serverUrl && !customTransport) {
39059
+ throw new Error("ServerBridgeProvider requires either serverUrl or transport");
39060
+ }
39061
+ if (serverUrl && customTransport) {
39062
+ throw new Error("ServerBridgeProvider accepts serverUrl OR transport, not both");
39063
+ }
38952
39064
  const eventBus = useEventBus();
38953
39065
  const [connected, setConnected] = useState(false);
38954
- const registerSchema = useCallback(async () => {
38955
- try {
38956
- const res = await fetch(`${serverUrl}/register`, {
38957
- method: "POST",
38958
- headers: { "Content-Type": "application/json" },
38959
- body: JSON.stringify({ schema })
38960
- });
38961
- const result = await res.json();
38962
- return !!result.success;
38963
- } catch (err) {
38964
- console.error("[ServerBridge] Registration failed:", err);
38965
- return false;
38966
- }
38967
- }, [schema, serverUrl]);
38968
- const unregisterSchema = useCallback(async () => {
38969
- try {
38970
- await fetch(`${serverUrl}/unregister`, { method: "DELETE" });
38971
- } catch {
38972
- }
38973
- }, [serverUrl]);
39066
+ const transport = useMemo(
39067
+ () => customTransport ?? createHttpTransport(serverUrl),
39068
+ [serverUrl, customTransport]
39069
+ );
39070
+ const registerSchema = useCallback(
39071
+ async () => transport.register(schema),
39072
+ [schema, transport]
39073
+ );
39074
+ const unregisterSchema = useCallback(
39075
+ async () => transport.unregister(),
39076
+ [transport]
39077
+ );
38974
39078
  const sendEvent = useCallback(async (orbitalName, event, payload) => {
38975
39079
  const emptyMeta = { success: false, clientEffects: 0, dataEntities: {}, emittedEvents: [] };
38976
39080
  if (!connected) return { effects: [], meta: emptyMeta };
38977
39081
  try {
38978
- const res = await fetch(`${serverUrl}/${orbitalName}/events`, {
38979
- method: "POST",
38980
- headers: { "Content-Type": "application/json" },
38981
- body: JSON.stringify({ event, payload })
38982
- });
38983
- const result = await res.json();
39082
+ const result = await transport.sendEvent(orbitalName, event, payload);
38984
39083
  const effects = [];
38985
39084
  const responseData = result.data || {};
38986
39085
  const dataEntities = {};
@@ -39038,7 +39137,7 @@ function ServerBridgeProvider({
39038
39137
  console.error("[ServerBridge] Event send failed:", err);
39039
39138
  return { effects: [], meta: { ...emptyMeta, error: err instanceof Error ? err.message : String(err) } };
39040
39139
  }
39041
- }, [connected, serverUrl, eventBus]);
39140
+ }, [connected, transport, eventBus]);
39042
39141
  useEffect(() => {
39043
39142
  if (!schema) return;
39044
39143
  let cancelled = false;
@@ -39225,32 +39324,49 @@ function SlotBridge() {
39225
39324
  }, [slots, render, clear]);
39226
39325
  return null;
39227
39326
  }
39228
- function applyServerEffects(effects, uiSlots, onNavigate) {
39327
+ function applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits) {
39229
39328
  for (const eff of effects) {
39230
39329
  if (eff.type === "render-ui" && eff.slot && eff.pattern) {
39231
39330
  const patternRecord = eff.pattern;
39232
39331
  const { type: patternType, children, ...inlineProps } = patternRecord;
39233
39332
  const normalizedChildren = Array.isArray(children) ? children.map((c) => normalizeChild(c)) : children;
39234
- xOrbitalLog3.info("slot-write", {
39235
- slot: eff.slot,
39236
- sourceTrait: eff.traitName ?? "server",
39237
- patternType: typeof patternType === "string" ? patternType : void 0
39238
- });
39239
- uiSlots.render({
39240
- target: eff.slot,
39241
- pattern: patternType,
39242
- props: {
39243
- ...inlineProps,
39244
- ...normalizedChildren !== void 0 ? { children: normalizedChildren } : {}
39245
- },
39246
- sourceTrait: eff.traitName ?? "server"
39247
- });
39333
+ const sourceTrait = eff.traitName ?? "server";
39334
+ const isEmbedded = embeddedTraits?.has(sourceTrait) ?? false;
39335
+ const props = {
39336
+ ...inlineProps,
39337
+ ...normalizedChildren !== void 0 ? { children: normalizedChildren } : {}
39338
+ };
39339
+ if (isEmbedded) {
39340
+ xOrbitalLog3.info("slot:embed-routed", {
39341
+ sourceTrait,
39342
+ slot: eff.slot,
39343
+ patternType: typeof patternType === "string" ? patternType : void 0
39344
+ });
39345
+ uiSlots.updateTraitContent(sourceTrait, {
39346
+ pattern: patternType,
39347
+ props,
39348
+ priority: 0,
39349
+ animation: "fade"
39350
+ });
39351
+ } else {
39352
+ xOrbitalLog3.info("slot-write", {
39353
+ slot: eff.slot,
39354
+ sourceTrait,
39355
+ patternType: typeof patternType === "string" ? patternType : void 0
39356
+ });
39357
+ uiSlots.render({
39358
+ target: eff.slot,
39359
+ pattern: patternType,
39360
+ props,
39361
+ sourceTrait
39362
+ });
39363
+ }
39248
39364
  } else if (eff.type === "navigate" && eff.route && onNavigate) {
39249
39365
  onNavigate(eff.route, eff.params);
39250
39366
  }
39251
39367
  }
39252
39368
  }
39253
- function TraitInitializer({ traits: traits2, orbitalNames, onNavigate, onLocalFallback, persistence, traitConfigsByName, orbitalsByTrait }) {
39369
+ function TraitInitializer({ traits: traits2, orbitalNames, onNavigate, onLocalFallback, persistence, traitConfigsByName, orbitalsByTrait, embeddedTraits }) {
39254
39370
  const slotsActions = useSlotsActions();
39255
39371
  const bridge = useServerBridge();
39256
39372
  const uiSlots = useUISlots();
@@ -39266,9 +39382,9 @@ function TraitInitializer({ traits: traits2, orbitalNames, onNavigate, onLocalFa
39266
39382
  for (const name of targets) {
39267
39383
  const { effects, meta } = await bridge.sendEvent(name, event, payload);
39268
39384
  recordServerResponse(name, event, meta);
39269
- applyServerEffects(effects, uiSlots, onNavigate);
39385
+ applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits);
39270
39386
  }
39271
- }, [bridge.connected, bridge.sendEvent, orbitalNames, uiSlots, onNavigate]);
39387
+ }, [bridge.connected, bridge.sendEvent, orbitalNames, uiSlots, onNavigate, embeddedTraits]);
39272
39388
  const opts = orbitalNames ? { onEventProcessed, navigate: onNavigate, traitConfigsByName, orbitalsByTrait } : { navigate: onNavigate, persistence, traitConfigsByName, orbitalsByTrait };
39273
39389
  const { sendEvent } = useTraitStateMachine(traits2, slotsActions, opts);
39274
39390
  const initSentRef = useRef(false);
@@ -39308,13 +39424,13 @@ function TraitInitializer({ traits: traits2, orbitalNames, onNavigate, onLocalFa
39308
39424
  effects: effectTraces,
39309
39425
  timestamp: Date.now()
39310
39426
  });
39311
- applyServerEffects(effects, uiSlots, onNavigate);
39427
+ applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits);
39312
39428
  }
39313
39429
  })();
39314
- }, [bridge.connected, orbitalNames, bridge.sendEvent, uiSlots, onNavigate]);
39430
+ }, [bridge.connected, orbitalNames, bridge.sendEvent, uiSlots, onNavigate, embeddedTraits]);
39315
39431
  return null;
39316
39432
  }
39317
- function SchemaRunner({ schema, serverUrl, mockData, pageName, onNavigate, onLocalFallback, persistence }) {
39433
+ function SchemaRunner({ schema, serverUrl, transport, mockData, pageName, onNavigate, onLocalFallback, persistence }) {
39318
39434
  const { traits: traits2, allEntities, ir } = useResolvedSchema(schema, pageName);
39319
39435
  const allPageTraits = useMemo(() => {
39320
39436
  if (pageName && traits2.length > 0) return traits2;
@@ -39417,6 +39533,9 @@ function SchemaRunner({ schema, serverUrl, mockData, pageName, onNavigate, onLoc
39417
39533
  }
39418
39534
  return map;
39419
39535
  }, [schema]);
39536
+ const embeddedTraits = useMemo(() => {
39537
+ return collectEmbeddedTraits(schema);
39538
+ }, [schema]);
39420
39539
  const inner = /* @__PURE__ */ jsx(VerificationProvider, { enabled: true, children: /* @__PURE__ */ jsx(SlotsProvider, { children: /* @__PURE__ */ jsxs(
39421
39540
  EntitySchemaProvider,
39422
39541
  {
@@ -39428,9 +39547,10 @@ function SchemaRunner({ schema, serverUrl, mockData, pageName, onNavigate, onLoc
39428
39547
  TraitInitializer,
39429
39548
  {
39430
39549
  traits: allPageTraits,
39431
- orbitalNames: serverUrl ? pageOrbitalNames : void 0,
39550
+ orbitalNames: serverUrl || transport ? pageOrbitalNames : void 0,
39432
39551
  traitConfigsByName,
39433
39552
  orbitalsByTrait,
39553
+ embeddedTraits,
39434
39554
  onNavigate,
39435
39555
  onLocalFallback,
39436
39556
  persistence
@@ -39441,8 +39561,8 @@ function SchemaRunner({ schema, serverUrl, mockData, pageName, onNavigate, onLoc
39441
39561
  ]
39442
39562
  }
39443
39563
  ) }) });
39444
- if (serverUrl) {
39445
- return /* @__PURE__ */ jsx(ServerBridgeProvider, { schema, serverUrl, children: inner });
39564
+ if (serverUrl || transport) {
39565
+ return /* @__PURE__ */ jsx(ServerBridgeProvider, { schema, serverUrl, transport, children: inner });
39446
39566
  }
39447
39567
  return inner;
39448
39568
  }
@@ -39453,8 +39573,12 @@ function OrbPreview({
39453
39573
  height = "400px",
39454
39574
  className,
39455
39575
  serverUrl,
39576
+ transport,
39456
39577
  initialPagePath
39457
39578
  }) {
39579
+ if (serverUrl && transport) {
39580
+ throw new Error("OrbPreview accepts serverUrl OR transport, not both");
39581
+ }
39458
39582
  const [localFallback, setLocalFallback] = useState(false);
39459
39583
  const eventBus = useEventBus();
39460
39584
  const handleLocalFallback = useCallback(() => {
@@ -39476,21 +39600,21 @@ function OrbPreview({
39476
39600
  } else {
39477
39601
  parsed = schema;
39478
39602
  }
39479
- if (autoMock && !serverUrl) {
39603
+ if (autoMock && !serverUrl && !transport) {
39480
39604
  const prepared = prepareSchemaForPreview(parsed);
39481
39605
  return { ok: true, schema: prepared.schema, mockData: prepared.mockData };
39482
39606
  }
39483
39607
  return { ok: true, schema: parsed, mockData: mockData ?? {} };
39484
- }, [schema, autoMock, serverUrl, mockData]);
39608
+ }, [schema, autoMock, serverUrl, transport, mockData]);
39485
39609
  const parsedSchema = parseResult.ok ? parseResult.schema : null;
39486
39610
  const effectiveMockData = parseResult.ok ? parseResult.mockData : {};
39487
39611
  const persistence = useMemo(() => {
39488
- if (!parsedSchema || serverUrl) return void 0;
39612
+ if (!parsedSchema || serverUrl || transport) return void 0;
39489
39613
  if (!autoMock) return void 0;
39490
39614
  const adapter = new InMemoryPersistence();
39491
39615
  adapter.seed(effectiveMockData);
39492
39616
  return adapter;
39493
- }, [parsedSchema, serverUrl, autoMock, effectiveMockData]);
39617
+ }, [parsedSchema, serverUrl, transport, autoMock, effectiveMockData]);
39494
39618
  const pages = useMemo(() => {
39495
39619
  if (!parsedSchema) return [];
39496
39620
  try {
@@ -39552,6 +39676,7 @@ function OrbPreview({
39552
39676
  {
39553
39677
  schema: parsedSchema,
39554
39678
  serverUrl,
39679
+ transport,
39555
39680
  mockData: effectiveMockData,
39556
39681
  pageName: currentPage,
39557
39682
  onNavigate: handleNavigate,
@@ -39564,5 +39689,47 @@ function OrbPreview({
39564
39689
  );
39565
39690
  }
39566
39691
  OrbPreview.displayName = "OrbPreview";
39692
+ function BrowserPlayground({
39693
+ schema,
39694
+ mode = "mock",
39695
+ initialPagePath,
39696
+ height,
39697
+ className
39698
+ }) {
39699
+ const [runtime] = useState(
39700
+ () => new OrbitalServerRuntime({ mode, debug: false })
39701
+ );
39702
+ useEffect(() => {
39703
+ void runtime.register(schema);
39704
+ return () => {
39705
+ runtime.unregisterAll();
39706
+ };
39707
+ }, [runtime, schema]);
39708
+ const transport = useMemo(() => ({
39709
+ register: async (s) => {
39710
+ await runtime.register(s);
39711
+ return true;
39712
+ },
39713
+ unregister: async () => {
39714
+ runtime.unregisterAll();
39715
+ },
39716
+ sendEvent: async (orbitalName, event, payload) => {
39717
+ return runtime.processOrbitalEvent(orbitalName, {
39718
+ event,
39719
+ payload
39720
+ });
39721
+ }
39722
+ }), [runtime]);
39723
+ return /* @__PURE__ */ jsx(
39724
+ OrbPreview,
39725
+ {
39726
+ schema,
39727
+ transport,
39728
+ initialPagePath,
39729
+ height,
39730
+ className
39731
+ }
39732
+ );
39733
+ }
39567
39734
 
39568
- export { EntitySchemaProvider, OrbPreview, ServerBridgeProvider, SlotsProvider, TraitContext, TraitProvider, adjustSchemaForMockData, buildMockData, clearSchemaCache, createClientEffectHandlers, prepareSchemaForPreview, useEntityDefinition, useEntitySchema, useEntitySchemaOptional, useResolvedSchema, useServerBridge, useSlotContent, useSlots, useSlotsActions, useTrait, useTraitContext, useTraitStateMachine };
39735
+ export { BrowserPlayground, EntitySchemaProvider, OrbPreview, ServerBridgeProvider, SlotsProvider, TraitContext, TraitProvider, adjustSchemaForMockData, buildMockData, clearSchemaCache, createClientEffectHandlers, prepareSchemaForPreview, useEntityDefinition, useEntitySchema, useEntitySchemaOptional, useResolvedSchema, useServerBridge, useSlotContent, useSlots, useSlotsActions, useTrait, useTraitContext, useTraitStateMachine };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/ui",
3
- "version": "4.10.7",
3
+ "version": "4.12.0",
4
4
  "description": "React UI components, hooks, and providers for Almadar",
5
5
  "type": "module",
6
6
  "main": "./dist/components/index.js",
@@ -121,7 +121,7 @@
121
121
  "@almadar/core": "^7.0.0",
122
122
  "@almadar/evaluator": ">=2.9.2",
123
123
  "@almadar/patterns": ">=2.17.1",
124
- "@almadar/runtime": "^5.5.0",
124
+ "@almadar/runtime": "^5.7.0",
125
125
  "@almadar/std": ">=6.4.1",
126
126
  "@almadar/syntax": ">=1.3.1",
127
127
  "@xyflow/react": "12.10.1",