@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,101 +1,89 @@
1
1
  import { detectDuplicateRoutes } from "./validate-routes.js";
2
+ //#region packages/play-router/src/build-tree.ts
2
3
  /**
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__";
4
+ * The ID and the state ID of the synthetic root node of every `RouteTree`.
5
+ *
6
+ * `buildRouteTree` always makes this node, and no state of a machine declares it. It
7
+ * gives the tree one parent for each absolute route, and it holds `routable: false`.
8
+ *
9
+ * A consumer that walks a tree must SKIP this node. `createRouteMapFromTree` skips it,
10
+ * and `getRoutableRoutes` of `@xmachines/play-router/xstate` skips it. A walk that keeps
11
+ * it maps the path `/` to a state that the machine does not hold.
12
+ */
13
+ var ROOT_NODE_ID = "__root__";
13
14
  /**
14
- * Builds the hierarchical route tree from the flat list of the routes
15
- *
16
- * The function makes a nested tree, and it keeps the parent-child relation of the
17
- * states. An absolute route becomes a route of the top level, and a relative route
18
- * goes below its parent. The function also makes the maps of both directions, for
19
- * the lookup between a state ID and a path.
20
- *
21
- * The detection of a duplicate runs here, after the function resolved each relative
22
- * route against the complete path of its parent. Therefore two relative routes with
23
- * the same raw string below two different parents are valid, and two different raw
24
- * strings that resolve to the same complete path are not: those two overwrite each
25
- * other in `byPath` in silence.
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
- *
31
- * @param routes - The flat list of the RouteInfo objects, from the extraction
32
- * @returns The RouteTree, with its root, its byStateId map, and its byPath map
33
- * @throws {DuplicateRoutePathError} When two states resolve to the same complete path
34
- */
35
- export const buildRouteTree = (routes) => {
36
- // 1. Create the root node
37
- const root = {
38
- id: ROOT_NODE_ID,
39
- path: "/",
40
- fullPath: "/",
41
- stateId: ROOT_NODE_ID,
42
- routable: false, // The root is a state without a route
43
- children: [],
44
- parent: null,
45
- metadata: "", // The synthetic root has no route, and therefore no real route metadata
46
- };
47
- // 2. Initialize the maps
48
- const byStateId = new Map([[root.stateId, root]]);
49
- const byPath = new Map([[root.fullPath, root]]);
50
- // 3. Sort the routes by their depth. The shallowest route comes first, for the link to each parent
51
- const sorted = routes.toSorted((a, b) => a.statePath.length - b.statePath.length);
52
- // 4. Build the tree, and collect each node for the detection of a duplicate resolved path
53
- const insertedNodes = [];
54
- for (const route of sorted) {
55
- // Find the parent, and walk up the state path.
56
- // Each entry of statePath is a complete dotted stateId, for example
57
- // "chain.app.section". Therefore the code tests each ancestor stateId directly,
58
- // from the end of the list.
59
- let parentNode = root;
60
- for (let i = route.statePath.length - 2; i >= 0; i--) {
61
- const parentStateId = route.statePath[i]; // nosemgrep: gitlab.eslint.detect-object-injection
62
- if (parentStateId === undefined)
63
- continue;
64
- const candidate = byStateId.get(parentStateId);
65
- if (candidate !== undefined) {
66
- parentNode = candidate;
67
- break;
68
- }
69
- }
70
- // Build the complete path
71
- const fullPath = route.isAbsolute
72
- ? route.routePath
73
- : `${parentNode.fullPath}/${route.routePath}`.replace(/\/+/g, "/");
74
- // Create the node
75
- const node = {
76
- id: route.stateId,
77
- path: route.routePath,
78
- fullPath,
79
- stateId: route.stateId,
80
- routable: route.routable,
81
- children: [],
82
- parent: parentNode,
83
- metadata: route.metadata,
84
- };
85
- // Add the pattern, when one is present
86
- if (route.pattern) {
87
- node.pattern = route.pattern;
88
- }
89
- // Link the node to its parent
90
- parentNode.children.push(node);
91
- // Put the node in the maps, for the lookup in both directions
92
- byStateId.set(node.stateId, node);
93
- byPath.set(node.fullPath, node);
94
- insertedNodes.push(node);
95
- }
96
- // 5. Refuse a collision of two resolved complete paths. A silent overwrite in
97
- // byPath leaves one state without a URL.
98
- detectDuplicateRoutes(insertedNodes);
99
- return { root, byStateId, byPath };
15
+ * Builds the hierarchical route tree from the flat list of the routes
16
+ *
17
+ * The function makes a nested tree, and it keeps the parent-child relation of the
18
+ * states. An absolute route becomes a route of the top level, and a relative route
19
+ * goes below its parent. The function also makes the maps of both directions, for
20
+ * the lookup between a state ID and a path.
21
+ *
22
+ * The detection of a duplicate runs here, after the function resolved each relative
23
+ * route against the complete path of its parent. Therefore two relative routes with
24
+ * the same raw string below two different parents are valid, and two different raw
25
+ * strings that resolve to the same complete path are not: those two overwrite each
26
+ * other in `byPath` in silence.
27
+ *
28
+ * `TGraph` names the type of the graph that a CALLER attaches afterwards. The builder
29
+ * attaches none: it reads a list of routes and writes the maps of the tree.
30
+ * `extractMachineRoutes` of `@xmachines/play-router/xstate` binds it to `MachineGraph`.
31
+ *
32
+ * @param routes - The flat list of the RouteInfo objects, from the extraction
33
+ * @returns The RouteTree, with its root, its byStateId map, and its byPath map
34
+ * @throws {DuplicateRoutePathError} When two states resolve to the same complete path
35
+ */
36
+ var buildRouteTree = (routes) => {
37
+ const root = {
38
+ id: ROOT_NODE_ID,
39
+ path: "/",
40
+ fullPath: "/",
41
+ stateId: ROOT_NODE_ID,
42
+ routable: false,
43
+ children: [],
44
+ parent: null,
45
+ metadata: ""
46
+ };
47
+ const byStateId = /* @__PURE__ */ new Map([[root.stateId, root]]);
48
+ const byPath = /* @__PURE__ */ new Map([[root.fullPath, root]]);
49
+ const sorted = routes.toSorted((a, b) => a.statePath.length - b.statePath.length);
50
+ const insertedNodes = [];
51
+ for (const route of sorted) {
52
+ let parentNode = root;
53
+ for (let i = route.statePath.length - 2; i >= 0; i--) {
54
+ const parentStateId = route.statePath[i];
55
+ if (parentStateId === void 0) continue;
56
+ const candidate = byStateId.get(parentStateId);
57
+ if (candidate !== void 0) {
58
+ parentNode = candidate;
59
+ break;
60
+ }
61
+ }
62
+ const fullPath = route.isAbsolute ? route.routePath : `${parentNode.fullPath}/${route.routePath}`.replace(/\/+/g, "/");
63
+ const node = {
64
+ id: route.stateId,
65
+ path: route.routePath,
66
+ fullPath,
67
+ stateId: route.stateId,
68
+ routable: route.routable,
69
+ children: [],
70
+ parent: parentNode,
71
+ metadata: route.metadata
72
+ };
73
+ if (route.pattern) node.pattern = route.pattern;
74
+ parentNode.children.push(node);
75
+ byStateId.set(node.stateId, node);
76
+ byPath.set(node.fullPath, node);
77
+ insertedNodes.push(node);
78
+ }
79
+ detectDuplicateRoutes(insertedNodes);
80
+ return {
81
+ root,
82
+ byStateId,
83
+ byPath
84
+ };
100
85
  };
86
+ //#endregion
87
+ export { ROOT_NODE_ID, buildRouteTree };
88
+
101
89
  //# sourceMappingURL=build-tree.js.map
@@ -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;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,UAAU,CAAC;AAEvC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAmB,MAAmB,EAAqB,EAAE;IAC1F,0BAA0B;IAC1B,MAAM,IAAI,GAAc;QACvB,EAAE,EAAE,YAAY;QAChB,IAAI,EAAE,GAAG;QACT,QAAQ,EAAE,GAAG;QACb,OAAO,EAAE,YAAY;QACrB,QAAQ,EAAE,KAAK,EAAE,sCAAsC;QACvD,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,IAAI;QACZ,QAAQ,EAAE,EAAE,EAAE,wEAAwE;KACtF,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,aAAa,KAAK,SAAS;gBAAE,SAAS;YAC1C,MAAM,SAAS,GAAG,SAAS,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;YAC/C,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;gBAC7B,UAAU,GAAG,SAAS,CAAC;gBACvB,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","names":[],"sources":["../src/build-tree.ts"],"sourcesContent":["import type { RouteInfo, RouteNode, RouteTree } from \"./types.js\";\nimport { detectDuplicateRoutes } from \"./validate-routes.js\";\n\n/**\n * The ID and the state ID of the synthetic root node of every `RouteTree`.\n *\n * `buildRouteTree` always makes this node, and no state of a machine declares it. It\n * gives the tree one parent for each absolute route, and it holds `routable: false`.\n *\n * A consumer that walks a tree must SKIP this node. `createRouteMapFromTree` skips it,\n * and `getRoutableRoutes` of `@xmachines/play-router/xstate` skips it. A walk that keeps\n * it maps the path `/` to a state that the machine does not hold.\n */\nexport const ROOT_NODE_ID = \"__root__\";\n\n/**\n * Builds the hierarchical route tree from the flat list of the routes\n *\n * The function makes a nested tree, and it keeps the parent-child relation of the\n * states. An absolute route becomes a route of the top level, and a relative route\n * goes below its parent. The function also makes the maps of both directions, for\n * the lookup between a state ID and a path.\n *\n * The detection of a duplicate runs here, after the function resolved each relative\n * route against the complete path of its parent. Therefore two relative routes with\n * the same raw string below two different parents are valid, and two different raw\n * strings that resolve to the same complete path are not: those two overwrite each\n * other in `byPath` in silence.\n *\n * `TGraph` names the type of the graph that a CALLER attaches afterwards. The builder\n * attaches none: it reads a list of routes and writes the maps of the tree.\n * `extractMachineRoutes` of `@xmachines/play-router/xstate` binds it to `MachineGraph`.\n *\n * @param routes - The flat list of the RouteInfo objects, from the extraction\n * @returns The RouteTree, with its root, its byStateId map, and its byPath map\n * @throws {DuplicateRoutePathError} When two states resolve to the same complete path\n */\nexport const buildRouteTree = <TGraph = unknown>(routes: RouteInfo[]): RouteTree<TGraph> => {\n\t// 1. Create the root node\n\tconst root: RouteNode = {\n\t\tid: ROOT_NODE_ID,\n\t\tpath: \"/\",\n\t\tfullPath: \"/\",\n\t\tstateId: ROOT_NODE_ID,\n\t\troutable: false, // The root is a state without a route\n\t\tchildren: [],\n\t\tparent: null,\n\t\tmetadata: \"\", // The synthetic root has no route, and therefore no real route metadata\n\t};\n\n\t// 2. Initialize the maps\n\tconst byStateId = new Map<string, RouteNode>([[root.stateId, root]]);\n\tconst byPath = new Map<string, RouteNode>([[root.fullPath, root]]);\n\n\t// 3. Sort the routes by their depth. The shallowest route comes first, for the link to each parent\n\tconst sorted = routes.toSorted((a, b) => a.statePath.length - b.statePath.length);\n\n\t// 4. Build the tree, and collect each node for the detection of a duplicate resolved path\n\tconst insertedNodes: RouteNode[] = [];\n\tfor (const route of sorted) {\n\t\t// Find the parent, and walk up the state path.\n\t\t// Each entry of statePath is a complete dotted stateId, for example\n\t\t// \"chain.app.section\". Therefore the code tests each ancestor stateId directly,\n\t\t// from the end of the list.\n\t\tlet parentNode = root;\n\t\tfor (let i = route.statePath.length - 2; i >= 0; i--) {\n\t\t\tconst parentStateId = route.statePath[i]; // nosemgrep: gitlab.eslint.detect-object-injection\n\t\t\tif (parentStateId === undefined) continue;\n\t\t\tconst candidate = byStateId.get(parentStateId);\n\t\t\tif (candidate !== undefined) {\n\t\t\t\tparentNode = candidate;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// Build the complete path\n\t\tconst fullPath = route.isAbsolute\n\t\t\t? route.routePath\n\t\t\t: `${parentNode.fullPath}/${route.routePath}`.replace(/\\/+/g, \"/\");\n\n\t\t// Create the node\n\t\tconst node: RouteNode = {\n\t\t\tid: route.stateId,\n\t\t\tpath: route.routePath,\n\t\t\tfullPath,\n\t\t\tstateId: route.stateId,\n\t\t\troutable: route.routable,\n\t\t\tchildren: [],\n\t\t\tparent: parentNode,\n\t\t\tmetadata: route.metadata,\n\t\t};\n\n\t\t// Add the pattern, when one is present\n\t\tif (route.pattern) {\n\t\t\tnode.pattern = route.pattern;\n\t\t}\n\n\t\t// Link the node to its parent\n\t\tparentNode.children.push(node);\n\n\t\t// Put the node in the maps, for the lookup in both directions\n\t\tbyStateId.set(node.stateId, node);\n\t\tbyPath.set(node.fullPath, node);\n\t\tinsertedNodes.push(node);\n\t}\n\n\t// 5. Refuse a collision of two resolved complete paths. A silent overwrite in\n\t// byPath leaves one state without a URL.\n\tdetectDuplicateRoutes(insertedNodes);\n\n\treturn { root, byStateId, byPath };\n};\n"],"mappings":";;;;;;;;;;;;AAaA,IAAa,eAAe;;;;;;;;;;;;;;;;;;;;;;;AAwB5B,IAAa,kBAAoC,WAA2C;CAE3F,MAAM,OAAkB;EACvB,IAAI;EACJ,MAAM;EACN,UAAU;EACV,SAAS;EACT,UAAU;EACV,UAAU,CAAC;EACX,QAAQ;EACR,UAAU;CACX;CAGA,MAAM,4BAAY,IAAI,IAAuB,CAAC,CAAC,KAAK,SAAS,IAAI,CAAC,CAAC;CACnE,MAAM,yBAAS,IAAI,IAAuB,CAAC,CAAC,KAAK,UAAU,IAAI,CAAC,CAAC;CAGjE,MAAM,SAAS,OAAO,UAAU,GAAG,MAAM,EAAE,UAAU,SAAS,EAAE,UAAU,MAAM;CAGhF,MAAM,gBAA6B,CAAC;CACpC,KAAK,MAAM,SAAS,QAAQ;EAK3B,IAAI,aAAa;EACjB,KAAK,IAAI,IAAI,MAAM,UAAU,SAAS,GAAG,KAAK,GAAG,KAAK;GACrD,MAAM,gBAAgB,MAAM,UAAU;GACtC,IAAI,kBAAkB,KAAA,GAAW;GACjC,MAAM,YAAY,UAAU,IAAI,aAAa;GAC7C,IAAI,cAAc,KAAA,GAAW;IAC5B,aAAa;IACb;GACD;EACD;EAGA,MAAM,WAAW,MAAM,aACpB,MAAM,YACN,GAAG,WAAW,SAAS,GAAG,MAAM,YAAY,QAAQ,QAAQ,GAAG;EAGlE,MAAM,OAAkB;GACvB,IAAI,MAAM;GACV,MAAM,MAAM;GACZ;GACA,SAAS,MAAM;GACf,UAAU,MAAM;GAChB,UAAU,CAAC;GACX,QAAQ;GACR,UAAU,MAAM;EACjB;EAGA,IAAI,MAAM,SACT,KAAK,UAAU,MAAM;EAItB,WAAW,SAAS,KAAK,IAAI;EAG7B,UAAU,IAAI,KAAK,SAAS,IAAI;EAChC,OAAO,IAAI,KAAK,UAAU,IAAI;EAC9B,cAAc,KAAK,IAAI;CACxB;CAIA,sBAAsB,aAAa;CAEnC,OAAO;EAAE;EAAM;EAAW;CAAO;AAClC"}
@@ -1,51 +1,54 @@
1
1
  import { RouteMap } from "./base-route-map.js";
