@torpor/build 0.4.14 → 1.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/README.md +20 -2
- package/dist/Jsonify-Dg6gChd3.d.mts +10 -0
- package/dist/Jsonify-Dg6gChd3.d.mts.map +1 -0
- package/dist/ParseRouteParams-DBmqlmLh.d.mts +42 -0
- package/dist/ParseRouteParams-DBmqlmLh.d.mts.map +1 -0
- package/dist/{RouteType-B6BMOyYw.mjs → Router-C4ADUfhv.mjs} +30 -42
- package/dist/Router-C4ADUfhv.mjs.map +1 -0
- package/dist/Server-BnjLJleZ.mjs +104 -0
- package/dist/Server-BnjLJleZ.mjs.map +1 -0
- package/dist/Server-Cler2w5O.d.mts +61 -0
- package/dist/Server-Cler2w5O.d.mts.map +1 -0
- package/dist/ServerEvent-CTx3Q-hk.d.mts +48 -0
- package/dist/ServerEvent-CTx3Q-hk.d.mts.map +1 -0
- package/dist/{ServerEvent-CxPwA4lH.mjs → ServerEvent-Dljn5i7A.mjs} +37 -11
- package/dist/ServerEvent-Dljn5i7A.mjs.map +1 -0
- package/dist/Site-CE0_o5Ci.d.mts +566 -0
- package/dist/Site-CE0_o5Ci.d.mts.map +1 -0
- package/dist/StandardSchema-D28L8_zZ.d.mts +100 -0
- package/dist/StandardSchema-D28L8_zZ.d.mts.map +1 -0
- package/dist/TypedResponse-6lznqN4U.d.mts +22 -0
- package/dist/TypedResponse-6lznqN4U.d.mts.map +1 -0
- package/dist/{_page-BQs4WvCO.mjs → _page-4cQKs_Dn.mjs} +3 -5
- package/dist/_page-4cQKs_Dn.mjs.map +1 -0
- package/dist/bin/index.d.mts +1 -1
- package/dist/bin/index.js +15 -11
- package/dist/bin/index.js.map +1 -1
- package/dist/clientEntry.d.mts +1 -0
- package/dist/clientEntry.mjs +59 -0
- package/dist/clientEntry.mjs.map +1 -0
- package/dist/clientEntryDev.d.mts +1 -0
- package/dist/clientEntryDev.mjs +7 -0
- package/dist/clientEntryDev.mjs.map +1 -0
- package/dist/dev.d.mts +3 -0
- package/dist/dev.mjs +3 -0
- package/dist/endpoint-EULRGCKA.mjs +108 -0
- package/dist/endpoint-EULRGCKA.mjs.map +1 -0
- package/dist/flattenHeaders-DM5qsSqO.mjs +13 -0
- package/dist/flattenHeaders-DM5qsSqO.mjs.map +1 -0
- package/dist/form.d.mts +35 -0
- package/dist/form.d.mts.map +1 -0
- package/dist/form.mjs +48 -0
- package/dist/form.mjs.map +1 -0
- package/dist/index.d.mts +72 -156
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +158 -26
- package/dist/index.mjs.map +1 -1
- package/dist/load-BntzvmwL.mjs +228 -0
- package/dist/load-BntzvmwL.mjs.map +1 -0
- package/dist/nav.d.mts +58 -5
- package/dist/nav.d.mts.map +1 -1
- package/dist/nav.mjs +63 -209
- package/dist/nav.mjs.map +1 -1
- package/dist/openapi.d.mts +144 -0
- package/dist/openapi.d.mts.map +1 -0
- package/dist/openapi.mjs +2 -0
- package/dist/pathTrie-D4Ax2Mu9.mjs +160 -0
- package/dist/pathTrie-D4Ax2Mu9.mjs.map +1 -0
- package/dist/plugin-D8Www11B.mjs +202 -0
- package/dist/plugin-D8Www11B.mjs.map +1 -0
- package/dist/{response-C5TtAsh1.mjs → response-Bg4w2S1q.mjs} +16 -39
- package/dist/response-Bg4w2S1q.mjs.map +1 -0
- package/dist/response.d.mts +309 -289
- package/dist/response.d.mts.map +1 -1
- package/dist/response.mjs +3 -4
- package/dist/run.d.mts +11 -4
- package/dist/run.d.mts.map +1 -1
- package/dist/run.mjs +2 -8
- package/dist/runOpenApi-DlOgF2Il.mjs +1419 -0
- package/dist/runOpenApi-DlOgF2Il.mjs.map +1 -0
- package/dist/schema.d.mts +32 -0
- package/dist/schema.d.mts.map +1 -0
- package/dist/schema.mjs +2 -0
- package/dist/server/Server.d.mts +2 -0
- package/dist/server/Server.mjs +2 -0
- package/dist/server.d.mts +11 -8
- package/dist/server.d.mts.map +1 -1
- package/dist/server.mjs +133 -4
- package/dist/server.mjs.map +1 -0
- package/dist/serverEntry-1qY3wZig.mjs +351 -0
- package/dist/serverEntry-1qY3wZig.mjs.map +1 -0
- package/dist/serverEntry-BAO8m03V.d.mts +6 -0
- package/dist/serverEntry-BAO8m03V.d.mts.map +1 -0
- package/dist/serverEntry.d.mts +2 -0
- package/dist/serverEntry.mjs +2 -0
- package/dist/state.d.mts.map +1 -1
- package/dist/state.mjs +2 -3
- package/dist/test.d.mts +8 -10
- package/dist/test.d.mts.map +1 -1
- package/dist/test.mjs +181 -79
- package/dist/test.mjs.map +1 -1
- package/dist/{seeOther-B4Yhu9iq.mjs → unprocessable-DAZbrDeR.mjs} +34 -13
- package/dist/unprocessable-DAZbrDeR.mjs.map +1 -0
- package/dist/validate-Ok4krlkX.mjs +40 -0
- package/dist/validate-Ok4krlkX.mjs.map +1 -0
- package/package.json +30 -18
- package/dist/RouteType-B6BMOyYw.mjs.map +0 -1
- package/dist/Server-CF5wDJp6.d.mts +0 -78
- package/dist/Server-CF5wDJp6.d.mts.map +0 -1
- package/dist/ServerEvent-CxPwA4lH.mjs.map +0 -1
- package/dist/Site-DgC6WWn1.d.mts +0 -78
- package/dist/Site-DgC6WWn1.d.mts.map +0 -1
- package/dist/_page-BQs4WvCO.mjs.map +0 -1
- package/dist/connectMiddleware-D7nehjj3.mjs +0 -252
- package/dist/connectMiddleware-D7nehjj3.mjs.map +0 -1
- package/dist/pathToRegex-TUAMc3zS.mjs +0 -11
- package/dist/pathToRegex-TUAMc3zS.mjs.map +0 -1
- package/dist/response-C5TtAsh1.mjs.map +0 -1
- package/dist/run-BPFtSYA3.mjs +0 -332
- package/dist/run-BPFtSYA3.mjs.map +0 -1
- package/dist/seeOther-B4Yhu9iq.mjs.map +0 -1
- package/src/bin/index.ts +0 -11
- package/src/index.ts +0 -30
- package/src/nav/formSubmit.ts +0 -56
- package/src/nav/load.ts +0 -9
- package/src/nav/loadData.ts +0 -134
- package/src/nav/navigate.ts +0 -164
- package/src/nav/reload.ts +0 -9
- package/src/nav.ts +0 -4
- package/src/response/badRequest.ts +0 -19
- package/src/response/created.ts +0 -20
- package/src/response/forbidden.ts +0 -21
- package/src/response/found.ts +0 -30
- package/src/response/methodNotAllowed.ts +0 -18
- package/src/response/movedPermanently.ts +0 -25
- package/src/response/notFound.ts +0 -24
- package/src/response/notModified.ts +0 -34
- package/src/response/ok.ts +0 -29
- package/src/response/permanentRedirect.ts +0 -30
- package/src/response/response.ts +0 -22
- package/src/response/seeOther.ts +0 -19
- package/src/response/serverError.ts +0 -24
- package/src/response/temporaryRedirect.ts +0 -31
- package/src/response/transfer.ts +0 -14
- package/src/response/unauthorized.ts +0 -21
- package/src/response/unprocessable.ts +0 -18
- package/src/response.ts +0 -37
- package/src/run/prepareTemplate.ts +0 -39
- package/src/run/run.ts +0 -62
- package/src/run/runBuild.ts +0 -77
- package/src/run/runDev.ts +0 -89
- package/src/run/runPreview.ts +0 -93
- package/src/run.ts +0 -6
- package/src/server/CookieHelper.ts +0 -36
- package/src/server/HeaderHelper.ts +0 -25
- package/src/server/Routerx.ts +0 -72
- package/src/server/Server.ts +0 -144
- package/src/server/ServerEvent.ts +0 -39
- package/src/server/connect/bufferToArrayBuffer.ts +0 -8
- package/src/server/connect/connectMiddleware.ts +0 -81
- package/src/server/connect/flattenHeaders.ts +0 -44
- package/src/server/connect/nodeMessageToNodeResponse.ts +0 -89
- package/src/server/connect/readableToBuffer.ts +0 -16
- package/src/server/connect/requestToNodeMessage.ts +0 -26
- package/src/server/contentType.ts +0 -85
- package/src/server/types/HttpMethod.ts +0 -12
- package/src/server/types/MiddlewareFunction.ts +0 -8
- package/src/server/types/ServerFunction.ts +0 -7
- package/src/server.ts +0 -7
- package/src/site/Router.ts +0 -164
- package/src/site/Site.ts +0 -214
- package/src/site/clientEntry.ts +0 -108
- package/src/site/clientEntryDev.ts +0 -3
- package/src/site/defaultAdapter.ts +0 -11
- package/src/site/manifest.ts +0 -54
- package/src/site/serverEntry.ts +0 -439
- package/src/state/$page.ts +0 -24
- package/src/state/client.ts +0 -15
- package/src/state.ts +0 -3
- package/src/test/runTest.ts +0 -350
- package/src/test.ts +0 -3
- package/src/types/Adapter.ts +0 -8
- package/src/types/ClientState.ts +0 -8
- package/src/types/InternalState.ts +0 -7
- package/src/types/LayoutHandler.ts +0 -5
- package/src/types/LayoutPath.ts +0 -10
- package/src/types/ManifestRoute.ts +0 -6
- package/src/types/PageEndPoint.ts +0 -39
- package/src/types/PageLoadEvent.ts +0 -14
- package/src/types/PageServerAction.ts +0 -7
- package/src/types/PageServerEndPoint.ts +0 -16
- package/src/types/PageServerLoad.ts +0 -7
- package/src/types/PageState.ts +0 -8
- package/src/types/Route.ts +0 -11
- package/src/types/RouteHandler.ts +0 -16
- package/src/types/RouteLayoutHandler.ts +0 -5
- package/src/types/RouteMatchResult.ts +0 -7
- package/src/types/RouteType.ts +0 -19
- package/src/types/ServerEndPoint.ts +0 -37
- package/src/types/ServerHook.ts +0 -11
- package/src/types/ServerLoadEvent.ts +0 -37
- package/src/types/ServerRequest.ts +0 -7
- package/src/utils/pathToRegex.ts +0 -15
- package/src/vite-env.d.ts +0 -2
package/dist/test.mjs
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import "./
|
|
2
|
-
import {
|
|
3
|
-
import { t as
|
|
4
|
-
import { n as
|
|
5
|
-
import { t as
|
|
1
|
+
import { t as Router } from "./Router-C4ADUfhv.mjs";
|
|
2
|
+
import { i as notFound, n as seeOther, r as ok } from "./unprocessable-DAZbrDeR.mjs";
|
|
3
|
+
import { t as ServerEvent } from "./ServerEvent-Dljn5i7A.mjs";
|
|
4
|
+
import { n as ValidationError, t as validate } from "./validate-Ok4krlkX.mjs";
|
|
5
|
+
import { a as searchParamsToRecord, i as validationErrorResponse, n as validateEndpointParams, o as $page, r as validateHandlerInput, s as formDataToRecord, t as endpointSchema } from "./endpoint-EULRGCKA.mjs";
|
|
6
6
|
import fpath from "node:path";
|
|
7
|
-
|
|
8
7
|
//#region src/test/runTest.ts
|
|
9
8
|
/**
|
|
10
9
|
* Runs a site route in test mode
|
|
@@ -20,48 +19,85 @@ async function runTest(site, route, ev) {
|
|
|
20
19
|
router.addPages(site.routes.map((r) => ({
|
|
21
20
|
path: r.path,
|
|
22
21
|
type: r.type,
|
|
23
|
-
endPoint: () =>
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
endPoint: async () => {
|
|
23
|
+
if (r.file) {
|
|
24
|
+
const mod = await import(
|
|
25
|
+
/* @vite-ignore */
|
|
26
|
+
fpath.join(site.root, r.file)
|
|
27
|
+
);
|
|
28
|
+
if (r.file.endsWith(".torp")) return { default: { component: mod.default } };
|
|
29
|
+
return mod;
|
|
30
|
+
}
|
|
31
|
+
return { default: r.endPoint };
|
|
32
|
+
},
|
|
27
33
|
subFolder: r.subFolder
|
|
28
34
|
})));
|
|
29
35
|
return await load(router, ev, "%COMPONENT_BODY%");
|
|
30
36
|
}
|
|
31
37
|
async function load(router, ev, template) {
|
|
32
|
-
const url =
|
|
38
|
+
const url = ev.url;
|
|
33
39
|
const path = url.pathname;
|
|
34
40
|
const query = url.searchParams;
|
|
35
41
|
const route = router.match(path, query);
|
|
36
42
|
if (!route) return notFound();
|
|
37
|
-
$
|
|
43
|
+
$page.url = url;
|
|
44
|
+
if (path.endsWith("/_error")) {
|
|
45
|
+
$page.status = parseInt(query.get("status") ?? "404");
|
|
46
|
+
$page.error = { message: query.get("message") ?? "" };
|
|
47
|
+
} else $page.status = 200;
|
|
38
48
|
const handler = route.handler;
|
|
39
49
|
const params = route.params || {};
|
|
40
50
|
switch (handler.type) {
|
|
41
|
-
case
|
|
42
|
-
case
|
|
51
|
+
case 0:
|
|
52
|
+
case 4:
|
|
43
53
|
if (ev.request.method === "GET") return await loadView(ev, url, handler, params, template);
|
|
44
|
-
else if (ev.request.method === "POST") return await runAction(ev, url, handler, params, query);
|
|
54
|
+
else if (ev.request.method === "POST") return await runAction(ev, url, handler, handler.serverEndPoint && (await handler.serverEndPoint()).default, params, query);
|
|
45
55
|
break;
|
|
46
|
-
case
|
|
47
|
-
case
|
|
56
|
+
case 1:
|
|
57
|
+
case 5:
|
|
48
58
|
if (ev.request.method === "GET") return await loadData(ev, url, handler, "load", params);
|
|
59
|
+
else if (ev.request.method === "POST") return await runAction(ev, url, handler, handler.endPoint && (await handler.endPoint()).default, params, query);
|
|
49
60
|
break;
|
|
50
|
-
case
|
|
51
|
-
case
|
|
52
|
-
case
|
|
61
|
+
case 3: return await loadData(ev, url, handler, ev.request.method.toLowerCase().replace("delete", "del"), params);
|
|
62
|
+
case 8: return await loadView(ev, url, handler, params, template);
|
|
63
|
+
case 6:
|
|
64
|
+
case 7: break;
|
|
53
65
|
}
|
|
54
66
|
return notFound();
|
|
55
67
|
}
|
|
56
68
|
async function loadData(ev, url, handler, functionName, params) {
|
|
57
69
|
const serverEndPoint = (await handler.endPoint()).default;
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
70
|
+
const handlerFn = serverEndPoint?.[functionName];
|
|
71
|
+
if (serverEndPoint && handlerFn) {
|
|
72
|
+
try {
|
|
73
|
+
params = await validateEndpointParams(serverEndPoint, params);
|
|
74
|
+
} catch (error) {
|
|
75
|
+
if (error instanceof ValidationError) return notFound();
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
const values = await validateHandlerInput(endpointSchema(serverEndPoint, functionName), functionName, url, ev);
|
|
80
|
+
const serverParams = buildServerParams(ev, url, params, values);
|
|
81
|
+
const serverHooks = await loadServerHooks(handler);
|
|
82
|
+
let entered = 0;
|
|
83
|
+
let enterResponse = void 0;
|
|
84
|
+
try {
|
|
85
|
+
for (; entered < serverHooks.length && !enterResponse; entered++) {
|
|
86
|
+
const hookResult = await serverHooks[entered].enter?.(serverParams);
|
|
87
|
+
if (hookResult) enterResponse = hookResult;
|
|
88
|
+
}
|
|
89
|
+
return enterResponse || await handlerFn(serverParams) || ok();
|
|
90
|
+
} finally {
|
|
91
|
+
while (entered > 0) {
|
|
92
|
+
entered--;
|
|
93
|
+
await serverHooks[entered].exit?.(serverParams);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
} catch (error) {
|
|
97
|
+
const response = validationErrorResponse(error);
|
|
98
|
+
if (response) return response;
|
|
99
|
+
throw error;
|
|
63
100
|
}
|
|
64
|
-
return await serverEndPoint[functionName](serverParams) || ok();
|
|
65
101
|
}
|
|
66
102
|
return notFound();
|
|
67
103
|
}
|
|
@@ -69,75 +105,130 @@ async function loadView(ev, url, handler, params, template) {
|
|
|
69
105
|
const clientEndPoint = (await handler.endPoint()).default;
|
|
70
106
|
if (!clientEndPoint?.component) return notFound();
|
|
71
107
|
const serverEndPoint = handler.serverEndPoint && (await handler.serverEndPoint())?.default;
|
|
72
|
-
|
|
73
|
-
if (
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
if (
|
|
108
|
+
let query;
|
|
109
|
+
if (serverEndPoint) {
|
|
110
|
+
try {
|
|
111
|
+
params = await validateEndpointParams(serverEndPoint, params);
|
|
112
|
+
} catch (error) {
|
|
113
|
+
if (error instanceof ValidationError) return notFound();
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
const loadSchema = endpointSchema(serverEndPoint, "load");
|
|
117
|
+
if (loadSchema) try {
|
|
118
|
+
query = await validate(loadSchema, searchParamsToRecord(url.searchParams));
|
|
119
|
+
} catch (error) {
|
|
120
|
+
const response = validationErrorResponse(error);
|
|
121
|
+
if (response) return response;
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
82
124
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
let
|
|
86
|
-
let
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const
|
|
98
|
-
if (
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
125
|
+
const serverParams = buildServerParams(ev, url, params, query ? { query } : {});
|
|
126
|
+
const serverHooks = await loadServerHooks(handler);
|
|
127
|
+
let entered = 0;
|
|
128
|
+
let enterResponse = void 0;
|
|
129
|
+
try {
|
|
130
|
+
for (; entered < serverHooks.length && !enterResponse; entered++) {
|
|
131
|
+
const hookResult = await serverHooks[entered].enter?.(serverParams);
|
|
132
|
+
if (hookResult) enterResponse = hookResult;
|
|
133
|
+
}
|
|
134
|
+
if (enterResponse) return enterResponse;
|
|
135
|
+
let data = {};
|
|
136
|
+
if (handler.layouts) for (let layout of handler.layouts) {
|
|
137
|
+
const layoutEndPoint = (await layout.endPoint())?.default;
|
|
138
|
+
const layoutServerEndPoint = layout.serverEndPoint && (await layout.serverEndPoint())?.default;
|
|
139
|
+
const layoutResponse = await loadClientAndServerData(url, params, serverParams, data, layoutEndPoint, layoutServerEndPoint);
|
|
140
|
+
if (layoutResponse?.ok === false) return layoutResponse;
|
|
141
|
+
}
|
|
142
|
+
let endPointResponse = await loadClientAndServerData(url, params, serverParams, data, clientEndPoint, serverEndPoint);
|
|
143
|
+
if (endPointResponse?.ok === false) return endPointResponse;
|
|
144
|
+
let $props = { data };
|
|
145
|
+
let styles = "";
|
|
146
|
+
let component = clientEndPoint.component;
|
|
147
|
+
let slots = void 0;
|
|
148
|
+
if (handler.layouts) {
|
|
149
|
+
let slotFunctions = [];
|
|
150
|
+
slotFunctions[handler.layouts.length] = (_, $context) => {
|
|
151
|
+
let { body, head } = clientEndPoint.component($props, $context);
|
|
103
152
|
styles += head;
|
|
104
153
|
return body;
|
|
105
154
|
};
|
|
155
|
+
for (let i = handler.layouts.length - 1; i >= 0; i--) {
|
|
156
|
+
const layoutEndPoint = (await handler.layouts[i].endPoint())?.default;
|
|
157
|
+
if (layoutEndPoint?.component) if (i === 0) {
|
|
158
|
+
component = layoutEndPoint.component;
|
|
159
|
+
slots = { _: slotFunctions[i + 1] };
|
|
160
|
+
} else slotFunctions[i] = (_, $context) => {
|
|
161
|
+
let { body, head } = layoutEndPoint.component($props, $context, { _: slotFunctions[i + 1] });
|
|
162
|
+
styles += head;
|
|
163
|
+
return body;
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
let html;
|
|
168
|
+
try {
|
|
169
|
+
let { body, head } = component($props, void 0, slots);
|
|
170
|
+
styles += head;
|
|
171
|
+
html = template.replace("%COMPONENT_BODY%", body).replace("%COMPONENT_HEAD%", styles);
|
|
172
|
+
} catch (error) {
|
|
173
|
+
html = "<span style=\"color: red\">Script syntax error</span><p>" + error + "</p>";
|
|
174
|
+
console.log(error);
|
|
175
|
+
}
|
|
176
|
+
return new Response(html, {
|
|
177
|
+
status: 200,
|
|
178
|
+
headers: { "Content-Type": "text/html" }
|
|
179
|
+
});
|
|
180
|
+
} finally {
|
|
181
|
+
while (entered > 0) {
|
|
182
|
+
entered--;
|
|
183
|
+
await serverHooks[entered].exit?.(serverParams);
|
|
106
184
|
}
|
|
107
185
|
}
|
|
108
|
-
let html;
|
|
109
|
-
try {
|
|
110
|
-
let { body, head } = component($props, void 0, slots);
|
|
111
|
-
styles += head;
|
|
112
|
-
html = template.replace("%COMPONENT_BODY%", body).replace("%COMPONENT_HEAD", styles);
|
|
113
|
-
} catch (error) {
|
|
114
|
-
html = "<span style=\"color: red\">Script syntax error</span><p>" + error + "</p>";
|
|
115
|
-
console.log(error);
|
|
116
|
-
}
|
|
117
|
-
return new Response(html, {
|
|
118
|
-
status: 200,
|
|
119
|
-
headers: { "Content-Type": "text/html" }
|
|
120
|
-
});
|
|
121
186
|
}
|
|
122
|
-
async function runAction(ev, url, handler, params, query) {
|
|
187
|
+
async function runAction(ev, url, handler, serverEndPoint, params, query) {
|
|
123
188
|
const actionName = (Array.from(query.keys())[0] || "default").replace(/^\//, "");
|
|
124
|
-
const serverEndPoint = handler.serverEndPoint && (await handler.serverEndPoint()).default;
|
|
125
189
|
if (serverEndPoint?.actions) {
|
|
126
190
|
const action = serverEndPoint.actions[actionName];
|
|
127
191
|
if (action) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
192
|
+
if (actionName === "load" || actionName === "params") throw new Error(`The action name "${actionName}" is reserved. Rename the action (and its schema key, if any), e.g. to "save".`);
|
|
193
|
+
try {
|
|
194
|
+
params = await validateEndpointParams(serverEndPoint, params);
|
|
195
|
+
} catch (error) {
|
|
196
|
+
if (error instanceof ValidationError) return notFound();
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
199
|
+
let result;
|
|
200
|
+
try {
|
|
201
|
+
const schema = endpointSchema(serverEndPoint, actionName);
|
|
202
|
+
const form = schema ? await validate(schema, await formDataToRecord(ev.request)) : void 0;
|
|
203
|
+
const serverParams = buildServerParams(ev, url, params, form ? { form } : {});
|
|
204
|
+
const serverHooks = await loadServerHooks(handler);
|
|
205
|
+
let entered = 0;
|
|
206
|
+
let enterResponse = void 0;
|
|
207
|
+
try {
|
|
208
|
+
for (; entered < serverHooks.length && !enterResponse; entered++) {
|
|
209
|
+
const hookResult = await serverHooks[entered].enter?.(serverParams);
|
|
210
|
+
if (hookResult) enterResponse = hookResult;
|
|
211
|
+
}
|
|
212
|
+
result = enterResponse || await action(serverParams);
|
|
213
|
+
} finally {
|
|
214
|
+
while (entered > 0) {
|
|
215
|
+
entered--;
|
|
216
|
+
await serverHooks[entered].exit?.(serverParams);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
} catch (error) {
|
|
220
|
+
const response = validationErrorResponse(error);
|
|
221
|
+
if (response) return response;
|
|
222
|
+
throw error;
|
|
132
223
|
}
|
|
133
|
-
return
|
|
224
|
+
return result || seeOther(url.pathname);
|
|
134
225
|
}
|
|
135
226
|
}
|
|
136
227
|
return notFound();
|
|
137
228
|
}
|
|
138
229
|
async function loadClientAndServerData(url, params, serverParams, data, clientEndPoint, serverEndPoint) {
|
|
139
230
|
if (clientEndPoint?.load) {
|
|
140
|
-
const clientParams = buildClientParams(url,
|
|
231
|
+
const clientParams = buildClientParams(url, params, data);
|
|
141
232
|
const clientResponse = await clientEndPoint.load(clientParams);
|
|
142
233
|
if (clientResponse) if (clientResponse.ok) {
|
|
143
234
|
if (clientResponse.headers.get("Content-Type")?.includes("application/json")) Object.assign(data, await clientResponse.json());
|
|
@@ -157,18 +248,29 @@ function buildClientParams(url, params, data) {
|
|
|
157
248
|
data
|
|
158
249
|
};
|
|
159
250
|
}
|
|
160
|
-
|
|
251
|
+
/**
|
|
252
|
+
* Loads the route's server hooks, from the root down
|
|
253
|
+
*/
|
|
254
|
+
async function loadServerHooks(handler) {
|
|
255
|
+
const hooks = [];
|
|
256
|
+
if (handler.serverHooks) for (let serverHook of handler.serverHooks) hooks.push((await serverHook()).default);
|
|
257
|
+
return hooks;
|
|
258
|
+
}
|
|
259
|
+
function buildServerParams(ev, url, params, values = {}) {
|
|
161
260
|
return {
|
|
162
261
|
url,
|
|
163
262
|
params,
|
|
164
263
|
appData: {},
|
|
165
264
|
request: ev.request,
|
|
265
|
+
json: () => values.json !== void 0 ? Promise.resolve(values.json) : ev.json(),
|
|
266
|
+
form: () => values.form !== void 0 ? Promise.resolve(values.form) : formDataToRecord(ev.request),
|
|
267
|
+
query: () => values.query !== void 0 ? Promise.resolve(values.query) : Promise.resolve(searchParamsToRecord(url.searchParams)),
|
|
166
268
|
cookies: ev.cookies,
|
|
167
269
|
headers: ev.headers,
|
|
168
270
|
adapter: ev.adapter
|
|
169
271
|
};
|
|
170
272
|
}
|
|
171
|
-
|
|
172
273
|
//#endregion
|
|
173
274
|
export { runTest };
|
|
275
|
+
|
|
174
276
|
//# sourceMappingURL=test.mjs.map
|
package/dist/test.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test.mjs","names":["path","serverEndPoint: ServerEndPoint | undefined","serverHook: ServerHook | undefined","clientEndPoint: PageEndPoint | undefined","serverEndPoint: PageServerEndPoint | undefined","layoutEndPoint: PageEndPoint | undefined","$props: Record<string, any>","slots: Record<string, ServerSlotRender> | undefined","slotFunctions: ServerSlotRender[]"],"sources":["../src/test/runTest.ts"],"sourcesContent":["import { type ServerComponent, type ServerSlotRender } from \"@torpor/view/ssr\";\nimport path from \"node:path\";\nimport notFound from \"../response/notFound.ts\";\nimport ok from \"../response/ok.ts\";\nimport seeOther from \"../response/seeOther.ts\";\nimport ServerEvent from \"../server/ServerEvent.ts\";\nimport Router from \"../site/Router.ts\";\nimport Site from \"../site/Site\";\nimport $page from \"../state/$page.ts\";\nimport type PageEndPoint from \"../types/PageEndPoint.ts\";\nimport type PageServerEndPoint from \"../types/PageServerEndPoint.ts\";\nimport type RouteHandler from \"../types/RouteHandler.ts\";\nimport {\n\tHOOK_ROUTE,\n\tHOOK_SERVER_ROUTE,\n\tLAYOUT_ROUTE,\n\tLAYOUT_SERVER_ROUTE,\n\tPAGE_ROUTE,\n\tPAGE_SERVER_ROUTE,\n\tSERVER_ROUTE,\n} from \"../types/RouteType\";\nimport type ServerEndPoint from \"../types/ServerEndPoint.ts\";\nimport type ServerHook from \"../types/ServerHook.ts\";\nimport type ServerLoadEvent from \"../types/ServerLoadEvent.ts\";\n\n/**\n * Runs a site route in test mode\n * @param site The site configuration, with routing configured\n * @param route The route path e.g. `/home`\n * @param ev An optional ServerEvent, which you can use to set test cookies and headers\n * @returns An HTTP response containing the requested data, HTML, or error status\n */\nexport default async function runTest(\n\tsite: Site,\n\troute: string,\n\tev?: ServerEvent,\n): Promise<Response> {\n\tif (!route.startsWith(\"/\")) route += \"/\";\n\tif (!ev) {\n\t\tconst req = new Request(`http://localhost${route}`);\n\t\tev = new ServerEvent(req);\n\t}\n\n\t// Build the router from the Site object created by the user\n\tconst router = new Router();\n\trouter.addPages(\n\t\tsite.routes.map((r) => ({\n\t\t\tpath: r.path,\n\t\t\ttype: r.type,\n\t\t\tendPoint: () => import(/* @vite-ignore */ path.join(site.root, r.file)),\n\t\t\tsubFolder: r.subFolder,\n\t\t})),\n\t);\n\n\treturn await load(router, ev, \"%COMPONENT_BODY%\");\n}\n\n// HACK: This is a copy of serverEntry\n// TODO: Make it shared instead!\nasync function load(router: Router, ev: ServerEvent, template: string): Promise<Response> {\n\t//const url = new URL(`http://${process.env.HOST ?? \"localhost\"}${ev.request.url}`);\n\tconst url = new URL(ev.request.url);\n\tconst path = url.pathname;\n\tconst query = url.searchParams;\n\n\t//console.log(`handling ${ev.request.method} for '${path}'${query.size ? ` with ${query}` : \"\"}`);\n\n\tconst route = router.match(path, query);\n\tif (!route) {\n\t\treturn notFound();\n\t}\n\n\t// Update $page before building the components\n\t// TODO: Find somewhere better to put this\n\t$page.url = url;\n\n\tconst handler = route.handler;\n\tconst params = route.params || {};\n\n\t// TODO: Hit the server hook out here\n\t// We could maybe set data loading up as middleware on a route??????\n\n\tswitch (handler.type) {\n\t\tcase PAGE_ROUTE:\n\t\tcase LAYOUT_ROUTE: {\n\t\t\t// It's a /+page.ts or /_layout.ts endpoint\n\t\t\tif (ev.request.method === \"GET\") {\n\t\t\t\treturn await loadView(ev, url, handler, params, template);\n\t\t\t} else if (ev.request.method === \"POST\") {\n\t\t\t\treturn await runAction(ev, url, handler, params, query);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tcase PAGE_SERVER_ROUTE:\n\t\tcase LAYOUT_SERVER_ROUTE: {\n\t\t\t// It's a /+page.server.ts or /_layout.server.ts endpoint\n\t\t\tif (ev.request.method === \"GET\") {\n\t\t\t\treturn await loadData(ev, url, handler, \"load\", params);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tcase SERVER_ROUTE: {\n\t\t\t// It's a /+server.ts endpoint\n\t\t\tconst functionName = ev.request.method.toLowerCase().replace(\"delete\", \"del\");\n\t\t\treturn await loadData(ev, url, handler, functionName, params);\n\t\t}\n\t\tcase HOOK_ROUTE:\n\t\tcase HOOK_SERVER_ROUTE: {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn notFound();\n}\n\nasync function loadData(\n\tev: ServerEvent,\n\turl: URL,\n\thandler: RouteHandler,\n\tfunctionName: string,\n\tparams: Record<string, string>,\n) {\n\tconst serverEndPoint: ServerEndPoint | undefined = (await handler.endPoint()).default;\n\tif (serverEndPoint && serverEndPoint[functionName]) {\n\t\tconst serverParams = buildServerParams(ev, url, params);\n\n\t\tif (handler.serverHook) {\n\t\t\tconst serverHook: ServerHook | undefined = (await handler.serverHook()).default;\n\t\t\tif (serverHook?.handle) {\n\t\t\t\tawait serverHook.handle(serverParams);\n\t\t\t}\n\t\t}\n\n\t\tconst result = await serverEndPoint[functionName](serverParams);\n\n\t\t// If there was no response returned from load (such as errors or a\n\t\t// redirect), send an ok response\n\t\treturn result || ok();\n\t}\n\n\treturn notFound();\n}\n\nasync function loadView(\n\tev: ServerEvent,\n\turl: URL,\n\thandler: RouteHandler,\n\tparams: Record<string, string>,\n\ttemplate: string,\n) {\n\t// There must be a client endpoint with a component\n\tconst clientEndPoint: PageEndPoint | undefined = (await handler.endPoint()).default;\n\tif (!clientEndPoint?.component) {\n\t\treturn notFound();\n\t}\n\n\t// There may be a server endpoint\n\tconst serverEndPoint: PageServerEndPoint | undefined =\n\t\thandler.serverEndPoint && (await handler.serverEndPoint())?.default;\n\n\t// Maybe hit the server hook\n\tconst serverParams = buildServerParams(ev, url, params);\n\tif (handler.serverHook) {\n\t\tconst serverHook: ServerHook | undefined = (await handler.serverHook()).default;\n\t\tif (serverHook?.handle) {\n\t\t\tawait serverHook.handle(serverParams);\n\t\t}\n\t}\n\n\t// Pass the data into $props\n\t// TODO: Promise.all\n\t// 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\n\tlet data = {};\n\tif (handler.layouts) {\n\t\tfor (let layout of handler.layouts) {\n\t\t\tconst layoutEndPoint: PageEndPoint | undefined = (await layout.endPoint())?.default;\n\t\t\tconst layoutServerEndPoint: PageServerEndPoint | undefined =\n\t\t\t\tlayout.serverEndPoint && (await layout.serverEndPoint())?.default;\n\t\t\tconst layoutResponse = await loadClientAndServerData(\n\t\t\t\turl,\n\t\t\t\tparams,\n\t\t\t\tserverParams,\n\t\t\t\tdata,\n\t\t\t\tlayoutEndPoint,\n\t\t\t\tlayoutServerEndPoint,\n\t\t\t);\n\t\t\tif (layoutResponse?.ok === false) {\n\t\t\t\treturn layoutResponse;\n\t\t\t}\n\t\t}\n\t}\n\tlet endPointResponse = await loadClientAndServerData(\n\t\turl,\n\t\tparams,\n\t\tserverParams,\n\t\tdata,\n\t\tclientEndPoint,\n\t\tserverEndPoint,\n\t);\n\tif (endPointResponse?.ok === false) {\n\t\treturn endPointResponse;\n\t}\n\tlet $props: Record<string, any> = { data };\n\n\tlet styles = \"\";\n\n\t// If there are layouts, work our way upwards, pushing each component into\n\t// the default slot of its parent\n\t// TODO: Also handle layout server data\n\t// TODO: There's probably a nicer way to do this with reducers or something\n\tlet component = clientEndPoint.component as ServerComponent;\n\tlet slots: Record<string, ServerSlotRender> | undefined = undefined;\n\tif (handler.layouts) {\n\t\tlet slotFunctions: ServerSlotRender[] = [];\n\t\tslotFunctions[handler.layouts.length] = (_, $context) => {\n\t\t\tlet { body, head } = (clientEndPoint.component as ServerComponent)($props, $context);\n\t\t\tstyles += head;\n\t\t\treturn body;\n\t\t};\n\t\tfor (let i = handler.layouts.length - 1; i >= 0; i--) {\n\t\t\tconst layoutEndPoint: PageEndPoint | undefined = (await handler.layouts[i].endPoint())\n\t\t\t\t?.default;\n\t\t\tif (layoutEndPoint?.component) {\n\t\t\t\tif (i === 0) {\n\t\t\t\t\tcomponent = layoutEndPoint.component as ServerComponent;\n\t\t\t\t\tslots = { _: slotFunctions[i + 1] };\n\t\t\t\t} else {\n\t\t\t\t\tslotFunctions[i] = (_, $context) => {\n\t\t\t\t\t\tlet { body, head } = (layoutEndPoint.component as ServerComponent)($props, $context, {\n\t\t\t\t\t\t\t_: slotFunctions[i + 1],\n\t\t\t\t\t\t});\n\t\t\t\t\t\tstyles += head;\n\t\t\t\t\t\treturn body;\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tlet html;\n\ttry {\n\t\tlet { body, head } = component($props, undefined, slots);\n\t\tstyles += head;\n\t\thtml = template.replace(\"%COMPONENT_BODY%\", body).replace(\"%COMPONENT_HEAD\", styles);\n\t} catch (error) {\n\t\t// TODO: Show a proper Error component\n\t\thtml = '<span style=\"color: red\">Script syntax error</span><p>' + error + \"</p>\";\n\t\tconsole.log(error);\n\t}\n\n\treturn new Response(html, {\n\t\tstatus: 200,\n\t\theaders: {\n\t\t\t\"Content-Type\": \"text/html\",\n\t\t},\n\t});\n}\n\nasync function runAction(\n\tev: ServerEvent,\n\turl: URL,\n\thandler: RouteHandler,\n\tparams: Record<string, string>,\n\tquery: URLSearchParams,\n) {\n\tconst actionName = (Array.from(query.keys())[0] || \"default\").replace(/^\\//, \"\");\n\tconst serverEndPoint: PageServerEndPoint | undefined =\n\t\thandler.serverEndPoint && (await handler.serverEndPoint()).default;\n\tif (serverEndPoint?.actions) {\n\t\tconst action = serverEndPoint.actions[actionName];\n\t\tif (action) {\n\t\t\t// TODO: form.errors etc\n\t\t\tconst serverParams = buildServerParams(ev, url, params);\n\n\t\t\tif (handler.serverHook) {\n\t\t\t\tconst serverHook: ServerHook | undefined = (await handler.serverHook()).default;\n\t\t\t\tif (serverHook?.handle) {\n\t\t\t\t\tawait serverHook.handle(serverParams);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst result = await action(serverParams); // <--\n\n\t\t\t// If there was no response returned from the action (such as errors\n\t\t\t// or a redirect), reload the page by redirecting\n\t\t\treturn result || seeOther(url.pathname);\n\t\t}\n\t}\n\n\treturn notFound();\n}\n\nasync function loadClientAndServerData(\n\turl: URL,\n\tparams: Record<string, string>,\n\tserverParams: ServerLoadEvent,\n\tdata: Record<string, any>,\n\tclientEndPoint?: PageEndPoint,\n\tserverEndPoint?: PageServerEndPoint,\n) {\n\tif (clientEndPoint?.load) {\n\t\tconst clientParams = buildClientParams(url, data, params);\n\t\tconst clientResponse = await clientEndPoint.load(clientParams);\n\t\tif (clientResponse) {\n\t\t\tif (clientResponse.ok) {\n\t\t\t\tif (clientResponse.headers.get(\"Content-Type\")?.includes(\"application/json\")) {\n\t\t\t\t\tObject.assign(data, await clientResponse.json());\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn clientResponse;\n\t\t\t}\n\t\t}\n\t}\n\tif (serverEndPoint?.load) {\n\t\tconst serverResponse = await serverEndPoint.load(serverParams);\n\t\tif (serverResponse) {\n\t\t\tif (serverResponse.ok) {\n\t\t\t\tif (serverResponse.headers.get(\"Content-Type\")?.includes(\"application/json\")) {\n\t\t\t\t\tObject.assign(data, await serverResponse.json());\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn serverResponse;\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction buildClientParams(url: URL, params: Record<string, string>, data: Record<string, any>) {\n\treturn {\n\t\turl,\n\t\tparams,\n\t\tdata,\n\t};\n}\n\nfunction buildServerParams(\n\tev: ServerEvent,\n\turl: URL,\n\tparams: Record<string, string>,\n): ServerLoadEvent {\n\treturn {\n\t\turl,\n\t\tparams,\n\t\tappData: {},\n\t\trequest: ev.request,\n\t\tcookies: ev.cookies,\n\t\theaders: ev.headers,\n\t\tadapter: ev.adapter,\n\t};\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"test.mjs","names":["path","handlers"],"sources":["../src/test/runTest.ts"],"sourcesContent":["import { type ServerComponent, type ServerSlotRender } from \"@torpor/view/ssr\";\nimport path from \"node:path\";\nimport formDataToRecord from \"../form/formDataToRecord.ts\";\nimport notFound from \"../response/notFound.ts\";\nimport ok from \"../response/ok.ts\";\nimport seeOther from \"../response/seeOther.ts\";\nimport ServerEvent from \"../server/ServerEvent.ts\";\nimport Router from \"../site/Router.ts\";\nimport Site from \"../site/Site\";\nimport $page from \"../state/$serverPage.ts\";\nimport type PageEndPoint from \"../types/PageEndPoint.ts\";\nimport type PageServerEndPoint from \"../types/PageServerEndPoint.ts\";\nimport type RouteHandler from \"../types/RouteHandler.ts\";\nimport type ServerRequest from \"../types/ServerRequest.ts\";\nimport {\n\tERROR_ROUTE,\n\tHOOK_ROUTE,\n\tHOOK_SERVER_ROUTE,\n\tLAYOUT_ROUTE,\n\tLAYOUT_SERVER_ROUTE,\n\tPAGE_ROUTE,\n\tPAGE_SERVER_ROUTE,\n\tSERVER_ROUTE,\n} from \"../types/RouteType\";\nimport type ServerEndPoint from \"../types/ServerEndPoint.ts\";\nimport type ServerHook from \"../types/ServerHook.ts\";\nimport type ServerLoadEvent from \"../types/ServerLoadEvent.ts\";\nimport searchParamsToRecord from \"../utils/searchParamsToRecord.ts\";\nimport validate from \"../validation/validate.ts\";\nimport ValidationError from \"../validation/ValidationError.ts\";\nimport {\n\tendpointSchema,\n\tvalidateEndpointParams,\n\tvalidateHandlerInput,\n\tvalidationErrorResponse,\n} from \"../validation/endpoint.ts\";\n\n/**\n * Runs a site route in test mode\n * @param site The site configuration, with routing configured\n * @param route The route path e.g. `/home`\n * @param ev An optional ServerEvent, which you can use to set test cookies and headers\n * @returns An HTTP response containing the requested data, HTML, or error status\n */\nexport default async function runTest(\n\tsite: Site,\n\troute: string,\n\tev?: ServerEvent,\n): Promise<Response> {\n\tif (!route.startsWith(\"/\")) route += \"/\";\n\tif (!ev) {\n\t\tconst req = new Request(`http://localhost${route}`);\n\t\tev = new ServerEvent(req);\n\t}\n\n\t// Build the router from the Site object created by the user\n\tconst router = new Router();\n\trouter.addPages(\n\t\tsite.routes.map((r) => ({\n\t\t\tpath: r.path,\n\t\t\ttype: r.type,\n\t\t\tendPoint: async () => {\n\t\t\t\tif (r.file) {\n\t\t\t\t\tconst mod = await import(/* @vite-ignore */ path.join(site.root, r.file));\n\t\t\t\t\t// A .torp file's default export is a component, so wrap it as\n\t\t\t\t\t// a PageEndPoint ({ component }) for the entries\n\t\t\t\t\tif (r.file.endsWith(\".torp\")) {\n\t\t\t\t\t\treturn { default: { component: mod.default } };\n\t\t\t\t\t}\n\t\t\t\t\treturn mod;\n\t\t\t\t}\n\t\t\t\t// Inline endpoint (no file) — return directly from memory\n\t\t\t\treturn { default: r.endPoint };\n\t\t\t},\n\t\t\tsubFolder: r.subFolder,\n\t\t})),\n\t);\n\n\treturn await load(router, ev, \"%COMPONENT_BODY%\");\n}\n\n// HACK: This is a copy of serverEntry\n// TODO: Make it shared instead!\nasync function load(router: Router, ev: ServerEvent, template: string): Promise<Response> {\n\t//const url = new URL(`http://${process.env.HOST ?? \"localhost\"}${ev.request.url}`);\n\tconst url = ev.url;\n\tconst path = url.pathname;\n\tconst query = url.searchParams;\n\n\t//console.log(`handling ${ev.request.method} for '${path}'${query.size ? ` with ${query}` : \"\"}`);\n\n\tconst route = router.match(path, query);\n\tif (!route) {\n\t\treturn notFound();\n\t}\n\n\t// Update $page before building the components\n\t// TODO: Find somewhere better to put this\n\t$page.url = url;\n\tif (path.endsWith(\"/_error\")) {\n\t\t$page.status = parseInt(query.get(\"status\") ?? \"404\");\n\t\t$page.error = { message: query.get(\"message\") ?? \"\" };\n\t} else {\n\t\t$page.status = 200;\n\t}\n\n\tconst handler = route.handler;\n\tconst params = route.params || {};\n\n\t// TODO: Hit the server hook out here\n\t// We could maybe set data loading up as middleware on a route??????\n\n\tswitch (handler.type) {\n\t\tcase PAGE_ROUTE:\n\t\tcase LAYOUT_ROUTE: {\n\t\t\t// It's a /+page.ts or /_layout.ts endpoint\n\t\t\tif (ev.request.method === \"GET\") {\n\t\t\t\treturn await loadView(ev, url, handler, params, template);\n\t\t\t} else if (ev.request.method === \"POST\") {\n\t\t\t\tconst serverEndPoint: PageServerEndPoint | undefined =\n\t\t\t\t\thandler.serverEndPoint && (await handler.serverEndPoint()).default;\n\t\t\t\treturn await runAction(ev, url, handler, serverEndPoint, params, query);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tcase PAGE_SERVER_ROUTE:\n\t\tcase LAYOUT_SERVER_ROUTE: {\n\t\t\t// It's a /+page.server.ts or /_layout.server.ts endpoint\n\t\t\tif (ev.request.method === \"GET\") {\n\t\t\t\treturn await loadData(ev, url, handler, \"load\", params);\n\t\t\t} else if (ev.request.method === \"POST\") {\n\t\t\t\tconst serverEndPoint: PageServerEndPoint | undefined =\n\t\t\t\t\thandler.endPoint && (await handler.endPoint()).default;\n\t\t\t\treturn await runAction(ev, url, handler, serverEndPoint, params, query);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tcase SERVER_ROUTE: {\n\t\t\t// It's a /+server.ts endpoint\n\t\t\tconst functionName = ev.request.method.toLowerCase().replace(\"delete\", \"del\");\n\t\t\treturn await loadData(ev, url, handler, functionName, params);\n\t\t}\n\t\tcase ERROR_ROUTE: {\n\t\t\treturn await loadView(ev, url, handler, params, template);\n\t\t}\n\t\tcase HOOK_ROUTE:\n\t\tcase HOOK_SERVER_ROUTE: {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn notFound();\n}\n\nasync function loadData(\n\tev: ServerEvent,\n\turl: URL,\n\thandler: RouteHandler,\n\tfunctionName: string,\n\tparams: Record<string, any>,\n) {\n\tconst serverEndPoint: ServerEndPoint | undefined = (await handler.endPoint()).default;\n\t// `functionName` is computed from the request method, so the handlers are\n\t// accessed through a loose record\n\tconst handlers = serverEndPoint as unknown as Record<string, ServerRequest | undefined>;\n\tconst handlerFn: ServerRequest | undefined = handlers?.[functionName];\n\tif (serverEndPoint && handlerFn) {\n\t\t// Route params come from the URL, so a failed validation means the\n\t\t// resource doesn't exist\n\t\ttry {\n\t\t\tparams = await validateEndpointParams(serverEndPoint, params);\n\t\t} catch (error) {\n\t\t\tif (error instanceof ValidationError) return notFound();\n\t\t\tthrow error;\n\t\t}\n\n\t\ttry {\n\t\t\t// If the endpoint declares a schema for this handler, validate its\n\t\t\t// input up front and reject with 422 if it fails. get/head schemas\n\t\t\t// validate the query string; other schemas validate the json body\n\t\t\tconst schema = endpointSchema(serverEndPoint, functionName);\n\t\t\tconst values = await validateHandlerInput(schema, functionName, url, ev);\n\n\t\t\tconst serverParams = buildServerParams(ev, url, params, values);\n\n\t\t\tconst serverHooks = await loadServerHooks(handler);\n\t\t\tlet entered = 0;\n\t\t\tlet enterResponse: Response | undefined = undefined;\n\t\t\ttry {\n\t\t\t\t// Hooks run from the root down; a hook's enter can return a\n\t\t\t\t// Response to short-circuit the request\n\t\t\t\tfor (; entered < serverHooks.length && !enterResponse; entered++) {\n\t\t\t\t\tconst hookResult = await serverHooks[entered].enter?.(serverParams);\n\t\t\t\t\tif (hookResult) {\n\t\t\t\t\t\tenterResponse = hookResult;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst result = enterResponse || (await handlerFn(serverParams));\n\n\t\t\t\t// If there was no response returned from load (such as errors or a\n\t\t\t\t// redirect), send an ok response\n\t\t\t\treturn result || ok();\n\t\t\t} finally {\n\t\t\t\t// Run exit hooks in reverse order for hooks that were entered\n\t\t\t\twhile (entered > 0) {\n\t\t\t\t\tentered--;\n\t\t\t\t\tawait serverHooks[entered].exit?.(serverParams);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst response = validationErrorResponse(error);\n\t\t\tif (response) return response;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\treturn notFound();\n}\n\nasync function loadView(\n\tev: ServerEvent,\n\turl: URL,\n\thandler: RouteHandler,\n\tparams: Record<string, any>,\n\ttemplate: string,\n) {\n\t// There must be a client endpoint with a component\n\tconst clientEndPoint: PageEndPoint | undefined = (await handler.endPoint()).default;\n\tif (!clientEndPoint?.component) {\n\t\treturn notFound();\n\t}\n\n\t// There may be a server endpoint\n\tconst serverEndPoint: PageServerEndPoint | undefined =\n\t\thandler.serverEndPoint && (await handler.serverEndPoint())?.default;\n\n\t// Validate the route params and the load query against the server\n\t// endpoint's schemas, if it declares any. Params come from the URL, so a\n\t// failed validation means the resource doesn't exist; a failed query\n\t// returns like any other load failure\n\tlet query: unknown;\n\tif (serverEndPoint) {\n\t\ttry {\n\t\t\tparams = await validateEndpointParams(serverEndPoint, params);\n\t\t} catch (error) {\n\t\t\tif (error instanceof ValidationError) return notFound();\n\t\t\tthrow error;\n\t\t}\n\t\tconst loadSchema = endpointSchema(serverEndPoint, \"load\");\n\t\tif (loadSchema) {\n\t\t\ttry {\n\t\t\t\tquery = await validate(loadSchema, searchParamsToRecord(url.searchParams));\n\t\t\t} catch (error) {\n\t\t\t\tconst response = validationErrorResponse(error);\n\t\t\t\tif (response) return response;\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Maybe hit the server hooks\n\tconst serverParams = buildServerParams(ev, url, params, query ? { query } : {});\n\tconst serverHooks = await loadServerHooks(handler);\n\tlet entered = 0;\n\tlet enterResponse: Response | undefined = undefined;\n\ttry {\n\t\t// Hooks run from the root down; a hook's enter can return a Response\n\t\t// to short-circuit the request\n\t\tfor (; entered < serverHooks.length && !enterResponse; entered++) {\n\t\t\tconst hookResult = await serverHooks[entered].enter?.(serverParams);\n\t\t\tif (hookResult) {\n\t\t\t\tenterResponse = hookResult;\n\t\t\t}\n\t\t}\n\t\tif (enterResponse) {\n\t\t\treturn enterResponse;\n\t\t}\n\n\t\t// Pass the data into $props\n\t\t// TODO: Promise.all\n\t\t// 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\n\t\tlet data = {};\n\t\tif (handler.layouts) {\n\t\t\tfor (let layout of handler.layouts) {\n\t\t\t\tconst layoutEndPoint: PageEndPoint | undefined = (await layout.endPoint())?.default;\n\t\t\t\tconst layoutServerEndPoint: PageServerEndPoint | undefined =\n\t\t\t\t\tlayout.serverEndPoint && (await layout.serverEndPoint())?.default;\n\t\t\t\tconst layoutResponse = await loadClientAndServerData(\n\t\t\t\t\turl,\n\t\t\t\t\tparams,\n\t\t\t\t\tserverParams,\n\t\t\t\t\tdata,\n\t\t\t\t\tlayoutEndPoint,\n\t\t\t\t\tlayoutServerEndPoint,\n\t\t\t\t);\n\t\t\t\tif (layoutResponse?.ok === false) {\n\t\t\t\t\treturn layoutResponse;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tlet endPointResponse = await loadClientAndServerData(\n\t\t\turl,\n\t\t\tparams,\n\t\t\tserverParams,\n\t\t\tdata,\n\t\t\tclientEndPoint,\n\t\t\tserverEndPoint,\n\t\t);\n\t\tif (endPointResponse?.ok === false) {\n\t\t\treturn endPointResponse;\n\t\t}\n\t\tlet $props: Record<string, any> = { data };\n\n\t\tlet styles = \"\";\n\n\t\t// If there are layouts, work our way upwards, pushing each component into\n\t\t// the default slot of its parent\n\t\t// TODO: Also handle layout server data\n\t\t// TODO: There's probably a nicer way to do this with reducers or something\n\t\tlet component = clientEndPoint.component as ServerComponent;\n\t\tlet slots: Record<string, ServerSlotRender> | undefined = undefined;\n\t\tif (handler.layouts) {\n\t\t\tlet slotFunctions: ServerSlotRender[] = [];\n\t\t\tslotFunctions[handler.layouts.length] = (_, $context) => {\n\t\t\t\tlet { body, head } = (clientEndPoint.component as ServerComponent)($props, $context);\n\t\t\t\tstyles += head;\n\t\t\t\treturn body;\n\t\t\t};\n\t\t\tfor (let i = handler.layouts.length - 1; i >= 0; i--) {\n\t\t\t\tconst layoutEndPoint: PageEndPoint | undefined = (await handler.layouts[i].endPoint())\n\t\t\t\t\t?.default;\n\t\t\t\tif (layoutEndPoint?.component) {\n\t\t\t\t\tif (i === 0) {\n\t\t\t\t\t\tcomponent = layoutEndPoint.component as ServerComponent;\n\t\t\t\t\t\tslots = { _: slotFunctions[i + 1] };\n\t\t\t\t\t} else {\n\t\t\t\t\t\tslotFunctions[i] = (_, $context) => {\n\t\t\t\t\t\t\tlet { body, head } = (layoutEndPoint.component as ServerComponent)($props, $context, {\n\t\t\t\t\t\t\t\t_: slotFunctions[i + 1],\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tstyles += head;\n\t\t\t\t\t\t\treturn body;\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlet html;\n\t\ttry {\n\t\t\tlet { body, head } = component($props, undefined, slots);\n\t\t\tstyles += head;\n\t\t\thtml = template.replace(\"%COMPONENT_BODY%\", body).replace(\"%COMPONENT_HEAD%\", styles);\n\t\t} catch (error) {\n\t\t\t// TODO: Show a proper Error component\n\t\t\thtml = '<span style=\"color: red\">Script syntax error</span><p>' + error + \"</p>\";\n\t\t\tconsole.log(error);\n\t\t}\n\n\t\treturn new Response(html, {\n\t\t\tstatus: 200,\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"text/html\",\n\t\t\t},\n\t\t});\n\t} finally {\n\t\t// Run exit hooks in reverse order for hooks that were entered\n\t\twhile (entered > 0) {\n\t\t\tentered--;\n\t\t\tawait serverHooks[entered].exit?.(serverParams);\n\t\t}\n\t}\n}\n\nasync function runAction(\n\tev: ServerEvent,\n\turl: URL,\n\thandler: RouteHandler,\n\tserverEndPoint: PageServerEndPoint | undefined,\n\tparams: Record<string, any>,\n\tquery: URLSearchParams,\n) {\n\tconst actionName = (Array.from(query.keys())[0] || \"default\").replace(/^\\//, \"\");\n\tif (serverEndPoint?.actions) {\n\t\tconst action = serverEndPoint.actions[actionName];\n\t\tif (action) {\n\t\t\tif (actionName === \"load\" || actionName === \"params\") {\n\t\t\t\t// These names are reserved in the endpoint's schema map: `load`\n\t\t\t\t// validates the query string and `params` the route params, so\n\t\t\t\t// an action with one of these names would get the wrong schema\n\t\t\t\t// applied to its form data\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`The action name \"${actionName}\" is reserved. ` +\n\t\t\t\t\t\t`Rename the action (and its schema key, if any), e.g. to \"save\".`,\n\t\t\t\t);\n\t\t\t}\n\t\t\t// TODO: form.errors etc\n\t\t\t// Route params come from the URL, so a failed validation means the\n\t\t\t// resource doesn't exist\n\t\t\ttry {\n\t\t\t\tparams = await validateEndpointParams(serverEndPoint, params);\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof ValidationError) return notFound();\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\tlet result: Response | undefined | void;\n\t\t\ttry {\n\t\t\t\t// If the endpoint declares a schema for this action, validate the\n\t\t\t\t// submitted form data up front and reject with 422 if it fails\n\t\t\t\tconst schema = endpointSchema(serverEndPoint, actionName);\n\t\t\t\tconst form = schema\n\t\t\t\t\t? await validate(schema, await formDataToRecord(ev.request))\n\t\t\t\t\t: undefined;\n\n\t\t\t\tconst serverParams = buildServerParams(ev, url, params, form ? { form } : {});\n\n\t\t\t\tconst serverHooks = await loadServerHooks(handler);\n\t\t\t\tlet entered = 0;\n\t\t\t\tlet enterResponse: Response | undefined = undefined;\n\t\t\t\ttry {\n\t\t\t\t\t// Hooks run from the root down; a hook's enter can return a\n\t\t\t\t\t// Response to short-circuit the action\n\t\t\t\t\tfor (; entered < serverHooks.length && !enterResponse; entered++) {\n\t\t\t\t\t\tconst hookResult = await serverHooks[entered].enter?.(serverParams);\n\t\t\t\t\t\tif (hookResult) {\n\t\t\t\t\t\t\tenterResponse = hookResult;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tresult = enterResponse || (await action(serverParams));\n\t\t\t\t} finally {\n\t\t\t\t\t// Run exit hooks in reverse order for hooks that were entered\n\t\t\t\t\twhile (entered > 0) {\n\t\t\t\t\t\tentered--;\n\t\t\t\t\t\tawait serverHooks[entered].exit?.(serverParams);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconst response = validationErrorResponse(error);\n\t\t\t\tif (response) return response;\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\t// If there was no response returned from the action (such as errors\n\t\t\t// or a redirect), reload the page by redirecting\n\t\t\treturn result || seeOther(url.pathname);\n\t\t}\n\t}\n\n\treturn notFound();\n}\n\nasync function loadClientAndServerData(\n\turl: URL,\n\tparams: Record<string, any>,\n\tserverParams: ServerLoadEvent,\n\tdata: Record<string, any>,\n\tclientEndPoint?: PageEndPoint,\n\tserverEndPoint?: PageServerEndPoint,\n) {\n\tif (clientEndPoint?.load) {\n\t\tconst clientParams = buildClientParams(url, params, data);\n\t\tconst clientResponse = await clientEndPoint.load(clientParams);\n\t\tif (clientResponse) {\n\t\t\tif (clientResponse.ok) {\n\t\t\t\tif (clientResponse.headers.get(\"Content-Type\")?.includes(\"application/json\")) {\n\t\t\t\t\tObject.assign(data, await clientResponse.json());\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn clientResponse;\n\t\t\t}\n\t\t}\n\t}\n\tif (serverEndPoint?.load) {\n\t\tconst serverResponse = await serverEndPoint.load(serverParams);\n\t\tif (serverResponse) {\n\t\t\tif (serverResponse.ok) {\n\t\t\t\tif (serverResponse.headers.get(\"Content-Type\")?.includes(\"application/json\")) {\n\t\t\t\t\tObject.assign(data, await serverResponse.json());\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn serverResponse;\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction buildClientParams(url: URL, params: Record<string, any>, data: Record<string, any>) {\n\treturn {\n\t\turl,\n\t\tparams,\n\t\tdata,\n\t};\n}\n\n/**\n * Loads the route's server hooks, from the root down\n */\nasync function loadServerHooks(handler: RouteHandler): Promise<ServerHook[]> {\n\tconst hooks: ServerHook[] = [];\n\tif (handler.serverHooks) {\n\t\tfor (let serverHook of handler.serverHooks) {\n\t\t\thooks.push((await serverHook()).default);\n\t\t}\n\t}\n\treturn hooks;\n}\n\nfunction buildServerParams(\n\tev: ServerEvent,\n\turl: URL,\n\tparams: Record<string, any>,\n\tvalues: { json?: unknown; form?: unknown; query?: unknown } = {},\n): ServerLoadEvent {\n\treturn {\n\t\turl,\n\t\tparams,\n\t\tappData: {},\n\t\trequest: ev.request,\n\t\t// The inputs may have been validated already, in which case the request\n\t\t// body has been consumed and the parsed values are returned instead.\n\t\t// Their types come from the endpoint's schemas, so they are loosely\n\t\t// typed here\n\t\tjson: (): Promise<any> =>\n\t\t\tvalues.json !== undefined ? Promise.resolve(values.json) : ev.json(),\n\t\tform: (): Promise<any> =>\n\t\t\tvalues.form !== undefined ? Promise.resolve(values.form) : formDataToRecord(ev.request),\n\t\tquery: (): Promise<any> =>\n\t\t\tvalues.query !== undefined\n\t\t\t\t? Promise.resolve(values.query)\n\t\t\t\t: Promise.resolve(searchParamsToRecord(url.searchParams)),\n\t\tcookies: ev.cookies,\n\t\theaders: ev.headers,\n\t\tadapter: ev.adapter,\n\t};\n}\n"],"mappings":"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"}
|
|
@@ -2,22 +2,21 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Creates a response with the supplied status code and optional body.
|
|
4
4
|
*
|
|
5
|
-
* If the body is an object, it will be converted to JSON and the
|
|
5
|
+
* If the body is an object, it will be converted to JSON and the Content-Type
|
|
6
6
|
* header set to "application/json". Otherwise, if the body is a string, the
|
|
7
|
-
*
|
|
7
|
+
* Content-Type header will be set to "text/plain".
|
|
8
8
|
*/
|
|
9
9
|
function response(status, body) {
|
|
10
10
|
let headers = void 0;
|
|
11
11
|
if (body) if (typeof body === "object") {
|
|
12
|
-
headers = { "
|
|
12
|
+
headers = { "Content-Type": "application/json" };
|
|
13
13
|
body = JSON.stringify(body);
|
|
14
|
-
} else headers = { "
|
|
14
|
+
} else headers = { "Content-Type": "text/plain" };
|
|
15
15
|
return new Response(body, {
|
|
16
16
|
status,
|
|
17
17
|
headers
|
|
18
18
|
});
|
|
19
19
|
}
|
|
20
|
-
|
|
21
20
|
//#endregion
|
|
22
21
|
//#region src/response/transfer.ts
|
|
23
22
|
/**
|
|
@@ -29,7 +28,6 @@ function transfer(status, location) {
|
|
|
29
28
|
headers: { location }
|
|
30
29
|
});
|
|
31
30
|
}
|
|
32
|
-
|
|
33
31
|
//#endregion
|
|
34
32
|
//#region src/response/notFound.ts
|
|
35
33
|
/**
|
|
@@ -54,7 +52,6 @@ function transfer(status, location) {
|
|
|
54
52
|
function notFound(body) {
|
|
55
53
|
return response(404, body ?? "Not found");
|
|
56
54
|
}
|
|
57
|
-
|
|
58
55
|
//#endregion
|
|
59
56
|
//#region src/response/ok.ts
|
|
60
57
|
/**
|
|
@@ -74,17 +71,19 @@ function notFound(body) {
|
|
|
74
71
|
* - TRACE: The response has a message body containing the request as received
|
|
75
72
|
* by the server.
|
|
76
73
|
*
|
|
77
|
-
* Although possible, successful PUT or DELETE requests often do not result in
|
|
78
|
-
* 200 OK response. It is more common to see 201 Created if the resource is
|
|
74
|
+
* Although possible, successful PUT or DELETE requests often do not result in
|
|
75
|
+
* a 200 OK response. It is more common to see 201 Created if the resource is
|
|
79
76
|
* uploaded or created for the first time, or 204 No Content upon successful
|
|
80
77
|
* deletion of a resource.
|
|
81
78
|
*
|
|
79
|
+
* An object body is typed: the client sees its JSON form through
|
|
80
|
+
* `makeApi`-created callers.
|
|
81
|
+
*
|
|
82
82
|
* See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/200
|
|
83
83
|
*/
|
|
84
84
|
function ok(body) {
|
|
85
85
|
return response(200, body);
|
|
86
86
|
}
|
|
87
|
-
|
|
88
87
|
//#endregion
|
|
89
88
|
//#region src/response/seeOther.ts
|
|
90
89
|
/**
|
|
@@ -104,7 +103,29 @@ function ok(body) {
|
|
|
104
103
|
function seeOther(location) {
|
|
105
104
|
return transfer(303, location);
|
|
106
105
|
}
|
|
107
|
-
|
|
108
106
|
//#endregion
|
|
109
|
-
|
|
110
|
-
|
|
107
|
+
//#region src/response/unprocessable.ts
|
|
108
|
+
/**
|
|
109
|
+
* 422 Unprocessable Content
|
|
110
|
+
*
|
|
111
|
+
* The HTTP 422 Unprocessable Content client error response status code
|
|
112
|
+
* indicates that the server understood the content type of the request content,
|
|
113
|
+
* and the syntax of the request content was correct, but it was unable to
|
|
114
|
+
* process the contained instructions.
|
|
115
|
+
*
|
|
116
|
+
* Clients that receive a 422 response should expect that repeating the request
|
|
117
|
+
* without modification will fail with the same error.
|
|
118
|
+
*
|
|
119
|
+
* An object body is typed: it becomes the page's `$props.form` (with its JSON
|
|
120
|
+
* form) after a form submit, so validation errors can be surfaced with their
|
|
121
|
+
* field types via `PageForm`.
|
|
122
|
+
*
|
|
123
|
+
* See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/422
|
|
124
|
+
*/
|
|
125
|
+
function unprocessable(body) {
|
|
126
|
+
return response(422, body ?? "Unprocessable");
|
|
127
|
+
}
|
|
128
|
+
//#endregion
|
|
129
|
+
export { transfer as a, notFound as i, seeOther as n, response as o, ok as r, unprocessable as t };
|
|
130
|
+
|
|
131
|
+
//# sourceMappingURL=unprocessable-DAZbrDeR.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unprocessable-DAZbrDeR.mjs","names":[],"sources":["../src/response/response.ts","../src/response/transfer.ts","../src/response/notFound.ts","../src/response/ok.ts","../src/response/seeOther.ts","../src/response/unprocessable.ts"],"sourcesContent":["/**\n * Creates a response with the supplied status code and optional body.\n *\n * If the body is an object, it will be converted to JSON and the Content-Type\n * header set to \"application/json\". Otherwise, if the body is a string, the\n * Content-Type header will be set to \"text/plain\".\n */\nexport default function response(status: number, body?: object | string): Response {\n\tlet headers: Record<string, string> | undefined = undefined;\n\tif (body) {\n\t\tif (typeof body === \"object\") {\n\t\t\theaders = { \"Content-Type\": \"application/json\" };\n\t\t\tbody = JSON.stringify(body);\n\t\t} else {\n\t\t\theaders = { \"Content-Type\": \"text/plain\" };\n\t\t}\n\t}\n\treturn new Response(body, {\n\t\tstatus,\n\t\theaders,\n\t});\n}\n","/**\n * Creates a redirect response with the supplied status code.\n */\nexport default function transfer(\n\tstatus: 300 | 301 | 302 | 303 | 304 | 307 | 308,\n\tlocation: string,\n): Response {\n\treturn new Response(null, {\n\t\tstatus,\n\t\theaders: {\n\t\t\tlocation,\n\t\t},\n\t});\n}\n","import response from \"./response\";\n\n/**\n * 404 Not Found\n *\n * The HTTP 404 Not Found client error response status code indicates that the\n * server cannot find the requested resource. Links that lead to a 404 page are\n * often called broken or dead links and can be subject to link rot.\n *\n * A 404 status code only indicates that the resource is missing without\n * indicating if this is temporary or permanent. If a resource is permanently\n * removed, servers should send the 410 Gone status instead.\n *\n * 404 errors on a website can lead to a poor user experience for your visitors,\n * so the number of broken links (internal and external) should be minimized to\n * prevent frustration for readers. Common causes of 404 responses are mistyped\n * URLs or pages that are moved or deleted without redirection. For more\n * information, see the Redirections in HTTP guide.\n *\n * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/404\n */\nexport default function notFound(body?: object | string): Response {\n\treturn response(404, body ?? \"Not found\");\n}\n","import type { Jsonify } from \"../types/Jsonify\";\nimport type TypedResponse from \"./TypedResponse\";\nimport response from \"./response\";\n\ntype OkResponse<T extends object | string | undefined> = T extends object\n\t? TypedResponse<Jsonify<T>>\n\t: Response;\n\n/**\n * 200 OK\n *\n * The HTTP 200 OK successful response status code indicates that a request has\n * succeeded. A 200 OK response is cacheable by default.\n *\n * A 200 OK response has a different meaning and format depending on the HTTP\n * request method. Here's how they vary for different methods:\n *\n * - GET: A resource was retrieved by the server and included in the response\n * body.\n * - POST: An action succeeded; the response has a message body describing the\n * result.\n * - HEAD: Identical to GET, except there is no message body.\n * - TRACE: The response has a message body containing the request as received\n * by the server.\n *\n * Although possible, successful PUT or DELETE requests often do not result in\n * a 200 OK response. It is more common to see 201 Created if the resource is\n * uploaded or created for the first time, or 204 No Content upon successful\n * deletion of a resource.\n *\n * An object body is typed: the client sees its JSON form through\n * `makeApi`-created callers.\n *\n * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/200\n */\nexport default function ok<T extends object | string | undefined>(body?: T): OkResponse<T> {\n\treturn response(200, body) as OkResponse<T>;\n}\n","import transfer from \"./transfer\";\n\n/**\n * 303 See Other\n *\n * The HTTP 303 See Other redirection response status code indicates that the\n * browser should redirect to the URL in the Location header instead of\n * rendering the requested resource.\n *\n * This response code is often sent back as a result of PUT or POST methods so\n * the client may retrieve a confirmation, or view a representation of a\n * real-world object (see HTTP range-14). The method to retrieve the redirected\n * resource is always GET.\n *\n * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/303\n */\nexport default function seeOther(location: string): Response {\n\treturn transfer(303, location);\n}\n","import response from \"./response\";\nimport type { Jsonify } from \"../types/Jsonify\";\nimport type TypedResponse from \"./TypedResponse\";\n\ntype UnprocessableResponse<T extends object | string | undefined> = T extends object\n\t? TypedResponse<Jsonify<T>>\n\t: Response;\n\n/**\n * 422 Unprocessable Content\n *\n * The HTTP 422 Unprocessable Content client error response status code\n * indicates that the server understood the content type of the request content,\n * and the syntax of the request content was correct, but it was unable to\n * process the contained instructions.\n *\n * Clients that receive a 422 response should expect that repeating the request\n * without modification will fail with the same error.\n *\n * An object body is typed: it becomes the page's `$props.form` (with its JSON\n * form) after a form submit, so validation errors can be surfaced with their\n * field types via `PageForm`.\n *\n * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/422\n */\nexport default function unprocessable<T extends object | string | undefined>(\n\tbody?: T,\n): UnprocessableResponse<T> {\n\treturn response(422, body ?? \"Unprocessable\") as UnprocessableResponse<T>;\n}\n"],"mappings":";;;;;;;;AAOA,SAAwB,SAAS,QAAgB,MAAkC;CAClF,IAAI,UAA8C,KAAA;CAClD,IAAI,MACH,IAAI,OAAO,SAAS,UAAU;EAC7B,UAAU,EAAE,gBAAgB,mBAAmB;EAC/C,OAAO,KAAK,UAAU,IAAI;CAC3B,OACC,UAAU,EAAE,gBAAgB,aAAa;CAG3C,OAAO,IAAI,SAAS,MAAM;EACzB;EACA;CACD,CAAC;AACF;;;;;;AClBA,SAAwB,SACvB,QACA,UACW;CACX,OAAO,IAAI,SAAS,MAAM;EACzB;EACA,SAAS,EACR,SACD;CACD,CAAC;AACF;;;;;;;;;;;;;;;;;;;;;;ACQA,SAAwB,SAAS,MAAkC;CAClE,OAAO,SAAS,KAAK,QAAQ,WAAW;AACzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACYA,SAAwB,GAA0C,MAAyB;CAC1F,OAAO,SAAS,KAAK,IAAI;AAC1B;;;;;;;;;;;;;;;;;ACrBA,SAAwB,SAAS,UAA4B;CAC5D,OAAO,SAAS,KAAK,QAAQ;AAC9B;;;;;;;;;;;;;;;;;;;;ACOA,SAAwB,cACvB,MAC2B;CAC3B,OAAO,SAAS,KAAK,QAAQ,eAAe;AAC7C"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
//#region src/validation/ValidationError.ts
|
|
2
|
+
/**
|
|
3
|
+
* The error thrown when a value fails standard schema validation. The
|
|
4
|
+
* framework turns it into a 422 response for endpoint request bodies.
|
|
5
|
+
*/
|
|
6
|
+
var ValidationError = class extends Error {
|
|
7
|
+
/**
|
|
8
|
+
* The issues reported by the schema.
|
|
9
|
+
*/
|
|
10
|
+
issues;
|
|
11
|
+
constructor(issues) {
|
|
12
|
+
super("Validation failed");
|
|
13
|
+
this.name = "ValidationError";
|
|
14
|
+
this.issues = issues;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region src/validation/validate.ts
|
|
19
|
+
/**
|
|
20
|
+
* Validates a value against a standard schema (zod, valibot, arktype, etc),
|
|
21
|
+
* returning the parsed value, or throwing a `ValidationError` with the
|
|
22
|
+
* schema's issues.
|
|
23
|
+
*
|
|
24
|
+
* ```ts
|
|
25
|
+
* const post = validate(postSchema, { title: "Hello" });
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @param schema A schema implementing the Standard Schema interface
|
|
29
|
+
* @param value The value to validate
|
|
30
|
+
* @returns The parsed value
|
|
31
|
+
*/
|
|
32
|
+
async function validate(schema, value) {
|
|
33
|
+
const result = await schema["~standard"].validate(value);
|
|
34
|
+
if (result.issues) throw new ValidationError(result.issues);
|
|
35
|
+
return result.value;
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { ValidationError as n, validate as t };
|
|
39
|
+
|
|
40
|
+
//# sourceMappingURL=validate-Ok4krlkX.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-Ok4krlkX.mjs","names":[],"sources":["../src/validation/ValidationError.ts","../src/validation/validate.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"../types/StandardSchema\";\n\n/**\n * The error thrown when a value fails standard schema validation. The\n * framework turns it into a 422 response for endpoint request bodies.\n */\nexport default class ValidationError extends Error {\n\t/**\n\t * The issues reported by the schema.\n\t */\n\tissues: ReadonlyArray<StandardSchemaV1.Issue>;\n\n\tconstructor(issues: ReadonlyArray<StandardSchemaV1.Issue>) {\n\t\tsuper(\"Validation failed\");\n\t\tthis.name = \"ValidationError\";\n\t\tthis.issues = issues;\n\t}\n}\n","import type { StandardSchemaV1 } from \"../types/StandardSchema\";\nimport ValidationError from \"./ValidationError\";\n\n/**\n * Validates a value against a standard schema (zod, valibot, arktype, etc),\n * returning the parsed value, or throwing a `ValidationError` with the\n * schema's issues.\n *\n * ```ts\n * const post = validate(postSchema, { title: \"Hello\" });\n * ```\n *\n * @param schema A schema implementing the Standard Schema interface\n * @param value The value to validate\n * @returns The parsed value\n */\nexport default async function validate<Schema extends StandardSchemaV1>(\n\tschema: Schema,\n\tvalue: unknown,\n): Promise<StandardSchemaV1.InferOutput<Schema>> {\n\tconst result = await schema[\"~standard\"].validate(value);\n\tif (result.issues) {\n\t\tthrow new ValidationError(result.issues);\n\t}\n\treturn result.value as StandardSchemaV1.InferOutput<Schema>;\n}\n"],"mappings":";;;;;AAMA,IAAqB,kBAArB,cAA6C,MAAM;;;;CAIlD;CAEA,YAAY,QAA+C;EAC1D,MAAM,mBAAmB;EACzB,KAAK,OAAO;EACZ,KAAK,SAAS;CACf;AACD;;;;;;;;;;;;;;;;ACDA,eAA8B,SAC7B,QACA,OACgD;CAChD,MAAM,SAAS,MAAM,OAAO,YAAY,CAAC,SAAS,KAAK;CACvD,IAAI,OAAO,QACV,MAAM,IAAI,gBAAgB,OAAO,MAAM;CAExC,OAAO,OAAO;AACf"}
|