├── .github └── ISSUE_TEMPLATE ├── 000-index.md ├── 001-public-stream-base.md ├── 002-es-modules.md ├── 003-url.md ├── 004-uv_link_t.md ├── 005-ABI-Stable-Module-API.md ├── 006-asynchooks-api.md └── README.md /.github/ISSUE_TEMPLATE: -------------------------------------------------------------------------------- 1 | Thanks for getting involved in Node's EP process. Please note that all opened 2 | issues are for the explicit purpose of allowing existing PR's to break 3 | conversation into subtopics. In the case that the PR's conversation becomes too 4 | long or complex to maintain in a single thread. 5 | 6 | All issues opened not following the above guideline will be immediately closed. 7 | Issues opened for a given PR should be referenced from that PR, and the PR 8 | referenced in the opened issue. 9 | -------------------------------------------------------------------------------- /000-index.md: -------------------------------------------------------------------------------- 1 | # Node.js Enhancement Proposals 2 | 3 | * [Public C++ Streams](001-public-stream-base.md) 4 | * [ES6 Module Interoperability](002-es-modules.md) 5 | * [WHATWG URL API](003-url.md) 6 | * [Port core to uv_link_t](004-uv_link_t.md) 7 | * [ABI Stable Module API](005-ABI-Stable-Module-API.md) 8 | * [AsyncHooks API](006-asynchooks-api.md) 9 | -------------------------------------------------------------------------------- /001-public-stream-base.md: -------------------------------------------------------------------------------- 1 | | Title | Make C++ Stream APIs public | 2 | |--------|-----------------------------| 3 | | Author | @indutny | 4 | | Status | REJECTED | 5 | | Date | 2015-12-17 19:39:46 | 6 | 7 | ## Description 8 | 9 | Currently we use so called `StreamBase` APIs through the core to implement 10 | various performance-critical networking protocols with maximum efficiency. 11 | It is evident that this performance improvement could be beneficial for 12 | user-land C++ addons too. 13 | 14 | I propose to make this APIs public, introducing C++ interface similar to the 15 | following one (simplified): 16 | 17 | ## Interface 18 | 19 | ```C++ 20 | class Stream { 21 | public: 22 | // 23 | // Low-level part 24 | // 25 | 26 | // Perform protocol-specific shutdown (EOF) 27 | virtual int DoShutdown(ShutdownWrap* req_wrap) = 0; 28 | 29 | // Write data asynchronously 30 | virtual int DoWrite(WriteWrap* w, 31 | uv_buf_t* bufs, 32 | size_t count, 33 | uv_stream_t* send_handle) = 0; 34 | 35 | // **(optional)** 36 | // Try to write provided data immediately without blocking, if not 37 | // possible to do - should return `0` if no data was written, should decrement 38 | // `count` and change pointers/length in `bufs` if some data was written. 39 | virtual int DoTryWrite(uv_buf_t** bufs, size_t* count); 40 | 41 | // 42 | // High-level part 43 | // 44 | 45 | // Cast to the deepest class. Just a stub, probably does not need to be 46 | // here. 47 | virtual void* Cast() = 0; 48 | 49 | // Return `true` or `false` depending on liveness of the underlying 50 | // resource. If Stream is not alive - all operations on it will result in 51 | // `EINVAL` 52 | virtual bool IsAlive() = 0; 53 | 54 | // Return `true` if stream is currently closing. Closed streams should return 55 | // `false` when calling `IsAlive` 56 | virtual bool IsClosing() = 0; 57 | 58 | // **optional**, if `true` - `send_handle` may be not `nullptr` in `DoWrite` 59 | virtual bool IsIPCPipe(); 60 | 61 | // **optional**, return fd 62 | virtual int GetFD(); 63 | 64 | // Start reading and emitting data 65 | virtual int ReadStart() = 0; 66 | 67 | // Stop reading and emitting data (immediately) 68 | virtual int ReadStop() = 0; 69 | 70 | protected: 71 | // One of these must be implemented 72 | virtual AsyncWrap* GetAsyncWrap(); 73 | virtual v8::Local GetObject(); 74 | }; 75 | ``` 76 | 77 | ## Public APIs 78 | 79 | Public facing APIs have two sides: JS and C++. 80 | 81 | ### JS 82 | 83 | ```js 84 | stream.fd; // stream's fd or -1 85 | stream._externalStream; // External pointer to the deepest C++ class 86 | stream.readStart(); 87 | stream.readStop(); 88 | stream.shutdown(req); 89 | stream.writev(req, [ ..., chunk, size, ... ]); 90 | stream.writeBuffer(req, buffer); 91 | stream.writeAsciiString(req, string); 92 | stream.writeUtf8String(req, string); 93 | stream.writeUcs2String(req, string); 94 | stream.writeBinaryString(req, string); 95 | ``` 96 | 97 | ### C++ 98 | 99 | All of the C++ interface methods may be called. Additionally following methods 100 | are available: 101 | 102 | ```C++ 103 | class Stream { 104 | public: 105 | // 106 | // Low-level APIs 107 | // 108 | 109 | // Invoke `after_write_cb` 110 | inline void OnAfterWrite(WriteWrap* w); 111 | 112 | // Invoke `alloc_cb` 113 | inline void OnAlloc(size_t size, uv_buf_t* buf); 114 | 115 | // Invoke `read_cb` 116 | inline void OnRead(size_t nread, 117 | const uv_buf_t* buf, 118 | uv_handle_type pending = UV_UNKNOWN_HANDLE); 119 | 120 | // Override and get callbacks 121 | inline void set_after_write_cb(Callback c); 122 | inline void set_alloc_cb(Callback c); 123 | inline void set_read_cb(Callback c); 124 | inline Callback after_write_cb(); 125 | inline Callback alloc_cb(); 126 | inline Callback read_cb(); 127 | 128 | // 129 | // High-level APIs 130 | // 131 | 132 | // Add JS API methods to the target `FunctionTemplate` 133 | // NOTE: `flags` control some optional methods like: `shutdown` and `writev` 134 | static inline void AddMethods(Environment* env, 135 | v8::Local target, 136 | int flags = kFlagNone); 137 | 138 | // Emit data to the JavaScript listeners 139 | void EmitData(ssize_t nread, 140 | v8::Local buf, 141 | v8::Local handle); 142 | 143 | // Mark stream as consumed 144 | inline void Consume(); 145 | 146 | // TODO(indutny): add this to node.js core 147 | inline void Unconsume(); 148 | }; 149 | ``` 150 | 151 | ## Putting things together 152 | 153 | This APIs could be used for implementing high-performance protocols in a 154 | following way: 155 | 156 | Each TCP socket has a `_handle` property, which is actually a C++ `Stream` 157 | instance. One may wrap this `_handle` into a custom C++ `Stream` instance, and 158 | replace the `_handle` property with it. This stream could override `read`, 159 | `alloc`, `after_write` callbacks to hook up the incoming data from the wrapped 160 | stream. 161 | 162 | Calling `DoWrite` and friends on the wrapped stream will result in performing 163 | actual TCP writes. 164 | 165 | It is easy to see that it could be wrapped this way several times, creating 166 | levels of abstraction without leaving C++! 167 | -------------------------------------------------------------------------------- /002-es-modules.md: -------------------------------------------------------------------------------- 1 | | Title | ES Module Interoperability | 2 | |--------|-----------------------------| 3 | | Author | @bmeck | 4 | | Status | DRAFT | 5 | | Date | 2017-03-01 | 6 | 7 | **NOTE:** `DRAFT` status does not mean ESM will be implemented in Node 8 | core. Instead that this is the standard, should Node core decide to implement 9 | ESM. At which time this draft would be moved to `ACCEPTED`. 10 | 11 | --- 12 | 13 | Abbreviations: 14 | * `ESM` - Ecma262 Modules (ES Modules) 15 | * `CJS` - Node Modules (a CommonJS variant) 16 | 17 | The intent of this standard is to: 18 | 19 | * implement interoperability for ESM and Node's existing CJS module system 20 | 21 | ## 1. Purpose 22 | 23 | 1. Allow a common module syntax for Browser and Server. 24 | 2. Allow a common set of context variables for Browser and Server. 25 | 26 | ## 2. Related 27 | 28 | [ECMA262](https://tc39.github.io/ecma262/) discusses the syntax and semantics of 29 | related syntax, and introduces ESM. 30 | 31 | [Dynamic Import](https://github.com/tc39/proposal-dynamic-import) introduces 32 | `import()` which will be available in all parsing goals. 33 | 34 | ### 2.1. Types 35 | 36 | * **[ModuleRecord] 37 | (https://tc39.github.io/ecma262/#sec-abstract-module-records)** 38 | - Defines the list of imports via `[[ImportEntry]]`. 39 | - Defines the list of exports via `[[ExportEntry]]`. 40 | 41 | * **[ModuleNamespace] 42 | (https://tc39.github.io/ecma262/#sec-module-namespace-objects)** 43 | - Represents a read-only static set of bindings to a module's exports. 44 | 45 | ### 2.2. Operations 46 | 47 | * **[ParseModule](https://tc39.github.io/ecma262/#sec-parsemodule)** 48 | - Creates a [SourceTextModuleRecord] 49 | (https://tc39.github.io/ecma262/#sec-source-text-module-records) from 50 | source code. 51 | 52 | * **[HostResolveImportedModule] 53 | (https://tc39.github.io/ecma262/#sec-hostresolveimportedmodule)** 54 | - A hook for when an import is exactly performed. This returns a 55 | `ModuleRecord`. Used as a means to grab modules from Node's loader/cache. 56 | 57 | ## 3. Semantics 58 | 59 | ### 3.1. Async loading 60 | 61 | ESM imports will be loaded asynchronously. This matches browser behavior. 62 | This means: 63 | 64 | * If a new `import` is queued up, it will never evaluate synchronously. 65 | * Between module evaluation within the same graph there may be other work done. 66 | Order of module evaluation within a module graph will always be preserved. 67 | * Multiple module graphs could be loading at the same time concurrently. 68 | 69 | ### 3.2. Determining if source is an ES Module 70 | 71 | A new file type will be recognised, `.mjs`, for ES modules. This file type will 72 | be registered with IANA as an official file type, see 73 | [TC39 issue](https://github.com/tc39/ecma262/issues/322). There are no known 74 | issues with browsers since they 75 | [do not determine MIME type using file extensions](https://mimesniff.spec.whatwg.org/#interpreting-the-resource-metadata). 76 | 77 | The `.mjs` file extension will not be loadable via `require()`. This means that, 78 | once the Node resolution algorithm reaches file expansion, the path for 79 | path + `.mjs` would throw an error. In order to support loading ESM in CJS files 80 | please use `import()`. 81 | 82 | #### 3.2.1 MIME of `.mjs` files 83 | 84 | The MIME used to identify `.mjs` files should be a [web compatible JavaScript MIME Type](https://html.spec.whatwg.org/multipage/scripting.html#javascript-mime-type). 85 | 86 | ### 3.3. ES Import Path Resolution 87 | 88 | 89 | ES `import` statements will perform non-exact searches on relative or 90 | absolute paths, like `require()`. This means that file extensions, and 91 | index files will be searched. However, ESM import specifier resolution will be 92 | done using URLs which match closer to the browser. Unlike browsers, only the 93 | `file:` protocol will be supported until network and security issues can be 94 | researched for other protocols. 95 | 96 | With `import` being URL based encoding and decoding will automatically be 97 | performed. This may affect file paths containing any of the following 98 | characters: `:`,`?`,`#`, or `%`. Details of the parsing algorithm are at the 99 | [WHATWG URL Spec](https://url.spec.whatwg.org/). 100 | 101 | * paths with `:` face multiple variations of path mutation 102 | * paths with `%` in their path segments would be decoded 103 | * paths with `?`, or `#` in their paths would face truncation of pathname 104 | 105 | All behavior differing from the 106 | [`type=module` path resolution algorithm](https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-module-specifier) 107 | will be places in locations that would throw errors in the browser. 108 | 109 | #### 3.3.1. Algorithms 110 | 111 | ##### 3.3.1.1. HostResolveImportedModule Search 112 | 113 | Notes: 114 | 115 | * The CLI has a location URL of the process working directory. 116 | * Paths are resolved to realpaths normally *after* all these steps. 117 | 118 | 1. Apply the URL parser to `specifier`. If the result is not failure, 119 | return the result. 120 | 2. If `specifier` does start with the character U+002F SOLIDUS (`/`), the 121 | two-character sequence U+002E FULL STOP, U+002F SOLIDUS (`./`), or the 122 | three-character sequence U+002E FULL STOP, U+002E FULL STOP, 123 | U+002F SOLIDUS (`../`) 124 | 1. Let `specifier` be the result of applying the URL parser to `specifier` 125 | with importing location's URL as the base URL. 126 | 2. Return the result of applying the path search algorithm to `specifier`. 127 | 3. Return the result of applying the module search algorithm to `specifier`. 128 | 129 | ##### 3.3.1.2. Path Search 130 | 131 | 1. If it does not throw an error, return the result of applying the file search 132 | algorithm to `specifier`. 133 | 2. If it does not throw an error, return the result of applying the directory 134 | search algorithm to `specifier`. 135 | 3. Throw an error. 136 | 137 | ##### 3.3.1.3. File Search 138 | 139 | 1. If the resource for `specifier` exists, return `specifier`. 140 | 2. For each file extension `[".mjs", ".js", ".json", ".node"]` 141 | 1. Let `searchable` be a new URL from `specifier`. 142 | 2. Append the file extension to the pathname of `searchable`. 143 | 3. If the resource for `searchable` exists, return `searchable`. 144 | 3. Throw an error. 145 | 146 | ##### 3.3.1.4. Package Main Search 147 | 148 | 1. If it does not throw an error, return the result of applying the file search 149 | algorithm to `specifier`. 150 | 2. If it does not throw an error, return the result of applying the index 151 | search algorithm to `specifier`. 152 | 3. Throw an error. 153 | 154 | ##### 3.3.1.5. Index Search 155 | 156 | 1. Let `searchable` be a new URL from `specifier`. 157 | 2. If `searchable` does not have a trailing `/` in its pathname append one. 158 | 3. Let `searchable` be the result of applying the URL parser to `./index` with 159 | `specifier` as the base URL. 160 | 4. If it does not throw an error, return the result of applying the file search 161 | algorithm to `searchable`. 162 | 5. Throw an error. 163 | 164 | ##### 3.3.1.6. Directory Search 165 | 166 | 1. Let `dir` be a new URL from `specifier`. 167 | 2. If `dir` does not have a trailing `/` in its pathname append one. 168 | 3. Let `searchable` be the result of applying the URL parser to `./package.json` 169 | with `dir` as the base URL. 170 | 4. If the resource for `searchable` exists and it contains a "main" field. 171 | 1. Let `main` be the result of applying the URL parser to the `main` field 172 | with `dir` as the base URL. 173 | 2. If it does not throw an error, return the result of applying the package 174 | main search algorithm to `main`. 175 | 5. If it does not throw an error, return the result of applying the index 176 | search algorithm to `dir`. 177 | 6. Throw an error. 178 | 179 | ##### 3.3.1.7. Module Search 180 | 181 | 1. Let `package` be a new URL from the directory containing the importing 182 | location. If `package` is the same as the importing location, throw an error. 183 | 2. If `package` does not have a trailing `/` in its pathname append one. 184 | 3. Let `searchable` be the result of applying the URL parser to 185 | `./node_modules/${specifier}` with `package` as the base URL. 186 | 4. If it does not throw an error, return the result of applying the file search 187 | algorithm to `searchable`. 188 | 5. If it does not throw an error, return the result of applying the directory 189 | search algorithm to `searchable`. 190 | 6. Let `parent` be the result of applying the URL parser to `../` with 191 | `package` as the base URL. 192 | 7. If it does not throw an error, return the result of applying the module 193 | search algorithm to `specifier` with an importing location of `parent`. 194 | 8. Throw an error. 195 | 196 | ##### 3.3.1.7. Examples 197 | 198 | ```javascript 199 | import 'file:///etc/config/app.json'; 200 | ``` 201 | 202 | Parseable with the URL parser. No searching. 203 | 204 | ```javascript 205 | import './foo'; 206 | import './foo?search'; 207 | import './foo#hash'; 208 | import '../bar'; 209 | import '/baz'; 210 | ``` 211 | 212 | Applies the URL parser to the specifiers with a base url of the importing 213 | location. Then performs the path search algorithm. 214 | 215 | ```javascript 216 | import 'baz'; 217 | import 'abc/123'; 218 | ``` 219 | 220 | Performs the module search algorithm. 221 | 222 | ### 4. Compatibility 223 | 224 | #### 4.3.2 Removal of non-local dependencies 225 | 226 | All of the following will not be supported by the `import` statement: 227 | 228 | * `$NODE_PATH` 229 | * `$HOME/.node_modules` 230 | * `$HOME/.node_libraries` 231 | * `$PREFIX/lib/node` 232 | 233 | Use local dependencies, and symbolic links as needed. 234 | 235 | ##### 4.3.2.1 How to support non-local dependencies 236 | 237 | Although not recommended, and in fact discouraged, there is a way to support 238 | non-local dependencies. **USE THIS AT YOUR OWN DISCRETION**. 239 | 240 | Symlinks of `node_modules -> $HOME/.node_modules`, `node_modules/foo/ -> 241 | $HOME/.node_modules/foo/`, etc. will continue to be supported. 242 | 243 | Adding a parent directory with `node_modules` symlinked will be an effective 244 | strategy for recreating these functionalities. This will incur the known 245 | problems with non-local dependencies, but now leaves the problems in the hands 246 | of the user, allowing Node to give more clear insight to your modules by 247 | reducing complexity. 248 | 249 | Given: 250 | 251 | ```sh 252 | /opt/local/myapp 253 | ``` 254 | 255 | Transform to: 256 | 257 | ```sh 258 | /opt/local/non-local-deps/myapp 259 | /opt/local/non-local-deps/node_modules -> $PREFIX/lib/node (etc.) 260 | ``` 261 | 262 | And nest as many times as needed. 263 | 264 | #### 4.3.3. Removal of fallthrough behavior. 265 | 266 | Exact algorithm TBD. 267 | 268 | #### 4.3.4. Errors from new path behavior. 269 | 270 | In the case that an `import` statement is unable to find a module, Node should 271 | make a **best effort** to see if `require` would have found the module and 272 | print out where it was found, if `NODE_PATH` was used, if `HOME` was used, etc. 273 | 274 | #### 4.4. Shipping both ESM and CJS 275 | 276 | When a `package.json` main is encountered, file extension searches are used to 277 | provide a means to ship both ESM and CJS variants of packages. If we have two 278 | entry points `index.mjs` and `index.js` setting `"main":"./index"` in 279 | `package.json` will make Node pick up either, depending on what is supported. 280 | 281 | ##### 4.4.1. Excluding main 282 | 283 | Since `main` in `package.json` is entirely optional even inside of npm 284 | packages, some people may prefer to exclude main entirely in the case of using 285 | `./index` as that is still in the Node module search algorithm. 286 | 287 | ### 4.5. ESM Evaluation 288 | 289 | #### 4.5.1. Environment Variables 290 | 291 | ESM will not be bootstrapped with magic variables and will await upcoming 292 | specifications in order to provide such behaviors in a standard way. As such, 293 | the following variables are changed: 294 | 295 | | Variable | Exists | Value | 296 | | ---- | ---- | --- | 297 | | this | y | [`undefined`](https://tc39.github.io/ecma262/#sec-module-environment-records-getthisbinding) | 298 | | arguments | n | | 299 | | require | n | | 300 | | module | n | | 301 | | exports | n | | 302 | | __filename | n | | 303 | | __dirname | n | | 304 | 305 | Like normal scoping rules, if a variable does not exist in a scope, the outer 306 | scope is used to find the variable. Since ESM are always strict, errors may be 307 | thrown upon trying to use variables that do not exist globally when using ESM. 308 | 309 | ##### 4.5.1.1. Standardization Effort 310 | 311 | [Efforts are ongoing to reserve a specifier](https://github.com/whatwg/html/issues/1013) 312 | that will be compatible in both Browsers and Node. Tentatively it will be 313 | `js:context` and export a single `{url}` value. 314 | 315 | ##### 4.5.1.2. Getting CJS Variables Workaround 316 | 317 | Although heavily advised against, you can have a CJS module sibling for your 318 | ESM that can export these things: 319 | 320 | ```js 321 | // expose.js 322 | module.exports = {__dirname}; 323 | ``` 324 | 325 | ```js 326 | // use.mjs 327 | import expose from './expose.js'; 328 | const {__dirname} = expose; 329 | ``` 330 | 331 | ### 4.6. ES consuming CommonJS 332 | 333 | After *any* CJS finishes evaluation, it will be placed into the same cache as 334 | ESM. The value of what is placed in the cache will reflect a single default 335 | export pointing to the value of `module.exports` at the time evaluation ended. 336 | 337 | Essentially after any CJS completes evaluation: 338 | 339 | 1. if there was an error, place the error in the ESM cache and return 340 | 2. let `export` be the value of `module.exports` 341 | 3. if there was an error, place the error in the ESM cache and return 342 | 4. create an ESM with `{default:module.exports}` as its namespace 343 | 5. place the ESM in the ESM cache 344 | 345 | Note: step 4 is the only time the value of `module.exports` is assigned to the 346 | ESM. 347 | 348 | #### 4.6.1. default imports 349 | 350 | `module.exports` is a single value. As such it does not have the dictionary 351 | like properties of ES module exports. In order to transform a CJS module into 352 | ESM a `default` export which will point to the value of `module.exports` that 353 | was snapshotted *imediately after the CJS finished evaluation*. Due to problems 354 | in supporting named imports, they will not be enabled by default. Space is 355 | intentionally left open to allow named properties to be supported through 356 | future explorations. 357 | 358 | ##### 4.6.1.1. Examples 359 | 360 | Given: 361 | 362 | ```javascript 363 | // cjs.js 364 | module.exports = { 365 | default:'my-default', 366 | thing:'stuff' 367 | }; 368 | ``` 369 | 370 | You will grab `module.exports` when performing an ESM import of `cjs.js`. 371 | 372 | ```javascript 373 | // es.mjs 374 | 375 | import * as baz from './cjs.js'; 376 | // baz = { 377 | // get default() {return module.exports;}, 378 | // } 379 | 380 | import foo from './cjs.js'; 381 | // foo = module.exports; 382 | 383 | import {default as bar} from './cjs.js'; 384 | // bar = module.exports 385 | ``` 386 | 387 | ------ 388 | 389 | Given: 390 | 391 | ```javascript 392 | // cjs.js 393 | module.exports = null; 394 | ``` 395 | 396 | You will grab `module.exports` when performing an ES import. 397 | 398 | ```javascript 399 | // es.mjs 400 | import foo from './cjs.js'; 401 | // foo = null; 402 | 403 | import * as bar from './cjs.js'; 404 | // bar = {default:null}; 405 | ``` 406 | 407 | ------ 408 | 409 | Given: 410 | 411 | ```javascript 412 | // cjs.js 413 | module.exports = function two() { 414 | return 2; 415 | }; 416 | ``` 417 | 418 | You will grab `module.exports` when performing an ESM import. 419 | 420 | ```javascript 421 | // es.mjs 422 | import foo from './cjs.js'; 423 | foo(); // 2 424 | 425 | import * as bar from './cjs.js'; 426 | bar.default(); // 2 427 | bar(); // throws, bar is not a function 428 | ``` 429 | 430 | ------ 431 | 432 | Given: 433 | 434 | ```javascript 435 | // cjs.js 436 | module.exports = Promise.resolve(3); 437 | ``` 438 | 439 | You will grab `module.exports` when performing an ES import. 440 | 441 | ```javascript 442 | // es.mjs 443 | import foo from './cjs.js'; 444 | foo.then(console.log); // outputs 3 445 | 446 | import * as bar from './cjs.js'; 447 | bar.default.then(console.log); // outputs 3 448 | bar.then(console.log); // throws, bar is not a Promise 449 | ``` 450 | 451 | ### 4.7. CommonJS consuming ES 452 | 453 | #### 4.7.1. default exports 454 | 455 | ES modules only export named values. A "default" export is an export that uses 456 | the property named `default`. 457 | 458 | ##### 4.7.1.1. Examples 459 | 460 | Given: 461 | 462 | ```javascript 463 | // es.mjs 464 | let foo = {bar:'my-default'}; 465 | // note: 466 | // this is a value 467 | // it is not a binding like `export {foo}` 468 | export default foo; 469 | foo = null; 470 | ``` 471 | 472 | ```javascript 473 | // cjs.js 474 | const es_namespace = await import('./es'); 475 | // es_namespace ~= { 476 | // get default() { 477 | // return result_from_evaluating_foo; 478 | // } 479 | // } 480 | console.log(es_namespace.default); 481 | // {bar:'my-default'} 482 | ``` 483 | 484 | ------ 485 | 486 | Given: 487 | 488 | ```javascript 489 | // es.mjs 490 | export let foo = {bar:'my-default'}; 491 | export {foo as bar}; 492 | export function f() {}; 493 | export class c {}; 494 | ``` 495 | 496 | ```javascript 497 | // cjs.js 498 | const es_namespace = await import('./es'); 499 | // es_namespace ~= { 500 | // get foo() {return foo;} 501 | // get bar() {return foo;} 502 | // get f() {return f;} 503 | // get c() {return c;} 504 | // } 505 | ``` 506 | 507 | ### 4.8. Known Gotchas 508 | 509 | All of these gotchas relate to opt-in semantics and the fact that CommonJS is a 510 | dynamic loader while ES is a static loader. 511 | 512 | No existing code will be affected. 513 | 514 | #### 4.8.1. ES exports are read only 515 | 516 | The objects create by an ES module are [ModuleNamespace Objects][5]. 517 | 518 | These have `[[Set]]` be a no-op and are read only views of the exports of an ES 519 | module. Attempting to reassign any named export will not work, but assigning to 520 | the properties of the exports follows normal rules. This also means that keys 521 | cannot be added. 522 | 523 | ### 4.9. CJS modules allow mutation of imported modules 524 | 525 | CJS modules have allowed mutation on imported modules. When ES modules are 526 | integrating against CJS systems like Grunt, it may be necessary to mutate a 527 | `module.exports`. 528 | 529 | Remember that `module.exports` from CJS is directly available under `default` 530 | for `import`. This means that if you use: 531 | 532 | ```javascript 533 | import * as namespace from 'grunt'; 534 | ``` 535 | 536 | According to ES `*` grabs the namespace directly whose properties will be 537 | read-only. 538 | 539 | However, doing: 540 | 541 | ```javascript 542 | import grunt_default from 'grunt'; 543 | ``` 544 | 545 | Grabs the `default` which is exactly what `module.exports` is, and all the 546 | properties will be mutable. 547 | 548 | #### 4.9.1. ES will not honor reassigning `module.exports` after evaluation 549 | 550 | Since we need a consistent time to snapshot the `module.exports` of a CJS 551 | module. We will execute it immediately after evaluation. Code such as: 552 | 553 | ```javascript 554 | // bad-cjs.js 555 | module.exports = 123; 556 | setTimeout(_ => module.exports = null); 557 | ``` 558 | 559 | Will not see `module.exports` change to `null`. All ES module `import`s of the 560 | module will always see `123`. 561 | 562 | ## 5. JS APIs 563 | 564 | * `vm.Module` and ways to create custom ESM implementations such as those in 565 | [jsdom](https://github.com/tmpvar/jsdom). 566 | * `vm.ReflectiveModule` as a means to declare a list of exports and expose a 567 | reflection API to those exports. 568 | * Providing an option to both `vm.Script` and `vm.Module` to intercept 569 | `import()`. 570 | 571 | * Loader hooks for: 572 | * Rewriting the URL of an `import` request *prior* to loader resolution. 573 | * Way to insert Modules a module's local ESM cache. 574 | * Way to insert Modules the global ESM cache. -------------------------------------------------------------------------------- /003-url.md: -------------------------------------------------------------------------------- 1 | | Title | Implement WHATWG URL Spec | 2 | |--------|-----------------------------| 3 | | Author | @jasnell | 4 | | Status | DRAFT | 5 | | Date | 2016-08-11T10:00:00-07:00 | 6 | 7 | ## Description 8 | 9 | The [WHATWG URL Standard](https://url.spec.whatwg.org/) specifies updated 10 | syntax, parsing and serialization of URLs as currently implemented by the main 11 | Web Browsers. The existing Node.js `url` module parsing and serialization 12 | implementation currently does not support the URL standard and fails to pass 13 | about 160 of the WHATWG URL parsing tests. 14 | 15 | This proposal is to implement the WHATWG URL Standard by modifying the existing 16 | `url` module to provide an implementation of the `URL` object and associated 17 | APIs as defined by the WHATWG URL Parsing specification. Doing so improves the 18 | robustness of URL parsing, provides consistency with browser JS code, and can 19 | eventually allow the reduction of node.js specific APIs. 20 | 21 | Initially, the implementation would be introduced as an *undocumented* 22 | experimental feature exposed via a new `URL` property in the `url` module. 23 | 24 | *Note*: In Browser implementations, the `URL` constructor is a global. In the 25 | initial Node.js implementation, the `URL` constructor would **not** be a 26 | global; instead, it would be accessible via the `url` module (e.g. 27 | `const URL = require('url').URL`). 28 | 29 | Because the existing `require('url')` module remains otherwise unmodified, 30 | there should be no backwards compatibility concerns. Once the performance of 31 | the new URL implementation is on par with the existing `url.parse()` 32 | implementation, `url.parse()` will go through a normal deprecation cycle and 33 | the new URL implementation will become a fully documentation and supported 34 | feature. 35 | 36 | The current implementation can be found at: 37 | 38 | https://github.com/nodejs/node/pull/7448 39 | 40 | ## Example 41 | 42 | ```js 43 | const URL = require('url').URL; 44 | const url = new URL('http://user:pass@example.org:1234/p/a/t/h?xyz=abc#hash'); 45 | 46 | console.log(url.protocol); // http: 47 | console.log(url.username); // user 48 | console.log(url.password); // password 49 | console.log(url.host); // example.org:1234 50 | console.log(url.hostname); // example.org 51 | console.log(url.port); // 1234 52 | console.log(url.pathname); // /p/a/t/h 53 | console.log(url.search); // ?xyz=abc 54 | console.log(url.searchParams); // SearchParams object 55 | console.log(url.hash); // hash 56 | 57 | // The SearchParams object is defined by the WHATWG spec also 58 | url.searchParams.append('key', 'value'); 59 | 60 | console.log(url); 61 | // http://user:pass@example.org:1234/p/a/t/h?xyz=abc&key=value#hash 62 | 63 | 64 | // Example using a base URL 65 | const url2 = new URL('/foo', url); 66 | console.log(url.protocol); // http: 67 | console.log(url.username); // user 68 | console.log(url.password); // password 69 | console.log(url.host); // example.org:1234 70 | console.log(url.hostname); // example.org 71 | console.log(url.port); // 1234 72 | console.log(url.pathname); // /foo 73 | console.log(url.search); // '' 74 | console.log(url.searchParams); // SearchParams object 75 | console.log(url.hash); // '' 76 | ``` 77 | 78 | ## APIs 79 | 80 | The public API would be as defined by the 81 | [WHATWG spec](https://url.spec.whatwg.org/#api). 82 | 83 | ### `new URL(href, base)` 84 | 85 | The constructor implements the WHATWG basic parsing algorithm. Accessible via 86 | `const URL = require('url').URL` 87 | 88 | * `href` is a `string` containing the URL to parse. 89 | * `base` is either a `string` or a `URL` that contains the base URL to resolve 90 | against while parsing. 91 | 92 | See https://url.spec.whatwg.org/#urlutils-members for detail on the properties 93 | of the `URL` object. 94 | 95 | #### `url.protocol` 96 | 97 | * Returns a string 98 | * Getter/Setter 99 | 100 | #### `url.username` 101 | 102 | * Returns a string 103 | * Getter/Setter 104 | 105 | #### `url.password` 106 | 107 | * Returns a string 108 | * Getter/Setter 109 | 110 | #### `url.host` 111 | 112 | * Returns a string 113 | * Getter/Setter 114 | 115 | #### `url.hostname` 116 | 117 | * Returns a string 118 | * Getter/Setter 119 | 120 | #### `url.port` 121 | 122 | * Returns an integer 123 | * Getter/Setter 124 | 125 | #### `url.pathname` 126 | 127 | * Returns a string 128 | * Getter/Setter 129 | 130 | #### `url.search` 131 | 132 | * Returns a string 133 | * Getter/Setter 134 | 135 | #### `url.searchParams` 136 | 137 | * Returns a URLSearchParams object 138 | * Getter 139 | 140 | #### `url.hash` 141 | 142 | * Returns a string 143 | * Getter/Setter 144 | 145 | #### `url.origin` 146 | 147 | * Returns a string 148 | * Getter 149 | 150 | #### `url.href` 151 | 152 | * Returns a string 153 | * Getter 154 | 155 | #### `url.toString()` 156 | 157 | * Returns a string (same as `url.href`) 158 | 159 | ### `URLSearchParams` 160 | 161 | Returned by the `url.searchParams` property and provides access read and write 162 | access to the search params. It is defined at 163 | https://url.spec.whatwg.org/#interface-urlsearchparams. 164 | 165 | Accessible via `require('url').URLSearchParams` 166 | 167 | #### `searchParams.append(name, value)` 168 | #### `searchParams.delete(name)` 169 | #### `searchParams.get(name)` 170 | #### `searchParams.getAll(name)` 171 | #### `searchParams.has(name)` 172 | #### `searchParams.set(name, value)` 173 | #### `searchParams.*[Symbol.iterator]()` 174 | #### `searchParams.toString()` 175 | 176 | ### `URL.domainToASCII(domain)` 177 | ### `URL.domainToUnicode(domain)` 178 | -------------------------------------------------------------------------------- /004-uv_link_t.md: -------------------------------------------------------------------------------- 1 | | Title | Port core to uv_link_t | 2 | |--------|-----------------------------| 3 | | Author | @indutny | 4 | | Status | DRAFT | 5 | | Date | 2016-06-03 | 6 | 7 | ## 1. Proposal 8 | 9 | I propose to replace `StreamBase` with [`uv_link_t`][0], and port existing 10 | classes that inherit from `StreamBase`. 11 | 12 | ## 2. Current state of C++ Streams in Node 13 | 14 | The fast HTTP and TLS protocol implementation in Node core depends on so called 15 | `StreamBase` C++ class and auxiliary `StreamReq` and `StreamResources` classes. 16 | This class is an analog of JavaScript streams in C++. 17 | The main ideas behind `StreamBase` are to: 18 | 19 | 1. Avoid unnecessary memory allocations by reading data directly into buffer 20 | from which it will be synchronously parsed 21 | 2. Avoid calling JavaScript unless absolutely necessary 22 | 23 | However, this API has lots of dependencies on the core internals, and thus 24 | can't be used outside of it. 25 | 26 | ## 3. uv_link_t 27 | 28 | From [`uv_link_t`][0] readme: 29 | 30 | Chainable libuv streams. 31 | 32 | It is quite easy to write a TCP server/client in [libuv][1]. Writing HTTP 33 | server/client is a bit harder. Writing HTTP server/client on top of TLS 34 | server could be unwieldy. 35 | 36 | `uv_link_t` aims to solve complexity problem that quickly escalates once 37 | using multiple layers of protocols in [libuv][1] by providing a way to 38 | implement protocols separately and chain them together in an easy and 39 | high-performant way using very narrow interfaces. 40 | 41 | Given that [`uv_link_t`][0] depends only on [libuv][1], it is very easy to 42 | envision how it will be used in Node.js addons. Even core modules could 43 | be reimplemented on a user level without a need to patch the core itself. 44 | 45 | Additionally, with the `uv_link_observer_t` (which is a part of [`uv_link_t`][0] 46 | distribution), all of the reads that right now happen internally within the 47 | `StreamBase` will be **observable**. This means that JavaScript user code will 48 | still be able to listen for `data` events on the `net.Socket` that was consumed 49 | by some `uv_link_t` stream. 50 | 51 | ## 3.1. uv_link_t technical description 52 | 53 | _(NOTE: chain is built from links)_ 54 | 55 | _(NOTE: many of these API methods have return values, check them!)_ 56 | 57 | First, a `uv_stream_t*` instance needs to be picked. It will act as a source 58 | link in a chain: 59 | ```c 60 | uv_stream_t* stream = ...; 61 | 62 | uv_link_source_t source; 63 | 64 | uv_link_source_init(uv_default_loop(), &source, stream); 65 | ``` 66 | 67 | A chain can be formed with `&source` and any other `uv_link_t` instance: 68 | ```c 69 | uv_link_t link; 70 | 71 | static uv_link_methods_t methods = { 72 | .read_start = read_start_impl, 73 | .read_stop = read_stop_impl, 74 | .write = write_impl, 75 | .try_write = try_write_impl, 76 | .shutdown = shutdown_impl, 77 | .close = close_impl, 78 | 79 | /* These will be used only when chaining two links together */ 80 | 81 | .alloc_cb_override = alloc_cb_impl, 82 | .read_cb_override = read_cb_impl 83 | }; 84 | 85 | uv_link_init(&link, &methods); 86 | 87 | /* Just like in libuv */ 88 | link.alloc_cb = my_alloc_cb; 89 | link.read_cb = my_read_cb; 90 | 91 | /* Creating a chain here */ 92 | uv_link_chain(&source, &link); 93 | 94 | uv_link_read_start(&link); 95 | ``` 96 | 97 | Now comes a funny part, any of these method implementations may hook up into 98 | the parent link in a chain to perform their actions: 99 | 100 | ```c 101 | static int shutdown_impl(uv_link_t* link, 102 | uv_link_t* source, 103 | uv_link_shutdown_cb cb, 104 | void* arg) { 105 | fprintf(stderr, "this will be printed\n"); 106 | return uv_link_propagate_shutdown(link->parent, source, cb, arg); 107 | } 108 | ``` 109 | 110 | ## 3.2. How things are going to be hooked up into the core 111 | 112 | TCP sockets, IPC pipes, and possibly TTYs will be a base elements of every 113 | chain, backed up by the `uv_link_source_t`. 114 | 115 | HTTP, TLS, WebSockets(?) will be custom `uv_link_methods_t`, so that the links 116 | could be created for them and chained together with the `uv_link_source_t`. 117 | 118 | The chaining process will happen in JavaScript through the method that will 119 | take `v8:External` from the both `uv_link_t` instances, and run `uv_link_chain` 120 | on them. 121 | 122 | User `uv_link_methods_t` implementations will work in pretty much the same way, 123 | bringing consistency to the C++ streams implementation. 124 | 125 | ## 3.3. Observability 126 | 127 | `uv_link_observer_t` may be inserted between any two `uv_link_t`s to observe 128 | the incoming data that flows between them. This is a huge difference for TLS, 129 | where right now it is not possible to read any raw incoming bytes. 130 | 131 | ## 4. Proof-of-Concept 132 | 133 | Several modules were created as a Proof-of-Concept [`uv_link_t`][0] 134 | implementations (which possibly could be modified to fit into core): 135 | 136 | * [`uv_ssl_t`][2] 137 | * [`uv_http_t`][3] 138 | 139 | They can be combined together quite trivially as demonstrated in 140 | [file-shooter][4]. 141 | 142 | ## 5. Further Thoughts 143 | 144 | Another abstraction level could be added for multiplexing [`uv_link_t`][0]s to 145 | provide a common C interface for http2 and http1.1. This will help us bring 146 | a http2 implementation into the core while reusing as much as possible of the 147 | existing code. 148 | 149 | [0]: https://github.com/indutny/uv_link_t 150 | [1]: https://github.com/libuv/libuv 151 | [2]: https://github.com/indutny/uv_ssl_t 152 | [3]: https://github.com/indutny/uv_http_t 153 | [4]: https://github.com/indutny/file-shooter 154 | -------------------------------------------------------------------------------- /005-ABI-Stable-Module-API.md: -------------------------------------------------------------------------------- 1 | | Title | ABI Stable Module API | 2 | |--------|---------------------------------------| 3 | | Author | @mhdawson, @stefanbu, @Ianwjhalliday | 4 | | Status | DRAFT | 5 | | Date | 2016-12-12 | 6 | 7 | # Introduction 8 | 9 | The module ecosystem is one of the features making Node.js the fastest 10 | growing runtime. An important subset of modules are those which have a 11 | native component. In specific cases it is important to be able to leverage native 12 | code in order to be able to: 13 | 14 | * use existing third party components written in C/C++ 15 | * access physical devices, for example a serial port 16 | * expose functionality from the operating system not otherwise available 17 | * achieve higher speed than possible in native JavaScript 18 | 19 | There is an existing interface (addons) that allows you to build these modules 20 | as a dynamically-linked shared module that can be loaded into Node.js and 21 | used as an ordinary Node.js module (for more details see 22 | https://nodejs.org/api/addons.html) 23 | 24 | Unfortunately in the existing implementation the V8 25 | (the JavaScript runtime for Node.js) APIs are exposed to modules. In 26 | addition Google changes these APIs on a regular basis. This results in 27 | the following issues: 28 | 29 | * Native modules must be recompiled for each version of Node.js 30 | * The code within modules may need to be modified for a new version of 31 | Node.js 32 | * It's not clear which parts of the V8 API the Node.js community believes 33 | are safe/unsafe to use in terms of long term support for that use. 34 | * Modules written against the V8 APIs may or may not be able to work 35 | with alternate JavaScript engines when/if Node.js supports them. 36 | 37 | The Native Abstractions for Node (NAN) 38 | project (https://github.com/nodejs/nan) can be used to limit 39 | the cases when modules must be updated in order to work with new versions 40 | of Node.js but does not address the other issues as it does not 41 | encapsulate the V8 argument types such that modules still need 42 | to be recompiled for new Node.js versions. 43 | 44 | # Proposed Action 45 | 46 | The goal of this eps is to define an API that: 47 | 48 | * Provides a stable API which is not tied to the current 49 | or further APIs provided by V8 50 | * Allows native modules built as shared libraries to be used 51 | with different versions of Node.js 52 | * Makes it clear which APIs can use with the expectation 53 | of not being affected by changes in the JavaScript engine 54 | or Node.js release 55 | * The API provided by the Node.js runtime will be in C. In 56 | addition a C++ wrapper which only contains inline functions 57 | and uses the C APIs so that the changes in the C++ wrapper 58 | would not require recompilation. 59 | 60 | # Background 61 | 62 | There have been ongoing discussions on a new module API in both the 63 | community api working group (https://github.com/nodejs/api), other 64 | issues in the Node.js issue tracker, and the recent community Node.js 65 | face-to-face VM summit (https://github.com/jasnell/vm-summit). 66 | 67 | In that summit there was agreement that we wanted to define this 68 | module API. The general approach was to be: 69 | 70 | * Start with NAN, trim down to minimal subset based on module usage 71 | and inspection. 72 | * Add in VM agnostic methods for object lifecycle management. 73 | * Add in VM agnostic exception management 74 | * Define C/C++ types for the API which are independent from V8 75 | (NAN currently uses the V8 types). 76 | * Use the NAN examples to work through the API and build up the 77 | definition of the key methods. 78 | * Validate on most used modules in the ecosystem 79 | * Complete performance analysis to confirm accepted level of overhead. 80 | 81 | Notes/details on other related work and the current experimental progress 82 | on implementing this eps can be found in: 83 | https://docs.google.com/document/d/1bX9SZKufbfhr7ncXL9fFev2LrljymtFZdWpwmv7mB9g/edit#heading=h.ln9aj9qjo969 84 | 85 | # API definition 86 | 87 | **WORK IN PROGRESS, DRIVEN BY MODULES PORTED SO FAR** 88 | 89 | This API will be built up from a minimal set. Based on this set 90 | we have been able to port the NaN example and level down. Additions 91 | will be made as we port additional modules. 92 | 93 | There has been enough progress that instead of listing the API methods 94 | here we will point to the header files in the working PoC repos so it 95 | remains up to date: 96 | 97 | Core methods: 98 | * [node_jsvmapi_types.h](https://github.com/nodejs/abi-stable-node/blob/api-prototype-6.2.0/src/node_jsvmapi_types.h) 99 | * [node_jsvmapi.h](https://github.com/nodejs/abi-stable-node/blob/api-prototype-6.2.0/src/node_jsvmapi.h) 100 | 101 | AsyncWorker support methods: 102 | 103 | * [node_asyncapi_types.h](https://github.com/nodejs/abi-stable-node/blob/api-prototype-6.2.0/src/node_asyncapi_types.h) 104 | * [node_asyncapi.h](https://github.com/nodejs/abi-stable-node/blob/api-prototype-6.2.0/src/node_asyncapi.h) 105 | 106 | 107 | # C++ Wrapper API 108 | 109 | The current view is that the API exported by the Node.js binary 110 | must be in C in order to achieve ABI stability. This [document](http://www.oracle.com/technetwork/articles/servers-storage-dev/stablecplusplusabi-333927.html) 111 | outlines some of the challenges with respect to C++ and 112 | ABI stability. 113 | 114 | However, we also believe that a C++ wrapper is possible and 115 | would provide ease of use. The key element is that the C++ part must all 116 | be inlined and only use the external functions/types exported 117 | by the C API. 118 | 119 | The current set of helpers to provide ease of use are defined in: 120 | 121 | [node_api_helpers.h](https://github.com/nodejs/abi-stable-node/blob/api-prototype-6.2.0/src/node_api_helpers.h) 122 | 123 | Some of the more complex parts like AsyncWorker will quite likely end up outside 124 | the scope of the ABI stable API itself, and be in a separate component. 125 | For example, they may end up part of a version of NaN that supports the new API. 126 | -------------------------------------------------------------------------------- /006-asynchooks-api.md: -------------------------------------------------------------------------------- 1 | | Title | AsyncHook API | 2 | |--------|---------------| 3 | | Author | @trevnorris | 4 | | Status | ACCEPTED | 5 | | Date | 2017-01-27 | 6 | 7 | ## Description 8 | 9 | Since its initial introduction along side the `AsyncListener` API, the internal 10 | class `AsyncWrap` has slowly evolved to ensure a generalized API that would 11 | serve as a solid base for module authors who wished to add listeners to the 12 | event loop's life cycle. Some of the use cases `AsyncWrap` has covered are long 13 | stack traces, continuation local storage, profiling of asynchronous requests 14 | and resource tracking. The public API is now exposed as `'async_hooks'`. 15 | 16 | It must be clarified that the `AsyncHook` API is not meant to abstract away 17 | Node's implementation details. Observing how operations are performed, not 18 | simply how they appear to perform from the public API, is a key point in its 19 | usability. For a real world example, a user was having difficulty figuring out 20 | why their `http.get()` calls were taking so long. Because `AsyncHook` exposed 21 | the `dns.lookup()` request to resolve the host being made by the `net` module 22 | it was trivial to write a performance measurement that exposed the hidden 23 | culprit. Which was that DNS resolution was taking much longer than expected. 24 | 25 | A small amount of abstraction is necessary to accommodate the conceptual nature 26 | of the API. For example, the `HTTPParser` is not destructed but instead placed 27 | into an unused pool to be used later. Even so, at the time it is placed into 28 | the pool the `destroy()` callback will run and then the id assigned to that 29 | instance will be removed. If that resource is again requested then it will be 30 | assigned a new id and will run `init()` again. 31 | 32 | 33 | ## Goals 34 | 35 | The intent for the initial release is to provide the most minimal set of API 36 | hooks that don't inhibit module authors from writing tools addressing anything 37 | in this problem space. In order to remain minimal, all potential features for 38 | initial release will first be judged on whether they can be achieved by the 39 | existing public API. If so then such a feature won't be included. 40 | 41 | If the feature cannot be done with the existing public API then discussion will 42 | be had on whether requested functionality should be included in the initial 43 | API. If so then the best course of action to include the capabilities of the 44 | request will be discussed and performed. Meaning, the resulting API may not be 45 | the same as initially requested, but the user will be able to achieve the same 46 | end result. 47 | 48 | Performance impact of `AsyncHook` should be zero if not being used, and near 49 | zero while being used. The performance overhead of `AsyncHook` callbacks 50 | supplied by the user should account for essentially all the performance 51 | overhead. 52 | 53 | 54 | ## Terminology 55 | 56 | Because of the potential ambiguity for those not familiar with the terms 57 | "handle" and "request" they should be well defined. 58 | 59 | Handles are a reference to a system resource. Some resources are a simple 60 | identifier. For example, file system handles are represented by a file 61 | descriptor. Other handles are represented by libuv as a platform abstracted 62 | struct, e.g. `uv_tcp_t`. Each handle can be continually reused to access and 63 | operate on the referenced resource. 64 | 65 | Requests are short lived data structures created to accomplish one task. The 66 | callback for a request should always and only ever fire one time. Which is when 67 | the assigned task has either completed or encountered an error. Requests are 68 | used by handles to perform these tasks. Such as accepting a new connection or 69 | writing data to disk. 70 | 71 | When both "handle" and "request" are being addressed it is simply called a 72 | "resource". 73 | 74 | 75 | ## API 76 | 77 | ### Overview 78 | 79 | Here is a quick overview of the entire API. All of this API is explained in 80 | more detail further down. 81 | 82 | ```js 83 | // Standard way of requiring a module. Snake case follows core module 84 | // convention. 85 | const async_hooks = require('async_hooks'); 86 | 87 | // Return the id of the current execution context. Useful for tracking state 88 | // and retrieving the resource of the current trigger without needing to use an 89 | // AsyncHook. 90 | const cid = async_hooks.currentId(); 91 | 92 | // Return the id of the resource responsible for triggering the callback of the 93 | // current execution scope to fire. 94 | const tid = async_hooks.triggerId(); 95 | 96 | // Propagating the correct trigger id to newly created asynchronous resource is 97 | // important. To make that easier triggerIdScope() will make sure all resources 98 | // created during callback() have that trigger. This also tracks nested calls 99 | // and will unwind properly. 100 | async_hooks.triggerIdScope(triggerId, callback); 101 | 102 | // Create a new instance of AsyncHook. All of these callbacks are optional. 103 | const asyncHook = async_hooks.createHook({ init, before, after, destroy }); 104 | 105 | // Allow callbacks of this AsyncHook instance to fire. This is not an implicit 106 | // action after running the constructor, and must be explicitly run to begin 107 | // executing callbacks. 108 | asyncHook.enable(); 109 | 110 | // Disable listening for all new asynchronous events. 111 | asyncHook.disable(); 112 | 113 | 114 | // The following are the callbacks that can be passed to createHook(). 115 | 116 | // init() is called during object construction. The resource may not have 117 | // completed construction when this callback runs. So all fields of the 118 | // resource referenced by "id" may not have been populated. 119 | function init(id, type, triggerId, resource) { } 120 | 121 | // before() is called just before the resource's callback is called. It can be 122 | // called 0-N times for handles (e.g. TCPWrap), and should be called exactly 1 123 | // time for requests (e.g. FSReqWrap). 124 | function before(id) { } 125 | 126 | // after() is called just after the resource's callback has finished, and will 127 | // always fire. In the case of an uncaught exception after() will fire after 128 | // the 'uncaughtException' handler, or domain, has handled the exception. 129 | function after(id) { } 130 | 131 | // destroy() is called when an AsyncWrap instance is destroyed. In cases like 132 | // HTTPParser where the resource is reused, or timers where the resource is 133 | // only a JS object, destroy() will be triggered manually to fire 134 | // asynchronously after the after() hook has completed. 135 | function destroy(id) { } 136 | 137 | 138 | // The following is the recommended embedder API. 139 | 140 | // AsyncEvent() is meant to be extended. Instantiating a new AsyncEvent() also 141 | // triggers init(). If triggerId is omitted then currentId() is used. 142 | const asyncEvent = new AsyncEvent(type[, triggerId]); 143 | 144 | // Call before() hooks. 145 | asyncEvent.emitBefore(); 146 | 147 | // Call after() hooks. It is important that before/after calls are unwound 148 | // in the same order they are called. Otherwise an unrecoverable exception 149 | // will be made. 150 | asyncEvent.emitAfter(); 151 | 152 | // Call destroy() hooks. 153 | asyncEvent.emitDestroy(); 154 | 155 | // Return the unique id assigned to the AsyncEvent instance. 156 | asyncEvent.asyncId(); 157 | 158 | // Return the trigger id for the AsyncEvent instance. 159 | asyncEvent.triggerId(); 160 | 161 | 162 | // The following calls are specific to the embedder API. This API is considered 163 | // more low-level than the AsyncEvent API, and the AsyncEvent API is 164 | // recommended over using the following. 165 | 166 | // Return new unique id for a constructing resource. This value must be 167 | // manually tracked by the user for triggering async events. 168 | const id = async_hooks.newId(); 169 | 170 | // Retrieve the triggerId for the constructed resource. This value must be 171 | // manually tracked by the user for triggering async events. 172 | async_hooks.initTriggerId(id); 173 | 174 | // Set the trigger id for the next asynchronous resource that is created. The 175 | // value is reset after it's been retrieved. This API is similar to 176 | // triggerIdScope() except it's more brittle because if a constructor fails the 177 | // then the internal field may not be reset. 178 | async_hooks.setInitTriggerId(id); 179 | 180 | // Call the init() callbacks. It is recommended that resource be passed. If it 181 | // is not then "null" will be passed to the init() hook. 182 | async_hooks.emitInit(id, type[, triggerId[, resource]]); 183 | 184 | // Call the before() callbacks. The reason for requiring both arguments is 185 | // explained in further detail below. 186 | async_hooks.emitBefore(id, triggerId); 187 | 188 | // Call the after() callbacks. 189 | async_hooks.emitAfter(id); 190 | 191 | // Call the destroy() callbacks. 192 | async_hooks.emitDestroy(id); 193 | ``` 194 | 195 | 196 | ### `async_hooks` 197 | 198 | The object returned from `require('async_hooks')`. 199 | 200 | 201 | #### `async_hooks.currentId()` 202 | 203 | * Returns {Number} 204 | 205 | Return the id of the current execution context. Useful to track when something 206 | fires. For example: 207 | 208 | ```js 209 | console.log(async_hooks.currentId()); // 1 - bootstrap 210 | fs.open(path, (err, fd) => { 211 | console.log(async_hooks.currentId()); // 2 - open() 212 | }): 213 | ``` 214 | 215 | It is important to note that the id returned has to do with execution timing. 216 | Not causality (which is covered by `triggerId()`). For example: 217 | 218 | ```js 219 | const server = net.createServer(function onconnection(conn) { 220 | // Returns the id of the server, not of the new connection. Because the 221 | // on connection callback runs in the execution scope of the server's 222 | // MakeCallback(). 223 | async_hooks.currentId(); 224 | 225 | }).listen(port, function onlistening() { 226 | // Returns the id of a TickObject (i.e. process.nextTick()) because all 227 | // callbacks passed to .listen() are wrapped in a nextTick(). 228 | async_hooks.currentId(); 229 | }); 230 | ``` 231 | 232 | 233 | #### `async_hooks.triggerId()` 234 | 235 | * Returns {Number} 236 | 237 | Return the id of the resource responsible for calling the callback that is 238 | currently being executed. For example: 239 | 240 | ```js 241 | const server = net.createServer(conn => { 242 | // Though the resource that caused (or triggered) this callback to 243 | // be called was that of the new connection. Thus the return value 244 | // of triggerId() is the id of "conn". 245 | async_hooks.triggerId(); 246 | 247 | }).listen(port, () => { 248 | // Even though all callbacks passed to .listen() are wrapped in a nextTick() 249 | // the callback itself exists because the call to the server's .listen() 250 | // was made. So the return value would be the id of the server. 251 | async_hooks.triggerId(); 252 | }); 253 | ``` 254 | 255 | 256 | #### `async_hooks.triggerIdScope(triggerId, callback)` 257 | 258 | * `triggerId` {Number} 259 | * `callback` {Function} 260 | * Returns {Undefined} 261 | 262 | All resources created during the execution of `callback` will be given 263 | `triggerId`. Unless it was otherwise 1) passed in as an argument to 264 | `AsyncEvent` 2) set via `setInitTriggerId()` or 3) a nested call to 265 | `triggerIdScope()` is made. 266 | 267 | Meant to be used in conjunction with the `AsyncEvent` API, and preferred over 268 | `setInitTriggerId()` because it is more error proof. 269 | 270 | Example using this to make sure the correct `triggerId` propagates to newly 271 | created asynchronous resources: 272 | 273 | ```js 274 | class MyThing extends AsyncEvent { 275 | constructor(foo, cb) { 276 | this.foo = foo; 277 | async_hooks.triggerIdScope(this.asyncId(), () => { 278 | process.nextTick(cb); 279 | }); 280 | } 281 | } 282 | ``` 283 | 284 | 285 | #### `async_hooks.createHook(callbacks)` 286 | 287 | * `callbacks` {Object} 288 | * Returns {AsyncHook} 289 | 290 | `createHook()` returns an `AsyncHook` instance that contains information about 291 | the callbacks that will fire during specific asynchronous events in the 292 | lifetime of the event loop. The focal point of these calls centers around the 293 | `AsyncWrap` C++ class. These callbacks will also be called to emulate the 294 | lifetime of resources that do not fit this model. For example, `HTTPParser` 295 | instances are recycled to improve performance. So the `destroy()` callback will 296 | be called manually after a request is complete. Just before it's placed into 297 | the unused resource pool. 298 | 299 | All callbacks are optional. So, for example, if only resource cleanup needs to 300 | be tracked then only the `destroy()` callback needs to be passed. The 301 | specifics of all functions that can be passed to `callbacks` is in the section 302 | `Hook Callbacks`. 303 | 304 | **Error Handling**: If any `AsyncHook` callbacks throw the application will 305 | print the stack trace and exit. The exit path does follow that of any uncaught 306 | exception, except for the fact that it is not catchable by an uncaught 307 | exception handler, so any `'exit'` callbacks will fire. Unless the application 308 | is run with `--abort-on-uncaught-exception`. In which case a stack trace will 309 | be printed and the application will exit, leaving a core file. 310 | 311 | The reason for this error handling behavior is that these callbacks are running 312 | at potentially volatile points in an object's lifetime. For example during 313 | class construction and destruction. Because of this, it is deemed necessary to 314 | bring down the process quickly as to prevent an unintentional abort in the 315 | future. This is subject to change in the future if a comprehensive analysis is 316 | performed to ensure an exception can follow the normal control flow without 317 | unintentional side effects. 318 | 319 | 320 | #### `asyncHook.enable()` 321 | 322 | * Returns {AsyncHook} A reference to `asyncHook`. 323 | 324 | Enable the callbacks for a given `AsyncHook` instance. When a hook is enabled 325 | it is added to a global pool of hooks to execute. These hooks do not propagate 326 | with a single asynchronous chain but will be completely disabled when 327 | `disable()` is called. 328 | 329 | The reason that `enable()`/`disable()` only work on a global scale, instead of 330 | allowing every asynchronous branch to track their own as was done in the 331 | previous implementation, is because it is prohibitively expensive to track 332 | all hook instances for every asynchronous execution chain. 333 | 334 | Callbacks are not implicitly enabled after an instance is created. One goal of 335 | the API is to require explicit action from the user. Though to help simplify 336 | using `AsyncHook`, `enable()` returns the `asyncHook` instance so the call can 337 | be chained. For example: 338 | 339 | ```js 340 | const async_hooks = require('async_hooks'); 341 | 342 | const hook = async_hooks.createHook(callbacks).enable(); 343 | ``` 344 | 345 | 346 | #### `asyncHook.disable()` 347 | 348 | * Returns {AsyncHook} A reference to `asyncHook`. 349 | 350 | Disable the callbacks for a given `AsyncHook` instance from the global pool of 351 | hooks to be executed. Once a hook has been disabled it will not fire again 352 | until enabled. 353 | 354 | For API consistency `disable()` also returns the `AsyncHook` instance. 355 | 356 | 357 | ### Hook Callbacks 358 | 359 | Key events in the lifetime of asynchronous events have been categorized into 360 | four areas. On instantiation, before/after the callback is called and when the 361 | instance is destructed. For cases where resources are reused, instantiation and 362 | destructor calls are emulated. 363 | 364 | 365 | #### `init(id, type, triggerId, resource)` 366 | 367 | * `id` {Number} 368 | * `type` {String} 369 | * `triggerId` {Number} 370 | * `resource` {Object} 371 | 372 | Called when a class is constructed that has the _possibility_ to trigger an 373 | asynchronous event. This _does not_ mean the instance must trigger 374 | `before()`/`after()` before `destroy()` is called. Only that the possibility 375 | exists. 376 | 377 | This behavior can be observed by doing something like opening a resource then 378 | closing it before the resource can be used. The following snippet demonstrates 379 | this. 380 | 381 | ```js 382 | require('net').createServer().listen(function() { this.close() }); 383 | // OR 384 | clearTimeout(setTimeout(() => {}, 10)); 385 | ``` 386 | 387 | Every new resource is assigned a unique id. Since JS only supports IEEE 754 388 | floating floating point numbers, the maximum assignable id is `2^53 - 1` (also 389 | defined in `Number.MAX_SAFE_INTEGER`). At this size node can assign a new id 390 | every 100 nanoseconds and not run out for over 28 years. So while another 391 | method could be used to identify new resources that would truly never run out, 392 | doing so is not deemed necessary at this time since. If an alternative is found 393 | that has at least the same performance as using a `double` then the 394 | consideration will be made. 395 | 396 | The `type` is a String that represents the type of resource that caused 397 | `init()` to fire. Generally it will be the name of the resource's constructor. 398 | Some examples include `TCP`, `GetAddrInfo` and `HTTPParser`. Users will be able 399 | to define their own `type` when using the public embedder API. 400 | 401 | **Note:** It's possible to have `type` name collisions. So embedders are 402 | recommended to use a prefix of sorts to prevent this. 403 | 404 | `triggerId` is the `id` of the resource that caused (or "triggered") the 405 | new resource to initialize and that caused `init()` to fire. 406 | 407 | The following is a simple demonstration of this: 408 | 409 | ```js 410 | const async_hooks = require('async_hooks'); 411 | 412 | asyns_hooks.createHook({ 413 | init: (id, type, triggerId) => { 414 | const cId = async_hooks.currentId(); 415 | process._rawDebug(`${type}(${id}): trigger: ${triggerId} scope: ${cId}`); 416 | } 417 | }).enable(); 418 | 419 | require('net').createServer(c => {}).listen(8080); 420 | ``` 421 | 422 | Output hitting the server with `nc localhost 8080`: 423 | 424 | ``` 425 | TCPWRAP(2): trigger: 1 scope: 1 426 | TCPWRAP(4): trigger: 2 scope: 0 427 | ``` 428 | 429 | The second `TCPWRAP` is the new connection from the client. When a new 430 | connection is made the `TCPWrap` instance is immediately constructed. This 431 | happens outside of any JavaScript stack (side note: a `currentId()` of `0` 432 | means it's being executed in "the void", with no JavaScript stack above it). 433 | With only that information it would be impossible to link resources together in 434 | terms of what caused them to be created. So `triggerId` is given the task of 435 | propagating what resource is responsible for the new resource's existence. 436 | 437 | Below is another example with additional information about the calls to 438 | `init()` between the `before()` and `after()` calls. Specifically what the 439 | callback to `listen()` will look like. The output formatting is slightly more 440 | elaborate to make calling context easier to see. 441 | 442 | ```js 443 | 'use strict'; 444 | const async_hooks = require('async_hooks'); 445 | 446 | let ws = 0; 447 | async_hooks.createHook({ 448 | init: (id, type, triggerId) => { 449 | const cId = async_hooks.currentId(); 450 | process._rawDebug(' '.repeat(ws) + 451 | `${type}(${id}): trigger: ${triggerId} scope: ${cId}`); 452 | }, 453 | before: (id) => { 454 | process._rawDebug(' '.repeat(ws) + 'before: ', id); 455 | ws += 2; 456 | }, 457 | after: (id) => { 458 | ws -= 2; 459 | process._rawDebug(' '.repeat(ws) + 'after: ', id); 460 | }, 461 | destroy: (id) => { 462 | process._rawDebug(' '.repeat(ws) + 'destroy:', id); 463 | }, 464 | }).enable(); 465 | 466 | require('net').createServer(() => {}).listen(8080, () => { 467 | // Let's wait 10ms before logging the server started. 468 | setTimeout(() => { 469 | console.log('>>>', async_hooks.currentId()); 470 | }, 10); 471 | }); 472 | ``` 473 | 474 | Output from only starting the server: 475 | 476 | ``` 477 | TCPWRAP(2): trigger: 1 scope: 1 478 | TickObject(3): trigger: 2 scope: 1 479 | before: 3 480 | Timeout(4): trigger: 3 scope: 3 481 | TIMERWRAP(5): trigger: 3 scope: 3 482 | after: 3 483 | destroy: 3 484 | before: 5 485 | before: 4 486 | TTYWRAP(6): trigger: 4 scope: 4 487 | SIGNALWRAP(7): trigger: 4 scope: 4 488 | TTYWRAP(8): trigger: 4 scope: 4 489 | >>> 4 490 | TickObject(9): trigger: 4 scope: 4 491 | after: 4 492 | destroy: 4 493 | after: 5 494 | before: 9 495 | after: 9 496 | destroy: 9 497 | destroy: 5 498 | ``` 499 | 500 | First notice that `scope` and the value returned by `currentId()` are always 501 | the same. That's because `currentId()` simply returns the value of the 502 | current execution context; which is defined by `before()` and `after()` calls. 503 | 504 | Now if we only use `scope` to graph resource allocation we get the following: 505 | 506 | ``` 507 | TTYWRAP(6) -> Timeout(4) -> TIMERWRAP(5) -> TickObject(3) -> root(1) 508 | ``` 509 | 510 | No where here do we see the `TCPWRAP` created; which was the reason for 511 | `console.log()` being called. This is because binding to a port without a 512 | hostname is actually synchronous, but to maintain a completely asynchronous API 513 | the user's callback is placed in a `process.nextTick()`. 514 | 515 | The graph only shows **when** a resource was created. Not **why**. So to track 516 | the **why** use `triggerId`. 517 | 518 | 519 | #### `before(id)` 520 | 521 | * `id` {Number} 522 | 523 | When an asynchronous operation is triggered (such as a TCP server receiving a 524 | new connection) or completes (such as writing data to disk) a callback is 525 | called to notify node. The `before()` callback is called just before said 526 | callback is executed. `id` is the unique identifier assigned to the 527 | resource about to execute the callback. 528 | 529 | The `before()` callback will be called 0-N times if the resource is a handle, 530 | and exactly 1 time if the resource is a request. 531 | 532 | 533 | #### `after(id)` 534 | 535 | * `id` {Number} 536 | 537 | Called immediately after the callback specified in `before()` is completed. If 538 | an uncaught exception occurs during execution of the callback then `after()` 539 | will run after `'uncaughtException'` or a `domain`'s handler runs. 540 | 541 | 542 | #### `destroy(id)` 543 | 544 | * `id` {Number} 545 | 546 | Called either when the class destructor is run or if the resource is manually 547 | marked as free. For core C++ classes that have a destructor the callback will 548 | fire during deconstruction. It is also called synchronously from the embedder 549 | API `emitDestroy()`. 550 | 551 | Some resources, such as `HTTPParser`, are not actually destructed but instead 552 | placed in an unused resource pool to be used later. For these `destroy()` will 553 | be called just before the resource is placed on the unused resource pool. 554 | 555 | **Note:** Some resources depend on GC for cleanup. So if a reference is made to 556 | the `resource` object passed to `init()` it's possible that `destroy()` is 557 | never called. Causing a memory leak in the application. Of course if you know 558 | the resource doesn't depend on GC then this isn't an issue. 559 | 560 | 561 | ## Embedder API 562 | 563 | Library developers that handle their own I/O will need to hook into the 564 | `AsyncWrap` API so that all the appropriate callbacks are called. To 565 | accommodate this both a C++ and JS API is provided. 566 | 567 | 568 | ### `class AsyncEvent()` 569 | 570 | 571 | #### `AsyncEvent(type[, triggerId])` 572 | 573 | * Returns {AsyncEvent} 574 | 575 | The class `AsyncEvent` was designed to be extended from for embedder's async 576 | resources. Using this users can easily trigger the lifetime events of their 577 | own resources. 578 | 579 | The `init()` hook will trigger when `AsyncEvent` is instantiated. 580 | 581 | Example usage: 582 | 583 | ```js 584 | class DBQuery extends AsyncEvent { 585 | construtor(db) { 586 | this.db = db; 587 | } 588 | 589 | getInfo(query, callback) { 590 | this.db.get(query, (err, data) => { 591 | this.emitBefore(); 592 | callback(err, data) 593 | this.emitAfter(); 594 | }); 595 | } 596 | 597 | close() { 598 | this.db = null; 599 | this.emitDestroy(); 600 | } 601 | } 602 | ``` 603 | 604 | 605 | #### `asyncEvent.emitBefore()` 606 | 607 | * Returns {Undefined} 608 | 609 | Call all `before()` hooks and let them know a new asynchronous execution 610 | context is being entered. If nested calls to `emitBefore()` are made the stack 611 | of `id`s will be tracked and properly unwound. 612 | 613 | 614 | #### `asyncEvent.emitAfter()` 615 | 616 | * Returns {Undefined} 617 | 618 | Call all `after()` hooks. If nested calls to `emitBefore()` were made then make 619 | sure the stack is unwound properly. Otherwise an error will be thrown. 620 | 621 | If the user's callback thrown an exception then `emitAfter()` will 622 | automatically be called for all `id`'s on the stack if the error is handled by 623 | a domain or `'uncaughtException'` handler. So there is no need to guard against 624 | this. 625 | 626 | 627 | #### `asyncEvent.emitDestroy()` 628 | 629 | * Returns {Undefined} 630 | 631 | Call all `destroy()` hooks. This should only ever be called once. An error will 632 | be thrown if it is. This **must** be manually called. If the resource is left 633 | to be collected by the GC then the `destroy()` hooks will never be called. 634 | 635 | 636 | #### `asyncEvent.asyncId()` 637 | 638 | * Returns {Number} 639 | 640 | Return the unique identifier assigned to the resource. Useful when used with 641 | `triggerIdScope()`. 642 | 643 | 644 | #### `asyncEvent.triggerId()` 645 | 646 | * Returns {Number} 647 | 648 | Return the same `triggerId` that is passed to `init()` hooks. 649 | 650 | 651 | ### Standalone JS API 652 | 653 | The following API can be used as an alternative to using `AsyncEvent()`, but it 654 | is left to the embedder to manually track values needed for all emitted events 655 | (e.g. `id` and `triggerId`). It is very recommended that embedders instead 656 | use `AsyncEvent`. 657 | 658 | 659 | #### `async_hooks.newId()` 660 | 661 | * Returns {Number} 662 | 663 | Return a new unique id meant for a newly created asynchronous resource. The 664 | value returned will never be assigned to another resource. 665 | 666 | Generally this should be used during object construction. e.g.: 667 | 668 | ```js 669 | class MyClass { 670 | constructor() { 671 | this._id = async_hooks.newId(); 672 | this._triggerId = async_hooks.initTriggerId(); 673 | } 674 | } 675 | ``` 676 | 677 | 678 | #### `async_hooks.initTriggerId()` 679 | 680 | * Returns {Number} 681 | 682 | There are several ways to set the `triggerId` for an instantiated resource. 683 | This API is how that value is retrieved. It returns the `id` of the resource 684 | responsible for the newly created resource being instantiated. For example: 685 | 686 | 687 | #### `async_hooks.emitInit(id, type[, triggerId][, resource])` 688 | 689 | * `id` {Number} Generated by calling `newId()` 690 | * `type` {String} 691 | * `triggerId` {Number} **Default:** `currentId()` 692 | * `resource` {Object} **Default:** `null` 693 | * Returns {Undefined} 694 | 695 | Emit that a resource is being initialized. `id` should be a value returned by 696 | `async_hooks.newId()`. Usage will probably be as follows: 697 | 698 | ```js 699 | class Foo { 700 | constructor() { 701 | this._id = async_hooks.newId(); 702 | this._triggerId = async_hooks.initTriggerId(); 703 | async_hooks.emitInit(this._id, 'Foo', this._triggerId, this); 704 | } 705 | } 706 | ``` 707 | 708 | In the circumstance that the embedder needs to define a different trigger id 709 | than `currentId()`, they can pass in that id manually. 710 | 711 | It is suggested to have `emitInit()` be the last call in the object's 712 | constructor. 713 | 714 | 715 | #### `async_hooks.emitBefore(id[, triggerId])` 716 | 717 | * `id` {Number} Generated by `newId()` 718 | * `triggerId` {Number} 719 | * Returns {Undefined} 720 | 721 | Notify `before()` hooks the resource is about to enter its execution call 722 | stack. If the `triggerId` of the resource is different from `id` then pass 723 | it in. 724 | 725 | Example usage: 726 | 727 | ```js 728 | MyThing.prototype.done = function done() { 729 | // First call the before() hooks. So currentId() shows the id of the 730 | // resource wrapping the id that's been passed. 731 | async_hooks.emitBefore(this._id); 732 | 733 | // Run the callback. 734 | this.callback(); 735 | 736 | // Call after() callbacks now that the old id has been restored. 737 | async_hooks.emitAfter(this._id); 738 | }; 739 | ``` 740 | 741 | 742 | #### `async_hooks.emitAfter(id)` 743 | 744 | * `id` {Number} Generated by `newId()` 745 | * Returns {Undefined} 746 | 747 | Notify `after()` hooks the resource is exiting its execution call stack. 748 | 749 | Even though the state of `id` is tracked internally, passing it in is required 750 | as a way to validate that the stack is unwinding properly. 751 | 752 | For example, no two async stack should cross when `emitAfter()` is called. 753 | 754 | ``` 755 | init # Foo 756 | init # Bar 757 | ... 758 | before # Foo 759 | before # Bar 760 | after # Foo <- Should be called after Bar 761 | after # Bar 762 | ``` 763 | 764 | **Note:** It is not necessary to wrap the callback in a `try`/`finally` and 765 | force `emitAfter()` if the callback throws. That is automatically handled by 766 | the fatal exception handler. 767 | 768 | 769 | #### `async_hooks.emitDestroy(id)` 770 | 771 | * `id` {Number} Generated by `newId()` 772 | * Returns {Undefined} 773 | 774 | Notify hooks that a resource is being destroyed (or being moved to the free'd 775 | resource pool). 776 | 777 | 778 | ### Native API 779 | 780 | **Note:** The native API is not yet implemented in the initial PR associated 781 | with this EP, but will come soon enough afterward. 782 | 783 | ```cpp 784 | // Helper class users can inherit from, but is not necessary. If 785 | // `AsyncHook::MakeCallback()` is used then all four callbacks will be 786 | // called automatically. 787 | class AsyncHook { 788 | public: 789 | AsyncHook(v8::Local resource, const char* name); 790 | ~AsyncHook(); 791 | v8::MaybeLocal MakeCallback( 792 | const v8::Local callback, 793 | int argc, 794 | v8::Local* argv); 795 | v8::Local get_resource(); 796 | double get_uid(); 797 | private: 798 | AsyncHook(); 799 | v8::Persistent resource_; 800 | const char* name_; 801 | double uid_; 802 | } 803 | 804 | // Returns the id of the current execution context. If the return value is 805 | // zero then no execution has been set. This will happen if the user handles 806 | // I/O from native code. 807 | double node::GetCurrentId(); 808 | 809 | // Return same value as async_hooks.triggerId(); 810 | double node::GetTriggerId(); 811 | 812 | // If the native API doesn't inherit from the helper class then the callbacks 813 | // must be triggered manually. This triggers the init() callback. The return 814 | // value is the uid assigned to the resource. 815 | // TODO(trevnorris): This always needs to be called so that the resource can be 816 | // placed on the Map for future query. 817 | double node::EmitAsyncInit(v8::Local resource); 818 | 819 | // Emit the destroy() callback. 820 | void node::EmitAsyncDestroy(double id); 821 | 822 | // An API specific to emit before/after callbacks is unnecessary because 823 | // MakeCallback will automatically call them for you. 824 | // TODO(trevnorris): There should be a final optional parameter of 825 | // "double triggerId" that's needed so async_hooks.triggerId() returns the 826 | // correct value. 827 | v8::MaybeLocal node::MakeCallback(v8::Isolate* isolate, 828 | v8::Local recv, 829 | v8::Local callback, 830 | int argc, 831 | v8::Local* argv, 832 | double id); 833 | ``` 834 | 835 | 836 | ## API Exceptions 837 | 838 | ### Reused Resources 839 | 840 | Resources like `HTTPParser` are reused throughout the lifetime of the 841 | application. This means node will have to synthesize the `init()` and 842 | `destroy()` calls. Also the id on the class instance will need to be changed 843 | every time the resource is acquired for use. 844 | 845 | Though for a shared resource like `TimerWrap` we're not concerned with 846 | reassigning the id because it isn't directly used by the user. Instead it is 847 | used internally for JS created resources that are all assigned their own unique 848 | id, and each of these JS resources are linked to the `TimerWrap` instance. 849 | 850 | 851 | ## Notes 852 | 853 | ### Native Modules 854 | 855 | Existing native modules that call `node::MakeCallback` today will always have 856 | their trigger id as `1`. Which is the root id. There will be two native APIs 857 | that users can use. One will be a static API and another will be a class that 858 | users can inherit from. 859 | 860 | 861 | ### Promises 862 | 863 | Recently V8 has implemented an API so that async hooks can properly interface 864 | with Promises. Unfortunately this API cannot be backported, so if this API is 865 | backported then there will be an API gap in which native Promises will break 866 | the async stack. 867 | 868 | 869 | ### Immediate Write Without Request 870 | 871 | When data is written through `StreamWrap` node first attempts to write as much 872 | to the kernel as possible. If all the data can be flushed to the kernel then 873 | the function exits without creating a `WriteWrap` and calls the user's 874 | callback in `nextTick()` (which will only be called if the user passes a 875 | callback). Meaning detection of the write won't be as straightforward as 876 | watching for a `WriteWrap`. 877 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Node.js Enhancement Proposals 2 | 3 | ## Overview 4 | 5 | This repository contains the Node Enhancement Proposals (EPs) collection. These 6 | are documents describing an enhancement proposal for inclusion in Node. 7 | 8 | EPs are used when the proposed feature is a substantial new API, is too broad 9 | or would modify APIs heavily. Minor changes do not require writing an EP. What 10 | is and isn't minor is subjective, so as a general rule, users should discuss 11 | the proposal briefly by other means (issue tracker, mailing list or IRC) and 12 | write an EP when requested by the Node core team. 13 | 14 | ## Rational 15 | 16 | The idea behind the EP process is to keep track of what ideas will be worked on 17 | and which ones were discarded, and why. This should help everyone (those 18 | closely involved with the project and newcomers) have a clear picture of where 19 | Node stands and where it wants to be in the future. 20 | 21 | ## Format 22 | 23 | EP documents don't follow a given format (other than being written in 24 | MarkDown). It is, however, required that all EPs include the following 25 | template and information at the top of the file: 26 | 27 | ``` 28 | | Title | Tile of EP | 29 | |--------|-----------------| 30 | | Author | @gihub_handle | 31 | | Status | DRAFT | 32 | | Date | YYYY-MM-DD | 33 | ``` 34 | 35 | The document file name must conform to the format `"XXX-title-ish.md"` 36 | (literally starting with `XXX` and not a self assigned number). At the time the 37 | EP lands it will be assigned a number and added to `000-index.md`. There is no 38 | need for a PR author to add the file to the index since no number has yet been 39 | given. 40 | 41 | Files should follow the convention of keeping lines to a maximum of 80 42 | characters. Exceptions can be made in cases like long URLs or when pasting the 43 | output of an application. For example a stack trace from gdb. 44 | 45 | More information of the "Status" field can be found in 46 | [Progress of an EP](#progress-of-an-ep). 47 | 48 | ## Content 49 | 50 | EP documents should be as detailed as possible. Any type of media which helps 51 | clarify what it tries to describe is more than welcome, be that an ASCII 52 | diagram, pseudocode or actual C code. 53 | 54 | ## Licensing 55 | 56 | All EP documents must be MIT licensed. 57 | 58 | ## Progress of an EP 59 | 60 | All EPs will be committed to the repository regardless of their acceptance. 61 | The initial status shall be **"DRAFT"**. 62 | 63 | If the document is uncontroversial and agreement is reached quickly it might be 64 | committed directly with the **"ACCEPTED"** status. Likewise, if the proposal is 65 | rejected the status shall be **"REJECTED"**. When a document is rejected a 66 | member of the core team should append a section describing the reasons for 67 | rejection. 68 | 69 | A document shall also be committed in **"DRAFT"** status. This means consensus 70 | has not been reached yet. 71 | 72 | The author of an EP is expected to actually pursue and implement the proposal. 73 | --------------------------------------------------------------------------------