2
- import { ROOT_NODE_ID } from "./build-tree.js";
2
+ import "./build-tree.js";
3
+ //#region packages/play-router/src/create-route-map-from-tree.ts
3
4
  /**
4
- * Creates a `RouteMap` from the node structure of a `RouteTree`.
5
- *
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
- *
10
- * The function walks every node, and it collects the pairs
11
- * `{ stateId: node.id, path: node.fullPath }`. `node.fullPath` is always the
12
- * absolute resolved path, for example `"/dashboard/overview"`, and `RouteMap` needs
13
- * that path for the match of a browser URL. `createRouteMap(machine)` behaves in the
14
- * same way, because it also uses `node.fullPath`.
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
- *
21
- * @param routeTree - A `RouteTree`, as `extractMachineRoutes()` returns it.
22
- * @param options - The optional configuration, for example `{ pathCache }` to replace the LRU cache of the resolved paths.
23
- * @returns A `RouteMap` for each adapter on `RouterBridgeBase`.
24
- *
25
- * @example
26
- * ```typescript
27
- * // The preferred form — one call for an XState machine:
28
- * import { createRouteMap } from '@xmachines/play-router/xstate';
29
- * const routeMap = createRouteMap(machine); // it takes an AnyStateMachine
30
- *
31
- * // The two-step form, for a framework adapter that works with a route tree:
32
- * import { createRouteMapFromTree } from '@xmachines/play-router';
33
- * import { extractMachineRoutes } from '@xmachines/play-router/xstate';
34
- * const routeTree = extractMachineRoutes(machine);
35
- * const routeMap = createRouteMapFromTree(routeTree); // it uses node.fullPath, which is absolute
36
- * ```
37
- */
38
- export function createRouteMapFromTree(routeTree, options) {
39
- const routes = [];
40
- function traverse(node) {
41
- if (node.id && node.fullPath && node.id !== ROOT_NODE_ID) {
42
- routes.push({ stateId: node.id, path: node.fullPath });
43
- }
44
- if (node.children) {
45
- node.children.forEach(traverse);
46
- }
47
- }
48
- traverse(routeTree.root);
49
- return new RouteMap(routes, options);
5
+ * Creates a `RouteMap` from the node structure of a `RouteTree`.
6
+ *
7
+ * A framework router adapter uses this function when it holds a `RouteTree`, for example
8
+ * from `extractMachineRoutes()` of `@xmachines/play-router/xstate`, and not when it calls
9
+ * `createRouteMap()` of that package directly.
10
+ *
11
+ * The function walks every node, and it collects the pairs
12
+ * `{ stateId: node.id, path: node.fullPath }`. `node.fullPath` is always the
13
+ * absolute resolved path, for example `"/dashboard/overview"`, and `RouteMap` needs
14
+ * that path for the match of a browser URL. `createRouteMap(machine)` behaves in the
15
+ * same way, because it also uses `node.fullPath`.
16
+ *
17
+ * The walk SKIPS the synthetic root node. `buildRouteTree` makes that node to hold each
18
+ * absolute route, and no state of a machine declares it. `createRouteMap` skips it as
19
+ * well, through `getRoutableRoutes`, and the two functions therefore answer the same map
20
+ * for the same tree.
21
+ *
22
+ * @param routeTree - A `RouteTree`, as `extractMachineRoutes()` returns it.
23
+ * @param options - The optional configuration, for example `{ pathCache }` to replace the LRU cache of the resolved paths.
24
+ * @returns A `RouteMap` for each adapter on `RouterBridgeBase`.
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * // The preferred form — one call for an XState machine:
29
+ * import { createRouteMap } from '@xmachines/play-router/xstate';
30
+ * const routeMap = createRouteMap(machine); // it takes an AnyStateMachine
31
+ *
32
+ * // The two-step form, for a framework adapter that works with a route tree:
33
+ * import { createRouteMapFromTree } from '@xmachines/play-router';
34
+ * import { extractMachineRoutes } from '@xmachines/play-router/xstate';
35
+ * const routeTree = extractMachineRoutes(machine);
36
+ * const routeMap = createRouteMapFromTree(routeTree); // it uses node.fullPath, which is absolute
37
+ * ```
38
+ */
39
+ function createRouteMapFromTree(routeTree, options) {
40
+ const routes = [];
41
+ function traverse(node) {
42
+ if (node.id && node.fullPath && node.id !== "__root__") routes.push({
43
+ stateId: node.id,
44
+ path: node.fullPath
45
+ });
46
+ if (node.children) node.children.forEach(traverse);
47
+ }
48
+ traverse(routeTree.root);
49
+ return new RouteMap(routes, options);
50
50
  }
51
+ //#endregion
52
+ export { createRouteMapFromTree };
53
+
51
54
  //# sourceMappingURL=create-route-map-from-tree.js.map
@@ -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;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"}
1
+ {"version":3,"file":"create-route-map-from-tree.js","names":[],"sources":["../src/create-route-map-from-tree.ts"],"sourcesContent":["import type { RouteTree, RouteNode, RouteMapOptions } from \"./types.js\";\nimport { RouteMap } from \"./base-route-map.js\";\nimport { ROOT_NODE_ID } from \"./build-tree.js\";\n\n/**\n * Creates a `RouteMap` from the node structure of a `RouteTree`.\n *\n * A framework router adapter uses this function when it holds a `RouteTree`, for example\n * from `extractMachineRoutes()` of `@xmachines/play-router/xstate`, and not when it calls\n * `createRouteMap()` of that package directly.\n *\n * The function walks every node, and it collects the pairs\n * `{ stateId: node.id, path: node.fullPath }`. `node.fullPath` is always the\n * absolute resolved path, for example `\"/dashboard/overview\"`, and `RouteMap` needs\n * that path for the match of a browser URL. `createRouteMap(machine)` behaves in the\n * same way, because it also uses `node.fullPath`.\n *\n * The walk SKIPS the synthetic root node. `buildRouteTree` makes that node to hold each\n * absolute route, and no state of a machine declares it. `createRouteMap` skips it as\n * well, through `getRoutableRoutes`, and the two functions therefore answer the same map\n * for the same tree.\n *\n * @param routeTree - A `RouteTree`, as `extractMachineRoutes()` returns it.\n * @param options - The optional configuration, for example `{ pathCache }` to replace the LRU cache of the resolved paths.\n * @returns A `RouteMap` for each adapter on `RouterBridgeBase`.\n *\n * @example\n * ```typescript\n * // The preferred form — one call for an XState machine:\n * import { createRouteMap } from '@xmachines/play-router/xstate';\n * const routeMap = createRouteMap(machine); // it takes an AnyStateMachine\n *\n * // The two-step form, for a framework adapter that works with a route tree:\n * import { createRouteMapFromTree } from '@xmachines/play-router';\n * import { extractMachineRoutes } from '@xmachines/play-router/xstate';\n * const routeTree = extractMachineRoutes(machine);\n * const routeMap = createRouteMapFromTree(routeTree); // it uses node.fullPath, which is absolute\n * ```\n */\nexport function createRouteMapFromTree(routeTree: RouteTree, options?: RouteMapOptions): RouteMap {\n\tconst routes: Array<{ stateId: string; path: string }> = [];\n\n\tfunction traverse(node: RouteNode): void {\n\t\tif (node.id && node.fullPath && node.id !== ROOT_NODE_ID) {\n\t\t\troutes.push({ stateId: node.id, path: node.fullPath });\n\t\t}\n\t\tif (node.children) {\n\t\t\tnode.children.forEach(traverse);\n\t\t}\n\t}\n\n\ttraverse(routeTree.root);\n\treturn new RouteMap(routes, options);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCA,SAAgB,uBAAuB,WAAsB,SAAqC;CACjG,MAAM,SAAmD,CAAC;CAE1D,SAAS,SAAS,MAAuB;EACxC,IAAI,KAAK,MAAM,KAAK,YAAY,KAAK,OAAA,YACpC,OAAO,KAAK;GAAE,SAAS,KAAK;GAAI,MAAM,KAAK;EAAS,CAAC;EAEtD,IAAI,KAAK,UACR,KAAK,SAAS,QAAQ,QAAQ;CAEhC;CAEA,SAAS,UAAU,IAAI;CACvB,OAAO,IAAI,SAAS,QAAQ,OAAO;AACpC"}
package/dist/errors.js CHANGED
@@ -1,141 +1,132 @@
1
1
  import { PlayError } from "@xmachines/play/errors";
