@pracht/core 0.6.1 → 0.8.0

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,77 @@
1
+ import { A as HrefRouteDefinition, F as LoaderLike, N as LoaderData, O as HrefFn, dt as RouteTarget, nt as RouteId, st as RouteParams } from "./app-BWriseLj.mjs";
2
+ import { c as SerializedRouteError } from "./runtime-context-ytVd7GmZ.mjs";
3
+ import * as _$preact from "preact";
4
+ import { JSX, h } from "preact";
5
+
6
+ //#region src/href.d.ts
7
+ declare function createHref(routes: readonly HrefRouteDefinition[]): HrefFn;
8
+ //#endregion
9
+ //#region src/runtime-hooks.d.ts
10
+ interface FormProps extends Omit<JSX.HTMLAttributes<HTMLFormElement>, "action" | "method"> {
11
+ action?: string;
12
+ method?: string;
13
+ }
14
+ type LinkProps<TRoute extends RouteId = RouteId> = Omit<JSX.HTMLAttributes<HTMLAnchorElement>, "href"> & RouteTarget<TRoute>;
15
+ interface Location {
16
+ pathname: string;
17
+ search: string;
18
+ }
19
+ declare function useRouteData<TLoader extends LoaderLike>(): LoaderData<TLoader>;
20
+ declare function useRouteData<TData = unknown>(): TData;
21
+ declare function useLocation(): Location;
22
+ declare function useParams(): RouteParams;
23
+ declare function useRevalidate(): () => Promise<unknown>;
24
+ declare function Link<TRoute extends RouteId>(props: LinkProps<TRoute>): _$preact.VNode<_$preact.ClassAttributes<HTMLAnchorElement> & h.JSX.HTMLAttributes<HTMLAnchorElement>>;
25
+ declare function Form(props: FormProps): _$preact.VNode<_$preact.ClassAttributes<HTMLFormElement> & h.JSX.HTMLAttributes<HTMLFormElement>>;
26
+ //#endregion
27
+ //#region src/runtime-client-fetch.d.ts
28
+ type RouteStateResult = {
29
+ type: "data";
30
+ data: unknown;
31
+ } | {
32
+ type: "redirect";
33
+ location: string;
34
+ } | {
35
+ type: "error";
36
+ error: SerializedRouteError;
37
+ };
38
+ /**
39
+ * Parse a possibly-server-supplied redirect target against a base URL and
40
+ * return it only if it uses a safe navigation scheme (`http:` or `https:`).
41
+ *
42
+ * `javascript:`, `data:`, `vbscript:`, `blob:`, `file:` and similar schemes
43
+ * can execute script or bypass same-origin assumptions when assigned to
44
+ * `window.location.href` — a server-controlled redirect (from a loader,
45
+ * middleware, form action response, or API route) must never be able to
46
+ * trigger them. Returns `null` for unsafe or unparseable inputs.
47
+ */
48
+ declare function parseSafeNavigationUrl(location: string, base: string | URL): URL | null;
49
+ declare function fetchPrachtRouteState(url: string, options?: {
50
+ signal?: AbortSignal;
51
+ useDataParam?: boolean;
52
+ }): Promise<RouteStateResult>;
53
+ //#endregion
54
+ //#region src/runtime-middleware.d.ts
55
+ type RedirectOptions = number | {
56
+ baseUrl?: string | URL;
57
+ method?: string;
58
+ request?: Request;
59
+ status?: number;
60
+ };
61
+ /**
62
+ * Convenience helper for middleware (and loaders/handlers) to short-circuit
63
+ * with a redirect Response. Validates the target's scheme and rejects
64
+ * CR/LF injection. Pass the current request (or method) when the default
65
+ * status should follow HTTP method safety: safe methods default to 302,
66
+ * unsafe methods default to 303.
67
+ *
68
+ * ```ts
69
+ * export const middleware: MiddlewareFn = async ({ request }, next) => {
70
+ * if (!hasSession(request)) return redirect("/login", { request });
71
+ * return next();
72
+ * };
73
+ * ```
74
+ */
75
+ declare function redirect(target: string, options?: RedirectOptions): Response;
76
+ //#endregion
77
+ export { parseSafeNavigationUrl as a, Link as c, useLocation as d, useParams as f, createHref as h, fetchPrachtRouteState as i, LinkProps as l, useRouteData as m, redirect as n, Form as o, useRevalidate as p, RouteStateResult as r, FormProps as s, RedirectOptions as t, Location as u };
@@ -0,0 +1,5 @@
1
+ import { $ as RouteComponentProps, A as HrefRouteDefinition, B as ModuleImporter, C as HeadAttributes, D as HrefArgs, E as HeadersArgs, G as PrachtAppConfig, H as ModuleRegistry, I as MiddlewareArgs, J as Register, K as PrachtHttpError, L as MiddlewareFn, M as LoaderArgs, N as LoaderData, O as HrefFn, P as LoaderFn, Q as ResolvedRoute, R as MiddlewareModule, S as HeadArgs, T as HeadScriptDescriptor, U as NavigateOptions, V as ModuleRef, W as PrachtApp, X as ResolvedApiRoute, Y as RenderMode, Z as ResolvedPrachtApp, _ as BuildHrefOptions, _t as ShellProps, a as matchApiRoute, at as RouteModule, b as GroupDefinition, c as resolveApp, ct as RouteParamsFor, d as ApiConfig, dt as RouteTarget, et as RouteConfig, f as ApiRouteArgs, ft as RouteTreeNode, g as BaseRouteArgs, gt as ShellModule, h as ApiRouteModule, ht as SearchParamsInput, i as group, it as RouteMeta, j as HttpMethod, k as HrefOptions, l as route, lt as RouteRevalidate, m as ApiRouteMatch, mt as SearchParamValue, n as buildPathFromSegments, nt as RouteId, o as matchAppRoute, ot as RouteParamInput, p as ApiRouteHandler, pt as SearchParamPrimitive, q as PrefetchStrategy, r as defineApp, rt as RouteMatch, s as resolveApiRoutes, st as RouteParams, t as buildHref, tt as RouteDefinition, u as timeRevalidate, ut as RouteSearchFor, v as DataModule, vt as TimeRevalidatePolicy, w as HeadMetadata, x as GroupMeta, y as ErrorBoundaryProps, z as MiddlewareNext } from "./app-BWriseLj.mjs";
2
+ import { h as createHref, n as redirect, t as RedirectOptions } from "./runtime-middleware-aeBdgjYr.mjs";
3
+ import { c as SerializedRouteError, n as PrachtRuntimeProvider, o as PrachtRuntimeDiagnosticPhase, s as PrachtRuntimeDiagnostics } from "./runtime-context-ytVd7GmZ.mjs";
4
+ import { a as prerenderApp, c as applyDefaultSecurityHeaders, i as PrerenderResult, n as PrerenderAppOptions, o as HandlePrachtRequestOptions, r as PrerenderAppResult, s as handlePrachtRequest, t as ISGManifestEntry } from "./prerender-B8_CqYwd.mjs";
5
+ export { type ApiConfig, type ApiRouteArgs, type ApiRouteHandler, type ApiRouteMatch, type ApiRouteModule, type BaseRouteArgs, type BuildHrefOptions, type DataModule, type ErrorBoundaryProps, type GroupDefinition, type GroupMeta, type HandlePrachtRequestOptions, type HeadArgs, type HeadAttributes, type HeadMetadata, type HeadScriptDescriptor, type HeadersArgs, type HrefArgs, type HrefFn, type HrefOptions, type HrefRouteDefinition, type HttpMethod, type ISGManifestEntry, type LoaderArgs, type LoaderData, type LoaderFn, type MiddlewareArgs, type MiddlewareFn, type MiddlewareModule, type MiddlewareNext, type ModuleImporter, type ModuleRef, type ModuleRegistry, type NavigateOptions, type PrachtApp, type PrachtAppConfig, PrachtHttpError, type PrachtRuntimeDiagnosticPhase, type PrachtRuntimeDiagnostics, PrachtRuntimeProvider, type PrefetchStrategy, type PrerenderAppOptions, type PrerenderAppResult, type PrerenderResult, type RedirectOptions, type Register, type RenderMode, type ResolvedApiRoute, type ResolvedPrachtApp, type ResolvedRoute, type RouteComponentProps, type RouteConfig, type RouteDefinition, type RouteId, type RouteMatch, type RouteMeta, type RouteModule, type RouteParamInput, type RouteParams, type RouteParamsFor, type RouteRevalidate, type RouteSearchFor, type RouteTarget, type RouteTreeNode, type SearchParamPrimitive, type SearchParamValue, type SearchParamsInput, type SerializedRouteError, type ShellModule, type ShellProps, type TimeRevalidatePolicy, applyDefaultSecurityHeaders, buildHref, buildPathFromSegments, createHref, defineApp, group, handlePrachtRequest, matchApiRoute, matchAppRoute, prerenderApp, redirect, resolveApiRoutes, resolveApp, route, timeRevalidate };
@@ -0,0 +1,5 @@
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, t as buildHref, u as timeRevalidate } from "./app-BvC1uQG5.mjs";
2
+ import { S as createHref, _ as applyDefaultSecurityHeaders, t as PrachtHttpError, u as redirect } from "./types-idmK5omD.mjs";
3
+ import { t as PrachtRuntimeProvider } from "./runtime-context-B5pREhcM.mjs";
4
+ import { n as handlePrachtRequest, t as prerenderApp } from "./prerender-D3E2H96p.mjs";
5
+ export { PrachtHttpError, PrachtRuntimeProvider, applyDefaultSecurityHeaders, buildHref, buildPathFromSegments, createHref, defineApp, group, handlePrachtRequest, matchApiRoute, matchAppRoute, prerenderApp, redirect, resolveApiRoutes, resolveApp, route, timeRevalidate };
@@ -0,0 +1,355 @@
1
+ import { t as buildHref } from "./app-BvC1uQG5.mjs";
2
+ import { c as navigateToClientLocation, h as SAFE_METHODS, l as parseSafeNavigationUrl, m as ROUTE_STATE_REQUEST_HEADER, p as ROUTE_STATE_CACHE_CONTROL, r as clearPrefetchCache, s as fetchPrachtRouteState } from "./prefetch-cache-DzP2Bj9H.mjs";
3
+ import { n as RouteDataContext, s as deserializeRouteError } from "./runtime-context-B5pREhcM.mjs";
4
+ import { h } from "preact";
5
+ import { useContext } from "preact/hooks";
6
+ //#region src/href.ts
7
+ function createHref(routes) {
8
+ return ((routeId, options) => buildHref(routes, routeId, options));
9
+ }
10
+ //#endregion
11
+ //#region src/runtime-headers.ts
12
+ const HEADER_CRLF_RE = /[\r\n]/;
13
+ /**
14
+ * Reject header values containing CR/LF. Some runtimes (Node `undici`
15
+ * Headers) throw on their own, but Web-runtime fetch implementations
16
+ * vary, and a user-supplied `headers()` value is never trusted input.
17
+ * Keeping the check here means response-splitting can't slip through on
18
+ * any adapter.
19
+ */
20
+ function assertSafeHeaderValue(name, value) {
21
+ if (HEADER_CRLF_RE.test(value)) throw new Error(`Refused to set header "${name}": value contains CR or LF`);
22
+ }
23
+ function applyHeaders(headers, init) {
24
+ for (const [key, value] of iterateHeaderInit(init)) assertSafeHeaderValue(key, value);
25
+ new Headers(init).forEach((value, key) => {
26
+ headers.set(key, value);
27
+ });
28
+ }
29
+ function* iterateHeaderInit(init) {
30
+ if (init instanceof Headers) {
31
+ for (const entry of init.entries()) yield entry;
32
+ return;
33
+ }
34
+ if (Array.isArray(init)) {
35
+ for (const entry of init) if (entry && entry.length >= 2) yield [entry[0], entry[1]];
36
+ return;
37
+ }
38
+ for (const [key, value] of Object.entries(init)) yield [key, value];
39
+ }
40
+ function applyDefaultSecurityHeaders(headers) {
41
+ if (!headers.has("permissions-policy")) headers.set("permissions-policy", "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()");
42
+ if (!headers.has("referrer-policy")) headers.set("referrer-policy", "strict-origin-when-cross-origin");
43
+ if (!headers.has("x-content-type-options")) headers.set("x-content-type-options", "nosniff");
44
+ if (!headers.has("x-frame-options")) headers.set("x-frame-options", "SAMEORIGIN");
45
+ return headers;
46
+ }
47
+ function applySecurityAndRouteHeaders(headers, options) {
48
+ applyDefaultSecurityHeaders(headers);
49
+ if (options) {
50
+ appendVaryHeader(headers, ROUTE_STATE_REQUEST_HEADER);
51
+ if (options.isRouteStateRequest && !headers.has("cache-control")) headers.set("cache-control", ROUTE_STATE_CACHE_CONTROL);
52
+ }
53
+ return headers;
54
+ }
55
+ function withDefaultSecurityHeaders(response) {
56
+ const headers = new Headers(response.headers);
57
+ applySecurityAndRouteHeaders(headers);
58
+ return new Response(response.body, {
59
+ status: response.status,
60
+ statusText: response.statusText,
61
+ headers
62
+ });
63
+ }
64
+ function withRouteResponseHeaders(response, options) {
65
+ const headers = new Headers(response.headers);
66
+ applySecurityAndRouteHeaders(headers, options);
67
+ return new Response(response.body, {
68
+ status: response.status,
69
+ statusText: response.statusText,
70
+ headers
71
+ });
72
+ }
73
+ function appendVaryHeader(headers, value) {
74
+ const current = headers.get("vary");
75
+ if (!current) {
76
+ headers.set("vary", value);
77
+ return;
78
+ }
79
+ const values = current.split(",").map((part) => part.trim().toLowerCase()).filter(Boolean);
80
+ if (values.includes("*") || values.includes(value.toLowerCase())) return;
81
+ headers.set("vary", `${current}, ${value}`);
82
+ }
83
+ //#endregion
84
+ //#region src/runtime-manifest.ts
85
+ /** Strip leading `./` and `/` so all module paths share one canonical form. */
86
+ function normalizeModulePath(path) {
87
+ return path.replace(/^\.?\//, "");
88
+ }
89
+ function buildSuffixIndex(manifest) {
90
+ const index = /* @__PURE__ */ new Map();
91
+ for (const key of Object.keys(manifest)) {
92
+ const normalized = normalizeModulePath(key);
93
+ if (!normalized) continue;
94
+ if (!index.has(normalized)) index.set(normalized, key);
95
+ for (let i = normalized.indexOf("/"); i !== -1; i = normalized.indexOf("/", i + 1)) {
96
+ const suffix = normalized.slice(i + 1);
97
+ if (suffix && !index.has(suffix)) index.set(suffix, key);
98
+ }
99
+ }
100
+ return index;
101
+ }
102
+ const suffixIndexCache = /* @__PURE__ */ new WeakMap();
103
+ function getSuffixIndex(manifest) {
104
+ let index = suffixIndexCache.get(manifest);
105
+ if (index) return index;
106
+ index = buildSuffixIndex(manifest);
107
+ suffixIndexCache.set(manifest, index);
108
+ return index;
109
+ }
110
+ function resolveManifestEntries(manifest, file) {
111
+ if (file in manifest) return manifest[file];
112
+ const resolved = getSuffixIndex(manifest).get(normalizeModulePath(file));
113
+ if (resolved) return manifest[resolved];
114
+ }
115
+ function resolvePageUrlsFromManifest(manifest, shellFile, routeFile) {
116
+ const urls = /* @__PURE__ */ new Set();
117
+ const add = (file) => {
118
+ const entries = resolveManifestEntries(manifest, file);
119
+ if (entries) for (const url of entries) urls.add(url);
120
+ };
121
+ if (shellFile) add(shellFile);
122
+ add(routeFile);
123
+ return [...urls];
124
+ }
125
+ function resolvePageCssUrls(cssManifest, shellFile, routeFile) {
126
+ if (!cssManifest) return [];
127
+ return resolvePageUrlsFromManifest(cssManifest, shellFile, routeFile);
128
+ }
129
+ function resolvePageJsUrls(jsManifest, shellFile, routeFile) {
130
+ if (!jsManifest) return [];
131
+ return resolvePageUrlsFromManifest(jsManifest, shellFile, routeFile);
132
+ }
133
+ async function resolveRegistryModule(modules, file) {
134
+ if (!modules) return void 0;
135
+ if (file in modules) return modules[file]();
136
+ const resolved = getSuffixIndex(modules).get(normalizeModulePath(file));
137
+ if (resolved) return modules[resolved]();
138
+ }
139
+ async function resolveDataFunctions(route, routeModule, registry) {
140
+ let loader = routeModule?.loader;
141
+ let loaderFile = routeModule?.loader ? route.file : void 0;
142
+ if (route.loaderFile) {
143
+ const dataModule = await resolveRegistryModule(registry.dataModules, route.loaderFile);
144
+ if (dataModule?.loader) {
145
+ loader = dataModule.loader;
146
+ loaderFile = route.loaderFile;
147
+ }
148
+ }
149
+ return {
150
+ loader,
151
+ loaderFile
152
+ };
153
+ }
154
+ //#endregion
155
+ //#region src/runtime-middleware.ts
156
+ const DEFAULT_REDIRECT_STATUS_SAFE = 302;
157
+ const DEFAULT_REDIRECT_STATUS_UNSAFE = 303;
158
+ const REDIRECT_VALIDATION_BASE = "https://invalid.pracht.local/";
159
+ /**
160
+ * Build a safe redirect response from middleware/loader output. Rejects
161
+ * non-http(s) schemes (no `javascript:`/`data:`/etc.) and CR/LF injection
162
+ * against the `Location` header. When status is omitted, non-GET/HEAD
163
+ * requests default to 303 so the browser does not resend the body to the
164
+ * redirect target; safe methods default to 302.
165
+ *
166
+ * The original `target` string is preserved on success (relative paths
167
+ * stay relative) — we only parse it to validate scheme, not to rewrite
168
+ * it. Both the original input and its resolved URL must be CR/LF-free.
169
+ */
170
+ function buildRedirectResponse(target, options) {
171
+ if (/[\r\n]/.test(target)) throw new Error("Refused redirect target containing CR/LF");
172
+ if (!parseSafeNavigationUrl(target, options.baseUrl)) throw new Error("Refused unsafe redirect target");
173
+ const method = (options.method ?? "GET").toUpperCase();
174
+ const defaultStatus = SAFE_METHODS.has(method) ? DEFAULT_REDIRECT_STATUS_SAFE : DEFAULT_REDIRECT_STATUS_UNSAFE;
175
+ const status = options.status ?? defaultStatus;
176
+ return new Response(null, {
177
+ status,
178
+ headers: { location: target }
179
+ });
180
+ }
181
+ /**
182
+ * Convenience helper for middleware (and loaders/handlers) to short-circuit
183
+ * with a redirect Response. Validates the target's scheme and rejects
184
+ * CR/LF injection. Pass the current request (or method) when the default
185
+ * status should follow HTTP method safety: safe methods default to 302,
186
+ * unsafe methods default to 303.
187
+ *
188
+ * ```ts
189
+ * export const middleware: MiddlewareFn = async ({ request }, next) => {
190
+ * if (!hasSession(request)) return redirect("/login", { request });
191
+ * return next();
192
+ * };
193
+ * ```
194
+ */
195
+ function redirect(target, options = {}) {
196
+ if (typeof options === "number") return buildRedirectResponse(target, {
197
+ baseUrl: REDIRECT_VALIDATION_BASE,
198
+ status: options
199
+ });
200
+ return buildRedirectResponse(target, {
201
+ baseUrl: options.baseUrl ?? options.request?.url ?? REDIRECT_VALIDATION_BASE,
202
+ method: options.method ?? options.request?.method,
203
+ status: options.status
204
+ });
205
+ }
206
+ /**
207
+ * Run the middleware chain wrap-around-style. Each middleware receives
208
+ * `next` and may call it at most once. Calling `next()` invokes the rest
209
+ * of the chain (downstream middleware then `terminal`) and resolves to
210
+ * the final `Response`. A middleware that returns without calling `next()`
211
+ * short-circuits with whatever Response it returned.
212
+ *
213
+ * Module imports are kicked off concurrently up front; execution stays
214
+ * sequential because middleware may mutate `args.context` and ordering
215
+ * is part of the public contract.
216
+ */
217
+ async function runMiddlewareChain(options) {
218
+ const { middlewareFiles, terminal } = options;
219
+ if (middlewareFiles.length === 0) return terminal();
220
+ const modulePromises = middlewareFiles.map((mwFile) => resolveRegistryModule(options.registry.middlewareModules, mwFile));
221
+ for (const p of modulePromises) p.catch(() => {});
222
+ const dispatch = async (i) => {
223
+ if (i >= middlewareFiles.length) return terminal();
224
+ const mwModule = await modulePromises[i];
225
+ if (!mwModule?.middleware) return dispatch(i + 1);
226
+ let calledNext = false;
227
+ const next = () => {
228
+ if (calledNext) throw new Error(`Middleware "${middlewareFiles[i]}" called next() multiple times`);
229
+ calledNext = true;
230
+ return dispatch(i + 1);
231
+ };
232
+ const args = {
233
+ request: options.request,
234
+ params: options.params,
235
+ context: options.context,
236
+ signal: options.signal,
237
+ url: options.url,
238
+ route: options.route
239
+ };
240
+ const response = await mwModule.middleware(args, next);
241
+ if (!(response instanceof Response)) throw new Error(`Middleware "${middlewareFiles[i]}" did not return a Response. Middleware must return the result of next() or a short-circuit Response.`);
242
+ return response;
243
+ };
244
+ return dispatch(0);
245
+ }
246
+ async function mergeHeadMetadata(shellModule, routeModule, routeArgs, data) {
247
+ const [shellHead, routeHead] = await Promise.all([shellModule?.head ? shellModule.head(routeArgs) : Promise.resolve({}), routeModule?.head ? routeModule.head({
248
+ ...routeArgs,
249
+ data
250
+ }) : Promise.resolve({})]);
251
+ return {
252
+ title: routeHead.title ?? shellHead.title,
253
+ lang: routeHead.lang ?? shellHead.lang,
254
+ meta: [...shellHead.meta ?? [], ...routeHead.meta ?? []],
255
+ link: [...shellHead.link ?? [], ...routeHead.link ?? []],
256
+ script: [...shellHead.script ?? [], ...routeHead.script ?? []]
257
+ };
258
+ }
259
+ async function mergeDocumentHeaders(shellModule, routeModule, routeArgs, data) {
260
+ const headers = new Headers();
261
+ const [shellHeaders, routeHeaders] = await Promise.all([shellModule?.headers ? shellModule.headers(routeArgs) : Promise.resolve(void 0), routeModule?.headers ? routeModule.headers({
262
+ ...routeArgs,
263
+ data
264
+ }) : Promise.resolve(void 0)]);
265
+ if (shellHeaders) applyHeaders(headers, shellHeaders);
266
+ if (routeHeaders) applyHeaders(headers, routeHeaders);
267
+ return headers;
268
+ }
269
+ //#endregion
270
+ //#region src/runtime-hooks.ts
271
+ function useRouteData() {
272
+ return useContext(RouteDataContext)?.data;
273
+ }
274
+ function useLocation() {
275
+ return parseLocation(useContext(RouteDataContext)?.url ?? (typeof window !== "undefined" ? window.location.pathname + window.location.search : "/"));
276
+ }
277
+ function useParams() {
278
+ return useContext(RouteDataContext)?.params ?? {};
279
+ }
280
+ function useRevalidate() {
281
+ const runtime = useContext(RouteDataContext);
282
+ return async () => {
283
+ if (typeof window === "undefined") return;
284
+ const result = await fetchPrachtRouteState(runtime?.url || window.location.pathname + window.location.search);
285
+ if (result.type === "redirect") {
286
+ await navigateToClientLocation(result.location);
287
+ return;
288
+ }
289
+ if (result.type === "error") throw deserializeRouteError(result.error);
290
+ runtime?.setData(result.data);
291
+ return result.data;
292
+ };
293
+ }
294
+ function Link(props) {
295
+ const routes = useContext(RouteDataContext)?.routes ?? globalThis.__PRACHT_ROUTE_DEFINITIONS__;
296
+ if (!routes) throw new Error("<Link route=...> must render inside a pracht route tree.");
297
+ const { route, params, search, hash, ...anchorProps } = props;
298
+ return h("a", {
299
+ ...anchorProps,
300
+ href: buildHref(routes, route, {
301
+ params,
302
+ search,
303
+ hash
304
+ })
305
+ });
306
+ }
307
+ function Form(props) {
308
+ const { onSubmit, method, ...rest } = props;
309
+ return h("form", {
310
+ ...rest,
311
+ method,
312
+ onSubmit: async (event) => {
313
+ onSubmit?.(event);
314
+ if (event.defaultPrevented) return;
315
+ const form = event.currentTarget;
316
+ if (!(form instanceof HTMLFormElement)) return;
317
+ const formMethod = (method ?? form.method ?? "post").toUpperCase();
318
+ if (SAFE_METHODS.has(formMethod)) return;
319
+ event.preventDefault();
320
+ clearPrefetchCache();
321
+ const response = await fetch(props.action ?? form.action, {
322
+ method: formMethod,
323
+ body: new FormData(form),
324
+ redirect: "manual"
325
+ });
326
+ if (response.type === "opaqueredirect" || response.status >= 300 && response.status < 400) {
327
+ const location = response.headers.get("location");
328
+ if (location) {
329
+ await navigateToClientLocation(location);
330
+ return;
331
+ }
332
+ window.location.href = props.action ?? form.action;
333
+ }
334
+ }
335
+ });
336
+ }
337
+ function parseLocation(value) {
338
+ const url = new URL(value, "http://pracht.local");
339
+ return {
340
+ pathname: url.pathname,
341
+ search: url.search
342
+ };
343
+ }
344
+ //#endregion
345
+ //#region src/types.ts
346
+ var PrachtHttpError = class extends Error {
347
+ status;
348
+ constructor(status, message) {
349
+ super(message);
350
+ this.name = "PrachtHttpError";
351
+ this.status = status;
352
+ }
353
+ };
354
+ //#endregion
355
+ export { createHref as S, applyDefaultSecurityHeaders as _, useParams as a, withDefaultSecurityHeaders as b, mergeDocumentHeaders as c, runMiddlewareChain as d, resolveDataFunctions as f, appendVaryHeader as g, resolveRegistryModule as h, useLocation as i, mergeHeadMetadata as l, resolvePageJsUrls as m, Form as n, useRevalidate as o, resolvePageCssUrls as p, Link as r, useRouteData as s, PrachtHttpError as t, redirect as u, applyHeaders as v, withRouteResponseHeaders as x, applySecurityAndRouteHeaders as y };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pracht/core",
3
- "version": "0.6.1",
3
+ "version": "0.8.0",
4
4
  "description": "Core runtime for Pracht, a full-stack Preact framework with per-route SSR, SSG, ISG, SPA, loaders, and API routes.",
5
5
  "keywords": [
6
6
  "pracht",
@@ -31,8 +31,25 @@
31
31
  "exports": {
32
32
  ".": {
33
33
  "types": "./dist/index.d.mts",
34
+ "browser": "./dist/browser.mjs",
34
35
  "default": "./dist/index.mjs"
35
36
  },
37
+ "./browser": {
38
+ "types": "./dist/browser.d.mts",
39
+ "default": "./dist/browser.mjs"
40
+ },
41
+ "./client": {
42
+ "types": "./dist/client.d.mts",
43
+ "default": "./dist/client.mjs"
44
+ },
45
+ "./manifest": {
46
+ "types": "./dist/manifest.d.mts",
47
+ "default": "./dist/manifest.mjs"
48
+ },
49
+ "./server": {
50
+ "types": "./dist/server.d.mts",
51
+ "default": "./dist/server.mjs"
52
+ },
36
53
  "./error-overlay": {
37
54
  "types": "./dist/error-overlay.d.mts",
38
55
  "default": "./dist/error-overlay.mjs"
@@ -42,7 +59,7 @@
42
59
  "provenance": true
43
60
  },
44
61
  "dependencies": {
45
- "preact-suspense": "^0.2.0"
62
+ "preact-suspense": "^0.3.0"
46
63
  },
47
64
  "peerDependencies": {
48
65
  "preact": "^10.0.0",