@c9up/ream 0.1.10 → 0.1.12
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/dist/testing/RequestBuilder.d.ts +21 -0
- package/dist/testing/RequestBuilder.d.ts.map +1 -1
- package/dist/testing/RequestBuilder.js +57 -0
- package/dist/testing/RequestBuilder.js.map +1 -1
- package/events.darwin-arm64.node +0 -0
- package/events.darwin-x64.node +0 -0
- package/events.linux-arm64-gnu.node +0 -0
- package/events.linux-x64-gnu.node +0 -0
- package/events.win32-x64-msvc.node +0 -0
- package/index.darwin-arm64.node +0 -0
- package/index.darwin-x64.node +0 -0
- package/index.linux-arm64-gnu.node +0 -0
- package/index.linux-x64-gnu.node +0 -0
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
- package/scheduler.darwin-arm64.node +0 -0
- package/scheduler.darwin-x64.node +0 -0
- package/scheduler.linux-arm64-gnu.node +0 -0
- package/scheduler.linux-x64-gnu.node +0 -0
- package/scheduler.win32-x64-msvc.node +0 -0
- package/src/testing/RequestBuilder.ts +86 -0
|
@@ -33,6 +33,13 @@ export type HttpSender = (method: HttpMethod, path: string, init: {
|
|
|
33
33
|
headers: Record<string, string>;
|
|
34
34
|
body: Buffer;
|
|
35
35
|
}) => Promise<TestResponse>;
|
|
36
|
+
/** Options for a multipart file part — mirrors Adonis/japa's `.file()` options. */
|
|
37
|
+
export interface FilePart {
|
|
38
|
+
/** Filename advertised in the part's `Content-Disposition`. Defaults to the field name. */
|
|
39
|
+
filename?: string;
|
|
40
|
+
/** MIME type of the part. Defaults to `application/octet-stream`. */
|
|
41
|
+
contentType?: string;
|
|
42
|
+
}
|
|
36
43
|
export declare class RequestBuilder {
|
|
37
44
|
#private;
|
|
38
45
|
constructor(sender: HttpSender, method: HttpMethod, path: string, authStrategy?: AuthStrategy | null);
|
|
@@ -41,6 +48,20 @@ export declare class RequestBuilder {
|
|
|
41
48
|
json(data: unknown): this;
|
|
42
49
|
body(data: Buffer | string, contentType?: string): this;
|
|
43
50
|
form(data: Record<string, string>): this;
|
|
51
|
+
/**
|
|
52
|
+
* Add a `multipart/form-data` text field — mirrors Adonis/japa's `.field()`.
|
|
53
|
+
* Calling `.field()` or `.file()` switches the body to multipart (use
|
|
54
|
+
* `.form()` for `application/x-www-form-urlencoded`). The boundary +
|
|
55
|
+
* `Content-Type` header are produced at send time.
|
|
56
|
+
*/
|
|
57
|
+
field(name: string, value: string): this;
|
|
58
|
+
/**
|
|
59
|
+
* Attach a file as a `multipart/form-data` part — mirrors Adonis/japa's
|
|
60
|
+
* `.file(field, contents, { filename, contentType })`. `contents` is a
|
|
61
|
+
* Buffer (binary) or a string (encoded as utf8). Server-side
|
|
62
|
+
* `request.file(field, …)` reads the resulting part.
|
|
63
|
+
*/
|
|
64
|
+
file(field: string, contents: Buffer | string, options?: FilePart): this;
|
|
44
65
|
cookies(map: Record<string, string>): this;
|
|
45
66
|
cookie(name: string, value: string): this;
|
|
46
67
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RequestBuilder.d.ts","sourceRoot":"","sources":["../../src/testing/RequestBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAEnD;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,WAAW;IAC1B,qFAAqF;IACrF,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;IACnB,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACtC;AAED,MAAM,WAAW,YAAY;IAC3B,8FAA8F;IAC9F,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAC1F,oDAAoD;IACpD,UAAU,CAAC,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CAC5F;AAUD,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAA;AAEpE;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,CACvB,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;IACJ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B,IAAI,EAAE,MAAM,CAAA;CACb,KACE,OAAO,CAAC,YAAY,CAAC,CAAA;AAE1B,qBAAa,cAAc;;
|
|
1
|
+
{"version":3,"file":"RequestBuilder.d.ts","sourceRoot":"","sources":["../../src/testing/RequestBuilder.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,6 +11,7 @@ export class RequestBuilder {
|
|
|
11
11
|
#path;
|
|
12
12
|
#headers = {};
|
|
13
13
|
#body = Buffer.alloc(0);
|
|
14
|
+
#multipart = [];
|
|
14
15
|
#cookies = {};
|
|
15
16
|
#authStrategy;
|
|
16
17
|
#pendingAuth = null;
|
|
@@ -48,6 +49,32 @@ export class RequestBuilder {
|
|
|
48
49
|
this.#headers['content-type'] = 'application/x-www-form-urlencoded';
|
|
49
50
|
return this;
|
|
50
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Add a `multipart/form-data` text field — mirrors Adonis/japa's `.field()`.
|
|
54
|
+
* Calling `.field()` or `.file()` switches the body to multipart (use
|
|
55
|
+
* `.form()` for `application/x-www-form-urlencoded`). The boundary +
|
|
56
|
+
* `Content-Type` header are produced at send time.
|
|
57
|
+
*/
|
|
58
|
+
field(name, value) {
|
|
59
|
+
this.#multipart.push({ kind: 'field', name, value });
|
|
60
|
+
return this;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Attach a file as a `multipart/form-data` part — mirrors Adonis/japa's
|
|
64
|
+
* `.file(field, contents, { filename, contentType })`. `contents` is a
|
|
65
|
+
* Buffer (binary) or a string (encoded as utf8). Server-side
|
|
66
|
+
* `request.file(field, …)` reads the resulting part.
|
|
67
|
+
*/
|
|
68
|
+
file(field, contents, options = {}) {
|
|
69
|
+
this.#multipart.push({
|
|
70
|
+
kind: 'file',
|
|
71
|
+
name: field,
|
|
72
|
+
filename: options.filename ?? field,
|
|
73
|
+
contentType: options.contentType ?? 'application/octet-stream',
|
|
74
|
+
content: typeof contents === 'string' ? Buffer.from(contents, 'utf8') : contents,
|
|
75
|
+
});
|
|
76
|
+
return this;
|
|
77
|
+
}
|
|
51
78
|
cookies(map) {
|
|
52
79
|
Object.assign(this.#cookies, map);
|
|
53
80
|
return this;
|
|
@@ -157,6 +184,13 @@ export class RequestBuilder {
|
|
|
157
184
|
}
|
|
158
185
|
}
|
|
159
186
|
}
|
|
187
|
+
// Multipart parts (.field()/.file()) take precedence over any body set by
|
|
188
|
+
// json()/form()/body(); encode them with a fresh boundary at send time.
|
|
189
|
+
if (this.#multipart.length > 0) {
|
|
190
|
+
const boundary = `----ReamRequestBuilder${crypto.randomUUID().replace(/-/g, '')}`;
|
|
191
|
+
this.#body = encodeMultipart(this.#multipart, boundary);
|
|
192
|
+
this.#headers['content-type'] = `multipart/form-data; boundary=${boundary}`;
|
|
193
|
+
}
|
|
160
194
|
// Serialise cookies into a single `Cookie:` header.
|
|
161
195
|
const cookieEntries = Object.entries(this.#cookies);
|
|
162
196
|
if (cookieEntries.length > 0) {
|
|
@@ -171,6 +205,29 @@ export class RequestBuilder {
|
|
|
171
205
|
function capBody(s, max = 512) {
|
|
172
206
|
return s.length <= max ? s : `${s.slice(0, max)}...[truncated]`;
|
|
173
207
|
}
|
|
208
|
+
/** Strip CR/LF and escape quotes so a name/filename can't break the headers. */
|
|
209
|
+
function sanitizeHeaderParam(value) {
|
|
210
|
+
return value.replace(/[\r\n]/g, '').replace(/"/g, '%22');
|
|
211
|
+
}
|
|
212
|
+
/** Encode multipart/form-data parts (RFC 7578) into a single body Buffer. */
|
|
213
|
+
function encodeMultipart(parts, boundary) {
|
|
214
|
+
const CRLF = '\r\n';
|
|
215
|
+
const chunks = [];
|
|
216
|
+
for (const part of parts) {
|
|
217
|
+
if (part.kind === 'field') {
|
|
218
|
+
chunks.push(Buffer.from(`--${boundary}${CRLF}` +
|
|
219
|
+
`Content-Disposition: form-data; name="${sanitizeHeaderParam(part.name)}"${CRLF}${CRLF}` +
|
|
220
|
+
`${part.value}${CRLF}`, 'utf8'));
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
chunks.push(Buffer.from(`--${boundary}${CRLF}` +
|
|
224
|
+
`Content-Disposition: form-data; name="${sanitizeHeaderParam(part.name)}"; filename="${sanitizeHeaderParam(part.filename)}"${CRLF}` +
|
|
225
|
+
`Content-Type: ${part.contentType}${CRLF}${CRLF}`, 'utf8'), part.content, Buffer.from(CRLF, 'utf8'));
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
chunks.push(Buffer.from(`--${boundary}--${CRLF}`, 'utf8'));
|
|
229
|
+
return Buffer.concat(chunks);
|
|
230
|
+
}
|
|
174
231
|
/** Narrowing guard — a non-null, non-array object usable as a string map. */
|
|
175
232
|
function isRecord(x) {
|
|
176
233
|
return typeof x === 'object' && x !== null && !Array.isArray(x);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RequestBuilder.js","sourceRoot":"","sources":["../../src/testing/RequestBuilder.ts"],"names":[],"mappings":"AA8BA,wEAAwE;AACxE,MAAM,gBAAiB,SAAQ,KAAK;IAClC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAA;IAChC,CAAC;CACF;
|
|
1
|
+
{"version":3,"file":"RequestBuilder.js","sourceRoot":"","sources":["../../src/testing/RequestBuilder.ts"],"names":[],"mappings":"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"}
|
package/events.darwin-arm64.node
CHANGED
|
Binary file
|
package/events.darwin-x64.node
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/index.darwin-arm64.node
CHANGED
|
Binary file
|
package/index.darwin-x64.node
CHANGED
|
Binary file
|
|
Binary file
|
package/index.linux-x64-gnu.node
CHANGED
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -51,12 +51,26 @@ export type HttpSender = (
|
|
|
51
51
|
},
|
|
52
52
|
) => Promise<TestResponse>
|
|
53
53
|
|
|
54
|
+
/** Options for a multipart file part — mirrors Adonis/japa's `.file()` options. */
|
|
55
|
+
export interface FilePart {
|
|
56
|
+
/** Filename advertised in the part's `Content-Disposition`. Defaults to the field name. */
|
|
57
|
+
filename?: string
|
|
58
|
+
/** MIME type of the part. Defaults to `application/octet-stream`. */
|
|
59
|
+
contentType?: string
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** One encoded multipart/form-data part — a text field or an uploaded file. */
|
|
63
|
+
type MultipartPart =
|
|
64
|
+
| { kind: 'field'; name: string; value: string }
|
|
65
|
+
| { kind: 'file'; name: string; filename: string; contentType: string; content: Buffer }
|
|
66
|
+
|
|
54
67
|
export class RequestBuilder {
|
|
55
68
|
#sender: HttpSender
|
|
56
69
|
#method: HttpMethod
|
|
57
70
|
#path: string
|
|
58
71
|
#headers: Record<string, string> = {}
|
|
59
72
|
#body: Buffer = Buffer.alloc(0)
|
|
73
|
+
#multipart: MultipartPart[] = []
|
|
60
74
|
#cookies: Record<string, string> = {}
|
|
61
75
|
#authStrategy: AuthStrategy | null
|
|
62
76
|
#pendingAuth: AuthSubject | null = null
|
|
@@ -107,6 +121,34 @@ export class RequestBuilder {
|
|
|
107
121
|
return this
|
|
108
122
|
}
|
|
109
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Add a `multipart/form-data` text field — mirrors Adonis/japa's `.field()`.
|
|
126
|
+
* Calling `.field()` or `.file()` switches the body to multipart (use
|
|
127
|
+
* `.form()` for `application/x-www-form-urlencoded`). The boundary +
|
|
128
|
+
* `Content-Type` header are produced at send time.
|
|
129
|
+
*/
|
|
130
|
+
field(name: string, value: string): this {
|
|
131
|
+
this.#multipart.push({ kind: 'field', name, value })
|
|
132
|
+
return this
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Attach a file as a `multipart/form-data` part — mirrors Adonis/japa's
|
|
137
|
+
* `.file(field, contents, { filename, contentType })`. `contents` is a
|
|
138
|
+
* Buffer (binary) or a string (encoded as utf8). Server-side
|
|
139
|
+
* `request.file(field, …)` reads the resulting part.
|
|
140
|
+
*/
|
|
141
|
+
file(field: string, contents: Buffer | string, options: FilePart = {}): this {
|
|
142
|
+
this.#multipart.push({
|
|
143
|
+
kind: 'file',
|
|
144
|
+
name: field,
|
|
145
|
+
filename: options.filename ?? field,
|
|
146
|
+
contentType: options.contentType ?? 'application/octet-stream',
|
|
147
|
+
content: typeof contents === 'string' ? Buffer.from(contents, 'utf8') : contents,
|
|
148
|
+
})
|
|
149
|
+
return this
|
|
150
|
+
}
|
|
151
|
+
|
|
110
152
|
cookies(map: Record<string, string>): this {
|
|
111
153
|
Object.assign(this.#cookies, map)
|
|
112
154
|
return this
|
|
@@ -231,6 +273,14 @@ export class RequestBuilder {
|
|
|
231
273
|
}
|
|
232
274
|
}
|
|
233
275
|
|
|
276
|
+
// Multipart parts (.field()/.file()) take precedence over any body set by
|
|
277
|
+
// json()/form()/body(); encode them with a fresh boundary at send time.
|
|
278
|
+
if (this.#multipart.length > 0) {
|
|
279
|
+
const boundary = `----ReamRequestBuilder${crypto.randomUUID().replace(/-/g, '')}`
|
|
280
|
+
this.#body = encodeMultipart(this.#multipart, boundary)
|
|
281
|
+
this.#headers['content-type'] = `multipart/form-data; boundary=${boundary}`
|
|
282
|
+
}
|
|
283
|
+
|
|
234
284
|
// Serialise cookies into a single `Cookie:` header.
|
|
235
285
|
const cookieEntries = Object.entries(this.#cookies)
|
|
236
286
|
if (cookieEntries.length > 0) {
|
|
@@ -248,6 +298,42 @@ function capBody(s: string, max = 512): string {
|
|
|
248
298
|
return s.length <= max ? s : `${s.slice(0, max)}...[truncated]`
|
|
249
299
|
}
|
|
250
300
|
|
|
301
|
+
/** Strip CR/LF and escape quotes so a name/filename can't break the headers. */
|
|
302
|
+
function sanitizeHeaderParam(value: string): string {
|
|
303
|
+
return value.replace(/[\r\n]/g, '').replace(/"/g, '%22')
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/** Encode multipart/form-data parts (RFC 7578) into a single body Buffer. */
|
|
307
|
+
function encodeMultipart(parts: MultipartPart[], boundary: string): Buffer {
|
|
308
|
+
const CRLF = '\r\n'
|
|
309
|
+
const chunks: Buffer[] = []
|
|
310
|
+
for (const part of parts) {
|
|
311
|
+
if (part.kind === 'field') {
|
|
312
|
+
chunks.push(
|
|
313
|
+
Buffer.from(
|
|
314
|
+
`--${boundary}${CRLF}` +
|
|
315
|
+
`Content-Disposition: form-data; name="${sanitizeHeaderParam(part.name)}"${CRLF}${CRLF}` +
|
|
316
|
+
`${part.value}${CRLF}`,
|
|
317
|
+
'utf8',
|
|
318
|
+
),
|
|
319
|
+
)
|
|
320
|
+
} else {
|
|
321
|
+
chunks.push(
|
|
322
|
+
Buffer.from(
|
|
323
|
+
`--${boundary}${CRLF}` +
|
|
324
|
+
`Content-Disposition: form-data; name="${sanitizeHeaderParam(part.name)}"; filename="${sanitizeHeaderParam(part.filename)}"${CRLF}` +
|
|
325
|
+
`Content-Type: ${part.contentType}${CRLF}${CRLF}`,
|
|
326
|
+
'utf8',
|
|
327
|
+
),
|
|
328
|
+
part.content,
|
|
329
|
+
Buffer.from(CRLF, 'utf8'),
|
|
330
|
+
)
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
chunks.push(Buffer.from(`--${boundary}--${CRLF}`, 'utf8'))
|
|
334
|
+
return Buffer.concat(chunks)
|
|
335
|
+
}
|
|
336
|
+
|
|
251
337
|
/** Narrowing guard — a non-null, non-array object usable as a string map. */
|
|
252
338
|
function isRecord(x: unknown): x is Record<string, unknown> {
|
|
253
339
|
return typeof x === 'object' && x !== null && !Array.isArray(x)
|