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

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 (66) hide show
  1. package/README.md +169 -47
  2. package/dist/base-route-map.d.ts +116 -0
  3. package/dist/base-route-map.d.ts.map +1 -0
  4. package/dist/base-route-map.js +206 -0
  5. package/dist/base-route-map.js.map +1 -0
  6. package/dist/build-tree.d.ts.map +1 -1
  7. package/dist/build-tree.js +6 -5
  8. package/dist/build-tree.js.map +1 -1
  9. package/dist/connect-router.d.ts.map +1 -1
  10. package/dist/connect-router.js +35 -45
  11. package/dist/connect-router.js.map +1 -1
  12. package/dist/create-browser-history.d.ts +38 -5
  13. package/dist/create-browser-history.d.ts.map +1 -1
  14. package/dist/create-browser-history.js +43 -17
  15. package/dist/create-browser-history.js.map +1 -1
  16. package/dist/create-route-map.d.ts +21 -1
  17. package/dist/create-route-map.d.ts.map +1 -1
  18. package/dist/create-route-map.js +73 -22
  19. package/dist/create-route-map.js.map +1 -1
  20. package/dist/errors.d.ts +75 -0
  21. package/dist/errors.d.ts.map +1 -0
  22. package/dist/errors.js +85 -0
  23. package/dist/errors.js.map +1 -0
  24. package/dist/extract-routes.d.ts +5 -31
  25. package/dist/extract-routes.d.ts.map +1 -1
  26. package/dist/extract-routes.js +70 -49
  27. package/dist/extract-routes.js.map +1 -1
  28. package/dist/find-route.d.ts +44 -0
  29. package/dist/find-route.d.ts.map +1 -0
  30. package/dist/find-route.js +126 -0
  31. package/dist/find-route.js.map +1 -0
  32. package/dist/index.d.ts +9 -48
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +16 -131
  35. package/dist/index.js.map +1 -1
  36. package/dist/machine-to-graph.d.ts +17 -0
  37. package/dist/machine-to-graph.d.ts.map +1 -0
  38. package/dist/machine-to-graph.js +115 -0
  39. package/dist/machine-to-graph.js.map +1 -0
  40. package/dist/query.d.ts +44 -1
  41. package/dist/query.d.ts.map +1 -1
  42. package/dist/query.js +80 -3
  43. package/dist/query.js.map +1 -1
  44. package/dist/router-bridge-base.d.ts +49 -19
  45. package/dist/router-bridge-base.d.ts.map +1 -1
  46. package/dist/router-bridge-base.js +120 -56
  47. package/dist/router-bridge-base.js.map +1 -1
  48. package/dist/router-sync.d.ts +62 -0
  49. package/dist/router-sync.d.ts.map +1 -0
  50. package/dist/router-sync.js +87 -0
  51. package/dist/router-sync.js.map +1 -0
  52. package/dist/types.d.ts +73 -14
  53. package/dist/types.d.ts.map +1 -1
  54. package/dist/validate-routes.d.ts +9 -9
  55. package/dist/validate-routes.d.ts.map +1 -1
  56. package/dist/validate-routes.js +12 -11
  57. package/dist/validate-routes.js.map +1 -1
  58. package/package.json +36 -18
  59. package/dist/crawl-machine.d.ts +0 -74
  60. package/dist/crawl-machine.d.ts.map +0 -1
  61. package/dist/crawl-machine.js +0 -95
  62. package/dist/crawl-machine.js.map +0 -1
  63. package/dist/extract-route.d.ts +0 -25
  64. package/dist/extract-route.d.ts.map +0 -1
  65. package/dist/extract-route.js +0 -63
  66. package/dist/extract-route.js.map +0 -1
