@pracht/core 0.2.6 → 0.2.7
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/README.md +1 -1
- package/dist/index.d.mts +9 -11
- package/dist/index.mjs +372 -161
- package/package.json +1 -1
- package/dist/app-CUL3q2ZA.mjs +0 -259
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ metadata for the failure phase and matched framework files when available.
|
|
|
37
37
|
- `startApp()` — client-side hydration and runtime
|
|
38
38
|
- `useLocation()` — access the current pathname and search string separately
|
|
39
39
|
- `useRouteData()` — access loader data inside a route component
|
|
40
|
-
- `
|
|
40
|
+
- `useRevalidate()` — trigger a revalidation of the current route's data
|
|
41
41
|
- `<Form>` — progressive enhancement form component
|
|
42
42
|
|
|
43
43
|
### Types
|
package/dist/index.d.mts
CHANGED
|
@@ -35,7 +35,9 @@ interface TimeRevalidatePolicy {
|
|
|
35
35
|
}
|
|
36
36
|
type RouteRevalidate = TimeRevalidatePolicy;
|
|
37
37
|
type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
|
|
38
|
-
type ApiRouteArgs<TContext = RegisteredContext> = BaseRouteArgs<TContext
|
|
38
|
+
type ApiRouteArgs<TContext = RegisteredContext> = Omit<BaseRouteArgs<TContext>, "route"> & {
|
|
39
|
+
route: ResolvedApiRoute;
|
|
40
|
+
};
|
|
39
41
|
type ApiRouteHandler<TContext = RegisteredContext> = (args: ApiRouteArgs<TContext>) => MaybePromise<Response>;
|
|
40
42
|
interface ApiRouteModule<TContext = any> {
|
|
41
43
|
default?: ApiRouteHandler<TContext>;
|
|
@@ -286,10 +288,8 @@ interface HandlePrachtRequestOptions<TContext = unknown> {
|
|
|
286
288
|
/** Expose raw server error details in rendered HTML and route-state JSON. */
|
|
287
289
|
debugErrors?: boolean;
|
|
288
290
|
clientEntryUrl?: string;
|
|
289
|
-
/** Per-source-file CSS map produced by the vite plugin
|
|
291
|
+
/** Per-source-file CSS map produced by the vite plugin. */
|
|
290
292
|
cssManifest?: Record<string, string[]>;
|
|
291
|
-
/** @deprecated Pass cssManifest instead for per-page CSS resolution. */
|
|
292
|
-
cssUrls?: string[];
|
|
293
293
|
/** Per-source-file JS chunk map produced by the vite plugin for modulepreload hints. */
|
|
294
294
|
jsManifest?: Record<string, string[]>;
|
|
295
295
|
apiRoutes?: ResolvedApiRoute[];
|
|
@@ -318,12 +318,14 @@ declare function PrachtRuntimeProvider<TData>({
|
|
|
318
318
|
data,
|
|
319
319
|
params,
|
|
320
320
|
routeId,
|
|
321
|
+
stateVersion,
|
|
321
322
|
url
|
|
322
323
|
}: {
|
|
323
324
|
children: ComponentChildren;
|
|
324
325
|
data: TData;
|
|
325
326
|
params?: RouteParams;
|
|
326
327
|
routeId: string;
|
|
328
|
+
stateVersion?: number;
|
|
327
329
|
url: string;
|
|
328
330
|
}): _$preact.VNode<_$preact.Attributes & {
|
|
329
331
|
value: PrachtRuntimeValue | undefined;
|
|
@@ -339,8 +341,6 @@ interface Location {
|
|
|
339
341
|
declare function useLocation(): Location;
|
|
340
342
|
declare function useParams(): RouteParams;
|
|
341
343
|
declare function useRevalidate(): () => Promise<unknown>;
|
|
342
|
-
/** @deprecated Use useRevalidate instead. */
|
|
343
|
-
declare const useRevalidateRoute: typeof useRevalidate;
|
|
344
344
|
declare function Form(props: FormProps): _$preact.VNode<_$preact.ClassAttributes<HTMLFormElement> & h.JSX.HTMLAttributes<HTMLFormElement>>;
|
|
345
345
|
type RouteStateResult = {
|
|
346
346
|
type: "data";
|
|
@@ -370,12 +370,10 @@ interface PrerenderAppOptions {
|
|
|
370
370
|
app: PrachtApp;
|
|
371
371
|
registry?: ModuleRegistry;
|
|
372
372
|
clientEntryUrl?: string;
|
|
373
|
-
/** Per-source-file CSS map produced by the vite plugin
|
|
373
|
+
/** Per-source-file CSS map produced by the vite plugin. */
|
|
374
374
|
cssManifest?: Record<string, string[]>;
|
|
375
375
|
/** Per-source-file JS map produced by the vite plugin for modulepreload hints. */
|
|
376
376
|
jsManifest?: Record<string, string[]>;
|
|
377
|
-
/** @deprecated Pass cssManifest instead for per-page CSS resolution. */
|
|
378
|
-
cssUrls?: string[];
|
|
379
377
|
}
|
|
380
378
|
declare function prerenderApp(options: PrerenderAppOptions): Promise<PrerenderResult[]>;
|
|
381
379
|
declare function prerenderApp(options: PrerenderAppOptions & {
|
|
@@ -389,7 +387,7 @@ declare global {
|
|
|
389
387
|
__PRACHT_ROUTER_READY__?: boolean;
|
|
390
388
|
}
|
|
391
389
|
}
|
|
392
|
-
type ModuleMap = Record<string, () => Promise<
|
|
390
|
+
type ModuleMap = Record<string, () => Promise<unknown>>;
|
|
393
391
|
type NavigateFn = (to: string, options?: {
|
|
394
392
|
replace?: boolean;
|
|
395
393
|
}) => Promise<void>;
|
|
@@ -404,4 +402,4 @@ interface InitClientRouterOptions {
|
|
|
404
402
|
}
|
|
405
403
|
declare function initClientRouter(options: InitClientRouterOptions): Promise<void>;
|
|
406
404
|
//#endregion
|
|
407
|
-
export { type ApiConfig, type ApiRouteArgs, type ApiRouteHandler, type ApiRouteMatch, type ApiRouteModule, type BaseRouteArgs, type DataModule, type ErrorBoundaryProps, Form, type FormProps, type GroupDefinition, type GroupMeta, type HandlePrachtRequestOptions, type HeadArgs, type HeadMetadata, type HeadersArgs, type HttpMethod, type ISGManifestEntry, type InitClientRouterOptions, type LoaderArgs, type LoaderData, type LoaderFn, type Location, type MiddlewareArgs, type MiddlewareFn, type MiddlewareModule, type MiddlewareResult, type ModuleImporter, type ModuleRef, type ModuleRegistry, type NavigateFn, type PrachtApp, type PrachtAppConfig, PrachtHttpError, type PrachtHydrationState, type PrachtRuntimeDiagnosticPhase, type PrachtRuntimeDiagnostics, PrachtRuntimeProvider, type PrefetchStrategy, type PrerenderAppOptions, type PrerenderAppResult, type PrerenderResult, type Register, type RenderMode, type ResolvedApiRoute, type ResolvedPrachtApp, type ResolvedRoute, type RouteComponentProps, type RouteConfig, type RouteDefinition, type RouteMatch, type RouteMeta, type RouteModule, type RouteParams, type RouteRevalidate, type RouteStateResult, type RouteTreeNode, type SerializedRouteError, type ShellModule, type ShellProps, type StartAppOptions, Suspense, type TimeRevalidatePolicy, applyDefaultSecurityHeaders, buildPathFromSegments, defineApp, forwardRef, group, handlePrachtRequest, initClientRouter, lazy, matchApiRoute, matchAppRoute, prerenderApp, readHydrationState, resolveApiRoutes, resolveApp, route, startApp, timeRevalidate, useIsHydrated, useLocation, useNavigate, useParams, useRevalidate,
|
|
405
|
+
export { type ApiConfig, type ApiRouteArgs, type ApiRouteHandler, type ApiRouteMatch, type ApiRouteModule, type BaseRouteArgs, type DataModule, type ErrorBoundaryProps, Form, type FormProps, type GroupDefinition, type GroupMeta, type HandlePrachtRequestOptions, type HeadArgs, type HeadMetadata, type HeadersArgs, type HttpMethod, type ISGManifestEntry, type InitClientRouterOptions, type LoaderArgs, type LoaderData, type LoaderFn, type Location, type MiddlewareArgs, type MiddlewareFn, type MiddlewareModule, type MiddlewareResult, type ModuleImporter, type ModuleRef, type ModuleRegistry, type NavigateFn, type PrachtApp, type PrachtAppConfig, PrachtHttpError, type PrachtHydrationState, type PrachtRuntimeDiagnosticPhase, type PrachtRuntimeDiagnostics, PrachtRuntimeProvider, type PrefetchStrategy, type PrerenderAppOptions, type PrerenderAppResult, type PrerenderResult, type Register, type RenderMode, type ResolvedApiRoute, type ResolvedPrachtApp, type ResolvedRoute, type RouteComponentProps, type RouteConfig, type RouteDefinition, type RouteMatch, type RouteMeta, type RouteModule, type RouteParams, type RouteRevalidate, type RouteStateResult, type RouteTreeNode, type SerializedRouteError, type ShellModule, type ShellProps, type StartAppOptions, Suspense, type TimeRevalidatePolicy, applyDefaultSecurityHeaders, buildPathFromSegments, defineApp, forwardRef, group, handlePrachtRequest, initClientRouter, lazy, matchApiRoute, matchAppRoute, prerenderApp, readHydrationState, resolveApiRoutes, resolveApp, route, startApp, timeRevalidate, useIsHydrated, useLocation, useNavigate, useParams, useRevalidate, useRouteData };
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,241 @@
|
|
|
1
|
-
import { a as matchApiRoute, c as resolveApp, i as group, l as route, n as buildPathFromSegments, o as matchAppRoute, r as defineApp, s as resolveApiRoutes, u as timeRevalidate } from "./app-CUL3q2ZA.mjs";
|
|
2
1
|
import { createContext, h, hydrate, options, render } from "preact";
|
|
3
2
|
import { useContext, useEffect, useMemo, useState } from "preact/hooks";
|
|
4
3
|
import { Suspense, lazy } from "preact-suspense";
|
|
4
|
+
//#region src/app.ts
|
|
5
|
+
function timeRevalidate(seconds) {
|
|
6
|
+
if (!Number.isInteger(seconds) || seconds <= 0) throw new Error("timeRevalidate expects a positive integer number of seconds.");
|
|
7
|
+
return {
|
|
8
|
+
kind: "time",
|
|
9
|
+
seconds
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function route(path, fileOrConfig, meta = {}) {
|
|
13
|
+
if (typeof fileOrConfig === "string" || typeof fileOrConfig === "function") return {
|
|
14
|
+
kind: "route",
|
|
15
|
+
path: normalizeRoutePath(path),
|
|
16
|
+
file: resolveModuleRef(fileOrConfig),
|
|
17
|
+
...meta
|
|
18
|
+
};
|
|
19
|
+
const { component, loader, ...routeMeta } = fileOrConfig;
|
|
20
|
+
return {
|
|
21
|
+
kind: "route",
|
|
22
|
+
path: normalizeRoutePath(path),
|
|
23
|
+
file: resolveModuleRef(component),
|
|
24
|
+
loaderFile: resolveModuleRef(loader),
|
|
25
|
+
...routeMeta
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function resolveModuleRef(ref) {
|
|
29
|
+
if (ref === void 0) return void 0;
|
|
30
|
+
if (typeof ref === "string") return ref;
|
|
31
|
+
return "";
|
|
32
|
+
}
|
|
33
|
+
function group(meta, routes) {
|
|
34
|
+
return {
|
|
35
|
+
kind: "group",
|
|
36
|
+
meta,
|
|
37
|
+
routes
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function defineApp(config) {
|
|
41
|
+
return {
|
|
42
|
+
shells: resolveModuleRefRecord(config.shells ?? {}),
|
|
43
|
+
middleware: resolveModuleRefRecord(config.middleware ?? {}),
|
|
44
|
+
api: config.api ?? {},
|
|
45
|
+
routes: config.routes
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function resolveModuleRefRecord(record) {
|
|
49
|
+
const result = {};
|
|
50
|
+
for (const [key, value] of Object.entries(record)) result[key] = resolveModuleRef(value);
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
function resolveApp(app) {
|
|
54
|
+
const routes = [];
|
|
55
|
+
const inherited = {
|
|
56
|
+
pathPrefix: "/",
|
|
57
|
+
middleware: []
|
|
58
|
+
};
|
|
59
|
+
for (const node of app.routes) flattenRouteNode(app, node, inherited, routes);
|
|
60
|
+
return {
|
|
61
|
+
shells: app.shells,
|
|
62
|
+
middleware: app.middleware,
|
|
63
|
+
api: app.api,
|
|
64
|
+
routes,
|
|
65
|
+
apiRoutes: []
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function matchAppRoute(app, pathname) {
|
|
69
|
+
const resolved = isResolvedApp(app) ? app : resolveApp(app);
|
|
70
|
+
const normalizedPathname = normalizeRoutePath(pathname);
|
|
71
|
+
const targetSegments = splitPathSegments(normalizedPathname);
|
|
72
|
+
for (const currentRoute of resolved.routes) {
|
|
73
|
+
const params = matchRouteSegments(currentRoute.segments, targetSegments);
|
|
74
|
+
if (params) return {
|
|
75
|
+
route: currentRoute,
|
|
76
|
+
params,
|
|
77
|
+
pathname: normalizedPathname
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function flattenRouteNode(app, node, inherited, routes) {
|
|
82
|
+
if (node.kind === "group") {
|
|
83
|
+
const nextInherited = {
|
|
84
|
+
pathPrefix: mergeRoutePaths(inherited.pathPrefix, node.meta.pathPrefix),
|
|
85
|
+
shell: node.meta.shell ?? inherited.shell,
|
|
86
|
+
render: node.meta.render ?? inherited.render,
|
|
87
|
+
middleware: [...inherited.middleware, ...node.meta.middleware ?? []]
|
|
88
|
+
};
|
|
89
|
+
for (const child of node.routes) flattenRouteNode(app, child, nextInherited, routes);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const fullPath = mergeRoutePaths(inherited.pathPrefix, node.path);
|
|
93
|
+
const shell = node.shell ?? inherited.shell;
|
|
94
|
+
const middleware = [...inherited.middleware, ...node.middleware ?? []];
|
|
95
|
+
routes.push({
|
|
96
|
+
id: node.id ?? createRouteId(fullPath),
|
|
97
|
+
path: fullPath,
|
|
98
|
+
file: node.file,
|
|
99
|
+
loaderFile: node.loaderFile,
|
|
100
|
+
shell,
|
|
101
|
+
shellFile: shell ? app.shells[shell] : void 0,
|
|
102
|
+
render: node.render ?? inherited.render,
|
|
103
|
+
middleware,
|
|
104
|
+
middlewareFiles: middleware.flatMap((name) => {
|
|
105
|
+
const middlewareFile = app.middleware[name];
|
|
106
|
+
return middlewareFile ? [middlewareFile] : [];
|
|
107
|
+
}),
|
|
108
|
+
revalidate: node.revalidate,
|
|
109
|
+
segments: parseRouteSegments(fullPath)
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
function isResolvedApp(app) {
|
|
113
|
+
return app.routes.length === 0 || "segments" in app.routes[0];
|
|
114
|
+
}
|
|
115
|
+
function matchRouteSegments(routeSegments, targetSegments) {
|
|
116
|
+
const params = {};
|
|
117
|
+
let routeIndex = 0;
|
|
118
|
+
let targetIndex = 0;
|
|
119
|
+
while (routeIndex < routeSegments.length) {
|
|
120
|
+
const currentSegment = routeSegments[routeIndex];
|
|
121
|
+
if (currentSegment.type === "catchall") {
|
|
122
|
+
params[currentSegment.name] = targetSegments.slice(targetIndex).join("/");
|
|
123
|
+
return params;
|
|
124
|
+
}
|
|
125
|
+
const targetSegment = targetSegments[targetIndex];
|
|
126
|
+
if (typeof targetSegment === "undefined") return null;
|
|
127
|
+
if (currentSegment.type === "static") {
|
|
128
|
+
if (currentSegment.value !== targetSegment) return null;
|
|
129
|
+
} else try {
|
|
130
|
+
params[currentSegment.name] = decodeURIComponent(targetSegment);
|
|
131
|
+
} catch {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
routeIndex += 1;
|
|
135
|
+
targetIndex += 1;
|
|
136
|
+
}
|
|
137
|
+
return targetIndex === targetSegments.length ? params : null;
|
|
138
|
+
}
|
|
139
|
+
function parseRouteSegments(path) {
|
|
140
|
+
return splitPathSegments(path).map((segment) => {
|
|
141
|
+
if (segment === "*") return {
|
|
142
|
+
type: "catchall",
|
|
143
|
+
name: "*"
|
|
144
|
+
};
|
|
145
|
+
if (segment.startsWith(":")) return {
|
|
146
|
+
type: "param",
|
|
147
|
+
name: segment.slice(1)
|
|
148
|
+
};
|
|
149
|
+
return {
|
|
150
|
+
type: "static",
|
|
151
|
+
value: segment
|
|
152
|
+
};
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
function splitPathSegments(path) {
|
|
156
|
+
return normalizeRoutePath(path).split("/").filter(Boolean);
|
|
157
|
+
}
|
|
158
|
+
function mergeRoutePaths(prefix, path) {
|
|
159
|
+
if (!path) return normalizeRoutePath(prefix);
|
|
160
|
+
const normalizedPrefix = normalizeRoutePath(prefix);
|
|
161
|
+
const normalizedPath = normalizeRoutePath(path);
|
|
162
|
+
if (normalizedPrefix === "/") return normalizedPath;
|
|
163
|
+
if (normalizedPath === "/") return normalizedPrefix;
|
|
164
|
+
return normalizeRoutePath(`${normalizedPrefix}/${normalizedPath.slice(1)}`);
|
|
165
|
+
}
|
|
166
|
+
function normalizeRoutePath(path) {
|
|
167
|
+
if (!path || path === "/") return "/";
|
|
168
|
+
const collapsed = (path.startsWith("/") ? path : `/${path}`).replace(/\/{2,}/g, "/");
|
|
169
|
+
return collapsed.length > 1 && collapsed.endsWith("/") ? collapsed.slice(0, -1) : collapsed;
|
|
170
|
+
}
|
|
171
|
+
function buildPathFromSegments(segments, params) {
|
|
172
|
+
return normalizeRoutePath("/" + segments.map((segment) => {
|
|
173
|
+
if (segment.type === "static") return segment.value;
|
|
174
|
+
if (segment.type === "param") return encodeURIComponent(params[segment.name] ?? "");
|
|
175
|
+
return params["*"] ?? "";
|
|
176
|
+
}).join("/"));
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Convert a list of file paths from `import.meta.glob` into resolved API routes.
|
|
180
|
+
*
|
|
181
|
+
* Example: `"/src/api/health.ts"` → path `/api/health`
|
|
182
|
+
* `"/src/api/users/[id].ts"` → path `/api/users/:id`
|
|
183
|
+
* `"/src/api/index.ts"` → path `/api`
|
|
184
|
+
*/
|
|
185
|
+
function resolveApiRoutes(files, apiDir = "/src/api") {
|
|
186
|
+
const normalizedDir = apiDir.replace(/\/$/, "");
|
|
187
|
+
return files.map((file) => {
|
|
188
|
+
let relative = file;
|
|
189
|
+
if (relative.startsWith(normalizedDir)) relative = relative.slice(normalizedDir.length);
|
|
190
|
+
relative = relative.replace(/\.(ts|tsx|js|jsx)$/, "");
|
|
191
|
+
if (relative.endsWith("/index")) relative = relative.slice(0, -6) || "/";
|
|
192
|
+
relative = relative.replace(/\[([^\]]+)\]/g, ":$1");
|
|
193
|
+
const path = normalizeRoutePath(`/api${relative}`);
|
|
194
|
+
return {
|
|
195
|
+
path,
|
|
196
|
+
file,
|
|
197
|
+
segments: parseRouteSegments(path)
|
|
198
|
+
};
|
|
199
|
+
}).sort(compareResolvedApiRoutes);
|
|
200
|
+
}
|
|
201
|
+
function matchApiRoute(apiRoutes, pathname) {
|
|
202
|
+
const normalizedPathname = normalizeRoutePath(pathname);
|
|
203
|
+
const targetSegments = splitPathSegments(normalizedPathname);
|
|
204
|
+
for (const route of apiRoutes) {
|
|
205
|
+
const params = matchRouteSegments(route.segments, targetSegments);
|
|
206
|
+
if (params) return {
|
|
207
|
+
route,
|
|
208
|
+
params,
|
|
209
|
+
pathname: normalizedPathname
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
function createRouteId(path) {
|
|
214
|
+
if (path === "/") return "index";
|
|
215
|
+
return path.slice(1).split("/").map((segment) => {
|
|
216
|
+
if (segment === "*") return "splat";
|
|
217
|
+
return segment.startsWith(":") ? segment.slice(1) : segment;
|
|
218
|
+
}).join("-").replace(/[^a-zA-Z0-9-]/g, "-");
|
|
219
|
+
}
|
|
220
|
+
function compareResolvedApiRoutes(left, right) {
|
|
221
|
+
const length = Math.max(left.segments.length, right.segments.length);
|
|
222
|
+
for (let index = 0; index < length; index += 1) {
|
|
223
|
+
const leftSegment = left.segments[index];
|
|
224
|
+
const rightSegment = right.segments[index];
|
|
225
|
+
if (!leftSegment) return 1;
|
|
226
|
+
if (!rightSegment) return -1;
|
|
227
|
+
const leftScore = getRouteSegmentSpecificity(leftSegment);
|
|
228
|
+
const rightScore = getRouteSegmentSpecificity(rightSegment);
|
|
229
|
+
if (leftScore !== rightScore) return rightScore - leftScore;
|
|
230
|
+
}
|
|
231
|
+
return left.path.localeCompare(right.path);
|
|
232
|
+
}
|
|
233
|
+
function getRouteSegmentSpecificity(segment) {
|
|
234
|
+
if (segment.type === "static") return 3;
|
|
235
|
+
if (segment.type === "param") return 2;
|
|
236
|
+
return 1;
|
|
237
|
+
}
|
|
238
|
+
//#endregion
|
|
5
239
|
//#region src/forwardRef.ts
|
|
6
240
|
let oldDiffHook = options.__b;
|
|
7
241
|
options.__b = (vnode) => {
|
|
@@ -80,6 +314,7 @@ const SAFE_METHODS = new Set(["GET", "HEAD"]);
|
|
|
80
314
|
const HYDRATION_STATE_ELEMENT_ID = "pracht-state";
|
|
81
315
|
const ROUTE_STATE_REQUEST_HEADER = "x-pracht-route-state-request";
|
|
82
316
|
const ROUTE_STATE_CACHE_CONTROL = "no-store";
|
|
317
|
+
const EMPTY_ROUTE_PARAMS = {};
|
|
83
318
|
let _renderToStringAsync;
|
|
84
319
|
async function getRenderToStringAsync() {
|
|
85
320
|
if (_renderToStringAsync) return _renderToStringAsync;
|
|
@@ -87,25 +322,37 @@ async function getRenderToStringAsync() {
|
|
|
87
322
|
return _renderToStringAsync;
|
|
88
323
|
}
|
|
89
324
|
const RouteDataContext = createContext(void 0);
|
|
90
|
-
function PrachtRuntimeProvider({ children, data, params =
|
|
91
|
-
const [
|
|
325
|
+
function PrachtRuntimeProvider({ children, data, params = EMPTY_ROUTE_PARAMS, routeId, stateVersion = 0, url }) {
|
|
326
|
+
const [routeDataState, setRouteDataState] = useState({
|
|
327
|
+
data,
|
|
328
|
+
stateVersion
|
|
329
|
+
});
|
|
330
|
+
const routeData = routeDataState.stateVersion === stateVersion ? routeDataState.data : data;
|
|
92
331
|
useEffect(() => {
|
|
93
|
-
|
|
332
|
+
setRouteDataState({
|
|
333
|
+
data,
|
|
334
|
+
stateVersion
|
|
335
|
+
});
|
|
94
336
|
}, [
|
|
95
337
|
data,
|
|
96
338
|
routeId,
|
|
339
|
+
stateVersion,
|
|
97
340
|
url
|
|
98
341
|
]);
|
|
99
342
|
const context = useMemo(() => ({
|
|
100
343
|
data: routeData,
|
|
101
344
|
params,
|
|
102
345
|
routeId,
|
|
103
|
-
setData:
|
|
346
|
+
setData: (nextData) => setRouteDataState({
|
|
347
|
+
data: nextData,
|
|
348
|
+
stateVersion
|
|
349
|
+
}),
|
|
104
350
|
url
|
|
105
351
|
}), [
|
|
106
352
|
routeData,
|
|
107
353
|
params,
|
|
108
354
|
routeId,
|
|
355
|
+
stateVersion,
|
|
109
356
|
url
|
|
110
357
|
]);
|
|
111
358
|
return h(RouteDataContext.Provider, {
|
|
@@ -125,13 +372,9 @@ function readHydrationState() {
|
|
|
125
372
|
if (!(element instanceof HTMLScriptElement)) return;
|
|
126
373
|
const raw = element.textContent;
|
|
127
374
|
if (!raw) return;
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
return state;
|
|
132
|
-
} catch {
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
375
|
+
const state = JSON.parse(raw);
|
|
376
|
+
window.__PRACHT_STATE__ = state;
|
|
377
|
+
return state;
|
|
135
378
|
}
|
|
136
379
|
function useRouteData() {
|
|
137
380
|
return useContext(RouteDataContext)?.data;
|
|
@@ -151,13 +394,11 @@ function useRevalidate() {
|
|
|
151
394
|
await navigateToClientLocation(result.location);
|
|
152
395
|
return;
|
|
153
396
|
}
|
|
154
|
-
if (result.type === "error") throw deserializeRouteError
|
|
397
|
+
if (result.type === "error") throw deserializeRouteError(result.error);
|
|
155
398
|
runtime?.setData(result.data);
|
|
156
399
|
return result.data;
|
|
157
400
|
};
|
|
158
401
|
}
|
|
159
|
-
/** @deprecated Use useRevalidate instead. */
|
|
160
|
-
const useRevalidateRoute = useRevalidate;
|
|
161
402
|
function Form(props) {
|
|
162
403
|
const { onSubmit, method, ...rest } = props;
|
|
163
404
|
return h("form", {
|
|
@@ -187,9 +428,10 @@ function Form(props) {
|
|
|
187
428
|
}
|
|
188
429
|
});
|
|
189
430
|
}
|
|
190
|
-
async function fetchPrachtRouteState(url) {
|
|
191
|
-
const
|
|
192
|
-
|
|
431
|
+
async function fetchPrachtRouteState(url, options) {
|
|
432
|
+
const fetchUrl = options?.useDataParam ? buildRouteStateUrl(url) : url;
|
|
433
|
+
const response = await fetch(fetchUrl, {
|
|
434
|
+
headers: options?.useDataParam ? {} : {
|
|
193
435
|
[ROUTE_STATE_REQUEST_HEADER]: "1",
|
|
194
436
|
"Cache-Control": "no-cache"
|
|
195
437
|
},
|
|
@@ -216,11 +458,16 @@ async function fetchPrachtRouteState(url) {
|
|
|
216
458
|
type: "data"
|
|
217
459
|
};
|
|
218
460
|
}
|
|
461
|
+
function buildRouteStateUrl(url) {
|
|
462
|
+
return `${url}${url.includes("?") ? "&" : "?"}_data=1`;
|
|
463
|
+
}
|
|
219
464
|
async function handlePrachtRequest(options) {
|
|
220
465
|
const url = new URL(options.request.url);
|
|
466
|
+
const hasDataParam = url.searchParams.get("_data") === "1";
|
|
467
|
+
if (hasDataParam) url.searchParams.delete("_data");
|
|
221
468
|
const requestPath = getRequestPath(url);
|
|
222
469
|
const registry = options.registry ?? {};
|
|
223
|
-
const isRouteStateRequest = options.request.headers.get(ROUTE_STATE_REQUEST_HEADER) === "1";
|
|
470
|
+
const isRouteStateRequest = options.request.headers.get(ROUTE_STATE_REQUEST_HEADER) === "1" || hasDataParam;
|
|
224
471
|
const exposeDiagnostics = shouldExposeServerErrors(options);
|
|
225
472
|
if (options.apiRoutes?.length) {
|
|
226
473
|
const apiMatch = matchApiRoute(options.apiRoutes, url.pathname);
|
|
@@ -311,7 +558,7 @@ async function handlePrachtRequest(options) {
|
|
|
311
558
|
let loaderFile;
|
|
312
559
|
let currentPhase = "middleware";
|
|
313
560
|
try {
|
|
314
|
-
const
|
|
561
|
+
const middlewarePromise = runMiddlewareChain({
|
|
315
562
|
context: routeArgs.context,
|
|
316
563
|
middlewareFiles: match.route.middlewareFiles,
|
|
317
564
|
params: match.params,
|
|
@@ -320,25 +567,31 @@ async function handlePrachtRequest(options) {
|
|
|
320
567
|
route: match.route,
|
|
321
568
|
url
|
|
322
569
|
});
|
|
570
|
+
const routeModulePromise = resolveRegistryModule(registry.routeModules, match.route.file);
|
|
571
|
+
const shellModulePromise = match.route.shellFile ? resolveRegistryModule(registry.shellModules, match.route.shellFile) : Promise.resolve(void 0);
|
|
572
|
+
const dataFunctionsPromise = routeModulePromise.then((mod) => resolveDataFunctions(match.route, mod, registry));
|
|
573
|
+
routeModulePromise.catch(() => {});
|
|
574
|
+
shellModulePromise.catch(() => {});
|
|
575
|
+
dataFunctionsPromise.catch(() => {});
|
|
576
|
+
const middlewareResult = await middlewarePromise;
|
|
323
577
|
if (middlewareResult.response) return normalizePageResponse(middlewareResult.response, { isRouteStateRequest });
|
|
324
578
|
routeArgs = {
|
|
325
579
|
...routeArgs,
|
|
326
580
|
context: middlewareResult.context
|
|
327
581
|
};
|
|
328
582
|
currentPhase = "render";
|
|
329
|
-
routeModule = await
|
|
583
|
+
routeModule = await routeModulePromise;
|
|
330
584
|
if (!routeModule) throw new Error("Route module not found");
|
|
331
585
|
currentPhase = "loader";
|
|
332
|
-
const { loader, loaderFile: resolvedLoaderFile } = await
|
|
586
|
+
const { loader, loaderFile: resolvedLoaderFile } = await dataFunctionsPromise;
|
|
333
587
|
loaderFile = resolvedLoaderFile;
|
|
334
588
|
const loaderResult = loader ? await loader(routeArgs) : void 0;
|
|
335
589
|
if (loaderResult instanceof Response) return normalizePageResponse(loaderResult, { isRouteStateRequest });
|
|
336
590
|
const data = loaderResult;
|
|
337
591
|
if (isRouteStateRequest) return withRouteResponseHeaders(Response.json({ data }), { isRouteStateRequest: true });
|
|
338
592
|
currentPhase = "render";
|
|
339
|
-
shellModule =
|
|
340
|
-
const head = await mergeHeadMetadata(shellModule, routeModule, routeArgs, data);
|
|
341
|
-
const documentHeaders = await mergeDocumentHeaders(shellModule, routeModule, routeArgs, data);
|
|
593
|
+
shellModule = await shellModulePromise;
|
|
594
|
+
const [head, documentHeaders] = await Promise.all([mergeHeadMetadata(shellModule, routeModule, routeArgs, data), mergeDocumentHeaders(shellModule, routeModule, routeArgs, data)]);
|
|
342
595
|
const cssUrls = resolvePageCssUrls(options, match.route.shellFile, match.route.file);
|
|
343
596
|
const modulePreloadUrls = resolvePageJsUrls(options, match.route.shellFile, match.route.file);
|
|
344
597
|
if (match.route.render === "spa") {
|
|
@@ -361,18 +614,20 @@ async function handlePrachtRequest(options) {
|
|
|
361
614
|
},
|
|
362
615
|
clientEntryUrl: options.clientEntryUrl,
|
|
363
616
|
cssUrls,
|
|
364
|
-
modulePreloadUrls
|
|
617
|
+
modulePreloadUrls,
|
|
618
|
+
routeStatePreloadUrl: loader ? buildRouteStateUrl(requestPath) : void 0
|
|
365
619
|
}), 200, documentHeaders);
|
|
366
620
|
}
|
|
367
621
|
const DefaultComponent = typeof routeModule.default === "function" ? routeModule.default : void 0;
|
|
368
622
|
const Component = routeModule.Component ?? DefaultComponent;
|
|
369
623
|
if (!Component) throw new Error("Route has no Component or default export");
|
|
370
624
|
const Shell = shellModule?.Shell;
|
|
625
|
+
const Comp = Component;
|
|
371
626
|
const componentProps = {
|
|
372
627
|
data,
|
|
373
628
|
params: match.params
|
|
374
629
|
};
|
|
375
|
-
const componentTree = Shell ? h(Shell, null, h(
|
|
630
|
+
const componentTree = Shell ? h(Shell, null, h(Comp, componentProps)) : h(Comp, componentProps);
|
|
376
631
|
const tree = h(PrachtRuntimeProvider, {
|
|
377
632
|
data,
|
|
378
633
|
params: match.params,
|
|
@@ -409,18 +664,11 @@ async function handlePrachtRequest(options) {
|
|
|
409
664
|
}
|
|
410
665
|
}
|
|
411
666
|
function parseLocation(value) {
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
};
|
|
418
|
-
} catch {
|
|
419
|
-
return {
|
|
420
|
-
pathname: value || "/",
|
|
421
|
-
search: ""
|
|
422
|
-
};
|
|
423
|
-
}
|
|
667
|
+
const url = new URL(value, "http://pracht.local");
|
|
668
|
+
return {
|
|
669
|
+
pathname: url.pathname,
|
|
670
|
+
search: url.search
|
|
671
|
+
};
|
|
424
672
|
}
|
|
425
673
|
function getRequestPath(url) {
|
|
426
674
|
return `${url.pathname}${url.search}`;
|
|
@@ -455,27 +703,23 @@ function resolveManifestEntries(manifest, file) {
|
|
|
455
703
|
const resolved = getSuffixIndex(manifest).get(normalizeModulePath(file));
|
|
456
704
|
if (resolved) return manifest[resolved];
|
|
457
705
|
}
|
|
706
|
+
function resolvePageUrlsFromManifest(manifest, shellFile, routeFile) {
|
|
707
|
+
const urls = /* @__PURE__ */ new Set();
|
|
708
|
+
const add = (file) => {
|
|
709
|
+
const entries = resolveManifestEntries(manifest, file);
|
|
710
|
+
if (entries) for (const url of entries) urls.add(url);
|
|
711
|
+
};
|
|
712
|
+
if (shellFile) add(shellFile);
|
|
713
|
+
add(routeFile);
|
|
714
|
+
return [...urls];
|
|
715
|
+
}
|
|
458
716
|
function resolvePageCssUrls(options, shellFile, routeFile) {
|
|
459
|
-
if (!options.cssManifest) return
|
|
460
|
-
|
|
461
|
-
function addFromManifest(file) {
|
|
462
|
-
const entries = resolveManifestEntries(options.cssManifest, file);
|
|
463
|
-
if (entries) for (const c of entries) css.add(c);
|
|
464
|
-
}
|
|
465
|
-
if (shellFile) addFromManifest(shellFile);
|
|
466
|
-
addFromManifest(routeFile);
|
|
467
|
-
return [...css];
|
|
717
|
+
if (!options.cssManifest) return [];
|
|
718
|
+
return resolvePageUrlsFromManifest(options.cssManifest, shellFile, routeFile);
|
|
468
719
|
}
|
|
469
720
|
function resolvePageJsUrls(options, shellFile, routeFile) {
|
|
470
721
|
if (!options.jsManifest) return [];
|
|
471
|
-
|
|
472
|
-
function addFromManifest(file) {
|
|
473
|
-
const entries = resolveManifestEntries(options.jsManifest, file);
|
|
474
|
-
if (entries) for (const j of entries) js.add(j);
|
|
475
|
-
}
|
|
476
|
-
if (shellFile) addFromManifest(shellFile);
|
|
477
|
-
addFromManifest(routeFile);
|
|
478
|
-
return [...js];
|
|
722
|
+
return resolvePageUrlsFromManifest(options.jsManifest, shellFile, routeFile);
|
|
479
723
|
}
|
|
480
724
|
async function navigateToClientLocation(location, options) {
|
|
481
725
|
if (typeof window === "undefined") return;
|
|
@@ -561,7 +805,7 @@ function normalizeRouteError(error, options) {
|
|
|
561
805
|
status: 500
|
|
562
806
|
};
|
|
563
807
|
}
|
|
564
|
-
function deserializeRouteError
|
|
808
|
+
function deserializeRouteError(error) {
|
|
565
809
|
const result = new Error(error.message);
|
|
566
810
|
result.name = error.name;
|
|
567
811
|
result.status = error.status;
|
|
@@ -643,7 +887,7 @@ async function renderRouteErrorResponse(options) {
|
|
|
643
887
|
const renderToString = await getRenderToStringAsync();
|
|
644
888
|
const ErrorBoundary = options.routeModule.ErrorBoundary;
|
|
645
889
|
const Shell = shellModule?.Shell;
|
|
646
|
-
const errorValue = deserializeRouteError
|
|
890
|
+
const errorValue = deserializeRouteError(routeErrorWithDiagnostics);
|
|
647
891
|
const componentTree = Shell ? h(Shell, null, h(ErrorBoundary, { error: errorValue })) : h(ErrorBoundary, { error: errorValue });
|
|
648
892
|
return htmlResponse(buildHtmlDocument({
|
|
649
893
|
head,
|
|
@@ -665,8 +909,10 @@ async function renderRouteErrorResponse(options) {
|
|
|
665
909
|
}
|
|
666
910
|
async function runMiddlewareChain(options) {
|
|
667
911
|
let context = options.context;
|
|
668
|
-
|
|
669
|
-
|
|
912
|
+
const modulePromises = options.middlewareFiles.map((mwFile) => resolveRegistryModule(options.registry.middlewareModules, mwFile));
|
|
913
|
+
for (const p of modulePromises) p.catch(() => {});
|
|
914
|
+
for (const modulePromise of modulePromises) {
|
|
915
|
+
const mwModule = await modulePromise;
|
|
670
916
|
if (!mwModule?.middleware) continue;
|
|
671
917
|
const result = await mwModule.middleware({
|
|
672
918
|
request: options.request,
|
|
@@ -711,11 +957,10 @@ async function resolveRegistryModule(modules, file) {
|
|
|
711
957
|
if (resolved) return modules[resolved]();
|
|
712
958
|
}
|
|
713
959
|
async function mergeHeadMetadata(shellModule, routeModule, routeArgs, data) {
|
|
714
|
-
const shellHead = shellModule?.head ?
|
|
715
|
-
const routeHead = routeModule?.head ? await routeModule.head({
|
|
960
|
+
const [shellHead, routeHead] = await Promise.all([shellModule?.head ? shellModule.head(routeArgs) : Promise.resolve({}), routeModule?.head ? routeModule.head({
|
|
716
961
|
...routeArgs,
|
|
717
962
|
data
|
|
718
|
-
}) : {};
|
|
963
|
+
}) : Promise.resolve({})]);
|
|
719
964
|
return {
|
|
720
965
|
title: routeHead.title ?? shellHead.title,
|
|
721
966
|
lang: routeHead.lang ?? shellHead.lang,
|
|
@@ -725,12 +970,11 @@ async function mergeHeadMetadata(shellModule, routeModule, routeArgs, data) {
|
|
|
725
970
|
}
|
|
726
971
|
async function mergeDocumentHeaders(shellModule, routeModule, routeArgs, data) {
|
|
727
972
|
const headers = new Headers();
|
|
728
|
-
const shellHeaders = shellModule?.headers ?
|
|
729
|
-
if (shellHeaders) applyHeaders(headers, shellHeaders);
|
|
730
|
-
const routeHeaders = routeModule?.headers ? await routeModule.headers({
|
|
973
|
+
const [shellHeaders, routeHeaders] = await Promise.all([shellModule?.headers ? shellModule.headers(routeArgs) : Promise.resolve(void 0), routeModule?.headers ? routeModule.headers({
|
|
731
974
|
...routeArgs,
|
|
732
975
|
data
|
|
733
|
-
}) : void 0;
|
|
976
|
+
}) : Promise.resolve(void 0)]);
|
|
977
|
+
if (shellHeaders) applyHeaders(headers, shellHeaders);
|
|
734
978
|
if (routeHeaders) applyHeaders(headers, routeHeaders);
|
|
735
979
|
return headers;
|
|
736
980
|
}
|
|
@@ -740,12 +984,13 @@ function applyHeaders(headers, init) {
|
|
|
740
984
|
});
|
|
741
985
|
}
|
|
742
986
|
function buildHtmlDocument(options) {
|
|
743
|
-
const { head, body, hydrationState, clientEntryUrl, cssUrls = [], modulePreloadUrls = [] } = options;
|
|
987
|
+
const { head, body, hydrationState, clientEntryUrl, cssUrls = [], modulePreloadUrls = [], routeStatePreloadUrl } = options;
|
|
744
988
|
const titleTag = head.title ? `<title>${escapeHtml(head.title)}</title>` : "";
|
|
745
989
|
const metaTags = (head.meta ?? []).map((m) => `<meta ${Object.entries(m).map(([k, v]) => `${k}="${escapeHtml(v)}"`).join(" ")}>`).join("\n ");
|
|
746
990
|
const linkTags = (head.link ?? []).map((l) => `<link ${Object.entries(l).map(([k, v]) => `${k}="${escapeHtml(v)}"`).join(" ")}>`).join("\n ");
|
|
747
991
|
const cssTags = cssUrls.map((url) => `<link rel="stylesheet" href="${escapeHtml(url)}">`).join("\n ");
|
|
748
992
|
const modulePreloadTags = modulePreloadUrls.map((url) => `<link rel="modulepreload" href="${escapeHtml(url)}">`).join("\n ");
|
|
993
|
+
const routeStatePreloadTag = routeStatePreloadUrl ? `<link rel="preload" as="fetch" href="${escapeHtml(routeStatePreloadUrl)}" crossorigin="anonymous">` : "";
|
|
749
994
|
const stateScript = `<script id="${HYDRATION_STATE_ELEMENT_ID}" type="application/json">${serializeJsonForHtml(hydrationState)}<\/script>`;
|
|
750
995
|
const entryScript = clientEntryUrl ? `<script type="module" src="${escapeHtml(clientEntryUrl)}"><\/script>` : "";
|
|
751
996
|
return `<!DOCTYPE html>
|
|
@@ -757,6 +1002,7 @@ function buildHtmlDocument(options) {
|
|
|
757
1002
|
${linkTags}
|
|
758
1003
|
${cssTags}
|
|
759
1004
|
${modulePreloadTags}
|
|
1005
|
+
${routeStatePreloadTag}
|
|
760
1006
|
</head>
|
|
761
1007
|
<body>
|
|
762
1008
|
<div id="pracht-root">${body}</div>
|
|
@@ -824,7 +1070,6 @@ function serializeJsonForHtml(value) {
|
|
|
824
1070
|
return JSON.stringify(value).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
825
1071
|
}
|
|
826
1072
|
async function prerenderApp(options) {
|
|
827
|
-
const { resolveApp } = await import("./app-CUL3q2ZA.mjs").then((n) => n.t);
|
|
828
1073
|
const resolved = resolveApp(options.app);
|
|
829
1074
|
const results = [];
|
|
830
1075
|
const isgManifest = {};
|
|
@@ -886,7 +1131,6 @@ async function collectSSGPaths(route, registry) {
|
|
|
886
1131
|
console.warn(` Warning: SSG route "${route.path}" has dynamic segments but no getStaticPaths() export, skipping.`);
|
|
887
1132
|
return [];
|
|
888
1133
|
}
|
|
889
|
-
const { buildPathFromSegments } = await import("./app-CUL3q2ZA.mjs").then((n) => n.t);
|
|
890
1134
|
return (await routeModule.getStaticPaths()).map((params) => buildPathFromSegments(route.segments, params));
|
|
891
1135
|
}
|
|
892
1136
|
//#endregion
|
|
@@ -939,7 +1183,6 @@ function setupPrefetching(app, warmModules) {
|
|
|
939
1183
|
const match = matchAppRoute(app, routePathname);
|
|
940
1184
|
if (!match) return "none";
|
|
941
1185
|
if (match.route.prefetch) return match.route.prefetch;
|
|
942
|
-
if (match.route.render === "spa") return "none";
|
|
943
1186
|
return "intent";
|
|
944
1187
|
}
|
|
945
1188
|
document.addEventListener("mouseenter", (e) => {
|
|
@@ -1041,7 +1284,30 @@ async function initClientRouter(options) {
|
|
|
1041
1284
|
if (!shellKey) return null;
|
|
1042
1285
|
return loadModule(shellModules, shellKey);
|
|
1043
1286
|
}
|
|
1044
|
-
|
|
1287
|
+
let updateRouteState = null;
|
|
1288
|
+
let routeStateVersion = 0;
|
|
1289
|
+
function RouterRoot({ initialState }) {
|
|
1290
|
+
const [routeState, setRouteState] = useState(initialState);
|
|
1291
|
+
updateRouteState = setRouteState;
|
|
1292
|
+
const navigateValue = useMemo(() => navigate, []);
|
|
1293
|
+
const { Shell, Component, componentProps, data, params, routeId, url, version } = routeState;
|
|
1294
|
+
const componentTree = Shell ? h(Shell, null, h(Component, componentProps)) : h(Component, componentProps);
|
|
1295
|
+
return h(NavigateContext.Provider, { value: navigateValue }, h(PrachtRuntimeProvider, {
|
|
1296
|
+
data,
|
|
1297
|
+
params,
|
|
1298
|
+
routeId,
|
|
1299
|
+
stateVersion: version,
|
|
1300
|
+
url
|
|
1301
|
+
}, componentTree));
|
|
1302
|
+
}
|
|
1303
|
+
function applyRouteState(routeState) {
|
|
1304
|
+
if (updateRouteState) {
|
|
1305
|
+
updateRouteState(routeState);
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1308
|
+
render(h(RouterRoot, { initialState: routeState }), root);
|
|
1309
|
+
}
|
|
1310
|
+
async function resolveRouteState(match, state, currentUrl, routeModPromise, shellModPromise) {
|
|
1045
1311
|
const routeMod = await (routeModPromise ?? startRouteImport(match));
|
|
1046
1312
|
if (!routeMod) return null;
|
|
1047
1313
|
let Shell = null;
|
|
@@ -1050,31 +1316,37 @@ async function initClientRouter(options) {
|
|
|
1050
1316
|
const DefaultComponent = typeof routeMod.default === "function" ? routeMod.default : void 0;
|
|
1051
1317
|
const Component = state.error ? routeMod.ErrorBoundary : routeMod.Component ?? DefaultComponent;
|
|
1052
1318
|
if (!Component) return null;
|
|
1053
|
-
const
|
|
1319
|
+
const componentProps = state.error ? { error: deserializeRouteError(state.error) } : {
|
|
1054
1320
|
data: state.data,
|
|
1055
1321
|
params: match.params
|
|
1056
1322
|
};
|
|
1057
|
-
|
|
1058
|
-
|
|
1323
|
+
return {
|
|
1324
|
+
Shell,
|
|
1325
|
+
Component,
|
|
1326
|
+
componentProps,
|
|
1059
1327
|
data: state.data,
|
|
1060
1328
|
params: match.params,
|
|
1061
1329
|
routeId: match.route.id ?? "",
|
|
1062
|
-
url: currentUrl
|
|
1063
|
-
|
|
1330
|
+
url: currentUrl,
|
|
1331
|
+
version: ++routeStateVersion
|
|
1332
|
+
};
|
|
1064
1333
|
}
|
|
1065
|
-
async function
|
|
1334
|
+
async function resolveSpaPendingState(match, currentUrl, shellModPromise) {
|
|
1066
1335
|
const resolvedShell = await (shellModPromise ?? startShellImport(match));
|
|
1067
1336
|
if (!resolvedShell) return null;
|
|
1068
|
-
const Shell = resolvedShell.Shell;
|
|
1337
|
+
const Shell = resolvedShell.Shell ?? null;
|
|
1069
1338
|
const Loading = resolvedShell.Loading;
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1339
|
+
if (!Shell && !Loading) return null;
|
|
1340
|
+
return {
|
|
1341
|
+
Shell,
|
|
1342
|
+
Component: Loading ?? (() => null),
|
|
1343
|
+
componentProps: {},
|
|
1073
1344
|
data: void 0,
|
|
1074
1345
|
params: match.params,
|
|
1075
1346
|
routeId: match.route.id ?? "",
|
|
1076
|
-
url: currentUrl
|
|
1077
|
-
|
|
1347
|
+
url: currentUrl,
|
|
1348
|
+
version: ++routeStateVersion
|
|
1349
|
+
};
|
|
1078
1350
|
}
|
|
1079
1351
|
function resolveRedirectTarget(location) {
|
|
1080
1352
|
const targetUrl = new URL(location, window.location.href);
|
|
@@ -1151,21 +1423,12 @@ async function initClientRouter(options) {
|
|
|
1151
1423
|
}
|
|
1152
1424
|
if (!opts?._popstate) if (opts?.replace) history.replaceState(null, "", target.browserUrl);
|
|
1153
1425
|
else history.pushState(null, "", target.browserUrl);
|
|
1154
|
-
const
|
|
1155
|
-
if (
|
|
1156
|
-
|
|
1426
|
+
const routeState = await resolveRouteState(match, state, target.requestUrl, routeModPromise, shellModPromise);
|
|
1427
|
+
if (routeState) {
|
|
1428
|
+
applyRouteState(routeState);
|
|
1157
1429
|
window.scrollTo(0, 0);
|
|
1158
1430
|
} else window.location.href = target.browserUrl;
|
|
1159
1431
|
}
|
|
1160
|
-
if (import.meta.env?.DEV) {
|
|
1161
|
-
const prev = options.__m;
|
|
1162
|
-
options.__m = (vnode, s) => {
|
|
1163
|
-
const message = `Hydration mismatch in <${(typeof vnode.type === "function" ? vnode.type.displayName || vnode.type.name : vnode.type) || "Unknown"}>: ${s}`;
|
|
1164
|
-
console.warn(`[pracht] ${message}`);
|
|
1165
|
-
appendHydrationWarning(message);
|
|
1166
|
-
if (prev) prev(vnode, s);
|
|
1167
|
-
};
|
|
1168
|
-
}
|
|
1169
1432
|
const initialTarget = resolveBrowserRouteTarget(options.initialState.url);
|
|
1170
1433
|
const initialRequestUrl = initialTarget?.requestUrl ?? options.initialState.url;
|
|
1171
1434
|
const initialBrowserUrl = initialTarget?.browserUrl ?? options.initialState.url;
|
|
@@ -1177,9 +1440,9 @@ async function initClientRouter(options) {
|
|
|
1177
1440
|
error: options.initialState.error ?? null
|
|
1178
1441
|
};
|
|
1179
1442
|
if (initialMatch.route.render === "spa" && options.initialState.pending) {
|
|
1180
|
-
const dataPromise = fetchPrachtRouteState(initialRequestUrl);
|
|
1181
|
-
const
|
|
1182
|
-
if (
|
|
1443
|
+
const dataPromise = fetchPrachtRouteState(initialRequestUrl, { useDataParam: true });
|
|
1444
|
+
const pendingState = await resolveSpaPendingState(initialMatch, initialRequestUrl, initialShellPromise);
|
|
1445
|
+
if (pendingState) hydrate(h(RouterRoot, { initialState: pendingState }), root);
|
|
1183
1446
|
try {
|
|
1184
1447
|
const result = await dataPromise;
|
|
1185
1448
|
if (result.type === "redirect") {
|
|
@@ -1198,12 +1461,15 @@ async function initClientRouter(options) {
|
|
|
1198
1461
|
window.location.href = initialBrowserUrl;
|
|
1199
1462
|
return;
|
|
1200
1463
|
}
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1464
|
+
const resolvedState = await resolveRouteState(initialMatch, state, initialRequestUrl, void 0, initialShellPromise);
|
|
1465
|
+
if (resolvedState) applyRouteState(resolvedState);
|
|
1466
|
+
} else {
|
|
1467
|
+
const initialRouteState = await resolveRouteState(initialMatch, state, initialRequestUrl, void 0, initialShellPromise);
|
|
1468
|
+
if (initialRouteState) if (initialMatch.route.render === "spa") render(h(RouterRoot, { initialState: initialRouteState }), root);
|
|
1469
|
+
else {
|
|
1470
|
+
markHydrating();
|
|
1471
|
+
hydrate(h(RouterRoot, { initialState: initialRouteState }), root);
|
|
1472
|
+
}
|
|
1207
1473
|
}
|
|
1208
1474
|
}
|
|
1209
1475
|
document.addEventListener("click", (e) => {
|
|
@@ -1238,61 +1504,6 @@ async function initClientRouter(options) {
|
|
|
1238
1504
|
};
|
|
1239
1505
|
setupPrefetching(app, warmModules);
|
|
1240
1506
|
}
|
|
1241
|
-
const HYDRATION_BANNER_ID = "__pracht_hydration_warnings__";
|
|
1242
|
-
function appendHydrationWarning(message) {
|
|
1243
|
-
let container = document.getElementById(HYDRATION_BANNER_ID);
|
|
1244
|
-
if (!container) {
|
|
1245
|
-
container = document.createElement("div");
|
|
1246
|
-
container.id = HYDRATION_BANNER_ID;
|
|
1247
|
-
Object.assign(container.style, {
|
|
1248
|
-
position: "fixed",
|
|
1249
|
-
bottom: "0",
|
|
1250
|
-
left: "0",
|
|
1251
|
-
right: "0",
|
|
1252
|
-
maxHeight: "30vh",
|
|
1253
|
-
overflow: "auto",
|
|
1254
|
-
background: "#2d1b00",
|
|
1255
|
-
borderTop: "2px solid #f0ad4e",
|
|
1256
|
-
color: "#ffc107",
|
|
1257
|
-
fontFamily: "ui-monospace, Consolas, monospace",
|
|
1258
|
-
fontSize: "13px",
|
|
1259
|
-
padding: "12px 16px",
|
|
1260
|
-
zIndex: "2147483647"
|
|
1261
|
-
});
|
|
1262
|
-
const header = document.createElement("div");
|
|
1263
|
-
Object.assign(header.style, {
|
|
1264
|
-
display: "flex",
|
|
1265
|
-
justifyContent: "space-between",
|
|
1266
|
-
alignItems: "center",
|
|
1267
|
-
marginBottom: "8px"
|
|
1268
|
-
});
|
|
1269
|
-
header.innerHTML = "<strong style=\"color:#f0ad4e\">⚠ Hydration Mismatches</strong>";
|
|
1270
|
-
const close = document.createElement("button");
|
|
1271
|
-
close.textContent = "×";
|
|
1272
|
-
Object.assign(close.style, {
|
|
1273
|
-
background: "none",
|
|
1274
|
-
border: "none",
|
|
1275
|
-
color: "#f0ad4e",
|
|
1276
|
-
fontSize: "18px",
|
|
1277
|
-
cursor: "pointer"
|
|
1278
|
-
});
|
|
1279
|
-
close.onclick = () => container.remove();
|
|
1280
|
-
header.appendChild(close);
|
|
1281
|
-
container.appendChild(header);
|
|
1282
|
-
document.body.appendChild(container);
|
|
1283
|
-
}
|
|
1284
|
-
const entry = document.createElement("div");
|
|
1285
|
-
entry.textContent = message;
|
|
1286
|
-
Object.assign(entry.style, { padding: "2px 0" });
|
|
1287
|
-
container.appendChild(entry);
|
|
1288
|
-
}
|
|
1289
|
-
function deserializeRouteError(error) {
|
|
1290
|
-
const result = new Error(error.message);
|
|
1291
|
-
result.name = error.name;
|
|
1292
|
-
result.status = error.status;
|
|
1293
|
-
result.diagnostics = error.diagnostics;
|
|
1294
|
-
return result;
|
|
1295
|
-
}
|
|
1296
1507
|
function resolveBrowserRouteTarget(to) {
|
|
1297
1508
|
if (typeof window === "undefined") return null;
|
|
1298
1509
|
try {
|
|
@@ -1318,4 +1529,4 @@ var PrachtHttpError = class extends Error {
|
|
|
1318
1529
|
}
|
|
1319
1530
|
};
|
|
1320
1531
|
//#endregion
|
|
1321
|
-
export { Form, PrachtHttpError, PrachtRuntimeProvider, Suspense, applyDefaultSecurityHeaders, buildPathFromSegments, defineApp, forwardRef, group, handlePrachtRequest, initClientRouter, lazy, matchApiRoute, matchAppRoute, prerenderApp, readHydrationState, resolveApiRoutes, resolveApp, route, startApp, timeRevalidate, useIsHydrated, useLocation, useNavigate, useParams, useRevalidate,
|
|
1532
|
+
export { Form, PrachtHttpError, PrachtRuntimeProvider, Suspense, applyDefaultSecurityHeaders, buildPathFromSegments, defineApp, forwardRef, group, handlePrachtRequest, initClientRouter, lazy, matchApiRoute, matchAppRoute, prerenderApp, readHydrationState, resolveApiRoutes, resolveApp, route, startApp, timeRevalidate, useIsHydrated, useLocation, useNavigate, useParams, useRevalidate, useRouteData };
|
package/package.json
CHANGED
package/dist/app-CUL3q2ZA.mjs
DELETED
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
//#region \0rolldown/runtime.js
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __exportAll = (all, no_symbols) => {
|
|
4
|
-
let target = {};
|
|
5
|
-
for (var name in all) __defProp(target, name, {
|
|
6
|
-
get: all[name],
|
|
7
|
-
enumerable: true
|
|
8
|
-
});
|
|
9
|
-
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
10
|
-
return target;
|
|
11
|
-
};
|
|
12
|
-
//#endregion
|
|
13
|
-
//#region src/app.ts
|
|
14
|
-
var app_exports = /* @__PURE__ */ __exportAll({
|
|
15
|
-
buildPathFromSegments: () => buildPathFromSegments,
|
|
16
|
-
defineApp: () => defineApp,
|
|
17
|
-
group: () => group,
|
|
18
|
-
matchApiRoute: () => matchApiRoute,
|
|
19
|
-
matchAppRoute: () => matchAppRoute,
|
|
20
|
-
resolveApiRoutes: () => resolveApiRoutes,
|
|
21
|
-
resolveApp: () => resolveApp,
|
|
22
|
-
route: () => route,
|
|
23
|
-
timeRevalidate: () => timeRevalidate
|
|
24
|
-
});
|
|
25
|
-
function timeRevalidate(seconds) {
|
|
26
|
-
if (!Number.isInteger(seconds) || seconds <= 0) throw new Error("timeRevalidate expects a positive integer number of seconds.");
|
|
27
|
-
return {
|
|
28
|
-
kind: "time",
|
|
29
|
-
seconds
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
function route(path, fileOrConfig, meta = {}) {
|
|
33
|
-
if (typeof fileOrConfig === "string" || typeof fileOrConfig === "function") return {
|
|
34
|
-
kind: "route",
|
|
35
|
-
path: normalizeRoutePath(path),
|
|
36
|
-
file: resolveModuleRef(fileOrConfig),
|
|
37
|
-
...meta
|
|
38
|
-
};
|
|
39
|
-
const { component, loader, ...routeMeta } = fileOrConfig;
|
|
40
|
-
return {
|
|
41
|
-
kind: "route",
|
|
42
|
-
path: normalizeRoutePath(path),
|
|
43
|
-
file: resolveModuleRef(component),
|
|
44
|
-
loaderFile: resolveModuleRef(loader),
|
|
45
|
-
...routeMeta
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
function resolveModuleRef(ref) {
|
|
49
|
-
if (ref === void 0) return void 0;
|
|
50
|
-
if (typeof ref === "string") return ref;
|
|
51
|
-
return "";
|
|
52
|
-
}
|
|
53
|
-
function group(meta, routes) {
|
|
54
|
-
return {
|
|
55
|
-
kind: "group",
|
|
56
|
-
meta,
|
|
57
|
-
routes
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
function defineApp(config) {
|
|
61
|
-
return {
|
|
62
|
-
shells: resolveModuleRefRecord(config.shells ?? {}),
|
|
63
|
-
middleware: resolveModuleRefRecord(config.middleware ?? {}),
|
|
64
|
-
api: config.api ?? {},
|
|
65
|
-
routes: config.routes
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
function resolveModuleRefRecord(record) {
|
|
69
|
-
const result = {};
|
|
70
|
-
for (const [key, value] of Object.entries(record)) result[key] = resolveModuleRef(value);
|
|
71
|
-
return result;
|
|
72
|
-
}
|
|
73
|
-
function resolveApp(app) {
|
|
74
|
-
const routes = [];
|
|
75
|
-
const inherited = {
|
|
76
|
-
pathPrefix: "/",
|
|
77
|
-
middleware: []
|
|
78
|
-
};
|
|
79
|
-
for (const node of app.routes) flattenRouteNode(app, node, inherited, routes);
|
|
80
|
-
return {
|
|
81
|
-
shells: app.shells,
|
|
82
|
-
middleware: app.middleware,
|
|
83
|
-
api: app.api,
|
|
84
|
-
routes,
|
|
85
|
-
apiRoutes: []
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
function matchAppRoute(app, pathname) {
|
|
89
|
-
const resolved = isResolvedApp(app) ? app : resolveApp(app);
|
|
90
|
-
const normalizedPathname = normalizeRoutePath(pathname);
|
|
91
|
-
const targetSegments = splitPathSegments(normalizedPathname);
|
|
92
|
-
for (const currentRoute of resolved.routes) {
|
|
93
|
-
const params = matchRouteSegments(currentRoute.segments, targetSegments);
|
|
94
|
-
if (params) return {
|
|
95
|
-
route: currentRoute,
|
|
96
|
-
params,
|
|
97
|
-
pathname: normalizedPathname
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
function flattenRouteNode(app, node, inherited, routes) {
|
|
102
|
-
if (node.kind === "group") {
|
|
103
|
-
const nextInherited = {
|
|
104
|
-
pathPrefix: mergeRoutePaths(inherited.pathPrefix, node.meta.pathPrefix),
|
|
105
|
-
shell: node.meta.shell ?? inherited.shell,
|
|
106
|
-
render: node.meta.render ?? inherited.render,
|
|
107
|
-
middleware: [...inherited.middleware, ...node.meta.middleware ?? []]
|
|
108
|
-
};
|
|
109
|
-
for (const child of node.routes) flattenRouteNode(app, child, nextInherited, routes);
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
const fullPath = mergeRoutePaths(inherited.pathPrefix, node.path);
|
|
113
|
-
const shell = node.shell ?? inherited.shell;
|
|
114
|
-
const middleware = [...inherited.middleware, ...node.middleware ?? []];
|
|
115
|
-
routes.push({
|
|
116
|
-
id: node.id ?? createRouteId(fullPath),
|
|
117
|
-
path: fullPath,
|
|
118
|
-
file: node.file,
|
|
119
|
-
loaderFile: node.loaderFile,
|
|
120
|
-
shell,
|
|
121
|
-
shellFile: shell ? app.shells[shell] : void 0,
|
|
122
|
-
render: node.render ?? inherited.render,
|
|
123
|
-
middleware,
|
|
124
|
-
middlewareFiles: middleware.flatMap((name) => {
|
|
125
|
-
const middlewareFile = app.middleware[name];
|
|
126
|
-
return middlewareFile ? [middlewareFile] : [];
|
|
127
|
-
}),
|
|
128
|
-
revalidate: node.revalidate,
|
|
129
|
-
segments: parseRouteSegments(fullPath)
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
function isResolvedApp(app) {
|
|
133
|
-
return app.routes.length === 0 || "segments" in app.routes[0];
|
|
134
|
-
}
|
|
135
|
-
function matchRouteSegments(routeSegments, targetSegments) {
|
|
136
|
-
const params = {};
|
|
137
|
-
let routeIndex = 0;
|
|
138
|
-
let targetIndex = 0;
|
|
139
|
-
while (routeIndex < routeSegments.length) {
|
|
140
|
-
const currentSegment = routeSegments[routeIndex];
|
|
141
|
-
if (currentSegment.type === "catchall") {
|
|
142
|
-
params[currentSegment.name] = targetSegments.slice(targetIndex).join("/");
|
|
143
|
-
return params;
|
|
144
|
-
}
|
|
145
|
-
const targetSegment = targetSegments[targetIndex];
|
|
146
|
-
if (typeof targetSegment === "undefined") return null;
|
|
147
|
-
if (currentSegment.type === "static") {
|
|
148
|
-
if (currentSegment.value !== targetSegment) return null;
|
|
149
|
-
} else try {
|
|
150
|
-
params[currentSegment.name] = decodeURIComponent(targetSegment);
|
|
151
|
-
} catch {
|
|
152
|
-
return null;
|
|
153
|
-
}
|
|
154
|
-
routeIndex += 1;
|
|
155
|
-
targetIndex += 1;
|
|
156
|
-
}
|
|
157
|
-
return targetIndex === targetSegments.length ? params : null;
|
|
158
|
-
}
|
|
159
|
-
function parseRouteSegments(path) {
|
|
160
|
-
return splitPathSegments(path).map((segment) => {
|
|
161
|
-
if (segment === "*") return {
|
|
162
|
-
type: "catchall",
|
|
163
|
-
name: "*"
|
|
164
|
-
};
|
|
165
|
-
if (segment.startsWith(":")) return {
|
|
166
|
-
type: "param",
|
|
167
|
-
name: segment.slice(1)
|
|
168
|
-
};
|
|
169
|
-
return {
|
|
170
|
-
type: "static",
|
|
171
|
-
value: segment
|
|
172
|
-
};
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
function splitPathSegments(path) {
|
|
176
|
-
return normalizeRoutePath(path).split("/").filter(Boolean);
|
|
177
|
-
}
|
|
178
|
-
function mergeRoutePaths(prefix, path) {
|
|
179
|
-
if (!path) return normalizeRoutePath(prefix);
|
|
180
|
-
const normalizedPrefix = normalizeRoutePath(prefix);
|
|
181
|
-
const normalizedPath = normalizeRoutePath(path);
|
|
182
|
-
if (normalizedPrefix === "/") return normalizedPath;
|
|
183
|
-
if (normalizedPath === "/") return normalizedPrefix;
|
|
184
|
-
return normalizeRoutePath(`${normalizedPrefix}/${normalizedPath.slice(1)}`);
|
|
185
|
-
}
|
|
186
|
-
function normalizeRoutePath(path) {
|
|
187
|
-
if (!path || path === "/") return "/";
|
|
188
|
-
const collapsed = (path.startsWith("/") ? path : `/${path}`).replace(/\/{2,}/g, "/");
|
|
189
|
-
return collapsed.length > 1 && collapsed.endsWith("/") ? collapsed.slice(0, -1) : collapsed;
|
|
190
|
-
}
|
|
191
|
-
function buildPathFromSegments(segments, params) {
|
|
192
|
-
return normalizeRoutePath("/" + segments.map((segment) => {
|
|
193
|
-
if (segment.type === "static") return segment.value;
|
|
194
|
-
if (segment.type === "param") return encodeURIComponent(params[segment.name] ?? "");
|
|
195
|
-
return params["*"] ?? "";
|
|
196
|
-
}).join("/"));
|
|
197
|
-
}
|
|
198
|
-
/**
|
|
199
|
-
* Convert a list of file paths from `import.meta.glob` into resolved API routes.
|
|
200
|
-
*
|
|
201
|
-
* Example: `"/src/api/health.ts"` → path `/api/health`
|
|
202
|
-
* `"/src/api/users/[id].ts"` → path `/api/users/:id`
|
|
203
|
-
* `"/src/api/index.ts"` → path `/api`
|
|
204
|
-
*/
|
|
205
|
-
function resolveApiRoutes(files, apiDir = "/src/api") {
|
|
206
|
-
const normalizedDir = apiDir.replace(/\/$/, "");
|
|
207
|
-
return files.map((file) => {
|
|
208
|
-
let relative = file;
|
|
209
|
-
if (relative.startsWith(normalizedDir)) relative = relative.slice(normalizedDir.length);
|
|
210
|
-
relative = relative.replace(/\.(ts|tsx|js|jsx)$/, "");
|
|
211
|
-
if (relative.endsWith("/index")) relative = relative.slice(0, -6) || "/";
|
|
212
|
-
relative = relative.replace(/\[([^\]]+)\]/g, ":$1");
|
|
213
|
-
const path = normalizeRoutePath(`/api${relative}`);
|
|
214
|
-
return {
|
|
215
|
-
path,
|
|
216
|
-
file,
|
|
217
|
-
segments: parseRouteSegments(path)
|
|
218
|
-
};
|
|
219
|
-
}).sort(compareResolvedApiRoutes);
|
|
220
|
-
}
|
|
221
|
-
function matchApiRoute(apiRoutes, pathname) {
|
|
222
|
-
const normalizedPathname = normalizeRoutePath(pathname);
|
|
223
|
-
const targetSegments = splitPathSegments(normalizedPathname);
|
|
224
|
-
for (const route of apiRoutes) {
|
|
225
|
-
const params = matchRouteSegments(route.segments, targetSegments);
|
|
226
|
-
if (params) return {
|
|
227
|
-
route,
|
|
228
|
-
params,
|
|
229
|
-
pathname: normalizedPathname
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
function createRouteId(path) {
|
|
234
|
-
if (path === "/") return "index";
|
|
235
|
-
return path.slice(1).split("/").map((segment) => {
|
|
236
|
-
if (segment === "*") return "splat";
|
|
237
|
-
return segment.startsWith(":") ? segment.slice(1) : segment;
|
|
238
|
-
}).join("-").replace(/[^a-zA-Z0-9-]/g, "-");
|
|
239
|
-
}
|
|
240
|
-
function compareResolvedApiRoutes(left, right) {
|
|
241
|
-
const length = Math.max(left.segments.length, right.segments.length);
|
|
242
|
-
for (let index = 0; index < length; index += 1) {
|
|
243
|
-
const leftSegment = left.segments[index];
|
|
244
|
-
const rightSegment = right.segments[index];
|
|
245
|
-
if (!leftSegment) return 1;
|
|
246
|
-
if (!rightSegment) return -1;
|
|
247
|
-
const leftScore = getRouteSegmentSpecificity(leftSegment);
|
|
248
|
-
const rightScore = getRouteSegmentSpecificity(rightSegment);
|
|
249
|
-
if (leftScore !== rightScore) return rightScore - leftScore;
|
|
250
|
-
}
|
|
251
|
-
return left.path.localeCompare(right.path);
|
|
252
|
-
}
|
|
253
|
-
function getRouteSegmentSpecificity(segment) {
|
|
254
|
-
if (segment.type === "static") return 3;
|
|
255
|
-
if (segment.type === "param") return 2;
|
|
256
|
-
return 1;
|
|
257
|
-
}
|
|
258
|
-
//#endregion
|
|
259
|
-
export { matchApiRoute as a, resolveApp as c, group as i, route as l, buildPathFromSegments as n, matchAppRoute as o, defineApp as r, resolveApiRoutes as s, app_exports as t, timeRevalidate as u };
|