@cedarjs/uploads 8.0.0-canary.3400 → 8.0.0-canary.3403
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/README.md +5 -0
- package/dist/contentTypes.d.ts +74 -0
- package/dist/contentTypes.d.ts.map +1 -0
- package/dist/contentTypes.js +78 -0
- package/dist/detectContentType.d.ts +168 -0
- package/dist/detectContentType.d.ts.map +1 -0
- package/dist/detectContentType.js +443 -0
- package/dist/fastify/plugin.d.ts +16 -0
- package/dist/fastify/plugin.d.ts.map +1 -1
- package/dist/fastify/plugin.js +32 -7
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -0
- package/dist/keys.d.ts +3 -1
- package/dist/keys.d.ts.map +1 -1
- package/dist/keys.js +2 -1
- package/dist/profiles.d.ts +8 -2
- package/dist/profiles.d.ts.map +1 -1
- package/dist/profiles.js +5 -4
- package/dist/providers/s3.d.ts.map +1 -1
- package/dist/providers/s3.js +16 -0
- package/dist/services.d.ts +24 -6
- package/dist/services.d.ts.map +1 -1
- package/dist/services.js +45 -10
- package/dist/types.d.ts +10 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/verifyStoredContentType.d.ts +19 -0
- package/dist/verifyStoredContentType.d.ts.map +1 -0
- package/dist/verifyStoredContentType.js +41 -0
- package/dist/webhooks/s3.d.ts +17 -8
- package/dist/webhooks/s3.d.ts.map +1 -1
- package/dist/webhooks/s3.js +22 -3
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -14,6 +14,11 @@ File uploads and storage for CedarJS apps.
|
|
|
14
14
|
uploads, and confirming them.
|
|
15
15
|
- **Lifecycle utilities** (`storeFile`, `deleteFile`, `cleanupStaleUploads`) for
|
|
16
16
|
server-generated files and explicit cleanup.
|
|
17
|
+
- **Content detection** (`detectContentType`, `acceptedContentType`) that
|
|
18
|
+
checks what an uploaded file really is from its bytes, not from its name or
|
|
19
|
+
the type the browser claimed: images, PDF, audio/video, and OpenDocument and
|
|
20
|
+
Office files. Uploads use it by default (`verifyContentType`), and store
|
|
21
|
+
each file under one canonical spelling of its type.
|
|
17
22
|
- **Web hooks and components** at `@cedarjs/uploads/web`, built on Uppy.
|
|
18
23
|
|
|
19
24
|
```bash
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content-type spellings and classes shared by every place uploads compare
|
|
3
|
+
* types: profile `allowedMimeTypes`, `acceptedContentType()`'s accept list,
|
|
4
|
+
* and the type a file is stored under.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Other spellings of types `detectContentType()` returns, mapped to the
|
|
8
|
+
* spelling it returns (the canonical one). Comparisons treat both spellings
|
|
9
|
+
* as the same type, so an accept list naming either accepts the file, and
|
|
10
|
+
* files are stored under the canonical spelling.
|
|
11
|
+
*
|
|
12
|
+
* - `image/jpg`, `image/pjpeg`: common misspelling, and what old Internet
|
|
13
|
+
* Explorer sent for progressive JPEGs.
|
|
14
|
+
* - `image/x-png`: what old Internet Explorer sent for PNGs.
|
|
15
|
+
* - `image/x-ms-bmp`, `image/x-bmp`: what `file`/libmagic and older systems
|
|
16
|
+
* report for BMPs. `image/bmp` is the registered type (RFC 7903).
|
|
17
|
+
* - `application/x-pdf`: pre-registration PDF type some systems still send.
|
|
18
|
+
* - `video/x-matroska`: the type browsers and most tools report for `.mkv`.
|
|
19
|
+
* `video/matroska` is the registered one (RFC 9559).
|
|
20
|
+
* - `audio/x-m4a`: what browsers on macOS report for `.m4a`. `audio/mp4` is
|
|
21
|
+
* the registered type (RFC 4337).
|
|
22
|
+
* - `video/x-m4v`: what browsers on macOS report for `.m4v`, an MP4 video.
|
|
23
|
+
*/
|
|
24
|
+
export declare const CONTENT_TYPE_ALIASES: ReadonlyMap<string, string>;
|
|
25
|
+
/**
|
|
26
|
+
* Types a browser runs script in, or renders as a document that can run
|
|
27
|
+
* script, when it is served from the app's origin. `acceptedContentType()`
|
|
28
|
+
* never accepts one of these through a wildcard: an accept list has to name
|
|
29
|
+
* it exactly. Every XML type (`text/xml`, `application/xml`, and anything
|
|
30
|
+
* with a `+xml` suffix, such as `image/svg+xml` or `application/xhtml+xml`)
|
|
31
|
+
* counts too, whether or not it is listed here.
|
|
32
|
+
*/
|
|
33
|
+
export declare const SCRIPTABLE_CONTENT_TYPES: ReadonlySet<string>;
|
|
34
|
+
/**
|
|
35
|
+
* Office types that can carry macros. Macros run in Office on the machine of
|
|
36
|
+
* whoever downloads the file, so accepting one is a way to deliver malware
|
|
37
|
+
* to the app's other users, not an XSS risk, and a profile meant for
|
|
38
|
+
* "documents" almost never means files with macros. `acceptedContentType()`
|
|
39
|
+
* accepts these only when an accept list names them exactly, never through
|
|
40
|
+
* a wildcard such as `application/*` or `*\/*`, whether the type was
|
|
41
|
+
* detected from the bytes or claimed for bytes it doesn't recognise.
|
|
42
|
+
*
|
|
43
|
+
* The macro-enabled Office Open XML types (documents, templates, add-ins and
|
|
44
|
+
* slide shows, and the binary `.xlsb` workbook), and the legacy binary
|
|
45
|
+
* `.doc`, `.xls` and `.ppt` types, which can carry VBA too and which
|
|
46
|
+
* detection doesn't recognise. Lower case, like every canonical type.
|
|
47
|
+
*/
|
|
48
|
+
export declare const MACRO_CONTENT_TYPES: ReadonlySet<string>;
|
|
49
|
+
/** `type/subtype`, lower-cased, without parameters such as `charset`. */
|
|
50
|
+
export declare function normalizeContentType(contentType: string): string;
|
|
51
|
+
/**
|
|
52
|
+
* The canonical spelling of `contentType`: normalized (lower-cased, without
|
|
53
|
+
* parameters), with aliases from `CONTENT_TYPE_ALIASES` resolved.
|
|
54
|
+
*/
|
|
55
|
+
export declare function canonicalContentType(contentType: string): string;
|
|
56
|
+
/** Every spelling of a canonical type: itself first, then its aliases. */
|
|
57
|
+
export declare function contentTypeSpellings(canonical: string): string[];
|
|
58
|
+
/**
|
|
59
|
+
* Whether a browser could run script in `contentType` served inline: one of
|
|
60
|
+
* `SCRIPTABLE_CONTENT_TYPES`, or any XML type.
|
|
61
|
+
*/
|
|
62
|
+
export declare function isScriptableContentType(contentType: string): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Whether `contentType` is only accepted when listed exactly, never through a
|
|
65
|
+
* wildcard: a type that can carry script (`isScriptableContentType()`) or
|
|
66
|
+
* macros (`MACRO_CONTENT_TYPES`).
|
|
67
|
+
*/
|
|
68
|
+
export declare function isExplicitOnlyContentType(contentType: string): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Whether `accept` names `contentType` exactly, not through a wildcard.
|
|
71
|
+
* Aliases count as the same type.
|
|
72
|
+
*/
|
|
73
|
+
export declare function isContentTypeListed(accept: string[], contentType: string): boolean;
|
|
74
|
+
//# sourceMappingURL=contentTypes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contentTypes.d.ts","sourceRoot":"","sources":["../src/contentTypes.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,oBAAoB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAU3D,CAAA;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,EAAE,WAAW,CAAC,MAAM,CAYvD,CAAA;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,mBAAmB,EAAE,WAAW,CAAC,MAAM,CAelD,CAAA;AAEF,yEAAyE;AACzE,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAIhE;AAED,0EAA0E;AAC1E,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAUhE;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAIpE;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAKtE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EAAE,EAChB,WAAW,EAAE,MAAM,GAClB,OAAO,CAIT"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
const CONTENT_TYPE_ALIASES = /* @__PURE__ */ new Map([
|
|
2
|
+
["image/jpg", "image/jpeg"],
|
|
3
|
+
["image/pjpeg", "image/jpeg"],
|
|
4
|
+
["image/x-png", "image/png"],
|
|
5
|
+
["image/x-ms-bmp", "image/bmp"],
|
|
6
|
+
["image/x-bmp", "image/bmp"],
|
|
7
|
+
["application/x-pdf", "application/pdf"],
|
|
8
|
+
["video/x-matroska", "video/matroska"],
|
|
9
|
+
["audio/x-m4a", "audio/mp4"],
|
|
10
|
+
["video/x-m4v", "video/mp4"]
|
|
11
|
+
]);
|
|
12
|
+
const SCRIPTABLE_CONTENT_TYPES = /* @__PURE__ */ new Set([
|
|
13
|
+
"image/svg+xml",
|
|
14
|
+
"text/html",
|
|
15
|
+
"application/xhtml+xml",
|
|
16
|
+
"text/xml",
|
|
17
|
+
"application/xml",
|
|
18
|
+
"text/xsl",
|
|
19
|
+
"text/javascript",
|
|
20
|
+
"application/javascript",
|
|
21
|
+
"application/x-javascript",
|
|
22
|
+
"text/ecmascript",
|
|
23
|
+
"application/ecmascript"
|
|
24
|
+
]);
|
|
25
|
+
const MACRO_CONTENT_TYPES = /* @__PURE__ */ new Set([
|
|
26
|
+
"application/vnd.ms-word.document.macroenabled.12",
|
|
27
|
+
"application/vnd.ms-word.template.macroenabled.12",
|
|
28
|
+
"application/vnd.ms-excel.sheet.macroenabled.12",
|
|
29
|
+
"application/vnd.ms-excel.template.macroenabled.12",
|
|
30
|
+
"application/vnd.ms-excel.addin.macroenabled.12",
|
|
31
|
+
"application/vnd.ms-excel.sheet.binary.macroenabled.12",
|
|
32
|
+
"application/vnd.ms-powerpoint.presentation.macroenabled.12",
|
|
33
|
+
"application/vnd.ms-powerpoint.template.macroenabled.12",
|
|
34
|
+
"application/vnd.ms-powerpoint.slideshow.macroenabled.12",
|
|
35
|
+
"application/vnd.ms-powerpoint.addin.macroenabled.12",
|
|
36
|
+
"application/vnd.ms-powerpoint.slide.macroenabled.12",
|
|
37
|
+
"application/msword",
|
|
38
|
+
"application/vnd.ms-excel",
|
|
39
|
+
"application/vnd.ms-powerpoint"
|
|
40
|
+
]);
|
|
41
|
+
function normalizeContentType(contentType) {
|
|
42
|
+
return contentType.split(";")[0].trim().toLowerCase();
|
|
43
|
+
}
|
|
44
|
+
function canonicalContentType(contentType) {
|
|
45
|
+
const normalized = normalizeContentType(contentType);
|
|
46
|
+
return CONTENT_TYPE_ALIASES.get(normalized) ?? normalized;
|
|
47
|
+
}
|
|
48
|
+
function contentTypeSpellings(canonical) {
|
|
49
|
+
const spellings = [canonical];
|
|
50
|
+
for (const [alias, target] of CONTENT_TYPE_ALIASES) {
|
|
51
|
+
if (target === canonical) {
|
|
52
|
+
spellings.push(alias);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return spellings;
|
|
56
|
+
}
|
|
57
|
+
function isScriptableContentType(contentType) {
|
|
58
|
+
const type = canonicalContentType(contentType);
|
|
59
|
+
return SCRIPTABLE_CONTENT_TYPES.has(type) || type.endsWith("+xml");
|
|
60
|
+
}
|
|
61
|
+
function isExplicitOnlyContentType(contentType) {
|
|
62
|
+
return isScriptableContentType(contentType) || MACRO_CONTENT_TYPES.has(canonicalContentType(contentType));
|
|
63
|
+
}
|
|
64
|
+
function isContentTypeListed(accept, contentType) {
|
|
65
|
+
const type = canonicalContentType(contentType);
|
|
66
|
+
return accept.some((entry) => canonicalContentType(entry) === type);
|
|
67
|
+
}
|
|
68
|
+
export {
|
|
69
|
+
CONTENT_TYPE_ALIASES,
|
|
70
|
+
MACRO_CONTENT_TYPES,
|
|
71
|
+
SCRIPTABLE_CONTENT_TYPES,
|
|
72
|
+
canonicalContentType,
|
|
73
|
+
contentTypeSpellings,
|
|
74
|
+
isContentTypeListed,
|
|
75
|
+
isExplicitOnlyContentType,
|
|
76
|
+
isScriptableContentType,
|
|
77
|
+
normalizeContentType
|
|
78
|
+
};
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How many bytes from the start of a file `detectContentType()` needs to
|
|
3
|
+
* recognise every format it knows. Pass at least this much when reading only
|
|
4
|
+
* the head of a large file, e.g. with a ranged GET. Fewer bytes still work for
|
|
5
|
+
* most formats. An ISO-BMFF file (MP4, HEIC…) whose type depends on a
|
|
6
|
+
* compatible brand needs its whole `ftyp` box, and is reported as `null`
|
|
7
|
+
* when the box is cut off. Office files (`.docx`, `.xlsx`, `.pptx`) also
|
|
8
|
+
* need the end of the file; see `DETECT_CONTENT_TYPE_TAIL_BYTES`.
|
|
9
|
+
*/
|
|
10
|
+
export declare const DETECT_CONTENT_TYPE_BYTES = 4096;
|
|
11
|
+
/**
|
|
12
|
+
* How many bytes from the end of a file to pass as `tail` to
|
|
13
|
+
* `detectContentType()` when `bytes` is only the head of the file. Only ZIP
|
|
14
|
+
* files (head starts with `PK\x03\x04`) need it: telling a `.docx` from a
|
|
15
|
+
* `.docm` or any other ZIP means reading the archive's central directory,
|
|
16
|
+
* which is at the end.
|
|
17
|
+
*
|
|
18
|
+
* The end-of-central-directory record is 22 bytes, plus an archive comment
|
|
19
|
+
* that office files don't have. Each directory entry is 46 bytes plus its
|
|
20
|
+
* name (and an extra field some tools add), about 100 bytes for a typical
|
|
21
|
+
* office part, so 64 KiB holds a directory of 600 or so entries: every
|
|
22
|
+
* ordinary document and workbook, and slide decks of a few hundred slides.
|
|
23
|
+
* A directory that doesn't fit is reported as `null`, never guessed; use
|
|
24
|
+
* `findZipCentralDirectory()` to read the rest of it.
|
|
25
|
+
*/
|
|
26
|
+
export declare const DETECT_CONTENT_TYPE_TAIL_BYTES: number;
|
|
27
|
+
/**
|
|
28
|
+
* The largest ZIP central directory `detectContentType()` reads: 4 MiB, some
|
|
29
|
+
* 40,000 entries, far beyond any real office file. A ZIP with a bigger
|
|
30
|
+
* directory is reported as `null`, so a crafted archive can't make detection
|
|
31
|
+
* walk (or a caller read) an unbounded directory.
|
|
32
|
+
*/
|
|
33
|
+
export declare const MAX_ZIP_DIRECTORY_BYTES: number;
|
|
34
|
+
export interface DetectContentTypeOptions {
|
|
35
|
+
/**
|
|
36
|
+
* The last bytes of the file, when `bytes` is only its head (for example
|
|
37
|
+
* from two ranged reads of an object in storage). Without it, the end of
|
|
38
|
+
* `bytes` is taken as the end of the file, which is right when `bytes` is
|
|
39
|
+
* the whole file. Read `DETECT_CONTENT_TYPE_TAIL_BYTES`, or the whole file
|
|
40
|
+
* when it is smaller. Only ZIP-based formats use it.
|
|
41
|
+
*/
|
|
42
|
+
tail?: Uint8Array;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Every MIME type `detectContentType()` can return, in its canonical
|
|
46
|
+
* spelling. A claimed type in this set (or an alias of one) is one the bytes
|
|
47
|
+
* can confirm or refute, which is what `acceptedContentType()` relies on.
|
|
48
|
+
*/
|
|
49
|
+
export declare const DETECTABLE_CONTENT_TYPES: ReadonlySet<string>;
|
|
50
|
+
/**
|
|
51
|
+
* Where a ZIP's central directory is, from the start of the file, read from
|
|
52
|
+
* the end-of-central-directory record at the very end of `tail` (the last
|
|
53
|
+
* bytes of the file). `null` when `tail` has no such record describing a
|
|
54
|
+
* non-empty directory, or the archive is one `detectContentType()` won't
|
|
55
|
+
* read (ZIP64, multi-disk, or a directory over `MAX_ZIP_DIRECTORY_BYTES`).
|
|
56
|
+
*
|
|
57
|
+
* For callers that read a large file in ranges: when `detectContentType(head,
|
|
58
|
+
* { tail })` returns `null` for a ZIP because the directory starts before the
|
|
59
|
+
* tail, read the bytes from `offset` up to where the tail starts, and pass
|
|
60
|
+
* them and the tail joined together as the new `tail`.
|
|
61
|
+
*/
|
|
62
|
+
export declare function findZipCentralDirectory(tail: Uint8Array): {
|
|
63
|
+
offset: number;
|
|
64
|
+
size: number;
|
|
65
|
+
} | null;
|
|
66
|
+
/**
|
|
67
|
+
* Tells what kind of file `bytes` is from its contents (its magic bytes), not
|
|
68
|
+
* from a filename or the type a browser claimed. This is content sniffing, in
|
|
69
|
+
* the sense of the WHATWG MIME Sniffing standard, limited to binary formats
|
|
70
|
+
* with unambiguous signatures.
|
|
71
|
+
*
|
|
72
|
+
* Use it wherever a file's type matters for safety: before handing a user
|
|
73
|
+
* upload to an image or video decoder, before serving it inline, before
|
|
74
|
+
* trusting an `accept` list. A file's claimed type and extension are chosen
|
|
75
|
+
* by whoever sent it.
|
|
76
|
+
*
|
|
77
|
+
* Returns a MIME type, or `null` when the format isn't one this function
|
|
78
|
+
* recognises. It never throws and never reads past the end of its input, so
|
|
79
|
+
* truncated or garbage input returns `null`. Pass the whole file, or its
|
|
80
|
+
* first `DETECT_CONTENT_TYPE_BYTES` bytes plus, for a ZIP, its last
|
|
81
|
+
* `DETECT_CONTENT_TYPE_TAIL_BYTES` as `tail`. Without the tail, a `.docx`,
|
|
82
|
+
* `.xlsx` or `.pptx` whose head is all there is returns `null`.
|
|
83
|
+
*
|
|
84
|
+
* Recognised: JPEG, PNG, GIF, WebP, BMP, TIFF, AVIF, HEIC/HEIF (stills and
|
|
85
|
+
* sequences), PDF, MP4 (video, and `audio/mp4` for `.m4a`), QuickTime/MOV,
|
|
86
|
+
* WebM, Matroska, OpenDocument (`.odt`, `.ods`, `.odp`) and Office Open XML
|
|
87
|
+
* (`.docx`, `.xlsx`, `.pptx`, and their macro-enabled `.docm`, `.xlsm`,
|
|
88
|
+
* `.pptm`, which are reported as their own types). MP4, MOV, HEIC and AVIF
|
|
89
|
+
* share the ISO-BMFF `ftyp` box and are told apart by its brands; WebM and
|
|
90
|
+
* Matroska share the EBML header and are told apart by its `DocType`. WebM
|
|
91
|
+
* and Matroska are always reported as video, because the header doesn't say
|
|
92
|
+
* whether there is a video track.
|
|
93
|
+
*
|
|
94
|
+
* Returned types are the canonical spellings (`image/jpeg`, `video/matroska`,
|
|
95
|
+
* `audio/mp4`); see `CONTENT_TYPE_ALIASES` for the others.
|
|
96
|
+
*
|
|
97
|
+
* Not recognised, deliberately: SVG and other text formats (an SVG is XML that
|
|
98
|
+
* can carry script, so it must never pass as a safe image, and text has no
|
|
99
|
+
* reliable signature), ZIPs that aren't one of the office formats above, and
|
|
100
|
+
* legacy binary Office files (`.doc`, `.xls`). See `DETECTABLE_CONTENT_TYPES`
|
|
101
|
+
* for the full list of possible results.
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* import { detectContentType } from '@cedarjs/uploads'
|
|
105
|
+
*
|
|
106
|
+
* const type = detectContentType(bytes)
|
|
107
|
+
*
|
|
108
|
+
* if (type !== 'image/jpeg' && type !== 'image/png') {
|
|
109
|
+
* throw new Error('Only JPEG and PNG images can be thumbnailed')
|
|
110
|
+
* }
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* // A large object in storage: its head, and its tail when it is a ZIP
|
|
114
|
+
* const type = detectContentType(head, { tail })
|
|
115
|
+
*/
|
|
116
|
+
export declare function detectContentType(bytes: Uint8Array, options?: DetectContentTypeOptions): string | null;
|
|
117
|
+
/**
|
|
118
|
+
* Whether a claimed type may be accepted before the file's bytes are seen,
|
|
119
|
+
* as when issuing a presigned upload URL: allowed by `accept`, not a
|
|
120
|
+
* wildcard, and, for a type that can carry script or macros, listed exactly.
|
|
121
|
+
* The bytes still have to be checked once they arrive.
|
|
122
|
+
*/
|
|
123
|
+
export declare function isClaimedContentTypeAllowed(accept: string[], claimedType: string): boolean;
|
|
124
|
+
/**
|
|
125
|
+
* Decides whether a file is one of the `accept`ed types by what its bytes
|
|
126
|
+
* are, and returns the content type to record for it, or `null` when the
|
|
127
|
+
* file is not accepted. `accept` takes the same entries as an upload
|
|
128
|
+
* profile's `allowedMimeTypes`: exact types and `type/*` wildcards, with
|
|
129
|
+
* either spelling of an aliased type (`CONTENT_TYPE_ALIASES`).
|
|
130
|
+
*
|
|
131
|
+
* - When `detectContentType()` recognises the bytes, the detected type must
|
|
132
|
+
* match `accept`, and is what's returned. The claimed type doesn't matter,
|
|
133
|
+
* so a HEIC photo a browser sent as `application/octet-stream` is accepted
|
|
134
|
+
* as `image/heic`. A macro-enabled Office file (`MACRO_CONTENT_TYPES`)
|
|
135
|
+
* must be listed exactly: `['application/*']` never admits a `.docm`.
|
|
136
|
+
* - When it doesn't, the file is accepted on the strength of `claimedType`
|
|
137
|
+
* when that matches `accept`, wildcards included, except:
|
|
138
|
+
* - a claimed type in `DETECTABLE_CONTENT_TYPES` is refused: a real PNG
|
|
139
|
+
* would have been recognised, so the claim is false;
|
|
140
|
+
* - a type that can carry script (`SCRIPTABLE_CONTENT_TYPES`, and any XML
|
|
141
|
+
* type) or macros (`MACRO_CONTENT_TYPES`, which includes the legacy
|
|
142
|
+
* `.doc`, `.xls` and `.ppt` types) must be listed exactly. `['image/*']`
|
|
143
|
+
* never admits an SVG, `['text/*']` never admits HTML, and `['*\/*']`
|
|
144
|
+
* never admits a `.doc`.
|
|
145
|
+
*
|
|
146
|
+
* No claimed type means not accepted.
|
|
147
|
+
*
|
|
148
|
+
* The returned type is canonical: lower-cased, without parameters, and with
|
|
149
|
+
* aliases resolved, so a JPEG claimed as `image/jpg` is stored as
|
|
150
|
+
* `image/jpeg`.
|
|
151
|
+
*
|
|
152
|
+
* Pass the whole file, or its head with `options.tail` (see
|
|
153
|
+
* `detectContentType()`). An office file checked by its head alone isn't
|
|
154
|
+
* recognised, and its claimed type is then refused as false.
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* import { acceptedContentType, storeFile } from '@cedarjs/uploads'
|
|
158
|
+
*
|
|
159
|
+
* const mimeType = acceptedContentType(data, ['image/*'], input.mimeType)
|
|
160
|
+
*
|
|
161
|
+
* if (!mimeType) {
|
|
162
|
+
* throw new Error('That file is not an image')
|
|
163
|
+
* }
|
|
164
|
+
*
|
|
165
|
+
* await storeFile(target, { db, filename, mimeType, data })
|
|
166
|
+
*/
|
|
167
|
+
export declare function acceptedContentType(bytes: Uint8Array, accept: string[], claimedType?: string, options?: DetectContentTypeOptions): string | null;
|
|
168
|
+
//# sourceMappingURL=detectContentType.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detectContentType.d.ts","sourceRoot":"","sources":["../src/detectContentType.ts"],"names":[],"mappings":"AAOA;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,OAAO,CAAA;AAE7C;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,8BAA8B,QAAY,CAAA;AAEvD;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,QAAkB,CAAA;AAEtD,MAAM,WAAW,wBAAwB;IACvC;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,UAAU,CAAA;CAClB;AAgBD;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,WAAW,CAAC,MAAM,CA2BvD,CAAA;AA+eF;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,UAAU,GACf;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAUzC;AAoHD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,UAAU,EACjB,OAAO,GAAE,wBAA6B,GACrC,MAAM,GAAG,IAAI,CAmCf;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,MAAM,EAAE,EAChB,WAAW,EAAE,MAAM,GAClB,OAAO,CAUT;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,MAAM,EAAE,EAChB,WAAW,CAAC,EAAE,MAAM,EACpB,OAAO,GAAE,wBAA6B,GACrC,MAAM,GAAG,IAAI,CAyBf"}
|