file-type 19.0.0 → 19.1.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
@@ -1,6 +1,16 @@
1
- import type {Readable as ReadableStream} from 'node:stream';
1
+ /**
2
+ Typings for primary entry point, Node.js specific typings can be found in index.d.ts
3
+ */
4
+
5
+ import type {ReadableStream as WebReadableStream} from 'node:stream/web';
2
6
  import type {ITokenizer} from 'strtok3';
3
7
 
8
+ /**
9
+ Either the Node.js ReadableStream or the `lib.dom.d.ts` ReadableStream.
10
+ Related issue: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/60377
11
+ */
12
+ export type AnyWebReadableStream<G> = WebReadableStream<G> | ReadableStream<G>;
13
+
4
14
  export type FileExtension =
5
15
  | 'jpg'
6
16
  | 'png'
@@ -318,18 +328,14 @@ export type FileTypeResult = {
318
328
  readonly mime: MimeType;
319
329
  };
320
330
 
321
- export type ReadableStreamWithFileType = ReadableStream & {
322
- readonly fileType?: FileTypeResult;
323
- };
324
-
325
331
  /**
326
- Detect the file type of a `Buffer`, `Uint8Array`, or `ArrayBuffer`.
332
+ Detect the file type of a `Uint8Array`, or `ArrayBuffer`.
327
333
 
328
334
  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.
329
335
 
330
336
  If file access is available, it is recommended to use `.fromFile()` instead.
331
337
 
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.
338
+ @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.
333
339
  @returns The detected file type, or `undefined` when there is no match.
334
340
  */
335
341
  export function fileTypeFromBuffer(buffer: Uint8Array | ArrayBuffer): Promise<FileTypeResult | undefined>;
