1 | import { combine } from "./context.js";
|
2 | import { accepts } from './content-negotiation.js';
|
3 | import { payloadTooLarge } from '@worker-tools/response-creators';
|
4 | export const JSON = 'application/json';
|
5 | export const FORM = 'application/x-www-form-urlencoded';
|
6 | export const FORM_DATA = 'multipart/form-data';
|
7 | export const TEXT_HTML = 'text/html';
|
8 | export const TEXT_PLAIN = 'text/plain';
|
9 | /** Standard MIME type for binary data */
|
10 | export const BINARY = 'application/octet-stream';
|
11 | /** Non-standard MIME type for binary data. Sometimes used, so included anyway. */
|
12 | export const X_BINARY = 'application/x-binary';
|
13 | const defaultBody = [
|
14 | FORM,
|
15 | FORM_DATA,
|
16 | JSON,
|
17 | BINARY,
|
18 | X_BINARY,
|
19 | TEXT_HTML,
|
20 | TEXT_PLAIN,
|
21 | ];
|
22 | // Not possible to provide a fallback rn: https://github.com/microsoft/TypeScript/issues/48073
|
23 | const _isString = (x) => !(x[1] instanceof File);
|
24 | const _isFile = (x) => x[1] instanceof File;
|
25 | const isBodyTextContext = (nx) => { var _a; return (_a = nx.accepted) === null || _a === void 0 ? void 0 : _a.startsWith('text/'); };
|
26 | const isBodyVendorJSONContext = (nx) => { var _a; return ((_a = nx.accepted) === null || _a === void 0 ? void 0 : _a.startsWith('application/')) && nx.accepted.endsWith('+json'); };
|
27 | // const isBodyVendorBinaryContext = <J = any>(nx: BodyContext<J>): nx is BodyVendorBinaryContext =>
|
28 | // nx.accepted?.startsWith('application/vnd.')
|
29 | const MB = 1024 ** 2;
|
30 | async function checkSize(req, maxSize) {
|
31 | let size = 0;
|
32 | await req.clone().body.pipeTo(new WritableStream({
|
33 | write(chunk, ctrl) {
|
34 | size += chunk.byteLength;
|
35 | if (size > maxSize) {
|
36 | ctrl.error(new Error('Payload too large'));
|
37 | }
|
38 | }
|
39 | }));
|
40 | return size <= maxSize;
|
41 | }
|
42 | export const bodyParser = (opts = {}) => async (ax) => {
|
43 | var _a;
|
44 | const x = await ax;
|
45 | const nx = x;
|
46 | const ok = await checkSize(x.request, (_a = opts.maxSize) !== null && _a !== void 0 ? _a : 1 * MB);
|
47 | if (!ok)
|
48 | throw payloadTooLarge();
|
49 | switch (nx.accepted) {
|
50 | case JSON: {
|
51 | nx.body = nx.json = await x.request.json();
|
52 | return nx;
|
53 | }
|
54 | case FORM: {
|
55 | nx.body = nx.form = new URLSearchParams(await x.request.text());
|
56 | // FIXME: Multiple values per key??
|
57 | // nx.form = Object.fromEntries(form);
|
58 | return nx;
|
59 | }
|
60 | case FORM_DATA: {
|
61 | nx.body = nx.formData = await x.request.formData();
|
62 | // FIXME: Multiple values per key??
|
63 | // const tuples = [...formData];
|
64 | // nx.form = Object.fromEntries(tuples.filter(isString));
|
65 | // nx.files = Object.fromEntries(tuples.filter(isFile));
|
66 | return nx;
|
67 | }
|
68 | case BINARY:
|
69 | case X_BINARY: {
|
70 | nx.body = nx.arrayBuffer = await x.request.arrayBuffer();
|
71 | nx.blob = new Blob([nx.arrayBuffer]); // TODO: does this copy??
|
72 | return nx;
|
73 | }
|
74 | default: {
|
75 | if (isBodyTextContext(nx)) {
|
76 | nx.body = nx.text = await x.request.text();
|
77 | }
|
78 | else if (isBodyVendorJSONContext(nx)) {
|
79 | nx.body = nx.json = await x.request.json();
|
80 | return nx;
|
81 | // } else if (isBodyVendorBinaryContext(nx)) {
|
82 | // nx.body = nx.buffer = await x.request.arrayBuffer();
|
83 | // nx.blob = new Blob([nx.buffer]) // TODO: does this copy??
|
84 | // return nx;
|
85 | }
|
86 | else {
|
87 | // Anything else gets the binary treatment (outside of scope of type system)
|
88 | nx.body = nx.buffer = await x.request.arrayBuffer();
|
89 | nx.blob = new Blob([nx.buffer]);
|
90 | return nx;
|
91 | }
|
92 | return nx;
|
93 | }
|
94 | }
|
95 | };
|
96 | export const defaultBodyParser = (options) => combine(accepts(defaultBody), bodyParser(options));
|
97 | // type ErrorOf<T> = T extends { error?: infer E } ? E : never
|
98 | // (async () => {
|
99 | // const ctx: Context = { request: new Request('/'), effects: [], waitUntil: (_f: any) => {}, handled: Promise.resolve(null as any) }
|
100 | // const z = provides([])(accepts([])(ctx))
|
101 | // const x = await parseBody()(accepts(['text/x-foo', 'application/vnd.github.v3+json', FORM, FORM_DATA])(ctx))
|
102 | // if (x.accepted === 'application/vnd.github.v3+json') {
|
103 | // x.body
|
104 | // } else if (x.accepted === 'text/x-foo') {
|
105 | // x.body
|
106 | // } else if (x.accepted === 'application/x-www-form-urlencoded') {
|
107 | // x.body
|
108 | // }
|
109 | // const y = await bodyParser()(ctx)
|
110 | // if (y.accepted === 'application/x-www-form-urlencoded') {
|
111 | // y.bodyParams
|
112 | // y.body
|
113 | // }
|
114 | // if (y.accepted === 'multipart/form-data') {
|
115 | // y.formData
|
116 | // y.body
|
117 | // }
|
118 | // if (y.accepted === 'application/foobar+json') {
|
119 | // y.json
|
120 | // y.body
|
121 | // }
|
122 | // // if (x.accepted === 'application/x-www-form-urlencoded') {
|
123 | // // x.body
|
124 | // // x.bodyParams
|
125 | // // x.form
|
126 | // // }
|
127 | // // else if (x.accepted === 'multipart/form-data') {
|
128 | // // x.formData
|
129 | // // x.form
|
130 | // // x.files
|
131 | // // } else if (x.accepted === 'application/octet-stream' || x.accepted === 'application/x-binary') {
|
132 | // // x.buffer
|
133 | // // x.blob
|
134 | // // } else if (x.accepted === 'application/vnd.github.v3+json') {
|
135 | // // } else if (x.accepted === 'text/foo') {
|
136 | // // x.text
|
137 | // // }
|
138 | // })
|
139 | //# sourceMappingURL=body-parser.js.map |
\ | No newline at end of file |