@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,262 @@
1
+ /**
2
+ * Navigation Component - Route Discovery UI
3
+ *
4
+ * Displays all available routes organized by category (Public, Protected, Auth).
5
+ * Demonstrates how UI can help users discover routes without hardcoding paths.
6
+ *
7
+ * @invariant Passive Infrastructure - Forwards events to actor, doesn't control navigation
8
+ * @invariant Strict Separation - Zero router imports
9
+ */
10
+
11
+ interface Route {
12
+ path: string;
13
+ label: string;
14
+ description: string;
15
+ requiresAuth: boolean;
16
+ authOnly: boolean;
17
+ }
18
+
19
+ import type { InferComponentProps } from "@xmachines/play-catalog";
20
+ import type { catalog } from "@xmachines/play-router-shared";
21
+
22
+ type NavigationProps = InferComponentProps<typeof catalog, "Navigation"> & {
23
+ send: (event: any) => void;
24
+ };
25
+
26
+ /**
27
+ * All routes in the application
28
+ * Organized by access level for user discovery
29
+ */
30
+ const ROUTES: Route[] = [
31
+ // Public routes (accessible anytime)
32
+ {
33
+ path: "/",
34
+ label: "Home",
35
+ description: "Landing page / Dashboard (when logged in)",
36
+ requiresAuth: false,
37
+ authOnly: false,
38
+ },
39
+ {
40
+ path: "/about",
41
+ label: "About",
42
+ description: "Project information",
43
+ requiresAuth: false,
44
+ authOnly: false,
45
+ },
46
+ {
47
+ path: "/contact",
48
+ label: "Contact",
49
+ description: "Get in touch",
50
+ requiresAuth: false,
51
+ authOnly: false,
52
+ },
53
+
54
+ // Protected routes (require authentication)
55
+ {
56
+ path: "/settings",
57
+ label: "Settings",
58
+ description: "Account settings",
59
+ requiresAuth: true,
60
+ authOnly: false,
61
+ },
62
+ {
63
+ path: "/settings/profile",
64
+ label: "Settings › Profile",
65
+ description: "Profile settings section",
66
+ requiresAuth: true,
67
+ authOnly: false,
68
+ },
69
+ {
70
+ path: "/profile/demo-user",
71
+ label: "Profile",
72
+ description: "User profile (example)",
73
+ requiresAuth: true,
74
+ authOnly: false,
75
+ },
76
+
77
+ // Auth-only routes (only when logged out)
78
+ {
79
+ path: "/login",
80
+ label: "Login",
81
+ description: "Sign in to your account",
82
+ requiresAuth: false,
83
+ authOnly: true,
84
+ },
85
+ {
86
+ path: "/register",
87
+ label: "Register",
88
+ description: "Create new account",
89
+ requiresAuth: false,
90
+ authOnly: true,
91
+ },
92
+ ];
93
+
94
+ /**
95
+ * Navigation component with route discovery
96
+ *
97
+ * Displays routes categorized by access level:
98
+ * - Public: Always available
99
+ * - Protected: Visible but disabled when logged out
100
+ * - Auth: Only shown when logged out
101
+ */
102
+ export function Navigation({ send, isAuthenticated, currentPath }: NavigationProps) {
103
+ // Filter routes by category
104
+ const publicRoutes = ROUTES.filter((r) => !r.requiresAuth && !r.authOnly);
105
+ const protectedRoutes = ROUTES.filter((r) => r.requiresAuth);
106
+ const authRoutes = ROUTES.filter((r) => r.authOnly);
107
+
108
+ const handleNavigate = (path: string) => {
109
+ // Map path to play.route event (unified navigation)
110
+ const eventMap: Record<string, any> = {
111
+ "/": { type: "play.route", to: "#home" },
112
+ "/about": { type: "play.route", to: "#about" },
113
+ "/contact": { type: "play.route", to: "#contact" },
114
+ "/settings": { type: "play.route", to: "#settings" },
115
+ "/settings/profile": {
116
+ type: "play.route",
117
+ to: "#settings",
118
+ params: { section: "profile" },
119
+ },
120
+ "/profile/demo-user": {
121
+ type: "play.route",
122
+ to: "#profile",
123
+ params: { userId: "demo-user" },
124
+ },
125
+ "/login": { type: "play.route", to: "#login" },
126
+ "/register": { type: "play.route", to: "#register" },
127
+ };
128
+
129
+ const event = eventMap[path];
130
+ if (event) {
131
+ send(event);
132
+ }
133
+ };
134
+
135
+ const buttonStyle = (path: string, disabled: boolean = false) => ({
136
+ padding: "10px 15px",
137
+ fontSize: "14px",
138
+ backgroundColor: currentPath === path ? "#0056b3" : "#007bff",
139
+ color: "white",
140
+ border: "none",
141
+ borderRadius: "4px",
142
+ cursor: disabled ? "not-allowed" : "pointer",
143
+ opacity: disabled ? 0.5 : 1,
144
+ transition: "background-color 0.2s",
145
+ textAlign: "left" as const,
146
+ width: "100%",
147
+ });
148
+
149
+ const sectionStyle = {
150
+ marginBottom: "20px",
151
+ };
152
+
153
+ const routeListStyle = {
154
+ display: "flex",
155
+ flexDirection: "column" as const,
156
+ gap: "8px",
157
+ };
158
+
159
+ const routeItemStyle = {
160
+ display: "flex",
161
+ flexDirection: "column" as const,
162
+ gap: "4px",
163
+ };
164
+
165
+ const descriptionStyle = {
166
+ fontSize: "12px",
167
+ color: "#6c757d",
168
+ marginLeft: "15px",
169
+ };
170
+
171
+ return (
172
+ <nav
173
+ style={{
174
+ padding: "20px",
175
+ backgroundColor: "#f8f9fa",
176
+ borderRadius: "8px",
177
+ marginBottom: "20px",
178
+ }}
179
+ >
180
+ <h2 style={{ marginTop: 0, marginBottom: "15px", fontSize: "18px" }}>
181
+ Available Routes
182
+ </h2>
183
+
184
+ {/* Public Routes */}
185
+ <div style={sectionStyle}>
186
+ <h3 style={{ fontSize: "14px", fontWeight: "600", marginBottom: "10px" }}>
187
+ 🌍 Public Routes
188
+ </h3>
189
+ <div style={routeListStyle}>
190
+ {publicRoutes.map((route) => (
191
+ <div key={route.path} style={routeItemStyle}>
192
+ <button
193
+ onClick={() => handleNavigate(route.path)}
194
+ style={buttonStyle(route.path)}
195
+ >
196
+ {route.label}
197
+ </button>
198
+ <span style={descriptionStyle}>{route.description}</span>
199
+ </div>
200
+ ))}
201
+ </div>
202
+ </div>
203
+
204
+ {/* Protected Routes */}
205
+ <div style={sectionStyle}>
206
+ <h3 style={{ fontSize: "14px", fontWeight: "600", marginBottom: "10px" }}>
207
+ 🔒 Protected Routes {!isAuthenticated && "(Login required)"}
208
+ </h3>
209
+ <div style={routeListStyle}>
210
+ {protectedRoutes.map((route) => (
211
+ <div key={route.path} style={routeItemStyle}>
212
+ <button
213
+ onClick={() => handleNavigate(route.path)}
214
+ disabled={!isAuthenticated}
215
+ style={buttonStyle(route.path, !isAuthenticated)}
216
+ >
217
+ {route.label}
218
+ {!isAuthenticated && " 🔒"}
219
+ </button>
220
+ <span style={descriptionStyle}>{route.description}</span>
221
+ </div>
222
+ ))}
223
+ </div>
224
+ </div>
225
+
226
+ {/* Auth-Only Routes */}
227
+ {!isAuthenticated && (
228
+ <div style={sectionStyle}>
229
+ <h3 style={{ fontSize: "14px", fontWeight: "600", marginBottom: "10px" }}>
230
+ 🔑 Authentication
231
+ </h3>
232
+ <div style={routeListStyle}>
233
+ {authRoutes.map((route) => (
234
+ <div key={route.path} style={routeItemStyle}>
235
+ <button
236
+ onClick={() => handleNavigate(route.path)}
237
+ style={buttonStyle(route.path)}
238
+ >
239
+ {route.label}
240
+ </button>
241
+ <span style={descriptionStyle}>{route.description}</span>
242
+ </div>
243
+ ))}
244
+ </div>
245
+ </div>
246
+ )}
247
+
248
+ {/* Status indicator */}
249
+ <div
250
+ style={{
251
+ marginTop: "20px",
252
+ padding: "10px",
253
+ backgroundColor: isAuthenticated ? "#d4edda" : "#fff3cd",
254
+ borderRadius: "4px",
255
+ fontSize: "14px",
256
+ }}
257
+ >
258
+ <strong>Status:</strong> {isAuthenticated ? "✓ Authenticated" : "Not authenticated"}
259
+ </div>
260
+ </nav>
261
+ );
262
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Profile - Protected user profile page with required userId parameter
3
+ *
4
+ * Demonstrates:
5
+ * - Protected route (requires authentication)
6
+ * - Required route parameter (userId)
7
+ * - Parameter validation (guard checks userId exists)
8
+ *
9
+ * @invariant Passive Infrastructure - Forwards events to actor, doesn't control navigation
10
+ * @invariant Strict Separation - Zero router imports
11
+ */
12
+
13
+ import type { InferComponentProps } from "@xmachines/play-catalog";
14
+ import type { catalog } from "@xmachines/play-router-shared";
15
+
16
+ type ProfileProps = InferComponentProps<typeof catalog, "Profile"> & {
17
+ send: (event: any) => void;
18
+ };
19
+
20
+ export function Profile({ username, send }: ProfileProps) {
21
+ return (
22
+ <div
23
+ className="profile"
24
+ style={{ maxWidth: "800px", margin: "50px auto", padding: "20px" }}
25
+ >
26
+ <h1>Profile: {username}</h1>
27
+ <p style={{ fontSize: "16px", lineHeight: "1.6", marginBottom: "20px" }}>
28
+ This is your profile page.
29
+ </p>
30
+ <button
31
+ onClick={() => send({ type: "play.route", to: "#dashboard" })}
32
+ style={{
33
+ padding: "10px 20px",
34
+ fontSize: "16px",
35
+ backgroundColor: "#6c757d",
36
+ color: "white",
37
+ border: "none",
38
+ borderRadius: "4px",
39
+ cursor: "pointer",
40
+ }}
41
+ >
42
+ Back to Dashboard
43
+ </button>
44
+ </div>
45
+ );
46
+ }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Register - Auth-only registration form
3
+ *
4
+ * Demonstrates:
5
+ * - Auth-only route (redirects to dashboard if already authenticated)
6
+ * - Form submission sending machine event
7
+ *
8
+ * @invariant Passive Infrastructure - Forwards events to actor, doesn't control navigation
9
+ * @invariant Strict Separation - Zero router imports
10
+ */
11
+
12
+ import { type FormEvent } from "react";
13
+ import type { InferComponentProps } from "@xmachines/play-catalog";
14
+ import type { catalog } from "@xmachines/play-router-shared";
15
+
16
+ type RegisterProps = InferComponentProps<typeof catalog, "Register"> & {
17
+ send: (event: any) => void;
18
+ };
19
+
20
+ export function Register({ title, send }: RegisterProps) {
21
+ const handleRegister = (e: FormEvent<HTMLFormElement>) => {
22
+ e.preventDefault();
23
+ const formData = new FormData(e.currentTarget);
24
+ send({
25
+ type: "auth.register",
26
+ username: formData.get("username") as string,
27
+ email: formData.get("email") as string,
28
+ password: formData.get("password") as string,
29
+ });
30
+ };
31
+
32
+ return (
33
+ <div
34
+ className="register"
35
+ style={{ maxWidth: "400px", margin: "50px auto", padding: "20px" }}
36
+ >
37
+ <h1>{title}</h1>
38
+ <form onSubmit={handleRegister}>
39
+ <div style={{ marginBottom: "15px" }}>
40
+ <label htmlFor="username" style={{ display: "block", marginBottom: "5px" }}>
41
+ Username
42
+ </label>
43
+ <input
44
+ type="text"
45
+ name="username"
46
+ id="username"
47
+ required
48
+ style={{ width: "100%", padding: "8px", fontSize: "16px" }}
49
+ />
50
+ </div>
51
+ <div style={{ marginBottom: "15px" }}>
52
+ <label htmlFor="email" style={{ display: "block", marginBottom: "5px" }}>
53
+ Email
54
+ </label>
55
+ <input
56
+ type="email"
57
+ name="email"
58
+ id="email"
59
+ required
60
+ style={{ width: "100%", padding: "8px", fontSize: "16px" }}
61
+ />
62
+ </div>
63
+ <div style={{ marginBottom: "15px" }}>
64
+ <label htmlFor="password" style={{ display: "block", marginBottom: "5px" }}>
65
+ Password
66
+ </label>
67
+ <input
68
+ type="password"
69
+ name="password"
70
+ id="password"
71
+ required
72
+ style={{ width: "100%", padding: "8px", fontSize: "16px" }}
73
+ />
74
+ </div>
75
+ <button
76
+ type="submit"
77
+ style={{
78
+ width: "100%",
79
+ padding: "10px",
80
+ fontSize: "16px",
81
+ backgroundColor: "#28a745",
82
+ color: "white",
83
+ border: "none",
84
+ borderRadius: "4px",
85
+ cursor: "pointer",
86
+ marginBottom: "15px",
87
+ }}
88
+ >
89
+ Register
90
+ </button>
91
+ </form>
92
+ <button
93
+ onClick={() => send({ type: "play.route", to: "#login" })}
94
+ style={{
95
+ width: "100%",
96
+ padding: "10px",
97
+ fontSize: "16px",
98
+ backgroundColor: "#6c757d",
99
+ color: "white",
100
+ border: "none",
101
+ borderRadius: "4px",
102
+ cursor: "pointer",
103
+ }}
104
+ >
105
+ Back to Login
106
+ </button>
107
+ </div>
108
+ );
109
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Settings - Protected settings page with optional section parameter
3
+ *
4
+ * Demonstrates:
5
+ * - Protected route (requires authentication)
6
+ * - Optional route parameter (section)
7
+ * - Parameter-driven content display
8
+ *
9
+ * @invariant Passive Infrastructure - Forwards events to actor, doesn't control navigation
10
+ * @invariant Strict Separation - Zero router imports
11
+ */
12
+
13
+ import type { InferComponentProps } from "@xmachines/play-catalog";
14
+ import type { catalog } from "@xmachines/play-router-shared";
15
+
16
+ type SettingsProps = InferComponentProps<typeof catalog, "Settings"> & {
17
+ send: (event: any) => void;
18
+ routeParams?: Record<string, string>;
19
+ };
20
+
21
+ export function Settings({ section, username, send, routeParams }: SettingsProps) {
22
+ // Use section from route params (dynamic from URL) with fallback to prop or "general"
23
+ const actualSection = routeParams?.section || section || "general";
24
+
25
+ return (
26
+ <div
27
+ className="settings"
28
+ style={{ maxWidth: "800px", margin: "50px auto", padding: "20px" }}
29
+ >
30
+ <h1>Settings - {username}</h1>
31
+ <p style={{ fontSize: "16px", lineHeight: "1.6", marginBottom: "20px" }}>
32
+ Current section: {actualSection}
33
+ </p>
34
+ <nav style={{ display: "flex", gap: "10px", marginBottom: "30px" }}>
35
+ <button
36
+ onClick={() =>
37
+ send({
38
+ type: "play.route",
39
+ to: "#settings",
40
+ params: { section: "account" },
41
+ })
42
+ }
43
+ style={{
44
+ padding: "10px 20px",
45
+ fontSize: "16px",
46
+ backgroundColor: "#007bff",
47
+ color: "white",
48
+ border: "none",
49
+ borderRadius: "4px",
50
+ cursor: "pointer",
51
+ }}
52
+ >
53
+ Account
54
+ </button>
55
+ <button
56
+ onClick={() =>
57
+ send({
58
+ type: "play.route",
59
+ to: "#settings",
60
+ params: { section: "privacy" },
61
+ })
62
+ }
63
+ style={{
64
+ padding: "10px 20px",
65
+ fontSize: "16px",
66
+ backgroundColor: "#007bff",
67
+ color: "white",
68
+ border: "none",
69
+ borderRadius: "4px",
70
+ cursor: "pointer",
71
+ }}
72
+ >
73
+ Privacy
74
+ </button>
75
+ </nav>
76
+ <button
77
+ onClick={() => send({ type: "play.route", to: "#home" })}
78
+ style={{
79
+ padding: "10px 20px",
80
+ fontSize: "16px",
81
+ backgroundColor: "#6c757d",
82
+ color: "white",
83
+ border: "none",
84
+ borderRadius: "4px",
85
+ cursor: "pointer",
86
+ }}
87
+ >
88
+ Back to Home
89
+ </button>
90
+ </div>
91
+ );
92
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Component Exports
3
+ *
4
+ * Centralized exports for all demo app components.
5
+ * Components are framework-agnostic and receive props from PlayRenderer.
6
+ */
7
+
8
+ export { About } from "./About.js";
9
+ export { Contact } from "./Contact.js";
10
+ export { Dashboard } from "./Dashboard.js";
11
+ export { Home } from "./Home.js";
12
+ export { Login } from "./Login.js";
13
+ export { Navigation } from "./Navigation.js";
14
+ export { Profile } from "./Profile.js";
15
+ export { Register } from "./Register.js";
16
+ export { Settings } from "./Settings.js";
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Application Entry Point
3
+ *
4
+ * Standard Vite + React entry point.
5
+ * Mounts the App component into the DOM root element.
6
+ */
7
+
8
+ import ReactDOM from "react-dom/client";
9
+ import App from "./App.js";
10
+
11
+ // Get root element from HTML
12
+ const rootElement = document.getElementById("root");
13
+
14
+ if (!rootElement) {
15
+ throw new Error("Root element not found. Check index.html has <div id='root'>");
16
+ }
17
+
18
+ // Create React root and render app
19
+ // Note: StrictMode disabled during development to avoid double-render issues with actors
20
+ ReactDOM.createRoot(rootElement).render(<App />);
@@ -0,0 +1,50 @@
1
+ /**
2
+ * DEMO-02: Actor Authority Invariant Test
3
+ *
4
+ * Validates that Actor controls navigation via guards (Actor Authority).
5
+ * Guards reject invalid navigation - infrastructure doesn't decide.
6
+ */
7
+
8
+ import { test } from "vitest";
9
+ import { expect } from "vitest";
10
+ import { definePlayer } from "@xmachines/play-xstate";
11
+ import { authMachine, catalog } from "@xmachines/play-router-shared";
12
+
13
+ test("DEMO-02: Actor Authority - Guards reject invalid navigation", async () => {
14
+ // Provide catalog so view-binding validation uses real component vocabulary
15
+ const createPlayer = definePlayer({ machine: authMachine, catalog });
16
+ const actor = createPlayer();
17
+ actor.start();
18
+
19
+ // Initial state: home (not authenticated)
20
+ expect(actor.getSnapshot().matches("home")).toBe(true);
21
+ expect(actor.currentRoute.get()).toBe("/");
22
+
23
+ // Navigate to login page
24
+ actor.send({ type: "play.route", to: "#login" });
25
+ expect(actor.getSnapshot().matches("login")).toBe(true);
26
+
27
+ // Attempt to navigate back to / (which should stay in home when not authenticated)
28
+ actor.send({ type: "play.route", to: "#home" });
29
+
30
+ // Should go to home state
31
+ expect(actor.getSnapshot().matches("home")).toBe(true);
32
+ expect(actor.currentRoute.get()).toBe("/");
33
+
34
+ // Login successfully
35
+ actor.send({ type: "auth.login", username: "test", password: "pass" });
36
+
37
+ // Now in dashboard state with protected route
38
+ expect(actor.getSnapshot().matches("dashboard")).toBe(true);
39
+ expect(actor.currentRoute.get()).toBe("/dashboard");
40
+ expect(actor.getSnapshot().context.username).toBe("test");
41
+
42
+ // Can now navigate to protected routes
43
+ actor.send({ type: "play.route", to: "#settings" });
44
+ expect(actor.getSnapshot().matches("settings")).toBe(true);
45
+
46
+ actor.stop();
47
+
48
+ // Allow microtasks to complete to prevent async activity warnings
49
+ await new Promise((resolve) => setTimeout(resolve, 10));
50
+ });