@xmachines/play-xstate 2.0.0 → 2.1.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 (56) hide show
  1. package/README.md +66 -65
  2. package/dist/define-player.d.ts +16 -16
  3. package/dist/define-player.js +16 -16
  4. package/dist/errors.d.ts +57 -50
  5. package/dist/errors.d.ts.map +1 -1
  6. package/dist/errors.js +63 -55
  7. package/dist/errors.js.map +1 -1
  8. package/dist/guards/compose.d.ts +53 -50
  9. package/dist/guards/compose.d.ts.map +1 -1
  10. package/dist/guards/compose.js +67 -63
  11. package/dist/guards/compose.js.map +1 -1
  12. package/dist/guards/helpers.d.ts +22 -22
  13. package/dist/guards/helpers.js +23 -23
  14. package/dist/guards/index.d.ts +9 -9
  15. package/dist/guards/index.js +9 -9
  16. package/dist/guards/types.d.ts +9 -8
  17. package/dist/guards/types.d.ts.map +1 -1
  18. package/dist/index.d.ts +6 -5
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +8 -7
  21. package/dist/index.js.map +1 -1
  22. package/dist/player-actor.d.ts +162 -146
  23. package/dist/player-actor.d.ts.map +1 -1
  24. package/dist/player-actor.js +269 -241
  25. package/dist/player-actor.js.map +1 -1
  26. package/dist/routing/build-url.d.ts +19 -16
  27. package/dist/routing/build-url.d.ts.map +1 -1
  28. package/dist/routing/build-url.js +62 -59
  29. package/dist/routing/build-url.js.map +1 -1
  30. package/dist/routing/derive-current-route.d.ts +42 -36
  31. package/dist/routing/derive-current-route.d.ts.map +1 -1
  32. package/dist/routing/derive-current-route.js +57 -49
  33. package/dist/routing/derive-current-route.js.map +1 -1
  34. package/dist/routing/derive-initial-route.d.ts +23 -20
  35. package/dist/routing/derive-initial-route.d.ts.map +1 -1
  36. package/dist/routing/derive-initial-route.js +27 -24
  37. package/dist/routing/derive-initial-route.js.map +1 -1
  38. package/dist/routing/derive-route.d.ts +38 -37
  39. package/dist/routing/derive-route.d.ts.map +1 -1
  40. package/dist/routing/derive-route.js +45 -42
  41. package/dist/routing/derive-route.js.map +1 -1
  42. package/dist/routing/format-play-route-transitions.d.ts +34 -28
  43. package/dist/routing/format-play-route-transitions.d.ts.map +1 -1
  44. package/dist/routing/format-play-route-transitions.js +32 -28
  45. package/dist/routing/format-play-route-transitions.js.map +1 -1
  46. package/dist/routing/index.d.ts +3 -3
  47. package/dist/routing/index.js +3 -3
  48. package/dist/routing/types.d.ts +12 -11
  49. package/dist/routing/types.d.ts.map +1 -1
  50. package/dist/types.d.ts +64 -60
  51. package/dist/types.d.ts.map +1 -1
  52. package/dist/view/derive-current-view.d.ts +42 -40
  53. package/dist/view/derive-current-view.d.ts.map +1 -1
  54. package/dist/view/derive-current-view.js +51 -47
  55. package/dist/view/derive-current-view.js.map +1 -1
  56. package/package.json +7 -6
@@ -1,11 +1,13 @@
1
1
  import { composePlayState, toAtomState } from "@xmachines/play-actor";
2
2
  import { activeStateMeta } from "../routing/index.js";
3
3
  /**
4
- * Once-per-spec migration warning for the removed 1.x `contextProps` field.
5
- * Hand-written meta.view objects bypass `typedSpec`, so a spec still carrying
6
- * the field compiles clean but its meaning inverted (an allowlist, or an
7
- * empty filter projecting nothing, is now the whole context). Keyed on the
8
- * static meta.view object so the warning fires once, not per snapshot.
4
+ * The migration warning of the `contextProps` field, which 1.x removed. The code
5
+ * writes it one time for each spec. A meta.view object from a person passes around
6
+ * `typedSpec`, and a spec with the field therefore compiles without an error. But
7
+ * the meaning of the field inverted: it was an allowlist, or an empty filter that
8
+ * projected nothing, and the whole context is the value now. The key is the static
9
+ * meta.view object. Therefore the warning appears one time, and not for each
10
+ * snapshot.
9
11
  */
