source: node_modules/undici/lib/fetch/formdata.js

main
Last change on this file was d24f17c, checked in by Aleksandar Panovski <apano77@…>, 15 months ago

Initial commit

  • Property mode set to 100644
File size: 7.7 KB
Line 
1'use strict'
2
3const { isBlobLike, toUSVString, makeIterator } = require('./util')
4const { kState } = require('./symbols')
5const { File: UndiciFile, FileLike, isFileLike } = require('./file')
6const { webidl } = require('./webidl')
7const { Blob, File: NativeFile } = require('buffer')
8
9/** @type {globalThis['File']} */
10const File = NativeFile ?? UndiciFile
11
12// https://xhr.spec.whatwg.org/#formdata
13class FormData {
14 constructor (form) {
15 if (form !== undefined) {
16 throw webidl.errors.conversionFailed({
17 prefix: 'FormData constructor',
18 argument: 'Argument 1',
19 types: ['undefined']
20 })
21 }
22
23 this[kState] = []
24 }
25
26 append (name, value, filename = undefined) {
27 webidl.brandCheck(this, FormData)
28
29 webidl.argumentLengthCheck(arguments, 2, { header: 'FormData.append' })
30
31 if (arguments.length === 3 && !isBlobLike(value)) {
32 throw new TypeError(
33 "Failed to execute 'append' on 'FormData': parameter 2 is not of type 'Blob'"
34 )
35 }
36
37 // 1. Let value be value if given; otherwise blobValue.
38
39 name = webidl.converters.USVString(name)
40 value = isBlobLike(value)
41 ? webidl.converters.Blob(value, { strict: false })
42 : webidl.converters.USVString(value)
43 filename = arguments.length === 3
44 ? webidl.converters.USVString(filename)
45 : undefined
46
47 // 2. Let entry be the result of creating an entry with
48 // name, value, and filename if given.
49 const entry = makeEntry(name, value, filename)
50
51 // 3. Append entry to this’s entry list.
52 this[kState].push(entry)
53 }
54
55 delete (name) {
56 webidl.brandCheck(this, FormData)
57
58 webidl.argumentLengthCheck(arguments, 1, { header: 'FormData.delete' })
59
60 name = webidl.converters.USVString(name)
61
62 // The delete(name) method steps are to remove all entries whose name
63 // is name from this’s entry list.
64 this[kState] = this[kState].filter(entry => entry.name !== name)
65 }
66
67 get (name) {
68 webidl.brandCheck(this, FormData)
69
70 webidl.argumentLengthCheck(arguments, 1, { header: 'FormData.get' })
71
72 name = webidl.converters.USVString(name)
73
74 // 1. If there is no entry whose name is name in this’s entry list,
75 // then return null.
76 const idx = this[kState].findIndex((entry) => entry.name === name)
77 if (idx === -1) {
78 return null
79 }
80
81 // 2. Return the value of the first entry whose name is name from
82 // this’s entry list.
83 return this[kState][idx].value
84 }
85
86 getAll (name) {
87 webidl.brandCheck(this, FormData)
88
89 webidl.argumentLengthCheck(arguments, 1, { header: 'FormData.getAll' })
90
91 name = webidl.converters.USVString(name)
92
93 // 1. If there is no entry whose name is name in this’s entry list,
94 // then return the empty list.
95 // 2. Return the values of all entries whose name is name, in order,
96 // from this’s entry list.
97 return this[kState]
98 .filter((entry) => entry.name === name)
99 .map((entry) => entry.value)
100 }
101
102 has (name) {
103 webidl.brandCheck(this, FormData)
104
105 webidl.argumentLengthCheck(arguments, 1, { header: 'FormData.has' })
106
107 name = webidl.converters.USVString(name)
108
109 // The has(name) method steps are to return true if there is an entry
110 // whose name is name in this’s entry list; otherwise false.
111 return this[kState].findIndex((entry) => entry.name === name) !== -1
112 }
113
114 set (name, value, filename = undefined) {
115 webidl.brandCheck(this, FormData)
116
117 webidl.argumentLengthCheck(arguments, 2, { header: 'FormData.set' })
118
119 if (arguments.length === 3 && !isBlobLike(value)) {
120 throw new TypeError(
121 "Failed to execute 'set' on 'FormData': parameter 2 is not of type 'Blob'"
122 )
123 }
124
125 // The set(name, value) and set(name, blobValue, filename) method steps
126 // are:
127
128 // 1. Let value be value if given; otherwise blobValue.
129
130 name = webidl.converters.USVString(name)
131 value = isBlobLike(value)
132 ? webidl.converters.Blob(value, { strict: false })
133 : webidl.converters.USVString(value)
134 filename = arguments.length === 3
135 ? toUSVString(filename)
136 : undefined
137
138 // 2. Let entry be the result of creating an entry with name, value, and
139 // filename if given.
140 const entry = makeEntry(name, value, filename)
141
142 // 3. If there are entries in this’s entry list whose name is name, then
143 // replace the first such entry with entry and remove the others.
144 const idx = this[kState].findIndex((entry) => entry.name === name)
145 if (idx !== -1) {
146 this[kState] = [
147 ...this[kState].slice(0, idx),
148 entry,
149 ...this[kState].slice(idx + 1).filter((entry) => entry.name !== name)
150 ]
151 } else {
152 // 4. Otherwise, append entry to this’s entry list.
153 this[kState].push(entry)
154 }
155 }
156
157 entries () {
158 webidl.brandCheck(this, FormData)
159
160 return makeIterator(
161 () => this[kState].map(pair => [pair.name, pair.value]),
162 'FormData',
163 'key+value'
164 )
165 }
166
167 keys () {
168 webidl.brandCheck(this, FormData)
169
170 return makeIterator(
171 () => this[kState].map(pair => [pair.name, pair.value]),
172 'FormData',
173 'key'
174 )
175 }
176
177 values () {
178 webidl.brandCheck(this, FormData)
179
180 return makeIterator(
181 () => this[kState].map(pair => [pair.name, pair.value]),
182 'FormData',
183 'value'
184 )
185 }
186
187 /**
188 * @param {(value: string, key: string, self: FormData) => void} callbackFn
189 * @param {unknown} thisArg
190 */
191 forEach (callbackFn, thisArg = globalThis) {
192 webidl.brandCheck(this, FormData)
193
194 webidl.argumentLengthCheck(arguments, 1, { header: 'FormData.forEach' })
195
196 if (typeof callbackFn !== 'function') {
197 throw new TypeError(
198 "Failed to execute 'forEach' on 'FormData': parameter 1 is not of type 'Function'."
199 )
200 }
201
202 for (const [key, value] of this) {
203 callbackFn.apply(thisArg, [value, key, this])
204 }
205 }
206}
207
208FormData.prototype[Symbol.iterator] = FormData.prototype.entries
209
210Object.defineProperties(FormData.prototype, {
211 [Symbol.toStringTag]: {
212 value: 'FormData',
213 configurable: true
214 }
215})
216
217/**
218 * @see https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#create-an-entry
219 * @param {string} name
220 * @param {string|Blob} value
221 * @param {?string} filename
222 * @returns
223 */
224function makeEntry (name, value, filename) {
225 // 1. Set name to the result of converting name into a scalar value string.
226 // "To convert a string into a scalar value string, replace any surrogates
227 // with U+FFFD."
228 // see: https://nodejs.org/dist/latest-v18.x/docs/api/buffer.html#buftostringencoding-start-end
229 name = Buffer.from(name).toString('utf8')
230
231 // 2. If value is a string, then set value to the result of converting
232 // value into a scalar value string.
233 if (typeof value === 'string') {
234 value = Buffer.from(value).toString('utf8')
235 } else {
236 // 3. Otherwise:
237
238 // 1. If value is not a File object, then set value to a new File object,
239 // representing the same bytes, whose name attribute value is "blob"
240 if (!isFileLike(value)) {
241 value = value instanceof Blob
242 ? new File([value], 'blob', { type: value.type })
243 : new FileLike(value, 'blob', { type: value.type })
244 }
245
246 // 2. If filename is given, then set value to a new File object,
247 // representing the same bytes, whose name attribute is filename.
248 if (filename !== undefined) {
249 /** @type {FilePropertyBag} */
250 const options = {
251 type: value.type,
252 lastModified: value.lastModified
253 }
254
255 value = (NativeFile && value instanceof NativeFile) || value instanceof UndiciFile
256 ? new File([value], filename, options)
257 : new FileLike(value, filename, options)
258 }
259 }
260
261 // 4. Return an entry whose name is name and whose value is value.
262 return { name, value }
263}
264
265module.exports = { FormData }
Note: See TracBrowser for help on using the repository browser.