@xmachines/play-router 6.0.0 → 7.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 (40) hide show
  1. package/README.md +79 -23
  2. package/dist/base-route-map.js +240 -270
  3. package/dist/base-route-map.js.map +1 -1
  4. package/dist/build-tree.js +84 -96
  5. package/dist/build-tree.js.map +1 -1
  6. package/dist/create-route-map-from-tree.js +50 -47
  7. package/dist/create-route-map-from-tree.js.map +1 -1
  8. package/dist/errors.js +124 -133
  9. package/dist/errors.js.map +1 -1
  10. package/dist/find-route.js +88 -105
  11. package/dist/find-route.js.map +1 -1
  12. package/dist/index.js +10 -28
  13. package/dist/provider-lifecycle.js +109 -143
  14. package/dist/provider-lifecycle.js.map +1 -1
  15. package/dist/router-bridge-base.d.ts +5 -5
  16. package/dist/router-bridge-base.d.ts.map +1 -1
  17. package/dist/router-bridge-base.js +1026 -1601
  18. package/dist/router-bridge-base.js.map +1 -1
  19. package/dist/router-sync.js +107 -139
  20. package/dist/router-sync.js.map +1 -1
  21. package/dist/types.d.ts +17 -17
  22. package/dist/types.d.ts.map +1 -1
  23. package/dist/validate-routes.js +58 -66
  24. package/dist/validate-routes.js.map +1 -1
  25. package/dist/xstate/create-route-map.js +36 -35
  26. package/dist/xstate/create-route-map.js.map +1 -1
  27. package/dist/xstate/extract-routes.js +66 -83
  28. package/dist/xstate/extract-routes.js.map +1 -1
  29. package/dist/xstate/index.js +5 -33
  30. package/dist/xstate/machine-to-graph.js +98 -109
  31. package/dist/xstate/machine-to-graph.js.map +1 -1
  32. package/dist/xstate/query.js +190 -212
  33. package/dist/xstate/query.js.map +1 -1
  34. package/package.json +16 -16
  35. package/dist/index.js.map +0 -1
  36. package/dist/types.js +0 -2
  37. package/dist/types.js.map +0 -1
  38. package/dist/xstate/index.js.map +0 -1
  39. package/dist/xstate/machine-types.js +0 -2
  40. package/dist/xstate/machine-types.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"router-sync.js","sourceRoot":"","sources":["../src/router-sync.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"router-sync.js","names":[],"sources":["../src/router-sync.ts"],"sourcesContent":["import {\n\tgetCompiledPattern,\n\tparsePattern,\n\tsanitizePathname,\n\ttype CompileOptions,\n\ttype URLPatternLike,\n} from \"@xmachines/play-url\";\nimport { createNullRecord } from \"@xmachines/core/utils\";\nimport type { PlayRouteEvent } from \"./types.js\";\n\nexport interface RouteMatch {\n\tto: string | null | undefined;\n\tparams?: Record<string, string>;\n}\n\nexport interface BuildPlayRouteEventOptions {\n\tpathname: string;\n\tsearch?: string | undefined;\n\tmatch: (sanitizedPathname: string) => RouteMatch;\n}\n\nexport interface RouterStateIdMap {\n\tgetStateIdByPath(path: string): string | null | undefined;\n\tgetPathByStateId(id: string): string | null | undefined;\n}\n\n/**\n * Matches a clean pathname against a route map, and reads each param of the match.\n *\n * The low-level sync path and the framework sync path share this function.\n * Therefore the semantics of the match in the route map stay identical.\n */\nexport function matchRouteMap(\n\tpathname: string,\n\trouteMap: RouterStateIdMap,\n\textractParams: (pathname: string, stateId: string) => Record<string, string>,\n): RouteMatch {\n\tconst stateId = routeMap.getStateIdByPath(pathname);\n\tif (!stateId) {\n\t\treturn { to: null, params: {} };\n\t}\n\n\treturn {\n\t\tto: stateId,\n\t\tparams: extractParams(pathname, stateId),\n\t};\n}\n\n/**\n * Reads the named path parameters of a URL, with the URLPattern API.\n *\n * The function takes the string of the pattern directly. Use it when you know the\n * pattern already. For a read through the lookup of a state ID, see\n * `RouterBridgeBase.extractParams`.\n *\n * The object of the return holds no undefined value. Such a value comes from an\n * optional segment without a match, for example `/settings` against\n * `/settings/:section?`.\n *\n * The function normalizes the pattern with the rules of the match in `RouteMap`: a\n * name with a hyphen, such as `:cat-id`, becomes `:cat_id` for URLPattern. It then\n * maps each group name back to the original param name. Therefore `/docs/123`\n * against `/docs/:cat-id` gives `{ \"cat-id\": \"123\" }`.\n *\n * The function decodes each percent sequence of a value (`john%20doe` → `john doe`),\n * as a framework router does, for example vue-router. It keeps a malformed sequence\n * raw.\n *\n * @param pathname - The concrete URL pathname, for example `/profile/alice`\n * @param pattern - The template of the URL pattern, for example `/profile/:username`\n * @returns The record of the parameter values of the read, or `{}` for a static pattern.\n * @throws {InvalidRoutePatternError} When URLPattern cannot compile the pattern.\n *\n * @example\n * ```typescript\n * import { extractRouteParams } from \"@xmachines/play-router\";\n *\n * extractRouteParams(\"/profile/alice\", \"/profile/:username\")\n * // → { username: \"alice\" }\n *\n * extractRouteParams(\"/settings\", \"/settings/:section?\")\n * // → {} — the optional param is absent, and the object holds it not\n * ```\n */\nexport function extractRouteParams(\n\tpathname: string,\n\tpattern: string,\n\toptions?: CompileOptions,\n): Record<string, string> {\n\t// ONE parse answers both questions of this function: does the pattern need a match,\n\t// and which name does each URLPattern group carry.\n\tconst parsed = parsePattern(pattern, options);\n\tif (!parsed.parameterized) return {};\n\t// Compile the pattern with the SAME normalization as the match in RouteMap.\n\t// Without it, URLPattern reads \":cat-id\" as the group \":cat\" and the literal \"-id\",\n\t// and the read of the params then contradicts the map that matched the path.\n\tconst urlPattern: URLPatternLike = getCompiledPattern(pattern, options);\n\tconst match = urlPattern.exec({ pathname });\n\tif (!match?.pathname.groups) return {};\n\t// Map each normalized group name (\":cat_id\") back to its original param name (\"cat-id\").\n\tconst nameMap = parsed.declaredNames;\n\t// A null prototype for the accumulator, exactly as `cleanFrameworkParams` uses: a plain\n\t// `{}` turns `params[\"__proto__\"] = value` into a write of the PROTOTYPE setter, which\n\t// drops the value in silence. A machine that declares `/:__proto__` then received `{}`\n\t// from this extraction while the parse of Vue Router or SolidJS Router carried the\n\t// value, so the two sources of the same params disagreed.\n\tconst params: Record<string, string> = createNullRecord<string>();\n\tfor (const [key, value] of Object.entries(match.pathname.groups)) {\n\t\tif (value !== undefined) {\n\t\t\tparams[nameMap.get(key) ?? key] = decodeParamValue(value); // nosemgrep: gitlab.eslint.detect-object-injection\n\t\t}\n\t}\n\t// Spread at the boundary: `event.params` reaches a machine of the user, and a plain\n\t// object is what it received before. A spread creates OWN properties, so a param named\n\t// \"__proto__\" stays a key while the value keeps a prototype.\n\treturn { ...params };\n}\n\n/**\n * Decodes each percent sequence of a URLPattern group value. It keeps the raw value\n * when the sequence is malformed, because `decodeURIComponent` then throws a\n * `URIError`.\n */\nfunction decodeParamValue(value: string): string {\n\ttry {\n\t\treturn decodeURIComponent(value);\n\t} catch {\n\t\treturn value;\n\t}\n}\n\n/**\n * Parses a URL search string into the plain object that a `play.route` event\n * needs.\n */\nexport function extractQuery(search: string): Record<string, string> {\n\ttry {\n\t\t// A null prototype for the accumulator, and a spread at the boundary: the same two\n\t\t// steps as `extractRouteParams` above, and for the same reason. A plain `{}` turns\n\t\t// `params[\"__proto__\"] = value` into a write of the PROTOTYPE setter, which drops\n\t\t// the value in silence — so a location of `?__proto__=nav` reached the machine with\n\t\t// an EMPTY query, while the same name in the path reached it as a param. The two\n\t\t// halves of one `play.route` event answered one name differently.\n\t\tconst params: Record<string, string> = createNullRecord<string>();\n\t\tnew URLSearchParams(search).forEach((value, key) => {\n\t\t\tparams[key] = value; // nosemgrep: gitlab.eslint.detect-object-injection\n\t\t});\n\t\treturn { ...params };\n\t} catch {\n\t\treturn {};\n\t}\n}\n\n/**\n * Builds a normalized `play.route` event from the raw input of a router or of a\n * browser.\n *\n * `connectRouter()` of `@xmachines/play-dom-router` and `RouterBridgeBase` both use\n * this helper. Therefore a low-level adapter and a framework adapter clean each\n * pathname, match each route, and read each query in the same way.\n */\nexport function buildPlayRouteEvent(\n\toptions: BuildPlayRouteEventOptions,\n): { pathname: string; event: PlayRouteEvent } | null {\n\tconst pathname = sanitizePathname(options.pathname);\n\tif (pathname === null) return null;\n\n\tconst match = options.match(pathname);\n\tif (!match.to) return null;\n\n\t// The one place of the canonical form of the emitted event: a `play.route` event\n\t// always carries a target with a '#' prefix. A route map can register a bare\n\t// stateId, and it can return one, because RouteMap keeps the registered form.\n\t// Therefore this code formats the target of the event, and no other code does\n\t// it.\n\tconst to = match.to.startsWith(\"#\") ? match.to : `#${match.to}`;\n\treturn {\n\t\tpathname,\n\t\tevent: {\n\t\t\ttype: \"play.route\",\n\t\t\tto,\n\t\t\tparams: match.params ?? {},\n\t\t\tquery: extractQuery(options.search ?? \"\"),\n\t\t},\n\t};\n}\n"],"mappings":";;;;;;;;;AAgCA,SAAgB,cACf,UACA,UACA,eACa;CACb,MAAM,UAAU,SAAS,iBAAiB,QAAQ;CAClD,IAAI,CAAC,SACJ,OAAO;EAAE,IAAI;EAAM,QAAQ,CAAC;CAAE;CAG/B,OAAO;EACN,IAAI;EACJ,QAAQ,cAAc,UAAU,OAAO;CACxC;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,SAAgB,mBACf,UACA,SACA,SACyB;CAGzB,MAAM,SAAS,aAAa,SAAS,OAAO;CAC5C,IAAI,CAAC,OAAO,eAAe,OAAO,CAAC;CAKnC,MAAM,QAD6B,mBAAmB,SAAS,OACjD,CAAA,CAAW,KAAK,EAAE,SAAS,CAAC;CAC1C,IAAI,CAAC,OAAO,SAAS,QAAQ,OAAO,CAAC;CAErC,MAAM,UAAU,OAAO;CAMvB,MAAM,SAAiC,iBAAyB;CAChE,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,SAAS,MAAM,GAC9D,IAAI,UAAU,KAAA,GACb,OAAO,QAAQ,IAAI,GAAG,KAAK,OAAO,iBAAiB,KAAK;CAM1D,OAAO,EAAE,GAAG,OAAO;AACpB;;;;;;AAOA,SAAS,iBAAiB,OAAuB;CAChD,IAAI;EACH,OAAO,mBAAmB,KAAK;CAChC,QAAQ;EACP,OAAO;CACR;AACD;;;;;AAMA,SAAgB,aAAa,QAAwC;CACpE,IAAI;EAOH,MAAM,SAAiC,iBAAyB;EAChE,IAAI,gBAAgB,MAAM,CAAC,CAAC,SAAS,OAAO,QAAQ;GACnD,OAAO,OAAO;EACf,CAAC;EACD,OAAO,EAAE,GAAG,OAAO;CACpB,QAAQ;EACP,OAAO,CAAC;CACT;AACD;;;;;;;;;AAUA,SAAgB,oBACf,SACqD;CACrD,MAAM,WAAW,iBAAiB,QAAQ,QAAQ;CAClD,IAAI,aAAa,MAAM,OAAO;CAE9B,MAAM,QAAQ,QAAQ,MAAM,QAAQ;CACpC,IAAI,CAAC,MAAM,IAAI,OAAO;CAQtB,OAAO;EACN;EACA,OAAO;GACN,MAAM;GACN,IALS,MAAM,GAAG,WAAW,GAAG,IAAI,MAAM,KAAK,IAAI,MAAM;GAMzD,QAAQ,MAAM,UAAU,CAAC;GACzB,OAAO,aAAa,QAAQ,UAAU,EAAE;EACzC;CACD;AACD"}