@@ -339,10 +345,10 @@ Detect the file type of a Node.js [readable stream](https://nodejs.org/api/strea
339
345
 
340
346
  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.
341
347
 
342
- @param stream - A readable stream representing file data.
348
+ @param stream - A Node.js Readable stream or Web API Readable Stream representing file data. The Web Readable stream **must be a byte stream**.
343
349
  @returns The detected file type, or `undefined` when there is no match.
344
350
  */
345
- export function fileTypeFromStream(stream: ReadableStream): Promise<FileTypeResult | undefined>;
351
+ export function fileTypeFromStream(stream: AnyWebReadableStream<Uint8Array>): Promise<FileTypeResult | undefined>;
346
352
 
347
353
  /**
348
354
  Detect the file type from an [`ITokenizer`](https://github.com/Borewit/strtok3#tokenizer) source.
@@ -391,37 +397,6 @@ export type StreamOptions = {
391
397
  readonly sampleSize?: number;
392
398
  };
393
399
 
394
- /**
395
- 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()`.
396
-
397
- This method can be handy to put in between a stream, but it comes with a price.
398
- Internally `stream()` builds up a buffer of `sampleSize` bytes, used as a sample, to determine the file type.
399
- The sample size impacts the file detection resolution.
400
- A smaller sample size will result in lower probability of the best file type detection.
401
-
402
- **Note:** This method is only available when using Node.js.
403
- **Note:** Requires Node.js 14 or later.
404
-
405
- @param readableStream - A [readable stream](https://nodejs.org/api/stream.html#stream_class_stream_readable) containing a file to examine.
406
- @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()`.
407
-
408
- @example
409
- ```
410
- import got from 'got';
411
- import {fileTypeStream} from 'file-type';
412
-
413
- const url = 'https://upload.wikimedia.org/wikipedia/en/a/a9/Example.jpg';
414
-
415
- const stream1 = got.stream(url);
416
- const stream2 = await fileTypeStream(stream1, {sampleSize: 1024});
417
-
418
- if (stream2.fileType?.mime === 'image/jpeg') {
419
- // stream2 can be used to stream the JPEG image (from the very beginning of the stream)
420
- }
421
- ```
422
- */
423
- export function fileTypeStream(readableStream: ReadableStream, options?: StreamOptions): Promise<ReadableStreamWithFileType>;
424
-
425
400
  /**
426
401
  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
402
 
@@ -508,11 +483,6 @@ export declare class FileTypeParser {
508
483
  */
509
484
  fromBuffer(buffer: Uint8Array | ArrayBuffer): Promise<FileTypeResult | undefined>;
510
485
 
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
486
  /**
517
487
  Works the same way as {@link fileTypeFromTokenizer}, additionally taking into account custom detectors (if any were provided to the constructor).
518
488
  */
@@ -522,9 +492,4 @@ export declare class FileTypeParser {
522
492
  Works the same way as {@link fileTypeFromBlob}, additionally taking into account custom detectors (if any were provided to the constructor).
523
493
  */
524
494
  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
495
  }
package/core.js CHANGED
@@ -1,6 +1,10 @@
1
- import {Buffer} from 'node:buffer';
1
+ /**
2
+ Primary entry point, Node.js specific entry point is index.js
3
+ */
4
+
2
5
  import * as Token from 'token-types';
3
6
  import * as strtok3 from 'strtok3/core';
7
+ import {includes, indexOf, getUintBE} from 'uint8array-extras';
4
8
  import {
5
9
  stringToBytes,
6
10
  tarHeaderChecksumMatches,
@@ -8,7 +12,7 @@ import {
8
12
  } from './util.js';
9
13
  import {extensions, mimeTypes} from './supported.js';
10
14
 
11
- const minimumBytes = 4100; // A fair amount of file-types are detectable within this range.
15
+ export const reasonableDetectionSizeInBytes = 4100; // A fair amount of file-types are detectable within this range.
12
16
 
13
17
  export async function fileTypeFromStream(stream) {
14
18
  return new FileTypeParser().fromStream(stream);
@@ -75,7 +79,7 @@ export class FileTypeParser {
75
79
 
76
80
  async fromBuffer(input) {
77
81
  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}\``);
82
+ throw new TypeError(`Expected the \`input\` argument to be of type \`Uint8Array\` or \`ArrayBuffer\`, got \`${typeof input}\``);
79
83
  }
80
84
 
81
85
  const buffer = input instanceof Uint8Array ? input : new Uint8Array(input);
@@ -88,12 +92,11 @@ export class FileTypeParser {
88
92
  }
89
93
 
90
94
  async fromBlob(blob) {
91
- const buffer = await blob.arrayBuffer();
92
- return this.fromBuffer(new Uint8Array(buffer));
95
+ return this.fromStream(blob.stream());
93
96
  }
94
97
 
95
98
  async fromStream(stream) {
96
- const tokenizer = await strtok3.fromStream(stream);
99
+ const tokenizer = await strtok3.fromWebStream(stream);
97
100
  try {
98
101
  return await this.fromTokenizer(tokenizer);
99
102
  } finally {
@@ -101,41 +104,6 @@ export class FileTypeParser {
101
104
  }
102
105
  }
103
106
 
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
- });
137
- }
138
-
139
107
  check(header, options) {
140
108
  return _check(this.buffer, header, options);
141
109
  }
@@ -145,7 +113,7 @@ export class FileTypeParser {
145
113
  }
146
114
 
