@almadar/ui 6.58.0 → 6.61.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.
- package/dist/BehaviorValueFrame-3ORZHPWQ.css +1252 -0
- package/dist/BehaviorValueFrame-QTD7BWOI.js +18 -0
- package/dist/{Canvas3DHost-BTvRPfsr.d.ts → Canvas3DHost-CNjANoaU.d.ts} +1 -1
- package/dist/{GameAudioProvider-kXbonWTv.d.ts → GameAudioProvider-Dv_L-PHt.d.ts} +1 -1
- package/dist/TraitValueFrame-2WMYJOA5.css +1252 -0
- package/dist/TraitValueFrame-WOXNLLMM.js +17 -0
- package/dist/Typography-BY4A2NBY.js +5 -0
- package/dist/{UISlotRenderer-N5DFYE6K.js → UISlotRenderer-EEOXSJCI.js} +10 -8
- package/dist/avl/index.cjs +31960 -27831
- package/dist/avl/index.d.ts +36 -13
- package/dist/avl/index.js +331 -6147
- package/dist/{avl-3d-context-BpBKZIbc.d.ts → avl-3d-context-Dxun3-ri.d.ts} +2 -0
- package/dist/{chunk-I2ZNE5TD.js → chunk-6PV4LASE.js} +9495 -639
- package/dist/{chunk-723MJUU3.js → chunk-6ZCU3BQN.js} +1 -1
- package/dist/{chunk-GZGDBS4S.js → chunk-7NIU5GDC.js} +2 -2
- package/dist/{chunk-YYYHH5BA.js → chunk-CB64IOYD.js} +0 -4
- package/dist/{chunk-MZIDOJMD.js → chunk-EIIQGOZW.js} +2 -1
- package/dist/chunk-F7NZ3KZQ.js +434 -0
- package/dist/{chunk-LZRW6GXG.js → chunk-FYX64CDL.js} +0 -2
- package/dist/{chunk-4ZGXIW3B.js → chunk-KZRAMPM6.js} +1 -1
- package/dist/{chunk-VHTJRS2N.js → chunk-L3MTZ3RH.js} +7 -0
- package/dist/chunk-LUREMDI3.js +49 -0
- package/dist/{chunk-HA7CJWQX.js → chunk-MGN6B6OQ.js} +1477 -1177
- package/dist/{chunk-Q47ZN2DW.js → chunk-N23SVGTH.js} +83 -1
- package/dist/{chunk-KJGVLXEN.js → chunk-N4IJS5DD.js} +50 -116
- package/dist/{chunk-I5QOYW2Z.js → chunk-UTSDPJUP.js} +205 -2
- package/dist/{chunk-V3FGTQUE.js → chunk-ZR45FGW3.js} +7 -1
- package/dist/{cn-DIql5232.d.ts → cn-CDIhHCUC.d.ts} +40 -16
- package/dist/components/index.cjs +14455 -4099
- package/dist/components/index.d.ts +165 -177
- package/dist/components/index.js +14 -15
- package/dist/context/index.cjs +16 -1
- package/dist/context/index.js +4 -2
- package/dist/hooks/index.cjs +79 -6
- package/dist/hooks/index.d.ts +43 -2
- package/dist/hooks/index.js +6 -7
- package/dist/lib/drawable/three/index.cjs +350 -26
- package/dist/lib/drawable/three/index.d.ts +5 -5
- package/dist/lib/drawable/three/index.js +28 -7
- package/dist/lib/index.cjs +90 -4
- package/dist/lib/index.d.ts +3 -3
- package/dist/lib/index.js +74 -6
- package/dist/locales/index.cjs +0 -6
- package/dist/locales/index.js +2 -2
- package/dist/{paintDispatch-BN8Vbzgs.d.ts → paintDispatch-Cp1_S93j.d.ts} +30 -2
- package/dist/providers/index.cjs +13853 -1075
- package/dist/providers/index.d.ts +38 -6
- package/dist/providers/index.js +40 -30
- package/dist/runtime/index.cjs +28369 -18287
- package/dist/runtime/index.d.ts +65 -3
- package/dist/runtime/index.js +13 -379
- package/dist/ssr/index.cjs +508 -133
- package/dist/ssr/index.d.cts +56 -4
- package/dist/ssr/index.d.ts +56 -4
- package/dist/ssr/index.js +446 -71
- package/dist/{verificationRegistry-CK76GUfH.d.ts → verificationRegistry-B5aGl7WF.d.ts} +3 -2
- package/locales/ar.json +0 -2
- package/locales/en.json +0 -2
- package/locales/sl.json +0 -2
- package/package.json +7 -7
- package/tailwind-preset.cjs +78 -8
- package/themes/_contract.md +6 -0
- package/themes/_fonts.css +4 -4
- package/themes/_foundation.css +11 -0
- package/themes/_presets.css +2 -0
- package/themes/almadar-website.css +10 -0
- package/themes/almadar.css +10 -0
- package/themes/atelier.css +10 -0
- package/themes/bauhaus.css +10 -0
- package/themes/bloomberg-dense.css +10 -0
- package/themes/clay.css +10 -0
- package/themes/corporate.css +10 -0
- package/themes/gazette.css +10 -0
- package/themes/glass.css +10 -0
- package/themes/kflow.css +5 -0
- package/themes/kiosk.css +10 -0
- package/themes/linear-clean.css +10 -0
- package/themes/material.css +10 -0
- package/themes/minimalist.css +10 -0
- package/themes/nata.css +304 -0
- package/themes/neon.css +10 -0
- package/themes/notion-editorial.css +10 -0
- package/themes/orb.css +10 -0
- package/themes/prism.css +10 -0
- package/themes/rawi.css +304 -0
- package/themes/retro.css +10 -0
- package/themes/risograph.css +10 -0
- package/themes/scandi.css +10 -0
- package/themes/solarpunk.css +10 -0
- package/themes/terminal.css +138 -128
- package/dist/Typography-LZTP72Y4.js +0 -5
- package/dist/chunk-D2E44ODB.js +0 -350
- package/dist/chunk-HB3D3HUJ.js +0 -2265
- package/dist/chunk-N7JIG57B.js +0 -138
package/dist/chunk-HB3D3HUJ.js
DELETED
|
@@ -1,2265 +0,0 @@
|
|
|
1
|
-
import { useTranslate, useRenderI18n, runTickFrame } from './chunk-N7JIG57B.js';
|
|
2
|
-
import { ALL_SLOTS } from './chunk-VHTJRS2N.js';
|
|
3
|
-
import { LoadingState, ErrorState, UISlotRenderer } from './chunk-I2ZNE5TD.js';
|
|
4
|
-
import { Box } from './chunk-HA7CJWQX.js';
|
|
5
|
-
import { Typography } from './chunk-4ZGXIW3B.js';
|
|
6
|
-
import { beginPending, endPending } from './chunk-I5QOYW2Z.js';
|
|
7
|
-
import { useEventBus } from './chunk-CHNC4UYE.js';
|
|
8
|
-
import { registerTrait, unregisterTrait, perfTimeAsync } from './chunk-KJGVLXEN.js';
|
|
9
|
-
import { bindTraitStateGetter, registerTraitSnapshot, recordTransition, recordingTransport, recordServerResponse } from './chunk-Q47ZN2DW.js';
|
|
10
|
-
import { isNotificationSlot } from './chunk-PEST66PN.js';
|
|
11
|
-
import React, { createContext, useState, useMemo, useEffect, useRef, useCallback, useSyncExternalStore, useContext } from 'react';
|
|
12
|
-
import { schemaToIR, getPage, clearSchemaCache as clearSchemaCache$1, traitsEmbeddedByEvent, traitReferencesCallsitePayload, configReferencesCallsitePayload, TRAIT_MOUNT_ERROR_TESTID, traitsEmbeddedByEffects, applyListenPayloadMapping, buildResolvedTraitConfigs, collectCallsiteCaptureChildren, orbitalInlineEntities, storesRowsInBrowser, walkSExpr, isRenderBindingMarker, containsEntityBinding, isSExpr, isEventPayloadValue, RENDER_BINDING_MARKER } from '@almadar/core';
|
|
13
|
-
import { createLogger } from '@almadar/logger';
|
|
14
|
-
import { collectListenerTargets, LIFECYCLE_EVENTS, TraitMountError, loadLazyPage, createInProcessTransport, buildTraitIndex, createMemoryCircuitStore, createLocalStoreTransport, createResidenceTransport, createClientKernel, createTickScheduler, isValidCronExpression, parseDurationString, UNMOUNT_EVENT, createClientEffectHandlers, EffectExecutor, isSExpression, collectDeclaredConfigDefaults, createContextFromBindings, openBrowserStore, browserStoreName, normalizeCallSiteConfigToValues } from '@almadar/runtime';
|
|
15
|
-
import { useUser, useArbitraryClassStyles, getAllPages, matchPathAmong, CurrentPagePathProvider, NavStackProvider, OrbitalProvider, ServerBridgeProvider, useNavStack, VerificationProvider, EntitySchemaProvider, OrbitalThemeProvider, useServerBridge, EntityBindingContext, AwaitingSkeletonContext } from '@almadar/ui/providers';
|
|
16
|
-
import { evaluateListenPayloadExpr, evaluate, createMinimalContext, evaluateGuard, executeEffects } from '@almadar/evaluator';
|
|
17
|
-
import { getPatternDefinition } from '@almadar/core/patterns';
|
|
18
|
-
import { isKnownStdOperator } from '@almadar/std/registry';
|
|
19
|
-
import { prepareSchemaForPreview, collectTraitRefsFromResolvedTrait, buildOrbitalsByTrait, collectEmbeddedTraits } from '@almadar/runtime/ui';
|
|
20
|
-
export { adjustSchemaForMockData, buildMockData, collectEmbeddedTraits, prepareSchemaForPreview } from '@almadar/runtime/ui';
|
|
21
|
-
import { useInRouterContext, useHref } from 'react-router-dom';
|
|
22
|
-
import { UISlotProvider, useUISlots } from '@almadar/ui/context';
|
|
23
|
-
import { useTranslate as useTranslate$1, createTranslate, I18nProvider } from '@almadar/ui/hooks';
|
|
24
|
-
import { InMemoryPersistence } from '@almadar/db/mock';
|
|
25
|
-
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
26
|
-
import { OrbitalServerRuntime } from '@almadar/runtime/OrbitalServerRuntime';
|
|
27
|
-
import { localeDirection } from '@almadar/core/i18n';
|
|
28
|
-
|
|
29
|
-
var resolvedSchemaLog = createLogger("almadar:ui:resolved-schema");
|
|
30
|
-
function useResolvedSchema(schema, pageName) {
|
|
31
|
-
const [loading, setLoading] = useState(true);
|
|
32
|
-
const [error, setError] = useState(null);
|
|
33
|
-
const ir = useMemo(() => {
|
|
34
|
-
if (!schema) return null;
|
|
35
|
-
try {
|
|
36
|
-
const resolved = schemaToIR(schema);
|
|
37
|
-
const callSiteListensByTrait = /* @__PURE__ */ new Map();
|
|
38
|
-
for (const orb of schema.orbitals) {
|
|
39
|
-
for (const traitRef of orb.traits ?? []) {
|
|
40
|
-
if (typeof traitRef === "string") continue;
|
|
41
|
-
const t = traitRef;
|
|
42
|
-
const inlineListens = t.listens?.length ? t.listens : void 0;
|
|
43
|
-
const resolvedListens = t._resolved?.listens?.length ? t._resolved.listens : void 0;
|
|
44
|
-
const listens = inlineListens ?? resolvedListens;
|
|
45
|
-
const traitName = t.name ?? t._resolved?.name;
|
|
46
|
-
if (typeof traitName !== "string" || !listens) continue;
|
|
47
|
-
callSiteListensByTrait.set(traitName, listens);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
for (const [traitName, trait] of resolved.traits) {
|
|
51
|
-
const callSiteListens = callSiteListensByTrait.get(traitName);
|
|
52
|
-
if (!callSiteListens) continue;
|
|
53
|
-
const beforeCount = trait.listens.length;
|
|
54
|
-
trait.listens = callSiteListens.map((l) => ({
|
|
55
|
-
event: l.event,
|
|
56
|
-
triggers: l.triggers,
|
|
57
|
-
source: l.source,
|
|
58
|
-
guard: l.guard,
|
|
59
|
-
payloadMapping: l.payloadMapping
|
|
60
|
-
}));
|
|
61
|
-
resolvedSchemaLog.info("listens:restored", {
|
|
62
|
-
trait: traitName,
|
|
63
|
-
beforeCount,
|
|
64
|
-
afterCount: trait.listens.length,
|
|
65
|
-
listens: callSiteListens.map((l) => {
|
|
66
|
-
const src = l.source;
|
|
67
|
-
const label = !src ? "(no-source)" : src.kind === "any" ? "*" : src.trait;
|
|
68
|
-
return `${label}.${l.event}->${l.triggers}`;
|
|
69
|
-
}).join(",")
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
return resolved;
|
|
73
|
-
} catch (err) {
|
|
74
|
-
setError(err instanceof Error ? err.message : "Schema resolution failed");
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
77
|
-
}, [schema]);
|
|
78
|
-
useEffect(() => {
|
|
79
|
-
setLoading(false);
|
|
80
|
-
}, [ir]);
|
|
81
|
-
const result = useMemo(() => {
|
|
82
|
-
if (!ir) {
|
|
83
|
-
return {
|
|
84
|
-
page: void 0,
|
|
85
|
-
traits: [],
|
|
86
|
-
entities: /* @__PURE__ */ new Map(),
|
|
87
|
-
allEntities: /* @__PURE__ */ new Map(),
|
|
88
|
-
allTraits: /* @__PURE__ */ new Map(),
|
|
89
|
-
loading,
|
|
90
|
-
error: error || (schema ? null : "No schema provided"),
|
|
91
|
-
ir: null
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
const page = getPage(ir, pageName);
|
|
95
|
-
resolvedSchemaLog.debug("Resolved page", () => ({
|
|
96
|
-
name: page?.name,
|
|
97
|
-
path: page?.path,
|
|
98
|
-
traits: page?.traits.length
|
|
99
|
-
}));
|
|
100
|
-
const traits = page?.traits || [];
|
|
101
|
-
const entities = /* @__PURE__ */ new Map();
|
|
102
|
-
if (page) {
|
|
103
|
-
for (const binding of page.entityBindings) {
|
|
104
|
-
entities.set(binding.entity.name, binding.entity);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return {
|
|
108
|
-
page,
|
|
109
|
-
traits,
|
|
110
|
-
entities,
|
|
111
|
-
allEntities: ir.entities,
|
|
112
|
-
allTraits: ir.traits,
|
|
113
|
-
loading,
|
|
114
|
-
error,
|
|
115
|
-
ir
|
|
116
|
-
};
|
|
117
|
-
}, [ir, pageName, loading, error, schema]);
|
|
118
|
-
return result;
|
|
119
|
-
}
|
|
120
|
-
function clearSchemaCache() {
|
|
121
|
-
clearSchemaCache$1();
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// lib/circuitVerificationObserver.ts
|
|
125
|
-
function createCircuitVerificationObserver() {
|
|
126
|
-
return {
|
|
127
|
-
onTransition(trace) {
|
|
128
|
-
const effects = trace.effects.map((effect) => ({
|
|
129
|
-
type: effect.type,
|
|
130
|
-
args: effect.args,
|
|
131
|
-
status: effect.status,
|
|
132
|
-
...effect.error !== void 0 ? { error: effect.error } : {},
|
|
133
|
-
...effect.durationMs !== void 0 ? { durationMs: effect.durationMs } : {}
|
|
134
|
-
}));
|
|
135
|
-
recordTransition({
|
|
136
|
-
traitName: trace.traitName,
|
|
137
|
-
from: trace.from,
|
|
138
|
-
to: trace.to,
|
|
139
|
-
event: trace.event,
|
|
140
|
-
effects,
|
|
141
|
-
timestamp: Date.now(),
|
|
142
|
-
...trace.guardResult !== void 0 ? { guardResult: trace.guardResult } : {}
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
function recordDispatchVerdict(orbitalName, event, verdict) {
|
|
148
|
-
if ("error" in verdict) {
|
|
149
|
-
recordServerResponse(orbitalName, event, {
|
|
150
|
-
success: false,
|
|
151
|
-
clientEffects: 0,
|
|
152
|
-
dataEntities: {},
|
|
153
|
-
emittedEvents: [],
|
|
154
|
-
error: verdict.error instanceof Error ? `${verdict.error.name}: ${verdict.error.message}` : verdict.error
|
|
155
|
-
});
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
const { response } = verdict;
|
|
159
|
-
const dataEntities = {};
|
|
160
|
-
for (const [entity, rows] of Object.entries(response.data ?? {})) dataEntities[entity] = rows.length;
|
|
161
|
-
recordServerResponse(orbitalName, event, {
|
|
162
|
-
success: response.success,
|
|
163
|
-
transitioned: response.transitioned,
|
|
164
|
-
clientEffects: response.clientEffects?.length ?? 0,
|
|
165
|
-
dataEntities,
|
|
166
|
-
emittedEvents: response.emittedEvents.map((e) => e.event),
|
|
167
|
-
emitted: response.emittedEvents.map((e) => e.payload !== void 0 ? { event: e.event, payload: e.payload } : { event: e.event }),
|
|
168
|
-
...response.error !== void 0 ? { error: response.error } : {}
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
var tabClientId;
|
|
172
|
-
function getTabClientId() {
|
|
173
|
-
if (tabClientId === void 0) tabClientId = crypto.randomUUID();
|
|
174
|
-
return tabClientId;
|
|
175
|
-
}
|
|
176
|
-
var log = createLogger("almadar:ui:circuit-kernel");
|
|
177
|
-
function postUnmounts(transport, traits, locale) {
|
|
178
|
-
for (const [traitName, orbitalName] of traits) {
|
|
179
|
-
transport.send(orbitalName, { event: UNMOUNT_EVENT, targetTrait: traitName, clientId: getTabClientId(), locale }).catch((err) => log.warn("unmount-post-failed", { trait: traitName, orbital: orbitalName, error: String(err) }));
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
function restrictTraitIndex(full, activeNames) {
|
|
183
|
-
const byName = /* @__PURE__ */ new Map();
|
|
184
|
-
for (const [name, entry] of full.byName) {
|
|
185
|
-
if (activeNames.has(name)) byName.set(name, entry);
|
|
186
|
-
}
|
|
187
|
-
return { byName, allEntities: full.allEntities, orbitals: full.orbitals };
|
|
188
|
-
}
|
|
189
|
-
function useCircuitKernel(traitBindings, options) {
|
|
190
|
-
const hostRunsProgram = options.transport?.hostsBrowserStore === true;
|
|
191
|
-
const fullTraitIndex = useMemo(
|
|
192
|
-
() => buildTraitIndex(options.orbitals, options.traitConfigsByName, { hostRunsProgram }),
|
|
193
|
-
[options.orbitals, options.traitConfigsByName, hostRunsProgram]
|
|
194
|
-
);
|
|
195
|
-
const appTraitNames = useMemo(() => new Set(fullTraitIndex.byName.keys()), [fullTraitIndex]);
|
|
196
|
-
const activeTraitKey = traitBindings.map((b) => b.trait.name).filter((n) => !!n).sort().join("\0");
|
|
197
|
-
const traitIndex = useMemo(
|
|
198
|
-
() => restrictTraitIndex(fullTraitIndex, new Set(activeTraitKey === "" ? [] : activeTraitKey.split("\0"))),
|
|
199
|
-
[activeTraitKey, fullTraitIndex]
|
|
200
|
-
);
|
|
201
|
-
const mountedRef = useRef(/* @__PURE__ */ new Map());
|
|
202
|
-
const { locale } = useTranslate();
|
|
203
|
-
const i18n = useRenderI18n();
|
|
204
|
-
const localeRef = useRef(locale);
|
|
205
|
-
localeRef.current = locale;
|
|
206
|
-
const transport = options.transport;
|
|
207
|
-
const topologyPending = options.awaitTopology === true;
|
|
208
|
-
useEffect(() => {
|
|
209
|
-
if (transport === void 0 || topologyPending) return;
|
|
210
|
-
const mounted = mountedRef.current;
|
|
211
|
-
const dropped = [...mounted].filter(([name]) => !traitIndex.byName.has(name));
|
|
212
|
-
mounted.clear();
|
|
213
|
-
for (const [name, entry] of traitIndex.byName) mounted.set(name, entry.orbitalName);
|
|
214
|
-
postUnmounts(transport, dropped, localeRef.current);
|
|
215
|
-
}, [traitIndex, transport, topologyPending]);
|
|
216
|
-
useEffect(() => {
|
|
217
|
-
if (transport === void 0) return;
|
|
218
|
-
const mounted = mountedRef.current;
|
|
219
|
-
return () => postUnmounts(transport, [...mounted], localeRef.current);
|
|
220
|
-
}, [transport]);
|
|
221
|
-
const store = useMemo(() => {
|
|
222
|
-
const traitDefs = Array.from(traitIndex.byName.values(), (entry) => entry.traitDef);
|
|
223
|
-
const config = {
|
|
224
|
-
...options.guardMode !== void 0 ? { guardMode: options.guardMode } : {},
|
|
225
|
-
...options.strictBindings !== void 0 ? { strictBindings: options.strictBindings } : {},
|
|
226
|
-
...options.contextExtensions !== void 0 ? { contextExtensions: options.contextExtensions } : {}
|
|
227
|
-
};
|
|
228
|
-
return createMemoryCircuitStore(traitDefs, config, options.observer);
|
|
229
|
-
}, [traitIndex, options.guardMode, options.strictBindings, options.contextExtensions, options.observer]);
|
|
230
|
-
const localTransportOptions = {
|
|
231
|
-
traitIndex,
|
|
232
|
-
store,
|
|
233
|
-
...options.callService !== void 0 ? { callService: options.callService } : {},
|
|
234
|
-
...options.user !== void 0 ? { user: options.user } : {},
|
|
235
|
-
...options.guardMode !== void 0 ? { guardMode: options.guardMode } : {},
|
|
236
|
-
...options.strictBindings !== void 0 ? { strictBindings: options.strictBindings } : {},
|
|
237
|
-
...options.contextExtensions !== void 0 ? { contextExtensions: options.contextExtensions } : {},
|
|
238
|
-
...options.debug !== void 0 ? { debug: options.debug } : {},
|
|
239
|
-
...options.logContext !== void 0 ? { logContext: options.logContext } : {}
|
|
240
|
-
};
|
|
241
|
-
const offlineTransport = useMemo(
|
|
242
|
-
() => options.transport !== void 0 || options.persistence === void 0 ? void 0 : createLocalStoreTransport({ ...localTransportOptions, persistence: options.persistence }),
|
|
243
|
-
[
|
|
244
|
-
options.transport,
|
|
245
|
-
options.persistence,
|
|
246
|
-
traitIndex,
|
|
247
|
-
store,
|
|
248
|
-
options.callService,
|
|
249
|
-
options.user,
|
|
250
|
-
options.guardMode,
|
|
251
|
-
options.strictBindings,
|
|
252
|
-
options.contextExtensions,
|
|
253
|
-
options.debug,
|
|
254
|
-
options.logContext
|
|
255
|
-
]
|
|
256
|
-
);
|
|
257
|
-
const routedTransport = useMemo(() => {
|
|
258
|
-
if (options.browserStore === void 0) return void 0;
|
|
259
|
-
const local = createLocalStoreTransport({ ...localTransportOptions, traitIndex: fullTraitIndex, persistence: options.browserStore, clientRelays: true });
|
|
260
|
-
const remote = options.transport ?? offlineTransport;
|
|
261
|
-
return createResidenceTransport({ local, ...remote !== void 0 ? { remote } : {}, traitIndex: fullTraitIndex });
|
|
262
|
-
}, [
|
|
263
|
-
options.browserStore,
|
|
264
|
-
options.transport,
|
|
265
|
-
offlineTransport,
|
|
266
|
-
fullTraitIndex,
|
|
267
|
-
store,
|
|
268
|
-
options.callService,
|
|
269
|
-
options.user,
|
|
270
|
-
options.guardMode,
|
|
271
|
-
options.strictBindings,
|
|
272
|
-
options.contextExtensions,
|
|
273
|
-
options.debug,
|
|
274
|
-
options.logContext
|
|
275
|
-
]);
|
|
276
|
-
const baseTransport = routedTransport ?? options.transport ?? offlineTransport;
|
|
277
|
-
const effectiveTransport = useMemo(
|
|
278
|
-
() => baseTransport === void 0 ? void 0 : recordingTransport(baseTransport),
|
|
279
|
-
[baseTransport]
|
|
280
|
-
);
|
|
281
|
-
const orbitalName = options.orbitals[0]?.name ?? "";
|
|
282
|
-
const topology = useMemo(() => {
|
|
283
|
-
let resolve = () => void 0;
|
|
284
|
-
const promise = new Promise((r) => {
|
|
285
|
-
resolve = r;
|
|
286
|
-
});
|
|
287
|
-
return { promise, resolve };
|
|
288
|
-
}, [effectiveTransport]);
|
|
289
|
-
const awaitTopology = options.awaitTopology === true;
|
|
290
|
-
const confirmedCarries = options.carriesCircuitState ?? false;
|
|
291
|
-
useEffect(() => {
|
|
292
|
-
if (!awaitTopology) topology.resolve({ carriesCircuitState: confirmedCarries });
|
|
293
|
-
}, [awaitTopology, confirmedCarries, topology]);
|
|
294
|
-
const rawKernel = useMemo(() => createClientKernel({
|
|
295
|
-
orbitalName,
|
|
296
|
-
traitIndex,
|
|
297
|
-
fullTraitIndex,
|
|
298
|
-
store,
|
|
299
|
-
...options.persistence !== void 0 ? { persistence: options.persistence } : {},
|
|
300
|
-
...options.user !== void 0 ? { user: options.user } : {},
|
|
301
|
-
locale,
|
|
302
|
-
...i18n !== void 0 ? { messages: { [i18n.locale]: i18n.messages } } : {},
|
|
303
|
-
...options.guardMode !== void 0 ? { guardMode: options.guardMode } : {},
|
|
304
|
-
...options.strictBindings !== void 0 ? { strictBindings: options.strictBindings } : {},
|
|
305
|
-
...options.contextExtensions !== void 0 ? { contextExtensions: options.contextExtensions } : {},
|
|
306
|
-
...options.debug !== void 0 ? { debug: options.debug } : {},
|
|
307
|
-
...options.logContext !== void 0 ? { logContext: options.logContext } : {},
|
|
308
|
-
carriesCircuitState: false,
|
|
309
|
-
topology: topology.promise,
|
|
310
|
-
...effectiveTransport !== void 0 ? { transport: effectiveTransport } : {}
|
|
311
|
-
}), [
|
|
312
|
-
orbitalName,
|
|
313
|
-
traitIndex,
|
|
314
|
-
fullTraitIndex,
|
|
315
|
-
store,
|
|
316
|
-
options.persistence,
|
|
317
|
-
options.user,
|
|
318
|
-
locale,
|
|
319
|
-
i18n,
|
|
320
|
-
options.guardMode,
|
|
321
|
-
options.strictBindings,
|
|
322
|
-
options.contextExtensions,
|
|
323
|
-
options.debug,
|
|
324
|
-
options.logContext,
|
|
325
|
-
topology,
|
|
326
|
-
effectiveTransport
|
|
327
|
-
]);
|
|
328
|
-
const kernel = useMemo(() => {
|
|
329
|
-
const stamp = (request) => ({
|
|
330
|
-
...request,
|
|
331
|
-
clientId: request.clientId ?? getTabClientId(),
|
|
332
|
-
locale: request.locale ?? locale
|
|
333
|
-
});
|
|
334
|
-
return {
|
|
335
|
-
store: rawKernel.store,
|
|
336
|
-
dispatch: (request, hooks) => rawKernel.dispatch(stamp(request), hooks),
|
|
337
|
-
dispatchMount: (seeds, hooks, base = {}) => rawKernel.dispatchMount(seeds, hooks, stamp(base)),
|
|
338
|
-
dispatchProgress: (request) => rawKernel.dispatchProgress(stamp(request)),
|
|
339
|
-
foldHostDispatch: (request, response) => rawKernel.foldHostDispatch(request, response)
|
|
340
|
-
};
|
|
341
|
-
}, [rawKernel, locale]);
|
|
342
|
-
return { kernel, store, traitIndex, appTraitNames, transport: effectiveTransport };
|
|
343
|
-
}
|
|
344
|
-
var log2 = createLogger("almadar:ui:circuit:bus-ingress");
|
|
345
|
-
function useBusIngress(traitBindings, traitIndex, settle, eventBus, appTraitNames) {
|
|
346
|
-
useEffect(() => {
|
|
347
|
-
const unsubscribes = [];
|
|
348
|
-
const subscribedQualified = /* @__PURE__ */ new Set();
|
|
349
|
-
const subscribedBare = /* @__PURE__ */ new Set();
|
|
350
|
-
const deliveredHere = (source) => source?.dispatched === true && source.trait !== void 0 && (traitIndex.byName.has(source.trait) || appTraitNames.has(source.trait));
|
|
351
|
-
const claims = /* @__PURE__ */ new WeakMap();
|
|
352
|
-
const claim = (source, traitName, eventKey) => {
|
|
353
|
-
if (source === void 0) return true;
|
|
354
|
-
const key = `${traitName}\0${eventKey}`;
|
|
355
|
-
const seen = claims.get(source);
|
|
356
|
-
if (seen === void 0) {
|
|
357
|
-
claims.set(source, /* @__PURE__ */ new Set([key]));
|
|
358
|
-
return true;
|
|
359
|
-
}
|
|
360
|
-
if (seen.has(key)) return false;
|
|
361
|
-
seen.add(key);
|
|
362
|
-
return true;
|
|
363
|
-
};
|
|
364
|
-
const dispatch = (traitName, eventKey, payload, source) => {
|
|
365
|
-
if (!claim(source, traitName, eventKey)) return;
|
|
366
|
-
const tick = source?.tick;
|
|
367
|
-
const pendingKey = source?.pendingKey;
|
|
368
|
-
if (pendingKey !== void 0) beginPending(pendingKey);
|
|
369
|
-
void settle(traitName, eventKey, payload, tick).catch((err) => {
|
|
370
|
-
log2.error("dispatch:failed", { trait: traitName, event: eventKey, error: String(err) });
|
|
371
|
-
}).finally(() => {
|
|
372
|
-
if (pendingKey !== void 0) endPending(pendingKey);
|
|
373
|
-
});
|
|
374
|
-
};
|
|
375
|
-
for (const binding of traitBindings) {
|
|
376
|
-
const traitName = binding.trait.name;
|
|
377
|
-
const orbitalName = traitIndex.byName.get(traitName)?.orbitalName;
|
|
378
|
-
if (orbitalName === void 0) continue;
|
|
379
|
-
for (const transition of binding.trait.transitions) {
|
|
380
|
-
const eventKey = transition.event;
|
|
381
|
-
if (LIFECYCLE_EVENTS.includes(eventKey)) continue;
|
|
382
|
-
const qualifiedKey = `UI:${orbitalName}.${traitName}.${eventKey}`;
|
|
383
|
-
if (!subscribedQualified.has(qualifiedKey)) {
|
|
384
|
-
subscribedQualified.add(qualifiedKey);
|
|
385
|
-
const unsub = eventBus.on(qualifiedKey, (event) => {
|
|
386
|
-
if (deliveredHere(event.source)) return;
|
|
387
|
-
log2.debug("qualified:fire", { key: qualifiedKey });
|
|
388
|
-
dispatch(traitName, eventKey, event.payload, event.source);
|
|
389
|
-
});
|
|
390
|
-
unsubscribes.push(unsub);
|
|
391
|
-
}
|
|
392
|
-
const bareKey = `UI:${eventKey}`;
|
|
393
|
-
if (!subscribedBare.has(bareKey)) {
|
|
394
|
-
subscribedBare.add(bareKey);
|
|
395
|
-
const unsub = eventBus.on(bareKey, (event) => {
|
|
396
|
-
if (deliveredHere(event.source)) return;
|
|
397
|
-
const routed = event.source?.dispatched === true ? new Set(collectListenerTargets(traitIndex, event.source, eventKey, event.payload).map((t) => t.listenerTrait)) : void 0;
|
|
398
|
-
for (const [name, entry] of traitIndex.byName) {
|
|
399
|
-
if (!entry.traitDef.transitions.some((t) => t.event === eventKey)) continue;
|
|
400
|
-
if (routed?.has(name) === true) continue;
|
|
401
|
-
log2.debug("bare:fire", { key: bareKey, trait: name });
|
|
402
|
-
dispatch(name, eventKey, event.payload, event.source);
|
|
403
|
-
}
|
|
404
|
-
});
|
|
405
|
-
unsubscribes.push(unsub);
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
const listenRoutesByKey = /* @__PURE__ */ new Map();
|
|
410
|
-
for (const binding of traitBindings) {
|
|
411
|
-
const listenerName = binding.trait.name;
|
|
412
|
-
const ownOrbital = traitIndex.byName.get(listenerName)?.orbitalName;
|
|
413
|
-
if (ownOrbital === void 0) continue;
|
|
414
|
-
for (const listen of binding.trait.listens ?? []) {
|
|
415
|
-
const src = listen.source;
|
|
416
|
-
if (src === void 0 || src.kind === "any") continue;
|
|
417
|
-
const sourceOrbital = (src.kind === "orbital" ? src.orbital : void 0) ?? ownOrbital;
|
|
418
|
-
const sourceKey = `UI:${sourceOrbital}.${src.trait}.${listen.event}`;
|
|
419
|
-
const route = {
|
|
420
|
-
listenerTrait: listenerName,
|
|
421
|
-
triggers: listen.triggers,
|
|
422
|
-
payloadMapping: listen.payloadMapping
|
|
423
|
-
};
|
|
424
|
-
const routes = listenRoutesByKey.get(sourceKey);
|
|
425
|
-
if (routes) routes.push(route);
|
|
426
|
-
else listenRoutesByKey.set(sourceKey, [route]);
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
for (const [sourceKey, routes] of listenRoutesByKey) {
|
|
430
|
-
const unsub = eventBus.on(sourceKey, (event) => {
|
|
431
|
-
if (deliveredHere(event.source)) return;
|
|
432
|
-
for (const route of routes) {
|
|
433
|
-
log2.debug("listen-source:fire", { key: sourceKey, listener: route.listenerTrait, triggers: route.triggers });
|
|
434
|
-
dispatch(
|
|
435
|
-
route.listenerTrait,
|
|
436
|
-
route.triggers,
|
|
437
|
-
applyListenPayloadMapping(route.payloadMapping, event.payload, evaluateListenPayloadExpr),
|
|
438
|
-
event.source
|
|
439
|
-
);
|
|
440
|
-
}
|
|
441
|
-
});
|
|
442
|
-
unsubscribes.push(unsub);
|
|
443
|
-
}
|
|
444
|
-
const routedBareEvents = /* @__PURE__ */ new Set();
|
|
445
|
-
for (const binding of traitBindings) {
|
|
446
|
-
for (const listen of binding.trait.listens ?? []) {
|
|
447
|
-
if (listen.source !== void 0 && listen.source.kind !== "any") routedBareEvents.add(listen.event);
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
for (const eventKey of routedBareEvents) {
|
|
451
|
-
const unsub = eventBus.on(`UI:${eventKey}`, (event) => {
|
|
452
|
-
if (deliveredHere(event.source) || event.source?.trait === void 0) return;
|
|
453
|
-
for (const target of collectListenerTargets(traitIndex, event.source, eventKey, event.payload)) {
|
|
454
|
-
log2.debug("listen-bare:fire", { event: eventKey, source: event.source.trait, listener: target.listenerTrait, triggers: target.triggers });
|
|
455
|
-
dispatch(target.listenerTrait, target.triggers, target.payload, event.source);
|
|
456
|
-
}
|
|
457
|
-
});
|
|
458
|
-
unsubscribes.push(unsub);
|
|
459
|
-
}
|
|
460
|
-
return () => {
|
|
461
|
-
for (const unsub of unsubscribes) unsub();
|
|
462
|
-
};
|
|
463
|
-
}, [traitBindings, traitIndex, settle, eventBus, appTraitNames]);
|
|
464
|
-
}
|
|
465
|
-
var lambdaLog = createLogger("almadar:ui:fn-form-lambda");
|
|
466
|
-
function patternPropsSchema(patternType) {
|
|
467
|
-
return typeof patternType === "string" ? getPatternDefinition(patternType)?.propsSchema : void 0;
|
|
468
|
-
}
|
|
469
|
-
function isDeclaredSExpr(def) {
|
|
470
|
-
return def?.types?.includes("sexpr") ?? false;
|
|
471
|
-
}
|
|
472
|
-
function isOperatorCall(value) {
|
|
473
|
-
const first = value[0];
|
|
474
|
-
if (typeof first !== "string") return false;
|
|
475
|
-
return isKnownStdOperator(first) || first.includes("/") || first === "lambda" || first === "let";
|
|
476
|
-
}
|
|
477
|
-
function isFnFormLambda(value) {
|
|
478
|
-
if (!Array.isArray(value)) return false;
|
|
479
|
-
const arr = value;
|
|
480
|
-
if (arr.length !== 3 || arr[0] !== "fn" || arr[2] === null || typeof arr[2] !== "object") {
|
|
481
|
-
return false;
|
|
482
|
-
}
|
|
483
|
-
const params = arr[1];
|
|
484
|
-
return typeof params === "string" || Array.isArray(params) && params.length > 0 && params.every((p) => typeof p === "string");
|
|
485
|
-
}
|
|
486
|
-
function fnFormParams(value) {
|
|
487
|
-
const p = value[1];
|
|
488
|
-
if (typeof p === "string") return [p];
|
|
489
|
-
if (Array.isArray(p)) return p.filter((x) => typeof x === "string");
|
|
490
|
-
return [];
|
|
491
|
-
}
|
|
492
|
-
function resolveLambdaBindings(body, params, item, index) {
|
|
493
|
-
const itemName = params[0];
|
|
494
|
-
const indexName = params[1];
|
|
495
|
-
const itemPrefix = itemName ? `@${itemName}.` : null;
|
|
496
|
-
const lookup = (path) => {
|
|
497
|
-
let cur = item;
|
|
498
|
-
for (const seg of path.split(".")) {
|
|
499
|
-
if (cur === null || cur === void 0) return void 0;
|
|
500
|
-
if (typeof cur !== "object" || Array.isArray(cur)) return void 0;
|
|
501
|
-
cur = cur[seg];
|
|
502
|
-
}
|
|
503
|
-
return cur;
|
|
504
|
-
};
|
|
505
|
-
const recur = (b) => resolveLambdaBindings(b, params, item, index);
|
|
506
|
-
if (typeof body === "string") {
|
|
507
|
-
if (indexName && body === `@${indexName}`) return index;
|
|
508
|
-
if (itemName && body === `@${itemName}`) return item;
|
|
509
|
-
if (itemPrefix && body.startsWith(itemPrefix)) {
|
|
510
|
-
const v = lookup(body.slice(itemPrefix.length));
|
|
511
|
-
return v === void 0 || v === null ? "" : v;
|
|
512
|
-
}
|
|
513
|
-
return body;
|
|
514
|
-
}
|
|
515
|
-
if (Array.isArray(body)) {
|
|
516
|
-
if (isFnFormLambda(body)) return body;
|
|
517
|
-
const arr = body;
|
|
518
|
-
const substituted = arr.map((b) => recur(b));
|
|
519
|
-
if (isOperatorCall(substituted) && isSExpr(substituted)) {
|
|
520
|
-
if (containsEntityBinding(substituted)) {
|
|
521
|
-
return substituted;
|
|
522
|
-
}
|
|
523
|
-
const evaluated = evaluate(substituted, createMinimalContext());
|
|
524
|
-
return isEventPayloadValue(evaluated) ? evaluated : String(evaluated);
|
|
525
|
-
}
|
|
526
|
-
return substituted;
|
|
527
|
-
}
|
|
528
|
-
if (body !== null && typeof body === "object" && !React.isValidElement(body) && !(body instanceof Date) && typeof body !== "function") {
|
|
529
|
-
const out = {};
|
|
530
|
-
for (const [k, v] of Object.entries(body)) {
|
|
531
|
-
out[k] = recur(v);
|
|
532
|
-
}
|
|
533
|
-
return out;
|
|
534
|
-
}
|
|
535
|
-
return body;
|
|
536
|
-
}
|
|
537
|
-
var LazySlotContentRenderer = React.lazy(
|
|
538
|
-
() => import('./UISlotRenderer-N5DFYE6K.js').then((m) => ({ default: m.SlotContentRenderer }))
|
|
539
|
-
);
|
|
540
|
-
function deferLambdaEntityExprs(value) {
|
|
541
|
-
if (typeof value === "string") {
|
|
542
|
-
if (containsEntityBinding(value)) {
|
|
543
|
-
const marker = { [RENDER_BINDING_MARKER]: true, expression: value };
|
|
544
|
-
return marker;
|
|
545
|
-
}
|
|
546
|
-
return value;
|
|
547
|
-
}
|
|
548
|
-
if (Array.isArray(value)) {
|
|
549
|
-
if (isFnFormLambda(value)) return value;
|
|
550
|
-
const arr = value;
|
|
551
|
-
if (isOperatorCall(arr) && isSExpr(arr) && containsEntityBinding(arr)) {
|
|
552
|
-
const marker = { [RENDER_BINDING_MARKER]: true, expression: arr };
|
|
553
|
-
return marker;
|
|
554
|
-
}
|
|
555
|
-
return arr.map((v) => deferLambdaEntityExprs(v));
|
|
556
|
-
}
|
|
557
|
-
if (value !== null && typeof value === "object" && !React.isValidElement(value) && !(value instanceof Date) && typeof value !== "function" && !isRenderBindingMarker(value)) {
|
|
558
|
-
const out = {};
|
|
559
|
-
for (const [k, v] of Object.entries(value)) {
|
|
560
|
-
out[k] = deferLambdaEntityExprs(v);
|
|
561
|
-
}
|
|
562
|
-
return out;
|
|
563
|
-
}
|
|
564
|
-
return value;
|
|
565
|
-
}
|
|
566
|
-
function makeLambdaFn(params, lambdaBody, callerKey) {
|
|
567
|
-
return (item, index) => {
|
|
568
|
-
const resolvedBody = deferLambdaEntityExprs(
|
|
569
|
-
resolveLambdaBindings(lambdaBody, params, item, index)
|
|
570
|
-
);
|
|
571
|
-
if (resolvedBody === null || typeof resolvedBody !== "object" || Array.isArray(resolvedBody) || typeof resolvedBody === "function" || React.isValidElement(resolvedBody) || resolvedBody instanceof Date) {
|
|
572
|
-
return null;
|
|
573
|
-
}
|
|
574
|
-
const record = resolvedBody;
|
|
575
|
-
if (typeof record.type !== "string") {
|
|
576
|
-
return null;
|
|
577
|
-
}
|
|
578
|
-
const rawChildProps = {};
|
|
579
|
-
for (const [k, v] of Object.entries(record)) {
|
|
580
|
-
if (k !== "type") rawChildProps[k] = v;
|
|
581
|
-
}
|
|
582
|
-
const childProps = convertObjectProps(rawChildProps, patternPropsSchema(record.type));
|
|
583
|
-
const childContent = {
|
|
584
|
-
id: `lambda-${callerKey}-${index}`,
|
|
585
|
-
pattern: record.type,
|
|
586
|
-
props: childProps,
|
|
587
|
-
priority: 0
|
|
588
|
-
};
|
|
589
|
-
return React.createElement(
|
|
590
|
-
React.Suspense,
|
|
591
|
-
{ fallback: null },
|
|
592
|
-
React.createElement(LazySlotContentRenderer, { content: childContent, onDismiss: () => void 0 })
|
|
593
|
-
);
|
|
594
|
-
};
|
|
595
|
-
}
|
|
596
|
-
function convertNode(node, callerKey, def) {
|
|
597
|
-
if (node === null || node === void 0) return node;
|
|
598
|
-
if (isRenderBindingMarker(node)) return node;
|
|
599
|
-
if (Array.isArray(node)) {
|
|
600
|
-
if (isFnFormLambda(node)) {
|
|
601
|
-
const arr2 = node;
|
|
602
|
-
return makeLambdaFn(fnFormParams(arr2), arr2[2], callerKey);
|
|
603
|
-
}
|
|
604
|
-
const arr = node;
|
|
605
|
-
let anyChanged = false;
|
|
606
|
-
const mapped = arr.map((item, i) => {
|
|
607
|
-
const next = convertNode(item, `${callerKey}[${i}]`, def?.items);
|
|
608
|
-
if (next !== item) anyChanged = true;
|
|
609
|
-
return next;
|
|
610
|
-
});
|
|
611
|
-
return anyChanged ? mapped : node;
|
|
612
|
-
}
|
|
613
|
-
if (typeof node === "object" && !React.isValidElement(node) && !(node instanceof Date)) {
|
|
614
|
-
return convertObjectProps(node, def?.properties);
|
|
615
|
-
}
|
|
616
|
-
return node;
|
|
617
|
-
}
|
|
618
|
-
function convertObjectProps(props, inherited) {
|
|
619
|
-
let convertedAny = false;
|
|
620
|
-
const out = {};
|
|
621
|
-
const schema = patternPropsSchema(props.type) ?? inherited;
|
|
622
|
-
for (const [key, value] of Object.entries(props)) {
|
|
623
|
-
const def = schema?.[key];
|
|
624
|
-
if (isDeclaredSExpr(def)) {
|
|
625
|
-
out[key] = value;
|
|
626
|
-
continue;
|
|
627
|
-
}
|
|
628
|
-
if (isFnFormLambda(value)) {
|
|
629
|
-
convertedAny = true;
|
|
630
|
-
const arr = value;
|
|
631
|
-
const compiled = makeLambdaFn(fnFormParams(arr), arr[2], key);
|
|
632
|
-
out[key] = compiled;
|
|
633
|
-
if (key === "renderItem") out.children = compiled;
|
|
634
|
-
lambdaLog.debug(`convert key=${key}${key === "renderItem" ? " (+children)" : ""}`);
|
|
635
|
-
continue;
|
|
636
|
-
}
|
|
637
|
-
const next = convertNode(value, key, def);
|
|
638
|
-
if (next !== value) convertedAny = true;
|
|
639
|
-
out[key] = next;
|
|
640
|
-
}
|
|
641
|
-
return convertedAny ? out : props;
|
|
642
|
-
}
|
|
643
|
-
function convertFnFormLambdasInProps(props, patternType) {
|
|
644
|
-
if (props === null || typeof props !== "object") return props;
|
|
645
|
-
return convertObjectProps(props, patternType !== void 0 ? patternPropsSchema(patternType) : void 0);
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
// hooks/circuit/useSlotFlush.ts
|
|
649
|
-
var flushLog = createLogger("almadar:ui:circuit:slot-flush");
|
|
650
|
-
function unwrapPattern(record) {
|
|
651
|
-
if (typeof record === "string") return { bareTraitRef: record };
|
|
652
|
-
if (record === null) return {};
|
|
653
|
-
const { type, children, ...inlineProps } = record;
|
|
654
|
-
const props = { ...inlineProps, ...children !== void 0 ? { children } : {} };
|
|
655
|
-
return { patternType: type, props };
|
|
656
|
-
}
|
|
657
|
-
function useSlotFlush(uiSlots, embeddedTraits) {
|
|
658
|
-
const uiSlotsRef = useRef(uiSlots);
|
|
659
|
-
uiSlotsRef.current = uiSlots;
|
|
660
|
-
const embeddedTraitsRef = useRef(embeddedTraits);
|
|
661
|
-
embeddedTraitsRef.current = embeddedTraits;
|
|
662
|
-
const writtenRef = useRef(/* @__PURE__ */ new Map());
|
|
663
|
-
const keyOf = (slot, trait) => `${slot}\0${trait}`;
|
|
664
|
-
const flushSlot = useCallback((traitName, slot, patterns, source) => {
|
|
665
|
-
const slots = uiSlotsRef.current;
|
|
666
|
-
const embedded = embeddedTraitsRef.current;
|
|
667
|
-
if (patterns.length === 0) {
|
|
668
|
-
flushLog.debug("clear", { traitName, slot });
|
|
669
|
-
slots.clearBySource(slot, traitName);
|
|
670
|
-
writtenRef.current.set(keyOf(slot, traitName), { kind: "clear", slot, trait: traitName });
|
|
671
|
-
return;
|
|
672
|
-
}
|
|
673
|
-
const last = patterns[patterns.length - 1];
|
|
674
|
-
const { bareTraitRef, patternType, props: unwrapped } = unwrapPattern(last.pattern);
|
|
675
|
-
const rawProps = bareTraitRef !== void 0 ? bareTraitRef : { ...unwrapped ?? {}, ...last.props };
|
|
676
|
-
const props = convertFnFormLambdasInProps(rawProps, typeof patternType === "string" ? patternType : void 0);
|
|
677
|
-
const isEmbedded = (embedded?.has(traitName) ?? false) && !isNotificationSlot(slot);
|
|
678
|
-
if (isEmbedded) {
|
|
679
|
-
const content = {
|
|
680
|
-
pattern: patternType,
|
|
681
|
-
props,
|
|
682
|
-
slot,
|
|
683
|
-
priority: 0,
|
|
684
|
-
transitionEvent: source?.event,
|
|
685
|
-
fromState: source?.state,
|
|
686
|
-
entity: source?.entity
|
|
687
|
-
};
|
|
688
|
-
slots.updateTraitContent(traitName, content);
|
|
689
|
-
writtenRef.current.set(keyOf(slot, traitName), { kind: "trait", trait: traitName, content });
|
|
690
|
-
return;
|
|
691
|
-
}
|
|
692
|
-
const config = {
|
|
693
|
-
target: slot,
|
|
694
|
-
pattern: patternType,
|
|
695
|
-
props,
|
|
696
|
-
sourceTrait: traitName,
|
|
697
|
-
transitionEvent: source?.event,
|
|
698
|
-
fromState: source?.state,
|
|
699
|
-
entity: source?.entity
|
|
700
|
-
};
|
|
701
|
-
slots.render(config);
|
|
702
|
-
writtenRef.current.set(keyOf(slot, traitName), { kind: "render", config });
|
|
703
|
-
}, []);
|
|
704
|
-
const applyClientEffects = useCallback((clientEffects, clientEffectsByTrait, onNavigate, onNavigateBack, activeTraits) => {
|
|
705
|
-
const slots = uiSlotsRef.current;
|
|
706
|
-
const embedded = embeddedTraitsRef.current;
|
|
707
|
-
const tuples = clientEffectsByTrait ? [...clientEffectsByTrait] : clientEffects.map((effect) => ({ effect }));
|
|
708
|
-
for (const { effect, traitName, event, fromState } of tuples) {
|
|
709
|
-
if (traitName !== void 0 && activeTraits !== void 0 && !activeTraits.has(traitName)) continue;
|
|
710
|
-
const kind = effect[0];
|
|
711
|
-
if (kind === "render-ui") {
|
|
712
|
-
const [, slot, pattern, rawProps] = effect;
|
|
713
|
-
const sourceTrait = traitName ?? "kernel";
|
|
714
|
-
const { bareTraitRef, patternType, props: unwrapped } = unwrapPattern(pattern);
|
|
715
|
-
const isEmbedded = (embedded?.has(sourceTrait) ?? false) && !isNotificationSlot(slot);
|
|
716
|
-
const propsValue = bareTraitRef !== void 0 ? bareTraitRef : { ...unwrapped ?? {}, ...rawProps };
|
|
717
|
-
const props = convertFnFormLambdasInProps(propsValue, typeof patternType === "string" ? patternType : void 0);
|
|
718
|
-
if (pattern === null) {
|
|
719
|
-
slots.clearBySource(slot, sourceTrait);
|
|
720
|
-
writtenRef.current.set(keyOf(slot, sourceTrait), { kind: "clear", slot, trait: sourceTrait });
|
|
721
|
-
} else if (isEmbedded) {
|
|
722
|
-
const content = { pattern: patternType, props, slot, priority: 0, transitionEvent: event, fromState };
|
|
723
|
-
slots.updateTraitContent(sourceTrait, content);
|
|
724
|
-
writtenRef.current.set(keyOf(slot, sourceTrait), { kind: "trait", trait: sourceTrait, content });
|
|
725
|
-
} else {
|
|
726
|
-
const config = { target: slot, pattern: patternType, props, sourceTrait, transitionEvent: event, fromState };
|
|
727
|
-
slots.render(config);
|
|
728
|
-
writtenRef.current.set(keyOf(slot, sourceTrait), { kind: "render", config });
|
|
729
|
-
}
|
|
730
|
-
} else if (kind === "navigate") {
|
|
731
|
-
const [, route, params, options] = effect;
|
|
732
|
-
onNavigate?.(route, params, options?.crumb);
|
|
733
|
-
} else if (kind === "navigate-back") {
|
|
734
|
-
onNavigateBack?.();
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
}, []);
|
|
738
|
-
const checkpoint = useCallback(() => new Map(writtenRef.current), []);
|
|
739
|
-
const rollback = useCallback((cp) => {
|
|
740
|
-
const slots = uiSlotsRef.current;
|
|
741
|
-
for (const [key, now] of writtenRef.current) {
|
|
742
|
-
const then = cp.get(key);
|
|
743
|
-
if (then === now) continue;
|
|
744
|
-
if (then === void 0 || then.kind === "clear") {
|
|
745
|
-
if (now.kind === "render") slots.clearBySource(now.config.target, now.config.sourceTrait ?? "kernel");
|
|
746
|
-
else if (now.kind === "trait") {
|
|
747
|
-
flushLog.warn("rollback:embedded-frame-kept", { trait: now.trait });
|
|
748
|
-
}
|
|
749
|
-
} else if (then.kind === "render") {
|
|
750
|
-
slots.render(then.config);
|
|
751
|
-
} else {
|
|
752
|
-
slots.updateTraitContent(then.trait, then.content);
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
writtenRef.current = new Map(cp);
|
|
756
|
-
}, []);
|
|
757
|
-
return useMemo(() => ({ flushSlot, applyClientEffects, checkpoint, rollback }), [flushSlot, applyClientEffects, checkpoint, rollback]);
|
|
758
|
-
}
|
|
759
|
-
var log3 = createLogger("almadar:ui:circuit:effect-runner");
|
|
760
|
-
async function runCircuitEffects(params) {
|
|
761
|
-
const { store, eventBus, traitName, frameKey, effects, payload, callsitePayload, config, user, tickName } = params;
|
|
762
|
-
if (effects.length === 0) return { emitted: [], didWrite: false };
|
|
763
|
-
let frame = store.frames.get(frameKey);
|
|
764
|
-
if (frame === void 0) {
|
|
765
|
-
frame = {};
|
|
766
|
-
store.frames.set(frameKey, frame);
|
|
767
|
-
}
|
|
768
|
-
const emitted = [];
|
|
769
|
-
let didWrite = false;
|
|
770
|
-
const handlers = createClientEffectHandlers({
|
|
771
|
-
eventBus: {
|
|
772
|
-
emit: (type, emitPayload, source) => {
|
|
773
|
-
const event = type.startsWith("UI:") ? type.slice(3) : type;
|
|
774
|
-
emitted.push(event);
|
|
775
|
-
eventBus.emit(type, emitPayload, tickName !== void 0 ? { ...source, tick: tickName } : source);
|
|
776
|
-
}
|
|
777
|
-
},
|
|
778
|
-
slotSetter: {
|
|
779
|
-
addPattern: (slot, pattern, props, priority) => params.onPattern(slot, pattern, props, priority),
|
|
780
|
-
clearSlot: (slot) => params.onPattern(slot, null)
|
|
781
|
-
},
|
|
782
|
-
navigate: params.navigate,
|
|
783
|
-
navigateBack: params.navigateBack,
|
|
784
|
-
liveEntity: frame,
|
|
785
|
-
// Ticks and callsite re-runs never carry a persistence adapter — the
|
|
786
|
-
// same bridge-mode placeholder the composition's client env delegates
|
|
787
|
-
// to a leg; here there is no leg to delegate to, so the write is a
|
|
788
|
-
// documented no-op (persist/call-service were never reachable from a
|
|
789
|
-
// tick's SYNC_TICK_OPERATORS filter or a lifecycle re-run's effects).
|
|
790
|
-
persistDelegated: true,
|
|
791
|
-
callServiceDelegated: true,
|
|
792
|
-
orbitalName: params.orbitalName
|
|
793
|
-
});
|
|
794
|
-
const baseSet = handlers.set;
|
|
795
|
-
const tracked = {
|
|
796
|
-
...handlers,
|
|
797
|
-
set: async (targetId, field, value) => {
|
|
798
|
-
if (baseSet) await baseSet(targetId, field, value);
|
|
799
|
-
didWrite = true;
|
|
800
|
-
}
|
|
801
|
-
};
|
|
802
|
-
const bindingCtx = {
|
|
803
|
-
entity: frame,
|
|
804
|
-
payload: payload ?? {},
|
|
805
|
-
state: params.state,
|
|
806
|
-
...user !== void 0 ? { user } : {},
|
|
807
|
-
...config !== void 0 ? { config } : {},
|
|
808
|
-
...callsitePayload !== void 0 ? { callsitePayload } : {}
|
|
809
|
-
};
|
|
810
|
-
const effectContext = {
|
|
811
|
-
traitName,
|
|
812
|
-
...params.orbitalName !== void 0 ? { orbitalName: params.orbitalName } : {},
|
|
813
|
-
...params.traitId !== void 0 ? { traitId: params.traitId } : {},
|
|
814
|
-
...params.orbitalId !== void 0 ? { orbitalId: params.orbitalId } : {},
|
|
815
|
-
...params.emits !== void 0 ? { emits: params.emits } : {},
|
|
816
|
-
state: params.state,
|
|
817
|
-
transition: params.transitionLabel
|
|
818
|
-
};
|
|
819
|
-
const executor = new EffectExecutor({ handlers: tracked, bindings: bindingCtx, context: effectContext, deferRenderBindings: true });
|
|
820
|
-
try {
|
|
821
|
-
await executor.executeAll(effects);
|
|
822
|
-
} catch (error) {
|
|
823
|
-
log3.error("effects:error", {
|
|
824
|
-
traitName,
|
|
825
|
-
transition: params.transitionLabel,
|
|
826
|
-
error: error instanceof Error ? error.message : String(error)
|
|
827
|
-
});
|
|
828
|
-
}
|
|
829
|
-
return { emitted, didWrite };
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
// hooks/circuit/useCallsiteCapture.ts
|
|
833
|
-
var log4 = createLogger("almadar:ui:circuit:callsite-capture");
|
|
834
|
-
function useCallsiteCapture(traitBindings, store, traitIndex, slotFlush, options) {
|
|
835
|
-
return useCallback(async function reRender(traitName, callsitePayload, entityByTrait, visited = /* @__PURE__ */ new Set(), serverComposed = /* @__PURE__ */ new Set(), keepComposed = false, composedBy) {
|
|
836
|
-
const children = options.callsiteCaptureChildrenByTrait?.get(traitName);
|
|
837
|
-
if (!children || children.size === 0) return;
|
|
838
|
-
const bindingMap = new Map(traitBindings.map((b) => [b.trait.name, b]));
|
|
839
|
-
for (const childName of children) {
|
|
840
|
-
if (composedBy !== void 0 && !composedBy.has(childName)) continue;
|
|
841
|
-
if (visited.has(childName)) continue;
|
|
842
|
-
visited.add(childName);
|
|
843
|
-
const childBinding = bindingMap.get(childName);
|
|
844
|
-
if (!childBinding) continue;
|
|
845
|
-
const composedWith = serverComposed.has(childName) || keepComposed ? store.callsitePayloads.get(childName) ?? callsitePayload : callsitePayload;
|
|
846
|
-
const lifecycleEvent = store.manager.repaintLifecycleEvent(childName);
|
|
847
|
-
const indexed = traitIndex.byName.get(childName);
|
|
848
|
-
const [entry] = lifecycleEvent === void 0 || indexed === void 0 ? [] : store.manager.sendEvent(lifecycleEvent, {}, void 0, entityByTrait, void 0, childName);
|
|
849
|
-
if (lifecycleEvent === void 0 || indexed === void 0 || !entry || !entry.result.executed) {
|
|
850
|
-
await reRender(childName, composedWith, entityByTrait, visited, serverComposed, true);
|
|
851
|
-
continue;
|
|
852
|
-
}
|
|
853
|
-
const frameKey = indexed.frameKey;
|
|
854
|
-
log4.debug("rerender", { referrer: traitName, child: childName, lifecycleEvent });
|
|
855
|
-
store.callsitePayloads.set(childName, composedWith);
|
|
856
|
-
const pendingSlots = /* @__PURE__ */ new Map();
|
|
857
|
-
await runCircuitEffects({
|
|
858
|
-
store,
|
|
859
|
-
eventBus: options.eventBus,
|
|
860
|
-
traitName: childName,
|
|
861
|
-
frameKey,
|
|
862
|
-
effects: entry.result.effects,
|
|
863
|
-
state: entry.result.previousState,
|
|
864
|
-
transitionLabel: `${entry.result.previousState}->${entry.result.newState}`,
|
|
865
|
-
payload: {},
|
|
866
|
-
callsitePayload: composedWith,
|
|
867
|
-
config: indexed.config,
|
|
868
|
-
user: options.user,
|
|
869
|
-
orbitalName: indexed.orbitalName,
|
|
870
|
-
orbitalId: indexed.orbitalId,
|
|
871
|
-
traitId: indexed.irTrait.id,
|
|
872
|
-
emits: indexed.irTrait.emits,
|
|
873
|
-
navigate: options.navigate,
|
|
874
|
-
navigateBack: options.navigateBack,
|
|
875
|
-
onPattern: (slot, pattern, props) => {
|
|
876
|
-
if (pattern === null) {
|
|
877
|
-
pendingSlots.set(slot, []);
|
|
878
|
-
return;
|
|
879
|
-
}
|
|
880
|
-
const existing = pendingSlots.get(slot) ?? [];
|
|
881
|
-
existing.push({ pattern, props });
|
|
882
|
-
pendingSlots.set(slot, existing);
|
|
883
|
-
}
|
|
884
|
-
});
|
|
885
|
-
store.notify();
|
|
886
|
-
for (const [slot, patterns] of pendingSlots) {
|
|
887
|
-
slotFlush.flushSlot(
|
|
888
|
-
childName,
|
|
889
|
-
slot,
|
|
890
|
-
patterns,
|
|
891
|
-
{ event: lifecycleEvent, state: entry.result.previousState, entity: childBinding.linkedEntity }
|
|
892
|
-
);
|
|
893
|
-
}
|
|
894
|
-
await reRender(childName, composedWith, entityByTrait, visited, serverComposed, keepComposed, traitsEmbeddedByEffects(indexed.irTrait, entry.result.effects));
|
|
895
|
-
}
|
|
896
|
-
}, [traitBindings, store, traitIndex, slotFlush, options.callsiteCaptureChildrenByTrait, options.eventBus, options.navigate, options.navigateBack, options.user]);
|
|
897
|
-
}
|
|
898
|
-
var tickLog = createLogger("almadar:ui:circuit:tick-effects");
|
|
899
|
-
var SHARED_ENTITY_WRITE_OPS = /* @__PURE__ */ new Set(["set"]);
|
|
900
|
-
var SHARED_ENTITY_RENDER_OPS = /* @__PURE__ */ new Set(["render-ui", "render"]);
|
|
901
|
-
function effectsCallOp(effects, ops) {
|
|
902
|
-
for (const effect of effects) {
|
|
903
|
-
let found = false;
|
|
904
|
-
walkSExpr(effect, (node) => {
|
|
905
|
-
if (!found && Array.isArray(node) && typeof node[0] === "string" && ops.has(node[0])) found = true;
|
|
906
|
-
});
|
|
907
|
-
if (found) return true;
|
|
908
|
-
}
|
|
909
|
-
return false;
|
|
910
|
-
}
|
|
911
|
-
function classifySharedTick(tick) {
|
|
912
|
-
const writes = effectsCallOp(tick.effects, SHARED_ENTITY_WRITE_OPS);
|
|
913
|
-
const renders = effectsCallOp(tick.effects, SHARED_ENTITY_RENDER_OPS);
|
|
914
|
-
if (writes && renders) return "both";
|
|
915
|
-
if (writes) return "writer";
|
|
916
|
-
if (renders) return "renderer";
|
|
917
|
-
return "neither";
|
|
918
|
-
}
|
|
919
|
-
function containsConfigForward(value) {
|
|
920
|
-
if (typeof value === "string") return value.startsWith("@config.");
|
|
921
|
-
if (Array.isArray(value)) return value.some(containsConfigForward);
|
|
922
|
-
if (value !== null && typeof value === "object") return Object.values(value).some(containsConfigForward);
|
|
923
|
-
return false;
|
|
924
|
-
}
|
|
925
|
-
function getBindingConfig(binding) {
|
|
926
|
-
return normalizeCallSiteConfigToValues(binding.config);
|
|
927
|
-
}
|
|
928
|
-
function evalFieldDefault(value) {
|
|
929
|
-
if (!Array.isArray(value) || !isSExpression(value)) return value;
|
|
930
|
-
return evaluate(value, createMinimalContext({}, {}, ""));
|
|
931
|
-
}
|
|
932
|
-
function createSharedEntityWriter(binding, tick, traitStatesRef, emit, traitConfigsByName, orbitalsByTrait, orbitalIdsByTrait) {
|
|
933
|
-
return (scratch) => {
|
|
934
|
-
const traitName = binding.trait.name;
|
|
935
|
-
const currentState = traitStatesRef.current.get(traitName)?.currentState ?? "";
|
|
936
|
-
if (tick.appliesTo.length > 0 && !tick.appliesTo.includes(currentState)) return [];
|
|
937
|
-
const scratchEntity = { ...scratch };
|
|
938
|
-
const writes = [];
|
|
939
|
-
const ctx = createMinimalContext(scratchEntity, {}, currentState);
|
|
940
|
-
const declaredDefaults = collectDeclaredConfigDefaults(binding.trait);
|
|
941
|
-
const resolvedByName = traitConfigsByName?.[traitName];
|
|
942
|
-
const callSiteRaw = getBindingConfig(binding);
|
|
943
|
-
const callSiteConfig = callSiteRaw ? Object.fromEntries(Object.entries(callSiteRaw).filter(([, v]) => !containsConfigForward(v))) : void 0;
|
|
944
|
-
if (declaredDefaults || resolvedByName || callSiteConfig) {
|
|
945
|
-
ctx.config = { ...declaredDefaults ?? {}, ...resolvedByName ?? {}, ...callSiteConfig ?? {} };
|
|
946
|
-
}
|
|
947
|
-
ctx.mutateEntity = (changes) => {
|
|
948
|
-
for (const [field, value] of Object.entries(changes)) {
|
|
949
|
-
const fieldValue = value;
|
|
950
|
-
scratchEntity[field] = fieldValue;
|
|
951
|
-
writes.push({ field, value: fieldValue });
|
|
952
|
-
}
|
|
953
|
-
};
|
|
954
|
-
ctx.emit = (event, payload) => {
|
|
955
|
-
emit(event, payload, {
|
|
956
|
-
orbital: orbitalsByTrait?.[traitName],
|
|
957
|
-
trait: traitName,
|
|
958
|
-
tick: tick.name,
|
|
959
|
-
orbitalId: orbitalIdsByTrait?.[traitName],
|
|
960
|
-
traitId: binding.trait.id
|
|
961
|
-
});
|
|
962
|
-
};
|
|
963
|
-
if (tick.guard !== void 0 && !evaluateGuard(tick.guard, ctx)) {
|
|
964
|
-
tickLog.debug("guard-blocked", { traitName, tick: tick.name, state: currentState });
|
|
965
|
-
return [];
|
|
966
|
-
}
|
|
967
|
-
executeEffects(tick.effects, ctx);
|
|
968
|
-
return writes;
|
|
969
|
-
};
|
|
970
|
-
}
|
|
971
|
-
function useClientTicks(traitBindings, store, traitIndex, options) {
|
|
972
|
-
useEffect(() => {
|
|
973
|
-
if (options.hostRunsProgram === true) return;
|
|
974
|
-
const scheduler = createTickScheduler();
|
|
975
|
-
const traitStatesRef = { current: store.manager.getAllStates() };
|
|
976
|
-
const refreshStates = () => {
|
|
977
|
-
traitStatesRef.current = store.manager.getAllStates();
|
|
978
|
-
};
|
|
979
|
-
const orbitalsByTrait = {};
|
|
980
|
-
const orbitalIdsByTrait = {};
|
|
981
|
-
for (const [name, entry] of traitIndex.byName) {
|
|
982
|
-
orbitalsByTrait[name] = entry.orbitalName;
|
|
983
|
-
if (entry.orbitalId !== void 0) orbitalIdsByTrait[name] = entry.orbitalId;
|
|
984
|
-
}
|
|
985
|
-
const emitFromWriter = (event, payload, source) => {
|
|
986
|
-
const prefixed = event.startsWith("UI:") ? event : `UI:${event}`;
|
|
987
|
-
options.eventBus.emit(prefixed, payload, source);
|
|
988
|
-
};
|
|
989
|
-
const frameAdapter = {
|
|
990
|
-
getSnapshot: (frameKey) => store.frames.get(frameKey) ?? {},
|
|
991
|
-
subscribe: (_frameKey, callback) => store.subscribe(callback),
|
|
992
|
-
commit: (frameKey, next) => {
|
|
993
|
-
store.frames.set(frameKey, next);
|
|
994
|
-
store.notify();
|
|
995
|
-
},
|
|
996
|
-
seed: (frameKey, initial) => {
|
|
997
|
-
if (!store.frames.has(frameKey)) store.frames.set(frameKey, initial);
|
|
998
|
-
}
|
|
999
|
-
};
|
|
1000
|
-
const writerTicksByFrame = /* @__PURE__ */ new Map();
|
|
1001
|
-
const pureWriterTickKeys = /* @__PURE__ */ new Set();
|
|
1002
|
-
const seededFrames = /* @__PURE__ */ new Set();
|
|
1003
|
-
for (const binding of traitBindings) {
|
|
1004
|
-
const entry = traitIndex.byName.get(binding.trait.name);
|
|
1005
|
-
if (!entry?.isSharedEntity) continue;
|
|
1006
|
-
if (!seededFrames.has(entry.frameKey)) {
|
|
1007
|
-
seededFrames.add(entry.frameKey);
|
|
1008
|
-
let defaults;
|
|
1009
|
-
for (const field of entry.entity.fields) {
|
|
1010
|
-
if (field.name !== void 0 && field.default !== void 0 && field.default !== null) {
|
|
1011
|
-
(defaults ?? (defaults = {}))[field.name] = evalFieldDefault(field.default);
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
if (defaults) frameAdapter.seed(entry.frameKey, defaults);
|
|
1015
|
-
}
|
|
1016
|
-
for (const tick of binding.trait.ticks ?? []) {
|
|
1017
|
-
if (classifySharedTick(tick) !== "writer") continue;
|
|
1018
|
-
pureWriterTickKeys.add(`${binding.trait.name}::${tick.name}`);
|
|
1019
|
-
const list = writerTicksByFrame.get(entry.frameKey) ?? [];
|
|
1020
|
-
list.push({ binding, tick });
|
|
1021
|
-
writerTicksByFrame.set(entry.frameKey, list);
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
for (const [frameKey, entries] of writerTicksByFrame) {
|
|
1025
|
-
const byInterval = /* @__PURE__ */ new Map();
|
|
1026
|
-
for (const item of entries) {
|
|
1027
|
-
const key = String(item.tick.interval);
|
|
1028
|
-
const list = byInterval.get(key) ?? [];
|
|
1029
|
-
list.push(item);
|
|
1030
|
-
byInterval.set(key, list);
|
|
1031
|
-
}
|
|
1032
|
-
for (const group of byInterval.values()) {
|
|
1033
|
-
const interval = group[0].tick.interval;
|
|
1034
|
-
const onDue = () => perfTimeAsync(`tick:shared:${frameKey}@${String(interval)}`, () => {
|
|
1035
|
-
const writers = group.map(
|
|
1036
|
-
({ binding, tick }) => createSharedEntityWriter(binding, tick, traitStatesRef, emitFromWriter, options.traitConfigsByName, orbitalsByTrait, orbitalIdsByTrait)
|
|
1037
|
-
);
|
|
1038
|
-
runTickFrame(frameKey, writers, frameAdapter);
|
|
1039
|
-
});
|
|
1040
|
-
if (interval === "frame") scheduler.add(0, onDue);
|
|
1041
|
-
else if (typeof interval === "number") scheduler.add(interval, onDue);
|
|
1042
|
-
else if (isValidCronExpression(interval)) scheduler.addCron(interval, onDue);
|
|
1043
|
-
else scheduler.add(parseDurationString(interval), onDue);
|
|
1044
|
-
}
|
|
1045
|
-
}
|
|
1046
|
-
const runNonWriterTick = (binding, tick) => {
|
|
1047
|
-
refreshStates();
|
|
1048
|
-
const traitName = binding.trait.name;
|
|
1049
|
-
const currentState = traitStatesRef.current.get(traitName)?.currentState ?? "";
|
|
1050
|
-
if (tick.appliesTo.length > 0 && !tick.appliesTo.includes(currentState)) return;
|
|
1051
|
-
const entry = traitIndex.byName.get(traitName);
|
|
1052
|
-
const frameKey = entry?.frameKey ?? traitName;
|
|
1053
|
-
if (tick.guard !== void 0) {
|
|
1054
|
-
const entity = store.frames.get(frameKey) ?? {};
|
|
1055
|
-
const guardCtx = {
|
|
1056
|
-
entity,
|
|
1057
|
-
payload: {},
|
|
1058
|
-
state: currentState,
|
|
1059
|
-
...options.user ? { user: options.user } : {},
|
|
1060
|
-
...entry?.config !== void 0 ? { config: entry.config } : {}
|
|
1061
|
-
};
|
|
1062
|
-
if (!evaluateGuard(tick.guard, createContextFromBindings(guardCtx))) {
|
|
1063
|
-
tickLog.debug("guard-blocked", { traitName, tick: tick.name, state: currentState });
|
|
1064
|
-
return;
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
|
-
const pendingSlots = /* @__PURE__ */ new Map();
|
|
1068
|
-
void runCircuitEffects({
|
|
1069
|
-
store,
|
|
1070
|
-
eventBus: options.eventBus,
|
|
1071
|
-
traitName,
|
|
1072
|
-
frameKey,
|
|
1073
|
-
effects: tick.effects,
|
|
1074
|
-
state: currentState,
|
|
1075
|
-
transitionLabel: `${currentState}->${currentState}`,
|
|
1076
|
-
config: entry?.config,
|
|
1077
|
-
user: options.user,
|
|
1078
|
-
orbitalName: entry?.orbitalName,
|
|
1079
|
-
orbitalId: entry?.orbitalId,
|
|
1080
|
-
traitId: entry?.irTrait.id,
|
|
1081
|
-
emits: entry?.irTrait.emits,
|
|
1082
|
-
navigate: options.navigate,
|
|
1083
|
-
navigateBack: options.navigateBack,
|
|
1084
|
-
tickName: tick.name,
|
|
1085
|
-
onPattern: (slot, pattern, props) => {
|
|
1086
|
-
if (pattern === null) {
|
|
1087
|
-
pendingSlots.set(slot, []);
|
|
1088
|
-
return;
|
|
1089
|
-
}
|
|
1090
|
-
const existing = pendingSlots.get(slot) ?? [];
|
|
1091
|
-
existing.push({ pattern, props });
|
|
1092
|
-
pendingSlots.set(slot, existing);
|
|
1093
|
-
}
|
|
1094
|
-
}).then(() => {
|
|
1095
|
-
store.notify();
|
|
1096
|
-
for (const [slot, patterns] of pendingSlots) {
|
|
1097
|
-
options.slotFlush.flushSlot(
|
|
1098
|
-
traitName,
|
|
1099
|
-
slot,
|
|
1100
|
-
patterns,
|
|
1101
|
-
{ event: `tick:${tick.name}`, state: currentState, entity: binding.linkedEntity }
|
|
1102
|
-
);
|
|
1103
|
-
}
|
|
1104
|
-
});
|
|
1105
|
-
};
|
|
1106
|
-
for (const binding of traitBindings) {
|
|
1107
|
-
const entry = traitIndex.byName.get(binding.trait.name);
|
|
1108
|
-
for (const tick of binding.trait.ticks ?? []) {
|
|
1109
|
-
if (entry?.isSharedEntity && pureWriterTickKeys.has(`${binding.trait.name}::${tick.name}`)) continue;
|
|
1110
|
-
const key = `tick:${binding.trait.name}::${tick.name}`;
|
|
1111
|
-
const onDue = () => perfTimeAsync(key, () => runNonWriterTick(binding, tick));
|
|
1112
|
-
if (tick.interval === "frame") scheduler.add(0, onDue);
|
|
1113
|
-
else if (typeof tick.interval === "number") scheduler.add(tick.interval, onDue);
|
|
1114
|
-
else if (isValidCronExpression(tick.interval)) scheduler.addCron(tick.interval, onDue);
|
|
1115
|
-
else scheduler.add(parseDurationString(tick.interval), onDue);
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
|
-
return () => scheduler.stopAll();
|
|
1119
|
-
}, [traitBindings, store, traitIndex, options.eventBus, options.slotFlush, options.traitConfigsByName, options.navigate, options.navigateBack, options.user, options.hostRunsProgram]);
|
|
1120
|
-
}
|
|
1121
|
-
var EMPTY_ENTITY = {};
|
|
1122
|
-
function useEntityBindingSource(store, traitIndex) {
|
|
1123
|
-
useSyncExternalStore(store.subscribe, store.getVersion);
|
|
1124
|
-
return useMemo(() => {
|
|
1125
|
-
const snapshots = /* @__PURE__ */ new Map();
|
|
1126
|
-
return {
|
|
1127
|
-
getEntitySnapshot: (traitName) => {
|
|
1128
|
-
const entry = traitIndex.byName.get(traitName);
|
|
1129
|
-
const frameKey = entry?.frameKey ?? traitName;
|
|
1130
|
-
const frame = store.frames.get(frameKey);
|
|
1131
|
-
if (frame === void 0) return EMPTY_ENTITY;
|
|
1132
|
-
const previous = snapshots.get(frameKey);
|
|
1133
|
-
if (previous !== void 0 && sameFields(previous, frame)) return previous;
|
|
1134
|
-
const next = { ...frame };
|
|
1135
|
-
snapshots.set(frameKey, next);
|
|
1136
|
-
return next;
|
|
1137
|
-
},
|
|
1138
|
-
getConfig: (traitName) => traitIndex.byName.get(traitName)?.config,
|
|
1139
|
-
getState: (traitName) => store.manager.getState(traitName)?.currentState ?? "",
|
|
1140
|
-
subscribe: (_traitName, callback) => store.subscribe(callback)
|
|
1141
|
-
};
|
|
1142
|
-
}, [store, traitIndex]);
|
|
1143
|
-
}
|
|
1144
|
-
function sameFields(a, b) {
|
|
1145
|
-
const keys = Object.keys(b);
|
|
1146
|
-
if (Object.keys(a).length !== keys.length) return false;
|
|
1147
|
-
for (const key of keys) {
|
|
1148
|
-
if (!Object.is(a[key], b[key])) return false;
|
|
1149
|
-
}
|
|
1150
|
-
return true;
|
|
1151
|
-
}
|
|
1152
|
-
function useAwaitingSkeletonSource(store, traitIndex, embeddedTraits) {
|
|
1153
|
-
return useMemo(() => {
|
|
1154
|
-
const activeRenders = () => {
|
|
1155
|
-
const renders = [];
|
|
1156
|
-
for (const awaiting of store.awaiting.list()) {
|
|
1157
|
-
const transitions = traitIndex.byName.get(awaiting.trait)?.irTrait.stateMachine?.transitions ?? [];
|
|
1158
|
-
const taken = transitions.find((t) => t.event === awaiting.event && t.from === awaiting.from);
|
|
1159
|
-
if (taken?.awaitRender) renders.push(...taken.awaitRender);
|
|
1160
|
-
}
|
|
1161
|
-
return renders;
|
|
1162
|
-
};
|
|
1163
|
-
return {
|
|
1164
|
-
forSlot: (slot) => activeRenders().find((r) => r.slot === slot && !(embeddedTraits?.has(r.trait) ?? false))?.skeleton,
|
|
1165
|
-
forTrait: (traitName) => activeRenders().find((r) => r.trait === traitName)?.skeleton,
|
|
1166
|
-
subscribe: (callback) => store.awaiting.subscribeAll(callback),
|
|
1167
|
-
getVersion: () => store.awaiting.getVersion()
|
|
1168
|
-
};
|
|
1169
|
-
}, [store, traitIndex, embeddedTraits]);
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
|
-
// hooks/useTraitStateMachine.ts
|
|
1173
|
-
var MOUNT_ERROR_SOURCE = "$mount";
|
|
1174
|
-
var stateLog = createLogger("almadar:ui:state-transitions");
|
|
1175
|
-
function diffDroppedTraitNames(prev, next) {
|
|
1176
|
-
const dropped = [];
|
|
1177
|
-
for (const name of prev) {
|
|
1178
|
-
if (!next.has(name)) dropped.push(name);
|
|
1179
|
-
}
|
|
1180
|
-
return dropped;
|
|
1181
|
-
}
|
|
1182
|
-
function normalizeEventKey(eventKey) {
|
|
1183
|
-
return eventKey.startsWith("UI:") ? eventKey.slice(3) : eventKey;
|
|
1184
|
-
}
|
|
1185
|
-
function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
1186
|
-
const eventBus = useEventBus();
|
|
1187
|
-
const { user: contextViewer } = useUser();
|
|
1188
|
-
const { t } = useTranslate();
|
|
1189
|
-
const userContext = options.user ?? contextViewer ?? void 0;
|
|
1190
|
-
const observer = useMemo(() => createCircuitVerificationObserver(), []);
|
|
1191
|
-
const { kernel, store, traitIndex, appTraitNames, transport } = useCircuitKernel(traitBindings, {
|
|
1192
|
-
orbitals: options.orbitals,
|
|
1193
|
-
...options.traitConfigsByName !== void 0 ? { traitConfigsByName: options.traitConfigsByName } : {},
|
|
1194
|
-
...options.transport !== void 0 ? { transport: options.transport } : {},
|
|
1195
|
-
...options.carriesCircuitState !== void 0 ? { carriesCircuitState: options.carriesCircuitState } : {},
|
|
1196
|
-
...options.awaitTopology !== void 0 ? { awaitTopology: options.awaitTopology } : {},
|
|
1197
|
-
...options.persistence !== void 0 ? { persistence: options.persistence } : {},
|
|
1198
|
-
...options.browserStore !== void 0 ? { browserStore: options.browserStore } : {},
|
|
1199
|
-
...options.callService !== void 0 ? { callService: options.callService } : {},
|
|
1200
|
-
...userContext !== void 0 ? { user: userContext } : {},
|
|
1201
|
-
...options.debug !== void 0 ? { debug: options.debug } : {},
|
|
1202
|
-
...options.logContext !== void 0 ? { logContext: options.logContext } : {},
|
|
1203
|
-
observer
|
|
1204
|
-
});
|
|
1205
|
-
const slotFlush = useSlotFlush(uiSlots, options.embeddedTraits);
|
|
1206
|
-
const reRenderCallsiteCaptureChildren = useCallsiteCapture(traitBindings, store, traitIndex, slotFlush, {
|
|
1207
|
-
...options.callsiteCaptureChildrenByTrait !== void 0 ? { callsiteCaptureChildrenByTrait: options.callsiteCaptureChildrenByTrait } : {},
|
|
1208
|
-
eventBus,
|
|
1209
|
-
...options.navigate !== void 0 ? { navigate: options.navigate } : {},
|
|
1210
|
-
...options.navigateBack !== void 0 ? { navigateBack: options.navigateBack } : {},
|
|
1211
|
-
...userContext !== void 0 ? { user: userContext } : {}
|
|
1212
|
-
});
|
|
1213
|
-
useClientTicks(traitBindings, store, traitIndex, {
|
|
1214
|
-
eventBus,
|
|
1215
|
-
slotFlush,
|
|
1216
|
-
hostRunsProgram: transport?.hostsBrowserStore === true,
|
|
1217
|
-
...options.traitConfigsByName !== void 0 ? { traitConfigsByName: options.traitConfigsByName } : {},
|
|
1218
|
-
...options.navigate !== void 0 ? { navigate: options.navigate } : {},
|
|
1219
|
-
...options.navigateBack !== void 0 ? { navigateBack: options.navigateBack } : {},
|
|
1220
|
-
...userContext !== void 0 ? { user: userContext } : {}
|
|
1221
|
-
});
|
|
1222
|
-
const entityBindingSource = useEntityBindingSource(store, traitIndex);
|
|
1223
|
-
const awaitingSkeletonSource = useAwaitingSkeletonSource(store, traitIndex, options.embeddedTraits);
|
|
1224
|
-
const activeTraitNamesRef = useRef(/* @__PURE__ */ new Set());
|
|
1225
|
-
useEffect(() => {
|
|
1226
|
-
activeTraitNamesRef.current = new Set(traitIndex.byName.keys());
|
|
1227
|
-
}, [traitIndex]);
|
|
1228
|
-
useEffect(() => {
|
|
1229
|
-
const ids = [];
|
|
1230
|
-
for (const binding of traitBindings) {
|
|
1231
|
-
const trait = binding.trait;
|
|
1232
|
-
const state = store.manager.getState(trait.name);
|
|
1233
|
-
const info = {
|
|
1234
|
-
id: trait.name,
|
|
1235
|
-
name: trait.name,
|
|
1236
|
-
currentState: state?.currentState ?? trait.states[0]?.name ?? "unknown",
|
|
1237
|
-
states: trait.states.map((s) => s.name),
|
|
1238
|
-
transitions: trait.transitions.flatMap((t2) => {
|
|
1239
|
-
const froms = Array.isArray(t2.from) ? t2.from : [t2.from];
|
|
1240
|
-
return froms.map((f) => ({ from: f, to: t2.to, event: t2.event, guard: t2.guard ? String(t2.guard) : void 0 }));
|
|
1241
|
-
}),
|
|
1242
|
-
guards: trait.transitions.filter((t2) => t2.guard).map((t2) => ({ name: String(t2.guard) })),
|
|
1243
|
-
transitionCount: 0
|
|
1244
|
-
};
|
|
1245
|
-
registerTrait(info);
|
|
1246
|
-
ids.push(trait.name);
|
|
1247
|
-
}
|
|
1248
|
-
bindTraitStateGetter((traitName) => store.manager.getState(traitName)?.currentState);
|
|
1249
|
-
const snapshotData = (traitName) => {
|
|
1250
|
-
const entry = traitIndex.byName.get(traitName);
|
|
1251
|
-
const frame = entry !== void 0 ? store.frames.get(entry.frameKey) : void 0;
|
|
1252
|
-
const entityName = entry?.entity.name;
|
|
1253
|
-
return frame !== void 0 && entityName !== void 0 ? { [entityName]: [{ ...frame }] } : {};
|
|
1254
|
-
};
|
|
1255
|
-
const snapshotUnregs = traitBindings.map((binding) => registerTraitSnapshot(binding.trait.name, () => {
|
|
1256
|
-
const currentState = store.manager.getState(binding.trait.name)?.currentState ?? binding.trait.states[0]?.name ?? "unknown";
|
|
1257
|
-
return {
|
|
1258
|
-
traitName: binding.trait.name,
|
|
1259
|
-
currentState,
|
|
1260
|
-
states: binding.trait.states.map((s) => s.name),
|
|
1261
|
-
events: binding.trait.events.map((e) => e.key),
|
|
1262
|
-
data: snapshotData(binding.trait.name),
|
|
1263
|
-
cascadeReceived: []
|
|
1264
|
-
};
|
|
1265
|
-
}));
|
|
1266
|
-
return () => {
|
|
1267
|
-
for (const id of ids) unregisterTrait(id);
|
|
1268
|
-
for (const unreg of snapshotUnregs) unreg();
|
|
1269
|
-
};
|
|
1270
|
-
}, [traitBindings, store, traitIndex]);
|
|
1271
|
-
const afterSettle = useCallback(async (traitName, eventKey, payload, transitioned, repaints, composedBefore) => {
|
|
1272
|
-
if (transitioned) {
|
|
1273
|
-
const entityByTrait = {};
|
|
1274
|
-
for (const [name, entry] of traitIndex.byName) {
|
|
1275
|
-
const row = store.frames.get(entry.frameKey);
|
|
1276
|
-
if (row !== void 0) entityByTrait[name] = row;
|
|
1277
|
-
}
|
|
1278
|
-
const repainted = new Set(repaints.map((e) => e.traitName));
|
|
1279
|
-
const serverComposed = new Set([...store.callsitePayloads].filter(([child, composed]) => composedBefore.get(child) !== composed).map(([child]) => child));
|
|
1280
|
-
const irTrait = traitIndex.byName.get(traitName)?.irTrait;
|
|
1281
|
-
const composedBy = irTrait !== void 0 ? traitsEmbeddedByEvent(irTrait, eventKey) : void 0;
|
|
1282
|
-
await reRenderCallsiteCaptureChildren(traitName, payload ?? {}, entityByTrait, repainted, serverComposed, false, composedBy);
|
|
1283
|
-
}
|
|
1284
|
-
options.onEventProcessed?.(eventKey, payload);
|
|
1285
|
-
}, [traitIndex, store, reRenderCallsiteCaptureChildren, options.onEventProcessed]);
|
|
1286
|
-
const settleOutcome = useCallback(async (traitName, eventKey, payload, outcome, composedBefore) => {
|
|
1287
|
-
recordDispatchVerdict(traitIndex.byName.get(traitName)?.orbitalName ?? traitName, eventKey, { response: outcome.response });
|
|
1288
|
-
const settledEffects = outcome.localPainted && outcome.serverEffects !== void 0 ? outcome.serverEffects : outcome.response;
|
|
1289
|
-
slotFlush.applyClientEffects(
|
|
1290
|
-
settledEffects.clientEffects ?? [],
|
|
1291
|
-
settledEffects.clientEffectsByTrait,
|
|
1292
|
-
options.navigate,
|
|
1293
|
-
options.navigateBack,
|
|
1294
|
-
activeTraitNamesRef.current
|
|
1295
|
-
);
|
|
1296
|
-
for (const emitted of outcome.response.emittedEvents) {
|
|
1297
|
-
eventBus.emit(`UI:${emitted.event}`, emitted.payload, { ...emitted.source ?? {}, dispatched: true });
|
|
1298
|
-
}
|
|
1299
|
-
stateLog.debug("dispatch:settled", { traitName, eventKey, mode: outcome.mode, transitioned: outcome.response.transitioned });
|
|
1300
|
-
await afterSettle(traitName, eventKey, payload, outcome.response.transitioned, outcome.response.clientEffectsByTrait ?? [], composedBefore);
|
|
1301
|
-
}, [slotFlush, traitIndex, eventBus, options.navigate, options.navigateBack, afterSettle]);
|
|
1302
|
-
const dispatchAndSettle = useCallback(async (traitName, eventKey, payload, tick) => {
|
|
1303
|
-
const entityId = typeof payload?.entityId === "string" ? payload.entityId : void 0;
|
|
1304
|
-
const orbitalName = traitIndex.byName.get(traitName)?.orbitalName ?? traitName;
|
|
1305
|
-
let outcome;
|
|
1306
|
-
const composedBefore = new Map(store.callsitePayloads);
|
|
1307
|
-
let beforeLocalPaint;
|
|
1308
|
-
try {
|
|
1309
|
-
outcome = await kernel.dispatch({
|
|
1310
|
-
event: eventKey,
|
|
1311
|
-
...payload !== void 0 ? { payload } : {},
|
|
1312
|
-
...entityId !== void 0 ? { entityId } : {},
|
|
1313
|
-
...tick !== void 0 ? { tick } : {},
|
|
1314
|
-
targetTrait: traitName
|
|
1315
|
-
}, {
|
|
1316
|
-
onLocal: (local) => {
|
|
1317
|
-
beforeLocalPaint = slotFlush.checkpoint();
|
|
1318
|
-
slotFlush.applyClientEffects(
|
|
1319
|
-
local.clientEffects ?? [],
|
|
1320
|
-
local.clientEffectsByTrait,
|
|
1321
|
-
options.navigate,
|
|
1322
|
-
options.navigateBack,
|
|
1323
|
-
activeTraitNamesRef.current
|
|
1324
|
-
);
|
|
1325
|
-
}
|
|
1326
|
-
});
|
|
1327
|
-
} catch (err) {
|
|
1328
|
-
if (beforeLocalPaint !== void 0) slotFlush.rollback(beforeLocalPaint);
|
|
1329
|
-
recordDispatchVerdict(orbitalName, eventKey, { error: err instanceof Error ? err : String(err) });
|
|
1330
|
-
throw err;
|
|
1331
|
-
}
|
|
1332
|
-
if (outcome.localPainted && !outcome.response.success && beforeLocalPaint !== void 0) {
|
|
1333
|
-
slotFlush.rollback(beforeLocalPaint);
|
|
1334
|
-
}
|
|
1335
|
-
await settleOutcome(traitName, eventKey, payload, outcome, composedBefore);
|
|
1336
|
-
}, [kernel, slotFlush, traitIndex, store, options.navigate, options.navigateBack, settleOutcome]);
|
|
1337
|
-
const progressAndSettle = useCallback(async (traitName, eventKey, payload) => {
|
|
1338
|
-
const entityId = typeof payload?.entityId === "string" ? payload.entityId : void 0;
|
|
1339
|
-
const composedBefore = new Map(store.callsitePayloads);
|
|
1340
|
-
const outcome = await kernel.dispatchProgress({
|
|
1341
|
-
event: eventKey,
|
|
1342
|
-
...payload !== void 0 ? { payload } : {},
|
|
1343
|
-
...entityId !== void 0 ? { entityId } : {},
|
|
1344
|
-
targetTrait: traitName
|
|
1345
|
-
});
|
|
1346
|
-
await settleOutcome(traitName, eventKey, payload, outcome, composedBefore);
|
|
1347
|
-
}, [kernel, store, settleOutcome]);
|
|
1348
|
-
useEffect(() => {
|
|
1349
|
-
if (!transport?.subscribe) return;
|
|
1350
|
-
return transport.subscribe((emitted, target) => {
|
|
1351
|
-
const listeners = collectListenerTargets(traitIndex, emitted.source, emitted.event, emitted.payload);
|
|
1352
|
-
if (target !== "origin") {
|
|
1353
|
-
for (const t2 of listeners) void dispatchAndSettle(t2.listenerTrait, t2.triggers, t2.payload);
|
|
1354
|
-
return;
|
|
1355
|
-
}
|
|
1356
|
-
const caller = emitted.source?.trait;
|
|
1357
|
-
if (caller !== void 0 && traitIndex.byName.has(caller) && !listeners.some((t2) => t2.listenerTrait === caller && t2.triggers === emitted.event)) {
|
|
1358
|
-
void progressAndSettle(caller, emitted.event, emitted.payload);
|
|
1359
|
-
}
|
|
1360
|
-
for (const t2 of listeners) void progressAndSettle(t2.listenerTrait, t2.triggers, t2.payload);
|
|
1361
|
-
}, { clientId: getTabClientId() });
|
|
1362
|
-
}, [transport, traitIndex, progressAndSettle, dispatchAndSettle]);
|
|
1363
|
-
useEffect(() => {
|
|
1364
|
-
if (!transport?.subscribeHostDispatches) return;
|
|
1365
|
-
return transport.subscribeHostDispatches((_orbitalName, request, response) => {
|
|
1366
|
-
const traitName = request.targetTrait;
|
|
1367
|
-
if (traitName === void 0 || !traitIndex.byName.has(traitName)) return;
|
|
1368
|
-
const composedBefore = new Map(store.callsitePayloads);
|
|
1369
|
-
void kernel.foldHostDispatch(request, response).then((outcome) => settleOutcome(traitName, request.event, request.payload, outcome, composedBefore));
|
|
1370
|
-
}, { clientId: getTabClientId() });
|
|
1371
|
-
}, [transport, traitIndex, kernel, store, settleOutcome]);
|
|
1372
|
-
const mountAndSettle = useCallback(async (seeds, payload) => {
|
|
1373
|
-
const composedBefore = new Map(store.callsitePayloads);
|
|
1374
|
-
let beforeLocalPaint;
|
|
1375
|
-
let outcome;
|
|
1376
|
-
try {
|
|
1377
|
-
outcome = await kernel.dispatchMount(seeds, {
|
|
1378
|
-
onLocal: (_trait, local) => {
|
|
1379
|
-
if (beforeLocalPaint === void 0) beforeLocalPaint = slotFlush.checkpoint();
|
|
1380
|
-
slotFlush.applyClientEffects(local.clientEffects ?? [], local.clientEffectsByTrait, options.navigate, options.navigateBack, activeTraitNamesRef.current);
|
|
1381
|
-
}
|
|
1382
|
-
}, { payload });
|
|
1383
|
-
} catch (err) {
|
|
1384
|
-
if (beforeLocalPaint !== void 0) slotFlush.rollback(beforeLocalPaint);
|
|
1385
|
-
for (const seed of seeds) {
|
|
1386
|
-
recordDispatchVerdict(traitIndex.byName.get(seed.trait)?.orbitalName ?? seed.trait, seed.event, { error: err instanceof Error ? err : String(err) });
|
|
1387
|
-
}
|
|
1388
|
-
throw err;
|
|
1389
|
-
}
|
|
1390
|
-
if (!outcome.success && beforeLocalPaint !== void 0) slotFlush.rollback(beforeLocalPaint);
|
|
1391
|
-
slotFlush.applyClientEffects(outcome.serverEffects.clientEffects ?? [], outcome.serverEffects.clientEffectsByTrait, options.navigate, options.navigateBack, activeTraitNamesRef.current);
|
|
1392
|
-
const emitted = [...outcome.seeds.flatMap((seed) => seed.local.emittedEvents), ...outcome.emittedEvents];
|
|
1393
|
-
for (const e of emitted) {
|
|
1394
|
-
eventBus.emit(`UI:${e.event}`, e.payload, { ...e.source ?? {}, dispatched: true });
|
|
1395
|
-
}
|
|
1396
|
-
const serverRepaints = outcome.serverEffects.clientEffectsByTrait ?? [];
|
|
1397
|
-
for (const seed of outcome.seeds) {
|
|
1398
|
-
recordDispatchVerdict(traitIndex.byName.get(seed.trait)?.orbitalName ?? seed.trait, seed.event, {
|
|
1399
|
-
response: { ...seed.local, success: outcome.success, ...outcome.error !== void 0 ? { error: outcome.error } : {} }
|
|
1400
|
-
});
|
|
1401
|
-
stateLog.debug("mount:settled", { traitName: seed.trait, eventKey: seed.event, mode: seed.mode, transitioned: seed.local.transitioned });
|
|
1402
|
-
await afterSettle(seed.trait, seed.event, payload, seed.local.transitioned, [...seed.local.clientEffectsByTrait ?? [], ...serverRepaints], composedBefore);
|
|
1403
|
-
}
|
|
1404
|
-
}, [kernel, slotFlush, traitIndex, store, eventBus, options.navigate, options.navigateBack, afterSettle]);
|
|
1405
|
-
useBusIngress(traitBindings, traitIndex, dispatchAndSettle, eventBus, appTraitNames);
|
|
1406
|
-
const sendEvent = useCallback((eventKey, payload) => {
|
|
1407
|
-
const normalized = normalizeEventKey(eventKey);
|
|
1408
|
-
for (const [traitName] of traitIndex.byName) {
|
|
1409
|
-
if (!store.manager.canHandleEvent(traitName, normalized)) continue;
|
|
1410
|
-
void dispatchAndSettle(traitName, normalized, payload);
|
|
1411
|
-
}
|
|
1412
|
-
}, [traitIndex, store, dispatchAndSettle]);
|
|
1413
|
-
const getTraitState = useCallback((traitName) => store.manager.getState(traitName), [store]);
|
|
1414
|
-
const canHandleEvent = useCallback((traitName, eventKey) => store.manager.canHandleEvent(traitName, normalizeEventKey(eventKey)), [store]);
|
|
1415
|
-
const prevActiveTraitNamesRef = useRef(/* @__PURE__ */ new Set());
|
|
1416
|
-
useEffect(() => {
|
|
1417
|
-
const nextActiveTraitNames = new Set(
|
|
1418
|
-
traitBindings.map((b) => b.trait.name).filter((n) => !!n)
|
|
1419
|
-
);
|
|
1420
|
-
const dropped = diffDroppedTraitNames(prevActiveTraitNamesRef.current, nextActiveTraitNames);
|
|
1421
|
-
if (dropped.length > 0) {
|
|
1422
|
-
for (const traitName of dropped) {
|
|
1423
|
-
for (const slot of ALL_SLOTS) {
|
|
1424
|
-
uiSlots.clearBySource(slot, traitName);
|
|
1425
|
-
}
|
|
1426
|
-
}
|
|
1427
|
-
}
|
|
1428
|
-
prevActiveTraitNamesRef.current = nextActiveTraitNames;
|
|
1429
|
-
}, [traitBindings, uiSlots]);
|
|
1430
|
-
const mountedRef = useRef(null);
|
|
1431
|
-
const aliveRef = useRef(true);
|
|
1432
|
-
useEffect(() => {
|
|
1433
|
-
aliveRef.current = true;
|
|
1434
|
-
return () => {
|
|
1435
|
-
aliveRef.current = false;
|
|
1436
|
-
};
|
|
1437
|
-
}, []);
|
|
1438
|
-
const mountKey = `${options.mountKey ?? ""}\0${JSON.stringify(Object.entries(options.initPayload ?? {}).sort(([a], [b]) => a.localeCompare(b)))}`;
|
|
1439
|
-
useEffect(() => {
|
|
1440
|
-
const prev = mountedRef.current;
|
|
1441
|
-
const mount = prev !== null && prev.key === mountKey && prev.store === store ? prev : { key: mountKey, store, initialized: /* @__PURE__ */ new Set() };
|
|
1442
|
-
const active = new Set(traitBindings.map((b) => b.trait.name));
|
|
1443
|
-
const left = [...mount.initialized].filter((traitName) => !active.has(traitName));
|
|
1444
|
-
for (const traitName of left) mount.initialized.delete(traitName);
|
|
1445
|
-
store.mount.unmounted(left);
|
|
1446
|
-
const entering = [];
|
|
1447
|
-
const embeddedChildren = new Set([...options.callsiteCaptureChildrenByTrait?.values() ?? []].flatMap((c) => [...c]));
|
|
1448
|
-
for (const binding of traitBindings) {
|
|
1449
|
-
const traitName = binding.trait.name;
|
|
1450
|
-
if (mount.initialized.has(traitName)) continue;
|
|
1451
|
-
mount.initialized.add(traitName);
|
|
1452
|
-
const indexed = traitIndex.byName.get(traitName);
|
|
1453
|
-
if (embeddedChildren.has(traitName) && indexed !== void 0 && (traitReferencesCallsitePayload(indexed.irTrait) || configReferencesCallsitePayload(indexed.config)) && store.manager.repaintLifecycleEvent(traitName) !== void 0 && !store.callsitePayloads.has(traitName)) {
|
|
1454
|
-
continue;
|
|
1455
|
-
}
|
|
1456
|
-
const lifecycleEvent = LIFECYCLE_EVENTS.find((evt) => store.manager.canHandleEvent(traitName, evt));
|
|
1457
|
-
if (lifecycleEvent !== void 0) entering.push({ trait: traitName, event: lifecycleEvent });
|
|
1458
|
-
}
|
|
1459
|
-
store.mount.mounting(entering.map((e) => e.trait));
|
|
1460
|
-
if (entering.length > 0) {
|
|
1461
|
-
uiSlots.clearBySource("main", MOUNT_ERROR_SOURCE);
|
|
1462
|
-
void mountAndSettle(entering, { ...options.initPayload ?? {} }).catch((err) => {
|
|
1463
|
-
const traits = entering.map((e) => e.trait);
|
|
1464
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1465
|
-
const failed = err instanceof TraitMountError ? err.trait : traits.join(", ");
|
|
1466
|
-
const cause = err instanceof TraitMountError ? err.original : err;
|
|
1467
|
-
stateLog.warn("mount:failed", { traits, failed, error: message, stack: cause instanceof Error ? cause.stack : void 0 });
|
|
1468
|
-
if (!aliveRef.current) return;
|
|
1469
|
-
uiSlots.render({
|
|
1470
|
-
target: "main",
|
|
1471
|
-
pattern: "error-state",
|
|
1472
|
-
props: { title: t("error.traitMountFailed", { trait: failed }), message, "data-testid": TRAIT_MOUNT_ERROR_TESTID },
|
|
1473
|
-
sourceTrait: MOUNT_ERROR_SOURCE
|
|
1474
|
-
});
|
|
1475
|
-
});
|
|
1476
|
-
}
|
|
1477
|
-
mountedRef.current = mount;
|
|
1478
|
-
}, [traitBindings, store, mountAndSettle, mountKey, uiSlots, traitIndex, options.callsiteCaptureChildrenByTrait, t]);
|
|
1479
|
-
useSyncExternalStore(store.subscribe, store.getVersion, store.getVersion);
|
|
1480
|
-
const traitStates = useMemo(() => store.manager.getAllStates(), [store, store.getVersion()]);
|
|
1481
|
-
return { traitStates, sendEvent, getTraitState, canHandleEvent, entityBindingSource, awaitingSkeletonSource };
|
|
1482
|
-
}
|
|
1483
|
-
function useBrowserStore(appName, orbitals, locale) {
|
|
1484
|
-
const entities = useMemo(() => orbitals.flatMap(orbitalInlineEntities).filter(storesRowsInBrowser), [orbitals]);
|
|
1485
|
-
const [state, setState] = useState(() => entities.length === 0 ? { status: "none" } : { status: "opening" });
|
|
1486
|
-
useEffect(() => {
|
|
1487
|
-
if (entities.length === 0) {
|
|
1488
|
-
setState({ status: "none" });
|
|
1489
|
-
return;
|
|
1490
|
-
}
|
|
1491
|
-
let cancelled = false;
|
|
1492
|
-
let opened;
|
|
1493
|
-
setState({ status: "opening" });
|
|
1494
|
-
const open = async () => {
|
|
1495
|
-
try {
|
|
1496
|
-
const store = await openBrowserStore(browserStoreName(appName, locale), orbitals, void 0, locale);
|
|
1497
|
-
if (store === null) return;
|
|
1498
|
-
opened = store;
|
|
1499
|
-
if (!cancelled) setState({ status: "ready", store });
|
|
1500
|
-
} catch (err) {
|
|
1501
|
-
if (!cancelled) setState({ status: "failed", error: err instanceof Error ? err : new Error(String(err)) });
|
|
1502
|
-
}
|
|
1503
|
-
};
|
|
1504
|
-
void open();
|
|
1505
|
-
return () => {
|
|
1506
|
-
cancelled = true;
|
|
1507
|
-
opened?.close();
|
|
1508
|
-
};
|
|
1509
|
-
}, [appName, locale, entities, orbitals]);
|
|
1510
|
-
return state;
|
|
1511
|
-
}
|
|
1512
|
-
|
|
1513
|
-
// runtime/fitContent.ts
|
|
1514
|
-
var IDENTITY = { scale: 1, x: 0, y: 0 };
|
|
1515
|
-
function fitContentTransform(box, content) {
|
|
1516
|
-
if (box.width <= 0 || box.height <= 0 || content.width <= 0 || content.height <= 0) return IDENTITY;
|
|
1517
|
-
const scale = Math.min(1, box.width / content.width, box.height / content.height);
|
|
1518
|
-
return {
|
|
1519
|
-
scale,
|
|
1520
|
-
x: box.width / 2 - (content.x + content.width / 2) * scale,
|
|
1521
|
-
y: box.height / 2 - (content.y + content.height / 2) * scale
|
|
1522
|
-
};
|
|
1523
|
-
}
|
|
1524
|
-
function slotContentRect(inner, scale) {
|
|
1525
|
-
const origin = inner.getBoundingClientRect();
|
|
1526
|
-
let left = Infinity;
|
|
1527
|
-
let top = Infinity;
|
|
1528
|
-
let right = -Infinity;
|
|
1529
|
-
let bottom = -Infinity;
|
|
1530
|
-
for (const slot of Array.from(inner.querySelectorAll("[data-orb-slot]"))) {
|
|
1531
|
-
for (const child of Array.from(slot.querySelectorAll("*"))) {
|
|
1532
|
-
const r = child.getBoundingClientRect();
|
|
1533
|
-
if (r.width === 0 && r.height === 0) continue;
|
|
1534
|
-
left = Math.min(left, r.left);
|
|
1535
|
-
top = Math.min(top, r.top);
|
|
1536
|
-
right = Math.max(right, r.right);
|
|
1537
|
-
bottom = Math.max(bottom, r.bottom);
|
|
1538
|
-
}
|
|
1539
|
-
}
|
|
1540
|
-
if (left === Infinity) return null;
|
|
1541
|
-
return {
|
|
1542
|
-
x: (left - origin.left) / scale,
|
|
1543
|
-
y: (top - origin.top) / scale,
|
|
1544
|
-
width: (right - left) / scale,
|
|
1545
|
-
height: (bottom - top) / scale
|
|
1546
|
-
};
|
|
1547
|
-
}
|
|
1548
|
-
var xOrbitalLog = createLogger("almadar:runtime:cross-orbital");
|
|
1549
|
-
var navLog = createLogger("almadar:runtime:navigation");
|
|
1550
|
-
function NavStackRefBridge({ apiRef }) {
|
|
1551
|
-
const api = useNavStack();
|
|
1552
|
-
useEffect(() => {
|
|
1553
|
-
apiRef.current = api;
|
|
1554
|
-
return () => {
|
|
1555
|
-
apiRef.current = null;
|
|
1556
|
-
};
|
|
1557
|
-
}, [api, apiRef]);
|
|
1558
|
-
return null;
|
|
1559
|
-
}
|
|
1560
|
-
function HostHrefBaseProbe({ onBase }) {
|
|
1561
|
-
const base = useHref("/");
|
|
1562
|
-
useEffect(() => onBase(base), [base, onBase]);
|
|
1563
|
-
return null;
|
|
1564
|
-
}
|
|
1565
|
-
function appPathFromHref(href, hostHrefBase) {
|
|
1566
|
-
const root = hostHrefBase.endsWith("/") ? hostHrefBase.slice(0, -1) : hostHrefBase;
|
|
1567
|
-
if (href === root) return "/";
|
|
1568
|
-
if (!href.startsWith(`${root}/`)) return null;
|
|
1569
|
-
return href.slice(root.length);
|
|
1570
|
-
}
|
|
1571
|
-
function TraitInitializer({ traits, routeParams, mountKey, orbitals, onNavigate, onNavigateBack, onLocalFallback, localFallbackTimeoutMs, persistence, browserStore, traitConfigsByName, embeddedTraits, callsiteCaptureChildrenByTrait, hasBridge, children }) {
|
|
1572
|
-
const bridge = useServerBridge();
|
|
1573
|
-
const uiSlots = useUISlots();
|
|
1574
|
-
useEffect(() => {
|
|
1575
|
-
if (!hasBridge) return;
|
|
1576
|
-
const timer = setTimeout(() => {
|
|
1577
|
-
if (!bridge.connected) onLocalFallback?.();
|
|
1578
|
-
}, localFallbackTimeoutMs ?? 5e3);
|
|
1579
|
-
return () => clearTimeout(timer);
|
|
1580
|
-
}, [hasBridge, bridge.connected, onLocalFallback, localFallbackTimeoutMs]);
|
|
1581
|
-
const { entityBindingSource, awaitingSkeletonSource } = useTraitStateMachine(traits, uiSlots, {
|
|
1582
|
-
orbitals,
|
|
1583
|
-
...onNavigate !== void 0 ? { navigate: onNavigate } : {},
|
|
1584
|
-
...onNavigateBack !== void 0 ? { navigateBack: onNavigateBack } : {},
|
|
1585
|
-
...traitConfigsByName !== void 0 ? { traitConfigsByName } : {},
|
|
1586
|
-
...embeddedTraits !== void 0 ? { embeddedTraits } : {},
|
|
1587
|
-
...callsiteCaptureChildrenByTrait !== void 0 ? { callsiteCaptureChildrenByTrait } : {},
|
|
1588
|
-
initPayload: routeParams,
|
|
1589
|
-
...mountKey !== void 0 ? { mountKey } : {},
|
|
1590
|
-
...hasBridge ? { transport: bridge.transport, carriesCircuitState: bridge.carriesCircuitState, awaitTopology: !bridge.topologyKnown } : { persistence },
|
|
1591
|
-
...browserStore !== void 0 ? { browserStore } : {}
|
|
1592
|
-
});
|
|
1593
|
-
return /* @__PURE__ */ jsx(EntityBindingContext.Provider, { value: entityBindingSource, children: /* @__PURE__ */ jsx(AwaitingSkeletonContext.Provider, { value: awaitingSkeletonSource, children }) });
|
|
1594
|
-
}
|
|
1595
|
-
var CONTENT_STAGE_WIDTH = 480;
|
|
1596
|
-
var NO_ORBITALS = [];
|
|
1597
|
-
var LazyPageOfRegisteredProgram = createContext(false);
|
|
1598
|
-
function FitToBox({ children, mode = "box" }) {
|
|
1599
|
-
const outerRef = useRef(null);
|
|
1600
|
-
const innerRef = useRef(null);
|
|
1601
|
-
const [fitted, setFitted] = useState({ scale: 1, x: 0, y: 0 });
|
|
1602
|
-
const scaleRef = useRef(1);
|
|
1603
|
-
useEffect(() => {
|
|
1604
|
-
const outer = outerRef.current;
|
|
1605
|
-
const inner = innerRef.current;
|
|
1606
|
-
if (!outer || !inner) return;
|
|
1607
|
-
const update = () => {
|
|
1608
|
-
const cw = outer.clientWidth;
|
|
1609
|
-
const ch = outer.clientHeight;
|
|
1610
|
-
if (mode === "content") {
|
|
1611
|
-
const rect = slotContentRect(inner, scaleRef.current);
|
|
1612
|
-
if (!rect) return;
|
|
1613
|
-
const next = fitContentTransform({ width: cw, height: ch }, rect);
|
|
1614
|
-
scaleRef.current = next.scale;
|
|
1615
|
-
setFitted(next);
|
|
1616
|
-
return;
|
|
1617
|
-
}
|
|
1618
|
-
const sw = inner.scrollWidth;
|
|
1619
|
-
const sh = inner.scrollHeight;
|
|
1620
|
-
if (!sw || !sh || !cw || !ch) return;
|
|
1621
|
-
setFitted({ scale: Math.min(1, cw / sw, ch / sh), x: 0, y: 0 });
|
|
1622
|
-
};
|
|
1623
|
-
update();
|
|
1624
|
-
const cleanups = [];
|
|
1625
|
-
if (typeof ResizeObserver !== "undefined") {
|
|
1626
|
-
const ro = new ResizeObserver(update);
|
|
1627
|
-
ro.observe(outer);
|
|
1628
|
-
ro.observe(inner);
|
|
1629
|
-
cleanups.push(() => ro.disconnect());
|
|
1630
|
-
}
|
|
1631
|
-
if (mode === "content" && typeof MutationObserver !== "undefined") {
|
|
1632
|
-
let frame = 0;
|
|
1633
|
-
const schedule = () => {
|
|
1634
|
-
cancelAnimationFrame(frame);
|
|
1635
|
-
frame = requestAnimationFrame(update);
|
|
1636
|
-
};
|
|
1637
|
-
const mo = new MutationObserver(schedule);
|
|
1638
|
-
mo.observe(inner, { childList: true, subtree: true, attributes: true, characterData: true });
|
|
1639
|
-
schedule();
|
|
1640
|
-
cleanups.push(() => {
|
|
1641
|
-
mo.disconnect();
|
|
1642
|
-
cancelAnimationFrame(frame);
|
|
1643
|
-
});
|
|
1644
|
-
}
|
|
1645
|
-
return () => cleanups.forEach((c) => c());
|
|
1646
|
-
}, [mode]);
|
|
1647
|
-
return /* @__PURE__ */ jsx("div", { ref: outerRef, className: "relative h-full w-full overflow-hidden", "data-fit-mode": mode, children: /* @__PURE__ */ jsx(
|
|
1648
|
-
"div",
|
|
1649
|
-
{
|
|
1650
|
-
ref: innerRef,
|
|
1651
|
-
style: {
|
|
1652
|
-
transform: `translate(${fitted.x}px, ${fitted.y}px) scale(${fitted.scale})`,
|
|
1653
|
-
transformOrigin: "top left",
|
|
1654
|
-
width: mode === "content" ? CONTENT_STAGE_WIDTH : "fit-content"
|
|
1655
|
-
},
|
|
1656
|
-
children
|
|
1657
|
-
}
|
|
1658
|
-
) });
|
|
1659
|
-
}
|
|
1660
|
-
function SchemaRunner({ schema, serverUrl, transport, getAccessToken, mockData, pageName, routeParams, onNavigate, onNavigateBack, onLocalFallback, localFallbackTimeoutMs, persistence, themeOverride }) {
|
|
1661
|
-
const { traits, allEntities, allTraits, ir } = useResolvedSchema(schema, pageName);
|
|
1662
|
-
const allPageTraits = useMemo(() => {
|
|
1663
|
-
let base;
|
|
1664
|
-
if (pageName && traits.length > 0) {
|
|
1665
|
-
base = traits;
|
|
1666
|
-
} else if (!ir?.pages || ir.pages.size <= 1) {
|
|
1667
|
-
base = traits;
|
|
1668
|
-
} else {
|
|
1669
|
-
const firstPage = ir.pages.values().next().value;
|
|
1670
|
-
if (!firstPage) {
|
|
1671
|
-
base = traits;
|
|
1672
|
-
} else {
|
|
1673
|
-
const firstPageTraits = [];
|
|
1674
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1675
|
-
for (const binding of firstPage.traits) {
|
|
1676
|
-
const name = binding.trait.name;
|
|
1677
|
-
if (name && !seen.has(name)) {
|
|
1678
|
-
seen.add(name);
|
|
1679
|
-
firstPageTraits.push(binding);
|
|
1680
|
-
}
|
|
1681
|
-
}
|
|
1682
|
-
base = firstPageTraits.length > 0 ? firstPageTraits : traits;
|
|
1683
|
-
}
|
|
1684
|
-
}
|
|
1685
|
-
const byName = new Set(base.map((b) => b.trait.name));
|
|
1686
|
-
const extra = [];
|
|
1687
|
-
const queue = [...base];
|
|
1688
|
-
while (queue.length > 0) {
|
|
1689
|
-
const binding = queue.shift();
|
|
1690
|
-
if (!binding) continue;
|
|
1691
|
-
for (const refName of collectTraitRefsFromResolvedTrait(binding.trait)) {
|
|
1692
|
-
if (byName.has(refName)) continue;
|
|
1693
|
-
const rt = allTraits.get(refName);
|
|
1694
|
-
if (!rt) continue;
|
|
1695
|
-
byName.add(refName);
|
|
1696
|
-
const sibling = { trait: rt, linkedEntity: rt.linkedEntity };
|
|
1697
|
-
extra.push(sibling);
|
|
1698
|
-
queue.push(sibling);
|
|
1699
|
-
}
|
|
1700
|
-
}
|
|
1701
|
-
return extra.length > 0 ? [...base, ...extra] : base;
|
|
1702
|
-
}, [ir, traits, pageName, allTraits]);
|
|
1703
|
-
const orbitalsByTrait = useMemo(
|
|
1704
|
-
() => buildOrbitalsByTrait(
|
|
1705
|
-
schema,
|
|
1706
|
-
ir ? Array.from(ir.pages.values()).map((p) => ({
|
|
1707
|
-
path: p.path,
|
|
1708
|
-
traitNames: p.traits.map((b) => b.trait.name)
|
|
1709
|
-
})) : []
|
|
1710
|
-
),
|
|
1711
|
-
[schema, ir]
|
|
1712
|
-
);
|
|
1713
|
-
const traitLinkedEntitiesMap = useMemo(() => {
|
|
1714
|
-
const map = /* @__PURE__ */ new Map();
|
|
1715
|
-
if (ir) {
|
|
1716
|
-
for (const page of ir.pages.values()) {
|
|
1717
|
-
for (const binding of page.traits) {
|
|
1718
|
-
if (binding.linkedEntity) {
|
|
1719
|
-
map.set(binding.trait.name, binding.linkedEntity);
|
|
1720
|
-
}
|
|
1721
|
-
}
|
|
1722
|
-
}
|
|
1723
|
-
}
|
|
1724
|
-
return map;
|
|
1725
|
-
}, [ir]);
|
|
1726
|
-
const orbitalsByTraitMap = useMemo(
|
|
1727
|
-
() => new Map(Object.entries(orbitalsByTrait)),
|
|
1728
|
-
[orbitalsByTrait]
|
|
1729
|
-
);
|
|
1730
|
-
const entitiesArray = useMemo(() => Array.from(allEntities.values()), [allEntities]);
|
|
1731
|
-
const pageOrbitalNames = useMemo(() => {
|
|
1732
|
-
const set = /* @__PURE__ */ new Set();
|
|
1733
|
-
for (const binding of allPageTraits) {
|
|
1734
|
-
const orb = orbitalsByTrait[binding.trait.name];
|
|
1735
|
-
if (orb) set.add(orb);
|
|
1736
|
-
}
|
|
1737
|
-
return Array.from(set);
|
|
1738
|
-
}, [allPageTraits, orbitalsByTrait]);
|
|
1739
|
-
useEffect(() => {
|
|
1740
|
-
const traitNames = allPageTraits.map((b) => b.trait.name);
|
|
1741
|
-
const orbitalsByTraitForPage = {};
|
|
1742
|
-
for (const name of traitNames) {
|
|
1743
|
-
const orb = orbitalsByTrait[name];
|
|
1744
|
-
if (orb) orbitalsByTraitForPage[name] = orb;
|
|
1745
|
-
}
|
|
1746
|
-
xOrbitalLog.info("SchemaRunner:mount", {
|
|
1747
|
-
pageName,
|
|
1748
|
-
traitNames: traitNames.join(","),
|
|
1749
|
-
orbitalsByTraitForPage,
|
|
1750
|
-
pageOrbitalNames: pageOrbitalNames.join(",")
|
|
1751
|
-
});
|
|
1752
|
-
}, [pageName, allPageTraits, orbitalsByTrait, pageOrbitalNames]);
|
|
1753
|
-
const traitConfigsByName = useMemo(
|
|
1754
|
-
() => buildResolvedTraitConfigs(schema),
|
|
1755
|
-
[schema]
|
|
1756
|
-
);
|
|
1757
|
-
const callsiteCaptureChildrenByTrait = useMemo(() => {
|
|
1758
|
-
const merged = /* @__PURE__ */ new Map();
|
|
1759
|
-
const orbitals = schema?.orbitals;
|
|
1760
|
-
if (orbitals) {
|
|
1761
|
-
for (const orbital of orbitals) {
|
|
1762
|
-
for (const [referrer, children] of collectCallsiteCaptureChildren(orbital)) {
|
|
1763
|
-
merged.set(referrer, children);
|
|
1764
|
-
}
|
|
1765
|
-
}
|
|
1766
|
-
}
|
|
1767
|
-
return merged;
|
|
1768
|
-
}, [schema]);
|
|
1769
|
-
const embeddedTraits = useMemo(() => {
|
|
1770
|
-
const set = collectEmbeddedTraits(schema);
|
|
1771
|
-
xOrbitalLog.info("SchemaRunner:embeddedTraits", {
|
|
1772
|
-
pageName,
|
|
1773
|
-
embedded: Array.from(set),
|
|
1774
|
-
embeddedCount: set.size
|
|
1775
|
-
});
|
|
1776
|
-
return set;
|
|
1777
|
-
}, [schema, pageName]);
|
|
1778
|
-
const activeOrbitalTheme = useMemo(() => resolvePreviewTheme(schema, pageName), [schema, pageName]);
|
|
1779
|
-
const { locale } = useTranslate$1();
|
|
1780
|
-
const browserStore = useBrowserStore(schema.name ?? "app", transport?.hostsBrowserStore === true ? NO_ORBITALS : schema.orbitals, locale);
|
|
1781
|
-
const inner = /* @__PURE__ */ jsx(VerificationProvider, { enabled: true, children: /* @__PURE__ */ jsx(
|
|
1782
|
-
EntitySchemaProvider,
|
|
1783
|
-
{
|
|
1784
|
-
entities: entitiesArray,
|
|
1785
|
-
traitLinkedEntities: traitLinkedEntitiesMap,
|
|
1786
|
-
orbitalsByTrait: orbitalsByTraitMap,
|
|
1787
|
-
children: /* @__PURE__ */ jsx(
|
|
1788
|
-
TraitInitializer,
|
|
1789
|
-
{
|
|
1790
|
-
traits: allPageTraits,
|
|
1791
|
-
routeParams,
|
|
1792
|
-
mountKey: pageName,
|
|
1793
|
-
orbitals: schema.orbitals,
|
|
1794
|
-
hasBridge: Boolean(serverUrl || transport),
|
|
1795
|
-
traitConfigsByName,
|
|
1796
|
-
embeddedTraits,
|
|
1797
|
-
callsiteCaptureChildrenByTrait,
|
|
1798
|
-
onNavigate,
|
|
1799
|
-
onNavigateBack,
|
|
1800
|
-
onLocalFallback,
|
|
1801
|
-
localFallbackTimeoutMs,
|
|
1802
|
-
persistence,
|
|
1803
|
-
...browserStore.status === "ready" ? { browserStore: browserStore.store } : {},
|
|
1804
|
-
children: /* @__PURE__ */ jsx(OrbitalThemeProvider, { theme: activeOrbitalTheme, override: themeOverride, children: /* @__PURE__ */ jsx(Box, { className: "h-full min-h-full overflow-auto p-4", children: /* @__PURE__ */ jsx(UISlotRenderer, { includeHud: true, hudMode: "inline", includeFloating: true, pageKey: pageName }) }) })
|
|
1805
|
-
}
|
|
1806
|
-
)
|
|
1807
|
-
}
|
|
1808
|
-
) });
|
|
1809
|
-
if (browserStore.status === "opening") return /* @__PURE__ */ jsx(LoadingState, {});
|
|
1810
|
-
if (browserStore.status === "failed") {
|
|
1811
|
-
return /* @__PURE__ */ jsx(ErrorState, { "data-testid": "browser-store-error", message: browserStore.error.message });
|
|
1812
|
-
}
|
|
1813
|
-
return inner;
|
|
1814
|
-
}
|
|
1815
|
-
function resolvePreviewTheme(schema, pageName) {
|
|
1816
|
-
if (!schema.orbitals?.length) return schema.theme;
|
|
1817
|
-
if (pageName) {
|
|
1818
|
-
for (const orb of schema.orbitals) {
|
|
1819
|
-
for (const pageRef of orb.pages ?? []) {
|
|
1820
|
-
const name = typeof pageRef === "object" && pageRef !== null && "name" in pageRef ? pageRef.name : void 0;
|
|
1821
|
-
if (name === pageName) return orb.theme ?? schema.theme;
|
|
1822
|
-
}
|
|
1823
|
-
}
|
|
1824
|
-
}
|
|
1825
|
-
return schema.orbitals[0]?.theme ?? schema.theme;
|
|
1826
|
-
}
|
|
1827
|
-
function NavigateListener({ onNavigate }) {
|
|
1828
|
-
const bus = useEventBus();
|
|
1829
|
-
useEffect(
|
|
1830
|
-
() => bus.on("UI:NAVIGATE", (event) => {
|
|
1831
|
-
const url = event.payload?.url;
|
|
1832
|
-
if (typeof url === "string" && url.length > 0) onNavigate(url);
|
|
1833
|
-
}),
|
|
1834
|
-
[bus, onNavigate]
|
|
1835
|
-
);
|
|
1836
|
-
return null;
|
|
1837
|
-
}
|
|
1838
|
-
function OrbPreview({
|
|
1839
|
-
schema,
|
|
1840
|
-
mockData,
|
|
1841
|
-
autoMock = false,
|
|
1842
|
-
height = "400px",
|
|
1843
|
-
className,
|
|
1844
|
-
serverUrl,
|
|
1845
|
-
transport,
|
|
1846
|
-
getAccessToken,
|
|
1847
|
-
initialPagePath,
|
|
1848
|
-
isolated = false,
|
|
1849
|
-
fit = false,
|
|
1850
|
-
onPageChange,
|
|
1851
|
-
user = null,
|
|
1852
|
-
localFallbackTimeoutMs,
|
|
1853
|
-
themeOverride,
|
|
1854
|
-
lazyLoader,
|
|
1855
|
-
schemaPath,
|
|
1856
|
-
onUnmatchedNavigate
|
|
1857
|
-
}) {
|
|
1858
|
-
if (serverUrl && transport) {
|
|
1859
|
-
throw new Error("OrbPreview accepts serverUrl OR transport, not both");
|
|
1860
|
-
}
|
|
1861
|
-
const { t } = useTranslate$1();
|
|
1862
|
-
const [localFallback, setLocalFallback] = useState(false);
|
|
1863
|
-
const inHostRouter = useInRouterContext();
|
|
1864
|
-
const [hostHrefBase, setHostHrefBase] = useState("/");
|
|
1865
|
-
const handleLocalFallback = useCallback(() => {
|
|
1866
|
-
if (localFallback) return;
|
|
1867
|
-
setLocalFallback(true);
|
|
1868
|
-
}, [localFallback]);
|
|
1869
|
-
const isLazyPageOfRegisteredProgram = useContext(LazyPageOfRegisteredProgram);
|
|
1870
|
-
const parseResult = useMemo(() => {
|
|
1871
|
-
let parsed;
|
|
1872
|
-
if (typeof schema === "string") {
|
|
1873
|
-
try {
|
|
1874
|
-
parsed = JSON.parse(schema);
|
|
1875
|
-
} catch (e) {
|
|
1876
|
-
return { ok: false, error: String(e) };
|
|
1877
|
-
}
|
|
1878
|
-
} else {
|
|
1879
|
-
parsed = schema;
|
|
1880
|
-
}
|
|
1881
|
-
if (autoMock && !serverUrl && !transport) {
|
|
1882
|
-
const prepared = prepareSchemaForPreview(parsed);
|
|
1883
|
-
return { ok: true, schema: prepared.schema, mockData: prepared.mockData };
|
|
1884
|
-
}
|
|
1885
|
-
return { ok: true, schema: parsed, mockData: mockData ?? {} };
|
|
1886
|
-
}, [schema, autoMock, serverUrl, transport, mockData]);
|
|
1887
|
-
const parsedSchema = parseResult.ok ? parseResult.schema : null;
|
|
1888
|
-
useArbitraryClassStyles(parsedSchema);
|
|
1889
|
-
const effectiveMockData = parseResult.ok ? parseResult.mockData : {};
|
|
1890
|
-
const persistence = useMemo(() => {
|
|
1891
|
-
if (!parsedSchema || serverUrl || transport) return void 0;
|
|
1892
|
-
if (!autoMock) return void 0;
|
|
1893
|
-
const adapter = new InMemoryPersistence();
|
|
1894
|
-
adapter.seed(effectiveMockData);
|
|
1895
|
-
return adapter;
|
|
1896
|
-
}, [parsedSchema, serverUrl, transport, autoMock, effectiveMockData]);
|
|
1897
|
-
const pages = useMemo(() => {
|
|
1898
|
-
if (!parsedSchema) return [];
|
|
1899
|
-
try {
|
|
1900
|
-
return getAllPages(parsedSchema);
|
|
1901
|
-
} catch {
|
|
1902
|
-
return [];
|
|
1903
|
-
}
|
|
1904
|
-
}, [parsedSchema]);
|
|
1905
|
-
const lazyPages = useMemo(() => parsedSchema?.lazyPages ?? [], [parsedSchema]);
|
|
1906
|
-
const matchRoute = useCallback((path) => {
|
|
1907
|
-
const candidates = [
|
|
1908
|
-
...pages.map((entry) => ({ kind: "page", entry, path: entry.page.path })),
|
|
1909
|
-
...lazyPages.map((page) => ({ kind: "lazy", page, path: page.path }))
|
|
1910
|
-
];
|
|
1911
|
-
const hit = matchPathAmong(candidates, path, (c) => c.path);
|
|
1912
|
-
if (!hit) return null;
|
|
1913
|
-
return hit.candidate.kind === "page" ? { kind: "page", entry: hit.candidate.entry, params: hit.params } : { kind: "lazy", page: hit.candidate.page, params: hit.params };
|
|
1914
|
-
}, [pages, lazyPages]);
|
|
1915
|
-
const initialRoute = useMemo(
|
|
1916
|
-
() => initialPagePath ? matchRoute(initialPagePath) : null,
|
|
1917
|
-
[matchRoute, initialPagePath]
|
|
1918
|
-
);
|
|
1919
|
-
const initialPageMatch = useMemo(() => {
|
|
1920
|
-
if (initialRoute?.kind !== "page" || !initialRoute.entry.page.name) return void 0;
|
|
1921
|
-
return { name: initialRoute.entry.page.name, params: initialRoute.params };
|
|
1922
|
-
}, [initialRoute]);
|
|
1923
|
-
const [lazyRoute, setLazyRoute] = useState(
|
|
1924
|
-
initialRoute?.kind === "lazy" && initialPagePath ? { page: initialRoute.page, path: initialPagePath } : null
|
|
1925
|
-
);
|
|
1926
|
-
const [lazyLoad, setLazyLoad] = useState({ status: "loading" });
|
|
1927
|
-
useEffect(() => {
|
|
1928
|
-
if (!lazyRoute) return;
|
|
1929
|
-
if (!lazyLoader) {
|
|
1930
|
-
setLazyLoad({ status: "error", error: `No loader for lazy page ${lazyRoute.page.path} (${lazyRoute.page.orbRef})` });
|
|
1931
|
-
return;
|
|
1932
|
-
}
|
|
1933
|
-
let cancelled = false;
|
|
1934
|
-
setLazyLoad({ status: "loading" });
|
|
1935
|
-
void loadLazyPage(lazyLoader, lazyRoute.page, schemaPath).then((result) => {
|
|
1936
|
-
if (cancelled) return;
|
|
1937
|
-
setLazyLoad(result.success ? { status: "ready", schema: result.data } : { status: "error", error: result.error });
|
|
1938
|
-
});
|
|
1939
|
-
return () => {
|
|
1940
|
-
cancelled = true;
|
|
1941
|
-
};
|
|
1942
|
-
}, [lazyRoute?.page, lazyLoader, schemaPath]);
|
|
1943
|
-
const initialPageName = initialPageMatch?.name;
|
|
1944
|
-
const [currentPage, setCurrentPage] = useState(initialPageName);
|
|
1945
|
-
const [routeParams, setRouteParams] = useState(initialPageMatch?.params ?? {});
|
|
1946
|
-
const prevInitialPagePathRef = useRef(initialPagePath);
|
|
1947
|
-
useEffect(() => {
|
|
1948
|
-
if (prevInitialPagePathRef.current !== initialPagePath) {
|
|
1949
|
-
prevInitialPagePathRef.current = initialPagePath;
|
|
1950
|
-
setCurrentPage(initialPageName);
|
|
1951
|
-
setRouteParams(initialPageMatch?.params ?? {});
|
|
1952
|
-
setLazyRoute(initialRoute?.kind === "lazy" && initialPagePath ? { page: initialRoute.page, path: initialPagePath } : null);
|
|
1953
|
-
}
|
|
1954
|
-
}, [initialPagePath, initialPageName, initialPageMatch, initialRoute]);
|
|
1955
|
-
const currentPagePath = useMemo(() => {
|
|
1956
|
-
const activePageName = currentPage ?? pages[0]?.page.name;
|
|
1957
|
-
const resolved = pages.find((p) => p.page.name === activePageName)?.page.path;
|
|
1958
|
-
return resolved ?? initialPagePath;
|
|
1959
|
-
}, [pages, currentPage, initialPagePath]);
|
|
1960
|
-
const navPages = useMemo(
|
|
1961
|
-
() => pages.filter((p) => typeof p.page.path === "string" && p.page.path.length > 0).map((p) => ({ path: p.page.path, name: p.page.name, orbital: p.orbitalName, label: p.page.label })),
|
|
1962
|
-
[pages]
|
|
1963
|
-
);
|
|
1964
|
-
const concreteCurrentPath = useMemo(() => {
|
|
1965
|
-
const pattern = currentPagePath ?? "/";
|
|
1966
|
-
return pattern.replace(/:([A-Za-z0-9_]+)/g, (whole, key) => routeParams[key] ?? whole);
|
|
1967
|
-
}, [currentPagePath, routeParams]);
|
|
1968
|
-
const navStackRef = useRef(null);
|
|
1969
|
-
const announcePage = useCallback((path) => {
|
|
1970
|
-
if (onPageChange) {
|
|
1971
|
-
onPageChange(path);
|
|
1972
|
-
} else if (!isolated && typeof window !== "undefined") {
|
|
1973
|
-
const url = new URL(window.location.href);
|
|
1974
|
-
url.searchParams.set("page", path);
|
|
1975
|
-
window.history.pushState({}, "", url.toString());
|
|
1976
|
-
window.dispatchEvent(new PopStateEvent("popstate"));
|
|
1977
|
-
}
|
|
1978
|
-
}, [isolated, onPageChange]);
|
|
1979
|
-
const handleNavigate = useCallback((path, navState) => {
|
|
1980
|
-
const route = matchRoute(path);
|
|
1981
|
-
if (!route) {
|
|
1982
|
-
onUnmatchedNavigate?.(path);
|
|
1983
|
-
return;
|
|
1984
|
-
}
|
|
1985
|
-
if (route.kind === "lazy") {
|
|
1986
|
-
setLazyRoute({ page: route.page, path });
|
|
1987
|
-
announcePage(path);
|
|
1988
|
-
return;
|
|
1989
|
-
}
|
|
1990
|
-
setLazyRoute(null);
|
|
1991
|
-
const match = route.entry;
|
|
1992
|
-
const params = { ...route.params, ...navState ?? {} };
|
|
1993
|
-
navLog.debug("handleNavigate", () => ({
|
|
1994
|
-
path,
|
|
1995
|
-
matched: match?.page.name ?? null,
|
|
1996
|
-
params,
|
|
1997
|
-
navState: navState ? JSON.stringify(navState) : void 0,
|
|
1998
|
-
availablePaths: pages.map((p) => p.page.path)
|
|
1999
|
-
}));
|
|
2000
|
-
if (match.page.name) {
|
|
2001
|
-
setRouteParams(params);
|
|
2002
|
-
setCurrentPage(match.page.name);
|
|
2003
|
-
announcePage(path);
|
|
2004
|
-
}
|
|
2005
|
-
}, [pages, matchRoute, announcePage, onUnmatchedNavigate]);
|
|
2006
|
-
const handleNavigateEffect = useCallback(
|
|
2007
|
-
(path, params, crumb) => {
|
|
2008
|
-
navStackRef.current?.beginNavigate(path, crumb);
|
|
2009
|
-
handleNavigate(path, params);
|
|
2010
|
-
},
|
|
2011
|
-
[handleNavigate]
|
|
2012
|
-
);
|
|
2013
|
-
const handleNavigateBack = useCallback(() => {
|
|
2014
|
-
navStackRef.current?.back();
|
|
2015
|
-
}, []);
|
|
2016
|
-
if (!parseResult.ok) {
|
|
2017
|
-
return /* @__PURE__ */ jsx(Box, { className, style: { height }, children: /* @__PURE__ */ jsxs(Typography, { as: "pre", color: "error", variant: "small", className: "font-mono whitespace-pre-wrap break-all m-0 p-4", children: [
|
|
2018
|
-
"Parse error: ",
|
|
2019
|
-
parseResult.error
|
|
2020
|
-
] }) });
|
|
2021
|
-
}
|
|
2022
|
-
const containerRef = useRef(null);
|
|
2023
|
-
useEffect(() => {
|
|
2024
|
-
const el = containerRef.current;
|
|
2025
|
-
if (!el) return;
|
|
2026
|
-
if (pages.length <= 1 && !isolated) {
|
|
2027
|
-
navLog.debug("interceptor:skipped", { reason: "single-page schema", pageCount: pages.length });
|
|
2028
|
-
return;
|
|
2029
|
-
}
|
|
2030
|
-
const handler = (e) => {
|
|
2031
|
-
const anchor = e.target.closest("a");
|
|
2032
|
-
if (!anchor) return;
|
|
2033
|
-
const href = anchor.getAttribute("href") ?? anchor.getAttribute("to") ?? "";
|
|
2034
|
-
navLog.debug("click:intercepted", {
|
|
2035
|
-
href,
|
|
2036
|
-
anchorText: anchor.textContent?.trim().slice(0, 40)
|
|
2037
|
-
});
|
|
2038
|
-
const external = href.startsWith("http") || href.startsWith("mailto:");
|
|
2039
|
-
const appPath = external ? null : appPathFromHref(href, hostHrefBase);
|
|
2040
|
-
if (isolated && appPath === null) {
|
|
2041
|
-
e.preventDefault();
|
|
2042
|
-
e.stopPropagation();
|
|
2043
|
-
e.stopImmediatePropagation();
|
|
2044
|
-
if (external) window.open(href, "_blank", "noopener,noreferrer");
|
|
2045
|
-
navLog.debug("click:sandboxed", { href, external });
|
|
2046
|
-
return;
|
|
2047
|
-
}
|
|
2048
|
-
if (appPath === null) {
|
|
2049
|
-
navLog.debug("click:skipped", { href, reason: "not an in-app href", hostHrefBase });
|
|
2050
|
-
return;
|
|
2051
|
-
}
|
|
2052
|
-
e.preventDefault();
|
|
2053
|
-
e.stopPropagation();
|
|
2054
|
-
e.stopImmediatePropagation();
|
|
2055
|
-
handleNavigate(appPath);
|
|
2056
|
-
};
|
|
2057
|
-
el.addEventListener("click", handler, true);
|
|
2058
|
-
navLog.info("interceptor:installed", { pageCount: pages.length, paths: pages.map((p) => p.page.path) });
|
|
2059
|
-
return () => el.removeEventListener("click", handler, true);
|
|
2060
|
-
}, [pages, handleNavigate, isolated, hostHrefBase]);
|
|
2061
|
-
const withBridge = (node) => (serverUrl || transport) && !isLazyPageOfRegisteredProgram ? /* @__PURE__ */ jsx(ServerBridgeProvider, { schema: parseResult.schema, serverUrl, transport, getAccessToken, children: node }) : node;
|
|
2062
|
-
if (lazyRoute) {
|
|
2063
|
-
return withBridge(
|
|
2064
|
-
/* @__PURE__ */ jsxs(Box, { ref: containerRef, className: `overflow-auto ${className ?? ""}`, style: { height }, children: [
|
|
2065
|
-
lazyLoad.status === "loading" && /* @__PURE__ */ jsx(LoadingState, {}),
|
|
2066
|
-
lazyLoad.status === "error" && /* @__PURE__ */ jsx(ErrorState, { "data-testid": "lazy-page-error", message: lazyLoad.error }),
|
|
2067
|
-
lazyLoad.status === "ready" && /* @__PURE__ */ jsx(LazyPageOfRegisteredProgram.Provider, { value: true, children: /* @__PURE__ */ jsx(
|
|
2068
|
-
OrbPreview,
|
|
2069
|
-
{
|
|
2070
|
-
schema: lazyLoad.schema,
|
|
2071
|
-
autoMock,
|
|
2072
|
-
height: "100%",
|
|
2073
|
-
className: "border-0",
|
|
2074
|
-
serverUrl,
|
|
2075
|
-
transport,
|
|
2076
|
-
getAccessToken,
|
|
2077
|
-
initialPagePath: lazyRoute.path,
|
|
2078
|
-
user,
|
|
2079
|
-
isolated,
|
|
2080
|
-
fit,
|
|
2081
|
-
onPageChange: announcePage,
|
|
2082
|
-
themeOverride,
|
|
2083
|
-
lazyLoader,
|
|
2084
|
-
onUnmatchedNavigate: handleNavigate
|
|
2085
|
-
},
|
|
2086
|
-
lazyRoute.page.orbRef
|
|
2087
|
-
) })
|
|
2088
|
-
] })
|
|
2089
|
-
);
|
|
2090
|
-
}
|
|
2091
|
-
return withBridge(
|
|
2092
|
-
/* @__PURE__ */ jsxs(
|
|
2093
|
-
Box,
|
|
2094
|
-
{
|
|
2095
|
-
ref: containerRef,
|
|
2096
|
-
className: `${fit ? "overflow-hidden" : "overflow-auto"} border border-[var(--color-border)] rounded-[var(--radius-md)] ${className ?? ""}`,
|
|
2097
|
-
style: { height },
|
|
2098
|
-
children: [
|
|
2099
|
-
localFallback && /* @__PURE__ */ jsx(Box, { className: "px-3 py-2 bg-[var(--color-warning)] bg-opacity-10 border-b border-[var(--color-warning)] flex items-center gap-2", children: /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-[var(--color-warning-foreground)] flex-1", children: t("orbPreview.serverUnreachable") }) }),
|
|
2100
|
-
inHostRouter ? /* @__PURE__ */ jsx(HostHrefBaseProbe, { onBase: setHostHrefBase }) : null,
|
|
2101
|
-
/* @__PURE__ */ jsx(CurrentPagePathProvider, { value: currentPagePath, children: /* @__PURE__ */ jsxs(
|
|
2102
|
-
NavStackProvider,
|
|
2103
|
-
{
|
|
2104
|
-
pages: navPages,
|
|
2105
|
-
currentPath: concreteCurrentPath,
|
|
2106
|
-
navigate: handleNavigate,
|
|
2107
|
-
storageKey: `almadar:navstack:${parseResult.schema.name ?? "preview"}`,
|
|
2108
|
-
children: [
|
|
2109
|
-
/* @__PURE__ */ jsx(NavStackRefBridge, { apiRef: navStackRef }),
|
|
2110
|
-
/* @__PURE__ */ jsxs(OrbitalProvider, { initialData: effectiveMockData, skipTheme: true, verification: true, isolated, user, children: [
|
|
2111
|
-
/* @__PURE__ */ jsx(NavigateListener, { onNavigate: handleNavigate }),
|
|
2112
|
-
/* @__PURE__ */ jsx(UISlotProvider, { children: fit ? /* @__PURE__ */ jsx(FitToBox, { mode: fit === "content" ? "content" : "box", children: /* @__PURE__ */ jsx(
|
|
2113
|
-
SchemaRunner,
|
|
2114
|
-
{
|
|
2115
|
-
schema: parseResult.schema,
|
|
2116
|
-
serverUrl,
|
|
2117
|
-
transport,
|
|
2118
|
-
getAccessToken,
|
|
2119
|
-
mockData: effectiveMockData,
|
|
2120
|
-
pageName: currentPage,
|
|
2121
|
-
routeParams,
|
|
2122
|
-
onNavigate: handleNavigateEffect,
|
|
2123
|
-
onNavigateBack: handleNavigateBack,
|
|
2124
|
-
onLocalFallback: handleLocalFallback,
|
|
2125
|
-
localFallbackTimeoutMs,
|
|
2126
|
-
persistence,
|
|
2127
|
-
themeOverride
|
|
2128
|
-
}
|
|
2129
|
-
) }) : /* @__PURE__ */ jsx(
|
|
2130
|
-
SchemaRunner,
|
|
2131
|
-
{
|
|
2132
|
-
schema: parseResult.schema,
|
|
2133
|
-
serverUrl,
|
|
2134
|
-
transport,
|
|
2135
|
-
getAccessToken,
|
|
2136
|
-
mockData: effectiveMockData,
|
|
2137
|
-
pageName: currentPage,
|
|
2138
|
-
routeParams,
|
|
2139
|
-
onNavigate: handleNavigateEffect,
|
|
2140
|
-
onNavigateBack: handleNavigateBack,
|
|
2141
|
-
onLocalFallback: handleLocalFallback,
|
|
2142
|
-
localFallbackTimeoutMs,
|
|
2143
|
-
persistence,
|
|
2144
|
-
themeOverride
|
|
2145
|
-
}
|
|
2146
|
-
) })
|
|
2147
|
-
] })
|
|
2148
|
-
]
|
|
2149
|
-
}
|
|
2150
|
-
) })
|
|
2151
|
-
]
|
|
2152
|
-
}
|
|
2153
|
-
)
|
|
2154
|
-
);
|
|
2155
|
-
}
|
|
2156
|
-
OrbPreview.displayName = "OrbPreview";
|
|
2157
|
-
var playgroundLog = createLogger("almadar:ui:browser-playground");
|
|
2158
|
-
function BrowserPlayground({
|
|
2159
|
-
schema,
|
|
2160
|
-
mode = "mock",
|
|
2161
|
-
initialPagePath,
|
|
2162
|
-
height,
|
|
2163
|
-
className,
|
|
2164
|
-
paused,
|
|
2165
|
-
fit,
|
|
2166
|
-
onPageChange,
|
|
2167
|
-
viewer,
|
|
2168
|
-
messages,
|
|
2169
|
-
locale,
|
|
2170
|
-
lazyLoader,
|
|
2171
|
-
schemaPath
|
|
2172
|
-
}) {
|
|
2173
|
-
const [runtime] = useState(
|
|
2174
|
-
() => new OrbitalServerRuntime({ mode, debug: false, ...viewer !== void 0 ? { defaultUser: viewer } : {} })
|
|
2175
|
-
);
|
|
2176
|
-
useEffect(() => {
|
|
2177
|
-
if (paused === void 0) return;
|
|
2178
|
-
if (paused) {
|
|
2179
|
-
runtime.pauseTicks();
|
|
2180
|
-
} else {
|
|
2181
|
-
runtime.resumeTicks();
|
|
2182
|
-
}
|
|
2183
|
-
}, [runtime, paused]);
|
|
2184
|
-
const lazySchemas = useMemo(() => {
|
|
2185
|
-
const lazyPages = schema.lazyPages ?? [];
|
|
2186
|
-
if (lazyPages.length === 0 || lazyLoader === void 0) return Promise.resolve([]);
|
|
2187
|
-
return Promise.all(lazyPages.map(async (page) => {
|
|
2188
|
-
const loaded = await loadLazyPage(lazyLoader, page, schemaPath);
|
|
2189
|
-
if (!loaded.success) throw new Error(loaded.error);
|
|
2190
|
-
return loaded.data;
|
|
2191
|
-
}));
|
|
2192
|
-
}, [schema.lazyPages, lazyLoader, schemaPath]);
|
|
2193
|
-
const registerProgram = useCallback((program) => {
|
|
2194
|
-
const orbitalNames = program.orbitals.map((o) => o.name);
|
|
2195
|
-
playgroundLog.debug("register:start", { schema: program.name, orbitalNames });
|
|
2196
|
-
return lazySchemas.then((lazy) => runtime.register(program, { ...messages !== void 0 ? { messages } : {}, lazy })).then(() => {
|
|
2197
|
-
playgroundLog.debug("register:done", { schema: program.name, orbitalNames });
|
|
2198
|
-
});
|
|
2199
|
-
}, [runtime, lazySchemas, messages]);
|
|
2200
|
-
const latestRegistration = useRef(Promise.resolve());
|
|
2201
|
-
const registrationReady = useMemo(() => {
|
|
2202
|
-
const ready = registerProgram(schema);
|
|
2203
|
-
latestRegistration.current = ready;
|
|
2204
|
-
return ready;
|
|
2205
|
-
}, [registerProgram, schema]);
|
|
2206
|
-
const teardownTimerRef = useRef(null);
|
|
2207
|
-
useEffect(() => {
|
|
2208
|
-
if (teardownTimerRef.current !== null) {
|
|
2209
|
-
clearTimeout(teardownTimerRef.current);
|
|
2210
|
-
teardownTimerRef.current = null;
|
|
2211
|
-
}
|
|
2212
|
-
return () => {
|
|
2213
|
-
teardownTimerRef.current = setTimeout(() => {
|
|
2214
|
-
teardownTimerRef.current = null;
|
|
2215
|
-
playgroundLog.debug("unregisterAll:unmount");
|
|
2216
|
-
runtime.unregisterAll();
|
|
2217
|
-
}, 0);
|
|
2218
|
-
};
|
|
2219
|
-
}, [runtime]);
|
|
2220
|
-
const transport = useMemo(
|
|
2221
|
-
() => createInProcessTransport(
|
|
2222
|
-
async (orbitalName, request) => {
|
|
2223
|
-
await latestRegistration.current;
|
|
2224
|
-
return runtime.processOrbitalEvent(orbitalName, request);
|
|
2225
|
-
},
|
|
2226
|
-
{
|
|
2227
|
-
// Registers what the bridge hands over (lazy behaviors included): a bridge that unregistered first gets it back.
|
|
2228
|
-
onRegister: (program) => {
|
|
2229
|
-
const ready = registerProgram(program);
|
|
2230
|
-
latestRegistration.current = ready;
|
|
2231
|
-
return ready;
|
|
2232
|
-
},
|
|
2233
|
-
onUnregister: () => {
|
|
2234
|
-
runtime.unregisterAll();
|
|
2235
|
-
}
|
|
2236
|
-
}
|
|
2237
|
-
),
|
|
2238
|
-
[runtime, registerProgram, registrationReady]
|
|
2239
|
-
);
|
|
2240
|
-
const viewerLocale = locale ?? schema.locales?.[0];
|
|
2241
|
-
const i18n = useMemo(() => {
|
|
2242
|
-
if (viewerLocale === void 0 || messages === void 0) return void 0;
|
|
2243
|
-
const catalog = messages[viewerLocale] ?? {};
|
|
2244
|
-
return { locale: viewerLocale, direction: localeDirection(viewerLocale), t: createTranslate(catalog, viewerLocale), messages: catalog };
|
|
2245
|
-
}, [viewerLocale, messages]);
|
|
2246
|
-
const preview = /* @__PURE__ */ jsx(
|
|
2247
|
-
OrbPreview,
|
|
2248
|
-
{
|
|
2249
|
-
schema,
|
|
2250
|
-
transport,
|
|
2251
|
-
initialPagePath,
|
|
2252
|
-
height,
|
|
2253
|
-
className,
|
|
2254
|
-
fit,
|
|
2255
|
-
onPageChange,
|
|
2256
|
-
user: viewer ?? null,
|
|
2257
|
-
lazyLoader,
|
|
2258
|
-
schemaPath,
|
|
2259
|
-
isolated: true
|
|
2260
|
-
}
|
|
2261
|
-
);
|
|
2262
|
-
return i18n !== void 0 ? /* @__PURE__ */ jsx(I18nProvider, { value: i18n, children: preview }) : preview;
|
|
2263
|
-
}
|
|
2264
|
-
|
|
2265
|
-
export { BrowserPlayground, OrbPreview, clearSchemaCache, useResolvedSchema, useTraitStateMachine };
|