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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +259 -335
  3. package/dist/base-route-map.d.ts +136 -0
  4. package/dist/base-route-map.d.ts.map +1 -0
  5. package/dist/base-route-map.js +182 -0
  6. package/dist/base-route-map.js.map +1 -0
  7. package/dist/build-tree.d.ts +7 -0
  8. package/dist/build-tree.d.ts.map +1 -1
  9. package/dist/build-tree.js +21 -6
  10. package/dist/build-tree.js.map +1 -1
  11. package/dist/create-route-map-from-tree.d.ts +33 -0
  12. package/dist/create-route-map-from-tree.d.ts.map +1 -0
  13. package/dist/create-route-map-from-tree.js +43 -0
  14. package/dist/create-route-map-from-tree.js.map +1 -0
  15. package/dist/create-route-map.d.ts +29 -33
  16. package/dist/create-route-map.d.ts.map +1 -1
  17. package/dist/create-route-map.js +25 -64
  18. package/dist/create-route-map.js.map +1 -1
  19. package/dist/errors.d.ts +152 -0
  20. package/dist/errors.d.ts.map +1 -0
  21. package/dist/errors.js +186 -0
  22. package/dist/errors.js.map +1 -0
  23. package/dist/extract-routes.d.ts +5 -31
  24. package/dist/extract-routes.d.ts.map +1 -1
  25. package/dist/extract-routes.js +73 -51
  26. package/dist/extract-routes.js.map +1 -1
  27. package/dist/find-route.d.ts +44 -0
  28. package/dist/find-route.d.ts.map +1 -0
  29. package/dist/find-route.js +103 -0
  30. package/dist/find-route.js.map +1 -0
  31. package/dist/index.d.ts +13 -51
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +13 -133
  34. package/dist/index.js.map +1 -1
  35. package/dist/machine-to-graph.d.ts +17 -0
  36. package/dist/machine-to-graph.d.ts.map +1 -0
  37. package/dist/machine-to-graph.js +115 -0
  38. package/dist/machine-to-graph.js.map +1 -0
  39. package/dist/query.d.ts +49 -1
  40. package/dist/query.d.ts.map +1 -1
  41. package/dist/query.js +98 -3
  42. package/dist/query.js.map +1 -1
  43. package/dist/router-bridge-base.d.ts +196 -32
  44. package/dist/router-bridge-base.d.ts.map +1 -1
  45. package/dist/router-bridge-base.js +319 -85
  46. package/dist/router-bridge-base.js.map +1 -1
  47. package/dist/router-sync.d.ts +95 -0
  48. package/dist/router-sync.d.ts.map +1 -0
  49. package/dist/router-sync.js +161 -0
  50. package/dist/router-sync.js.map +1 -0
  51. package/dist/types.d.ts +163 -22
  52. package/dist/types.d.ts.map +1 -1
  53. package/dist/url-pattern-utils.d.ts +117 -0
  54. package/dist/url-pattern-utils.d.ts.map +1 -0
  55. package/dist/url-pattern-utils.js +176 -0
  56. package/dist/url-pattern-utils.js.map +1 -0
  57. package/dist/validate-routes.d.ts +32 -14
  58. package/dist/validate-routes.d.ts.map +1 -1
  59. package/dist/validate-routes.js +28 -21
  60. package/dist/validate-routes.js.map +1 -1
  61. package/package.json +42 -14
  62. package/dist/connect-router.d.ts +0 -56
  63. package/dist/connect-router.d.ts.map +0 -1
  64. package/dist/connect-router.js +0 -119
  65. package/dist/connect-router.js.map +0 -1
  66. package/dist/crawl-machine.d.ts +0 -74
  67. package/dist/crawl-machine.d.ts.map +0 -1
  68. package/dist/crawl-machine.js +0 -95
  69. package/dist/crawl-machine.js.map +0 -1
  70. package/dist/create-browser-history.d.ts +0 -68
  71. package/dist/create-browser-history.d.ts.map +0 -1
  72. package/dist/create-browser-history.js +0 -94
  73. package/dist/create-browser-history.js.map +0 -1
  74. package/dist/create-router.d.ts +0 -73
  75. package/dist/create-router.d.ts.map +0 -1
  76. package/dist/create-router.js +0 -63
  77. package/dist/create-router.js.map +0 -1
  78. package/dist/extract-route.d.ts +0 -25
  79. package/dist/extract-route.d.ts.map +0 -1
  80. package/dist/extract-route.js +0 -63
  81. package/dist/extract-route.js.map +0 -1
