@xmachines/play-router 1.0.0-beta.8 → 1.0.0

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 (81) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +259 -335
  3. package/dist/base-route-map.d.ts +136 -0
  4. package/dist/base-route-map.d.ts.map +1 -0
  5. package/dist/base-route-map.js +182 -0
  6. package/dist/base-route-map.js.map +1 -0
  7. package/dist/build-tree.d.ts +7 -0
  8. package/dist/build-tree.d.ts.map +1 -1
  9. package/dist/build-tree.js +21 -6
  10. package/dist/build-tree.js.map +1 -1
  11. package/dist/create-route-map-from-tree.d.ts +33 -0
  12. package/dist/create-route-map-from-tree.d.ts.map +1 -0
  13. package/dist/create-route-map-from-tree.js +43 -0
  14. package/dist/create-route-map-from-tree.js.map +1 -0
  15. package/dist/create-route-map.d.ts +29 -33
  16. package/dist/create-route-map.d.ts.map +1 -1
  17. package/dist/create-route-map.js +25 -64
  18. package/dist/create-route-map.js.map +1 -1
  19. package/dist/errors.d.ts +152 -0
  20. package/dist/errors.d.ts.map +1 -0
  21. package/dist/errors.js +186 -0
  22. package/dist/errors.js.map +1 -0
  23. package/dist/extract-routes.d.ts +5 -31
  24. package/dist/extract-routes.d.ts.map +1 -1
  25. package/dist/extract-routes.js +73 -51
  26. package/dist/extract-routes.js.map +1 -1
  27. package/dist/find-route.d.ts +44 -0
  28. package/dist/find-route.d.ts.map +1 -0
  29. package/dist/find-route.js +103 -0
  30. package/dist/find-route.js.map +1 -0
  31. package/dist/index.d.ts +13 -51
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +13 -133
  34. package/dist/index.js.map +1 -1
  35. package/dist/machine-to-graph.d.ts +17 -0
  36. package/dist/machine-to-graph.d.ts.map +1 -0
  37. package/dist/machine-to-graph.js +115 -0
  38. package/dist/machine-to-graph.js.map +1 -0
  39. package/dist/query.d.ts +49 -1
  40. package/dist/query.d.ts.map +1 -1
  41. package/dist/query.js +98 -3
  42. package/dist/query.js.map +1 -1
  43. package/dist/router-bridge-base.d.ts +196 -32
  44. package/dist/router-bridge-base.d.ts.map +1 -1
  45. package/dist/router-bridge-base.js +319 -85
  46. package/dist/router-bridge-base.js.map +1 -1
  47. package/dist/router-sync.d.ts +95 -0
  48. package/dist/router-sync.d.ts.map +1 -0
  49. package/dist/router-sync.js +161 -0
  50. package/dist/router-sync.js.map +1 -0
  51. package/dist/types.d.ts +163 -22
  52. package/dist/types.d.ts.map +1 -1
  53. package/dist/url-pattern-utils.d.ts +117 -0
  54. package/dist/url-pattern-utils.d.ts.map +1 -0
  55. package/dist/url-pattern-utils.js +176 -0
  56. package/dist/url-pattern-utils.js.map +1 -0
  57. package/dist/validate-routes.d.ts +32 -14
  58. package/dist/validate-routes.d.ts.map +1 -1
  59. package/dist/validate-routes.js +28 -21
  60. package/dist/validate-routes.js.map +1 -1
  61. package/package.json +42 -14
  62. package/dist/connect-router.d.ts +0 -56
  63. package/dist/connect-router.d.ts.map +0 -1
  64. package/dist/connect-router.js +0 -119
  65. package/dist/connect-router.js.map +0 -1
  66. package/dist/crawl-machine.d.ts +0 -74
  67. package/dist/crawl-machine.d.ts.map +0 -1
  68. package/dist/crawl-machine.js +0 -95
  69. package/dist/crawl-machine.js.map +0 -1
  70. package/dist/create-browser-history.d.ts +0 -68
  71. package/dist/create-browser-history.d.ts.map +0 -1
  72. package/dist/create-browser-history.js +0 -94
  73. package/dist/create-browser-history.js.map +0 -1
  74. package/dist/create-router.d.ts +0 -73
  75. package/dist/create-router.d.ts.map +0 -1
  76. package/dist/create-router.js +0 -63
  77. package/dist/create-router.js.map +0 -1
  78. package/dist/extract-route.d.ts +0 -25
  79. package/dist/extract-route.d.ts.map +0 -1
  80. package/dist/extract-route.js +0 -63
  81. package/dist/extract-route.js.map +0 -1
