@jayalfredprufrock/mobx-toolbox 0.6.5 → 0.7.1
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/dist/router.d.mts +168 -59
- package/dist/router.d.mts.map +1 -1
- package/dist/router.mjs +256 -54
- package/dist/router.mjs.map +1 -1
- package/package.json +5 -2
package/dist/router.d.mts
CHANGED
|
@@ -1,50 +1,6 @@
|
|
|
1
1
|
import React$1 from "react";
|
|
2
2
|
import { History, Location } from "history";
|
|
3
3
|
|
|
4
|
-
//#region src/router/outlet.d.ts
|
|
5
|
-
interface OutletConfig {
|
|
6
|
-
component?: Component | LazyComponent;
|
|
7
|
-
loader?: Loader;
|
|
8
|
-
}
|
|
9
|
-
type RouteSegmentState = "preloading" | "loading" | "error" | "ready";
|
|
10
|
-
declare const DefaultOutlet: Component;
|
|
11
|
-
declare class Outlet {
|
|
12
|
-
readonly config: OutletConfig;
|
|
13
|
-
state: RouteSegmentState;
|
|
14
|
-
promise: Promise<unknown> | undefined;
|
|
15
|
-
data: unknown;
|
|
16
|
-
component: Component | undefined;
|
|
17
|
-
get Component(): Component | undefined;
|
|
18
|
-
constructor(config: OutletConfig);
|
|
19
|
-
load(route: Route): Promise<void>;
|
|
20
|
-
setData(data: unknown): void;
|
|
21
|
-
setState(state: RouteSegmentState): void;
|
|
22
|
-
private loadData;
|
|
23
|
-
private loadComponent;
|
|
24
|
-
}
|
|
25
|
-
//#endregion
|
|
26
|
-
//#region src/router/route.d.ts
|
|
27
|
-
interface RouteConfig$1 {
|
|
28
|
-
path: string;
|
|
29
|
-
outlets: Outlet[];
|
|
30
|
-
guards: Guard[];
|
|
31
|
-
context?: Obj;
|
|
32
|
-
layout?: Component;
|
|
33
|
-
params: Obj;
|
|
34
|
-
}
|
|
35
|
-
declare class Route {
|
|
36
|
-
readonly path: string;
|
|
37
|
-
readonly outlets: Outlet[];
|
|
38
|
-
readonly guards: Guard[];
|
|
39
|
-
readonly context: Obj;
|
|
40
|
-
readonly params: Obj;
|
|
41
|
-
readonly layout?: Component;
|
|
42
|
-
get data(): Obj;
|
|
43
|
-
constructor(def: RouteConfig$1);
|
|
44
|
-
guard(): Promise<void>;
|
|
45
|
-
load(): Promise<void>;
|
|
46
|
-
}
|
|
47
|
-
//#endregion
|
|
48
4
|
//#region src/router/symbols.d.ts
|
|
49
5
|
declare const CONTEXT: unique symbol;
|
|
50
6
|
declare const LAYOUT: unique symbol;
|
|
@@ -52,6 +8,7 @@ declare const WRAPPER: unique symbol;
|
|
|
52
8
|
declare const LOAD: unique symbol;
|
|
53
9
|
declare const GUARD: unique symbol;
|
|
54
10
|
declare const PAGE: unique symbol;
|
|
11
|
+
declare const ERROR: unique symbol;
|
|
55
12
|
declare const REDIRECT: unique symbol;
|
|
56
13
|
//#endregion
|
|
57
14
|
//#region src/router/types.d.ts
|
|
@@ -60,12 +17,29 @@ type LazyComponent = () => Promise<any>;
|
|
|
60
17
|
type Obj<T = any> = Record<string, T>;
|
|
61
18
|
type Loader = (route: Route) => Promise<any>;
|
|
62
19
|
type Guard = (route: Route) => Promise<void>;
|
|
20
|
+
/** The props every `[ERROR]` component receives. */
|
|
21
|
+
interface ErrorComponentProps {
|
|
22
|
+
route: Route;
|
|
23
|
+
error: RouterError;
|
|
24
|
+
}
|
|
25
|
+
/** @internal a guard together with the route level that declared it */
|
|
26
|
+
interface GuardEntry {
|
|
27
|
+
guard: Guard;
|
|
28
|
+
depth: number;
|
|
29
|
+
}
|
|
30
|
+
/** @internal per-level snapshot used to build synthetic error routes */
|
|
31
|
+
interface MatchLevel {
|
|
32
|
+
wrapper?: Component;
|
|
33
|
+
layout?: Component;
|
|
34
|
+
errorComponent?: Component;
|
|
35
|
+
}
|
|
63
36
|
interface RouteConfig {
|
|
64
37
|
[CONTEXT]?: Obj;
|
|
65
38
|
[LAYOUT]?: Component;
|
|
66
39
|
[WRAPPER]?: Component;
|
|
67
40
|
[GUARD]?: Guard;
|
|
68
41
|
[LOAD]?: Loader;
|
|
42
|
+
[ERROR]?: Component;
|
|
69
43
|
}
|
|
70
44
|
/**
|
|
71
45
|
* A route page definition.
|
|
@@ -134,9 +108,155 @@ type DynamicRoutePath = Extract<RoutePath, `${string}:${string}`>;
|
|
|
134
108
|
type StaticRoutePath = Exclude<RoutePath, `${string}:${string}`>;
|
|
135
109
|
/********************************************************************************* */
|
|
136
110
|
type JoinSegments<S1, S2> = `/${S1 extends string ? S1 : ""}${S2 extends string ? S2 : ""}`;
|
|
111
|
+
type SegmentName<S> = S extends `$${infer Param}` ? `:${Param}` : S;
|
|
137
112
|
type ExtractParam<P, NextPart> = P extends `:${infer Param}` ? Record<Param, string> & NextPart : NextPart;
|
|
138
113
|
type ExtractParams<P> = P extends `${infer S1}/${infer Rest}` ? ExtractParam<S1, ExtractParams<Rest>> : ExtractParam<P, {}>;
|
|
139
|
-
type ExtractPaths<R> = { [S in keyof R]: S extends string ? S extends "index" ? "/" : R[S] extends Leaf ? `/${S}` : JoinSegments<S
|
|
114
|
+
type ExtractPaths<R> = { [S in keyof R]: S extends string ? S extends "index" ? "/" : R[S] extends Leaf ? `/${SegmentName<S> extends string ? SegmentName<S> : ""}` : JoinSegments<SegmentName<S>, ExtractPaths<R[S]>> : never }[keyof R];
|
|
115
|
+
//#endregion
|
|
116
|
+
//#region src/router/route.d.ts
|
|
117
|
+
interface RouteConfig$1 {
|
|
118
|
+
path: string;
|
|
119
|
+
outlets: Outlet[];
|
|
120
|
+
guards: GuardEntry[];
|
|
121
|
+
levels: MatchLevel[];
|
|
122
|
+
context?: Obj;
|
|
123
|
+
layout?: Component;
|
|
124
|
+
params: Obj;
|
|
125
|
+
error?: RouterError;
|
|
126
|
+
}
|
|
127
|
+
declare class Route {
|
|
128
|
+
readonly path: string;
|
|
129
|
+
readonly outlets: Outlet[];
|
|
130
|
+
readonly guards: Guard[];
|
|
131
|
+
readonly context: Obj;
|
|
132
|
+
readonly params: Obj;
|
|
133
|
+
readonly layout?: Component;
|
|
134
|
+
/** set on synthetic error routes; the error being rendered */
|
|
135
|
+
readonly error?: RouterError;
|
|
136
|
+
/** @internal */
|
|
137
|
+
readonly guardEntries: GuardEntry[];
|
|
138
|
+
/** @internal */
|
|
139
|
+
readonly levels: MatchLevel[];
|
|
140
|
+
get data(): Obj;
|
|
141
|
+
constructor(def: RouteConfig$1);
|
|
142
|
+
guard(): Promise<void>;
|
|
143
|
+
load(): Promise<void>;
|
|
144
|
+
}
|
|
145
|
+
//#endregion
|
|
146
|
+
//#region src/router/outlet.d.ts
|
|
147
|
+
interface OutletConfig {
|
|
148
|
+
component?: Component | LazyComponent;
|
|
149
|
+
loader?: Loader;
|
|
150
|
+
errorComponent?: Component;
|
|
151
|
+
}
|
|
152
|
+
type RouteSegmentState = "preloading" | "loading" | "error" | "ready";
|
|
153
|
+
declare const DefaultOutlet: Component;
|
|
154
|
+
declare class Outlet {
|
|
155
|
+
readonly config: OutletConfig;
|
|
156
|
+
state: RouteSegmentState;
|
|
157
|
+
promise: Promise<unknown> | undefined;
|
|
158
|
+
data: unknown;
|
|
159
|
+
error: RouterError | undefined;
|
|
160
|
+
component: Component | undefined;
|
|
161
|
+
get Component(): Component | undefined;
|
|
162
|
+
constructor(config: OutletConfig);
|
|
163
|
+
load(route: Route): Promise<void>;
|
|
164
|
+
setData(data: unknown): void;
|
|
165
|
+
setError(error: RouterError): void;
|
|
166
|
+
setState(state: RouteSegmentState): void;
|
|
167
|
+
private loadData;
|
|
168
|
+
private loadComponent;
|
|
169
|
+
}
|
|
170
|
+
//#endregion
|
|
171
|
+
//#region src/router/make-routes.d.ts
|
|
172
|
+
interface MatchState {
|
|
173
|
+
segments: string[];
|
|
174
|
+
context: Obj;
|
|
175
|
+
params: Obj;
|
|
176
|
+
outlets: (Outlet | undefined)[];
|
|
177
|
+
guards: GuardEntry[];
|
|
178
|
+
levels: MatchLevel[];
|
|
179
|
+
layout?: Component;
|
|
180
|
+
errorComponent?: Component;
|
|
181
|
+
}
|
|
182
|
+
declare const makeRoute: (matchState: MatchState) => Route;
|
|
183
|
+
/**
|
|
184
|
+
* Builds the synthetic route rendered when navigation fails. Bubbles
|
|
185
|
+
* from the failing level (`error.depth`, defaulting to the deepest
|
|
186
|
+
* matched level) to the nearest `[ERROR]` component, preserving the
|
|
187
|
+
* `[LAYOUT]` and `[WRAPPER]`s accumulated up to that level. Ancestor
|
|
188
|
+
* `[LOAD]` loaders are intentionally not run — error routes never
|
|
189
|
+
* fetch data.
|
|
190
|
+
*/
|
|
191
|
+
declare const makeErrorRoute: (error: RouterError, pathname: string, source?: {
|
|
192
|
+
levels: MatchLevel[];
|
|
193
|
+
params: Obj;
|
|
194
|
+
context: Obj;
|
|
195
|
+
}) => Route;
|
|
196
|
+
declare const matchRoute: (path: string, routeDef: Routes, matchState?: MatchState) => Route;
|
|
197
|
+
declare const makeRoutes: () => <R extends Routes>(routes: R) => R;
|
|
198
|
+
//#endregion
|
|
199
|
+
//#region src/router/errors.d.ts
|
|
200
|
+
type RouterErrorType = "NOT_FOUND" | "GUARD" | "LOAD" | "RENDER";
|
|
201
|
+
interface RouterErrorOptions {
|
|
202
|
+
message?: string;
|
|
203
|
+
cause?: unknown;
|
|
204
|
+
path?: string;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* The single error type surfaced to `[ERROR]` components. `type`
|
|
208
|
+
* discriminates the failure source; when the router wraps an
|
|
209
|
+
* application-level error (thrown by a guard or loader), the original
|
|
210
|
+
* is preserved on the standard `cause` property.
|
|
211
|
+
*
|
|
212
|
+
* Guards and loaders may also throw `RouterError` directly — e.g.
|
|
213
|
+
* `throw new RouterError("NOT_FOUND")` from a loader when an entity
|
|
214
|
+
* doesn't exist — and it passes through unwrapped.
|
|
215
|
+
*/
|
|
216
|
+
declare class RouterError extends Error {
|
|
217
|
+
readonly type: RouterErrorType;
|
|
218
|
+
readonly path?: string;
|
|
219
|
+
/** @internal matched-prefix state captured when the matcher throws NOT_FOUND */
|
|
220
|
+
state?: MatchState;
|
|
221
|
+
/** @internal level index of the failing guard, for depth-aware bubbling */
|
|
222
|
+
depth?: number;
|
|
223
|
+
constructor(type: RouterErrorType, options?: RouterErrorOptions);
|
|
224
|
+
}
|
|
225
|
+
//#endregion
|
|
226
|
+
//#region src/router/components/error.d.ts
|
|
227
|
+
/**
|
|
228
|
+
* Rendered when an error occurs and no `[ERROR]` component is defined
|
|
229
|
+
* on the matched prefix. Deliberately minimal and dependency-free —
|
|
230
|
+
* define a root-level `[ERROR]` to replace it.
|
|
231
|
+
*/
|
|
232
|
+
declare const DefaultErrorPage: Component;
|
|
233
|
+
interface RouteErrorBoundaryProps {
|
|
234
|
+
route: Route;
|
|
235
|
+
fallback: Component;
|
|
236
|
+
children?: React$1.ReactNode;
|
|
237
|
+
}
|
|
238
|
+
interface RouteErrorBoundaryState {
|
|
239
|
+
error?: RouterError;
|
|
240
|
+
route?: Route;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Catches render-time crashes in page and `[WRAPPER]` components and
|
|
244
|
+
* renders the nearest `[ERROR]` component with `type: "RENDER"`. Mounted
|
|
245
|
+
* inside the `[LAYOUT]` so the layout survives page crashes; crashes in
|
|
246
|
+
* the layout itself (or in the fallback) propagate out of `<Router>` by
|
|
247
|
+
* design — those are developer bugs that should stay loud.
|
|
248
|
+
*
|
|
249
|
+
* Deliberately NOT keyed by location: the boundary must be transparent
|
|
250
|
+
* to reconciliation (a key would remount the entire subtree and re-fire
|
|
251
|
+
* every effect on each navigation). Instead, a captured error is cleared
|
|
252
|
+
* when a new Route object arrives.
|
|
253
|
+
*/
|
|
254
|
+
declare class RouteErrorBoundary extends React$1.Component<RouteErrorBoundaryProps, RouteErrorBoundaryState> {
|
|
255
|
+
state: RouteErrorBoundaryState;
|
|
256
|
+
static getDerivedStateFromError(cause: unknown): RouteErrorBoundaryState;
|
|
257
|
+
static getDerivedStateFromProps(props: RouteErrorBoundaryProps, state: RouteErrorBoundaryState): Partial<RouteErrorBoundaryState> | null;
|
|
258
|
+
render(): React$1.ReactNode;
|
|
259
|
+
}
|
|
140
260
|
//#endregion
|
|
141
261
|
//#region src/router/components/link.d.ts
|
|
142
262
|
type LinkComponentProps<C extends React$1.ElementType> = Omit<React$1.ComponentProps<C>, "ref" | "exact" | "to" | "params" | "onClick" | "asChild">;
|
|
@@ -182,6 +302,8 @@ declare class RouterStore {
|
|
|
182
302
|
doesPathMatch<P extends RoutePath>(path: P, exact?: boolean): boolean;
|
|
183
303
|
navigate<P extends RoutePath>(options: NavigateOptions<P>): void;
|
|
184
304
|
_navigate<P extends RoutePath>(options: NavigateOptions<P>): void;
|
|
305
|
+
private resolveLocation;
|
|
306
|
+
private isCurrentLocation;
|
|
185
307
|
setQueryParam(param: string, value: string): void;
|
|
186
308
|
removeQueryParam(param: string): string | undefined;
|
|
187
309
|
setLocation(location: Location): Promise<void>;
|
|
@@ -204,19 +326,6 @@ declare const Router: (({
|
|
|
204
326
|
displayName: string;
|
|
205
327
|
};
|
|
206
328
|
//#endregion
|
|
207
|
-
//#region src/router/make-routes.d.ts
|
|
208
|
-
interface MatchState {
|
|
209
|
-
segments: string[];
|
|
210
|
-
context: Obj;
|
|
211
|
-
params: Obj;
|
|
212
|
-
outlets: (Outlet | undefined)[];
|
|
213
|
-
guards: (Guard | undefined)[];
|
|
214
|
-
layout?: Component;
|
|
215
|
-
}
|
|
216
|
-
declare const makeRoute: (matchState: MatchState) => Route;
|
|
217
|
-
declare const matchRoute: (path: string, routeDef: Routes, matchState?: MatchState) => Route;
|
|
218
|
-
declare const makeRoutes: () => <R extends Routes>(routes: R) => R;
|
|
219
|
-
//#endregion
|
|
220
329
|
//#region src/router/redirect.d.ts
|
|
221
330
|
declare class Redirect<P extends RoutePath> {
|
|
222
331
|
readonly options: NavigateOptions<P>;
|
|
@@ -232,5 +341,5 @@ declare const isRedirect: (data: any) => data is Redirector;
|
|
|
232
341
|
declare const isLeaf: (data: any) => data is Leaf;
|
|
233
342
|
declare const isLazyComponent: (data: any) => data is LazyComponent;
|
|
234
343
|
//#endregion
|
|
235
|
-
export { CONTEXT, Component, DefaultOutlet, DynamicRoutePath, ExtractParam, ExtractParams, ExtractPaths, GUARD, Guard, HasParam, JoinSegments, LAYOUT, LOAD, LazyComponent, Leaf, LinkComponent, LinkPropsBase, Loader, MatchState, MobxRenderSegment, MobxRouter, MobxRouterConfig, MobxRouterRoutes, Navigate, NavigateOptions, Obj, Outlet, OutletConfig, PAGE, Page, PassThrough, REDIRECT, Redirect, Redirector, Route, RouteConfig, RoutePath, RouteSegmentState, Router, RouterOutlet, RouterProps, RouterStore, Routes, StaticRoutePath, WRAPPER, WithToAndParams, isComponent, isLazyComponent, isLeaf, isPage, isRedirect, makeLinkComponent, makeRoute, makeRoutes, matchRoute, redirect, resolvePath, routerContext, useRouter };
|
|
344
|
+
export { CONTEXT, Component, DefaultErrorPage, DefaultOutlet, DynamicRoutePath, ERROR, ErrorComponentProps, ExtractParam, ExtractParams, ExtractPaths, GUARD, Guard, GuardEntry, HasParam, JoinSegments, LAYOUT, LOAD, LazyComponent, Leaf, LinkComponent, LinkPropsBase, Loader, MatchLevel, MatchState, MobxRenderSegment, MobxRouter, MobxRouterConfig, MobxRouterRoutes, Navigate, NavigateOptions, Obj, Outlet, OutletConfig, PAGE, Page, PassThrough, REDIRECT, Redirect, Redirector, Route, RouteConfig, RouteErrorBoundary, RouteErrorBoundaryProps, RoutePath, RouteSegmentState, Router, RouterError, RouterErrorOptions, RouterErrorType, RouterOutlet, RouterProps, RouterStore, Routes, SegmentName, StaticRoutePath, WRAPPER, WithToAndParams, isComponent, isLazyComponent, isLeaf, isPage, isRedirect, makeErrorRoute, makeLinkComponent, makeRoute, makeRoutes, matchRoute, redirect, resolvePath, routerContext, useRouter };
|
|
236
345
|
//# sourceMappingURL=router.d.mts.map
|
package/dist/router.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.d.mts","names":[],"sources":["../src/router/outlet.tsx","../src/router/
|
|
1
|
+
{"version":3,"file":"router.d.mts","names":[],"sources":["../src/router/symbols.ts","../src/router/types.ts","../src/router/route.tsx","../src/router/outlet.tsx","../src/router/make-routes.tsx","../src/router/errors.ts","../src/router/components/error.tsx","../src/router/components/link.tsx","../src/router/components/navigate.tsx","../src/router/router.store.ts","../src/router/components/router.tsx","../src/router/redirect.ts","../src/router/util.ts"],"mappings":"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"}
|
package/dist/router.mjs
CHANGED
|
@@ -1,9 +1,89 @@
|
|
|
1
1
|
import { observer } from "mobx-react-lite";
|
|
2
2
|
import React, { createContext, useCallback, useContext, useLayoutEffect } from "react";
|
|
3
3
|
import { action, computed, makeAutoObservable, makeObservable, observable, runInAction } from "mobx";
|
|
4
|
-
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
import { createBrowserHistory } from "history";
|
|
6
6
|
|
|
7
|
+
//#region src/router/errors.ts
|
|
8
|
+
const defaultMessage = (type, path) => {
|
|
9
|
+
switch (type) {
|
|
10
|
+
case "NOT_FOUND": return path ? `No route matches '${path}'.` : "No matching route.";
|
|
11
|
+
case "GUARD": return "A route guard rejected the navigation.";
|
|
12
|
+
case "LOAD": return "A route loader or lazy component failed.";
|
|
13
|
+
case "RENDER": return "A route component failed to render.";
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* The single error type surfaced to `[ERROR]` components. `type`
|
|
18
|
+
* discriminates the failure source; when the router wraps an
|
|
19
|
+
* application-level error (thrown by a guard or loader), the original
|
|
20
|
+
* is preserved on the standard `cause` property.
|
|
21
|
+
*
|
|
22
|
+
* Guards and loaders may also throw `RouterError` directly — e.g.
|
|
23
|
+
* `throw new RouterError("NOT_FOUND")` from a loader when an entity
|
|
24
|
+
* doesn't exist — and it passes through unwrapped.
|
|
25
|
+
*/
|
|
26
|
+
var RouterError = class extends Error {
|
|
27
|
+
type;
|
|
28
|
+
path;
|
|
29
|
+
/** @internal matched-prefix state captured when the matcher throws NOT_FOUND */
|
|
30
|
+
state;
|
|
31
|
+
/** @internal level index of the failing guard, for depth-aware bubbling */
|
|
32
|
+
depth;
|
|
33
|
+
constructor(type, options) {
|
|
34
|
+
super(options?.message ?? defaultMessage(type, options?.path), { cause: options?.cause });
|
|
35
|
+
this.name = "RouterError";
|
|
36
|
+
this.type = type;
|
|
37
|
+
this.path = options?.path;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/router/components/error.tsx
|
|
43
|
+
/**
|
|
44
|
+
* Rendered when an error occurs and no `[ERROR]` component is defined
|
|
45
|
+
* on the matched prefix. Deliberately minimal and dependency-free —
|
|
46
|
+
* define a root-level `[ERROR]` to replace it.
|
|
47
|
+
*/
|
|
48
|
+
const DefaultErrorPage = ({ error }) => /* @__PURE__ */ jsxs("div", {
|
|
49
|
+
role: "alert",
|
|
50
|
+
children: [/* @__PURE__ */ jsx("h1", { children: error.type === "NOT_FOUND" ? "Page Not Found" : "Something Went Wrong" }), /* @__PURE__ */ jsx("p", { children: error.message })]
|
|
51
|
+
});
|
|
52
|
+
/**
|
|
53
|
+
* Catches render-time crashes in page and `[WRAPPER]` components and
|
|
54
|
+
* renders the nearest `[ERROR]` component with `type: "RENDER"`. Mounted
|
|
55
|
+
* inside the `[LAYOUT]` so the layout survives page crashes; crashes in
|
|
56
|
+
* the layout itself (or in the fallback) propagate out of `<Router>` by
|
|
57
|
+
* design — those are developer bugs that should stay loud.
|
|
58
|
+
*
|
|
59
|
+
* Deliberately NOT keyed by location: the boundary must be transparent
|
|
60
|
+
* to reconciliation (a key would remount the entire subtree and re-fire
|
|
61
|
+
* every effect on each navigation). Instead, a captured error is cleared
|
|
62
|
+
* when a new Route object arrives.
|
|
63
|
+
*/
|
|
64
|
+
var RouteErrorBoundary = class extends React.Component {
|
|
65
|
+
state = {};
|
|
66
|
+
static getDerivedStateFromError(cause) {
|
|
67
|
+
return { error: cause instanceof RouterError ? cause : new RouterError("RENDER", { cause }) };
|
|
68
|
+
}
|
|
69
|
+
static getDerivedStateFromProps(props, state) {
|
|
70
|
+
if (state.route === props.route) return null;
|
|
71
|
+
return {
|
|
72
|
+
route: props.route,
|
|
73
|
+
error: void 0
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
render() {
|
|
77
|
+
if (!this.state.error) return this.props.children;
|
|
78
|
+
const Fallback = this.props.fallback;
|
|
79
|
+
return /* @__PURE__ */ jsx(Fallback, {
|
|
80
|
+
route: this.props.route,
|
|
81
|
+
error: this.state.error
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
//#endregion
|
|
7
87
|
//#region src/router/symbols.ts
|
|
8
88
|
const CONTEXT = Symbol.for("MOBX_ROUTER_CONTEXT");
|
|
9
89
|
const LAYOUT = Symbol.for("MOBX_ROUTER_LAYOUT");
|
|
@@ -11,6 +91,7 @@ const WRAPPER = Symbol.for("MOBX_ROUTER_WRAPPER");
|
|
|
11
91
|
const LOAD = Symbol.for("MOBX_ROUTER_LOAD");
|
|
12
92
|
const GUARD = Symbol.for("MOBX_ROUTER_GUARD");
|
|
13
93
|
const PAGE = Symbol.for("MOBX_ROUTER_PAGE");
|
|
94
|
+
const ERROR = Symbol.for("MOBX_ROUTER_ERROR");
|
|
14
95
|
const REDIRECT = Symbol.for("MOBX_ROUTER_REDIRECT");
|
|
15
96
|
|
|
16
97
|
//#endregion
|
|
@@ -18,7 +99,7 @@ const REDIRECT = Symbol.for("MOBX_ROUTER_REDIRECT");
|
|
|
18
99
|
const resolvePath = (to, params) => {
|
|
19
100
|
return to.replaceAll(/:[^/]*/g, (segment) => {
|
|
20
101
|
const value = params?.[segment.slice(1)];
|
|
21
|
-
if (!value) throw new Error(`Unable to resolve route '${to}.
|
|
102
|
+
if (!value) throw new Error(`Unable to resolve route '${to}'. Parameter '${segment}' not specified.`);
|
|
22
103
|
return value;
|
|
23
104
|
});
|
|
24
105
|
};
|
|
@@ -58,16 +139,22 @@ const RouterOutlet = ({ route, components }) => {
|
|
|
58
139
|
const routerContext = createContext(null);
|
|
59
140
|
const useRouter = () => useContext(routerContext);
|
|
60
141
|
const Router = observer(({ store }) => {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const
|
|
142
|
+
const route = store.activeRoute;
|
|
143
|
+
if (!route) return null;
|
|
144
|
+
const Layout = route.layout ?? PassThrough;
|
|
145
|
+
const outlet = /* @__PURE__ */ jsx(RouterOutlet, {
|
|
146
|
+
route,
|
|
147
|
+
components: route.outlets.map((o) => o.Component)
|
|
148
|
+
});
|
|
149
|
+
const fallback = route.levels.at(-1)?.errorComponent ?? DefaultErrorPage;
|
|
64
150
|
return /* @__PURE__ */ jsx(routerContext.Provider, {
|
|
65
151
|
value: store,
|
|
66
152
|
children: /* @__PURE__ */ jsx(Layout, {
|
|
67
|
-
route
|
|
68
|
-
children: /* @__PURE__ */ jsx(
|
|
69
|
-
route
|
|
70
|
-
|
|
153
|
+
route,
|
|
154
|
+
children: route.error ? outlet : /* @__PURE__ */ jsx(RouteErrorBoundary, {
|
|
155
|
+
route,
|
|
156
|
+
fallback,
|
|
157
|
+
children: outlet
|
|
71
158
|
})
|
|
72
159
|
})
|
|
73
160
|
});
|
|
@@ -90,7 +177,7 @@ const makeLinkComponent = (C, baseProps) => {
|
|
|
90
177
|
router.navigate({
|
|
91
178
|
to,
|
|
92
179
|
preserveSearch,
|
|
93
|
-
|
|
180
|
+
params
|
|
94
181
|
});
|
|
95
182
|
}, [
|
|
96
183
|
router,
|
|
@@ -112,6 +199,16 @@ const Navigate = (props) => {
|
|
|
112
199
|
return null;
|
|
113
200
|
};
|
|
114
201
|
|
|
202
|
+
//#endregion
|
|
203
|
+
//#region src/router/redirect.ts
|
|
204
|
+
var Redirect = class {
|
|
205
|
+
options;
|
|
206
|
+
constructor(options) {
|
|
207
|
+
this.options = options;
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
const redirect = (options) => new Redirect(options);
|
|
211
|
+
|
|
115
212
|
//#endregion
|
|
116
213
|
//#region src/router/outlet.tsx
|
|
117
214
|
const DefaultOutlet = ({ children }) => children;
|
|
@@ -121,11 +218,20 @@ var Outlet = class {
|
|
|
121
218
|
state = "preloading";
|
|
122
219
|
promise;
|
|
123
220
|
data;
|
|
221
|
+
error;
|
|
124
222
|
component;
|
|
125
223
|
get Component() {
|
|
126
224
|
switch (this.state) {
|
|
127
225
|
case "loading": return LoadingPlaceholder;
|
|
128
226
|
case "ready": return this.component ?? DefaultOutlet;
|
|
227
|
+
case "error": {
|
|
228
|
+
const ErrorComponent = this.config.errorComponent ?? DefaultErrorPage;
|
|
229
|
+
const error = this.error ?? new RouterError("LOAD");
|
|
230
|
+
return (props) => /* @__PURE__ */ jsx(ErrorComponent, {
|
|
231
|
+
...props,
|
|
232
|
+
error
|
|
233
|
+
});
|
|
234
|
+
}
|
|
129
235
|
default: return;
|
|
130
236
|
}
|
|
131
237
|
}
|
|
@@ -135,6 +241,7 @@ var Outlet = class {
|
|
|
135
241
|
makeAutoObservable(this, {
|
|
136
242
|
promise: observable.ref,
|
|
137
243
|
data: observable.ref,
|
|
244
|
+
error: observable.ref,
|
|
138
245
|
component: false,
|
|
139
246
|
config: false
|
|
140
247
|
});
|
|
@@ -156,15 +263,20 @@ var Outlet = class {
|
|
|
156
263
|
this.setState("ready");
|
|
157
264
|
}, 300);
|
|
158
265
|
else this.setState("ready");
|
|
159
|
-
}).catch(() => {
|
|
266
|
+
}).catch((e) => {
|
|
160
267
|
clearTimeout(preloadingTimer);
|
|
161
268
|
this.setState("error");
|
|
269
|
+
if (e instanceof Redirect) throw e;
|
|
270
|
+
this.setError(e instanceof RouterError ? e : new RouterError("LOAD", { cause: e }));
|
|
162
271
|
});
|
|
163
272
|
await this.promise;
|
|
164
273
|
}
|
|
165
274
|
setData(data) {
|
|
166
275
|
this.data = data;
|
|
167
276
|
}
|
|
277
|
+
setError(error) {
|
|
278
|
+
this.error = error;
|
|
279
|
+
}
|
|
168
280
|
setState(state) {
|
|
169
281
|
this.state = state;
|
|
170
282
|
}
|
|
@@ -182,16 +294,6 @@ var Outlet = class {
|
|
|
182
294
|
}
|
|
183
295
|
};
|
|
184
296
|
|
|
185
|
-
//#endregion
|
|
186
|
-
//#region src/router/redirect.ts
|
|
187
|
-
var Redirect = class {
|
|
188
|
-
options;
|
|
189
|
-
constructor(options) {
|
|
190
|
-
this.options = options;
|
|
191
|
-
}
|
|
192
|
-
};
|
|
193
|
-
const redirect = (options) => new Redirect(options);
|
|
194
|
-
|
|
195
297
|
//#endregion
|
|
196
298
|
//#region src/router/route.tsx
|
|
197
299
|
var Route = class {
|
|
@@ -201,20 +303,36 @@ var Route = class {
|
|
|
201
303
|
context;
|
|
202
304
|
params;
|
|
203
305
|
layout;
|
|
306
|
+
/** set on synthetic error routes; the error being rendered */
|
|
307
|
+
error;
|
|
308
|
+
/** @internal */
|
|
309
|
+
guardEntries;
|
|
310
|
+
/** @internal */
|
|
311
|
+
levels;
|
|
204
312
|
get data() {
|
|
205
313
|
return Object.assign({}, ...this.outlets.map((o) => o.data));
|
|
206
314
|
}
|
|
207
315
|
constructor(def) {
|
|
208
316
|
this.path = def.path;
|
|
209
|
-
this.
|
|
317
|
+
this.guardEntries = def.guards;
|
|
318
|
+
this.guards = def.guards.map((entry) => entry.guard);
|
|
319
|
+
this.levels = def.levels;
|
|
210
320
|
this.context = def.context ?? {};
|
|
211
321
|
this.outlets = def.outlets;
|
|
212
322
|
this.params = def.params;
|
|
213
323
|
this.layout = def.layout;
|
|
324
|
+
this.error = def.error;
|
|
214
325
|
makeObservable(this, { data: computed });
|
|
215
326
|
}
|
|
216
327
|
async guard() {
|
|
217
|
-
for (const guard of this.
|
|
328
|
+
for (const { guard, depth } of this.guardEntries) try {
|
|
329
|
+
await guard(this);
|
|
330
|
+
} catch (e) {
|
|
331
|
+
if (e instanceof Redirect) throw e;
|
|
332
|
+
const error = e instanceof RouterError ? e : new RouterError("GUARD", { cause: e });
|
|
333
|
+
error.depth ??= depth;
|
|
334
|
+
throw error;
|
|
335
|
+
}
|
|
218
336
|
}
|
|
219
337
|
async load() {
|
|
220
338
|
await Promise.all(this.outlets.map((outlet) => outlet.load(this)));
|
|
@@ -228,59 +346,126 @@ const pathToSegments = (path) => {
|
|
|
228
346
|
};
|
|
229
347
|
const makeRoute = (matchState) => {
|
|
230
348
|
const outlets = matchState.outlets.filter((o) => o !== void 0);
|
|
231
|
-
const guards = matchState.guards.filter((g) => g !== void 0);
|
|
232
349
|
return new Route({
|
|
233
350
|
...matchState,
|
|
234
351
|
outlets,
|
|
235
|
-
guards,
|
|
236
352
|
path: matchState.segments.join("/")
|
|
237
353
|
});
|
|
238
354
|
};
|
|
355
|
+
/**
|
|
356
|
+
* Builds the synthetic route rendered when navigation fails. Bubbles
|
|
357
|
+
* from the failing level (`error.depth`, defaulting to the deepest
|
|
358
|
+
* matched level) to the nearest `[ERROR]` component, preserving the
|
|
359
|
+
* `[LAYOUT]` and `[WRAPPER]`s accumulated up to that level. Ancestor
|
|
360
|
+
* `[LOAD]` loaders are intentionally not run — error routes never
|
|
361
|
+
* fetch data.
|
|
362
|
+
*/
|
|
363
|
+
const makeErrorRoute = (error, pathname, source) => {
|
|
364
|
+
const levels = error.state?.levels ?? source?.levels ?? [];
|
|
365
|
+
const depth = Math.min(error.depth ?? levels.length - 1, levels.length - 1);
|
|
366
|
+
const level = depth >= 0 ? levels[depth] : void 0;
|
|
367
|
+
const ErrorComponent = level?.errorComponent ?? DefaultErrorPage;
|
|
368
|
+
const outlets = levels.slice(0, depth + 1).flatMap((l) => l.wrapper ? [new Outlet({ component: l.wrapper })] : []);
|
|
369
|
+
outlets.push(new Outlet({ component: (props) => /* @__PURE__ */ jsx(ErrorComponent, {
|
|
370
|
+
...props,
|
|
371
|
+
error
|
|
372
|
+
}) }));
|
|
373
|
+
return new Route({
|
|
374
|
+
path: pathname.replace(/^\/+/, ""),
|
|
375
|
+
outlets,
|
|
376
|
+
guards: [],
|
|
377
|
+
levels: [],
|
|
378
|
+
params: error.state?.params ?? source?.params ?? {},
|
|
379
|
+
context: error.state?.context ?? source?.context ?? {},
|
|
380
|
+
layout: level?.layout,
|
|
381
|
+
error
|
|
382
|
+
});
|
|
383
|
+
};
|
|
384
|
+
const notFound = (state, attemptedSegments) => {
|
|
385
|
+
const error = new RouterError("NOT_FOUND", { path: `/${attemptedSegments.filter((s) => s !== "").join("/")}` });
|
|
386
|
+
error.state = state;
|
|
387
|
+
return error;
|
|
388
|
+
};
|
|
239
389
|
const matchRoute = (path, routeDef, matchState) => {
|
|
390
|
+
const depth = matchState?.levels.length ?? 0;
|
|
391
|
+
const layout = routeDef[LAYOUT] ?? matchState?.layout;
|
|
392
|
+
const errorComponent = routeDef[ERROR] ?? matchState?.errorComponent;
|
|
240
393
|
const state = {
|
|
241
394
|
segments: [],
|
|
242
395
|
params: {},
|
|
243
396
|
...matchState,
|
|
244
|
-
layout
|
|
397
|
+
layout,
|
|
398
|
+
errorComponent,
|
|
245
399
|
context: {
|
|
246
400
|
...matchState?.context,
|
|
247
401
|
...routeDef[CONTEXT]
|
|
248
402
|
},
|
|
249
|
-
guards: [...matchState?.guards ?? [], routeDef[GUARD]
|
|
403
|
+
guards: [...matchState?.guards ?? [], ...routeDef[GUARD] ? [{
|
|
404
|
+
guard: routeDef[GUARD],
|
|
405
|
+
depth
|
|
406
|
+
}] : []],
|
|
250
407
|
outlets: [
|
|
251
408
|
...matchState?.outlets ?? [],
|
|
252
|
-
routeDef[WRAPPER] ? new Outlet({
|
|
253
|
-
|
|
254
|
-
|
|
409
|
+
routeDef[WRAPPER] ? new Outlet({
|
|
410
|
+
component: routeDef[WRAPPER],
|
|
411
|
+
errorComponent
|
|
412
|
+
}) : void 0,
|
|
413
|
+
routeDef[LOAD] ? new Outlet({
|
|
414
|
+
loader: routeDef[LOAD],
|
|
415
|
+
errorComponent
|
|
416
|
+
}) : void 0
|
|
417
|
+
],
|
|
418
|
+
levels: [...matchState?.levels ?? [], {
|
|
419
|
+
wrapper: routeDef[WRAPPER],
|
|
420
|
+
layout,
|
|
421
|
+
errorComponent
|
|
422
|
+
}]
|
|
255
423
|
};
|
|
256
424
|
const [segment, ...remainingSegments] = pathToSegments(path);
|
|
257
425
|
const remainingPath = remainingSegments.join("/");
|
|
258
426
|
let defAtSegment = routeDef[segment || "index"];
|
|
259
427
|
if (!defAtSegment) {
|
|
260
|
-
const matchedSegment = Object.keys(routeDef).find((segment) => segment.startsWith("$"));
|
|
428
|
+
const matchedSegment = Object.keys(routeDef).find((segment) => segment.startsWith("$") || segment.startsWith(":"));
|
|
261
429
|
if (matchedSegment) {
|
|
262
430
|
defAtSegment = routeDef[matchedSegment];
|
|
263
431
|
state.params[matchedSegment.slice(1)] = segment;
|
|
264
432
|
}
|
|
265
433
|
}
|
|
266
|
-
if (!defAtSegment) throw
|
|
434
|
+
if (!defAtSegment) throw notFound(state, [
|
|
435
|
+
...state.segments,
|
|
436
|
+
segment ?? "",
|
|
437
|
+
...remainingSegments
|
|
438
|
+
]);
|
|
267
439
|
state.segments.push(segment ?? "index");
|
|
268
440
|
if (isLeaf(defAtSegment)) {
|
|
269
|
-
if (remainingPath) throw
|
|
441
|
+
if (remainingPath) throw notFound(state, [...state.segments, ...remainingSegments]);
|
|
270
442
|
if (isRedirect(defAtSegment)) throw new Redirect(typeof defAtSegment[REDIRECT] === "string" ? { to: defAtSegment[REDIRECT] } : defAtSegment[REDIRECT]);
|
|
271
443
|
if (isComponent(defAtSegment) || isLazyComponent(defAtSegment)) {
|
|
272
|
-
state.outlets.push(new Outlet({
|
|
444
|
+
state.outlets.push(new Outlet({
|
|
445
|
+
component: defAtSegment,
|
|
446
|
+
errorComponent
|
|
447
|
+
}));
|
|
273
448
|
return makeRoute(state);
|
|
274
449
|
}
|
|
275
450
|
}
|
|
276
451
|
if (isPage(defAtSegment)) {
|
|
277
452
|
state.layout = defAtSegment[LAYOUT] ?? state.layout;
|
|
453
|
+
state.errorComponent = defAtSegment[ERROR] ?? state.errorComponent;
|
|
278
454
|
Object.assign(state.context, defAtSegment[CONTEXT]);
|
|
279
|
-
state.guards.push(
|
|
455
|
+
if (defAtSegment[GUARD]) state.guards.push({
|
|
456
|
+
guard: defAtSegment[GUARD],
|
|
457
|
+
depth
|
|
458
|
+
});
|
|
280
459
|
state.outlets.push(new Outlet({
|
|
281
460
|
component: defAtSegment[PAGE],
|
|
282
|
-
loader: defAtSegment[LOAD]
|
|
461
|
+
loader: defAtSegment[LOAD],
|
|
462
|
+
errorComponent: state.errorComponent
|
|
283
463
|
}));
|
|
464
|
+
state.levels[state.levels.length - 1] = {
|
|
465
|
+
...state.levels[state.levels.length - 1],
|
|
466
|
+
layout: state.layout,
|
|
467
|
+
errorComponent: state.errorComponent
|
|
468
|
+
};
|
|
284
469
|
return makeRoute(state);
|
|
285
470
|
}
|
|
286
471
|
return matchRoute(remainingPath, defAtSegment, state);
|
|
@@ -303,14 +488,7 @@ var RouterStore = class {
|
|
|
303
488
|
return Object.fromEntries(this.search);
|
|
304
489
|
}
|
|
305
490
|
get pathParams() {
|
|
306
|
-
|
|
307
|
-
const paramSegments = this.activeSegments.filter((segment) => segment.startsWith("$"));
|
|
308
|
-
const pathValues = this.location.pathname.split("/").slice(1);
|
|
309
|
-
paramSegments.forEach((segment, index) => {
|
|
310
|
-
const value = pathValues[index + 1];
|
|
311
|
-
if (value) params[segment] = value;
|
|
312
|
-
});
|
|
313
|
-
return params;
|
|
491
|
+
return { ...this.activeRoute?.params };
|
|
314
492
|
}
|
|
315
493
|
get activeSegments() {
|
|
316
494
|
return this.activeRoute?.path.split("/") ?? [];
|
|
@@ -335,36 +513,45 @@ var RouterStore = class {
|
|
|
335
513
|
}
|
|
336
514
|
doesPathMatch(path, exact) {
|
|
337
515
|
const segments = path.slice(1).split("/");
|
|
338
|
-
return segments.every((segment, i) => segment === this.activeSegments[i] || segment.startsWith("
|
|
516
|
+
return segments.every((segment, i) => segment === this.activeSegments[i] || segment.startsWith(":")) && this.activeSegments.length >= segments.length && (!exact || segments.length === this.activeSegments.length);
|
|
339
517
|
}
|
|
340
518
|
navigate(options) {
|
|
519
|
+
if (!options.state && this.isCurrentLocation(options)) return;
|
|
341
520
|
if (!document.startViewTransition) this._navigate(options);
|
|
342
521
|
else document.startViewTransition(() => this._navigate(options)).ready.catch((e) => console.log(e, typeof e));
|
|
343
522
|
}
|
|
344
523
|
_navigate(options) {
|
|
345
|
-
const
|
|
524
|
+
const location = this.resolveLocation(options);
|
|
525
|
+
if (options.replace) this.history.replace(location, options.state);
|
|
526
|
+
else this.history.push(location, options.state);
|
|
527
|
+
}
|
|
528
|
+
resolveLocation(options) {
|
|
529
|
+
const { to, search = {}, preserveSearch, params } = options;
|
|
346
530
|
const searchParams = search instanceof URLSearchParams ? search : new URLSearchParams(search);
|
|
347
531
|
if (preserveSearch) {
|
|
348
532
|
for (const [name, value] of this.search) if (!searchParams.has(name)) searchParams.set(name, value);
|
|
349
533
|
}
|
|
350
|
-
|
|
534
|
+
return {
|
|
351
535
|
pathname: resolvePath(to, params),
|
|
352
536
|
search: searchParams.size ? `?${searchParams.toString()}` : void 0
|
|
353
537
|
};
|
|
354
|
-
|
|
355
|
-
|
|
538
|
+
}
|
|
539
|
+
isCurrentLocation(options) {
|
|
540
|
+
if (!this.location) return false;
|
|
541
|
+
const target = this.resolveLocation(options);
|
|
542
|
+
return target.pathname === this.location.pathname && (target.search ?? "") === this.location.search;
|
|
356
543
|
}
|
|
357
544
|
setQueryParam(param, value) {
|
|
358
545
|
const params = new URLSearchParams(this.location.search);
|
|
359
546
|
params.set(param, value);
|
|
360
|
-
this.history.replace({ search: params.toString() });
|
|
547
|
+
this.history.replace({ search: `?${params.toString()}` });
|
|
361
548
|
}
|
|
362
549
|
removeQueryParam(param) {
|
|
363
550
|
const params = new URLSearchParams(this.location.search);
|
|
364
551
|
const value = params.get(param) ?? void 0;
|
|
365
552
|
if (value !== void 0) {
|
|
366
553
|
params.delete(param);
|
|
367
|
-
this.history.replace({ search: params.toString() });
|
|
554
|
+
this.history.replace({ search: params.size ? `?${params.toString()}` : "" });
|
|
368
555
|
}
|
|
369
556
|
return value;
|
|
370
557
|
}
|
|
@@ -377,9 +564,14 @@ var RouterStore = class {
|
|
|
377
564
|
});
|
|
378
565
|
return;
|
|
379
566
|
}
|
|
567
|
+
if (this.activeRoute && this.location?.pathname === location.pathname) {
|
|
568
|
+
this.location = location;
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
380
571
|
this.location = location;
|
|
572
|
+
let matchedRoute;
|
|
381
573
|
try {
|
|
382
|
-
|
|
574
|
+
matchedRoute = matchRoute(location.pathname, this.routesDef);
|
|
383
575
|
await matchedRoute.guard();
|
|
384
576
|
if (this.location !== location) return;
|
|
385
577
|
runInAction(() => {
|
|
@@ -391,11 +583,21 @@ var RouterStore = class {
|
|
|
391
583
|
this.navigate(e.options);
|
|
392
584
|
return;
|
|
393
585
|
}
|
|
394
|
-
|
|
586
|
+
if (this.location !== location) return;
|
|
587
|
+
const error = e instanceof RouterError ? e : new RouterError("RENDER", {
|
|
588
|
+
cause: e,
|
|
589
|
+
path: location.pathname
|
|
590
|
+
});
|
|
591
|
+
console.error(error);
|
|
592
|
+
const errorRoute = makeErrorRoute(error, location.pathname, matchedRoute);
|
|
593
|
+
runInAction(() => {
|
|
594
|
+
this.activeRoute = errorRoute;
|
|
595
|
+
});
|
|
596
|
+
await errorRoute.load();
|
|
395
597
|
}
|
|
396
598
|
}
|
|
397
599
|
};
|
|
398
600
|
|
|
399
601
|
//#endregion
|
|
400
|
-
export { CONTEXT, DefaultOutlet, GUARD, LAYOUT, LOAD, Navigate, Outlet, PAGE, PassThrough, REDIRECT, Redirect, Route, Router, RouterOutlet, RouterStore, WRAPPER, isComponent, isLazyComponent, isLeaf, isPage, isRedirect, makeLinkComponent, makeRoute, makeRoutes, matchRoute, redirect, resolvePath, routerContext, useRouter };
|
|
602
|
+
export { CONTEXT, DefaultErrorPage, DefaultOutlet, ERROR, GUARD, LAYOUT, LOAD, Navigate, Outlet, PAGE, PassThrough, REDIRECT, Redirect, Route, RouteErrorBoundary, Router, RouterError, RouterOutlet, RouterStore, WRAPPER, isComponent, isLazyComponent, isLeaf, isPage, isRedirect, makeErrorRoute, makeLinkComponent, makeRoute, makeRoutes, matchRoute, redirect, resolvePath, routerContext, useRouter };
|
|
401
603
|
//# sourceMappingURL=router.mjs.map
|
package/dist/router.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.mjs","names":[],"sources":["../src/router/symbols.ts","../src/router/util.ts","../src/router/components/router.tsx","../src/router/components/link.tsx","../src/router/components/navigate.tsx","../src/router/outlet.tsx","../src/router/redirect.ts","../src/router/route.tsx","../src/router/make-routes.tsx","../src/router/router.store.ts"],"sourcesContent":["export const CONTEXT: unique symbol = Symbol.for(\"MOBX_ROUTER_CONTEXT\");\nexport const LAYOUT: unique symbol = Symbol.for(\"MOBX_ROUTER_LAYOUT\");\nexport const WRAPPER: unique symbol = Symbol.for(\"MOBX_ROUTER_WRAPPER\");\nexport const LOAD: unique symbol = Symbol.for(\"MOBX_ROUTER_LOAD\");\nexport const GUARD: unique symbol = Symbol.for(\"MOBX_ROUTER_GUARD\");\nexport const PAGE: unique symbol = Symbol.for(\"MOBX_ROUTER_PAGE\");\nexport const REDIRECT: unique symbol = Symbol.for(\"MOBX_ROUTER_REDIRECT\");\n","import { PAGE, REDIRECT } from \"./symbols\";\nimport type { Component, LazyComponent, Leaf, Obj, Page, Redirector } from \"./types\";\n\nexport const resolvePath = (to: string, params?: Obj): string => {\n return to.replaceAll(/:[^/]*/g, (segment) => {\n const value = params?.[segment.slice(1)];\n if (!value)\n throw new Error(`Unable to resolve route '${to}. Paramater '${segment}' not specified.`);\n return value;\n });\n};\n\nexport const isComponent = (data: any): data is Component => {\n if (!data) return false;\n return (\n typeof data === \"function\" ||\n (typeof data === \"object\" && data[\"$$typeof\"] === Symbol.for(\"react.memo\"))\n );\n};\n\nexport const isPage = (data: any): data is Page => {\n if (typeof data !== \"object\") return false;\n const symbols = Object.getOwnPropertySymbols(data);\n return symbols.includes(PAGE);\n};\n\nexport const isRedirect = (data: any): data is Redirector => {\n if (typeof data !== \"object\") return false;\n const symbols = Object.getOwnPropertySymbols(data);\n return symbols.includes(REDIRECT);\n};\n\nexport const isLeaf = (data: any): data is Leaf => {\n return isComponent(data) || isPage(data) || isRedirect(data);\n};\n\nexport const isLazyComponent = (data: any): data is LazyComponent => {\n return typeof data === \"function\" && data.toString().startsWith(\"() => import(\");\n};\n","import { observer } from \"mobx-react-lite\";\nimport { createContext, useContext } from \"react\";\nimport type { Route } from \"../route\";\nimport type { RouterStore } from \"../router.store\";\nimport type { Component } from \"../types\";\n\nexport const PassThrough: Component = ({ children }) => children;\n\n// Plain (non-observer) renderer. State observation lives one level up\n// in `Router`, so the page component renders as a child of a plain\n// FunctionComponent — no memo wrapper in the parent chain to interact\n// with React Refresh's family-update propagation.\nexport const RouterOutlet: React.FC<{ route: Route; components: (Component | undefined)[] }> = ({\n route,\n components,\n}) => {\n const [C, ...remaining] = components;\n\n if (!C) return null;\n\n return (\n <C route={route}>\n {remaining.length > 0 && <RouterOutlet route={route} components={remaining} />}\n </C>\n );\n};\n\nexport const routerContext = createContext<RouterStore>(null as any);\nexport const useRouter = () => useContext(routerContext);\n\nexport interface RouterProps {\n store: RouterStore;\n}\n\nexport const Router = observer(({ store }: RouterProps) => {\n if (!store.activeRoute) {\n return null;\n }\n\n const Layout = store.activeRoute.layout ?? PassThrough;\n const components = store.activeRoute.outlets.map((o) => o.Component);\n\n return (\n <routerContext.Provider value={store}>\n <Layout route={store.activeRoute}>\n <RouterOutlet route={store.activeRoute} components={components} />\n </Layout>\n </routerContext.Provider>\n );\n});\n","import { observer } from \"mobx-react-lite\";\nimport React, { useCallback } from \"react\";\nimport type {\n DynamicRoutePath,\n ExtractParams,\n NavigateOptions,\n RoutePath,\n StaticRoutePath,\n} from \"../types\";\nimport { resolvePath } from \"../util\";\nimport { useRouter } from \"./router\";\n\ntype LinkComponentProps<C extends React.ElementType> = Omit<\n React.ComponentProps<C>,\n \"ref\" | \"exact\" | \"to\" | \"params\" | \"onClick\" | \"asChild\"\n>;\n\nexport type LinkPropsBase<\n C extends React.ElementType,\n I extends React.ElementType = C,\n> = LinkComponentProps<C> & {\n exact?: boolean;\n preserveSearch?: boolean;\n ref?: React.Ref<React.ComponentRef<I>>;\n};\n\n// function overloading is much faster than leveraging conditional types\n// but once the typescript go compiler is released and performance is no\n// longer an issue, it might make sense to simplify this a bit so it can\n// be more easily consumed by users\nexport interface LinkComponent<C extends React.ElementType, I extends React.ElementType = C> {\n <P extends StaticRoutePath>(\n props: LinkPropsBase<C, I> & { to: P; params?: undefined },\n ): React.ReactNode;\n <P extends DynamicRoutePath>(\n props: LinkPropsBase<C, I> & { to: P; params: ExtractParams<P> },\n ): React.ReactNode;\n}\n\n// final thing to do is make sure refs still work in React 19\n\n// this smooths over some of the awkwardness when extending this component\nexport const makeLinkComponent = <C extends React.ElementType, I extends React.ElementType = C>(\n C: C,\n baseProps?: Partial<LinkComponentProps<C>> & { as?: I },\n) => {\n return observer(({ to, params, exact, preserveSearch, children, ...props }: any) => {\n const router = useRouter();\n const mergedProps = { ...baseProps, ...props };\n\n if (props.role !== \"link\") {\n mergedProps.href = resolvePath(to, params);\n }\n\n if (router.doesPathMatch(to, exact)) {\n mergedProps[\"aria-current\"] = \"page\";\n }\n\n mergedProps.onClick = useCallback(\n (event: React.MouseEvent<HTMLElement>) => {\n event.preventDefault();\n if (props.disabled) return;\n router.navigate({ to, preserveSearch, ...(params as any) } as NavigateOptions<RoutePath>);\n },\n [router, params, to, props.disabled],\n );\n\n return React.createElement(C, mergedProps, children);\n }) as LinkComponent<C, I>;\n};\n\n//export const Link = makeLinkComponent('a');\n","import { useLayoutEffect } from \"react\";\nimport type { NavigateOptions, RoutePath } from \"../types\";\nimport { useRouter } from \"./router\";\n\nexport const Navigate = <P extends RoutePath>(props: NavigateOptions<P>) => {\n const router = useRouter();\n\n useLayoutEffect(() => {\n router.navigate(props);\n }, [router, props]);\n\n return null;\n};\n","import { makeAutoObservable, observable } from \"mobx\";\nimport type { Route } from \"./route\";\nimport type { Component, LazyComponent, Loader } from \"./types\";\nimport { isLazyComponent } from \"./util\";\n\nexport interface OutletConfig {\n component?: Component | LazyComponent;\n loader?: Loader;\n}\n\nexport type RouteSegmentState = \"preloading\" | \"loading\" | \"error\" | \"ready\";\n\nexport const DefaultOutlet: Component = ({ children }) => children;\n\nconst LoadingPlaceholder: Component = () => <p>Loading...</p>;\n\nexport class Outlet {\n state: RouteSegmentState = \"preloading\";\n promise: Promise<unknown> | undefined;\n data: unknown;\n\n // Plain (non-observable) reference. The page component must reach\n // React unmediated by MobX — once MobX deep-observes the holder,\n // React Refresh can no longer swap the page identity via family\n // lookup on the original function. This mirrors how Route holds\n // `layout` as a plain field under makeObservable.\n component: Component | undefined;\n\n get Component(): Component | undefined {\n switch (this.state) {\n case \"loading\":\n return LoadingPlaceholder;\n case \"ready\":\n return this.component ?? DefaultOutlet;\n default:\n return undefined;\n }\n }\n\n constructor(readonly config: OutletConfig) {\n if (!isLazyComponent(config.component)) {\n this.component = config.component;\n }\n\n makeAutoObservable<Outlet, \"component\" | \"config\">(this, {\n promise: observable.ref,\n data: observable.ref,\n component: false,\n config: false,\n });\n }\n\n async load(route: Route): Promise<void> {\n const promises: Promise<void>[] = [];\n\n if (isLazyComponent(this.config.component) && !this.component) {\n promises.push(this.loadComponent());\n }\n\n if (this.config.loader) {\n promises.push(this.loadData(route));\n }\n\n if (!promises.length) {\n this.setState(\"ready\");\n return;\n }\n\n // wait to transition to loading to avoid\n // screen flashes when the loader function\n // executes quickly\n const preloadingTimer = setTimeout(() => {\n if (this.state === \"preloading\") {\n this.setState(\"loading\");\n }\n }, 300);\n\n this.promise = Promise.all(promises)\n .then(() => {\n clearTimeout(preloadingTimer);\n if (this.state === \"loading\") {\n // if we had to transition to regular loading because\n // the loader was taking too long, force an additional\n // timeout to prevent a loading flash\n setTimeout(() => {\n this.setState(\"ready\");\n }, 300);\n } else {\n this.setState(\"ready\");\n }\n })\n .catch(() => {\n clearTimeout(preloadingTimer);\n // TODO: check for type of error\n // handle access denied and redirects\n this.setState(\"error\");\n });\n\n await this.promise;\n }\n\n setData(data: unknown) {\n this.data = data;\n }\n\n setState(state: RouteSegmentState) {\n this.state = state;\n }\n\n private async loadData(route: Route): Promise<void> {\n await this.config.loader?.(route).then((data) => this.setData(data));\n }\n\n private async loadComponent(): Promise<void> {\n if (!isLazyComponent(this.config.component)) return;\n const module = await this.config.component();\n for (const exportName in module) {\n if (exportName === \"default\" || exportName.endsWith(\"Page\")) {\n this.component = module[exportName];\n return;\n }\n }\n throw new Error(\n \"Lazy route component module did not export `default` or a `*Page` named export\",\n );\n }\n}\n","import type { NavigateOptions, RoutePath } from \"./types\";\n\nexport class Redirect<P extends RoutePath> {\n constructor(readonly options: NavigateOptions<P>) {}\n}\n\nexport const redirect = <P extends RoutePath>(options: NavigateOptions<P>): Redirect<P> =>\n new Redirect(options);\n","import { computed, makeObservable } from \"mobx\";\nimport type { Outlet } from \"./outlet\";\nimport type { Component, Guard, Obj } from \"./types\";\n\nexport interface RouteConfig {\n path: string;\n outlets: Outlet[];\n guards: Guard[];\n context?: Obj;\n layout?: Component;\n params: Obj;\n}\n\nexport class Route {\n readonly path: string;\n readonly outlets: Outlet[];\n readonly guards: Guard[];\n readonly context: Obj;\n readonly params: Obj;\n readonly layout?: Component;\n\n get data(): Obj {\n return Object.assign({}, ...this.outlets.map((o) => o.data));\n }\n\n constructor(def: RouteConfig) {\n this.path = def.path;\n this.guards = def.guards;\n this.context = def.context ?? {};\n this.outlets = def.outlets;\n this.params = def.params;\n this.layout = def.layout;\n\n makeObservable(this, {\n data: computed,\n });\n }\n\n async guard(): Promise<void> {\n for (const guard of this.guards) {\n await guard(this);\n }\n }\n\n async load(): Promise<void> {\n await Promise.all(this.outlets.map((outlet) => outlet.load(this)));\n }\n}\n","import { Outlet } from \"./outlet\";\nimport { Redirect } from \"./redirect\";\nimport { Route } from \"./route\";\nimport { CONTEXT, GUARD, LAYOUT, LOAD, PAGE, REDIRECT, WRAPPER } from \"./symbols\";\nimport type { Component, Guard, Obj, Routes } from \"./types\";\nimport { isComponent, isLazyComponent, isLeaf, isPage, isRedirect } from \"./util\";\n\nconst pathToSegments = (path: string): string[] => {\n return path.replace(/^\\/+|\\/+$/g, \"\").split(\"/\");\n};\n\nexport interface MatchState {\n segments: string[];\n context: Obj;\n params: Obj;\n outlets: (Outlet | undefined)[];\n guards: (Guard | undefined)[];\n layout?: Component;\n}\n\nexport const makeRoute = (matchState: MatchState): Route => {\n const outlets = matchState.outlets.filter((o) => o !== undefined);\n const guards = matchState.guards.filter((g) => g !== undefined);\n\n return new Route({ ...matchState, outlets, guards, path: matchState.segments.join(\"/\") });\n};\n\nexport const matchRoute = (path: string, routeDef: Routes, matchState?: MatchState): Route => {\n const state: MatchState = {\n segments: [],\n params: {},\n ...matchState,\n layout: routeDef[LAYOUT] ?? matchState?.layout,\n context: { ...matchState?.context, ...routeDef[CONTEXT] },\n guards: [...(matchState?.guards ?? []), routeDef[GUARD]],\n outlets: [\n ...(matchState?.outlets ?? []),\n routeDef[WRAPPER] ? new Outlet({ component: routeDef[WRAPPER] }) : undefined,\n routeDef[LOAD] ? new Outlet({ loader: routeDef[LOAD] }) : undefined,\n ],\n };\n\n const [segment, ...remainingSegments] = pathToSegments(path);\n const remainingPath = remainingSegments.join(\"/\");\n\n let defAtSegment = routeDef[segment || \"index\"];\n\n if (!defAtSegment) {\n const matchedSegment = Object.keys(routeDef).find((segment) => segment.startsWith(\"$\"));\n if (matchedSegment) {\n defAtSegment = routeDef[matchedSegment];\n state.params[matchedSegment.slice(1)] = segment;\n }\n }\n\n if (!defAtSegment) {\n throw new Error(\"Not Found.\");\n }\n\n state.segments.push(segment ?? \"index\");\n\n if (isLeaf(defAtSegment)) {\n if (remainingPath) {\n throw new Error(\"Not Found.\");\n }\n\n if (isRedirect(defAtSegment)) {\n const redirect =\n typeof defAtSegment[REDIRECT] === \"string\"\n ? { to: defAtSegment[REDIRECT] }\n : defAtSegment[REDIRECT];\n throw new Redirect(redirect as any);\n }\n\n if (isComponent(defAtSegment) || isLazyComponent(defAtSegment)) {\n state.outlets.push(new Outlet({ component: defAtSegment }));\n return makeRoute(state);\n }\n }\n\n // at this point we have a nested route or a [Page] definition\n\n if (isPage(defAtSegment)) {\n state.layout = defAtSegment[LAYOUT] ?? state.layout;\n Object.assign(state.context, defAtSegment[CONTEXT]);\n state.guards.push(defAtSegment[GUARD]);\n state.outlets.push(\n new Outlet({\n component: defAtSegment[PAGE],\n loader: defAtSegment[LOAD],\n }),\n );\n\n return makeRoute(state);\n }\n\n // now we know we have a nested route\n return matchRoute(remainingPath, defAtSegment, state);\n};\n\n// TODO: ideally this could resolve to something less than R,\n// but specific enough to infer all paths as a literal union.\n// As it stands, there are certain things we can't access reliably\n// without the compiler complaining about circular references\n// try \"as const satisfies\" approach which would allow us to\n// exchange a less specific version of MobxRoutesRoot for this\nexport const makeRoutes =\n () =>\n <R extends Routes>(routes: R): R => {\n // todo: perform some validation here\n // - no forward slashes in keys\n // - at most one variable segment per level\n // - only lowercase letters (except variables)\n // - paths/variables cannot contain $ that aren't at the beginning\n // - path variables must be unique across a path\n\n return routes;\n };\n","import { createBrowserHistory, type History, type Location } from \"history\";\nimport { action, computed, makeObservable, observable, runInAction } from \"mobx\";\nimport { matchRoute } from \"./make-routes\";\nimport { Redirect } from \"./redirect\";\nimport type { Route } from \"./route\";\nimport type { Component, MobxRouterConfig, NavigateOptions, Obj, RoutePath, Routes } from \"./types\";\nimport { resolvePath } from \"./util\";\n\nexport interface MobxRenderSegment {\n segment: string;\n component: Component;\n props?: Obj;\n}\n\nexport class RouterStore {\n readonly history: History;\n\n routesDef?: Routes;\n\n location!: Location;\n activeRoute: Route | undefined;\n\n get search(): URLSearchParams {\n return new URLSearchParams(this.location?.search);\n }\n\n get query(): Record<string, string> {\n return Object.fromEntries(this.search);\n }\n\n get pathParams(): Record<string, string> {\n const params = {} as Record<string, string>;\n\n const paramSegments = this.activeSegments.filter((segment) => segment.startsWith(\"$\"));\n const pathValues = this.location.pathname.split(\"/\").slice(1);\n\n paramSegments.forEach((segment, index) => {\n const value = pathValues[index + 1];\n if (value) {\n params[segment] = value;\n }\n });\n\n return params as Record<string, string>;\n }\n\n get activeSegments(): string[] {\n return this.activeRoute?.path.split(\"/\") ?? [];\n }\n\n constructor(config?: MobxRouterConfig) {\n makeObservable(this, {\n location: observable.ref,\n activeRoute: observable.ref,\n\n search: computed,\n pathParams: computed,\n activeSegments: computed,\n\n setLocation: action,\n });\n\n this.history = config?.history ?? createBrowserHistory();\n }\n\n initialize(routesDef: Routes): void {\n this.routesDef = routesDef;\n this.history.listen((data) => {\n void this.setLocation(data.location);\n });\n\n void this.setLocation(this.history.location);\n }\n\n doesPathMatch<P extends RoutePath>(path: P, exact?: boolean): boolean {\n const segments = path.slice(1).split(\"/\");\n const segmentsMatch = segments.every(\n (segment, i) => segment === this.activeSegments[i] || segment.startsWith(\"$\"),\n );\n\n return (\n segmentsMatch &&\n this.activeSegments.length >= segments.length &&\n (!exact || segments.length === this.activeSegments.length)\n );\n }\n\n navigate<P extends RoutePath>(options: NavigateOptions<P>): void {\n if (!document.startViewTransition) {\n this._navigate(options);\n } else {\n const transition = document.startViewTransition(() => this._navigate(options));\n transition.ready.catch((e) => console.log(e, typeof e));\n }\n }\n\n _navigate<P extends RoutePath>(options: NavigateOptions<P>): void {\n const { to, replace, state, search = {}, preserveSearch, params } = options;\n\n const searchParams = search instanceof URLSearchParams ? search : new URLSearchParams(search);\n\n if (preserveSearch) {\n for (const [name, value] of this.search) {\n if (!searchParams.has(name)) {\n searchParams.set(name, value);\n }\n }\n }\n\n const location = {\n pathname: resolvePath(to, params),\n search: searchParams.size ? `?${searchParams.toString()}` : undefined,\n };\n\n if (replace) {\n this.history.replace(location, state);\n } else {\n this.history.push(location, state);\n }\n }\n\n setQueryParam(param: string, value: string): void {\n const params = new URLSearchParams(this.location.search);\n params.set(param, value);\n this.history.replace({ search: params.toString() });\n }\n\n removeQueryParam(param: string): string | undefined {\n const params = new URLSearchParams(this.location.search);\n const value = params.get(param) ?? undefined;\n if (value !== undefined) {\n params.delete(param);\n this.history.replace({ search: params.toString() });\n }\n return value;\n }\n\n async setLocation(location: Location): Promise<void> {\n if (!this.routesDef) return;\n\n // TODO: this should not be the responsibility of mobx-router\n // and should really be handled server-side\n if (location.pathname !== \"/\" && location.pathname.endsWith(\"/\")) {\n this.history.replace({ ...location, pathname: location.pathname.slice(0, -1) });\n return;\n }\n\n this.location = location;\n\n try {\n const matchedRoute = matchRoute(location.pathname, this.routesDef);\n\n await matchedRoute.guard();\n\n // navigating within a guard function\n // is essentially a redirect\n if (this.location !== location) {\n return;\n }\n\n runInAction(() => {\n this.activeRoute = matchedRoute;\n });\n\n await this.activeRoute?.load();\n } catch (e) {\n if (e instanceof Redirect) {\n this.navigate(e.options);\n return;\n }\n throw e;\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"router.mjs","names":[],"sources":["../src/router/errors.ts","../src/router/components/error.tsx","../src/router/symbols.ts","../src/router/util.ts","../src/router/components/router.tsx","../src/router/components/link.tsx","../src/router/components/navigate.tsx","../src/router/redirect.ts","../src/router/outlet.tsx","../src/router/route.tsx","../src/router/make-routes.tsx","../src/router/router.store.ts"],"sourcesContent":["import type { MatchState } from \"./make-routes\";\n\nexport type RouterErrorType = \"NOT_FOUND\" | \"GUARD\" | \"LOAD\" | \"RENDER\";\n\nexport interface RouterErrorOptions {\n message?: string;\n cause?: unknown;\n path?: string;\n}\n\nconst defaultMessage = (type: RouterErrorType, path?: string): string => {\n switch (type) {\n case \"NOT_FOUND\":\n return path ? `No route matches '${path}'.` : \"No matching route.\";\n case \"GUARD\":\n return \"A route guard rejected the navigation.\";\n case \"LOAD\":\n return \"A route loader or lazy component failed.\";\n case \"RENDER\":\n return \"A route component failed to render.\";\n }\n};\n\n/**\n * The single error type surfaced to `[ERROR]` components. `type`\n * discriminates the failure source; when the router wraps an\n * application-level error (thrown by a guard or loader), the original\n * is preserved on the standard `cause` property.\n *\n * Guards and loaders may also throw `RouterError` directly — e.g.\n * `throw new RouterError(\"NOT_FOUND\")` from a loader when an entity\n * doesn't exist — and it passes through unwrapped.\n */\nexport class RouterError extends Error {\n readonly type: RouterErrorType;\n readonly path?: string;\n\n /** @internal matched-prefix state captured when the matcher throws NOT_FOUND */\n state?: MatchState;\n /** @internal level index of the failing guard, for depth-aware bubbling */\n depth?: number;\n\n constructor(type: RouterErrorType, options?: RouterErrorOptions) {\n super(options?.message ?? defaultMessage(type, options?.path), { cause: options?.cause });\n this.name = \"RouterError\";\n this.type = type;\n this.path = options?.path;\n }\n}\n","import React from \"react\";\nimport { RouterError } from \"../errors\";\nimport type { Route } from \"../route\";\nimport type { Component, ErrorComponentProps } from \"../types\";\n\n/**\n * Rendered when an error occurs and no `[ERROR]` component is defined\n * on the matched prefix. Deliberately minimal and dependency-free —\n * define a root-level `[ERROR]` to replace it.\n */\nexport const DefaultErrorPage: Component = ({ error }: ErrorComponentProps) => (\n <div role=\"alert\">\n <h1>{error.type === \"NOT_FOUND\" ? \"Page Not Found\" : \"Something Went Wrong\"}</h1>\n <p>{error.message}</p>\n </div>\n);\n\nexport interface RouteErrorBoundaryProps {\n route: Route;\n fallback: Component;\n children?: React.ReactNode;\n}\n\ninterface RouteErrorBoundaryState {\n error?: RouterError;\n route?: Route;\n}\n\n/**\n * Catches render-time crashes in page and `[WRAPPER]` components and\n * renders the nearest `[ERROR]` component with `type: \"RENDER\"`. Mounted\n * inside the `[LAYOUT]` so the layout survives page crashes; crashes in\n * the layout itself (or in the fallback) propagate out of `<Router>` by\n * design — those are developer bugs that should stay loud.\n *\n * Deliberately NOT keyed by location: the boundary must be transparent\n * to reconciliation (a key would remount the entire subtree and re-fire\n * every effect on each navigation). Instead, a captured error is cleared\n * when a new Route object arrives.\n */\nexport class RouteErrorBoundary extends React.Component<\n RouteErrorBoundaryProps,\n RouteErrorBoundaryState\n> {\n override state: RouteErrorBoundaryState = {};\n\n static getDerivedStateFromError(cause: unknown): RouteErrorBoundaryState {\n return { error: cause instanceof RouterError ? cause : new RouterError(\"RENDER\", { cause }) };\n }\n\n static getDerivedStateFromProps(\n props: RouteErrorBoundaryProps,\n state: RouteErrorBoundaryState,\n ): Partial<RouteErrorBoundaryState> | null {\n if (state.route === props.route) return null;\n // a new Route object means a navigation occurred — clear any\n // captured error so the boundary doesn't keep a stale fallback\n return { route: props.route, error: undefined };\n }\n\n override render(): React.ReactNode {\n if (!this.state.error) return this.props.children;\n\n const Fallback = this.props.fallback;\n return <Fallback route={this.props.route} error={this.state.error} />;\n }\n}\n","export const CONTEXT: unique symbol = Symbol.for(\"MOBX_ROUTER_CONTEXT\");\nexport const LAYOUT: unique symbol = Symbol.for(\"MOBX_ROUTER_LAYOUT\");\nexport const WRAPPER: unique symbol = Symbol.for(\"MOBX_ROUTER_WRAPPER\");\nexport const LOAD: unique symbol = Symbol.for(\"MOBX_ROUTER_LOAD\");\nexport const GUARD: unique symbol = Symbol.for(\"MOBX_ROUTER_GUARD\");\nexport const PAGE: unique symbol = Symbol.for(\"MOBX_ROUTER_PAGE\");\nexport const ERROR: unique symbol = Symbol.for(\"MOBX_ROUTER_ERROR\");\nexport const REDIRECT: unique symbol = Symbol.for(\"MOBX_ROUTER_REDIRECT\");\n","import { PAGE, REDIRECT } from \"./symbols\";\nimport type { Component, LazyComponent, Leaf, Obj, Page, Redirector } from \"./types\";\n\nexport const resolvePath = (to: string, params?: Obj): string => {\n return to.replaceAll(/:[^/]*/g, (segment) => {\n const value = params?.[segment.slice(1)];\n if (!value)\n throw new Error(`Unable to resolve route '${to}'. Parameter '${segment}' not specified.`);\n return value;\n });\n};\n\nexport const isComponent = (data: any): data is Component => {\n if (!data) return false;\n return (\n typeof data === \"function\" ||\n (typeof data === \"object\" && data[\"$$typeof\"] === Symbol.for(\"react.memo\"))\n );\n};\n\nexport const isPage = (data: any): data is Page => {\n if (typeof data !== \"object\") return false;\n const symbols = Object.getOwnPropertySymbols(data);\n return symbols.includes(PAGE);\n};\n\nexport const isRedirect = (data: any): data is Redirector => {\n if (typeof data !== \"object\") return false;\n const symbols = Object.getOwnPropertySymbols(data);\n return symbols.includes(REDIRECT);\n};\n\nexport const isLeaf = (data: any): data is Leaf => {\n return isComponent(data) || isPage(data) || isRedirect(data);\n};\n\nexport const isLazyComponent = (data: any): data is LazyComponent => {\n return typeof data === \"function\" && data.toString().startsWith(\"() => import(\");\n};\n","import { observer } from \"mobx-react-lite\";\nimport { createContext, useContext } from \"react\";\nimport type { Route } from \"../route\";\nimport type { RouterStore } from \"../router.store\";\nimport type { Component } from \"../types\";\nimport { DefaultErrorPage, RouteErrorBoundary } from \"./error\";\n\nexport const PassThrough: Component = ({ children }) => children;\n\n// Plain (non-observer) renderer. State observation lives one level up\n// in `Router`, so the page component renders as a child of a plain\n// FunctionComponent — no memo wrapper in the parent chain to interact\n// with React Refresh's family-update propagation.\nexport const RouterOutlet: React.FC<{ route: Route; components: (Component | undefined)[] }> = ({\n route,\n components,\n}) => {\n const [C, ...remaining] = components;\n\n if (!C) return null;\n\n return (\n <C route={route}>\n {remaining.length > 0 && <RouterOutlet route={route} components={remaining} />}\n </C>\n );\n};\n\nexport const routerContext = createContext<RouterStore>(null as any);\nexport const useRouter = () => useContext(routerContext);\n\nexport interface RouterProps {\n store: RouterStore;\n}\n\nexport const Router = observer(({ store }: RouterProps) => {\n const route = store.activeRoute;\n if (!route) {\n return null;\n }\n\n const Layout = route.layout ?? PassThrough;\n const components = route.outlets.map((o) => o.Component);\n const outlet = <RouterOutlet route={route} components={components} />;\n\n // Render crashes in pages/wrappers funnel to the nearest [ERROR]\n // component; the layout survives. On synthetic error routes the\n // boundary is omitted so a crashing [ERROR] component propagates\n // out of <Router> — a developer bug that should stay loud. Layout\n // crashes propagate for the same reason. The boundary is unkeyed on\n // purpose — it resets itself when the route changes — so navigation\n // reconciles by component type instead of remounting the subtree.\n const fallback = route.levels.at(-1)?.errorComponent ?? DefaultErrorPage;\n\n return (\n <routerContext.Provider value={store}>\n <Layout route={route}>\n {route.error ? (\n outlet\n ) : (\n <RouteErrorBoundary route={route} fallback={fallback}>\n {outlet}\n </RouteErrorBoundary>\n )}\n </Layout>\n </routerContext.Provider>\n );\n});\n","import { observer } from \"mobx-react-lite\";\nimport React, { useCallback } from \"react\";\nimport type {\n DynamicRoutePath,\n ExtractParams,\n NavigateOptions,\n RoutePath,\n StaticRoutePath,\n} from \"../types\";\nimport { resolvePath } from \"../util\";\nimport { useRouter } from \"./router\";\n\ntype LinkComponentProps<C extends React.ElementType> = Omit<\n React.ComponentProps<C>,\n \"ref\" | \"exact\" | \"to\" | \"params\" | \"onClick\" | \"asChild\"\n>;\n\nexport type LinkPropsBase<\n C extends React.ElementType,\n I extends React.ElementType = C,\n> = LinkComponentProps<C> & {\n exact?: boolean;\n preserveSearch?: boolean;\n ref?: React.Ref<React.ComponentRef<I>>;\n};\n\n// function overloading is much faster than leveraging conditional types\n// but once the typescript go compiler is released and performance is no\n// longer an issue, it might make sense to simplify this a bit so it can\n// be more easily consumed by users\nexport interface LinkComponent<C extends React.ElementType, I extends React.ElementType = C> {\n <P extends StaticRoutePath>(\n props: LinkPropsBase<C, I> & { to: P; params?: undefined },\n ): React.ReactNode;\n <P extends DynamicRoutePath>(\n props: LinkPropsBase<C, I> & { to: P; params: ExtractParams<P> },\n ): React.ReactNode;\n}\n\n// final thing to do is make sure refs still work in React 19\n\n// this smooths over some of the awkwardness when extending this component\nexport const makeLinkComponent = <C extends React.ElementType, I extends React.ElementType = C>(\n C: C,\n baseProps?: Partial<LinkComponentProps<C>> & { as?: I },\n) => {\n return observer(({ to, params, exact, preserveSearch, children, ...props }: any) => {\n const router = useRouter();\n const mergedProps = { ...baseProps, ...props };\n\n if (props.role !== \"link\") {\n mergedProps.href = resolvePath(to, params);\n }\n\n if (router.doesPathMatch(to, exact)) {\n mergedProps[\"aria-current\"] = \"page\";\n }\n\n mergedProps.onClick = useCallback(\n (event: React.MouseEvent<HTMLElement>) => {\n event.preventDefault();\n if (props.disabled) return;\n router.navigate({ to, preserveSearch, params } as NavigateOptions<RoutePath>);\n },\n [router, params, to, props.disabled],\n );\n\n return React.createElement(C, mergedProps, children);\n }) as LinkComponent<C, I>;\n};\n\n//export const Link = makeLinkComponent('a');\n","import { useLayoutEffect } from \"react\";\nimport type { NavigateOptions, RoutePath } from \"../types\";\nimport { useRouter } from \"./router\";\n\nexport const Navigate = <P extends RoutePath>(props: NavigateOptions<P>) => {\n const router = useRouter();\n\n useLayoutEffect(() => {\n router.navigate(props);\n }, [router, props]);\n\n return null;\n};\n","import type { NavigateOptions, RoutePath } from \"./types\";\n\nexport class Redirect<P extends RoutePath> {\n constructor(readonly options: NavigateOptions<P>) {}\n}\n\nexport const redirect = <P extends RoutePath>(options: NavigateOptions<P>): Redirect<P> =>\n new Redirect(options);\n","import { makeAutoObservable, observable } from \"mobx\";\nimport { DefaultErrorPage } from \"./components/error\";\nimport { RouterError } from \"./errors\";\nimport { Redirect } from \"./redirect\";\nimport type { Route } from \"./route\";\nimport type { Component, LazyComponent, Loader, Obj } from \"./types\";\nimport { isLazyComponent } from \"./util\";\n\nexport interface OutletConfig {\n component?: Component | LazyComponent;\n loader?: Loader;\n errorComponent?: Component;\n}\n\nexport type RouteSegmentState = \"preloading\" | \"loading\" | \"error\" | \"ready\";\n\nexport const DefaultOutlet: Component = ({ children }) => children;\n\nconst LoadingPlaceholder: Component = () => <p>Loading...</p>;\n\nexport class Outlet {\n state: RouteSegmentState = \"preloading\";\n promise: Promise<unknown> | undefined;\n data: unknown;\n error: RouterError | undefined;\n\n // Plain (non-observable) reference. The page component must reach\n // React unmediated by MobX — once MobX deep-observes the holder,\n // React Refresh can no longer swap the page identity via family\n // lookup on the original function. This mirrors how Route holds\n // `layout` as a plain field under makeObservable.\n component: Component | undefined;\n\n get Component(): Component | undefined {\n switch (this.state) {\n case \"loading\":\n return LoadingPlaceholder;\n case \"ready\":\n return this.component ?? DefaultOutlet;\n case \"error\": {\n // render the nearest [ERROR] component in this outlet's slot,\n // leaving the rest of the page intact\n const ErrorComponent = this.config.errorComponent ?? DefaultErrorPage;\n const error = this.error ?? new RouterError(\"LOAD\");\n return (props: Obj) => <ErrorComponent {...props} error={error} />;\n }\n default:\n return undefined;\n }\n }\n\n constructor(readonly config: OutletConfig) {\n if (!isLazyComponent(config.component)) {\n this.component = config.component;\n }\n\n makeAutoObservable<Outlet, \"component\" | \"config\">(this, {\n promise: observable.ref,\n data: observable.ref,\n error: observable.ref,\n component: false,\n config: false,\n });\n }\n\n async load(route: Route): Promise<void> {\n const promises: Promise<void>[] = [];\n\n if (isLazyComponent(this.config.component) && !this.component) {\n promises.push(this.loadComponent());\n }\n\n if (this.config.loader) {\n promises.push(this.loadData(route));\n }\n\n if (!promises.length) {\n this.setState(\"ready\");\n return;\n }\n\n // wait to transition to loading to avoid\n // screen flashes when the loader function\n // executes quickly\n const preloadingTimer = setTimeout(() => {\n if (this.state === \"preloading\") {\n this.setState(\"loading\");\n }\n }, 300);\n\n this.promise = Promise.all(promises)\n .then(() => {\n clearTimeout(preloadingTimer);\n if (this.state === \"loading\") {\n // if we had to transition to regular loading because\n // the loader was taking too long, force an additional\n // timeout to prevent a loading flash\n setTimeout(() => {\n this.setState(\"ready\");\n }, 300);\n } else {\n this.setState(\"ready\");\n }\n })\n .catch((e) => {\n clearTimeout(preloadingTimer);\n this.setState(\"error\");\n // a Redirect thrown by a loader propagates so the router\n // navigates; everything else renders in-slot error UI\n if (e instanceof Redirect) throw e;\n this.setError(e instanceof RouterError ? e : new RouterError(\"LOAD\", { cause: e }));\n });\n\n await this.promise;\n }\n\n setData(data: unknown) {\n this.data = data;\n }\n\n setError(error: RouterError) {\n this.error = error;\n }\n\n setState(state: RouteSegmentState) {\n this.state = state;\n }\n\n private async loadData(route: Route): Promise<void> {\n await this.config.loader?.(route).then((data) => this.setData(data));\n }\n\n private async loadComponent(): Promise<void> {\n if (!isLazyComponent(this.config.component)) return;\n const module = await this.config.component();\n for (const exportName in module) {\n if (exportName === \"default\" || exportName.endsWith(\"Page\")) {\n this.component = module[exportName];\n return;\n }\n }\n throw new Error(\n \"Lazy route component module did not export `default` or a `*Page` named export\",\n );\n }\n}\n","import { computed, makeObservable } from \"mobx\";\nimport { RouterError } from \"./errors\";\nimport type { Outlet } from \"./outlet\";\nimport { Redirect } from \"./redirect\";\nimport type { Component, Guard, GuardEntry, MatchLevel, Obj } from \"./types\";\n\nexport interface RouteConfig {\n path: string;\n outlets: Outlet[];\n guards: GuardEntry[];\n levels: MatchLevel[];\n context?: Obj;\n layout?: Component;\n params: Obj;\n error?: RouterError;\n}\n\nexport class Route {\n readonly path: string;\n readonly outlets: Outlet[];\n readonly guards: Guard[];\n readonly context: Obj;\n readonly params: Obj;\n readonly layout?: Component;\n /** set on synthetic error routes; the error being rendered */\n readonly error?: RouterError;\n /** @internal */\n readonly guardEntries: GuardEntry[];\n /** @internal */\n readonly levels: MatchLevel[];\n\n get data(): Obj {\n return Object.assign({}, ...this.outlets.map((o) => o.data));\n }\n\n constructor(def: RouteConfig) {\n this.path = def.path;\n this.guardEntries = def.guards;\n this.guards = def.guards.map((entry) => entry.guard);\n this.levels = def.levels;\n this.context = def.context ?? {};\n this.outlets = def.outlets;\n this.params = def.params;\n this.layout = def.layout;\n this.error = def.error;\n\n makeObservable(this, {\n data: computed,\n });\n }\n\n async guard(): Promise<void> {\n for (const { guard, depth } of this.guardEntries) {\n try {\n await guard(this);\n } catch (e) {\n if (e instanceof Redirect) throw e;\n const error = e instanceof RouterError ? e : new RouterError(\"GUARD\", { cause: e });\n error.depth ??= depth;\n throw error;\n }\n }\n }\n\n async load(): Promise<void> {\n await Promise.all(this.outlets.map((outlet) => outlet.load(this)));\n }\n}\n","import { DefaultErrorPage } from \"./components/error\";\nimport { RouterError } from \"./errors\";\nimport { Outlet } from \"./outlet\";\nimport { Redirect } from \"./redirect\";\nimport { Route } from \"./route\";\nimport { CONTEXT, ERROR, GUARD, LAYOUT, LOAD, PAGE, REDIRECT, WRAPPER } from \"./symbols\";\nimport type { Component, GuardEntry, MatchLevel, Obj, Routes } from \"./types\";\nimport { isComponent, isLazyComponent, isLeaf, isPage, isRedirect } from \"./util\";\n\nconst pathToSegments = (path: string): string[] => {\n return path.replace(/^\\/+|\\/+$/g, \"\").split(\"/\");\n};\n\nexport interface MatchState {\n segments: string[];\n context: Obj;\n params: Obj;\n outlets: (Outlet | undefined)[];\n guards: GuardEntry[];\n levels: MatchLevel[];\n layout?: Component;\n errorComponent?: Component;\n}\n\nexport const makeRoute = (matchState: MatchState): Route => {\n const outlets = matchState.outlets.filter((o) => o !== undefined);\n\n return new Route({ ...matchState, outlets, path: matchState.segments.join(\"/\") });\n};\n\n/**\n * Builds the synthetic route rendered when navigation fails. Bubbles\n * from the failing level (`error.depth`, defaulting to the deepest\n * matched level) to the nearest `[ERROR]` component, preserving the\n * `[LAYOUT]` and `[WRAPPER]`s accumulated up to that level. Ancestor\n * `[LOAD]` loaders are intentionally not run — error routes never\n * fetch data.\n */\nexport const makeErrorRoute = (\n error: RouterError,\n pathname: string,\n source?: { levels: MatchLevel[]; params: Obj; context: Obj },\n): Route => {\n const levels = error.state?.levels ?? source?.levels ?? [];\n const depth = Math.min(error.depth ?? levels.length - 1, levels.length - 1);\n const level = depth >= 0 ? levels[depth] : undefined;\n\n const ErrorComponent = level?.errorComponent ?? DefaultErrorPage;\n const outlets = levels\n .slice(0, depth + 1)\n .flatMap((l) => (l.wrapper ? [new Outlet({ component: l.wrapper })] : []));\n outlets.push(\n new Outlet({ component: (props: Obj) => <ErrorComponent {...props} error={error} /> }),\n );\n\n return new Route({\n path: pathname.replace(/^\\/+/, \"\"),\n outlets,\n guards: [],\n levels: [],\n params: error.state?.params ?? source?.params ?? {},\n context: error.state?.context ?? source?.context ?? {},\n layout: level?.layout,\n error,\n });\n};\n\nconst notFound = (state: MatchState, attemptedSegments: string[]): RouterError => {\n const error = new RouterError(\"NOT_FOUND\", {\n path: `/${attemptedSegments.filter((s) => s !== \"\").join(\"/\")}`,\n });\n error.state = state;\n return error;\n};\n\nexport const matchRoute = (path: string, routeDef: Routes, matchState?: MatchState): Route => {\n const depth = matchState?.levels.length ?? 0;\n const layout = routeDef[LAYOUT] ?? matchState?.layout;\n const errorComponent = routeDef[ERROR] ?? matchState?.errorComponent;\n\n const state: MatchState = {\n segments: [],\n params: {},\n ...matchState,\n layout,\n errorComponent,\n context: { ...matchState?.context, ...routeDef[CONTEXT] },\n guards: [\n ...(matchState?.guards ?? []),\n ...(routeDef[GUARD] ? [{ guard: routeDef[GUARD], depth }] : []),\n ],\n outlets: [\n ...(matchState?.outlets ?? []),\n routeDef[WRAPPER] ? new Outlet({ component: routeDef[WRAPPER], errorComponent }) : undefined,\n routeDef[LOAD] ? new Outlet({ loader: routeDef[LOAD], errorComponent }) : undefined,\n ],\n levels: [...(matchState?.levels ?? []), { wrapper: routeDef[WRAPPER], layout, errorComponent }],\n };\n\n const [segment, ...remainingSegments] = pathToSegments(path);\n const remainingPath = remainingSegments.join(\"/\");\n\n let defAtSegment = routeDef[segment || \"index\"];\n\n if (!defAtSegment) {\n const matchedSegment = Object.keys(routeDef).find(\n (segment) => segment.startsWith(\"$\") || segment.startsWith(\":\"),\n );\n if (matchedSegment) {\n defAtSegment = routeDef[matchedSegment];\n state.params[matchedSegment.slice(1)] = segment;\n }\n }\n\n if (!defAtSegment) {\n throw notFound(state, [...state.segments, segment ?? \"\", ...remainingSegments]);\n }\n\n state.segments.push(segment ?? \"index\");\n\n if (isLeaf(defAtSegment)) {\n if (remainingPath) {\n throw notFound(state, [...state.segments, ...remainingSegments]);\n }\n\n if (isRedirect(defAtSegment)) {\n const redirect =\n typeof defAtSegment[REDIRECT] === \"string\"\n ? { to: defAtSegment[REDIRECT] }\n : defAtSegment[REDIRECT];\n throw new Redirect(redirect as any);\n }\n\n if (isComponent(defAtSegment) || isLazyComponent(defAtSegment)) {\n state.outlets.push(new Outlet({ component: defAtSegment, errorComponent }));\n return makeRoute(state);\n }\n }\n\n // at this point we have a nested route or a [Page] definition\n\n if (isPage(defAtSegment)) {\n state.layout = defAtSegment[LAYOUT] ?? state.layout;\n state.errorComponent = defAtSegment[ERROR] ?? state.errorComponent;\n Object.assign(state.context, defAtSegment[CONTEXT]);\n if (defAtSegment[GUARD]) {\n state.guards.push({ guard: defAtSegment[GUARD], depth });\n }\n state.outlets.push(\n new Outlet({\n component: defAtSegment[PAGE],\n loader: defAtSegment[LOAD],\n errorComponent: state.errorComponent,\n }),\n );\n state.levels[state.levels.length - 1] = {\n ...state.levels[state.levels.length - 1],\n layout: state.layout,\n errorComponent: state.errorComponent,\n };\n\n return makeRoute(state);\n }\n\n // now we know we have a nested route\n return matchRoute(remainingPath, defAtSegment, state);\n};\n\n// TODO: ideally this could resolve to something less than R,\n// but specific enough to infer all paths as a literal union.\n// As it stands, there are certain things we can't access reliably\n// without the compiler complaining about circular references\n// try \"as const satisfies\" approach which would allow us to\n// exchange a less specific version of MobxRoutesRoot for this\nexport const makeRoutes =\n () =>\n <R extends Routes>(routes: R): R => {\n // todo: perform some validation here\n // - no forward slashes in keys\n // - at most one variable segment per level\n // - only lowercase letters (except variables)\n // - paths/variables cannot contain $ or : that aren't at the beginning\n // - path variables must be unique across a path\n return routes;\n };\n","import { createBrowserHistory, type History, type Location } from \"history\";\nimport { action, computed, makeObservable, observable, runInAction } from \"mobx\";\nimport { RouterError } from \"./errors\";\nimport { makeErrorRoute, matchRoute } from \"./make-routes\";\nimport { Redirect } from \"./redirect\";\nimport type { Route } from \"./route\";\nimport type { Component, MobxRouterConfig, NavigateOptions, Obj, RoutePath, Routes } from \"./types\";\nimport { resolvePath } from \"./util\";\n\nexport interface MobxRenderSegment {\n segment: string;\n component: Component;\n props?: Obj;\n}\n\nexport class RouterStore {\n readonly history: History;\n\n routesDef?: Routes;\n\n location!: Location;\n activeRoute: Route | undefined;\n\n get search(): URLSearchParams {\n return new URLSearchParams(this.location?.search);\n }\n\n get query(): Record<string, string> {\n return Object.fromEntries(this.search);\n }\n\n get pathParams(): Record<string, string> {\n return { ...this.activeRoute?.params };\n }\n\n get activeSegments(): string[] {\n return this.activeRoute?.path.split(\"/\") ?? [];\n }\n\n constructor(config?: MobxRouterConfig) {\n makeObservable(this, {\n location: observable.ref,\n activeRoute: observable.ref,\n\n search: computed,\n pathParams: computed,\n activeSegments: computed,\n\n setLocation: action,\n });\n\n this.history = config?.history ?? createBrowserHistory();\n }\n\n initialize(routesDef: Routes): void {\n this.routesDef = routesDef;\n this.history.listen((data) => {\n void this.setLocation(data.location);\n });\n\n void this.setLocation(this.history.location);\n }\n\n doesPathMatch<P extends RoutePath>(path: P, exact?: boolean): boolean {\n const segments = path.slice(1).split(\"/\");\n const segmentsMatch = segments.every(\n (segment, i) => segment === this.activeSegments[i] || segment.startsWith(\":\"),\n );\n\n return (\n segmentsMatch &&\n this.activeSegments.length >= segments.length &&\n (!exact || segments.length === this.activeSegments.length)\n );\n }\n\n navigate<P extends RoutePath>(options: NavigateOptions<P>): void {\n // navigating to the current URL attaches no new information — skip\n // the navigation (and its view transition) entirely so redundant\n // navigations (e.g. clicking an already-active link) cause no churn\n if (!options.state && this.isCurrentLocation(options)) {\n return;\n }\n\n if (!document.startViewTransition) {\n this._navigate(options);\n } else {\n const transition = document.startViewTransition(() => this._navigate(options));\n transition.ready.catch((e) => console.log(e, typeof e));\n }\n }\n\n _navigate<P extends RoutePath>(options: NavigateOptions<P>): void {\n const location = this.resolveLocation(options);\n\n if (options.replace) {\n this.history.replace(location, options.state);\n } else {\n this.history.push(location, options.state);\n }\n }\n\n private resolveLocation<P extends RoutePath>(\n options: NavigateOptions<P>,\n ): { pathname: string; search: string | undefined } {\n const { to, search = {}, preserveSearch, params } = options;\n\n const searchParams = search instanceof URLSearchParams ? search : new URLSearchParams(search);\n\n if (preserveSearch) {\n for (const [name, value] of this.search) {\n if (!searchParams.has(name)) {\n searchParams.set(name, value);\n }\n }\n }\n\n return {\n pathname: resolvePath(to, params),\n search: searchParams.size ? `?${searchParams.toString()}` : undefined,\n };\n }\n\n private isCurrentLocation<P extends RoutePath>(options: NavigateOptions<P>): boolean {\n if (!this.location) return false;\n\n const target = this.resolveLocation(options);\n return (\n target.pathname === this.location.pathname && (target.search ?? \"\") === this.location.search\n );\n }\n\n setQueryParam(param: string, value: string): void {\n const params = new URLSearchParams(this.location.search);\n params.set(param, value);\n this.history.replace({ search: `?${params.toString()}` });\n }\n\n removeQueryParam(param: string): string | undefined {\n const params = new URLSearchParams(this.location.search);\n const value = params.get(param) ?? undefined;\n if (value !== undefined) {\n params.delete(param);\n this.history.replace({ search: params.size ? `?${params.toString()}` : \"\" });\n }\n return value;\n }\n\n async setLocation(location: Location): Promise<void> {\n if (!this.routesDef) return;\n\n // TODO: this should not be the responsibility of mobx-router\n // and should really be handled server-side\n if (location.pathname !== \"/\" && location.pathname.endsWith(\"/\")) {\n this.history.replace({ ...location, pathname: location.pathname.slice(0, -1) });\n return;\n }\n\n // a same-pathname change (query params, history state) can't affect\n // which route matches, its guards, or its loaders (none of which can\n // observe search params) — update the observable location without\n // rebuilding the route, so query-param changes don't refetch loaders\n // or replace activeRoute\n if (this.activeRoute && this.location?.pathname === location.pathname) {\n this.location = location;\n return;\n }\n\n this.location = location;\n\n let matchedRoute: Route | undefined;\n try {\n matchedRoute = matchRoute(location.pathname, this.routesDef);\n\n await matchedRoute.guard();\n\n // navigating within a guard function\n // is essentially a redirect\n if (this.location !== location) {\n return;\n }\n\n runInAction(() => {\n this.activeRoute = matchedRoute;\n });\n\n await this.activeRoute?.load();\n } catch (e) {\n if (e instanceof Redirect) {\n this.navigate(e.options);\n return;\n }\n\n // navigating within a guard before it threw — treat as a redirect\n if (this.location !== location) {\n return;\n }\n\n const error =\n e instanceof RouterError\n ? e\n : new RouterError(\"RENDER\", { cause: e, path: location.pathname });\n console.error(error);\n\n const errorRoute = makeErrorRoute(error, location.pathname, matchedRoute);\n runInAction(() => {\n this.activeRoute = errorRoute;\n });\n await errorRoute.load();\n }\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jayalfredprufrock/mobx-toolbox",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "Misc mobx/react modules/utilities.",
|
|
5
5
|
"homepage": "https://github.com/jayalfredprufrock/mobx-toolbox#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -33,10 +33,13 @@
|
|
|
33
33
|
"@tsconfig/node24": "^24.0.4",
|
|
34
34
|
"@types/node": "^24.12.0",
|
|
35
35
|
"@types/react": "^19.2.2",
|
|
36
|
+
"@types/react-dom": "catalog:",
|
|
36
37
|
"@typescript/native-preview": "7.0.0-dev.20260328.1",
|
|
38
|
+
"happy-dom": "catalog:",
|
|
37
39
|
"history": "^5.3.0",
|
|
38
40
|
"mobx-react-lite": "^4.1.1",
|
|
39
|
-
"react": "
|
|
41
|
+
"react": "catalog:",
|
|
42
|
+
"react-dom": "catalog:",
|
|
40
43
|
"semantic-release": "^25.0.3",
|
|
41
44
|
"typebox": "^1.1.17",
|
|
42
45
|
"typescript": "^6.0.2",
|