@xmachines/play-xstate 1.0.0-beta.8 → 1.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 (97) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +175 -336
  3. package/dist/define-player.d.ts +6 -56
  4. package/dist/define-player.d.ts.map +1 -1
  5. package/dist/define-player.js +7 -61
  6. package/dist/define-player.js.map +1 -1
  7. package/dist/define-player.typecheck.js +6 -0
  8. package/dist/define-player.typecheck.js.map +1 -1
  9. package/dist/errors.d.ts +170 -0
  10. package/dist/errors.d.ts.map +1 -0
  11. package/dist/errors.js +202 -0
  12. package/dist/errors.js.map +1 -0
  13. package/dist/guards/compose.d.ts +14 -3
  14. package/dist/guards/compose.d.ts.map +1 -1
  15. package/dist/guards/compose.js +29 -2
  16. package/dist/guards/compose.js.map +1 -1
  17. package/dist/guards/helpers.d.ts +16 -1
  18. package/dist/guards/helpers.d.ts.map +1 -1
  19. package/dist/guards/helpers.js +16 -35
  20. package/dist/guards/helpers.js.map +1 -1
  21. package/dist/guards/index.d.ts +2 -1
  22. package/dist/guards/index.d.ts.map +1 -1
  23. package/dist/guards/index.js +1 -1
  24. package/dist/guards/index.js.map +1 -1
  25. package/dist/guards/types.d.ts +1 -1
  26. package/dist/guards/types.d.ts.map +1 -1
  27. package/dist/index.d.ts +6 -10
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +3 -5
  30. package/dist/index.js.map +1 -1
  31. package/dist/player-actor.d.ts +134 -28
  32. package/dist/player-actor.d.ts.map +1 -1
  33. package/dist/player-actor.js +424 -159
  34. package/dist/player-actor.js.map +1 -1
  35. package/dist/player-actor.typecheck.js +9 -5
  36. package/dist/player-actor.typecheck.js.map +1 -1
  37. package/dist/routing/build-url.d.ts +27 -16
  38. package/dist/routing/build-url.d.ts.map +1 -1
  39. package/dist/routing/build-url.js +48 -25
  40. package/dist/routing/build-url.js.map +1 -1
  41. package/dist/routing/derive-current-route.d.ts +22 -0
  42. package/dist/routing/derive-current-route.d.ts.map +1 -0
  43. package/dist/routing/derive-current-route.js +122 -0
  44. package/dist/routing/derive-current-route.js.map +1 -0
  45. package/dist/routing/derive-initial-route.d.ts +43 -0
  46. package/dist/routing/derive-initial-route.d.ts.map +1 -0
  47. package/dist/routing/derive-initial-route.js +57 -0
  48. package/dist/routing/derive-initial-route.js.map +1 -0
  49. package/dist/routing/derive-route.d.ts +15 -1
  50. package/dist/routing/derive-route.d.ts.map +1 -1
  51. package/dist/routing/derive-route.js +25 -10
  52. package/dist/routing/derive-route.js.map +1 -1
  53. package/dist/routing/format-play-route-transitions.d.ts +29 -2
  54. package/dist/routing/format-play-route-transitions.d.ts.map +1 -1
  55. package/dist/routing/format-play-route-transitions.js +32 -8
  56. package/dist/routing/format-play-route-transitions.js.map +1 -1
  57. package/dist/routing/index.d.ts +3 -1
  58. package/dist/routing/index.d.ts.map +1 -1
  59. package/dist/routing/index.js +2 -0
  60. package/dist/routing/index.js.map +1 -1
  61. package/dist/routing/types.d.ts +11 -4
  62. package/dist/routing/types.d.ts.map +1 -1
  63. package/dist/types.d.ts +12 -6
  64. package/dist/types.d.ts.map +1 -1
  65. package/package.json +34 -11
  66. package/dist/catalog/index.d.ts +0 -13
  67. package/dist/catalog/index.d.ts.map +0 -1
  68. package/dist/catalog/index.js +0 -11
  69. package/dist/catalog/index.js.map +0 -1
  70. package/dist/catalog/types.d.ts +0 -27
  71. package/dist/catalog/types.d.ts.map +0 -1
  72. package/dist/catalog/types.js +0 -2
  73. package/dist/catalog/types.js.map +0 -1
  74. package/dist/catalog/validate-binding.d.ts +0 -22
  75. package/dist/catalog/validate-binding.d.ts.map +0 -1
  76. package/dist/catalog/validate-binding.js +0 -30
  77. package/dist/catalog/validate-binding.js.map +0 -1
  78. package/dist/catalog/validate-props.d.ts +0 -51
  79. package/dist/catalog/validate-props.d.ts.map +0 -1
  80. package/dist/catalog/validate-props.js +0 -155
  81. package/dist/catalog/validate-props.js.map +0 -1
  82. package/dist/catalog/validate-props.typecheck.d.ts +0 -2
  83. package/dist/catalog/validate-props.typecheck.d.ts.map +0 -1
  84. package/dist/catalog/validate-props.typecheck.js +0 -6
  85. package/dist/catalog/validate-props.typecheck.js.map +0 -1
  86. package/dist/signals/debounce.d.ts +0 -18
  87. package/dist/signals/debounce.d.ts.map +0 -1
  88. package/dist/signals/debounce.js +0 -35
  89. package/dist/signals/debounce.js.map +0 -1
  90. package/dist/signals/index.d.ts +0 -2
  91. package/dist/signals/index.d.ts.map +0 -1
  92. package/dist/signals/index.js +0 -2
  93. package/dist/signals/index.js.map +0 -1
  94. package/dist/signals/state-signal.d.ts +0 -33
  95. package/dist/signals/state-signal.d.ts.map +0 -1
  96. package/dist/signals/state-signal.js +0 -41
  97. package/dist/signals/state-signal.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../../src/guards/compose.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,eAAO,MAAM,aAAa,GAAI,QAAQ,GAAG,GAAG,EAAE,MAAM,GAAG,GAAG,EACzD,QAAQ,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,KAClC,GAYF,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,eAAO,MAAM,eAAe,GAAI,QAAQ,GAAG,GAAG,EAAE,MAAM,GAAG,GAAG,EAC3D,QAAQ,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,KAClC,GAUF,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,eAAO,MAAM,WAAW,GAAI,QAAQ,GAAG,GAAG,EAAE,MAAM,GAAG,GAAG,EACvD,OAAO,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG,MAAM,KACrC,GAEF,CAAC"}
