file-type 20.4.1 → 21.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/core.d.ts CHANGED
@@ -24,16 +24,17 @@ export type FileTypeResult = {
24
24
  };
25
25
 
26
26
  /**
27
- Detect the file type of a `Uint8Array`, or `ArrayBuffer`.
27
+ Detect the file type of a `Uint8Array` or `ArrayBuffer`.
28
28
 
29
29
  The file type is detected by checking the [magic number](https://en.wikipedia.org/wiki/Magic_number_(programming)#Magic_numbers_in_files) of the buffer.
30
30
 
31
31
  If file access is available, it is recommended to use `.fromFile()` instead.
32
32
 
33
33
  @param buffer - An Uint8Array or ArrayBuffer representing file data. It works best if the buffer contains the entire file. It may work with a smaller portion as well.
34
+ @param options - Options to override default behavior.
34
35
  @returns The detected file type, or `undefined` when there is no match.
35
36
  */
36
- export function fileTypeFromBuffer(buffer: Uint8Array | ArrayBuffer): Promise<FileTypeResult | undefined>;
37
+ export function fileTypeFromBuffer(buffer: Uint8Array | ArrayBuffer, options?: FileTypeOptions): Promise<FileTypeResult | undefined>;
37
38
 
38
39
  /**
39
40
  Detect the file type of a [web `ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream).
@@ -41,9 +42,10 @@ Detect the file type of a [web `ReadableStream`](https://developer.mozilla.org/e
41
42
  The file type is detected by checking the [magic number](https://en.wikipedia.org/wiki/Magic_number_(programming)#Magic_numbers_in_files) of the buffer.
42
43
 
43
44
  @param stream - A [web `ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) streaming a file to examine.
45
+ @param options - Options to override default behavior.
44
46
  @returns A `Promise` for an object with the detected file type, or `undefined` when there is no match.
45
47
  */
46
- export function fileTypeFromStream(stream: AnyWebByteStream): Promise<FileTypeResult | undefined>;
48
+ export function fileTypeFromStream(stream: AnyWebByteStream, options?: FileTypeOptions): Promise<FileTypeResult | undefined>;
47
49
 
48
50
  /**
49
51
  Detect the file type from an [`ITokenizer`](https://github.com/Borewit/strtok3#tokenizer) source.
@@ -53,6 +55,7 @@ This method is used internally, but can also be used for a special "tokenizer" r
53
55
  A tokenizer propagates the internal read functions, allowing alternative transport mechanisms, to access files, to be implemented and used.
54
56
 
55
57
  @param tokenizer - File source implementing the tokenizer interface.
58
+ @param options - Options to override default behavior.
56
59
  @returns The detected file type, or `undefined` when there is no match.
57
60
 
58
61
  An example is [`@tokenizer/http`](https://github.com/Borewit/tokenizer-http), which requests data using [HTTP-range-requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests). A difference with a conventional stream and the [*tokenizer*](https://github.com/Borewit/strtok3#tokenizer), is that it is able to *ignore* (seek, fast-forward) in the stream. For example, you may only need and read the first 6 bytes, and the last 128 bytes, which may be an advantage in case reading the entire file would take longer.
@@ -71,7 +74,7 @@ console.log(fileType);
71
74
  //=> {ext: 'mp3', mime: 'audio/mpeg'}
72
75
  ```
73
76
  */
74
- export function fileTypeFromTokenizer(tokenizer: ITokenizer): Promise<FileTypeResult | undefined>;
77
+ export function fileTypeFromTokenizer(tokenizer: ITokenizer, options?: FileTypeOptions): Promise<FileTypeResult | undefined>;
75
78
 
76
79
  /**
77
80
  Supported file extensions.
@@ -96,6 +99,7 @@ export type StreamOptions = {
96
99
  Detect the file type of a [`Blob`](https://nodejs.org/api/buffer.html#class-blob) or [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File).
97
100
 
98
101
  @param blob - The [`Blob`](https://nodejs.org/api/buffer.html#class-blob) used for file detection.
102
+ @param options - Options to override default behavior.
99
103
  @returns The detected file type, or `undefined` when there is no match.
100
104
 
101
105
  @example
@@ -111,7 +115,7 @@ console.log(await fileTypeFromBlob(blob));
111
115
  //=> {ext: 'txt', mime: 'text/plain'}
112
116
  ```
113
117
  */
114
- export declare function fileTypeFromBlob(blob: Blob): Promise<FileTypeResult | undefined>;
118
+ export declare function fileTypeFromBlob(blob: Blob, options?: FileTypeOptions): Promise<FileTypeResult | undefined>;
115
119
 
116
120
  /**
117
121
  A custom file type detector.
@@ -184,6 +188,17 @@ export type Detector = {
184
188
 
185
189
  export type FileTypeOptions = {
186
190
  customDetectors?: Iterable<Detector>;
191
+
192
+ /**
193
+ Specifies the byte tolerance for locating the first MPEG audio frame (e.g. `.mp1`, `.mp2`, `.mp3`, `.aac`).
194
+
195
+ Allows detection to handle slight sync offsets between the expected and actual frame start. Common in malformed or incorrectly muxed files, which, while technically invalid, do occur in the wild.
196
+
197
+ A tolerance of 10 bytes covers most cases.
198
+
199
+ @default 0
200
+ */
201
+ mpegOffsetTolerance?: number;
187
202
  };
