gruber 0.2.0 → 0.4.0

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 (73) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +156 -16
  3. package/core/configuration.d.ts +157 -0
  4. package/core/configuration.js +222 -96
  5. package/core/configuration.test.d.ts +1 -0
  6. package/core/configuration.test.js +242 -53
  7. package/{types/core → core}/fetch-router.d.ts +0 -1
  8. package/core/fetch-router.test.d.ts +1 -0
  9. package/{types/core → core}/http.d.ts +30 -12
  10. package/core/http.js +42 -17
  11. package/core/http.test.d.ts +1 -0
  12. package/core/http.test.js +57 -35
  13. package/{types/core → core}/migrator.d.ts +0 -1
  14. package/core/migrator.test.d.ts +1 -0
  15. package/{types/core → core}/mod.d.ts +1 -1
  16. package/core/mod.js +1 -0
  17. package/{types/core → core}/postgres.d.ts +0 -1
  18. package/core/structures.d.ts +91 -0
  19. package/core/structures.js +260 -0
  20. package/core/structures.test.d.ts +1 -0
  21. package/core/structures.test.js +474 -0
  22. package/core/test-deps.d.ts +1 -0
  23. package/core/test-deps.js +1 -1
  24. package/{types/core → core}/types.d.ts +0 -1
  25. package/{types/core → core}/utilities.d.ts +0 -1
  26. package/core/utilities.test.d.ts +1 -0
  27. package/package.json +4 -5
  28. package/{types/source → source}/configuration.d.ts +4 -9
  29. package/source/configuration.js +0 -2
  30. package/source/core.d.ts +1 -0
  31. package/{types/source → source}/express-router.d.ts +2 -3
  32. package/source/express-router.js +1 -1
  33. package/{types/source → source}/koa-router.d.ts +0 -1
  34. package/{types/source → source}/mod.d.ts +0 -3
  35. package/source/mod.js +2 -2
  36. package/{types/source → source}/node-router.d.ts +8 -8
  37. package/source/node-router.js +1 -1
  38. package/source/package-lock.json +3 -9
  39. package/source/package.json +0 -2
  40. package/source/polyfill.d.ts +1 -0
  41. package/{types/source → source}/postgres.d.ts +0 -1
  42. package/tsconfig.json +0 -2
  43. package/types/core/configuration.d.ts +0 -57
  44. package/types/core/configuration.d.ts.map +0 -1
  45. package/types/core/configuration.test.d.ts +0 -2
  46. package/types/core/configuration.test.d.ts.map +0 -1
  47. package/types/core/fetch-router.d.ts.map +0 -1
  48. package/types/core/fetch-router.test.d.ts +0 -2
  49. package/types/core/fetch-router.test.d.ts.map +0 -1
  50. package/types/core/http.d.ts.map +0 -1
  51. package/types/core/http.test.d.ts +0 -2
  52. package/types/core/http.test.d.ts.map +0 -1
  53. package/types/core/migrator.d.ts.map +0 -1
  54. package/types/core/migrator.test.d.ts +0 -2
  55. package/types/core/migrator.test.d.ts.map +0 -1
  56. package/types/core/mod.d.ts.map +0 -1
  57. package/types/core/postgres.d.ts.map +0 -1
  58. package/types/core/test-deps.d.ts +0 -2
  59. package/types/core/test-deps.d.ts.map +0 -1
  60. package/types/core/types.d.ts.map +0 -1
  61. package/types/core/utilities.d.ts.map +0 -1
  62. package/types/core/utilities.test.d.ts +0 -2
  63. package/types/core/utilities.test.d.ts.map +0 -1
  64. package/types/source/configuration.d.ts.map +0 -1
  65. package/types/source/core.d.ts +0 -2
  66. package/types/source/core.d.ts.map +0 -1
  67. package/types/source/express-router.d.ts.map +0 -1
  68. package/types/source/koa-router.d.ts.map +0 -1
  69. package/types/source/mod.d.ts.map +0 -1
  70. package/types/source/node-router.d.ts.map +0 -1
  71. package/types/source/polyfill.d.ts +0 -2
  72. package/types/source/polyfill.d.ts.map +0 -1
  73. package/types/source/postgres.d.ts.map +0 -1
@@ -1,10 +1,8 @@
1
1
  import { Configuration } from "./configuration.js";
