@xmachines/play-xstate 3.0.0 → 4.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 (75) hide show
  1. package/README.md +148 -57
  2. package/dist/capabilities.d.ts +92 -0
  3. package/dist/capabilities.d.ts.map +1 -0
  4. package/dist/capabilities.js +4 -0
  5. package/dist/capabilities.js.map +1 -0
  6. package/dist/define-player.d.ts +7 -1
  7. package/dist/define-player.d.ts.map +1 -1
  8. package/dist/define-player.js +9 -60
  9. package/dist/define-player.js.map +1 -1
  10. package/dist/errors.d.ts +29 -26
  11. package/dist/errors.d.ts.map +1 -1
  12. package/dist/errors.js +53 -35
  13. package/dist/errors.js.map +1 -1
  14. package/dist/index.d.ts +2 -4
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +9 -4
  17. package/dist/index.js.map +1 -1
  18. package/dist/player-actor.d.ts +52 -117
  19. package/dist/player-actor.d.ts.map +1 -1
  20. package/dist/player-actor.js +72 -228
  21. package/dist/player-actor.js.map +1 -1
  22. package/dist/routing/derive-current-route.d.ts +1 -36
  23. package/dist/routing/derive-current-route.d.ts.map +1 -1
  24. package/dist/routing/derive-current-route.js +2 -76
  25. package/dist/routing/derive-current-route.js.map +1 -1
  26. package/dist/routing/derive-initial-route.d.ts.map +1 -1
  27. package/dist/routing/derive-initial-route.js +11 -0
  28. package/dist/routing/derive-initial-route.js.map +1 -1
  29. package/dist/routing/derive-route.d.ts +81 -2
  30. package/dist/routing/derive-route.d.ts.map +1 -1
  31. package/dist/routing/derive-route.js +97 -3
  32. package/dist/routing/derive-route.js.map +1 -1
  33. package/dist/routing/format-play-route-transitions.d.ts +8 -2
  34. package/dist/routing/format-play-route-transitions.d.ts.map +1 -1
  35. package/dist/routing/format-play-route-transitions.js +170 -43
  36. package/dist/routing/format-play-route-transitions.js.map +1 -1
  37. package/dist/routing/index.d.ts +1 -1
  38. package/dist/routing/index.d.ts.map +1 -1
  39. package/dist/routing/types.d.ts +9 -5
  40. package/dist/routing/types.d.ts.map +1 -1
  41. package/dist/state-meta.d.ts +52 -0
  42. package/dist/state-meta.d.ts.map +1 -0
  43. package/dist/state-meta.js +77 -0
  44. package/dist/state-meta.js.map +1 -0
  45. package/dist/types.d.ts +18 -3
  46. package/dist/types.d.ts.map +1 -1
  47. package/dist/view/derive-current-view.d.ts +1 -1
  48. package/dist/view/derive-current-view.d.ts.map +1 -1
  49. package/dist/view/derive-current-view.js +2 -2
  50. package/dist/view/derive-current-view.js.map +1 -1
  51. package/dist/with-routing.d.ts +45 -0
  52. package/dist/with-routing.d.ts.map +1 -0
  53. package/dist/with-routing.js +78 -0
  54. package/dist/with-routing.js.map +1 -0
  55. package/dist/with-view.d.ts +42 -0
  56. package/dist/with-view.d.ts.map +1 -0
  57. package/dist/with-view.js +150 -0
  58. package/dist/with-view.js.map +1 -0
  59. package/package.json +38 -17
  60. package/dist/guards/compose.d.ts +0 -158
  61. package/dist/guards/compose.d.ts.map +0 -1
  62. package/dist/guards/compose.js +0 -188
  63. package/dist/guards/compose.js.map +0 -1
  64. package/dist/guards/helpers.d.ts +0 -62
  65. package/dist/guards/helpers.d.ts.map +0 -1
  66. package/dist/guards/helpers.js +0 -85
  67. package/dist/guards/helpers.js.map +0 -1
  68. package/dist/guards/index.d.ts +0 -20
  69. package/dist/guards/index.d.ts.map +0 -1
  70. package/dist/guards/index.js +0 -18
  71. package/dist/guards/index.js.map +0 -1
  72. package/dist/guards/types.d.ts +0 -22
  73. package/dist/guards/types.d.ts.map +0 -1
  74. package/dist/guards/types.js +0 -2
  75. package/dist/guards/types.js.map +0 -1
