@xmachines/play-router 3.0.0 → 5.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 (85) hide show
  1. package/README.md +111 -116
  2. package/dist/base-route-map.d.ts +24 -13
  3. package/dist/base-route-map.d.ts.map +1 -1
  4. package/dist/base-route-map.js +116 -27
  5. package/dist/base-route-map.js.map +1 -1
  6. package/dist/build-tree.d.ts +16 -1
  7. package/dist/build-tree.d.ts.map +1 -1
  8. package/dist/build-tree.js +22 -4
  9. package/dist/build-tree.js.map +1 -1
  10. package/dist/create-route-map-from-tree.d.ts +13 -7
  11. package/dist/create-route-map-from-tree.d.ts.map +1 -1
  12. package/dist/create-route-map-from-tree.js +14 -6
  13. package/dist/create-route-map-from-tree.js.map +1 -1
  14. package/dist/errors.d.ts +2 -134
  15. package/dist/errors.d.ts.map +1 -1
  16. package/dist/errors.js +11 -151
  17. package/dist/errors.js.map +1 -1
  18. package/dist/find-route.d.ts +13 -1
  19. package/dist/find-route.d.ts.map +1 -1
  20. package/dist/find-route.js +33 -26
  21. package/dist/find-route.js.map +1 -1
  22. package/dist/index.d.ts +8 -18
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +6 -12
  25. package/dist/index.js.map +1 -1
  26. package/dist/provider-lifecycle.d.ts +18 -6
  27. package/dist/provider-lifecycle.d.ts.map +1 -1
  28. package/dist/provider-lifecycle.js +10 -5
  29. package/dist/provider-lifecycle.js.map +1 -1
  30. package/dist/router-bridge-base.d.ts +73 -38
  31. package/dist/router-bridge-base.d.ts.map +1 -1
  32. package/dist/router-bridge-base.js +304 -124
  33. package/dist/router-bridge-base.js.map +1 -1
  34. package/dist/router-sync.d.ts +3 -27
  35. package/dist/router-sync.d.ts.map +1 -1
  36. package/dist/router-sync.js +27 -45
  37. package/dist/router-sync.js.map +1 -1
  38. package/dist/types.d.ts +206 -102
  39. package/dist/types.d.ts.map +1 -1
  40. package/dist/xstate/create-route-map.d.ts +30 -0
  41. package/dist/xstate/create-route-map.d.ts.map +1 -0
  42. package/dist/{create-route-map.js → xstate/create-route-map.js} +11 -6
  43. package/dist/xstate/create-route-map.js.map +1 -0
  44. package/dist/{extract-routes.d.ts → xstate/extract-routes.d.ts} +4 -2
  45. package/dist/xstate/extract-routes.d.ts.map +1 -0
  46. package/dist/{extract-routes.js → xstate/extract-routes.js} +11 -6
  47. package/dist/xstate/extract-routes.js.map +1 -0
  48. package/dist/xstate/index.d.ts +35 -0
  49. package/dist/xstate/index.d.ts.map +1 -0
  50. package/dist/xstate/index.js +33 -0
  51. package/dist/xstate/index.js.map +1 -0
  52. package/dist/{machine-to-graph.d.ts → xstate/machine-to-graph.d.ts} +1 -1
  53. package/dist/xstate/machine-to-graph.d.ts.map +1 -0
  54. package/dist/{machine-to-graph.js → xstate/machine-to-graph.js} +2 -2
  55. package/dist/xstate/machine-to-graph.js.map +1 -0
  56. package/dist/xstate/machine-types.d.ts +42 -0
  57. package/dist/xstate/machine-types.d.ts.map +1 -0
  58. package/dist/xstate/machine-types.js +2 -0
  59. package/dist/xstate/machine-types.js.map +1 -0
  60. package/dist/{query.d.ts → xstate/query.d.ts} +6 -4
  61. package/dist/xstate/query.d.ts.map +1 -0
  62. package/dist/{query.js → xstate/query.js} +3 -2
  63. package/dist/xstate/query.js.map +1 -0
  64. package/package.json +30 -21
  65. package/dist/base-path.d.ts +0 -209
  66. package/dist/base-path.d.ts.map +0 -1
  67. package/dist/base-path.js +0 -418
  68. package/dist/base-path.js.map +0 -1
  69. package/dist/create-route-map.d.ts +0 -44
  70. package/dist/create-route-map.d.ts.map +0 -1
  71. package/dist/create-route-map.js.map +0 -1
  72. package/dist/extract-routes.d.ts.map +0 -1
  73. package/dist/extract-routes.js.map +0 -1
  74. package/dist/framework-params.d.ts +0 -144
  75. package/dist/framework-params.d.ts.map +0 -1
  76. package/dist/framework-params.js +0 -291
  77. package/dist/framework-params.js.map +0 -1
  78. package/dist/machine-to-graph.d.ts.map +0 -1
  79. package/dist/machine-to-graph.js.map +0 -1
  80. package/dist/query.d.ts.map +0 -1
  81. package/dist/query.js.map +0 -1
  82. package/dist/url-pattern-utils.d.ts +0 -93
  83. package/dist/url-pattern-utils.d.ts.map +0 -1
  84. package/dist/url-pattern-utils.js +0 -233
  85. package/dist/url-pattern-utils.js.map +0 -1
