@xmachines/play-solid-router 1.0.0-beta.4 → 1.0.0-beta.40
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.
- package/LICENSE +21 -0
- package/README.md +93 -63
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/play-router-provider.d.ts +62 -11
- package/dist/play-router-provider.d.ts.map +1 -1
- package/dist/play-router-provider.jsx +28 -0
- package/dist/play-router-provider.jsx.map +1 -1
- package/dist/solid-router-bridge.d.ts +54 -12
- package/dist/solid-router-bridge.d.ts.map +1 -1
- package/dist/solid-router-bridge.js +72 -19
- package/dist/solid-router-bridge.js.map +1 -1
- package/dist/types.d.ts +0 -15
- package/dist/types.d.ts.map +1 -1
- package/package.json +22 -13
- package/dist/create-route-map.d.ts +0 -25
- package/dist/create-route-map.d.ts.map +0 -1
- package/dist/create-route-map.js +0 -36
- package/dist/create-route-map.js.map +0 -1
- package/dist/route-map.d.ts +0 -75
- package/dist/route-map.d.ts.map +0 -1
- package/dist/route-map.js +0 -107
- package/dist/route-map.js.map +0 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mikael Karon
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ SolidJS Router adapter using `RouterBridgeBase` for consistent actor↔router sy
|
|
|
8
8
|
|
|
9
9
|
`@xmachines/play-solid-router` provides seamless integration between SolidJS Router and XMachines state machines. Built on Solid's reactive primitives, it enables zero-adaptation signals synchronization.
|
|
10
10
|
|
|
11
|
-
Per [
|
|
11
|
+
Per [Play RFC](../docs/rfc/play.md), this package implements:
|
|
12
12
|
|
|
13
13
|
- **Actor Authority (INV-01):** State machine controls navigation, router reflects decisions
|
|
14
14
|
- **Passive Infrastructure (INV-04):** Router observes `actor.currentRoute` signal
|
|
@@ -36,12 +36,12 @@ npm install @solidjs/router@^0.13.0 solid-js@^1.8.0 @xmachines/play-solid-router
|
|
|
36
36
|
|
|
37
37
|
**Peer dependencies:**
|
|
38
38
|
|
|
39
|
-
- `@solidjs/router` ^0.13.0
|
|
40
|
-
- `solid-js` ^1.8.0
|
|
41
|
-
- `@xmachines/play-solid`
|
|
42
|
-
- `@xmachines/play-actor`
|
|
43
|
-
- `@xmachines/play-router`
|
|
44
|
-
- `@xmachines/play-signals`
|
|
39
|
+
- `@solidjs/router` ^0.13.0 — SolidJS Router library
|
|
40
|
+
- `solid-js` ^1.8.0 — SolidJS runtime
|
|
41
|
+
- `@xmachines/play-solid` — Solid renderer (`PlayRenderer`)
|
|
42
|
+
- `@xmachines/play-actor` — Actor base
|
|
43
|
+
- `@xmachines/play-router` — Route extraction
|
|
44
|
+
- `@xmachines/play-signals` — TC39 Signals primitives
|
|
45
45
|
|
|
46
46
|
## Quick Start
|
|
47
47
|
|
|
@@ -126,51 +126,57 @@ class SolidRouterBridge {
|
|
|
126
126
|
|
|
127
127
|
**Methods:**
|
|
128
128
|
|
|
129
|
-
- `connect()` - Start bidirectional synchronization.
|
|
129
|
+
- `connect()` - Start bidirectional synchronization. Both `location.pathname` and `location.search` are forwarded to the actor on first connect, so users arriving on `/profile?tab=posts` have `query: { tab: "posts" }` in the initial `play.route` event.
|
|
130
130
|
- `disconnect()` - Stop synchronization and cleanup bridge resources.
|
|
131
131
|
- `dispose()` - Alias of `disconnect()`.
|
|
132
132
|
|
|
133
|
+
`connect()` creates a Solid-owned router watcher, and `disconnect()`/`dispose()` tears it down immediately. Do not rely on owner unmount alone if you need the bridge to stop processing router updates earlier.
|
|
134
|
+
|
|
133
135
|
**Internal Behavior:**
|
|
134
136
|
|
|
135
137
|
- Uses `RouterBridgeBase` TC39 watcher lifecycle for actor→router synchronization
|
|
136
138
|
- Updates SolidJS Router via `navigate(path)` when actor state changes
|
|
137
139
|
- Uses `createEffect(on(...))` to watch `location.pathname` signal
|
|
138
|
-
- Sends `play.route` events to actor when user navigates
|
|
140
|
+
- Sends `play.route` events to actor when user navigates, using Solid's pre-parsed `useParams()` for path parameter extraction (no URLPattern polyfill required)
|
|
139
141
|
- Prevents circular updates with path tracking and processing flags
|
|
140
142
|
|
|
141
143
|
### `RouteMap`
|
|
142
144
|
|
|
143
145
|
Bidirectional mapping between XMachines state IDs and SolidJS Router paths with pattern matching support.
|
|
144
146
|
|
|
145
|
-
|
|
147
|
+
`RouteMap` extends `BaseRouteMap` from `@xmachines/play-router`, inheriting bucket-indexed
|
|
148
|
+
bidirectional route matching. No routing logic lives in the adapter itself.
|
|
146
149
|
|
|
147
150
|
```typescript
|
|
148
151
|
interface RouteMapping {
|
|
149
|
-
stateId: string;
|
|
150
|
-
path: string;
|
|
152
|
+
readonly stateId: string;
|
|
153
|
+
readonly path: string;
|
|
151
154
|
}
|
|
152
155
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
156
|
+
// RouteMap is a thin subclass of BaseRouteMap — no extra methods
|
|
157
|
+
class RouteMap extends BaseRouteMap {}
|
|
158
|
+
|
|
159
|
+
// Inherited API:
|
|
160
|
+
routeMap.getStateIdByPath(path: string): string | null
|
|
161
|
+
routeMap.getPathByStateId(stateId: string): string | null
|
|
158
162
|
```
|
|
159
163
|
|
|
164
|
+
`getStateIdByPath` and `getPathByStateId` both return `null` (not `undefined`) for misses.
|
|
165
|
+
|
|
160
166
|
**Constructor Parameters:**
|
|
161
167
|
|
|
162
|
-
- `mappings` - Array of
|
|
163
|
-
- `stateId`
|
|
164
|
-
- `path`
|
|
168
|
+
- `mappings` - Array of `{ stateId, path }` entries:
|
|
169
|
+
- `stateId` — State machine state ID (e.g., `'#profile'`)
|
|
170
|
+
- `path` — SolidJS Router path pattern (e.g., `'/profile/:userId'`)
|
|
165
171
|
|
|
166
172
|
**Methods:**
|
|
167
173
|
|
|
168
|
-
- `
|
|
169
|
-
- `getStateIdByPath(path)`
|
|
174
|
+
- `getPathByStateId(stateId)` — Find path pattern from state ID
|
|
175
|
+
- `getStateIdByPath(path)` — Find state ID from path with pattern matching (supports `:param` and `:param?` syntax)
|
|
170
176
|
|
|
171
177
|
**Pattern Matching:**
|
|
172
178
|
|
|
173
|
-
Uses
|
|
179
|
+
Uses bucket-indexed RegExp matching for dynamic routes:
|
|
174
180
|
|
|
175
181
|
```typescript
|
|
176
182
|
const routeMap = new RouteMap([{ stateId: "#settings", path: "/settings/:section?" }]);
|
|
@@ -178,6 +184,7 @@ const routeMap = new RouteMap([{ stateId: "#settings", path: "/settings/:section
|
|
|
178
184
|
routeMap.getStateIdByPath("/settings"); // '#settings'
|
|
179
185
|
routeMap.getStateIdByPath("/settings/account"); // '#settings'
|
|
180
186
|
routeMap.getStateIdByPath("/settings/privacy"); // '#settings'
|
|
187
|
+
routeMap.getStateIdByPath("/other"); // null
|
|
181
188
|
```
|
|
182
189
|
|
|
183
190
|
## Examples
|
|
@@ -186,8 +193,20 @@ routeMap.getStateIdByPath("/settings/privacy"); // '#settings'
|
|
|
186
193
|
|
|
187
194
|
```typescript
|
|
188
195
|
import { Router, Route } from '@solidjs/router';
|
|
189
|
-
import {
|
|
190
|
-
import {
|
|
196
|
+
import { defineCatalog } from "@json-render/core";
|
|
197
|
+
import { schema } from "@json-render/react/schema";
|
|
198
|
+
import { defineRegistry } from "@xmachines/play-solid";
|
|
199
|
+
import { z } from "zod";
|
|
200
|
+
|
|
201
|
+
// Catalog and registry
|
|
202
|
+
const appCatalog = defineCatalog(schema, {
|
|
203
|
+
components: {
|
|
204
|
+
Home: { props: z.object({}) },
|
|
205
|
+
About: { props: z.object({}) },
|
|
206
|
+
Contact: { props: z.object({}) },
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
const registry = defineRegistry(appCatalog, { components: { Home, About, Contact } });
|
|
191
210
|
|
|
192
211
|
// State machine with 3 states
|
|
193
212
|
const appMachine = setup({
|
|
@@ -210,12 +229,6 @@ const appMachine = setup({
|
|
|
210
229
|
}
|
|
211
230
|
});
|
|
212
231
|
|
|
213
|
-
const catalog = defineCatalog({
|
|
214
|
-
Home,
|
|
215
|
-
About,
|
|
216
|
-
Contact,
|
|
217
|
-
});
|
|
218
|
-
|
|
219
232
|
// Component setup
|
|
220
233
|
function App() {
|
|
221
234
|
const navigate = useNavigate();
|
|
@@ -224,7 +237,7 @@ function App() {
|
|
|
224
237
|
|
|
225
238
|
const routeMap = createRouteMap(appMachine);
|
|
226
239
|
|
|
227
|
-
const createPlayer = definePlayer({ machine: appMachine, catalog });
|
|
240
|
+
const createPlayer = definePlayer({ machine: appMachine, catalog: appCatalog });
|
|
228
241
|
const actor = createPlayer();
|
|
229
242
|
actor.start();
|
|
230
243
|
const bridge = new SolidRouterBridge(navigate, location, params, actor, routeMap);
|
|
@@ -246,7 +259,18 @@ function App() {
|
|
|
246
259
|
```typescript
|
|
247
260
|
// State machine with parameter routes
|
|
248
261
|
import { formatPlayRouteTransitions } from "@xmachines/play-xstate";
|
|
249
|
-
import { defineCatalog } from "@
|
|
262
|
+
import { defineCatalog } from "@json-render/core";
|
|
263
|
+
import { schema } from "@json-render/react/schema";
|
|
264
|
+
import { defineRegistry } from "@xmachines/play-solid";
|
|
265
|
+
import { z } from "zod";
|
|
266
|
+
|
|
267
|
+
const appCatalog = defineCatalog(schema, {
|
|
268
|
+
components: {
|
|
269
|
+
Profile: { props: z.object({ userId: z.string() }) },
|
|
270
|
+
Settings: { props: z.object({ section: z.string().optional() }) },
|
|
271
|
+
},
|
|
272
|
+
});
|
|
273
|
+
const registry = defineRegistry(appCatalog, { components: { Profile, Settings } });
|
|
250
274
|
|
|
251
275
|
const machineConfig = {
|
|
252
276
|
id: 'app',
|
|
@@ -274,11 +298,6 @@ const appMachine = setup({
|
|
|
274
298
|
}
|
|
275
299
|
}).createMachine(formatPlayRouteTransitions(machineConfig));
|
|
276
300
|
|
|
277
|
-
const catalog = defineCatalog({
|
|
278
|
-
Profile,
|
|
279
|
-
Settings,
|
|
280
|
-
});
|
|
281
|
-
|
|
282
301
|
// Router with dynamic routes
|
|
283
302
|
function App() {
|
|
284
303
|
const navigate = useNavigate();
|
|
@@ -287,7 +306,7 @@ function App() {
|
|
|
287
306
|
|
|
288
307
|
const routeMap = createRouteMap(appMachine);
|
|
289
308
|
|
|
290
|
-
const createPlayer = definePlayer({ machine: appMachine, catalog });
|
|
309
|
+
const createPlayer = definePlayer({ machine: appMachine, catalog: appCatalog });
|
|
291
310
|
const actor = createPlayer();
|
|
292
311
|
actor.start();
|
|
293
312
|
const bridge = new SolidRouterBridge(navigate, location, params, actor, routeMap);
|
|
@@ -328,7 +347,17 @@ function ProfileButton(props: { userId: string }) {
|
|
|
328
347
|
```typescript
|
|
329
348
|
// State machine with query param handling
|
|
330
349
|
import { formatPlayRouteTransitions } from "@xmachines/play-xstate";
|
|
331
|
-
import { defineCatalog } from "@
|
|
350
|
+
import { defineCatalog } from "@json-render/core";
|
|
351
|
+
import { schema } from "@json-render/react/schema";
|
|
352
|
+
import { defineRegistry } from "@xmachines/play-solid";
|
|
353
|
+
import { z } from "zod";
|
|
354
|
+
|
|
355
|
+
const searchCatalog = defineCatalog(schema, {
|
|
356
|
+
components: {
|
|
357
|
+
Search: { props: z.object({ query: z.string().optional() }) },
|
|
358
|
+
},
|
|
359
|
+
});
|
|
360
|
+
const registry = defineRegistry(searchCatalog, { components: { Search } });
|
|
332
361
|
|
|
333
362
|
const machineConfig = {
|
|
334
363
|
context: { query: '', filters: {} },
|
|
@@ -349,11 +378,7 @@ const searchMachine = setup({
|
|
|
349
378
|
}
|
|
350
379
|
}).createMachine(formatPlayRouteTransitions(machineConfig));
|
|
351
380
|
|
|
352
|
-
const
|
|
353
|
-
Search,
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
const player = definePlayer({ machine: searchMachine, catalog });
|
|
381
|
+
const player = definePlayer({ machine: searchMachine, catalog: searchCatalog });
|
|
357
382
|
|
|
358
383
|
// Component sends query params
|
|
359
384
|
function SearchBar(props) {
|
|
@@ -387,7 +412,19 @@ function SearchBar(props) {
|
|
|
387
412
|
|
|
388
413
|
```typescript
|
|
389
414
|
// State machine with auth guards
|
|
390
|
-
import { defineCatalog } from "@
|
|
415
|
+
import { defineCatalog } from "@json-render/core";
|
|
416
|
+
import { schema } from "@json-render/react/schema";
|
|
417
|
+
import { defineRegistry } from "@xmachines/play-solid";
|
|
418
|
+
import { z } from "zod";
|
|
419
|
+
|
|
420
|
+
const authCatalog = defineCatalog(schema, {
|
|
421
|
+
components: {
|
|
422
|
+
Home: { props: z.object({}) },
|
|
423
|
+
Login: { props: z.object({ title: z.string() }) },
|
|
424
|
+
Dashboard: { props: z.object({ username: z.string() }) },
|
|
425
|
+
},
|
|
426
|
+
});
|
|
427
|
+
const registry = defineRegistry(authCatalog, { components: { Home, Login, Dashboard } });
|
|
391
428
|
|
|
392
429
|
const authMachine = setup({
|
|
393
430
|
types: {
|
|
@@ -420,13 +457,7 @@ const authMachine = setup({
|
|
|
420
457
|
},
|
|
421
458
|
});
|
|
422
459
|
|
|
423
|
-
const
|
|
424
|
-
Home,
|
|
425
|
-
Login,
|
|
426
|
-
Dashboard,
|
|
427
|
-
});
|
|
428
|
-
|
|
429
|
-
const player = definePlayer({ machine: authMachine, catalog });
|
|
460
|
+
const player = definePlayer({ machine: authMachine, catalog: authCatalog });
|
|
430
461
|
```
|
|
431
462
|
|
|
432
463
|
**Guard behavior:**
|
|
@@ -479,7 +510,7 @@ function App() {
|
|
|
479
510
|
2. `actor.currentRoute` signal updates
|
|
480
511
|
3. `createEffect(on(...))` fires with new route value
|
|
481
512
|
4. Bridge extracts state ID from signal
|
|
482
|
-
5. Bridge looks up path via `routeMap.
|
|
513
|
+
5. Bridge looks up path via `routeMap.getPathByStateId(stateId)`
|
|
483
514
|
6. Bridge calls `navigate(path)`
|
|
484
515
|
7. SolidJS Router updates URL and renders component
|
|
485
516
|
|
|
@@ -603,15 +634,11 @@ function App() {
|
|
|
603
634
|
|
|
604
635
|
### Pattern Matching for Dynamic Routes
|
|
605
636
|
|
|
606
|
-
**
|
|
637
|
+
**Bucket-indexed matching via `BaseRouteMap`:**
|
|
607
638
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
const urlPattern = new URLPattern({ pathname: pattern });
|
|
612
|
-
return urlPattern.test({ pathname: path });
|
|
613
|
-
}
|
|
614
|
-
```
|
|
639
|
+
Routes are grouped by their first path segment into buckets. On each lookup only the
|
|
640
|
+
relevant bucket (plus the wildcard `*` bucket for `:param`-first routes) is scanned —
|
|
641
|
+
typically far fewer than all registered routes.
|
|
615
642
|
|
|
616
643
|
**Supported syntax:**
|
|
617
644
|
|
|
@@ -634,4 +661,7 @@ routeMap.getStateIdByPath("/settings/privacy"); // '#settings'
|
|
|
634
661
|
|
|
635
662
|
## License
|
|
636
663
|
|
|
637
|
-
|
|
664
|
+
Copyright (c) 2016 [Mikael Karon](mailto:mikael@karon.se). All rights reserved.
|
|
665
|
+
|
|
666
|
+
This work is licensed under the terms of the MIT license.
|
|
667
|
+
For a copy, see <https://opensource.org/licenses/MIT>.
|
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
export { SolidRouterBridge } from "./solid-router-bridge.js";
|
|
7
7
|
export { PlayRouterProvider } from "./play-router-provider.js";
|
|
8
8
|
export type { PlayRouterProviderProps, RoutableActor, SolidRouterHooks, } from "./play-router-provider.js";
|
|
9
|
-
export { RouteMap } from "
|
|
10
|
-
export {
|
|
11
|
-
export type {
|
|
9
|
+
export { RouteMap, createRouteMapFromMachine as createRouteMap, type RouteMapping, } from "@xmachines/play-router";
|
|
10
|
+
export type { PlayRouteEvent, RouterBridge } from "./types.js";
|
|
11
|
+
export type { AbstractActor } from "@xmachines/play-actor";
|
|
12
12
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,YAAY,EACX,uBAAuB,EACvB,aAAa,EACb,gBAAgB,GAChB,MAAM,2BAA2B,CAAC;AACnC,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,YAAY,EACX,uBAAuB,EACvB,aAAa,EACb,gBAAgB,GAChB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACN,QAAQ,EACR,yBAAyB,IAAI,cAAc,EAC3C,KAAK,YAAY,GACjB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/D,YAAY,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,5 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export { SolidRouterBridge } from "./solid-router-bridge.js";
|
|
7
7
|
export { PlayRouterProvider } from "./play-router-provider.js";
|
|
8
|
-
export { RouteMap } from "
|
|
9
|
-
export { createRouteMap } from "./create-route-map.js";
|
|
8
|
+
export { RouteMap, createRouteMapFromMachine as createRouteMap, } from "@xmachines/play-router";
|
|
10
9
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAM/D,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAM/D,OAAO,EACN,QAAQ,EACR,yBAAyB,IAAI,cAAc,GAE3C,MAAM,wBAAwB,CAAC"}
|
|
@@ -1,21 +1,72 @@
|
|
|
1
1
|
import { type JSX } from "solid-js";
|
|
2
|
+
import type { Navigator, Location, Params } from "@solidjs/router";
|
|
2
3
|
import type { AbstractActor, Routable, Viewable } from "@xmachines/play-actor";
|
|
3
4
|
import type { AnyActorLogic } from "xstate";
|
|
4
|
-
import type { RouteMap } from "
|
|
5
|
+
import type { RouteMap } from "@xmachines/play-router";
|
|
6
|
+
/** Minimum actor shape accepted by PlayRouterProvider. */
|
|
5
7
|
export type RoutableActor = AbstractActor<AnyActorLogic> & Routable & Viewable;
|
|
8
|
+
/**
|
|
9
|
+
* The three Solid Router hook results that `PlayRouterProvider` and `SolidRouterBridge`
|
|
10
|
+
* require. Pass these directly from your component's hook calls:
|
|
11
|
+
*
|
|
12
|
+
* ```tsx
|
|
13
|
+
* const navigate = useNavigate(); // → SolidRouterHooks.navigate
|
|
14
|
+
* const location = useLocation(); // → SolidRouterHooks.location
|
|
15
|
+
* const params = useParams(); // → SolidRouterHooks.params
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* - `navigate` — used to push URL changes when the actor's `currentRoute` changes.
|
|
19
|
+
* - `location` — `pathname` and `search` are read at `connect()` time for deep-link sync.
|
|
20
|
+
* Subsequent pathname changes drive router→actor sync via `createEffect`.
|
|
21
|
+
* - `params` — Solid's pre-parsed path parameters for the current route segment. Used
|
|
22
|
+
* directly in `extractParams()` to avoid re-parsing with URLPattern.
|
|
23
|
+
*/
|
|
6
24
|
export type SolidRouterHooks = {
|
|
7
|
-
navigate:
|
|
8
|
-
location:
|
|
9
|
-
|
|
10
|
-
search: string;
|
|
11
|
-
};
|
|
12
|
-
params: Record<string, string | undefined>;
|
|
25
|
+
navigate: Navigator;
|
|
26
|
+
location: Location;
|
|
27
|
+
params: Params;
|
|
13
28
|
};
|
|
14
|
-
export interface PlayRouterProviderProps {
|
|
15
|
-
actor
|
|
29
|
+
export interface PlayRouterProviderProps<TActor extends RoutableActor = RoutableActor> {
|
|
30
|
+
/** The actor to sync with Solid Router. */
|
|
31
|
+
actor: TActor;
|
|
32
|
+
/** Bidirectional route map for state ID ↔ URL path lookups. */
|
|
16
33
|
routeMap: RouteMap;
|
|
34
|
+
/**
|
|
35
|
+
* The three Solid Router hook results that drive bidirectional sync.
|
|
36
|
+
* Obtain these from `useNavigate()`, `useLocation()`, and `useParams()`
|
|
37
|
+
* in the parent component (they must be called inside a router context).
|
|
38
|
+
*/
|
|
17
39
|
router: SolidRouterHooks;
|
|
18
|
-
|
|
40
|
+
/** Renderer callback receives the same concrete actor type that was passed in. */
|
|
41
|
+
renderer: (actor: TActor, router: SolidRouterHooks) => JSX.Element;
|
|
19
42
|
}
|
|
20
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Connects a `PlayerActor` to Solid Router, keeping actor state and browser URL
|
|
45
|
+
* in sync bidirectionally.
|
|
46
|
+
*
|
|
47
|
+
* The bridge is created synchronously at component evaluation time (Solid's
|
|
48
|
+
* execution model) and torn down via `onCleanup` when the component is disposed.
|
|
49
|
+
* Unlike React, prop stability is not a concern — Solid's `props` accessor is
|
|
50
|
+
* already reactive and the bridge is created once per component instance.
|
|
51
|
+
*
|
|
52
|
+
* The `router` prop must be obtained from Solid Router hooks in the parent component:
|
|
53
|
+
*
|
|
54
|
+
* ```tsx
|
|
55
|
+
* function AppShell() {
|
|
56
|
+
* const navigate = useNavigate();
|
|
57
|
+
* const location = useLocation();
|
|
58
|
+
* const params = useParams();
|
|
59
|
+
*
|
|
60
|
+
* return (
|
|
61
|
+
* <PlayRouterProvider
|
|
62
|
+
* actor={actor}
|
|
63
|
+
* routeMap={routeMap}
|
|
64
|
+
* router={{ navigate, location, params }}
|
|
65
|
+
* renderer={(a) => <PlayRenderer actor={a} registry={registry} />}
|
|
66
|
+
* />
|
|
67
|
+
* );
|
|
68
|
+
* }
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export declare function PlayRouterProvider<TActor extends RoutableActor>(props: PlayRouterProviderProps<TActor>): any;
|
|
21
72
|
//# sourceMappingURL=play-router-provider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"play-router-provider.d.ts","sourceRoot":"","sources":["../src/play-router-provider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAC/E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAE5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"play-router-provider.d.ts","sourceRoot":"","sources":["../src/play-router-provider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAC/E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAE5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAEvD,0DAA0D;AAC1D,MAAM,MAAM,aAAa,GAAG,aAAa,CAAC,aAAa,CAAC,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE/E;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC9B,QAAQ,EAAE,SAAS,CAAC;IACpB,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,WAAW,uBAAuB,CAAC,MAAM,SAAS,aAAa,GAAG,aAAa;IACpF,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,QAAQ,EAAE,QAAQ,CAAC;IACnB;;;;OAIG;IACH,MAAM,EAAE,gBAAgB,CAAC;IACzB,kFAAkF;IAClF,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,KAAK,GAAG,CAAC,OAAO,CAAC;CACnE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,SAAS,aAAa,EAC9D,KAAK,EAAE,uBAAuB,CAAC,MAAM,CAAC,OAgBtC"}
|
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
import { onCleanup } from "solid-js";
|
|
2
2
|
import { SolidRouterBridge } from "./solid-router-bridge.js";
|
|
3
|
+
/**
|
|
4
|
+
* Connects a `PlayerActor` to Solid Router, keeping actor state and browser URL
|
|
5
|
+
* in sync bidirectionally.
|
|
6
|
+
*
|
|
7
|
+
* The bridge is created synchronously at component evaluation time (Solid's
|
|
8
|
+
* execution model) and torn down via `onCleanup` when the component is disposed.
|
|
9
|
+
* Unlike React, prop stability is not a concern — Solid's `props` accessor is
|
|
10
|
+
* already reactive and the bridge is created once per component instance.
|
|
11
|
+
*
|
|
12
|
+
* The `router` prop must be obtained from Solid Router hooks in the parent component:
|
|
13
|
+
*
|
|
14
|
+
* ```tsx
|
|
15
|
+
* function AppShell() {
|
|
16
|
+
* const navigate = useNavigate();
|
|
17
|
+
* const location = useLocation();
|
|
18
|
+
* const params = useParams();
|
|
19
|
+
*
|
|
20
|
+
* return (
|
|
21
|
+
* <PlayRouterProvider
|
|
22
|
+
* actor={actor}
|
|
23
|
+
* routeMap={routeMap}
|
|
24
|
+
* router={{ navigate, location, params }}
|
|
25
|
+
* renderer={(a) => <PlayRenderer actor={a} registry={registry} />}
|
|
26
|
+
* />
|
|
27
|
+
* );
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
3
31
|
export function PlayRouterProvider(props) {
|
|
4
32
|
const bridge = new SolidRouterBridge(props.router.navigate, props.router.location, props.router.params, props.actor, props.routeMap);
|
|
5
33
|
bridge.connect();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"play-router-provider.jsx","sourceRoot":"","sources":["../src/play-router-provider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAY,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"play-router-provider.jsx","sourceRoot":"","sources":["../src/play-router-provider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAY,MAAM,UAAU,CAAC;AAI/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AA2C7D;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,UAAU,kBAAkB,CACjC,KAAsC;IAEtC,MAAM,MAAM,GAAG,IAAI,iBAAiB,CACnC,KAAK,CAAC,MAAM,CAAC,QAAQ,EACrB,KAAK,CAAC,MAAM,CAAC,QAAQ,EACrB,KAAK,CAAC,MAAM,CAAC,MAAM,EACnB,KAAK,CAAC,KAAK,EACX,KAAK,CAAC,QAAQ,CACd,CAAC;IACF,MAAM,CAAC,OAAO,EAAE,CAAC;IAEjB,SAAS,CAAC,GAAG,EAAE;QACd,MAAM,CAAC,UAAU,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,OAAO,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC;AACzD,CAAC"}
|
|
@@ -5,8 +5,10 @@
|
|
|
5
5
|
* Uses Solid's native reactive primitives (createEffect) for router→actor direction.
|
|
6
6
|
*
|
|
7
7
|
* **IMPORTANT:** `connect()` MUST be called inside a Solid reactive owner (component
|
|
8
|
-
* or createRoot). The createEffect() in watchRouterChanges()
|
|
9
|
-
*
|
|
8
|
+
* or createRoot). The `createEffect()` in `watchRouterChanges()` runs inside
|
|
9
|
+
* `createRoot()`, which deliberately isolates it from any parent owner — automatic
|
|
10
|
+
* cleanup on component unmount does NOT happen. You MUST call `disconnect()` (or
|
|
11
|
+
* `dispose()`) explicitly, typically in `onCleanup()`.
|
|
10
12
|
*
|
|
11
13
|
* @example
|
|
12
14
|
* ```tsx
|
|
@@ -30,20 +32,32 @@
|
|
|
30
32
|
* }
|
|
31
33
|
* ```
|
|
32
34
|
*/
|
|
35
|
+
import type { Navigator, Params } from "@solidjs/router";
|
|
33
36
|
import { RouterBridgeBase } from "@xmachines/play-router";
|
|
37
|
+
import type { LocationLike } from "@xmachines/play-router";
|
|
34
38
|
import type { AbstractActor, Routable } from "@xmachines/play-actor";
|
|
35
39
|
import type { AnyActorLogic } from "xstate";
|
|
36
|
-
import type { RouteMap } from "
|
|
40
|
+
import type { RouteMap } from "@xmachines/play-router";
|
|
37
41
|
/**
|
|
38
42
|
* SolidJS Router integration bridge extending RouterBridgeBase
|
|
39
43
|
*
|
|
40
44
|
* Implements RouterBridge protocol for SolidJS Router using Solid's reactive
|
|
41
45
|
* primitives. The actor→router direction uses TC39 Signal watcher (from base class).
|
|
42
46
|
* The router→actor direction uses Solid's createEffect for native reactivity.
|
|
47
|
+
*
|
|
48
|
+
* Path parameters are extracted from Solid's `useParams()` reactive proxy rather than
|
|
49
|
+
* re-parsing the URL with URLPattern. This means parameterized routes work without the
|
|
50
|
+
* URLPattern polyfill — Solid's router has already extracted the values.
|
|
43
51
|
*/
|
|
44
52
|
export declare class SolidRouterBridge extends RouterBridgeBase {
|
|
45
53
|
private readonly solidNavigate;
|
|
46
54
|
private readonly location;
|
|
55
|
+
private disposeRouterWatcher;
|
|
56
|
+
/**
|
|
57
|
+
* Live reactive params object from Solid's `useParams()`.
|
|
58
|
+
* Read inside the createEffect callback so it always reflects the current route.
|
|
59
|
+
*/
|
|
60
|
+
private readonly solidParams;
|
|
47
61
|
/**
|
|
48
62
|
* Create a SolidJS Router bridge
|
|
49
63
|
*
|
|
@@ -51,14 +65,28 @@ export declare class SolidRouterBridge extends RouterBridgeBase {
|
|
|
51
65
|
*
|
|
52
66
|
* @param solidNavigate - Result of useNavigate() hook
|
|
53
67
|
* @param location - Result of useLocation() hook
|
|
54
|
-
* @param
|
|
68
|
+
* @param params - Result of useParams() hook — used directly for path parameter extraction,
|
|
69
|
+
* avoiding the URLPattern polyfill requirement for parameterized routes
|
|
55
70
|
* @param actor - XMachines actor instance
|
|
56
71
|
* @param routeMap - Bidirectional state ID ↔ path mapping
|
|
57
72
|
*/
|
|
58
|
-
constructor(solidNavigate:
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
73
|
+
constructor(solidNavigate: Navigator, location: LocationLike, params: Params, actor: AbstractActor<AnyActorLogic> & Routable, routeMap: RouteMap);
|
|
74
|
+
/**
|
|
75
|
+
* Extract path parameters using Solid's pre-parsed `useParams()` values.
|
|
76
|
+
*
|
|
77
|
+
* Solid's router has already extracted all named parameters for the matched route
|
|
78
|
+
* segment. Reading `this.solidParams` inside the createEffect callback that drives
|
|
79
|
+
* `syncActorFromRouter` is safe — the reactive proxy always reflects the current
|
|
80
|
+
* route at the time the effect runs.
|
|
81
|
+
*
|
|
82
|
+
* Falls back to URLPattern-based extraction (base class) only when Solid provided
|
|
83
|
+
* no params for this route (i.e. the route has no `:param` segments).
|
|
84
|
+
*
|
|
85
|
+
* @param pathname - The actual URL path (unused — params already extracted by Solid)
|
|
86
|
+
* @param stateId - The matched state ID (unused — params already extracted by Solid)
|
|
87
|
+
* @returns Normalized path parameters with undefined/empty values filtered out
|
|
88
|
+
*/
|
|
89
|
+
protected extractParams(pathname: string, stateId: string): Record<string, string>;
|
|
62
90
|
/**
|
|
63
91
|
* Navigate SolidJS Router to the given path.
|
|
64
92
|
*/
|
|
@@ -67,18 +95,32 @@ export declare class SolidRouterBridge extends RouterBridgeBase {
|
|
|
67
95
|
* Get the current router pathname for initial URL -> actor sync on connect.
|
|
68
96
|
*/
|
|
69
97
|
protected getInitialRouterPath(): string | null;
|
|
98
|
+
/**
|
|
99
|
+
* Return the initial URL search string for query-param forwarding on `connect()`.
|
|
100
|
+
*
|
|
101
|
+
* Reads `this.location.search` from Solid's `useLocation()` reactive object —
|
|
102
|
+
* the same source used by `getInitialRouterPath()`. An empty string (no query
|
|
103
|
+
* params) returns `undefined` so `syncActorFromRouter` produces `query: {}`.
|
|
104
|
+
*/
|
|
105
|
+
protected getInitialRouterSearch(): string | undefined;
|
|
70
106
|
/**
|
|
71
107
|
* Subscribe to SolidJS Router location changes using createEffect.
|
|
72
108
|
*
|
|
73
|
-
* MUST be called inside a Solid reactive owner (component
|
|
74
|
-
*
|
|
109
|
+
* MUST be called inside a Solid reactive owner (component or createRoot).
|
|
110
|
+
*
|
|
111
|
+
* The effect runs inside `createRoot()` to give it a stable owner independent
|
|
112
|
+
* of the calling component's lifecycle — this prevents the effect from being
|
|
113
|
+
* disposed if the component re-renders while the bridge should stay active.
|
|
114
|
+
* The trade-off is that component unmount does NOT automatically clean up the
|
|
115
|
+
* effect; `disconnect()` (or `dispose()`) MUST be called explicitly to avoid a leak.
|
|
75
116
|
*/
|
|
76
117
|
protected watchRouterChanges(): void;
|
|
77
118
|
/**
|
|
78
119
|
* Stop watching SolidJS Router changes.
|
|
79
120
|
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
121
|
+
* Calls the `dispose` function returned by `createRoot()` in `watchRouterChanges()`,
|
|
122
|
+
* tearing down the reactive effect and freeing the isolated owner. This is the only
|
|
123
|
+
* cleanup path — component unmount does NOT trigger this automatically.
|
|
82
124
|
*/
|
|
83
125
|
protected unwatchRouterChanges(): void;
|
|
84
126
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"solid-router-bridge.d.ts","sourceRoot":"","sources":["../src/solid-router-bridge.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"solid-router-bridge.d.ts","sourceRoot":"","sources":["../src/solid-router-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAEvD;;;;;;;;;;GAUG;AACH,qBAAa,iBAAkB,SAAQ,gBAAgB;IAsBrD,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAtB1B,OAAO,CAAC,oBAAoB,CAA6B;IAEzD;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IAErC;;;;;;;;;;;OAWG;gBAEe,aAAa,EAAE,SAAS,EACxB,QAAQ,EAAE,YAAY,EACvC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,aAAa,CAAC,aAAa,CAAC,GAAG,QAAQ,EAC9C,QAAQ,EAAE,QAAQ;IASnB;;;;;;;;;;;;;;OAcG;cACgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAY3F;;OAEG;IACH,SAAS,CAAC,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI5C;;OAEG;cACgB,oBAAoB,IAAI,MAAM,GAAG,IAAI;IAIxD;;;;;;OAMG;cACgB,sBAAsB,IAAI,MAAM,GAAG,SAAS;IAI/D;;;;;;;;;;OAUG;IACH,SAAS,CAAC,kBAAkB,IAAI,IAAI;IAepC;;;;;;OAMG;IACH,SAAS,CAAC,oBAAoB,IAAI,IAAI;IAKtC;;;;;;;OAOG;IACH,OAAO,IAAI,IAAI;CAGf"}
|
|
@@ -5,8 +5,10 @@
|
|
|
5
5
|
* Uses Solid's native reactive primitives (createEffect) for router→actor direction.
|
|
6
6
|
*
|
|
7
7
|
* **IMPORTANT:** `connect()` MUST be called inside a Solid reactive owner (component
|
|
8
|
-
* or createRoot). The createEffect() in watchRouterChanges()
|
|
9
|
-
*
|
|
8
|
+
* or createRoot). The `createEffect()` in `watchRouterChanges()` runs inside
|
|
9
|
+
* `createRoot()`, which deliberately isolates it from any parent owner — automatic
|
|
10
|
+
* cleanup on component unmount does NOT happen. You MUST call `disconnect()` (or
|
|
11
|
+
* `dispose()`) explicitly, typically in `onCleanup()`.
|
|
10
12
|
*
|
|
11
13
|
* @example
|
|
12
14
|
* ```tsx
|
|
@@ -30,7 +32,7 @@
|
|
|
30
32
|
* }
|
|
31
33
|
* ```
|
|
32
34
|
*/
|
|
33
|
-
import { createEffect, on } from "solid-js";
|
|
35
|
+
import { createEffect, createRoot, on } from "solid-js";
|
|
34
36
|
import { RouterBridgeBase } from "@xmachines/play-router";
|
|
35
37
|
/**
|
|
36
38
|
* SolidJS Router integration bridge extending RouterBridgeBase
|
|
@@ -38,10 +40,20 @@ import { RouterBridgeBase } from "@xmachines/play-router";
|
|
|
38
40
|
* Implements RouterBridge protocol for SolidJS Router using Solid's reactive
|
|
39
41
|
* primitives. The actor→router direction uses TC39 Signal watcher (from base class).
|
|
40
42
|
* The router→actor direction uses Solid's createEffect for native reactivity.
|
|
43
|
+
*
|
|
44
|
+
* Path parameters are extracted from Solid's `useParams()` reactive proxy rather than
|
|
45
|
+
* re-parsing the URL with URLPattern. This means parameterized routes work without the
|
|
46
|
+
* URLPattern polyfill — Solid's router has already extracted the values.
|
|
41
47
|
*/
|
|
42
48
|
export class SolidRouterBridge extends RouterBridgeBase {
|
|
43
49
|
solidNavigate;
|
|
44
50
|
location;
|
|
51
|
+
disposeRouterWatcher = null;
|
|
52
|
+
/**
|
|
53
|
+
* Live reactive params object from Solid's `useParams()`.
|
|
54
|
+
* Read inside the createEffect callback so it always reflects the current route.
|
|
55
|
+
*/
|
|
56
|
+
solidParams;
|
|
45
57
|
/**
|
|
46
58
|
* Create a SolidJS Router bridge
|
|
47
59
|
*
|
|
@@ -49,17 +61,42 @@ export class SolidRouterBridge extends RouterBridgeBase {
|
|
|
49
61
|
*
|
|
50
62
|
* @param solidNavigate - Result of useNavigate() hook
|
|
51
63
|
* @param location - Result of useLocation() hook
|
|
52
|
-
* @param
|
|
64
|
+
* @param params - Result of useParams() hook — used directly for path parameter extraction,
|
|
65
|
+
* avoiding the URLPattern polyfill requirement for parameterized routes
|
|
53
66
|
* @param actor - XMachines actor instance
|
|
54
67
|
* @param routeMap - Bidirectional state ID ↔ path mapping
|
|
55
68
|
*/
|
|
56
|
-
constructor(solidNavigate, location,
|
|
69
|
+
constructor(solidNavigate, location, params, actor, routeMap) {
|
|
57
70
|
super(actor, {
|
|
58
71
|
getStateIdByPath: (path) => routeMap.getStateIdByPath(path),
|
|
59
|
-
getPathByStateId: (id) => routeMap.
|
|
72
|
+
getPathByStateId: (id) => routeMap.getPathByStateId(id),
|
|
60
73
|
});
|
|
61
74
|
this.solidNavigate = solidNavigate;
|
|
62
75
|
this.location = location;
|
|
76
|
+
this.solidParams = params;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Extract path parameters using Solid's pre-parsed `useParams()` values.
|
|
80
|
+
*
|
|
81
|
+
* Solid's router has already extracted all named parameters for the matched route
|
|
82
|
+
* segment. Reading `this.solidParams` inside the createEffect callback that drives
|
|
83
|
+
* `syncActorFromRouter` is safe — the reactive proxy always reflects the current
|
|
84
|
+
* route at the time the effect runs.
|
|
85
|
+
*
|
|
86
|
+
* Falls back to URLPattern-based extraction (base class) only when Solid provided
|
|
87
|
+
* no params for this route (i.e. the route has no `:param` segments).
|
|
88
|
+
*
|
|
89
|
+
* @param pathname - The actual URL path (unused — params already extracted by Solid)
|
|
90
|
+
* @param stateId - The matched state ID (unused — params already extracted by Solid)
|
|
91
|
+
* @returns Normalized path parameters with undefined/empty values filtered out
|
|
92
|
+
*/
|
|
93
|
+
extractParams(pathname, stateId) {
|
|
94
|
+
const entries = Object.entries(this.solidParams).filter((entry) => entry[1] !== undefined && entry[1] !== null && entry[1] !== "");
|
|
95
|
+
if (entries.length > 0) {
|
|
96
|
+
return Object.fromEntries(entries);
|
|
97
|
+
}
|
|
98
|
+
// No params from Solid — fall back to URLPattern for routes with no segments
|
|
99
|
+
return super.extractParams(pathname, stateId);
|
|
63
100
|
}
|
|
64
101
|
/**
|
|
65
102
|
* Navigate SolidJS Router to the given path.
|
|
@@ -73,30 +110,46 @@ export class SolidRouterBridge extends RouterBridgeBase {
|
|
|
73
110
|
getInitialRouterPath() {
|
|
74
111
|
return this.location.pathname ?? null;
|
|
75
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Return the initial URL search string for query-param forwarding on `connect()`.
|
|
115
|
+
*
|
|
116
|
+
* Reads `this.location.search` from Solid's `useLocation()` reactive object —
|
|
117
|
+
* the same source used by `getInitialRouterPath()`. An empty string (no query
|
|
118
|
+
* params) returns `undefined` so `syncActorFromRouter` produces `query: {}`.
|
|
119
|
+
*/
|
|
120
|
+
getInitialRouterSearch() {
|
|
121
|
+
return this.location.search || undefined;
|
|
122
|
+
}
|
|
76
123
|
/**
|
|
77
124
|
* Subscribe to SolidJS Router location changes using createEffect.
|
|
78
125
|
*
|
|
79
|
-
* MUST be called inside a Solid reactive owner (component
|
|
80
|
-
*
|
|
126
|
+
* MUST be called inside a Solid reactive owner (component or createRoot).
|
|
127
|
+
*
|
|
128
|
+
* The effect runs inside `createRoot()` to give it a stable owner independent
|
|
129
|
+
* of the calling component's lifecycle — this prevents the effect from being
|
|
130
|
+
* disposed if the component re-renders while the bridge should stay active.
|
|
131
|
+
* The trade-off is that component unmount does NOT automatically clean up the
|
|
132
|
+
* effect; `disconnect()` (or `dispose()`) MUST be called explicitly to avoid a leak.
|
|
81
133
|
*/
|
|
82
134
|
watchRouterChanges() {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
135
|
+
this.disposeRouterWatcher = createRoot((dispose) => {
|
|
136
|
+
createEffect(on(() => this.location.pathname, (pathname) => {
|
|
137
|
+
const search = this.location.search ?? "";
|
|
138
|
+
this.syncActorFromRouter(pathname, search);
|
|
139
|
+
}));
|
|
140
|
+
return dispose;
|
|
141
|
+
});
|
|
89
142
|
}
|
|
90
143
|
/**
|
|
91
144
|
* Stop watching SolidJS Router changes.
|
|
92
145
|
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
146
|
+
* Calls the `dispose` function returned by `createRoot()` in `watchRouterChanges()`,
|
|
147
|
+
* tearing down the reactive effect and freeing the isolated owner. This is the only
|
|
148
|
+
* cleanup path — component unmount does NOT trigger this automatically.
|
|
95
149
|
*/
|
|
96
150
|
unwatchRouterChanges() {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
this.isProcessingNavigation = true;
|
|
151
|
+
this.disposeRouterWatcher?.();
|
|
152
|
+
this.disposeRouterWatcher = null;
|
|
100
153
|
}
|
|
101
154
|
/**
|
|
102
155
|
* Dispose the bridge (alias for disconnect).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"solid-router-bridge.js","sourceRoot":"","sources":["../src/solid-router-bridge.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"solid-router-bridge.js","sourceRoot":"","sources":["../src/solid-router-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC;AAExD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAM1D;;;;;;;;;;GAUG;AACH,MAAM,OAAO,iBAAkB,SAAQ,gBAAgB;IAsBpC;IACA;IAtBV,oBAAoB,GAAwB,IAAI,CAAC;IAEzD;;;OAGG;IACc,WAAW,CAAS;IAErC;;;;;;;;;;;OAWG;IACH,YACkB,aAAwB,EACxB,QAAsB,EACvC,MAAc,EACd,KAA8C,EAC9C,QAAkB;QAElB,KAAK,CAAC,KAAK,EAAE;YACZ,gBAAgB,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,QAAQ,CAAC,gBAAgB,CAAC,IAAI,CAAC;YACnE,gBAAgB,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;SAC/D,CAAC,CAAC;QATc,kBAAa,GAAb,aAAa,CAAW;QACxB,aAAQ,GAAR,QAAQ,CAAc;QASvC,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC;IAC3B,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACgB,aAAa,CAAC,QAAgB,EAAE,OAAe;QACjE,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,MAAM,CACtD,CAAC,KAAK,EAA6B,EAAE,CACpC,KAAK,CAAC,CAAC,CAAC,KAAK,SAAS,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,CAC/D,CAAC;QACF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,OAAO,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QACpC,CAAC;QACD,6EAA6E;QAC7E,OAAO,KAAK,CAAC,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC/C,CAAC;IAED;;OAEG;IACO,cAAc,CAAC,IAAY;QACpC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED;;OAEG;IACgB,oBAAoB;QACtC,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,IAAI,IAAI,CAAC;IACvC,CAAC;IAED;;;;;;OAMG;IACgB,sBAAsB;QACxC,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,IAAI,SAAS,CAAC;IAC1C,CAAC;IAED;;;;;;;;;;OAUG;IACO,kBAAkB;QAC3B,IAAI,CAAC,oBAAoB,GAAG,UAAU,CAAC,CAAC,OAAO,EAAE,EAAE;YAClD,YAAY,CACX,EAAE,CACD,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAC5B,CAAC,QAAgB,EAAE,EAAE;gBACpB,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,IAAI,EAAE,CAAC;gBAC1C,IAAI,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YAC5C,CAAC,CACD,CACD,CAAC;YACF,OAAO,OAAO,CAAC;QAChB,CAAC,CAAC,CAAC;IACJ,CAAC;IAED;;;;;;OAMG;IACO,oBAAoB;QAC7B,IAAI,CAAC,oBAAoB,EAAE,EAAE,CAAC;QAC9B,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC;IAClC,CAAC;IAED;;;;;;;OAOG;IACH,OAAO;QACN,IAAI,CAAC,UAAU,EAAE,CAAC;IACnB,CAAC;CACD"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,21 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Type definitions for @xmachines/play-solid-router
|
|
3
3
|
*/
|
|
4
|
-
/**
|
|
5
|
-
* Mapping between state ID and URL path for SolidJS Router
|
|
6
|
-
*/
|
|
7
|
-
export interface RouteMapping {
|
|
8
|
-
/**
|
|
9
|
-
* XMachines state ID with # prefix
|
|
10
|
-
* @example '#home', '#profile'
|
|
11
|
-
*/
|
|
12
|
-
readonly stateId: string;
|
|
13
|
-
/**
|
|
14
|
-
* SolidJS Router path pattern
|
|
15
|
-
* @example '/', '/profile/:userId', '/settings/:section?'
|
|
16
|
-
*/
|
|
17
|
-
readonly path: string;
|
|
18
|
-
}
|
|
19
4
|
export type { PlayRouteEvent, RouterBridge } from "@xmachines/play-router";
|
|
20
5
|
export type { AbstractActor } from "@xmachines/play-actor";
|
|
21
6
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3E,YAAY,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xmachines/play-solid-router",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.40",
|
|
4
4
|
"description": "SolidJS Router adapter for XMachines Universal Player Architecture",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "XMachines Contributors",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"types": "./dist/index.d.ts",
|
|
20
20
|
"exports": {
|
|
21
21
|
".": {
|
|
22
|
+
"source": "./src/index.ts",
|
|
22
23
|
"types": "./dist/index.d.ts",
|
|
23
24
|
"default": "./dist/index.js"
|
|
24
25
|
}
|
|
@@ -28,27 +29,35 @@
|
|
|
28
29
|
},
|
|
29
30
|
"scripts": {
|
|
30
31
|
"build": "tsc --build",
|
|
31
|
-
"
|
|
32
|
+
"lint": "oxlint .",
|
|
33
|
+
"format": "oxfmt .",
|
|
34
|
+
"test": "vitest",
|
|
32
35
|
"test:watch": "vitest",
|
|
33
|
-
"
|
|
34
|
-
"clean": "rm -rf dist *.tsbuildinfo",
|
|
36
|
+
"clean": "rm -rf dist *.tsbuildinfo coverage node_modules/.svelte2tsx-*",
|
|
35
37
|
"prepublishOnly": "npm run build"
|
|
36
38
|
},
|
|
37
39
|
"dependencies": {
|
|
38
|
-
"@xmachines/play": "1.0.0-beta.
|
|
39
|
-
"@xmachines/play-actor": "1.0.0-beta.
|
|
40
|
-
"@xmachines/play-router": "1.0.0-beta.
|
|
41
|
-
"@xmachines/play-signals": "1.0.0-beta.
|
|
40
|
+
"@xmachines/play": "1.0.0-beta.40",
|
|
41
|
+
"@xmachines/play-actor": "1.0.0-beta.40",
|
|
42
|
+
"@xmachines/play-router": "1.0.0-beta.40",
|
|
43
|
+
"@xmachines/play-signals": "1.0.0-beta.40"
|
|
42
44
|
},
|
|
43
45
|
"devDependencies": {
|
|
44
46
|
"@solidjs/router": "^0.16.1",
|
|
45
47
|
"@solidjs/testing-library": "^0.8.10",
|
|
46
|
-
"@xmachines/
|
|
47
|
-
"
|
|
48
|
-
"
|
|
48
|
+
"@xmachines/play-xstate": "1.0.0-beta.40",
|
|
49
|
+
"@xmachines/shared": "1.0.0-beta.40",
|
|
50
|
+
"jsdom": "^29.0.2",
|
|
51
|
+
"oxfmt": "^0.45.0",
|
|
52
|
+
"oxlint": "^1.60.0",
|
|
53
|
+
"solid-js": "^1.9.12",
|
|
54
|
+
"vite-plugin-solid": "^2.11.11",
|
|
55
|
+
"vitest": "^4.1.4",
|
|
56
|
+
"xstate": "^5.30.0"
|
|
49
57
|
},
|
|
50
58
|
"peerDependencies": {
|
|
51
|
-
"@solidjs/router": "^0.
|
|
52
|
-
"solid-js": "^1.8.0"
|
|
59
|
+
"@solidjs/router": "^0.16.1",
|
|
60
|
+
"solid-js": "^1.8.0",
|
|
61
|
+
"xstate": "^5.30.0"
|
|
53
62
|
}
|
|
54
63
|
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Create RouteMap helper for SolidJS Router adapter
|
|
3
|
-
*
|
|
4
|
-
* Simplifies demo code by hiding route extraction complexity.
|
|
5
|
-
* Internally calls extractMachineRoutes() and getRoutableRoutes(),
|
|
6
|
-
* then constructs RouteMap with proper state ID ↔ path mappings.
|
|
7
|
-
*/
|
|
8
|
-
import type { AnyStateMachine } from "xstate";
|
|
9
|
-
import { RouteMap } from "./route-map.js";
|
|
10
|
-
/**
|
|
11
|
-
* Create a RouteMap from an XState machine
|
|
12
|
-
*
|
|
13
|
-
* @param machine - XState machine with route: {} config on states
|
|
14
|
-
* @returns RouteMap for bidirectional state ID ↔ path resolution
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* ```typescript
|
|
18
|
-
* import { createRouteMap } from '@xmachines/play-solid-router';
|
|
19
|
-
* import { authMachine } from './machine.js';
|
|
20
|
-
*
|
|
21
|
-
* const routeMap = createRouteMap(authMachine);
|
|
22
|
-
* ```
|
|
23
|
-
*/
|
|
24
|
-
export declare function createRouteMap(machine: AnyStateMachine): RouteMap;
|
|
25
|
-
//# sourceMappingURL=create-route-map.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"create-route-map.d.ts","sourceRoot":"","sources":["../src/create-route-map.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe,GAAG,QAAQ,CAcjE"}
|
package/dist/create-route-map.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Create RouteMap helper for SolidJS Router adapter
|
|
3
|
-
*
|
|
4
|
-
* Simplifies demo code by hiding route extraction complexity.
|
|
5
|
-
* Internally calls extractMachineRoutes() and getRoutableRoutes(),
|
|
6
|
-
* then constructs RouteMap with proper state ID ↔ path mappings.
|
|
7
|
-
*/
|
|
8
|
-
import { extractMachineRoutes, getRoutableRoutes } from "@xmachines/play-router";
|
|
9
|
-
import { RouteMap } from "./route-map.js";
|
|
10
|
-
/**
|
|
11
|
-
* Create a RouteMap from an XState machine
|
|
12
|
-
*
|
|
13
|
-
* @param machine - XState machine with route: {} config on states
|
|
14
|
-
* @returns RouteMap for bidirectional state ID ↔ path resolution
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* ```typescript
|
|
18
|
-
* import { createRouteMap } from '@xmachines/play-solid-router';
|
|
19
|
-
* import { authMachine } from './machine.js';
|
|
20
|
-
*
|
|
21
|
-
* const routeMap = createRouteMap(authMachine);
|
|
22
|
-
* ```
|
|
23
|
-
*/
|
|
24
|
-
export function createRouteMap(machine) {
|
|
25
|
-
// Extract route tree from machine
|
|
26
|
-
const routeTree = extractMachineRoutes(machine);
|
|
27
|
-
// Get only routable nodes (states with route: {} config)
|
|
28
|
-
const routes = getRoutableRoutes(routeTree);
|
|
29
|
-
// Map to RouteMapping format expected by RouteMap constructor
|
|
30
|
-
const mappings = routes.map((node) => ({
|
|
31
|
-
stateId: node.stateId,
|
|
32
|
-
path: node.fullPath,
|
|
33
|
-
}));
|
|
34
|
-
return new RouteMap(mappings);
|
|
35
|
-
}
|
|
36
|
-
//# sourceMappingURL=create-route-map.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"create-route-map.js","sourceRoot":"","sources":["../src/create-route-map.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AACjF,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,cAAc,CAAC,OAAwB;IACtD,kCAAkC;IAClC,MAAM,SAAS,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAEhD,yDAAyD;IACzD,MAAM,MAAM,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAE5C,8DAA8D;IAC9D,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACtC,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,IAAI,EAAE,IAAI,CAAC,QAAQ;KACnB,CAAC,CAAC,CAAC;IAEJ,OAAO,IAAI,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAC/B,CAAC"}
|
package/dist/route-map.d.ts
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RouteMap for bidirectional state ID ↔ path mapping
|
|
3
|
-
*
|
|
4
|
-
* Provides efficient lookup between XMachines state IDs and SolidJS Router paths.
|
|
5
|
-
* Supports pattern matching for dynamic routes with parameters.
|
|
6
|
-
*/
|
|
7
|
-
import type { RouteMapping } from "./types.js";
|
|
8
|
-
export declare class RouteMap {
|
|
9
|
-
private stateToPath;
|
|
10
|
-
private pathToState;
|
|
11
|
-
/**
|
|
12
|
-
* Create a RouteMap with bidirectional mappings
|
|
13
|
-
*
|
|
14
|
-
* @param mappings - Array of state ID to path mappings
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* ```typescript
|
|
18
|
-
* const routeMap = new RouteMap([
|
|
19
|
-
* { stateId: '#home', path: '/' },
|
|
20
|
-
* { stateId: '#profile', path: '/profile/:userId' },
|
|
21
|
-
* { stateId: '#settings', path: '/settings/:section?' }
|
|
22
|
-
* ]);
|
|
23
|
-
* ```
|
|
24
|
-
*/
|
|
25
|
-
constructor(mappings: RouteMapping[]);
|
|
26
|
-
/**
|
|
27
|
-
* Get path pattern for a state ID
|
|
28
|
-
*
|
|
29
|
-
* @param stateId - XMachines state ID (e.g., '#profile')
|
|
30
|
-
* @returns Path pattern or undefined if not found
|
|
31
|
-
*
|
|
32
|
-
* @example
|
|
33
|
-
* ```typescript
|
|
34
|
-
* routeMap.getPath('#profile'); // '/profile/:userId'
|
|
35
|
-
* ```
|
|
36
|
-
*/
|
|
37
|
-
getPath(stateId: string): string | undefined;
|
|
38
|
-
/**
|
|
39
|
-
* Get state ID for a path, with pattern matching support
|
|
40
|
-
*
|
|
41
|
-
* Performs exact match first, then fuzzy pattern matching for dynamic routes.
|
|
42
|
-
* Supports both required (:param) and optional (:param?) parameters.
|
|
43
|
-
*
|
|
44
|
-
* @param path - Actual URL path (e.g., '/profile/123')
|
|
45
|
-
* @returns State ID or undefined if no match found
|
|
46
|
-
*
|
|
47
|
-
* @example
|
|
48
|
-
* ```typescript
|
|
49
|
-
* routeMap.getStateIdByPath('/profile/123'); // '#profile'
|
|
50
|
-
* routeMap.getStateIdByPath('/settings'); // '#settings'
|
|
51
|
-
* routeMap.getStateIdByPath('/settings/account'); // '#settings'
|
|
52
|
-
* ```
|
|
53
|
-
*/
|
|
54
|
-
getStateIdByPath(path: string): string | undefined;
|
|
55
|
-
/**
|
|
56
|
-
* Check if a path matches a pattern
|
|
57
|
-
*
|
|
58
|
-
* Supports:
|
|
59
|
-
* - Required parameters: :param
|
|
60
|
-
* - Optional parameters: :param?
|
|
61
|
-
*
|
|
62
|
-
* @param path - Actual URL path
|
|
63
|
-
* @param pattern - Route pattern with :param syntax
|
|
64
|
-
* @returns true if path matches pattern
|
|
65
|
-
*
|
|
66
|
-
* @example
|
|
67
|
-
* ```typescript
|
|
68
|
-
* matchesPattern('/profile/123', '/profile/:userId'); // true
|
|
69
|
-
* matchesPattern('/settings', '/settings/:section?'); // true
|
|
70
|
-
* matchesPattern('/settings/account', '/settings/:section?'); // true
|
|
71
|
-
* ```
|
|
72
|
-
*/
|
|
73
|
-
private matchesPattern;
|
|
74
|
-
}
|
|
75
|
-
//# sourceMappingURL=route-map.d.ts.map
|
package/dist/route-map.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"route-map.d.ts","sourceRoot":"","sources":["../src/route-map.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,qBAAa,QAAQ;IACpB,OAAO,CAAC,WAAW,CAA6B;IAChD,OAAO,CAAC,WAAW,CAA6B;IAEhD;;;;;;;;;;;;;OAaG;gBACS,QAAQ,EAAE,YAAY,EAAE;IAOpC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI5C;;;;;;;;;;;;;;;OAeG;IACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAmBlD;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,cAAc;CAetB"}
|
package/dist/route-map.js
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RouteMap for bidirectional state ID ↔ path mapping
|
|
3
|
-
*
|
|
4
|
-
* Provides efficient lookup between XMachines state IDs and SolidJS Router paths.
|
|
5
|
-
* Supports pattern matching for dynamic routes with parameters.
|
|
6
|
-
*/
|
|
7
|
-
export class RouteMap {
|
|
8
|
-
stateToPath = new Map();
|
|
9
|
-
pathToState = new Map();
|
|
10
|
-
/**
|
|
11
|
-
* Create a RouteMap with bidirectional mappings
|
|
12
|
-
*
|
|
13
|
-
* @param mappings - Array of state ID to path mappings
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* ```typescript
|
|
17
|
-
* const routeMap = new RouteMap([
|
|
18
|
-
* { stateId: '#home', path: '/' },
|
|
19
|
-
* { stateId: '#profile', path: '/profile/:userId' },
|
|
20
|
-
* { stateId: '#settings', path: '/settings/:section?' }
|
|
21
|
-
* ]);
|
|
22
|
-
* ```
|
|
23
|
-
*/
|
|
24
|
-
constructor(mappings) {
|
|
25
|
-
mappings.forEach(({ stateId, path }) => {
|
|
26
|
-
this.stateToPath.set(stateId, path);
|
|
27
|
-
this.pathToState.set(path, stateId);
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Get path pattern for a state ID
|
|
32
|
-
*
|
|
33
|
-
* @param stateId - XMachines state ID (e.g., '#profile')
|
|
34
|
-
* @returns Path pattern or undefined if not found
|
|
35
|
-
*
|
|
36
|
-
* @example
|
|
37
|
-
* ```typescript
|
|
38
|
-
* routeMap.getPath('#profile'); // '/profile/:userId'
|
|
39
|
-
* ```
|
|
40
|
-
*/
|
|
41
|
-
getPath(stateId) {
|
|
42
|
-
return this.stateToPath.get(stateId);
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Get state ID for a path, with pattern matching support
|
|
46
|
-
*
|
|
47
|
-
* Performs exact match first, then fuzzy pattern matching for dynamic routes.
|
|
48
|
-
* Supports both required (:param) and optional (:param?) parameters.
|
|
49
|
-
*
|
|
50
|
-
* @param path - Actual URL path (e.g., '/profile/123')
|
|
51
|
-
* @returns State ID or undefined if no match found
|
|
52
|
-
*
|
|
53
|
-
* @example
|
|
54
|
-
* ```typescript
|
|
55
|
-
* routeMap.getStateIdByPath('/profile/123'); // '#profile'
|
|
56
|
-
* routeMap.getStateIdByPath('/settings'); // '#settings'
|
|
57
|
-
* routeMap.getStateIdByPath('/settings/account'); // '#settings'
|
|
58
|
-
* ```
|
|
59
|
-
*/
|
|
60
|
-
getStateIdByPath(path) {
|
|
61
|
-
// Strip query string and hash fragment for matching
|
|
62
|
-
const cleanPath = path.split("?")[0].split("#")[0];
|
|
63
|
-
// Direct lookup first (exact match)
|
|
64
|
-
if (this.pathToState.has(cleanPath)) {
|
|
65
|
-
return this.pathToState.get(cleanPath);
|
|
66
|
-
}
|
|
67
|
-
// Fuzzy match for dynamic routes
|
|
68
|
-
for (const [pattern, stateId] of this.pathToState) {
|
|
69
|
-
if (this.matchesPattern(cleanPath, pattern)) {
|
|
70
|
-
return stateId;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return undefined;
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Check if a path matches a pattern
|
|
77
|
-
*
|
|
78
|
-
* Supports:
|
|
79
|
-
* - Required parameters: :param
|
|
80
|
-
* - Optional parameters: :param?
|
|
81
|
-
*
|
|
82
|
-
* @param path - Actual URL path
|
|
83
|
-
* @param pattern - Route pattern with :param syntax
|
|
84
|
-
* @returns true if path matches pattern
|
|
85
|
-
*
|
|
86
|
-
* @example
|
|
87
|
-
* ```typescript
|
|
88
|
-
* matchesPattern('/profile/123', '/profile/:userId'); // true
|
|
89
|
-
* matchesPattern('/settings', '/settings/:section?'); // true
|
|
90
|
-
* matchesPattern('/settings/account', '/settings/:section?'); // true
|
|
91
|
-
* ```
|
|
92
|
-
*/
|
|
93
|
-
matchesPattern(path, pattern) {
|
|
94
|
-
// Convert route pattern to regex
|
|
95
|
-
// Process replacements in correct order: optional params first, then required
|
|
96
|
-
let regexPattern = pattern
|
|
97
|
-
// Replace /:param? with optional segment (matches both /value and nothing)
|
|
98
|
-
.replace(/\/:(\w+)\?/g, "(?:/([^/]+))?")
|
|
99
|
-
// Replace /:param with required segment
|
|
100
|
-
.replace(/\/:(\w+)/g, "/([^/]+)");
|
|
101
|
-
// Add anchors for exact match
|
|
102
|
-
regexPattern = "^" + regexPattern + "$";
|
|
103
|
-
const regex = new RegExp(regexPattern);
|
|
104
|
-
return regex.test(path);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
//# sourceMappingURL=route-map.js.map
|
package/dist/route-map.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"route-map.js","sourceRoot":"","sources":["../src/route-map.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,OAAO,QAAQ;IACZ,WAAW,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,WAAW,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEhD;;;;;;;;;;;;;OAaG;IACH,YAAY,QAAwB;QACnC,QAAQ,CAAC,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE;YACtC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YACpC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;IACJ,CAAC;IAED;;;;;;;;;;OAUG;IACH,OAAO,CAAC,OAAe;QACtB,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACtC,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACH,gBAAgB,CAAC,IAAY;QAC5B,oDAAoD;QACpD,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAEnD,oCAAoC;QACpC,IAAI,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YACrC,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACxC,CAAC;QAED,iCAAiC;QACjC,KAAK,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACnD,IAAI,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,CAAC;gBAC7C,OAAO,OAAO,CAAC;YAChB,CAAC;QACF,CAAC;QAED,OAAO,SAAS,CAAC;IAClB,CAAC;IAED;;;;;;;;;;;;;;;;;OAiBG;IACK,cAAc,CAAC,IAAY,EAAE,OAAe;QACnD,iCAAiC;QACjC,8EAA8E;QAC9E,IAAI,YAAY,GAAG,OAAO;YACzB,2EAA2E;aAC1E,OAAO,CAAC,aAAa,EAAE,eAAe,CAAC;YACxC,wCAAwC;aACvC,OAAO,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;QAEnC,8BAA8B;QAC9B,YAAY,GAAG,GAAG,GAAG,YAAY,GAAG,GAAG,CAAC;QAExC,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,YAAY,CAAC,CAAC;QACvC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;CACD"}
|