@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.
Files changed (42) hide show
  1. package/README.md +64 -12
  2. package/dist/capabilities.js +6 -2
  3. package/dist/capabilities.js.map +1 -1
  4. package/dist/define-player.js +8 -8
  5. package/dist/define-player.js.map +1 -1
  6. package/dist/errors.js +262 -259
  7. package/dist/errors.js.map +1 -1
  8. package/dist/index.js +4 -25
  9. package/dist/player-actor.js +318 -448
  10. package/dist/player-actor.js.map +1 -1
  11. package/dist/routing/build-url.js +74 -108
  12. package/dist/routing/build-url.js.map +1 -1
  13. package/dist/routing/derive-current-route.d.ts.map +1 -1
  14. package/dist/routing/derive-current-route.js +46 -67
  15. package/dist/routing/derive-current-route.js.map +1 -1
  16. package/dist/routing/derive-initial-route.js +53 -64
  17. package/dist/routing/derive-initial-route.js.map +1 -1
  18. package/dist/routing/derive-route.js +225 -242
  19. package/dist/routing/derive-route.js.map +1 -1
  20. package/dist/routing/format-play-route-transitions.js +192 -258
  21. package/dist/routing/format-play-route-transitions.js.map +1 -1
  22. package/dist/state-meta.d.ts +56 -25
  23. package/dist/state-meta.d.ts.map +1 -1
  24. package/dist/state-meta.js +164 -105
  25. package/dist/state-meta.js.map +1 -1
  26. package/dist/view/derive-current-view.d.ts +18 -10
  27. package/dist/view/derive-current-view.d.ts.map +1 -1
  28. package/dist/view/derive-current-view.js +243 -221
  29. package/dist/view/derive-current-view.js.map +1 -1
  30. package/dist/with-routing.js +94 -136
  31. package/dist/with-routing.js.map +1 -1
  32. package/dist/with-view.d.ts.map +1 -1
  33. package/dist/with-view.js +82 -152
  34. package/dist/with-view.js.map +1 -1
  35. package/package.json +19 -19
  36. package/dist/index.js.map +0 -1
  37. package/dist/routing/index.js +0 -14
  38. package/dist/routing/index.js.map +0 -1
  39. package/dist/routing/types.js +0 -2
  40. package/dist/routing/types.js.map +0 -1
  41. package/dist/types.js +0 -2
  42. package/dist/types.js.map +0 -1
@@ -1,235 +1,257 @@
1
- import { composePlayState, composeViews, toAtomState, } from "@xmachines/play-view";
1
+ import { activeChildKeys, activeChildValue, branchOrder, flatStateMeta, warnStateIds } from "../state-meta.js";
2
2
  import { createWarnOnce } from "@xmachines/core/utils";