@@ -10,9 +10,8 @@
10
10
  * segment, and that number is much smaller than the number of all the routes. The
11
11
  * class matches a parameterized route with URLPattern.
12
12
  */
13
- import QuickLRU from "quick-lru";
14
- import { getURLPatternCtor, getCompiledPattern, getIndexKey, getCandidates, isParameterizedPattern, } from "./url-pattern-utils.js";
15
- import { URLPatternUnavailableError, InvalidRoutePatternError } from "./errors.js";
13
+ import { createPatternCache, getCandidates, getCompiledPattern, getIndexKey, parsePattern, splitLocation, } from "@xmachines/play-url";
14
+ import { InvalidRoutePatternError } from "./errors.js";
16
15
  /**
17
16
  * The canonical key of a stateId: the bare form, without a `#` at its start.
18
17
  *
@@ -25,6 +24,65 @@ import { URLPatternUnavailableError, InvalidRoutePatternError } from "./errors.j
25
24
  function canonicalStateIdKey(stateId) {
26
25
  return stateId.startsWith("#") ? stateId.slice(1) : stateId;
27
26
  }
27
+ /**
28
+ * The bound of the path cache that a map builds for itself.
29
+ *
30
+ * `createPatternCache` keeps two generations, so the cache holds between this number and
31
+ * twice it. A caller that wants another bound passes a cache: `createPatternCache(5000)`
32
+ * builds one. A separate size option would configure THIS cache alone and say so nowhere,
33
+ * and it would do nothing at all beside a cache that the caller passed.
34
+ */
35
+ const DEFAULT_PATH_CACHE_SIZE = 500;
36
+ /**
37
+ * The id of one route table: a digest of its mappings, in order.
38
+ *
39
+ * It is the key space that each ROUTE TABLE owns inside a shared path cache. An entry of
40
+ * that cache says which state a path resolves to, and the answer is a pure function of
41
+ * the mappings and the path: two maps built from the SAME mappings resolve every path the
42
+ * same way, and two maps built from different mappings do not. A map with no route for
43
+ * `/settings/x` writes `null`, and a map that DOES hold `/settings/:s` then read that
44
+ * `null` and reported no state for a route of its own.
45
+ *
46
+ * The key therefore names the TABLE, and not the instance. An instance counter kept the
47
+ * two apart as well, and it also gave a server that rebuilds its map for each request a
48
+ * cache that answered nothing: every request opened a key space of its own, and the dead
49
+ * keys of the last one evicted the live entries of this one.
50
+ *
51
+ * Two maps built from the same mappings share the id, which is what a server that
52
+ * rebuilds its map for each request needs, and two maps built from different mappings do
53
+ * not — a map with no route for a path writes `null` for it, and a map that HOLDS one
54
+ * must not read that.
55
+ *
56
+ * A REGISTER of the serialized tables answered this before, and it kept every table
57
+ * string it had seen for the life of the process: a per-tenant table of fifty routes is a
58
+ * few kilobytes, and a bounded register of them still retained megabytes in exactly the
59
+ * per-request case that the cache exists for. A digest retains nothing.
60
+ *
61
+ * ## The collision
62
+ *
63
+ * Two DIFFERENT tables that share a digest share a key space, and the negative entries of
64
+ * one then answer for the other. The digest is 64 bits over the full signature, so a
65
+ * process that built ten thousand distinct route tables carries a chance of about 3 in
66
+ * 10^12 of one collision — far below the chance of the bit flip that would corrupt the
67
+ * cache anyway. This library already identifies by digest: an XMachines id is a Git OID.
68
+ */
69
+ function routeTableId(mappings) {
70
+ // FNV-1a, in two 32-bit halves, because a bitwise operator of JavaScript truncates to
71
+ // 32 bits and `BigInt` costs an allocation for each character.
72
+ let hashLow = 0x811c9dc5;
73
+ let hashHigh = 0x811c9dc5;
74
+ for (const { stateId, path } of mappings) {
75
+ // The ORDER is significant: it gives the priority when more than one pattern matches,
76
+ // so the separators below keep `[{a, /x}, {b, /y}]` apart from `[{a, /x/b}, {/y}]`.
77
+ const entry = `${stateId}\u0000${path}\u0001`;
78
+ for (let index = 0; index < entry.length; index += 1) {
79
+ const code = entry.charCodeAt(index);
80
+ hashLow = Math.imul(hashLow ^ code, 0x01000193) >>> 0;
81
+ hashHigh = Math.imul(hashHigh ^ (code + index), 0x01000193) >>> 0;
82
+ }
83
+ }
84
+ return `${hashLow.toString(36)}.${hashHigh.toString(36)}`;
85
+ }
28
86
  /**
29
87
  * The shared base class of the route map for both directions.
30
88
  *
@@ -35,14 +93,21 @@ function canonicalStateIdKey(stateId) {
35
93
  * - A static path, without a `:param` → a `Map` lookup in O(1)
36
94
  * - A dynamic path → a scan of the bucket index in O(k), with `URLPattern`, where
37
95
  * `k` is the number of the routes with the same first path segment
38
- * - The class keeps each result of a first match in an LRU cache. The default size
39
- * is 500 entries, and the `cacheSize` constructor option changes it
96
+ * - The class keeps each result of a first match in an LRU cache. The default cache
97
+ * holds between 500 and 1000 entries, and the `pathCache` constructor option
98
+ * replaces it
40
99
  *
41
- * **The syntax of a pattern** (`:param`, `:param?`, and `*`):
100
+ * **The syntax of a pattern:** the whole URLPattern pathname grammar. The forms used most
101
+ * often are:
42
102
  * - `:param` — a necessary segment. It matches exactly one segment without a `/`
43
103
  * - `:param?` — an optional segment. It matches zero segments or one segment without a `/`
44
104
  * - `*` — a wildcard. It matches each number of segments, as URLPattern defines
45
105
  *
106
+ * A `{…}` group, a `:param(\d+)` constraint, a `+` modifier and a `\:` escape are read
107
+ * too. The [routing guide](../../docs/guides/routing.md) states every form, the prefix
108
+ * rule, and the one divergence from the standard: a param name of this library can hold a
109
+ * hyphen.
110
+ *
46
111
  * **The forms of a stateId:** you can register a stateId, and you can look one up,
47
112
  * in the form `"#stateId"` or in the form `"stateId"`. `RouteMap` makes the
48
113
  * canonical form itself. `getStateIdByPath` returns the stateId exactly as you
@@ -75,6 +140,16 @@ export class RouteMap {
75
140
  pathToStateId;
76
141
  patternBuckets;
77
142
  pathMatchCache;
143
+ /** The key space of this ROUTE TABLE inside {@link pathMatchCache}, which a caller may share. */
144
+ pathCacheKey;
145
+ /**
146
+ * The caches that every parse and every compilation of this map reads.
147
+ *
148
+ * Each default stays SHARED on purpose. A map that built one of its own would keep its
149
+ * patterns out of the cache that `find-route.ts` and `router-sync.ts` read, so the same
150
+ * pattern would parse two times. A caller that wants the isolation passes a cache.
151
+ */
152
+ compileOptions;
78
153
  /**
79
154
  * Builds a route map from an array of the mappings between a state ID and a path.
80
155
  *
@@ -85,35 +160,44 @@ export class RouteMap {
85
160
  *
86
161
  * @param mappings - The array of the `{ stateId, path }` entries. The order gives the
87
162
  * priority when more than one pattern can match the same path.
88
- * @param options - The optional configuration.
89
- * `options.cacheSize`: the maximum number of the resolved parameterized path
90
- * lookups in the cache. The default is `500`. Raise it for an application with
91
- * many different values in a parameterized URL, for example a page of a user
92
- * profile with thousands of different IDs. After an eviction, the path goes to the
93
- * bucket pattern scan in O(k) again, which is correct but slower. The smallest
94
- * effective value is `1`, because QuickLRU requires it.
163
+ * @param options - See {@link RouteMapOptions}. A map holds TWO caches, so no option is
164
+ * named `cache`: each one names the cache that it carries.
95
165
  */
