@almadar/shell-web 0.1.0 → 1.0.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.
Files changed (118) hide show
  1. package/dist/{ArbitraryClassCompiler-Mzabq-UB.d.ts → ArbitraryClassCompiler-CO_iASBn.d.ts} +99 -4
  2. package/dist/AvlGlyph-EPJKFEO3.js +6 -0
  3. package/dist/AvlStateMachine-RKZIKNGC.js +9 -0
  4. package/dist/AvlTransitionDetail-63SD6GZF.js +11 -0
  5. package/dist/AvlTransitionExplainer-ZBTLOM3T.js +12 -0
  6. package/dist/BehaviorView-YS5G4GNB.js +11 -0
  7. package/dist/{Canvas3DHost-_Y-5uLM_.d.ts → Canvas3DHost-BkZQB5l2.d.ts} +1 -1
  8. package/dist/{EventBusProvider-CVwjkV_2.d.ts → EventBusProvider-BFFHfN4O.d.ts} +12 -0
  9. package/dist/FlowCanvas-AUMTWMNG.js +26 -0
  10. package/dist/{GameAudioProvider-Cm9BnvmP.d.ts → GameAudioProvider-ceM_n0ey.d.ts} +4 -7
  11. package/dist/KernelEngineContext-CcxGYtlr.d.ts +33 -0
  12. package/dist/LayersPanel-M2LHWUFF.js +6 -0
  13. package/dist/ModuleCard-QHDGK5XK.js +7 -0
  14. package/dist/OrbInspector-QCPEPUPH.js +26 -0
  15. package/dist/{Select-mDZIwNqN.d.ts → Select-B-iDdnvE.d.ts} +16 -30
  16. package/dist/ServerBridge-Bqsj-lOU.d.ts +124 -0
  17. package/dist/UISlotRenderer-C7WWOK43.js +26 -0
  18. package/dist/auth/browser/index.js +1 -0
  19. package/dist/auth/react/index.d.ts +8 -1
  20. package/dist/auth/react/index.js +40 -2
  21. package/dist/avl/index.d.ts +89 -79
  22. package/dist/avl/index.js +38 -38
  23. package/dist/{avl-3d-context-BIRKWpaL.d.ts → avl-3d-context-BycmO6tG.d.ts} +9 -12
  24. package/dist/{chunk-GKIFONTE.js → chunk-3S54FWRC.js} +2 -2
  25. package/dist/{chunk-DYEKAA4L.js → chunk-3VZ36ZKO.js} +240 -252
  26. package/dist/{chunk-PC7K4SK7.js → chunk-4T3P43NS.js} +18 -269
  27. package/dist/{chunk-MBQAMMSE.js → chunk-4YXBKHHM.js} +269 -658
  28. package/dist/{chunk-YDNJLLG6.js → chunk-57DC34LY.js} +19 -10
  29. package/dist/{chunk-SJGJJKBY.js → chunk-AB3X7QTE.js} +6 -3
  30. package/dist/{chunk-AILY3XPS.js → chunk-AE7VCQB6.js} +5 -6
  31. package/dist/{chunk-FAJDTHWC.js → chunk-AGXD77SL.js} +2 -2
  32. package/dist/chunk-AU7O65OX.js +66 -0
  33. package/dist/{chunk-L677XGY3.js → chunk-CPLN6NBQ.js} +5 -5
  34. package/dist/{chunk-DZ42OJQC.js → chunk-DZBQA2OW.js} +1 -1
  35. package/dist/{chunk-2PY54Z44.js → chunk-E6KA4J6N.js} +5 -5
  36. package/dist/{chunk-DJ433ET5.js → chunk-GFXCPUMX.js} +11 -4
  37. package/dist/{chunk-FP3WIKTC.js → chunk-HH5KSMH5.js} +9 -1
  38. package/dist/{chunk-U6QRZASX.js → chunk-ILQXJ2AR.js} +1 -1
  39. package/dist/{chunk-B4BU77LT.js → chunk-ILZ7ORX7.js} +1 -1
  40. package/dist/{chunk-SGM3FASH.js → chunk-K4MOPSEL.js} +14 -12
  41. package/dist/{chunk-GYTPOFVC.js → chunk-LHP2TK7O.js} +7402 -6970
  42. package/dist/{chunk-VFF5OO47.js → chunk-PCR37T3U.js} +15 -38
  43. package/dist/{chunk-R5O5AUPI.js → chunk-PRXQGGCO.js} +93 -86
  44. package/dist/{chunk-42FY4RYA.js → chunk-REXYVBUO.js} +2 -2
  45. package/dist/{chunk-DLZF4PVE.js → chunk-ROICKAU7.js} +3 -3
  46. package/dist/{chunk-KVQS4GXU.js → chunk-SP5CDJ6K.js} +18 -8
  47. package/dist/{chunk-P4LNHAFI.js → chunk-TUPZWTRE.js} +2 -2
  48. package/dist/chunk-TXAUOLWZ.js +490 -0
  49. package/dist/{chunk-EQAMU2ZC.js → chunk-UTDC2NEA.js} +1 -1
  50. package/dist/{chunk-73SRAB4P.js → chunk-UU57VNS2.js} +2 -2
  51. package/dist/{chunk-DFNZUYX4.js → chunk-UXLE3IPR.js} +70 -27
  52. package/dist/{chunk-L2DEKY4J.js → chunk-VO2TTLS5.js} +3 -6
  53. package/dist/{chunk-TESBIVUA.js → chunk-X3DMMX27.js} +2 -2
  54. package/dist/components/index.d.ts +331 -125
  55. package/dist/components/index.js +28 -28
  56. package/dist/context/index.d.ts +2 -3
  57. package/dist/context/index.js +4 -3
  58. package/dist/core.d.ts +93 -17
  59. package/dist/core.js +2 -1
  60. package/dist/design-classes/index.cjs +3 -1
  61. package/dist/design-classes/index.js +3 -1
  62. package/dist/hooks/index.d.ts +40 -5
  63. package/dist/hooks/index.js +5 -4
  64. package/dist/index.d.ts +36 -114
  65. package/dist/index.js +174 -34
  66. package/dist/kernel-client-TZKGHJRY.js +516 -0
  67. package/dist/kernel-wire-wNdAPfhJ.d.ts +88 -0
  68. package/dist/lib/index.d.ts +3 -3
  69. package/dist/lib/index.js +5 -4
  70. package/dist/{paintDispatch-ChW5mVuK.d.ts → paintDispatch-CuJLsr9Y.d.ts} +5 -15
  71. package/dist/{prism-AIWGN2X5.js → prism-X5YWKXO5.js} +5 -3
  72. package/dist/providers/index.d.ts +42 -27
  73. package/dist/providers/index.js +22 -23
  74. package/dist/renderer/index.d.ts +0 -2
  75. package/dist/renderer/index.js +1 -0
  76. package/dist/runtime/index.d.ts +52 -253
  77. package/dist/runtime/index.js +21 -24
  78. package/dist/ssr/index.cjs +2643 -535
  79. package/dist/ssr/index.d.cts +0 -23
  80. package/dist/ssr/index.d.ts +0 -23
  81. package/dist/ssr/index.js +2639 -535
  82. package/dist/storage/browser/index.js +1 -0
  83. package/dist/test-support.d.ts +15 -8
  84. package/dist/test-support.js +91 -24
  85. package/dist/three/index.d.ts +6 -6
  86. package/dist/three/index.js +49 -37
  87. package/dist/{verificationRegistry-CabxhfTv.d.ts → verificationRegistry-CVrcxG1s.d.ts} +3 -3
  88. package/package.json +13 -9
  89. package/src/core/lib/band-shapes.json +7 -0
  90. package/src/core/locales/ar.json +18 -5
  91. package/src/core/locales/en.json +18 -5
  92. package/src/core/locales/sl.json +18 -5
  93. package/tailwind-preset.cjs +30 -1
  94. package/dist/AvlGlyph-JKJOAH23.js +0 -6
  95. package/dist/AvlStateMachine-PI4YGREK.js +0 -8
  96. package/dist/AvlTransitionDetail-ZH7V24UG.js +0 -12
  97. package/dist/AvlTransitionExplainer-ZSRT7L6Y.js +0 -13
  98. package/dist/BehaviorValueFrame-6RUJXT4A.js +0 -29
  99. package/dist/BehaviorValueFrame-SSEMEBTG.css +0 -1756
  100. package/dist/BehaviorView-RFEE5J6E.js +0 -10
  101. package/dist/FlowCanvas-STDC4OWK.js +0 -27
  102. package/dist/KernelEngineContext-BOCQoFwk.d.ts +0 -43
  103. package/dist/LayersPanel-BBQ54IVG.js +0 -7
  104. package/dist/ModuleCard-3TT2X7IW.js +0 -7
  105. package/dist/OrbInspector-K6T6O7GI.js +0 -27
  106. package/dist/ServerBridge-Cm1On51b.d.ts +0 -233
  107. package/dist/TraitValueFrame-2PJGLLVI.js +0 -28
  108. package/dist/TraitValueFrame-4G3KOUBR.css +0 -1756
  109. package/dist/UISlotRenderer-JKFSX24W.js +0 -27
  110. package/dist/UserContext-DqWeZkUZ.d.ts +0 -100
  111. package/dist/chunk-2QOH2HWM.js +0 -95
  112. package/dist/chunk-6P3MWXYE.js +0 -440
  113. package/dist/chunk-FIW6WN23.js +0 -508
  114. package/dist/chunk-GAR5TQ5B.js +0 -199
  115. package/dist/chunk-ZO6MXAS3.js +0 -69
  116. package/dist/kernel-client-ZH4PSMCN.js +0 -367
  117. package/dist/kernel-wire-B4S8pnP3.d.ts +0 -65
  118. package/dist/useKeyboardRouter-Dsq553Ur.d.ts +0 -42
