@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,461 @@
1
+ # Architectural Invariants Validation
2
+
3
+ This document explains how the demo application validates each of the 5 architectural invariants that define the Universal Player Architecture.
4
+
5
+ ---
6
+
7
+ ## Table of Contents
8
+
9
+ 1. [Actor Authority](#1-actor-authority)
10
+ 2. [Strict Separation](#2-strict-separation)
11
+ 3. [Signal-Only Reactivity](#3-signal-only-reactivity)
12
+ 4. [Passive Infrastructure](#4-passive-infrastructure)
13
+ 5. [State-Driven Reset](#5-state-driven-reset)
14
+ 6. [Why These Invariants Matter](#why-these-invariants-matter)
15
+
16
+ ---
17
+
18
+ ## 1. Actor Authority
19
+
20
+ ### What It Means
21
+
22
+ **The actor controls navigation via guards. Infrastructure obeys actor decisions.**
23
+
24
+ In traditional web apps, routing libraries control navigation - they decide if a route transition is valid. In Universal Player Architecture, the **actor** decides via guard functions, and infrastructure (router, components) obeys.
25
+
26
+ ### How Demo Proves It
27
+
28
+ **Code Location:** `src/machines/auth-machine.ts`
29
+
30
+ ```typescript
31
+ dashboard: {
32
+ meta: { route: "/dashboard" },
33
+ // Always-guard validates state entry
34
+ always: [
35
+ {
36
+ target: "login",
37
+ guard: ({ context }) => !context.isAuthenticated
38
+ }
39
+ ]
40
+ }
41
+ ```
42
+
43
+ **Proof:**
44
+
45
+ 1. Try to access `/dashboard` while logged out
46
+ 2. Always-guard checks: `!context.isAuthenticated` → `true`
47
+ 3. Automatic transition to `login` state
48
+ 4. Actor Authority: actor decides navigation validity, not the router
49
+
50
+ **Test Reference:** `test/invariants/actor-authority.test.ts`
51
+
52
+ ```typescript
53
+ // Attempt to access protected route while logged out
54
+ actor.send({ type: "play.route", to: "/dashboard" });
55
+
56
+ // Always-guard validates: !isAuthenticated → redirect to login
57
+ assert.strictEqual(actor.getSnapshot().matches("login"), true);
58
+ ```
59
+
60
+ **Run Test:**
61
+
62
+ ```bash
63
+ cd examples/demo-app
64
+ npm test -- test/invariants/actor-authority.test.ts
65
+ ```
66
+
67
+ ### Why This Matters
68
+
69
+ - **Security:** Authentication logic lives in actor, not URL patterns
70
+ - **Consistency:** Same guard logic works across all platforms (web, mobile, desktop)
71
+ - **Testability:** Test guards in isolation without DOM or routing libraries
72
+
73
+ ---
74
+
75
+ ## 2. Strict Separation
76
+
77
+ ### What It Means
78
+
79
+ **Business logic has zero UI framework imports. View structure defined in metadata, not JSX.**
80
+
81
+ Traditional React apps mix business logic with JSX. In Universal Player Architecture, state machines define view structure declaratively in `meta.view`, with zero React imports.
82
+
83
+ ### How Demo Proves It
84
+
85
+ **Code Location:** `src/machines/auth-machine.ts` lines 55-63
86
+
87
+ ```typescript
88
+ loggedOut: {
89
+ meta: {
90
+ route: "/",
91
+ // View structure in metadata, not JSX
92
+ view: {
93
+ component: "LoginForm", // String reference, not JSX import
94
+ props: { title: "Please Log In" },
95
+ },
96
+ },
97
+ }
98
+ ```
99
+
100
+ **Proof:**
101
+
102
+ 1. `auth-machine.ts` imports: `createMachine`, `assign` from `xstate` - **no React**
103
+ 2. `catalog.ts` imports: `z` from `zod`, `defineCatalog` - **no React**
104
+ 3. View structure is plain data: `{ component: "LoginForm", props: {...} }`
105
+ 4. Renderer resolves component name at runtime via catalog
106
+
107
+ **Test Reference:** `test/invariants/strict-separation.test.ts`
108
+
109
+ ```typescript
110
+ const authMachineContent = readFileSync("src/machines/auth-machine.ts", "utf-8");
111
+ const catalogContent = readFileSync("src/machines/catalog.ts", "utf-8");
112
+
113
+ // Verify no React imports
114
+ const reactImportPattern = /from ['"]react['"]/;
115
+ assert.strictEqual(reactImportPattern.test(authMachineContent), false);
116
+ assert.strictEqual(reactImportPattern.test(catalogContent), false);
117
+ ```
118
+
119
+ **Run Test:**
120
+
121
+ ```bash
122
+ cd examples/demo-app
123
+ npm test -- test/invariants/strict-separation.test.ts
124
+ ```
125
+
126
+ **Manual Verification:**
127
+
128
+ ```bash
129
+ grep -rn "from ['\"']react" src/machines/
130
+ # Expected: no output (zero React imports)
131
+ ```
132
+
133
+ ### Why This Matters
134
+
135
+ - **Framework Agnostic:** Same state machine works with React, Vue, Svelte, or any renderer
136
+ - **Portability:** Business logic runs in Node.js, browser, mobile, desktop without changes
137
+ - **Longevity:** Framework churn doesn't affect business logic
138
+
139
+ ---
140
+
141
+ ## 3. Signal-Only Reactivity
142
+
143
+ ### What It Means
144
+
145
+ **React rendering driven by signals, not React state for business logic.**
146
+
147
+ Traditional React apps use `useState` for business state. In Universal Player Architecture, business state lives in actor signals. React observes signals via `useSignalEffect` and only uses `useState` for render triggering.
148
+
149
+ ### How Demo Proves It
150
+
151
+ **Code Location:** `packages/play-react/src/PlayRenderer.tsx` (simplified)
152
+
153
+ ```typescript
154
+ function PlayRenderer({ actor, components }) {
155
+ const [, setTick] = useState(0);
156
+
157
+ // Observe actor.state signal
158
+ useSignalEffect(() => {
159
+ const currentState = actor.state.get();
160
+ setTick((t) => t + 1); // Trigger React re-render
161
+ });
162
+
163
+ const currentState = actor.state.get();
164
+ const view = currentState.meta?.view;
165
+ const Component = components[view.component];
166
+
167
+ return <Component send={actor.send.bind(actor)} {...view.props} />;
168
+ }
169
+ ```
170
+
171
+ **Proof:**
172
+
173
+ 1. `PlayRenderer` uses `useSignalEffect` to observe `actor.state` signal
174
+ 2. `useState` is used ONLY for render triggering (`setTick`), not business state
175
+ 3. Business state comes from `actor.state.get()` - signal, not React state
176
+ 4. Components receive `send` function to forward events to actor
177
+
178
+ **Test Reference:** `test/invariants/signal-only.test.ts`
179
+
180
+ ```typescript
181
+ const playRendererContent = readFileSync("packages/play-react/src/PlayRenderer.tsx", "utf-8");
182
+
183
+ // PlayRenderer must use useSignalEffect for signal observation
184
+ assert.strictEqual(playRendererContent.includes("useSignalEffect"), true);
185
+ ```
186
+
187
+ **Run Test:**
188
+
189
+ ```bash
190
+ cd examples/demo-app
191
+ npm test -- test/invariants/signal-only.test.ts
192
+ ```
193
+
194
+ ### Why This Matters
195
+
196
+ - **Single Source of Truth:** Business state lives in actor, not duplicated in React state
197
+ - **Consistency:** Signal updates are atomic and synchronous - no race conditions
198
+ - **Debuggability:** Inspect actor state via XState DevTools, not React DevTools
199
+
200
+ ---
201
+
202
+ ## 4. Passive Infrastructure
203
+
204
+ ### What It Means
205
+
206
+ **Components forward events to actor, don't control navigation. Zero router imports.**
207
+
208
+ Traditional React components import routing libraries and navigate directly (e.g., `navigate("/dashboard")`). In Universal Player Architecture, components forward events to actor via `send()` and actor decides navigation.
209
+
210
+ ### How Demo Proves It
211
+
212
+ **Code Location:** `src/components/LoginForm.tsx` lines 54-64
213
+
214
+ ```typescript
215
+ const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
216
+ e.preventDefault();
217
+
218
+ // Forward event to actor - component doesn't navigate
219
+ send({ type: "auth.login", username, password });
220
+ };
221
+ ```
222
+
223
+ **Proof:**
224
+
225
+ 1. `LoginForm.tsx` imports: `React`, `useState`, `FormEvent` - **no router imports**
226
+ 2. Component receives `send` function via props (bound to actor)
227
+ 3. Submit button forwards `auth.login` event to actor
228
+ 4. Actor decides if login is valid and transitions to dashboard state
229
+ 5. Router observes actor.currentRoute signal (derived from state) and updates browser URL
230
+
231
+ **Test Reference:** `test/invariants/passive-infra.test.ts`
232
+
233
+ ```typescript
234
+ const componentsDir = "src/components";
235
+ const files = readdirSync(componentsDir).filter((f) => f.endsWith(".tsx"));
236
+
237
+ const routerImportPattern = /from ['"]@tanstack\/react-router['"]/;
238
+
239
+ for (const file of files) {
240
+ const content = readFileSync(join(componentsDir, file), "utf-8");
241
+
242
+ // Verify no TanStack Router imports
243
+ assert.strictEqual(routerImportPattern.test(content), false);
244
+
245
+ // Verify components use send() for event forwarding
246
+ assert.strictEqual(content.includes("send(") || content.includes("send:"), true);
247
+ }
248
+ ```
249
+
250
+ **Run Test:**
251
+
252
+ ```bash
253
+ cd examples/demo-app
254
+ npm test -- test/invariants/passive-infra.test.ts
255
+ ```
256
+
257
+ **Manual Verification:**
258
+
259
+ ```bash
260
+ grep -rn "@tanstack/react-router" src/components/
261
+ # Expected: no output (zero router imports)
262
+ ```
263
+
264
+ ### Why This Matters
265
+
266
+ - **Testability:** Test components without routing libraries - just pass mock `send` function
267
+ - **Flexibility:** Change routing libraries without touching components
268
+ - **Consistency:** Navigation logic centralized in actor, not scattered across components
269
+
270
+ ---
271
+
272
+ ## 5. State-Driven Reset
273
+
274
+ ### What It Means
275
+
276
+ **Browser back button sends event to actor for validation. Infrastructure doesn't decide.**
277
+
278
+ Traditional routers update URL immediately when back button pressed, then components react. In Universal Player Architecture, router sends `play.route` event to actor, actor processes event with always-guards, then state updates.
279
+
280
+ ### How Demo Proves It
281
+
282
+ **Code Location:** `packages/play-tanstack-react-router/src/SignalSyncedHistory.ts` (simplified)
283
+
284
+ ```typescript
285
+ window.addEventListener("popstate", () => {
286
+ const newPath = window.location.pathname;
287
+ // Send event to actor for validation
288
+ actor.send({ type: "play.route", to: newPath });
289
+ });
290
+ ```
291
+
292
+ **Actor handles event:**
293
+
294
+ ```typescript
295
+ dashboard: {
296
+ // Always-guard validates state entry
297
+ always: [
298
+ {
299
+ target: "login",
300
+ guard: ({ context }) => !context.isAuthenticated,
301
+ },
302
+ ];
303
+ }
304
+ ```
305
+
306
+ **Proof:**
307
+
308
+ 1. User presses browser back button
309
+ 2. `popstate` event fires
310
+ 3. `SignalSyncedHistory` sends `{ type: "play.route", to: "/" }` to actor
311
+ 4. Actor processes event - always-guards validate state entry
312
+ 5. If guards allow, state updates
313
+ 6. If guards reject, automatic redirect to valid state
314
+
315
+ **Test Reference:** `test/invariants/state-driven.test.ts`
316
+
317
+ ```typescript
318
+ // Login to dashboard
319
+ actor.send({ type: "auth.login", username: "test", password: "pass" });
320
+ assert.strictEqual(actor.getSnapshot().matches("dashboard"), true);
321
+
322
+ // Simulate browser back button: send play.route event
323
+ actor.send({ type: "play.route", to: "/" });
324
+
325
+ // Actor processes event - always-guards validate state entry
326
+ assert.strictEqual(actor.getSnapshot().matches("home"), true);
327
+ ```
328
+
329
+ **Run Test:**
330
+
331
+ ```bash
332
+ cd examples/demo-app
333
+ npm test -- test/invariants/state-driven.test.ts
334
+ ```
335
+
336
+ ### Why This Matters
337
+
338
+ - **Security:** Browser back can't bypass authentication guards
339
+ - **Consistency:** Navigation validation logic centralized in actor
340
+ - **Predictability:** Actor state is single source of truth, not URL
341
+
342
+ ---
343
+
344
+ ## Why These Invariants Matter
345
+
346
+ ### Traditional Approach Problems
347
+
348
+ **Example: Traditional React + React Router app**
349
+
350
+ ```typescript
351
+ // Component controls navigation directly
352
+ function LoginForm() {
353
+ const navigate = useNavigate();
354
+ const [user, setUser] = useState(null);
355
+
356
+ const handleLogin = async () => {
357
+ const result = await api.login(username, password);
358
+ setUser(result.user); // Business state in React state
359
+ navigate("/dashboard"); // Component decides navigation
360
+ };
361
+
362
+ return <form onSubmit={handleLogin}>...</form>;
363
+ }
364
+
365
+ // Protected route check in router config
366
+ <Route
367
+ path="/dashboard"
368
+ element={
369
+ <ProtectedRoute>
370
+ <Dashboard />
371
+ </ProtectedRoute>
372
+ }
373
+ />;
374
+ ```
375
+
376
+ **Problems:**
377
+
378
+ 1. **No Actor Authority:** `navigate("/dashboard")` bypasses authentication logic
379
+ 2. **No Strict Separation:** Business logic (`api.login`, `setUser`) mixed with React
380
+ 3. **No Signal-Only Reactivity:** Business state in `useState` - duplicated across components
381
+ 4. **No Passive Infrastructure:** Component imports router and navigates directly
382
+ 5. **No State-Driven Reset:** Browser back updates URL immediately - component reacts after
383
+
384
+ ### Universal Player Architecture Solution
385
+
386
+ **Example: This demo app**
387
+
388
+ ```typescript
389
+ // State machine defines behavior
390
+ const authMachine = createMachine({
391
+ states: {
392
+ loggedOut: {
393
+ on: {
394
+ "auth.login": { target: "loggedIn" },
395
+ },
396
+ },
397
+ loggedIn: {
398
+ // Always-guard validates state entry
399
+ always: [
400
+ {
401
+ target: "loggedOut",
402
+ guard: ({ context }) => !context.isAuthenticated,
403
+ },
404
+ ],
405
+ },
406
+ },
407
+ });
408
+
409
+ // Component forwards events
410
+ function LoginForm({ send }) {
411
+ const [username, setUsername] = useState("");
412
+ const [password, setPassword] = useState("");
413
+
414
+ const handleSubmit = () => {
415
+ send({ type: "auth.login", username, password });
416
+ };
417
+
418
+ return <form onSubmit={handleSubmit}>...</form>;
419
+ }
420
+ ```
421
+
422
+ **Benefits:**
423
+
424
+ 1. **Actor Authority:** Always-guard validates state entry - component can't bypass
425
+ 2. **Strict Separation:** State machine has zero React imports
426
+ 3. **Signal-Only Reactivity:** Business state in actor signals
427
+ 4. **Passive Infrastructure:** Component forwards events, doesn't navigate
428
+ 5. **State-Driven Reset:** Browser back sends event to actor for validation via always-guards
429
+
430
+ ---
431
+
432
+ ## Running All Tests
433
+
434
+ Validate all 5 invariants:
435
+
436
+ ```bash
437
+ cd examples/demo-app
438
+ npm test
439
+ ```
440
+
441
+ Expected output:
442
+
443
+ ```
444
+ ✔ DEMO-02: Actor Authority - Guards reject invalid navigation
445
+ ✔ DEMO-03: Strict Separation - Business logic has zero React imports
446
+ ✔ DEMO-04: State-Driven Reset - Browser back sends event to actor
447
+ ✔ DEMO-05: Passive Infrastructure - Components never import TanStack Router
448
+ ✔ DEMO-06: Signal-Only Reactivity - PlayRenderer uses signals not React state
449
+
450
+ ℹ tests 5
451
+ ℹ pass 5
452
+ ℹ fail 0
453
+ ```
454
+
455
+ ---
456
+
457
+ ## Next Steps
458
+
459
+ - **Line-by-Line Walkthrough:** Read [ARCHITECTURE.md](./ARCHITECTURE.md) for detailed code explanations
460
+ - **Swap Frameworks:** Read [SWAP-REACT.md](./SWAP-REACT.md) to see how business logic remains unchanged when swapping React for Vue/Svelte
461
+ - **Build Your Own:** Use invariant tests as regression protection when building your application