@@ -0,0 +1,95 @@
1
+ import type { PlayRouteEvent } from "./types.js";
2
+ export interface RouteMatch {
3
+ to: string | null | undefined;
4
+ params?: Record<string, string>;
5
+ }
6
+ export interface BuildPlayRouteEventOptions {
7
+ pathname: string;
8
+ search?: string | undefined;
9
+ match: (sanitizedPathname: string) => RouteMatch;
10
+ }
11
+ export interface RouterStateIdMap {
12
+ getStateIdByPath(path: string): string | null | undefined;
13
+ getPathByStateId(id: string): string | null | undefined;
14
+ }
15
+ /**
16
+ * Match a sanitized pathname against a route map and extract any matched params.
17
+ *
18
+ * Shared by low-level and framework router sync paths to keep route-map matching
19
+ * semantics aligned.
20
+ */
21
+ export declare function matchRouteMap(pathname: string, routeMap: RouterStateIdMap, extractParams: (pathname: string, stateId: string) => Record<string, string>): RouteMatch;
22
+ /**
23
+ * Normalize a pathname before route-map lookup.
24
+ *
25
+ * This strips query/hash fragments, collapses duplicate slashes, and rejects
26
+ * implausibly long paths that should never enter route resolution.
27
+ *
28
+ * Returns `null` for paths exceeding 2048 characters (malformed/adversarial input).
29
+ * Adapters that bypass `RouterBridgeBase.syncActorFromRouter()` (e.g. `VueRouterBridge`)
30
+ * must call this function directly and return early on `null` to apply the same
31
+ * defense-in-depth security guarantee as the shared base class.
32
+ *
33
+ * @param pathname - Raw path string from router navigation event.
34
+ * @returns Normalized path string, or `null` if the path is malformed/too long.
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * import { sanitizePathname } from "@xmachines/play-router";
39
+ *
40
+ * const clean = sanitizePathname(to.path);
41
+ * if (clean === null) return; // reject malformed path
42
+ * ```
43
+ */
44
+ export declare function sanitizePathname(pathname: string): string | null;
45
+ /**
46
+ * Extract named path parameters from a URL using the URLPattern API.
47
+ *
48
+ * Takes the pattern string directly — use this when the pattern is already known.
49
+ * For extraction via a state ID lookup, see `RouterBridgeBase.extractParams`.
50
+ *
51
+ * Undefined values (unmatched optional segments, e.g. `/settings` against
52
+ * `/settings/:section?`) are omitted from the returned object.
53
+ *
54
+ * The pattern is normalized with the same rules `RouteMap` uses for matching
55
+ * (hyphenated names like `:cat-id` become `:cat_id` for URLPattern), and the
56
+ * extracted group names are mapped back to the original param names, so
57
+ * `/docs/123` against `/docs/:cat-id` yields `{ "cat-id": "123" }`.
58
+ *
59
+ * Values are percent-decoded (`john%20doe` → `john doe`), matching framework
60
+ * router semantics (e.g. vue-router). Malformed sequences are kept raw.
61
+ *
62
+ * @param pathname - The concrete URL pathname (e.g. `/profile/alice`)
63
+ * @param pattern - The URL pattern template (e.g. `/profile/:username`)
64
+ * @returns A record of extracted parameter values, or `{}` for static patterns.
65
+ * @throws {URLPatternUnavailableError} When `URLPattern` is absent and the pattern is parameterized.
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * import { extractRouteParams } from "@xmachines/play-router";
70
+ *
71
+ * extractRouteParams("/profile/alice", "/profile/:username")
72
+ * // → { username: "alice" }
73
+ *
74
+ * extractRouteParams("/settings", "/settings/:section?")
75
+ * // → {} (optional param absent — not included)
76
+ * ```
77
+ */
78
+ export declare function extractRouteParams(pathname: string, pattern: string): Record<string, string>;
79
+ /**
80
+ * Parse a URL search string into the plain object shape expected by
81
+ * `play.route` events.
82
+ */
83
+ export declare function extractQuery(search: string): Record<string, string>;
84
+ /**
85
+ * Build a normalized `play.route` event from raw router/browser input.
86
+ *
87
+ * Both `connectRouter()` (from `@xmachines/play-dom-router`) and `RouterBridgeBase`
88
+ * use this helper so low-level and framework adapters share the same pathname
89
+ * sanitization, route matching, and query extraction behavior.
90
+ */
91
+ export declare function buildPlayRouteEvent(options: BuildPlayRouteEventOptions): {
92
+ pathname: string;
93
+ event: PlayRouteEvent;
94
+ } | null;
95
+ //# sourceMappingURL=router-sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router-sync.d.ts","sourceRoot":"","sources":["../src/router-sync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAWjD,MAAM,WAAW,UAAU;IAC1B,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,KAAK,EAAE,CAAC,iBAAiB,EAAE,MAAM,KAAK,UAAU,CAAC;CACjD;AAED,MAAM,WAAW,gBAAgB;IAChC,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC1D,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CACxD;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC5B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,gBAAgB,EAC1B,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC1E,UAAU,CAUZ;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKhE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAmB5F;AAcD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAUnE;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAClC,OAAO,EAAE,0BAA0B,GACjC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,cAAc,CAAA;CAAE,GAAG,IAAI,CAqBpD"}
@@ -0,0 +1,161 @@
1
+ import { getURLPatternCtor, getNormalizedParamNameMap, getCompiledPattern, isParameterizedPattern, } from "./url-pattern-utils.js";
2
+ import { URLPatternUnavailableError } from "./errors.js";
3
+ /**
4
+ * Match a sanitized pathname against a route map and extract any matched params.
5
+ *
6
+ * Shared by low-level and framework router sync paths to keep route-map matching
7
+ * semantics aligned.
8
+ */
9
+ export function matchRouteMap(pathname, routeMap, extractParams) {
10
+ const stateId = routeMap.getStateIdByPath(pathname);
11
+ if (!stateId) {
12
+ return { to: null, params: {} };
13
+ }
14
+ return {
15
+ to: stateId,
16
+ params: extractParams(pathname, stateId),
17
+ };
18
+ }
19
+ /**
20
+ * Normalize a pathname before route-map lookup.
21
+ *
22
+ * This strips query/hash fragments, collapses duplicate slashes, and rejects
23
+ * implausibly long paths that should never enter route resolution.
24
+ *
25
+ * Returns `null` for paths exceeding 2048 characters (malformed/adversarial input).
26
+ * Adapters that bypass `RouterBridgeBase.syncActorFromRouter()` (e.g. `VueRouterBridge`)
27
+ * must call this function directly and return early on `null` to apply the same
28
+ * defense-in-depth security guarantee as the shared base class.
29
+ *
30
+ * @param pathname - Raw path string from router navigation event.
31
+ * @returns Normalized path string, or `null` if the path is malformed/too long.
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * import { sanitizePathname } from "@xmachines/play-router";
36
+ *
37
+ * const clean = sanitizePathname(to.path);
38
+ * if (clean === null) return; // reject malformed path
39
+ * ```
40
+ */
41
+ export function sanitizePathname(pathname) {
42
+ if (pathname.length > 2048)
43
+ return null;
44
+ const withoutQuery = pathname.split("?")[0];
45
+ const withoutHash = withoutQuery.split("#")[0];
46
+ return withoutHash.replace(/\/+/g, "/");
47
+ }
48
+ /**
49
+ * Extract named path parameters from a URL using the URLPattern API.
50
+ *
51
+ * Takes the pattern string directly — use this when the pattern is already known.
52
+ * For extraction via a state ID lookup, see `RouterBridgeBase.extractParams`.
53
+ *
54
+ * Undefined values (unmatched optional segments, e.g. `/settings` against
55
+ * `/settings/:section?`) are omitted from the returned object.
56
+ *
57
+ * The pattern is normalized with the same rules `RouteMap` uses for matching
58
+ * (hyphenated names like `:cat-id` become `:cat_id` for URLPattern), and the
59
+ * extracted group names are mapped back to the original param names, so
60
+ * `/docs/123` against `/docs/:cat-id` yields `{ "cat-id": "123" }`.
61
+ *
62
+ * Values are percent-decoded (`john%20doe` → `john doe`), matching framework
63
+ * router semantics (e.g. vue-router). Malformed sequences are kept raw.
64
+ *
65
+ * @param pathname - The concrete URL pathname (e.g. `/profile/alice`)
66
+ * @param pattern - The URL pattern template (e.g. `/profile/:username`)
67
+ * @returns A record of extracted parameter values, or `{}` for static patterns.
68
+ * @throws {URLPatternUnavailableError} When `URLPattern` is absent and the pattern is parameterized.
69
+ *
70
+ * @example
71
+ * ```typescript
72
+ * import { extractRouteParams } from "@xmachines/play-router";
73
+ *
74
+ * extractRouteParams("/profile/alice", "/profile/:username")
75
+ * // → { username: "alice" }
76
+ *
77
+ * extractRouteParams("/settings", "/settings/:section?")
78
+ * // → {} (optional param absent — not included)
79
+ * ```
80
+ */
81
+ export function extractRouteParams(pathname, pattern) {
82
+ const Ctor = getURLPatternCtor();
83
+ if (!Ctor)
84
+ throw new URLPatternUnavailableError();
85
+ if (!isParameterizedPattern(pattern))
86
+ return {};
87
+ // Compile with the SAME normalization RouteMap uses for matching — otherwise
88
+ // URLPattern parses ":cat-id" as group ":cat" + literal "-id" and extraction
89
+ // disagrees with the map that matched the path in the first place.
90
+ const urlPattern = getCompiledPattern(pattern, Ctor);
91
+ const match = urlPattern.exec({ pathname });
92
+ if (!match?.pathname.groups)
93
+ return {};
94
+ // Map normalized group names (":cat_id") back to the original param names ("cat-id").
95
+ const nameMap = getNormalizedParamNameMap(pattern);
96
+ const params = {};
97
+ for (const [key, value] of Object.entries(match.pathname.groups)) {
98
+ if (value !== undefined) {
99
+ params[nameMap.get(key) ?? key] = decodeParamValue(value); // nosemgrep: gitlab.eslint.detect-object-injection
100
+ }
101
+ }
102
+ return params;
103
+ }
104
+ /**
105
+ * Percent-decode a URLPattern group value, keeping the raw value when the
106
+ * sequence is malformed (`decodeURIComponent` would throw `URIError`).
107
+ */
108
+ function decodeParamValue(value) {
109
+ try {
110
+ return decodeURIComponent(value);
111
+ }
112
+ catch {
113
+ return value;
114
+ }
115
+ }
116
+ /**
117
+ * Parse a URL search string into the plain object shape expected by
118
+ * `play.route` events.
119
+ */
120
+ export function extractQuery(search) {
121
+ try {
122
+ const params = {};
123
+ new URLSearchParams(search).forEach((value, key) => {
124
+ params[key] = value; // nosemgrep: gitlab.eslint.detect-object-injection
125
+ });
126
+ return params;
127
+ }
128
+ catch {
129
+ return {};
130
+ }
131
+ }
132
+ /**
133
+ * Build a normalized `play.route` event from raw router/browser input.
134
+ *
135
+ * Both `connectRouter()` (from `@xmachines/play-dom-router`) and `RouterBridgeBase`
136
+ * use this helper so low-level and framework adapters share the same pathname
137
+ * sanitization, route matching, and query extraction behavior.
138
+ */
139
+ export function buildPlayRouteEvent(options) {
140
+ const pathname = sanitizePathname(options.pathname);
141
+ if (pathname === null)
142
+ return null;
143
+ const match = options.match(pathname);
144
+ if (!match.to)
145
+ return null;
146
+ // Single canonicalization point for the emitted event shape: `play.route`
147
+ // events always carry a '#'-prefixed target. Route maps may legitimately
148
+ // register (and return) bare stateIds — RouteMap preserves the registered
149
+ // form — so the event target is formatted here, and only here.
150
+ const to = match.to.startsWith("#") ? match.to : `#${match.to}`;
151
+ return {
152
+ pathname,
153
+ event: {
154
+ type: "play.route",
155
+ to,
156
+ params: match.params ?? {},
157
+ query: extractQuery(options.search ?? ""),
158
+ },
159
+ };
160
+ }
161
+ //# sourceMappingURL=router-sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router-sync.js","sourceRoot":"","sources":["../src/router-sync.ts"],"names":[],"mappings":"AACA,OAAO,EAGN,iBAAiB,EACjB,yBAAyB,EACzB,kBAAkB,EAClB,sBAAsB,GACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AAkBzD;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAC5B,QAAgB,EAChB,QAA0B,EAC1B,aAA4E;IAE5E,MAAM,OAAO,GAAG,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IACpD,IAAI,CAAC,OAAO,EAAE,CAAC;QACd,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACjC,CAAC;IAED,OAAO;QACN,EAAE,EAAE,OAAO;QACX,MAAM,EAAE,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC;KACxC,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAChD,IAAI,QAAQ,CAAC,MAAM,GAAG,IAAI;QAAE,OAAO,IAAI,CAAC;IACxC,MAAM,YAAY,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5C,MAAM,WAAW,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/C,OAAO,WAAW,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AACzC,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAAgB,EAAE,OAAe;IACnE,MAAM,IAAI,GAA+B,iBAAiB,EAAE,CAAC;IAC7D,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,0BAA0B,EAAE,CAAC;IAClD,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,CAAC;IAChD,6EAA6E;IAC7E,6EAA6E;IAC7E,mEAAmE;IACnE,MAAM,UAAU,GAAmB,kBAAkB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACrE,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC5C,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IACvC,sFAAsF;IACtF,MAAM,OAAO,GAAG,yBAAyB,CAAC,OAAO,CAAC,CAAC;IACnD,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAClE,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,mDAAmD;QAC/G,CAAC;IACF,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,SAAS,gBAAgB,CAAC,KAAa;IACtC,IAAI,CAAC;QACJ,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;AACF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,MAAc;IAC1C,IAAI,CAAC;QACJ,MAAM,MAAM,GAA2B,EAAE,CAAC;QAC1C,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;YAClD,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,mDAAmD;QACzE,CAAC,CAAC,CAAC;QACH,OAAO,MAAM,CAAC;IACf,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,EAAE,CAAC;IACX,CAAC;AACF,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAClC,OAAmC;IAEnC,MAAM,QAAQ,GAAG,gBAAgB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpD,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAEnC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACtC,IAAI,CAAC,KAAK,CAAC,EAAE;QAAE,OAAO,IAAI,CAAC;IAE3B,0EAA0E;IAC1E,yEAAyE;IACzE,0EAA0E;IAC1E,+DAA+D;IAC/D,MAAM,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,EAAE,EAAE,CAAC;IAChE,OAAO;QACN,QAAQ;QACR,KAAK,EAAE;YACN,IAAI,EAAE,YAAY;YAClB,EAAE;YACF,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,EAAE;YAC1B,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC;SACzC;KACD,CAAC;AACH,CAAC"}
package/dist/types.d.ts CHANGED
@@ -1,4 +1,30 @@
1
- import type { StateNode } from "xstate";
1
+ import type { Graph } from "@statelyai/graph";
2
+ import type { Signal } from "@xmachines/play-signals";
3
+ import type { PlaySpec } from "@xmachines/play-actor";
4
+ /**
5
+ * Data attached to each node in the machine graph representation.
6
+ * Captures the essential state metadata needed for route extraction and queries.
7
+ */
8
+ export interface MachineNodeData {
9
+ /** XState state ID (e.g., "test.dashboard.overview") */
10
+ stateId: string;
11
+ /** State type from XState */
12
+ type: "atomic" | "compound" | "parallel" | "final" | "history";
13
+ /** Original state meta object */
14
+ meta?: Record<string, unknown>;
15
+ /** Extracted route path from meta.route (string form) */
16
+ route?: string;
17
+ }
18
+ /**
19
+ * Data attached to each edge in the machine graph representation.
20
+ * Captures transition event and guard information.
21
+ */
22
+ export interface MachineEdgeData {
23
+ /** The event type that triggers this transition */
24
+ eventType: string;
25
+ /** String representation of guard (if any) */
26
+ guardType?: string;
27
+ }
2
28
  /**
3
29
  * Routing protocol type definitions for @xmachines/play-router
4
30
  *
@@ -18,17 +44,6 @@ export interface RouteObject {
18
44
  * Route metadata from state machine `meta.route` field.
19
45
  */
