file-type 18.6.0 → 19.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
@@ -153,6 +153,7 @@ export type FileExtension =
153
153
  | 'ace'
154
154
  | 'avro'
155
155
  | 'icc'
156
+ | 'fbx'
156
157
  ; // eslint-disable-line semi-style
157
158
 
158
159
  export type MimeType =
@@ -191,7 +192,7 @@ export type MimeType =
191
192
  | 'video/webm'
192
193
  | 'video/quicktime'
193
194
  | 'video/vnd.avi'
194
- | 'audio/vnd.wave'
195
+ | 'audio/wav'
195
196
  | 'audio/qcelp'
196
197
  | 'audio/x-ms-asf'
197
198
  | 'video/x-ms-asf'
@@ -302,6 +303,7 @@ export type MimeType =
302
303
  | 'application/x-ace-compressed'
303
304
  | 'application/avro'
304
305
  | 'application/vnd.iccprofile'
306
+ | 'application/x.autodesk.fbx'
305
307
  ; // eslint-disable-line semi-style
306
308
 
307
309
  export type FileTypeResult = {
@@ -327,8 +329,8 @@ The file type is detected by checking the [magic number](https://en.wikipedia.or
327
329
 
328
330
  If file access is available, it is recommended to use `.fromFile()` instead.
329
331
 
330
- @param buffer - An Uint8Array or Buffer representing file data. It works best if the buffer contains the entire file, it may work with a smaller portion as well.
331
- @returns The detected file type and MIME type, or `undefined` when there is no match.
332
+ @param buffer - An Uint8Array or Buffer representing file data. It works best if the buffer contains the entire file. It may work with a smaller portion as well.
333
+ @returns The detected file type, or `undefined` when there is no match.
332
334
  */
333
335
  export function fileTypeFromBuffer(buffer: Uint8Array | ArrayBuffer): Promise<FileTypeResult | undefined>;
334
336
 
@@ -338,7 +340,7 @@ Detect the file type of a Node.js [readable stream](https://nodejs.org/api/strea
338
340
  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.
339
341
 
340
342
  @param stream - A readable stream representing file data.
341
- @returns The detected file type and MIME type, or `undefined` when there is no match.
343
+ @returns The detected file type, or `undefined` when there is no match.
342
344
  */
343
345
  export function fileTypeFromStream(stream: ReadableStream): Promise<FileTypeResult | undefined>;
344
346
 
@@ -350,7 +352,7 @@ This method is used internally, but can also be used for a special "tokenizer" r
350
352
  A tokenizer propagates the internal read functions, allowing alternative transport mechanisms, to access files, to be implemented and used.
351
353
 
352
354
  @param tokenizer - File source implementing the tokenizer interface.
353
- @returns The detected file type and MIME type, or `undefined` when there is no match.
355
+ @returns The detected file type, or `undefined` when there is no match.
354
356
 
355
357
  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.
356
358
 
@@ -421,19 +423,108 @@ if (stream2.fileType?.mime === 'image/jpeg') {
421
423
  export function fileTypeStream(readableStream: ReadableStream, options?: StreamOptions): Promise<ReadableStreamWithFileType>;
422
424
 
423
425
  /**
424
- Detect the file type of a [`Blob`](https://nodejs.org/api/buffer.html#class-blob).
426
+ 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).
427
+
428
+ @param blob - The [`Blob`](https://nodejs.org/api/buffer.html#class-blob) used for file detection.
429
+ @returns The detected file type, or `undefined` when there is no match.
425
430
 
426
431
  @example
427
432
  ```
428
433
  import {fileTypeFromBlob} from 'file-type';
429
434
 
430
435
  const blob = new Blob(['<?xml version="1.0" encoding="ISO-8859-1" ?>'], {
431
- type: 'plain/text',
436
+ type: 'text/plain',
432
437
  endings: 'native'
433
438
  });
434
439
 
435
440
  console.log(await fileTypeFromBlob(blob));
436
- //=> {ext: 'txt', mime: 'plain/text'}
441
+ //=> {ext: 'txt', mime: 'text/plain'}
437
442
  ```
438
443
  */
439
444
  export declare function fileTypeFromBlob(blob: Blob): Promise<FileTypeResult | undefined>;
445
+
446
+ /**
447
+ Function that allows specifying custom detection mechanisms.
448
+
449
+ An iterable of detectors can be provided via the `fileTypeOptions` argument for the {@link FileTypeParser.constructor}.
450
+
451
+ The detectors are called before the default detections in the provided order.
452
+
453
+ Custom detectors can be used to add new `FileTypeResults` or to modify return behavior of existing `FileTypeResult` detections.
454
+
455
+ If the detector returns `undefined`, there are 2 possible scenarios:
456
+
457
+ 1. The detector has not read from the tokenizer, it will be proceeded with the next available detector.
458
+ 2. The detector has read from the tokenizer (`tokenizer.position` has been increased).
459
+ In that case no further detectors will be executed and the final conclusion is that file-type returns undefined.
460
+ Note that this an exceptional scenario, as the detector takes the opportunity from any other detector to determine the file type.
461
+
462
+ Example detector array which can be extended and provided via the fileTypeOptions argument:
463
+
464
+ ```
465
+ import {FileTypeParser} from 'file-type';
466
+
467
+ const customDetectors = [
468
+ async tokenizer => {
469
+ const unicornHeader = [85, 78, 73, 67, 79, 82, 78]; // 'UNICORN' as decimal string
470
+
471
+ const buffer = Buffer.alloc(7);
472
+ await tokenizer.peekBuffer(buffer, {length: unicornHeader.length, mayBeLess: true});
473
+ if (unicornHeader.every((value, index) => value === buffer[index])) {
474
+ return {ext: 'unicorn', mime: 'application/unicorn'};
475
+ }
476
+
477
+ return undefined;
478
+ },
479
+ ];
480
+
481
+ const buffer = Buffer.from('UNICORN');
482
+ const parser = new FileTypeParser({customDetectors});
483
+ const fileType = await parser.fromBuffer(buffer);
484
+ console.log(fileType);
485
+ ```
486
+
487
+ @param tokenizer - The [tokenizer](https://github.com/Borewit/strtok3#tokenizer) used to read the file content from.
488
+ @param fileType - The file type detected by the standard detections or a previous custom detection, or `undefined`` if no matching file type could be found.
489
+ @returns The detected file type, or `undefined` when there is no match.
490
+ */
491
+ export type Detector = (tokenizer: ITokenizer, fileType?: FileTypeResult) => Promise<FileTypeResult | undefined>;
492
+
493
+ export type FileTypeOptions = {
494
+ customDetectors?: Iterable<Detector>;
495
+ };
496
+
497
+ export declare class TokenizerPositionError extends Error {
498
+ constructor(message?: string);
499
+ }
500
+
501
+ export declare class FileTypeParser {
502
+ detectors: Iterable<Detector>;
503
+
504
+ constructor(options?: {customDetectors?: Iterable<Detector>});
505
+
506
+ /**
507
+ Works the same way as {@link fileTypeFromBuffer}, additionally taking into account custom detectors (if any were provided to the constructor).
508
+ */
509
+ fromBuffer(buffer: Uint8Array | ArrayBuffer): Promise<FileTypeResult | undefined>;
510
+
511
+ /**
512
+ Works the same way as {@link fileTypeFromStream}, additionally taking into account custom detectors (if any were provided to the constructor).
513
+ */
514
+ fromStream(stream: ReadableStream): Promise<FileTypeResult | undefined>;
515
+
516
+ /**
517
+ Works the same way as {@link fileTypeFromTokenizer}, additionally taking into account custom detectors (if any were provided to the constructor).
518
+ */
519
+ fromTokenizer(tokenizer: ITokenizer): Promise<FileTypeResult | undefined>;
520
+
521
+ /**
522
+ Works the same way as {@link fileTypeFromBlob}, additionally taking into account custom detectors (if any were provided to the constructor).
523
+ */
524
+ fromBlob(blob: Blob): Promise<FileTypeResult | undefined>;
525
+
526
+ /**
527
+ Works the same way as {@link fileTypeStream}, additionally taking into account custom detectors (if any were provided to the constructor).
528
+ */
529
+ toDetectionStream(readableStream: ReadableStream, options?: StreamOptions): Promise<FileTypeResult | undefined>;
530
+ }
package/core.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {Buffer} from 'node:buffer';
2
2
  import * as Token from 'token-types';
3
- import * as strtok3 from 'strtok3/core'; // eslint-disable-line n/file-extension-in-import
3
+ import * as strtok3 from 'strtok3/core';
4
4
  import {
5
5
  stringToBytes,
6
6
  tarHeaderChecksumMatches,
@@ -11,31 +11,15 @@ import {extensions, mimeTypes} from './supported.js';
11
11
  const minimumBytes = 4100; // A fair amount of file-types are detectable within this range.
12
12
 
13
13
  export async function fileTypeFromStream(stream) {
14
- const tokenizer = await strtok3.fromStream(stream);
15
- try {
16
- return await fileTypeFromTokenizer(tokenizer);
17
- } finally {
18
- await tokenizer.close();
19
- }
14
+ return new FileTypeParser().fromStream(stream);
20
15
  }
21
16
 
22
17
  export async function fileTypeFromBuffer(input) {
23
- if (!(input instanceof Uint8Array || input instanceof ArrayBuffer)) {
24
- throw new TypeError(`Expected the \`input\` argument to be of type \`Uint8Array\` or \`Buffer\` or \`ArrayBuffer\`, got \`${typeof input}\``);
25
- }
26
-
27
- const buffer = input instanceof Uint8Array ? input : new Uint8Array(input);
28
-
29
- if (!(buffer?.length > 1)) {
30
- return;
31
- }
32
-
33
- return fileTypeFromTokenizer(strtok3.fromBuffer(buffer));
18
+ return new FileTypeParser().fromBuffer(input);
34
19
  }
35
20
 
36
21
  export async function fileTypeFromBlob(blob) {
37
- const buffer = await blob.arrayBuffer();
38
- return fileTypeFromBuffer(new Uint8Array(buffer));
22
+ return new FileTypeParser().fromBlob(blob);
39
23
  }
40
24
 
41
25
  function _check(buffer, headers, options) {
@@ -60,16 +44,98 @@ function _check(buffer, headers, options) {
60
44
  }
61
45
 
62
46
  export async function fileTypeFromTokenizer(tokenizer) {
63
- try {
64
- return new FileTypeParser().parse(tokenizer);
65
- } catch (error) {
66
- if (!(error instanceof strtok3.EndOfStreamError)) {
67
- throw error;
47
+ return new FileTypeParser().fromTokenizer(tokenizer);
48
+ }
49
+
50
+ export class FileTypeParser {
51
+ constructor(options) {
52
+ this.detectors = options?.customDetectors;
53
+
54
+ this.fromTokenizer = this.fromTokenizer.bind(this);
55
+ this.fromBuffer = this.fromBuffer.bind(this);
56
+ this.parse = this.parse.bind(this);
57
+ }
58
+
59
+ async fromTokenizer(tokenizer) {
60
+ const initialPosition = tokenizer.position;
61
+
62
+ for (const detector of this.detectors || []) {
63
+ const fileType = await detector(tokenizer);
64
+ if (fileType) {
65
+ return fileType;
66
+ }
67
+
68
+ if (initialPosition !== tokenizer.position) {
69
+ return undefined; // Cannot proceed scanning of the tokenizer is at an arbitrary position
70
+ }
68
71
  }
72
+
73
+ return this.parse(tokenizer);
74
+ }
75
+
76
+ async fromBuffer(input) {
77
+ if (!(input instanceof Uint8Array || input instanceof ArrayBuffer)) {
78
+ throw new TypeError(`Expected the \`input\` argument to be of type \`Uint8Array\` or \`Buffer\` or \`ArrayBuffer\`, got \`${typeof input}\``);
79
+ }
80
+
81
+ const buffer = input instanceof Uint8Array ? input : new Uint8Array(input);
82
+
83
+ if (!(buffer?.length > 1)) {
84
+ return;
85
+ }
86
+
87
+ return this.fromTokenizer(strtok3.fromBuffer(buffer));
88
+ }
89
+
90
+ async fromBlob(blob) {
91
+ const buffer = await blob.arrayBuffer();
92
+ return this.fromBuffer(new Uint8Array(buffer));
93
+ }
94
+
95
+ async fromStream(stream) {
96
+ const tokenizer = await strtok3.fromStream(stream);
97
+ try {
98
+ return await this.fromTokenizer(tokenizer);
99
+ } finally {
100
+ await tokenizer.close();
101
+ }
102
+ }
103
+
104
+ async toDetectionStream(readableStream, options = {}) {
105
+ const {default: stream} = await import('node:stream');
106
+ const {sampleSize = minimumBytes} = options;
107
+
108
+ return new Promise((resolve, reject) => {
109
+ readableStream.on('error', reject);
110
+
111
+ readableStream.once('readable', () => {
112
+ (async () => {
113
+ try {
114
+ // Set up output stream
115
+ const pass = new stream.PassThrough();
116
+ const outputStream = stream.pipeline ? stream.pipeline(readableStream, pass, () => {}) : readableStream.pipe(pass);
117
+
118
+ // Read the input stream and detect the filetype
119
+ const chunk = readableStream.read(sampleSize) ?? readableStream.read() ?? Buffer.alloc(0);
120
+ try {
121
+ pass.fileType = await this.fromBuffer(chunk);
122
+ } catch (error) {
123
+ if (error instanceof strtok3.EndOfStreamError) {
124
+ pass.fileType = undefined;
125
+ } else {
126
+ reject(error);
127
+ }
128
+ }
129
+
130
+ resolve(outputStream);
131
+ } catch (error) {
132
+ reject(error);
133
+ }
134
+ })();
135
+ });
136
+ });
69
137
  }
70
- }
71
138
 
72
- class FileTypeParser {
73
139
  check(header, options) {
74
140
  return _check(this.buffer, header, options);
75
141
  }
@@ -211,7 +277,7 @@ class FileTypeParser {
211
277
  }
212
278
 
213
279
  await tokenizer.ignore(id3HeaderLength);
214
- return fileTypeFromTokenizer(tokenizer); // Skip ID3 header, recursion
280
+ return this.fromTokenizer(tokenizer); // Skip ID3 header, recursion
215
281
  }
216
282
 
217
283
  // Musepack, SV7
@@ -728,7 +794,7 @@ class FileTypeParser {
728
794
  const element = await readElement();
729
795
  if (element.id === 0x42_82) {
730
796
  const rawValue = await tokenizer.readToken(new Token.StringType(element.len, 'utf-8'));
731
- return rawValue.replace(/\00.*$/g, ''); // Return DocType
797
+ return rawValue.replaceAll(/\00.*$/g, ''); // Return DocType
732
798
  }
733
799
 
734
800
  await tokenizer.ignore(element.len); // ignore payload
@@ -769,7 +835,7 @@ class FileTypeParser {
769
835
  if (this.check([0x57, 0x41, 0x56, 0x45], {offset: 8})) {
770
836
  return {
771
837
  ext: 'wav',
772
- mime: 'audio/vnd.wave',
838
+ mime: 'audio/wav',
773
839
  };
774
840
  }
775
841
 
@@ -1440,6 +1506,13 @@ class FileTypeParser {
1440
1506
  };
1441
1507
  }
1442
1508
 
1509
+ if (this.checkString('Kaydara FBX Binary \u0000')) {
1510
+ return {
1511
+ ext: 'fbx',
1512
+ mime: 'application/x.autodesk.fbx', // Invented by us
1513
+ };
1514
+ }
1515
+
1443
1516
  if (
1444
1517
  this.check([0x4C, 0x50], {offset: 34})
1445
1518
  && (
@@ -1609,39 +1682,8 @@ class FileTypeParser {
1609
1682
  }
1610
1683
  }
1611
1684
 
1612
- export async function fileTypeStream(readableStream, {sampleSize = minimumBytes} = {}) {
1613
- const {default: stream} = await import('node:stream');
1614
-
1615
- return new Promise((resolve, reject) => {
1616
- readableStream.on('error', reject);
1617
-
1618
- readableStream.once('readable', () => {
1619
- (async () => {
1620
- try {
1621
- // Set up output stream
1622
- const pass = new stream.PassThrough();
1623
- const outputStream = stream.pipeline ? stream.pipeline(readableStream, pass, () => {}) : readableStream.pipe(pass);
1624
-
1625
- // Read the input stream and detect the filetype
1626
- const chunk = readableStream.read(sampleSize) ?? readableStream.read() ?? Buffer.alloc(0);
1627
- try {
1628
- const fileType = await fileTypeFromBuffer(chunk);
1629
- pass.fileType = fileType;
1630
- } catch (error) {
1631
- if (error instanceof strtok3.EndOfStreamError) {
1632
- pass.fileType = undefined;
1633
- } else {
1634
- reject(error);
1635
- }
1636
- }
1637
-
1638
- resolve(outputStream);
1639
- } catch (error) {
1640
- reject(error);
1641
- }
1642
- })();
1643
- });
1644
- });
1685
+ export async function fileTypeStream(readableStream, options = {}) {
1686
+ return new FileTypeParser().toDetectionStream(readableStream, options);
1645
1687
  }
1646
1688
 
1647
1689
  export const supportedExtensions = new Set(extensions);
package/index.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import * as strtok3 from 'strtok3';
2
- import {fileTypeFromTokenizer} from './core.js';
2
+ import {FileTypeParser} from './core.js';
3
3
 
4
- export async function fileTypeFromFile(path) {
4
+ export async function fileTypeFromFile(path, fileTypeOptions) {
5
5
  const tokenizer = await strtok3.fromFile(path);
6
6
  try {
7
- return await fileTypeFromTokenizer(tokenizer);
7
+ const parser = new FileTypeParser(fileTypeOptions);
8
+ return await parser.fromTokenizer(tokenizer);
8
9
  } finally {
9
10
  await tokenizer.close();
10
11
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "file-type",
3
- "version": "18.6.0",
3
+ "version": "19.0.0",
4
4
  "description": "Detect the file type of a Buffer/Uint8Array/ArrayBuffer",
5
5
  "license": "MIT",
6
6
  "repository": "sindresorhus/file-type",
@@ -18,8 +18,9 @@
18
18
  },
19
19
  "./core": "./core.js"
20
20
  },
21
+ "sideEffects": false,
21
22
  "engines": {
22
- "node": ">=14.16"
23
+ "node": ">=18"
23
24
  },
24
25
  "scripts": {
25
26
  "test": "xo && ava && tsd"
@@ -205,7 +206,8 @@
205
206
  "cpio",
206
207
  "ace",
207
208
  "avro",
208
- "icc"
209
+ "icc",
210
+ "fbx"
209
211
  ],
210
212
  "dependencies": {
211
213
  "readable-web-to-node-stream": "^3.0.2",
@@ -214,18 +216,21 @@
214
216
  },
215
217
  "devDependencies": {
216
218
  "@tokenizer/token": "^0.3.0",
217
- "@types/node": "^20.1.2",
218
- "ava": "^5.2.0",
219
+ "@types/node": "^20.10.7",
220
+ "ava": "^6.0.1",
219
221
  "commonmark": "^0.30.0",
220
222
  "noop-stream": "^1.0.0",
221
- "tsd": "^0.28.1",
222
- "xo": "^0.54.2"
223
+ "tsd": "^0.30.3",
224
+ "xo": "^0.56.0"
223
225
  },
224
226
  "xo": {
225
227
  "envs": [
226
228
  "node",
227
229
  "browser"
228
230
  ],
231
+ "ignores": [
232
+ "fixture"
233
+ ],
229
234
  "rules": {
230
235
  "no-inner-declarations": "warn",
231
236
  "no-await-in-loop": "warn",
package/readme.md CHANGED
@@ -113,7 +113,7 @@ The file type is detected by checking the [magic number](https://en.wikipedia.or
113
113
 
114
114
  If file access is available, it is recommended to use `fileTypeFromFile()` instead.
115
115
 
116
- Returns a `Promise` for an object with the detected file type and MIME type:
116
+ Returns a `Promise` for an object with the detected file type:
117
117
 
118
118
  - `ext` - One of the [supported file types](#supported-file-types)
119
119
  - `mime` - The [MIME type](https://en.wikipedia.org/wiki/Internet_media_type)
@@ -124,7 +124,7 @@ Or `undefined` when there is no match.
124
124
 
125
125
  Type: `Buffer | Uint8Array | ArrayBuffer`
126
126
 
127
- A buffer representing file data. It works best if the buffer contains the entire file, it may work with a smaller portion as well.
127
+ A buffer representing file data. It works best if the buffer contains the entire file. It may work with a smaller portion as well.
128
128
 
129
129
  ### fileTypeFromFile(filePath)
130
130
 
@@ -132,7 +132,7 @@ Detect the file type of a file path.
132
132
 
133
133
  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.
134
134
 
135
- Returns a `Promise` for an object with the detected file type and MIME type:
135
+ Returns a `Promise` for an object with the detected file type:
136
136
 
137
137
  - `ext` - One of the [supported file types](#supported-file-types)
138
138
  - `mime` - The [MIME type](https://en.wikipedia.org/wiki/Internet_media_type)
@@ -151,7 +151,7 @@ Detect the file type of a Node.js [readable stream](https://nodejs.org/api/strea
151
151
 
152
152
  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.
153
153
 
154
- Returns a `Promise` for an object with the detected file type and MIME type:
154
+ Returns a `Promise` for an object with the detected file type:
155
155
 
156
156
  - `ext` - One of the [supported file types](#supported-file-types)
157
157
  - `mime` - The [MIME type](https://en.wikipedia.org/wiki/Internet_media_type)
@@ -170,7 +170,7 @@ Detect the file type of a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/
170
170
 
171
171
  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.
172
172
 
173
- Returns a `Promise` for an object with the detected file type and MIME type:
173
+ Returns a `Promise` for an object with the detected file type:
174
174
 
175
175
  - `ext` - One of the [supported file types](#supported-file-types)
176
176
  - `mime` - The [MIME type](https://en.wikipedia.org/wiki/Internet_media_type)
@@ -181,14 +181,18 @@ Or `undefined` when there is no match.
181
181
  import {fileTypeFromBlob} from 'file-type';
182
182
 
183
183
  const blob = new Blob(['<?xml version="1.0" encoding="ISO-8859-1" ?>'], {
184
- type: 'plain/text',
184
+ type: 'text/plain',
185
185
  endings: 'native'
186
186
  });
187
187
 
188
188
  console.log(await fileTypeFromBlob(blob));
189
- //=> {ext: 'txt', mime: 'plain/text'}
189
+ //=> {ext: 'txt', mime: 'text/plain'}
190
190
  ```
191
191
 
192
+ #### blob
193
+
194
+ Type: [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob)
195
+
192
196
  ### fileTypeFromTokenizer(tokenizer)
193
197
 
194
198
  Detect the file type from an `ITokenizer` source.
@@ -197,7 +201,7 @@ This method is used internally, but can also be used for a special "tokenizer" r
197
201
 
198
202
  A tokenizer propagates the internal read functions, allowing alternative transport mechanisms, to access files, to be implemented and used.
199
203
 
200
- Returns a `Promise` for an object with the detected file type and MIME type:
204
+ Returns a `Promise` for an object with the detected file type:
201
205
 
202
206
  - `ext` - One of the [supported file types](#supported-file-types)
203
207
  - `mime` - The [MIME type](https://en.wikipedia.org/wiki/Internet_media_type)
@@ -305,6 +309,49 @@ Returns a `Set<string>` of supported file extensions.
305
309
 
306
310
  Returns a `Set<string>` of supported MIME types.
307
311
 
312
+ ## Custom detectors
313
+
314
+ A custom detector is a function that allows specifying custom detection mechanisms.
315
+
316
+ An iterable of detectors can be provided via the `fileTypeOptions` argument for the `FileTypeParser` constructor.
317
+
318
+ The detectors are called before the default detections in the provided order.
319
+
320
+ Custom detectors can be used to add new `FileTypeResults` or to modify return behaviour of existing `FileTypeResult` detections.
321
+
322
+ If the detector returns `undefined`, there are 2 possible scenarios:
323
+
324
+ 1. The detector has not read from the tokenizer, it will be proceeded with the next available detector.
325
+ 2. The detector has read from the tokenizer (`tokenizer.position` has been increased).
326
+ In that case no further detectors will be executed and the final conclusion is that file-type returns undefined.
327
+ Note that this an exceptional scenario, as the detector takes the opportunity from any other detector to determine the file type.
328
+
329
+ Example detector array which can be extended and provided to each public method via the `fileTypeOptions` argument:
330
+
331
+ ```js
332
+ import {FileTypeParser} from 'file-type';
333
+
334
+ const customDetectors = [
335
+ async tokenizer => {
336
+ const unicornHeader = [85, 78, 73, 67, 79, 82, 78]; // 'UNICORN' as decimal string
337
+
338
+ const buffer = Buffer.alloc(7);
339
+ await tokenizer.peekBuffer(buffer, {length: unicornHeader.length, mayBeLess: true});
340
+
341
+ if (unicornHeader.every((value, index) => value === buffer[index])) {
342
+ return {ext: 'unicorn', mime: 'application/unicorn'};
343
+ }
344
+
345
+ return undefined;
346
+ },
347
+ ];
348
+
349
+ const buffer = Buffer.from('UNICORN');
350
+ const parser = new FileTypeParser({customDetectors});
351
+ const fileType = await parser.fromBuffer(buffer);
352
+ console.log(fileType);
353
+ ```
354
+
308
355
  ## Supported file types
309
356
 
310
357
  - [`3g2`](https://en.wikipedia.org/wiki/3GP_and_3G2#3G2) - Multimedia container format defined by the 3GPP2 for 3G CDMA2000 multimedia services
@@ -335,7 +382,7 @@ Returns a `Set<string>` of supported MIME types.
335
382
  - [`bpg`](https://bellard.org/bpg/) - Better Portable Graphics file
336
383
  - [`bz2`](https://en.wikipedia.org/wiki/Bzip2) - Archive file
337
384
  - [`cab`](https://en.wikipedia.org/wiki/Cabinet_(file_format)) - Cabinet file
338
- - [`cfb`](https://en.wikipedia.org/wiki/Compound_File_Binary_Format) - Compount File Binary Format
385
+ - [`cfb`](https://en.wikipedia.org/wiki/Compound_File_Binary_Format) - Compound File Binary Format
339
386
  - [`chm`](https://en.wikipedia.org/wiki/Microsoft_Compiled_HTML_Help) - Microsoft Compiled HTML Help
340
387
  - [`class`](https://en.wikipedia.org/wiki/Java_class_file) - Java class file
341
388
  - [`cpio`](https://en.wikipedia.org/wiki/Cpio) - Cpio archive
@@ -347,7 +394,7 @@ Returns a `Set<string>` of supported MIME types.
347
394
  - [`deb`](https://en.wikipedia.org/wiki/Deb_(file_format)) - Debian package
348
395
  - [`dmg`](https://en.wikipedia.org/wiki/Apple_Disk_Image) - Apple Disk Image
349
396
  - [`dng`](https://en.wikipedia.org/wiki/Digital_Negative) - Adobe Digital Negative image file
350
- - [`docx`](https://en.wikipedia.org/wiki/Office_Open_XML) - Microsoft Word
397
+ - [`docx`](https://en.wikipedia.org/wiki/Office_Open_XML) - Microsoft Word document
351
398
  - [`dsf`](https://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf) - Sony DSD Stream File (DSF)
352
399
  - [`dwg`](https://en.wikipedia.org/wiki/.dwg) - Autodesk CAD file
353
400
  - [`elf`](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format) - Unix Executable and Linkable Format
@@ -359,6 +406,7 @@ Returns a `Set<string>` of supported MIME types.
359
406
  - [`f4b`](https://en.wikipedia.org/wiki/Flash_Video) - Audiobook and podcast ISO base media file format used by Adobe Flash Player
360
407
  - [`f4p`](https://en.wikipedia.org/wiki/Flash_Video) - ISO base media file format protected by Adobe Access DRM used by Adobe Flash Player
361
408
  - [`f4v`](https://en.wikipedia.org/wiki/Flash_Video) - ISO base media file format used by Adobe Flash Player
409
+ - [`fbx`](https://en.wikipedia.org/wiki/FBX) - Filmbox is a proprietary file format used to provide interoperability between digital content creation apps.
362
410
  - [`flac`](https://en.wikipedia.org/wiki/FLAC) - Free Lossless Audio Codec
363
411
  - [`flif`](https://en.wikipedia.org/wiki/Free_Lossless_Image_Format) - Free Lossless Image Format
364
412
  - [`flv`](https://en.wikipedia.org/wiki/Flash_Video) - Flash video
@@ -382,7 +430,7 @@ Returns a `Set<string>` of supported MIME types.
382
430
  - [`jxr`](https://en.wikipedia.org/wiki/JPEG_XR) - Joint Photographic Experts Group extended range
383
431
  - [`ktx`](https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/) - OpenGL and OpenGL ES textures
384
432
  - [`lnk`](https://en.wikipedia.org/wiki/Shortcut_%28computing%29#Microsoft_Windows) - Microsoft Windows file shortcut
385
- - [`lz`](https://en.wikipedia.org/wiki/Lzip) - Arhive file
433
+ - [`lz`](https://en.wikipedia.org/wiki/Lzip) - Archive file
386
434
  - [`lzh`](https://en.wikipedia.org/wiki/LHA_(file_format)) - LZH archive
387
435
  - [`m4a`](https://en.wikipedia.org/wiki/M4A) - Audio-only MPEG-4 files
388
436
  - [`m4b`](https://en.wikipedia.org/wiki/M4B) - Audiobook and podcast MPEG-4 files, which also contain metadata including chapter markers, images, and hyperlinks
@@ -421,7 +469,7 @@ Returns a `Set<string>` of supported MIME types.
421
469
  - [`pdf`](https://en.wikipedia.org/wiki/Portable_Document_Format) - Portable Document Format
422
470
  - [`pgp`](https://en.wikipedia.org/wiki/Pretty_Good_Privacy) - Pretty Good Privacy
423
471
  - [`png`](https://en.wikipedia.org/wiki/Portable_Network_Graphics) - Portable Network Graphics
424
- - [`pptx`](https://en.wikipedia.org/wiki/Office_Open_XML) - Microsoft Powerpoint
472
+ - [`pptx`](https://en.wikipedia.org/wiki/Office_Open_XML) - Microsoft Powerpoint document
425
473
  - [`ps`](https://en.wikipedia.org/wiki/Postscript) - Postscript
426
474
  - [`psd`](https://en.wikipedia.org/wiki/Adobe_Photoshop#File_format) - Adobe Photoshop document
427
475
  - [`pst`](https://en.wikipedia.org/wiki/Personal_Storage_Table) - Personal Storage Table file
@@ -451,7 +499,7 @@ Returns a `Set<string>` of supported MIME types.
451
499
  - [`woff2`](https://en.wikipedia.org/wiki/Web_Open_Font_Format) - Web Open Font Format
452
500
  - [`wv`](https://en.wikipedia.org/wiki/WavPack) - WavPack
453
501
  - [`xcf`](https://en.wikipedia.org/wiki/XCF_(file_format)) - eXperimental Computing Facility
454
- - [`xlsx`](https://en.wikipedia.org/wiki/Office_Open_XML) - Microsoft Excel
502
+ - [`xlsx`](https://en.wikipedia.org/wiki/Office_Open_XML) - Microsoft Excel document
455
503
  - [`xm`](https://wiki.openmpt.org/Manual:_Module_formats#The_FastTracker_2_format_.28.xm.29) - Audio module format: FastTracker 2
456
504
  - [`xml`](https://en.wikipedia.org/wiki/XML) - eXtensible Markup Language
457
505
  - [`xpi`](https://en.wikipedia.org/wiki/XPInstall) - XPInstall file
@@ -459,7 +507,7 @@ Returns a `Set<string>` of supported MIME types.
459
507
  - [`zip`](https://en.wikipedia.org/wiki/Zip_(file_format)) - Archive file
460
508
  - [`zst`](https://en.wikipedia.org/wiki/Zstandard) - Archive file
461
509
 
462
- *Pull requests are welcome for additional commonly used file types.*
510
+ *[Pull requests](.github/pull_request_template.md) are welcome for additional commonly used file types.*
463
511
 
464
512
  The following file types will not be accepted:
465
513
  - [MS-CFB: Microsoft Compound File Binary File Format based formats](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-cfb/53989ce4-7b05-4f8d-829b-d08d6148375b), too old and difficult to parse:
@@ -470,16 +518,26 @@ The following file types will not be accepted:
470
518
  - `.csv` - [Reason.](https://github.com/sindresorhus/file-type/issues/264#issuecomment-568439196)
471
519
  - `.svg` - Detecting it requires a full-blown parser. Check out [`is-svg`](https://github.com/sindresorhus/is-svg) for something that mostly works.
472
520
 
521
+ #### tokenizer
522
+
523
+ Type: [`ITokenizer`](https://github.com/Borewit/strtok3#tokenizer)
524
+
525
+ Usable as source of the examined file.
526
+
527
+ #### fileType
528
+
529
+ Type: `FileTypeResult`
530
+
531
+ An object having an `ext` (extension) and `mime` (mime type) property.
532
+
533
+ Detected by the standard detections or a previous custom detection. Undefined if no matching fileTypeResult could be found.
534
+
473
535
  ## Related
474
536
 
475
537
  - [file-type-cli](https://github.com/sindresorhus/file-type-cli) - CLI for this module
538
+ - [image-dimensions](https://github.com/sindresorhus/image-dimensions) - Get the dimensions of an image
476
539
 
477
540
  ## Maintainers
478
541
 
479
542
  - [Sindre Sorhus](https://github.com/sindresorhus)
480
543
  - [Borewit](https://github.com/Borewit)
481
-
482
- **Former**
483
-
484
- - [Mikael Finstad](https://github.com/mifi)
485
- - [Ben Brook](https://github.com/bencmbrook)
package/supported.js CHANGED
@@ -150,6 +150,7 @@ export const extensions = [
150
150
  'ace',
151
151
  'avro',
152
152
  'icc',
153
+ 'fbx',
153
154
  ];
154
155
 
155
156
  export const mimeTypes = [
@@ -188,7 +189,7 @@ export const mimeTypes = [
188
189
  'video/webm',
189
190
  'video/quicktime',
190
191
  'video/vnd.avi',
191
- 'audio/vnd.wave',
192
+ 'audio/wav',
192
193
  'audio/qcelp',
193
194
  'audio/x-ms-asf',
194
195
  'video/x-ms-asf',
@@ -299,4 +300,5 @@ export const mimeTypes = [
299
300
  'application/x-ace-compressed',
300
301
  'application/avro',
301
302
  'application/vnd.iccprofile',
303
+ 'application/x.autodesk.fbx', // Invented by us
302
304
  ];