@@ -0,0 +1,136 @@
1
+ /**
2
+ * RouteMap — 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.
10
+ */
11
+ /**
12
+ * A single state ID ↔ path mapping entry.
13
+ *
14
+ * Both fields are `readonly` — mappings are immutable once passed to `RouteMap`.
15
+ * Adapter packages re-export a structurally compatible `RouteMapping` type under
16
+ * their own name. This type is published from `@xmachines/play-router` as
17
+ * `RouteMapping` to avoid name collisions with those adapter-local types.
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * const mapping: RouteMapping = { stateId: "home", path: "/" };
22
+ * const paramMapping: RouteMapping = { stateId: "profile", path: "/profile/:userId" };
23
+ * const optionalMapping: RouteMapping = { stateId: "settings", path: "/settings/:section?" };
24
+ * ```
25
+ */
26
+ export interface RouteMapping {
27
+ /** State machine state ID (e.g., `"home"`, `"#profile"`) */
28
+ readonly stateId: string;
29
+ /** URL path pattern (e.g., `"/"`, `"/profile/:userId"`, `"/settings/:section?"`) */
30
+ readonly path: string;
31
+ }
32
+ /**
33
+ * Shared bidirectional route map base class.
34
+ *
35
+ * All framework adapters use this class as their route map — 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 in an LRU cache (default 500 entries,
43
+ * configurable via the `cacheSize` constructor option)
44
+ *
45
+ * **Pattern syntax** (`:param` / `:param?` / `*`):
46
+ * - `:param` — required segment, matches exactly one non-`/` segment
47
+ * - `:param?` — optional segment, matches zero or one non-`/` segment
48
+ * - `*` — wildcard, matches any number of segments (URLPattern semantics)
49
+ *
50
+ * **StateId forms:** stateIds may be registered and looked up in either
51
+ * `"#stateId"` or `"stateId"` form — `RouteMap` canonicalizes internally.
52
+ * `getStateIdByPath` returns the stateId exactly as registered;
53
+ * `getPathByStateId` accepts both forms. Registering the same stateId in both
54
+ * forms refers to one entry (the later registration wins for reverse lookup).
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * import { RouteMap } from "@xmachines/play-router";
59
+ *
60
+ * const map = new RouteMap([
61
+ * { stateId: "home", path: "/" },
62
+ * { stateId: "profile", path: "/profile/:userId" },
63
+ * { stateId: "settings", path: "/settings/:section?" },
64
+ * ]);
65
+ *
66
+ * map.getStateIdByPath("/"); // "home"
67
+ * map.getStateIdByPath("/profile/123"); // "profile"
68
+ * map.getStateIdByPath("/settings"); // "settings"
69
+ * map.getStateIdByPath("/unknown"); // null
70
+ *
71
+ * map.getPathByStateId("profile"); // "/profile/:userId"
72
+ * map.getPathByStateId("missing"); // null
73
+ * ```
74
+ */
75
+ export declare class RouteMap {
76
+ /** Keyed on the canonical (bare, `#`-stripped) stateId form. */
77
+ private stateIdToPath;
78
+ private pathToStateId;
79
+ private patternBuckets;
80
+ private pathMatchCache;
81
+ /**
82
+ * Build a route map from an array of state ID ↔ path mappings.
83
+ *
84
+ * Static paths (no `:param`) are indexed in an O(1) `Map`.
85
+ * Parameterized paths are compiled to `URLPattern` and grouped into first-segment
86
+ * buckets for efficient candidate selection.
87
+ *
88
+ * @param mappings - Array of `{ stateId, path }` entries. Order determines
89
+ * priority when multiple patterns could match the same path.
90
+ * @param options - Optional configuration.
91
+ * `options.cacheSize`: Maximum number of resolved parameterized path lookups
92
+ * to cache. Defaults to `500`. Increase for applications with many unique
93
+ * parameterized URL values (e.g. user profile pages with thousands of distinct IDs).
94
+ * After eviction the path falls back to the O(k) bucket pattern scan — correct
95
+ * but slower. Minimum effective value is `1` (QuickLRU constraint).
96
+ */
97
+ constructor(mappings: RouteMapping[], { cacheSize }?: {
98
+ cacheSize?: number;
99
+ });
100
+ /**
101
+ * Resolve a URL path to its mapped state ID.
102
+ *
103
+ * Strips query strings and hash fragments before matching. Tries an O(1) exact
104
+ * lookup first, then falls back to bucket-indexed pattern matching. Results are
105
+ * cached after the first pattern match.
106
+ *
107
+ * @param path - URL pathname, optionally including query/hash (e.g., `"/profile/123?ref=nav"`)
108
+ * @returns The mapped state ID, or `null` if no route matches
109
+ *
110
+ * @example
111
+ * ```typescript
112
+ * map.getStateIdByPath("/profile/123"); // "profile"
113
+ * map.getStateIdByPath("/unknown"); // null
114
+ * ```
115
+ */
116
+ getStateIdByPath(path: string): string | null;
117
+ /**
118
+ * Look up the path pattern registered for a state ID.
119
+ *
120
+ * Accepts the stateId in either `"#stateId"` or `"stateId"` form regardless of
121
+ * which form was used at registration — lookups are canonicalized internally,
122
+ * so consumers never need to try both forms.
123
+ *
124
+ * @param stateId - State machine state ID (e.g., `"profile"`, `"#settings"`)
125
+ * @returns The registered path pattern, or `null` if the state ID is unknown
126
+ *
127
+ * @example
128
+ * ```typescript
129
+ * map.getPathByStateId("profile"); // "/profile/:userId"
130
+ * map.getPathByStateId("#profile"); // "/profile/:userId" (same entry)
131
+ * map.getPathByStateId("missing"); // null
132
+ * ```
133
+ */
134
+ getPathByStateId(stateId: string): string | null;
135
+ }
136
+ //# sourceMappingURL=base-route-map.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-route-map.d.ts","sourceRoot":"","sources":["../src/base-route-map.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA0BH;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,YAAY;IAC5B,4DAA4D;IAC5D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,oFAAoF;IACpF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,qBAAa,QAAQ;IACpB,gEAAgE;IAChE,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,cAAc,CAGpB;IACF,OAAO,CAAC,cAAc,CAAkC;IAExD;;;;;;;;;;;;;;;OAeG;gBACS,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE,SAAe,EAAE,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO;IAmCtF;;;;;;;;;;;;;;;OAeG;IACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAuB7C;;;;;;;;;;;;;;;;OAgBG;IACH,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;CAGhD"}
@@ -0,0 +1,182 @@
1
+ /**
2
+ * RouteMap — 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.
10
+ */
11
+ import QuickLRU from "quick-lru";
12
+ import { getURLPatternCtor, getCompiledPattern, getIndexKey, getCandidates, isParameterizedPattern, } from "./url-pattern-utils.js";
13
+ import { URLPatternUnavailableError, InvalidRoutePatternError } from "./errors.js";
14
+ /**
15
+ * Canonical stateId key: the bare form with any leading `#` stripped.
16
+ *
17
+ * `RouteMap` accepts stateIds in both `"#stateId"` and `"stateId"` form on insert
18
+ * and on lookup. Internally every stateId is keyed on this canonical bare form so
19
+ * consumers never need to re-implement dual-form handling. The *registered* form
20
+ * is preserved and returned by `getStateIdByPath` so the map is transparent about
21
+ * how routes were declared.
22
+ */
23
+ function canonicalStateIdKey(stateId) {
24
+ return stateId.startsWith("#") ? stateId.slice(1) : stateId;
25
+ }
26
+ /**
27
+ * Shared bidirectional route map base class.
28
+ *
29
+ * All framework adapters use this class as their route map — they add no logic of their
30
+ * own and inherit the full public API from here.
31
+ *
32
+ * **Lookup strategy:**
33
+ * - Static paths (no `:param`) → O(1) `Map` lookup
34
+ * - Dynamic paths → O(k) bucket-indexed scan using `URLPattern`, where `k` is the number
35
+ * of routes sharing the same first path segment
36
+ * - Results are cached after the first match in an LRU cache (default 500 entries,
37
+ * configurable via the `cacheSize` constructor option)
38
+ *
39
+ * **Pattern syntax** (`:param` / `:param?` / `*`):
40
+ * - `:param` — required segment, matches exactly one non-`/` segment
41
+ * - `:param?` — optional segment, matches zero or one non-`/` segment
42
+ * - `*` — wildcard, matches any number of segments (URLPattern semantics)
43
+ *
44
+ * **StateId forms:** stateIds may be registered and looked up in either
45
+ * `"#stateId"` or `"stateId"` form — `RouteMap` canonicalizes internally.
46
+ * `getStateIdByPath` returns the stateId exactly as registered;
47
+ * `getPathByStateId` accepts both forms. Registering the same stateId in both
48
+ * forms refers to one entry (the later registration wins for reverse lookup).
49
+ *
50
+ * @example
51
+ * ```typescript
52
+ * import { RouteMap } from "@xmachines/play-router";
53
+ *
54
+ * const map = new RouteMap([
55
+ * { stateId: "home", path: "/" },
56
+ * { stateId: "profile", path: "/profile/:userId" },
57
+ * { stateId: "settings", path: "/settings/:section?" },
58
+ * ]);
59
+ *
60
+ * map.getStateIdByPath("/"); // "home"
61
+ * map.getStateIdByPath("/profile/123"); // "profile"
62
+ * map.getStateIdByPath("/settings"); // "settings"
63
+ * map.getStateIdByPath("/unknown"); // null
64
+ *
65
+ * map.getPathByStateId("profile"); // "/profile/:userId"
66
+ * map.getPathByStateId("missing"); // null
67
+ * ```
68
+ */
69
+ export class RouteMap {
70
+ /** Keyed on the canonical (bare, `#`-stripped) stateId form. */
71
+ stateIdToPath;
72
+ pathToStateId;
73
+ patternBuckets;
74
+ pathMatchCache;
75
+ /**
76
+ * Build a route map from an array of state ID ↔ path mappings.
77
+ *
78
+ * Static paths (no `:param`) are indexed in an O(1) `Map`.
79
+ * Parameterized paths are compiled to `URLPattern` and grouped into first-segment
80
+ * buckets for efficient candidate selection.
81
+ *
82
+ * @param mappings - Array of `{ stateId, path }` entries. Order determines
83
+ * priority when multiple patterns could match the same path.
84
+ * @param options - Optional configuration.
85
+ * `options.cacheSize`: Maximum number of resolved parameterized path lookups
86
+ * to cache. Defaults to `500`. Increase for applications with many unique
87
+ * parameterized URL values (e.g. user profile pages with thousands of distinct IDs).
88
+ * After eviction the path falls back to the O(k) bucket pattern scan — correct
89
+ * but slower. Minimum effective value is `1` (QuickLRU constraint).
90
+ */
91
+ constructor(mappings, { cacheSize = 500 } = {}) {
92
+ this.stateIdToPath = new Map();
93
+ this.pathToStateId = new Map();
94
+ this.patternBuckets = new Map();
95
+ this.pathMatchCache = new QuickLRU({ maxSize: Math.max(1, cacheSize) });
96
+ let patternOrder = 0;
97
+ for (const { stateId, path } of mappings) {
98
+ // Key on the canonical (bare) form so getPathByStateId accepts both
99
+ // "#stateId" and "stateId". The registered form is kept for returns.
100
+ this.stateIdToPath.set(canonicalStateIdKey(stateId), path);
101
+ if (isParameterizedPattern(path)) {
102
+ const URLPatternCtorFn = getURLPatternCtor();
103
+ if (!URLPatternCtorFn) {
104
+ throw new URLPatternUnavailableError();
105
+ }
106
+ const bucketKey = getIndexKey(path);
107
+ const bucket = this.patternBuckets.get(bucketKey) ?? [];
108
+ try {
109
+ bucket.push({
110
+ pattern: getCompiledPattern(path, URLPatternCtorFn),
111
+ stateId,
112
+ order: patternOrder++,
113
+ });
114
+ }
115
+ catch (err) {
116
+ throw new InvalidRoutePatternError(path, { cause: err });
117
+ }
118
+ this.patternBuckets.set(bucketKey, bucket);
119
+ }
120
+ else {
121
+ this.pathToStateId.set(path, stateId);
122
+ }
123
+ }
124
+ }
125
+ /**
126
+ * Resolve a URL path to its mapped state ID.
127
+ *
128
+ * Strips query strings and hash fragments before matching. Tries an O(1) exact
129
+ * lookup first, then falls back to bucket-indexed pattern matching. Results are
130
+ * cached after the first pattern match.
131
+ *
132
+ * @param path - URL pathname, optionally including query/hash (e.g., `"/profile/123?ref=nav"`)
133
+ * @returns The mapped state ID, or `null` if no route matches
134
+ *
135
+ * @example
136
+ * ```typescript
137
+ * map.getStateIdByPath("/profile/123"); // "profile"
138
+ * map.getStateIdByPath("/unknown"); // null
139
+ * ```
140
+ */
141
+ getStateIdByPath(path) {
142
+ // Strip query string and hash fragment for matching
143
+ const cleanPath = path.split("?")[0].split("#")[0];
144
+ const exactMatch = this.pathToStateId.get(cleanPath);
145
+ if (exactMatch !== undefined)
146
+ return exactMatch;
147
+ const cachedMatch = this.pathMatchCache.get(cleanPath);
148
+ if (cachedMatch !== undefined)
149
+ return cachedMatch;
150
+ const candidates = getCandidates(this.patternBuckets, getIndexKey(cleanPath));
151
+ for (const { pattern, stateId } of candidates) {
152
+ const match = pattern.exec({ pathname: cleanPath });
153
+ if (match) {
154
+ this.pathMatchCache.set(cleanPath, stateId);
155
+ return stateId;
156
+ }
157
+ }
158
+ this.pathMatchCache.set(cleanPath, null);
159
+ return null;
160
+ }
161
+ /**
162
+ * Look up the path pattern registered for a state ID.
163
+ *
164
+ * Accepts the stateId in either `"#stateId"` or `"stateId"` form regardless of
165
+ * which form was used at registration — lookups are canonicalized internally,
166
+ * so consumers never need to try both forms.
167
+ *
168
+ * @param stateId - State machine state ID (e.g., `"profile"`, `"#settings"`)
169
+ * @returns The registered path pattern, or `null` if the state ID is unknown
170
+ *
171
+ * @example
172
+ * ```typescript
173
+ * map.getPathByStateId("profile"); // "/profile/:userId"
174
+ * map.getPathByStateId("#profile"); // "/profile/:userId" (same entry)
175
+ * map.getPathByStateId("missing"); // null
176
+ * ```
177
+ */
178
+ getPathByStateId(stateId) {
179
+ return this.stateIdToPath.get(canonicalStateIdKey(stateId)) ?? null;
180
+ }
181
+ }
182
+ //# 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":"AAAA;;;;;;;;;GASG;AAEH,OAAO,QAAQ,MAAM,WAAW,CAAC;AACjC,OAAO,EAEN,iBAAiB,EACjB,kBAAkB,EAClB,WAAW,EACX,aAAa,EACb,sBAAsB,GACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,0BAA0B,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAEnF;;;;;;;;GAQG;AACH,SAAS,mBAAmB,CAAC,OAAe;IAC3C,OAAO,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;AAC7D,CAAC;AAwBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,MAAM,OAAO,QAAQ;IACpB,gEAAgE;IACxD,aAAa,CAAsB;IACnC,aAAa,CAAsB;IACnC,cAAc,CAGpB;IACM,cAAc,CAAkC;IAExD;;;;;;;;;;;;;;;OAeG;IACH,YAAY,QAAwB,EAAE,EAAE,SAAS,GAAG,GAAG,KAA6B,EAAE;QACrF,IAAI,CAAC,aAAa,GAAG,IAAI,GAAG,EAAE,CAAC;QAC/B,IAAI,CAAC,aAAa,GAAG,IAAI,GAAG,EAAE,CAAC;QAC/B,IAAI,CAAC,cAAc,GAAG,IAAI,GAAG,EAAE,CAAC;QAChC,IAAI,CAAC,cAAc,GAAG,IAAI,QAAQ,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC;QACxE,IAAI,YAAY,GAAG,CAAC,CAAC;QAErB,KAAK,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,QAAQ,EAAE,CAAC;YAC1C,oEAAoE;YACpE,qEAAqE;YACrE,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,mBAAmB,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC;YAE3D,IAAI,sBAAsB,CAAC,IAAI,CAAC,EAAE,CAAC;gBAClC,MAAM,gBAAgB,GAAG,iBAAiB,EAAE,CAAC;gBAC7C,IAAI,CAAC,gBAAgB,EAAE,CAAC;oBACvB,MAAM,IAAI,0BAA0B,EAAE,CAAC;gBACxC,CAAC;gBACD,MAAM,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;gBACpC,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;gBACxD,IAAI,CAAC;oBACJ,MAAM,CAAC,IAAI,CAAC;wBACX,OAAO,EAAE,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,CAAC;wBACnD,OAAO;wBACP,KAAK,EAAE,YAAY,EAAE;qBACrB,CAAC,CAAC;gBACJ,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACd,MAAM,IAAI,wBAAwB,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;gBAC1D,CAAC;gBACD,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YAC5C,CAAC;iBAAM,CAAC;gBACP,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YACvC,CAAC;QACF,CAAC;IACF,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACH,gBAAgB,CAAC,IAAY;QAC5B,oDAAoD;QACpD,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAEnD,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACrD,IAAI,UAAU,KAAK,SAAS;YAAE,OAAO,UAAU,CAAC;QAEhD,MAAM,WAAW,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACvD,IAAI,WAAW,KAAK,SAAS;YAAE,OAAO,WAAW,CAAC;QAElD,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,cAAc,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC;QAC9E,KAAK,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,UAAU,EAAE,CAAC;YAC/C,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;YACpD,IAAI,KAAK,EAAE,CAAC;gBACX,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;gBAC5C,OAAO,OAAO,CAAC;YAChB,CAAC;QACF,CAAC;QAED,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QACzC,OAAO,IAAI,CAAC;IACb,CAAC;IAED;;;;;;;;;;;;;;;;OAgBG;IACH,gBAAgB,CAAC,OAAe;QAC/B,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC,IAAI,IAAI,CAAC;IACrE,CAAC;CACD"}
@@ -6,8 +6,15 @@ import type { RouteInfo, RouteTree } from "./types.js";
6
6
  * Absolute routes become top-level, relative routes nest under parents.