2
- // The errors of the URL language live in `@xmachines/play-url`, with the parser that
3
- // raises them. They are re-exported here, so that a consumer of this package sorts every
4
- // routing error from one import, exactly as before the split.
5
- //
6
- // The specifier is the `./errors` subpath, and not the root barrel. A consumer that
7
- // imports `@xmachines/play-router/errors` to name a fault in a `catch` then loads the
8
- // three classes alone: no pattern parser, no base path, and no `urlpattern-polyfill`. The
9
- // root barrel of this package reaches the rest of `@xmachines/play-url` for the consumer
10
- // that needs it.
11
- export { InvalidRoutePatternError, InvalidBasePathError, MissingBasePathParamError, } from "@xmachines/play-url/errors";
2
+ import { InvalidBasePathError, InvalidRoutePatternError, MissingBasePathParamError } from "@xmachines/play-url/errors";
3
+ //#region packages/play-router/src/errors.ts
12
4
  /**
13
- * The bridge throws this error when `RouterBridgeBase.syncActorFromRouter()`
14
- * cannot process a location change of the router and send the `play.route` event to
15
- * the actor.
16
- *
17
- * The `cause` property holds the original error.
18
- *
19
- * **Error code:** `PLAY_ROUTER_SYNC_FAILED`
20
- *
21
- * @example
22
- * ```typescript
23
- * import { RouterSyncError } from "@xmachines/play-router/errors";
24
- *
25
- * try {
26
- * bridge.connect();
27
- * } catch (err) {
28
- * if (err instanceof RouterSyncError) {
29
- * console.error("Router sync failed:", err.message, err.cause);
30
- * }
31
- * }
32
- * ```
33
- */
34
- export class RouterSyncError extends PlayError {
35
- constructor(message, options) {
36
- super("RouterBridgeBase", "PLAY_ROUTER_SYNC_FAILED", message, options);
37
- this.name = "RouterSyncError";
38
- }
39
- }
5
+ * The bridge throws this error when `RouterBridgeBase.syncActorFromRouter()`
6
+ * cannot process a location change of the router and send the `play.route` event to
7
+ * the actor.
8
+ *
9
+ * The `cause` property holds the original error.
10
+ *
11
+ * **Error code:** `PLAY_ROUTER_SYNC_FAILED`
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import { RouterSyncError } from "@xmachines/play-router/errors";
16
+ *
17
+ * try {
18
+ * bridge.connect();
19
+ * } catch (err) {
20
+ * if (err instanceof RouterSyncError) {
21
+ * console.error("Router sync failed:", err.message, err.cause);
22
+ * }
23
+ * }
24
+ * ```
25
+ */
26
+ var RouterSyncError = class extends PlayError {
27
+ constructor(message, options) {
28
+ super("RouterBridgeBase", "PLAY_ROUTER_SYNC_FAILED", message, options);
29
+ this.name = "RouterSyncError";
30
+ }
31
+ };
40
32
  /**
41
- * `RouterBridgeBase.connect()` throws this error when a second bridge connects to an
42
- * actor with an active bridge connection.
43
- *
44
- * One actor can hold one router bridge at a time. Two bridges of the same actor both
45
- * receive and process every navigation event of the router. They therefore send two
46
- * `play.route` events to the actor, and their `lastSyncedPath` values of the echo
47
- * suppression contradict each other.
48
- *
49
- * **The remedy:** call `bridge.disconnect()` on the first bridge before you connect
50
- * the second one. You can also permit one active `connectRouter()` call for each
51
- * actor at a time.
52
- *
53
- * **Error code:** `PLAY_ROUTER_DUPLICATE_BRIDGE`
54
- *
55
- * @example
56
- * ```typescript
57
- * import { DuplicateBridgeError } from "@xmachines/play-router/errors";
58
- *
59
- * try {
60
- * bridge.connect();
61
- * } catch (err) {
62
- * if (err instanceof DuplicateBridgeError) {
63
- * console.error("Actor already has an active router bridge — disconnect first.");
64
- * }
65
- * }
66
- * ```
67
- */
68
- export class DuplicateBridgeError extends PlayError {
69
- constructor() {
70
- super("RouterBridgeBase", "PLAY_ROUTER_DUPLICATE_BRIDGE", "An actor can only be connected to one router bridge at a time. " +
71
- "Call disconnect() on the existing bridge before connecting a new one.");
72
- this.name = "DuplicateBridgeError";
73
- }
74
- }
33
+ * `RouterBridgeBase.connect()` throws this error when a second bridge connects to an
34
+ * actor with an active bridge connection.
35
+ *
36
+ * One actor can hold one router bridge at a time. Two bridges of the same actor both
37
+ * receive and process every navigation event of the router. They therefore send two
38
+ * `play.route` events to the actor, and their `lastSyncedPath` values of the echo
39
+ * suppression contradict each other.
40
+ *
41
+ * **The remedy:** call `bridge.disconnect()` on the first bridge before you connect
42
+ * the second one. You can also permit one active `connectRouter()` call for each
43
+ * actor at a time.
44
+ *
45
+ * **Error code:** `PLAY_ROUTER_DUPLICATE_BRIDGE`
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * import { DuplicateBridgeError } from "@xmachines/play-router/errors";
50
+ *
51
+ * try {
52
+ * bridge.connect();
53
+ * } catch (err) {
54
+ * if (err instanceof DuplicateBridgeError) {
55
+ * console.error("Actor already has an active router bridge — disconnect first.");
56
+ * }
57
+ * }
58
+ * ```
59
+ */
60
+ var DuplicateBridgeError = class extends PlayError {
61
+ constructor() {
62
+ super("RouterBridgeBase", "PLAY_ROUTER_DUPLICATE_BRIDGE", "An actor can only be connected to one router bridge at a time. Call disconnect() on the existing bridge before connecting a new one.");
63
+ this.name = "DuplicateBridgeError";
64
+ }
65
+ };
75
66
  /**
76
- * `validateRouteFormat()` throws this error when a state declares a `meta.route`
77
- * field with an empty string as its path.
78
- *
79
- * **Error code:** `PLAY_ROUTE_EMPTY_PATH`
80
- */
81
- export class EmptyRoutePathError extends PlayError {
82
- /** The state ID with the empty route path. */
83
- stateId;
84
- constructor(stateId) {
85
- super("RouteMap", "PLAY_ROUTE_EMPTY_PATH", `Empty route path in state "${stateId}": routes must have a non-empty path`);
86
- this.name = "EmptyRoutePathError";
87
- this.stateId = stateId;
88
- }
89
- }
67
+ * `validateRouteFormat()` throws this error when a state declares a `meta.route`
68
+ * field with an empty string as its path.
69
+ *
70
+ * **Error code:** `PLAY_ROUTE_EMPTY_PATH`
71
+ */
72
+ var EmptyRoutePathError = class extends PlayError {
73
+ /** The state ID with the empty route path. */
74
+ stateId;
75
+ constructor(stateId) {
76
+ super("RouteMap", "PLAY_ROUTE_EMPTY_PATH", `Empty route path in state "${stateId}": routes must have a non-empty path`);
77
+ this.name = "EmptyRoutePathError";
78
+ this.stateId = stateId;
79
+ }
80
+ };
90
81
  /**
91
- * `validateStateExists()` throws this error when a route names a state ID that the
92
- * machine graph does not hold.
93
- *
94
- * **Error code:** `PLAY_ROUTE_INVALID_STATE_ID`
95
- */
96
- export class InvalidStateIdError extends PlayError {
97
- /** The state ID of the reference. The graph does not hold it. */
98
- stateId;
99
- constructor(stateId) {
100
- super("RouteMap", "PLAY_ROUTE_INVALID_STATE_ID", `Route references non-existent state ID: ${stateId}`);
101
- this.name = "InvalidStateIdError";
102
- this.stateId = stateId;
103
- }
104
- }
82
+ * `validateStateExists()` throws this error when a route names a state ID that the
83
+ * machine graph does not hold.
84
+ *
85
+ * **Error code:** `PLAY_ROUTE_INVALID_STATE_ID`
86
+ */
87
+ var InvalidStateIdError = class extends PlayError {
88
+ /** The state ID of the reference. The graph does not hold it. */
89
+ stateId;
90
+ constructor(stateId) {
91
+ super("RouteMap", "PLAY_ROUTE_INVALID_STATE_ID", `Route references non-existent state ID: ${stateId}`);
92
+ this.name = "InvalidStateIdError";
93
+ this.stateId = stateId;
94
+ }
95
+ };
105
96
  /**
106
- * `detectDuplicateRoutes()` throws this error when two states or more hold the same
107
- * URL path.
108
- *
109
- * **Error code:** `PLAY_ROUTE_DUPLICATE_PATH`
110
- */
111
- export class DuplicateRoutePathError extends PlayError {
112
- /** The list of the path → state-ID mappings of the conflict, for a person to read. */
113
- duplicates;
114
- constructor(duplicates) {
115
- super("RouteMap", "PLAY_ROUTE_DUPLICATE_PATH", `Duplicate route paths detected:\n${duplicates.join("\n")}\n\n` +
116
- `Each route path must map to exactly one state.\n` +
117
- `Use different paths (e.g., /dashboard vs /) or conditional rendering within a single state.`);
118
- this.name = "DuplicateRoutePathError";
119
- this.duplicates = duplicates;
120
- }
121
- }
97
+ * `detectDuplicateRoutes()` throws this error when two states or more hold the same
98
+ * URL path.
99
+ *
100
+ * **Error code:** `PLAY_ROUTE_DUPLICATE_PATH`
101
+ */
102
+ var DuplicateRoutePathError = class extends PlayError {
103
+ /** The list of the path → state-ID mappings of the conflict, for a person to read. */
104
+ duplicates;
105
+ constructor(duplicates) {
106
+ super("RouteMap", "PLAY_ROUTE_DUPLICATE_PATH", `Duplicate route paths detected:\n${duplicates.join("\n")}\n\nEach route path must map to exactly one state.\nUse different paths (e.g., /dashboard vs /) or conditional rendering within a single state.`);
107
+ this.name = "DuplicateRoutePathError";
108
+ this.duplicates = duplicates;
109
+ }
110
+ };
122
111
  /**
123
- * `machineToGraph()` throws this error when an XState state node has a `.type` value
124
- * that the package does not know.
125
- *
126
- * **Error code:** `PLAY_ROUTE_UNKNOWN_STATE_TYPE`
127
- */
128
- export class UnknownStateTypeError extends PlayError {
129
- /** The string of the state type that the package does not know. */
130
- stateType;
131
- /** The ID of the state node with the unknown type. */
132
- nodeId;
133
- constructor(stateType, nodeId, validTypes) {
134
- super("machineToGraph", "PLAY_ROUTE_UNKNOWN_STATE_TYPE", `Unknown XState state type "${stateType}" on node "${nodeId}". ` +
135
- `Expected one of: ${validTypes.join(", ")}.`);
136
- this.name = "UnknownStateTypeError";
137
- this.stateType = stateType;
138
- this.nodeId = nodeId;
139
- }
140
- }
112
+ * `machineToGraph()` throws this error when an XState state node has a `.type` value
113
+ * that the package does not know.
114
+ *
115
+ * **Error code:** `PLAY_ROUTE_UNKNOWN_STATE_TYPE`
116
+ */
117
+ var UnknownStateTypeError = class extends PlayError {
118
+ /** The string of the state type that the package does not know. */
119
+ stateType;
120
+ /** The ID of the state node with the unknown type. */
121
+ nodeId;
122
+ constructor(stateType, nodeId, validTypes) {
123
+ super("machineToGraph", "PLAY_ROUTE_UNKNOWN_STATE_TYPE", `Unknown XState state type "${stateType}" on node "${nodeId}". Expected one of: ${validTypes.join(", ")}.`);
124
+ this.name = "UnknownStateTypeError";
125
+ this.stateType = stateType;
126
+ this.nodeId = nodeId;
127
+ }
128
+ };
129
+ //#endregion
130
+ export { DuplicateBridgeError, DuplicateRoutePathError, EmptyRoutePathError, InvalidBasePathError, InvalidRoutePatternError, InvalidStateIdError, MissingBasePathParamError, RouterSyncError, UnknownStateTypeError };
131
+
141
132
  //# sourceMappingURL=errors.js.map