@xmachines/play-xstate 6.0.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 +87 -28
- package/dist/capabilities.d.ts +2 -2
- 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.d.ts.map +1 -1
- package/dist/routing/format-play-route-transitions.js +192 -257
- package/dist/routing/format-play-route-transitions.js.map +1 -1
- package/dist/state-meta.d.ts +84 -28
- package/dist/state-meta.d.ts.map +1 -1
- package/dist/state-meta.js +170 -72
- 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 +249 -128
- 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 +1 -3
- 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
package/dist/with-routing.js
CHANGED
|
@@ -1,140 +1,98 @@
|
|
|
1
|
-
import { createAtom } from "@xmachines/play-atom";
|
|
2
|
-
import { deriveCurrentRoute, deriveInitialRoute } from "./routing/index.js";
|
|
3
1
|
import { toError } from "./player-actor.js";
|
|
2
|
+
import { deriveRoute, isAbsoluteRoute } from "./routing/derive-route.js";
|
|
3
|
+
import { buildRouteUrl } from "./routing/build-url.js";
|
|
4
|
+
import { deriveCurrentRoute } from "./routing/derive-current-route.js";
|
|
5
|
+
import { deriveInitialRoute } from "./routing/derive-initial-route.js";
|
|
6
|
+
import { formatPlayRouteTransitions } from "./routing/format-play-route-transitions.js";
|
|
7
|
+
import { createAtom } from "@xmachines/play-atom";
|
|
8
|
+
//#region packages/play-xstate/src/with-routing.ts
|
|
4
9
|
/**
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
//
|
|
90
|
-
// The bridge is written against a notification for each change of the SNAPSHOT,
|
|
91
|
-
// and not for each change of the derived string, so the comparison has to answer
|
|
92
|
-
// "different" always. A consumer that reads the value pays nothing for it: the
|
|
93
|
-
// derivation still runs one time for each transition, and `state` itself compares
|
|
94
|
-
// with `Object.is`, so an event that XState ignores delivers the identical
|
|
95
|
-
// snapshot and propagates nothing.
|
|
96
|
-
compare: () => false,
|
|
97
|
-
});
|
|
98
|
-
/**
|
|
99
|
-
* The route of the initial state of the machine. The construction fixes it, and it
|
|
100
|
-
* never changes, also when the code restores the actor from a snapshot.
|
|
101
|
-
*
|
|
102
|
-
* A router bridge compares it with the browser URL, and it therefore separates a
|
|
103
|
-
* deep link (a URL that is not the initial one → the router wins) from a restore
|
|
104
|
-
* (the initial URL, and the actor at a different route from the restore → the actor
|
|
105
|
-
* wins).
|
|
106
|
-
*
|
|
107
|
-
* `deriveInitialRoute` derives the value statically from the machine definition,
|
|
108
|
-
* with the pure `initialTransition` helper of XState: the chain of the initial states
|
|
109
|
-
* and their `meta.route` templates are fixed at the moment of the machine
|
|
110
|
-
* definition, and the substitution of a `:param` uses the real initial context of
|
|
111
|
-
* the machine for the `input` of this actor. The code makes no second actor, and a
|
|
112
|
-
* snapshot of a restore changes the value never: it is always the **default**
|
|
113
|
-
* initial route of the machine.
|
|
114
|
-
*
|
|
115
|
-
* Without a snapshot of a restore, the pre-start snapshot of this actor IS that
|
|
116
|
-
* default initial state, so the code derives the route from it directly. A restore
|
|
117
|
-
* alone needs `initialTransition`, whose inert actor scope runs the initial
|
|
118
|
-
* transition two more times, one of them with an undefined `input`. That is a quirk
|
|
119
|
-
* of XState, and it costs too much for every other case.
|
|
120
|
-
*/
|
|
121
|
-
initialRoute = this.playerRestoredSnapshot === undefined
|
|
122
|
-
? deriveCurrentRoute(this.getSnapshot())
|
|
123
|
-
: deriveInitialRoute(this.playerMachine, this.playerInput);
|
|
124
|
-
}
|
|
125
|
-
return RoutableActor;
|
|
10
|
+
* Adds the routing capability to an actor class.
|
|
11
|
+
*
|
|
12
|
+
* The result satisfies `Routable`, so a router bridge accepts it. A class without this
|
|
13
|
+
* mixin exposes no `currentRoute`, and a bridge therefore refuses it when it compiles,
|
|
14
|
+
* rather than at the first navigation.
|
|
15
|
+
*
|
|
16
|
+
* The mixin declares NO constructor, and that is deliberate: TypeScript demands the
|
|
17
|
+
* signature `...args: any[]` from a mixin over a generic base (TS2545), and this
|
|
18
|
+
* workspace writes no `any`. A field initializer of a subclass runs after `super()`
|
|
19
|
+
* instead, and it reads the inputs that `PlayerActor` keeps for a capability.
|
|
20
|
+
*
|
|
21
|
+
* @param Base - The actor class to extend, normally `PlayerActor`.
|
|
22
|
+
*/
|
|
23
|
+
function withRouting(Base) {
|
|
24
|
+
class RoutableActor extends Base {
|
|
25
|
+
/**
|
|
26
|
+
* A computed atom. It derives the current URL path from the `meta.route`
|
|
27
|
+
* template of the active machine state and from the context of the actor.
|
|
28
|
+
*
|
|
29
|
+
* It returns `null` when the current state has no `meta.route` field, and also when
|
|
30
|
+
* a necessary `:param` has no value in the context. The second case is normally
|
|
31
|
+
* temporary during a transition, and the atom computes the value again on the next
|
|
32
|
+
* snapshot.
|
|
33
|
+
*
|
|
34
|
+
* It THROWS never. A derivation of this architecture reports a failure through
|
|
35
|
+
* `onError` and keeps its last good value, and `currentView` of
|
|
36
|
+
* `@xmachines/play-xstate/view` follows the same rule.
|
|
37
|
+
*
|
|
38
|
+
* The reason is the propagation. A subscriber makes this atom eager, so the
|
|
39
|
+
* derivation runs inside the `set` of `state`, which runs inside the XState
|
|
40
|
+
* subscription. A throw that left this function would unwind into `actor.send` and
|
|
41
|
+
* break the transition for EVERY other observer of the actor.
|
|
42
|
+
*
|
|
43
|
+
* The containment therefore belongs to the derivation, and not to each subscriber:
|
|
44
|
+
* a consumer that writes its own `watchAtom(actor.currentRoute, ...)` is protected
|
|
45
|
+
* by the same rule that protects a router bridge.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```typescript
|
|
49
|
+
* // It returns "/profile/alice" when context.params.userId === "alice",
|
|
50
|
+
* // and null while the param is still absent.
|
|
51
|
+
* const route = actor.currentRoute.get();
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
currentRoute = createAtom((previous) => {
|
|
55
|
+
try {
|
|
56
|
+
return deriveCurrentRoute(this.state.get());
|
|
57
|
+
} catch (error) {
|
|
58
|
+
const onError = this.hooks.onError;
|
|
59
|
+
if (onError) {
|
|
60
|
+
const reported = toError(error);
|
|
61
|
+
queueMicrotask(() => {
|
|
62
|
+
onError(this, reported);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return previous ?? null;
|
|
66
|
+
}
|
|
67
|
+
}, { compare: () => false });
|
|
68
|
+
/**
|
|
69
|
+
* The route of the initial state of the machine. The construction fixes it, and it
|
|
70
|
+
* never changes, also when the code restores the actor from a snapshot.
|
|
71
|
+
*
|
|
72
|
+
* A router bridge compares it with the browser URL, and it therefore separates a
|
|
73
|
+
* deep link (a URL that is not the initial one → the router wins) from a restore
|
|
74
|
+
* (the initial URL, and the actor at a different route from the restore → the actor
|
|
75
|
+
* wins).
|
|
76
|
+
*
|
|
77
|
+
* `deriveInitialRoute` derives the value statically from the machine definition,
|
|
78
|
+
* with the pure `initialTransition` helper of XState: the chain of the initial states
|
|
79
|
+
* and their `meta.route` templates are fixed at the moment of the machine
|
|
80
|
+
* definition, and the substitution of a `:param` uses the real initial context of
|
|
81
|
+
* the machine for the `input` of this actor. The code makes no second actor, and a
|
|
82
|
+
* snapshot of a restore changes the value never: it is always the **default**
|
|
83
|
+
* initial route of the machine.
|
|
84
|
+
*
|
|
85
|
+
* Without a snapshot of a restore, the pre-start snapshot of this actor IS that
|
|
86
|
+
* default initial state, so the code derives the route from it directly. A restore
|
|
87
|
+
* alone needs `initialTransition`, whose inert actor scope runs the initial
|
|
88
|
+
* transition two more times, one of them with an undefined `input`. That is a quirk
|
|
89
|
+
* of XState, and it costs too much for every other case.
|
|
90
|
+
*/
|
|
91
|
+
initialRoute = this.playerRestoredSnapshot === void 0 ? deriveCurrentRoute(this.getSnapshot()) : deriveInitialRoute(this.playerMachine, this.playerInput);
|
|
92
|
+
}
|
|
93
|
+
return RoutableActor;
|
|
126
94
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
// `@xmachines/play-router` in its own `.d.ts` — `PlayRouteEvent`, `RouteData`,
|
|
131
|
-
// `RouteObject` and `RouteMetadata` all have their single definition there. That
|
|
132
|
-
// package is an OPTIONAL peer, so a main entry that reached them gave every consumer
|
|
133
|
-
// of `definePlayer` a type graph that names a package their node_modules does not
|
|
134
|
-
// hold. `skipLibCheck` hides the fault and does not remove it: the types resolve to
|
|
135
|
-
// nothing.
|
|
136
|
-
//
|
|
137
|
-
// The view half exposes nothing on the main entry, for the same reason. The two
|
|
138
|
-
// capabilities hold one shape.
|
|
139
|
-
export { deriveRoute, isAbsoluteRoute, buildRouteUrl, formatPlayRouteTransitions, } from "./routing/index.js";
|
|
95
|
+
//#endregion
|
|
96
|
+
export { buildRouteUrl, deriveRoute, formatPlayRouteTransitions, isAbsoluteRoute, withRouting };
|
|
97
|
+
|
|
140
98
|
//# sourceMappingURL=with-routing.js.map
|
package/dist/with-routing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"with-routing.js","sourceRoot":"","sources":["../src/with-routing.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAKlD,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAE5C;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,WAAW,CAC1B,IAAiC;IAEjC,MAAM,aAAc,SAAQ,IAAI;QAC/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;WA4BG;QACa,YAAY,GAAgC,UAAU,CACrE,CAAC,QAAQ,EAAiB,EAAE;YAC3B,IAAI,CAAC;gBACJ,OAAO,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;YAC7C,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,+EAA+E;gBAC/E,gFAAgF;gBAChF,6EAA6E;gBAC7E,8EAA8E;gBAC9E,wEAAwE;gBACxE,+EAA+E;gBAC/E,6EAA6E;gBAC7E,4EAA4E;gBAC5E,0EAA0E;gBAC1E,+EAA+E;gBAC/E,2DAA2D;gBAC3D,EAAE;gBACF,+EAA+E;gBAC/E,8EAA8E;gBAC9E,4DAA4D;gBAC5D,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC;gBACnC,IAAI,OAAO,EAAE,CAAC;oBACb,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;oBAChC,cAAc,CAAC,GAAG,EAAE;wBACnB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;oBACzB,CAAC,CAAC,CAAC;gBACJ,CAAC;gBACD,OAAO,QAAQ,IAAI,IAAI,CAAC;YACzB,CAAC;QACF,CAAC,EACD;YACC,8EAA8E;YAC9E,+BAA+B;YAC/B,EAAE;YACF,+EAA+E;YAC/E,iFAAiF;YACjF,gFAAgF;YAChF,gFAAgF;YAChF,6EAA6E;YAC7E,6EAA6E;YAC7E,+EAA+E;YAC/E,EAAE;YACF,gFAAgF;YAChF,iFAAiF;YACjF,+EAA+E;YAC/E,kFAAkF;YAClF,2EAA2E;YAC3E,mCAAmC;YACnC,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK;SACpB,CACD,CAAC;QAEF;;;;;;;;;;;;;;;;;;;;;;WAsBG;QACa,YAAY,GAC3B,IAAI,CAAC,sBAAsB,KAAK,SAAS;YACxC,CAAC,CAAC,kBAAkB,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACxC,CAAC,CAAC,kBAAkB,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;KAC7D;IAED,OAAO,aAAa,CAAC;AACtB,CAAC;AAED,qEAAqE;AACrE,EAAE;AACF,oEAAoE;AACpE,+EAA+E;AAC/E,iFAAiF;AACjF,qFAAqF;AACrF,kFAAkF;AAClF,oFAAoF;AACpF,WAAW;AACX,EAAE;AACF,gFAAgF;AAChF,+BAA+B;AAC/B,OAAO,EACN,WAAW,EACX,eAAe,EACf,aAAa,EACb,0BAA0B,GAC1B,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"with-routing.js","names":[],"sources":["../src/with-routing.ts"],"sourcesContent":["/**\n * The routing capability of an XState actor, as a mixin.\n *\n * `PlayerActor` holds `state` and `send`, and nothing about a route. This module adds\n * `currentRoute` and `initialRoute`, which is what `Routable` of `@xmachines/play-router`\n * asks for and what every router bridge reads.\n *\n * It is a SEPARATE entry point, `@xmachines/play-xstate/routing`, and the core imports it\n * never. `@xmachines/play-router` is an OPTIONAL peer of this package for the same reason:\n * an application that declares no route installs neither that package nor the route map\n * and the bridge that it carries.\n *\n * @example\n * ```typescript\n * import { definePlayer, PlayerActor, compose } from \"@xmachines/play-xstate\";\n * import { withRouting } from \"@xmachines/play-xstate/routing\";\n *\n * const createPlayer = definePlayer({ machine, actor: compose(PlayerActor, withRouting) });\n * const actor = createPlayer();\n * actor.currentRoute.get(); // \"/profile/alice\"\n * ```\n *\n * @packageDocumentation\n */\nimport type { AnyStateMachine } from \"xstate\";\nimport { createAtom } from \"@xmachines/play-atom\";\nimport type { ReadonlyAtom } from \"@xmachines/play-atom\";\nimport type { Routable } from \"@xmachines/play-router\";\n\nimport type { PlayerConstructor } from \"./capabilities.js\";\nimport { deriveCurrentRoute, deriveInitialRoute } from \"./routing/index.js\";\nimport { toError } from \"./player-actor.js\";\n\n/**\n * Adds the routing capability to an actor class.\n *\n * The result satisfies `Routable`, so a router bridge accepts it. A class without this\n * mixin exposes no `currentRoute`, and a bridge therefore refuses it when it compiles,\n * rather than at the first navigation.\n *\n * The mixin declares NO constructor, and that is deliberate: TypeScript demands the\n * signature `...args: any[]` from a mixin over a generic base (TS2545), and this\n * workspace writes no `any`. A field initializer of a subclass runs after `super()`\n * instead, and it reads the inputs that `PlayerActor` keeps for a capability.\n *\n * @param Base - The actor class to extend, normally `PlayerActor`.\n */\nexport function withRouting<TMachine extends AnyStateMachine>(\n\tBase: PlayerConstructor<TMachine>,\n): PlayerConstructor<TMachine, Routable> {\n\tclass RoutableActor extends Base implements Routable {\n\t\t/**\n\t\t * A computed atom. It derives the current URL path from the `meta.route`\n\t\t * template of the active machine state and from the context of the actor.\n\t\t *\n\t\t * It returns `null` when the current state has no `meta.route` field, and also when\n\t\t * a necessary `:param` has no value in the context. The second case is normally\n\t\t * temporary during a transition, and the atom computes the value again on the next\n\t\t * snapshot.\n\t\t *\n\t\t * It THROWS never. A derivation of this architecture reports a failure through\n\t\t * `onError` and keeps its last good value, and `currentView` of\n\t\t * `@xmachines/play-xstate/view` follows the same rule.\n\t\t *\n\t\t * The reason is the propagation. A subscriber makes this atom eager, so the\n\t\t * derivation runs inside the `set` of `state`, which runs inside the XState\n\t\t * subscription. A throw that left this function would unwind into `actor.send` and\n\t\t * break the transition for EVERY other observer of the actor.\n\t\t *\n\t\t * The containment therefore belongs to the derivation, and not to each subscriber:\n\t\t * a consumer that writes its own `watchAtom(actor.currentRoute, ...)` is protected\n\t\t * by the same rule that protects a router bridge.\n\t\t *\n\t\t * @example\n\t\t * ```typescript\n\t\t * // It returns \"/profile/alice\" when context.params.userId === \"alice\",\n\t\t * // and null while the param is still absent.\n\t\t * const route = actor.currentRoute.get();\n\t\t * ```\n\t\t */\n\t\tpublic readonly currentRoute: ReadonlyAtom<string | null> = createAtom(\n\t\t\t(previous): string | null => {\n\t\t\t\ttry {\n\t\t\t\t\treturn deriveCurrentRoute(this.state.get());\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// `deriveCurrentRoute` answers the EXPECTED failure itself: a missing `:param`\n\t\t\t\t\t// returns `null`, and the next snapshot resolves it. Whatever reaches this line\n\t\t\t\t\t// is unexpected — a `URIError` of `encodeURIComponent` for a lone surrogate,\n\t\t\t\t\t// for example. Report it, and keep the last route: a caller that reads `null`\n\t\t\t\t\t// or a stale route navigates nowhere, which the bridge handles already.\n\t\t\t\t\t// The derivation runs inside the `set` of `state`, and the engine TRACKS every\n\t\t\t\t\t// atom that this function reads — the handler of the host included. A report\n\t\t\t\t\t// from HERE therefore made whatever that handler reads a dependency of this\n\t\t\t\t\t// atom, and an unrelated write of it re-derived and reported again: three\n\t\t\t\t\t// reports for one failure. The microtask leaves the evaluation, so the handler\n\t\t\t\t\t// reads what it likes and this atom depends on none of it.\n\t\t\t\t\t//\n\t\t\t\t\t// It leaves the `set` as well, which is what the comment below already needed:\n\t\t\t\t\t// a throw from the handler lands on the global handler of the runtime, and it\n\t\t\t\t\t// breaks the transition for no other observer of the actor.\n\t\t\t\t\tconst onError = this.hooks.onError;\n\t\t\t\t\tif (onError) {\n\t\t\t\t\t\tconst reported = toError(error);\n\t\t\t\t\t\tqueueMicrotask(() => {\n\t\t\t\t\t\t\tonError(this, reported);\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\treturn previous ?? null;\n\t\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\t// Every WRITE of `state` reaches an observer of this atom, and not the writes\n\t\t\t\t// that change the route alone.\n\t\t\t\t//\n\t\t\t\t// The default `Object.is` stops a notification for a value that the atom holds\n\t\t\t\t// already, and a route is a STRING: a guard that refuses an inbound location and\n\t\t\t\t// moves the actor back to the state it came from derives the same string, so no\n\t\t\t\t// observer heard the answer. `RouterBridgeBase` waits for exactly that answer —\n\t\t\t\t// `syncActorFromRouter` clears `lastActorRoute` before it sends, so the push\n\t\t\t\t// that puts the address bar back on the route of the machine is owed to this\n\t\t\t\t// notification — and the address bar kept the location that the guard refused.\n\t\t\t\t//\n\t\t\t\t// The bridge is written against a notification for each change of the SNAPSHOT,\n\t\t\t\t// and not for each change of the derived string, so the comparison has to answer\n\t\t\t\t// \"different\" always. A consumer that reads the value pays nothing for it: the\n\t\t\t\t// derivation still runs one time for each transition, and `state` itself compares\n\t\t\t\t// with `Object.is`, so an event that XState ignores delivers the identical\n\t\t\t\t// snapshot and propagates nothing.\n\t\t\t\tcompare: () => false,\n\t\t\t},\n\t\t);\n\n\t\t/**\n\t\t * The route of the initial state of the machine. The construction fixes it, and it\n\t\t * never changes, also when the code restores the actor from a snapshot.\n\t\t *\n\t\t * A router bridge compares it with the browser URL, and it therefore separates a\n\t\t * deep link (a URL that is not the initial one → the router wins) from a restore\n\t\t * (the initial URL, and the actor at a different route from the restore → the actor\n\t\t * wins).\n\t\t *\n\t\t * `deriveInitialRoute` derives the value statically from the machine definition,\n\t\t * with the pure `initialTransition` helper of XState: the chain of the initial states\n\t\t * and their `meta.route` templates are fixed at the moment of the machine\n\t\t * definition, and the substitution of a `:param` uses the real initial context of\n\t\t * the machine for the `input` of this actor. The code makes no second actor, and a\n\t\t * snapshot of a restore changes the value never: it is always the **default**\n\t\t * initial route of the machine.\n\t\t *\n\t\t * Without a snapshot of a restore, the pre-start snapshot of this actor IS that\n\t\t * default initial state, so the code derives the route from it directly. A restore\n\t\t * alone needs `initialTransition`, whose inert actor scope runs the initial\n\t\t * transition two more times, one of them with an undefined `input`. That is a quirk\n\t\t * of XState, and it costs too much for every other case.\n\t\t */\n\t\tpublic readonly initialRoute: string | null =\n\t\t\tthis.playerRestoredSnapshot === undefined\n\t\t\t\t? deriveCurrentRoute(this.getSnapshot())\n\t\t\t\t: deriveInitialRoute(this.playerMachine, this.playerInput);\n\t}\n\n\treturn RoutableActor;\n}\n\n// The utilities of the route derivation and of the URL construction.\n//\n// They belong HERE, and no longer on the main entry. Each one names\n// `@xmachines/play-router` in its own `.d.ts` — `PlayRouteEvent`, `RouteData`,\n// `RouteObject` and `RouteMetadata` all have their single definition there. That\n// package is an OPTIONAL peer, so a main entry that reached them gave every consumer\n// of `definePlayer` a type graph that names a package their node_modules does not\n// hold. `skipLibCheck` hides the fault and does not remove it: the types resolve to\n// nothing.\n//\n// The view half exposes nothing on the main entry, for the same reason. The two\n// capabilities hold one shape.\nexport {\n\tderiveRoute,\n\tisAbsoluteRoute,\n\tbuildRouteUrl,\n\tformatPlayRouteTransitions,\n} from \"./routing/index.js\";\nexport type {\n\tRouteMachineConfig,\n\tRouteStateNode,\n\tRouteContext,\n\tRouteObject,\n\tRouteMetadata,\n\tRouteData,\n\tRouteDataResolver,\n} from \"./routing/index.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA+CA,SAAgB,YACf,MACwC;CACxC,MAAM,sBAAsB,KAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8BpD,eAA4D,YAC1D,aAA4B;GAC5B,IAAI;IACH,OAAO,mBAAmB,KAAK,MAAM,IAAI,CAAC;GAC3C,SAAS,OAAO;IAgBf,MAAM,UAAU,KAAK,MAAM;IAC3B,IAAI,SAAS;KACZ,MAAM,WAAW,QAAQ,KAAK;KAC9B,qBAAqB;MACpB,QAAQ,MAAM,QAAQ;KACvB,CAAC;IACF;IACA,OAAO,YAAY;GACpB;EACD,GACA,EAkBC,eAAe,MAChB,CACD;;;;;;;;;;;;;;;;;;;;;;;;EAyBA,eACC,KAAK,2BAA2B,KAAA,IAC7B,mBAAmB,KAAK,YAAY,CAAC,IACrC,mBAAmB,KAAK,eAAe,KAAK,WAAW;CAC5D;CAEA,OAAO;AACR"}
|
package/dist/with-view.d.ts
CHANGED
|
@@ -46,9 +46,7 @@ import type { PlayerConstructor } from "./capabilities.js";
|
|
|
46
46
|
*
|
|
47
47
|
* The ORDER of the composition does not matter. `currentView` and `currentRoute` are two
|
|
48
48
|
* computed atoms over `state`, and neither reads the other, so the engine evaluates both
|
|
49
|
-
* from one write in topological order.
|
|
50
|
-
* `onSnapshot` override, and the order then decided which capability held its new value
|
|
51
|
-
* first.
|
|
49
|
+
* from one write in topological order.
|
|
52
50
|
*
|
|
53
51
|
* @param Base - The actor class to extend, normally `PlayerActor` or the result of
|
|
54
52
|
* another capability.
|
package/dist/with-view.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"with-view.d.ts","sourceRoot":"","sources":["../src/with-view.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"with-view.d.ts","sourceRoot":"","sources":["../src/with-view.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AAG9C,OAAO,EAAqC,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAGxF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAG3D;;;;;;;;;;;;;GAaG;AAEH;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,SAAS,eAAe,EACxD,IAAI,EAAE,iBAAiB,CAAC,QAAQ,CAAC,GAC/B,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAuFvC"}
|
package/dist/with-view.js
CHANGED
|
@@ -1,158 +1,88 @@
|
|
|
1
|
-
import { createAtom } from "@xmachines/play-atom";
|
|
2
|
-
import { shallowEqualExcept } from "@xmachines/core/utils";
|
|
3
|
-
import { reuseComposedState } from "@xmachines/play-view";
|
|
4
1
|
import { toError } from "./player-actor.js";
|
|
5
2
|
import { deriveCurrentView } from "./view/derive-current-view.js";
|
|
3
|
+
import { createAtom } from "@xmachines/play-atom";
|
|
4
|
+
import { reuseComposedState } from "@xmachines/play-view";
|
|
5
|
+
//#region packages/play-xstate/src/with-view.ts
|
|
6
6
|
/**
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if (!a || !b)
|
|
21
|
-
return false;
|
|
22
|
-
if (!shallowEqualExcept(a, b, "elements"))
|
|
23
|
-
return false;
|
|
24
|
-
const aElements = a.elements ?? {};
|
|
25
|
-
const bElements = b.elements ?? {};
|
|
26
|
-
// A derived spec spreads the same static meta.view. Therefore the elements
|
|
27
|
-
// usually have the same reference, and the walk over each element is then not
|
|
28
|
-
// necessary.
|
|
29
|
-
if (aElements === bElements)
|
|
30
|
-
return true;
|
|
31
|
-
const elementKeys = Object.keys(aElements);
|
|
32
|
-
if (elementKeys.length !== Object.keys(bElements).length)
|
|
33
|
-
return false;
|
|
34
|
-
for (const key of elementKeys) {
|
|
35
|
-
const aElement = aElements[key]; // nosemgrep: gitlab.eslint.detect-object-injection
|
|
36
|
-
const bElement = bElements[key]; // nosemgrep: gitlab.eslint.detect-object-injection
|
|
37
|
-
if (aElement === bElement)
|
|
38
|
-
continue;
|
|
39
|
-
if (!aElement || !bElement)
|
|
40
|
-
return false;
|
|
41
|
-
if (!shallowEqualExcept(aElement, bElement, "props"))
|
|
42
|
-
return false;
|
|
43
|
-
if (!shallowEqualExcept(aElement.props ?? {}, bElement.props ?? {}))
|
|
44
|
-
return false;
|
|
45
|
-
}
|
|
46
|
-
return true;
|
|
47
|
-
};
|
|
48
|
-
/**
|
|
49
|
-
* Two WeakMaps stood here, and the derivation below needs neither.
|
|
50
|
-
*
|
|
51
|
-
* `lastViewSnapshots` skipped the derivation for a snapshot that the pipeline read
|
|
52
|
-
* already, because XState notifies each observer on EVERY event that it processes and an
|
|
53
|
-
* event that it ignores delivers the identical snapshot again. A computed atom answers
|
|
54
|
-
* that by itself: `state.set(snapshot)` compares with `Object.is`, an identical reference
|
|
55
|
-
* therefore propagates nothing, and this atom stays valid.
|
|
56
|
-
*
|
|
57
|
-
* `lastEmittedViews` held the previous emission for the equality gate, because a read of
|
|
58
|
-
* `currentView` would have registered it as a dependency of the computation around the
|
|
59
|
-
* call and `@xstate/store` exports no `untrack`. The `previous` parameter of a computed
|
|
60
|
-
* atom is that same value, and the engine hands it in rather than tracking a read.
|
|
61
|
-
*/
|
|
7
|
+
* Two WeakMaps stood here, and the derivation below needs neither.
|
|
8
|
+
*
|
|
9
|
+
* `lastViewSnapshots` skipped the derivation for a snapshot that the pipeline read
|
|
10
|
+
* already, because XState notifies each observer on EVERY event that it processes and an
|
|
11
|
+
* event that it ignores delivers the identical snapshot again. A computed atom answers
|
|
12
|
+
* that by itself: `state.set(snapshot)` compares with `Object.is`, an identical reference
|
|
13
|
+
* therefore propagates nothing, and this atom stays valid.
|
|
14
|
+
*
|
|
15
|
+
* `lastEmittedViews` held the previous emission for the equality gate, because a read of
|
|
16
|
+
* `currentView` would have registered it as a dependency of the computation around the
|
|
17
|
+
* call and `@xstate/store` exports no `untrack`. The `previous` parameter of a computed
|
|
18
|
+
* atom is that same value, and the engine hands it in rather than tracking a read.
|
|
19
|
+
*/
|
|
62
20
|
/**
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
? lastEmittedView
|
|
127
|
-
: nextView;
|
|
128
|
-
}
|
|
129
|
-
catch (error) {
|
|
130
|
-
// The derivation runs inside the `set` of `state`, which runs inside the XState
|
|
131
|
-
// subscription. A throw that leaves this function therefore leaves that `set`
|
|
132
|
-
// and breaks the transition for every other observer of the actor. Report it,
|
|
133
|
-
// and keep the last valid view.
|
|
134
|
-
//
|
|
135
|
-
// The derivation runs inside the `set` of `state`, and the engine TRACKS every
|
|
136
|
-
// atom that this function reads — the handler of the host included. A report
|
|
137
|
-
// from HERE therefore made whatever that handler reads a dependency of this
|
|
138
|
-
// atom, and an unrelated write of it re-derived and reported again: three
|
|
139
|
-
// reports for one failure. The microtask leaves the evaluation, so the handler
|
|
140
|
-
// reads what it likes and this atom depends on none of it.
|
|
141
|
-
//
|
|
142
|
-
// It leaves the `set` as well, which is what the comment below already needed:
|
|
143
|
-
// a throw from the handler lands on the global handler of the runtime, and it
|
|
144
|
-
// breaks the transition for no other observer of the actor.
|
|
145
|
-
const onError = this.hooks.onError;
|
|
146
|
-
if (onError) {
|
|
147
|
-
const reported = toError(error);
|
|
148
|
-
queueMicrotask(() => {
|
|
149
|
-
onError(this, reported);
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
return lastEmittedView;
|
|
153
|
-
}
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
return ViewableActor;
|
|
21
|
+
* Adds the view capability to an actor class.
|
|
22
|
+
*
|
|
23
|
+
* The ORDER of the composition does not matter. `currentView` and `currentRoute` are two
|
|
24
|
+
* computed atoms over `state`, and neither reads the other, so the engine evaluates both
|
|
25
|
+
* from one write in topological order.
|
|
26
|
+
*
|
|
27
|
+
* @param Base - The actor class to extend, normally `PlayerActor` or the result of
|
|
28
|
+
* another capability.
|
|
29
|
+
*/
|
|
30
|
+
function withView(Base) {
|
|
31
|
+
class ViewableActor extends Base {
|
|
32
|
+
/**
|
|
33
|
+
* The reactive atom of the current view spec. The atom derives the spec from the
|
|
34
|
+
* `meta.view` metadata of the active state.
|
|
35
|
+
*
|
|
36
|
+
* It emits a **new object reference** for the view of a different state, and for a
|
|
37
|
+
* change of a value of the context, which the spec reads under `/context`. A snapshot
|
|
38
|
+
* that changes neither keeps the previous reference: an event that the machine
|
|
39
|
+
* ignores, a transition inside a region that no outlet shows, or an assign that
|
|
40
|
+
* writes the same values again. The viewKey of a new emission tells a provider
|
|
41
|
+
* whether to seed its store again or to refresh `/context` in place, so the UI
|
|
42
|
+
* mounts again only for a new viewKey.
|
|
43
|
+
*
|
|
44
|
+
* The `PlaySpec` of the emission carries the context of the machine in its composed
|
|
45
|
+
* `state` field, under the read-only `/context` subtree. A spec therefore reads the
|
|
46
|
+
* context, and also each URL param, through the ordinary state grammar
|
|
47
|
+
* (`{ $state: "/context/params/section" }`). The context-projection module of
|
|
48
|
+
* `@xmachines/play-view` holds the complete contract.
|
|
49
|
+
*
|
|
50
|
+
* The atom returns `null` when the current state has no `meta.view` metadata.
|
|
51
|
+
*
|
|
52
|
+
* Two states that each declare a `meta.view` emit two different `viewKey` values,
|
|
53
|
+
* also when the two specs are one `typedSpec` constant: the key is the id of the
|
|
54
|
+
* state node that declares the view. A transition between those two states therefore
|
|
55
|
+
* emits again, and a provider seeds a new store, which removes the ephemeral state.
|
|
56
|
+
* The ephemeral state survives a move between the children when the parent holds
|
|
57
|
+
* the key: declare the view on the parent and none on the children, or declare
|
|
58
|
+
* `outlets` on the parent, which keeps its id as the `viewKey` of the composed view.
|
|
59
|
+
*
|
|
60
|
+
* It is a COMPUTED atom over `state`, and not a written one. One write for each
|
|
61
|
+
* transition therefore reaches the whole actor — `state.set(snapshot)` — and the
|
|
62
|
+
* engine evaluates this atom and `currentRoute` in one propagation, in topological
|
|
63
|
+
* order. A second write would flush a second time, and an observer that reads two
|
|
64
|
+
* atoms would see the new state beside the old view in between.
|
|
65
|
+
*/
|
|
66
|
+
currentView = createAtom((previous) => {
|
|
67
|
+
const lastEmittedView = previous ?? null;
|
|
68
|
+
try {
|
|
69
|
+
const view = deriveCurrentView(this.state.get());
|
|
70
|
+
return reuseComposedState(lastEmittedView, view);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
const onError = this.hooks.onError;
|
|
73
|
+
if (onError) {
|
|
74
|
+
const reported = toError(error);
|
|
75
|
+
queueMicrotask(() => {
|
|
76
|
+
onError(this, reported);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return lastEmittedView;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return ViewableActor;
|
|
157
84
|
}
|
|
85
|
+
//#endregion
|
|
86
|
+
export { withView };
|
|
87
|
+
|
|
158
88
|
//# sourceMappingURL=with-view.js.map
|
package/dist/with-view.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"with-view.js","
|
|
1
|
+
{"version":3,"file":"with-view.js","names":[],"sources":["../src/with-view.ts"],"sourcesContent":["/**\n * The view capability of an XState actor, as a mixin.\n *\n * `PlayerActor` holds `state` and `send`, and nothing about a view. This module adds\n * `currentView`, which is what `Viewable` of `@xmachines/play-view` asks for and what\n * every renderer reads.\n *\n * It is a SEPARATE entry point, `@xmachines/play-xstate/view`, and the core imports it\n * never. `@xmachines/play-view` is an OPTIONAL peer of this package for the same reason:\n * an application that renders no view installs neither that package nor the json-render\n * types that it names.\n *\n * @example\n * ```typescript\n * import { definePlayer, PlayerActor, compose } from \"@xmachines/play-xstate\";\n * import { withRouting } from \"@xmachines/play-xstate/routing\";\n * import { withView } from \"@xmachines/play-xstate/view\";\n *\n * const createPlayer = definePlayer({\n * machine,\n * actor: compose(PlayerActor, withRouting, withView),\n * });\n * ```\n *\n * @packageDocumentation\n */\nimport type { AnyStateMachine } from \"xstate\";\nimport { createAtom } from \"@xmachines/play-atom\";\nimport type { ReadonlyAtom } from \"@xmachines/play-atom\";\nimport { reuseComposedState, type PlaySpec, type Viewable } from \"@xmachines/play-view\";\n\nimport { toError } from \"./player-actor.js\";\nimport type { PlayerConstructor } from \"./capabilities.js\";\nimport { deriveCurrentView } from \"./view/derive-current-view.js\";\n\n/**\n * Two WeakMaps stood here, and the derivation below needs neither.\n *\n * `lastViewSnapshots` skipped the derivation for a snapshot that the pipeline read\n * already, because XState notifies each observer on EVERY event that it processes and an\n * event that it ignores delivers the identical snapshot again. A computed atom answers\n * that by itself: `state.set(snapshot)` compares with `Object.is`, an identical reference\n * therefore propagates nothing, and this atom stays valid.\n *\n * `lastEmittedViews` held the previous emission for the equality gate, because a read of\n * `currentView` would have registered it as a dependency of the computation around the\n * call and `@xstate/store` exports no `untrack`. The `previous` parameter of a computed\n * atom is that same value, and the engine hands it in rather than tracking a read.\n */\n\n/**\n * Adds the view capability to an actor class.\n *\n * The ORDER of the composition does not matter. `currentView` and `currentRoute` are two\n * computed atoms over `state`, and neither reads the other, so the engine evaluates both\n * from one write in topological order.\n *\n * @param Base - The actor class to extend, normally `PlayerActor` or the result of\n * another capability.\n */\nexport function withView<TMachine extends AnyStateMachine>(\n\tBase: PlayerConstructor<TMachine>,\n): PlayerConstructor<TMachine, Viewable> {\n\tclass ViewableActor extends Base implements Viewable {\n\t\t/**\n\t\t * The reactive atom of the current view spec. The atom derives the spec from the\n\t\t * `meta.view` metadata of the active state.\n\t\t *\n\t\t * It emits a **new object reference** for the view of a different state, and for a\n\t\t * change of a value of the context, which the spec reads under `/context`. A snapshot\n\t\t * that changes neither keeps the previous reference: an event that the machine\n\t\t * ignores, a transition inside a region that no outlet shows, or an assign that\n\t\t * writes the same values again. The viewKey of a new emission tells a provider\n\t\t * whether to seed its store again or to refresh `/context` in place, so the UI\n\t\t * mounts again only for a new viewKey.\n\t\t *\n\t\t * The `PlaySpec` of the emission carries the context of the machine in its composed\n\t\t * `state` field, under the read-only `/context` subtree. A spec therefore reads the\n\t\t * context, and also each URL param, through the ordinary state grammar\n\t\t * (`{ $state: \"/context/params/section\" }`). The context-projection module of\n\t\t * `@xmachines/play-view` holds the complete contract.\n\t\t *\n\t\t * The atom returns `null` when the current state has no `meta.view` metadata.\n\t\t *\n\t\t * Two states that each declare a `meta.view` emit two different `viewKey` values,\n\t\t * also when the two specs are one `typedSpec` constant: the key is the id of the\n\t\t * state node that declares the view. A transition between those two states therefore\n\t\t * emits again, and a provider seeds a new store, which removes the ephemeral state.\n\t\t * The ephemeral state survives a move between the children when the parent holds\n\t\t * the key: declare the view on the parent and none on the children, or declare\n\t\t * `outlets` on the parent, which keeps its id as the `viewKey` of the composed view.\n\t\t *\n\t\t * It is a COMPUTED atom over `state`, and not a written one. One write for each\n\t\t * transition therefore reaches the whole actor — `state.set(snapshot)` — and the\n\t\t * engine evaluates this atom and `currentRoute` in one propagation, in topological\n\t\t * order. A second write would flush a second time, and an observer that reads two\n\t\t * atoms would see the new state beside the old view in between.\n\t\t */\n\t\tpublic readonly currentView: ReadonlyAtom<PlaySpec | null> = createAtom(\n\t\t\t(previous): PlaySpec | null => {\n\t\t\t\t// `previous` is the value that this atom holds already. The engine hands it in,\n\t\t\t\t// so the gate below reads it without a `get()`, and it therefore registers\n\t\t\t\t// `currentView` as a dependency of nothing.\n\t\t\t\tconst lastEmittedView = previous ?? null;\n\n\t\t\t\ttry {\n\t\t\t\t\tconst view = deriveCurrentView(this.state.get());\n\n\t\t\t\t\t// Emit only after a real change: deriveCurrentView returns a fresh object on\n\t\t\t\t\t// each call, and the identity of the reference therefore tells nothing. The\n\t\t\t\t\t// derivation keeps `elements` and `viewParts` for one tree of views, so the\n\t\t\t\t\t// gate compares the top-level fields and the context one level deep. A deep\n\t\t\t\t\t// equality test is deliberately NOT here: it sees nothing inside a Map or a\n\t\t\t\t\t// Set, and it therefore stops a real change, and it recurses without an end\n\t\t\t\t\t// on a cyclic prop.\n\t\t\t\t\t//\n\t\t\t\t\t// A return of `lastEmittedView` compares equal under `Object.is`, so the atom\n\t\t\t\t\t// notifies no subscriber and the propagation stops here.\n\t\t\t\t\treturn reuseComposedState(lastEmittedView, view);\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// The derivation runs inside the `set` of `state`, which runs inside the XState\n\t\t\t\t\t// subscription. A throw that leaves this function therefore leaves that `set`\n\t\t\t\t\t// and breaks the transition for every other observer of the actor. Report it,\n\t\t\t\t\t// and keep the last valid view.\n\t\t\t\t\t//\n\t\t\t\t\t// The derivation runs inside the `set` of `state`, and the engine TRACKS every\n\t\t\t\t\t// atom that this function reads — the handler of the host included. A report\n\t\t\t\t\t// from HERE therefore made whatever that handler reads a dependency of this\n\t\t\t\t\t// atom, and an unrelated write of it re-derived and reported again: three\n\t\t\t\t\t// reports for one failure. The microtask leaves the evaluation, so the handler\n\t\t\t\t\t// reads what it likes and this atom depends on none of it.\n\t\t\t\t\t//\n\t\t\t\t\t// It leaves the `set` as well, which is what the comment below already needed:\n\t\t\t\t\t// a throw from the handler lands on the global handler of the runtime, and it\n\t\t\t\t\t// breaks the transition for no other observer of the actor.\n\t\t\t\t\tconst onError = this.hooks.onError;\n\t\t\t\t\tif (onError) {\n\t\t\t\t\t\tconst reported = toError(error);\n\t\t\t\t\t\tqueueMicrotask(() => {\n\t\t\t\t\t\t\tonError(this, reported);\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\treturn lastEmittedView;\n\t\t\t\t}\n\t\t\t},\n\t\t);\n\t}\n\n\treturn ViewableActor;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4DA,SAAgB,SACf,MACwC;CACxC,MAAM,sBAAsB,KAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmCpD,cAA6D,YAC3D,aAA8B;GAI9B,MAAM,kBAAkB,YAAY;GAEpC,IAAI;IACH,MAAM,OAAO,kBAAkB,KAAK,MAAM,IAAI,CAAC;IAY/C,OAAO,mBAAmB,iBAAiB,IAAI;GAChD,SAAS,OAAO;IAgBf,MAAM,UAAU,KAAK,MAAM;IAC3B,IAAI,SAAS;KACZ,MAAM,WAAW,QAAQ,KAAK;KAC9B,qBAAqB;MACpB,QAAQ,MAAM,QAAQ;KACvB,CAAC;IACF;IACA,OAAO;GACR;EACD,CACD;CACD;CAEA,OAAO;AACR"}
|