7
7
  * Creates bidirectional maps for state ID ↔ path lookup.
8
8
  *
9
+ * Duplicate detection runs here — after relative routes are resolved against
10
+ * their parent's full path — so two relative routes with the same raw string
11
+ * under different parents are valid, while distinct raw strings that resolve to
12
+ * the same full path (which would silently overwrite each other in `byPath`)
13
+ * are rejected.
14
+ *
9
15
  * @param routes - Flat list of RouteInfo from extraction
10
16
  * @returns RouteTree with root, byStateId map, and byPath map
17
+ * @throws {DuplicateRoutePathError} If two states resolve to the same full path
11
18
  */
12
19
  export declare const buildRouteTree: (routes: RouteInfo[]) => RouteTree;
13
20
  //# sourceMappingURL=build-tree.d.ts.map
@@ -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;AAGjF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,cAAc,GAAI,QAAQ,SAAS,EAAE,KAAG,SAwEpD,CAAC"}
@@ -1,3 +1,4 @@
1
+ import { detectDuplicateRoutes } from "./validate-routes.js";
1
2
  /**
2
3
  * Build hierarchical route tree from flat route list
3
4
  *
@@ -5,8 +6,15 @@
5
6
  * Absolute routes become top-level, relative routes nest under parents.
6
7
  * Creates bidirectional maps for state ID ↔ path lookup.
7
8
  *
9
+ * Duplicate detection runs here — after relative routes are resolved against
10
+ * their parent's full path — so two relative routes with the same raw string
11
+ * under different parents are valid, while distinct raw strings that resolve to
12
+ * the same full path (which would silently overwrite each other in `byPath`)
13
+ * are rejected.
14
+ *
8
15
  * @param routes - Flat list of RouteInfo from extraction
9
16
  * @returns RouteTree with root, byStateId map, and byPath map
17
+ * @throws {DuplicateRoutePathError} If two states resolve to the same full path
10
18
  */