@@ -0,0 +1,206 @@
1
+ /**
2
+ * BaseRouteMap — Shared bidirectional route mapping base class
3
+ *
4
+ * Provides bucket-based pattern matching shared across all framework adapters.
5
+ * Adapters extend this class rather than duplicating the pattern-match logic.
6
+ *
7
+ * Algorithm: O(1) exact match via Map, then bucket-based O(k) pattern match
8
+ * where k = routes in the first-segment bucket (typically << total routes).
9
+ * Uses URLPattern for parameterized route matching (same engine as createRouteMap).
10
+ */
11
+ import QuickLRU from "quick-lru";
12
+ function getURLPatternCtor() {
13
+ return globalThis["URLPattern"];
14
+ }
15
+ /**
16
+ * Normalize a route path so its parameter names are valid URLPattern identifiers.
17
+ *
18
+ * URLPattern requires parameter names to be valid JS identifiers (no hyphens).
19
+ * Hyphenated names like `:cat-id` are replaced with underscored equivalents
20
+ * (`:cat_id`) for the URLPattern compilation step. Since BaseRouteMap only tests
21
+ * for a match (not the captured group values), this substitution is transparent.
22
+ *
23
+ * @example
24
+ * normalizeParamNames("/docs/:cat-id/:page-num?")
25
+ * // → "/docs/:cat_id/:page_num?"
26
+ */
27
+ function normalizeParamNames(path) {
28
+ return path.replace(/:([A-Za-z][A-Za-z0-9]*(?:-[A-Za-z0-9]+)+)(\??)/g, (_m, name, optional) => {
29
+ return `:${name.replace(/-/g, "_")}${optional}`;
30
+ });
31
+ }
32
+ /**
33
+ * Shared bidirectional route map base class.
34
+ *
35
+ * All framework adapter `RouteMap` classes extend this — they add no logic of their
36
+ * own and inherit the full public API from here.
37
+ *
38
+ * **Lookup strategy:**
39
+ * - Static paths (no `:param`) → O(1) `Map` lookup
40
+ * - Dynamic paths → O(k) bucket-indexed scan using `URLPattern`, where `k` is the number
41
+ * of routes sharing the same first path segment
42
+ * - Results are cached after the first match
43
+ *
44
+ * **Pattern syntax** (`:param` / `:param?`):
45
+ * - `:param` — required segment, matches exactly one non-`/` segment
46
+ * - `:param?` — optional segment, matches zero or one non-`/` segment
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * import { BaseRouteMap } from "@xmachines/play-router";
51
+ *
52
+ * const map = new BaseRouteMap([
53
+ * { stateId: "home", path: "/" },
54
+ * { stateId: "profile", path: "/profile/:userId" },
55
+ * { stateId: "settings", path: "/settings/:section?" },
56
+ * ]);
57
+ *
58
+ * map.getStateIdByPath("/"); // "home"
59
+ * map.getStateIdByPath("/profile/123"); // "profile"
60
+ * map.getStateIdByPath("/settings"); // "settings"
61
+ * map.getStateIdByPath("/unknown"); // null
62
+ *
63
+ * map.getPathByStateId("profile"); // "/profile/:userId"
64
+ * map.getPathByStateId("missing"); // null
65
+ * ```
66
+ */
67
+ export class BaseRouteMap {
68
+ stateIdToPath;
69
+ pathToStateId;
70
+ patternBuckets;
71
+ pathMatchCache;
72
+ /**
73
+ * Build a route map from an array of state ID ↔ path mappings.
74
+ *
75
+ * Static paths (no `:param`) are indexed in an O(1) `Map`.
76
+ * Parameterized paths are compiled to `URLPattern` and grouped into first-segment
77
+ * buckets for efficient candidate selection.
78
+ *
79
+ * @param mappings - Array of `{ stateId, path }` entries. Order determines
80
+ * priority when multiple patterns could match the same path.
81
+ */
82
+ constructor(mappings) {
83
+ this.stateIdToPath = new Map();
84
+ this.pathToStateId = new Map();
85
+ this.patternBuckets = new Map();
86
+ this.pathMatchCache = new QuickLRU({ maxSize: 500 });
87
+ let patternOrder = 0;
88
+ for (const { stateId, path } of mappings) {
89
+ this.stateIdToPath.set(stateId, path);
90
+ if (path.includes(":")) {
91
+ const URLPatternCtorFn = getURLPatternCtor();
92
+ if (!URLPatternCtorFn) {
93
+ // Graceful degradation: skip parameterized route when URLPattern unavailable
94
+ continue;
95
+ }
96
+ const bucketKey = this.getIndexKey(path);
97
+ const bucket = this.patternBuckets.get(bucketKey) ?? [];
98
+ try {
99
+ bucket.push({
100
+ pattern: new URLPatternCtorFn({ pathname: normalizeParamNames(path) }),
101
+ stateId,
102
+ order: patternOrder++,
103
+ });
104
+ }
105
+ catch {
106
+ // Invalid pattern — skip entry
107
+ }
108
+ this.patternBuckets.set(bucketKey, bucket);
109
+ }
110
+ else {
111
+ this.pathToStateId.set(path, stateId);
112
+ }
113
+ }
114
+ }
115
+ /**
116
+ * Resolve a URL path to its mapped state ID.
117
+ *
118
+ * Strips query strings and hash fragments before matching. Tries an O(1) exact
119
+ * lookup first, then falls back to bucket-indexed pattern matching. Results are
120
+ * cached after the first pattern match.
121
+ *
122
+ * @param path - URL pathname, optionally including query/hash (e.g., `"/profile/123?ref=nav"`)
123
+ * @returns The mapped state ID, or `null` if no route matches
124
+ *
125
+ * @example
126
+ * ```typescript
127
+ * map.getStateIdByPath("/profile/123"); // "profile"
128
+ * map.getStateIdByPath("/unknown"); // null
129
+ * ```
130
+ */
131
+ getStateIdByPath(path) {
132
+ // Strip query string and hash fragment for matching
133
+ const cleanPath = path.split("?")[0].split("#")[0];
134
+ const exactMatch = this.pathToStateId.get(cleanPath);
135
+ if (exactMatch !== undefined)
136
+ return exactMatch;
137
+ const cachedMatch = this.pathMatchCache.get(cleanPath);
138
+ if (cachedMatch !== undefined)
139
+ return cachedMatch;
140
+ const candidates = this.getCandidates(this.getIndexKey(cleanPath));
141
+ for (const { pattern, stateId } of candidates) {
142
+ const match = pattern.exec({ pathname: cleanPath });
143
+ if (match) {
144
+ this.pathMatchCache.set(cleanPath, stateId);
145
+ return stateId;
146
+ }
147
+ }
148
+ this.pathMatchCache.set(cleanPath, null);
149
+ return null;
150
+ }
151
+ /**
152
+ * Look up the path pattern registered for a state ID.
153
+ *
154
+ * @param stateId - State machine state ID (e.g., `"profile"`, `"#settings"`)
155
+ * @returns The registered path pattern, or `null` if the state ID is unknown
156
+ *
157
+ * @example
158
+ * ```typescript
159
+ * map.getPathByStateId("profile"); // "/profile/:userId"
160
+ * map.getPathByStateId("missing"); // null
161
+ * ```
162
+ */
163
+ getPathByStateId(stateId) {
164
+ return this.stateIdToPath.get(stateId) ?? null;
165
+ }
166
+ getIndexKey(path) {
167
+ const trimmed = path.startsWith("/") ? path.slice(1) : path;
168
+ if (trimmed.length === 0)
169
+ return "/";
170
+ const segment = trimmed.split("/")[0];
171
+ if (segment === undefined || segment.startsWith(":"))
172
+ return "*";
173
+ return segment;
174
+ }
175
+ getCandidates(indexKey) {
176
+ const bucket = this.patternBuckets.get(indexKey) ?? [];
177
+ const wildcardBucket = this.patternBuckets.get("*") ?? [];
178
+ if (bucket.length === 0)
179
+ return wildcardBucket;
180
+ if (wildcardBucket.length === 0)
181
+ return bucket;
182
+ const merged = [];
183
+ let b = 0;
184
+ let w = 0;
185
+ while (b < bucket.length && w < wildcardBucket.length) {
186
+ if (bucket[b].order < wildcardBucket[w].order) {
187
+ merged.push(bucket[b]);
188
+ b += 1;
189
+ }
190
+ else {
191
+ merged.push(wildcardBucket[w]);
192
+ w += 1;
193
+ }
194
+ }
195
+ while (b < bucket.length) {
196
+ merged.push(bucket[b]);
197
+ b += 1;
198
+ }
199
+ while (w < wildcardBucket.length) {
200
+ merged.push(wildcardBucket[w]);
201
+ w += 1;
202
+ }
203
+ return merged;
204
+ }
205
+ }
206
+ //# sourceMappingURL=base-route-map.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-route-map.js","sourceRoot":"","sources":["../src/base-route-map.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"build-tree.d.ts","sourceRoot":"","sources":["../src/build-tree.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAa,SAAS,EAAE,MAAM,YAAY,CAAC;AAElE;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,GAAI,QAAQ,SAAS,EAAE,KAAG,SAgEpD,CAAC"}
1
+ {"version":3,"file":"build-tree.d.ts","sourceRoot":"","sources":["../src/build-tree.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAA4B,SAAS,EAAE,MAAM,YAAY,CAAC;AAEjF;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,GAAI,QAAQ,SAAS,EAAE,KAAG,SAiEpD,CAAC"}
@@ -18,7 +18,7 @@ export const buildRouteTree = (routes) => {
18
18
  routable: false, // Root is not a routable state
19
19
  children: [],
20
20
  parent: null,
21
- metadata: {},
21
+ metadata: "", // Synthetic non-routable root — no real route metadata
22
22
  };
23
23
  // 2. Initialize maps
24
24
  const byStateId = new Map([[root.stateId, root]]);
@@ -27,11 +27,12 @@ export const buildRouteTree = (routes) => {
27
27
  const sorted = routes.slice().sort((a, b) => a.statePath.length - b.statePath.length);
28
28
  // 4. Build tree
29
29
  for (const route of sorted) {
30
- // Find parent by walking up state path
30
+ // Find parent by walking up state path.
31
+ // Each entry in statePath is a full dotted stateId (e.g., "chain.app.section"),
32
+ // so we check each ancestor stateId directly from the end of the list.
31
33
  let parentNode = root;
32
- for (let i = route.statePath.length - 1; i >= 0; i--) {
33
- const parentPath = route.statePath.slice(0, i);
34
- const parentStateId = parentPath.join(".");
34
+ for (let i = route.statePath.length - 2; i >= 0; i--) {
35
+ const parentStateId = route.statePath[i];
35
36
  if (byStateId.has(parentStateId)) {
36
37
  parentNode = byStateId.get(parentStateId);
37
38
  break;
@@ -1 +1 @@
1
- {"version":3,"file":"build-tree.js","sourceRoot":"","sources":["../src/build-tree.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,MAAmB,EAAa,EAAE;IAChE,sBAAsB;IACtB,MAAM,IAAI,GAAc;QACvB,EAAE,EAAE,UAAU;QACd,IAAI,EAAE,GAAG;QACT,QAAQ,EAAE,GAAG;QACb,OAAO,EAAE,UAAU;QACnB,QAAQ,EAAE,KAAK,EAAE,+BAA+B;QAChD,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,IAAI;QACZ,QAAQ,EAAE,EAAE;KACZ,CAAC;IAEF,qBAAqB;IACrB,MAAM,SAAS,GAAG,IAAI,GAAG,CAAoB,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IACrE,MAAM,MAAM,GAAG,IAAI,GAAG,CAAoB,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IAEnE,uEAAuE;IACvE,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAEtF,gBAAgB;IAChB,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC5B,uCAAuC;QACvC,IAAI,UAAU,GAAG,IAAI,CAAC;QACtB,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YACtD,MAAM,UAAU,GAAG,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAC/C,MAAM,aAAa,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC3C,IAAI,SAAS,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC;gBAClC,UAAU,GAAG,SAAS,CAAC,GAAG,CAAC,aAAa,CAAE,CAAC;gBAC3C,MAAM;YACP,CAAC;QACF,CAAC;QAED,kBAAkB;QAClB,MAAM,QAAQ,GAAG,KAAK,CAAC,UAAU;YAChC,CAAC,CAAC,KAAK,CAAC,SAAS;YACjB,CAAC,CAAC,GAAG,UAAU,CAAC,QAAQ,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAEpE,cAAc;QACd,MAAM,IAAI,GAAc;YACvB,EAAE,EAAE,KAAK,CAAC,OAAO;YACjB,IAAI,EAAE,KAAK,CAAC,SAAS;YACrB,QAAQ;YACR,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,QAAQ,EAAE,EAAE;YACZ,MAAM,EAAE,UAAU;YAClB,QAAQ,EAAE,KAAK,CAAC,QAAQ;SACxB,CAAC;QAEF,2BAA2B;QAC3B,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YACnB,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;QAC9B,CAAC;QAED,iBAAiB;QACjB,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAE/B,uCAAuC;QACvC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAClC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;AACpC,CAAC,CAAC"}
1
+ {"version":3,"file":"build-tree.js","sourceRoot":"","sources":["../src/build-tree.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,MAAmB,EAAa,EAAE;IAChE,sBAAsB;IACtB,MAAM,IAAI,GAAc;QACvB,EAAE,EAAE,UAAU;QACd,IAAI,EAAE,GAAG;QACT,QAAQ,EAAE,GAAG;QACb,OAAO,EAAE,UAAU;QACnB,QAAQ,EAAE,KAAK,EAAE,+BAA+B;QAChD,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,IAAI;QACZ,QAAQ,EAAE,EAAmB,EAAE,uDAAuD;KACtF,CAAC;IAEF,qBAAqB;IACrB,MAAM,SAAS,GAAG,IAAI,GAAG,CAAoB,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IACrE,MAAM,MAAM,GAAG,IAAI,GAAG,CAAoB,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IAEnE,uEAAuE;IACvE,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAEtF,gBAAgB;IAChB,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC5B,wCAAwC;QACxC,gFAAgF;QAChF,uEAAuE;QACvE,IAAI,UAAU,GAAG,IAAI,CAAC;QACtB,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YACtD,MAAM,aAAa,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YACzC,IAAI,SAAS,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC;gBAClC,UAAU,GAAG,SAAS,CAAC,GAAG,CAAC,aAAa,CAAE,CAAC;gBAC3C,MAAM;YACP,CAAC;QACF,CAAC;QAED,kBAAkB;QAClB,MAAM,QAAQ,GAAG,KAAK,CAAC,UAAU;YAChC,CAAC,CAAC,KAAK,CAAC,SAAS;YACjB,CAAC,CAAC,GAAG,UAAU,CAAC,QAAQ,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAEpE,cAAc;QACd,MAAM,IAAI,GAAc;YACvB,EAAE,EAAE,KAAK,CAAC,OAAO;YACjB,IAAI,EAAE,KAAK,CAAC,SAAS;YACrB,QAAQ;YACR,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,QAAQ,EAAE,EAAE;YACZ,MAAM,EAAE,UAAU;YAClB,QAAQ,EAAE,KAAK,CAAC,QAAQ;SACxB,CAAC;QAEF,2BAA2B;QAC3B,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YACnB,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;QAC9B,CAAC;QAED,iBAAiB;QACjB,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAE/B,uCAAuC;QACvC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAClC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;AACpC,CAAC,CAAC"}
@@ -1 +1 @@
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,CAmFvE"}
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,4 +1,5 @@
1
- import { Signal } from "@xmachines/play-signals";
1
+ import { watchSignal } from "@xmachines/play-signals";
2
+ import { buildPlayRouteEvent } from "./router-sync.js";
2
3
  /**
3
4
  * Connect vanilla router to actor (pure browser integration).
4
5
  *
@@ -47,73 +48,62 @@ import { Signal } from "@xmachines/play-signals";
47
48
  export function connectRouter(options) {
48
49
  const { actor, router, routeMap } = options;
49
50
  const { history } = router;
50
- // Prevent circular updates
51
- let isProcessingHistoryChange = false;
52
- let isProcessingActorChange = false;
53
- let isProcessingPopstate = false;
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;
54
55
  // Subscribe to history changes (browser navigation)
55
56
  const unsubscribeHistory = history.subscribe((location) => {
56
- if (isProcessingActorChange)
57
+ if (isProcessingNavigation)
57
58
  return;
58
- const { to, params } = routeMap.resolve(location.pathname);
59
- if (to) {
60
- isProcessingHistoryChange = true;
61
- isProcessingPopstate = true;
62
- actor.send({ type: "play.route", to, params });
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);
63
67
  // Check immediately if actor redirected (always-guard)
64
68
  // XState processes events synchronously, so snapshot is already updated
65
69
  const newActorRoute = actor.currentRoute.get();
66
- if (newActorRoute && newActorRoute !== location.pathname) {
70
+ if (newActorRoute && newActorRoute !== nextRoute.pathname) {
67
71
  // Actor redirected - update URL
68
- isProcessingActorChange = true;
69
72
  history.replace(newActorRoute);
70
- isProcessingActorChange = false;
71
73
  }
72
- // Clear flags synchronously to prevent race conditions
73
- isProcessingHistoryChange = false;
74
- isProcessingPopstate = false;
74
+ // Clear flag synchronously to prevent race conditions
75
+ isProcessingNavigation = false;
75
76
  }
76
77
  });
77
78
  // Watch actor's currentRoute signal (actor-driven navigation)
78
- const watcher = new Signal.subtle.Watcher(() => {
79
- queueMicrotask(() => {
80
- if (isProcessingHistoryChange)
81
- return;
82
- // Skip if we're processing popstate - let the history subscriber handle URL updates
83
- if (isProcessingPopstate)
84
- return;
85
- for (const signal of watcher.getPending()) {
86
- signal.get();
87
- }
88
- watcher.watch(actor.currentRoute);
89
- // Sync URL from actor
90
- const route = actor.currentRoute.get();
91
- const currentPath = history.location.pathname;
92
- // Find path from route (reverse lookup)
93
- // Note: This assumes route tree has path metadata
94
- // For now, simple implementation - can enhance later
95
- if (route && route !== currentPath) {
96
- isProcessingActorChange = true;
97
- history.push(route);
98
- isProcessingActorChange = false;
99
- }
100
- });
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
+ }
101
88
  });
102
- watcher.watch(actor.currentRoute);
103
89
  // Sync initial URL to actor
104
90
  // ONLY send route event if URL doesn't match actor's initial state
105
91
  const initialPath = history.location.pathname;
106
92
  const initialActorRoute = actor.currentRoute.get();
107
93
  if (initialPath !== initialActorRoute) {
108
- const { to, params } = routeMap.resolve(initialPath);
109
- if (to) {
110
- actor.send({ type: "play.route", to, params });
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);
111
101
  }
112
102
  }
113
103
  // Return cleanup
114
104
  return () => {
115
105
  unsubscribeHistory();
116
- watcher.unwatch(actor.currentRoute);
106
+ unwatchRoute();
117
107
  };
118
108
  }
119
109
  //# sourceMappingURL=connect-router.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"connect-router.js","sourceRoot":"","sources":["../src/connect-router.ts"],"names":[],"mappings":"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"}
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"}
@@ -6,16 +6,16 @@ export interface BrowserHistory {
6
6
  pathname: string;
7
7
  search: string;
8
8
  hash: string;
9
- state: any;
9
+ state: unknown;
10
10
  };
11
11
  /**
12
12
  * Push new URL to history
13
13
  */
14
- push(path: string, state?: any): void;
14
+ push(path: string, state?: unknown): void;
15
15
  /**
16
16
  * Replace current URL in history
17
17
  */
18
- replace(path: string, state?: any): void;
18
+ replace(path: string, state?: unknown): void;
19
19
  /**
20
20
  * Go back/forward
21
21
  */
@@ -32,10 +32,40 @@ export interface BrowserHistory {
32
32
  */
33
33
  createHref(path: string): string;
34
34
  /**
35
- * Cleanup
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.
36
40
  */
37
41
  destroy(): void;
38
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
+ }
39
69
  /**
40
70
  * Create browser history that wraps window.history
41
71
  *
@@ -61,8 +91,11 @@ export interface BrowserHistory {
61
91
  * unsubscribe();
62
92
  * history.destroy();
63
93
  * ```
94
+ *
95
+ * `destroy()` is idempotent and cooperates with other wrappers created for the
96
+ * same `window` instance.
64
97
  */
65
98
  export declare function createBrowserHistory(options: {
66
- window: any;
99
+ window: BrowserWindow;
67
100
  }): BrowserHistory;
68
101
  //# sourceMappingURL=create-browser-history.d.ts.map
@@ -1 +1 @@
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,GAAG,CAAC;KACX,CAAC;IAEF;;OAEG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;IAEtC;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;IAEzC;;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;;OAEG;IACH,OAAO,IAAI,IAAI,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE;IAC7C,MAAM,EAAE,GAAG,CAAC;CACZ,GAAG,cAAc,CAmFjB"}
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,3 +1,4 @@
1
+ const historyPatchStates = new WeakMap();
1
2
  /**
2
3
  * Create browser history that wraps window.history
3
4
  *
@@ -23,13 +24,15 @@
23
24
  * unsubscribe();
24
25
  * history.destroy();
25
26
  * ```
27
+ *
28
+ * `destroy()` is idempotent and cooperates with other wrappers created for the
29
+ * same `window` instance.
26
30
  */
27
31
  export function createBrowserHistory(options) {
28
32
  const win = options.window;
29
33
  const listeners = new Set();
30
- // Save originals
31
- const originalPushState = win.history.pushState.bind(win.history);
32
- const originalReplaceState = win.history.replaceState.bind(win.history);
34
+ const patchState = getOrCreatePatchState(win);
35
+ let destroyed = false;
33
36
  function getLocation() {
34
37
  return {
35
38
  pathname: win.location.pathname,
@@ -42,15 +45,7 @@ export function createBrowserHistory(options) {
42
45
  const location = getLocation();
43
46
  listeners.forEach((listener) => listener(location));
44
47
  }
45
- // Patch history methods
46
- win.history.pushState = function (state, title, url) {
47
- originalPushState(state, title, url);
48
- notify();
49
- };
50
- win.history.replaceState = function (state, title, url) {
51
- originalReplaceState(state, title, url);
52
- notify();
53
- };
48
+ patchState.wrappers.add(notify);
54
49
  // Listen to popstate
55
50
  const popstateHandler = () => notify();
56
51
  win.addEventListener("popstate", popstateHandler);
@@ -81,14 +76,45 @@ export function createBrowserHistory(options) {
81
76
  return path;
82
77
  },
83
78
  destroy() {
84
- // Restore originals
85
- win.history.pushState = originalPushState;
86
- win.history.replaceState = originalReplaceState;
87
- // Remove listener
79
+ if (destroyed) {
80
+ return;
81
+ }
82
+ destroyed = true;
83
+ patchState.wrappers.delete(notify);
84
+ patchState.refCount -= 1;
88
85
  win.removeEventListener("popstate", popstateHandler);
89
- // Clear subscribers
90
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
+ }
91
92
  },
92
93
  };
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
+ }
94
120
  //# sourceMappingURL=create-browser-history.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-browser-history.js","sourceRoot":"","sources":["../src/create-browser-history.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"create-browser-history.js","sourceRoot":"","sources":["../src/create-browser-history.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,14 @@
1
1
  import type { RouteTree } from "./types.js";
2
+ /**
3
+ * Bidirectional route resolution interface returned by `createRouteMap()`.
4
+ *
5
+ * Resolves incoming URL paths to state IDs (with `#` prefix) and extracts
6
+ * route parameters. Used by `RouterBridgeBase` to translate router navigation
7
+ * events into `play.route` actor events.
8
+ *
9
+ * @see {@link createRouteMap} for the factory function
10
+ * @see {@link BaseRouteMap} for direct state ID ↔ path lookups without params extraction
11
+ */
2
12
  export interface RouteMap {
3
13
  /**
4
14
  * Resolve a URL path to a state ID and params.
@@ -27,10 +37,20 @@ export interface RouteMap {
27
37
  * Architecture:
28
38
  * - Pure function - no side effects
29
39
  * - O(1) lookups for exact matches via Map
30
- * - O(n) fallback for pattern matching with URLPattern
40
+ * - O(k) bucket-indexed pattern matching with URLPattern, where k = routes in the
41
+ * first-segment bucket (typically << total routes)
31
42
  * - Returns formatted state IDs (with # prefix)
32
43
  * - Extracts params from matched patterns
33
44
  *
45
+ * **URLPattern requirement:** Dynamic route pattern matching requires `URLPattern`
46
+ * to be available on `globalThis`. On Node.js < 24 and older browsers, load a polyfill
47
+ * before calling this function:
48
+ * ```typescript
49
+ * import "urlpattern-polyfill"; // before importing @xmachines/play-router
50
+ * ```
51
+ * If `URLPattern` is unavailable, a `URLPatternUnavailableError` is thrown.
52
+ * Static (exact) routes are unaffected by URLPattern availability.
53
+ *
34
54
  * Usage:
35
55
  * ```typescript
36
56
  * const routeTree = extractMachineRoutes(machine);
@@ -1 +1 @@
1
- {"version":3,"file":"create-route-map.d.ts","sourceRoot":"","sources":["../src/create-route-map.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C,MAAM,WAAW,QAAQ;IACxB;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG;QACtB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAC/B,CAAC;CACF;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,QAAQ,CAyD7D"}
1
+ {"version":3,"file":"create-route-map.d.ts","sourceRoot":"","sources":["../src/create-route-map.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,YAAY,CAAC;AAiBvD;;;;;;;;;GASG;AACH,MAAM,WAAW,QAAQ;IACxB;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG;QACtB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAC/B,CAAC;CACF;AAyCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,QAAQ,CAuD7D"}