@typespec/http-server-js 0.58.0-alpha.10-dev.3
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 +69 -0
- package/LICENSE +21 -0
- package/README.md +183 -0
- package/build-helpers.ts +170 -0
- package/dist/generated-defs/helpers/header.d.ts +4 -0
- package/dist/generated-defs/helpers/header.d.ts.map +1 -0
- package/dist/generated-defs/helpers/header.js +76 -0
- package/dist/generated-defs/helpers/header.js.map +1 -0
- package/dist/generated-defs/helpers/http.d.ts +4 -0
- package/dist/generated-defs/helpers/http.d.ts.map +1 -0
- package/dist/generated-defs/helpers/http.js +134 -0
- package/dist/generated-defs/helpers/http.js.map +1 -0
- package/dist/generated-defs/helpers/index.d.ts +4 -0
- package/dist/generated-defs/helpers/index.d.ts.map +1 -0
- package/dist/generated-defs/helpers/index.js +21 -0
- package/dist/generated-defs/helpers/index.js.map +1 -0
- package/dist/generated-defs/helpers/multipart.d.ts +4 -0
- package/dist/generated-defs/helpers/multipart.d.ts.map +1 -0
- package/dist/generated-defs/helpers/multipart.js +249 -0
- package/dist/generated-defs/helpers/multipart.js.map +1 -0
- package/dist/generated-defs/helpers/router.d.ts +4 -0
- package/dist/generated-defs/helpers/router.d.ts.map +1 -0
- package/dist/generated-defs/helpers/router.js +259 -0
- package/dist/generated-defs/helpers/router.js.map +1 -0
- package/dist/src/common/declaration.d.ts +13 -0
- package/dist/src/common/declaration.d.ts.map +1 -0
- package/dist/src/common/declaration.js +45 -0
- package/dist/src/common/declaration.js.map +1 -0
- package/dist/src/common/documentation.d.ts +12 -0
- package/dist/src/common/documentation.d.ts.map +1 -0
- package/dist/src/common/documentation.js +21 -0
- package/dist/src/common/documentation.js.map +1 -0
- package/dist/src/common/enum.d.ts +10 -0
- package/dist/src/common/enum.d.ts.map +1 -0
- package/dist/src/common/enum.js +21 -0
- package/dist/src/common/enum.js.map +1 -0
- package/dist/src/common/interface.d.ts +50 -0
- package/dist/src/common/interface.d.ts.map +1 -0
- package/dist/src/common/interface.js +194 -0
- package/dist/src/common/interface.js.map +1 -0
- package/dist/src/common/model.d.ts +26 -0
- package/dist/src/common/model.d.ts.map +1 -0
- package/dist/src/common/model.js +115 -0
- package/dist/src/common/model.js.map +1 -0
- package/dist/src/common/namespace.d.ts +38 -0
- package/dist/src/common/namespace.d.ts.map +1 -0
- package/dist/src/common/namespace.js +184 -0
- package/dist/src/common/namespace.js.map +1 -0
- package/dist/src/common/reference.d.ts +46 -0
- package/dist/src/common/reference.d.ts.map +1 -0
- package/dist/src/common/reference.js +243 -0
- package/dist/src/common/reference.js.map +1 -0
- package/dist/src/common/scalar.d.ts +50 -0
- package/dist/src/common/scalar.d.ts.map +1 -0
- package/dist/src/common/scalar.js +144 -0
- package/dist/src/common/scalar.js.map +1 -0
- package/dist/src/common/serialization/index.d.ts +11 -0
- package/dist/src/common/serialization/index.d.ts.map +1 -0
- package/dist/src/common/serialization/index.js +72 -0
- package/dist/src/common/serialization/index.js.map +1 -0
- package/dist/src/common/serialization/json.d.ts +6 -0
- package/dist/src/common/serialization/json.d.ts.map +1 -0
- package/dist/src/common/serialization/json.js +341 -0
- package/dist/src/common/serialization/json.js.map +1 -0
- package/dist/src/common/union.d.ts +23 -0
- package/dist/src/common/union.d.ts.map +1 -0
- package/dist/src/common/union.js +57 -0
- package/dist/src/common/union.js.map +1 -0
- package/dist/src/ctx.d.ts +242 -0
- package/dist/src/ctx.d.ts.map +1 -0
- package/dist/src/ctx.js +211 -0
- package/dist/src/ctx.js.map +1 -0
- package/dist/src/helpers/header.d.ts +14 -0
- package/dist/src/helpers/header.d.ts.map +1 -0
- package/dist/src/helpers/header.js +38 -0
- package/dist/src/helpers/header.js.map +1 -0
- package/dist/src/helpers/http.d.ts +70 -0
- package/dist/src/helpers/http.d.ts.map +1 -0
- package/dist/src/helpers/http.js +86 -0
- package/dist/src/helpers/http.js.map +1 -0
- package/dist/src/helpers/multipart.d.ts +26 -0
- package/dist/src/helpers/multipart.d.ts.map +1 -0
- package/dist/src/helpers/multipart.js +182 -0
- package/dist/src/helpers/multipart.js.map +1 -0
- package/dist/src/helpers/router.d.ts +176 -0
- package/dist/src/helpers/router.d.ts.map +1 -0
- package/dist/src/helpers/router.js +55 -0
- package/dist/src/helpers/router.js.map +1 -0
- package/dist/src/http/index.d.ts +24 -0
- package/dist/src/http/index.d.ts.map +1 -0
- package/dist/src/http/index.js +52 -0
- package/dist/src/http/index.js.map +1 -0
- package/dist/src/http/server/index.d.ts +11 -0
- package/dist/src/http/server/index.d.ts.map +1 -0
- package/dist/src/http/server/index.js +413 -0
- package/dist/src/http/server/index.js.map +1 -0
- package/dist/src/http/server/multipart.d.ts +16 -0
- package/dist/src/http/server/multipart.d.ts.map +1 -0
- package/dist/src/http/server/multipart.js +214 -0
- package/dist/src/http/server/multipart.js.map +1 -0
- package/dist/src/http/server/router.d.ts +15 -0
- package/dist/src/http/server/router.d.ts.map +1 -0
- package/dist/src/http/server/router.js +459 -0
- package/dist/src/http/server/router.js.map +1 -0
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +38 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/lib.d.ts +141 -0
- package/dist/src/lib.d.ts.map +1 -0
- package/dist/src/lib.js +116 -0
- package/dist/src/lib.js.map +1 -0
- package/dist/src/scripts/scaffold/bin.d.mts +14 -0
- package/dist/src/scripts/scaffold/bin.d.mts.map +1 -0
- package/dist/src/scripts/scaffold/bin.mjs +559 -0
- package/dist/src/scripts/scaffold/bin.mjs.map +1 -0
- package/dist/src/testing/index.d.ts +3 -0
- package/dist/src/testing/index.d.ts.map +1 -0
- package/dist/src/testing/index.js +6 -0
- package/dist/src/testing/index.js.map +1 -0
- package/dist/src/util/case.d.ts +81 -0
- package/dist/src/util/case.d.ts.map +1 -0
- package/dist/src/util/case.js +111 -0
- package/dist/src/util/case.js.map +1 -0
- package/dist/src/util/differentiate.d.ts +251 -0
- package/dist/src/util/differentiate.d.ts.map +1 -0
- package/dist/src/util/differentiate.js +580 -0
- package/dist/src/util/differentiate.js.map +1 -0
- package/dist/src/util/error.d.ts +13 -0
- package/dist/src/util/error.d.ts.map +1 -0
- package/dist/src/util/error.js +25 -0
- package/dist/src/util/error.js.map +1 -0
- package/dist/src/util/extends.d.ts +10 -0
- package/dist/src/util/extends.d.ts.map +1 -0
- package/dist/src/util/extends.js +31 -0
- package/dist/src/util/extends.js.map +1 -0
- package/dist/src/util/iter.d.ts +39 -0
- package/dist/src/util/iter.d.ts.map +1 -0
- package/dist/src/util/iter.js +72 -0
- package/dist/src/util/iter.js.map +1 -0
- package/dist/src/util/keywords.d.ts +10 -0
- package/dist/src/util/keywords.d.ts.map +1 -0
- package/dist/src/util/keywords.js +85 -0
- package/dist/src/util/keywords.js.map +1 -0
- package/dist/src/util/name.d.ts +12 -0
- package/dist/src/util/name.d.ts.map +1 -0
- package/dist/src/util/name.js +26 -0
- package/dist/src/util/name.js.map +1 -0
- package/dist/src/util/once-queue.d.ts +24 -0
- package/dist/src/util/once-queue.d.ts.map +1 -0
- package/dist/src/util/once-queue.js +34 -0
- package/dist/src/util/once-queue.js.map +1 -0
- package/dist/src/util/openapi3.d.ts +23 -0
- package/dist/src/util/openapi3.d.ts.map +1 -0
- package/dist/src/util/openapi3.js +40 -0
- package/dist/src/util/openapi3.js.map +1 -0
- package/dist/src/util/pluralism.d.ts +23 -0
- package/dist/src/util/pluralism.d.ts.map +1 -0
- package/dist/src/util/pluralism.js +36 -0
- package/dist/src/util/pluralism.js.map +1 -0
- package/dist/src/util/scope.d.ts +85 -0
- package/dist/src/util/scope.d.ts.map +1 -0
- package/dist/src/util/scope.js +111 -0
- package/dist/src/util/scope.js.map +1 -0
- package/dist/src/write.d.ts +23 -0
- package/dist/src/write.d.ts.map +1 -0
- package/dist/src/write.js +62 -0
- package/dist/src/write.js.map +1 -0
- package/generated-defs/helpers/header.ts +83 -0
- package/generated-defs/helpers/http.ts +141 -0
- package/generated-defs/helpers/index.ts +27 -0
- package/generated-defs/helpers/multipart.ts +256 -0
- package/generated-defs/helpers/router.ts +266 -0
- package/package.json +71 -0
- package/src/common/declaration.ts +52 -0
- package/src/common/documentation.ts +26 -0
- package/src/common/enum.ts +28 -0
- package/src/common/interface.ts +264 -0
- package/src/common/model.ts +160 -0
- package/src/common/namespace.ts +243 -0
- package/src/common/reference.ts +319 -0
- package/src/common/scalar.ts +173 -0
- package/src/common/serialization/index.ts +124 -0
- package/src/common/serialization/json.ts +444 -0
- package/src/common/union.ts +76 -0
- package/src/ctx.ts +497 -0
- package/src/helpers/header.ts +55 -0
- package/src/helpers/http.ts +113 -0
- package/src/helpers/multipart.ts +228 -0
- package/src/helpers/router.ts +238 -0
- package/src/http/index.ts +81 -0
- package/src/http/server/index.ts +548 -0
- package/src/http/server/multipart.ts +272 -0
- package/src/http/server/router.ts +686 -0
- package/src/index.ts +56 -0
- package/src/lib.ts +130 -0
- package/src/scripts/scaffold/bin.mts +781 -0
- package/src/testing/index.ts +10 -0
- package/src/util/case.ts +182 -0
- package/src/util/differentiate.ts +957 -0
- package/src/util/error.ts +28 -0
- package/src/util/extends.ts +43 -0
- package/src/util/iter.ts +85 -0
- package/src/util/keywords.ts +90 -0
- package/src/util/name.ts +33 -0
- package/src/util/once-queue.ts +55 -0
- package/src/util/openapi3.ts +53 -0
- package/src/util/pluralism.ts +37 -0
- package/src/util/scope.ts +211 -0
- package/src/write.ts +88 -0
- package/temp/tsconfig.tsbuildinfo +1 -0
- package/test/header.test.ts +26 -0
- package/test/multipart.test.ts +169 -0
- package/tsconfig.json +10 -0
- package/vitest.config.ts +4 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { HttpOperation, HttpOperationMultipartBody, isHttpFile } from "@typespec/http";
|
|
2
|
+
import { Module } from "../../ctx.js";
|
|
3
|
+
import { HttpContext } from "../index.js";
|
|
4
|
+
|
|
5
|
+
import { module as headerHelpers } from "../../../generated-defs/helpers/header.js";
|
|
6
|
+
import { module as multipartHelpers } from "../../../generated-defs/helpers/multipart.js";
|
|
7
|
+
import { emitTypeReference } from "../../common/reference.js";
|
|
8
|
+
import { requireSerialization } from "../../common/serialization/index.js";
|
|
9
|
+
import { requiresJsonSerialization } from "../../common/serialization/json.js";
|
|
10
|
+
import { parseCase } from "../../util/case.js";
|
|
11
|
+
import { UnimplementedError } from "../../util/error.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Parse a multipart request body according to the given body spec.
|
|
15
|
+
*
|
|
16
|
+
* @param ctx - The emitter context.
|
|
17
|
+
* @param module - The module that this parser is written into.
|
|
18
|
+
* @param operation - The HTTP operation this body is being parsed for.
|
|
19
|
+
* @param body - The multipart body spec
|
|
20
|
+
* @param bodyName - The name of the variable to store the parsed body in.
|
|
21
|
+
* @param bodyTypeName - The name of the type of the parsed body.
|
|
22
|
+
*/
|
|
23
|
+
export function* emitMultipart(
|
|
24
|
+
ctx: HttpContext,
|
|
25
|
+
module: Module,
|
|
26
|
+
operation: HttpOperation,
|
|
27
|
+
body: HttpOperationMultipartBody,
|
|
28
|
+
ctxName: string,
|
|
29
|
+
bodyName: string,
|
|
30
|
+
bodyTypeName: string,
|
|
31
|
+
): Iterable<string> {
|
|
32
|
+
module.imports.push(
|
|
33
|
+
{ binder: ["parseHeaderValueParameters"], from: headerHelpers },
|
|
34
|
+
{ binder: ["createMultipartReadable"], from: multipartHelpers },
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
yield `const ${bodyName} = await new Promise<${bodyTypeName}>(`;
|
|
38
|
+
yield `// eslint-disable-next-line no-async-promise-executor`;
|
|
39
|
+
yield `async function parse${bodyTypeName}MultipartRequest(resolve, reject) {`;
|
|
40
|
+
|
|
41
|
+
// Wrap this whole thing in a try/catch because the executor is async. If anything in here throws, we want to reject the promise instead of
|
|
42
|
+
// just letting the executor die and the promise never settle.
|
|
43
|
+
yield ` try {`;
|
|
44
|
+
|
|
45
|
+
const stream = ctx.gensym("stream");
|
|
46
|
+
|
|
47
|
+
yield ` const ${stream} = createMultipartReadable(${ctxName}.request);`;
|
|
48
|
+
yield "";
|
|
49
|
+
|
|
50
|
+
const contentDisposition = ctx.gensym("contentDisposition");
|
|
51
|
+
const contentType = ctx.gensym("contentType");
|
|
52
|
+
const name = ctx.gensym("name");
|
|
53
|
+
const fields = ctx.gensym("fields");
|
|
54
|
+
|
|
55
|
+
yield ` const ${fields}: { [k: string]: any } = {};`;
|
|
56
|
+
yield "";
|
|
57
|
+
|
|
58
|
+
const partsWithMulti = body.parts.filter((part) => part.name && part.multi);
|
|
59
|
+
const anonymousParts = body.parts.filter((part) => !part.name);
|
|
60
|
+
const anonymousPartsAreMulti = anonymousParts.some((part) => part.multi);
|
|
61
|
+
|
|
62
|
+
if (anonymousParts.length > 0) {
|
|
63
|
+
throw new UnimplementedError("Anonymous parts are not yet supported in multipart parsing.");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let hadMulti = false;
|
|
67
|
+
|
|
68
|
+
for (const partWithMulti of partsWithMulti) {
|
|
69
|
+
if (!partWithMulti.optional) {
|
|
70
|
+
hadMulti = true;
|
|
71
|
+
const name = partWithMulti.name!;
|
|
72
|
+
|
|
73
|
+
const propName = parseCase(name).camelCase;
|
|
74
|
+
|
|
75
|
+
yield ` ${fields}.${propName} = [];`;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (anonymousPartsAreMulti) {
|
|
80
|
+
hadMulti = true;
|
|
81
|
+
yield ` const ${fields}.__anonymous = [];`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (hadMulti) yield "";
|
|
85
|
+
|
|
86
|
+
const partName = ctx.gensym("part");
|
|
87
|
+
|
|
88
|
+
yield ` for await (const ${partName} of ${stream}) {`;
|
|
89
|
+
yield ` const ${contentDisposition} = parseHeaderValueParameters(${partName}.headers["content-disposition"]);`;
|
|
90
|
+
yield ` if (!${contentDisposition}) {`;
|
|
91
|
+
yield ` return reject("Invalid request: missing content-disposition in part.");`;
|
|
92
|
+
yield ` }`;
|
|
93
|
+
yield "";
|
|
94
|
+
yield ` const ${contentType} = parseHeaderValueParameters(${partName}.headers["content-type"]);`;
|
|
95
|
+
yield "";
|
|
96
|
+
yield ` const ${name} = ${contentDisposition}.params.name ?? "";`;
|
|
97
|
+
yield "";
|
|
98
|
+
yield ` switch (${name}) {`;
|
|
99
|
+
|
|
100
|
+
for (const namedPart of body.parts.filter((part) => part.name)) {
|
|
101
|
+
// TODO: this is wrong. The name of the part is not necessarily the name of the property in the body.
|
|
102
|
+
// The HTTP library does not provide the property that maps to this part if it's explicitly named.
|
|
103
|
+
const propName = parseCase(namedPart.name!).camelCase;
|
|
104
|
+
|
|
105
|
+
let value = ctx.gensym("value");
|
|
106
|
+
|
|
107
|
+
yield ` case ${JSON.stringify(namedPart.name)}: {`;
|
|
108
|
+
// HTTP API is doing too much work for us. I need to know whether I'm looking at an HTTP file, and the only way to do that is to
|
|
109
|
+
// look at the model that the body is a property of. This is more than a bit of a hack, but it will work for now.
|
|
110
|
+
if (
|
|
111
|
+
namedPart.body.contentTypeProperty?.model &&
|
|
112
|
+
isHttpFile(ctx.program, namedPart.body.contentTypeProperty.model)
|
|
113
|
+
) {
|
|
114
|
+
// We have an http file, so we will buffer the body and then optionally get the filename and content type.
|
|
115
|
+
// TODO: support models that inherit from File and have other optional metadata. The Http.File structure
|
|
116
|
+
// doesn't make this easy to do, since it doesn't describe where the fields of the file come from in the
|
|
117
|
+
// multipart request. However, we could recognize models that extend File and handle the special fields
|
|
118
|
+
// of Http.File specially.
|
|
119
|
+
// TODO: find a way to avoid buffering the entire file in memory. I have to do this to return an object that
|
|
120
|
+
// has the keys described in the TypeSpec model and because the underlying multipart stream has to be
|
|
121
|
+
// drained sequentially. Server authors could stall the stream by trying to read part bodies out of order if
|
|
122
|
+
// I represented the file contents as a stream. We will need some way to identify the whole multipart
|
|
123
|
+
// envelope and represent it as a stream of named parts. The backend for multipart streaming supports this,
|
|
124
|
+
// and it's how we receive the part data in this handler, but we don't have a way to represent it to the
|
|
125
|
+
// implementor yet.
|
|
126
|
+
|
|
127
|
+
yield ` const __chunks = [];`;
|
|
128
|
+
yield "";
|
|
129
|
+
yield ` for await (const __chunk of ${partName}.body) {`;
|
|
130
|
+
yield ` __chunks.push(__chunk);`;
|
|
131
|
+
yield ` }`;
|
|
132
|
+
yield "";
|
|
133
|
+
|
|
134
|
+
yield ` const ${value}: { filename?: string; contentType?: string; contents: Buffer; } = { contents: Buffer.concat(__chunks) };`;
|
|
135
|
+
yield "";
|
|
136
|
+
|
|
137
|
+
yield ` if (${contentType}) {`;
|
|
138
|
+
yield ` ${value}.contentType = ${contentType}.verbatim;`;
|
|
139
|
+
yield ` }`;
|
|
140
|
+
yield "";
|
|
141
|
+
|
|
142
|
+
yield ` const __filename = ${contentDisposition}.params.filename;`;
|
|
143
|
+
yield ` if (__filename) {`;
|
|
144
|
+
yield ` ${value}.filename = __filename;`;
|
|
145
|
+
yield ` }`;
|
|
146
|
+
} else {
|
|
147
|
+
// Not a file. We just use the given content-type to determine how to parse the body.
|
|
148
|
+
|
|
149
|
+
yield ` if (${contentType}?.value && ${contentType}.value !== "application/json") {`;
|
|
150
|
+
yield ` throw new Error("Unsupported content-type for part: " + ${contentType}.value);`;
|
|
151
|
+
yield ` }`;
|
|
152
|
+
yield "";
|
|
153
|
+
|
|
154
|
+
if (namedPart.headers.length > 0) {
|
|
155
|
+
// TODO: support reconstruction of mixed objects with headers and bodies.
|
|
156
|
+
throw new UnimplementedError(
|
|
157
|
+
"Named parts with headers are not yet supported in multipart parsing.",
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
yield ` const __chunks = [];`;
|
|
162
|
+
yield "";
|
|
163
|
+
yield ` for await (const __chunk of ${partName}.body) {`;
|
|
164
|
+
yield ` __chunks.push(__chunk);`;
|
|
165
|
+
yield ` }`;
|
|
166
|
+
|
|
167
|
+
yield ` const __object = JSON.parse(Buffer.concat(__chunks).toString("utf-8"));`;
|
|
168
|
+
yield "";
|
|
169
|
+
|
|
170
|
+
if (requiresJsonSerialization(ctx, namedPart.body.type)) {
|
|
171
|
+
const bodyTypeReference = emitTypeReference(
|
|
172
|
+
ctx,
|
|
173
|
+
namedPart.body.type,
|
|
174
|
+
namedPart.body.property ?? namedPart.body.type,
|
|
175
|
+
module,
|
|
176
|
+
{ altName: bodyTypeName + "Body", requireDeclaration: true },
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
requireSerialization(ctx, namedPart.body.type, "application/json");
|
|
180
|
+
|
|
181
|
+
value = `${bodyTypeReference}.fromJsonObject(__object)`;
|
|
182
|
+
} else {
|
|
183
|
+
value = "__object";
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (namedPart.multi) {
|
|
187
|
+
if (namedPart.optional) {
|
|
188
|
+
yield ` (${fields}.${propName} ??= []).push(${value});`;
|
|
189
|
+
} else {
|
|
190
|
+
yield ` ${fields}.${propName}.push(${value});`;
|
|
191
|
+
}
|
|
192
|
+
} else {
|
|
193
|
+
yield ` ${fields}.${propName} = ${value};`;
|
|
194
|
+
}
|
|
195
|
+
yield ` break;`;
|
|
196
|
+
yield ` }`;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (anonymousParts.length > 0) {
|
|
200
|
+
yield ` "": {`;
|
|
201
|
+
if (anonymousPartsAreMulti) {
|
|
202
|
+
yield ` ${fields}.__anonymous.push({`;
|
|
203
|
+
yield ` headers: ${partName}.headers,`;
|
|
204
|
+
yield ` body: ${partName}.body,`;
|
|
205
|
+
yield ` });`;
|
|
206
|
+
yield ` break;`;
|
|
207
|
+
} else {
|
|
208
|
+
yield ` ${fields}.__anonymous = {}`;
|
|
209
|
+
yield ` break;`;
|
|
210
|
+
}
|
|
211
|
+
yield ` }`;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
yield ` default: {`;
|
|
215
|
+
yield ` reject("Invalid request: unknown part name.");`;
|
|
216
|
+
yield ` return;`;
|
|
217
|
+
yield ` }`;
|
|
218
|
+
yield ` }`;
|
|
219
|
+
yield ` }`;
|
|
220
|
+
yield "";
|
|
221
|
+
|
|
222
|
+
yield ` resolve(${fields} as ${bodyTypeName});`;
|
|
223
|
+
|
|
224
|
+
yield ` } catch (err) { reject(err); }`;
|
|
225
|
+
|
|
226
|
+
yield "});";
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// This function is old and broken. I'm not likely to fix it unless we decide to continue supporting legacy multipart
|
|
230
|
+
// parsing after 1.0.
|
|
231
|
+
export function* emitMultipartLegacy(
|
|
232
|
+
ctxName: string,
|
|
233
|
+
bodyName: string,
|
|
234
|
+
bodyTypeName: string,
|
|
235
|
+
): Iterable<string> {
|
|
236
|
+
yield `const ${bodyName} = await new Promise(function parse${bodyTypeName}MultipartRequest(resolve, reject) {`;
|
|
237
|
+
yield ` const boundary = ${ctxName}.request.headers["content-type"]?.split(";").find((s) => s.includes("boundary="))?.split("=", 2)[1];`;
|
|
238
|
+
yield ` if (!boundary) {`;
|
|
239
|
+
yield ` return reject("Invalid request: missing boundary in content-type.");`;
|
|
240
|
+
yield ` }`;
|
|
241
|
+
yield "";
|
|
242
|
+
yield ` const chunks: Array<Buffer> = [];`;
|
|
243
|
+
yield ` ${ctxName}.request.on("data", function appendChunk(chunk) { chunks.push(chunk); });`;
|
|
244
|
+
yield ` ${ctxName}.request.on("end", function finalize() {`;
|
|
245
|
+
yield ` const text = Buffer.concat(chunks).toString();`;
|
|
246
|
+
yield ` const parts = text.split(boundary).slice(1, -1);`;
|
|
247
|
+
yield ` const fields: { [k: string]: any } = {};`;
|
|
248
|
+
yield "";
|
|
249
|
+
yield ` for (const part of parts) {`;
|
|
250
|
+
yield ` const [headerText, body] = part.split("\\r\\n\\r\\n", 2);`;
|
|
251
|
+
yield " const headers = Object.fromEntries(";
|
|
252
|
+
yield ` headerText.split("\\r\\n").map((line) => line.split(": ", 2))`;
|
|
253
|
+
yield " ) as { [k: string]: string };";
|
|
254
|
+
yield ` const name = headers["Content-Disposition"].split("name=\\"")[1].split("\\"")[0];`;
|
|
255
|
+
yield ` const contentType = headers["Content-Type"] ?? "text/plain";`;
|
|
256
|
+
yield "";
|
|
257
|
+
yield ` switch (contentType) {`;
|
|
258
|
+
yield ` case "application/json":`;
|
|
259
|
+
yield ` fields[name] = JSON.parse(body);`;
|
|
260
|
+
yield ` break;`;
|
|
261
|
+
yield ` case "application/octet-stream":`;
|
|
262
|
+
yield ` fields[name] = Buffer.from(body, "utf-8");`;
|
|
263
|
+
yield ` break;`;
|
|
264
|
+
yield ` default:`;
|
|
265
|
+
yield ` fields[name] = body;`;
|
|
266
|
+
yield ` }`;
|
|
267
|
+
yield ` }`;
|
|
268
|
+
yield "";
|
|
269
|
+
yield ` resolve(fields as ${bodyTypeName});`;
|
|
270
|
+
yield ` });`;
|
|
271
|
+
yield `}) as ${bodyTypeName};`;
|
|
272
|
+
}
|