diff --git a/content/v18.x/en-US/doc/api/assert.md b/content/v18.x/en-US/doc/api/assert.md index 4864023f65d29..a77dd196e0063 100644 --- a/content/v18.x/en-US/doc/api/assert.md +++ b/content/v18.x/en-US/doc/api/assert.md @@ -172,7 +172,7 @@ import assert from 'node:assert'; const { message } = new assert.AssertionError({ actual: 1, expected: 2, - operator: 'strictEqual' + operator: 'strictEqual', }); // Verify error output: @@ -197,7 +197,7 @@ const assert = require('node:assert'); const { message } = new assert.AssertionError({ actual: 1, expected: 2, - operator: 'strictEqual' + operator: 'strictEqual', }); // Verify error output: @@ -647,18 +647,18 @@ import assert from 'node:assert'; const obj1 = { a: { - b: 1 - } + b: 1, + }, }; const obj2 = { a: { - b: 2 - } + b: 2, + }, }; const obj3 = { a: { - b: 1 - } + b: 1, + }, }; const obj4 = Object.create(obj1); @@ -682,18 +682,18 @@ const assert = require('node:assert'); const obj1 = { a: { - b: 1 - } + b: 1, + }, }; const obj2 = { a: { - b: 2 - } + b: 2, + }, }; const obj3 = { a: { - b: 1 - } + b: 1, + }, }; const obj4 = Object.create(obj1); @@ -1062,7 +1062,7 @@ await assert.doesNotReject( async () => { throw new TypeError('Wrong value'); }, - SyntaxError + SyntaxError, ); ``` @@ -1074,7 +1074,7 @@ const assert = require('node:assert/strict'); async () => { throw new TypeError('Wrong value'); }, - SyntaxError + SyntaxError, ); })(); ``` @@ -1144,7 +1144,7 @@ assert.doesNotThrow( () => { throw new TypeError('Wrong value'); }, - SyntaxError + SyntaxError, ); ``` @@ -1155,7 +1155,7 @@ assert.doesNotThrow( () => { throw new TypeError('Wrong value'); }, - SyntaxError + SyntaxError, ); ``` @@ -1169,7 +1169,7 @@ assert.doesNotThrow( () => { throw new TypeError('Wrong value'); }, - TypeError + TypeError, ); ``` @@ -1180,7 +1180,7 @@ assert.doesNotThrow( () => { throw new TypeError('Wrong value'); }, - TypeError + TypeError, ); ``` @@ -1196,7 +1196,7 @@ assert.doesNotThrow( throw new TypeError('Wrong value'); }, /Wrong value/, - 'Whoops' + 'Whoops', ); // Throws: AssertionError: Got unwanted exception: Whoops ``` @@ -1209,7 +1209,7 @@ assert.doesNotThrow( throw new TypeError('Wrong value'); }, /Wrong value/, - 'Whoops' + 'Whoops', ); // Throws: AssertionError: Got unwanted exception: Whoops ``` @@ -1606,18 +1606,18 @@ import assert from 'node:assert'; const obj1 = { a: { - b: 1 - } + b: 1, + }, }; const obj2 = { a: { - b: 2 - } + b: 2, + }, }; const obj3 = { a: { - b: 1 - } + b: 1, + }, }; const obj4 = Object.create(obj1); @@ -1639,18 +1639,18 @@ const assert = require('node:assert'); const obj1 = { a: { - b: 1 - } + b: 1, + }, }; const obj2 = { a: { - b: 2 - } + b: 2, + }, }; const obj3 = { a: { - b: 1 - } + b: 1, + }, }; const obj4 = Object.create(obj1); @@ -2008,8 +2008,8 @@ await assert.rejects( }, { name: 'TypeError', - message: 'Wrong value' - } + message: 'Wrong value', + }, ); ``` @@ -2023,8 +2023,8 @@ const assert = require('node:assert/strict'); }, { name: 'TypeError', - message: 'Wrong value' - } + message: 'Wrong value', + }, ); })(); ``` @@ -2040,7 +2040,7 @@ await assert.rejects( assert.strictEqual(err.name, 'TypeError'); assert.strictEqual(err.message, 'Wrong value'); return true; - } + }, ); ``` @@ -2056,7 +2056,7 @@ const assert = require('node:assert/strict'); assert.strictEqual(err.name, 'TypeError'); assert.strictEqual(err.message, 'Wrong value'); return true; - } + }, ); })(); ``` @@ -2066,7 +2066,7 @@ import assert from 'node:assert/strict'; assert.rejects( Promise.reject(new Error('Wrong value')), - Error + Error, ).then(() => { // ... }); @@ -2077,7 +2077,7 @@ const assert = require('node:assert/strict'); assert.rejects( Promise.reject(new Error('Wrong value')), - Error + Error, ).then(() => { // ... }); @@ -2254,7 +2254,7 @@ err.code = 404; err.foo = 'bar'; err.info = { nested: true, - baz: 'text' + baz: 'text', }; err.reg = /abc/i; @@ -2267,12 +2267,12 @@ assert.throws( message: 'Wrong value', info: { nested: true, - baz: 'text' - } + baz: 'text', + }, // Only properties on the validation object will be tested for. // Using nested objects requires all properties to be present. Otherwise // the validation is going to fail. - } + }, ); // Using regular expressions to validate error properties: @@ -2290,13 +2290,13 @@ assert.throws( info: { nested: true, // It is not possible to use regular expressions for nested properties! - baz: 'text' + baz: 'text', }, // The `reg` property contains a regular expression and only if the // validation object contains an identical regular expression, it is going // to pass. - reg: /abc/i - } + reg: /abc/i, + }, ); // Fails due to the different `message` and `name` properties: @@ -2311,7 +2311,7 @@ assert.throws( }, // The error's `message` and `name` properties will also be checked when using // an error as validation object. - err + err, ); ``` @@ -2323,7 +2323,7 @@ err.code = 404; err.foo = 'bar'; err.info = { nested: true, - baz: 'text' + baz: 'text', }; err.reg = /abc/i; @@ -2336,12 +2336,12 @@ assert.throws( message: 'Wrong value', info: { nested: true, - baz: 'text' - } + baz: 'text', + }, // Only properties on the validation object will be tested for. // Using nested objects requires all properties to be present. Otherwise // the validation is going to fail. - } + }, ); // Using regular expressions to validate error properties: @@ -2359,13 +2359,13 @@ assert.throws( info: { nested: true, // It is not possible to use regular expressions for nested properties! - baz: 'text' + baz: 'text', }, // The `reg` property contains a regular expression and only if the // validation object contains an identical regular expression, it is going // to pass. - reg: /abc/i - } + reg: /abc/i, + }, ); // Fails due to the different `message` and `name` properties: @@ -2380,7 +2380,7 @@ assert.throws( }, // The error's `message` and `name` properties will also be checked when using // an error as validation object. - err + err, ); ``` @@ -2393,7 +2393,7 @@ assert.throws( () => { throw new Error('Wrong value'); }, - Error + Error, ); ``` @@ -2404,7 +2404,7 @@ assert.throws( () => { throw new Error('Wrong value'); }, - Error + Error, ); ``` @@ -2420,7 +2420,7 @@ assert.throws( () => { throw new Error('Wrong value'); }, - /^Error: Wrong value$/ + /^Error: Wrong value$/, ); ``` @@ -2431,7 +2431,7 @@ assert.throws( () => { throw new Error('Wrong value'); }, - /^Error: Wrong value$/ + /^Error: Wrong value$/, ); ``` @@ -2457,7 +2457,7 @@ assert.throws( // possible. return true; }, - 'unexpected error' + 'unexpected error', ); ``` @@ -2478,7 +2478,7 @@ assert.throws( // possible. return true; }, - 'unexpected error' + 'unexpected error', ); ``` diff --git a/content/v18.x/en-US/doc/api/async_context.md b/content/v18.x/en-US/doc/api/async_context.md index 8f79c7364485c..49c5ea564da7d 100644 --- a/content/v18.x/en-US/doc/api/async_context.md +++ b/content/v18.x/en-US/doc/api/async_context.md @@ -116,17 +116,35 @@ Each instance of `AsyncLocalStorage` maintains an independent storage context. Multiple instances can safely exist simultaneously without risk of interfering with each other's data. -### `new AsyncLocalStorage()` +### `new AsyncLocalStorage([options])` +> Stability: 1 - `options.onPropagate` is experimental. + +* `options` {Object} + * `onPropagate` {Function} Optional callback invoked before a store is + propagated to a new async resource. Returning `true` allows propagation, + returning `false` avoids it. Default is to propagate always. + Creates a new instance of `AsyncLocalStorage`. Store is only provided within a `run()` call or after an `enterWith()` call. +The `onPropagate` is called during creation of an async resource. Throwing at +this time will print the stack trace and exit. See +[`async_hooks` Error handling][] for details. + +Creating an async resource within the `onPropagate` callback will result in +a recursive call to `onPropagate`. + ### `asyncLocalStorage.disable()` -> Stability: 1 - Experimental +> Stability: 1 - Experimental. Please migrate away from this API, if you can. +> We do not recommend using the [`createHook`][], [`AsyncHook`][], and +> [`executionAsyncResource`][] APIs as they have usability issues, safety risks, +> and performance implications. Async context tracking use cases are better +> served by the stable [`AsyncLocalStorage`][] API. If you have a use case for +> `createHook`, `AsyncHook`, or `executionAsyncResource` beyond the context +> tracking need solved by [`AsyncLocalStorage`][] or diagnostics data currently +> provided by [Diagnostics Channel][], please open an issue at +> describing your use case so we can +> create a more purpose-focused API. +We strongly discourage the use of the `async_hooks` API. +Other APIs that can cover most of its use cases include: + +* [`AsyncLocalStorage`][] tracks async context +* [`process.getActiveResourcesInfo()`][] tracks active resources + The `node:async_hooks` module provides an API to track asynchronous resources. It can be accessed using: @@ -159,7 +174,7 @@ import { createHook } from 'node:async_hooks'; const asyncHook = createHook({ init(asyncId, type, triggerAsyncId, resource) { }, - destroy(asyncId) { } + destroy(asyncId) { }, }); ``` @@ -168,7 +183,7 @@ const async_hooks = require('node:async_hooks'); const asyncHook = async_hooks.createHook({ init(asyncId, type, triggerAsyncId, resource) { }, - destroy(asyncId) { } + destroy(asyncId) { }, }); ``` @@ -329,18 +344,14 @@ The `type` is a string identifying the type of resource that caused `init` to be called. Generally, it will correspond to the name of the resource's constructor. -Valid values are: - -```text -FSEVENTWRAP, FSREQCALLBACK, GETADDRINFOREQWRAP, GETNAMEINFOREQWRAP, HTTPINCOMINGMESSAGE, -HTTPCLIENTREQUEST, JSSTREAM, PIPECONNECTWRAP, PIPEWRAP, PROCESSWRAP, QUERYWRAP, -SHUTDOWNWRAP, SIGNALWRAP, STATWATCHER, TCPCONNECTWRAP, TCPSERVERWRAP, TCPWRAP, -TTYWRAP, UDPSENDWRAP, UDPWRAP, WRITEWRAP, ZLIB, SSLCONNECTION, PBKDF2REQUEST, -RANDOMBYTESREQUEST, TLSWRAP, Microtask, Timeout, Immediate, TickObject -``` +The `type` of resources created by Node.js itself can change in any Node.js +release. Valid values include `TLSWRAP`, +`TCPWRAP`, `TCPSERVERWRAP`, `GETADDRINFOREQWRAP`, `FSREQCALLBACK`, +`Microtask`, and `Timeout`. Inspect the source code of the Node.js version used +to get the full list. -These values can change in any Node.js release. Furthermore users of [`AsyncResource`][] -likely provide other values. +Furthermore users of [`AsyncResource`][] create async resources independent +of Node.js itself. There is also the `PROMISE` resource type, which is used to track `Promise` instances and asynchronous work scheduled by them. @@ -371,7 +382,7 @@ createHook({ fs.writeSync( stdout.fd, `${type}(${asyncId}): trigger: ${triggerAsyncId} execution: ${eid}\n`); - } + }, }).enable(); net.createServer((conn) => {}).listen(8080); @@ -388,7 +399,7 @@ createHook({ fs.writeSync( stdout.fd, `${type}(${asyncId}): trigger: ${triggerAsyncId} execution: ${eid}\n`); - } + }, }).enable(); net.createServer((conn) => {}).listen(8080); @@ -414,19 +425,19 @@ of propagating what resource is responsible for the new resource's existence. ##### `resource` `resource` is an object that represents the actual async resource that has -been initialized. This can contain useful information that can vary based on -the value of `type`. For instance, for the `GETADDRINFOREQWRAP` resource type, -`resource` provides the host name used when looking up the IP address for the -host in `net.Server.listen()`. The API for accessing this information is -not supported, but using the Embedder API, users can provide -and document their own resource objects. For example, such a resource object -could contain the SQL query being executed. +been initialized. The API to access the object may be specified by the +creator of the resource. Resources created by Node.js itself are internal +and may change at any time. Therefore no API is specified for these. In some cases the resource object is reused for performance reasons, it is thus not safe to use it as a key in a `WeakMap` or add properties to it. ##### Asynchronous context example +The context tracking use case is covered by the stable API [`AsyncLocalStorage`][]. +This example only illustrates async hooks operation but [`AsyncLocalStorage`][] +fits better to this use case. + The following is an example with additional information about the calls to `init` between the `before` and `after` calls, specifically what the callback to `listen()` will look like. The output formatting is slightly more @@ -568,6 +579,9 @@ made to the `resource` object passed to `init` it is possible that `destroy` will never be called, causing a memory leak in the application. If the resource does not depend on garbage collection, then this will not be an issue. +Using the destroy hook results in additional overhead because it enables +tracking of `Promise` instances via the garbage collector. + #### `promiseResolve(asyncId)` * `value` {string|Buffer|Uint8Array|integer} The value with which to fill `buf`. + Empty value (string, Uint8Array, Buffer) is coerced to `0`. * `offset` {integer} Number of bytes to skip before starting to fill `buf`. **Default:** `0`. * `end` {integer} Where to stop filling `buf` (not inclusive). **Default:** @@ -1902,6 +1903,12 @@ const b = Buffer.allocUnsafe(50).fill('h'); console.log(b.toString()); // Prints: hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh + +// Fill a buffer with empty string +const c = Buffer.allocUnsafe(5).fill(''); + +console.log(c.fill('')); +// Prints: ``` ```cjs @@ -1913,6 +1920,12 @@ const b = Buffer.allocUnsafe(50).fill('h'); console.log(b.toString()); // Prints: hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh + +// Fill a buffer with empty string +const c = Buffer.allocUnsafe(5).fill(''); + +console.log(c.fill('')); +// Prints: ``` `value` is coerced to a `uint32` value if it is not a string, `Buffer`, or @@ -5009,6 +5022,56 @@ changes: See [`Buffer.from(string[, encoding])`][`Buffer.from(string)`]. +## Class: `File` + + + +> Stability: 1 - Experimental + +* Extends: {Blob} + +A [`File`][] provides information about files. + +### `new buffer.File(sources, fileName[, options])` + + + +* `sources` {string\[]|ArrayBuffer\[]|TypedArray\[]|DataView\[]|Blob\[]|File\[]} + An array of string, {ArrayBuffer}, {TypedArray}, {DataView}, {File}, or {Blob} + objects, or any mix of such objects, that will be stored within the `File`. +* `fileName` {string} The name of the file. +* `options` {Object} + * `endings` {string} One of either `'transparent'` or `'native'`. When set + to `'native'`, line endings in string source parts will be converted to + the platform native line-ending as specified by `require('node:os').EOL`. + * `type` {string} The File content-type. + * `lastModified` {number} The last modified date of the file. + **Default:** `Date.now()`. + +### `file.name` + + + +* Type: {string} + +The name of the `File`. + +### `file.lastModified` + + + +* Type: {number} + +The last modified date of the `File`. + ## `node:buffer` module APIs While, the `Buffer` object is available as a global, there are additional @@ -5355,6 +5418,7 @@ introducing security vulnerabilities into an application. [`ERR_INVALID_ARG_VALUE`]: errors.md#err_invalid_arg_value [`ERR_INVALID_BUFFER_SIZE`]: errors.md#err_invalid_buffer_size [`ERR_OUT_OF_RANGE`]: errors.md#err_out_of_range +[`File`]: https://developer.mozilla.org/en-US/docs/Web/API/File [`JSON.stringify()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify [`SharedArrayBuffer`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer [`String.prototype.indexOf()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf diff --git a/content/v18.x/en-US/doc/api/child_process.md b/content/v18.x/en-US/doc/api/child_process.md index 557eb3d53a3a3..f2f1755f4a9b2 100644 --- a/content/v18.x/en-US/doc/api/child_process.md +++ b/content/v18.x/en-US/doc/api/child_process.md @@ -272,7 +272,7 @@ const { exec } = require('node:child_process'); const controller = new AbortController(); const { signal } = controller; const child = exec('grep ssh', { signal }, (error) => { - console.log(error); // an AbortError + console.error(error); // an AbortError }); controller.abort(); ``` @@ -384,7 +384,7 @@ const { execFile } = require('node:child_process'); const controller = new AbortController(); const { signal } = controller; const child = execFile('node', ['--version'], { signal }, (error) => { - console.log(error); // an AbortError + console.error(error); // an AbortError }); controller.abort(); ``` @@ -606,7 +606,7 @@ A third argument may be used to specify additional options, with these defaults: ```js const defaults = { cwd: undefined, - env: process.env + env: process.env, }; ``` @@ -749,7 +749,7 @@ const { spawn } = require('node:child_process'); const subprocess = spawn(process.argv[0], ['child_program.js'], { detached: true, - stdio: 'ignore' + stdio: 'ignore', }); subprocess.unref(); @@ -765,7 +765,7 @@ const err = fs.openSync('./out.log', 'a'); const subprocess = spawn('prg', [], { detached: true, - stdio: [ 'ignore', out, err ] + stdio: [ 'ignore', out, err ], }); subprocess.unref(); @@ -1392,8 +1392,8 @@ const subprocess = spawn( console.log(process.pid, 'is alive') }, 500);"`, ], { - stdio: ['inherit', 'inherit', 'inherit'] - } + stdio: ['inherit', 'inherit', 'inherit'], + }, ); setTimeout(() => { @@ -1449,7 +1449,7 @@ const { spawn } = require('node:child_process'); const subprocess = spawn(process.argv[0], ['child_program.js'], { detached: true, - stdio: 'ignore' + stdio: 'ignore', }); subprocess.unref(); @@ -1733,7 +1733,7 @@ const subprocess = child_process.spawn('ls', { 0, // Use parent's stdin for child. 'pipe', // Pipe child's stdout to parent. fs.openSync('err.out', 'w'), // Direct child's stderr to a file. - ] + ], }); assert.strictEqual(subprocess.stdio[0], null); @@ -1796,7 +1796,7 @@ const { spawn } = require('node:child_process'); const subprocess = spawn(process.argv[0], ['child_program.js'], { detached: true, - stdio: 'ignore' + stdio: 'ignore', }); subprocess.unref(); diff --git a/content/v18.x/en-US/doc/api/cli.md b/content/v18.x/en-US/doc/api/cli.md index 7b17162c22ebe..bdc2b5c4fcb47 100644 --- a/content/v18.x/en-US/doc/api/cli.md +++ b/content/v18.x/en-US/doc/api/cli.md @@ -411,10 +411,18 @@ Disable experimental support for the [Fetch API][]. +--> Use this flag to disable top-level await in REPL. +### `--experimental-shadow-realm` + + + +Use this flag to enable [ShadowRealm][] support. + ### `--experimental-specifier-resolution=mode` Starts the Node.js command line test runner. This flag cannot be combined with -`--check`, `--eval`, `--interactive`, or the inspector. See the documentation -on [running tests from the command line][] for more details. +`--watch-path`, `--check`, `--eval`, `--interactive`, or the inspector. +See the documentation on [running tests from the command line][] +for more details. ### `--test-name-pattern` @@ -1533,16 +1555,21 @@ added: v5.10.0 Set V8's thread pool size which will be used to allocate background jobs. -If set to `0` then V8 will choose an appropriate size of the thread pool based -on the number of online processors. +If set to `0` then Node.js will choose an appropriate size of the thread pool +based on an estimate of the amount of parallelism. -If the value provided is larger than V8's maximum, then the largest value -will be chosen. +The amount of parallelism refers to the number of computations that can be +carried out simultaneously in a given machine. In general, it's the same as the +amount of CPUs, but it may diverge in environments such as VMs or containers. ### `--watch` > Stability: 1 - Experimental @@ -1576,7 +1603,7 @@ This will turn off watching of required or imported modules, even when used in combination with `--watch`. This flag cannot be combined with -`--check`, `--eval`, `--interactive`, or the REPL. +`--check`, `--eval`, `--interactive`, `--test`, or the REPL. ```console $ node --watch-path=./src --watch-path=./tests index.js @@ -1586,6 +1613,14 @@ This option is only supported on macOS and Windows. An `ERR_FEATURE_UNAVAILABLE_ON_PLATFORM` exception will be thrown when the option is used on a platform that does not support it. +### `--watch-preserve-output` + +Disable the clearing of the console when watch mode restarts the process. + +```console +$ node --watch --watch-preserve-output test.js +``` + ### `--zero-fill-buffers` -Type: Runtime. +Type: Runtime The implicit suppression of uncaught exceptions in Node-API callbacks is now deprecated. @@ -3264,7 +3267,54 @@ Set the flag [`--force-node-api-uncaught-exceptions-policy`][] to force Node.js to emit an [`'uncaughtException'`][] event if the exception is not handled in Node-API callbacks. -[Legacy URL API]: url.md#legacy-url-api +### DEP0169: Insecure url.parse() + + + +Type: Documentation-only + +[`url.parse()`][] behavior is not standardized and prone to errors that +have security implications. Use the [WHATWG URL API][] instead. CVEs are not +issued for `url.parse()` vulnerabilities. + +### DEP0170: Invalid port when using `url.parse()` + + + +Type: Documentation-only + +[`url.parse()`][] accepts URLs with ports that are not numbers. This behavior +might result in host name spoofing with unexpected input. These URLs will throw +an error in future versions of Node.js, as the [WHATWG URL API][] does already. + +### DEP0171: Setters for `http.IncomingMessage` headers and trailers + + + +Type: Documentation-only + +In a future version of Node.js, [`message.headers`][], +[`message.headersDistinct`][], [`message.trailers`][], and +[`message.trailersDistinct`][] will be read-only. + [NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf [RFC 6066]: https://tools.ietf.org/html/rfc6066#section-3 [RFC 8247 Section 2.4]: https://www.rfc-editor.org/rfc/rfc8247#section-2.4 @@ -3341,7 +3391,11 @@ Node-API callbacks. [`https.get()`]: https.md#httpsgetoptions-callback [`https.request()`]: https.md#httpsrequestoptions-callback [`message.connection`]: http.md#messageconnection +[`message.headersDistinct`]: http.md#messageheadersdistinct +[`message.headers`]: http.md#messageheaders [`message.socket`]: http.md#messagesocket +[`message.trailersDistinct`]: http.md#messagetrailersdistinct +[`message.trailers`]: http.md#messagetrailers [`module.createRequire()`]: module.md#modulecreaterequirefilename [`os.networkInterfaces()`]: os.md#osnetworkinterfaces [`os.tmpdir()`]: os.md#ostmpdir @@ -3410,6 +3464,7 @@ Node-API callbacks. [alloc_unsafe_size]: buffer.md#static-method-bufferallocunsafesize [from_arraybuffer]: buffer.md#static-method-bufferfromarraybuffer-byteoffset-length [from_string_encoding]: buffer.md#static-method-bufferfromstring-encoding +[legacy URL API]: url.md#legacy-url-api [legacy `urlObject`]: url.md#legacy-urlobject [static methods of `crypto.Certificate()`]: crypto.md#class-certificate [subpath exports]: packages.md#subpath-exports diff --git a/content/v18.x/en-US/doc/api/dgram.md b/content/v18.x/en-US/doc/api/dgram.md index 4c188629e2bb2..d984e871af553 100644 --- a/content/v18.x/en-US/doc/api/dgram.md +++ b/content/v18.x/en-US/doc/api/dgram.md @@ -16,7 +16,7 @@ import dgram from 'node:dgram'; const server = dgram.createSocket('udp4'); server.on('error', (err) => { - console.log(`server error:\n${err.stack}`); + console.error(`server error:\n${err.stack}`); server.close(); }); @@ -38,7 +38,7 @@ const dgram = require('node:dgram'); const server = dgram.createSocket('udp4'); server.on('error', (err) => { - console.log(`server error:\n${err.stack}`); + console.error(`server error:\n${err.stack}`); server.close(); }); @@ -268,7 +268,7 @@ import dgram from 'node:dgram'; const server = dgram.createSocket('udp4'); server.on('error', (err) => { - console.log(`server error:\n${err.stack}`); + console.error(`server error:\n${err.stack}`); server.close(); }); @@ -290,7 +290,7 @@ const dgram = require('node:dgram'); const server = dgram.createSocket('udp4'); server.on('error', (err) => { - console.log(`server error:\n${err.stack}`); + console.error(`server error:\n${err.stack}`); server.close(); }); @@ -357,7 +357,7 @@ An example socket listening on an exclusive port is shown below. socket.bind({ address: 'localhost', port: 8000, - exclusive: true + exclusive: true, }); ``` diff --git a/content/v18.x/en-US/doc/api/diagnostics_channel.md b/content/v18.x/en-US/doc/api/diagnostics_channel.md index 761a0567a1435..ac48ad78ee615 100644 --- a/content/v18.x/en-US/doc/api/diagnostics_channel.md +++ b/content/v18.x/en-US/doc/api/diagnostics_channel.md @@ -1,8 +1,16 @@ # Diagnostics Channel + + -> Stability: 1 - Experimental +> Stability: 2 - Stable @@ -54,7 +62,7 @@ diagnostics_channel.subscribe('my-channel', onMessage); if (channel.hasSubscribers) { // Publish data to the channel channel.publish({ - some: 'data' + some: 'data', }); } @@ -79,7 +87,7 @@ diagnostics_channel.subscribe('my-channel', onMessage); if (channel.hasSubscribers) { // Publish data to the channel channel.publish({ - some: 'data' + some: 'data', }); } @@ -164,7 +172,7 @@ will be run synchronously whenever a message is published to the channel. Any errors thrown in the message handler will trigger an [`'uncaughtException'`][]. ```mjs -import diagnostics_channel from 'diagnostics_channel'; +import diagnostics_channel from 'node:diagnostics_channel'; diagnostics_channel.subscribe('my-channel', (message, name) => { // Received data @@ -172,7 +180,7 @@ diagnostics_channel.subscribe('my-channel', (message, name) => { ``` ```cjs -const diagnostics_channel = require('diagnostics_channel'); +const diagnostics_channel = require('node:diagnostics_channel'); diagnostics_channel.subscribe('my-channel', (message, name) => { // Received data @@ -194,7 +202,7 @@ Remove a message handler previously registered to this channel with [`diagnostics_channel.subscribe(name, onMessage)`][]. ```mjs -import diagnostics_channel from 'diagnostics_channel'; +import diagnostics_channel from 'node:diagnostics_channel'; function onMessage(message, name) { // Received data @@ -206,7 +214,7 @@ diagnostics_channel.unsubscribe('my-channel', onMessage); ``` ```cjs -const diagnostics_channel = require('diagnostics_channel'); +const diagnostics_channel = require('node:diagnostics_channel'); function onMessage(message, name) { // Received data @@ -288,7 +296,7 @@ import diagnostics_channel from 'node:diagnostics_channel'; const channel = diagnostics_channel.channel('my-channel'); channel.publish({ - some: 'message' + some: 'message', }); ``` @@ -298,7 +306,7 @@ const diagnostics_channel = require('node:diagnostics_channel'); const channel = diagnostics_channel.channel('my-channel'); channel.publish({ - some: 'message' + some: 'message', }); ``` @@ -395,6 +403,12 @@ channel.unsubscribe(onMessage); ### Built-in Channels +> Stability: 1 - Experimental + +While the diagnostics\_channel API is now considered stable, the built-in +channels currently available are not. Each channel must be declared stable +independently. + #### HTTP `http.client.request.start` diff --git a/content/v18.x/en-US/doc/api/errors.md b/content/v18.x/en-US/doc/api/errors.md index 2c2618b42b603..9f724cf044dca 100644 --- a/content/v18.x/en-US/doc/api/errors.md +++ b/content/v18.x/en-US/doc/api/errors.md @@ -1967,6 +1967,12 @@ An invalid HTTP token was supplied. An IP address is not valid. + + +### `ERR_INVALID_MIME_SYNTAX` + +The syntax of a MIME is not valid. + ### `ERR_INVALID_MODULE` @@ -2676,6 +2682,25 @@ An unspecified or non-specific system error has occurred within the Node.js process. The error object will have an `err.info` object property with additional details. + + +### `ERR_TAP_LEXER_ERROR` + +An error representing a failing lexer state. + + + +### `ERR_TAP_PARSER_ERROR` + +An error representing a failing parser state. Additional information about +the token causing the error is available via the `cause` property. + + + +### `ERR_TAP_VALIDATION_ERROR` + +This error represents a failed TAP validation. + ### `ERR_TEST_FAILURE` diff --git a/content/v18.x/en-US/doc/api/esm.md b/content/v18.x/en-US/doc/api/esm.md index 8360c6f821b25..eab0f3957eee2 100644 --- a/content/v18.x/en-US/doc/api/esm.md +++ b/content/v18.x/en-US/doc/api/esm.md @@ -485,9 +485,9 @@ These CommonJS variables are not available in ES modules. `__filename` and `__dirname` use cases can be replicated via [`import.meta.url`][]. -#### No Native Module Loading +#### No Addon Loading -Native modules are not currently supported with ES module imports. +[Addons][] are not currently supported with ES module imports. They can instead be loaded with [`module.createRequire()`][] or [`process.dlopen`][]. @@ -1016,7 +1016,7 @@ export function resolve(specifier, context, nextResolve) { if (specifier.startsWith('https://')) { return { shortCircuit: true, - url: specifier + url: specifier, }; } else if (parentURL && parentURL.startsWith('https://')) { return { @@ -1097,7 +1097,7 @@ export async function resolve(specifier, context, nextResolve) { // specifiers ending in the CoffeeScript file extensions. return { shortCircuit: true, - url: new URL(specifier, parentURL).href + url: new URL(specifier, parentURL).href, }; } @@ -1539,6 +1539,7 @@ success! [6.1.7 Array Index]: https://tc39.es/ecma262/#integer-index +[Addons]: addons.md [CommonJS]: modules.md [Conditional exports]: packages.md#conditional-exports [Core modules]: modules.md#core-modules diff --git a/content/v18.x/en-US/doc/api/events.md b/content/v18.x/en-US/doc/api/events.md index c488694d8bef9..7ddd624dd9564 100644 --- a/content/v18.x/en-US/doc/api/events.md +++ b/content/v18.x/en-US/doc/api/events.md @@ -68,10 +68,11 @@ myEmitter.on('event', function(a, b) { console.log(a, b, this, this === myEmitter); // Prints: // a b MyEmitter { - // domain: null, - // _events: { event: [Function] }, + // _events: [Object: null prototype] { event: [Function (anonymous)] }, // _eventsCount: 1, - // _maxListeners: undefined } true + // _maxListeners: undefined, + // [Symbol(kCapture)]: false + // } true }); myEmitter.emit('event', 'a', 'b'); ``` @@ -1041,7 +1042,7 @@ process.nextTick(() => { try { await once(ee, 'myevent'); } catch (err) { - console.log('error happened', err); + console.error('error happened', err); } ``` @@ -1066,7 +1067,7 @@ async function run() { try { await once(ee, 'myevent'); } catch (err) { - console.log('error happened', err); + console.error('error happened', err); } } @@ -1085,7 +1086,7 @@ const ee = new EventEmitter(); once(ee, 'error') .then(([err]) => console.log('ok', err.message)) - .catch((err) => console.log('error', err.message)); + .catch((err) => console.error('error', err.message)); ee.emit('error', new Error('boom')); @@ -1099,7 +1100,7 @@ const ee = new EventEmitter(); once(ee, 'error') .then(([err]) => console.log('ok', err.message)) - .catch((err) => console.log('error', err.message)); + .catch((err) => console.error('error', err.message)); ee.emit('error', new Error('boom')); @@ -1465,7 +1466,7 @@ setMaxListeners(5, target, emitter); ```cjs const { setMaxListeners, - EventEmitter + EventEmitter, } = require('node:events'); const target = new EventTarget(); @@ -1688,13 +1689,13 @@ async function handler2(event) { const handler3 = { handleEvent(event) { console.log(event.type); // Prints 'foo' - } + }, }; const handler4 = { async handleEvent(event) { console.log(event.type); // Prints 'foo' - } + }, }; const target = new EventTarget(); @@ -1974,7 +1975,7 @@ Dispatches the `event` to the list of handlers for `event.type`. The registered event listeners is synchronously invoked in the order they were registered. -#### `eventTarget.removeEventListener(type, listener)` +#### `eventTarget.removeEventListener(type, listener[, options])` @@ -937,7 +937,7 @@ try { await copyFile('source.txt', 'destination.txt'); console.log('source.txt was copied to destination.txt'); } catch { - console.log('The file could not be copied'); + console.error('The file could not be copied'); } // By using COPYFILE_EXCL, the operation will fail if destination.txt exists. @@ -945,7 +945,7 @@ try { await copyFile('source.txt', 'destination.txt', constants.COPYFILE_EXCL); console.log('source.txt was copied to destination.txt'); } catch { - console.log('The file could not be copied'); + console.error('The file could not be copied'); } ``` @@ -971,6 +971,9 @@ changes: * `filter` {Function} Function to filter copied files/directories. Return `true` to copy the item, `false` to ignore it. Can also return a `Promise` that resolves to `true` or `false` **Default:** `undefined`. + * `src` {string} source path to copy. + * `dest` {string} destination path to copy to. + * Returns: {boolean|Promise} * `force` {boolean} overwrite existing file or directory. The copy operation will ignore errors if you set this to false and the destination exists. Use the `errorOnExist` option to change this behavior. @@ -1719,6 +1722,12 @@ system requests but rather the internal buffering `fs.writeFile` performs. ### `fsPromises.constants` + + * {Object} Returns an object containing commonly used constants for file system @@ -2188,7 +2197,7 @@ changes: `ERR_INVALID_CALLBACK`. - version: v14.0.0 pr-url: https://github.com/nodejs/node/pull/27044 - description: Changed 'flags' argument to 'mode' and imposed + description: Changed `flags` argument to `mode` and imposed stricter type validation. --> @@ -2259,6 +2268,9 @@ changes: * `filter` {Function} Function to filter copied files/directories. Return `true` to copy the item, `false` to ignore it. Can also return a `Promise` that resolves to `true` or `false` **Default:** `undefined`. + * `src` {string} source path to copy. + * `dest` {string} destination path to copy to. + * Returns: {boolean|Promise} * `force` {boolean} overwrite existing file or directory. The copy operation will ignore errors if you set this to false and the destination exists. Use the `errorOnExist` option to change this behavior. @@ -5012,7 +5024,7 @@ added: v8.5.0 changes: - version: v14.0.0 pr-url: https://github.com/nodejs/node/pull/27044 - description: Changed 'flags' argument to 'mode' and imposed + description: Changed `flags` argument to `mode` and imposed stricter type validation. --> @@ -5071,6 +5083,9 @@ changes: exists, throw an error. **Default:** `false`. * `filter` {Function} Function to filter copied files/directories. Return `true` to copy the item, `false` to ignore it. **Default:** `undefined` + * `src` {string} source path to copy. + * `dest` {string} destination path to copy to. + * Returns: {boolean} * `force` {boolean} overwrite existing file or directory. The copy operation will ignore errors if you set this to false and the destination exists. Use the `errorOnExist` option to change this behavior. @@ -7003,7 +7018,7 @@ import { open, constants } from 'node:fs'; const { O_RDWR, O_CREAT, - O_EXCL + O_EXCL, } = constants; open('/path/to/my/file', O_RDWR | O_CREAT | O_EXCL, (err, fd) => { diff --git a/content/v18.x/en-US/doc/api/globals.md b/content/v18.x/en-US/doc/api/globals.md index 1669083176f41..e8c8a8020ed22 100644 --- a/content/v18.x/en-US/doc/api/globals.md +++ b/content/v18.x/en-US/doc/api/globals.md @@ -435,6 +435,10 @@ This variable may appear to be global but is not. See [`exports`][]. > Stability: 1 - Experimental. Disable this API with the [`--no-experimental-fetch`][] @@ -446,6 +450,10 @@ A browser-compatible implementation of the [`fetch()`][] function. > Stability: 1 - Experimental. Disable this API with the [`--no-experimental-fetch`][] @@ -472,6 +480,10 @@ Node.js this is different. The top-level scope is not the global scope; > Stability: 1 - Experimental. Disable this API with the [`--no-experimental-fetch`][] @@ -641,6 +653,10 @@ This variable may appear to be global but is not. See [`require()`][]. > Stability: 1 - Experimental. Disable this API with the [`--no-experimental-fetch`][] @@ -652,6 +668,10 @@ A browser-compatible implementation of {Response}. > Stability: 1 - Experimental. Disable this API with the [`--no-experimental-fetch`][] diff --git a/content/v18.x/en-US/doc/api/http.md b/content/v18.x/en-US/doc/api/http.md index 9144f8aec7f3b..7758504ad6334 100644 --- a/content/v18.x/en-US/doc/api/http.md +++ b/content/v18.x/en-US/doc/api/http.md @@ -106,7 +106,7 @@ http.get({ hostname: 'localhost', port: 80, path: '/', - agent: false // Create a new agent just for this one request + agent: false, // Create a new agent just for this one request }, (res) => { // Do stuff with response }); @@ -502,7 +502,7 @@ proxy.listen(1337, '127.0.0.1', () => { port: 1337, host: '127.0.0.1', method: 'CONNECT', - path: 'www.google.com:80' + path: 'www.google.com:80', }; const req = http.request(options); @@ -573,7 +573,7 @@ const http = require('node:http'); const options = { host: '127.0.0.1', port: 8080, - path: '/length_request' + path: '/length_request', }; // Make a request @@ -671,8 +671,8 @@ server.listen(1337, '127.0.0.1', () => { host: '127.0.0.1', headers: { 'Connection': 'Upgrade', - 'Upgrade': 'websocket' - } + 'Upgrade': 'websocket', + }, }; const req = http.request(options); @@ -748,12 +748,15 @@ See [`writable.cork()`][]. -* `data` {string|Buffer} +* `data` {string|Buffer|Uint8Array} * `encoding` {string} * `callback` {Function} * Returns: {this} @@ -1203,9 +1206,13 @@ before the [`'finish'`][] event is emitted. -* `chunk` {string|Buffer} +* `chunk` {string|Buffer|Uint8Array} * `encoding` {string} * `callback` {Function} * Returns: {boolean} @@ -1727,12 +1734,15 @@ See [`writable.cork()`][]. -* `data` {string|Buffer} +* `data` {string|Buffer|Uint8Array} * `encoding` {string} * `callback` {Function} * Returns: {this} @@ -2081,9 +2091,13 @@ before the [`'finish'`][] event is emitted. -* `chunk` {string|Buffer} +* `chunk` {string|Buffer|Uint8Array} * `encoding` {string} **Default:** `'utf8'` * `callback` {Function} * Returns: {boolean} @@ -2158,11 +2172,13 @@ const earlyHintsLinks = [ ]; response.writeEarlyHints({ 'link': earlyHintsLinks, - 'x-trace-id': 'id for diagnostics' + 'x-trace-id': 'id for diagnostics', }); const earlyHintsCallback = () => console.log('early hints message sent'); -response.writeEarlyHints(earlyHintsLinks, earlyHintsCallback); +response.writeEarlyHints({ + 'link': earlyHintsLinks, +}, earlyHintsCallback); ``` ### `response.writeHead(statusCode[, statusMessage][, headers])` @@ -2209,7 +2225,7 @@ const body = 'hello world'; response .writeHead(200, { 'Content-Length': Buffer.byteLength(body), - 'Content-Type': 'text/plain' + 'Content-Type': 'text/plain', }) .end(body); ``` @@ -2346,7 +2362,7 @@ server fully transmitted a message before a connection was terminated: const req = http.request({ host: '127.0.0.1', port: 8080, - method: 'POST' + method: 'POST', }, (res) => { res.resume(); res.on('end', () => { @@ -2763,11 +2779,14 @@ and is connected, that socket will be destroyed as well. -* `chunk` {string | Buffer} +* `chunk` {string|Buffer|Uint8Array} * `encoding` {string} Optional, **Default**: `utf8` * `callback` {Function} Optional * Returns: {this} @@ -3023,11 +3042,14 @@ Always `false`. -* `chunk` {string | Buffer} +* `chunk` {string|Buffer|Uint8Array} * `encoding` {string} **Default**: `utf8` * `callback` {Function} * Returns {boolean} @@ -3159,7 +3181,7 @@ const http = require('node:http'); const server = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ - data: 'Hello World!' + data: 'Hello World!', })); }); @@ -3176,7 +3198,7 @@ const server = http.createServer(); server.on('request', (request, res) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ - data: 'Hello World!' + data: 'Hello World!', })); }); @@ -3258,7 +3280,7 @@ http.get('http://localhost:8000/', (res) => { const server = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ - data: 'Hello World!' + data: 'Hello World!', })); }); @@ -3411,7 +3433,7 @@ upload a file with a POST request, then write to the `ClientRequest` object. const http = require('node:http'); const postData = JSON.stringify({ - 'msg': 'Hello World!' + 'msg': 'Hello World!', }); const options = { @@ -3421,8 +3443,8 @@ const options = { method: 'POST', headers: { 'Content-Type': 'application/json', - 'Content-Length': Buffer.byteLength(postData) - } + 'Content-Length': Buffer.byteLength(postData), + }, }; const req = http.request(options, (res) => { diff --git a/content/v18.x/en-US/doc/api/http2.md b/content/v18.x/en-US/doc/api/http2.md index 0964c91e4e0f6..71aeeb536316c 100644 --- a/content/v18.x/en-US/doc/api/http2.md +++ b/content/v18.x/en-US/doc/api/http2.md @@ -43,7 +43,7 @@ let http2; try { http2 = require('node:http2'); } catch (err) { - console.log('http2 support is disabled!'); + console.error('http2 support is disabled!'); } ``` @@ -61,7 +61,7 @@ let http2; try { http2 = await import('node:http2'); } catch (err) { - console.log('http2 support is disabled!'); + console.error('http2 support is disabled!'); } ``` @@ -90,7 +90,7 @@ const fs = require('node:fs'); const server = http2.createSecureServer({ key: fs.readFileSync('localhost-privkey.pem'), - cert: fs.readFileSync('localhost-cert.pem') + cert: fs.readFileSync('localhost-cert.pem'), }); server.on('error', (err) => console.error(err)); @@ -98,7 +98,7 @@ server.on('stream', (stream, headers) => { // stream is a Duplex stream.respond({ 'content-type': 'text/html; charset=utf-8', - ':status': 200 + ':status': 200, }); stream.end('