2
- import { assertEquals, assertThrows } from "./test-deps.js";
3
- import { superstruct } from "./test-deps.js";
2
+ import { assertEquals, assertThrows, describe, it } from "./test-deps.js";
4
3
 
5
4
  /** @type {import("./configuration.js").ConfigurationOptions} */
6
5
  const bareOptions = {
7
- superstruct,
8
6
  readTextFile() {},
9
7
  getEnvironmentVariable(_key) {},
10
8
  getCommandArgument(_key) {},
@@ -12,11 +10,10 @@ const bareOptions = {
12
10
  parse(_value) {},
13
11
  };
14
12
 
15
- Deno.test("Configuration", async ({ step }) => {
16
- await step("constructor", async ({ step }) => {
17
- await step("validates options", () => {
13
+ describe("Configuration", () => {
14
+ describe("constructor", () => {
15
+ it("validates options", () => {
18
16
  new Configuration({
19
- superstruct,
20
17
  readTextFile() {},
21
18
  getEnvironmentVariable(_key) {},
22
19
  getCommandArgument(_key) {},
@@ -26,76 +23,205 @@ Deno.test("Configuration", async ({ step }) => {
26
23
  });
27
24
  });
28
25
 
29
- await step("object", async ({ step }) => {
26
+ describe("object", () => {
30
27
  const config = new Configuration(bareOptions);
31
28
 
32
- await step("stores spec", () => {
33
- const result = config.object({});
29
+ it("stores the options", () => {
30
+ const result = config.object({ key: "value" });
31
+
34
32
  assertEquals(result[Configuration.spec].type, "object");
35
- assertEquals(result[Configuration.spec].value, {});
33
+ assertEquals(result[Configuration.spec].options, { key: "value" });
34
+ });
35
+ it("describes itself", () => {
36
+ const spec = config.object({
37
+ fullName: config.string({ variable: "FULL_NAME", fallback: "Geoff T" }),
38
+ age: config.number({ flag: "--age", fallback: 42 }),
39
+ });
40
+ //
41
+ const result = spec[Configuration.spec].describe(
42
+ "person",
43
+ (options, name) => ({ name, ...options }),
44
+ );
45
+
46
+ assertEquals(result, {
47
+ fallback: { fullName: "Geoff T", age: 42 },
48
+ fields: [
49
+ {
50
+ name: "person.fullName",
51
+ type: "string",
52
+ variable: "FULL_NAME",
53
+ fallback: "Geoff T",
54
+ },
55
+ {
56
+ name: "person.age",
57
+ type: "number",
58
+ flag: "--age",
59
+ fallback: 42,
60
+ },
61
+ ],
62
+ });
36
63
  });
37
64
  });
38
65
 
39
- await step("string", async ({ step }) => {
66
+ describe("string", () => {
40
67
  const config = new Configuration(bareOptions);
41
68
 
42
- await step("requires a fallback", () => {
69
+ it("requires a fallback", () => {
43
70
  assertThrows(() => config.string({}), TypeError);
44
71
  });
45
-
46
- await step("uses the fallback", () => {
72
+ it("uses the fallback", () => {
47
73
  const struct = config.string({ fallback: "Geoff Testington" });
48
- const result = superstruct.create(undefined, struct);
74
+ const result = struct.process(undefined);
49
75
  assertEquals(result, "Geoff Testington");
50
76
  });
51
-
52
- await step("stores spec", () => {
77
+ it("stores the options", () => {
53
78
  const result = config.string({
54
79
  variable: "SOME_VAR",
80
+ flag: "--some-flag",
55
81
  fallback: "value",
56
82
  });
57
83
  assertEquals(result[Configuration.spec].type, "string");
58
- assertEquals(result[Configuration.spec].value, {
84
+ assertEquals(result[Configuration.spec].options, {
85
+ variable: "SOME_VAR",
86
+ flag: "--some-flag",
87
+ fallback: "value",
88
+ });
89
+ });
90
+ it("describes itself", () => {
91
+ const spec = config.string({
59
92
  variable: "SOME_VAR",
93
+ flag: "--some-flag",
94
+ fallback: "value",
95
+ });
96
+ const result = spec[Configuration.spec].describe("fullName");
97
+ assertEquals(result, {
60
98
  fallback: "value",
99
+ fields: [
100
+ {
101
+ name: "fullName",
102
+ type: "string",
103
+ variable: "SOME_VAR",
104
+ flag: "--some-flag",
105
+ fallback: "value",
106
+ },
107
+ ],
61
108
  });
62
109
  });
63
110
  });
64
111
 
65
- await step("url", async ({ step }) => {
112
+ describe("number", () => {
66
113
  const config = new Configuration(bareOptions);
67
114
 
68
- await step("requires a fallback", () => {
69
- assertThrows(() => config.url({}), TypeError);
115
+ it("requires a fallback", () => {
116
+ assertThrows(() => config.number({}), TypeError);
117
+ });
118
+ it("uses the fallback", () => {
119
+ const struct = config.number({ fallback: 42 });
120
+ const result = struct.process(undefined);
121
+ assertEquals(result, 42);
122
+ });
123
+ it("stores the options", () => {
124
+ const result = config.number({
125
+ variable: "SOME_VAR",
126
+ flag: "--some-flag",
127
+ fallback: 42,
128
+ });
129
+ assertEquals(result[Configuration.spec].type, "number");
130
+ assertEquals(result[Configuration.spec].options, {
131
+ variable: "SOME_VAR",
132
+ flag: "--some-flag",
133
+ fallback: 42,
134
+ });
70
135
  });
136
+ it("parses strings", () => {
137
+ const vars = { SOME_VAR: "12.34" };
138
+ const config = new Configuration({
139
+ ...bareOptions,
140
+ getEnvironmentVariable: (key) => vars[key],
141
+ });
71
142
 
72
- await step("converts to URL", () => {
73
- const struct = config.url({ fallback: "" });
74
- const result = superstruct.create("https://example.com", struct);
75
- assertEquals(result, new URL("https://example.com"));
143
+ const result = config.number({
144
+ variable: "SOME_VAR",
145
+ fallback: 42,
146
+ });
147
+
148
+ assertEquals(result.process(undefined), 12.34);
76
149
  });
150
+ });
77
151
 
78
- await step("uses the fallback", () => {
79
- const struct = config.url({ fallback: "https://example.com" });
80
- const result = superstruct.create(undefined, struct);
81
- assertEquals(result, new URL("https://example.com"));
152
+ describe("boolean", () => {
153
+ const config = new Configuration(bareOptions);
154
+
155
+ it("requires a fallback", () => {
156
+ assertThrows(() => config.boolean({}), TypeError);
157
+ });
158
+ it("uses the fallback", () => {
159
+ const struct = config.boolean({ fallback: false });
160
+ const result = struct.process(undefined);
161
+ assertEquals(result, false);
162
+ });
163
+ it("stores the options", () => {
164
+ const result = config.boolean({
165
+ variable: "SOME_VAR",
166
+ flag: "--some-flag",
167
+ fallback: false,
168
+ });
169
+ assertEquals(result[Configuration.spec].type, "boolean");
170
+ assertEquals(result[Configuration.spec].options, {
171
+ variable: "SOME_VAR",
172
+ flag: "--some-flag",
173
+ fallback: false,
174
+ });
175
+ });
176
+ it("parses strings", () => {
177
+ const vars = { SOME_VAR: "true" };
178
+ const config = new Configuration({
179
+ ...bareOptions,
180
+ getEnvironmentVariable: (key) => vars[key],
181
+ });
182
+
183
+ const result = config.boolean({
184
+ variable: "SOME_VAR",
185
+ fallback: false,
186
+ });
187
+
188
+ assertEquals(result.process(undefined), true);
82
189
  });
190
+ });
83
191
 
84
- await step("stores spec", () => {
192
+ describe("url", () => {
193
+ const config = new Configuration(bareOptions);
194
+
195
+ it("requires a fallback", () => {
196
+ assertThrows(() => config.url({}), TypeError);
197
+ });
198
+ it("converts to URL", () => {
199
+ const struct = config.url({ fallback: "https://fallback.example.com" });
200
+ const result = struct.process("https://example.com");
201
+ assertEquals(result, new URL("https://example.com"));
202
+ });
203
+ it("uses the fallback", () => {
204
+ const struct = config.url({ fallback: "https://fallback.example.com" });
205
+ const result = struct.process(undefined);
206
+ assertEquals(result, new URL("https://fallback.example.com"));
207
+ });
208
+ it("stores the options", () => {
85
209
  const result = config.url({
86
210
  variable: "SOME_VAR",
211
+ flag: "--some-flag",
87
212
  fallback: "https://example.com",
88
213
  });
89
214
  assertEquals(result[Configuration.spec].type, "url");
90
- assertEquals(result[Configuration.spec].value, {
215
+ assertEquals(result[Configuration.spec].options, {
91
216
  variable: "SOME_VAR",
92
- fallback: "https://example.com",
217
+ flag: "--some-flag",
218
+ fallback: new URL("https://example.com"),
93
219
  });
94
220
  });
95
221
  });
96
222
 
97
- await step("_getValue", async ({ step }) => {
98
- await step("uses arguments", () => {
223
+ describe("_getValue", () => {
224
+ it("uses arguments", () => {
99
225
  const args = { "--option": "value-from-arg" };
100
226
  const config = new Configuration({
101
227
  ...bareOptions,
@@ -104,10 +230,12 @@ Deno.test("Configuration", async ({ step }) => {
104
230
  const result = config._getValue({
105
231
  flag: "--option",
106
232
  });
107
- assertEquals(result, "value-from-arg");
233
+ assertEquals(result, {
234
+ source: "argument",
235
+ value: "value-from-arg",
236
+ });
108
237
  });
109
-
110
- await step("uses environment variables", () => {
238
+ it("uses environment variables", () => {
111
239
  const env = { MY_VAR: "value-from-env" };
112
240
  const config = new Configuration({
113
241
  ...bareOptions,
@@ -116,17 +244,78 @@ Deno.test("Configuration", async ({ step }) => {
116
244
  const result = config._getValue({
117
245
  variable: "MY_VAR",
118
246
  });
119
- assertEquals(result, "value-from-env");
247
+ assertEquals(result, {
248
+ source: "variable",
249
+ value: "value-from-env",
250
+ });
120
251
  });
121
-
122
- await step("uses the fallback", () => {
252
+ it("uses the fallback", () => {
123
253
  const config = new Configuration(bareOptions);
124
254
  const result = config._getValue({ fallback: "value-from-fallback" });
125
- assertEquals(result, "value-from-fallback");
255
+ assertEquals(result, {
256
+ source: "fallback",
257
+ value: "value-from-fallback",
258
+ });
259
+ });
260
+ });
261
+
262
+ describe("_parseFloat", () => {
263
+ it("parses strings", () => {
264
+ const config = new Configuration(bareOptions);
265
+ assertEquals(
266
+ config._parseFloat({ source: "argument", value: "12.34" }),
267
+ 12.34,
268
+ "should parse the float from the result",
269
+ );
270
+ });
271
+ it("passes numbers through", () => {
272
+ const config = new Configuration(bareOptions);
273
+ assertEquals(
274
+ config._parseFloat({ source: "fallback", value: 98.76 }),
275
+ 98.76,
276
+ "should preserve number literals",
277
+ );
278
+ });
279
+ });
280
+
281
+ describe("_parseBoolean", () => {
282
+ it("parses strings", () => {
283
+ const config = new Configuration(bareOptions);
284
+ assertEquals(
285
+ config._parseBoolean({ source: "argument", value: "1" }),
286
+ true,
287
+ );
288
+ assertEquals(
289
+ config._parseBoolean({ source: "argument", value: "true" }),
290
+ true,
291
+ );
292
+ assertEquals(
293
+ config._parseBoolean({ source: "argument", value: "0" }),
294
+ false,
295
+ );
296
+ assertEquals(
297
+ config._parseBoolean({ source: "argument", value: "false" }),
298
+ false,
299
+ );
300
+ });
301
+ it("passes booleans through", () => {
302
+ const config = new Configuration(bareOptions);
303
+ assertEquals(
304
+ config._parseBoolean({ source: "fallback", value: true }),
305
+ true,
306
+ "should preserve boolean literals",
307
+ );
308
+ });
309
+ it("allows empty-string for arguments", () => {
310
+ const config = new Configuration(bareOptions);
311
+ assertEquals(
312
+ config._parseBoolean({ source: "argument", value: "" }),
313
+ true,
314
+ );
126
315
  });
127
316
  });
128
317
 
129
- await step("load", async ({ step }) => {
318
+ describe("load", () => {
130
319
  const files = {
131
320
  "config.json":
132
321
  '{"env":"production","meta":{"version":"1.2.3"},"selfUrl":"https://example.com"}',
@@ -146,7 +335,7 @@ Deno.test("Configuration", async ({ step }) => {
146
335
  selfUrl: config.url({ fallback: "http://localhost" }),
147
336
  });
148
337
 
149
- await step("loads config", async () => {
338
+ it("loads config", async () => {
150
339
  const result = await config.load("config.json", spec);
151
340
  assertEquals(result, {
152
341
  env: "production",
@@ -155,7 +344,7 @@ Deno.test("Configuration", async ({ step }) => {
155
344
  });
156
345
  });
157
346
 
158
- await step("uses the fallback", async () => {
347
+ it("uses the fallback", async () => {
159
348
  const result = await config.load("missing-config.json", spec);
160
349
  assertEquals(result, {
161
350
  env: "development",
@@ -165,10 +354,10 @@ Deno.test("Configuration", async ({ step }) => {
165
354
  });
166
355
  });
167
356
 
168
- await step("describeSpecification", async ({ step }) => {
169
- await step("processes strings", () => {
357
+ describe("describe", () => {
358
+ it("processes strings", () => {
170
359
  const config = new Configuration(bareOptions);
171
- const result = config.describeSpecification(
360
+ const result = config.describe(
172
361
  config.string({
173
362
  fallback: "test-app",
174
363
  variable: "APP_NAME",
@@ -188,9 +377,9 @@ Deno.test("Configuration", async ({ step }) => {
188
377
  ]);
189
378
  });
190
379
 
191
- await step("processes urls", () => {
380
+ it("processes urls", () => {
192
381
  const config = new Configuration(bareOptions);
193
- const result = config.describeSpecification(
382
+ const result = config.describe(
194
383
  config.url({
195
384
  fallback: "https://example.com",
196
385
  variable: "SELF_URL",
@@ -203,16 +392,16 @@ Deno.test("Configuration", async ({ step }) => {
203
392
  {
204
393
  name: "selfUrl",
205
394
  type: "url",
206
- fallback: "https://example.com",
395
+ fallback: new URL("https://example.com"),
207
396
  variable: "SELF_URL",
208
397
  flag: "--self-url",
209
398
  },
210
399
  ]);
211
400
  });
212
401
 
213
- await step("processes objects", () => {
402
+ it("processes objects", () => {
214
403
  const config = new Configuration(bareOptions);
215
- const result = config.describeSpecification(
404
+ const result = config.describe(
216
405
  config.object({
217
406
  name: config.string({
218
407
  fallback: "testing-app",
@@ -49,4 +49,3 @@ export type FetchRouterOptions = {
49
49
  routes?: import("./types.ts").RouteDefinition<any>[];
50
50
  errorHandler?: RouteErrorHandler;
51
51
  };
52
- //# sourceMappingURL=fetch-router.d.ts.map
@@ -0,0 +1 @@
1
+ export {};
@@ -26,23 +26,42 @@
26
26
  */
27
27
  export function defineRoute<T extends string>(init: import("./types.ts").RouteOptions<T>): import("./types.ts").RouteDefinition<T>;
28
28
  export class HTTPError extends Error {
29
- /** https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400 */
30
- static badRequest(): HTTPError;
31
- /** https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401 */
32
- static unauthorized(): HTTPError;
33
- /** https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404 */
34
- static notFound(): HTTPError;
35
- /** https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500 */
36
- static internalServerError(): HTTPError;
37
- /** https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/501 */
38
- static notImplemented(): HTTPError;
29
+ /**
30
+ * https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400
31
+ * @param {BodyInit} [body]
32
+ */
33
+ static badRequest(body?: BodyInit): HTTPError;
34
+ /**
35
+ * https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401
36
+ * @param {BodyInit} [body]
37
+ */
38
+ static unauthorized(body?: BodyInit): HTTPError;
39
+ /**
40
+ * https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404
41
+ * @param {BodyInit} [body]
42
+ */
43
+ static notFound(body?: BodyInit): HTTPError;
44
+ /**
45
+ * https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500
46
+ * @param {BodyInit} [body]
47
+ */
48
+ static internalServerError(body?: BodyInit): HTTPError;
49
+ /**
50
+ * https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/501
51
+ * @param {BodyInit} [body]
52
+ */
53
+ static notImplemented(body?: BodyInit): HTTPError;
39
54
  /**
40
55
  * @param {number} status
41
56
  * @param {string} statusText
57
+ * @param {BodyInit|null|undefined} [body=null]
58
+ * @param {HeadersInit} [headers=undefined]
42
59
  */
43
- constructor(status?: number, statusText?: string);
60
+ constructor(status?: number, statusText?: string, body?: BodyInit | null | undefined, headers?: HeadersInit);
44
61
  /** @type {number} */ status: number;
45
62
  /** @type {string}*/ statusText: string;
63
+ body: BodyInit;
64
+ headers: Headers;
46
65
  toResponse(): Response;
47
66
  }
48
67
  export type HTTPMethod = import("./types.ts").HTTPMethod;
@@ -52,4 +71,3 @@ export type RouteContext<T> = import("./types.ts").RouteContext<T>;
52
71
  export type RouteHandler<T> = import("./types.ts").RouteHandler<T>;
53
72
  export type RouteOptions<T extends string> = import("./types.ts").RouteOptions<T>;
54
73
  export type RouteDefinition<T = any> = import("./types.ts").RouteDefinition<T>;
55
- //# sourceMappingURL=http.d.ts.map
package/core/http.js CHANGED
@@ -41,29 +41,44 @@ export function defineRoute(init) {
41
41
  // NOTE: add more error codes as needed
42
42
  // NOTE: design an API for setting the body on static errors
43
43
  export class HTTPError extends Error {
44
- /** https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400 */
45
- static badRequest() {
46
- return new HTTPError(400, "Bad Request");
44
+ /**
45
+ * https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400
46
+ * @param {BodyInit} [body]
47
+ */
48
+ static badRequest(body = undefined) {
49
+ return new HTTPError(400, "Bad Request", body);
47
50
  }
48
51
 
49
- /** https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401 */
50
- static unauthorized() {
51
- return new HTTPError(401, "Unauthorized");
52
+ /**
53
+ * https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401
54
+ * @param {BodyInit} [body]
55
+ */
56
+ static unauthorized(body = undefined) {
57
+ return new HTTPError(401, "Unauthorized", body);
52
58
  }
53
59
 
54
- /** https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404 */
55
- static notFound() {
56
- return new HTTPError(404, "Not Found");
60
+ /**
61
+ * https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404
62
+ * @param {BodyInit} [body]
63
+ */
64
+ static notFound(body = undefined) {
65
+ return new HTTPError(404, "Not Found", body);
57
66
  }
58
67
 
59
- /** https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500 */
60
- static internalServerError() {
61
- return new HTTPError(500, "Internal Server Error");
68
+ /**
69
+ * https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500
70
+ * @param {BodyInit} [body]
71
+ */
72
+ static internalServerError(body = undefined) {
73
+ return new HTTPError(500, "Internal Server Error", body);
62
74
  }
63
75
 
64
- /** https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/501 */
65
- static notImplemented() {
66
- return new HTTPError(501, "Not Implemented");
76
+ /**
77
+ * https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/501
78
+ * @param {BodyInit} [body]
79
+ */
80
+ static notImplemented(body = undefined) {
81
+ return new HTTPError(501, "Not Implemented", body);
67
82
  }
68
83
 
69
84
  /** @type {number} */ status;
@@ -72,19 +87,29 @@ export class HTTPError extends Error {
72
87
  /**
73
88
  * @param {number} status
74
89
  * @param {string} statusText
90
+ * @param {BodyInit|null|undefined} [body=null]
91
+ * @param {HeadersInit} [headers=undefined]
75
92
  */
76
- constructor(status = 200, statusText = "OK") {
93
+ constructor(
94
+ status = 200,
95
+ statusText = "OK",
96
+ body = null,
97
+ headers = undefined,
98
+ ) {
77
99
  super(statusText);
78
100
  this.status = status;
79
101
  this.statusText = statusText;
102
+ this.body = body;
80
103
  this.name = "HTTPError";
104
+ this.headers = new Headers(headers);
81
105
  Error.captureStackTrace(this, HTTPError);
82
106
  }
83
107
 
84
108
  toResponse() {
85
- return new Response(null, {
109
+ return new Response(this.body, {
86
110
  status: this.status,
87
111
  statusText: this.statusText,
112
+ headers: this.headers,
88
113
  });
89
114
  }
90
115
  }
@@ -0,0 +1 @@
1
+ export {};