@torpor/build 0.4.10 → 0.4.11

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 (84) hide show
  1. package/package.json +4 -4
  2. package/src/bin/index.ts +11 -0
  3. package/src/index.ts +30 -0
  4. package/src/nav/formSubmit.ts +56 -0
  5. package/src/nav/load.ts +9 -0
  6. package/src/nav/loadData.ts +134 -0
  7. package/src/nav/navigate.ts +164 -0
  8. package/src/nav/reload.ts +9 -0
  9. package/src/nav.ts +4 -0
  10. package/src/response/badRequest.ts +19 -0
  11. package/src/response/created.ts +20 -0
  12. package/src/response/forbidden.ts +21 -0
  13. package/src/response/found.ts +30 -0
  14. package/src/response/methodNotAllowed.ts +18 -0
  15. package/src/response/movedPermanently.ts +25 -0
  16. package/src/response/notFound.ts +24 -0
  17. package/src/response/notModified.ts +34 -0
  18. package/src/response/ok.ts +29 -0
  19. package/src/response/permanentRedirect.ts +30 -0
  20. package/src/response/response.ts +22 -0
  21. package/src/response/seeOther.ts +19 -0
  22. package/src/response/serverError.ts +24 -0
  23. package/src/response/temporaryRedirect.ts +31 -0
  24. package/src/response/transfer.ts +14 -0
  25. package/src/response/unauthorized.ts +21 -0
  26. package/src/response/unprocessable.ts +18 -0
  27. package/src/response.ts +37 -0
  28. package/src/run/prepareTemplate.ts +39 -0
  29. package/src/run/run.ts +62 -0
  30. package/src/run/runBuild.ts +77 -0
  31. package/src/run/runDev.ts +89 -0
  32. package/src/run/runPreview.ts +93 -0
  33. package/src/run.ts +6 -0
  34. package/src/server/CookieHelper.ts +36 -0
  35. package/src/server/HeaderHelper.ts +25 -0
  36. package/src/server/Routerx.ts +72 -0
  37. package/src/server/Server.ts +144 -0
  38. package/src/server/ServerEvent.ts +39 -0
  39. package/src/server/connect/bufferToArrayBuffer.ts +8 -0
  40. package/src/server/connect/connectMiddleware.ts +81 -0
  41. package/src/server/connect/flattenHeaders.ts +44 -0
  42. package/src/server/connect/nodeMessageToNodeResponse.ts +89 -0
  43. package/src/server/connect/readableToBuffer.ts +16 -0
  44. package/src/server/connect/requestToNodeMessage.ts +26 -0
  45. package/src/server/contentType.ts +85 -0
  46. package/src/server/types/HttpMethod.ts +12 -0
  47. package/src/server/types/MiddlewareFunction.ts +8 -0
  48. package/src/server/types/ServerFunction.ts +7 -0
  49. package/src/server.ts +7 -0
  50. package/src/site/Router.ts +164 -0
  51. package/src/site/Site.ts +214 -0
  52. package/src/site/clientEntry.ts +108 -0
  53. package/src/site/clientEntryDev.ts +3 -0
  54. package/src/site/defaultAdapter.ts +11 -0
  55. package/src/site/manifest.ts +54 -0
  56. package/src/site/serverEntry.ts +417 -0
  57. package/src/state/$page.ts +24 -0
  58. package/src/state/client.ts +15 -0
  59. package/src/state.ts +3 -0
  60. package/src/test/runTest.ts +350 -0
  61. package/src/test.ts +3 -0
  62. package/src/types/Adapter.ts +8 -0
  63. package/src/types/ClientState.ts +8 -0
  64. package/src/types/InternalState.ts +7 -0
  65. package/src/types/LayoutHandler.ts +5 -0
  66. package/src/types/LayoutPath.ts +10 -0
  67. package/src/types/ManifestRoute.ts +6 -0
  68. package/src/types/PageEndPoint.ts +39 -0
  69. package/src/types/PageLoadEvent.ts +14 -0
  70. package/src/types/PageServerAction.ts +7 -0
  71. package/src/types/PageServerEndPoint.ts +16 -0
  72. package/src/types/PageServerLoad.ts +7 -0
  73. package/src/types/PageState.ts +8 -0
  74. package/src/types/Route.ts +11 -0
  75. package/src/types/RouteHandler.ts +16 -0
  76. package/src/types/RouteLayoutHandler.ts +5 -0
  77. package/src/types/RouteMatchResult.ts +7 -0
  78. package/src/types/RouteType.ts +19 -0
  79. package/src/types/ServerEndPoint.ts +37 -0
  80. package/src/types/ServerHook.ts +11 -0
  81. package/src/types/ServerLoadEvent.ts +37 -0
  82. package/src/types/ServerRequest.ts +7 -0
  83. package/src/utils/pathToRegex.ts +15 -0
  84. package/src/vite-env.d.ts +2 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@torpor/build",
