@xmachines/play-xstate 6.1.0 → 7.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.
- package/README.md +64 -12
- package/dist/capabilities.js +6 -2
- package/dist/capabilities.js.map +1 -1
- package/dist/define-player.js +8 -8
- package/dist/define-player.js.map +1 -1
- package/dist/errors.js +262 -259
- package/dist/errors.js.map +1 -1
- package/dist/index.js +4 -25
- package/dist/player-actor.js +318 -448
- package/dist/player-actor.js.map +1 -1
- package/dist/routing/build-url.js +74 -108
- package/dist/routing/build-url.js.map +1 -1
- package/dist/routing/derive-current-route.d.ts.map +1 -1
- package/dist/routing/derive-current-route.js +46 -67
- package/dist/routing/derive-current-route.js.map +1 -1
- package/dist/routing/derive-initial-route.js +53 -64
- package/dist/routing/derive-initial-route.js.map +1 -1
- package/dist/routing/derive-route.js +225 -242
- package/dist/routing/derive-route.js.map +1 -1
- package/dist/routing/format-play-route-transitions.js +192 -258
- package/dist/routing/format-play-route-transitions.js.map +1 -1
- package/dist/state-meta.d.ts +56 -25
- package/dist/state-meta.d.ts.map +1 -1
- package/dist/state-meta.js +164 -105
- package/dist/state-meta.js.map +1 -1
- package/dist/view/derive-current-view.d.ts +18 -10
- package/dist/view/derive-current-view.d.ts.map +1 -1
- package/dist/view/derive-current-view.js +243 -221
- package/dist/view/derive-current-view.js.map +1 -1
- package/dist/with-routing.js +94 -136
- package/dist/with-routing.js.map +1 -1
- package/dist/with-view.d.ts.map +1 -1
- package/dist/with-view.js +82 -152
- package/dist/with-view.js.map +1 -1
- package/package.json +19 -19
- package/dist/index.js.map +0 -1
- package/dist/routing/index.js +0 -14
- package/dist/routing/index.js.map +0 -1
- package/dist/routing/types.js +0 -2
- package/dist/routing/types.js.map +0 -1
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
|
@@ -1,235 +1,257 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { activeChildKeys, activeChildValue, branchOrder, flatStateMeta, warnStateIds } from "../state-meta.js";
|
|
2
2
|
import { createWarnOnce } from "@xmachines/core/utils";
|
|
3
|
-
import {
|
|
3
|
+
import { composePlayState, composeViews, toAtomState } from "@xmachines/play-view";
|
|
4
|
+
//#region packages/play-xstate/src/view/derive-current-view.ts
|
|
4
5
|
/**
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"is always projected under /context. Delete the field — it no longer filters anything.");
|
|
6
|
+
* The migration warning of the `contextProps` field, which 1.x removed. The code
|
|
7
|
+
* writes it one time for each spec. A meta.view object from a person passes around
|
|
8
|
+
* `typedSpec`, and a spec with the field therefore compiles without an error. But
|
|
9
|
+
* the meaning of the field inverted: it was an allowlist, or an empty filter that
|
|
10
|
+
* projected nothing, and the whole context is the value now. The key is the static
|
|
11
|
+
* meta.view object. Therefore the warning appears one time, and not for each
|
|
12
|
+
* snapshot.
|
|
13
|
+
*/
|
|
14
|
+
var warnOnce = createWarnOnce();
|
|
15
|
+
var warnRemovedContextProps = (viewMeta) => {
|
|
16
|
+
warnOnce(viewMeta, "[play-xstate] meta.view.contextProps has been removed: the whole machine context is always projected under /context. Delete the field — it no longer filters anything.");
|
|
17
17
|
};
|
|
18
18
|
/** Reads the `view` of one meta entry, when the entry holds a whole spec. */
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
: undefined;
|
|
23
|
-
return maybeView &&
|
|
24
|
-
typeof maybeView === "object" &&
|
|
25
|
-
"root" in maybeView &&
|
|
26
|
-
"elements" in maybeView
|
|
27
|
-
? maybeView
|
|
28
|
-
: null;
|
|
19
|
+
var viewOfEntry = (stateMeta) => {
|
|
20
|
+
const maybeView = stateMeta && typeof stateMeta === "object" ? stateMeta.view : void 0;
|
|
21
|
+
return maybeView && typeof maybeView === "object" && "root" in maybeView && "elements" in maybeView ? maybeView : null;
|
|
29
22
|
};
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Resolves the view of one active state node and its active descendants.
|
|
25
|
+
*
|
|
26
|
+
* A node whose view declares `outlets` composes: the tree holds the resolved view of
|
|
27
|
+
* each active child that an outlet shows. Any other node takes the deepest view below
|
|
28
|
+
* it. A parallel node tries its regions in {@link branchOrder}: the region of the routes
|
|
29
|
+
* first, so the view belongs to the region of the URL when that region resolves to a
|
|
30
|
+
* view, and to the first region that resolves to one otherwise.
|
|
31
|
+
*/
|
|
32
|
+
var resolveViewTree = (node, value) => {
|
|
33
|
+
const view = viewOfEntry(node.meta);
|
|
34
|
+
if (view && "contextProps" in view) warnRemovedContextProps(view);
|
|
35
|
+
if (view?.outlets) {
|
|
36
|
+
const { outlets } = view;
|
|
37
|
+
const children = [];
|
|
38
|
+
for (const key of activeChildKeys(value)) {
|
|
39
|
+
if (!outlets[Object.hasOwn(outlets, key) ? key : "*"]) continue;
|
|
40
|
+
const child = node.states?.[key];
|
|
41
|
+
if (!child) continue;
|
|
42
|
+
const tree = resolveViewTree(child, activeChildValue(value, key));
|
|
43
|
+
if (tree) children.push({
|
|
44
|
+
key,
|
|
45
|
+
tree
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
id: node.id,
|
|
50
|
+
view,
|
|
51
|
+
children
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
for (const key of branchOrder(node, value)) {
|
|
55
|
+
const child = node.states?.[key];
|
|
56
|
+
if (!child) continue;
|
|
57
|
+
const tree = resolveViewTree(child, activeChildValue(value, key));
|
|
58
|
+
if (tree) return tree;
|
|
59
|
+
}
|
|
60
|
+
return view ? {
|
|
61
|
+
id: node.id,
|
|
62
|
+
view,
|
|
63
|
+
children: []
|
|
64
|
+
} : null;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* The view of a snapshot that exposes no machine tree to walk, from its flat `getMeta()`
|
|
68
|
+
* record. The keys of the ancestors come first, so the LAST entry that holds a view is
|
|
69
|
+
* the deepest one. The route derivation reads the same record for such a snapshot, so
|
|
70
|
+
* the route and the view agree there too.
|
|
71
|
+
*
|
|
72
|
+
* The record names no state key, so the fallback composes nothing: it gives the deepest
|
|
73
|
+
* view alone, and it drops the `outlets` of that view, which no spec carries.
|
|
74
|
+
*/
|
|
75
|
+
var flatView = (snapshot) => {
|
|
76
|
+
const meta = flatStateMeta(snapshot);
|
|
77
|
+
if (!meta) return null;
|
|
78
|
+
let deepest = null;
|
|
79
|
+
for (const [id, stateMeta] of Object.entries(meta)) {
|
|
80
|
+
const found = viewOfEntry(stateMeta);
|
|
81
|
+
if (found) deepest = {
|
|
82
|
+
id,
|
|
83
|
+
view: found
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
if (!deepest) return null;
|
|
87
|
+
if ("contextProps" in deepest.view) warnRemovedContextProps(deepest.view);
|
|
88
|
+
const { outlets: _outlets, ...view } = deepest.view;
|
|
89
|
+
return {
|
|
90
|
+
id: deepest.id,
|
|
91
|
+
view
|
|
92
|
+
};
|
|
55
93
|
};
|
|
56
94
|
/**
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const children = [];
|
|
73
|
-
for (const key of keys) {
|
|
74
|
-
const child = node.states?.[key]; // nosemgrep: gitlab.eslint.detect-object-injection
|
|
75
|
-
if (!child)
|
|
76
|
-
continue;
|
|
77
|
-
const resolved = resolveViewTree(child, activeChildValue(value, key));
|
|
78
|
-
if (resolved)
|
|
79
|
-
children.push({ key, resolved });
|
|
80
|
-
// Without outlets, only the first child that resolves counts.
|
|
81
|
-
if (resolved && !view?.outlets)
|
|
82
|
-
break;
|
|
83
|
-
}
|
|
84
|
-
if (view?.outlets) {
|
|
85
|
-
return {
|
|
86
|
-
tree: {
|
|
87
|
-
id: node.id,
|
|
88
|
-
view,
|
|
89
|
-
children: children.map(({ key, resolved }) => ({ key, tree: resolved.tree })),
|
|
90
|
-
},
|
|
91
|
-
composes: true,
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
const deeper = children[0]?.resolved;
|
|
95
|
-
if (deeper)
|
|
96
|
-
return deeper;
|
|
97
|
-
return view ? { tree: { id: node.id, view, children: [] }, composes: false } : null;
|
|
95
|
+
* Collects each view that a layout can compose, keyed on the id of its state node.
|
|
96
|
+
*
|
|
97
|
+
* The key is the STATE, and not the view object: two states that use one spec constant
|
|
98
|
+
* are two pages of the layout, and they share its store as two distinct objects do.
|
|
99
|
+
*/
|
|
100
|
+
var collectViews = (node, into) => {
|
|
101
|
+
const view = viewOfEntry(node.meta);
|
|
102
|
+
if (view) into.set(node.id, view);
|
|
103
|
+
for (const [key, child] of Object.entries(node.states ?? {})) {
|
|
104
|
+
if (view?.outlets) {
|
|
105
|
+
const outletKey = Object.hasOwn(view.outlets, key) ? key : "*";
|
|
106
|
+
if (!view.outlets[outletKey]) continue;
|
|
107
|
+
}
|
|
108
|
+
collectViews(child, into);
|
|
109
|
+
}
|
|
98
110
|
};
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
+
var warnSharedKeysOnce = createWarnOnce();
|
|
112
|
+
/** Warns about the shared keys of each outermost layout at or below `node`. */
|
|
113
|
+
var warnLayoutsBelow = (node) => {
|
|
114
|
+
const view = viewOfEntry(node.meta);
|
|
115
|
+
if (!view?.outlets) {
|
|
116
|
+
for (const child of Object.values(node.states ?? {})) warnLayoutsBelow(child);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const views = /* @__PURE__ */ new Map();
|
|
120
|
+
collectViews(node, views);
|
|
121
|
+
const owners = /* @__PURE__ */ new Map();
|
|
122
|
+
for (const [id, composed] of views) {
|
|
123
|
+
let keys;
|
|
124
|
+
try {
|
|
125
|
+
keys = Object.keys(toAtomState(composed.state));
|
|
126
|
+
} catch {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
for (const key of keys) owners.set(key, [...owners.get(key) ?? [], id]);
|
|
130
|
+
}
|
|
131
|
+
const shared = [...owners].filter(([, ids]) => ids.length > 1);
|
|
132
|
+
if (shared.length === 0) return;
|
|
133
|
+
warnSharedKeysOnce(view, `[play-xstate] The views that the layout "${node.id}" composes share one store, and two or more of them declare the same state key: ` + shared.map(([key, ids]) => `"${key}" (${ids.join(", ")})`).join("; ") + `. They share one value. Give each view its own keys.`);
|
|
111
134
|
};
|
|
112
135
|
/**
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
136
|
+
* Warns about each authored state key that two views of one layout declare.
|
|
137
|
+
*
|
|
138
|
+
* The views that a layout composes share the store of the layout, because the `viewKey`
|
|
139
|
+
* is the id of the layout. Two pages that each declare `query` therefore share one value,
|
|
140
|
+
* and the second page finds the input of the first. The check runs one time for each
|
|
141
|
+
* machine, and it reads the state nodes only, so it also finds two pages that are never
|
|
142
|
+
* on the screen together.
|
|
143
|
+
*/
|
|
144
|
+
var checkedMachines = /* @__PURE__ */ new WeakSet();
|
|
145
|
+
var warnSharedStateKeys = (root) => {
|
|
146
|
+
if (checkedMachines.has(root)) return;
|
|
147
|
+
checkedMachines.add(root);
|
|
148
|
+
warnLayoutsBelow(root);
|
|
149
|
+
};
|
|
150
|
+
/** The token that closes the children of one view in the walk of a tree. */
|
|
151
|
+
var CLOSE = Symbol("close");
|
|
152
|
+
var stepTo = (step, token) => {
|
|
153
|
+
let next = step.next.get(token);
|
|
154
|
+
if (!next) {
|
|
155
|
+
next = { next: /* @__PURE__ */ new Map() };
|
|
156
|
+
step.next.set(token, next);
|
|
157
|
+
}
|
|
158
|
+
return next;
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* Walks the trie along a {@link ViewTree}: each view object, the id of its state, the key
|
|
162
|
+
* of each child, and a close token after the children. The tokens are the objects and the
|
|
163
|
+
* strings themselves, so two different trees never reach one step, and a walk builds no
|
|
164
|
+
* key and no array.
|
|
165
|
+
*/
|
|
166
|
+
var walkTrie = (step, tree) => {
|
|
167
|
+
let at = stepTo(stepTo(step, tree.view), tree.id);
|
|
168
|
+
for (const child of tree.children) at = walkTrie(stepTo(at, child.key), child.tree);
|
|
169
|
+
return stepTo(at, CLOSE);
|
|
170
|
+
};
|
|
171
|
+
var compositions = /* @__PURE__ */ new WeakMap();
|
|
172
|
+
var composeOnce = (root, tree) => {
|
|
173
|
+
let start = compositions.get(root);
|
|
174
|
+
if (!start) {
|
|
175
|
+
start = { next: /* @__PURE__ */ new Map() };
|
|
176
|
+
compositions.set(root, start);
|
|
177
|
+
}
|
|
178
|
+
const step = walkTrie(start, tree);
|
|
179
|
+
step.composed ??= composeViews(tree);
|
|
180
|
+
return step.composed;
|
|
141
181
|
};
|
|
142
182
|
/**
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
if (flatMeta && typeof flatMeta === "object") {
|
|
214
|
-
resolved = resolveViewMeta(flatMeta);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
if (!resolved) {
|
|
218
|
-
return null;
|
|
219
|
-
}
|
|
220
|
-
const { key: viewKey, view: viewMeta } = resolved;
|
|
221
|
-
if ("contextProps" in viewMeta) {
|
|
222
|
-
warnRemovedContextProps(viewMeta);
|
|
223
|
-
}
|
|
224
|
-
// Project the complete context under /context. composePlayState skips the
|
|
225
|
-
// projection, the authored state wins, and the code writes a warning in development,
|
|
226
|
-
// when spec.state declares a top-level "context" key already.
|
|
227
|
-
const authoredState = viewMeta.state !== undefined ? toAtomState(viewMeta.state) : undefined;
|
|
228
|
-
const state = composePlayState(authoredState, slice);
|
|
229
|
-
return {
|
|
230
|
-
...viewMeta,
|
|
231
|
-
viewKey,
|
|
232
|
-
...(state !== undefined && { state }),
|
|
233
|
-
};
|
|
183
|
+
* Derives the current view of the actor from the state metadata.
|
|
184
|
+
*
|
|
185
|
+
* The function always returns a **fresh** `PlaySpec` object. The `Object.is`
|
|
186
|
+
* equality test of the atom therefore stops no real change of the view.
|
|
187
|
+
* The `currentView` atom of `withView` decides if the actor emits that fresh object:
|
|
188
|
+
* it compares the object with the last spec of an emission, it reuses the reference
|
|
189
|
+
* of the composed `state` field when the value of the projection did not change, and
|
|
190
|
+
* it returns the previous spec when the view on the screen is the same — which the
|
|
191
|
+
* `Object.is` compare of the atom reads as no change, so it notifies no subscriber. A
|
|
192
|
+
* provider below the actor therefore mounts the UI again not on every event.
|
|
193
|
+
*
|
|
194
|
+
* ### The context projection
|
|
195
|
+
*
|
|
196
|
+
* The function projects the context of the machine into the `state` field of the
|
|
197
|
+
* derived spec, under the read-only `/context` subtree:
|
|
198
|
+
* `state: { ...meta.view.state, context: slice }`. The spec of the emission is
|
|
199
|
+
* therefore **consistent with itself**, and its `state` field describes the contents
|
|
200
|
+
* of the store correctly. `repeat.statePath: "/context/tasks"`,
|
|
201
|
+
* `visible: { $state: "/context/…" }`, and a `{ $state: "/context/…" }` prop all
|
|
202
|
+
* resolve through the ordinary state grammar. A tool such as `validateSpec` also
|
|
203
|
+
* passes on a derived view, with no special case. Validate the derived view, and not
|
|
204
|
+
* the raw `meta.view`.
|
|
205
|
+
*
|
|
206
|
+
* The store always holds the complete context. The function changes the authored
|
|
207
|
+
* `meta.view` object never.
|
|
208
|
+
*
|
|
209
|
+
* ### The viewKey
|
|
210
|
+
*
|
|
211
|
+
* The derived spec carries a `viewKey` field. Its value is the id of the state node
|
|
212
|
+
* whose view the walk selected: the outermost state that composes with `outlets`, or
|
|
213
|
+
* the state of the deepest view. A provider uses it as the key of its store lifecycle:
|
|
214
|
+
* a new key seeds the store again, and the same key refreshes `/context` in place. The
|
|
215
|
+
* value comes from the selected view, and not from "the id of the active state",
|
|
216
|
+
* because for a parallel machine the walk can select a view of a different region than
|
|
217
|
+
* the leaf of the routed branch.
|
|
218
|
+
*
|
|
219
|
+
* ### One rule
|
|
220
|
+
*
|
|
221
|
+
* One walk of the active state tree derives every view, and a machine with no
|
|
222
|
+
* `outlets` is its simple case: the deepest view wins. Declare `outlets` on a state to
|
|
223
|
+
* show its view beside the views of its children. A snapshot that exposes no machine
|
|
224
|
+
* tree falls back to its flat `getMeta()` record, as the route derivation does.
|
|
225
|
+
*
|
|
226
|
+
* @param snapshot - The current snapshot of the XState machine.
|
|
227
|
+
* @returns The derived `PlaySpec`, or `null` when the current state has no view metadata.
|
|
228
|
+
*/
|
|
229
|
+
var deriveCurrentView = (snapshot) => {
|
|
230
|
+
const root = snapshot?.machine?.root;
|
|
231
|
+
let id;
|
|
232
|
+
let view;
|
|
233
|
+
if (root) {
|
|
234
|
+
warnSharedStateKeys(root);
|
|
235
|
+
warnStateIds(root);
|
|
236
|
+
const tree = resolveViewTree(root, snapshot.value);
|
|
237
|
+
if (!tree) return null;
|
|
238
|
+
id = tree.id;
|
|
239
|
+
view = tree.view.outlets ? composeOnce(root, tree) : tree.view;
|
|
240
|
+
} else {
|
|
241
|
+
const flat = flatView(snapshot);
|
|
242
|
+
if (!flat) return null;
|
|
243
|
+
({id, view} = flat);
|
|
244
|
+
}
|
|
245
|
+
const slice = snapshot.context !== null && typeof snapshot.context === "object" ? snapshot.context : void 0;
|
|
246
|
+
const authoredState = view.state !== void 0 ? toAtomState(view.state) : void 0;
|
|
247
|
+
const state = composePlayState(authoredState, slice);
|
|
248
|
+
return {
|
|
249
|
+
...view,
|
|
250
|
+
viewKey: id,
|
|
251
|
+
...state !== void 0 && { state }
|
|
252
|
+
};
|
|
234
253
|
};
|
|
254
|
+
//#endregion
|
|
255
|
+
export { deriveCurrentView };
|
|
256
|
+
|
|
235
257
|
//# sourceMappingURL=derive-current-view.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"derive-current-view.js","sourceRoot":"","sources":["../../src/view/derive-current-view.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"derive-current-view.js","names":[],"sources":["../../src/view/derive-current-view.ts"],"sourcesContent":["/**\n * The view derivation — the meta.view half of the player pipeline.\n *\n * This file is beside src/routing/, so that player-actor.ts holds the actor\n * itself only: the atoms, the hooks, and the lifecycle. The walk shares\n * `branchOrder` with the route derivation, so a parallel node tries the region of its\n * URL first. When that region resolves to no view, the view comes from the next region\n * that resolves to one.\n */\nimport type { AnyMachineSnapshot } from \"xstate\";\nimport {\n\tcomposePlayState,\n\tcomposeViews,\n\ttoAtomState,\n\ttype ComposedView,\n\ttype PlaySpec,\n\ttype ViewMeta,\n\ttype ViewTree,\n\ttype ViewTreeChild,\n} from \"@xmachines/play-view\";\nimport { createWarnOnce } from \"@xmachines/core/utils\";\n\nimport {\n\tactiveChildKeys,\n\tactiveChildValue,\n\tbranchOrder,\n\tflatStateMeta,\n\twarnStateIds,\n\ttype BranchNode,\n} from \"../state-meta.js\";\n\n/**\n * The migration warning of the `contextProps` field, which 1.x removed. The code\n * writes it one time for each spec. A meta.view object from a person passes around\n * `typedSpec`, and a spec with the field therefore compiles without an error. But\n * the meaning of the field inverted: it was an allowlist, or an empty filter that\n * projected nothing, and the whole context is the value now. The key is the static\n * meta.view object. Therefore the warning appears one time, and not for each\n * snapshot.\n */\nconst warnOnce = createWarnOnce();\nconst warnRemovedContextProps = (viewMeta: object): void => {\n\twarnOnce(\n\t\tviewMeta,\n\t\t\"[play-xstate] meta.view.contextProps has been removed: the whole machine context \" +\n\t\t\t\"is always projected under /context. Delete the field — it no longer filters anything.\",\n\t);\n};\n\n/** Reads the `view` of one meta entry, when the entry holds a whole spec. */\nconst viewOfEntry = (stateMeta: unknown): ViewMeta | null => {\n\tconst maybeView =\n\t\tstateMeta && typeof stateMeta === \"object\"\n\t\t\t? (stateMeta as { view?: unknown }).view\n\t\t\t: undefined;\n\n\treturn maybeView &&\n\t\ttypeof maybeView === \"object\" &&\n\t\t\"root\" in (maybeView as object) &&\n\t\t\"elements\" in (maybeView as object)\n\t\t? (maybeView as ViewMeta)\n\t\t: null;\n};\n\n/**\n * Resolves the view of one active state node and its active descendants.\n *\n * A node whose view declares `outlets` composes: the tree holds the resolved view of\n * each active child that an outlet shows. Any other node takes the deepest view below\n * it. A parallel node tries its regions in {@link branchOrder}: the region of the routes\n * first, so the view belongs to the region of the URL when that region resolves to a\n * view, and to the first region that resolves to one otherwise.\n */\nconst resolveViewTree = (node: BranchNode, value: unknown): ViewTree | null => {\n\tconst view = viewOfEntry(node.meta);\n\tif (view && \"contextProps\" in view) {\n\t\twarnRemovedContextProps(view);\n\t}\n\n\tif (view?.outlets) {\n\t\tconst { outlets } = view;\n\t\tconst children: ViewTreeChild[] = [];\n\t\t// A view with outlets places its children in the order of the machine.\n\t\tfor (const key of activeChildKeys(value)) {\n\t\t\t// A child that no outlet shows stays out of the tree, so a change inside it\n\t\t\t// changes the composed view not.\n\t\t\tconst outletKey = Object.hasOwn(outlets, key) ? key : \"*\";\n\t\t\tif (!outlets[outletKey]) continue; // nosemgrep: gitlab.eslint.detect-object-injection\n\t\t\tconst child = node.states?.[key]; // nosemgrep: gitlab.eslint.detect-object-injection\n\t\t\tif (!child) continue;\n\t\t\tconst tree = resolveViewTree(child, activeChildValue(value, key));\n\t\t\tif (tree) children.push({ key, tree });\n\t\t}\n\t\treturn { id: node.id, view, children };\n\t}\n\n\tfor (const key of branchOrder(node, value)) {\n\t\tconst child = node.states?.[key]; // nosemgrep: gitlab.eslint.detect-object-injection\n\t\tif (!child) continue;\n\t\tconst tree = resolveViewTree(child, activeChildValue(value, key));\n\t\tif (tree) return tree;\n\t}\n\treturn view ? { id: node.id, view, children: [] } : null;\n};\n\n/**\n * The view of a snapshot that exposes no machine tree to walk, from its flat `getMeta()`\n * record. The keys of the ancestors come first, so the LAST entry that holds a view is\n * the deepest one. The route derivation reads the same record for such a snapshot, so\n * the route and the view agree there too.\n *\n * The record names no state key, so the fallback composes nothing: it gives the deepest\n * view alone, and it drops the `outlets` of that view, which no spec carries.\n */\nconst flatView = (snapshot: AnyMachineSnapshot): { id: string; view: ViewMeta } | null => {\n\tconst meta = flatStateMeta(snapshot);\n\tif (!meta) return null;\n\tlet deepest: { id: string; view: ViewMeta } | null = null;\n\tfor (const [id, stateMeta] of Object.entries(meta)) {\n\t\tconst found = viewOfEntry(stateMeta);\n\t\tif (found) deepest = { id, view: found };\n\t}\n\tif (!deepest) return null;\n\tif (\"contextProps\" in deepest.view) warnRemovedContextProps(deepest.view);\n\tconst { outlets: _outlets, ...view } = deepest.view;\n\treturn { id: deepest.id, view };\n};\n\n/**\n * Collects each view that a layout can compose, keyed on the id of its state node.\n *\n * The key is the STATE, and not the view object: two states that use one spec constant\n * are two pages of the layout, and they share its store as two distinct objects do.\n */\nconst collectViews = (node: BranchNode, into: Map<string, ViewMeta>): void => {\n\tconst view = viewOfEntry(node.meta);\n\tif (view) into.set(node.id, view);\n\tfor (const [key, child] of Object.entries(node.states ?? {})) {\n\t\tif (view?.outlets) {\n\t\t\tconst outletKey = Object.hasOwn(view.outlets, key) ? key : \"*\";\n\t\t\tif (!view.outlets[outletKey]) continue; // nosemgrep: gitlab.eslint.detect-object-injection\n\t\t}\n\t\tcollectViews(child, into);\n\t}\n};\n\n// The warning of the shared keys keeps its own memory of the views. With the memory of\n// `warnRemovedContextProps`, a layout that carries both faults showed one warning alone.\nconst warnSharedKeysOnce = createWarnOnce();\n\n/** Warns about the shared keys of each outermost layout at or below `node`. */\nconst warnLayoutsBelow = (node: BranchNode): void => {\n\tconst view = viewOfEntry(node.meta);\n\tif (!view?.outlets) {\n\t\tfor (const child of Object.values(node.states ?? {})) warnLayoutsBelow(child);\n\t\treturn;\n\t}\n\tconst views = new Map<string, ViewMeta>();\n\tcollectViews(node, views);\n\tconst owners = new Map<string, string[]>();\n\tfor (const [id, composed] of views) {\n\t\t// The check reads views that are not on the screen, and a warning must not break\n\t\t// the derivation. A `state` accessor can throw — the renderers contain that as a\n\t\t// failure of the view, when the view shows — so such a view is left out here.\n\t\tlet keys: string[];\n\t\ttry {\n\t\t\tkeys = Object.keys(toAtomState(composed.state));\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t\tfor (const key of keys) owners.set(key, [...(owners.get(key) ?? []), id]);\n\t}\n\tconst shared = [...owners].filter(([, ids]) => ids.length > 1);\n\tif (shared.length === 0) return;\n\twarnSharedKeysOnce(\n\t\tview,\n\t\t`[play-xstate] The views that the layout \"${node.id}\" composes share one store, and ` +\n\t\t\t`two or more of them declare the same state key: ` +\n\t\t\tshared.map(([key, ids]) => `\"${key}\" (${ids.join(\", \")})`).join(\"; \") +\n\t\t\t`. They share one value. Give each view its own keys.`,\n\t);\n};\n\n/**\n * Warns about each authored state key that two views of one layout declare.\n *\n * The views that a layout composes share the store of the layout, because the `viewKey`\n * is the id of the layout. Two pages that each declare `query` therefore share one value,\n * and the second page finds the input of the first. The check runs one time for each\n * machine, and it reads the state nodes only, so it also finds two pages that are never\n * on the screen together.\n */\nconst checkedMachines = new WeakSet<BranchNode>();\nconst warnSharedStateKeys = (root: BranchNode): void => {\n\tif (checkedMachines.has(root)) return;\n\tcheckedMachines.add(root);\n\twarnLayoutsBelow(root);\n};\n\n/** One step of the trie of kept compositions. */\ninterface CompositionStep {\n\treadonly next: Map<unknown, CompositionStep>;\n\tcomposed?: ComposedView;\n}\n\n/** The token that closes the children of one view in the walk of a tree. */\nconst CLOSE = Symbol(\"close\");\n\nconst stepTo = (step: CompositionStep, token: unknown): CompositionStep => {\n\tlet next = step.next.get(token);\n\tif (!next) {\n\t\tnext = { next: new Map() };\n\t\tstep.next.set(token, next);\n\t}\n\treturn next;\n};\n\n/**\n * Walks the trie along a {@link ViewTree}: each view object, the id of its state, the key\n * of each child, and a close token after the children. The tokens are the objects and the\n * strings themselves, so two different trees never reach one step, and a walk builds no\n * key and no array.\n */\nconst walkTrie = (step: CompositionStep, tree: ViewTree): CompositionStep => {\n\tlet at = stepTo(stepTo(step, tree.view), tree.id);\n\tfor (const child of tree.children) at = walkTrie(stepTo(at, child.key), child.tree);\n\treturn stepTo(at, CLOSE);\n};\n\n// One composition for each distinct tree of views of each machine. A machine has a finite\n// set of views and of such trees, and the cache goes with the machine.\n//\n// The cache is what keeps `elements` and `viewParts` the SAME objects from one snapshot\n// to the next: the emit gate of the actor compares them by reference, and a new object on\n// every context assign would render the whole screen again.\nconst compositions = new WeakMap<BranchNode, CompositionStep>();\n\nconst composeOnce = (root: BranchNode, tree: ViewTree): ComposedView => {\n\tlet start = compositions.get(root);\n\tif (!start) {\n\t\tstart = { next: new Map() };\n\t\tcompositions.set(root, start);\n\t}\n\tconst step = walkTrie(start, tree);\n\tstep.composed ??= composeViews(tree);\n\treturn step.composed;\n};\n\n/**\n * Derives the current view of the actor from the state metadata.\n *\n * The function always returns a **fresh** `PlaySpec` object. The `Object.is`\n * equality test of the atom therefore stops no real change of the view.\n * The `currentView` atom of `withView` decides if the actor emits that fresh object:\n * it compares the object with the last spec of an emission, it reuses the reference\n * of the composed `state` field when the value of the projection did not change, and\n * it returns the previous spec when the view on the screen is the same — which the\n * `Object.is` compare of the atom reads as no change, so it notifies no subscriber. A\n * provider below the actor therefore mounts the UI again not on every event.\n *\n * ### The context projection\n *\n * The function projects the context of the machine into the `state` field of the\n * derived spec, under the read-only `/context` subtree:\n * `state: { ...meta.view.state, context: slice }`. The spec of the emission is\n * therefore **consistent with itself**, and its `state` field describes the contents\n * of the store correctly. `repeat.statePath: \"/context/tasks\"`,\n * `visible: { $state: \"/context/…\" }`, and a `{ $state: \"/context/…\" }` prop all\n * resolve through the ordinary state grammar. A tool such as `validateSpec` also\n * passes on a derived view, with no special case. Validate the derived view, and not\n * the raw `meta.view`.\n *\n * The store always holds the complete context. The function changes the authored\n * `meta.view` object never.\n *\n * ### The viewKey\n *\n * The derived spec carries a `viewKey` field. Its value is the id of the state node\n * whose view the walk selected: the outermost state that composes with `outlets`, or\n * the state of the deepest view. A provider uses it as the key of its store lifecycle:\n * a new key seeds the store again, and the same key refreshes `/context` in place. The\n * value comes from the selected view, and not from \"the id of the active state\",\n * because for a parallel machine the walk can select a view of a different region than\n * the leaf of the routed branch.\n *\n * ### One rule\n *\n * One walk of the active state tree derives every view, and a machine with no\n * `outlets` is its simple case: the deepest view wins. Declare `outlets` on a state to\n * show its view beside the views of its children. A snapshot that exposes no machine\n * tree falls back to its flat `getMeta()` record, as the route derivation does.\n *\n * @param snapshot - The current snapshot of the XState machine.\n * @returns The derived `PlaySpec`, or `null` when the current state has no view metadata.\n */\nexport const deriveCurrentView = (snapshot: AnyMachineSnapshot): PlaySpec | null => {\n\tconst root = snapshot?.machine?.root as BranchNode | undefined;\n\tlet id: string;\n\tlet view: Omit<PlaySpec, \"viewKey\">;\n\tif (root) {\n\t\t// Two checks of the machine, each one time for each machine: the state keys that the\n\t\t// views of a layout share, and the ids of the states.\n\t\twarnSharedStateKeys(root);\n\t\twarnStateIds(root);\n\t\tconst tree = resolveViewTree(root, snapshot.value);\n\t\tif (!tree) return null;\n\t\tid = tree.id;\n\t\t// A tree without outlets is the view of one state, as the author wrote it.\n\t\tview = tree.view.outlets ? composeOnce(root, tree) : tree.view;\n\t} else {\n\t\tconst flat = flatView(snapshot);\n\t\tif (!flat) return null;\n\t\t({ id, view } = flat);\n\t}\n\n\t// Project the complete context under /context. composePlayState skips the\n\t// projection, the authored state wins, and the code writes a warning in development,\n\t// when spec.state declares a top-level \"context\" key already.\n\tconst slice =\n\t\tsnapshot.context !== null && typeof snapshot.context === \"object\"\n\t\t\t? (snapshot.context as Record<string, unknown>)\n\t\t\t: undefined;\n\tconst authoredState = view.state !== undefined ? toAtomState(view.state) : undefined;\n\tconst state = composePlayState(authoredState, slice);\n\n\treturn {\n\t\t...view,\n\t\tviewKey: id,\n\t\t...(state !== undefined && { state }),\n\t};\n};\n"],"mappings":"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"}
|