VueJS lekcja 1
This commit is contained in:
1 parent
11eb98f6b7
commit
e66b5ebc22
602 files changed
+63992
No files matched your search
+22
@@ -0,0 +1,22 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2014-2017 Douglas Christopher Wilson
|
||||
|
||||
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.
|
||||
+211
@@ -0,0 +1,211 @@
|
||||
# content-disposition
|
||||
|
||||
[![NPM Version][npm-image]][npm-url]
|
||||
[![NPM Downloads][downloads-image]][downloads-url]
|
||||
[![Node.js Version][node-version-image]][node-version-url]
|
||||
[![Build Status][github-actions-ci-image]][github-actions-ci-url]
|
||||
[![Test Coverage][coveralls-image]][coveralls-url]
|
||||
|
||||
Create and parse HTTP `Content-Disposition` header
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
$ npm install content-disposition
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
```js
|
||||
import { create, parse, format } from 'content-disposition';
|
||||
```
|
||||
|
||||
### create(filename, options)
|
||||
|
||||
Create an attachment `Content-Disposition` header value using the given file name,
|
||||
if supplied. The `filename` is optional and if no file name is desired, but you
|
||||
want to specify `options`, set `filename` to `undefined`.
|
||||
|
||||
```js
|
||||
res.setHeader('Content-Disposition', create('∫ maths.pdf'));
|
||||
```
|
||||
|
||||
**note** HTTP headers are of the ISO-8859-1 character set. If you are writing this
|
||||
header through a means different from `setHeader` in Node.js, you'll want to specify
|
||||
the `'binary'` encoding in Node.js.
|
||||
|
||||
#### Options
|
||||
|
||||
`contentDisposition` accepts these properties in the options object.
|
||||
|
||||
##### fallback
|
||||
|
||||
If the `filename` option is outside US-ASCII, then the file name is actually
|
||||
stored in a supplemental field for clients that support Unicode file names and
|
||||
a US-ASCII version of the file name is automatically generated.
|
||||
|
||||
This specifies the US-ASCII file name to override the automatic generation or
|
||||
disables the generation all together, defaults to `true`.
|
||||
|
||||
- A string will specify the US-ASCII file name to use in place of automatic
|
||||
generation.
|
||||
- `false` will disable including a US-ASCII file name and only include the
|
||||
Unicode version (unless the file name is already US-ASCII).
|
||||
- `true` will enable automatic generation if the file name is outside US-ASCII.
|
||||
|
||||
If the `filename` option is US-ASCII and this option is specified and has a
|
||||
different value, then the `filename` option is encoded in the extended field
|
||||
and this set as the fallback field, even though they are both US-ASCII.
|
||||
|
||||
##### type
|
||||
|
||||
Specifies the disposition type, defaults to `"attachment"`. This can also be
|
||||
`"inline"`, or any other value (all values except inline are treated like
|
||||
`attachment`, but can convey additional information if both parties agree to
|
||||
it).
|
||||
|
||||
### parse(string, options)
|
||||
|
||||
```js
|
||||
const disposition = parse(
|
||||
'attachment; filename="EURO rates.txt"; filename*=UTF-8\'\'%e2%82%ac%20rates.txt',
|
||||
);
|
||||
```
|
||||
|
||||
Parse a `Content-Disposition` header string. This automatically handles extended
|
||||
("Unicode") parameters by decoding them and providing them under the standard
|
||||
parameter name. This will return an object with the following properties:
|
||||
|
||||
- `type`: The disposition type (always lower case). Example: `'attachment'`
|
||||
|
||||
- `parameters`: An object of the parameters in the disposition (name of parameter
|
||||
always lower case and extended versions replace non-extended versions). Example:
|
||||
`{filename: "€ rates.txt"}`
|
||||
|
||||
#### Options
|
||||
|
||||
##### multipart
|
||||
|
||||
Parse parameters using browser `multipart/form-data` behavior.
|
||||
|
||||
```js
|
||||
parse('form-data; name="file"; filename="the %22plans%22.pdf"', {
|
||||
multipart: true,
|
||||
});
|
||||
```
|
||||
|
||||
##### extended
|
||||
|
||||
Parse RFC 5987 extended header parameters automatically when decoding
|
||||
parameters, defaults to `true`.
|
||||
|
||||
### format(obj, options)
|
||||
|
||||
```js
|
||||
const disposition = format({
|
||||
type: 'attachment',
|
||||
parameters: {
|
||||
filename: '€ rates.txt',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Formats an object to a `Content-Disposition` header string. This automatically
|
||||
handles extended ("Unicode") parameters and returns a string. Example:
|
||||
`'attachment; filename*=UTF-8''%E2%82%AC%20rates.txt'`
|
||||
|
||||
#### Options
|
||||
|
||||
##### multipart
|
||||
|
||||
Format parameters using browser `multipart/form-data` behavior. This quotes
|
||||
parameter values, escapes `"` as `%22`, and writes Unicode values directly
|
||||
instead of using extended parameters.
|
||||
|
||||
```js
|
||||
format(
|
||||
{
|
||||
type: 'form-data',
|
||||
parameters: { name: 'file', filename: '€ rates.txt' },
|
||||
},
|
||||
{ multipart: true },
|
||||
);
|
||||
```
|
||||
|
||||
##### extended
|
||||
|
||||
Encode Unicode parameter values using RFC 5987 extended header parameters,
|
||||
e.g. `filename*=`, defaults to `true`.
|
||||
|
||||
## Examples
|
||||
|
||||
### Send a file for download
|
||||
|
||||
```js
|
||||
const contentDisposition = require('content-disposition');
|
||||
const fs = require('fs');
|
||||
const http = require('http');
|
||||
const onFinished = require('on-finished');
|
||||
|
||||
const filePath = '/path/to/public/plans.pdf';
|
||||
|
||||
http.createServer(function onRequest(req, res) {
|
||||
// set headers
|
||||
res.setHeader('Content-Type', 'application/pdf');
|
||||
res.setHeader('Content-Disposition', contentDisposition(filePath));
|
||||
|
||||
// send file
|
||||
const stream = fs.createReadStream(filePath);
|
||||
stream.pipe(res);
|
||||
onFinished(res, function () {
|
||||
stream.destroy();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Local demo
|
||||
|
||||
Run the upload inspector locally:
|
||||
|
||||
```sh
|
||||
npm run demo
|
||||
```
|
||||
|
||||
Then open `http://127.0.0.1:3000` in your browser. The demo lets you upload files, inspect the multipart upload part headers sent by the browser, and compare them with the download `Content-Disposition` header generated by this package.
|
||||
|
||||
## Testing
|
||||
|
||||
```sh
|
||||
$ npm test
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- [RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1][rfc-2616]
|
||||
- [RFC 5987: Character Set and Language Encoding for Hypertext Transfer Protocol (HTTP) Header Field Parameters][rfc-5987]
|
||||
- [RFC 6266: Use of the Content-Disposition Header Field in the Hypertext Transfer Protocol (HTTP)][rfc-6266]
|
||||
- [RFC 8187: Indicating Character Encoding and Language for HTTP Header Field Parameters][rfc-8187]
|
||||
- [HTML Living Standard: Multipart form data][html-form]
|
||||
- [Test Cases for HTTP Content-Disposition header field (RFC 6266) and the Encodings defined in RFCs 2047, 2231 and 5987][tc-2231]
|
||||
|
||||
[rfc-2616]: https://datatracker.ietf.org/doc/html/rfc2616
|
||||
[rfc-5987]: https://datatracker.ietf.org/doc/html/rfc5987
|
||||
[rfc-6266]: https://datatracker.ietf.org/doc/html/rfc6266
|
||||
[rfc-8187]: https://datatracker.ietf.org/doc/html/rfc8187
|
||||
[html-form]: https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart-form-data
|
||||
[tc-2231]: http://greenbytes.de/tech/tc2231/
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
[npm-image]: https://img.shields.io/npm/v/content-disposition
|
||||
[npm-url]: https://www.npmjs.com/package/content-disposition
|
||||
[node-version-image]: https://img.shields.io/node/v/content-disposition
|
||||
[node-version-url]: https://nodejs.org/en/download
|
||||
[coveralls-image]: https://img.shields.io/coverallsCoverage/github/jshttp/content-disposition
|
||||
[coveralls-url]: https://coveralls.io/github/jshttp/content-disposition?branch=master
|
||||
[downloads-image]: https://img.shields.io/npm/dm/content-disposition
|
||||
[downloads-url]: https://www.npmjs.com/package/content-disposition
|
||||
[github-actions-ci-image]: https://img.shields.io/github/actions/workflow/status/jshttp/content-disposition/ci.yml
|
||||
[github-actions-ci-url]: https://github.com/jshttp/content-disposition/actions/workflows/ci.yml
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*!
|
||||
* content-disposition
|
||||
* Copyright(c) 2014-2017 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
export interface ContentDisposition {
|
||||
/**
|
||||
* Content-Disposition type, such as "attachment" or "inline"
|
||||
*/
|
||||
type: string;
|
||||
/**
|
||||
* Content-Disposition parameters, such as "filename"
|
||||
*/
|
||||
parameters: Record<string, string>;
|
||||
}
|
||||
export interface CreateOptions {
|
||||
/**
|
||||
* Content-Disposition type, defaults to "attachment"
|
||||
* @default "attachment"
|
||||
*/
|
||||
type?: string;
|
||||
/**
|
||||
* Fallback filename for non-US-ASCII strings. If true, a fallback will be generated by replacing non-US-ASCII characters with "?". If false, no fallback will be generated.
|
||||
* @default true
|
||||
*/
|
||||
fallback?: string | boolean;
|
||||
}
|
||||
/**
|
||||
* Create an attachment Content-Disposition header.
|
||||
*/
|
||||
export declare function create(filename?: string, options?: CreateOptions): string;
|
||||
export interface ParseOptions {
|
||||
/**
|
||||
* Whether to decode RFC 8187 encoded parameters.
|
||||
* @default true
|
||||
*/
|
||||
extended?: boolean;
|
||||
/**
|
||||
* Parse parameters as sent by browsers in `multipart/form-data`.
|
||||
* @default false
|
||||
*/
|
||||
multipart?: boolean;
|
||||
}
|
||||
/**
|
||||
* Parse Content-Disposition header string.
|
||||
*/
|
||||
export declare function parse(header: string, options?: ParseOptions): ContentDisposition;
|
||||
/**
|
||||
* Decode a RFC 8187 field value (gracefully).
|
||||
*/
|
||||
export declare function decodeExtended(str: string): string | undefined;
|
||||
export interface FormatOptions {
|
||||
/**
|
||||
* Whether to use extended parameter encoding for non-ISO-8859-1 strings.
|
||||
* If false, an error will be thrown when formatting such strings.
|
||||
* @default true
|
||||
*/
|
||||
extended?: boolean;
|
||||
/**
|
||||
* Format parameters as sent by browsers in `multipart/form-data`.
|
||||
* @default false
|
||||
*/
|
||||
multipart?: boolean;
|
||||
}
|
||||
/**
|
||||
* Format object to Content-Disposition header.
|
||||
*/
|
||||
export declare function format(obj: Partial<ContentDisposition>, options?: FormatOptions): string;
|
||||
/**
|
||||
* Encode a Unicode string for HTTP (RFC 8187).
|
||||
*/
|
||||
export declare function encodeExtended(str: string): string;
|
||||
+388
@@ -0,0 +1,388 @@
|
||||
"use strict";
|
||||
/*!
|
||||
* content-disposition
|
||||
* Copyright(c) 2014-2017 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.create = create;
|
||||
exports.parse = parse;
|
||||
exports.decodeExtended = decodeExtended;
|
||||
exports.format = format;
|
||||
exports.encodeExtended = encodeExtended;
|
||||
/**
|
||||
* Null object perf optimization. Faster than `Object.create(null)` and `{ __proto__: null }`.
|
||||
*/
|
||||
const NullObject = /* @__PURE__ */ (() => {
|
||||
const C = function () { };
|
||||
C.prototype = Object.create(null);
|
||||
return C;
|
||||
})();
|
||||
/**
|
||||
* Create an attachment Content-Disposition header.
|
||||
*/
|
||||
function create(filename, options) {
|
||||
const type = options?.type || 'attachment';
|
||||
const parameters = createParameters(filename, options?.fallback);
|
||||
return format({ type, parameters }, { extended: false });
|
||||
}
|
||||
const SP = 32; // " "
|
||||
const HTAB = 9; // "\t"
|
||||
const SEMI = 59; // ";"
|
||||
const EQ = 61; // "="
|
||||
const DQUOTE = 34; // '"'
|
||||
const BSLASH = 92; // "\\"
|
||||
const ASTERISK = 42; // "*"
|
||||
/**
|
||||
* Parse Content-Disposition header string.
|
||||
*/
|
||||
function parse(header, options) {
|
||||
const len = header.length;
|
||||
const multipart = options?.multipart === true;
|
||||
const extended = options?.extended !== false;
|
||||
let index = skipOWS(header, 0, header.length);
|
||||
const typeStart = index;
|
||||
index = parseToken(header, index, len);
|
||||
const typeEnd = trailingOWS(header, typeStart, index);
|
||||
const type = header.slice(typeStart, typeEnd).toLowerCase();
|
||||
const parameters = new NullObject();
|
||||
parameter: while (index < len) {
|
||||
index = skipOWS(header, index + 1, len); // Skip over semicolon.
|
||||
const keyStart = index;
|
||||
while (index < len) {
|
||||
const char = header.charCodeAt(index);
|
||||
if (char === SEMI)
|
||||
continue parameter;
|
||||
if (char === EQ) {
|
||||
const keyEnd = trailingOWS(header, keyStart, index);
|
||||
const key = header.slice(keyStart, keyEnd).toLowerCase();
|
||||
index = skipOWS(header, index + 1, len);
|
||||
if (index < len && header.charCodeAt(index) === DQUOTE) {
|
||||
index++;
|
||||
let value = '';
|
||||
if (multipart) {
|
||||
while (index < len) {
|
||||
const code = header.charCodeAt(index++);
|
||||
if (code === DQUOTE) {
|
||||
index = parseToken(header, index, len);
|
||||
if (parameters[key] === undefined)
|
||||
parameters[key] = value;
|
||||
break;
|
||||
}
|
||||
if (code === /* % */ 37 && index + 1 < len) {
|
||||
const code2 = header.charCodeAt(index);
|
||||
const code3 = header.charCodeAt(index + 1);
|
||||
if (code2 === 50 /* "2" */ && code3 === 50 /* "2" */) {
|
||||
value += '"';
|
||||
index += 2;
|
||||
continue;
|
||||
}
|
||||
if (code2 === 48 /* "0" */) {
|
||||
if (code3 === 100 /* "d" */ || code3 === 68 /* "D" */) {
|
||||
value += '\r';
|
||||
index += 2;
|
||||
continue;
|
||||
}
|
||||
if (code3 === 97 /* "a" */ || code3 === 65 /* "A" */) {
|
||||
value += '\n';
|
||||
index += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
value += String.fromCharCode(code);
|
||||
}
|
||||
}
|
||||
else {
|
||||
while (index < len) {
|
||||
const code = header.charCodeAt(index++);
|
||||
if (code === DQUOTE) {
|
||||
index = parseToken(header, index, len);
|
||||
if (parameters[key] === undefined)
|
||||
parameters[key] = value;
|
||||
break;
|
||||
}
|
||||
if (code === BSLASH && index < len) {
|
||||
value += header[index++];
|
||||
continue;
|
||||
}
|
||||
value += String.fromCharCode(code);
|
||||
}
|
||||
}
|
||||
continue parameter;
|
||||
}
|
||||
const valueStart = index;
|
||||
index = parseToken(header, index, len);
|
||||
const valueEnd = trailingOWS(header, valueStart, index);
|
||||
const value = header.slice(valueStart, valueEnd);
|
||||
if (parameters[key] === undefined) {
|
||||
parameters[key] = value;
|
||||
if (extended && key.charCodeAt(key.length - 1) === ASTERISK) {
|
||||
const normalizedKey = key.slice(0, -1);
|
||||
const decoded = decodeExtended(value);
|
||||
if (decoded !== undefined)
|
||||
parameters[normalizedKey] = decoded;
|
||||
}
|
||||
}
|
||||
continue parameter;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
return { type, parameters };
|
||||
}
|
||||
/**
|
||||
* RegExp to match non attr-char, *after* encodeURIComponent (i.e. not including "%")
|
||||
*/
|
||||
const ENCODE_URL_ATTR_CHAR_REGEXP = /[\x00-\x20"'()*,/:;<=>?@[\\\]{}\x7f]/g; // eslint-disable-line no-control-regex
|
||||
/**
|
||||
* RegExp to match non-US-ASCII characters.
|
||||
*/
|
||||
const NON_ASCII_REGEXP = /[^\x20-\x7e]/g;
|
||||
/**
|
||||
* RegExp to match chars that must be quoted-pair in RFC 2616
|
||||
*/
|
||||
const QUOTE_REGEXP = /[\\"]/g;
|
||||
/**
|
||||
* Match chars that can't be used in the filename for compatibility.
|
||||
*/
|
||||
const INVALID_FILENAME_REGEXP = /%[0-9A-Fa-f]{2}/;
|
||||
/**
|
||||
* RegExp for various RFC 2616 grammar
|
||||
*
|
||||
* parameter = token "=" ( token | quoted-string )
|
||||
* token = 1*<any CHAR except CTLs or separators>
|
||||
* separators = "(" | ")" | "<" | ">" | "@"
|
||||
* | "," | ";" | ":" | "\" | <">
|
||||
* | "/" | "[" | "]" | "?" | "="
|
||||
* | "{" | "}" | SP | HT
|
||||
* quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
|
||||
* qdtext = <any TEXT except <">>
|
||||
* quoted-pair = "\" CHAR
|
||||
* CHAR = <any US-ASCII character (octets 0 - 127)>
|
||||
* TEXT = <any OCTET except CTLs, but including LWS>
|
||||
* LWS = [CRLF] 1*( SP | HT )
|
||||
* CRLF = CR LF
|
||||
* CR = <US-ASCII CR, carriage return (13)>
|
||||
* LF = <US-ASCII LF, linefeed (10)>
|
||||
* SP = <US-ASCII SP, space (32)>
|
||||
* HT = <US-ASCII HT, horizontal-tab (9)>
|
||||
* CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
|
||||
* OCTET = <any 8-bit sequence of data>
|
||||
*/
|
||||
const TEXT_REGEXP = /^[\x20-\x7e\x80-\xff]*$/;
|
||||
const ASCII_TEXT_REGEXP = /^[\x20-\x7e]*$/;
|
||||
const TOKEN_REGEXP = /^[!#$%&'*+.0-9A-Z^_`a-z|~-]+$/;
|
||||
/**
|
||||
* Create parameters object from filename and fallback.
|
||||
*/
|
||||
function createParameters(filename, fallback = true) {
|
||||
if (filename === undefined)
|
||||
return;
|
||||
if (typeof fallback === 'string') {
|
||||
if (!ASCII_TEXT_REGEXP.test(fallback)) {
|
||||
throw new TypeError('Fallback must be valid US-ASCII: ' + fallback);
|
||||
}
|
||||
if (fallback === filename)
|
||||
return { filename };
|
||||
return { filename: fallback, 'filename*': encodeExtended(filename) };
|
||||
}
|
||||
// Use `filename` when possible, otherwise use `filename*`.
|
||||
if (ASCII_TEXT_REGEXP.test(filename) &&
|
||||
!INVALID_FILENAME_REGEXP.test(filename)) {
|
||||
return { filename };
|
||||
}
|
||||
if (fallback === false) {
|
||||
return { 'filename*': encodeExtended(filename) };
|
||||
}
|
||||
return {
|
||||
filename: getAscii(filename),
|
||||
'filename*': encodeExtended(filename),
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Decode a RFC 8187 field value (gracefully).
|
||||
*/
|
||||
function decodeExtended(str) {
|
||||
const charsetEnd = str.indexOf("'");
|
||||
if (charsetEnd <= 0) {
|
||||
return undefined;
|
||||
}
|
||||
const languageEnd = str.indexOf("'", charsetEnd + 1);
|
||||
if (languageEnd === -1) {
|
||||
return undefined;
|
||||
}
|
||||
const charset = str.slice(0, charsetEnd).toLowerCase();
|
||||
const encoded = str.slice(languageEnd + 1);
|
||||
switch (charset) {
|
||||
case 'iso-8859-1': {
|
||||
return decodeHexEscapes(encoded);
|
||||
}
|
||||
case 'utf-8':
|
||||
case 'utf8': {
|
||||
return tryDecodeURIComponent(encoded);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
/**
|
||||
* Decode URI component but return `undefined` on error.
|
||||
*/
|
||||
function tryDecodeURIComponent(str) {
|
||||
try {
|
||||
return decodeURIComponent(str);
|
||||
}
|
||||
catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Parse a token starting at the provided index.
|
||||
*/
|
||||
function parseToken(str, index, len) {
|
||||
while (index < len) {
|
||||
const char = str.charCodeAt(index);
|
||||
if (char === SEMI)
|
||||
break;
|
||||
index++;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
/**
|
||||
* Skip RFC 2616 linear whitespace (space / tab).
|
||||
*/
|
||||
function skipOWS(str, index, len) {
|
||||
while (index < len) {
|
||||
const char = str.charCodeAt(index);
|
||||
if (char !== SP && char !== HTAB)
|
||||
break;
|
||||
index++;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
/**
|
||||
* Skip RFC 2616 linear whitespace (space / tab) from the end of a string.
|
||||
*/
|
||||
function trailingOWS(str, start, end) {
|
||||
while (end > start) {
|
||||
const char = str.charCodeAt(end - 1);
|
||||
if (char !== SP && char !== HTAB)
|
||||
break;
|
||||
end--;
|
||||
}
|
||||
return end;
|
||||
}
|
||||
/**
|
||||
* Format object to Content-Disposition header.
|
||||
*/
|
||||
function format(obj, options) {
|
||||
const { type, parameters } = obj;
|
||||
const multipart = options?.multipart === true;
|
||||
const extended = options?.extended !== false;
|
||||
if (!type || !TOKEN_REGEXP.test(type)) {
|
||||
throw new TypeError('Invalid type: ' + type);
|
||||
}
|
||||
let result = type;
|
||||
if (parameters) {
|
||||
for (const param of Object.keys(parameters)) {
|
||||
const value = parameters[param];
|
||||
if (!TOKEN_REGEXP.test(param)) {
|
||||
throw new TypeError('Invalid parameter name: ' + param);
|
||||
}
|
||||
if (multipart) {
|
||||
result += '; ' + param + '=' + qmultipart(value);
|
||||
continue;
|
||||
}
|
||||
if (TOKEN_REGEXP.test(value)) {
|
||||
result += '; ' + param + '=' + value;
|
||||
continue;
|
||||
}
|
||||
if (TEXT_REGEXP.test(value)) {
|
||||
result += '; ' + param + '=' + qstring(value);
|
||||
continue;
|
||||
}
|
||||
if (!extended) {
|
||||
throw new TypeError('Invalid parameter value: ' + value);
|
||||
}
|
||||
result += '; ' + param + '*=' + encodeExtended(value);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Get US-ASCII version of string.
|
||||
*/
|
||||
function getAscii(val) {
|
||||
// simple Unicode -> US-ASCII transformation
|
||||
return val.replace(NON_ASCII_REGEXP, '?');
|
||||
}
|
||||
/**
|
||||
* Percent encode a single character.
|
||||
*/
|
||||
function pencode(char) {
|
||||
return '%' + char.charCodeAt(0).toString(16).toUpperCase();
|
||||
}
|
||||
/**
|
||||
* Quote a string for HTTP.
|
||||
*/
|
||||
function qstring(str) {
|
||||
return '"' + str.replace(QUOTE_REGEXP, '\\$&') + '"';
|
||||
}
|
||||
/**
|
||||
* Quote a string for multipart/form-data.
|
||||
*
|
||||
* @see https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart-form-data
|
||||
*/
|
||||
function qmultipart(str) {
|
||||
return '"' + str.replace(/[\n\r"]/g, multipartEscape) + '"';
|
||||
}
|
||||
/**
|
||||
* Escape a multipart/form-data string character.
|
||||
*/
|
||||
function multipartEscape(char) {
|
||||
if (char === '\n')
|
||||
return '%0A';
|
||||
if (char === '\r')
|
||||
return '%0D';
|
||||
return '%22';
|
||||
}
|
||||
/**
|
||||
* Encode a Unicode string for HTTP (RFC 8187).
|
||||
*/
|
||||
function encodeExtended(str) {
|
||||
const encoded = encodeURIComponent(str).replace(ENCODE_URL_ATTR_CHAR_REGEXP, pencode);
|
||||
return "UTF-8''" + encoded;
|
||||
}
|
||||
/**
|
||||
* Check if a character is a hex digit [0-9A-Fa-f]
|
||||
*/
|
||||
function isHexDigit(char) {
|
||||
const code = char.charCodeAt(0);
|
||||
return ((code >= 48 && code <= 57) || // 0-9
|
||||
(code >= 65 && code <= 70) || // A-F
|
||||
(code >= 97 && code <= 102) // a-f
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Decode hex escapes in a string (e.g., %20 -> space)
|
||||
*/
|
||||
function decodeHexEscapes(str) {
|
||||
const firstEscape = str.indexOf('%');
|
||||
if (firstEscape === -1)
|
||||
return str;
|
||||
let result = str.slice(0, firstEscape);
|
||||
for (let idx = firstEscape; idx < str.length; idx++) {
|
||||
if (str[idx] === '%' &&
|
||||
idx + 2 < str.length &&
|
||||
isHexDigit(str[idx + 1]) &&
|
||||
isHexDigit(str[idx + 2])) {
|
||||
result += String.fromCharCode(Number.parseInt(str[idx + 1] + str[idx + 2], 16));
|
||||
idx += 2;
|
||||
}
|
||||
else {
|
||||
result += str[idx];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
//# sourceMappingURL=index.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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 truncated
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"name": "content-disposition",
|
||||
"version": "2.0.1",
|
||||
"description": "Create and parse Content-Disposition header",
|
||||
"keywords": [
|
||||
"content-disposition",
|
||||
"http",
|
||||
"rfc6266",
|
||||
"res"
|
||||
],
|
||||
"repository": "jshttp/content-disposition",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
},
|
||||
"license": "MIT",
|
||||
"author": "Douglas Christopher Wilson <[email protected]>",
|
||||
"type": "commonjs",
|
||||
"exports": "./dist/index.js",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"files": [
|
||||
"dist/"
|
||||
],
|
||||
"scripts": {
|
||||
"bench": "vitest bench",
|
||||
"build": "ts-scripts build",
|
||||
"demo": "node demo/server.js",
|
||||
"format": "ts-scripts format",
|
||||
"lint": "ts-scripts lint",
|
||||
"prepare": "ts-scripts install && npm run build",
|
||||
"specs": "ts-scripts specs",
|
||||
"test": "ts-scripts test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@borderless/ts-scripts": "^0.15.0",
|
||||
"@vitest/coverage-v8": "^3.2.4",
|
||||
"typescript": "^5.9.3",
|
||||
"vitest": "^3.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"ts-scripts": {
|
||||
"dist": [
|
||||
"dist"
|
||||
],
|
||||
"project": [
|
||||
"tsconfig.build.json"
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user