diff --git a/lib/interceptor/decompress.js b/lib/interceptor/decompress.js index c43565a0535..295390ad9ce 100644 --- a/lib/interceptor/decompress.js +++ b/lib/interceptor/decompress.js @@ -3,7 +3,6 @@ const { createInflate, createGunzip, createBrotliDecompress, createZstdDecompress } = require('node:zlib') const { pipeline } = require('node:stream') const DecoratorHandler = require('../handler/decorator-handler') -const { runtimeFeatures } = require('../util/runtime-features') /** @typedef {import('node:stream').Transform} Transform */ /** @typedef {import('node:stream').Transform} Controller */ @@ -17,7 +16,7 @@ const supportedEncodings = { deflate: createInflate, compress: createInflate, 'x-compress': createInflate, - ...(runtimeFeatures.has('zstd') ? { zstd: createZstdDecompress } : {}) + zstd: createZstdDecompress } const defaultSkipStatusCodes = /** @type {const} */ ([204, 304]) diff --git a/lib/util/runtime-features.js b/lib/util/runtime-features.js index 3e62dc004d3..f6fe3198e1d 100644 --- a/lib/util/runtime-features.js +++ b/lib/util/runtime-features.js @@ -6,9 +6,7 @@ const lazyLoaders = { __proto__: null, 'node:crypto': () => require('node:crypto'), - 'node:sqlite': () => require('node:sqlite'), - 'node:worker_threads': () => require('node:worker_threads'), - 'node:zlib': () => require('node:zlib') + 'node:sqlite': () => require('node:sqlite') } /** @@ -27,35 +25,9 @@ function detectRuntimeFeatureByNodeModule (moduleName) { } } -/** - * @param {NodeModuleName} moduleName - * @param {string} property - * @returns {boolean} - */ -function detectRuntimeFeatureByExportedProperty (moduleName, property) { - const module = lazyLoaders[moduleName]() - return typeof module[property] !== 'undefined' -} - -const runtimeFeaturesByExportedProperty = /** @type {const} */ (['markAsUncloneable', 'zstd']) - -/** @type {Record} */ -const exportedPropertyLookup = { - markAsUncloneable: ['node:worker_threads', 'markAsUncloneable'], - zstd: ['node:zlib', 'createZstdDecompress'] -} - -/** @typedef {typeof runtimeFeaturesByExportedProperty[number]} RuntimeFeatureByExportedProperty */ - const runtimeFeaturesAsNodeModule = /** @type {const} */ (['crypto', 'sqlite']) /** @typedef {typeof runtimeFeaturesAsNodeModule[number]} RuntimeFeatureByNodeModule */ - -const features = /** @type {const} */ ([ - ...runtimeFeaturesAsNodeModule, - ...runtimeFeaturesByExportedProperty -]) - -/** @typedef {typeof features[number]} Feature */ +/** @typedef {RuntimeFeatureByNodeModule} Feature */ /** * @param {Feature} feature @@ -64,9 +36,6 @@ const features = /** @type {const} */ ([ function detectRuntimeFeature (feature) { if (runtimeFeaturesAsNodeModule.includes(/** @type {RuntimeFeatureByNodeModule} */ (feature))) { return detectRuntimeFeatureByNodeModule(`node:${feature}`) - } else if (runtimeFeaturesByExportedProperty.includes(/** @type {RuntimeFeatureByExportedProperty} */ (feature))) { - const [moduleName, property] = exportedPropertyLookup[feature] - return detectRuntimeFeatureByExportedProperty(moduleName, property) } throw new TypeError(`unknown feature: ${feature}`) } @@ -101,7 +70,7 @@ class RuntimeFeatures { * @param {boolean} value */ set (feature, value) { - if (features.includes(feature) === false) { + if (runtimeFeaturesAsNodeModule.includes(feature) === false) { throw new TypeError(`unknown feature: ${feature}`) } this.#map.set(feature, value) diff --git a/lib/web/fetch/index.js b/lib/web/fetch/index.js index 2f19d0cb154..57df806926f 100644 --- a/lib/web/fetch/index.js +++ b/lib/web/fetch/index.js @@ -66,10 +66,6 @@ const { STATUS_CODES } = require('node:http') const { bytesMatch } = require('../subresource-integrity/subresource-integrity') const { createDeferredPromise } = require('../../util/promise') const { isomorphicEncode } = require('../infra') -const { runtimeFeatures } = require('../../util/runtime-features') - -// Node.js v23.8.0+ and v22.15.0+ supports Zstandard -const hasZstd = runtimeFeatures.has('zstd') const GET_OR_HEAD = ['GET', 'HEAD'] @@ -2251,7 +2247,7 @@ async function httpNetworkFetch ( flush: zlib.constants.BROTLI_OPERATION_FLUSH, finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH })) - } else if (coding === 'zstd' && hasZstd) { + } else if (coding === 'zstd') { decoders.push(zlib.createZstdDecompress({ flush: zlib.constants.ZSTD_e_continue, finishFlush: zlib.constants.ZSTD_e_end diff --git a/lib/web/webidl/index.js b/lib/web/webidl/index.js index 5518b495906..e5ee5cbb6e7 100644 --- a/lib/web/webidl/index.js +++ b/lib/web/webidl/index.js @@ -2,7 +2,7 @@ const assert = require('node:assert') const { types, inspect } = require('node:util') -const { runtimeFeatures } = require('../../util/runtime-features') +const { markAsUncloneable } = require('node:worker_threads') const UNDEFINED = 1 const BOOLEAN = 2 @@ -158,9 +158,7 @@ webidl.util.TypeValueToString = function (o) { } } -webidl.util.markAsUncloneable = runtimeFeatures.has('markAsUncloneable') - ? require('node:worker_threads').markAsUncloneable - : () => {} +webidl.util.markAsUncloneable = markAsUncloneable // https://webidl.spec.whatwg.org/#abstract-opdef-converttoint webidl.util.ConvertToInt = function (V, bitLength, signedness, flags) { diff --git a/test/fetch/encoding.js b/test/fetch/encoding.js index 4f6c16ed687..75bba56ef8b 100644 --- a/test/fetch/encoding.js +++ b/test/fetch/encoding.js @@ -76,18 +76,16 @@ describe('content-encoding handling', () => { t.assert.strictEqual(await response.text(), 'Hello, World!') }) - test('should decompress zstandard response', - { skip: typeof require('node:zlib').createZstdDecompress !== 'function' }, - async (t) => { - const response = await fetch(`http://localhost:${server.address().port}`, { - keepalive: false, - headers: { 'accept-encoding': 'zstd' } - }) - - t.assert.strictEqual(response.headers.get('content-encoding'), 'zstd') - t.assert.strictEqual(response.headers.get('content-type'), 'text/plain') - t.assert.strictEqual(await response.text(), 'Hello, World!') + test('should decompress zstandard response', async (t) => { + const response = await fetch(`http://localhost:${server.address().port}`, { + keepalive: false, + headers: { 'accept-encoding': 'zstd' } }) + + t.assert.strictEqual(response.headers.get('content-encoding'), 'zstd') + t.assert.strictEqual(response.headers.get('content-type'), 'text/plain') + t.assert.strictEqual(await response.text(), 'Hello, World!') + }) }) describe('content-encoding chain limit', () => { diff --git a/test/interceptors/decompress.js b/test/interceptors/decompress.js index cb42083d1e8..21a55c7bc7e 100644 --- a/test/interceptors/decompress.js +++ b/test/interceptors/decompress.js @@ -135,7 +135,7 @@ test('should decompress brotli response', async t => { await t.completed }) -test('should decompress zstd response', { skip: typeof createZstdCompress !== 'function' }, async t => { +test('should decompress zstd response', async t => { t = tspl(t, { plan: 3 }) const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => { diff --git a/test/node-platform-objects.js b/test/node-platform-objects.js index 7bc71e8c699..37e619fcde3 100644 --- a/test/node-platform-objects.js +++ b/test/node-platform-objects.js @@ -2,35 +2,32 @@ const { tspl } = require('@matteo.collina/tspl') const { test } = require('node:test') -const { markAsUncloneable } = require('node:worker_threads') const { Response, Request, FormData, Headers, ErrorEvent, MessageEvent, CloseEvent, EventSource, WebSocket } = require('..') const { CacheStorage } = require('../lib/web/cache/cachestorage') const { Cache } = require('../lib/web/cache/cache') const { kConstruct } = require('../lib/core/symbols') -test('unserializable web instances should be uncloneable if node exposes the api', (t) => { - if (markAsUncloneable !== undefined) { - t = tspl(t, { plan: 11 }) - const uncloneables = [ - { Uncloneable: Response, brand: 'Response' }, - { Uncloneable: Request, value: 'http://localhost', brand: 'Request' }, - { Uncloneable: FormData, brand: 'FormData' }, - { Uncloneable: MessageEvent, value: 'dummy event', brand: 'MessageEvent' }, - { Uncloneable: CloseEvent, value: 'dummy event', brand: 'CloseEvent' }, - { Uncloneable: ErrorEvent, value: 'dummy event', brand: 'ErrorEvent' }, - { Uncloneable: EventSource, value: 'http://localhost', brand: 'EventSource', doneCb: (entity) => entity.close() }, - { Uncloneable: Headers, brand: 'Headers' }, - { Uncloneable: WebSocket, value: 'http://localhost', brand: 'WebSocket' }, - { Uncloneable: Cache, value: kConstruct, brand: 'Cache' }, - { Uncloneable: CacheStorage, value: kConstruct, brand: 'CacheStorage' } - ] - uncloneables.forEach((platformEntity) => { - const entity = new platformEntity.Uncloneable(platformEntity.value) - t.throws(() => structuredClone(entity), - DOMException, - `Cloning ${platformEntity.brand} should throw DOMException`) +test('unserializable web instances should be uncloneable', (t) => { + t = tspl(t, { plan: 11 }) + const uncloneables = [ + { Uncloneable: Response, brand: 'Response' }, + { Uncloneable: Request, value: 'http://localhost', brand: 'Request' }, + { Uncloneable: FormData, brand: 'FormData' }, + { Uncloneable: MessageEvent, value: 'dummy event', brand: 'MessageEvent' }, + { Uncloneable: CloseEvent, value: 'dummy event', brand: 'CloseEvent' }, + { Uncloneable: ErrorEvent, value: 'dummy event', brand: 'ErrorEvent' }, + { Uncloneable: EventSource, value: 'http://localhost', brand: 'EventSource', doneCb: (entity) => entity.close() }, + { Uncloneable: Headers, brand: 'Headers' }, + { Uncloneable: WebSocket, value: 'http://localhost', brand: 'WebSocket' }, + { Uncloneable: Cache, value: kConstruct, brand: 'Cache' }, + { Uncloneable: CacheStorage, value: kConstruct, brand: 'CacheStorage' } + ] + uncloneables.forEach((platformEntity) => { + const entity = new platformEntity.Uncloneable(platformEntity.value) + t.throws(() => structuredClone(entity), + DOMException, + `Cloning ${platformEntity.brand} should throw DOMException`) - platformEntity.doneCb?.(entity) - }) - } + platformEntity.doneCb?.(entity) + }) }) diff --git a/types/webidl.d.ts b/types/webidl.d.ts index f95d9b56737..b1873b9e52d 100644 --- a/types/webidl.d.ts +++ b/types/webidl.d.ts @@ -87,7 +87,6 @@ interface WebidlUtil { /** * Mark a value as uncloneable for Node.js. - * This is only effective in some newer Node.js versions. */ markAsUncloneable (V: any): void