gruber 0.3.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.
- package/CHANGELOG.md +17 -0
- package/README.md +67 -13
- package/core/configuration.d.ts +115 -17
- package/core/configuration.js +197 -69
- package/core/configuration.test.js +215 -16
- package/core/http.d.ts +30 -11
- package/core/http.js +42 -17
- package/core/http.test.js +57 -35
- package/core/mod.d.ts +1 -0
- package/core/mod.js +1 -0
- package/core/structures.d.ts +22 -9
- package/core/structures.js +87 -29
- package/core/structures.test.js +157 -32
- package/package.json +1 -1
|
@@ -26,10 +26,40 @@ describe("Configuration", () => {
|
|
|
26
26
|
describe("object", () => {
|
|
27
27
|
const config = new Configuration(bareOptions);
|
|
28
28
|
|
|
29
|
-
it("stores the
|
|
30
|
-
const result = config.object({});
|
|
29
|
+
it("stores the options", () => {
|
|
30
|
+
const result = config.object({ key: "value" });
|
|
31
|
+
|
|
31
32
|
assertEquals(result[Configuration.spec].type, "object");
|
|
32
|
-
assertEquals(result[Configuration.spec].
|
|
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
|
+
});
|
|
33
63
|
});
|
|
34
64
|
});
|
|
35
65
|
|
|
@@ -44,16 +74,118 @@ describe("Configuration", () => {
|
|
|
44
74
|
const result = struct.process(undefined);
|
|
45
75
|
assertEquals(result, "Geoff Testington");
|
|
46
76
|
});
|
|
47
|
-
it("stores the
|
|
77
|
+
it("stores the options", () => {
|
|
48
78
|
const result = config.string({
|
|
49
79
|
variable: "SOME_VAR",
|
|
80
|
+
flag: "--some-flag",
|
|
50
81
|
fallback: "value",
|
|
51
82
|
});
|
|
52
83
|
assertEquals(result[Configuration.spec].type, "string");
|
|
53
|
-
assertEquals(result[Configuration.spec].
|
|
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({
|
|
54
92
|
variable: "SOME_VAR",
|
|
93
|
+
flag: "--some-flag",
|
|
55
94
|
fallback: "value",
|
|
56
95
|
});
|
|
96
|
+
const result = spec[Configuration.spec].describe("fullName");
|
|
97
|
+
assertEquals(result, {
|
|
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
|
+
],
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
describe("number", () => {
|
|
113
|
+
const config = new Configuration(bareOptions);
|
|
114
|
+
|
|
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
|
+
});
|
|
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
|
+
});
|
|
142
|
+
|
|
143
|
+
const result = config.number({
|
|
144
|
+
variable: "SOME_VAR",
|
|
145
|
+
fallback: 42,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
assertEquals(result.process(undefined), 12.34);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
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);
|
|
57
189
|
});
|
|
58
190
|
});
|
|
59
191
|
|
|
@@ -73,15 +205,17 @@ describe("Configuration", () => {
|
|
|
73
205
|
const result = struct.process(undefined);
|
|
74
206
|
assertEquals(result, new URL("https://fallback.example.com"));
|
|
75
207
|
});
|
|
76
|
-
it("stores the
|
|
208
|
+
it("stores the options", () => {
|
|
77
209
|
const result = config.url({
|
|
78
210
|
variable: "SOME_VAR",
|
|
211
|
+
flag: "--some-flag",
|
|
79
212
|
fallback: "https://example.com",
|
|
80
213
|
});
|
|
81
214
|
assertEquals(result[Configuration.spec].type, "url");
|
|
82
|
-
assertEquals(result[Configuration.spec].
|
|
215
|
+
assertEquals(result[Configuration.spec].options, {
|
|
83
216
|
variable: "SOME_VAR",
|
|
84
|
-
|
|
217
|
+
flag: "--some-flag",
|
|
218
|
+
fallback: new URL("https://example.com"),
|
|
85
219
|
});
|
|
86
220
|
});
|
|
87
221
|
});
|
|
@@ -96,7 +230,10 @@ describe("Configuration", () => {
|
|
|
96
230
|
const result = config._getValue({
|
|
97
231
|
flag: "--option",
|
|
98
232
|
});
|
|
99
|
-
assertEquals(result,
|
|
233
|
+
assertEquals(result, {
|
|
234
|
+
source: "argument",
|
|
235
|
+
value: "value-from-arg",
|
|
236
|
+
});
|
|
100
237
|
});
|
|
101
238
|
it("uses environment variables", () => {
|
|
102
239
|
const env = { MY_VAR: "value-from-env" };
|
|
@@ -107,12 +244,74 @@ describe("Configuration", () => {
|
|
|
107
244
|
const result = config._getValue({
|
|
108
245
|
variable: "MY_VAR",
|
|
109
246
|
});
|
|
110
|
-
assertEquals(result,
|
|
247
|
+
assertEquals(result, {
|
|
248
|
+
source: "variable",
|
|
249
|
+
value: "value-from-env",
|
|
250
|
+
});
|
|
111
251
|
});
|
|
112
252
|
it("uses the fallback", () => {
|
|
113
253
|
const config = new Configuration(bareOptions);
|
|
114
254
|
const result = config._getValue({ fallback: "value-from-fallback" });
|
|
115
|
-
assertEquals(result,
|
|
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
|
+
);
|
|
116
315
|
});
|
|
117
316
|
});
|
|
118
317
|
|
|
@@ -155,10 +354,10 @@ describe("Configuration", () => {
|
|
|
155
354
|
});
|
|
156
355
|
});
|
|
157
356
|
|
|
158
|
-
describe("
|
|
357
|
+
describe("describe", () => {
|
|
159
358
|
it("processes strings", () => {
|
|
160
359
|
const config = new Configuration(bareOptions);
|
|
161
|
-
const result = config.
|
|
360
|
+
const result = config.describe(
|
|
162
361
|
config.string({
|
|
163
362
|
fallback: "test-app",
|
|
164
363
|
variable: "APP_NAME",
|
|
@@ -180,7 +379,7 @@ describe("Configuration", () => {
|
|
|
180
379
|
|
|
181
380
|
it("processes urls", () => {
|
|
182
381
|
const config = new Configuration(bareOptions);
|
|
183
|
-
const result = config.
|
|
382
|
+
const result = config.describe(
|
|
184
383
|
config.url({
|
|
185
384
|
fallback: "https://example.com",
|
|
186
385
|
variable: "SELF_URL",
|
|
@@ -193,7 +392,7 @@ describe("Configuration", () => {
|
|
|
193
392
|
{
|
|
194
393
|
name: "selfUrl",
|
|
195
394
|
type: "url",
|
|
196
|
-
fallback: "https://example.com",
|
|
395
|
+
fallback: new URL("https://example.com"),
|
|
197
396
|
variable: "SELF_URL",
|
|
198
397
|
flag: "--self-url",
|
|
199
398
|
},
|
|
@@ -202,7 +401,7 @@ describe("Configuration", () => {
|
|
|
202
401
|
|
|
203
402
|
it("processes objects", () => {
|
|
204
403
|
const config = new Configuration(bareOptions);
|
|
205
|
-
const result = config.
|
|
404
|
+
const result = config.describe(
|
|
206
405
|
config.object({
|
|
207
406
|
name: config.string({
|
|
208
407
|
fallback: "testing-app",
|
package/core/http.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
static
|
|
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;
|
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
|
-
/**
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
/**
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
/**
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
/**
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
/**
|
|
65
|
-
|
|
66
|
-
|
|
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(
|
|
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(
|
|
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
|
}
|
package/core/http.test.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { assertInstanceOf, assertEquals } from "./test-deps.js";
|
|
1
|
+
import { assertInstanceOf, assertEquals, describe, it } from "./test-deps.js";
|
|
2
2
|
import { defineRoute, HTTPError } from "./http.js";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
describe("defineRoute", () => {
|
|
5
|
+
it("sets the method", () => {
|
|
6
6
|
const result = defineRoute({
|
|
7
7
|
method: "GET",
|
|
8
8
|
pathname: "/",
|
|
@@ -11,7 +11,7 @@ Deno.test("defineRoute", async (t) => {
|
|
|
11
11
|
assertEquals(result.method, "GET");
|
|
12
12
|
});
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
it("creates a URLPattern", () => {
|
|
15
15
|
const result = defineRoute({
|
|
16
16
|
method: "GET",
|
|
17
17
|
pathname: "/hello/:name",
|
|
@@ -22,47 +22,69 @@ Deno.test("defineRoute", async (t) => {
|
|
|
22
22
|
});
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
describe("HTTPError", () => {
|
|
26
|
+
describe("constructor", () => {
|
|
27
|
+
it("creates the error", () => {
|
|
28
|
+
const result = new HTTPError(418, "I'm a teapot");
|
|
29
|
+
assertEquals(result.status, 418);
|
|
30
|
+
assertEquals(result.statusText, "I'm a teapot");
|
|
31
|
+
assertEquals(result.name, "HTTPError");
|
|
32
|
+
});
|
|
31
33
|
});
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
describe("toResponse", () => {
|
|
36
|
+
it("creates the error", () => {
|
|
37
|
+
const result = new HTTPError(200, "OK").toResponse();
|
|
38
|
+
assertEquals(result.status, 200);
|
|
39
|
+
assertEquals(result.statusText, "OK");
|
|
40
|
+
});
|
|
41
|
+
it("sets headers", () => {
|
|
42
|
+
const error = new HTTPError(200, "OK", null, {
|
|
43
|
+
"X-HOTEL-BAR": "Hotel Bar?",
|
|
44
|
+
});
|
|
38
45
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
assertEquals(result.statusText, "Bad Request");
|
|
46
|
+
const result = error.toResponse();
|
|
47
|
+
assertEquals(result.headers.get("X-HOTEL-BAR"), "Hotel Bar?");
|
|
48
|
+
});
|
|
43
49
|
});
|
|
44
50
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
51
|
+
describe("badRequest", () => {
|
|
52
|
+
it("creates the error", () => {
|
|
53
|
+
const result = HTTPError.badRequest("body");
|
|
54
|
+
assertEquals(result.status, 400);
|
|
55
|
+
assertEquals(result.statusText, "Bad Request");
|
|
56
|
+
});
|
|
49
57
|
});
|
|
50
58
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
59
|
+
describe("unauthorized", () => {
|
|
60
|
+
it("creates the error", () => {
|
|
61
|
+
const result = HTTPError.unauthorized();
|
|
62
|
+
assertEquals(result.status, 401);
|
|
63
|
+
assertEquals(result.statusText, "Unauthorized");
|
|
64
|
+
});
|
|
55
65
|
});
|
|
56
66
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
67
|
+
describe("notFound", () => {
|
|
68
|
+
it("creates the error", () => {
|
|
69
|
+
const result = HTTPError.notFound();
|
|
70
|
+
assertEquals(result.status, 404);
|
|
71
|
+
assertEquals(result.statusText, "Not Found");
|
|
72
|
+
});
|
|
61
73
|
});
|
|
62
74
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
75
|
+
describe("internalServerError", () => {
|
|
76
|
+
it("creates the error", () => {
|
|
77
|
+
const result = HTTPError.internalServerError();
|
|
78
|
+
assertEquals(result.status, 500);
|
|
79
|
+
assertEquals(result.statusText, "Internal Server Error");
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe("notImplemented", () => {
|
|
83
|
+
it("creates the error", () => {
|
|
84
|
+
const result = HTTPError.notImplemented();
|
|
85
|
+
assertEquals(result.status, 501);
|
|
86
|
+
assertEquals(result.statusText, "Not Implemented");
|
|
87
|
+
});
|
|
88
|
+
});
|
|
67
89
|
});
|
|
68
90
|
});
|
package/core/mod.d.ts
CHANGED
package/core/mod.js
CHANGED
package/core/structures.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ export class StructError extends Error {
|
|
|
23
23
|
*/
|
|
24
24
|
/**
|
|
25
25
|
* @template T
|
|
26
|
-
* @typedef {(input?: unknown, context
|
|
26
|
+
* @typedef {(input?: unknown, context?: StructContext) => T} StructExec
|
|
27
27
|
*/
|
|
28
28
|
/**
|
|
29
29
|
* @template T
|
|
@@ -34,26 +34,39 @@ export class StructError extends Error {
|
|
|
34
34
|
*/
|
|
35
35
|
export class Structure<T> {
|
|
36
36
|
/**
|
|
37
|
-
* @param {string} fallback
|
|
37
|
+
* @param {string} [fallback]
|
|
38
38
|
* @returns {Structure<string>}
|
|
39
39
|
*/
|
|
40
|
-
static string(fallback
|
|
40
|
+
static string(fallback?: string): Structure<string>;
|
|
41
41
|
/**
|
|
42
|
-
* @param {number} fallback
|
|
42
|
+
* @param {number} [fallback]
|
|
43
43
|
* @returns {Structure<number>}
|
|
44
44
|
*/
|
|
45
|
-
static number(fallback
|
|
45
|
+
static number(fallback?: number): Structure<number>;
|
|
46
46
|
/**
|
|
47
|
-
* @param {
|
|
47
|
+
* @param {boolean} fallback
|
|
48
|
+
* @returns {Structure<boolean>}
|
|
49
|
+
*/
|
|
50
|
+
static boolean(fallback: boolean): Structure<boolean>;
|
|
51
|
+
/**
|
|
52
|
+
* @param {string | URL} [fallback]
|
|
48
53
|
* @returns {Structure<URL>}
|
|
49
54
|
*/
|
|
50
|
-
static url(fallback
|
|
55
|
+
static url(fallback?: string | URL): Structure<URL>;
|
|
51
56
|
/**
|
|
52
57
|
* @template {Record<string, Structure<any>>} U
|
|
53
58
|
* @param {U} fields
|
|
54
59
|
* @returns {Structure<{ [K in keyof U]: Infer<U[K]> }>}
|
|
55
60
|
*/
|
|
56
61
|
static object<U extends Record<string, Structure<any>>>(fields: U): Structure<{ [K in keyof U]: Infer<U[K]>; }>;
|
|
62
|
+
/**
|
|
63
|
+
* **UNSTABLE** use at your own risk
|
|
64
|
+
*
|
|
65
|
+
* @template {Structure<any>} U
|
|
66
|
+
* @param {U} struct
|
|
67
|
+
* @returns {Structure<Array<Infer<U>>>}
|
|
68
|
+
*/
|
|
69
|
+
static array<U_1 extends Structure<any>>(struct: U_1): Structure<Infer<U_1>[]>;
|
|
57
70
|
/**
|
|
58
71
|
* @param {Schema} schema
|
|
59
72
|
* @param {StructExec<T>} process
|
|
@@ -68,11 +81,11 @@ export class Structure<T> {
|
|
|
68
81
|
export type StructContext = {
|
|
69
82
|
path: string[];
|
|
70
83
|
};
|
|
71
|
-
export type StructOptions<Type,
|
|
84
|
+
export type StructOptions<Type, Schema> = {
|
|
72
85
|
type: 'object' | 'string' | 'url';
|
|
73
86
|
schema: Schema;
|
|
74
87
|
fn: (value: unknown, ctx: StructContext) => Type;
|
|
75
88
|
};
|
|
76
89
|
export type Schema = Record<string, unknown>;
|
|
77
|
-
export type StructExec<T> = (input?: unknown, context
|
|
90
|
+
export type StructExec<T> = (input?: unknown, context?: StructContext) => T;
|
|
78
91
|
export type Infer<T> = T extends Structure<infer U> ? U : never;
|