@@ -0,0 +1,52 @@
1
+ /**
2
+ * The read of the `meta` of the active states, which BOTH capabilities share.
3
+ *
4
+ * `deriveCurrentRoute` folds this record into a URL, and `deriveCurrentView` folds the
5
+ * same record into a view spec. The two therefore cannot move apart on the selection of
6
+ * the branch, and they cannot move apart on their tolerance of a degenerate snapshot.
7
+ *
8
+ * It lives in its own module, and no longer beside the route derivation, because it
9
+ * belongs to neither capability. `@xmachines/play-xstate/view` imported it from
10
+ * `routing/derive-current-route.ts`, and that module reads `deriveRoute`, which names
11
+ * `@xmachines/play-router`. A view-only application therefore loaded the whole route
12
+ * derivation, and the OPTIONAL peer of the routing reached the runtime graph of the view
13
+ * entry point.
14
+ */
15
+ import type { AnyMachineSnapshot } from "xstate";
16
+ /**
17
+ * Collects the `meta` object of the active state nodes of a SINGLE branch, from the
18
+ * root to a leaf. The key of each entry is the state id, and the order is the
19
+ * ancestors first. {@link deriveRoute} folds this shape.
20
+ *
21
+ * `snapshot.getMeta()` returns a FLAT record of the meta object of every active
22
+ * state. For a machine that is not parallel, that record is one chain of ancestors
23
+ * already. For a PARALLEL machine, it mixes the entries of every active region. A
24
+ * blind fold therefore joins a relative route of one region to an absolute route of
25
+ * a sibling region, and the URL belongs to the tree of neither region. The flat
26
+ * record also cannot go back into a hierarchy: when a state declares an explicit
27
+ * `id`, for example a child with `id: "dashboard-overview"` under `id: "dashboard"`,
28
+ * the meta keys are those ids, and they carry no information about the ancestry.
29
+ *
30
+ * Therefore this function walks `snapshot.value`, which holds the real hierarchy of
31
+ * the active states by the state key, against the node tree of the machine. It
32
+ * follows the FIRST active child at each level. This gives one deterministic branch,
33
+ * which matches the historical behavior of "the first that it finds" for a parallel
34
+ * machine. It also works with an explicit id, because each node carries its own `id`
35
+ * and its own `meta`.
36
+ *
37
+ * The function returns `null` when the snapshot exposes no machine and no value to
38
+ * walk. This is a defensive measure, because a real XState snapshot always exposes
39
+ * them. The caller then uses `getMeta()`, as before.
40
+ */
41
+ export declare const firstActiveBranchMeta: (snapshot: AnyMachineSnapshot) => Record<string, unknown> | null;
42
+ /**
43
+ * Resolves the meta record that the route derivation and the view derivation fold:
44
+ * the single active branch that {@link firstActiveBranchMeta} walks, which is
45
+ * correct for a parallel machine and for an explicit id, or the flat `getMeta()`
46
+ * record when the snapshot has no machine tree to walk. `deriveCurrentRoute` and
47
+ * `deriveCurrentView` share this function. Therefore the two sides cannot move apart
48
+ * on the selection of the branch, and they cannot move apart on their tolerance of a
49
+ * degenerate snapshot.
50
+ */
51
+ export declare const activeStateMeta: (snapshot: AnyMachineSnapshot) => Record<string, unknown> | null;
52
+ //# sourceMappingURL=state-meta.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-meta.d.ts","sourceRoot":"","sources":["../src/state-meta.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,qBAAqB,GACjC,UAAU,kBAAkB,KAC1B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAqC5B,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,GAAI,UAAU,kBAAkB,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAMxF,CAAC"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Collects the `meta` object of the active state nodes of a SINGLE branch, from the
3
+ * root to a leaf. The key of each entry is the state id, and the order is the
4
+ * ancestors first. {@link deriveRoute} folds this shape.
5
+ *
6
+ * `snapshot.getMeta()` returns a FLAT record of the meta object of every active
7
+ * state. For a machine that is not parallel, that record is one chain of ancestors
8
+ * already. For a PARALLEL machine, it mixes the entries of every active region. A
9
+ * blind fold therefore joins a relative route of one region to an absolute route of
10
+ * a sibling region, and the URL belongs to the tree of neither region. The flat
11
+ * record also cannot go back into a hierarchy: when a state declares an explicit
12
+ * `id`, for example a child with `id: "dashboard-overview"` under `id: "dashboard"`,
13
+ * the meta keys are those ids, and they carry no information about the ancestry.
14
+ *
15
+ * Therefore this function walks `snapshot.value`, which holds the real hierarchy of
16
+ * the active states by the state key, against the node tree of the machine. It
17
+ * follows the FIRST active child at each level. This gives one deterministic branch,
18
+ * which matches the historical behavior of "the first that it finds" for a parallel
19
+ * machine. It also works with an explicit id, because each node carries its own `id`
20
+ * and its own `meta`.
21
+ *
22
+ * The function returns `null` when the snapshot exposes no machine and no value to
23
+ * walk. This is a defensive measure, because a real XState snapshot always exposes
24
+ * them. The caller then uses `getMeta()`, as before.
25
+ */
26
+ export const firstActiveBranchMeta = (snapshot) => {
27
+ const root = snapshot.machine?.root;
28
+ if (!root)
29
+ return null;
30
+ const ordered = {};
31
+ let node = root;
32
+ let value = snapshot.value;
33
+ while (node) {
34
+ if (node.meta && typeof node.meta === "object") {
35
+ ordered[node.id] = node.meta; // nosemgrep: gitlab.eslint.detect-object-injection
36
+ }
37
+ // Find the key of the active child at this level. A string value is the name of an
38
+ // atomic active leaf. An object value is a compound node, with one key, or a
39
+ // parallel node, with many keys: take the first key, for one deterministic
40
+ // branch.
41
+ let key;
42
+ if (typeof value === "string") {
43
+ key = value;
44
+ }
45
+ else if (value && typeof value === "object") {
46
+ key = Object.keys(value)[0];
47
+ }
48
+ if (key === undefined)
49
+ break;
50
+ const child = node.states?.[key]; // nosemgrep: gitlab.eslint.detect-object-injection
51
+ if (!child)
52
+ break;
53
+ value =
54
+ typeof value === "object" && value !== null
55
+ ? value[key] // nosemgrep: gitlab.eslint.detect-object-injection
56
+ : {};
57
+ node = child;
58
+ }
59
+ return ordered;
60
+ };
61
+ /**
62
+ * Resolves the meta record that the route derivation and the view derivation fold:
63
+ * the single active branch that {@link firstActiveBranchMeta} walks, which is
64
+ * correct for a parallel machine and for an explicit id, or the flat `getMeta()`
65
+ * record when the snapshot has no machine tree to walk. `deriveCurrentRoute` and
66
+ * `deriveCurrentView` share this function. Therefore the two sides cannot move apart
67
+ * on the selection of the branch, and they cannot move apart on their tolerance of a
68
+ * degenerate snapshot.
69
+ */
70
+ export const activeStateMeta = (snapshot) => {
71
+ if (!snapshot || typeof snapshot.getMeta !== "function") {
72
+ return null;
73
+ }
74
+ const meta = firstActiveBranchMeta(snapshot) ?? snapshot.getMeta();
75
+ return meta && typeof meta === "object" ? meta : null;
76
+ };
77
+ //# sourceMappingURL=state-meta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-meta.js","sourceRoot":"","sources":["../src/state-meta.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACpC,QAA4B,EACK,EAAE;IACnC,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,EAAE,IAEnB,CAAC;IACb,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAEvB,MAAM,OAAO,GAA4B,EAAE,CAAC;IAC5C,IAAI,IAAI,GAA4B,IAAI,CAAC;IACzC,IAAI,KAAK,GAAY,QAAQ,CAAC,KAAK,CAAC;IAEpC,OAAO,IAAI,EAAE,CAAC;QACb,IAAI,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAChD,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,mDAAmD;QAClF,CAAC;QAED,mFAAmF;QACnF,6EAA6E;QAC7E,2EAA2E;QAC3E,UAAU;QACV,IAAI,GAAuB,CAAC;QAC5B,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC/B,GAAG,GAAG,KAAK,CAAC;QACb,CAAC;aAAM,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC/C,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7B,CAAC;QACD,IAAI,GAAG,KAAK,SAAS;YAAE,MAAM;QAE7B,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,CAA4B,CAAC,CAAC,mDAAmD;QAChH,IAAI,CAAC,KAAK;YAAE,MAAM;QAClB,KAAK;YACJ,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;gBAC1C,CAAC,CAAE,KAAiC,CAAC,GAAG,CAAC,CAAC,mDAAmD;gBAC7F,CAAC,CAAC,EAAE,CAAC;QACP,IAAI,GAAG,KAAK,CAAC;IACd,CAAC;IAED,OAAO,OAAO,CAAC;AAChB,CAAC,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,QAA4B,EAAkC,EAAE;IAC/F,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;QACzD,OAAO,IAAI,CAAC;IACb,CAAC;IACD,MAAM,IAAI,GAAG,qBAAqB,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;IACnE,OAAO,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAE,IAAgC,CAAC,CAAC,CAAC,IAAI,CAAC;AACpF,CAAC,CAAC"}
package/dist/types.d.ts CHANGED
@@ -1,13 +1,28 @@
1
1
  import type { ActorOptions, AnyStateMachine, InputFrom, SnapshotFrom } from "xstate";
