diff --git a/fixtures/addons.md b/fixtures/addons.md deleted file mode 100644 index 446a63b..0000000 --- a/fixtures/addons.md +++ /dev/null @@ -1,1387 +0,0 @@ -# C++ addons - - - - - -_Addons_ are dynamically-linked shared objects written in C++. The -[`require()`][require] function can load addons as ordinary Node.js modules. -Addons provide an interface between JavaScript and C/C++ libraries. - -There are three options for implementing addons: Node-API, nan, or direct -use of internal V8, libuv, and Node.js libraries. Unless there is a need for -direct access to functionality which is not exposed by Node-API, use Node-API. -Refer to [C/C++ addons with Node-API](n-api.md) for more information on -Node-API. - -When not using Node-API, implementing addons is complicated, -involving knowledge of several components and APIs: - -* [V8][]: the C++ library Node.js uses to provide the - JavaScript implementation. V8 provides the mechanisms for creating objects, - calling functions, etc. V8's API is documented mostly in the - `v8.h` header file (`deps/v8/include/v8.h` in the Node.js source - tree), which is also available [online][v8-docs]. - -* [libuv][]: The C library that implements the Node.js event loop, its worker - threads and all of the asynchronous behaviors of the platform. It also - serves as a cross-platform abstraction library, giving easy, POSIX-like - access across all major operating systems to many common system tasks, such - as interacting with the file system, sockets, timers, and system events. libuv - also provides a threading abstraction similar to POSIX threads for - more sophisticated asynchronous addons that need to move beyond the - standard event loop. Addon authors should - avoid blocking the event loop with I/O or other time-intensive tasks by - offloading work via libuv to non-blocking system operations, worker threads, - or a custom use of libuv threads. - -* Internal Node.js libraries. Node.js itself exports C++ APIs that addons can - use, the most important of which is the `node::ObjectWrap` class. - -* Node.js includes other statically linked libraries including OpenSSL. These - other libraries are located in the `deps/` directory in the Node.js source - tree. Only the libuv, OpenSSL, V8, and zlib symbols are purposefully - re-exported by Node.js and may be used to various extents by addons. See - [Linking to libraries included with Node.js][] for additional information. - -All of the following examples are available for [download][] and may -be used as the starting-point for an addon. - -## Hello world - -This "Hello world" example is a simple addon, written in C++, that is the -equivalent of the following JavaScript code: - -```js -module.exports.hello = () => 'world'; -``` - -First, create the file `hello.cc`: - -```cpp -// hello.cc -#include - -namespace demo { - -using v8::FunctionCallbackInfo; -using v8::Isolate; -using v8::Local; -using v8::Object; -using v8::String; -using v8::Value; - -void Method(const FunctionCallbackInfo& args) { - Isolate* isolate = args.GetIsolate(); - args.GetReturnValue().Set(String::NewFromUtf8( - isolate, "world").ToLocalChecked()); -} - -void Initialize(Local exports) { - NODE_SET_METHOD(exports, "hello", Method); -} - -NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize) - -} // namespace demo -``` - -All Node.js addons must export an initialization function following -the pattern: - -```cpp -void Initialize(Local exports); -NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize) -``` - -There is no semi-colon after `NODE_MODULE` as it's not a function (see -`node.h`). - -The `module_name` must match the filename of the final binary (excluding -the `.node` suffix). - -In the `hello.cc` example, then, the initialization function is `Initialize` -and the addon module name is `addon`. - -When building addons with `node-gyp`, using the macro `NODE_GYP_MODULE_NAME` as -the first parameter of `NODE_MODULE()` will ensure that the name of the final -binary will be passed to `NODE_MODULE()`. - -Addons defined with `NODE_MODULE()` can not be loaded in multiple contexts or -multiple threads at the same time. - -### Context-aware addons - -There are environments in which Node.js addons may need to be loaded multiple -times in multiple contexts. For example, the [Electron][] runtime runs multiple -instances of Node.js in a single process. Each instance will have its own -`require()` cache, and thus each instance will need a native addon to behave -correctly when loaded via `require()`. This means that the addon -must support multiple initializations. - -A context-aware addon can be constructed by using the macro -`NODE_MODULE_INITIALIZER`, which expands to the name of a function which Node.js -will expect to find when it loads an addon. An addon can thus be initialized as -in the following example: - -```cpp -using namespace v8; - -extern "C" NODE_MODULE_EXPORT void -NODE_MODULE_INITIALIZER(Local exports, - Local module, - Local context) { - /* Perform addon initialization steps here. */ -} -``` - -Another option is to use the macro `NODE_MODULE_INIT()`, which will also -construct a context-aware addon. Unlike `NODE_MODULE()`, which is used to -construct an addon around a given addon initializer function, -`NODE_MODULE_INIT()` serves as the declaration of such an initializer to be -followed by a function body. - -The following three variables may be used inside the function body following an -invocation of `NODE_MODULE_INIT()`: - -* `Local exports`, -* `Local module`, and -* `Local context` - -The choice to build a context-aware addon carries with it the responsibility of -carefully managing global static data. Since the addon may be loaded multiple -times, potentially even from different threads, any global static data stored -in the addon must be properly protected, and must not contain any persistent -references to JavaScript objects. The reason for this is that JavaScript -objects are only valid in one context, and will likely cause a crash when -accessed from the wrong context or from a different thread than the one on which -they were created. - -The context-aware addon can be structured to avoid global static data by -performing the following steps: - -* Define a class which will hold per-addon-instance data and which has a static - member of the form - ```cpp - static void DeleteInstance(void* data) { - // Cast `data` to an instance of the class and delete it. - } - ``` -* Heap-allocate an instance of this class in the addon initializer. This can be - accomplished using the `new` keyword. -* Call `node::AddEnvironmentCleanupHook()`, passing it the above-created - instance and a pointer to `DeleteInstance()`. This will ensure the instance is - deleted when the environment is torn down. -* Store the instance of the class in a `v8::External`, and -* Pass the `v8::External` to all methods exposed to JavaScript by passing it - to `v8::FunctionTemplate::New()` or `v8::Function::New()` which creates the - native-backed JavaScript functions. The third parameter of - `v8::FunctionTemplate::New()` or `v8::Function::New()` accepts the - `v8::External` and makes it available in the native callback using the - `v8::FunctionCallbackInfo::Data()` method. - -This will ensure that the per-addon-instance data reaches each binding that can -be called from JavaScript. The per-addon-instance data must also be passed into -any asynchronous callbacks the addon may create. - -The following example illustrates the implementation of a context-aware addon: - -```cpp -#include - -using namespace v8; - -class AddonData { - public: - explicit AddonData(Isolate* isolate): - call_count(0) { - // Ensure this per-addon-instance data is deleted at environment cleanup. - node::AddEnvironmentCleanupHook(isolate, DeleteInstance, this); - } - - // Per-addon data. - int call_count; - - static void DeleteInstance(void* data) { - delete static_cast(data); - } -}; - -static void Method(const v8::FunctionCallbackInfo& info) { - // Retrieve the per-addon-instance data. - AddonData* data = - reinterpret_cast(info.Data().As()->Value()); - data->call_count++; - info.GetReturnValue().Set((double)data->call_count); -} - -// Initialize this addon to be context-aware. -NODE_MODULE_INIT(/* exports, module, context */) { - Isolate* isolate = context->GetIsolate(); - - // Create a new instance of `AddonData` for this instance of the addon and - // tie its life cycle to that of the Node.js environment. - AddonData* data = new AddonData(isolate); - - // Wrap the data in a `v8::External` so we can pass it to the method we - // expose. - Local external = External::New(isolate, data); - - // Expose the method `Method` to JavaScript, and make sure it receives the - // per-addon-instance data we created above by passing `external` as the - // third parameter to the `FunctionTemplate` constructor. - exports->Set(context, - String::NewFromUtf8(isolate, "method").ToLocalChecked(), - FunctionTemplate::New(isolate, Method, external) - ->GetFunction(context).ToLocalChecked()).FromJust(); -} -``` - -#### Worker support - - - -In order to be loaded from multiple Node.js environments, -such as a main thread and a Worker thread, an add-on needs to either: - -* Be an Node-API addon, or -* Be declared as context-aware using `NODE_MODULE_INIT()` as described above - -In order to support [`Worker`][] threads, addons need to clean up any resources -they may have allocated when such a thread exists. This can be achieved through -the usage of the `AddEnvironmentCleanupHook()` function: - -```cpp -void AddEnvironmentCleanupHook(v8::Isolate* isolate, - void (*fun)(void* arg), - void* arg); -``` - -This function adds a hook that will run before a given Node.js instance shuts -down. If necessary, such hooks can be removed before they are run using -`RemoveEnvironmentCleanupHook()`, which has the same signature. Callbacks are -run in last-in first-out order. - -If necessary, there is an additional pair of `AddEnvironmentCleanupHook()` -and `RemoveEnvironmentCleanupHook()` overloads, where the cleanup hook takes a -callback function. This can be used for shutting down asynchronous resources, -such as any libuv handles registered by the addon. - -The following `addon.cc` uses `AddEnvironmentCleanupHook`: - -```cpp -// addon.cc -#include -#include -#include - -using node::AddEnvironmentCleanupHook; -using v8::HandleScope; -using v8::Isolate; -using v8::Local; -using v8::Object; - -// Note: In a real-world application, do not rely on static/global data. -static char cookie[] = "yum yum"; -static int cleanup_cb1_called = 0; -static int cleanup_cb2_called = 0; - -static void cleanup_cb1(void* arg) { - Isolate* isolate = static_cast(arg); - HandleScope scope(isolate); - Local obj = Object::New(isolate); - assert(!obj.IsEmpty()); // assert VM is still alive - assert(obj->IsObject()); - cleanup_cb1_called++; -} - -static void cleanup_cb2(void* arg) { - assert(arg == static_cast(cookie)); - cleanup_cb2_called++; -} - -static void sanity_check(void*) { - assert(cleanup_cb1_called == 1); - assert(cleanup_cb2_called == 1); -} - -// Initialize this addon to be context-aware. -NODE_MODULE_INIT(/* exports, module, context */) { - Isolate* isolate = context->GetIsolate(); - - AddEnvironmentCleanupHook(isolate, sanity_check, nullptr); - AddEnvironmentCleanupHook(isolate, cleanup_cb2, cookie); - AddEnvironmentCleanupHook(isolate, cleanup_cb1, isolate); -} -``` - -Test in JavaScript by running: - -```js -// test.js -require('./build/Release/addon'); -``` - -### Building - -Once the source code has been written, it must be compiled into the binary -`addon.node` file. To do so, create a file called `binding.gyp` in the -top-level of the project describing the build configuration of the module -using a JSON-like format. This file is used by [node-gyp][], a tool written -specifically to compile Node.js addons. - -```json -{ - "targets": [ - { - "target_name": "addon", - "sources": [ "hello.cc" ] - } - ] -} -``` - -A version of the `node-gyp` utility is bundled and distributed with -Node.js as part of `npm`. This version is not made directly available for -developers to use and is intended only to support the ability to use the -`npm install` command to compile and install addons. Developers who wish to -use `node-gyp` directly can install it using the command -`npm install -g node-gyp`. See the `node-gyp` [installation instructions][] for -more information, including platform-specific requirements. - -Once the `binding.gyp` file has been created, use `node-gyp configure` to -generate the appropriate project build files for the current platform. This -will generate either a `Makefile` (on Unix platforms) or a `vcxproj` file -(on Windows) in the `build/` directory. - -Next, invoke the `node-gyp build` command to generate the compiled `addon.node` -file. This will be put into the `build/Release/` directory. - -When using `npm install` to install a Node.js addon, npm uses its own bundled -version of `node-gyp` to perform this same set of actions, generating a -compiled version of the addon for the user's platform on demand. - -Once built, the binary addon can be used from within Node.js by pointing -[`require()`][require] to the built `addon.node` module: - -```js -// hello.js -const addon = require('./build/Release/addon'); - -console.log(addon.hello()); -// Prints: 'world' -``` - -Because the exact path to the compiled addon binary can vary depending on how -it is compiled (i.e. sometimes it may be in `./build/Debug/`), addons can use -the [bindings][] package to load the compiled module. - -While the `bindings` package implementation is more sophisticated in how it -locates addon modules, it is essentially using a `try…catch` pattern similar to: - -```js -try { - return require('./build/Release/addon.node'); -} catch (err) { - return require('./build/Debug/addon.node'); -} -``` - -### Linking to libraries included with Node.js - -Node.js uses statically linked libraries such as V8, libuv, and OpenSSL. All -addons are required to link to V8 and may link to any of the other dependencies -as well. Typically, this is as simple as including the appropriate -`#include <...>` statements (e.g. `#include `) and `node-gyp` will locate -the appropriate headers automatically. However, there are a few caveats to be -aware of: - -* When `node-gyp` runs, it will detect the specific release version of Node.js - and download either the full source tarball or just the headers. If the full - source is downloaded, addons will have complete access to the full set of - Node.js dependencies. However, if only the Node.js headers are downloaded, - then only the symbols exported by Node.js will be available. - -* `node-gyp` can be run using the `--nodedir` flag pointing at a local Node.js - source image. Using this option, the addon will have access to the full set of - dependencies. - -### Loading addons using `require()` - -The filename extension of the compiled addon binary is `.node` (as opposed -to `.dll` or `.so`). The [`require()`][require] function is written to look for -files with the `.node` file extension and initialize those as dynamically-linked -libraries. - -When calling [`require()`][require], the `.node` extension can usually be -omitted and Node.js will still find and initialize the addon. One caveat, -however, is that Node.js will first attempt to locate and load modules or -JavaScript files that happen to share the same base name. For instance, if -there is a file `addon.js` in the same directory as the binary `addon.node`, -then [`require('addon')`][require] will give precedence to the `addon.js` file -and load it instead. - -## Native abstractions for Node.js - -Each of the examples illustrated in this document directly use the -Node.js and V8 APIs for implementing addons. The V8 API can, and has, changed -dramatically from one V8 release to the next (and one major Node.js release to -the next). With each change, addons may need to be updated and recompiled in -order to continue functioning. The Node.js release schedule is designed to -minimize the frequency and impact of such changes but there is little that -Node.js can do to ensure stability of the V8 APIs. - -The [Native Abstractions for Node.js][] (or `nan`) provide a set of tools that -addon developers are recommended to use to keep compatibility between past and -future releases of V8 and Node.js. See the `nan` [examples][] for an -illustration of how it can be used. - -## Node-API - -> Stability: 2 - Stable - -Node-API is an API for building native addons. It is independent from -the underlying JavaScript runtime (e.g. V8) and is maintained as part of -Node.js itself. This API will be Application Binary Interface (ABI) stable -across versions of Node.js. It is intended to insulate addons from -changes in the underlying JavaScript engine and allow modules -compiled for one version to run on later versions of Node.js without -recompilation. Addons are built/packaged with the same approach/tools -outlined in this document (node-gyp, etc.). The only difference is the -set of APIs that are used by the native code. Instead of using the V8 -or [Native Abstractions for Node.js][] APIs, the functions available -in the Node-API are used. - -Creating and maintaining an addon that benefits from the ABI stability -provided by Node-API carries with it certain -[implementation considerations][]. - -To use Node-API in the above "Hello world" example, replace the content of -`hello.cc` with the following. All other instructions remain the same. - -```cpp -// hello.cc using Node-API -#include - -namespace demo { - -napi_value Method(napi_env env, napi_callback_info args) { - napi_value greeting; - napi_status status; - - status = napi_create_string_utf8(env, "world", NAPI_AUTO_LENGTH, &greeting); - if (status != napi_ok) return nullptr; - return greeting; -} - -napi_value init(napi_env env, napi_value exports) { - napi_status status; - napi_value fn; - - status = napi_create_function(env, nullptr, 0, Method, nullptr, &fn); - if (status != napi_ok) return nullptr; - - status = napi_set_named_property(env, exports, "hello", fn); - if (status != napi_ok) return nullptr; - return exports; -} - -NAPI_MODULE(NODE_GYP_MODULE_NAME, init) - -} // namespace demo -``` - -The functions available and how to use them are documented in -[C/C++ addons with Node-API](n-api.md). - -## Addon examples - -Following are some example addons intended to help developers get started. The -examples use the V8 APIs. Refer to the online [V8 reference][v8-docs] -for help with the various V8 calls, and V8's [Embedder's Guide][] for an -explanation of several concepts used such as handles, scopes, function -templates, etc. - -Each of these examples using the following `binding.gyp` file: - -```json -{ - "targets": [ - { - "target_name": "addon", - "sources": [ "addon.cc" ] - } - ] -} -``` - -In cases where there is more than one `.cc` file, simply add the additional -filename to the `sources` array: - -```json -"sources": ["addon.cc", "myexample.cc"] -``` - -Once the `binding.gyp` file is ready, the example addons can be configured and -built using `node-gyp`: - -```bash -node-gyp configure build -``` - -### Function arguments - -Addons will typically expose objects and functions that can be accessed from -JavaScript running within Node.js. When functions are invoked from JavaScript, -the input arguments and return value must be mapped to and from the C/C++ -code. - -The following example illustrates how to read function arguments passed from -JavaScript and how to return a result: - -```cpp -// addon.cc -#include - -namespace demo { - -using v8::Exception; -using v8::FunctionCallbackInfo; -using v8::Isolate; -using v8::Local; -using v8::Number; -using v8::Object; -using v8::String; -using v8::Value; - -// This is the implementation of the "add" method -// Input arguments are passed using the -// const FunctionCallbackInfo& args struct -void Add(const FunctionCallbackInfo& args) { - Isolate* isolate = args.GetIsolate(); - - // Check the number of arguments passed. - if (args.Length() < 2) { - // Throw an Error that is passed back to JavaScript - isolate->ThrowException(Exception::TypeError( - String::NewFromUtf8(isolate, - "Wrong number of arguments").ToLocalChecked())); - return; - } - - // Check the argument types - if (!args[0]->IsNumber() || !args[1]->IsNumber()) { - isolate->ThrowException(Exception::TypeError( - String::NewFromUtf8(isolate, - "Wrong arguments").ToLocalChecked())); - return; - } - - // Perform the operation - double value = - args[0].As()->Value() + args[1].As()->Value(); - Local num = Number::New(isolate, value); - - // Set the return value (using the passed in - // FunctionCallbackInfo&) - args.GetReturnValue().Set(num); -} - -void Init(Local exports) { - NODE_SET_METHOD(exports, "add", Add); -} - -NODE_MODULE(NODE_GYP_MODULE_NAME, Init) - -} // namespace demo -``` - -Once compiled, the example addon can be required and used from within Node.js: - -```js -// test.js -const addon = require('./build/Release/addon'); - -console.log('This should be eight:', addon.add(3, 5)); -``` - -### Callbacks - -It is common practice within addons to pass JavaScript functions to a C++ -function and execute them from there. The following example illustrates how -to invoke such callbacks: - -```cpp -// addon.cc -#include - -namespace demo { - -using v8::Context; -using v8::Function; -using v8::FunctionCallbackInfo; -using v8::Isolate; -using v8::Local; -using v8::Null; -using v8::Object; -using v8::String; -using v8::Value; - -void RunCallback(const FunctionCallbackInfo& args) { - Isolate* isolate = args.GetIsolate(); - Local context = isolate->GetCurrentContext(); - Local cb = Local::Cast(args[0]); - const unsigned argc = 1; - Local argv[argc] = { - String::NewFromUtf8(isolate, - "hello world").ToLocalChecked() }; - cb->Call(context, Null(isolate), argc, argv).ToLocalChecked(); -} - -void Init(Local exports, Local module) { - NODE_SET_METHOD(module, "exports", RunCallback); -} - -NODE_MODULE(NODE_GYP_MODULE_NAME, Init) - -} // namespace demo -``` - -This example uses a two-argument form of `Init()` that receives the full -`module` object as the second argument. This allows the addon to completely -overwrite `exports` with a single function instead of adding the function as a -property of `exports`. - -To test it, run the following JavaScript: - -```js -// test.js -const addon = require('./build/Release/addon'); - -addon((msg) => { - console.log(msg); -// Prints: 'hello world' -}); -``` - -In this example, the callback function is invoked synchronously. - -### Object factory - -Addons can create and return new objects from within a C++ function as -illustrated in the following example. An object is created and returned with a -property `msg` that echoes the string passed to `createObject()`: - -```cpp -// addon.cc -#include - -namespace demo { - -using v8::Context; -using v8::FunctionCallbackInfo; -using v8::Isolate; -using v8::Local; -using v8::Object; -using v8::String; -using v8::Value; - -void CreateObject(const FunctionCallbackInfo& args) { - Isolate* isolate = args.GetIsolate(); - Local context = isolate->GetCurrentContext(); - - Local obj = Object::New(isolate); - obj->Set(context, - String::NewFromUtf8(isolate, - "msg").ToLocalChecked(), - args[0]->ToString(context).ToLocalChecked()) - .FromJust(); - - args.GetReturnValue().Set(obj); -} - -void Init(Local exports, Local module) { - NODE_SET_METHOD(module, "exports", CreateObject); -} - -NODE_MODULE(NODE_GYP_MODULE_NAME, Init) - -} // namespace demo -``` - -To test it in JavaScript: - -```js -// test.js -const addon = require('./build/Release/addon'); - -const obj1 = addon('hello'); -const obj2 = addon('world'); -console.log(obj1.msg, obj2.msg); -// Prints: 'hello world' -``` - -### Function factory - -Another common scenario is creating JavaScript functions that wrap C++ -functions and returning those back to JavaScript: - -```cpp -// addon.cc -#include - -namespace demo { - -using v8::Context; -using v8::Function; -using v8::FunctionCallbackInfo; -using v8::FunctionTemplate; -using v8::Isolate; -using v8::Local; -using v8::Object; -using v8::String; -using v8::Value; - -void MyFunction(const FunctionCallbackInfo& args) { - Isolate* isolate = args.GetIsolate(); - args.GetReturnValue().Set(String::NewFromUtf8( - isolate, "hello world").ToLocalChecked()); -} - -void CreateFunction(const FunctionCallbackInfo& args) { - Isolate* isolate = args.GetIsolate(); - - Local context = isolate->GetCurrentContext(); - Local tpl = FunctionTemplate::New(isolate, MyFunction); - Local fn = tpl->GetFunction(context).ToLocalChecked(); - - // omit this to make it anonymous - fn->SetName(String::NewFromUtf8( - isolate, "theFunction").ToLocalChecked()); - - args.GetReturnValue().Set(fn); -} - -void Init(Local exports, Local module) { - NODE_SET_METHOD(module, "exports", CreateFunction); -} - -NODE_MODULE(NODE_GYP_MODULE_NAME, Init) - -} // namespace demo -``` - -To test: - -```js -// test.js -const addon = require('./build/Release/addon'); - -const fn = addon(); -console.log(fn()); -// Prints: 'hello world' -``` - -### Wrapping C++ objects - -It is also possible to wrap C++ objects/classes in a way that allows new -instances to be created using the JavaScript `new` operator: - -```cpp -// addon.cc -#include -#include "myobject.h" - -namespace demo { - -using v8::Local; -using v8::Object; - -void InitAll(Local exports) { - MyObject::Init(exports); -} - -NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll) - -} // namespace demo -``` - -Then, in `myobject.h`, the wrapper class inherits from `node::ObjectWrap`: - -```cpp -// myobject.h -#ifndef MYOBJECT_H -#define MYOBJECT_H - -#include -#include - -namespace demo { - -class MyObject : public node::ObjectWrap { - public: - static void Init(v8::Local exports); - - private: - explicit MyObject(double value = 0); - ~MyObject(); - - static void New(const v8::FunctionCallbackInfo& args); - static void PlusOne(const v8::FunctionCallbackInfo& args); - - double value_; -}; - -} // namespace demo - -#endif -``` - -In `myobject.cc`, implement the various methods that are to be exposed. -In the following code, the method `plusOne()` is exposed by adding it to the -constructor's prototype: - -```cpp -// myobject.cc -#include "myobject.h" - -namespace demo { - -using v8::Context; -using v8::Function; -using v8::FunctionCallbackInfo; -using v8::FunctionTemplate; -using v8::Isolate; -using v8::Local; -using v8::Number; -using v8::Object; -using v8::ObjectTemplate; -using v8::String; -using v8::Value; - -MyObject::MyObject(double value) : value_(value) { -} - -MyObject::~MyObject() { -} - -void MyObject::Init(Local exports) { - Isolate* isolate = exports->GetIsolate(); - Local context = isolate->GetCurrentContext(); - - Local addon_data_tpl = ObjectTemplate::New(isolate); - addon_data_tpl->SetInternalFieldCount(1); // 1 field for the MyObject::New() - Local addon_data = - addon_data_tpl->NewInstance(context).ToLocalChecked(); - - // Prepare constructor template - Local tpl = FunctionTemplate::New(isolate, New, addon_data); - tpl->SetClassName(String::NewFromUtf8(isolate, "MyObject").ToLocalChecked()); - tpl->InstanceTemplate()->SetInternalFieldCount(1); - - // Prototype - NODE_SET_PROTOTYPE_METHOD(tpl, "plusOne", PlusOne); - - Local constructor = tpl->GetFunction(context).ToLocalChecked(); - addon_data->SetInternalField(0, constructor); - exports->Set(context, String::NewFromUtf8( - isolate, "MyObject").ToLocalChecked(), - constructor).FromJust(); -} - -void MyObject::New(const FunctionCallbackInfo& args) { - Isolate* isolate = args.GetIsolate(); - Local context = isolate->GetCurrentContext(); - - if (args.IsConstructCall()) { - // Invoked as constructor: `new MyObject(...)` - double value = args[0]->IsUndefined() ? - 0 : args[0]->NumberValue(context).FromMaybe(0); - MyObject* obj = new MyObject(value); - obj->Wrap(args.This()); - args.GetReturnValue().Set(args.This()); - } else { - // Invoked as plain function `MyObject(...)`, turn into construct call. - const int argc = 1; - Local argv[argc] = { args[0] }; - Local cons = - args.Data().As()->GetInternalField(0) - .As().As(); - Local result = - cons->NewInstance(context, argc, argv).ToLocalChecked(); - args.GetReturnValue().Set(result); - } -} - -void MyObject::PlusOne(const FunctionCallbackInfo& args) { - Isolate* isolate = args.GetIsolate(); - - MyObject* obj = ObjectWrap::Unwrap(args.This()); - obj->value_ += 1; - - args.GetReturnValue().Set(Number::New(isolate, obj->value_)); -} - -} // namespace demo -``` - -To build this example, the `myobject.cc` file must be added to the -`binding.gyp`: - -```json -{ - "targets": [ - { - "target_name": "addon", - "sources": [ - "addon.cc", - "myobject.cc" - ] - } - ] -} -``` - -Test it with: - -```js -// test.js -const addon = require('./build/Release/addon'); - -const obj = new addon.MyObject(10); -console.log(obj.plusOne()); -// Prints: 11 -console.log(obj.plusOne()); -// Prints: 12 -console.log(obj.plusOne()); -// Prints: 13 -``` - -The destructor for a wrapper object will run when the object is -garbage-collected. For destructor testing, there are command-line flags that -can be used to make it possible to force garbage collection. These flags are -provided by the underlying V8 JavaScript engine. They are subject to change -or removal at any time. They are not documented by Node.js or V8, and they -should never be used outside of testing. - -During shutdown of the process or worker threads destructors are not called -by the JS engine. Therefore it's the responsibility of the user to track -these objects and ensure proper destruction to avoid resource leaks. - -### Factory of wrapped objects - -Alternatively, it is possible to use a factory pattern to avoid explicitly -creating object instances using the JavaScript `new` operator: - -```js -const obj = addon.createObject(); -// instead of: -// const obj = new addon.Object(); -``` - -First, the `createObject()` method is implemented in `addon.cc`: - -```cpp -// addon.cc -#include -#include "myobject.h" - -namespace demo { - -using v8::FunctionCallbackInfo; -using v8::Isolate; -using v8::Local; -using v8::Object; -using v8::String; -using v8::Value; - -void CreateObject(const FunctionCallbackInfo& args) { - MyObject::NewInstance(args); -} - -void InitAll(Local exports, Local module) { - MyObject::Init(exports->GetIsolate()); - - NODE_SET_METHOD(module, "exports", CreateObject); -} - -NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll) - -} // namespace demo -``` - -In `myobject.h`, the static method `NewInstance()` is added to handle -instantiating the object. This method takes the place of using `new` in -JavaScript: - -```cpp -// myobject.h -#ifndef MYOBJECT_H -#define MYOBJECT_H - -#include -#include - -namespace demo { - -class MyObject : public node::ObjectWrap { - public: - static void Init(v8::Isolate* isolate); - static void NewInstance(const v8::FunctionCallbackInfo& args); - - private: - explicit MyObject(double value = 0); - ~MyObject(); - - static void New(const v8::FunctionCallbackInfo& args); - static void PlusOne(const v8::FunctionCallbackInfo& args); - static v8::Global constructor; - double value_; -}; - -} // namespace demo - -#endif -``` - -The implementation in `myobject.cc` is similar to the previous example: - -```cpp -// myobject.cc -#include -#include "myobject.h" - -namespace demo { - -using node::AddEnvironmentCleanupHook; -using v8::Context; -using v8::Function; -using v8::FunctionCallbackInfo; -using v8::FunctionTemplate; -using v8::Global; -using v8::Isolate; -using v8::Local; -using v8::Number; -using v8::Object; -using v8::String; -using v8::Value; - -// Warning! This is not thread-safe, this addon cannot be used for worker -// threads. -Global MyObject::constructor; - -MyObject::MyObject(double value) : value_(value) { -} - -MyObject::~MyObject() { -} - -void MyObject::Init(Isolate* isolate) { - // Prepare constructor template - Local tpl = FunctionTemplate::New(isolate, New); - tpl->SetClassName(String::NewFromUtf8(isolate, "MyObject").ToLocalChecked()); - tpl->InstanceTemplate()->SetInternalFieldCount(1); - - // Prototype - NODE_SET_PROTOTYPE_METHOD(tpl, "plusOne", PlusOne); - - Local context = isolate->GetCurrentContext(); - constructor.Reset(isolate, tpl->GetFunction(context).ToLocalChecked()); - - AddEnvironmentCleanupHook(isolate, [](void*) { - constructor.Reset(); - }, nullptr); -} - -void MyObject::New(const FunctionCallbackInfo& args) { - Isolate* isolate = args.GetIsolate(); - Local context = isolate->GetCurrentContext(); - - if (args.IsConstructCall()) { - // Invoked as constructor: `new MyObject(...)` - double value = args[0]->IsUndefined() ? - 0 : args[0]->NumberValue(context).FromMaybe(0); - MyObject* obj = new MyObject(value); - obj->Wrap(args.This()); - args.GetReturnValue().Set(args.This()); - } else { - // Invoked as plain function `MyObject(...)`, turn into construct call. - const int argc = 1; - Local argv[argc] = { args[0] }; - Local cons = Local::New(isolate, constructor); - Local instance = - cons->NewInstance(context, argc, argv).ToLocalChecked(); - args.GetReturnValue().Set(instance); - } -} - -void MyObject::NewInstance(const FunctionCallbackInfo& args) { - Isolate* isolate = args.GetIsolate(); - - const unsigned argc = 1; - Local argv[argc] = { args[0] }; - Local cons = Local::New(isolate, constructor); - Local context = isolate->GetCurrentContext(); - Local instance = - cons->NewInstance(context, argc, argv).ToLocalChecked(); - - args.GetReturnValue().Set(instance); -} - -void MyObject::PlusOne(const FunctionCallbackInfo& args) { - Isolate* isolate = args.GetIsolate(); - - MyObject* obj = ObjectWrap::Unwrap(args.This()); - obj->value_ += 1; - - args.GetReturnValue().Set(Number::New(isolate, obj->value_)); -} - -} // namespace demo -``` - -Once again, to build this example, the `myobject.cc` file must be added to the -`binding.gyp`: - -```json -{ - "targets": [ - { - "target_name": "addon", - "sources": [ - "addon.cc", - "myobject.cc" - ] - } - ] -} -``` - -Test it with: - -```js -// test.js -const createObject = require('./build/Release/addon'); - -const obj = createObject(10); -console.log(obj.plusOne()); -// Prints: 11 -console.log(obj.plusOne()); -// Prints: 12 -console.log(obj.plusOne()); -// Prints: 13 - -const obj2 = createObject(20); -console.log(obj2.plusOne()); -// Prints: 21 -console.log(obj2.plusOne()); -// Prints: 22 -console.log(obj2.plusOne()); -// Prints: 23 -``` - -### Passing wrapped objects around - -In addition to wrapping and returning C++ objects, it is possible to pass -wrapped objects around by unwrapping them with the Node.js helper function -`node::ObjectWrap::Unwrap`. The following examples shows a function `add()` -that can take two `MyObject` objects as input arguments: - -```cpp -// addon.cc -#include -#include -#include "myobject.h" - -namespace demo { - -using v8::Context; -using v8::FunctionCallbackInfo; -using v8::Isolate; -using v8::Local; -using v8::Number; -using v8::Object; -using v8::String; -using v8::Value; - -void CreateObject(const FunctionCallbackInfo& args) { - MyObject::NewInstance(args); -} - -void Add(const FunctionCallbackInfo& args) { - Isolate* isolate = args.GetIsolate(); - Local context = isolate->GetCurrentContext(); - - MyObject* obj1 = node::ObjectWrap::Unwrap( - args[0]->ToObject(context).ToLocalChecked()); - MyObject* obj2 = node::ObjectWrap::Unwrap( - args[1]->ToObject(context).ToLocalChecked()); - - double sum = obj1->value() + obj2->value(); - args.GetReturnValue().Set(Number::New(isolate, sum)); -} - -void InitAll(Local exports) { - MyObject::Init(exports->GetIsolate()); - - NODE_SET_METHOD(exports, "createObject", CreateObject); - NODE_SET_METHOD(exports, "add", Add); -} - -NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll) - -} // namespace demo -``` - -In `myobject.h`, a new public method is added to allow access to private values -after unwrapping the object. - -```cpp -// myobject.h -#ifndef MYOBJECT_H -#define MYOBJECT_H - -#include -#include - -namespace demo { - -class MyObject : public node::ObjectWrap { - public: - static void Init(v8::Isolate* isolate); - static void NewInstance(const v8::FunctionCallbackInfo& args); - inline double value() const { return value_; } - - private: - explicit MyObject(double value = 0); - ~MyObject(); - - static void New(const v8::FunctionCallbackInfo& args); - static v8::Global constructor; - double value_; -}; - -} // namespace demo - -#endif -``` - -The implementation of `myobject.cc` is similar to before: - -```cpp -// myobject.cc -#include -#include "myobject.h" - -namespace demo { - -using node::AddEnvironmentCleanupHook; -using v8::Context; -using v8::Function; -using v8::FunctionCallbackInfo; -using v8::FunctionTemplate; -using v8::Global; -using v8::Isolate; -using v8::Local; -using v8::Object; -using v8::String; -using v8::Value; - -// Warning! This is not thread-safe, this addon cannot be used for worker -// threads. -Global MyObject::constructor; - -MyObject::MyObject(double value) : value_(value) { -} - -MyObject::~MyObject() { -} - -void MyObject::Init(Isolate* isolate) { - // Prepare constructor template - Local tpl = FunctionTemplate::New(isolate, New); - tpl->SetClassName(String::NewFromUtf8(isolate, "MyObject").ToLocalChecked()); - tpl->InstanceTemplate()->SetInternalFieldCount(1); - - Local context = isolate->GetCurrentContext(); - constructor.Reset(isolate, tpl->GetFunction(context).ToLocalChecked()); - - AddEnvironmentCleanupHook(isolate, [](void*) { - constructor.Reset(); - }, nullptr); -} - -void MyObject::New(const FunctionCallbackInfo& args) { - Isolate* isolate = args.GetIsolate(); - Local context = isolate->GetCurrentContext(); - - if (args.IsConstructCall()) { - // Invoked as constructor: `new MyObject(...)` - double value = args[0]->IsUndefined() ? - 0 : args[0]->NumberValue(context).FromMaybe(0); - MyObject* obj = new MyObject(value); - obj->Wrap(args.This()); - args.GetReturnValue().Set(args.This()); - } else { - // Invoked as plain function `MyObject(...)`, turn into construct call. - const int argc = 1; - Local argv[argc] = { args[0] }; - Local cons = Local::New(isolate, constructor); - Local instance = - cons->NewInstance(context, argc, argv).ToLocalChecked(); - args.GetReturnValue().Set(instance); - } -} - -void MyObject::NewInstance(const FunctionCallbackInfo& args) { - Isolate* isolate = args.GetIsolate(); - - const unsigned argc = 1; - Local argv[argc] = { args[0] }; - Local cons = Local::New(isolate, constructor); - Local context = isolate->GetCurrentContext(); - Local instance = - cons->NewInstance(context, argc, argv).ToLocalChecked(); - - args.GetReturnValue().Set(instance); -} - -} // namespace demo -``` - -Test it with: - -```js -// test.js -const addon = require('./build/Release/addon'); - -const obj1 = addon.createObject(10); -const obj2 = addon.createObject(20); -const result = addon.add(obj1, obj2); - -console.log(result); -// Prints: 30 -``` - -[Electron]: https://electronjs.org/ -[Embedder's Guide]: https://v8.dev/docs/embed -[Linking to libraries included with Node.js]: #linking-to-libraries-included-with-nodejs -[Native Abstractions for Node.js]: https://github.com/nodejs/nan -[V8]: https://v8.dev/ -[`Worker`]: worker_threads.md#class-worker -[bindings]: https://github.com/TooTallNate/node-bindings -[download]: https://github.com/nodejs/node-addon-examples -[examples]: https://github.com/nodejs/nan/tree/HEAD/examples/ -[implementation considerations]: n-api.md#implications-of-abi-stability -[installation instructions]: https://github.com/nodejs/node-gyp#installation -[libuv]: https://github.com/libuv/libuv -[node-gyp]: https://github.com/nodejs/node-gyp -[require]: modules.md#requireid -[v8-docs]: https://v8docs.nodesource.com/ diff --git a/fixtures/assert.md b/fixtures/assert.md deleted file mode 100644 index 70f4ac6..0000000 --- a/fixtures/assert.md +++ /dev/null @@ -1,2587 +0,0 @@ -# Assert - - - -> Stability: 2 - Stable - - - -The `node:assert` module provides a set of assertion functions for verifying -invariants. - -## Strict assertion mode - - - -In strict assertion mode, non-strict methods behave like their corresponding -strict methods. For example, [`assert.deepEqual()`][] will behave like -[`assert.deepStrictEqual()`][]. - -In strict assertion mode, error messages for objects display a diff. In legacy -assertion mode, error messages for objects display the objects, often truncated. - -To use strict assertion mode: - -```mjs -import { strict as assert } from 'node:assert'; -``` - -```cjs -const assert = require('node:assert').strict; -``` - -```mjs -import assert from 'node:assert/strict'; -``` - -```cjs -const assert = require('node:assert/strict'); -``` - -Example error diff: - -```mjs -import { strict as assert } from 'node:assert'; - -assert.deepEqual([[[1, 2, 3]], 4, 5], [[[1, 2, '3']], 4, 5]); -// AssertionError: Expected inputs to be strictly deep-equal: -// + actual - expected ... Lines skipped -// -// [ -// [ -// ... -// 2, -// + 3 -// - '3' -// ], -// ... -// 5 -// ] -``` - -```cjs -const assert = require('node:assert/strict'); - -assert.deepEqual([[[1, 2, 3]], 4, 5], [[[1, 2, '3']], 4, 5]); -// AssertionError: Expected inputs to be strictly deep-equal: -// + actual - expected ... Lines skipped -// -// [ -// [ -// ... -// 2, -// + 3 -// - '3' -// ], -// ... -// 5 -// ] -``` - -To deactivate the colors, use the `NO_COLOR` or `NODE_DISABLE_COLORS` -environment variables. This will also deactivate the colors in the REPL. For -more on color support in terminal environments, read the tty -[`getColorDepth()`][] documentation. - -## Legacy assertion mode - -Legacy assertion mode uses the [`==` operator][] in: - -* [`assert.deepEqual()`][] -* [`assert.equal()`][] -* [`assert.notDeepEqual()`][] -* [`assert.notEqual()`][] - -To use legacy assertion mode: - -```mjs -import assert from 'node:assert'; -``` - -```cjs -const assert = require('node:assert'); -``` - -Legacy assertion mode may have surprising results, especially when using -[`assert.deepEqual()`][]: - -```cjs -// WARNING: This does not throw an AssertionError in legacy assertion mode! -assert.deepEqual(/a/gi, new Date()); -``` - -## Class: assert.AssertionError - -* Extends: {errors.Error} - -Indicates the failure of an assertion. All errors thrown by the `node:assert` -module will be instances of the `AssertionError` class. - -### `new assert.AssertionError(options)` - - - -* `options` {Object} - * `message` {string} If provided, the error message is set to this value. - * `actual` {any} The `actual` property on the error instance. - * `expected` {any} The `expected` property on the error instance. - * `operator` {string} The `operator` property on the error instance. - * `stackStartFn` {Function} If provided, the generated stack trace omits - frames before this function. - -A subclass of `Error` that indicates the failure of an assertion. - -All instances contain the built-in `Error` properties (`message` and `name`) -and: - -* `actual` {any} Set to the `actual` argument for methods such as - [`assert.strictEqual()`][]. -* `expected` {any} Set to the `expected` value for methods such as - [`assert.strictEqual()`][]. -* `generatedMessage` {boolean} Indicates if the message was auto-generated - (`true`) or not. -* `code` {string} Value is always `ERR_ASSERTION` to show that the error is an - assertion error. -* `operator` {string} Set to the passed in operator value. - -```mjs -import assert from 'node:assert'; - -// Generate an AssertionError to compare the error message later: -const { message } = new assert.AssertionError({ - actual: 1, - expected: 2, - operator: 'strictEqual', -}); - -// Verify error output: -try { - assert.strictEqual(1, 2); -} catch (err) { - assert(err instanceof assert.AssertionError); - assert.strictEqual(err.message, message); - assert.strictEqual(err.name, 'AssertionError'); - assert.strictEqual(err.actual, 1); - assert.strictEqual(err.expected, 2); - assert.strictEqual(err.code, 'ERR_ASSERTION'); - assert.strictEqual(err.operator, 'strictEqual'); - assert.strictEqual(err.generatedMessage, true); -} -``` - -```cjs -const assert = require('node:assert'); - -// Generate an AssertionError to compare the error message later: -const { message } = new assert.AssertionError({ - actual: 1, - expected: 2, - operator: 'strictEqual', -}); - -// Verify error output: -try { - assert.strictEqual(1, 2); -} catch (err) { - assert(err instanceof assert.AssertionError); - assert.strictEqual(err.message, message); - assert.strictEqual(err.name, 'AssertionError'); - assert.strictEqual(err.actual, 1); - assert.strictEqual(err.expected, 2); - assert.strictEqual(err.code, 'ERR_ASSERTION'); - assert.strictEqual(err.operator, 'strictEqual'); - assert.strictEqual(err.generatedMessage, true); -} -``` - -## Class: `assert.CallTracker` - - - -> Stability: 0 - Deprecated - -This feature is deprecated and will be removed in a future version. -Please consider using alternatives such as the -[`mock`][] helper function. - -### `new assert.CallTracker()` - - - -Creates a new [`CallTracker`][] object which can be used to track if functions -were called a specific number of times. The `tracker.verify()` must be called -for the verification to take place. The usual pattern would be to call it in a -[`process.on('exit')`][] handler. - -```mjs -import assert from 'node:assert'; -import process from 'node:process'; - -const tracker = new assert.CallTracker(); - -function func() {} - -// callsfunc() must be called exactly 1 time before tracker.verify(). -const callsfunc = tracker.calls(func, 1); - -callsfunc(); - -// Calls tracker.verify() and verifies if all tracker.calls() functions have -// been called exact times. -process.on('exit', () => { - tracker.verify(); -}); -``` - -```cjs -const assert = require('node:assert'); -const process = require('node:process'); - -const tracker = new assert.CallTracker(); - -function func() {} - -// callsfunc() must be called exactly 1 time before tracker.verify(). -const callsfunc = tracker.calls(func, 1); - -callsfunc(); - -// Calls tracker.verify() and verifies if all tracker.calls() functions have -// been called exact times. -process.on('exit', () => { - tracker.verify(); -}); -``` - -### `tracker.calls([fn][, exact])` - - - -* `fn` {Function} **Default:** A no-op function. -* `exact` {number} **Default:** `1`. -* Returns: {Function} A function that wraps `fn`. - -The wrapper function is expected to be called exactly `exact` times. If the -function has not been called exactly `exact` times when -[`tracker.verify()`][] is called, then [`tracker.verify()`][] will throw an -error. - -```mjs -import assert from 'node:assert'; - -// Creates call tracker. -const tracker = new assert.CallTracker(); - -function func() {} - -// Returns a function that wraps func() that must be called exact times -// before tracker.verify(). -const callsfunc = tracker.calls(func); -``` - -```cjs -const assert = require('node:assert'); - -// Creates call tracker. -const tracker = new assert.CallTracker(); - -function func() {} - -// Returns a function that wraps func() that must be called exact times -// before tracker.verify(). -const callsfunc = tracker.calls(func); -``` - -### `tracker.getCalls(fn)` - - - -* `fn` {Function} - -* Returns: {Array} An array with all the calls to a tracked function. - -* Object {Object} - * `thisArg` {Object} - * `arguments` {Array} the arguments passed to the tracked function - -```mjs -import assert from 'node:assert'; - -const tracker = new assert.CallTracker(); - -function func() {} -const callsfunc = tracker.calls(func); -callsfunc(1, 2, 3); - -assert.deepStrictEqual(tracker.getCalls(callsfunc), - [{ thisArg: undefined, arguments: [1, 2, 3] }]); -``` - -```cjs -const assert = require('node:assert'); - -// Creates call tracker. -const tracker = new assert.CallTracker(); - -function func() {} -const callsfunc = tracker.calls(func); -callsfunc(1, 2, 3); - -assert.deepStrictEqual(tracker.getCalls(callsfunc), - [{ thisArg: undefined, arguments: [1, 2, 3] }]); -``` - -### `tracker.report()` - - - -* Returns: {Array} An array of objects containing information about the wrapper - functions returned by [`tracker.calls()`][]. -* Object {Object} - * `message` {string} - * `actual` {number} The actual number of times the function was called. - * `expected` {number} The number of times the function was expected to be - called. - * `operator` {string} The name of the function that is wrapped. - * `stack` {Object} A stack trace of the function. - -The arrays contains information about the expected and actual number of calls of -the functions that have not been called the expected number of times. - -```mjs -import assert from 'node:assert'; - -// Creates call tracker. -const tracker = new assert.CallTracker(); - -function func() {} - -// Returns a function that wraps func() that must be called exact times -// before tracker.verify(). -const callsfunc = tracker.calls(func, 2); - -// Returns an array containing information on callsfunc() -console.log(tracker.report()); -// [ -// { -// message: 'Expected the func function to be executed 2 time(s) but was -// executed 0 time(s).', -// actual: 0, -// expected: 2, -// operator: 'func', -// stack: stack trace -// } -// ] -``` - -```cjs -const assert = require('node:assert'); - -// Creates call tracker. -const tracker = new assert.CallTracker(); - -function func() {} - -// Returns a function that wraps func() that must be called exact times -// before tracker.verify(). -const callsfunc = tracker.calls(func, 2); - -// Returns an array containing information on callsfunc() -console.log(tracker.report()); -// [ -// { -// message: 'Expected the func function to be executed 2 time(s) but was -// executed 0 time(s).', -// actual: 0, -// expected: 2, -// operator: 'func', -// stack: stack trace -// } -// ] -``` - -### `tracker.reset([fn])` - - - -* `fn` {Function} a tracked function to reset. - -Reset calls of the call tracker. -If a tracked function is passed as an argument, the calls will be reset for it. -If no arguments are passed, all tracked functions will be reset. - -```mjs -import assert from 'node:assert'; - -const tracker = new assert.CallTracker(); - -function func() {} -const callsfunc = tracker.calls(func); - -callsfunc(); -// Tracker was called once -assert.strictEqual(tracker.getCalls(callsfunc).length, 1); - -tracker.reset(callsfunc); -assert.strictEqual(tracker.getCalls(callsfunc).length, 0); -``` - -```cjs -const assert = require('node:assert'); - -const tracker = new assert.CallTracker(); - -function func() {} -const callsfunc = tracker.calls(func); - -callsfunc(); -// Tracker was called once -assert.strictEqual(tracker.getCalls(callsfunc).length, 1); - -tracker.reset(callsfunc); -assert.strictEqual(tracker.getCalls(callsfunc).length, 0); -``` - -### `tracker.verify()` - - - -Iterates through the list of functions passed to -[`tracker.calls()`][] and will throw an error for functions that -have not been called the expected number of times. - -```mjs -import assert from 'node:assert'; - -// Creates call tracker. -const tracker = new assert.CallTracker(); - -function func() {} - -// Returns a function that wraps func() that must be called exact times -// before tracker.verify(). -const callsfunc = tracker.calls(func, 2); - -callsfunc(); - -// Will throw an error since callsfunc() was only called once. -tracker.verify(); -``` - -```cjs -const assert = require('node:assert'); - -// Creates call tracker. -const tracker = new assert.CallTracker(); - -function func() {} - -// Returns a function that wraps func() that must be called exact times -// before tracker.verify(). -const callsfunc = tracker.calls(func, 2); - -callsfunc(); - -// Will throw an error since callsfunc() was only called once. -tracker.verify(); -``` - -## `assert(value[, message])` - - - -* `value` {any} The input that is checked for being truthy. -* `message` {string|Error} - -An alias of [`assert.ok()`][]. - -## `assert.deepEqual(actual, expected[, message])` - - - -* `actual` {any} -* `expected` {any} -* `message` {string|Error} - -**Strict assertion mode** - -An alias of [`assert.deepStrictEqual()`][]. - -**Legacy assertion mode** - -> Stability: 3 - Legacy: Use [`assert.deepStrictEqual()`][] instead. - -Tests for deep equality between the `actual` and `expected` parameters. Consider -using [`assert.deepStrictEqual()`][] instead. [`assert.deepEqual()`][] can have -surprising results. - -_Deep equality_ means that the enumerable "own" properties of child objects -are also recursively evaluated by the following rules. - -### Comparison details - -* Primitive values are compared with the [`==` operator][], - with the exception of `NaN`. It is treated as being identical in case - both sides are `NaN`. -* [Type tags][Object.prototype.toString()] of objects should be the same. -* Only [enumerable "own" properties][] are considered. -* [`Error`][] names, messages, causes, and errors are always compared, - even if these are not enumerable properties. -* [Object wrappers][] are compared both as objects and unwrapped values. -* `Object` properties are compared unordered. -* [`Map`][] keys and [`Set`][] items are compared unordered. -* Recursion stops when both sides differ or both sides encounter a circular - reference. -* Implementation does not test the [`[[Prototype]]`][prototype-spec] of - objects. -* [`Symbol`][] properties are not compared. -* [`WeakMap`][] and [`WeakSet`][] comparison does not rely on their values - but only on their instances. -* [`RegExp`][] lastIndex, flags, and source are always compared, even if these - are not enumerable properties. - -The following example does not throw an [`AssertionError`][] because the -primitives are compared using the [`==` operator][]. - -```mjs -import assert from 'node:assert'; -// WARNING: This does not throw an AssertionError! - -assert.deepEqual('+00000000', false); -``` - -```cjs -const assert = require('node:assert'); -// WARNING: This does not throw an AssertionError! - -assert.deepEqual('+00000000', false); -``` - -"Deep" equality means that the enumerable "own" properties of child objects -are evaluated also: - -```mjs -import assert from 'node:assert'; - -const obj1 = { - a: { - b: 1, - }, -}; -const obj2 = { - a: { - b: 2, - }, -}; -const obj3 = { - a: { - b: 1, - }, -}; -const obj4 = { __proto__: obj1 }; - -assert.deepEqual(obj1, obj1); -// OK - -// Values of b are different: -assert.deepEqual(obj1, obj2); -// AssertionError: { a: { b: 1 } } deepEqual { a: { b: 2 } } - -assert.deepEqual(obj1, obj3); -// OK - -// Prototypes are ignored: -assert.deepEqual(obj1, obj4); -// AssertionError: { a: { b: 1 } } deepEqual {} -``` - -```cjs -const assert = require('node:assert'); - -const obj1 = { - a: { - b: 1, - }, -}; -const obj2 = { - a: { - b: 2, - }, -}; -const obj3 = { - a: { - b: 1, - }, -}; -const obj4 = { __proto__: obj1 }; - -assert.deepEqual(obj1, obj1); -// OK - -// Values of b are different: -assert.deepEqual(obj1, obj2); -// AssertionError: { a: { b: 1 } } deepEqual { a: { b: 2 } } - -assert.deepEqual(obj1, obj3); -// OK - -// Prototypes are ignored: -assert.deepEqual(obj1, obj4); -// AssertionError: { a: { b: 1 } } deepEqual {} -``` - -If the values are not equal, an [`AssertionError`][] is thrown with a `message` -property set equal to the value of the `message` parameter. If the `message` -parameter is undefined, a default error message is assigned. If the `message` -parameter is an instance of an [`Error`][] then it will be thrown instead of the -[`AssertionError`][]. - -## `assert.deepStrictEqual(actual, expected[, message])` - - - -* `actual` {any} -* `expected` {any} -* `message` {string|Error} - -Tests for deep equality between the `actual` and `expected` parameters. -"Deep" equality means that the enumerable "own" properties of child objects -are recursively evaluated also by the following rules. - -### Comparison details - -* Primitive values are compared using [`Object.is()`][]. -* [Type tags][Object.prototype.toString()] of objects should be the same. -* [`[[Prototype]]`][prototype-spec] of objects are compared using - the [`===` operator][]. -* Only [enumerable "own" properties][] are considered. -* [`Error`][] names, messages, causes, and errors are always compared, - even if these are not enumerable properties. - `errors` is also compared. -* Enumerable own [`Symbol`][] properties are compared as well. -* [Object wrappers][] are compared both as objects and unwrapped values. -* `Object` properties are compared unordered. -* [`Map`][] keys and [`Set`][] items are compared unordered. -* Recursion stops when both sides differ or both sides encounter a circular - reference. -* [`WeakMap`][] and [`WeakSet`][] comparison does not rely on their values. See - below for further details. -* [`RegExp`][] lastIndex, flags, and source are always compared, even if these - are not enumerable properties. - -```mjs -import assert from 'node:assert/strict'; - -// This fails because 1 !== '1'. -assert.deepStrictEqual({ a: 1 }, { a: '1' }); -// AssertionError: Expected inputs to be strictly deep-equal: -// + actual - expected -// -// { -// + a: 1 -// - a: '1' -// } - -// The following objects don't have own properties -const date = new Date(); -const object = {}; -const fakeDate = {}; -Object.setPrototypeOf(fakeDate, Date.prototype); - -// Different [[Prototype]]: -assert.deepStrictEqual(object, fakeDate); -// AssertionError: Expected inputs to be strictly deep-equal: -// + actual - expected -// -// + {} -// - Date {} - -// Different type tags: -assert.deepStrictEqual(date, fakeDate); -// AssertionError: Expected inputs to be strictly deep-equal: -// + actual - expected -// -// + 2018-04-26T00:49:08.604Z -// - Date {} - -assert.deepStrictEqual(NaN, NaN); -// OK because Object.is(NaN, NaN) is true. - -// Different unwrapped numbers: -assert.deepStrictEqual(new Number(1), new Number(2)); -// AssertionError: Expected inputs to be strictly deep-equal: -// + actual - expected -// -// + [Number: 1] -// - [Number: 2] - -assert.deepStrictEqual(new String('foo'), Object('foo')); -// OK because the object and the string are identical when unwrapped. - -assert.deepStrictEqual(-0, -0); -// OK - -// Different zeros: -assert.deepStrictEqual(0, -0); -// AssertionError: Expected inputs to be strictly deep-equal: -// + actual - expected -// -// + 0 -// - -0 - -const symbol1 = Symbol(); -const symbol2 = Symbol(); -assert.deepStrictEqual({ [symbol1]: 1 }, { [symbol1]: 1 }); -// OK, because it is the same symbol on both objects. - -assert.deepStrictEqual({ [symbol1]: 1 }, { [symbol2]: 1 }); -// AssertionError [ERR_ASSERTION]: Inputs identical but not reference equal: -// -// { -// [Symbol()]: 1 -// } - -const weakMap1 = new WeakMap(); -const weakMap2 = new WeakMap([[{}, {}]]); -const weakMap3 = new WeakMap(); -weakMap3.unequal = true; - -assert.deepStrictEqual(weakMap1, weakMap2); -// OK, because it is impossible to compare the entries - -// Fails because weakMap3 has a property that weakMap1 does not contain: -assert.deepStrictEqual(weakMap1, weakMap3); -// AssertionError: Expected inputs to be strictly deep-equal: -// + actual - expected -// -// WeakMap { -// + [items unknown] -// - [items unknown], -// - unequal: true -// } -``` - -```cjs -const assert = require('node:assert/strict'); - -// This fails because 1 !== '1'. -assert.deepStrictEqual({ a: 1 }, { a: '1' }); -// AssertionError: Expected inputs to be strictly deep-equal: -// + actual - expected -// -// { -// + a: 1 -// - a: '1' -// } - -// The following objects don't have own properties -const date = new Date(); -const object = {}; -const fakeDate = {}; -Object.setPrototypeOf(fakeDate, Date.prototype); - -// Different [[Prototype]]: -assert.deepStrictEqual(object, fakeDate); -// AssertionError: Expected inputs to be strictly deep-equal: -// + actual - expected -// -// + {} -// - Date {} - -// Different type tags: -assert.deepStrictEqual(date, fakeDate); -// AssertionError: Expected inputs to be strictly deep-equal: -// + actual - expected -// -// + 2018-04-26T00:49:08.604Z -// - Date {} - -assert.deepStrictEqual(NaN, NaN); -// OK because Object.is(NaN, NaN) is true. - -// Different unwrapped numbers: -assert.deepStrictEqual(new Number(1), new Number(2)); -// AssertionError: Expected inputs to be strictly deep-equal: -// + actual - expected -// -// + [Number: 1] -// - [Number: 2] - -assert.deepStrictEqual(new String('foo'), Object('foo')); -// OK because the object and the string are identical when unwrapped. - -assert.deepStrictEqual(-0, -0); -// OK - -// Different zeros: -assert.deepStrictEqual(0, -0); -// AssertionError: Expected inputs to be strictly deep-equal: -// + actual - expected -// -// + 0 -// - -0 - -const symbol1 = Symbol(); -const symbol2 = Symbol(); -assert.deepStrictEqual({ [symbol1]: 1 }, { [symbol1]: 1 }); -// OK, because it is the same symbol on both objects. - -assert.deepStrictEqual({ [symbol1]: 1 }, { [symbol2]: 1 }); -// AssertionError [ERR_ASSERTION]: Inputs identical but not reference equal: -// -// { -// [Symbol()]: 1 -// } - -const weakMap1 = new WeakMap(); -const weakMap2 = new WeakMap([[{}, {}]]); -const weakMap3 = new WeakMap(); -weakMap3.unequal = true; - -assert.deepStrictEqual(weakMap1, weakMap2); -// OK, because it is impossible to compare the entries - -// Fails because weakMap3 has a property that weakMap1 does not contain: -assert.deepStrictEqual(weakMap1, weakMap3); -// AssertionError: Expected inputs to be strictly deep-equal: -// + actual - expected -// -// WeakMap { -// + [items unknown] -// - [items unknown], -// - unequal: true -// } -``` - -If the values are not equal, an [`AssertionError`][] is thrown with a `message` -property set equal to the value of the `message` parameter. If the `message` -parameter is undefined, a default error message is assigned. If the `message` -parameter is an instance of an [`Error`][] then it will be thrown instead of the -`AssertionError`. - -## `assert.doesNotMatch(string, regexp[, message])` - - - -* `string` {string} -* `regexp` {RegExp} -* `message` {string|Error} - -Expects the `string` input not to match the regular expression. - -```mjs -import assert from 'node:assert/strict'; - -assert.doesNotMatch('I will fail', /fail/); -// AssertionError [ERR_ASSERTION]: The input was expected to not match the ... - -assert.doesNotMatch(123, /pass/); -// AssertionError [ERR_ASSERTION]: The "string" argument must be of type string. - -assert.doesNotMatch('I will pass', /different/); -// OK -``` - -```cjs -const assert = require('node:assert/strict'); - -assert.doesNotMatch('I will fail', /fail/); -// AssertionError [ERR_ASSERTION]: The input was expected to not match the ... - -assert.doesNotMatch(123, /pass/); -// AssertionError [ERR_ASSERTION]: The "string" argument must be of type string. - -assert.doesNotMatch('I will pass', /different/); -// OK -``` - -If the values do match, or if the `string` argument is of another type than -`string`, an [`AssertionError`][] is thrown with a `message` property set equal -to the value of the `message` parameter. If the `message` parameter is -undefined, a default error message is assigned. If the `message` parameter is an -instance of an [`Error`][] then it will be thrown instead of the -[`AssertionError`][]. - -## `assert.doesNotReject(asyncFn[, error][, message])` - - - -* `asyncFn` {Function|Promise} -* `error` {RegExp|Function} -* `message` {string} - -Awaits the `asyncFn` promise or, if `asyncFn` is a function, immediately -calls the function and awaits the returned promise to complete. It will then -check that the promise is not rejected. - -If `asyncFn` is a function and it throws an error synchronously, -`assert.doesNotReject()` will return a rejected `Promise` with that error. If -the function does not return a promise, `assert.doesNotReject()` will return a -rejected `Promise` with an [`ERR_INVALID_RETURN_VALUE`][] error. In both cases -the error handler is skipped. - -Using `assert.doesNotReject()` is actually not useful because there is little -benefit in catching a rejection and then rejecting it again. Instead, consider -adding a comment next to the specific code path that should not reject and keep -error messages as expressive as possible. - -If specified, `error` can be a [`Class`][], [`RegExp`][], or a validation -function. See [`assert.throws()`][] for more details. - -Besides the async nature to await the completion behaves identically to -[`assert.doesNotThrow()`][]. - -```mjs -import assert from 'node:assert/strict'; - -await assert.doesNotReject( - async () => { - throw new TypeError('Wrong value'); - }, - SyntaxError, -); -``` - -```cjs -const assert = require('node:assert/strict'); - -(async () => { - await assert.doesNotReject( - async () => { - throw new TypeError('Wrong value'); - }, - SyntaxError, - ); -})(); -``` - -```mjs -import assert from 'node:assert/strict'; - -assert.doesNotReject(Promise.reject(new TypeError('Wrong value'))) - .then(() => { - // ... - }); -``` - -```cjs -const assert = require('node:assert/strict'); - -assert.doesNotReject(Promise.reject(new TypeError('Wrong value'))) - .then(() => { - // ... - }); -``` - -## `assert.doesNotThrow(fn[, error][, message])` - - - -* `fn` {Function} -* `error` {RegExp|Function} -* `message` {string} - -Asserts that the function `fn` does not throw an error. - -Using `assert.doesNotThrow()` is actually not useful because there -is no benefit in catching an error and then rethrowing it. Instead, consider -adding a comment next to the specific code path that should not throw and keep -error messages as expressive as possible. - -When `assert.doesNotThrow()` is called, it will immediately call the `fn` -function. - -If an error is thrown and it is the same type as that specified by the `error` -parameter, then an [`AssertionError`][] is thrown. If the error is of a -different type, or if the `error` parameter is undefined, the error is -propagated back to the caller. - -If specified, `error` can be a [`Class`][], [`RegExp`][], or a validation -function. See [`assert.throws()`][] for more details. - -The following, for instance, will throw the [`TypeError`][] because there is no -matching error type in the assertion: - -```mjs -import assert from 'node:assert/strict'; - -assert.doesNotThrow( - () => { - throw new TypeError('Wrong value'); - }, - SyntaxError, -); -``` - -```cjs -const assert = require('node:assert/strict'); - -assert.doesNotThrow( - () => { - throw new TypeError('Wrong value'); - }, - SyntaxError, -); -``` - -However, the following will result in an [`AssertionError`][] with the message -'Got unwanted exception...': - -```mjs -import assert from 'node:assert/strict'; - -assert.doesNotThrow( - () => { - throw new TypeError('Wrong value'); - }, - TypeError, -); -``` - -```cjs -const assert = require('node:assert/strict'); - -assert.doesNotThrow( - () => { - throw new TypeError('Wrong value'); - }, - TypeError, -); -``` - -If an [`AssertionError`][] is thrown and a value is provided for the `message` -parameter, the value of `message` will be appended to the [`AssertionError`][] -message: - -```mjs -import assert from 'node:assert/strict'; - -assert.doesNotThrow( - () => { - throw new TypeError('Wrong value'); - }, - /Wrong value/, - 'Whoops', -); -// Throws: AssertionError: Got unwanted exception: Whoops -``` - -```cjs -const assert = require('node:assert/strict'); - -assert.doesNotThrow( - () => { - throw new TypeError('Wrong value'); - }, - /Wrong value/, - 'Whoops', -); -// Throws: AssertionError: Got unwanted exception: Whoops -``` - -## `assert.equal(actual, expected[, message])` - - - -* `actual` {any} -* `expected` {any} -* `message` {string|Error} - -**Strict assertion mode** - -An alias of [`assert.strictEqual()`][]. - -**Legacy assertion mode** - -> Stability: 3 - Legacy: Use [`assert.strictEqual()`][] instead. - -Tests shallow, coercive equality between the `actual` and `expected` parameters -using the [`==` operator][]. `NaN` is specially handled -and treated as being identical if both sides are `NaN`. - -```mjs -import assert from 'node:assert'; - -assert.equal(1, 1); -// OK, 1 == 1 -assert.equal(1, '1'); -// OK, 1 == '1' -assert.equal(NaN, NaN); -// OK - -assert.equal(1, 2); -// AssertionError: 1 == 2 -assert.equal({ a: { b: 1 } }, { a: { b: 1 } }); -// AssertionError: { a: { b: 1 } } == { a: { b: 1 } } -``` - -```cjs -const assert = require('node:assert'); - -assert.equal(1, 1); -// OK, 1 == 1 -assert.equal(1, '1'); -// OK, 1 == '1' -assert.equal(NaN, NaN); -// OK - -assert.equal(1, 2); -// AssertionError: 1 == 2 -assert.equal({ a: { b: 1 } }, { a: { b: 1 } }); -// AssertionError: { a: { b: 1 } } == { a: { b: 1 } } -``` - -If the values are not equal, an [`AssertionError`][] is thrown with a `message` -property set equal to the value of the `message` parameter. If the `message` -parameter is undefined, a default error message is assigned. If the `message` -parameter is an instance of an [`Error`][] then it will be thrown instead of the -`AssertionError`. - -## `assert.fail([message])` - - - -* `message` {string|Error} **Default:** `'Failed'` - -Throws an [`AssertionError`][] with the provided error message or a default -error message. If the `message` parameter is an instance of an [`Error`][] then -it will be thrown instead of the [`AssertionError`][]. - -```mjs -import assert from 'node:assert/strict'; - -assert.fail(); -// AssertionError [ERR_ASSERTION]: Failed - -assert.fail('boom'); -// AssertionError [ERR_ASSERTION]: boom - -assert.fail(new TypeError('need array')); -// TypeError: need array -``` - -```cjs -const assert = require('node:assert/strict'); - -assert.fail(); -// AssertionError [ERR_ASSERTION]: Failed - -assert.fail('boom'); -// AssertionError [ERR_ASSERTION]: boom - -assert.fail(new TypeError('need array')); -// TypeError: need array -``` - -Using `assert.fail()` with more than two arguments is possible but deprecated. -See below for further details. - -## `assert.fail(actual, expected[, message[, operator[, stackStartFn]]])` - - - -> Stability: 0 - Deprecated: Use `assert.fail([message])` or other assert -> functions instead. - -* `actual` {any} -* `expected` {any} -* `message` {string|Error} -* `operator` {string} **Default:** `'!='` -* `stackStartFn` {Function} **Default:** `assert.fail` - -If `message` is falsy, the error message is set as the values of `actual` and -`expected` separated by the provided `operator`. If just the two `actual` and -`expected` arguments are provided, `operator` will default to `'!='`. If -`message` is provided as third argument it will be used as the error message and -the other arguments will be stored as properties on the thrown object. If -`stackStartFn` is provided, all stack frames above that function will be -removed from stacktrace (see [`Error.captureStackTrace`][]). If no arguments are -given, the default message `Failed` will be used. - -```mjs -import assert from 'node:assert/strict'; - -assert.fail('a', 'b'); -// AssertionError [ERR_ASSERTION]: 'a' != 'b' - -assert.fail(1, 2, undefined, '>'); -// AssertionError [ERR_ASSERTION]: 1 > 2 - -assert.fail(1, 2, 'fail'); -// AssertionError [ERR_ASSERTION]: fail - -assert.fail(1, 2, 'whoops', '>'); -// AssertionError [ERR_ASSERTION]: whoops - -assert.fail(1, 2, new TypeError('need array')); -// TypeError: need array -``` - -```cjs -const assert = require('node:assert/strict'); - -assert.fail('a', 'b'); -// AssertionError [ERR_ASSERTION]: 'a' != 'b' - -assert.fail(1, 2, undefined, '>'); -// AssertionError [ERR_ASSERTION]: 1 > 2 - -assert.fail(1, 2, 'fail'); -// AssertionError [ERR_ASSERTION]: fail - -assert.fail(1, 2, 'whoops', '>'); -// AssertionError [ERR_ASSERTION]: whoops - -assert.fail(1, 2, new TypeError('need array')); -// TypeError: need array -``` - -In the last three cases `actual`, `expected`, and `operator` have no -influence on the error message. - -Example use of `stackStartFn` for truncating the exception's stacktrace: - -```mjs -import assert from 'node:assert/strict'; - -function suppressFrame() { - assert.fail('a', 'b', undefined, '!==', suppressFrame); -} -suppressFrame(); -// AssertionError [ERR_ASSERTION]: 'a' !== 'b' -// at repl:1:1 -// at ContextifyScript.Script.runInThisContext (vm.js:44:33) -// ... -``` - -```cjs -const assert = require('node:assert/strict'); - -function suppressFrame() { - assert.fail('a', 'b', undefined, '!==', suppressFrame); -} -suppressFrame(); -// AssertionError [ERR_ASSERTION]: 'a' !== 'b' -// at repl:1:1 -// at ContextifyScript.Script.runInThisContext (vm.js:44:33) -// ... -``` - -## `assert.ifError(value)` - - - -* `value` {any} - -Throws `value` if `value` is not `undefined` or `null`. This is useful when -testing the `error` argument in callbacks. The stack trace contains all frames -from the error passed to `ifError()` including the potential new frames for -`ifError()` itself. - -```mjs -import assert from 'node:assert/strict'; - -assert.ifError(null); -// OK -assert.ifError(0); -// AssertionError [ERR_ASSERTION]: ifError got unwanted exception: 0 -assert.ifError('error'); -// AssertionError [ERR_ASSERTION]: ifError got unwanted exception: 'error' -assert.ifError(new Error()); -// AssertionError [ERR_ASSERTION]: ifError got unwanted exception: Error - -// Create some random error frames. -let err; -(function errorFrame() { - err = new Error('test error'); -})(); - -(function ifErrorFrame() { - assert.ifError(err); -})(); -// AssertionError [ERR_ASSERTION]: ifError got unwanted exception: test error -// at ifErrorFrame -// at errorFrame -``` - -```cjs -const assert = require('node:assert/strict'); - -assert.ifError(null); -// OK -assert.ifError(0); -// AssertionError [ERR_ASSERTION]: ifError got unwanted exception: 0 -assert.ifError('error'); -// AssertionError [ERR_ASSERTION]: ifError got unwanted exception: 'error' -assert.ifError(new Error()); -// AssertionError [ERR_ASSERTION]: ifError got unwanted exception: Error - -// Create some random error frames. -let err; -(function errorFrame() { - err = new Error('test error'); -})(); - -(function ifErrorFrame() { - assert.ifError(err); -})(); -// AssertionError [ERR_ASSERTION]: ifError got unwanted exception: test error -// at ifErrorFrame -// at errorFrame -``` - -## `assert.match(string, regexp[, message])` - - - -* `string` {string} -* `regexp` {RegExp} -* `message` {string|Error} - -Expects the `string` input to match the regular expression. - -```mjs -import assert from 'node:assert/strict'; - -assert.match('I will fail', /pass/); -// AssertionError [ERR_ASSERTION]: The input did not match the regular ... - -assert.match(123, /pass/); -// AssertionError [ERR_ASSERTION]: The "string" argument must be of type string. - -assert.match('I will pass', /pass/); -// OK -``` - -```cjs -const assert = require('node:assert/strict'); - -assert.match('I will fail', /pass/); -// AssertionError [ERR_ASSERTION]: The input did not match the regular ... - -assert.match(123, /pass/); -// AssertionError [ERR_ASSERTION]: The "string" argument must be of type string. - -assert.match('I will pass', /pass/); -// OK -``` - -If the values do not match, or if the `string` argument is of another type than -`string`, an [`AssertionError`][] is thrown with a `message` property set equal -to the value of the `message` parameter. If the `message` parameter is -undefined, a default error message is assigned. If the `message` parameter is an -instance of an [`Error`][] then it will be thrown instead of the -[`AssertionError`][]. - -## `assert.notDeepEqual(actual, expected[, message])` - - - -* `actual` {any} -* `expected` {any} -* `message` {string|Error} - -**Strict assertion mode** - -An alias of [`assert.notDeepStrictEqual()`][]. - -**Legacy assertion mode** - -> Stability: 3 - Legacy: Use [`assert.notDeepStrictEqual()`][] instead. - -Tests for any deep inequality. Opposite of [`assert.deepEqual()`][]. - -```mjs -import assert from 'node:assert'; - -const obj1 = { - a: { - b: 1, - }, -}; -const obj2 = { - a: { - b: 2, - }, -}; -const obj3 = { - a: { - b: 1, - }, -}; -const obj4 = { __proto__: obj1 }; - -assert.notDeepEqual(obj1, obj1); -// AssertionError: { a: { b: 1 } } notDeepEqual { a: { b: 1 } } - -assert.notDeepEqual(obj1, obj2); -// OK - -assert.notDeepEqual(obj1, obj3); -// AssertionError: { a: { b: 1 } } notDeepEqual { a: { b: 1 } } - -assert.notDeepEqual(obj1, obj4); -// OK -``` - -```cjs -const assert = require('node:assert'); - -const obj1 = { - a: { - b: 1, - }, -}; -const obj2 = { - a: { - b: 2, - }, -}; -const obj3 = { - a: { - b: 1, - }, -}; -const obj4 = { __proto__: obj1 }; - -assert.notDeepEqual(obj1, obj1); -// AssertionError: { a: { b: 1 } } notDeepEqual { a: { b: 1 } } - -assert.notDeepEqual(obj1, obj2); -// OK - -assert.notDeepEqual(obj1, obj3); -// AssertionError: { a: { b: 1 } } notDeepEqual { a: { b: 1 } } - -assert.notDeepEqual(obj1, obj4); -// OK -``` - -If the values are deeply equal, an [`AssertionError`][] is thrown with a -`message` property set equal to the value of the `message` parameter. If the -`message` parameter is undefined, a default error message is assigned. If the -`message` parameter is an instance of an [`Error`][] then it will be thrown -instead of the `AssertionError`. - -## `assert.notDeepStrictEqual(actual, expected[, message])` - - - -* `actual` {any} -* `expected` {any} -* `message` {string|Error} - -Tests for deep strict inequality. Opposite of [`assert.deepStrictEqual()`][]. - -```mjs -import assert from 'node:assert/strict'; - -assert.notDeepStrictEqual({ a: 1 }, { a: '1' }); -// OK -``` - -```cjs -const assert = require('node:assert/strict'); - -assert.notDeepStrictEqual({ a: 1 }, { a: '1' }); -// OK -``` - -If the values are deeply and strictly equal, an [`AssertionError`][] is thrown -with a `message` property set equal to the value of the `message` parameter. If -the `message` parameter is undefined, a default error message is assigned. If -the `message` parameter is an instance of an [`Error`][] then it will be thrown -instead of the [`AssertionError`][]. - -## `assert.notEqual(actual, expected[, message])` - - - -* `actual` {any} -* `expected` {any} -* `message` {string|Error} - -**Strict assertion mode** - -An alias of [`assert.notStrictEqual()`][]. - -**Legacy assertion mode** - -> Stability: 3 - Legacy: Use [`assert.notStrictEqual()`][] instead. - -Tests shallow, coercive inequality with the [`!=` operator][]. `NaN` is -specially handled and treated as being identical if both sides are `NaN`. - -```mjs -import assert from 'node:assert'; - -assert.notEqual(1, 2); -// OK - -assert.notEqual(1, 1); -// AssertionError: 1 != 1 - -assert.notEqual(1, '1'); -// AssertionError: 1 != '1' -``` - -```cjs -const assert = require('node:assert'); - -assert.notEqual(1, 2); -// OK - -assert.notEqual(1, 1); -// AssertionError: 1 != 1 - -assert.notEqual(1, '1'); -// AssertionError: 1 != '1' -``` - -If the values are equal, an [`AssertionError`][] is thrown with a `message` -property set equal to the value of the `message` parameter. If the `message` -parameter is undefined, a default error message is assigned. If the `message` -parameter is an instance of an [`Error`][] then it will be thrown instead of the -`AssertionError`. - -## `assert.notStrictEqual(actual, expected[, message])` - - - -* `actual` {any} -* `expected` {any} -* `message` {string|Error} - -Tests strict inequality between the `actual` and `expected` parameters as -determined by [`Object.is()`][]. - -```mjs -import assert from 'node:assert/strict'; - -assert.notStrictEqual(1, 2); -// OK - -assert.notStrictEqual(1, 1); -// AssertionError [ERR_ASSERTION]: Expected "actual" to be strictly unequal to: -// -// 1 - -assert.notStrictEqual(1, '1'); -// OK -``` - -```cjs -const assert = require('node:assert/strict'); - -assert.notStrictEqual(1, 2); -// OK - -assert.notStrictEqual(1, 1); -// AssertionError [ERR_ASSERTION]: Expected "actual" to be strictly unequal to: -// -// 1 - -assert.notStrictEqual(1, '1'); -// OK -``` - -If the values are strictly equal, an [`AssertionError`][] is thrown with a -`message` property set equal to the value of the `message` parameter. If the -`message` parameter is undefined, a default error message is assigned. If the -`message` parameter is an instance of an [`Error`][] then it will be thrown -instead of the `AssertionError`. - -## `assert.ok(value[, message])` - - - -* `value` {any} -* `message` {string|Error} - -Tests if `value` is truthy. It is equivalent to -`assert.equal(!!value, true, message)`. - -If `value` is not truthy, an [`AssertionError`][] is thrown with a `message` -property set equal to the value of the `message` parameter. If the `message` -parameter is `undefined`, a default error message is assigned. If the `message` -parameter is an instance of an [`Error`][] then it will be thrown instead of the -`AssertionError`. -If no arguments are passed in at all `message` will be set to the string: -``'No value argument passed to `assert.ok()`'``. - -Be aware that in the `repl` the error message will be different to the one -thrown in a file! See below for further details. - -```mjs -import assert from 'node:assert/strict'; - -assert.ok(true); -// OK -assert.ok(1); -// OK - -assert.ok(); -// AssertionError: No value argument passed to `assert.ok()` - -assert.ok(false, 'it\'s false'); -// AssertionError: it's false - -// In the repl: -assert.ok(typeof 123 === 'string'); -// AssertionError: false == true - -// In a file (e.g. test.js): -assert.ok(typeof 123 === 'string'); -// AssertionError: The expression evaluated to a falsy value: -// -// assert.ok(typeof 123 === 'string') - -assert.ok(false); -// AssertionError: The expression evaluated to a falsy value: -// -// assert.ok(false) - -assert.ok(0); -// AssertionError: The expression evaluated to a falsy value: -// -// assert.ok(0) -``` - -```cjs -const assert = require('node:assert/strict'); - -assert.ok(true); -// OK -assert.ok(1); -// OK - -assert.ok(); -// AssertionError: No value argument passed to `assert.ok()` - -assert.ok(false, 'it\'s false'); -// AssertionError: it's false - -// In the repl: -assert.ok(typeof 123 === 'string'); -// AssertionError: false == true - -// In a file (e.g. test.js): -assert.ok(typeof 123 === 'string'); -// AssertionError: The expression evaluated to a falsy value: -// -// assert.ok(typeof 123 === 'string') - -assert.ok(false); -// AssertionError: The expression evaluated to a falsy value: -// -// assert.ok(false) - -assert.ok(0); -// AssertionError: The expression evaluated to a falsy value: -// -// assert.ok(0) -``` - -```mjs -import assert from 'node:assert/strict'; - -// Using `assert()` works the same: -assert(0); -// AssertionError: The expression evaluated to a falsy value: -// -// assert(0) -``` - -```cjs -const assert = require('node:assert'); - -// Using `assert()` works the same: -assert(0); -// AssertionError: The expression evaluated to a falsy value: -// -// assert(0) -``` - -## `assert.rejects(asyncFn[, error][, message])` - - - -* `asyncFn` {Function|Promise} -* `error` {RegExp|Function|Object|Error} -* `message` {string} - -Awaits the `asyncFn` promise or, if `asyncFn` is a function, immediately -calls the function and awaits the returned promise to complete. It will then -check that the promise is rejected. - -If `asyncFn` is a function and it throws an error synchronously, -`assert.rejects()` will return a rejected `Promise` with that error. If the -function does not return a promise, `assert.rejects()` will return a rejected -`Promise` with an [`ERR_INVALID_RETURN_VALUE`][] error. In both cases the error -handler is skipped. - -Besides the async nature to await the completion behaves identically to -[`assert.throws()`][]. - -If specified, `error` can be a [`Class`][], [`RegExp`][], a validation function, -an object where each property will be tested for, or an instance of error where -each property will be tested for including the non-enumerable `message` and -`name` properties. - -If specified, `message` will be the message provided by the [`AssertionError`][] -if the `asyncFn` fails to reject. - -```mjs -import assert from 'node:assert/strict'; - -await assert.rejects( - async () => { - throw new TypeError('Wrong value'); - }, - { - name: 'TypeError', - message: 'Wrong value', - }, -); -``` - -```cjs -const assert = require('node:assert/strict'); - -(async () => { - await assert.rejects( - async () => { - throw new TypeError('Wrong value'); - }, - { - name: 'TypeError', - message: 'Wrong value', - }, - ); -})(); -``` - -```mjs -import assert from 'node:assert/strict'; - -await assert.rejects( - async () => { - throw new TypeError('Wrong value'); - }, - (err) => { - assert.strictEqual(err.name, 'TypeError'); - assert.strictEqual(err.message, 'Wrong value'); - return true; - }, -); -``` - -```cjs -const assert = require('node:assert/strict'); - -(async () => { - await assert.rejects( - async () => { - throw new TypeError('Wrong value'); - }, - (err) => { - assert.strictEqual(err.name, 'TypeError'); - assert.strictEqual(err.message, 'Wrong value'); - return true; - }, - ); -})(); -``` - -```mjs -import assert from 'node:assert/strict'; - -assert.rejects( - Promise.reject(new Error('Wrong value')), - Error, -).then(() => { - // ... -}); -``` - -```cjs -const assert = require('node:assert/strict'); - -assert.rejects( - Promise.reject(new Error('Wrong value')), - Error, -).then(() => { - // ... -}); -``` - -`error` cannot be a string. If a string is provided as the second -argument, then `error` is assumed to be omitted and the string will be used for -`message` instead. This can lead to easy-to-miss mistakes. Please read the -example in [`assert.throws()`][] carefully if using a string as the second -argument gets considered. - -## `assert.strictEqual(actual, expected[, message])` - - - -* `actual` {any} -* `expected` {any} -* `message` {string|Error} - -Tests strict equality between the `actual` and `expected` parameters as -determined by [`Object.is()`][]. - -```mjs -import assert from 'node:assert/strict'; - -assert.strictEqual(1, 2); -// AssertionError [ERR_ASSERTION]: Expected inputs to be strictly equal: -// -// 1 !== 2 - -assert.strictEqual(1, 1); -// OK - -assert.strictEqual('Hello foobar', 'Hello World!'); -// AssertionError [ERR_ASSERTION]: Expected inputs to be strictly equal: -// + actual - expected -// -// + 'Hello foobar' -// - 'Hello World!' -// ^ - -const apples = 1; -const oranges = 2; -assert.strictEqual(apples, oranges, `apples ${apples} !== oranges ${oranges}`); -// AssertionError [ERR_ASSERTION]: apples 1 !== oranges 2 - -assert.strictEqual(1, '1', new TypeError('Inputs are not identical')); -// TypeError: Inputs are not identical -``` - -```cjs -const assert = require('node:assert/strict'); - -assert.strictEqual(1, 2); -// AssertionError [ERR_ASSERTION]: Expected inputs to be strictly equal: -// -// 1 !== 2 - -assert.strictEqual(1, 1); -// OK - -assert.strictEqual('Hello foobar', 'Hello World!'); -// AssertionError [ERR_ASSERTION]: Expected inputs to be strictly equal: -// + actual - expected -// -// + 'Hello foobar' -// - 'Hello World!' -// ^ - -const apples = 1; -const oranges = 2; -assert.strictEqual(apples, oranges, `apples ${apples} !== oranges ${oranges}`); -// AssertionError [ERR_ASSERTION]: apples 1 !== oranges 2 - -assert.strictEqual(1, '1', new TypeError('Inputs are not identical')); -// TypeError: Inputs are not identical -``` - -If the values are not strictly equal, an [`AssertionError`][] is thrown with a -`message` property set equal to the value of the `message` parameter. If the -`message` parameter is undefined, a default error message is assigned. If the -`message` parameter is an instance of an [`Error`][] then it will be thrown -instead of the [`AssertionError`][]. - -## `assert.throws(fn[, error][, message])` - - - -* `fn` {Function} -* `error` {RegExp|Function|Object|Error} -* `message` {string} - -Expects the function `fn` to throw an error. - -If specified, `error` can be a [`Class`][], [`RegExp`][], a validation function, -a validation object where each property will be tested for strict deep equality, -or an instance of error where each property will be tested for strict deep -equality including the non-enumerable `message` and `name` properties. When -using an object, it is also possible to use a regular expression, when -validating against a string property. See below for examples. - -If specified, `message` will be appended to the message provided by the -`AssertionError` if the `fn` call fails to throw or in case the error validation -fails. - -Custom validation object/error instance: - -```mjs -import assert from 'node:assert/strict'; - -const err = new TypeError('Wrong value'); -err.code = 404; -err.foo = 'bar'; -err.info = { - nested: true, - baz: 'text', -}; -err.reg = /abc/i; - -assert.throws( - () => { - throw err; - }, - { - name: 'TypeError', - message: 'Wrong value', - info: { - nested: true, - 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: -assert.throws( - () => { - throw err; - }, - { - // The `name` and `message` properties are strings and using regular - // expressions on those will match against the string. If they fail, an - // error is thrown. - name: /^TypeError$/, - message: /Wrong/, - foo: 'bar', - info: { - nested: true, - // It is not possible to use regular expressions for nested properties! - 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, - }, -); - -// Fails due to the different `message` and `name` properties: -assert.throws( - () => { - const otherErr = new Error('Not found'); - // Copy all enumerable properties from `err` to `otherErr`. - for (const [key, value] of Object.entries(err)) { - otherErr[key] = value; - } - throw otherErr; - }, - // The error's `message` and `name` properties will also be checked when using - // an error as validation object. - err, -); -``` - -```cjs -const assert = require('node:assert/strict'); - -const err = new TypeError('Wrong value'); -err.code = 404; -err.foo = 'bar'; -err.info = { - nested: true, - baz: 'text', -}; -err.reg = /abc/i; - -assert.throws( - () => { - throw err; - }, - { - name: 'TypeError', - message: 'Wrong value', - info: { - nested: true, - 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: -assert.throws( - () => { - throw err; - }, - { - // The `name` and `message` properties are strings and using regular - // expressions on those will match against the string. If they fail, an - // error is thrown. - name: /^TypeError$/, - message: /Wrong/, - foo: 'bar', - info: { - nested: true, - // It is not possible to use regular expressions for nested properties! - 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, - }, -); - -// Fails due to the different `message` and `name` properties: -assert.throws( - () => { - const otherErr = new Error('Not found'); - // Copy all enumerable properties from `err` to `otherErr`. - for (const [key, value] of Object.entries(err)) { - otherErr[key] = value; - } - throw otherErr; - }, - // The error's `message` and `name` properties will also be checked when using - // an error as validation object. - err, -); -``` - -Validate instanceof using constructor: - -```mjs -import assert from 'node:assert/strict'; - -assert.throws( - () => { - throw new Error('Wrong value'); - }, - Error, -); -``` - -```cjs -const assert = require('node:assert/strict'); - -assert.throws( - () => { - throw new Error('Wrong value'); - }, - Error, -); -``` - -Validate error message using [`RegExp`][]: - -Using a regular expression runs `.toString` on the error object, and will -therefore also include the error name. - -```mjs -import assert from 'node:assert/strict'; - -assert.throws( - () => { - throw new Error('Wrong value'); - }, - /^Error: Wrong value$/, -); -``` - -```cjs -const assert = require('node:assert/strict'); - -assert.throws( - () => { - throw new Error('Wrong value'); - }, - /^Error: Wrong value$/, -); -``` - -Custom error validation: - -The function must return `true` to indicate all internal validations passed. -It will otherwise fail with an [`AssertionError`][]. - -```mjs -import assert from 'node:assert/strict'; - -assert.throws( - () => { - throw new Error('Wrong value'); - }, - (err) => { - assert(err instanceof Error); - assert(/value/.test(err)); - // Avoid returning anything from validation functions besides `true`. - // Otherwise, it's not clear what part of the validation failed. Instead, - // throw an error about the specific validation that failed (as done in this - // example) and add as much helpful debugging information to that error as - // possible. - return true; - }, - 'unexpected error', -); -``` - -```cjs -const assert = require('node:assert/strict'); - -assert.throws( - () => { - throw new Error('Wrong value'); - }, - (err) => { - assert(err instanceof Error); - assert(/value/.test(err)); - // Avoid returning anything from validation functions besides `true`. - // Otherwise, it's not clear what part of the validation failed. Instead, - // throw an error about the specific validation that failed (as done in this - // example) and add as much helpful debugging information to that error as - // possible. - return true; - }, - 'unexpected error', -); -``` - -`error` cannot be a string. If a string is provided as the second -argument, then `error` is assumed to be omitted and the string will be used for -`message` instead. This can lead to easy-to-miss mistakes. Using the same -message as the thrown error message is going to result in an -`ERR_AMBIGUOUS_ARGUMENT` error. Please read the example below carefully if using -a string as the second argument gets considered: - -```mjs -import assert from 'node:assert/strict'; - -function throwingFirst() { - throw new Error('First'); -} - -function throwingSecond() { - throw new Error('Second'); -} - -function notThrowing() {} - -// The second argument is a string and the input function threw an Error. -// The first case will not throw as it does not match for the error message -// thrown by the input function! -assert.throws(throwingFirst, 'Second'); -// In the next example the message has no benefit over the message from the -// error and since it is not clear if the user intended to actually match -// against the error message, Node.js throws an `ERR_AMBIGUOUS_ARGUMENT` error. -assert.throws(throwingSecond, 'Second'); -// TypeError [ERR_AMBIGUOUS_ARGUMENT] - -// The string is only used (as message) in case the function does not throw: -assert.throws(notThrowing, 'Second'); -// AssertionError [ERR_ASSERTION]: Missing expected exception: Second - -// If it was intended to match for the error message do this instead: -// It does not throw because the error messages match. -assert.throws(throwingSecond, /Second$/); - -// If the error message does not match, an AssertionError is thrown. -assert.throws(throwingFirst, /Second$/); -// AssertionError [ERR_ASSERTION] -``` - -```cjs -const assert = require('node:assert/strict'); - -function throwingFirst() { - throw new Error('First'); -} - -function throwingSecond() { - throw new Error('Second'); -} - -function notThrowing() {} - -// The second argument is a string and the input function threw an Error. -// The first case will not throw as it does not match for the error message -// thrown by the input function! -assert.throws(throwingFirst, 'Second'); -// In the next example the message has no benefit over the message from the -// error and since it is not clear if the user intended to actually match -// against the error message, Node.js throws an `ERR_AMBIGUOUS_ARGUMENT` error. -assert.throws(throwingSecond, 'Second'); -// TypeError [ERR_AMBIGUOUS_ARGUMENT] - -// The string is only used (as message) in case the function does not throw: -assert.throws(notThrowing, 'Second'); -// AssertionError [ERR_ASSERTION]: Missing expected exception: Second - -// If it was intended to match for the error message do this instead: -// It does not throw because the error messages match. -assert.throws(throwingSecond, /Second$/); - -// If the error message does not match, an AssertionError is thrown. -assert.throws(throwingFirst, /Second$/); -// AssertionError [ERR_ASSERTION] -``` - -Due to the confusing error-prone notation, avoid a string as the second -argument. - -[Object wrappers]: https://developer.mozilla.org/en-US/docs/Glossary/Primitive#Primitive_wrapper_objects_in_JavaScript -[Object.prototype.toString()]: https://tc39.github.io/ecma262/#sec-object.prototype.tostring -[`!=` operator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Inequality -[`===` operator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Strict_equality -[`==` operator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Equality -[`AssertionError`]: #class-assertassertionerror -[`CallTracker`]: #class-assertcalltracker -[`Class`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes -[`ERR_INVALID_RETURN_VALUE`]: errors.md#err_invalid_return_value -[`Error.captureStackTrace`]: errors.md#errorcapturestacktracetargetobject-constructoropt -[`Error`]: errors.md#class-error -[`Map`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map -[`Object.is()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is -[`RegExp`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions -[`Set`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set -[`Symbol`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol -[`TypeError`]: errors.md#class-typeerror -[`WeakMap`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap -[`WeakSet`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet -[`assert.deepEqual()`]: #assertdeepequalactual-expected-message -[`assert.deepStrictEqual()`]: #assertdeepstrictequalactual-expected-message -[`assert.doesNotThrow()`]: #assertdoesnotthrowfn-error-message -[`assert.equal()`]: #assertequalactual-expected-message -[`assert.notDeepEqual()`]: #assertnotdeepequalactual-expected-message -[`assert.notDeepStrictEqual()`]: #assertnotdeepstrictequalactual-expected-message -[`assert.notEqual()`]: #assertnotequalactual-expected-message -[`assert.notStrictEqual()`]: #assertnotstrictequalactual-expected-message -[`assert.ok()`]: #assertokvalue-message -[`assert.strictEqual()`]: #assertstrictequalactual-expected-message -[`assert.throws()`]: #assertthrowsfn-error-message -[`getColorDepth()`]: tty.md#writestreamgetcolordepthenv -[`mock`]: test.md#mocking -[`process.on('exit')`]: process.md#event-exit -[`tracker.calls()`]: #trackercallsfn-exact -[`tracker.verify()`]: #trackerverify -[enumerable "own" properties]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Enumerability_and_ownership_of_properties -[prototype-spec]: https://tc39.github.io/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots diff --git a/fixtures/async_context.md b/fixtures/async_context.md deleted file mode 100644 index d1be2fb..0000000 --- a/fixtures/async_context.md +++ /dev/null @@ -1,888 +0,0 @@ -# Asynchronous context tracking - - - -> Stability: 2 - Stable - - - -## Introduction - -These classes are used to associate state and propagate it throughout -callbacks and promise chains. -They allow storing data throughout the lifetime of a web request -or any other asynchronous duration. It is similar to thread-local storage -in other languages. - -The `AsyncLocalStorage` and `AsyncResource` classes are part of the -`node:async_hooks` module: - -```mjs -import { AsyncLocalStorage, AsyncResource } from 'node:async_hooks'; -``` - -```cjs -const { AsyncLocalStorage, AsyncResource } = require('node:async_hooks'); -``` - -## Class: `AsyncLocalStorage` - - - -This class creates stores that stay coherent through asynchronous operations. - -While you can create your own implementation on top of the `node:async_hooks` -module, `AsyncLocalStorage` should be preferred as it is a performant and memory -safe implementation that involves significant optimizations that are non-obvious -to implement. - -The following example uses `AsyncLocalStorage` to build a simple logger -that assigns IDs to incoming HTTP requests and includes them in messages -logged within each request. - -```mjs -import http from 'node:http'; -import { AsyncLocalStorage } from 'node:async_hooks'; - -const asyncLocalStorage = new AsyncLocalStorage(); - -function logWithId(msg) { - const id = asyncLocalStorage.getStore(); - console.log(`${id !== undefined ? id : '-'}:`, msg); -} - -let idSeq = 0; -http.createServer((req, res) => { - asyncLocalStorage.run(idSeq++, () => { - logWithId('start'); - // Imagine any chain of async operations here - setImmediate(() => { - logWithId('finish'); - res.end(); - }); - }); -}).listen(8080); - -http.get('http://localhost:8080'); -http.get('http://localhost:8080'); -// Prints: -// 0: start -// 1: start -// 0: finish -// 1: finish -``` - -```cjs -const http = require('node:http'); -const { AsyncLocalStorage } = require('node:async_hooks'); - -const asyncLocalStorage = new AsyncLocalStorage(); - -function logWithId(msg) { - const id = asyncLocalStorage.getStore(); - console.log(`${id !== undefined ? id : '-'}:`, msg); -} - -let idSeq = 0; -http.createServer((req, res) => { - asyncLocalStorage.run(idSeq++, () => { - logWithId('start'); - // Imagine any chain of async operations here - setImmediate(() => { - logWithId('finish'); - res.end(); - }); - }); -}).listen(8080); - -http.get('http://localhost:8080'); -http.get('http://localhost:8080'); -// Prints: -// 0: start -// 1: start -// 0: finish -// 1: finish -``` - -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()` - - - -Creates a new instance of `AsyncLocalStorage`. Store is only provided within a -`run()` call or after an `enterWith()` call. - -### Static method: `AsyncLocalStorage.bind(fn)` - - - -> Stability: 1 - Experimental - -* `fn` {Function} The function to bind to the current execution context. -* Returns: {Function} A new function that calls `fn` within the captured - execution context. - -Binds the given function to the current execution context. - -### Static method: `AsyncLocalStorage.snapshot()` - - - -> Stability: 1 - Experimental - -* Returns: {Function} A new function with the signature - `(fn: (...args) : R, ...args) : R`. - -Captures the current execution context and returns a function that accepts a -function as an argument. Whenever the returned function is called, it -calls the function passed to it within the captured context. - -```js -const asyncLocalStorage = new AsyncLocalStorage(); -const runInAsyncScope = asyncLocalStorage.run(123, () => AsyncLocalStorage.snapshot()); -const result = asyncLocalStorage.run(321, () => runInAsyncScope(() => asyncLocalStorage.getStore())); -console.log(result); // returns 123 -``` - -AsyncLocalStorage.snapshot() can replace the use of AsyncResource for simple -async context tracking purposes, for example: - -```js -class Foo { - #runInAsyncScope = AsyncLocalStorage.snapshot(); - - get() { return this.#runInAsyncScope(() => asyncLocalStorage.getStore()); } -} - -const foo = asyncLocalStorage.run(123, () => new Foo()); -console.log(asyncLocalStorage.run(321, () => foo.get())); // returns 123 -``` - -### `asyncLocalStorage.disable()` - - - -> Stability: 1 - Experimental - -Disables the instance of `AsyncLocalStorage`. All subsequent calls -to `asyncLocalStorage.getStore()` will return `undefined` until -`asyncLocalStorage.run()` or `asyncLocalStorage.enterWith()` is called again. - -When calling `asyncLocalStorage.disable()`, all current contexts linked to the -instance will be exited. - -Calling `asyncLocalStorage.disable()` is required before the -`asyncLocalStorage` can be garbage collected. This does not apply to stores -provided by the `asyncLocalStorage`, as those objects are garbage collected -along with the corresponding async resources. - -Use this method when the `asyncLocalStorage` is not in use anymore -in the current process. - -### `asyncLocalStorage.getStore()` - - - -* Returns: {any} - -Returns the current store. -If called outside of an asynchronous context initialized by -calling `asyncLocalStorage.run()` or `asyncLocalStorage.enterWith()`, it -returns `undefined`. - -### `asyncLocalStorage.enterWith(store)` - - - -> Stability: 1 - Experimental - -* `store` {any} - -Transitions into the context for the remainder of the current -synchronous execution and then persists the store through any following -asynchronous calls. - -Example: - -```js -const store = { id: 1 }; -// Replaces previous store with the given store object -asyncLocalStorage.enterWith(store); -asyncLocalStorage.getStore(); // Returns the store object -someAsyncOperation(() => { - asyncLocalStorage.getStore(); // Returns the same object -}); -``` - -This transition will continue for the _entire_ synchronous execution. -This means that if, for example, the context is entered within an event -handler subsequent event handlers will also run within that context unless -specifically bound to another context with an `AsyncResource`. That is why -`run()` should be preferred over `enterWith()` unless there are strong reasons -to use the latter method. - -```js -const store = { id: 1 }; - -emitter.on('my-event', () => { - asyncLocalStorage.enterWith(store); -}); -emitter.on('my-event', () => { - asyncLocalStorage.getStore(); // Returns the same object -}); - -asyncLocalStorage.getStore(); // Returns undefined -emitter.emit('my-event'); -asyncLocalStorage.getStore(); // Returns the same object -``` - -### `asyncLocalStorage.run(store, callback[, ...args])` - - - -* `store` {any} -* `callback` {Function} -* `...args` {any} - -Runs a function synchronously within a context and returns its -return value. The store is not accessible outside of the callback function. -The store is accessible to any asynchronous operations created within the -callback. - -The optional `args` are passed to the callback function. - -If the callback function throws an error, the error is thrown by `run()` too. -The stacktrace is not impacted by this call and the context is exited. - -Example: - -```js -const store = { id: 2 }; -try { - asyncLocalStorage.run(store, () => { - asyncLocalStorage.getStore(); // Returns the store object - setTimeout(() => { - asyncLocalStorage.getStore(); // Returns the store object - }, 200); - throw new Error(); - }); -} catch (e) { - asyncLocalStorage.getStore(); // Returns undefined - // The error will be caught here -} -``` - -### `asyncLocalStorage.exit(callback[, ...args])` - - - -> Stability: 1 - Experimental - -* `callback` {Function} -* `...args` {any} - -Runs a function synchronously outside of a context and returns its -return value. The store is not accessible within the callback function or -the asynchronous operations created within the callback. Any `getStore()` -call done within the callback function will always return `undefined`. - -The optional `args` are passed to the callback function. - -If the callback function throws an error, the error is thrown by `exit()` too. -The stacktrace is not impacted by this call and the context is re-entered. - -Example: - -```js -// Within a call to run -try { - asyncLocalStorage.getStore(); // Returns the store object or value - asyncLocalStorage.exit(() => { - asyncLocalStorage.getStore(); // Returns undefined - throw new Error(); - }); -} catch (e) { - asyncLocalStorage.getStore(); // Returns the same object or value - // The error will be caught here -} -``` - -### Usage with `async/await` - -If, within an async function, only one `await` call is to run within a context, -the following pattern should be used: - -```js -async function fn() { - await asyncLocalStorage.run(new Map(), () => { - asyncLocalStorage.getStore().set('key', value); - return foo(); // The return value of foo will be awaited - }); -} -``` - -In this example, the store is only available in the callback function and the -functions called by `foo`. Outside of `run`, calling `getStore` will return -`undefined`. - -### Troubleshooting: Context loss - -In most cases, `AsyncLocalStorage` works without issues. In rare situations, the -current store is lost in one of the asynchronous operations. - -If your code is callback-based, it is enough to promisify it with -[`util.promisify()`][] so it starts working with native promises. - -If you need to use a callback-based API or your code assumes -a custom thenable implementation, use the [`AsyncResource`][] class -to associate the asynchronous operation with the correct execution context. -Find the function call responsible for the context loss by logging the content -of `asyncLocalStorage.getStore()` after the calls you suspect are responsible -for the loss. When the code logs `undefined`, the last callback called is -probably responsible for the context loss. - -## Class: `AsyncResource` - - - -The class `AsyncResource` is designed to be extended by the embedder's async -resources. Using this, users can easily trigger the lifetime events of their -own resources. - -The `init` hook will trigger when an `AsyncResource` is instantiated. - -The following is an overview of the `AsyncResource` API. - -```mjs -import { AsyncResource, executionAsyncId } from 'node:async_hooks'; - -// AsyncResource() is meant to be extended. Instantiating a -// new AsyncResource() also triggers init. If triggerAsyncId is omitted then -// async_hook.executionAsyncId() is used. -const asyncResource = new AsyncResource( - type, { triggerAsyncId: executionAsyncId(), requireManualDestroy: false }, -); - -// Run a function in the execution context of the resource. This will -// * establish the context of the resource -// * trigger the AsyncHooks before callbacks -// * call the provided function `fn` with the supplied arguments -// * trigger the AsyncHooks after callbacks -// * restore the original execution context -asyncResource.runInAsyncScope(fn, thisArg, ...args); - -// Call AsyncHooks destroy callbacks. -asyncResource.emitDestroy(); - -// Return the unique ID assigned to the AsyncResource instance. -asyncResource.asyncId(); - -// Return the trigger ID for the AsyncResource instance. -asyncResource.triggerAsyncId(); -``` - -```cjs -const { AsyncResource, executionAsyncId } = require('node:async_hooks'); - -// AsyncResource() is meant to be extended. Instantiating a -// new AsyncResource() also triggers init. If triggerAsyncId is omitted then -// async_hook.executionAsyncId() is used. -const asyncResource = new AsyncResource( - type, { triggerAsyncId: executionAsyncId(), requireManualDestroy: false }, -); - -// Run a function in the execution context of the resource. This will -// * establish the context of the resource -// * trigger the AsyncHooks before callbacks -// * call the provided function `fn` with the supplied arguments -// * trigger the AsyncHooks after callbacks -// * restore the original execution context -asyncResource.runInAsyncScope(fn, thisArg, ...args); - -// Call AsyncHooks destroy callbacks. -asyncResource.emitDestroy(); - -// Return the unique ID assigned to the AsyncResource instance. -asyncResource.asyncId(); - -// Return the trigger ID for the AsyncResource instance. -asyncResource.triggerAsyncId(); -``` - -### `new AsyncResource(type[, options])` - -* `type` {string} The type of async event. -* `options` {Object} - * `triggerAsyncId` {number} The ID of the execution context that created this - async event. **Default:** `executionAsyncId()`. - * `requireManualDestroy` {boolean} If set to `true`, disables `emitDestroy` - when the object is garbage collected. This usually does not need to be set - (even if `emitDestroy` is called manually), unless the resource's `asyncId` - is retrieved and the sensitive API's `emitDestroy` is called with it. - When set to `false`, the `emitDestroy` call on garbage collection - will only take place if there is at least one active `destroy` hook. - **Default:** `false`. - -Example usage: - -```js -class DBQuery extends AsyncResource { - constructor(db) { - super('DBQuery'); - this.db = db; - } - - getInfo(query, callback) { - this.db.get(query, (err, data) => { - this.runInAsyncScope(callback, null, err, data); - }); - } - - close() { - this.db = null; - this.emitDestroy(); - } -} -``` - -### Static method: `AsyncResource.bind(fn[, type[, thisArg]])` - - - -* `fn` {Function} The function to bind to the current execution context. -* `type` {string} An optional name to associate with the underlying - `AsyncResource`. -* `thisArg` {any} - -Binds the given function to the current execution context. - -### `asyncResource.bind(fn[, thisArg])` - - - -* `fn` {Function} The function to bind to the current `AsyncResource`. -* `thisArg` {any} - -Binds the given function to execute to this `AsyncResource`'s scope. - -### `asyncResource.runInAsyncScope(fn[, thisArg, ...args])` - - - -* `fn` {Function} The function to call in the execution context of this async - resource. -* `thisArg` {any} The receiver to be used for the function call. -* `...args` {any} Optional arguments to pass to the function. - -Call the provided function with the provided arguments in the execution context -of the async resource. This will establish the context, trigger the AsyncHooks -before callbacks, call the function, trigger the AsyncHooks after callbacks, and -then restore the original execution context. - -### `asyncResource.emitDestroy()` - -* Returns: {AsyncResource} A reference to `asyncResource`. - -Call all `destroy` hooks. This should only ever be called once. An error will -be thrown if it is called more than once. This **must** be manually called. If -the resource is left to be collected by the GC then the `destroy` hooks will -never be called. - -### `asyncResource.asyncId()` - -* Returns: {number} The unique `asyncId` assigned to the resource. - -### `asyncResource.triggerAsyncId()` - -* Returns: {number} The same `triggerAsyncId` that is passed to the - `AsyncResource` constructor. - - - -### Using `AsyncResource` for a `Worker` thread pool - -The following example shows how to use the `AsyncResource` class to properly -provide async tracking for a [`Worker`][] pool. Other resource pools, such as -database connection pools, can follow a similar model. - -Assuming that the task is adding two numbers, using a file named -`task_processor.js` with the following content: - -```mjs -import { parentPort } from 'node:worker_threads'; -parentPort.on('message', (task) => { - parentPort.postMessage(task.a + task.b); -}); -``` - -```cjs -const { parentPort } = require('node:worker_threads'); -parentPort.on('message', (task) => { - parentPort.postMessage(task.a + task.b); -}); -``` - -a Worker pool around it could use the following structure: - -```mjs -import { AsyncResource } from 'node:async_hooks'; -import { EventEmitter } from 'node:events'; -import path from 'node:path'; -import { Worker } from 'node:worker_threads'; - -const kTaskInfo = Symbol('kTaskInfo'); -const kWorkerFreedEvent = Symbol('kWorkerFreedEvent'); - -class WorkerPoolTaskInfo extends AsyncResource { - constructor(callback) { - super('WorkerPoolTaskInfo'); - this.callback = callback; - } - - done(err, result) { - this.runInAsyncScope(this.callback, null, err, result); - this.emitDestroy(); // `TaskInfo`s are used only once. - } -} - -export default class WorkerPool extends EventEmitter { - constructor(numThreads) { - super(); - this.numThreads = numThreads; - this.workers = []; - this.freeWorkers = []; - this.tasks = []; - - for (let i = 0; i < numThreads; i++) - this.addNewWorker(); - - // Any time the kWorkerFreedEvent is emitted, dispatch - // the next task pending in the queue, if any. - this.on(kWorkerFreedEvent, () => { - if (this.tasks.length > 0) { - const { task, callback } = this.tasks.shift(); - this.runTask(task, callback); - } - }); - } - - addNewWorker() { - const worker = new Worker(new URL('task_processor.js', import.meta.url)); - worker.on('message', (result) => { - // In case of success: Call the callback that was passed to `runTask`, - // remove the `TaskInfo` associated with the Worker, and mark it as free - // again. - worker[kTaskInfo].done(null, result); - worker[kTaskInfo] = null; - this.freeWorkers.push(worker); - this.emit(kWorkerFreedEvent); - }); - worker.on('error', (err) => { - // In case of an uncaught exception: Call the callback that was passed to - // `runTask` with the error. - if (worker[kTaskInfo]) - worker[kTaskInfo].done(err, null); - else - this.emit('error', err); - // Remove the worker from the list and start a new Worker to replace the - // current one. - this.workers.splice(this.workers.indexOf(worker), 1); - this.addNewWorker(); - }); - this.workers.push(worker); - this.freeWorkers.push(worker); - this.emit(kWorkerFreedEvent); - } - - runTask(task, callback) { - if (this.freeWorkers.length === 0) { - // No free threads, wait until a worker thread becomes free. - this.tasks.push({ task, callback }); - return; - } - - const worker = this.freeWorkers.pop(); - worker[kTaskInfo] = new WorkerPoolTaskInfo(callback); - worker.postMessage(task); - } - - close() { - for (const worker of this.workers) worker.terminate(); - } -} -``` - -```cjs -const { AsyncResource } = require('node:async_hooks'); -const { EventEmitter } = require('node:events'); -const path = require('node:path'); -const { Worker } = require('node:worker_threads'); - -const kTaskInfo = Symbol('kTaskInfo'); -const kWorkerFreedEvent = Symbol('kWorkerFreedEvent'); - -class WorkerPoolTaskInfo extends AsyncResource { - constructor(callback) { - super('WorkerPoolTaskInfo'); - this.callback = callback; - } - - done(err, result) { - this.runInAsyncScope(this.callback, null, err, result); - this.emitDestroy(); // `TaskInfo`s are used only once. - } -} - -class WorkerPool extends EventEmitter { - constructor(numThreads) { - super(); - this.numThreads = numThreads; - this.workers = []; - this.freeWorkers = []; - this.tasks = []; - - for (let i = 0; i < numThreads; i++) - this.addNewWorker(); - - // Any time the kWorkerFreedEvent is emitted, dispatch - // the next task pending in the queue, if any. - this.on(kWorkerFreedEvent, () => { - if (this.tasks.length > 0) { - const { task, callback } = this.tasks.shift(); - this.runTask(task, callback); - } - }); - } - - addNewWorker() { - const worker = new Worker(path.resolve(__dirname, 'task_processor.js')); - worker.on('message', (result) => { - // In case of success: Call the callback that was passed to `runTask`, - // remove the `TaskInfo` associated with the Worker, and mark it as free - // again. - worker[kTaskInfo].done(null, result); - worker[kTaskInfo] = null; - this.freeWorkers.push(worker); - this.emit(kWorkerFreedEvent); - }); - worker.on('error', (err) => { - // In case of an uncaught exception: Call the callback that was passed to - // `runTask` with the error. - if (worker[kTaskInfo]) - worker[kTaskInfo].done(err, null); - else - this.emit('error', err); - // Remove the worker from the list and start a new Worker to replace the - // current one. - this.workers.splice(this.workers.indexOf(worker), 1); - this.addNewWorker(); - }); - this.workers.push(worker); - this.freeWorkers.push(worker); - this.emit(kWorkerFreedEvent); - } - - runTask(task, callback) { - if (this.freeWorkers.length === 0) { - // No free threads, wait until a worker thread becomes free. - this.tasks.push({ task, callback }); - return; - } - - const worker = this.freeWorkers.pop(); - worker[kTaskInfo] = new WorkerPoolTaskInfo(callback); - worker.postMessage(task); - } - - close() { - for (const worker of this.workers) worker.terminate(); - } -} - -module.exports = WorkerPool; -``` - -Without the explicit tracking added by the `WorkerPoolTaskInfo` objects, -it would appear that the callbacks are associated with the individual `Worker` -objects. However, the creation of the `Worker`s is not associated with the -creation of the tasks and does not provide information about when tasks -were scheduled. - -This pool could be used as follows: - -```mjs -import WorkerPool from './worker_pool.js'; -import os from 'node:os'; - -const pool = new WorkerPool(os.availableParallelism()); - -let finished = 0; -for (let i = 0; i < 10; i++) { - pool.runTask({ a: 42, b: 100 }, (err, result) => { - console.log(i, err, result); - if (++finished === 10) - pool.close(); - }); -} -``` - -```cjs -const WorkerPool = require('./worker_pool.js'); -const os = require('node:os'); - -const pool = new WorkerPool(os.availableParallelism()); - -let finished = 0; -for (let i = 0; i < 10; i++) { - pool.runTask({ a: 42, b: 100 }, (err, result) => { - console.log(i, err, result); - if (++finished === 10) - pool.close(); - }); -} -``` - -### Integrating `AsyncResource` with `EventEmitter` - -Event listeners triggered by an [`EventEmitter`][] may be run in a different -execution context than the one that was active when `eventEmitter.on()` was -called. - -The following example shows how to use the `AsyncResource` class to properly -associate an event listener with the correct execution context. The same -approach can be applied to a [`Stream`][] or a similar event-driven class. - -```mjs -import { createServer } from 'node:http'; -import { AsyncResource, executionAsyncId } from 'node:async_hooks'; - -const server = createServer((req, res) => { - req.on('close', AsyncResource.bind(() => { - // Execution context is bound to the current outer scope. - })); - req.on('close', () => { - // Execution context is bound to the scope that caused 'close' to emit. - }); - res.end(); -}).listen(3000); -``` - -```cjs -const { createServer } = require('node:http'); -const { AsyncResource, executionAsyncId } = require('node:async_hooks'); - -const server = createServer((req, res) => { - req.on('close', AsyncResource.bind(() => { - // Execution context is bound to the current outer scope. - })); - req.on('close', () => { - // Execution context is bound to the scope that caused 'close' to emit. - }); - res.end(); -}).listen(3000); -``` - -[`AsyncResource`]: #class-asyncresource -[`EventEmitter`]: events.md#class-eventemitter -[`Stream`]: stream.md#stream -[`Worker`]: worker_threads.md#class-worker -[`util.promisify()`]: util.md#utilpromisifyoriginal