3
- import { activeChildKeys, activeChildValue, activeStateMeta, branchOrder, } from "../state-meta.js";
3
+ import { composePlayState, composeViews, toAtomState } from "@xmachines/play-view";
4
+ //#region packages/play-xstate/src/view/derive-current-view.ts
4
5
  /**
5
- * The migration warning of the `contextProps` field, which 1.x removed. The code
6
- * writes it one time for each spec. A meta.view object from a person passes around
7
- * `typedSpec`, and a spec with the field therefore compiles without an error. But
8
- * the meaning of the field inverted: it was an allowlist, or an empty filter that
9
- * projected nothing, and the whole context is the value now. The key is the static
10
- * meta.view object. Therefore the warning appears one time, and not for each
11
- * snapshot.
12
- */
13
- const warnOnce = createWarnOnce();
14
- const warnRemovedContextProps = (viewMeta) => {
15
- warnOnce(viewMeta, "[play-xstate] meta.view.contextProps has been removed: the whole machine context " +
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
- const viewOfEntry = (stateMeta) => {
20
- const maybeView = stateMeta && typeof stateMeta === "object"
21
- ? stateMeta.view
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
- const resolveViewMeta = (meta) => {
31
- // Take the LAST entry that holds a view, and not the first: snapshot.getMeta() puts
32
- // the keys of the ancestors first, so the last match is the meta.view of the deepest
33
- // active state. An ancestor hides it not.
34
- //
35
- // A walk from the end, and not a findLast or a mapped copy: this runs for each
36
- // snapshot. The walk stops at the first match, and `viewOfEntry` runs one time for
37
- // each entry that it visits — a `findLast` would run it a second time on the entry
38
- // that its own predicate had already resolved, and a `map()` would visit every entry
39
- // and allocate one object for each match before the search began.
40
- //
41
- // `toReversed()`, which copies, and not `reverse()`, which mutates: one spelling for
42
- // one operation, beside the `toSorted()` that `unicorn/no-array-sort` asks for.
43
- //
44
- // The copy costs about 10 ns for the handful of entries that a state path holds, and
45
- // `Object.entries` above it costs twenty times that. Measured, after a review called
46
- // this line a cost of a hot path: it is not one, at any rate of snapshots that an
47
- // actor produces.
48
- for (const [key, stateMeta] of Object.entries(meta).toReversed()) {
49
- const view = viewOfEntry(stateMeta);
50
- if (view) {
51
- return { key, view };
52
- }
53
- }
54
- return null;
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
- * Resolves the view of one active state node and its active descendants.
58
- *
59
- * A node whose view declares `outlets` composes: the tree holds the resolved view of
60
- * each active child. Any other node keeps the rule of a view without outlets: the
61
- * deepest view below it wins, and a parallel node follows the region of the URL when
62
- * that region resolves to a view, and its first region that resolves to one otherwise.
63
- */
64
- const resolveViewTree = (node, value) => {
65
- const view = viewOfEntry(node.meta);
66
- if (view && "contextProps" in view) {
67
- warnRemovedContextProps(view);
68
- }
69
- // A view with outlets places its children in the order of the machine. Any other
70
- // node tries the routed region first, so its view belongs to the region of the URL.
71
- const keys = view?.outlets ? activeChildKeys(value) : branchOrder(node, value);
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
- // Whether any state node of a machine declares `outlets`, for each root node. A
100
- // machine without outlets skips the walk of the view tree on every snapshot.
101
- const outletsByMachine = new WeakMap();
102
- const declaresOutlets = (node) => Boolean(viewOfEntry(node.meta)?.outlets) ||
103
- Object.values(node.states ?? {}).some(declaresOutlets);
104
- const machineDeclaresOutlets = (root) => {
105
- let declares = outletsByMachine.get(root);
106
- if (declares === undefined) {
107
- declares = declaresOutlets(root);
108
- outletsByMachine.set(root, declares);
109
- }
110
- return declares;
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
- * Derives the view of a machine that declares `outlets` somewhere. The walk of the view
114
- * tree alone decides that view: a composed view, the plain view that the walk resolved,
115
- * or `null` when no active state holds a view.
116
- *
117
- * The function returns `undefined` for a machine that declares no `outlets`. The caller
118
- * then derives the view as before, which keeps every such machine on the exact rule that
119
- * it had, the flat fallback of a parallel machine included.
120
- */
121
- const deriveTreeView = (snapshot) => {
122
- const root = snapshot?.machine?.root;
123
- if (!root || !machineDeclaresOutlets(root))
124
- return undefined;
125
- const resolved = resolveViewTree(root, snapshot.value);
126
- if (!resolved)
127
- return null;
128
- // A tree that does not compose is a view without outlets.
129
- if (!resolved.composes)
130
- return { viewKey: resolved.tree.id, view: resolved.tree.view };
131
- const composed = composeViews(resolved.tree);
132
- return {
133
- viewKey: resolved.tree.id,
134
- view: {
135
- root: composed.root,
136
- elements: composed.elements,
137
- ...(composed.state !== undefined && { state: composed.state }),
138
- viewParts: composed.viewParts,
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
- * Derives the current view of the actor from the state metadata.
144
- *
145
- * The function always returns a **fresh** `PlaySpec` object. The `Object.is`
146
- * equality test of the atom therefore stops no real change of the view.
147
- * The `currentView` atom of `withView` decides if the actor emits that fresh object:
148
- * it compares the object with the last spec of an emission, it reuses the reference
149
- * of the composed `state` field when the value of the projection did not change, and
150
- * it returns the previous spec when the view on the screen is the same — which the
151
- * `Object.is` compare of the atom reads as no change, so it notifies no subscriber. A
152
- * provider below the actor therefore mounts the UI again not on every event.
153
- *
154
- * ### The context projection
155
- *
156
- * The function projects the context of the machine into the `state` field of the
157
- * derived spec, under the read-only `/context` subtree:
158
- * `state: { ...meta.view.state, context: slice }`. The spec of the emission is
159
- * therefore **consistent with itself**, and its `state` field describes the contents
160
- * of the store correctly. `repeat.statePath: "/context/tasks"`,
161
- * `visible: { $state: "/context/…" }`, and a `{ $state: "/context/…" }` prop all
162
- * resolve through the ordinary state grammar. A tool such as `validateSpec` also
163
- * passes on a derived view, with no special case. Validate the derived view, and not
164
- * the raw `meta.view`.
165
- *
166
- * The store always holds the complete context. The function changes the authored
167
- * `meta.view` object never.
168
- *
169
- * ### The viewKey
170
- *
171
- * The derived spec carries a `viewKey` field. Its value is the key of the meta
172
- * record, which is the id of the state node, of the entry that this derivation
173
- * selected. A provider uses it as the key of its store lifecycle: a new key seeds
174
- * the store again, and the same key refreshes `/context` in place. The value comes
175
- * from the selected entry, and not from "the id of the active state", because for a
176
- * parallel machine the branch of the walk and the flat `getMeta()` fallback can
177
- * select a view of a different region than the leaf of the branch.
178
- *
179
- * @param snapshot - The current snapshot of the XState machine.
180
- * @returns The derived `PlaySpec`, or `null` when the current state has no view metadata.
181
- */
182
- export const deriveCurrentView = (snapshot) => {
183
- const slice = snapshot?.context !== null && typeof snapshot?.context === "object"
184
- ? snapshot.context
185
- : undefined;
186
- // A parent view with `outlets` shares the screen with the views of its substates.
187
- const treeView = deriveTreeView(snapshot);
188
- if (treeView !== undefined) {
189
- if (treeView === null)
190
- return null;
191
- const authoredState = treeView.view.state !== undefined ? toAtomState(treeView.view.state) : undefined;
192
- const state = composePlayState(authoredState, slice);
193
- return {
194
- ...treeView.view,
195
- viewKey: treeView.viewKey,
196
- ...(state !== undefined && { state }),
197
- };
198
- }
199
- // Follow the SAME single active branch as the route derivation. Therefore the view
200
- // on the screen of a parallel machine belongs to the region of the URL, when that
201
- // region declares a view.
202
- const meta = activeStateMeta(snapshot);
203
- if (!meta) {
204
- return null;
205
- }
206
- // A parallel machine can hold its routes and its views in DIFFERENT regions: one
207
- // region owns the navigation, and another region owns the shell. When the branch of
208
- // the walk declares no view, scan the flat record of every active region, where the
209
- // deepest entry wins, before the conclusion that no view is present.
210
- let resolved = resolveViewMeta(meta);
211
- if (!resolved) {
212
- const flatMeta = snapshot.getMeta();
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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"}