2
2
  import type { PlayerActor as PlayerActorClass } from "./player-actor.js";
3
+ import type { PlayerConstructor } from "./capabilities.js";
3
4
  /**
4
5
  * The configuration of definePlayer()
5
6
  */
6
- export interface PlayerConfig<TMachine extends AnyStateMachine> {
7
+ export interface PlayerConfig<TMachine extends AnyStateMachine, TCapabilities extends object = object> {
7
8
  /** The XState v5 state machine */
8
9
  machine: TMachine;
9
10
  /** The lifecycle hooks and the configuration */
10
11
  options?: PlayerOptions<TMachine>;
12
+ /**
13
+ * The actor class to build, which decides which CAPABILITIES the actor carries.
14
+ *
15
+ * `PlayerActor` is the default, and it holds `state` and `send` alone. Compose a class
16
+ * to add a capability, and the factory then answers an actor with it:
17
+ *
18
+ * ```typescript
19
+ * definePlayer({ machine, actor: compose(PlayerActor, withRouting, withView) });
20
+ * ```
21
+ *
22
+ * An application that declares no route, and renders no view, passes nothing and loads
23
+ * neither body of code.
24
+ */
25
+ actor?: PlayerConstructor<TMachine, TCapabilities>;
11
26
  }
12
27
  /**
13
28
  * The lifecycle hooks of the player — the observability surface around the actor.
@@ -21,7 +36,7 @@ export interface PlayerOptions<TMachine extends AnyStateMachine> {
21
36
  onStart?: (actor: PlayerActorClass<TMachine>) => void;
22
37
  /**
23
38
  * The actor calls it on each real stop, which means only after it tore a running
24
- * actor down. A second `stop()` call, a second `dispose()` call, and a stop of an
39
+ * actor down. A second `stop()` call, and a stop of an
25
40
  * actor that never started fire the hook not.
26
41
  */
27
42
  onStop?: (actor: PlayerActorClass<TMachine>) => void;
@@ -118,5 +133,5 @@ export interface PlayerFactoryResumeOptions<TMachine extends AnyStateMachine> {
118
133
  * An absent input is then a compile error, and not an actor that starts in an error
119
134
  * status with a `null` initial route.
120
135
  */
121
- export type PlayerFactory<TMachine extends AnyStateMachine> = undefined extends InputFrom<TMachine> ? (input?: InputFrom<TMachine>, options?: PlayerFactoryResumeOptions<TMachine>) => PlayerActorClass<TMachine> : (input: InputFrom<TMachine>, options?: PlayerFactoryResumeOptions<TMachine>) => PlayerActorClass<TMachine>;
136
+ export type PlayerFactory<TMachine extends AnyStateMachine, TCapabilities extends object = object> = undefined extends InputFrom<TMachine> ? (input?: InputFrom<TMachine>, options?: PlayerFactoryResumeOptions<TMachine>) => PlayerActorClass<TMachine> & TCapabilities : (input: InputFrom<TMachine>, options?: PlayerFactoryResumeOptions<TMachine>) => PlayerActorClass<TMachine> & TCapabilities;
122
137
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACrF,OAAO,KAAK,EAAE,WAAW,IAAI,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEzE;;GAEG;AACH,MAAM,WAAW,YAAY,CAAC,QAAQ,SAAS,eAAe;IAC7D,kCAAkC;IAClC,OAAO,EAAE,QAAQ,CAAC;IAElB,gDAAgD;IAChD,OAAO,CAAC,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa,CAAC,QAAQ,SAAS,eAAe;IAC9D;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;IAEtD;;;;OAIG;IACH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;IAErD;;;;;OAKG;IACH,YAAY,CAAC,EAAE,CACd,KAAK,EAAE,gBAAgB,CAAC,QAAQ,CAAC,EACjC,SAAS,EAAE,YAAY,CAAC,QAAQ,CAAC,EACjC,SAAS,EAAE,YAAY,CAAC,QAAQ,CAAC,KAC7B,IAAI,CAAC;IAEV,uDAAuD;IACvD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,YAAY,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;IAE3F;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgCG;IACH,OAAO,CAAC,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC;CAC5C;AAED;;;;;GAKG;AACH,MAAM,WAAW,0BAA0B,CAAC,QAAQ,SAAS,eAAe;IAC3E;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC,UAAU,CAAC,CAAC;CAC9C;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,CAAC,QAAQ,SAAS,eAAe,IACzD,SAAS,SAAS,SAAS,CAAC,QAAQ,CAAC,GAClC,CACA,KAAK,CAAC,EAAE,SAAS,CAAC,QAAQ,CAAC,EAC3B,OAAO,CAAC,EAAE,0BAA0B,CAAC,QAAQ,CAAC,KAC1C,gBAAgB,CAAC,QAAQ,CAAC,GAC9B,CACA,KAAK,EAAE,SAAS,CAAC,QAAQ,CAAC,EAC1B,OAAO,CAAC,EAAE,0BAA0B,CAAC,QAAQ,CAAC,KAC1C,gBAAgB,CAAC,QAAQ,CAAC,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACrF,OAAO,KAAK,EAAE,WAAW,IAAI,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE3D;;GAEG;AACH,MAAM,WAAW,YAAY,CAC5B,QAAQ,SAAS,eAAe,EAChC,aAAa,SAAS,MAAM,GAAG,MAAM;IAErC,kCAAkC;IAClC,OAAO,EAAE,QAAQ,CAAC;IAElB,gDAAgD;IAChD,OAAO,CAAC,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;IAElC;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,EAAE,iBAAiB,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;CACnD;AAED;;GAEG;AACH,MAAM,WAAW,aAAa,CAAC,QAAQ,SAAS,eAAe;IAC9D;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;IAEtD;;;;OAIG;IACH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;IAErD;;;;;OAKG;IACH,YAAY,CAAC,EAAE,CACd,KAAK,EAAE,gBAAgB,CAAC,QAAQ,CAAC,EACjC,SAAS,EAAE,YAAY,CAAC,QAAQ,CAAC,EACjC,SAAS,EAAE,YAAY,CAAC,QAAQ,CAAC,KAC7B,IAAI,CAAC;IAEV,uDAAuD;IACvD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,YAAY,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;IAE3F;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgCG;IACH,OAAO,CAAC,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC;CAC5C;AAED;;;;;GAKG;AACH,MAAM,WAAW,0BAA0B,CAAC,QAAQ,SAAS,eAAe;IAC3E;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC,UAAU,CAAC,CAAC;CAC9C;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,CAAC,QAAQ,SAAS,eAAe,EAAE,aAAa,SAAS,MAAM,GAAG,MAAM,IAChG,SAAS,SAAS,SAAS,CAAC,QAAQ,CAAC,GAClC,CACA,KAAK,CAAC,EAAE,SAAS,CAAC,QAAQ,CAAC,EAC3B,OAAO,CAAC,EAAE,0BAA0B,CAAC,QAAQ,CAAC,KAC1C,gBAAgB,CAAC,QAAQ,CAAC,GAAG,aAAa,GAC9C,CACA,KAAK,EAAE,SAAS,CAAC,QAAQ,CAAC,EAC1B,OAAO,CAAC,EAAE,0BAA0B,CAAC,QAAQ,CAAC,KAC1C,gBAAgB,CAAC,QAAQ,CAAC,GAAG,aAAa,CAAC"}
@@ -7,7 +7,7 @@
7
7
  * machine always belongs to the region of its URL.
8
8
  */
9
9
  import type { AnyMachineSnapshot } from "xstate";
10
- import { type PlaySpec } from "@xmachines/play-actor";
10
+ import { type PlaySpec } from "@xmachines/play-view";
11
11
  /**
12
12
  * Derives the current view of the actor from the state metadata.
13
13
  *
@@ -1 +1 @@
1
- {"version":3,"file":"derive-current-view.d.ts","sourceRoot":"","sources":["../../src/view/derive-current-view.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AACjD,OAAO,EAAiC,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAyErF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,eAAO,MAAM,iBAAiB,GAAI,UAAU,kBAAkB,KAAG,QAAQ,GAAG,IA4C3E,CAAC"}
1
+ {"version":3,"file":"derive-current-view.d.ts","sourceRoot":"","sources":["../../src/view/derive-current-view.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AACjD,OAAO,EAAiC,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAyEpF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,eAAO,MAAM,iBAAiB,GAAI,UAAU,kBAAkB,KAAG,QAAQ,GAAG,IA4C3E,CAAC"}
@@ -1,5 +1,5 @@
1
- import { composePlayState, toAtomState } from "@xmachines/play-actor";
2
- import { activeStateMeta } from "../routing/index.js";
1
+ import { composePlayState, toAtomState } from "@xmachines/play-view";
2
+ import { activeStateMeta } from "../state-meta.js";
3
3
  /**
4
4
  * The migration warning of the `contextProps` field, which 1.x removed. The code
5
5
  * writes it one time for each spec. A meta.view object from a person passes around
@@ -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","sourceRoot":"","sources":["../../src/view/derive-current-view.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * The routing capability of an XState actor, as a mixin.
3
+ *
4
+ * `PlayerActor` holds `state` and `send`, and nothing about a route. This module adds
5
+ * `currentRoute` and `initialRoute`, which is what `Routable` of `@xmachines/play-router`
6
+ * asks for and what every router bridge reads.
7
+ *
8
+ * It is a SEPARATE entry point, `@xmachines/play-xstate/routing`, and the core imports it
9
+ * never. `@xmachines/play-router` is an OPTIONAL peer of this package for the same reason:
10
+ * an application that declares no route installs neither that package nor the route map
11
+ * and the bridge that it carries.
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import { definePlayer, PlayerActor, compose } from "@xmachines/play-xstate";
16
+ * import { withRouting } from "@xmachines/play-xstate/routing";
17
+ *
18
+ * const createPlayer = definePlayer({ machine, actor: compose(PlayerActor, withRouting) });
19
+ * const actor = createPlayer();
20
+ * actor.currentRoute.get(); // "/profile/alice"
21
+ * ```
22
+ *
23
+ * @packageDocumentation
24
+ */
25
+ import type { AnyStateMachine } from "xstate";
26
+ import type { Routable } from "@xmachines/play-router";
27
+ import type { PlayerConstructor } from "./capabilities.js";
28
+ /**
29
+ * Adds the routing capability to an actor class.
30
+ *
31
+ * The result satisfies `Routable`, so a router bridge accepts it. A class without this
32
+ * mixin exposes no `currentRoute`, and a bridge therefore refuses it when it compiles,
33
+ * rather than at the first navigation.
34
+ *
35
+ * The mixin declares NO constructor, and that is deliberate: TypeScript demands the
36
+ * signature `...args: any[]` from a mixin over a generic base (TS2545), and this
37
+ * workspace writes no `any`. A field initializer of a subclass runs after `super()`
38
+ * instead, and it reads the inputs that `PlayerActor` keeps for a capability.
39
+ *
40
+ * @param Base - The actor class to extend, normally `PlayerActor`.
41
+ */
42
+ export declare function withRouting<TMachine extends AnyStateMachine>(Base: PlayerConstructor<TMachine>): PlayerConstructor<TMachine, Routable>;
43
+ export { deriveRoute, isAbsoluteRoute, buildRouteUrl, formatPlayRouteTransitions, } from "./routing/index.js";
44
+ export type { RouteMachineConfig, RouteStateNode, RouteContext, RouteObject, RouteMetadata, RouteData, RouteDataResolver, } from "./routing/index.js";
45
+ //# sourceMappingURL=with-routing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with-routing.d.ts","sourceRoot":"","sources":["../src/with-routing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAEvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAG3D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,QAAQ,SAAS,eAAe,EAC3D,IAAI,EAAE,iBAAiB,CAAC,QAAQ,CAAC,GAC/B,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAoDvC;AAcD,OAAO,EACN,WAAW,EACX,eAAe,EACf,aAAa,EACb,0BAA0B,GAC1B,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACX,kBAAkB,EAClB,cAAc,EACd,YAAY,EACZ,WAAW,EACX,aAAa,EACb,SAAS,EACT,iBAAiB,GACjB,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,78 @@
1
+ import { Signal } from "@xmachines/play-signals";
2
+ import { deriveCurrentRoute, deriveInitialRoute } from "./routing/index.js";
3
+ /**
4
+ * Adds the routing capability to an actor class.
5
+ *
6
+ * The result satisfies `Routable`, so a router bridge accepts it. A class without this
7
+ * mixin exposes no `currentRoute`, and a bridge therefore refuses it when it compiles,
8
+ * rather than at the first navigation.
9
+ *
10
+ * The mixin declares NO constructor, and that is deliberate: TypeScript demands the
11
+ * signature `...args: any[]` from a mixin over a generic base (TS2545), and this
12
+ * workspace writes no `any`. A field initializer of a subclass runs after `super()`
13
+ * instead, and it reads the inputs that `PlayerActor` keeps for a capability.
14
+ *
15
+ * @param Base - The actor class to extend, normally `PlayerActor`.
16
+ */
17
+ export function withRouting(Base) {
18
+ class RoutableActor extends Base {
19
+ /**
20
+ * A TC39 `Signal.Computed`. It derives the current URL path from the `meta.route`
21
+ * template of the active machine state and from the context of the actor.
22
+ *
23
+ * It returns `null` when the current state has no `meta.route` field, and also when
24
+ * a necessary `:param` has no value in the context. The second case is normally
25
+ * temporary during a transition, and the signal computes the value again on the next
26
+ * snapshot.
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * // It returns "/profile/alice" when context.params.userId === "alice",
31
+ * // and null while the param is still absent.
32
+ * const route = actor.currentRoute.get();
33
+ * ```
34
+ */
35
+ currentRoute = new Signal.Computed(() => deriveCurrentRoute(this.state.get()));
36
+ /**
37
+ * The route of the initial state of the machine. The construction fixes it, and it
38
+ * never changes, also when the code restores the actor from a snapshot.
39
+ *
40
+ * A router bridge compares it with the browser URL, and it therefore separates a
41
+ * deep link (a URL that is not the initial one → the router wins) from a restore
42
+ * (the initial URL, and the actor at a different route from the restore → the actor
43
+ * wins).
44
+ *
45
+ * `deriveInitialRoute` derives the value statically from the machine definition,
46
+ * with the pure `initialTransition` helper of XState: the chain of the initial states
47
+ * and their `meta.route` templates are fixed at the moment of the machine
48
+ * definition, and the substitution of a `:param` uses the real initial context of
49
+ * the machine for the `input` of this actor. The code makes no second actor, and a
50
+ * snapshot of a restore changes the value never: it is always the **default**
51
+ * initial route of the machine.
52
+ *
53
+ * Without a snapshot of a restore, the pre-start snapshot of this actor IS that
54
+ * default initial state, so the code derives the route from it directly. A restore
55
+ * alone needs `initialTransition`, whose inert actor scope runs the initial
56
+ * transition two more times, one of them with an undefined `input`. That is a quirk
57
+ * of XState, and it costs too much for every other case.
58
+ */
59
+ initialRoute = this.playerRestoredSnapshot === undefined
60
+ ? deriveCurrentRoute(this.getSnapshot())
61
+ : deriveInitialRoute(this.playerMachine, this.playerInput);
62
+ }
63
+ return RoutableActor;
64
+ }
65
+ // The utilities of the route derivation and of the URL construction.
66
+ //
67
+ // They belong HERE, and no longer on the main entry. Each one names
68
+ // `@xmachines/play-router` in its own `.d.ts` — `PlayRouteEvent`, `RouteData`,
69
+ // `RouteObject` and `RouteMetadata` all have their single definition there. That
70
+ // package is an OPTIONAL peer, so a main entry that reached them gave every consumer
71
+ // of `definePlayer` a type graph that names a package their node_modules does not
72
+ // hold. `skipLibCheck` hides the fault and does not remove it: the types resolve to
73
+ // nothing.
74
+ //
75
+ // The view half exposes nothing on the main entry, for the same reason. The two
76
+ // capabilities hold one shape.
77
+ export { deriveRoute, isAbsoluteRoute, buildRouteUrl, formatPlayRouteTransitions, } from "./routing/index.js";
78
+ //# sourceMappingURL=with-routing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with-routing.js","sourceRoot":"","sources":["../src/with-routing.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAIjD,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAE5E;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,WAAW,CAC1B,IAAiC;IAEjC,MAAM,aAAc,SAAQ,IAAI;QAC/B;;;;;;;;;;;;;;;WAeG;QACa,YAAY,GAAmC,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CACvF,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CACpC,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"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * The view capability of an XState actor, as a mixin.
3
+ *
4
+ * `PlayerActor` holds `state` and `send`, and nothing about a view. This module adds
5
+ * `currentView`, which is what `Viewable` of `@xmachines/play-view` asks for and what
6
+ * every renderer reads.
7
+ *
8
+ * It is a SEPARATE entry point, `@xmachines/play-xstate/view`, and the core imports it
9
+ * never. `@xmachines/play-view` is an OPTIONAL peer of this package for the same reason:
10
+ * an application that renders no view installs neither that package nor the json-render
11
+ * types that it names.
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import { definePlayer, PlayerActor, compose } from "@xmachines/play-xstate";
16
+ * import { withRouting } from "@xmachines/play-xstate/routing";
17
+ * import { withView } from "@xmachines/play-xstate/view";
18
+ *
19
+ * const createPlayer = definePlayer({
20
+ * machine,
21
+ * actor: compose(PlayerActor, withRouting, withView),
22
+ * });
23
+ * ```
24
+ *
25
+ * @packageDocumentation
26
+ */
27
+ import type { AnyStateMachine } from "xstate";
28
+ import { type Viewable } from "@xmachines/play-view";
29
+ import type { PlayerConstructor } from "./capabilities.js";
30
+ /**
31
+ * Adds the view capability to an actor class.
32
+ *
33
+ * Compose it AFTER the routing capability. An override calls `super.onSnapshot()` first,
34
+ * so `compose(PlayerActor, withRouting, withView)` gives the route its new value before
35
+ * the view derives from the same transition. A router bridge therefore sees a guard
36
+ * redirect before a renderer sees the view of the state that the guard refused.
37
+ *
38
+ * @param Base - The actor class to extend, normally `PlayerActor` or the result of
39
+ * another capability.
40
+ */
41
+ export declare function withView<TMachine extends AnyStateMachine>(Base: PlayerConstructor<TMachine>): PlayerConstructor<TMachine, Viewable>;
42
+ //# sourceMappingURL=with-view.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with-view.d.ts","sourceRoot":"","sources":["../src/with-view.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,KAAK,EAAsB,eAAe,EAAE,MAAM,QAAQ,CAAC;AAGlE,OAAO,EAAqC,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAGxF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAsD3D;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,SAAS,eAAe,EACxD,IAAI,EAAE,iBAAiB,CAAC,QAAQ,CAAC,GAC/B,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAgFvC"}
@@ -0,0 +1,150 @@
1
+ import { Signal } from "@xmachines/play-signals";
2
+ import { shallowEqualExcept } from "@xmachines/play";
3
+ import { reuseComposedState } from "@xmachines/play-view";
4
+ import { toError } from "./player-actor.js";
5
+ import { deriveCurrentView } from "./view/derive-current-view.js";
6
+ /**
7
+ * The structural equality of two derived view specs, with a limit on its depth.
8
+ *
9
+ * The function walks exactly the shape that `deriveCurrentView` builds: the spec
10
+ * fields, then `elements`, then the `props` object of each element. It compares
11
+ * each leaf with `Object.is`. It never enters the VALUE of a prop: a new reference
12
+ * therefore emits the view again, also when the contents are equal. This design
13
+ * keeps two things correct: a prop of a container (a Map, a Set, or an instance of a
14
+ * class, which a structural comparison cannot see), and a cyclic value, which gives
15
+ * a structural comparison a recursion without an end.
16
+ */
17
+ const viewSpecsEquivalent = (a, b) => {
18
+ if (a === b)
19
+ return true;
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
+ * The last snapshot that the view pipeline of each actor read.
50
+ *
51
+ * A WeakMap at the module level, and not a field of the class: a `private` or a `#` field
52
+ * makes a class type NOMINAL, so the compiler then compares the mixin class with no other
53
+ * type and the return of this function needs `as unknown as`. The map holds the same state
54
+ * with no member, and it releases each entry with its actor.
55
+ *
56
+ * XState notifies each observer on EVERY event that it processes, and an event that it
57
+ * ignores delivers the identical snapshot again. `deriveCurrentView` is pure in the
58
+ * snapshot, so an identical reference can change no result. An actor that the map does not
59
+ * hold has read no snapshot yet, which is correct for the first derivation: the snapshot of
60
+ * the construction has the same reference as the snapshot that `start()` replays.
61
+ */
62
+ const lastViewSnapshots = new WeakMap();
63
+ /**
64
+ * Adds the view capability to an actor class.
65
+ *
66
+ * Compose it AFTER the routing capability. An override calls `super.onSnapshot()` first,
67
+ * so `compose(PlayerActor, withRouting, withView)` gives the route its new value before
68
+ * the view derives from the same transition. A router bridge therefore sees a guard
69
+ * redirect before a renderer sees the view of the state that the guard refused.
70
+ *
71
+ * @param Base - The actor class to extend, normally `PlayerActor` or the result of
72
+ * another capability.
73
+ */
74
+ export function withView(Base) {
75
+ class ViewableActor extends Base {
76
+ /**
77
+ * The reactive signal of the current view spec. The signal derives the spec from the
78
+ * `meta.view` metadata of the active state.
79
+ *
80
+ * It emits a **new object reference** on each real change of the view on the screen:
81
+ * the view of a different state, or a change of a param or of the context that changes
82
+ * the resolved spec. A re-entry with `reenter: true` and new params also changes the
83
+ * spec. A snapshot that changes no view on the screen, such as an assign of the
84
+ * context alone, keeps the previous reference. A provider below the signal therefore
85
+ * mounts the UI again not on every event.
86
+ *
87
+ * The `PlaySpec` of the emission carries the context of the machine in its composed
88
+ * `state` field, under the read-only `/context` subtree. A spec therefore reads the
89
+ * context, and also each URL param, through the ordinary state grammar
90
+ * (`{ $state: "/context/params/section" }`). The context-projection module of
91
+ * `@xmachines/play-view` holds the complete contract.
92
+ *
93
+ * The signal returns `null` when the current state has no `meta.view` metadata.
94
+ *
95
+ * Two states can declare two separate `meta.view` literals with an identical
96
+ * structure. A transition between those two states then emits two different
97
+ * references, and a provider mounts the UI again. Move the shared literal into one
98
+ * `typedSpec` constant, and the identity then removes the duplicate.
99
+ */
100
+ currentView = new Signal.State(null);
101
+ onSnapshot(snapshot) {
102
+ // FIRST, so that every capability composed before this one holds its new value.
103
+ super.onSnapshot(snapshot);
104
+ this.validateAndCacheView(snapshot);
105
+ }
106
+ /**
107
+ * Derives the view at the entry of a state, and keeps it. This happens one time for
108
+ * each transition. The signal holds the view, and the code computes it not on each
109
+ * read.
110
+ *
111
+ * @param snapshot - The current XState snapshot
112
+ */
113
+ validateAndCacheView(snapshot) {
114
+ if (snapshot === lastViewSnapshots.get(this)) {
115
+ return;
116
+ }
117
+ lastViewSnapshots.set(this, snapshot);
118
+ try {
119
+ const view = deriveCurrentView(snapshot);
120
+ // Emit only after a real change of the view on the screen: deriveCurrentView
121
+ // returns a fresh object on each call, and the identity of the reference therefore
122
+ // tells nothing. A new reference for a snapshot that changes the view not, for
123
+ // example a context-only assign, makes a provider below mount the UI again, and
124
+ // that removes the state of the view. A deep equality test is deliberately NOT
125
+ // here: it sees nothing inside a Map or a Set, and it therefore stops a real
126
+ // change, and it recurses without an end on a cyclic prop. The last spec of an
127
+ // emission IS the current value of the signal. Read it without a track, so that
128
+ // the gate registers currentView never as a dependency of a computation around it.
129
+ const lastEmittedView = Signal.subtle.untrack(() => this.currentView.get());
130
+ // Use the reference of the previous composed state again when the value of the
131
+ // /context projection did not change. A context-only assign that changes no
132
+ // projected value therefore changes the identity of the state not.
133
+ const nextView = reuseComposedState(lastEmittedView, view);
134
+ if (viewSpecsEquivalent(lastEmittedView, nextView)) {
135
+ return;
136
+ }
137
+ this.currentView.set(nextView);
138
+ }
139
+ catch (error) {
140
+ const onError = this.hooks.onError;
141
+ if (onError) {
142
+ onError(this, toError(error));
143
+ }
144
+ // On an error: keep the last valid view, and clear it not
145
+ }
146
+ }
147
+ }
148
+ return ViewableActor;
149
+ }
150
+ //# sourceMappingURL=with-view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with-view.js","sourceRoot":"","sources":["../src/with-view.ts"],"names":[],"mappings":"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"}