@almadar/ui 5.157.0 → 5.158.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/{EntityBindingContext-BfZGeDfX.d.cts → EntityBindingContext-Bn3ePJQC.d.cts} +6 -2
- package/dist/{EntityBindingContext-BfZGeDfX.d.ts → EntityBindingContext-Bn3ePJQC.d.ts} +6 -2
- package/dist/avl/index.cjs +244 -25
- package/dist/avl/index.js +244 -25
- package/dist/components/index.cjs +205 -13
- package/dist/components/index.d.cts +40 -6
- package/dist/components/index.d.ts +40 -6
- package/dist/components/index.js +205 -13
- package/dist/providers/index.cjs +212 -17
- package/dist/providers/index.d.cts +1 -1
- package/dist/providers/index.d.ts +1 -1
- package/dist/providers/index.js +212 -17
- package/dist/runtime/index.cjs +244 -25
- package/dist/runtime/index.d.cts +12 -3
- package/dist/runtime/index.d.ts +12 -3
- package/dist/runtime/index.js +244 -25
- package/package.json +4 -4
|
@@ -145,7 +145,7 @@ interface SendEventResult {
|
|
|
145
145
|
}
|
|
146
146
|
interface ServerBridgeContextValue {
|
|
147
147
|
connected: boolean;
|
|
148
|
-
sendEvent: (orbitalName: string, event: string, payload?: EventPayload) => Promise<SendEventResult>;
|
|
148
|
+
sendEvent: (orbitalName: string, event: string, payload?: EventPayload, tick?: string, sourceTrait?: string) => Promise<SendEventResult>;
|
|
149
149
|
}
|
|
150
150
|
/**
|
|
151
151
|
* Transport adapter for ServerBridgeProvider. Decouples the bridge's
|
|
@@ -168,8 +168,12 @@ interface ServerBridgeTransport {
|
|
|
168
168
|
* `clientId` (Almadar_Live_Push.md) is the per-tab id stamped on every
|
|
169
169
|
* dispatch so the server's push broadcast can exclude the origin tab
|
|
170
170
|
* (it already got this cascade in-response).
|
|
171
|
+
*
|
|
172
|
+
* `tick`/`sourceTrait` (T6, docs/Almadar_Tick_Loop.md §3a) mark a
|
|
173
|
+
* tick-originated latest-state broadcast: the server coalesces these
|
|
174
|
+
* newest-per-key and relays them to other tabs at snapshot rate.
|
|
171
175
|
*/
|
|
172
|
-
sendEvent: (orbitalName: string, event: string, payload?: EventPayload, clientId?: string) => Promise<OrbitalEventResponse>;
|
|
176
|
+
sendEvent: (orbitalName: string, event: string, payload?: EventPayload, clientId?: string, tick?: string, sourceTrait?: string) => Promise<OrbitalEventResponse>;
|
|
173
177
|
}
|
|
174
178
|
/**
|
|
175
179
|
* Access the server bridge. Returns a no-op stub when outside the provider.
|
|
@@ -145,7 +145,7 @@ interface SendEventResult {
|
|
|
145
145
|
}
|
|
146
146
|
interface ServerBridgeContextValue {
|
|
147
147
|
connected: boolean;
|
|
148
|
-
sendEvent: (orbitalName: string, event: string, payload?: EventPayload) => Promise<SendEventResult>;
|
|
148
|
+
sendEvent: (orbitalName: string, event: string, payload?: EventPayload, tick?: string, sourceTrait?: string) => Promise<SendEventResult>;
|
|
149
149
|
}
|
|
150
150
|
/**
|
|
151
151
|
* Transport adapter for ServerBridgeProvider. Decouples the bridge's
|
|
@@ -168,8 +168,12 @@ interface ServerBridgeTransport {
|
|
|
168
168
|
* `clientId` (Almadar_Live_Push.md) is the per-tab id stamped on every
|
|
169
169
|
* dispatch so the server's push broadcast can exclude the origin tab
|
|
170
170
|
* (it already got this cascade in-response).
|
|
171
|
+
*
|
|
172
|
+
* `tick`/`sourceTrait` (T6, docs/Almadar_Tick_Loop.md §3a) mark a
|
|
173
|
+
* tick-originated latest-state broadcast: the server coalesces these
|
|
174
|
+
* newest-per-key and relays them to other tabs at snapshot rate.
|
|
171
175
|
*/
|
|
172
|
-
sendEvent: (orbitalName: string, event: string, payload?: EventPayload, clientId?: string) => Promise<OrbitalEventResponse>;
|
|
176
|
+
sendEvent: (orbitalName: string, event: string, payload?: EventPayload, clientId?: string, tick?: string, sourceTrait?: string) => Promise<OrbitalEventResponse>;
|
|
173
177
|
}
|
|
174
178
|
/**
|
|
175
179
|
* Access the server bridge. Returns a no-op stub when outside the provider.
|
package/dist/avl/index.cjs
CHANGED
|
@@ -4664,11 +4664,32 @@ function isPlainObject(value) {
|
|
|
4664
4664
|
if (typeof value === "function") return false;
|
|
4665
4665
|
return true;
|
|
4666
4666
|
}
|
|
4667
|
+
function subtreeHasMarker(value) {
|
|
4668
|
+
const cached = markerPresenceCache.get(value);
|
|
4669
|
+
if (cached !== void 0) return cached;
|
|
4670
|
+
let found = false;
|
|
4671
|
+
const children = Array.isArray(value) ? value : Object.values(value);
|
|
4672
|
+
for (const child of children) {
|
|
4673
|
+
if (core.isRenderBindingMarker(child)) {
|
|
4674
|
+
found = true;
|
|
4675
|
+
break;
|
|
4676
|
+
}
|
|
4677
|
+
if (Array.isArray(child) || isPlainObject(child)) {
|
|
4678
|
+
if (subtreeHasMarker(child)) {
|
|
4679
|
+
found = true;
|
|
4680
|
+
break;
|
|
4681
|
+
}
|
|
4682
|
+
}
|
|
4683
|
+
}
|
|
4684
|
+
markerPresenceCache.set(value, found);
|
|
4685
|
+
return found;
|
|
4686
|
+
}
|
|
4667
4687
|
function walkValue(value, scopeTrait, entity, config, state) {
|
|
4668
4688
|
if (core.isRenderBindingMarker(value)) {
|
|
4669
4689
|
return { resolved: resolveMarkerExpression(value.expression, entity, config, state), changed: true };
|
|
4670
4690
|
}
|
|
4671
4691
|
if (Array.isArray(value)) {
|
|
4692
|
+
if (!subtreeHasMarker(value)) return { resolved: value, changed: false };
|
|
4672
4693
|
const out = [];
|
|
4673
4694
|
let changed = false;
|
|
4674
4695
|
for (const item of value) {
|
|
@@ -4686,6 +4707,7 @@ function walkValue(value, scopeTrait, entity, config, state) {
|
|
|
4686
4707
|
return changed ? { resolved: out, changed: true } : { resolved: value, changed: false };
|
|
4687
4708
|
}
|
|
4688
4709
|
if (isPlainObject(value)) {
|
|
4710
|
+
if (!subtreeHasMarker(value)) return { resolved: value, changed: false };
|
|
4689
4711
|
const sourceTrait = value._sourceTrait;
|
|
4690
4712
|
if (typeof sourceTrait === "string" && sourceTrait !== scopeTrait) {
|
|
4691
4713
|
return { resolved: value, changed: false };
|
|
@@ -4711,9 +4733,11 @@ function resolveRenderBindingMarkers(props, scopeTrait, entity, config, state) {
|
|
|
4711
4733
|
}
|
|
4712
4734
|
return changed ? out : props;
|
|
4713
4735
|
}
|
|
4736
|
+
var markerPresenceCache;
|
|
4714
4737
|
var init_resolve_render_bindings = __esm({
|
|
4715
4738
|
"lib/resolve-render-bindings.ts"() {
|
|
4716
4739
|
"use client";
|
|
4740
|
+
markerPresenceCache = /* @__PURE__ */ new WeakMap();
|
|
4717
4741
|
}
|
|
4718
4742
|
});
|
|
4719
4743
|
function getCurrentIconFamily() {
|
|
@@ -29707,7 +29731,7 @@ function fileIcon(name) {
|
|
|
29707
29731
|
return "file";
|
|
29708
29732
|
}
|
|
29709
29733
|
}
|
|
29710
|
-
var TreeNodeItem, FileTree;
|
|
29734
|
+
var TreeNodeItem, FlatTreeNodeItem, FileTree;
|
|
29711
29735
|
var init_FileTree = __esm({
|
|
29712
29736
|
"components/core/molecules/FileTree.tsx"() {
|
|
29713
29737
|
"use client";
|
|
@@ -29792,14 +29816,101 @@ var init_FileTree = __esm({
|
|
|
29792
29816
|
)) })
|
|
29793
29817
|
] });
|
|
29794
29818
|
};
|
|
29819
|
+
FlatTreeNodeItem = ({
|
|
29820
|
+
item,
|
|
29821
|
+
depth,
|
|
29822
|
+
indent,
|
|
29823
|
+
childrenByParent,
|
|
29824
|
+
onNodeSelect,
|
|
29825
|
+
defaultExpanded = false
|
|
29826
|
+
}) => {
|
|
29827
|
+
const [expanded, setExpanded] = React94.useState(defaultExpanded || depth < 1);
|
|
29828
|
+
const children = childrenByParent.get(item.id);
|
|
29829
|
+
const hasChildren = !!children && children.length > 0;
|
|
29830
|
+
const handleClick = React94.useCallback(() => {
|
|
29831
|
+
if (hasChildren) setExpanded((prev) => !prev);
|
|
29832
|
+
onNodeSelect?.(item.id);
|
|
29833
|
+
}, [hasChildren, item.id, onNodeSelect]);
|
|
29834
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
29835
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
29836
|
+
Box,
|
|
29837
|
+
{
|
|
29838
|
+
className: "flex items-center gap-1.5 py-0.5 px-2 cursor-pointer rounded-sm transition-colors hover:bg-muted",
|
|
29839
|
+
style: { paddingLeft: depth * indent + 8 },
|
|
29840
|
+
onClick: handleClick,
|
|
29841
|
+
role: "treeitem",
|
|
29842
|
+
"aria-expanded": hasChildren ? expanded : void 0,
|
|
29843
|
+
children: [
|
|
29844
|
+
hasChildren ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
29845
|
+
Icon,
|
|
29846
|
+
{
|
|
29847
|
+
name: expanded ? "chevron-down" : "chevron-right",
|
|
29848
|
+
size: "xs",
|
|
29849
|
+
className: "text-[var(--color-muted-foreground)] flex-shrink-0"
|
|
29850
|
+
}
|
|
29851
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(Box, { style: { width: 12, flexShrink: 0 } }),
|
|
29852
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
29853
|
+
Icon,
|
|
29854
|
+
{
|
|
29855
|
+
name: item.icon ?? (hasChildren ? expanded ? "folder-open" : "folder" : "file"),
|
|
29856
|
+
size: "xs",
|
|
29857
|
+
className: hasChildren ? "text-[var(--color-warning)]" : "text-[var(--color-muted-foreground)]"
|
|
29858
|
+
}
|
|
29859
|
+
),
|
|
29860
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "truncate font-mono text-xs", children: item.label })
|
|
29861
|
+
]
|
|
29862
|
+
}
|
|
29863
|
+
),
|
|
29864
|
+
hasChildren && expanded && /* @__PURE__ */ jsxRuntime.jsx(Box, { role: "group", children: children.map((child) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
29865
|
+
FlatTreeNodeItem,
|
|
29866
|
+
{
|
|
29867
|
+
item: child,
|
|
29868
|
+
depth: depth + 1,
|
|
29869
|
+
indent,
|
|
29870
|
+
childrenByParent,
|
|
29871
|
+
onNodeSelect
|
|
29872
|
+
},
|
|
29873
|
+
child.id
|
|
29874
|
+
)) })
|
|
29875
|
+
] });
|
|
29876
|
+
};
|
|
29795
29877
|
FileTree = ({
|
|
29796
29878
|
tree,
|
|
29879
|
+
items,
|
|
29797
29880
|
selectedPath,
|
|
29798
29881
|
onFileSelect,
|
|
29882
|
+
onNodeSelect,
|
|
29799
29883
|
className,
|
|
29800
29884
|
indent = 16
|
|
29801
29885
|
}) => {
|
|
29802
|
-
if (
|
|
29886
|
+
if (items) {
|
|
29887
|
+
if (items.length === 0) return null;
|
|
29888
|
+
const ids = new Set(items.map((node) => node.id));
|
|
29889
|
+
const childrenByParent = /* @__PURE__ */ new Map();
|
|
29890
|
+
const roots = [];
|
|
29891
|
+
for (const item of items) {
|
|
29892
|
+
if (item.parentId && ids.has(item.parentId)) {
|
|
29893
|
+
const siblings = childrenByParent.get(item.parentId);
|
|
29894
|
+
if (siblings) siblings.push(item);
|
|
29895
|
+
else childrenByParent.set(item.parentId, [item]);
|
|
29896
|
+
} else {
|
|
29897
|
+
roots.push(item);
|
|
29898
|
+
}
|
|
29899
|
+
}
|
|
29900
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: `py-1 overflow-y-auto ${className ?? ""}`, role: "tree", children: roots.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
29901
|
+
FlatTreeNodeItem,
|
|
29902
|
+
{
|
|
29903
|
+
item,
|
|
29904
|
+
depth: 0,
|
|
29905
|
+
indent,
|
|
29906
|
+
childrenByParent,
|
|
29907
|
+
onNodeSelect,
|
|
29908
|
+
defaultExpanded: true
|
|
29909
|
+
},
|
|
29910
|
+
item.id
|
|
29911
|
+
)) });
|
|
29912
|
+
}
|
|
29913
|
+
if (!tree || tree.length === 0) return null;
|
|
29803
29914
|
return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: `py-1 overflow-y-auto ${className ?? ""}`, role: "tree", children: tree.map((node) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
29804
29915
|
TreeNodeItem,
|
|
29805
29916
|
{
|
|
@@ -31194,7 +31305,7 @@ var init_debug = __esm({
|
|
|
31194
31305
|
logger.createLogger("almadar:ui:debug:game-state");
|
|
31195
31306
|
}
|
|
31196
31307
|
});
|
|
31197
|
-
var isRelationsDebugEnabled, RelationSelect;
|
|
31308
|
+
var isRelationsDebugEnabled, MANY_CARDINALITIES, RelationSelect;
|
|
31198
31309
|
var init_RelationSelect = __esm({
|
|
31199
31310
|
"components/core/molecules/RelationSelect.tsx"() {
|
|
31200
31311
|
"use client";
|
|
@@ -31208,6 +31319,11 @@ var init_RelationSelect = __esm({
|
|
|
31208
31319
|
init_Typography();
|
|
31209
31320
|
init_debug();
|
|
31210
31321
|
isRelationsDebugEnabled = () => isDebugEnabled();
|
|
31322
|
+
MANY_CARDINALITIES = [
|
|
31323
|
+
"many",
|
|
31324
|
+
"one-to-many",
|
|
31325
|
+
"many-to-many"
|
|
31326
|
+
];
|
|
31211
31327
|
RelationSelect = ({
|
|
31212
31328
|
value,
|
|
31213
31329
|
onChange,
|
|
@@ -32872,17 +32988,21 @@ var init_MathCanvas = __esm({
|
|
|
32872
32988
|
error
|
|
32873
32989
|
}) => {
|
|
32874
32990
|
const eventBus = useEventBus();
|
|
32991
|
+
const keyMapKey = keyMap ? JSON.stringify(keyMap) : null;
|
|
32992
|
+
const keyUpMapKey = keyUpMap ? JSON.stringify(keyUpMap) : null;
|
|
32993
|
+
const stableKeyMap = React94.useMemo(() => keyMap, [keyMapKey]);
|
|
32994
|
+
const stableKeyUpMap = React94.useMemo(() => keyUpMap, [keyUpMapKey]);
|
|
32875
32995
|
React94.useEffect(() => {
|
|
32876
|
-
if (!
|
|
32996
|
+
if (!stableKeyMap && !stableKeyUpMap) return;
|
|
32877
32997
|
const onDown = (e) => {
|
|
32878
|
-
const ev =
|
|
32998
|
+
const ev = stableKeyMap?.[e.code];
|
|
32879
32999
|
if (ev) {
|
|
32880
33000
|
eventBus.emit(`UI:${ev}`, {});
|
|
32881
33001
|
e.preventDefault();
|
|
32882
33002
|
}
|
|
32883
33003
|
};
|
|
32884
33004
|
const onUp = (e) => {
|
|
32885
|
-
const ev =
|
|
33005
|
+
const ev = stableKeyUpMap?.[e.code];
|
|
32886
33006
|
if (ev) eventBus.emit(`UI:${ev}`, {});
|
|
32887
33007
|
};
|
|
32888
33008
|
window.addEventListener("keydown", onDown);
|
|
@@ -32891,7 +33011,7 @@ var init_MathCanvas = __esm({
|
|
|
32891
33011
|
window.removeEventListener("keydown", onDown);
|
|
32892
33012
|
window.removeEventListener("keyup", onUp);
|
|
32893
33013
|
};
|
|
32894
|
-
}, [
|
|
33014
|
+
}, [stableKeyMap, stableKeyUpMap, eventBus]);
|
|
32895
33015
|
const derivedShapes = React94.useMemo(() => {
|
|
32896
33016
|
const out = [];
|
|
32897
33017
|
const margin = 24;
|
|
@@ -44919,6 +45039,9 @@ function determineInputType(field) {
|
|
|
44919
45039
|
if (field.type === "relation" || field.relation) {
|
|
44920
45040
|
return "relation";
|
|
44921
45041
|
}
|
|
45042
|
+
if (field.type === "array") {
|
|
45043
|
+
return "array";
|
|
45044
|
+
}
|
|
44922
45045
|
if (field.type === "enum" || field.values || getEnumOptions(field).length > 0) {
|
|
44923
45046
|
return "select";
|
|
44924
45047
|
}
|
|
@@ -44994,6 +45117,7 @@ var init_Form = __esm({
|
|
|
44994
45117
|
init_Typography();
|
|
44995
45118
|
init_Icon();
|
|
44996
45119
|
init_RelationSelect();
|
|
45120
|
+
init_TagInput();
|
|
44997
45121
|
init_UploadDropZone();
|
|
44998
45122
|
init_Alert();
|
|
44999
45123
|
init_useEventBus();
|
|
@@ -45073,7 +45197,7 @@ var init_Form = __esm({
|
|
|
45073
45197
|
values: "values" in f3 ? f3.values : void 0,
|
|
45074
45198
|
min: f3.min,
|
|
45075
45199
|
max: f3.max,
|
|
45076
|
-
relation: "relation" in f3 ? { entity: f3.relation.entity } : void 0
|
|
45200
|
+
relation: "relation" in f3 ? { entity: f3.relation.entity, cardinality: f3.relation.cardinality } : void 0
|
|
45077
45201
|
})
|
|
45078
45202
|
);
|
|
45079
45203
|
}, [entity, fields]);
|
|
@@ -45308,7 +45432,7 @@ var init_Form = __esm({
|
|
|
45308
45432
|
values: "values" in entityField ? entityField.values : void 0,
|
|
45309
45433
|
min: entityField.min,
|
|
45310
45434
|
max: entityField.max,
|
|
45311
|
-
relation: "relation" in entityField ? { entity: entityField.relation.entity } : void 0
|
|
45435
|
+
relation: "relation" in entityField ? { entity: entityField.relation.entity, cardinality: entityField.relation.cardinality } : void 0
|
|
45312
45436
|
};
|
|
45313
45437
|
}
|
|
45314
45438
|
return { name: field, type: "string" };
|
|
@@ -45420,6 +45544,22 @@ var init_Form = __esm({
|
|
|
45420
45544
|
case "relation": {
|
|
45421
45545
|
const relationOptions = relationsData[fieldName] || [];
|
|
45422
45546
|
const relationLoading = relationsLoading[fieldName] || false;
|
|
45547
|
+
if (field.relation?.cardinality !== void 0 && MANY_CARDINALITIES.includes(field.relation.cardinality)) {
|
|
45548
|
+
const selectedValues = Array.isArray(currentValue) ? currentValue.map((v) => String(v)) : [];
|
|
45549
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
45550
|
+
Select,
|
|
45551
|
+
{
|
|
45552
|
+
...commonProps,
|
|
45553
|
+
multiple: true,
|
|
45554
|
+
searchable: true,
|
|
45555
|
+
clearable: true,
|
|
45556
|
+
options: [...relationOptions],
|
|
45557
|
+
value: selectedValues,
|
|
45558
|
+
onValueChange: (value) => handleChange(fieldName, Array.isArray(value) ? value : [value]),
|
|
45559
|
+
placeholder: field.placeholder || `Select ${label}...`
|
|
45560
|
+
}
|
|
45561
|
+
);
|
|
45562
|
+
}
|
|
45423
45563
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
45424
45564
|
RelationSelect,
|
|
45425
45565
|
{
|
|
@@ -45434,6 +45574,18 @@ var init_Form = __esm({
|
|
|
45434
45574
|
}
|
|
45435
45575
|
);
|
|
45436
45576
|
}
|
|
45577
|
+
case "array": {
|
|
45578
|
+
const arrayValue = Array.isArray(currentValue) ? currentValue.map((v) => String(v)) : currentValue != null && currentValue !== "" ? [String(currentValue)] : [];
|
|
45579
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
45580
|
+
TagInput,
|
|
45581
|
+
{
|
|
45582
|
+
placeholder: field.placeholder,
|
|
45583
|
+
disabled: isLoading,
|
|
45584
|
+
value: arrayValue,
|
|
45585
|
+
onChange: (next) => handleChange(fieldName, [...next])
|
|
45586
|
+
}
|
|
45587
|
+
);
|
|
45588
|
+
}
|
|
45437
45589
|
case "number":
|
|
45438
45590
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
45439
45591
|
Input,
|
|
@@ -51002,7 +51154,10 @@ function enrichFormFields(fields, entityDef) {
|
|
|
51002
51154
|
enriched.values = entityField.enumValues;
|
|
51003
51155
|
}
|
|
51004
51156
|
if (entityField.relation) {
|
|
51005
|
-
enriched.relation =
|
|
51157
|
+
enriched.relation = {
|
|
51158
|
+
entity: entityField.relation.entity,
|
|
51159
|
+
cardinality: entityField.relation.cardinality
|
|
51160
|
+
};
|
|
51006
51161
|
}
|
|
51007
51162
|
return enriched;
|
|
51008
51163
|
}
|
|
@@ -51030,7 +51185,10 @@ function enrichFormFields(fields, entityDef) {
|
|
|
51030
51185
|
}
|
|
51031
51186
|
}
|
|
51032
51187
|
if (!obj.relation && entityField.relation) {
|
|
51033
|
-
enriched.relation =
|
|
51188
|
+
enriched.relation = {
|
|
51189
|
+
entity: entityField.relation.entity,
|
|
51190
|
+
cardinality: entityField.relation.cardinality
|
|
51191
|
+
};
|
|
51034
51192
|
}
|
|
51035
51193
|
return enriched;
|
|
51036
51194
|
}
|
|
@@ -51045,7 +51203,12 @@ function enrichDetailFields(fields, entityDef) {
|
|
|
51045
51203
|
const meta = { type: entityField.type };
|
|
51046
51204
|
const values = entityField.values ?? entityField.enumValues;
|
|
51047
51205
|
if (values && values.length > 0) meta.values = values;
|
|
51048
|
-
if (entityField.relation)
|
|
51206
|
+
if (entityField.relation) {
|
|
51207
|
+
meta.relation = {
|
|
51208
|
+
entity: entityField.relation.entity,
|
|
51209
|
+
cardinality: entityField.relation.cardinality
|
|
51210
|
+
};
|
|
51211
|
+
}
|
|
51049
51212
|
return meta;
|
|
51050
51213
|
};
|
|
51051
51214
|
return fields.map((field) => {
|
|
@@ -51599,6 +51762,32 @@ function isPlainConfigObject(value) {
|
|
|
51599
51762
|
const proto = Object.getPrototypeOf(value);
|
|
51600
51763
|
return proto === Object.prototype || proto === null;
|
|
51601
51764
|
}
|
|
51765
|
+
function subtreeHasTraitRef(value) {
|
|
51766
|
+
const cached = traitRefPresenceCache.get(value);
|
|
51767
|
+
if (cached !== void 0) return cached;
|
|
51768
|
+
let found = false;
|
|
51769
|
+
const children = Array.isArray(value) ? value : Object.values(value);
|
|
51770
|
+
for (const child of children) {
|
|
51771
|
+
if (typeof child === "string" && TRAIT_BINDING_RE.test(child)) {
|
|
51772
|
+
found = true;
|
|
51773
|
+
break;
|
|
51774
|
+
}
|
|
51775
|
+
if (core.isRenderBindingMarker(child)) continue;
|
|
51776
|
+
if (Array.isArray(child)) {
|
|
51777
|
+
if (subtreeHasTraitRef(child)) {
|
|
51778
|
+
found = true;
|
|
51779
|
+
break;
|
|
51780
|
+
}
|
|
51781
|
+
} else if (child !== null && typeof child === "object" && isPlainConfigObject(child)) {
|
|
51782
|
+
if (subtreeHasTraitRef(child)) {
|
|
51783
|
+
found = true;
|
|
51784
|
+
break;
|
|
51785
|
+
}
|
|
51786
|
+
}
|
|
51787
|
+
}
|
|
51788
|
+
traitRefPresenceCache.set(value, found);
|
|
51789
|
+
return found;
|
|
51790
|
+
}
|
|
51602
51791
|
function substituteTraitRefsDeep(value, pathKey) {
|
|
51603
51792
|
if (core.isRenderBindingMarker(value)) return value;
|
|
51604
51793
|
if (typeof value === "string") {
|
|
@@ -51613,11 +51802,13 @@ function substituteTraitRefsDeep(value, pathKey) {
|
|
|
51613
51802
|
return value;
|
|
51614
51803
|
}
|
|
51615
51804
|
if (Array.isArray(value)) {
|
|
51805
|
+
if (!subtreeHasTraitRef(value)) return value;
|
|
51616
51806
|
return value.map(
|
|
51617
51807
|
(item, i) => substituteTraitRefsDeep(item, `${pathKey}[${i}]`)
|
|
51618
51808
|
);
|
|
51619
51809
|
}
|
|
51620
51810
|
if (typeof value === "object" && isPlainConfigObject(value)) {
|
|
51811
|
+
if (!subtreeHasTraitRef(value)) return value;
|
|
51621
51812
|
const out = {};
|
|
51622
51813
|
for (const [k, v] of Object.entries(value)) {
|
|
51623
51814
|
out[k] = substituteTraitRefsDeep(v, `${pathKey}.${k}`);
|
|
@@ -51906,7 +52097,7 @@ function UISlotRenderer({
|
|
|
51906
52097
|
}
|
|
51907
52098
|
return wrapped;
|
|
51908
52099
|
}
|
|
51909
|
-
var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
|
|
52100
|
+
var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN, traitRefPresenceCache;
|
|
51910
52101
|
var init_UISlotRenderer = __esm({
|
|
51911
52102
|
"components/core/organisms/UISlotRenderer.tsx"() {
|
|
51912
52103
|
"use client";
|
|
@@ -51980,6 +52171,7 @@ var init_UISlotRenderer = __esm({
|
|
|
51980
52171
|
"alert",
|
|
51981
52172
|
"dialog"
|
|
51982
52173
|
]);
|
|
52174
|
+
traitRefPresenceCache = /* @__PURE__ */ new WeakMap();
|
|
51983
52175
|
UISlotRenderer.displayName = "UISlotRenderer";
|
|
51984
52176
|
}
|
|
51985
52177
|
});
|
|
@@ -55654,6 +55846,20 @@ function createClientEffectHandlers(options) {
|
|
|
55654
55846
|
};
|
|
55655
55847
|
}
|
|
55656
55848
|
|
|
55849
|
+
// lib/event-queue-coalesce.ts
|
|
55850
|
+
function enqueueEvent(queue, entry) {
|
|
55851
|
+
if (entry.tick !== void 0) {
|
|
55852
|
+
const pending = queue.find(
|
|
55853
|
+
(e) => e.tick !== void 0 && e.eventKey === entry.eventKey && e.targetTrait === entry.targetTrait
|
|
55854
|
+
);
|
|
55855
|
+
if (pending !== void 0) {
|
|
55856
|
+
pending.payload = entry.payload;
|
|
55857
|
+
return;
|
|
55858
|
+
}
|
|
55859
|
+
}
|
|
55860
|
+
queue.push(entry);
|
|
55861
|
+
}
|
|
55862
|
+
|
|
55657
55863
|
// hooks/useTraitStateMachine.ts
|
|
55658
55864
|
init_traitRegistry();
|
|
55659
55865
|
init_verificationRegistry();
|
|
@@ -56228,12 +56434,13 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
56228
56434
|
entityId
|
|
56229
56435
|
};
|
|
56230
56436
|
const emittedDuringExec = [];
|
|
56437
|
+
const tickName = flushEvent.startsWith("tick:") ? flushEvent.slice(5) : void 0;
|
|
56231
56438
|
const baseEmit = handlers.emit;
|
|
56232
56439
|
const trackingHandlers = {
|
|
56233
56440
|
...handlers,
|
|
56234
56441
|
emit: (event, eventPayload, source) => {
|
|
56235
56442
|
emittedDuringExec.push(event);
|
|
56236
|
-
baseEmit(event, eventPayload, source);
|
|
56443
|
+
baseEmit(event, eventPayload, tickName !== void 0 ? { ...source, tick: tickName } : source);
|
|
56237
56444
|
}
|
|
56238
56445
|
};
|
|
56239
56446
|
if (traitName === "Hero") {
|
|
@@ -56392,7 +56599,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
56392
56599
|
}
|
|
56393
56600
|
return () => scheduler.stopAll();
|
|
56394
56601
|
}, [traitBindings, runTickEffects, sharedGroups, sharedEntityStore, emitFromSharedWriter]);
|
|
56395
|
-
const processEventQueued = React94.useCallback(async (eventKey, payload, targetTrait) => {
|
|
56602
|
+
const processEventQueued = React94.useCallback(async (eventKey, payload, targetTrait, tick, sourceTrait) => {
|
|
56396
56603
|
const normalizedEvent = normalizeEventKey(eventKey);
|
|
56397
56604
|
const bindings = traitBindingsRef.current;
|
|
56398
56605
|
const currentManager = managerRef.current;
|
|
@@ -56581,7 +56788,11 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
56581
56788
|
if (orbital) dispatchedOrbitals.add(orbital);
|
|
56582
56789
|
}
|
|
56583
56790
|
const relayPayload = targetTrait !== void 0 ? { ...payload ?? {}, _targetTrait: targetTrait } : payload;
|
|
56584
|
-
|
|
56791
|
+
if (tick !== void 0) {
|
|
56792
|
+
void onEventProcessed(normalizedEvent, relayPayload, dispatchedOrbitals, tick, sourceTrait);
|
|
56793
|
+
} else {
|
|
56794
|
+
await onEventProcessed(normalizedEvent, relayPayload, dispatchedOrbitals);
|
|
56795
|
+
}
|
|
56585
56796
|
}
|
|
56586
56797
|
}, [entities, eventBus, sharedEntityStore]);
|
|
56587
56798
|
const drainEventQueue = React94.useCallback(async () => {
|
|
@@ -56590,14 +56801,14 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
56590
56801
|
try {
|
|
56591
56802
|
while (eventQueueRef.current.length > 0) {
|
|
56592
56803
|
const entry = eventQueueRef.current.shift();
|
|
56593
|
-
await processEventQueued(entry.eventKey, entry.payload, entry.targetTrait);
|
|
56804
|
+
await processEventQueued(entry.eventKey, entry.payload, entry.targetTrait, entry.tick, entry.sourceTrait);
|
|
56594
56805
|
}
|
|
56595
56806
|
} finally {
|
|
56596
56807
|
processingRef.current = false;
|
|
56597
56808
|
}
|
|
56598
56809
|
}, [processEventQueued]);
|
|
56599
|
-
const enqueueAndDrain = React94.useCallback((eventKey, payload, targetTrait) => {
|
|
56600
|
-
eventQueueRef.current
|
|
56810
|
+
const enqueueAndDrain = React94.useCallback((eventKey, payload, targetTrait, tick, sourceTrait) => {
|
|
56811
|
+
enqueueEvent(eventQueueRef.current, { eventKey, payload, targetTrait, tick, sourceTrait });
|
|
56601
56812
|
void drainEventQueue();
|
|
56602
56813
|
}, [drainEventQueue]);
|
|
56603
56814
|
React94.useCallback((eventKey, payload) => {
|
|
@@ -56650,7 +56861,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
56650
56861
|
crossTraitLog.debug("self:fire-server-cascade", { traitName, busKey: selfBusKey, eventKey });
|
|
56651
56862
|
}
|
|
56652
56863
|
crossTraitLog.debug("self:fire", { traitName, busKey: selfBusKey, eventKey });
|
|
56653
|
-
enqueueAndDrain(eventKey, event.payload, traitName);
|
|
56864
|
+
enqueueAndDrain(eventKey, event.payload, traitName, event.source?.tick, event.source?.trait);
|
|
56654
56865
|
});
|
|
56655
56866
|
unsubscribes.push(() => {
|
|
56656
56867
|
crossTraitLog.debug("self:unsubscribe", { traitName, busKey: selfBusKey, eventKey });
|
|
@@ -56668,7 +56879,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
56668
56879
|
const bareKey = `UI:${eventKey}`;
|
|
56669
56880
|
const unsub = eventBus.on(bareKey, (event) => {
|
|
56670
56881
|
crossTraitLog.debug("bare-cascade:fire", { bareKey, eventKey });
|
|
56671
|
-
enqueueAndDrain(eventKey, event.payload);
|
|
56882
|
+
enqueueAndDrain(eventKey, event.payload, void 0, event.source?.tick, event.source?.trait);
|
|
56672
56883
|
});
|
|
56673
56884
|
unsubscribes.push(() => {
|
|
56674
56885
|
crossTraitLog.debug("bare-cascade:unsubscribe", { bareKey, eventKey });
|
|
@@ -56702,7 +56913,9 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
56702
56913
|
enqueueAndDrain(
|
|
56703
56914
|
listen.triggers,
|
|
56704
56915
|
core.applyListenPayloadMapping(listen.payloadMapping, event.payload, evaluator.evaluateListenPayloadExpr),
|
|
56705
|
-
binding.trait.name
|
|
56916
|
+
binding.trait.name,
|
|
56917
|
+
event.source?.tick,
|
|
56918
|
+
event.source?.trait
|
|
56706
56919
|
);
|
|
56707
56920
|
});
|
|
56708
56921
|
unsubscribes.push(() => {
|
|
@@ -56894,7 +57107,7 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
|
|
|
56894
57107
|
[serverActiveTraits]
|
|
56895
57108
|
);
|
|
56896
57109
|
const uiSlots = context.useUISlots();
|
|
56897
|
-
const onEventProcessed = React94.useCallback(async (event, payload, dispatchedOrbitals) => {
|
|
57110
|
+
const onEventProcessed = React94.useCallback(async (event, payload, dispatchedOrbitals, tick, sourceTrait) => {
|
|
56898
57111
|
if (!bridge.connected || !orbitalNames?.length) return;
|
|
56899
57112
|
const targets = dispatchedOrbitals && dispatchedOrbitals.size > 0 ? orbitalNames.filter((n) => dispatchedOrbitals.has(n)) : orbitalNames;
|
|
56900
57113
|
xOrbitalLog.debug("TraitInitializer:fanout", () => ({
|
|
@@ -56904,6 +57117,10 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
|
|
|
56904
57117
|
dispatchedOrbitalsSize: dispatchedOrbitals?.size ?? 0
|
|
56905
57118
|
}));
|
|
56906
57119
|
for (const name of targets) {
|
|
57120
|
+
if (tick !== void 0) {
|
|
57121
|
+
void bridge.sendEvent(name, event, withActiveTraits(payload), tick, sourceTrait);
|
|
57122
|
+
continue;
|
|
57123
|
+
}
|
|
56907
57124
|
const { effects, meta } = await bridge.sendEvent(name, event, withActiveTraits(payload));
|
|
56908
57125
|
recordServerResponse(name, event, meta);
|
|
56909
57126
|
applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraitNames, onNavigateBack);
|
|
@@ -57379,11 +57596,13 @@ function BrowserPlayground({
|
|
|
57379
57596
|
unregister: async () => {
|
|
57380
57597
|
runtime.unregisterAll();
|
|
57381
57598
|
},
|
|
57382
|
-
sendEvent: async (orbitalName, event, payload) => {
|
|
57599
|
+
sendEvent: async (orbitalName, event, payload, _clientId, tick, sourceTrait) => {
|
|
57383
57600
|
await registrationReady;
|
|
57384
57601
|
return runtime.processOrbitalEvent(orbitalName, {
|
|
57385
57602
|
event,
|
|
57386
|
-
payload
|
|
57603
|
+
payload,
|
|
57604
|
+
tick,
|
|
57605
|
+
sourceTrait
|
|
57387
57606
|
// @almadar/runtime OrbitalEventResponse.clientEffects uses a wider ClientEffectTuple than
|
|
57388
57607
|
// ServerBridge's local definition — cast at this boundary (upstream fix queued).
|
|
57389
57608
|
});
|