@pracht/core 0.2.5 → 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/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-Cep0el7c.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 = {}, routeId, url }) {
91
- const [routeData, setRouteData] = useState(data);
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
- setRouteData(data);
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: setRouteData,
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,19 +372,15 @@ function readHydrationState() {
125
372
  if (!(element instanceof HTMLScriptElement)) return;
126
373
  const raw = element.textContent;
127
374
  if (!raw) return;
128
- try {
129
- const state = JSON.parse(raw);
130
- window.__PRACHT_STATE__ = state;
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;
138
381
  }
139
382
  function useLocation() {
140
- return { pathname: useContext(RouteDataContext)?.url ?? (typeof window !== "undefined" ? window.location.pathname : "/") };
383
+ return parseLocation(useContext(RouteDataContext)?.url ?? (typeof window !== "undefined" ? window.location.pathname + window.location.search : "/"));
141
384
  }
142
385
  function useParams() {
143
386
  return useContext(RouteDataContext)?.params ?? {};
@@ -151,13 +394,11 @@ function useRevalidate() {
151
394
  await navigateToClientLocation(result.location);
152
395
  return;
153
396
  }
154
- if (result.type === "error") throw deserializeRouteError$1(result.error);
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 response = await fetch(url, {
192
- headers: {
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
  },
@@ -200,6 +442,10 @@ async function fetchPrachtRouteState(url) {
200
442
  type: "redirect"
201
443
  };
202
444
  const json = await response.json();
445
+ if (json.redirect) return {
446
+ location: json.redirect,
447
+ type: "redirect"
448
+ };
203
449
  if (!response.ok) {
204
450
  if (json.error) return {
205
451
  error: json.error,
@@ -212,10 +458,16 @@ async function fetchPrachtRouteState(url) {
212
458
  type: "data"
213
459
  };
214
460
  }
461
+ function buildRouteStateUrl(url) {
462
+ return `${url}${url.includes("?") ? "&" : "?"}_data=1`;
463
+ }
215
464
  async function handlePrachtRequest(options) {
216
465
  const url = new URL(options.request.url);
466
+ const hasDataParam = url.searchParams.get("_data") === "1";
467
+ if (hasDataParam) url.searchParams.delete("_data");
468
+ const requestPath = getRequestPath(url);
217
469
  const registry = options.registry ?? {};
218
- const isRouteStateRequest = options.request.headers.get(ROUTE_STATE_REQUEST_HEADER) === "1";
470
+ const isRouteStateRequest = options.request.headers.get(ROUTE_STATE_REQUEST_HEADER) === "1" || hasDataParam;
219
471
  const exposeDiagnostics = shouldExposeServerErrors(options);
220
472
  if (options.apiRoutes?.length) {
221
473
  const apiMatch = matchApiRoute(options.apiRoutes, url.pathname);
@@ -306,7 +558,7 @@ async function handlePrachtRequest(options) {
306
558
  let loaderFile;
307
559
  let currentPhase = "middleware";
308
560
  try {
309
- const middlewareResult = await runMiddlewareChain({
561
+ const middlewarePromise = runMiddlewareChain({
310
562
  context: routeArgs.context,
311
563
  middlewareFiles: match.route.middlewareFiles,
312
564
  params: match.params,
@@ -315,25 +567,31 @@ async function handlePrachtRequest(options) {
315
567
  route: match.route,
316
568
  url
317
569
  });
318
- if (middlewareResult.response) return withRouteResponseHeaders(middlewareResult.response, { isRouteStateRequest });
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;
577
+ if (middlewareResult.response) return normalizePageResponse(middlewareResult.response, { isRouteStateRequest });
319
578
  routeArgs = {
320
579
  ...routeArgs,
321
580
  context: middlewareResult.context
322
581
  };
323
582
  currentPhase = "render";
324
- routeModule = await resolveRegistryModule(registry.routeModules, match.route.file);
583
+ routeModule = await routeModulePromise;
325
584
  if (!routeModule) throw new Error("Route module not found");
326
585
  currentPhase = "loader";
327
- const { loader, loaderFile: resolvedLoaderFile } = await resolveDataFunctions(match.route, routeModule, registry);
586
+ const { loader, loaderFile: resolvedLoaderFile } = await dataFunctionsPromise;
328
587
  loaderFile = resolvedLoaderFile;
329
588
  const loaderResult = loader ? await loader(routeArgs) : void 0;
330
- if (loaderResult instanceof Response) return withRouteResponseHeaders(loaderResult, { isRouteStateRequest });
589
+ if (loaderResult instanceof Response) return normalizePageResponse(loaderResult, { isRouteStateRequest });
331
590
  const data = loaderResult;
332
591
  if (isRouteStateRequest) return withRouteResponseHeaders(Response.json({ data }), { isRouteStateRequest: true });
333
592
  currentPhase = "render";
334
- shellModule = match.route.shellFile ? await resolveRegistryModule(registry.shellModules, match.route.shellFile) : void 0;
335
- const head = await mergeHeadMetadata(shellModule, routeModule, routeArgs, data);
336
- 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)]);
337
595
  const cssUrls = resolvePageCssUrls(options, match.route.shellFile, match.route.file);
338
596
  const modulePreloadUrls = resolvePageJsUrls(options, match.route.shellFile, match.route.file);
339
597
  if (match.route.render === "spa") {
@@ -348,7 +606,7 @@ async function handlePrachtRequest(options) {
348
606
  head,
349
607
  body,
350
608
  hydrationState: {
351
- url: url.pathname,
609
+ url: requestPath,
352
610
  routeId: match.route.id ?? "",
353
611
  data: null,
354
612
  error: null,
@@ -356,29 +614,31 @@ async function handlePrachtRequest(options) {
356
614
  },
357
615
  clientEntryUrl: options.clientEntryUrl,
358
616
  cssUrls,
359
- modulePreloadUrls
617
+ modulePreloadUrls,
618
+ routeStatePreloadUrl: loader ? buildRouteStateUrl(requestPath) : void 0
360
619
  }), 200, documentHeaders);
361
620
  }
362
621
  const DefaultComponent = typeof routeModule.default === "function" ? routeModule.default : void 0;
363
622
  const Component = routeModule.Component ?? DefaultComponent;
364
623
  if (!Component) throw new Error("Route has no Component or default export");
365
624
  const Shell = shellModule?.Shell;
625
+ const Comp = Component;
366
626
  const componentProps = {
367
627
  data,
368
628
  params: match.params
369
629
  };
370
- const componentTree = Shell ? h(Shell, null, h(Component, componentProps)) : h(Component, componentProps);
630
+ const componentTree = Shell ? h(Shell, null, h(Comp, componentProps)) : h(Comp, componentProps);
371
631
  const tree = h(PrachtRuntimeProvider, {
372
632
  data,
373
633
  params: match.params,
374
634
  routeId: match.route.id ?? "",
375
- url: url.pathname
635
+ url: requestPath
376
636
  }, componentTree);
377
637
  return htmlResponse(buildHtmlDocument({
378
638
  head,
379
639
  body: await (await getRenderToStringAsync())(tree),
380
640
  hydrationState: {
381
- url: url.pathname,
641
+ url: requestPath,
382
642
  routeId: match.route.id ?? "",
383
643
  data,
384
644
  error: null
@@ -399,10 +659,20 @@ async function handlePrachtRequest(options) {
399
659
  routeModule,
400
660
  shellFile: match.route.shellFile,
401
661
  shellModule,
402
- urlPathname: url.pathname
662
+ requestPath
403
663
  });
404
664
  }
405
665
  }
666
+ function parseLocation(value) {
667
+ const url = new URL(value, "http://pracht.local");
668
+ return {
669
+ pathname: url.pathname,
670
+ search: url.search
671
+ };
672
+ }
673
+ function getRequestPath(url) {
674
+ return `${url.pathname}${url.search}`;
675
+ }
406
676
  /** Strip leading `./` and `/` so all module paths share one canonical form. */
407
677
  function normalizeModulePath(path) {
408
678
  return path.replace(/^\.?\//, "");
@@ -433,27 +703,23 @@ function resolveManifestEntries(manifest, file) {
433
703
  const resolved = getSuffixIndex(manifest).get(normalizeModulePath(file));
434
704
  if (resolved) return manifest[resolved];
435
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
+ }
436
716
  function resolvePageCssUrls(options, shellFile, routeFile) {
437
- if (!options.cssManifest) return options.cssUrls ?? [];
438
- const css = /* @__PURE__ */ new Set();
439
- function addFromManifest(file) {
440
- const entries = resolveManifestEntries(options.cssManifest, file);
441
- if (entries) for (const c of entries) css.add(c);
442
- }
443
- if (shellFile) addFromManifest(shellFile);
444
- addFromManifest(routeFile);
445
- return [...css];
717
+ if (!options.cssManifest) return [];
718
+ return resolvePageUrlsFromManifest(options.cssManifest, shellFile, routeFile);
446
719
  }
447
720
  function resolvePageJsUrls(options, shellFile, routeFile) {
448
721
  if (!options.jsManifest) return [];
449
- const js = /* @__PURE__ */ new Set();
450
- function addFromManifest(file) {
451
- const entries = resolveManifestEntries(options.jsManifest, file);
452
- if (entries) for (const j of entries) js.add(j);
453
- }
454
- if (shellFile) addFromManifest(shellFile);
455
- addFromManifest(routeFile);
456
- return [...js];
722
+ return resolvePageUrlsFromManifest(options.jsManifest, shellFile, routeFile);
457
723
  }
458
724
  async function navigateToClientLocation(location, options) {
459
725
  if (typeof window === "undefined") return;
@@ -539,7 +805,7 @@ function normalizeRouteError(error, options) {
539
805
  status: 500
540
806
  };
541
807
  }
542
- function deserializeRouteError$1(error) {
808
+ function deserializeRouteError(error) {
543
809
  const result = new Error(error.message);
544
810
  result.name = error.name;
545
811
  result.status = error.status;
@@ -553,6 +819,24 @@ function jsonErrorResponse(routeError, options) {
553
819
  headers
554
820
  });
555
821
  }
822
+ function jsonRedirectResponse(location, options) {
823
+ const headers = new Headers(options.headers);
824
+ headers.set("content-type", "application/json; charset=utf-8");
825
+ return withRouteResponseHeaders(new Response(JSON.stringify({ redirect: location }), {
826
+ status: 200,
827
+ headers
828
+ }), { isRouteStateRequest: options.isRouteStateRequest });
829
+ }
830
+ function normalizePageResponse(response, options) {
831
+ if (options.isRouteStateRequest && response.status >= 300 && response.status < 400) {
832
+ const location = response.headers.get("location");
833
+ if (location) return jsonRedirectResponse(location, {
834
+ headers: response.headers,
835
+ isRouteStateRequest: true
836
+ });
837
+ }
838
+ return withRouteResponseHeaders(response, options);
839
+ }
556
840
  function renderApiErrorResponse(options) {
557
841
  const exposeDetails = shouldExposeServerErrors(options.options);
558
842
  const routeError = normalizeRouteError(options.error, { exposeDetails });
@@ -603,17 +887,17 @@ async function renderRouteErrorResponse(options) {
603
887
  const renderToString = await getRenderToStringAsync();
604
888
  const ErrorBoundary = options.routeModule.ErrorBoundary;
605
889
  const Shell = shellModule?.Shell;
606
- const errorValue = deserializeRouteError$1(routeErrorWithDiagnostics);
890
+ const errorValue = deserializeRouteError(routeErrorWithDiagnostics);
607
891
  const componentTree = Shell ? h(Shell, null, h(ErrorBoundary, { error: errorValue })) : h(ErrorBoundary, { error: errorValue });
608
892
  return htmlResponse(buildHtmlDocument({
609
893
  head,
610
894
  body: await renderToString(h(PrachtRuntimeProvider, {
611
895
  data: null,
612
896
  routeId: options.routeId,
613
- url: options.urlPathname
897
+ url: options.requestPath
614
898
  }, componentTree)),
615
899
  hydrationState: {
616
- url: options.urlPathname,
900
+ url: options.requestPath,
617
901
  routeId: options.routeId,
618
902
  data: null,
619
903
  error: routeErrorWithDiagnostics
@@ -625,8 +909,10 @@ async function renderRouteErrorResponse(options) {
625
909
  }
626
910
  async function runMiddlewareChain(options) {
627
911
  let context = options.context;
628
- for (const mwFile of options.middlewareFiles) {
629
- const mwModule = await resolveRegistryModule(options.registry.middlewareModules, mwFile);
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;
630
916
  if (!mwModule?.middleware) continue;
631
917
  const result = await mwModule.middleware({
632
918
  request: options.request,
@@ -671,11 +957,10 @@ async function resolveRegistryModule(modules, file) {
671
957
  if (resolved) return modules[resolved]();
672
958
  }
673
959
  async function mergeHeadMetadata(shellModule, routeModule, routeArgs, data) {
674
- const shellHead = shellModule?.head ? await shellModule.head(routeArgs) : {};
675
- 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({
676
961
  ...routeArgs,
677
962
  data
678
- }) : {};
963
+ }) : Promise.resolve({})]);
679
964
  return {
680
965
  title: routeHead.title ?? shellHead.title,
681
966
  lang: routeHead.lang ?? shellHead.lang,
@@ -685,12 +970,11 @@ async function mergeHeadMetadata(shellModule, routeModule, routeArgs, data) {
685
970
  }
686
971
  async function mergeDocumentHeaders(shellModule, routeModule, routeArgs, data) {
687
972
  const headers = new Headers();
688
- const shellHeaders = shellModule?.headers ? await shellModule.headers(routeArgs) : void 0;
689
- if (shellHeaders) applyHeaders(headers, shellHeaders);
690
- 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({
691
974
  ...routeArgs,
692
975
  data
693
- }) : void 0;
976
+ }) : Promise.resolve(void 0)]);
977
+ if (shellHeaders) applyHeaders(headers, shellHeaders);
694
978
  if (routeHeaders) applyHeaders(headers, routeHeaders);
695
979
  return headers;
696
980
  }
@@ -700,12 +984,13 @@ function applyHeaders(headers, init) {
700
984
  });
701
985
  }
702
986
  function buildHtmlDocument(options) {
703
- const { head, body, hydrationState, clientEntryUrl, cssUrls = [], modulePreloadUrls = [] } = options;
987
+ const { head, body, hydrationState, clientEntryUrl, cssUrls = [], modulePreloadUrls = [], routeStatePreloadUrl } = options;
704
988
  const titleTag = head.title ? `<title>${escapeHtml(head.title)}</title>` : "";
705
989
  const metaTags = (head.meta ?? []).map((m) => `<meta ${Object.entries(m).map(([k, v]) => `${k}="${escapeHtml(v)}"`).join(" ")}>`).join("\n ");
706
990
  const linkTags = (head.link ?? []).map((l) => `<link ${Object.entries(l).map(([k, v]) => `${k}="${escapeHtml(v)}"`).join(" ")}>`).join("\n ");
707
991
  const cssTags = cssUrls.map((url) => `<link rel="stylesheet" href="${escapeHtml(url)}">`).join("\n ");
708
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">` : "";
709
994
  const stateScript = `<script id="${HYDRATION_STATE_ELEMENT_ID}" type="application/json">${serializeJsonForHtml(hydrationState)}<\/script>`;
710
995
  const entryScript = clientEntryUrl ? `<script type="module" src="${escapeHtml(clientEntryUrl)}"><\/script>` : "";
711
996
  return `<!DOCTYPE html>
@@ -717,6 +1002,7 @@ function buildHtmlDocument(options) {
717
1002
  ${linkTags}
718
1003
  ${cssTags}
719
1004
  ${modulePreloadTags}
1005
+ ${routeStatePreloadTag}
720
1006
  </head>
721
1007
  <body>
722
1008
  <div id="pracht-root">${body}</div>
@@ -784,7 +1070,6 @@ function serializeJsonForHtml(value) {
784
1070
  return JSON.stringify(value).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
785
1071
  }
786
1072
  async function prerenderApp(options) {
787
- const { resolveApp } = await import("./app-Cep0el7c.mjs").then((n) => n.t);
788
1073
  const resolved = resolveApp(options.app);
789
1074
  const results = [];
790
1075
  const isgManifest = {};
@@ -846,7 +1131,6 @@ async function collectSSGPaths(route, registry) {
846
1131
  console.warn(` Warning: SSG route "${route.path}" has dynamic segments but no getStaticPaths() export, skipping.`);
847
1132
  return [];
848
1133
  }
849
- const { buildPathFromSegments } = await import("./app-Cep0el7c.mjs").then((n) => n.t);
850
1134
  return (await routeModule.getStaticPaths()).map((params) => buildPathFromSegments(route.segments, params));
851
1135
  }
852
1136
  //#endregion
@@ -874,6 +1158,13 @@ function prefetchRouteState(url) {
874
1158
  }
875
1159
  function setupPrefetching(app, warmModules) {
876
1160
  let hoverTimer = null;
1161
+ function getRoutePathname(url) {
1162
+ try {
1163
+ return new URL(url, window.location.origin).pathname;
1164
+ } catch {
1165
+ return null;
1166
+ }
1167
+ }
877
1168
  function getInternalHref(anchor) {
878
1169
  const href = anchor.getAttribute("href");
879
1170
  if (!href || href.startsWith("#")) return null;
@@ -887,10 +1178,11 @@ function setupPrefetching(app, warmModules) {
887
1178
  return url.pathname + url.search;
888
1179
  }
889
1180
  function getPrefetchStrategy(pathname) {
890
- const match = matchAppRoute(app, pathname);
1181
+ const routePathname = getRoutePathname(pathname);
1182
+ if (!routePathname) return "none";
1183
+ const match = matchAppRoute(app, routePathname);
891
1184
  if (!match) return "none";
892
1185
  if (match.route.prefetch) return match.route.prefetch;
893
- if (match.route.render === "spa") return "none";
894
1186
  return "intent";
895
1187
  }
896
1188
  document.addEventListener("mouseenter", (e) => {
@@ -904,7 +1196,8 @@ function setupPrefetching(app, warmModules) {
904
1196
  hoverTimer = setTimeout(() => {
905
1197
  prefetchRouteState(href);
906
1198
  if (warmModules) {
907
- const m = matchAppRoute(app, href);
1199
+ const pathname = getRoutePathname(href);
1200
+ const m = pathname ? matchAppRoute(app, pathname) : void 0;
908
1201
  if (m) warmModules(m);
909
1202
  }
910
1203
  }, 50);
@@ -925,7 +1218,8 @@ function setupPrefetching(app, warmModules) {
925
1218
  if (strategy !== "hover" && strategy !== "intent") return;
926
1219
  prefetchRouteState(href);
927
1220
  if (warmModules) {
928
- const m = matchAppRoute(app, href);
1221
+ const pathname = getRoutePathname(href);
1222
+ const m = pathname ? matchAppRoute(app, pathname) : void 0;
929
1223
  if (m) warmModules(m);
930
1224
  }
931
1225
  }, true);
@@ -938,7 +1232,8 @@ function setupPrefetching(app, warmModules) {
938
1232
  if (!href) continue;
939
1233
  prefetchRouteState(href);
940
1234
  if (warmModules) {
941
- const m = matchAppRoute(app, href);
1235
+ const pathname = getRoutePathname(href);
1236
+ const m = pathname ? matchAppRoute(app, pathname) : void 0;
942
1237
  if (m) warmModules(m);
943
1238
  }
944
1239
  observer.unobserve(anchor);
@@ -989,7 +1284,30 @@ async function initClientRouter(options) {
989
1284
  if (!shellKey) return null;
990
1285
  return loadModule(shellModules, shellKey);
991
1286
  }
992
- async function buildRouteTree(match, state, routeModPromise, shellModPromise) {
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) {
993
1311
  const routeMod = await (routeModPromise ?? startRouteImport(match));
994
1312
  if (!routeMod) return null;
995
1313
  let Shell = null;
@@ -998,39 +1316,69 @@ async function initClientRouter(options) {
998
1316
  const DefaultComponent = typeof routeMod.default === "function" ? routeMod.default : void 0;
999
1317
  const Component = state.error ? routeMod.ErrorBoundary : routeMod.Component ?? DefaultComponent;
1000
1318
  if (!Component) return null;
1001
- const props = state.error ? { error: deserializeRouteError(state.error) } : {
1319
+ const componentProps = state.error ? { error: deserializeRouteError(state.error) } : {
1002
1320
  data: state.data,
1003
1321
  params: match.params
1004
1322
  };
1005
- const componentTree = Shell ? h(Shell, null, h(Component, props)) : h(Component, props);
1006
- return h(NavigateContext.Provider, { value: navigate }, h(PrachtRuntimeProvider, {
1323
+ return {
1324
+ Shell,
1325
+ Component,
1326
+ componentProps,
1007
1327
  data: state.data,
1008
1328
  params: match.params,
1009
1329
  routeId: match.route.id ?? "",
1010
- url: match.pathname
1011
- }, componentTree));
1330
+ url: currentUrl,
1331
+ version: ++routeStateVersion
1332
+ };
1012
1333
  }
1013
- async function buildSpaPendingTree(match, shellModPromise) {
1334
+ async function resolveSpaPendingState(match, currentUrl, shellModPromise) {
1014
1335
  const resolvedShell = await (shellModPromise ?? startShellImport(match));
1015
1336
  if (!resolvedShell) return null;
1016
- const Shell = resolvedShell.Shell;
1337
+ const Shell = resolvedShell.Shell ?? null;
1017
1338
  const Loading = resolvedShell.Loading;
1018
- const componentTree = Shell != null ? h(Shell, null, Loading ? h(Loading, null) : null) : Loading ? h(Loading, null) : null;
1019
- if (!componentTree) return null;
1020
- return h(NavigateContext.Provider, { value: navigate }, h(PrachtRuntimeProvider, {
1339
+ if (!Shell && !Loading) return null;
1340
+ return {
1341
+ Shell,
1342
+ Component: Loading ?? (() => null),
1343
+ componentProps: {},
1021
1344
  data: void 0,
1022
1345
  params: match.params,
1023
1346
  routeId: match.route.id ?? "",
1024
- url: match.pathname
1025
- }, componentTree));
1347
+ url: currentUrl,
1348
+ version: ++routeStateVersion
1349
+ };
1350
+ }
1351
+ function resolveRedirectTarget(location) {
1352
+ const targetUrl = new URL(location, window.location.href);
1353
+ const fullInternalTarget = targetUrl.pathname + targetUrl.search + targetUrl.hash;
1354
+ const internalPath = targetUrl.pathname + targetUrl.search;
1355
+ const currentPath = window.location.pathname + window.location.search + window.location.hash;
1356
+ const isCurrentLocation = targetUrl.origin === window.location.origin && fullInternalTarget === currentPath;
1357
+ if (targetUrl.origin !== window.location.origin) return {
1358
+ externalUrl: targetUrl.toString(),
1359
+ isCurrentLocation: false
1360
+ };
1361
+ if (targetUrl.hash) return {
1362
+ documentUrl: targetUrl.toString(),
1363
+ isCurrentLocation
1364
+ };
1365
+ return {
1366
+ internalPath,
1367
+ isCurrentLocation
1368
+ };
1026
1369
  }
1027
1370
  async function navigate(to, opts) {
1028
- const match = matchAppRoute(app, to);
1029
- if (!match) {
1371
+ const target = resolveBrowserRouteTarget(to);
1372
+ if (!target) {
1030
1373
  window.location.href = to;
1031
1374
  return;
1032
1375
  }
1033
- const statePromise = getCachedRouteState(to) ?? fetchPrachtRouteState(to);
1376
+ const match = matchAppRoute(app, target.pathname);
1377
+ if (!match) {
1378
+ window.location.href = target.browserUrl;
1379
+ return;
1380
+ }
1381
+ const statePromise = getCachedRouteState(target.requestUrl) ?? fetchPrachtRouteState(target.requestUrl);
1034
1382
  const routeModPromise = startRouteImport(match);
1035
1383
  const shellModPromise = startShellImport(match);
1036
1384
  let state = {
@@ -1041,10 +1389,24 @@ async function initClientRouter(options) {
1041
1389
  const result = await statePromise;
1042
1390
  if (result.type === "redirect") {
1043
1391
  if (result.location) {
1044
- await navigate(result.location, opts);
1392
+ const redirect = resolveRedirectTarget(result.location);
1393
+ if (redirect.externalUrl) {
1394
+ window.location.href = redirect.externalUrl;
1395
+ return;
1396
+ }
1397
+ if (redirect.isCurrentLocation) return;
1398
+ if (redirect.documentUrl) {
1399
+ window.location.href = redirect.documentUrl;
1400
+ return;
1401
+ }
1402
+ if (redirect.internalPath) {
1403
+ await navigate(redirect.internalPath, opts);
1404
+ return;
1405
+ }
1406
+ window.location.href = result.location;
1045
1407
  return;
1046
1408
  }
1047
- window.location.href = to;
1409
+ window.location.href = target.browserUrl;
1048
1410
  return;
1049
1411
  }
1050
1412
  if (result.type === "error") state = {
@@ -1056,27 +1418,21 @@ async function initClientRouter(options) {
1056
1418
  error: null
1057
1419
  };
1058
1420
  } catch {
1059
- window.location.href = to;
1421
+ window.location.href = target.browserUrl;
1060
1422
  return;
1061
1423
  }
1062
- if (!opts?._popstate) if (opts?.replace) history.replaceState(null, "", to);
1063
- else history.pushState(null, "", to);
1064
- const tree = await buildRouteTree(match, state, routeModPromise, shellModPromise);
1065
- if (tree) {
1066
- render(tree, root);
1424
+ if (!opts?._popstate) if (opts?.replace) history.replaceState(null, "", target.browserUrl);
1425
+ else history.pushState(null, "", target.browserUrl);
1426
+ const routeState = await resolveRouteState(match, state, target.requestUrl, routeModPromise, shellModPromise);
1427
+ if (routeState) {
1428
+ applyRouteState(routeState);
1067
1429
  window.scrollTo(0, 0);
1068
- } else window.location.href = to;
1069
- }
1070
- if (import.meta.env?.DEV) {
1071
- const prev = options.__m;
1072
- options.__m = (vnode, s) => {
1073
- const message = `Hydration mismatch in <${(typeof vnode.type === "function" ? vnode.type.displayName || vnode.type.name : vnode.type) || "Unknown"}>: ${s}`;
1074
- console.warn(`[pracht] ${message}`);
1075
- appendHydrationWarning(message);
1076
- if (prev) prev(vnode, s);
1077
- };
1430
+ } else window.location.href = target.browserUrl;
1078
1431
  }
1079
- const initialMatch = matchAppRoute(app, options.initialState.url);
1432
+ const initialTarget = resolveBrowserRouteTarget(options.initialState.url);
1433
+ const initialRequestUrl = initialTarget?.requestUrl ?? options.initialState.url;
1434
+ const initialBrowserUrl = initialTarget?.browserUrl ?? options.initialState.url;
1435
+ const initialMatch = matchAppRoute(app, initialTarget?.pathname ?? options.initialState.url);
1080
1436
  if (initialMatch) {
1081
1437
  const initialShellPromise = initialMatch.route.render === "spa" && options.initialState.pending ? startShellImport(initialMatch) : null;
1082
1438
  let state = {
@@ -1084,9 +1440,9 @@ async function initClientRouter(options) {
1084
1440
  error: options.initialState.error ?? null
1085
1441
  };
1086
1442
  if (initialMatch.route.render === "spa" && options.initialState.pending) {
1087
- const dataPromise = fetchPrachtRouteState(options.initialState.url);
1088
- const pendingTree = await buildSpaPendingTree(initialMatch, initialShellPromise);
1089
- if (pendingTree) hydrate(pendingTree, root);
1443
+ const dataPromise = fetchPrachtRouteState(initialRequestUrl, { useDataParam: true });
1444
+ const pendingState = await resolveSpaPendingState(initialMatch, initialRequestUrl, initialShellPromise);
1445
+ if (pendingState) hydrate(h(RouterRoot, { initialState: pendingState }), root);
1090
1446
  try {
1091
1447
  const result = await dataPromise;
1092
1448
  if (result.type === "redirect") {
@@ -1102,15 +1458,18 @@ async function initClientRouter(options) {
1102
1458
  error: null
1103
1459
  };
1104
1460
  } catch {
1105
- window.location.href = options.initialState.url;
1461
+ window.location.href = initialBrowserUrl;
1106
1462
  return;
1107
1463
  }
1108
- }
1109
- const tree = await buildRouteTree(initialMatch, state, void 0, initialShellPromise);
1110
- if (tree) if (initialMatch.route.render === "spa") render(tree, root);
1111
- else {
1112
- markHydrating();
1113
- hydrate(tree, root);
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
+ }
1114
1473
  }
1115
1474
  }
1116
1475
  document.addEventListener("click", (e) => {
@@ -1132,10 +1491,10 @@ async function initClientRouter(options) {
1132
1491
  }
1133
1492
  if (url.origin !== window.location.origin) return;
1134
1493
  e.preventDefault();
1135
- navigate(url.pathname + url.search);
1494
+ navigate(url.pathname + url.search + url.hash);
1136
1495
  });
1137
1496
  window.addEventListener("popstate", () => {
1138
- navigate(window.location.pathname + window.location.search, { _popstate: true });
1497
+ navigate(window.location.pathname + window.location.search + window.location.hash, { _popstate: true });
1139
1498
  });
1140
1499
  window.__PRACHT_NAVIGATE__ = navigate;
1141
1500
  window.__PRACHT_ROUTER_READY__ = true;
@@ -1145,60 +1504,19 @@ async function initClientRouter(options) {
1145
1504
  };
1146
1505
  setupPrefetching(app, warmModules);
1147
1506
  }
1148
- const HYDRATION_BANNER_ID = "__pracht_hydration_warnings__";
1149
- function appendHydrationWarning(message) {
1150
- let container = document.getElementById(HYDRATION_BANNER_ID);
1151
- if (!container) {
1152
- container = document.createElement("div");
1153
- container.id = HYDRATION_BANNER_ID;
1154
- Object.assign(container.style, {
1155
- position: "fixed",
1156
- bottom: "0",
1157
- left: "0",
1158
- right: "0",
1159
- maxHeight: "30vh",
1160
- overflow: "auto",
1161
- background: "#2d1b00",
1162
- borderTop: "2px solid #f0ad4e",
1163
- color: "#ffc107",
1164
- fontFamily: "ui-monospace, Consolas, monospace",
1165
- fontSize: "13px",
1166
- padding: "12px 16px",
1167
- zIndex: "2147483647"
1168
- });
1169
- const header = document.createElement("div");
1170
- Object.assign(header.style, {
1171
- display: "flex",
1172
- justifyContent: "space-between",
1173
- alignItems: "center",
1174
- marginBottom: "8px"
1175
- });
1176
- header.innerHTML = "<strong style=\"color:#f0ad4e\">⚠ Hydration Mismatches</strong>";
1177
- const close = document.createElement("button");
1178
- close.textContent = "×";
1179
- Object.assign(close.style, {
1180
- background: "none",
1181
- border: "none",
1182
- color: "#f0ad4e",
1183
- fontSize: "18px",
1184
- cursor: "pointer"
1185
- });
1186
- close.onclick = () => container.remove();
1187
- header.appendChild(close);
1188
- container.appendChild(header);
1189
- document.body.appendChild(container);
1507
+ function resolveBrowserRouteTarget(to) {
1508
+ if (typeof window === "undefined") return null;
1509
+ try {
1510
+ const url = new URL(to, window.location.href);
1511
+ if (url.origin !== window.location.origin) return null;
1512
+ return {
1513
+ browserUrl: url.pathname + url.search + url.hash,
1514
+ pathname: url.pathname,
1515
+ requestUrl: url.pathname + url.search
1516
+ };
1517
+ } catch {
1518
+ return null;
1190
1519
  }
1191
- const entry = document.createElement("div");
1192
- entry.textContent = message;
1193
- Object.assign(entry.style, { padding: "2px 0" });
1194
- container.appendChild(entry);
1195
- }
1196
- function deserializeRouteError(error) {
1197
- const result = new Error(error.message);
1198
- result.name = error.name;
1199
- result.status = error.status;
1200
- result.diagnostics = error.diagnostics;
1201
- return result;
1202
1520
  }
1203
1521
  //#endregion
1204
1522
  //#region src/types.ts
@@ -1211,4 +1529,4 @@ var PrachtHttpError = class extends Error {
1211
1529
  }
1212
1530
  };
1213
1531
  //#endregion
1214
- 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, useRevalidateRoute, useRouteData };
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 };