@xmachines/play-router 1.0.0-beta.21 → 1.0.0-beta.23

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 (39) hide show
  1. package/README.md +20 -5
  2. package/dist/base-route-map.d.ts +0 -2
  3. package/dist/base-route-map.d.ts.map +1 -1
  4. package/dist/base-route-map.js +7 -48
  5. package/dist/base-route-map.js.map +1 -1
  6. package/dist/create-route-map.d.ts.map +1 -1
  7. package/dist/create-route-map.js +1 -42
  8. package/dist/create-route-map.js.map +1 -1
  9. package/dist/find-route.d.ts.map +1 -1
  10. package/dist/find-route.js +20 -48
  11. package/dist/find-route.js.map +1 -1
  12. package/dist/index.d.ts +2 -4
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +2 -8
  15. package/dist/index.js.map +1 -1
  16. package/dist/router-bridge-base.d.ts +12 -0
  17. package/dist/router-bridge-base.d.ts.map +1 -1
  18. package/dist/router-bridge-base.js +16 -3
  19. package/dist/router-bridge-base.js.map +1 -1
  20. package/dist/router-sync.d.ts +5 -6
  21. package/dist/router-sync.d.ts.map +1 -1
  22. package/dist/router-sync.js +3 -3
  23. package/dist/url-pattern-utils.d.ts +60 -0
  24. package/dist/url-pattern-utils.d.ts.map +1 -0
  25. package/dist/url-pattern-utils.js +77 -0
  26. package/dist/url-pattern-utils.js.map +1 -0
  27. package/package.json +12 -8
  28. package/dist/connect-router.d.ts +0 -56
  29. package/dist/connect-router.d.ts.map +0 -1
  30. package/dist/connect-router.js +0 -109
  31. package/dist/connect-router.js.map +0 -1
  32. package/dist/create-browser-history.d.ts +0 -101
  33. package/dist/create-browser-history.d.ts.map +0 -1
  34. package/dist/create-browser-history.js +0 -120
  35. package/dist/create-browser-history.js.map +0 -1
  36. package/dist/create-router.d.ts +0 -73
  37. package/dist/create-router.d.ts.map +0 -1
  38. package/dist/create-router.js +0 -63
  39. package/dist/create-router.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url-pattern-utils.d.ts","sourceRoot":"","sources":["../src/url-pattern-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG;IAC5B,IAAI,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG;QAClC,QAAQ,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;SAAE,CAAC;KACzD,GAAG,IAAI,CAAC;IACT,IAAI,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;CAC3C,CAAC;AAEF,qDAAqD;AACrD,MAAM,MAAM,cAAc,GAAG,KAAK,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,KAAK,cAAc,CAAC;AAEhF;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,cAAc,GAAG,SAAS,CAE9D;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMhD;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,EACxD,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,EAChC,QAAQ,EAAE,MAAM,GACd,CAAC,EAAE,CA2BL"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Shared URLPattern utilities — internal module, NOT exported from index.ts
