@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
+ * Browser test: GAP-12 validation - No duplicate history entries
3
+ *
4
+ * Validates the architectural fix:
5
+ * - Components send goto.* events (semantic navigation)
6
+ * - Browser back/forward sends play.route events
7
+ * - SignalSyncedHistory prevents duplicate entries via isProcessingBrowserNavigation flag
8
+ *
9
+ * Test sequence:
10
+ * 1. Login → auto-redirect to /dashboard
11
+ * 2. Click "About" button → sends goto.about event → /about
12
+ * 3. Click "Home" button → sends goto.home event → /home
13
+ * 4. Browser back → should go to /about (unique)
14
+ * 5. Browser back → should go to /dashboard (unique, NOT /home duplicate)
15
+ *
16
+ * Expected history: [/, /dashboard, /about, /home]
17
+ * History length: 4 entries (no duplicates)
18
+ */
19
+
20
+ import { test, expect, afterEach } from "vitest";
21
+ import { render, screen, cleanup, waitFor } from "@testing-library/react";
22
+ import userEvent from "@testing-library/user-event";
23
+ import App from "../../src/App.js";
24
+
25
+ afterEach(async () => {
26
+ cleanup();
27
+ });
28
+
29
+ test("GAP-12: navigation via goto.* events creates single history entries", async () => {
30
+ // Reset URL BEFORE rendering to ensure clean initial state
31
+ window.history.pushState({}, "", "/");
32
+
33
+ render(<App />);
34
+ const user = userEvent.setup();
35
+
36
+ // Record initial history length
37
+ const initialLength = window.history.length;
38
+ console.log(`\n=== Initial history length: ${initialLength} ===`);
39
+
40
+ // Step 0: Navigate to login page (if not already there)
41
+ console.log("\n=== Step 0: Navigate to login ===");
42
+ if (!screen.queryByLabelText(/username/i)) {
43
+ await user.click(screen.getByRole("button", { name: /login/i }));
44
+ await new Promise((resolve) => setTimeout(resolve, 50));
45
+ }
46
+
47
+ // Step 1: Login (sets isAuthenticated=true, auto-redirects to dashboard at /)
48
+ console.log("\n=== Step 1: Login ===");
49
+ await user.type(screen.getByLabelText(/username/i), "testuser");
50
+ await user.type(screen.getByLabelText(/password/i), "password123");
51
+ await user.click(screen.getByRole("button", { name: /log in/i }));
52
+
53
+ // Should auto-redirect to dashboard (at /)
54
+ await waitFor(
55
+ () => {
56
+ expect(window.location.pathname).toBe("/");
57
+ },
58
+ { timeout: 2000 },
59
+ );
60
+ console.log("✓ At dashboard (path: /)");
61
+
62
+ // Step 2: Navigate to /about via goto.about event
63
+ console.log("\n=== Step 2: Navigate to /about (goto.about event) ===");
64
+ const aboutButton = screen
65
+ .getAllByRole("button")
66
+ .find((btn) => btn.textContent?.includes("About"));
67
+ if (!aboutButton) throw new Error("About button not found");
68
+ await user.click(aboutButton);
69
+
70
+ await waitFor(
71
+ () => {
72
+ expect(window.location.pathname).toBe("/about");
73
+ },
74
+ { timeout: 2000 },
75
+ );
76
+ console.log("✓ At /about");
77
+
78
+ // Step 3: Navigate to home via goto.home event
79
+ // Since user is authenticated, home (/) auto-redirects to dashboard (also /)
80
+ console.log(
81
+ "\n=== Step 3: Navigate to home (goto.home event) - authenticated user stays at dashboard ===",
82
+ );
83
+ const homeButton = screen
84
+ .getAllByRole("button")
85
+ .find((btn) => btn.textContent?.includes("Home"));
86
+ if (!homeButton) throw new Error("Home button not found");
87
+ await user.click(homeButton);
88
+
89
+ // Authenticated users navigating to / stay at dashboard (/)
90
+ await waitFor(
91
+ () => {
92
+ expect(window.location.pathname).toBe("/");
93
+ },
94
+ { timeout: 2000 },
95
+ );
96
+ console.log("✓ At / (dashboard - authenticated user auto-redirect)");
97
+
98
+ // Expected history at this point: [/, / (dashboard), /about, /]
99
+ // Each navigation should create SINGLE entry (no duplicates)
100
+ const expectedLength = initialLength + 3; // login, about, home->dashboard (all create history entries)
101
+ console.log(
102
+ `\n=== History length after navigation: ${window.history.length}, expected: ${expectedLength} ===`,
103
+ );
104
+
105
+ // Step 4: Browser back button #1 (expect /about)
106
+ console.log("\n=== Step 4: Back button #1 (expect /about) ===");
107
+ window.history.back();
108
+
109
+ await waitFor(
110
+ () => {
111
+ console.log(`Current pathname after back #1: ${window.location.pathname}`);
112
+ expect(window.location.pathname).toBe("/about");
113
+ },
114
+ { timeout: 2000 },
115
+ );
116
+ console.log("✓ Back #1 correct: / → /about");
117
+
118
+ // Step 5: Browser back button #2 (expect /, NOT duplicate /about!)
119
+ console.log("\n=== Step 5: Back button #2 (expect /) ===");
120
+ window.history.back();
121
+
122
+ await waitFor(
123
+ () => {
124
+ console.log(`Current pathname after back #2: ${window.location.pathname}`);
125
+ return window.location.pathname === "/";
126
+ },
127
+ { timeout: 2000 },
128
+ );
129
+
130
+ // This validates GAP-12 is fixed
131
+ // If pathname is /about (duplicate), GAP-12 not fixed
132
+ // If pathname is / (dashboard), history is unique (GAP-12 resolved)
133
+
134
+ if (window.location.pathname === "/about") {
135
+ console.error("❌ GAP-12 NOT FIXED: Back button went to /about again instead of /");
136
+ console.error("This indicates duplicate history entries are still being created");
137
+ console.error("Expected history: [/, / (dashboard), /about, /]");
138
+ console.error(
139
+ "Actual history appears to be: [/, / (dashboard), /about, /about, /, /] (duplicates!)",
140
+ );
141
+ throw new Error("GAP-12 still present: duplicate history entries detected");
142
+ }
143
+
144
+ expect(window.location.pathname).toBe("/");
145
+ console.log("✓ Back #2 correct: /about → / (dashboard)");
146
+ console.log("✅ GAP-12 RESOLVED: Navigation creates unique history entries");
147
+ console.log("✅ goto.* events + isProcessingBrowserNavigation flag working correctly");
148
+ }, 10000); // 10 second timeout
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Browser Back/Forward URL Sync Tests
3
+ *
4
+ * Tests that browser back/forward navigation keeps URL in sync with actor state.
5
+ */
6
+
7
+ import { test, expect, afterEach } from "vitest";
8
+ import { render, screen, cleanup, waitFor } from "@testing-library/react";
9
+ import { userEvent } from "@testing-library/user-event";
10
+ import App from "../../src/App.js";
11
+
12
+ afterEach(async () => {
13
+ cleanup();
14
+ });
15
+
16
+ test("Back/Forward: URL stays in sync with actor state", async () => {
17
+ // Reset URL BEFORE rendering
18
+ window.history.pushState({}, "", "/");
19
+
20
+ const { unmount } = render(<App />);
21
+ const user = userEvent.setup();
22
+
23
+ console.log("Initial URL:", window.location.pathname);
24
+ expect(window.location.pathname).toBe("/");
25
+
26
+ // Navigate to About using the "Back to Home" pattern - click directly in nav
27
+ const buttons = screen.getAllByRole("button");
28
+ const aboutButton = buttons.find((b) => b.textContent?.trim() === "About");
29
+ expect(aboutButton).toBeDefined();
30
+ await user.click(aboutButton!);
31
+
32
+ // Wait for navigation
33
+ await waitFor(
34
+ () => {
35
+ console.log("After clicking About, URL:", window.location.pathname);
36
+ expect(window.location.pathname).toBe("/about");
37
+ },
38
+ { timeout: 2000 },
39
+ );
40
+
41
+ // Use browser back to go back to home
42
+ window.history.back();
43
+ await waitFor(
44
+ () => {
45
+ console.log("After back(), URL:", window.location.pathname);
46
+ expect(window.location.pathname).toBe("/");
47
+ },
48
+ { timeout: 2000 },
49
+ );
50
+
51
+ // Use browser forward to go forward to about
52
+ window.history.forward();
53
+ await waitFor(
54
+ () => {
55
+ console.log("After forward(), URL:", window.location.pathname);
56
+ expect(window.location.pathname).toBe("/about");
57
+ },
58
+ { timeout: 2000 },
59
+ );
60
+
61
+ unmount();
62
+ });
63
+
64
+ test("Back/Forward: Multiple rapid navigations", async () => {
65
+ // Reset URL BEFORE rendering
66
+ window.history.pushState({}, "", "/");
67
+
68
+ const { unmount } = render(<App />);
69
+ const user = userEvent.setup();
70
+
71
+ // Get all navigation buttons
72
+ const getButton = (name: string) => {
73
+ const buttons = screen.getAllByRole("button");
74
+ return buttons.find((b) => b.textContent?.trim() === name);
75
+ };
76
+
77
+ // Navigate through multiple pages quickly
78
+ await user.click(getButton("About")!);
79
+ await waitFor(
80
+ () => {
81
+ expect(window.location.pathname).toBe("/about");
82
+ },
83
+ { timeout: 2000 },
84
+ );
85
+
86
+ // Go back and forth
87
+ window.history.back();
88
+ await waitFor(
89
+ () => {
90
+ console.log("After back(), URL:", window.location.pathname);
91
+ expect(window.location.pathname).toBe("/");
92
+ },
93
+ { timeout: 2000 },
94
+ );
95
+
96
+ window.history.forward();
97
+ await waitFor(
98
+ () => {
99
+ console.log("After forward(), URL:", window.location.pathname);
100
+ expect(window.location.pathname).toBe("/about");
101
+ },
102
+ { timeout: 2000 },
103
+ );
104
+
105
+ unmount();
106
+ });
107
+
108
+ test("Back/Forward: Works correctly", async () => {
109
+ // Reset URL BEFORE rendering
110
+ window.history.pushState({}, "", "/");
111
+
112
+ const { unmount } = render(<App />);
113
+ const user = userEvent.setup();
114
+
115
+ // Get navigation button helper
116
+ const getButton = (name: string) => {
117
+ const buttons = screen.getAllByRole("button");
118
+ return buttons.find((b) => b.textContent?.trim() === name);
119
+ };
120
+
121
+ // Simple navigation test
122
+ await user.click(getButton("About")!);
123
+ await waitFor(
124
+ () => {
125
+ expect(window.location.pathname).toBe("/about");
126
+ },
127
+ { timeout: 2000 },
128
+ );
129
+
130
+ // Back
131
+ window.history.back();
132
+ await waitFor(
133
+ () => {
134
+ expect(window.location.pathname).toBe("/");
135
+ },
136
+ { timeout: 2000 },
137
+ );
138
+
139
+ // Forward
140
+ window.history.forward();
141
+ await waitFor(
142
+ () => {
143
+ expect(window.location.pathname).toBe("/about");
144
+ },
145
+ { timeout: 2000 },
146
+ );
147
+
148
+ unmount();
149
+ });
@@ -0,0 +1,146 @@
1
+ /**
2
+ * GAP: Direct route navigation doesn't load correct page
3
+ *
4
+ * Tests that navigating directly to a route URL (e.g., /contact) loads the correct page.
5
+ * This reproduces the issue: going to http://localhost:3002/contact does not load contact page.
6
+ */
7
+
8
+ import { test, expect } from "vitest";
9
+ import { definePlayer } from "@xmachines/play-xstate";
10
+ import { authMachine } from "../../src/machines/auth-machine.js";
11
+ import { catalog } from "../../src/machines/catalog.js";
12
+ import { SignalSyncedHistory } from "@xmachines/play-tanstack-react-router";
13
+ import { extractMachineRoutes } from "@xmachines/play-router";
14
+
15
+ test("Direct navigation to /contact loads contact page", async () => {
16
+ const createPlayer = definePlayer({ machine: authMachine, catalog });
17
+ const actor = createPlayer();
18
+ actor.start();
19
+
20
+ // Extract route tree from machine
21
+ const routeTree = extractMachineRoutes(authMachine);
22
+ // Initialize with /contact as initial path - simulates direct navigation
23
+ const history = new SignalSyncedHistory(actor, routeTree, "/contact");
24
+
25
+ await new Promise((resolve) => setTimeout(resolve, 100));
26
+
27
+ const snapshot = actor.getSnapshot();
28
+ console.log(`Direct /contact: state=${snapshot.value}, route=${actor.currentRoute.get()}`);
29
+
30
+ expect(snapshot.matches("contact")).toBe(true);
31
+ expect(actor.currentRoute.get()).toBe("/contact");
32
+
33
+ const view = actor.currentView.get();
34
+ expect(view).toBeTruthy();
35
+ expect(view?.component).toBe("Contact");
36
+
37
+ history.dispose();
38
+ actor.stop();
39
+ });
40
+
41
+ test("Direct navigation to /about loads about page", async () => {
42
+ const createPlayer = definePlayer({ machine: authMachine, catalog });
43
+ const actor = createPlayer();
44
+ actor.start();
45
+
46
+ const routeTree = extractMachineRoutes(authMachine);
47
+ const history = new SignalSyncedHistory(actor, routeTree, "/about");
48
+
49
+ await new Promise((resolve) => setTimeout(resolve, 100));
50
+
51
+ const snapshot = actor.getSnapshot();
52
+ console.log(`Direct /about: state=${snapshot.value}, route=${actor.currentRoute.get()}`);
53
+
54
+ expect(snapshot.matches("about")).toBe(true);
55
+ expect(actor.currentRoute.get()).toBe("/about");
56
+
57
+ const view = actor.currentView.get();
58
+ expect(view).toBeTruthy();
59
+ expect(view?.component).toBe("About");
60
+
61
+ history.dispose();
62
+ actor.stop();
63
+ });
64
+
65
+ test("Direct navigation to /home loads home page", async () => {
66
+ const createPlayer = definePlayer({ machine: authMachine, catalog });
67
+ const actor = createPlayer();
68
+ actor.start();
69
+
70
+ const routeTree = extractMachineRoutes(authMachine);
71
+ // Home route is actually "/" not "/home"
72
+ const history = new SignalSyncedHistory(actor, routeTree, "/");
73
+
74
+ await new Promise((resolve) => setTimeout(resolve, 100));
75
+
76
+ const snapshot = actor.getSnapshot();
77
+ console.log(`Direct /home: state=${snapshot.value}, route=${actor.currentRoute.get()}`);
78
+
79
+ expect(snapshot.matches("home")).toBe(true);
80
+ // Home state uses "/" as route (not "/home")
81
+ expect(actor.currentRoute.get()).toBe("/");
82
+
83
+ const view = actor.currentView.get();
84
+ expect(view).toBeTruthy();
85
+ expect(view?.component).toBe("Home");
86
+
87
+ history.dispose();
88
+ actor.stop();
89
+ });
90
+
91
+ test("Direct navigation to protected route while unauthenticated redirects to login", async () => {
92
+ const createPlayer = definePlayer({ machine: authMachine, catalog });
93
+ const actor = createPlayer();
94
+ actor.start();
95
+
96
+ // Try to navigate directly to protected route /dashboard while unauthenticated
97
+ // Dashboard route is "/" with auth guard, unauthenticated user stays at home (also "/")
98
+ const routeTree = extractMachineRoutes(authMachine);
99
+ const history = new SignalSyncedHistory(actor, routeTree, "/");
100
+
101
+ await new Promise((resolve) => setTimeout(resolve, 100));
102
+
103
+ const snapshot = actor.getSnapshot();
104
+ console.log(
105
+ `Direct /dashboard (unauthenticated): state=${snapshot.value}, route=${actor.currentRoute.get()}`,
106
+ );
107
+
108
+ // Unauthenticated user navigating to "/" stays at home (not redirected to login)
109
+ // Dashboard's always guard redirects to home if not authenticated
110
+ expect(snapshot.matches("home")).toBe(true);
111
+
112
+ history.dispose();
113
+ actor.stop();
114
+ });
115
+
116
+ test("Direct navigation to protected route while authenticated loads dashboard", async () => {
117
+ const createPlayer = definePlayer({ machine: authMachine, catalog });
118
+ const actor = createPlayer();
119
+ actor.start();
120
+
121
+ // Login first
122
+ actor.send({ type: "auth.login", username: "test", password: "pass" } as any);
123
+ expect(actor.getSnapshot().matches("dashboard")).toBe(true);
124
+
125
+ // Now navigate directly to /dashboard (which is route "/")
126
+ const routeTree = extractMachineRoutes(authMachine);
127
+ const history = new SignalSyncedHistory(actor, routeTree, "/");
128
+
129
+ await new Promise((resolve) => setTimeout(resolve, 100));
130
+
131
+ const snapshot = actor.getSnapshot();
132
+ console.log(
133
+ `Direct /dashboard (authenticated): state=${snapshot.value}, route=${actor.currentRoute.get()}`,
134
+ );
135
+
136
+ expect(snapshot.matches("dashboard")).toBe(true);
137
+ // Dashboard route is "/" (not "/dashboard")
138
+ expect(actor.currentRoute.get()).toBe("/");
139
+
140
+ const view = actor.currentView.get();
141
+ expect(view).toBeTruthy();
142
+ expect(view?.component).toBe("Dashboard");
143
+
144
+ history.dispose();
145
+ actor.stop();
146
+ });