@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.
Files changed (193) hide show
  1. package/README.md +20 -2
  2. package/dist/Jsonify-Dg6gChd3.d.mts +10 -0
  3. package/dist/Jsonify-Dg6gChd3.d.mts.map +1 -0
  4. package/dist/ParseRouteParams-DBmqlmLh.d.mts +42 -0
  5. package/dist/ParseRouteParams-DBmqlmLh.d.mts.map +1 -0
  6. package/dist/{RouteType-B6BMOyYw.mjs → Router-C4ADUfhv.mjs} +30 -42
  7. package/dist/Router-C4ADUfhv.mjs.map +1 -0
  8. package/dist/Server-BnjLJleZ.mjs +104 -0
  9. package/dist/Server-BnjLJleZ.mjs.map +1 -0
  10. package/dist/Server-Cler2w5O.d.mts +61 -0
  11. package/dist/Server-Cler2w5O.d.mts.map +1 -0
  12. package/dist/ServerEvent-CTx3Q-hk.d.mts +48 -0
  13. package/dist/ServerEvent-CTx3Q-hk.d.mts.map +1 -0
  14. package/dist/{ServerEvent-CxPwA4lH.mjs → ServerEvent-Dljn5i7A.mjs} +37 -11
  15. package/dist/ServerEvent-Dljn5i7A.mjs.map +1 -0
  16. package/dist/Site-CE0_o5Ci.d.mts +566 -0
  17. package/dist/Site-CE0_o5Ci.d.mts.map +1 -0
  18. package/dist/StandardSchema-D28L8_zZ.d.mts +100 -0
  19. package/dist/StandardSchema-D28L8_zZ.d.mts.map +1 -0
  20. package/dist/TypedResponse-6lznqN4U.d.mts +22 -0
  21. package/dist/TypedResponse-6lznqN4U.d.mts.map +1 -0
  22. package/dist/{_page-BQs4WvCO.mjs → _page-4cQKs_Dn.mjs} +3 -5
  23. package/dist/_page-4cQKs_Dn.mjs.map +1 -0
  24. package/dist/bin/index.d.mts +1 -1
  25. package/dist/bin/index.js +15 -11
  26. package/dist/bin/index.js.map +1 -1
  27. package/dist/clientEntry.d.mts +1 -0
  28. package/dist/clientEntry.mjs +59 -0
  29. package/dist/clientEntry.mjs.map +1 -0
  30. package/dist/clientEntryDev.d.mts +1 -0
  31. package/dist/clientEntryDev.mjs +7 -0
  32. package/dist/clientEntryDev.mjs.map +1 -0
  33. package/dist/dev.d.mts +3 -0
  34. package/dist/dev.mjs +3 -0
  35. package/dist/endpoint-EULRGCKA.mjs +108 -0
  36. package/dist/endpoint-EULRGCKA.mjs.map +1 -0
  37. package/dist/flattenHeaders-DM5qsSqO.mjs +13 -0
  38. package/dist/flattenHeaders-DM5qsSqO.mjs.map +1 -0
  39. package/dist/form.d.mts +35 -0
  40. package/dist/form.d.mts.map +1 -0
  41. package/dist/form.mjs +48 -0
  42. package/dist/form.mjs.map +1 -0
  43. package/dist/index.d.mts +72 -156
  44. package/dist/index.d.mts.map +1 -1
  45. package/dist/index.mjs +158 -26
  46. package/dist/index.mjs.map +1 -1
  47. package/dist/load-BntzvmwL.mjs +228 -0
  48. package/dist/load-BntzvmwL.mjs.map +1 -0
  49. package/dist/nav.d.mts +58 -5
  50. package/dist/nav.d.mts.map +1 -1
  51. package/dist/nav.mjs +63 -209
  52. package/dist/nav.mjs.map +1 -1
  53. package/dist/openapi.d.mts +144 -0
  54. package/dist/openapi.d.mts.map +1 -0
  55. package/dist/openapi.mjs +2 -0
  56. package/dist/pathTrie-D4Ax2Mu9.mjs +160 -0
  57. package/dist/pathTrie-D4Ax2Mu9.mjs.map +1 -0
  58. package/dist/plugin-D8Www11B.mjs +202 -0
  59. package/dist/plugin-D8Www11B.mjs.map +1 -0
  60. package/dist/{response-C5TtAsh1.mjs → response-Bg4w2S1q.mjs} +16 -39
  61. package/dist/response-Bg4w2S1q.mjs.map +1 -0
  62. package/dist/response.d.mts +309 -289
  63. package/dist/response.d.mts.map +1 -1
  64. package/dist/response.mjs +3 -4
  65. package/dist/run.d.mts +11 -4
  66. package/dist/run.d.mts.map +1 -1
  67. package/dist/run.mjs +2 -8
  68. package/dist/runOpenApi-DlOgF2Il.mjs +1419 -0
  69. package/dist/runOpenApi-DlOgF2Il.mjs.map +1 -0
  70. package/dist/schema.d.mts +32 -0
  71. package/dist/schema.d.mts.map +1 -0
  72. package/dist/schema.mjs +2 -0
  73. package/dist/server/Server.d.mts +2 -0
  74. package/dist/server/Server.mjs +2 -0
  75. package/dist/server.d.mts +11 -8
  76. package/dist/server.d.mts.map +1 -1
  77. package/dist/server.mjs +133 -4
  78. package/dist/server.mjs.map +1 -0
  79. package/dist/serverEntry-1qY3wZig.mjs +351 -0
  80. package/dist/serverEntry-1qY3wZig.mjs.map +1 -0
  81. package/dist/serverEntry-BAO8m03V.d.mts +6 -0
  82. package/dist/serverEntry-BAO8m03V.d.mts.map +1 -0
  83. package/dist/serverEntry.d.mts +2 -0
  84. package/dist/serverEntry.mjs +2 -0
  85. package/dist/state.d.mts.map +1 -1
  86. package/dist/state.mjs +2 -3
  87. package/dist/test.d.mts +8 -10
  88. package/dist/test.d.mts.map +1 -1
  89. package/dist/test.mjs +181 -79
  90. package/dist/test.mjs.map +1 -1
  91. package/dist/{seeOther-B4Yhu9iq.mjs → unprocessable-DAZbrDeR.mjs} +34 -13
  92. package/dist/unprocessable-DAZbrDeR.mjs.map +1 -0
  93. package/dist/validate-Ok4krlkX.mjs +40 -0
  94. package/dist/validate-Ok4krlkX.mjs.map +1 -0
  95. package/package.json +30 -18
  96. package/dist/RouteType-B6BMOyYw.mjs.map +0 -1
  97. package/dist/Server-CF5wDJp6.d.mts +0 -78
  98. package/dist/Server-CF5wDJp6.d.mts.map +0 -1
  99. package/dist/ServerEvent-CxPwA4lH.mjs.map +0 -1
  100. package/dist/Site-DgC6WWn1.d.mts +0 -78
  101. package/dist/Site-DgC6WWn1.d.mts.map +0 -1
  102. package/dist/_page-BQs4WvCO.mjs.map +0 -1
  103. package/dist/connectMiddleware-D7nehjj3.mjs +0 -252
  104. package/dist/connectMiddleware-D7nehjj3.mjs.map +0 -1
  105. package/dist/pathToRegex-TUAMc3zS.mjs +0 -11
  106. package/dist/pathToRegex-TUAMc3zS.mjs.map +0 -1
  107. package/dist/response-C5TtAsh1.mjs.map +0 -1
  108. package/dist/run-BPFtSYA3.mjs +0 -332
  109. package/dist/run-BPFtSYA3.mjs.map +0 -1
  110. package/dist/seeOther-B4Yhu9iq.mjs.map +0 -1
  111. package/src/bin/index.ts +0 -11
  112. package/src/index.ts +0 -30
  113. package/src/nav/formSubmit.ts +0 -56
  114. package/src/nav/load.ts +0 -9
  115. package/src/nav/loadData.ts +0 -134
  116. package/src/nav/navigate.ts +0 -164
  117. package/src/nav/reload.ts +0 -9
  118. package/src/nav.ts +0 -4
  119. package/src/response/badRequest.ts +0 -19
  120. package/src/response/created.ts +0 -20
  121. package/src/response/forbidden.ts +0 -21
  122. package/src/response/found.ts +0 -30
  123. package/src/response/methodNotAllowed.ts +0 -18
  124. package/src/response/movedPermanently.ts +0 -25
  125. package/src/response/notFound.ts +0 -24
  126. package/src/response/notModified.ts +0 -34
  127. package/src/response/ok.ts +0 -29
  128. package/src/response/permanentRedirect.ts +0 -30
  129. package/src/response/response.ts +0 -22
  130. package/src/response/seeOther.ts +0 -19
  131. package/src/response/serverError.ts +0 -24
  132. package/src/response/temporaryRedirect.ts +0 -31
  133. package/src/response/transfer.ts +0 -14
  134. package/src/response/unauthorized.ts +0 -21
  135. package/src/response/unprocessable.ts +0 -18
  136. package/src/response.ts +0 -37
  137. package/src/run/prepareTemplate.ts +0 -39
  138. package/src/run/run.ts +0 -62
  139. package/src/run/runBuild.ts +0 -77
  140. package/src/run/runDev.ts +0 -89
  141. package/src/run/runPreview.ts +0 -93
  142. package/src/run.ts +0 -6
  143. package/src/server/CookieHelper.ts +0 -36
  144. package/src/server/HeaderHelper.ts +0 -25
  145. package/src/server/Routerx.ts +0 -72
  146. package/src/server/Server.ts +0 -144
  147. package/src/server/ServerEvent.ts +0 -39
  148. package/src/server/connect/bufferToArrayBuffer.ts +0 -8
  149. package/src/server/connect/connectMiddleware.ts +0 -81
  150. package/src/server/connect/flattenHeaders.ts +0 -44
  151. package/src/server/connect/nodeMessageToNodeResponse.ts +0 -89
  152. package/src/server/connect/readableToBuffer.ts +0 -16
  153. package/src/server/connect/requestToNodeMessage.ts +0 -26
  154. package/src/server/contentType.ts +0 -85
  155. package/src/server/types/HttpMethod.ts +0 -12
  156. package/src/server/types/MiddlewareFunction.ts +0 -8
  157. package/src/server/types/ServerFunction.ts +0 -7
  158. package/src/server.ts +0 -7
  159. package/src/site/Router.ts +0 -164
  160. package/src/site/Site.ts +0 -214
  161. package/src/site/clientEntry.ts +0 -108
  162. package/src/site/clientEntryDev.ts +0 -3
  163. package/src/site/defaultAdapter.ts +0 -11
  164. package/src/site/manifest.ts +0 -54
  165. package/src/site/serverEntry.ts +0 -439
  166. package/src/state/$page.ts +0 -24
  167. package/src/state/client.ts +0 -15
  168. package/src/state.ts +0 -3
  169. package/src/test/runTest.ts +0 -350
  170. package/src/test.ts +0 -3
  171. package/src/types/Adapter.ts +0 -8
  172. package/src/types/ClientState.ts +0 -8
  173. package/src/types/InternalState.ts +0 -7
  174. package/src/types/LayoutHandler.ts +0 -5
  175. package/src/types/LayoutPath.ts +0 -10
  176. package/src/types/ManifestRoute.ts +0 -6
  177. package/src/types/PageEndPoint.ts +0 -39
  178. package/src/types/PageLoadEvent.ts +0 -14
  179. package/src/types/PageServerAction.ts +0 -7
  180. package/src/types/PageServerEndPoint.ts +0 -16
  181. package/src/types/PageServerLoad.ts +0 -7
  182. package/src/types/PageState.ts +0 -8
  183. package/src/types/Route.ts +0 -11
  184. package/src/types/RouteHandler.ts +0 -16
  185. package/src/types/RouteLayoutHandler.ts +0 -5
  186. package/src/types/RouteMatchResult.ts +0 -7
  187. package/src/types/RouteType.ts +0 -19
  188. package/src/types/ServerEndPoint.ts +0 -37
  189. package/src/types/ServerHook.ts +0 -11
  190. package/src/types/ServerLoadEvent.ts +0 -37
  191. package/src/types/ServerRequest.ts +0 -7
  192. package/src/utils/pathToRegex.ts +0 -15
  193. package/src/vite-env.d.ts +0 -2