3
+ *
4
+ * Provides the common URLPattern plumbing shared by:
5
+ * - `create-route-map.ts`
6
+ * - `base-route-map.ts`
7
+ * - `find-route.ts`
8
+ * - `router-bridge-base.ts`
9
+ *
10
+ * URLPattern is accessed via globalThis — consumers must load a polyfill on
11
+ * environments without native support (Node < 24, older browsers).
12
+ *
13
+ * @internal
14
+ */
15
+ /**
16
+ * Retrieve the URLPattern constructor from `globalThis`, or `undefined` if
17
+ * it is not available (Node < 24, older browsers without a polyfill).
18
+ */
19
+ export function getURLPatternCtor() {
20
+ return globalThis["URLPattern"];
21
+ }
22
+ /**
23
+ * Compute the bucket index key for a given path or pattern.
24
+ *
25
+ * The key is the first path segment (e.g. `"settings"` for `"/settings/:id"`).
26
+ * Parameterised first segments (`:lang`) fall into the `"*"` wildcard bucket.
27
+ * The root path `"/"` maps to the key `"/"`.
28
+ *
29
+ * @param path - URL path or route pattern string
30
+ */
31
+ export function getIndexKey(path) {
32
+ const trimmed = path.startsWith("/") ? path.slice(1) : path;
33
+ if (trimmed.length === 0)
34
+ return "/";
35
+ const segment = trimmed.split("/")[0];
36
+ if (segment === undefined || segment.startsWith(":"))
37
+ return "*";
38
+ return segment;
39
+ }
40
+ /**
41
+ * Merge the named-segment bucket and the wildcard bucket for a given index key,
42
+ * preserving insertion order (by `order` field).
43
+ *
44
+ * @param patternBuckets - Map of bucket key → ordered pattern entries
45
+ * @param indexKey - First-segment key computed by `getIndexKey`
46
+ */
47
+ export function getCandidates(patternBuckets, indexKey) {
48
+ const bucket = patternBuckets.get(indexKey) ?? [];
49
+ const wildcardBucket = patternBuckets.get("*") ?? [];
50
+ if (bucket.length === 0)
51
+ return wildcardBucket;
52
+ if (wildcardBucket.length === 0)
53
+ return bucket;
54
+ const merged = [];
55
+ let b = 0;
56
+ let w = 0;
57
+ while (b < bucket.length && w < wildcardBucket.length) {
58
+ if (bucket[b].order < wildcardBucket[w].order) {
59
+ merged.push(bucket[b]);
60
+ b += 1;
61
+ }
62
+ else {
63
+ merged.push(wildcardBucket[w]);
64
+ w += 1;
65
+ }
66
+ }
67
+ while (b < bucket.length) {
68
+ merged.push(bucket[b]);
69
+ b += 1;
70
+ }
71
+ while (w < wildcardBucket.length) {
72
+ merged.push(wildcardBucket[w]);
73
+ w += 1;
74
+ }
75
+ return merged;
76
+ }
77
+ //# sourceMappingURL=url-pattern-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url-pattern-utils.js","sourceRoot":"","sources":["../src/url-pattern-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAgBH;;;GAGG;AACH,MAAM,UAAU,iBAAiB;IAChC,OAAQ,UAAsC,CAAC,YAAY,CAA+B,CAAC;AAC5F,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY;IACvC,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IACrC,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACtC,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC;IACjE,OAAO,OAAO,CAAC;AAChB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAC5B,cAAgC,EAChC,QAAgB;IAEhB,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;IAClD,MAAM,cAAc,GAAG,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;IACrD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,cAAc,CAAC;IAC/C,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC;IAE/C,MAAM,MAAM,GAAQ,EAAE,CAAC;IACvB,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,IAAI,CAAC,GAAG,cAAc,CAAC,MAAM,EAAE,CAAC;QACvD,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;YAC/C,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YACvB,CAAC,IAAI,CAAC,CAAC;QACR,CAAC;aAAM,CAAC;YACP,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;YAC/B,CAAC,IAAI,CAAC,CAAC;QACR,CAAC;IACF,CAAC;IACD,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QACvB,CAAC,IAAI,CAAC,CAAC;IACR,CAAC;IACD,OAAO,CAAC,GAAG,cAAc,CAAC,MAAM,EAAE,CAAC;QAClC,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/B,CAAC,IAAI,CAAC,CAAC;IACR,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xmachines/play-router",
3
- "version": "1.0.0-beta.21",
3
+ "version": "1.0.0-beta.23",
4
4
  "description": "Route tree extraction from XState v5 state machines. Part of @xmachines/play Universal Player Architecture.",
5
5
  "keywords": [
6
6
  "routing",
@@ -34,21 +34,25 @@
34
34
  },
