@torpor/build 0.0.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.
- package/LICENSE.md +5 -0
- package/README.md +55 -0
- package/dist/bin/index.cjs +137 -0
- package/dist/bin/index.cjs.map +1 -0
- package/dist/bin/index.d.cts +1 -0
- package/dist/bin/index.d.ts +1 -0
- package/dist/bin/index.js +114 -0
- package/dist/bin/index.js.map +1 -0
- package/dist/index.cjs +55 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +150 -0
- package/dist/index.d.ts +150 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/metafile-cjs.json +1 -0
- package/dist/metafile-esm.json +1 -0
- package/dist/response.cjs +150 -0
- package/dist/response.cjs.map +1 -0
- package/dist/response.d.cts +162 -0
- package/dist/response.d.ts +162 -0
- package/dist/response.js +109 -0
- package/dist/response.js.map +1 -0
- package/dist/state.cjs +41 -0
- package/dist/state.cjs.map +1 -0
- package/dist/state.d.cts +6 -0
- package/dist/state.d.ts +6 -0
- package/dist/state.js +14 -0
- package/dist/state.js.map +1 -0
- package/package.json +49 -0
- package/src/bin/index.ts +41 -0
- package/src/index.ts +24 -0
- package/src/response/badRequest.ts +14 -0
- package/src/response/created.ts +13 -0
- package/src/response/forbidden.ts +14 -0
- package/src/response/methodNotAllowed.ts +14 -0
- package/src/response/moved.ts +16 -0
- package/src/response/notFound.ts +17 -0
- package/src/response/notModified.ts +14 -0
- package/src/response/ok.ts +20 -0
- package/src/response/redirect.ts +17 -0
- package/src/response/response.ts +22 -0
- package/src/response/seeOther.ts +13 -0
- package/src/response/serverError.ts +10 -0
- package/src/response/transfer.ts +14 -0
- package/src/response/unauthorized.ts +12 -0
- package/src/response/unprocessable.ts +12 -0
- package/src/response.ts +33 -0
- package/src/site/FileSystemRouter.ts +24 -0
- package/src/site/buildRoutePath.ts +25 -0
- package/src/site/clientEntry.ts +191 -0
- package/src/site/defineConfig.ts +5 -0
- package/src/site/defineSite.ts +74 -0
- package/src/site/lazyRoute.ts +72 -0
- package/src/site/routeHandlers.ts +113 -0
- package/src/site/serverEntry.ts +384 -0
- package/src/state/$page.ts +11 -0
- package/src/state.ts +3 -0
- package/src/types/CookieHelper.ts +7 -0
- package/src/types/PageEndPoint.ts +37 -0
- package/src/types/PageEvent.ts +14 -0
- package/src/types/PageServerEndPoint.ts +18 -0
- package/src/types/RouteHandler.ts +12 -0
- package/src/types/RouteHandlerCollection.ts +7 -0
- package/src/types/RouteLayoutHandler.ts +5 -0
- package/src/types/RouteMatchResult.ts +7 -0
- package/src/types/ServerEndPoint.ts +37 -0
- package/src/types/ServerEvent.ts +27 -0
- package/src/types/ServerHook.ts +13 -0
- package/src/types/UserConfig.ts +3 -0
- package/tsconfig.json +4 -0
- package/tsup.config.ts +10 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
import type { ServerComponent, ServerSlotRender } from "@torpor/view";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import { IncomingMessage } from "node:http";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { deleteCookie, eventHandler, getCookie, setCookie } from "vinxi/http";
|
|
6
|
+
import type { CookieSerializeOptions, EventHandlerRequest, H3Event } from "vinxi/http";
|
|
7
|
+
import { getManifest } from "vinxi/manifest";
|
|
8
|
+
import { ServerEndPoint } from "..";
|
|
9
|
+
import notFound from "../response/notFound";
|
|
10
|
+
import ok from "../response/ok";
|
|
11
|
+
import seeOther from "../response/seeOther";
|
|
12
|
+
import serverError from "../response/serverError";
|
|
13
|
+
import $page from "../state/$page";
|
|
14
|
+
import type PageEndPoint from "../types/PageEndPoint";
|
|
15
|
+
import type PageServerEndPoint from "../types/PageServerEndPoint";
|
|
16
|
+
import type RouteHandler from "../types/RouteHandler";
|
|
17
|
+
import type ServerEvent from "../types/ServerEvent";
|
|
18
|
+
import type ServerHook from "../types/ServerHook";
|
|
19
|
+
import routeHandlers from "./routeHandlers";
|
|
20
|
+
|
|
21
|
+
let printedRoutes = false;
|
|
22
|
+
|
|
23
|
+
export default eventHandler(async (event) => {
|
|
24
|
+
const url = new URL(`http://${process.env.HOST ?? "localhost"}${event.node.req.url}`);
|
|
25
|
+
const path = url.pathname;
|
|
26
|
+
|
|
27
|
+
const searchParams = url.searchParams;
|
|
28
|
+
|
|
29
|
+
console.log("handling server", event.method, "for", path, "with", searchParams);
|
|
30
|
+
|
|
31
|
+
if (!printedRoutes) {
|
|
32
|
+
printedRoutes = true;
|
|
33
|
+
console.log(
|
|
34
|
+
"routes:\n " +
|
|
35
|
+
routeHandlers.handlers
|
|
36
|
+
.map((h) => h.path)
|
|
37
|
+
.sort()
|
|
38
|
+
.join("\n "),
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const route = routeHandlers.match(path, searchParams);
|
|
43
|
+
if (!route) {
|
|
44
|
+
return notFound();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Update $page before building the components
|
|
48
|
+
// TODO: Find somewhere better to put this
|
|
49
|
+
$page.url = url;
|
|
50
|
+
|
|
51
|
+
const handler = route.handler;
|
|
52
|
+
const params = route.routeParams || {};
|
|
53
|
+
|
|
54
|
+
// TODO: Hit the server hook out here
|
|
55
|
+
|
|
56
|
+
// HACK: Support any +server location??
|
|
57
|
+
if (path.startsWith("/api/")) {
|
|
58
|
+
return await loadData(event, url, handler, params);
|
|
59
|
+
} else if (path.endsWith("~server")) {
|
|
60
|
+
// HACK: Is this the best way to signal that we want server data??
|
|
61
|
+
// And can we maybe combine this with the above method, or are they too different?
|
|
62
|
+
if (event.method === "GET") {
|
|
63
|
+
return await loadServerData(event, url, handler, params);
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
if (event.method === "GET") {
|
|
67
|
+
return await loadView(event, url, handler, params);
|
|
68
|
+
} else if (event.method === "POST") {
|
|
69
|
+
return await runAction(event, url, handler, params, searchParams);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
async function loadData(
|
|
75
|
+
event: H3Event<EventHandlerRequest>,
|
|
76
|
+
url: URL,
|
|
77
|
+
handler: RouteHandler,
|
|
78
|
+
params: Record<string, string>,
|
|
79
|
+
) {
|
|
80
|
+
const functionName = event.method.toLowerCase().replace("delete", "del");
|
|
81
|
+
|
|
82
|
+
const serverEndPoint: ServerEndPoint | undefined = (await handler.endPoint).default;
|
|
83
|
+
if (serverEndPoint && serverEndPoint[functionName]) {
|
|
84
|
+
const serverParams = await buildServerParams(event, url, params);
|
|
85
|
+
|
|
86
|
+
if (handler.serverHook) {
|
|
87
|
+
const serverHook: ServerHook | undefined = (await handler.serverHook).default;
|
|
88
|
+
if (serverHook?.handle) {
|
|
89
|
+
await serverHook.handle(serverParams);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const result = await serverEndPoint[functionName](serverParams);
|
|
94
|
+
// If there was no response returned from load (such as errors or a
|
|
95
|
+
// redirect), send an ok response
|
|
96
|
+
return result || ok();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function loadServerData(
|
|
101
|
+
event: H3Event<EventHandlerRequest>,
|
|
102
|
+
url: URL,
|
|
103
|
+
handler: RouteHandler,
|
|
104
|
+
params: Record<string, string>,
|
|
105
|
+
) {
|
|
106
|
+
const serverEndPoint: PageServerEndPoint | undefined = (await handler.endPoint).default;
|
|
107
|
+
if (serverEndPoint?.load) {
|
|
108
|
+
const serverParams = await buildServerParams(event, url, params);
|
|
109
|
+
|
|
110
|
+
if (handler.serverHook) {
|
|
111
|
+
const serverHook: ServerHook | undefined = (await handler.serverHook).default;
|
|
112
|
+
if (serverHook?.handle) {
|
|
113
|
+
await serverHook.handle(serverParams);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const result = await serverEndPoint.load(serverParams);
|
|
118
|
+
// If there was no response returned from load (such as errors or a
|
|
119
|
+
// redirect), send an ok response
|
|
120
|
+
return result || ok();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// It's ok if there's no load method -- we can't tell whether there will be
|
|
124
|
+
// when loading routes
|
|
125
|
+
//return ok();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function loadView(
|
|
129
|
+
event: H3Event<EventHandlerRequest>,
|
|
130
|
+
url: URL,
|
|
131
|
+
handler: RouteHandler,
|
|
132
|
+
params: Record<string, string>,
|
|
133
|
+
) {
|
|
134
|
+
// There must be a client endpoint with a component
|
|
135
|
+
const clientEndPoint: PageEndPoint | undefined = (await handler.endPoint).default;
|
|
136
|
+
if (!clientEndPoint?.component) {
|
|
137
|
+
return notFound();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// There may be a server endpoint
|
|
141
|
+
const serverEndPoint: PageServerEndPoint | undefined = (await handler.serverEndPoint)?.default;
|
|
142
|
+
|
|
143
|
+
// HACK: wrangle the view into app.html
|
|
144
|
+
// We could instead have an App.torp component with a slot, but you can run
|
|
145
|
+
// into hydration problems that way e.g. if there is a browser plugin that
|
|
146
|
+
// injects elements into <head> or <body>
|
|
147
|
+
const appHtml = loadAppHtml();
|
|
148
|
+
let contentStart = regexIndexOf(appHtml, /\<div\s+id=("app"|'app'|app)\s+/);
|
|
149
|
+
contentStart = appHtml.indexOf(">", contentStart) + 1;
|
|
150
|
+
let contentEnd = appHtml.indexOf("</div>", contentStart);
|
|
151
|
+
if (contentStart === -1 || contentEnd === -1) {
|
|
152
|
+
return serverError;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Build client scripts
|
|
156
|
+
const clientManifest = getManifest("client");
|
|
157
|
+
const clientHandler = clientManifest.inputs[clientManifest.handler];
|
|
158
|
+
const clientScript = clientHandler.output.path;
|
|
159
|
+
const hasClientScript = !!clientScript;
|
|
160
|
+
const manifestJson = JSON.stringify(clientManifest.json());
|
|
161
|
+
const hasManifestJson = manifestJson !== "{}";
|
|
162
|
+
|
|
163
|
+
// Maybe hit the server hook
|
|
164
|
+
const serverParams = await buildServerParams(event, url, params);
|
|
165
|
+
if (handler.serverHook) {
|
|
166
|
+
const serverHook: ServerHook | undefined = (await handler.serverHook).default;
|
|
167
|
+
if (serverHook?.handle) {
|
|
168
|
+
await serverHook.handle(serverParams);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Pass the data into $props
|
|
173
|
+
// TODO: Promise.all
|
|
174
|
+
// NOTE: We're loading data from top to bottom, overriding as we go, and I'm not sure if this is the best way to go
|
|
175
|
+
let data = {};
|
|
176
|
+
if (handler.layouts) {
|
|
177
|
+
for (let layout of handler.layouts) {
|
|
178
|
+
const layoutEndPoint: PageEndPoint | undefined = (await layout.endPoint)?.default;
|
|
179
|
+
const layoutServerEndPoint: PageServerEndPoint | undefined = (await layout.serverEndPoint)
|
|
180
|
+
?.default;
|
|
181
|
+
const layoutData = await loadClientAndServerData(
|
|
182
|
+
url,
|
|
183
|
+
params,
|
|
184
|
+
serverParams,
|
|
185
|
+
data,
|
|
186
|
+
layoutEndPoint,
|
|
187
|
+
layoutServerEndPoint,
|
|
188
|
+
);
|
|
189
|
+
Object.assign(data, layoutData);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
let endPointData = await loadClientAndServerData(
|
|
193
|
+
url,
|
|
194
|
+
params,
|
|
195
|
+
serverParams,
|
|
196
|
+
data,
|
|
197
|
+
clientEndPoint,
|
|
198
|
+
serverEndPoint,
|
|
199
|
+
);
|
|
200
|
+
Object.assign(data, endPointData);
|
|
201
|
+
let $props: Record<string, any> = { data };
|
|
202
|
+
|
|
203
|
+
// If there are layouts, work our way upwards, pushing each component into
|
|
204
|
+
// the default slot of its parent
|
|
205
|
+
// TODO: Also handle layout server data
|
|
206
|
+
// TODO: There's probably a nicer way to do this with reducers or something
|
|
207
|
+
// TODO: Re-use parent layouts
|
|
208
|
+
let component = clientEndPoint.component as ServerComponent;
|
|
209
|
+
let slots: Record<string, ServerSlotRender> | undefined = undefined;
|
|
210
|
+
if (handler.layouts) {
|
|
211
|
+
let slotFunctions: ServerSlotRender[] = [];
|
|
212
|
+
slotFunctions[handler.layouts.length] = (_, context) =>
|
|
213
|
+
(clientEndPoint.component as ServerComponent)($props, context);
|
|
214
|
+
for (let i = handler.layouts.length - 1; i >= 0; i--) {
|
|
215
|
+
const layoutEndPoint: PageEndPoint | undefined = (await handler.layouts[i].endPoint)?.default;
|
|
216
|
+
if (layoutEndPoint?.component) {
|
|
217
|
+
if (i === 0) {
|
|
218
|
+
component = layoutEndPoint.component as ServerComponent;
|
|
219
|
+
slots = { _: slotFunctions[i + 1] };
|
|
220
|
+
} else {
|
|
221
|
+
slotFunctions[i] = (_, context) =>
|
|
222
|
+
(layoutEndPoint.component as ServerComponent)($props, context, {
|
|
223
|
+
_: slotFunctions[i + 1],
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
event.node.res.setHeader("content-type", "text/html");
|
|
231
|
+
|
|
232
|
+
let componentCode = "";
|
|
233
|
+
try {
|
|
234
|
+
componentCode = component($props, undefined, slots);
|
|
235
|
+
} catch (error) {
|
|
236
|
+
// TODO: Show a proper Error component
|
|
237
|
+
componentCode = '<span style="color: red">Script syntax error</span><p>' + error + "</p>";
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Put it all together
|
|
241
|
+
let html =
|
|
242
|
+
appHtml.substring(0, contentStart) +
|
|
243
|
+
componentCode +
|
|
244
|
+
appHtml.substring(contentEnd) +
|
|
245
|
+
(hasClientScript ? `<script type="module" src="${clientScript}"></script>` : "") +
|
|
246
|
+
(hasManifestJson ? `<script>window.manifest = ${manifestJson}</script>` : "");
|
|
247
|
+
|
|
248
|
+
//console.log(html);
|
|
249
|
+
|
|
250
|
+
return html;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
async function runAction(
|
|
254
|
+
event: H3Event<EventHandlerRequest>,
|
|
255
|
+
url: URL,
|
|
256
|
+
handler: RouteHandler,
|
|
257
|
+
params: Record<string, string>,
|
|
258
|
+
searchParams: URLSearchParams,
|
|
259
|
+
) {
|
|
260
|
+
const actionName = (Array.from(searchParams.keys())[0] || "default").replace(/^\//, "");
|
|
261
|
+
const serverEndPoint: PageServerEndPoint | undefined = (await handler.serverEndPoint).default;
|
|
262
|
+
if (serverEndPoint?.actions) {
|
|
263
|
+
const action = serverEndPoint.actions[actionName];
|
|
264
|
+
if (action) {
|
|
265
|
+
// TODO: form.errors etc
|
|
266
|
+
const serverParams = await buildServerParams(event, url, params);
|
|
267
|
+
|
|
268
|
+
if (handler.serverHook) {
|
|
269
|
+
const serverHook: ServerHook | undefined = (await handler.serverHook).default;
|
|
270
|
+
if (serverHook?.handle) {
|
|
271
|
+
await serverHook.handle(serverParams);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const result = await action(serverParams);
|
|
276
|
+
// If there was no response returned from the action (such as errors
|
|
277
|
+
// or a redirect), reload the page by redirecting
|
|
278
|
+
return result || seeOther(url.pathname);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async function loadClientAndServerData(
|
|
284
|
+
url: URL,
|
|
285
|
+
params: Record<string, string>,
|
|
286
|
+
serverParams: ServerEvent,
|
|
287
|
+
data: Record<string, any>,
|
|
288
|
+
clientEndPoint?: PageEndPoint,
|
|
289
|
+
serverEndPoint?: PageServerEndPoint,
|
|
290
|
+
) {
|
|
291
|
+
let newData = {};
|
|
292
|
+
if (clientEndPoint?.load) {
|
|
293
|
+
const clientParams = buildClientParams(url, data, params);
|
|
294
|
+
const clientData = await clientEndPoint.load(clientParams);
|
|
295
|
+
if (clientData) {
|
|
296
|
+
Object.assign(newData, clientData);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
if (serverEndPoint?.load) {
|
|
300
|
+
const serverData = await serverEndPoint.load(serverParams);
|
|
301
|
+
if (serverData?.ok) {
|
|
302
|
+
Object.assign(newData, await serverData.json());
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return newData;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function buildClientParams(url: URL, params: Record<string, string>, data: Record<string, any>) {
|
|
309
|
+
return {
|
|
310
|
+
url,
|
|
311
|
+
params,
|
|
312
|
+
data,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
async function buildServerParams(
|
|
317
|
+
event: H3Event<EventHandlerRequest>,
|
|
318
|
+
url: URL,
|
|
319
|
+
params: Record<string, string>,
|
|
320
|
+
): Promise<ServerEvent> {
|
|
321
|
+
return {
|
|
322
|
+
url,
|
|
323
|
+
params,
|
|
324
|
+
appData: {},
|
|
325
|
+
request: await incomingMessageToRequest(event.node.req, url),
|
|
326
|
+
response: event.node.res,
|
|
327
|
+
cookies: {
|
|
328
|
+
get: (name: string) => getCookie(event, name),
|
|
329
|
+
set: (name: string, value: string, options?: CookieSerializeOptions) =>
|
|
330
|
+
setCookie(event, name, value, options),
|
|
331
|
+
delete: (name: string, options?: CookieSerializeOptions) =>
|
|
332
|
+
deleteCookie(event, name, options),
|
|
333
|
+
},
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// From https://stackoverflow.com/a/78849544
|
|
338
|
+
async function incomingMessageToRequest(req: IncomingMessage, url: URL): Promise<Request> {
|
|
339
|
+
const headers = new Headers();
|
|
340
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
341
|
+
if (Array.isArray(value)) {
|
|
342
|
+
value.forEach((v) => headers.append(key, v));
|
|
343
|
+
} else if (value !== undefined) {
|
|
344
|
+
headers.set(key, value);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
// Convert body to Buffer if applicable
|
|
348
|
+
const body = req.method !== "GET" && req.method !== "HEAD" ? await getRequestBody(req) : null;
|
|
349
|
+
return new Request(url, {
|
|
350
|
+
method: req.method,
|
|
351
|
+
headers: headers,
|
|
352
|
+
body: body,
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function getRequestBody(req: IncomingMessage): Promise<Buffer> {
|
|
357
|
+
return new Promise<Buffer>((resolve, reject) => {
|
|
358
|
+
const chunks: Buffer[] = [];
|
|
359
|
+
req.on("data", (chunk: Buffer) => {
|
|
360
|
+
chunks.push(chunk);
|
|
361
|
+
});
|
|
362
|
+
req.on("end", () => {
|
|
363
|
+
resolve(Buffer.concat(chunks));
|
|
364
|
+
});
|
|
365
|
+
req.on("error", (err) => {
|
|
366
|
+
reject(err);
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
let loadedAppHtml = "";
|
|
372
|
+
function loadAppHtml() {
|
|
373
|
+
if (!loadedAppHtml) {
|
|
374
|
+
let file = path.resolve("src/app.html");
|
|
375
|
+
loadedAppHtml = fs.readFileSync(file).toString();
|
|
376
|
+
}
|
|
377
|
+
return loadedAppHtml;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// From https://stackoverflow.com/a/274094
|
|
381
|
+
function regexIndexOf(string: string, regex: RegExp, position?: number) {
|
|
382
|
+
var indexOf = string.substring(position || 0).search(regex);
|
|
383
|
+
return indexOf >= 0 ? indexOf + (position || 0) : indexOf;
|
|
384
|
+
}
|
package/src/state.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { CookieSerializeOptions } from "vinxi/http";
|
|
2
|
+
|
|
3
|
+
export default interface CookieHelper {
|
|
4
|
+
get: (name: string) => string | undefined;
|
|
5
|
+
set: (name: string, value: string, options?: CookieSerializeOptions) => void;
|
|
6
|
+
delete: (name: string, options?: CookieSerializeOptions) => void;
|
|
7
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Component } from "@torpor/view";
|
|
2
|
+
import type PageEvent from "./PageEvent";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* For +page.
|
|
6
|
+
*/
|
|
7
|
+
export default interface PageEndPoint {
|
|
8
|
+
/**
|
|
9
|
+
* Builds the route path for the page in a type-safe manner.
|
|
10
|
+
* TODO: Not sure this is actually the best way to do it...
|
|
11
|
+
*/
|
|
12
|
+
route?: (...args: any[]) => string;
|
|
13
|
+
/**
|
|
14
|
+
* Loads data for the page.
|
|
15
|
+
*/
|
|
16
|
+
load?: (event: PageEvent) => any;
|
|
17
|
+
/**
|
|
18
|
+
* The component that is displayed for the page.
|
|
19
|
+
*/
|
|
20
|
+
component?: Component;
|
|
21
|
+
// TODO: Better typing
|
|
22
|
+
/**
|
|
23
|
+
* The head element data for the page, which may include a <title> and <meta> elements.
|
|
24
|
+
*/
|
|
25
|
+
head?: HeadElement[] | ((event: PageEvent) => HeadElement[]);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type HeadElement = TitleElement | MetaElement;
|
|
29
|
+
|
|
30
|
+
interface TitleElement {
|
|
31
|
+
title: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface MetaElement {
|
|
35
|
+
name: string;
|
|
36
|
+
content: string;
|
|
37
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export default interface PageEvent {
|
|
2
|
+
/**
|
|
3
|
+
* The URL for the server function.
|
|
4
|
+
*/
|
|
5
|
+
url: URL;
|
|
6
|
+
/**
|
|
7
|
+
* Route params from the URL and route path.
|
|
8
|
+
*/
|
|
9
|
+
params: Record<string, string>;
|
|
10
|
+
/**
|
|
11
|
+
* Data that is (optionally) loaded from the load function and passed into the page as $props.data.
|
|
12
|
+
*/
|
|
13
|
+
data: Record<string, any>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import ServerEvent from "./ServerEvent";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* For +page.server.
|
|
5
|
+
*/
|
|
6
|
+
export default interface PageServerEndPoint {
|
|
7
|
+
/**
|
|
8
|
+
* Loads data from the server for a page.
|
|
9
|
+
*/
|
|
10
|
+
load?: ServerLoad;
|
|
11
|
+
/**
|
|
12
|
+
* A map of actions that can be performed on the server for a page, generally from a form submit.
|
|
13
|
+
*/
|
|
14
|
+
actions?: Record<string, ServerAction>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type ServerLoad = (event: ServerEvent) => Response | Promise<Response | undefined> | void;
|
|
18
|
+
type ServerAction = (event: ServerEvent) => Response | Promise<Response | undefined> | void;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import RouteLayoutHandler from "./RouteLayoutHandler";
|
|
2
|
+
|
|
3
|
+
export default interface RouteHandler {
|
|
4
|
+
path: string;
|
|
5
|
+
regex: RegExp;
|
|
6
|
+
endPoint: Promise<any>;
|
|
7
|
+
|
|
8
|
+
loaded: boolean;
|
|
9
|
+
layouts?: RouteLayoutHandler[];
|
|
10
|
+
serverEndPoint?: Promise<any>;
|
|
11
|
+
serverHook?: Promise<any>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type RouteHandler from "./RouteHandler";
|
|
2
|
+
import type RouteMatchResult from "./RouteMatchResult";
|
|
3
|
+
|
|
4
|
+
export default interface RouteHandlerCollection {
|
|
5
|
+
handlers: RouteHandler[];
|
|
6
|
+
match(path: string, urlParams?: URLSearchParams): RouteMatchResult | undefined;
|
|
7
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import ServerEvent from "./ServerEvent";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* For +server.
|
|
5
|
+
*/
|
|
6
|
+
export default interface ServerEndPoint {
|
|
7
|
+
/**
|
|
8
|
+
* Performs a GET.
|
|
9
|
+
*/
|
|
10
|
+
get?: ServerRequest;
|
|
11
|
+
/**
|
|
12
|
+
* Perfroms a POST.
|
|
13
|
+
*/
|
|
14
|
+
post?: ServerRequest;
|
|
15
|
+
/**
|
|
16
|
+
* Perfroms a PATCH.
|
|
17
|
+
*/
|
|
18
|
+
patch?: ServerRequest;
|
|
19
|
+
/**
|
|
20
|
+
* Perfroms a PUT.
|
|
21
|
+
*/
|
|
22
|
+
put?: ServerRequest;
|
|
23
|
+
/**
|
|
24
|
+
* Perfroms a DELETE.
|
|
25
|
+
*/
|
|
26
|
+
del?: ServerRequest;
|
|
27
|
+
/**
|
|
28
|
+
* Perfroms an OPTIONS request.
|
|
29
|
+
*/
|
|
30
|
+
options?: ServerRequest;
|
|
31
|
+
/**
|
|
32
|
+
* Perfroms a HEAD request.
|
|
33
|
+
*/
|
|
34
|
+
head?: ServerRequest;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
type ServerRequest = (event: ServerEvent) => Response | Promise<Response | undefined> | void;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ServerResponse } from "node:http";
|
|
2
|
+
import CookieHelper from "./CookieHelper";
|
|
3
|
+
|
|
4
|
+
export default interface ServerEvent {
|
|
5
|
+
/**
|
|
6
|
+
* The URL for the server function.
|
|
7
|
+
*/
|
|
8
|
+
url: URL;
|
|
9
|
+
/**
|
|
10
|
+
* Route params from the URL and route path.
|
|
11
|
+
*/
|
|
12
|
+
params: Record<string, string>;
|
|
13
|
+
// TODO: Maybe we should find a better name for the data that is set set in
|
|
14
|
+
// pages, and just call this data?
|
|
15
|
+
/**
|
|
16
|
+
* Data that is available to functions in +server hooks, layouts, endpoints
|
|
17
|
+
* and pages. It flows down in that order.
|
|
18
|
+
*/
|
|
19
|
+
appData: Record<string, any>;
|
|
20
|
+
// TODO: Would be nicer if these were Request/Response?
|
|
21
|
+
request: Request;
|
|
22
|
+
response: ServerResponse;
|
|
23
|
+
/**
|
|
24
|
+
* A helper for getting and setting cookie data.
|
|
25
|
+
*/
|
|
26
|
+
cookies: CookieHelper;
|
|
27
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import ServerEvent from "./ServerEvent";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* For _hook.server.
|
|
5
|
+
*/
|
|
6
|
+
export default interface ServerHook {
|
|
7
|
+
/**
|
|
8
|
+
* Called on each server request.
|
|
9
|
+
*/
|
|
10
|
+
handle?: ServerLoad;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type ServerLoad = (event: ServerEvent) => Promise<void> | void;
|
package/tsconfig.json
ADDED
package/tsup.config.ts
ADDED