@xmachines/play-router 2.1.1 → 3.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 (42) hide show
  1. package/README.md +280 -11
  2. package/dist/base-path.d.ts +209 -0
  3. package/dist/base-path.d.ts.map +1 -0
  4. package/dist/base-path.js +418 -0
  5. package/dist/base-path.js.map +1 -0
  6. package/dist/base-route-map.d.ts.map +1 -1
  7. package/dist/base-route-map.js +5 -0
  8. package/dist/base-route-map.js.map +1 -1
  9. package/dist/build-tree.d.ts.map +1 -1
  10. package/dist/build-tree.js +1 -2
  11. package/dist/build-tree.js.map +1 -1
  12. package/dist/errors.d.ts +87 -4
  13. package/dist/errors.d.ts.map +1 -1
  14. package/dist/errors.js +97 -4
  15. package/dist/errors.js.map +1 -1
  16. package/dist/framework-params.d.ts +144 -0
  17. package/dist/framework-params.d.ts.map +1 -0
  18. package/dist/framework-params.js +291 -0
  19. package/dist/framework-params.js.map +1 -0
  20. package/dist/index.d.ts +8 -2
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +18 -1
  23. package/dist/index.js.map +1 -1
  24. package/dist/provider-lifecycle.d.ts +197 -0
  25. package/dist/provider-lifecycle.d.ts.map +1 -0
  26. package/dist/provider-lifecycle.js +167 -0
  27. package/dist/provider-lifecycle.js.map +1 -0
  28. package/dist/query.d.ts +49 -0
  29. package/dist/query.d.ts.map +1 -1
  30. package/dist/query.js +59 -0
  31. package/dist/query.js.map +1 -1
  32. package/dist/router-bridge-base.d.ts +320 -14
  33. package/dist/router-bridge-base.d.ts.map +1 -1
  34. package/dist/router-bridge-base.js +922 -105
  35. package/dist/router-bridge-base.js.map +1 -1
  36. package/dist/types.d.ts +44 -0
  37. package/dist/types.d.ts.map +1 -1
  38. package/dist/url-pattern-utils.d.ts +0 -30
  39. package/dist/url-pattern-utils.d.ts.map +1 -1
  40. package/dist/url-pattern-utils.js +52 -1
  41. package/dist/url-pattern-utils.js.map +1 -1
  42. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -6,7 +6,19 @@ export { sanitizePathname, buildPlayRouteEvent, extractQuery, extractRouteParams
6
6
  export { validateRouteFormat, validateStateExists, detectDuplicateRoutes, } from "./validate-routes.js";
7
7
  export { buildRouteTree } from "./build-tree.js";
8
8
  export { extractMachineRoutes } from "./extract-routes.js";
9
- export { getNavigableRoutes, getRoutableRoutes, routeExists, getTransitionReachableRoutes, isRouteReachable, } from "./query.js";
9
+ export { getNavigableRoutes, getRoutableRoutes, routeExists, getRouteMappings, getTransitionReachableRoutes, isRouteReachable, } from "./query.js";
10
+ // The base-path mount — it lets a host own a part of the same router as a machine.
11
+ // A consumer normally gives `basePath` to a bridge, to `connectRouter`, or to a
12
+ // `PlayRouterProvider`. These primitives serve an adapter, and a host that builds its
13
+ // own route table.
14
+ export { normalizeBasePath, resolveBasePath, stripBasePath, joinBasePath, NO_BASE_PATH, } from "./base-path.js";
15
+ // The params of a framework router, reconciled with the pattern of the machine. A
16
+ // bridge whose framework parses the params itself calls this, and it keeps that parse.
17
+ export { cleanFrameworkParams, getPatternParamNames, getRequiredPatternParamNames, pickOwnParams, resolveFrameworkParams, } from "./framework-params.js";
18
+ // The framework-free half of a PlayRouterProvider: the props, the constructor shape,
19
+ // and the lifecycle of the bridge. A provider of a framework adds its own effects and
20
+ // nothing else. This entry stays framework-agnostic — nothing here imports one.
21
+ export { isMountableBridge, mountKey, createRouterConnection, openProviderBridge, repointProviderBridge, } from "./provider-lifecycle.js";
10
22
  // The graph adapter — it converts an XState machine into a Graph of @statelyai/graph
11
23
  export { machineToGraph } from "./machine-to-graph.js";
12
24
  // The shared base class of the route map for both directions.
@@ -19,4 +31,9 @@ export { createRouteMap } from "./create-route-map.js";
19
31
  export { createRouteMapFromTree } from "./create-route-map-from-tree.js";
20
32
  // The helpers of a route lookup. matchesPattern stays private inside find-route.ts
21
33
  export { findRouteById, findRouteByPath } from "./find-route.js";
34
+ // The release protocol of @xmachines/play. This package's own published `.d.ts` names
35
+ // exactly these, so a consumer reads them from here and needs no second manifest
36
+ // entry. It names `asCleanup` nowhere: a consumer of this package RECEIVES a
37
+ // release, and builds one only with @xmachines/play itself.
38
+ export { DISPOSE } from "@xmachines/play";
22
39
  //# 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":"AAEA,mEAAmE;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAG3D,wHAAwH;AACxH,OAAO,EACN,gBAAgB,EAChB,mBAAmB,EACnB,YAAY,EACZ,kBAAkB,GAClB,MAAM,kBAAkB,CAAC;AAG1B,sBAAsB;AACtB,OAAO,EACN,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,GACrB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EACN,kBAAkB,EAClB,iBAAiB,EACjB,WAAW,EACX,4BAA4B,EAC5B,gBAAgB,GAChB,MAAM,YAAY,CAAC;AAEpB,qFAAqF;AACrF,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAmBvD,8DAA8D;AAC9D,qFAAqF;AACrF,qBAAqB;AACrB,OAAO,EAAE,QAAQ,EAAqB,MAAM,qBAAqB,CAAC;AAOlE,kFAAkF;AAClF,iFAAiF;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AAEzE,mFAAmF;AACnF,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,mEAAmE;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAG3D,wHAAwH;AACxH,OAAO,EACN,gBAAgB,EAChB,mBAAmB,EACnB,YAAY,EACZ,kBAAkB,GAClB,MAAM,kBAAkB,CAAC;AAG1B,sBAAsB;AACtB,OAAO,EACN,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,GACrB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EACN,kBAAkB,EAClB,iBAAiB,EACjB,WAAW,EACX,gBAAgB,EAChB,4BAA4B,EAC5B,gBAAgB,GAChB,MAAM,YAAY,CAAC;AAEpB,mFAAmF;AACnF,gFAAgF;AAChF,sFAAsF;AACtF,mBAAmB;AACnB,OAAO,EACN,iBAAiB,EACjB,eAAe,EACf,aAAa,EACb,YAAY,EACZ,YAAY,GACZ,MAAM,gBAAgB,CAAC;AAExB,kFAAkF;AAClF,uFAAuF;AACvF,OAAO,EACN,oBAAoB,EACpB,oBAAoB,EACpB,4BAA4B,EAC5B,aAAa,EACb,sBAAsB,GAEtB,MAAM,uBAAuB,CAAC;AAE/B,qFAAqF;AACrF,sFAAsF;AACtF,gFAAgF;AAChF,OAAO,EACN,iBAAiB,EACjB,QAAQ,EACR,sBAAsB,EACtB,kBAAkB,EAClB,qBAAqB,GACrB,MAAM,yBAAyB,CAAC;AAQjC,qFAAqF;AACrF,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAoBvD,8DAA8D;AAC9D,qFAAqF;AACrF,qBAAqB;AACrB,OAAO,EAAE,QAAQ,EAAqB,MAAM,qBAAqB,CAAC;AAOlE,kFAAkF;AAClF,iFAAiF;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AAEzE,mFAAmF;AACnF,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEjE,sFAAsF;AACtF,iFAAiF;AACjF,6EAA6E;AAC7E,4DAA4D;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,197 @@
1
+ /**
2
+ * The framework-free half of a `PlayRouterProvider`.
3
+ *
4
+ * A provider of a framework is two things: the lifecycle of a bridge, and about fifteen
5
+ * lines that bind that lifecycle to the effects of the framework. The lifecycle carries
6
+ * every decision — when the bridge is built, in which order it connects and
7
+ * disconnects, when a mount moves rather than rebuilds, and how a bridge that never
8
+ * heard of `basePath` is detected — and none of those decisions is framework-specific.
9
+ * They therefore live here, in a module that imports no framework, and each adapter
10
+ * keeps only its own effects.
11
+ *
12
+ * Four adapters used to hold a copy of the whole provider: React Router beside TanStack
13
+ * React, Solid Router beside TanStack Solid. Neither member of a pair could import the
14
+ * other, because each declares its own router as a `peerDependency`. This module is the
15
+ * home they all already depend on, and it stays framework-agnostic, so a consumer of
16
+ * `@xmachines/play-router` gains no framework dependency of any kind.
17
+ *
18
+ * @see [Multi-router integration](../../docs/examples/multi-router-integration.md)
19
+ */
20
+ import { DISPOSE } from "@xmachines/play";
21
+ import { type BasePathOptions } from "./base-path.js";
22
+ import type { MountableRouterBridge, PlayActor, RouterBridge } from "./types.js";
23
+ import type { RouteMap } from "./base-route-map.js";
24
+ /**
25
+ * The props that every `PlayRouterProvider` of a framework shares.
26
+ *
27
+ * `TNode` is what the framework renders: `ReactNode` for React, `JSX.Element` for
28
+ * Solid. It is the ONLY thing in these props that a framework decides, which is why
29
+ * they live here and not in an adapter.
30
+ *
31
+ * An adapter re-exports a two-parameter alias of this type, with `TRouter` bound to
32
+ * the type of its router instance and `TNode` bound to its own node type.
33
+ */
34
+ export interface PlayRouterProviderBaseProps<TRouter, TActor extends PlayActor, TNode> {
35
+ /**
36
+ * The actor to keep in step with the router. It must be a stable reference: give
37
+ * the same actor instance on every render. An actor in the JSX, or a new actor on
38
+ * each render, makes the bridge disconnect and connect again each time.
39
+ */
40
+ actor: TActor;
41
+ /** The router instance that the bridge keeps in step with the actor. It must be a stable reference. */
42
+ router: TRouter;
43
+ /**
44
+ * The route map of both directions, for the lookup between a state ID and a URL path.
45
+ *
46
+ * **It must be a stable reference.** The provider builds the bridge again on each
47
+ * change of the identity of `routeMap`. A value in the JSX, for example
48
+ * `createRouteMapFromTree(routeTree)`, makes a new object on every render, and the
49
+ * bridge therefore connects again on every render. Hold the value with `useMemo`:
50
+ *
51
+ * ```tsx
52
+ * const routeMap = useMemo(() => createRouteMapFromTree(routeTree), [routeTree]);
53
+ * ```
54
+ */
55
+ routeMap: RouteMap;
56
+ /**
57
+ * Mounts the routes of the machine under a URL prefix that the host owns, so that
58
+ * the routes of the host and the routes of the machine share one router.
59
+ *
60
+ * `actor`, `router`, and `routeMap` each need a stable reference. This prop needs
61
+ * none, and it rebuilds the bridge never: a change moves the live bridge with
62
+ * `setBasePath()`, so a host route that renders again with a new prefix keeps the
63
+ * actor, the route map, and its cache.
64
+ *
65
+ * The prefix says WHERE an actor is mounted, and never WHICH actor is mounted. An
66
+ * actor never changes identity, so a prefix segment that IDENTIFIES the actor gives
67
+ * a different `actor` prop instead, and THAT rebuilds the bridge — which is correct,
68
+ * because one actor takes one bridge.
69
+ *
70
+ * A location outside the prefix belongs completely to the host: the bridge sends no
71
+ * event there, and it corrects no URL there.
72
+ *
73
+ * Every `:param` needs a value. An unresolved one throws a
74
+ * `MissingBasePathParamError` out of the mount effect, because the bridge writes no
75
+ * URL without it. Render this component only after the host holds the value, which
76
+ * a route loader or a `useParams()` call does already.
77
+ */
78
+ basePath?: string;
79
+ /**
80
+ * The values of the `:param` segments of
81
+ * {@link PlayRouterProviderBaseProps.basePath}, for example `{ machineId }` for
82
+ * `basePath="/:machineId/play"`.
83
+ *
84
+ * An inline object literal is correct here: the provider compares this prop by
85
+ * value, and a move to the same prefix does nothing.
86
+ */
87
+ basePathParams?: Record<string, string | number>;
88
+ /** The renderer callback receives the same concrete actor type as the prop. */
89
+ renderer: (actor: TActor, router: TRouter) => TNode;
90
+ }
91
+ /**
92
+ * The constructor shape that a bridge class must satisfy for a provider factory: `(router, actor, routeMap, options?) → RouterBridge`.
93
+ *
94
+ * The return type is `RouterBridge`, the published contract of a bridge, so a
95
+ * consumer bridge of its own keeps compiling. Every bridge on `RouterBridgeBase` also
96
+ * satisfies `MountableRouterBridge`, and the provider finds that mount API at run
97
+ * time: a bridge without it ignores `basePath`, exactly as it did before the option
98
+ * existed.
99
+ */
100
+ export type PlayRouterBridgeConstructor<TRouter> = new (router: TRouter, actor: PlayActor, routeMap: RouteMap, options?: BasePathOptions) => RouterBridge;
101
+ /**
102
+ * Tells you whether a bridge can move its mount.
103
+ *
104
+ * {@link PlayRouterBridgeConstructor} asks for a `RouterBridge`, the published
105
+ * contract, so a consumer bridge that never heard of `basePath` still compiles. Such a
106
+ * bridge ignores the prefix, exactly as it did before the option existed, so the probe
107
+ * runs at run time rather than in the type system.
108
+ */
109
+ export declare function isMountableBridge(bridge: RouterBridge | null | undefined): bridge is MountableRouterBridge;
110
+ /**
111
+ * A key that changes when the MOUNT changes, and never when its identity changes.
112
+ *
113
+ * The comparison is BY VALUE, because `basePathParams` is normally an inline object
114
+ * literal: its identity changes on every render while the prefix does not, and a
115
+ * provider that watched the identity would move the mount on each render.
116
+ *
117
+ * @returns A string to compare, or to give to the dependency list of an effect.
118
+ */
119
+ export declare function mountKey(basePath?: BasePathOptions["basePath"], basePathParams?: BasePathOptions["basePathParams"]): string;
120
+ /** What {@link openProviderBridge} needs to build and connect a bridge. */
121
+ export interface OpenProviderBridgeArgs<TRouter, TActor extends PlayActor> extends BasePathOptions {
122
+ router: TRouter;
123
+ actor: TActor;
124
+ routeMap: RouteMap;
125
+ }
126
+ /**
127
+ * Builds a bridge, connects it, and hands back the way to close it.
128
+ *
129
+ * The order is the contract: the constructor applies the mount, `connect()` runs the
130
+ * first synchronization against it, and `close()` disconnects exactly the bridge that
131
+ * this call opened. A provider that closed "the current bridge" instead would
132
+ * disconnect a later one when two mounts overlap.
133
+ *
134
+ * @returns The bridge, and a `close` that disconnects it one time.
135
+ */
136
+ export declare function openProviderBridge<TRouter, TActor extends PlayActor>(BridgeCtor: PlayRouterBridgeConstructor<TRouter>, { router, actor, routeMap, basePath, basePathParams }: OpenProviderBridgeArgs<TRouter, TActor>): {
137
+ bridge: RouterBridge;
138
+ close: () => void;
139
+ };
140
+ /**
141
+ * Moves the mount of a live bridge, and rebuilds nothing.
142
+ *
143
+ * The call is safe directly after the open: it resolves to the prefix that the
144
+ * constructor applied already, and `setBasePath` then returns at once. A `null` bridge,
145
+ * and a bridge with no mount API, are both no-ops.
146
+ */
147
+ export declare function repointProviderBridge(bridge: RouterBridge | null | undefined, basePath?: BasePathOptions["basePath"], basePathParams?: BasePathOptions["basePathParams"]): void;
148
+ /**
149
+ * What `connectRouter` of an adapter returns.
150
+ *
151
+ * The value is CALLABLE, so `const disconnect = connectRouter(…); disconnect();` keeps
152
+ * working exactly as it did. It also carries the mount API, because a host that gives a
153
+ * `basePath` needs the two things {@link MountableRouterBridge} promises: the resolved
154
+ * mount to read back, and a way to move it. The bridge itself lives inside
155
+ * `connectRouter`, so without this handle a host had to rebuild the actor and the bridge
156
+ * to move a mount — which throws away the LRU cache of the route map, the very thing
157
+ * `setBasePath` exists to keep.
158
+ *
159
+ * `basePath` and `basePathParams` are GETTERS on the live bridge, so they cannot go
160
+ * stale.
161
+ */
162
+ export interface RouterConnection {
163
+ /** Stops the synchronization. The same call as {@link RouterConnection.disconnect}. */
164
+ (): void;
165
+ /** Stops the synchronization. */
166
+ disconnect(): void;
167
+ /**
168
+ * Stops the synchronization when the scope of a `using` declaration ends.
169
+ *
170
+ * This value IS the release of `connectRouter`, and all eight router adapters return
171
+ * it, so it carries the protocol of `@xmachines/play` like every other release of the
172
+ * ecosystem:
173
+ *
174
+ * ```ts
175
+ * {
176
+ * using connection = connectRouter({ actor, router, routeMap });
177
+ * // disconnect() runs at the end of the scope, and after an exception too
178
+ * }
179
+ * ```
180
+ *
181
+ * The release is {@link RouterConnection.disconnect}, which is idempotent.
182
+ */
183
+ [DISPOSE](): void;
184
+ /** The resolved prefix of the mount, or `""` when the machine owns the whole router. */
185
+ readonly basePath: string;
186
+ /** The values of the `:param` segments of the mount. They travel in no event. */
187
+ readonly basePathParams: Readonly<Record<string, string>>;
188
+ /** Moves the mount of the live bridge, with no teardown. */
189
+ setBasePath(basePath?: BasePathOptions["basePath"], basePathParams?: BasePathOptions["basePathParams"]): void;
190
+ }
191
+ /**
192
+ * Wraps a live bridge in the {@link RouterConnection} that `connectRouter` returns.
193
+ *
194
+ * @param bridge - The bridge that `connectRouter` built and connected.
195
+ */
196
+ export declare function createRouterConnection(bridge: MountableRouterBridge): RouterConnection;
197
+ //# sourceMappingURL=provider-lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-lifecycle.d.ts","sourceRoot":"","sources":["../src/provider-lifecycle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,167 @@
1
+ /**
2
+ * The framework-free half of a `PlayRouterProvider`.
3
+ *
4
+ * A provider of a framework is two things: the lifecycle of a bridge, and about fifteen
5
+ * lines that bind that lifecycle to the effects of the framework. The lifecycle carries
6
+ * every decision — when the bridge is built, in which order it connects and
7
+ * disconnects, when a mount moves rather than rebuilds, and how a bridge that never
8
+ * heard of `basePath` is detected — and none of those decisions is framework-specific.
9
+ * They therefore live here, in a module that imports no framework, and each adapter
10
+ * keeps only its own effects.
11
+ *
12
+ * Four adapters used to hold a copy of the whole provider: React Router beside TanStack
13
+ * React, Solid Router beside TanStack Solid. Neither member of a pair could import the
14
+ * other, because each declares its own router as a `peerDependency`. This module is the
15
+ * home they all already depend on, and it stays framework-agnostic, so a consumer of
16
+ * `@xmachines/play-router` gains no framework dependency of any kind.
17
+ *
18
+ * @see [Multi-router integration](../../docs/examples/multi-router-integration.md)
19
+ */
20
+ import { asCleanup, DISPOSE } from "@xmachines/play";
21
+ import { normalizeBasePath } from "./base-path.js";
22
+ /**
23
+ * Tells you whether a bridge can move its mount.
24
+ *
25
+ * {@link PlayRouterBridgeConstructor} asks for a `RouterBridge`, the published
26
+ * contract, so a consumer bridge that never heard of `basePath` still compiles. Such a
27
+ * bridge ignores the prefix, exactly as it did before the option existed, so the probe
28
+ * runs at run time rather than in the type system.
29
+ */
30
+ export function isMountableBridge(bridge) {
31
+ // `!= null`, and not `!== null`: a binding that has not opened its bridge yet holds
32
+ // `undefined` as readily as `null`, and a strict test then read the property of it and
33
+ // threw a TypeError out of an effect.
34
+ return bridge != null && typeof bridge.setBasePath === "function";
35
+ }
36
+ /**
37
+ * A key that changes when the MOUNT changes, and never when its identity changes.
38
+ *
39
+ * The comparison is BY VALUE, because `basePathParams` is normally an inline object
40
+ * literal: its identity changes on every render while the prefix does not, and a
41
+ * provider that watched the identity would move the mount on each render.
42
+ *
43
+ * @returns A string to compare, or to give to the dependency list of an effect.
44
+ */
45
+ export function mountKey(basePath, basePathParams) {
46
+ // The entries are SORTED before they are joined, because `JSON.stringify` follows the
47
+ // order of the properties: a params object that a host rebuilds in another order —
48
+ // `{ ...defaults, machineId }`, or one derived from a Map — gave a different key for
49
+ // the same mount, and every render then re-resolved the prefix.
50
+ //
51
+ // Each name, each value AND the prefix are escaped, because a raw join ALIASES. With
52
+ // "=" and "&" as literal separators, `{ a: "1&b=2" }` and `{ a: "1", b: "2" }` gave
53
+ // one key for two different mounts: the provider then saw no change, never
54
+ // re-pointed, and the bridge kept writing and stripping the old prefix — so every
55
+ // location read as foreign and the machine went silent.
56
+ // An absent value counts as an absent ENTRY, exactly as `resolveBasePath` reads it: a
57
+ // host that spreads an optional value — `{ ...defaults, region }` with no region —
58
+ // hands over `{ region: undefined }`, and `String(undefined)` put the text
59
+ // "undefined" in the key. That key differed from the key of the same mount without
60
+ // the entry, so every render re-pointed a bridge that had not moved.
61
+ const entries = Object.entries(basePathParams ?? {})
62
+ .filter(([, value]) => value !== undefined && value !== null)
63
+ .map(([name, value]) => `${encodeURIComponent(name)}=${encodeURIComponent(String(value))}`)
64
+ .toSorted();
65
+ return `${encodeURIComponent(basePath ?? "")}|${entries.join("&")}`;
66
+ }
67
+ /**
68
+ * Builds a bridge, connects it, and hands back the way to close it.
69
+ *
70
+ * The order is the contract: the constructor applies the mount, `connect()` runs the
71
+ * first synchronization against it, and `close()` disconnects exactly the bridge that
72
+ * this call opened. A provider that closed "the current bridge" instead would
73
+ * disconnect a later one when two mounts overlap.
74
+ *
75
+ * @returns The bridge, and a `close` that disconnects it one time.
76
+ */
77
+ export function openProviderBridge(BridgeCtor, { router, actor, routeMap, basePath, basePathParams }) {
78
+ const bridge = new BridgeCtor(router, actor, routeMap, { basePath, basePathParams });
79
+ // A bridge on the three-argument signature drops the options object, and
80
+ // `PlayRouterBridgeConstructor` accepts one: it asks for the published `RouterBridge`,
81
+ // so a consumer bridge that never heard of `basePath` still compiles. Such a bridge
82
+ // then claims the WHOLE router, and its correction of an unknown path drags the user
83
+ // off every page the host owns — which is the opposite of what a prefix was asked
84
+ // for. Say so, once, rather than let it fail silently.
85
+ // `normalizeBasePath` decides what "no prefix" means, and no list of literals here
86
+ // does: an absent value, `""`, `"/"`, and `"//"` all normalize to `""` — see
87
+ // `BasePathOptions.basePath`. A hand-written list warned that a prefix was ignored for
88
+ // a caller that asked for no prefix at all, and it had to be kept in step with that
89
+ // function by hand.
90
+ //
91
+ // The probe of the bridge runs FIRST, so the normalization reaches a mountable bridge
92
+ // never: the constructor of such a bridge has refused an invalid prefix already, and a
93
+ // second refusal here would replace its error with an identical one from another
94
+ // stack. For a bridge with no mount the normalization can still throw, and a prefix
95
+ // that it refuses is a prefix all the same — the warning is the answer, not the throw.
96
+ if (!isMountableBridge(bridge) && hasPrefix(basePath)) {
97
+ console.warn(`[@xmachines/play-router] The provider received basePath "${basePath}", and ${BridgeCtor.name || "the bridge"} takes no mount: it has no setBasePath(), so the prefix is ignored and the bridge claims the whole router. Extend RouterBridgeBase, or drop the basePath prop.`);
98
+ }
99
+ void bridge.connect();
100
+ return {
101
+ bridge,
102
+ close: () => {
103
+ void bridge.disconnect();
104
+ },
105
+ };
106
+ }
107
+ /**
108
+ * Tells you whether a `basePath` option asks for a prefix at all.
109
+ *
110
+ * An absent value, `""`, `"/"`, and `"//"` all mean "no prefix", and
111
+ * {@link normalizeBasePath} is the one place that says so. A prefix that the function
112
+ * REFUSES is still a prefix that the caller asked for, so the refusal counts as `true`
113
+ * here: the caller learns that its bridge ignores the option, which is the message that
114
+ * this test exists to raise.
115
+ */
116
+ function hasPrefix(basePath) {
117
+ try {
118
+ return normalizeBasePath(basePath) !== "";
119
+ }
120
+ catch {
121
+ return true;
122
+ }
123
+ }
124
+ /**
125
+ * Moves the mount of a live bridge, and rebuilds nothing.
126
+ *
127
+ * The call is safe directly after the open: it resolves to the prefix that the
128
+ * constructor applied already, and `setBasePath` then returns at once. A `null` bridge,
129
+ * and a bridge with no mount API, are both no-ops.
130
+ */
131
+ export function repointProviderBridge(bridge, basePath, basePathParams) {
132
+ if (isMountableBridge(bridge))
133
+ bridge.setBasePath(basePath, basePathParams);
134
+ }
135
+ /**
136
+ * Wraps a live bridge in the {@link RouterConnection} that `connectRouter` returns.
137
+ *
138
+ * @param bridge - The bridge that `connectRouter` built and connected.
139
+ */
140
+ export function createRouterConnection(bridge) {
141
+ const disconnect = () => {
142
+ bridge.disconnect();
143
+ };
144
+ // `asCleanup` publishes the release under the dispose key, so a scope may own the
145
+ // connection. It is the ONE place that knows how for a release that is a FUNCTION: it
146
+ // writes `DISPOSE` and, where a polyfill of Explicit Resource Management installed the
147
+ // well-known symbol after this module evaluated, `Symbol.dispose` beside it, and it
148
+ // gives both keys a descriptor that a test double, a wrapper, and a later `asCleanup`
149
+ // can still redefine. A second copy of that rule here would be a second chance to
150
+ // disagree with it.
151
+ //
152
+ // An object LITERAL writes the key itself — `createRouter` and `createBrowserHistory`
153
+ // of play-dom-router each do. `asCleanup` takes a callable, and those two values are
154
+ // no callables, so the note of `packages/play/src/disposable.ts` covers them instead:
155
+ // a literal carries the key that existed when it was defined, so a polyfill belongs
156
+ // before the first import of `@xmachines/play`.
157
+ return Object.defineProperties(asCleanup(disconnect), {
158
+ disconnect: { value: disconnect, enumerable: true },
159
+ basePath: { get: () => bridge.basePath, enumerable: true },
160
+ basePathParams: { get: () => bridge.basePathParams, enumerable: true },
161
+ setBasePath: {
162
+ value: (basePath, basePathParams) => bridge.setBasePath(basePath, basePathParams),
163
+ enumerable: true,
164
+ },
165
+ });
166
+ }
167
+ //# sourceMappingURL=provider-lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-lifecycle.js","sourceRoot":"","sources":["../src/provider-lifecycle.ts"],"names":[],"mappings":"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"}
package/dist/query.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import type { Graph } from "@statelyai/graph";
2
2
  import type { RouteTree, RouteNode, MachineNodeData, MachineEdgeData } from "./types.js";
3
+ import type { RouteMapping } from "./base-route-map.js";
4
+ import { type BasePathOptions } from "./base-path.js";
3
5
  /**
4
6
  * Returns every route of a navigation from the given state
5
7
  *
@@ -99,4 +101,51 @@ export declare const getTransitionReachableRoutes: (graph: Graph<MachineNodeData
99
101
  * ```
100
102
  */
101
103
  export declare const isRouteReachable: (graph: Graph<MachineNodeData, MachineEdgeData>, fromStateId: string, toStateId: string) => boolean;
104
+ /**
105
+ * Returns the `{ stateId, path }` entries that a host router needs, so that it can
106
+ * register the routes of a machine under a base path, and remove them again later.
107
+ *
108
+ * This function makes the routes dynamic. `getStateIdByPath` and `getPathByStateId`
109
+ * answer the question "what is this URL?" at the moment of a navigation, but a host
110
+ * that declares real route objects — a route tree of TanStack, a `RouteObject[]`, an
111
+ * `addRoute` call of vue-router — needs the LIST in advance, and it needs that list
112
+ * with the prefix of the mount. A registration of these paths, and a removal of them
113
+ * when the machine unloads, keeps the route table of the host in step with the mount.
114
+ *
115
+ * `basePathParams` decides the form of each path, and both forms are useful:
116
+ * - **Given** → a concrete path (`/abc123/play/profile/:userId`), for a route that
117
+ * the host adds at run time, after a loader resolved the mount.
118
+ * - **Absent** → the pattern stays (`/:machineId/play/profile/:userId`), for a static
119
+ * route declaration of the host.
120
+ *
121
+ * The function ALWAYS keeps each `:param` segment that the machine declares itself:
122
+ * those are the params that the route of the host must match.
123
+ *
124
+ * @param tree - The route tree of extractMachineRoutes()
125
+ * @param options - The optional `{ basePath, basePathParams }`, the same option as the bridge takes.
126
+ * Each `stateId` comes from the route tree, so it carries NO `#` prefix. A host that
127
+ * keys its route table on the target of a `play.route` event adds the `#` itself,
128
+ * because the event always carries the prefixed form.
129
+ *
130
+ * @returns One entry for each state with a route, in the order of the route tree.
131
+ * @throws {InvalidBasePathError} For a base path that resolves to one concrete path never.
132
+ * @throws {MissingBasePathParamError} When `basePathParams` is present and it omits a `:param`.
133
+ *
134
+ * @example
135
+ * ```typescript
136
+ * const tree = extractMachineRoutes(machine);
137
+ *
138
+ * // Concrete, for a route that the host adds after a loader resolved the mount
139
+ * getRouteMappings(tree, { basePath: "/:machineId/play", basePathParams: { machineId: "abc123" } });
140
+ * // [
141
+ * // { stateId: "app.home", path: "/abc123/play" },
142
+ * // { stateId: "app.profile", path: "/abc123/play/profile/:userId" },
143
+ * // ]
144
+ *
145
+ * // The pattern, for a static route declaration of the host
146
+ * getRouteMappings(tree, { basePath: "/:machineId/play" });
147
+ * // [{ stateId: "app.home", path: "/:machineId/play" }, ...]
148
+ * ```
149
+ */
150
+ export declare const getRouteMappings: (tree: RouteTree, options?: BasePathOptions) => RouteMapping[];
102
151
  //# sourceMappingURL=query.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAa,MAAM,kBAAkB,CAAC;AAEzD,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAqBzF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,SAAS,EAAE,SAAS,MAAM,KAAG,SAAS,EA0B9E,CAAC;AAkBF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,iBAAiB,GAAI,MAAM,SAAS,KAAG,SAAS,EAW5D,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,SAAS,EAAE,MAAM,MAAM,KAAG,OAE3D,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,4BAA4B,GACxC,OAAO,KAAK,CAAC,eAAe,EAAE,eAAe,CAAC,EAC9C,SAAS,MAAM,KACb,MAAM,EAER,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,gBAAgB,GAC5B,OAAO,KAAK,CAAC,eAAe,EAAE,eAAe,CAAC,EAC9C,aAAa,MAAM,EACnB,WAAW,MAAM,KACf,OAKF,CAAC"}
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAa,MAAM,kBAAkB,CAAC;AAEzD,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AACzF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAIN,KAAK,eAAe,EACpB,MAAM,gBAAgB,CAAC;AAqBxB;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,SAAS,EAAE,SAAS,MAAM,KAAG,SAAS,EA0B9E,CAAC;AAkBF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,iBAAiB,GAAI,MAAM,SAAS,KAAG,SAAS,EAW5D,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,SAAS,EAAE,MAAM,MAAM,KAAG,OAE3D,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,4BAA4B,GACxC,OAAO,KAAK,CAAC,eAAe,EAAE,eAAe,CAAC,EAC9C,SAAS,MAAM,KACb,MAAM,EAER,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,gBAAgB,GAC5B,OAAO,KAAK,CAAC,eAAe,EAAE,eAAe,CAAC,EAC9C,aAAa,MAAM,EACnB,WAAW,MAAM,KACf,OAKF,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,eAAO,MAAM,gBAAgB,GAAI,MAAM,SAAS,EAAE,UAAU,eAAe,KAAG,YAAY,EAazF,CAAC"}
package/dist/query.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { getSuccessors, hasNode, hasPath } from "@statelyai/graph";
2
+ import { joinBasePath, normalizeBasePath, resolveBasePath, } from "./base-path.js";
2
3
  /**
3
4
  * Returns the successor nodes of a state that have a route: the states of a direct
4
5
  * transition edge that hold a `meta.route` field.
@@ -166,4 +167,62 @@ export const isRouteReachable = (graph, fromStateId, toStateId) => {
166
167
  ? hasPath(graph, fromStateId, toStateId)
167
168
  : false;
168
169
  };
170
+ /**
171
+ * Returns the `{ stateId, path }` entries that a host router needs, so that it can
172
+ * register the routes of a machine under a base path, and remove them again later.
173
+ *
174
+ * This function makes the routes dynamic. `getStateIdByPath` and `getPathByStateId`
175
+ * answer the question "what is this URL?" at the moment of a navigation, but a host
176
+ * that declares real route objects — a route tree of TanStack, a `RouteObject[]`, an
177
+ * `addRoute` call of vue-router — needs the LIST in advance, and it needs that list
178
+ * with the prefix of the mount. A registration of these paths, and a removal of them
179
+ * when the machine unloads, keeps the route table of the host in step with the mount.
180
+ *
181
+ * `basePathParams` decides the form of each path, and both forms are useful:
182
+ * - **Given** → a concrete path (`/abc123/play/profile/:userId`), for a route that
183
+ * the host adds at run time, after a loader resolved the mount.
184
+ * - **Absent** → the pattern stays (`/:machineId/play/profile/:userId`), for a static
185
+ * route declaration of the host.
186
+ *
187
+ * The function ALWAYS keeps each `:param` segment that the machine declares itself:
188
+ * those are the params that the route of the host must match.
189
+ *
190
+ * @param tree - The route tree of extractMachineRoutes()
191
+ * @param options - The optional `{ basePath, basePathParams }`, the same option as the bridge takes.
192
+ * Each `stateId` comes from the route tree, so it carries NO `#` prefix. A host that
193
+ * keys its route table on the target of a `play.route` event adds the `#` itself,
194
+ * because the event always carries the prefixed form.
195
+ *
196
+ * @returns One entry for each state with a route, in the order of the route tree.
197
+ * @throws {InvalidBasePathError} For a base path that resolves to one concrete path never.
198
+ * @throws {MissingBasePathParamError} When `basePathParams` is present and it omits a `:param`.
199
+ *
200
+ * @example
201
+ * ```typescript
202
+ * const tree = extractMachineRoutes(machine);
203
+ *
204
+ * // Concrete, for a route that the host adds after a loader resolved the mount
205
+ * getRouteMappings(tree, { basePath: "/:machineId/play", basePathParams: { machineId: "abc123" } });
206
+ * // [
207
+ * // { stateId: "app.home", path: "/abc123/play" },
208
+ * // { stateId: "app.profile", path: "/abc123/play/profile/:userId" },
209
+ * // ]
210
+ *
211
+ * // The pattern, for a static route declaration of the host
212
+ * getRouteMappings(tree, { basePath: "/:machineId/play" });
213
+ * // [{ stateId: "app.home", path: "/:machineId/play" }, ...]
214
+ * ```
215
+ */
216
+ export const getRouteMappings = (tree, options) => {
217
+ // With the params, resolve the prefix to a concrete path. Without them, keep the
218
+ // pattern, so that a host declares its routes statically and substitutes each param
219
+ // itself.
220
+ const basePath = options?.basePathParams === undefined
221
+ ? normalizeBasePath(options?.basePath)
222
+ : resolveBasePath(options.basePath, options.basePathParams).path;
223
+ return getRoutableRoutes(tree).map((node) => ({
224
+ stateId: node.stateId,
225
+ path: joinBasePath(basePath, node.fullPath),
226
+ }));
227
+ };
169
228
  //# sourceMappingURL=query.js.map
package/dist/query.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}