From 1fce3c4d10a3bef0ea6ee9fd189a000d675027d3 Mon Sep 17 00:00:00 2001 From: Borewit Date: Thu, 4 Jul 2024 23:28:02 +0200 Subject: [PATCH 01/13] Add support for reading from a WebStreams - Stream Blob via a WebStreams, instead of buffering the full content - Update strtok3 to v7.0.0 --- .github/workflows/main.yml | 1 + browser.d.ts | 29 ---------------- browser.js | 15 -------- core.d.ts | 19 ++++++---- core.js | 10 ++++-- index.d.ts | 18 ++++++---- index.js | 4 +++ index.test-d.ts | 2 +- package.json | 5 +-- readme.md | 4 ++- test.js | 71 +++++++++++++++++++++++--------------- 11 files changed, 83 insertions(+), 95 deletions(-) delete mode 100644 browser.d.ts delete mode 100644 browser.js diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 346585cf..6e981506 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,6 +10,7 @@ jobs: fail-fast: false matrix: node-version: + - 22 - 20 - 18 steps: diff --git a/browser.d.ts b/browser.d.ts deleted file mode 100644 index 83c015f9..00000000 --- a/browser.d.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type {FileTypeResult} from './core.js'; - -/** -Detect the file type of a [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream). - -@example -``` -import {fileTypeFromStream} from 'file-type'; - -const url = 'https://upload.wikimedia.org/wikipedia/en/a/a9/Example.jpg'; - -const response = await fetch(url); -const fileType = await fileTypeFromStream(response.body); - -console.log(fileType); -//=> {ext: 'jpg', mime: 'image/jpeg'} -``` -*/ -export declare function fileTypeFromStream(stream: ReadableStream): Promise; - -export { - fileTypeFromBuffer, - fileTypeFromBlob, - supportedExtensions, - supportedMimeTypes, - type FileTypeResult, - type FileExtension, - type MimeType, -} from './core.js'; diff --git a/browser.js b/browser.js deleted file mode 100644 index 54cdb06e..00000000 --- a/browser.js +++ /dev/null @@ -1,15 +0,0 @@ -import {ReadableWebToNodeStream} from 'readable-web-to-node-stream'; -import {fileTypeFromStream as coreFileTypeFromStream} from './core.js'; - -export async function fileTypeFromStream(stream) { - const readableWebToNodeStream = new ReadableWebToNodeStream(stream); - const fileType = await coreFileTypeFromStream(readableWebToNodeStream); - await readableWebToNodeStream.close(); - return fileType; -} - -export { - fileTypeFromTokenizer, - fileTypeFromBuffer, - fileTypeStream, -} from './core.js'; diff --git a/core.d.ts b/core.d.ts index 9247a843..c0bb1b99 100644 --- a/core.d.ts +++ b/core.d.ts @@ -1,4 +1,9 @@ -import type {Readable as ReadableStream} from 'node:stream'; +/** + * Typings for primary entry point, Node.js specific typings can be found in index.d.ts + */ + +import type {Readable as NodeReadableStream} from 'node:stream'; +import type {ReadableStream as WebReadableStream} from 'node:stream/web'; import type {ITokenizer} from 'strtok3'; export type FileExtension = @@ -318,7 +323,7 @@ export type FileTypeResult = { readonly mime: MimeType; }; -export type ReadableStreamWithFileType = ReadableStream & { +export type ReadableStreamWithFileType = NodeReadableStream & { readonly fileType?: FileTypeResult; }; @@ -339,10 +344,10 @@ Detect the file type of a Node.js [readable stream](https://nodejs.org/api/strea 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. -@param stream - A readable stream representing file data. +@param stream - A Node.js Readable stream or Web API Readable Stream representing file data. The Web Readable stream **must be a byte stream**. @returns The detected file type, or `undefined` when there is no match. */ -export function fileTypeFromStream(stream: ReadableStream): Promise; +export function fileTypeFromStream(stream: NodeReadableStream | WebReadableStream): Promise; /** Detect the file type from an [`ITokenizer`](https://github.com/Borewit/strtok3#tokenizer) source. @@ -420,7 +425,7 @@ if (stream2.fileType?.mime === 'image/jpeg') { } ``` */ -export function fileTypeStream(readableStream: ReadableStream, options?: StreamOptions): Promise; +export function fileTypeStream(readableStream: NodeReadableStream, options?: StreamOptions): Promise; /** 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). @@ -511,7 +516,7 @@ export declare class FileTypeParser { /** Works the same way as {@link fileTypeFromStream}, additionally taking into account custom detectors (if any were provided to the constructor). */ - fromStream(stream: ReadableStream): Promise; + fromStream(stream: NodeReadableStream | WebReadableStream): Promise; /** Works the same way as {@link fileTypeFromTokenizer}, additionally taking into account custom detectors (if any were provided to the constructor). @@ -526,5 +531,5 @@ export declare class FileTypeParser { /** Works the same way as {@link fileTypeStream}, additionally taking into account custom detectors (if any were provided to the constructor). */ - toDetectionStream(readableStream: ReadableStream, options?: StreamOptions): Promise; + toDetectionStream(readableStream: NodeReadableStream, options?: StreamOptions): Promise; } diff --git a/core.js b/core.js index 5f92e5f9..7b5fed16 100644 --- a/core.js +++ b/core.js @@ -1,3 +1,8 @@ +/** + * Primary entry point, Node.js specific entry point is index.js + */ + +import {ReadableStream as WebReadableStream} from 'node:stream/web'; import * as Token from 'token-types'; import * as strtok3 from 'strtok3/core'; import {includes, indexOf, getUintBE} from 'uint8array-extras'; @@ -88,12 +93,11 @@ export class FileTypeParser { } async fromBlob(blob) { - const buffer = await blob.arrayBuffer(); - return this.fromBuffer(new Uint8Array(buffer)); + return this.fromStream(blob.stream()); } async fromStream(stream) { - const tokenizer = await strtok3.fromStream(stream); + const tokenizer = await (stream instanceof WebReadableStream ? strtok3.fromWebStream(stream) : strtok3.fromStream(stream)); try { return await this.fromTokenizer(tokenizer); } finally { diff --git a/index.d.ts b/index.d.ts index e2b91ad4..799eaa65 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,13 +1,17 @@ -import type {FileTypeResult} from './core.js'; - /** -Detect the file type of a file path. + * Typings for Node.js specific entry point + */ -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. +import type {FileTypeResult} from './core.js'; -@param path - The file path to parse. -@returns The detected file type and MIME type or `undefined` when there is no match. -*/ +/** + * Detect the file type of a file path. + * + * 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. + * + * @param path + * @returns The detected file type and MIME type or `undefined` when there is no match. + */ export function fileTypeFromFile(path: string): Promise; export * from './core.js'; diff --git a/index.js b/index.js index 24bacf9d..34f8a6b1 100644 --- a/index.js +++ b/index.js @@ -1,3 +1,7 @@ +/** + * Node.js specific entry point + */ + import * as strtok3 from 'strtok3'; import {FileTypeParser} from './core.js'; diff --git a/index.test-d.ts b/index.test-d.ts index d37b2ccb..4518db65 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -2,7 +2,7 @@ import {createReadStream} from 'node:fs'; import {expectType} from 'tsd'; import { type FileTypeResult as FileTypeResultBrowser, -} from './browser.js'; +} from './core.js'; import { fileTypeFromBlob, fileTypeFromBuffer, diff --git a/package.json b/package.json index a7c6eccf..057c444c 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "exports": { ".": { "node": "./index.js", - "default": "./browser.js" + "default": "./core.js" }, "./core": "./core.js" }, @@ -28,8 +28,6 @@ "files": [ "index.js", "index.d.ts", - "browser.js", - "browser.d.ts", "core.js", "core.d.ts", "supported.js", @@ -210,7 +208,6 @@ "fbx" ], "dependencies": { - "readable-web-to-node-stream": "^3.0.2", "strtok3": "^7.1.0", "token-types": "^6.0.0", "uint8array-extras": "^1.3.0" diff --git a/readme.md b/readme.md index bc1cc465..332d263c 100644 --- a/readme.md +++ b/readme.md @@ -147,7 +147,7 @@ The file path to parse. ### fileTypeFromStream(stream) -Detect the file type of a Node.js [readable stream](https://nodejs.org/api/stream.html#stream_class_stream_readable). +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). 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. @@ -168,6 +168,8 @@ A readable stream representing file data. Detect the file type of a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob). +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**. + 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. Returns a `Promise` for an object with the detected file type: diff --git a/test.js b/test.js index 8d937afe..3fb92a10 100644 --- a/test.js +++ b/test.js @@ -25,6 +25,9 @@ const missingTests = new Set([ 'mpc', ]); +const [nodeMajorVersion] = process.versions.node.split('.').map(Number); +const nodeVersionSupportingByeBlobStream = 20; + const types = [...supportedExtensions].filter(ext => !missingTests.has(ext)); // Define an entry here only if the fixture has a different @@ -362,6 +365,10 @@ async function testStream(t, ext, name) { t.true(areUint8ArraysEqual(bufferA, bufferB)); } +test('Test suite must be able to detect Node.js major version', t => { + t.is(typeof nodeMajorVersion, 'number', 'Detected Node.js major version should be a number'); +}); + let i = 0; for (const type of types) { if (Object.prototype.hasOwnProperty.call(names, type)) { @@ -371,7 +378,11 @@ for (const type of types) { _test(`${name}.${type} ${i++} .fileTypeFromFile() method - same fileType`, testFromFile, type, name); _test(`${name}.${type} ${i++} .fileTypeFromBuffer() method - same fileType`, testFromBuffer, type, name); - _test(`${name}.${type} ${i++} .fileTypeFromBlob() method - same fileType`, testFromBlob, type, name); + if (nodeMajorVersion >= nodeVersionSupportingByeBlobStream) { + // Blob requires to stream to BYOB ReadableStream, requiring Node.js ≥ 20 + _test(`${name}.${type} ${i++} .fileTypeFromBlob() method - same fileType`, testFromBlob, type, name); + } + _test(`${name}.${type} ${i++} .fileTypeFromStream() method - same fileType`, testFileFromStream, type, name); test(`${name}.${type} ${i++} .fileTypeStream() - identical streams`, testStream, type, name); } @@ -693,41 +704,45 @@ const tokenizerPositionChanger = tokenizer => { tokenizer.readBuffer(buffer, {length: 1, mayBeLess: true}); }; -test('fileTypeFromBlob should detect custom file type "unicorn" using custom detectors', async t => { - // Set up the "unicorn" file content - const header = 'UNICORN FILE\n'; - const blob = new Blob([header]); +if (nodeMajorVersion >= nodeVersionSupportingByeBlobStream) { + // Blob requires to stream to BYOB ReadableStream, requiring Node.js ≥ 20 - const customDetectors = [unicornDetector]; - const parser = new FileTypeParser({customDetectors}); + test('fileTypeFromBlob should detect custom file type "unicorn" using custom detectors', async t => { + // Set up the "unicorn" file content + const header = 'UNICORN FILE\n'; + const blob = new Blob([header]); - const result = await parser.fromBlob(blob); - t.deepEqual(result, {ext: 'unicorn', mime: 'application/unicorn'}); -}); + const customDetectors = [unicornDetector]; + const parser = new FileTypeParser({customDetectors}); -test('fileTypeFromBlob should keep detecting default file types when no custom detector matches', async t => { - const file = path.join(__dirname, 'fixture', 'fixture.png'); - const chunk = fs.readFileSync(file); - const blob = new Blob([chunk]); + const result = await parser.fromBlob(blob); + t.deepEqual(result, {ext: 'unicorn', mime: 'application/unicorn'}); + }); - const customDetectors = [unicornDetector]; - const parser = new FileTypeParser({customDetectors}); + test('fileTypeFromBlob should keep detecting default file types when no custom detector matches', async t => { + const file = path.join(__dirname, 'fixture', 'fixture.png'); + const chunk = fs.readFileSync(file); + const blob = new Blob([chunk]); - const result = await parser.fromBlob(blob); - t.deepEqual(result, {ext: 'png', mime: 'image/png'}); -}); + const customDetectors = [unicornDetector]; + const parser = new FileTypeParser({customDetectors}); -test('fileTypeFromBlob should allow overriding default file type detectors', async t => { - const file = path.join(__dirname, 'fixture', 'fixture.png'); - const chunk = fs.readFileSync(file); - const blob = new Blob([chunk]); + const result = await parser.fromBlob(blob); + t.deepEqual(result, {ext: 'png', mime: 'image/png'}); + }); - const customDetectors = [mockPngDetector]; - const parser = new FileTypeParser({customDetectors}); + test('fileTypeFromBlob should allow overriding default file type detectors', async t => { + const file = path.join(__dirname, 'fixture', 'fixture.png'); + const chunk = fs.readFileSync(file); + const blob = new Blob([chunk]); - const result = await parser.fromBlob(blob); - t.deepEqual(result, {ext: 'mockPng', mime: 'image/mockPng'}); -}); + const customDetectors = [mockPngDetector]; + const parser = new FileTypeParser({customDetectors}); + + const result = await parser.fromBlob(blob); + t.deepEqual(result, {ext: 'mockPng', mime: 'image/mockPng'}); + }); +} test('fileTypeFromBuffer should detect custom file type "unicorn" using custom detectors', async t => { const header = 'UNICORN FILE\n'; From 7271b4bde4aacea790458dd7f063b37e2bb72ddd Mon Sep 17 00:00:00 2001 From: Borewit Date: Sat, 6 Jul 2024 16:21:56 +0200 Subject: [PATCH 02/13] Move Node.js dependencies from `core.js` to `index.js` Introduce class `NodeFileTypeParser`, overriding `fromStream()`, which also allow a Node.js Readable stream. --- core.d.ts | 10 +++++----- core.js | 3 +-- index.d.ts | 13 +++++++++++++ index.js | 18 +++++++++++++++++- index.test-d.ts | 7 ++++--- test.js | 34 +++++++++++++++++----------------- 6 files changed, 57 insertions(+), 28 deletions(-) diff --git a/core.d.ts b/core.d.ts index c0bb1b99..7c2a06c6 100644 --- a/core.d.ts +++ b/core.d.ts @@ -2,8 +2,8 @@ * Typings for primary entry point, Node.js specific typings can be found in index.d.ts */ -import type {Readable as NodeReadableStream} from 'node:stream'; import type {ReadableStream as WebReadableStream} from 'node:stream/web'; +import type {Readable as NodeReadableStream} from 'node:stream'; import type {ITokenizer} from 'strtok3'; export type FileExtension = @@ -323,7 +323,7 @@ export type FileTypeResult = { readonly mime: MimeType; }; -export type ReadableStreamWithFileType = NodeReadableStream & { +export type ReadableStreamWithFileType = WebReadableStream & { readonly fileType?: FileTypeResult; }; @@ -347,7 +347,7 @@ The file type is detected by checking the [magic number](https://en.wikipedia.or @param stream - A Node.js Readable stream or Web API Readable Stream representing file data. The Web Readable stream **must be a byte stream**. @returns The detected file type, or `undefined` when there is no match. */ -export function fileTypeFromStream(stream: NodeReadableStream | WebReadableStream): Promise; +export function fileTypeFromStream(stream: WebReadableStream): Promise; /** Detect the file type from an [`ITokenizer`](https://github.com/Borewit/strtok3#tokenizer) source. @@ -425,7 +425,7 @@ if (stream2.fileType?.mime === 'image/jpeg') { } ``` */ -export function fileTypeStream(readableStream: NodeReadableStream, options?: StreamOptions): Promise; +export function fileTypeStream(readableStream: WebReadableStream, options?: StreamOptions): Promise; /** 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). @@ -516,7 +516,7 @@ export declare class FileTypeParser { /** Works the same way as {@link fileTypeFromStream}, additionally taking into account custom detectors (if any were provided to the constructor). */ - fromStream(stream: NodeReadableStream | WebReadableStream): Promise; + fromStream(stream: NodeReadableStream): Promise; /** Works the same way as {@link fileTypeFromTokenizer}, additionally taking into account custom detectors (if any were provided to the constructor). diff --git a/core.js b/core.js index 7b5fed16..33e23f18 100644 --- a/core.js +++ b/core.js @@ -2,7 +2,6 @@ * Primary entry point, Node.js specific entry point is index.js */ -import {ReadableStream as WebReadableStream} from 'node:stream/web'; import * as Token from 'token-types'; import * as strtok3 from 'strtok3/core'; import {includes, indexOf, getUintBE} from 'uint8array-extras'; @@ -97,7 +96,7 @@ export class FileTypeParser { } async fromStream(stream) { - const tokenizer = await (stream instanceof WebReadableStream ? strtok3.fromWebStream(stream) : strtok3.fromStream(stream)); + const tokenizer = await strtok3.fromWebStream(stream); try { return await this.fromTokenizer(tokenizer); } finally { diff --git a/index.d.ts b/index.d.ts index 799eaa65..98da1cf4 100644 --- a/index.d.ts +++ b/index.d.ts @@ -2,7 +2,18 @@ * Typings for Node.js specific entry point */ +import type {Readable as NodeReadableStream} from 'node:stream'; +import type {ReadableStream as WebReadableStream} from 'node:stream/web'; import type {FileTypeResult} from './core.js'; +import {FileTypeParser} from './core.js'; + +export declare class NodeFileTypeParser extends FileTypeParser { + /** + * + * @param stream Node.js Stream readable or Web API StreamReadable + */ + fromStream(stream: WebReadableStream | NodeReadableStream): Promise; +} /** * Detect the file type of a file path. @@ -14,4 +25,6 @@ import type {FileTypeResult} from './core.js'; */ export function fileTypeFromFile(path: string): Promise; +export function fileTypeFromStream(stream: WebReadableStream | NodeReadableStream): Promise; + export * from './core.js'; diff --git a/index.js b/index.js index 34f8a6b1..4d582101 100644 --- a/index.js +++ b/index.js @@ -2,9 +2,21 @@ * Node.js specific entry point */ +import {ReadableStream as WebReadableStream} from 'node:stream/web'; import * as strtok3 from 'strtok3'; import {FileTypeParser} from './core.js'; +export class NodeFileTypeParser extends FileTypeParser { + async fromStream(stream) { + const tokenizer = await (stream instanceof WebReadableStream ? strtok3.fromWebStream(stream) : strtok3.fromStream(stream)); + try { + return super.fromTokenizer(tokenizer); + } finally { + await tokenizer.close(); + } + } +} + export async function fileTypeFromFile(path, fileTypeOptions) { const tokenizer = await strtok3.fromFile(path); try { @@ -15,4 +27,8 @@ export async function fileTypeFromFile(path, fileTypeOptions) { } } -export * from './core.js'; +export async function fileTypeFromStream(stream, fileTypeOptions) { + return (new NodeFileTypeParser(fileTypeOptions)).fromStream(stream); +} + +export {fileTypeFromBuffer, fileTypeFromBlob, fileTypeStream, FileTypeParser, supportedMimeTypes, supportedExtensions} from './core.js'; diff --git a/index.test-d.ts b/index.test-d.ts index 4518db65..ff9b84e4 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -1,4 +1,5 @@ import {createReadStream} from 'node:fs'; +import {ReadableStream as WebReadableStream} from 'node:stream/web'; import {expectType} from 'tsd'; import { type FileTypeResult as FileTypeResultBrowser, @@ -54,8 +55,8 @@ expectType>(supportedExtensions); expectType>(supportedMimeTypes); -const readableStream = createReadStream('file.png'); -const streamWithFileType = fileTypeStream(readableStream); +const webStream = new WebReadableStream(); +const streamWithFileType = fileTypeStream(webStream); expectType>(streamWithFileType); (async () => { const {fileType} = await streamWithFileType; @@ -63,4 +64,4 @@ expectType>(streamWithFileType); })(); // Browser -expectType>(fileTypeFromBlob(new Blob())); +expectType>(fileTypeFromBlob(new Blob([]))); diff --git a/test.js b/test.js index 3fb92a10..fab062a7 100644 --- a/test.js +++ b/test.js @@ -10,13 +10,13 @@ import * as strtok3 from 'strtok3/core'; import {areUint8ArraysEqual} from 'uint8array-extras'; import { fileTypeFromBuffer, - fileTypeFromStream, + fileTypeFromStream as fileTypeNodeFromStream, fileTypeFromFile, fileTypeFromBlob, - FileTypeParser, fileTypeStream, supportedExtensions, supportedMimeTypes, + NodeFileTypeParser, } from './index.js'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); @@ -323,10 +323,10 @@ async function testFalsePositive(t, ext, name) { t.is(await fileTypeFromBuffer(chunk.buffer), undefined); } -async function testFileFromStream(t, ext, name) { +async function testFileNodeFromStream(t, ext, name) { const filename = `${(name ?? 'fixture')}.${ext}`; const file = path.join(__dirname, 'fixture', filename); - const fileType = await fileTypeFromStream(fs.createReadStream(file)); + const fileType = await fileTypeNodeFromStream(fs.createReadStream(file)); t.truthy(fileType, `identify ${filename}`); t.is(fileType.ext, ext, 'fileType.ext'); @@ -383,7 +383,7 @@ for (const type of types) { _test(`${name}.${type} ${i++} .fileTypeFromBlob() method - same fileType`, testFromBlob, type, name); } - _test(`${name}.${type} ${i++} .fileTypeFromStream() method - same fileType`, testFileFromStream, type, name); + _test(`${name}.${type} ${i++} .fileTypeFromStream() Node.js method - same fileType`, testFileNodeFromStream, type, name); test(`${name}.${type} ${i++} .fileTypeStream() - identical streams`, testStream, type, name); } } else { @@ -392,7 +392,7 @@ for (const type of types) { _test(`${type} ${i++} .fileTypeFromFile()`, testFromFile, type); _test(`${type} ${i++} .fileTypeFromBuffer()`, testFromBuffer, type); - _test(`${type} ${i++} .fileTypeFromStream()`, testFileFromStream, type); + _test(`${type} ${i++} .fileTypeFromStream() Node.js`, testFileNodeFromStream, type); test(`${type} ${i++} .fileTypeStream() - identical streams`, testStream, type); } @@ -647,7 +647,7 @@ test('odd file sizes', async t => { for (const size of oddFileSizes) { const buffer = new Uint8Array(size); const stream = new BufferedStream(buffer); - await t.notThrowsAsync(fileTypeFromStream(stream), `fromStream: File size: ${size} bytes`); + await t.notThrowsAsync(fileTypeNodeFromStream(stream), `fromStream: File size: ${size} bytes`); } }); @@ -713,7 +713,7 @@ if (nodeMajorVersion >= nodeVersionSupportingByeBlobStream) { const blob = new Blob([header]); const customDetectors = [unicornDetector]; - const parser = new FileTypeParser({customDetectors}); + const parser = new NodeFileTypeParser({customDetectors}); const result = await parser.fromBlob(blob); t.deepEqual(result, {ext: 'unicorn', mime: 'application/unicorn'}); @@ -725,7 +725,7 @@ if (nodeMajorVersion >= nodeVersionSupportingByeBlobStream) { const blob = new Blob([chunk]); const customDetectors = [unicornDetector]; - const parser = new FileTypeParser({customDetectors}); + const parser = new NodeFileTypeParser({customDetectors}); const result = await parser.fromBlob(blob); t.deepEqual(result, {ext: 'png', mime: 'image/png'}); @@ -737,7 +737,7 @@ if (nodeMajorVersion >= nodeVersionSupportingByeBlobStream) { const blob = new Blob([chunk]); const customDetectors = [mockPngDetector]; - const parser = new FileTypeParser({customDetectors}); + const parser = new NodeFileTypeParser({customDetectors}); const result = await parser.fromBlob(blob); t.deepEqual(result, {ext: 'mockPng', mime: 'image/mockPng'}); @@ -749,7 +749,7 @@ test('fileTypeFromBuffer should detect custom file type "unicorn" using custom d const uint8ArrayContent = new TextEncoder().encode(header); const customDetectors = [unicornDetector]; - const parser = new FileTypeParser({customDetectors}); + const parser = new NodeFileTypeParser({customDetectors}); const result = await parser.fromBuffer(uint8ArrayContent); t.deepEqual(result, {ext: 'unicorn', mime: 'application/unicorn'}); @@ -760,7 +760,7 @@ test('fileTypeFromBuffer should keep detecting default file types when no custom const uint8ArrayContent = fs.readFileSync(file); const customDetectors = [unicornDetector]; - const parser = new FileTypeParser({customDetectors}); + const parser = new NodeFileTypeParser({customDetectors}); const result = await parser.fromBuffer(uint8ArrayContent); t.deepEqual(result, {ext: 'png', mime: 'image/png'}); @@ -771,7 +771,7 @@ test('fileTypeFromBuffer should allow overriding default file type detectors', a const uint8ArrayContent = fs.readFileSync(file); const customDetectors = [mockPngDetector]; - const parser = new FileTypeParser({customDetectors}); + const parser = new NodeFileTypeParser({customDetectors}); const result = await parser.fromBuffer(uint8ArrayContent); t.deepEqual(result, {ext: 'mockPng', mime: 'image/mockPng'}); @@ -786,7 +786,7 @@ test('fileTypeFromStream should detect custom file type "unicorn" using custom d const readableStream = new CustomReadableStream(); const customDetectors = [unicornDetector]; - const parser = new FileTypeParser({customDetectors}); + const parser = new NodeFileTypeParser({customDetectors}); const result = await parser.fromStream(readableStream); t.deepEqual(result, {ext: 'unicorn', mime: 'application/unicorn'}); @@ -797,7 +797,7 @@ test('fileTypeFromStream should keep detecting default file types when no custom const readableStream = fs.createReadStream(file); const customDetectors = [unicornDetector]; - const parser = new FileTypeParser({customDetectors}); + const parser = new NodeFileTypeParser({customDetectors}); const result = await parser.fromStream(readableStream); t.deepEqual(result, {ext: 'png', mime: 'image/png'}); @@ -808,7 +808,7 @@ test('fileTypeFromStream should allow overriding default file type detectors', a const readableStream = fs.createReadStream(file); const customDetectors = [mockPngDetector]; - const parser = new FileTypeParser({customDetectors}); + const parser = new NodeFileTypeParser({customDetectors}); const result = await parser.fromStream(readableStream); t.deepEqual(result, {ext: 'mockPng', mime: 'image/mockPng'}); @@ -847,7 +847,7 @@ test('fileTypeFromTokenizer should return undefined when a custom detector chang // Include the unicormDetector here to verify it's not used after the tokenizer.position changed const customDetectors = [tokenizerPositionChanger, unicornDetector]; - const parser = new FileTypeParser({customDetectors}); + const parser = new NodeFileTypeParser({customDetectors}); const result = await parser.fromTokenizer(strtok3.fromBuffer(uint8ArrayContent)); t.is(result, undefined); From 98f2abd5d3d3ac8ef547d095e1fab78344b34c35 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Sat, 6 Jul 2024 17:28:46 +0200 Subject: [PATCH 03/13] Update core.d.ts --- core.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core.d.ts b/core.d.ts index 7c2a06c6..483419a2 100644 --- a/core.d.ts +++ b/core.d.ts @@ -1,6 +1,6 @@ /** - * Typings for primary entry point, Node.js specific typings can be found in index.d.ts - */ +Typings for primary entry point, Node.js specific typings can be found in index.d.ts +*/ import type {ReadableStream as WebReadableStream} from 'node:stream/web'; import type {Readable as NodeReadableStream} from 'node:stream'; From 5e5017d403426383aa6284d0bf278e244937f038 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Sat, 6 Jul 2024 17:29:46 +0200 Subject: [PATCH 04/13] Update core.js --- core.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core.js b/core.js index 33e23f18..e124cef7 100644 --- a/core.js +++ b/core.js @@ -1,6 +1,6 @@ /** - * Primary entry point, Node.js specific entry point is index.js - */ +Primary entry point, Node.js specific entry point is index.js +*/ import * as Token from 'token-types'; import * as strtok3 from 'strtok3/core'; From 8e083cc4170e31c0d993e0563b9ede3d3d4a8679 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Sat, 6 Jul 2024 17:30:50 +0200 Subject: [PATCH 05/13] Update index.js --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 4d582101..f35265bf 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,6 @@ /** - * Node.js specific entry point - */ +Node.js specific entry point. +*/ import {ReadableStream as WebReadableStream} from 'node:stream/web'; import * as strtok3 from 'strtok3'; From 3caa304028f3c60cddf13f96f0bf92e900b51cbf Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Sat, 6 Jul 2024 17:30:56 +0200 Subject: [PATCH 06/13] Update index.d.ts --- index.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.d.ts b/index.d.ts index 98da1cf4..4c7f3261 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,6 +1,6 @@ /** - * Typings for Node.js specific entry point - */ +Typings for Node.js specific entry point. +*/ import type {Readable as NodeReadableStream} from 'node:stream'; import type {ReadableStream as WebReadableStream} from 'node:stream/web'; From 259a97dc002532fbcf8012312d0d78bcf2b8a183 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Sat, 6 Jul 2024 17:31:27 +0200 Subject: [PATCH 07/13] Update index.d.ts --- index.d.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/index.d.ts b/index.d.ts index 4c7f3261..91c8aa54 100644 --- a/index.d.ts +++ b/index.d.ts @@ -9,20 +9,19 @@ import {FileTypeParser} from './core.js'; export declare class NodeFileTypeParser extends FileTypeParser { /** - * - * @param stream Node.js Stream readable or Web API StreamReadable - */ + @param stream Node.js Stream readable or Web API StreamReadable + */ fromStream(stream: WebReadableStream | NodeReadableStream): Promise; } /** - * Detect the file type of a file path. - * - * 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. - * - * @param path - * @returns The detected file type and MIME type or `undefined` when there is no match. - */ +Detect the file type of a file path. + +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. + +@param path +@returns The detected file type and MIME type or `undefined` when there is no match. +*/ export function fileTypeFromFile(path: string): Promise; export function fileTypeFromStream(stream: WebReadableStream | NodeReadableStream): Promise; From fc746a20a15b4d2468f76307215ca87d3a49a4f2 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Sat, 6 Jul 2024 17:34:12 +0200 Subject: [PATCH 08/13] Update index.d.ts --- index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index 91c8aa54..75223f77 100644 --- a/index.d.ts +++ b/index.d.ts @@ -9,7 +9,7 @@ import {FileTypeParser} from './core.js'; export declare class NodeFileTypeParser extends FileTypeParser { /** - @param stream Node.js Stream readable or Web API StreamReadable + @param stream - Node.js `stream.Readable` or Web API `ReadableStream`. */ fromStream(stream: WebReadableStream | NodeReadableStream): Promise; } From d5d55eff88eb8ece1742b43c0deda0a830330f0b Mon Sep 17 00:00:00 2001 From: Borewit Date: Sun, 7 Jul 2024 12:12:36 +0200 Subject: [PATCH 09/13] Move Node detection Stream functionality from primary entry point (core) to Node (only) entry point --- core.d.ts | 48 +----------------------------------------------- core.js | 43 ++----------------------------------------- index.d.ts | 42 +++++++++++++++++++++++++++++++++++++++++- index.js | 43 +++++++++++++++++++++++++++++++++++++++++-- index.test-d.ts | 4 ++-- 5 files changed, 87 insertions(+), 93 deletions(-) diff --git a/core.d.ts b/core.d.ts index 483419a2..012b2dd1 100644 --- a/core.d.ts +++ b/core.d.ts @@ -3,7 +3,6 @@ Typings for primary entry point, Node.js specific typings can be found in index. */ import type {ReadableStream as WebReadableStream} from 'node:stream/web'; -import type {Readable as NodeReadableStream} from 'node:stream'; import type {ITokenizer} from 'strtok3'; export type FileExtension = @@ -323,10 +322,6 @@ export type FileTypeResult = { readonly mime: MimeType; }; -export type ReadableStreamWithFileType = WebReadableStream & { - readonly fileType?: FileTypeResult; -}; - /** Detect the file type of a `Uint8Array`, or `ArrayBuffer`. @@ -347,7 +342,7 @@ The file type is detected by checking the [magic number](https://en.wikipedia.or @param stream - A Node.js Readable stream or Web API Readable Stream representing file data. The Web Readable stream **must be a byte stream**. @returns The detected file type, or `undefined` when there is no match. */ -export function fileTypeFromStream(stream: WebReadableStream): Promise; +export function fileTypeFromStream(stream: WebReadableStream): Promise; /** Detect the file type from an [`ITokenizer`](https://github.com/Borewit/strtok3#tokenizer) source. @@ -396,37 +391,6 @@ export type StreamOptions = { readonly sampleSize?: number; }; -/** -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()`. - -This method can be handy to put in between a stream, but it comes with a price. -Internally `stream()` builds up a buffer of `sampleSize` bytes, used as a sample, to determine the file type. -The sample size impacts the file detection resolution. -A smaller sample size will result in lower probability of the best file type detection. - -**Note:** This method is only available when using Node.js. -**Note:** Requires Node.js 14 or later. - -@param readableStream - A [readable stream](https://nodejs.org/api/stream.html#stream_class_stream_readable) containing a file to examine. -@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()`. - -@example -``` -import got from 'got'; -import {fileTypeStream} from 'file-type'; - -const url = 'https://upload.wikimedia.org/wikipedia/en/a/a9/Example.jpg'; - -const stream1 = got.stream(url); -const stream2 = await fileTypeStream(stream1, {sampleSize: 1024}); - -if (stream2.fileType?.mime === 'image/jpeg') { - // stream2 can be used to stream the JPEG image (from the very beginning of the stream) -} -``` -*/ -export function fileTypeStream(readableStream: WebReadableStream, options?: StreamOptions): Promise; - /** 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). @@ -513,11 +477,6 @@ export declare class FileTypeParser { */ fromBuffer(buffer: Uint8Array | ArrayBuffer): Promise; - /** - Works the same way as {@link fileTypeFromStream}, additionally taking into account custom detectors (if any were provided to the constructor). - */ - fromStream(stream: NodeReadableStream): Promise; - /** Works the same way as {@link fileTypeFromTokenizer}, additionally taking into account custom detectors (if any were provided to the constructor). */ @@ -527,9 +486,4 @@ export declare class FileTypeParser { Works the same way as {@link fileTypeFromBlob}, additionally taking into account custom detectors (if any were provided to the constructor). */ fromBlob(blob: Blob): Promise; - - /** - Works the same way as {@link fileTypeStream}, additionally taking into account custom detectors (if any were provided to the constructor). - */ - toDetectionStream(readableStream: NodeReadableStream, options?: StreamOptions): Promise; } diff --git a/core.js b/core.js index e124cef7..bf9ee61b 100644 --- a/core.js +++ b/core.js @@ -12,7 +12,7 @@ import { } from './util.js'; import {extensions, mimeTypes} from './supported.js'; -const minimumBytes = 4100; // A fair amount of file-types are detectable within this range. +export const reasonableDetectionSizeInBytes = 4100; // A fair amount of file-types are detectable within this range. export async function fileTypeFromStream(stream) { return new FileTypeParser().fromStream(stream); @@ -104,41 +104,6 @@ export class FileTypeParser { } } - async toDetectionStream(readableStream, options = {}) { - const {default: stream} = await import('node:stream'); - const {sampleSize = minimumBytes} = options; - - return new Promise((resolve, reject) => { - readableStream.on('error', reject); - - readableStream.once('readable', () => { - (async () => { - try { - // Set up output stream - const pass = new stream.PassThrough(); - const outputStream = stream.pipeline ? stream.pipeline(readableStream, pass, () => {}) : readableStream.pipe(pass); - - // Read the input stream and detect the filetype - const chunk = readableStream.read(sampleSize) ?? readableStream.read() ?? new Uint8Array(0); - try { - pass.fileType = await this.fromBuffer(chunk); - } catch (error) { - if (error instanceof strtok3.EndOfStreamError) { - pass.fileType = undefined; - } else { - reject(error); - } - } - - resolve(outputStream); - } catch (error) { - reject(error); - } - })(); - }); - }); - } - check(header, options) { return _check(this.buffer, header, options); } @@ -148,7 +113,7 @@ export class FileTypeParser { } async parse(tokenizer) { - this.buffer = new Uint8Array(minimumBytes); + this.buffer = new Uint8Array(reasonableDetectionSizeInBytes); // Keep reading until EOF if the file size is unknown. if (tokenizer.fileInfo.size === undefined) { @@ -1693,9 +1658,5 @@ export class FileTypeParser { } } -export async function fileTypeStream(readableStream, options = {}) { - return new FileTypeParser().toDetectionStream(readableStream, options); -} - export const supportedExtensions = new Set(extensions); export const supportedMimeTypes = new Set(mimeTypes); diff --git a/index.d.ts b/index.d.ts index 75223f77..dc0417c3 100644 --- a/index.d.ts +++ b/index.d.ts @@ -4,14 +4,23 @@ Typings for Node.js specific entry point. import type {Readable as NodeReadableStream} from 'node:stream'; import type {ReadableStream as WebReadableStream} from 'node:stream/web'; -import type {FileTypeResult} from './core.js'; +import type {FileTypeResult, StreamOptions} from './core.js'; import {FileTypeParser} from './core.js'; +export type ReadableStreamWithFileType = NodeReadableStream & { + readonly fileType?: FileTypeResult; +}; + export declare class NodeFileTypeParser extends FileTypeParser { /** @param stream - Node.js `stream.Readable` or Web API `ReadableStream`. */ fromStream(stream: WebReadableStream | NodeReadableStream): Promise; + + /** + Works the same way as {@link fileTypeStream}, additionally taking into account custom detectors (if any were provided to the constructor). + */ + toDetectionStream(readableStream: NodeReadableStream, options?: StreamOptions): Promise; } /** @@ -26,4 +35,35 @@ export function fileTypeFromFile(path: string): Promise | NodeReadableStream): Promise; +/** +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()`. + +This method can be handy to put in between a stream, but it comes with a price. +Internally `stream()` builds up a buffer of `sampleSize` bytes, used as a sample, to determine the file type. +The sample size impacts the file detection resolution. +A smaller sample size will result in lower probability of the best file type detection. + +**Note:** This method is only available when using Node.js. +**Note:** Requires Node.js 14 or later. + +@param readableStream - A [readable stream](https://nodejs.org/api/stream.html#stream_class_stream_readable) containing a file to examine. +@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()`. + +@example +``` +import got from 'got'; +import {fileTypeStream} from 'file-type'; + +const url = 'https://upload.wikimedia.org/wikipedia/en/a/a9/Example.jpg'; + +const stream1 = got.stream(url); +const stream2 = await fileTypeStream(stream1, {sampleSize: 1024}); + +if (stream2.fileType?.mime === 'image/jpeg') { + // stream2 can be used to stream the JPEG image (from the very beginning of the stream) +} +``` + */ +export function fileTypeStream(readableStream: NodeReadableStream, options?: StreamOptions): Promise; + export * from './core.js'; diff --git a/index.js b/index.js index f35265bf..47670fa5 100644 --- a/index.js +++ b/index.js @@ -4,7 +4,7 @@ Node.js specific entry point. import {ReadableStream as WebReadableStream} from 'node:stream/web'; import * as strtok3 from 'strtok3'; -import {FileTypeParser} from './core.js'; +import {FileTypeParser, reasonableDetectionSizeInBytes} from './core.js'; export class NodeFileTypeParser extends FileTypeParser { async fromStream(stream) { @@ -15,6 +15,41 @@ export class NodeFileTypeParser extends FileTypeParser { await tokenizer.close(); } } + + async toDetectionStream(readableStream, options = {}) { + const {default: stream} = await import('node:stream'); + const {sampleSize = reasonableDetectionSizeInBytes} = options; + + return new Promise((resolve, reject) => { + readableStream.on('error', reject); + + readableStream.once('readable', () => { + (async () => { + try { + // Set up output stream + const pass = new stream.PassThrough(); + const outputStream = stream.pipeline ? stream.pipeline(readableStream, pass, () => {}) : readableStream.pipe(pass); + + // Read the input stream and detect the filetype + const chunk = readableStream.read(sampleSize) ?? readableStream.read() ?? new Uint8Array(0); + try { + pass.fileType = await this.fromBuffer(chunk); + } catch (error) { + if (error instanceof strtok3.EndOfStreamError) { + pass.fileType = undefined; + } else { + reject(error); + } + } + + resolve(outputStream); + } catch (error) { + reject(error); + } + })(); + }); + }); + } } export async function fileTypeFromFile(path, fileTypeOptions) { @@ -31,4 +66,8 @@ export async function fileTypeFromStream(stream, fileTypeOptions) { return (new NodeFileTypeParser(fileTypeOptions)).fromStream(stream); } -export {fileTypeFromBuffer, fileTypeFromBlob, fileTypeStream, FileTypeParser, supportedMimeTypes, supportedExtensions} from './core.js'; +export async function fileTypeStream(readableStream, options = {}) { + return new NodeFileTypeParser().toDetectionStream(readableStream, options); +} + +export {fileTypeFromBuffer, fileTypeFromBlob, FileTypeParser, supportedMimeTypes, supportedExtensions} from './core.js'; diff --git a/index.test-d.ts b/index.test-d.ts index ff9b84e4..875f39b8 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -55,8 +55,8 @@ expectType>(supportedExtensions); expectType>(supportedMimeTypes); -const webStream = new WebReadableStream(); -const streamWithFileType = fileTypeStream(webStream); +const readableStream = createReadStream('file.png'); +const streamWithFileType = fileTypeStream(readableStream); expectType>(streamWithFileType); (async () => { const {fileType} = await streamWithFileType; From fea8f4c1ce047360cd6114fb175fd066f840dc42 Mon Sep 17 00:00:00 2001 From: Borewit Date: Sun, 7 Jul 2024 13:13:37 +0200 Subject: [PATCH 10/13] Accept either the Node.js ReadableStream or the `lib.dom.d.ts` ReadableStream --- core.d.ts | 8 +++++++- index.d.ts | 10 +++++----- index.test-d.ts | 1 - 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/core.d.ts b/core.d.ts index 012b2dd1..725f4688 100644 --- a/core.d.ts +++ b/core.d.ts @@ -5,6 +5,12 @@ Typings for primary entry point, Node.js specific typings can be found in index. import type {ReadableStream as WebReadableStream} from 'node:stream/web'; import type {ITokenizer} from 'strtok3'; +/** + * Either the Node.js ReadableStream or the `lib.dom.d.ts` ReadableStream + * Related issue: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/60377 + */ +export type AnyWebReadableStream = WebReadableStream | ReadableStream; + export type FileExtension = | 'jpg' | 'png' @@ -342,7 +348,7 @@ The file type is detected by checking the [magic number](https://en.wikipedia.or @param stream - A Node.js Readable stream or Web API Readable Stream representing file data. The Web Readable stream **must be a byte stream**. @returns The detected file type, or `undefined` when there is no match. */ -export function fileTypeFromStream(stream: WebReadableStream): Promise; +export function fileTypeFromStream(stream: AnyWebReadableStream): Promise; /** Detect the file type from an [`ITokenizer`](https://github.com/Borewit/strtok3#tokenizer) source. diff --git a/index.d.ts b/index.d.ts index dc0417c3..c10b45c3 100644 --- a/index.d.ts +++ b/index.d.ts @@ -3,8 +3,7 @@ Typings for Node.js specific entry point. */ import type {Readable as NodeReadableStream} from 'node:stream'; -import type {ReadableStream as WebReadableStream} from 'node:stream/web'; -import type {FileTypeResult, StreamOptions} from './core.js'; +import type {FileTypeResult, StreamOptions, AnyWebReadableStream} from './core.js'; import {FileTypeParser} from './core.js'; export type ReadableStreamWithFileType = NodeReadableStream & { @@ -15,7 +14,7 @@ export declare class NodeFileTypeParser extends FileTypeParser { /** @param stream - Node.js `stream.Readable` or Web API `ReadableStream`. */ - fromStream(stream: WebReadableStream | NodeReadableStream): Promise; + fromStream(stream: AnyWebReadableStream | NodeReadableStream): Promise; /** Works the same way as {@link fileTypeStream}, additionally taking into account custom detectors (if any were provided to the constructor). @@ -33,7 +32,7 @@ The file type is detected by checking the [magic number](https://en.wikipedia.or */ export function fileTypeFromFile(path: string): Promise; -export function fileTypeFromStream(stream: WebReadableStream | NodeReadableStream): Promise; +export function fileTypeFromStream(stream: AnyWebReadableStream | NodeReadableStream): Promise; /** 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()`. @@ -47,7 +46,8 @@ A smaller sample size will result in lower probability of the best file type det **Note:** Requires Node.js 14 or later. @param readableStream - A [readable stream](https://nodejs.org/api/stream.html#stream_class_stream_readable) containing a file to examine. -@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()`. +@param options - Maybe used to override the default sample-size. + @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()`. @example ``` diff --git a/index.test-d.ts b/index.test-d.ts index 875f39b8..72804263 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -1,5 +1,4 @@ import {createReadStream} from 'node:fs'; -import {ReadableStream as WebReadableStream} from 'node:stream/web'; import {expectType} from 'tsd'; import { type FileTypeResult as FileTypeResultBrowser, From 4e206691bb5351e857d0d301859719e48038f9b9 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Sun, 7 Jul 2024 23:08:31 +0200 Subject: [PATCH 11/13] Update index.d.ts --- index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index c10b45c3..b9947a64 100644 --- a/index.d.ts +++ b/index.d.ts @@ -47,7 +47,7 @@ A smaller sample size will result in lower probability of the best file type det @param readableStream - A [readable stream](https://nodejs.org/api/stream.html#stream_class_stream_readable) containing a file to examine. @param options - Maybe used to override the default sample-size. - @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()`. +@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()`. @example ``` From 47dca333ca25d2c21cdc1e022112cd0a28796713 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Sun, 7 Jul 2024 23:09:17 +0200 Subject: [PATCH 12/13] Update core.d.ts --- core.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core.d.ts b/core.d.ts index 725f4688..2c6a7180 100644 --- a/core.d.ts +++ b/core.d.ts @@ -6,9 +6,9 @@ import type {ReadableStream as WebReadableStream} from 'node:stream/web'; import type {ITokenizer} from 'strtok3'; /** - * Either the Node.js ReadableStream or the `lib.dom.d.ts` ReadableStream - * Related issue: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/60377 - */ +Either the Node.js ReadableStream or the `lib.dom.d.ts` ReadableStream. +Related issue: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/60377 +*/ export type AnyWebReadableStream = WebReadableStream | ReadableStream; export type FileExtension = From c19a4e89fae71b95612408a69f87b0f9b4d1219f Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Sun, 7 Jul 2024 23:46:12 +0200 Subject: [PATCH 13/13] Update index.d.ts --- index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index b9947a64..06405516 100644 --- a/index.d.ts +++ b/index.d.ts @@ -18,7 +18,7 @@ export declare class NodeFileTypeParser extends FileTypeParser { /** Works the same way as {@link fileTypeStream}, additionally taking into account custom detectors (if any were provided to the constructor). - */ + */ toDetectionStream(readableStream: NodeReadableStream, options?: StreamOptions): Promise; }