parse-nested-form-data 1.0.0
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 +13 -0
- package/LICENSE +20 -0
- package/README.md +363 -0
- package/dist/__tests__/index.d.ts +1 -0
- package/dist/index.d.ts +255 -0
- package/dist/parse-nested-form-data.cjs.js +393 -0
- package/dist/parse-nested-form-data.esm.js +387 -0
- package/dist/parse-nested-form-data.umd.js +398 -0
- package/dist/parse-nested-form-data.umd.js.map +1 -0
- package/dist/parse-nested-form-data.umd.min.js +8 -0
- package/dist/parse-nested-form-data.umd.min.js.map +1 -0
- package/package.json +77 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
|
2
|
+
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
|
3
|
+
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
|
|
4
|
+
|
|
5
|
+
- [CHANGELOG](#changelog)
|
|
6
|
+
|
|
7
|
+
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
|
8
|
+
|
|
9
|
+
# CHANGELOG
|
|
10
|
+
|
|
11
|
+
The changelog is automatically updated using
|
|
12
|
+
[semantic-release](https://github.com/semantic-release/semantic-release). You
|
|
13
|
+
can see it on the [releases page](../../releases).
|
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Christian Schurr
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
7
|
+
the Software without restriction, including without limitation the rights to
|
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
10
|
+
subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<h1>parse-nested-form-data</h1>
|
|
3
|
+
|
|
4
|
+
<p>A tiny node module for parsing FormData by name into objects and arrays</p>
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
<!-- prettier-ignore-start -->
|
|
10
|
+
[![Build Status][build-badge]][build]
|
|
11
|
+
[![Code Coverage][coverage-badge]][coverage]
|
|
12
|
+
[![version][version-badge]][package]
|
|
13
|
+
[![downloads][downloads-badge]][npmtrends]
|
|
14
|
+
[![MIT License][license-badge]][license]
|
|
15
|
+
[![All Contributors][all-contributors-badge]](#contributors-)
|
|
16
|
+
[![PRs Welcome][prs-badge]][prs]
|
|
17
|
+
[![Code of Conduct][coc-badge]][coc]
|
|
18
|
+
<!-- prettier-ignore-end -->
|
|
19
|
+
|
|
20
|
+
## The problem
|
|
21
|
+
|
|
22
|
+
1. You use Forms to upload data to your server (e.g. with remix-run)
|
|
23
|
+
2. You want a way to upload objects and arrays with booleans and numbers
|
|
24
|
+
3. You do not want to transfer the data per hand
|
|
25
|
+
|
|
26
|
+
## This solution
|
|
27
|
+
|
|
28
|
+
A parse functions that uses the name prop to tells the parser how to create an
|
|
29
|
+
object out of the values. You can tell the parser to do not parse empty values.
|
|
30
|
+
|
|
31
|
+
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
|
32
|
+
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
|
33
|
+
|
|
34
|
+
- [Installation](#installation)
|
|
35
|
+
- [Name](#name)
|
|
36
|
+
- [Boolean](#boolean)
|
|
37
|
+
- [Number](#number)
|
|
38
|
+
- [Null](#null)
|
|
39
|
+
- [Array](#array)
|
|
40
|
+
- [Object](#object)
|
|
41
|
+
- [Basic Usage](#basic-usage)
|
|
42
|
+
- [Transform values](#transform-values)
|
|
43
|
+
- [Advanced usage](#advanced-usage)
|
|
44
|
+
- [Options](#options)
|
|
45
|
+
- [removeEmptyString: `[boolean]`](#removeemptystring-boolean)
|
|
46
|
+
- [transformEntry: `(entry: [path: string, value: string | File], defaultTransform: DefaultTransform) => [string, string | JsonLeafValue]`](#transformentry-entry-path-string-value-string--file-defaulttransform-defaulttransform--string-string--jsonleafvalue)
|
|
47
|
+
- [Issues](#issues)
|
|
48
|
+
- [🐛 Bugs](#-bugs)
|
|
49
|
+
- [💡 Feature Requests](#-feature-requests)
|
|
50
|
+
- [Contributors ✨](#contributors-)
|
|
51
|
+
- [LICENSE](#license)
|
|
52
|
+
- [Special Thanks](#special-thanks)
|
|
53
|
+
|
|
54
|
+
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
|
55
|
+
|
|
56
|
+
## Installation
|
|
57
|
+
|
|
58
|
+
This module is distributed via [npm][npm] which is bundled with [node][node] and
|
|
59
|
+
should be installed as one of your project's `dependencies`:
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
npm install parse-nested-form-data
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Name
|
|
66
|
+
|
|
67
|
+
The name prop is used to tell the parser how to create an object out of the
|
|
68
|
+
values. The name prop is a string that can contain the following characters:
|
|
69
|
+
|
|
70
|
+
- `.`: to create a nested object
|
|
71
|
+
- `[]`: to create an array (pushes value)
|
|
72
|
+
- `[$order]`: to create an array (sets value at index and squashes array)
|
|
73
|
+
- `&`: to transform the value to a boolean
|
|
74
|
+
- `-`: to transform the value to null
|
|
75
|
+
- `+`: to transform the value to a number
|
|
76
|
+
|
|
77
|
+
### Boolean
|
|
78
|
+
|
|
79
|
+
If the name prop starts with a `&` the value will be transformed to a boolean.
|
|
80
|
+
|
|
81
|
+
True if the value has the following values:
|
|
82
|
+
|
|
83
|
+
- `true`
|
|
84
|
+
- `1`
|
|
85
|
+
- `on`
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
const formData = new FormData()
|
|
89
|
+
formData.append('&isTrue', 'true')
|
|
90
|
+
formData.append('&isFalse', 'false')
|
|
91
|
+
formData.append('&isTrue1', '1')
|
|
92
|
+
formData.append('&isFalse1', '0')
|
|
93
|
+
formData.append('&isTrue2', 'on')
|
|
94
|
+
formData.append('&isFalse2', 'off')
|
|
95
|
+
parseFormData(formData)
|
|
96
|
+
// {
|
|
97
|
+
// isTrue: true,
|
|
98
|
+
// isFalse: false,
|
|
99
|
+
// isTrue1: true,
|
|
100
|
+
// isFalse1: false,
|
|
101
|
+
// isTrue2: true,
|
|
102
|
+
// isFalse2: false,
|
|
103
|
+
// }
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Number
|
|
107
|
+
|
|
108
|
+
If the name prop starts with a `+` the value will be transformed to a number.
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
const formData = new FormData()
|
|
112
|
+
formData.append('+number', '1')
|
|
113
|
+
formData.append('+number2', '1.1')
|
|
114
|
+
formData.append('+number3', 'a')
|
|
115
|
+
parseFormData(formData)
|
|
116
|
+
// {
|
|
117
|
+
// number: 1,
|
|
118
|
+
// number2: 1.1,
|
|
119
|
+
// number3: NaN,
|
|
120
|
+
// }
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Null
|
|
124
|
+
|
|
125
|
+
If the name prop starts with a `-` the value will be transformed to null.
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
const formData = new FormData()
|
|
129
|
+
formData.append('-null', 'null')
|
|
130
|
+
formData.append('-ignored', 'ignored')
|
|
131
|
+
parseFormData(formData)
|
|
132
|
+
// {
|
|
133
|
+
// null: null,
|
|
134
|
+
// ignored: null,
|
|
135
|
+
// }
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Array
|
|
139
|
+
|
|
140
|
+
If a path of the name prop ends with `[]` the value will be pushed to an array.
|
|
141
|
+
If the path of the name prop ends with `[$order]` the value will be set at the
|
|
142
|
+
index of the order. Cannot be mixed with `[]`.
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
const formData = new FormData()
|
|
146
|
+
formData.append('array[0]', '1')
|
|
147
|
+
formData.append('array[1]', '2')
|
|
148
|
+
formData.append('array[2]', '3')
|
|
149
|
+
formData.append('array[3]', '4')
|
|
150
|
+
parseFormData(formData)
|
|
151
|
+
// {
|
|
152
|
+
// array: ['1', '2', '3', '4'],
|
|
153
|
+
// }
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
const formData = new FormData()
|
|
158
|
+
formData.append('array[]', '1')
|
|
159
|
+
formData.append('array[]', '2')
|
|
160
|
+
formData.append('array[]', '3')
|
|
161
|
+
parseFormData(formData)
|
|
162
|
+
// {
|
|
163
|
+
// array: ['1', '2', '3'],
|
|
164
|
+
// }
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Also works with nested objects:
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
const formData = new FormData()
|
|
171
|
+
formData.append('array[0].a', '1')
|
|
172
|
+
formData.append('array[1].a', '2')
|
|
173
|
+
formData.append('array[2].a', '3')
|
|
174
|
+
parseFormData(formData)
|
|
175
|
+
// {
|
|
176
|
+
// array: [{a: '1'}, {a: '2'}, {a: '3'}],
|
|
177
|
+
// }
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Also works with nested arrays:
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
const formData = new FormData()
|
|
184
|
+
formData.append('array[0][]', '1')
|
|
185
|
+
formData.append('array[0][]', '2')
|
|
186
|
+
formData.append('array[1][]', '3')
|
|
187
|
+
formData.append('array[1][]', '4')
|
|
188
|
+
parseFormData(formData)
|
|
189
|
+
// {
|
|
190
|
+
// array: [['1', '2'], ['3', '4']],
|
|
191
|
+
// }
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Object
|
|
195
|
+
|
|
196
|
+
If the name prop contains a `.` the value will be nested in an object.
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
const formData = new FormData()
|
|
200
|
+
formData.append('object.a', '1')
|
|
201
|
+
formData.append('object.b', '2')
|
|
202
|
+
parseFormData(formData)
|
|
203
|
+
// {
|
|
204
|
+
// object: {
|
|
205
|
+
// a: '1',
|
|
206
|
+
// b: '2',
|
|
207
|
+
// },
|
|
208
|
+
// }
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Basic Usage
|
|
212
|
+
|
|
213
|
+
### Transform values
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
const formData = new FormData()
|
|
217
|
+
formData.append('+a', '1')
|
|
218
|
+
formData.append('&b', 'true')
|
|
219
|
+
formData.append('-c', 'null')
|
|
220
|
+
formData.append('d', 'foo')
|
|
221
|
+
parseFormData(formData, defaultTransform)
|
|
222
|
+
// => {a: 1, b: true, c: null, d: 'foo'}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Advanced usage
|
|
226
|
+
|
|
227
|
+
Complex examples:
|
|
228
|
+
|
|
229
|
+
```ts
|
|
230
|
+
const formData = new FormData()
|
|
231
|
+
formData.append('a[].b.c', '1')
|
|
232
|
+
formData.append('a[].b.d', '2')
|
|
233
|
+
formData.append('a[]', '3')
|
|
234
|
+
formData.append('b[0]', '4')
|
|
235
|
+
formData.append('+b[100]', '5')
|
|
236
|
+
parseFormData(formData)
|
|
237
|
+
// {
|
|
238
|
+
// a: [
|
|
239
|
+
// {
|
|
240
|
+
// b: {
|
|
241
|
+
// c: '1',
|
|
242
|
+
// d: '2',
|
|
243
|
+
// },
|
|
244
|
+
// },
|
|
245
|
+
// '3',
|
|
246
|
+
// ],
|
|
247
|
+
// b: ['4', 5],
|
|
248
|
+
// }
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Options
|
|
252
|
+
|
|
253
|
+
The second argument is an option object with following properties:
|
|
254
|
+
|
|
255
|
+
### removeEmptyString: `[boolean]`
|
|
256
|
+
|
|
257
|
+
_Default: `false`_
|
|
258
|
+
|
|
259
|
+
If `true` empty strings will be removed from the result.
|
|
260
|
+
|
|
261
|
+
```ts
|
|
262
|
+
const formData = new FormData()
|
|
263
|
+
formData.append('foo[1].foo', '')
|
|
264
|
+
formData.append('foo[1].bar', 'test2')
|
|
265
|
+
formData.append('foo[0].foo', 'test3')
|
|
266
|
+
formData.append('foo[0].baz', '4')
|
|
267
|
+
|
|
268
|
+
parseFormData(formData, {removeEmptyString: true})
|
|
269
|
+
// {foo: [{foo: 'test3', baz: '4'}, {bar: 'test2'}]}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### transformEntry: `(entry: [path: string, value: string | File], defaultTransform: DefaultTransform) => [string, string | JsonLeafValue]`
|
|
273
|
+
|
|
274
|
+
```ts
|
|
275
|
+
const formData = new FormData()
|
|
276
|
+
formData.append('a', 'a')
|
|
277
|
+
formData.append('b', 'b')
|
|
278
|
+
parseFormData(formData, {
|
|
279
|
+
transformEntry: ([path, value], defaultTransform) => {
|
|
280
|
+
return {
|
|
281
|
+
path,
|
|
282
|
+
value:
|
|
283
|
+
typeof value === 'string'
|
|
284
|
+
? value.toUpperCase()
|
|
285
|
+
: defaultTransform(value),
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
})
|
|
289
|
+
// => {a: 'A', b: 'B'}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Issues
|
|
293
|
+
|
|
294
|
+
_Looking to contribute? Look for the [Good First Issue][good-first-issue]
|
|
295
|
+
label._
|
|
296
|
+
|
|
297
|
+
### 🐛 Bugs
|
|
298
|
+
|
|
299
|
+
Please file an issue for bugs, missing documentation, or unexpected behavior.
|
|
300
|
+
|
|
301
|
+
[**See Bugs**][bugs]
|
|
302
|
+
|
|
303
|
+
### 💡 Feature Requests
|
|
304
|
+
|
|
305
|
+
Please file an issue to suggest new features. Vote on feature requests by adding
|
|
306
|
+
a 👍. This helps maintainers prioritize what to work on.
|
|
307
|
+
|
|
308
|
+
[**See Feature Requests**][requests]
|
|
309
|
+
|
|
310
|
+
## Contributors ✨
|
|
311
|
+
|
|
312
|
+
Thanks goes to these people ([emoji key][emojis]):
|
|
313
|
+
|
|
314
|
+
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
|
315
|
+
<!-- prettier-ignore-start -->
|
|
316
|
+
<!-- markdownlint-disable -->
|
|
317
|
+
<table>
|
|
318
|
+
<tr>
|
|
319
|
+
</tr>
|
|
320
|
+
</table>
|
|
321
|
+
|
|
322
|
+
<!-- markdownlint-restore -->
|
|
323
|
+
<!-- prettier-ignore-end -->
|
|
324
|
+
|
|
325
|
+
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
|
326
|
+
|
|
327
|
+
This project follows the [all-contributors][all-contributors] specification.
|
|
328
|
+
Contributions of any kind welcome!
|
|
329
|
+
|
|
330
|
+
## LICENSE
|
|
331
|
+
|
|
332
|
+
MIT
|
|
333
|
+
|
|
334
|
+
## Special Thanks
|
|
335
|
+
|
|
336
|
+
Special thanks to Kent C. Dodds and his [match-sorter][match-sorter] package
|
|
337
|
+
where most of the setup is from.
|
|
338
|
+
|
|
339
|
+
<!-- prettier-ignore-start -->
|
|
340
|
+
[npm]: https://www.npmjs.com
|
|
341
|
+
[node]: https://nodejs.org
|
|
342
|
+
[build-badge]: https://img.shields.io/github/workflow/status/milamer/parse-nested-form-data/validate?logo=github&style=flat-square
|
|
343
|
+
[build]: https://github.com/milamer/parse-nested-form-data/actions?query=workflow%3Avalidate
|
|
344
|
+
[coverage-badge]: https://img.shields.io/codecov/c/github/milamer/parse-nested-form-data.svg?style=flat-square
|
|
345
|
+
[coverage]: https://codecov.io/github/milamer/parse-nested-form-data
|
|
346
|
+
[version-badge]: https://img.shields.io/npm/v/parse-nested-form-data.svg?style=flat-square
|
|
347
|
+
[package]: https://www.npmjs.com/package/parse-nested-form-data
|
|
348
|
+
[downloads-badge]: https://img.shields.io/npm/dm/parse-nested-form-data.svg?style=flat-square
|
|
349
|
+
[npmtrends]: https://www.npmtrends.com/parse-nested-form-data
|
|
350
|
+
[license-badge]: https://img.shields.io/npm/l/parse-nested-form-data.svg?style=flat-square
|
|
351
|
+
[license]: https://github.com/milamer/parse-nested-form-data/blob/master/LICENSE
|
|
352
|
+
[prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square
|
|
353
|
+
[prs]: http://makeapullrequest.com
|
|
354
|
+
[coc-badge]: https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square
|
|
355
|
+
[coc]: https://github.com/milamer/parse-nested-form-data/blob/master/CODE_OF_CONDUCT.md
|
|
356
|
+
[emojis]: https://github.com/all-contributors/all-contributors#emoji-key
|
|
357
|
+
[all-contributors]: https://github.com/all-contributors/all-contributors
|
|
358
|
+
[all-contributors-badge]: https://img.shields.io/github/all-contributors/milamer/parse-nested-form-data?color=orange&style=flat-square
|
|
359
|
+
[bugs]: https://github.com/milamer/parse-nested-form-data/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+sort%3Acreated-desc+label%3Abug
|
|
360
|
+
[requests]: https://github.com/milamer/parse-nested-form-data/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc+label%3Aenhancement
|
|
361
|
+
[good-first-issue]: https://github.com/milamer/parse-nested-form-data/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc+label%3Aenhancement+label%3A%22good+first+issue%22
|
|
362
|
+
[match-sorter]: https://github.com/kentcdodds/match-sorter/blob/main/README.md
|
|
363
|
+
<!-- prettier-ignore-end -->
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @name parse-nested-form-data
|
|
3
|
+
* @license MIT license.
|
|
4
|
+
* @copyright (c) 2022 Christian Schurr
|
|
5
|
+
* @author Christian Schurr <chris@schurr.dev>
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Thrown when a path is used multiple times or has missmatching path parts.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* const formData = new FormData()
|
|
13
|
+
* formData.append('a[0]', 'b')
|
|
14
|
+
* formData.append('a[0]', 'c')
|
|
15
|
+
* parseFormData(formData)
|
|
16
|
+
* // throws DuplicateKeyError('a[0]')
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* const formData = new FormData()
|
|
22
|
+
* formData.append('a', 'b')
|
|
23
|
+
* formData.append('a', 'c')
|
|
24
|
+
* parseFormData(formData)
|
|
25
|
+
* // throws DuplicateKeyError('a')
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* const formData = new FormData()
|
|
30
|
+
* formData.append('a', 'b')
|
|
31
|
+
* formData.append('a[]', 'c')
|
|
32
|
+
* parseFormData(formData)
|
|
33
|
+
* // throws DuplicateKeyError('a[]')
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
*/
|
|
37
|
+
export declare class DuplicateKeyError extends Error {
|
|
38
|
+
key: string;
|
|
39
|
+
constructor(key: string);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Thrown when an array is used at the same path with an order parameter and
|
|
43
|
+
* without an order parameter.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* const formData = new FormData()
|
|
48
|
+
* formData.append('a[0]', 'a')
|
|
49
|
+
* formData.append('a[]', 'b')
|
|
50
|
+
* parseFormData(formData)
|
|
51
|
+
* // => throws `MixedArrayError(a[])`
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```ts
|
|
56
|
+
* const formData = new FormData()
|
|
57
|
+
* formData.append('a[]', 'a')
|
|
58
|
+
* formData.append('a[0]', 'b')
|
|
59
|
+
* parseFormData(formData)
|
|
60
|
+
* // => throws `MixedArrayError(a[0])`
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export declare class MixedArrayError extends Error {
|
|
64
|
+
key: string;
|
|
65
|
+
constructor(key: string);
|
|
66
|
+
}
|
|
67
|
+
type JsonObject = {
|
|
68
|
+
[Key in string]?: JsonValue;
|
|
69
|
+
};
|
|
70
|
+
type JsonArray = Array<JsonValue>;
|
|
71
|
+
type JsonValue = string | number | JsonObject | JsonArray | boolean | null | File;
|
|
72
|
+
type JsonLeafValue = Exclude<JsonValue, JsonArray | JsonObject>;
|
|
73
|
+
/**
|
|
74
|
+
* Default Transformer for `parseFormData`.
|
|
75
|
+
*
|
|
76
|
+
* Transforms a FormData Entry into a path and a `JsonLeafValue`.
|
|
77
|
+
*
|
|
78
|
+
* - `path` starts with `+` -> transform value to `number`
|
|
79
|
+
* - `path` starts with `&` -> transform value to `boolean`
|
|
80
|
+
* - `path` starts with `-` -> transform value to `null`
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```ts
|
|
84
|
+
* const entry = ['a[0]', 'b']
|
|
85
|
+
* const result = defaultTransform(entry)
|
|
86
|
+
* // => {path: 'a[0]', value: 'b'}
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```ts
|
|
91
|
+
* const entry = ['+a[0]', '1']
|
|
92
|
+
* const result = defaultTransform(entry)
|
|
93
|
+
* // => {path: 'a[0]', value: 1}
|
|
94
|
+
* ```
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```ts
|
|
98
|
+
* const entry = ['&a[0]', 'true']
|
|
99
|
+
* const result = defaultTransform(entry)
|
|
100
|
+
* // => {path: 'a[0]', value: true}
|
|
101
|
+
* ```
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```ts
|
|
105
|
+
* const entry = ['-a[0]', 'null']
|
|
106
|
+
* const result = defaultTransform(entry)
|
|
107
|
+
* // => {path: 'a[0]', value: null}
|
|
108
|
+
* ```
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```ts
|
|
112
|
+
* const entry = ['a[0]', new File([''], 'file.txt')]
|
|
113
|
+
* const result = defaultTransform(entry)
|
|
114
|
+
* // => {path: 'a[0]', value: File}
|
|
115
|
+
* ```
|
|
116
|
+
*
|
|
117
|
+
*
|
|
118
|
+
* @param entry [path, value]: the FormData entry
|
|
119
|
+
* @returns the path and the transformed value
|
|
120
|
+
*/
|
|
121
|
+
declare function defaultTransform(entry: [path: string, value: string | File]): {
|
|
122
|
+
path: string;
|
|
123
|
+
value: JsonLeafValue;
|
|
124
|
+
};
|
|
125
|
+
type DefaultTransform = typeof defaultTransform;
|
|
126
|
+
/**
|
|
127
|
+
* Options to change the behavior of `parseFormData`.
|
|
128
|
+
* @param transformEntry - a function to transform the FormData entry into a path and a value
|
|
129
|
+
* (default: `defaultTransform`)
|
|
130
|
+
* @param removeEmptyString - skip empty values '' (default: `false`)
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* ```ts
|
|
134
|
+
* const formData = new FormData()
|
|
135
|
+
* formData.append('a', '')
|
|
136
|
+
* formData.append('b', 'b')
|
|
137
|
+
* const result = parseFormData(formData, {removeEmptyString: true})
|
|
138
|
+
* // => {b: 'b'}
|
|
139
|
+
* ```
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```ts
|
|
143
|
+
* const formData = new FormData()
|
|
144
|
+
* formData.append('a', 'a')
|
|
145
|
+
* formData.append('b', 'b')
|
|
146
|
+
* parseFormData(formData, {
|
|
147
|
+
* transformEntry: ([path, value], defaultTransform) => {
|
|
148
|
+
* return {
|
|
149
|
+
* path,
|
|
150
|
+
* value:
|
|
151
|
+
* typeof value === 'string'
|
|
152
|
+
* ? value.toUpperCase()
|
|
153
|
+
* : defaultTransform(value),
|
|
154
|
+
* }
|
|
155
|
+
* },
|
|
156
|
+
* })
|
|
157
|
+
* // => {a: 'A', b: 'B'}
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
160
|
+
export type ParseFormDataOptions = {
|
|
161
|
+
removeEmptyString?: boolean;
|
|
162
|
+
transformEntry?: (entry: [path: string, value: string | File], defaultTransform: DefaultTransform) => {
|
|
163
|
+
path: string;
|
|
164
|
+
value: JsonLeafValue;
|
|
165
|
+
};
|
|
166
|
+
};
|
|
167
|
+
/**
|
|
168
|
+
*
|
|
169
|
+
* Parses a FormData object to a JSON object. This is done by parsing the `name`
|
|
170
|
+
* attribute of each `FormDataEntryValue` and then inserting the value at the
|
|
171
|
+
* path. Also by default the start of the path is used to transform the value.
|
|
172
|
+
*
|
|
173
|
+
*
|
|
174
|
+
* In front of the whole `key`:
|
|
175
|
+
* - `+` => parse to `Number`
|
|
176
|
+
* - `-` => set value to `null`
|
|
177
|
+
* - `&` => parse to `Boolean`
|
|
178
|
+
*
|
|
179
|
+
* - `.` between path parts => nest into `objects`
|
|
180
|
+
* - `[\d*]` after path part => push to array in order `\d` or push to end if `[]`
|
|
181
|
+
*
|
|
182
|
+
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* ```ts
|
|
186
|
+
* const formData = new FormData()
|
|
187
|
+
* formData.append('+a', '1')
|
|
188
|
+
* formData.append('&b', 'true')
|
|
189
|
+
* formData.append('-c', 'null')
|
|
190
|
+
* formData.append('d', 'foo')
|
|
191
|
+
* parseFormData(formData, defaultTransform)
|
|
192
|
+
* // => {a: 1, b: true, c: null, d: 'foo'}
|
|
193
|
+
* ```
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```ts
|
|
197
|
+
* const formData = new FormData()
|
|
198
|
+
* formData.append('a.b', 'foo')
|
|
199
|
+
* parseFormData(formData)
|
|
200
|
+
* // => {a: {b: 'foo'}}
|
|
201
|
+
* ```
|
|
202
|
+
*
|
|
203
|
+
* @example
|
|
204
|
+
* ```ts
|
|
205
|
+
* const formData = new FormData()
|
|
206
|
+
* formData.append('a[0]', 'foo')
|
|
207
|
+
* formData.append('a[1]', 'bar')
|
|
208
|
+
* parseFormData(formData)
|
|
209
|
+
* // => {a: ['foo', 'bar']}
|
|
210
|
+
* ```
|
|
211
|
+
*
|
|
212
|
+
* @example
|
|
213
|
+
* ```ts
|
|
214
|
+
* const formData = new FormData()
|
|
215
|
+
* formData.append('a[]', 'foo')
|
|
216
|
+
* formData.append('a[]', 'bar')
|
|
217
|
+
* parseFormData(formData)
|
|
218
|
+
* // => {a: ['foo', 'bar']}
|
|
219
|
+
* ```
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* ```ts
|
|
223
|
+
* const formData = new FormData()
|
|
224
|
+
* formData.append('a[0]', 'foo')
|
|
225
|
+
* parseFormData(formData, {transformEntry: (path, value) => {path, value: value + 'bar'}})
|
|
226
|
+
* // => {a: ['foobar']}
|
|
227
|
+
* ```
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* ```ts
|
|
231
|
+
* const formData = new FormData()
|
|
232
|
+
* formData.append('a[0]', 'foo')
|
|
233
|
+
* formData.append('a[1]', '')
|
|
234
|
+
* parseFormData(formData, {removeEmptyString: true})
|
|
235
|
+
* // => {a: ['foo']}
|
|
236
|
+
* ```
|
|
237
|
+
*
|
|
238
|
+
* @param {Iterable<[string, string | File]>} formData - an iterator of an [`path`, `value`] tuple
|
|
239
|
+
* - `path` := `^(\+|\-|\&)?([^\.]+?(\[\d*\])*)(\.[^\.]+?(\[\d*\])*)*$` (e.g. `+a[][1].b`)
|
|
240
|
+
* - `value` := `string` or `File`
|
|
241
|
+
* @param {ParseFormDataOptions} options - options for parsing the form data
|
|
242
|
+
* - `transformEntry` - a function to transform the path and the value before
|
|
243
|
+
* inserting the value at the path in the resulting object (default: `defaultTransform`)
|
|
244
|
+
* - `removeEmptyString` - if `true` removes all entries where the value is an empty string
|
|
245
|
+
* @returns {JsonObject} the parsed JSON object
|
|
246
|
+
* @throws `DuplicateKeyError` if
|
|
247
|
+
* - a path part is an object and the path part is already defined as an object
|
|
248
|
+
* - a path part is an array and the path part is already defined as an array
|
|
249
|
+
* @throws `MixedArrayError` if at a specific path part an unordered array is
|
|
250
|
+
* defined and at a later path part an ordered array is defined or vice versa
|
|
251
|
+
* - e.g. `a[0]` and `a[]`
|
|
252
|
+
* - e.g. `a[]` and `a[0]`
|
|
253
|
+
*/
|
|
254
|
+
export declare function parseFormData(formData: Iterable<[string, string | File]>, { removeEmptyString, transformEntry, }?: ParseFormDataOptions): JsonObject;
|
|
255
|
+
export {};
|