@pracht/core 0.0.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.
- package/dist/app-CAoDWWNO.mjs +227 -0
- package/dist/error-overlay.d.mts +16 -0
- package/dist/error-overlay.mjs +112 -0
- package/dist/index.d.mts +343 -0
- package/dist/index.mjs +837 -0
- package/package.json +28 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __exportAll = (all, no_symbols) => {
|
|
4
|
+
let target = {};
|
|
5
|
+
for (var name in all) __defProp(target, name, {
|
|
6
|
+
get: all[name],
|
|
7
|
+
enumerable: true
|
|
8
|
+
});
|
|
9
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
10
|
+
return target;
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/app.ts
|
|
14
|
+
var app_exports = /* @__PURE__ */ __exportAll({
|
|
15
|
+
buildPathFromSegments: () => buildPathFromSegments,
|
|
16
|
+
defineApp: () => defineApp,
|
|
17
|
+
group: () => group,
|
|
18
|
+
matchApiRoute: () => matchApiRoute,
|
|
19
|
+
matchAppRoute: () => matchAppRoute,
|
|
20
|
+
resolveApiRoutes: () => resolveApiRoutes,
|
|
21
|
+
resolveApp: () => resolveApp,
|
|
22
|
+
route: () => route,
|
|
23
|
+
timeRevalidate: () => timeRevalidate
|
|
24
|
+
});
|
|
25
|
+
function timeRevalidate(seconds) {
|
|
26
|
+
if (!Number.isInteger(seconds) || seconds <= 0) throw new Error("timeRevalidate expects a positive integer number of seconds.");
|
|
27
|
+
return {
|
|
28
|
+
kind: "time",
|
|
29
|
+
seconds
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function route(path, fileOrConfig, meta = {}) {
|
|
33
|
+
if (typeof fileOrConfig === "string") return {
|
|
34
|
+
kind: "route",
|
|
35
|
+
path: normalizeRoutePath(path),
|
|
36
|
+
file: fileOrConfig,
|
|
37
|
+
...meta
|
|
38
|
+
};
|
|
39
|
+
const { component, loader, ...routeMeta } = fileOrConfig;
|
|
40
|
+
return {
|
|
41
|
+
kind: "route",
|
|
42
|
+
path: normalizeRoutePath(path),
|
|
43
|
+
file: component,
|
|
44
|
+
loaderFile: loader,
|
|
45
|
+
...routeMeta
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function group(meta, routes) {
|
|
49
|
+
return {
|
|
50
|
+
kind: "group",
|
|
51
|
+
meta,
|
|
52
|
+
routes
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function defineApp(config) {
|
|
56
|
+
return {
|
|
57
|
+
shells: config.shells ?? {},
|
|
58
|
+
middleware: config.middleware ?? {},
|
|
59
|
+
api: config.api ?? {},
|
|
60
|
+
routes: config.routes
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function resolveApp(app) {
|
|
64
|
+
const routes = [];
|
|
65
|
+
const inherited = {
|
|
66
|
+
pathPrefix: "/",
|
|
67
|
+
middleware: []
|
|
68
|
+
};
|
|
69
|
+
for (const node of app.routes) flattenRouteNode(app, node, inherited, routes);
|
|
70
|
+
return {
|
|
71
|
+
shells: app.shells,
|
|
72
|
+
middleware: app.middleware,
|
|
73
|
+
api: app.api,
|
|
74
|
+
routes,
|
|
75
|
+
apiRoutes: []
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function matchAppRoute(app, pathname) {
|
|
79
|
+
const resolved = isResolvedApp(app) ? app : resolveApp(app);
|
|
80
|
+
const normalizedPathname = normalizeRoutePath(pathname);
|
|
81
|
+
const targetSegments = splitPathSegments(normalizedPathname);
|
|
82
|
+
for (const currentRoute of resolved.routes) {
|
|
83
|
+
const params = matchRouteSegments(currentRoute.segments, targetSegments);
|
|
84
|
+
if (params) return {
|
|
85
|
+
route: currentRoute,
|
|
86
|
+
params,
|
|
87
|
+
pathname: normalizedPathname
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function flattenRouteNode(app, node, inherited, routes) {
|
|
92
|
+
if (node.kind === "group") {
|
|
93
|
+
const nextInherited = {
|
|
94
|
+
pathPrefix: mergeRoutePaths(inherited.pathPrefix, node.meta.pathPrefix),
|
|
95
|
+
shell: node.meta.shell ?? inherited.shell,
|
|
96
|
+
render: node.meta.render ?? inherited.render,
|
|
97
|
+
middleware: [...inherited.middleware, ...node.meta.middleware ?? []]
|
|
98
|
+
};
|
|
99
|
+
for (const child of node.routes) flattenRouteNode(app, child, nextInherited, routes);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const fullPath = mergeRoutePaths(inherited.pathPrefix, node.path);
|
|
103
|
+
const shell = node.shell ?? inherited.shell;
|
|
104
|
+
const middleware = [...inherited.middleware, ...node.middleware ?? []];
|
|
105
|
+
routes.push({
|
|
106
|
+
id: node.id ?? createRouteId(fullPath),
|
|
107
|
+
path: fullPath,
|
|
108
|
+
file: node.file,
|
|
109
|
+
loaderFile: node.loaderFile,
|
|
110
|
+
shell,
|
|
111
|
+
shellFile: shell ? app.shells[shell] : void 0,
|
|
112
|
+
render: node.render ?? inherited.render,
|
|
113
|
+
middleware,
|
|
114
|
+
middlewareFiles: middleware.flatMap((name) => {
|
|
115
|
+
const middlewareFile = app.middleware[name];
|
|
116
|
+
return middlewareFile ? [middlewareFile] : [];
|
|
117
|
+
}),
|
|
118
|
+
revalidate: node.revalidate,
|
|
119
|
+
segments: parseRouteSegments(fullPath)
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
function isResolvedApp(app) {
|
|
123
|
+
return app.routes.length === 0 || "segments" in app.routes[0];
|
|
124
|
+
}
|
|
125
|
+
function matchRouteSegments(routeSegments, targetSegments) {
|
|
126
|
+
const params = {};
|
|
127
|
+
let routeIndex = 0;
|
|
128
|
+
let targetIndex = 0;
|
|
129
|
+
while (routeIndex < routeSegments.length) {
|
|
130
|
+
const currentSegment = routeSegments[routeIndex];
|
|
131
|
+
if (currentSegment.type === "catchall") {
|
|
132
|
+
params[currentSegment.name] = targetSegments.slice(targetIndex).join("/");
|
|
133
|
+
return params;
|
|
134
|
+
}
|
|
135
|
+
const targetSegment = targetSegments[targetIndex];
|
|
136
|
+
if (typeof targetSegment === "undefined") return null;
|
|
137
|
+
if (currentSegment.type === "static") {
|
|
138
|
+
if (currentSegment.value !== targetSegment) return null;
|
|
139
|
+
} else params[currentSegment.name] = decodeURIComponent(targetSegment);
|
|
140
|
+
routeIndex += 1;
|
|
141
|
+
targetIndex += 1;
|
|
142
|
+
}
|
|
143
|
+
return targetIndex === targetSegments.length ? params : null;
|
|
144
|
+
}
|
|
145
|
+
function parseRouteSegments(path) {
|
|
146
|
+
return splitPathSegments(path).map((segment) => {
|
|
147
|
+
if (segment === "*") return {
|
|
148
|
+
type: "catchall",
|
|
149
|
+
name: "*"
|
|
150
|
+
};
|
|
151
|
+
if (segment.startsWith(":")) return {
|
|
152
|
+
type: "param",
|
|
153
|
+
name: segment.slice(1)
|
|
154
|
+
};
|
|
155
|
+
return {
|
|
156
|
+
type: "static",
|
|
157
|
+
value: segment
|
|
158
|
+
};
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
function splitPathSegments(path) {
|
|
162
|
+
return normalizeRoutePath(path).split("/").filter(Boolean);
|
|
163
|
+
}
|
|
164
|
+
function mergeRoutePaths(prefix, path) {
|
|
165
|
+
if (!path) return normalizeRoutePath(prefix);
|
|
166
|
+
const normalizedPrefix = normalizeRoutePath(prefix);
|
|
167
|
+
const normalizedPath = normalizeRoutePath(path);
|
|
168
|
+
if (normalizedPrefix === "/") return normalizedPath;
|
|
169
|
+
if (normalizedPath === "/") return normalizedPrefix;
|
|
170
|
+
return normalizeRoutePath(`${normalizedPrefix}/${normalizedPath.slice(1)}`);
|
|
171
|
+
}
|
|
172
|
+
function normalizeRoutePath(path) {
|
|
173
|
+
if (!path || path === "/") return "/";
|
|
174
|
+
const collapsed = (path.startsWith("/") ? path : `/${path}`).replace(/\/{2,}/g, "/");
|
|
175
|
+
return collapsed.length > 1 && collapsed.endsWith("/") ? collapsed.slice(0, -1) : collapsed;
|
|
176
|
+
}
|
|
177
|
+
function buildPathFromSegments(segments, params) {
|
|
178
|
+
return normalizeRoutePath("/" + segments.map((segment) => {
|
|
179
|
+
if (segment.type === "static") return segment.value;
|
|
180
|
+
if (segment.type === "param") return encodeURIComponent(params[segment.name] ?? "");
|
|
181
|
+
return params["*"] ?? "";
|
|
182
|
+
}).join("/"));
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Convert a list of file paths from `import.meta.glob` into resolved API routes.
|
|
186
|
+
*
|
|
187
|
+
* Example: `"/src/api/health.ts"` → path `/api/health`
|
|
188
|
+
* `"/src/api/users/[id].ts"` → path `/api/users/:id`
|
|
189
|
+
* `"/src/api/index.ts"` → path `/api`
|
|
190
|
+
*/
|
|
191
|
+
function resolveApiRoutes(files, apiDir = "/src/api") {
|
|
192
|
+
const normalizedDir = apiDir.replace(/\/$/, "");
|
|
193
|
+
return files.map((file) => {
|
|
194
|
+
let relative = file;
|
|
195
|
+
if (relative.startsWith(normalizedDir)) relative = relative.slice(normalizedDir.length);
|
|
196
|
+
relative = relative.replace(/\.(ts|tsx|js|jsx)$/, "");
|
|
197
|
+
if (relative.endsWith("/index")) relative = relative.slice(0, -6) || "/";
|
|
198
|
+
relative = relative.replace(/\[([^\]]+)\]/g, ":$1");
|
|
199
|
+
const path = normalizeRoutePath(`/api${relative}`);
|
|
200
|
+
return {
|
|
201
|
+
path,
|
|
202
|
+
file,
|
|
203
|
+
segments: parseRouteSegments(path)
|
|
204
|
+
};
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
function matchApiRoute(apiRoutes, pathname) {
|
|
208
|
+
const normalizedPathname = normalizeRoutePath(pathname);
|
|
209
|
+
const targetSegments = splitPathSegments(normalizedPathname);
|
|
210
|
+
for (const route of apiRoutes) {
|
|
211
|
+
const params = matchRouteSegments(route.segments, targetSegments);
|
|
212
|
+
if (params) return {
|
|
213
|
+
route,
|
|
214
|
+
params,
|
|
215
|
+
pathname: normalizedPathname
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function createRouteId(path) {
|
|
220
|
+
if (path === "/") return "index";
|
|
221
|
+
return path.slice(1).split("/").map((segment) => {
|
|
222
|
+
if (segment === "*") return "splat";
|
|
223
|
+
return segment.startsWith(":") ? segment.slice(1) : segment;
|
|
224
|
+
}).join("-").replace(/[^a-zA-Z0-9-]/g, "-");
|
|
225
|
+
}
|
|
226
|
+
//#endregion
|
|
227
|
+
export { matchApiRoute as a, resolveApp as c, group as i, route as l, buildPathFromSegments as n, matchAppRoute as o, defineApp as r, resolveApiRoutes as s, app_exports as t, timeRevalidate as u };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/error-overlay.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Self-contained error overlay for pracht dev mode.
|
|
4
|
+
*
|
|
5
|
+
* Returns a standalone HTML document with inline styles and scripts.
|
|
6
|
+
* Not a Preact component — must render even when Preact itself fails.
|
|
7
|
+
*/
|
|
8
|
+
interface ErrorOverlayOptions {
|
|
9
|
+
message: string;
|
|
10
|
+
stack?: string;
|
|
11
|
+
routeId?: string;
|
|
12
|
+
file?: string;
|
|
13
|
+
}
|
|
14
|
+
declare function buildErrorOverlayHtml(options: ErrorOverlayOptions): string;
|
|
15
|
+
//#endregion
|
|
16
|
+
export { ErrorOverlayOptions, buildErrorOverlayHtml };
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
//#region src/error-overlay.ts
|
|
2
|
+
function buildErrorOverlayHtml(options) {
|
|
3
|
+
const { message, stack, routeId, file } = options;
|
|
4
|
+
const stackHtml = stack ? `<pre class="stack">${escapeHtml(stack)}</pre>` : "";
|
|
5
|
+
const routeHtml = routeId ? `<div class="meta"><span class="label">Route</span> <span class="value">${escapeHtml(routeId)}</span></div>` : "";
|
|
6
|
+
const fileHtml = file ? `<div class="meta"><span class="label">File</span> <span class="value">${escapeHtml(file)}</span></div>` : "";
|
|
7
|
+
return `<!DOCTYPE html>
|
|
8
|
+
<html>
|
|
9
|
+
<head>
|
|
10
|
+
<meta charset="utf-8">
|
|
11
|
+
<title>pracht error</title>
|
|
12
|
+
<style>
|
|
13
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
14
|
+
body {
|
|
15
|
+
font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas, monospace;
|
|
16
|
+
background: #1a1a2e;
|
|
17
|
+
color: #e0e0e0;
|
|
18
|
+
padding: 32px;
|
|
19
|
+
line-height: 1.5;
|
|
20
|
+
}
|
|
21
|
+
.overlay {
|
|
22
|
+
max-width: 900px;
|
|
23
|
+
margin: 0 auto;
|
|
24
|
+
}
|
|
25
|
+
.header {
|
|
26
|
+
display: flex;
|
|
27
|
+
align-items: center;
|
|
28
|
+
gap: 12px;
|
|
29
|
+
margin-bottom: 24px;
|
|
30
|
+
padding-bottom: 16px;
|
|
31
|
+
border-bottom: 1px solid #333;
|
|
32
|
+
}
|
|
33
|
+
.badge {
|
|
34
|
+
background: #e74c3c;
|
|
35
|
+
color: #fff;
|
|
36
|
+
font-size: 11px;
|
|
37
|
+
font-weight: 700;
|
|
38
|
+
text-transform: uppercase;
|
|
39
|
+
letter-spacing: 0.05em;
|
|
40
|
+
padding: 4px 10px;
|
|
41
|
+
border-radius: 4px;
|
|
42
|
+
}
|
|
43
|
+
.title {
|
|
44
|
+
font-size: 14px;
|
|
45
|
+
color: #888;
|
|
46
|
+
}
|
|
47
|
+
.message {
|
|
48
|
+
font-size: 18px;
|
|
49
|
+
font-weight: 600;
|
|
50
|
+
color: #ff6b6b;
|
|
51
|
+
margin-bottom: 20px;
|
|
52
|
+
word-break: break-word;
|
|
53
|
+
}
|
|
54
|
+
.meta {
|
|
55
|
+
font-size: 13px;
|
|
56
|
+
margin-bottom: 6px;
|
|
57
|
+
}
|
|
58
|
+
.meta .label {
|
|
59
|
+
color: #888;
|
|
60
|
+
margin-right: 8px;
|
|
61
|
+
}
|
|
62
|
+
.meta .value {
|
|
63
|
+
color: #a0c4ff;
|
|
64
|
+
}
|
|
65
|
+
.stack {
|
|
66
|
+
background: #16162a;
|
|
67
|
+
border: 1px solid #333;
|
|
68
|
+
border-radius: 8px;
|
|
69
|
+
padding: 20px;
|
|
70
|
+
margin-top: 20px;
|
|
71
|
+
font-size: 13px;
|
|
72
|
+
line-height: 1.7;
|
|
73
|
+
overflow-x: auto;
|
|
74
|
+
white-space: pre-wrap;
|
|
75
|
+
word-break: break-word;
|
|
76
|
+
color: #ccc;
|
|
77
|
+
}
|
|
78
|
+
.hint {
|
|
79
|
+
margin-top: 24px;
|
|
80
|
+
font-size: 12px;
|
|
81
|
+
color: #666;
|
|
82
|
+
}
|
|
83
|
+
</style>
|
|
84
|
+
</head>
|
|
85
|
+
<body>
|
|
86
|
+
<div class="overlay">
|
|
87
|
+
<div class="header">
|
|
88
|
+
<span class="badge">Error</span>
|
|
89
|
+
<span class="title">pracht dev</span>
|
|
90
|
+
</div>
|
|
91
|
+
<div class="message">${escapeHtml(message)}</div>
|
|
92
|
+
${routeHtml}
|
|
93
|
+
${fileHtml}
|
|
94
|
+
${stackHtml}
|
|
95
|
+
<div class="hint">Fix the error and save — the page will reload automatically.</div>
|
|
96
|
+
</div>
|
|
97
|
+
<script>
|
|
98
|
+
// Auto-reload when Vite triggers a full reload (e.g. file saved after fix)
|
|
99
|
+
if (import.meta.hot) {
|
|
100
|
+
import.meta.hot.on("vite:beforeFullReload", function () {
|
|
101
|
+
window.location.reload();
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
<\/script>
|
|
105
|
+
</body>
|
|
106
|
+
</html>`;
|
|
107
|
+
}
|
|
108
|
+
function escapeHtml(str) {
|
|
109
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
110
|
+
}
|
|
111
|
+
//#endregion
|
|
112
|
+
export { buildErrorOverlayHtml };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import { Suspense, lazy } from "preact-suspense";
|
|
2
|
+
import * as _$preact from "preact";
|
|
3
|
+
import { ComponentChildren, FunctionComponent, JSX, h } from "preact";
|
|
4
|
+
|
|
5
|
+
//#region src/types.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Augment this interface to register your app's context type globally.
|
|
8
|
+
* Once registered, all route args (`BaseRouteArgs`, `LoaderArgs`, etc.)
|
|
9
|
+
* will use your context type automatically — no per-file generics needed.
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* // src/env.d.ts
|
|
13
|
+
* declare module "@pracht/core" {
|
|
14
|
+
* interface Register {
|
|
15
|
+
* context: { env: Env; executionContext: ExecutionContext };
|
|
16
|
+
* }
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
interface Register {}
|
|
21
|
+
type RegisteredContext = Register extends {
|
|
22
|
+
context: infer T;
|
|
23
|
+
} ? T : unknown;
|
|
24
|
+
type RenderMode = "spa" | "ssr" | "ssg" | "isg";
|
|
25
|
+
type RouteParams = Record<string, string>;
|
|
26
|
+
interface TimeRevalidatePolicy {
|
|
27
|
+
kind: "time";
|
|
28
|
+
seconds: number;
|
|
29
|
+
}
|
|
30
|
+
type RouteRevalidate = TimeRevalidatePolicy;
|
|
31
|
+
type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
|
|
32
|
+
type ApiRouteHandler<TContext = RegisteredContext> = (args: BaseRouteArgs<TContext>) => MaybePromise<Response>;
|
|
33
|
+
interface ApiRouteModule<TContext = any> {
|
|
34
|
+
GET?: ApiRouteHandler<TContext>;
|
|
35
|
+
POST?: ApiRouteHandler<TContext>;
|
|
36
|
+
PUT?: ApiRouteHandler<TContext>;
|
|
37
|
+
PATCH?: ApiRouteHandler<TContext>;
|
|
38
|
+
DELETE?: ApiRouteHandler<TContext>;
|
|
39
|
+
HEAD?: ApiRouteHandler<TContext>;
|
|
40
|
+
OPTIONS?: ApiRouteHandler<TContext>;
|
|
41
|
+
}
|
|
42
|
+
interface ResolvedApiRoute {
|
|
43
|
+
path: string;
|
|
44
|
+
file: string;
|
|
45
|
+
segments: RouteSegment[];
|
|
46
|
+
}
|
|
47
|
+
interface ApiRouteMatch {
|
|
48
|
+
route: ResolvedApiRoute;
|
|
49
|
+
params: RouteParams;
|
|
50
|
+
pathname: string;
|
|
51
|
+
}
|
|
52
|
+
type PrefetchStrategy = "none" | "hover" | "viewport" | "intent";
|
|
53
|
+
interface RouteMeta {
|
|
54
|
+
id?: string;
|
|
55
|
+
shell?: string;
|
|
56
|
+
render?: RenderMode;
|
|
57
|
+
middleware?: string[];
|
|
58
|
+
revalidate?: RouteRevalidate;
|
|
59
|
+
prefetch?: PrefetchStrategy;
|
|
60
|
+
}
|
|
61
|
+
interface GroupMeta {
|
|
62
|
+
shell?: string;
|
|
63
|
+
render?: RenderMode;
|
|
64
|
+
middleware?: string[];
|
|
65
|
+
pathPrefix?: string;
|
|
66
|
+
}
|
|
67
|
+
interface ApiConfig {
|
|
68
|
+
middleware?: string[];
|
|
69
|
+
}
|
|
70
|
+
interface RouteConfig extends RouteMeta {
|
|
71
|
+
component: string;
|
|
72
|
+
loader?: string;
|
|
73
|
+
}
|
|
74
|
+
interface RouteDefinition extends RouteMeta {
|
|
75
|
+
kind: "route";
|
|
76
|
+
path: string;
|
|
77
|
+
file: string;
|
|
78
|
+
loaderFile?: string;
|
|
79
|
+
}
|
|
80
|
+
interface GroupDefinition {
|
|
81
|
+
kind: "group";
|
|
82
|
+
meta: GroupMeta;
|
|
83
|
+
routes: RouteTreeNode[];
|
|
84
|
+
}
|
|
85
|
+
type RouteTreeNode = RouteDefinition | GroupDefinition;
|
|
86
|
+
interface PrachtAppConfig {
|
|
87
|
+
shells?: Record<string, string>;
|
|
88
|
+
middleware?: Record<string, string>;
|
|
89
|
+
api?: ApiConfig;
|
|
90
|
+
routes: RouteTreeNode[];
|
|
91
|
+
}
|
|
92
|
+
interface PrachtApp {
|
|
93
|
+
shells: Record<string, string>;
|
|
94
|
+
middleware: Record<string, string>;
|
|
95
|
+
api: ApiConfig;
|
|
96
|
+
routes: RouteTreeNode[];
|
|
97
|
+
}
|
|
98
|
+
interface StaticRouteSegment {
|
|
99
|
+
type: "static";
|
|
100
|
+
value: string;
|
|
101
|
+
}
|
|
102
|
+
interface ParamRouteSegment {
|
|
103
|
+
type: "param";
|
|
104
|
+
name: string;
|
|
105
|
+
}
|
|
106
|
+
interface CatchAllRouteSegment {
|
|
107
|
+
type: "catchall";
|
|
108
|
+
name: "*";
|
|
109
|
+
}
|
|
110
|
+
type RouteSegment = StaticRouteSegment | ParamRouteSegment | CatchAllRouteSegment;
|
|
111
|
+
interface ResolvedRoute extends Omit<RouteMeta, "middleware"> {
|
|
112
|
+
path: string;
|
|
113
|
+
file: string;
|
|
114
|
+
loaderFile?: string;
|
|
115
|
+
shell?: string;
|
|
116
|
+
shellFile?: string;
|
|
117
|
+
middleware: string[];
|
|
118
|
+
middlewareFiles: string[];
|
|
119
|
+
segments: RouteSegment[];
|
|
120
|
+
}
|
|
121
|
+
interface ResolvedPrachtApp extends Omit<PrachtApp, "routes"> {
|
|
122
|
+
routes: ResolvedRoute[];
|
|
123
|
+
apiRoutes: ResolvedApiRoute[];
|
|
124
|
+
}
|
|
125
|
+
interface RouteMatch {
|
|
126
|
+
route: ResolvedRoute;
|
|
127
|
+
params: RouteParams;
|
|
128
|
+
pathname: string;
|
|
129
|
+
}
|
|
130
|
+
interface BaseRouteArgs<TContext = RegisteredContext> {
|
|
131
|
+
request: Request;
|
|
132
|
+
params: RouteParams;
|
|
133
|
+
context: TContext;
|
|
134
|
+
signal: AbortSignal;
|
|
135
|
+
url: URL;
|
|
136
|
+
route: ResolvedRoute;
|
|
137
|
+
}
|
|
138
|
+
interface LoaderArgs<TContext = RegisteredContext> extends BaseRouteArgs<TContext> {}
|
|
139
|
+
interface MiddlewareArgs<TContext = RegisteredContext> extends BaseRouteArgs<TContext> {}
|
|
140
|
+
interface HeadMetadata {
|
|
141
|
+
title?: string;
|
|
142
|
+
lang?: string;
|
|
143
|
+
meta?: Array<Record<string, string>>;
|
|
144
|
+
link?: Array<Record<string, string>>;
|
|
145
|
+
}
|
|
146
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
147
|
+
type LoaderLike = ((args: LoaderArgs<any>) => unknown) | undefined;
|
|
148
|
+
type LoaderData<TLoader extends LoaderLike> = TLoader extends ((...args: any[]) => infer TResult) ? Awaited<TResult> : never;
|
|
149
|
+
interface HeadArgs<TLoader extends LoaderLike = undefined, TContext = any> extends BaseRouteArgs<TContext> {
|
|
150
|
+
data: LoaderData<TLoader>;
|
|
151
|
+
}
|
|
152
|
+
interface RouteComponentProps<TLoader extends LoaderLike = undefined> {
|
|
153
|
+
data: LoaderData<TLoader>;
|
|
154
|
+
params: RouteParams;
|
|
155
|
+
}
|
|
156
|
+
interface ErrorBoundaryProps {
|
|
157
|
+
error: Error;
|
|
158
|
+
}
|
|
159
|
+
interface ShellProps {
|
|
160
|
+
children: ComponentChildren;
|
|
161
|
+
}
|
|
162
|
+
type LoaderFn<TContext = any, TData = unknown> = (args: LoaderArgs<TContext>) => MaybePromise<TData>;
|
|
163
|
+
interface RouteModule<TContext = any, TLoader extends LoaderLike = undefined> {
|
|
164
|
+
loader?: LoaderFn<TContext>;
|
|
165
|
+
head?: (args: HeadArgs<TLoader, TContext>) => MaybePromise<HeadMetadata>;
|
|
166
|
+
Component: FunctionComponent<RouteComponentProps<TLoader>>;
|
|
167
|
+
ErrorBoundary?: FunctionComponent<ErrorBoundaryProps>;
|
|
168
|
+
getStaticPaths?: () => MaybePromise<RouteParams[]>;
|
|
169
|
+
}
|
|
170
|
+
interface ShellModule<TContext = any> {
|
|
171
|
+
Shell: FunctionComponent<ShellProps>;
|
|
172
|
+
head?: (args: BaseRouteArgs<TContext>) => MaybePromise<HeadMetadata>;
|
|
173
|
+
}
|
|
174
|
+
type MiddlewareResult<TContext = any> = void | Response | {
|
|
175
|
+
redirect: string;
|
|
176
|
+
} | {
|
|
177
|
+
context: Partial<TContext>;
|
|
178
|
+
};
|
|
179
|
+
type MiddlewareFn<TContext = any> = (args: MiddlewareArgs<TContext>) => MaybePromise<MiddlewareResult<TContext>>;
|
|
180
|
+
interface MiddlewareModule<TContext = any> {
|
|
181
|
+
middleware: MiddlewareFn<TContext>;
|
|
182
|
+
}
|
|
183
|
+
type ModuleImporter<TModule = unknown> = () => Promise<TModule>;
|
|
184
|
+
interface DataModule<TContext = any> {
|
|
185
|
+
loader?: LoaderFn<TContext>;
|
|
186
|
+
}
|
|
187
|
+
interface ModuleRegistry {
|
|
188
|
+
routeModules?: Record<string, ModuleImporter<RouteModule>>;
|
|
189
|
+
shellModules?: Record<string, ModuleImporter<ShellModule>>;
|
|
190
|
+
middlewareModules?: Record<string, ModuleImporter<MiddlewareModule>>;
|
|
191
|
+
apiModules?: Record<string, ModuleImporter<ApiRouteModule>>;
|
|
192
|
+
dataModules?: Record<string, ModuleImporter<DataModule>>;
|
|
193
|
+
}
|
|
194
|
+
declare class PrachtHttpError extends Error {
|
|
195
|
+
readonly status: number;
|
|
196
|
+
constructor(status: number, message: string);
|
|
197
|
+
}
|
|
198
|
+
//#endregion
|
|
199
|
+
//#region src/app.d.ts
|
|
200
|
+
declare function timeRevalidate(seconds: number): TimeRevalidatePolicy;
|
|
201
|
+
declare function route(path: string, file: string, meta?: RouteMeta): RouteDefinition;
|
|
202
|
+
declare function route(path: string, config: RouteConfig): RouteDefinition;
|
|
203
|
+
declare function group(meta: GroupMeta, routes: RouteTreeNode[]): GroupDefinition;
|
|
204
|
+
declare function defineApp(config: PrachtAppConfig): PrachtApp;
|
|
205
|
+
declare function resolveApp(app: PrachtApp): ResolvedPrachtApp;
|
|
206
|
+
declare function matchAppRoute(app: PrachtApp | ResolvedPrachtApp, pathname: string): RouteMatch | undefined;
|
|
207
|
+
declare function buildPathFromSegments(segments: RouteSegment[], params: RouteParams): string;
|
|
208
|
+
/**
|
|
209
|
+
* Convert a list of file paths from `import.meta.glob` into resolved API routes.
|
|
210
|
+
*
|
|
211
|
+
* Example: `"/src/api/health.ts"` → path `/api/health`
|
|
212
|
+
* `"/src/api/users/[id].ts"` → path `/api/users/:id`
|
|
213
|
+
* `"/src/api/index.ts"` → path `/api`
|
|
214
|
+
*/
|
|
215
|
+
declare function resolveApiRoutes(files: string[], apiDir?: string): ResolvedApiRoute[];
|
|
216
|
+
declare function matchApiRoute(apiRoutes: ResolvedApiRoute[], pathname: string): ApiRouteMatch | undefined;
|
|
217
|
+
//#endregion
|
|
218
|
+
//#region src/runtime.d.ts
|
|
219
|
+
interface PrachtHydrationState<TData = unknown> {
|
|
220
|
+
url: string;
|
|
221
|
+
routeId: string;
|
|
222
|
+
data: TData;
|
|
223
|
+
error?: SerializedRouteError | null;
|
|
224
|
+
}
|
|
225
|
+
interface SerializedRouteError {
|
|
226
|
+
message: string;
|
|
227
|
+
name: string;
|
|
228
|
+
status: number;
|
|
229
|
+
}
|
|
230
|
+
interface StartAppOptions<TData = unknown> {
|
|
231
|
+
initialData?: TData;
|
|
232
|
+
}
|
|
233
|
+
interface HandlePrachtRequestOptions<TContext = unknown> {
|
|
234
|
+
app: PrachtApp;
|
|
235
|
+
request: Request;
|
|
236
|
+
context?: TContext;
|
|
237
|
+
registry?: ModuleRegistry;
|
|
238
|
+
clientEntryUrl?: string;
|
|
239
|
+
/** Per-source-file CSS map produced by the vite plugin (preferred over cssUrls). */
|
|
240
|
+
cssManifest?: Record<string, string[]>;
|
|
241
|
+
/** @deprecated Pass cssManifest instead for per-page CSS resolution. */
|
|
242
|
+
cssUrls?: string[];
|
|
243
|
+
/** Per-source-file JS chunk map produced by the vite plugin for modulepreload hints. */
|
|
244
|
+
jsManifest?: Record<string, string[]>;
|
|
245
|
+
apiRoutes?: ResolvedApiRoute[];
|
|
246
|
+
}
|
|
247
|
+
interface FormProps extends Omit<JSX.HTMLAttributes<HTMLFormElement>, "action" | "method"> {
|
|
248
|
+
action?: string;
|
|
249
|
+
method?: string;
|
|
250
|
+
}
|
|
251
|
+
declare global {
|
|
252
|
+
interface Window {
|
|
253
|
+
__PRACHT_STATE__?: PrachtHydrationState;
|
|
254
|
+
__PRACHT_NAVIGATE__?: (to: string, options?: {
|
|
255
|
+
replace?: boolean;
|
|
256
|
+
}) => Promise<void>;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
interface PrachtRuntimeValue {
|
|
260
|
+
data: unknown;
|
|
261
|
+
params: RouteParams;
|
|
262
|
+
routeId: string;
|
|
263
|
+
url: string;
|
|
264
|
+
setData: (data: unknown) => void;
|
|
265
|
+
}
|
|
266
|
+
declare function PrachtRuntimeProvider<TData>({
|
|
267
|
+
children,
|
|
268
|
+
data,
|
|
269
|
+
params,
|
|
270
|
+
routeId,
|
|
271
|
+
url
|
|
272
|
+
}: {
|
|
273
|
+
children: ComponentChildren;
|
|
274
|
+
data: TData;
|
|
275
|
+
params?: RouteParams;
|
|
276
|
+
routeId: string;
|
|
277
|
+
url: string;
|
|
278
|
+
}): _$preact.VNode<_$preact.Attributes & {
|
|
279
|
+
value: PrachtRuntimeValue | undefined;
|
|
280
|
+
children?: ComponentChildren;
|
|
281
|
+
}>;
|
|
282
|
+
declare function startApp<TData = unknown>(options?: StartAppOptions<TData>): TData | undefined;
|
|
283
|
+
declare function readHydrationState<TData = unknown>(): PrachtHydrationState<TData> | undefined;
|
|
284
|
+
declare function useRouteData<TData = unknown>(): TData;
|
|
285
|
+
interface Location {
|
|
286
|
+
pathname: string;
|
|
287
|
+
}
|
|
288
|
+
declare function useLocation(): Location;
|
|
289
|
+
declare function useParams(): RouteParams;
|
|
290
|
+
declare function useRevalidate(): () => Promise<unknown>;
|
|
291
|
+
/** @deprecated Use useRevalidate instead. */
|
|
292
|
+
declare const useRevalidateRoute: typeof useRevalidate;
|
|
293
|
+
declare function Form(props: FormProps): _$preact.VNode<_$preact.ClassAttributes<HTMLFormElement> & h.JSX.HTMLAttributes<HTMLFormElement>>;
|
|
294
|
+
declare function handlePrachtRequest<TContext>(options: HandlePrachtRequestOptions<TContext>): Promise<Response>;
|
|
295
|
+
declare function applyDefaultSecurityHeaders(headers: Headers): Headers;
|
|
296
|
+
interface PrerenderResult {
|
|
297
|
+
path: string;
|
|
298
|
+
html: string;
|
|
299
|
+
}
|
|
300
|
+
interface ISGManifestEntry {
|
|
301
|
+
revalidate: RouteRevalidate;
|
|
302
|
+
}
|
|
303
|
+
interface PrerenderAppResult {
|
|
304
|
+
pages: PrerenderResult[];
|
|
305
|
+
isgManifest: Record<string, ISGManifestEntry>;
|
|
306
|
+
}
|
|
307
|
+
interface PrerenderAppOptions {
|
|
308
|
+
app: PrachtApp;
|
|
309
|
+
registry?: ModuleRegistry;
|
|
310
|
+
clientEntryUrl?: string;
|
|
311
|
+
/** Per-source-file CSS map produced by the vite plugin (preferred over cssUrls). */
|
|
312
|
+
cssManifest?: Record<string, string[]>;
|
|
313
|
+
/** @deprecated Pass cssManifest instead for per-page CSS resolution. */
|
|
314
|
+
cssUrls?: string[];
|
|
315
|
+
}
|
|
316
|
+
declare function prerenderApp(options: PrerenderAppOptions): Promise<PrerenderResult[]>;
|
|
317
|
+
declare function prerenderApp(options: PrerenderAppOptions & {
|
|
318
|
+
withISGManifest: true;
|
|
319
|
+
}): Promise<PrerenderAppResult>;
|
|
320
|
+
//#endregion
|
|
321
|
+
//#region src/router.d.ts
|
|
322
|
+
declare global {
|
|
323
|
+
interface Window {
|
|
324
|
+
__PRACHT_NAVIGATE__?: NavigateFn;
|
|
325
|
+
__PRACHT_ROUTER_READY__?: boolean;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
type ModuleMap = Record<string, () => Promise<any>>;
|
|
329
|
+
type NavigateFn = (to: string, options?: {
|
|
330
|
+
replace?: boolean;
|
|
331
|
+
}) => Promise<void>;
|
|
332
|
+
declare function useNavigate(): NavigateFn;
|
|
333
|
+
interface InitClientRouterOptions {
|
|
334
|
+
app: ResolvedPrachtApp;
|
|
335
|
+
routeModules: ModuleMap;
|
|
336
|
+
shellModules: ModuleMap;
|
|
337
|
+
initialState: PrachtHydrationState;
|
|
338
|
+
root: HTMLElement;
|
|
339
|
+
findModuleKey: (modules: ModuleMap, file: string) => string | null;
|
|
340
|
+
}
|
|
341
|
+
declare function initClientRouter(options: InitClientRouterOptions): Promise<void>;
|
|
342
|
+
//#endregion
|
|
343
|
+
export { type ApiConfig, type ApiRouteHandler, type ApiRouteMatch, type ApiRouteModule, type BaseRouteArgs, type DataModule, type ErrorBoundaryProps, Form, type FormProps, type GroupDefinition, type GroupMeta, type HandlePrachtRequestOptions, type HeadArgs, type HeadMetadata, type HttpMethod, type ISGManifestEntry, type InitClientRouterOptions, type LoaderArgs, type LoaderData, type LoaderFn, type Location, type MiddlewareArgs, type MiddlewareFn, type MiddlewareModule, type MiddlewareResult, type ModuleImporter, type ModuleRegistry, type NavigateFn, type PrachtApp, type PrachtAppConfig, PrachtHttpError, type PrachtHydrationState, PrachtRuntimeProvider, type PrefetchStrategy, type PrerenderAppOptions, type PrerenderAppResult, type PrerenderResult, type Register, type RenderMode, type ResolvedApiRoute, type ResolvedPrachtApp, type ResolvedRoute, type RouteComponentProps, type RouteConfig, type RouteDefinition, type RouteMatch, type RouteMeta, type RouteModule, type RouteParams, type RouteRevalidate, type RouteTreeNode, type ShellModule, type ShellProps, type StartAppOptions, Suspense, type TimeRevalidatePolicy, applyDefaultSecurityHeaders, buildPathFromSegments, defineApp, group, handlePrachtRequest, initClientRouter, lazy, matchApiRoute, matchAppRoute, prerenderApp, readHydrationState, resolveApiRoutes, resolveApp, route, startApp, timeRevalidate, useLocation, useNavigate, useParams, useRevalidate, useRevalidateRoute, useRouteData };
|