Hello World

'); }); @@ -121,7 +121,7 @@ The following illustrates an HTTP/2 client: const http2 = require('node:http2'); const fs = require('node:fs'); const client = http2.connect('https://localhost:8443', { - ca: fs.readFileSync('localhost-cert.pem') + ca: fs.readFileSync('localhost-cert.pem'), }); client.on('error', (err) => console.error(err)); @@ -327,7 +327,7 @@ session.on('stream', (stream, headers, flags) => { // ... stream.respond({ ':status': 200, - 'content-type': 'text/plain; charset=utf-8' + 'content-type': 'text/plain; charset=utf-8', }); stream.write('hello '); stream.end('world'); @@ -348,7 +348,7 @@ const server = http2.createServer(); server.on('stream', (stream, headers) => { stream.respond({ 'content-type': 'text/html; charset=utf-8', - ':status': 200 + ':status': 200, }); stream.on('error', (error) => console.error(error)); stream.end('

Hello World

'); @@ -971,7 +971,7 @@ const http2 = require('node:http2'); const clientSession = http2.connect('https://localhost:1234'); const { HTTP2_HEADER_PATH, - HTTP2_HEADER_STATUS + HTTP2_HEADER_STATUS, } = http2.constants; const req = clientSession.request({ [HTTP2_HEADER_PATH]: '/' }); @@ -1038,7 +1038,7 @@ encoding. ```js stream.respond({ 'content-type': 'text/html; charset=utf-8', - ':status': 200 + ':status': 200, }); ``` @@ -1731,7 +1731,7 @@ server.on('stream', (stream) => { const headers = { 'content-length': stat.size, 'last-modified': stat.mtime.toUTCString(), - 'content-type': 'text/plain; charset=utf-8' + 'content-type': 'text/plain; charset=utf-8', }; stream.respondWithFD(fd, headers); stream.on('close', () => fs.closeSync(fd)); @@ -1776,7 +1776,7 @@ server.on('stream', (stream) => { const headers = { 'content-length': stat.size, 'last-modified': stat.mtime.toUTCString(), - 'content-type': 'text/plain; charset=utf-8' + 'content-type': 'text/plain; charset=utf-8', }; stream.respondWithFD(fd, headers, { waitForTrailers: true }); stream.on('wantTrailers', () => { @@ -1850,7 +1850,7 @@ server.on('stream', (stream) => { } } catch (err) { // Perform actual error handling. - console.log(err); + console.error(err); } stream.end(); } @@ -2022,7 +2022,7 @@ const { HTTP2_HEADER_METHOD, HTTP2_HEADER_PATH, HTTP2_HEADER_STATUS, - HTTP2_HEADER_CONTENT_TYPE + HTTP2_HEADER_CONTENT_TYPE, } = http2.constants; const server = http2.createServer(); @@ -2032,7 +2032,7 @@ server.on('stream', (stream, headers, flags) => { // ... stream.respond({ [HTTP2_HEADER_STATUS]: 200, - [HTTP2_HEADER_CONTENT_TYPE]: 'text/plain; charset=utf-8' + [HTTP2_HEADER_CONTENT_TYPE]: 'text/plain; charset=utf-8', }); stream.write('hello '); stream.end('world'); @@ -2242,7 +2242,7 @@ const { HTTP2_HEADER_METHOD, HTTP2_HEADER_PATH, HTTP2_HEADER_STATUS, - HTTP2_HEADER_CONTENT_TYPE + HTTP2_HEADER_CONTENT_TYPE, } = http2.constants; const options = getOptionsSomehow(); @@ -2254,7 +2254,7 @@ server.on('stream', (stream, headers, flags) => { // ... stream.respond({ [HTTP2_HEADER_STATUS]: 200, - [HTTP2_HEADER_CONTENT_TYPE]: 'text/plain; charset=utf-8' + [HTTP2_HEADER_CONTENT_TYPE]: 'text/plain; charset=utf-8', }); stream.write('hello '); stream.end('world'); @@ -2513,7 +2513,7 @@ const server = http2.createServer(); server.on('stream', (stream, headers) => { stream.respond({ 'content-type': 'text/html; charset=utf-8', - ':status': 200 + ':status': 200, }); stream.end('

