@pracht/core 0.7.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,501 @@
1
+ import { o as matchAppRoute, t as buildHref } from "./app-w-P1wf5T.mjs";
2
+ import { i as getCachedRouteState, l as parseSafeNavigationUrl, s as fetchPrachtRouteState, u as routeNeedsServerFetch } from "./prefetch-cache-DzP2Bj9H.mjs";
3
+ import { s as deserializeRouteError, t as PrachtRuntimeProvider } from "./runtime-context-B5pREhcM.mjs";
4
+ import { createContext, h, hydrate, options, render } from "preact";
5
+ import { useContext, useEffect, useMemo, useState } from "preact/hooks";
6
+ import { Suspense } from "preact-suspense";
7
+ //#region src/hydration.ts
8
+ const MODE_HYDRATE$1 = 32;
9
+ let _hydrating = false;
10
+ let _suspensionCount = 0;
11
+ let _hydrated = false;
12
+ new Suspense({});
13
+ const oldCatchError = options.__e;
14
+ options.__e = (err, newVNode, oldVNode, errorInfo) => {
15
+ if (_hydrating && !_hydrated && err && err.then) {
16
+ if (!!(newVNode && newVNode.__u && newVNode.__u & MODE_HYDRATE$1) || !!(newVNode && newVNode.__h)) {
17
+ _suspensionCount++;
18
+ let settled = false;
19
+ const onSettled = () => {
20
+ if (settled) return;
21
+ settled = true;
22
+ _suspensionCount--;
23
+ };
24
+ err.then(onSettled, onSettled);
25
+ }
26
+ }
27
+ if (oldCatchError) oldCatchError(err, newVNode, oldVNode, errorInfo);
28
+ };
29
+ const oldCommit = options.__c;
30
+ options.__c = (vnode, commitQueue) => {
31
+ if (_hydrating && !_hydrated && _suspensionCount <= 0) {
32
+ _hydrated = true;
33
+ _hydrating = false;
34
+ }
35
+ if (oldCommit) oldCommit(vnode, commitQueue);
36
+ };
37
+ /**
38
+ * Mark the start of a hydration pass. Call this right before `hydrate()`.
39
+ */
40
+ function markHydrating() {
41
+ if (!_hydrated) _hydrating = true;
42
+ }
43
+ /**
44
+ * Returns `true` once the initial hydration (including all Suspense
45
+ * boundaries) has fully resolved. During SSR and hydration this returns
46
+ * `false`.
47
+ */
48
+ function useIsHydrated() {
49
+ const [hydrated, setHydrated] = useState(_hydrated);
50
+ useEffect(() => {
51
+ setHydrated(true);
52
+ }, []);
53
+ return hydrated;
54
+ }
55
+ //#endregion
56
+ //#region src/hydration-mismatch.ts
57
+ const HYDRATION_BANNER_ID = "__pracht_hydration_mismatch__";
58
+ const MODE_HYDRATE = 32;
59
+ let installed = false;
60
+ let prevMismatch;
61
+ let prevCatchError;
62
+ let prevCommit;
63
+ const pendingSuspenseChecks = /* @__PURE__ */ new Set();
64
+ let flushScheduled = false;
65
+ function installHydrationMismatchWarning() {
66
+ if (installed) return;
67
+ installed = true;
68
+ const opts = options;
69
+ prevMismatch = opts.__m;
70
+ prevCatchError = opts.__e;
71
+ prevCommit = opts.__c;
72
+ opts.__m = function(vnode) {
73
+ appendHydrationWarning(vnode);
74
+ if (prevMismatch) prevMismatch(vnode);
75
+ };
76
+ opts.__e = function(err, newVNode, oldVNode, errorInfo) {
77
+ trackSuspendingVNode(err, newVNode);
78
+ if (prevCatchError) prevCatchError(err, newVNode, oldVNode, errorInfo);
79
+ };
80
+ opts.__c = function(vnode, commitQueue) {
81
+ if (prevCommit) prevCommit(vnode, commitQueue);
82
+ scheduleSuspenseCheckFlush();
83
+ };
84
+ }
85
+ function trackSuspendingVNode(err, vnode) {
86
+ if (!vnode) return;
87
+ if (!err || typeof err.then !== "function") return;
88
+ if (!!!(vnode.__u && vnode.__u & MODE_HYDRATE || vnode.__h)) return;
89
+ const promise = err;
90
+ const onSettle = () => {
91
+ pendingSuspenseChecks.add(vnode);
92
+ };
93
+ promise.then(onSettle, onSettle);
94
+ }
95
+ function scheduleSuspenseCheckFlush() {
96
+ if (flushScheduled) return;
97
+ if (pendingSuspenseChecks.size === 0) return;
98
+ flushScheduled = true;
99
+ queueMicrotask(flushSuspenseChecks);
100
+ }
101
+ function flushSuspenseChecks() {
102
+ flushScheduled = false;
103
+ if (pendingSuspenseChecks.size === 0) return;
104
+ const checks = Array.from(pendingSuspenseChecks);
105
+ pendingSuspenseChecks.clear();
106
+ for (const vnode of checks) {
107
+ const rendered = currentVNode(vnode);
108
+ const count = countTopLevelDomNodes(rendered);
109
+ if (count !== 1) appendSuspenseOffsetWarning(pickReportableVNode(rendered), count);
110
+ }
111
+ }
112
+ function currentVNode(vnode) {
113
+ return vnode.__c?.__v ?? vnode;
114
+ }
115
+ function pickReportableVNode(vnode) {
116
+ let current = vnode;
117
+ for (let depth = 0; depth < 4; depth++) {
118
+ if (!isLazyWrapperVNode(current)) break;
119
+ const children = current.__k;
120
+ if (!Array.isArray(children) || children.length !== 1) break;
121
+ const child = children[0];
122
+ if (!child || typeof child.type !== "function") break;
123
+ current = currentVNode(child);
124
+ }
125
+ return current;
126
+ }
127
+ function isLazyWrapperVNode(vnode) {
128
+ const type = vnode.type;
129
+ if (typeof type !== "function") return false;
130
+ const fn = type;
131
+ return fn.displayName === "Lazy" || fn.name === "Lazy";
132
+ }
133
+ function countTopLevelDomNodes(vnode) {
134
+ if (!vnode || typeof vnode !== "object") return 0;
135
+ const type = vnode.type;
136
+ if (type === null) return 1;
137
+ if (typeof type === "string") return 1;
138
+ const children = vnode.__k;
139
+ if (!Array.isArray(children)) return 0;
140
+ let total = 0;
141
+ for (const child of children) total += countTopLevelDomNodes(child);
142
+ return total;
143
+ }
144
+ function appendSuspenseOffsetWarning(vnode, count) {
145
+ if (typeof document === "undefined") return;
146
+ appendBannerMessage(`Suspense boundary resolved during hydration: <${getVNodeName(vnode)}> ${count === 0 ? "rendered 0 DOM nodes" : `rendered ${count} DOM nodes`}. Components that unsuspend during hydration must render exactly one DOM node — otherwise sibling offsets can drift and later updates may bind to the wrong nodes.`);
147
+ }
148
+ function appendHydrationWarning(vnode) {
149
+ appendBannerMessage(`Hydration mismatch detected on <${getVNodeName(vnode)}>. The server-rendered HTML did not match the client.`);
150
+ }
151
+ function appendBannerMessage(message) {
152
+ if (typeof document === "undefined") return;
153
+ let banner = document.getElementById(HYDRATION_BANNER_ID);
154
+ if (banner) {
155
+ const list = banner.querySelector(`[data-pracht-mismatch-list]`);
156
+ if (list) {
157
+ const item = document.createElement("li");
158
+ item.textContent = message;
159
+ list.appendChild(item);
160
+ }
161
+ return;
162
+ }
163
+ banner = document.createElement("div");
164
+ banner.id = HYDRATION_BANNER_ID;
165
+ banner.setAttribute("role", "alert");
166
+ banner.style.cssText = [
167
+ "position:fixed",
168
+ "top:0",
169
+ "left:0",
170
+ "right:0",
171
+ "z-index:2147483647",
172
+ "background:#1a1a2e",
173
+ "color:#ff6b6b",
174
+ "padding:12px 16px",
175
+ "font:12px/1.5 ui-monospace,Menlo,Consolas,monospace",
176
+ "border-bottom:2px solid #e74c3c",
177
+ "box-shadow:0 2px 8px rgba(0,0,0,0.3)"
178
+ ].join(";");
179
+ const title = document.createElement("strong");
180
+ title.textContent = "pracht: hydration mismatch";
181
+ title.style.cssText = "display:block;margin-bottom:4px;color:#fff";
182
+ banner.appendChild(title);
183
+ const list = document.createElement("ul");
184
+ list.setAttribute("data-pracht-mismatch-list", "");
185
+ list.style.cssText = "margin:0;padding-left:18px";
186
+ const item = document.createElement("li");
187
+ item.textContent = message;
188
+ list.appendChild(item);
189
+ banner.appendChild(list);
190
+ document.body.appendChild(banner);
191
+ }
192
+ function getVNodeName(vnode) {
193
+ if (!vnode) return "Unknown";
194
+ const type = vnode.type;
195
+ if (typeof type === "string") return type;
196
+ if (typeof type === "function") {
197
+ const fn = type;
198
+ return fn.displayName || fn.name || "Component";
199
+ }
200
+ return "Unknown";
201
+ }
202
+ //#endregion
203
+ //#region src/router.ts
204
+ const NavigateContext = createContext(async () => {});
205
+ function useNavigate() {
206
+ return useContext(NavigateContext);
207
+ }
208
+ async function initClientRouter(options) {
209
+ const { app, routeModules, shellModules, root, findModuleKey } = options;
210
+ if (import.meta.env?.DEV) installHydrationMismatchWarning();
211
+ const moduleCache = /* @__PURE__ */ new Map();
212
+ function loadModule(modules, key) {
213
+ let cached = moduleCache.get(key);
214
+ if (!cached) {
215
+ cached = modules[key]();
216
+ moduleCache.set(key, cached);
217
+ }
218
+ return cached;
219
+ }
220
+ function startRouteImport(match) {
221
+ const routeKey = findModuleKey(routeModules, match.route.file);
222
+ if (!routeKey) return null;
223
+ return loadModule(routeModules, routeKey);
224
+ }
225
+ function startShellImport(match) {
226
+ if (!match.route.shellFile) return null;
227
+ const shellKey = findModuleKey(shellModules, match.route.shellFile);
228
+ if (!shellKey) return null;
229
+ return loadModule(shellModules, shellKey);
230
+ }
231
+ let updateRouteState = null;
232
+ let routeStateVersion = 0;
233
+ let latestNavigationId = 0;
234
+ let activeNavigationAbort = null;
235
+ function RouterRoot({ initialState }) {
236
+ const [routeState, setRouteState] = useState(initialState);
237
+ updateRouteState = setRouteState;
238
+ const navigateValue = useMemo(() => navigate, []);
239
+ const { Shell, Component, componentProps, data, params, routeId, url, version } = routeState;
240
+ const componentTree = Shell ? h(Shell, null, h(Component, componentProps)) : h(Component, componentProps);
241
+ return h(NavigateContext.Provider, { value: navigateValue }, h(PrachtRuntimeProvider, {
242
+ data,
243
+ params,
244
+ routeId,
245
+ routes: app.routes,
246
+ stateVersion: version,
247
+ url
248
+ }, componentTree));
249
+ }
250
+ function applyRouteState(routeState) {
251
+ if (updateRouteState) {
252
+ updateRouteState(routeState);
253
+ return;
254
+ }
255
+ render(h(RouterRoot, { initialState: routeState }), root);
256
+ }
257
+ async function resolveRouteState(match, state, currentUrl, routeModPromise, shellModPromise) {
258
+ const routeMod = await (routeModPromise ?? startRouteImport(match));
259
+ if (!routeMod) return null;
260
+ let Shell = null;
261
+ const resolvedShell = await (shellModPromise ?? startShellImport(match));
262
+ if (resolvedShell) Shell = resolvedShell.Shell;
263
+ const DefaultComponent = typeof routeMod.default === "function" ? routeMod.default : void 0;
264
+ const ErrorBoundary = routeMod.ErrorBoundary ?? resolvedShell?.ErrorBoundary;
265
+ const Component = state.error ? ErrorBoundary : routeMod.Component ?? DefaultComponent;
266
+ if (!Component) return null;
267
+ const componentProps = state.error ? { error: deserializeRouteError(state.error) } : {
268
+ data: state.data,
269
+ params: match.params
270
+ };
271
+ return {
272
+ Shell,
273
+ Component,
274
+ componentProps,
275
+ data: state.data,
276
+ params: match.params,
277
+ routeId: match.route.id ?? "",
278
+ url: currentUrl,
279
+ version: ++routeStateVersion
280
+ };
281
+ }
282
+ async function resolveSpaPendingState(match, currentUrl, shellModPromise) {
283
+ const resolvedShell = await (shellModPromise ?? startShellImport(match));
284
+ if (!resolvedShell) return null;
285
+ const Shell = resolvedShell.Shell ?? null;
286
+ const Loading = resolvedShell.Loading;
287
+ if (!Shell && !Loading) return null;
288
+ return {
289
+ Shell,
290
+ Component: Loading ?? (() => null),
291
+ componentProps: {},
292
+ data: void 0,
293
+ params: match.params,
294
+ routeId: match.route.id ?? "",
295
+ url: currentUrl,
296
+ version: ++routeStateVersion
297
+ };
298
+ }
299
+ function resolveRedirectTarget(location) {
300
+ const targetUrl = parseSafeNavigationUrl(location, window.location.href);
301
+ if (!targetUrl) return {
302
+ isCurrentLocation: false,
303
+ unsafe: true
304
+ };
305
+ const fullInternalTarget = targetUrl.pathname + targetUrl.search + targetUrl.hash;
306
+ const internalPath = targetUrl.pathname + targetUrl.search;
307
+ const currentPath = window.location.pathname + window.location.search + window.location.hash;
308
+ const isCurrentLocation = targetUrl.origin === window.location.origin && fullInternalTarget === currentPath;
309
+ if (targetUrl.origin !== window.location.origin) return {
310
+ externalUrl: targetUrl.toString(),
311
+ isCurrentLocation: false
312
+ };
313
+ if (targetUrl.hash) return {
314
+ documentUrl: targetUrl.toString(),
315
+ isCurrentLocation
316
+ };
317
+ return {
318
+ internalPath,
319
+ isCurrentLocation
320
+ };
321
+ }
322
+ async function navigate(to, opts) {
323
+ const navigationId = ++latestNavigationId;
324
+ activeNavigationAbort?.abort();
325
+ const abortController = new AbortController();
326
+ activeNavigationAbort = abortController;
327
+ const navigationTarget = typeof to === "string" ? to : buildHref(app.routes, to.route, to);
328
+ const target = resolveBrowserRouteTarget(navigationTarget);
329
+ if (!target) {
330
+ const safeUrl = parseSafeNavigationUrl(navigationTarget, window.location.href);
331
+ if (safeUrl) window.location.href = safeUrl.toString();
332
+ else if (navigationTarget) console.error(`[pracht] refused to navigate to unsafe URL: ${navigationTarget}`);
333
+ return;
334
+ }
335
+ const match = matchAppRoute(app, target.pathname);
336
+ if (!match) {
337
+ window.location.href = target.browserUrl;
338
+ return;
339
+ }
340
+ let statePromise;
341
+ if (routeNeedsServerFetch(match.route)) statePromise = getCachedRouteState(target.requestUrl) ?? fetchPrachtRouteState(target.requestUrl, { signal: abortController.signal });
342
+ else statePromise = Promise.resolve({
343
+ type: "data",
344
+ data: void 0
345
+ });
346
+ const routeModPromise = startRouteImport(match);
347
+ const shellModPromise = startShellImport(match);
348
+ let state = {
349
+ data: void 0,
350
+ error: null
351
+ };
352
+ try {
353
+ const result = await statePromise;
354
+ if (navigationId !== latestNavigationId) return;
355
+ if (result.type === "redirect") {
356
+ if (result.location) {
357
+ const redirect = resolveRedirectTarget(result.location);
358
+ if (redirect.unsafe) {
359
+ console.error(`[pracht] refused to navigate to unsafe URL: ${result.location}`);
360
+ return;
361
+ }
362
+ if (redirect.externalUrl) {
363
+ window.location.href = redirect.externalUrl;
364
+ return;
365
+ }
366
+ if (redirect.isCurrentLocation) return;
367
+ if (redirect.documentUrl) {
368
+ window.location.href = redirect.documentUrl;
369
+ return;
370
+ }
371
+ if (redirect.internalPath) {
372
+ await navigate(redirect.internalPath, opts);
373
+ return;
374
+ }
375
+ window.location.href = target.browserUrl;
376
+ return;
377
+ }
378
+ window.location.href = target.browserUrl;
379
+ return;
380
+ }
381
+ if (result.type === "error") state = {
382
+ data: void 0,
383
+ error: result.error
384
+ };
385
+ else state = {
386
+ data: result.data,
387
+ error: null
388
+ };
389
+ } catch {
390
+ if (abortController.signal.aborted || navigationId !== latestNavigationId) return;
391
+ window.location.href = target.browserUrl;
392
+ return;
393
+ }
394
+ if (navigationId !== latestNavigationId) return;
395
+ if (!opts?._popstate) if (opts?.replace) history.replaceState(null, "", target.browserUrl);
396
+ else history.pushState(null, "", target.browserUrl);
397
+ const routeState = await resolveRouteState(match, state, target.requestUrl, routeModPromise, shellModPromise);
398
+ if (navigationId !== latestNavigationId) return;
399
+ if (routeState) {
400
+ applyRouteState(routeState);
401
+ window.scrollTo(0, 0);
402
+ } else window.location.href = target.browserUrl;
403
+ }
404
+ const initialTarget = resolveBrowserRouteTarget(options.initialState.url);
405
+ const initialRequestUrl = initialTarget?.requestUrl ?? options.initialState.url;
406
+ const initialBrowserUrl = initialTarget?.browserUrl ?? options.initialState.url;
407
+ const initialMatch = matchAppRoute(app, initialTarget?.pathname ?? options.initialState.url);
408
+ if (initialMatch) {
409
+ const initialShellPromise = initialMatch.route.render === "spa" && options.initialState.pending ? startShellImport(initialMatch) : null;
410
+ let state = {
411
+ data: options.initialState.data,
412
+ error: options.initialState.error ?? null
413
+ };
414
+ if (initialMatch.route.render === "spa" && options.initialState.pending) {
415
+ const dataPromise = fetchPrachtRouteState(initialRequestUrl, { useDataParam: true });
416
+ const pendingState = await resolveSpaPendingState(initialMatch, initialRequestUrl, initialShellPromise);
417
+ if (pendingState) hydrate(h(RouterRoot, { initialState: pendingState }), root);
418
+ try {
419
+ const result = await dataPromise;
420
+ if (result.type === "redirect") {
421
+ const safeRedirect = parseSafeNavigationUrl(result.location, window.location.href);
422
+ if (!safeRedirect) {
423
+ console.error(`[pracht] refused to navigate to unsafe URL: ${result.location}`);
424
+ return;
425
+ }
426
+ window.location.href = safeRedirect.toString();
427
+ return;
428
+ }
429
+ if (result.type === "error") state = {
430
+ data: void 0,
431
+ error: result.error
432
+ };
433
+ else state = {
434
+ data: result.data,
435
+ error: null
436
+ };
437
+ } catch {
438
+ window.location.href = initialBrowserUrl;
439
+ return;
440
+ }
441
+ const resolvedState = await resolveRouteState(initialMatch, state, initialRequestUrl, void 0, initialShellPromise);
442
+ if (resolvedState) applyRouteState(resolvedState);
443
+ } else {
444
+ const initialRouteState = await resolveRouteState(initialMatch, state, initialRequestUrl, void 0, initialShellPromise);
445
+ if (initialRouteState) if (initialMatch.route.render === "spa") render(h(RouterRoot, { initialState: initialRouteState }), root);
446
+ else {
447
+ markHydrating();
448
+ hydrate(h(RouterRoot, { initialState: initialRouteState }), root);
449
+ }
450
+ }
451
+ }
452
+ document.addEventListener("click", (e) => {
453
+ const anchor = e.target.closest?.("a");
454
+ if (!anchor) return;
455
+ if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
456
+ if (e.defaultPrevented) return;
457
+ if (e.button !== 0) return;
458
+ const target = anchor.getAttribute("target");
459
+ if (target && target !== "_self") return;
460
+ if (anchor.hasAttribute("download")) return;
461
+ const href = anchor.getAttribute("href");
462
+ if (!href || href.startsWith("#")) return;
463
+ let url;
464
+ try {
465
+ url = new URL(href, window.location.origin);
466
+ } catch {
467
+ return;
468
+ }
469
+ if (url.origin !== window.location.origin) return;
470
+ e.preventDefault();
471
+ navigate(url.pathname + url.search + url.hash);
472
+ });
473
+ window.addEventListener("popstate", () => {
474
+ navigate(window.location.pathname + window.location.search + window.location.hash, { _popstate: true });
475
+ });
476
+ window.__PRACHT_NAVIGATE__ = navigate;
477
+ window.__PRACHT_ROUTER_READY__ = true;
478
+ const warmModules = (match) => {
479
+ startRouteImport(match);
480
+ startShellImport(match);
481
+ };
482
+ import("./prefetch-D9amIQtw.mjs").then(({ setupPrefetching }) => {
483
+ setupPrefetching(app, warmModules);
484
+ });
485
+ }
486
+ function resolveBrowserRouteTarget(to) {
487
+ if (typeof window === "undefined") return null;
488
+ try {
489
+ const url = new URL(to, window.location.href);
490
+ if (url.origin !== window.location.origin) return null;
491
+ return {
492
+ browserUrl: url.pathname + url.search + url.hash,
493
+ pathname: url.pathname,
494
+ requestUrl: url.pathname + url.search
495
+ };
496
+ } catch {
497
+ return null;
498
+ }
499
+ }
500
+ //#endregion
501
+ export { useNavigate as n, useIsHydrated as r, initClientRouter as t };
@@ -0,0 +1,164 @@
1
+ import { d as EMPTY_ROUTE_PARAMS, f as HYDRATION_STATE_ELEMENT_ID } from "./prefetch-cache-DzP2Bj9H.mjs";
2
+ import { createContext, h } from "preact";
3
+ import { useEffect, useMemo, useState } from "preact/hooks";
4
+ //#region src/runtime-errors.ts
5
+ function isPrachtHttpError(error) {
6
+ return error instanceof Error && error.name === "PrachtHttpError" && "status" in error;
7
+ }
8
+ let warnedAboutProductionDebugErrors = false;
9
+ /**
10
+ * `debugErrors: true` opts into surfacing stack traces, module paths,
11
+ * and middleware names in error responses. That is great in dev and
12
+ * dangerous in production — a misconfigured deploy would leak internals
13
+ * to the public. When `NODE_ENV === "production"` we refuse to honor
14
+ * the flag and emit a single console warning so the misconfiguration
15
+ * is visible in logs.
16
+ */
17
+ function shouldExposeServerErrors(options) {
18
+ if (options.debugErrors !== true) return false;
19
+ if ((typeof process !== "undefined" && process.env ? process.env.NODE_ENV : typeof globalThis !== "undefined" && globalThis.process ? globalThis.process?.env?.NODE_ENV : void 0) === "production") {
20
+ if (!warnedAboutProductionDebugErrors) {
21
+ warnedAboutProductionDebugErrors = true;
22
+ console.warn("[pracht] debugErrors is ignored in production builds. Remove it to silence this warning.");
23
+ }
24
+ return false;
25
+ }
26
+ return true;
27
+ }
28
+ function createSerializedRouteError(message, status, options = {}) {
29
+ return {
30
+ message,
31
+ name: options.name ?? "Error",
32
+ status,
33
+ ...options.diagnostics ? { diagnostics: options.diagnostics } : {}
34
+ };
35
+ }
36
+ function buildRuntimeDiagnostics(options) {
37
+ const route = options.route;
38
+ const routeId = route && "id" in route ? route.id : void 0;
39
+ return {
40
+ phase: options.phase,
41
+ routeId,
42
+ routePath: route?.path,
43
+ routeFile: route?.file,
44
+ loaderFile: options.loaderFile,
45
+ shellFile: options.shellFile,
46
+ middlewareFiles: options.middlewareFiles ? [...options.middlewareFiles] : [],
47
+ status: options.status
48
+ };
49
+ }
50
+ function normalizeRouteError(error, options) {
51
+ if (isPrachtHttpError(error)) {
52
+ const status = typeof error.status === "number" ? error.status : 500;
53
+ if (status >= 400 && status < 500) return {
54
+ message: error.message,
55
+ name: error.name,
56
+ status
57
+ };
58
+ if (options.exposeDetails) return {
59
+ message: error.message || "Internal Server Error",
60
+ name: error.name || "Error",
61
+ status
62
+ };
63
+ return {
64
+ message: "Internal Server Error",
65
+ name: "Error",
66
+ status
67
+ };
68
+ }
69
+ if (error instanceof Error) {
70
+ if (options.exposeDetails) return {
71
+ message: error.message || "Internal Server Error",
72
+ name: error.name || "Error",
73
+ status: 500
74
+ };
75
+ return {
76
+ message: "Internal Server Error",
77
+ name: "Error",
78
+ status: 500
79
+ };
80
+ }
81
+ if (options.exposeDetails) return {
82
+ message: typeof error === "string" && error ? error : "Internal Server Error",
83
+ name: "Error",
84
+ status: 500
85
+ };
86
+ return {
87
+ message: "Internal Server Error",
88
+ name: "Error",
89
+ status: 500
90
+ };
91
+ }
92
+ function deserializeRouteError(error) {
93
+ const result = new Error(error.message);
94
+ result.name = error.name;
95
+ result.status = error.status;
96
+ result.diagnostics = error.diagnostics;
97
+ return result;
98
+ }
99
+ //#endregion
100
+ //#region src/runtime-context.ts
101
+ const RouteDataContext = createContext(void 0);
102
+ function PrachtRuntimeProvider({ children, data, params = EMPTY_ROUTE_PARAMS, routeId, routes, stateVersion = 0, url }) {
103
+ registerRuntimeRoutes(routes);
104
+ const [routeDataState, setRouteDataState] = useState({
105
+ data,
106
+ stateVersion
107
+ });
108
+ const routeData = routeDataState.stateVersion === stateVersion ? routeDataState.data : data;
109
+ useEffect(() => {
110
+ setRouteDataState({
111
+ data,
112
+ stateVersion
113
+ });
114
+ }, [
115
+ data,
116
+ routeId,
117
+ stateVersion,
118
+ url
119
+ ]);
120
+ const context = useMemo(() => ({
121
+ data: routeData,
122
+ params,
123
+ routeId,
124
+ routes,
125
+ setData: (nextData) => setRouteDataState({
126
+ data: nextData,
127
+ stateVersion
128
+ }),
129
+ url
130
+ }), [
131
+ routeData,
132
+ params,
133
+ routeId,
134
+ routes,
135
+ stateVersion,
136
+ url
137
+ ]);
138
+ return h(RouteDataContext.Provider, {
139
+ value: context,
140
+ children
141
+ });
142
+ }
143
+ function startApp(options = {}) {
144
+ if (typeof window === "undefined") return options.initialData;
145
+ if (typeof options.initialData !== "undefined") return options.initialData;
146
+ return readHydrationState()?.data;
147
+ }
148
+ function readHydrationState() {
149
+ if (typeof window === "undefined") return;
150
+ if (window.__PRACHT_STATE__) return window.__PRACHT_STATE__;
151
+ const element = document.getElementById(HYDRATION_STATE_ELEMENT_ID);
152
+ if (!(element instanceof HTMLScriptElement)) return;
153
+ const raw = element.textContent;
154
+ if (!raw) return;
155
+ const state = JSON.parse(raw);
156
+ window.__PRACHT_STATE__ = state;
157
+ return state;
158
+ }
159
+ function registerRuntimeRoutes(routes) {
160
+ if (!routes) return;
161
+ globalThis.__PRACHT_ROUTE_DEFINITIONS__ = routes;
162
+ }
163
+ //#endregion
164
+ export { buildRuntimeDiagnostics as a, normalizeRouteError as c, startApp as i, shouldExposeServerErrors as l, RouteDataContext as n, createSerializedRouteError as o, readHydrationState as r, deserializeRouteError as s, PrachtRuntimeProvider as t };