10
12
  const warnedContextProps = new WeakSet();
11
13
  const warnRemovedContextProps = (viewMeta) => {
@@ -16,12 +18,12 @@ const warnRemovedContextProps = (viewMeta) => {
16
18
  "is always projected under /context. Delete the field — it no longer filters anything.");
17
19
  };
18
20
  const resolveViewMeta = (meta) => {
19
- // Iterate last-to-first: snapshot.getMeta() orders keys ancestors-first, so
20
- // scanning from the end makes the deepest active state's meta.view win over
21
- // an ancestor's instead of being shadowed by it.
21
+ // Walk from the last entry to the first: snapshot.getMeta() puts the keys of the
22
+ // ancestors first. A scan from the end therefore lets the meta.view of the deepest
23
+ // active state win, and an ancestor hides it not.
22
24
  const entries = Object.entries(meta);
23
25
  for (let i = entries.length - 1; i >= 0; i--) {
24
- // Loop-bounded array index, not user-controlled property access.
26
+ // The array index has the bound of the loop. No user controls this property access.
25
27
  const [key, stateMeta] = entries[i]; // nosemgrep: gitlab.eslint.detect-object-injection
26
28
  const maybeView = stateMeta && typeof stateMeta === "object"
27
29
  ? stateMeta.view
@@ -36,54 +38,56 @@ const resolveViewMeta = (meta) => {
36
38
  return null;
37
39
  };
38
40
  /**
39
- * Derive the actor's current view from state metadata.
41
+ * Derives the current view of the actor from the state metadata.
40
42
  *
41
- * Always returns a **fresh** `PlaySpec` object so a genuine view change is never
42
- * suppressed by `Signal.State`'s `Object.is` equality check. Whether that fresh
43
- * object is actually emitted is decided by `PlayerActor.validateAndCacheView`,
44
- * which compares against the last emitted spec (reusing the composed `state`
45
- * reference when the projection is value-unchanged) and skips emission when the
46
- * rendered view is unchanged preventing downstream providers from remounting
47
- * the UI on every event.
43
+ * The function always returns a **fresh** `PlaySpec` object. The `Object.is`
44
+ * equality test of `Signal.State` therefore stops no real change of the view.
45
+ * `PlayerActor.validateAndCacheView` decides if the actor emits that fresh object:
46
+ * it compares the object with the last spec of an emission, it reuses the reference
47
+ * of the composed `state` field when the value of the projection did not change, and
48
+ * it emits nothing when the view on the screen is the same. A provider below the
49
+ * actor therefore mounts the UI again not on every event.
48
50
  *
49
- * ### Context projection
51
+ * ### The context projection
50
52
  *
51
- * The machine's context is projected into the derived spec's `state` under the
52
- * read-only `/context` subtree: `state: { ...meta.view.state, context: slice }`.
53
- * The emitted spec is therefore **self-consistent** its `state` honestly
54
- * describes the store contents so `repeat.statePath: "/context/tasks"`,
55
- * `visible: { $state: "/context/…" }`, and `{ $state: "/context/…" }` props all
56
- * resolve through the ordinary state grammar, and tools like `validateSpec`
57
- * pass on derived views with no special-casing (validate the derived view, not
58
- * the raw `meta.view`).
53
+ * The function projects the context of the machine into the `state` field of the
54
+ * derived spec, under the read-only `/context` subtree:
55
+ * `state: { ...meta.view.state, context: slice }`. The spec of the emission is
56
+ * therefore **consistent with itself**, and its `state` field describes the contents
57
+ * of the store correctly. `repeat.statePath: "/context/tasks"`,
58
+ * `visible: { $state: "/context/…" }`, and a `{ $state: "/context/…" }` prop all
59
+ * resolve through the ordinary state grammar. A tool such as `validateSpec` also
60
+ * passes on a derived view, with no special case. Validate the derived view, and not
61
+ * the raw `meta.view`.
59
62
  *
60
- * The whole context is always projected; the authored `meta.view` object is
61
- * never mutated.
63
+ * The store always holds the complete context. The function changes the authored
64
+ * `meta.view` object never.
62
65
  *
63
- * ### viewKey
66
+ * ### The viewKey
64
67
  *
65
- * The derived spec carries `viewKey` the meta record key (state node id) of
66
- * the entry this derivation actually selected. Providers key their store
67
- * lifecycle on it: changed reseed, unchanged refresh `/context` in place.
68
- * It is taken from the selected entry rather than "the active state id"
69
- * because for parallel machines the walked branch and the flat `getMeta()`
70
- * fallback can select a view from a different region than the branch leaf.
68
+ * The derived spec carries a `viewKey` field. Its value is the key of the meta
69
+ * record, which is the id of the state node, of the entry that this derivation
70
+ * selected. A provider uses it as the key of its store lifecycle: a new key seeds
71
+ * the store again, and the same key refreshes `/context` in place. The value comes
72
+ * from the selected entry, and not from "the id of the active state", because for a
73
+ * parallel machine the branch of the walk and the flat `getMeta()` fallback can
74
+ * select a view of a different region than the leaf of the branch.
71
75
  *
72
- * @param snapshot - Current XState machine snapshot.
73
- * @returns Derived `PlaySpec`, or `null` if the current state has no view metadata.
76
+ * @param snapshot - The current snapshot of the XState machine.
77
+ * @returns The derived `PlaySpec`, or `null` when the current state has no view metadata.
74
78
  */
75
79
  export const deriveCurrentView = (snapshot) => {
76
- // Follow the SAME single active branch the route derivation walks, so that
77
- // for a parallel machine the rendered view belongs to the region the URL
78
- // points at whenever that region declares one.
80
+ // Follow the SAME single active branch as the route derivation. Therefore the view
81
+ // on the screen of a parallel machine belongs to the region of the URL, when that
82
+ // region declares a view.
79
83
  const meta = activeStateMeta(snapshot);
80
84
  if (!meta) {
81
85
  return null;
82
86
  }
83
- // A parallel machine may keep routes and views in DIFFERENT regions (one
84
- // region owns navigation, another owns the shell). When the walked branch
85
- // declares no view, scan the flat record of every active region deepest
86
- // entry wins before concluding there is no view at all.
87
+ // A parallel machine can hold its routes and its views in DIFFERENT regions: one
88
+ // region owns the navigation, and another region owns the shell. When the branch of
89
+ // the walk declares no view, scan the flat record of every active region, where the
90
+ // deepest entry wins, before the conclusion that no view is present.
87
91
  let resolved = resolveViewMeta(meta);
88
92
  if (!resolved) {
89
93
  const flatMeta = snapshot.getMeta();
@@ -98,9 +102,9 @@ export const deriveCurrentView = (snapshot) => {
98
102
  if ("contextProps" in viewMeta) {
99
103
  warnRemovedContextProps(viewMeta);
100
104
  }
101
- // Project the whole context under /context. composePlayState skips the
102
- // projection (authored state wins, with a dev warning) when spec.state
103
- // already declares a top-level "context" key.
105
+ // Project the complete context under /context. composePlayState skips the
106
+ // projection, the authored state wins, and the code writes a warning in development,
107
+ // when spec.state declares a top-level "context" key already.
104
108
  const slice = snapshot.context !== null && typeof snapshot.context === "object"
105
109
  ? snapshot.context
106
110
  : undefined;
@@ -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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xmachines/play-xstate",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "XState v5 adapter for Play Architecture",
5
5
  "keywords": [
6
6
  "actor-model",
@@ -44,19 +44,20 @@
44
44
  "clean": "rm -rf dist *.tsbuildinfo coverage node_modules/.svelte2tsx-* node_modules/.vite*",
45
45
  "lint": "oxlint .",
46
46
  "format": "oxfmt .",
47
- "test": "vitest"
47
+ "test": "vitest",
48
+ "test:coverage": "vitest run --coverage"
48
49
  },
49
50
  "dependencies": {
50
- "@xmachines/play": "2.0.0",
51
- "@xmachines/play-actor": "2.0.0",
52
- "@xmachines/play-signals": "2.0.0",
51
+ "@xmachines/play": "2.1.0",
52
+ "@xmachines/play-actor": "2.1.0",
53
+ "@xmachines/play-signals": "2.1.0",
53
54
  "dequal": "^2.0.3"
54
55
  },
55
56
  "devDependencies": {
56
57
  "@testing-library/jest-dom": "^6.9.1",
57
58
  "@types/node": "^26.2.0",
58
59
  "@xmachines/json-render-core": "^0.20.0-xm.2",
59
- "@xmachines/play-router": "2.0.0",
60
+ "@xmachines/play-router": "2.1.0",
60
61
  "oxfmt": "^0.64.0",
61
62
  "oxlint": "^1.79.0",
62
63
  "typescript": "^5.9.3 || ^6.0.3",