Hello World

'); }); @@ -2645,7 +2645,7 @@ const fs = require('node:fs'); const options = { key: fs.readFileSync('server-key.pem'), - cert: fs.readFileSync('server-cert.pem') + cert: fs.readFileSync('server-cert.pem'), }; // Create a secure HTTP/2 server @@ -2654,7 +2654,7 @@ const server = http2.createSecureServer(options); server.on('stream', (stream, headers) => { stream.respond({ 'content-type': 'text/html; charset=utf-8', - ':status': 200 + ':status': 200, }); stream.end('

Hello World

'); }); @@ -2875,7 +2875,7 @@ to send more than one value per header field). const headers = { ':status': '200', 'content-type': 'text-plain', - 'ABC': ['has', 'more', 'than', 'one', 'value'] + 'ABC': ['has', 'more', 'than', 'one', 'value'], }; stream.respond(headers); @@ -2926,7 +2926,7 @@ const headers = { 'content-type': 'text-plain', 'cookie': 'some-cookie', 'other-sensitive-header': 'very secret data', - [http2.sensitiveHeaders]: ['cookie', 'other-sensitive-header'] + [http2.sensitiveHeaders]: ['cookie', 'other-sensitive-header'], }; stream.respond(headers); @@ -3112,7 +3112,7 @@ const client = http2.connect('http://localhost:8001'); // for CONNECT requests or an error will be thrown. const req = client.request({ ':method': 'CONNECT', - ':authority': `localhost:${port}` + ':authority': `localhost:${port}`, }); req.on('response', (headers) => { @@ -3208,7 +3208,7 @@ const key = readFileSync('./key.pem'); const server = createSecureServer( { cert, key, allowHTTP1: true }, - onRequest + onRequest, ).listen(4443); function onRequest(req, res) { @@ -3218,7 +3218,7 @@ function onRequest(req, res) { res.writeHead(200, { 'content-type': 'application/json' }); res.end(JSON.stringify({ alpnProtocol, - httpVersion: req.httpVersion + httpVersion: req.httpVersion, })); } ``` @@ -3773,7 +3773,7 @@ Removes a header that has been queued for implicit sending. response.removeHeader('Content-Encoding'); ``` -### `response.req` +#### `response.req` -* `links` {string|Array} +* `hints` {Object} Sends a status `103 Early Hints` to the client with a Link header, indicating that the user agent can preload/preconnect the linked resources. -The `links` can be a string or an array of strings containing the values -of the `Link` header. +The `hints` is an object containing the values of headers to be sent with +early hints message. **Example** ```js const earlyHintsLink = '; rel=preload; as=style'; -response.writeEarlyHints(earlyHintsLink); +response.writeEarlyHints({ + 'link': earlyHintsLink, +}); const earlyHintsLinks = [ '; rel=preload; as=style', '; rel=preload; as=script', ]; -response.writeEarlyHints(earlyHintsLinks); +response.writeEarlyHints({ + 'link': earlyHintsLinks, +}); ``` #### `response.writeHead(statusCode[, statusMessage][, headers])` diff --git a/content/v18.x/en-US/doc/api/https.md b/content/v18.x/en-US/doc/api/https.md index 750fef7a8f7f2..c56aa9bf94b38 100644 --- a/content/v18.x/en-US/doc/api/https.md +++ b/content/v18.x/en-US/doc/api/https.md @@ -24,7 +24,7 @@ let https; try { https = require('node:https'); } catch (err) { - console.log('https support is disabled!'); + console.error('https support is disabled!'); } ``` @@ -42,7 +42,7 @@ let https; try { https = await import('node:https'); } catch (err) { - console.log('https support is disabled!'); + console.error('https support is disabled!'); } ``` @@ -236,7 +236,7 @@ const fs = require('node:fs'); const options = { key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'), - cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem') + cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem'), }; https.createServer(options, (req, res) => { @@ -253,7 +253,7 @@ const fs = require('node:fs'); const options = { pfx: fs.readFileSync('test/fixtures/test_cert.pfx'), - passphrase: 'sample' + passphrase: 'sample', }; https.createServer(options, (req, res) => { @@ -375,7 +375,7 @@ const options = { hostname: 'encrypted.google.com', port: 443, path: '/', - method: 'GET' + method: 'GET', }; const req = https.request(options, (res) => { @@ -402,7 +402,7 @@ const options = { path: '/', method: 'GET', key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'), - cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem') + cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem'), }; options.agent = new https.Agent(options); @@ -421,7 +421,7 @@ const options = { method: 'GET', key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'), cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem'), - agent: false + agent: false, }; const req = https.request(options, (res) => { diff --git a/content/v18.x/en-US/doc/api/modules.md b/content/v18.x/en-US/doc/api/modules.md index 6f663d473c360..eb54d7921f29a 100644 --- a/content/v18.x/en-US/doc/api/modules.md +++ b/content/v18.x/en-US/doc/api/modules.md @@ -90,8 +90,8 @@ By default, Node.js will treat the following as CommonJS modules: * Files with an extension that is not `.mjs`, `.cjs`, `.json`, `.node`, or `.js` (when the nearest parent `package.json` file contains a top-level field [`"type"`][] with a value of `"module"`, those files will be recognized as - CommonJS modules only if they are being `require`d, not when used as the - command-line entry point of the program). + CommonJS modules only if they are being included via `require()`, not when + used as the command-line entry point of the program). See [Determining module system][] for more details. @@ -272,15 +272,10 @@ LOAD_PACKAGE_SELF(X, DIR) 6. RESOLVE_ESM_MATCH(MATCH) RESOLVE_ESM_MATCH(MATCH) -1. let { RESOLVED, EXACT } = MATCH -2. let RESOLVED_PATH = fileURLToPath(RESOLVED) -3. If EXACT is true, - a. If the file at RESOLVED_PATH exists, load RESOLVED_PATH as its extension - format. STOP -4. Otherwise, if EXACT is false, - a. LOAD_AS_FILE(RESOLVED_PATH) - b. LOAD_AS_DIRECTORY(RESOLVED_PATH) -5. THROW "not found" +1. let RESOLVED_PATH = fileURLToPath(MATCH) +2. If the file at RESOLVED_PATH exists, load RESOLVED_PATH as its extension + format. STOP +3. THROW "not found" ## Caching diff --git a/content/v18.x/en-US/doc/api/n-api.md b/content/v18.x/en-US/doc/api/n-api.md index 056634cbe6aec..bc70638f3443f 100644 --- a/content/v18.x/en-US/doc/api/n-api.md +++ b/content/v18.x/en-US/doc/api/n-api.md @@ -579,6 +579,7 @@ typedef enum { napi_arraybuffer_expected, napi_detachable_arraybuffer_expected, napi_would_deadlock, /* unused */ + napi_no_external_buffers_allowed } napi_status; ``` @@ -897,6 +898,24 @@ typedef void (*napi_threadsafe_function_call_js)(napi_env env, Unless for reasons discussed in [Object Lifetime Management][], creating a handle and/or callback scope inside the function body is not necessary. +#### `napi_cleanup_hook` + + + +Function pointer used with [`napi_add_env_cleanup_hook`][]. It will be called +when the environment is being torn down. + +Callback functions must satisfy the following signature: + +```c +typedef void (*napi_cleanup_hook)(void* data); +``` + +* `[in] data`: The data that was passed to [`napi_add_env_cleanup_hook`][]. + #### `napi_async_cleanup_hook` + +* Returns {string} + +Returns the machine type as a string, such as `arm`, `arm64`, `aarch64`, +`mips`, `mips64`, `ppc64`, `ppc64le`, `s390`, `s390x`, `i386`, `i686`, `x86_64`. + +On POSIX systems, the machine type is determined by calling +[`uname(3)`][]. On Windows, `RtlGetVersion()` is used, and if it is not +available, `GetVersionExW()` will be used. See + for more information. + ## `os.networkInterfaces()` - -* Returns {string} - -Returns the machine type as a string, such as `arm`, `aarch64`, `mips`, -`mips64`, `ppc64`, `ppc64le`, `s390`, `s390x`, `i386`, `i686`, `x86_64`. - -On POSIX systems, the machine type is determined by calling -[`uname(3)`][]. On Windows, `RtlGetVersion()` is used, and if it is not -available, `GetVersionExW()` will be used. See - for more information. - ## OS constants The following constants are exported by `os.constants`. diff --git a/content/v18.x/en-US/doc/api/packages.md b/content/v18.x/en-US/doc/api/packages.md index 52ba6f432bbf8..aadfb76515271 100644 --- a/content/v18.x/en-US/doc/api/packages.md +++ b/content/v18.x/en-US/doc/api/packages.md @@ -706,6 +706,9 @@ is provided below to assist with ecosystem coordination. the given export. _This condition should always be included first._ * `"deno"` - indicates a variation for the Deno platform. * `"browser"` - any web browser environment. +* `"react-native"` - will be matched by the React Native framework (all + platforms). _To target React Native for Web, `"browser"` should be specified + before this condition._ * `"development"` - can be used to define a development-only environment entry point, for example to provide additional debugging context such as better error messages when running in a development mode. _Must always be @@ -1033,7 +1036,7 @@ CommonJS and ES module instances of the package: // ./node_modules/pkg/index.mjs import state from './state.cjs'; export { - state + state, }; ``` diff --git a/content/v18.x/en-US/doc/api/path.md b/content/v18.x/en-US/doc/api/path.md index 5b66e54f9393d..08d6a97421daf 100644 --- a/content/v18.x/en-US/doc/api/path.md +++ b/content/v18.x/en-US/doc/api/path.md @@ -234,7 +234,7 @@ For example, on POSIX: path.format({ root: '/ignored', dir: '/home/user/dir', - base: 'file.txt' + base: 'file.txt', }); // Returns: '/home/user/dir/file.txt' @@ -244,7 +244,7 @@ path.format({ path.format({ root: '/', base: 'file.txt', - ext: 'ignored' + ext: 'ignored', }); // Returns: '/file.txt' @@ -252,7 +252,7 @@ path.format({ path.format({ root: '/', name: 'file', - ext: '.txt' + ext: '.txt', }); // Returns: '/file.txt' ``` @@ -262,7 +262,7 @@ On Windows: ```js path.format({ dir: 'C:\\path\\dir', - base: 'file.txt' + base: 'file.txt', }); // Returns: 'C:\\path\\dir\\file.txt' ``` diff --git a/content/v18.x/en-US/doc/api/perf_hooks.md b/content/v18.x/en-US/doc/api/perf_hooks.md index afd24227089c6..fbc36572ae438 100644 --- a/content/v18.x/en-US/doc/api/perf_hooks.md +++ b/content/v18.x/en-US/doc/api/perf_hooks.md @@ -359,7 +359,7 @@ event type in order for the timing details to be accessed. ```js const { performance, - PerformanceObserver + PerformanceObserver, } = require('node:perf_hooks'); function someFunction() { @@ -939,7 +939,7 @@ changes: ```js const { performance, - PerformanceObserver + PerformanceObserver, } = require('node:perf_hooks'); const obs = new PerformanceObserver((list, observer) => { @@ -1005,7 +1005,7 @@ or `options.type`: ```js const { performance, - PerformanceObserver + PerformanceObserver, } = require('node:perf_hooks'); const obs = new PerformanceObserver((list, observer) => { @@ -1041,7 +1041,7 @@ with respect to `performanceEntry.startTime`. ```js const { performance, - PerformanceObserver + PerformanceObserver, } = require('node:perf_hooks'); const obs = new PerformanceObserver((perfObserverList, observer) => { @@ -1091,7 +1091,7 @@ equal to `name`, and optionally, whose `performanceEntry.entryType` is equal to ```js const { performance, - PerformanceObserver + PerformanceObserver, } = require('node:perf_hooks'); const obs = new PerformanceObserver((perfObserverList, observer) => { @@ -1147,7 +1147,7 @@ is equal to `type`. ```js const { performance, - PerformanceObserver + PerformanceObserver, } = require('node:perf_hooks'); const obs = new PerformanceObserver((perfObserverList, observer) => { @@ -1493,7 +1493,7 @@ to execute the callback). const async_hooks = require('node:async_hooks'); const { performance, - PerformanceObserver + PerformanceObserver, } = require('node:perf_hooks'); const set = new Set(); @@ -1512,7 +1512,7 @@ const hook = async_hooks.createHook({ `Timeout-${id}-Init`, `Timeout-${id}-Destroy`); } - } + }, }); hook.enable(); @@ -1538,7 +1538,7 @@ dependencies: 'use strict'; const { performance, - PerformanceObserver + PerformanceObserver, } = require('node:perf_hooks'); const mod = require('node:module'); diff --git a/content/v18.x/en-US/doc/api/process.md b/content/v18.x/en-US/doc/api/process.md index 05a22a8d34b7d..6f81cf33d6a9a 100644 --- a/content/v18.x/en-US/doc/api/process.md +++ b/content/v18.x/en-US/doc/api/process.md @@ -363,7 +363,7 @@ process.on('uncaughtException', (err, origin) => { fs.writeSync( process.stderr.fd, `Caught exception: ${err}\n` + - `Exception origin: ${origin}` + `Exception origin: ${origin}`, ); }); @@ -383,7 +383,7 @@ process.on('uncaughtException', (err, origin) => { fs.writeSync( process.stderr.fd, `Caught exception: ${err}\n` + - `Exception origin: ${origin}` + `Exception origin: ${origin}`, ); }); @@ -1297,7 +1297,7 @@ import { emitWarning } from 'node:process'; // Emit a warning with a code and additional detail. emitWarning('Something happened!', { code: 'MY_WARNING', - detail: 'This is some additional information' + detail: 'This is some additional information', }); // Emits: // (node:56338) [MY_WARNING] Warning: Something happened! @@ -1310,7 +1310,7 @@ const { emitWarning } = require('node:process'); // Emit a warning with a code and additional detail. emitWarning('Something happened!', { code: 'MY_WARNING', - detail: 'This is some additional information' + detail: 'This is some additional information', }); // Emits: // (node:56338) [MY_WARNING] Warning: Something happened! @@ -2703,17 +2703,17 @@ tarball. only the source header files for the current release. This file is significantly smaller than the full source file and can be used for compiling Node.js native add-ons. -* `libUrl` {string} an absolute URL pointing to a _`node.lib`_ file matching the - architecture and version of the current release. This file is used for - compiling Node.js native add-ons. _This property is only present on Windows - builds of Node.js and will be missing on all other platforms._ -* `lts` {string} a string label identifying the [LTS][] label for this release. - This property only exists for LTS releases and is `undefined` for all other - release types, including _Current_ releases. - Valid values include the LTS Release code names (including those - that are no longer supported). - * `'Dubnium'` for the 10.x LTS line beginning with 10.13.0. - * `'Erbium'` for the 12.x LTS line beginning with 12.13.0. +* `libUrl` {string|undefined} an absolute URL pointing to a _`node.lib`_ file + matching the architecture and version of the current release. This file is + used for compiling Node.js native add-ons. _This property is only present on + Windows builds of Node.js and will be missing on all other platforms._ +* `lts` {string|undefined} a string label identifying the [LTS][] label for this + release. This property only exists for LTS releases and is `undefined` for all + other release types, including _Current_ releases. Valid values include the + LTS Release code names (including those that are no longer supported). + * `'Fermium'` for the 14.x LTS line beginning with 14.15.0. + * `'Gallium'` for the 16.x LTS line beginning with 16.13.0. + * `'Hydrogen'` for the 18.x LTS line beginning with 18.12.0. For other LTS Release code names, see [Node.js Changelog Archive](https://github.com/nodejs/node/blob/HEAD/doc/changelogs/CHANGELOG_ARCHIVE.md) @@ -2721,10 +2721,10 @@ tarball. ```js { name: 'node', - lts: 'Erbium', - sourceUrl: 'https://nodejs.org/download/release/v12.18.1/node-v12.18.1.tar.gz', - headersUrl: 'https://nodejs.org/download/release/v12.18.1/node-v12.18.1-headers.tar.gz', - libUrl: 'https://nodejs.org/download/release/v12.18.1/win-x64/node.lib' + lts: 'Hydrogen', + sourceUrl: 'https://nodejs.org/download/release/v18.12.0/node-v18.12.0.tar.gz', + headersUrl: 'https://nodejs.org/download/release/v18.12.0/node-v18.12.0-headers.tar.gz', + libUrl: 'https://nodejs.org/download/release/v18.12.0/win-x64/node.lib' } ``` @@ -3190,7 +3190,7 @@ if (process.getegid && process.setegid) { process.setegid(501); console.log(`New gid: ${process.getegid()}`); } catch (err) { - console.log(`Failed to set gid: ${err}`); + console.error(`Failed to set gid: ${err}`); } } ``` @@ -3204,7 +3204,7 @@ if (process.getegid && process.setegid) { process.setegid(501); console.log(`New gid: ${process.getegid()}`); } catch (err) { - console.log(`Failed to set gid: ${err}`); + console.error(`Failed to set gid: ${err}`); } } ``` @@ -3235,7 +3235,7 @@ if (process.geteuid && process.seteuid) { process.seteuid(501); console.log(`New uid: ${process.geteuid()}`); } catch (err) { - console.log(`Failed to set uid: ${err}`); + console.error(`Failed to set uid: ${err}`); } } ``` @@ -3249,7 +3249,7 @@ if (process.geteuid && process.seteuid) { process.seteuid(501); console.log(`New uid: ${process.geteuid()}`); } catch (err) { - console.log(`Failed to set uid: ${err}`); + console.error(`Failed to set uid: ${err}`); } } ``` @@ -3280,7 +3280,7 @@ if (process.getgid && process.setgid) { process.setgid(501); console.log(`New gid: ${process.getgid()}`); } catch (err) { - console.log(`Failed to set gid: ${err}`); + console.error(`Failed to set gid: ${err}`); } } ``` @@ -3294,7 +3294,7 @@ if (process.getgid && process.setgid) { process.setgid(501); console.log(`New gid: ${process.getgid()}`); } catch (err) { - console.log(`Failed to set gid: ${err}`); + console.error(`Failed to set gid: ${err}`); } } ``` @@ -3325,7 +3325,7 @@ if (process.getgroups && process.setgroups) { process.setgroups([501]); console.log(process.getgroups()); // new groups } catch (err) { - console.log(`Failed to set groups: ${err}`); + console.error(`Failed to set groups: ${err}`); } } ``` @@ -3338,7 +3338,7 @@ if (process.getgroups && process.setgroups) { process.setgroups([501]); console.log(process.getgroups()); // new groups } catch (err) { - console.log(`Failed to set groups: ${err}`); + console.error(`Failed to set groups: ${err}`); } } ``` @@ -3369,7 +3369,7 @@ if (process.getuid && process.setuid) { process.setuid(501); console.log(`New uid: ${process.getuid()}`); } catch (err) { - console.log(`Failed to set uid: ${err}`); + console.error(`Failed to set uid: ${err}`); } } ``` @@ -3383,7 +3383,7 @@ if (process.getuid && process.setuid) { process.setuid(501); console.log(`New uid: ${process.getuid()}`); } catch (err) { - console.log(`Failed to set uid: ${err}`); + console.error(`Failed to set uid: ${err}`); } } ``` @@ -3675,7 +3675,7 @@ import { umask } from 'node:process'; const newmask = 0o022; const oldmask = umask(newmask); console.log( - `Changed umask from ${oldmask.toString(8)} to ${newmask.toString(8)}` + `Changed umask from ${oldmask.toString(8)} to ${newmask.toString(8)}`, ); ``` @@ -3685,7 +3685,7 @@ const { umask } = require('node:process'); const newmask = 0o022; const oldmask = umask(newmask); console.log( - `Changed umask from ${oldmask.toString(8)} to ${newmask.toString(8)}` + `Changed umask from ${oldmask.toString(8)} to ${newmask.toString(8)}`, ); ``` diff --git a/content/v18.x/en-US/doc/api/readline.md b/content/v18.x/en-US/doc/api/readline.md index 443d89f8b4939..c4701eefc244c 100644 --- a/content/v18.x/en-US/doc/api/readline.md +++ b/content/v18.x/en-US/doc/api/readline.md @@ -504,7 +504,7 @@ const rl = readline.createInterface(process.stdin); const showResults = debounce(() => { console.log( '\n', - values.filter((val) => val.startsWith(rl.line)).join(' ') + values.filter((val) => val.startsWith(rl.line)).join(' '), ); }, 300); process.stdin.on('keypress', (c, k) => { @@ -762,7 +762,7 @@ instance. const readlinePromises = require('node:readline/promises'); const rl = readlinePromises.createInterface({ input: process.stdin, - output: process.stdout + output: process.stdout, }); ``` @@ -1019,7 +1019,7 @@ instance. const readline = require('node:readline'); const rl = readline.createInterface({ input: process.stdin, - output: process.stdout + output: process.stdout, }); ``` @@ -1158,7 +1158,7 @@ const readline = require('node:readline'); const rl = readline.createInterface({ input: process.stdin, output: process.stdout, - prompt: 'OHAI> ' + prompt: 'OHAI> ', }); rl.prompt(); @@ -1194,7 +1194,7 @@ async function processLineByLine() { const rl = readline.createInterface({ input: fileStream, - crlfDelay: Infinity + crlfDelay: Infinity, }); // Note: we use the crlfDelay option to recognize all instances of CR LF // ('\r\n') in input.txt as a single line break. @@ -1216,7 +1216,7 @@ const readline = require('node:readline'); const rl = readline.createInterface({ input: fs.createReadStream('sample.txt'), - crlfDelay: Infinity + crlfDelay: Infinity, }); rl.on('line', (line) => { @@ -1236,7 +1236,7 @@ const { createInterface } = require('node:readline'); try { const rl = createInterface({ input: createReadStream('big-file.txt'), - crlfDelay: Infinity + crlfDelay: Infinity, }); rl.on('line', (line) => { diff --git a/content/v18.x/en-US/doc/api/repl.md b/content/v18.x/en-US/doc/api/repl.md index 28dc5ecce55f5..1d216c4358fcd 100644 --- a/content/v18.x/en-US/doc/api/repl.md +++ b/content/v18.x/en-US/doc/api/repl.md @@ -132,7 +132,7 @@ const r = repl.start('> '); Object.defineProperty(r.context, 'm', { configurable: false, enumerable: true, - value: msg + value: msg, }); ``` @@ -485,7 +485,7 @@ replServer.defineCommand('sayhello', { this.clearBufferedCommand(); console.log(`Hello, ${name}!`); this.displayPrompt(); - } + }, }); replServer.defineCommand('saybye', function saybye() { console.log('Goodbye!'); @@ -733,7 +733,7 @@ let connections = 0; repl.start({ prompt: 'Node.js via stdin> ', input: process.stdin, - output: process.stdout + output: process.stdout, }); net.createServer((socket) => { @@ -741,7 +741,7 @@ net.createServer((socket) => { repl.start({ prompt: 'Node.js via Unix socket> ', input: socket, - output: socket + output: socket, }).on('exit', () => { socket.end(); }); @@ -752,7 +752,7 @@ net.createServer((socket) => { repl.start({ prompt: 'Node.js via TCP socket> ', input: socket, - output: socket + output: socket, }).on('exit', () => { socket.end(); }); diff --git a/content/v18.x/en-US/doc/api/report.md b/content/v18.x/en-US/doc/api/report.md index 6d5341a59a2f4..fc005d6e0e8dd 100644 --- a/content/v18.x/en-US/doc/api/report.md +++ b/content/v18.x/en-US/doc/api/report.md @@ -23,7 +23,7 @@ is provided below for reference. ```json { "header": { - "reportVersion": 1, + "reportVersion": 3, "event": "exception", "trigger": "Exception", "filename": "report.20181221.005011.8974.0.001.json", @@ -198,10 +198,17 @@ is provided below for reference. } }, "resourceUsage": { - "userCpuSeconds": 0.069595, - "kernelCpuSeconds": 0.019163, - "cpuConsumptionPercent": 0.000000, - "maxRss": 18079744, + "rss": "35766272", + "free_memory": "1598337024", + "total_memory": "17179869184", + "available_memory": "1598337024", + "maxRss": "36624662528", + "constrained_memory": "36624662528", + "userCpuSeconds": 0.040072, + "kernelCpuSeconds": 0.016029, + "cpuConsumptionPercent": 5.6101, + "userCpuConsumptionPercent": 4.0072, + "kernelCpuConsumptionPercent": 1.6029, "pageFaults": { "IORequired": 0, "IONotRequired": 4610 @@ -212,9 +219,11 @@ is provided below for reference. } }, "uvthreadResourceUsage": { - "userCpuSeconds": 0.068457, - "kernelCpuSeconds": 0.019127, - "cpuConsumptionPercent": 0.000000, + "userCpuSeconds": 0.039843, + "kernelCpuSeconds": 0.015937, + "cpuConsumptionPercent": 5.578, + "userCpuConsumptionPercent": 3.9843, + "kernelCpuConsumptionPercent": 1.5937, "fsActivity": { "reads": 0, "writes": 0 @@ -528,6 +537,11 @@ includes the date, time, PID, and a sequence number. The sequence number helps in associating the report dump with the runtime state if generated multiple times for the same Node.js process. +Diagnostic report has an associated single-digit version number (`report.header.reportVersion`), +uniquely representing the report format. The version number is bumped +when new key is added or removed, or the data type of a value is changed. +Report version definitions are consistent across LTS releases. + ## Configuration Additional runtime configuration of report generation is available via diff --git a/content/v18.x/en-US/doc/api/stream.md b/content/v18.x/en-US/doc/api/stream.md index 9bd9877326fd0..06233dad2a9e7 100644 --- a/content/v18.x/en-US/doc/api/stream.md +++ b/content/v18.x/en-US/doc/api/stream.md @@ -172,7 +172,7 @@ server.listen(1337); // $ curl localhost:1337 -d "\"foo\"" // string // $ curl localhost:1337 -d "not json" -// error: Unexpected token o in JSON at position 1 +// error: Unexpected token 'o', "not json" is not valid JSON ``` [`Writable`][] streams (such as `res` in the example) expose methods such as @@ -880,8 +880,10 @@ pass.unpipe(writable); // readableFlowing is now false. pass.on('data', (chunk) => { console.log(chunk.toString()); }); +// readableFlowing is still false. pass.write('ok'); // Will not emit 'data'. pass.resume(); // Must be called to make stream emit 'data'. +// readableFlowing is now true. ``` While `readable.readableFlowing` is `false`, data may be accumulating @@ -1679,6 +1681,41 @@ option. In the code example above, data will be in a single chunk if the file has less then 64 KiB of data because no `highWaterMark` option is provided to [`fs.createReadStream()`][]. +##### `readable.compose(stream[, options])` + + + +> Stability: 1 - Experimental + +* `stream` {Stream|Iterable|AsyncIterable|Function} +* `options` {Object} + * `signal` {AbortSignal} allows destroying the stream if the signal is + aborted. +* Returns: {Duplex} a stream composed with the stream `stream`. + +```mjs +import { Readable } from 'node:stream'; + +async function* splitToWords(source) { + for await (const chunk of source) { + const words = String(chunk).split(' '); + + for (const word of words) { + yield word; + } + } +} + +const wordsStream = Readable.from(['this is', 'compose as operator']).compose(splitToWords); +const words = await wordsStream.toArray(); + +console.log(words); // prints ['this', 'is', 'compose', 'as', 'operator'] +``` + +See [`stream.compose`][] for more information. + ##### `readable.iterator([options])` * `stream` {Stream} A readable and/or writable stream. + * `options` {Object} * `error` {boolean} If set to `false`, then a call to `emit('error', err)` is not treated as finished. **Default:** `true`. @@ -2354,8 +2392,12 @@ changes: underlying stream will _not_ be aborted if the signal is aborted. The callback will get called with an `AbortError`. All registered listeners added by this function will also be removed. + * `cleanup` {boolean} remove all registered stream listeners. + **Default:** `false`. + * `callback` {Function} A callback function that takes an optional error argument. + * Returns: {Function} A cleanup function which removes all registered listeners. @@ -2474,7 +2516,7 @@ pipeline( } else { console.log('Pipeline succeeded.'); } - } + }, ); ``` @@ -2493,7 +2535,7 @@ async function run() { await pipeline( fs.createReadStream('archive.tar'), zlib.createGzip(), - fs.createWriteStream('archive.tar.gz') + fs.createWriteStream('archive.tar.gz'), ); console.log('Pipeline succeeded.'); } @@ -2540,7 +2582,7 @@ async function run() { yield await processChunk(chunk, { signal }); } }, - fs.createWriteStream('uppercase.txt') + fs.createWriteStream('uppercase.txt'), ); console.log('Pipeline succeeded.'); } @@ -2562,7 +2604,7 @@ async function run() { await someLongRunningfn({ signal }); yield 'asd'; }, - fs.createWriteStream('uppercase.txt') + fs.createWriteStream('uppercase.txt'), ); console.log('Pipeline succeeded.'); } @@ -2634,7 +2676,7 @@ import { compose, Transform } from 'node:stream'; const removeSpaces = new Transform({ transform(chunk, encoding, callback) { callback(null, String(chunk).replace(' ', '')); - } + }, }); async function* toUpper(source) { @@ -2693,6 +2735,8 @@ await finished(compose(s1, s2, s3)); console.log(res); // prints 'HELLOWORLD' ``` +See [`readable.compose(stream)`][] for `stream.compose` as operator. + ### `stream.Readable.from(iterable[, options])` + +> Stability: 1 - Experimental + +The Node.js test runner supports running in watch mode by passing the `--watch` flag: + +```bash +node --test --watch +``` + +In watch mode, the test runner will watch for changes to test files and +their dependencies. When a change is detected, the test runner will +rerun the tests affected by the change. +The test runner will continue to run until the process is terminated. + ## Running tests from the command line The Node.js test runner can be invoked from the command line by passing the @@ -352,6 +371,89 @@ Otherwise, the test is considered to be a failure. Test files must be executable by Node.js, but are not required to use the `node:test` module internally. +## Mocking + +The `node:test` module supports mocking during testing via a top-level `mock` +object. The following example creates a spy on a function that adds two numbers +together. The spy is then used to assert that the function was called as +expected. + +```mjs +import assert from 'node:assert'; +import { mock, test } from 'node:test'; + +test('spies on a function', () => { + const sum = mock.fn((a, b) => { + return a + b; + }); + + assert.strictEqual(sum.mock.calls.length, 0); + assert.strictEqual(sum(3, 4), 7); + assert.strictEqual(sum.mock.calls.length, 1); + + const call = sum.mock.calls[0]; + assert.deepStrictEqual(call.arguments, [3, 4]); + assert.strictEqual(call.result, 7); + assert.strictEqual(call.error, undefined); + + // Reset the globally tracked mocks. + mock.reset(); +}); +``` + +```cjs +'use strict'; +const assert = require('node:assert'); +const { mock, test } = require('node:test'); + +test('spies on a function', () => { + const sum = mock.fn((a, b) => { + return a + b; + }); + + assert.strictEqual(sum.mock.calls.length, 0); + assert.strictEqual(sum(3, 4), 7); + assert.strictEqual(sum.mock.calls.length, 1); + + const call = sum.mock.calls[0]; + assert.deepStrictEqual(call.arguments, [3, 4]); + assert.strictEqual(call.result, 7); + assert.strictEqual(call.error, undefined); + + // Reset the globally tracked mocks. + mock.reset(); +}); +``` + +The same mocking functionality is also exposed on the [`TestContext`][] object +of each test. The following example creates a spy on an object method using the +API exposed on the `TestContext`. The benefit of mocking via the test context is +that the test runner will automatically restore all mocked functionality once +the test finishes. + +```js +test('spies on an object method', (t) => { + const number = { + value: 5, + add(a) { + return this.value + a; + }, + }; + + t.mock.method(number, 'add'); + assert.strictEqual(number.add.mock.calls.length, 0); + assert.strictEqual(number.add(3), 8); + assert.strictEqual(number.add.mock.calls.length, 1); + + const call = number.add.mock.calls[0]; + + assert.deepStrictEqual(call.arguments, [3]); + assert.strictEqual(call.result, 8); + assert.strictEqual(call.target, undefined); + assert.strictEqual(call.this, number); +}); +``` + ## `run([options])` + +The `MockFunctionContext` class is used to inspect or manipulate the behavior of +mocks created via the [`MockTracker`][] APIs. + +### `ctx.calls` + + + +* {Array} + +A getter that returns a copy of the internal array used to track calls to the +mock. Each entry in the array is an object with the following properties. + +* `arguments` {Array} An array of the arguments passed to the mock function. +* `error` {any} If the mocked function threw then this property contains the + thrown value. **Default:** `undefined`. +* `result` {any} The value returned by the mocked function. +* `stack` {Error} An `Error` object whose stack can be used to determine the + callsite of the mocked function invocation. +* `target` {Function|undefined} If the mocked function is a constructor, this + field contains the class being constructed. Otherwise this will be + `undefined`. +* `this` {any} The mocked function's `this` value. + +### `ctx.callCount()` + + + +* Returns: {integer} The number of times that this mock has been invoked. + +This function returns the number of times that this mock has been invoked. This +function is more efficient than checking `ctx.calls.length` because `ctx.calls` +is a getter that creates a copy of the internal call tracking array. + +### `ctx.mockImplementation(implementation)` + + + +* `implementation` {Function|AsyncFunction} The function to be used as the + mock's new implementation. + +This function is used to change the behavior of an existing mock. + +The following example creates a mock function using `t.mock.fn()`, calls the +mock function, and then changes the mock implementation to a different function. + +```js +test('changes a mock behavior', (t) => { + let cnt = 0; + + function addOne() { + cnt++; + return cnt; + } + + function addTwo() { + cnt += 2; + return cnt; + } + + const fn = t.mock.fn(addOne); + + assert.strictEqual(fn(), 1); + fn.mock.mockImplementation(addTwo); + assert.strictEqual(fn(), 3); + assert.strictEqual(fn(), 5); +}); +``` + +### `ctx.mockImplementationOnce(implementation[, onCall])` + + + +* `implementation` {Function|AsyncFunction} The function to be used as the + mock's implementation for the invocation number specified by `onCall`. +* `onCall` {integer} The invocation number that will use `implementation`. If + the specified invocation has already occurred then an exception is thrown. + **Default:** The number of the next invocation. + +This function is used to change the behavior of an existing mock for a single +invocation. Once invocation `onCall` has occurred, the mock will revert to +whatever behavior it would have used had `mockImplementationOnce()` not been +called. + +The following example creates a mock function using `t.mock.fn()`, calls the +mock function, changes the mock implementation to a different function for the +next invocation, and then resumes its previous behavior. + +```js +test('changes a mock behavior once', (t) => { + let cnt = 0; + + function addOne() { + cnt++; + return cnt; + } + + function addTwo() { + cnt += 2; + return cnt; + } + + const fn = t.mock.fn(addOne); + + assert.strictEqual(fn(), 1); + fn.mock.mockImplementationOnce(addTwo); + assert.strictEqual(fn(), 3); + assert.strictEqual(fn(), 4); +}); +``` + +### `ctx.resetCalls()` + + + +Resets the call history of the mock function. + +### `ctx.restore()` + + + +Resets the implementation of the mock function to its original behavior. The +mock can still be used after calling this function. + +## Class: `MockTracker` + + + +The `MockTracker` class is used to manage mocking functionality. The test runner +module provides a top level `mock` export which is a `MockTracker` instance. +Each test also provides its own `MockTracker` instance via the test context's +`mock` property. + +### `mock.fn([original[, implementation]][, options])` + + + +* `original` {Function|AsyncFunction} An optional function to create a mock on. + **Default:** A no-op function. +* `implementation` {Function|AsyncFunction} An optional function used as the + mock implementation for `original`. This is useful for creating mocks that + exhibit one behavior for a specified number of calls and then restore the + behavior of `original`. **Default:** The function specified by `original`. +* `options` {Object} Optional configuration options for the mock function. The + following properties are supported: + * `times` {integer} The number of times that the mock will use the behavior of + `implementation`. Once the mock function has been called `times` times, it + will automatically restore the behavior of `original`. This value must be an + integer greater than zero. **Default:** `Infinity`. +* Returns: {Proxy} The mocked function. The mocked function contains a special + `mock` property, which is an instance of [`MockFunctionContext`][], and can + be used for inspecting and changing the behavior of the mocked function. + +This function is used to create a mock function. + +The following example creates a mock function that increments a counter by one +on each invocation. The `times` option is used to modify the mock behavior such +that the first two invocations add two to the counter instead of one. + +```js +test('mocks a counting function', (t) => { + let cnt = 0; + + function addOne() { + cnt++; + return cnt; + } + + function addTwo() { + cnt += 2; + return cnt; + } + + const fn = t.mock.fn(addOne, addTwo, { times: 2 }); + + assert.strictEqual(fn(), 2); + assert.strictEqual(fn(), 4); + assert.strictEqual(fn(), 5); + assert.strictEqual(fn(), 6); +}); +``` + +### `mock.getter(object, methodName[, implementation][, options])` + + + +This function is syntax sugar for [`MockTracker.method`][] with `options.getter` +set to `true`. + +### `mock.method(object, methodName[, implementation][, options])` + + + +* `object` {Object} The object whose method is being mocked. +* `methodName` {string|symbol} The identifier of the method on `object` to mock. + If `object[methodName]` is not a function, an error is thrown. +* `implementation` {Function|AsyncFunction} An optional function used as the + mock implementation for `object[methodName]`. **Default:** The original method + specified by `object[methodName]`. +* `options` {Object} Optional configuration options for the mock method. The + following properties are supported: + * `getter` {boolean} If `true`, `object[methodName]` is treated as a getter. + This option cannot be used with the `setter` option. **Default:** false. + * `setter` {boolean} If `true`, `object[methodName]` is treated as a setter. + This option cannot be used with the `getter` option. **Default:** false. + * `times` {integer} The number of times that the mock will use the behavior of + `implementation`. Once the mocked method has been called `times` times, it + will automatically restore the original behavior. This value must be an + integer greater than zero. **Default:** `Infinity`. +* Returns: {Proxy} The mocked method. The mocked method contains a special + `mock` property, which is an instance of [`MockFunctionContext`][], and can + be used for inspecting and changing the behavior of the mocked method. + +This function is used to create a mock on an existing object method. The +following example demonstrates how a mock is created on an existing object +method. + +```js +test('spies on an object method', (t) => { + const number = { + value: 5, + subtract(a) { + return this.value - a; + }, + }; + + t.mock.method(number, 'subtract'); + assert.strictEqual(number.subtract.mock.calls.length, 0); + assert.strictEqual(number.subtract(3), 2); + assert.strictEqual(number.subtract.mock.calls.length, 1); + + const call = number.subtract.mock.calls[0]; + + assert.deepStrictEqual(call.arguments, [3]); + assert.strictEqual(call.result, 2); + assert.strictEqual(call.error, undefined); + assert.strictEqual(call.target, undefined); + assert.strictEqual(call.this, number); +}); +``` + +### `mock.reset()` + + + +This function restores the default behavior of all mocks that were previously +created by this `MockTracker` and disassociates the mocks from the +`MockTracker` instance. Once disassociated, the mocks can still be used, but the +`MockTracker` instance can no longer be used to reset their behavior or +otherwise interact with them. + +After each test completes, this function is called on the test context's +`MockTracker`. If the global `MockTracker` is used extensively, calling this +function manually is recommended. + +### `mock.restoreAll()` + + + +This function restores the default behavior of all mocks that were previously +created by this `MockTracker`. Unlike `mock.reset()`, `mock.restoreAll()` does +not disassociate the mocks from the `MockTracker` instance. + +### `mock.setter(object, methodName[, implementation][, options])` + + + +This function is syntax sugar for [`MockTracker.method`][] with `options.setter` +set to `true`. + ## Class: `TapStream` + +* `fn` {Function|AsyncFunction} The hook function. The first argument + to this function is a [`TestContext`][] object. If the hook uses callbacks, + the callback function is passed as the second argument. **Default:** A no-op + function. +* `options` {Object} Configuration options for the hook. The following + properties are supported: + * `signal` {AbortSignal} Allows aborting an in-progress hook. + * `timeout` {number} A number of milliseconds the hook will fail after. + If unspecified, subtests inherit this value from their parent. + **Default:** `Infinity`. + +This function is used to create a hook that runs after the current test +finishes. + +```js +test('top level test', async (t) => { + t.after((t) => t.diagnostic(`finished running ${t.name}`)); + assert.ok('some relevant assertion here'); +}); +``` + ### `context.afterEach([fn][, options])` Returns an async iterator that generates values in an interval of `delay` ms. +If `ref` is `true`, you need to call `next()` of async iterator explicitly +or implicitly to keep the event loop alive. * `delay` {number} The number of milliseconds to wait between iterations. **Default:** `1`. diff --git a/content/v18.x/en-US/doc/api/tls.md b/content/v18.x/en-US/doc/api/tls.md index 4902dca33bba1..00ba977db7b24 100644 --- a/content/v18.x/en-US/doc/api/tls.md +++ b/content/v18.x/en-US/doc/api/tls.md @@ -1173,6 +1173,9 @@ certificate. -> Stability: 3 - Legacy: Use the WHATWG URL API instead. +> Stability: 0 - Deprecated: Use the WHATWG URL API instead. * `urlString` {string} The URL string to parse. * `parseQueryString` {boolean} If `true`, the `query` property will always @@ -1562,16 +1565,9 @@ A `URIError` is thrown if the `auth` property is present but cannot be decoded. `url.parse()` uses a lenient, non-standard algorithm for parsing URL strings. It is prone to security issues such as [host name spoofing][] -and incorrect handling of usernames and passwords. - -`url.parse()` is an exception to most of the legacy APIs. Despite its security -concerns, it is legacy and not deprecated because it is: - -* Faster than the alternative WHATWG `URL` parser. -* Easier to use with regards to relative URLs than the alternative WHATWG `URL` API. -* Widely relied upon within the npm ecosystem. - -Use with caution. +and incorrect handling of usernames and passwords. Do not use with untrusted +input. CVEs are not issued for `url.parse()` vulnerabilities. Use the +[WHATWG URL][] API instead. ### `url.resolve(from, to)` diff --git a/content/v18.x/en-US/doc/api/util.md b/content/v18.x/en-US/doc/api/util.md index 5d3eba7e9ced4..d1923a7749cda 100644 --- a/content/v18.x/en-US/doc/api/util.md +++ b/content/v18.x/en-US/doc/api/util.md @@ -676,7 +676,7 @@ const o = { 'eiusmod \ntempor incididunt ut labore et dolore magna aliqua.', 'test', 'foo']], 4], - b: new Map([['za', 1], ['zb', 'test']]) + b: new Map([['za', 1], ['zb', 'test']]), }; console.log(util.inspect(o, { compact: true, depth: 5, breakLength: 80 })); @@ -744,7 +744,7 @@ const assert = require('node:assert'); const o1 = { b: [2, 3, 1], a: '`a` comes before `b`', - c: new Set([2, 3, 1]) + c: new Set([2, 3, 1]), }; console.log(inspect(o1, { sorted: true })); // { a: '`a` comes before `b`', b: [ 2, 3, 1 ], c: Set(3) { 1, 2, 3 } } @@ -754,11 +754,11 @@ console.log(inspect(o1, { sorted: (a, b) => b.localeCompare(a) })); const o2 = { c: new Set([2, 1, 3]), a: '`a` comes before `b`', - b: [2, 3, 1] + b: [2, 3, 1], }; assert.strict.equal( inspect(o1, { sorted: true }), - inspect(o2, { sorted: true }) + inspect(o2, { sorted: true }), ); ``` @@ -905,7 +905,7 @@ class Box { } const newOptions = Object.assign({}, options, { - depth: options.depth === null ? null : options.depth - 1 + depth: options.depth === null ? null : options.depth - 1, }); // Five space padding because that's the size of "Box< ". @@ -1020,6 +1020,355 @@ Otherwise, returns `false`. See [`assert.deepStrictEqual()`][] for more information about deep strict equality. +## Class: `util.MIMEType` + + + +> Stability: 1 - Experimental + +An implementation of [the MIMEType class](https://bmeck.github.io/node-proposal-mime-api/). + +In accordance with browser conventions, all properties of `MIMEType` objects +are implemented as getters and setters on the class prototype, rather than as +data properties on the object itself. + +A MIME string is a structured string containing multiple meaningful +components. When parsed, a `MIMEType` object is returned containing +properties for each of these components. + +### Constructor: `new MIMEType(input)` + +* `input` {string} The input MIME to parse + +Creates a new `MIMEType` object by parsing the `input`. + +```mjs +import { MIMEType } from 'node:util'; + +const myMIME = new MIMEType('text/plain'); +``` + +```cjs +const { MIMEType } = require('node:util'); + +const myMIME = new MIMEType('text/plain'); +``` + +A `TypeError` will be thrown if the `input` is not a valid MIME. Note +that an effort will be made to coerce the given values into strings. For +instance: + +```mjs +import { MIMEType } from 'node:util'; +const myMIME = new MIMEType({ toString: () => 'text/plain' }); +console.log(String(myMIME)); +// Prints: text/plain +``` + +```cjs +const { MIMEType } = require('node:util'); +const myMIME = new MIMEType({ toString: () => 'text/plain' }); +console.log(String(myMIME)); +// Prints: text/plain +``` + +#### `mime.type` + +* {string} + +Gets and sets the type portion of the MIME. + +```mjs +import { MIMEType } from 'node:util'; + +const myMIME = new MIMEType('text/javascript'); +console.log(myMIME.type); +// Prints: text +myMIME.type = 'application'; +console.log(myMIME.type); +// Prints: application +console.log(String(myMIME)); +// Prints: application/javascript +``` + +```cjs +const { MIMEType } = require('node:util'); + +const myMIME = new MIMEType('text/javascript'); +console.log(myMIME.type); +// Prints: text +myMIME.type = 'application'; +console.log(myMIME.type); +// Prints: application +console.log(String(myMIME)); +// Prints: application/javascript/javascript +``` + +#### `mime.subtype` + +* {string} + +Gets and sets the subtype portion of the MIME. + +```mjs +import { MIMEType } from 'node:util'; + +const myMIME = new MIMEType('text/ecmascript'); +console.log(myMIME.subtype); +// Prints: ecmascript +myMIME.subtype = 'javascript'; +console.log(myMIME.subtype); +// Prints: javascript +console.log(String(myMIME)); +// Prints: text/javascript +``` + +```cjs +const { MIMEType } = require('node:util'); + +const myMIME = new MIMEType('text/ecmascript'); +console.log(myMIME.subtype); +// Prints: ecmascript +myMIME.subtype = 'javascript'; +console.log(myMIME.subtype); +// Prints: javascript +console.log(String(myMIME)); +// Prints: text/javascript +``` + +#### `mime.essence` + +* {string} + +Gets the essence of the MIME. This property is read only. +Use `mime.type` or `mime.subtype` to alter the MIME. + +```mjs +import { MIMEType } from 'node:util'; + +const myMIME = new MIMEType('text/javascript;key=value'); +console.log(myMIME.essence); +// Prints: text/javascript +myMIME.type = 'application'; +console.log(myMIME.essence); +// Prints: application/javascript +console.log(String(myMIME)); +// Prints: application/javascript;key=value +``` + +```cjs +const { MIMEType } = require('node:util'); + +const myMIME = new MIMEType('text/javascript;key=value'); +console.log(myMIME.essence); +// Prints: text/javascript +myMIME.type = 'application'; +console.log(myMIME.essence); +// Prints: application/javascript +console.log(String(myMIME)); +// Prints: application/javascript;key=value +``` + +#### `mime.params` + +* {MIMEParams} + +Gets the [`MIMEParams`][] object representing the +parameters of the MIME. This property is read-only. See +[`MIMEParams`][] documentation for details. + +#### `mime.toString()` + +* Returns: {string} + +The `toString()` method on the `MIMEType` object returns the serialized MIME. + +Because of the need for standard compliance, this method does not allow users +to customize the serialization process of the MIME. + +#### `mime.toJSON()` + +* Returns: {string} + +Alias for [`mime.toString()`][]. + +This method is automatically called when an `MIMEType` object is serialized +with [`JSON.stringify()`][]. + +```mjs +import { MIMEType } from 'node:util'; + +const myMIMES = [ + new MIMEType('image/png'), + new MIMEType('image/gif'), +]; +console.log(JSON.stringify(myMIMES)); +// Prints: ["image/png", "image/gif"] +``` + +```cjs +const { MIMEType } = require('node:util'); + +const myMIMES = [ + new MIMEType('image/png'), + new MIMEType('image/gif'), +]; +console.log(JSON.stringify(myMIMES)); +// Prints: ["image/png", "image/gif"] +``` + +### Class: `util.MIMEParams` + + + +The `MIMEParams` API provides read and write access to the parameters of a +`MIMEType`. + +#### Constructor: `new MIMEParams()` + +Creates a new `MIMEParams` object by with empty parameters + +```mjs +import { MIMEParams } from 'node:util'; + +const myParams = new MIMEParams(); +``` + +```cjs +const { MIMEParams } = require('node:util'); + +const myParams = new MIMEParams(); +``` + +#### `mimeParams.delete(name)` + +* `name` {string} + +Remove all name-value pairs whose name is `name`. + +#### `mimeParams.entries()` + +* Returns: {Iterator} + +Returns an iterator over each of the name-value pairs in the parameters. +Each item of the iterator is a JavaScript `Array`. The first item of the array +is the `name`, the second item of the array is the `value`. + +#### `mimeParams.get(name)` + +* `name` {string} +* Returns: {string} or `null` if there is no name-value pair with the given + `name`. + +Returns the value of the first name-value pair whose name is `name`. If there +are no such pairs, `null` is returned. + +#### `mimeParams.has(name)` + +* `name` {string} +* Returns: {boolean} + +Returns `true` if there is at least one name-value pair whose name is `name`. + +#### `mimeParams.keys()` + +* Returns: {Iterator} + +Returns an iterator over the names of each name-value pair. + +```mjs +import { MIMEType } from 'node:util'; + +const { params } = new MIMEType('text/plain;foo=0;bar=1'); +for (const name of params.keys()) { + console.log(name); +} +// Prints: +// foo +// bar +``` + +```cjs +const { MIMEType } = require('node:util'); + +const { params } = new MIMEType('text/plain;foo=0;bar=1'); +for (const name of params.keys()) { + console.log(name); +} +// Prints: +// foo +// bar +``` + +#### `mimeParams.set(name, value)` + +* `name` {string} +* `value` {string} + +Sets the value in the `MIMEParams` object associated with `name` to +`value`. If there are any pre-existing name-value pairs whose names are `name`, +set the first such pair's value to `value`. + +```mjs +import { MIMEType } from 'node:util'; + +const { params } = new MIMEType('text/plain;foo=0;bar=1'); +params.set('foo', 'def'); +params.set('baz', 'xyz'); +console.log(params.toString()); +// Prints: foo=def&bar=1&baz=xyz +``` + +```cjs +const { MIMEType } = require('node:util'); + +const { params } = new MIMEType('text/plain;foo=0;bar=1'); +params.set('foo', 'def'); +params.set('baz', 'xyz'); +console.log(params.toString()); +// Prints: foo=def&bar=1&baz=xyz +``` + +#### `mimeParams.values()` + +* Returns: {Iterator} + +Returns an iterator over the values of each name-value pair. + +#### `mimeParams[@@iterator]()` + +* Returns: {Iterator} + +Alias for [`mimeParams.entries()`][]. + +```mjs +import { MIMEType } from 'node:util'; + +const { params } = new MIMEType('text/plain;foo=bar;xyz=baz'); +for (const [name, value] of params) { + console.log(name, value); +} +// Prints: +// foo bar +// xyz baz +``` + +```cjs +const { MIMEType } = require('node:util'); + +const { params } = new MIMEType('text/plain;foo=bar;xyz=baz'); +for (const [name, value] of params) { + console.log(name, value); +} +// Prints: +// foo bar +// xyz baz +``` + ## `util.parseArgs([config])` + +* {string|undefined} + +When the script is compiled from a source that contains a source map magic +comment, this property will be set to the URL of the source map. + +```mjs +import vm from 'node:vm'; + +const script = new vm.Script(` +function myFunc() {} +//# sourceMappingURL=sourcemap.json +`); + +console.log(script.sourceMapURL); +// Prints: sourcemap.json +``` + +```cjs +const vm = require('node:vm'); + +const script = new vm.Script(` +function myFunc() {} +//# sourceMappingURL=sourcemap.json +`); + +console.log(script.sourceMapURL); +// Prints: sourcemap.json +``` + ## Class: `vm.Module` ` are considered HTML nodes, + that's because YAML isn't valid Markdown content. (Doesn't abide by the + Markdown spec) +* "New Tooling" references to the (written from-scratch) API build tooling + introduced in `nodejs/nodejs.dev` that might replace the current one from + `nodejs/node` + +## CLI Arguments + +The tooling requires a `filename` argument and supports extra arguments (some +also required) as shown below: + +| Argument | Description | Required | Example | +| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | -------- | ---------------------------------- | +| `--node-version=` | The version of Node.js that is being documented. It defaults to `process.version` which is supplied by Node.js itself | No | v19.0.0 | +| `--output-directory=` | The directory where the output files will be generated. | Yes | `./out/api/` | +| `--apilinks=` | This file is used as an index to specify the source file for each module | No | `./out/doc/api/apilinks.json` | +| `--versions-file=` | This file is used to specify an index of all previous versions of Node.js. It is used for the Version Navigation on the API docs page. | No | `./out/previous-doc-versions.json` | + +**Note:** both of the `apilinks` and `versions-file` parameters are generated by +the Node.js build process (Makefile). And they're files containing a JSON +object. + +### Basic Usage + +```bash +# cd tools/doc +npm run node-doc-generator ${filename} +``` + +**OR** + +```bash +# nodejs/node root directory +make doc +``` + +## Dependencies and how the Tooling works internally + +The API tooling uses an-AST-alike library called +[unified](https://github.com/unifiedjs/unified) for processing the Input file as +a Graph that supports easy modification and update of its nodes. + +In addition to `unified` we also use +[Remark](https://github.com/remarkjs/remark) for manipulating the Markdown part, +and [Rehype](https://github.com/rehypejs/rehype)to help convert to and from +Markdown. + +### What are the steps of the internal tooling? + +The tooling uses `unified` pipe-alike engine to pipe each part of the process. +(The description below is a simplified version) + +* Starting from reading the Frontmatter section of the Markdown file with + [remark-frontmatter](https://www.npmjs.com/package/remark-frontmatter). +* Then the tooling goes to parse the Markdown by using `remark-parse` and adds + support to [GitHub Flavoured Markdown](https://github.github.com/gfm/). +* The tooling proceeds by parsing some of the Markdown nodes and transforming + them to HTML. +* The tooling proceeds to generate the JSON output of the file. +* Finally it does its final node transformations and generates a stringified + HTML. +* It then stores the output to a JSON file and adds extra styling to the HTML + and then stores the HTML file. + +### What each file is responsible for? + +The files listed below are the ones referenced and actually used during the +build process of the API docs as we see on . The +remaining files from the directory might be used by other steps of the Node.js +Makefile or might even be deprecated/remnant of old processes and might need to +be revisited/removed. + +* **`html.mjs`**: Responsible for transforming nodes by decorating them with + visual artifacts for the HTML pages; + * For example, transforming man or JS doc references to links correctly + referring to respective External documentation. +* **`json.mjs`**: Responsible for generating the JSON output of the file; + * It is mostly responsible for going through the whole Markdown file and + generating a JSON object that represent the Metadata of a specific Module. + * For example, for the FS module, it will generate an object with all its + methods, events, classes and use several regular expressions (ReGeX) for + extracting the information needed. +* **`generate.mjs`**: Main entry-point of doc generation for a specific file. It + does e2e processing of a documentation file; +* **`allhtml.mjs`**: A script executed after all files are generated to create a + single "all" page containing all the HTML documentation; +* **`alljson.mjs`**: A script executed after all files are generated to create a + single "all" page containing all the JSON entries; +* **`markdown.mjs`**: Contains utility to replace Markdown links to work with + the website. +* **`common.mjs`**: Contains a few utility functions that are used by the other + files. +* **`type-parser.mjs`**: Used to replace "type references" (e.g. "String", or + "Buffer") to the correct Internal/External documentation pages (i.e. MDN or + other Node.js documentation pages). + +**Note:** It is important to mention that other files not mentioned here might +be used during the process but are not relevant to the generation of the API +docs themselves. You will notice that a lot of the logic within the build +process is **specific** to the current infrastructure. +Just as adding some JavaScript snippets, styles, transforming certain Markdown +elements into HTML, and adding certain HTML classes or such things. + +**Note:** Regarding the previous **Note** it is important to mention that we're +currently working on an API tooling that is generic and independent of the +current Nodejs.org Infrastructure. +[The new tooling that is functional is available at the nodejs.dev repository](https://github.com/nodejs/nodejs.dev/blob/main/scripts/syncApiDocs.js) +and uses plain ReGeX (No AST) and [MDX](https://mdxjs.com/). + +## The Build Process + +The build process that happens on `generate.mjs` follows the steps below: + +* Links within the Markdown are replaced directly within the source Markdown + (AST) (`markdown.replaceLinks`) + * This happens within `markdown.mjs` and basically it adds suffixes or + modifies link references within the Markdown + * This is necessary for the `https://nodejs.org` infrastructure as all pages + are suffixed with `.html` +* Text (and some YAML) Nodes are transformed/modified through + `html.preprocessText` +* JSON output is generated through `json.jsonAPI` +* The title of the page is inferred through `html.firstHeader` +* Nodes are transformed into HTML Elements through `html.preprocessElements` +* The HTML Table of Contents (ToC) is generated through `html.buildToc` + +### `html.mjs` + +This file is responsible for doing node AST transformations that either update +Markdown nodes to decorate them with more data or transform them into HTML Nodes +that attain a certain visual responsibility; For example, to generate the "Added +at" label, or the Source Links or the Stability Index, or the History table. + +**Note:** Methods not listed below are either not relevant or utility methods +for string/array/object manipulation (e.g.: are used by the other methods +mentioned below). + +#### `preprocessText` + +**New Tooling:** Most of the features within this method are available within +the new tooling. + +This method does two things: + +* Replaces the Source Link YAML entry `<-- source_link= -->` into a "Source + Link" HTML anchor element. +* Replaces type references within the Markdown (text) (i.e.: "String", "Buffer") + into the correct HTML anchor element that links to the correct documentation + page. + * The original node then gets mutated from text to HTML. + * It also updates references to Linux "MAN" pages to Web versions of them. + +#### `firstHeader` + +**New Tooling:** All features within this method are available within the new +Tooling. + +Is used to attempt to extract the first heading of the page (recursively) to +define the "title" of the page. + +**Note:** As all API Markdown files start with a Heading, this could possibly be +improved to a reduced complexity. + +#### `preprocessElements` + +**New Tooling:** All features within this method are available within the new +tooling. + +This method is responsible for doing multiple transformations within the AST +Nodes, in majority, transforming the source node in respective HTML elements +with diverse responsibilities, such as: + +* Updating Markdown `code` blocks by adding Language highlighting + * It also adds the "CJS"/"MJS" switch to Nodes that are followed by their + CJS/ESM equivalents. +* Increasing the Heading level of each Heading +* Parses YAML blocks and transforms them into HTML elements (See more at the + `parseYAML` method) +* Updates BlockQuotes that are prefixed by the "Stability" word into a Stability + Index HTML element. + +#### `parseYAML` + +**New Tooling:** Most of the features within this method are available within +the new tooling. + +This method is responsible for parsing the `<--YAML snippets -->` and +transforming them into HTML elements. + +It follows a certain kind of "schema" that basically constitues in the following +options: + +| YAML Key | Description | Example | Example Result | Available on new tooling | +| ------------- | ------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | --------------------------- | ------------------------ | +| `added` | It's used to reference when a certain "module", "class" or "method" was added on Node.js | `added: v0.1.90` | `Added in: v0.1.90` | Yes | +| `deprecated` | It's used to reference when a certain "module", "class" or "method" was deprecated on Node.js | `deprecated: v0.1.90` | `Deprecated since: v0.1.90` | Yes | +| `removed` | It's used to reference when a certain "module", "class" or "method" was removed on Node.js | `removed: v0.1.90` | `Removed in: v0.1.90` | No | +| `changes` | It's used to describe all the changes (historical ones) that happened within a certain "module", "class" or "method" in Node.js | `[{ version: v0.1.90, pr-url: '', description: '' }]` | -- | Yes | +| `napiVersion` | It's used to describe in which version of the N-API this "module", "class" or "method" is available within Node.js | `napiVersion: 1` | `N-API version: 1` | Yes | + +**Note:** The `changes` field gets prepended with the `added`, `deprecated` and +`removed` fields if they exist. The table only gets generated if a `changes` +field exists. In the new tooling only "added" is prepended for now. + +#### `buildToc` + +**New Tooling:** This feature is natively available within the new tooling +through MDX. + +This method generates the Table of Contents based on all the Headings of the +Markdown file. + +#### `altDocs` + +**New Tooling:** All features within this method are available within the new +tooling. + +This method generates a version picker for the current page to be shown in older +versions of the API docs. + +### `json.mjs` + +This file is responsible for generating a JSON object that (supposedly) is used +for IDE-Intellisense or for indexing of all the "methods", "classes", "modules", +"events", "constants" and "globals" available within a certain Markdown file. + +It attempts a best effort extraction of the data by using several regular +expression patterns (ReGeX). + +**Note:** JSON output generation is currently not supported by the new tooling, +but it is in the pipeline for development. + +#### `jsonAPI` + +This method traverses all the AST Nodes by iterating through each one of them +and infers the kind of information each node contains through ReGeX. Then it +mutate the data and appends it to the final JSON object. + +For a more in-depth information we recommend to refer to the `json.mjs` file as +it contains a lot of comments. diff --git a/content/v18.x/en-US/doc/contributing/maintaining-dependencies.md b/content/v18.x/en-US/doc/contributing/maintaining-dependencies.md new file mode 100644 index 0000000000000..b43f1d36a40fc --- /dev/null +++ b/content/v18.x/en-US/doc/contributing/maintaining-dependencies.md @@ -0,0 +1,106 @@ +# Maintaining Dependencies + +Node.js depends on additional components beyond the Node.js code +itself. These dependencies provide both native and JavaScript code +and are built together with the code under the `src` and `lib` +directories to create the Node.js binaries. + +All dependencies are located within the `deps` directory. + +Any code which meets one or more of these conditions should +be managed as a dependency: + +* originates in an upstream project and is maintained + in that upstream project. +* is not built from the `preferred form of the work for + making modifications to it` (see + [GNU GPL v2, section 3.](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) + when `make node` is run. A good example is + WASM code generated from C (the preferred form). + Typically generation is only supported on a subset of platforms, needs + additional tools, and is pre-built outside of the `make node` + step and then committed as a WASM binary in the directory + for the dependency under the `deps` directory. + +By default all dependencies are bundled into the Node.js +binary, however, `configure` options should be available to +use an externalized version at runtime when: + +* the dependency provides native code and is available as + a shared library in one or more of the common Node.js + distributions. +* the dependency provides JavaScript and is not built + from the `preferred form of the work for making modifications + to it` when `make node` is run. + +Many distributions use externalized dependencies for one or +more of these reasons: + +1. They have a requirement to build everything that they ship + from the `preferred form of the work for making + modifications to it`. This means that they need to + replace any pre-built components (for example WASM + binaries) with an equivalent that they have built. +2. They manage the dependency separately as it is used by + more applications than just Node.js. Linking against + a shared library allows them to manage updates and + CVE fixes against the library instead of having to + patch all of the individual applications. +3. They have a system wide configuration for the + dependency that all applications should respect. + +## Supporting externalized dependencies with native code. + +Support for externalized dependencies with native code for which a +shared library is available can added by: + +* adding options to `configure.py`. These are added to the + shared\_optgroup and include an options to: + * enable use of a shared library + * set the name of the shared library + * set the path to the directory with the includes for the + shared library + * set the path to where to find the shared library at + runtime +* add a call to configure\_library() to `configure.py` for the + library at the end of list of existing configure\_library() calls. + If there are additional libraries that are required it is + possible to list more than one with the `pkgname` option. +* in `node.gypi` guard the build for the dependency + with `node_shared_depname` so that it is only built if + the dependency is being bundled into Node.js itself. For example: + +```text + [ 'node_shared_brotli=="false"', { + 'dependencies': [ 'deps/brotli/brotli.gyp:brotli' ], + }], +``` + +## Supporting externalizable dependencies with JavaScript codeIZA + +Support for an externalizable dependency with JavaScript code +can be added by: + +* adding an entry to the `sharable_builtins` map in + `configure.py`. The path should correspond to the file + within the deps directory that is normally bundled into + Node.js. For example `deps/cjs-module-lexer/lexer.js`. + This will add a new option for building with that dependency + externalized. After adding the entry you can see + the new option by running `./configure --help`. + +* adding a call to `AddExternalizedBuiltin` to the constructor + for BuildinLoader in `src/node_builtins.cc` for the + dependency using the `NODE_SHARED_BUILTLIN` #define generated for + the dependency. After running `./configure` with the new + option you can find the #define in `config.gypi`. You can cut and + paste one of the existing entries and then update to match the + inport name for the dependency and the #define generated. + +## Supporting non-externalized dependencies with JavaScript code + +If the dependency consists of JavaScript in the +`preferred form of the work for making modifications to it`, it +can be added as a non-externalizable dependency. In this case +simply add the path to the JavaScript file in the `deps_files` +list in the `node.gyp` file. diff --git a/content/v18.x/en-US/doc/contributing/maintaining-openssl.md b/content/v18.x/en-US/doc/contributing/maintaining-openssl.md index d8e93b89a48f1..678b0df92c22f 100644 --- a/content/v18.x/en-US/doc/contributing/maintaining-openssl.md +++ b/content/v18.x/en-US/doc/contributing/maintaining-openssl.md @@ -7,9 +7,11 @@ currently need to generate four PRs as follows: * a PR for `main` which is generated following the instructions below for OpenSSL 3.x.x. +* a PR for 18.x following the instructions in the v18.x-staging version + of this guide. * a PR for 16.x following the instructions in the v16.x-staging version of this guide. -* a PR for 14.x following the instructions in the v14.x-staging version +* a PR for 14.x following the instructions in the [v14.x-staging version][] of this guide. ## Use of the quictls/openssl fork @@ -152,3 +154,5 @@ regenerated and committed by: ``` Finally, build Node.js and run the tests. + +[v14.x-staging version]: https://github.com/nodejs/node/blob/v14.x-staging/doc/guides/maintaining-openssl.md diff --git a/content/v18.x/en-US/doc/contributing/maintaining-zlib.md b/content/v18.x/en-US/doc/contributing/maintaining-zlib.md index caed2534c69c3..75c2b54c04a73 100644 --- a/content/v18.x/en-US/doc/contributing/maintaining-zlib.md +++ b/content/v18.x/en-US/doc/contributing/maintaining-zlib.md @@ -9,10 +9,10 @@ Update zlib: ```bash git clone https://chromium.googlesource.com/chromium/src/third_party/zlib -cp deps/zlib/zlib.gyp deps/zlib/win32/zlib.def deps +cp deps/zlib/zlib.gyp deps/zlib/GN-scraper.py deps/zlib/win32/zlib.def deps rm -rf deps/zlib zlib/.git mv zlib deps/ -mv deps/zlib.gyp deps/zlib/ +mv deps/zlib.gyp deps/GN-scraper.py deps/zlib/ mkdir deps/zlib/win32 mv deps/zlib.def deps/zlib/win32 sed -i -- 's_^#include "chromeconf.h"_//#include "chromeconf.h"_' deps/zlib/zconf.h diff --git a/content/v18.x/en-US/doc/contributing/offboarding.md b/content/v18.x/en-US/doc/contributing/offboarding.md index 87f21103540be..f30688a33d0d8 100644 --- a/content/v18.x/en-US/doc/contributing/offboarding.md +++ b/content/v18.x/en-US/doc/contributing/offboarding.md @@ -12,6 +12,9 @@ emeritus or leaves the project. a team listing. For example, if someone is removed from @nodejs/build, they should also be removed from the Build WG README.md file in the repository. + * When in doubt, especially if you are unable to get in contact with the + collaborator, remove them from all teams. It is easy enough to add them + back later, so we err on the side of privacy and security. * Open an issue in the [build](https://github.com/nodejs/build) repository titled `Remove Collaborator from Coverity` asking that the collaborator be removed from the Node.js coverity project if they had access. diff --git a/content/v18.x/en-US/doc/contributing/primordials.md b/content/v18.x/en-US/doc/contributing/primordials.md index 7980bd79b3b27..5570a17f6bc5b 100644 --- a/content/v18.x/en-US/doc/contributing/primordials.md +++ b/content/v18.x/en-US/doc/contributing/primordials.md @@ -363,33 +363,52 @@ Object.defineProperty(Object.prototype, Symbol.isConcatSpreadable, { // 1. Lookup @@iterator property on `array` (user-mutable if user-provided). // 2. Lookup @@iterator property on %Array.prototype% (user-mutable). // 3. Lookup `next` property on %ArrayIteratorPrototype% (user-mutable). +// 4. Lookup `then` property on %Array.Prototype% (user-mutable). +// 5. Lookup `then` property on %Object.Prototype% (user-mutable). PromiseAll([]); // unsafe -PromiseAll(new SafeArrayIterator([])); // safe +// 1. Lookup `then` property on %Array.Prototype% (user-mutable). +// 2. Lookup `then` property on %Object.Prototype% (user-mutable). +PromiseAll(new SafeArrayIterator([])); // still unsafe +SafePromiseAll([]); // still unsafe + +SafePromiseAllReturnVoid([]); // safe +SafePromiseAllReturnArrayLike([]); // safe const array = [promise]; const set = new SafeSet().add(promise); // When running one of these functions on a non-empty iterable, it will also: -// 4. Lookup `then` property on `promise` (user-mutable if user-provided). -// 5. Lookup `then` property on `%Promise.prototype%` (user-mutable). +// 1. Lookup `then` property on `promise` (user-mutable if user-provided). +// 2. Lookup `then` property on `%Promise.prototype%` (user-mutable). +// 3. Lookup `then` property on %Array.Prototype% (user-mutable). +// 4. Lookup `then` property on %Object.Prototype% (user-mutable). PromiseAll(new SafeArrayIterator(array)); // unsafe - PromiseAll(set); // unsafe -SafePromiseAll(array); // safe +SafePromiseAllReturnVoid(array); // safe +SafePromiseAllReturnArrayLike(array); // safe // Some key differences between `SafePromise[...]` and `Promise[...]` methods: -// 1. SafePromiseAll, SafePromiseAllSettled, SafePromiseAny, and SafePromiseRace -// support passing a mapperFunction as second argument. +// 1. SafePromiseAll, SafePromiseAllSettled, SafePromiseAny, SafePromiseRace, +// SafePromiseAllReturnArrayLike, SafePromiseAllReturnVoid, and +// SafePromiseAllSettledReturnVoid support passing a mapperFunction as second +// argument. SafePromiseAll(ArrayPrototypeMap(array, someFunction)); SafePromiseAll(array, someFunction); // Same as the above, but more efficient. -// 2. SafePromiseAll, SafePromiseAllSettled, SafePromiseAny, and SafePromiseRace -// only support arrays, not iterables. Use ArrayFrom to convert an iterable -// to an array. -SafePromiseAll(set); // ignores set content. -SafePromiseAll(ArrayFrom(set)); // safe +// 2. SafePromiseAll, SafePromiseAllSettled, SafePromiseAny, SafePromiseRace, +// SafePromiseAllReturnArrayLike, SafePromiseAllReturnVoid, and +// SafePromiseAllSettledReturnVoid only support arrays and array-like +// objects, not iterables. Use ArrayFrom to convert an iterable to an array. +SafePromiseAllReturnVoid(set); // ignores set content. +SafePromiseAllReturnVoid(ArrayFrom(set)); // works + +// 3. SafePromiseAllReturnArrayLike is safer than SafePromiseAll, however you +// should not use them when its return value is passed to the user as it can +// be surprising for them not to receive a genuine array. +SafePromiseAllReturnArrayLike(array).then((val) => val instanceof Array); // false +SafePromiseAll(array).then((val) => val instanceof Array); // true ``` @@ -497,7 +516,7 @@ Promise.prototype.then = function then(a, b) { let thenBlockExecuted = false; PromisePrototypeThen( PromiseAll(new SafeArrayIterator([PromiseResolve()])), - () => { thenBlockExecuted = true; } + () => { thenBlockExecuted = true; }, ); process.on('exit', () => console.log(thenBlockExecuted)); // false ``` @@ -512,7 +531,7 @@ Promise.prototype.then = function then(a, b) { let thenBlockExecuted = false; PromisePrototypeThen( SafePromiseAll([PromiseResolve()]), - () => { thenBlockExecuted = true; } + () => { thenBlockExecuted = true; }, ); process.on('exit', () => console.log(thenBlockExecuted)); // true ``` @@ -639,6 +658,9 @@ RegExp.prototype.exec = () => null; // Core console.log(RegExpPrototypeTest(/o/, 'foo')); // false console.log(RegExpPrototypeExec(/o/, 'foo') !== null); // true + +console.log(RegExpPrototypeSymbolSearch(/o/, 'foo')); // -1 +console.log(SafeStringPrototypeSearch('foo', /o/)); // 1 ``` #### Don't trust `RegExp` flags @@ -668,19 +690,7 @@ Object.defineProperty(RegExp.prototype, 'global', { value: false }); // Core console.log(RegExpPrototypeSymbolReplace(/o/g, 'foo', 'a')); // 'fao' - -const regex = /o/g; -ObjectDefineProperties(regex, { - dotAll: { value: false }, - exec: { value: undefined }, - flags: { value: 'g' }, - global: { value: true }, - ignoreCase: { value: false }, - multiline: { value: false }, - unicode: { value: false }, - sticky: { value: false }, -}); -console.log(RegExpPrototypeSymbolReplace(regex, 'foo', 'a')); // 'faa' +console.log(RegExpPrototypeSymbolReplace(hardenRegExp(/o/g), 'foo', 'a')); // 'faa' ``` ### Defining object own properties diff --git a/content/v18.x/en-US/doc/contributing/pull-requests.md b/content/v18.x/en-US/doc/contributing/pull-requests.md index 403c9aa7daf24..33715bc3c2096 100644 --- a/content/v18.x/en-US/doc/contributing/pull-requests.md +++ b/content/v18.x/en-US/doc/contributing/pull-requests.md @@ -47,7 +47,7 @@ Node.js has many channels on the channels are: [#nodejs](https://openjs-foundation.slack.com/archives/CK9Q4MB53) for general help, questions, and discussions. -[#nodejs-dev](https://openjs-foundation.slack.com/archives/C019Y2T6STH) for +[#nodejs-core](https://openjs-foundation.slack.com/archives/C019Y2T6STH) for development of Node.js core specifically. Node.js also has an unofficial IRC channel: @@ -107,13 +107,13 @@ git checkout -b my-branch -t upstream/HEAD ### Step 3: Code -The vast majority of pull requests opened against the `nodejs/node` -repository includes changes to one or more of the following: +Pull requests in Node.js typically involve changes to +one or more of a few places in the repository. -* the C/C++ code contained in the `src` directory -* the JavaScript code contained in the `lib` directory -* the documentation in `doc/api` -* tests within the `test` directory. +* C/C++ code contained in the `src` directory +* JavaScript code contained in the `lib` directory +* Documentation in `doc/api` +* Tests within the `test` directory If you are modifying code, please be sure to run `make lint` (or `vcbuild.bat lint` on Windows) to ensure that the changes follow the Node.js diff --git a/content/v18.x/en-US/doc/contributing/releases.md b/content/v18.x/en-US/doc/contributing/releases.md index a1045476d1839..a1c53bbf74935 100644 --- a/content/v18.x/en-US/doc/contributing/releases.md +++ b/content/v18.x/en-US/doc/contributing/releases.md @@ -603,13 +603,14 @@ the build before moving forward. Use the following list as a baseline: must be in the expected updated version) * npm version (check it matches what we expect) * Run the test suite against the built binaries (optional) + * Remember to use the proposal branch + * Run `make build-addons` before running the tests + * Remove `config.gypi` file ```console ./tools/test.py --shell ~/Downloads/node-v18.5.0-linux-x64/bin/node ``` -There may be test issues if the branch used to test does not match the Node.js binary. - ### 11. Tag and sign the release commit Once you have produced builds that you're happy with, create a new tag. By @@ -720,6 +721,24 @@ Revert all changes that were made to `src/node_version.h`: $ git checkout --ours HEAD -- src/node_version.h ``` +
+Major version release + +On the main branch, instead of reverting changes made to `src/node_version.h` +edit it instead and: + +* Increment `NODE_MAJOR_VERSION` by one +* Reset `NODE_PATCH_VERSION` to `0` +* Change `NODE_VERSION_IS_RELEASE` back to `0` + +Amend the current commit to apply the changes: + +```console +$ git commit --amend +``` + +
+ Even if there are no conflicts, ensure that you revert all the changes that were made to `src/node_version.h`. @@ -739,7 +758,7 @@ Then finish cherry-picking and push the commit upstream: $ git add src/node_version.h doc $ git diff --staged src doc # read output to validate that changes shows up as expected $ git cherry-pick --continue -$ make lint +$ make lint-md && make lint-cpp $ git push upstream main ``` @@ -902,6 +921,8 @@ This script will use the promoted builds and changelog to generate the post. Run * Select the tag version you pushed earlier. * For release title, copy the title from the changelog. * For the description, copy the rest of the changelog entry. +* If you are not releasing the latest "Current", uncheck + "Set as the latest release". * Click on the "Publish release" button. ### 19. Cleanup @@ -940,6 +961,31 @@ _In whatever form you do this..._ ### Marking a release line as LTS +The process of marking a release line as LTS has been automated using +[node-core-utils](https://github.com/nodejs/node-core-utils). + +Start by checking out the staging branch for the release line that is going to +be marked as LTS, e.g: + +```console +$ git checkout v1.x-staging +``` + +Next, make sure you have **node-core-utils** installed: + +```console +$ npm i -g node-core-utils +``` + +Run the prepare LTS release command: + +```console +$ git node release --prepare --startLTS +``` + +
+Manual steps for reference. + To mark a release line as LTS, the following changes must be made to `src/node_version.h`: @@ -968,6 +1014,18 @@ For example: The changes must be made as part of a new semver-minor release. +Updating changelogs to properly reflect the changes between **Current** and +**Long Term Support** is also necessary, along with adding a reference to the +current LTS codename in its release line changelog file. + +The `test/parallel/test-process-release.js` file might also need to be updated. + +In case you can not run the automated `node-core-utils` command and you are +currently running these steps manually it's a good idea to refer to previous +LTS proposal PRs and make sure all required changes are covered. + +
+ ### Update release labels The `lts-watch-vN.x` issue label must be created, with the same color as other @@ -976,6 +1034,25 @@ existing labels for that release line, such as `vN.x`. If the release is transitioning from Active LTS to Maintenance, the `backport-requested-vN.x` label must be deleted. +### Add new codename to nodejs-latest-linker + +In order to make sure a download URL +(e.g: ) will be available +for the new LTS release line you need to submit a PR to + and add a new entry for the +new LTS codename in its `ltsNames` map located in the `./latest-linker.js` +file. + +Make sure to reach out to the Build WG in order to validate that the new URL is +available as part of the LTS release promotion. + +### Update Release repo info + +Add the new LTS codename to the release schedule table located in the +`./README.md` file located at the +repository along with the addition of the new codename to the `./schedule.json` +file in that same repo. + ## Major releases The process for cutting a new Node.js major release has a number of differences diff --git a/content/v18.x/en-US/doc/contributing/security-release-process.md b/content/v18.x/en-US/doc/contributing/security-release-process.md index 468f9ad4e5174..c2847da26665b 100644 --- a/content/v18.x/en-US/doc/contributing/security-release-process.md +++ b/content/v18.x/en-US/doc/contributing/security-release-process.md @@ -26,7 +26,7 @@ The current security stewards are documented in the main Node.js | RH and IBM | Joe | 2022-Mar-18 | | NearForm | Matteo / Rafael | 2022-Jul-07 | | Datadog | Vladimir | 2022-Sep-23 | -| NodeSource | Juan | | +| NodeSource | Juan | 2022-Nov-04 | | RH and IBM | Michael | | ## Planning @@ -196,6 +196,27 @@ out a better way, forward the email you receive to [Security release stewards](https://github.com/nodejs/node/blob/HEAD/doc/contributing/security-release-process.md#security-release-stewards). If necessary add the next rotation of the steward rotation. +## When things go wrong + +### Incomplete fixes + +When a CVE is reported as fixed in a security release and it turns out that the +fix was incomplete, a new CVE should be used to cover subsequent fix. This +is best practice and avoids confusion that might occur if people believe +they have patched the original CVE by updating their Node.js version and +then we later change the `fixed in` value for the CVE. + +### Updating CVEs + +The steps to correct CVE information are: + +* Go to the “CVE IDs” section in your program + sections () +* Click the “Request a CVE ID” button +* Enter the CVE ID that needs to be updated +* Include all the details that need updating within the form +* Submit the request + [H1 CVE requests]: https://hackerone.com/nodejs/cve_requests [docker-node]: https://github.com/nodejs/docker-node/issues [email]: https://groups.google.com/forum/#!forum/nodejs-sec diff --git a/content/v18.x/en-US/doc/contributing/streaming-to-youtube.md b/content/v18.x/en-US/doc/contributing/streaming-to-youtube.md new file mode 100644 index 0000000000000..5cac25b76d342 --- /dev/null +++ b/content/v18.x/en-US/doc/contributing/streaming-to-youtube.md @@ -0,0 +1,117 @@ +# Streaming Meetings to Youtube + +We publicly live stream our meetings to YouTube using [Zoom](https://zoom.us/). + +## Getting Access + +You need the Foundation login credentials for Zoom in order to host the meeting. +The userid and password is shared through 1password. + +Your YouTube Account must be a manager of the +[Node.js YouTube account](https://www.youtube.com/channel/UCQPYJluYC_sn_Qz_XE-YbTQ). + +To request access open an issue in the Node.js +[admin](https://github.com/nodejs/admin) repository with the title being +`Zoom and Youtube access for X` where X is the GitHub id and +the YouTube ID of the person for which access is being requested. +Include a short reason why access is needed(for example streaming +a team meeting etc.). + +Unless there are objections the request is considered approved after 48 hours. + +## Managing access + +### Youtube + +To add managers or verify an account is a manager: + +1. Go to +2. Click on the Node.js icon on the right top. +3. Select settings, select "Add or remove managers", select "Manage permissions" +4. On that page you can use the +people at the top right of the popup to add + people. It also lists all current managers. + +### Zoom + +To share the Zoom password log into 1password, select the settings gear for +the`zoom-creds` vault and then use `Share Vault` to share the vault with the +new user. Use the gear to set the permissions to include only `View Items` and +`View and Copy passwords`. + +When adding access for a user also ask them to create a PR adding themselves +to the `zoom-nodejs` group in the +[iojs.org/aliases.json](https://github.com/nodejs/email/blob/main/iojs.org/aliases.json) +file [nodejs/email](https://github.com/nodejs/email/) + +## Live streaming a meeting + +### Start and Stop the stream + +1. Login to using the Foundation credentials. +2. Go to , find the meeting. +3. Press "Start", it should open the meeting in the Zoom application. +4. Go to "Participants" panel, check Attendees, promote them to panelists. +5. Go to "... More" in toolbar, choose "Live on YouTube", it will open in + browser. +6. Choose to login to with Node.js account, accept + Zoom usage agreement (on first use) +7. On the Streaming page, edit the webinar title to include the meeting date, + then press the red "Go Live!" button. Troubleshooting note: at least one + person has found that "Go Live!" errored with a message "Please grant + necessary privilege for live streaming". Copying the link from the default + browser to a different browser may work around this issue. + +Every participant can choose whether to participate with or without video. + +YouTube records the live stream. Recordings are made available on the +[Node.js channel](https://www.youtube.com/channel/UCQPYJluYC_sn_Qz_XE-YbTQ/videos). + +The stream title is set automatically from the information in Zoom. We usually +set it to `YYYY-MM-DD - Meeting Name` for example +`2022-11-02 - Technical Steering Community Meeting`. + +The description should be a link to the meeting issue. + +You can edit title and description on YouTube at a later time if needed. + +![YouTube Basic Info example text](./doc_img/youtube-stream-title-description.png) + +### Share the meeting once you've gone live + +The meeting link should be `http://www.youtube.com/c/nodejs-foundation/live`. + +Send it in a tweet such as: + +```text +.@nodejs Technical Steering Committee meeting live now: +http://www.youtube.com/c/nodejs-foundation/live +``` + +Adjust the `Technical Steering Committee` part as necessary and remove the +`.@nodejs` if tweeting from the official twitter account. + +![YouTube Share input box](./doc_img/youtube-stream-share.png) + +## Check stream status + +This should say online when you are streaming, and usually be green. + +However, it may turn yellow and issue a warning in a "stream health" +section below. Since we usually stream with static images for the +video, there will often be warnings that the video bitrate is low. +This is a not a problem and should almost always be ignored. + +![YouTube Stream Status showing Offline](./doc_img/youtube-stream-status.png) + +## Check how many people are watching + +![YouTube Analytics graph](./doc_img/youtube-stream-analytics.png) + +## Moderate the chat and solicit questions + +Moderation follows the [Moderation Policy](https://github.com/nodejs/admin/blob/main/Moderation-Policy.md). +Messages can be moderated right-clicking and selecting the necessary action, +such as `remove`. + +If you participate in the chat while logged in as Node.js, it's good +practice to append your initials to your messages. diff --git a/content/v18.x/en-US/doc/contributing/suggesting-social-media-posts.md b/content/v18.x/en-US/doc/contributing/suggesting-social-media-posts.md new file mode 100644 index 0000000000000..7b7200ee7bac8 --- /dev/null +++ b/content/v18.x/en-US/doc/contributing/suggesting-social-media-posts.md @@ -0,0 +1,5 @@ +# Suggesting Social Media Posts + +Node.js social media is managed by OpenJS Foundation staff. The processes are +documented in +[Node.js Social Amplification Request Guidelines](https://docs.google.com/document/d/1yrYZJ2twrbpUuScbo3rmN_v-Jfv6d2tO74nCT6PcpxI). diff --git a/content/v18.x/en-US/doc/contributing/using-internal-errors.md b/content/v18.x/en-US/doc/contributing/using-internal-errors.md index b55b187bff015..6e88ca9863f5a 100644 --- a/content/v18.x/en-US/doc/contributing/using-internal-errors.md +++ b/content/v18.x/en-US/doc/contributing/using-internal-errors.md @@ -122,7 +122,7 @@ assert.throws(() => { socket.bind(); }, common.expectsError({ code: 'ERR_SOCKET_ALREADY_BOUND', - type: Error + type: Error, })); ``` diff --git a/content/v18.x/en-US/doc/contributing/writing-and-running-benchmarks.md b/content/v18.x/en-US/doc/contributing/writing-and-running-benchmarks.md index 99325d876928b..9422d42519453 100644 --- a/content/v18.x/en-US/doc/contributing/writing-and-running-benchmarks.md +++ b/content/v18.x/en-US/doc/contributing/writing-and-running-benchmarks.md @@ -486,12 +486,12 @@ const configs = { // Most benchmarks just use one value for all runs. n: [1024], type: ['fast', 'slow'], // Custom configurations - size: [16, 128, 1024] // Custom configurations + size: [16, 128, 1024], // Custom configurations }; const options = { // Add --expose-internals in order to require internal modules in main - flags: ['--zero-fill-buffers'] + flags: ['--zero-fill-buffers'], }; // `main` and `configs` are required, `options` is optional. @@ -535,7 +535,7 @@ const common = require('../common.js'); const bench = common.createBenchmark(main, { kb: [64, 128, 256, 1024], connections: [100, 500], - duration: 5 + duration: 5, }); function main(conf) { diff --git a/content/v18.x/en-US/doc/contributing/writing-tests.md b/content/v18.x/en-US/doc/contributing/writing-tests.md index cc75191d32027..dc4954fe9a874 100644 --- a/content/v18.x/en-US/doc/contributing/writing-tests.md +++ b/content/v18.x/en-US/doc/contributing/writing-tests.md @@ -46,7 +46,7 @@ const server = http.createServer(common.mustCall((req, res) => { // 11 server.listen(0, () => { // 14 http.get({ // 15 port: server.address().port, // 16 - headers: { 'Test': 'Düsseldorf' } // 17 + headers: { 'Test': 'Düsseldorf' }, // 17 }, common.mustCall((res) => { // 18 assert.strictEqual(res.statusCode, 200); // 19 server.close(); // 20 @@ -116,7 +116,7 @@ const server = http.createServer(common.mustCall((req, res) => { server.listen(0, () => { http.get({ port: server.address().port, - headers: { 'Test': 'Düsseldorf' } + headers: { 'Test': 'Düsseldorf' }, }, common.mustCall((res) => { assert.strictEqual(res.statusCode, 200); server.close(); @@ -192,7 +192,7 @@ const server = http.createServer((req, res) => { listening++; const options = { agent: null, - port: server.address().port + port: server.address().port, }; http.get(options, (res) => { response++; @@ -214,7 +214,7 @@ const server = http.createServer(common.mustCall((req, res) => { })).listen(0, common.mustCall(() => { const options = { agent: null, - port: server.address().port + port: server.address().port, }; http.get(options, common.mustCall((res) => { res.resume(); @@ -262,7 +262,7 @@ const fs = require('node:fs').promises; // Wrap the `onFulfilled` handler in `common.mustCall()`. fs.readFile('test-file').then( common.mustCall( - (content) => assert.strictEqual(content.toString(), 'test2') + (content) => assert.strictEqual(content.toString(), 'test2'), )); ``` @@ -308,7 +308,7 @@ assert.throws( () => { throw new Error('Wrong value'); }, - /^Error: Wrong value$/ // Instead of something like /Wrong value/ + /^Error: Wrong value$/, // Instead of something like /Wrong value/ ); ``` @@ -319,7 +319,7 @@ assert.throws( () => { throw new ERR_FS_FILE_TOO_LARGE(`${sizeKiB} Kb`); }, - { code: 'ERR_FS_FILE_TOO_LARGE' } + { code: 'ERR_FS_FILE_TOO_LARGE' }, // Do not include message: /^File size ([0-9]+ Kb) is greater than 2 GiB$/ ); ```