11
19
  export const buildRouteTree = (routes) => {
12
20
  // 1. Create root node
@@ -18,20 +26,23 @@ export const buildRouteTree = (routes) => {
18
26
  routable: false, // Root is not a routable state
19
27
  children: [],
20
28
  parent: null,
21
- metadata: {},
29
+ metadata: "", // Synthetic non-routable root — no real route metadata
22
30
  };
23
31
  // 2. Initialize maps
24
32
  const byStateId = new Map([[root.stateId, root]]);
25
33
  const byPath = new Map([[root.fullPath, root]]);
26
34
  // 3. Sort routes by depth (shallowest first for proper parent linking)
35
+ // oxlint-disable-next-line unicorn/no-array-sort
27
36
  const sorted = routes.slice().sort((a, b) => a.statePath.length - b.statePath.length);
28
- // 4. Build tree
37
+ // 4. Build tree, collecting inserted nodes for resolved-path duplicate detection
38
+ const insertedNodes = [];
29
39
  for (const route of sorted) {
30
- // Find parent by walking up state path
40
+ // Find parent by walking up state path.
41
+ // Each entry in statePath is a full dotted stateId (e.g., "chain.app.section"),
42
+ // so we check each ancestor stateId directly from the end of the list.
31
43
  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(".");
44
+ for (let i = route.statePath.length - 2; i >= 0; i--) {
45
+ const parentStateId = route.statePath[i]; // nosemgrep: gitlab.eslint.detect-object-injection
35
46
  if (byStateId.has(parentStateId)) {
36
47
  parentNode = byStateId.get(parentStateId);
37
48
  break;
@@ -61,7 +72,11 @@ export const buildRouteTree = (routes) => {
61
72
  // Add to maps for bidirectional lookup
62
73
  byStateId.set(node.stateId, node);
63
74
  byPath.set(node.fullPath, node);
75
+ insertedNodes.push(node);
64
76
  }
77
+ // 5. Reject resolved full-path collisions — a silent byPath overwrite would
78
+ // leave one state unreachable by URL.
79
+ detectDuplicateRoutes(insertedNodes);
65
80
  return { root, byStateId, byPath };
66
81
  };
67
82
  //# sourceMappingURL=build-tree.js.map
@@ -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":"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"}
@@ -0,0 +1,33 @@
1
+ import type { RouteTree } from "./types.js";
2
+ import { RouteMap } from "./base-route-map.js";
3
+ import type { RouteMapOptions } from "./create-route-map.js";
4
+ /**
5
+ * Create a `RouteMap` from a `RouteTree` node structure.
6
+ *
7
+ * Used by framework-router adapters that pass a
8
+ * `RouteTree` produced by `extractMachineRoutes()` rather than calling
9
+ * `createRouteMap()` directly.
10
+ *
11
+ * Traverses all nodes collecting `{ stateId: node.id, path: node.fullPath }` pairs.
12
+ * `node.fullPath` is always the absolute resolved path (e.g. `"/dashboard/overview"`),
13
+ * which is what `RouteMap` needs for browser URL matching. This matches the
14
+ * behaviour of `createRouteMap(machine)`, which also uses `node.fullPath`.
15
+ *
16
+ * @param routeTree - A `RouteTree` as returned by `extractMachineRoutes()`.
17
+ * @param options - Optional configuration (e.g. `{ cacheSize }` to override the LRU cache size).
18
+ * @returns A `RouteMap` for use with any `RouterBridgeBase`-based adapter.
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * // Preferred — single call for XState machines:
23
+ * import { createRouteMap } from '@xmachines/play-router';
24
+ * const routeMap = createRouteMap(machine); // takes AnyStateMachine
25
+ *
26
+ * // Two-step form used by framework adapters that work with route trees:
27
+ * import { extractMachineRoutes, createRouteMapFromTree } from '@xmachines/play-router';
28
+ * const routeTree = extractMachineRoutes(machine);
29
+ * const routeMap = createRouteMapFromTree(routeTree); // uses node.fullPath (absolute)
30
+ * ```
31
+ */
32
+ export declare function createRouteMapFromTree(routeTree: RouteTree, options?: RouteMapOptions): RouteMap;
33
+ //# sourceMappingURL=create-route-map-from-tree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-route-map-from-tree.d.ts","sourceRoot":"","sources":["../src/create-route-map-from-tree.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,QAAQ,CAchG"}
@@ -0,0 +1,43 @@
1
+ import { RouteMap } from "./base-route-map.js";
2
+ /**
3
+ * Create a `RouteMap` from a `RouteTree` node structure.
4
+ *
5
+ * Used by framework-router adapters that pass a
6
+ * `RouteTree` produced by `extractMachineRoutes()` rather than calling
7
+ * `createRouteMap()` directly.
8
+ *
9
+ * Traverses all nodes collecting `{ stateId: node.id, path: node.fullPath }` pairs.
10
+ * `node.fullPath` is always the absolute resolved path (e.g. `"/dashboard/overview"`),
11
+ * which is what `RouteMap` needs for browser URL matching. This matches the
12
+ * behaviour of `createRouteMap(machine)`, which also uses `node.fullPath`.
13
+ *
14
+ * @param routeTree - A `RouteTree` as returned by `extractMachineRoutes()`.
15
+ * @param options - Optional configuration (e.g. `{ cacheSize }` to override the LRU cache size).
16
+ * @returns A `RouteMap` for use with any `RouterBridgeBase`-based adapter.
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * // Preferred — single call for XState machines:
21
+ * import { createRouteMap } from '@xmachines/play-router';
22
+ * const routeMap = createRouteMap(machine); // takes AnyStateMachine
23
+ *
24
+ * // Two-step form used by framework adapters that work with route trees:
25
+ * import { extractMachineRoutes, createRouteMapFromTree } from '@xmachines/play-router';
26
+ * const routeTree = extractMachineRoutes(machine);
27
+ * const routeMap = createRouteMapFromTree(routeTree); // uses node.fullPath (absolute)
28
+ * ```
29
+ */
30
+ export function createRouteMapFromTree(routeTree, options) {
31
+ const routes = [];
32
+ function traverse(node) {
33
+ if (node.id && node.fullPath) {
34
+ routes.push({ stateId: node.id, path: node.fullPath });
35
+ }
36
+ if (node.children) {
37
+ node.children.forEach(traverse);
38
+ }
39
+ }
40
+ traverse(routeTree.root);
41
+ return new RouteMap(routes, options);
42
+ }
43
+ //# sourceMappingURL=create-route-map-from-tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-route-map-from-tree.js","sourceRoot":"","sources":["../src/create-route-map-from-tree.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAG/C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,UAAU,sBAAsB,CAAC,SAAoB,EAAE,OAAyB;IACrF,MAAM,MAAM,GAA6C,EAAE,CAAC;IAE5D,SAAS,QAAQ,CAAC,IAAe;QAChC,IAAI,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC9B,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QACxD,CAAC;QACD,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACjC,CAAC;IACF,CAAC;IAED,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACzB,OAAO,IAAI,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACtC,CAAC"}
@@ -1,46 +1,42 @@
1
- import type { RouteTree } from "./types.js";
2
- export interface RouteMap {
1
+ import type { AnyStateMachine } from "xstate";
2
+ import { RouteMap } from "./base-route-map.js";
3
+ /**
4
+ * Options for `createRouteMap` and `createRouteMapFromTree`.
5
+ */
6
+ export interface RouteMapOptions {
3
7
  /**
4
- * Resolve a URL path to a state ID and params.
8
+ * Maximum number of resolved parameterized path lookups to cache.
5
9
  *
6
- * @param path - URL pathname (e.g., "/settings/account")
7
- * @returns Object with `to` (state ID with # prefix) and `params`
10
+ * `RouteMap.getStateIdByPath()` resolves parameterized patterns (e.g.
11
+ * `/profile/:userId`) via URLPattern on every call. Frequently visited
12
+ * paths are cached in an LRU so subsequent lookups are O(1).
8
13
  *
9
- * Example:
10
- * ```typescript
11
- * const { to, params } = routeMap.resolve('/settings/account');
12
- * // { to: "#settings", params: { section: "account" } }
13
- *
14
- * if (to) {
15
- * actor.send({ type: 'play.route', to, params });
16
- * }
17
- * ```
14
+ * Increase this value for applications with large parameterized route sets
15
+ * or high navigation frequency. Default: `500`.
18
16
  */
19
- resolve(path: string): {
20
- to: string | null;
21
- params: Record<string, string>;
22
- };
17
+ cacheSize?: number;
23
18
  }
24
19
  /**
25
- * Create a RouteMap from a RouteTree for efficient path lookups.
20
+ * Create a `RouteMap` from an XState state machine.
21
+ *
22
+ * Extracts all routable states (those with `meta.route`) and builds a bidirectional
23
+ * path ↔ stateId lookup structure. The returned map is used by `RouterBridgeBase`
24
+ * subclasses to translate browser URL changes into `play.route` actor events and
25
+ * vice-versa.
26
26
  *
27
- * Architecture:
28
- * - Pure function - no side effects
29
- * - O(1) lookups for exact matches via Map
30
- * - O(n) fallback for pattern matching with URLPattern
31
- * - Returns formatted state IDs (with # prefix)
32
- * - Extracts params from matched patterns
27
+ * @param machine - XState v5 state machine with `meta.route` annotations on states.
28
+ * @param options - Optional configuration. Pass `{ cacheSize }` to override the
29
+ * default LRU cache size for parameterized path lookups.
30
+ * @returns A `RouteMap` for passing to any `RouterBridgeBase`-based adapter.
33
31
  *
34
- * Usage:
32
+ * @example
35
33
  * ```typescript
36
- * const routeTree = extractMachineRoutes(machine);
37
- * const routeMap = createRouteMap(routeTree);
34
+ * import { createRouteMap } from '@xmachines/play-router';
35
+ * import { connectRouter } from '@xmachines/play-dom-router';
38
36
  *
39
- * const { to, params } = routeMap.resolve('/settings/account');
40
- * if (to) {
41
- * actor.send({ type: 'play.route', to, params });
42
- * }
37
+ * const routeMap = createRouteMap(myMachine);
38
+ * const disconnect = connectRouter({ actor, router, routeMap });
43
39
  * ```
44
40
  */
45
- export declare function createRouteMap(routeTree: RouteTree): RouteMap;
41
+ export declare function createRouteMap(machine: AnyStateMachine, options?: RouteMapOptions): RouteMap;
46
42
  //# sourceMappingURL=create-route-map.d.ts.map
@@ -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,eAAe,EAAE,MAAM,QAAQ,CAAC;AAG9C,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B;;;;;;;;;OASG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,QAAQ,CAU5F"}