@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.
@@ -1,4 +1,33 @@
1
1
  import type { ReactNode } from 'react';
2
+ import type { BusEventSource, EventPayload } from '@almadar/core';
3
+ interface OrbitalEventResponse {
4
+ success: boolean;
5
+ transitioned: boolean;
6
+ states: Record<string, string>;
7
+ /**
8
+ * Server-cascade events carried back in the response. Each entry has a
9
+ * `source: BusEventSource` stamped by the compiled handler (`emit ...
10
+ * { source: __ORBITAL_SOURCE }`) so the client can re-broadcast on the
11
+ * qualified `UI:Orbital.Trait.EVENT` bus key (gap #13).
12
+ */
13
+ emittedEvents?: Array<{
14
+ event: string;
15
+ payload?: EventPayload;
16
+ source?: BusEventSource;
17
+ }>;
18
+ data?: Record<string, unknown[]>;
19
+ clientEffects?: unknown[];
20
+ /**
21
+ * Same effects as `clientEffects`, paired with the trait that produced
22
+ * each one. When present, prefer this for trait attribution. Falls back
23
+ * to legacy `clientEffects` parsing on older servers.
24
+ */
25
+ clientEffectsByTrait?: Array<{
26
+ traitName: string;
27
+ effect: unknown[];
28
+ }>;
29
+ error?: string;
30
+ }
2
31
  export interface ServerClientEffect {
3
32
  type: 'render-ui' | 'navigate' | 'notify';
4
33
  slot?: string;
@@ -31,13 +60,40 @@ export interface ServerBridgeContextValue {
31
60
  connected: boolean;
32
61
  sendEvent: (orbitalName: string, event: string, payload?: Record<string, unknown>) => Promise<SendEventResult>;
33
62
  }
63
+ /**
64
+ * Transport adapter for ServerBridgeProvider. Decouples the bridge's
65
+ * cascade-rebroadcast / effect-parsing logic from its wire format.
66
+ *
67
+ * - The `serverUrl` mode (default) uses an HTTP transport that POSTs to
68
+ * `/register`, `/unregister`, `/:orbital/events`. This is what canonical
69
+ * playground-runtime (`tools/runtime-verify`) and apps/builder-server
70
+ * speak.
71
+ * - The `transport` mode lets a consumer plug in a direct function-call
72
+ * adapter — used by `<BrowserPlayground>` to invoke
73
+ * `OrbitalServerRuntime.processOrbitalEvent` in-process, no HTTP, no
74
+ * server. Both modes return the same `OrbitalEventResponse` shape so the
75
+ * cascade-rebroadcast logic is identical downstream.
76
+ */
77
+ export interface ServerBridgeTransport {
78
+ register: (schema: unknown) => Promise<boolean>;
79
+ unregister: () => Promise<void>;
80
+ sendEvent: (orbitalName: string, event: string, payload?: Record<string, unknown>) => Promise<OrbitalEventResponse>;
81
+ }
34
82
  /**
35
83
  * Access the server bridge. Returns a no-op stub when outside the provider.
36
84
  */
37
85
  export declare function useServerBridge(): ServerBridgeContextValue;
38
86
  export interface ServerBridgeProviderProps {
39
87
  schema: unknown;
40
- serverUrl: string;
88
+ /** HTTP server URL (canonical playground-runtime / apps/builder-server). */
89
+ serverUrl?: string;
90
+ /**
91
+ * Custom transport adapter. Use this for in-process execution (e.g.
92
+ * `<BrowserPlayground>` invokes `OrbitalServerRuntime.processOrbitalEvent`
93
+ * directly). Mutually exclusive with `serverUrl`.
94
+ */
95
+ transport?: ServerBridgeTransport;
41
96
  children: ReactNode;
42
97
  }
43
- export declare function ServerBridgeProvider({ schema, serverUrl, children, }: ServerBridgeProviderProps): import("react/jsx-runtime").JSX.Element;
98
+ export declare function ServerBridgeProvider({ schema, serverUrl, transport: customTransport, children, }: ServerBridgeProviderProps): import("react/jsx-runtime").JSX.Element;
99
+ export {};
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Embed-aware slot routing — static analysis pass.
3
+ *
4
+ * Walks every trait's `transitions[].effects` looking for `render-ui`
5
+ * patterns whose tree contains `@trait.<Name>` string literals. Returns
6
+ * the flat set of trait names that are referenced this way by some
7
+ * sibling layout.
8
+ *
9
+ * Used by `<OrbPreview>` to route `applyServerEffects` — when an
10
+ * embedded trait's render-ui effect arrives, the runtime updates that
11
+ * trait's per-trait sidecar (`traitIndexRef`) only and skips the slot
12
+ * write. The sibling layout owns the slot and embeds the trait's frame
13
+ * via `<TraitFrame>`. Mirrors the compiled-path codegen which inlines
14
+ * the atom views as JSX inside the layout's pattern, never having them
15
+ * write a shared slot.
16
+ *
17
+ * The walker is a structural twin of
18
+ * `packages/almadar-runtime/src/resolver/reference-resolver.ts`'s
19
+ * `renameEventsInRenderUiConfig` — same recursive pattern shape, just
20
+ * collecting `@trait.X` substrings instead of renaming events.
21
+ *
22
+ * @packageDocumentation
23
+ */
24
+ import type { OrbitalSchema } from '@almadar/core';
25
+ /**
26
+ * Build the flat set of trait names that are referenced via `@trait.X`
27
+ * by at least one trait's render-ui in the resolved schema.
28
+ *
29
+ * Safe to call on the resolved (post-inline) schema. Memoize by
30
+ * reference at the call site.
31
+ */
32
+ export declare function collectEmbeddedTraits(schema: OrbitalSchema | undefined | null): ReadonlySet<string>;
@@ -37,6 +37,7 @@ var langGraphql = require('react-syntax-highlighter/dist/esm/languages/prism/gra
37
37
  var core = require('@almadar/core');
38
38
  require('@tanstack/react-query');
39
39
  var runtime = require('@almadar/runtime');
40
+ var OrbitalServerRuntime = require('@almadar/runtime/OrbitalServerRuntime');
40
41
 
41
42
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
42
43
 
@@ -8229,13 +8230,13 @@ var init_MapView = __esm({
8229
8230
  shadowSize: [41, 41]
8230
8231
  });
8231
8232
  L.Marker.prototype.options.icon = defaultIcon;
8232
- const { useEffect: useEffect68, useRef: useRef65, useCallback: useCallback110, useState: useState102 } = React115__namespace.default;
8233
+ const { useEffect: useEffect69, useRef: useRef65, useCallback: useCallback110, useState: useState103 } = React115__namespace.default;
8233
8234
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
8234
8235
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
8235
8236
  function MapUpdater({ centerLat, centerLng, zoom }) {
8236
8237
  const map = useMap();
8237
8238
  const prevRef = useRef65({ centerLat, centerLng, zoom });
8238
- useEffect68(() => {
8239
+ useEffect69(() => {
8239
8240
  const prev = prevRef.current;
8240
8241
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
8241
8242
  map.setView([centerLat, centerLng], zoom);
@@ -8246,7 +8247,7 @@ var init_MapView = __esm({
8246
8247
  }
8247
8248
  function MapClickHandler({ onMapClick }) {
8248
8249
  const map = useMap();
8249
- useEffect68(() => {
8250
+ useEffect69(() => {
8250
8251
  if (!onMapClick) return;
8251
8252
  const handler = (e) => {
8252
8253
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -8273,7 +8274,7 @@ var init_MapView = __esm({
8273
8274
  showAttribution = true
8274
8275
  }) {
8275
8276
  const eventBus = useEventBus2();
8276
- const [clickedPosition, setClickedPosition] = useState102(null);
8277
+ const [clickedPosition, setClickedPosition] = useState103(null);
8277
8278
  const handleMapClick = useCallback110((lat, lng) => {
8278
8279
  if (showClickedPin) {
8279
8280
  setClickedPosition({ lat, lng });
@@ -38973,6 +38974,78 @@ init_Box();
38973
38974
  init_Typography();
38974
38975
  init_UISlotRenderer();
38975
38976
  init_useEventBus();
38977
+
38978
+ // runtime/embedded-traits.ts
38979
+ var TRAIT_BINDING_PREFIX = "@trait.";
38980
+ function collectTraitRefsFromValue(value, into) {
38981
+ if (value === null || value === void 0) return;
38982
+ if (typeof value === "string") {
38983
+ if (value.startsWith(TRAIT_BINDING_PREFIX)) {
38984
+ const rest = value.slice(TRAIT_BINDING_PREFIX.length);
38985
+ const dot = rest.indexOf(".");
38986
+ const traitName = dot === -1 ? rest : rest.slice(0, dot);
38987
+ if (traitName.length > 0) into.add(traitName);
38988
+ }
38989
+ return;
38990
+ }
38991
+ if (Array.isArray(value)) {
38992
+ for (const item of value) collectTraitRefsFromValue(item, into);
38993
+ return;
38994
+ }
38995
+ if (typeof value === "object") {
38996
+ for (const v of Object.values(value)) {
38997
+ collectTraitRefsFromValue(v, into);
38998
+ }
38999
+ }
39000
+ }
39001
+ function collectTraitRefsFromEffects(effects, into) {
39002
+ if (!effects) return;
39003
+ for (const effect of effects) {
39004
+ if (!Array.isArray(effect)) continue;
39005
+ if (effect[0] === "render-ui" && effect.length >= 3) {
39006
+ collectTraitRefsFromValue(effect[2], into);
39007
+ continue;
39008
+ }
39009
+ for (let i = 1; i < effect.length; i++) {
39010
+ const arg = effect[i];
39011
+ if (Array.isArray(arg)) collectTraitRefsFromEffects([arg], into);
39012
+ else collectTraitRefsFromValue(arg, into);
39013
+ }
39014
+ }
39015
+ }
39016
+ function collectEmbeddedTraits(schema) {
39017
+ const out = /* @__PURE__ */ new Set();
39018
+ if (!schema?.orbitals) return out;
39019
+ for (const orbital of schema.orbitals) {
39020
+ if (!orbital || typeof orbital !== "object") continue;
39021
+ const traits2 = orbital.traits;
39022
+ if (!Array.isArray(traits2)) continue;
39023
+ for (const trait of traits2) {
39024
+ if (!trait || typeof trait !== "object") continue;
39025
+ const resolved = trait._resolved;
39026
+ const target = resolved && typeof resolved === "object" ? resolved : trait;
39027
+ const stateMachine = target.stateMachine;
39028
+ const transitions = stateMachine?.transitions;
39029
+ if (!Array.isArray(transitions)) continue;
39030
+ for (const t of transitions) {
39031
+ if (!t || typeof t !== "object") continue;
39032
+ const effects = t.effects;
39033
+ collectTraitRefsFromEffects(effects, out);
39034
+ }
39035
+ const initialEffects = target.initialEffects;
39036
+ collectTraitRefsFromEffects(initialEffects, out);
39037
+ const ticks2 = target.ticks;
39038
+ if (Array.isArray(ticks2)) {
39039
+ for (const tick of ticks2) {
39040
+ collectTraitRefsFromEffects(tick?.effects, out);
39041
+ }
39042
+ }
39043
+ }
39044
+ }
39045
+ return out;
39046
+ }
39047
+
39048
+ // runtime/OrbPreview.tsx
38976
39049
  init_SlotsContext();
38977
39050
  init_EntitySchemaContext();
38978
39051
 
@@ -38980,6 +39053,38 @@ init_EntitySchemaContext();
38980
39053
  init_useEventBus();
38981
39054
  init_logger();
38982
39055
  var xOrbitalLog2 = createLogger("almadar:runtime:cross-orbital");
39056
+ function createHttpTransport(serverUrl) {
39057
+ return {
39058
+ register: async (schema) => {
39059
+ try {
39060
+ const res = await fetch(`${serverUrl}/register`, {
39061
+ method: "POST",
39062
+ headers: { "Content-Type": "application/json" },
39063
+ body: JSON.stringify({ schema })
39064
+ });
39065
+ const result = await res.json();
39066
+ return !!result.success;
39067
+ } catch (err) {
39068
+ console.error("[ServerBridge] Registration failed:", err);
39069
+ return false;
39070
+ }
39071
+ },
39072
+ unregister: async () => {
39073
+ try {
39074
+ await fetch(`${serverUrl}/unregister`, { method: "DELETE" });
39075
+ } catch {
39076
+ }
39077
+ },
39078
+ sendEvent: async (orbitalName, event, payload) => {
39079
+ const res = await fetch(`${serverUrl}/${orbitalName}/events`, {
39080
+ method: "POST",
39081
+ headers: { "Content-Type": "application/json" },
39082
+ body: JSON.stringify({ event, payload })
39083
+ });
39084
+ return res.json();
39085
+ }
39086
+ };
39087
+ }
38983
39088
  var ServerBridgeContext = React115.createContext(null);
38984
39089
  function useServerBridge() {
38985
39090
  const ctx = React115.useContext(ServerBridgeContext);
@@ -38992,40 +39097,34 @@ function useServerBridge() {
38992
39097
  function ServerBridgeProvider({
38993
39098
  schema,
38994
39099
  serverUrl,
39100
+ transport: customTransport,
38995
39101
  children
38996
39102
  }) {
39103
+ if (!serverUrl && !customTransport) {
39104
+ throw new Error("ServerBridgeProvider requires either serverUrl or transport");
39105
+ }
39106
+ if (serverUrl && customTransport) {
39107
+ throw new Error("ServerBridgeProvider accepts serverUrl OR transport, not both");
39108
+ }
38997
39109
  const eventBus = useEventBus();
38998
39110
  const [connected, setConnected] = React115.useState(false);
38999
- const registerSchema = React115.useCallback(async () => {
39000
- try {
39001
- const res = await fetch(`${serverUrl}/register`, {
39002
- method: "POST",
39003
- headers: { "Content-Type": "application/json" },
39004
- body: JSON.stringify({ schema })
39005
- });
39006
- const result = await res.json();
39007
- return !!result.success;
39008
- } catch (err) {
39009
- console.error("[ServerBridge] Registration failed:", err);
39010
- return false;
39011
- }
39012
- }, [schema, serverUrl]);
39013
- const unregisterSchema = React115.useCallback(async () => {
39014
- try {
39015
- await fetch(`${serverUrl}/unregister`, { method: "DELETE" });
39016
- } catch {
39017
- }
39018
- }, [serverUrl]);
39111
+ const transport = React115.useMemo(
39112
+ () => customTransport ?? createHttpTransport(serverUrl),
39113
+ [serverUrl, customTransport]
39114
+ );
39115
+ const registerSchema = React115.useCallback(
39116
+ async () => transport.register(schema),
39117
+ [schema, transport]
39118
+ );
39119
+ const unregisterSchema = React115.useCallback(
39120
+ async () => transport.unregister(),
39121
+ [transport]
39122
+ );
39019
39123
  const sendEvent = React115.useCallback(async (orbitalName, event, payload) => {
39020
39124
  const emptyMeta = { success: false, clientEffects: 0, dataEntities: {}, emittedEvents: [] };
39021
39125
  if (!connected) return { effects: [], meta: emptyMeta };
39022
39126
  try {
39023
- const res = await fetch(`${serverUrl}/${orbitalName}/events`, {
39024
- method: "POST",
39025
- headers: { "Content-Type": "application/json" },
39026
- body: JSON.stringify({ event, payload })
39027
- });
39028
- const result = await res.json();
39127
+ const result = await transport.sendEvent(orbitalName, event, payload);
39029
39128
  const effects = [];
39030
39129
  const responseData = result.data || {};
39031
39130
  const dataEntities = {};
@@ -39083,7 +39182,7 @@ function ServerBridgeProvider({
39083
39182
  console.error("[ServerBridge] Event send failed:", err);
39084
39183
  return { effects: [], meta: { ...emptyMeta, error: err instanceof Error ? err.message : String(err) } };
39085
39184
  }
39086
- }, [connected, serverUrl, eventBus]);
39185
+ }, [connected, transport, eventBus]);
39087
39186
  React115.useEffect(() => {
39088
39187
  if (!schema) return;
39089
39188
  let cancelled = false;
@@ -39270,32 +39369,49 @@ function SlotBridge() {
39270
39369
  }, [slots, render, clear]);
39271
39370
  return null;
39272
39371
  }
39273
- function applyServerEffects(effects, uiSlots, onNavigate) {
39372
+ function applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits) {
39274
39373
  for (const eff of effects) {
39275
39374
  if (eff.type === "render-ui" && eff.slot && eff.pattern) {
39276
39375
  const patternRecord = eff.pattern;
39277
39376
  const { type: patternType, children, ...inlineProps } = patternRecord;
39278
39377
  const normalizedChildren = Array.isArray(children) ? children.map((c) => normalizeChild(c)) : children;
39279
- xOrbitalLog3.info("slot-write", {
39280
- slot: eff.slot,
39281
- sourceTrait: eff.traitName ?? "server",
39282
- patternType: typeof patternType === "string" ? patternType : void 0
39283
- });
39284
- uiSlots.render({
39285
- target: eff.slot,
39286
- pattern: patternType,
39287
- props: {
39288
- ...inlineProps,
39289
- ...normalizedChildren !== void 0 ? { children: normalizedChildren } : {}
39290
- },
39291
- sourceTrait: eff.traitName ?? "server"
39292
- });
39378
+ const sourceTrait = eff.traitName ?? "server";
39379
+ const isEmbedded = embeddedTraits?.has(sourceTrait) ?? false;
39380
+ const props = {
39381
+ ...inlineProps,
39382
+ ...normalizedChildren !== void 0 ? { children: normalizedChildren } : {}
39383
+ };
39384
+ if (isEmbedded) {
39385
+ xOrbitalLog3.info("slot:embed-routed", {
39386
+ sourceTrait,
39387
+ slot: eff.slot,
39388
+ patternType: typeof patternType === "string" ? patternType : void 0
39389
+ });
39390
+ uiSlots.updateTraitContent(sourceTrait, {
39391
+ pattern: patternType,
39392
+ props,
39393
+ priority: 0,
39394
+ animation: "fade"
39395
+ });
39396
+ } else {
39397
+ xOrbitalLog3.info("slot-write", {
39398
+ slot: eff.slot,
39399
+ sourceTrait,
39400
+ patternType: typeof patternType === "string" ? patternType : void 0
39401
+ });
39402
+ uiSlots.render({
39403
+ target: eff.slot,
39404
+ pattern: patternType,
39405
+ props,
39406
+ sourceTrait
39407
+ });
39408
+ }
39293
39409
  } else if (eff.type === "navigate" && eff.route && onNavigate) {
39294
39410
  onNavigate(eff.route, eff.params);
39295
39411
  }
39296
39412
  }
39297
39413
  }
39298
- function TraitInitializer({ traits: traits2, orbitalNames, onNavigate, onLocalFallback, persistence, traitConfigsByName, orbitalsByTrait }) {
39414
+ function TraitInitializer({ traits: traits2, orbitalNames, onNavigate, onLocalFallback, persistence, traitConfigsByName, orbitalsByTrait, embeddedTraits }) {
39299
39415
  const slotsActions = useSlotsActions();
39300
39416
  const bridge = useServerBridge();
39301
39417
  const uiSlots = context.useUISlots();
@@ -39311,9 +39427,9 @@ function TraitInitializer({ traits: traits2, orbitalNames, onNavigate, onLocalFa
39311
39427
  for (const name of targets) {
39312
39428
  const { effects, meta } = await bridge.sendEvent(name, event, payload);
39313
39429
  recordServerResponse(name, event, meta);
39314
- applyServerEffects(effects, uiSlots, onNavigate);
39430
+ applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits);
39315
39431
  }
39316
- }, [bridge.connected, bridge.sendEvent, orbitalNames, uiSlots, onNavigate]);
39432
+ }, [bridge.connected, bridge.sendEvent, orbitalNames, uiSlots, onNavigate, embeddedTraits]);
39317
39433
  const opts = orbitalNames ? { onEventProcessed, navigate: onNavigate, traitConfigsByName, orbitalsByTrait } : { navigate: onNavigate, persistence, traitConfigsByName, orbitalsByTrait };
39318
39434
  const { sendEvent } = useTraitStateMachine(traits2, slotsActions, opts);
39319
39435
  const initSentRef = React115.useRef(false);
@@ -39353,13 +39469,13 @@ function TraitInitializer({ traits: traits2, orbitalNames, onNavigate, onLocalFa
39353
39469
  effects: effectTraces,
39354
39470
  timestamp: Date.now()
39355
39471
  });
39356
- applyServerEffects(effects, uiSlots, onNavigate);
39472
+ applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits);
39357
39473
  }
39358
39474
  })();
39359
- }, [bridge.connected, orbitalNames, bridge.sendEvent, uiSlots, onNavigate]);
39475
+ }, [bridge.connected, orbitalNames, bridge.sendEvent, uiSlots, onNavigate, embeddedTraits]);
39360
39476
  return null;
39361
39477
  }
39362
- function SchemaRunner({ schema, serverUrl, mockData, pageName, onNavigate, onLocalFallback, persistence }) {
39478
+ function SchemaRunner({ schema, serverUrl, transport, mockData, pageName, onNavigate, onLocalFallback, persistence }) {
39363
39479
  const { traits: traits2, allEntities, ir } = useResolvedSchema(schema, pageName);
39364
39480
  const allPageTraits = React115.useMemo(() => {
39365
39481
  if (pageName && traits2.length > 0) return traits2;
@@ -39462,6 +39578,9 @@ function SchemaRunner({ schema, serverUrl, mockData, pageName, onNavigate, onLoc
39462
39578
  }
39463
39579
  return map;
39464
39580
  }, [schema]);
39581
+ const embeddedTraits = React115.useMemo(() => {
39582
+ return collectEmbeddedTraits(schema);
39583
+ }, [schema]);
39465
39584
  const inner = /* @__PURE__ */ jsxRuntime.jsx(providers.VerificationProvider, { enabled: true, children: /* @__PURE__ */ jsxRuntime.jsx(SlotsProvider, { children: /* @__PURE__ */ jsxRuntime.jsxs(
39466
39585
  EntitySchemaProvider,
39467
39586
  {
@@ -39473,9 +39592,10 @@ function SchemaRunner({ schema, serverUrl, mockData, pageName, onNavigate, onLoc
39473
39592
  TraitInitializer,
39474
39593
  {
39475
39594
  traits: allPageTraits,
39476
- orbitalNames: serverUrl ? pageOrbitalNames : void 0,
39595
+ orbitalNames: serverUrl || transport ? pageOrbitalNames : void 0,
39477
39596
  traitConfigsByName,
39478
39597
  orbitalsByTrait,
39598
+ embeddedTraits,
39479
39599
  onNavigate,
39480
39600
  onLocalFallback,
39481
39601
  persistence
@@ -39486,8 +39606,8 @@ function SchemaRunner({ schema, serverUrl, mockData, pageName, onNavigate, onLoc
39486
39606
  ]
39487
39607
  }
39488
39608
  ) }) });
39489
- if (serverUrl) {
39490
- return /* @__PURE__ */ jsxRuntime.jsx(ServerBridgeProvider, { schema, serverUrl, children: inner });
39609
+ if (serverUrl || transport) {
39610
+ return /* @__PURE__ */ jsxRuntime.jsx(ServerBridgeProvider, { schema, serverUrl, transport, children: inner });
39491
39611
  }
39492
39612
  return inner;
39493
39613
  }
@@ -39498,8 +39618,12 @@ function OrbPreview({
39498
39618
  height = "400px",
39499
39619
  className,
39500
39620
  serverUrl,
39621
+ transport,
39501
39622
  initialPagePath
39502
39623
  }) {
39624
+ if (serverUrl && transport) {
39625
+ throw new Error("OrbPreview accepts serverUrl OR transport, not both");
39626
+ }
39503
39627
  const [localFallback, setLocalFallback] = React115.useState(false);
39504
39628
  const eventBus = useEventBus();
39505
39629
  const handleLocalFallback = React115.useCallback(() => {
@@ -39521,21 +39645,21 @@ function OrbPreview({
39521
39645
  } else {
39522
39646
  parsed = schema;
39523
39647
  }
39524
- if (autoMock && !serverUrl) {
39648
+ if (autoMock && !serverUrl && !transport) {
39525
39649
  const prepared = prepareSchemaForPreview(parsed);
39526
39650
  return { ok: true, schema: prepared.schema, mockData: prepared.mockData };
39527
39651
  }
39528
39652
  return { ok: true, schema: parsed, mockData: mockData ?? {} };
39529
- }, [schema, autoMock, serverUrl, mockData]);
39653
+ }, [schema, autoMock, serverUrl, transport, mockData]);
39530
39654
  const parsedSchema = parseResult.ok ? parseResult.schema : null;
39531
39655
  const effectiveMockData = parseResult.ok ? parseResult.mockData : {};
39532
39656
  const persistence = React115.useMemo(() => {
39533
- if (!parsedSchema || serverUrl) return void 0;
39657
+ if (!parsedSchema || serverUrl || transport) return void 0;
39534
39658
  if (!autoMock) return void 0;
39535
39659
  const adapter = new runtime.InMemoryPersistence();
39536
39660
  adapter.seed(effectiveMockData);
39537
39661
  return adapter;
39538
- }, [parsedSchema, serverUrl, autoMock, effectiveMockData]);
39662
+ }, [parsedSchema, serverUrl, transport, autoMock, effectiveMockData]);
39539
39663
  const pages = React115.useMemo(() => {
39540
39664
  if (!parsedSchema) return [];
39541
39665
  try {
@@ -39597,6 +39721,7 @@ function OrbPreview({
39597
39721
  {
39598
39722
  schema: parsedSchema,
39599
39723
  serverUrl,
39724
+ transport,
39600
39725
  mockData: effectiveMockData,
39601
39726
  pageName: currentPage,
39602
39727
  onNavigate: handleNavigate,
@@ -39609,7 +39734,50 @@ function OrbPreview({
39609
39734
  );
39610
39735
  }
39611
39736
  OrbPreview.displayName = "OrbPreview";
39737
+ function BrowserPlayground({
39738
+ schema,
39739
+ mode = "mock",
39740
+ initialPagePath,
39741
+ height,
39742
+ className
39743
+ }) {
39744
+ const [runtime] = React115.useState(
39745
+ () => new OrbitalServerRuntime.OrbitalServerRuntime({ mode, debug: false })
39746
+ );
39747
+ React115.useEffect(() => {
39748
+ void runtime.register(schema);
39749
+ return () => {
39750
+ runtime.unregisterAll();
39751
+ };
39752
+ }, [runtime, schema]);
39753
+ const transport = React115.useMemo(() => ({
39754
+ register: async (s) => {
39755
+ await runtime.register(s);
39756
+ return true;
39757
+ },
39758
+ unregister: async () => {
39759
+ runtime.unregisterAll();
39760
+ },
39761
+ sendEvent: async (orbitalName, event, payload) => {
39762
+ return runtime.processOrbitalEvent(orbitalName, {
39763
+ event,
39764
+ payload
39765
+ });
39766
+ }
39767
+ }), [runtime]);
39768
+ return /* @__PURE__ */ jsxRuntime.jsx(
39769
+ OrbPreview,
39770
+ {
39771
+ schema,
39772
+ transport,
39773
+ initialPagePath,
39774
+ height,
39775
+ className
39776
+ }
39777
+ );
39778
+ }
39612
39779
 
39780
+ exports.BrowserPlayground = BrowserPlayground;
39613
39781
  exports.EntitySchemaProvider = EntitySchemaProvider;
39614
39782
  exports.OrbPreview = OrbPreview;
39615
39783
  exports.ServerBridgeProvider = ServerBridgeProvider;
@@ -13,6 +13,7 @@ export { TraitProvider, TraitContext, useTraitContext, useTrait, type TraitConte
13
13
  export { SlotsProvider, useSlots, useSlotContent, useSlotsActions, type SlotsState, type SlotState, type SlotPatternEntry, type SlotSource, type SlotsActions, type SlotsProviderProps, } from './ui/SlotsContext';
14
14
  export { createClientEffectHandlers, type ClientEventBus, type SlotSetter, type CreateClientEffectHandlersOptions, } from './createClientEffectHandlers';
15
15
  export { OrbPreview, type OrbPreviewProps } from './OrbPreview';
16
+ export { BrowserPlayground, type BrowserPlaygroundProps } from './BrowserPlayground';
16
17
  export { prepareSchemaForPreview, buildMockData, adjustSchemaForMockData, type PreparedPreviewSchema, } from './prepareSchemaForPreview';
17
- export { ServerBridgeProvider, useServerBridge, type ServerBridgeContextValue, type ServerClientEffect } from './ServerBridge';
18
+ export { ServerBridgeProvider, useServerBridge, type ServerBridgeContextValue, type ServerBridgeTransport, type ServerClientEffect } from './ServerBridge';
18
19
  export type { ResolvedTraitBinding, ResolvedTrait, ResolvedEntity, ResolvedPage, ResolvedIR, } from './types';