@torpor/build 0.4.10 → 0.4.12
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/{_page-DBSsnHFG.mjs → _page-BQs4WvCO.mjs} +2 -2
- package/dist/_page-BQs4WvCO.mjs.map +1 -0
- package/dist/nav.mjs +2 -2
- package/dist/nav.mjs.map +1 -1
- package/dist/state.mjs +1 -1
- package/dist/test.mjs +1 -1
- package/package.json +4 -4
- package/src/bin/index.ts +11 -0
- package/src/index.ts +30 -0
- package/src/nav/formSubmit.ts +56 -0
- package/src/nav/load.ts +9 -0
- package/src/nav/loadData.ts +134 -0
- package/src/nav/navigate.ts +164 -0
- package/src/nav/reload.ts +9 -0
- package/src/nav.ts +4 -0
- package/src/response/badRequest.ts +19 -0
- package/src/response/created.ts +20 -0
- package/src/response/forbidden.ts +21 -0
- package/src/response/found.ts +30 -0
- package/src/response/methodNotAllowed.ts +18 -0
- package/src/response/movedPermanently.ts +25 -0
- package/src/response/notFound.ts +24 -0
- package/src/response/notModified.ts +34 -0
- package/src/response/ok.ts +29 -0
- package/src/response/permanentRedirect.ts +30 -0
- package/src/response/response.ts +22 -0
- package/src/response/seeOther.ts +19 -0
- package/src/response/serverError.ts +24 -0
- package/src/response/temporaryRedirect.ts +31 -0
- package/src/response/transfer.ts +14 -0
- package/src/response/unauthorized.ts +21 -0
- package/src/response/unprocessable.ts +18 -0
- package/src/response.ts +37 -0
- package/src/run/prepareTemplate.ts +39 -0
- package/src/run/run.ts +62 -0
- package/src/run/runBuild.ts +77 -0
- package/src/run/runDev.ts +89 -0
- package/src/run/runPreview.ts +93 -0
- package/src/run.ts +6 -0
- package/src/server/CookieHelper.ts +36 -0
- package/src/server/HeaderHelper.ts +25 -0
- package/src/server/Routerx.ts +72 -0
- package/src/server/Server.ts +144 -0
- package/src/server/ServerEvent.ts +39 -0
- package/src/server/connect/bufferToArrayBuffer.ts +8 -0
- package/src/server/connect/connectMiddleware.ts +81 -0
- package/src/server/connect/flattenHeaders.ts +44 -0
- package/src/server/connect/nodeMessageToNodeResponse.ts +89 -0
- package/src/server/connect/readableToBuffer.ts +16 -0
- package/src/server/connect/requestToNodeMessage.ts +26 -0
- package/src/server/contentType.ts +85 -0
- package/src/server/types/HttpMethod.ts +12 -0
- package/src/server/types/MiddlewareFunction.ts +8 -0
- package/src/server/types/ServerFunction.ts +7 -0
- package/src/server.ts +7 -0
- package/src/site/Router.ts +164 -0
- package/src/site/Site.ts +214 -0
- package/src/site/clientEntry.ts +108 -0
- package/src/site/clientEntryDev.ts +3 -0
- package/src/site/defaultAdapter.ts +11 -0
- package/src/site/manifest.ts +54 -0
- package/src/site/serverEntry.ts +417 -0
- package/src/state/$page.ts +24 -0
- package/src/state/client.ts +15 -0
- package/src/state.ts +3 -0
- package/src/test/runTest.ts +350 -0
- package/src/test.ts +3 -0
- package/src/types/Adapter.ts +8 -0
- package/src/types/ClientState.ts +8 -0
- package/src/types/InternalState.ts +7 -0
- package/src/types/LayoutHandler.ts +5 -0
- package/src/types/LayoutPath.ts +10 -0
- package/src/types/ManifestRoute.ts +6 -0
- package/src/types/PageEndPoint.ts +39 -0
- package/src/types/PageLoadEvent.ts +14 -0
- package/src/types/PageServerAction.ts +7 -0
- package/src/types/PageServerEndPoint.ts +16 -0
- package/src/types/PageServerLoad.ts +7 -0
- package/src/types/PageState.ts +8 -0
- package/src/types/Route.ts +11 -0
- package/src/types/RouteHandler.ts +16 -0
- package/src/types/RouteLayoutHandler.ts +5 -0
- package/src/types/RouteMatchResult.ts +7 -0
- package/src/types/RouteType.ts +19 -0
- package/src/types/ServerEndPoint.ts +37 -0
- package/src/types/ServerHook.ts +11 -0
- package/src/types/ServerLoadEvent.ts +37 -0
- package/src/types/ServerRequest.ts +7 -0
- package/src/utils/pathToRegex.ts +15 -0
- package/src/vite-env.d.ts +2 -0
- package/dist/_page-DBSsnHFG.mjs.map +0 -1
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { type ServerComponent, type ServerSlotRender } from "@torpor/view/ssr";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import notFound from "../response/notFound.ts";
|
|
4
|
+
import ok from "../response/ok.ts";
|
|
5
|
+
import seeOther from "../response/seeOther.ts";
|
|
6
|
+
import ServerEvent from "../server/ServerEvent.ts";
|
|
7
|
+
import Router from "../site/Router.ts";
|
|
8
|
+
import Site from "../site/Site";
|
|
9
|
+
import $page from "../state/$page.ts";
|
|
10
|
+
import type PageEndPoint from "../types/PageEndPoint.ts";
|
|
11
|
+
import type PageServerEndPoint from "../types/PageServerEndPoint.ts";
|
|
12
|
+
import type RouteHandler from "../types/RouteHandler.ts";
|
|
13
|
+
import {
|
|
14
|
+
HOOK_ROUTE,
|
|
15
|
+
HOOK_SERVER_ROUTE,
|
|
16
|
+
LAYOUT_ROUTE,
|
|
17
|
+
LAYOUT_SERVER_ROUTE,
|
|
18
|
+
PAGE_ROUTE,
|
|
19
|
+
PAGE_SERVER_ROUTE,
|
|
20
|
+
SERVER_ROUTE,
|
|
21
|
+
} from "../types/RouteType";
|
|
22
|
+
import type ServerEndPoint from "../types/ServerEndPoint.ts";
|
|
23
|
+
import type ServerHook from "../types/ServerHook.ts";
|
|
24
|
+
import type ServerLoadEvent from "../types/ServerLoadEvent.ts";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Runs a site route in test mode
|
|
28
|
+
* @param site The site configuration, with routing configured
|
|
29
|
+
* @param route The route path e.g. `/home`
|
|
30
|
+
* @param ev An optional ServerEvent, which you can use to set test cookies and headers
|
|
31
|
+
* @returns An HTTP response containing the requested data, HTML, or error status
|
|
32
|
+
*/
|
|
33
|
+
export default async function runTest(
|
|
34
|
+
site: Site,
|
|
35
|
+
route: string,
|
|
36
|
+
ev?: ServerEvent,
|
|
37
|
+
): Promise<Response> {
|
|
38
|
+
if (!route.startsWith("/")) route += "/";
|
|
39
|
+
if (!ev) {
|
|
40
|
+
const req = new Request(`http://localhost${route}`);
|
|
41
|
+
ev = new ServerEvent(req);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Build the router from the Site object created by the user
|
|
45
|
+
const router = new Router();
|
|
46
|
+
router.addPages(
|
|
47
|
+
site.routes.map((r) => ({
|
|
48
|
+
path: r.path,
|
|
49
|
+
type: r.type,
|
|
50
|
+
endPoint: () => import(/* @vite-ignore */ path.join(site.root, r.file)),
|
|
51
|
+
subFolder: r.subFolder,
|
|
52
|
+
})),
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
return await load(router, ev, "%COMPONENT_BODY%");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// HACK: This is a copy of serverEntry
|
|
59
|
+
// TODO: Make it shared instead!
|
|
60
|
+
async function load(router: Router, ev: ServerEvent, template: string): Promise<Response> {
|
|
61
|
+
//const url = new URL(`http://${process.env.HOST ?? "localhost"}${ev.request.url}`);
|
|
62
|
+
const url = new URL(ev.request.url);
|
|
63
|
+
const path = url.pathname;
|
|
64
|
+
const query = url.searchParams;
|
|
65
|
+
|
|
66
|
+
//console.log(`handling ${ev.request.method} for '${path}'${query.size ? ` with ${query}` : ""}`);
|
|
67
|
+
|
|
68
|
+
const route = router.match(path, query);
|
|
69
|
+
if (!route) {
|
|
70
|
+
return notFound();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Update $page before building the components
|
|
74
|
+
// TODO: Find somewhere better to put this
|
|
75
|
+
$page.url = url;
|
|
76
|
+
|
|
77
|
+
const handler = route.handler;
|
|
78
|
+
const params = route.params || {};
|
|
79
|
+
|
|
80
|
+
// TODO: Hit the server hook out here
|
|
81
|
+
// We could maybe set data loading up as middleware on a route??????
|
|
82
|
+
|
|
83
|
+
switch (handler.type) {
|
|
84
|
+
case PAGE_ROUTE:
|
|
85
|
+
case LAYOUT_ROUTE: {
|
|
86
|
+
// It's a /+page.ts or /_layout.ts endpoint
|
|
87
|
+
if (ev.request.method === "GET") {
|
|
88
|
+
return await loadView(ev, url, handler, params, template);
|
|
89
|
+
} else if (ev.request.method === "POST") {
|
|
90
|
+
return await runAction(ev, url, handler, params, query);
|
|
91
|
+
}
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
case PAGE_SERVER_ROUTE:
|
|
95
|
+
case LAYOUT_SERVER_ROUTE: {
|
|
96
|
+
// It's a /+page.server.ts or /_layout.server.ts endpoint
|
|
97
|
+
if (ev.request.method === "GET") {
|
|
98
|
+
return await loadData(ev, url, handler, "load", params);
|
|
99
|
+
}
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
case SERVER_ROUTE: {
|
|
103
|
+
// It's a /+server.ts endpoint
|
|
104
|
+
const functionName = ev.request.method.toLowerCase().replace("delete", "del");
|
|
105
|
+
return await loadData(ev, url, handler, functionName, params);
|
|
106
|
+
}
|
|
107
|
+
case HOOK_ROUTE:
|
|
108
|
+
case HOOK_SERVER_ROUTE: {
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return notFound();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function loadData(
|
|
117
|
+
ev: ServerEvent,
|
|
118
|
+
url: URL,
|
|
119
|
+
handler: RouteHandler,
|
|
120
|
+
functionName: string,
|
|
121
|
+
params: Record<string, string>,
|
|
122
|
+
) {
|
|
123
|
+
const serverEndPoint: ServerEndPoint | undefined = (await handler.endPoint()).default;
|
|
124
|
+
if (serverEndPoint && serverEndPoint[functionName]) {
|
|
125
|
+
const serverParams = buildServerParams(ev, url, params);
|
|
126
|
+
|
|
127
|
+
if (handler.serverHook) {
|
|
128
|
+
const serverHook: ServerHook | undefined = (await handler.serverHook()).default;
|
|
129
|
+
if (serverHook?.handle) {
|
|
130
|
+
await serverHook.handle(serverParams);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const result = await serverEndPoint[functionName](serverParams);
|
|
135
|
+
|
|
136
|
+
// If there was no response returned from load (such as errors or a
|
|
137
|
+
// redirect), send an ok response
|
|
138
|
+
return result || ok();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return notFound();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function loadView(
|
|
145
|
+
ev: ServerEvent,
|
|
146
|
+
url: URL,
|
|
147
|
+
handler: RouteHandler,
|
|
148
|
+
params: Record<string, string>,
|
|
149
|
+
template: string,
|
|
150
|
+
) {
|
|
151
|
+
// There must be a client endpoint with a component
|
|
152
|
+
const clientEndPoint: PageEndPoint | undefined = (await handler.endPoint()).default;
|
|
153
|
+
if (!clientEndPoint?.component) {
|
|
154
|
+
return notFound();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// There may be a server endpoint
|
|
158
|
+
const serverEndPoint: PageServerEndPoint | undefined =
|
|
159
|
+
handler.serverEndPoint && (await handler.serverEndPoint())?.default;
|
|
160
|
+
|
|
161
|
+
// Maybe hit the server hook
|
|
162
|
+
const serverParams = buildServerParams(ev, url, params);
|
|
163
|
+
if (handler.serverHook) {
|
|
164
|
+
const serverHook: ServerHook | undefined = (await handler.serverHook()).default;
|
|
165
|
+
if (serverHook?.handle) {
|
|
166
|
+
await serverHook.handle(serverParams);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Pass the data into $props
|
|
171
|
+
// TODO: Promise.all
|
|
172
|
+
// 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
|
|
173
|
+
let data = {};
|
|
174
|
+
if (handler.layouts) {
|
|
175
|
+
for (let layout of handler.layouts) {
|
|
176
|
+
const layoutEndPoint: PageEndPoint | undefined = (await layout.endPoint())?.default;
|
|
177
|
+
const layoutServerEndPoint: PageServerEndPoint | undefined =
|
|
178
|
+
layout.serverEndPoint && (await layout.serverEndPoint())?.default;
|
|
179
|
+
const layoutResponse = await loadClientAndServerData(
|
|
180
|
+
url,
|
|
181
|
+
params,
|
|
182
|
+
serverParams,
|
|
183
|
+
data,
|
|
184
|
+
layoutEndPoint,
|
|
185
|
+
layoutServerEndPoint,
|
|
186
|
+
);
|
|
187
|
+
if (layoutResponse?.ok === false) {
|
|
188
|
+
return layoutResponse;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
let endPointResponse = await loadClientAndServerData(
|
|
193
|
+
url,
|
|
194
|
+
params,
|
|
195
|
+
serverParams,
|
|
196
|
+
data,
|
|
197
|
+
clientEndPoint,
|
|
198
|
+
serverEndPoint,
|
|
199
|
+
);
|
|
200
|
+
if (endPointResponse?.ok === false) {
|
|
201
|
+
return endPointResponse;
|
|
202
|
+
}
|
|
203
|
+
let $props: Record<string, any> = { data };
|
|
204
|
+
|
|
205
|
+
let styles = "";
|
|
206
|
+
|
|
207
|
+
// If there are layouts, work our way upwards, pushing each component into
|
|
208
|
+
// the default slot of its parent
|
|
209
|
+
// TODO: Also handle layout server data
|
|
210
|
+
// TODO: There's probably a nicer way to do this with reducers or something
|
|
211
|
+
let component = clientEndPoint.component as ServerComponent;
|
|
212
|
+
let slots: Record<string, ServerSlotRender> | undefined = undefined;
|
|
213
|
+
if (handler.layouts) {
|
|
214
|
+
let slotFunctions: ServerSlotRender[] = [];
|
|
215
|
+
slotFunctions[handler.layouts.length] = (_, $context) => {
|
|
216
|
+
let { body, head } = (clientEndPoint.component as ServerComponent)($props, $context);
|
|
217
|
+
styles += head;
|
|
218
|
+
return body;
|
|
219
|
+
};
|
|
220
|
+
for (let i = handler.layouts.length - 1; i >= 0; i--) {
|
|
221
|
+
const layoutEndPoint: PageEndPoint | undefined = (await handler.layouts[i].endPoint())
|
|
222
|
+
?.default;
|
|
223
|
+
if (layoutEndPoint?.component) {
|
|
224
|
+
if (i === 0) {
|
|
225
|
+
component = layoutEndPoint.component as ServerComponent;
|
|
226
|
+
slots = { _: slotFunctions[i + 1] };
|
|
227
|
+
} else {
|
|
228
|
+
slotFunctions[i] = (_, $context) => {
|
|
229
|
+
let { body, head } = (layoutEndPoint.component as ServerComponent)($props, $context, {
|
|
230
|
+
_: slotFunctions[i + 1],
|
|
231
|
+
});
|
|
232
|
+
styles += head;
|
|
233
|
+
return body;
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
let html;
|
|
241
|
+
try {
|
|
242
|
+
let { body, head } = component($props, undefined, slots);
|
|
243
|
+
styles += head;
|
|
244
|
+
html = template.replace("%COMPONENT_BODY%", body).replace("%COMPONENT_HEAD", styles);
|
|
245
|
+
} catch (error) {
|
|
246
|
+
// TODO: Show a proper Error component
|
|
247
|
+
html = '<span style="color: red">Script syntax error</span><p>' + error + "</p>";
|
|
248
|
+
console.log(error);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return new Response(html, {
|
|
252
|
+
status: 200,
|
|
253
|
+
headers: {
|
|
254
|
+
"Content-Type": "text/html",
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
async function runAction(
|
|
260
|
+
ev: ServerEvent,
|
|
261
|
+
url: URL,
|
|
262
|
+
handler: RouteHandler,
|
|
263
|
+
params: Record<string, string>,
|
|
264
|
+
query: URLSearchParams,
|
|
265
|
+
) {
|
|
266
|
+
const actionName = (Array.from(query.keys())[0] || "default").replace(/^\//, "");
|
|
267
|
+
const serverEndPoint: PageServerEndPoint | undefined =
|
|
268
|
+
handler.serverEndPoint && (await handler.serverEndPoint()).default;
|
|
269
|
+
if (serverEndPoint?.actions) {
|
|
270
|
+
const action = serverEndPoint.actions[actionName];
|
|
271
|
+
if (action) {
|
|
272
|
+
// TODO: form.errors etc
|
|
273
|
+
const serverParams = buildServerParams(ev, url, params);
|
|
274
|
+
|
|
275
|
+
if (handler.serverHook) {
|
|
276
|
+
const serverHook: ServerHook | undefined = (await handler.serverHook()).default;
|
|
277
|
+
if (serverHook?.handle) {
|
|
278
|
+
await serverHook.handle(serverParams);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const result = await action(serverParams); // <--
|
|
283
|
+
|
|
284
|
+
// If there was no response returned from the action (such as errors
|
|
285
|
+
// or a redirect), reload the page by redirecting
|
|
286
|
+
return result || seeOther(url.pathname);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return notFound();
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
async function loadClientAndServerData(
|
|
294
|
+
url: URL,
|
|
295
|
+
params: Record<string, string>,
|
|
296
|
+
serverParams: ServerLoadEvent,
|
|
297
|
+
data: Record<string, any>,
|
|
298
|
+
clientEndPoint?: PageEndPoint,
|
|
299
|
+
serverEndPoint?: PageServerEndPoint,
|
|
300
|
+
) {
|
|
301
|
+
if (clientEndPoint?.load) {
|
|
302
|
+
const clientParams = buildClientParams(url, data, params);
|
|
303
|
+
const clientResponse = await clientEndPoint.load(clientParams);
|
|
304
|
+
if (clientResponse) {
|
|
305
|
+
if (clientResponse.ok) {
|
|
306
|
+
if (clientResponse.headers.get("Content-Type")?.includes("application/json")) {
|
|
307
|
+
Object.assign(data, await clientResponse.json());
|
|
308
|
+
}
|
|
309
|
+
} else {
|
|
310
|
+
return clientResponse;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (serverEndPoint?.load) {
|
|
315
|
+
const serverResponse = await serverEndPoint.load(serverParams);
|
|
316
|
+
if (serverResponse) {
|
|
317
|
+
if (serverResponse.ok) {
|
|
318
|
+
if (serverResponse.headers.get("Content-Type")?.includes("application/json")) {
|
|
319
|
+
Object.assign(data, await serverResponse.json());
|
|
320
|
+
}
|
|
321
|
+
} else {
|
|
322
|
+
return serverResponse;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function buildClientParams(url: URL, params: Record<string, string>, data: Record<string, any>) {
|
|
329
|
+
return {
|
|
330
|
+
url,
|
|
331
|
+
params,
|
|
332
|
+
data,
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function buildServerParams(
|
|
337
|
+
ev: ServerEvent,
|
|
338
|
+
url: URL,
|
|
339
|
+
params: Record<string, string>,
|
|
340
|
+
): ServerLoadEvent {
|
|
341
|
+
return {
|
|
342
|
+
url,
|
|
343
|
+
params,
|
|
344
|
+
appData: {},
|
|
345
|
+
request: ev.request,
|
|
346
|
+
cookies: ev.cookies,
|
|
347
|
+
headers: ev.headers,
|
|
348
|
+
adapter: ev.adapter,
|
|
349
|
+
};
|
|
350
|
+
}
|
package/src/test.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import Server from "../server/Server";
|
|
2
|
+
import Site from "../site/Site";
|
|
3
|
+
|
|
4
|
+
export default interface Adapter {
|
|
5
|
+
prebuild?: (site: Site) => Promise<void> | void;
|
|
6
|
+
postbuild?: (site: Site) => Promise<void> | void;
|
|
7
|
+
serve: (server: Server, site: Site) => void;
|
|
8
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Store the current layout stack, so that we can re-use parts of it as necessary
|
|
2
|
+
export default interface LayoutPath {
|
|
3
|
+
path: string;
|
|
4
|
+
/** Whether to re-use this layout */
|
|
5
|
+
reuse: boolean;
|
|
6
|
+
/** Data loaded from endpoints that should be re-used */
|
|
7
|
+
data: any;
|
|
8
|
+
/** The range of the UI in this layout's slot */
|
|
9
|
+
slotRegion: any;
|
|
10
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type Component } from "@torpor/view";
|
|
2
|
+
import type PageLoadEvent from "./PageLoadEvent";
|
|
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?: (
|
|
17
|
+
event: PageLoadEvent,
|
|
18
|
+
) => Response | undefined | void | Promise<Response | undefined | void>;
|
|
19
|
+
/**
|
|
20
|
+
* The component that is displayed for the page.
|
|
21
|
+
*/
|
|
22
|
+
component?: Component;
|
|
23
|
+
// TODO: Better typing
|
|
24
|
+
/**
|
|
25
|
+
* The head element data for the page, which may include a <title> and <meta> elements.
|
|
26
|
+
*/
|
|
27
|
+
head?: HeadElement[] | ((event: PageLoadEvent) => HeadElement[]);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type HeadElement = TitleElement | MetaElement;
|
|
31
|
+
|
|
32
|
+
interface TitleElement {
|
|
33
|
+
title: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface MetaElement {
|
|
37
|
+
name: string;
|
|
38
|
+
content: string;
|
|
39
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export default interface PageLoadEvent {
|
|
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,16 @@
|
|
|
1
|
+
import type PageServerAction from "./PageServerAction";
|
|
2
|
+
import type PageServerLoad from "./PageServerLoad";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* For +page.server.
|
|
6
|
+
*/
|
|
7
|
+
export default interface PageServerEndPoint {
|
|
8
|
+
/**
|
|
9
|
+
* Loads data from the server for a page.
|
|
10
|
+
*/
|
|
11
|
+
load?: PageServerLoad;
|
|
12
|
+
/**
|
|
13
|
+
* A map of actions that can be performed on the server for a page, generally from a form submit.
|
|
14
|
+
*/
|
|
15
|
+
actions?: Record<string, PageServerAction>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type RouteLayoutHandler from "./RouteLayoutHandler";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A handler for a route in the Router.
|
|
5
|
+
*/
|
|
6
|
+
export default interface RouteHandler {
|
|
7
|
+
path: string;
|
|
8
|
+
type: number;
|
|
9
|
+
endPoint: () => Promise<any>;
|
|
10
|
+
subFolder?: string;
|
|
11
|
+
|
|
12
|
+
loaded?: boolean;
|
|
13
|
+
layouts?: RouteLayoutHandler[];
|
|
14
|
+
serverEndPoint?: () => Promise<any>;
|
|
15
|
+
serverHook?: () => Promise<any>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const PAGE_ROUTE = 0;
|
|
2
|
+
export const PAGE_SERVER_ROUTE = 1;
|
|
3
|
+
export const SERVER_ROUTE = 3;
|
|
4
|
+
export const LAYOUT_ROUTE = 4;
|
|
5
|
+
export const LAYOUT_SERVER_ROUTE = 5;
|
|
6
|
+
export const HOOK_ROUTE = 6;
|
|
7
|
+
export const HOOK_SERVER_ROUTE = 7;
|
|
8
|
+
export const ERROR_ROUTE = 8;
|
|
9
|
+
|
|
10
|
+
export type RouteType =
|
|
11
|
+
| typeof PAGE_ROUTE
|
|
12
|
+
| typeof PAGE_SERVER_ROUTE
|
|
13
|
+
| typeof SERVER_ROUTE
|
|
14
|
+
| typeof LAYOUT_ROUTE
|
|
15
|
+
| typeof LAYOUT_SERVER_ROUTE
|
|
16
|
+
| typeof HOOK_ROUTE
|
|
17
|
+
| typeof HOOK_SERVER_ROUTE
|
|
18
|
+
| typeof ERROR_ROUTE
|
|
19
|
+
| -1;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type ServerRequest from "./ServerRequest";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* For +server.
|
|
5
|
+
*/
|
|
6
|
+
type ServerEndPoint = { [key: string]: ServerRequest } & {
|
|
7
|
+
/**
|
|
8
|
+
* Performs a GET.
|
|
9
|
+
*/
|
|
10
|
+
get?: ServerRequest;
|
|
11
|
+
/**
|
|
12
|
+
* Performs a POST.
|
|
13
|
+
*/
|
|
14
|
+
post?: ServerRequest;
|
|
15
|
+
/**
|
|
16
|
+
* Performs a PATCH.
|
|
17
|
+
*/
|
|
18
|
+
patch?: ServerRequest;
|
|
19
|
+
/**
|
|
20
|
+
* Performs a PUT.
|
|
21
|
+
*/
|
|
22
|
+
put?: ServerRequest;
|
|
23
|
+
/**
|
|
24
|
+
* Performs a DELETE.
|
|
25
|
+
*/
|
|
26
|
+
del?: ServerRequest;
|
|
27
|
+
/**
|
|
28
|
+
* Performs an OPTIONS request.
|
|
29
|
+
*/
|
|
30
|
+
options?: ServerRequest;
|
|
31
|
+
/**
|
|
32
|
+
* Performs a HEAD request.
|
|
33
|
+
*/
|
|
34
|
+
head?: ServerRequest;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default ServerEndPoint;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import CookieHelper from "../server/CookieHelper";
|
|
2
|
+
import HeaderHelper from "../server/HeaderHelper";
|
|
3
|
+
|
|
4
|
+
export default interface ServerLoadEvent {
|
|
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
|
+
/**
|
|
21
|
+
* The server request.
|
|
22
|
+
*/
|
|
23
|
+
request: Request;
|
|
24
|
+
//response: ServerResponse;
|
|
25
|
+
/**
|
|
26
|
+
* A helper for getting and setting cookie data.
|
|
27
|
+
*/
|
|
28
|
+
cookies: CookieHelper;
|
|
29
|
+
/**
|
|
30
|
+
* A helper for getting and setting headers.
|
|
31
|
+
*/
|
|
32
|
+
headers: HeaderHelper;
|
|
33
|
+
/**
|
|
34
|
+
* An optional adapter object, containing adapter-specific functionality.
|
|
35
|
+
*/
|
|
36
|
+
adapter: Record<PropertyKey, any>;
|
|
37
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default function pathToRegex(path: string): RegExp {
|
|
2
|
+
const pattern =
|
|
3
|
+
path
|
|
4
|
+
.split("/")
|
|
5
|
+
// Replace `[slug]` with a grouped match so we can pull out params
|
|
6
|
+
.map((p) => {
|
|
7
|
+
return p.replace(/\[([^/]+?)\]/, "(?<$1>[^\\/]+?)");
|
|
8
|
+
})
|
|
9
|
+
.join("\\/")
|
|
10
|
+
// Replace globs with regex syntax
|
|
11
|
+
.replaceAll("*", ".*") +
|
|
12
|
+
// Ignore trailing slashes -- `/path` should be treated the same as `/path/`
|
|
13
|
+
"/*";
|
|
14
|
+
return new RegExp(`^${pattern}/*$`);
|
|
15
|
+
}
|