diff --git a/benchmarks/core/is-blob-like.mjs b/benchmarks/core/is-blob-like.mjs new file mode 100644 index 00000000000..a024ef14508 --- /dev/null +++ b/benchmarks/core/is-blob-like.mjs @@ -0,0 +1,64 @@ +import { bench, group, run } from 'mitata' +import { isBlobLike } from '../../lib/core/util.js' + +const buffer = Buffer.alloc(1) + +const blob = new Blob(['asd'], { + type: 'application/json' +}) + +const file = new File(['asd'], 'file.txt', { + type: 'text/plain' +}) + +const blobLikeStream = { + [Symbol.toStringTag]: 'Blob', + stream: () => {} +} + +const fileLikeStream = { + stream: () => {}, + [Symbol.toStringTag]: 'File' +} + +const blobLikeArrayBuffer = { + [Symbol.toStringTag]: 'Blob', + arrayBuffer: () => {} +} + +const fileLikeArrayBuffer = { + [Symbol.toStringTag]: 'File', + arrayBuffer: () => {} +} + +group('isBlobLike', () => { + bench('blob', () => { + return isBlobLike(blob) + }) + bench('file', () => { + return isBlobLike(file) + }) + bench('blobLikeStream', () => { + return isBlobLike(blobLikeStream) + }) + bench('fileLikeStream', () => { + return isBlobLike(fileLikeStream) + }) + bench('fileLikeArrayBuffer', () => { + return isBlobLike(fileLikeArrayBuffer) + }) + bench('blobLikeArrayBuffer', () => { + return isBlobLike(blobLikeArrayBuffer) + }) + bench('buffer', () => { + return isBlobLike(buffer) + }) + bench('null', () => { + return isBlobLike(null) + }) + bench('string', () => { + return isBlobLike('invalid') + }) +}) + +await run() diff --git a/lib/core/util.js b/lib/core/util.js index d4bcdff5ba9..b7d7f87a34e 100644 --- a/lib/core/util.js +++ b/lib/core/util.js @@ -22,13 +22,20 @@ function isStream (obj) { // based on https://github.com/node-fetch/fetch-blob/blob/8ab587d34080de94140b54f07168451e7d0b655e/index.js#L229-L241 (MIT License) function isBlobLike (object) { - return (Blob && object instanceof Blob) || ( - object && - typeof object === 'object' && - (typeof object.stream === 'function' || - typeof object.arrayBuffer === 'function') && - /^(Blob|File)$/.test(object[Symbol.toStringTag]) - ) + if (object === null) { + return false + } else if (object instanceof Blob) { + return true + } else if (typeof object !== 'object') { + return false + } else { + const sTag = object[Symbol.toStringTag] + + return (sTag === 'Blob' || sTag === 'File') && ( + ('stream' in object && typeof object.stream === 'function') || + ('arrayBuffer' in object && typeof object.arrayBuffer === 'function') + ) + } } function buildURL (url, queryParams) { diff --git a/test/util.js b/test/util.js new file mode 100644 index 00000000000..b8b661bca40 --- /dev/null +++ b/test/util.js @@ -0,0 +1,67 @@ +'use strict' + +const { strictEqual } = require('node:assert') +const { test, describe } = require('node:test') +const { isBlobLike } = require('../lib/core/util') +const { Blob, File } = require('node:buffer') + +describe('isBlobLike', () => { + test('buffer', () => { + const buffer = Buffer.alloc(1) + strictEqual(isBlobLike(buffer), false) + }) + + test('blob', { skip: !Blob }, () => { + const blob = new Blob(['asd'], { + type: 'application/json' + }) + strictEqual(isBlobLike(blob), true) + }) + + test('file', { skip: !File }, () => { + const file = new File(['asd'], 'file.txt', { + type: 'text/plain' + }) + strictEqual(isBlobLike(file), true) + }) + + test('blobLikeStream', () => { + const blobLikeStream = { + [Symbol.toStringTag]: 'Blob', + stream: () => { } + } + strictEqual(isBlobLike(blobLikeStream), true) + }) + + test('fileLikeStream', () => { + const fileLikeStream = { + stream: () => { }, + [Symbol.toStringTag]: 'File' + } + strictEqual(isBlobLike(fileLikeStream), true) + }) + + test('fileLikeArrayBuffer', () => { + const blobLikeArrayBuffer = { + [Symbol.toStringTag]: 'Blob', + arrayBuffer: () => { } + } + strictEqual(isBlobLike(blobLikeArrayBuffer), true) + }) + + test('blobLikeArrayBuffer', () => { + const fileLikeArrayBuffer = { + [Symbol.toStringTag]: 'File', + arrayBuffer: () => { } + } + strictEqual(isBlobLike(fileLikeArrayBuffer), true) + }) + + test('string', () => { + strictEqual(isBlobLike('Blob'), false) + }) + + test('null', () => { + strictEqual(isBlobLike(null), false) + }) +})