diff --git a/.eslintrc.js b/.eslintrc.js
index f46a64bcbf2acd..8b462e0777c5d4 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -361,5 +361,6 @@ module.exports = {
WritableStream: 'readable',
WritableStreamDefaultWriter: 'readable',
WritableStreamDefaultController: 'readable',
+ WebSocket: 'readable',
},
};
diff --git a/benchmark/misc/hidestackframes.js b/benchmark/error/hidestackframes.js
similarity index 100%
rename from benchmark/misc/hidestackframes.js
rename to benchmark/error/hidestackframes.js
diff --git a/benchmark/error/node-error-instantiation.js b/benchmark/error/node-error-instantiation.js
new file mode 100644
index 00000000000000..333087b9195894
--- /dev/null
+++ b/benchmark/error/node-error-instantiation.js
@@ -0,0 +1,66 @@
+'use strict';
+
+const common = require('../common');
+const assert = require('assert');
+
+const bench = common.createBenchmark(main, {
+ n: [1e6],
+ code: [
+ 'built-in',
+ 'ERR_HTTP2_STREAM_SELF_DEPENDENCY',
+ 'ERR_INVALID_STATE',
+ 'ERR_INVALID_URL',
+ ],
+ stackTraceLimit: [0, 10],
+}, {
+ flags: ['--expose-internals'],
+});
+
+function getErrorFactory(code) {
+ const {
+ ERR_HTTP2_STREAM_SELF_DEPENDENCY,
+ ERR_INVALID_STATE,
+ ERR_INVALID_URL,
+ } = require('internal/errors').codes;
+
+ switch (code) {
+ case 'built-in':
+ return (n) => new Error();
+ case 'ERR_HTTP2_STREAM_SELF_DEPENDENCY':
+ return (n) => new ERR_HTTP2_STREAM_SELF_DEPENDENCY();
+ case 'ERR_INVALID_STATE':
+ return (n) => new ERR_INVALID_STATE(n + '');
+ case 'ERR_INVALID_URL':
+ return (n) => new ERR_INVALID_URL({ input: n + '' });
+ default:
+ throw new Error(`${code} not supported`);
+ }
+}
+
+function main({ n, code, stackTraceLimit }) {
+ const getError = getErrorFactory(code);
+
+ Error.stackTraceLimit = stackTraceLimit;
+
+ // Warm up.
+ const length = 1024;
+ const array = [];
+ for (let i = 0; i < length; ++i) {
+ array.push(getError(i));
+ }
+
+ bench.start();
+
+ for (let i = 0; i < n; ++i) {
+ const index = i % length;
+ array[index] = getError(index);
+ }
+
+ bench.end(n);
+
+ // Verify the entries to prevent dead code elimination from making
+ // the benchmark invalid.
+ for (let i = 0; i < length; ++i) {
+ assert.strictEqual(typeof array[i], 'object');
+ }
+}
diff --git a/benchmark/error/node-error-stack.js b/benchmark/error/node-error-stack.js
new file mode 100644
index 00000000000000..06319ccd17105f
--- /dev/null
+++ b/benchmark/error/node-error-stack.js
@@ -0,0 +1,62 @@
+'use strict';
+
+const common = require('../common');
+const assert = require('assert');
+
+const bench = common.createBenchmark(main, {
+ n: [1e6],
+ code: [
+ 'built-in',
+ 'ERR_HTTP2_STREAM_SELF_DEPENDENCY',
+ 'ERR_INVALID_STATE',
+ ],
+ stackTraceLimit: [0, 10],
+}, {
+ flags: ['--expose-internals'],
+});
+
+function getErrorStackFactory(code) {
+ const {
+ ERR_INVALID_STATE,
+ ERR_HTTP2_STREAM_SELF_DEPENDENCY,
+ } = require('internal/errors').codes;
+
+ switch (code) {
+ case 'built-in':
+ return (n) => new Error().stack;
+ case 'ERR_HTTP2_STREAM_SELF_DEPENDENCY':
+ return (n) => new ERR_HTTP2_STREAM_SELF_DEPENDENCY().stack;
+ case 'ERR_INVALID_STATE':
+ return (n) => new ERR_INVALID_STATE(n + '').stack;
+ default:
+ throw new Error(`${code} not supported`);
+ }
+}
+
+function main({ n, code, stackTraceLimit }) {
+ const getStack = getErrorStackFactory(code);
+
+ Error.stackTraceLimit = stackTraceLimit;
+
+ // Warm up.
+ const length = 1024;
+ const array = [];
+ for (let i = 0; i < length; ++i) {
+ array.push(getStack(i));
+ }
+
+ bench.start();
+
+ for (let i = 0; i < n; ++i) {
+ const index = i % length;
+ array[index] = getStack(index);
+ }
+
+ bench.end(n);
+
+ // Verify the entries to prevent dead code elimination from making
+ // the benchmark invalid.
+ for (let i = 0; i < length; ++i) {
+ assert.strictEqual(typeof array[i], 'string');
+ }
+}
diff --git a/benchmark/error/node-error.js b/benchmark/error/node-error.js
deleted file mode 100644
index 3a0aef91f04a06..00000000000000
--- a/benchmark/error/node-error.js
+++ /dev/null
@@ -1,21 +0,0 @@
-'use strict';
-
-const common = require('../common');
-
-const bench = common.createBenchmark(main, {
- n: [1e7],
-}, {
- flags: ['--expose-internals'],
-});
-
-function main({ n }) {
- const {
- codes: {
- ERR_INVALID_STATE,
- },
- } = require('internal/errors');
- bench.start();
- for (let i = 0; i < n; ++i)
- new ERR_INVALID_STATE.TypeError('test');
- bench.end(n);
-}
diff --git a/common.gypi b/common.gypi
index 14f05fcf9ccb8d..909d09934a1b25 100644
--- a/common.gypi
+++ b/common.gypi
@@ -36,7 +36,7 @@
# Reset this number to 0 on major V8 upgrades.
# Increment by one for each non-official patch applied to deps/v8.
- 'v8_embedder_string': '-node.18',
+ 'v8_embedder_string': '-node.19',
##### V8 defaults for Node.js #####
diff --git a/deps/v8/src/snapshot/embedded/platform-embedded-file-writer-base.cc b/deps/v8/src/snapshot/embedded/platform-embedded-file-writer-base.cc
index e0602edc7e1c6a..e4ca7bfdacb11c 100644
--- a/deps/v8/src/snapshot/embedded/platform-embedded-file-writer-base.cc
+++ b/deps/v8/src/snapshot/embedded/platform-embedded-file-writer-base.cc
@@ -130,7 +130,8 @@ EmbeddedTargetOs ToEmbeddedTargetOs(const char* s) {
}
std::string string(s);
- if (string == "aix") {
+ // Python 3.9+ on IBM i returns os400 as sys.platform instead of aix
+ if (string == "aix" || string == "os400") {
return EmbeddedTargetOs::kAIX;
} else if (string == "chromeos") {
return EmbeddedTargetOs::kChromeOS;
diff --git a/doc/api/cli.md b/doc/api/cli.md
index 1da38c1739126b..013256d776bac7 100644
--- a/doc/api/cli.md
+++ b/doc/api/cli.md
@@ -738,6 +738,14 @@ added: v12.3.0
Enable experimental WebAssembly module support.
+### `--experimental-websocket`
+
+
+
+Enable experimental [`WebSocket`][] support.
+
### `--force-context-aware`
+
+> Stability: 1 - Experimental.
+
+A browser-compatible implementation of [`WebSocket`][]. Enable this API
+with the [`--experimental-websocket`][] CLI flag.
+
## Class: `WritableStream`
diff --git a/lib/internal/blob.js b/lib/internal/blob.js
index a54adb615fbc17..b68037612ab8b3 100644
--- a/lib/internal/blob.js
+++ b/lib/internal/blob.js
@@ -24,6 +24,9 @@ const {
concat,
getDataObject,
} = internalBinding('blob');
+const {
+ kMaxLength,
+} = internalBinding('buffer');
const {
TextDecoder,
@@ -62,7 +65,6 @@ const {
} = require('internal/errors');
const {
- isUint32,
validateDictionary,
} = require('internal/validators');
@@ -160,8 +162,8 @@ class Blob {
return src;
});
- if (!isUint32(length))
- throw new ERR_BUFFER_TOO_LARGE(0xFFFFFFFF);
+ if (length > kMaxLength)
+ throw new ERR_BUFFER_TOO_LARGE(kMaxLength);
this[kHandle] = _createBlob(sources_, length);
this[kLength] = length;
diff --git a/lib/internal/crypto/hkdf.js b/lib/internal/crypto/hkdf.js
index 7f0fe5534ee843..cf3c39e8d9da5a 100644
--- a/lib/internal/crypto/hkdf.js
+++ b/lib/internal/crypto/hkdf.js
@@ -57,7 +57,7 @@ const validateParameters = hideStackFrames((hash, key, salt, info, length) => {
validateInteger(length, 'length', 0, kMaxLength);
if (info.byteLength > 1024) {
- throw ERR_OUT_OF_RANGE(
+ throw new ERR_OUT_OF_RANGE(
'info',
'must not contain more than 1024 bytes',
info.byteLength);
diff --git a/lib/internal/errors.js b/lib/internal/errors.js
index 4e332e1ce18d16..6bba8ec095e86e 100644
--- a/lib/internal/errors.js
+++ b/lib/internal/errors.js
@@ -175,9 +175,10 @@ const aggregateErrors = hideStackFrames((errors, message, code) => {
return err;
});
+const assert = require('internal/assert');
+
// Lazily loaded
let util;
-let assert;
let internalUtil = null;
function lazyInternalUtil() {
@@ -371,42 +372,103 @@ function makeSystemErrorWithCode(key) {
}
function makeNodeErrorWithCode(Base, key) {
- return function NodeError(...args) {
- const limit = Error.stackTraceLimit;
- if (isErrorStackTraceLimitWritable()) Error.stackTraceLimit = 0;
- const error = new Base();
- // Reset the limit and setting the name property.
- if (isErrorStackTraceLimitWritable()) Error.stackTraceLimit = limit;
- const message = getMessage(key, args, error);
- ObjectDefineProperties(error, {
- [kIsNodeError]: {
- __proto__: null,
- value: true,
- enumerable: false,
- writable: false,
- configurable: true,
- },
- message: {
- __proto__: null,
- value: message,
- enumerable: false,
- writable: true,
- configurable: true,
- },
- toString: {
- __proto__: null,
- value() {
+ const msg = messages.get(key);
+ const expectedLength = typeof msg !== 'string' ? -1 : getExpectedArgumentLength(msg);
+
+ switch (expectedLength) {
+ case 0: {
+ class NodeError extends Base {
+ code = key;
+
+ constructor(...args) {
+ assert(
+ args.length === 0,
+ `Code: ${key}; The provided arguments length (${args.length}) does not ` +
+ `match the required ones (${expectedLength}).`,
+ );
+ super(msg);
+ }
+
+ // This is a workaround for wpt tests that expect that the error
+ // constructor has a `name` property of the base class.
+ get ['constructor']() {
+ return Base;
+ }
+
+ get [kIsNodeError]() {
+ return true;
+ }
+
+ toString() {
return `${this.name} [${key}]: ${this.message}`;
- },
- enumerable: false,
- writable: true,
- configurable: true,
- },
- });
- captureLargerStackTrace(error);
- error.code = key;
- return error;
- };
+ }
+ }
+ return NodeError;
+ }
+ case -1: {
+ class NodeError extends Base {
+ code = key;
+
+ constructor(...args) {
+ super();
+ ObjectDefineProperty(this, 'message', {
+ __proto__: null,
+ value: getMessage(key, args, this),
+ enumerable: false,
+ writable: true,
+ configurable: true,
+ });
+ }
+
+ // This is a workaround for wpt tests that expect that the error
+ // constructor has a `name` property of the base class.
+ get ['constructor']() {
+ return Base;
+ }
+
+ get [kIsNodeError]() {
+ return true;
+ }
+
+ toString() {
+ return `${this.name} [${key}]: ${this.message}`;
+ }
+ }
+ return NodeError;
+ }
+ default: {
+
+ class NodeError extends Base {
+ code = key;
+
+ constructor(...args) {
+ assert(
+ args.length === expectedLength,
+ `Code: ${key}; The provided arguments length (${args.length}) does not ` +
+ `match the required ones (${expectedLength}).`,
+ );
+
+ ArrayPrototypeUnshift(args, msg);
+ super(ReflectApply(lazyInternalUtilInspect().format, null, args));
+ }
+
+ // This is a workaround for wpt tests that expect that the error
+ // constructor has a `name` property of the base class.
+ get ['constructor']() {
+ return Base;
+ }
+
+ get [kIsNodeError]() {
+ return true;
+ }
+
+ toString() {
+ return `${this.name} [${key}]: ${this.message}`;
+ }
+ }
+ return NodeError;
+ }
+ }
}
/**
@@ -443,11 +505,16 @@ function E(sym, val, def, ...otherClasses) {
codes[sym] = def;
}
+function getExpectedArgumentLength(msg) {
+ let expectedLength = 0;
+ const regex = /%[dfijoOs]/g;
+ while (RegExpPrototypeExec(regex, msg) !== null) expectedLength++;
+ return expectedLength;
+}
+
function getMessage(key, args, self) {
const msg = messages.get(key);
- assert ??= require('internal/assert');
-
if (typeof msg === 'function') {
assert(
msg.length <= args.length, // Default options do not count.
@@ -457,9 +524,7 @@ function getMessage(key, args, self) {
return ReflectApply(msg, self, args);
}
- const regex = /%[dfijoOs]/g;
- let expectedLength = 0;
- while (RegExpPrototypeExec(regex, msg) !== null) expectedLength++;
+ const expectedLength = getExpectedArgumentLength(msg);
assert(
expectedLength === args.length,
`Code: ${key}; The provided arguments length (${args.length}) does not ` +
@@ -1476,8 +1541,7 @@ E('ERR_NETWORK_IMPORT_DISALLOWED',
"import of '%s' by %s is not supported: %s", Error);
E('ERR_NOT_BUILDING_SNAPSHOT',
'Operation cannot be invoked when not building startup snapshot', Error);
-E('ERR_NOT_SUPPORTED_IN_SNAPSHOT',
- '%s is not supported in startup snapshot', Error);
+E('ERR_NOT_SUPPORTED_IN_SNAPSHOT', '%s is not supported in startup snapshot', Error);
E('ERR_NO_CRYPTO',
'Node.js is not compiled with OpenSSL crypto support', Error);
E('ERR_NO_ICU',
diff --git a/lib/internal/fs/streams.js b/lib/internal/fs/streams.js
index c317f3b9202af9..6a213693342b94 100644
--- a/lib/internal/fs/streams.js
+++ b/lib/internal/fs/streams.js
@@ -143,8 +143,8 @@ function importFd(stream, options) {
return options.fd.fd;
}
- throw ERR_INVALID_ARG_TYPE('options.fd',
- ['number', 'FileHandle'], options.fd);
+ throw new ERR_INVALID_ARG_TYPE('options.fd',
+ ['number', 'FileHandle'], options.fd);
}
function ReadStream(path, options) {
diff --git a/lib/internal/modules/esm/hooks.js b/lib/internal/modules/esm/hooks.js
index 8ad4d00bbfe06f..8dd81ca52c5318 100644
--- a/lib/internal/modules/esm/hooks.js
+++ b/lib/internal/modules/esm/hooks.js
@@ -407,7 +407,7 @@ class Hooks {
!isAnyArrayBuffer(source) &&
!isArrayBufferView(source)
) {
- throw ERR_INVALID_RETURN_PROPERTY_VALUE(
+ throw new ERR_INVALID_RETURN_PROPERTY_VALUE(
'a string, an ArrayBuffer, or a TypedArray',
hookErrIdentifier,
'source',
@@ -599,7 +599,7 @@ class HooksProxy {
if (status === 'error') {
if (body == null || typeof body !== 'object') { throw body; }
if (body.serializationFailed || body.serialized == null) {
- throw ERR_WORKER_UNSERIALIZABLE_ERROR();
+ throw new ERR_WORKER_UNSERIALIZABLE_ERROR();
}
// eslint-disable-next-line no-restricted-syntax
diff --git a/lib/internal/navigator.js b/lib/internal/navigator.js
index 3b8343cd7ed6f8..5971891ea73cc9 100644
--- a/lib/internal/navigator.js
+++ b/lib/internal/navigator.js
@@ -27,7 +27,7 @@ class Navigator {
if (arguments[0] === kInitialize) {
return;
}
- throw ERR_ILLEGAL_CONSTRUCTOR();
+ throw new ERR_ILLEGAL_CONSTRUCTOR();
}
/**
diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js
index 1f4a08515b5ae9..f120f371d9b634 100644
--- a/lib/internal/process/pre_execution.js
+++ b/lib/internal/process/pre_execution.js
@@ -78,7 +78,7 @@ function prepareExecution(options) {
setupTraceCategoryState();
setupInspectorHooks();
setupWarningHandler();
- setupFetch();
+ setupUndici();
setupWebCrypto();
setupCustomEvent();
setupCodeCoverage();
@@ -262,9 +262,9 @@ function setupWarningHandler() {
}
// https://fetch.spec.whatwg.org/
-function setupFetch() {
- if (getEmbedderOptions().noBrowserGlobals ||
- getOptionValue('--no-experimental-fetch')) {
+// https://websockets.spec.whatwg.org/
+function setupUndici() {
+ if (getEmbedderOptions().noBrowserGlobals) {
return;
}
@@ -278,12 +278,6 @@ function setupFetch() {
return undici;
}
- async function fetch(input, init = undefined) {
- return lazyUndici().fetch(input, init);
- }
-
- defineOperation(globalThis, 'fetch', fetch);
-
function lazyInterface(name) {
return {
configurable: true,
@@ -297,17 +291,31 @@ function setupFetch() {
};
}
- ObjectDefineProperties(globalThis, {
- FormData: lazyInterface('FormData'),
- Headers: lazyInterface('Headers'),
- Request: lazyInterface('Request'),
- Response: lazyInterface('Response'),
- });
+ if (!getOptionValue('--no-experimental-fetch')) {
+ async function fetch(input, init = undefined) {
+ return lazyUndici().fetch(input, init);
+ }
- // The WebAssembly Web API: https://webassembly.github.io/spec/web-api
- internalBinding('wasm_web_api').setImplementation((streamState, source) => {
- require('internal/wasm_web_api').wasmStreamingCallback(streamState, source);
- });
+ defineOperation(globalThis, 'fetch', fetch);
+
+ ObjectDefineProperties(globalThis, {
+ FormData: lazyInterface('FormData'),
+ Headers: lazyInterface('Headers'),
+ Request: lazyInterface('Request'),
+ Response: lazyInterface('Response'),
+ });
+
+ // The WebAssembly Web API: https://webassembly.github.io/spec/web-api
+ internalBinding('wasm_web_api').setImplementation((streamState, source) => {
+ require('internal/wasm_web_api').wasmStreamingCallback(streamState, source);
+ });
+ }
+
+ if (getOptionValue('--experimental-websocket')) {
+ ObjectDefineProperties(globalThis, {
+ WebSocket: lazyInterface('WebSocket'),
+ });
+ }
}
// TODO(aduh95): move this to internal/bootstrap/web/* when the CLI flag is
diff --git a/lib/internal/url.js b/lib/internal/url.js
index 13a9e287ffc47a..ca41c48582b19d 100644
--- a/lib/internal/url.js
+++ b/lib/internal/url.js
@@ -855,7 +855,7 @@ class URL {
set href(value) {
value = `${value}`;
const href = bindingUrl.update(this.#context.href, updateActions.kHref, value);
- if (!href) { throw ERR_INVALID_URL(value); }
+ if (!href) { throw new ERR_INVALID_URL(value); }
this.#updateContext(href);
}
diff --git a/lib/stream.js b/lib/stream.js
index 9a09401e7d016a..cdbc1fe0380694 100644
--- a/lib/stream.js
+++ b/lib/stream.js
@@ -64,7 +64,7 @@ for (const key of ObjectKeys(streamReturningOperators)) {
const op = streamReturningOperators[key];
function fn(...args) {
if (new.target) {
- throw ERR_ILLEGAL_CONSTRUCTOR();
+ throw new ERR_ILLEGAL_CONSTRUCTOR();
}
return Stream.Readable.from(ReflectApply(op, this, args));
}
@@ -82,7 +82,7 @@ for (const key of ObjectKeys(promiseReturningOperators)) {
const op = promiseReturningOperators[key];
function fn(...args) {
if (new.target) {
- throw ERR_ILLEGAL_CONSTRUCTOR();
+ throw new ERR_ILLEGAL_CONSTRUCTOR();
}
return ReflectApply(op, this, args);
}
diff --git a/src/node_options.cc b/src/node_options.cc
index b544f1209143c0..0285f422dfd62c 100644
--- a/src/node_options.cc
+++ b/src/node_options.cc
@@ -370,6 +370,11 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
&EnvironmentOptions::experimental_fetch,
kAllowedInEnvvar,
true);
+ AddOption("--experimental-websocket",
+ "experimental WebSocket API",
+ &EnvironmentOptions::experimental_websocket,
+ kAllowedInEnvvar,
+ true);
AddOption("--experimental-global-customevent",
"expose experimental CustomEvent on the global scope",
&EnvironmentOptions::experimental_global_customevent,
diff --git a/src/node_options.h b/src/node_options.h
index bc18a45e681a3c..2b9f3d3084651a 100644
--- a/src/node_options.h
+++ b/src/node_options.h
@@ -111,6 +111,7 @@ class EnvironmentOptions : public Options {
std::string dns_result_order;
bool enable_source_maps = false;
bool experimental_fetch = true;
+ bool experimental_websocket = false;
bool experimental_global_customevent = true;
bool experimental_global_web_crypto = true;
bool experimental_https_modules = false;
diff --git a/test/common/globals.js b/test/common/globals.js
index f18b358e657a55..8b9b6b34f6abaf 100644
--- a/test/common/globals.js
+++ b/test/common/globals.js
@@ -123,6 +123,7 @@ const webIdlExposedWindow = new Set([
'Headers',
'Request',
'Response',
+ 'WebSocket',
]);
const nodeGlobals = new Set([
diff --git a/test/common/index.js b/test/common/index.js
index c10dea59319264..9d8accf939a1ff 100644
--- a/test/common/index.js
+++ b/test/common/index.js
@@ -370,6 +370,9 @@ if (global.ReadableStream) {
global.DecompressionStream,
);
}
+if (global.WebSocket) {
+ knownGlobals.push(WebSocket);
+}
function allowGlobals(...allowlist) {
knownGlobals = knownGlobals.concat(allowlist);
@@ -720,9 +723,8 @@ function expectsError(validator, exact) {
assert.fail(`Expected one argument, got ${inspect(args)}`);
}
const error = args.pop();
- const descriptor = Object.getOwnPropertyDescriptor(error, 'message');
// The error message should be non-enumerable
- assert.strictEqual(descriptor.enumerable, false);
+ assert.strictEqual(Object.prototype.propertyIsEnumerable.call(error, 'message'), false);
assert.throws(() => { throw error; }, validator);
return true;
diff --git a/test/fixtures/test-runner/output/junit_reporter.snapshot b/test/fixtures/test-runner/output/junit_reporter.snapshot
index 6516387e7ed582..e0a4c331dd5c22 100644
--- a/test/fixtures/test-runner/output/junit_reporter.snapshot
+++ b/test/fixtures/test-runner/output/junit_reporter.snapshot
@@ -10,6 +10,7 @@
[Error [ERR_TEST_FAILURE]: thrown from sync fail todo] {
+ code: 'ERR_TEST_FAILURE',
failureType: 'testCodeFailure',
cause: Error: thrown from sync fail todo
*
@@ -18,8 +19,7 @@
*
*
*
- at async Test.processPendingSubtests (node:internal/test_runner/test:374:7),
- code: 'ERR_TEST_FAILURE'
+ *
}
@@ -27,6 +27,7 @@
[Error [ERR_TEST_FAILURE]: thrown from sync fail todo with message] {
+ code: 'ERR_TEST_FAILURE',
failureType: 'testCodeFailure',
cause: Error: thrown from sync fail todo with message
*
@@ -35,8 +36,7 @@
*
*
*
- at async Test.processPendingSubtests (node:internal/test_runner/test:374:7),
- code: 'ERR_TEST_FAILURE'
+ *
}
@@ -51,6 +51,7 @@
[Error [ERR_TEST_FAILURE]: thrown from sync throw fail] {
+ code: 'ERR_TEST_FAILURE',
failureType: 'testCodeFailure',
cause: Error: thrown from sync throw fail
*
@@ -59,8 +60,7 @@
*
*
*
- at async Test.processPendingSubtests (node:internal/test_runner/test:374:7),
- code: 'ERR_TEST_FAILURE'
+ *
}
@@ -71,6 +71,7 @@
[Error [ERR_TEST_FAILURE]: thrown from async throw fail] {
+ code: 'ERR_TEST_FAILURE',
failureType: 'testCodeFailure',
cause: Error: thrown from async throw fail
*
@@ -79,8 +80,7 @@
*
*
*
- at async Test.processPendingSubtests (node:internal/test_runner/test:374:7),
- code: 'ERR_TEST_FAILURE'
+ *
}
@@ -88,6 +88,7 @@
[Error [ERR_TEST_FAILURE]: thrown from async throw fail] {
+ code: 'ERR_TEST_FAILURE',
failureType: 'testCodeFailure',
cause: Error: thrown from async throw fail
*
@@ -96,8 +97,7 @@
*
*
*
- at async Test.processPendingSubtests (node:internal/test_runner/test:374:7),
- code: 'ERR_TEST_FAILURE'
+ *
}
@@ -107,6 +107,7 @@
true !== false
] {
+ code: 'ERR_TEST_FAILURE',
failureType: 'testCodeFailure',
cause: AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
@@ -124,8 +125,7 @@ true !== false
actual: true,
expected: false,
operator: 'strictEqual'
- },
- code: 'ERR_TEST_FAILURE'
+ }
}
@@ -133,6 +133,7 @@ true !== false
[Error [ERR_TEST_FAILURE]: rejected from reject fail] {
+ code: 'ERR_TEST_FAILURE',
failureType: 'testCodeFailure',
cause: Error: rejected from reject fail
*
@@ -141,8 +142,7 @@ true !== false
*
*
*
- at async Test.processPendingSubtests (node:internal/test_runner/test:374:7),
- code: 'ERR_TEST_FAILURE'
+ *
}
@@ -156,6 +156,7 @@ true !== false
Error [ERR_TEST_FAILURE]: thrown from subtest sync throw fail
* {
+ code: 'ERR_TEST_FAILURE',
failureType: 'testCodeFailure',
cause: Error: thrown from subtest sync throw fail
*
@@ -167,8 +168,7 @@ Error [ERR_TEST_FAILURE]: thrown from subtest sync throw fail
*
*
*
- at Test.postRun (node:internal/test_runner/test:715:19),
- code: 'ERR_TEST_FAILURE'
+ *
}
@@ -176,7 +176,7 @@ Error [ERR_TEST_FAILURE]: thrown from subtest sync throw fail
-[Error [ERR_TEST_FAILURE]: Symbol(thrown symbol from sync throw non-error fail)] { failureType: 'testCodeFailure', cause: Symbol(thrown symbol from sync throw non-error fail), code: 'ERR_TEST_FAILURE' }
+[Error [ERR_TEST_FAILURE]: Symbol(thrown symbol from sync throw non-error fail)] { code: 'ERR_TEST_FAILURE', failureType: 'testCodeFailure', cause: Symbol(thrown symbol from sync throw non-error fail) }
@@ -188,7 +188,7 @@ Error [ERR_TEST_FAILURE]: thrown from subtest sync throw fail
-[Error [ERR_TEST_FAILURE]: test did not finish before its parent and was cancelled] { failureType: 'cancelledByParent', cause: 'test did not finish before its parent and was cancelled', code: 'ERR_TEST_FAILURE' }
+[Error [ERR_TEST_FAILURE]: test did not finish before its parent and was cancelled] { code: 'ERR_TEST_FAILURE', failureType: 'cancelledByParent', cause: 'test did not finish before its parent and was cancelled' }
@@ -205,6 +205,7 @@ Error [ERR_TEST_FAILURE]: thrown from subtest sync throw fail
[Error [ERR_TEST_FAILURE]: this should be executed] {
+ code: 'ERR_TEST_FAILURE',
failureType: 'testCodeFailure',
cause: Error: this should be executed
*
@@ -213,8 +214,7 @@ Error [ERR_TEST_FAILURE]: thrown from subtest sync throw fail
*
*
*
- at async Test.processPendingSubtests (node:internal/test_runner/test:374:7),
- code: 'ERR_TEST_FAILURE'
+ *
}
@@ -236,11 +236,11 @@ Error [ERR_TEST_FAILURE]: thrown from subtest sync throw fail
[Error [ERR_TEST_FAILURE]: callback failure] {
+ code: 'ERR_TEST_FAILURE',
failureType: 'testCodeFailure',
cause: Error: callback failure
*
- at process.processImmediate (node:internal/timers:478:21),
- code: 'ERR_TEST_FAILURE'
+ *
}
@@ -249,12 +249,13 @@ Error [ERR_TEST_FAILURE]: thrown from subtest sync throw fail
-[Error [ERR_TEST_FAILURE]: passed a callback but also returned a Promise] { failureType: 'callbackAndPromisePresent', cause: 'passed a callback but also returned a Promise', code: 'ERR_TEST_FAILURE' }
+[Error [ERR_TEST_FAILURE]: passed a callback but also returned a Promise] { code: 'ERR_TEST_FAILURE', failureType: 'callbackAndPromisePresent', cause: 'passed a callback but also returned a Promise' }
[Error [ERR_TEST_FAILURE]: thrown from callback throw] {
+ code: 'ERR_TEST_FAILURE',
failureType: 'testCodeFailure',
cause: Error: thrown from callback throw
*
@@ -263,8 +264,7 @@ Error [ERR_TEST_FAILURE]: thrown from subtest sync throw fail
*
*
*
- at async Test.processPendingSubtests (node:internal/test_runner/test:374:7),
- code: 'ERR_TEST_FAILURE'
+ *
}
@@ -273,9 +273,9 @@ Error [ERR_TEST_FAILURE]: thrown from subtest sync throw fail
Error [ERR_TEST_FAILURE]: callback invoked multiple times
*
* {
+ code: 'ERR_TEST_FAILURE',
failureType: 'multipleCallbackInvocations',
- cause: 'callback invoked multiple times',
- code: 'ERR_TEST_FAILURE'
+ cause: 'callback invoked multiple times'
}
@@ -284,14 +284,14 @@ Error [ERR_TEST_FAILURE]: callback invoked multiple times
Error [ERR_TEST_FAILURE]: callback invoked multiple times
* {
+ code: 'ERR_TEST_FAILURE',
failureType: 'uncaughtException',
cause: Error [ERR_TEST_FAILURE]: callback invoked multiple times
* {
+ code: 'ERR_TEST_FAILURE',
failureType: 'multipleCallbackInvocations',
- cause: 'callback invoked multiple times',
- code: 'ERR_TEST_FAILURE'
- },
- code: 'ERR_TEST_FAILURE'
+ cause: 'callback invoked multiple times'
+ }
}
@@ -299,11 +299,11 @@ Error [ERR_TEST_FAILURE]: callback invoked multiple times
Error [ERR_TEST_FAILURE]: thrown from callback async throw
* {
+ code: 'ERR_TEST_FAILURE',
failureType: 'uncaughtException',
cause: Error: thrown from callback async throw
*
- at process.processImmediate (node:internal/timers:478:21),
- code: 'ERR_TEST_FAILURE'
+ *
}
@@ -319,7 +319,7 @@ Error [ERR_TEST_FAILURE]: thrown from callback async throw
-[Error [ERR_TEST_FAILURE]: customized] { failureType: 'testCodeFailure', cause: customized, code: 'ERR_TEST_FAILURE' }
+[Error [ERR_TEST_FAILURE]: customized] { code: 'ERR_TEST_FAILURE', failureType: 'testCodeFailure', cause: customized }
@@ -328,9 +328,9 @@ Error [ERR_TEST_FAILURE]: thrown from callback async throw
foo: 1,
[Symbol(nodejs.util.inspect.custom)]: [Function: [nodejs.util.inspect.custom]]
}] {
+ code: 'ERR_TEST_FAILURE',
failureType: 'testCodeFailure',
- cause: { foo: 1, [Symbol(nodejs.util.inspect.custom)]: [Function: [nodejs.util.inspect.custom]] },
- code: 'ERR_TEST_FAILURE'
+ cause: { foo: 1, [Symbol(nodejs.util.inspect.custom)]: [Function: [nodejs.util.inspect.custom]] }
}
@@ -339,6 +339,7 @@ Error [ERR_TEST_FAILURE]: thrown from callback async throw
Error [ERR_TEST_FAILURE]: thrown from subtest sync throw fails at first
* {
+ code: 'ERR_TEST_FAILURE',
failureType: 'testCodeFailure',
cause: Error: thrown from subtest sync throw fails at first
*
@@ -350,8 +351,7 @@ Error [ERR_TEST_FAILURE]: thrown from subtest sync throw fails at first
*
*
*
- at Test.postRun (node:internal/test_runner/test:715:19),
- code: 'ERR_TEST_FAILURE'
+ *
}
@@ -359,6 +359,7 @@ Error [ERR_TEST_FAILURE]: thrown from subtest sync throw fails at first
Error [ERR_TEST_FAILURE]: thrown from subtest sync throw fails at second
* {
+ code: 'ERR_TEST_FAILURE',
failureType: 'testCodeFailure',
cause: Error: thrown from subtest sync throw fails at second
*
@@ -370,20 +371,19 @@ Error [ERR_TEST_FAILURE]: thrown from subtest sync throw fails at second
*
*
*
- at async Test.run (node:internal/test_runner/test:632:9),
- code: 'ERR_TEST_FAILURE'
+ *
}
-[Error [ERR_TEST_FAILURE]: test timed out after 5ms] { failureType: 'testTimeoutFailure', cause: 'test timed out after 5ms', code: 'ERR_TEST_FAILURE' }
+[Error [ERR_TEST_FAILURE]: test timed out after 5ms] { code: 'ERR_TEST_FAILURE', failureType: 'testTimeoutFailure', cause: 'test timed out after 5ms' }
-[Error [ERR_TEST_FAILURE]: test timed out after 5ms] { failureType: 'testTimeoutFailure', cause: 'test timed out after 5ms', code: 'ERR_TEST_FAILURE' }
+[Error [ERR_TEST_FAILURE]: test timed out after 5ms] { code: 'ERR_TEST_FAILURE', failureType: 'testTimeoutFailure', cause: 'test timed out after 5ms' }
@@ -391,19 +391,19 @@ Error [ERR_TEST_FAILURE]: thrown from subtest sync throw fails at second
-[Error [ERR_TEST_FAILURE]: custom error] { failureType: 'testCodeFailure', cause: 'custom error', code: 'ERR_TEST_FAILURE' }
+[Error [ERR_TEST_FAILURE]: custom error] { code: 'ERR_TEST_FAILURE', failureType: 'testCodeFailure', cause: 'custom error' }
Error [ERR_TEST_FAILURE]: foo
* {
+ code: 'ERR_TEST_FAILURE',
failureType: 'uncaughtException',
cause: Error: foo
*
*
- at process.processTimers (node:internal/timers:514:7),
- code: 'ERR_TEST_FAILURE'
+ *
}
@@ -411,12 +411,12 @@ Error [ERR_TEST_FAILURE]: foo
Error [ERR_TEST_FAILURE]: bar
* {
+ code: 'ERR_TEST_FAILURE',
failureType: 'unhandledRejection',
cause: Error: bar
*
*
- at process.processTimers (node:internal/timers:514:7),
- code: 'ERR_TEST_FAILURE'
+ *
}
@@ -435,6 +435,7 @@ should loosely deep-equal
bar: 2,
c: [Circular *1]
}] {
+ code: 'ERR_TEST_FAILURE',
failureType: 'testCodeFailure',
cause: AssertionError [ERR_ASSERTION]: Expected values to be loosely deep-equal:
@@ -455,8 +456,7 @@ should loosely deep-equal
actual: [Object],
expected: [Object],
operator: 'deepEqual'
- },
- code: 'ERR_TEST_FAILURE'
+ }
}
@@ -464,9 +464,9 @@ should loosely deep-equal
Error [ERR_TEST_FAILURE]: test could not be started because its parent finished
* {
+ code: 'ERR_TEST_FAILURE',
failureType: 'parentAlreadyFinished',
- cause: 'test could not be started because its parent finished',
- code: 'ERR_TEST_FAILURE'
+ cause: 'test could not be started because its parent finished'
}
diff --git a/test/fixtures/test-runner/output/spec_reporter.snapshot b/test/fixtures/test-runner/output/spec_reporter.snapshot
index 5dc05d5b43c12d..8f14dc7fc9bade 100644
--- a/test/fixtures/test-runner/output/spec_reporter.snapshot
+++ b/test/fixtures/test-runner/output/spec_reporter.snapshot
@@ -178,9 +178,9 @@
callback called twice in future tick (*ms)
Error [ERR_TEST_FAILURE]: callback invoked multiple times
* {
+ code: 'ERR_TEST_FAILURE',
failureType: 'multipleCallbackInvocations',
- cause: 'callback invoked multiple times',
- code: 'ERR_TEST_FAILURE'
+ cause: 'callback invoked multiple times'
}
callback async throw (*ms)
@@ -449,9 +449,9 @@
callback called twice in future tick (*ms)
Error [ERR_TEST_FAILURE]: callback invoked multiple times
* {
+ code: 'ERR_TEST_FAILURE',
failureType: 'multipleCallbackInvocations',
- cause: 'callback invoked multiple times',
- code: 'ERR_TEST_FAILURE'
+ cause: 'callback invoked multiple times'
}
*
diff --git a/test/fixtures/test-runner/output/spec_reporter_cli.snapshot b/test/fixtures/test-runner/output/spec_reporter_cli.snapshot
index 25c22069c3b8e7..a9b70560d905b2 100644
--- a/test/fixtures/test-runner/output/spec_reporter_cli.snapshot
+++ b/test/fixtures/test-runner/output/spec_reporter_cli.snapshot
@@ -178,9 +178,9 @@
callback called twice in future tick (*ms)
Error [ERR_TEST_FAILURE]: callback invoked multiple times
* {
+ code: 'ERR_TEST_FAILURE',
failureType: 'multipleCallbackInvocations',
- cause: 'callback invoked multiple times',
- code: 'ERR_TEST_FAILURE'
+ cause: 'callback invoked multiple times'
}
callback async throw (*ms)
@@ -449,9 +449,9 @@
callback called twice in future tick (*ms)
Error [ERR_TEST_FAILURE]: callback invoked multiple times
* {
+ code: 'ERR_TEST_FAILURE',
failureType: 'multipleCallbackInvocations',
- cause: 'callback invoked multiple times',
- code: 'ERR_TEST_FAILURE'
+ cause: 'callback invoked multiple times'
}
*
diff --git a/test/message/internal_assert.out b/test/message/internal_assert.out
index bd25c879478083..197b863bf6ae69 100644
--- a/test/message/internal_assert.out
+++ b/test/message/internal_assert.out
@@ -5,7 +5,6 @@ node:internal/assert:*
Error [ERR_INTERNAL_ASSERTION]: This is caused by either a bug in Node.js or incorrect usage of Node.js internals.
Please open an issue with this stack trace at https://github.com/nodejs/node/issues
- at new NodeError (node:internal/errors:*:*)
at assert (node:internal/assert:*:*)
at * (*test*message*internal_assert.js:7:1)
at *
diff --git a/test/message/internal_assert_fail.out b/test/message/internal_assert_fail.out
index 408d6d3364470d..e6895691cda9c1 100644
--- a/test/message/internal_assert_fail.out
+++ b/test/message/internal_assert_fail.out
@@ -6,7 +6,6 @@ Error [ERR_INTERNAL_ASSERTION]: Unreachable!
This is caused by either a bug in Node.js or incorrect usage of Node.js internals.
Please open an issue with this stack trace at https://github.com/nodejs/node/issues
- at new NodeError (node:internal/errors:*:*)
at Function.fail (node:internal/assert:*:*)
at * (*test*message*internal_assert_fail.js:7:8)
at *
diff --git a/test/parallel/test-blob-buffer-too-large.js b/test/parallel/test-blob-buffer-too-large.js
index 2fd8b8754bd593..a9cf53b025bbff 100644
--- a/test/parallel/test-blob-buffer-too-large.js
+++ b/test/parallel/test-blob-buffer-too-large.js
@@ -3,17 +3,17 @@
const common = require('../common');
const assert = require('assert');
-const { Blob } = require('buffer');
+const { Blob, kMaxLength } = require('buffer');
if (common.isFreeBSD)
common.skip('Oversized buffer make the FreeBSD CI runner crash');
try {
- new Blob([new Uint8Array(0xffffffff), [1]]);
+ new Blob([new Uint8Array(kMaxLength), [1]]);
} catch (e) {
if (
e.message === 'Array buffer allocation failed' ||
- e.message === 'Invalid typed array length: 4294967295'
+ e.message === `Invalid typed array length: ${kMaxLength}`
) {
common.skip(
'Insufficient memory on this platform for oversized buffer test.'
diff --git a/test/parallel/test-buffer-alloc.js b/test/parallel/test-buffer-alloc.js
index c6b728027057ec..aad9c6bcab69e9 100644
--- a/test/parallel/test-buffer-alloc.js
+++ b/test/parallel/test-buffer-alloc.js
@@ -4,13 +4,16 @@ const common = require('../common');
const assert = require('assert');
const vm = require('vm');
-const SlowBuffer = require('buffer').SlowBuffer;
+const {
+ SlowBuffer,
+ kMaxLength,
+} = require('buffer');
// Verify the maximum Uint8Array size. There is no concrete limit by spec. The
// internal limits should be updated if this fails.
assert.throws(
- () => new Uint8Array(2 ** 32 + 1),
- { message: 'Invalid typed array length: 4294967297' }
+ () => new Uint8Array(kMaxLength + 1),
+ { message: `Invalid typed array length: ${kMaxLength + 1}` },
);
const b = Buffer.allocUnsafe(1024);
diff --git a/test/parallel/test-buffer-over-max-length.js b/test/parallel/test-buffer-over-max-length.js
index d2df358cc00ca4..f29d6b62d4aa40 100644
--- a/test/parallel/test-buffer-over-max-length.js
+++ b/test/parallel/test-buffer-over-max-length.js
@@ -12,18 +12,8 @@ const bufferMaxSizeMsg = {
name: 'RangeError',
};
-assert.throws(() => Buffer((-1 >>> 0) + 2), bufferMaxSizeMsg);
-assert.throws(() => SlowBuffer((-1 >>> 0) + 2), bufferMaxSizeMsg);
-assert.throws(() => Buffer.alloc((-1 >>> 0) + 2), bufferMaxSizeMsg);
-assert.throws(() => Buffer.allocUnsafe((-1 >>> 0) + 2), bufferMaxSizeMsg);
-assert.throws(() => Buffer.allocUnsafeSlow((-1 >>> 0) + 2), bufferMaxSizeMsg);
-
assert.throws(() => Buffer(kMaxLength + 1), bufferMaxSizeMsg);
assert.throws(() => SlowBuffer(kMaxLength + 1), bufferMaxSizeMsg);
assert.throws(() => Buffer.alloc(kMaxLength + 1), bufferMaxSizeMsg);
assert.throws(() => Buffer.allocUnsafe(kMaxLength + 1), bufferMaxSizeMsg);
assert.throws(() => Buffer.allocUnsafeSlow(kMaxLength + 1), bufferMaxSizeMsg);
-
-// issue GH-4331
-assert.throws(() => Buffer.allocUnsafe(0x100000001), bufferMaxSizeMsg);
-assert.throws(() => Buffer.allocUnsafe(0xFFFFFFFFF), bufferMaxSizeMsg);
diff --git a/test/parallel/test-buffer-tostring-rangeerror.js b/test/parallel/test-buffer-tostring-rangeerror.js
index d2e1e0d6e46438..0ebea759b5c42b 100644
--- a/test/parallel/test-buffer-tostring-rangeerror.js
+++ b/test/parallel/test-buffer-tostring-rangeerror.js
@@ -1,17 +1,22 @@
'use strict';
require('../common');
-// This test ensures that Node.js throws a RangeError when trying to convert a
-// gigantic buffer into a string.
+// This test ensures that Node.js throws an Error when trying to convert a
+// large buffer into a string.
// Regression test for https://github.com/nodejs/node/issues/649.
const assert = require('assert');
-const SlowBuffer = require('buffer').SlowBuffer;
+const {
+ SlowBuffer,
+ constants: {
+ MAX_STRING_LENGTH,
+ },
+} = require('buffer');
-const len = 1422561062959;
+const len = MAX_STRING_LENGTH + 1;
const message = {
- code: 'ERR_OUT_OF_RANGE',
- name: 'RangeError',
+ code: 'ERR_STRING_TOO_LONG',
+ name: 'Error',
};
assert.throws(() => Buffer(len).toString('utf8'), message);
assert.throws(() => SlowBuffer(len).toString('utf8'), message);
diff --git a/test/parallel/test-repl-top-level-await.js b/test/parallel/test-repl-top-level-await.js
index 1abcca75f1e2a0..c8bc26fad62e5c 100644
--- a/test/parallel/test-repl-top-level-await.js
+++ b/test/parallel/test-repl-top-level-await.js
@@ -207,8 +207,8 @@ async function ctrlCTest() {
assert.deepStrictEqual(output.slice(0, 3), [
'await new Promise(() => {})\r',
'Uncaught:',
- 'Error [ERR_SCRIPT_EXECUTION_INTERRUPTED]: ' +
- 'Script execution was interrupted by `SIGINT`',
+ '[Error [ERR_SCRIPT_EXECUTION_INTERRUPTED]: ' +
+ 'Script execution was interrupted by `SIGINT`] {',
]);
assert.deepStrictEqual(output.slice(-2), [
'}',
diff --git a/test/parallel/test-websocket.js b/test/parallel/test-websocket.js
new file mode 100644
index 00000000000000..2a7069ccf3b739
--- /dev/null
+++ b/test/parallel/test-websocket.js
@@ -0,0 +1,7 @@
+// Flags: --experimental-websocket
+'use strict';
+
+require('../common');
+const assert = require('assert');
+
+assert.strictEqual(typeof WebSocket, 'function');
diff --git a/tools/doc/allhtml.mjs b/tools/doc/allhtml.mjs
index cdf7140f728469..ccf3a10ea7f95b 100644
--- a/tools/doc/allhtml.mjs
+++ b/tools/doc/allhtml.mjs
@@ -27,7 +27,7 @@ for (const link of toc.match(//g)) {
const data = fs.readFileSync(new URL(`./${href}`, source), 'utf8');
// Split the doc.
- const match = /(<\/ul>\s*)?<\/\w+>\s*<\w+ id="apicontent">/.exec(data);
+ const match = /(<\/ul>\s*)?<\/\w+>\s*<\w+ role="main" id="apicontent">/.exec(data);
// Get module name
const moduleName = href.replace(/\.html$/, '');
@@ -89,7 +89,7 @@ all = all.slice(0, tocStart.index + tocStart[0].length) +
all.slice(tocStart.index + tocStart[0].length);
// Replace apicontent with the concatenated set of apicontents from each source.
-const apiStart = /<\w+ id="apicontent">\s*/.exec(all);
+const apiStart = /<\w+ role="main" id="apicontent">\s*/.exec(all);
const apiEnd = all.lastIndexOf('');
all = all.slice(0, apiStart.index + apiStart[0].length)
.replace(
diff --git a/tools/doc/html.mjs b/tools/doc/html.mjs
index 168f68c1b03f21..95782efe03d554 100644
--- a/tools/doc/html.mjs
+++ b/tools/doc/html.mjs
@@ -467,7 +467,7 @@ export function buildToc({ filename, apilinks }) {
.use(htmlStringify)
.processSync(toc).toString();
- file.toc = `Table of contents
${inner} `;
+ file.toc = `Table of contents
${inner} `;
file.tocPicker = `${inner}
`;
} else {
file.toc = file.tocPicker = '';
diff --git a/tools/github_reporter/index.js b/tools/github_reporter/index.js
index 3b22136ab5260a..e9cf741b2ddc86 100644
--- a/tools/github_reporter/index.js
+++ b/tools/github_reporter/index.js
@@ -1621,7 +1621,7 @@ var require_oidc_utils = __commonJS({
Error Code : ${error.statusCode}
- Error Message: ${error.result.message}`);
+ Error Message: ${error.message}`);
});
const id_token = (_a = res.result) === null || _a === void 0 ? void 0 : _a.value;
if (!id_token) {
diff --git a/tools/github_reporter/package.json b/tools/github_reporter/package.json
index ff8ac3ec813d20..a4606a05892ced 100644
--- a/tools/github_reporter/package.json
+++ b/tools/github_reporter/package.json
@@ -1,7 +1,8 @@
{
"name": "@reporters/github",
- "version": "1.5.2",
+ "version": "1.5.3",
"description": "A github actions reporter for `node:test`",
+ "type": "commonjs",
"keywords": [
"github actions",
"node:test",
diff --git a/tools/osx-notarize.sh b/tools/osx-notarize.sh
index 31c92c2ca426f3..beea7041793cdf 100755
--- a/tools/osx-notarize.sh
+++ b/tools/osx-notarize.sh
@@ -1,34 +1,87 @@
#!/bin/sh
-# Uses gon, from https://github.com/mitchellh/gon, to notarize a generated node-.pkg file
-# with Apple for installation on macOS Catalina and later as validated by Gatekeeper.
+# Notarize a generated node-.pkg file as an Apple requirement for installation on macOS Catalina and later, as validated by Gatekeeper.
+# Uses gon (Xcode version < 13.0) or notarytool (Xcode >= 13.0).
-set -e
-
-gon_version="0.2.2"
-gon_exe="${HOME}/.gon/gon_${gon_version}"
+version() {
+ echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }' || echo "0"
+}
+xcode_version=$(xcodebuild -version | awk '/Xcode/ {print $2}')
+xcode_version_result=$(version "$xcode_version")
+xcode_version_threshold=$(version "13.0")
pkgid="$1"
-[ -z "$pkgid" ] && \
- echo "Usage: $0 " \
+if [ -z "$pkgid" ]; then
+ echo "Usage: $0 "
exit 1
+fi
# shellcheck disable=SC2154
-[ -z "$NOTARIZATION_ID" ] && \
- echo "No NOTARIZATION_ID environment var. Skipping notarization." \
+if [ -z "$NOTARIZATION_ID" ]; then
+ echo "No NOTARIZATION_ID environment variable. Skipping notarization."
exit 0
+fi
-set -x
-
-mkdir -p "${HOME}/.gon/"
+if [ -z "$NOTARIZATION_PASSWORD" ]; then
+ echo "No NOTARIZATION_PASSWORD environment variable. Skipping notarization."
+ exit 0
+fi
-if [ ! -f "${gon_exe}" ]; then
- curl -sL "https://github.com/mitchellh/gon/releases/download/v${gon_version}/gon_${gon_version}_macos.zip" -o "${gon_exe}.zip"
- (cd "${HOME}/.gon/" && rm -f gon && unzip "${gon_exe}.zip" && mv gon "${gon_exe}")
+if [ -z "$NOTARIZATION_TEAM_ID" ]; then
+ echo "No NOTARIZATION_TEAM_ID environment variable. Skipping notarization."
+ exit 0
fi
-sed -e "s/{{appleid}}/${NOTARIZATION_ID}/" -e "s/{{pkgid}}/${pkgid}/" tools/osx-gon-config.json.tmpl \
- > gon-config.json
+# TODO(@ulisesGascon): remove support for gon
+# when https://github.com/nodejs/build/issues/3385#issuecomment-1729281269 is ready
+if [ "$xcode_version_result" -lt "$xcode_version_threshold" ]; then
+ echo "Notarization process is done with gon."
+ set -x
+
+ gon_version="0.2.2"
+ gon_exe="${HOME}/.gon/gon_${gon_version}"
-"${gon_exe}" -log-level=info gon-config.json
+ mkdir -p "${HOME}/.gon/"
+
+ if [ ! -f "${gon_exe}" ]; then
+ curl -sL "https://github.com/mitchellh/gon/releases/download/v${gon_version}/gon_${gon_version}_macos.zip" -o "${gon_exe}.zip"
+ (cd "${HOME}/.gon/" && rm -f gon && unzip "${gon_exe}.zip" && mv gon "${gon_exe}")
+ fi
+
+ sed -e "s/{{appleid}}/${NOTARIZATION_ID}/" -e "s/{{pkgid}}/${pkgid}/" tools/osx-gon-config.json.tmpl \
+ > gon-config.json
+
+ "${gon_exe}" -log-level=info gon-config.json
+
+else
+ echo "Notarization process is done with Notarytool."
+
+ if ! command -v xcrun notarytool > /dev/null
+ then
+ echo "Notarytool is not present in the system. Notarization has failed."
+ exit 1
+ fi
+
+ # Submit the package for notarization
+ # TODO(@ulisesGascon): refactor to use --keychain-profile
+ # when https://github.com/nodejs/build/issues/3385#issuecomment-1729281269 is ready
+ notarization_output=$(
+ xcrun notarytool submit \
+ --apple-id "$NOTARIZATION_ID" \
+ --password "$NOTARIZATION_PASSWORD" \
+ --team-id "$NOTARIZATION_TEAM_ID" \
+ --wait \
+ "node-$pkgid.pkg" 2>&1
+ )
+
+ if [ $? -eq 0 ]; then
+ # Extract the operation ID from the output
+ operation_id=$(echo "$notarization_output" | awk '/RequestUUID/ {print $NF}')
+ echo "Notarization submitted. Operation ID: $operation_id"
+ exit 0
+ else
+ echo "Notarization failed. Error: $notarization_output"
+ exit 1
+ fi
+fi