diff --git a/doc/api/deprecations.md b/doc/api/deprecations.md index d60b6bd451fa..ad7e5b0c8535 100644 --- a/doc/api/deprecations.md +++ b/doc/api/deprecations.md @@ -4126,6 +4126,9 @@ and [`crypto.setEngine()`][] all depend on this functionality from OpenSSL. -Type: Runtime +Type: End-of-Life -Instantiating classes without the `new` qualifier exported by the `node:zlib` module is deprecated. -It is recommended to use the `new` qualifier instead. This applies to all Zlib classes, such as `Deflate`, -`DeflateRaw`, `Gunzip`, `Inflate`, `InflateRaw`, `Unzip`, and `Zlib`. +Instantiating classes without the `new` qualifier exported by the `node:zlib` module is no longer +supported. The `new` qualifier must be used instead. This applies to all Zlib classes, such as +`Deflate`, `DeflateRaw`, `Gunzip`, `Inflate`, `InflateRaw`, `Unzip`, `BrotliCompress`, +`BrotliDecompress`, `ZstdCompress`, and `ZstdDecompress`. ### DEP0185: Instantiating `node:repl` classes without `new` diff --git a/lib/zlib.js b/lib/zlib.js index e6c5c420d551..a113fa33bbe5 100644 --- a/lib/zlib.js +++ b/lib/zlib.js @@ -30,7 +30,6 @@ const { ObjectEntries, ObjectFreeze, ObjectKeys, - ObjectSetPrototypeOf, Symbol, Uint32Array, Uint8Array, @@ -50,7 +49,6 @@ const { const { Transform, finished } = require('stream'); const { assignFunctionName, - deprecateInstantiation, } = require('internal/util'); const { isArrayBufferView, @@ -206,124 +204,6 @@ const FLUSH_BOUND_IDX_NORMAL = 0; const FLUSH_BOUND_IDX_BROTLI = 1; const FLUSH_BOUND_IDX_ZSTD = 2; -/** - * The base class for all Zlib-style streams. - * @class - */ -function ZlibBase(opts, mode, handle, { flush, finishFlush, fullFlush }) { - let chunkSize = Z_DEFAULT_CHUNK; - let maxOutputLength = kMaxLength; - // The ZlibBase class is not exported to user land, the mode should only be - // passed in by us. - assert(typeof mode === 'number'); - assert(mode >= DEFLATE && mode <= ZSTD_DECOMPRESS); - - let flushBoundIdx; - if (mode === BROTLI_ENCODE || mode === BROTLI_DECODE) { - flushBoundIdx = FLUSH_BOUND_IDX_BROTLI; - } else if (mode === ZSTD_COMPRESS || mode === ZSTD_DECOMPRESS) { - flushBoundIdx = FLUSH_BOUND_IDX_ZSTD; - } else { - flushBoundIdx = FLUSH_BOUND_IDX_NORMAL; - } - - if (opts) { - chunkSize = opts.chunkSize; - if (!validateFiniteNumber(chunkSize, 'options.chunkSize')) { - chunkSize = Z_DEFAULT_CHUNK; - } else if (chunkSize < Z_MIN_CHUNK) { - throw new ERR_OUT_OF_RANGE('options.chunkSize', - `>= ${Z_MIN_CHUNK}`, chunkSize); - } - - flush = checkRangesOrGetDefault( - opts.flush, 'options.flush', - FLUSH_BOUND[flushBoundIdx][0], FLUSH_BOUND[flushBoundIdx][1], flush); - - finishFlush = checkRangesOrGetDefault( - opts.finishFlush, 'options.finishFlush', - FLUSH_BOUND[flushBoundIdx][0], FLUSH_BOUND[flushBoundIdx][1], - finishFlush); - - maxOutputLength = checkRangesOrGetDefault( - opts.maxOutputLength, 'options.maxOutputLength', - 1, kMaxLength, kMaxLength); - - if (opts.rejectGarbageAfterEnd !== undefined) { - validateBoolean( - opts.rejectGarbageAfterEnd, - 'options.rejectGarbageAfterEnd', - ); - } - - if (opts.encoding || opts.objectMode || opts.writableObjectMode) { - opts = { ...opts }; - opts.encoding = null; - opts.objectMode = false; - opts.writableObjectMode = false; - } - } - - Transform.call(this, { autoDestroy: true, ...opts }); - this[kError] = null; - this.bytesWritten = 0; - this._handle = handle; - handle[owner_symbol] = this; - // Used by processCallback() and zlibOnError() - handle.onerror = zlibOnError; - this._outBuffer = Buffer.allocUnsafe(chunkSize); - this._outOffset = 0; - - this._chunkSize = chunkSize; - this._defaultFlushFlag = flush; - this._finishFlushFlag = finishFlush; - this._defaultFullFlushFlag = fullFlush; - this._flushBoundIdx = flushBoundIdx; - this._info = opts?.info; - this._maxOutputLength = maxOutputLength; - - this._rejectGarbageAfterEnd = opts?.rejectGarbageAfterEnd === true; -} -ObjectSetPrototypeOf(ZlibBase.prototype, Transform.prototype); -ObjectSetPrototypeOf(ZlibBase, Transform); - -ObjectDefineProperty(ZlibBase.prototype, '_closed', { - __proto__: null, - configurable: true, - enumerable: true, - get() { - return !this._handle; - }, -}); - -/** - * @this {ZlibBase} - * @returns {void} - */ -ZlibBase.prototype.reset = function() { - assert(this._handle, 'zlib binding closed'); - return this._handle.reset(); -}; - -/** - * This is the _flush function called by the transform class, - * internally, when the last chunk has been written. - * @returns {void} - * @this {ZlibBase} - */ -ZlibBase.prototype._flush = function(callback) { - this._transform(new FastBuffer(), '', callback); -}; - -/** - * Force Transform compat behavior. - * @returns {void} - * @this {ZlibBase} - */ -ZlibBase.prototype._final = function(callback) { - callback(); -}; - // If a flush is scheduled while another flush is still pending, a way to figure // out which one is the "stronger" flush is needed. // This is currently only used to figure out which flush flag to use for the @@ -354,64 +234,169 @@ const kFlushBuffers = []; } } -ZlibBase.prototype.flush = function(kind, callback) { - if (typeof kind === 'function' || (kind === undefined && !callback)) { - callback = kind; - kind = this._defaultFullFlushFlag; +/** + * The base class for all Zlib-style streams. + */ +class ZlibBase extends Transform { + constructor(opts, mode, handle, { flush, finishFlush, fullFlush }) { + let chunkSize = Z_DEFAULT_CHUNK; + let maxOutputLength = kMaxLength; + // The ZlibBase class is not exported to user land, the mode should only be + // passed in by us. + assert(typeof mode === 'number'); + assert(mode >= DEFLATE && mode <= ZSTD_DECOMPRESS); + + let flushBoundIdx; + if (mode === BROTLI_ENCODE || mode === BROTLI_DECODE) { + flushBoundIdx = FLUSH_BOUND_IDX_BROTLI; + } else if (mode === ZSTD_COMPRESS || mode === ZSTD_DECOMPRESS) { + flushBoundIdx = FLUSH_BOUND_IDX_ZSTD; + } else { + flushBoundIdx = FLUSH_BOUND_IDX_NORMAL; + } + + if (opts) { + chunkSize = opts.chunkSize; + if (!validateFiniteNumber(chunkSize, 'options.chunkSize')) { + chunkSize = Z_DEFAULT_CHUNK; + } else if (chunkSize < Z_MIN_CHUNK) { + throw new ERR_OUT_OF_RANGE('options.chunkSize', + `>= ${Z_MIN_CHUNK}`, chunkSize); + } + + flush = checkRangesOrGetDefault( + opts.flush, 'options.flush', + FLUSH_BOUND[flushBoundIdx][0], FLUSH_BOUND[flushBoundIdx][1], flush); + + finishFlush = checkRangesOrGetDefault( + opts.finishFlush, 'options.finishFlush', + FLUSH_BOUND[flushBoundIdx][0], FLUSH_BOUND[flushBoundIdx][1], + finishFlush); + + maxOutputLength = checkRangesOrGetDefault( + opts.maxOutputLength, 'options.maxOutputLength', + 1, kMaxLength, kMaxLength); + + if (opts.rejectGarbageAfterEnd !== undefined) { + validateBoolean( + opts.rejectGarbageAfterEnd, + 'options.rejectGarbageAfterEnd', + ); + } + + if (opts.encoding || opts.objectMode || opts.writableObjectMode) { + opts = { ...opts }; + opts.encoding = null; + opts.objectMode = false; + opts.writableObjectMode = false; + } + } + + super({ autoDestroy: true, ...opts }); + this[kError] = null; + this.bytesWritten = 0; + this._handle = handle; + handle[owner_symbol] = this; + // Used by processCallback() and zlibOnError() + handle.onerror = zlibOnError; + this._outBuffer = Buffer.allocUnsafe(chunkSize); + this._outOffset = 0; + + this._chunkSize = chunkSize; + this._defaultFlushFlag = flush; + this._finishFlushFlag = finishFlush; + this._defaultFullFlushFlag = fullFlush; + this._flushBoundIdx = flushBoundIdx; + this._info = opts?.info; + this._maxOutputLength = maxOutputLength; + + this._rejectGarbageAfterEnd = opts?.rejectGarbageAfterEnd === true; + } + + get _closed() { + return !this._handle; } - kind = checkRangesOrGetDefault( - kind, 'kind', - FLUSH_BOUND[this._flushBoundIdx][0], FLUSH_BOUND[this._flushBoundIdx][1], - this._defaultFullFlushFlag); + reset() { + assert(this._handle, 'zlib binding closed'); + return this._handle.reset(); + } - if (this.writableFinished) { - if (callback) - process.nextTick(callback); - } else if (this.writableEnded) { - if (callback) - this.once('end', callback); - } else { - this.write(kFlushBuffers[kind], '', callback); + /** + * This is the _flush function called by the transform class, + * internally, when the last chunk has been written. + * @returns {void} + */ + _flush(callback) { + this._transform(new FastBuffer(), '', callback); } -}; -/** - * @this {import('stream').Transform} - * @param {(err?: Error) => any} [callback] - */ -ZlibBase.prototype.close = function(callback) { - if (callback) finished(this, callback); - this.destroy(); -}; + /** + * Force Transform compat behavior. + * @returns {void} + */ + _final(callback) { + callback(); + } -ZlibBase.prototype._destroy = function(err, callback) { - _close(this); - callback(err); -}; + flush(kind, callback) { + if (typeof kind === 'function' || (kind === undefined && !callback)) { + callback = kind; + kind = this._defaultFullFlushFlag; + } + + kind = checkRangesOrGetDefault( + kind, 'kind', + FLUSH_BOUND[this._flushBoundIdx][0], FLUSH_BOUND[this._flushBoundIdx][1], + this._defaultFullFlushFlag); + + if (this.writableFinished) { + if (callback) + process.nextTick(callback); + } else if (this.writableEnded) { + if (callback) + this.once('end', callback); + } else { + this.write(kFlushBuffers[kind], '', callback); + } + } -ZlibBase.prototype._transform = function(chunk, encoding, cb) { - let flushFlag = this._defaultFlushFlag; - // We use a 'fake' zero-length chunk to carry information about flushes from - // the public API to the actual stream implementation. - if (typeof chunk[kFlushFlag] === 'number') { - flushFlag = chunk[kFlushFlag]; + /** + * @param {(err?: Error) => any} [callback] + */ + close(callback) { + if (callback) finished(this, callback); + this.destroy(); } - // For the last chunk, also apply `_finishFlushFlag`. - if (this.writableEnded && this.writableLength === chunk.byteLength) { - flushFlag = maxFlush(flushFlag, this._finishFlushFlag); + _destroy(err, callback) { + _close(this); + callback(err); } - processChunk(this, chunk, flushFlag, cb); -}; -ZlibBase.prototype._processChunk = function(chunk, flushFlag, cb) { - // _processChunk() is left for backwards compatibility - if (typeof cb === 'function') + _transform(chunk, encoding, cb) { + let flushFlag = this._defaultFlushFlag; + // We use a 'fake' zero-length chunk to carry information about flushes from + // the public API to the actual stream implementation. + if (typeof chunk[kFlushFlag] === 'number') { + flushFlag = chunk[kFlushFlag]; + } + + // For the last chunk, also apply `_finishFlushFlag`. + if (this.writableEnded && this.writableLength === chunk.byteLength) { + flushFlag = maxFlush(flushFlag, this._finishFlushFlag); + } processChunk(this, chunk, flushFlag, cb); - else - return processChunkSync(this, chunk, flushFlag); -}; + } + + _processChunk(chunk, flushFlag, cb) { + // _processChunk() is left for backwards compatibility + if (typeof cb === 'function') + processChunk(this, chunk, flushFlag, cb); + else + return processChunkSync(this, chunk, flushFlag); + } +} function processChunkSync(self, chunk, flushFlag) { let availInBefore = chunk.byteLength; @@ -636,80 +621,6 @@ const zlibDefaultOpts = { finishFlush: Z_FINISH, fullFlush: Z_FULL_FLUSH, }; -// Base class for all streams actually backed by zlib and using zlib-specific -// parameters. -function Zlib(opts, mode) { - let windowBits = Z_DEFAULT_WINDOWBITS; - let level = Z_DEFAULT_COMPRESSION; - let memLevel = Z_DEFAULT_MEMLEVEL; - let strategy = Z_DEFAULT_STRATEGY; - let dictionary; - - if (opts) { - // windowBits is special. On the compression side, 0 is an invalid value. - // But on the decompression side, a value of 0 for windowBits tells zlib - // to use the window size in the zlib header of the compressed stream. - if ((opts.windowBits == null || opts.windowBits === 0) && - (mode === INFLATE || - mode === GUNZIP || - mode === UNZIP)) { - windowBits = 0; - } else { - // `{ windowBits: 8 }` is valid for deflate but not gzip. - const min = Z_MIN_WINDOWBITS + (mode === GZIP ? 1 : 0); - windowBits = checkRangesOrGetDefault( - opts.windowBits, 'options.windowBits', - min, Z_MAX_WINDOWBITS, Z_DEFAULT_WINDOWBITS); - } - - level = checkRangesOrGetDefault( - opts.level, 'options.level', - Z_MIN_LEVEL, Z_MAX_LEVEL, Z_DEFAULT_COMPRESSION); - - memLevel = checkRangesOrGetDefault( - opts.memLevel, 'options.memLevel', - Z_MIN_MEMLEVEL, Z_MAX_MEMLEVEL, Z_DEFAULT_MEMLEVEL); - - strategy = checkRangesOrGetDefault( - opts.strategy, 'options.strategy', - Z_DEFAULT_STRATEGY, Z_FIXED, Z_DEFAULT_STRATEGY); - - dictionary = opts.dictionary; - if (dictionary !== undefined && !isArrayBufferView(dictionary)) { - if (isAnyArrayBuffer(dictionary)) { - dictionary = Buffer.from(dictionary); - } else { - throw new ERR_INVALID_ARG_TYPE( - 'options.dictionary', - ['Buffer', 'TypedArray', 'DataView', 'ArrayBuffer'], - dictionary, - ); - } - } - } - - const handle = new binding.Zlib(mode); - // Ideally, we could let ZlibBase() set up _writeState. I haven't been able - // to come up with a good solution that doesn't break our internal API, - // and with it all supported npm versions at the time of writing. - this._writeState = new Uint32Array(2); - handle.init(windowBits, - level, - memLevel, - strategy, - this._writeState, - processCallback, - dictionary, - opts?.rejectGarbageAfterEnd === true); - - ZlibBase.call(this, opts, mode, handle, zlibDefaultOpts); - - this._level = level; - this._strategy = strategy; - this._mode = mode; -} -ObjectSetPrototypeOf(Zlib.prototype, ZlibBase.prototype); -ObjectSetPrototypeOf(Zlib, ZlibBase); // This callback is used by `.params()` to wait until a full flush happened // before adjusting the parameters. In particular, the call to the native @@ -725,85 +636,140 @@ function paramsAfterFlushCallback(level, strategy, callback) { } } -Zlib.prototype.params = function params(level, strategy, callback) { - checkRangesOrGetDefault(level, 'level', Z_MIN_LEVEL, Z_MAX_LEVEL); - checkRangesOrGetDefault(strategy, 'strategy', Z_DEFAULT_STRATEGY, Z_FIXED); +// Base class for all streams actually backed by zlib and using zlib-specific +// parameters. +class Zlib extends ZlibBase { + constructor(opts, mode) { + let windowBits = Z_DEFAULT_WINDOWBITS; + let level = Z_DEFAULT_COMPRESSION; + let memLevel = Z_DEFAULT_MEMLEVEL; + let strategy = Z_DEFAULT_STRATEGY; + let dictionary; + + if (opts) { + // windowBits is special. On the compression side, 0 is an invalid value. + // But on the decompression side, a value of 0 for windowBits tells zlib + // to use the window size in the zlib header of the compressed stream. + if ((opts.windowBits == null || opts.windowBits === 0) && + (mode === INFLATE || + mode === GUNZIP || + mode === UNZIP)) { + windowBits = 0; + } else { + // `{ windowBits: 8 }` is valid for deflate but not gzip. + const min = Z_MIN_WINDOWBITS + (mode === GZIP ? 1 : 0); + windowBits = checkRangesOrGetDefault( + opts.windowBits, 'options.windowBits', + min, Z_MAX_WINDOWBITS, Z_DEFAULT_WINDOWBITS); + } - if (this._level !== level || this._strategy !== strategy) { - this.flush( - Z_SYNC_FLUSH, - paramsAfterFlushCallback.bind(this, level, strategy, callback), - ); - } else { - process.nextTick(callback); + level = checkRangesOrGetDefault( + opts.level, 'options.level', + Z_MIN_LEVEL, Z_MAX_LEVEL, Z_DEFAULT_COMPRESSION); + + memLevel = checkRangesOrGetDefault( + opts.memLevel, 'options.memLevel', + Z_MIN_MEMLEVEL, Z_MAX_MEMLEVEL, Z_DEFAULT_MEMLEVEL); + + strategy = checkRangesOrGetDefault( + opts.strategy, 'options.strategy', + Z_DEFAULT_STRATEGY, Z_FIXED, Z_DEFAULT_STRATEGY); + + dictionary = opts.dictionary; + if (dictionary !== undefined && !isArrayBufferView(dictionary)) { + if (isAnyArrayBuffer(dictionary)) { + dictionary = Buffer.from(dictionary); + } else { + throw new ERR_INVALID_ARG_TYPE( + 'options.dictionary', + ['Buffer', 'TypedArray', 'DataView', 'ArrayBuffer'], + dictionary, + ); + } + } + } + + const handle = new binding.Zlib(mode); + // Ideally, we could let ZlibBase() set up _writeState. I haven't been able + // to come up with a good solution that doesn't break our internal API, + // and with it all supported npm versions at the time of writing. + const writeState = new Uint32Array(2); + handle.init(windowBits, + level, + memLevel, + strategy, + writeState, + processCallback, + dictionary, + opts?.rejectGarbageAfterEnd === true); + + super(opts, mode, handle, zlibDefaultOpts); + + this._writeState = writeState; + this._level = level; + this._strategy = strategy; + this._mode = mode; } -}; + + params(level, strategy, callback) { + checkRangesOrGetDefault(level, 'level', Z_MIN_LEVEL, Z_MAX_LEVEL); + checkRangesOrGetDefault(strategy, 'strategy', Z_DEFAULT_STRATEGY, Z_FIXED); + + if (this._level !== level || this._strategy !== strategy) { + this.flush( + Z_SYNC_FLUSH, + paramsAfterFlushCallback.bind(this, level, strategy, callback), + ); + } else { + process.nextTick(callback); + } + } +} // generic zlib // minimal 2-byte header -function Deflate(opts) { - if (!(this instanceof Deflate)) { - return deprecateInstantiation(Deflate, 'DEP0184', opts); +class Deflate extends Zlib { + constructor(opts) { + super(opts, DEFLATE); } - Zlib.call(this, opts, DEFLATE); } -ObjectSetPrototypeOf(Deflate.prototype, Zlib.prototype); -ObjectSetPrototypeOf(Deflate, Zlib); -function Inflate(opts) { - if (!(this instanceof Inflate)) { - return deprecateInstantiation(Inflate, 'DEP0184', opts); +class Inflate extends Zlib { + constructor(opts) { + super(opts, INFLATE); } - Zlib.call(this, opts, INFLATE); } -ObjectSetPrototypeOf(Inflate.prototype, Zlib.prototype); -ObjectSetPrototypeOf(Inflate, Zlib); -function Gzip(opts) { - if (!(this instanceof Gzip)) { - return deprecateInstantiation(Gzip, 'DEP0184', opts); +class Gzip extends Zlib { + constructor(opts) { + super(opts, GZIP); } - Zlib.call(this, opts, GZIP); } -ObjectSetPrototypeOf(Gzip.prototype, Zlib.prototype); -ObjectSetPrototypeOf(Gzip, Zlib); -function Gunzip(opts) { - if (!(this instanceof Gunzip)) { - return deprecateInstantiation(Gunzip, 'DEP0184', opts); +class Gunzip extends Zlib { + constructor(opts) { + super(opts, GUNZIP); } - Zlib.call(this, opts, GUNZIP); } -ObjectSetPrototypeOf(Gunzip.prototype, Zlib.prototype); -ObjectSetPrototypeOf(Gunzip, Zlib); -function DeflateRaw(opts) { - if (opts && opts.windowBits === 8) opts.windowBits = 9; - if (!(this instanceof DeflateRaw)) { - return deprecateInstantiation(DeflateRaw, 'DEP0184', opts); +class DeflateRaw extends Zlib { + constructor(opts) { + if (opts && opts.windowBits === 8) opts.windowBits = 9; + super(opts, DEFLATERAW); } - Zlib.call(this, opts, DEFLATERAW); } -ObjectSetPrototypeOf(DeflateRaw.prototype, Zlib.prototype); -ObjectSetPrototypeOf(DeflateRaw, Zlib); -function InflateRaw(opts) { - if (!(this instanceof InflateRaw)) { - return deprecateInstantiation(InflateRaw, 'DEP0184', opts); +class InflateRaw extends Zlib { + constructor(opts) { + super(opts, INFLATERAW); } - Zlib.call(this, opts, INFLATERAW); } -ObjectSetPrototypeOf(InflateRaw.prototype, Zlib.prototype); -ObjectSetPrototypeOf(InflateRaw, Zlib); -function Unzip(opts) { - if (!(this instanceof Unzip)) { - return deprecateInstantiation(Unzip, 'DEP0184', opts); +class Unzip extends Zlib { + constructor(opts) { + super(opts, UNZIP); } - Zlib.call(this, opts, UNZIP); } -ObjectSetPrototypeOf(Unzip.prototype, Zlib.prototype); -ObjectSetPrototypeOf(Unzip, Zlib); function createConvenienceMethod(ctor, sync) { if (sync) { @@ -831,73 +797,69 @@ const brotliDefaultOpts = { finishFlush: BROTLI_OPERATION_FINISH, fullFlush: BROTLI_OPERATION_FLUSH, }; -function Brotli(opts, mode) { - assert(mode === BROTLI_DECODE || mode === BROTLI_ENCODE); - - brotliInitParamsArray.fill(-1); - if (opts?.params) { - ObjectKeys(opts.params).forEach((origKey) => { - const key = +origKey; - if (NumberIsNaN(key) || key < 0 || key > kMaxBrotliParam || - (brotliInitParamsArray[key] | 0) !== -1) { - throw new ERR_BROTLI_INVALID_PARAM(origKey); - } - const value = opts.params[origKey]; - if (typeof value !== 'number' && typeof value !== 'boolean') { - throw new ERR_INVALID_ARG_TYPE('options.params[key]', - 'number', opts.params[origKey]); - } - brotliInitParamsArray[key] = value; - }); - } +class Brotli extends ZlibBase { + constructor(opts, mode) { + assert(mode === BROTLI_DECODE || mode === BROTLI_ENCODE); - let dictionary = opts?.dictionary; - if (dictionary !== undefined && !isArrayBufferView(dictionary)) { - if (isAnyArrayBuffer(dictionary)) { - dictionary = Buffer.from(dictionary); - } else { - throw new ERR_INVALID_ARG_TYPE( - 'options.dictionary', - ['Buffer', 'TypedArray', 'DataView', 'ArrayBuffer'], - dictionary, - ); + brotliInitParamsArray.fill(-1); + if (opts?.params) { + ObjectKeys(opts.params).forEach((origKey) => { + const key = +origKey; + if (NumberIsNaN(key) || key < 0 || key > kMaxBrotliParam || + (brotliInitParamsArray[key] | 0) !== -1) { + throw new ERR_BROTLI_INVALID_PARAM(origKey); + } + + const value = opts.params[origKey]; + if (typeof value !== 'number' && typeof value !== 'boolean') { + throw new ERR_INVALID_ARG_TYPE('options.params[key]', + 'number', opts.params[origKey]); + } + brotliInitParamsArray[key] = value; + }); + } + + let dictionary = opts?.dictionary; + if (dictionary !== undefined && !isArrayBufferView(dictionary)) { + if (isAnyArrayBuffer(dictionary)) { + dictionary = Buffer.from(dictionary); + } else { + throw new ERR_INVALID_ARG_TYPE( + 'options.dictionary', + ['Buffer', 'TypedArray', 'DataView', 'ArrayBuffer'], + dictionary, + ); + } } - } - const handle = mode === BROTLI_DECODE ? - new binding.BrotliDecoder(mode) : new binding.BrotliEncoder(mode); + const handle = mode === BROTLI_DECODE ? + new binding.BrotliDecoder(mode) : new binding.BrotliEncoder(mode); - this._writeState = new Uint32Array(2); - handle.init( - brotliInitParamsArray, - this._writeState, - processCallback, - dictionary, - ); + const writeState = new Uint32Array(2); + handle.init( + brotliInitParamsArray, + writeState, + processCallback, + dictionary, + ); - ZlibBase.call(this, opts, mode, handle, brotliDefaultOpts); + super(opts, mode, handle, brotliDefaultOpts); + this._writeState = writeState; + } } -ObjectSetPrototypeOf(Brotli.prototype, Zlib.prototype); -ObjectSetPrototypeOf(Brotli, Zlib); -function BrotliCompress(opts) { - if (!(this instanceof BrotliCompress)) { - return deprecateInstantiation(BrotliCompress, 'DEP0184', opts); +class BrotliCompress extends Brotli { + constructor(opts) { + super(opts, BROTLI_ENCODE); } - Brotli.call(this, opts, BROTLI_ENCODE); } -ObjectSetPrototypeOf(BrotliCompress.prototype, Brotli.prototype); -ObjectSetPrototypeOf(BrotliCompress, Brotli); -function BrotliDecompress(opts) { - if (!(this instanceof BrotliDecompress)) { - return deprecateInstantiation(BrotliDecompress, 'DEP0184', opts); +class BrotliDecompress extends Brotli { + constructor(opts) { + super(opts, BROTLI_DECODE); } - Brotli.call(this, opts, BROTLI_DECODE); } -ObjectSetPrototypeOf(BrotliDecompress.prototype, Brotli.prototype); -ObjectSetPrototypeOf(BrotliDecompress, Brotli); const zstdDefaultOpts = { diff --git a/test/parallel/test-zlib-deflate-constructors.js b/test/parallel/test-zlib-deflate-constructors.js index 492048d3d0a1..66a505c13e70 100644 --- a/test/parallel/test-zlib-deflate-constructors.js +++ b/test/parallel/test-zlib-deflate-constructors.js @@ -5,13 +5,19 @@ require('../common'); const zlib = require('zlib'); const assert = require('assert'); -// Work with and without `new` keyword -assert.ok(zlib.Deflate() instanceof zlib.Deflate); +// Require the `new` keyword (DEP0184 End-of-Life) assert.ok(new zlib.Deflate() instanceof zlib.Deflate); - -assert.ok(zlib.DeflateRaw() instanceof zlib.DeflateRaw); assert.ok(new zlib.DeflateRaw() instanceof zlib.DeflateRaw); +assert.throws(() => zlib.Deflate(), { + name: 'TypeError', + message: /Class constructor Deflate cannot be invoked without 'new'/, +}); +assert.throws(() => zlib.DeflateRaw(), { + name: 'TypeError', + message: /Class constructor DeflateRaw cannot be invoked without 'new'/, +}); + // Throws if `options.chunkSize` is invalid assert.throws( () => new zlib.Deflate({ chunkSize: 'test' }), diff --git a/test/parallel/test-zlib-deflate-raw-inherits.js b/test/parallel/test-zlib-deflate-raw-inherits.js index 34bf31058a1d..1f780172dbee 100644 --- a/test/parallel/test-zlib-deflate-raw-inherits.js +++ b/test/parallel/test-zlib-deflate-raw-inherits.js @@ -4,15 +4,14 @@ require('../common'); const { DeflateRaw } = require('zlib'); const { Readable } = require('stream'); -// Validates that zlib.DeflateRaw can be inherited -// with Object.setPrototypeOf +// Validates that zlib.DeflateRaw can be subclassed with class syntax. -function NotInitialized(options) { - DeflateRaw.call(this, options); - this.prop = true; +class NotInitialized extends DeflateRaw { + constructor(options) { + super(options); + this.prop = true; + } } -Object.setPrototypeOf(NotInitialized.prototype, DeflateRaw.prototype); -Object.setPrototypeOf(NotInitialized, DeflateRaw); const dest = new NotInitialized(); diff --git a/test/parallel/test-zlib-invalid-arg-value-brotli-compress.js b/test/parallel/test-zlib-invalid-arg-value-brotli-compress.js index 688acddd16a1..38e95cf6b049 100644 --- a/test/parallel/test-zlib-invalid-arg-value-brotli-compress.js +++ b/test/parallel/test-zlib-invalid-arg-value-brotli-compress.js @@ -15,6 +15,6 @@ const opts = { } }; -assert.throws(() => BrotliCompress(opts), { +assert.throws(() => new BrotliCompress(opts), { code: 'ERR_INVALID_ARG_TYPE' }); diff --git a/test/parallel/test-zlib-invalid-input.js b/test/parallel/test-zlib-invalid-input.js index 721badc13f6a..7a0146a5d641 100644 --- a/test/parallel/test-zlib-invalid-input.js +++ b/test/parallel/test-zlib-invalid-input.js @@ -35,11 +35,11 @@ const nonStringInputs = [ // zlib.Unzip classes need to get valid data, or else they'll throw. const unzips = [ - zlib.Unzip(), - zlib.Gunzip(), - zlib.Inflate(), - zlib.InflateRaw(), - zlib.BrotliDecompress(), + new zlib.Unzip(), + new zlib.Gunzip(), + new zlib.Inflate(), + new zlib.InflateRaw(), + new zlib.BrotliDecompress(), new zlib.ZstdDecompress(), ]; diff --git a/test/parallel/test-zlib.js b/test/parallel/test-zlib.js index bda63a45b7b1..35fd2c946030 100644 --- a/test/parallel/test-zlib.js +++ b/test/parallel/test-zlib.js @@ -234,8 +234,9 @@ testKeys.forEach(common.mustCall((file) => { }, testKeys.length)); { - // Test instantiation without 'new' - common.expectWarning('DeprecationWarning', `Instantiating Gzip without the 'new' keyword has been deprecated.`, 'DEP0184'); - const gzip = zlib.Gzip(); - assert.ok(gzip instanceof zlib.Gzip); + // Instantiating without `new` is End-of-Life (DEP0184). + assert.throws(() => zlib.Gzip(), { + name: 'TypeError', + message: /Class constructor Gzip cannot be invoked without 'new'/, + }); }