@esmx/router 3.0.0-rc.103

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 (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +77 -0
  3. package/README.zh-CN.md +158 -0
  4. package/dist/error.d.ts +23 -0
  5. package/dist/error.mjs +64 -0
  6. package/dist/increment-id.d.ts +7 -0
  7. package/dist/increment-id.mjs +16 -0
  8. package/dist/index.d.ts +14 -0
  9. package/dist/index.mjs +13 -0
  10. package/dist/location.d.ts +22 -0
  11. package/dist/location.mjs +64 -0
  12. package/dist/matcher.d.ts +4 -0
  13. package/dist/matcher.mjs +46 -0
  14. package/dist/micro-app.d.ts +18 -0
  15. package/dist/micro-app.mjs +85 -0
  16. package/dist/navigation.d.ts +45 -0
  17. package/dist/navigation.mjs +153 -0
  18. package/dist/options.d.ts +4 -0
  19. package/dist/options.mjs +94 -0
  20. package/dist/route-task.d.ts +40 -0
  21. package/dist/route-task.mjs +77 -0
  22. package/dist/route-transition.d.ts +53 -0
  23. package/dist/route-transition.mjs +356 -0
  24. package/dist/route.d.ts +77 -0
  25. package/dist/route.mjs +223 -0
  26. package/dist/router-link.d.ts +10 -0
  27. package/dist/router-link.mjs +139 -0
  28. package/dist/router.d.ts +122 -0
  29. package/dist/router.mjs +355 -0
  30. package/dist/scroll.d.ts +33 -0
  31. package/dist/scroll.mjs +49 -0
  32. package/dist/types.d.ts +282 -0
  33. package/dist/types.mjs +18 -0
  34. package/dist/util.d.ts +27 -0
  35. package/dist/util.mjs +67 -0
  36. package/package.json +62 -0
  37. package/src/error.ts +84 -0
  38. package/src/increment-id.ts +12 -0
  39. package/src/index.ts +67 -0
  40. package/src/location.ts +124 -0
  41. package/src/matcher.ts +68 -0
  42. package/src/micro-app.ts +101 -0
  43. package/src/navigation.ts +202 -0
  44. package/src/options.ts +135 -0
  45. package/src/route-task.ts +102 -0
  46. package/src/route-transition.ts +472 -0
  47. package/src/route.ts +335 -0
  48. package/src/router-link.ts +238 -0
  49. package/src/router.ts +395 -0
  50. package/src/scroll.ts +106 -0
  51. package/src/types.ts +381 -0
  52. package/src/util.ts +133 -0
@@ -0,0 +1,356 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+ import { Route } from "./route.mjs";
5
+ import {
6
+ createRouteTask,
7
+ RouteTaskController
8
+ } from "./route-task.mjs";
9
+ import {
10
+ getSavedScrollPosition,
11
+ saveScrollPosition,
12
+ scrollToPosition
13
+ } from "./scroll.mjs";
14
+ import { RouteType } from "./types.mjs";
15
+ import {
16
+ isBrowser,
17
+ isRouteMatched,
18
+ isUrlEqual,
19
+ isValidConfirmHookResult,
20
+ removeFromArray
21
+ } from "./util.mjs";
22
+ export const ROUTE_TRANSITION_HOOKS = {
23
+ async fallback(to, from, router) {
24
+ if (to.matched.length === 0) {
25
+ return router.parsedOptions.fallback;
26
+ }
27
+ },
28
+ async override(to, from, router) {
29
+ var _a, _b;
30
+ const result = await ((_b = (_a = to.config) == null ? void 0 : _a.override) == null ? void 0 : _b.call(_a, to, from, router));
31
+ if (isValidConfirmHookResult(result)) {
32
+ return result;
33
+ }
34
+ },
35
+ async asyncComponent(to, from, router) {
36
+ await Promise.all(
37
+ to.matched.map(async (matched) => {
38
+ const { asyncComponent, component } = matched;
39
+ if (!component && typeof asyncComponent === "function") {
40
+ try {
41
+ const result = await asyncComponent();
42
+ matched.component = result;
43
+ } catch (e) {
44
+ const error = e instanceof Error ? e : new Error(String(e));
45
+ throw new Error(
46
+ "Async component '".concat(matched.compilePath, "' is not a valid component. Original error: ").concat(error.message)
47
+ );
48
+ }
49
+ }
50
+ })
51
+ );
52
+ },
53
+ async beforeLeave(to, from, router) {
54
+ if (!(from == null ? void 0 : from.matched.length)) return;
55
+ const leavingRoutes = from.matched.filter(
56
+ (fromRoute) => !to.matched.some((toRoute) => toRoute === fromRoute)
57
+ );
58
+ for (let i = leavingRoutes.length - 1; i >= 0; i--) {
59
+ const route = leavingRoutes[i];
60
+ if (route.beforeLeave) {
61
+ const result = await route.beforeLeave(to, from, router);
62
+ if (isValidConfirmHookResult(result)) {
63
+ return result;
64
+ }
65
+ }
66
+ }
67
+ },
68
+ async beforeEnter(to, from, router) {
69
+ if (!to.matched.length) return;
70
+ const enteringRoutes = to.matched.filter(
71
+ (toRoute) => !(from == null ? void 0 : from.matched.some((fromRoute) => fromRoute === toRoute))
72
+ );
73
+ for (const route of enteringRoutes) {
74
+ if (route.beforeEnter) {
75
+ const result = await route.beforeEnter(to, from, router);
76
+ if (isValidConfirmHookResult(result)) {
77
+ return result;
78
+ }
79
+ }
80
+ }
81
+ },
82
+ async beforeUpdate(to, from, router) {
83
+ if (!isRouteMatched(to, from, "route")) return;
84
+ if (!from || to.matched.length !== from.matched.length) return;
85
+ const isSameRouteSet = to.matched.every(
86
+ (toRoute, index) => toRoute === from.matched[index]
87
+ );
88
+ if (!isSameRouteSet) return;
89
+ if (!isRouteMatched(to, from, "exact")) {
90
+ for (const route of to.matched) {
91
+ if (route.beforeUpdate) {
92
+ const result = await route.beforeUpdate(to, from, router);
93
+ if (isValidConfirmHookResult(result)) {
94
+ return result;
95
+ }
96
+ }
97
+ }
98
+ }
99
+ },
100
+ async beforeEach(to, from, router) {
101
+ const transition = router.transition;
102
+ for (const guard of transition.guards.beforeEach) {
103
+ const result = await guard(to, from, router);
104
+ if (isValidConfirmHookResult(result)) {
105
+ return result;
106
+ }
107
+ }
108
+ },
109
+ async confirm(to, from, router) {
110
+ if (to.confirm) {
111
+ const result = await to.confirm(to, from, router);
112
+ if (isValidConfirmHookResult(result)) {
113
+ return result;
114
+ }
115
+ }
116
+ if (isBrowser && "scrollRestoration" in window.history)
117
+ window.history.scrollRestoration = "manual";
118
+ if (from && isBrowser && !router.isLayer)
119
+ switch (to.type) {
120
+ case RouteType.push:
121
+ case RouteType.replace: {
122
+ if (!to.keepScrollPosition) {
123
+ saveScrollPosition(from.url.href);
124
+ scrollToPosition({ left: 0, top: 0 });
125
+ } else {
126
+ to.applyNavigationState({
127
+ __keepScrollPosition: to.keepScrollPosition
128
+ });
129
+ }
130
+ break;
131
+ }
132
+ case RouteType.go:
133
+ case RouteType.forward:
134
+ case RouteType.back:
135
+ // for popstate
136
+ case RouteType.unknown: {
137
+ saveScrollPosition(from.url.href);
138
+ setTimeout(async () => {
139
+ const state = window.history.state;
140
+ if (state == null ? void 0 : state.__keepScrollPosition) {
141
+ return;
142
+ }
143
+ const savedPosition = getSavedScrollPosition(
144
+ to.url.href,
145
+ { left: 0, top: 0 }
146
+ );
147
+ if (!savedPosition) return;
148
+ await router.parsedOptions.nextTick();
149
+ scrollToPosition(savedPosition);
150
+ });
151
+ break;
152
+ }
153
+ }
154
+ switch (to.type) {
155
+ case RouteType.push:
156
+ return ROUTE_TYPE_HANDLERS.push;
157
+ case RouteType.replace:
158
+ return ROUTE_TYPE_HANDLERS.replace;
159
+ case RouteType.restartApp:
160
+ return ROUTE_TYPE_HANDLERS.restartApp;
161
+ case RouteType.pushWindow:
162
+ return ROUTE_TYPE_HANDLERS.pushWindow;
163
+ case RouteType.replaceWindow:
164
+ return ROUTE_TYPE_HANDLERS.replaceWindow;
165
+ case RouteType.pushLayer:
166
+ return ROUTE_TYPE_HANDLERS.pushLayer;
167
+ default:
168
+ return ROUTE_TYPE_HANDLERS.default;
169
+ }
170
+ }
171
+ };
172
+ export const ROUTE_TYPE_HANDLERS = {
173
+ push(to, from, router) {
174
+ router.transition.route = to;
175
+ router.microApp._update(router);
176
+ if (!isUrlEqual(to.url, from == null ? void 0 : from.url)) {
177
+ const newState = router.navigation.push(to.state, to.url);
178
+ to.applyNavigationState(newState);
179
+ } else {
180
+ const newState = router.navigation.replace(to.state, to.url);
181
+ to.applyNavigationState(newState);
182
+ }
183
+ },
184
+ replace(to, from, router) {
185
+ router.transition.route = to;
186
+ router.microApp._update(router);
187
+ const newState = router.navigation.replace(to.state, to.url);
188
+ to.applyNavigationState(newState);
189
+ },
190
+ restartApp(to, from, router) {
191
+ router.transition.route = to;
192
+ router.microApp._update(router, true);
193
+ const newState = router.navigation.replace(to.state, to.url);
194
+ to.applyNavigationState(newState);
195
+ },
196
+ pushWindow(to, from, router) {
197
+ return router.parsedOptions.fallback(to, from, router);
198
+ },
199
+ replaceWindow(to, from, router) {
200
+ return router.parsedOptions.fallback(to, from, router);
201
+ },
202
+ async pushLayer(to, from, router) {
203
+ const { promise } = await router.createLayer(to);
204
+ return promise;
205
+ },
206
+ default(to, from, router) {
207
+ router.transition.route = to;
208
+ router.microApp._update(router);
209
+ if (!isUrlEqual(to.url, from == null ? void 0 : from.url)) {
210
+ const newState = router.navigation.replace(to.state, to.url);
211
+ to.applyNavigationState(newState);
212
+ }
213
+ }
214
+ };
215
+ const ROUTE_TRANSITION_PIPELINE = {
216
+ [RouteType.push]: [
217
+ ROUTE_TRANSITION_HOOKS.fallback,
218
+ ROUTE_TRANSITION_HOOKS.override,
219
+ ROUTE_TRANSITION_HOOKS.beforeLeave,
220
+ ROUTE_TRANSITION_HOOKS.beforeEach,
221
+ ROUTE_TRANSITION_HOOKS.beforeUpdate,
222
+ ROUTE_TRANSITION_HOOKS.beforeEnter,
223
+ ROUTE_TRANSITION_HOOKS.asyncComponent,
224
+ ROUTE_TRANSITION_HOOKS.confirm
225
+ ],
226
+ [RouteType.replace]: [
227
+ ROUTE_TRANSITION_HOOKS.fallback,
228
+ ROUTE_TRANSITION_HOOKS.override,
229
+ ROUTE_TRANSITION_HOOKS.beforeLeave,
230
+ ROUTE_TRANSITION_HOOKS.beforeEach,
231
+ ROUTE_TRANSITION_HOOKS.beforeUpdate,
232
+ ROUTE_TRANSITION_HOOKS.beforeEnter,
233
+ ROUTE_TRANSITION_HOOKS.asyncComponent,
234
+ ROUTE_TRANSITION_HOOKS.confirm
235
+ ],
236
+ [RouteType.pushWindow]: [
237
+ ROUTE_TRANSITION_HOOKS.fallback,
238
+ ROUTE_TRANSITION_HOOKS.override,
239
+ // ROUTE_TRANSITION_HOOKS.beforeLeave
240
+ ROUTE_TRANSITION_HOOKS.beforeEach,
241
+ // ROUTE_TRANSITION_HOOKS.beforeUpdate
242
+ // ROUTE_TRANSITION_HOOKS.beforeEnter
243
+ // ROUTE_TRANSITION_HOOKS.asyncComponent
244
+ ROUTE_TRANSITION_HOOKS.confirm
245
+ ],
246
+ [RouteType.replaceWindow]: [
247
+ ROUTE_TRANSITION_HOOKS.fallback,
248
+ ROUTE_TRANSITION_HOOKS.override,
249
+ ROUTE_TRANSITION_HOOKS.beforeLeave,
250
+ ROUTE_TRANSITION_HOOKS.beforeEach,
251
+ // ROUTE_TRANSITION_HOOKS.beforeUpdate
252
+ // ROUTE_TRANSITION_HOOKS.beforeEnter
253
+ // ROUTE_TRANSITION_HOOKS.asyncComponent
254
+ ROUTE_TRANSITION_HOOKS.confirm
255
+ ],
256
+ [RouteType.pushLayer]: [
257
+ ROUTE_TRANSITION_HOOKS.fallback,
258
+ ROUTE_TRANSITION_HOOKS.override,
259
+ // ROUTE_TRANSITION_HOOKS.beforeLeave
260
+ ROUTE_TRANSITION_HOOKS.beforeEach,
261
+ // ROUTE_TRANSITION_HOOKS.beforeUpdate
262
+ // ROUTE_TRANSITION_HOOKS.beforeEnter
263
+ // ROUTE_TRANSITION_HOOKS.asyncComponent
264
+ ROUTE_TRANSITION_HOOKS.confirm
265
+ ],
266
+ [RouteType.restartApp]: [
267
+ ROUTE_TRANSITION_HOOKS.fallback,
268
+ // ROUTE_TRANSITION_HOOKS.override,
269
+ ROUTE_TRANSITION_HOOKS.beforeLeave,
270
+ ROUTE_TRANSITION_HOOKS.beforeEach,
271
+ ROUTE_TRANSITION_HOOKS.beforeUpdate,
272
+ ROUTE_TRANSITION_HOOKS.beforeEnter,
273
+ ROUTE_TRANSITION_HOOKS.asyncComponent,
274
+ ROUTE_TRANSITION_HOOKS.confirm
275
+ ],
276
+ [RouteType.unknown]: [
277
+ ROUTE_TRANSITION_HOOKS.fallback,
278
+ // ROUTE_TRANSITION_HOOKS.override,
279
+ ROUTE_TRANSITION_HOOKS.beforeLeave,
280
+ ROUTE_TRANSITION_HOOKS.beforeEach,
281
+ ROUTE_TRANSITION_HOOKS.beforeUpdate,
282
+ ROUTE_TRANSITION_HOOKS.beforeEnter,
283
+ ROUTE_TRANSITION_HOOKS.asyncComponent,
284
+ ROUTE_TRANSITION_HOOKS.confirm
285
+ ]
286
+ };
287
+ export class RouteTransition {
288
+ constructor(router) {
289
+ __publicField(this, "router");
290
+ __publicField(this, "route", null);
291
+ // Task controller for the current transition.
292
+ __publicField(this, "_controller", null);
293
+ // Guard arrays, responsible for storing navigation guards.
294
+ __publicField(this, "guards", {
295
+ beforeEach: [],
296
+ afterEach: []
297
+ });
298
+ this.router = router;
299
+ }
300
+ beforeEach(guard) {
301
+ this.guards.beforeEach.push(guard);
302
+ return () => {
303
+ removeFromArray(this.guards.beforeEach, guard);
304
+ };
305
+ }
306
+ afterEach(guard) {
307
+ this.guards.afterEach.push(guard);
308
+ return () => {
309
+ removeFromArray(this.guards.afterEach, guard);
310
+ };
311
+ }
312
+ destroy() {
313
+ var _a;
314
+ (_a = this._controller) == null ? void 0 : _a.abort();
315
+ this._controller = null;
316
+ }
317
+ async to(toType, toInput) {
318
+ var _a;
319
+ const from = this.route;
320
+ const to = await this._runTask(
321
+ new Route({
322
+ options: this.router.parsedOptions,
323
+ toType,
324
+ toInput,
325
+ from: (_a = from == null ? void 0 : from.url) != null ? _a : null
326
+ }),
327
+ from
328
+ );
329
+ if (typeof to.handle === "function") {
330
+ to.handleResult = await to.handle(to, from, this.router);
331
+ }
332
+ if (to.handle) {
333
+ for (const guard of this.guards.afterEach) {
334
+ guard(to, from, this.router);
335
+ }
336
+ }
337
+ return to;
338
+ }
339
+ async _runTask(to, from) {
340
+ var _a;
341
+ (_a = this._controller) == null ? void 0 : _a.abort();
342
+ this._controller = new RouteTaskController();
343
+ const taskFunctions = ROUTE_TRANSITION_PIPELINE[to.type] || ROUTE_TRANSITION_PIPELINE[RouteType.unknown];
344
+ const tasks = taskFunctions.map((taskFn) => ({
345
+ name: taskFn.name,
346
+ task: taskFn
347
+ }));
348
+ return createRouteTask({
349
+ to,
350
+ from,
351
+ tasks,
352
+ controller: this._controller,
353
+ router: this.router
354
+ });
355
+ }
356
+ }
@@ -0,0 +1,77 @@
1
+ import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ import { type RouteConfirmHook, type RouteHandleHook, type RouteHandleResult, type RouteLayerOptions, type RouteLocationInput, type RouteMatchResult, type RouteMeta, type RouteOptions, type RouteParsedConfig, type RouteState, RouteType } from './types';
3
+ /**
4
+ * Configuration for non-enumerable properties in Route class
5
+ * These properties will be hidden during object traversal and serialization
6
+ */
7
+ export declare const NON_ENUMERABLE_PROPERTIES: string[];
8
+ /**
9
+ * Append user-provided parameters to URL path
10
+ * @param match Route matching result
11
+ * @param toInput User-provided route location object
12
+ * @param base Base URL
13
+ * @param to Current parsed URL object
14
+ */
15
+ export declare function applyRouteParams(match: RouteMatchResult, toInput: RouteLocationInput, base: URL, to: URL): void;
16
+ /**
17
+ * Route class provides complete route object functionality
18
+ */
19
+ export declare class Route {
20
+ private _handled;
21
+ private _handle;
22
+ private _handleResult;
23
+ private readonly _options;
24
+ readonly statusCode: number | null;
25
+ readonly state: RouteState;
26
+ readonly keepScrollPosition: boolean;
27
+ /** Custom confirm handler that overrides default route-transition confirm logic */
28
+ readonly confirm: RouteConfirmHook | null;
29
+ /** Layer configuration for layer routes */
30
+ readonly layer: RouteLayerOptions | null;
31
+ readonly type: RouteType;
32
+ readonly req: IncomingMessage | null;
33
+ readonly res: ServerResponse | null;
34
+ readonly context: Record<string | symbol, any>;
35
+ readonly url: URL;
36
+ readonly path: string;
37
+ readonly fullPath: string;
38
+ readonly hash: string;
39
+ readonly params: Record<string, string>;
40
+ readonly paramsArray: Record<string, string[]>;
41
+ readonly query: Record<string, string | undefined>;
42
+ readonly queryArray: Record<string, string[] | undefined>;
43
+ readonly meta: RouteMeta;
44
+ readonly matched: readonly RouteParsedConfig[];
45
+ readonly config: RouteParsedConfig | null;
46
+ /** @deprecated Use `url.pathname` instead. */
47
+ get pathname(): string;
48
+ /** @deprecated Use `url.href` instead. */
49
+ get href(): string;
50
+ constructor(routeOptions?: Partial<RouteOptions>);
51
+ get isPush(): boolean;
52
+ get handle(): RouteHandleHook | null;
53
+ set handle(val: RouteHandleHook | null);
54
+ get handleResult(): RouteHandleResult | null;
55
+ set handleResult(val: RouteHandleResult | null);
56
+ /**
57
+ * Set handle function with validation logic wrapper
58
+ */
59
+ setHandle(val: RouteHandleHook | null): void;
60
+ /**
61
+ * Apply navigation-generated state to current route
62
+ * Used by route handlers to add system state like pageId
63
+ * @param navigationState Navigation-generated state to apply
64
+ */
65
+ applyNavigationState(navigationState: Partial<RouteState>): void;
66
+ /**
67
+ * Sync all properties of current route to target route object
68
+ * Used for route object updates in reactive systems
69
+ * @param targetRoute Target route object
70
+ */
71
+ syncTo(targetRoute: Route): void;
72
+ /**
73
+ * Clone current route instance
74
+ * Returns a new Route instance with same configuration and state
75
+ */
76
+ clone(): Route;
77
+ }
package/dist/route.mjs ADDED
@@ -0,0 +1,223 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+ import { parseLocation, resolveRouteLocationInput } from "./location.mjs";
5
+ import { parsedOptions } from "./options.mjs";
6
+ import {
7
+ RouteType
8
+ } from "./types.mjs";
9
+ import { decodeParams, isNonEmptyPlainObject, isPlainObject } from "./util.mjs";
10
+ export const NON_ENUMERABLE_PROPERTIES = [
11
+ // Private fields - internal implementation details
12
+ "_handled",
13
+ "_handle",
14
+ "_handleResult",
15
+ "_options",
16
+ // SSR-specific properties - meaningless in client environment
17
+ "req",
18
+ "res",
19
+ // Internal context - used by framework internally
20
+ "context",
21
+ // Status code - internal status information
22
+ "statusCode",
23
+ // Route behavior overrides - framework internal logic
24
+ "confirm",
25
+ // Layer configuration - used for layer routes
26
+ "layer"
27
+ ];
28
+ export function applyRouteParams(match, toInput, base, to) {
29
+ if (!isPlainObject(toInput) || !isNonEmptyPlainObject(toInput.params) || !match.matches.length) {
30
+ return;
31
+ }
32
+ const lastMatch = match.matches[match.matches.length - 1];
33
+ const current = to.pathname.split("/");
34
+ const next = new URL(
35
+ lastMatch.compile(toInput.params).substring(1),
36
+ base
37
+ ).pathname.split("/");
38
+ next.forEach((item, index) => {
39
+ current[index] = item || current[index];
40
+ });
41
+ to.pathname = current.join("/");
42
+ Object.assign(match.params, toInput.params);
43
+ }
44
+ export class Route {
45
+ constructor(routeOptions = {}) {
46
+ // Private fields for handle validation
47
+ __publicField(this, "_handled", false);
48
+ __publicField(this, "_handle", null);
49
+ __publicField(this, "_handleResult", null);
50
+ __publicField(this, "_options");
51
+ // Public properties
52
+ __publicField(this, "statusCode", null);
53
+ __publicField(this, "state");
54
+ __publicField(this, "keepScrollPosition");
55
+ /** Custom confirm handler that overrides default route-transition confirm logic */
56
+ __publicField(this, "confirm");
57
+ /** Layer configuration for layer routes */
58
+ __publicField(this, "layer");
59
+ // Read-only properties
60
+ __publicField(this, "type");
61
+ __publicField(this, "req");
62
+ __publicField(this, "res");
63
+ __publicField(this, "context");
64
+ __publicField(this, "url");
65
+ __publicField(this, "path");
66
+ __publicField(this, "fullPath");
67
+ __publicField(this, "hash");
68
+ __publicField(this, "params", {});
69
+ __publicField(this, "paramsArray", {});
70
+ __publicField(this, "query", {});
71
+ __publicField(this, "queryArray", {});
72
+ __publicField(this, "meta");
73
+ __publicField(this, "matched");
74
+ __publicField(this, "config");
75
+ var _a;
76
+ const {
77
+ toType = RouteType.push,
78
+ from = null,
79
+ options = parsedOptions()
80
+ } = routeOptions;
81
+ this._options = options;
82
+ this.type = toType;
83
+ this.req = options.req;
84
+ this.res = options.res;
85
+ this.context = options.context;
86
+ const base = options.base;
87
+ const toInput = resolveRouteLocationInput(routeOptions.toInput, from);
88
+ const to = options.normalizeURL(parseLocation(toInput, base), from);
89
+ let match = null;
90
+ if (to.origin === base.origin && to.pathname.startsWith(base.pathname)) {
91
+ const isLayer = toType === RouteType.pushLayer || options.layer;
92
+ match = options.matcher(to, base, (config) => {
93
+ if (isLayer) {
94
+ return config.layer !== false;
95
+ }
96
+ return config.layer !== true;
97
+ });
98
+ }
99
+ if (match) {
100
+ applyRouteParams(match, toInput, base, to);
101
+ const decodedParams = decodeParams(match.params);
102
+ for (const key in decodedParams) {
103
+ const value = decodedParams[key];
104
+ if (Array.isArray(value)) {
105
+ this.params[key] = value[0] || "";
106
+ this.paramsArray[key] = value;
107
+ } else {
108
+ this.params[key] = value;
109
+ this.paramsArray[key] = [value];
110
+ }
111
+ }
112
+ }
113
+ this.url = to;
114
+ this.path = match ? to.pathname.substring(base.pathname.length - 1) : to.pathname;
115
+ this.fullPath = (match ? this.path : to.pathname) + to.search + to.hash;
116
+ this.matched = match ? match.matches : Object.freeze([]);
117
+ this.keepScrollPosition = Boolean(toInput.keepScrollPosition);
118
+ this.confirm = toInput.confirm || null;
119
+ this.layer = toType === RouteType.pushLayer && toInput.layer ? toInput.layer : null;
120
+ this.config = this.matched.length > 0 ? this.matched[this.matched.length - 1] : null;
121
+ this.meta = ((_a = this.config) == null ? void 0 : _a.meta) || {};
122
+ const state = {};
123
+ if (toInput.state) {
124
+ Object.assign(state, toInput.state);
125
+ }
126
+ this.state = state;
127
+ for (const key of new Set(to.searchParams.keys())) {
128
+ this.query[key] = to.searchParams.get(key);
129
+ this.queryArray[key] = to.searchParams.getAll(key);
130
+ }
131
+ this.hash = to.hash;
132
+ if (typeof toInput.statusCode === "number") {
133
+ this.statusCode = toInput.statusCode;
134
+ }
135
+ for (const property of NON_ENUMERABLE_PROPERTIES) {
136
+ Object.defineProperty(this, property, { enumerable: false });
137
+ }
138
+ }
139
+ /** @deprecated Use `url.pathname` instead. */
140
+ get pathname() {
141
+ return this.url.pathname;
142
+ }
143
+ /** @deprecated Use `url.href` instead. */
144
+ get href() {
145
+ return this.url.href;
146
+ }
147
+ get isPush() {
148
+ return this.type.startsWith("push");
149
+ }
150
+ // handle related getter/setter
151
+ get handle() {
152
+ return this._handle;
153
+ }
154
+ set handle(val) {
155
+ this.setHandle(val);
156
+ }
157
+ get handleResult() {
158
+ return this._handleResult;
159
+ }
160
+ set handleResult(val) {
161
+ this._handleResult = val;
162
+ }
163
+ /**
164
+ * Set handle function with validation logic wrapper
165
+ */
166
+ setHandle(val) {
167
+ if (typeof val !== "function") {
168
+ this._handle = null;
169
+ return;
170
+ }
171
+ const self = this;
172
+ this._handle = function handle(to, from, router) {
173
+ if (self._handled) {
174
+ throw new Error(
175
+ "Route handle hook can only be called once per navigation"
176
+ );
177
+ }
178
+ self._handled = true;
179
+ return val.call(this, to, from, router);
180
+ };
181
+ }
182
+ /**
183
+ * Apply navigation-generated state to current route
184
+ * Used by route handlers to add system state like pageId
185
+ * @param navigationState Navigation-generated state to apply
186
+ */
187
+ applyNavigationState(navigationState) {
188
+ Object.assign(this.state, navigationState);
189
+ }
190
+ /**
191
+ * Sync all properties of current route to target route object
192
+ * Used for route object updates in reactive systems
193
+ * @param targetRoute Target route object
194
+ */
195
+ syncTo(targetRoute) {
196
+ Object.assign(targetRoute, this);
197
+ for (const property of NON_ENUMERABLE_PROPERTIES) {
198
+ if (!(property in this && property in targetRoute)) continue;
199
+ const value = Reflect.get(this, property);
200
+ Reflect.set(targetRoute, property, value);
201
+ }
202
+ }
203
+ /**
204
+ * Clone current route instance
205
+ * Returns a new Route instance with same configuration and state
206
+ */
207
+ clone() {
208
+ const toInput = {
209
+ path: this.fullPath,
210
+ state: { ...this.state },
211
+ ...this.confirm && { confirm: this.confirm },
212
+ ...this.layer && { layer: this.layer },
213
+ ...this.statusCode !== null && { statusCode: this.statusCode }
214
+ };
215
+ const options = this._options;
216
+ const clonedRoute = new Route({
217
+ options,
218
+ toType: this.type,
219
+ toInput
220
+ });
221
+ return clonedRoute;
222
+ }
223
+ }
@@ -0,0 +1,10 @@
1
+ import type { Router } from './router';
2
+ import type { RouterLinkProps, RouterLinkResolved } from './types';
3
+ /**
4
+ * Framework-agnostic link resolver
5
+ *
6
+ * @param router Router instance
7
+ * @param props Link configuration
8
+ * @returns Resolution result
9
+ */
10
+ export declare function createLinkResolver(router: Router, props: RouterLinkProps): RouterLinkResolved;