188
203
 
189
204
  export declare class TokenizerPositionError extends Error {
package/core.js CHANGED
@@ -5,7 +5,7 @@ Primary entry point, Node.js specific entry point is index.js
5
5
  import * as Token from 'token-types';
6
6
  import * as strtok3 from 'strtok3/core';
7
7
  import {ZipHandler} from '@tokenizer/inflate';
8
- import {includes, getUintBE} from 'uint8array-extras';
8
+ import {getUintBE} from 'uint8array-extras';
9
9
  import {
10
10
  stringToBytes,
11
11
  tarHeaderChecksumMatches,
@@ -15,124 +15,135 @@ import {extensions, mimeTypes} from './supported.js';
15
15
 
16
16
  export const reasonableDetectionSizeInBytes = 4100; // A fair amount of file-types are detectable within this range.
17
17
 
18
- export async function fileTypeFromStream(stream) {
19
- return new FileTypeParser().fromStream(stream);
18
+ export async function fileTypeFromStream(stream, options) {
19
+ return new FileTypeParser(options).fromStream(stream);
20
20
  }
21
21
 
22
- export async function fileTypeFromBuffer(input) {
23
- return new FileTypeParser().fromBuffer(input);
22
+ export async function fileTypeFromBuffer(input, options) {
23
+ return new FileTypeParser(options).fromBuffer(input);
24
24
  }
25
25
 
26
- export async function fileTypeFromBlob(blob) {
27
- return new FileTypeParser().fromBlob(blob);
26
+ export async function fileTypeFromBlob(blob, options) {
27
+ return new FileTypeParser(options).fromBlob(blob);
28
28
  }
29
29
 
30
30
  function getFileTypeFromMimeType(mimeType) {
31
+ mimeType = mimeType.toLowerCase();
31
32
  switch (mimeType) {
32
33
  case 'application/epub+zip':
33
34
  return {
34
35
  ext: 'epub',
35
- mime: 'application/epub+zip',
36
+ mime: mimeType,
36
37
  };
37
38
  case 'application/vnd.oasis.opendocument.text':
38
39
  return {
39
40
  ext: 'odt',
40
- mime: 'application/vnd.oasis.opendocument.text',
41
+ mime: mimeType,
41
42
  };
42
43
  case 'application/vnd.oasis.opendocument.text-template':
43
44
  return {
44
45
  ext: 'ott',
45
- mime: 'application/vnd.oasis.opendocument.text-template',
46
+ mime: mimeType,
46
47
  };
47
48
  case 'application/vnd.oasis.opendocument.spreadsheet':
48
49
  return {
49
50
  ext: 'ods',
50
- mime: 'application/vnd.oasis.opendocument.spreadsheet',
51
+ mime: mimeType,
51
52
  };
52
53
  case 'application/vnd.oasis.opendocument.spreadsheet-template':
53
54
  return {
54
55
  ext: 'ots',
55
- mime: 'application/vnd.oasis.opendocument.spreadsheet-template',
56
+ mime: mimeType,
56
57
  };
57
58
  case 'application/vnd.oasis.opendocument.presentation':
58
59
  return {
59
60
  ext: 'odp',
60
- mime: 'application/vnd.oasis.opendocument.presentation',
61
+ mime: mimeType,
61
62
  };
62
63
  case 'application/vnd.oasis.opendocument.presentation-template':
63
64
  return {
64
65
  ext: 'otp',
65
- mime: 'application/vnd.oasis.opendocument.presentation-template',
66
+ mime: mimeType,
66
67
  };
67
68
  case 'application/vnd.oasis.opendocument.graphics':
68
69
  return {
69
70
  ext: 'odg',
70
- mime: 'application/vnd.oasis.opendocument.graphics',
71
+ mime: mimeType,
71
72
  };
72
73
  case 'application/vnd.oasis.opendocument.graphics-template':
73
74
  return {
74
75
  ext: 'otg',
75
- mime: 'application/vnd.oasis.opendocument.graphics-template',
76
+ mime: mimeType,
77
+ };
78
+ case 'application/vnd.openxmlformats-officedocument.presentationml.slideshow':
79
+ return {
80
+ ext: 'ppsx',
81
+ mime: mimeType,
76
82
  };
77
83
  case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
78
84
  return {
79
85
  ext: 'xlsx',
80
- mime: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
86
+ mime: mimeType,
81
87
  };
82
- case 'application/vnd.ms-excel.sheet.macroEnabled':
88
+ case 'application/vnd.ms-excel.sheet.macroenabled':
83
89
  return {
84
90
  ext: 'xlsm',
85
- mime: 'application/vnd.ms-excel.sheet.macroEnabled.12',
91
+ mime: 'application/vnd.ms-excel.sheet.macroenabled.12',
86
92
  };
87
93
  case 'application/vnd.openxmlformats-officedocument.spreadsheetml.template':
88
94
  return {
89
95
  ext: 'xltx',
90
- mime: 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
96
+ mime: mimeType,
91
97
  };
92
- case 'application/vnd.ms-excel.template.macroEnabled':
98
+ case 'application/vnd.ms-excel.template.macroenabled':
93
99
  return {
94
100
  ext: 'xltm',
95
101
  mime: 'application/vnd.ms-excel.template.macroenabled.12',
96
102
  };
103
+ case 'application/vnd.ms-powerpoint.slideshow.macroenabled':
104
+ return {
105
+ ext: 'ppsm',
106
+ mime: 'application/vnd.ms-powerpoint.slideshow.macroenabled.12',
107
+ };
97
108
  case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
98
109
  return {
99
110
  ext: 'docx',
100
- mime: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
111
+ mime: mimeType,
101
112
  };
102
- case 'application/vnd.ms-word.document.macroEnabled':
113
+ case 'application/vnd.ms-word.document.macroenabled':
103
114
  return {
104
115
  ext: 'docm',
105
- mime: 'application/vnd.ms-word.document.macroEnabled.12',
116
+ mime: 'application/vnd.ms-word.document.macroenabled.12',
106
117
  };
107
118
  case 'application/vnd.openxmlformats-officedocument.wordprocessingml.template':
108
119
  return {
109
120
  ext: 'dotx',
110
- mime: 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
121
+ mime: mimeType,
111
122
  };
112
- case 'application/vnd.ms-word.template.macroEnabledTemplate':
123
+ case 'application/vnd.ms-word.template.macroenabledtemplate':
113
124
  return {
114
125
  ext: 'dotm',
115
- mime: 'application/vnd.ms-word.template.macroEnabled.12',
126
+ mime: 'application/vnd.ms-word.template.macroenabled.12',
116
127
  };
117
128
  case 'application/vnd.openxmlformats-officedocument.presentationml.template':
118
129
  return {
119
130
  ext: 'potx',
120
- mime: 'application/vnd.openxmlformats-officedocument.presentationml.template',
131
+ mime: mimeType,
121
132
  };
122
- case 'application/vnd.ms-powerpoint.template.macroEnabled':
133
+ case 'application/vnd.ms-powerpoint.template.macroenabled':
123
134
  return {
124
135
  ext: 'potm',
125
- mime: 'application/vnd.ms-powerpoint.template.macroEnabled.12',
136
+ mime: 'application/vnd.ms-powerpoint.template.macroenabled.12',
126
137
  };
127
138
  case 'application/vnd.openxmlformats-officedocument.presentationml.presentation':
128
139
  return {
129
140
  ext: 'pptx',
130
- mime: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
141
+ mime: mimeType,
131
142
  };
132
- case 'application/vnd.ms-powerpoint.presentation.macroEnabled':
143
+ case 'application/vnd.ms-powerpoint.presentation.macroenabled':
133
144
  return {
134
145
  ext: 'pptm',
135
- mime: 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
146
+ mime: 'application/vnd.ms-powerpoint.presentation.macroenabled.12',
136
147
  };
137
148
  case 'application/vnd.ms-visio.drawing':
138
149
  return {
@@ -169,8 +180,8 @@ function _check(buffer, headers, options) {
169
180
  return true;
170
181
  }
171
182
 
172
- export async function fileTypeFromTokenizer(tokenizer) {
173
- return new FileTypeParser().fromTokenizer(tokenizer);
183
+ export async function fileTypeFromTokenizer(tokenizer, options) {
184
+ return new FileTypeParser(options).fromTokenizer(tokenizer);
174
185
  }
175
186
 
176
187
  export async function fileTypeStream(webStream, options) {
@@ -179,6 +190,11 @@ export async function fileTypeStream(webStream, options) {
179
190
 
180
191
  export class FileTypeParser {
181
192
  constructor(options) {
193
+ this.options = {
194
+ mpegOffsetTolerance: 0,
195
+ ...options,
196
+ };
197
+
182
198
  this.detectors = [...(options?.customDetectors ?? []),
183
199
  {id: 'core', detect: this.detectConfident},
184
200
  {id: 'core.imprecise', detect: this.detectImprecise}];
@@ -243,7 +259,7 @@ export class FileTypeParser {
243
259
  if (!done && chunk) {
244
260
  try {
245
261
  // Attempt to detect the file type from the chunk
246
- detectedFileType = await this.fromBuffer(chunk.slice(0, sampleSize));
262
+ detectedFileType = await this.fromBuffer(chunk.subarray(0, sampleSize));
247
263
  } catch (error) {
248
264
  if (!(error instanceof strtok3.EndOfStreamError)) {
249
265
  throw error; // Re-throw non-EndOfStreamError
@@ -699,7 +715,7 @@ export class FileTypeParser {
699
715
  if (this.checkString('fLaC')) {
700
716
  return {
701
717
  ext: 'flac',
702
- mime: 'audio/x-flac',
718
+ mime: 'audio/flac',
703
719
  };
704
720
  }
705
721
 
@@ -718,28 +734,6 @@ export class FileTypeParser {
718
734
  }
719
735
 
720
736
  if (this.checkString('%PDF')) {
721
- try {
722
- const skipBytes = 1350;
723
- if (skipBytes === await tokenizer.ignore(skipBytes)) {
724
- const maxBufferSize = 10 * 1024 * 1024;
725
- const buffer = new Uint8Array(Math.min(maxBufferSize, tokenizer.fileInfo.size - skipBytes));
726
- await tokenizer.readBuffer(buffer, {mayBeLess: true});
727
-
728
- // Check if this is an Adobe Illustrator file
729
- if (includes(buffer, new TextEncoder().encode('AIPrivateData'))) {
730
- return {
731
- ext: 'ai',
732
- mime: 'application/postscript',
733
- };
734
- }
735
- }
736
- } catch (error) {
737
- // Swallow end of stream error if file is too small for the Adobe AI check
738
- if (!(error instanceof strtok3.EndOfStreamError)) {
739
- throw error;
740
- }
741
- }
742
-
743
737
  // Assume this is just a normal PDF
744
738
  return {
745
739
  ext: 'pdf',
@@ -836,7 +830,7 @@ export class FileTypeParser {
836
830
  case 'matroska':
837
831
  return {
838
832
  ext: 'mkv',
839
- mime: 'video/x-matroska',
833
+ mime: 'video/matroska',
840
834
  };
841
835
 
842
836
  default:
@@ -910,10 +904,10 @@ export class FileTypeParser {
910
904
  };
911
905
  }
912
906
 
913
- if (this.checkString('PAR1')) {
907
+ if (this.checkString('PAR1') || this.checkString('PARE')) {
914
908
  return {
915
909
  ext: 'parquet',
916
- mime: 'application/x-parquet',
910
+ mime: 'application/vnd.apache.parquet',
917
911
  };
918
912
  }
919
913
 
@@ -1189,7 +1183,7 @@ export class FileTypeParser {
1189
1183
  if (this.check([0x41, 0x52, 0x52, 0x4F, 0x57, 0x31, 0x00, 0x00])) {
1190
1184
  return {
1191
1185
  ext: 'arrow',
1192
- mime: 'application/x-apache-arrow',
1186
+ mime: 'application/vnd.apache.arrow.file',
1193
1187
  };
1194
1188
  }
1195
1189
 
@@ -1531,7 +1525,7 @@ export class FileTypeParser {
1531
1525
 
1532
1526
  if (jsonSize > 12 && this.buffer.length >= jsonSize + 16) {
1533
1527
  try {
1534
- const header = new TextDecoder().decode(this.buffer.slice(16, jsonSize + 16));
1528
+ const header = new TextDecoder().decode(this.buffer.subarray(16, jsonSize + 16));
1535
1529
  const json = JSON.parse(header);
1536
1530
  // Check if Pickle is ASAR
1537
1531
  if (json.files) { // Final check, assuring Pickle/ASAR format
@@ -1634,7 +1628,8 @@ export class FileTypeParser {
1634
1628
  await tokenizer.peekBuffer(this.buffer, {length: Math.min(512, tokenizer.fileInfo.size), mayBeLess: true});
1635
1629
 
1636
1630
  // Requires a buffer size of 512 bytes
1637
- if (tarHeaderChecksumMatches(this.buffer)) {
1631
+ if ((this.checkString('ustar', {offset: 257}) && (this.checkString('\0', {offset: 262}) || this.checkString(' ', {offset: 262})))
1632
+ || (this.check([0, 0, 0, 0, 0, 0], {offset: 257}) && tarHeaderChecksumMatches(this.buffer))) {
1638
1633
  return {
1639
1634
  ext: 'tar',
1640
1635
  mime: 'application/x-tar',
@@ -1704,47 +1699,16 @@ export class FileTypeParser {
1704
1699
  };
1705
1700
  }
1706
1701
 
1702
+ // Adjust buffer to `mpegOffsetTolerance`
1703
+ await tokenizer.peekBuffer(this.buffer, {length: Math.min(2 + this.options.mpegOffsetTolerance, tokenizer.fileInfo.size), mayBeLess: true});
1704
+
1707
1705
  // Check MPEG 1 or 2 Layer 3 header, or 'layer 0' for ADTS (MPEG sync-word 0xFFE)
1708
- if (this.buffer.length >= 2 && this.check([0xFF, 0xE0], {offset: 0, mask: [0xFF, 0xE0]})) {
1709
- if (this.check([0x10], {offset: 1, mask: [0x16]})) {
1710
- // Check for (ADTS) MPEG-2
1711
- if (this.check([0x08], {offset: 1, mask: [0x08]})) {
1712
- return {
1713
- ext: 'aac',
1714
- mime: 'audio/aac',
1715
- };
1706
+ if (this.buffer.length >= (2 + this.options.mpegOffsetTolerance)) {
1707
+ for (let depth = 0; depth <= this.options.mpegOffsetTolerance; ++depth) {
1708
+ const type = this.scanMpeg(depth);
1709
+ if (type) {
1710
+ return type;
1716
1711
  }
1717
-
1718
- // Must be (ADTS) MPEG-4
1719
- return {
1720
- ext: 'aac',
1721
- mime: 'audio/aac',
1722
- };
1723
- }
1724
-
1725
- // MPEG 1 or 2 Layer 3 header
1726
- // Check for MPEG layer 3
1727
- if (this.check([0x02], {offset: 1, mask: [0x06]})) {
1728
- return {
1729
- ext: 'mp3',
1730
- mime: 'audio/mpeg',
1731
- };
1732
- }
1733
-
1734
- // Check for MPEG layer 2
1735
- if (this.check([0x04], {offset: 1, mask: [0x06]})) {
1736
- return {
1737
- ext: 'mp2',
1738
- mime: 'audio/mpeg',
1739
- };
1740
- }
1741
-
1742
- // Check for MPEG layer 1
1743
- if (this.check([0x06], {offset: 1, mask: [0x06]})) {
1744
- return {
1745
- ext: 'mp1',
1746
- mime: 'audio/mpeg',
1747
- };
1748
1712
  }
1749
1713
  }
1750
1714
  };
@@ -1821,6 +1785,57 @@ export class FileTypeParser {
1821
1785
  };
1822
1786
  }
1823
1787
  }
1788
+
1789
+ /**
1790
+ Scan check MPEG 1 or 2 Layer 3 header, or 'layer 0' for ADTS (MPEG sync-word 0xFFE).
1791
+
1792
+ @param offset - Offset to scan for sync-preamble.
1793
+ @returns {{ext: string, mime: string}}
1794
+ */
1795
+ scanMpeg(offset) {
1796
+ if (this.check([0xFF, 0xE0], {offset, mask: [0xFF, 0xE0]})) {
1797
+ if (this.check([0x10], {offset: offset + 1, mask: [0x16]})) {
1798
+ // Check for (ADTS) MPEG-2
1799
+ if (this.check([0x08], {offset: offset + 1, mask: [0x08]})) {
1800
+ return {
1801
+ ext: 'aac',
1802
+ mime: 'audio/aac',
1803
+ };
1804
+ }
1805
+
1806
+ // Must be (ADTS) MPEG-4
1807
+ return {
1808
+ ext: 'aac',
1809
+ mime: 'audio/aac',
1810
+ };
1811
+ }
1812
+
1813
+ // MPEG 1 or 2 Layer 3 header
1814
+ // Check for MPEG layer 3
1815
+ if (this.check([0x02], {offset: offset + 1, mask: [0x06]})) {
1816
+ return {
1817
+ ext: 'mp3',
1818
+ mime: 'audio/mpeg',
1819
+ };
1820
+ }
1821
+
1822
+ // Check for MPEG layer 2
1823
+ if (this.check([0x04], {offset: offset + 1, mask: [0x06]})) {
1824
+ return {
1825
+ ext: 'mp2',
1826
+ mime: 'audio/mpeg',
1827
+ };
1828
+ }
1829
+
1830
+ // Check for MPEG layer 1
1831
+ if (this.check([0x06], {offset: offset + 1, mask: [0x06]})) {
1832
+ return {
1833
+ ext: 'mp1',
1834
+ mime: 'audio/mpeg',
1835
+ };
1836
+ }
1837
+ }
1838
+ }
1824
1839
  }
1825
1840
 
1826
1841
  export const supportedExtensions = new Set(extensions);
package/index.d.ts CHANGED
@@ -8,8 +8,8 @@ import {
8
8
  type FileTypeResult,
9
9
  type StreamOptions,
10
10
  type AnyWebReadableStream,
11
- type Detector,
12
11
  type AnyWebReadableByteStreamWithFileType,
12
+ type FileTypeOptions,
13
13
  FileTypeParser as DefaultFileTypeParser,
14
14
  } from './core.js';
15
15
 
@@ -31,8 +31,8 @@ export declare class FileTypeParser extends DefaultFileTypeParser {
31
31
  /**
32
32
  Works the same way as {@link fileTypeStream}, additionally taking into account custom detectors (if any were provided to the constructor).
33
33
  */
34
- toDetectionStream(readableStream: NodeReadableStream, options?: StreamOptions): Promise<ReadableStreamWithFileType>;
35
- toDetectionStream(webStream: AnyWebReadableStream<Uint8Array>, options?: StreamOptions): Promise<AnyWebReadableByteStreamWithFileType>;
34
+ toDetectionStream(readableStream: NodeReadableStream, options?: FileTypeOptions & StreamOptions): Promise<ReadableStreamWithFileType>;
35
+ toDetectionStream(webStream: AnyWebReadableStream<Uint8Array>, options?: FileTypeOptions & StreamOptions): Promise<AnyWebReadableByteStreamWithFileType>;
36
36
  }
37
37
 
38
38
  /**
@@ -48,7 +48,7 @@ The file type is detected by checking the [magic number](https://en.wikipedia.or
48
48
 
49
49
  @returns The detected file type and MIME type or `undefined` when there is no match.
50
50
  */
51
- export function fileTypeFromFile(filePath: string, options?: {customDetectors?: Iterable<Detector>}): Promise<FileTypeResult | undefined>;
51
+ export function fileTypeFromFile(filePath: string, options?: FileTypeOptions): Promise<FileTypeResult | undefined>;
52
52
 
53
53
  /**
54
54
  Detect the file type of a [web `ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream).
@@ -60,9 +60,10 @@ Direct support for Node.js streams will be dropped in the future, when Node.js s
60
60
  The file type is detected by checking the [magic number](https://en.wikipedia.org/wiki/Magic_number_(programming)#Magic_numbers_in_files) of the buffer.
61
61
 
62
62
  @param stream - A [web `ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) or [Node.js `stream.Readable`](https://nodejs.org/api/stream.html#stream_class_stream_readable) streaming a file to examine.
63
- @returns A `Promise` for an object with the detected file type, or `undefined` when there is no match.
63
+ @param options - Options to override default behaviour.
64
+ @returns A `Promise` for an object with the detected file type, or `undefined` when there is no match.
64
65
  */
65
- export function fileTypeFromStream(stream: AnyWebReadableStream<Uint8Array> | NodeReadableStream): Promise<FileTypeResult | undefined>;
66
+ export function fileTypeFromStream(stream: AnyWebReadableStream<Uint8Array> | NodeReadableStream, options?: FileTypeOptions): Promise<FileTypeResult | undefined>;
66
67
 
67
68
  /**
68
69
  Returns a `Promise` which resolves to the original readable stream argument, but with an added `fileType` property, which is an object like the one returned from `fileTypeFromFile()`.
@@ -91,7 +92,7 @@ if (stream2.fileType?.mime === 'image/jpeg') {
91
92
  }
92
93
  ```
93
94
  */
94
- export function fileTypeStream(readableStream: NodeReadableStream, options?: StreamOptions): Promise<ReadableStreamWithFileType>;
95
- export function fileTypeStream(webStream: AnyWebByteStream, options?: StreamOptions): Promise<AnyWebReadableByteStreamWithFileType>;
95
+ export function fileTypeStream(readableStream: NodeReadableStream, options?: FileTypeOptions & StreamOptions): Promise<ReadableStreamWithFileType>;
96
+ export function fileTypeStream(webStream: AnyWebByteStream, options?: FileTypeOptions & StreamOptions): Promise<AnyWebReadableByteStreamWithFileType>;
96
97
 
97
98
  export * from './core.js';
package/index.js CHANGED
@@ -65,12 +65,12 @@ export class FileTypeParser extends DefaultFileTypeParser {
65
65
  }
66
66
  }
67
67
 
68
- export async function fileTypeFromFile(path, fileTypeOptions) {
69
- return (new FileTypeParser(fileTypeOptions)).fromFile(path, fileTypeOptions);
68
+ export async function fileTypeFromFile(path, options) {
69
+ return (new FileTypeParser(options)).fromFile(path, options);
70
70
  }
71
71
 
72
- export async function fileTypeFromStream(stream, fileTypeOptions) {
73
- return (new FileTypeParser(fileTypeOptions)).fromStream(stream);
72
+ export async function fileTypeFromStream(stream, options) {
73
+ return (new FileTypeParser(options)).fromStream(stream);
74
74
  }
75
75
 
76
76
  export async function fileTypeStream(readableStream, options = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "file-type",
3
- "version": "20.4.1",
3
+ "version": "21.0.0",
4
4
  "description": "Detect the file type of a file, stream, or data",
5
5
  "license": "MIT",
6
6
  "repository": "sindresorhus/file-type",
@@ -35,7 +35,7 @@
35
35
  },
36
36
  "sideEffects": false,
37
37
  "engines": {
38
- "node": ">=18"
38
+ "node": ">=20"
39
39
  },
40
40
  "scripts": {
41
41
  "test": "xo && ava && tsd"
@@ -198,7 +198,6 @@
198
198
  "it",
199
199
  "s3m",
200
200
  "xm",
201
- "ai",
202
201
  "skp",
203
202
  "avif",
204
203
  "eps",
@@ -242,22 +241,24 @@
242
241
  "potm",
243
242
  "pptm",
244
243
  "jar",
245
- "rm"
244
+ "rm",
245
+ "ppsm",
246
+ "ppsx"
246
247
  ],
247
248
  "dependencies": {
248
- "@tokenizer/inflate": "^0.2.6",
249
- "strtok3": "^10.2.0",
249
+ "@tokenizer/inflate": "^0.2.7",
250
+ "strtok3": "^10.2.2",
250
251
  "token-types": "^6.0.0",
251
252
  "uint8array-extras": "^1.4.0"
252
253
  },
253
254
  "devDependencies": {
254
255
  "@tokenizer/token": "^0.3.0",
255
- "@types/node": "^22.10.5",
256
- "ava": "^6.0.1",
256
+ "@types/node": "^22.15.21",
257
+ "ava": "^6.3.0",
257
258
  "commonmark": "^0.31.2",
258
259
  "get-stream": "^9.0.1",
259
260
  "noop-stream": "^1.0.0",
260
- "tsd": "^0.31.2",
261
+ "tsd": "^0.32.0",
261
262
  "xo": "^0.60.0"
262
263
  },
263
264
  "xo": {
package/readme.md CHANGED
@@ -109,7 +109,7 @@ console.log(fileType);
109
109
 
110
110
  ## API
111
111
 
112
- ### fileTypeFromBuffer(buffer)
112
+ ### fileTypeFromBuffer(buffer, options)
113
113
 
114
114
  Detect the file type of a `Uint8Array`, or `ArrayBuffer`.
115
115
 
@@ -130,13 +130,13 @@ Type: `Uint8Array | ArrayBuffer`
130
130
 
131
131
  A buffer representing file data. It works best if the buffer contains the entire file. It may work with a smaller portion as well.
132
132
 
133
- ### fileTypeFromFile(filePath)
133
+ ### fileTypeFromFile(filePath, options)
134
134
 
135
135
  Detect the file type of a file path.
136
136
 
137
137
  This is for Node.js only.
138
138
 
139
- To read from a [`File`](https://developer.mozilla.org/docs/Web/API/File), see [`fileTypeFromBlob()`](#filetypefromblobblob).
139
+ To read from a [`File`](https://developer.mozilla.org/docs/Web/API/File), see [`fileTypeFromBlob()`](#filetypefromblobblob-options).
140
140
 
141
141
  The file type is detected by checking the [magic number](https://en.wikipedia.org/wiki/Magic_number_(programming)#Magic_numbers_in_files) of the buffer.
142
142
 
@@ -176,7 +176,7 @@ Type: [Web `ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/Re
176
176
 
177
177
  A readable stream representing file data.
178
178
 
179
- ### fileTypeFromBlob(blob)
179
+ ### fileTypeFromBlob(blob, options)
180
180
 
181
181
  Detect the file type of a [`Blob`](https://developer.mozilla.org/docs/Web/API/Blob),
182
182
 
@@ -225,7 +225,7 @@ async function readFromBlobWithoutStreaming(blob) {
225
225
 
226
226
  Type: [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob)
227
227
 
228
- ### fileTypeFromTokenizer(tokenizer)
228
+ ### fileTypeFromTokenizer(tokenizer, options)
229
229
 
230
230
  Detect the file type from an `ITokenizer` source.
231
231
 
@@ -304,6 +304,8 @@ Type: [`stream.Readable`](https://nodejs.org/api/stream.html#stream_class_stream
304
304
 
305
305
  Type: `object`
306
306
 
307
+ Supports the following options in addition to the [general options](#options):
308
+
307
309
  ##### sampleSize
308
310
 
309
311
  Type: `number`\
@@ -341,6 +343,32 @@ Returns a `Set<string>` of supported file extensions.
341
343
 
342
344
  Returns a `Set<string>` of supported MIME types.
343
345
 
346
+ ### Options
347
+
348
+ #### customDetectors
349
+
350
+ Array of custom file type detectors to run before default detectors.
351
+
352
+ For example:
353
+
354
+ ```js
355
+ import {fileTypeFromFile} from 'file-type';
356
+ import {detectXml} from '@file-type/xml';
357
+
358
+ const fileType = await fileTypeFromFile('sample.kml', {customDetectors: [detectXml]});
359
+ console.log(fileType);
360
+ ```
361
+
362
+ #### mpegOffsetTolerance
363
+
364
+ Default: `0`
365
+
366
+ Specifies the byte tolerance for locating the first MPEG audio frame (e.g. `.mp1`, `.mp2`, `.mp3`, `.aac`).
367
+
368
+ Allows detection to handle slight sync offsets between the expected and actual frame start. Common in malformed or incorrectly muxed files, which, while technically invalid, do occur in the wild.
369
+
370
+ A tolerance of 10 bytes covers most cases.
371
+
344
372
  ## Custom detectors
345
373
 
346
374
  Custom file type detectors are plugins designed to extend the default detection capabilities.
@@ -353,6 +381,8 @@ Detectors can be added via the constructor options or by directly modifying `Fil
353
381
 
354
382
  ### Example adding a detector
355
383
 
384
+ For example:
385
+
356
386
  ```js
357
387
  import {FileTypeParser} from 'file-type';
358
388
  import {detectXml} from '@file-type/xml';
@@ -436,7 +466,6 @@ abortController.abort(); // Abort file-type reading from the Blob stream.
436
466
  - [`aac`](https://en.wikipedia.org/wiki/Advanced_Audio_Coding) - Advanced Audio Coding
437
467
  - [`ac3`](https://www.atsc.org/standard/a522012-digital-audio-compression-ac-3-e-ac-3-standard-12172012/) - ATSC A/52 Audio File
438
468
  - [`ace`](https://en.wikipedia.org/wiki/ACE_(compressed_file_format)) - ACE archive
439
- - [`ai`](https://en.wikipedia.org/wiki/Adobe_Illustrator_Artwork) - Adobe Illustrator Artwork
440
469
  - [`aif`](https://en.wikipedia.org/wiki/Audio_Interchange_File_Format) - Audio Interchange file
441
470
  - [`alias`](https://en.wikipedia.org/wiki/Alias_%28Mac_OS%29) - macOS Alias file
442
471
  - [`amr`](https://en.wikipedia.org/wiki/Adaptive_Multi-Rate_audio_codec) - Adaptive Multi-Rate audio codec
@@ -557,6 +586,8 @@ abortController.abort(); // Abort file-type reading from the Blob stream.
557
586
  - [`png`](https://en.wikipedia.org/wiki/Portable_Network_Graphics) - Portable Network Graphics
558
587
  - [`potm`](https://en.wikipedia.org/wiki/List_of_Microsoft_Office_filename_extensions) - Microsoft PowerPoint macro-enabled template
559
588
  - [`potx`](https://en.wikipedia.org/wiki/List_of_Microsoft_Office_filename_extensions) - Microsoft PowerPoint template
589
+ - [`ppsm`](https://en.wikipedia.org/wiki/List_of_Microsoft_Office_filename_extensions#PowerPoint) - Office PowerPoint 2007 macro-enabled slide show
590
+ - [`ppsx`](https://en.wikipedia.org/wiki/List_of_Microsoft_Office_filename_extensions#PowerPoint) - Office PowerPoint 2007 slide show
560
591
  - [`pptm`](https://en.wikipedia.org/wiki/List_of_Microsoft_Office_filename_extensions) - Microsoft PowerPoint macro-enabled document
561
592
  - [`pptx`](https://en.wikipedia.org/wiki/Office_Open_XML) - Microsoft PowerPoint document
562
593
  - [`ps`](https://en.wikipedia.org/wiki/Postscript) - Postscript
package/supported.js CHANGED
@@ -128,7 +128,6 @@ export const extensions = [
128
128
  'it',
129
129
  's3m',
130
130
  'xm',
131
- 'ai',
132
131
  'skp',
133
132
  'avif',
134
133
  'eps',
@@ -173,6 +172,8 @@ export const extensions = [
173
172
  'pptm',
174
173
  'jar',
175
174
  'rm',
175
+ 'ppsm',
176
+ 'ppsx',
176
177
  ];
177
178
 
178
179
  export const mimeTypes = [
@@ -191,12 +192,14 @@ export const mimeTypes = [
191
192
  'application/x-indesign',
192
193
  'application/epub+zip',
193
194
  'application/x-xpinstall',
195
+ 'application/vnd.ms-powerpoint.slideshow.macroenabled.12',
194
196
  'application/vnd.oasis.opendocument.text',
195
197
  'application/vnd.oasis.opendocument.spreadsheet',
196
198
  'application/vnd.oasis.opendocument.presentation',
197
199
  'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
198
200
  'application/vnd.openxmlformats-officedocument.presentationml.presentation',
199
201
  'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
202
+ 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
200
203
  'application/zip',
201
204
  'application/x-tar',
202
205
  'application/x-rar-compressed',
@@ -204,10 +207,10 @@ export const mimeTypes = [
204
207
  'application/x-bzip2',
205
208
  'application/x-7z-compressed',
206
209
  'application/x-apple-diskimage',
207
- 'application/x-apache-arrow',
210
+ 'application/vnd.apache.arrow.file',
208
211
  'video/mp4',
209
212
  'audio/midi',
210
- 'video/x-matroska',
213
+ 'video/matroska',
211
214
  'video/webm',
212
215
  'video/quicktime',
213
216
  'video/vnd.avi',
@@ -224,7 +227,7 @@ export const mimeTypes = [
224
227
  'audio/ogg',
225
228
  'audio/ogg; codecs=opus',
226
229
  'application/ogg',
227
- 'audio/x-flac',
230
+ 'audio/flac',
228
231
  'audio/ape',
229
232
  'audio/wavpack',
230
233
  'audio/amr',
@@ -317,7 +320,7 @@ export const mimeTypes = [
317
320
  'image/jls',
318
321
  'application/vnd.ms-outlook',
319
322
  'image/vnd.dwg',
320
- 'application/x-parquet',
323
+ 'application/vnd.apache.parquet',
321
324
  'application/java-vm',
322
325
  'application/x-arj',
323
326
  'application/x-cpio',
@@ -338,11 +341,11 @@ export const mimeTypes = [
338
341
  'application/vnd.oasis.opendocument.presentation-template',
339
342
  'application/vnd.oasis.opendocument.graphics',
340
343
  'application/vnd.oasis.opendocument.graphics-template',
341
- 'application/vnd.ms-excel.sheet.macroEnabled.12',
342
- 'application/vnd.ms-word.document.macroEnabled.12',
343
- 'application/vnd.ms-word.template.macroEnabled.12',
344
- 'application/vnd.ms-powerpoint.template.macroEnabled.12',
345
- 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
344
+ 'application/vnd.ms-excel.sheet.macroenabled.12',
345
+ 'application/vnd.ms-word.document.macroenabled.12',
346
+ 'application/vnd.ms-word.template.macroenabled.12',
347
+ 'application/vnd.ms-powerpoint.template.macroenabled.12',
348
+ 'application/vnd.ms-powerpoint.presentation.macroenabled.12',
346
349
  'application/java-archive',
347
350
  'application/vnd.rn-realmedia',
348
351
  ];