1
+ {"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../../src/guards/compose.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAG/E;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa,GAAG,cAAc,CACzC,cAAc,EACd,WAAW,EACX,OAAO,EACP,mBAAmB,CACnB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAEH,eAAO,MAAM,aAAa,GAAI,QAAQ,GAAG,GAAG,EAAE,MAAM,GAAG,GAAG,EACzD,QAAQ,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,KAClC,aAqBF,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,eAAO,MAAM,eAAe,GAAI,QAAQ,GAAG,GAAG,EAAE,MAAM,GAAG,GAAG,EAC3D,QAAQ,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,KAClC,aAmBF,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,eAAO,MAAM,WAAW,GAAI,QAAQ,GAAG,GAAG,EAAE,MAAM,GAAG,GAAG,EACvD,OAAO,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG,MAAM,KACrC,aAOF,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { and, or, not } from "xstate";
2
+ import { EmptyGuardArrayError } from "../errors.js";
2
3
  /**
3
4
  * Compose guards with AND logic using XState's and() helper
4
5
  *
@@ -54,15 +55,25 @@ import { and, or, not } from "xstate";
54
55
  * @see {@link composeGuardsOr} for OR composition
55
56
  * @see {@link negateGuard} for NOT logic
56
57
  */
58
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
57
59
  export const composeGuards = (guards) => {
58
60
  if (guards.length === 0) {
59
- throw new Error("composeGuards requires at least one guard");
61
+ throw new EmptyGuardArrayError("and");
60
62
  }
61
63
  if (guards.length === 1) {
64
+ // Single guard passthrough — cast through unknown because our Guard<TContext, TEvent>
65
+ // does not structurally match ComposedGuard at compile time (generic event mismatch).
66
+ // At runtime both are plain function objects with identical shape.
67
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
62
68
  return guards[0];
63
69
  }
64
70
  // Use XState's built-in and() for type inference and serialization
65
71
  // Per RESEARCH.md: Don't hand-roll guard composition
72
+ // XState 5.28.0: and() requires `readonly [...tuple]` but our GuardArray is a mutable array.
73
+ // We cast through unknown to express the narrowest possible type boundary rather than
74
+ // escaping to `any`. The runtime value is identical.
75
+ // Track XState typing improvements: https://github.com/statelyai/xstate/issues
76
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
66
77
  return and(guards);
67
78
  };
68
79
  /**
@@ -105,13 +116,23 @@ export const composeGuards = (guards) => {
105
116
  * @see {@link composeGuards} for AND composition
106
117
  * @see {@link negateGuard} for NOT logic
107
118
  */
119
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
108
120
  export const composeGuardsOr = (guards) => {
109
121
  if (guards.length === 0) {
110
- throw new Error("composeGuardsOr requires at least one guard");
122
+ throw new EmptyGuardArrayError("or");
111
123
  }
112
124
  if (guards.length === 1) {
125
+ // Single guard passthrough — cast through unknown because our Guard<TContext, TEvent>
126
+ // does not structurally match ComposedGuard at compile time (generic event mismatch).
127
+ // At runtime both are plain function objects with identical shape.
128
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
113
129
  return guards[0];
114
130
  }
131
+ // XState 5.28.0: or() requires `readonly [...tuple]` but our GuardArray is a mutable array.
132
+ // We cast through unknown to express the narrowest possible type boundary rather than
133
+ // escaping to `any`. The runtime value is identical.
134
+ // Track XState typing improvements: https://github.com/statelyai/xstate/issues
135
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
115
136
  return or(guards);
116
137
  };
117
138
  /**
@@ -150,7 +171,13 @@ export const composeGuardsOr = (guards) => {
150
171
  * @see {@link composeGuards} for AND composition
151
172
  * @see {@link composeGuardsOr} for OR composition
152
173
  */
174
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
153
175
  export const negateGuard = (guard) => {
176
+ // XState 5.28.0: not() requires a specific SingleGuardArg shape, not our Guard type.
177
+ // We cast through unknown to express the narrowest possible type boundary rather than
178
+ // escaping to `any`. The runtime value is identical.
179
+ // Track XState typing improvements: https://github.com/statelyai/xstate/issues
180
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
154
181
  return not(guard);
155
182
  };
156
183
  //# sourceMappingURL=compose.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"compose.js","sourceRoot":"","sources":["../../src/guards/compose.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAGtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAC5B,MAAoC,EAC9B,EAAE;IACR,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC9D,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,mEAAmE;IACnE,qDAAqD;IACrD,OAAO,GAAG,CAAC,MAAa,CAAC,CAAC;AAC3B,CAAC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAC9B,MAAoC,EAC9B,EAAE;IACR,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IAChE,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,EAAE,CAAC,MAAa,CAAC,CAAC;AAC1B,CAAC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAC1B,KAAuC,EACjC,EAAE;IACR,OAAO,GAAG,CAAC,KAAY,CAAC,CAAC;AAC1B,CAAC,CAAC"}
1
+ {"version":3,"file":"compose.js","sourceRoot":"","sources":["../../src/guards/compose.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAItC,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAiBpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,8DAA8D;AAC9D,MAAM,CAAC,MAAM,aAAa,GAAG,CAC5B,MAAoC,EACpB,EAAE;IAClB,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,oBAAoB,CAAC,KAAK,CAAC,CAAC;IACvC,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,sFAAsF;QACtF,sFAAsF;QACtF,mEAAmE;QACnE,8DAA8D;QAC9D,OAAO,MAAM,CAAC,CAAC,CAA6B,CAAC;IAC9C,CAAC;IAED,mEAAmE;IACnE,qDAAqD;IACrD,6FAA6F;IAC7F,sFAAsF;IACtF,qDAAqD;IACrD,+EAA+E;IAC/E,8DAA8D;IAC9D,OAAO,GAAG,CAAC,MAA8C,CAAC,CAAC;AAC5D,CAAC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,8DAA8D;AAC9D,MAAM,CAAC,MAAM,eAAe,GAAG,CAC9B,MAAoC,EACpB,EAAE;IAClB,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,oBAAoB,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,sFAAsF;QACtF,sFAAsF;QACtF,mEAAmE;QACnE,8DAA8D;QAC9D,OAAO,MAAM,CAAC,CAAC,CAA6B,CAAC;IAC9C,CAAC;IAED,4FAA4F;IAC5F,sFAAsF;IACtF,qDAAqD;IACrD,+EAA+E;IAC/E,8DAA8D;IAC9D,OAAO,EAAE,CAAC,MAA6C,CAAC,CAAC;AAC1D,CAAC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,8DAA8D;AAC9D,MAAM,CAAC,MAAM,WAAW,GAAG,CAC1B,KAAuC,EACvB,EAAE;IAClB,qFAAqF;IACrF,sFAAsF;IACtF,qDAAqD;IACrD,+EAA+E;IAC/E,8DAA8D;IAC9D,OAAO,GAAG,CAAC,KAA6C,CAAC,CAAC;AAC3D,CAAC,CAAC"}
@@ -39,5 +39,20 @@ export declare const hasContext: <TContext = Record<string, unknown>>(path: stri
39
39
  * @returns Guard predicate checking event type
40
40
  */
41
41
  export declare const eventMatches: <TEvent extends PlayEvent = PlayEvent>(eventType: string) => Guard<unknown, TEvent>;
42
- export declare const stateMatches: <TContext = Record<string, unknown>>(stateValue: string) => Guard<TContext, PlayEvent>;
42
+ /**
43
+ * Check if a context field matches an expected value.
44
+ *
45
+ * - Accepts an explicit dot-separated field path (e.g. `"user.role"`)
46
+ * - Uses strict equality for primitives, deep structural equality for objects
47
+ * - Object comparison is key-order-insensitive: `{a:1,b:2}` equals `{b:2,a:1}`
48
+ * - Supports Date, RegExp, class instances, nested objects and arrays via `dequal/lite`
49
+ * - Does NOT use substring matching — `"a"` will not match `"active"`
50
+ *
51
+ * For XState state-node matching, use the built-in `in:` guard syntax instead.
52
+ *
53
+ * @param fieldPath - Dot-separated path to context property (e.g., "status", "user.role")
54
+ * @param expectedValue - Value to compare against (string, object, Date, etc.)
55
+ * @returns Guard predicate checking if context field matches
56
+ */
57
+ export declare const contextFieldMatches: <TContext = Record<string, unknown>>(fieldPath: string, expectedValue: unknown) => Guard<TContext, PlayEvent>;
43
58
  //# sourceMappingURL=helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/guards/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,UAAU,GACrB,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,MAAM,KAAG,KAAK,CAAC,QAAQ,EAAE,SAAS,CAI5E,CAAC;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,YAAY,GACvB,MAAM,SAAS,SAAS,GAAG,SAAS,EAAE,WAAW,MAAM,KAAG,KAAK,CAAC,OAAO,EAAE,MAAM,CAG/E,CAAC;AAsBH,eAAO,MAAM,YAAY,GACvB,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,MAAM,KAAG,KAAK,CAAC,QAAQ,EAAE,SAAS,CAqBlF,CAAC"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/guards/helpers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,UAAU,GACrB,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,MAAM,KAAG,KAAK,CAAC,QAAQ,EAAE,SAAS,CAI5E,CAAC;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,YAAY,GACvB,MAAM,SAAS,SAAS,GAAG,SAAS,EAAE,WAAW,MAAM,KAAG,KAAK,CAAC,OAAO,EAAE,MAAM,CAG/E,CAAC;AAEH;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClC,WAAW,MAAM,EACjB,eAAe,OAAO,KACpB,KAAK,CAAC,QAAQ,EAAE,SAAS,CAI3B,CAAC"}
@@ -1,3 +1,4 @@
1
+ import { dequal } from "dequal/lite";
1
2
  /**
2
3
  * Check if context has a truthy value at path
3
4
  *
@@ -43,43 +44,23 @@ export const eventMatches = (eventType) => ({ event }) => {
43
44
  return event.type === eventType;
44
45
  };
45
46
  /**
46
- * Check if current state matches expected value
47
+ * Check if a context field matches an expected value.
47
48
  *
48
- * @example
49
- * ```typescript
50
- * const machine = setup({
51
- * guards: {
52
- * isLoggedIn: stateMatches('authenticated'),
53
- * isDashboard: stateMatches('authenticated.dashboard')
54
- * }
55
- * });
56
- * ```
49
+ * - Accepts an explicit dot-separated field path (e.g. `"user.role"`)
50
+ * - Uses strict equality for primitives, deep structural equality for objects
51
+ * - Object comparison is key-order-insensitive: `{a:1,b:2}` equals `{b:2,a:1}`
52
+ * - Supports Date, RegExp, class instances, nested objects and arrays via `dequal/lite`
53
+ * - Does NOT use substring matching — `"a"` will not match `"active"`
54
+ *
55
+ * For XState state-node matching, use the built-in `in:` guard syntax instead.
57
56
  *
58
- * @param stateValue - Expected state value (string or object)
59
- * @returns Guard predicate checking state value
57
+ * @param fieldPath - Dot-separated path to context property (e.g., "status", "user.role")
58
+ * @param expectedValue - Value to compare against (string, object, Date, etc.)
59
+ * @returns Guard predicate checking if context field matches
60
60
  */
61
- const isRecord = (value) => {
62
- return typeof value === "object" && value !== null;
63
- };
64
- export const stateMatches = (stateValue) => ({ context }) => {
65
- // Note: This helper checks context for state info
66
- // For actual XState state matching, use machine.matches() in actions
67
- // This is a simplified version for context-based state checks
68
- let currentState;
69
- if (isRecord(context) && "_state" in context) {
70
- currentState = context._state;
71
- }
72
- else if (isRecord(context) && "state" in context) {
73
- currentState = context.state;
74
- }
75
- if (!currentState)
76
- return false;
77
- // Simple string matching (can be enhanced for nested states)
78
- if (typeof currentState === "string") {
79
- return currentState === stateValue;
80
- }
81
- // Object state matching
82
- return JSON.stringify(currentState).includes(stateValue);
61
+ export const contextFieldMatches = (fieldPath, expectedValue) => ({ context }) => {
62
+ const actual = getNestedValue(context, fieldPath);
63
+ return dequal(actual, expectedValue);
83
64
  };
84
65
  /**
85
66
  * Get nested value from object using dot-separated path
@@ -93,7 +74,7 @@ const getNestedValue = (obj, path) => {
93
74
  if (value === null || value === undefined || typeof value !== "object") {
94
75
  return undefined;
95
76
  }
96
- value = value[key];
77
+ value = value[key]; // nosemgrep: gitlab.eslint.detect-object-injection
97
78
  }
98
79
  return value;
99
80
  };
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/guards/helpers.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,UAAU,GACtB,CAAqC,IAAY,EAA8B,EAAE,CACjF,CAAC,EAAE,OAAO,EAAyB,EAAE,EAAE;IACtC,MAAM,KAAK,GAAG,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC5C,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;AAC9D,CAAC,CAAC;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,YAAY,GACxB,CAAuC,SAAiB,EAA0B,EAAE,CACpF,CAAC,EAAE,KAAK,EAAqB,EAAE,EAAE;IAChC,OAAO,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC;AACjC,CAAC,CAAC;AAEH;;;;;;;;;;;;;;;GAeG;AACH,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAoC,EAAE;IACrE,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;AACpD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GACxB,CAAqC,UAAkB,EAA8B,EAAE,CACvF,CAAC,EAAE,OAAO,EAAyB,EAAE,EAAE;IACtC,kDAAkD;IAClD,qEAAqE;IACrE,8DAA8D;IAC9D,IAAI,YAAqB,CAAC;IAC1B,IAAI,QAAQ,CAAC,OAAO,CAAC,IAAI,QAAQ,IAAI,OAAO,EAAE,CAAC;QAC9C,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAC/B,CAAC;SAAM,IAAI,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,IAAI,OAAO,EAAE,CAAC;QACpD,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC;IAC9B,CAAC;IAED,IAAI,CAAC,YAAY;QAAE,OAAO,KAAK,CAAC;IAEhC,6DAA6D;IAC7D,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;QACtC,OAAO,YAAY,KAAK,UAAU,CAAC;IACpC,CAAC;IAED,wBAAwB;IACxB,OAAO,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;AAC1D,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,cAAc,GAAG,CAAC,GAAY,EAAE,IAAY,EAAW,EAAE;IAC9D,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,KAAK,GAAG,GAAG,CAAC;IAEhB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACxB,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YACxE,OAAO,SAAS,CAAC;QAClB,CAAC;QACD,KAAK,GAAI,KAAiC,CAAC,GAAG,CAAC,CAAC;IACjD,CAAC;IAED,OAAO,KAAK,CAAC;AACd,CAAC,CAAC"}
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/guards/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAIrC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,UAAU,GACtB,CAAqC,IAAY,EAA8B,EAAE,CACjF,CAAC,EAAE,OAAO,EAAyB,EAAE,EAAE;IACtC,MAAM,KAAK,GAAG,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC5C,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;AAC9D,CAAC,CAAC;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,YAAY,GACxB,CAAuC,SAAiB,EAA0B,EAAE,CACpF,CAAC,EAAE,KAAK,EAAqB,EAAE,EAAE;IAChC,OAAO,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC;AACjC,CAAC,CAAC;AAEH;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAC/B,CACC,SAAiB,EACjB,aAAsB,EACO,EAAE,CAChC,CAAC,EAAE,OAAO,EAAyB,EAAE,EAAE;IACtC,MAAM,MAAM,GAAG,cAAc,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAClD,OAAO,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AACtC,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,cAAc,GAAG,CAAC,GAAY,EAAE,IAAY,EAAW,EAAE;IAC9D,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,KAAK,GAAG,GAAG,CAAC;IAEhB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACxB,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YACxE,OAAO,SAAS,CAAC;QAClB,CAAC;QACD,KAAK,GAAI,KAAiC,CAAC,GAAG,CAAC,CAAC,CAAC,mDAAmD;IACrG,CAAC;IAED,OAAO,KAAK,CAAC;AACd,CAAC,CAAC"}
@@ -7,6 +7,7 @@
7
7
  * @packageDocumentation
8
8
  */
9
9
  export { composeGuards, composeGuardsOr, negateGuard } from "./compose.js";
10
- export { hasContext, eventMatches, stateMatches } from "./helpers.js";
10
+ export type { ComposedGuard } from "./compose.js";
11
+ export { hasContext, eventMatches, contextFieldMatches } from "./helpers.js";
11
12
  export type { Guard, GuardArray } from "./types.js";
12
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/guards/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACtE,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/guards/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3E,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAC7E,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
@@ -7,5 +7,5 @@
7
7
  * @packageDocumentation
8
8
  */
9
9
  export { composeGuards, composeGuardsOr, negateGuard } from "./compose.js";
10
- export { hasContext, eventMatches, stateMatches } from "./helpers.js";
10
+ export { hasContext, eventMatches, contextFieldMatches } from "./helpers.js";
11
11
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/guards/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/guards/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3E,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC"}
@@ -18,5 +18,5 @@ export type Guard<TContext = Record<string, unknown>, TEvent = PlayEvent> = (arg
18
18
  *
19
19
  * Per CONTEXT.md: Array means AND - all guards must pass
20
20
  */
21
- export type GuardArray<TContext = Record<string, unknown>, TEvent = PlayEvent> = Array<Guard<TContext, TEvent>> | Array<string>;
21
+ export type GuardArray<TContext = Record<string, unknown>, TEvent = PlayEvent> = Array<Guard<TContext, TEvent> | string>;
22
22
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/guards/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD;;;;;;;;;GASG;AACH,MAAM,MAAM,KAAK,CAAC,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,IAAI,CAAC,IAAI,EAAE;IAClF,OAAO,EAAE,QAAQ,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACd,KAAK,OAAO,CAAC;AAEd;;;;GAIG;AACH,MAAM,MAAM,UAAU,CAAC,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,IAC1E,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,GAC9B,KAAK,CAAC,MAAM,CAAC,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/guards/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD;;;;;;;;;GASG;AACH,MAAM,MAAM,KAAK,CAAC,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,IAAI,CAAC,IAAI,EAAE;IAClF,OAAO,EAAE,QAAQ,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACd,KAAK,OAAO,CAAC;AAEd;;;;GAIG;AACH,MAAM,MAAM,UAAU,CAAC,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,IAAI,KAAK,CACrF,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG,MAAM,CAChC,CAAC"}
package/dist/index.d.ts CHANGED
@@ -2,9 +2,9 @@
2
2
  * @xmachines/play-xstate - XState v5 adapter for Play Architecture
3
3
  *
4
4
  * Provides definePlayer() API for binding XState state machines to the
5
- * actor base with catalog binding, signal lifecycle, and DevTools integration.
5
+ * actor base with signal lifecycle and DevTools integration.
6
6
  *
7
- * Per RFC Play v1, this package implements the Logic Layer adapter that
7
+ * Per the Play RFC, this package implements the Logic Layer adapter that
8
8
  * transforms declarative machine definitions into live actors with signal-driven
9
9
  * reactivity.
10
10
  *
@@ -12,13 +12,9 @@
12
12
  */
13
13
  export { definePlayer } from "./define-player.js";
14
14
  export { PlayerActor } from "./player-actor.js";
15
- export type { PlayerConfig, PlayerOptions, PlayerFactory } from "./types.js";
16
- export { composeGuards, composeGuardsOr, negateGuard, hasContext, eventMatches, stateMatches, } from "./guards/index.js";
17
- export type { Guard, GuardArray } from "./guards/types.js";
15
+ export type { PlayerConfig, PlayerOptions, PlayerFactory, PlayerFactoryResumeOptions, } from "./types.js";
16
+ export { composeGuards, composeGuardsOr, negateGuard, hasContext, eventMatches, contextFieldMatches, } from "./guards/index.js";
17
+ export type { Guard, GuardArray, ComposedGuard } from "./guards/index.js";
18
18
  export { deriveRoute, isAbsoluteRoute, buildRouteUrl, formatPlayRouteTransitions, } from "./routing/index.js";
19
- export type { RouteMachineConfig, RouteStateNode } from "./routing/index.js";
20
- export type { RouteContext } from "./routing/types.js";
21
- export { validateComponentBinding, validateViewProps, mergeViewProps } from "./catalog/index.js";
22
- export type { ValidationResult, ViewMergeContext } from "./catalog/index.js";
23
- export type { Catalog, CatalogEntry } from "./catalog/types.js";
19
+ export type { RouteMachineConfig, RouteStateNode, RouteContext, RouteObject, RouteMetadata, } from "./routing/index.js";
24
20
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAG7E,OAAO,EACN,aAAa,EACb,eAAe,EACf,WAAW,EACX,UAAU,EACV,YAAY,EACZ,YAAY,GACZ,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAG3D,OAAO,EACN,WAAW,EACX,eAAe,EACf,aAAa,EACb,0BAA0B,GAC1B,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC7E,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGvD,OAAO,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACjG,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC7E,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EACX,YAAY,EACZ,aAAa,EACb,aAAa,EACb,0BAA0B,GAC1B,MAAM,YAAY,CAAC;AAGpB,OAAO,EACN,aAAa,EACb,eAAe,EACf,WAAW,EACX,UAAU,EACV,YAAY,EACZ,mBAAmB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAG1E,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,GACb,MAAM,oBAAoB,CAAC"}
package/dist/index.js CHANGED
@@ -2,9 +2,9 @@
2
2
  * @xmachines/play-xstate - XState v5 adapter for Play Architecture
3
3
  *
4
4
  * Provides definePlayer() API for binding XState state machines to the
5
- * actor base with catalog binding, signal lifecycle, and DevTools integration.
5
+ * actor base with signal lifecycle and DevTools integration.
6
6
  *
7
- * Per RFC Play v1, this package implements the Logic Layer adapter that
7
+ * Per the Play RFC, this package implements the Logic Layer adapter that
8
8
  * transforms declarative machine definitions into live actors with signal-driven
9
9
  * reactivity.
10
10
  *
@@ -13,9 +13,7 @@
13
13
  export { definePlayer } from "./define-player.js";
14
14
  export { PlayerActor } from "./player-actor.js";
15
15
  // Guard utilities
16
- export { composeGuards, composeGuardsOr, negateGuard, hasContext, eventMatches, stateMatches, } from "./guards/index.js";
16
+ export { composeGuards, composeGuardsOr, negateGuard, hasContext, eventMatches, contextFieldMatches, } from "./guards/index.js";
17
17
  // Routing utilities
18
18
  export { deriveRoute, isAbsoluteRoute, buildRouteUrl, formatPlayRouteTransitions, } from "./routing/index.js";
19
- // Catalog utilities
20
- export { validateComponentBinding, validateViewProps, mergeViewProps } from "./catalog/index.js";
21
19
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhD,kBAAkB;AAClB,OAAO,EACN,aAAa,EACb,eAAe,EACf,WAAW,EACX,UAAU,EACV,YAAY,EACZ,YAAY,GACZ,MAAM,mBAAmB,CAAC;AAG3B,oBAAoB;AACpB,OAAO,EACN,WAAW,EACX,eAAe,EACf,aAAa,EACb,0BAA0B,GAC1B,MAAM,oBAAoB,CAAC;AAI5B,oBAAoB;AACpB,OAAO,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAQhD,kBAAkB;AAClB,OAAO,EACN,aAAa,EACb,eAAe,EACf,WAAW,EACX,UAAU,EACV,YAAY,EACZ,mBAAmB,GACnB,MAAM,mBAAmB,CAAC;AAG3B,oBAAoB;AACpB,OAAO,EACN,WAAW,EACX,eAAe,EACf,aAAa,EACb,0BAA0B,GAC1B,MAAM,oBAAoB,CAAC"}
@@ -1,9 +1,7 @@
1
- import { type AnyStateMachine, type Actor, type AnyActorLogic, type AnyMachineSnapshot } from "xstate";
2
- import { AbstractActor, type Routable, type Viewable, type ViewMetadata } from "@xmachines/play-actor";
1
+ import { type AnyStateMachine, type Actor, type AnyActorLogic, type EmittedFrom, type InputFrom, type Observer, type Snapshot, type SnapshotFrom, type Subscription, type EventFromLogic } from "xstate";
2
+ import { AbstractActor, type Routable, type Viewable, type PlaySpec } from "@xmachines/play-actor";
3
3
  import { Signal } from "@xmachines/play-signals";
4
- import type { PlayEvent } from "@xmachines/play";
5
4
  import type { PlayerOptions } from "./types.js";
6
- import type { Catalog } from "./catalog/types.js";
7
5
  /**
8
6
  * Concrete XState actor implementing Play Architecture signal protocol
9
7
  *
@@ -48,7 +46,7 @@ import type { Catalog } from "./catalog/types.js";
48
46
  * ```
49
47
  *
50
48
  * @example
51
- * Signal lifecycle with watchers (microtask batching)
49
+ * Signal lifecycle with watchers
52
50
  * ```typescript
53
51
  * import { Signal } from "@xmachines/play-signals";
54
52
  *
@@ -61,10 +59,10 @@ import type { Catalog } from "./catalog/types.js";
61
59
  *
62
60
  * watcher.watch(actor.state);
63
61
  * actor.send({ type: 'play.route', to: '#about' });
64
- * // Watcher notification scheduled via microtask
62
+ * // Watcher notification scheduled via microtask by the watcher itself
65
63
  * ```
66
64
  *
67
- * @see {@link https://gitlab.com/xmachin-es/rfc/-/blob/main/src/play-v1.md | RFC Play v1}
65
+ * @see [Play RFC](../../docs/rfc/play.md)
68
66
  * @see {@link definePlayer} for factory creation
69
67
  * @see {@link @xmachines/play-actor!AbstractActor} for signal protocol
70
68
  * @see {@link @xmachines/play-actor!Routable} for routing capability
@@ -79,19 +77,87 @@ import type { Catalog } from "./catalog/types.js";
79
77
  * `Signal.Computed`) to ensure proper watcher propagation in PlayRenderer. Views are
80
78
  * cached and updated at state entry, not computed on every read.
81
79
  */
82
- export declare class PlayerActor<TMachine extends AnyStateMachine> extends AbstractActor<AnyActorLogic> implements Routable, Viewable {
83
- [key: string]: unknown;
84
- private _xstateActor;
85
- private _stateManager;
86
- private _options;
87
- private _catalog;
88
- private _viewSignal;
89
- private _lastViewMemo;
90
- state: Signal.State<AnyMachineSnapshot>;
80
+ export declare class PlayerActor<TMachine extends AnyStateMachine> extends AbstractActor<AnyActorLogic, EventFromLogic<TMachine>> implements Routable, Viewable {
81
+ private xstateActor;
82
+ private playerOptions;
83
+ private viewSignal;
84
+ /** Last spec emitted on the view signal — used to skip no-change re-emissions. */
85
+ private lastEmittedView;
86
+ state: Signal.State<ReturnType<TMachine["transition"]>>;
87
+ /**
88
+ * Returns whether the actor's current state can accept the given event.
89
+ *
90
+ * Typed to the machine's event union — passing an unknown event type is a
91
+ * compile error. Delegates to the underlying XState actor's snapshot.
92
+ *
93
+ * @example
94
+ * ```typescript
95
+ * if (actor.can({ type: "auth.logout" })) { ... }
96
+ * ```
97
+ */
98
+ can(event: EventFromLogic<TMachine>): boolean;
99
+ /**
100
+ * A TC39 `Signal.Computed` that derives the current URL path from the active
101
+ * machine state's `meta.route` template and the actor's context.
102
+ *
103
+ * Returns `null` when the current state has no `meta.route`, or when the route
104
+ * template cannot be fully resolved (e.g. a required parameter is absent from
105
+ * context).
106
+ *
107
+ * @throws {MissingRouteParamError} When a required `:param` placeholder in the
108
+ * route template has no matching value in the actor's context. Import the class
109
+ * from `@xmachines/play-xstate/errors`.
110
+ *
111
+ * @example
112
+ * ```typescript
113
+ * // Returns "/profile/alice" when context.userId === "alice"
114
+ * const route = actor.currentRoute.get();
115
+ * ```
116
+ */
91
117
  currentRoute: Signal.Computed<string | null>;
92
- currentView: Signal.State<ViewMetadata | null>;
93
- catalog: Catalog;
94
- constructor(machine: TMachine, catalog: Catalog | null | undefined, options: PlayerOptions<TMachine>, input?: any);
118
+ /**
119
+ * The route derived from the machine's initial state — fixed at construction,
120
+ * never changes even when the actor is restored from a snapshot.
121
+ *
122
+ * Router bridges compare this against the browser URL to distinguish a deep-link
123
+ * (non-initial URL → router wins) from a restore (initial URL + actor at a
124
+ * different restored route → actor wins).
125
+ *
126
+ * Derived statically from the machine definition via `deriveInitialRoute`
127
+ * (XState's pure `initialTransition` helper): the initial state chain and its
128
+ * `meta.route` templates are fixed at machine definition time, while `:param`
129
+ * substitution uses the machine's real initial context for this actor's `input`.
130
+ * No extra actor is ever created, and a restored snapshot never influences the
131
+ * value — it is always the machine's **default** initial route.
132
+ */
133
+ readonly initialRoute: string | null;
134
+ /**
135
+ * Reactive signal containing the current view spec derived from the active state's
136
+ * `meta.view` metadata.
137
+ *
138
+ * Emits a **fresh object reference** whenever the rendered view actually changes —
139
+ * a different state's view, or a param/context change that alters the resolved
140
+ * spec (including `reenter: true` re-entries with new params). Snapshots that do
141
+ * not change the rendered view (e.g. context-only assigns) keep the previous
142
+ * reference so downstream providers do not remount the UI on every event.
143
+ *
144
+ * The emitted `PlaySpec` has its element `props` enriched with `context.params`
145
+ * before emission — URL path parameters (e.g. `:section?`) flow into component props
146
+ * automatically. See `mergeRouteParamsIntoProps` for the merge priority rules.
147
+ *
148
+ * Returns `null` when the current state has no `meta.view` metadata.
149
+ *
150
+ * @example
151
+ * ```typescript
152
+ * const view = actor.currentView.get();
153
+ * if (view) {
154
+ * console.log(view.root); // e.g. "root"
155
+ * console.log(view.elements); // @xmachines/json-render-core Spec elements
156
+ * }
157
+ * ```
158
+ */
159
+ currentView: Signal.State<PlaySpec | null>;
160
+ constructor(machine: TMachine, options: PlayerOptions<TMachine>, input?: InputFrom<TMachine>, restoredSnapshot?: SnapshotFrom<TMachine>);
95
161
  /**
96
162
  * Start the actor
97
163
  *
@@ -103,27 +169,67 @@ export declare class PlayerActor<TMachine extends AnyStateMachine> extends Abstr
103
169
  */
104
170
  stop(): this;
105
171
  /**
106
- * Send event to actor
172
+ * Send an event to the underlying XState actor.
107
173
  *
108
- * Forwards events to the underlying XState actor. The actor's state machine
109
- * guards determine whether each event is valid from the current state.
174
+ * The actor's state machine guards decide whether the event causes a transition.
175
+ * Pass any event from the machine's event union — domain events, routing events, etc.
110
176
  *
111
- * @param event - Any event object with a type property
177
+ * @param event - An event from the machine's `EventFromLogic<TMachine>` union.
178
+ *
179
+ * @throws {InvalidEventError} When `event` is not a plain object (`null`, `undefined`,
180
+ * a string, number, etc.). Import the class from `@xmachines/play-xstate/errors`.
112
181
  *
113
182
  * @example
114
183
  * ```typescript
115
- * // Domain event
116
- * actor.send({ type: 'auth.login', userId: '123' });
184
+ * // Domain event (typed to machine's event union)
185
+ * actor.send({ type: "auth.login", userId: "123" });
117
186
  *
118
187
  * // Routing event
119
- * actor.send({ type: 'play.route', to: '#home' });
188
+ * actor.send({ type: "play.route", to: "#home" });
120
189
  * ```
121
190
  */
122
- send(event: PlayEvent): void;
191
+ send(event: EventFromLogic<TMachine>): void;
123
192
  /**
124
193
  * Get current snapshot
125
194
  */
126
195
  getSnapshot(): ReturnType<Actor<TMachine>["getSnapshot"]>;
196
+ /**
197
+ * Subscribe to snapshot updates from the wrapped XState actor.
198
+ *
199
+ * Accepts an observer object, exactly like XState's `Actor.subscribe`.
200
+ *
201
+ * @param observer - Observer with `next`/`error`/`complete` handlers.
202
+ * @returns Subscription with an `unsubscribe()` method.
203
+ */
204
+ subscribe(observer: Observer<SnapshotFrom<TMachine>>): Subscription;
205
+ /**
206
+ * Subscribe to snapshot updates from the wrapped XState actor.
207
+ *
208
+ * Accepts listener functions, exactly like XState's `Actor.subscribe`.
209
+ *
210
+ * @param nextListener - Snapshot listener function.
211
+ * @param errorListener - Called when the actor errors.
212
+ * @param completeListener - Called when the actor completes (reaches a final state).
213
+ * @returns Subscription with an `unsubscribe()` method.
214
+ */
215
+ subscribe(nextListener?: (snapshot: SnapshotFrom<TMachine>) => void, errorListener?: (error: unknown) => void, completeListener?: () => void): Subscription;
216
+ /**
217
+ * Listen for events emitted by the wrapped XState actor via the `emit` action.
218
+ *
219
+ * @param type - Emitted event type to listen for, or `"*"` for all.
220
+ * @param handler - Called with each matching emitted event.
221
+ * @returns Subscription with an `unsubscribe()` method.
222
+ */
223
+ on<TType extends EmittedFrom<TMachine>["type"] | "*">(type: TType, handler: (emitted: EmittedFrom<TMachine> & (TType extends "*" ? unknown : {
224
+ type: TType;
225
+ })) => void): Subscription;
226
+ /**
227
+ * Get the persisted snapshot of the wrapped XState actor.
228
+ *
229
+ * Suitable for serialization and later restoration via the factory's
230
+ * `restore.snapshot` option.
231
+ */
232
+ getPersistedSnapshot(): Snapshot<unknown>;
127
233
  /**
128
234
  * Validate view at state entry and cache result
129
235
  *
@@ -132,7 +238,7 @@ export declare class PlayerActor<TMachine extends AnyStateMachine> extends Abstr
132
238
  *
133
239
  * @param snapshot - Current XState snapshot
134
240
  */
135
- private _validateAndCacheView;
241
+ private validateAndCacheView;
136
242
  /**
137
243
  * Convenience dispose method for cleanup
138
244
  *
@@ -1 +1 @@
1
- {"version":3,"file":"player-actor.d.ts","sourceRoot":"","sources":["../src/player-actor.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,eAAe,EACpB,KAAK,KAAK,EACV,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,MAAM,QAAQ,CAAC;AAChB,OAAO,EACN,aAAa,EACb,KAAK,QAAQ,EACb,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAQhD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAkBlD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0EG;AACH,qBAAa,WAAW,CAAC,QAAQ,SAAS,eAAe,CACxD,SAAQ,aAAa,CAAC,aAAa,CACnC,YAAW,QAAQ,EAAE,QAAQ;IAE7B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAEvB,OAAO,CAAC,YAAY,CAAkB;IACtC,OAAO,CAAC,aAAa,CAAyC;IAC9D,OAAO,CAAC,QAAQ,CAA0B;IAC1C,OAAO,CAAC,QAAQ,CAAU;IAC1B,OAAO,CAAC,WAAW,CAAoC;IACvD,OAAO,CAAC,aAAa,CAKZ;IAGF,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;IACxC,YAAY,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAC/C,OAAO,EAAE,OAAO,CAAC;gBAGvB,OAAO,EAAE,QAAQ,EACjB,OAAO,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,EACnC,OAAO,EAAE,aAAa,CAAC,QAAQ,CAAC,EAChC,KAAK,CAAC,EAAE,GAAG;IAiFZ;;;;OAIG;IACM,KAAK,IAAI,IAAI;IAWtB;;OAEG;IACM,IAAI,IAAI,IAAI;IAYrB;;;;;;;;;;;;;;;;OAgBG;IACM,IAAI,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAsBrC;;OAEG;IACM,WAAW,IAAI,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,aAAa,CAAC,CAAC;IAIlE;;;;;;;OAOG;IACH,OAAO,CAAC,qBAAqB;IAsG7B;;;;OAIG;IACH,OAAO,IAAI,IAAI;CAGf"}
1
+ {"version":3,"file":"player-actor.d.ts","sourceRoot":"","sources":["../src/player-actor.ts"],"names":[],"mappings":"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"}