@cfworker/web 5.1.1 → 6.1.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/README.md +39 -0
- package/dist/commonjs/index.d.ts +1 -0
- package/dist/commonjs/index.d.ts.map +1 -1
- package/dist/commonjs/index.js +1 -0
- package/dist/commonjs/index.js.map +1 -1
- package/dist/commonjs/parse-entries.d.ts +3 -0
- package/dist/commonjs/parse-entries.d.ts.map +1 -0
- package/dist/commonjs/parse-entries.js +20 -0
- package/dist/commonjs/parse-entries.js.map +1 -0
- package/dist/commonjs/validate.d.ts.map +1 -1
- package/dist/commonjs/validate.js +3 -2
- package/dist/commonjs/validate.js.map +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/parse-entries.d.ts +3 -0
- package/dist/esm/parse-entries.d.ts.map +1 -0
- package/dist/esm/parse-entries.js +17 -0
- package/dist/esm/parse-entries.js.map +1 -0
- package/dist/esm/validate.d.ts.map +1 -1
- package/dist/esm/validate.js +3 -2
- package/dist/esm/validate.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -81,6 +81,45 @@ new Application()
|
|
|
81
81
|
.listen();
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
+
## Repeated validation values
|
|
85
|
+
|
|
86
|
+
By default, `validate` converts headers, query parameters, and form fields into
|
|
87
|
+
objects before applying their schemas. A value that occurs once is stored
|
|
88
|
+
directly. Repeated values are stored as an array in encounter order, including
|
|
89
|
+
empty values:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
// ?tag=a&tag=&tag=b
|
|
93
|
+
const query = { tag: ['a', '', 'b'] };
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
For query parameters and URL-encoded forms, values are strings. Multipart form
|
|
97
|
+
values can also be `File` objects. `Headers` supplies normalized values and
|
|
98
|
+
usually combines repeated header lines into one string before validation.
|
|
99
|
+
|
|
100
|
+
The array shape makes a scalar schema reject duplicate query parameters or form
|
|
101
|
+
fields rather than validating a different value from the one application code
|
|
102
|
+
might read. To accept both single and repeated values, declare both shapes
|
|
103
|
+
explicitly:
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
validate({
|
|
107
|
+
search: {
|
|
108
|
+
properties: {
|
|
109
|
+
tag: {
|
|
110
|
+
oneOf: [
|
|
111
|
+
{ type: 'string' },
|
|
112
|
+
{ type: 'array', items: { type: 'string' } }
|
|
113
|
+
]
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
A custom `RequestParser` continues to receive the original `URLSearchParams` or
|
|
121
|
+
`FormData` input and controls its resulting shape.
|
|
122
|
+
|
|
84
123
|
[To run this example](/packages/examples/web/index.ts):
|
|
85
124
|
|
|
86
125
|
```
|
package/dist/commonjs/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export * from './cookies.js';
|
|
|
5
5
|
export * from './http-error.js';
|
|
6
6
|
export * from './middleware.js';
|
|
7
7
|
export * from './normalize-pathname.js';
|
|
8
|
+
export * from './parse-entries.js';
|
|
8
9
|
export * from './req.js';
|
|
9
10
|
export * from './response-builder.js';
|
|
10
11
|
export * from './router.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,yBAAyB,CAAC;AACxC,cAAc,UAAU,CAAC;AACzB,cAAc,uBAAuB,CAAC;AACtC,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,UAAU,CAAC;AACzB,cAAc,uBAAuB,CAAC;AACtC,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC"}
|
package/dist/commonjs/index.js
CHANGED
|
@@ -21,6 +21,7 @@ __exportStar(require("./cookies.js"), exports);
|
|
|
21
21
|
__exportStar(require("./http-error.js"), exports);
|
|
22
22
|
__exportStar(require("./middleware.js"), exports);
|
|
23
23
|
__exportStar(require("./normalize-pathname.js"), exports);
|
|
24
|
+
__exportStar(require("./parse-entries.js"), exports);
|
|
24
25
|
__exportStar(require("./req.js"), exports);
|
|
25
26
|
__exportStar(require("./response-builder.js"), exports);
|
|
26
27
|
__exportStar(require("./router.js"), exports);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,+CAA6B;AAC7B,mDAAiC;AACjC,+CAA6B;AAC7B,+CAA6B;AAC7B,kDAAgC;AAChC,kDAAgC;AAChC,0DAAwC;AACxC,2CAAyB;AACzB,wDAAsC;AACtC,8CAA4B;AAC5B,gDAA8B","sourcesContent":["export * from './accepts.js';\nexport * from './application.js';\nexport * from './context.js';\nexport * from './cookies.js';\nexport * from './http-error.js';\nexport * from './middleware.js';\nexport * from './normalize-pathname.js';\nexport * from './req.js';\nexport * from './response-builder.js';\nexport * from './router.js';\nexport * from './validate.js';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,+CAA6B;AAC7B,mDAAiC;AACjC,+CAA6B;AAC7B,+CAA6B;AAC7B,kDAAgC;AAChC,kDAAgC;AAChC,0DAAwC;AACxC,qDAAmC;AACnC,2CAAyB;AACzB,wDAAsC;AACtC,8CAA4B;AAC5B,gDAA8B","sourcesContent":["export * from './accepts.js';\nexport * from './application.js';\nexport * from './context.js';\nexport * from './cookies.js';\nexport * from './http-error.js';\nexport * from './middleware.js';\nexport * from './normalize-pathname.js';\nexport * from './parse-entries.js';\nexport * from './req.js';\nexport * from './response-builder.js';\nexport * from './router.js';\nexport * from './validate.js';\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-entries.d.ts","sourceRoot":"","sources":["../../src/parse-entries.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAEvD,wBAAgB,YAAY,CAAC,CAAC,EAC5B,KAAK,EAAE,QAAQ,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,GACpC,aAAa,CAAC,CAAC,CAAC,CAkBlB"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseEntries = parseEntries;
|
|
4
|
+
function parseEntries(input) {
|
|
5
|
+
const entries = new Map();
|
|
6
|
+
for (const [name, value] of input) {
|
|
7
|
+
const values = entries.get(name);
|
|
8
|
+
if (values === undefined) {
|
|
9
|
+
entries.set(name, [value]);
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
values.push(value);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return Object.fromEntries(Array.from(entries, ([name, values]) => [
|
|
16
|
+
name,
|
|
17
|
+
values.length === 1 ? values[0] : values
|
|
18
|
+
]));
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=parse-entries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-entries.js","sourceRoot":"","sources":["../../src/parse-entries.ts"],"names":[],"mappings":";;;AAEA,sBACE,KAAqC;IAErC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAe,CAAC;IAEvC,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,KAAK,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;QAC7B,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC,WAAW,CACvB,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC;QACtC,IAAI;QACJ,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,MAAM;KAC1C,CAAC,CACH,CAAC;AACJ,CAAC","sourcesContent":["export type ParsedEntries<T> = Record<string, T | T[]>;\n\nexport function parseEntries<T>(\n input: Iterable<readonly [string, T]>\n): ParsedEntries<T> {\n const entries = new Map<string, T[]>();\n\n for (const [name, value] of input) {\n const values = entries.get(name);\n if (values === undefined) {\n entries.set(name, [value]);\n } else {\n values.push(value);\n }\n }\n\n return Object.fromEntries(\n Array.from(entries, ([name, values]) => [\n name,\n values.length === 1 ? values[0]! : values\n ])\n );\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,UAAU,EACV,MAAM,EAEP,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,UAAU,EACV,MAAM,EAEP,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG7C,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,WAAW,GAAG,MAAM,cAAc,CAAC;AAE/C,MAAM,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,eAAe,GAAG,QAAQ,KAAK,GAAG,CAAC;AAWtE,iBAAS,iBAAiB,CACxB,OAAO,EAAE,cAAc,EACvB,MAAM,GAAE,aAA4B,EACpC,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAuB,GAC7D,UAAU,CAkDZ;AAED,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAKhD,CAAC;AAEF,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,GAAG,CAAC;IACZ,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAsBD,eAAO,MAAM,QAAQ,EAAE,OAAO,iBAAqC,CAAC"}
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.validate = exports.messages = void 0;
|
|
4
4
|
const json_schema_1 = require("@cfworker/json-schema");
|
|
5
5
|
const http_error_js_1 = require("./http-error.js");
|
|
6
|
+
const parse_entries_js_1 = require("./parse-entries.js");
|
|
6
7
|
const draft = '2019-09';
|
|
7
8
|
const hasBody = {
|
|
8
9
|
POST: true,
|
|
@@ -10,7 +11,7 @@ const hasBody = {
|
|
|
10
11
|
DELETE: true,
|
|
11
12
|
PATCH: true
|
|
12
13
|
};
|
|
13
|
-
function middlewareFactory(schemas, parser =
|
|
14
|
+
function middlewareFactory(schemas, parser = parse_entries_js_1.parseEntries, lookup = Object.create(null)) {
|
|
14
15
|
const { headers: $headers, search: $search, params: $params, body: $body } = schemas;
|
|
15
16
|
if ($headers) {
|
|
16
17
|
(0, json_schema_1.dereference)($headers, { ...lookup });
|
|
@@ -29,7 +30,7 @@ function middlewareFactory(schemas, parser = Object.fromEntries, lookup = Object
|
|
|
29
30
|
validateRequestPart('params', req.params, $params, lookup);
|
|
30
31
|
}
|
|
31
32
|
if ($headers) {
|
|
32
|
-
const headers =
|
|
33
|
+
const headers = (0, parse_entries_js_1.parseEntries)(req.headers);
|
|
33
34
|
validateRequestPart('headers', headers, $headers, lookup);
|
|
34
35
|
}
|
|
35
36
|
if ($search) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/validate.ts"],"names":[],"mappings":";;;AAAA,uDAK+B;AAC/B,mDAA4C;
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/validate.ts"],"names":[],"mappings":";;;AAAA,uDAK+B;AAC/B,mDAA4C;AAE5C,yDAAkD;AAalD,MAAM,KAAK,GAAG,SAAS,CAAC;AAExB,MAAM,OAAO,GAAyB;IACpC,IAAI,EAAE,IAAI;IACV,GAAG,EAAE,IAAI;IACT,MAAM,EAAE,IAAI;IACZ,KAAK,EAAE,IAAI;CACZ,CAAC;AAEF,SAAS,iBAAiB,CACxB,OAAuB,EACvB,MAAM,GAAkB,+BAAY,EACpC,MAAM,GAAqC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;IAE9D,MAAM,EACJ,OAAO,EAAE,QAAQ,EACjB,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,OAAO,EACf,IAAI,EAAE,KAAK,EACZ,GAAG,OAAO,CAAC;IAEZ,IAAI,QAAQ,EAAE,CAAC;QACb,IAAA,yBAAW,EAAC,QAAQ,EAAE,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;IACvC,CAAC;IACD,IAAI,OAAO,EAAE,CAAC;QACZ,IAAA,yBAAW,EAAC,OAAO,EAAE,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,OAAO,EAAE,CAAC;QACZ,IAAA,yBAAW,EAAC,OAAO,EAAE,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,KAAK,EAAE,CAAC;QACV,IAAA,yBAAW,EAAC,KAAK,EAAE,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;IACpC,CAAC;IAED,OAAO,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,EAAE;QAC7B,IAAI,OAAO,EAAE,CAAC;YACZ,mBAAmB,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QAC7D,CAAC;QACD,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,IAAA,+BAAY,EAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAC1C,mBAAmB,CAAC,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC5D,CAAC;QACD,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;YAC5C,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QACzD,CAAC;QACD,IAAI,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACjC,IAAI,IAAS,CAAC;YACd,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;YAC1D,IACE,WAAW,KAAK,mCAAmC;gBACnD,WAAW,CAAC,UAAU,CAAC,oCAAoC,CAAC;gBAC5D,WAAW,CAAC,UAAU,CAAC,sBAAsB,CAAC,EAC9C,CAAC;gBACD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACvC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;YACtB,CAAC;iBAAM,CAAC;gBACN,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YAC/B,CAAC;YACD,mBAAmB,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QACnD,CAAC;QACD,MAAM,IAAI,EAAE,CAAC;IACf,CAAC,CAAC;AACJ,CAAC;AAEY,QAAA,QAAQ,GAAgC;IACnD,MAAM,EAAE,yCAAyC;IACjD,OAAO,EAAE,sBAAsB;IAC/B,MAAM,EAAE,sCAAsC;IAC9C,IAAI,EAAE,kBAAkB;CACzB,CAAC;AAUF,SAAS,mBAAmB,CAC1B,IAAiB,EACjB,QAAa,EACb,MAAc,EACd,MAAwC;IAExC,MAAM,MAAM,GAAG,IAAA,sBAAc,EAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IAC/D,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,OAAO;IACT,CAAC;IACD,MAAM,SAAS,GAA+B;QAC5C,MAAM,EAAE,GAAG;QACX,IAAI;QACJ,OAAO,EAAE,QAAA,QAAQ,CAAC,IAAI,CAAC;QACvB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,MAAM;KACP,CAAC;IACF,MAAM,IAAI,yBAAS,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;AACtC,CAAC;AAEY,QAAA,QAAQ,GAA6B,iBAAiB,CAAC","sourcesContent":["import {\n dereference,\n OutputUnit,\n Schema,\n validate as schemaValidate\n} from '@cfworker/json-schema';\nimport { HttpError } from './http-error.js';\nimport { Middleware } from './middleware.js';\nimport { parseEntries } from './parse-entries.js';\n\nexport interface RequestSchemas {\n body?: Schema;\n headers?: Schema;\n params?: Schema;\n search?: Schema;\n}\n\nexport type RequestPart = keyof RequestSchemas;\n\nexport type RequestParser = (data: URLSearchParams | FormData) => any;\n\nconst draft = '2019-09';\n\nconst hasBody: Record<string, true> = {\n POST: true,\n PUT: true,\n DELETE: true,\n PATCH: true\n};\n\nfunction middlewareFactory(\n schemas: RequestSchemas,\n parser: RequestParser = parseEntries,\n lookup: Record<string, Schema | boolean> = Object.create(null)\n): Middleware {\n const {\n headers: $headers,\n search: $search,\n params: $params,\n body: $body\n } = schemas;\n\n if ($headers) {\n dereference($headers, { ...lookup });\n }\n if ($search) {\n dereference($search, { ...lookup });\n }\n if ($params) {\n dereference($params, { ...lookup });\n }\n if ($body) {\n dereference($body, { ...lookup });\n }\n\n return async ({ req }, next) => {\n if ($params) {\n validateRequestPart('params', req.params, $params, lookup);\n }\n if ($headers) {\n const headers = parseEntries(req.headers);\n validateRequestPart('headers', headers, $headers, lookup);\n }\n if ($search) {\n const search = parser(req.url.searchParams);\n validateRequestPart('search', search, $search, lookup);\n }\n if ($body && hasBody[req.method]) {\n let body: any;\n const contentType = req.headers.get('content-type') || '';\n if (\n contentType === 'application/x-www-form-urlencoded' ||\n contentType.startsWith('application/x-www-form-urlencoded;') ||\n contentType.startsWith('multipart/form-data;')\n ) {\n const form = await req.body.formData();\n body = parser(form);\n } else {\n body = await req.body.json();\n }\n validateRequestPart('body', body, $body, lookup);\n }\n await next();\n };\n}\n\nexport const messages: Record<RequestPart, string> = {\n params: 'Parameters in request path are invalid.',\n headers: 'Headers are invalid.',\n search: 'Query string parameters are invalid.',\n body: 'Body is invalid.'\n};\n\nexport interface RequestValidationErrorBody {\n status: 400;\n part: RequestPart;\n message: string;\n errors: OutputUnit[];\n schema: Schema;\n}\n\nfunction validateRequestPart(\n part: RequestPart,\n instance: any,\n schema: Schema,\n lookup: Record<string, boolean | Schema>\n) {\n const result = schemaValidate(instance, schema, draft, lookup);\n if (result.valid) {\n return;\n }\n const errorBody: RequestValidationErrorBody = {\n status: 400,\n part,\n message: messages[part],\n errors: result.errors,\n schema\n };\n throw new HttpError(400, errorBody);\n}\n\nexport const validate: typeof middlewareFactory = middlewareFactory;\n"]}
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export * from './cookies.js';
|
|
|
5
5
|
export * from './http-error.js';
|
|
6
6
|
export * from './middleware.js';
|
|
7
7
|
export * from './normalize-pathname.js';
|
|
8
|
+
export * from './parse-entries.js';
|
|
8
9
|
export * from './req.js';
|
|
9
10
|
export * from './response-builder.js';
|
|
10
11
|
export * from './router.js';
|
package/dist/esm/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,yBAAyB,CAAC;AACxC,cAAc,UAAU,CAAC;AACzB,cAAc,uBAAuB,CAAC;AACtC,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,UAAU,CAAC;AACzB,cAAc,uBAAuB,CAAC;AACtC,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC"}
|
package/dist/esm/index.js
CHANGED
|
@@ -5,6 +5,7 @@ export * from './cookies.js';
|
|
|
5
5
|
export * from './http-error.js';
|
|
6
6
|
export * from './middleware.js';
|
|
7
7
|
export * from './normalize-pathname.js';
|
|
8
|
+
export * from './parse-entries.js';
|
|
8
9
|
export * from './req.js';
|
|
9
10
|
export * from './response-builder.js';
|
|
10
11
|
export * from './router.js';
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,yBAAyB,CAAC;AACxC,cAAc,UAAU,CAAC;AACzB,cAAc,uBAAuB,CAAC;AACtC,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC","sourcesContent":["export * from './accepts.js';\nexport * from './application.js';\nexport * from './context.js';\nexport * from './cookies.js';\nexport * from './http-error.js';\nexport * from './middleware.js';\nexport * from './normalize-pathname.js';\nexport * from './req.js';\nexport * from './response-builder.js';\nexport * from './router.js';\nexport * from './validate.js';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,UAAU,CAAC;AACzB,cAAc,uBAAuB,CAAC;AACtC,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC","sourcesContent":["export * from './accepts.js';\nexport * from './application.js';\nexport * from './context.js';\nexport * from './cookies.js';\nexport * from './http-error.js';\nexport * from './middleware.js';\nexport * from './normalize-pathname.js';\nexport * from './parse-entries.js';\nexport * from './req.js';\nexport * from './response-builder.js';\nexport * from './router.js';\nexport * from './validate.js';\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-entries.d.ts","sourceRoot":"","sources":["../../src/parse-entries.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAEvD,wBAAgB,YAAY,CAAC,CAAC,EAC5B,KAAK,EAAE,QAAQ,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,GACpC,aAAa,CAAC,CAAC,CAAC,CAkBlB"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function parseEntries(input) {
|
|
2
|
+
const entries = new Map();
|
|
3
|
+
for (const [name, value] of input) {
|
|
4
|
+
const values = entries.get(name);
|
|
5
|
+
if (values === undefined) {
|
|
6
|
+
entries.set(name, [value]);
|
|
7
|
+
}
|
|
8
|
+
else {
|
|
9
|
+
values.push(value);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return Object.fromEntries(Array.from(entries, ([name, values]) => [
|
|
13
|
+
name,
|
|
14
|
+
values.length === 1 ? values[0] : values
|
|
15
|
+
]));
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=parse-entries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-entries.js","sourceRoot":"","sources":["../../src/parse-entries.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,YAAY,CAC1B,KAAqC;IAErC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAe,CAAC;IAEvC,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,KAAK,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;QAC7B,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC,WAAW,CACvB,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC;QACtC,IAAI;QACJ,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,MAAM;KAC1C,CAAC,CACH,CAAC;AACJ,CAAC","sourcesContent":["export type ParsedEntries<T> = Record<string, T | T[]>;\n\nexport function parseEntries<T>(\n input: Iterable<readonly [string, T]>\n): ParsedEntries<T> {\n const entries = new Map<string, T[]>();\n\n for (const [name, value] of input) {\n const values = entries.get(name);\n if (values === undefined) {\n entries.set(name, [value]);\n } else {\n values.push(value);\n }\n }\n\n return Object.fromEntries(\n Array.from(entries, ([name, values]) => [\n name,\n values.length === 1 ? values[0]! : values\n ])\n );\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,UAAU,EACV,MAAM,EAEP,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,UAAU,EACV,MAAM,EAEP,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG7C,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,WAAW,GAAG,MAAM,cAAc,CAAC;AAE/C,MAAM,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,eAAe,GAAG,QAAQ,KAAK,GAAG,CAAC;AAWtE,iBAAS,iBAAiB,CACxB,OAAO,EAAE,cAAc,EACvB,MAAM,GAAE,aAA4B,EACpC,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAuB,GAC7D,UAAU,CAkDZ;AAED,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAKhD,CAAC;AAEF,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,GAAG,CAAC;IACZ,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAsBD,eAAO,MAAM,QAAQ,EAAE,OAAO,iBAAqC,CAAC"}
|
package/dist/esm/validate.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { dereference, validate as schemaValidate } from '@cfworker/json-schema';
|
|
2
2
|
import { HttpError } from './http-error.js';
|
|
3
|
+
import { parseEntries } from './parse-entries.js';
|
|
3
4
|
const draft = '2019-09';
|
|
4
5
|
const hasBody = {
|
|
5
6
|
POST: true,
|
|
@@ -7,7 +8,7 @@ const hasBody = {
|
|
|
7
8
|
DELETE: true,
|
|
8
9
|
PATCH: true
|
|
9
10
|
};
|
|
10
|
-
function middlewareFactory(schemas, parser =
|
|
11
|
+
function middlewareFactory(schemas, parser = parseEntries, lookup = Object.create(null)) {
|
|
11
12
|
const { headers: $headers, search: $search, params: $params, body: $body } = schemas;
|
|
12
13
|
if ($headers) {
|
|
13
14
|
dereference($headers, { ...lookup });
|
|
@@ -26,7 +27,7 @@ function middlewareFactory(schemas, parser = Object.fromEntries, lookup = Object
|
|
|
26
27
|
validateRequestPart('params', req.params, $params, lookup);
|
|
27
28
|
}
|
|
28
29
|
if ($headers) {
|
|
29
|
-
const headers =
|
|
30
|
+
const headers = parseEntries(req.headers);
|
|
30
31
|
validateRequestPart('headers', headers, $headers, lookup);
|
|
31
32
|
}
|
|
32
33
|
if ($search) {
|
package/dist/esm/validate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EAGX,QAAQ,IAAI,cAAc,EAC3B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/validate.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n dereference,\n OutputUnit,\n Schema,\n validate as schemaValidate\n} from '@cfworker/json-schema';\nimport { HttpError } from './http-error.js';\nimport { Middleware } from './middleware.js';\nimport { parseEntries } from './parse-entries.js';\n\nexport interface RequestSchemas {\n body?: Schema;\n headers?: Schema;\n params?: Schema;\n search?: Schema;\n}\n\nexport type RequestPart = keyof RequestSchemas;\n\nexport type RequestParser = (data: URLSearchParams | FormData) => any;\n\nconst draft = '2019-09';\n\nconst hasBody: Record<string, true> = {\n POST: true,\n PUT: true,\n DELETE: true,\n PATCH: true\n};\n\nfunction middlewareFactory(\n schemas: RequestSchemas,\n parser: RequestParser = parseEntries,\n lookup: Record<string, Schema | boolean> = Object.create(null)\n): Middleware {\n const {\n headers: $headers,\n search: $search,\n params: $params,\n body: $body\n } = schemas;\n\n if ($headers) {\n dereference($headers, { ...lookup });\n }\n if ($search) {\n dereference($search, { ...lookup });\n }\n if ($params) {\n dereference($params, { ...lookup });\n }\n if ($body) {\n dereference($body, { ...lookup });\n }\n\n return async ({ req }, next) => {\n if ($params) {\n validateRequestPart('params', req.params, $params, lookup);\n }\n if ($headers) {\n const headers = parseEntries(req.headers);\n validateRequestPart('headers', headers, $headers, lookup);\n }\n if ($search) {\n const search = parser(req.url.searchParams);\n validateRequestPart('search', search, $search, lookup);\n }\n if ($body && hasBody[req.method]) {\n let body: any;\n const contentType = req.headers.get('content-type') || '';\n if (\n contentType === 'application/x-www-form-urlencoded' ||\n contentType.startsWith('application/x-www-form-urlencoded;') ||\n contentType.startsWith('multipart/form-data;')\n ) {\n const form = await req.body.formData();\n body = parser(form);\n } else {\n body = await req.body.json();\n }\n validateRequestPart('body', body, $body, lookup);\n }\n await next();\n };\n}\n\nexport const messages: Record<RequestPart, string> = {\n params: 'Parameters in request path are invalid.',\n headers: 'Headers are invalid.',\n search: 'Query string parameters are invalid.',\n body: 'Body is invalid.'\n};\n\nexport interface RequestValidationErrorBody {\n status: 400;\n part: RequestPart;\n message: string;\n errors: OutputUnit[];\n schema: Schema;\n}\n\nfunction validateRequestPart(\n part: RequestPart,\n instance: any,\n schema: Schema,\n lookup: Record<string, boolean | Schema>\n) {\n const result = schemaValidate(instance, schema, draft, lookup);\n if (result.valid) {\n return;\n }\n const errorBody: RequestValidationErrorBody = {\n status: 400,\n part,\n message: messages[part],\n errors: result.errors,\n schema\n };\n throw new HttpError(400, errorBody);\n}\n\nexport const validate: typeof middlewareFactory = middlewareFactory;\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cfworker/web",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.1.0",
|
|
4
4
|
"description": "Web framework for Cloudflare Workers and service workers, inspired by Koa and fastify",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"middleware",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"test": "node ../../test.mjs"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@cfworker/json-schema": "^4.1.
|
|
49
|
+
"@cfworker/json-schema": "^4.1.4",
|
|
50
50
|
"@hapi/bourne": "^4.0.1",
|
|
51
51
|
"@types/statuses": "^2.0.6",
|
|
52
52
|
"cookie": "^2.0.1",
|