Skip to content

Commit 89a51c8

Browse files
authored
added build for quick testing
1 parent 2a7be5e commit 89a51c8

File tree

4 files changed

+1276
-0
lines changed

4 files changed

+1276
-0
lines changed

lib/index.cjs

Lines changed: 375 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,375 @@
1+
"use strict";
2+
var __defProp = Object.defineProperty;
3+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4+
var __getOwnPropNames = Object.getOwnPropertyNames;
5+
var __hasOwnProp = Object.prototype.hasOwnProperty;
6+
var __typeError = (msg) => {
7+
throw TypeError(msg);
8+
};
9+
var __export = (target, all) => {
10+
for (var name in all)
11+
__defProp(target, name, { get: all[name], enumerable: true });
12+
};
13+
var __copyProps = (to, from, except, desc) => {
14+
if (from && typeof from === "object" || typeof from === "function") {
15+
for (let key of __getOwnPropNames(from))
16+
if (!__hasOwnProp.call(to, key) && key !== except)
17+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18+
}
19+
return to;
20+
};
21+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
22+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
23+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
24+
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
25+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
26+
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
27+
28+
// src/index.ts
29+
var src_exports = {};
30+
__export(src_exports, {
31+
FormDataEncoder: () => FormDataEncoder,
32+
isFile: () => isFile,
33+
isFormData: () => isFormData
34+
});
35+
module.exports = __toCommonJS(src_exports);
36+
37+
// src/util/isFunction.ts
38+
var isFunction = (value) => typeof value === "function";
39+
40+
// src/util/isReadableStreamFallback.ts
41+
var isReadableStreamFallback = (value) => !!value && typeof value === "object" && !Array.isArray(value) && isFunction(value.getReader);
42+
43+
// src/util/isAsyncIterable.ts
44+
var isAsyncIterable = (value) => isFunction(value[Symbol.asyncIterator]);
45+
46+
// src/util/chunk.ts
47+
var MAX_CHUNK_SIZE = 65536;
48+
function* chunk(value) {
49+
if (value.byteLength <= MAX_CHUNK_SIZE) {
50+
yield value;
51+
return;
52+
}
53+
let offset = 0;
54+
while (offset < value.byteLength) {
55+
const size = Math.min(value.byteLength - offset, MAX_CHUNK_SIZE);
56+
const buffer = value.buffer.slice(offset, offset + size);
57+
offset += buffer.byteLength;
58+
yield new Uint8Array(buffer);
59+
}
60+
}
61+
62+
// src/util/getStreamIterator.ts
63+
async function* readStream(readable) {
64+
const reader = readable.getReader();
65+
while (true) {
66+
const { done, value } = await reader.read();
67+
if (done) {
68+
break;
69+
}
70+
yield value;
71+
}
72+
}
73+
async function* chunkStream(stream) {
74+
for await (const value of stream) {
75+
yield* chunk(value);
76+
}
77+
}
78+
var getStreamIterator = (source) => {
79+
if (isAsyncIterable(source)) {
80+
return chunkStream(source);
81+
}
82+
if (isReadableStreamFallback(source)) {
83+
return chunkStream(readStream(source));
84+
}
85+
throw new TypeError(
86+
"Unsupported data source: Expected either ReadableStream or async iterable."
87+
);
88+
};
89+
90+
// src/util/createBoundary.ts
91+
var alphabet = "abcdefghijklmnopqrstuvwxyz0123456789";
92+
function createBoundary() {
93+
let size = 16;
94+
let res = "";
95+
while (size--) {
96+
res += alphabet[Math.random() * alphabet.length << 0];
97+
}
98+
return res;
99+
}
100+
101+
// src/util/normalizeValue.ts
102+
var normalizeValue = (value) => String(value).replace(/\r|\n/g, (match, i, str) => {
103+
if (match === "\r" && str[i + 1] !== "\n" || match === "\n" && str[i - 1] !== "\r") {
104+
return "\r\n";
105+
}
106+
return match;
107+
});
108+
109+
// src/util/isPlainObject.ts
110+
var getType = (value) => Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
111+
function isPlainObject(value) {
112+
if (getType(value) !== "object") {
113+
return false;
114+
}
115+
const pp = Object.getPrototypeOf(value);
116+
if (pp === null || pp === void 0) {
117+
return true;
118+
}
119+
return pp.constructor?.toString?.() === Object.toString();
120+
}
121+
122+
// src/util/proxyHeaders.ts
123+
function getProperty(target, prop) {
124+
if (typeof prop === "string") {
125+
for (const [name, value] of Object.entries(target)) {
126+
if (prop.toLowerCase() === name.toLowerCase()) {
127+
return value;
128+
}
129+
}
130+
}
131+
return void 0;
132+
}
133+
var proxyHeaders = (object) => new Proxy(
134+
object,
135+
{
136+
get: (target, prop) => getProperty(target, prop),
137+
has: (target, prop) => getProperty(target, prop) !== void 0
138+
}
139+
);
140+
141+
// src/util/isFormData.ts
142+
var isFormData = (value) => Boolean(
143+
value && isFunction(value.constructor) && value[Symbol.toStringTag] === "FormData" && isFunction(value.append) && isFunction(value.getAll) && isFunction(value.entries) && isFunction(value[Symbol.iterator])
144+
);
145+
146+
// src/util/escapeName.ts
147+
var escapeName = (name) => String(name).replace(/\r/g, "%0D").replace(/\n/g, "%0A").replace(/"/g, "%22");
148+
149+
// src/util/isFile.ts
150+
var isFile = (value) => Boolean(
151+
value && typeof value === "object" && isFunction(value.constructor) && value[Symbol.toStringTag] === "File" && isFunction(value.stream) && value.name != null
152+
);
153+
154+
// src/FormDataEncoder.ts
155+
var defaultOptions = {
156+
enableAdditionalHeaders: false
157+
};
158+
var readonlyProp = { writable: false, configurable: false };
159+
var _CRLF, _CRLF_BYTES, _CRLF_BYTES_LENGTH, _DASHES, _encoder, _footer, _form, _options, _FormDataEncoder_instances, getFieldHeader_fn, getContentLength_fn;
160+
var FormDataEncoder = class {
161+
constructor(form, boundaryOrOptions, options) {
162+
__privateAdd(this, _FormDataEncoder_instances);
163+
__privateAdd(this, _CRLF, "\r\n");
164+
__privateAdd(this, _CRLF_BYTES);
165+
__privateAdd(this, _CRLF_BYTES_LENGTH);
166+
__privateAdd(this, _DASHES, "-".repeat(2));
167+
/**
168+
* TextEncoder instance
169+
*/
170+
__privateAdd(this, _encoder, new TextEncoder());
171+
/**
172+
* Returns form-data footer bytes
173+
*/
174+
__privateAdd(this, _footer);
175+
/**
176+
* FormData instance
177+
*/
178+
__privateAdd(this, _form);
179+
/**
180+
* Instance options
181+
*/
182+
__privateAdd(this, _options);
183+
if (!isFormData(form)) {
184+
throw new TypeError("Expected first argument to be a FormData instance.");
185+
}
186+
let boundary;
187+
if (isPlainObject(boundaryOrOptions)) {
188+
options = boundaryOrOptions;
189+
} else {
190+
boundary = boundaryOrOptions;
191+
}
192+
if (!boundary) {
193+
boundary = createBoundary();
194+
}
195+
if (typeof boundary !== "string") {
196+
throw new TypeError("Expected boundary argument to be a string.");
197+
}
198+
if (options && !isPlainObject(options)) {
199+
throw new TypeError("Expected options argument to be an object.");
200+
}
201+
__privateSet(this, _form, Array.from(form.entries()));
202+
__privateSet(this, _options, { ...defaultOptions, ...options });
203+
__privateSet(this, _CRLF_BYTES, __privateGet(this, _encoder).encode(__privateGet(this, _CRLF)));
204+
__privateSet(this, _CRLF_BYTES_LENGTH, __privateGet(this, _CRLF_BYTES).byteLength);
205+
this.boundary = boundary;
206+
this.contentType = `multipart/form-data; boundary=${this.boundary}`;
207+
__privateSet(this, _footer, __privateGet(this, _encoder).encode(
208+
`${__privateGet(this, _DASHES)}${this.boundary}${__privateGet(this, _DASHES)}${__privateGet(this, _CRLF).repeat(2)}`
209+
));
210+
const headers = {
211+
"Content-Type": this.contentType
212+
};
213+
const contentLength = __privateMethod(this, _FormDataEncoder_instances, getContentLength_fn).call(this);
214+
if (contentLength) {
215+
this.contentLength = contentLength;
216+
headers["Content-Length"] = contentLength;
217+
}
218+
this.headers = proxyHeaders(Object.freeze(headers));
219+
Object.defineProperties(this, {
220+
boundary: readonlyProp,
221+
contentType: readonlyProp,
222+
contentLength: readonlyProp,
223+
headers: readonlyProp
224+
});
225+
}
226+
/**
227+
* Creates an iterator allowing to go through form-data parts (with metadata).
228+
* This method **will not** read the files and **will not** split values big into smaller chunks.
229+
*
230+
* Using this method, you can convert form-data content into Blob:
231+
*
232+
* @example
233+
*
234+
* ```ts
235+
* import {Readable} from "stream"
236+
*
237+
* import {FormDataEncoder} from "form-data-encoder"
238+
*
239+
* import {FormData} from "formdata-polyfill/esm-min.js"
240+
* import {fileFrom} from "fetch-blob/form.js"
241+
* import {File} from "fetch-blob/file.js"
242+
* import {Blob} from "fetch-blob"
243+
*
244+
* import fetch from "node-fetch"
245+
*
246+
* const form = new FormData()
247+
*
248+
* form.set("field", "Just a random string")
249+
* form.set("file", new File(["Using files is class amazing"]))
250+
* form.set("fileFromPath", await fileFrom("path/to/a/file.txt"))
251+
*
252+
* const encoder = new FormDataEncoder(form)
253+
*
254+
* const options = {
255+
* method: "post",
256+
* body: new Blob(encoder, {type: encoder.contentType})
257+
* }
258+
*
259+
* const response = await fetch("https://httpbin.org/post", options)
260+
*
261+
* console.log(await response.json())
262+
* ```
263+
*/
264+
*values() {
265+
for (const [name, raw] of __privateGet(this, _form)) {
266+
const value = isFile(raw) ? raw : __privateGet(this, _encoder).encode(normalizeValue(raw));
267+
yield __privateMethod(this, _FormDataEncoder_instances, getFieldHeader_fn).call(this, name, value);
268+
yield value;
269+
yield __privateGet(this, _CRLF_BYTES);
270+
}
271+
yield __privateGet(this, _footer);
272+
}
273+
/**
274+
* Creates an async iterator allowing to perform the encoding by portions.
275+
* This method reads through files and splits big values into smaller pieces (65536 bytes per each).
276+
*
277+
* @example
278+
*
279+
* ```ts
280+
* import {Readable} from "stream"
281+
*
282+
* import {FormData, File, fileFromPath} from "formdata-node"
283+
* import {FormDataEncoder} from "form-data-encoder"
284+
*
285+
* import fetch from "node-fetch"
286+
*
287+
* const form = new FormData()
288+
*
289+
* form.set("field", "Just a random string")
290+
* form.set("file", new File(["Using files is class amazing"], "file.txt"))
291+
* form.set("fileFromPath", await fileFromPath("path/to/a/file.txt"))
292+
*
293+
* const encoder = new FormDataEncoder(form)
294+
*
295+
* const options = {
296+
* method: "post",
297+
* headers: encoder.headers,
298+
* body: Readable.from(encoder.encode()) // or Readable.from(encoder)
299+
* }
300+
*
301+
* const response = await fetch("https://httpbin.org/post", options)
302+
*
303+
* console.log(await response.json())
304+
* ```
305+
*/
306+
async *encode() {
307+
for (const part of this.values()) {
308+
if (isFile(part)) {
309+
yield* getStreamIterator(part.stream());
310+
} else {
311+
yield* chunk(part);
312+
}
313+
}
314+
}
315+
/**
316+
* Creates an iterator allowing to read through the encoder data using for...of loops
317+
*/
318+
[Symbol.iterator]() {
319+
return this.values();
320+
}
321+
/**
322+
* Creates an **async** iterator allowing to read through the encoder data using for-await...of loops
323+
*/
324+
[Symbol.asyncIterator]() {
325+
return this.encode();
326+
}
327+
};
328+
_CRLF = new WeakMap();
329+
_CRLF_BYTES = new WeakMap();
330+
_CRLF_BYTES_LENGTH = new WeakMap();
331+
_DASHES = new WeakMap();
332+
_encoder = new WeakMap();
333+
_footer = new WeakMap();
334+
_form = new WeakMap();
335+
_options = new WeakMap();
336+
_FormDataEncoder_instances = new WeakSet();
337+
getFieldHeader_fn = function(name, value) {
338+
let header = "";
339+
header += `${__privateGet(this, _DASHES)}${this.boundary}${__privateGet(this, _CRLF)}`;
340+
header += `Content-Disposition: form-data; name="${escapeName(name)}"`;
341+
if (isFile(value)) {
342+
header += `; filename="${escapeName(value.name)}"${__privateGet(this, _CRLF)}`;
343+
header += `Content-Type: ${value.type || "application/octet-stream"}`;
344+
}
345+
if (__privateGet(this, _options).enableAdditionalHeaders === true) {
346+
const size = isFile(value) ? value.size : value.byteLength;
347+
if (size != null && !isNaN(size)) {
348+
header += `${__privateGet(this, _CRLF)}Content-Length: ${size}`;
349+
}
350+
}
351+
return __privateGet(this, _encoder).encode(`${header}${__privateGet(this, _CRLF).repeat(2)}`);
352+
};
353+
/**
354+
* Returns form-data content length
355+
*/
356+
getContentLength_fn = function() {
357+
let length = 0;
358+
for (const [name, raw] of __privateGet(this, _form)) {
359+
const value = isFile(raw) ? raw : __privateGet(this, _encoder).encode(normalizeValue(raw));
360+
const size = isFile(value) ? value.size : value.byteLength;
361+
if (size == null || isNaN(size)) {
362+
return void 0;
363+
}
364+
length += __privateMethod(this, _FormDataEncoder_instances, getFieldHeader_fn).call(this, name, value).byteLength;
365+
length += size;
366+
length += __privateGet(this, _CRLF_BYTES_LENGTH);
367+
}
368+
return String(length + __privateGet(this, _footer).byteLength);
369+
};
370+
// Annotate the CommonJS export names for ESM import in node:
371+
0 && (module.exports = {
372+
FormDataEncoder,
373+
isFile,
374+
isFormData
375+
});

0 commit comments

Comments
 (0)