file-type 22.0.0 → 22.0.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "file-type",
3
- "version": "22.0.0",
3
+ "version": "22.0.2",
4
4
  "description": "Detect the file type of a file, stream, or data",
5
5
  "license": "MIT",
6
6
  "repository": "sindresorhus/file-type",
@@ -241,6 +241,7 @@
241
241
  "@types/node": "^25.5.0",
242
242
  "ava": "^7.0.0",
243
243
  "commonmark": "^0.31.2",
244
+ "esbuild": "^0.28.0",
244
245
  "get-stream": "^9.0.1",
245
246
  "tsd": "^0.33.0",
246
247
  "xo": "^2.0.2"
package/readme.md CHANGED
@@ -306,6 +306,9 @@ They allow support for uncommon file types, non-binary formats, or customized de
306
306
  Detectors can be added via the constructor options or by directly modifying `FileTypeParser#detectors`.
307
307
  Detectors provided through the constructor are executed before the default ones.
308
308
 
309
+ > [!NOTE]
310
+ > Not safe for concurrent use. Create a new instance per call if you need concurrency.
311
+
309
312
  ### Example adding a detector
310
313
 
311
314
  ```js
@@ -53,12 +53,17 @@ async function decompressDeflateRawWithLimit(data, {maximumLength = maximumZipEn
53
53
 
54
54
  totalLength += value.length;
55
55
  if (totalLength > maximumLength) {
56
- await reader.cancel();
56
+ await reader.cancel().catch(() => {});
57
57
  throw new Error(`ZIP entry decompressed data exceeds ${maximumLength} bytes`);
58
58
  }
59
59
 
60
60
  chunks.push(value);
61
61
  }
62
+ } catch (error) {
63
+ // A ZIP entry with a data descriptor has an unknown compressed size, so the buffered data can contain bytes that follow the deflate stream. Node.js 24 rejects those, unlike other versions. The data decompressed so far is still valid and enough for detection.
64
+ if (error.code !== 'ERR_TRAILING_JUNK_AFTER_STREAM_END') {
65
+ throw error;
66
+ }
62
67
  } finally {
63
68
  reader.releaseLock();
64
69
  }
package/source/index.d.ts CHANGED
@@ -238,6 +238,9 @@ This method can be handy to put in a stream pipeline, but it comes with a price.
238
238
  */
239
239
  export function fileTypeStream(webStream: AnyWebReadableStream<Uint8Array>, options?: StreamOptions & FileTypeOptions): Promise<AnyWebReadableByteStreamWithFileType>;
240
240
 
241
+ /**
242
+ Not safe for concurrent use. Create a new instance per call if you need concurrency.
243
+ */
241
244
  export declare class FileTypeParser {
242
245
  /**
243
246
  File type detectors.
package/source/index.js CHANGED
@@ -25,7 +25,7 @@ import {detectPng} from './detectors/png.js';
25
25
  import {detectAsf} from './detectors/asf.js';
26
26
 
27
27
  export const reasonableDetectionSizeInBytes = 4100; // A fair amount of file-types are detectable within this range.
28
- const maximumMpegOffsetTolerance = reasonableDetectionSizeInBytes - 2;
28
+ const maximumMpegOffsetTolerance = reasonableDetectionSizeInBytes - 4; // Keep room for the 4 bytes of the frame header at the deepest scanned offset.
29
29
  const maximumNestedGzipDetectionSizeInBytes = maximumUntrustedSkipSizeInBytes;
30
30
  const maximumNestedGzipProbeDepth = 1;
31
31
  const unknownSizeGzipProbeTimeoutInMilliseconds = 100;
@@ -62,6 +62,11 @@ function getKnownFileSizeOrMaximum(fileSize) {
62
62
  return Math.max(0, fileSize);
63
63
  }
64
64
 
65
+ // Keep the specifier non-literal at the call site so browser bundlers do not try to resolve Node-only imports.
66
+ function importAtRuntime(specifier) {
67
+ return import(specifier);
68
+ }
69
+
65
70
  // Wrap stream in an identity TransformStream to avoid BYOB readers.
66
71
  // Node.js has a bug where calling controller.close() inside a BYOB stream's
67
72
  // pull() callback does not resolve pending reader.read() calls, causing
@@ -258,8 +263,8 @@ export class FileTypeParser {
258
263
  this.options.signal?.throwIfAborted();
259
264
  // TODO: Remove this when `strtok3.fromFile()` safely rejects non-regular filesystem objects without a pathname race.
260
265
  const [{default: fsPromises}, {FileTokenizer}] = await Promise.all([
261
- import('node:fs/promises'),
262
- import('strtok3'),
266
+ importAtRuntime('node:fs/promises'),
267
+ importAtRuntime('strtok3'),
263
268
  ]);
264
269
  const fileHandle = await fsPromises.open(path, fsPromises.constants.O_RDONLY | fsPromises.constants.O_NONBLOCK);
265
270
  const fileStat = await fileHandle.stat();
@@ -1724,11 +1729,11 @@ export class FileTypeParser {
1724
1729
  };
1725
1730
  }
1726
1731
 
1727
- // Adjust buffer to `mpegOffsetTolerance`
1728
- await tokenizer.peekBuffer(this.buffer, {length: Math.min(2 + this.options.mpegOffsetTolerance, fileSize), mayBeLess: true});
1732
+ // Adjust buffer to `mpegOffsetTolerance`, plus the 4 bytes of the frame header itself
1733
+ await tokenizer.peekBuffer(this.buffer, {length: Math.min(4 + this.options.mpegOffsetTolerance, fileSize), mayBeLess: true});
1729
1734
 
1730
1735
  // Check MPEG 1 or 2 Layer 3 header, or 'layer 0' for ADTS (MPEG sync-word 0xFFE)
1731
- if (this.buffer.length >= (2 + this.options.mpegOffsetTolerance)) {
1736
+ if (this.buffer.length >= (4 + this.options.mpegOffsetTolerance)) {
1732
1737
  for (let depth = 0; depth <= this.options.mpegOffsetTolerance; ++depth) {
1733
1738
  const type = this.scanMpeg(depth);
1734
1739
  if (type) {
@@ -1860,22 +1865,34 @@ export class FileTypeParser {
1860
1865
  */
1861
1866
  scanMpeg(offset) {
1862
1867
  if (this.check([0xFF, 0xE0], {offset, mask: [0xFF, 0xE0]})) {
1868
+ // Both (ADTS) MPEG-2 and MPEG-4 are AAC
1863
1869
  if (this.check([0x10], {offset: offset + 1, mask: [0x16]})) {
1864
- // Check for (ADTS) MPEG-2
1865
- if (this.check([0x08], {offset: offset + 1, mask: [0x08]})) {
1866
- return {
1867
- ext: 'aac',
1868
- mime: 'audio/aac',
1869
- };
1870
- }
1871
-
1872
- // Must be (ADTS) MPEG-4
1873
1870
  return {
1874
1871
  ext: 'aac',
1875
1872
  mime: 'audio/aac',
1876
1873
  };
1877
1874
  }
1878
1875
 
1876
+ // An MPEG-1 Layer I header with CRC protection enabled is byte-identical to a UTF-16 LE BOM, so text files cannot be told apart from audio. `file(1)` disables MPEG-1 Layer I detection completely for this reason; we only drop the colliding variant.
1877
+ if (this.check([0xFF, 0xFE], {offset})) {
1878
+ return;
1879
+ }
1880
+
1881
+ // Reject the reserved MPEG version `01`
1882
+ if (this.check([0x08], {offset: offset + 1, mask: [0x18]})) {
1883
+ return;
1884
+ }
1885
+
1886
+ // Reject the `bad` (`1111`) bitrate index
1887
+ if (this.check([0xF0], {offset: offset + 2, mask: [0xF0]})) {
1888
+ return;
1889
+ }
1890
+
1891
+ // Reject the reserved sampling frequency `11`
1892
+ if (this.check([0x0C], {offset: offset + 2, mask: [0x0C]})) {
1893
+ return;
1894
+ }
1895
+
1879
1896
  // MPEG 1 or 2 Layer 3 header
1880
1897
  // Check for MPEG layer 3
1881
1898
  if (this.check([0x02], {offset: offset + 1, mask: [0x06]})) {