@xmachines/play-tanstack-react-router 1.0.0-beta.1

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 (203) hide show
  1. package/.oxfmtrc.json +3 -0
  2. package/.oxlintrc.json +3 -0
  3. package/README.md +177 -0
  4. package/dist/extract-params.d.ts +45 -0
  5. package/dist/extract-params.d.ts.map +1 -0
  6. package/dist/extract-params.js +70 -0
  7. package/dist/extract-params.js.map +1 -0
  8. package/dist/index.d.ts +18 -0
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/index.js +17 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/play-router-provider.d.ts +33 -0
  13. package/dist/play-router-provider.d.ts.map +1 -0
  14. package/dist/play-router-provider.js +31 -0
  15. package/dist/play-router-provider.js.map +1 -0
  16. package/dist/route-map.d.ts +101 -0
  17. package/dist/route-map.d.ts.map +1 -0
  18. package/dist/route-map.js +139 -0
  19. package/dist/route-map.js.map +1 -0
  20. package/dist/tanstack-router-bridge.d.ts +115 -0
  21. package/dist/tanstack-router-bridge.d.ts.map +1 -0
  22. package/dist/tanstack-router-bridge.js +112 -0
  23. package/dist/tanstack-router-bridge.js.map +1 -0
  24. package/dist/types.d.ts +26 -0
  25. package/dist/types.d.ts.map +1 -0
  26. package/dist/types.js +7 -0
  27. package/dist/types.js.map +1 -0
  28. package/dist/utils.d.ts +9 -0
  29. package/dist/utils.d.ts.map +1 -0
  30. package/dist/utils.js +10 -0
  31. package/dist/utils.js.map +1 -0
  32. package/examples/demo/README.md +100 -0
  33. package/examples/demo/docs/ARCHITECTURE.md +643 -0
  34. package/examples/demo/docs/INVARIANTS.md +461 -0
  35. package/examples/demo/docs/SWAP-REACT.md +635 -0
  36. package/examples/demo/index.html +16 -0
  37. package/examples/demo/package.json +39 -0
  38. package/examples/demo/src/App.tsx +148 -0
  39. package/examples/demo/src/components/About.tsx +49 -0
  40. package/examples/demo/src/components/Contact.tsx +43 -0
  41. package/examples/demo/src/components/Dashboard.tsx +46 -0
  42. package/examples/demo/src/components/DebugPanel.tsx +68 -0
  43. package/examples/demo/src/components/HeaderNav.tsx +103 -0
  44. package/examples/demo/src/components/Home.tsx +41 -0
  45. package/examples/demo/src/components/Login.tsx +82 -0
  46. package/examples/demo/src/components/Navigation.tsx +262 -0
  47. package/examples/demo/src/components/Profile.tsx +46 -0
  48. package/examples/demo/src/components/Register.tsx +109 -0
  49. package/examples/demo/src/components/Settings.tsx +92 -0
  50. package/examples/demo/src/components/index.ts +16 -0
  51. package/examples/demo/src/main.tsx +20 -0
  52. package/examples/demo/test/actor-authority.test.ts +50 -0
  53. package/examples/demo/test/browser/__screenshots__/back-button-duplicate.browser.test.tsx/Browser-back-button-navigates-through-unique-history--no-duplicates--1.png +0 -0
  54. package/examples/demo/test/browser/__screenshots__/back-button-duplicate.browser.test.tsx/GAP-12--navigation-via-goto---events-creates-single-history-entries-1.png +0 -0
  55. package/examples/demo/test/browser/__screenshots__/back-button-duplicate.browser.test.tsx/GAP-12--navigation-via-goto---events-creates-single-history-entries-2.png +0 -0
  56. package/examples/demo/test/browser/__screenshots__/back-forward-sync.browser.test.tsx/Back-Forward--After-authentication-flow-1.png +0 -0
  57. package/examples/demo/test/browser/__screenshots__/back-forward-sync.browser.test.tsx/Back-Forward--Multiple-rapid-navigations-1.png +0 -0
  58. package/examples/demo/test/browser/__screenshots__/back-forward-sync.browser.test.tsx/Back-Forward--Multiple-rapid-navigations-2.png +0 -0
  59. package/examples/demo/test/browser/__screenshots__/back-forward-sync.browser.test.tsx/Back-Forward--URL-stays-in-sync-with-actor-state-1.png +0 -0
  60. package/examples/demo/test/browser/__screenshots__/back-forward-sync.browser.test.tsx/Back-Forward--URL-stays-in-sync-with-actor-state-2.png +0 -0
  61. package/examples/demo/test/browser/__screenshots__/back-forward-sync.browser.test.tsx/Back-Forward--Works-correctly-1.png +0 -0
  62. package/examples/demo/test/browser/__screenshots__/back-forward-sync.browser.test.tsx/Back-Forward--Works-correctly-2.png +0 -0
  63. package/examples/demo/test/browser/__screenshots__/direct-navigation.browser.test.ts/Direct-navigation-to--about-loads-about-page-1.png +0 -0
  64. package/examples/demo/test/browser/__screenshots__/direct-navigation.browser.test.ts/Direct-navigation-to--contact-loads-contact-page-1.png +0 -0
  65. package/examples/demo/test/browser/__screenshots__/direct-navigation.browser.test.ts/Direct-navigation-to--home-loads-home-page-1.png +0 -0
  66. package/examples/demo/test/browser/__screenshots__/direct-navigation.browser.test.ts/Direct-navigation-to-protected-route-while-authenticated-loads-dashboard-1.png +0 -0
  67. package/examples/demo/test/browser/__screenshots__/direct-navigation.browser.test.ts/Direct-navigation-to-protected-route-while-unauthenticated-redirects-to-login-1.png +0 -0
  68. package/examples/demo/test/browser/__screenshots__/exact-user-scenario.browser.test.tsx/Debug--Print-history-after-each-navigation-1.png +0 -0
  69. package/examples/demo/test/browser/__screenshots__/exact-user-scenario.browser.test.tsx/Debug--Print-history-after-each-navigation-2.png +0 -0
  70. package/examples/demo/test/browser/__screenshots__/exact-user-scenario.browser.test.tsx/EXACT-USER-SCENARIO--home---about---home---contact---home--then-back-3x-should-land-on-about-1.png +0 -0
  71. package/examples/demo/test/browser/__screenshots__/exact-user-scenario.browser.test.tsx/EXACT-USER-SCENARIO--home---about---home---contact---home--then-back-3x-should-land-on-about-2.png +0 -0
  72. package/examples/demo/test/browser/__screenshots__/guard-rejection.browser.test.tsx/E2E--Actor-Authority---infrastructure-cannot-override-guards-1.png +0 -0
  73. package/examples/demo/test/browser/__screenshots__/guard-rejection.browser.test.tsx/E2E--Actor-Authority---infrastructure-cannot-override-guards-2.png +0 -0
  74. package/examples/demo/test/browser/__screenshots__/guard-rejection.browser.test.tsx/E2E--Guards-reject-invalid-navigation-1.png +0 -0
  75. package/examples/demo/test/browser/__screenshots__/guard-rejection.browser.test.tsx/E2E--Guards-reject-invalid-navigation-2.png +0 -0
  76. package/examples/demo/test/browser/__screenshots__/history-investigation.browser.test.tsx/baseHistory-back---navigation--avoiding-window-history--1.png +0 -0
  77. package/examples/demo/test/browser/__screenshots__/history-investigation.browser.test.tsx/baseHistory-back---navigation--avoiding-window-history--2.png +0 -0
  78. package/examples/demo/test/browser/__screenshots__/history-navigation.browser.test.ts/GAP-12--Back-forward-with-guard-transitions---authenticated-user-1.png +0 -0
  79. package/examples/demo/test/browser/__screenshots__/history-navigation.browser.test.ts/GAP-12--Back-forward-with-guard-transitions---authenticated-user-2.png +0 -0
  80. package/examples/demo/test/browser/__screenshots__/history-navigation.browser.test.ts/GAP-12--Back-forward-with-guard-transitions---unauthenticated-user-1.png +0 -0
  81. package/examples/demo/test/browser/__screenshots__/history-navigation.browser.test.ts/GAP-12--Back-forward-with-guard-transitions---unauthenticated-user-2.png +0 -0
  82. package/examples/demo/test/browser/__screenshots__/history-navigation.browser.test.ts/GAP-12--Back-with-guard---authenticated-user-navigates-back-1.png +0 -0
  83. package/examples/demo/test/browser/__screenshots__/history-navigation.browser.test.ts/GAP-12--Back-with-guard---authenticated-user-navigates-back-2.png +0 -0
  84. package/examples/demo/test/browser/__screenshots__/history-navigation.browser.test.ts/GAP-12--Back-with-guard---unauthenticated-user-stays-on-public-routes-1.png +0 -0
  85. package/examples/demo/test/browser/__screenshots__/history-navigation.browser.test.ts/GAP-12--Back-with-guard---unauthenticated-user-stays-on-public-routes-2.png +0 -0
  86. package/examples/demo/test/browser/__screenshots__/history-navigation.browser.test.ts/GAP-12--Forward-button-after-back---unique-history-1.png +0 -0
  87. package/examples/demo/test/browser/__screenshots__/history-navigation.browser.test.ts/GAP-12--Forward-button-after-back---unique-history-2.png +0 -0
  88. package/examples/demo/test/browser/__screenshots__/history-navigation.browser.test.ts/GAP-12--Forward-button-after-back-1.png +0 -0
  89. package/examples/demo/test/browser/__screenshots__/history-navigation.browser.test.ts/GAP-12--Forward-button-after-back-2.png +0 -0
  90. package/examples/demo/test/browser/__screenshots__/history-navigation.browser.test.ts/GAP-12--Navigate-forward-then-back---unique-history-entries-1.png +0 -0
  91. package/examples/demo/test/browser/__screenshots__/history-navigation.browser.test.ts/GAP-12--Navigate-forward-then-back---unique-history-entries-2.png +0 -0
  92. package/examples/demo/test/browser/__screenshots__/history-navigation.browser.test.ts/GAP-12--Rapid-back-forward-navigation-doesn-t-cause-duplicate-entries-1.png +0 -0
  93. package/examples/demo/test/browser/__screenshots__/history-navigation.browser.test.ts/GAP-12--Rapid-back-forward-navigation-doesn-t-cause-duplicate-entries-2.png +0 -0
  94. package/examples/demo/test/browser/__screenshots__/history-navigation.browser.test.ts/GAP-12--Single-back-navigation---about-to-home-1.png +0 -0
  95. package/examples/demo/test/browser/__screenshots__/history-navigation.browser.test.ts/GAP-12--Single-back-navigation---about-to-home-2.png +0 -0
  96. package/examples/demo/test/browser/__screenshots__/history-navigation.browser.test.ts/GAP-12--Single-back-navigation---contact-to-about-1.png +0 -0
  97. package/examples/demo/test/browser/__screenshots__/history-navigation.browser.test.ts/GAP-12--Single-back-navigation---contact-to-about-2.png +0 -0
  98. package/examples/demo/test/browser/__screenshots__/history-navigation.browser.test.ts/GAP-12--View-syncs-with-URL-after-back-forward-1.png +0 -0
  99. package/examples/demo/test/browser/__screenshots__/history-navigation.browser.test.ts/GAP-12--View-syncs-with-URL-after-back-forward-2.png +0 -0
  100. package/examples/demo/test/browser/__screenshots__/history-navigation.browser.test.ts/GAP-12--View-syncs-with-URL-after-back-navigation-1.png +0 -0
  101. package/examples/demo/test/browser/__screenshots__/history-navigation.browser.test.ts/GAP-12--View-syncs-with-URL-after-back-navigation-2.png +0 -0
  102. package/examples/demo/test/browser/__screenshots__/login-flow.browser.test.tsx/E2E--User-can-log-in-and-see-dashboard-1.png +0 -0
  103. package/examples/demo/test/browser/__screenshots__/login-flow.browser.test.tsx/E2E--User-can-log-in-and-see-dashboard-2.png +0 -0
  104. package/examples/demo/test/browser/__screenshots__/navigation.browser.test.tsx/E2E--Navigation-reflects-actor-state-transitions-1.png +0 -0
  105. package/examples/demo/test/browser/__screenshots__/navigation.browser.test.tsx/E2E--Navigation-reflects-actor-state-transitions-2.png +0 -0
  106. package/examples/demo/test/browser/__screenshots__/protected-route-navigation.browser.test.tsx/Browser-back-forward-through-multiple-protected-routes-1.png +0 -0
  107. package/examples/demo/test/browser/__screenshots__/protected-route-navigation.browser.test.tsx/Browser-back-forward-through-multiple-protected-routes-2.png +0 -0
  108. package/examples/demo/test/browser/__screenshots__/protected-route-navigation.browser.test.tsx/Browser-back-navigates-from-dashboard-to-settings--protected-route--1.png +0 -0
  109. package/examples/demo/test/browser/__screenshots__/protected-route-navigation.browser.test.tsx/Browser-back-navigates-from-dashboard-to-settings--protected-route--2.png +0 -0
  110. package/examples/demo/test/browser/__screenshots__/protected-route-navigation.browser.test.tsx/RED--Browser-back-forward-through-multiple-protected-routes-1.png +0 -0
  111. package/examples/demo/test/browser/__screenshots__/protected-route-navigation.browser.test.tsx/RED--Browser-back-from-dashboard-to-settings--protected-route--1.png +0 -0
  112. package/examples/demo/test/browser/__screenshots__/protected-route-navigation.browser.test.tsx/RED--Browser-back-navigates-from-dashboard-to-settings--protected-route--1.png +0 -0
  113. package/examples/demo/test/browser/__screenshots__/settings-parameter.browser.test.tsx/Settings-Parameter-Display-should-display--account--section-when-navigating-to--settings-account-1.png +0 -0
  114. package/examples/demo/test/browser/__screenshots__/settings-parameter.browser.test.tsx/Settings-Parameter-Display-should-display--account--section-when-navigating-to--settings-account-2.png +0 -0
  115. package/examples/demo/test/browser/__screenshots__/settings-parameter.browser.test.tsx/Settings-Parameter-Display-should-display--general--section-when-navigating-to--settings--no-parameter--1.png +0 -0
  116. package/examples/demo/test/browser/__screenshots__/settings-parameter.browser.test.tsx/Settings-Parameter-Display-should-display--general--section-when-navigating-to--settings--no-parameter--2.png +0 -0
  117. package/examples/demo/test/browser/__screenshots__/settings-parameter.browser.test.tsx/Settings-Parameter-Display-should-display--profile--section-when-navigating-to--settings-profile-1.png +0 -0
  118. package/examples/demo/test/browser/__screenshots__/settings-parameter.browser.test.tsx/Settings-Parameter-Display-should-display--profile--section-when-navigating-to--settings-profile-2.png +0 -0
  119. package/examples/demo/test/browser/__screenshots__/settings-parameter.browser.test.tsx/Settings-Parameter-Display-should-update-section-display-when-clicking-section-navigation-buttons-1.png +0 -0
  120. package/examples/demo/test/browser/__screenshots__/settings-parameter.browser.test.tsx/Settings-Parameter-Display-should-update-section-display-when-clicking-section-navigation-buttons-2.png +0 -0
  121. package/examples/demo/test/browser/__screenshots__/settings-query-freeze.browser.test.ts/Settings-with-query-parameters-works-correctly-1.png +0 -0
  122. package/examples/demo/test/browser/__screenshots__/settings-query-freeze.browser.test.ts/Settings-with-section-parameter-works-correctly-1.png +0 -0
  123. package/examples/demo/test/browser/__screenshots__/state-driven.browser.test.ts/DEMO-04--State-Driven-Reset---Browser-back-sends-event-to-actor-1.png +0 -0
  124. package/examples/demo/test/browser/__screenshots__/state-driven.browser.test.ts/DEMO-04--State-Driven-Reset---Browser-back-sends-event-to-actor-2.png +0 -0
  125. package/examples/demo/test/browser/__screenshots__/state-driven.browser.test.ts/DEMO-04b--Browser-navigation-with-SignalSyncedHistory-integration-1.png +0 -0
  126. package/examples/demo/test/browser/__screenshots__/state-driven.browser.test.ts/DEMO-04b--Browser-navigation-with-SignalSyncedHistory-integration-2.png +0 -0
  127. package/examples/demo/test/browser/__screenshots__/tanstack-integration.browser.test.tsx/TanStack-Router-Integration-renders-with-RouterProvider-context-1.png +0 -0
  128. package/examples/demo/test/browser/__screenshots__/tanstack-integration.browser.test.tsx/TanStack-Router-Integration-renders-with-RouterProvider-context-2.png +0 -0
  129. package/examples/demo/test/browser/__screenshots__/test-multiple-back.browser.test.tsx/Multiple-back--Navigate-forward-3x-then-back-3x-1.png +0 -0
  130. package/examples/demo/test/browser/__screenshots__/uat-xstate-route-regression.browser.test.ts/UAT-1--Opening-with--someinvalidstate-stays-at-current-state-1.png +0 -0
  131. package/examples/demo/test/browser/__screenshots__/uat-xstate-route-regression.browser.test.ts/UAT-2--Opening-with--about-renders-About-component--not-Login-1.png +0 -0
  132. package/examples/demo/test/browser/__screenshots__/uat-xstate-route-regression.browser.test.ts/UAT-2b--Opening-with--home-renders-Home-component--not-Login-1.png +0 -0
  133. package/examples/demo/test/browser/__screenshots__/uat-xstate-route-regression.browser.test.ts/UAT-2c--Opening-with--contact-renders-Contact-component--not-Login-1.png +0 -0
  134. package/examples/demo/test/browser/__screenshots__/uat-xstate-route-regression.browser.test.ts/UAT-3--Back-forward-navigation---rendering-syncs-with-URL-1.png +0 -0
  135. package/examples/demo/test/browser/__screenshots__/uat-xstate-route-regression.browser.test.ts/UAT-3--Back-forward-navigation---rendering-syncs-with-URL-2.png +0 -0
  136. package/examples/demo/test/browser/__screenshots__/uat-xstate-route-regression.browser.test.ts/UAT-4--Auth-state-preserved-when-navigating-between-authenticated-anonymous-states-1.png +0 -0
  137. package/examples/demo/test/browser/__screenshots__/uat-xstate-route-regression.browser.test.ts/UAT-4--Auth-state-preserved-when-navigating-between-authenticated-anonymous-states-2.png +0 -0
  138. package/examples/demo/test/browser/__screenshots__/uat-xstate-route-regression.browser.test.ts/UAT-4b--Browser-back-forward-preserves-auth-state-1.png +0 -0
  139. package/examples/demo/test/browser/__screenshots__/uat-xstate-route-regression.browser.test.ts/UAT-4b--Browser-back-forward-preserves-auth-state-2.png +0 -0
  140. package/examples/demo/test/browser/__screenshots__/uat-xstate-route-regression.browser.test.ts/UAT-5--Protected-route-with-play-route-respects-authentication-guard-1.png +0 -0
  141. package/examples/demo/test/browser/__screenshots__/uat-xstate-route-regression.browser.test.ts/UAT-5--Protected-route-with-play-route-respects-authentication-guard-2.png +0 -0
  142. package/examples/demo/test/browser/__screenshots__/user-reported-scenario.browser.test.tsx/User-scenario--home---about---home---contact---home--then-back-3x-1.png +0 -0
  143. package/examples/demo/test/browser/__screenshots__/user-reported-scenario.browser.test.tsx/User-scenario--login---home---about---home---contact---home--then-back-3x-1.png +0 -0
  144. package/examples/demo/test/browser/__screenshots__/xstate-route-events.browser.test.ts/Browser-back-button-sends-play-route-event-with-correct-state-ID-1.png +0 -0
  145. package/examples/demo/test/browser/__screenshots__/xstate-route-events.browser.test.ts/Browser-back-button-sends-play-route-event-with-correct-state-ID-2.png +0 -0
  146. package/examples/demo/test/browser/__screenshots__/xstate-route-events.browser.test.ts/Browser-back-button-sends-xstate-route-event-with-correct-state-ID-1.png +0 -0
  147. package/examples/demo/test/browser/__screenshots__/xstate-route-events.browser.test.ts/Direct-URL-navigation-sends-play-route-event-1.png +0 -0
  148. package/examples/demo/test/browser/__screenshots__/xstate-route-events.browser.test.ts/Direct-URL-navigation-sends-xstate-route-event-1.png +0 -0
  149. package/examples/demo/test/browser/__screenshots__/xstate-route-events.browser.test.ts/Forward-button-sends-play-route-event-1.png +0 -0
  150. package/examples/demo/test/browser/__screenshots__/xstate-route-events.browser.test.ts/Forward-button-sends-play-route-event-2.png +0 -0
  151. package/examples/demo/test/browser/__screenshots__/xstate-route-events.browser.test.ts/Forward-button-sends-xstate-route-event-1.png +0 -0
  152. package/examples/demo/test/browser/__screenshots__/xstate-route-events.browser.test.ts/GAP-12-fix-preserved--No-duplicate-history-entries-with-play-route-1.png +0 -0
  153. package/examples/demo/test/browser/__screenshots__/xstate-route-events.browser.test.ts/GAP-12-fix-preserved--No-duplicate-history-entries-with-play-route-2.png +0 -0
  154. package/examples/demo/test/browser/__screenshots__/xstate-route-events.browser.test.ts/Protected-route-sends-xstate-route-with-authentication-guard-1.png +0 -0
  155. package/examples/demo/test/browser/back-button-duplicate.browser.test.tsx +148 -0
  156. package/examples/demo/test/browser/back-forward-sync.browser.test.tsx +149 -0
  157. package/examples/demo/test/browser/direct-navigation.browser.test.ts +146 -0
  158. package/examples/demo/test/browser/exact-user-scenario.browser.test.tsx +207 -0
  159. package/examples/demo/test/browser/guard-rejection.browser.test.tsx +52 -0
  160. package/examples/demo/test/browser/history-investigation.browser.test.tsx +82 -0
  161. package/examples/demo/test/browser/history-navigation.browser.test.ts +351 -0
  162. package/examples/demo/test/browser/login-flow.browser.test.tsx +34 -0
  163. package/examples/demo/test/browser/navigation.browser.test.tsx +34 -0
  164. package/examples/demo/test/browser/protected-route-navigation.browser.test.tsx +161 -0
  165. package/examples/demo/test/browser/redirect-url-update.browser.test.tsx +140 -0
  166. package/examples/demo/test/browser/settings-parameter.browser.test.tsx +164 -0
  167. package/examples/demo/test/browser/settings-query-freeze.browser.test.ts +141 -0
  168. package/examples/demo/test/browser/state-driven.browser.test.ts +112 -0
  169. package/examples/demo/test/browser/tanstack-integration.browser.test.tsx +61 -0
  170. package/examples/demo/test/browser/uat-xstate-route-regression.browser.test.ts +58 -0
  171. package/examples/demo/test/browser/xstate-route-events.browser.test.ts +293 -0
  172. package/examples/demo/test/browser-back-view-rendering.test.ts +104 -0
  173. package/examples/demo/test/browser-e2e/auth-flow.browser.test.tsx +49 -0
  174. package/examples/demo/test/invalid-route-redirect.test.ts +40 -0
  175. package/examples/demo/test/passive-infra.test.ts +35 -0
  176. package/examples/demo/test/route-parameters.test.ts +539 -0
  177. package/examples/demo/test/signal-only.test.ts +54 -0
  178. package/examples/demo/test/strict-separation.test.ts +37 -0
  179. package/examples/demo/test/test-utils.ts +49 -0
  180. package/examples/demo/tsconfig.json +21 -0
  181. package/examples/demo/tsconfig.tsbuildinfo +1 -0
  182. package/examples/demo/vite.config.ts +13 -0
  183. package/examples/demo/vitest.browser.config.ts +72 -0
  184. package/examples/demo/vitest.config.e2e.browser.ts +28 -0
  185. package/examples/demo/vitest.config.ts +35 -0
  186. package/package.json +51 -0
  187. package/src/extract-params.ts +75 -0
  188. package/src/index.ts +31 -0
  189. package/src/play-router-provider.tsx +46 -0
  190. package/src/route-map.ts +158 -0
  191. package/src/tanstack-router-bridge.ts +135 -0
  192. package/src/types.ts +26 -0
  193. package/src/utils.ts +12 -0
  194. package/test/browser/__screenshots__/signal-synced-history.browser.test.ts/Browser-back-button-sends-route-navigate-event-to-actor-1.png +0 -0
  195. package/test/browser/__screenshots__/signal-synced-history.browser.test.ts/SignalSyncedHistory-prevents-circular-updates-1.png +0 -0
  196. package/test/browser/__screenshots__/signal-synced-history.browser.test.ts/SignalSyncedHistory-syncs-actor-route-to-browser-URL-1.png +0 -0
  197. package/test/browser/signal-synced-history.browser.test.ts +95 -0
  198. package/test/route-map.test.ts +107 -0
  199. package/test/tanstack-router-bridge.test.ts +318 -0
  200. package/test/urlpattern-integration.test.ts +145 -0
  201. package/tsconfig.json +16 -0
  202. package/tsconfig.tsbuildinfo +1 -0
  203. package/vitest.config.ts +35 -0
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Main Application - Integration Layer
3
+ *
4
+ * Demonstrates complete Universal Player Architecture integration:
5
+ * - Actor Authority: Auth machine guards control navigation
6
+ * - Passive Infrastructure: Router observes actor.currentRoute signal
7
+ * - Signal-Only Reactivity: PlayRenderer watches signals for UI updates
8
+ * - Strict Separation: Business logic (machines/) has zero React imports
9
+ * - State-Driven Reset: Browser navigation sends events to actor
10
+ *
11
+ * This layer wires together business logic (auth-machine), infrastructure
12
+ * (TanStack Router), and view (React components) while maintaining strict
13
+ * separation of concerns.
14
+ */
15
+
16
+ import "@xmachines/play-router-shared/index.css";
17
+
18
+ import { useMemo, useEffect } from "react";
19
+ import { createRouter, createRootRoute } from "@tanstack/react-router";
20
+ import type { AbstractActor, Routable, Viewable } from "@xmachines/play-actor";
21
+ import { PlayRouterProvider } from "@xmachines/play-tanstack-react-router";
22
+ import { PlayRenderer } from "@xmachines/play-react";
23
+ import { extractMachineRoutes } from "@xmachines/play-router";
24
+ import { createRouteMapFromTree } from "@xmachines/play-tanstack-react-router";
25
+ import { definePlayer } from "@xmachines/play-xstate";
26
+ import { defineComponents } from "@xmachines/play-catalog";
27
+ import type { AnyActorLogic } from "xstate";
28
+ import { authMachine, catalog } from "@xmachines/play-router-shared";
29
+ import { Login } from "./components/Login.js";
30
+ import { Dashboard } from "./components/Dashboard.js";
31
+ import { Home } from "./components/Home.js";
32
+ import { About } from "./components/About.js";
33
+ import { Contact } from "./components/Contact.js";
34
+ import { Settings } from "./components/Settings.js";
35
+ import { Profile } from "./components/Profile.js";
36
+ import { Register } from "./components/Register.js";
37
+ import { Navigation } from "./components/Navigation.js";
38
+ import { DebugPanel } from "./components/DebugPanel.js";
39
+ import { HeaderNav } from "./components/HeaderNav.js";
40
+
41
+ /**
42
+ * Define React components matching catalog schema
43
+ *
44
+ * Type safety: defineComponents() ensures component props match catalog schemas.
45
+ * This enforces the contract between business logic (catalog) and view layer (React).
46
+ */
47
+ const components = defineComponents(catalog, {
48
+ Login,
49
+ Dashboard,
50
+ Home,
51
+ About,
52
+ Contact,
53
+ Settings,
54
+ Profile,
55
+ Register,
56
+ Navigation,
57
+ });
58
+
59
+ function Shell({
60
+ actor,
61
+ router,
62
+ }: {
63
+ actor: AbstractActor<AnyActorLogic> & Routable & Viewable;
64
+ router: unknown;
65
+ }) {
66
+ void router;
67
+
68
+ return (
69
+ <div className="demo-app">
70
+ <header className="demo-header">
71
+ <h1 className="demo-title">XMachines Play - React Demo</h1>
72
+ <HeaderNav actor={actor} />
73
+ </header>
74
+
75
+ <main className="demo-content">
76
+ <PlayRenderer actor={actor} components={components} />
77
+ </main>
78
+
79
+ <DebugPanel actor={actor} />
80
+ </div>
81
+ );
82
+ }
83
+
84
+ /**
85
+ * Main App component
86
+ *
87
+ * Architecture:
88
+ * 1. Creates actor instance from player factory
89
+ * 2. Creates route map for path → state ID resolution
90
+ * 3. Creates TanStack Router
91
+ * 4. Uses PlayRouterProvider with renderer prop pattern
92
+ *
93
+ * Lifecycle:
94
+ * - Mount: actor.start() → begin state machine execution
95
+ * - Runtime: PlayRenderer observes actor.currentView signal
96
+ * - Runtime: Provider subscribes to router changes
97
+ * - Unmount: actor.stop() → halt machine
98
+ *
99
+ * Actor Authority: Auth machine guards decide navigation validity.
100
+ * Passive Infrastructure: Router reflects actor state, doesn't control it.
101
+ */
102
+ export function App() {
103
+ // 1. Create actor
104
+ const actor = useMemo(() => {
105
+ const createPlayer = definePlayer({ machine: authMachine, catalog });
106
+ const actorInstance = createPlayer();
107
+ actorInstance.start();
108
+ return actorInstance;
109
+ }, []);
110
+
111
+ // 2. Create route map for path → state ID resolution
112
+ const routeMap = useMemo(() => {
113
+ const routeTree = extractMachineRoutes(authMachine);
114
+ return createRouteMapFromTree(routeTree);
115
+ }, []);
116
+
117
+ // 3. Create TanStack Router (using TanStack's API)
118
+ const router = useMemo(() => {
119
+ // TanStack Router manages its own route configuration
120
+ // Note: In real app, you'd configure TanStack routes separately
121
+ const rootRoute = createRootRoute();
122
+ return createRouter({
123
+ routeTree: rootRoute,
124
+ defaultPreload: "intent",
125
+ });
126
+ }, []);
127
+
128
+ // 4. Cleanup on unmount
129
+ useEffect(() => {
130
+ return () => {
131
+ actor.stop();
132
+ };
133
+ }, [actor]);
134
+
135
+ // 5. Use PlayRouterProvider with renderer prop and structured layout
136
+ return (
137
+ <PlayRouterProvider
138
+ actor={actor}
139
+ router={router}
140
+ routeMap={routeMap}
141
+ renderer={(currentActor, currentRouter) => (
142
+ <Shell actor={currentActor} router={currentRouter} />
143
+ )}
144
+ />
145
+ );
146
+ }
147
+
148
+ export default App;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * About - Public information page
3
+ *
4
+ * Demonstrates public route with content display.
5
+ *
6
+ * @invariant Passive Infrastructure - Forwards events to actor, doesn't control navigation
7
+ * @invariant Strict Separation - Zero router imports
8
+ */
9
+
10
+ import type { InferComponentProps } from "@xmachines/play-catalog";
11
+ import type { catalog } from "@xmachines/play-router-shared";
12
+
13
+ type AboutProps = InferComponentProps<typeof catalog, "About"> & {
14
+ send: (event: any) => void;
15
+ };
16
+
17
+ export function About({ title, send }: AboutProps) {
18
+ return (
19
+ <div className="about" style={{ maxWidth: "800px", margin: "50px auto", padding: "20px" }}>
20
+ <h1>{title}</h1>
21
+ <p style={{ fontSize: "16px", lineHeight: "1.6", marginBottom: "15px" }}>
22
+ This is a demonstration of the XMachines Play Universal Player Architecture.
23
+ </p>
24
+ <p style={{ fontSize: "16px", lineHeight: "1.6", marginBottom: "15px" }}>
25
+ Features demonstrated:
26
+ </p>
27
+ <ul style={{ fontSize: "16px", lineHeight: "1.6", marginBottom: "30px" }}>
28
+ <li>State machine-driven navigation</li>
29
+ <li>Protected routes with guards</li>
30
+ <li>Framework-agnostic business logic</li>
31
+ <li>TC39 Signals for reactivity</li>
32
+ </ul>
33
+ <button
34
+ onClick={() => send({ type: "play.route", to: "#home" })}
35
+ style={{
36
+ padding: "10px 20px",
37
+ fontSize: "16px",
38
+ backgroundColor: "#007bff",
39
+ color: "white",
40
+ border: "none",
41
+ borderRadius: "4px",
42
+ cursor: "pointer",
43
+ }}
44
+ >
45
+ Back to Home
46
+ </button>
47
+ </div>
48
+ );
49
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Contact - Public contact form
3
+ *
4
+ * Demonstrates public route with user interaction.
5
+ *
6
+ * @invariant Passive Infrastructure - Forwards events to actor, doesn't control navigation
7
+ * @invariant Strict Separation - Zero router imports
8
+ */
9
+
10
+ import type { InferComponentProps } from "@xmachines/play-catalog";
11
+ import type { catalog } from "@xmachines/play-router-shared";
12
+
13
+ type ContactProps = InferComponentProps<typeof catalog, "Contact"> & {
14
+ send: (event: any) => void;
15
+ };
16
+
17
+ export function Contact({ title, send }: ContactProps) {
18
+ return (
19
+ <div
20
+ className="contact"
21
+ style={{ maxWidth: "800px", margin: "50px auto", padding: "20px" }}
22
+ >
23
+ <h1>{title}</h1>
24
+ <p style={{ fontSize: "16px", lineHeight: "1.6", marginBottom: "15px" }}>
25
+ For support, please reach out.
26
+ </p>
27
+ <button
28
+ onClick={() => send({ type: "play.route", to: "#home" })}
29
+ style={{
30
+ padding: "10px 20px",
31
+ fontSize: "16px",
32
+ backgroundColor: "#007bff",
33
+ color: "white",
34
+ border: "none",
35
+ borderRadius: "4px",
36
+ cursor: "pointer",
37
+ }}
38
+ >
39
+ Back to Home
40
+ </button>
41
+ </div>
42
+ );
43
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Dashboard Component - Passive Infrastructure Layer
3
+ *
4
+ * Simplified dashboard for unified auth machine demo.
5
+ */
6
+
7
+ import React from "react";
8
+
9
+ import type { InferComponentProps } from "@xmachines/play-catalog";
10
+ import type { catalog } from "@xmachines/play-router-shared";
11
+
12
+ type DashboardProps = InferComponentProps<typeof catalog, "Dashboard"> & {
13
+ send: (event: any) => void;
14
+ };
15
+
16
+ export const Dashboard: React.FC<DashboardProps> = ({ send, welcome }) => {
17
+ return (
18
+ <div style={{ maxWidth: "800px", margin: "50px auto", padding: "20px" }}>
19
+ {welcome && <h1 style={{ marginBottom: "20px" }}>Welcome to the Dashboard!</h1>}
20
+ <div style={{ marginBottom: "20px" }}>
21
+ <p style={{ fontSize: "16px", lineHeight: "1.6" }}>
22
+ This is a protected route - you can only see this because you're logged in.
23
+ </p>
24
+ <p style={{ fontSize: "16px", lineHeight: "1.6", marginTop: "10px" }}>
25
+ Use the navigation in the header to explore other pages.
26
+ </p>
27
+ </div>
28
+
29
+ <button
30
+ onClick={() => send({ type: "auth.logout" })}
31
+ style={{
32
+ padding: "10px 20px",
33
+ fontSize: "16px",
34
+ backgroundColor: "#dc3545",
35
+ color: "white",
36
+ border: "none",
37
+ borderRadius: "4px",
38
+ cursor: "pointer",
39
+ marginTop: "20px",
40
+ }}
41
+ >
42
+ Logout
43
+ </button>
44
+ </div>
45
+ );
46
+ };
@@ -0,0 +1,68 @@
1
+ import { useEffect, useState } from "react";
2
+ import type { AbstractActor } from "@xmachines/play-actor";
3
+ import { Signal } from "@xmachines/play-signals";
4
+ import type { AnyActorLogic } from "xstate";
5
+
6
+ type RoutableActor = AbstractActor<AnyActorLogic> & {
7
+ currentRoute: { get(): string | null };
8
+ };
9
+
10
+ type Snapshot = {
11
+ value: unknown;
12
+ context: {
13
+ isAuthenticated?: boolean;
14
+ };
15
+ };
16
+
17
+ interface DebugPanelProps {
18
+ actor: RoutableActor;
19
+ }
20
+
21
+ export function DebugPanel({ actor }: DebugPanelProps) {
22
+ const [state, setState] = useState(String((actor.getSnapshot() as Snapshot).value));
23
+ const [auth, setAuth] = useState((actor.getSnapshot() as Snapshot).context.isAuthenticated);
24
+ const [route, setRoute] = useState(actor.currentRoute.get());
25
+
26
+ useEffect(() => {
27
+ const watcher = new Signal.subtle.Watcher(() => {
28
+ queueMicrotask(() => {
29
+ for (const signal of watcher.getPending()) {
30
+ signal.get();
31
+ }
32
+
33
+ const typedSnapshot = actor.getSnapshot() as Snapshot;
34
+ setState(String(typedSnapshot.value));
35
+ setAuth(typedSnapshot.context.isAuthenticated);
36
+ setRoute(actor.currentRoute.get());
37
+
38
+ watcher.watch(actor.state);
39
+ watcher.watch(actor.currentRoute as any);
40
+ });
41
+ });
42
+
43
+ watcher.watch(actor.state);
44
+ watcher.watch(actor.currentRoute as any);
45
+
46
+ return () => {
47
+ watcher.unwatch(actor.state);
48
+ watcher.unwatch(actor.currentRoute as any);
49
+ };
50
+ }, [actor]);
51
+
52
+ return (
53
+ <footer className="demo-debug">
54
+ <h3 className="debug-title">Debug Panel</h3>
55
+ <div className="debug-state">
56
+ <span className="debug-item">
57
+ State: <code>{state}</code>
58
+ </span>
59
+ <span className="debug-item">
60
+ Auth: <code>{String(auth)}</code>
61
+ </span>
62
+ <span className="debug-item">
63
+ Route: <code>{route}</code>
64
+ </span>
65
+ </div>
66
+ </footer>
67
+ );
68
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Header Navigation Component
3
+ *
4
+ * Reactive navigation that shows/hides links based on authentication state
5
+ */
6
+
7
+ import { useState } from "react";
8
+ import { useSignalEffect } from "@xmachines/play-react";
9
+ import type { AbstractActor } from "@xmachines/play-actor";
10
+ import type { AnyActorLogic } from "xstate";
11
+
12
+ interface HeaderNavProps {
13
+ actor: AbstractActor<AnyActorLogic>;
14
+ }
15
+
16
+ export function HeaderNav({ actor }: HeaderNavProps) {
17
+ type Snapshot = {
18
+ context: {
19
+ isAuthenticated?: boolean;
20
+ username?: string | null;
21
+ };
22
+ };
23
+
24
+ const [isAuthenticated, setIsAuthenticated] = useState(() => {
25
+ const snapshot = actor.getSnapshot() as Snapshot;
26
+ return snapshot.context.isAuthenticated ?? false;
27
+ });
28
+
29
+ const [username, setUsername] = useState(() => {
30
+ const snapshot = actor.getSnapshot() as Snapshot;
31
+ return snapshot.context.username || "demo";
32
+ });
33
+
34
+ // Watch actor.state signal for changes
35
+ useSignalEffect(() => {
36
+ const snapshot = actor.state.get() as Snapshot;
37
+ setIsAuthenticated(snapshot.context.isAuthenticated ?? false);
38
+ setUsername(snapshot.context.username || "demo");
39
+ });
40
+
41
+ return (
42
+ <nav className="demo-nav" aria-label="Main navigation">
43
+ <a
44
+ href="/"
45
+ onClick={(e) => {
46
+ e.preventDefault();
47
+ actor.send({ type: "play.route", to: "#home" });
48
+ }}
49
+ >
50
+ Home
51
+ </a>
52
+ <a
53
+ href="/about"
54
+ onClick={(e) => {
55
+ e.preventDefault();
56
+ actor.send({ type: "play.route", to: "#about" });
57
+ }}
58
+ >
59
+ About
60
+ </a>
61
+ <a
62
+ href="/contact"
63
+ onClick={(e) => {
64
+ e.preventDefault();
65
+ actor.send({ type: "play.route", to: "#contact" });
66
+ }}
67
+ >
68
+ Contact
69
+ </a>
70
+ {!isAuthenticated && (
71
+ <a
72
+ href="/login"
73
+ onClick={(e) => {
74
+ e.preventDefault();
75
+ actor.send({ type: "play.route", to: "#login" });
76
+ }}
77
+ >
78
+ Login
79
+ </a>
80
+ )}
81
+ {isAuthenticated && (
82
+ <>
83
+ <a
84
+ href={`/profile/${username}`}
85
+ onClick={(e) => {
86
+ e.preventDefault();
87
+ actor.send({
88
+ type: "play.route",
89
+ to: "#profile",
90
+ params: { userId: username },
91
+ });
92
+ }}
93
+ >
94
+ Profile
95
+ </a>
96
+ <button type="button" onClick={() => actor.send({ type: "auth.logout" })}>
97
+ Logout
98
+ </button>
99
+ </>
100
+ )}
101
+ </nav>
102
+ );
103
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Home - Public landing page component
3
+ */
4
+
5
+ import type { InferComponentProps } from "@xmachines/play-catalog";
6
+ import type { catalog } from "@xmachines/play-router-shared";
7
+
8
+ type HomeProps = InferComponentProps<typeof catalog, "Home"> & {
9
+ send: (event: any) => void;
10
+ };
11
+
12
+ export function Home({ title, send }: HomeProps) {
13
+ return (
14
+ <div className="home" style={{ maxWidth: "800px", margin: "50px auto", padding: "20px" }}>
15
+ <h1>{title}</h1>
16
+ <p style={{ fontSize: "16px", lineHeight: "1.6", marginBottom: "15px" }}>
17
+ Welcome to the XMachines Play demo application.
18
+ </p>
19
+ <p style={{ fontSize: "16px", lineHeight: "1.6", marginBottom: "30px" }}>
20
+ This demonstrates the Universal Player Architecture with React and TanStack Router.
21
+ </p>
22
+
23
+ <div style={{ marginBottom: "20px" }}>
24
+ <button
25
+ onClick={() => send({ type: "play.route", to: "#login" })}
26
+ style={{
27
+ padding: "10px 20px",
28
+ fontSize: "16px",
29
+ backgroundColor: "#007bff",
30
+ color: "white",
31
+ border: "none",
32
+ borderRadius: "4px",
33
+ cursor: "pointer",
34
+ }}
35
+ >
36
+ Go to Login
37
+ </button>
38
+ </div>
39
+ </div>
40
+ );
41
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Login Component - Passive Infrastructure Layer
3
+ *
4
+ * Simplified login form (username only, no password).
5
+ *
6
+ * Demonstrates:
7
+ * - Passive Infrastructure: Forwards events to actor, doesn't control navigation
8
+ * - Signal-Only Reactivity: No business logic in component (only UI state)
9
+ * - Strict Separation: Zero router imports - actor decides navigation
10
+ *
11
+ * @invariant Passive Infrastructure - Component never imports @tanstack/react-router
12
+ * @invariant Actor Authority - Login validation happens in actor guards, not here
13
+ */
14
+
15
+ import React, { useState, type FormEvent } from "react";
16
+
17
+ /**
18
+ * Login props (inferred from catalog)
19
+ */
20
+ import type { InferComponentProps } from "@xmachines/play-catalog";
21
+ import type { catalog } from "@xmachines/play-router-shared";
22
+
23
+ type LoginProps = InferComponentProps<typeof catalog, "Login"> & {
24
+ send: (event: any) => void;
25
+ };
26
+
27
+ /**
28
+ * Simplified login form - username only (no password for demo)
29
+ */
30
+ export const Login: React.FC<LoginProps> = ({ send, title = "Login" }) => {
31
+ const [username, setUsername] = useState("");
32
+
33
+ const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
34
+ e.preventDefault();
35
+
36
+ // Forward to actor - let business logic decide what happens
37
+ send({
38
+ type: "auth.login",
39
+ username,
40
+ });
41
+ };
42
+
43
+ return (
44
+ <div style={{ maxWidth: "400px", margin: "50px auto", padding: "20px" }}>
45
+ <h1>{title}</h1>
46
+ <form onSubmit={handleSubmit}>
47
+ <div style={{ marginBottom: "15px" }}>
48
+ <label htmlFor="username" style={{ display: "block", marginBottom: "5px" }}>
49
+ Username
50
+ </label>
51
+ <input
52
+ type="text"
53
+ id="username"
54
+ value={username}
55
+ onChange={(e) => setUsername((e.target as HTMLInputElement).value)}
56
+ style={{ width: "100%", padding: "8px", fontSize: "16px" }}
57
+ placeholder="Enter any username"
58
+ required
59
+ />
60
+ </div>
61
+ <button
62
+ type="submit"
63
+ style={{
64
+ width: "100%",
65
+ padding: "10px",
66
+ fontSize: "16px",
67
+ backgroundColor: "#007bff",
68
+ color: "white",
69
+ border: "none",
70
+ borderRadius: "4px",
71
+ cursor: "pointer",
72
+ }}
73
+ >
74
+ Log In
75
+ </button>
76
+ </form>
77
+ <p style={{ marginTop: "15px", fontSize: "14px", color: "#666" }}>
78
+ Enter any username - no password required for demo
79
+ </p>
80
+ </div>
81
+ );
82
+ };