3
- "version": "0.4.10",
3
+ "version": "0.4.11",
4
4
  "description": "A framework for building sites and apps with Torpor",
5
5
  "type": "module",
6
6
  "exports": {
@@ -38,7 +38,7 @@
38
38
  },
39
39
  "files": [
40
40
  "dist",
41
- "./src/types/manifest.d.ts"
41
+ "src"
42
42
  ],
43
43
  "publishConfig": {
44
44
  "access": "public"
@@ -54,8 +54,8 @@
54
54
  "dotenv": "^17.2.3",
55
55
  "vite": "^7.2.2",
56
56
  "vite-tsconfig-paths": "^5.1.4",
57
- "@torpor/view": "^0.4.16",
58
- "@torpor/unplugin": "^0.2.1"
57
+ "@torpor/unplugin": "^0.2.1",
58
+ "@torpor/view": "^0.4.16"
59
59
  },
60
60
  "devDependencies": {
61
61
  "@trivago/prettier-plugin-sort-imports": "^6.0.0",
@@ -0,0 +1,11 @@
1
+ #! /usr/bin/env node
2
+ import run from "../run/run";
3
+
4
+ const workingDir = process.cwd();
5
+ if (process.argv.includes("--dev")) {
6
+ await run(workingDir, "dev");
7
+ } else if (process.argv.includes("--build")) {
8
+ await run(workingDir, "build");
9
+ } else if (process.argv.includes("--preview")) {
10
+ await run(workingDir, "preview");
11
+ }
package/src/index.ts ADDED
@@ -0,0 +1,30 @@
1
+ import Router from "./site/Router";
2
+ import Site from "./site/Site";
3
+ import type Adapter from "./types/Adapter";
4
+ import type PageEndPoint from "./types/PageEndPoint";
5
+ import type PageLoadEvent from "./types/PageLoadEvent";
6
+ import type PageServerAction from "./types/PageServerAction";
7
+ import type PageServerEndPoint from "./types/PageServerEndPoint";
8
+ import type PageServerLoad from "./types/PageServerLoad";
9
+ import type ServerEndPoint from "./types/ServerEndPoint";
10
+ import type ServerHook from "./types/ServerHook";
11
+ import type ServerLoadEvent from "./types/ServerLoadEvent";
12
+ import type ServerRequest from "./types/ServerRequest";
13
+
14
+ // NOTE: Don't export any general functionality from here -- it may result in
15
+ // Vite errors like `Could not resolve '../pkg' in lightningcss` etc
16
+
17
+ export { Site, Router };
18
+
19
+ export type {
20
+ Adapter,
21
+ PageEndPoint,
22
+ PageServerEndPoint,
23
+ PageServerLoad,
24
+ PageServerAction,
25
+ ServerEndPoint,
26
+ ServerRequest,
27
+ ServerHook,
28
+ PageLoadEvent,
29
+ ServerLoadEvent,
30
+ };
@@ -0,0 +1,56 @@
1
+ import $page from "../state/$page";
2
+ import load from "./load";
3
+ import reload from "./reload";
4
+
5
+ export default async function formSubmit(e: SubmitEvent): Promise<void> {
6
+ // We'll take it from here
7
+ e.preventDefault();
8
+
9
+ // Reset $page on submit and link navigation
10
+ // TODO: Should we put data in $page.data??
11
+ $page.form = undefined;
12
+
13
+ // Perform a POST fetch to the form's action location
14
+ const form = e.target as HTMLFormElement;
15
+ const button = e.submitter as HTMLButtonElement;
16
+ // HACK: submit buttons have their formAction set to the current page if not
17
+ // set, so we can't just check whether it's empty and fallback to form action
18
+ //const action = button?.formAction ?? form.action;
19
+ let action = form.action || document.URL;
20
+ if (button && button.formAction && button.formAction !== document.URL) {
21
+ action = button.formAction;
22
+ }
23
+ const options = {
24
+ body: new FormData(form),
25
+ method: "POST",
26
+ mode: "cors",
27
+ credentials: "same-origin",
28
+ // Add a special header which tells the server that we want the raw
29
+ // error response (if applicable) rather than the reloaded page that you
30
+ // would get if submitting a page without javascript
31
+ headers: {
32
+ "x-torpor-form-submit": "",
33
+ },
34
+ };
35
+ const response = await fetch(
36
+ action,
37
+ // @ts-ignore -- don't know what it's complaining about here
38
+ options,
39
+ );
40
+
41
+ if (response.status === 200 && response.headers.has("x-torpor-form-redirect")) {
42
+ // TODO: Should do the correct type of redirect e.g. seeOther etc
43
+ const redirect = response.headers.get("location")!;
44
+ await load(redirect);
45
+ window.location.href = redirect;
46
+ } else if (
47
+ (response.status >= 200 && response.status <= 299) ||
48
+ (response.status >= 400 && response.status <= 499)
49
+ ) {
50
+ // Put the response into `$page.form`, and it will be put into `$props` in reload
51
+ if (response.headers.get("content-type") === "application/json") {
52
+ $page.form = await response.json();
53
+ }
54
+ await reload();
55
+ }
56
+ }
@@ -0,0 +1,9 @@
1
+ import navigate from "./navigate";
2
+
3
+ /**
4
+ * Loads and renders the page at the supplied path.
5
+ */
6
+ export default async function load(path: string): Promise<boolean> {
7
+ const url = new URL(path, document.location.href);
8
+ return await navigate(url);
9
+ }
@@ -0,0 +1,134 @@
1
+ import client from "../state/client";
2
+ import type LayoutPath from "../types/LayoutPath";
3
+ import type PageEndPoint from "../types/PageEndPoint";
4
+ import type PageServerEndPoint from "../types/PageServerEndPoint";
5
+ import type RouteHandler from "../types/RouteHandler";
6
+
7
+ export default async function loadData(
8
+ handler: RouteHandler,
9
+ params: Record<string, string>,
10
+ path: string,
11
+ query: URLSearchParams,
12
+ newLayoutStack: LayoutPath[],
13
+ clientEndPoint: PageEndPoint | undefined,
14
+ serverEndPoint: PageServerEndPoint | undefined,
15
+ prefetch = false,
16
+ ): Promise<Record<string, string> | void> {
17
+ let data = {};
18
+ if (handler.layouts) {
19
+ let layoutStack = client.layoutStack;
20
+ for (let [i, layout] of handler.layouts.entries()) {
21
+ let layoutPath = layout.path;
22
+ for (let key in params) {
23
+ layoutPath = layoutPath.replace(`[${key}]`, params[key]);
24
+ }
25
+ if (layoutStack.at(i)?.path === layoutPath) {
26
+ // We've already loaded this layout, we can just re-use its data and UI
27
+ if (!prefetch) {
28
+ layoutStack[i].reuse = true;
29
+ }
30
+ newLayoutStack[i] = layoutStack[i];
31
+ Object.assign(data, layoutStack[i].data);
32
+ } else {
33
+ const stackLayout = { path: layoutPath, data: {}, reuse: false, slotRegion: null };
34
+ const layoutEndPoint: PageEndPoint | undefined = (await layout.endPoint())?.default;
35
+ const layoutServerEndPoint: PageServerEndPoint | undefined =
36
+ layout.serverEndPoint && (await layout.serverEndPoint())?.default;
37
+ const layoutResponse = await loadClientAndServerData(
38
+ stackLayout.data,
39
+ document.location.origin + layoutPath,
40
+ query,
41
+ params,
42
+ layoutEndPoint,
43
+ layoutServerEndPoint,
44
+ );
45
+ if (layoutResponse?.ok === false) {
46
+ return;
47
+ }
48
+ Object.assign(data, stackLayout.data);
49
+ newLayoutStack.push(stackLayout);
50
+ }
51
+ }
52
+ }
53
+ let endPointResponse = await loadClientAndServerData(
54
+ data,
55
+ document.location.origin + path,
56
+ query,
57
+ params,
58
+ clientEndPoint,
59
+ serverEndPoint,
60
+ );
61
+ if (endPointResponse?.ok === false) {
62
+ return;
63
+ }
64
+ return data;
65
+ }
66
+
67
+ async function loadClientAndServerData(
68
+ data: Record<string, any>,
69
+ location: string,
70
+ query: URLSearchParams,
71
+ params: Record<string, string>,
72
+ clientEndPoint?: PageEndPoint,
73
+ serverEndPoint?: PageServerEndPoint,
74
+ ): Promise<Response | undefined | void> {
75
+ let prefetchedData = client.prefetchedData;
76
+
77
+ if (clientEndPoint?.load) {
78
+ const clientKey = location + (query.size > 0 ? `?${query}` : "");
79
+ if (prefetchedData[clientKey]) {
80
+ Object.assign(data, prefetchedData[clientKey]);
81
+ } else {
82
+ const clientUrl = new URL(document.location.href);
83
+ for (let [name, value] of query) {
84
+ clientUrl.searchParams.append(name, value);
85
+ }
86
+ const clientParams = buildClientParams(clientUrl, params, data);
87
+ const clientResponse = await clientEndPoint.load(clientParams);
88
+ if (clientResponse) {
89
+ if (clientResponse.ok) {
90
+ if (clientResponse.headers.get("Content-Type")?.includes("application/json")) {
91
+ const clientData = await clientResponse.json();
92
+ Object.assign(data, clientData);
93
+ prefetchedData[clientKey] = clientData;
94
+ }
95
+ } else {
96
+ return clientResponse;
97
+ }
98
+ }
99
+ }
100
+ }
101
+
102
+ if (serverEndPoint?.load) {
103
+ const serverLocation = location.replace(/\/$/, "") + "/~server";
104
+ const serverKey = serverLocation + (query.size > 0 ? `?${query}` : "");
105
+ if (prefetchedData[serverKey]) {
106
+ Object.assign(data, prefetchedData[serverKey]);
107
+ } else {
108
+ const serverUrl = new URL(serverLocation);
109
+ for (let [name, value] of query) {
110
+ serverUrl.searchParams.append(name, value);
111
+ }
112
+ const serverResponse = await fetch(serverUrl);
113
+ if (serverResponse) {
114
+ if (serverResponse.ok) {
115
+ if (serverResponse.headers.get("Content-Type")?.includes("application/json")) {
116
+ const serverData = await serverResponse.json();
117
+ Object.assign(data, serverData);
118
+ prefetchedData[serverKey] = serverData;
119
+ }
120
+ } else {
121
+ return serverResponse;
122
+ }
123
+ }
124
+ }
125
+ }
126
+ }
127
+
128
+ function buildClientParams(url: URL, params: Record<string, string>, data: Record<string, any>) {
129
+ return {
130
+ url,
131
+ params,
132
+ data,
133
+ };
134
+ }
@@ -0,0 +1,164 @@
1
+ import { clearLayoutSlot, fillLayoutSlot, hydrate } from "@torpor/view";
2
+ import { type Component, type SlotRender } from "@torpor/view";
3
+ import { mount } from "@torpor/view";
4
+ import $page from "../state/$page";
5
+ import client from "../state/client";
6
+ import type LayoutPath from "../types/LayoutPath";
7
+ import type PageEndPoint from "../types/PageEndPoint";
8
+ import type PageServerEndPoint from "../types/PageServerEndPoint";
9
+ import formSubmit from "./formSubmit";
10
+ import loadData from "./loadData";
11
+
12
+ // @ts-ignore
13
+ export default async function navigate(url: URL, withHydration = false): Promise<boolean> {
14
+ let parent = document.getElementById("app");
15
+ if (!parent) {
16
+ // TODO: 500
17
+ console.log("500");
18
+ return false;
19
+ }
20
+
21
+ const path = url.pathname;
22
+ const query = url.searchParams;
23
+
24
+ //console.log(`navigating to '${path}'${query.size ? ` with ${query}` : ""}`);
25
+
26
+ const route = client.router.match(path, query);
27
+ if (!route) {
28
+ // TODO: 404
29
+ console.log("404");
30
+ return false;
31
+ }
32
+
33
+ // Update $page before building the components
34
+ $page.url = url;
35
+ if (path.endsWith("/_error")) {
36
+ $page.status = parseInt(query.get("status") ?? "404");
37
+ $page.error = { message: query.get("message") ?? "" };
38
+ // Make it look a bit classier by removing the query
39
+ window.history.replaceState({}, "", url.toString().split("?")[0]);
40
+ } else {
41
+ $page.status = 200;
42
+ }
43
+ const handler = route.handler;
44
+ const params = route.params || {};
45
+
46
+ // There must be a client endpoint with a component
47
+ const clientEndPoint: PageEndPoint | undefined = (await handler.endPoint()).default;
48
+ if (!clientEndPoint?.component) {
49
+ // TODO: 404
50
+ console.log("404");
51
+ return false;
52
+ }
53
+
54
+ // There may be a server endpoint
55
+ const serverEndPoint: PageServerEndPoint | undefined =
56
+ handler.serverEndPoint && (await handler.serverEndPoint())?.default;
57
+
58
+ let newLayoutStack: LayoutPath[] = [];
59
+
60
+ // Pass the data into $props
61
+ // TODO: Don't load if this is the first time -- it should have been passed
62
+ // to us, somehow...
63
+ const data = await loadData(
64
+ handler,
65
+ params,
66
+ path,
67
+ query,
68
+ newLayoutStack,
69
+ clientEndPoint,
70
+ serverEndPoint,
71
+ );
72
+ // We may have form data in a hidden input -- not sure if this is the best
73
+ // way to do it
74
+ let formInput = document.getElementById("t-form-data") as HTMLInputElement;
75
+ let form: Record<string, string> | undefined;
76
+ if (formInput) {
77
+ form = JSON.parse(formInput.value);
78
+ $page.form = form;
79
+ formInput.remove();
80
+ }
81
+ let $props: Record<string, any> = { data };
82
+
83
+ // We may have form data in $page.form, either from the hidden input
84
+ // (above), or added via onformsubmit (below)
85
+ $props.form = $page.form;
86
+
87
+ // Add some special context for submitting Forms client-side
88
+ const $context = {
89
+ TorporBuildContext: { onformsubmit: formSubmit },
90
+ };
91
+
92
+ client.layoutStack.push({ path: route.handler.path, data: {}, reuse: false, slotRegion: null });
93
+ client.layoutStack = newLayoutStack;
94
+ let layoutStack = client.layoutStack;
95
+
96
+ // If there are layouts, work our way upwards, pushing each component into
97
+ // the default slot of its parent
98
+ // TODO: There's probably a nicer way to do this with reducers or something
99
+ let component = clientEndPoint.component as Component;
100
+ let slots: Record<string, SlotRender> | undefined = undefined;
101
+ if (handler.layouts) {
102
+ let slotFunctions: SlotRender[] = [];
103
+ // The last slot function will render the client component
104
+ slotFunctions[handler.layouts.length] = function clientComponent(parent, anchor) {
105
+ let i = layoutStack.length - 1;
106
+ layoutStack[i].slotRegion = fillLayoutSlot(
107
+ clientEndPoint.component!,
108
+ slotFunctions[i + 1],
109
+ parent,
110
+ anchor,
111
+ $props,
112
+ $context,
113
+ );
114
+ };
115
+
116
+ // Each earlier slot function will render a layout
117
+ for (let i = handler.layouts.length - 1; i >= 0; i--) {
118
+ const layoutEndPoint: PageEndPoint | undefined = (await handler.layouts[i].endPoint())
119
+ ?.default;
120
+ if (layoutEndPoint?.component) {
121
+ if (layoutStack[i].reuse) {
122
+ // Set the parent to add the new content to (from the old
123
+ // content), clear the range under this point, and set the
124
+ // component to the slot function within this layout
125
+ parent = layoutStack[i].slotRegion.startNode.parentNode as HTMLElement;
126
+ clearLayoutSlot(layoutStack[i].slotRegion);
127
+ component = slotFunctions[i + 1];
128
+ break;
129
+ } else if (i === 0) {
130
+ component = layoutEndPoint.component as Component;
131
+ slots = { _: slotFunctions[i + 1] };
132
+ } else {
133
+ slotFunctions[i] = function layoutComponent(parent, anchor, _, $context) {
134
+ layoutStack[i - 1].slotRegion = fillLayoutSlot(
135
+ layoutEndPoint.component!,
136
+ slotFunctions[i + 1],
137
+ parent,
138
+ anchor,
139
+ $props,
140
+ $context,
141
+ );
142
+ };
143
+ }
144
+ }
145
+ }
146
+ }
147
+
148
+ if (withHydration) {
149
+ hydrate(parent, component, $props, slots);
150
+ } else {
151
+ try {
152
+ mount(parent, component, $props, slots);
153
+ } catch (error) {
154
+ // TODO: Show a proper Error component
155
+ parent.innerHTML = '<span style="color: red">Script syntax error</span><p>' + error + "</p>";
156
+ console.log(error);
157
+ }
158
+ }
159
+
160
+ // Reset prefetched data on each navigation
161
+ client.prefetchedData = {};
162
+
163
+ return true;
164
+ }
@@ -0,0 +1,9 @@
1
+ import navigate from "./navigate";
2
+
3
+ /**
4
+ * Reloads data for the currently loaded page and rerenders it.
5
+ */
6
+ export default async function reload(): Promise<boolean> {
7
+ const url = new URL(document.location.href);
8
+ return await navigate(url);
9
+ }
package/src/nav.ts ADDED
@@ -0,0 +1,4 @@
1
+ import load from "./nav/load";
2
+ import reload from "./nav/reload";
3
+
4
+ export { load, reload };
@@ -0,0 +1,19 @@
1
+ import response from "./response";
2
+
3
+ /**
4
+ * 400 Bad Request
5
+ *
6
+ * The HTTP 400 Bad Request client error response status code indicates that the
7
+ * server would not process the request due to something the server considered
8
+ * to be a client error. The reason for a 400 response is typically due to
9
+ * malformed request syntax, invalid request message framing, or deceptive
10
+ * request routing.
11
+ *
12
+ * Clients that receive a 400 response should expect that repeating the request
13
+ * without modification will fail with the same error.
14
+ *
15
+ * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/400
16
+ */
17
+ export default function badRequest(body?: object | string): Response {
18
+ return response(400, body ?? "Bad request");
19
+ }
@@ -0,0 +1,20 @@
1
+ import response from "./response";
2
+
3
+ /**
4
+ * 201 Created
5
+ *
6
+ * The HTTP 201 Created successful response status code indicates that the HTTP
7
+ * request has led to the creation of a resource. This status code is commonly
8
+ * sent as the result of a POST request.
9
+ *
10
+ * The new resource, or a description and link to the new resource, is created
11
+ * before the response is returned. The newly-created items can be returned in
12
+ * the body of the response message, but must be locatable by the URL of the
13
+ * initiating request or by the URL in the value of the Location header provided
14
+ * with the response.
15
+ *
16
+ * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/201
17
+ */
18
+ export default function created(body?: object | string): Response {
19
+ return response(201, body);
20
+ }
@@ -0,0 +1,21 @@
1
+ import response from "./response";
2
+
3
+ /**
4
+ * 403 Forbidden
5
+ *
6
+ * The HTTP 403 Forbidden client error response status code indicates that the
7
+ * server understood the request but refused to process it. This status is
8
+ * similar to 401, except that for 403 Forbidden responses, authenticating or
9
+ * re-authenticating makes no difference. The request failure is tied to
10
+ * application logic, such as insufficient permissions to a resource or action.
11
+ *
12
+ * Clients that receive a 403 response should expect that repeating the request
13
+ * without modification will fail with the same error. Server owners may decide
14
+ * to send a 404 response instead of a 403 if acknowledging the existence of a
15
+ * resource to clients with insufficient privileges is not desired.
16
+ *
17
+ * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/403
18
+ */
19
+ export default function forbidden(body?: object | string): Response {
20
+ return response(403, body ?? "Forbidden");
21
+ }
@@ -0,0 +1,30 @@
1
+ import transfer from "./transfer";
2
+
3
+ /**
4
+ * 302 Found
5
+ *
6
+ * The HTTP 302 Found redirection response status code indicates that the
7
+ * requested resource has been temporarily moved to the URL in the Location
8
+ * header.
9
+ *
10
+ * A browser receiving this status will automatically request the resource at
11
+ * the URL in the Location header, redirecting the user to the new page. Search
12
+ * engines receiving this response will not attribute links to the original URL
13
+ * to the new resource, meaning no SEO value is transferred to the new URL.
14
+ *
15
+ * > Note: In the Fetch Standard, when a user agent receives a 302 in response
16
+ * > to a POST request, it uses the GET method in the subsequent redirection
17
+ * > request, as permitted by the HTTP specification. To avoid user agents
18
+ * > modifying the request, use 307 Temporary Redirect instead, as altering the
19
+ * > method after a 307 response is prohibited.
20
+ *
21
+ * > In cases where you want any request method to be changed to GET, use 303
22
+ * > See Other. This is useful when you want to give a response to a PUT method
23
+ * > that is not the uploaded resource but a confirmation message such as: "you
24
+ * > successfully uploaded XYZ".
25
+ *
26
+ * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/302
27
+ */
28
+ export default function permRedirect(location: string): Response {
29
+ return transfer(308, location);
30
+ }
@@ -0,0 +1,18 @@
1
+ import response from "./response";
2
+
3
+ /**
4
+ * 405 Method Not Allowed
5
+ *
6
+ * The HTTP 405 Method Not Allowed client error response status code indicates
7
+ * that the server knows the request method, but the target resource doesn't
8
+ * support this method. The server must generate an Allow header in a 405
9
+ * response with a list of methods that the target resource currently supports.
10
+ *
11
+ * Improper server-side permissions set on files or directories may cause a 405
12
+ * response when the request would otherwise be expected to succeed.
13
+ *
14
+ * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/405
15
+ */
16
+ export default function methodNotAllowed(body?: object | string): Response {
17
+ return response(405, body ?? "Method not allowed");
18
+ }
@@ -0,0 +1,25 @@
1
+ import transfer from "./transfer";
2
+
3
+ /**
4
+ * 301 Moved Permanently
5
+ *
6
+ * The HTTP 301 Moved Permanently redirection response status code indicates
7
+ * that the requested resource has been permanently moved to the URL in the
8
+ * Location header.
9
+ *
10
+ * A browser receiving this status will automatically request the resource at
11
+ * the URL in the Location header, redirecting the user to the new page. Search
12
+ * engines receiving this response will attribute links to the original URL to
13
+ * the redirected resource, passing the SEO ranking to the new URL.
14
+ *
15
+ * > Note: In the Fetch Standard, when a user agent receives a 301 in response
16
+ * > to a POST request, it uses the GET method in the subsequent redirection
17
+ * > request, as permitted by the HTTP specification. To avoid user agents
18
+ * > modifying the request, use 308 Permanent Redirect instead, as altering the
19
+ * > method after a 308 response is prohibited.
20
+ *
21
+ * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/301
22
+ */
23
+ export default function permRedirect(location: string): Response {
24
+ return transfer(308, location);
25
+ }
@@ -0,0 +1,24 @@
1
+ import response from "./response";
2
+
3
+ /**
4
+ * 404 Not Found
5
+ *
6
+ * The HTTP 404 Not Found client error response status code indicates that the
7
+ * server cannot find the requested resource. Links that lead to a 404 page are
8
+ * often called broken or dead links and can be subject to link rot.
9
+ *
10
+ * A 404 status code only indicates that the resource is missing without
11
+ * indicating if this is temporary or permanent. If a resource is permanently
12
+ * removed, servers should send the 410 Gone status instead.
13
+ *
14
+ * 404 errors on a website can lead to a poor user experience for your visitors,
15
+ * so the number of broken links (internal and external) should be minimized to
16
+ * prevent frustration for readers. Common causes of 404 responses are mistyped
17
+ * URLs or pages that are moved or deleted without redirection. For more
18
+ * information, see the Redirections in HTTP guide.
19
+ *
20
+ * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/404
21
+ */
22
+ export default function notFound(body?: object | string): Response {
23
+ return response(404, body ?? "Not found");
24
+ }
@@ -0,0 +1,34 @@
1
+ import transfer from "./transfer";
2
+
3
+ /**
4
+ * 304 Not Modified
5
+ *
6
+ * The HTTP 304 Not Modified redirection response status code indicates that
7
+ * there is no need to retransmit the requested resources.
8
+ *
9
+ * This response code is sent when the request is a conditional GET or HEAD
10
+ * request with an If-None-Match or an If-Modified-Since header and the
11
+ * condition evaluates to 'false'. It confirms that the resource cached by the
12
+ * client is still valid and that the server would have sent a 200 OK response
13
+ * with the resource if the condition evaluated to 'true'. See HTTP caching for
14
+ * more information.
15
+ *
16
+ * The response must not contain a body and must include the headers that would
17
+ * have been sent in an equivalent 200 response, such as:
18
+ *
19
+ * - Cache-Control
20
+ * - Content-Location
21
+ * - Date
22
+ * - ETag
23
+ * - Expires
24
+ * - Vary
25
+ *
26
+ * > Note: Many developer tools' network panels of browsers create extraneous
27
+ * > requests leading to 304 responses, so that access to the local cache is
28
+ * > visible to developers.
29
+ *
30
+ * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/304
31
+ */
32
+ export default function notModified(location: string): Response {
33
+ return transfer(304, location);
34
+ }