package/dist/types.d.ts CHANGED
@@ -45,18 +45,21 @@ export interface RouteObject {
45
45
  * two siblings therefore still runs the `entry` action of their shared parent.
46
46
  *
47
47
  * {@link RouteObject.handler} is the field that spares those intermediate ancestors:
48
- * `"local"` and `"both"` move the domain down to the parent of the state. Use the
49
- * two fields together where an `entry` action must run one time for each state that
50
- * the actor really enters.
48
+ * `"local"` and `"both"` move the domain down to the parent of the state, while the
49
+ * parent is active. Use the two fields together where an `entry` action must run one
50
+ * time for each state that the actor really enters. The target itself re-enters
51
+ * under both settings, so a route to the state that the actor already holds runs the
52
+ * `exit` and `entry` actions of that state again. The ROOT of the machine is the one
53
+ * exception: a route to the root has the root as its domain, so the flag decides.
51
54
  *
52
55
  * Set the field to `true` where the domain must run its `entry` actions again on
53
- * every route to a descendant.
56
+ * each route to this state. The flag of a state applies to the routes to that state
57
+ * alone, and not to the routes to its descendants.
54
58
  *
55
59
  * The flag does not decide the destination. The value of the machine after a route
56
60
  * is identical under both settings. A route to a state that has children still
57
- * enters the `initial` child of that state, because the generated transition targets
58
- * the state by its id, and XState enters the initial child of an id target under
59
- * both settings.
61
+ * enters the `initial` child of that state, because XState enters the initial child
62
+ * of a compound target under both settings.
60
63
  *
61
64
  * @see [Play RFC](../../docs/rfc/play.md)
62
65
  */
@@ -73,8 +76,8 @@ export interface RouteObject {
73
76
  *
74
77
  * `"local"` installs the transition on the PARENT of this state instead, and it
75
78
  * targets this state relatively. XState offers the transitions of the deepest active
76
- * state node first, so the parent answers the event itself and runs its `exit` and
77
- * its `entry` actions NOT again.
79
+ * state node first, so the parent answers the event itself. The parent then runs its
80
+ * `exit` and its `entry` actions again only where the state declares `reenter: true`.
78
81
  *
79
82
  * `"both"` installs one transition in each place. The local one answers while the
80
83
  * parent is active, and the root one answers a route that arrives from outside the
@@ -132,14 +135,11 @@ export interface RouteObject {
132
135
  * The additional metadata of the route: a title, a breadcrumb, and so on.
133
136
  *
134
137
  * CAUTION: `path`, `handler`, `reenter` and `data` are RESERVED. The routing layer
135
- * reads those four names, and a release before this one read `path` alone. A state
136
- * that carries its own `handler` value now throws an `InvalidRouteHandlerError` from
137
- * `formatPlayRouteTransitions`, so the machine builds never; a state that carries its
138
- * own `data` value now writes that value to `context.data`, where the `/context`
139
- * projection of every view reads it. A `reenter` value is read as the FLAG of the
140
- * generated transition, and the literal `true` alone turns the reentry on: the
141
- * default is `false`, and a release before this one generated every transition with
142
- * `reenter: true`. Give such a field another name.
138
+ * reads those four names. An unknown `handler` value throws an
139
+ * `InvalidRouteHandlerError` from `formatPlayRouteTransitions`, so the machine builds
140
+ * never. A `data` value goes to `context.data`, where the `/context` projection of
141
+ * every view reads it. A `reenter` value is the FLAG of the generated transition, and
142
+ * the literal `true` alone turns the reentry on. Give a field of your own another name.
143
143
  */
144
144
  [key: string]: unknown;
145
145
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -1,73 +1,65 @@
1
- import { EmptyRoutePathError, InvalidStateIdError, DuplicateRoutePathError } from "./errors.js";
1
+ import { DuplicateRoutePathError, EmptyRoutePathError, InvalidStateIdError } from "./errors.js";
2
+ //#region packages/play-router/src/validate-routes.ts
2
3
  /**
3
- * Checks the format of a route path
4
- *
5
- * The function requires a string that is not empty. It accepts an absolute path
6
- * ("/foo") and also a relative path ("foo" or "child/nested"). A relative route
7
- * receives the path prefix of its parent state during the construction of the tree.
8
- *
9
- * @param routePath - The route path to check: absolute or relative
10
- * @param stateId - The identifier of the state, for the error message
11
- * @throws {EmptyRoutePathError} When the route path is empty
12
- */
13
- export const validateRouteFormat = (routePath, stateId) => {
14
- if (!routePath) {
15
- throw new EmptyRoutePathError(stateId);
16
- }
4
+ * Checks the format of a route path
5
+ *
6
+ * The function requires a string that is not empty. It accepts an absolute path
7
+ * ("/foo") and also a relative path ("foo" or "child/nested"). A relative route
8
+ * receives the path prefix of its parent state during the construction of the tree.
9
+ *
10
+ * @param routePath - The route path to check: absolute or relative
11
+ * @param stateId - The identifier of the state, for the error message
12
+ * @throws {EmptyRoutePathError} When the route path is empty
13
+ */
14
+ var validateRouteFormat = (routePath, stateId) => {
15
+ if (!routePath) throw new EmptyRoutePathError(stateId);
17
16
  };
18
17
  /**
19
- * Checks that the set of the state IDs holds a state
20
- *
21
- * The function requires each state ID of a reference in the machine graph. This
22
- * check at the build time therefore stops a broken route reference.
23
- *
24
- * @param stateId - The identifier of the state to check
25
- * @param stateIds - The set of every known state ID of the machine graph
26
- * @throws {InvalidStateIdError} When the set does not hold the state ID
27
- */
28
- export const validateStateExists = (stateId, stateIds) => {
29
- if (!stateIds.has(stateId)) {
30
- throw new InvalidStateIdError(stateId);
31
- }
18
+ * Checks that the set of the state IDs holds a state
19
+ *
20
+ * The function requires each state ID of a reference in the machine graph. This
21
+ * check at the build time therefore stops a broken route reference.
22
+ *
23
+ * @param stateId - The identifier of the state to check
24
+ * @param stateIds - The set of every known state ID of the machine graph
25
+ * @throws {InvalidStateIdError} When the set does not hold the state ID
26
+ */
27
+ var validateStateExists = (stateId, stateIds) => {
28
+ if (!stateIds.has(stateId)) throw new InvalidStateIdError(stateId);
32
29
  };
33
30
  /**
34
- * Finds each duplicate route path
35
- *
36
- * The function THROWS AN ERROR when more than one state holds the same RESOLVED
37
- * complete path.
38
- *
39
- * The detection works on the resolved complete paths, after `buildRouteTree` put the
40
- * path of each parent before its relative routes. It does not work on the raw route
41
- * strings: two relative `"settings"` routes below two different parents resolve to
42
- * two different complete paths, and they are valid; a relative route and an absolute
43
- * route that resolve to the same URL are a real collision, and the function refuses
44
- * them. `buildRouteTree` calls this function when the complete paths are known.
45
- *
46
- * The reason: a routing on the URL requires one state for each URL. Two states at
47
- * the same path make the browser navigation ambiguous, for the BACK button and for a
48
- * direct URL. Use a different path for each state, or use one state with a
49
- * conditional render.
50
- *
51
- * @param routes - The array of the route entries with their resolved complete paths, for example the `RouteNode` objects
52
- * @throws {DuplicateRoutePathError} When the function finds two resolved route paths that are equal
53
- */
54
- export const detectDuplicateRoutes = (routes) => {
55
- const routeMap = new Map();
56
- // Build the map from each resolved complete path to its state IDs
57
- for (const route of routes) {
58
- const stateIds = routeMap.get(route.fullPath) || [];
59
- stateIds.push(route.stateId);
60
- routeMap.set(route.fullPath, stateIds);
61
- }
62
- // Find each duplicate, and throw an error
63
- const duplicates = [];
64
- for (const [routePath, stateIds] of routeMap.entries()) {
65
- if (stateIds.length > 1) {
66
- duplicates.push(` ${routePath}: [${stateIds.join(", ")}]`);
67
- }
68
- }
69
- if (duplicates.length > 0) {
70
- throw new DuplicateRoutePathError(duplicates);
71
- }
31
+ * Finds each duplicate route path
32
+ *
33
+ * The function THROWS AN ERROR when more than one state holds the same RESOLVED
34
+ * complete path.
35
+ *
36
+ * The detection works on the resolved complete paths, after `buildRouteTree` put the
37
+ * path of each parent before its relative routes. It does not work on the raw route
38
+ * strings: two relative `"settings"` routes below two different parents resolve to
39
+ * two different complete paths, and they are valid; a relative route and an absolute
40
+ * route that resolve to the same URL are a real collision, and the function refuses
41
+ * them. `buildRouteTree` calls this function when the complete paths are known.
42
+ *
43
+ * The reason: a routing on the URL requires one state for each URL. Two states at
44
+ * the same path make the browser navigation ambiguous, for the BACK button and for a
45
+ * direct URL. Use a different path for each state, or use one state with a
46
+ * conditional render.
47
+ *
48
+ * @param routes - The array of the route entries with their resolved complete paths, for example the `RouteNode` objects
49
+ * @throws {DuplicateRoutePathError} When the function finds two resolved route paths that are equal
50
+ */
51
+ var detectDuplicateRoutes = (routes) => {
52
+ const routeMap = /* @__PURE__ */ new Map();
53
+ for (const route of routes) {
54
+ const stateIds = routeMap.get(route.fullPath) || [];
55
+ stateIds.push(route.stateId);
56
+ routeMap.set(route.fullPath, stateIds);
57
+ }
58
+ const duplicates = [];
59
+ for (const [routePath, stateIds] of routeMap.entries()) if (stateIds.length > 1) duplicates.push(` ${routePath}: [${stateIds.join(", ")}]`);
60
+ if (duplicates.length > 0) throw new DuplicateRoutePathError(duplicates);
72
61
  };
62
+ //#endregion
63
+ export { detectDuplicateRoutes, validateRouteFormat, validateStateExists };
64
+
73
65
  //# sourceMappingURL=validate-routes.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"validate-routes.js","sourceRoot":"","sources":["../src/validate-routes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAehG;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,SAAiB,EAAE,OAAe,EAAQ,EAAE;IAC/E,IAAI,CAAC,SAAS,EAAE,CAAC;QAChB,MAAM,IAAI,mBAAmB,CAAC,OAAO,CAAC,CAAC;IACxC,CAAC;AACF,CAAC,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,OAAe,EAAE,QAAqB,EAAQ,EAAE;IACnF,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,mBAAmB,CAAC,OAAO,CAAC,CAAC;IACxC,CAAC;AACF,CAAC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,MAA2B,EAAQ,EAAE;IAC1E,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAoB,CAAC;IAE7C,kEAAkE;IAClE,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC5B,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QACpD,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC7B,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACxC,CAAC;IAED,0CAA0C;IAC1C,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,KAAK,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC,IAAI,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC;QACxD,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzB,UAAU,CAAC,IAAI,CAAC,KAAK,SAAS,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7D,CAAC;IACF,CAAC;IAED,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,uBAAuB,CAAC,UAAU,CAAC,CAAC;IAC/C,CAAC;AACF,CAAC,CAAC"}
1
+ {"version":3,"file":"validate-routes.js","names":[],"sources":["../src/validate-routes.ts"],"sourcesContent":["import { EmptyRoutePathError, InvalidStateIdError, DuplicateRoutePathError } from \"./errors.js\";\n\n/**\n * A route entry with its RESOLVED complete path, which is the path after the tree\n * construction put the path of the parent before each relative route.\n *\n * `RouteNode` satisfies this shape structurally.\n */\nexport interface ResolvedRoutePath {\n\t/** The identifier of the state that owns the route, for example `\"app.dashboard.settings\"`. */\n\tstateId: string;\n\t/** The resolved absolute route path, for example `\"/dashboard/settings\"`. */\n\tfullPath: string;\n}\n\n/**\n * Checks the format of a route path\n *\n * The function requires a string that is not empty. It accepts an absolute path\n * (\"/foo\") and also a relative path (\"foo\" or \"child/nested\"). A relative route\n * receives the path prefix of its parent state during the construction of the tree.\n *\n * @param routePath - The route path to check: absolute or relative\n * @param stateId - The identifier of the state, for the error message\n * @throws {EmptyRoutePathError} When the route path is empty\n */\nexport const validateRouteFormat = (routePath: string, stateId: string): void => {\n\tif (!routePath) {\n\t\tthrow new EmptyRoutePathError(stateId);\n\t}\n};\n\n/**\n * Checks that the set of the state IDs holds a state\n *\n * The function requires each state ID of a reference in the machine graph. This\n * check at the build time therefore stops a broken route reference.\n *\n * @param stateId - The identifier of the state to check\n * @param stateIds - The set of every known state ID of the machine graph\n * @throws {InvalidStateIdError} When the set does not hold the state ID\n */\nexport const validateStateExists = (stateId: string, stateIds: Set<string>): void => {\n\tif (!stateIds.has(stateId)) {\n\t\tthrow new InvalidStateIdError(stateId);\n\t}\n};\n\n/**\n * Finds each duplicate route path\n *\n * The function THROWS AN ERROR when more than one state holds the same RESOLVED\n * complete path.\n *\n * The detection works on the resolved complete paths, after `buildRouteTree` put the\n * path of each parent before its relative routes. It does not work on the raw route\n * strings: two relative `\"settings\"` routes below two different parents resolve to\n * two different complete paths, and they are valid; a relative route and an absolute\n * route that resolve to the same URL are a real collision, and the function refuses\n * them. `buildRouteTree` calls this function when the complete paths are known.\n *\n * The reason: a routing on the URL requires one state for each URL. Two states at\n * the same path make the browser navigation ambiguous, for the BACK button and for a\n * direct URL. Use a different path for each state, or use one state with a\n * conditional render.\n *\n * @param routes - The array of the route entries with their resolved complete paths, for example the `RouteNode` objects\n * @throws {DuplicateRoutePathError} When the function finds two resolved route paths that are equal\n */\nexport const detectDuplicateRoutes = (routes: ResolvedRoutePath[]): void => {\n\tconst routeMap = new Map<string, string[]>();\n\n\t// Build the map from each resolved complete path to its state IDs\n\tfor (const route of routes) {\n\t\tconst stateIds = routeMap.get(route.fullPath) || [];\n\t\tstateIds.push(route.stateId);\n\t\trouteMap.set(route.fullPath, stateIds);\n\t}\n\n\t// Find each duplicate, and throw an error\n\tconst duplicates: string[] = [];\n\tfor (const [routePath, stateIds] of routeMap.entries()) {\n\t\tif (stateIds.length > 1) {\n\t\t\tduplicates.push(` ${routePath}: [${stateIds.join(\", \")}]`);\n\t\t}\n\t}\n\n\tif (duplicates.length > 0) {\n\t\tthrow new DuplicateRoutePathError(duplicates);\n\t}\n};\n"],"mappings":";;;;;;;;;;;;;AA0BA,IAAa,uBAAuB,WAAmB,YAA0B;CAChF,IAAI,CAAC,WACJ,MAAM,IAAI,oBAAoB,OAAO;AAEvC;;;;;;;;;;;AAYA,IAAa,uBAAuB,SAAiB,aAAgC;CACpF,IAAI,CAAC,SAAS,IAAI,OAAO,GACxB,MAAM,IAAI,oBAAoB,OAAO;AAEvC;;;;;;;;;;;;;;;;;;;;;;AAuBA,IAAa,yBAAyB,WAAsC;CAC3E,MAAM,2BAAW,IAAI,IAAsB;CAG3C,KAAK,MAAM,SAAS,QAAQ;EAC3B,MAAM,WAAW,SAAS,IAAI,MAAM,QAAQ,KAAK,CAAC;EAClD,SAAS,KAAK,MAAM,OAAO;EAC3B,SAAS,IAAI,MAAM,UAAU,QAAQ;CACtC;CAGA,MAAM,aAAuB,CAAC;CAC9B,KAAK,MAAM,CAAC,WAAW,aAAa,SAAS,QAAQ,GACpD,IAAI,SAAS,SAAS,GACrB,WAAW,KAAK,KAAK,UAAU,KAAK,SAAS,KAAK,IAAI,EAAE,EAAE;CAI5D,IAAI,WAAW,SAAS,GACvB,MAAM,IAAI,wBAAwB,UAAU;AAE9C"}
@@ -1,40 +1,41 @@
1
+ import { RouteMap } from "../base-route-map.js";
1
2
  import { extractMachineRoutes } from "./extract-routes.js";
2
3
  import { getRoutableRoutes } from "./query.js";
3
- import { RouteMap } from "../base-route-map.js";
4
+ //#region packages/play-router/src/xstate/create-route-map.ts
4
5
  /**
5
- * Creates a `RouteMap` from an XState state machine.
6
- *
7
- * The function reads every state with a route, which means each state with a
8
- * `meta.route` field. It keeps the routable states alone, and it drops the synthetic
9
- * root of the tree. `createRouteMapFromTree` of the main entry point drops the same
10
- * node, and the two functions therefore answer the same map for the same machine. It then builds the lookup structure between a path and a
11
- * stateId, for both directions. A subclass of `RouterBridgeBase` uses the map: it
12
- * converts each change of the browser URL into a `play.route` actor event, and each
13
- * actor route into a URL.
14
- *
15
- * @param machine - The XState v5 state machine, with a `meta.route` annotation on each state with a route.
16
- * @param options - The optional configuration. Give `{ pathCache }` to replace the
17
- * default LRU cache of the parameterized path lookups with one that you own.
18
- * @returns A `RouteMap` for each adapter on `RouterBridgeBase`.
19
- *
20
- * @example
21
- * ```typescript
22
- * import { createRouteMap } from '@xmachines/play-router/xstate';
23
- * import { connectRouter } from '@xmachines/play-dom-router';
24
- *
25
- * const routeMap = createRouteMap(myMachine);
26
- * const disconnect = connectRouter({ actor, router, routeMap });
27
- * ```
28
- */
29
- export function createRouteMap(machine, options) {
30
- // The options reach the EXTRACTION too. `extractMachineRoutes` parses the route of
31
- // every node, so a caller that passes a cache for the isolation of a tenant would
32
- // otherwise write each of those patterns into the shared default cache.
33
- const routeTree = extractMachineRoutes(machine, options);
34
- const routes = getRoutableRoutes(routeTree);
35
- return new RouteMap(routes.map((node) => ({
36
- stateId: node.stateId,
37
- path: node.fullPath,
38
- })), options);
6
+ * Creates a `RouteMap` from an XState state machine.
7
+ *
8
+ * The function reads every state with a route, which means each state with a
9
+ * `meta.route` field. It keeps the routable states alone, and it drops the synthetic
10
+ * root of the tree. `createRouteMapFromTree` of the main entry point drops the same
11
+ * node, and the two functions therefore answer the same map for the same machine. It then builds the lookup structure between a path and a
12
+ * stateId, for both directions. A subclass of `RouterBridgeBase` uses the map: it
13
+ * converts each change of the browser URL into a `play.route` actor event, and each
14
+ * actor route into a URL.
15
+ *
16
+ * @param machine - The XState v5 state machine, with a `meta.route` annotation on each state with a route.
17
+ * @param options - The optional configuration. Give `{ pathCache }` to replace the
18
+ * default LRU cache of the parameterized path lookups with one that you own.
19
+ * @returns A `RouteMap` for each adapter on `RouterBridgeBase`.
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * import { createRouteMap } from '@xmachines/play-router/xstate';
24
+ * import { connectRouter } from '@xmachines/play-dom-router';
25
+ *
26
+ * const routeMap = createRouteMap(myMachine);
27
+ * const disconnect = connectRouter({ actor, router, routeMap });
28
+ * ```
29
+ */
30
+ function createRouteMap(machine, options) {
31
+ const routeTree = extractMachineRoutes(machine, options);
32
+ const routes = getRoutableRoutes(routeTree);
33
+ return new RouteMap(routes.map((node) => ({
34
+ stateId: node.stateId,
35
+ path: node.fullPath
36
+ })), options);
39
37
  }
38
+ //#endregion
39
+ export { createRouteMap };
40
+
40
41
  //# sourceMappingURL=create-route-map.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-route-map.js","sourceRoot":"","sources":["../../src/xstate/create-route-map.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAGhD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,cAAc,CAAC,OAAwB,EAAE,OAAyB;IACjF,mFAAmF;IACnF,kFAAkF;IAClF,wEAAwE;IACxE,MAAM,SAAS,GAAG,oBAAoB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACzD,MAAM,MAAM,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC5C,OAAO,IAAI,QAAQ,CAClB,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACrB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,IAAI,EAAE,IAAI,CAAC,QAAQ;KACnB,CAAC,CAAC,EACH,OAAO,CACP,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"create-route-map.js","names":[],"sources":["../../src/xstate/create-route-map.ts"],"sourcesContent":["import type { AnyStateMachine } from \"xstate\";\nimport { extractMachineRoutes } from \"./extract-routes.js\";\nimport { getRoutableRoutes } from \"./query.js\";\nimport { RouteMap } from \"../base-route-map.js\";\nimport type { RouteMapOptions } from \"../types.js\";\n\n/**\n * Creates a `RouteMap` from an XState state machine.\n *\n * The function reads every state with a route, which means each state with a\n * `meta.route` field. It keeps the routable states alone, and it drops the synthetic\n * root of the tree. `createRouteMapFromTree` of the main entry point drops the same\n * node, and the two functions therefore answer the same map for the same machine. It then builds the lookup structure between a path and a\n * stateId, for both directions. A subclass of `RouterBridgeBase` uses the map: it\n * converts each change of the browser URL into a `play.route` actor event, and each\n * actor route into a URL.\n *\n * @param machine - The XState v5 state machine, with a `meta.route` annotation on each state with a route.\n * @param options - The optional configuration. Give `{ pathCache }` to replace the\n * default LRU cache of the parameterized path lookups with one that you own.\n * @returns A `RouteMap` for each adapter on `RouterBridgeBase`.\n *\n * @example\n * ```typescript\n * import { createRouteMap } from '@xmachines/play-router/xstate';\n * import { connectRouter } from '@xmachines/play-dom-router';\n *\n * const routeMap = createRouteMap(myMachine);\n * const disconnect = connectRouter({ actor, router, routeMap });\n * ```\n */\nexport function createRouteMap(machine: AnyStateMachine, options?: RouteMapOptions): RouteMap {\n\t// The options reach the EXTRACTION too. `extractMachineRoutes` parses the route of\n\t// every node, so a caller that passes a cache for the isolation of a tenant would\n\t// otherwise write each of those patterns into the shared default cache.\n\tconst routeTree = extractMachineRoutes(machine, options);\n\tconst routes = getRoutableRoutes(routeTree);\n\treturn new RouteMap(\n\t\troutes.map((node) => ({\n\t\t\tstateId: node.stateId,\n\t\t\tpath: node.fullPath,\n\t\t})),\n\t\toptions,\n\t);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,SAAgB,eAAe,SAA0B,SAAqC;CAI7F,MAAM,YAAY,qBAAqB,SAAS,OAAO;CACvD,MAAM,SAAS,kBAAkB,SAAS;CAC1C,OAAO,IAAI,SACV,OAAO,KAAK,UAAU;EACrB,SAAS,KAAK;EACd,MAAM,KAAK;CACZ,EAAE,GACF,OACD;AACD"}
@@ -1,92 +1,75 @@
1
- import { parsePattern } from "@xmachines/play-url";
2
- import { machineToGraph } from "./machine-to-graph.js";
3
1
  import { validateRouteFormat, validateStateExists } from "../validate-routes.js";
4
2
  import { buildRouteTree } from "../build-tree.js";
3
+ import { machineToGraph } from "./machine-to-graph.js";
4
+ import { parsePattern } from "@xmachines/play-url";
5
+ //#region packages/play-router/src/xstate/extract-routes.ts
5
6
  /**
6
- * Builds the statePath of a graph node, and it walks the chain of the parentId
7
- * values.
8
- *
9
- * The function makes an array of the stateId segments, from the root to this node,
10
- * and the array holds no root. This is the format of the parent lookup in
11
- * buildRouteTree.
12
- *
13
- * @param graphNode - The node of the path
14
- * @param graphNodeMap - The map from a node id to a GraphNode, for a parent lookup in O(1)
15
- * @returns The array of the stateId segments, for example ["dashboard", "overview"]
16
- */
17
- const buildStatePath = (graphNode, graphNodeMap) => {
18
- const path = [];
19
- let current = graphNode;
20
- while (current && current.parentId !== undefined && current.parentId !== null) {
21
- path.unshift(current.data.stateId);
22
- current = graphNodeMap.get(current.parentId);
23
- }
24
- return path;
7
+ * Builds the statePath of a graph node, and it walks the chain of the parentId
8
+ * values.
9
+ *
10
+ * The function makes an array of the stateId segments, from the root to this node,
11
+ * and the array holds no root. This is the format of the parent lookup in
12
+ * buildRouteTree.
13
+ *
14
+ * @param graphNode - The node of the path
15
+ * @param graphNodeMap - The map from a node id to a GraphNode, for a parent lookup in O(1)
16
+ * @returns The array of the stateId segments, for example ["dashboard", "overview"]
17
+ */
18
+ var buildStatePath = (graphNode, graphNodeMap) => {
19
+ const path = [];
20
+ let current = graphNode;
21
+ while (current && current.parentId !== void 0 && current.parentId !== null) {
22
+ path.unshift(current.data.stateId);
23
+ current = graphNodeMap.get(current.parentId);
24
+ }
25
+ return path;
25
26
  };
26
27
  /**
27
- * Reads the route information of the MachineNodeData object of a graph node.
28
- * The function returns null when the node has no route.
29
- */
30
- const extractRouteFromNode = (graphNode, stateIds, graphNodeMap, options) => {
31
- const { stateId, route, meta } = graphNode.data;
32
- // The node has no route
33
- if (!route)
34
- return null;
35
- // Check the format. The path must start with /
36
- validateRouteFormat(route, stateId);
37
- // Check that the graph holds the state. This test is redundant, and it guards against a corruption
38
- validateStateExists(stateId, stateIds);
39
- // Build the statePath from the chain of the parentId values. This works for an explicit ID and also for a generated one
40
- const statePath = buildStatePath(graphNode, graphNodeMap);
41
- // The parse answers this, and not a search for a ":". `route.includes(":")` was the
42
- // last expression that read the grammar on its own: it reported no pattern for
43
- // `/docs/*`, for `/books{/:id}?` and for an escaped literal, so a consumer that reads
44
- // `node.pattern` learned that a parameterized route is a literal path.
45
- const pattern = parsePattern(route, options).parameterized ? route : undefined;
46
- // Build the original shape of the metadata again, from the data of the graph
47
- const metadata = meta && typeof meta === "object" && "route" in meta
48
- ? meta["route"]
49
- : route;
50
- const result = {
51
- stateId,
52
- statePath,
53
- routePath: route,
54
- isAbsolute: route.startsWith("/"),
55
- routable: true,
56
- metadata,
57
- };
58
- if (pattern)
59
- result.pattern = pattern;
60
- return result;
28
+ * Reads the route information of the MachineNodeData object of a graph node.
29
+ * The function returns null when the node has no route.
30
+ */
31
+ var extractRouteFromNode = (graphNode, stateIds, graphNodeMap, options) => {
32
+ const { stateId, route, meta } = graphNode.data;
33
+ if (!route) return null;
34
+ validateRouteFormat(route, stateId);
35
+ validateStateExists(stateId, stateIds);
36
+ const statePath = buildStatePath(graphNode, graphNodeMap);
37
+ const pattern = parsePattern(route, options).parameterized ? route : void 0;
38
+ const metadata = meta && typeof meta === "object" && "route" in meta ? meta["route"] : route;
39
+ const result = {
40
+ stateId,
41
+ statePath,
42
+ routePath: route,
43
+ isAbsolute: route.startsWith("/"),
44
+ routable: true,
45
+ metadata
46
+ };
47
+ if (pattern) result.pattern = pattern;
48
+ return result;
61
49
  };
62
50
  /**
63
- * Reads the complete route tree from the graph of a state machine
64
- *
65
- * The function converts the XState machine into a Graph of @statelyai/graph, with
66
- * machineToGraph(). It then walks the graph nodes: it reads the route metadata of
67
- * each node, it checks each route reference, and it builds a hierarchical RouteTree.
68
- * The RouteTree of the return value also holds the graph, for a later query that
69
- * needs the transitions.
70
- *
71
- * @param machine - The XState v5 state machine
72
- * @returns The route tree, with its root, its byStateId map, its byPath map, and its graph
73
- */
74
- export const extractMachineRoutes = (machine, options) => {
75
- // 1. Convert the machine into a graph
76
- const graph = machineToGraph(machine);
77
- // 2. Build the set of the state IDs, for the check, and the node map, for a parent lookup in O(1)
78
- const stateIds = new Set(graph.nodes.map((n) => n.data.stateId));
79
- const graphNodeMap = new Map(graph.nodes.map((n) => [n.id, n]));
80
- // 3. Read the routes of the graph nodes
81
- const routes = graph.nodes
82
- .map((node) => extractRouteFromNode(node, stateIds, graphNodeMap, options))
83
- .filter((r) => r !== null);
84
- // 4. Build the hierarchical tree, and attach the graph.
85
- // buildRouteTree finds each duplicate route, AFTER it resolved each relative route
86
- // to a complete path. The raw route strings are not comparable, because two
87
- // relative "settings" routes below two different parents are two different
88
- // routes.
89
- const tree = buildRouteTree(routes);
90
- return { ...tree, graph };
51
+ * Reads the complete route tree from the graph of a state machine
52
+ *
53
+ * The function converts the XState machine into a Graph of @statelyai/graph, with
54
+ * machineToGraph(). It then walks the graph nodes: it reads the route metadata of
55
+ * each node, it checks each route reference, and it builds a hierarchical RouteTree.
56
+ * The RouteTree of the return value also holds the graph, for a later query that
57
+ * needs the transitions.
58
+ *
59
+ * @param machine - The XState v5 state machine
60
+ * @returns The route tree, with its root, its byStateId map, its byPath map, and its graph
61
+ */
62
+ var extractMachineRoutes = (machine, options) => {
63
+ const graph = machineToGraph(machine);
64
+ const stateIds = new Set(graph.nodes.map((n) => n.data.stateId));
65
+ const graphNodeMap = new Map(graph.nodes.map((n) => [n.id, n]));
66
+ const routes = graph.nodes.map((node) => extractRouteFromNode(node, stateIds, graphNodeMap, options)).filter((r) => r !== null);
67
+ return {
68
+ ...buildRouteTree(routes),
69
+ graph
70
+ };
91
71
  };
72
+ //#endregion
73
+ export { extractMachineRoutes };
74
+
92
75
  //# sourceMappingURL=extract-routes.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"extract-routes.js","sourceRoot":"","sources":["../../src/xstate/extract-routes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"extract-routes.js","names":[],"sources":["../../src/xstate/extract-routes.ts"],"sourcesContent":["import { parsePattern } from \"@xmachines/play-url\";\nimport type { ParseOptions } from \"@xmachines/play-url\";\nimport type { AnyStateMachine } from \"xstate\";\nimport type { GraphNode } from \"@statelyai/graph\";\nimport type { RouteTree, RouteInfo, RouteMetadata } from \"../types.js\";\nimport type { MachineNodeData } from \"./machine-types.js\";\nimport { machineToGraph, type MachineGraph } from \"./machine-to-graph.js\";\nimport { validateRouteFormat, validateStateExists } from \"../validate-routes.js\";\nimport { buildRouteTree } from \"../build-tree.js\";\n\n/**\n * Builds the statePath of a graph node, and it walks the chain of the parentId\n * values.\n *\n * The function makes an array of the stateId segments, from the root to this node,\n * and the array holds no root. This is the format of the parent lookup in\n * buildRouteTree.\n *\n * @param graphNode - The node of the path\n * @param graphNodeMap - The map from a node id to a GraphNode, for a parent lookup in O(1)\n * @returns The array of the stateId segments, for example [\"dashboard\", \"overview\"]\n */\nconst buildStatePath = (\n\tgraphNode: GraphNode<MachineNodeData>,\n\tgraphNodeMap: Map<string, GraphNode<MachineNodeData>>,\n): string[] => {\n\tconst path: string[] = [];\n\tlet current: GraphNode<MachineNodeData> | undefined = graphNode;\n\n\twhile (current && current.parentId !== undefined && current.parentId !== null) {\n\t\tpath.unshift(current.data.stateId);\n\t\tcurrent = graphNodeMap.get(current.parentId);\n\t}\n\n\treturn path;\n};\n\n/**\n * Reads the route information of the MachineNodeData object of a graph node.\n * The function returns null when the node has no route.\n */\nconst extractRouteFromNode = (\n\tgraphNode: GraphNode<MachineNodeData>,\n\tstateIds: Set<string>,\n\tgraphNodeMap: Map<string, GraphNode<MachineNodeData>>,\n\toptions?: ParseOptions,\n): RouteInfo | null => {\n\tconst { stateId, route, meta } = graphNode.data;\n\n\t// The node has no route\n\tif (!route) return null;\n\n\t// Check the format. The path must start with /\n\tvalidateRouteFormat(route, stateId);\n\n\t// Check that the graph holds the state. This test is redundant, and it guards against a corruption\n\tvalidateStateExists(stateId, stateIds);\n\n\t// Build the statePath from the chain of the parentId values. This works for an explicit ID and also for a generated one\n\tconst statePath = buildStatePath(graphNode, graphNodeMap);\n\n\t// The parse answers this, and not a search for a \":\". `route.includes(\":\")` was the\n\t// last expression that read the grammar on its own: it reported no pattern for\n\t// `/docs/*`, for `/books{/:id}?` and for an escaped literal, so a consumer that reads\n\t// `node.pattern` learned that a parameterized route is a literal path.\n\tconst pattern = parsePattern(route, options).parameterized ? route : undefined;\n\n\t// Build the original shape of the metadata again, from the data of the graph\n\tconst metadata: RouteMetadata =\n\t\tmeta && typeof meta === \"object\" && \"route\" in meta\n\t\t\t? (meta[\"route\"] as RouteMetadata)\n\t\t\t: route;\n\n\tconst result: RouteInfo = {\n\t\tstateId,\n\t\tstatePath,\n\t\troutePath: route,\n\t\tisAbsolute: route.startsWith(\"/\"),\n\t\troutable: true,\n\t\tmetadata,\n\t};\n\n\tif (pattern) result.pattern = pattern;\n\n\treturn result;\n};\n\n/**\n * Reads the complete route tree from the graph of a state machine\n *\n * The function converts the XState machine into a Graph of @statelyai/graph, with\n * machineToGraph(). It then walks the graph nodes: it reads the route metadata of\n * each node, it checks each route reference, and it builds a hierarchical RouteTree.\n * The RouteTree of the return value also holds the graph, for a later query that\n * needs the transitions.\n *\n * @param machine - The XState v5 state machine\n * @returns The route tree, with its root, its byStateId map, its byPath map, and its graph\n */\nexport const extractMachineRoutes = (\n\tmachine: AnyStateMachine,\n\toptions?: ParseOptions,\n): RouteTree<MachineGraph> => {\n\t// 1. Convert the machine into a graph\n\tconst graph: MachineGraph = machineToGraph(machine);\n\n\t// 2. Build the set of the state IDs, for the check, and the node map, for a parent lookup in O(1)\n\tconst stateIds = new Set(graph.nodes.map((n) => n.data.stateId));\n\tconst graphNodeMap = new Map(graph.nodes.map((n) => [n.id, n]));\n\n\t// 3. Read the routes of the graph nodes\n\tconst routes = graph.nodes\n\t\t.map((node) => extractRouteFromNode(node, stateIds, graphNodeMap, options))\n\t\t.filter((r): r is RouteInfo => r !== null);\n\n\t// 4. Build the hierarchical tree, and attach the graph.\n\t// buildRouteTree finds each duplicate route, AFTER it resolved each relative route\n\t// to a complete path. The raw route strings are not comparable, because two\n\t// relative \"settings\" routes below two different parents are two different\n\t// routes.\n\tconst tree = buildRouteTree(routes);\n\treturn { ...tree, graph };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;AAsBA,IAAM,kBACL,WACA,iBACc;CACd,MAAM,OAAiB,CAAC;CACxB,IAAI,UAAkD;CAEtD,OAAO,WAAW,QAAQ,aAAa,KAAA,KAAa,QAAQ,aAAa,MAAM;EAC9E,KAAK,QAAQ,QAAQ,KAAK,OAAO;EACjC,UAAU,aAAa,IAAI,QAAQ,QAAQ;CAC5C;CAEA,OAAO;AACR;;;;;AAMA,IAAM,wBACL,WACA,UACA,cACA,YACsB;CACtB,MAAM,EAAE,SAAS,OAAO,SAAS,UAAU;CAG3C,IAAI,CAAC,OAAO,OAAO;CAGnB,oBAAoB,OAAO,OAAO;CAGlC,oBAAoB,SAAS,QAAQ;CAGrC,MAAM,YAAY,eAAe,WAAW,YAAY;CAMxD,MAAM,UAAU,aAAa,OAAO,OAAO,CAAC,CAAC,gBAAgB,QAAQ,KAAA;CAGrE,MAAM,WACL,QAAQ,OAAO,SAAS,YAAY,WAAW,OAC3C,KAAK,WACN;CAEJ,MAAM,SAAoB;EACzB;EACA;EACA,WAAW;EACX,YAAY,MAAM,WAAW,GAAG;EAChC,UAAU;EACV;CACD;CAEA,IAAI,SAAS,OAAO,UAAU;CAE9B,OAAO;AACR;;;;;;;;;;;;;AAcA,IAAa,wBACZ,SACA,YAC6B;CAE7B,MAAM,QAAsB,eAAe,OAAO;CAGlD,MAAM,WAAW,IAAI,IAAI,MAAM,MAAM,KAAK,MAAM,EAAE,KAAK,OAAO,CAAC;CAC/D,MAAM,eAAe,IAAI,IAAI,MAAM,MAAM,KAAK,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;CAG9D,MAAM,SAAS,MAAM,MACnB,KAAK,SAAS,qBAAqB,MAAM,UAAU,cAAc,OAAO,CAAC,CAAC,CAC1E,QAAQ,MAAsB,MAAM,IAAI;CAQ1C,OAAO;EAAE,GADI,eAAe,MAChB;EAAM;CAAM;AACzB"}
@@ -1,33 +1,5 @@
1
- /**
2
- * `@xmachines/play-router/xstate` - the routes of an XState machine
3
- *
4
- * A route of XMachines is a fact about a STATE. This entry point reads that fact out of an
5
- * XState v5 machine: it walks the machine, collects each `meta.route`, builds the route
6
- * tree, and answers the questions that the tree can be asked — which routes are reachable,
7
- * which are navigable from a state, and which path maps to which state id.
8
- *
9
- * The MAIN entry point holds what happens AFTERWARDS: the route map, the match of a
10
- * location, the bridge, and the provider lifecycle. It names no machine, and it reads this
11
- * module never. `xstate` is therefore an OPTIONAL peer of this package, and a bundler drops
12
- * `@statelyai/graph` for a consumer that imports this entry point nowhere.
13
- *
14
- * This is the same shape as `@xmachines/play-xstate/routing` and
15
- * `@xmachines/play-xstate/view`: an optional capability behind its own entry point, with
16
- * its own dependency.
17
- *
18
- * @example
19
- * ```typescript
20
- * import { createRouteMap } from "@xmachines/play-router/xstate";
21
- * import { connectRouter } from "@xmachines/play-dom-router";
22
- *
23
- * const routeMap = createRouteMap(myMachine);
24
- * ```
25
- *
26
- * @packageDocumentation
27
- * @see [Routing guide](../../../docs/guides/routing.md)
28
- */
29
- export { extractMachineRoutes } from "./extract-routes.js";
30
- export { createRouteMap } from "./create-route-map.js";
31
- export { machineToGraph } from "./machine-to-graph.js";
32
- export { getNavigableRoutes, getRoutableRoutes, routeExists, getTransitionReachableRoutes, isRouteReachable, getRouteMappings, } from "./query.js";
33
- //# sourceMappingURL=index.js.map
1
+ import { machineToGraph } from "./machine-to-graph.js";
2
+ import { extractMachineRoutes } from "./extract-routes.js";
3
+ import { getNavigableRoutes, getRoutableRoutes, getRouteMappings, getTransitionReachableRoutes, isRouteReachable, routeExists } from "./query.js";
4
+ import { createRouteMap } from "./create-route-map.js";
5
+ export { createRouteMap, extractMachineRoutes, getNavigableRoutes, getRoutableRoutes, getRouteMappings, getTransitionReachableRoutes, isRouteReachable, machineToGraph, routeExists };