FormData-4.0.10/000077500000000000000000000000001412533321300132665ustar00rootroot00000000000000FormData-4.0.10/.github/000077500000000000000000000000001412533321300146265ustar00rootroot00000000000000FormData-4.0.10/.github/FUNDING.yml000066400000000000000000000001511412533321300164400ustar00rootroot00000000000000github: [jimmywarting] custom: ["https://paypal.me/jimmywarting", "https://jimmy.warting.se/opensource"] FormData-4.0.10/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000014101412533321300204230ustar00rootroot00000000000000 ## The purpose of this PR is: ... ## This is what had to change: ... ## This is what like reviewers to know: ... ------------------------------------------------------------------------------------------------- - [ ] I prefixed the PR-title with `docs: `, `fix(area): `, `feat(area): ` or `breaking(area): ` - [ ] I updated ./CHANGELOG.md with a link to this PR or Issue - [ ] I updated the README.md - [ ] I Added unit test(s) ------------------------------------------------------------------------------------------------- - fix #000 FormData-4.0.10/.github/stale.yml000066400000000000000000000012541412533321300164630ustar00rootroot00000000000000# Number of days of inactivity before an issue becomes stale daysUntilStale: 30 # Number of days of inactivity before a stale issue is closed daysUntilClose: 7 # Issues with these labels will never be considered stale exemptLabels: - pinned - security # Label to use when marking an issue as stale staleLabel: wontfix # Comment to post when marking an issue as stale. Set to `false` to disable markComment: > This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. # Comment to post when closing a stale issue. Set to `false` to disable closeComment: false FormData-4.0.10/.github/workflows/000077500000000000000000000000001412533321300166635ustar00rootroot00000000000000FormData-4.0.10/.github/workflows/release.yml000066400000000000000000000007311412533321300210270ustar00rootroot00000000000000name: Release on: push: branches: - main - master - next - beta - "*.x" # maintenance releases such as 2.x jobs: release: name: release runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v2 with: node-version: 16 - run: npx semantic-release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} FormData-4.0.10/.gitignore000066400000000000000000000011251412533321300152550ustar00rootroot00000000000000# Logs logs *.log npm-debug.log* # Runtime data pids *.pid *.seed # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage # nyc test coverage .nyc_output # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt # node-waf configuration .lock-wscript # Compiled binary addons (http://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules jspm_packages # Optional npm cache directory .npm # Optional REPL history .node_repl_history # Compiled output FormData-4.0.10/CHANGELOG.md000066400000000000000000000014331412533321300151000ustar00rootroot00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Added - Start writing a changelog - ensure right number of arguments are provided - Started running test against WPTs own test ### Changed - Updated fetch-blob dep - downgraded optional chaining to support node 12.20 ### Removed - removed travis build process file - removed empty npmignore file ### Fixed - Pass blobs property bag over to file when converting it ## [4.0.5] - 2021-06-19 [Unreleased]: https://github.com/jimmywarting/FormData/compare/4.0.5...HEAD [4.0.5]: https://github.com/jimmywarting/FormData/compare/4.0.4...4.0.5 FormData-4.0.10/FormData.js000066400000000000000000000271421412533321300153270ustar00rootroot00000000000000/* formdata-polyfill. MIT License. Jimmy Wärting */ /* global FormData self Blob File */ /* eslint-disable no-inner-declarations */ if (typeof Blob !== 'undefined' && (typeof FormData === 'undefined' || !FormData.prototype.keys)) { const global = typeof globalThis === 'object' ? globalThis : typeof window === 'object' ? window : typeof self === 'object' ? self : this // keep a reference to native implementation const _FormData = global.FormData // To be monkey patched const _send = global.XMLHttpRequest && global.XMLHttpRequest.prototype.send const _fetch = global.Request && global.fetch const _sendBeacon = global.navigator && global.navigator.sendBeacon // Might be a worker thread... const _match = global.Element && global.Element.prototype // Unable to patch Request/Response constructor correctly #109 // only way is to use ES6 class extend // https://github.com/babel/babel/issues/1966 const stringTag = global.Symbol && Symbol.toStringTag // Add missing stringTags to blob and files if (stringTag) { if (!Blob.prototype[stringTag]) { Blob.prototype[stringTag] = 'Blob' } if ('File' in global && !File.prototype[stringTag]) { File.prototype[stringTag] = 'File' } } // Fix so you can construct your own File try { new File([], '') // eslint-disable-line } catch (a) { global.File = function File (b, d, c) { const blob = new Blob(b, c || {}) const t = c && void 0 !== c.lastModified ? new Date(c.lastModified) : new Date() Object.defineProperties(blob, { name: { value: d }, lastModified: { value: +t }, toString: { value () { return '[object File]' } } }) if (stringTag) { Object.defineProperty(blob, stringTag, { value: 'File' }) } return blob } } function ensureArgs (args, expected) { if (args.length < expected) { throw new TypeError(`${expected} argument required, but only ${args.length} present.`) } } /** * @param {string} name * @param {string | undefined} filename * @returns {[string, File|string]} */ function normalizeArgs (name, value, filename) { if (value instanceof Blob) { filename = filename !== undefined ? String(filename + '') : typeof value.name === 'string' ? value.name : 'blob' if (value.name !== filename || Object.prototype.toString.call(value) === '[object Blob]') { value = new File([value], filename) } return [String(name), value] } return [String(name), String(value)] } // normalize line feeds for textarea // https://html.spec.whatwg.org/multipage/form-elements.html#textarea-line-break-normalisation-transformation function normalizeLinefeeds (value) { return value.replace(/\r?\n|\r/g, '\r\n') } /** * @template T * @param {ArrayLike} arr * @param {{ (elm: T): void; }} cb */ function each (arr, cb) { for (let i = 0; i < arr.length; i++) { cb(arr[i]) } } const escape = str => str.replace(/\n/g, '%0A').replace(/\r/g, '%0D').replace(/"/g, '%22') /** * @implements {Iterable} */ class FormDataPolyfill { /** * FormData class * * @param {HTMLFormElement=} form */ constructor (form) { /** @type {[string, string|File][]} */ this._data = [] const self = this form && each(form.elements, (/** @type {HTMLInputElement} */ elm) => { if ( !elm.name || elm.disabled || elm.type === 'submit' || elm.type === 'button' || elm.matches('form fieldset[disabled] *') ) return if (elm.type === 'file') { const files = elm.files && elm.files.length ? elm.files : [new File([], '', { type: 'application/octet-stream' })] // #78 each(files, file => { self.append(elm.name, file) }) } else if (elm.type === 'select-multiple' || elm.type === 'select-one') { each(elm.options, opt => { !opt.disabled && opt.selected && self.append(elm.name, opt.value) }) } else if (elm.type === 'checkbox' || elm.type === 'radio') { if (elm.checked) self.append(elm.name, elm.value) } else { const value = elm.type === 'textarea' ? normalizeLinefeeds(elm.value) : elm.value self.append(elm.name, value) } }) } /** * Append a field * * @param {string} name field name * @param {string|Blob|File} value string / blob / file * @param {string=} filename filename to use with blob * @return {undefined} */ append (name, value, filename) { ensureArgs(arguments, 2) this._data.push(normalizeArgs(name, value, filename)) } /** * Delete all fields values given name * * @param {string} name Field name * @return {undefined} */ delete (name) { ensureArgs(arguments, 1) const result = [] name = String(name) each(this._data, entry => { entry[0] !== name && result.push(entry) }) this._data = result } /** * Iterate over all fields as [name, value] * * @return {Iterator} */ * entries () { for (var i = 0; i < this._data.length; i++) { yield this._data[i] } } /** * Iterate over all fields * * @param {Function} callback Executed for each item with parameters (value, name, thisArg) * @param {Object=} thisArg `this` context for callback function */ forEach (callback, thisArg) { ensureArgs(arguments, 1) for (const [name, value] of this) { callback.call(thisArg, value, name, this) } } /** * Return first field value given name * or null if non existent * * @param {string} name Field name * @return {string|File|null} value Fields value */ get (name) { ensureArgs(arguments, 1) const entries = this._data name = String(name) for (let i = 0; i < entries.length; i++) { if (entries[i][0] === name) { return entries[i][1] } } return null } /** * Return all fields values given name * * @param {string} name Fields name * @return {Array} [{String|File}] */ getAll (name) { ensureArgs(arguments, 1) const result = [] name = String(name) each(this._data, data => { data[0] === name && result.push(data[1]) }) return result } /** * Check for field name existence * * @param {string} name Field name * @return {boolean} */ has (name) { ensureArgs(arguments, 1) name = String(name) for (let i = 0; i < this._data.length; i++) { if (this._data[i][0] === name) { return true } } return false } /** * Iterate over all fields name * * @return {Iterator} */ * keys () { for (const [name] of this) { yield name } } /** * Overwrite all values given name * * @param {string} name Filed name * @param {string} value Field value * @param {string=} filename Filename (optional) */ set (name, value, filename) { ensureArgs(arguments, 2) name = String(name) /** @type {[string, string|File][]} */ const result = [] const args = normalizeArgs(name, value, filename) let replace = true // - replace the first occurrence with same name // - discards the remaining with same name // - while keeping the same order items where added each(this._data, data => { data[0] === name ? replace && (replace = !result.push(args)) : result.push(data) }) replace && result.push(args) this._data = result } /** * Iterate over all fields * * @return {Iterator} */ * values () { for (const [, value] of this) { yield value } } /** * Return a native (perhaps degraded) FormData with only a `append` method * Can throw if it's not supported * * @return {FormData} */ ['_asNative'] () { const fd = new _FormData() for (const [name, value] of this) { fd.append(name, value) } return fd } /** * [_blob description] * * @return {Blob} [description] */ ['_blob'] () { const boundary = '----formdata-polyfill-' + Math.random(), chunks = [], p = `--${boundary}\r\nContent-Disposition: form-data; name="` this.forEach((value, name) => typeof value == 'string' ? chunks.push(p + escape(normalizeLinefeeds(name)) + `"\r\n\r\n${normalizeLinefeeds(value)}\r\n`) : chunks.push(p + escape(normalizeLinefeeds(name)) + `"; filename="${escape(value.name)}"\r\nContent-Type: ${value.type||"application/octet-stream"}\r\n\r\n`, value, `\r\n`)) chunks.push(`--${boundary}--`) return new Blob(chunks, { type: "multipart/form-data; boundary=" + boundary }) } /** * The class itself is iterable * alias for formdata.entries() * * @return {Iterator} */ [Symbol.iterator] () { return this.entries() } /** * Create the default string description. * * @return {string} [object FormData] */ toString () { return '[object FormData]' } } if (_match && !_match.matches) { _match.matches = _match.matchesSelector || _match.mozMatchesSelector || _match.msMatchesSelector || _match.oMatchesSelector || _match.webkitMatchesSelector || function (s) { var matches = (this.document || this.ownerDocument).querySelectorAll(s) var i = matches.length while (--i >= 0 && matches.item(i) !== this) {} return i > -1 } } if (stringTag) { /** * Create the default string description. * It is accessed internally by the Object.prototype.toString(). */ FormDataPolyfill.prototype[stringTag] = 'FormData' } // Patch xhr's send method to call _blob transparently if (_send) { const setRequestHeader = global.XMLHttpRequest.prototype.setRequestHeader global.XMLHttpRequest.prototype.setRequestHeader = function (name, value) { setRequestHeader.call(this, name, value) if (name.toLowerCase() === 'content-type') this._hasContentType = true } global.XMLHttpRequest.prototype.send = function (data) { // need to patch send b/c old IE don't send blob's type (#44) if (data instanceof FormDataPolyfill) { const blob = data['_blob']() if (!this._hasContentType) this.setRequestHeader('Content-Type', blob.type) _send.call(this, blob) } else { _send.call(this, data) } } } // Patch fetch's function to call _blob transparently if (_fetch) { global.fetch = function (input, init) { if (init && init.body && init.body instanceof FormDataPolyfill) { init.body = init.body['_blob']() } return _fetch.call(this, input, init) } } // Patch navigator.sendBeacon to use native FormData if (_sendBeacon) { global.navigator.sendBeacon = function (url, data) { if (data instanceof FormDataPolyfill) { data = data['_asNative']() } return _sendBeacon.call(this, url, data) } } global['FormData'] = FormDataPolyfill } FormData-4.0.10/LICENSE000066400000000000000000000020731412533321300142750ustar00rootroot00000000000000MIT License Copyright (c) 2016 Jimmy Karl Roland Wärting Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. FormData-4.0.10/README.md000066400000000000000000000122571412533321300145540ustar00rootroot00000000000000### A `FormData` polyfill for the browser ...and a module for NodeJS (`New!`) ```bash npm install formdata-polyfill ``` The browser polyfill will likely have done its part already, and i hope you stop supporting old browsers c",)
But NodeJS still laks a proper FormData
The good old form-data package is a very old and isn't spec compatible and dose some abnormal stuff to construct and read FormData instances that other http libraries are not happy about when it comes to follow the spec. ### The NodeJS / ESM version - The modular (~2.3 KiB minified uncompressed) version of this package is independent of any browser stuff and don't patch anything - It's as pure/spec compatible as it possible gets the test are run by WPT. - It's compatible with [node-fetch](https://github.com/node-fetch/node-fetch). - It have higher platform dependencies as it uses classes, symbols, ESM & private fields - Only dependency it has is [fetch-blob](https://github.com/node-fetch/fetch-blob) ```js // Node example import fetch from 'node-fetch' import File from 'fetch-blob/file.js' import { fileFromSync } from 'fetch-blob/from.js' import { FormData } from 'formdata-polyfill/esm.min.js' const file = fileFromSync('./README.md') const fd = new FormData() fd.append('file-upload', new File(['abc'], 'hello-world.txt')) fd.append('file-upload', file) // it's also possible to append file/blob look-a-like items // if you have streams coming from other destinations fd.append('file-upload', { size: 123, type: '', name: 'cat-video.mp4', stream() { return stream }, [Symbol.toStringTag]: 'File' }) fetch('https://httpbin.org/post', { method: 'POST', body: fd }) ``` ---- It also comes with way to convert FormData into Blobs - it's not something that every developer should have to deal with. It's mainly for [node-fetch](https://github.com/node-fetch/node-fetch) and other http library to ease the process of serializing a FormData into a blob and just wish to deal with Blobs instead (Both Deno and Undici adapted a version of this [formDataToBlob](https://github.com/jimmywarting/FormData/blob/5ddea9e0de2fc5e246ab1b2f9d404dee0c319c02/formdata-to-blob.js) to the core and passes all WPT tests run by the browser itself) ```js import { Readable } from 'node:stream' import { FormData, formDataToBlob } from 'formdata-polyfill/esm.min.js' const blob = formDataToBlob(new FormData()) fetch('https://httpbin.org/post', { method: 'POST', body: blob }) // node built in http and other similar http library have to do: const stream = Readable.from(blob.stream()) const req = http.request('http://httpbin.org/post', { method: 'post', headers: { 'Content-Length': blob.size, 'Content-Type': blob.type } }) stream.pipe(req) ``` PS: blob & file that are appended to the FormData will not be read until any of the serialized blob read-methods gets called ...so uploading very large files is no biggie ### Browser polyfill usage: ```js import 'formdata-polyfill' // that's it ``` The browser polyfill conditionally replaces the native implementation rather than fixing the missing functions, since otherwise there is no way to get or delete existing values in the FormData object. Therefore this also patches `XMLHttpRequest.prototype.send` and `fetch` to send the `FormData` as a blob, and `navigator.sendBeacon` to send native `FormData`. I was unable to patch the Response/Request constructor so if you are constructing them with FormData then you need to call `fd._blob()` manually. ```js new Request(url, { method: 'post', body: fd._blob ? fd._blob() : fd }) ``` Dependencies --- If you need to support IE <= 9 then I recommend you to include eligrey's [blob.js] (which i hope you don't - since IE is now dead)
Updating from 2.x to 3.x Previously you had to import the polyfill and use that, since it didn't replace the global (existing) FormData implementation. But now it transparently calls `_blob()` for you when you are sending something with fetch or XHR, by way of monkey-patching the `XMLHttpRequest.prototype.send` and `fetch` functions. So you maybe had something like this: ```javascript var FormData = require('formdata-polyfill') var fd = new FormData(form) xhr.send(fd._blob()) ``` There is no longer anything exported from the module (though you of course still need to import it to install the polyfill), so you can now use the FormData object as normal: ```javascript require('formdata-polyfill') var fd = new FormData(form) xhr.send(fd) ```
Native Browser compatibility (as of 2021-05-08) --- Based on this you can decide for yourself if you need this polyfill. [![screenshot](https://user-images.githubusercontent.com/1148376/117550329-0993aa80-b040-11eb-976c-14e31f1a3ba4.png)](https://developer.mozilla.org/en-US/docs/Web/API/FormData#Browser_compatibility) This normalizes support for the FormData API: - `append` with filename - `delete()`, `get()`, `getAll()`, `has()`, `set()` - `entries()`, `keys()`, `values()`, and support for `for...of` - Available in web workers (just include the polyfill) [npm-image]: https://img.shields.io/npm/v/formdata-polyfill.svg [npm-url]: https://www.npmjs.com/package/formdata-polyfill [blob.js]: https://github.com/eligrey/Blob.js FormData-4.0.10/build.js000066400000000000000000000011771412533321300147310ustar00rootroot00000000000000import { writeFileSync } from 'node:fs' import closure from 'google-closure-compiler' const ClosureCompiler = closure.compiler const closureCompiler = new ClosureCompiler({ js: 'FormData.js', warning_level: 'QUIET', output_wrapper: '/*! formdata-polyfill. MIT License. Jimmy Wärting */\n;(function(){%output%})();', compilation_level: 'ADVANCED' }) closureCompiler.run((exitCode, stdOut, stdErr) => { if (exitCode) { console.log('FATAL An error occurred trying to use closure compiler') console.log(stdErr) process.exit(-2) } writeFileSync('formdata.min.js', stdOut) }) FormData-4.0.10/esm.min.d.ts000066400000000000000000000002211412533321300154210ustar00rootroot00000000000000export declare const FormData: { new (): FormData; prototype: FormData; }; export declare function formDataToBlob(formData: FormData): Blob; FormData-4.0.10/esm.min.js000066400000000000000000000045121412533321300151740ustar00rootroot00000000000000/*! formdata-polyfill. MIT License. Jimmy Wärting */ import C from 'fetch-blob' import F from 'fetch-blob/file.js' var {toStringTag:t,iterator:i,hasInstance:h}=Symbol, r=Math.random, m='append,set,get,getAll,delete,keys,values,entries,forEach,constructor'.split(','), f=(a,b,c)=>(a+='',/^(Blob|File)$/.test(b && b[t])?[(c=c!==void 0?c+'':b[t]=='File'?b.name:'blob',a),b.name!==c||b[t]=='blob'?new F([b],c,b):b]:[a,b+'']), e=(c,f)=>(f?c:c.replace(/\r?\n|\r/g,'\r\n')).replace(/\n/g,'%0A').replace(/\r/g,'%0D').replace(/"/g,'%22'), x=(n, a, e)=>{if(a.lengthtypeof o[m]!='function')} append(...a){x('append',arguments,2);this.#d.push(f(...a))} delete(a){x('delete',arguments,1);a+='';this.#d=this.#d.filter(([b])=>b!==a)} get(a){x('get',arguments,1);a+='';for(var b=this.#d,l=b.length,c=0;cc[0]===a&&b.push(c[1]));return b} has(a){x('has',arguments,1);a+='';return this.#d.some(b=>b[0]===a)} forEach(a,b){x('forEach',arguments,1);for(var [c,d]of this)a.call(b,d,c,this)} set(...a){x('set',arguments,2);var b=[],c=!0;a=f(...a);this.#d.forEach(d=>{d[0]===a[0]?c&&(c=!b.push(a)):b.push(d)});c&&b.push(a);this.#d=b} *entries(){yield*this.#d} *keys(){for(var[a]of this)yield a} *values(){for(var[,a]of this)yield a}} /** @param {FormData} F */ export function formDataToBlob (F,B=C){ var b=`${r()}${r()}`.replace(/\./g, '').slice(-28).padStart(32, '-'),c=[],p=`--${b}\r\nContent-Disposition: form-data; name="` F.forEach((v,n)=>typeof v=='string' ?c.push(p+e(n)+`"\r\n\r\n${v.replace(/\r(?!\n)|(? */ const escape = (str, filename) => (filename ? str : str.replace(/\r?\n|\r/g, '\r\n')) .replace(/\n/g, '%0A') .replace(/\r/g, '%0D') .replace(/"/g, '%22') /** * pure function to convert any formData instance to a Blob * instances synchronous without reading all of the files * * @param {FormData|*} formData an instance of a formData Class * @param {Blob|*} [BlobClass=Blob] the Blob class to use when constructing it */ export function formDataToBlob (formData, BlobClass = Blob) { const boundary = ('----formdata-polyfill-' + Math.random()) const chunks = [] const prefix = `--${boundary}\r\nContent-Disposition: form-data; name="` for (let [name, value] of formData) { if (typeof value === 'string') { chunks.push(prefix + escape(name) + `"\r\n\r\n${value.replace(/\r(?!\n)|(?=4" } }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" }, "engines": { "node": ">=4" } }, "node_modules/clone": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", "dev": true, "engines": { "node": ">=0.8" } }, "node_modules/clone-buffer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", "dev": true, "engines": { "node": ">= 0.10" } }, "node_modules/clone-stats": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", "dev": true }, "node_modules/cloneable-readable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", "dev": true, "dependencies": { "inherits": "^2.0.1", "process-nextick-args": "^2.0.0", "readable-stream": "^2.3.5" } }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "dependencies": { "color-name": "1.1.3" } }, "node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, "node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true, "engines": { "node": ">=0.8.0" } }, "node_modules/fetch-blob": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.1.2.tgz", "integrity": "sha512-hunJbvy/6OLjCD0uuhLdp0mMPzP/yd2ssd1t2FCJsaA7wkWhpbp9xfuNVpv7Ll4jFhzp6T4LAupSiV9uOeg0VQ==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/jimmywarting" }, { "type": "paypal", "url": "https://paypal.me/jimmywarting" } ], "dependencies": { "web-streams-polyfill": "^3.0.3" }, "engines": { "node": "^12.20 || >= 14.13" } }, "node_modules/google-closure-compiler": { "version": "20210808.0.0", "resolved": "https://registry.npmjs.org/google-closure-compiler/-/google-closure-compiler-20210808.0.0.tgz", "integrity": "sha512-+R2+P1tT1lEnDDGk8b+WXfyVZgWjcCK9n1mmZe8pMEzPaPWxqK7GMetLVWnqfTDJ5Q+LRspOiFBv3Is+0yuhCA==", "dev": true, "dependencies": { "chalk": "2.x", "google-closure-compiler-java": "^20210808.0.0", "minimist": "1.x", "vinyl": "2.x", "vinyl-sourcemaps-apply": "^0.2.0" }, "bin": { "google-closure-compiler": "cli.js" }, "engines": { "node": ">=10" }, "optionalDependencies": { "google-closure-compiler-linux": "^20210808.0.0", "google-closure-compiler-osx": "^20210808.0.0", "google-closure-compiler-windows": "^20210808.0.0" } }, "node_modules/google-closure-compiler-java": { "version": "20210808.0.0", "resolved": "https://registry.npmjs.org/google-closure-compiler-java/-/google-closure-compiler-java-20210808.0.0.tgz", "integrity": "sha512-7dEQfBzOdwdjwa/Pq8VAypNBKyWRrOcKjnNYOO9gEg2hjh8XVMeQzTqw4uANfVvvANGdE/JjD+HF6zHVgLRwjg==", "dev": true }, "node_modules/google-closure-compiler-linux": { "version": "20210808.0.0", "resolved": "https://registry.npmjs.org/google-closure-compiler-linux/-/google-closure-compiler-linux-20210808.0.0.tgz", "integrity": "sha512-byKi5ITUiWRvEIcQo76i1siVnOwrTmG+GNcBG4cJ7x8IE6+4ki9rG5pUe4+DOYHkfk52XU6XHt9aAAgCcFDKpg==", "cpu": [ "x64", "x86" ], "dev": true, "optional": true, "os": [ "linux" ] }, "node_modules/google-closure-compiler-osx": { "version": "20210808.0.0", "resolved": "https://registry.npmjs.org/google-closure-compiler-osx/-/google-closure-compiler-osx-20210808.0.0.tgz", "integrity": "sha512-iwyAY6dGj1FrrBdmfwKXkjtTGJnqe8F+9WZbfXxiBjkWLtIsJt2dD1+q7g/sw3w8mdHrGQAdxtDZP/usMwj/Rg==", "cpu": [ "x64", "x86", "arm64" ], "dev": true, "optional": true, "os": [ "darwin" ] }, "node_modules/google-closure-compiler-windows": { "version": "20210808.0.0", "resolved": "https://registry.npmjs.org/google-closure-compiler-windows/-/google-closure-compiler-windows-20210808.0.0.tgz", "integrity": "sha512-VI+UUYwtGWDYwpiixrWRD8EklHgl6PMbiEaHxQSrQbH8PDXytwaOKqmsaH2lWYd5Y/BOZie2MzjY7F5JI69q1w==", "cpu": [ "x64" ], "dev": true, "optional": true, "os": [ "win32" ] }, "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true, "engines": { "node": ">=4" } }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, "node_modules/minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, "node_modules/readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "node_modules/remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", "dev": true }, "node_modules/replace-ext": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", "dev": true, "engines": { "node": ">= 0.10" } }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, "node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "dependencies": { "safe-buffer": "~5.1.0" } }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "dependencies": { "has-flag": "^3.0.0" }, "engines": { "node": ">=4" } }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, "node_modules/vinyl": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", "dev": true, "dependencies": { "clone": "^2.1.1", "clone-buffer": "^1.0.0", "clone-stats": "^1.0.0", "cloneable-readable": "^1.0.0", "remove-trailing-separator": "^1.0.1", "replace-ext": "^1.0.0" }, "engines": { "node": ">= 0.10" } }, "node_modules/vinyl-sourcemaps-apply": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", "integrity": "sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU=", "dev": true, "dependencies": { "source-map": "^0.5.1" } }, "node_modules/web-streams-polyfill": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.1.0.tgz", "integrity": "sha512-wO9r1YnYe7kFBLHyyVEhV1H8VRWoNiNnuP+v/HUUmSTaRF8F93Kmd3JMrETx0f11GXxRek6OcL2QtjFIdc5WYw==", "engines": { "node": ">= 8" } } }, "dependencies": { "@types/google-closure-compiler": { "version": "0.0.19", "resolved": "https://registry.npmjs.org/@types/google-closure-compiler/-/google-closure-compiler-0.0.19.tgz", "integrity": "sha512-5fs1oWR2H84+54UU0fWKlGxjekBH03+nyWav8+0xlUDl0lEQ9abI2cUc7SJJsnf7lC/cJofxeTQ2R4x3vWENmw==", "dev": true, "requires": { "@types/node": "*" } }, "@types/node": { "version": "16.7.10", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.10.tgz", "integrity": "sha512-S63Dlv4zIPb8x6MMTgDq5WWRJQe56iBEY0O3SOFA9JrRienkOVDXSXBjjJw6HTNQYSE2JI6GMCR6LVbIMHJVvA==", "dev": true }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { "color-convert": "^1.9.0" } }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "clone": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", "dev": true }, "clone-buffer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", "dev": true }, "clone-stats": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", "dev": true }, "cloneable-readable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", "dev": true, "requires": { "inherits": "^2.0.1", "process-nextick-args": "^2.0.0", "readable-stream": "^2.3.5" } }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "requires": { "color-name": "1.1.3" } }, "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, "core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, "fetch-blob": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.1.2.tgz", "integrity": "sha512-hunJbvy/6OLjCD0uuhLdp0mMPzP/yd2ssd1t2FCJsaA7wkWhpbp9xfuNVpv7Ll4jFhzp6T4LAupSiV9uOeg0VQ==", "requires": { "web-streams-polyfill": "^3.0.3" } }, "google-closure-compiler": { "version": "20210808.0.0", "resolved": "https://registry.npmjs.org/google-closure-compiler/-/google-closure-compiler-20210808.0.0.tgz", "integrity": "sha512-+R2+P1tT1lEnDDGk8b+WXfyVZgWjcCK9n1mmZe8pMEzPaPWxqK7GMetLVWnqfTDJ5Q+LRspOiFBv3Is+0yuhCA==", "dev": true, "requires": { "chalk": "2.x", "google-closure-compiler-java": "^20210808.0.0", "google-closure-compiler-linux": "^20210808.0.0", "google-closure-compiler-osx": "^20210808.0.0", "google-closure-compiler-windows": "^20210808.0.0", "minimist": "1.x", "vinyl": "2.x", "vinyl-sourcemaps-apply": "^0.2.0" } }, "google-closure-compiler-java": { "version": "20210808.0.0", "resolved": "https://registry.npmjs.org/google-closure-compiler-java/-/google-closure-compiler-java-20210808.0.0.tgz", "integrity": "sha512-7dEQfBzOdwdjwa/Pq8VAypNBKyWRrOcKjnNYOO9gEg2hjh8XVMeQzTqw4uANfVvvANGdE/JjD+HF6zHVgLRwjg==", "dev": true }, "google-closure-compiler-linux": { "version": "20210808.0.0", "resolved": "https://registry.npmjs.org/google-closure-compiler-linux/-/google-closure-compiler-linux-20210808.0.0.tgz", "integrity": "sha512-byKi5ITUiWRvEIcQo76i1siVnOwrTmG+GNcBG4cJ7x8IE6+4ki9rG5pUe4+DOYHkfk52XU6XHt9aAAgCcFDKpg==", "dev": true, "optional": true }, "google-closure-compiler-osx": { "version": "20210808.0.0", "resolved": "https://registry.npmjs.org/google-closure-compiler-osx/-/google-closure-compiler-osx-20210808.0.0.tgz", "integrity": "sha512-iwyAY6dGj1FrrBdmfwKXkjtTGJnqe8F+9WZbfXxiBjkWLtIsJt2dD1+q7g/sw3w8mdHrGQAdxtDZP/usMwj/Rg==", "dev": true, "optional": true }, "google-closure-compiler-windows": { "version": "20210808.0.0", "resolved": "https://registry.npmjs.org/google-closure-compiler-windows/-/google-closure-compiler-windows-20210808.0.0.tgz", "integrity": "sha512-VI+UUYwtGWDYwpiixrWRD8EklHgl6PMbiEaHxQSrQbH8PDXytwaOKqmsaH2lWYd5Y/BOZie2MzjY7F5JI69q1w==", "dev": true, "optional": true }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, "minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", "dev": true }, "replace-ext": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", "dev": true }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" } }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" } }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, "vinyl": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", "dev": true, "requires": { "clone": "^2.1.1", "clone-buffer": "^1.0.0", "clone-stats": "^1.0.0", "cloneable-readable": "^1.0.0", "remove-trailing-separator": "^1.0.1", "replace-ext": "^1.0.0" } }, "vinyl-sourcemaps-apply": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", "integrity": "sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU=", "dev": true, "requires": { "source-map": "^0.5.1" } }, "web-streams-polyfill": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.1.0.tgz", "integrity": "sha512-wO9r1YnYe7kFBLHyyVEhV1H8VRWoNiNnuP+v/HUUmSTaRF8F93Kmd3JMrETx0f11GXxRek6OcL2QtjFIdc5WYw==" } } } FormData-4.0.10/package.json000066400000000000000000000023521412533321300155560ustar00rootroot00000000000000{ "name": "formdata-polyfill", "version": "4.0.6", "description": "HTML5 `FormData` for Browsers and Node.", "type": "module", "main": "formdata.min.js", "scripts": { "build": "node build.js", "test": "node test/test-esm.js", "test-wpt": "node --experimental-loader ./test/http-loader.js ./test/test-wpt-in-node.js", "test-polyfill": "php -S localhost:4445 & open http://localhost:4445/test/test-polyfill.html" }, "repository": { "type": "git", "url": "git+https://jimmywarting@github.com/jimmywarting/FormData.git" }, "files": [ "esm.min.js", "esm.min.d.ts", "FormData.js", "formdata-to-blob.js", "formdata.min.js", "README.md" ], "engines": { "node": ">=12.20.0" }, "keywords": [ "formdata", "fetch", "node-fetch", "html5", "browser", "polyfill" ], "author": "Jimmy Wärting", "license": "MIT", "bugs": { "url": "https://github.com/jimmywarting/FormData/issues" }, "homepage": "https://github.com/jimmywarting/FormData#readme", "dependencies": { "fetch-blob": "^3.1.2" }, "devDependencies": { "@types/google-closure-compiler": "^0.0.19", "@types/node": "^16.7.10", "google-closure-compiler": "^20210808.0.0" } } FormData-4.0.10/test/000077500000000000000000000000001412533321300142455ustar00rootroot00000000000000FormData-4.0.10/test/http-loader.js000066400000000000000000000032751412533321300170350ustar00rootroot00000000000000// https-loader.mjs import { get } from 'https'; export function resolve(specifier, context, defaultResolve) { const { parentURL = null } = context; // Normally Node.js would error on specifiers starting with 'https://', so // this hook intercepts them and converts them into absolute URLs to be // passed along to the later hooks below. if (specifier.startsWith('https://')) { return { url: specifier }; } else if (parentURL && parentURL.startsWith('https://')) { return { url: new URL(specifier, parentURL).href }; } // Let Node.js handle all other specifiers. return defaultResolve(specifier, context, defaultResolve); } export function getFormat(url, context, defaultGetFormat) { // This loader assumes all network-provided JavaScript is ES module code. if (url.startsWith('https://')) { return { format: 'module' }; } // Let Node.js handle all other URLs. return defaultGetFormat(url, context, defaultGetFormat); } export function getSource(url, context, defaultGetSource) { // For JavaScript to be loaded over the network, we need to fetch and // return it. if (url.startsWith('https://')) { const {searchParams} = new URL(url) return new Promise((resolve, reject) => { let data = 'globalThis.self = globalThis;' get(url, async res => { for await (const chunk of res) data += chunk; searchParams.getAll('expose').forEach(variable => { data += `\n;globalThis.${variable} = ${variable};` }) resolve({ source: data }); }).on('error', (err) => reject(err)); }); } // Let Node.js handle all other URLs. return defaultGetSource(url, context, defaultGetSource); } FormData-4.0.10/test/test-esm.js000066400000000000000000000312251412533321300163470ustar00rootroot00000000000000import Blob from 'fetch-blob' import File from 'fetch-blob/file.js' import {FormData, formDataToBlob} from '../esm.min.js' console.assert( formDataToBlob(createFormData(['key', 'value1'])).size > 100, 'It can create a blob from FormData' ) // All of the below code can be executed in the browser as well to spot if the test are doing anything unexpected function createFormData (...args) { const fd = new FormData() for (const arg of args) { fd.append(...arg) } return fd } console.assert( createFormData(['key', 'value1']).get('key') === 'value1', 'GET: Getting a key value return value1' ) console.assert( createFormData(['key', '1st'], ['key', '2nd']).get('key') === '1st', 'GET: Getting one of the keys value return the first value' ) console.assert( createFormData().get('key') === null, `GET: Getting a key that don't exist returns null` ) for (let x of [undefined, null, 0, '', {}, false, true, globalThis, NaN]) { console.assert( createFormData(['key', x]).get('key') === String(x), `APPEND: it should normalize none blob values to string` ) } // #120 escapes keys when encoding FormData for (let [key, expected] of [['key\n', 'key%0D%0A'], ['key\r', 'key%0D%0A'], ['key"', 'key%22']]) { const fd = createFormData([key, 'value']) const str = await formDataToBlob(fd).text() console.assert(str.includes(expected) === true) } // #120 escapes filename encoding FormData for (let [filename, expected] of [['val\nue', 'val%0Aue'], ['val%0Aue', 'val%0Aue']]) { const fd = createFormData(['key', new File([], filename)]) const str = await formDataToBlob(fd).text() console.assert(str.includes(expected)) } { // Appending a empty blob with a name should convert it to a File var val = createFormData(['key', new Blob(), 'blank.txt']).get('key') console.assert(val[Symbol.toStringTag], 'The value should have a symbol toStringTag') console.assert(val[Symbol.toStringTag] === 'File', 'the symbol.toStringTag should be "File"') console.assert(val.name === 'blank.txt', 'File name should be blank.txt') console.assert(val.type === '', 'The blob type should be unspecified') console.assert(val.size === 0, 'The size is empty') } { var fd = createFormData() console.assert(fd.has('key1') === false, `HAS: a key that have not been appended or set returns false`) fd.append('key1', 'value') console.assert(fd.has('key1') === true, `HAS: it has a value after appending to it`) console.assert(fd.has('key2') === false, `HAS: a key that have not been appended or set returns false`) fd.append('key2', 'value') console.assert(fd.has('key1') === true) console.assert(fd.has('key2') === true) fd.append('key3', new Blob(['content'])) console.assert(fd.has('key3') === true) } class FormDataLike { append(){} set(){} get(){} getAll(){} delete(){} keys(){} values(){} entries(){} forEach(){} get [Symbol.toStringTag](){ return 'FormData' } } console.assert(new FormDataLike() instanceof FormData, 'It should be a formdata like object') console.assert(!(null instanceof FormData), 'null check dont throw') console.assert(new FormData() instanceof FormData, 'instance check works') { const msg = 'should return the keys/values/entres in the order they was appended' const entries = [ ['keyA', 'val1'], ['keyA', 'val2'], ['keyB', 'val3'], ['keyA', 'val4'] ] const fd = createFormData(...entries) const keys = [...fd.keys()]+'' const values = [...fd.values()]+'' const result = [...fd]+'' console.assert(keys === 'keyA,keyA,keyB,keyA', msg) console.assert(values === 'val1,val2,val3,val4', msg) console.assert(result === 'keyA,val1,keyA,val2,keyB,val3,keyA,val4', msg) } { const fd = createFormData( ['keyA', 'val1'], ['keyA', 'val2'], ['keyB', 'val3'], ['keyA', 'val4'] ) fd.set('keyA', 'val3') console.assert([...fd]+'' === 'keyA,val3,keyB,val3', 'SET: Overwrite first matching key') } { const fd = createFormData(['keyB', 'val3']) fd.set('keyA', 'val3') console.assert([...fd]+'' === 'keyB,val3,keyA,val3', 'SET: appends value when no matching key exist') } { const fd = createFormData(['name', 'value']) console.assert(fd.has('name') === true) fd.delete('name') console.assert(fd.has('name') === false) fd.append('name', new Blob(['content'])) console.assert(fd.has('name') === true) fd.delete('name') console.assert(fd.has('name') === false) fd.append('n1', 'v1') fd.append('n2', 'v2') fd.append('n1', 'v3') fd.delete('n1') console.assert(fd.has('n1') === false) console.assert([...fd]+'' === 'n2,v2') } { const value = createFormData(['key', '\n']).get('key') console.assert('\n' === value, 'Should not convert LF to CRLF when provided by append') } { const value = createFormData(['key', '\r']).get('key') console.assert('\r' === value, 'Should not convert CR to CRLF when provided by append') } { const file = createFormData(['key', new File([], 'doc.txt')]).get('key') console.assert('doc.txt' === file.name, 'should return correct filename with File') } { const file = createFormData(['key', new Blob(), 'doc.txt']).get('key') console.assert('doc.txt' === file.name, 'should return correct filename with Blob filename') } { const file = createFormData(['key', new Blob()]).get('key') console.assert('blob' === file.name, 'should return correct filename with just Blob') } { const fd = createFormData(['key', new Blob()]) console.assert(fd.get('key') === fd.get('key'), 'should return same instances') } { var fd = new FormData() fd.set('a', 'a\na') fd.set('b', 'b\rb') fd.set('c', 'c\n\rc') console.assert(fd.get('a') === 'a\na') console.assert(fd.get('b') === 'b\rb') console.assert(fd.get('c') === 'c\n\rc') formDataToBlob(fd).text().then(str => { console.assert(str.includes('a\r\na')) console.assert(str.includes('b\r\nb')) console.assert(str.includes('c\r\n\r\nc')) }) } const kTestChars = "ABC~‾¥≈¤・・•∙·☼★星🌟星★☼·∙•・・¤≈¥‾~XYZ" // formDataPostFileUploadTest - verifies multipart upload structure and // numeric character reference replacement for filenames, field names, // and field values using FormData and fetch(). // // Uses /fetch/api/resources/echo-content.py to echo the upload // POST (unlike in send-file-form-helper.js, here we expect all // multipart/form-data request bodies to be UTF-8, so we don't need to // escape controls and non-ASCII bytes). // // Fields in the parameter object: // // - fileNameSource: purely explanatory and gives a clue about which // character encoding is the source for the non-7-bit-ASCII parts of // the fileBaseName, or Unicode if no smaller-than-Unicode source // contains all the characters. Used in the test name. // - fileBaseName: the not-necessarily-just-7-bit-ASCII file basename // used for the constructed test file. Used in the test name. async function formDataPostFileUploadTest(fileBaseName, asBlob = false) { const formData = new FormData(); const file = new File([kTestChars], fileBaseName, { type: "text/plain" }); // Used to verify that the browser agrees with the test about // field value replacement and encoding independently of file system // idiosyncracies. formData.append("filename", fileBaseName); // Same, but with name and value reversed to ensure field names // get the same treatment. formData.append(fileBaseName, "filename"); formData.append("file", file, fileBaseName); const formDataText = await formDataToBlob(formData).text() const formDataLines = formDataText.split("\r\n") if (formDataLines.length && !formDataLines[formDataLines.length - 1]) { formDataLines.length-- } console.assert(formDataLines.length > 2, `${fileBaseName}: multipart form data must have at least 3 lines: ${ JSON.stringify(formDataText) }`) const boundary = formDataLines[0]; console.assert( formDataLines[formDataLines.length - 1] === boundary + "--", `${fileBaseName}: multipart form data must end with ${boundary}--: ${ JSON.stringify(formDataText) }`, ); const asValue = fileBaseName.replace(/\r\n?|\n/g, "\r\n"); const asName = asValue.replace(/[\r\n"]/g, encodeURIComponent); const asFilename = fileBaseName.replace(/[\r\n"]/g, encodeURIComponent); const expectedText = [ boundary, 'Content-Disposition: form-data; name="filename"', "", asValue, boundary, `Content-Disposition: form-data; name="${asName}"`, "", "filename", boundary, `Content-Disposition: form-data; name="file"; ` + `filename="${asFilename}"`, "Content-Type: text/plain", "", kTestChars, boundary + "--", ].join("\r\n") console.assert( formDataText.startsWith(expectedText), `Unexpected multipart-shaped form data received:\n${formDataText}\nExpected:\n${expectedText}`, ) } await formDataPostFileUploadTest("file-for-upload-in-form.txt") await formDataPostFileUploadTest("file-for-upload-in-form-\uF7F0\uF793\uF783\uF7A0.txt") await formDataPostFileUploadTest("file-for-upload-in-form-☺😂.txt") await formDataPostFileUploadTest("file-for-upload-in-form-★星★.txt") await formDataPostFileUploadTest("file-for-upload-in-form-☺😂.txt") await formDataPostFileUploadTest(`file-for-upload-in-form-${kTestChars}.txt`) await formDataPostFileUploadTest("file-for-upload-in-form.txt") await formDataPostFileUploadTest("file-for-upload-in-form-NUL-[\0].txt") await formDataPostFileUploadTest("file-for-upload-in-form-BS-[\b].txt") await formDataPostFileUploadTest("file-for-upload-in-form-VT-[\v].txt") // These have characters that undergo processing in name=, // filename=, and/or value; formDataPostFileUploadTest postprocesses // expectedEncodedBaseName for these internally. await formDataPostFileUploadTest("file-for-upload-in-form-LF-[\n].txt") await formDataPostFileUploadTest("file-for-upload-in-form-LF-CR-[\n\r].txt") await formDataPostFileUploadTest("file-for-upload-in-form-CR-[\r].txt") await formDataPostFileUploadTest("file-for-upload-in-form-CR-LF-[\r\n].txt") await formDataPostFileUploadTest("file-for-upload-in-form-HT-[\t].txt") await formDataPostFileUploadTest("file-for-upload-in-form-FF-[\f].txt") await formDataPostFileUploadTest("file-for-upload-in-form-DEL-[\x7F].txt") // The rest should be passed through unmodified: await formDataPostFileUploadTest("file-for-upload-in-form-ESC-[\x1B].txt") await formDataPostFileUploadTest("file-for-upload-in-form-SPACE-[ ].txt") // These have characters that undergo processing in name=, // filename=, and/or value; formDataPostFileUploadTest postprocesses // expectedEncodedBaseName for these internally. await formDataPostFileUploadTest("file-for-upload-in-form-QUOTATION-MARK-[\x22].txt") await formDataPostFileUploadTest('"file-for-upload-in-form-double-quoted.txt"') await formDataPostFileUploadTest("file-for-upload-in-form-REVERSE-SOLIDUS-[\\].txt") // The rest should be passed through unmodified: await formDataPostFileUploadTest("file-for-upload-in-form-EXCLAMATION-MARK-[!].txt") await formDataPostFileUploadTest("file-for-upload-in-form-DOLLAR-SIGN-[$].txt") await formDataPostFileUploadTest("file-for-upload-in-form-PERCENT-SIGN-[%].txt") await formDataPostFileUploadTest("file-for-upload-in-form-AMPERSAND-[&].txt") await formDataPostFileUploadTest("file-for-upload-in-form-APOSTROPHE-['].txt") await formDataPostFileUploadTest("file-for-upload-in-form-LEFT-PARENTHESIS-[(].txt") await formDataPostFileUploadTest("file-for-upload-in-form-RIGHT-PARENTHESIS-[)].txt") await formDataPostFileUploadTest("file-for-upload-in-form-ASTERISK-[*].txt") await formDataPostFileUploadTest("file-for-upload-in-form-PLUS-SIGN-[+].txt") await formDataPostFileUploadTest("file-for-upload-in-form-COMMA-[,].txt") await formDataPostFileUploadTest("file-for-upload-in-form-FULL-STOP-[.].txt") await formDataPostFileUploadTest("file-for-upload-in-form-SOLIDUS-[/].txt") await formDataPostFileUploadTest("file-for-upload-in-form-COLON-[:].txt") await formDataPostFileUploadTest("file-for-upload-in-form-SEMICOLON-[;].txt") await formDataPostFileUploadTest("file-for-upload-in-form-EQUALS-SIGN-[=].txt") await formDataPostFileUploadTest("file-for-upload-in-form-QUESTION-MARK-[?].txt") await formDataPostFileUploadTest("file-for-upload-in-form-CIRCUMFLEX-ACCENT-[^].txt") await formDataPostFileUploadTest("file-for-upload-in-form-LEFT-SQUARE-BRACKET-[[].txt") await formDataPostFileUploadTest("file-for-upload-in-form-RIGHT-SQUARE-BRACKET-[]].txt") await formDataPostFileUploadTest("file-for-upload-in-form-LEFT-CURLY-BRACKET-[{].txt") await formDataPostFileUploadTest("file-for-upload-in-form-VERTICAL-LINE-[|].txt") await formDataPostFileUploadTest("file-for-upload-in-form-RIGHT-CURLY-BRACKET-[}].txt") await formDataPostFileUploadTest("file-for-upload-in-form-TILDE-[~].txt") await formDataPostFileUploadTest("'file-for-upload-in-form-single-quoted.txt'") FormData-4.0.10/test/test-polyfill.html000066400000000000000000000513371412533321300177530ustar00rootroot00000000000000See console in devtool FormData-4.0.10/test/test-wpt-in-node.js000066400000000000000000000055231412533321300177260ustar00rootroot00000000000000import 'https://wpt.live/resources/testharness.js' import 'https://wpt.live/FileAPI/support/send-file-formdata-helper.js?expose=kTestChars&expose=formDataPostFileUploadTest' import {File, Blob} from 'fetch-blob/from.js' import {FormData, formDataToBlob} from '../esm.min.js' globalThis.FormData = FormData globalThis.Blob = Blob globalThis.File = File globalThis.fetch = (url, opts) => formDataToBlob(opts.body) setup({ explicit_timeout: true, explicit_done: true, }) globalThis.add_result_callback(test => { const INDENT_SIZE = 2; const reporter = { startSuite: name => console.log(`\n ${(name)}\n`), pass: message => console.log((indent(("√ ") + message.replace(/(\r\n|\n|\r)/gm, ''), INDENT_SIZE))), fail: message => console.log((indent("\u00D7 " + message, INDENT_SIZE))), reportStack: stack => console.log((indent(stack, INDENT_SIZE * 2))), } if (test.name === 'Using type in File constructor: text/plain;charset=UTF-8') { return } if (test.name === 'Using type in File constructor: TEXT/PLAIN') { return } function indent(string, times) { const prefix = " ".repeat(times); return string.split("\n").map(l => prefix + l).join("\n"); } if (test.status === 0) { reporter.pass(test.name); } else if (test.status === 1) { reporter.fail(`${test.name}\n`); reporter.reportStack(`${test.message}\n${test.stack}`); } else if (test.status === 2) { reporter.fail(`${test.name} (timeout)\n`); reporter.reportStack(`${test.message}\n${test.stack}`); } else if (test.status === 3) { reporter.fail(`${test.name} (incomplete)\n`); reporter.reportStack(`${test.message}\n${test.stack}`); } else if (test.status === 4) { reporter.fail(`${test.name} (precondition failed)\n`); reporter.reportStack(`${test.message}\n${test.stack}`); } else { reporter.fail(`unknown test status: ${test.status}`); } // hasFailed && process.exit(1); }) // Test the FormData implementation await import('https://wpt.live/xhr/formdata/append.any.js') await import('https://wpt.live/xhr/formdata/constructor.any.js') await import('https://wpt.live/xhr/formdata/delete.any.js') await import('https://wpt.live/xhr/formdata/foreach.any.js') await import('https://wpt.live/xhr/formdata/get.any.js') await import('https://wpt.live/xhr/formdata/has.any.js') await import('https://wpt.live/xhr/formdata/set-blob.any.js') await import('https://wpt.live/xhr/formdata/set.any.js') // Test the formDataToBlob encoder/decoder await import('https://wpt.live/FileAPI/file/send-file-formdata-punctuation.any.js') await import('https://wpt.live/FileAPI/file/send-file-formdata-controls.any.js') await import('https://wpt.live/FileAPI/file/send-file-formdata-controls.any.js') await import('https://wpt.live/FileAPI/file/send-file-formdata-utf-8.any.js') await import('https://wpt.live/FileAPI/file/send-file-formdata.any.js') FormData-4.0.10/tsconfig.json000066400000000000000000000005511412533321300157760ustar00rootroot00000000000000{ "include": ["*.js"], "compilerOptions": { "target": "ES2020", "lib": ["ES2020", "DOM"], "module": "ES2020", "moduleResolution": "node", "allowJs": true, "checkJs": true, "declaration": true, "emitDeclarationOnly": true, "allowSyntheticDefaultImports": true, "skipLibCheck": true, "strictNullChecks": true } }