20
46
  export type RouteMetadata = string | RouteObject;
21
- /**
22
- * Represents a visit to a state node during graph crawling
23
- */
24
- export interface StateVisit {
25
- /** The state node being visited */
26
- node: StateNode;
27
- /** Path from root to this state (e.g., ['dashboard', 'overview']) */
28
- path: string[];
29
- /** Parent state node (null for root) */
30
- parent: StateNode | null;
31
- }
32
47
  /**
33
48
  * Extracted route information from a state node
34
49
  */
@@ -57,9 +72,16 @@ export interface RouteInfo {
57
72
  export interface RouteNode {
58
73
  /** Unique identifier (state ID) */
59
74
  id: string;
60
- /** Route path segment (relative or absolute) */
75
+ /**
76
+ * The raw route path segment as declared in `meta.route` — may be relative (e.g. `"overview"`)
77
+ * or absolute (e.g. `"/dashboard"`). Do not use this for URL matching; use `fullPath` instead.
78
+ */
61
79
  path: string;
62
- /** Full resolved path from root */
80
+ /**
81
+ * The fully resolved absolute path from the root (e.g. `"/dashboard/overview"`).
82
+ * Always use `fullPath` for browser URL matching and route map construction.
83
+ * `createRouteMapFromTree` and `createRouteMap` both use this field.
84
+ */
63
85
  fullPath: string;
64
86
  /** Route pattern with parameters (e.g., /profile/:userId) if path contains params */
65
87
  pattern?: string;
@@ -75,7 +97,7 @@ export interface RouteNode {
75
97
  /** Parent route (null for root) */
76
98
  parent: RouteNode | null;
77
99
  /** Original meta.route metadata */
78
- metadata: unknown;
100
+ metadata: RouteMetadata;
79
101
  }
80
102
  /**
81
103
  * Complete route tree with lookup maps
@@ -97,6 +119,18 @@ export interface RouteTree {
97
119
  * Used to look up state ID from URL path for play.route event targeting
98
120
  */
99
121
  byPath: Map<string, RouteNode>;
122
+ /**
123
+ * Graph representation of the state machine for advanced queries.
124
+ * Populated by extractMachineRoutes() — use for hierarchy queries, reachability checks,
125
+ * and transition-aware navigation via @statelyai/graph algorithms.
126
+ *
127
+ * @example
128
+ * ```typescript
129
+ * import { getSuccessors, hasPath } from "@statelyai/graph";
130
+ * const successors = getSuccessors(tree.graph!, "myMachine.home");
131
+ * ```
132
+ */
133
+ graph?: Graph<MachineNodeData, MachineEdgeData>;
100
134
  }
101
135
  /**
102
136
  * Enhanced routing event with parameter and query support
@@ -116,7 +150,7 @@ export interface RouteTree {
116
150
  *
117
151
  * @param type - Event discriminator (always "play.route")
118
152
  * @param to - Target state ID with # prefix (e.g., '#home', '#profile')
119
- * @param params - Merged path + query parameters (path parameters win conflicts)
153
+ * @param params - Path-only route parameters extracted from the URL path (e.g., `{ userId: '123' }` from `/profile/123`). Query parameters are kept separate in `query`.
120
154
  * @param query - Query parameters only (isolated from path params)
121
155
  * @param match - Full URLPattern match result for debugging/observability (optional)
122
156
  *
@@ -165,14 +199,14 @@ export interface RouteTree {
165
199
  * const event: PlayRouteEvent = {
166
200
  * type: 'play.route',
167
201
  * to: '#settings',
168
- * params: { section: 'profile' }, // Merged: path + query
202
+ * params: { section: 'profile' }, // Path-only route parameter
169
203
  * query: { tab: 'security' } // Query-only
170
204
  * };
171
205
  * actor.send(event);
172
206
  * // Resolves to route: /settings/profile?tab=security
173
207
  * ```
174
208
  *
175
- * @see {@link https://gitlab.com/xmachin-es/rfc/-/blob/main/src/play-v1.md | RFC Play v1}
209
+ * @see [Play RFC](../../docs/rfc/play.md)
176
210
  *
177
211
  * @remarks
178
212
  * Use `play.route` when you need parameter-aware navigation with the `route: {}`
@@ -187,10 +221,76 @@ export interface PlayRouteEvent {
187
221
  readonly match?: unknown;
188
222
  [key: string]: unknown;
189
223
  }
224
+ /**
225
+ * Minimal actor interface required by `RouterBridgeBase` and all framework router
226
+ * adapters.
227
+ *
228
+ * Using this interface instead of `AbstractActor<AnyActorLogic> & Routable` lets
229
+ * `RouterBridgeBase.actor.send` be typed to accept `PlayRouteEvent` directly —
230
+ * eliminating the unsafe `(actor.send as (e: PlayRouteEvent) => void)` cast that
231
+ * existed when the actor was typed with the weaker `EventObject` constraint.
232
+ *
233
+ * All `AbstractActor` subclasses satisfy this interface structurally because:
234
+ * - `AbstractActor` implements `send(event: TEvent): void` where `TEvent` accepts
235
+ * any `EventObject`, so `PlayRouteEvent` (a subtype) is always accepted.
236
+ * - `Routable` provides `currentRoute` and `initialRoute`.
237
+ *
238
+ * @example
239
+ * ```typescript
240
+ * import type { RoutableActor } from "@xmachines/play-router";
241
+ *
242
+ * class MyBridge extends RouterBridgeBase {
243
+ * constructor(actor: RoutableActor, routeMap: RouteMap) {
244
+ * super(actor, routeMap);
245
+ * }
246
+ * }
247
+ * ```
248
+ */
249
+ export interface RoutableActor {
250
+ /** TC39 Signal exposing the actor's current URL path (or state ID). */
251
+ readonly currentRoute: Signal.Computed<string | null>;
252
+ /**
253
+ * The route derived from the machine's initial state — fixed at construction.
254
+ * Router bridges compare this against the browser URL to distinguish a deep-link
255
+ * (router wins) from a session restore (actor wins).
256
+ */
257
+ readonly initialRoute: string | null;
258
+ /** Send a route navigation event to the actor. */
259
+ send(event: PlayRouteEvent): void;
260
+ }
261
+ /**
262
+ * Full actor shape used by `PlayRouterProvider` components across all framework
263
+ * adapters (`play-solid-router`, `play-vue-router`, `play-react-router`, and the
264
+ * adapters built on the shared framework-router bridge bases).
265
+ *
266
+ * Extends `RoutableActor` with `currentView` — the provider renders the current
267
+ * view spec in addition to synchronizing routes, so it needs both capabilities.
268
+ *
269
+ * - Use `RoutableActor` when only routing is needed (e.g. `RouterBridgeBase` subclasses,
270
+ * `connectRouter`).
271
+ * - Use `PlayActor` when the component also renders the current view spec
272
+ * (e.g. `PlayRouterProvider` renderer callback parameter, `PlayRenderer`).
273
+ *
274
+ * All `AbstractActor` subclasses that implement both `Routable` and `Viewable`
275
+ * satisfy this interface structurally.
276
+ *
277
+ * @example
278
+ * ```typescript
279
+ * import type { PlayActor } from "@xmachines/play-router";
280
+ *
281
+ * function MyRouterProvider({ actor }: { actor: PlayActor }) {
282
+ * // access actor.currentRoute (routing) and actor.currentView (rendering)
283
+ * }
284
+ * ```
285
+ */
286
+ export interface PlayActor extends RoutableActor {
287
+ /** TC39 Signal exposing the actor's current view spec, or `null` when inactive. */
288
+ readonly currentView: Signal.State<PlaySpec | null>;
289
+ }
190
290
  /**
191
291
  * RouterBridge interface for runtime infrastructure adapters
192
292
  *
193
- * Defines the lifecycle connection between Infrastructure (e.g., TanStack Router) and
293
+ * Defines the lifecycle connection between Infrastructure (e.g., a framework router) and
194
294
  * the Actor. Infrastructure "bridges" to the Actor by observing its signals and
195
295
  * managing its own lifecycle accordingly.
196
296
  *
@@ -199,12 +299,12 @@ export interface PlayRouteEvent {
199
299
  * (currentRoute, currentView, state) and reflects changes without making state decisions.
200
300
  *
201
301
  * @example
202
- * TanStack Router bridge implementation
302
+ * Framework router bridge implementation
203
303
  * ```typescript
204
304
  * import type { RouterBridge } from "@xmachines/play-router";
205
305
  * import { Signal } from "@xmachines/play-signals";
206
306
  *
207
- * class TanStackRouterBridge implements RouterBridge {
307
+ * class MyRouterBridge implements RouterBridge {
208
308
  * private watcher: Signal.Watcher | null = null;
209
309
  *
210
310
  * async connect(): Promise<void> {
@@ -224,7 +324,7 @@ export interface PlayRouteEvent {
224
324
  * }
225
325
  * ```
226
326
  *
227
- * @see {@link https://gitlab.com/xmachin-es/rfc/-/blob/main/src/play-v1.md | RFC Play v1 - Invariant INV-04}
327
+ * @see [Play RFC](../../docs/rfc/play.md) - Invariant INV-04
228
328
  */
229
329
  export interface RouterBridge {
230
330
  /**
@@ -259,4 +359,45 @@ export interface RouterBridge {
259
359
  */
260
360
  disconnect(): void | Promise<void>;
261
361
  }
362
+ /**
363
+ * Minimal window interface required by adapters that subscribe to DOM events
364
+ * (e.g. `hashchange`). Injectable for SSR and testing — pass a mock instead of
365
+ * the global `window` when the DOM is unavailable.
366
+ *
367
+ * Defined structurally (no `Window` reference) so this package compiles without
368
+ * the DOM lib.
369
+ *
370
+ * @example
371
+ * ```typescript
372
+ * // Normal usage — global window (default)
373
+ * connectRouter({ actor, routeMap });
374
+ *
375
+ * // SSR / test — injected mock
376
+ * const mockWin: WindowLike = { addEventListener: vi.fn(), removeEventListener: vi.fn() };
377
+ * connectRouter({ actor, routeMap, window: mockWin });
378
+ * ```
379
+ */
380
+ export interface WindowLike {
381
+ addEventListener(type: string, listener: (event: Event) => void): void;
382
+ removeEventListener(type: string, listener: (event: Event) => void): void;
383
+ }
384
+ /**
385
+ * Minimal location interface required by adapters that read the current URL at
386
+ * `connect()` time. Injectable for SSR and testing — pass a mock instead of the
387
+ * global `location` when the DOM is unavailable.
388
+ *
389
+ * Defined structurally (no `Location` reference) so this package compiles without
390
+ * the DOM lib.
391
+ *
392
+ * @example
393
+ * ```typescript
394
+ * // SSR / test — injected mock
395
+ * const mockLoc: LocationLike = { pathname: "/dashboard", search: "?tab=posts" };
396
+ * connectRouter({ actor, routeMap, location: mockLoc });
397
+ * ```
398
+ */
399
+ export interface LocationLike {
400
+ readonly pathname: string;
401
+ readonly search: string;
402
+ }
262
403
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAExC;;;;;GAKG;AAEH;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,WAAW,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,UAAU;IAC1B,mCAAmC;IACnC,IAAI,EAAE,SAAS,CAAC;IAChB,qEAAqE;IACrE,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,wCAAwC;IACxC,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACzB,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,0FAA0F;IAC1F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,UAAU,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,QAAQ,EAAE,OAAO,CAAC;IAClB,8BAA8B;IAC9B,QAAQ,EAAE,aAAa,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACzB,mCAAmC;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,qFAAqF;IACrF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,QAAQ,EAAE,OAAO,CAAC;IAClB,mBAAmB;IACnB,QAAQ,EAAE,SAAS,EAAE,CAAC;IACtB,mCAAmC;IACnC,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;IACzB,mCAAmC;IACnC,QAAQ,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,SAAS;IACzB,sBAAsB;IACtB,IAAI,EAAE,SAAS,CAAC;IAChB;;;OAGG;IACH,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAClC;;;OAGG;IACH,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgFG;AACH,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,MAAM,WAAW,YAAY;IAC5B;;;;;;;;;;;;;;OAcG;IACH,OAAO,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhC;;;;;;;;;;;;;OAaG;IACH,UAAU,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Shared URLPattern utilities — internal module, NOT exported from index.ts
3
+ *
4
+ * Provides the common URLPattern plumbing shared by:
5
+ * - `create-route-map.ts`
6
+ * - `base-route-map.ts`
7
+ * - `find-route.ts`
8
+ * - `router-bridge-base.ts`
9
+ *
10
+ * URLPattern is accessed via globalThis — consumers must load a polyfill on
11
+ * environments without native support (Node < 24, older browsers).
12
+ *
13
+ * @internal
14
+ */
15
+ /**
16
+ * Minimal structural type for the subset of URLPattern we use.
17
+ * Avoids a hard dependency on the full URLPattern type declaration.
18
+ */
19
+ export type URLPatternLike = {
20
+ exec(input: {
21
+ pathname: string;
22
+ }): {
23
+ pathname: {
24
+ groups: Record<string, string | undefined>;
25
+ };
26
+ } | null;
27
+ test(input: {
28
+ pathname: string;
29
+ }): boolean;
30
+ };
31
+ /** Constructor type for URLPatternLike instances. */
32
+ export type URLPatternCtor = new (init: {
33
+ pathname: string;
34
+ }) => URLPatternLike;
35
+ /**
36
+ * Retrieve the URLPattern constructor from `globalThis`, or `undefined` if
37
+ * it is not available (Node < 24, older browsers without a polyfill).
38
+ */
39
+ export declare function getURLPatternCtor(): URLPatternCtor | undefined;
40
+ /**
41
+ * Check whether a route path is a parameterized pattern rather than a concrete URL.
42
+ *
43
+ * A pattern contains `:param` segments (required or optional) and/or the `*`
44
+ * wildcard — both require URLPattern matching and carry no concrete values, so
45
+ * they can never be pushed to a browser URL verbatim.
46
+ *
47
+ * This is THE single source of truth for "is this path a pattern?" — used by
48
+ * `RouteMap` to decide static-map vs pattern-bucket indexing and by
49
+ * `RouterBridgeBase.resolveNavigationPath` to skip non-navigable routes, so the
50
+ * two checks can never drift apart.
51
+ *
52
+ * @param path - URL path or route pattern string (e.g. `/profile/:userId`, `/docs/*`)
53
+ * @returns `true` if the path contains `:param` or `*` pattern syntax
54
+ */
55
+ export declare function isParameterizedPattern(path: string): boolean;
56
+ /**
57
+ * Normalize a route path so its parameter names are valid URLPattern identifiers.
58
+ *
59
+ * URLPattern requires parameter names to be valid JS identifiers (no hyphens).
60
+ * Hyphenated names like `:cat-id` are replaced with underscored equivalents
61
+ * (`:cat_id`) for the URLPattern compilation step. Callers that need the captured
62
+ * group values (not just a match test) must map the normalized group names back to
63
+ * the original param names via {@link getNormalizedParamNameMap}.
64
+ *
65
+ * @example
66
+ * normalizeParamNames("/docs/:cat-id/:page-num?")
67
+ * // → "/docs/:cat_id/:page_num?"
68
+ */
69
+ export declare function normalizeParamNames(path: string): string;
70
+ /**
71
+ * Build a map from normalized URLPattern group names back to the original
72
+ * (hyphenated) param names for a route pattern.
73
+ *
74
+ * Only params rewritten by {@link normalizeParamNames} appear in the map —
75
+ * params that are already valid identifiers keep their own name and need no entry.
76
+ *
77
+ * @example
78
+ * getNormalizedParamNameMap("/docs/:cat-id/:plain")
79
+ * // → Map { "cat_id" → "cat-id" }
80
+ */
81
+ export declare function getNormalizedParamNameMap(path: string): Map<string, string>;
82
+ /**
83
+ * Compile a route pattern to a `URLPattern`, reusing the module-level cache.
84
+ *
85
+ * The pattern is normalized with {@link normalizeParamNames} before compilation
86
+ * so hyphenated param names (`:cat-id`) become valid URLPattern identifiers
87
+ * (`:cat_id`). Callers that extract group values must map the normalized names
88
+ * back via {@link getNormalizedParamNameMap}.
89
+ *
90
+ * Compilation errors from the URLPattern constructor propagate as-is — callers
91
+ * that need a typed error wrap this in `InvalidRoutePatternError`.
92
+ *
93
+ * @param pattern - Route pattern string (e.g. `/profile/:userId`)
94
+ * @param Ctor - URLPattern constructor retrieved via {@link getURLPatternCtor}
95
+ */
96
+ export declare function getCompiledPattern(pattern: string, Ctor: URLPatternCtor): URLPatternLike;
97
+ /**
98
+ * Compute the bucket index key for a given path or pattern.
99
+ *
100
+ * The key is the first path segment (e.g. `"settings"` for `"/settings/:id"`).
101
+ * Parameterised first segments (`:lang`) fall into the `"*"` wildcard bucket.
102
+ * The root path `"/"` maps to the key `"/"`.
103
+ *
104
+ * @param path - URL path or route pattern string
105
+ */
106
+ export declare function getIndexKey(path: string): string;
107
+ /**
108
+ * Merge the named-segment bucket and the wildcard bucket for a given index key,
109
+ * preserving insertion order (by `order` field).
110
+ *
111
+ * @param patternBuckets - Map of bucket key → ordered pattern entries
112
+ * @param indexKey - First-segment key computed by `getIndexKey`
113
+ */
114
+ export declare function getCandidates<T extends {
115
+ order: number;
116
+ }>(patternBuckets: Map<string, T[]>, indexKey: string): T[];
117
+ //# sourceMappingURL=url-pattern-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url-pattern-utils.d.ts","sourceRoot":"","sources":["../src/url-pattern-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG;IAC5B,IAAI,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG;QAClC,QAAQ,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;SAAE,CAAC;KACzD,GAAG,IAAI,CAAC;IACT,IAAI,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;CAC3C,CAAC;AAEF,qDAAqD;AACrD,MAAM,MAAM,cAAc,GAAG,KAAK,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,KAAK,cAAc,CAAC;AAEhF;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,cAAc,GAAG,SAAS,CAE9D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE5D;AASD;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIxD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAO3E;AAeD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,cAAc,CAWxF;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMhD;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,EACxD,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,EAChC,QAAQ,EAAE,MAAM,GACd,CAAC,EAAE,CA2BL"}