@@ -1,199 +0,0 @@
1
- import { getStateRole } from './chunk-DFNZUYX4.js';
2
- import { useState, useLayoutEffect } from 'react';
3
-
4
- function useContainerWidth(ref) {
5
- const [width, setWidth] = useState(null);
6
- useLayoutEffect(() => {
7
- const el = ref.current;
8
- if (!el) return;
9
- setWidth(el.clientWidth);
10
- if (typeof ResizeObserver === "undefined") return;
11
- const ro = new ResizeObserver(([entry]) => setWidth(entry.contentRect.width));
12
- ro.observe(el);
13
- return () => ro.disconnect();
14
- }, [ref]);
15
- return width;
16
- }
17
-
18
- // src/lib/avl-elk-layout.ts
19
- function stateWidth(transitionCount) {
20
- if (transitionCount >= 5) return 160;
21
- if (transitionCount >= 3) return 130;
22
- return 110;
23
- }
24
- var STATE_H = 40;
25
- var elk;
26
- function loadElk() {
27
- elk ??= import('elkjs/lib/elk.bundled.js').then(({ default: Elk }) => new Elk());
28
- return elk;
29
- }
30
- function flowOrder(data) {
31
- const byName = new Map(data.states.map((s) => [s.name, s]));
32
- const seen = /* @__PURE__ */ new Set();
33
- const queue = data.states.filter((s) => s.isInitial).map((s) => s.name);
34
- const out = [];
35
- while (queue.length > 0) {
36
- const name = queue.shift();
37
- if (seen.has(name)) continue;
38
- const state = byName.get(name);
39
- if (!state) continue;
40
- seen.add(name);
41
- out.push(state);
42
- for (const t of data.transitions) if (t.from === name && !seen.has(t.to)) queue.push(t.to);
43
- }
44
- for (const s of data.states) if (!seen.has(s.name)) out.push(s);
45
- return out;
46
- }
47
- async function computeTraitLayout(data, metrics) {
48
- const transitionCounts = {};
49
- for (const s of data.states) transitionCounts[s.name] = 0;
50
- for (const t of data.transitions) {
51
- transitionCounts[t.from] = (transitionCounts[t.from] ?? 0) + 1;
52
- transitionCounts[t.to] = (transitionCounts[t.to] ?? 0) + 1;
53
- }
54
- const maxTC = Math.max(...Object.values(transitionCounts), 0);
55
- const LABEL_BASE_W = 100;
56
- const LABEL_H = 44;
57
- const states = metrics ? flowOrder(data) : data.states;
58
- const elkGraph = {
59
- id: "root",
60
- layoutOptions: {
61
- "elk.algorithm": "layered",
62
- "elk.direction": metrics?.direction === "rtl" ? "LEFT" : "RIGHT",
63
- "elk.spacing.nodeNode": String(metrics?.nodeSpacing ?? 80),
64
- "elk.spacing.edgeNode": "30",
65
- "elk.spacing.edgeEdge": "24",
66
- "elk.spacing.edgeLabel": "10",
67
- "elk.spacing.labelLabel": "8",
68
- "elk.layered.spacing.nodeNodeBetweenLayers": String(metrics?.layerSpacing ?? 100),
69
- ...metrics?.orthogonal ? { "elk.edgeRouting": "ORTHOGONAL" } : {},
70
- ...metrics ? { "elk.layered.considerModelOrder.strategy": "NODES_AND_EDGES", "elk.layered.cycleBreaking.strategy": "MODEL_ORDER" } : {},
71
- "elk.edgeLabels.placement": "CENTER",
72
- "elk.layered.mergeEdges": "false",
73
- "elk.layered.nodePlacement.strategy": "NETWORK_SIMPLEX"
74
- },
75
- children: states.map((s) => {
76
- const tc = transitionCounts[s.name] ?? 0;
77
- const size = metrics ? metrics.nodeSize(s.name) : { width: stateWidth(tc), height: STATE_H };
78
- return {
79
- id: s.name,
80
- width: size.width,
81
- height: size.height,
82
- labels: [{ text: s.name, width: size.width - 10, height: 14 }],
83
- ...metrics && s.isInitial ? { layoutOptions: { "elk.layered.layering.layerConstraint": "FIRST" } } : {}
84
- };
85
- }),
86
- edges: data.transitions.map((t, i) => {
87
- const textW = Math.max(t.event.length * 8, 60);
88
- const iconsW = Math.min(t.effects.length, 6) * 28 + 20;
89
- const label = metrics ? metrics.labelSize(t) : { width: Math.max(textW, iconsW, LABEL_BASE_W), height: LABEL_H };
90
- return {
91
- id: `e${i}`,
92
- sources: [t.from],
93
- targets: [t.to],
94
- labels: [{
95
- text: t.event,
96
- width: label.width,
97
- height: label.height,
98
- ...metrics ? { layoutOptions: { "elk.edgeLabels.inline": "true" } } : {}
99
- }]
100
- };
101
- })
102
- };
103
- const layout = await (await loadElk()).layout(elkGraph);
104
- const layoutChildren = layout.children ?? [];
105
- const layoutEdges = layout.edges ?? [];
106
- const nodeXMap = {};
107
- for (const n of layoutChildren) {
108
- nodeXMap[n.id] = n.x ?? 0;
109
- }
110
- const nodes = layoutChildren.map((n) => {
111
- const name = n.id;
112
- const stateInfo = data.states.find((s) => s.name === name);
113
- const tc = transitionCounts[name] ?? 0;
114
- return {
115
- id: name,
116
- x: n.x ?? 0,
117
- y: n.y ?? 0,
118
- width: n.width ?? 110,
119
- height: n.height ?? STATE_H,
120
- isInitial: stateInfo?.isInitial ?? void 0,
121
- isTerminal: stateInfo?.isTerminal ?? void 0,
122
- role: getStateRole(stateInfo?.isInitial ?? void 0, stateInfo?.isTerminal ?? void 0, tc, maxTC),
123
- transitionCount: tc
124
- };
125
- });
126
- const edges = layoutEdges.map((e, i) => {
127
- const t = data.transitions[i];
128
- const labels = e.labels ?? [];
129
- const label = labels[0];
130
- const sections = e.sections ?? [];
131
- const points = [];
132
- for (const section of sections) {
133
- const bendPoints = section.bendPoints ?? [];
134
- points.push({ x: section.startPoint.x, y: section.startPoint.y });
135
- for (const bp of bendPoints) points.push({ x: bp.x, y: bp.y });
136
- points.push({ x: section.endPoint.x, y: section.endPoint.y });
137
- }
138
- const isSelf = t.from === t.to;
139
- const toX = nodeXMap[t.to] ?? 0;
140
- const fromX = nodeXMap[t.from] ?? 0;
141
- const isBackward = !isSelf && (metrics?.direction === "rtl" ? toX >= fromX : toX <= fromX);
142
- const lw = label?.width ?? LABEL_BASE_W;
143
- const lh = label?.height ?? LABEL_H;
144
- return {
145
- id: e.id,
146
- from: t.from,
147
- to: t.to,
148
- event: t.event,
149
- effects: t.effects,
150
- guard: !!t.guard,
151
- guardExpr: typeof t.guard === "string" ? t.guard : void 0,
152
- index: t.index,
153
- labelX: label?.x ?? 0,
154
- labelY: label?.y ?? 0,
155
- labelW: lw,
156
- labelH: lh,
157
- points,
158
- isSelf,
159
- isBackward
160
- };
161
- });
162
- return {
163
- nodes,
164
- edges,
165
- width: layout.width ?? 600,
166
- height: layout.height ?? 400
167
- };
168
- }
169
- function roundedEdgePath(points, radius) {
170
- if (points.length < 3) return edgePath(points);
171
- let d = `M ${points[0].x},${points[0].y}`;
172
- for (let i = 1; i < points.length - 1; i++) {
173
- const prev = points[i - 1];
174
- const cur = points[i];
175
- const next = points[i + 1];
176
- const inLen = Math.hypot(cur.x - prev.x, cur.y - prev.y);
177
- const outLen = Math.hypot(next.x - cur.x, next.y - cur.y);
178
- const r = Math.min(radius, inLen / 2, outLen / 2);
179
- if (r <= 0) {
180
- d += ` L ${cur.x},${cur.y}`;
181
- continue;
182
- }
183
- const ax = cur.x - (cur.x - prev.x) / inLen * r;
184
- const ay = cur.y - (cur.y - prev.y) / inLen * r;
185
- const bx = cur.x + (next.x - cur.x) / outLen * r;
186
- const by = cur.y + (next.y - cur.y) / outLen * r;
187
- d += ` L ${ax},${ay} Q ${cur.x},${cur.y} ${bx},${by}`;
188
- }
189
- const last = points[points.length - 1];
190
- return `${d} L ${last.x},${last.y}`;
191
- }
192
- function edgePath(points) {
193
- if (points.length === 0) return "";
194
- let d = `M ${points[0].x},${points[0].y}`;
195
- for (let i = 1; i < points.length; i++) d += ` L ${points[i].x},${points[i].y}`;
196
- return d;
197
- }
198
-
199
- export { computeTraitLayout, edgePath, roundedEdgePath, useContainerWidth };
@@ -1,69 +0,0 @@
1
- import { createContext, useMemo, useContext } from 'react';
2
- import { createLogger } from '@almadar/logger';
3
- import { jsx } from 'react/jsx-runtime';
4
-
5
- var log = createLogger("almadar:ui:trait-provider");
6
- var TraitContext = createContext(null);
7
- function TraitProvider({
8
- traits: traitBindings,
9
- children
10
- }) {
11
- const traitInstances = useMemo(() => {
12
- const map = /* @__PURE__ */ new Map();
13
- for (const binding of traitBindings) {
14
- const trait = binding.trait;
15
- const initialState = trait.states.find((s) => s.isInitial);
16
- const stateName = initialState?.name || trait.states[0]?.name || "idle";
17
- const instance = {
18
- name: trait.name,
19
- currentState: stateName,
20
- availableEvents: trait.transitions.filter((t) => t.from === stateName).map((t) => t.event),
21
- dispatch: (eventKey, payload) => {
22
- log.debug("Dispatch", () => ({
23
- trait: trait.name,
24
- event: eventKey,
25
- payloadKeys: payload ? Object.keys(payload) : []
26
- }));
27
- },
28
- canDispatch: (eventKey) => {
29
- return trait.transitions.some(
30
- (t) => t.from === stateName && t.event === eventKey
31
- );
32
- },
33
- trait
34
- };
35
- map.set(trait.name, instance);
36
- }
37
- return map;
38
- }, [traitBindings]);
39
- const contextValue = useMemo(() => {
40
- return {
41
- traits: traitInstances,
42
- getTrait: (name) => traitInstances.get(name),
43
- dispatchToTrait: (traitName, eventKey, payload) => {
44
- const instance = traitInstances.get(traitName);
45
- if (instance) {
46
- instance.dispatch(eventKey, payload);
47
- }
48
- },
49
- canDispatch: (traitName, eventKey) => {
50
- const instance = traitInstances.get(traitName);
51
- return instance?.canDispatch(eventKey) || false;
52
- }
53
- };
54
- }, [traitInstances]);
55
- return /* @__PURE__ */ jsx(TraitContext.Provider, { value: contextValue, children });
56
- }
57
- function useTraitContext() {
58
- const context = useContext(TraitContext);
59
- if (!context) {
60
- throw new Error("useTraitContext must be used within a TraitProvider");
61
- }
62
- return context;
63
- }
64
- function useTrait(traitName) {
65
- const context = useTraitContext();
66
- return context.getTrait(traitName);
67
- }
68
-
69
- export { TraitContext, TraitProvider, useTrait, useTraitContext };
@@ -1,367 +0,0 @@
1
- import { createLocalFold, src_exports, createTickDriver, runTickFrame, mirrorResponse, traceResponse } from './chunk-6P3MWXYE.js';
2
- import './chunk-DYEKAA4L.js';
3
- import { presenceGroup, getTraitPresence } from './chunk-MBQAMMSE.js';
4
- import './chunk-G7DCAZ22.js';
5
- import { TraitMountError } from '@almadar/shell-runtime/client/trait-mount-error';
6
- import { createLogger } from '@almadar/logger';
7
-
8
- var log = createLogger("almadar:ui:kernel-client");
9
- var defaultFrameScheduler = (callback) => {
10
- if (typeof requestAnimationFrame === "function") {
11
- const handle = requestAnimationFrame(callback);
12
- return () => cancelAnimationFrame(handle);
13
- }
14
- const timer = setTimeout(() => callback(performance.now()), 16);
15
- return () => clearTimeout(timer);
16
- };
17
- function parse(text) {
18
- return JSON.parse(text);
19
- }
20
- function payloadJson(payload) {
21
- return payload === void 0 ? void 0 : JSON.stringify(payload);
22
- }
23
- function createKernelClient(opts) {
24
- const { store, traitIndex, transport } = opts;
25
- let loaded;
26
- const configs = /* @__PURE__ */ new Map();
27
- const ready = opts.instance.then((k) => {
28
- loaded = k;
29
- return k;
30
- });
31
- ready.catch(() => void 0);
32
- const visibleGroups = [...traitIndex.byName].filter(([, entry]) => (entry.irTrait.ticks ?? []).some((tick) => tick.whileVisible === true)).map(([name]) => [name, presenceGroup(traitIndex, name)]);
33
- let queueTail = Promise.resolve();
34
- const enqueue = (run) => {
35
- const result = queueTail.then(run);
36
- queueTail = result.then(() => void 0, () => void 0);
37
- return result;
38
- };
39
- let progressTail = Promise.resolve();
40
- let scopeTail = Promise.resolve();
41
- const declareScope = (apply) => {
42
- const applied = scopeTail.then(() => ready).then(apply);
43
- scopeTail = applied.then(() => void 0, () => void 0);
44
- return applied;
45
- };
46
- const frameKeyOf = (trait) => traitIndex.byName.get(trait)?.frameKey;
47
- const mirror = (response) => mirrorResponse(store, frameKeyOf, response);
48
- const trace = (response) => traceResponse(opts.observer, response);
49
- const localFold = createLocalFold({ store, frameKeyOf, ...opts.observer !== void 0 ? { observer: opts.observer } : {} });
50
- const lane = new src_exports.DispatchLane();
51
- const syncStates = (k, traits) => {
52
- for (const trait of traits) {
53
- const state = k.getState(trait);
54
- if (state !== "" && store.states.getState(trait)?.currentState !== state) store.states.seedState(trait, state);
55
- }
56
- };
57
- const restore = (k, wire, entityId) => {
58
- const snapshot = wire.snapshot;
59
- if (snapshot === null) return { clientEffects: [], clientEffectsByTrait: [] };
60
- const trait = wire.seed.trait;
61
- const repaint = parse(k.restore(trait, entityId, JSON.stringify(snapshot)));
62
- const frameKey = traitIndex.byName.get(trait)?.frameKey ?? trait;
63
- if (snapshot.state !== "") store.states.rollbackState(trait, snapshot.state, entityId, "server-rejected");
64
- const entity = snapshot.entity;
65
- if (entity !== null && typeof entity === "object" && !Array.isArray(entity)) store.frames.set(frameKey, { ...entity });
66
- else store.frames.delete(frameKey);
67
- mirror(repaint);
68
- store.notify();
69
- return { clientEffects: repaint.clientEffects ?? [], clientEffectsByTrait: repaint.clientEffectsByTrait ?? [] };
70
- };
71
- const planDispatch = (wire, carriesCircuitState) => {
72
- if (wire.serverLegs.length > 0) {
73
- return {
74
- awaiting: wire.awaiting,
75
- legs: wire.serverLegs.map(({ orbital, leg }) => ({
76
- orbital,
77
- leg,
78
- keepsState: carriesCircuitState || wire.mode === "hybridClientOnly",
79
- alreadyDelivered: wire.alreadyDelivered,
80
- foldSeeded: wire.foldSeeded,
81
- wires: [wire]
82
- }))
83
- };
84
- }
85
- if (!carriesCircuitState && wire.statefulLeg !== null) {
86
- return {
87
- awaiting: wire.statefulLeg.awaiting,
88
- legs: [{ orbital: wire.statefulLeg.orbital, leg: wire.statefulLeg.leg, keepsState: true, alreadyDelivered: wire.alreadyDelivered, foldSeeded: [], wires: [wire] }]
89
- };
90
- }
91
- return { legs: [], awaiting: [] };
92
- };
93
- const sendLegs = async (k, legs, request, flags, initial) => {
94
- const out = {
95
- response: initial,
96
- serverEffects: { clientEffects: [], clientEffectsByTrait: [] },
97
- success: true,
98
- rejections: [],
99
- foldedEmits: []
100
- };
101
- if (transport === void 0) return out;
102
- const queue = [...legs];
103
- for (let planned = queue.shift(); planned !== void 0; planned = queue.shift()) {
104
- const shaped = planned.keepsState ? planned.leg : JSON.parse(k.statefulLeg(JSON.stringify(planned.leg)));
105
- const sent = request.locale !== void 0 ? { ...shaped, locale: request.locale } : shaped;
106
- const base = k.foldBase(sent.entityByTrait !== void 0 ? JSON.stringify(sent.entityByTrait) : void 0, JSON.stringify(planned.foldSeeded), sent.entityId);
107
- let posted;
108
- try {
109
- posted = await transport.send(planned.orbital, sent);
110
- } catch (err) {
111
- if (flags.rollback || flags.mount) for (const wire of planned.wires) restore(k, wire, request.entityId);
112
- throw err instanceof Error ? err : new Error(String(err));
113
- }
114
- if (!posted.success) {
115
- const optimistic = planned.wires.filter((wire) => wire.mode === "runtimeOptimistic");
116
- if (flags.rollback && optimistic.length > 0) {
117
- return { ...out, response: posted, success: false, rolledBack: restore(k, optimistic[0], request.entityId) };
118
- }
119
- if (flags.mount) for (const wire of optimistic) restore(k, wire, request.entityId);
120
- }
121
- const applied = parse(k.applyServerResponse(JSON.stringify(posted), JSON.stringify(planned.alreadyDelivered), base));
122
- log.debug("fold", { orbital: planned.orbital, event: sent.event, posted: { success: posted.success, states: posted.states, emitted: posted.emittedEvents.map((e) => e.event) }, applied: applied.applied, cascadeEmitted: applied.response.emittedEvents.map((e) => e.event) });
123
- trace(applied.response);
124
- for (const routed of applied.serverLegs) {
125
- queue.push({ orbital: routed.orbital, leg: routed.leg, keepsState: true, alreadyDelivered: [], foldSeeded: [], wires: [] });
126
- }
127
- mirror(posted);
128
- mirror(applied.response);
129
- syncStates(k, [...Object.keys(posted.states), ...applied.applied.map(([trait]) => trait)]);
130
- store.notify();
131
- const fold = applied.response;
132
- const delivered = new Set(planned.alreadyDelivered.map(([trait, event]) => `${trait}\0${event}`));
133
- const serverEmits = posted.emittedEvents.filter((e) => !delivered.has(`${e.source?.trait ?? ""}\0${e.event}`));
134
- const effects = [...posted.clientEffects ?? [], ...fold.clientEffects ?? []];
135
- const byTrait = [...posted.clientEffectsByTrait ?? [], ...fold.clientEffectsByTrait ?? []];
136
- out.serverEffects.clientEffects?.push(...effects);
137
- out.serverEffects.clientEffectsByTrait?.push(...byTrait);
138
- out.foldedEmits.push(...serverEmits, ...fold.emittedEvents);
139
- out.response = {
140
- ...out.response,
141
- emittedEvents: [...out.response.emittedEvents, ...serverEmits, ...fold.emittedEvents],
142
- clientEffects: [...out.response.clientEffects ?? [], ...effects],
143
- clientEffectsByTrait: [...out.response.clientEffectsByTrait ?? [], ...byTrait],
144
- effectResults: [...out.response.effectResults ?? [], ...fold.effectResults ?? []]
145
- };
146
- out.success = out.success && posted.success;
147
- if (posted.error !== void 0) out.error = posted.error;
148
- out.rejections.push(...(fold.rejections ?? []).filter((r) => r.code === "effect-failed"));
149
- }
150
- return out;
151
- };
152
- const post = async (k, wire, request, flags) => {
153
- const topology = await opts.topology;
154
- const plan = planDispatch(wire, topology.carriesCircuitState);
155
- const awaiting = flags.awaiting ? plan.awaiting : [];
156
- store.awaiting.begin(awaiting);
157
- try {
158
- return await sendLegs(k, plan.legs, request, { rollback: flags.rollback, mount: false }, wire.response);
159
- } finally {
160
- store.awaiting.end(awaiting.map((a) => a.trait));
161
- }
162
- };
163
- const framePost = (k) => {
164
- if (transport === void 0) return void 0;
165
- return {
166
- postDispatch: async (wire) => (await post(k, wire, {}, { rollback: false, awaiting: false })).serverEffects,
167
- sendBrowserLeg: async (routed, frameResponse) => {
168
- const planned = { orbital: routed.orbital, leg: routed.leg, keepsState: true, alreadyDelivered: [], foldSeeded: [], wires: [] };
169
- return (await sendLegs(k, [planned], {}, { rollback: false, mount: false }, frameResponse)).serverEffects;
170
- }
171
- };
172
- };
173
- const tickPorts = (k) => ({
174
- kernel: {
175
- frame: (nowMs) => {
176
- const presence = getTraitPresence();
177
- for (const [traitName, group] of visibleGroups) k.setTraitOnScreen(traitName, presence.state(group) !== "off-screen");
178
- return k.frame(nowMs);
179
- },
180
- framePause: () => k.framePause(),
181
- frameResume: () => k.frameResume(),
182
- tickCount: () => k.tickCount()
183
- },
184
- lane,
185
- client: localFold,
186
- post: framePost(k)
187
- });
188
- const dispatchOne = async (request, hooks, progress) => {
189
- const traitName = request.targetTrait;
190
- if (traitName === void 0) throw new Error("dispatch requires request.targetTrait (the seed trait)");
191
- const k = await ready;
192
- if (request.runTick !== void 0) {
193
- const response = parse(k.tick(request.runTick.trait, request.runTick.tick, request.entityId));
194
- mirror(response);
195
- store.notify();
196
- return { response, mode: parse(k.dispatchMode(request.runTick.trait)) };
197
- }
198
- const wire = parse(k.dispatch(traitName, request.entityId, request.event, payloadJson(request.payload)));
199
- log.debug("dispatch", { trait: traitName, event: request.event, mode: wire.mode, legs: wire.serverLegs.length, success: wire.response.success, states: wire.response.states, effects: (wire.response.clientEffectsByTrait ?? []).map((e) => `${e.traitName}:${e.effect[0]}:${e.effect[1] ?? ""}`), emitted: wire.response.emittedEvents.map((e) => e.event) });
200
- trace(wire.response);
201
- mirror(wire.response);
202
- store.notify();
203
- if (transport === void 0 || wire.serverLegs.length === 0 && wire.statefulLeg === null) return { response: wire.response, mode: wire.mode };
204
- const painters = hooks.filter((h) => h.onLocal !== void 0);
205
- for (const h of painters) h.onLocal?.(wire.response);
206
- const posted = await post(k, wire, request, { rollback: true, awaiting: !progress });
207
- const localPainted = painters.length > 0;
208
- if (posted.rolledBack !== void 0) {
209
- return { response: posted.response, mode: wire.mode, ...localPainted ? { localPainted, serverEffects: posted.rolledBack } : {} };
210
- }
211
- return localPainted ? { response: posted.response, mode: wire.mode, localPainted, serverEffects: posted.serverEffects } : { response: posted.response, mode: wire.mode };
212
- };
213
- const client = {
214
- store,
215
- dispatch: (request, hooks) => enqueue(() => dispatchOne(request, hooks !== void 0 ? [hooks] : [], false)),
216
- dispatchProgress: (request) => {
217
- const outcome = progressTail.then(() => dispatchOne(request, [], true));
218
- progressTail = outcome.then(() => void 0, () => void 0);
219
- return outcome;
220
- },
221
- foldHostDispatch: (request, response) => enqueue(async () => {
222
- const k = await ready;
223
- if (request.targetTrait === void 0) throw new Error("foldHostDispatch requires request.targetTrait (the seed trait)");
224
- const mode = parse(k.dispatchMode(request.targetTrait));
225
- if (!response.success) return { response: { ...response, clientEffects: [], clientEffectsByTrait: [] }, mode };
226
- const applied = parse(k.applyServerResponse(JSON.stringify(response), "[]"));
227
- trace(applied.response);
228
- mirror(response);
229
- mirror(applied.response);
230
- syncStates(k, [...Object.keys(response.states), ...applied.applied.map(([trait]) => trait)]);
231
- store.notify();
232
- return {
233
- response: {
234
- ...response,
235
- clientEffects: [...response.clientEffects ?? [], ...applied.response.clientEffects ?? []],
236
- clientEffectsByTrait: [...response.clientEffectsByTrait ?? [], ...applied.response.clientEffectsByTrait ?? []]
237
- },
238
- mode
239
- };
240
- }),
241
- dispatchMount: (seeds, hooks, base) => enqueue(async () => {
242
- const k = await ready;
243
- await scopeTail;
244
- const baseRequest = base ?? {};
245
- k.mount(JSON.stringify(seeds.map((s) => s.trait)));
246
- const mount = parse(k.dispatchMount(JSON.stringify(seeds), baseRequest.entityId, payloadJson(baseRequest.payload)));
247
- if (mount.failed !== null) {
248
- const failed = mount.failed;
249
- throw new TraitMountError(failed.trait, failed.event, new Error(failed.message));
250
- }
251
- const wires = mount.seeds;
252
- for (const wire of wires) {
253
- log.debug("mount", { trait: wire.seed.trait, event: wire.seed.event, mode: wire.mode, legs: wire.serverLegs.length, success: wire.response.success });
254
- trace(wire.response);
255
- mirror(wire.response);
256
- }
257
- store.notify();
258
- for (const wire of wires) hooks?.onLocal?.(wire.seed.trait, wire.response);
259
- const outcome = {
260
- seeds: wires.map((w) => ({ trait: w.seed.trait, event: w.seed.event, mode: w.mode, local: w.response })),
261
- posted: false,
262
- success: true,
263
- rejections: [],
264
- serverEffects: { clientEffects: [], clientEffectsByTrait: [] },
265
- emittedEvents: []
266
- };
267
- if (transport === void 0) return outcome;
268
- const topology = await opts.topology;
269
- for (const group of topology.carriesCircuitState ? mount.legs : mount.statefulLegs) {
270
- const answered = wires.filter((w) => group.seeds.some((s) => s.trait === w.seed.trait && s.event === w.seed.event));
271
- const planned = { orbital: group.orbital, leg: group.leg, keepsState: true, alreadyDelivered: group.alreadyDelivered, foldSeeded: group.foldSeeded, wires: answered };
272
- store.awaiting.begin(group.awaiting);
273
- let posted;
274
- try {
275
- posted = await sendLegs(k, [planned], baseRequest, { rollback: false, mount: true }, answered[0]?.response ?? outcome.seeds[0].local);
276
- } finally {
277
- store.awaiting.end(group.awaiting.map((a) => a.trait));
278
- }
279
- outcome.posted = true;
280
- outcome.success = outcome.success && posted.success;
281
- if (posted.error !== void 0) outcome.error = posted.error;
282
- outcome.rejections.push(...posted.rejections);
283
- outcome.serverEffects.clientEffects?.push(...posted.serverEffects.clientEffects ?? []);
284
- outcome.serverEffects.clientEffectsByTrait?.push(...posted.serverEffects.clientEffectsByTrait ?? []);
285
- outcome.emittedEvents.push(...posted.foldedEmits);
286
- }
287
- return outcome;
288
- }),
289
- routeMounts: async (routePath) => parse((await ready).routeMounts(routePath)),
290
- remount: (traits) => declareScope((k) => k.remount(JSON.stringify(traits))),
291
- mount: (traits) => declareScope((k) => k.mount(JSON.stringify(traits))),
292
- unmount: (traits) => declareScope((k) => k.unmount(JSON.stringify(traits))),
293
- frame: async (nowMs) => {
294
- const k = await ready;
295
- await scopeTail;
296
- return (await runTickFrame(tickPorts(k), () => nowMs)).outcomes;
297
- },
298
- framePause: () => loaded?.framePause(),
299
- frameResume: () => loaded?.frameResume(),
300
- startFrames: (onFrame, schedule = defaultFrameScheduler) => {
301
- const presence = getTraitPresence();
302
- const releases = visibleGroups.map(([, group]) => presence.watch(group));
303
- let stopped = false;
304
- let latestMs = 0;
305
- let nextHandle = 0;
306
- const cancels = /* @__PURE__ */ new Map();
307
- const visibility = {
308
- hidden: () => document.visibilityState === "hidden",
309
- onChange: (listener) => {
310
- document.addEventListener("visibilitychange", listener);
311
- return () => document.removeEventListener("visibilitychange", listener);
312
- }
313
- };
314
- let driver;
315
- ready.then(async (k) => {
316
- await scopeTail;
317
- if (stopped) return;
318
- driver = createTickDriver({
319
- ...tickPorts(k),
320
- now: () => latestMs,
321
- requestFrame: (callback) => {
322
- nextHandle += 1;
323
- const handle = nextHandle;
324
- cancels.set(handle, schedule((nowMs) => {
325
- cancels.delete(handle);
326
- latestMs = nowMs;
327
- callback(nowMs);
328
- }));
329
- return handle;
330
- },
331
- cancelFrame: (handle) => {
332
- cancels.get(handle)?.();
333
- cancels.delete(handle);
334
- },
335
- visibility,
336
- onApplied: (applied) => {
337
- if (!stopped && applied.outcomes.length > 0) onFrame(applied.outcomes);
338
- },
339
- onError: (error) => log.error("kernel-frame-failed", { error: error.message })
340
- });
341
- driver.start();
342
- }).catch((error) => log.error("kernel-frame-failed", { error: error.message }));
343
- return () => {
344
- stopped = true;
345
- driver?.stop();
346
- releases.forEach((release) => release());
347
- };
348
- },
349
- traitState: (traitName) => loaded?.getState(traitName),
350
- traitConfig: (traitName) => {
351
- if (loaded === void 0) return void 0;
352
- const held = configs.get(traitName);
353
- if (held !== void 0) return held;
354
- const config = parse(loaded.traitConfig(traitName));
355
- configs.set(traitName, config);
356
- return config;
357
- },
358
- setViewer: async (user) => {
359
- const k = await ready;
360
- k.setCurrentUser(JSON.stringify(user));
361
- configs.clear();
362
- }
363
- };
364
- return client;
365
- }
366
-
367
- export { createKernelClient };