@askrjs/askr 0.0.34 → 0.0.36
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/bench/components/benchmark-row.js.map +1 -1
- package/dist/bench/components/benchmark-table.js.map +1 -1
- package/dist/benchmark.js +2 -2
- package/dist/benchmark.js.map +1 -1
- package/dist/bin/askr-ssg.js.map +1 -1
- package/dist/boot/index.d.ts.map +1 -1
- package/dist/boot/index.js +9 -29
- package/dist/boot/index.js.map +1 -1
- package/dist/common/control.js.map +1 -1
- package/dist/common/env.js.map +1 -1
- package/dist/common/router.d.ts +8 -1
- package/dist/common/router.d.ts.map +1 -1
- package/dist/common/ssr-errors.js.map +1 -1
- package/dist/common/vnode.js.map +1 -1
- package/dist/components/error-boundary.js.map +1 -1
- package/dist/components/link.js.map +1 -1
- package/dist/control/case.js.map +1 -1
- package/dist/control/for.js.map +1 -1
- package/dist/control/shared.js.map +1 -1
- package/dist/control/show.js.map +1 -1
- package/dist/data/index.d.ts.map +1 -1
- package/dist/data/index.js +165 -23
- package/dist/data/index.js.map +1 -1
- package/dist/dev/invariant.js.map +1 -1
- package/dist/dev/logger.js.map +1 -1
- package/dist/foundations/icon/icon.js.map +1 -1
- package/dist/foundations/interactions/dismissable.js.map +1 -1
- package/dist/foundations/interactions/focusable.js.map +1 -1
- package/dist/foundations/interactions/hoverable.js.map +1 -1
- package/dist/foundations/interactions/interaction-policy.js.map +1 -1
- package/dist/foundations/interactions/pressable.js.map +1 -1
- package/dist/foundations/interactions/roving-focus.js.map +1 -1
- package/dist/foundations/state/controllable.js.map +1 -1
- package/dist/foundations/structures/collection.js.map +1 -1
- package/dist/foundations/structures/layer.js.map +1 -1
- package/dist/foundations/structures/layout.js.map +1 -1
- package/dist/foundations/structures/portal.js.map +1 -1
- package/dist/foundations/structures/presence.js.map +1 -1
- package/dist/foundations/structures/slot.js.map +1 -1
- package/dist/foundations/utilities/aria.js.map +1 -1
- package/dist/foundations/utilities/compose-handlers.js.map +1 -1
- package/dist/foundations/utilities/compose-ref.js.map +1 -1
- package/dist/foundations/utilities/merge-props.js.map +1 -1
- package/dist/foundations/utilities/use-id.js.map +1 -1
- package/dist/fx/fx.js.map +1 -1
- package/dist/fx/timing.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/jsx/utils.js.map +1 -1
- package/dist/jsx-dev-runtime.js.map +1 -1
- package/dist/jsx-runtime.js.map +1 -1
- package/dist/renderer/children.js.map +1 -1
- package/dist/renderer/cleanup.js.map +1 -1
- package/dist/renderer/dom.js +128 -53
- package/dist/renderer/dom.js.map +1 -1
- package/dist/renderer/evaluate.js +7 -0
- package/dist/renderer/evaluate.js.map +1 -1
- package/dist/renderer/fastpath.js.map +1 -1
- package/dist/renderer/index.js.map +1 -1
- package/dist/renderer/keyed.js +1 -1
- package/dist/renderer/keyed.js.map +1 -1
- package/dist/renderer/reconcile.js.map +1 -1
- package/dist/renderer/utils.js.map +1 -1
- package/dist/router/index.d.ts +3 -3
- package/dist/router/index.js +2 -2
- package/dist/router/match.js.map +1 -1
- package/dist/router/navigate.d.ts.map +1 -1
- package/dist/router/navigate.js +8 -3
- package/dist/router/navigate.js.map +1 -1
- package/dist/router/policy.js.map +1 -1
- package/dist/router/route-context.js.map +1 -1
- package/dist/router/route.d.ts +11 -2
- package/dist/router/route.d.ts.map +1 -1
- package/dist/router/route.js +268 -109
- package/dist/router/route.js.map +1 -1
- package/dist/runtime/child-scope.d.ts +1 -0
- package/dist/runtime/child-scope.d.ts.map +1 -1
- package/dist/runtime/child-scope.js +2 -0
- package/dist/runtime/child-scope.js.map +1 -1
- package/dist/runtime/component.js +1 -1
- package/dist/runtime/component.js.map +1 -1
- package/dist/runtime/context.js.map +1 -1
- package/dist/runtime/control.js.map +1 -1
- package/dist/runtime/derive.d.ts +2 -1
- package/dist/runtime/derive.d.ts.map +1 -1
- package/dist/runtime/derive.js +15 -10
- package/dist/runtime/derive.js.map +1 -1
- package/dist/runtime/dev-namespace.js.map +1 -1
- package/dist/runtime/effect.d.ts.map +1 -1
- package/dist/runtime/effect.js +34 -9
- package/dist/runtime/effect.js.map +1 -1
- package/dist/runtime/events.js.map +1 -1
- package/dist/runtime/execution-model.js.map +1 -1
- package/dist/runtime/fastlane.js.map +1 -1
- package/dist/runtime/for-bench.d.ts +1 -1
- package/dist/runtime/for-bench.d.ts.map +1 -1
- package/dist/runtime/for-bench.js +15 -14
- package/dist/runtime/for-bench.js.map +1 -1
- package/dist/runtime/for.d.ts +1 -0
- package/dist/runtime/for.d.ts.map +1 -1
- package/dist/runtime/for.js +63 -16
- package/dist/runtime/for.js.map +1 -1
- package/dist/runtime/operations.d.ts.map +1 -1
- package/dist/runtime/operations.js +9 -7
- package/dist/runtime/operations.js.map +1 -1
- package/dist/runtime/perf-metrics.js +8 -2
- package/dist/runtime/perf-metrics.js.map +1 -1
- package/dist/runtime/readable.d.ts +1 -1
- package/dist/runtime/readable.d.ts.map +1 -1
- package/dist/runtime/readable.js +2 -1
- package/dist/runtime/readable.js.map +1 -1
- package/dist/runtime/resource-cell.js +3 -2
- package/dist/runtime/resource-cell.js.map +1 -1
- package/dist/runtime/scheduler.d.ts +0 -1
- package/dist/runtime/scheduler.d.ts.map +1 -1
- package/dist/runtime/scheduler.js +6 -19
- package/dist/runtime/scheduler.js.map +1 -1
- package/dist/runtime/selector.d.ts.map +1 -1
- package/dist/runtime/selector.js +6 -4
- package/dist/runtime/selector.js.map +1 -1
- package/dist/runtime/snapshot-source.d.ts +10 -0
- package/dist/runtime/snapshot-source.d.ts.map +1 -0
- package/dist/runtime/snapshot-source.js +17 -0
- package/dist/runtime/snapshot-source.js.map +1 -0
- package/dist/runtime/ssr-bridge.js.map +1 -1
- package/dist/runtime/state.d.ts.map +1 -1
- package/dist/runtime/state.js +1 -5
- package/dist/runtime/state.js.map +1 -1
- package/dist/ssg/batch-render.js.map +1 -1
- package/dist/ssg/create-static-gen.js.map +1 -1
- package/dist/ssg/generate-metadata.js.map +1 -1
- package/dist/ssg/incremental-manifest.js.map +1 -1
- package/dist/ssg/resolve-ssg-data.js.map +1 -1
- package/dist/ssg/route-utils.js.map +1 -1
- package/dist/ssg/write-static-files.js.map +1 -1
- package/dist/ssr/attrs.js.map +1 -1
- package/dist/ssr/context.d.ts +1 -0
- package/dist/ssr/context.d.ts.map +1 -1
- package/dist/ssr/context.js +3 -0
- package/dist/ssr/context.js.map +1 -1
- package/dist/ssr/escape.js +56 -16
- package/dist/ssr/escape.js.map +1 -1
- package/dist/ssr/index.d.ts.map +1 -1
- package/dist/ssr/index.js +15 -3
- package/dist/ssr/index.js.map +1 -1
- package/dist/ssr/render-keys.js.map +1 -1
- package/dist/ssr/sink.js.map +1 -1
- package/dist/ssr/stream-render.js +14 -2
- package/dist/ssr/stream-render.js.map +1 -1
- package/package.json +8 -6
package/dist/router/route.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { defineContext, readContext } from "../runtime/context.js";
|
|
2
|
+
import { markReactivePropsDirtySource, markReadableDerivedSubscribersDirty, notifyReadableReaders, recordReadableRead } from "../runtime/readable.js";
|
|
1
3
|
import { getCurrentComponentInstance } from "../runtime/component.js";
|
|
4
|
+
import { getRenderContext } from "../ssr/context.js";
|
|
2
5
|
import { computeRank, matchSegments, parseSegments } from "./match.js";
|
|
3
6
|
import { requireAuth, requireGuest, requirePermission, requireRole } from "./policy.js";
|
|
4
7
|
import { buildRouteContext, buildRouteContextBase, deepFreeze, makeQuery, parseLocation } from "./route-context.js";
|
|
5
8
|
import { getExecutionModel } from "../runtime/execution-model.js";
|
|
6
|
-
import { getRenderContext } from "../ssr/context.js";
|
|
7
9
|
//#region src/router/route.ts
|
|
8
10
|
/**
|
|
9
11
|
* Route definition, registration, and matching.
|
|
@@ -11,12 +13,14 @@ import { getRenderContext } from "../ssr/context.js";
|
|
|
11
13
|
* Primary public authoring:
|
|
12
14
|
* - `registerRoutes(() => { ... }, options?)`
|
|
13
15
|
* - `group(options, () => { ... })`
|
|
16
|
+
* - `page(path, Component, () => { ... })`
|
|
17
|
+
* - `index(Component, options?)`
|
|
14
18
|
* - `route(path, Component, options?)`
|
|
15
19
|
* - `fallback(Component)`
|
|
20
|
+
* - `Outlet()` for page child rendering
|
|
16
21
|
* - `currentRoute()` for render-time access
|
|
17
22
|
*
|
|
18
23
|
*/
|
|
19
|
-
/** Legacy flat route array — kept for resolver and route() accessor backward compat. */
|
|
20
24
|
const routes = [];
|
|
21
25
|
/** Normalized route records built by the declarative registration API. */
|
|
22
26
|
const records = [];
|
|
@@ -24,6 +28,7 @@ const records = [];
|
|
|
24
28
|
const registrationScopeStack = [];
|
|
25
29
|
const registrationSessionStack = [];
|
|
26
30
|
const namespaces = /* @__PURE__ */ new Set();
|
|
31
|
+
let registrationLocked = false;
|
|
27
32
|
let defaultRouteAuthOptions;
|
|
28
33
|
let activeClientRouteAuthOptions;
|
|
29
34
|
const HAS_ROUTES_KEY = Symbol.for("__ASKR_HAS_ROUTES__");
|
|
@@ -72,6 +77,9 @@ function cachedSortedList(routeList) {
|
|
|
72
77
|
}
|
|
73
78
|
return sorted;
|
|
74
79
|
}
|
|
80
|
+
let currentRouteSnapshot = buildRouteSnapshot("/", "", "");
|
|
81
|
+
const currentRouteSource = (() => currentRouteSnapshot);
|
|
82
|
+
currentRouteSource._readers = /* @__PURE__ */ new Map();
|
|
75
83
|
let serverLocation = null;
|
|
76
84
|
function setServerLocation(url) {
|
|
77
85
|
serverLocation = url;
|
|
@@ -79,27 +87,87 @@ function setServerLocation(url) {
|
|
|
79
87
|
function isPromise(value) {
|
|
80
88
|
return value instanceof Promise;
|
|
81
89
|
}
|
|
90
|
+
function buildRouteSnapshot(pathname, search, hash) {
|
|
91
|
+
const query = makeQuery(search);
|
|
92
|
+
const matches = computeMatchesFromRoutes(pathname, getActiveRoutes());
|
|
93
|
+
return Object.freeze({
|
|
94
|
+
path: pathname,
|
|
95
|
+
params: deepFreeze({ ...matches[0]?.params }),
|
|
96
|
+
query,
|
|
97
|
+
hash: hash || null,
|
|
98
|
+
matches: Object.freeze(matches)
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
function setCurrentRouteSnapshot(pathname, search, hash) {
|
|
102
|
+
currentRouteSnapshot = buildRouteSnapshot(pathname, search, hash);
|
|
103
|
+
const instance = getCurrentComponentInstance();
|
|
104
|
+
markReadableDerivedSubscribersDirty(currentRouteSource);
|
|
105
|
+
markReactivePropsDirtySource(currentRouteSource);
|
|
106
|
+
notifyReadableReaders(currentRouteSource, instance);
|
|
107
|
+
}
|
|
108
|
+
function matchFallbackPrefix(pathname, fallbackPrefix) {
|
|
109
|
+
const normalizedPath = pathname.endsWith("/") && pathname !== "/" ? pathname.slice(0, -1) : pathname;
|
|
110
|
+
const normalizedPrefix = fallbackPrefix.endsWith("/") && fallbackPrefix !== "/" ? fallbackPrefix.slice(0, -1) : fallbackPrefix;
|
|
111
|
+
if (normalizedPrefix === "/") {
|
|
112
|
+
const urlParts = normalizedPath === "/" ? [] : normalizedPath.split("/").filter(Boolean);
|
|
113
|
+
return { "*": urlParts.length === 0 ? "/" : urlParts.length === 1 ? urlParts[0] : "/" + urlParts.join("/") };
|
|
114
|
+
}
|
|
115
|
+
if (normalizedPath !== normalizedPrefix && !normalizedPath.startsWith(`${normalizedPrefix}/`)) return null;
|
|
116
|
+
const remainder = normalizedPath === normalizedPrefix ? "/" : normalizedPath.slice(normalizedPrefix.length + 1);
|
|
117
|
+
const remainderParts = remainder === "/" ? [] : remainder.split("/").filter(Boolean);
|
|
118
|
+
return { "*": remainderParts.length === 0 ? "/" : remainderParts.length === 1 ? remainderParts[0] : "/" + remainderParts.join("/") };
|
|
119
|
+
}
|
|
82
120
|
function computeMatchesFromRoutes(pathname, routesList) {
|
|
83
|
-
const
|
|
121
|
+
const bestMatch = routesList === routes ? getMatchingRecord(pathname, records) : findBestResolvedRouteFromRoutes(pathname, routesList);
|
|
122
|
+
if (!bestMatch) return [];
|
|
123
|
+
return [{
|
|
124
|
+
path: "route" in bestMatch ? bestMatch.route.path : bestMatch.record.path,
|
|
125
|
+
params: deepFreeze({ ...bestMatch.params }),
|
|
126
|
+
name: "route" in bestMatch ? bestMatch.route.name : void 0,
|
|
127
|
+
namespace: "route" in bestMatch ? bestMatch.route.namespace : bestMatch.record.options.namespace
|
|
128
|
+
}];
|
|
129
|
+
}
|
|
130
|
+
function findBestResolvedRouteFromRoutes(pathname, routeList) {
|
|
84
131
|
const normalized = pathname.endsWith("/") && pathname !== "/" ? pathname.slice(0, -1) : pathname;
|
|
85
132
|
const urlParts = normalized === "/" ? [] : normalized.split("/").filter(Boolean);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
133
|
+
const sorted = cachedSortedList(routeList);
|
|
134
|
+
let bestRoute = null;
|
|
135
|
+
let bestParams = {};
|
|
136
|
+
let bestRank = -Infinity;
|
|
137
|
+
for (const route of sorted) {
|
|
138
|
+
if (route.fallbackPrefix) continue;
|
|
139
|
+
const rank = cachedRank(route);
|
|
140
|
+
if (rank < bestRank) break;
|
|
141
|
+
if (bestRoute !== null && rank === bestRank) continue;
|
|
142
|
+
const params = matchSegments(urlParts, cachedSegs(route));
|
|
143
|
+
if (params !== null) {
|
|
144
|
+
bestRoute = route;
|
|
145
|
+
bestParams = params;
|
|
146
|
+
bestRank = rank;
|
|
147
|
+
}
|
|
95
148
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
149
|
+
if (bestRoute !== null) return {
|
|
150
|
+
route: bestRoute,
|
|
151
|
+
params: bestParams
|
|
152
|
+
};
|
|
153
|
+
let bestFallback = null;
|
|
154
|
+
let bestFallbackParams = null;
|
|
155
|
+
let bestPrefixLength = -1;
|
|
156
|
+
for (const route of routeList) {
|
|
157
|
+
const internalRoute = route;
|
|
158
|
+
if (!internalRoute.fallbackPrefix) continue;
|
|
159
|
+
const params = matchFallbackPrefix(normalized, internalRoute.fallbackPrefix);
|
|
160
|
+
if (params === null) continue;
|
|
161
|
+
if (internalRoute.fallbackPrefix.length > bestPrefixLength) {
|
|
162
|
+
bestFallback = internalRoute;
|
|
163
|
+
bestFallbackParams = params;
|
|
164
|
+
bestPrefixLength = internalRoute.fallbackPrefix.length;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return bestFallback && bestFallbackParams ? {
|
|
168
|
+
route: bestFallback,
|
|
169
|
+
params: bestFallbackParams
|
|
170
|
+
} : null;
|
|
103
171
|
}
|
|
104
172
|
function getActiveRoutes() {
|
|
105
173
|
return getRenderContext()?.routes ?? routes;
|
|
@@ -125,6 +193,20 @@ function getCurrentLayoutChain() {
|
|
|
125
193
|
for (const scope of registrationScopeStack) if (scope.layout) layoutChain.push({ component: scope.layout });
|
|
126
194
|
return layoutChain;
|
|
127
195
|
}
|
|
196
|
+
function getCurrentPageChain() {
|
|
197
|
+
const pageChain = [];
|
|
198
|
+
for (const scope of registrationScopeStack) if (scope.page) pageChain.push({ component: scope.page });
|
|
199
|
+
return pageChain;
|
|
200
|
+
}
|
|
201
|
+
function hasActivePageScope() {
|
|
202
|
+
return registrationScopeStack.some((scope) => !!scope.page);
|
|
203
|
+
}
|
|
204
|
+
function getCurrentScopeKind() {
|
|
205
|
+
return registrationScopeStack[registrationScopeStack.length - 1]?.kind ?? null;
|
|
206
|
+
}
|
|
207
|
+
function getCurrentPathPrefix() {
|
|
208
|
+
return registrationScopeStack[registrationScopeStack.length - 1]?.pathPrefix ?? "";
|
|
209
|
+
}
|
|
128
210
|
function getCurrentInheritedPolicies() {
|
|
129
211
|
const policies = [];
|
|
130
212
|
for (const scope of registrationScopeStack) if (scope.policies.length > 0) policies.push(...scope.policies);
|
|
@@ -134,7 +216,6 @@ function getCurrentInheritedPolicies() {
|
|
|
134
216
|
* Prevent route registrations after the app has started.
|
|
135
217
|
* Enforced in production; tests may unlock explicitly.
|
|
136
218
|
*/
|
|
137
|
-
let registrationLocked = false;
|
|
138
219
|
function lockRouteRegistration() {
|
|
139
220
|
registrationLocked = true;
|
|
140
221
|
}
|
|
@@ -145,6 +226,37 @@ function validateRoutePath(path) {
|
|
|
145
226
|
throw new Error(`Route parameter syntax uses {name} interpolation, not :name. Use "${suggested}" instead of "${path}".`);
|
|
146
227
|
}
|
|
147
228
|
}
|
|
229
|
+
function normalizeAbsoluteRoutePath(path) {
|
|
230
|
+
if (!path || path === "/") return "/";
|
|
231
|
+
return (path.endsWith("/") ? path.slice(0, -1) : path) || "/";
|
|
232
|
+
}
|
|
233
|
+
function joinRoutePaths(prefix, path) {
|
|
234
|
+
const normalizedPrefix = normalizeAbsoluteRoutePath(prefix || "/");
|
|
235
|
+
const normalizedPath = path.replace(/^\/+|\/+$/g, "");
|
|
236
|
+
if (!normalizedPath) return normalizedPrefix;
|
|
237
|
+
return normalizedPrefix === "/" ? `/${normalizedPath}` : `${normalizedPrefix}/${normalizedPath}`;
|
|
238
|
+
}
|
|
239
|
+
function resolvePageScopePath(path) {
|
|
240
|
+
if (!path) throw new Error("page(path, Component, fn) requires a non-empty path.");
|
|
241
|
+
if (path.startsWith("/")) {
|
|
242
|
+
validateRoutePath(path);
|
|
243
|
+
return normalizeAbsoluteRoutePath(path);
|
|
244
|
+
}
|
|
245
|
+
return joinRoutePaths(getCurrentPathPrefix(), path);
|
|
246
|
+
}
|
|
247
|
+
function resolveIndexPath() {
|
|
248
|
+
return normalizeAbsoluteRoutePath(getCurrentPathPrefix() || "/");
|
|
249
|
+
}
|
|
250
|
+
function resolveRouteRegistrationPath(path) {
|
|
251
|
+
if (path.startsWith("/")) {
|
|
252
|
+
if (hasActivePageScope()) throw new Error(`Child route paths inside page() must be relative. Use "${path.slice(1)}" instead of "${path}".`);
|
|
253
|
+
validateRoutePath(path);
|
|
254
|
+
return normalizeAbsoluteRoutePath(path);
|
|
255
|
+
}
|
|
256
|
+
const prefix = getCurrentPathPrefix();
|
|
257
|
+
if (!prefix) throw new Error(`Route path must begin with "/". Got: "${path}"`);
|
|
258
|
+
return joinRoutePaths(prefix, path);
|
|
259
|
+
}
|
|
148
260
|
function insertRecordSorted(record) {
|
|
149
261
|
let lo = 0;
|
|
150
262
|
let hi = records.length;
|
|
@@ -169,6 +281,10 @@ function addRouteToStores(routeObj) {
|
|
|
169
281
|
}
|
|
170
282
|
/** Promises from in-flight lazy() imports, drained by createSPA / hydrateSPA. */
|
|
171
283
|
const pendingLazy = /* @__PURE__ */ new Set();
|
|
284
|
+
const outletContext = defineContext(null);
|
|
285
|
+
function Outlet() {
|
|
286
|
+
return readContext(outletContext);
|
|
287
|
+
}
|
|
172
288
|
/**
|
|
173
289
|
* Snapshot the current in-flight lazy() imports.
|
|
174
290
|
* Boot uses this before clearing route state so manifest-based startup can
|
|
@@ -227,9 +343,30 @@ function _drainLazy(additionalPending = []) {
|
|
|
227
343
|
function group(options, fn) {
|
|
228
344
|
pushGroupScope(options, fn);
|
|
229
345
|
}
|
|
346
|
+
function page(path, Component, optionsOrFn, maybeFn) {
|
|
347
|
+
const options = typeof optionsOrFn === "function" ? {} : optionsOrFn;
|
|
348
|
+
const fn = typeof optionsOrFn === "function" ? optionsOrFn : maybeFn;
|
|
349
|
+
if (typeof Component !== "function") throw new Error("page(path, Component, fn) requires a component function as the second argument.");
|
|
350
|
+
if (typeof fn !== "function") throw new Error("page(path, Component, fn) requires a route definition callback as the final argument.");
|
|
351
|
+
pushPageScope(path, Component, options, fn);
|
|
352
|
+
}
|
|
353
|
+
function index(Component, options) {
|
|
354
|
+
registerRouteAtResolvedPath(resolveIndexPath(), Component, options);
|
|
355
|
+
}
|
|
230
356
|
function fallback(Component) {
|
|
357
|
+
if (hasActivePageScope()) {
|
|
358
|
+
if (getCurrentScopeKind() !== "page") throw new Error("fallback() inside page() must be declared directly in the page scope, not inside nested group().");
|
|
359
|
+
registerRouteAtResolvedPath(`${getCurrentPathPrefix()}/*`, Component, void 0, {
|
|
360
|
+
isFallback: true,
|
|
361
|
+
fallbackPrefix: getCurrentPathPrefix()
|
|
362
|
+
});
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
231
365
|
if (!registrationScopeStack.every((scope) => scope.policies.length === 0 && !scope.state.guestOnly && !scope.state.authenticated)) throw new Error("fallback() can only be registered at the root scope. Use route(\"/*\", Component) if you need compatibility behavior.");
|
|
232
|
-
|
|
366
|
+
registerRouteAtResolvedPath("/*", Component, void 0, {
|
|
367
|
+
isFallback: true,
|
|
368
|
+
fallbackPrefix: "/"
|
|
369
|
+
});
|
|
233
370
|
}
|
|
234
371
|
function pushRegistrationScope(scope, fn) {
|
|
235
372
|
registrationScopeStack.push(scope);
|
|
@@ -246,11 +383,28 @@ function pushGroupScope(options, fn) {
|
|
|
246
383
|
});
|
|
247
384
|
const policies = compileNodePolicies(options);
|
|
248
385
|
pushRegistrationScope({
|
|
386
|
+
kind: "group",
|
|
387
|
+
pathPrefix: getCurrentPathPrefix(),
|
|
249
388
|
layout: options.layout,
|
|
250
389
|
policies,
|
|
251
390
|
state: nextAccessScopeState(options, getCurrentAccessScopeState())
|
|
252
391
|
}, fn);
|
|
253
392
|
}
|
|
393
|
+
function pushPageScope(path, Component, options, fn) {
|
|
394
|
+
if (hasActivePageScope()) throw new Error("page() cannot be nested inside another page(). Use route() for child leaves or group() for inherited behavior inside the existing page scope.");
|
|
395
|
+
validateAccessMetadata(options, {
|
|
396
|
+
authConfigured: getCurrentRegistrationSession().authConfigured,
|
|
397
|
+
state: getCurrentAccessScopeState()
|
|
398
|
+
});
|
|
399
|
+
const policies = compileNodePolicies(options);
|
|
400
|
+
pushRegistrationScope({
|
|
401
|
+
kind: "page",
|
|
402
|
+
pathPrefix: resolvePageScopePath(path),
|
|
403
|
+
page: Component,
|
|
404
|
+
policies,
|
|
405
|
+
state: nextAccessScopeState(options, getCurrentAccessScopeState())
|
|
406
|
+
}, fn);
|
|
407
|
+
}
|
|
254
408
|
function hasBuiltInAuthMetadata(node) {
|
|
255
409
|
return node.auth !== void 0 || !!node.role || !!node.permission;
|
|
256
410
|
}
|
|
@@ -297,6 +451,55 @@ function normalizeRouteOptions(options) {
|
|
|
297
451
|
...options.namespace ? { namespace: options.namespace } : {}
|
|
298
452
|
};
|
|
299
453
|
}
|
|
454
|
+
function applyPageChain(pageChain, params, content) {
|
|
455
|
+
let nextContent = content;
|
|
456
|
+
for (let i = pageChain.length - 1; i >= 0; i--) nextContent = outletContext.Scope({
|
|
457
|
+
value: nextContent,
|
|
458
|
+
children: pageChain[i].component(params)
|
|
459
|
+
});
|
|
460
|
+
return nextContent;
|
|
461
|
+
}
|
|
462
|
+
function registerRouteAtResolvedPath(path, Component, options, metadata) {
|
|
463
|
+
validateAccessMetadata(options ?? {}, {
|
|
464
|
+
authConfigured: getCurrentRegistrationSession().authConfigured,
|
|
465
|
+
state: getCurrentAccessScopeState()
|
|
466
|
+
});
|
|
467
|
+
const chain = getCurrentLayoutChain();
|
|
468
|
+
const pageChain = getCurrentPageChain();
|
|
469
|
+
const segments = parseSegments(path);
|
|
470
|
+
const rank = computeRank(segments);
|
|
471
|
+
const isFallback = metadata?.isFallback ?? path === "/*";
|
|
472
|
+
const comp = Component;
|
|
473
|
+
const normalizedOptions = normalizeRouteOptions(options);
|
|
474
|
+
const policies = [...getCurrentInheritedPolicies(), ...normalizedOptions?.policies ?? []];
|
|
475
|
+
const handler = (params) => {
|
|
476
|
+
let content = comp(params);
|
|
477
|
+
content = applyPageChain(pageChain, params, content);
|
|
478
|
+
for (let i = chain.length - 1; i >= 0; i--) content = chain[i].component({ children: content });
|
|
479
|
+
return content;
|
|
480
|
+
};
|
|
481
|
+
insertRecordSorted({
|
|
482
|
+
path,
|
|
483
|
+
component: comp,
|
|
484
|
+
segments,
|
|
485
|
+
rank,
|
|
486
|
+
layoutChain: chain,
|
|
487
|
+
pageChain,
|
|
488
|
+
options: normalizedOptions ? {
|
|
489
|
+
...normalizedOptions,
|
|
490
|
+
...policies.length > 0 ? { policies } : {}
|
|
491
|
+
} : policies.length > 0 ? { policies } : {},
|
|
492
|
+
isFallback,
|
|
493
|
+
handler,
|
|
494
|
+
...metadata?.fallbackPrefix ? { fallbackPrefix: metadata.fallbackPrefix } : {}
|
|
495
|
+
});
|
|
496
|
+
addRouteToStores({
|
|
497
|
+
path,
|
|
498
|
+
handler,
|
|
499
|
+
namespace: normalizedOptions?.namespace ?? options?.namespace,
|
|
500
|
+
...metadata?.fallbackPrefix ? { fallbackPrefix: metadata.fallbackPrefix } : {}
|
|
501
|
+
});
|
|
502
|
+
}
|
|
300
503
|
function registerRoutes(definition, options = {}) {
|
|
301
504
|
defaultRouteAuthOptions = options.auth;
|
|
302
505
|
registrationSessionStack.push({ authConfigured: !!options.auth?.resolve });
|
|
@@ -341,50 +544,23 @@ function readCurrentRouteSnapshot() {
|
|
|
341
544
|
});
|
|
342
545
|
}
|
|
343
546
|
function currentRoute() {
|
|
547
|
+
const instance = getCurrentComponentInstance();
|
|
548
|
+
if (!instance) throw new Error("currentRoute() can only be called during component render execution. Call currentRoute() from inside your component function.");
|
|
549
|
+
if (typeof window === "undefined" || instance.ssr) return readCurrentRouteSnapshot();
|
|
550
|
+
recordReadableRead(currentRouteSource);
|
|
344
551
|
return readCurrentRouteSnapshot();
|
|
345
552
|
}
|
|
553
|
+
function syncCurrentRouteSnapshot(pathname, search, hash) {
|
|
554
|
+
setCurrentRouteSnapshot(pathname, search, hash);
|
|
555
|
+
}
|
|
346
556
|
function route(path, Component, options) {
|
|
347
|
-
if (getExecutionModel() === "islands") throw new Error("Routes are not supported with islands. Use createSPA (client) or createSSR (server) instead.");
|
|
348
557
|
if (typeof path === "undefined") throw new Error("route() is only for route registration. Use currentRoute() inside components.");
|
|
558
|
+
if (getExecutionModel() === "islands") throw new Error("Routes are not supported with islands. Use createSPA (client) or createSSR (server) instead.");
|
|
349
559
|
const currentInst = getCurrentComponentInstance();
|
|
350
560
|
if (currentInst && currentInst.ssr) throw new Error("route() cannot be called during SSR rendering. Register routes at module load time instead.");
|
|
351
561
|
if (registrationLocked) throw new Error("Route registration is locked after app startup. Register routes at module load time before calling createSPA or createSSR.");
|
|
352
562
|
if (typeof Component !== "function") throw new Error("route(path, Component) requires a component function as the second argument. Passing JSX elements or VNodes directly is not supported.");
|
|
353
|
-
|
|
354
|
-
validateAccessMetadata(options ?? {}, {
|
|
355
|
-
authConfigured: getCurrentRegistrationSession().authConfigured,
|
|
356
|
-
state: getCurrentAccessScopeState()
|
|
357
|
-
});
|
|
358
|
-
const chain = getCurrentLayoutChain();
|
|
359
|
-
const segments = parseSegments(path);
|
|
360
|
-
const rank = computeRank(segments);
|
|
361
|
-
const isFallback = path === "/*";
|
|
362
|
-
const comp = Component;
|
|
363
|
-
const normalizedOptions = normalizeRouteOptions(options);
|
|
364
|
-
const policies = [...getCurrentInheritedPolicies(), ...normalizedOptions?.policies ?? []];
|
|
365
|
-
const handler = (params) => {
|
|
366
|
-
let content = comp(params);
|
|
367
|
-
for (let i = chain.length - 1; i >= 0; i--) content = chain[i].component({ children: content });
|
|
368
|
-
return content;
|
|
369
|
-
};
|
|
370
|
-
insertRecordSorted({
|
|
371
|
-
path,
|
|
372
|
-
component: comp,
|
|
373
|
-
segments,
|
|
374
|
-
rank,
|
|
375
|
-
layoutChain: chain,
|
|
376
|
-
options: normalizedOptions ? {
|
|
377
|
-
...normalizedOptions,
|
|
378
|
-
...policies.length > 0 ? { policies } : {}
|
|
379
|
-
} : policies.length > 0 ? { policies } : {},
|
|
380
|
-
isFallback,
|
|
381
|
-
handler
|
|
382
|
-
});
|
|
383
|
-
addRouteToStores({
|
|
384
|
-
path,
|
|
385
|
-
handler,
|
|
386
|
-
namespace: normalizedOptions?.namespace ?? options?.namespace
|
|
387
|
-
});
|
|
563
|
+
registerRouteAtResolvedPath(resolveRouteRegistrationPath(path), Component, options);
|
|
388
564
|
}
|
|
389
565
|
/**
|
|
390
566
|
* Return the normalized route manifest built from registered route definitions.
|
|
@@ -410,14 +586,14 @@ function getManifest() {
|
|
|
410
586
|
function _applyManifest(manifest) {
|
|
411
587
|
defaultRouteAuthOptions = manifest.auth;
|
|
412
588
|
for (const record of manifest.records) {
|
|
413
|
-
|
|
589
|
+
insertRecordSorted(record);
|
|
414
590
|
addRouteToStores({
|
|
415
591
|
path: record.path,
|
|
416
592
|
handler: record.handler,
|
|
417
|
-
namespace: record.options.namespace
|
|
593
|
+
namespace: record.options.namespace,
|
|
594
|
+
..."fallbackPrefix" in record && typeof record.fallbackPrefix === "string" ? { fallbackPrefix: record.fallbackPrefix } : {}
|
|
418
595
|
});
|
|
419
596
|
}
|
|
420
|
-
records.sort((a, b) => b.rank - a.rank);
|
|
421
597
|
}
|
|
422
598
|
/**
|
|
423
599
|
* Get all registered routes (flat list, insertion order).
|
|
@@ -426,27 +602,6 @@ function _applyManifest(manifest) {
|
|
|
426
602
|
function getRoutes() {
|
|
427
603
|
return [...routes];
|
|
428
604
|
}
|
|
429
|
-
/** Get routes for a specific namespace. */
|
|
430
|
-
function getNamespaceRoutes(namespace) {
|
|
431
|
-
return routes.filter((r) => r.namespace === namespace);
|
|
432
|
-
}
|
|
433
|
-
/** Unload all routes from a namespace (for MFE unmounting). */
|
|
434
|
-
function unloadNamespace(namespace) {
|
|
435
|
-
const before = routes.length;
|
|
436
|
-
for (let i = routes.length - 1; i >= 0; i--) if (routes[i].namespace === namespace) {
|
|
437
|
-
const removed = routes[i];
|
|
438
|
-
routes.splice(i, 1);
|
|
439
|
-
const depth = getDepth(removed.path);
|
|
440
|
-
const depthRoutes = routesByDepth.get(depth);
|
|
441
|
-
if (depthRoutes) {
|
|
442
|
-
const idx = depthRoutes.indexOf(removed);
|
|
443
|
-
if (idx >= 0) depthRoutes.splice(idx, 1);
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
for (let i = records.length - 1; i >= 0; i--) if (records[i].options.namespace === namespace) records.splice(i, 1);
|
|
447
|
-
namespaces.delete(namespace);
|
|
448
|
-
return before - routes.length;
|
|
449
|
-
}
|
|
450
605
|
/** Clear all registered routes and records (testing / boot reset). */
|
|
451
606
|
function clearRoutes() {
|
|
452
607
|
routes.length = 0;
|
|
@@ -461,11 +616,25 @@ function clearRoutes() {
|
|
|
461
616
|
setHasRoutes(false);
|
|
462
617
|
pendingLazy.clear();
|
|
463
618
|
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
619
|
+
function findBestScopedFallbackRecord(pathname, routeRecords) {
|
|
620
|
+
let bestRecord = null;
|
|
621
|
+
let bestParams = null;
|
|
622
|
+
let bestPrefixLength = -1;
|
|
623
|
+
for (const routeRecord of routeRecords) {
|
|
624
|
+
const record = routeRecord;
|
|
625
|
+
if (!record.fallbackPrefix) continue;
|
|
626
|
+
const params = matchFallbackPrefix(pathname, record.fallbackPrefix);
|
|
627
|
+
if (params === null) continue;
|
|
628
|
+
if (record.fallbackPrefix.length > bestPrefixLength) {
|
|
629
|
+
bestRecord = record;
|
|
630
|
+
bestParams = params;
|
|
631
|
+
bestPrefixLength = record.fallbackPrefix.length;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
return bestRecord && bestParams ? {
|
|
635
|
+
record: bestRecord,
|
|
636
|
+
params: bestParams
|
|
637
|
+
} : null;
|
|
469
638
|
}
|
|
470
639
|
/**
|
|
471
640
|
* Resolve a path to a route handler.
|
|
@@ -479,26 +648,32 @@ function resolveRoute(pathname) {
|
|
|
479
648
|
const normalized = pathname.endsWith("/") && pathname !== "/" ? pathname.slice(0, -1) : pathname;
|
|
480
649
|
const urlParts = normalized === "/" ? [] : normalized.split("/").filter(Boolean);
|
|
481
650
|
for (const record of records) {
|
|
651
|
+
if (record.fallbackPrefix) continue;
|
|
482
652
|
const params = matchSegments(urlParts, record.segments);
|
|
483
653
|
if (params !== null) return {
|
|
484
654
|
handler: record.handler,
|
|
485
655
|
params
|
|
486
656
|
};
|
|
487
657
|
}
|
|
488
|
-
|
|
658
|
+
const fallbackMatch = findBestScopedFallbackRecord(normalized, records);
|
|
659
|
+
return fallbackMatch ? {
|
|
660
|
+
handler: fallbackMatch.record.handler,
|
|
661
|
+
params: fallbackMatch.params
|
|
662
|
+
} : null;
|
|
489
663
|
}
|
|
490
664
|
function getMatchingRecord(target, routeRecords) {
|
|
491
665
|
const location = parseLocation(target);
|
|
492
666
|
const normalized = location.pathname.endsWith("/") && location.pathname !== "/" ? location.pathname.slice(0, -1) : location.pathname;
|
|
493
667
|
const urlParts = normalized === "/" ? [] : normalized.split("/").filter(Boolean);
|
|
494
668
|
for (const record of routeRecords) {
|
|
669
|
+
if (record.fallbackPrefix) continue;
|
|
495
670
|
const params = matchSegments(urlParts, record.segments);
|
|
496
671
|
if (params !== null) return {
|
|
497
672
|
record,
|
|
498
673
|
params
|
|
499
674
|
};
|
|
500
675
|
}
|
|
501
|
-
return
|
|
676
|
+
return findBestScopedFallbackRecord(normalized, routeRecords);
|
|
502
677
|
}
|
|
503
678
|
function getRoutePolicies(options) {
|
|
504
679
|
if (!options) return [];
|
|
@@ -571,29 +746,13 @@ function resolveRouteRequest(target, options = {}) {
|
|
|
571
746
|
*/
|
|
572
747
|
function resolveRouteFromRoutes(pathname, routeList) {
|
|
573
748
|
if (routeList === routes) return resolveRoute(pathname);
|
|
574
|
-
const
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
let bestParams = {};
|
|
579
|
-
let bestRank = -Infinity;
|
|
580
|
-
for (const r of sorted) {
|
|
581
|
-
const rank = cachedRank(r);
|
|
582
|
-
if (rank < bestRank) break;
|
|
583
|
-
if (bestHandler !== null && rank === bestRank) continue;
|
|
584
|
-
const params = matchSegments(urlParts, cachedSegs(r));
|
|
585
|
-
if (params !== null) {
|
|
586
|
-
bestHandler = r.handler;
|
|
587
|
-
bestParams = params;
|
|
588
|
-
bestRank = rank;
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
return bestHandler !== null ? {
|
|
592
|
-
handler: bestHandler,
|
|
593
|
-
params: bestParams
|
|
749
|
+
const match = findBestResolvedRouteFromRoutes(pathname, routeList);
|
|
750
|
+
return match ? {
|
|
751
|
+
handler: match.route.handler,
|
|
752
|
+
params: match.params
|
|
594
753
|
} : null;
|
|
595
754
|
}
|
|
596
755
|
//#endregion
|
|
597
|
-
export { _applyManifest, _drainLazy, _setActiveRouteAuthOptions, _snapshotLazy, clearRoutes, currentRoute, fallback,
|
|
756
|
+
export { Outlet, _applyManifest, _drainLazy, _setActiveRouteAuthOptions, _snapshotLazy, clearRoutes, currentRoute, fallback, getManifest, getRoutes, group, index, lazy, lockRouteRegistration, page, registerRoutes, resolveRoute, resolveRouteFromRoutes, resolveRouteRequest, route, setServerLocation, syncCurrentRouteSnapshot };
|
|
598
757
|
|
|
599
758
|
//# sourceMappingURL=route.js.map
|