package/dist/test.mjs CHANGED
@@ -1,10 +1,9 @@
1
- import "./pathToRegex-TUAMc3zS.mjs";
2
- import { a as LAYOUT_SERVER_ROUTE, c as SERVER_ROUTE, i as LAYOUT_ROUTE, l as Router, n as HOOK_ROUTE, o as PAGE_ROUTE, r as HOOK_SERVER_ROUTE, s as PAGE_SERVER_ROUTE } from "./RouteType-B6BMOyYw.mjs";
3
- import { t as $page_default } from "./_page-BQs4WvCO.mjs";
4
- import { n as ok, r as notFound, t as seeOther } from "./seeOther-B4Yhu9iq.mjs";
5
- import { t as ServerEvent } from "./ServerEvent-CxPwA4lH.mjs";
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: () => import(
24
- /* @vite-ignore */
25
- fpath.join(site.root, r.file)
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 = new URL(ev.request.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
- $page_default.url = url;
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 PAGE_ROUTE:
42
- case LAYOUT_ROUTE:
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 PAGE_SERVER_ROUTE:
47
- case LAYOUT_SERVER_ROUTE:
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 SERVER_ROUTE: return await loadData(ev, url, handler, ev.request.method.toLowerCase().replace("delete", "del"), params);
51
- case HOOK_ROUTE:
52
- case HOOK_SERVER_ROUTE: break;
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
- if (serverEndPoint && serverEndPoint[functionName]) {
59
- const serverParams = buildServerParams(ev, url, params);
60
- if (handler.serverHook) {
61
- const serverHook = (await handler.serverHook()).default;
62
- if (serverHook?.handle) await serverHook.handle(serverParams);
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
- const serverParams = buildServerParams(ev, url, params);
73
- if (handler.serverHook) {
74
- const serverHook = (await handler.serverHook()).default;
75
- if (serverHook?.handle) await serverHook.handle(serverParams);
76
- }
77
- let data = {};
78
- if (handler.layouts) for (let layout of handler.layouts) {
79
- const layoutEndPoint = (await layout.endPoint())?.default;
80
- const layoutResponse = await loadClientAndServerData(url, params, serverParams, data, layoutEndPoint, layout.serverEndPoint && (await layout.serverEndPoint())?.default);
81
- if (layoutResponse?.ok === false) return layoutResponse;
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
- let endPointResponse = await loadClientAndServerData(url, params, serverParams, data, clientEndPoint, serverEndPoint);
84
- if (endPointResponse?.ok === false) return endPointResponse;
85
- let $props = { data };
86
- let styles = "";
87
- let component = clientEndPoint.component;
88
- let slots = void 0;
89
- if (handler.layouts) {
90
- let slotFunctions = [];
91
- slotFunctions[handler.layouts.length] = (_, $context) => {
92
- let { body, head } = clientEndPoint.component($props, $context);
93
- styles += head;
94
- return body;
95
- };
96
- for (let i = handler.layouts.length - 1; i >= 0; i--) {
97
- const layoutEndPoint = (await handler.layouts[i].endPoint())?.default;
98
- if (layoutEndPoint?.component) if (i === 0) {
99
- component = layoutEndPoint.component;
100
- slots = { _: slotFunctions[i + 1] };
101
- } else slotFunctions[i] = (_, $context) => {
102
- let { body, head } = layoutEndPoint.component($props, $context, { _: slotFunctions[i + 1] });
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
- const serverParams = buildServerParams(ev, url, params);
129
- if (handler.serverHook) {
130
- const serverHook = (await handler.serverHook()).default;
131
- if (serverHook?.handle) await serverHook.handle(serverParams);
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 await action(serverParams) || seeOther(url.pathname);
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, data, params);
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
- function buildServerParams(ev, url, params) {
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 content-type
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
- * content-type header will be set to "text/plain".
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 = { "content-type": "application/json" };
12
+ headers = { "Content-Type": "application/json" };
13
13
  body = JSON.stringify(body);
14
- } else headers = { "content-type": "text/plain" };
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 a
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
- export { response as a, transfer as i, ok as n, notFound as r, seeOther as t };
110
- //# sourceMappingURL=seeOther-B4Yhu9iq.mjs.map
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"}