35
35
  "scripts": {
36
36
  "build": "tsc --build",
37
- "clean": "rm -rf dist *.tsbuildinfo coverage .vitest-attachments test/browser/__screenshots__",
37
+ "clean": "rm -rf dist *.tsbuildinfo coverage .vitest-attachments test/browser/__screenshots__ node_modules/.vite*",
38
+ "lint": "oxlint .",
39
+ "format": "oxfmt .",
38
40
  "test": "vitest",
39
41
  "prepublishOnly": "npm run build"
40
42
  },
41
43
  "dependencies": {
42
- "@statelyai/graph": "^0.9.0",
43
- "@xmachines/play": "1.0.0-beta.21",
44
- "@xmachines/play-actor": "1.0.0-beta.21",
45
- "@xmachines/play-signals": "1.0.0-beta.21",
44
+ "@statelyai/graph": "^0.10.0",
45
+ "@xmachines/play": "1.0.0-beta.23",
46
+ "@xmachines/play-actor": "1.0.0-beta.23",
47
+ "@xmachines/play-signals": "1.0.0-beta.23",
46
48
  "quick-lru": "^7.0.0"
47
49
  },
48
50
  "devDependencies": {
49
51
  "@types/node": "^25.5.0",
50
- "@xmachines/play-xstate": "1.0.0-beta.21",
51
- "@xmachines/shared": "1.0.0-beta.21",
52
+ "@xmachines/play-xstate": "1.0.0-beta.23",
53
+ "@xmachines/shared": "1.0.0-beta.23",
54
+ "oxfmt": "^0.43.0",
55
+ "oxlint": "^1.57.0",
52
56
  "typescript": "^5.9.3 || ^6.0.2",
53
57
  "urlpattern-polyfill": "^10.1.0",
54
58
  "vitest": "^4.1.2",
@@ -1,56 +0,0 @@
1
- import type { AbstractActor, Routable } from "@xmachines/play-actor";
2
- import type { AnyActorLogic } from "xstate";
3
- import type { VanillaRouter } from "./create-router.js";
4
- import type { RouteMap } from "./create-route-map.js";
5
- export interface ConnectRouterOptions {
6
- actor: AbstractActor<AnyActorLogic> & Routable;
7
- router: VanillaRouter;
8
- routeMap: RouteMap;
9
- }
10
- /**
11
- * Connect vanilla router to actor (pure browser integration).
12
- *
13
- * This is the LOW-LEVEL API for maximum control. No JSX, no rendering,
14
- * just actor ↔ router synchronization.
15
- *
16
- * Use this when:
17
- * - You want manual control over rendering
18
- * - Using non-JSX framework (jQuery, Alpine, HTMX, etc.)
19
- * - Building custom integration
20
- *
21
- * For JSX frameworks, use framework adapters instead:
22
- * - @xmachines/play-react (React components)
23
- * - Future: @xmachines/play-preact, @xmachines/play-solid, @xmachines/play-vue
24
- *
25
- * Architecture:
26
- * - Subscribes to history changes → sends play.route to actor
27
- * - Watches actor.currentRoute signal → updates browser URL
28
- * - Prevents circular updates (history change triggers actor, actor triggers history)
29
- * - Returns cleanup function
30
- *
31
- * Usage:
32
- * ```typescript
33
- * import { createBrowserHistory, createRouter, connectRouter } from '@xmachines/play-router';
34
- *
35
- * const history = createBrowserHistory({ window });
36
- * const router = createRouter({ routeTree, history });
37
- *
38
- * // Connect router to actor
39
- * const disconnect = connectRouter({ actor, router, routeMap });
40
- *
41
- * // User handles rendering (vanilla JS)
42
- * const watcher = new Signal.subtle.Watcher(() => {
43
- * queueMicrotask(() => {
44
- * const view = actor.currentView.get();
45
- * document.getElementById('app').innerHTML = render(view);
46
- * });
47
- * });
48
- * watcher.watch(actor.currentView);
49
- *
50
- * // Later: cleanup
51
- * disconnect();
52
- * watcher.unwatch(actor.currentView);
53
- * ```
54
- */
55
- export declare function connectRouter(options: ConnectRouterOptions): () => void;
56
- //# sourceMappingURL=connect-router.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"connect-router.d.ts","sourceRoot":"","sources":["../src/connect-router.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGtD,MAAM,WAAW,oBAAoB;IACpC,KAAK,EAAE,aAAa,CAAC,aAAa,CAAC,GAAG,QAAQ,CAAC;IAC/C,MAAM,EAAE,aAAa,CAAC;IACtB,QAAQ,EAAE,QAAQ,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,MAAM,IAAI,CAqEvE"}
@@ -1,109 +0,0 @@
1
- import { watchSignal } from "@xmachines/play-signals";
2
- import { buildPlayRouteEvent } from "./router-sync.js";
3
- /**
4
- * Connect vanilla router to actor (pure browser integration).
5
- *
6
- * This is the LOW-LEVEL API for maximum control. No JSX, no rendering,
7
- * just actor ↔ router synchronization.
8
- *
9
- * Use this when:
10
- * - You want manual control over rendering
11
- * - Using non-JSX framework (jQuery, Alpine, HTMX, etc.)
12
- * - Building custom integration
13
- *
14
- * For JSX frameworks, use framework adapters instead:
15
- * - @xmachines/play-react (React components)
16
- * - Future: @xmachines/play-preact, @xmachines/play-solid, @xmachines/play-vue
17
- *
18
- * Architecture:
19
- * - Subscribes to history changes → sends play.route to actor
20
- * - Watches actor.currentRoute signal → updates browser URL
21
- * - Prevents circular updates (history change triggers actor, actor triggers history)
22
- * - Returns cleanup function
23
- *
24
- * Usage:
25
- * ```typescript
26
- * import { createBrowserHistory, createRouter, connectRouter } from '@xmachines/play-router';
27
- *
28
- * const history = createBrowserHistory({ window });
29
- * const router = createRouter({ routeTree, history });
30
- *
31
- * // Connect router to actor
32
- * const disconnect = connectRouter({ actor, router, routeMap });
33
- *
34
- * // User handles rendering (vanilla JS)
35
- * const watcher = new Signal.subtle.Watcher(() => {
36
- * queueMicrotask(() => {
37
- * const view = actor.currentView.get();
38
- * document.getElementById('app').innerHTML = render(view);
39
- * });
40
- * });
41
- * watcher.watch(actor.currentView);
42
- *
43
- * // Later: cleanup
44
- * disconnect();
45
- * watcher.unwatch(actor.currentView);
46
- * ```
47
- */
48
- export function connectRouter(options) {
49
- const { actor, router, routeMap } = options;
50
- const { history } = router;
51
- // Prevent circular updates — single navigation-processing guard.
52
- // Matches RouterBridgeBase.isProcessingNavigation pattern.
53
- // Reduces race-condition surface from 2³=8 flag combinations to 2.
54
- let isProcessingNavigation = false;
55
- // Subscribe to history changes (browser navigation)
56
- const unsubscribeHistory = history.subscribe((location) => {
57
- if (isProcessingNavigation)
58
- return;
59
- const nextRoute = buildPlayRouteEvent({
60
- pathname: location.pathname,
61
- search: location.search,
62
- resolve: (pathname) => routeMap.resolve(pathname),
63
- });
64
- if (nextRoute) {
65
- isProcessingNavigation = true;
66
- actor.send(nextRoute.event);
67
- // Check immediately if actor redirected (always-guard)
68
- // XState processes events synchronously, so snapshot is already updated
69
- const newActorRoute = actor.currentRoute.get();
70
- if (newActorRoute && newActorRoute !== nextRoute.pathname) {
71
- // Actor redirected - update URL
72
- history.replace(newActorRoute);
73
- }
74
- // Clear flag synchronously to prevent race conditions
75
- isProcessingNavigation = false;
76
- }
77
- });
78
- // Watch actor's currentRoute signal (actor-driven navigation)
79
- const unwatchRoute = watchSignal(actor.currentRoute, (route) => {
80
- if (isProcessingNavigation)
81
- return;
82
- const currentPath = history.location.pathname;
83
- if (route && route !== currentPath) {
84
- isProcessingNavigation = true;
85
- history.push(route);
86
- isProcessingNavigation = false;
87
- }
88
- });
89
- // Sync initial URL to actor
90
- // ONLY send route event if URL doesn't match actor's initial state
91
- const initialPath = history.location.pathname;
92
- const initialActorRoute = actor.currentRoute.get();
93
- if (initialPath !== initialActorRoute) {
94
- const nextRoute = buildPlayRouteEvent({
95
- pathname: initialPath,
96
- search: history.location.search,
97
- resolve: (pathname) => routeMap.resolve(pathname),
98
- });
99
- if (nextRoute) {
100
- actor.send(nextRoute.event);
101
- }
102
- }
103
- // Return cleanup
104
- return () => {
105
- unsubscribeHistory();
106
- unwatchRoute();
107
- };
108
- }
109
- //# sourceMappingURL=connect-router.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"connect-router.js","sourceRoot":"","sources":["../src/connect-router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAKtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAQvD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,MAAM,UAAU,aAAa,CAAC,OAA6B;IAC1D,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;IAC5C,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;IAE3B,iEAAiE;IACjE,2DAA2D;IAC3D,mEAAmE;IACnE,IAAI,sBAAsB,GAAG,KAAK,CAAC;IAEnC,oDAAoD;IACpD,MAAM,kBAAkB,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,QAAQ,EAAE,EAAE;QACzD,IAAI,sBAAsB;YAAE,OAAO;QAEnC,MAAM,SAAS,GAAG,mBAAmB,CAAC;YACrC,QAAQ,EAAE,QAAQ,CAAC,QAAQ;YAC3B,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,OAAO,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC;SACjD,CAAC,CAAC;QACH,IAAI,SAAS,EAAE,CAAC;YACf,sBAAsB,GAAG,IAAI,CAAC;YAE9B,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YAE5B,uDAAuD;YACvD,wEAAwE;YACxE,MAAM,aAAa,GAAG,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC;YAC/C,IAAI,aAAa,IAAI,aAAa,KAAK,SAAS,CAAC,QAAQ,EAAE,CAAC;gBAC3D,gCAAgC;gBAChC,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;YAChC,CAAC;YAED,sDAAsD;YACtD,sBAAsB,GAAG,KAAK,CAAC;QAChC,CAAC;IACF,CAAC,CAAC,CAAC;IAEH,8DAA8D;IAC9D,MAAM,YAAY,GAAG,WAAW,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC,KAAK,EAAE,EAAE;QAC9D,IAAI,sBAAsB;YAAE,OAAO;QAEnC,MAAM,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAC9C,IAAI,KAAK,IAAI,KAAK,KAAK,WAAW,EAAE,CAAC;YACpC,sBAAsB,GAAG,IAAI,CAAC;YAC9B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACpB,sBAAsB,GAAG,KAAK,CAAC;QAChC,CAAC;IACF,CAAC,CAAC,CAAC;IAEH,4BAA4B;IAC5B,mEAAmE;IACnE,MAAM,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;IAC9C,MAAM,iBAAiB,GAAG,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC;IAEnD,IAAI,WAAW,KAAK,iBAAiB,EAAE,CAAC;QACvC,MAAM,SAAS,GAAG,mBAAmB,CAAC;YACrC,QAAQ,EAAE,WAAW;YACrB,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,MAAM;YAC/B,OAAO,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC;SACjD,CAAC,CAAC;QACH,IAAI,SAAS,EAAE,CAAC;YACf,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC;IACF,CAAC;IAED,iBAAiB;IACjB,OAAO,GAAG,EAAE;QACX,kBAAkB,EAAE,CAAC;QACrB,YAAY,EAAE,CAAC;IAChB,CAAC,CAAC;AACH,CAAC"}
@@ -1,101 +0,0 @@
1
- export interface BrowserHistory {
2
- /**
3
- * Get current location state
4
- */
5
- readonly location: {
6
- pathname: string;
7
- search: string;
8
- hash: string;
9
- state: unknown;
10
- };
11
- /**
12
- * Push new URL to history
13
- */
14
- push(path: string, state?: unknown): void;
15
- /**
16
- * Replace current URL in history
17
- */
18
- replace(path: string, state?: unknown): void;
19
- /**
20
- * Go back/forward
21
- */
22
- go(delta: number): void;
23
- back(): void;
24
- forward(): void;
25
- /**
26
- * Subscribe to history changes
27
- * Returns unsubscribe function
28
- */
29
- subscribe(listener: (location: BrowserHistory["location"]) => void): () => void;
30
- /**
31
- * Create href from path
32
- */
33
- createHref(path: string): string;
34
- /**
35
- * Cleanup.
36
- *
37
- * Safe to call more than once. When multiple wrappers share the same
38
- * `window.history`, the original methods are restored only after the last
39
- * wrapper is destroyed.
40
- */
41
- destroy(): void;
42
- }
43
- /**
44
- * Minimal window interface for createBrowserHistory
45
- *
46
- * Structural interface covering only the properties actually used.
47
- * Accepts Window, JSDOM window, or any compatible test double.
48
- * Avoids coupling to `Window & typeof globalThis` which varies across environments.
49
- *
50
- * @public
51
- */
52
- export interface BrowserWindow {
53
- history: {
54
- readonly state: unknown;
55
- pushState(state: unknown, title: string, url?: string | null): void;
56
- replaceState(state: unknown, title: string, url?: string | null): void;
57
- go(delta: number): void;
58
- back(): void;
59
- forward(): void;
60
- };
61
- location: {
62
- readonly pathname: string;
63
- readonly search: string;
64
- readonly hash: string;
65
- };
66
- addEventListener(type: "popstate", listener: () => void): void;
67
- removeEventListener(type: "popstate", listener: () => void): void;
68
- }
69
- /**
70
- * Create browser history that wraps window.history
71
- *
72
- * Aligned with TanStack Router's history interface for API parallelism.
73
- *
74
- * Architecture:
75
- * - Patches window.history.pushState/replaceState to detect changes
76
- * - Listens to popstate for browser back/forward
77
- * - Provides subscribe() for listeners (like PlayRouterProvider)
78
- * - Testable (accepts window object)
79
- *
80
- * Usage:
81
- * ```typescript
82
- * const history = createBrowserHistory({ window });
83
- *
84
- * const unsubscribe = history.subscribe((location) => {
85
- * console.log('URL changed:', location.pathname);
86
- * });
87
- *
88
- * history.push('/new-path');
89
- *
90
- * // Later:
91
- * unsubscribe();
92
- * history.destroy();
93
- * ```
94
- *
95
- * `destroy()` is idempotent and cooperates with other wrappers created for the
96
- * same `window` instance.
97
- */
98
- export declare function createBrowserHistory(options: {
99
- window: BrowserWindow;
100
- }): BrowserHistory;
101
- //# sourceMappingURL=create-browser-history.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"create-browser-history.d.ts","sourceRoot":"","sources":["../src/create-browser-history.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC9B;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,OAAO,CAAC;KACf,CAAC;IAEF;;OAEG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAE1C;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAE7C;;OAEG;IACH,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,IAAI,IAAI,CAAC;IACb,OAAO,IAAI,IAAI,CAAC;IAEhB;;;OAGG;IACH,SAAS,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,cAAc,CAAC,UAAU,CAAC,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAEhF;;OAEG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAEjC;;;;;;OAMG;IACH,OAAO,IAAI,IAAI,CAAC;CAChB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,aAAa;IAC7B,OAAO,EAAE;QACR,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;QACxB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;QACpE,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;QACvE,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,IAAI,IAAI,IAAI,CAAC;QACb,OAAO,IAAI,IAAI,CAAC;KAChB,CAAC;IACF,QAAQ,EAAE;QACT,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAC/D,mBAAmB,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;CAClE;AAWD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE;IAAE,MAAM,EAAE,aAAa,CAAA;CAAE,GAAG,cAAc,CA8EvF"}
@@ -1,120 +0,0 @@
1
- const historyPatchStates = new WeakMap();
2
- /**
3
- * Create browser history that wraps window.history
4
- *
5
- * Aligned with TanStack Router's history interface for API parallelism.
6
- *
7
- * Architecture:
8
- * - Patches window.history.pushState/replaceState to detect changes
9
- * - Listens to popstate for browser back/forward
10
- * - Provides subscribe() for listeners (like PlayRouterProvider)
11
- * - Testable (accepts window object)
12
- *
13
- * Usage:
14
- * ```typescript
15
- * const history = createBrowserHistory({ window });
16
- *
17
- * const unsubscribe = history.subscribe((location) => {
18
- * console.log('URL changed:', location.pathname);
19
- * });
20
- *
21
- * history.push('/new-path');
22
- *
23
- * // Later:
24
- * unsubscribe();
25
- * history.destroy();
26
- * ```
27
- *
28
- * `destroy()` is idempotent and cooperates with other wrappers created for the
29
- * same `window` instance.
30
- */
31
- export function createBrowserHistory(options) {
32
- const win = options.window;
33
- const listeners = new Set();
34
- const patchState = getOrCreatePatchState(win);
35
- let destroyed = false;
36
- function getLocation() {
37
- return {
38
- pathname: win.location.pathname,
39
- search: win.location.search,
40
- hash: win.location.hash,
41
- state: win.history.state,
42
- };
43
- }
44
- function notify() {
45
- const location = getLocation();
46
- listeners.forEach((listener) => listener(location));
47
- }
48
- patchState.wrappers.add(notify);
49
- // Listen to popstate
50
- const popstateHandler = () => notify();
51
- win.addEventListener("popstate", popstateHandler);
52
- return {
53
- get location() {
54
- return getLocation();
55
- },
56
- push(path, state) {
57
- win.history.pushState(state, "", path);
58
- },
59
- replace(path, state) {
60
- win.history.replaceState(state, "", path);
61
- },
62
- go(delta) {
63
- win.history.go(delta);
64
- },
65
- back() {
66
- win.history.back();
67
- },
68
- forward() {
69
- win.history.forward();
70
- },
71
- subscribe(listener) {
72
- listeners.add(listener);
73
- return () => listeners.delete(listener);
74
- },
75
- createHref(path) {
76
- return path;
77
- },
78
- destroy() {
79
- if (destroyed) {
80
- return;
81
- }
82
- destroyed = true;
83
- patchState.wrappers.delete(notify);
84
- patchState.refCount -= 1;
85
- win.removeEventListener("popstate", popstateHandler);
86
- listeners.clear();
87
- if (patchState.refCount === 0) {
88
- win.history.pushState = patchState.originalPushState;
89
- win.history.replaceState = patchState.originalReplaceState;
90
- historyPatchStates.delete(win.history);
91
- }
92
- },
93
- };
94
- }
95
- function getOrCreatePatchState(win) {
96
- const existing = historyPatchStates.get(win.history);
97
- if (existing) {
98
- existing.refCount += 1;
99
- return existing;
100
- }
101
- const originalPushState = win.history.pushState.bind(win.history);
102
- const originalReplaceState = win.history.replaceState.bind(win.history);
103
- const patchState = {
104
- originalPushState,
105
- originalReplaceState,
106
- wrappers: new Set(),
107
- refCount: 1,
108
- };
109
- win.history.pushState = function (state, title, url) {
110
- patchState.originalPushState(state, title, url);
111
- patchState.wrappers.forEach((notify) => notify());
112
- };
113
- win.history.replaceState = function (state, title, url) {
114
- patchState.originalReplaceState(state, title, url);
115
- patchState.wrappers.forEach((notify) => notify());
116
- };
117
- historyPatchStates.set(win.history, patchState);
118
- return patchState;
119
- }
120
- //# sourceMappingURL=create-browser-history.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"create-browser-history.js","sourceRoot":"","sources":["../src/create-browser-history.ts"],"names":[],"mappings":"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"}
@@ -1,73 +0,0 @@
1
- import type { RouteTree } from "./types.js";
2
- import type { BrowserHistory } from "./create-browser-history.js";
3
- export interface VanillaRouter {
4
- /**
5
- * History instance
6
- */
7
- readonly history: BrowserHistory;
8
- /**
9
- * Route tree (for structure reference)
10
- */
11
- readonly routeTree: RouteTree;
12
- /**
13
- * Cleanup
14
- */
15
- destroy(): void;
16
- }
17
- /**
18
- * Create vanilla router for framework-agnostic routing.
19
- *
20
- * Architecture:
21
- * - Just wraps history and routeTree
22
- * - Does NOT embed routeMap (providers need it as prop)
23
- * - Framework-agnostic history management only
24
- *
25
- * Why no routeMap:
26
- * - Router doesn't know about state IDs (that's Play-specific)
27
- * - RouteMap is the bridge between router and actor
28
- * - Provider receives routeMap as prop to do path → state ID resolution
29
- *
30
- * Usage (parallel to TanStack mode):
31
- * ```typescript
32
- * // Both modes: identical setup
33
- * const routeTree = extractMachineRoutes(machine);
34
- * const routeMap = createRouteMap(routeTree);
35
- * const history = createBrowserHistory({ window });
36
- *
37
- * // Vanilla router
38
- * const router = createRouter({ routeTree, history });
39
- *
40
- * // Provider needs routeMap as prop (router doesn't have it)
41
- * <PlayRouterProvider
42
- * actor={actor}
43
- * router={router}
44
- * routeMap={routeMap} // ← Must pass separately
45
- * renderer={(currentActor, currentRouter) => {
46
- * void currentRouter;
47
- * return <Renderer actor={currentActor} components={components} />;
48
- * }}
49
- * />
50
- * ```
51
- *
52
- * Compare to TanStack mode:
53
- * ```typescript
54
- * // TanStack router (external library)
55
- * const router = TanStackCreateRouter({ routeTree, history });
56
- *
57
- * // Provider needs routeMap as prop (same as vanilla!)
58
- * <PlayRouterProvider
59
- * actor={actor}
60
- * router={router}
61
- * routeMap={routeMap} // ← Must pass separately
62
- * renderer={(currentActor, currentRouter) => {
63
- * void currentRouter;
64
- * return <Renderer actor={currentActor} components={components} />;
65
- * }}
66
- * />
67
- * ```
68
- */
69
- export declare function createRouter(options: {
70
- routeTree: RouteTree;
71
- history: BrowserHistory;
72
- }): VanillaRouter;
73
- //# sourceMappingURL=create-router.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"create-router.d.ts","sourceRoot":"","sources":["../src/create-router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAElE,MAAM,WAAW,aAAa;IAC7B;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IAEjC;;OAEG;IACH,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAE9B;;OAEG;IACH,OAAO,IAAI,IAAI,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE;IACrC,SAAS,EAAE,SAAS,CAAC;IACrB,OAAO,EAAE,cAAc,CAAC;CACxB,GAAG,aAAa,CAWhB"}