147
115
  async parse(tokenizer) {
148
- this.buffer = Buffer.alloc(minimumBytes);
116
+ this.buffer = new Uint8Array(reasonableDetectionSizeInBytes);
149
117
 
150
118
  // Keep reading until EOF if the file size is unknown.
151
119
  if (tokenizer.fileInfo.size === undefined) {
@@ -372,12 +340,14 @@ export class FileTypeParser {
372
340
  while (tokenizer.position + 30 < tokenizer.fileInfo.size) {
373
341
  await tokenizer.readBuffer(this.buffer, {length: 30});
374
342
 
343
+ const view = new DataView(this.buffer.buffer);
344
+
375
345
  // https://en.wikipedia.org/wiki/Zip_(file_format)#File_headers
376
346
  const zipHeader = {
377
- compressedSize: this.buffer.readUInt32LE(18),
378
- uncompressedSize: this.buffer.readUInt32LE(22),
379
- filenameLength: this.buffer.readUInt16LE(26),
380
- extraFieldLength: this.buffer.readUInt16LE(28),
347
+ compressedSize: view.getUint32(18, true),
348
+ uncompressedSize: view.getUint32(22, true),
349
+ filenameLength: view.getUint16(26, true),
350
+ extraFieldLength: view.getUint16(28, true),
381
351
  };
382
352
 
383
353
  zipHeader.filename = await tokenizer.readToken(new Token.StringType(zipHeader.filenameLength, 'utf-8'));
@@ -472,7 +442,8 @@ export class FileTypeParser {
472
442
  while (nextHeaderIndex < 0 && (tokenizer.position < tokenizer.fileInfo.size)) {
473
443
  await tokenizer.peekBuffer(this.buffer, {mayBeLess: true});
474
444
 
475
- nextHeaderIndex = this.buffer.indexOf('504B0304', 0, 'hex');
445
+ nextHeaderIndex = indexOf(this.buffer, new Uint8Array([0x50, 0x4B, 0x03, 0x04]));
446
+
476
447
  // Move position to the next header if found, skip the whole buffer otherwise
477
448
  await tokenizer.ignore(nextHeaderIndex >= 0 ? nextHeaderIndex : this.buffer.length);
478
449
  }
@@ -495,7 +466,7 @@ export class FileTypeParser {
495
466
  if (this.checkString('OggS')) {
496
467
  // This is an OGG container
497
468
  await tokenizer.ignore(28);
498
- const type = Buffer.alloc(8);
469
+ const type = new Uint8Array(8);
499
470
  await tokenizer.readBuffer(type);
500
471
 
501
472
  // Needs to be before `ogg` check
@@ -576,7 +547,7 @@ export class FileTypeParser {
576
547
  ) {
577
548
  // They all can have MIME `video/mp4` except `application/mp4` special-case which is hard to detect.
578
549
  // For some cases, we're specific, everything else falls to `video/mp4` with `mp4` extension.
579
- const brandMajor = this.buffer.toString('binary', 8, 12).replace('\0', ' ').trim();
550
+ const brandMajor = new Token.StringType(4, 'latin1').get(this.buffer, 8).replace('\0', ' ').trim();
580
551
  switch (brandMajor) {
581
552
  case 'avif':
582
553
  case 'avis':
@@ -706,11 +677,11 @@ export class FileTypeParser {
706
677
  try {
707
678
  await tokenizer.ignore(1350);
708
679
  const maxBufferSize = 10 * 1024 * 1024;
709
- const buffer = Buffer.alloc(Math.min(maxBufferSize, tokenizer.fileInfo.size));
680
+ const buffer = new Uint8Array(Math.min(maxBufferSize, tokenizer.fileInfo.size));
710
681
  await tokenizer.readBuffer(buffer, {mayBeLess: true});
711
682
 
712
683
  // Check if this is an Adobe Illustrator file
713
- if (buffer.includes(Buffer.from('AIPrivateData'))) {
684
+ if (includes(buffer, new TextEncoder().encode('AIPrivateData'))) {
714
685
  return {
715
686
  ext: 'ai',
716
687
  mime: 'application/postscript',
@@ -765,27 +736,31 @@ export class FileTypeParser {
765
736
  async function readField() {
766
737
  const msb = await tokenizer.peekNumber(Token.UINT8);
767
738
  let mask = 0x80;
768
- let ic = 0; // 0 = A, 1 = B, 2 = C, 3
769
- // = D
739
+ let ic = 0; // 0 = A, 1 = B, 2 = C, 3 = D
770
740
 
771
741
  while ((msb & mask) === 0 && mask !== 0) {
772
742
  ++ic;
773
743
  mask >>= 1;
774
744
  }
775
745
 
776
- const id = Buffer.alloc(ic + 1);
746
+ const id = new Uint8Array(ic + 1);
777
747
  await tokenizer.readBuffer(id);
778
748
  return id;
779
749
  }
780
750
 
781
751
  async function readElement() {
782
- const id = await readField();
752
+ const idField = await readField();
783
753
  const lengthField = await readField();
754
+
784
755
  lengthField[0] ^= 0x80 >> (lengthField.length - 1);
785
756
  const nrLength = Math.min(6, lengthField.length); // JavaScript can max read 6 bytes integer
757
+
758
+ const idView = new DataView(idField.buffer);
759
+ const lengthView = new DataView(lengthField.buffer, lengthField.length - nrLength, nrLength);
760
+
786
761
  return {
787
- id: id.readUIntBE(0, id.length),
788
- len: lengthField.readUIntBE(lengthField.length - nrLength, nrLength),
762
+ id: getUintBE(idView),
763
+ len: getUintBE(lengthView),
789
764
  };
790
765
  }
791
766
 
@@ -793,7 +768,7 @@ export class FileTypeParser {
793
768
  while (children > 0) {
794
769
  const element = await readElement();
795
770
  if (element.id === 0x42_82) {
796
- const rawValue = await tokenizer.readToken(new Token.StringType(element.len, 'utf-8'));
771
+ const rawValue = await tokenizer.readToken(new Token.StringType(element.len));
797
772
  return rawValue.replaceAll(/\00.*$/g, ''); // Return DocType
798
773
  }
799
774
 
@@ -1059,7 +1034,7 @@ export class FileTypeParser {
1059
1034
  }
1060
1035
 
1061
1036
  if (this.checkString('AC')) {
1062
- const version = this.buffer.toString('binary', 2, 6);
1037
+ const version = new Token.StringType(4, 'latin1').get(this.buffer, 2);
1063
1038
  if (version.match('^d*') && version >= 1000 && version <= 1050) {
1064
1039
  return {
1065
1040
  ext: 'dwg',
@@ -1126,7 +1101,7 @@ export class FileTypeParser {
1126
1101
  async function readChunkHeader() {
1127
1102
  return {
1128
1103
  length: await tokenizer.readToken(Token.INT32_BE),
1129
- type: await tokenizer.readToken(new Token.StringType(4, 'binary')),
1104
+ type: await tokenizer.readToken(new Token.StringType(4, 'latin1')),
1130
1105
  };
1131
1106
  }
1132
1107
 
@@ -1213,7 +1188,7 @@ export class FileTypeParser {
1213
1188
  // ASF_Header_Object first 80 bytes
1214
1189
  if (this.check([0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xA6, 0xD9])) {
1215
1190
  async function readHeader() {
1216
- const guid = Buffer.alloc(16);
1191
+ const guid = new Uint8Array(16);
1217
1192
  await tokenizer.readBuffer(guid);
1218
1193
  return {
1219
1194
  id: guid,
@@ -1228,7 +1203,7 @@ export class FileTypeParser {
1228
1203
  let payload = header.size - 24;
1229
1204
  if (_check(header.id, [0x91, 0x07, 0xDC, 0xB7, 0xB7, 0xA9, 0xCF, 0x11, 0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65])) {
1230
1205
  // Sync on Stream-Properties-Object (B7DC0791-A9B7-11CF-8EE6-00C00C205365)
1231
- const typeId = Buffer.alloc(16);
1206
+ const typeId = new Uint8Array(16);
1232
1207
  payload -= await tokenizer.readBuffer(typeId);
1233
1208
 
1234
1209
  if (_check(typeId, [0x40, 0x9E, 0x69, 0xF8, 0x4D, 0x5B, 0xCF, 0x11, 0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B])) {
@@ -1432,10 +1407,11 @@ export class FileTypeParser {
1432
1407
  }
1433
1408
 
1434
1409
  if (this.check([0x04, 0x00, 0x00, 0x00]) && this.buffer.length >= 16) { // Rough & quick check Pickle/ASAR
1435
- const jsonSize = this.buffer.readUInt32LE(12);
1410
+ const jsonSize = new DataView(this.buffer.buffer).getUint32(12, true);
1411
+
1436
1412
  if (jsonSize > 12 && this.buffer.length >= jsonSize + 16) {
1437
1413
  try {
1438
- const header = this.buffer.slice(16, jsonSize + 16).toString();
1414
+ const header = new TextDecoder().decode(this.buffer.slice(16, jsonSize + 16));
1439
1415
  const json = JSON.parse(header);
1440
1416
  // Check if Pickle is ASAR
1441
1417
  if (json.files) { // Final check, assuring Pickle/ASAR format
@@ -1682,9 +1658,5 @@ export class FileTypeParser {
1682
1658
  }
1683
1659
  }
1684
1660
 
1685
- export async function fileTypeStream(readableStream, options = {}) {
1686
- return new FileTypeParser().toDetectionStream(readableStream, options);
1687
- }
1688
-
1689
1661
  export const supportedExtensions = new Set(extensions);
1690
1662
  export const supportedMimeTypes = new Set(mimeTypes);
package/index.d.ts CHANGED
@@ -1,13 +1,69 @@
1
- import type {FileTypeResult} from './core.js';
1
+ /**
2
+ Typings for Node.js specific entry point.
3
+ */
4
+
5
+ import type {Readable as NodeReadableStream} from 'node:stream';
6
+ import type {FileTypeResult, StreamOptions, AnyWebReadableStream} from './core.js';
7
+ import {FileTypeParser} from './core.js';
8
+
9
+ export type ReadableStreamWithFileType = NodeReadableStream & {
10
+ readonly fileType?: FileTypeResult;
11
+ };
12
+
13
+ export declare class NodeFileTypeParser extends FileTypeParser {
14
+ /**
15
+ @param stream - Node.js `stream.Readable` or Web API `ReadableStream`.
16
+ */
17
+ fromStream(stream: AnyWebReadableStream<Uint8Array> | NodeReadableStream): Promise<FileTypeResult | undefined>;
18
+
19
+ /**
20
+ Works the same way as {@link fileTypeStream}, additionally taking into account custom detectors (if any were provided to the constructor).
21
+ */
22
+ toDetectionStream(readableStream: NodeReadableStream, options?: StreamOptions): Promise<ReadableStreamWithFileType>;
23
+ }
2
24
 
3
25
  /**
4
26
  Detect the file type of a file path.
5
27
 
6
28
  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.
7
29
 
8
- @param path - The file path to parse.
30
+ @param path
9
31
  @returns The detected file type and MIME type or `undefined` when there is no match.
10
32
  */
11
33
  export function fileTypeFromFile(path: string): Promise<FileTypeResult | undefined>;
12
34
 
35
+ export function fileTypeFromStream(stream: AnyWebReadableStream<Uint8Array> | NodeReadableStream): Promise<FileTypeResult | undefined>;
36
+
37
+ /**
38
+ 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()`.
39
+
40
+ This method can be handy to put in between a stream, but it comes with a price.
41
+ Internally `stream()` builds up a buffer of `sampleSize` bytes, used as a sample, to determine the file type.
42
+ The sample size impacts the file detection resolution.
43
+ A smaller sample size will result in lower probability of the best file type detection.
44
+
45
+ **Note:** This method is only available when using Node.js.
46
+ **Note:** Requires Node.js 14 or later.
47
+
48
+ @param readableStream - A [readable stream](https://nodejs.org/api/stream.html#stream_class_stream_readable) containing a file to examine.
49
+ @param options - Maybe used to override the default sample-size.
50
+ @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()`.
51
+
52
+ @example
53
+ ```
54
+ import got from 'got';
55
+ import {fileTypeStream} from 'file-type';
56
+
57
+ const url = 'https://upload.wikimedia.org/wikipedia/en/a/a9/Example.jpg';
58
+
59
+ const stream1 = got.stream(url);
60
+ const stream2 = await fileTypeStream(stream1, {sampleSize: 1024});
61
+
62
+ if (stream2.fileType?.mime === 'image/jpeg') {
63
+ // stream2 can be used to stream the JPEG image (from the very beginning of the stream)
64
+ }
65
+ ```
66
+ */
67
+ export function fileTypeStream(readableStream: NodeReadableStream, options?: StreamOptions): Promise<ReadableStreamWithFileType>;
68
+
13
69
  export * from './core.js';
package/index.js CHANGED
@@ -1,5 +1,56 @@
1
+ /**
2
+ Node.js specific entry point.
3
+ */
4
+
5
+ import {ReadableStream as WebReadableStream} from 'node:stream/web';
1
6
  import * as strtok3 from 'strtok3';
2
- import {FileTypeParser} from './core.js';
7
+ import {FileTypeParser, reasonableDetectionSizeInBytes} from './core.js';
8
+
9
+ export class NodeFileTypeParser extends FileTypeParser {
10
+ async fromStream(stream) {
11
+ const tokenizer = await (stream instanceof WebReadableStream ? strtok3.fromWebStream(stream) : strtok3.fromStream(stream));
12
+ try {
13
+ return super.fromTokenizer(tokenizer);
14
+ } finally {
15
+ await tokenizer.close();
16
+ }
17
+ }
18
+
19
+ async toDetectionStream(readableStream, options = {}) {
20
+ const {default: stream} = await import('node:stream');
21
+ const {sampleSize = reasonableDetectionSizeInBytes} = options;
22
+
23
+ return new Promise((resolve, reject) => {
24
+ readableStream.on('error', reject);
25
+
26
+ readableStream.once('readable', () => {
27
+ (async () => {
28
+ try {
29
+ // Set up output stream
30
+ const pass = new stream.PassThrough();
31
+ const outputStream = stream.pipeline ? stream.pipeline(readableStream, pass, () => {}) : readableStream.pipe(pass);
32
+
33
+ // Read the input stream and detect the filetype
34
+ const chunk = readableStream.read(sampleSize) ?? readableStream.read() ?? new Uint8Array(0);
35
+ try {
36
+ pass.fileType = await this.fromBuffer(chunk);
37
+ } catch (error) {
38
+ if (error instanceof strtok3.EndOfStreamError) {
39
+ pass.fileType = undefined;
40
+ } else {
41
+ reject(error);
42
+ }
43
+ }
44
+
45
+ resolve(outputStream);
46
+ } catch (error) {
47
+ reject(error);
48
+ }
49
+ })();
50
+ });
51
+ });
52
+ }
53
+ }
3
54
 
4
55
  export async function fileTypeFromFile(path, fileTypeOptions) {
5
56
  const tokenizer = await strtok3.fromFile(path);
@@ -11,4 +62,12 @@ export async function fileTypeFromFile(path, fileTypeOptions) {
11
62
  }
12
63
  }
13
64
 
14
- export * from './core.js';
65
+ export async function fileTypeFromStream(stream, fileTypeOptions) {
66
+ return (new NodeFileTypeParser(fileTypeOptions)).fromStream(stream);
67
+ }
68
+
69
+ export async function fileTypeStream(readableStream, options = {}) {
70
+ return new NodeFileTypeParser().toDetectionStream(readableStream, options);
71
+ }
72
+
73
+ export {fileTypeFromBuffer, fileTypeFromBlob, FileTypeParser, supportedMimeTypes, supportedExtensions} from './core.js';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "file-type",
3
- "version": "19.0.0",
4
- "description": "Detect the file type of a Buffer/Uint8Array/ArrayBuffer",
3
+ "version": "19.1.0",
4
+ "description": "Detect the file type of a Uint8Array/ArrayBuffer",
5
5
  "license": "MIT",
6
6
  "repository": "sindresorhus/file-type",
7
7
  "funding": "https://github.com/sindresorhus/file-type?sponsor=1",
@@ -14,7 +14,7 @@
14
14
  "exports": {
15
15
  ".": {
16
16
  "node": "./index.js",
17
- "default": "./browser.js"
17
+ "default": "./core.js"
18
18
  },
19
19
  "./core": "./core.js"
20
20
  },
@@ -28,8 +28,6 @@
28
28
  "files": [
29
29
  "index.js",
30
30
  "index.d.ts",
31
- "browser.js",
32
- "browser.d.ts",
33
31
  "core.js",
34
32
  "core.d.ts",
35
33
  "supported.js",
@@ -210,9 +208,9 @@
210
208
  "fbx"
211
209
  ],
212
210
  "dependencies": {
213
- "readable-web-to-node-stream": "^3.0.2",
214
- "strtok3": "^7.0.0",
215
- "token-types": "^5.0.1"
211
+ "strtok3": "^7.1.0",
212
+ "token-types": "^6.0.0",
213
+ "uint8array-extras": "^1.3.0"
216
214
  },
217
215
  "devDependencies": {
218
216
  "@tokenizer/token": "^0.3.0",
package/readme.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # file-type
2
2
 
3
- > Detect the file type of a Buffer/Uint8Array/ArrayBuffer
3
+ > Detect the file type of a Uint8Array/ArrayBuffer
4
4
 
5
5
  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.
6
6
 
@@ -31,7 +31,7 @@ console.log(await fileTypeFromFile('Unicorn.png'));
31
31
  //=> {ext: 'png', mime: 'image/png'}
32
32
  ```
33
33
 
34
- Determine file type from a Buffer, which may be a portion of the beginning of a file:
34
+ Determine file type from a Uint8Array/ArrayBuffer, which may be a portion of the beginning of a file:
35
35
 
36
36
  ```js
37
37
  import {fileTypeFromBuffer} from 'file-type';
@@ -107,7 +107,7 @@ console.log(fileType);
107
107
 
108
108
  ### fileTypeFromBuffer(buffer)
109
109
 
110
- Detect the file type of a `Buffer`, `Uint8Array`, or `ArrayBuffer`.
110
+ Detect the file type of a `Uint8Array`, or `ArrayBuffer`.
111
111
 
112
112
  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.
113
113
 
@@ -122,7 +122,7 @@ Or `undefined` when there is no match.
122
122
 
123
123
  #### buffer
124
124
 
125
- Type: `Buffer | Uint8Array | ArrayBuffer`
125
+ Type: `Uint8Array | ArrayBuffer`
126
126
 
127
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
 
@@ -147,7 +147,7 @@ The file path to parse.
147
147
 
148
148
  ### fileTypeFromStream(stream)
149
149
 
150
- Detect the file type of a Node.js [readable stream](https://nodejs.org/api/stream.html#stream_class_stream_readable).
150
+ Detect the file type of a [Node.js readable stream](https://nodejs.org/api/stream.html#stream_class_stream_readable) or a [Web API ReadableStream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream).
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
 
@@ -168,6 +168,8 @@ A readable stream representing file data.
168
168
 
169
169
  Detect the file type of a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob).
170
170
 
171
+ It will **stream** the underlying Blob, and required a [ReadableStreamBYOBReader](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamBYOBReader) which **require Node.js ≥ 20**.
172
+
171
173
  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
174
 
173
175
  Returns a `Promise` for an object with the detected file type:
@@ -335,7 +337,7 @@ const customDetectors = [
335
337
  async tokenizer => {
336
338
  const unicornHeader = [85, 78, 73, 67, 79, 82, 78]; // 'UNICORN' as decimal string
337
339
 
338
- const buffer = Buffer.alloc(7);
340
+ const buffer = new Uint8Array(7);
339
341
  await tokenizer.peekBuffer(buffer, {length: unicornHeader.length, mayBeLess: true});
340
342
 
341
343
  if (unicornHeader.every((value, index) => value === buffer[index])) {
@@ -346,7 +348,7 @@ const customDetectors = [
346
348
  },
347
349
  ];
348
350
 
349
- const buffer = Buffer.from('UNICORN');
351
+ const buffer = new Uint8Array(new TextEncoder().encode('UNICORN'));
350
352
  const parser = new FileTypeParser({customDetectors});
351
353
  const fileType = await parser.fromBuffer(buffer);
352
354
  console.log(fileType);
package/util.js CHANGED
@@ -1,3 +1,5 @@
1
+ import {StringType} from 'token-types';
2
+
1
3
  export function stringToBytes(string) {
2
4
  return [...string].map(character => character.charCodeAt(0)); // eslint-disable-line unicorn/prefer-code-point
3
5
  }
@@ -5,12 +7,12 @@ export function stringToBytes(string) {
5
7
  /**
6
8
  Checks whether the TAR checksum is valid.
7
9
 
8
- @param {Buffer} buffer - The TAR header `[offset ... offset + 512]`.
10
+ @param {Uint8Array} arrayBuffer - The TAR header `[offset ... offset + 512]`.
9
11
  @param {number} offset - TAR header offset.
10
12
  @returns {boolean} `true` if the TAR checksum is valid, otherwise `false`.
11
13
  */
12
- export function tarHeaderChecksumMatches(buffer, offset = 0) {
13
- const readSum = Number.parseInt(buffer.toString('utf8', 148, 154).replace(/\0.*$/, '').trim(), 8); // Read sum in header
14
+ export function tarHeaderChecksumMatches(arrayBuffer, offset = 0) {
15
+ const readSum = Number.parseInt(new StringType(6).get(arrayBuffer, 148).replace(/\0.*$/, '').trim(), 8); // Read sum in header
14
16
  if (Number.isNaN(readSum)) {
15
17
  return false;
16
18
  }
@@ -18,11 +20,11 @@ export function tarHeaderChecksumMatches(buffer, offset = 0) {
18
20
  let sum = 8 * 0x20; // Initialize signed bit sum
19
21
 
20
22
  for (let index = offset; index < offset + 148; index++) {
21
- sum += buffer[index];
23
+ sum += arrayBuffer[index];
22
24
  }
23
25
 
24
26
  for (let index = offset + 156; index < offset + 512; index++) {
25
- sum += buffer[index];
27
+ sum += arrayBuffer[index];
26
28
  }
27
29
 
28
30
  return readSum === sum;
package/browser.d.ts DELETED
@@ -1,29 +0,0 @@
1
- import type {FileTypeResult} from './core.js';
2
-
3
- /**
4
- Detect the file type of a [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream).
5
-
6
- @example
7
- ```
8
- import {fileTypeFromStream} from 'file-type';
9
-
10
- const url = 'https://upload.wikimedia.org/wikipedia/en/a/a9/Example.jpg';
11
-
12
- const response = await fetch(url);
13
- const fileType = await fileTypeFromStream(response.body);
14
-
15
- console.log(fileType);
16
- //=> {ext: 'jpg', mime: 'image/jpeg'}
17
- ```
18
- */
19
- export declare function fileTypeFromStream(stream: ReadableStream): Promise<FileTypeResult | undefined>;
20
-
21
- export {
22
- fileTypeFromBuffer,
23
- fileTypeFromBlob,
24
- supportedExtensions,
25
- supportedMimeTypes,
26
- type FileTypeResult,
27
- type FileExtension,
28
- type MimeType,
29
- } from './core.js';
package/browser.js DELETED
@@ -1,15 +0,0 @@
1
- import {ReadableWebToNodeStream} from 'readable-web-to-node-stream';
2
- import {fileTypeFromStream as coreFileTypeFromStream} from './core.js';
3
-
4
- export async function fileTypeFromStream(stream) {
5
- const readableWebToNodeStream = new ReadableWebToNodeStream(stream);
6
- const fileType = await coreFileTypeFromStream(readableWebToNodeStream);
7
- await readableWebToNodeStream.close();
8
- return fileType;
9
- }
10
-
11
- export {
12
- fileTypeFromTokenizer,
13
- fileTypeFromBuffer,
14
- fileTypeStream,
15
- } from './core.js';