96
- constructor(mappings, { cacheSize = 500 } = {}) {
166
+ constructor(mappings, options = {}) {
167
+ const { pathCache, ...compileOptions } = options;
168
+ this.compileOptions = compileOptions;
97
169
  this.stateIdToPath = new Map();
98
170
  this.pathToStateId = new Map();
99
171
  this.patternBuckets = new Map();
100
- this.pathMatchCache = new QuickLRU({ maxSize: Math.max(1, cacheSize) });
172
+ this.pathMatchCache =
173
+ pathCache ?? createPatternCache(DEFAULT_PATH_CACHE_SIZE);
174
+ // A map that BUILDS its own cache owns every key in it, and a prefix would only make
175
+ // each key longer. A map that RECEIVES one shares it, so its keys carry the prefix.
176
+ if (pathCache === undefined) {
177
+ this.pathCacheKey = (path) => path;
178
+ }
179
+ else {
180
+ const prefix = `${routeTableId(mappings)}\u0002`;
181
+ this.pathCacheKey = (path) => prefix + path;
182
+ }
101
183
  let patternOrder = 0;
102
184
  for (const { stateId, path } of mappings) {
103
185
  // The key is the canonical bare form. Therefore getPathByStateId accepts
104
186
  // "#stateId" and also "stateId". The map keeps the registered form for its return
105
187
  // values.
106
188
  this.stateIdToPath.set(canonicalStateIdKey(stateId), path);
107
- if (isParameterizedPattern(path)) {
108
- const URLPatternCtorFn = getURLPatternCtor();
109
- if (!URLPatternCtorFn) {
110
- throw new URLPatternUnavailableError();
111
- }
112
- const bucketKey = getIndexKey(path);
189
+ // ONE parse answers both questions: is the path a pattern, and which bucket does
190
+ // it belong to. The key comes from that parse, and not from a split of the raw
191
+ // string. A pattern such as `/books{/:id}?` keeps the literal first segment
192
+ // `books`, and a split on `/` read the key `books{` and put the route in a bucket
193
+ // that the path `/books/1` reaches never.
194
+ const parsed = parsePattern(path, this.compileOptions);
195
+ if (parsed.parameterized) {
196
+ const bucketKey = parsed.indexKey;
113
197
  const bucket = this.patternBuckets.get(bucketKey) ?? [];
114
198
  try {
115
199
  bucket.push({
116
- pattern: getCompiledPattern(path, URLPatternCtorFn),
200
+ pattern: getCompiledPattern(path, this.compileOptions),
117
201
  stateId,
118
202
  order: patternOrder++,
119
203
  });
@@ -150,23 +234,28 @@ export class RouteMap {
150
234
  * ```
151
235
  */
152
236
  getStateIdByPath(path) {
153
- // Remove the query string and the hash fragment before the match
154
- const cleanPath = path.split("?")[0].split("#")[0];
237
+ // Remove the query string and the hash fragment before the match. `splitLocation` is
238
+ // the ONE answer to "where does a pathname end", and `RouterBridgeBase`,
239
+ // `framework-params.ts` and `sanitizePathname` read it too, so the four cannot
240
+ // disagree. Two splits allocated two arrays of every part on the hottest lookup of
241
+ // the library, and this function then read one element of each.
242
+ const cleanPath = splitLocation(path).pathname;
155
243
  const exactMatch = this.pathToStateId.get(cleanPath);
156
244
  if (exactMatch !== undefined)
157
245
  return exactMatch;
158
- const cachedMatch = this.pathMatchCache.get(cleanPath);
246
+ const cacheKey = this.pathCacheKey(cleanPath);
247
+ const cachedMatch = this.pathMatchCache.get(cacheKey);
159
248
  if (cachedMatch !== undefined)
160
249
  return cachedMatch;
161
250
  const candidates = getCandidates(this.patternBuckets, getIndexKey(cleanPath));
162
251
  for (const { pattern, stateId } of candidates) {
163
252
  const match = pattern.exec({ pathname: cleanPath });
164
253
  if (match) {
165
- this.pathMatchCache.set(cleanPath, stateId);
254
+ this.pathMatchCache.set(cacheKey, stateId);
166
255
  return stateId;
167
256
  }
168
257
  }
169
- this.pathMatchCache.set(cleanPath, null);
258
+ this.pathMatchCache.set(cacheKey, null);
170
259
  return null;
171
260
  }
172
261
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"base-route-map.js","sourceRoot":"","sources":["../src/base-route-map.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"base-route-map.js","sourceRoot":"","sources":["../src/base-route-map.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,15 @@
1
1
  import type { RouteInfo, RouteTree } from "./types.js";
2
+ /**
3
+ * The ID and the state ID of the synthetic root node of every `RouteTree`.
4
+ *
5
+ * `buildRouteTree` always makes this node, and no state of a machine declares it. It
6
+ * gives the tree one parent for each absolute route, and it holds `routable: false`.
7
+ *
8
+ * A consumer that walks a tree must SKIP this node. `createRouteMapFromTree` skips it,
9
+ * and `getRoutableRoutes` of `@xmachines/play-router/xstate` skips it. A walk that keeps
10
+ * it maps the path `/` to a state that the machine does not hold.
11
+ */
12
+ export declare const ROOT_NODE_ID = "__root__";
2
13
  /**
3
14
  * Builds the hierarchical route tree from the flat list of the routes
4
15
  *
@@ -13,9 +24,13 @@ import type { RouteInfo, RouteTree } from "./types.js";
13
24
  * strings that resolve to the same complete path are not: those two overwrite each
14
25
  * other in `byPath` in silence.
15
26
  *
27
+ * `TGraph` names the type of the graph that a CALLER attaches afterwards. The builder
28
+ * attaches none: it reads a list of routes and writes the maps of the tree.
29
+ * `extractMachineRoutes` of `@xmachines/play-router/xstate` binds it to `MachineGraph`.
30
+ *
16
31
  * @param routes - The flat list of the RouteInfo objects, from the extraction
17
32
  * @returns The RouteTree, with its root, its byStateId map, and its byPath map
18
33
  * @throws {DuplicateRoutePathError} When two states resolve to the same complete path
19
34
  */
20
- export declare const buildRouteTree: (routes: RouteInfo[]) => RouteTree;
35
+ export declare const buildRouteTree: <TGraph = unknown>(routes: RouteInfo[]) => RouteTree<TGraph>;
21
36
  //# 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,EAA4B,SAAS,EAAE,MAAM,YAAY,CAAC;AAGjF;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,cAAc,GAAI,QAAQ,SAAS,EAAE,KAAG,SAwEpD,CAAC"}
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;AAGlE;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,aAAa,CAAC;AAEvC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,cAAc,GAAI,MAAM,GAAG,OAAO,EAAE,QAAQ,SAAS,EAAE,KAAG,SAAS,CAAC,MAAM,CA0EtF,CAAC"}
@@ -1,4 +1,15 @@
1
1
  import { detectDuplicateRoutes } from "./validate-routes.js";
2
+ /**
3
+ * The ID and the state ID of the synthetic root node of every `RouteTree`.
4
+ *
5
+ * `buildRouteTree` always makes this node, and no state of a machine declares it. It
6
+ * gives the tree one parent for each absolute route, and it holds `routable: false`.
7
+ *
8
+ * A consumer that walks a tree must SKIP this node. `createRouteMapFromTree` skips it,
9
+ * and `getRoutableRoutes` of `@xmachines/play-router/xstate` skips it. A walk that keeps
10
+ * it maps the path `/` to a state that the machine does not hold.
11
+ */
12
+ export const ROOT_NODE_ID = "__root__";
2
13
  /**
3
14
  * Builds the hierarchical route tree from the flat list of the routes
4
15
  *
@@ -13,6 +24,10 @@ import { detectDuplicateRoutes } from "./validate-routes.js";
13
24
  * strings that resolve to the same complete path are not: those two overwrite each
14
25
  * other in `byPath` in silence.
15
26
  *
27
+ * `TGraph` names the type of the graph that a CALLER attaches afterwards. The builder
28
+ * attaches none: it reads a list of routes and writes the maps of the tree.
29
+ * `extractMachineRoutes` of `@xmachines/play-router/xstate` binds it to `MachineGraph`.
30
+ *
16
31
  * @param routes - The flat list of the RouteInfo objects, from the extraction
17
32
  * @returns The RouteTree, with its root, its byStateId map, and its byPath map
18
33
  * @throws {DuplicateRoutePathError} When two states resolve to the same complete path
@@ -20,10 +35,10 @@ import { detectDuplicateRoutes } from "./validate-routes.js";
20
35
  export const buildRouteTree = (routes) => {
21
36
  // 1. Create the root node
22
37
  const root = {
23
- id: "__root__",
38
+ id: ROOT_NODE_ID,
24
39
  path: "/",
25
40
  fullPath: "/",
26
- stateId: "__root__",
41
+ stateId: ROOT_NODE_ID,
27
42
  routable: false, // The root is a state without a route
28
43
  children: [],
29
44
  parent: null,
@@ -44,8 +59,11 @@ export const buildRouteTree = (routes) => {
44
59
  let parentNode = root;
45
60
  for (let i = route.statePath.length - 2; i >= 0; i--) {
46
61
  const parentStateId = route.statePath[i]; // nosemgrep: gitlab.eslint.detect-object-injection
47
- if (byStateId.has(parentStateId)) {
48
- parentNode = byStateId.get(parentStateId);
62
+ if (parentStateId === undefined)
63
+ continue;
64
+ const candidate = byStateId.get(parentStateId);
65
+ if (candidate !== undefined) {
66
+ parentNode = candidate;
49
67
  break;
50
68
  }
51
69
  }
@@ -1 +1 @@
1
- {"version":3,"file":"build-tree.js","sourceRoot":"","sources":["../src/build-tree.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7D;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,MAAmB,EAAa,EAAE;IAChE,0BAA0B;IAC1B,MAAM,IAAI,GAAc;QACvB,EAAE,EAAE,UAAU;QACd,IAAI,EAAE,GAAG;QACT,QAAQ,EAAE,GAAG;QACb,OAAO,EAAE,UAAU;QACnB,QAAQ,EAAE,KAAK,EAAE,sCAAsC;QACvD,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,IAAI;QACZ,QAAQ,EAAE,EAAmB,EAAE,wEAAwE;KACvG,CAAC;IAEF,yBAAyB;IACzB,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,mGAAmG;IACnG,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAElF,0FAA0F;IAC1F,MAAM,aAAa,GAAgB,EAAE,CAAC;IACtC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC5B,+CAA+C;QAC/C,oEAAoE;QACpE,gFAAgF;QAChF,4BAA4B;QAC5B,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,CAAC,mDAAmD;YAC7F,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,0BAA0B;QAC1B,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,kBAAkB;QAClB,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,uCAAuC;QACvC,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YACnB,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;QAC9B,CAAC;QAED,8BAA8B;QAC9B,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAE/B,8DAA8D;QAC9D,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAClC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAChC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,8EAA8E;IAC9E,yCAAyC;IACzC,qBAAqB,CAAC,aAAa,CAAC,CAAC;IAErC,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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"}
@@ -1,11 +1,11 @@
1
- import type { RouteTree } from "./types.js";
1
+ import type { RouteTree, RouteMapOptions } from "./types.js";
2
2
  import { RouteMap } from "./base-route-map.js";
3
- import type { RouteMapOptions } from "./create-route-map.js";
4
3
  /**
5
4
  * Creates a `RouteMap` from the node structure of a `RouteTree`.
6
5
  *
7
- * A framework router adapter uses this function when it gives a `RouteTree` from
8
- * `extractMachineRoutes()`, and not when it calls `createRouteMap()` directly.
6
+ * A framework router adapter uses this function when it holds a `RouteTree`, for example
7
+ * from `extractMachineRoutes()` of `@xmachines/play-router/xstate`, and not when it calls
8
+ * `createRouteMap()` of that package directly.
9
9
  *
10
10
  * The function walks every node, and it collects the pairs
11
11
  * `{ stateId: node.id, path: node.fullPath }`. `node.fullPath` is always the
@@ -13,18 +13,24 @@ import type { RouteMapOptions } from "./create-route-map.js";
13
13
  * that path for the match of a browser URL. `createRouteMap(machine)` behaves in the
14
14
  * same way, because it also uses `node.fullPath`.
15
15
  *
16
+ * The walk SKIPS the synthetic root node. `buildRouteTree` makes that node to hold each
17
+ * absolute route, and no state of a machine declares it. `createRouteMap` skips it as
18
+ * well, through `getRoutableRoutes`, and the two functions therefore answer the same map
19
+ * for the same tree.
20
+ *
16
21
  * @param routeTree - A `RouteTree`, as `extractMachineRoutes()` returns it.
17
- * @param options - The optional configuration, for example `{ cacheSize }` to change the size of the LRU cache.
22
+ * @param options - The optional configuration, for example `{ pathCache }` to replace the LRU cache of the resolved paths.
18
23
  * @returns A `RouteMap` for each adapter on `RouterBridgeBase`.
19
24
  *
20
25
  * @example
21
26
  * ```typescript
22
27
  * // The preferred form — one call for an XState machine:
23
- * import { createRouteMap } from '@xmachines/play-router';
28
+ * import { createRouteMap } from '@xmachines/play-router/xstate';
24
29
  * const routeMap = createRouteMap(machine); // it takes an AnyStateMachine
25
30
  *
26
31
  * // The two-step form, for a framework adapter that works with a route tree:
27
- * import { extractMachineRoutes, createRouteMapFromTree } from '@xmachines/play-router';
32
+ * import { createRouteMapFromTree } from '@xmachines/play-router';
33
+ * import { extractMachineRoutes } from '@xmachines/play-router/xstate';
28
34
  * const routeTree = extractMachineRoutes(machine);
29
35
  * const routeMap = createRouteMapFromTree(routeTree); // it uses node.fullPath, which is absolute
30
36
  * ```
@@ -1 +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"}
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,eAAe,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAG/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,QAAQ,CAchG"}
@@ -1,9 +1,11 @@
1
1
  import { RouteMap } from "./base-route-map.js";
2
+ import { ROOT_NODE_ID } from "./build-tree.js";
2
3
  /**
3
4
  * Creates a `RouteMap` from the node structure of a `RouteTree`.
4
5
  *
5
- * A framework router adapter uses this function when it gives a `RouteTree` from
6
- * `extractMachineRoutes()`, and not when it calls `createRouteMap()` directly.
6
+ * A framework router adapter uses this function when it holds a `RouteTree`, for example
7
+ * from `extractMachineRoutes()` of `@xmachines/play-router/xstate`, and not when it calls
8
+ * `createRouteMap()` of that package directly.
7
9
  *
8
10
  * The function walks every node, and it collects the pairs
9
11
  * `{ stateId: node.id, path: node.fullPath }`. `node.fullPath` is always the
@@ -11,18 +13,24 @@ import { RouteMap } from "./base-route-map.js";
11
13
  * that path for the match of a browser URL. `createRouteMap(machine)` behaves in the
12
14
  * same way, because it also uses `node.fullPath`.
13
15
  *
16
+ * The walk SKIPS the synthetic root node. `buildRouteTree` makes that node to hold each
17
+ * absolute route, and no state of a machine declares it. `createRouteMap` skips it as
18
+ * well, through `getRoutableRoutes`, and the two functions therefore answer the same map
19
+ * for the same tree.
20
+ *
14
21
  * @param routeTree - A `RouteTree`, as `extractMachineRoutes()` returns it.
15
- * @param options - The optional configuration, for example `{ cacheSize }` to change the size of the LRU cache.
22
+ * @param options - The optional configuration, for example `{ pathCache }` to replace the LRU cache of the resolved paths.
16
23
  * @returns A `RouteMap` for each adapter on `RouterBridgeBase`.
17
24
  *
18
25
  * @example
19
26
  * ```typescript
20
27
  * // The preferred form — one call for an XState machine:
21
- * import { createRouteMap } from '@xmachines/play-router';
28
+ * import { createRouteMap } from '@xmachines/play-router/xstate';
22
29
  * const routeMap = createRouteMap(machine); // it takes an AnyStateMachine
23
30
  *
24
31
  * // The two-step form, for a framework adapter that works with a route tree:
25
- * import { extractMachineRoutes, createRouteMapFromTree } from '@xmachines/play-router';
32
+ * import { createRouteMapFromTree } from '@xmachines/play-router';
33
+ * import { extractMachineRoutes } from '@xmachines/play-router/xstate';
26
34
  * const routeTree = extractMachineRoutes(machine);
27
35
  * const routeMap = createRouteMapFromTree(routeTree); // it uses node.fullPath, which is absolute
28
36
  * ```
@@ -30,7 +38,7 @@ import { RouteMap } from "./base-route-map.js";
30
38
  export function createRouteMapFromTree(routeTree, options) {
31
39
  const routes = [];
32
40
  function traverse(node) {
33
- if (node.id && node.fullPath) {
41
+ if (node.id && node.fullPath && node.id !== ROOT_NODE_ID) {
34
42
  routes.push({ stateId: node.id, path: node.fullPath });
35
43
  }
36
44
  if (node.children) {
@@ -1 +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
+ {"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;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;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,IAAI,IAAI,CAAC,EAAE,KAAK,YAAY,EAAE,CAAC;YAC1D,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"}
package/dist/errors.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { PlayError } from "@xmachines/play";
1
+ import { PlayError } from "@xmachines/play/errors";
2
+ export { InvalidRoutePatternError, InvalidBasePathError, MissingBasePathParamError, } from "@xmachines/play-url/errors";
2
3
  /**
3
4
  * The bridge throws this error when `RouterBridgeBase.syncActorFromRouter()`
4
5
  * cannot process a location change of the router and send the `play.route` event to
@@ -55,27 +56,6 @@ export declare class RouterSyncError extends PlayError {
55
56
  export declare class DuplicateBridgeError extends PlayError {
56
57
  constructor();
57
58
  }
58
- /**
59
- * The `RouteMap` constructor throws this error when the caller registers a
60
- * parameterized route, the current runtime has no `URLPattern` API, and no polyfill
61
- * is loaded.
62
- *
63
- * **The remedy:** load `urlpattern-polyfill` at the entry point of your application,
64
- * before each import of `@xmachines/play-router`:
65
- *
66
- * ```typescript
67
- * // the entry point of the application — it must run first
68
- * import "urlpattern-polyfill";
69
- * ```
70
- *
71
- * URLPattern is native in Node.js ≥ 24 and in a modern browser (Chrome 95+,
72
- * Firefox 117+, Safari 16.4+). Use the polyfill for an older target.
73
- *
74
- * **Error code:** `PLAY_ROUTE_MAP_URLPATTERN_UNAVAILABLE`
75
- */
76
- export declare class URLPatternUnavailableError extends PlayError {
77
- constructor();
78
- }
79
59
  /**
80
60
  * `validateRouteFormat()` throws this error when a state declares a `meta.route`
81
61
  * field with an empty string as its path.
@@ -122,116 +102,4 @@ export declare class UnknownStateTypeError extends PlayError {
122
102
  readonly nodeId: string;
123
103
  constructor(stateType: string, nodeId: string, validTypes: string[]);
124
104
  }
125
- /**
126
- * The library throws this error when `URLPattern` cannot compile the string of a route
127
- * pattern. The `pattern` field holds the string in question, and `cause` holds the
128
- * original error of the `URLPattern` constructor.
129
- *
130
- * Every compilation reports through this error, so `extractRouteParams` raises it for a
131
- * pattern that no `RouteMap` ever held, and not a bare `TypeError`.
132
- *
133
- * The common causes:
134
- * - A parenthesis or a bracket without its pair in the string of the pattern
135
- * - A character that a URL pathname pattern does not permit
136
- * - Two params of one pattern that land on the same URLPattern group. A name with a
137
- * hyphen compiles with an underscore, so `:cat-id` and `:cat_id` are one group. The
138
- * message names the two params and the group, and `cause` is absent: the library
139
- * refuses the pattern before URLPattern sees it.
140
- *
141
- * **Error code:** `PLAY_ROUTE_MAP_INVALID_PATTERN`
142
- *
143
- * @example
144
- * ```typescript
145
- * import { InvalidRoutePatternError } from "@xmachines/play-router/errors";
146
- *
147
- * try {
148
- * const routeMap = createRouteMap(machine);
149
- * } catch (err) {
150
- * if (err instanceof InvalidRoutePatternError) {
151
- * console.error(`Bad route pattern: "${err.pattern}"`, err.cause);
152
- * }
153
- * }
154
- * ```
155
- */
156
- export declare class InvalidRoutePatternError extends PlayError {
157
- /** The string of the route pattern. URLPattern could not compile it. */
158
- readonly pattern: string;
159
- /**
160
- * @param pattern - The route pattern that URLPattern could not compile.
161
- * @param options - The standard `cause`, and an optional `reason` that says WHY. Give
162
- * a reason whenever the library knows it: the pattern alone shows a caller nothing
163
- * when the fault is a rewrite that the library made, and not the text they wrote.
164
- */
165
- constructor(pattern: string, options?: ErrorOptions & {
166
- reason?: string;
167
- });
168
- }
169
- /**
170
- * Thrown when a `basePath` option resolves to one concrete URL prefix never.
171
- *
172
- * A bridge writes a real browser URL from its prefix, so every segment must hold
173
- * exactly one value. A wildcard segment (`*`), an optional segment (`:section?`), a
174
- * nameless `:` segment, a query string, and a hash fragment each describe a SET of
175
- * prefixes instead, or no prefix at all. A `$param` segment is the route-param
176
- * syntax of TanStack Router: a base path writes a param as `:param`, and it takes
177
- * the value of that param from `basePathParams`.
178
- *
179
- * **How to fix it:** mount on the prefix itself, and let the machine own the rest.
180
- * For the splat route `/$machineId/play/$` of TanStack, the base path is
181
- * `"/:machineId/play"` with `basePathParams: { machineId }`.
182
- *
183
- * **Error code:** `PLAY_ROUTER_INVALID_BASE_PATH`
184
- *
185
- * @example
186
- * ```typescript
187
- * import { InvalidBasePathError } from "@xmachines/play-router/errors";
188
- *
189
- * try {
190
- * bridge.setBasePath("/:machineId/play/*");
191
- * } catch (err) {
192
- * if (err instanceof InvalidBasePathError) {
193
- * console.error(`Bad base path: "${err.basePath}"`, err.message);
194
- * }
195
- * }
196
- * ```
197
- */
198
- export declare class InvalidBasePathError extends PlayError {
199
- /** The `basePath` option of the refusal, exactly as the caller gave it. */
200
- readonly basePath: string;
201
- constructor(basePath: string, reason: string);
202
- }
203
- /**
204
- * Thrown when a `:param` segment of a `basePath` has no value in `basePathParams`.
205
- *
206
- * A base path can be a pattern, so a host keeps one string that mirrors its own
207
- * route config. The bridge must still resolve that pattern to a concrete prefix,
208
- * because it writes a real browser URL, and it removes a literal prefix from every
209
- * inbound location. Every `:param` therefore needs a value.
210
- *
211
- * **How to fix it:** give the value that the host resolved already — a loader of
212
- * TanStack, or a `useParams()` call, holds it — as `basePathParams: { machineId }`.
213
- * An empty string counts as an absent value, because it would collapse the segment.
214
- *
215
- * **Error code:** `PLAY_ROUTER_MISSING_BASE_PATH_PARAM`
216
- *
217
- * @example
218
- * ```typescript
219
- * import { MissingBasePathParamError } from "@xmachines/play-router/errors";
220
- *
221
- * try {
222
- * connectRouter({ actor, router, routeMap, basePath: "/:machineId/play" });
223
- * } catch (err) {
224
- * if (err instanceof MissingBasePathParamError) {
225
- * console.error(`basePath needs a value for :${err.param}`);
226
- * }
227
- * }
228
- * ```
229
- */
230
- export declare class MissingBasePathParamError extends PlayError {
231
- /** The name of the `:param` segment without a value, and without its `:`. */
232
- readonly param: string;
233
- /** The `basePath` option that declares the param. */
234
- readonly basePath: string;
235
- constructor(param: string, basePath: string);
236
- }
237
105
  //# sourceMappingURL=errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,eAAgB,SAAQ,SAAS;gBACjC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAInD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,oBAAqB,SAAQ,SAAS;;CAUlD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,0BAA2B,SAAQ,SAAS;;CASxD;AAED;;;;;GAKG;AACH,qBAAa,mBAAoB,SAAQ,SAAS;IACjD,8CAA8C;IAC9C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEb,OAAO,EAAE,MAAM;CAS3B;AAED;;;;;GAKG;AACH,qBAAa,mBAAoB,SAAQ,SAAS;IACjD,iEAAiE;IACjE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEb,OAAO,EAAE,MAAM;CAS3B;AAED;;;;;GAKG;AACH,qBAAa,uBAAwB,SAAQ,SAAS;IACrD,sFAAsF;IACtF,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC;gBAElB,UAAU,EAAE,MAAM,EAAE;CAWhC;AAED;;;;;GAKG;AACH,qBAAa,qBAAsB,SAAQ,SAAS;IACnD,mEAAmE;IACnE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,sDAAsD;IACtD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE;CAWnE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAAa,wBAAyB,SAAQ,SAAS;IACtD,wEAAwE;IACxE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB;;;;;OAKG;gBACS,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;CAazE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,oBAAqB,SAAQ,SAAS;IAClD,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAS5C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBAAa,yBAA0B,SAAQ,SAAS;IACvD,6EAA6E;IAC7E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,qDAAqD;IACrD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAU3C"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAWnD,OAAO,EACN,wBAAwB,EACxB,oBAAoB,EACpB,yBAAyB,GACzB,MAAM,4BAA4B,CAAC;AAEpC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,eAAgB,SAAQ,SAAS;gBACjC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAInD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,oBAAqB,SAAQ,SAAS;;CAUlD;AAED;;;;;GAKG;AACH,qBAAa,mBAAoB,SAAQ,SAAS;IACjD,8CAA8C;IAC9C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEb,OAAO,EAAE,MAAM;CAS3B;AAED;;;;;GAKG;AACH,qBAAa,mBAAoB,SAAQ,SAAS;IACjD,iEAAiE;IACjE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEb,OAAO,EAAE,MAAM;CAS3B;AAED;;;;;GAKG;AACH,qBAAa,uBAAwB,SAAQ,SAAS;IACrD,sFAAsF;IACtF,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC;gBAElB,UAAU,EAAE,MAAM,EAAE;CAWhC;AAED;;;;;GAKG;AACH,qBAAa,qBAAsB,SAAQ,SAAS;IACnD,mEAAmE;IACnE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,sDAAsD;IACtD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE;CAWnE"}