From c122a25c9bf0492d5aceb791c53a86dfc1a95f42 Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Mon, 13 Jul 2026 22:20:11 +1000 Subject: [PATCH 01/48] Add assert.match, doesNotMatch, and partialDeepStrictEqual Bind newer Node.js assert APIs for regex string matching and partial deep equality. Co-authored-by: Cursor --- src/js/node/Assert.hx | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/js/node/Assert.hx b/src/js/node/Assert.hx index 48df60f2..4d894b4c 100644 --- a/src/js/node/Assert.hx +++ b/src/js/node/Assert.hx @@ -76,6 +76,22 @@ extern class Assert { @:overload(function(actual:T, expected:T, ?message:Error):Void {}) static function deepStrictEqual(actual:T, expected:T, ?message:String):Void; + /** + Expects the `string` input not to match the regular expression. + + @see https://nodejs.org/api/assert.html#assert_assert_doesnotmatch_string_regexp_message + **/ + @:overload(function(string:String, regexp:RegExp, ?message:Error):Void {}) + static function doesNotMatch(string:String, regexp:RegExp, ?message:String):Void; + + /** + Expects the `string` input not to match the regular expression. + + @see https://nodejs.org/api/assert.html#assert_assert_doesnotmatch_string_regexp_message + **/ + @:overload(function(string:String, regexp:RegExp, ?message:Error):Void {}) + static function doesNotMatch(string:String, regexp:RegExp, ?message:String):Void; + /** Awaits the `asyncFn` promise or, if `asyncFn` is a function, immediately calls the function and awaits the returned promise to complete. @@ -142,6 +158,14 @@ extern class Assert { **/ static function ifError(value:Dynamic):Void; + /** + Expects the `string` input to match the regular expression. + + @see https://nodejs.org/api/assert.html#assert_assert_match_string_regexp_message + **/ + @:overload(function(string:String, regexp:RegExp, ?message:Error):Void {}) + static function match(string:String, regexp:RegExp, ?message:String):Void; + /** An alias of `Assert.notDeepStrictEqual()`. @@ -185,6 +209,16 @@ extern class Assert { @:overload(function(value:Dynamic, ?message:Error):Void {}) static function ok(value:Dynamic, ?message:String):Void; + /** + Tests for partial deep equality between the `actual` and `expected` parameters. + "Partial" equality means that only properties that exist on the `expected` + parameter are compared. + + @see https://nodejs.org/api/assert.html#assert_assert_partialdeepstrictequal_actual_expected_message + **/ + @:overload(function(actual:T, expected:T, ?message:Error):Void {}) + static function partialDeepStrictEqual(actual:T, expected:T, ?message:String):Void; + /** Awaits the `asyncFn` promise or, if `asyncFn` is a function, immediately calls the function and awaits the returned promise to complete. From c61d3ad59912e6ade66058a005abc3eccebbd0d1 Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Mon, 13 Jul 2026 22:20:15 +1000 Subject: [PATCH 02/48] Add zlib Brotli compress/decompress APIs Expose Node.js Brotli stream classes, create helpers, sync/async buffer APIs, and crc32. Co-authored-by: Cursor --- src/js/node/Zlib.hx | 74 +++++++++++++++++++++++++++- src/js/node/zlib/BrotliCompress.hx | 29 +++++++++++ src/js/node/zlib/BrotliDecompress.hx | 29 +++++++++++ 3 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 src/js/node/zlib/BrotliCompress.hx create mode 100644 src/js/node/zlib/BrotliDecompress.hx diff --git a/src/js/node/Zlib.hx b/src/js/node/Zlib.hx index 4790db59..b14eab92 100644 --- a/src/js/node/Zlib.hx +++ b/src/js/node/Zlib.hx @@ -22,6 +22,7 @@ package js.node; +import haxe.DynamicAccess; import haxe.extern.EitherType; import js.node.Buffer; import js.node.zlib.*; @@ -66,8 +67,39 @@ typedef ZlibOptions = { } /** - This provides bindings to Gzip/Gunzip, Deflate/Inflate, and DeflateRaw/InflateRaw classes. - Each class takes the same options, and is a readable/writable Stream. + Options for Brotli compression and decompression. +**/ +typedef BrotliOptions = { + /** + default: `zlib.constants.BROTLI_OPERATION_PROCESS` + **/ + @:optional var flush:Int; + + /** + default: `zlib.constants.BROTLI_OPERATION_FINISH` + **/ + @:optional var finishFlush:Int; + + /** + default: 16*1024 + **/ + @:optional var chunkSize:Int; + + /** + Key-value object containing indexed Brotli parameters. + **/ + @:optional var params:DynamicAccess; + + /** + The maximum length of the output that can be produced by zlib streams. + **/ + @:optional var maxOutputLength:Int; +} + +/** + This provides bindings to Gzip/Gunzip, Deflate/Inflate, DeflateRaw/InflateRaw, + and BrotliCompress/BrotliDecompress classes. + Each class takes options, and is a readable/writable Stream. **/ @:jsRequire("zlib") extern class Zlib { @@ -171,6 +203,22 @@ extern class Zlib { **/ static function createUnzip(?options:ZlibOptions):Unzip; + /** + Returns a new `BrotliCompress` object with an `options`. + **/ + static function createBrotliCompress(?options:BrotliOptions):BrotliCompress; + + /** + Returns a new `BrotliDecompress` object with an `options`. + **/ + static function createBrotliDecompress(?options:BrotliOptions):BrotliDecompress; + + /** + Computes a 32-bit Cyclic Redundancy Check checksum of `data`. + If `value` is given, it is used as the starting value of the checksum. + **/ + static function crc32(data:EitherType, ?value:Int):Int; + /** Compress a string with `Deflate`. **/ @@ -247,4 +295,26 @@ extern class Zlib { Decompress a raw Buffer with `Unzip` (synchronous version). **/ static function unzipSync(buf:EitherType, ?options:ZlibOptions):Buffer; + + /** + Compress a string with `BrotliCompress`. + **/ + @:overload(function(buf:EitherType, options:BrotliOptions, callback:Error->Buffer->Void):Void {}) + static function brotliCompress(buf:EitherType, callback:Error->Buffer->Void):Void; + + /** + Compress a string with `BrotliCompress` (synchronous version). + **/ + static function brotliCompressSync(buf:EitherType, ?options:BrotliOptions):Buffer; + + /** + Decompress a Buffer with `BrotliDecompress`. + **/ + @:overload(function(buf:EitherType, options:BrotliOptions, callback:Error->Buffer->Void):Void {}) + static function brotliDecompress(buf:EitherType, callback:Error->Buffer->Void):Void; + + /** + Decompress a Buffer with `BrotliDecompress` (synchronous version). + **/ + static function brotliDecompressSync(buf:EitherType, ?options:BrotliOptions):Buffer; } diff --git a/src/js/node/zlib/BrotliCompress.hx b/src/js/node/zlib/BrotliCompress.hx new file mode 100644 index 00000000..a93450f3 --- /dev/null +++ b/src/js/node/zlib/BrotliCompress.hx @@ -0,0 +1,29 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.zlib; + +/** + Compress data using the Brotli algorithm. +**/ +@:jsRequire("zlib", "BrotliCompress") +extern class BrotliCompress extends Zlib {} diff --git a/src/js/node/zlib/BrotliDecompress.hx b/src/js/node/zlib/BrotliDecompress.hx new file mode 100644 index 00000000..d33d6441 --- /dev/null +++ b/src/js/node/zlib/BrotliDecompress.hx @@ -0,0 +1,29 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.zlib; + +/** + Decompress a Brotli stream. +**/ +@:jsRequire("zlib", "BrotliDecompress") +extern class BrotliDecompress extends Zlib {} From 6b6fb173c77e86edf180f508745b6cb86bd2d422 Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Mon, 13 Jul 2026 22:21:08 +1000 Subject: [PATCH 03/48] Remove duplicate doesNotMatch declaration Co-authored-by: Cursor --- src/js/node/Assert.hx | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/js/node/Assert.hx b/src/js/node/Assert.hx index 4d894b4c..987df780 100644 --- a/src/js/node/Assert.hx +++ b/src/js/node/Assert.hx @@ -84,14 +84,6 @@ extern class Assert { @:overload(function(string:String, regexp:RegExp, ?message:Error):Void {}) static function doesNotMatch(string:String, regexp:RegExp, ?message:String):Void; - /** - Expects the `string` input not to match the regular expression. - - @see https://nodejs.org/api/assert.html#assert_assert_doesnotmatch_string_regexp_message - **/ - @:overload(function(string:String, regexp:RegExp, ?message:Error):Void {}) - static function doesNotMatch(string:String, regexp:RegExp, ?message:String):Void; - /** Awaits the `asyncFn` promise or, if `asyncFn` is a function, immediately calls the function and awaits the returned promise to complete. From 41de359bb72fe0b7ab646cf1353aaebfbe6dbada Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Mon, 13 Jul 2026 22:21:13 +1000 Subject: [PATCH 04/48] Add stream.finished/helpers and events utilities Expose Node stream.finished, stream type helpers, and events module/EventEmitter statics (errorMonitor, getEventListeners, setMaxListeners, etc.). Co-authored-by: Cursor --- src/js/node/Events.hx | 49 +++++++++++++++++++++++ src/js/node/Stream.hx | 64 ++++++++++++++++++++++++++++++ src/js/node/events/EventEmitter.hx | 47 ++++++++++++++++++++++ 3 files changed, 160 insertions(+) diff --git a/src/js/node/Events.hx b/src/js/node/Events.hx index ad0e8cd9..af646f07 100644 --- a/src/js/node/Events.hx +++ b/src/js/node/Events.hx @@ -23,9 +23,11 @@ package js.node; import haxe.Constraints.Function; +import haxe.extern.Rest; import js.node.events.EventEmitter; #if haxe4 import js.lib.Promise; +import js.lib.Symbol; #else import js.Promise; #end @@ -39,6 +41,30 @@ import js.Promise; */ @:jsRequire("events") extern class Events { + /** + This symbol shall be used to install a listener for only monitoring `'error'` + events. Listeners installed using this symbol are called before the regular + `'error'` listeners are called. + + @see https://nodejs.org/api/events.html#eventserrormonitor + **/ + #if haxe4 + static final errorMonitor:Symbol; + #else + static var errorMonitor(default, never):Dynamic; + #end + + /** + Value: `Symbol.for('nodejs.rejection')` + + @see https://nodejs.org/api/events.html#eventscapturerejectionsymbol + **/ + #if haxe4 + static final captureRejectionSymbol:Symbol; + #else + static var captureRejectionSymbol(default, never):Dynamic; + #end + /** Creates a `Promise` that is resolved when the `EventEmitter` emits the given event or that is rejected when the `EventEmitter` emits `'error'`. @@ -48,4 +74,27 @@ extern class Events { @see https://nodejs.org/api/events.html#events_events_once_emitter_name **/ static function once(emitter:IEventEmitter, name:Event):Promise>; + + /** + Returns a copy of the array of listeners for the event named `name`. + + @see https://nodejs.org/api/events.html#eventsgeteventlistenersemitter-name + **/ + static function getEventListeners(emitter:IEventEmitter, name:Event):Array; + + /** + Change the default `maxListeners` value for all `EventEmitter` instances, + and optionally apply that change to the given emitters. + + @see https://nodejs.org/api/events.html#eventssetmaxlistenersn-eventtargets + **/ + static function setMaxListeners(n:Int, emitters:Rest):Void; + + /** + A class method that returns the number of listeners for the given `eventName` + registered on the given `emitter`. + + @see https://nodejs.org/api/events.html#eventsemitterlistenercountemitter-eventname + **/ + static function listenerCount(emitter:IEventEmitter, eventName:Event):Int; } diff --git a/src/js/node/Stream.hx b/src/js/node/Stream.hx index 2aa7631b..d3027427 100644 --- a/src/js/node/Stream.hx +++ b/src/js/node/Stream.hx @@ -62,6 +62,70 @@ extern class Stream> extends EventEmitter implements @:overload(function(readable:IReadable, writable1:IWritable, writable2:IWritable, writable3:IWritable, writable4:IWritable, writable5:IWritable, writable6:IWritable, writable7:IWritable, writable8:IWritable, callback:Null->Void):Void {}) static function pipeline(readable:IReadable, streams:Rest):Promise; + + /** + A module method to wait for a readable or writable stream to finish. + + @see https://nodejs.org/api/stream.html#streamfinishedstream-options-callback + **/ + @:overload(function(stream:IStream, callback:Null->Void):Void {}) + @:overload(function(stream:IStream, options:StreamFinishedOptions, callback:Null->Void):Void {}) + static function finished(stream:IStream, ?options:StreamFinishedOptions):Promise; + + /** + Returns whether the stream is readable. + + @see https://nodejs.org/api/stream.html#streamisreadablestream + **/ + static function isReadable(stream:Dynamic):Bool; + + /** + Returns whether the stream is writable. + + @see https://nodejs.org/api/stream.html#streamiswritablestream + **/ + static function isWritable(stream:Dynamic):Bool; + + /** + Returns whether the stream has been destroyed. + + @see https://nodejs.org/api/stream.html#streamisdestroyedstream + **/ + static function isDestroyed(stream:Dynamic):Bool; + + /** + Returns whether the stream has been read from or written to. + + @see https://nodejs.org/api/stream.html#streamisdisturbedstream + **/ + static function isDisturbed(stream:Dynamic):Bool; + + /** + Returns whether the stream has encountered an error. + + @see https://nodejs.org/api/stream.html#streamiserroredstream + **/ + static function isErrored(stream:Dynamic):Bool; +} + +/** + Options for `Stream.finished`. +**/ +typedef StreamFinishedOptions = { + /** + If `true`, emit `'error'` as an `'error'` event instead of rejecting the Promise / calling the callback. + **/ + @:optional var error:Bool; + + /** + Makes `finished()` wait until the stream ends before calling the callback / resolving the Promise if the stream is readable. + **/ + @:optional var readable:Bool; + + /** + Makes `finished()` wait until the stream ends before calling the callback / resolving the Promise if the stream is writable. + **/ + @:optional var writable:Bool; } /** diff --git a/src/js/node/events/EventEmitter.hx b/src/js/node/events/EventEmitter.hx index 5ba67045..4195fac1 100644 --- a/src/js/node/events/EventEmitter.hx +++ b/src/js/node/events/EventEmitter.hx @@ -78,6 +78,53 @@ extern class EventEmitter> implements IEventEmitter { **/ static var defaultMaxListeners:Int; + /** + This symbol shall be used to install a listener for only monitoring `'error'` + events. Listeners installed using this symbol are called before the regular + `'error'` listeners are called. + + @see https://nodejs.org/api/events.html#eventserrormonitor + **/ + #if haxe4 + static final errorMonitor:Symbol; + #else + static var errorMonitor(default, never):Dynamic; + #end + + /** + Value: `Symbol.for('nodejs.rejection')` + + @see https://nodejs.org/api/events.html#eventscapturerejectionsymbol + **/ + #if haxe4 + static final captureRejectionSymbol:Symbol; + #else + static var captureRejectionSymbol(default, never):Dynamic; + #end + + /** + Returns a copy of the array of listeners for the event named `name`. + + @see https://nodejs.org/api/events.html#eventsgeteventlistenersemitter-name + **/ + static function getEventListeners(emitter:IEventEmitter, name:Event):Array; + + /** + Change the default `maxListeners` value for all `EventEmitter` instances, + and optionally apply that change to the given emitters. + + @see https://nodejs.org/api/events.html#eventssetmaxlistenersn-eventtargets + **/ + static function setMaxListeners(n:Int, emitters:Rest):Void; + + /** + A class method that returns the number of listeners for the given `eventName` + registered on the given `emitter`. + + @see https://nodejs.org/api/events.html#eventsemitterlistenercountemitter-eventname + **/ + static function listenerCount(emitter:IEventEmitter, eventName:Event):Int; + /** Alias for `emitter.on(eventName, listener)`. From b3e624ae7db51e249ede39e3e45f51eaf4437853 Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Mon, 13 Jul 2026 22:21:54 +1000 Subject: [PATCH 05/48] Add missing fs LTS APIs (copyFile, rm, cp, glob, etc.). Bring hxnodejs fs externs closer to Node LTS for common sync/callback filesystem helpers. Co-authored-by: Cursor --- src/js/node/Fs.hx | 358 ++++++++++++++++++++++++++++++++++++++ src/js/node/fs/Dir.hx | 68 ++++++++ src/js/node/fs/Dirent.hx | 79 +++++++++ src/js/node/fs/StatsFs.hx | 70 ++++++++ 4 files changed, 575 insertions(+) create mode 100644 src/js/node/fs/Dir.hx create mode 100644 src/js/node/fs/Dirent.hx create mode 100644 src/js/node/fs/StatsFs.hx diff --git a/src/js/node/Fs.hx b/src/js/node/Fs.hx index a353a428..3fe5c490 100644 --- a/src/js/node/Fs.hx +++ b/src/js/node/Fs.hx @@ -25,9 +25,12 @@ package js.node; import haxe.DynamicAccess; import haxe.extern.EitherType; import js.node.Buffer; +import js.node.fs.Dir; +import js.node.fs.Dirent; import js.node.fs.FSWatcher; import js.node.fs.ReadStream; import js.node.fs.Stats; +import js.node.fs.StatsFs; import js.node.fs.WriteStream; #if haxe4 import js.lib.Error; @@ -463,6 +466,26 @@ typedef FsConstants = { File mode indicating executable by others. **/ var S_IXOTH:Int; + + /** + Constant for `Fs.copyFile`. + If present, the copy operation will fail with an error if the destination path already exists. + **/ + var COPYFILE_EXCL:Int; + + /** + Constant for `Fs.copyFile`. + If present, the copy operation will attempt to create a copy-on-write reflink. + If the underlying platform does not support copy-on-write, then a fallback copy mechanism is used. + **/ + var COPYFILE_FICLONE:Int; + + /** + Constant for `Fs.copyFile`. + If present, the copy operation will attempt to create a copy-on-write reflink. + If the underlying platform does not support copy-on-write, then the operation will fail with an error. + **/ + var COPYFILE_FICLONE_FORCE:Int; } /** @@ -491,6 +514,159 @@ typedef FsRmdirOptions = { @:optional var retryDelay:Int; } +/** + Options for `Fs.rm` and `Fs.rmSync`. +**/ +typedef FsRmOptions = { + /** + When `true`, exceptions will be ignored if `path` does not exist. + Default: `false`. + **/ + @:optional var force:Bool; + + /** + If an `EBUSY`, `EMFILE`, `ENFILE`, `ENOTEMPTY`, or `EPERM` error is encountered, + Node.js will retry the operation with a linear backoff wait of `retryDelay` ms longer on each try. + This option represents the number of retries. + This option is ignored if the `recursive` option is not `true`. + Default: `0`. + **/ + @:optional var maxRetries:Int; + + /** + If `true`, perform a recursive removal. + In recursive mode, operations are retried on failure. + Default: `false`. + **/ + @:optional var recursive:Bool; + + /** + The amount of time in milliseconds to wait between retries. + This option is ignored if the `recursive` option is not `true`. + Default: `100`. + **/ + @:optional var retryDelay:Int; +} + +/** + Options for `Fs.cp` and `Fs.cpSync`. +**/ +typedef FsCpOptions = { + /** + Dereference symlinks. + Default: `false`. + **/ + @:optional var dereference:Bool; + + /** + When `force` is `false`, and the destination exists, throw an error. + Default: `false`. + **/ + @:optional var errorOnExist:Bool; + + /** + Function to filter copied files/directories. + Return `true` to copy the item, `false` to ignore it. + When ignoring a directory, all of its contents will be skipped as well. + **/ + @:optional var filter:String->String->Bool; + + /** + Overwrite existing file or directory. + The copy operation will ignore errors if you set this to `false` and the destination exists. + Use the `errorOnExist` option to change this behavior. + Default: `true`. + **/ + @:optional var force:Bool; + + /** + Modifiers for copy operation. + Default: `0`. + See `mode` flag of `Fs.copyFile`. + **/ + @:optional var mode:Int; + + /** + When `true`, timestamps from `src` will be preserved. + Default: `false`. + **/ + @:optional var preserveTimestamps:Bool; + + /** + Copy directories recursively. + Default: `false`. + **/ + @:optional var recursive:Bool; + + /** + When `true`, path resolution for symlinks will be skipped. + Default: `false`. + **/ + @:optional var verbatimSymlinks:Bool; +} + +/** + Options for `Fs.opendir` and `Fs.opendirSync`. +**/ +typedef FsOpendirOptions = { + /** + Encoding for the path while opening the directory and subsequent read operations. + Default: `'utf8'`. + **/ + @:optional var encoding:String; + + /** + Number of directory entries that are buffered internally when reading from the directory. + Higher values lead to better performance but higher memory usage. + Default: `32`. + **/ + @:optional var bufferSize:Int; + + /** + When `true`, reads the directory recursively. + Default: `false`. + **/ + @:optional var recursive:Bool; +} + +/** + Options for `Fs.glob` and `Fs.globSync`. +**/ +typedef FsGlobOptions = { + /** + Current working directory. + Default: `process.cwd()`. + **/ + @:optional var cwd:String; + + /** + Function to filter out files/directories, or a list of glob patterns to be excluded. + If a function is provided, return `true` to exclude the item, `false` to include it. + **/ + @:optional var exclude:EitherTypeBool, Array>; + + /** + When `true`, symbolic links to directories are followed while expanding `**` patterns. + Default: `false`. + **/ + @:optional var followSymlinks:Bool; + + /** + `true` if the glob should return paths as `Dirent`s, `false` otherwise. + Default: `false`. + **/ + @:optional var withFileTypes:Bool; +} + +/** + A buffer descriptor for `Fs.readv` / `Fs.writev`. +**/ +typedef FsVectorBuffer = EitherType; + /** File I/O is provided by simple wrappers around standard POSIX functions. All the methods have asynchronous and synchronous forms. @@ -521,6 +697,45 @@ extern class Fs { **/ static function renameSync(oldPath:FsPath, newPath:FsPath):Void; + /** + Asynchronously copies `src` to `dest`. + By default, `dest` is overwritten if it already exists. + + No arguments other than a possible exception are given to the callback function. + + `mode` is an optional integer that specifies the behavior of the copy operation. + It is possible to create a mask consisting of the bitwise OR of two or more values + (e.g. `Fs.constants.COPYFILE_EXCL | Fs.constants.COPYFILE_FICLONE`). + **/ + @:overload(function(src:FsPath, dest:FsPath, callback:Error->Void):Void {}) + static function copyFile(src:FsPath, dest:FsPath, mode:Int, callback:Error->Void):Void; + + /** + Synchronously copies `src` to `dest`. + By default, `dest` is overwritten if it already exists. + + `mode` is an optional integer that specifies the behavior of the copy operation. + See `copyFile` for details. + **/ + @:overload(function(src:FsPath, dest:FsPath):Void {}) + static function copyFileSync(src:FsPath, dest:FsPath, mode:Int):Void; + + /** + Asynchronously copies the entire directory structure from `src` to `dest`, + including subdirectories and files. + + When copying a directory to another directory, globs are not supported and + behavior is similar to `cp dir1/ dir2/`. + **/ + @:overload(function(src:FsPath, dest:FsPath, callback:Error->Void):Void {}) + static function cp(src:FsPath, dest:FsPath, options:FsCpOptions, callback:Error->Void):Void; + + /** + Synchronously copies the entire directory structure from `src` to `dest`, + including subdirectories and files. + **/ + static function cpSync(src:FsPath, dest:FsPath, ?options:FsCpOptions):Void; + /** Asynchronous ftruncate(2). **/ @@ -638,6 +853,20 @@ extern class Fs { **/ static function fstatSync(fd:Int):Stats; + /** + Asynchronous statfs(2). + Returns information about the mounted file system which contains `path`. + + The callback gets two arguments `(err, stats)` where `stats` is a `StatsFs` object. + **/ + static function statfs(path:FsPath, callback:Error->StatsFs->Void):Void; + + /** + Synchronous statfs(2). + Returns information about the mounted file system which contains `path`. + **/ + static function statfsSync(path:FsPath):StatsFs; + /** Asynchronous link(2). **/ @@ -706,6 +935,22 @@ extern class Fs { **/ static function unlinkSync(path:FsPath):Void; + /** + Asynchronously removes files and directories (modeled on the standard POSIX `rm` utility). + + No arguments other than a possible exception are given to the completion callback. + + To get a behavior similar to the `rm -rf` Unix command, use `Fs.rm` with options + `{ recursive: true, force: true }`. + **/ + @:overload(function(path:FsPath, callback:Error->Void):Void {}) + static function rm(path:FsPath, options:FsRmOptions, callback:Error->Void):Void; + + /** + Synchronously removes files and directories (modeled on the standard POSIX `rm` utility). + **/ + static function rmSync(path:FsPath, ?options:FsRmOptions):Void; + /** Asynchronous rmdir(2). **/ @@ -760,6 +1005,55 @@ extern class Fs { **/ static function readdirSync(path:FsPath):Array; + /** + Asynchronously open a directory for iterative scanning. + See the POSIX opendir(3) documentation for more details. + + Creates a `Dir`, which contains all further functions for reading from + and cleaning up the directory. + + The `callback` gets two arguments `(err, dir)`. + **/ + @:overload(function(path:FsPath, callback:Error->Dir->Void):Void {}) + static function opendir(path:FsPath, options:FsOpendirOptions, callback:Error->Dir->Void):Void; + + /** + Synchronously open a directory. + See `opendir` for more details. + **/ + static function opendirSync(path:FsPath, ?options:FsOpendirOptions):Dir; + + /** + Retrieves the files matching the specified pattern. + + The `callback` gets two arguments `(err, matches)`. + When `options.withFileTypes` is `true`, `matches` is an array of `Dirent` objects; + otherwise it is an array of path strings. + **/ + @:overload(function(pattern:EitherType>, callback:Error->Array->Void):Void {}) + @:overload(function(pattern:EitherType>, options:FsGlobOptions, callback:Error->Array->Void):Void {}) + static function glob(pattern:EitherType>, options:{ + ?cwd:String, + ?exclude:EitherTypeBool, Array>, + ?followSymlinks:Bool, + withFileTypes:Bool + }, callback:Error->Array->Void):Void; + + /** + Synchronously retrieves the files matching the specified pattern. + + When `options.withFileTypes` is `true`, returns an array of `Dirent` objects; + otherwise returns an array of path strings. + **/ + @:overload(function(pattern:EitherType>):Array {}) + @:overload(function(pattern:EitherType>, options:FsGlobOptions):Array {}) + static function globSync(pattern:EitherType>, options:{ + ?cwd:String, + ?exclude:EitherTypeBool, Array>, + ?followSymlinks:Bool, + withFileTypes:Bool + }):Array; + /** Asynchronous close(2). **/ @@ -809,6 +1103,20 @@ extern class Fs { **/ static function futimesSync(fd:Int, atime:Date, mtime:Date):Void; + /** + Change the file system timestamps of the symbolic link referenced by `path`. + + Same as `utimes`, except that if the path refers to a symbolic link, + then the link is not dereferenced: instead, the timestamps of the symbolic link itself are changed. + **/ + static function lutimes(path:FsPath, atime:Date, mtime:Date, callback:Error->Void):Void; + + /** + Change the file system timestamps of the symbolic link referenced by `path`. + This is the synchronous version of `lutimes`. + **/ + static function lutimesSync(path:FsPath, atime:Date, mtime:Date):Void; + /** Asynchronous fsync(2). **/ @@ -819,6 +1127,22 @@ extern class Fs { **/ static function fsyncSync(fd:Int):Void; + /** + Asynchronous fdatasync(2). + + Forces all currently queued I/O operations associated with the file to the + operating system's synchronized I/O completion state. + Refer to the POSIX fdatasync(2) documentation for details. + + No arguments other than a possible exception are given to the completion callback. + **/ + static function fdatasync(fd:Int, callback:Error->Void):Void; + + /** + Synchronous fdatasync(2). + **/ + static function fdatasyncSync(fd:Int):Void; + /** Documentation for the overloads with the `buffer` argument: @@ -874,6 +1198,23 @@ extern class Fs { @:overload(function(fd:Int, data:Dynamic, ?position:Int):Int {}) static function writeSync(fd:Int, buffer:Buffer, offset:Int, length:Int, ?position:Int):Int; + /** + Write an array of buffers to the file specified by `fd` using writev(). + + `position` is the offset from the beginning of the file where this data should be written. + If `position` is not a number, the data will be written at the current position. + + The callback will be given three arguments: `(err, bytesWritten, buffers)`. + `bytesWritten` is how many bytes were written from `buffers`. + **/ + @:overload(function(fd:Int, buffers:Array, callback:Error->Int->Array->Void):Void {}) + static function writev(fd:Int, buffers:Array, position:Int, callback:Error->Int->Array->Void):Void; + + /** + Synchronous version of `writev`. Returns the number of bytes written. + **/ + static function writevSync(fd:Int, buffers:Array, ?position:Int):Int; + /** Read data from the file specified by `fd`. @@ -895,6 +1236,23 @@ extern class Fs { **/ static function readSync(fd:Int, buffer:Buffer, offset:Int, length:Int, position:Null):Int; + /** + Read from a file specified by `fd` and write to an array of buffers using readv(). + + `position` is the offset from the beginning of the file from where data should be read. + If `position` is not a number, the data will be read from the current position. + + The callback will be given three arguments: `(err, bytesRead, buffers)`. + `bytesRead` is how many bytes were read from the file. + **/ + @:overload(function(fd:Int, buffers:Array, callback:Error->Int->Array->Void):Void {}) + static function readv(fd:Int, buffers:Array, position:Int, callback:Error->Int->Array->Void):Void; + + /** + Synchronous version of `readv`. Returns the number of bytes read. + **/ + static function readvSync(fd:Int, buffers:Array, ?position:Int):Int; + /** Asynchronously reads the entire contents of a file. diff --git a/src/js/node/fs/Dir.hx b/src/js/node/fs/Dir.hx new file mode 100644 index 00000000..28f76116 --- /dev/null +++ b/src/js/node/fs/Dir.hx @@ -0,0 +1,68 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.fs; + +#if haxe4 +import js.lib.Error; +#else +import js.Error; +#end + +/** + A class representing a directory stream. + + Created by `Fs.opendir` or `Fs.opendirSync`. +**/ +@:jsRequire("fs", "Dir") +extern class Dir { + /** + The read-only path of this directory as was provided to `Fs.opendir` / `Fs.opendirSync`. + **/ + var path(default, null):String; + + /** + Asynchronously close the directory's underlying resource handle. + Subsequent reads will result in errors. + **/ + function close(callback:Error->Void):Void; + + /** + Synchronously close the directory's underlying resource handle. + Subsequent reads will result in errors. + **/ + function closeSync():Void; + + /** + Asynchronously read the next directory entry via readdir(3) as a `Dirent`. + + The callback is called with a `Dirent`, or `null` if there are no more directory entries to read. + **/ + function read(callback:Error->Null->Void):Void; + + /** + Synchronously read the next directory entry as a `Dirent`. + + If there are no more directory entries to read, `null` will be returned. + **/ + function readSync():Null; +} diff --git a/src/js/node/fs/Dirent.hx b/src/js/node/fs/Dirent.hx new file mode 100644 index 00000000..e328164e --- /dev/null +++ b/src/js/node/fs/Dirent.hx @@ -0,0 +1,79 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.fs; + +import haxe.extern.EitherType; +import js.node.Buffer; + +/** + A representation of a directory entry, which can be a file or a subdirectory + within the directory, as returned by reading from an `Dir` or from + `Fs.readdir` / `Fs.glob` when `withFileTypes` is `true`. +**/ +extern class Dirent { + /** + The file name that this `Dirent` object refers to. + The type of this value is determined by the `encoding` option. + **/ + var name:EitherType; + + /** + The path to the parent directory of the file this `Dirent` object refers to. + **/ + var parentPath:String; + + /** + Returns `true` if this `Dirent` describes a regular file. + **/ + function isFile():Bool; + + /** + Returns `true` if this `Dirent` describes a file system directory. + **/ + function isDirectory():Bool; + + /** + Returns `true` if this `Dirent` describes a block device. + **/ + function isBlockDevice():Bool; + + /** + Returns `true` if this `Dirent` describes a character device. + **/ + function isCharacterDevice():Bool; + + /** + Returns `true` if this `Dirent` describes a symbolic link. + **/ + function isSymbolicLink():Bool; + + /** + Returns `true` if this `Dirent` describes a first-in-first-out (FIFO) pipe. + **/ + function isFIFO():Bool; + + /** + Returns `true` if this `Dirent` describes a socket. + **/ + function isSocket():Bool; +} diff --git a/src/js/node/fs/StatsFs.hx b/src/js/node/fs/StatsFs.hx new file mode 100644 index 00000000..250b9978 --- /dev/null +++ b/src/js/node/fs/StatsFs.hx @@ -0,0 +1,70 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.fs; + +/** + Provides information about a mounted file system. + + Objects returned from `Fs.statfs` and `Fs.statfsSync` are of this type. +**/ +extern class StatsFs { + /** + Type of file system. + **/ + var type:Float; + + /** + Optimal transfer block size. + **/ + var bsize:Int; + + /** + Fragment size. + **/ + var frsize:Int; + + /** + Total data blocks in file system. + **/ + var blocks:Float; + + /** + Free blocks in file system. + **/ + var bfree:Float; + + /** + Free blocks available to unprivileged users. + **/ + var bavail:Float; + + /** + Total file nodes in file system. + **/ + var files:Float; + + /** + Free file nodes in file system. + **/ + var ffree:Float; +} From 4d745a2aad8e336896c620eb69d97c50ac66ced8 Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Mon, 13 Jul 2026 22:22:11 +1000 Subject: [PATCH 06/48] Add net BlockList/SocketAddress, buffer utils, cluster aliases Expose newer Node.js net blocklist/address APIs, buffer module helpers, and cluster primary aliases. Co-authored-by: Cursor --- src/js/node/Cluster.hx | 12 ++++++ src/js/node/Net.hx | 44 ++++++++++++++++++++ src/js/node/buffer/Buffer.hx | 48 ++++++++++++++++++++++ src/js/node/net/BlockList.hx | 70 ++++++++++++++++++++++++++++++++ src/js/node/net/SocketAddress.hx | 65 +++++++++++++++++++++++++++++ 5 files changed, 239 insertions(+) create mode 100644 src/js/node/net/BlockList.hx create mode 100644 src/js/node/net/SocketAddress.hx diff --git a/src/js/node/Cluster.hx b/src/js/node/Cluster.hx index 3673863d..a81059ad 100644 --- a/src/js/node/Cluster.hx +++ b/src/js/node/Cluster.hx @@ -168,6 +168,13 @@ extern class Cluster extends EventEmitter { **/ var isMaster(default, null):Bool; + /** + True if the process is a primary (alias of `isMaster`). + This is determined by the process.env.NODE_UNIQUE_ID. + If process.env.NODE_UNIQUE_ID is undefined, then `isPrimary` is true. + **/ + var isPrimary(default, null):Bool; + /** True if the process is not a master (it is the negation of `isMaster`). **/ @@ -189,6 +196,11 @@ extern class Cluster extends EventEmitter { **/ function setupMaster(?settings:{?exec:String, ?args:Array, ?silent:Bool}):Void; + /** + Alias of `setupMaster`. + **/ + function setupPrimary(?settings:{?exec:String, ?args:Array, ?silent:Bool}):Void; + /** Spawn a new worker process. diff --git a/src/js/node/Net.hx b/src/js/node/Net.hx index dc011b82..9ceeca67 100644 --- a/src/js/node/Net.hx +++ b/src/js/node/Net.hx @@ -23,8 +23,10 @@ package js.node; import haxe.extern.EitherType; +import js.node.net.BlockList as BlockListObject; import js.node.net.Server; import js.node.net.Socket; +import js.node.net.SocketAddress as SocketAddressObject; typedef NetCreateServerOptions = { > SocketOptionsBase, @@ -122,4 +124,46 @@ extern class Net { Returns true if input is a version 6 IP address, otherwise returns false. **/ static function isIPv6(input:String):Bool; + + /** + `BlockList` class constructor. + + @see https://nodejs.org/api/net.html#class-netblocklist + **/ + static var BlockList:Class; + + /** + `SocketAddress` class constructor. + + @see https://nodejs.org/api/net.html#class-netsocketaddress + **/ + static var SocketAddress:Class; + + /** + Gets the current default value of the `autoSelectFamily` option of `socket.connect(options)`. + + @see https://nodejs.org/api/net.html#netgetdefaultautoselectfamily + **/ + static function getDefaultAutoSelectFamily():Bool; + + /** + Sets the default value of the `autoSelectFamily` option of `socket.connect(options)`. + + @see https://nodejs.org/api/net.html#netsetdefaultautoselectfamilyvalue + **/ + static function setDefaultAutoSelectFamily(value:Bool):Void; + + /** + Gets the current default value of the `autoSelectFamilyAttemptTimeout` option of `socket.connect(options)`. + + @see https://nodejs.org/api/net.html#netgetdefaultautoselectfamilyattempttimeout + **/ + static function getDefaultAutoSelectFamilyAttemptTimeout():Int; + + /** + Sets the default value of the `autoSelectFamilyAttemptTimeout` option of `socket.connect(options)`. + + @see https://nodejs.org/api/net.html#netsetdefaultautoselectfamilyattempttimeoutvalue + **/ + static function setDefaultAutoSelectFamilyAttemptTimeout(ms:Int):Void; } diff --git a/src/js/node/buffer/Buffer.hx b/src/js/node/buffer/Buffer.hx index c4e8e08b..7622be3f 100644 --- a/src/js/node/buffer/Buffer.hx +++ b/src/js/node/buffer/Buffer.hx @@ -691,6 +691,50 @@ extern class Buffer extends Uint8Array { return BufferModule.transcode(source, fromEnc, toEnc); }; + /** + Returns `true` if the given `input` contains only valid UTF-8-encoded data, `false` otherwise. + + This is a property on the `buffer` module returned by `require('buffer')`, not on the `Buffer` global or a `Buffer` instance. + + @see https://nodejs.org/api/buffer.html#bufferisutf8input + **/ + static inline function isUtf8(input:Uint8Array):Bool { + return BufferModule.isUtf8(input); + } + + /** + Returns `true` if the given `input` contains only valid ASCII-encoded data, `false` otherwise. + + This is a property on the `buffer` module returned by `require('buffer')`, not on the `Buffer` global or a `Buffer` instance. + + @see https://nodejs.org/api/buffer.html#bufferisasciiinput + **/ + static inline function isAscii(input:Uint8Array):Bool { + return BufferModule.isAscii(input); + } + + /** + Decodes a string of Base64-encoded data into bytes, and encodes those bytes into a string using Latin-1 (ISO-8859-1). + + This is a property on the `buffer` module returned by `require('buffer')`, not on the `Buffer` global or a `Buffer` instance. + + @see https://nodejs.org/api/buffer.html#bufferatobdata + **/ + static inline function atob(data:String):String { + return BufferModule.atob(data); + } + + /** + Decodes a string into bytes using Latin-1 (ISO-8859), and encodes those bytes into a string using Base64. + + This is a property on the `buffer` module returned by `require('buffer')`, not on the `Buffer` global or a `Buffer` instance. + + @see https://nodejs.org/api/buffer.html#bufferbtoadata + **/ + static inline function btoa(data:String):String { + return BufferModule.btoa(data); + } + /** `buffer.constants` is a property on the `buffer` module returned by `require('buffer')`, not on the `Buffer` global or a `Buffer` instance. @@ -741,6 +785,10 @@ private extern class BufferModule { static var INSPECT_MAX_BYTES:Int; static var kMaxLength(default, never):Int; static function transcode(source:Uint8Array, fromEnc:String, toEnc:String):Buffer; + static function isUtf8(input:Uint8Array):Bool; + static function isAscii(input:Uint8Array):Bool; + static function atob(data:String):String; + static function btoa(data:String):String; static var constants(default, never):BufferConstants; } diff --git a/src/js/node/net/BlockList.hx b/src/js/node/net/BlockList.hx new file mode 100644 index 00000000..eff4bff6 --- /dev/null +++ b/src/js/node/net/BlockList.hx @@ -0,0 +1,70 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.net; + +import haxe.extern.EitherType; + +/** + The `BlockList` object can be used with some network APIs to specify rules + for disabling inbound or outbound access to specific IP addresses, IP ranges, or IP subnets. +**/ +@:jsRequire("net", "BlockList") +extern class BlockList { + function new(); + + /** + Adds a rule to block the given IP address. + + `type` is either `'ipv4'` or `'ipv6'`. Default: `'ipv4'`. + **/ + function addAddress(address:EitherType, ?type:String):Void; + + /** + Adds a rule to block a range of IP addresses from `start` (inclusive) to `end` (inclusive). + + `type` is either `'ipv4'` or `'ipv6'`. Default: `'ipv4'`. + **/ + function addRange(start:EitherType, end:EitherType, ?type:String):Void; + + /** + Adds a rule to block a range of IP addresses specified as a subnet mask. + + `prefix` is the number of CIDR prefix bits. For IPv4, this must be between 0 and 32. + For IPv6, this must be between 0 and 128. + + `type` is either `'ipv4'` or `'ipv6'`. Default: `'ipv4'`. + **/ + function addSubnet(net:EitherType, prefix:Int, ?type:String):Void; + + /** + Returns `true` if the given IP address matches any of the rules added to the `BlockList`. + + `type` is either `'ipv4'` or `'ipv6'`. Default: `'ipv4'`. + **/ + function check(address:EitherType, ?type:String):Bool; + + /** + The list of rules added to the blocklist. + **/ + var rules(default, null):Array; +} diff --git a/src/js/node/net/SocketAddress.hx b/src/js/node/net/SocketAddress.hx new file mode 100644 index 00000000..525960ee --- /dev/null +++ b/src/js/node/net/SocketAddress.hx @@ -0,0 +1,65 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.net; + +import haxe.extern.EitherType; + +/** + Represents a network endpoint as an IP address and port pair. + + @see https://nodejs.org/api/net.html#class-netsocketaddress +**/ +@:jsRequire("net", "SocketAddress") +extern class SocketAddress { + /** + Creates a new `SocketAddress`. + + @see https://nodejs.org/api/net.html#new-netsocketaddressoptions + **/ + function new(?options:{ + ?address:String, + ?family:EitherType, + ?flowlabel:Int, + ?port:Int + }):Void; + + /** + The network address as either an IPv4 or IPv6 string. + **/ + var address(default, null):String; + + /** + Either `'ipv4'` or `'ipv6'`. + **/ + var family(default, null):String; + + /** + An IP port. + **/ + var port(default, null):Int; + + /** + An IPv6 flow-label used only if `family` is `'ipv6'`. + **/ + var flowlabel(default, null):Int; +} From e097cc6c04a3383718768d644eb9b5adbd6caece Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Mon, 13 Jul 2026 22:22:24 +1000 Subject: [PATCH 07/48] Add Os/Path/Url/Tls LTS constants and helpers Expose newer Node.js APIs: os.availableParallelism/machine/version/devNull, path.matchesGlob, url.urlToHttpOptions, and tls root/default/CA helpers. Co-authored-by: Cursor --- src/js/node/Os.hx | 32 ++++++++++++++++++++++++ src/js/node/Path.hx | 8 ++++++ src/js/node/Tls.hx | 40 +++++++++++++++++++++++++++++ src/js/node/Url.hx | 61 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 141 insertions(+) diff --git a/src/js/node/Os.hx b/src/js/node/Os.hx index c94b9941..512f1a30 100644 --- a/src/js/node/Os.hx +++ b/src/js/node/Os.hx @@ -45,6 +45,14 @@ extern class Os { **/ static function arch():String; + /** + Returns an estimate of the default amount of parallelism a program should use. + Always returns a value greater than zero. + + @see https://nodejs.org/api/os.html#osavailableparallelism + **/ + static function availableParallelism():Int; + /** Returns an object containing commonly used operating system specific constants for error codes, process signals, and so on. The specific constants currently defined are described in OS Constants. @@ -59,6 +67,14 @@ extern class Os { **/ static function cpus():Array; + /** + The platform-specific file path of the null device. + `'\\.\nul'` on Windows and `'/dev/null'` on POSIX. + + @see https://nodejs.org/api/os.html#osdevnull + **/ + static var devNull(default, null):String; + /** The `os.endianness()` method returns a string identifying the endianness of the CPU for which the Node.js binary was compiled. @@ -101,6 +117,13 @@ extern class Os { **/ static function loadavg():Array; + /** + Returns the machine type as a string, such as `arm`, `arm64`, `aarch64`, `mips`, `mips64`, `ppc64`, `ppc64le`, `s390`, `s390x`, `i386`, `i686`, `x86_64`. + + @see https://nodejs.org/api/os.html#osmachine + **/ + static function machine():String; + /** The `os.networkInterfaces()` method returns an object containing only network interfaces that have been assigned a network address. @@ -163,6 +186,15 @@ extern class Os { @see https://nodejs.org/api/os.html#os_os_userinfo_options **/ static function userInfo(?options:{encoding:String}):OsUserInfo; + + /** + Returns a string identifying the kernel version. + + On POSIX systems, the operating system release is determined by calling uname(3). On Windows, `RtlGetVersion()` is used, and if it is not available, `GetVersionExW()` will be used. + + @see https://nodejs.org/api/os.html#osversion + **/ + static function version():String; } /** diff --git a/src/js/node/Path.hx b/src/js/node/Path.hx index f52390fc..f208af82 100644 --- a/src/js/node/Path.hx +++ b/src/js/node/Path.hx @@ -82,6 +82,13 @@ extern class Path { **/ static function join(paths:haxe.extern.Rest):String; + /** + The `path.matchesGlob()` method determines if `path` matches the `pattern`. + + @see https://nodejs.org/api/path.html#pathmatchesglobpath-pattern + **/ + static function matchesGlob(path:String, pattern:String):Bool; + /** The `path.normalize()` method normalizes the given `path`, resolving `'..'` and `'.'` segments. @@ -189,6 +196,7 @@ private typedef PathModule = { function dirname(path:String):String; function basename(path:String, ?ext:String):String; function extname(path:String):String; + function matchesGlob(path:String, pattern:String):Bool; var sep(default, null):String; var delimiter(default, null):String; function parse(pathString:String):PathObject; diff --git a/src/js/node/Tls.hx b/src/js/node/Tls.hx index a1111062..caddd19c 100644 --- a/src/js/node/Tls.hx +++ b/src/js/node/Tls.hx @@ -169,6 +169,38 @@ extern class Tls { **/ static var CLIENT_RENEG_WINDOW:Int; + /** + The default value of the `ciphers` option of `Tls.createSecureContext`. + It can be assigned any of the supported OpenSSL ciphers. + **/ + static var DEFAULT_CIPHERS:String; + + /** + The default named curve to use for ECDH key agreement in a tls server. + The default value is `'auto'`. + **/ + static var DEFAULT_ECDH_CURVE:String; + + /** + The default value of the `maxVersion` option of `Tls.createSecureContext`. + It can be assigned any of the supported TLS protocol versions: + `'TLSv1.3'`, `'TLSv1.2'`, `'TLSv1.1'`, or `'TLSv1'`. + **/ + static var DEFAULT_MAX_VERSION:String; + + /** + The default value of the `minVersion` option of `Tls.createSecureContext`. + It can be assigned any of the supported TLS protocol versions: + `'TLSv1.3'`, `'TLSv1.2'`, `'TLSv1.1'`, or `'TLSv1'`. + **/ + static var DEFAULT_MIN_VERSION:String; + + /** + An immutable array of strings representing the root certificates (in PEM format) + from the bundled Mozilla CA store as supplied by the current Node.js version. + **/ + static var rootCertificates(default, null):Array; + /** Size of slab buffer used by all tls servers and clients. Default: 10 * 1024 * 1024. @@ -176,6 +208,14 @@ extern class Tls { **/ static var SLAB_BUFFER_SIZE:Int; + /** + Returns an array containing the CA certificates from various sources, depending on `type`. + + Valid values for `type` are `'default'`, `'system'`, `'bundled'` and `'extra'`. + Default: `'default'`. + **/ + static function getCACertificates(?type:String):Array; + /** Returns an array with the names of the supported SSL ciphers. **/ diff --git a/src/js/node/Url.hx b/src/js/node/Url.hx index d0f10333..92cbfc2c 100644 --- a/src/js/node/Url.hx +++ b/src/js/node/Url.hx @@ -22,6 +22,7 @@ package js.node; +import haxe.extern.EitherType; import js.node.url.URL; /** @@ -95,6 +96,66 @@ extern class Url { **/ @:deprecated static function resolve(from:String, to:String):String; + + /** + This utility function converts a URL object into an ordinary options object as expected by the `http.request()` and `https.request()` APIs. + + @see https://nodejs.org/api/url.html#urlurltohttpoptionsurl + **/ + static function urlToHttpOptions(url:URL):UrlHttpOptions; +} + +/** + Options object returned by `Url.urlToHttpOptions`, compatible with `http.request` / `https.request`. + + @see https://nodejs.org/api/url.html#urlurltohttpoptionsurl +**/ +typedef UrlHttpOptions = { + /** + Protocol to use. + **/ + @:optional var protocol:String; + + /** + A domain name or IP address of the server to issue the request to. + **/ + @:optional var hostname:String; + + /** + The fragment portion of the URL. + **/ + @:optional var hash:String; + + /** + The serialized query portion of the URL. + **/ + @:optional var search:String; + + /** + The path portion of the URL. + **/ + @:optional var pathname:String; + + /** + Request path. Should include query string if any. + E.g. `'/index.html?page=12'`. + **/ + @:optional var path:String; + + /** + The serialized URL. + **/ + @:optional var href:String; + + /** + Port of remote server. + **/ + @:optional var port:EitherType; + + /** + Basic authentication i.e. `'user:password'` to compute an Authorization header. + **/ + @:optional var auth:String; } typedef UrlFormatOptions = { From a4799466e94e74e4f98b77775c80f26db968aa0f Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Mon, 13 Jul 2026 22:22:39 +1000 Subject: [PATCH 08/48] Expose common crypto RNG, KDF, and FIPS helpers for modern Node.js. These externs were missing from Crypto.hx, so Haxe code could not call randomFill/randomInt/UUID, scrypt/hkdf, timingSafeEqual, or FIPS/prime/cipher-info APIs without untyped workarounds. Co-authored-by: Cursor --- src/js/node/Crypto.hx | 209 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 209 insertions(+) diff --git a/src/js/node/Crypto.hx b/src/js/node/Crypto.hx index 007ee024..81d7ecb7 100644 --- a/src/js/node/Crypto.hx +++ b/src/js/node/Crypto.hx @@ -28,8 +28,10 @@ import js.node.crypto.*; import js.node.crypto.DiffieHellman.IDiffieHellman; import js.node.tls.SecureContext; #if haxe4 +import js.lib.ArrayBufferView; import js.lib.Error; #else +import js.html.ArrayBufferView; import js.Error; #end @@ -92,11 +94,33 @@ extern class Crypto { **/ static var fips:Bool; + /** + Returns `1` if and only if a FIPS compliant crypto provider is currently in use, `0` otherwise. + **/ + static function getFips():Int; + + /** + Enables the FIPS compliant crypto provider in a FIPS-enabled Node.js build. + Throws an error if FIPS mode is not available. + **/ + static function setFips(bool:Bool):Void; + /** Returns an array with the names of the supported ciphers. **/ static function getCiphers():Array; + /** + Returns information about a given cipher. + + Some ciphers accept variable length keys and initialization vectors. + By default, the method will return the default values for these ciphers. + To test if a given key length or IV length is acceptable for a given cipher, + use the `keyLength` and `ivLength` options. + If the given values are unacceptable, `null`/`undefined` will be returned. + **/ + static function getCipherInfo(nameOrNid:EitherType, ?options:CipherInfoOptions):Null; + /** Returns an array with the names of the supported hash algorithms. **/ @@ -222,6 +246,33 @@ extern class Crypto { **/ static function pbkdf2Sync(password:EitherType, salt:EitherType, iterations:Int, keylen:Int, ?digest:String):Buffer; + /** + Provides an asynchronous scrypt implementation. + Scrypt is a password-based key derivation function that is designed to be expensive + computationally and memory-wise in order to make brute-force attacks unrewarding. + **/ + @:overload(function(password:EitherType, salt:EitherType, keylen:Int, options:ScryptOptions, + callback:Error->Buffer->Void):Void {}) + static function scrypt(password:EitherType, salt:EitherType, keylen:Int, callback:Error->Buffer->Void):Void; + + /** + Provides a synchronous scrypt implementation. + **/ + static function scryptSync(password:EitherType, salt:EitherType, keylen:Int, ?options:ScryptOptions):Buffer; + + /** + HKDF is a simple key derivation function defined in RFC 5869. + The given `ikm`, `salt` and `info` are used with the `digest` to derive a key of `keylen` bytes. + **/ + static function hkdf(digest:String, ikm:EitherType, salt:EitherType, info:EitherType, keylen:Int, + callback:Error->Buffer->Void):Void; + + /** + Provides a synchronous HKDF key derivation function as defined in RFC 5869. + **/ + static function hkdfSync(digest:String, ikm:EitherType, salt:EitherType, info:EitherType, + keylen:Int):Buffer; + /** Generates cryptographically strong pseudo-random data. @@ -235,6 +286,61 @@ extern class Crypto { @:overload(function(size:Int, callback:Error->Buffer->Void):Void {}) static function randomBytes(size:Int):Buffer; + /** + This function is similar to `randomBytes` but requires the first argument to be a `Buffer` or `TypedArray` + that will be filled. It also requires that a callback is passed in. + + The supplied callback is invoked with two arguments: `err` and `buf`. + The `buf` argument is a reference to the `buffer` filled with random data. + **/ + @:overload(function(buffer:T, offset:Int, callback:Error->T->Void):Void {}) + @:overload(function(buffer:T, offset:Int, size:Int, callback:Error->T->Void):Void {}) + static function randomFill(buffer:T, callback:Error->T->Void):Void; + + /** + Synchronous version of `randomFill`. Returns the filled buffer. + **/ + @:overload(function(buffer:T, offset:Int):T {}) + @:overload(function(buffer:T, offset:Int, size:Int):T {}) + static function randomFillSync(buffer:T):T; + + /** + Return a random integer `n` such that `min <= n < max`. + This implementation avoids modulo bias. + + The range (`max - min`) must be less than 2^48. `min` and `max` must be safe integers. + + If the callback function is not provided, the random integer is generated synchronously. + **/ + @:overload(function(max:Int, callback:Error->Int->Void):Void {}) + @:overload(function(min:Int, max:Int):Int {}) + @:overload(function(min:Int, max:Int, callback:Error->Int->Void):Void {}) + static function randomInt(max:Int):Int; + + /** + Generates a random RFC 4122 version 4 UUID. + The UUID is generated using a cryptographic pseudorandom number generator. + **/ + static function randomUUID(?options:RandomUUIDOptions):String; + + /** + Compares the underlying bytes that represent the given `Buffer` or `ArrayBufferView` + instances using a constant-time algorithm. + `a` and `b` must have the same byte length. + **/ + static function timingSafeEqual(a:EitherType, b:EitherType):Bool; + + /** + Checks the primality of the `candidate`. + **/ + @:overload(function(candidate:EitherType, options:CheckPrimeOptions, callback:Error->Bool->Void):Void {}) + static function checkPrime(candidate:EitherType, callback:Error->Bool->Void):Void; + + /** + Checks the primality of the `candidate` (synchronous version). + **/ + static function checkPrimeSync(candidate:EitherType, ?options:CheckPrimeOptions):Bool; + /** Decrypts `buffer` with `private_key`. @@ -304,3 +410,106 @@ typedef CryptoKeyOptions = { **/ @:optional var padding:Int; } + +/** + Options for `Crypto.scrypt` and `Crypto.scryptSync`. +**/ +typedef ScryptOptions = { + /** + CPU/memory cost parameter. Must be a power of two greater than one. Default: `16384`. + **/ + @:optional var N:Int; + + /** + Block size parameter. Default: `8`. + **/ + @:optional var r:Int; + + /** + Parallelization parameter. Default: `1`. + **/ + @:optional var p:Int; + + /** + Memory upper bound. It is an error when (approximately) `128 * N * r > maxmem`. + Default: `32 * 1024 * 1024`. + **/ + @:optional var maxmem:Int; +} + +/** + Options for `Crypto.randomUUID`. +**/ +typedef RandomUUIDOptions = { + /** + By default, to improve performance, Node.js generates and caches enough random data + to generate up to 128 random UUIDs. To generate a UUID without using the cache, + set `disableEntropyCache` to `true`. Default: `false`. + **/ + @:optional var disableEntropyCache:Bool; +} + +/** + Options for `Crypto.checkPrime` and `Crypto.checkPrimeSync`. +**/ +typedef CheckPrimeOptions = { + /** + The number of Miller-Rabin probabilistic primality iterations to perform. + When the value is `0`, a number of checks is used that yields a false positive rate + of at most 2^-64 for random input. Default: `0`. + **/ + @:optional var checks:Int; +} + +/** + Options for `Crypto.getCipherInfo`. +**/ +typedef CipherInfoOptions = { + /** + A test key length. + **/ + @:optional var keyLength:Int; + + /** + A test IV length. + **/ + @:optional var ivLength:Int; +} + +/** + Information about a cipher returned by `Crypto.getCipherInfo`. +**/ +typedef CipherInfo = { + /** + The name of the cipher. + **/ + var name:String; + + /** + The nid of the cipher. + **/ + var nid:Int; + + /** + The block size of the cipher in bytes. + This property is omitted when `mode` is `'stream'`. + **/ + @:optional var blockSize:Int; + + /** + The expected or default initialization vector length in bytes. + This property is omitted if the cipher does not use an initialization vector. + **/ + @:optional var ivLength:Int; + + /** + The expected or default key length in bytes. + **/ + @:optional var keyLength:Int; + + /** + The cipher mode. One of `'cbc'`, `'ccm'`, `'cfb'`, `'ctr'`, `'ecb'`, `'gcm'`, + `'ocb'`, `'ofb'`, `'stream'`, `'wrap'`, `'xts'`. + **/ + @:optional var mode:String; +} From 362f92d563d19f25443e24469454dda6dbe7eb90 Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Mon, 13 Jul 2026 22:32:57 +1000 Subject: [PATCH 09/48] Align BrotliOptions with Node.js LTS zlib docs Add the documented `info` option and clarify `maxOutputLength` default. Co-authored-by: Cursor --- src/js/node/Zlib.hx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/js/node/Zlib.hx b/src/js/node/Zlib.hx index b14eab92..0684c4de 100644 --- a/src/js/node/Zlib.hx +++ b/src/js/node/Zlib.hx @@ -92,8 +92,15 @@ typedef BrotliOptions = { /** The maximum length of the output that can be produced by zlib streams. + Default: `buffer.kMaxLength` **/ @:optional var maxOutputLength:Int; + + /** + If `true`, returns an object with `buffer` and `engine`. + Default: `false` + **/ + @:optional var info:Bool; } /** From 68f13587110365111f6bf7c0555a619349a23c86 Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Mon, 13 Jul 2026 22:33:39 +1000 Subject: [PATCH 10/48] Align stream.finished/helpers and events utils with Node 24 LTS Fix finished callback return type and options (signal/cleanup), isReadable/isWritable nullability, document isDestroyed/isDisturbed accurately, add Readable.isDisturbed and events.getMaxListeners. Co-authored-by: Cursor --- src/js/node/Events.hx | 13 +++++++-- src/js/node/Stream.hx | 46 +++++++++++++++++++++++------- src/js/node/events/EventEmitter.hx | 9 +++++- src/js/node/stream/Readable.hx | 7 +++++ 4 files changed, 61 insertions(+), 14 deletions(-) diff --git a/src/js/node/Events.hx b/src/js/node/Events.hx index af646f07..2afe837c 100644 --- a/src/js/node/Events.hx +++ b/src/js/node/Events.hx @@ -91,10 +91,17 @@ extern class Events { static function setMaxListeners(n:Int, emitters:Rest):Void; /** - A class method that returns the number of listeners for the given `eventName` - registered on the given `emitter`. + Returns the currently set max amount of listeners for the given emitter. - @see https://nodejs.org/api/events.html#eventsemitterlistenercountemitter-eventname + @see https://nodejs.org/api/events.html#eventsgetmaxlistenersemitterortarget + **/ + static function getMaxListeners(emitter:IEventEmitter):Int; + + /** + Returns the number of listeners for the given `eventName` registered on the + given `emitter`. + + @see https://nodejs.org/api/events.html#eventslistenercountemitterortarget-eventname **/ static function listenerCount(emitter:IEventEmitter, eventName:Event):Int; } diff --git a/src/js/node/Stream.hx b/src/js/node/Stream.hx index d3027427..cf37cc46 100644 --- a/src/js/node/Stream.hx +++ b/src/js/node/Stream.hx @@ -23,6 +23,7 @@ package js.node; import haxe.extern.Rest; +import js.html.AbortSignal; import js.node.events.EventEmitter; import js.node.stream.Readable.IReadable; import js.node.stream.Writable.IWritable; @@ -66,37 +67,45 @@ extern class Stream> extends EventEmitter implements /** A module method to wait for a readable or writable stream to finish. + The callback form returns a cleanup function that removes registered listeners. + @see https://nodejs.org/api/stream.html#streamfinishedstream-options-callback **/ - @:overload(function(stream:IStream, callback:Null->Void):Void {}) - @:overload(function(stream:IStream, options:StreamFinishedOptions, callback:Null->Void):Void {}) + @:overload(function(stream:IStream, callback:Null->Void):Void->Void {}) + @:overload(function(stream:IStream, options:StreamFinishedOptions, callback:Null->Void):Void->Void {}) static function finished(stream:IStream, ?options:StreamFinishedOptions):Promise; /** Returns whether the stream is readable. + Returns `null` if `stream` is not a valid readable stream. + @see https://nodejs.org/api/stream.html#streamisreadablestream **/ - static function isReadable(stream:Dynamic):Bool; + static function isReadable(stream:Dynamic):Null; /** Returns whether the stream is writable. + Returns `null` if `stream` is not a valid writable stream. + @see https://nodejs.org/api/stream.html#streamiswritablestream **/ - static function isWritable(stream:Dynamic):Bool; + static function isWritable(stream:Dynamic):Null; /** Returns whether the stream has been destroyed. - @see https://nodejs.org/api/stream.html#streamisdestroyedstream + Exported by the `stream` module (undocumented helper; available since Node 16+). **/ static function isDestroyed(stream:Dynamic):Bool; /** - Returns whether the stream has been read from or written to. + Returns whether the stream has been read from or cancelled. - @see https://nodejs.org/api/stream.html#streamisdisturbedstream + Also available as `Readable.isDisturbed`. + + @see https://nodejs.org/api/stream.html#streamreadableisdisturbedstream **/ static function isDisturbed(stream:Dynamic):Bool; @@ -113,19 +122,36 @@ extern class Stream> extends EventEmitter implements **/ typedef StreamFinishedOptions = { /** - If `true`, emit `'error'` as an `'error'` event instead of rejecting the Promise / calling the callback. + If set to `false`, then a call to `emit('error', err)` is not treated as finished. + Default: `true`. **/ @:optional var error:Bool; /** - Makes `finished()` wait until the stream ends before calling the callback / resolving the Promise if the stream is readable. + When set to `false`, the callback / Promise will resolve when the stream ends + even though the stream might still be readable. + Default: `true`. **/ @:optional var readable:Bool; /** - Makes `finished()` wait until the stream ends before calling the callback / resolving the Promise if the stream is writable. + When set to `false`, the callback / Promise will resolve when the stream ends + even though the stream might still be writable. + Default: `true`. **/ @:optional var writable:Bool; + + /** + Allows aborting the wait for the stream to finish. The underlying stream is not + aborted if the signal is aborted. The callback / Promise rejects with an `AbortError`. + **/ + @:optional var signal:AbortSignal; + + /** + If `true`, removes the listeners registered by this function before the Promise is fulfilled. + Only used by the Promise form of `finished`. Default: `false`. + **/ + @:optional var cleanup:Bool; } /** diff --git a/src/js/node/events/EventEmitter.hx b/src/js/node/events/EventEmitter.hx index 4195fac1..786a11cd 100644 --- a/src/js/node/events/EventEmitter.hx +++ b/src/js/node/events/EventEmitter.hx @@ -117,11 +117,18 @@ extern class EventEmitter> implements IEventEmitter { **/ static function setMaxListeners(n:Int, emitters:Rest):Void; + /** + Returns the currently set max amount of listeners for the given emitter. + + @see https://nodejs.org/api/events.html#eventsgetmaxlistenersemitterortarget + **/ + static function getMaxListeners(emitter:IEventEmitter):Int; + /** A class method that returns the number of listeners for the given `eventName` registered on the given `emitter`. - @see https://nodejs.org/api/events.html#eventsemitterlistenercountemitter-eventname + @see https://nodejs.org/api/events.html#eventslistenercountemitterortarget-eventname **/ static function listenerCount(emitter:IEventEmitter, eventName:Event):Int; diff --git a/src/js/node/stream/Readable.hx b/src/js/node/stream/Readable.hx index bce31028..b9a80091 100644 --- a/src/js/node/stream/Readable.hx +++ b/src/js/node/stream/Readable.hx @@ -286,6 +286,13 @@ extern class Readable> extends Stream implements IR // --------- static API -------------------------------------------------- // TODO @:overload(function(iterable:AsyncIterator, ?options:ReadableNewOptions):IReadable {}) static function from(iterable:Iterator, ?options:ReadableNewOptions):IReadable; + + /** + Returns whether the stream has been read from or cancelled. + + @see https://nodejs.org/api/stream.html#streamreadableisdisturbedstream + **/ + static function isDisturbed(stream:Dynamic):Bool; } /** From 50e56c86b633030ecd36914311bca910621a1281 Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Mon, 13 Jul 2026 22:33:51 +1000 Subject: [PATCH 11/48] Align readv/writev buffer typing with Node LTS. Node fs.readv/writev accept ArrayBufferView arrays, not {buffer,offset,length} descriptors; also match StatFs.frsize docs wording. Co-authored-by: Cursor --- src/js/node/Fs.hx | 11 +++++------ src/js/node/fs/StatsFs.hx | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/js/node/Fs.hx b/src/js/node/Fs.hx index 3fe5c490..7b03a79d 100644 --- a/src/js/node/Fs.hx +++ b/src/js/node/Fs.hx @@ -659,13 +659,12 @@ typedef FsGlobOptions = { } /** - A buffer descriptor for `Fs.readv` / `Fs.writev`. + A buffer for `Fs.readv` / `Fs.writev`. + + Node.js accepts an `ArrayBufferView` (Buffer, TypedArray, or DataView). + This library follows the existing `Fs.read` / `Fs.write` convention of typing these as `Buffer`. **/ -typedef FsVectorBuffer = EitherType; +typedef FsVectorBuffer = Buffer; /** File I/O is provided by simple wrappers around standard POSIX functions. diff --git a/src/js/node/fs/StatsFs.hx b/src/js/node/fs/StatsFs.hx index 250b9978..dd5596b6 100644 --- a/src/js/node/fs/StatsFs.hx +++ b/src/js/node/fs/StatsFs.hx @@ -39,7 +39,7 @@ extern class StatsFs { var bsize:Int; /** - Fragment size. + Fundamental file system block size. **/ var frsize:Int; From c42934e8204867f69a54033799cca5a926a8988e Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Mon, 13 Jul 2026 22:33:51 +1000 Subject: [PATCH 12/48] Align net/buffer/cluster externs with Node 24 LTS APIs. Add BlockList.isBlockList/fromJSON/toJSON and SocketAddress.parse, fix SocketAddress family typing, widen buffer isUtf8/isAscii inputs, and correct primary alias docs. Co-authored-by: Cursor --- src/js/node/Cluster.hx | 8 ++++++-- src/js/node/buffer/Buffer.hx | 11 +++++++---- src/js/node/net/BlockList.hx | 25 +++++++++++++++++++++++++ src/js/node/net/SocketAddress.hx | 16 +++++++++++++--- 4 files changed, 51 insertions(+), 9 deletions(-) diff --git a/src/js/node/Cluster.hx b/src/js/node/Cluster.hx index a81059ad..9235377a 100644 --- a/src/js/node/Cluster.hx +++ b/src/js/node/Cluster.hx @@ -169,9 +169,11 @@ extern class Cluster extends EventEmitter { var isMaster(default, null):Bool; /** - True if the process is a primary (alias of `isMaster`). + True if the process is a primary. This is determined by the process.env.NODE_UNIQUE_ID. If process.env.NODE_UNIQUE_ID is undefined, then `isPrimary` is true. + + `isMaster` is a deprecated alias of `isPrimary`. **/ var isPrimary(default, null):Bool; @@ -197,7 +199,9 @@ extern class Cluster extends EventEmitter { function setupMaster(?settings:{?exec:String, ?args:Array, ?silent:Bool}):Void; /** - Alias of `setupMaster`. + Used to change the default `fork` behavior. Once called, the settings will be present in `settings`. + + `setupMaster` is a deprecated alias of `setupPrimary`. **/ function setupPrimary(?settings:{?exec:String, ?args:Array, ?silent:Bool}):Void; diff --git a/src/js/node/buffer/Buffer.hx b/src/js/node/buffer/Buffer.hx index 7622be3f..41ba2eb7 100644 --- a/src/js/node/buffer/Buffer.hx +++ b/src/js/node/buffer/Buffer.hx @@ -22,6 +22,7 @@ package js.node.buffer; +import haxe.extern.EitherType; import haxe.io.Bytes; import haxe.io.UInt8Array; #if haxe4 @@ -694,22 +695,24 @@ extern class Buffer extends Uint8Array { /** Returns `true` if the given `input` contains only valid UTF-8-encoded data, `false` otherwise. + `input` may be a `TypedArray`/`ArrayBufferView` or an `ArrayBuffer`. This is a property on the `buffer` module returned by `require('buffer')`, not on the `Buffer` global or a `Buffer` instance. @see https://nodejs.org/api/buffer.html#bufferisutf8input **/ - static inline function isUtf8(input:Uint8Array):Bool { + static inline function isUtf8(input:EitherType):Bool { return BufferModule.isUtf8(input); } /** Returns `true` if the given `input` contains only valid ASCII-encoded data, `false` otherwise. + `input` may be a `TypedArray`/`ArrayBufferView` or an `ArrayBuffer`. This is a property on the `buffer` module returned by `require('buffer')`, not on the `Buffer` global or a `Buffer` instance. @see https://nodejs.org/api/buffer.html#bufferisasciiinput **/ - static inline function isAscii(input:Uint8Array):Bool { + static inline function isAscii(input:EitherType):Bool { return BufferModule.isAscii(input); } @@ -785,8 +788,8 @@ private extern class BufferModule { static var INSPECT_MAX_BYTES:Int; static var kMaxLength(default, never):Int; static function transcode(source:Uint8Array, fromEnc:String, toEnc:String):Buffer; - static function isUtf8(input:Uint8Array):Bool; - static function isAscii(input:Uint8Array):Bool; + static function isUtf8(input:EitherType):Bool; + static function isAscii(input:EitherType):Bool; static function atob(data:String):String; static function btoa(data:String):String; static var constants(default, never):BufferConstants; diff --git a/src/js/node/net/BlockList.hx b/src/js/node/net/BlockList.hx index eff4bff6..64ed4c2b 100644 --- a/src/js/node/net/BlockList.hx +++ b/src/js/node/net/BlockList.hx @@ -32,6 +32,13 @@ import haxe.extern.EitherType; extern class BlockList { function new(); + /** + Returns `true` if the `value` is a `net.BlockList`. + + @see https://nodejs.org/api/net.html#blocklistisblocklistvalue + **/ + static function isBlockList(value:Dynamic):Bool; + /** Adds a rule to block the given IP address. @@ -67,4 +74,22 @@ extern class BlockList { The list of rules added to the blocklist. **/ var rules(default, null):Array; + + /** + Loads rules from a JSON string or an array of rule strings (same format as `rules`). + + Stability: 1 - Experimental. + + @see https://nodejs.org/api/net.html#blocklistfromjsonvalue + **/ + function fromJSON(value:EitherType>):Void; + + /** + Returns the rules as a JSON-serializable array of strings (same format as `rules`). + + Stability: 1 - Experimental. + + @see https://nodejs.org/api/net.html#blocklisttojson + **/ + function toJSON():Array; } diff --git a/src/js/node/net/SocketAddress.hx b/src/js/node/net/SocketAddress.hx index 525960ee..19c5fb1e 100644 --- a/src/js/node/net/SocketAddress.hx +++ b/src/js/node/net/SocketAddress.hx @@ -22,8 +22,6 @@ package js.node.net; -import haxe.extern.EitherType; - /** Represents a network endpoint as an IP address and port pair. @@ -34,15 +32,27 @@ extern class SocketAddress { /** Creates a new `SocketAddress`. + `family` is either `'ipv4'` or `'ipv6'`. Default: `'ipv4'`. + @see https://nodejs.org/api/net.html#new-netsocketaddressoptions **/ function new(?options:{ ?address:String, - ?family:EitherType, + ?family:String, ?flowlabel:Int, ?port:Int }):Void; + /** + Parses an IP address and optional port string into a `SocketAddress`. + + `input` examples: `123.1.2.3:1234` or `[1::1]:1234`. + Returns `null`/`undefined` if parsing fails. + + @see https://nodejs.org/api/net.html#socketaddressparseinput + **/ + static function parse(input:String):Null; + /** The network address as either an IPv4 or IPv6 string. **/ From 96f394244b05a49fd9893e71d9d6cdbeaef34a55 Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Mon, 13 Jul 2026 22:34:51 +1000 Subject: [PATCH 13/48] Align crypto externs with Node.js 24 Active LTS. Fix hkdf/hkdfSync to return ArrayBuffer, add scrypt cost/blockSize/parallelization options, deprecate fips, accept ArrayBuffer on timingSafeEqual/checkPrime, and expose randomUUIDv7. Co-authored-by: Cursor --- src/js/node/Crypto.hx | 60 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/src/js/node/Crypto.hx b/src/js/node/Crypto.hx index 81d7ecb7..f3d8b56e 100644 --- a/src/js/node/Crypto.hx +++ b/src/js/node/Crypto.hx @@ -28,9 +28,11 @@ import js.node.crypto.*; import js.node.crypto.DiffieHellman.IDiffieHellman; import js.node.tls.SecureContext; #if haxe4 +import js.lib.ArrayBuffer; import js.lib.ArrayBufferView; import js.lib.Error; #else +import js.html.ArrayBuffer; import js.html.ArrayBufferView; import js.Error; #end @@ -91,7 +93,10 @@ extern class Crypto { /** Property for checking and controlling whether a FIPS compliant crypto provider is currently in use. Setting to true requires a FIPS build of Node.js. + + Deprecated since Node.js v10.0.0. Use `getFips` / `setFips` instead. **/ + @:deprecated static var fips:Bool; /** @@ -263,15 +268,19 @@ extern class Crypto { /** HKDF is a simple key derivation function defined in RFC 5869. The given `ikm`, `salt` and `info` are used with the `digest` to derive a key of `keylen` bytes. + + The successfully generated derived key is passed to the callback as an `ArrayBuffer`. **/ static function hkdf(digest:String, ikm:EitherType, salt:EitherType, info:EitherType, keylen:Int, - callback:Error->Buffer->Void):Void; + callback:Error->ArrayBuffer->Void):Void; /** Provides a synchronous HKDF key derivation function as defined in RFC 5869. + + Returns the derived key as an `ArrayBuffer`. **/ static function hkdfSync(digest:String, ikm:EitherType, salt:EitherType, info:EitherType, - keylen:Int):Buffer; + keylen:Int):ArrayBuffer; /** Generates cryptographically strong pseudo-random data. @@ -324,22 +333,30 @@ extern class Crypto { static function randomUUID(?options:RandomUUIDOptions):String; /** - Compares the underlying bytes that represent the given `Buffer` or `ArrayBufferView` - instances using a constant-time algorithm. + Generates a random RFC 9562 version 7 UUID. + The UUID contains a millisecond precision Unix timestamp in the most significant 48 bits, + followed by cryptographically secure random bits for the remaining fields. + Added in Node.js v24.16.0 (Active LTS). + **/ + static function randomUUIDv7(?options:RandomUUIDOptions):String; + + /** + Compares the underlying bytes that represent the given `ArrayBuffer`, `Buffer`, + or `ArrayBufferView` instances using a constant-time algorithm. `a` and `b` must have the same byte length. **/ - static function timingSafeEqual(a:EitherType, b:EitherType):Bool; + static function timingSafeEqual(a:CryptoArrayBufferLike, b:CryptoArrayBufferLike):Bool; /** Checks the primality of the `candidate`. **/ - @:overload(function(candidate:EitherType, options:CheckPrimeOptions, callback:Error->Bool->Void):Void {}) - static function checkPrime(candidate:EitherType, callback:Error->Bool->Void):Void; + @:overload(function(candidate:CryptoArrayBufferLike, options:CheckPrimeOptions, callback:Error->Bool->Void):Void {}) + static function checkPrime(candidate:CryptoArrayBufferLike, callback:Error->Bool->Void):Void; /** Checks the primality of the `candidate` (synchronous version). **/ - static function checkPrimeSync(candidate:EitherType, ?options:CheckPrimeOptions):Bool; + static function checkPrimeSync(candidate:CryptoArrayBufferLike, ?options:CheckPrimeOptions):Bool; /** Decrypts `buffer` with `private_key`. @@ -411,22 +428,45 @@ typedef CryptoKeyOptions = { @:optional var padding:Int; } +/** + Binary input accepted by several crypto helpers (`ArrayBuffer`, `Buffer`, or `ArrayBufferView`). +**/ +typedef CryptoArrayBufferLike = EitherType>; + /** Options for `Crypto.scrypt` and `Crypto.scryptSync`. **/ typedef ScryptOptions = { /** CPU/memory cost parameter. Must be a power of two greater than one. Default: `16384`. + Only one of `cost` or `N` may be specified. **/ - @:optional var N:Int; + @:optional var cost:Int; /** Block size parameter. Default: `8`. + Only one of `blockSize` or `r` may be specified. **/ - @:optional var r:Int; + @:optional var blockSize:Int; /** Parallelization parameter. Default: `1`. + Only one of `parallelization` or `p` may be specified. + **/ + @:optional var parallelization:Int; + + /** + Alias for `cost`. Only one of `cost` or `N` may be specified. + **/ + @:optional var N:Int; + + /** + Alias for `blockSize`. Only one of `blockSize` or `r` may be specified. + **/ + @:optional var r:Int; + + /** + Alias for `parallelization`. Only one of `parallelization` or `p` may be specified. **/ @:optional var p:Int; From 4f79cd507897eae21f287fa6a6518b7f20a8c1ef Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Mon, 13 Jul 2026 22:35:28 +1000 Subject: [PATCH 14/48] Align TLS CA helpers with Node LTS by adding setDefaultCACertificates. Also sync os.machine doc examples with the Node 24 LTS API docs. Co-authored-by: Cursor --- src/js/node/Os.hx | 2 +- src/js/node/Tls.hx | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/js/node/Os.hx b/src/js/node/Os.hx index 512f1a30..706949f3 100644 --- a/src/js/node/Os.hx +++ b/src/js/node/Os.hx @@ -118,7 +118,7 @@ extern class Os { static function loadavg():Array; /** - Returns the machine type as a string, such as `arm`, `arm64`, `aarch64`, `mips`, `mips64`, `ppc64`, `ppc64le`, `s390`, `s390x`, `i386`, `i686`, `x86_64`. + Returns the machine type as a string, such as `arm`, `arm64`, `aarch64`, `mips`, `mips64`, `ppc64`, `ppc64le`, `s390x`, `i386`, `i686`, `x86_64`. @see https://nodejs.org/api/os.html#osmachine **/ diff --git a/src/js/node/Tls.hx b/src/js/node/Tls.hx index caddd19c..911ae9a3 100644 --- a/src/js/node/Tls.hx +++ b/src/js/node/Tls.hx @@ -213,9 +213,21 @@ extern class Tls { Valid values for `type` are `'default'`, `'system'`, `'bundled'` and `'extra'`. Default: `'default'`. + + @see https://nodejs.org/api/tls.html#tlsgetcacertificatestype **/ static function getCACertificates(?type:String):Array; + /** + Sets the default CA certificates used by Node.js TLS clients. + If the provided certificates are parsed successfully, they become the default CA + certificate list returned by `getCACertificates()` and used by subsequent TLS + connections that don't specify their own CA certificates. + + @see https://nodejs.org/api/tls.html#tlssetdefaultcacertificatescerts + **/ + static function setDefaultCACertificates(certs:Array>):Void; + /** Returns an array with the names of the supported SSL ciphers. **/ From c2e16659bf3349e50781256425c4a4de4b5bf8dd Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Mon, 13 Jul 2026 22:59:55 +1000 Subject: [PATCH 15/48] Add globalThis and structuredClone globals Expose Node 24 globalThis and structuredClone on js.Node, and note that global is legacy in favor of globalThis. Co-authored-by: Cursor --- src/js/Node.hx | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/js/Node.hx b/src/js/Node.hx index 9e7f23ed..c414c062 100644 --- a/src/js/Node.hx +++ b/src/js/Node.hx @@ -110,9 +110,26 @@ extern class Node { This means that within the browser `var something` will define a new global variable. In Node.js this is different. The top-level scope is not the global scope; `var something` inside a Node.js module will be local to that module. + + Stability: 3 - Legacy. Use `globalThis` instead. **/ static inline var global:Dynamic = cast Node; + /** + `globalThis` is the universal way to access the global object. + + @see https://nodejs.org/api/globals.html#globalthis + **/ + static var globalThis(get, never):Dynamic; + + private static inline function get_globalThis():Dynamic { + #if haxe4 + return code("globalThis"); + #else + return untyped __js__("globalThis"); + #end + } + /** This variable may appear to be global but is not. See [module](https://nodejs.org/api/modules.html#modules_module). **/ @@ -174,6 +191,23 @@ extern class Node { `setTimeout` is described in the [timers](https://nodejs.org/api/timers.html) section. **/ static function setTimeout(callback:Function, delay:Int, args:Rest):Timeout; + + /** + The WHATWG [`structuredClone`](https://developer.mozilla.org/en-US/docs/Web/API/structuredClone) method. + + @see https://nodejs.org/api/globals.html#structuredclonevalue-options + **/ + static function structuredClone(value:Dynamic, ?options:StructuredCloneOptions):Dynamic; +} + +/** + Options for `Node.structuredClone`. +**/ +typedef StructuredCloneOptions = { + /** + A list of transferable objects that will be moved rather than cloned. + **/ + @:optional var transfer:Array; } @:deprecated typedef TimeoutObject = js.node.Timers.Timeout; From 6a54b1fbe141577fb96378e5e9b0b6dc5e89d5a0 Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Mon, 13 Jul 2026 23:00:06 +1000 Subject: [PATCH 16/48] Add Event, EventTarget, CustomEvent, DOMException externs Add Node 24 web EventTarget API types under js.node.web without wiring them through Node.hx yet. Co-authored-by: Cursor --- src/js/node/web/CustomEvent.hx | 64 +++++++++++++ src/js/node/web/DOMException.hx | 75 +++++++++++++++ src/js/node/web/Event.hx | 161 ++++++++++++++++++++++++++++++++ src/js/node/web/EventTarget.hx | 107 +++++++++++++++++++++ 4 files changed, 407 insertions(+) create mode 100644 src/js/node/web/CustomEvent.hx create mode 100644 src/js/node/web/DOMException.hx create mode 100644 src/js/node/web/Event.hx create mode 100644 src/js/node/web/EventTarget.hx diff --git a/src/js/node/web/CustomEvent.hx b/src/js/node/web/CustomEvent.hx new file mode 100644 index 00000000..bf7a1aef --- /dev/null +++ b/src/js/node/web/CustomEvent.hx @@ -0,0 +1,64 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.web; + +/** + A browser-compatible implementation of `CustomEvent`. + + @see https://nodejs.org/api/events.html#class-customevent + @see https://nodejs.org/api/globals.html#class-customevent +**/ +@:native("CustomEvent") +extern class CustomEvent extends Event { + /** + Custom data passed when initializing the event. + **/ + var detail(default, null):Dynamic; + + function new(type:String, ?eventInitDict:CustomEventInit):Void; +} + +/** + Options passed to the `CustomEvent` constructor. +**/ +typedef CustomEventInit = { + /** + Not used in Node.js. Default: `false`. + **/ + @:optional var bubbles:Bool; + + /** + When `true`, `preventDefault()` can cancel the event. Default: `false`. + **/ + @:optional var cancelable:Bool; + + /** + Not used in Node.js. Default: `false`. + **/ + @:optional var composed:Bool; + + /** + Custom data exposed as `detail`. + **/ + @:optional var detail:Dynamic; +} diff --git a/src/js/node/web/DOMException.hx b/src/js/node/web/DOMException.hx new file mode 100644 index 00000000..065704c9 --- /dev/null +++ b/src/js/node/web/DOMException.hx @@ -0,0 +1,75 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.web; + +/** + The WHATWG `DOMException` class. + + @see https://nodejs.org/api/globals.html#class-domexception +**/ +@:native("DOMException") +extern class DOMException { + static inline var INDEX_SIZE_ERR:Int = 1; + static inline var DOMSTRING_SIZE_ERR:Int = 2; + static inline var HIERARCHY_REQUEST_ERR:Int = 3; + static inline var WRONG_DOCUMENT_ERR:Int = 4; + static inline var INVALID_CHARACTER_ERR:Int = 5; + static inline var NO_DATA_ALLOWED_ERR:Int = 6; + static inline var NO_MODIFICATION_ALLOWED_ERR:Int = 7; + static inline var NOT_FOUND_ERR:Int = 8; + static inline var NOT_SUPPORTED_ERR:Int = 9; + static inline var INUSE_ATTRIBUTE_ERR:Int = 10; + static inline var INVALID_STATE_ERR:Int = 11; + static inline var SYNTAX_ERR:Int = 12; + static inline var INVALID_MODIFICATION_ERR:Int = 13; + static inline var NAMESPACE_ERR:Int = 14; + static inline var INVALID_ACCESS_ERR:Int = 15; + static inline var VALIDATION_ERR:Int = 16; + static inline var TYPE_MISMATCH_ERR:Int = 17; + static inline var SECURITY_ERR:Int = 18; + static inline var NETWORK_ERR:Int = 19; + static inline var ABORT_ERR:Int = 20; + static inline var URL_MISMATCH_ERR:Int = 21; + static inline var QUOTA_EXCEEDED_ERR:Int = 22; + static inline var TIMEOUT_ERR:Int = 23; + static inline var INVALID_NODE_TYPE_ERR:Int = 24; + static inline var DATA_CLONE_ERR:Int = 25; + + /** + One of the strings associated with an error name. + **/ + var name(default, null):String; + + /** + A message or description associated with the given error name. + **/ + var message(default, null):String; + + /** + One of the legacy error codes, or `0` if none match. + New DOM exceptions put this info in `name` instead. + **/ + var code(default, null):Int; + + function new(?message:String, ?name:String):Void; +} diff --git a/src/js/node/web/Event.hx b/src/js/node/web/Event.hx new file mode 100644 index 00000000..46fcd57d --- /dev/null +++ b/src/js/node/web/Event.hx @@ -0,0 +1,161 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.web; + +/** + A browser-compatible implementation of the `Event` class. + + @see https://nodejs.org/api/events.html#class-event + @see https://nodejs.org/api/globals.html#class-event +**/ +@:native("Event") +extern class Event { + static inline var NONE:Int = 0; + static inline var CAPTURING_PHASE:Int = 1; + static inline var AT_TARGET:Int = 2; + static inline var BUBBLING_PHASE:Int = 3; + + /** + The event type identifier. + **/ + var type(default, null):String; + + /** + The `EventTarget` dispatching the event. + **/ + var target(default, null):EventTarget; + + /** + Alias for `target`. + **/ + var currentTarget(default, null):EventTarget; + + /** + Returns `0` while an event is not being dispatched, `2` while it is being dispatched. + This is not used in Node.js and is provided purely for completeness. + **/ + var eventPhase(default, null):Int; + + /** + Always returns `false` in Node.js. Provided purely for completeness. + **/ + var bubbles(default, null):Bool; + + /** + True if the event was created with the `cancelable` option. + **/ + var cancelable(default, null):Bool; + + /** + Stability: 3 - Legacy: Use `defaultPrevented` instead. + + True if the event has not been canceled. + **/ + var returnValue:Bool; + + /** + Is `true` if `cancelable` is `true` and `preventDefault()` has been called. + **/ + var defaultPrevented(default, null):Bool; + + /** + Always returns `false` in Node.js. Provided purely for completeness. + **/ + var composed(default, null):Bool; + + /** + The `"abort"` event is emitted with `isTrusted` set to `true`. + The value is `false` in all other cases. + **/ + var isTrusted(default, null):Bool; + + /** + The millisecond timestamp when the `Event` object was created. + **/ + var timeStamp(default, null):Float; + + /** + Stability: 3 - Legacy: Use `stopPropagation()` instead. + + Alias for `stopPropagation()` if set to `true`. + **/ + var cancelBubble:Bool; + + /** + Stability: 3 - Legacy: Use `target` instead. + + Alias for `target`. + **/ + var srcElement(default, null):EventTarget; + + function new(type:String, ?eventInitDict:EventInit):Void; + + /** + Returns an array containing the current `EventTarget` as the only entry, + or empty if the event is not being dispatched. + This is not used in Node.js and is provided purely for completeness. + **/ + function composedPath():Array; + + /** + Sets the `defaultPrevented` property to `true` if `cancelable` is `true`. + **/ + function preventDefault():Void; + + /** + Stops the invocation of event listeners after the current one completes. + **/ + function stopImmediatePropagation():Void; + + /** + This is not used in Node.js and is provided purely for completeness. + **/ + function stopPropagation():Void; + + /** + Stability: 3 - Legacy: The WHATWG spec considers it deprecated. + + Redundant with event constructors and incapable of setting `composed`. + **/ + function initEvent(type:String, ?bubbles:Bool, ?cancelable:Bool):Void; +} + +/** + Options passed to the `Event` constructor. +**/ +typedef EventInit = { + /** + Not used in Node.js. Default: `false`. + **/ + @:optional var bubbles:Bool; + + /** + When `true`, `preventDefault()` can cancel the event. Default: `false`. + **/ + @:optional var cancelable:Bool; + + /** + Not used in Node.js. Default: `false`. + **/ + @:optional var composed:Bool; +} diff --git a/src/js/node/web/EventTarget.hx b/src/js/node/web/EventTarget.hx new file mode 100644 index 00000000..17956383 --- /dev/null +++ b/src/js/node/web/EventTarget.hx @@ -0,0 +1,107 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.web; + +import haxe.Constraints.Function; +import haxe.extern.EitherType; + +/** + A browser-compatible implementation of the `EventTarget` class. + + @see https://nodejs.org/api/events.html#class-eventtarget + @see https://nodejs.org/api/globals.html#class-eventtarget +**/ +@:native("EventTarget") +extern class EventTarget { + function new():Void; + + /** + Adds a new handler for the `type` event. + Any given `listener` is added only once per `type` and per `capture` option value. + **/ + @:overload(function(type:String, listener:EventListener, ?options:EitherType):Void {}) + function addEventListener(type:String, listener:Function, ?options:EitherType):Void; + + /** + Removes the `listener` from the list of handlers for event `type`. + **/ + @:overload(function(type:String, listener:EventListener, ?options:EitherType):Void {}) + function removeEventListener(type:String, listener:Function, ?options:EitherType):Void; + + /** + Dispatches the `event` to the list of handlers for `event.type`. + + @return `true` if either event's `cancelable` attribute value is false + or its `preventDefault()` method was not invoked, otherwise `false`. + **/ + function dispatchEvent(event:Event):Bool; +} + +/** + An object with a `handleEvent` method, usable as an event listener. +**/ +typedef EventListener = { + function handleEvent(event:Event):Void; +} + +/** + Options for `EventTarget.removeEventListener`. +**/ +typedef EventListenerOptions = { + /** + Not directly used by Node.js except as part of the listener registration key. + Default: `false`. + **/ + @:optional var capture:Bool; +} + +/** + Options for `EventTarget.addEventListener`. +**/ +typedef AddEventListenerOptions = { + /** + Not directly used by Node.js except as part of the listener registration key. + Default: `false`. + **/ + @:optional var capture:Bool; + + /** + When `true`, the listener is automatically removed when it is first invoked. + Default: `false`. + **/ + @:optional var once:Bool; + + /** + When `true`, serves as a hint that the listener will not call `preventDefault()`. + Default: `false`. + **/ + @:optional var passive:Bool; + + /** + The listener will be removed when the given `AbortSignal` object's `abort()` method is called. + + Typed as `Dynamic` so this module does not hard-depend on the AbortSignal externs; + use `js.node.web.AbortSignal` when that module is available. + **/ + @:optional var signal:Dynamic; +} From 4592f51162bbb5e76d02920971d9a3d81190d08a Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Mon, 13 Jul 2026 23:00:06 +1000 Subject: [PATCH 17/48] Add AbortController and AbortSignal externs Add Node 24 AbortController/AbortSignal under js.node.web, including shared EventTarget path types so AbortSignal can extend EventTarget independently of the events PR. Co-authored-by: Cursor --- src/js/node/web/AbortController.hx | 46 +++++++++ src/js/node/web/AbortSignal.hx | 73 +++++++++++++ src/js/node/web/Event.hx | 161 +++++++++++++++++++++++++++++ src/js/node/web/EventTarget.hx | 107 +++++++++++++++++++ 4 files changed, 387 insertions(+) create mode 100644 src/js/node/web/AbortController.hx create mode 100644 src/js/node/web/AbortSignal.hx create mode 100644 src/js/node/web/Event.hx create mode 100644 src/js/node/web/EventTarget.hx diff --git a/src/js/node/web/AbortController.hx b/src/js/node/web/AbortController.hx new file mode 100644 index 00000000..9c40e249 --- /dev/null +++ b/src/js/node/web/AbortController.hx @@ -0,0 +1,46 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.web; + +/** + A utility class used to signal cancelation in selected `Promise`-based APIs. + The API is based on the Web API `AbortController`. + + @see https://nodejs.org/api/globals.html#class-abortcontroller +**/ +@:native("AbortController") +extern class AbortController { + /** + The `AbortSignal` object associated with this controller. + **/ + var signal(default, null):AbortSignal; + + function new():Void; + + /** + Triggers the abort signal, causing `signal` to emit the `'abort'` event. + + @param reason An optional reason, retrievable on the `AbortSignal`'s `reason` property. + **/ + function abort(?reason:Dynamic):Void; +} diff --git a/src/js/node/web/AbortSignal.hx b/src/js/node/web/AbortSignal.hx new file mode 100644 index 00000000..f5ff08a4 --- /dev/null +++ b/src/js/node/web/AbortSignal.hx @@ -0,0 +1,73 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.web; + +import haxe.Constraints.Function; + +/** + Used to notify observers when `AbortController.abort()` is called. + + Extends `js.node.web.EventTarget`. Prefer these Node web externs over the + incomplete Haxe standard library `js.html.AbortSignal`. + + @see https://nodejs.org/api/globals.html#class-abortsignal +**/ +@:native("AbortSignal") +extern class AbortSignal extends EventTarget { + /** + Returns a new already aborted `AbortSignal`. + **/ + static function abort(?reason:Dynamic):AbortSignal; + + /** + Returns a new `AbortSignal` which will be aborted in `delay` milliseconds. + **/ + static function timeout(delay:Float):AbortSignal; + + /** + Returns a new `AbortSignal` which will be aborted if any of the provided + signals are aborted. Its `reason` will be set to whichever signal caused the abort. + **/ + static function any(signals:Array):AbortSignal; + + /** + True after the associated `AbortController` has been aborted. + **/ + var aborted(default, null):Bool; + + /** + An optional reason specified when the `AbortSignal` was triggered. + **/ + var reason(default, null):Dynamic; + + /** + An optional callback function that may be set by user code to be notified + when `AbortController.abort()` has been called. + **/ + var onabort:Function; + + /** + If `aborted` is `true`, throws `reason`. + **/ + function throwIfAborted():Void; +} diff --git a/src/js/node/web/Event.hx b/src/js/node/web/Event.hx new file mode 100644 index 00000000..46fcd57d --- /dev/null +++ b/src/js/node/web/Event.hx @@ -0,0 +1,161 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.web; + +/** + A browser-compatible implementation of the `Event` class. + + @see https://nodejs.org/api/events.html#class-event + @see https://nodejs.org/api/globals.html#class-event +**/ +@:native("Event") +extern class Event { + static inline var NONE:Int = 0; + static inline var CAPTURING_PHASE:Int = 1; + static inline var AT_TARGET:Int = 2; + static inline var BUBBLING_PHASE:Int = 3; + + /** + The event type identifier. + **/ + var type(default, null):String; + + /** + The `EventTarget` dispatching the event. + **/ + var target(default, null):EventTarget; + + /** + Alias for `target`. + **/ + var currentTarget(default, null):EventTarget; + + /** + Returns `0` while an event is not being dispatched, `2` while it is being dispatched. + This is not used in Node.js and is provided purely for completeness. + **/ + var eventPhase(default, null):Int; + + /** + Always returns `false` in Node.js. Provided purely for completeness. + **/ + var bubbles(default, null):Bool; + + /** + True if the event was created with the `cancelable` option. + **/ + var cancelable(default, null):Bool; + + /** + Stability: 3 - Legacy: Use `defaultPrevented` instead. + + True if the event has not been canceled. + **/ + var returnValue:Bool; + + /** + Is `true` if `cancelable` is `true` and `preventDefault()` has been called. + **/ + var defaultPrevented(default, null):Bool; + + /** + Always returns `false` in Node.js. Provided purely for completeness. + **/ + var composed(default, null):Bool; + + /** + The `"abort"` event is emitted with `isTrusted` set to `true`. + The value is `false` in all other cases. + **/ + var isTrusted(default, null):Bool; + + /** + The millisecond timestamp when the `Event` object was created. + **/ + var timeStamp(default, null):Float; + + /** + Stability: 3 - Legacy: Use `stopPropagation()` instead. + + Alias for `stopPropagation()` if set to `true`. + **/ + var cancelBubble:Bool; + + /** + Stability: 3 - Legacy: Use `target` instead. + + Alias for `target`. + **/ + var srcElement(default, null):EventTarget; + + function new(type:String, ?eventInitDict:EventInit):Void; + + /** + Returns an array containing the current `EventTarget` as the only entry, + or empty if the event is not being dispatched. + This is not used in Node.js and is provided purely for completeness. + **/ + function composedPath():Array; + + /** + Sets the `defaultPrevented` property to `true` if `cancelable` is `true`. + **/ + function preventDefault():Void; + + /** + Stops the invocation of event listeners after the current one completes. + **/ + function stopImmediatePropagation():Void; + + /** + This is not used in Node.js and is provided purely for completeness. + **/ + function stopPropagation():Void; + + /** + Stability: 3 - Legacy: The WHATWG spec considers it deprecated. + + Redundant with event constructors and incapable of setting `composed`. + **/ + function initEvent(type:String, ?bubbles:Bool, ?cancelable:Bool):Void; +} + +/** + Options passed to the `Event` constructor. +**/ +typedef EventInit = { + /** + Not used in Node.js. Default: `false`. + **/ + @:optional var bubbles:Bool; + + /** + When `true`, `preventDefault()` can cancel the event. Default: `false`. + **/ + @:optional var cancelable:Bool; + + /** + Not used in Node.js. Default: `false`. + **/ + @:optional var composed:Bool; +} diff --git a/src/js/node/web/EventTarget.hx b/src/js/node/web/EventTarget.hx new file mode 100644 index 00000000..17956383 --- /dev/null +++ b/src/js/node/web/EventTarget.hx @@ -0,0 +1,107 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.web; + +import haxe.Constraints.Function; +import haxe.extern.EitherType; + +/** + A browser-compatible implementation of the `EventTarget` class. + + @see https://nodejs.org/api/events.html#class-eventtarget + @see https://nodejs.org/api/globals.html#class-eventtarget +**/ +@:native("EventTarget") +extern class EventTarget { + function new():Void; + + /** + Adds a new handler for the `type` event. + Any given `listener` is added only once per `type` and per `capture` option value. + **/ + @:overload(function(type:String, listener:EventListener, ?options:EitherType):Void {}) + function addEventListener(type:String, listener:Function, ?options:EitherType):Void; + + /** + Removes the `listener` from the list of handlers for event `type`. + **/ + @:overload(function(type:String, listener:EventListener, ?options:EitherType):Void {}) + function removeEventListener(type:String, listener:Function, ?options:EitherType):Void; + + /** + Dispatches the `event` to the list of handlers for `event.type`. + + @return `true` if either event's `cancelable` attribute value is false + or its `preventDefault()` method was not invoked, otherwise `false`. + **/ + function dispatchEvent(event:Event):Bool; +} + +/** + An object with a `handleEvent` method, usable as an event listener. +**/ +typedef EventListener = { + function handleEvent(event:Event):Void; +} + +/** + Options for `EventTarget.removeEventListener`. +**/ +typedef EventListenerOptions = { + /** + Not directly used by Node.js except as part of the listener registration key. + Default: `false`. + **/ + @:optional var capture:Bool; +} + +/** + Options for `EventTarget.addEventListener`. +**/ +typedef AddEventListenerOptions = { + /** + Not directly used by Node.js except as part of the listener registration key. + Default: `false`. + **/ + @:optional var capture:Bool; + + /** + When `true`, the listener is automatically removed when it is first invoked. + Default: `false`. + **/ + @:optional var once:Bool; + + /** + When `true`, serves as a hint that the listener will not call `preventDefault()`. + Default: `false`. + **/ + @:optional var passive:Bool; + + /** + The listener will be removed when the given `AbortSignal` object's `abort()` method is called. + + Typed as `Dynamic` so this module does not hard-depend on the AbortSignal externs; + use `js.node.web.AbortSignal` when that module is available. + **/ + @:optional var signal:Dynamic; +} From 614ac4e40205d70745cc1c0f36700351cfb322d5 Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Mon, 13 Jul 2026 23:00:06 +1000 Subject: [PATCH 18/48] Add fetch, Request, Response, Headers, FormData, Blob, File externs Add undici-based Node 24 fetch globals surface under js.node.web, with AbortSignal typed via the shared package path. Co-authored-by: Cursor --- src/js/node/web/AbortSignal.hx | 73 +++++++++++++++ src/js/node/web/Blob.hx | 107 ++++++++++++++++++++++ src/js/node/web/Event.hx | 161 +++++++++++++++++++++++++++++++++ src/js/node/web/EventTarget.hx | 107 ++++++++++++++++++++++ src/js/node/web/Fetch.hx | 47 ++++++++++ src/js/node/web/File.hx | 68 ++++++++++++++ src/js/node/web/FormData.hx | 74 +++++++++++++++ src/js/node/web/Headers.hx | 75 +++++++++++++++ src/js/node/web/Request.hx | 110 ++++++++++++++++++++++ src/js/node/web/Response.hx | 94 +++++++++++++++++++ 10 files changed, 916 insertions(+) create mode 100644 src/js/node/web/AbortSignal.hx create mode 100644 src/js/node/web/Blob.hx create mode 100644 src/js/node/web/Event.hx create mode 100644 src/js/node/web/EventTarget.hx create mode 100644 src/js/node/web/Fetch.hx create mode 100644 src/js/node/web/File.hx create mode 100644 src/js/node/web/FormData.hx create mode 100644 src/js/node/web/Headers.hx create mode 100644 src/js/node/web/Request.hx create mode 100644 src/js/node/web/Response.hx diff --git a/src/js/node/web/AbortSignal.hx b/src/js/node/web/AbortSignal.hx new file mode 100644 index 00000000..f5ff08a4 --- /dev/null +++ b/src/js/node/web/AbortSignal.hx @@ -0,0 +1,73 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.web; + +import haxe.Constraints.Function; + +/** + Used to notify observers when `AbortController.abort()` is called. + + Extends `js.node.web.EventTarget`. Prefer these Node web externs over the + incomplete Haxe standard library `js.html.AbortSignal`. + + @see https://nodejs.org/api/globals.html#class-abortsignal +**/ +@:native("AbortSignal") +extern class AbortSignal extends EventTarget { + /** + Returns a new already aborted `AbortSignal`. + **/ + static function abort(?reason:Dynamic):AbortSignal; + + /** + Returns a new `AbortSignal` which will be aborted in `delay` milliseconds. + **/ + static function timeout(delay:Float):AbortSignal; + + /** + Returns a new `AbortSignal` which will be aborted if any of the provided + signals are aborted. Its `reason` will be set to whichever signal caused the abort. + **/ + static function any(signals:Array):AbortSignal; + + /** + True after the associated `AbortController` has been aborted. + **/ + var aborted(default, null):Bool; + + /** + An optional reason specified when the `AbortSignal` was triggered. + **/ + var reason(default, null):Dynamic; + + /** + An optional callback function that may be set by user code to be notified + when `AbortController.abort()` has been called. + **/ + var onabort:Function; + + /** + If `aborted` is `true`, throws `reason`. + **/ + function throwIfAborted():Void; +} diff --git a/src/js/node/web/Blob.hx b/src/js/node/web/Blob.hx new file mode 100644 index 00000000..70657315 --- /dev/null +++ b/src/js/node/web/Blob.hx @@ -0,0 +1,107 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.web; + +import haxe.extern.EitherType; +#if haxe4 +import js.lib.ArrayBuffer; +import js.lib.ArrayBufferView; +import js.lib.Promise; +import js.lib.Uint8Array; +#else +import js.html.ArrayBuffer; +import js.html.ArrayBufferView; +import js.Promise; +import js.html.Uint8Array; +#end + +/** + Encapsulates immutable, raw data that can be safely shared across workers. + + Available as a global and via `require('node:buffer').Blob`. + + @see https://nodejs.org/api/buffer.html#class-blob + @see https://nodejs.org/api/globals.html#class-blob +**/ +@:native("Blob") +extern class Blob { + /** + The total size of the `Blob` in bytes. + **/ + var size(default, null):Int; + + /** + The content-type of the `Blob`. + **/ + var type(default, null):String; + + function new(?sources:Array, ?options:BlobPropertyBag):Void; + + /** + Returns a promise that fulfills with an `ArrayBuffer` containing a copy of the `Blob` data. + **/ + function arrayBuffer():Promise; + + /** + Returns a promise that fulfills with a `Uint8Array` containing a copy of the `Blob` data. + **/ + function bytes():Promise; + + /** + Creates and returns a new `Blob` containing a subset of this `Blob`'s data. + **/ + function slice(?start:Int, ?end:Int, ?type:String):Blob; + + /** + Returns a new `ReadableStream` that allows the content of the `Blob` to be read. + + Typed as `Dynamic` until web streams externs are added. + **/ + function stream():Dynamic; + + /** + Returns a promise that fulfills with the contents of the `Blob` decoded as a UTF-8 string. + **/ + function text():Promise; +} + +/** + A part accepted by the `Blob` / `File` constructors. +**/ +typedef BlobPart = EitherType>>; + +/** + Options for the `Blob` constructor. +**/ +typedef BlobPropertyBag = { + /** + One of `'transparent'` or `'native'`. When `'native'`, line endings in string + source parts are converted to the platform native line-ending. + **/ + @:optional var endings:String; + + /** + The Blob content-type (MIME media type). No validation is performed. + **/ + @:optional var type:String; +} diff --git a/src/js/node/web/Event.hx b/src/js/node/web/Event.hx new file mode 100644 index 00000000..46fcd57d --- /dev/null +++ b/src/js/node/web/Event.hx @@ -0,0 +1,161 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.web; + +/** + A browser-compatible implementation of the `Event` class. + + @see https://nodejs.org/api/events.html#class-event + @see https://nodejs.org/api/globals.html#class-event +**/ +@:native("Event") +extern class Event { + static inline var NONE:Int = 0; + static inline var CAPTURING_PHASE:Int = 1; + static inline var AT_TARGET:Int = 2; + static inline var BUBBLING_PHASE:Int = 3; + + /** + The event type identifier. + **/ + var type(default, null):String; + + /** + The `EventTarget` dispatching the event. + **/ + var target(default, null):EventTarget; + + /** + Alias for `target`. + **/ + var currentTarget(default, null):EventTarget; + + /** + Returns `0` while an event is not being dispatched, `2` while it is being dispatched. + This is not used in Node.js and is provided purely for completeness. + **/ + var eventPhase(default, null):Int; + + /** + Always returns `false` in Node.js. Provided purely for completeness. + **/ + var bubbles(default, null):Bool; + + /** + True if the event was created with the `cancelable` option. + **/ + var cancelable(default, null):Bool; + + /** + Stability: 3 - Legacy: Use `defaultPrevented` instead. + + True if the event has not been canceled. + **/ + var returnValue:Bool; + + /** + Is `true` if `cancelable` is `true` and `preventDefault()` has been called. + **/ + var defaultPrevented(default, null):Bool; + + /** + Always returns `false` in Node.js. Provided purely for completeness. + **/ + var composed(default, null):Bool; + + /** + The `"abort"` event is emitted with `isTrusted` set to `true`. + The value is `false` in all other cases. + **/ + var isTrusted(default, null):Bool; + + /** + The millisecond timestamp when the `Event` object was created. + **/ + var timeStamp(default, null):Float; + + /** + Stability: 3 - Legacy: Use `stopPropagation()` instead. + + Alias for `stopPropagation()` if set to `true`. + **/ + var cancelBubble:Bool; + + /** + Stability: 3 - Legacy: Use `target` instead. + + Alias for `target`. + **/ + var srcElement(default, null):EventTarget; + + function new(type:String, ?eventInitDict:EventInit):Void; + + /** + Returns an array containing the current `EventTarget` as the only entry, + or empty if the event is not being dispatched. + This is not used in Node.js and is provided purely for completeness. + **/ + function composedPath():Array; + + /** + Sets the `defaultPrevented` property to `true` if `cancelable` is `true`. + **/ + function preventDefault():Void; + + /** + Stops the invocation of event listeners after the current one completes. + **/ + function stopImmediatePropagation():Void; + + /** + This is not used in Node.js and is provided purely for completeness. + **/ + function stopPropagation():Void; + + /** + Stability: 3 - Legacy: The WHATWG spec considers it deprecated. + + Redundant with event constructors and incapable of setting `composed`. + **/ + function initEvent(type:String, ?bubbles:Bool, ?cancelable:Bool):Void; +} + +/** + Options passed to the `Event` constructor. +**/ +typedef EventInit = { + /** + Not used in Node.js. Default: `false`. + **/ + @:optional var bubbles:Bool; + + /** + When `true`, `preventDefault()` can cancel the event. Default: `false`. + **/ + @:optional var cancelable:Bool; + + /** + Not used in Node.js. Default: `false`. + **/ + @:optional var composed:Bool; +} diff --git a/src/js/node/web/EventTarget.hx b/src/js/node/web/EventTarget.hx new file mode 100644 index 00000000..17956383 --- /dev/null +++ b/src/js/node/web/EventTarget.hx @@ -0,0 +1,107 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.web; + +import haxe.Constraints.Function; +import haxe.extern.EitherType; + +/** + A browser-compatible implementation of the `EventTarget` class. + + @see https://nodejs.org/api/events.html#class-eventtarget + @see https://nodejs.org/api/globals.html#class-eventtarget +**/ +@:native("EventTarget") +extern class EventTarget { + function new():Void; + + /** + Adds a new handler for the `type` event. + Any given `listener` is added only once per `type` and per `capture` option value. + **/ + @:overload(function(type:String, listener:EventListener, ?options:EitherType):Void {}) + function addEventListener(type:String, listener:Function, ?options:EitherType):Void; + + /** + Removes the `listener` from the list of handlers for event `type`. + **/ + @:overload(function(type:String, listener:EventListener, ?options:EitherType):Void {}) + function removeEventListener(type:String, listener:Function, ?options:EitherType):Void; + + /** + Dispatches the `event` to the list of handlers for `event.type`. + + @return `true` if either event's `cancelable` attribute value is false + or its `preventDefault()` method was not invoked, otherwise `false`. + **/ + function dispatchEvent(event:Event):Bool; +} + +/** + An object with a `handleEvent` method, usable as an event listener. +**/ +typedef EventListener = { + function handleEvent(event:Event):Void; +} + +/** + Options for `EventTarget.removeEventListener`. +**/ +typedef EventListenerOptions = { + /** + Not directly used by Node.js except as part of the listener registration key. + Default: `false`. + **/ + @:optional var capture:Bool; +} + +/** + Options for `EventTarget.addEventListener`. +**/ +typedef AddEventListenerOptions = { + /** + Not directly used by Node.js except as part of the listener registration key. + Default: `false`. + **/ + @:optional var capture:Bool; + + /** + When `true`, the listener is automatically removed when it is first invoked. + Default: `false`. + **/ + @:optional var once:Bool; + + /** + When `true`, serves as a hint that the listener will not call `preventDefault()`. + Default: `false`. + **/ + @:optional var passive:Bool; + + /** + The listener will be removed when the given `AbortSignal` object's `abort()` method is called. + + Typed as `Dynamic` so this module does not hard-depend on the AbortSignal externs; + use `js.node.web.AbortSignal` when that module is available. + **/ + @:optional var signal:Dynamic; +} diff --git a/src/js/node/web/Fetch.hx b/src/js/node/web/Fetch.hx new file mode 100644 index 00000000..cdca9600 --- /dev/null +++ b/src/js/node/web/Fetch.hx @@ -0,0 +1,47 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.web; + +import haxe.extern.EitherType; +import js.node.web.Request.RequestInit; +#if haxe4 +import js.lib.Promise; +#else +import js.Promise; +#end + +/** + Browser-compatible `fetch()` (undici), exposed as a static on `globalThis`. + + Usage: `Fetch.fetch(url)` / `Fetch.fetch(url, init)`. + + @see https://nodejs.org/api/globals.html#fetch +**/ +@:native("globalThis") +extern class Fetch { + /** + Starts the process of fetching a resource from the network. + **/ + @:overload(function(input:Request, ?init:RequestInit):Promise {}) + static function fetch(input:String, ?init:RequestInit):Promise; +} diff --git a/src/js/node/web/File.hx b/src/js/node/web/File.hx new file mode 100644 index 00000000..4c56f017 --- /dev/null +++ b/src/js/node/web/File.hx @@ -0,0 +1,68 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.web; + +import js.node.web.Blob.BlobPart; + +/** + Provides information about files. Extends `Blob`. + + Available as a global and via `require('node:buffer').File`. + + @see https://nodejs.org/api/buffer.html#class-file + @see https://nodejs.org/api/globals.html#class-file +**/ +@:native("File") +extern class File extends Blob { + /** + The name of the file. + **/ + var name(default, null):String; + + /** + The last modified date of the file (milliseconds since UNIX epoch). + **/ + var lastModified(default, null):Float; + + function new(sources:Array, fileName:String, ?options:FilePropertyBag):Void; +} + +/** + Options for the `File` constructor. +**/ +typedef FilePropertyBag = { + /** + One of `'transparent'` or `'native'`. + **/ + @:optional var endings:String; + + /** + The File content-type. + **/ + @:optional var type:String; + + /** + The last modified date of the file. Default: `Date.now()`. + **/ + @:optional var lastModified:Float; +} diff --git a/src/js/node/web/FormData.hx b/src/js/node/web/FormData.hx new file mode 100644 index 00000000..0370c69e --- /dev/null +++ b/src/js/node/web/FormData.hx @@ -0,0 +1,74 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.web; + +import haxe.extern.EitherType; +import js.node.Iterator; +import js.node.KeyValue; + +/** + A browser-compatible `FormData` implementation (undici / fetch). + + @see https://nodejs.org/api/globals.html#class-formdata +**/ +@:native("FormData") +extern class FormData { + function new():Void; + + /** + Appends a new value onto an existing key, or adds the key if it does not exist. + **/ + @:overload(function(name:String, value:Blob, ?filename:String):Void {}) + function append(name:String, value:String):Void; + + /** + Deletes a key/value pair. + **/ + function delete(name:String):Void; + + /** + Returns the first value associated with a given key. + **/ + function get(name:String):Null>>; + + /** + Returns all values associated with a given key. + **/ + function getAll(name:String):Array>>; + + /** + Returns whether a key exists. + **/ + function has(name:String):Bool; + + /** + Sets a new value for an existing key, or adds the key if it does not exist. + **/ + @:overload(function(name:String, value:Blob, ?filename:String):Void {}) + function set(name:String, value:String):Void; + + function entries():Iterator>>>; + function keys():Iterator; + function values():Iterator>>; + function forEach(callback:EitherType>->String->FormData->Void, ?thisArg:Dynamic):Void; +} diff --git a/src/js/node/web/Headers.hx b/src/js/node/web/Headers.hx new file mode 100644 index 00000000..a6b9bd8c --- /dev/null +++ b/src/js/node/web/Headers.hx @@ -0,0 +1,75 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.web; + +import haxe.DynamicAccess; +import haxe.extern.EitherType; +import js.node.Iterator; +import js.node.KeyValue; + +/** + HTTP headers for `fetch` / `Request` / `Response` (undici). + + @see https://nodejs.org/api/globals.html#class-headers +**/ +@:native("Headers") +extern class Headers { + @:overload(function(?init:Array>):Void {}) + @:overload(function(?init:DynamicAccess):Void {}) + function new(?init:Headers):Void; + + /** + Appends a new value onto an existing header, or adds the header if it does not exist. + **/ + function append(name:String, value:String):Void; + + /** + Deletes a header. + **/ + function delete(name:String):Void; + + /** + Returns the value of a header with the given name, or `null` if not present. + **/ + function get(name:String):Null; + + /** + Returns an array containing the values of all `Set-Cookie` headers. + **/ + function getSetCookie():Array; + + /** + Returns whether a header with the given name exists. + **/ + function has(name:String):Bool; + + /** + Sets a new value for an existing header, or adds the header if it does not exist. + **/ + function set(name:String, value:String):Void; + + function entries():Iterator>; + function keys():Iterator; + function values():Iterator; + function forEach(callback:String->String->Headers->Void, ?thisArg:Dynamic):Void; +} diff --git a/src/js/node/web/Request.hx b/src/js/node/web/Request.hx new file mode 100644 index 00000000..22a0a699 --- /dev/null +++ b/src/js/node/web/Request.hx @@ -0,0 +1,110 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.web; + +import haxe.DynamicAccess; +import haxe.extern.EitherType; +import js.node.url.URLSearchParams; +#if haxe4 +import js.lib.ArrayBuffer; +import js.lib.ArrayBufferView; +import js.lib.Promise; +import js.lib.Uint8Array; +#else +import js.html.ArrayBuffer; +import js.html.ArrayBufferView; +import js.Promise; +import js.html.Uint8Array; +#end + +/** + The Fetch API `Request` interface (undici). + + @see https://nodejs.org/api/globals.html#class-request +**/ +@:native("Request") +extern class Request { + var method(default, null):String; + var url(default, null):String; + var headers(default, null):Headers; + var destination(default, null):String; + var referrer(default, null):String; + var referrerPolicy(default, null):String; + var mode(default, null):String; + var credentials(default, null):String; + var cache(default, null):String; + var redirect(default, null):String; + var integrity(default, null):String; + var keepalive(default, null):Bool; + var signal(default, null):AbortSignal; + var duplex(default, null):String; + + /** + A `ReadableStream` of the body contents, or `null`. + Typed as `Dynamic` until web streams externs are added. + **/ + var body(default, null):Dynamic; + + var bodyUsed(default, null):Bool; + + @:overload(function(input:Request, ?init:RequestInit):Void {}) + function new(input:String, ?init:RequestInit):Void; + + function clone():Request; + function arrayBuffer():Promise; + function blob():Promise; + function bytes():Promise; + function formData():Promise; + function json():Promise; + function text():Promise; +} + +/** + Init options for `Request` / `fetch`. +**/ +typedef RequestInit = { + @:optional var method:String; + @:optional var headers:EitherType>, DynamicAccess>>; + @:optional var body:BodyInit; + @:optional var referrer:String; + @:optional var referrerPolicy:String; + @:optional var mode:String; + @:optional var credentials:String; + @:optional var cache:String; + @:optional var redirect:String; + @:optional var integrity:String; + @:optional var keepalive:Bool; + @:optional var signal:AbortSignal; + @:optional var duplex:String; + + /** + Undici-specific: custom dispatcher for the request. + **/ + @:optional var dispatcher:Dynamic; +} + +/** + Values accepted as request/response bodies. +**/ +typedef BodyInit = EitherType>>>>>; diff --git a/src/js/node/web/Response.hx b/src/js/node/web/Response.hx new file mode 100644 index 00000000..bdad2179 --- /dev/null +++ b/src/js/node/web/Response.hx @@ -0,0 +1,94 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.web; + +import haxe.DynamicAccess; +import haxe.extern.EitherType; +import js.node.web.Request.BodyInit; +#if haxe4 +import js.lib.ArrayBuffer; +import js.lib.Promise; +import js.lib.Uint8Array; +#else +import js.html.ArrayBuffer; +import js.Promise; +import js.html.Uint8Array; +#end + +/** + The Fetch API `Response` interface (undici). + + @see https://nodejs.org/api/globals.html#class-response +**/ +@:native("Response") +extern class Response { + /** + Returns a new `Response` object associated with a network error. + **/ + static function error():Response; + + /** + Creates a new response with a different URL. + **/ + static function redirect(url:String, ?status:Int):Response; + + /** + Creates a new response with a JSON body. + **/ + static function json(data:Dynamic, ?init:ResponseInit):Response; + + var type(default, null):String; + var url(default, null):String; + var redirected(default, null):Bool; + var status(default, null):Int; + var ok(default, null):Bool; + var statusText(default, null):String; + var headers(default, null):Headers; + + /** + A `ReadableStream` of the body contents, or `null`. + Typed as `Dynamic` until web streams externs are added. + **/ + var body(default, null):Dynamic; + + var bodyUsed(default, null):Bool; + + function new(?body:BodyInit, ?init:ResponseInit):Void; + + function clone():Response; + function arrayBuffer():Promise; + function blob():Promise; + function bytes():Promise; + function formData():Promise; + function json():Promise; + function text():Promise; +} + +/** + Init options for `Response`. +**/ +typedef ResponseInit = { + @:optional var status:Int; + @:optional var statusText:String; + @:optional var headers:EitherType>, DynamicAccess>>; +} From 679c4fdbc3fdb5388d75d5c3488688503f26bf76 Mon Sep 17 00:00:00 2001 From: tobil4sk Date: Mon, 13 Jul 2026 14:11:46 +0100 Subject: [PATCH 19/48] [ci] Add github action build workflow (#210) * [ci] Add github actions workflow * Require haxe 4+ officially Support for haxe 3 broke when switching to "enum abstract" instead of "@:enum abstract" * Fix macro for haxe 5 --- .github/workflows/main.yml | 27 +++++++++++++++++++++++++++ ImportAll.hx | 4 ++++ README.md | 2 +- 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 00000000..815c33f9 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,27 @@ +name: CI + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + haxe_ver: [4.0.5, 4.3.7, latest] + + steps: + - uses: actions/checkout@v7 + + - uses: krdlab/setup-haxe@v2 + with: + haxe-version: ${{ matrix.haxe_ver }} + + - run: haxelib dev hxnodejs . + name: Set haxelib dev path + + - run: | + haxe build.hxml + haxelib git dox https://github.com/HaxeFoundation/dox + haxe doc.hxml + name: Build + diff --git a/ImportAll.hx b/ImportAll.hx index fc2f4742..dc655e2e 100644 --- a/ImportAll.hx +++ b/ImportAll.hx @@ -24,6 +24,10 @@ class ImportAll { Context.getType(typePath); } } + #if haxe5 + Context.onAfterInitMacros(() -> loop([])); + #else loop([]); + #end } } diff --git a/README.md b/README.md index 984468b0..b3201abb 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [![Haxelib Downloads](https://badgen.net/haxelib/d/hxnodejs?color=blue)](https://lib.haxe.org/p/hxnodejs) [![Haxelib License](https://badgen.net/haxelib/license/hxnodejs)](LICENSE.md) -Extern type definitions for Node.JS. Haxe **3.4** or newer is required. +Extern type definitions for Node.JS. Haxe **4.0** or newer is required. Haxe-generated API documentation is available at http://haxefoundation.github.io/hxnodejs/js/Node.html. From c2ebfa5fce95f75ac22c46bdf1b3f704bc786e2c Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Mon, 13 Jul 2026 23:31:21 +1000 Subject: [PATCH 20/48] [ci] Retire Travis and deploy docs from GitHub Actions Move gh-pages API docs publish into a master-only Actions job and drop the legacy Travis config and deploy helpers. Partially addresses #188; Haxe test suite remains open. Co-authored-by: Cursor --- .github/workflows/main.yml | 27 +++++++++++++++++ .travis.yml | 23 -------------- README.md | 2 +- ci/Config.hx | 9 ------ ci/DeployGhPages.hx | 45 --------------------------- ci/Utils.hx | 62 -------------------------------------- deploy.hxml | 2 -- 7 files changed, 28 insertions(+), 142 deletions(-) delete mode 100644 .travis.yml delete mode 100644 ci/Config.hx delete mode 100644 ci/DeployGhPages.hx delete mode 100644 ci/Utils.hx delete mode 100644 deploy.hxml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 815c33f9..ffb06d4a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -25,3 +25,30 @@ jobs: haxe doc.hxml name: Build + deploy: + needs: build + if: github.ref == 'refs/heads/master' && github.event_name == 'push' + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v7 + + - uses: krdlab/setup-haxe@v2 + with: + haxe-version: latest + + - run: haxelib dev hxnodejs . + name: Set haxelib dev path + + - run: | + haxe build.hxml + haxelib git dox https://github.com/HaxeFoundation/dox + haxe doc.hxml + name: Build docs + + - name: Deploy to GitHub Pages + uses: peaceiris/actions-gh-pages@v4 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: bin/api diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 27009ef6..00000000 --- a/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -language: haxe -dist: xenial - -haxe: - - 3.4.7 - - development - -install: - haxelib dev hxnodejs . - -script: - - haxe build.hxml - - haxelib git dox https://github.com/HaxeFoundation/dox - - haxe doc.hxml - -deploy: - provider: script - script: haxe deploy.hxml - on: - branch: master - haxe: development - condition: $TRAVIS_EVENT_TYPE != "cron" - skip_cleanup: true diff --git a/README.md b/README.md index b3201abb..61f557c7 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # Haxe Node.JS -[![Build Status](https://badgen.net/travis/HaxeFoundation/hxnodejs?label=build)](https://travis-ci.org/HaxeFoundation/hxnodejs) +[![Build Status](https://img.shields.io/github/actions/workflow/status/HaxeFoundation/hxnodejs/main.yml?branch=master&label=build)](https://github.com/HaxeFoundation/hxnodejs/actions/workflows/main.yml) [![Haxelib Version](https://badgen.net/haxelib/v/hxnodejs)](https://lib.haxe.org/p/hxnodejs) [![Haxelib Downloads](https://badgen.net/haxelib/d/hxnodejs?color=blue)](https://lib.haxe.org/p/hxnodejs) [![Haxelib License](https://badgen.net/haxelib/license/hxnodejs)](LICENSE.md) diff --git a/ci/Config.hx b/ci/Config.hx deleted file mode 100644 index b082fba0..00000000 --- a/ci/Config.hx +++ /dev/null @@ -1,9 +0,0 @@ -import Utils.*; - -class Config { - static public var htmlDir = env("GHP_HTMLDIR", "bin/api"); - static public var remote = env("GHP_REMOTE", null); // should be in the form of https://token@github.com/account/repo.git - static public var branch = env("GHP_BRANCH", "gh-pages"); - static public var username = env("GHP_USERNAME", null); - static public var email = env("GHP_EMAIL", null); -} diff --git a/ci/DeployGhPages.hx b/ci/DeployGhPages.hx deleted file mode 100644 index d3282e29..00000000 --- a/ci/DeployGhPages.hx +++ /dev/null @@ -1,45 +0,0 @@ -import Sys.*; -import Utils.*; -import Config.*; - -using StringTools; - -class DeployGhPages { - static function main():Void { - var root = getCwd(); - - if (remote == null) { - println('GHP_REMOTE is not set, skip deploy.'); - return; - } - - // TravisCI by default clones repositories to a depth of 50 commits. - // We need a full clone inorder to do git operations. - runCommand("git", ["fetch", "--unshallow"]); - - var sha = commandOutput("git", ["rev-parse", "HEAD"]).trim(); - - setCwd(htmlDir); - runCommand("git", ["init"]); - if (username != null) - runCommand("git", ["config", "--local", "user.name", username]); - if (email != null) - runCommand("git", ["config", "--local", "user.email", email]); - runCommand("git", ["remote", "add", "local", root]); - runCommand("git", ["remote", "add", "remote", remote]); - runCommand("git", ["fetch", "--all"]); - runCommand("git", ["checkout", "--orphan", branch]); - if (commandOutput("git", ["ls-remote", "--heads", "local", branch]).trim() != "") { - runCommand("git", ["reset", "--soft", 'local/${branch}']); - } - if (commandOutput("git", ["ls-remote", "--heads", "remote", branch]).trim() != "") { - runCommand("git", ["reset", "--soft", 'remote/${branch}']); - } - runCommand("git", ["add", "--all"]); - runCommand("git", ["commit", "--allow-empty", "--quiet", "-m", 'deploy for ${sha}']); - runCommand("git", ["push", "local", branch]); - - setCwd(root); - runCommand("git", ["push", remote, branch]); - } -} diff --git a/ci/Utils.hx b/ci/Utils.hx deleted file mode 100644 index 73e77176..00000000 --- a/ci/Utils.hx +++ /dev/null @@ -1,62 +0,0 @@ -import Sys.*; -import sys.io.*; -import sys.FileSystem.*; -import haxe.io.*; - -class Utils { - static public function env(name:String, def:String):String { - return switch (getEnv(name)) { - case null: - def; - case v: - v; - } - } - - static public function runCommand(cmd:String, args:Array):Void { - println('run: $cmd $args'); - switch (command(cmd, args)) { - case 0: - // pass - case exitCode: - exit(exitCode); - } - } - - static public function commandOutput(cmd:String, args:Array):String { - var p = new Process(cmd, args); - var exitCode = p.exitCode(); - var output = p.stdout.readAll().toString(); - p.close(); - if (exitCode != 0) - exit(exitCode); - return output; - } - - static public function copyRecursive(src:String, dest:String):Void { - if (isDirectory(src)) { - createDirectory(dest); - for (item in readDirectory(src)) { - var srcPath = Path.join([src, item]); - var destPath = Path.join([dest, item]); - copyRecursive(srcPath, destPath); - } - } else { - File.copy(src, dest); - } - } - - static public function deleteRecursive(path:String):Void { - if (!exists(path)) - return; - - if (isDirectory(path)) { - for (item in readDirectory(path)) { - deleteRecursive(Path.join([path, item])); - } - deleteDirectory(path); - } else { - deleteFile(path); - } - } -} diff --git a/deploy.hxml b/deploy.hxml deleted file mode 100644 index cc9859db..00000000 --- a/deploy.hxml +++ /dev/null @@ -1,2 +0,0 @@ --cp ci ---run DeployGhPages \ No newline at end of file From 26c8cd0f190cfd1f7de070107dde5ac634dc993d Mon Sep 17 00:00:00 2001 From: Tobiasz Laskowski Date: Mon, 13 Jul 2026 14:21:20 +0100 Subject: [PATCH 21/48] [ci] Use official github pages deployment actions --- .github/workflows/main.yml | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ffb06d4a..dd59c75d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -25,30 +25,26 @@ jobs: haxe doc.hxml name: Build + - name: Upload artifact + if: matrix.haxe_ver == 'latest' + uses: actions/upload-pages-artifact@v3 + with: + path: ./bin/api + deploy: needs: build if: github.ref == 'refs/heads/master' && github.event_name == 'push' runs-on: ubuntu-latest permissions: - contents: write - steps: - - uses: actions/checkout@v7 - - - uses: krdlab/setup-haxe@v2 - with: - haxe-version: latest - - - run: haxelib dev hxnodejs . - name: Set haxelib dev path + contents: read + pages: write + id-token: write - - run: | - haxe build.hxml - haxelib git dox https://github.com/HaxeFoundation/dox - haxe doc.hxml - name: Build docs + environment: + name: github-pages + url: ${{steps.deployment.outputs.page_url}} + steps: + - name: Deploy artifact + id: deployment + uses: actions/deploy-pages@v4 - - name: Deploy to GitHub Pages - uses: peaceiris/actions-gh-pages@v4 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: bin/api From 8f683b5cae5787fae30a49f621da7fc9aa4751c0 Mon Sep 17 00:00:00 2001 From: tobil4sk Date: Tue, 14 Jul 2026 01:21:36 +0100 Subject: [PATCH 22/48] [ci] Add test workflow (#223) * [ci] Run haxe repo tests * [ci] Disable test fail fast * [ci] Remove 4.0.5 from test matrix UTest requires 4.1.0 or above * [ci] Use node 24 for tests on windows Older versions of node have a buggy libuv that crashes on certain boundary unicode characters. * [ci] Wait for tests before deploying --- .github/workflows/main.yml | 52 +++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index dd59c75d..94fe2f92 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -31,8 +31,58 @@ jobs: with: path: ./bin/api + test: + strategy: + fail-fast: false + matrix: + haxe_ver: [4.3.7, latest] + runner: [ubuntu-latest, windows-latest, macos-latest] + runs-on: ${{ matrix.runner }} + steps: + - uses: actions/checkout@v7 + + - uses: krdlab/setup-haxe@v2 + with: + haxe-version: ${{ matrix.haxe_ver }} + + - uses: actions/setup-node@v4 + if: matrix.runner == 'windows-latest' + with: + node-version: 24 + + - run: haxelib dev hxnodejs . + name: Set haxelib dev path + + - name: Get Haxe commit SHA + id: haxe_sha + shell: bash + env: + GH_TOKEN: ${{ github.token }} + run: | + version=$(haxe --version) + echo "Haxe version: $version" + short_sha=${version##*+} + full_sha=$(gh api repos/HaxeFoundation/haxe/commits/$short_sha --jq '.sha') + echo "Commit SHA: $short_sha, Full SHA: $full_sha" + echo "sha=$full_sha" >> "$GITHUB_OUTPUT" + + - uses: actions/checkout@v7 + with: + repository: HaxeFoundation/haxe + path: haxe + ref: ${{ steps.haxe_sha.outputs.sha }} + + - name: Test + shell: bash + run: | + if [[ "${{ matrix.runner }}" == "macos-latest" ]]; then + echo "" > sys/compile-fs.hxml + fi + haxe --run RunCi js + working-directory: haxe/tests + deploy: - needs: build + needs: [build, test] if: github.ref == 'refs/heads/master' && github.event_name == 'push' runs-on: ubuntu-latest permissions: From a72b250b90a21b1ebe2bc86557b452caa771d94d Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Tue, 14 Jul 2026 14:52:51 +1000 Subject: [PATCH 23/48] Add diagnostics_channel module externs (#213) * Add diagnostics_channel module externs for Node.js 24 LTS. Co-authored-by: Cursor * Fix TracingChannel extern: drop invalid jsRequire and lock channel fields. TracingChannel is not exported from diagnostics_channel, so treat it like fs.Stats; mark start/end/asyncStart/asyncEnd/error as (default, never). Co-authored-by: Cursor --------- Co-authored-by: Cursor --- src/js/node/DiagnosticsChannel.hx | 91 +++++++++ src/js/node/diagnostics_channel/Channel.hx | 154 ++++++++++++++ .../diagnostics_channel/TracingChannel.hx | 190 ++++++++++++++++++ 3 files changed, 435 insertions(+) create mode 100644 src/js/node/DiagnosticsChannel.hx create mode 100644 src/js/node/diagnostics_channel/Channel.hx create mode 100644 src/js/node/diagnostics_channel/TracingChannel.hx diff --git a/src/js/node/DiagnosticsChannel.hx b/src/js/node/DiagnosticsChannel.hx new file mode 100644 index 00000000..7b4a815c --- /dev/null +++ b/src/js/node/DiagnosticsChannel.hx @@ -0,0 +1,91 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node; + +import haxe.extern.EitherType; +import js.node.diagnostics_channel.Channel; +import js.node.diagnostics_channel.Channel.ChannelListener; +import js.node.diagnostics_channel.Channel.ChannelName; +import js.node.diagnostics_channel.TracingChannel; +import js.node.diagnostics_channel.TracingChannel.TracingChannelChannels; + +/** + The `diagnostics_channel` module provides an API to create named channels + to report arbitrary message data for diagnostics purposes. + + @see https://nodejs.org/docs/latest-v24.x/api/diagnostics_channel.html +**/ +@:jsRequire("diagnostics_channel") +extern class DiagnosticsChannel { + /** + Check if there are active subscribers to the named channel. + This is helpful if the message you want to send might be expensive to prepare. + + This API is optional but helpful when trying to publish messages from very + performance-sensitive code. + + @see https://nodejs.org/docs/latest-v24.x/api/diagnostics_channel.html#diagnostics_channelhassubscribersname + **/ + static function hasSubscribers(name:ChannelName):Bool; + + /** + This is the primary entry-point for anyone wanting to publish to a named channel. + It produces a channel object which is optimized to reduce overhead at publish time + as much as possible. + + @see https://nodejs.org/docs/latest-v24.x/api/diagnostics_channel.html#diagnostics_channelchannelname + **/ + static function channel(name:ChannelName):Channel; + + /** + Register a message handler to subscribe to this channel. + This message handler will be run synchronously whenever a message is published + to the channel. Any errors thrown in the message handler will trigger an + `'uncaughtException'`. + + @see https://nodejs.org/docs/latest-v24.x/api/diagnostics_channel.html#diagnostics_channelsubscribename-onmessage + **/ + static function subscribe(name:ChannelName, onMessage:ChannelListener):Void; + + /** + Remove a message handler previously registered to this channel with + `DiagnosticsChannel.subscribe(name, onMessage)`. + + Returns `true` if the handler was found, `false` otherwise. + + @see https://nodejs.org/docs/latest-v24.x/api/diagnostics_channel.html#diagnostics_channelunsubscribename-onmessage + **/ + static function unsubscribe(name:ChannelName, onMessage:ChannelListener):Bool; + + /** + Creates a `TracingChannel` wrapper for the given TracingChannel Channels. + If a name is given, the corresponding tracing channels will be created in the + form of `tracing:${name}:${eventType}` where `eventType` corresponds to the + types of TracingChannel Channels. + + Stability: 1 - Experimental + + @see https://nodejs.org/docs/latest-v24.x/api/diagnostics_channel.html#diagnostics_channeltracingchannelnameorchannels + **/ + static function tracingChannel(nameOrChannels:EitherType):TracingChannel; +} diff --git a/src/js/node/diagnostics_channel/Channel.hx b/src/js/node/diagnostics_channel/Channel.hx new file mode 100644 index 00000000..e586268b --- /dev/null +++ b/src/js/node/diagnostics_channel/Channel.hx @@ -0,0 +1,154 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.diagnostics_channel; + +import haxe.Constraints.Function; +import haxe.extern.EitherType; +import haxe.extern.Rest; +#if haxe4 +import js.lib.Symbol; +#end + +/** + Channel name: a string or symbol. +**/ +#if haxe4 +typedef ChannelName = EitherType; +#else +typedef ChannelName = EitherType; +#end + +/** + Handler invoked when a message is published on a channel. + + Arguments: + * `message` - The message data + * `name` - The name of the channel +**/ +typedef ChannelListener = Dynamic->ChannelName->Void; + +/** + Transform function used by `Channel.bindStore` to convert published context + data into the value stored in an `AsyncLocalStorage` instance. +**/ +typedef ChannelStoreTransform = Dynamic->Dynamic; + +/** + The class `Channel` represents an individual named channel within the data pipeline. + It is used to track subscribers and to publish messages when there are subscribers present. + + Channels are created with `DiagnosticsChannel.channel(name)`; constructing a channel + directly with `new Channel(name)` is not supported. + + @see https://nodejs.org/docs/latest-v24.x/api/diagnostics_channel.html#class-channel +**/ +@:jsRequire("diagnostics_channel", "Channel") +extern class Channel { + private function new(); + + /** + The channel name. + **/ + var name(default, never):ChannelName; + + /** + Check if there are active subscribers to this channel. + This is helpful if the message you want to send might be expensive to prepare. + + @see https://nodejs.org/docs/latest-v24.x/api/diagnostics_channel.html#channelhassubscribers + **/ + var hasSubscribers(default, never):Bool; + + /** + Publish a message to any subscribers to the channel. + This will trigger message handlers synchronously so they will execute within + the same context. + + @see https://nodejs.org/docs/latest-v24.x/api/diagnostics_channel.html#channelpublishmessage + **/ + function publish(message:Dynamic):Void; + + /** + Register a message handler to subscribe to this channel. + This message handler will be run synchronously whenever a message is published + to the channel. Any errors thrown in the message handler will trigger an + `'uncaughtException'`. + + @see https://nodejs.org/docs/latest-v24.x/api/diagnostics_channel.html#channelsubscribeonmessage + **/ + function subscribe(onMessage:ChannelListener):Void; + + /** + Remove a message handler previously registered to this channel with + `channel.subscribe(onMessage)`. + + Returns `true` if the handler was found, `false` otherwise. + + @see https://nodejs.org/docs/latest-v24.x/api/diagnostics_channel.html#channelunsubscribeonmessage + **/ + function unsubscribe(onMessage:ChannelListener):Bool; + + /** + When `channel.runStores(context, ...)` is called, the given context data will be + applied to any store bound to the channel. If the store has already been bound + the previous `transform` function will be replaced with the new one. + The `transform` function may be omitted to set the given context data as the + context directly. + + `store` should be an `AsyncLocalStorage` instance from `node:async_hooks`. + + Stability: 1 - Experimental + + @see https://nodejs.org/docs/latest-v24.x/api/diagnostics_channel.html#channelbindstorestore-transform + **/ + function bindStore(store:Dynamic, ?transform:ChannelStoreTransform):Void; + + /** + Remove a store previously bound to this channel with `channel.bindStore(store)`. + + `store` should be an `AsyncLocalStorage` instance from `node:async_hooks`. + + Returns `true` if the store was found, `false` otherwise. + + Stability: 1 - Experimental + + @see https://nodejs.org/docs/latest-v24.x/api/diagnostics_channel.html#channelunbindstorestore + **/ + function unbindStore(store:Dynamic):Bool; + + /** + Applies the given data to any AsyncLocalStorage instances bound to the channel + for the duration of the given function, then publishes to the channel within + the scope of that data is applied to the stores. + + If a transform function was given to `channel.bindStore(store)` it will be + applied to transform the message data before it becomes the context value for + the store. The prior storage context is accessible from within the transform + function in cases where context linking is required. + + Stability: 1 - Experimental + + @see https://nodejs.org/docs/latest-v24.x/api/diagnostics_channel.html#channelrunstorescontext-fn-thisarg-args + **/ + function runStores(context:Dynamic, fn:Function, ?thisArg:Dynamic, args:Rest):Dynamic; +} diff --git a/src/js/node/diagnostics_channel/TracingChannel.hx b/src/js/node/diagnostics_channel/TracingChannel.hx new file mode 100644 index 00000000..7a8f64ff --- /dev/null +++ b/src/js/node/diagnostics_channel/TracingChannel.hx @@ -0,0 +1,190 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.diagnostics_channel; + +import haxe.Constraints.Function; +import haxe.extern.Rest; +#if haxe4 +import js.lib.Promise; +#else +import js.Promise; +#end + +/** + Object containing all the TracingChannel Channels, passed to + `DiagnosticsChannel.tracingChannel`. +**/ +typedef TracingChannelChannels = { + var start:Channel; + var end:Channel; + var asyncStart:Channel; + var asyncEnd:Channel; + var error:Channel; +} + +/** + Handler for a tracing channel message. +**/ +typedef TracingChannelMessageHandler = Dynamic->Void; + +/** + Set of TracingChannel Channels subscribers. + Each handler is optional; omit any that are not needed. +**/ +typedef TracingChannelSubscribers = { + /** + The `start` event subscriber. + **/ + @:optional var start:TracingChannelMessageHandler; + + /** + The `end` event subscriber. + **/ + @:optional var end:TracingChannelMessageHandler; + + /** + The `asyncStart` event subscriber. + **/ + @:optional var asyncStart:TracingChannelMessageHandler; + + /** + The `asyncEnd` event subscriber. + **/ + @:optional var asyncEnd:TracingChannelMessageHandler; + + /** + The `error` event subscriber. + **/ + @:optional var error:TracingChannelMessageHandler; +} + +/** + The class `TracingChannel` is a collection of TracingChannel Channels which + together express a single traceable action. It is used to formalize and simplify + the process of producing events for tracing application flow. + + `DiagnosticsChannel.tracingChannel()` is used to construct a `TracingChannel`. + As with `Channel` it is recommended to create and reuse a single `TracingChannel` + at the top-level of the file rather than creating them dynamically. + + Stability: 1 - Experimental + + @see https://nodejs.org/docs/latest-v24.x/api/diagnostics_channel.html#class-tracingchannel +**/ +extern class TracingChannel { + private function new(); + + /** + Channel for the `start` event (`tracing:${name}:start`). + **/ + var start(default, never):Channel; + + /** + Channel for the `end` event (`tracing:${name}:end`). + **/ + var end(default, never):Channel; + + /** + Channel for the `asyncStart` event (`tracing:${name}:asyncStart`). + **/ + var asyncStart(default, never):Channel; + + /** + Channel for the `asyncEnd` event (`tracing:${name}:asyncEnd`). + **/ + var asyncEnd(default, never):Channel; + + /** + Channel for the `error` event (`tracing:${name}:error`). + **/ + var error(default, never):Channel; + + /** + This is a helper to check if any of the TracingChannel Channels have subscribers. + Returns `true` if any of them have at least one subscriber, `false` otherwise. + + @see https://nodejs.org/docs/latest-v24.x/api/diagnostics_channel.html#tracingchannelhassubscribers + **/ + var hasSubscribers(default, never):Bool; + + /** + Helper to subscribe a collection of functions to the corresponding channels. + This is the same as calling `channel.subscribe(onMessage)` on each channel + individually. + + @see https://nodejs.org/docs/latest-v24.x/api/diagnostics_channel.html#tracingchannelsubscribesubscribers + **/ + function subscribe(subscribers:TracingChannelSubscribers):Void; + + /** + Helper to unsubscribe a collection of functions from the corresponding channels. + This is the same as calling `channel.unsubscribe(onMessage)` on each channel + individually. + + Returns `true` if all handlers were successfully unsubscribed, and `false` otherwise. + + @see https://nodejs.org/docs/latest-v24.x/api/diagnostics_channel.html#tracingchannelunsubscribesubscribers + **/ + function unsubscribe(subscribers:TracingChannelSubscribers):Bool; + + /** + Trace a synchronous function call. This will always produce a `start` event and + `end` event around the execution and may produce an `error` event if the given + function throws an error. + + This will run the given function using `channel.runStores(context, ...)` on the + `start` channel which ensures all events should have any bound stores set to + match this trace context. + + @see https://nodejs.org/docs/latest-v24.x/api/diagnostics_channel.html#tracingchanneltracesyncfn-context-thisarg-args + **/ + function traceSync(fn:Function, ?context:{}, ?thisArg:Dynamic, args:Rest):Dynamic; + + /** + Trace a promise-returning function call. This will always produce a `start` + event and `end` event around the synchronous portion of the function execution, + and will produce an `asyncStart` event and `asyncEnd` event when a promise + continuation is reached. It may also produce an `error` event if the given + function throws an error or the returned promise rejects. + + @see https://nodejs.org/docs/latest-v24.x/api/diagnostics_channel.html#tracingchanneltracepromisefn-context-thisarg-args + **/ + function tracePromise(fn:Function, ?context:{}, ?thisArg:Dynamic, args:Rest):Promise; + + /** + Trace a callback-receiving function call. The callback is expected to follow + the error as first arg convention typically used. + + This will always produce a `start` event and `end` event around the synchronous + portion of the function execution, and will produce a `asyncStart` event and + `asyncEnd` event around the callback execution. It may also produce an `error` + event if the given function throws or the first argument passed to the callback + is set. + + `position` is the zero-indexed argument position of the expected callback + (defaults to last argument if `undefined` is passed). + + @see https://nodejs.org/docs/latest-v24.x/api/diagnostics_channel.html#tracingchanneltracecallbackfn-position-context-thisarg-args + **/ + function traceCallback(fn:Function, ?position:Int, ?context:{}, ?thisArg:Dynamic, args:Rest):Dynamic; +} From c0e3eefda08b8620d3695cb34d21baa27bae0fa3 Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Tue, 14 Jul 2026 15:23:33 +1000 Subject: [PATCH 24/48] Add perf_hooks module externs and global performance (#216) * Add perf_hooks externs and wire global performance. Expose Node.js performance measurement APIs under js.node.PerfHooks / js.node.perf_hooks and add the global performance getter on js.Node. Co-authored-by: Cursor * Fix Performance measure overloads and option typedef imports. Drop the unused PerformanceObserver module re-export and resolve PerformanceMarkOptions/PerformanceMeasureOptions across modules. Co-authored-by: Cursor * Document dual-LTS availability of module-level perf_hooks aliases. Mark eventLoopUtilization and timerify as Added in v24.12.0 and prefer Performance methods on Node 22. Co-authored-by: Cursor --------- Co-authored-by: Cursor --- src/js/Node.hx | 18 ++ src/js/node/PerfHooks.hx | 168 ++++++++++++++++ src/js/node/perf_hooks/Histogram.hx | 111 +++++++++++ src/js/node/perf_hooks/IntervalHistogram.hx | 42 ++++ src/js/node/perf_hooks/Performance.hx | 179 ++++++++++++++++++ src/js/node/perf_hooks/PerformanceEntry.hx | 57 ++++++ .../node/perf_hooks/PerformanceEntryType.hx | 60 ++++++ src/js/node/perf_hooks/PerformanceMark.hx | 54 ++++++ src/js/node/perf_hooks/PerformanceMeasure.hx | 65 +++++++ .../node/perf_hooks/PerformanceNodeEntry.hx | 57 ++++++ .../node/perf_hooks/PerformanceNodeTiming.hx | 102 ++++++++++ src/js/node/perf_hooks/PerformanceObserver.hx | 89 +++++++++ .../PerformanceObserverEntryList.hx | 51 +++++ .../perf_hooks/PerformanceResourceTiming.hx | 122 ++++++++++++ src/js/node/perf_hooks/RecordableHistogram.hx | 48 +++++ 15 files changed, 1223 insertions(+) create mode 100644 src/js/node/PerfHooks.hx create mode 100644 src/js/node/perf_hooks/Histogram.hx create mode 100644 src/js/node/perf_hooks/IntervalHistogram.hx create mode 100644 src/js/node/perf_hooks/Performance.hx create mode 100644 src/js/node/perf_hooks/PerformanceEntry.hx create mode 100644 src/js/node/perf_hooks/PerformanceEntryType.hx create mode 100644 src/js/node/perf_hooks/PerformanceMark.hx create mode 100644 src/js/node/perf_hooks/PerformanceMeasure.hx create mode 100644 src/js/node/perf_hooks/PerformanceNodeEntry.hx create mode 100644 src/js/node/perf_hooks/PerformanceNodeTiming.hx create mode 100644 src/js/node/perf_hooks/PerformanceObserver.hx create mode 100644 src/js/node/perf_hooks/PerformanceObserverEntryList.hx create mode 100644 src/js/node/perf_hooks/PerformanceResourceTiming.hx create mode 100644 src/js/node/perf_hooks/RecordableHistogram.hx diff --git a/src/js/Node.hx b/src/js/Node.hx index c414c062..be786d76 100644 --- a/src/js/Node.hx +++ b/src/js/Node.hx @@ -29,6 +29,7 @@ import js.node.Process; import js.node.Timers.Immediate; import js.node.Timers.Timeout; import js.node.console.Console; +import js.node.perf_hooks.Performance; #if haxe4 import js.Syntax.code; #end @@ -156,6 +157,23 @@ extern class Node { #end } + /** + An object that can be used to collect performance metrics from the current Node.js instance. + It is similar to `window.performance` in browsers. + + @see https://nodejs.org/api/globals.html#performance + @see https://nodejs.org/api/perf_hooks.html#perf_hooksperformance + **/ + static var performance(get, never):Performance; + + private static inline function get_performance():Performance { + #if haxe4 + return code("performance"); + #else + return untyped __js__("performance"); + #end + } + /** The `queueMicrotask()` method queues a microtask to invoke `callback`. If `callback` throws an exception, the [process object](https://nodejs.org/api/process.html#process_process) 'uncaughtException' event will be emitted. diff --git a/src/js/node/PerfHooks.hx b/src/js/node/PerfHooks.hx new file mode 100644 index 00000000..0428b59d --- /dev/null +++ b/src/js/node/PerfHooks.hx @@ -0,0 +1,168 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node; + +import haxe.Constraints.Function; +import haxe.extern.EitherType; +import js.node.perf_hooks.IntervalHistogram; +import js.node.perf_hooks.Performance; +import js.node.perf_hooks.RecordableHistogram; + +/** + This module provides an implementation of a subset of the W3C Web Performance APIs + as well as additional APIs for Node.js-specific performance measurements. + + @see https://nodejs.org/api/perf_hooks.html +**/ +@:jsRequire("perf_hooks") +extern class PerfHooks { + /** + An object that can be used to collect performance metrics from the current Node.js instance. + It is similar to `window.performance` in browsers. + + @see https://nodejs.org/api/perf_hooks.html#perf_hooksperformance + **/ + static var performance(default, never):Performance; + + /** + Constants for garbage collection kinds and flags. + + @see https://nodejs.org/api/perf_hooks.html#perf_hooksconstants + **/ + static var constants(default, never):PerfHooksConstants; + + /** + Returns a `RecordableHistogram`. + + @see https://nodejs.org/api/perf_hooks.html#perf_hookscreatehistogramoptions + **/ + static function createHistogram(?options:CreateHistogramOptions):RecordableHistogram; + + /** + Creates an `IntervalHistogram` object that samples and reports the event loop delay over time. + The delays will be reported in nanoseconds. + + This property is an extension by Node.js. It is not available in Web browsers. + + @see https://nodejs.org/api/perf_hooks.html#perf_hooksmonitoreventloopdelayoptions + **/ + static function monitorEventLoopDelay(?options:EventLoopMonitorOptions):IntervalHistogram; + + /** + Returns an object that contains the cumulative duration of time the event loop has been + both idle and active as a high resolution milliseconds timer. + + Module-level alias of `performance.eventLoopUtilization()`. + Added in Node.js v24.12.0 (Active LTS). On earlier versions (including Node.js 22 LTS) + this is `undefined` at module level; use `PerfHooks.performance.eventLoopUtilization` instead. + + @see https://nodejs.org/api/perf_hooks.html#perf_hookseventlooputilizationutilization1-utilization2 + **/ + static function eventLoopUtilization(?utilization1:EventLoopUtilization, ?utilization2:EventLoopUtilization):EventLoopUtilization; + + /** + Wraps a function within a new function that measures the running time of the wrapped function. + A `PerformanceObserver` must be subscribed to the `'function'` event type in order for the + timing details to be accessed. + + This property is an extension by Node.js. It is not available in Web browsers. + + Module-level alias of `performance.timerify()`. + Added in Node.js v24.12.0 (Active LTS). On earlier versions (including Node.js 22 LTS) + this is `undefined` at module level; use `PerfHooks.performance.timerify` instead. + + @see https://nodejs.org/api/perf_hooks.html#perf_hookstimerifyfn-options + **/ + static function timerify(fn:T, ?options:TimerifyOptions):T; +} + +/** + Constants exported by `perf_hooks.constants`. +**/ +typedef PerfHooksConstants = { + var NODE_PERFORMANCE_GC_MAJOR(default, never):Int; + var NODE_PERFORMANCE_GC_MINOR(default, never):Int; + var NODE_PERFORMANCE_GC_INCREMENTAL(default, never):Int; + var NODE_PERFORMANCE_GC_WEAKCB(default, never):Int; + var NODE_PERFORMANCE_GC_FLAGS_NO(default, never):Int; + var NODE_PERFORMANCE_GC_FLAGS_CONSTRUCT_RETAINED(default, never):Int; + var NODE_PERFORMANCE_GC_FLAGS_FORCED(default, never):Int; + var NODE_PERFORMANCE_GC_FLAGS_SYNCHRONOUS_PHANTOM_PROCESSING(default, never):Int; + var NODE_PERFORMANCE_GC_FLAGS_ALL_AVAILABLE_GARBAGE(default, never):Int; + var NODE_PERFORMANCE_GC_FLAGS_ALL_EXTERNAL_MEMORY(default, never):Int; + var NODE_PERFORMANCE_GC_FLAGS_SCHEDULE_IDLE(default, never):Int; +} + +/** + Options for `PerfHooks.createHistogram`. +**/ +typedef CreateHistogramOptions = { + /** + The lowest discernible value. Must be an integer value greater than 0. + Default: `1`. + **/ + @:optional var lowest:EitherType; + + /** + The highest recordable value. Must be an integer value that is equal to or greater than + two times `lowest`. Default: `Number.MAX_SAFE_INTEGER`. + **/ + @:optional var highest:EitherType; + + /** + The number of accuracy digits. Must be a number between `1` and `5`. + Default: `3`. + **/ + @:optional var figures:Int; +} + +/** + Options for `PerfHooks.monitorEventLoopDelay`. +**/ +typedef EventLoopMonitorOptions = { + /** + The sampling rate in milliseconds. Must be greater than zero. + Default: `10`. + **/ + @:optional var resolution:Float; +} + +/** + Result of `PerfHooks.eventLoopUtilization` / `Performance.eventLoopUtilization`. +**/ +typedef EventLoopUtilization = { + var idle:Float; + var active:Float; + var utilization:Float; +} + +/** + Options for `PerfHooks.timerify` / `Performance.timerify`. +**/ +typedef TimerifyOptions = { + /** + A histogram object created using `PerfHooks.createHistogram()` that will record + runtime durations in nanoseconds. + **/ + @:optional var histogram:RecordableHistogram; +} diff --git a/src/js/node/perf_hooks/Histogram.hx b/src/js/node/perf_hooks/Histogram.hx new file mode 100644 index 00000000..b5247d35 --- /dev/null +++ b/src/js/node/perf_hooks/Histogram.hx @@ -0,0 +1,111 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.perf_hooks; + +/** + Histogram for recording values (e.g. event loop delay samples). + + The constructor of this class is not exposed to users. + + @see https://nodejs.org/api/perf_hooks.html#class-histogram +**/ +extern class Histogram { + /** + The number of samples recorded by the histogram. + **/ + var count(default, null):Float; + + /** + The number of samples recorded by the histogram (as a BigInt). + **/ + var countBigInt(default, null):Dynamic; + + /** + The number of times the event loop delay exceeded the maximum 1 hour event loop delay threshold. + **/ + var exceeds(default, null):Float; + + /** + The number of times the event loop delay exceeded the maximum 1 hour event loop delay threshold (as a BigInt). + **/ + var exceedsBigInt(default, null):Dynamic; + + /** + The maximum recorded event loop delay. + **/ + var max(default, null):Float; + + /** + The maximum recorded event loop delay (as a BigInt). + **/ + var maxBigInt(default, null):Dynamic; + + /** + The mean of the recorded event loop delays. + **/ + var mean(default, null):Float; + + /** + The minimum recorded event loop delay. + **/ + var min(default, null):Float; + + /** + The minimum recorded event loop delay (as a BigInt). + **/ + var minBigInt(default, null):Dynamic; + + /** + Returns the value at the given percentile. + + @param percentile A percentile value in the range (0, 100]. + **/ + function percentile(percentile:Float):Float; + + /** + Returns the value at the given percentile (as a BigInt). + + @param percentile A percentile value in the range (0, 100]. + **/ + function percentileBigInt(percentile:Float):Dynamic; + + /** + Returns a `Map` object detailing the accumulated percentile distribution. + **/ + var percentiles(default, null):Dynamic; + + /** + Returns a `Map` object detailing the accumulated percentile distribution (BigInt keys/values). + **/ + var percentilesBigInt(default, null):Dynamic; + + /** + Resets the collected histogram data. + **/ + function reset():Void; + + /** + The standard deviation of the recorded event loop delays. + **/ + var stddev(default, null):Float; +} diff --git a/src/js/node/perf_hooks/IntervalHistogram.hx b/src/js/node/perf_hooks/IntervalHistogram.hx new file mode 100644 index 00000000..4bf79365 --- /dev/null +++ b/src/js/node/perf_hooks/IntervalHistogram.hx @@ -0,0 +1,42 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.perf_hooks; + +/** + A `Histogram` that is periodically updated on a given interval. + + @see https://nodejs.org/api/perf_hooks.html#class-intervalhistogram-extends-histogram +**/ +extern class IntervalHistogram extends Histogram { + /** + Disables the update interval timer. Returns `true` if the timer was stopped, + `false` if it was already stopped. + **/ + function disable():Bool; + + /** + Enables the update interval timer. Returns `true` if the timer was started, + `false` if it was already started. + **/ + function enable():Bool; +} diff --git a/src/js/node/perf_hooks/Performance.hx b/src/js/node/perf_hooks/Performance.hx new file mode 100644 index 00000000..41b635e9 --- /dev/null +++ b/src/js/node/perf_hooks/Performance.hx @@ -0,0 +1,179 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.perf_hooks; + +import haxe.Constraints.Function; +import haxe.extern.EitherType; +import js.node.PerfHooks.EventLoopUtilization; +import js.node.PerfHooks.TimerifyOptions; +import js.node.web.Event; +import js.node.web.EventTarget; + +// Bring secondary types (option typedefs) from sibling modules into scope. +import js.node.perf_hooks.PerformanceMark; +import js.node.perf_hooks.PerformanceMeasure; + +/** + Events emitted by `Performance` (via `EventTarget`). +**/ +enum abstract PerformanceEvent(String) from String to String { + /** + The `'resourcetimingbufferfull'` event is fired when the global performance resource + timing buffer is full. Adjust resource timing buffer size with + `performance.setResourceTimingBufferSize()` or clear the buffer with + `performance.clearResourceTimings()` in the event listener to allow more entries + to be added to the performance timeline buffer. + + @see https://nodejs.org/api/perf_hooks.html#event-resourcetimingbufferfull + **/ + var ResourceTimingBufferFull = "resourcetimingbufferfull"; +} + +/** + An object that can be used to collect performance metrics from the current Node.js instance. + It is similar to `window.performance` in browsers. + + Extends `EventTarget` (not `EventEmitter`). + + @see https://nodejs.org/api/perf_hooks.html#class-performance + @see https://nodejs.org/api/globals.html#performance +**/ +@:jsRequire("perf_hooks", "Performance") +extern class Performance extends EventTarget { + /** + If `name` is not provided, removes all `PerformanceMark` objects from the Performance Timeline. + If `name` is provided, removes only the named mark. + **/ + function clearMarks(?name:String):Void; + + /** + If `name` is not provided, removes all `PerformanceMeasure` objects from the Performance Timeline. + If `name` is provided, removes only the named measure. + **/ + function clearMeasures(?name:String):Void; + + /** + If `name` is not provided, removes all `PerformanceResourceTiming` objects from the Resource Timeline. + If `name` is provided, removes only the named resource. + **/ + function clearResourceTimings(?name:String):Void; + + /** + Returns an object that contains the cumulative duration of time the event loop has been + both idle and active as a high resolution milliseconds timer. + + This property is an extension by Node.js. It is not available in Web browsers. + + Prefer this over the module-level `PerfHooks.eventLoopUtilization` when targeting + Node.js 22 LTS as well as Node.js 24; the module-level alias was only added in v24.12.0. + **/ + function eventLoopUtilization(?utilization1:EventLoopUtilization, ?utilization2:EventLoopUtilization):EventLoopUtilization; + + /** + Returns a list of `PerformanceEntry` objects in chronological order with respect to + `performanceEntry.startTime`. + **/ + function getEntries():Array; + + /** + Returns a list of `PerformanceEntry` objects in chronological order with respect to + `performanceEntry.startTime` whose `performanceEntry.name` is equal to `name`, and optionally, + whose `performanceEntry.entryType` is equal to `type`. + **/ + function getEntriesByName(name:String, ?type:PerformanceEntryType):Array; + + /** + Returns a list of `PerformanceEntry` objects in chronological order with respect to + `performanceEntry.startTime` whose `performanceEntry.entryType` is equal to `type`. + **/ + function getEntriesByType(type:PerformanceEntryType):Array; + + /** + Creates a new `PerformanceMark` entry in the Performance Timeline. + **/ + function mark(name:String, ?options:PerformanceMarkOptions):PerformanceMark; + + /** + Creates a new `PerformanceResourceTiming` entry in the Resource Timeline. + + This property is an extension by Node.js. It is not available in Web browsers. + **/ + function markResourceTiming(timingInfo:Dynamic, requestedUrl:String, initiatorType:String, global:Dynamic, cacheMode:String, bodyInfo:Dynamic, + responseStatus:Int, ?deliveryType:String):PerformanceResourceTiming; + + /** + Creates a new `PerformanceMeasure` entry in the Performance Timeline. + **/ + @:overload(function(name:String, options:PerformanceMeasureOptions):PerformanceMeasure {}) + function measure(name:String, ?startMark:String, ?endMark:String):PerformanceMeasure; + + /** + An instance of the `PerformanceNodeTiming` class that provides performance metrics for + specific Node.js operational milestones. + + This property is an extension by Node.js. It is not available in Web browsers. + **/ + var nodeTiming(default, null):PerformanceNodeTiming; + + /** + Returns the current high resolution millisecond timestamp, where 0 represents the start + of the current `node` process. + **/ + function now():Float; + + /** + Sets the global performance resource timing buffer size to the specified number of + "resource" type performance entry objects. + + By default the max buffer size is set to 250. + **/ + function setResourceTimingBufferSize(maxSize:Int):Void; + + /** + The timeOrigin specifies the high resolution millisecond timestamp at which the current + `node` process began, measured in Unix time. + **/ + var timeOrigin(default, null):Float; + + /** + Wraps a function within a new function that measures the running time of the wrapped function. + A `PerformanceObserver` must be subscribed to the `'function'` event type in order for the + timing details to be accessed. + + This property is an extension by Node.js. It is not available in Web browsers. + + Prefer this over the module-level `PerfHooks.timerify` when targeting Node.js 22 LTS as well + as Node.js 24; the module-level alias was only added in v24.12.0. + **/ + function timerify(fn:T, ?options:TimerifyOptions):T; + + /** + An object which is JSON representation of the `performance` object. + **/ + function toJSON():Dynamic; + + /** + Event handler for the `'resourcetimingbufferfull'` event. + **/ + var onresourcetimingbufferfull:EitherTypeVoid, Function>; +} diff --git a/src/js/node/perf_hooks/PerformanceEntry.hx b/src/js/node/perf_hooks/PerformanceEntry.hx new file mode 100644 index 00000000..77ea20f4 --- /dev/null +++ b/src/js/node/perf_hooks/PerformanceEntry.hx @@ -0,0 +1,57 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.perf_hooks; + +/** + The constructor of this class is not exposed to users directly. + + @see https://nodejs.org/api/perf_hooks.html#class-performanceentry +**/ +@:jsRequire("perf_hooks", "PerformanceEntry") +extern class PerformanceEntry { + /** + The total number of milliseconds elapsed for this entry. + This value will not be meaningful for all Performance Entry types. + **/ + var duration(default, null):Float; + + /** + The type of the performance entry. + **/ + var entryType(default, null):PerformanceEntryType; + + /** + The name of the performance entry. + **/ + var name(default, null):String; + + /** + The high resolution millisecond timestamp marking the starting time of the Performance Entry. + **/ + var startTime(default, null):Float; + + /** + Returns a JSON representation of the `PerformanceEntry` object. + **/ + function toJSON():Dynamic; +} diff --git a/src/js/node/perf_hooks/PerformanceEntryType.hx b/src/js/node/perf_hooks/PerformanceEntryType.hx new file mode 100644 index 00000000..ef9b92a0 --- /dev/null +++ b/src/js/node/perf_hooks/PerformanceEntryType.hx @@ -0,0 +1,60 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.perf_hooks; + +/** + Type of a performance entry (`performanceEntry.entryType`). + + @see https://nodejs.org/api/perf_hooks.html#performanceentryentrytype +**/ +enum abstract PerformanceEntryType(String) from String to String { + /** Node.js only **/ + var Dns = "dns"; + + /** Node.js only **/ + var Function = "function"; + + /** Node.js only **/ + var Gc = "gc"; + + /** Node.js only **/ + var Http2 = "http2"; + + /** Node.js only **/ + var Http = "http"; + + /** Available on the Web **/ + var Mark = "mark"; + + /** Available on the Web **/ + var Measure = "measure"; + + /** Node.js only **/ + var Net = "net"; + + /** Node.js only **/ + var Node = "node"; + + /** Available on the Web **/ + var Resource = "resource"; +} diff --git a/src/js/node/perf_hooks/PerformanceMark.hx b/src/js/node/perf_hooks/PerformanceMark.hx new file mode 100644 index 00000000..4225d3d5 --- /dev/null +++ b/src/js/node/perf_hooks/PerformanceMark.hx @@ -0,0 +1,54 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.perf_hooks; + +/** + Exposes marks created via the `Performance.mark()` method. + + @see https://nodejs.org/api/perf_hooks.html#class-performancemark +**/ +@:jsRequire("perf_hooks", "PerformanceMark") +extern class PerformanceMark extends PerformanceEntry { + function new(markName:String, ?markOptions:PerformanceMarkOptions); + + /** + Additional detail specified when creating with `Performance.mark()` method. + **/ + var detail(default, null):Dynamic; +} + +/** + Options for `Performance.mark`. +**/ +typedef PerformanceMarkOptions = { + /** + Additional optional detail to include with the mark. + **/ + @:optional var detail:Dynamic; + + /** + An optional timestamp to be used as the mark time. + Default: `performance.now()`. + **/ + @:optional var startTime:Float; +} diff --git a/src/js/node/perf_hooks/PerformanceMeasure.hx b/src/js/node/perf_hooks/PerformanceMeasure.hx new file mode 100644 index 00000000..d16a5f87 --- /dev/null +++ b/src/js/node/perf_hooks/PerformanceMeasure.hx @@ -0,0 +1,65 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.perf_hooks; + +import haxe.extern.EitherType; + +/** + Exposes measures created via the `Performance.measure()` method. + + The constructor of this class is not exposed to users directly. + + @see https://nodejs.org/api/perf_hooks.html#class-performancemeasure +**/ +@:jsRequire("perf_hooks", "PerformanceMeasure") +extern class PerformanceMeasure extends PerformanceEntry { + /** + Additional detail specified when creating with `Performance.measure()` method. + **/ + var detail(default, null):Dynamic; +} + +/** + Options for `Performance.measure` when using the options-object form. +**/ +typedef PerformanceMeasureOptions = { + /** + Additional optional detail to include with the measure. + **/ + @:optional var detail:Dynamic; + + /** + Duration between start and end times. + **/ + @:optional var duration:Float; + + /** + Timestamp to be used as the end time, or a string identifying a previously recorded mark. + **/ + @:optional var end:EitherType; + + /** + Timestamp to be used as the start time, or a string identifying a previously recorded mark. + **/ + @:optional var start:EitherType; +} diff --git a/src/js/node/perf_hooks/PerformanceNodeEntry.hx b/src/js/node/perf_hooks/PerformanceNodeEntry.hx new file mode 100644 index 00000000..5f5a387e --- /dev/null +++ b/src/js/node/perf_hooks/PerformanceNodeEntry.hx @@ -0,0 +1,57 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.perf_hooks; + +/** + This class is an extension by Node.js. It is not available in Web browsers. + + Provides detailed Node.js timing data. + + The constructor of this class is not exposed to users directly. + + @see https://nodejs.org/api/perf_hooks.html#class-performancenodeentry +**/ +extern class PerformanceNodeEntry extends PerformanceEntry { + /** + Additional detail specific to the `entryType`. + **/ + var detail(default, null):Dynamic; + + /** + Stability: 0 - Deprecated: Use `detail` instead. + + When `entryType` is equal to `'gc'`, contains additional information about + garbage collection operation. + **/ + @:deprecated("Use detail instead") + var flags(default, null):Int; + + /** + Stability: 0 - Deprecated: Use `detail` instead. + + When `entryType` is equal to `'gc'`, identifies the type of garbage collection + operation that occurred. + **/ + @:deprecated("Use detail instead") + var kind(default, null):Int; +} diff --git a/src/js/node/perf_hooks/PerformanceNodeTiming.hx b/src/js/node/perf_hooks/PerformanceNodeTiming.hx new file mode 100644 index 00000000..a67a6e00 --- /dev/null +++ b/src/js/node/perf_hooks/PerformanceNodeTiming.hx @@ -0,0 +1,102 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.perf_hooks; + +/** + This property is an extension by Node.js. It is not available in Web browsers. + + Provides timing details for Node.js itself. The constructor of this class is + not exposed to users. + + @see https://nodejs.org/api/perf_hooks.html#class-performancenodetiming +**/ +extern class PerformanceNodeTiming extends PerformanceEntry { + /** + The high resolution millisecond timestamp at which the Node.js process completed bootstrapping. + If bootstrapping has not yet finished, the property has the value of -1. + **/ + var bootstrapComplete(default, null):Float; + + /** + The high resolution millisecond timestamp at which the Node.js environment was initialized. + **/ + var environment(default, null):Float; + + /** + The high resolution millisecond timestamp of the amount of time the event loop has been idle + within the event loop's event provider (e.g. `epoll_wait`). This does not take CPU usage into + consideration. If the event loop has not yet started, the property has the value of 0. + **/ + var idleTime(default, null):Float; + + /** + The high resolution millisecond timestamp at which the Node.js event loop exited. + If the event loop has not yet exited, the property has the value of -1. + **/ + var loopExit(default, null):Float; + + /** + The high resolution millisecond timestamp at which the Node.js event loop started. + If the event loop has not yet started, the property has the value of -1. + **/ + var loopStart(default, null):Float; + + /** + The high resolution millisecond timestamp at which the Node.js process was initialized. + **/ + var nodeStart(default, null):Float; + + /** + A wrapper to the `uv_metrics_info` function. Returns the current set of event loop metrics. + + It is recommended to use this property inside a function whose execution was scheduled using + `setImmediate` to avoid collecting metrics before finishing all operations scheduled during + the current loop iteration. + **/ + var uvMetricsInfo(default, null):UVMetrics; + + /** + The high resolution millisecond timestamp at which the V8 platform was initialized. + **/ + var v8Start(default, null):Float; +} + +/** + Event loop metrics from `PerformanceNodeTiming.uvMetricsInfo`. +**/ +typedef UVMetrics = { + /** + Number of event loop iterations. + **/ + var loopCount:Float; + + /** + Number of events that have been processed by the event handler. + **/ + var events:Float; + + /** + Number of events that were waiting to be processed when the event provider was called. + **/ + var eventsWaiting:Float; +} diff --git a/src/js/node/perf_hooks/PerformanceObserver.hx b/src/js/node/perf_hooks/PerformanceObserver.hx new file mode 100644 index 00000000..d9aade08 --- /dev/null +++ b/src/js/node/perf_hooks/PerformanceObserver.hx @@ -0,0 +1,89 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.perf_hooks; + +/** + `PerformanceObserver` objects provide notifications when new `PerformanceEntry` + instances have been added to the Performance Timeline. + + @see https://nodejs.org/api/perf_hooks.html#class-performanceobserver +**/ +@:jsRequire("perf_hooks", "PerformanceObserver") +extern class PerformanceObserver { + /** + Get supported entry types. + **/ + static var supportedEntryTypes(default, never):Array; + + /** + Creates a new `PerformanceObserver`. + + The `callback` is invoked when a `PerformanceObserver` is notified about new + `PerformanceEntry` instances. + **/ + function new(callback:PerformanceObserverCallback); + + /** + Disconnects the `PerformanceObserver` instance from all notifications. + **/ + function disconnect():Void; + + /** + Subscribes the instance to notifications of new instances identified either by + `options.entryTypes` or `options.type`. + **/ + function observe(options:PerformanceObserverObserveOptions):Void; + + /** + Returns the current list of entries stored in the performance observer, emptying it out. + **/ + function takeRecords():Array; +} + +/** + Callback invoked when a `PerformanceObserver` is notified about new entries. +**/ +typedef PerformanceObserverCallback = PerformanceObserverEntryList->PerformanceObserver->Void; + +/** + Options for `PerformanceObserver.observe`. +**/ +typedef PerformanceObserverObserveOptions = { + /** + A single entry type. Must not be given if `entryTypes` is already specified. + **/ + @:optional var type:PerformanceEntryType; + + /** + An array of strings identifying the types of instances the observer is interested in. + If not provided an error will be thrown. + **/ + @:optional var entryTypes:Array; + + /** + If `true`, the observer callback is called with a list of global `PerformanceEntry` + buffered entries. If `false`, only `PerformanceEntry`s created after the time point + are sent to the observer callback. Default: `false`. + **/ + @:optional var buffered:Bool; +} diff --git a/src/js/node/perf_hooks/PerformanceObserverEntryList.hx b/src/js/node/perf_hooks/PerformanceObserverEntryList.hx new file mode 100644 index 00000000..64d0f3c6 --- /dev/null +++ b/src/js/node/perf_hooks/PerformanceObserverEntryList.hx @@ -0,0 +1,51 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.perf_hooks; + +/** + Used to provide access to the `PerformanceEntry` instances passed to a `PerformanceObserver`. + The constructor of this class is not exposed to users. + + @see https://nodejs.org/api/perf_hooks.html#class-performanceobserverentrylist +**/ +@:jsRequire("perf_hooks", "PerformanceObserverEntryList") +extern class PerformanceObserverEntryList { + /** + Returns a list of `PerformanceEntry` objects in chronological order with respect to + `performanceEntry.startTime`. + **/ + function getEntries():Array; + + /** + Returns a list of `PerformanceEntry` objects in chronological order with respect to + `performanceEntry.startTime` whose `performanceEntry.name` is equal to `name`, and optionally, + whose `performanceEntry.entryType` is equal to `type`. + **/ + function getEntriesByName(name:String, ?type:PerformanceEntryType):Array; + + /** + Returns a list of `PerformanceEntry` objects in chronological order with respect to + `performanceEntry.startTime` whose `performanceEntry.entryType` is equal to `type`. + **/ + function getEntriesByType(type:PerformanceEntryType):Array; +} diff --git a/src/js/node/perf_hooks/PerformanceResourceTiming.hx b/src/js/node/perf_hooks/PerformanceResourceTiming.hx new file mode 100644 index 00000000..e46616da --- /dev/null +++ b/src/js/node/perf_hooks/PerformanceResourceTiming.hx @@ -0,0 +1,122 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.perf_hooks; + +/** + Provides detailed network timing data regarding the loading of an application's resources. + + The constructor of this class is not exposed to users directly. + + @see https://nodejs.org/api/perf_hooks.html#class-performanceresourcetiming +**/ +@:jsRequire("perf_hooks", "PerformanceResourceTiming") +extern class PerformanceResourceTiming extends PerformanceEntry { + /** + The high resolution millisecond timestamp at immediately before dispatching the `fetch` request. + If the resource is not intercepted by a worker the property will always return 0. + **/ + var workerStart(default, null):Float; + + /** + The high resolution millisecond timestamp that represents the start time of the fetch which + initiates the redirect. + **/ + var redirectStart(default, null):Float; + + /** + The high resolution millisecond timestamp that will be created immediately after receiving the + last byte of the response of the last redirect. + **/ + var redirectEnd(default, null):Float; + + /** + The high resolution millisecond timestamp immediately before the Node.js starts to fetch the resource. + **/ + var fetchStart(default, null):Float; + + /** + The high resolution millisecond timestamp immediately before the Node.js starts the domain name + lookup for the resource. + **/ + var domainLookupStart(default, null):Float; + + /** + The high resolution millisecond timestamp representing the time immediately after the Node.js + finished the domain name lookup for the resource. + **/ + var domainLookupEnd(default, null):Float; + + /** + The high resolution millisecond timestamp representing the time immediately before Node.js starts + to establish the connection to the server to retrieve the resource. + **/ + var connectStart(default, null):Float; + + /** + The high resolution millisecond timestamp representing the time immediately after Node.js finishes + establishing the connection to the server to retrieve the resource. + **/ + var connectEnd(default, null):Float; + + /** + The high resolution millisecond timestamp representing the time immediately before Node.js starts + the handshake process to secure the current connection. + **/ + var secureConnectionStart(default, null):Float; + + /** + The high resolution millisecond timestamp representing the time immediately before Node.js receives + the first byte of the response from the server. + **/ + var requestStart(default, null):Float; + + /** + The high resolution millisecond timestamp representing the time immediately after Node.js receives + the last byte of the resource or immediately before the transport connection is closed, whichever + comes first. + **/ + var responseEnd(default, null):Float; + + /** + A number representing the size (in octets) of the fetched resource. The size includes the response + header fields plus the response payload body. + **/ + var transferSize(default, null):Float; + + /** + A number representing the size (in octets) received from the fetch (HTTP or cache), of the payload + body, before removing any applied content-codings. + **/ + var encodedBodySize(default, null):Float; + + /** + A number representing the size (in octets) received from the fetch (HTTP or cache), of the message + body, after removing any applied content-codings. + **/ + var decodedBodySize(default, null):Float; + + /** + Returns an object that is the JSON representation of the `PerformanceResourceTiming` object. + **/ + override function toJSON():Dynamic; +} diff --git a/src/js/node/perf_hooks/RecordableHistogram.hx b/src/js/node/perf_hooks/RecordableHistogram.hx new file mode 100644 index 00000000..635eacc3 --- /dev/null +++ b/src/js/node/perf_hooks/RecordableHistogram.hx @@ -0,0 +1,48 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.perf_hooks; + +import haxe.extern.EitherType; + +/** + A `Histogram` that can record values. + + @see https://nodejs.org/api/perf_hooks.html#class-recordablehistogram-extends-histogram +**/ +extern class RecordableHistogram extends Histogram { + /** + Adds the values from `other` to this histogram. + **/ + function add(other:RecordableHistogram):Void; + + /** + Records `val` in the histogram. + **/ + function record(val:EitherType):Void; + + /** + Calculates the amount of time (in nanoseconds) that has passed since the previous call + to `recordDelta()` and records that amount in the histogram. + **/ + function recordDelta():Void; +} From b3d7832dc0584e57b143dba87f70e64f8e071cd3 Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Tue, 14 Jul 2026 15:25:08 +1000 Subject: [PATCH 25/48] Add Node.js inspector (+ inspector/promises) externs (#218) * Add Node.js inspector and inspector/promises externs. Cover the stable module surface (open/close/url/waitForDebugger/console/Session) plus pragmatic Network, NetworkResources, and DOMStorage helpers without exhaustive CDP typings. Co-authored-by: Cursor * Fix inspector LTS review: DebuggerResumed typing and Node 24 notes. Align SessionEvent.DebuggerResumed with notification message handlers, document webSocket*/DomStorage as Node 24-only, and clarify Disposable open() / promises SessionEvent reuse. Co-authored-by: Cursor --------- Co-authored-by: Cursor --- src/js/node/Inspector.hx | 73 +++++++ src/js/node/InspectorPromises.hx | 67 +++++++ src/js/node/inspector/DomStorage.hx | 89 +++++++++ src/js/node/inspector/InspectorConsole.hx | 56 ++++++ src/js/node/inspector/Network.hx | 221 ++++++++++++++++++++++ src/js/node/inspector/NetworkResources.hx | 41 ++++ src/js/node/inspector/Session.hx | 114 +++++++++++ src/js/node/inspector/promises/Session.hx | 74 ++++++++ 8 files changed, 735 insertions(+) create mode 100644 src/js/node/Inspector.hx create mode 100644 src/js/node/InspectorPromises.hx create mode 100644 src/js/node/inspector/DomStorage.hx create mode 100644 src/js/node/inspector/InspectorConsole.hx create mode 100644 src/js/node/inspector/Network.hx create mode 100644 src/js/node/inspector/NetworkResources.hx create mode 100644 src/js/node/inspector/Session.hx create mode 100644 src/js/node/inspector/promises/Session.hx diff --git a/src/js/node/Inspector.hx b/src/js/node/Inspector.hx new file mode 100644 index 00000000..6545881a --- /dev/null +++ b/src/js/node/Inspector.hx @@ -0,0 +1,73 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node; + +import js.node.inspector.InspectorConsole; + +/** + The `inspector` module provides an API for interacting with the V8 inspector. + + Related types live under `js.node.inspector` (`Session`, `Network`, `NetworkResources`, `DomStorage`). + + @see https://nodejs.org/api/inspector.html +**/ +@:jsRequire("inspector") +extern class Inspector { + /** + Activate inspector on host and port. Equivalent to `node --inspect=[[host:]port]`, + but can be done programmatically after node has started. + + If `wait` is `true`, will block until a client has connected to the inspect port + and flow control has been passed to the debugger client. + + Returns a Disposable (`{ [Symbol.dispose](): void }`) that calls `inspector.close()`. + Typed as `Dynamic` because hxnodejs does not yet model the Web IDL `Disposable` interface. + **/ + static function open(?port:Int, ?host:String, ?wait:Bool):Dynamic; + + /** + Attempts to close all remaining connections, blocking the event loop until all are closed. + Once all connections are closed, deactivates the inspector. + **/ + static function close():Void; + + /** + Return the URL of the active inspector, or `undefined` if there is none. + **/ + static function url():Null; + + /** + Blocks until a client (existing or connected later) has sent + `Runtime.runIfWaitingForDebugger` command. + + An exception will be thrown if there is no active inspector. + **/ + static function waitForDebugger():Void; + + /** + An object to send messages to the remote inspector console. + + The inspector console does not have API parity with Node.js console. + **/ + static var console(default, null):InspectorConsole; +} diff --git a/src/js/node/InspectorPromises.hx b/src/js/node/InspectorPromises.hx new file mode 100644 index 00000000..166baadd --- /dev/null +++ b/src/js/node/InspectorPromises.hx @@ -0,0 +1,67 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node; + +import js.node.inspector.InspectorConsole; +import js.node.inspector.promises.Session; + +/** + Promise-based `inspector` API (`inspector/promises`). + + Stability: 1 - Experimental. + + Module-level helpers match `Inspector`; use `js.node.inspector.promises.Session` + for a `Session` whose `post` returns a `Promise`. + + @see https://nodejs.org/api/inspector.html#promises-api +**/ +@:jsRequire("inspector/promises") +extern class InspectorPromises { + /** + Activate inspector on host and port. + + Returns a Disposable (`{ [Symbol.dispose](): void }`) that calls `close()`. + Typed as `Dynamic` because hxnodejs does not yet model the Web IDL `Disposable` interface. + **/ + static function open(?port:Int, ?host:String, ?wait:Bool):Dynamic; + + /** + Attempts to close all remaining connections, blocking the event loop until all are closed. + **/ + static function close():Void; + + /** + Return the URL of the active inspector, or `undefined` if there is none. + **/ + static function url():Null; + + /** + Blocks until a client has sent `Runtime.runIfWaitingForDebugger`. + **/ + static function waitForDebugger():Void; + + /** + An object to send messages to the remote inspector console. + **/ + static var console(default, null):InspectorConsole; +} diff --git a/src/js/node/inspector/DomStorage.hx b/src/js/node/inspector/DomStorage.hx new file mode 100644 index 00000000..f4f629f0 --- /dev/null +++ b/src/js/node/inspector/DomStorage.hx @@ -0,0 +1,89 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.inspector; + +/** + Helpers that broadcast Chrome DevTools Protocol `DOMStorage` events to connected frontends. + + Added in: v24.16.0 (Active LTS). Not available on Maintenance LTS 22.x. + Only available with the `--experimental-storage-inspection` flag enabled. + + Haxe type is `DomStorage` (acronyms are not uppercased); the Node.js export name is `DOMStorage`. + + @see https://nodejs.org/api/inspector.html#inspectordomstoragedomstorageitemadded +**/ +@:jsRequire("inspector", "DOMStorage") +extern class DomStorage { + /** + Broadcasts the `DOMStorage.domStorageItemAdded` event to connected frontends. + **/ + static function domStorageItemAdded(params:DomStorageItemAddedParams):Void; + + /** + Broadcasts the `DOMStorage.domStorageItemRemoved` event to connected frontends. + **/ + static function domStorageItemRemoved(params:DomStorageItemRemovedParams):Void; + + /** + Broadcasts the `DOMStorage.domStorageItemUpdated` event to connected frontends. + **/ + static function domStorageItemUpdated(params:DomStorageItemUpdatedParams):Void; + + /** + Broadcasts the `DOMStorage.domStorageItemsCleared` event to connected frontends. + **/ + static function domStorageItemsCleared(params:DomStorageItemsClearedParams):Void; + + /** + Registers storage for inspection. + **/ + static function registerStorage(params:Dynamic):Void; +} + +typedef DomStorageId = { + @:optional var securityOrigin:String; + @:optional var storageKey:String; + var isLocalStorage:Bool; +} + +typedef DomStorageItemAddedParams = { + var storageId:DomStorageId; + var key:String; + var newValue:String; +} + +typedef DomStorageItemRemovedParams = { + var storageId:DomStorageId; + var key:String; +} + +typedef DomStorageItemUpdatedParams = { + var storageId:DomStorageId; + var key:String; + var oldValue:String; + var newValue:String; +} + +typedef DomStorageItemsClearedParams = { + var storageId:DomStorageId; +} diff --git a/src/js/node/inspector/InspectorConsole.hx b/src/js/node/inspector/InspectorConsole.hx new file mode 100644 index 00000000..b61bd4fb --- /dev/null +++ b/src/js/node/inspector/InspectorConsole.hx @@ -0,0 +1,56 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.inspector; + +import haxe.extern.Rest; + +/** + Object used to send messages to the remote inspector console. + + The inspector console does not have API parity with Node.js console. + + @see https://nodejs.org/api/inspector.html#inspectorconsole +**/ +extern class InspectorConsole { + function debug(data:Rest):Void; + function error(data:Rest):Void; + function info(data:Rest):Void; + function log(data:Rest):Void; + function warn(data:Rest):Void; + function dir(data:Rest):Void; + function dirxml(data:Rest):Void; + function table(data:Rest):Void; + function trace(data:Rest):Void; + function group(data:Rest):Void; + function groupCollapsed(data:Rest):Void; + function groupEnd(data:Rest):Void; + function clear(data:Rest):Void; + function count(?label:Dynamic):Void; + function countReset(?label:Dynamic):Void; + function assert(?value:Dynamic, data:Rest):Void; + function profile(?label:Dynamic):Void; + function profileEnd(?label:Dynamic):Void; + function time(?label:Dynamic):Void; + function timeLog(?label:Dynamic):Void; + function timeStamp(?label:Dynamic):Void; +} diff --git a/src/js/node/inspector/Network.hx b/src/js/node/inspector/Network.hx new file mode 100644 index 00000000..08dfeafb --- /dev/null +++ b/src/js/node/inspector/Network.hx @@ -0,0 +1,221 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.inspector; + +/** + Broadcast helpers for Chrome DevTools Protocol `Network.*` events. + + These APIs require the `--experimental-network-inspection` flag. + + Usage: `Network.requestWillBeSent({ ... })` (maps to `inspector.Network` in Node.js). + + @see https://nodejs.org/api/inspector.html#integration-with-devtools +**/ +@:jsRequire("inspector", "Network") +extern class Network { + /** + Broadcasts the `Network.dataReceived` event to connected frontends, or buffers the data if + `Network.streamResourceContent` command was not invoked for the given request yet. + + Also enables `Network.getResponseBody` command to retrieve the response data. + **/ + static function dataReceived(?params:NetworkDataReceivedParams):Void; + + /** + Enables `Network.getRequestPostData` command to retrieve the request data. + **/ + static function dataSent(?params:Dynamic):Void; + + /** + Broadcasts the `Network.requestWillBeSent` event to connected frontends. + This event indicates that the application is about to send an HTTP request. + **/ + static function requestWillBeSent(?params:NetworkRequestWillBeSentParams):Void; + + /** + Broadcasts the `Network.responseReceived` event to connected frontends. + This event indicates that HTTP response is available. + **/ + static function responseReceived(?params:NetworkResponseReceivedParams):Void; + + /** + Broadcasts the `Network.loadingFinished` event to connected frontends. + This event indicates that HTTP request has finished loading. + **/ + static function loadingFinished(?params:NetworkLoadingFinishedParams):Void; + + /** + Broadcasts the `Network.loadingFailed` event to connected frontends. + This event indicates that HTTP request has failed to load. + **/ + static function loadingFailed(?params:NetworkLoadingFailedParams):Void; + + /** + Broadcasts the `Network.webSocketCreated` event to connected frontends. + This event indicates that a WebSocket connection has been initiated. + + Added in: v24.7.0. Not available on Maintenance LTS 22.x. + **/ + static function webSocketCreated(?params:NetworkWebSocketCreatedParams):Void; + + /** + Broadcasts the `Network.webSocketHandshakeResponseReceived` event to connected frontends. + This event indicates that the WebSocket handshake response has been received. + + Added in: v24.7.0. Not available on Maintenance LTS 22.x. + **/ + static function webSocketHandshakeResponseReceived(?params:NetworkWebSocketHandshakeResponseReceivedParams):Void; + + /** + Broadcasts the `Network.webSocketClosed` event to connected frontends. + This event indicates that a WebSocket connection has been closed. + + Added in: v24.7.0. Not available on Maintenance LTS 22.x. + **/ + static function webSocketClosed(?params:NetworkWebSocketClosedParams):Void; +} + +/** + Pragmatic subset of CDP `Network.Request`. +**/ +typedef NetworkRequest = { + var url:String; + var method:String; + @:optional var headers:Dynamic; + @:optional var postData:String; + @:optional var hasPostData:Bool; +} + +/** + Pragmatic subset of CDP `Network.Response`. +**/ +typedef NetworkResponse = { + @:optional var url:String; + @:optional var status:Int; + @:optional var statusText:String; + @:optional var headers:Dynamic; + @:optional var mimeType:String; + @:optional var charset:String; +} + +/** + Pragmatic subset of CDP request initiator. +**/ +typedef NetworkInitiator = { + var type:String; + @:optional var url:String; + @:optional var lineNumber:Float; + @:optional var columnNumber:Float; + @:optional var stack:Dynamic; +} + +/** + Parameters for `Network.requestWillBeSent`. +**/ +typedef NetworkRequestWillBeSentParams = { + var requestId:String; + var timestamp:Float; + var wallTime:Float; + var request:NetworkRequest; + @:optional var initiator:NetworkInitiator; + @:optional var type:String; + @:optional var frameId:String; + @:optional var hasUserGesture:Bool; +} + +/** + Parameters for `Network.responseReceived`. +**/ +typedef NetworkResponseReceivedParams = { + var requestId:String; + var timestamp:Float; + var type:String; + var response:NetworkResponse; + @:optional var frameId:String; +} + +/** + Parameters for `Network.loadingFinished`. +**/ +typedef NetworkLoadingFinishedParams = { + var requestId:String; + var timestamp:Float; + @:optional var encodedDataLength:Float; +} + +/** + Parameters for `Network.loadingFailed`. +**/ +typedef NetworkLoadingFailedParams = { + var requestId:String; + var timestamp:Float; + var type:String; + var errorText:String; + @:optional var canceled:Bool; +} + +/** + Parameters for `Network.dataReceived`. +**/ +typedef NetworkDataReceivedParams = { + var requestId:String; + var timestamp:Float; + var dataLength:Int; + var encodedDataLength:Int; + @:optional var data:String; +} + +/** + Parameters for `Network.webSocketCreated`. +**/ +typedef NetworkWebSocketCreatedParams = { + var requestId:String; + var url:String; + @:optional var initiator:NetworkInitiator; +} + +/** + Parameters for `Network.webSocketHandshakeResponseReceived`. +**/ +typedef NetworkWebSocketHandshakeResponseReceivedParams = { + var requestId:String; + var timestamp:Float; + var response:NetworkWebSocketResponse; +} + +/** + Pragmatic subset of CDP WebSocket handshake response. +**/ +typedef NetworkWebSocketResponse = { + var status:Int; + var statusText:String; + @:optional var headers:Dynamic; +} + +/** + Parameters for `Network.webSocketClosed`. +**/ +typedef NetworkWebSocketClosedParams = { + var requestId:String; + var timestamp:Float; +} diff --git a/src/js/node/inspector/NetworkResources.hx b/src/js/node/inspector/NetworkResources.hx new file mode 100644 index 00000000..09e93561 --- /dev/null +++ b/src/js/node/inspector/NetworkResources.hx @@ -0,0 +1,41 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.inspector; + +/** + Provides responses for `Network.loadNetworkResource` CDP requests + (e.g. source maps requested by a DevTools frontend). + + Requires the `--experimental-inspector-network-resource` flag. + + Usage: `NetworkResources.put(url, data)` (maps to `inspector.NetworkResources` in Node.js). + + @see https://nodejs.org/api/inspector.html#inspectornetworkresourcesput +**/ +@:jsRequire("inspector", "NetworkResources") +extern class NetworkResources { + /** + Register resource content to be served in response to a `loadNetworkResource` request. + **/ + static function put(url:String, data:String):Void; +} diff --git a/src/js/node/inspector/Session.hx b/src/js/node/inspector/Session.hx new file mode 100644 index 00000000..f38c56de --- /dev/null +++ b/src/js/node/inspector/Session.hx @@ -0,0 +1,114 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.inspector; + +import js.node.events.EventEmitter; +#if haxe4 +import js.lib.Error; +#else +import js.Error; +#end + +/** + Enumeration of events emitted by `inspector.Session`. + + Arbitrary Chrome DevTools Protocol notification method names can also be used + as event names (e.g. `'Runtime.consoleAPICalled'`). +**/ +enum abstract SessionEvent(Event) to Event { + /** + Emitted when any notification from the V8 Inspector is received. + **/ + var InspectorNotification:SessionEventVoid> = "inspectorNotification"; + + /** + Emitted when an inspector notification is received with method `Debugger.paused`. + **/ + var DebuggerPaused:SessionEventVoid> = "Debugger.paused"; + + /** + Emitted when an inspector notification is received with method `Debugger.resumed`. + **/ + var DebuggerResumed:SessionEventVoid> = "Debugger.resumed"; + + /** + Emitted when an inspector notification is received with method `HeapProfiler.addHeapSnapshotChunk`. + **/ + var HeapProfilerAddHeapSnapshotChunk:SessionEventVoid> = "HeapProfiler.addHeapSnapshotChunk"; +} + +/** + A notification message object received from the V8 inspector. + + `params` shape depends on the Chrome DevTools Protocol method; kept pragmatic here. +**/ +typedef InspectorNotificationMessage = { + var method:String; + @:optional var params:Dynamic; +} + +/** + The `inspector.Session` is used for dispatching messages to the V8 inspector + back-end and receiving message responses and notifications. + + @see https://nodejs.org/api/inspector.html#class-inspectorsession +**/ +@:jsRequire("inspector", "Session") +extern class Session extends EventEmitter { + /** + Create a new instance of the `inspector.Session` class. + The inspector session needs to be connected through `session.connect()` + before the messages can be dispatched to the inspector backend. + **/ + function new(); + + /** + Connects a session to the inspector back-end. + **/ + function connect():Void; + + /** + Connects a session to the main thread inspector back-end. + An exception will be thrown if this API was not called on a Worker thread. + **/ + function connectToMainThread():Void; + + /** + Immediately close the session. All pending message callbacks will be called with an error. + `session.connect()` will need to be called to be able to send messages again. + Reconnected session will lose all inspector state, such as enabled agents or configured breakpoints. + **/ + function disconnect():Void; + + /** + Posts a message to the inspector back-end. + + `callback` will be notified when a response is received. + `callback` is a function that accepts two optional arguments: error and message-specific result. + + Protocol method names and parameter/result shapes follow the Chrome DevTools Protocol; + they are typed as `String` / `Dynamic` rather than enumerating the full CDP schema. + **/ + @:overload(function(method:String, ?callback:Null->Dynamic->Void):Void {}) + function post(method:String, ?params:Dynamic, ?callback:Null->Dynamic->Void):Void; +} diff --git a/src/js/node/inspector/promises/Session.hx b/src/js/node/inspector/promises/Session.hx new file mode 100644 index 00000000..8a964044 --- /dev/null +++ b/src/js/node/inspector/promises/Session.hx @@ -0,0 +1,74 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.inspector.promises; + +import js.node.events.EventEmitter; +#if haxe4 +import js.lib.Promise; +#else +import js.Promise; +#end + +/** + Promise-based `inspector.Session` from `inspector/promises`. + + Stability: 1 - Experimental. + + Emits the same notification events as callback `inspector.Session`; + use `js.node.inspector.SessionEvent` with `on` / `once` / `off`. + + @see https://nodejs.org/api/inspector.html#promises-api +**/ +@:jsRequire("inspector/promises", "Session") +extern class Session extends EventEmitter { + /** + Create a new instance of the `inspector.Session` class. + The inspector session needs to be connected through `session.connect()` + before the messages can be dispatched to the inspector backend. + **/ + function new(); + + /** + Connects a session to the inspector back-end. + **/ + function connect():Void; + + /** + Connects a session to the main thread inspector back-end. + An exception will be thrown if this API was not called on a Worker thread. + **/ + function connectToMainThread():Void; + + /** + Immediately close the session. All pending message callbacks will be called with an error. + `session.connect()` will need to be called to be able to send messages again. + Reconnected session will lose all inspector state, such as enabled agents or configured breakpoints. + **/ + function disconnect():Void; + + /** + Posts a message to the inspector back-end and returns a Promise that resolves + with the message-specific result object. + **/ + function post(method:String, ?params:Dynamic):Promise; +} From 55fb01e67cf93d194010b7ca7550482e0dbf88ac Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Tue, 14 Jul 2026 15:27:23 +1000 Subject: [PATCH 26/48] Add stream/promises externs (#222) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add stream/promises externs Thin façade around finished/pipeline Promise forms from stream/promises. Co-authored-by: Cursor * Add stream/promises finished and pipeline façade Thin Promise-only wrappers around stream.finished and stream.pipeline. Co-authored-by: Cursor * Fix stream/promises pipeline options and finished signature. Use StreamPipelineOptions for pipeline, drop the invalid single-stream overload, and align finished with optional options like Stream.finished. Co-authored-by: Cursor * Remove duplicate StreamPipelineOptions typedef. Co-authored-by: Cursor --------- Co-authored-by: Cursor --- src/js/node/Stream.hx | 15 ++++++++ src/js/node/StreamPromises.hx | 72 +++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 src/js/node/StreamPromises.hx diff --git a/src/js/node/Stream.hx b/src/js/node/Stream.hx index cf37cc46..7c8f2919 100644 --- a/src/js/node/Stream.hx +++ b/src/js/node/Stream.hx @@ -154,6 +154,21 @@ typedef StreamFinishedOptions = { @:optional var cleanup:Bool; } +/** + Options for `Stream.pipeline` / `StreamPromises.pipeline`. +**/ +typedef StreamPipelineOptions = { + /** + If specified, the pipeline will abort when the signal is aborted. + **/ + @:optional var signal:AbortSignal; + + /** + End the destination stream when the source stream ends. Default: `true`. + **/ + @:optional var end:Bool; +} + /** `IStream` interface is used as "any Stream". diff --git a/src/js/node/StreamPromises.hx b/src/js/node/StreamPromises.hx new file mode 100644 index 00000000..ef3cdcfd --- /dev/null +++ b/src/js/node/StreamPromises.hx @@ -0,0 +1,72 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node; + +import haxe.extern.Rest; +import js.node.Stream; +import js.node.stream.Readable.IReadable; +import js.node.stream.Writable.IWritable; +#if haxe4 +import js.lib.Promise; +#else +import js.Promise; +#end + +/** + The `stream/promises` API provides an alternative set of asynchronous stream + utilities that return `Promise` objects rather than using callbacks. + + Accessible via `require('stream/promises')` or `require('stream').promises`. + + @see https://nodejs.org/api/stream.html#streams-promises-api +**/ +@:jsRequire("stream/promises") +extern class StreamPromises { + /** + A module method to pipe between streams forwarding errors and properly cleaning up, + returning a Promise when the pipeline is complete. + + Requires at least a source and a destination. Pass `options` (including `signal`) + as the last argument when needed. + **/ + @:overload(function(readable:IReadable, writable1:IWritable, ?options:StreamPipelineOptions):Promise {}) + @:overload(function(readable:IReadable, writable1:IWritable, writable2:IWritable, ?options:StreamPipelineOptions):Promise {}) + @:overload(function(readable:IReadable, writable1:IWritable, writable2:IWritable, writable3:IWritable, ?options:StreamPipelineOptions):Promise {}) + @:overload(function(readable:IReadable, writable1:IWritable, writable2:IWritable, writable3:IWritable, writable4:IWritable, + ?options:StreamPipelineOptions):Promise {}) + @:overload(function(readable:IReadable, writable1:IWritable, writable2:IWritable, writable3:IWritable, writable4:IWritable, writable5:IWritable, + ?options:StreamPipelineOptions):Promise {}) + @:overload(function(readable:IReadable, writable1:IWritable, writable2:IWritable, writable3:IWritable, writable4:IWritable, writable5:IWritable, + writable6:IWritable, ?options:StreamPipelineOptions):Promise {}) + @:overload(function(readable:IReadable, writable1:IWritable, writable2:IWritable, writable3:IWritable, writable4:IWritable, writable5:IWritable, + writable6:IWritable, writable7:IWritable, ?options:StreamPipelineOptions):Promise {}) + @:overload(function(readable:IReadable, writable1:IWritable, writable2:IWritable, writable3:IWritable, writable4:IWritable, writable5:IWritable, + writable6:IWritable, writable7:IWritable, writable8:IWritable, ?options:StreamPipelineOptions):Promise {}) + static function pipeline(readable:IReadable, streams:Rest):Promise; + + /** + Returns a Promise that fulfills when the stream is no longer readable, + writable or has experienced an error or a premature close event. + **/ + static function finished(stream:IStream, ?options:StreamFinishedOptions):Promise; +} From 2bcdccfec4ab8d8277f1da44837cd2ceb90c650d Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Tue, 14 Jul 2026 15:27:39 +1000 Subject: [PATCH 27/48] Add readline/promises externs (#221) * Add readline/promises externs Promise-based Interface.question plus Readline batching helpers. Co-authored-by: Cursor * Fix readline/promises LTS options and AbortSignal typing. Add history/tabSize/signal to createInterface options, allow Promise-returning completers, and use js.node.web.AbortSignal. Co-authored-by: Cursor --------- Co-authored-by: Cursor --- src/js/node/Readline.hx | 23 ++++ src/js/node/ReadlinePromises.hx | 133 ++++++++++++++++++++++ src/js/node/readline/PromisesInterface.hx | 52 +++++++++ src/js/node/readline/PromisesReadline.hx | 82 +++++++++++++ 4 files changed, 290 insertions(+) create mode 100644 src/js/node/ReadlinePromises.hx create mode 100644 src/js/node/readline/PromisesInterface.hx create mode 100644 src/js/node/readline/PromisesReadline.hx diff --git a/src/js/node/Readline.hx b/src/js/node/Readline.hx index 2f92cfb8..69708579 100644 --- a/src/js/node/Readline.hx +++ b/src/js/node/Readline.hx @@ -26,6 +26,7 @@ import haxe.extern.EitherType; import js.node.readline.*; import js.node.stream.Readable.IReadable; import js.node.stream.Writable.IWritable; +import js.node.web.AbortSignal; /** The readline module provides an interface for reading data from a `Readable` stream (such as `process.stdin`) one @@ -113,6 +114,15 @@ typedef ReadlineOptions = { **/ @:optional var terminal:Bool; + /** + Initial list of history lines. + This option makes sense only if `terminal` is set to `true` by the user or by an internal `output` check, + otherwise the history caching mechanism is not initialized at all. + + Default: `[]`. + **/ + @:optional var history:Array; + /** Maximum number of history lines retained. To disable the history set this value to `0`. @@ -157,6 +167,19 @@ typedef ReadlineOptions = { Default: `500`. **/ @:optional var escapeCodeTimeout:Int; + + /** + The number of spaces a tab is equal to (minimum 1). + + Default: `8`. + **/ + @:optional var tabSize:Int; + + /** + Allows closing the interface using an `AbortSignal`. + Aborting the signal will internally call `close` on the interface. + **/ + @:optional var signal:AbortSignal; } #if haxe4 diff --git a/src/js/node/ReadlinePromises.hx b/src/js/node/ReadlinePromises.hx new file mode 100644 index 00000000..149efb96 --- /dev/null +++ b/src/js/node/ReadlinePromises.hx @@ -0,0 +1,133 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node; + +import haxe.extern.EitherType; +import js.node.readline.PromisesInterface; +import js.node.stream.Readable.IReadable; +import js.node.stream.Writable.IWritable; +import js.node.web.AbortSignal; +#if haxe4 +import js.lib.Promise; +#else +import js.Promise; +#end + +/** + Completer result: matching entries, then the substring used for matching. +**/ +typedef ReadlinePromisesCompleterResult = Array, String>>; + +/** + Tab autocompletion for `readline/promises`. + Unlike the callback API, the completer may return a `Promise`. +**/ +#if haxe4 +typedef ReadlinePromisesCompleterCallback = (line:String) -> EitherType>; +#else +typedef ReadlinePromisesCompleterCallback = String->EitherType>; +#end + +/** + Options for `ReadlinePromises.createInterface`. + + Same surface as `ReadlineOptions`, but `completer` may return a `Promise`. + + @see https://nodejs.org/api/readline.html#readlinepromisescreateinterfaceoptions +**/ +typedef ReadlinePromisesOptions = { + /** + The `Readable` stream to listen to. + **/ + var input:IReadable; + + /** + The `Writable` stream to write readline data to. + **/ + @:optional var output:IWritable; + + /** + An optional function used for Tab autocompletion. + May return a `Promise` resolving to the completer result. + **/ + @:optional var completer:ReadlinePromisesCompleterCallback; + + /** + `true` if the `input` and `output` streams should be treated like a TTY. + **/ + @:optional var terminal:Bool; + + /** + Initial list of history lines. + **/ + @:optional var history:Array; + + /** + Maximum number of history lines retained. + **/ + @:optional var historySize:Int; + + /** + The prompt string to use. + **/ + @:optional var prompt:String; + + /** + Delay threshold for treating `\r\n` as a single newline. + **/ + @:optional var crlfDelay:Int; + + /** + If `true`, remove older duplicate history entries. + **/ + @:optional var removeHistoryDuplicates:Bool; + + /** + Ambiguous key sequence timeout in milliseconds. + **/ + @:optional var escapeCodeTimeout:Int; + + /** + The number of spaces a tab is equal to (minimum 1). + **/ + @:optional var tabSize:Int; + + /** + Allows closing the interface using an `AbortSignal`. + **/ + @:optional var signal:AbortSignal; +} + +/** + The `readline/promises` API provides an alternative set of interfaces that return promises. + + @see https://nodejs.org/api/readline.html#promises-api +**/ +@:jsRequire("readline/promises") +extern class ReadlinePromises { + /** + Creates a new `readlinePromises.Interface` instance. + **/ + @:overload(function(input:IReadable, ?output:IWritable, ?completer:ReadlinePromisesCompleterCallback, ?terminal:Bool):PromisesInterface {}) + static function createInterface(options:ReadlinePromisesOptions):PromisesInterface; +} diff --git a/src/js/node/readline/PromisesInterface.hx b/src/js/node/readline/PromisesInterface.hx new file mode 100644 index 00000000..2ae00e10 --- /dev/null +++ b/src/js/node/readline/PromisesInterface.hx @@ -0,0 +1,52 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.readline; + +import js.node.readline.Interface; +import js.node.web.AbortSignal; +#if haxe4 +import js.lib.Promise; +#else +import js.Promise; +#end + +/** + Instances of `readlinePromises.Interface` are constructed using + `ReadlinePromises.createInterface()`. + + Differs from `readline.Interface` mainly in that `question` returns a `Promise`. + + @see https://nodejs.org/api/readline.html#class-readlinepromisesinterface +**/ +@:jsRequire("readline/promises", "Interface") +extern class PromisesInterface extends Interface { + /** + Displays `query` by writing it to `output`, waits for user input on `input`, + then fulfills with the provided input. + + When called, resumes the `input` stream if it has been paused. + If called after `close()`, returns a rejected promise. + **/ + @:overload(function(query:String):Promise {}) + function question(query:String, options:{?signal:AbortSignal}):Promise; +} diff --git a/src/js/node/readline/PromisesReadline.hx b/src/js/node/readline/PromisesReadline.hx new file mode 100644 index 00000000..c0f3f5c9 --- /dev/null +++ b/src/js/node/readline/PromisesReadline.hx @@ -0,0 +1,82 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.readline; + +import js.node.Readline.ClearLineDirection; +import js.node.stream.Writable.IWritable; +#if haxe4 +import js.lib.Promise; +#else +import js.Promise; +#end + +/** + Options for constructing a `PromisesReadline`. +**/ +typedef PromisesReadlineOptions = { + /** + If `true`, no need to call `commit()`. + **/ + @:optional var autoCommit:Bool; +} + +/** + The `readlinePromises.Readline` class provides an abstraction for collecting + pending actions on a TTY stream and applying them with `commit()`. + + @see https://nodejs.org/api/readline.html#class-readlinepromisesreadline +**/ +@:jsRequire("readline/promises", "Readline") +extern class PromisesReadline { + function new(stream:IWritable, ?options:PromisesReadlineOptions); + + /** + Adds an action that clears the current line of the associated stream. + **/ + function clearLine(dir:ClearLineDirection):PromisesReadline; + + /** + Adds an action that clears the associated stream from the current cursor position down. + **/ + function clearScreenDown():PromisesReadline; + + /** + Sends all pending actions to the associated stream and clears the internal list. + **/ + function commit():Promise; + + /** + Adds an action that moves the cursor to the specified position. + **/ + function cursorTo(x:Int, ?y:Int):PromisesReadline; + + /** + Adds an action that moves the cursor relative to its current position. + **/ + function moveCursor(dx:Int, dy:Int):PromisesReadline; + + /** + Clears the internal list of pending actions without sending it to the stream. + **/ + function rollback():PromisesReadline; +} From 41d1d68eb08f38fdb470cb030d2e6ae3e1406fe4 Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Tue, 14 Jul 2026 15:27:53 +1000 Subject: [PATCH 28/48] Add timers/promises externs (#220) * Add timers/promises externs Promise-based setTimeout/setImmediate/setInterval plus experimental scheduler helpers. Co-authored-by: Cursor * Polish timers/promises docs and AbortSignal import. Clarify setTimeout/setImmediate fulfill with value, and use js.node.web.AbortSignal. Co-authored-by: Cursor --------- Co-authored-by: Cursor --- src/js/node/TimersPromises.hx | 107 ++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 src/js/node/TimersPromises.hx diff --git a/src/js/node/TimersPromises.hx b/src/js/node/TimersPromises.hx new file mode 100644 index 00000000..4d43b4b3 --- /dev/null +++ b/src/js/node/TimersPromises.hx @@ -0,0 +1,107 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node; + +import js.node.web.AbortSignal; +#if haxe4 +import js.lib.Promise; +#else +import js.Promise; +#end + +/** + The `timers/promises` API provides an alternative set of timer functions that return `Promise` objects. + + @see https://nodejs.org/api/timers.html#timers-promises-api +**/ +@:jsRequire("timers/promises") +extern class TimersPromises { + /** + Returns a Promise that is fulfilled with `value` after `delay` milliseconds. + **/ + @:overload(function():Promise {}) + @:overload(function(delay:Int):Promise {}) + @:overload(function(delay:Int, value:T):Promise {}) + static function setTimeout(delay:Int, value:T, options:TimersPromisesOptions):Promise; + + /** + Returns a Promise that is fulfilled with `value` in the next iteration of the event loop + (after I/O events' callbacks). + **/ + @:overload(function():Promise {}) + @:overload(function(value:T):Promise {}) + static function setImmediate(value:T, options:TimersPromisesOptions):Promise; + + /** + Returns an async iterator that generates values in an interval of `delay` ms. + **/ + @:overload(function():TimersPromisesAsyncIterator {}) + @:overload(function(delay:Int):TimersPromisesAsyncIterator {}) + @:overload(function(delay:Int, value:T):TimersPromisesAsyncIterator {}) + static function setInterval(delay:Int, value:T, options:TimersPromisesOptions):TimersPromisesAsyncIterator; + + /** + Experimental Scheduling APIs draft helpers (`scheduler.wait` / `scheduler.yield`). + **/ + static var scheduler(default, null):TimersPromisesScheduler; +} + +/** + Options shared by `TimersPromises` scheduling methods. +**/ +typedef TimersPromisesOptions = { + /** + Set to `false` so the scheduled timer does not require the event loop to remain active. + Default: `true`. + **/ + @:optional var ref:Bool; + + /** + An optional `AbortSignal` that can be used to cancel the scheduled timer. + **/ + @:optional var signal:AbortSignal; +} + +/** + Minimal async iterator surface used by `setInterval` (for `for await...of`). +**/ +typedef TimersPromisesAsyncIterator = { + function next():Promise<{done:Bool, ?value:T}>; +} + +/** + Experimental `scheduler` helpers from `timers/promises`. +**/ +typedef TimersPromisesScheduler = { + /** + Wait `delay` milliseconds before resolving. + Equivalent to `setTimeout(delay, undefined, options)`. + **/ + function wait(delay:Int, ?options:TimersPromisesOptions):Promise; + + /** + Yield to the event loop. + Equivalent to `setImmediate()` with no arguments. + **/ + function yield():Promise; +} From 3e56e9f0d87d231544cb411fca8d3f00151b78a8 Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Tue, 14 Jul 2026 15:42:55 +1000 Subject: [PATCH 29/48] Add http2 module externs (#217) * Add http2 module externs for Node.js HTTP/2 API. Cover the documented Core and Compatibility surfaces (sessions, streams, servers, settings/constants) using existing http/tls/stream patterns. Co-authored-by: Cursor * Fix http2 externs for current Node LTS. Replace removed streamClosed with Duplex close+rstCode, drop @:jsRequire on non-exported session/stream classes, and document Active-LTS-only APIs. Co-authored-by: Cursor * Drop Haxe 3 conditionals from http2 externs. CI already targets Haxe 4+, so keep arrow-function typings and js.lib.Error only. Co-authored-by: Cursor --------- Co-authored-by: Cursor --- src/js/node/Http2.hx | 383 +++++++++++++++++++++++ src/js/node/http2/ClientHttp2Session.hx | 67 ++++ src/js/node/http2/ClientHttp2Stream.hx | 59 ++++ src/js/node/http2/Http2Constants.hx | 271 ++++++++++++++++ src/js/node/http2/Http2SecureServer.hx | 99 ++++++ src/js/node/http2/Http2Server.hx | 94 ++++++ src/js/node/http2/Http2ServerRequest.hx | 147 +++++++++ src/js/node/http2/Http2ServerResponse.hx | 192 ++++++++++++ src/js/node/http2/Http2Session.hx | 205 ++++++++++++ src/js/node/http2/Http2Stream.hx | 161 ++++++++++ src/js/node/http2/ServerHttp2Session.hx | 75 +++++ src/js/node/http2/ServerHttp2Stream.hx | 77 +++++ 12 files changed, 1830 insertions(+) create mode 100644 src/js/node/Http2.hx create mode 100644 src/js/node/http2/ClientHttp2Session.hx create mode 100644 src/js/node/http2/ClientHttp2Stream.hx create mode 100644 src/js/node/http2/Http2Constants.hx create mode 100644 src/js/node/http2/Http2SecureServer.hx create mode 100644 src/js/node/http2/Http2Server.hx create mode 100644 src/js/node/http2/Http2ServerRequest.hx create mode 100644 src/js/node/http2/Http2ServerResponse.hx create mode 100644 src/js/node/http2/Http2Session.hx create mode 100644 src/js/node/http2/Http2Stream.hx create mode 100644 src/js/node/http2/ServerHttp2Session.hx create mode 100644 src/js/node/http2/ServerHttp2Stream.hx diff --git a/src/js/node/Http2.hx b/src/js/node/Http2.hx new file mode 100644 index 00000000..5b763fb1 --- /dev/null +++ b/src/js/node/Http2.hx @@ -0,0 +1,383 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node; + +import haxe.DynamicAccess; +import haxe.extern.EitherType; +import js.node.http2.ClientHttp2Session; +import js.node.http2.Http2Constants; +import js.node.http2.Http2SecureServer; +import js.node.http2.Http2Server; +import js.node.http2.Http2ServerRequest; +import js.node.http2.Http2ServerResponse; +import js.node.http2.ServerHttp2Session; +import js.node.stream.Duplex; +import js.node.tls.SecureContext.SecureContextOptions; +import js.node.url.URL; +import js.lib.Symbol; + +/** + HTTP/2 headers object. + + Header names are lower-cased. Pseudo-headers such as `:status` may be present. + Outgoing `:status` values are numeric. +**/ +typedef Http2Headers = DynamicAccess>, Int>>; + +/** + Incoming HTTP/2 headers. Values are strings or string arrays; `:status` may appear as a number on responses. +**/ +typedef IncomingHttp2Headers = DynamicAccess>, Int>>; + +/** + Settings object used by `Http2Session` instances. +**/ +typedef Http2Settings = { + @:optional var headerTableSize:Int; + @:optional var enablePush:Bool; + @:optional var initialWindowSize:Int; + @:optional var maxFrameSize:Int; + @:optional var maxConcurrentStreams:Int; + @:optional var maxHeaderListSize:Int; + @:optional var enableConnectProtocol:Bool; + @:optional var customSettings:DynamicAccess; +} + +/** + State information about an `Http2Session`. +**/ +typedef Http2SessionState = { + @:optional var effectiveLocalWindowSize:Int; + @:optional var effectiveRecvDataLength:Int; + @:optional var nextStreamID:Int; + @:optional var localWindowSize:Int; + @:optional var lastProcStreamID:Int; + @:optional var remoteWindowSize:Int; + @:optional var outboundQueueSize:Int; + @:optional var deflateDynamicTableSize:Int; + @:optional var inflateDynamicTableSize:Int; +} + +/** + State information about an `Http2Stream`. +**/ +typedef Http2StreamState = { + @:optional var localWindowSize:Int; + @:optional var state:Int; + @:optional var localClose:Int; + @:optional var remoteClose:Int; + @:deprecated @:optional var sumDependencyWeight:Int; + @:deprecated @:optional var weight:Int; +} + +/** + Base session options shared by client and server sessions. +**/ +typedef Http2SessionOptions = { + /** + Sets the maximum dynamic table size for deflating header fields. + Default: `4096`. + **/ + @:optional var maxDeflateDynamicTableSize:Int; + + /** + Sets the maximum number of settings entries per `SETTINGS` frame. + Default: `32`. + **/ + @:optional var maxSettings:Int; + + /** + Sets the maximum memory that the `Http2Session` is permitted to use (megabytes). + Default: `10`. + **/ + @:optional var maxSessionMemory:Int; + + /** + Sets the maximum number of header entries. + Default: `128`. + **/ + @:optional var maxHeaderListPairs:Int; + + /** + Sets the maximum number of outstanding, unacknowledged pings. + Default: `10`. + **/ + @:optional var maxOutstandingPings:Int; + + /** + Sets the maximum allowed size for a serialized, compressed block of headers. + **/ + @:optional var maxSendHeaderBlockLength:Int; + + /** + Strategy used for determining the amount of padding to use for `HEADERS` and `DATA` frames. + Default: `Http2.constants.PADDING_STRATEGY_NONE`. + **/ + @:optional var paddingStrategy:Int; + + /** + Sets the maximum number of concurrent streams for the remote peer. + Default: `100`. + **/ + @:optional var peerMaxConcurrentStreams:Int; + + /** + The initial settings to send to the remote peer upon connection. + **/ + @:optional var settings:Http2Settings; + + /** + Setting types included in the `customSettings` property of received remote settings. + **/ + @:optional var remoteCustomSettings:Array; + + /** + Timeout in milliseconds when an `'unknownProtocol'` event is emitted. + Default: `10000`. + **/ + @:optional var unknownProtocolTimeout:Int; + + /** + If `true`, turns on strict leading/trailing whitespace validation for HTTP/2 header fields. + Default: `true`. + **/ + @:optional var strictFieldWhitespaceValidation:Bool; +} + +/** + Options for `Http2.connect`. +**/ +typedef Http2ClientSessionOptions = { + > Http2SessionOptions, + + /** + Maximum number of reserved push streams the client will accept. + Default: `200`. + **/ + @:optional var maxReservedRemoteStreams:Int; + + /** + Optional callback that returns a `Duplex` stream to use as the connection. + **/ + @:optional var createConnection:(authority:URL, options:Http2SessionOptions) -> IDuplex; + + /** + The protocol to connect with if not set in the authority. + Default: `'https:'`. + **/ + @:optional var protocol:String; +} + +/** + Secure client session options (combines session and TLS connect options). +**/ +typedef Http2SecureClientSessionOptions = { + > Http2ClientSessionOptions, + > js.node.Tls.TlsConnectOptions, + > SecureContextOptions, +} + +/** + Options for HTTP/1 fallback classes used by HTTP/2 servers. +**/ +typedef Http2Http1Options = { + @:optional var IncomingMessage:Class; + @:optional var ServerResponse:Class; + @:optional var keepAliveTimeout:Int; +} + +/** + Options for `Http2.createServer`. +**/ +typedef Http2ServerOptions = { + > Http2SessionOptions, + + @:optional var maxSessionRejectedStreams:Int; + @:optional var maxSessionInvalidFrames:Int; + @:optional var streamResetBurst:Int; + @:optional var streamResetRate:Int; + + /** + Deprecated. Use `http1Options.IncomingMessage` instead. + See DEP0202. + **/ + @:deprecated("Use http1Options.IncomingMessage instead") + @:optional var Http1IncomingMessage:Class; + + /** + Deprecated. Use `http1Options.ServerResponse` instead. + See DEP0202. + **/ + @:deprecated("Use http1Options.ServerResponse instead") + @:optional var Http1ServerResponse:Class; + + /** + Options for configuring the HTTP/1 fallback when `allowHTTP1` is `true`. + Added in Node.js v24.15.0 (Active LTS); not available on Maintenance LTS 22.x. + **/ + @:optional var http1Options:Http2Http1Options; + + @:optional var Http2ServerRequest:Class; + @:optional var Http2ServerResponse:Class; + + /** + If `true`, strict validation is used for headers and trailers defined as + having only a single value. + Default: `true`. + Added in Node.js v24.15.0 (Active LTS); not available on Maintenance LTS 22.x. + **/ + @:optional var strictSingleValueFields:Bool; +} + +/** + Options for `Http2.createSecureServer`. +**/ +typedef Http2SecureServerOptions = { + > Http2ServerOptions, + > js.node.Tls.TlsCreateServerOptions, + > SecureContextOptions, + + /** + Set to `true` to enable support for HTTP/1.1 requests. + **/ + @:optional var allowHTTP1:Bool; + + /** + Origins to include in an `ORIGIN` frame. + **/ + @:optional var origins:Array; +} + +/** + Options for `ClientHttp2Session.request`. +**/ +typedef Http2ClientSessionRequestOptions = { + @:optional var endStream:Bool; + @:optional var exclusive:Bool; + @:optional var parent:Int; + @:optional var waitForTrailers:Bool; + @:optional var signal:js.html.AbortSignal; +} + +/** + Options for `ServerHttp2Stream.respond`. +**/ +typedef Http2ServerStreamResponseOptions = { + @:optional var endStream:Bool; + @:optional var waitForTrailers:Bool; +} + +/** + Options for `ServerHttp2Stream.pushStream`. +**/ +typedef Http2PushStreamOptions = { + @:optional var exclusive:Bool; + @:optional var parent:Int; + + /** + Priority signaling is deprecated (RFC 9113). Ignored with a runtime warning since Node.js v24.2.0. + **/ + @:deprecated("Priority signaling is no longer supported in Node.js") + @:optional var weight:Int; + + @:optional var silent:Bool; +} + +/** + `statCheck` options for file responses. +**/ +typedef Http2StatOptions = { + var offset:Int; + var length:Int; +} + +/** + Options for `ServerHttp2Stream.respondWithFile` / `respondWithFD`. +**/ +typedef Http2ServerStreamFileResponseOptions = { + @:optional var statCheck:(stats:js.node.fs.Stats, headers:Http2Headers, statOptions:Http2StatOptions) -> EitherType; + @:optional var waitForTrailers:Bool; + @:optional var offset:Int; + @:optional var length:Int; + @:optional var onError:(err:js.lib.Error) -> Void; +} + +/** + The `node:http2` module provides an implementation of the HTTP/2 protocol. +**/ +@:jsRequire("http2") +extern class Http2 { + /** + Constants for HTTP/2 sessions, streams, headers, methods and status codes. + **/ + static var constants(default, null):Http2Constants; + + /** + Symbol that can be set as a property on an HTTP/2 headers object with an array value + to mark headers as sensitive. + **/ + static var sensitiveHeaders(default, null):Symbol; + + /** + Returns an object containing the default settings for an `Http2Session` instance. + This method returns a new object instance every time it is called. + **/ + static function getDefaultSettings():Http2Settings; + + /** + Returns a `Buffer` containing the serialized representation of the given HTTP/2 settings. + **/ + static function getPackedSettings(settings:Http2Settings):Buffer; + + /** + Returns an HTTP/2 settings object containing the deserialized settings from the given buffer. + **/ + static function getUnpackedSettings(buf:Buffer):Http2Settings; + + /** + Returns a `net.Server` instance that creates and manages `Http2Session` instances. + **/ + @:overload(function(options:Http2ServerOptions, ?onRequestHandler:(request:Http2ServerRequest, response:Http2ServerResponse) -> Void):Http2Server {}) + static function createServer(?onRequestHandler:(request:Http2ServerRequest, response:Http2ServerResponse) -> Void):Http2Server; + + /** + Returns a `tls.Server` instance that creates and manages `Http2Session` instances. + **/ + @:overload(function(options:Http2SecureServerOptions, ?onRequestHandler:(request:Http2ServerRequest, response:Http2ServerResponse) -> Void):Http2SecureServer {}) + static function createSecureServer(?onRequestHandler:(request:Http2ServerRequest, response:Http2ServerResponse) -> Void):Http2SecureServer; + + /** + Returns a `ClientHttp2Session` instance. + **/ + @:overload(function(authority:URL, listener:(session:ClientHttp2Session, socket:js.node.net.Socket) -> Void):ClientHttp2Session {}) + @:overload(function(authority:String, listener:(session:ClientHttp2Session, socket:js.node.net.Socket) -> Void):ClientHttp2Session {}) + @:overload(function(authority:URL, ?options:EitherType, + ?listener:(session:ClientHttp2Session, socket:js.node.net.Socket) -> Void):ClientHttp2Session {}) + static function connect(authority:String, ?options:EitherType, + ?listener:(session:ClientHttp2Session, socket:js.node.net.Socket) -> Void):ClientHttp2Session; + + /** + Create an HTTP/2 server session from an existing duplex socket. + **/ + static function performServerHandshake(socket:IDuplex, ?options:Http2ServerOptions):ServerHttp2Session; +} diff --git a/src/js/node/http2/ClientHttp2Session.hx b/src/js/node/http2/ClientHttp2Session.hx new file mode 100644 index 00000000..2f6451ea --- /dev/null +++ b/src/js/node/http2/ClientHttp2Session.hx @@ -0,0 +1,67 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.http2; + +import js.node.Http2.Http2ClientSessionRequestOptions; +import js.node.Http2.Http2Headers; +import js.node.events.EventEmitter.Event; +import js.node.net.Socket; + +/** + Enumeration of events emitted by `ClientHttp2Session` in addition to `Http2Session` events. +**/ +enum abstract ClientHttp2SessionEvent(Event) to Event { + /** + Emitted when an `ALTSVC` frame is received. + **/ + var Altsvc:ClientHttp2SessionEvent<(alt:String, origin:String, streamId:Int) -> Void> = "altsvc"; + + /** + Emitted once the client session has been successfully connected. + **/ + var Connect:ClientHttp2SessionEvent<(session:ClientHttp2Session, socket:Socket) -> Void> = "connect"; + + /** + Emitted when an `ORIGIN` frame is received. + **/ + var Origin:ClientHttp2SessionEvent->Void> = "origin"; + + /** + Emitted when a new client stream is created. + **/ + var Stream:ClientHttp2SessionEvent<(stream:ClientHttp2Stream, headers:Http2Headers, flags:Int, rawHeaders:Array) -> Void> = "stream"; +} + +/** + An `Http2Session` for use on an HTTP/2 client. + The class is not exported directly by the `node:http2` module. + + @see https://nodejs.org/api/http2.html#class-clienthttp2session +**/ +extern class ClientHttp2Session extends Http2Session { + /** + Creates and returns an `Http2Stream` instance that can be used to send an HTTP/2 request. + **/ + @:overload(function(?headers:Array, ?options:Http2ClientSessionRequestOptions):ClientHttp2Stream {}) + function request(?headers:Http2Headers, ?options:Http2ClientSessionRequestOptions):ClientHttp2Stream; +} diff --git a/src/js/node/http2/ClientHttp2Stream.hx b/src/js/node/http2/ClientHttp2Stream.hx new file mode 100644 index 00000000..79277565 --- /dev/null +++ b/src/js/node/http2/ClientHttp2Stream.hx @@ -0,0 +1,59 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.http2; + +import js.node.Http2.Http2Headers; +import js.node.events.EventEmitter.Event; + +/** + Enumeration of events emitted by `ClientHttp2Stream` in addition to `Http2Stream` events. +**/ +enum abstract ClientHttp2StreamEvent(Event) to Event { + /** + Emitted when the server sends a `100 Continue` response. + **/ + var Continue:ClientHttp2StreamEventVoid> = "continue"; + + /** + Emitted when additional headers are received for an open stream. + **/ + var Headers:ClientHttp2StreamEvent<(headers:Http2Headers, flags:Int, rawHeaders:Array) -> Void> = "headers"; + + /** + Emitted when the server pushes a stream. + **/ + var Push:ClientHttp2StreamEvent<(headers:Http2Headers, flags:Int) -> Void> = "push"; + + /** + Emitted when a response `HEADERS` frame has been received. + **/ + var Response:ClientHttp2StreamEvent<(headers:Http2Headers, flags:Int, rawHeaders:Array) -> Void> = "response"; +} + +/** + An `Http2Stream` for use on an HTTP/2 client. + The class is not exported directly by the `node:http2` module. + + @see https://nodejs.org/api/http2.html#class-clienthttp2stream +**/ +extern class ClientHttp2Stream extends Http2Stream {} diff --git a/src/js/node/http2/Http2Constants.hx b/src/js/node/http2/Http2Constants.hx new file mode 100644 index 00000000..25e0664d --- /dev/null +++ b/src/js/node/http2/Http2Constants.hx @@ -0,0 +1,271 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.http2; + +/** + Constants exposed by the `http2` module. + + @see https://nodejs.org/api/http2.html#http2constants +**/ +typedef Http2Constants = { + var DEFAULT_SETTINGS_ENABLE_CONNECT_PROTOCOL:Int; + var DEFAULT_SETTINGS_ENABLE_PUSH:Int; + var DEFAULT_SETTINGS_HEADER_TABLE_SIZE:Int; + var DEFAULT_SETTINGS_INITIAL_WINDOW_SIZE:Int; + var DEFAULT_SETTINGS_MAX_CONCURRENT_STREAMS:Int; + var DEFAULT_SETTINGS_MAX_FRAME_SIZE:Int; + var DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE:Int; + var HTTP2_HEADER_ACCEPT:String; + var HTTP2_HEADER_ACCEPT_CHARSET:String; + var HTTP2_HEADER_ACCEPT_ENCODING:String; + var HTTP2_HEADER_ACCEPT_LANGUAGE:String; + var HTTP2_HEADER_ACCEPT_RANGES:String; + var HTTP2_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS:String; + var HTTP2_HEADER_ACCESS_CONTROL_ALLOW_HEADERS:String; + var HTTP2_HEADER_ACCESS_CONTROL_ALLOW_METHODS:String; + var HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN:String; + var HTTP2_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS:String; + var HTTP2_HEADER_ACCESS_CONTROL_MAX_AGE:String; + var HTTP2_HEADER_ACCESS_CONTROL_REQUEST_HEADERS:String; + var HTTP2_HEADER_ACCESS_CONTROL_REQUEST_METHOD:String; + var HTTP2_HEADER_AGE:String; + var HTTP2_HEADER_ALLOW:String; + var HTTP2_HEADER_ALT_SVC:String; + var HTTP2_HEADER_AUTHORITY:String; + var HTTP2_HEADER_AUTHORIZATION:String; + var HTTP2_HEADER_CACHE_CONTROL:String; + var HTTP2_HEADER_CONNECTION:String; + var HTTP2_HEADER_CONTENT_DISPOSITION:String; + var HTTP2_HEADER_CONTENT_ENCODING:String; + var HTTP2_HEADER_CONTENT_LANGUAGE:String; + var HTTP2_HEADER_CONTENT_LENGTH:String; + var HTTP2_HEADER_CONTENT_LOCATION:String; + var HTTP2_HEADER_CONTENT_MD5:String; + var HTTP2_HEADER_CONTENT_RANGE:String; + var HTTP2_HEADER_CONTENT_SECURITY_POLICY:String; + var HTTP2_HEADER_CONTENT_TYPE:String; + var HTTP2_HEADER_COOKIE:String; + var HTTP2_HEADER_DATE:String; + var HTTP2_HEADER_DNT:String; + var HTTP2_HEADER_EARLY_DATA:String; + var HTTP2_HEADER_ETAG:String; + var HTTP2_HEADER_EXPECT:String; + var HTTP2_HEADER_EXPECT_CT:String; + var HTTP2_HEADER_EXPIRES:String; + var HTTP2_HEADER_FORWARDED:String; + var HTTP2_HEADER_FROM:String; + var HTTP2_HEADER_HOST:String; + var HTTP2_HEADER_HTTP2_SETTINGS:String; + var HTTP2_HEADER_IF_MATCH:String; + var HTTP2_HEADER_IF_MODIFIED_SINCE:String; + var HTTP2_HEADER_IF_NONE_MATCH:String; + var HTTP2_HEADER_IF_RANGE:String; + var HTTP2_HEADER_IF_UNMODIFIED_SINCE:String; + var HTTP2_HEADER_KEEP_ALIVE:String; + var HTTP2_HEADER_LAST_MODIFIED:String; + var HTTP2_HEADER_LINK:String; + var HTTP2_HEADER_LOCATION:String; + var HTTP2_HEADER_MAX_FORWARDS:String; + var HTTP2_HEADER_METHOD:String; + var HTTP2_HEADER_ORIGIN:String; + var HTTP2_HEADER_PATH:String; + var HTTP2_HEADER_PREFER:String; + var HTTP2_HEADER_PRIORITY:String; + var HTTP2_HEADER_PROTOCOL:String; + var HTTP2_HEADER_PROXY_AUTHENTICATE:String; + var HTTP2_HEADER_PROXY_AUTHORIZATION:String; + var HTTP2_HEADER_PROXY_CONNECTION:String; + var HTTP2_HEADER_PURPOSE:String; + var HTTP2_HEADER_RANGE:String; + var HTTP2_HEADER_REFERER:String; + var HTTP2_HEADER_REFRESH:String; + var HTTP2_HEADER_RETRY_AFTER:String; + var HTTP2_HEADER_SCHEME:String; + var HTTP2_HEADER_SERVER:String; + var HTTP2_HEADER_SET_COOKIE:String; + var HTTP2_HEADER_STATUS:String; + var HTTP2_HEADER_STRICT_TRANSPORT_SECURITY:String; + var HTTP2_HEADER_TE:String; + var HTTP2_HEADER_TIMING_ALLOW_ORIGIN:String; + var HTTP2_HEADER_TK:String; + var HTTP2_HEADER_TRAILER:String; + var HTTP2_HEADER_TRANSFER_ENCODING:String; + var HTTP2_HEADER_UPGRADE:String; + var HTTP2_HEADER_UPGRADE_INSECURE_REQUESTS:String; + var HTTP2_HEADER_USER_AGENT:String; + var HTTP2_HEADER_VARY:String; + var HTTP2_HEADER_VIA:String; + var HTTP2_HEADER_WARNING:String; + var HTTP2_HEADER_WWW_AUTHENTICATE:String; + var HTTP2_HEADER_X_CONTENT_TYPE_OPTIONS:String; + var HTTP2_HEADER_X_FORWARDED_FOR:String; + var HTTP2_HEADER_X_FRAME_OPTIONS:String; + var HTTP2_HEADER_X_XSS_PROTECTION:String; + var HTTP2_METHOD_ACL:String; + var HTTP2_METHOD_BASELINE_CONTROL:String; + var HTTP2_METHOD_BIND:String; + var HTTP2_METHOD_CHECKIN:String; + var HTTP2_METHOD_CHECKOUT:String; + var HTTP2_METHOD_CONNECT:String; + var HTTP2_METHOD_COPY:String; + var HTTP2_METHOD_DELETE:String; + var HTTP2_METHOD_GET:String; + var HTTP2_METHOD_HEAD:String; + var HTTP2_METHOD_LABEL:String; + var HTTP2_METHOD_LINK:String; + var HTTP2_METHOD_LOCK:String; + var HTTP2_METHOD_MERGE:String; + var HTTP2_METHOD_MKACTIVITY:String; + var HTTP2_METHOD_MKCALENDAR:String; + var HTTP2_METHOD_MKCOL:String; + var HTTP2_METHOD_MKREDIRECTREF:String; + var HTTP2_METHOD_MKWORKSPACE:String; + var HTTP2_METHOD_MOVE:String; + var HTTP2_METHOD_OPTIONS:String; + var HTTP2_METHOD_ORDERPATCH:String; + var HTTP2_METHOD_PATCH:String; + var HTTP2_METHOD_POST:String; + var HTTP2_METHOD_PRI:String; + var HTTP2_METHOD_PROPFIND:String; + var HTTP2_METHOD_PROPPATCH:String; + var HTTP2_METHOD_PUT:String; + var HTTP2_METHOD_REBIND:String; + var HTTP2_METHOD_REPORT:String; + var HTTP2_METHOD_SEARCH:String; + var HTTP2_METHOD_TRACE:String; + var HTTP2_METHOD_UNBIND:String; + var HTTP2_METHOD_UNCHECKOUT:String; + var HTTP2_METHOD_UNLINK:String; + var HTTP2_METHOD_UNLOCK:String; + var HTTP2_METHOD_UPDATE:String; + var HTTP2_METHOD_UPDATEREDIRECTREF:String; + var HTTP2_METHOD_VERSION_CONTROL:String; + var HTTP_STATUS_ACCEPTED:Int; + var HTTP_STATUS_ALREADY_REPORTED:Int; + var HTTP_STATUS_BAD_GATEWAY:Int; + var HTTP_STATUS_BAD_REQUEST:Int; + var HTTP_STATUS_BANDWIDTH_LIMIT_EXCEEDED:Int; + var HTTP_STATUS_CONFLICT:Int; + var HTTP_STATUS_CONTINUE:Int; + var HTTP_STATUS_CREATED:Int; + var HTTP_STATUS_EARLY_HINTS:Int; + var HTTP_STATUS_EXPECTATION_FAILED:Int; + var HTTP_STATUS_FAILED_DEPENDENCY:Int; + var HTTP_STATUS_FORBIDDEN:Int; + var HTTP_STATUS_FOUND:Int; + var HTTP_STATUS_GATEWAY_TIMEOUT:Int; + var HTTP_STATUS_GONE:Int; + var HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED:Int; + var HTTP_STATUS_IM_USED:Int; + var HTTP_STATUS_INSUFFICIENT_STORAGE:Int; + var HTTP_STATUS_INTERNAL_SERVER_ERROR:Int; + var HTTP_STATUS_LENGTH_REQUIRED:Int; + var HTTP_STATUS_LOCKED:Int; + var HTTP_STATUS_LOOP_DETECTED:Int; + var HTTP_STATUS_METHOD_NOT_ALLOWED:Int; + var HTTP_STATUS_MISDIRECTED_REQUEST:Int; + var HTTP_STATUS_MOVED_PERMANENTLY:Int; + var HTTP_STATUS_MULTIPLE_CHOICES:Int; + var HTTP_STATUS_MULTI_STATUS:Int; + var HTTP_STATUS_NETWORK_AUTHENTICATION_REQUIRED:Int; + var HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION:Int; + var HTTP_STATUS_NOT_ACCEPTABLE:Int; + var HTTP_STATUS_NOT_EXTENDED:Int; + var HTTP_STATUS_NOT_FOUND:Int; + var HTTP_STATUS_NOT_IMPLEMENTED:Int; + var HTTP_STATUS_NOT_MODIFIED:Int; + var HTTP_STATUS_NO_CONTENT:Int; + var HTTP_STATUS_OK:Int; + var HTTP_STATUS_PARTIAL_CONTENT:Int; + var HTTP_STATUS_PAYLOAD_TOO_LARGE:Int; + var HTTP_STATUS_PAYMENT_REQUIRED:Int; + var HTTP_STATUS_PERMANENT_REDIRECT:Int; + var HTTP_STATUS_PRECONDITION_FAILED:Int; + var HTTP_STATUS_PRECONDITION_REQUIRED:Int; + var HTTP_STATUS_PROCESSING:Int; + var HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED:Int; + var HTTP_STATUS_RANGE_NOT_SATISFIABLE:Int; + var HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE:Int; + var HTTP_STATUS_REQUEST_TIMEOUT:Int; + var HTTP_STATUS_RESET_CONTENT:Int; + var HTTP_STATUS_SEE_OTHER:Int; + var HTTP_STATUS_SERVICE_UNAVAILABLE:Int; + var HTTP_STATUS_SWITCHING_PROTOCOLS:Int; + var HTTP_STATUS_TEAPOT:Int; + var HTTP_STATUS_TEMPORARY_REDIRECT:Int; + var HTTP_STATUS_TOO_EARLY:Int; + var HTTP_STATUS_TOO_MANY_REQUESTS:Int; + var HTTP_STATUS_UNAUTHORIZED:Int; + var HTTP_STATUS_UNAVAILABLE_FOR_LEGAL_REASONS:Int; + var HTTP_STATUS_UNPROCESSABLE_ENTITY:Int; + var HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE:Int; + var HTTP_STATUS_UPGRADE_REQUIRED:Int; + var HTTP_STATUS_URI_TOO_LONG:Int; + var HTTP_STATUS_USE_PROXY:Int; + var HTTP_STATUS_VARIANT_ALSO_NEGOTIATES:Int; + var MAX_INITIAL_WINDOW_SIZE:Int; + var MAX_MAX_FRAME_SIZE:Int; + var MIN_MAX_FRAME_SIZE:Int; + var NGHTTP2_CANCEL:Int; + var NGHTTP2_COMPRESSION_ERROR:Int; + var NGHTTP2_CONNECT_ERROR:Int; + var NGHTTP2_DEFAULT_WEIGHT:Int; + var NGHTTP2_ENHANCE_YOUR_CALM:Int; + var NGHTTP2_ERR_FRAME_SIZE_ERROR:Int; + var NGHTTP2_FLAG_ACK:Int; + var NGHTTP2_FLAG_END_HEADERS:Int; + var NGHTTP2_FLAG_END_STREAM:Int; + var NGHTTP2_FLAG_NONE:Int; + var NGHTTP2_FLAG_PADDED:Int; + var NGHTTP2_FLAG_PRIORITY:Int; + var NGHTTP2_FLOW_CONTROL_ERROR:Int; + var NGHTTP2_FRAME_SIZE_ERROR:Int; + var NGHTTP2_HTTP_1_1_REQUIRED:Int; + var NGHTTP2_INADEQUATE_SECURITY:Int; + var NGHTTP2_INTERNAL_ERROR:Int; + var NGHTTP2_NO_ERROR:Int; + var NGHTTP2_PROTOCOL_ERROR:Int; + var NGHTTP2_REFUSED_STREAM:Int; + var NGHTTP2_SESSION_CLIENT:Int; + var NGHTTP2_SESSION_SERVER:Int; + var NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:Int; + var NGHTTP2_SETTINGS_ENABLE_PUSH:Int; + var NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:Int; + var NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:Int; + var NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:Int; + var NGHTTP2_SETTINGS_MAX_FRAME_SIZE:Int; + var NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:Int; + var NGHTTP2_SETTINGS_TIMEOUT:Int; + var NGHTTP2_STREAM_CLOSED:Int; + var NGHTTP2_STREAM_STATE_CLOSED:Int; + var NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL:Int; + var NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE:Int; + var NGHTTP2_STREAM_STATE_IDLE:Int; + var NGHTTP2_STREAM_STATE_OPEN:Int; + var NGHTTP2_STREAM_STATE_RESERVED_LOCAL:Int; + var NGHTTP2_STREAM_STATE_RESERVED_REMOTE:Int; + var PADDING_STRATEGY_ALIGNED:Int; + var PADDING_STRATEGY_CALLBACK:Int; + var PADDING_STRATEGY_MAX:Int; + var PADDING_STRATEGY_NONE:Int; +} diff --git a/src/js/node/http2/Http2SecureServer.hx b/src/js/node/http2/Http2SecureServer.hx new file mode 100644 index 00000000..b1dbaa3e --- /dev/null +++ b/src/js/node/http2/Http2SecureServer.hx @@ -0,0 +1,99 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.http2; + +import js.node.Http2.Http2Headers; +import js.node.Http2.Http2Settings; +import js.node.events.EventEmitter.Event; +import js.node.stream.Duplex.IDuplex; +import js.lib.Error; + +/** + Events emitted by `Http2SecureServer` in addition to its parent `tls.Server` events. +**/ +enum abstract Http2SecureServerEvent(Event) to Event { + /** + Emitted each time a request with an HTTP `Expect: 100-continue` is received. + **/ + var CheckContinue:Http2SecureServerEvent<(request:Http2ServerRequest, response:Http2ServerResponse) -> Void> = "checkContinue"; + + /** + Emitted when a new TCP stream is established, before the TLS handshake begins. + **/ + var Connection:Http2SecureServerEventVoid> = "connection"; + + /** + Emitted each time there is a request. See the Compatibility API. + **/ + var Request:Http2SecureServerEvent<(request:Http2ServerRequest, response:Http2ServerResponse) -> Void> = "request"; + + /** + Emitted when a new `Http2Session` is created by the `Http2SecureServer`. + **/ + var Session:Http2SecureServerEventVoid> = "session"; + + /** + Emitted when an `'error'` event is emitted by an `Http2Session` associated with the server. + **/ + var SessionError:Http2SecureServerEvent<(error:Error, session:ServerHttp2Session) -> Void> = "sessionError"; + + /** + Emitted when a `'stream'` event has been emitted by an `Http2Session` associated with the server. + **/ + var Stream:Http2SecureServerEvent<(stream:ServerHttp2Stream, headers:Http2Headers, flags:Int, rawHeaders:Array) -> Void> = "stream"; + + /** + Emitted when there is no activity on the Server for a given number of milliseconds. + **/ + var Timeout:Http2SecureServerEventVoid> = "timeout"; + + /** + Emitted when a connecting client fails to negotiate an allowed protocol (HTTP/2 or HTTP/1.1). + **/ + var UnknownProtocol:Http2SecureServerEventVoid> = "unknownProtocol"; +} + +/** + Instances of `Http2SecureServer` are created using `http2.createSecureServer()`. + The class is not exported directly by the `node:http2` module. + + @see https://nodejs.org/api/http2.html#class-http2secureserver +**/ +extern class Http2SecureServer extends js.node.tls.Server { + /** + Used to set the timeout value for http2 secure server requests. + **/ + @:overload(function(?callback:Void->Void):Http2SecureServer {}) + function setTimeout(?msecs:Int, ?callback:Void->Void):Http2SecureServer; + + /** + The number of milliseconds of inactivity before a socket is presumed to have timed out. + Default: `0` (no timeout). + **/ + var timeout:Int; + + /** + Used to update the server with the provided settings. + **/ + function updateSettings(?settings:Http2Settings):Void; +} diff --git a/src/js/node/http2/Http2Server.hx b/src/js/node/http2/Http2Server.hx new file mode 100644 index 00000000..3ee1cae8 --- /dev/null +++ b/src/js/node/http2/Http2Server.hx @@ -0,0 +1,94 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.http2; + +import js.node.Http2.Http2Headers; +import js.node.Http2.Http2Settings; +import js.node.events.EventEmitter.Event; +import js.node.stream.Duplex.IDuplex; +import js.lib.Error; + +/** + Events emitted by `Http2Server` in addition to its parent `net.Server` events. +**/ +enum abstract Http2ServerEvent(Event) to Event { + /** + Emitted each time a request with an HTTP `Expect: 100-continue` is received. + **/ + var CheckContinue:Http2ServerEvent<(request:Http2ServerRequest, response:Http2ServerResponse) -> Void> = "checkContinue"; + + /** + Emitted when a new TCP stream is established. + **/ + var Connection:Http2ServerEventVoid> = "connection"; + + /** + Emitted each time there is a request. See the Compatibility API. + **/ + var Request:Http2ServerEvent<(request:Http2ServerRequest, response:Http2ServerResponse) -> Void> = "request"; + + /** + Emitted when a new `Http2Session` is created by the `Http2Server`. + **/ + var Session:Http2ServerEventVoid> = "session"; + + /** + Emitted when an `'error'` event is emitted by an `Http2Session` associated with the server. + **/ + var SessionError:Http2ServerEvent<(error:Error, session:ServerHttp2Session) -> Void> = "sessionError"; + + /** + Emitted when a `'stream'` event has been emitted by an `Http2Session` associated with the server. + **/ + var Stream:Http2ServerEvent<(stream:ServerHttp2Stream, headers:Http2Headers, flags:Int, rawHeaders:Array) -> Void> = "stream"; + + /** + Emitted when there is no activity on the Server for a given number of milliseconds. + **/ + var Timeout:Http2ServerEventVoid> = "timeout"; +} + +/** + Instances of `Http2Server` are created using `http2.createServer()`. + The class is not exported directly by the `node:http2` module. + + @see https://nodejs.org/api/http2.html#class-http2server +**/ +extern class Http2Server extends js.node.net.Server { + /** + Used to set the timeout value for http2 server requests. + **/ + @:overload(function(?callback:Void->Void):Http2Server {}) + function setTimeout(?msecs:Int, ?callback:Void->Void):Http2Server; + + /** + The number of milliseconds of inactivity before a socket is presumed to have timed out. + Default: `0` (no timeout). + **/ + var timeout:Int; + + /** + Used to update the server with the provided settings. + **/ + function updateSettings(?settings:Http2Settings):Void; +} diff --git a/src/js/node/http2/Http2ServerRequest.hx b/src/js/node/http2/Http2ServerRequest.hx new file mode 100644 index 00000000..2008c3e3 --- /dev/null +++ b/src/js/node/http2/Http2ServerRequest.hx @@ -0,0 +1,147 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.http2; + +import haxe.DynamicAccess; +import haxe.extern.EitherType; +import js.node.Http2.Http2Headers; +import js.node.events.EventEmitter.Event; +import js.node.net.Socket; +import js.node.stream.Readable; +import js.lib.Error; + +/** + Events emitted by `Http2ServerRequest` in addition to its parent class events. +**/ +enum abstract Http2ServerRequestEvent(Event) to Event { + /** + Emitted whenever a `Http2ServerRequest` instance is abnormally aborted in mid-communication. + **/ + var Aborted:Http2ServerRequestEventVoid> = "aborted"; + + /** + Indicates that the underlying `Http2Stream` was closed. + **/ + var Close:Http2ServerRequestEventVoid> = "close"; +} + +/** + A `Http2ServerRequest` object is created by `http2.Server` or `http2.SecureServer` + and passed as the first argument to the `'request'` event. + + @see https://nodejs.org/api/http2.html#class-http2http2serverrequest +**/ +@:jsRequire("http2", "Http2ServerRequest") +extern class Http2ServerRequest extends Readable { + /** + The `request.aborted` property will be `true` if the request has been aborted. + **/ + var aborted(default, null):Bool; + + /** + The request authority pseudo header field. + **/ + var authority(default, null):String; + + /** + The `request.complete` property will be `true` if the request has been completed, aborted, or destroyed. + **/ + var complete(default, null):Bool; + + /** + See `request.socket`. + **/ + @:deprecated("Use request.socket instead") + var connection(default, null):Socket; + + /** + Calls `destroy()` on the `Http2Stream` that received the `Http2ServerRequest`. + **/ + override function destroy(?error:Error):Http2ServerRequest; + + /** + The request/response headers object. + **/ + var headers(default, null):Http2Headers; + + /** + In case of server request, the HTTP version sent by the client. Returns `'2.0'`. + **/ + var httpVersion(default, null):String; + + /** + HTTP Version first integer. + **/ + var httpVersionMajor(default, null):Int; + + /** + HTTP Version second integer. + **/ + var httpVersionMinor(default, null):Int; + + /** + The request method as a string. Read-only. + **/ + var method(default, null):String; + + /** + The raw request/response headers list exactly as they were received. + **/ + var rawHeaders(default, null):Array; + + /** + The raw request/response trailer keys and values exactly as they were received. + **/ + var rawTrailers(default, null):Array; + + /** + The request scheme pseudo header field. + **/ + var scheme(default, null):String; + + /** + Sets the `Http2Stream`'s timeout value to `msecs`. + **/ + function setTimeout(msecs:Int, ?callback:Void->Void):Http2ServerRequest; + + /** + Returns a `Proxy` object that acts as a `net.Socket` (or `tls.TLSSocket`) + but applies getters, setters, and methods based on HTTP/2 logic. + **/ + var socket(default, null):Socket; + + /** + The `Http2Stream` object backing the request. + **/ + var stream(default, null):ServerHttp2Stream; + + /** + The request/response trailers object. Only populated at the `'end'` event. + **/ + var trailers(default, null):DynamicAccess; + + /** + Request URL string. Contains only the URL present in the actual HTTP request. + **/ + var url:String; +} diff --git a/src/js/node/http2/Http2ServerResponse.hx b/src/js/node/http2/Http2ServerResponse.hx new file mode 100644 index 00000000..0a3dee02 --- /dev/null +++ b/src/js/node/http2/Http2ServerResponse.hx @@ -0,0 +1,192 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.http2; + +import haxe.DynamicAccess; +import haxe.extern.EitherType; +import js.node.Buffer; +import js.node.Http2.Http2Headers; +import js.node.events.EventEmitter.Event; +import js.node.net.Socket; +import js.node.stream.Writable; +import js.lib.Error; + +/** + Events emitted by `Http2ServerResponse` in addition to its parent class events. +**/ +enum abstract Http2ServerResponseEvent(Event) to Event { + /** + Indicates that the underlying `Http2Stream` was terminated before `response.end()` was called or able to flush. + **/ + var Close:Http2ServerResponseEventVoid> = "close"; + + /** + Emitted when the response has been sent. + **/ + var Finish:Http2ServerResponseEventVoid> = "finish"; +} + +/** + This object is created internally by an HTTP server — not by the user. + It is passed as the second parameter to the `'request'` event. + + @see https://nodejs.org/api/http2.html#class-http2http2serverresponse +**/ +@:jsRequire("http2", "Http2ServerResponse") +extern class Http2ServerResponse extends Writable { + /** + This method adds HTTP trailing headers to the response. + **/ + function addTrailers(headers:Http2Headers):Void; + + /** + Append a single header value to the header object. + **/ + @:overload(function(name:String, value:Array):Void {}) + function appendHeader(name:String, value:String):Void; + + /** + Flushes the response headers to the client. + **/ + function flushHeaders():Void; + + /** + See `response.socket`. + **/ + @:deprecated("Use response.socket instead") + var connection(default, null):Socket; + + /** + Call `http2stream.pushStream()` with the given headers, and wrap the given `Http2Stream` + on a newly created `Http2ServerResponse` as the callback parameter if successful. + **/ + function createPushResponse(headers:Http2Headers, callback:(err:Null, res:Http2ServerResponse) -> Void):Void; + + /** + Boolean value that indicates whether the response has completed. + **/ + @:deprecated("Use response.writableEnded instead") + var finished(default, null):Bool; + + /** + Reads out a header that has already been queued but not sent to the client. + **/ + function getHeader(name:String):EitherType>; + + /** + Returns an array containing the unique names of the current outgoing headers. + **/ + function getHeaderNames():Array; + + /** + Returns a shallow copy of the current outgoing headers. + **/ + function getHeaders():DynamicAccess>>; + + /** + Returns `true` if the header identified by `name` is currently set in the outgoing headers. + **/ + function hasHeader(name:String):Bool; + + /** + True if headers were sent, false otherwise (read-only). + **/ + var headersSent(default, null):Bool; + + /** + Removes a header that has been queued for implicit sending. + **/ + function removeHeader(name:String):Void; + + /** + A reference to the original HTTP2 `request` object. + **/ + var req(default, null):Http2ServerRequest; + + /** + When true, the Date header will be automatically generated and sent in the response if it is not already present. + **/ + var sendDate:Bool; + + /** + Sets a single header value for implicit headers. + **/ + @:overload(function(name:String, value:Array):Void {}) + function setHeader(name:String, value:String):Void; + + /** + Sets a single trailing header value. + Must be called before the trailers are flushed. + **/ + @:overload(function(name:String, value:Array):Void {}) + function setTrailer(name:String, value:String):Void; + + /** + Sets the `Http2Stream`'s timeout value to `msecs`. + **/ + function setTimeout(msecs:Int, ?callback:Void->Void):Http2ServerResponse; + + /** + Returns a `Proxy` object that acts as a `net.Socket` (or `tls.TLSSocket`) + but applies getters, setters, and methods based on HTTP/2 logic. + **/ + var socket(default, null):Socket; + + /** + When using implicit headers, this property controls the status code that will be sent to the client. + **/ + var statusCode:Int; + + /** + Status message is not supported by HTTP/2. It returns an empty string. + **/ + var statusMessage:String; + + /** + The `Http2Stream` object backing the response. + **/ + var stream(default, null):ServerHttp2Stream; + + /** + Sends a status `100 Continue` to the client. + **/ + function writeContinue():Void; + + /** + Sends a status `103 Early Hints` to the client with a Link header. + **/ + function writeEarlyHints(hints:DynamicAccess>>):Void; + + /** + Sends an arbitrary HTTP 1xx informational response. + Added in Node.js v24.18.0 (Active LTS); not available on Maintenance LTS 22.x. + **/ + function writeInformation(statusCode:Int, ?headers:Http2Headers):Bool; + + /** + Sends a response header to the request. + **/ + @:overload(function(statusCode:Int, ?headers:Http2Headers):Void {}) + @:overload(function(statusCode:Int, statusMessage:String, ?headers:Http2Headers):Void {}) + function writeHead(statusCode:Int, ?headers:DynamicAccess>>):Void; +} diff --git a/src/js/node/http2/Http2Session.hx b/src/js/node/http2/Http2Session.hx new file mode 100644 index 00000000..fe1ceba5 --- /dev/null +++ b/src/js/node/http2/Http2Session.hx @@ -0,0 +1,205 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.http2; + +import haxe.extern.EitherType; +import js.node.Buffer; +import js.node.Http2.Http2Headers; +import js.node.Http2.Http2SessionState; +import js.node.Http2.Http2Settings; +import js.node.events.EventEmitter; +import js.node.events.EventEmitter.Event; +import js.node.net.Socket; +import js.lib.Error; + +/** + Enumeration of events emitted by `Http2Session` in addition to its parent class events. +**/ +enum abstract Http2SessionEvent(Event) to Event { + /** + Emitted once the `Http2Session` has been destroyed. + **/ + var Close:Http2SessionEventVoid> = "close"; + + /** + Emitted once the `Http2Session` has been successfully connected to the remote peer. + **/ + var Connect:Http2SessionEvent<(session:Http2Session, socket:Socket) -> Void> = "connect"; + + /** + Emitted when an error occurs during processing of an `Http2Session`. + **/ + var Error:Http2SessionEventVoid> = "error"; + + /** + Emitted when an error occurs while attempting to send a frame on the session. + **/ + var FrameError:Http2SessionEvent<(type:Int, code:Int, id:Int) -> Void> = "frameError"; + + /** + Emitted when a `GOAWAY` frame is received. + **/ + var Goaway:Http2SessionEvent<(errorCode:Int, lastStreamID:Int, opaqueData:Buffer) -> Void> = "goaway"; + + /** + Emitted when an acknowledgment `SETTINGS` frame has been received. + **/ + var LocalSettings:Http2SessionEventVoid> = "localSettings"; + + /** + Emitted whenever a `PING` frame is received from the connected peer. + **/ + var Ping:Http2SessionEventVoid> = "ping"; + + /** + Emitted when a new `SETTINGS` frame is received from the connected peer. + **/ + var RemoteSettings:Http2SessionEventVoid> = "remoteSettings"; + + /** + Emitted when a new `Http2Stream` is created. + **/ + var Stream:Http2SessionEvent<(stream:Http2Stream, headers:Http2Headers, flags:Int, rawHeaders:Array) -> Void> = "stream"; + + /** + Emitted after the `Http2Session` times out due to inactivity. + **/ + var Timeout:Http2SessionEventVoid> = "timeout"; +} + +/** + The `Http2Session` class represents an active communications session between an HTTP/2 client and server. + + Instances of this class are not intended to be constructed directly by user code. + The class is not exported directly by the `node:http2` module. + + @see https://nodejs.org/api/http2.html#class-http2session +**/ +extern class Http2Session extends EventEmitter { + /** + Value will be `undefined` if the session is not yet connected to a socket, + `'h2c'` if not connected to a `TLSSocket`, or the connected socket's `alpnProtocol`. + **/ + var alpnProtocol(default, null):Null; + + /** + `true` if this `Http2Session` instance has been closed. + **/ + var closed(default, null):Bool; + + /** + `true` if this `Http2Session` instance is still connecting. + **/ + var connecting(default, null):Bool; + + /** + `true` if this `Http2Session` instance has been destroyed. + **/ + var destroyed(default, null):Bool; + + /** + `true` if connected with a `TLSSocket`, `false` for other sockets, `undefined` if not yet connected. + **/ + var encrypted(default, null):Null; + + /** + A prototype-less object describing the current local settings of this session. + **/ + var localSettings(default, null):Http2Settings; + + /** + Array of origins for which the session may be considered authoritative (TLS only). + **/ + var originSet(default, null):Null>; + + /** + `true` while waiting for acknowledgment of a sent `SETTINGS` frame. + **/ + var pendingSettingsAck(default, null):Bool; + + /** + A prototype-less object describing the current remote settings of this session. + **/ + var remoteSettings(default, null):Http2Settings; + + /** + A `Proxy` object that acts as a `net.Socket` / `tls.TLSSocket` but limits available methods. + **/ + var socket(default, null):Socket; + + /** + Miscellaneous information about the current state of the session. + **/ + var state(default, null):Http2SessionState; + + /** + Equal to `NGHTTP2_SESSION_SERVER` or `NGHTTP2_SESSION_CLIENT`. + **/ + var type(default, null):Int; + + /** + Gracefully closes the `Http2Session`, allowing existing streams to complete. + **/ + function close(?callback:Void->Void):Void; + + /** + Immediately terminates the `Http2Session` and the associated socket. + **/ + function destroy(?error:Error, ?code:Int):Void; + + /** + Transmits a `GOAWAY` frame without shutting down the session. + **/ + function goaway(?code:Int, ?lastStreamID:Int, ?opaqueData:Buffer):Void; + + /** + Sends a `PING` frame to the connected HTTP/2 peer. + **/ + @:overload(function(payload:Buffer, callback:(err:Null, duration:Float, payload:Buffer) -> Void):Bool {}) + function ping(callback:(err:Null, duration:Float, payload:Buffer) -> Void):Bool; + + /** + Calls `ref()` on this session's underlying socket. + **/ + function ref():Void; + + /** + Sets the local endpoint's window size (total, not delta). + **/ + function setLocalWindowSize(windowSize:Int):Void; + + /** + Sets a callback invoked when there is no activity on the session after `msecs` milliseconds. + **/ + function setTimeout(msecs:Int, ?callback:Void->Void):Void; + + /** + Updates the current local settings and sends a new `SETTINGS` frame. + **/ + function settings(settings:Http2Settings, ?callback:(err:Null, settings:Http2Settings, duration:Float) -> Void):Void; + + /** + Calls `unref()` on this session's underlying socket. + **/ + function unref():Void; +} diff --git a/src/js/node/http2/Http2Stream.hx b/src/js/node/http2/Http2Stream.hx new file mode 100644 index 00000000..5ca3de9d --- /dev/null +++ b/src/js/node/http2/Http2Stream.hx @@ -0,0 +1,161 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.http2; + +import js.node.Http2.Http2Headers; +import js.node.Http2.Http2StreamState; +import js.node.events.EventEmitter.Event; +import js.node.stream.Duplex; + +/** + Enumeration of events emitted by `Http2Stream` in addition to duplex stream events. +**/ +enum abstract Http2StreamEvent(Event) to Event { + /** + Emitted when the `Http2Stream` instance is aborted abnormally. + **/ + var Aborted:Http2StreamEventVoid> = "aborted"; + + /** + Emitted when an error occurs while attempting to send a frame. + **/ + var FrameError:Http2StreamEvent<(type:Int, code:Int, id:Int) -> Void> = "frameError"; + + /** + Emitted when the `Http2Stream` has been opened and is ready for use. + **/ + var Ready:Http2StreamEventVoid> = "ready"; + + /** + Emitted when the `Http2Stream` is destroyed (Duplex `'close'`). + No listener arguments; use `stream.rstCode` for the `RST_STREAM` error code. + (The legacy `'streamClosed'` event is no longer emitted on Node.js LTS.) + **/ + var Close:Http2StreamEventVoid> = "close"; + + /** + Emitted after the stream times out due to inactivity. + **/ + var Timeout:Http2StreamEventVoid> = "timeout"; + + /** + Emitted when trailing headers are received. + **/ + var Trailers:Http2StreamEvent<(trailers:Http2Headers, flags:Int) -> Void> = "trailers"; + + /** + Emitted when the stream is ready for trailing headers to be sent. + **/ + var WantTrailers:Http2StreamEventVoid> = "wantTrailers"; +} + +/** + A duplex `Http2Stream` class representing a bidirectional HTTP/2 stream. + The class is not exported directly by the `node:http2` module. + + @see https://nodejs.org/api/http2.html#class-http2stream +**/ +extern class Http2Stream extends Duplex { + /** + `true` if the `Http2Stream` instance was aborted abnormally. + **/ + var aborted(default, null):Bool; + + /** + Number of characters currently buffered to be written. + **/ + var bufferSize(default, null):Int; + + /** + `true` if the `Http2Stream` instance has been closed. + **/ + var closed(default, null):Bool; + + // `destroyed` is inherited from Readable/Duplex. + + /** + `true` if the `END_STREAM` flag was set in the received headers. + **/ + var endAfterHeaders(default, null):Bool; + + /** + The numeric stream identifier, or `undefined` if not yet assigned. + **/ + var id(default, null):Null; + + /** + `true` if the stream has not yet been assigned a numeric stream identifier. + **/ + var pending(default, null):Bool; + + /** + The `RST_STREAM` error code reported when the stream was destroyed, if any. + **/ + var rstCode(default, null):Null; + + /** + Outbound headers sent for this stream. + **/ + var sentHeaders(default, null):Http2Headers; + + /** + Outbound informational headers sent for this stream. + **/ + var sentInfoHeaders(default, null):Null>; + + /** + Outbound trailers sent for this stream. + **/ + var sentTrailers(default, null):Null; + + /** + The `Http2Session` that owns this stream. + **/ + var session(default, null):Null; + + /** + Miscellaneous information about the current state of the stream. + **/ + var state(default, null):Http2StreamState; + + /** + Closes the stream by sending an `RST_STREAM` frame. + **/ + function close(?code:Int, ?callback:Void->Void):Void; + + /** + Priority signaling is no longer supported in Node.js. + **/ + @:deprecated("Priority signaling is no longer supported in Node.js") + function priority(options:Dynamic):Void; + + /** + Sets the stream timeout value. + **/ + function setTimeout(msecs:Int, ?callback:Void->Void):Void; + + /** + Sends a trailing `HEADERS` frame. Must be called after `'wantTrailers'`. + **/ + function sendTrailers(headers:Http2Headers):Void; +} diff --git a/src/js/node/http2/ServerHttp2Session.hx b/src/js/node/http2/ServerHttp2Session.hx new file mode 100644 index 00000000..ade5a4af --- /dev/null +++ b/src/js/node/http2/ServerHttp2Session.hx @@ -0,0 +1,75 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.http2; + +import haxe.extern.EitherType; +import haxe.extern.Rest; +import js.node.Http2.Http2Headers; +import js.node.events.EventEmitter.Event; +import js.node.net.Socket; +import js.node.url.URL; + +/** + Options object accepted by `ServerHttp2Session.altsvc` for an origin. +**/ +typedef AlternativeServiceOptions = { + var origin:EitherType>; +} + +/** + Enumeration of events emitted by `ServerHttp2Session` in addition to `Http2Session` events. +**/ +enum abstract ServerHttp2SessionEvent(Event) to Event { + /** + Emitted once the server session has been successfully connected. + **/ + var Connect:ServerHttp2SessionEvent<(session:ServerHttp2Session, socket:Socket) -> Void> = "connect"; + + /** + Emitted when a new server stream is created. + **/ + var Stream:ServerHttp2SessionEvent<(stream:ServerHttp2Stream, headers:Http2Headers, flags:Int, rawHeaders:Array) -> Void> = "stream"; +} + +/** + An `Http2Session` for use on an HTTP/2 server. + The class is not exported directly by the `node:http2` module. + + @see https://nodejs.org/api/http2.html#class-serverhttp2session +**/ +extern class ServerHttp2Session extends Http2Session { + /** + The `Http2Server` or `Http2SecureServer` that owns this session. + **/ + var server(default, null):EitherType; + + /** + Submits an `ALTSVC` frame to the connected client. + **/ + function altsvc(alt:String, originOrStream:EitherType>>):Void; + + /** + Submits an `ORIGIN` frame advertising the set of origins for which the server is authoritative. + **/ + function origin(origins:Rest>>):Void; +} diff --git a/src/js/node/http2/ServerHttp2Stream.hx b/src/js/node/http2/ServerHttp2Stream.hx new file mode 100644 index 00000000..fa5de558 --- /dev/null +++ b/src/js/node/http2/ServerHttp2Stream.hx @@ -0,0 +1,77 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.http2; + +import js.node.Http2.Http2Headers; +import js.node.Http2.Http2PushStreamOptions; +import js.node.Http2.Http2ServerStreamFileResponseOptions; +import js.node.Http2.Http2ServerStreamResponseOptions; +import js.lib.Error; + +/** + An `Http2Stream` for use on an HTTP/2 server. + The class is not exported directly by the `node:http2` module. + + @see https://nodejs.org/api/http2.html#class-serverhttp2stream +**/ +extern class ServerHttp2Stream extends Http2Stream { + /** + `true` if headers were sent, `false` otherwise. + **/ + var headersSent(default, null):Bool; + + /** + `true` if the remote peer accepts push streams. + **/ + var pushAllowed(default, null):Bool; + + /** + Sends an additional informational `HEADERS` frame. + **/ + function additionalHeaders(headers:Http2Headers):Void; + + /** + Initiates a push stream. + **/ + @:overload(function(headers:Http2Headers, options:Http2PushStreamOptions, + ?callback:(err:Null, pushStream:ServerHttp2Stream, headers:Http2Headers) -> Void):Void {}) + function pushStream(headers:Http2Headers, ?callback:(err:Null, pushStream:ServerHttp2Stream, headers:Http2Headers) -> Void):Void; + + /** + Sends an HTTP/2 response header to the connected client. + **/ + @:overload(function(?headers:Array, ?options:Http2ServerStreamResponseOptions):Void {}) + function respond(?headers:Http2Headers, ?options:Http2ServerStreamResponseOptions):Void; + + /** + Sends a regular file as the response. + **/ + @:overload(function(path:String, ?options:Http2ServerStreamFileResponseOptions):Void {}) + function respondWithFile(path:String, ?headers:Http2Headers, ?options:Http2ServerStreamFileResponseOptions):Void; + + /** + Sends a regular file as the response using an open file descriptor. + **/ + @:overload(function(fd:Int, ?options:Http2ServerStreamFileResponseOptions):Void {}) + function respondWithFD(fd:Int, ?headers:Http2Headers, ?options:Http2ServerStreamFileResponseOptions):Void; +} From 66e5c57c5f5a1eaec77cb112b609860f79ea860a Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Tue, 14 Jul 2026 15:43:34 +1000 Subject: [PATCH 30/48] Add fs/promises and FileHandle externs (#215) * Add fs/promises externs and FileHandle Expose Node's promise-based filesystem API as thin wrappers over existing Fs typedefs, including the FileHandle class returned by open(). Co-authored-by: Cursor * Fix FileHandle extern after LTS review Drop invalid @:jsRequire for FileHandle, add readableWebStream, and use js.node.web.AbortSignal for watch options. Co-authored-by: Cursor --------- Co-authored-by: Cursor --- src/js/node/FsPromises.hx | 300 +++++++++++++++++++++++++++++++++++ src/js/node/fs/FileHandle.hx | 274 ++++++++++++++++++++++++++++++++ 2 files changed, 574 insertions(+) create mode 100644 src/js/node/FsPromises.hx create mode 100644 src/js/node/fs/FileHandle.hx diff --git a/src/js/node/FsPromises.hx b/src/js/node/FsPromises.hx new file mode 100644 index 00000000..d01d83ad --- /dev/null +++ b/src/js/node/FsPromises.hx @@ -0,0 +1,300 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node; + +import haxe.extern.EitherType; +import js.node.Fs; +import js.node.fs.Dir; +import js.node.fs.Dirent; +import js.node.fs.FileHandle; +import js.node.fs.Stats; +import js.node.fs.StatsFs; +#if haxe4 +import js.lib.Promise; +#else +import js.Promise; +#end + +/** + Minimal async iterator surface used by `glob` / `watch` (for `for await...of`). +**/ +typedef FsPromisesAsyncIterator = { + function next():Promise<{done:Bool, ?value:T}>; +} + +/** + The `fs/promises` API provides asynchronous file system methods that return promises. + + The API is accessible via `require('fs/promises')` or `require('fs').promises`. + + @see https://nodejs.org/api/fs.html#promises-api +**/ +@:jsRequire("fs/promises") +extern class FsPromises { + /** + An object containing commonly used constants for file system operations. + Same as `Fs.constants`. + **/ + static var constants(default, null):FsConstants; + + /** + Tests a user's permissions for the file or directory specified by `path`. + **/ + @:overload(function(path:FsPath):Promise {}) + static function access(path:FsPath, mode:Int):Promise; + + /** + Asynchronously append data to a file, creating the file if it does not exist. + **/ + @:overload(function(path:EitherType, data:EitherType):Promise {}) + static function appendFile(path:EitherType, data:EitherType, + options:EitherType):Promise; + + /** + Changes file permissions of `path`. + **/ + static function chmod(path:FsPath, mode:FsMode):Promise; + + /** + Changes file ownership of `path`. + **/ + static function chown(path:FsPath, uid:Int, gid:Int):Promise; + + /** + Asynchronously copies `src` to `dest`. + **/ + @:overload(function(src:FsPath, dest:FsPath):Promise {}) + static function copyFile(src:FsPath, dest:FsPath, mode:Int):Promise; + + /** + Asynchronously copies the entire directory structure from `src` to `dest`, + including subdirectories and files. + **/ + @:overload(function(src:FsPath, dest:FsPath):Promise {}) + static function cp(src:FsPath, dest:FsPath, options:FsCpOptions):Promise; + + /** + Retrieves the files matching the specified pattern as an async iterator. + **/ + @:overload(function(pattern:EitherType>):FsPromisesAsyncIterator {}) + @:overload(function(pattern:EitherType>, options:FsGlobOptions):FsPromisesAsyncIterator {}) + static function glob(pattern:EitherType>, options:{ + ?cwd:String, + ?exclude:EitherTypeBool, Array>, + ?followSymlinks:Bool, + withFileTypes:Bool + }):FsPromisesAsyncIterator; + + /** + Changes the permissions on a symbolic link. + No-op on Windows. + **/ + static function lchmod(path:FsPath, mode:FsMode):Promise; + + /** + Changes the ownership on a symbolic link. + **/ + static function lchown(path:FsPath, uid:Int, gid:Int):Promise; + + /** + Changes the access and modification times of a file in the same way as `utimes`, + without following symlinks. + **/ + static function lutimes(path:FsPath, atime:EitherType>, + mtime:EitherType>):Promise; + + /** + Creates a new link from `existingPath` to `newPath`. + **/ + static function link(existingPath:FsPath, newPath:FsPath):Promise; + + /** + Retrieves the `fs.Stats` for the symbolic link referred to by `path`. + **/ + @:overload(function(path:FsPath):Promise {}) + static function lstat(path:FsPath, options:{?bigint:Bool}):Promise; + + /** + Asynchronously creates a directory. + When `recursive` is true, fulfills with the first directory path created, if any. + **/ + @:overload(function(path:FsPath):Promise> {}) + @:overload(function(path:FsPath, mode:FsMode):Promise> {}) + static function mkdir(path:FsPath, options:{?recursive:Bool, ?mode:FsMode}):Promise>; + + /** + Creates a unique temporary directory. + Generates six random characters to be appended behind a required `prefix` to create a unique temporary directory. + **/ + @:overload(function(prefix:String):Promise {}) + static function mkdtemp(prefix:String, options:EitherType):Promise; + + /** + Opens a `FileHandle`. + **/ + @:overload(function(path:FsPath):Promise {}) + @:overload(function(path:FsPath, flags:FsOpenFlag):Promise {}) + static function open(path:FsPath, flags:FsOpenFlag, mode:FsMode):Promise; + + /** + Asynchronously open a directory for iterative scanning. + **/ + @:overload(function(path:FsPath):Promise {}) + static function opendir(path:FsPath, options:FsOpendirOptions):Promise; + + /** + Reads the contents of a directory. + **/ + @:overload(function(path:FsPath):Promise> {}) + @:overload(function(path:FsPath, options:EitherType):Promise> {}) + static function readdir(path:FsPath, options:{?encoding:String, withFileTypes:Bool, ?recursive:Bool}):Promise>; + + /** + Asynchronously reads the entire contents of a file. + If no encoding is specified, the data is returned as a `Buffer`. + **/ + @:overload(function(path:EitherType):Promise {}) + @:overload(function(path:EitherType, options:EitherType):Promise {}) + static function readFile(path:EitherType, + ?options:{?encoding:String, ?flag:FsOpenFlag}):Promise>; + + /** + Reads the contents of the symbolic link referred to by `path`. + **/ + @:overload(function(path:FsPath):Promise {}) + static function readlink(path:FsPath, options:EitherType):Promise>; + + /** + Determines the actual location of `path` using the same semantics as the `fs.realpath` algorithm. + **/ + @:overload(function(path:FsPath):Promise {}) + static function realpath(path:FsPath, options:EitherType):Promise>; + + /** + Renames `oldPath` to `newPath`. + **/ + static function rename(oldPath:FsPath, newPath:FsPath):Promise; + + /** + Removes files and directories (modeled on the standard POSIX `rm` utility). + **/ + @:overload(function(path:FsPath):Promise {}) + static function rm(path:FsPath, options:FsRmOptions):Promise; + + /** + Removes the directory identified by `path`. + **/ + @:overload(function(path:FsPath):Promise {}) + static function rmdir(path:FsPath, options:FsRmdirOptions):Promise; + + /** + Retrieves the `fs.Stats` for the file referred to by `path`. + **/ + @:overload(function(path:FsPath):Promise {}) + static function stat(path:FsPath, options:{?bigint:Bool}):Promise; + + /** + Retrieves information about the file system containing the given `path`. + **/ + @:overload(function(path:FsPath):Promise {}) + static function statfs(path:FsPath, options:{?bigint:Bool}):Promise; + + /** + Creates a symbolic link. + **/ + @:overload(function(target:FsPath, path:FsPath):Promise {}) + static function symlink(target:FsPath, path:FsPath, type:SymlinkType):Promise; + + /** + Truncates (shortens or extends the length) of the content at `path` to be `len` bytes long. + **/ + @:overload(function(path:FsPath):Promise {}) + static function truncate(path:FsPath, len:Int):Promise; + + /** + Removes the link and deletes the file named by `path` if no other references exist. + **/ + static function unlink(path:FsPath):Promise; + + /** + Change the file system timestamps of the object referenced by `path`. + **/ + static function utimes(path:FsPath, atime:EitherType>, + mtime:EitherType>):Promise; + + /** + Returns an async iterator that watches for changes on `filename`. + **/ + @:overload(function(filename:FsPath):FsPromisesAsyncIterator {}) + static function watch(filename:FsPath, options:EitherType):FsPromisesAsyncIterator; + + /** + Asynchronously writes data to a file, replacing the file if it already exists. + **/ + @:overload(function(file:EitherType, data:EitherType):Promise {}) + static function writeFile(file:EitherType, data:EitherType, + options:EitherType):Promise; +} + +/** + Options for `FsPromises.watch`. +**/ +typedef FsPromisesWatchOptions = { + /** + Specifies the character encoding to be used for the filename passed to the listener. + Default: `'utf8'`. + **/ + @:optional var encoding:String; + + /** + Indicates whether the process should continue to run as long as files are being watched. + Default: `true`. + **/ + @:optional var persistent:Bool; + + /** + Indicates whether all files should be watched, or only the current directory. + Default: `false`. + **/ + @:optional var recursive:Bool; + + /** + Allows aborting an in-progress watch with an AbortSignal. + **/ + @:optional var signal:js.node.web.AbortSignal; +} + +/** + Event yielded by `FsPromises.watch`. +**/ +typedef FsPromisesWatchEvent = { + /** + The type of change: `'rename'` or `'change'`. + **/ + var eventType:String; + + /** + The name of the file that changed. + **/ + var filename:Null>; +} diff --git a/src/js/node/fs/FileHandle.hx b/src/js/node/fs/FileHandle.hx new file mode 100644 index 00000000..8bddbfe8 --- /dev/null +++ b/src/js/node/fs/FileHandle.hx @@ -0,0 +1,274 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.fs; + +import haxe.extern.EitherType; +import js.node.Buffer; +import js.node.Fs; +import js.node.events.EventEmitter; +import js.node.events.EventEmitter.Event; +import js.node.readline.Interface; +#if haxe4 +import js.lib.Promise; +#else +import js.Promise; +#end + +/** + A `FileHandle` object is a wrapper for a numeric file descriptor. + + Instances are created by `FsPromises.open()`. + Not a public export of `fs/promises` (`require("fs/promises").FileHandle` is undefined), + so this is a plain extern like `Stats` / `ReadStream`. + + `Symbol.asyncDispose` (await using) is intentionally deferred. + + @see https://nodejs.org/api/fs.html#class-filehandle +**/ +extern class FileHandle extends EventEmitter { + /** + The numeric file descriptor managed by this `FileHandle`. + **/ + var fd(default, null):Int; + + /** + Alias of `writeFile()`. + Asynchronously append data to a file, creating the file if it does not exist. + **/ + @:overload(function(data:EitherType):Promise {}) + function appendFile(data:EitherType, options:EitherType):Promise; + + /** + Modifies the permissions on the file. See chmod(2). + **/ + function chmod(mode:FsMode):Promise; + + /** + Changes the ownership of the file. See chown(2). + **/ + function chown(uid:Int, gid:Int):Promise; + + /** + Closes the file handle after waiting for any pending operation on the handle to complete. + **/ + function close():Promise; + + /** + Unlike `createReadStream` on the `fs` module, this reads sequentially from the current file position. + **/ + function createReadStream(?options:FsCreateReadStreamOptions):ReadStream; + + /** + Creates a write stream associated with this file handle. + **/ + function createWriteStream(?options:FsCreateWriteStreamOptions):WriteStream; + + /** + Forces all currently queued I/O operations associated with the file to the + operating system's synchronized I/O completion state. See fdatasync(2). + **/ + function datasync():Promise; + + /** + Reads data from the file and stores that in the given buffer. + **/ + @:overload(function(?options:FileHandleReadOptions):Promise {}) + @:overload(function(buffer:Buffer, ?options:FileHandleReadOptions):Promise {}) + function read(buffer:Buffer, offset:Int, length:Int, position:Null>):Promise; + + /** + Asynchronously reads the entire contents of a file. + If no encoding is specified, the data is returned as a `Buffer`. + **/ + @:overload(function():Promise {}) + @:overload(function(options:EitherType):Promise {}) + function readFile(?options:{?encoding:String}):Promise>; + + /** + Convenience method to create a `readline` interface and stream over the file. + **/ + function readLines(?options:FsCreateReadStreamOptions):Interface; + + /** + Returns a byte-oriented `ReadableStream` for the file contents. + + Typed as `Dynamic` until web streams externs are added (same pattern as `Blob.stream`). + **/ + function readableWebStream(?options:FileHandleReadableWebStreamOptions):Dynamic; + + /** + Read from a file and write to an array of buffers. + **/ + @:overload(function(buffers:Array):Promise {}) + function readv(buffers:Array, position:Null):Promise; + + /** + Retrieves `fs.Stats` for the file. + **/ + @:overload(function():Promise {}) + function stat(options:{?bigint:Bool}):Promise; + + /** + Request that all data for the open file descriptor is flushed to the storage device. + See fsync(2). + **/ + function sync():Promise; + + /** + Truncates the file. + If `len` is negative then `0` will be used. + **/ + @:overload(function():Promise {}) + function truncate(len:Int):Promise; + + /** + Change the file system timestamps of the object referenced by this `FileHandle`. + **/ + function utimes(atime:EitherType>, + mtime:EitherType>):Promise; + + /** + Write `buffer` / `string` to the file. + **/ + @:overload(function(buffer:Buffer, ?options:FileHandleWriteOptions):Promise {}) + @:overload(function(string:String, ?position:Null, ?encoding:String):Promise {}) + function write(buffer:Buffer, offset:Int, ?length:Int, ?position:Null):Promise; + + /** + Asynchronously writes data to a file, replacing the file if it already exists. + **/ + @:overload(function(data:EitherType):Promise {}) + function writeFile(data:EitherType, options:EitherType):Promise; + + /** + Write an array of buffers to the file. + **/ + @:overload(function(buffers:Array):Promise {}) + function writev(buffers:Array, position:Null):Promise; +} + +/** + Events emitted by `FileHandle`. +**/ +enum abstract FileHandleEvent(Event) to Event { + /** + Emitted when the `FileHandle` has been closed and can no longer be used. + **/ + var Close:FileHandleEventVoid> = "close"; +} + +/** + Options for `FileHandle.readableWebStream`. +**/ +typedef FileHandleReadableWebStreamOptions = { + /** + When `true`, closes the `FileHandle` when the stream is closed. + Default: `false`. + **/ + @:optional var autoClose:Bool; +} + +/** + Options for `FileHandle.read`. +**/ +typedef FileHandleReadOptions = { + /** + A buffer that will be filled with the file data read. + Default: `Buffer.alloc(16384)`. + **/ + @:optional var buffer:Buffer; + + /** + The location in the buffer at which to start filling. + Default: `0`. + **/ + @:optional var offset:Int; + + /** + The number of bytes to read. + Default: `buffer.byteLength - offset`. + **/ + @:optional var length:Int; + + /** + The location where to begin reading data from the file. + If `null` or `-1`, data will be read from the current file position. + Default: `null`. + **/ + @:optional var position:Null>; +} + +/** + Result of `FileHandle.read`. +**/ +typedef FileHandleReadResult = { + var bytesRead:Int; + var buffer:Buffer; +} + +/** + Result of `FileHandle.readv`. +**/ +typedef FileHandleReadvResult = { + var bytesRead:Int; + var buffers:Array; +} + +/** + Options for `FileHandle.write` (buffer form). +**/ +typedef FileHandleWriteOptions = { + /** + The start position from within `buffer` where the data to write begins. + Default: `0`. + **/ + @:optional var offset:Int; + + /** + The number of bytes from `buffer` to write. + Default: `buffer.byteLength - offset`. + **/ + @:optional var length:Int; + + /** + The offset from the beginning of the file where the data should be written. + Default: `null` (write at current position). + **/ + @:optional var position:Null; +} + +/** + Result of `FileHandle.write`. +**/ +typedef FileHandleWriteResult = { + var bytesWritten:Int; + var buffer:EitherType; +} + +/** + Result of `FileHandle.writev`. +**/ +typedef FileHandleWritevResult = { + var bytesWritten:Int; + var buffers:Array; +} From 59b22802d2dd218c9a3bad803b4d8b68d6f8c031 Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Tue, 14 Jul 2026 15:44:02 +1000 Subject: [PATCH 31/48] Add dns/promises externs (#219) * Add dns/promises externs Thin Promise wrappers mirroring the existing Dns callback API. Co-authored-by: Cursor * Remove ADDRCONFIG/V4MAPPED from dns/promises externs. Those flags are undefined on dns/promises; document using Dns.* and note intentional API gaps. Co-authored-by: Cursor * Align Dns and DnsPromises APIs with Node.js 22 LTS. Add missing resolve helpers, result-order APIs, TTL options, Resolver classes, and shared record typedefs so callback and promise surfaces match. Co-authored-by: Cursor * Point dns extern docs at Node.js 24 LTS. Co-authored-by: Cursor --------- Co-authored-by: Cursor --- src/js/node/Dns.hx | 250 +++++++++++++++++++++++++++- src/js/node/DnsPromises.hx | 188 +++++++++++++++++++++ src/js/node/dns/PromisesResolver.hx | 78 +++++++++ src/js/node/dns/Resolver.hx | 78 +++++++++ 4 files changed, 591 insertions(+), 3 deletions(-) create mode 100644 src/js/node/DnsPromises.hx create mode 100644 src/js/node/dns/PromisesResolver.hx create mode 100644 src/js/node/dns/Resolver.hx diff --git a/src/js/node/Dns.hx b/src/js/node/Dns.hx index 8a09041e..2ef67edd 100644 --- a/src/js/node/Dns.hx +++ b/src/js/node/Dns.hx @@ -23,9 +23,12 @@ package js.node; import haxe.extern.EitherType; +import js.node.dns.Resolver as ResolverObject; #if haxe4 +import js.lib.ArrayBuffer; import js.lib.Error; #else +import js.html.ArrayBuffer; import js.Error; #end @@ -37,14 +40,26 @@ enum abstract DnsAddressFamily(Int) from Int to Int { var IPv6 = 6; } +/** + Values for the `order` option of `Dns.lookup` / `DnsPromises.lookup` + and for `Dns.setDefaultResultOrder` / `Dns.getDefaultResultOrder`. +**/ +enum abstract DnsResultOrder(String) from String to String { + var Ipv4First = "ipv4first"; + var Ipv6First = "ipv6first"; + var Verbatim = "verbatim"; +} + /** Type of the `options` argument for `Dns.lookup`. **/ typedef DnsLookupOptions = { /** - The record family. If not provided, both IP v4 and v6 addresses are accepted. + The record family. Must be `4`, `6`, or `0`. + For compatibility, `'IPv4'` and `'IPv6'` are also accepted. + The value `0` indicates that either an IPv4 or IPv6 address is returned. **/ - @:optional var family:DnsAddressFamily; + @:optional var family:EitherType; /** If present, it should be one or more of the supported `getaddrinfo` flags. @@ -58,6 +73,52 @@ typedef DnsLookupOptions = { Defaults to false. **/ @:optional var all:Bool; + + /** + When `verbatim`, the resolved addresses are returned unsorted. + When `ipv4first` / `ipv6first`, addresses are sorted accordingly. + Default is configurable via `Dns.setDefaultResultOrder`. + **/ + @:optional var order:DnsResultOrder; + + /** + When `true`, the callback receives IPv4 and IPv6 addresses in the order the DNS resolver returned them. + When `false`, IPv4 addresses are placed before IPv6 addresses. + + Deprecated in favor of `order`. When both are specified, `order` has higher precedence. + **/ + @:optional var verbatim:Bool; +} + +/** + Options for `Dns.resolve4` / `Dns.resolve6`. +**/ +typedef DnsResolveOptions = { + /** + When `true`, each record includes its TTL (in seconds). + **/ + @:optional var ttl:Bool; +} + +/** + Options for constructing a `dns.Resolver`. +**/ +typedef DnsResolverOptions = { + /** + Query timeout in milliseconds, or `-1` to use the default timeout. + **/ + @:optional var timeout:Int; + + /** + The number of tries the resolver will try contacting each name server before giving up. + Default: `4`. + **/ + @:optional var tries:Int; + + /** + The max retry timeout, in milliseconds. Default: `0` (disabled). + **/ + @:optional var maxTimeout:Int; } /** @@ -74,6 +135,16 @@ enum abstract DnsRrtype(String) from String to String { **/ var AAAA = "AAAA"; + /** + any records + **/ + var ANY = "ANY"; + + /** + CA authorization records + **/ + var CAA = "CAA"; + /** mail exchange records **/ @@ -89,11 +160,21 @@ enum abstract DnsRrtype(String) from String to String { **/ var SRV = "SRV"; + /** + certificate associations + **/ + var TLSA = "TLSA"; + /** used for reverse IP lookups **/ var PTR = "PTR"; + /** + name authority pointer records + **/ + var NAPTR = "NAPTR"; + /** name server records **/ @@ -117,7 +198,116 @@ typedef DnsResolvedAddressMX = {priority:Int, exchange:String}; typedef DnsResolvedAddressSRV = {priority:Int, weight:Int, port:Int, name:String}; typedef DnsResolvedAddressSOA = {nsname:String, hostmaster:String, serial:Int, refresh:Int, retry:Int, expire:Int, minttl:Int}; -typedef DnsResolvedAddress = EitherType>>; + +typedef DnsResolvedAddressCAA = { + var critical:Int; + @:optional var issue:String; + @:optional var issuewild:String; + @:optional var iodef:String; + @:optional var contactemail:String; + @:optional var contactphone:String; +}; + +typedef DnsResolvedAddressNAPTR = { + var flags:String; + var service:String; + var regexp:String; + var replacement:String; + var order:Int; + var preference:Int; +}; + +typedef DnsResolvedAddressTLSA = { + var certUsage:Int; + var selector:Int; + var match:Int; + var data:ArrayBuffer; +}; + +/** + Address record that includes TTL, returned by `resolve4`/`resolve6` when `ttl` is true. +**/ +typedef DnsRecordWithTtl = { + var address:String; + var ttl:Int; +}; + +typedef DnsAnyARecord = { + > DnsRecordWithTtl, + var type:String; +}; + +typedef DnsAnyAaaaRecord = { + > DnsRecordWithTtl, + var type:String; +}; + +typedef DnsAnyCaaRecord = { + > DnsResolvedAddressCAA, + var type:String; +}; + +typedef DnsAnyMxRecord = { + > DnsResolvedAddressMX, + var type:String; +}; + +typedef DnsAnyNaptrRecord = { + > DnsResolvedAddressNAPTR, + var type:String; +}; + +typedef DnsAnySoaRecord = { + > DnsResolvedAddressSOA, + var type:String; +}; + +typedef DnsAnySrvRecord = { + > DnsResolvedAddressSRV, + var type:String; +}; + +typedef DnsAnyTlsaRecord = { + > DnsResolvedAddressTLSA, + var type:String; +}; + +typedef DnsAnyTxtRecord = { + var type:String; + var entries:Array; +}; + +typedef DnsAnyNsRecord = { + var type:String; + var value:String; +}; + +typedef DnsAnyPtrRecord = { + var type:String; + var value:String; +}; + +typedef DnsAnyCnameRecord = { + var type:String; + var value:String; +}; + +typedef DnsAnyRecord = EitherType>>>>>>>>>>; + +typedef DnsResolvedAddress = EitherType>>>>>; /** Error objects returned by dns lookups are of this type @@ -276,6 +466,8 @@ typedef DnsLookupCallbackAllEntry = {address:String, family:DnsAddressFamily}; These functions do not use the same set of configuration files than what `lookup` uses. For instance, they do not use the configuration from /etc/hosts. These functions should be used by developers who do not want to use the underlying operating system's facilities for name resolution, and instead want to always perform DNS queries. + + @see https://nodejs.org/docs/latest-v24.x/api/dns.html **/ @:jsRequire("dns") extern class Dns { @@ -321,6 +513,13 @@ extern class Dns { **/ static var V4MAPPED(default, null):Int; + /** + A flag passed in the `hints` argument of `lookup` method. + + If `V4MAPPED` is specified, return resolved IPv6 addresses as well as IPv4 mapped IPv6 addresses. + **/ + static var ALL(default, null):Int; + /** Resolves the given `address` and `port` into a hostname and service using `getnameinfo`. @@ -346,14 +545,32 @@ extern class Dns { /** The same as `resolve`, but only for IPv4 queries (A records). `addresses` is an array of IPv4 addresses (e.g. ['74.125.79.104', '74.125.79.105', '74.125.79.106']). + + When `options.ttl` is `true`, each entry is `{ address, ttl }` instead of a string. **/ + @:overload(function(hostname:String, options:DnsResolveOptions, + callback:DnsError->EitherType, Array>->Void):Void {}) static function resolve4(hostname:String, callback:DnsError->Array->Void):Void; /** The same as `resolve4` except for IPv6 queries (an AAAA query). + + When `options.ttl` is `true`, each entry is `{ address, ttl }` instead of a string. **/ + @:overload(function(hostname:String, options:DnsResolveOptions, + callback:DnsError->EitherType, Array>->Void):Void {}) static function resolve6(hostname:String, callback:DnsError->Array->Void):Void; + /** + Uses the DNS protocol to resolve all records (also known as `ANY` or `*` query). + **/ + static function resolveAny(hostname:String, callback:DnsError->Array->Void):Void; + + /** + Uses the DNS protocol to resolve `CAA` records for the `hostname`. + **/ + static function resolveCaa(hostname:String, callback:DnsError->Array->Void):Void; + /** The same as `resolve`, but only for mail exchange queries (MX records). `addresses` is an array of MX records, each with a priority @@ -361,6 +578,11 @@ extern class Dns { **/ static function resolveMx(hostname:String, callback:DnsError->Array->Void):Void; + /** + Uses the DNS protocol to resolve regular expression-based records (`NAPTR` records) for the `hostname`. + **/ + static function resolveNaptr(hostname:String, callback:DnsError->Array->Void):Void; + /** The same as `resolve`, but only for text queries (TXT records). `addresses` is a 2-d array of the text records available for hostname (e.g., [ ['v=spf1 ip4:0.0.0.0 ', '~all' ] ]). @@ -377,6 +599,11 @@ extern class Dns { **/ static function resolveSrv(hostname:String, callback:DnsError->Array->Void):Void; + /** + Uses the DNS protocol to resolve certificate associations (`TLSA` records) for the `hostname`. + **/ + static function resolveTlsa(hostname:String, callback:DnsError->Array->Void):Void; + /** Uses the DNS protocol to resolve pointer records (PTR records) for the `hostname`. The addresses argument passed to the callback function will be an array of strings containing the reply records. @@ -430,4 +657,21 @@ extern class Dns { This will throw if you pass invalid input. **/ static function setServers(servers:Array):Void; + + /** + Get the default value for `order` in `Dns.lookup` and `DnsPromises.lookup`. + **/ + static function getDefaultResultOrder():DnsResultOrder; + + /** + Set the default value of `order` in `Dns.lookup` and `DnsPromises.lookup`. + **/ + static function setDefaultResultOrder(order:DnsResultOrder):Void; + + /** + `Resolver` class constructor for an independent DNS resolver. + + @see https://nodejs.org/docs/latest-v24.x/api/dns.html#class-dnsresolver + **/ + static var Resolver:Class; } diff --git a/src/js/node/DnsPromises.hx b/src/js/node/DnsPromises.hx new file mode 100644 index 00000000..6a3de20b --- /dev/null +++ b/src/js/node/DnsPromises.hx @@ -0,0 +1,188 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node; + +import haxe.extern.EitherType; +import js.node.Dns; +import js.node.dns.PromisesResolver as PromisesResolverObject; +#if haxe4 +import js.lib.Promise; +#else +import js.Promise; +#end + +/** + The `dns/promises` API provides an alternative set of asynchronous DNS methods + that return `Promise` objects rather than using callbacks. + + The API is accessible via `require('dns/promises')` or `require('dns').promises`. + + `ADDRCONFIG`, `V4MAPPED`, and `ALL` are not exported by `dns/promises` (they are `undefined` + there). For lookup `hints`, use `Dns.ADDRCONFIG`, `Dns.V4MAPPED`, and `Dns.ALL`. + + @see https://nodejs.org/docs/latest-v24.x/api/dns.html#dns-promises-api +**/ +@:jsRequire("dns/promises") +extern class DnsPromises { + /** + Returns an array of IP address strings, formatted according to + [RFC 5952](https://tools.ietf.org/html/rfc5952), that are currently configured for DNS resolution. + **/ + static function getServers():Array; + + /** + Resolves a `hostname` into the first found A (IPv4) or AAAA (IPv6) record. + + With `all: false` (default), fulfills with `{ address, family }`. + With `all: true`, fulfills with an array of such objects. + **/ + @:overload(function(hostname:String):Promise {}) + @:overload(function(hostname:String, options:DnsAddressFamily):Promise {}) + @:overload(function(hostname:String, options:{family:EitherType, ?hints:Int, all:Bool, ?order:DnsResultOrder, ?verbatim:Bool}):Promise> {}) + static function lookup(hostname:String, options:EitherType):Promise>>; + + /** + Resolves the given `address` and `port` into a hostname and service using `getnameinfo`. + **/ + static function lookupService(address:String, port:Int):Promise; + + /** + Uses the DNS protocol to resolve a hostname into an array of the record types specified by `rrtype`. + **/ + @:overload(function(hostname:String):Promise> {}) + static function resolve(hostname:String, rrtype:DnsRrtype):Promise>; + + /** + Uses the DNS protocol to resolve IPv4 addresses (`A` records) for the `hostname`. + + When `options.ttl` is `true`, each entry is `{ address, ttl }` instead of a string. + **/ + @:overload(function(hostname:String, options:DnsResolveOptions):Promise, Array>> {}) + static function resolve4(hostname:String):Promise>; + + /** + Uses the DNS protocol to resolve IPv6 addresses (`AAAA` records) for the `hostname`. + + When `options.ttl` is `true`, each entry is `{ address, ttl }` instead of a string. + **/ + @:overload(function(hostname:String, options:DnsResolveOptions):Promise, Array>> {}) + static function resolve6(hostname:String):Promise>; + + /** + Uses the DNS protocol to resolve all records (also known as `ANY` or `*` query). + **/ + static function resolveAny(hostname:String):Promise>; + + /** + Uses the DNS protocol to resolve `CAA` records for the `hostname`. + **/ + static function resolveCaa(hostname:String):Promise>; + + /** + Uses the DNS protocol to resolve `CNAME` records for the `hostname`. + **/ + static function resolveCname(hostname:String):Promise>; + + /** + Uses the DNS protocol to resolve mail exchange records (`MX` records) for the `hostname`. + **/ + static function resolveMx(hostname:String):Promise>; + + /** + Uses the DNS protocol to resolve regular expression-based records (`NAPTR` records) for the `hostname`. + **/ + static function resolveNaptr(hostname:String):Promise>; + + /** + Uses the DNS protocol to resolve name server records (`NS` records) for the `hostname`. + **/ + static function resolveNs(hostname:String):Promise>; + + /** + Uses the DNS protocol to resolve pointer records (`PTR` records) for the `hostname`. + **/ + static function resolvePtr(hostname:String):Promise>; + + /** + Uses the DNS protocol to resolve a start of authority record (`SOA` record) for the `hostname`. + **/ + static function resolveSoa(hostname:String):Promise; + + /** + Uses the DNS protocol to resolve service records (`SRV` records) for the `hostname`. + **/ + static function resolveSrv(hostname:String):Promise>; + + /** + Uses the DNS protocol to resolve certificate associations (`TLSA` records) for the `hostname`. + **/ + static function resolveTlsa(hostname:String):Promise>; + + /** + Uses the DNS protocol to resolve text queries (`TXT` records) for the `hostname`. + **/ + static function resolveTxt(hostname:String):Promise>>; + + /** + Performs a reverse DNS query that resolves an IPv4 or IPv6 address to an array of hostnames. + **/ + static function reverse(ip:String):Promise>; + + /** + Sets the IP address and port of servers to be used when performing DNS resolution. + **/ + static function setServers(servers:Array):Void; + + /** + Get the default value for `order` in `Dns.lookup` and `DnsPromises.lookup`. + **/ + static function getDefaultResultOrder():DnsResultOrder; + + /** + Set the default value of `order` in `Dns.lookup` and `DnsPromises.lookup`. + **/ + static function setDefaultResultOrder(order:DnsResultOrder):Void; + + /** + `Resolver` class constructor for an independent promise-based DNS resolver. + + @see https://nodejs.org/docs/latest-v24.x/api/dns.html#class-dnspromisesresolver + **/ + static var Resolver:Class; +} + +/** + Result of `DnsPromises.lookup` when `all` is not `true`. +**/ +typedef DnsPromisesLookupResult = { + var address:String; + var family:DnsAddressFamily; +} + +/** + Result of `DnsPromises.lookupService`. +**/ +typedef DnsPromisesLookupServiceResult = { + var hostname:String; + var service:String; +} diff --git a/src/js/node/dns/PromisesResolver.hx b/src/js/node/dns/PromisesResolver.hx new file mode 100644 index 00000000..91f4f404 --- /dev/null +++ b/src/js/node/dns/PromisesResolver.hx @@ -0,0 +1,78 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.dns; + +import haxe.extern.EitherType; +import js.node.Dns; +#if haxe4 +import js.lib.Promise; +#else +import js.Promise; +#end + +/** + Promise-based independent resolver for DNS requests (`dns/promises.Resolver`). + + @see https://nodejs.org/docs/latest-v24.x/api/dns.html#class-dnspromisesresolver +**/ +@:jsRequire("dns/promises", "Resolver") +extern class PromisesResolver { + function new(?options:DnsResolverOptions); + + /** + Cancel all outstanding DNS queries made by this resolver. + The corresponding promises will be rejected with an error with the code `ECANCELLED`. + **/ + function cancel():Void; + + /** + The resolver instance will send its requests from the specified IP address. + This allows programs to specify outbound interfaces when used on multi-homed systems. + **/ + function setLocalAddress(?ipv4:String, ?ipv6:String):Void; + + function getServers():Array; + function setServers(servers:Array):Void; + + @:overload(function(hostname:String):Promise> {}) + function resolve(hostname:String, rrtype:DnsRrtype):Promise>; + + @:overload(function(hostname:String, options:DnsResolveOptions):Promise, Array>> {}) + function resolve4(hostname:String):Promise>; + + @:overload(function(hostname:String, options:DnsResolveOptions):Promise, Array>> {}) + function resolve6(hostname:String):Promise>; + + function resolveAny(hostname:String):Promise>; + function resolveCaa(hostname:String):Promise>; + function resolveCname(hostname:String):Promise>; + function resolveMx(hostname:String):Promise>; + function resolveNaptr(hostname:String):Promise>; + function resolveNs(hostname:String):Promise>; + function resolvePtr(hostname:String):Promise>; + function resolveSoa(hostname:String):Promise; + function resolveSrv(hostname:String):Promise>; + function resolveTlsa(hostname:String):Promise>; + function resolveTxt(hostname:String):Promise>>; + function reverse(ip:String):Promise>; +} diff --git a/src/js/node/dns/Resolver.hx b/src/js/node/dns/Resolver.hx new file mode 100644 index 00000000..15f9d325 --- /dev/null +++ b/src/js/node/dns/Resolver.hx @@ -0,0 +1,78 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.dns; + +import haxe.extern.EitherType; +import js.node.Dns; + +/** + An independent resolver for DNS requests. + + Creating a new resolver uses the default server settings. + Setting the servers used for a resolver using `setServers()` does not affect other resolvers. + + @see https://nodejs.org/docs/latest-v24.x/api/dns.html#class-dnsresolver +**/ +@:jsRequire("dns", "Resolver") +extern class Resolver { + function new(?options:DnsResolverOptions); + + /** + Cancel all outstanding DNS queries made by this resolver. + The corresponding callbacks will be called with an error with code `ECANCELLED`. + **/ + function cancel():Void; + + /** + The resolver instance will send its requests from the specified IP address. + This allows programs to specify outbound interfaces when used on multi-homed systems. + **/ + function setLocalAddress(?ipv4:String, ?ipv6:String):Void; + + function getServers():Array; + function setServers(servers:Array):Void; + + @:overload(function(hostname:String, callback:DnsError->Array->Void):Void {}) + function resolve(hostname:String, rrtype:DnsRrtype, callback:DnsError->Array->Void):Void; + + @:overload(function(hostname:String, options:DnsResolveOptions, + callback:DnsError->EitherType, Array>->Void):Void {}) + function resolve4(hostname:String, callback:DnsError->Array->Void):Void; + + @:overload(function(hostname:String, options:DnsResolveOptions, + callback:DnsError->EitherType, Array>->Void):Void {}) + function resolve6(hostname:String, callback:DnsError->Array->Void):Void; + + function resolveAny(hostname:String, callback:DnsError->Array->Void):Void; + function resolveCaa(hostname:String, callback:DnsError->Array->Void):Void; + function resolveCname(hostname:String, callback:DnsError->Array->Void):Void; + function resolveMx(hostname:String, callback:DnsError->Array->Void):Void; + function resolveNaptr(hostname:String, callback:DnsError->Array->Void):Void; + function resolveNs(hostname:String, callback:DnsError->Array->Void):Void; + function resolvePtr(hostname:String, callback:DnsError->Array->Void):Void; + function resolveSoa(hostname:String, callback:DnsError->DnsResolvedAddressSOA->Void):Void; + function resolveSrv(hostname:String, callback:DnsError->Array->Void):Void; + function resolveTlsa(hostname:String, callback:DnsError->Array->Void):Void; + function resolveTxt(hostname:String, callback:DnsError->Array>->Void):Void; + function reverse(ip:String, callback:DnsError->Array->Void):Void; +} From 79ea6825c77bd9f87e32ccb7f44fb4f694331eec Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Tue, 14 Jul 2026 15:45:33 +1000 Subject: [PATCH 32/48] Add node:test runner externs (#214) * Add node:test runner externs for Node.js 24 Expose js.node.Test with suite/test/hooks/mock/snapshot/run APIs and supporting types under js.node.test for contexts, mocks, and TestsStream. Co-authored-by: Cursor * Refine node:test externs: split suite/it helpers and mock APIs. Separate SuiteFunction/ItFunction modules, keep MockTimers in its own file, and tighten TestsStream/context typing after a full ImportAll build. Co-authored-by: Cursor * Fix node:test LTS review gaps for expectFailure and waitFor. Add Test.expectFailure and suite.expectFailure shorthands, allow async waitFor conditions, and document the Node 24+ Active LTS baseline. Co-authored-by: Cursor * Type MockTimers.enable now as number | Date. Restore JsDate (js.lib.Date) for MockTimersEnableOptions.now to match Node's API instead of Dynamic. Co-authored-by: Cursor --------- Co-authored-by: Cursor --- src/js/node/Test.hx | 547 ++++++++++++++++++++++++++++++ src/js/node/test/ItFunction.hx | 83 +++++ src/js/node/test/MockTimers.hx | 100 ++++++ src/js/node/test/MockTracker.hx | 359 ++++++++++++++++++++ src/js/node/test/SuiteContext.hx | 83 +++++ src/js/node/test/SuiteFunction.hx | 84 +++++ src/js/node/test/TestContext.hx | 239 +++++++++++++ src/js/node/test/TestsStream.hx | 233 +++++++++++++ 8 files changed, 1728 insertions(+) create mode 100644 src/js/node/Test.hx create mode 100644 src/js/node/test/ItFunction.hx create mode 100644 src/js/node/test/MockTimers.hx create mode 100644 src/js/node/test/MockTracker.hx create mode 100644 src/js/node/test/SuiteContext.hx create mode 100644 src/js/node/test/SuiteFunction.hx create mode 100644 src/js/node/test/TestContext.hx create mode 100644 src/js/node/test/TestsStream.hx diff --git a/src/js/node/Test.hx b/src/js/node/Test.hx new file mode 100644 index 00000000..0d410f4d --- /dev/null +++ b/src/js/node/Test.hx @@ -0,0 +1,547 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node; + +import haxe.Constraints.Function; +import haxe.DynamicAccess; +import haxe.extern.EitherType; +import js.html.AbortSignal; +import js.node.test.ItFunction; +import js.node.test.MockTracker; +import js.node.test.SuiteContext; +import js.node.test.SuiteFunction; +import js.node.test.TestContext; +import js.node.test.TestsStream; +#if haxe4 +import js.lib.Error; +import js.lib.Promise; +import js.lib.RegExp; +#else +import js.Error; +import js.Promise; +import js.RegExp; +#end + +/** + The `node:test` module facilitates the creation of JavaScript tests. + + This module is only available under the `node:` scheme. + + These externs target **Node.js 24+ Active LTS**. Dual-LTS APIs such as + `expectFailure`, `run({ randomize })`, and `TestContext.workerId` are typed + without version gates; older LTS releases may not provide them at runtime. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html +**/ +@:jsRequire("node:test") +extern class Test { + /** + The `test()` function creates a test whose lifecycle is managed by the test runner. + + Each invocation results in reporting the test to the test runner. + Returns a `Promise` that fulfills once the test completes (or immediately if + called within a suite). + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#testname-options-fn + **/ + @:selfCall + @:overload(function(fn:TestCallback):Promise {}) + @:overload(function(name:String, fn:TestCallback):Promise {}) + @:overload(function(options:TestOptions, fn:TestCallback):Promise {}) + static function test(?name:String, ?options:TestOptions, ?fn:TestCallback):Promise; + + /** + Shorthand for skipping a test: `test(name, { skip: true }[, fn])`. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#testskipname-options-fn + **/ + @:overload(function(fn:TestCallback):Promise {}) + @:overload(function(name:String, fn:TestCallback):Promise {}) + @:overload(function(options:TestOptions, fn:TestCallback):Promise {}) + static function skip(?name:String, ?options:TestOptions, ?fn:TestCallback):Promise; + + /** + Shorthand for marking a test as `TODO`: `test(name, { todo: true }[, fn])`. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#testtodoname-options-fn + **/ + @:overload(function(fn:TestCallback):Promise {}) + @:overload(function(name:String, fn:TestCallback):Promise {}) + @:overload(function(options:TestOptions, fn:TestCallback):Promise {}) + static function todo(?name:String, ?options:TestOptions, ?fn:TestCallback):Promise; + + /** + Shorthand for marking a test as `only`: `test(name, { only: true }[, fn])`. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#testonlyname-options-fn + **/ + @:overload(function(fn:TestCallback):Promise {}) + @:overload(function(name:String, fn:TestCallback):Promise {}) + @:overload(function(options:TestOptions, fn:TestCallback):Promise {}) + static function only(?name:String, ?options:TestOptions, ?fn:TestCallback):Promise; + + /** + Shorthand for expecting a test to fail: `test(name, { expectFailure: true }[, fn])`. + + Added in: v24.14.0 + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#expecting-tests-to-fail + **/ + @:overload(function(fn:TestCallback):Promise {}) + @:overload(function(name:String, fn:TestCallback):Promise {}) + @:overload(function(options:TestOptions, fn:TestCallback):Promise {}) + static function expectFailure(?name:String, ?options:TestOptions, ?fn:TestCallback):Promise; + + /** + Alias for `suite()`. Suites group related tests. + + Supports `.skip`, `.todo`, `.only`, and `.expectFailure` shorthands. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#describename-options-fn + **/ + static var describe(default, never):SuiteFunction; + + /** + Creates a suite whose lifecycle is managed by the test runner. + + Supports `.skip`, `.todo`, `.only`, and `.expectFailure` shorthands. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#suitename-options-fn + **/ + static var suite(default, never):SuiteFunction; + + /** + Alias for `test()`. + + Supports `.skip`, `.todo`, `.only`, and `.expectFailure` shorthands. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#itname-options-fn + **/ + static var it(default, never):ItFunction; + + /** + Creates a hook that runs before executing a suite. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#beforefn-options + **/ + @:overload(function(fn:HookCallback):Void {}) + static function before(?fn:HookCallback, ?options:HookOptions):Void; + + /** + Creates a hook that runs after executing a suite. + Guaranteed to run even if tests within the suite fail. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#afterfn-options + **/ + @:overload(function(fn:HookCallback):Void {}) + static function after(?fn:HookCallback, ?options:HookOptions):Void; + + /** + Creates a hook that runs before each test in the current suite. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#beforeeachfn-options + **/ + @:overload(function(fn:HookCallback):Void {}) + static function beforeEach(?fn:HookCallback, ?options:HookOptions):Void; + + /** + Creates a hook that runs after each test in the current suite. + Runs even if the test fails. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#aftereachfn-options + **/ + @:overload(function(fn:HookCallback):Void {}) + static function afterEach(?fn:HookCallback, ?options:HookOptions):Void; + + /** + Top-level `MockTracker` instance for the process. + + Each test also provides its own tracker via `TestContext.mock`. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#class-mocktracker + **/ + static var mock(default, never):MockTracker; + + /** + Object whose methods configure default snapshot settings in the current process. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#snapshot + **/ + static var snapshot(default, never):Snapshot; + + /** + Object whose methods configure available assertions on `TestContext` objects + in the current process. + + Methods from `node:assert` and snapshot testing functions are available on + `TestContext.assert` by default; see `js.node.Assert` for the assert API. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#assert + **/ + static var assert(default, never):TestAssert; + + /** + Programmatically run tests and return a stream of test reporter events. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#runoptions + **/ + static function run(?options:RunOptions):TestsStream; +} + +/** + Callback for a test created with `test()` / `it()` / `TestContext.test()`. + + May be synchronous, return a `Promise`, or accept a Node-style `done` callback + as the second argument. +**/ +typedef TestCallback = EitherTypeDynamic, TestContext->(Null->Void)->Dynamic>; + +/** + Callback for a suite created with `suite()` / `describe()`. +**/ +typedef SuiteCallback = EitherTypeDynamic, SuiteContext->(Null->Void)->Dynamic>; + +/** + Callback for `before` / `after` / `beforeEach` / `afterEach` hooks. +**/ +typedef HookCallback = Function; + +/** + Configuration options for a test or suite. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#testname-options-fn +**/ +typedef TestOptions = { + /** + If a number is provided, that many tests run asynchronously. + If `true`, all scheduled asynchronous tests run concurrently. + If `false`, only one test runs at a time. + Default: `false` (subtests inherit from parent when unspecified). + **/ + @:optional var concurrency:EitherType; + + /** + If truthy, the test is expected to fail. + A string is shown as the reason; a `RegExp` / function / object / `Error` + is matched like `Assert.throws`. Use `{ label, match }` for both. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#expecting-tests-to-fail + **/ + @:optional var expectFailure:EitherType>>>>; + + /** + If truthy, and the test context is configured to run `only` tests, this test runs. + Otherwise it is skipped. Default: `false`. + **/ + @:optional var only:Bool; + + /** + Allows aborting an in-progress test. + **/ + @:optional var signal:AbortSignal; + + /** + If truthy, the test is skipped. A string is shown as the skip reason. Default: `false`. + **/ + @:optional var skip:EitherType; + + /** + If truthy, the test is marked `TODO`. A string is shown as the reason. Default: `false`. + **/ + @:optional var todo:EitherType; + + /** + Milliseconds after which the test fails. Default: `Infinity`. + **/ + @:optional var timeout:Float; + + /** + Number of assertions and subtests expected to run. Default: `undefined`. + **/ + @:optional var plan:Int; +} + +/** + Object form of `expectFailure` providing both a label and a matcher. +**/ +typedef ExpectFailureOptions = { + /** + Reason displayed in the test results. + **/ + @:optional var label:String; + + /** + Matcher for the thrown value (same forms as `Assert.throws`). + **/ + @:optional var match:EitherType>>; +} + +/** + Configuration options for hooks. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#beforefn-options +**/ +typedef HookOptions = { + /** + Allows aborting an in-progress hook. + **/ + @:optional var signal:AbortSignal; + + /** + Milliseconds after which the hook fails. Default: `Infinity`. + **/ + @:optional var timeout:Float; +} + +/** + Options for `TestContext.plan()`. +**/ +typedef PlanOptions = { + /** + If `true`, wait indefinitely for planned assertions/subtests. + If `false`, check immediately when the test function completes. + If a number, maximum wait time in milliseconds. Default: `false`. + **/ + @:optional var wait:EitherType; +} + +/** + Options for `TestContext.waitFor()`. +**/ +typedef WaitForOptions = { + /** + Milliseconds to wait after an unsuccessful `condition` before retrying. Default: `50`. + **/ + @:optional var interval:Float; + + /** + Poll timeout in milliseconds. Default: `1000`. + **/ + @:optional var timeout:Float; +} + +/** + Options for snapshot assertions on `TestContext.assert`. +**/ +typedef SnapshotAssertionOptions = { + /** + Serializers applied in order; final result is coerced to a string. + **/ + @:optional var serializers:ArrayDynamic>; +} + +/** + Configuration options for `Test.run()`. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#runoptions +**/ +typedef RunOptions = { + /** + Parallelism for test files. Number of processes, or `true`/`false`. Default: `false`. + **/ + @:optional var concurrency:EitherType; + + /** + Working directory used as the base path for resolving files. Default: `process.cwd()`. + **/ + @:optional var cwd:String; + + /** + List of files to run. Default: same as CLI. + **/ + @:optional var files:Array; + + /** + Exit the process once all known tests finished even if the event loop remains active. + Default: `false`. + **/ + @:optional var forceExit:Bool; + + /** + Glob patterns to match test files. Cannot be used with `files`. + **/ + @:optional var globPatterns:Array; + + /** + Inspector port for test child processes, or a function returning a port. + **/ + @:optional var inspectPort:EitherTypeInt>; + + /** + Test isolation: `'process'` (default) or `'none'`. + **/ + @:optional var isolation:String; + + /** + If truthy, only run tests with the `only` option set. + **/ + @:optional var only:Bool; + + /** + Setup listeners on the returned `TestsStream` before tests run. + **/ + @:optional var setup:TestsStream->Any; + + /** + CLI flags passed to the `node` executable when spawning subprocesses. + **/ + @:optional var execArgv:Array; + + /** + CLI flags passed to each test file when spawning subprocesses. + **/ + @:optional var argv:Array; + + /** + Allows aborting an in-progress test execution. + **/ + @:optional var signal:AbortSignal; + + /** + Only run tests whose names match these pattern(s). + **/ + @:optional var testNamePatterns:EitherType>>>; + + /** + Skip tests whose names match these pattern(s). + **/ + @:optional var testSkipPatterns:EitherType>>>; + + /** + Fail the run after this many milliseconds. Default: `Infinity`. + **/ + @:optional var timeout:Float; + + /** + Whether to run in watch mode. Default: `false`. + **/ + @:optional var watch:Bool; + + /** + Shard configuration for horizontal parallelization. + **/ + @:optional var shard:RunShardOptions; + + /** + Randomize execution order for test files and queued tests. Default: `false`. + **/ + @:optional var randomize:Bool; + + /** + Seed for deterministic randomization (`0`..`4294967295`). Enables randomization. + **/ + @:optional var randomSeed:Int; + + /** + File path where the runner stores state for `--test-rerun-failures`. + **/ + @:optional var rerunFailuresFilePath:String; + + /** + Enable code coverage collection. Default: `false`. + **/ + @:optional var coverage:Bool; + + /** + Exclude files from coverage via glob(s). + **/ + @:optional var coverageExcludeGlobs:EitherType>; + + /** + Include files in coverage via glob(s). + **/ + @:optional var coverageIncludeGlobs:EitherType>; + + /** + Require a minimum percent of covered lines. Default: `0`. + **/ + @:optional var lineCoverage:Float; + + /** + Require a minimum percent of covered branches. Default: `0`. + **/ + @:optional var branchCoverage:Float; + + /** + Require a minimum percent of covered functions. Default: `0`. + **/ + @:optional var functionCoverage:Float; + + /** + Environment variables for test processes (not merged with `process.env`). + Incompatible with `isolation: 'none'`. + **/ + @:optional var env:DynamicAccess; +} + +/** + Shard selection for `RunOptions.shard`. +**/ +typedef RunShardOptions = { + /** + Shard index between `1` and `total` (required). + **/ + var index:Int; + + /** + Total number of shards (required). + **/ + var total:Int; +} + +/** + Module-level snapshot configuration (`Test.snapshot`). +**/ +typedef Snapshot = { + /** + Customize the default serializers used by snapshot tests. + Default serialization is `JSON.stringify(value, null, 2)`. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#snapshotsetdefaultsnapshotserializersserializers + **/ + function setDefaultSnapshotSerializers(serializers:ArrayDynamic>):Void; + + /** + Customize where snapshot files are written. + `fn` receives the test file path (or `undefined` in the REPL) and must return a path string. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#snapshotsetresolvesnapshotpathfn + **/ + function setResolveSnapshotPath(fn:Null->String):Void; +} + +/** + Module-level assert configuration (`Test.assert`). + + Does not re-export `node:assert`; see `js.node.Assert` for assertions. + `TestContext.assert` exposes assert methods bound to the test context for planning. +**/ +typedef TestAssert = { + /** + Defines a new assertion function available on `TestContext.assert`. + Overwrites an existing assertion with the same name. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#assertregistername-fn + **/ + function register(name:String, fn:Function):Void; +} diff --git a/src/js/node/test/ItFunction.hx b/src/js/node/test/ItFunction.hx new file mode 100644 index 00000000..ba3fb647 --- /dev/null +++ b/src/js/node/test/ItFunction.hx @@ -0,0 +1,83 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.test; + +import js.node.Test.TestCallback; +import js.node.Test.TestOptions; +#if haxe4 +import js.lib.Promise; +#else +import js.Promise; +#end + +/** + Callable test entry used by `it`, with suite-style shorthands plus `expectFailure`. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#itname-options-fn +**/ +extern class ItFunction { + /** + Create a test (alias of `test()`). + **/ + @:selfCall + @:overload(function(fn:TestCallback):Promise {}) + @:overload(function(name:String, fn:TestCallback):Promise {}) + @:overload(function(options:TestOptions, fn:TestCallback):Promise {}) + function call(?name:String, ?options:TestOptions, ?fn:TestCallback):Promise; + + /** + Shorthand for skipping a test. + **/ + @:overload(function(fn:TestCallback):Promise {}) + @:overload(function(name:String, fn:TestCallback):Promise {}) + @:overload(function(options:TestOptions, fn:TestCallback):Promise {}) + function skip(?name:String, ?options:TestOptions, ?fn:TestCallback):Promise; + + /** + Shorthand for marking a test as `TODO`. + **/ + @:overload(function(fn:TestCallback):Promise {}) + @:overload(function(name:String, fn:TestCallback):Promise {}) + @:overload(function(options:TestOptions, fn:TestCallback):Promise {}) + function todo(?name:String, ?options:TestOptions, ?fn:TestCallback):Promise; + + /** + Shorthand for marking a test as `only`. + **/ + @:overload(function(fn:TestCallback):Promise {}) + @:overload(function(name:String, fn:TestCallback):Promise {}) + @:overload(function(options:TestOptions, fn:TestCallback):Promise {}) + function only(?name:String, ?options:TestOptions, ?fn:TestCallback):Promise; + + /** + Shorthand for expecting a test to fail (`expectFailure: true`). + + Added in: v24.14.0 + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#expecting-tests-to-fail + **/ + @:overload(function(fn:TestCallback):Promise {}) + @:overload(function(name:String, fn:TestCallback):Promise {}) + @:overload(function(options:TestOptions, fn:TestCallback):Promise {}) + function expectFailure(?name:String, ?options:TestOptions, ?fn:TestCallback):Promise; +} diff --git a/src/js/node/test/MockTimers.hx b/src/js/node/test/MockTimers.hx new file mode 100644 index 00000000..98909758 --- /dev/null +++ b/src/js/node/test/MockTimers.hx @@ -0,0 +1,100 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.test; + +import haxe.extern.EitherType; +#if haxe4 +import js.lib.Date as JsDate; +#else +import js.Date as JsDate; +#end + +/** + Timer / Date APIs that can be mocked via `MockTimers.enable()`. +**/ +enum abstract MockTimerApi(String) from String to String { + var SetInterval = "setInterval"; + var SetTimeout = "setTimeout"; + var SetImmediate = "setImmediate"; + var Date = "Date"; +} + +/** + Options for `MockTimers.enable()`. +**/ +typedef MockTimersEnableOptions = { + /** + Timers to mock. Default mocks all time-related APIs including `Date`. + **/ + @:optional var apis:Array; + + /** + Initial epoch as a Unix timestamp (ms) or a JS `Date`. Default: `0`. + + Matches Node's `number | Date`. + **/ + @:optional var now:EitherType; +} + +/** + Mocking timers simulates and controls `setInterval` / `setTimeout` / `setImmediate` + and optionally the `Date` object without waiting for real time. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#class-mocktimers +**/ +extern class MockTimers { + /** + Enable timer mocking for the specified timers. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#timersenableenableoptions + **/ + function enable(?enableOptions:MockTimersEnableOptions):Void; + + /** + Restore default behavior of all mocks created by this instance. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#timersreset + **/ + function reset():Void; + + /** + Advance time for all mocked timers by `milliseconds` (default: `1`). + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#timerstickmilliseconds + **/ + function tick(?milliseconds:Float):Void; + + /** + Trigger all pending mocked timers immediately. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#timersrunall + **/ + function runAll():Void; + + /** + Set the current Unix timestamp used as reference for mocked `Date` objects. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#timerssettimemilliseconds + **/ + function setTime(milliseconds:Float):Void; +} diff --git a/src/js/node/test/MockTracker.hx b/src/js/node/test/MockTracker.hx new file mode 100644 index 00000000..c61492c2 --- /dev/null +++ b/src/js/node/test/MockTracker.hx @@ -0,0 +1,359 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.test; + +import haxe.Constraints.Function; +import haxe.extern.EitherType; +#if haxe4 +import js.lib.Error; +import js.lib.Symbol; +#else +import js.Error; +#end + +/** + Manages mocking functionality. + + Obtained from `Test.mock` or `TestContext.mock`. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#class-mocktracker +**/ +extern class MockTracker { + /** + Create a mock function. + + The returned function has a `mock` property of type `MockFunctionContext`. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#mockfnoriginal-implementation-options + **/ + @:overload(function(?original:Function, ?options:MockFunctionOptions):MockedFunction {}) + function fn(?original:Function, ?implementation:Function, ?options:MockFunctionOptions):MockedFunction; + + /** + Syntax sugar for `method` with `options.getter` set to `true`. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#mockgetterobject-methodname-implementation-options + **/ + #if haxe4 + @:overload(function(object:{}, methodName:EitherType, ?options:MockMethodOptions):MockedFunction {}) + function getter(object:{}, methodName:EitherType, ?implementation:Function, ?options:MockMethodOptions):MockedFunction; + #else + @:overload(function(object:{}, methodName:String, ?options:MockMethodOptions):MockedFunction {}) + function getter(object:{}, methodName:String, ?implementation:Function, ?options:MockMethodOptions):MockedFunction; + #end + + /** + Create a mock on an existing object method. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#mockmethodobject-methodname-implementation-options + **/ + #if haxe4 + @:overload(function(object:{}, methodName:EitherType, ?options:MockMethodOptions):MockedFunction {}) + function method(object:{}, methodName:EitherType, ?implementation:Function, ?options:MockMethodOptions):MockedFunction; + #else + @:overload(function(object:{}, methodName:String, ?options:MockMethodOptions):MockedFunction {}) + function method(object:{}, methodName:String, ?implementation:Function, ?options:MockMethodOptions):MockedFunction; + #end + + /** + Mock exports of an ESM, CommonJS, JSON, or builtin module. + + Requires `--experimental-test-module-mocks`. Stability: 1.0. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#mockmodulespecifier-options + **/ + function module(specifier:String, ?options:MockModuleOptions):MockModuleContext; + + /** + Mock a property value on an object. + + Added in: v24.3.0 + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#mockpropertyobject-propertyname-value + **/ + #if haxe4 + function property(object:{}, propertyName:EitherType, ?value:Dynamic):MockedProperty; + #else + function property(object:{}, propertyName:String, ?value:Dynamic):MockedProperty; + #end + + /** + Restore default behavior of all mocks created by this tracker and + disassociate them from the tracker. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#mockreset + **/ + function reset():Void; + + /** + Restore default behavior of all mocks without disassociating them. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#mockrestoreall + **/ + function restoreAll():Void; + + /** + Syntax sugar for `method` with `options.setter` set to `true`. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#mocksetterobject-methodname-implementation-options + **/ + #if haxe4 + @:overload(function(object:{}, methodName:EitherType, ?options:MockMethodOptions):MockedFunction {}) + function setter(object:{}, methodName:EitherType, ?implementation:Function, ?options:MockMethodOptions):MockedFunction; + #else + @:overload(function(object:{}, methodName:String, ?options:MockMethodOptions):MockedFunction {}) + function setter(object:{}, methodName:String, ?implementation:Function, ?options:MockMethodOptions):MockedFunction; + #end + + /** + Mock timers APIs (`setTimeout`, `setInterval`, `setImmediate`, `Date`, …). + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#class-mocktimers + **/ + var timers(default, null):MockTimers; +} + +/** + Options for `MockTracker.fn`. +**/ +typedef MockFunctionOptions = { + /** + How many times `implementation` is used before restoring `original`. + Must be an integer greater than zero. Default: `Infinity`. + **/ + @:optional var times:Int; +} + +/** + Options for `MockTracker.method` / `getter` / `setter`. +**/ +typedef MockMethodOptions = { + > MockFunctionOptions, + + /** + Treat the member as a getter. Cannot be combined with `setter`. Default: `false`. + **/ + @:optional var getter:Bool; + + /** + Treat the member as a setter. Cannot be combined with `getter`. Default: `false`. + **/ + @:optional var setter:Bool; +} + +/** + Options for `MockTracker.module`. +**/ +typedef MockModuleOptions = { + /** + If `false`, each import/require gets a new mock. If `true`, the mock is cached. Default: `false`. + **/ + @:optional var cache:Bool; + + /** + Mocked exports object. Prefer this over deprecated `defaultExport` / `namedExports`. + **/ + @:optional var exports:Dynamic; + + /** + Deprecated. Prefer `exports.default`. + **/ + @:deprecated("Prefer options.exports.default") + @:optional var defaultExport:Dynamic; + + /** + Deprecated. Prefer `options.exports`. + **/ + @:deprecated("Prefer options.exports") + @:optional var namedExports:Dynamic; +} + +/** + A mocked function returned by `fn` / `method` / `getter` / `setter`. + Callable; inspect or reconfigure via `mock`. +**/ +@:callable +abstract MockedFunction(Dynamic) from Dynamic to Dynamic { + /** + `MockFunctionContext` for inspecting and changing mock behavior. + **/ + public var mock(get, never):MockFunctionContext; + + inline function get_mock():MockFunctionContext + return this.mock; +} + +/** + A mocked property proxy returned by `MockTracker.property`. +**/ +@:forward +abstract MockedProperty(Dynamic) from Dynamic to Dynamic { + /** + `MockPropertyContext` for inspecting and changing mock behavior. + **/ + public var mock(get, never):MockPropertyContext; + + inline function get_mock():MockPropertyContext + return this.mock; +} + +/** + Inspect or manipulate mocks created via `MockTracker` function APIs. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#class-mockfunctioncontext +**/ +extern class MockFunctionContext { + /** + Copy of the internal call-tracking array. + **/ + var calls(default, null):Array; + + /** + Number of times this mock has been invoked. + **/ + function callCount():Int; + + /** + Change the mock's implementation. + **/ + function mockImplementation(implementation:Function):Void; + + /** + Change the mock's implementation for a single invocation. + **/ + function mockImplementationOnce(implementation:Function, ?onCall:Int):Void; + + /** + Reset the call history of the mock function. + **/ + function resetCalls():Void; + + /** + Reset the mock implementation to its original behavior. + **/ + function restore():Void; +} + +/** + One recorded invocation of a mocked function. +**/ +typedef MockFunctionCall = { + /** + Arguments passed to the mock. + **/ + var arguments:Array; + + /** + Thrown value, if any. + **/ + @:optional var error:Dynamic; + + /** + Return value of the mock. + **/ + var result:Dynamic; + + /** + Error whose stack identifies the callsite. + **/ + var stack:Error; + + /** + Constructed class when the mock was used as a constructor. + **/ + @:optional var target:Function; + + /** + `this` value for the invocation. + **/ + @:native("this") var this_:Dynamic; +} + +/** + Manipulate module mocks created via `MockTracker.module`. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#class-mockmodulecontext +**/ +extern class MockModuleContext { + /** + Reset the mock module implementation. + **/ + function restore():Void; +} + +/** + Inspect or manipulate property mocks created via `MockTracker.property`. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#class-mockpropertycontext +**/ +extern class MockPropertyContext { + /** + Copy of the internal access-tracking array. + **/ + var accesses(default, null):Array; + + /** + Number of times the property was accessed (get or set). + **/ + function accessCount():Int; + + /** + Change the value returned by the mocked property getter. + **/ + function mockImplementation(value:Dynamic):Void; + + /** + Change the mocked value for a single access. + **/ + function mockImplementationOnce(value:Dynamic, ?onAccess:Int):Void; + + /** + Reset the access history of the mocked property. + **/ + function resetAccesses():Void; + + /** + Reset the mock property to its original behavior. + **/ + function restore():Void; +} + +/** + One recorded get/set of a mocked property. +**/ +typedef MockPropertyAccess = { + /** + Either `'get'` or `'set'`. + **/ + var type:String; + + /** + Value that was read or written. + **/ + var value:Dynamic; + + /** + Error whose stack identifies the callsite. + **/ + var stack:Error; +} diff --git a/src/js/node/test/SuiteContext.hx b/src/js/node/test/SuiteContext.hx new file mode 100644 index 00000000..01397549 --- /dev/null +++ b/src/js/node/test/SuiteContext.hx @@ -0,0 +1,83 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.test; + +import js.html.AbortSignal; + +/** + Passed to each suite function to interact with the test runner. + + The `SuiteContext` constructor is not part of the public API. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#class-suitecontext +**/ +extern class SuiteContext { + /** + Absolute path of the test file that created the current suite. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contextfilepath-1 + **/ + var filePath(default, null):Null; + + /** + Name of the suite and each of its ancestors, separated by `>`. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contextfullname-1 + **/ + var fullName(default, null):String; + + /** + Name of the suite. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contextname-1 + **/ + var name(default, null):String; + + /** + Abort signal for cancelling suite subtasks when the suite is aborted. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contextsignal-1 + **/ + var signal(default, null):AbortSignal; + + /** + Whether the suite and all of its subtests have passed. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contextpassed-1 + **/ + var passed(default, null):Bool; + + /** + Zero-based attempt number when using `--test-rerun-failures`. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contextattempt-1 + **/ + var attempt(default, null):Int; + + /** + Output a diagnostic message for the suite. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contextdiagnosticmessage-1 + **/ + function diagnostic(message:String):Void; +} diff --git a/src/js/node/test/SuiteFunction.hx b/src/js/node/test/SuiteFunction.hx new file mode 100644 index 00000000..c0f22097 --- /dev/null +++ b/src/js/node/test/SuiteFunction.hx @@ -0,0 +1,84 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.test; + +import js.node.Test.SuiteCallback; +import js.node.Test.TestOptions; +#if haxe4 +import js.lib.Promise; +#else +import js.Promise; +#end + +/** + Callable suite entry (`describe` / `suite`) with `.skip`, `.todo`, `.only`, + and `.expectFailure` shorthands. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#suitename-options-fn +**/ +extern class SuiteFunction { + /** + Create a suite. + **/ + @:selfCall + @:overload(function(fn:SuiteCallback):Promise {}) + @:overload(function(name:String, fn:SuiteCallback):Promise {}) + @:overload(function(options:TestOptions, fn:SuiteCallback):Promise {}) + function call(?name:String, ?options:TestOptions, ?fn:SuiteCallback):Promise; + + /** + Shorthand for skipping a suite. + **/ + @:overload(function(fn:SuiteCallback):Promise {}) + @:overload(function(name:String, fn:SuiteCallback):Promise {}) + @:overload(function(options:TestOptions, fn:SuiteCallback):Promise {}) + function skip(?name:String, ?options:TestOptions, ?fn:SuiteCallback):Promise; + + /** + Shorthand for marking a suite as `TODO`. + **/ + @:overload(function(fn:SuiteCallback):Promise {}) + @:overload(function(name:String, fn:SuiteCallback):Promise {}) + @:overload(function(options:TestOptions, fn:SuiteCallback):Promise {}) + function todo(?name:String, ?options:TestOptions, ?fn:SuiteCallback):Promise; + + /** + Shorthand for marking a suite as `only`. + **/ + @:overload(function(fn:SuiteCallback):Promise {}) + @:overload(function(name:String, fn:SuiteCallback):Promise {}) + @:overload(function(options:TestOptions, fn:SuiteCallback):Promise {}) + function only(?name:String, ?options:TestOptions, ?fn:SuiteCallback):Promise; + + /** + Shorthand for expecting a suite to fail (`expectFailure: true`). + + Added in: v24.14.0 + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#expecting-tests-to-fail + **/ + @:overload(function(fn:SuiteCallback):Promise {}) + @:overload(function(name:String, fn:SuiteCallback):Promise {}) + @:overload(function(options:TestOptions, fn:SuiteCallback):Promise {}) + function expectFailure(?name:String, ?options:TestOptions, ?fn:SuiteCallback):Promise; +} diff --git a/src/js/node/test/TestContext.hx b/src/js/node/test/TestContext.hx new file mode 100644 index 00000000..e9aa6503 --- /dev/null +++ b/src/js/node/test/TestContext.hx @@ -0,0 +1,239 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.test; + +import haxe.extern.EitherType; +import js.html.AbortSignal; +import js.node.Test.HookCallback; +import js.node.Test.HookOptions; +import js.node.Test.PlanOptions; +import js.node.Test.SnapshotAssertionOptions; +import js.node.Test.TestCallback; +import js.node.Test.TestOptions; +import js.node.Test.WaitForOptions; +#if haxe4 +import js.lib.Error; +import js.lib.Promise; +#else +import js.Error; +import js.Promise; +#end + +/** + Passed to each test function to interact with the test runner. + + The `TestContext` constructor is not part of the public API. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#class-testcontext +**/ +extern class TestContext { + /** + Create a hook that runs before subtests of the current test. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contextbeforefn-options + **/ + @:overload(function(fn:HookCallback):Void {}) + function before(?fn:HookCallback, ?options:HookOptions):Void; + + /** + Create a hook that runs before each subtest of the current test. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contextbeforeeachfn-options + **/ + @:overload(function(fn:HookCallback):Void {}) + function beforeEach(?fn:HookCallback, ?options:HookOptions):Void; + + /** + Create a hook that runs after the current test finishes. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contextafterfn-options + **/ + @:overload(function(fn:HookCallback):Void {}) + function after(?fn:HookCallback, ?options:HookOptions):Void; + + /** + Create a hook that runs after each subtest of the current test. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contextaftereachfn-options + **/ + @:overload(function(fn:HookCallback):Void {}) + function afterEach(?fn:HookCallback, ?options:HookOptions):Void; + + /** + Assertion methods bound to this context, used for test plans. + + Includes the usual `node:assert` methods (see `js.node.Assert`) plus + runner-specific snapshot helpers. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contextassert + **/ + var assert(default, null):TestContextAssert; + + /** + Write a diagnostic message included at the end of the test's results. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contextdiagnosticmessage + **/ + function diagnostic(message:String):Void; + + /** + Absolute path of the test file that created the current test. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contextfilepath + **/ + var filePath(default, null):Null; + + /** + Name of the test and each of its ancestors, separated by `>`. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contextfullname + **/ + var fullName(default, null):String; + + /** + Name of the test. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contextname + **/ + var name(default, null):String; + + /** + Whether the test succeeded. `false` before the test has executed + (e.g. in a `beforeEach` hook). + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contextpassed + **/ + var passed(default, null):Bool; + + /** + Failure reason for the test when it did not pass; wrapped with `cause`. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contexterror + **/ + var error(default, null):Null; + + /** + Zero-based attempt number when using `--test-rerun-failures`. + + Added in: v25.0.0 (documented on the Node 24 test page for newer runtimes). + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contextattempt + **/ + var attempt(default, null):Int; + + /** + Unique worker id for the current test file process, or `undefined` + outside a test context. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contextworkerid + **/ + var workerId(default, null):Null; + + /** + Set the number of assertions and subtests expected to run. + Use `t.assert` (not bare `assert`) so assertions are tracked. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contextplancountoptions + **/ + function plan(count:Int, ?options:PlanOptions):Void; + + /** + When `true`, only subtests with the `only` option set are run. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contextrunonlyshouldrunonlytests + **/ + function runOnly(shouldRunOnlyTests:Bool):Void; + + /** + Abort signal for cancelling test subtasks when the test is aborted. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contextsignal + **/ + var signal(default, null):AbortSignal; + + /** + Mark the test as skipped. Does not terminate the test function. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contextskipmessage + **/ + function skip(?message:String):Void; + + /** + Mark the test as `TODO`. Does not terminate the test function. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contexttodomessage + **/ + function todo(?message:String):Void; + + /** + Create a subtest under the current test. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contexttestname-options-fn + **/ + @:overload(function(fn:TestCallback):Promise {}) + @:overload(function(name:String, fn:TestCallback):Promise {}) + @:overload(function(options:TestOptions, fn:TestCallback):Promise {}) + function test(?name:String, ?options:TestOptions, ?fn:TestCallback):Promise; + + /** + Poll `condition` until it succeeds or the timeout elapses. + + `condition` may return a value or a `Promise` of that value; the returned + promise fulfills with the awaited successful result. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contextwaitforcondition-options + **/ + function waitFor(condition:Void->EitherType>, ?options:WaitForOptions):Promise; + + /** + `MockTracker` instance for this test. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#class-mocktracker + **/ + var mock(default, null):MockTracker; +} + +/** + Assertion helpers on `TestContext.assert`. + + Runner-specific snapshot APIs are typed here. The object also exposes the + usual `node:assert` methods bound to this context (see `js.node.Assert`); + those are intentionally not fully re-declared. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contextassert +**/ +extern class TestContextAssert implements Dynamic { + /** + Serialize `value` and write/compare against the snapshot file at `path`. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contextassertfilesnapshotvalue-path-options + **/ + function fileSnapshot(value:Dynamic, path:String, ?options:SnapshotAssertionOptions):Void; + + /** + Assert against (or update) a snapshot entry for this test. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contextassertsnapshotvalue-options + **/ + function snapshot(value:Dynamic, ?options:SnapshotAssertionOptions):Void; +} diff --git a/src/js/node/test/TestsStream.hx b/src/js/node/test/TestsStream.hx new file mode 100644 index 00000000..5906c106 --- /dev/null +++ b/src/js/node/test/TestsStream.hx @@ -0,0 +1,233 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.test; + +import haxe.Constraints.Function; +import haxe.extern.EitherType; +import js.node.events.EventEmitter.Event; +import js.node.stream.Readable; +#if haxe4 +import js.lib.Error; +#else +import js.Error; +#end + +/** + Events emitted by `TestsStream`. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#class-testsstream +**/ +enum abstract TestsStreamEvent(Event) to Event { + /** + Emitted when code coverage is enabled and all tests have completed. + **/ + var TestCoverage:TestsStreamEventVoid> = "test:coverage"; + + /** + Emitted when a test completes (execution order; see also `test:pass` / `test:fail`). + **/ + var TestComplete:TestsStreamEventVoid> = "test:complete"; + + /** + Emitted when a test is dequeued, right before it is executed. + **/ + var TestDequeue:TestsStreamEventVoid> = "test:dequeue"; + + /** + Emitted when `context.diagnostic` is called (declaration order). + **/ + var TestDiagnostic:TestsStreamEventVoid> = "test:diagnostic"; + + /** + Emitted when a test is enqueued for execution. + **/ + var TestEnqueue:TestsStreamEventVoid> = "test:enqueue"; + + /** + Emitted when a test fails (declaration order). + **/ + var TestFail:TestsStreamEventVoid> = "test:fail"; + + /** + Emitted when the runner is interrupted by `SIGINT`. + **/ + var TestInterrupted:TestsStreamEventVoid> = "test:interrupted"; + + /** + Emitted when a test passes (declaration order). + **/ + var TestPass:TestsStreamEventVoid> = "test:pass"; + + /** + Emitted when all subtests have completed for a given test. + **/ + var TestPlan:TestsStreamEventVoid> = "test:plan"; + + /** + Emitted when a test starts reporting status (declaration order). + **/ + var TestStart:TestsStreamEventVoid> = "test:start"; + + /** + Emitted when a running test writes to `stderr` (only with `--test`). + **/ + var TestStderr:TestsStreamEventVoid> = "test:stderr"; + + /** + Emitted when a running test writes to `stdout` (only with `--test`). + **/ + var TestStdout:TestsStreamEventVoid> = "test:stdout"; + + /** + Emitted when a test run completes, with aggregate counts. + **/ + var TestSummary:TestsStreamEventVoid> = "test:summary"; + + /** + Emitted when no more tests are queued in watch mode. + **/ + var TestWatchDrained:TestsStreamEventVoid> = "test:watch:drained"; + + /** + Emitted when tests are restarted due to a file change in watch mode. + **/ + var TestWatchRestarted:TestsStreamEventVoid> = "test:watch:restarted"; +} + +/** + Readable stream of test reporter events returned by `Test.run()`. + + @see https://nodejs.org/docs/latest-v24.x/api/test.html#class-testsstream +**/ +extern class TestsStream extends Readable {} + +/** + Payload for most test lifecycle events (`data` field shape varies slightly by event). +**/ +typedef TestsStreamTestEvent = { + var data:TestsStreamTestData; +} + +typedef TestsStreamTestData = { + @:optional var column:Null; + @:optional var details:TestsStreamTestDetails; + @:optional var file:Null; + @:optional var line:Null; + var name:String; + var nesting:Int; + @:optional var testId:Int; + @:optional var testNumber:Int; + @:optional var type:String; + @:optional var todo:EitherType; + @:optional var skip:EitherType; + @:optional var attempt:Int; + @:optional var passed_on_attempt:Int; +} + +typedef TestsStreamTestDetails = { + @:optional var passed:Bool; + @:optional var duration_ms:Float; + @:optional var error:Error; + @:optional var type:String; +} + +typedef TestsStreamDiagnosticEvent = { + var data:TestsStreamDiagnosticData; +} + +typedef TestsStreamDiagnosticData = { + @:optional var column:Null; + @:optional var file:Null; + @:optional var line:Null; + var message:String; + var nesting:Int; + var level:String; +} + +typedef TestsStreamPlanEvent = { + var data:TestsStreamPlanData; +} + +typedef TestsStreamPlanData = { + @:optional var column:Null; + @:optional var file:Null; + @:optional var line:Null; + var nesting:Int; + var count:Int; +} + +typedef TestsStreamStdioEvent = { + var data:TestsStreamStdioData; +} + +typedef TestsStreamStdioData = { + var file:String; + var message:String; +} + +typedef TestsStreamInterruptedEvent = { + var data:TestsStreamInterruptedData; +} + +typedef TestsStreamInterruptedData = { + var tests:Array; +} + +typedef TestsStreamInterruptedTest = { + @:optional var column:Null; + @:optional var file:Null; + @:optional var line:Null; + var name:String; + var nesting:Int; +} + +typedef TestsStreamSummaryEvent = { + var data:TestsStreamSummaryData; +} + +typedef TestsStreamSummaryData = { + var counts:TestsStreamSummaryCounts; + var duration_ms:Float; + @:optional var file:Null; + var success:Bool; +} + +typedef TestsStreamSummaryCounts = { + var cancelled:Int; + var failed:Int; + var passed:Int; + var skipped:Int; + var suites:Int; + var tests:Int; + var todo:Int; + var topLevel:Int; +} + +typedef TestsStreamCoverageEvent = { + var data:TestsStreamCoverageData; +} + +typedef TestsStreamCoverageData = { + var summary:Dynamic; + var nesting:Int; +} \ No newline at end of file From 1c0e66bdae40cda4c94e5af97915a38020c6a39e Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Tue, 14 Jul 2026 16:29:07 +1000 Subject: [PATCH 33/48] Audit fs/path/os/zlib for Node 24 and Haxe 4 (#224) * Audit fs/path/os/zlib for Node 24 and Haxe 4 Add missing Node 24 fs/zlib APIs (openAsBlob, mkdtempDisposable*, Zstd), drop Haxe 3 conditionals in this section, and tighten Dynamic where possible. Co-authored-by: Cursor * Complete section-2 Node 24 fs/zlib gap fill Add StatWatcher/Utf8Stream, Fs.promises, F_OK deprecations, glob URL cwd, FSWatcher ref/unref, Zlib options polish, and PathModule.toNamespacedPath. Co-authored-by: Cursor --------- Co-authored-by: Cursor --- src/Sys.hx | 8 +- src/js/node/Fs.hx | 100 ++++++++++-- src/js/node/FsPromises.hx | 33 +++- src/js/node/Path.hx | 1 + src/js/node/Zlib.hx | 118 ++++++++++++++- src/js/node/fs/Dir.hx | 12 +- src/js/node/fs/FSWatcher.hx | 16 +- src/js/node/fs/FileHandle.hx | 7 +- src/js/node/fs/StatWatcher.hx | 42 +++++ src/js/node/fs/Stats.hx | 22 +++ src/js/node/fs/Utf8Stream.hx | 236 +++++++++++++++++++++++++++++ src/js/node/zlib/Zlib.hx | 6 + src/js/node/zlib/ZstdCompress.hx | 31 ++++ src/js/node/zlib/ZstdDecompress.hx | 31 ++++ src/sys/FileSystem.hx | 8 +- src/sys/NodeSync.hx | 2 +- src/sys/io/FileInput.hx | 2 + src/sys/net/Host.hx | 2 +- 18 files changed, 626 insertions(+), 51 deletions(-) create mode 100644 src/js/node/fs/StatWatcher.hx create mode 100644 src/js/node/fs/Utf8Stream.hx create mode 100644 src/js/node/zlib/ZstdCompress.hx create mode 100644 src/js/node/zlib/ZstdDecompress.hx diff --git a/src/Sys.hx b/src/Sys.hx index e423a65d..68a8ac69 100644 --- a/src/Sys.hx +++ b/src/Sys.hx @@ -82,18 +82,14 @@ class Sys { return process.uptime(); } - #if (haxe_ver >= 3.3) @:deprecated("Use programPath instead") - #end public static inline function executablePath():String { return process.argv[0]; } - #if (haxe_ver >= 3.3) public static inline function programPath():String { return js.Node.__filename; } - #end public static function getChar(echo:Bool):Int { throw "Sys.getChar is currently not implemented on node.js"; @@ -132,7 +128,7 @@ private class FileOutput extends haxe.io.Output { return Fs.writeSync(fd, Buffer.hxFromBytes(s), pos, len); } - override public function writeString(s:String #if (haxe_ver >= 4), ?encoding:haxe.io.Encoding #end) { + override public function writeString(s:String, ?encoding:haxe.io.Encoding) { Fs.writeSync(fd, s); } @@ -154,6 +150,7 @@ private class FileInput extends haxe.io.Input { override public function readByte():Int { var buf = Buffer.alloc(1); + // TODO(section-2): typed Node ErrnoException instead of Dynamic catch try { Fs.readSync(fd, buf, 0, 1, null); } catch (e:Dynamic) { @@ -167,6 +164,7 @@ private class FileInput extends haxe.io.Input { override public function readBytes(s:Bytes, pos:Int, len:Int):Int { var buf = Buffer.hxFromBytes(s); + // TODO(section-2): typed Node ErrnoException instead of Dynamic catch try { return Fs.readSync(fd, buf, pos, len, null); } catch (e:Dynamic) { diff --git a/src/js/node/Fs.hx b/src/js/node/Fs.hx index 7b03a79d..2f0d76f2 100644 --- a/src/js/node/Fs.hx +++ b/src/js/node/Fs.hx @@ -31,12 +31,12 @@ import js.node.fs.FSWatcher; import js.node.fs.ReadStream; import js.node.fs.Stats; import js.node.fs.StatsFs; +import js.node.fs.StatWatcher; import js.node.fs.WriteStream; -#if haxe4 import js.lib.Error; -#else -import js.Error; -#end +import js.lib.Promise; +import js.node.web.Blob; +import js.node.url.URL; /** Most FS functions now support passing `String` and `Buffer`. @@ -87,6 +87,12 @@ typedef FsWriteFileOptions = { default: 'w' for `Fs.writeFile`, 'a' for `Fs.appendFile` **/ @:optional var flag:FsOpenFlag; + + /** + If `true`, the underlying file descriptor is flushed prior to closing it. + Default: `false`. + **/ + @:optional var flush:Bool; } /** @@ -637,7 +643,7 @@ typedef FsGlobOptions = { Current working directory. Default: `process.cwd()`. **/ - @:optional var cwd:String; + @:optional var cwd:EitherType; /** Function to filter out files/directories, or a list of glob patterns to be excluded. @@ -658,6 +664,32 @@ typedef FsGlobOptions = { @:optional var withFileTypes:Bool; } +/** + Disposable temporary directory returned by `Fs.mkdtempDisposableSync`. +**/ +typedef FsMkdtempDisposable = { + /** + The path of the created directory. + **/ + var path:String; + + /** + Removes the created directory and its contents. + Same as the `[Symbol.dispose]` method on the returned object. + **/ + function remove():Void; +} + +/** + Options for `Fs.openAsBlob`. +**/ +typedef FsOpenAsBlobOptions = { + /** + An optional MIME type for the blob. + **/ + @:optional var type:String; +} + /** A buffer for `Fs.readv` / `Fs.writev`. @@ -686,6 +718,15 @@ extern class Fs { **/ static var constants(default, null):FsConstants; + /** + Promise-based file system methods (`require('fs').promises`). + Same module object as `js.node.FsPromises`. + + Prefer calling static methods on `FsPromises` for Haxe typing of overloads. + **/ + // TODO(section-2): value type mirroring FsPromises static surface (static-method extern cannot be reused as value type) + static var promises(default, null):Dynamic; + /** Asynchronous rename(2). **/ @@ -914,6 +955,7 @@ extern class Fs { `cache` is an object literal of mapped paths that can be used to force a specific path resolution or avoid additional `stat` calls for known real paths. **/ + // TODO(section-2): model `realpath.native` / `realpathSync.native` function properties @:overload(function(path:FsPath, callback:Error->String->Void):Void {}) static function realpath(path:FsPath, cache:DynamicAccess, callback:Error->String->Void):Void; @@ -980,6 +1022,7 @@ extern class Fs { The created folder path is passed as a string to the `callback`'s second parameter. **/ + @:overload(function(prefix:String, options:EitherType, callback:Error->String->Void):Void {}) static function mkdtemp(prefix:String, callback:Error->String->Void):Void; /** @@ -987,7 +1030,19 @@ extern class Fs { Returns the created folder path. **/ - static function mkdtempSync(template:String):String; + @:overload(function(prefix:String, options:EitherType):String {}) + static function mkdtempSync(prefix:String):String; + + /** + Synchronously creates a unique temporary directory and returns a disposable object. + + When the object is disposed (or `remove` is called), the directory and its contents are removed + if they still exist. + + @see https://nodejs.org/api/fs.html#fsmkdtempdisposablesyncprefix-options + **/ + @:overload(function(prefix:String, options:EitherType):FsMkdtempDisposable {}) + static function mkdtempDisposableSync(prefix:String):FsMkdtempDisposable; /** Asynchronous readdir(3). @@ -1032,7 +1087,7 @@ extern class Fs { @:overload(function(pattern:EitherType>, callback:Error->Array->Void):Void {}) @:overload(function(pattern:EitherType>, options:FsGlobOptions, callback:Error->Array->Void):Void {}) static function glob(pattern:EitherType>, options:{ - ?cwd:String, + ?cwd:EitherType, ?exclude:EitherTypeBool, Array>, ?followSymlinks:Bool, withFileTypes:Bool @@ -1047,7 +1102,7 @@ extern class Fs { @:overload(function(pattern:EitherType>):Array {}) @:overload(function(pattern:EitherType>, options:FsGlobOptions):Array {}) static function globSync(pattern:EitherType>, options:{ - ?cwd:String, + ?cwd:EitherType, ?exclude:EitherTypeBool, Array>, ?followSymlinks:Bool, withFileTypes:Bool @@ -1082,6 +1137,17 @@ extern class Fs { @:overload(function(path:FsPath, flags:FsOpenFlag):Int {}) static function openSync(path:FsPath, flags:FsOpenFlag, mode:FsMode):Int; + /** + Returns a `Blob` whose data is backed by the given file. + + The file must not be modified after the `Blob` is created. + Any modifications will cause reading the `Blob` data to fail with a `DOMException` error. + + @see https://nodejs.org/api/fs.html#fsopenasblobpath-options + **/ + @:overload(function(path:FsPath):Promise {}) + static function openAsBlob(path:FsPath, options:FsOpenAsBlobOptions):Promise; + /** Change file timestamps of the file referenced by the supplied path. **/ @@ -1184,17 +1250,17 @@ extern class Fs { On Linux, positional writes don't work when the file is opened in append mode. The kernel ignores the position argument and always appends the data to the end of the file. **/ - @:overload(function(fd:Int, data:Dynamic, position:Int, encoding:String, callback:Error->Int->String->Void):Void {}) - @:overload(function(fd:Int, data:Dynamic, position:Int, callback:Error->Int->String->Void):Void {}) - @:overload(function(fd:Int, data:Dynamic, callback:Error->Int->String->Void):Void {}) + @:overload(function(fd:Int, data:String, position:Int, encoding:String, callback:Error->Int->String->Void):Void {}) + @:overload(function(fd:Int, data:String, position:Int, callback:Error->Int->String->Void):Void {}) + @:overload(function(fd:Int, data:String, callback:Error->Int->String->Void):Void {}) @:overload(function(fd:Int, buffer:Buffer, offset:Int, length:Int, callback:Error->Int->Buffer->Void):Void {}) static function write(fd:Int, buffer:Buffer, offset:Int, length:Int, position:Int, callback:Error->Int->Buffer->Void):Void; /** Synchronous version of `write`. Returns the number of bytes written. **/ - @:overload(function(fd:Int, data:Dynamic, position:Int, encoding:String):Int {}) - @:overload(function(fd:Int, data:Dynamic, ?position:Int):Int {}) + @:overload(function(fd:Int, data:String, position:Int, encoding:String):Int {}) + @:overload(function(fd:Int, data:String, ?position:Int):Int {}) static function writeSync(fd:Int, buffer:Buffer, offset:Int, length:Int, ?position:Int):Int; /** @@ -1324,8 +1390,8 @@ extern class Fs { The `listener` gets two arguments: the current stat object and the previous stat object. **/ - @:overload(function(filename:FsPath, listener:Stats->Stats->Void):Void {}) - static function watchFile(filename:FsPath, options:FsWatchFileOptions, listener:Stats->Stats->Void):Void; + @:overload(function(filename:FsPath, listener:Stats->Stats->Void):StatWatcher {}) + static function watchFile(filename:FsPath, options:FsWatchFileOptions, listener:Stats->Stats->Void):StatWatcher; /** Unstable. Use `watch` instead, if possible. @@ -1395,6 +1461,7 @@ extern class Fs { File is visible to the calling process. This is useful for determining if a file exists, but says nothing about rwx permissions. **/ + @:deprecated("Use Fs.constants.F_OK instead") static var F_OK(default, null):Int; /** @@ -1402,6 +1469,7 @@ extern class Fs { File can be read by the calling process. **/ + @:deprecated("Use Fs.constants.R_OK instead") static var R_OK(default, null):Int; /** @@ -1409,6 +1477,7 @@ extern class Fs { File can be written by the calling process. **/ + @:deprecated("Use Fs.constants.W_OK instead") static var W_OK(default, null):Int; /** @@ -1417,6 +1486,7 @@ extern class Fs { File can be executed by the calling process. This has no effect on Windows. **/ + @:deprecated("Use Fs.constants.X_OK instead") static var X_OK(default, null):Int; /** diff --git a/src/js/node/FsPromises.hx b/src/js/node/FsPromises.hx index d01d83ad..fde56d3b 100644 --- a/src/js/node/FsPromises.hx +++ b/src/js/node/FsPromises.hx @@ -29,11 +29,7 @@ import js.node.fs.Dirent; import js.node.fs.FileHandle; import js.node.fs.Stats; import js.node.fs.StatsFs; -#if haxe4 import js.lib.Promise; -#else -import js.Promise; -#end /** Minimal async iterator surface used by `glob` / `watch` (for `for await...of`). @@ -99,7 +95,7 @@ extern class FsPromises { @:overload(function(pattern:EitherType>):FsPromisesAsyncIterator {}) @:overload(function(pattern:EitherType>, options:FsGlobOptions):FsPromisesAsyncIterator {}) static function glob(pattern:EitherType>, options:{ - ?cwd:String, + ?cwd:haxe.extern.EitherType, ?exclude:EitherTypeBool, Array>, ?followSymlinks:Bool, withFileTypes:Bool @@ -149,6 +145,17 @@ extern class FsPromises { @:overload(function(prefix:String):Promise {}) static function mkdtemp(prefix:String, options:EitherType):Promise; + /** + Asynchronously creates a unique temporary directory and returns an async-disposable object. + + When the object is disposed (or `remove` is awaited), the directory and its contents are removed + if they still exist. + + @see https://nodejs.org/api/fs.html#fspromisesmkdtempdisposableprefix-options + **/ + @:overload(function(prefix:String):Promise {}) + static function mkdtempDisposable(prefix:String, options:EitherType):Promise; + /** Opens a `FileHandle`. **/ @@ -298,3 +305,19 @@ typedef FsPromisesWatchEvent = { **/ var filename:Null>; } + +/** + Async-disposable temporary directory returned by `FsPromises.mkdtempDisposable`. +**/ +typedef FsPromisesMkdtempDisposable = { + /** + The path of the created directory. + **/ + var path:String; + + /** + Asynchronously removes the created directory and its contents. + Same as the `[Symbol.asyncDispose]` method on the returned object. + **/ + function remove():Promise; +} diff --git a/src/js/node/Path.hx b/src/js/node/Path.hx index f208af82..9a86a848 100644 --- a/src/js/node/Path.hx +++ b/src/js/node/Path.hx @@ -201,4 +201,5 @@ private typedef PathModule = { var delimiter(default, null):String; function parse(pathString:String):PathObject; function format(pathObject:PathObject):String; + function toNamespacedPath(path:String):String; } diff --git a/src/js/node/Zlib.hx b/src/js/node/Zlib.hx index 0684c4de..326b5fba 100644 --- a/src/js/node/Zlib.hx +++ b/src/js/node/Zlib.hx @@ -26,11 +26,7 @@ import haxe.DynamicAccess; import haxe.extern.EitherType; import js.node.Buffer; import js.node.zlib.*; -#if haxe4 import js.lib.Error; -#else -import js.Error; -#end typedef ZlibOptions = { /** @@ -38,6 +34,11 @@ typedef ZlibOptions = { **/ @:optional var flush:Int; + /** + default: `Zlib.Z_FINISH` + **/ + @:optional var finishFlush:Int; + /** default: 16*1024 **/ @@ -64,6 +65,18 @@ typedef ZlibOptions = { deflate/inflate only, empty dictionary by default **/ @:optional var dictionary:Buffer; + + /** + Limits output size when using convenience methods. + Default: `buffer.kMaxLength` + **/ + @:optional var maxOutputLength:Int; + + /** + If `true`, returns an object with `buffer` and `engine`. + Default: `false` + **/ + @:optional var info:Bool; } /** @@ -103,9 +116,53 @@ typedef BrotliOptions = { @:optional var info:Bool; } +/** + Options for Zstandard (zstd) compression and decompression. + + Stability: 1 - Experimental +**/ +typedef ZstdOptions = { + /** + default: `zlib.constants.ZSTD_e_continue` + **/ + @:optional var flush:Int; + + /** + default: `zlib.constants.ZSTD_e_end` + **/ + @:optional var finishFlush:Int; + + /** + default: 16*1024 + **/ + @:optional var chunkSize:Int; + + /** + Key-value object containing indexed Zstd parameters. + **/ + @:optional var params:DynamicAccess; + + /** + The maximum length of the output that can be produced by zlib streams. + Default: `buffer.kMaxLength` + **/ + @:optional var maxOutputLength:Int; + + /** + If `true`, returns an object with `buffer` and `engine`. + Default: `false` + **/ + @:optional var info:Bool; + + /** + Optional dictionary used to improve compression efficiency. + **/ + @:optional var dictionary:Buffer; +} + /** This provides bindings to Gzip/Gunzip, Deflate/Inflate, DeflateRaw/InflateRaw, - and BrotliCompress/BrotliDecompress classes. + BrotliCompress/BrotliDecompress, and ZstdCompress/ZstdDecompress classes. Each class takes options, and is a readable/writable Stream. **/ @:jsRequire("zlib") @@ -175,6 +232,13 @@ extern class Zlib { **/ static var Z_NULL(default, null):Int; + /** + Object containing all zlib constants (including Z_*, BROTLI_*, and ZSTD_*). + Prefer this over the legacy top-level `Z_*` fields on this class. + **/ + // TODO(section-2): typed ZlibConstants covering Z_*/BROTLI_*/ZSTD_* instead of DynamicAccess + static var constants(default, null):DynamicAccess; + /** Returns a new `Gzip` object with an `options`. **/ @@ -220,6 +284,20 @@ extern class Zlib { **/ static function createBrotliDecompress(?options:BrotliOptions):BrotliDecompress; + /** + Returns a new `ZstdCompress` object with an `options`. + + Stability: 1 - Experimental + **/ + static function createZstdCompress(?options:ZstdOptions):ZstdCompress; + + /** + Returns a new `ZstdDecompress` object with an `options`. + + Stability: 1 - Experimental + **/ + static function createZstdDecompress(?options:ZstdOptions):ZstdDecompress; + /** Computes a 32-bit Cyclic Redundancy Check checksum of `data`. If `value` is given, it is used as the starting value of the checksum. @@ -324,4 +402,34 @@ extern class Zlib { Decompress a Buffer with `BrotliDecompress` (synchronous version). **/ static function brotliDecompressSync(buf:EitherType, ?options:BrotliOptions):Buffer; + + /** + Compress a chunk of data with `ZstdCompress`. + + Stability: 1 - Experimental + **/ + @:overload(function(buf:EitherType, options:ZstdOptions, callback:Error->Buffer->Void):Void {}) + static function zstdCompress(buf:EitherType, callback:Error->Buffer->Void):Void; + + /** + Compress a chunk of data with `ZstdCompress` (synchronous version). + + Stability: 1 - Experimental + **/ + static function zstdCompressSync(buf:EitherType, ?options:ZstdOptions):Buffer; + + /** + Decompress a chunk of data with `ZstdDecompress`. + + Stability: 1 - Experimental + **/ + @:overload(function(buf:EitherType, options:ZstdOptions, callback:Error->Buffer->Void):Void {}) + static function zstdDecompress(buf:EitherType, callback:Error->Buffer->Void):Void; + + /** + Decompress a chunk of data with `ZstdDecompress` (synchronous version). + + Stability: 1 - Experimental + **/ + static function zstdDecompressSync(buf:EitherType, ?options:ZstdOptions):Buffer; } diff --git a/src/js/node/fs/Dir.hx b/src/js/node/fs/Dir.hx index 28f76116..196d38aa 100644 --- a/src/js/node/fs/Dir.hx +++ b/src/js/node/fs/Dir.hx @@ -22,11 +22,8 @@ package js.node.fs; -#if haxe4 import js.lib.Error; -#else -import js.Error; -#end +import js.lib.Promise; /** A class representing a directory stream. @@ -43,7 +40,10 @@ extern class Dir { /** Asynchronously close the directory's underlying resource handle. Subsequent reads will result in errors. + + Without a callback, returns a `Promise` that is fulfilled after the resource has been closed. **/ + @:overload(function():Promise {}) function close(callback:Error->Void):Void; /** @@ -55,8 +55,10 @@ extern class Dir { /** Asynchronously read the next directory entry via readdir(3) as a `Dirent`. - The callback is called with a `Dirent`, or `null` if there are no more directory entries to read. + Without a callback, returns a `Promise` fulfilled with a `Dirent`, or `null` if there are no more entries. + With a callback, it is called with a `Dirent`, or `null` if there are no more directory entries to read. **/ + @:overload(function():Promise> {}) function read(callback:Error->Null->Void):Void; /** diff --git a/src/js/node/fs/FSWatcher.hx b/src/js/node/fs/FSWatcher.hx index d3c11995..7570338c 100644 --- a/src/js/node/fs/FSWatcher.hx +++ b/src/js/node/fs/FSWatcher.hx @@ -24,11 +24,7 @@ package js.node.fs; import js.node.Fs.FsPath; import js.node.events.EventEmitter; -#if haxe4 import js.lib.Error; -#else -import js.Error; -#end /** Enumeration of possible types of changes for 'change' event. @@ -65,4 +61,16 @@ extern class FSWatcher extends EventEmitter { Stop watching for changes on the given `FSWatcher`. **/ function close():Void; + + /** + When called, requests that the Node.js event loop not exit so long as the `FSWatcher` is active. + Calling `ref()` multiple times has no effect. + **/ + function ref():FSWatcher; + + /** + When called, the active `FSWatcher` will not require the Node.js event loop to remain active. + Calling `unref()` multiple times has no effect. + **/ + function unref():FSWatcher; } diff --git a/src/js/node/fs/FileHandle.hx b/src/js/node/fs/FileHandle.hx index 8bddbfe8..9def4dd8 100644 --- a/src/js/node/fs/FileHandle.hx +++ b/src/js/node/fs/FileHandle.hx @@ -28,11 +28,7 @@ import js.node.Fs; import js.node.events.EventEmitter; import js.node.events.EventEmitter.Event; import js.node.readline.Interface; -#if haxe4 import js.lib.Promise; -#else -import js.Promise; -#end /** A `FileHandle` object is a wrapper for a numeric file descriptor. @@ -111,9 +107,8 @@ extern class FileHandle extends EventEmitter { /** Returns a byte-oriented `ReadableStream` for the file contents. - - Typed as `Dynamic` until web streams externs are added (same pattern as `Blob.stream`). **/ + // TODO(section-6): return typed ReadableStream once web streams externs are available function readableWebStream(?options:FileHandleReadableWebStreamOptions):Dynamic; /** diff --git a/src/js/node/fs/StatWatcher.hx b/src/js/node/fs/StatWatcher.hx new file mode 100644 index 00000000..db9d7e9b --- /dev/null +++ b/src/js/node/fs/StatWatcher.hx @@ -0,0 +1,42 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.fs; + +/** + Objects returned from `Fs.watchFile` are of this type. + + @see https://nodejs.org/api/fs.html#class-fsstatwatcher +**/ +extern class StatWatcher { + /** + When called, requests that the Node.js event loop not exit so long as the `StatWatcher` is active. + Calling `ref()` multiple times has no effect. + **/ + function ref():StatWatcher; + + /** + When called, the active `StatWatcher` will not require the Node.js event loop to remain active. + Calling `unref()` multiple times has no effect. + **/ + function unref():StatWatcher; +} diff --git a/src/js/node/fs/Stats.hx b/src/js/node/fs/Stats.hx index 90f9ad4b..2935205f 100644 --- a/src/js/node/fs/Stats.hx +++ b/src/js/node/fs/Stats.hx @@ -37,6 +37,28 @@ extern class Stats { var blksize:Null; var blocks:Null; + /** + Milliseconds timestamp of `atime`. + **/ + var atimeMs:Float; + + /** + Milliseconds timestamp of `mtime`. + **/ + var mtimeMs:Float; + + /** + Milliseconds timestamp of `ctime`. + **/ + var ctimeMs:Float; + + /** + Milliseconds timestamp of `birthtime`. + **/ + var birthtimeMs:Float; + + // TODO(section-2): atimeNs/mtimeNs/ctimeNs/birthtimeNs (bigint when `{bigint: true}` is passed to stat) + /** "Access Time" - Time when file data last accessed. diff --git a/src/js/node/fs/Utf8Stream.hx b/src/js/node/fs/Utf8Stream.hx new file mode 100644 index 00000000..65b41e37 --- /dev/null +++ b/src/js/node/fs/Utf8Stream.hx @@ -0,0 +1,236 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.fs; + +import haxe.extern.EitherType; +import js.lib.Error; +import js.node.Buffer; +import js.node.Fs.FsMode; +import js.node.Fs.FsPath; +import js.node.events.EventEmitter; +import js.node.events.EventEmitter.Event; + +/** + An optimized UTF-8 stream writer with on-demand flushing. + + Stability: 1 - Experimental (added in Node.js v24.6.0). + + @see https://nodejs.org/api/fs.html#class-fsutf8stream +**/ +@:jsRequire("fs", "Utf8Stream") +extern class Utf8Stream extends EventEmitter { + /** + Whether the stream is appending to the file or truncating it. + **/ + var append(default, null):Bool; + + /** + The type of data that can be written: `'utf8'` or `'buffer'`. + **/ + var contentMode(default, null):String; + + /** + The file descriptor being written to. + **/ + var fd(default, null):Null; + + /** + The file being written to. + **/ + var file(default, null):Null; + + /** + Whether `fs.fsyncSync()` is performed after every write. + **/ + var fsync(default, null):Bool; + + /** + Maximum length of the internal buffer. + **/ + var maxLength(default, null):Null; + + /** + Minimum length of the internal buffer required before flushing. + **/ + var minLength(default, null):Null; + + /** + Whether the stream ensures the destination directory exists. + **/ + var mkdir(default, null):Bool; + + /** + Mode of the file being written to. + **/ + var mode(default, null):Null; + + /** + Milliseconds between periodic flushes (`0` disables). + **/ + var periodicFlush(default, null):Int; + + /** + Whether the stream writes synchronously. + **/ + var sync(default, null):Bool; + + /** + Whether the stream is currently writing. + **/ + var writing(default, null):Bool; + + function new(?options:Utf8StreamOptions); + + /** + Close the stream immediately, without flushing the internal buffer. + **/ + function destroy():Void; + + /** + Close the stream gracefully, flushing the internal buffer before closing. + **/ + function end():Void; + + /** + Writes the current buffer to the file if a write is not already in progress. + **/ + function flush(callback:Null->Void):Void; + + /** + Flushes buffered data synchronously. + **/ + function flushSync():Void; + + /** + Reopen the file in place (useful for log rotation). + **/ + function reopen(file:FsPath):Void; + + /** + Write `data` to the stream. + Returns `true` if the data was written / buffered successfully. + **/ + function write(data:EitherType):Bool; +} + +/** + Events emitted by `Utf8Stream`. +**/ +enum abstract Utf8StreamEvent(Event) to Event { + var Close:Utf8StreamEventVoid> = "close"; + var Drain:Utf8StreamEventVoid> = "drain"; + + /** + Emitted when `maxLength` is reached and data is dropped. + **/ + var Drop:Utf8StreamEvent->Void> = "drop"; + + var Error:Utf8StreamEventVoid> = "error"; + var Finish:Utf8StreamEventVoid> = "finish"; + var Ready:Utf8StreamEventVoid> = "ready"; + + /** + Emitted when a write completes; argument is bytes written. + **/ + var Write:Utf8StreamEventVoid> = "write"; +} + +/** + Options for `new Utf8Stream()`. +**/ +typedef Utf8StreamOptions = { + /** + Append writes instead of truncating. + Default: `true`. + **/ + @:optional var append:Bool; + + /** + `'utf8'` or `'buffer'`. + Default: `'utf8'`. + **/ + @:optional var contentMode:String; + + /** + Destination file path. + **/ + @:optional var dest:FsPath; + + /** + Existing file descriptor. + **/ + @:optional var fd:Int; + + /** + Custom `fs` implementation for mocking / testing. + **/ + // TODO(section-2): type as structural fs subset once practical + @:optional var fs:Dynamic; + + /** + Perform `fsyncSync` after every write. + **/ + @:optional var fsync:Bool; + + /** + Maximum internal buffer length; excess writes emit `drop`. + **/ + @:optional var maxLength:Int; + + /** + Maximum bytes per write. + Default: `16384`. + **/ + @:optional var maxWrite:Int; + + /** + Minimum buffer length required before flushing. + **/ + @:optional var minLength:Int; + + /** + Ensure directory for `dest` exists. + Default: `false`. + **/ + @:optional var mkdir:Bool; + + /** + File mode when creating. + **/ + @:optional var mode:FsMode; + + /** + Call flush every `periodicFlush` milliseconds. + **/ + @:optional var periodicFlush:Int; + + /** + Called on `EAGAIN` / `EBUSY`; return `true` to retry. + **/ + @:optional var retryEAGAIN:Null->Int->Int->Bool; + + /** + Perform writes synchronously. + **/ + @:optional var sync:Bool; +} diff --git a/src/js/node/zlib/Zlib.hx b/src/js/node/zlib/Zlib.hx index a62746a3..8b2a3cd0 100644 --- a/src/js/node/zlib/Zlib.hx +++ b/src/js/node/zlib/Zlib.hx @@ -27,6 +27,12 @@ package js.node.zlib; It is documented here because it is the base class of the compressor/decompressor classes. **/ extern class Zlib extends js.node.stream.Transform { + /** + The number of bytes written to the engine before the bytes are processed + (compressed or decompressed, as appropriate for the derived class). + **/ + var bytesWritten(default, null):Float; + /** Flush pending data. diff --git a/src/js/node/zlib/ZstdCompress.hx b/src/js/node/zlib/ZstdCompress.hx new file mode 100644 index 00000000..1bedde64 --- /dev/null +++ b/src/js/node/zlib/ZstdCompress.hx @@ -0,0 +1,31 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.zlib; + +/** + Compress data using the Zstandard (zstd) algorithm. + + Stability: 1 - Experimental +**/ +@:jsRequire("zlib", "ZstdCompress") +extern class ZstdCompress extends Zlib {} diff --git a/src/js/node/zlib/ZstdDecompress.hx b/src/js/node/zlib/ZstdDecompress.hx new file mode 100644 index 00000000..d46ee1cd --- /dev/null +++ b/src/js/node/zlib/ZstdDecompress.hx @@ -0,0 +1,31 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.zlib; + +/** + Decompress data using the Zstandard (zstd) algorithm. + + Stability: 1 - Experimental +**/ +@:jsRequire("zlib", "ZstdDecompress") +extern class ZstdDecompress extends Zlib {} diff --git a/src/sys/FileSystem.hx b/src/sys/FileSystem.hx index 6facacc0..0c27efc5 100644 --- a/src/sys/FileSystem.hx +++ b/src/sys/FileSystem.hx @@ -2,16 +2,13 @@ package sys; import js.node.Fs; import js.node.Path; -#if haxe4 import js.lib.Error; -#else -import js.Error; -#end @:dce @:coreApi class FileSystem { public static function exists(path:String):Bool { + // TODO(section-2): typed Node ErrnoException instead of Dynamic catch return try { Fs.accessSync(path); true; @@ -27,6 +24,7 @@ class FileSystem { } public static inline function fullPath(relPath:String):String { + // TODO(section-2): typed Node ErrnoException instead of Dynamic catch return try Fs.realpathSync(relPath) catch (e:Dynamic) null; } @@ -37,10 +35,12 @@ class FileSystem { } public static function isDirectory(path:String):Bool { + // TODO(section-2): typed Node ErrnoException instead of Dynamic catch return try Fs.statSync(path).isDirectory() catch (e:Dynamic) false; } public static function createDirectory(path:String):Void { + // TODO(section-2): typed Node ErrnoException instead of Dynamic catch try { Fs.mkdirSync(path); } catch (e:Dynamic) { diff --git a/src/sys/NodeSync.hx b/src/sys/NodeSync.hx index ce7e48d5..2ccbdb02 100644 --- a/src/sys/NodeSync.hx +++ b/src/sys/NodeSync.hx @@ -6,7 +6,7 @@ private extern class Deasync { } class NodeSync { - public static function callMany(f:Dynamic->Void):Array { + public static function callMany(f:Dynamic->Void):Array { // TODO(section-2): replace Dynamic with typed callback/result once ErrnoException lands var retArgs = null; var wait = Reflect.makeVarArgs(function(args) retArgs = args); f(wait); diff --git a/src/sys/io/FileInput.hx b/src/sys/io/FileInput.hx index 7c689536..671b2f2b 100644 --- a/src/sys/io/FileInput.hx +++ b/src/sys/io/FileInput.hx @@ -26,6 +26,7 @@ class FileInput extends haxe.io.Input { override public function readByte():Int { var buf = Buffer.alloc(1); + // TODO(section-2): typed Node ErrnoException instead of Dynamic catch var bytesRead = try { Fs.readSync(fd, buf, 0, 1, pos); } catch (e:Dynamic) { @@ -44,6 +45,7 @@ class FileInput extends haxe.io.Input { var bytesRead = try { Fs.readSync(fd, buf, pos, len, this.pos); } catch (e:Dynamic) { + // TODO(section-2): typed Node ErrnoException instead of Dynamic catch if (e.code == "EOF") throwEof(); throw Error.Custom(e); diff --git a/src/sys/net/Host.hx b/src/sys/net/Host.hx index 584fa3e1..98dea0b7 100644 --- a/src/sys/net/Host.hx +++ b/src/sys/net/Host.hx @@ -3,7 +3,7 @@ package sys.net; @:coreApi @:allow(sys.net.Socket) class Host { - #if (haxe_ver >= 3.3) public #end var host(default, null):String; + public var host(default, null):String; public var ip(default, null):Int; From 140fa6eec31560442c28ff2bbaa8eb026e19407e Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Tue, 14 Jul 2026 16:35:52 +1000 Subject: [PATCH 34/48] Audit observability/test externs for Node 24 + Haxe 4 (#225) * Audit observability and test externs for Node 24 / Haxe 4. Drop remaining Haxe 3 conditionals, prefer js.node.web.AbortSignal, and add core node:trace_events, node:sqlite, and node:sea surfaces. Co-authored-by: Cursor * Polish section 7 audit: DomStorage params, sqlite fields, typing. Fix registerStorage options shape, expose DatabaseSync open/txn/limits, tighten Network headers typing, and clean MockTracker formatting. Co-authored-by: Cursor --------- Co-authored-by: Cursor --- src/_internal/SuppressDeprecated.hx | 2 - src/js/node/Inspector.hx | 7 +- src/js/node/InspectorPromises.hx | 8 +- src/js/node/PerfHooks.hx | 13 +- src/js/node/Sea.hx | 88 +++++++ src/js/node/Sqlite.hx | 131 ++++++++++ src/js/node/Test.hx | 8 +- src/js/node/TraceEvents.hx | 60 +++++ src/js/node/diagnostics_channel/Channel.hx | 10 +- .../diagnostics_channel/TracingChannel.hx | 4 - src/js/node/inspector/DomStorage.hx | 13 +- src/js/node/inspector/Network.hx | 22 +- src/js/node/inspector/Session.hx | 4 - src/js/node/inspector/promises/Session.hx | 4 - src/js/node/perf_hooks/Histogram.hx | 6 +- src/js/node/perf_hooks/RecordableHistogram.hx | 2 + src/js/node/sqlite/DatabaseSync.hx | 244 ++++++++++++++++++ src/js/node/sqlite/SQLTagStore.hx | 79 ++++++ src/js/node/sqlite/Session.hx | 50 ++++ src/js/node/sqlite/StatementSync.hx | 122 +++++++++ src/js/node/test/ItFunction.hx | 4 - src/js/node/test/MockTimers.hx | 4 - src/js/node/test/MockTracker.hx | 23 -- src/js/node/test/SuiteContext.hx | 2 +- src/js/node/test/SuiteFunction.hx | 4 - src/js/node/test/TestContext.hx | 9 +- src/js/node/test/TestsStream.hx | 4 - src/js/node/trace_events/Tracing.hx | 51 ++++ 28 files changed, 891 insertions(+), 87 deletions(-) create mode 100644 src/js/node/Sea.hx create mode 100644 src/js/node/Sqlite.hx create mode 100644 src/js/node/TraceEvents.hx create mode 100644 src/js/node/sqlite/DatabaseSync.hx create mode 100644 src/js/node/sqlite/SQLTagStore.hx create mode 100644 src/js/node/sqlite/Session.hx create mode 100644 src/js/node/sqlite/StatementSync.hx create mode 100644 src/js/node/trace_events/Tracing.hx diff --git a/src/_internal/SuppressDeprecated.hx b/src/_internal/SuppressDeprecated.hx index 1671aec1..4d781c06 100644 --- a/src/_internal/SuppressDeprecated.hx +++ b/src/_internal/SuppressDeprecated.hx @@ -6,7 +6,6 @@ import haxe.macro.Context; @:noCompletion class SuppressDeprecated { public static function run() { - #if haxe4 Context.onAfterTyping(function(_) { Context.filterMessages(x -> switch x { case Warning(msg, pos) if (~/js\/node\/Url\.hx$/.match(Context.getPosInfos(pos).file)): @@ -17,7 +16,6 @@ class SuppressDeprecated { true; }); }); - #end } } #end diff --git a/src/js/node/Inspector.hx b/src/js/node/Inspector.hx index 6545881a..00de4e80 100644 --- a/src/js/node/Inspector.hx +++ b/src/js/node/Inspector.hx @@ -28,8 +28,10 @@ import js.node.inspector.InspectorConsole; The `inspector` module provides an API for interacting with the V8 inspector. Related types live under `js.node.inspector` (`Session`, `Network`, `NetworkResources`, `DomStorage`). + Use those types directly (e.g. `js.node.inspector.Network.requestWillBeSent(...)`) — they map to + `inspector.Network` / `inspector.NetworkResources` / `inspector.DOMStorage` via `@:jsRequire`. - @see https://nodejs.org/api/inspector.html + @see https://nodejs.org/docs/latest-v24.x/api/inspector.html **/ @:jsRequire("inspector") extern class Inspector { @@ -41,7 +43,8 @@ extern class Inspector { and flow control has been passed to the debugger client. Returns a Disposable (`{ [Symbol.dispose](): void }`) that calls `inspector.close()`. - Typed as `Dynamic` because hxnodejs does not yet model the Web IDL `Disposable` interface. + + // TODO: model Web IDL `Disposable` / `Symbol.dispose` instead of `Dynamic`. **/ static function open(?port:Int, ?host:String, ?wait:Bool):Dynamic; diff --git a/src/js/node/InspectorPromises.hx b/src/js/node/InspectorPromises.hx index 166baadd..48fe1d34 100644 --- a/src/js/node/InspectorPromises.hx +++ b/src/js/node/InspectorPromises.hx @@ -31,9 +31,10 @@ import js.node.inspector.promises.Session; Stability: 1 - Experimental. Module-level helpers match `Inspector`; use `js.node.inspector.promises.Session` - for a `Session` whose `post` returns a `Promise`. + for a `Session` whose `post` returns a `Promise`. DevTools helpers live under + `js.node.inspector` (`Network`, `NetworkResources`, `DomStorage`). - @see https://nodejs.org/api/inspector.html#promises-api + @see https://nodejs.org/docs/latest-v24.x/api/inspector.html#promises-api **/ @:jsRequire("inspector/promises") extern class InspectorPromises { @@ -41,7 +42,8 @@ extern class InspectorPromises { Activate inspector on host and port. Returns a Disposable (`{ [Symbol.dispose](): void }`) that calls `close()`. - Typed as `Dynamic` because hxnodejs does not yet model the Web IDL `Disposable` interface. + + // TODO: model Web IDL `Disposable` / `Symbol.dispose` instead of `Dynamic`. **/ static function open(?port:Int, ?host:String, ?wait:Bool):Dynamic; diff --git a/src/js/node/PerfHooks.hx b/src/js/node/PerfHooks.hx index 0428b59d..fbc7d7d1 100644 --- a/src/js/node/PerfHooks.hx +++ b/src/js/node/PerfHooks.hx @@ -32,7 +32,7 @@ import js.node.perf_hooks.RecordableHistogram; This module provides an implementation of a subset of the W3C Web Performance APIs as well as additional APIs for Node.js-specific performance measurements. - @see https://nodejs.org/api/perf_hooks.html + @see https://nodejs.org/docs/latest-v24.x/api/perf_hooks.html **/ @:jsRequire("perf_hooks") extern class PerfHooks { @@ -40,14 +40,17 @@ extern class PerfHooks { An object that can be used to collect performance metrics from the current Node.js instance. It is similar to `window.performance` in browsers. - @see https://nodejs.org/api/perf_hooks.html#perf_hooksperformance + // TODO(section-1): `Node.performance` in `src/js/Node.hx` still has a Haxe 3 + // `untyped __js__` fallback; drop that when section-1 audits globals. + + @see https://nodejs.org/docs/latest-v24.x/api/perf_hooks.html#perf_hooksperformance **/ static var performance(default, never):Performance; /** Constants for garbage collection kinds and flags. - @see https://nodejs.org/api/perf_hooks.html#perf_hooksconstants + @see https://nodejs.org/docs/latest-v24.x/api/perf_hooks.html#perf_hooksconstants **/ static var constants(default, never):PerfHooksConstants; @@ -120,12 +123,16 @@ typedef CreateHistogramOptions = { /** The lowest discernible value. Must be an integer value greater than 0. Default: `1`. + + // TODO: allow BigInt when hxnodejs gains a BigInt type (Node accepts number | bigint). **/ @:optional var lowest:EitherType; /** The highest recordable value. Must be an integer value that is equal to or greater than two times `lowest`. Default: `Number.MAX_SAFE_INTEGER`. + + // TODO: allow BigInt when hxnodejs gains a BigInt type (Node accepts number | bigint). **/ @:optional var highest:EitherType; diff --git a/src/js/node/Sea.hx b/src/js/node/Sea.hx new file mode 100644 index 00000000..daae3977 --- /dev/null +++ b/src/js/node/Sea.hx @@ -0,0 +1,88 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node; + +import js.lib.ArrayBuffer; +import js.node.web.Blob; + +/** + Built-in helpers for scripts running inside a single-executable application (SEA). + + Stability: 1.1 - Active development. + + @see https://nodejs.org/docs/latest-v24.x/api/single-executable-applications.html#single-executable-application-api +**/ +@:jsRequire("node:sea") +extern class Sea { + /** + Whether this script is running inside a single-executable application. + + @see https://nodejs.org/docs/latest-v24.x/api/single-executable-applications.html#seaissea + **/ + static function isSea():Bool; + + /** + Retrieve a bundled asset by key. + + Without `encoding`, returns a copy of the asset as an `ArrayBuffer`. + With `encoding`, returns a decoded string (any encoding supported by `TextDecoder`). + + @see https://nodejs.org/docs/latest-v24.x/api/single-executable-applications.html#seagetassetkey-encoding + **/ + @:overload(function(key:String, encoding:String):String {}) + static function getAsset(key:String):ArrayBuffer; + + /** + Retrieve a bundled asset as a `Blob`. + + @see https://nodejs.org/docs/latest-v24.x/api/single-executable-applications.html#seagetassetasblobkey-options + **/ + static function getAssetAsBlob(key:String, ?options:SeaBlobOptions):Blob; + + /** + Retrieve the raw asset without copying. + + @see https://nodejs.org/docs/latest-v24.x/api/single-executable-applications.html#seagetrawassetkey + **/ + static function getRawAsset(key:String):ArrayBuffer; + + /** + Keys of all assets embedded in the executable. + Throws when not running inside a SEA. + + Added in: v24.8.0 + + @see https://nodejs.org/docs/latest-v24.x/api/single-executable-applications.html#seagetassetkeys + **/ + static function getAssetKeys():Array; +} + +/** + Options for `Sea.getAssetAsBlob`. +**/ +typedef SeaBlobOptions = { + /** + Optional MIME type for the blob. + **/ + @:optional var type:String; +} diff --git a/src/js/node/Sqlite.hx b/src/js/node/Sqlite.hx new file mode 100644 index 00000000..a2b71dde --- /dev/null +++ b/src/js/node/Sqlite.hx @@ -0,0 +1,131 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node; + +import haxe.extern.EitherType; +import js.lib.Promise; +import js.node.buffer.Buffer; +import js.node.sqlite.DatabaseSync; +import js.node.url.URL; + +/** + The `node:sqlite` module facilitates working with SQLite databases. + + Stability: 1.2 - Release candidate. + + Available only under the `node:` scheme. + + @see https://nodejs.org/docs/latest-v24.x/api/sqlite.html +**/ +@:jsRequire("node:sqlite") +extern class Sqlite { + /** + Constants used by authorizer callbacks and changeset conflict handlers. + **/ + static var constants(default, never):SqliteConstants; + + /** + Make a backup of `sourceDb` to `path`. + + @see https://nodejs.org/docs/latest-v24.x/api/sqlite.html#sqlitebackupsourcedb-path-options + **/ + static function backup(sourceDb:DatabaseSync, path:SqlitePath, ?options:SqliteBackupOptions):Promise; +} + +/** + Path accepted by SQLite open / backup APIs. +**/ +typedef SqlitePath = EitherType>; + +/** + Options for `Sqlite.backup`. +**/ +typedef SqliteBackupOptions = { + @:optional var source:String; + @:optional var target:String; + @:optional var rate:Int; + @:optional var progress:SqliteBackupProgress->Void; +} + +/** + Progress info for `Sqlite.backup`. +**/ +typedef SqliteBackupProgress = { + var totalPages:Float; + var remainingPages:Float; +} + +/** + Constants exported by `node:sqlite`. + + Includes authorizer action codes, authorizer results, and changeset conflict codes. +**/ +typedef SqliteConstants = { + var SQLITE_OK(default, never):Int; + var SQLITE_DENY(default, never):Int; + var SQLITE_IGNORE(default, never):Int; + + var SQLITE_CHANGESET_OMIT(default, never):Int; + var SQLITE_CHANGESET_REPLACE(default, never):Int; + var SQLITE_CHANGESET_ABORT(default, never):Int; + var SQLITE_CHANGESET_DATA(default, never):Int; + var SQLITE_CHANGESET_NOTFOUND(default, never):Int; + var SQLITE_CHANGESET_CONFLICT(default, never):Int; + var SQLITE_CHANGESET_CONSTRAINT(default, never):Int; + var SQLITE_CHANGESET_FOREIGN_KEY(default, never):Int; + + var SQLITE_CREATE_INDEX(default, never):Int; + var SQLITE_CREATE_TABLE(default, never):Int; + var SQLITE_CREATE_TEMP_INDEX(default, never):Int; + var SQLITE_CREATE_TEMP_TABLE(default, never):Int; + var SQLITE_CREATE_TEMP_TRIGGER(default, never):Int; + var SQLITE_CREATE_TEMP_VIEW(default, never):Int; + var SQLITE_CREATE_TRIGGER(default, never):Int; + var SQLITE_CREATE_VIEW(default, never):Int; + var SQLITE_DELETE(default, never):Int; + var SQLITE_DROP_INDEX(default, never):Int; + var SQLITE_DROP_TABLE(default, never):Int; + var SQLITE_DROP_TEMP_INDEX(default, never):Int; + var SQLITE_DROP_TEMP_TABLE(default, never):Int; + var SQLITE_DROP_TEMP_TRIGGER(default, never):Int; + var SQLITE_DROP_TEMP_VIEW(default, never):Int; + var SQLITE_DROP_TRIGGER(default, never):Int; + var SQLITE_DROP_VIEW(default, never):Int; + var SQLITE_INSERT(default, never):Int; + var SQLITE_PRAGMA(default, never):Int; + var SQLITE_READ(default, never):Int; + var SQLITE_SELECT(default, never):Int; + var SQLITE_TRANSACTION(default, never):Int; + var SQLITE_UPDATE(default, never):Int; + var SQLITE_ATTACH(default, never):Int; + var SQLITE_DETACH(default, never):Int; + var SQLITE_ALTER_TABLE(default, never):Int; + var SQLITE_REINDEX(default, never):Int; + var SQLITE_ANALYZE(default, never):Int; + var SQLITE_CREATE_VTABLE(default, never):Int; + var SQLITE_DROP_VTABLE(default, never):Int; + var SQLITE_FUNCTION(default, never):Int; + var SQLITE_SAVEPOINT(default, never):Int; + var SQLITE_COPY(default, never):Int; + var SQLITE_RECURSIVE(default, never):Int; +} diff --git a/src/js/node/Test.hx b/src/js/node/Test.hx index 0d410f4d..aae8e702 100644 --- a/src/js/node/Test.hx +++ b/src/js/node/Test.hx @@ -25,22 +25,16 @@ package js.node; import haxe.Constraints.Function; import haxe.DynamicAccess; import haxe.extern.EitherType; -import js.html.AbortSignal; +import js.node.web.AbortSignal; import js.node.test.ItFunction; import js.node.test.MockTracker; import js.node.test.SuiteContext; import js.node.test.SuiteFunction; import js.node.test.TestContext; import js.node.test.TestsStream; -#if haxe4 import js.lib.Error; import js.lib.Promise; import js.lib.RegExp; -#else -import js.Error; -import js.Promise; -import js.RegExp; -#end /** The `node:test` module facilitates the creation of JavaScript tests. diff --git a/src/js/node/TraceEvents.hx b/src/js/node/TraceEvents.hx new file mode 100644 index 00000000..ff54bfd3 --- /dev/null +++ b/src/js/node/TraceEvents.hx @@ -0,0 +1,60 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node; + +import js.node.trace_events.Tracing; + +/** + The `node:trace_events` module centralizes tracing information generated by + V8, Node.js core, and userspace code. + + Stability: 1 - Experimental. + + @see https://nodejs.org/docs/latest-v24.x/api/tracing.html +**/ +@:jsRequire("node:trace_events") +extern class TraceEvents { + /** + Creates and returns a `Tracing` object for the given set of `categories`. + + @see https://nodejs.org/docs/latest-v24.x/api/tracing.html#trace_eventscreatetracingoptions + **/ + static function createTracing(options:CreateTracingOptions):Tracing; + + /** + Returns a comma-separated list of all currently-enabled trace event categories. + + @see https://nodejs.org/docs/latest-v24.x/api/tracing.html#trace_eventsgetenabledcategories + **/ + static function getEnabledCategories():Null; +} + +/** + Options for `TraceEvents.createTracing`. +**/ +typedef CreateTracingOptions = { + /** + An array of trace category names. + **/ + var categories:Array; +} diff --git a/src/js/node/diagnostics_channel/Channel.hx b/src/js/node/diagnostics_channel/Channel.hx index e586268b..92e02907 100644 --- a/src/js/node/diagnostics_channel/Channel.hx +++ b/src/js/node/diagnostics_channel/Channel.hx @@ -25,18 +25,12 @@ package js.node.diagnostics_channel; import haxe.Constraints.Function; import haxe.extern.EitherType; import haxe.extern.Rest; -#if haxe4 import js.lib.Symbol; -#end /** Channel name: a string or symbol. **/ -#if haxe4 typedef ChannelName = EitherType; -#else -typedef ChannelName = EitherType; -#end /** Handler invoked when a message is published on a channel. @@ -119,6 +113,8 @@ extern class Channel { Stability: 1 - Experimental + // TODO(section-5): type `store` as AsyncLocalStorage once async_hooks externs land. + @see https://nodejs.org/docs/latest-v24.x/api/diagnostics_channel.html#channelbindstorestore-transform **/ function bindStore(store:Dynamic, ?transform:ChannelStoreTransform):Void; @@ -132,6 +128,8 @@ extern class Channel { Stability: 1 - Experimental + // TODO(section-5): type `store` as AsyncLocalStorage once async_hooks externs land. + @see https://nodejs.org/docs/latest-v24.x/api/diagnostics_channel.html#channelunbindstorestore **/ function unbindStore(store:Dynamic):Bool; diff --git a/src/js/node/diagnostics_channel/TracingChannel.hx b/src/js/node/diagnostics_channel/TracingChannel.hx index 7a8f64ff..fd9edb55 100644 --- a/src/js/node/diagnostics_channel/TracingChannel.hx +++ b/src/js/node/diagnostics_channel/TracingChannel.hx @@ -24,11 +24,7 @@ package js.node.diagnostics_channel; import haxe.Constraints.Function; import haxe.extern.Rest; -#if haxe4 import js.lib.Promise; -#else -import js.Promise; -#end /** Object containing all the TracingChannel Channels, passed to diff --git a/src/js/node/inspector/DomStorage.hx b/src/js/node/inspector/DomStorage.hx index f4f629f0..64e6ce89 100644 --- a/src/js/node/inspector/DomStorage.hx +++ b/src/js/node/inspector/DomStorage.hx @@ -57,7 +57,7 @@ extern class DomStorage { /** Registers storage for inspection. **/ - static function registerStorage(params:Dynamic):Void; + static function registerStorage(params:DomStorageRegisterParams):Void; } typedef DomStorageId = { @@ -87,3 +87,14 @@ typedef DomStorageItemUpdatedParams = { typedef DomStorageItemsClearedParams = { var storageId:DomStorageId; } + +/** + Parameters for `DomStorage.registerStorage`. + + `storageMap` is the storage backing object; shape follows the experimental + storage-inspection API (kept pragmatic). +**/ +typedef DomStorageRegisterParams = { + var isLocalStorage:Bool; + var storageMap:{}; +} diff --git a/src/js/node/inspector/Network.hx b/src/js/node/inspector/Network.hx index 08dfeafb..5617dd50 100644 --- a/src/js/node/inspector/Network.hx +++ b/src/js/node/inspector/Network.hx @@ -22,6 +22,8 @@ package js.node.inspector; +import haxe.DynamicAccess; + /** Broadcast helpers for Chrome DevTools Protocol `Network.*` events. @@ -44,7 +46,7 @@ extern class Network { /** Enables `Network.getRequestPostData` command to retrieve the request data. **/ - static function dataSent(?params:Dynamic):Void; + static function dataSent(?params:NetworkDataSentParams):Void; /** Broadcasts the `Network.requestWillBeSent` event to connected frontends. @@ -101,7 +103,7 @@ extern class Network { typedef NetworkRequest = { var url:String; var method:String; - @:optional var headers:Dynamic; + @:optional var headers:DynamicAccess; @:optional var postData:String; @:optional var hasPostData:Bool; } @@ -113,13 +115,15 @@ typedef NetworkResponse = { @:optional var url:String; @:optional var status:Int; @:optional var statusText:String; - @:optional var headers:Dynamic; + @:optional var headers:DynamicAccess; @:optional var mimeType:String; @:optional var charset:String; } /** Pragmatic subset of CDP request initiator. + + // TODO: model CDP `StackTrace` for `stack` instead of `Dynamic`. **/ typedef NetworkInitiator = { var type:String; @@ -185,6 +189,16 @@ typedef NetworkDataReceivedParams = { @:optional var data:String; } +/** + Parameters for `Network.dataSent`. +**/ +typedef NetworkDataSentParams = { + var requestId:String; + @:optional var timestamp:Float; + @:optional var dataLength:Int; + @:optional var data:String; +} + /** Parameters for `Network.webSocketCreated`. **/ @@ -209,7 +223,7 @@ typedef NetworkWebSocketHandshakeResponseReceivedParams = { typedef NetworkWebSocketResponse = { var status:Int; var statusText:String; - @:optional var headers:Dynamic; + @:optional var headers:DynamicAccess; } /** diff --git a/src/js/node/inspector/Session.hx b/src/js/node/inspector/Session.hx index f38c56de..739aea9b 100644 --- a/src/js/node/inspector/Session.hx +++ b/src/js/node/inspector/Session.hx @@ -23,11 +23,7 @@ package js.node.inspector; import js.node.events.EventEmitter; -#if haxe4 import js.lib.Error; -#else -import js.Error; -#end /** Enumeration of events emitted by `inspector.Session`. diff --git a/src/js/node/inspector/promises/Session.hx b/src/js/node/inspector/promises/Session.hx index 8a964044..c553442a 100644 --- a/src/js/node/inspector/promises/Session.hx +++ b/src/js/node/inspector/promises/Session.hx @@ -23,11 +23,7 @@ package js.node.inspector.promises; import js.node.events.EventEmitter; -#if haxe4 import js.lib.Promise; -#else -import js.Promise; -#end /** Promise-based `inspector.Session` from `inspector/promises`. diff --git a/src/js/node/perf_hooks/Histogram.hx b/src/js/node/perf_hooks/Histogram.hx index b5247d35..a67ab49c 100644 --- a/src/js/node/perf_hooks/Histogram.hx +++ b/src/js/node/perf_hooks/Histogram.hx @@ -37,6 +37,8 @@ extern class Histogram { /** The number of samples recorded by the histogram (as a BigInt). + + // TODO: type as BigInt when hxnodejs provides one. **/ var countBigInt(default, null):Dynamic; @@ -92,10 +94,12 @@ extern class Histogram { /** Returns a `Map` object detailing the accumulated percentile distribution. **/ - var percentiles(default, null):Dynamic; + var percentiles(default, null):js.lib.Map; /** Returns a `Map` object detailing the accumulated percentile distribution (BigInt keys/values). + + // TODO: type as Map when hxnodejs provides BigInt. **/ var percentilesBigInt(default, null):Dynamic; diff --git a/src/js/node/perf_hooks/RecordableHistogram.hx b/src/js/node/perf_hooks/RecordableHistogram.hx index 635eacc3..896034e9 100644 --- a/src/js/node/perf_hooks/RecordableHistogram.hx +++ b/src/js/node/perf_hooks/RecordableHistogram.hx @@ -37,6 +37,8 @@ extern class RecordableHistogram extends Histogram { /** Records `val` in the histogram. + + // TODO: allow BigInt when hxnodejs gains a BigInt type (Node accepts number | bigint). **/ function record(val:EitherType):Void; diff --git a/src/js/node/sqlite/DatabaseSync.hx b/src/js/node/sqlite/DatabaseSync.hx new file mode 100644 index 00000000..68e1746b --- /dev/null +++ b/src/js/node/sqlite/DatabaseSync.hx @@ -0,0 +1,244 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.sqlite; + +import haxe.Constraints.Function; +import haxe.extern.EitherType; +import js.lib.ArrayBuffer; +import js.lib.Uint8Array; +import js.node.Sqlite.SqlitePath; +import js.node.sqlite.SQLTagStore; +import js.node.sqlite.Session as SqliteSession; +import js.node.sqlite.StatementSync; + +/** + A single synchronous connection to a SQLite database. + + @see https://nodejs.org/docs/latest-v24.x/api/sqlite.html#class-databasesync +**/ +@:jsRequire("node:sqlite", "DatabaseSync") +extern class DatabaseSync { + /** + Constructs a new database connection. + **/ + function new(path:SqlitePath, ?options:DatabaseSyncOptions); + + /** + Whether the database connection is open. + **/ + var isOpen(default, null):Bool; + + /** + Whether a transaction is currently active. + **/ + var isTransaction(default, null):Bool; + + /** + Configured SQLite run-time limits for this connection. + **/ + var limits(default, null):DatabaseSyncLimits; + + /** + Registers an aggregate function. + + @see https://nodejs.org/docs/latest-v24.x/api/sqlite.html#databaseaggregatename-options + **/ + function aggregate(name:String, options:SqliteAggregateOptions):Void; + + /** + Applies a binary changeset or patchset. + + @see https://nodejs.org/docs/latest-v24.x/api/sqlite.html#databaseapplychangesetchangeset-options + **/ + function applyChangeset(changeset:EitherType, ?options:ApplyChangesetOptions):Bool; + + /** + Closes the database connection. + **/ + function close():Void; + + /** + Creates and attaches a session used to track changes. + + @see https://nodejs.org/docs/latest-v24.x/api/sqlite.html#databasecreatesessionoptions + **/ + function createSession(?options:CreateSessionOptions):SqliteSession; + + /** + Creates an LRU cache of prepared statements (SQL tag store). + + Added in: v24.9.0 + + @see https://nodejs.org/docs/latest-v24.x/api/sqlite.html#databasecreatetagstoremaxsize + **/ + function createTagStore(?maxSize:Int):SQLTagStore; + + /** + Replace database contents with a serialized buffer. + + @see https://nodejs.org/docs/latest-v24.x/api/sqlite.html#databasedeserializebuffer-options + **/ + function deserialize(buffer:EitherType, ?options:DatabaseSerializeOptions):Void; + + /** + Enable or disable defensive mode. + + @see https://nodejs.org/docs/latest-v24.x/api/sqlite.html#databaseenabledefensiveenabled + **/ + function enableDefensive(enabled:Bool):Void; + + /** + Enable or disable the `loadExtension` SQL function / method. + + @see https://nodejs.org/docs/latest-v24.x/api/sqlite.html#databaseenableloadextensionenabled + **/ + function enableLoadExtension(enabled:Bool):Void; + + /** + Execute one or more SQL statements without returning results. + **/ + function exec(sql:String):Void; + + /** + Registers a SQL function callable from SQL. + + @see https://nodejs.org/docs/latest-v24.x/api/sqlite.html#databasefunctionname-options-func + **/ + @:native("function") + @:overload(function(name:String, fn:Function):Void {}) + function function_(name:String, options:SqliteFunctionOptions, fn:Function):Void; + + /** + Loads a shared library into the connection. + **/ + function loadExtension(path:String, ?entryPoint:String):Void; + + /** + Location of the database file, or `null` for in-memory databases. + **/ + function location(?dbName:String):Null; + + /** + Opens the database when constructed with `{ open: false }`. + **/ + function open():Void; + + /** + Creates a new prepared statement. + **/ + function prepare(sql:String):StatementSync; + + /** + Serialize the database. Returns a `Uint8Array`. + + @see https://nodejs.org/docs/latest-v24.x/api/sqlite.html#databaseserializedbname + **/ + function serialize(?dbName:String):Uint8Array; + + /** + Sets the authorizer callback used during statement compilation. + **/ + function setAuthorizer(?callback:NullNull->Null->Null->Null->Int>):Void; +} + +/** + Options for `new DatabaseSync`. +**/ +typedef DatabaseSyncOptions = { + @:optional var open:Bool; + @:optional var readOnly:Bool; + @:optional var enableForeignKeyConstraints:Bool; + @:optional var enableDoubleQuotedStringLiterals:Bool; + @:optional var allowExtension:Bool; + @:optional var timeout:Float; + @:optional var readBigInts:Bool; + @:optional var returnArrays:Bool; + @:optional var allowBareNamedParameters:Bool; + @:optional var allowUnknownNamedParameters:Bool; + @:optional var defensive:Bool; + @:optional var limits:DatabaseSyncLimits; +} + +/** + SQLite run-time limits for `DatabaseSyncOptions.limits`. +**/ +typedef DatabaseSyncLimits = { + @:optional var length:Float; + @:optional var sqlLength:Float; + @:optional var column:Float; + @:optional var exprDepth:Float; + @:optional var compoundSelect:Float; + @:optional var vdbeOp:Float; + @:optional var functionArg:Float; + @:optional var attach:Float; + @:optional var likePatternLength:Float; + @:optional var variableNumber:Float; + @:optional var triggerDepth:Float; +} + +/** + Options for `DatabaseSync.aggregate`. +**/ +typedef SqliteAggregateOptions = { + @:optional var deterministic:Bool; + @:optional var directOnly:Bool; + @:optional var useBigIntArguments:Bool; + @:optional var varargs:Bool; + @:optional var start:Dynamic; + var step:Function; + @:optional var result:Function; + @:optional var inverse:Function; +} + +/** + Options for `DatabaseSync.function_`. +**/ +typedef SqliteFunctionOptions = { + @:optional var deterministic:Bool; + @:optional var directOnly:Bool; + @:optional var useBigIntArguments:Bool; + @:optional var varargs:Bool; +} + +/** + Options for `DatabaseSync.createSession`. +**/ +typedef CreateSessionOptions = { + @:optional var table:String; + @:optional var db:String; +} + +/** + Options for `DatabaseSync.applyChangeset`. +**/ +typedef ApplyChangesetOptions = { + @:optional var filter:String->Bool; + @:optional var onConflict:Int->Int; +} + +/** + Options for `deserialize`. +**/ +typedef DatabaseSerializeOptions = { + @:optional var readOnly:Bool; +} diff --git a/src/js/node/sqlite/SQLTagStore.hx b/src/js/node/sqlite/SQLTagStore.hx new file mode 100644 index 00000000..b44b8e97 --- /dev/null +++ b/src/js/node/sqlite/SQLTagStore.hx @@ -0,0 +1,79 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.sqlite; + +import haxe.extern.Rest; +import js.node.Iterator; +import js.node.sqlite.StatementSync.StatementResult; + +/** + LRU cache of prepared statements created via `DatabaseSync.createTagStore()`. + + Tagged-template call sites pass a `string[]` of template elements followed by + bound values (Node's template-tag convention). + + Added in: v24.9.0 + + @see https://nodejs.org/docs/latest-v24.x/api/sqlite.html#class-sqltagstore +**/ +extern class SQLTagStore { + /** + Number of prepared statements currently in the cache. + **/ + var size(default, null):Int; + + /** + Maximum number of prepared statements the cache can hold. + **/ + var capacity(default, null):Int; + + /** + The `DatabaseSync` associated with this store. + **/ + var db(default, null):DatabaseSync; + + /** + Execute query and return all rows (template-tag style). + **/ + function all(strings:Array, values:Rest):Array; + + /** + Execute query and return the first row (template-tag style). + **/ + function get(strings:Array, values:Rest):Null; + + /** + Execute query and iterate rows (template-tag style). + **/ + function iterate(strings:Array, values:Rest):Iterator; + + /** + Execute a mutating statement (template-tag style). + **/ + function run(strings:Array, values:Rest):StatementResult; + + /** + Clear all cached prepared statements. + **/ + function clear():Void; +} diff --git a/src/js/node/sqlite/Session.hx b/src/js/node/sqlite/Session.hx new file mode 100644 index 00000000..2fbc2049 --- /dev/null +++ b/src/js/node/sqlite/Session.hx @@ -0,0 +1,50 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.sqlite; + +import js.lib.Uint8Array; + +/** + Tracks changes for later changeset / patchset application. + + Created via `DatabaseSync.createSession()`. + + @see https://nodejs.org/docs/latest-v24.x/api/sqlite.html#class-session +**/ +@:jsRequire("node:sqlite", "Session") +extern class Session { + /** + Binary changeset of all tracked changes. + **/ + function changeset():Uint8Array; + + /** + More compact binary patchset of tracked changes. + **/ + function patchset():Uint8Array; + + /** + Closes the session. + **/ + function close():Void; +} diff --git a/src/js/node/sqlite/StatementSync.hx b/src/js/node/sqlite/StatementSync.hx new file mode 100644 index 00000000..f06ab9c0 --- /dev/null +++ b/src/js/node/sqlite/StatementSync.hx @@ -0,0 +1,122 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.sqlite; + +import haxe.extern.Rest; +import js.node.Iterator; + +/** + A prepared statement created via `DatabaseSync.prepare()`. + + @see https://nodejs.org/docs/latest-v24.x/api/sqlite.html#class-statementsync +**/ +@:jsRequire("node:sqlite", "StatementSync") +extern class StatementSync { + /** + Source SQL with parameter placeholders replaced by the most recent bound values. + **/ + var expandedSQL(default, null):String; + + /** + Source SQL used to create this prepared statement. + **/ + var sourceSQL(default, null):String; + + /** + Execute and return all result rows. + + Accepts an optional named-parameters object followed by anonymous bind values + (Node's `all([namedParameters][, ...anonymousParameters])`). + **/ + function all(args:Rest):Array; + + /** + Column metadata for the prepared statement result set. + **/ + function columns():Array; + + /** + Execute and return the first result row, or `undefined`. + **/ + function get(args:Rest):Null; + + /** + Execute and return an iterator of result rows. + **/ + function iterate(args:Rest):Iterator; + + /** + Execute a mutating statement and return change summary. + **/ + function run(args:Rest):StatementResult; + + /** + Allow binding named parameters without the SQL prefix character. + **/ + function setAllowBareNamedParameters(enabled:Bool):Void; + + /** + Ignore unknown named parameters when binding. + **/ + function setAllowUnknownNamedParameters(enabled:Bool):Void; + + /** + When enabled, `all` / `get` / `iterate` return rows as arrays. + **/ + function setReturnArrays(enabled:Bool):Void; + + /** + When enabled, `INTEGER` columns are read as JavaScript BigInt values. + + // TODO: replace Dynamic BigInt usages with a proper BigInt type when hxnodejs provides one. + **/ + function setReadBigInts(enabled:Bool):Void; +} + +/** + Result of `StatementSync.run` / `SQLTagStore.run`. +**/ +typedef StatementResult = { + /** + Rows modified. Number or BigInt depending on statement configuration. + + // TODO: tighten with BigInt when available. + **/ + var changes:Dynamic; + + /** + Most recently inserted rowid. Number or BigInt depending on configuration. + **/ + var lastInsertRowid:Dynamic; +} + +/** + Column metadata from `StatementSync.columns`. +**/ +typedef StatementColumnInfo = { + var name:String; + var column:Null; + var database:Null; + var table:Null; + var type:Null; +} diff --git a/src/js/node/test/ItFunction.hx b/src/js/node/test/ItFunction.hx index ba3fb647..074badbe 100644 --- a/src/js/node/test/ItFunction.hx +++ b/src/js/node/test/ItFunction.hx @@ -24,11 +24,7 @@ package js.node.test; import js.node.Test.TestCallback; import js.node.Test.TestOptions; -#if haxe4 import js.lib.Promise; -#else -import js.Promise; -#end /** Callable test entry used by `it`, with suite-style shorthands plus `expectFailure`. diff --git a/src/js/node/test/MockTimers.hx b/src/js/node/test/MockTimers.hx index 98909758..70036f50 100644 --- a/src/js/node/test/MockTimers.hx +++ b/src/js/node/test/MockTimers.hx @@ -23,11 +23,7 @@ package js.node.test; import haxe.extern.EitherType; -#if haxe4 import js.lib.Date as JsDate; -#else -import js.Date as JsDate; -#end /** Timer / Date APIs that can be mocked via `MockTimers.enable()`. diff --git a/src/js/node/test/MockTracker.hx b/src/js/node/test/MockTracker.hx index c61492c2..43eb679d 100644 --- a/src/js/node/test/MockTracker.hx +++ b/src/js/node/test/MockTracker.hx @@ -24,12 +24,8 @@ package js.node.test; import haxe.Constraints.Function; import haxe.extern.EitherType; -#if haxe4 import js.lib.Error; import js.lib.Symbol; -#else -import js.Error; -#end /** Manages mocking functionality. @@ -54,26 +50,16 @@ extern class MockTracker { @see https://nodejs.org/docs/latest-v24.x/api/test.html#mockgetterobject-methodname-implementation-options **/ - #if haxe4 @:overload(function(object:{}, methodName:EitherType, ?options:MockMethodOptions):MockedFunction {}) function getter(object:{}, methodName:EitherType, ?implementation:Function, ?options:MockMethodOptions):MockedFunction; - #else - @:overload(function(object:{}, methodName:String, ?options:MockMethodOptions):MockedFunction {}) - function getter(object:{}, methodName:String, ?implementation:Function, ?options:MockMethodOptions):MockedFunction; - #end /** Create a mock on an existing object method. @see https://nodejs.org/docs/latest-v24.x/api/test.html#mockmethodobject-methodname-implementation-options **/ - #if haxe4 @:overload(function(object:{}, methodName:EitherType, ?options:MockMethodOptions):MockedFunction {}) function method(object:{}, methodName:EitherType, ?implementation:Function, ?options:MockMethodOptions):MockedFunction; - #else - @:overload(function(object:{}, methodName:String, ?options:MockMethodOptions):MockedFunction {}) - function method(object:{}, methodName:String, ?implementation:Function, ?options:MockMethodOptions):MockedFunction; - #end /** Mock exports of an ESM, CommonJS, JSON, or builtin module. @@ -91,11 +77,7 @@ extern class MockTracker { @see https://nodejs.org/docs/latest-v24.x/api/test.html#mockpropertyobject-propertyname-value **/ - #if haxe4 function property(object:{}, propertyName:EitherType, ?value:Dynamic):MockedProperty; - #else - function property(object:{}, propertyName:String, ?value:Dynamic):MockedProperty; - #end /** Restore default behavior of all mocks created by this tracker and @@ -117,13 +99,8 @@ extern class MockTracker { @see https://nodejs.org/docs/latest-v24.x/api/test.html#mocksetterobject-methodname-implementation-options **/ - #if haxe4 @:overload(function(object:{}, methodName:EitherType, ?options:MockMethodOptions):MockedFunction {}) function setter(object:{}, methodName:EitherType, ?implementation:Function, ?options:MockMethodOptions):MockedFunction; - #else - @:overload(function(object:{}, methodName:String, ?options:MockMethodOptions):MockedFunction {}) - function setter(object:{}, methodName:String, ?implementation:Function, ?options:MockMethodOptions):MockedFunction; - #end /** Mock timers APIs (`setTimeout`, `setInterval`, `setImmediate`, `Date`, …). diff --git a/src/js/node/test/SuiteContext.hx b/src/js/node/test/SuiteContext.hx index 01397549..1ddd5029 100644 --- a/src/js/node/test/SuiteContext.hx +++ b/src/js/node/test/SuiteContext.hx @@ -22,7 +22,7 @@ package js.node.test; -import js.html.AbortSignal; +import js.node.web.AbortSignal; /** Passed to each suite function to interact with the test runner. diff --git a/src/js/node/test/SuiteFunction.hx b/src/js/node/test/SuiteFunction.hx index c0f22097..24921172 100644 --- a/src/js/node/test/SuiteFunction.hx +++ b/src/js/node/test/SuiteFunction.hx @@ -24,11 +24,7 @@ package js.node.test; import js.node.Test.SuiteCallback; import js.node.Test.TestOptions; -#if haxe4 import js.lib.Promise; -#else -import js.Promise; -#end /** Callable suite entry (`describe` / `suite`) with `.skip`, `.todo`, `.only`, diff --git a/src/js/node/test/TestContext.hx b/src/js/node/test/TestContext.hx index e9aa6503..42d4bbb9 100644 --- a/src/js/node/test/TestContext.hx +++ b/src/js/node/test/TestContext.hx @@ -23,7 +23,7 @@ package js.node.test; import haxe.extern.EitherType; -import js.html.AbortSignal; +import js.node.web.AbortSignal; import js.node.Test.HookCallback; import js.node.Test.HookOptions; import js.node.Test.PlanOptions; @@ -31,13 +31,8 @@ import js.node.Test.SnapshotAssertionOptions; import js.node.Test.TestCallback; import js.node.Test.TestOptions; import js.node.Test.WaitForOptions; -#if haxe4 import js.lib.Error; import js.lib.Promise; -#else -import js.Error; -import js.Promise; -#end /** Passed to each test function to interact with the test runner. @@ -220,6 +215,8 @@ extern class TestContext { usual `node:assert` methods bound to this context (see `js.node.Assert`); those are intentionally not fully re-declared. + // TODO(section-4): compose with `js.node.Assert` method signatures instead of `implements Dynamic`. + @see https://nodejs.org/docs/latest-v24.x/api/test.html#contextassert **/ extern class TestContextAssert implements Dynamic { diff --git a/src/js/node/test/TestsStream.hx b/src/js/node/test/TestsStream.hx index 5906c106..86f6b846 100644 --- a/src/js/node/test/TestsStream.hx +++ b/src/js/node/test/TestsStream.hx @@ -26,11 +26,7 @@ import haxe.Constraints.Function; import haxe.extern.EitherType; import js.node.events.EventEmitter.Event; import js.node.stream.Readable; -#if haxe4 import js.lib.Error; -#else -import js.Error; -#end /** Events emitted by `TestsStream`. diff --git a/src/js/node/trace_events/Tracing.hx b/src/js/node/trace_events/Tracing.hx new file mode 100644 index 00000000..cf09dde9 --- /dev/null +++ b/src/js/node/trace_events/Tracing.hx @@ -0,0 +1,51 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.trace_events; + +/** + Enables or disables tracing for a set of categories. + Created via `TraceEvents.createTracing()`. + + @see https://nodejs.org/docs/latest-v24.x/api/tracing.html#tracing-object +**/ +extern class Tracing { + /** + A comma-separated list of the trace event categories covered by this object. + **/ + var categories(default, null):String; + + /** + `true` only if this `Tracing` object has been enabled. + **/ + var enabled(default, null):Bool; + + /** + Enables this `Tracing` object for its set of categories. + **/ + function enable():Void; + + /** + Disables this `Tracing` object. + **/ + function disable():Void; +} From 13aabddeb6c57048ced355083b3ad77cc4058d75 Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Tue, 14 Jul 2026 16:42:02 +1000 Subject: [PATCH 35/48] Align networking externs with Node 24 and drop Haxe 3. (#226) Fill net/http/https/http2/tls/dgram/dns/tty API gaps against Node 24, replace Haxe 3 conditionals with Haxe 4 types, and tighten Dynamics where practical. Co-authored-by: Cursor --- src/js/node/Dns.hx | 13 +-- src/js/node/DnsPromises.hx | 4 - src/js/node/Http.hx | 164 +++++++++++++++++++++++++--- src/js/node/Http2.hx | 15 +-- src/js/node/Https.hx | 6 - src/js/node/Net.hx | 25 +++++ src/js/node/Tls.hx | 80 +++++++++++++- src/js/node/dgram/Socket.hx | 125 ++++++++++++++++++++- src/js/node/dns/PromisesResolver.hx | 4 - src/js/node/http/Agent.hx | 24 ++-- src/js/node/http/ClientRequest.hx | 79 ++++++++++++-- src/js/node/http/IncomingMessage.hx | 14 ++- src/js/node/http/OutgoingMessage.hx | 119 ++++++++++++++++++++ src/js/node/http/Server.hx | 64 ++++++----- src/js/node/http/ServerResponse.hx | 47 ++++++++ src/js/node/http2/Http2Stream.hx | 4 +- src/js/node/net/BlockList.hx | 2 +- src/js/node/net/Server.hx | 21 +++- src/js/node/net/Socket.hx | 160 ++++++++++++++++++++++++++- src/js/node/tls/SecureContext.hx | 30 +++++ src/js/node/tls/Server.hx | 31 ++++-- src/js/node/tls/TLSSocket.hx | 103 +++++++++++++++-- src/js/node/tty/ReadStream.hx | 4 +- src/js/node/tty/WriteStream.hx | 56 ++++++++++ 24 files changed, 1059 insertions(+), 135 deletions(-) create mode 100644 src/js/node/http/OutgoingMessage.hx diff --git a/src/js/node/Dns.hx b/src/js/node/Dns.hx index 2ef67edd..24fc1ba6 100644 --- a/src/js/node/Dns.hx +++ b/src/js/node/Dns.hx @@ -24,13 +24,8 @@ package js.node; import haxe.extern.EitherType; import js.node.dns.Resolver as ResolverObject; -#if haxe4 import js.lib.ArrayBuffer; import js.lib.Error; -#else -import js.html.ArrayBuffer; -import js.Error; -#end /** Enumeration of possible Int `options` values for `Dns.lookup`. @@ -445,11 +440,11 @@ extern enum abstract DnsErrorCode(String) { var CANCELLED; } -typedef DnsLookupCallbackSingle = #if (haxe_ver >= 4) (err:DnsError, address:String, - family:DnsAddressFamily) -> Void; #else DnsError->String->DnsAddressFamily->Void #end +typedef DnsLookupCallbackSingle = (err:DnsError, address:String, + family:DnsAddressFamily) -> Void; -typedef DnsLookupCallbackAll = #if (haxe_ver >= 4) (err:DnsError, - addresses:Array) -> Void; #else DnsError->Array->Void; #end +typedef DnsLookupCallbackAll = (err:DnsError, + addresses:Array) -> Void; typedef DnsLookupCallbackAllEntry = {address:String, family:DnsAddressFamily}; diff --git a/src/js/node/DnsPromises.hx b/src/js/node/DnsPromises.hx index 6a3de20b..0d038dd0 100644 --- a/src/js/node/DnsPromises.hx +++ b/src/js/node/DnsPromises.hx @@ -25,11 +25,7 @@ package js.node; import haxe.extern.EitherType; import js.node.Dns; import js.node.dns.PromisesResolver as PromisesResolverObject; -#if haxe4 import js.lib.Promise; -#else -import js.Promise; -#end /** The `dns/promises` API provides an alternative set of asynchronous DNS methods diff --git a/src/js/node/Http.hx b/src/js/node/Http.hx index f2530bfb..ebcbab18 100644 --- a/src/js/node/Http.hx +++ b/src/js/node/Http.hx @@ -28,11 +28,8 @@ import js.node.http.*; import js.node.net.Socket; import js.node.stream.Duplex; import js.node.url.URL; -#if haxe4 +import js.node.web.AbortSignal; import js.lib.Error; -#else -import js.Error; -#end /** The HTTP interfaces in Node are designed to support many features of the protocol @@ -67,13 +64,8 @@ extern class Http { The `requestListener` is a function which is automatically added to the `'request'` event. **/ - #if haxe4 @:overload(function(options:HttpCreateServerOptions, ?requestListener:(request:IncomingMessage, response:ServerResponse) -> Void):Server {}) static function createServer(?requestListener:(request:IncomingMessage, response:ServerResponse) -> Void):Server; - #else - @:overload(function(options:HttpCreateServerOptions, ?requestListener:IncomingMessage->ServerResponse->Void):Server {}) - static function createServer(?requestListener:IncomingMessage->ServerResponse->Void):Server; - #end /** Since most requests are GET requests without bodies, Node.js provides this convenience method. @@ -110,6 +102,50 @@ extern class Http { @:overload(function(url:URL, ?options:HttpRequestOptions, ?callback:IncomingMessage->Void):ClientRequest {}) @:overload(function(url:String, ?options:HttpRequestOptions, ?callback:IncomingMessage->Void):ClientRequest {}) static function request(options:HttpRequestOptions, ?callback:IncomingMessage->Void):ClientRequest; + + /** + Performs the low-level validations on the provided `name` that are done when `res.setHeader(name, value)` is called. + @throws `TypeError` if the `name` is invalid. + **/ + static function validateHeaderName(name:String, ?label:String):Void; + + /** + Performs the low-level validations on the provided `value` that are done when `res.setHeader(name, value)` is called. + @throws `TypeError` if the `value` is invalid. + **/ + static function validateHeaderValue(name:String, value:EitherType>):Void; + + /** + Set the maximum number of idle HTTP parsers that can stay around for reuse. + Default: `1000`. + **/ + static function setMaxIdleHTTPParsers(max:Int):Void; + + /** + Undici `WebSocket` constructor exposed via `node:http`. + + // TODO(section-6): prefer `js.node.web` WebSocket typing once section 6 lands + **/ + static var WebSocket:Class; + + /** + Parent class of `http.ClientRequest` and `http.ServerResponse`. + + @see https://nodejs.org/docs/latest-v24.x/api/http.html#class-httpoutgoingmessage + **/ + static var OutgoingMessage:Class; + + /** + Dynamically resets the global configurations to enable built-in proxy support for + `fetch()` and `http.request()` / `https.request()` at runtime. + + Returns a restore function for the previous agent/dispatcher settings. + + Added in: v24.14.0. + + @see https://nodejs.org/docs/latest-v24.x/api/http.html#httpsetglobalproxyfromenvproxyenv + **/ + static function setGlobalProxyFromEnv(?proxyEnv:DynamicAccess):() -> Void; } typedef HttpCreateServerOptions = { @@ -118,14 +154,81 @@ typedef HttpCreateServerOptions = { Default: `js.node.http.IncomingMessage`. **/ - @:optional var IncomingMessage:Class; + @:optional var IncomingMessage:Class; /** Specifies the `ServerResponse` class to be used. Useful for extending the original `ServerResponse`. Default: `ServerResponse`. **/ - @:optional var ServerResponse:Class; + @:optional var ServerResponse:Class; + + /** + Sets the timeout value in milliseconds for receiving the entire request from the client. + See `server.requestTimeout`. + **/ + @:optional var requestTimeout:Int; + + /** + Sets the timeout value in milliseconds for receiving the complete HTTP headers from the client. + See `server.headersTimeout`. + **/ + @:optional var headersTimeout:Int; + + /** + The number of milliseconds of inactivity a server needs to wait for additional incoming data, + after it has finished writing the last response, before a socket will be destroyed. + **/ + @:optional var keepAliveTimeout:Int; + + /** + Additional milliseconds of inactivity to allow before destroying a keep-alive socket + after `keepAliveTimeout` elapses. Default: `1000`. + **/ + @:optional var keepAliveTimeoutBuffer:Int; + + /** + Sets the interval value in milliseconds to check for request and headers timeout in incomplete requests. + **/ + @:optional var connectionsCheckingInterval:Int; + + /** + Sets the maximum number of requests that may be made on a single socket before it is closed. + Default: `0` (no limit). + **/ + @:optional var maxRequestsPerSocket:Int; + + /** + Whether or not to enable keep-alive on incoming connections. + **/ + @:optional var keepAlive:Bool; + + /** + Initial delay for keep-alive in milliseconds. + **/ + @:optional var keepAliveInitialDelay:Int; + + /** + Optionally overrides all `net.Socket`s' `readableHighWaterMark` and `writableHighWaterMark`. + **/ + @:optional var highWaterMark:Int; + + /** + If set to `true`, it disables the use of Nagle's algorithm immediately after a new incoming connection is received. + **/ + @:optional var noDelay:Bool; + + /** + Sets the maximum number of unique header names allowed per request. Default: `2000`. + **/ + @:optional var joinDuplicateHeaders:Bool; + + /** + `uniqueHeaders` is an optional configuration specific to HTTP servers. + + @see https://nodejs.org/api/http.html#httpcreateserveroptions-requestlistener + **/ + @:optional var uniqueHeaders:Array>>; } /** @@ -154,11 +257,7 @@ typedef HttpRequestOptions = { See [agent.createConnection()](https://nodejs.org/api/http.html#http_agent_createconnection_options_callback) for more details. Any `Duplex` stream is a valid return value. **/ - #if haxe4 @:optional var createConnection:(options:SocketConnectOptionsTcp, ?callabck:(err:Error, stream:IDuplex) -> Void) -> IDuplex; - #else - @:optional var createConnection:SocketConnectOptionsTcp->?(Error->IDuplex->Void)->IDuplex; - #end /** Default port for the protocol. @@ -242,4 +341,39 @@ typedef HttpRequestOptions = { This will set the timeout before the socket is connected. **/ @:optional var timeout:Int; + + /** + Local port to bind to for network connections. + **/ + @:optional var localPort:Int; + + /** + Optional dns.lookup() hints. + **/ + @:optional var hints:Int; + + /** + An AbortSignal that may be used to abort an ongoing request. + **/ + @:optional var signal:AbortSignal; + + /** + Headers names which values are treated as arrays of values. + **/ + @:optional var uniqueHeaders:Array>>; + + /** + Join duplicate headers into a comma-separated string for select headers. + **/ + @:optional var joinDuplicateHeaders:Bool; + + /** + Optionally overrides the value of `--max-http-header-size` for request parsing. + **/ + @:optional var maxHeaderSize:Int; + + /** + If `true`, use an insecure HTTP parser that accepts invalid HTTP headers. + **/ + @:optional var insecureHTTPParser:Bool; } diff --git a/src/js/node/Http2.hx b/src/js/node/Http2.hx index 5b763fb1..8b99f265 100644 --- a/src/js/node/Http2.hx +++ b/src/js/node/Http2.hx @@ -34,6 +34,7 @@ import js.node.http2.ServerHttp2Session; import js.node.stream.Duplex; import js.node.tls.SecureContext.SecureContextOptions; import js.node.url.URL; +import js.node.web.AbortSignal; import js.lib.Symbol; /** @@ -201,8 +202,8 @@ typedef Http2SecureClientSessionOptions = { Options for HTTP/1 fallback classes used by HTTP/2 servers. **/ typedef Http2Http1Options = { - @:optional var IncomingMessage:Class; - @:optional var ServerResponse:Class; + @:optional var IncomingMessage:Class; + @:optional var ServerResponse:Class; @:optional var keepAliveTimeout:Int; } @@ -222,14 +223,14 @@ typedef Http2ServerOptions = { See DEP0202. **/ @:deprecated("Use http1Options.IncomingMessage instead") - @:optional var Http1IncomingMessage:Class; + @:optional var Http1IncomingMessage:Class; /** Deprecated. Use `http1Options.ServerResponse` instead. See DEP0202. **/ @:deprecated("Use http1Options.ServerResponse instead") - @:optional var Http1ServerResponse:Class; + @:optional var Http1ServerResponse:Class; /** Options for configuring the HTTP/1 fallback when `allowHTTP1` is `true`. @@ -237,8 +238,8 @@ typedef Http2ServerOptions = { **/ @:optional var http1Options:Http2Http1Options; - @:optional var Http2ServerRequest:Class; - @:optional var Http2ServerResponse:Class; + @:optional var Http2ServerRequest:Class; + @:optional var Http2ServerResponse:Class; /** If `true`, strict validation is used for headers and trailers defined as @@ -276,7 +277,7 @@ typedef Http2ClientSessionRequestOptions = { @:optional var exclusive:Bool; @:optional var parent:Int; @:optional var waitForTrailers:Bool; - @:optional var signal:js.html.AbortSignal; + @:optional var signal:AbortSignal; } /** diff --git a/src/js/node/Https.hx b/src/js/node/Https.hx index 62af3384..c321f257 100644 --- a/src/js/node/Https.hx +++ b/src/js/node/Https.hx @@ -37,13 +37,8 @@ extern class Https { /** Returns a new HTTPS web server object. **/ - #if haxe4 @:overload(function(options:HttpsCreateServerOptions, ?requestListener:(request:IncomingMessage, response:ServerResponse) -> Void):Server {}) static function createServer(?requestListener:(request:IncomingMessage, response:ServerResponse) -> Void):Server; - #else - @:overload(function(options:HttpsCreateServerOptions, ?requestListener:IncomingMessage->ServerResponse->Void):Server {}) - static function createServer(?requestListener:IncomingMessage->ServerResponse->Void):Server; - #end /** Like `Http.get` but for HTTPS. @@ -90,5 +85,4 @@ typedef HttpsCreateServerOptions = { typedef HttpsRequestOptions = { > js.node.Http.HttpRequestOptions, > js.node.Tls.TlsConnectOptions, - // TODO: clean those options up } diff --git a/src/js/node/Net.hx b/src/js/node/Net.hx index 9ceeca67..424677f4 100644 --- a/src/js/node/Net.hx +++ b/src/js/node/Net.hx @@ -41,6 +41,31 @@ typedef NetCreateServerOptions = { Default: false **/ @:optional var pauseOnConnect:Bool; + + /** + If set to `true`, it enables keep-alive functionality on the socket immediately after the connection is established. + **/ + @:optional var keepAlive:Bool; + + /** + If set to a positive number, it sets the initial delay before the first keepalive probe is sent on an idle socket. + **/ + @:optional var keepAliveInitialDelay:Int; + + /** + If set to `true`, it disables the use of Nagle's algorithm immediately after a connection is established. + **/ + @:optional var noDelay:Bool; + + /** + Optionally overrides all `net.Socket`s' `readableHighWaterMark` and `writableHighWaterMark`. + **/ + @:optional var highWaterMark:Int; + + /** + `net.BlockList` used as an IP allow/deny list for incoming connections. + **/ + @:optional var blockList:BlockListObject; } /** diff --git a/src/js/node/Tls.hx b/src/js/node/Tls.hx index 911ae9a3..b3f85597 100644 --- a/src/js/node/Tls.hx +++ b/src/js/node/Tls.hx @@ -22,17 +22,14 @@ package js.node; +import haxe.DynamicAccess; import haxe.extern.EitherType; import js.node.Buffer; import js.node.tls.SecureContext; import js.node.tls.SecurePair; import js.node.tls.Server; import js.node.tls.TLSSocket; -#if haxe4 import js.lib.Error; -#else -import js.Error; -#end typedef TlsOptionsBase = { /** @@ -44,8 +41,17 @@ typedef TlsOptionsBase = { /** possible NPN protocols. (Protocols should be ordered by their priority). + + @deprecated Use `ALPNProtocols` instead. **/ + @:deprecated("Use ALPNProtocols instead") @:optional var NPNProtocols:EitherType, Buffer>; + + /** + An array of strings or a Buffer naming possible ALPN protocols. + (Protocols should be ordered by their priority.) + **/ + @:optional var ALPNProtocols:EitherType>, Buffer>; } typedef TlsServerOptionsBase = { @@ -65,7 +71,7 @@ typedef TlsServerOptionsBase = { (You can use tls.createSecureContext(...) to get proper `SecureContext`). If `SNICallback` wasn't provided - default callback with high-level API will be used. **/ - @:optional var SNICallback:#if (haxe_ver >= 4)(servername:String, cb:(Error->SecureContext)) -> Void #else String->(Error->SecureContext)->Void #end; + @:optional var SNICallback:(servername:String, cb:(Null, SecureContext) -> Void) -> Void; } typedef TlsClientOptionsBase = { @@ -111,6 +117,21 @@ typedef TlsCreateServerOptions = { NOTE: Automatically shared between cluster module workers. **/ @:optional var ticketKeys:Buffer; + + /** + If true, `tls.TLSSocket.enableTrace()` is called on new connections. + + @see https://nodejs.org/api/tls.html#tlscreateserveroptions-secureconnectionlistener + **/ + @:optional var enableTrace:Bool; + + /** + If set, called when a client opens a connection using the ALPN extension. + Return one of the client's protocols, or `undefined` to reject. + + @see https://nodejs.org/api/tls.html#tlscreateserveroptions-secureconnectionlistener + **/ + @:optional var ALPNCallback:(arg:{servername:String, protocols:Array}) -> Null; } typedef TlsConnectOptions = { @@ -149,8 +170,39 @@ typedef TlsConnectOptions = { An override for checking server's hostname against the certificate. Should return an error if verification fails. Return `js.Lib.undefined` if passing. **/ - @:optional var checkServerIdentity:String -> {}->Dynamic; // TODO: peer cerficicate structure + @:optional var checkServerIdentity:(servername:String, cert:PeerCertificate) -> Null; +} +/** + Common fields of the certificate object returned by `TLSSocket.getPeerCertificate`. + + // TODO(section-3): expand full PeerCertificate / DetailedPeerCertificate field set from OpenSSL +**/ +typedef PeerCertificate = { + @:optional var subject:CertificateSubject; + @:optional var issuer:CertificateSubject; + @:optional var subjectaltname:String; + @:optional var infoAccess:haxe.DynamicAccess>; + @:optional var modulus:String; + @:optional var exponent:String; + @:optional var valid_from:String; + @:optional var valid_to:String; + @:optional var fingerprint:String; + @:optional var fingerprint256:String; + @:optional var fingerprint512:String; + @:optional var serialNumber:String; + @:optional var raw:Buffer; + // present when detailed=true + @:optional var issuerCertificate:PeerCertificate; +} + +typedef CertificateSubject = { + @:optional var C:String; + @:optional var ST:String; + @:optional var L:String; + @:optional var O:String; + @:optional var OU:String; + @:optional var CN:String; } /** @@ -233,6 +285,20 @@ extern class Tls { **/ static function getCiphers():Array; + /** + Verifies the certificate `cert` is issued to `hostname`. + + Returns an `Error` object on failure, or `undefined` on success. + **/ + static function checkServerIdentity(hostname:String, cert:PeerCertificate):Null; + + /** + Converts the given ALPN protocols list into wire-format expected by OpenSSL. + + @see https://nodejs.org/api/tls.html#tlsconvertalpnprotocolsprotocols-out + **/ + static function convertALPNProtocols(protocols:EitherType>, Buffer>, out:{}):Void; + /** Creates a new `Server`. The `connectionListener` argument is automatically set as a listener for the 'secureConnection' event. @@ -247,6 +313,7 @@ extern class Tls { @:overload(function(port:Int, options:TlsConnectOptions, ?callback:Void->Void):TLSSocket {}) @:overload(function(port:Int, host:String, ?callback:Void->Void):TLSSocket {}) @:overload(function(port:Int, host:String, options:TlsConnectOptions, ?callback:Void->Void):TLSSocket {}) + @:overload(function(path:String, ?options:TlsConnectOptions, ?callback:Void->Void):TLSSocket {}) static function connect(options:TlsConnectOptions, ?callback:Void->Void):TLSSocket; /** @@ -260,5 +327,6 @@ extern class Tls { Generally the encrypted one is piped to/from an incoming encrypted data stream, and the cleartext one is used as a replacement for the initial encrypted stream. **/ + @:deprecated("Use Tls.TLSSocket instead") static function createSecurePair(?context:SecureContext, ?isServer:Bool, ?requestCert:Bool, ?rejectUnauthorized:Bool):SecurePair; } diff --git a/src/js/node/dgram/Socket.hx b/src/js/node/dgram/Socket.hx index 2232dfb2..d04c85dc 100644 --- a/src/js/node/dgram/Socket.hx +++ b/src/js/node/dgram/Socket.hx @@ -22,13 +22,10 @@ package js.node.dgram; +import haxe.extern.EitherType; import js.node.events.EventEmitter; import js.node.net.Socket.SocketAdress; -#if haxe4 import js.lib.Error; -#else -import js.Error; -#end /** Enumeration of events for the `Socket` object. @@ -58,6 +55,11 @@ enum abstract SocketEvent(Event) to Event { Emitted when an error occurs. **/ var Error:SocketEventVoid> = "error"; + + /** + Emitted when a socket has been connected with `socket.connect()`. + **/ + var Connect:SocketEventVoid> = "connect"; } /** @@ -83,7 +85,7 @@ enum abstract SocketType(String) from String to String { } /** - Options passed to the Socket consturctor. + Options passed to the Socket constructor. **/ typedef SocketOptions = { /** @@ -96,6 +98,49 @@ typedef SocketOptions = { Defaults to false. **/ @:optional var reuseAddr:Bool; + + /** + When `true`, enables `SO_REUSEPORT` so multiple processes can bind to the same address/port. + **/ + @:optional var reusePort:Bool; + + /** + Used to set the `IPV6_V6ONLY` socket option. + **/ + @:optional var ipv6Only:Bool; + + /** + Sets the `SO_RCVBUF` socket value. + **/ + @:optional var recvBufferSize:Int; + + /** + Sets the `SO_SNDBUF` socket value. + **/ + @:optional var sendBufferSize:Int; + + /** + Custom lookup function. Defaults to `Dns.lookup`. + **/ + @:optional var lookup:String->js.node.Dns.DnsLookupOptions->js.node.Dns.DnsLookupCallbackSingle->Void; + + /** + AbortSignal that can be used to close the socket. + **/ + @:optional var signal:js.node.web.AbortSignal; + + /** + When `true`, the receive buffer receives a cloned, shared `ArrayBuffer`. + Default: `false`. + Stability: 1 - Experimental. + **/ + @:optional var receiveBlockList:js.node.net.BlockList; + + /** + When `true`, messages to blocked destinations are dropped. + Stability: 1 - Experimental. + **/ + @:optional var sendBlockList:js.node.net.BlockList; } /** @@ -136,8 +181,33 @@ extern class Socket extends EventEmitter { to reuse the buf object. Note that DNS lookups delay the time to send for at least one tick. The only way to know for sure that the datagram has been sent is by using a `callback`. **/ + @:overload(function(msg:EitherType>>>, ?callback:Error->Int->Void):Void {}) + @:overload(function(msg:EitherType>>>, port:Int, ?callback:Error->Int->Void):Void {}) + @:overload(function(msg:EitherType>>>, port:Int, address:String, + ?callback:Error->Int->Void):Void {}) + @:overload(function(msg:Buffer, offset:Int, length:Int, ?callback:Error->Int->Void):Void {}) + @:overload(function(msg:Buffer, offset:Int, length:Int, port:Int, ?callback:Error->Int->Void):Void {}) function send(buf:Buffer, offset:Int, length:Int, port:Int, address:String, ?callback:Error->Int->Void):Void; + /** + Associates the `dgram.Socket` to a remote address and port. Subsequent messages will be sent to that + destination. Calling `connect()` on an already-connected socket regenerates an `ERR_SOCKET_DGRAM_IS_CONNECTED` error. + **/ + @:overload(function(port:Int, callback:Void->Void):Void {}) + @:overload(function(port:Int, address:String, ?callback:Void->Void):Void {}) + function connect(port:Int, ?callback:Void->Void):Void; + + /** + A synchronous function that disassociates a connected `dgram.Socket` from its remote address. + **/ + function disconnect():Void; + + /** + Returns an object containing the `address`, `family`, and `port` of the remote endpoint. + Throws `ERR_SOCKET_DGRAM_NOT_CONNECTED` if the socket is not connected. + **/ + function remoteAddress():SocketAdress; + /** Listen for datagrams on a named `port` and optional `address`. If `port` is not specified, the OS will try to bind to a random port. @@ -215,6 +285,51 @@ extern class Socket extends EventEmitter { **/ function dropMembership(multicastAddress:String, ?multicastInterface:String):Void; + /** + Instructs the kernel to join a source-specific multicast channel with the `IP_ADD_SOURCE_MEMBERSHIP` socket option. + **/ + function addSourceSpecificMembership(sourceAddress:String, groupAddress:String, ?multicastInterface:String):Void; + + /** + Instructs the kernel to leave a source-specific multicast channel with the `IP_DROP_SOURCE_MEMBERSHIP` socket option. + **/ + function dropSourceSpecificMembership(sourceAddress:String, groupAddress:String, ?multicastInterface:String):Void; + + /** + Sets the default outgoing multicast interface of the socket to a chosen interface or back to system interface selection. + **/ + function setMulticastInterface(multicastInterface:String):Void; + + /** + Sets the `SO_RCVBUF` socket option. + **/ + function setRecvBufferSize(size:Int):Void; + + /** + Sets the `SO_SNDBUF` socket option. + **/ + function setSendBufferSize(size:Int):Void; + + /** + Gets the current value of the `SO_RCVBUF` socket option. + **/ + function getRecvBufferSize():Int; + + /** + Gets the current value of the `SO_SNDBUF` socket option. + **/ + function getSendBufferSize():Int; + + /** + Number of bytes queued for sending. + **/ + function getSendQueueSize():Int; + + /** + Number of send requests currently in the queue awaiting to be processed. + **/ + function getSendQueueCount():Int; + /** Calling `unref` on a socket will allow the program to exit if this is the only active socket in the event system. If the socket is already `unref`d calling `unref` again will have no effect. diff --git a/src/js/node/dns/PromisesResolver.hx b/src/js/node/dns/PromisesResolver.hx index 91f4f404..2d245d5d 100644 --- a/src/js/node/dns/PromisesResolver.hx +++ b/src/js/node/dns/PromisesResolver.hx @@ -24,11 +24,7 @@ package js.node.dns; import haxe.extern.EitherType; import js.node.Dns; -#if haxe4 import js.lib.Promise; -#else -import js.Promise; -#end /** Promise-based independent resolver for DNS requests (`dns/promises.Resolver`). diff --git a/src/js/node/http/Agent.hx b/src/js/node/http/Agent.hx index c18f21bc..ce2e3758 100644 --- a/src/js/node/http/Agent.hx +++ b/src/js/node/http/Agent.hx @@ -24,11 +24,7 @@ package js.node.http; import haxe.DynamicAccess; import js.node.net.Socket; -#if haxe4 import js.lib.Error; -#else -import js.Error; -#end /** An `Agent` is responsible for managing connection persistence and reuse for HTTP clients. @@ -76,11 +72,7 @@ extern class Agent { `callback` has a signature of `(err, stream)`. **/ - #if haxe4 function createConnection(options:SocketConnectOptionsTcp, ?callback:(err:Error, stream:Socket) -> Void):Socket; - #else - function createConnection(options:SocketConnectOptionsTcp, ?callback:Error->Socket->Void):Socket; - #end /** Called when `socket` is detached from a request and could be persisted by the `Agent`. @@ -131,6 +123,12 @@ extern class Agent { **/ var maxSockets:Float; + /** + By default set to `Infinity`. + Determines how many concurrent sockets the agent can have open across all hosts in total. + **/ + var maxTotalSockets:Float; + /** An object which contains queues of requests that have not yet been assigned to sockets. Do not modify. @@ -182,8 +180,18 @@ typedef HttpAgentOptions = { **/ @:optional var maxFreeSockets:Int; + /** + Maximum total number of sockets allowed across all hosts. Default: `Infinity`. + **/ + @:optional var maxTotalSockets:Int; + /** Socket timeout in milliseconds. This will set the timeout when the socket is created. **/ @:optional var timeout:Int; + + /** + Scheduling strategy when picking an idle socket. One of `'fifo'` or `'lifo'`. Default: `'lifo'`. + **/ + @:optional var scheduling:String; } diff --git a/src/js/node/http/ClientRequest.hx b/src/js/node/http/ClientRequest.hx index de8f5634..59d80cc9 100644 --- a/src/js/node/http/ClientRequest.hx +++ b/src/js/node/http/ClientRequest.hx @@ -38,15 +38,21 @@ enum abstract ClientRequestEvent(Event) to Event **/ var Abort:ClientRequestEventVoid> = "abort"; + /** + Emitted when the request has been closed / completed. + **/ + var Close:ClientRequestEventVoid> = "close"; + + /** + Emitted when the request has been sent / finished writing. + **/ + var Finish:ClientRequestEventVoid> = "finish"; + /** Emitted each time a server responds to a request with a `CONNECT` method. If this event is not being listened for, clients receiving a `CONNECT` method will have their connections closed. **/ - #if haxe4 var Connect:ClientRequestEvent<(response:IncomingMessage, socket:Socket, head:Buffer) -> Void> = "connect"; - #else - var Connect:ClientRequestEventSocket->Buffer->Void> = "connect"; - #end /** Emitted when the server sends a '100 Continue' HTTP response, @@ -85,12 +91,8 @@ enum abstract ClientRequestEvent(Event) to Event If this event is not being listened for and the response status code is 101 Switching Protocols, clients receiving an upgrade header will have their connections closed. **/ - #if haxe4 var Upgrade:ClientRequestEvent<(response:IncomingMessage, socket:Socket, head:Buffer) -> Void> = "upgrade"; - #else - var Upgrade:ClientRequestEventSocket->Buffer->Void> = "upgrade"; - #end -} + } /** This object is created internally and returned from http.request(). @@ -155,6 +157,27 @@ extern class ClientRequest extends Writable { **/ function getHeader(name:String):haxe.extern.EitherType>; + /** + Returns an array containing the unique names of the current outgoing headers. + **/ + function getHeaderNames():Array; + + /** + Returns a shallow copy of the current outgoing headers. + **/ + function getHeaders():DynamicAccess>>; + + /** + Returns an array containing the unique names of the current outgoing raw headers. + Header names are returned with their exact casing. + **/ + function getRawHeaderNames():Array; + + /** + Returns `true` if the header identified by `name` is currently set in the outgoing headers. + **/ + function hasHeader(name:String):Bool; + /** Limits maximum response headers count. If set to 0, no limit will be applied. @@ -165,7 +188,27 @@ extern class ClientRequest extends Writable { /** The request path. **/ - var path(default, null):String; + var path:String; + + /** + The HTTP request method. + **/ + var method:Method; + + /** + The request host header value. + **/ + var host(default, null):String; + + /** + The request protocol (`http:` / `https:`). + **/ + var protocol(default, null):String; + + /** + Whether the request reused an existing socket. + **/ + var reusedSocket(default, null):Bool; /** Removes a header that's already defined into headers object. @@ -182,6 +225,22 @@ extern class ClientRequest extends Writable { @:overload(function(name:String, value:Array):Void {}) function setHeader(name:String, value:String):Void; + /** + Append a single header value for the header object. + + @see https://nodejs.org/api/http.html#outgoingmessageappendheadername-value + **/ + @:overload(function(name:String, value:Array):ClientRequest {}) + function appendHeader(name:String, value:String):ClientRequest; + + /** + Sets multiple header values for implicit headers. + + @see https://nodejs.org/api/http.html#outgoingmessagesetheadersheaders + **/ + @:overload(function(headers:DynamicAccess>>):Void {}) + function setHeaders(headers:js.node.web.Headers):Void; + /** Once a socket is assigned to this request and is connected `socket.setNoDelay` will be called. diff --git a/src/js/node/http/IncomingMessage.hx b/src/js/node/http/IncomingMessage.hx index be8aaba8..17864adf 100644 --- a/src/js/node/http/IncomingMessage.hx +++ b/src/js/node/http/IncomingMessage.hx @@ -26,11 +26,7 @@ import haxe.DynamicAccess; import js.node.events.EventEmitter.Event; import js.node.net.Socket; import js.node.stream.Readable; -#if haxe4 import js.lib.Error; -#else -import js.Error; -#end /** Enumeration of events emitted by the `IncomingMessage` objects in addition to its parent class events. @@ -86,6 +82,11 @@ extern class IncomingMessage extends Readable { **/ var headers(default, null):DynamicAccess>>; + /** + Similar to `headers`, but with header values as arrays of values, without joining. + **/ + var headersDistinct(default, null):DynamicAccess>; + /** In case of server request, the HTTP version sent by the client. In the case of client response, the HTTP version of the connected-to server. @@ -162,6 +163,11 @@ extern class IncomingMessage extends Readable { **/ var trailers(default, null):DynamicAccess; + /** + Similar to `trailers`, but with values as arrays of strings. + **/ + var trailersDistinct(default, null):DynamicAccess>; + /** *Only valid for request obtained from* `Server`. diff --git a/src/js/node/http/OutgoingMessage.hx b/src/js/node/http/OutgoingMessage.hx new file mode 100644 index 00000000..1ae5a6b0 --- /dev/null +++ b/src/js/node/http/OutgoingMessage.hx @@ -0,0 +1,119 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node.http; + +import haxe.DynamicAccess; +import haxe.extern.EitherType; +import js.node.net.Socket; +import js.node.stream.Writable; +import js.node.web.Headers; + +/** + This class serves as the parent class of `http.ClientRequest` and `http.ServerResponse`. + It is an abstract outgoing message from the perspective of the participants of an HTTP transaction. + + @see https://nodejs.org/docs/latest-v24.x/api/http.html#class-httpoutgoingmessage +**/ +@:jsRequire("http", "OutgoingMessage") +extern class OutgoingMessage extends Writable { + /** + Append a single header value for the header object. + + If the value is an array, this is equivalent to calling this method multiple times. + **/ + @:overload(function(name:String, value:Array):OutgoingMessage {}) + function appendHeader(name:String, value:String):OutgoingMessage; + + /** + Flushes the message headers. + **/ + function flushHeaders():Void; + + /** + Reads out a header that's already been queued but not sent. + The name is case-insensitive. + **/ + function getHeader(name:String):EitherType>; + + /** + Returns an array containing the unique names of the current outgoing headers. + All header names are lowercase. + **/ + function getHeaderNames():Array; + + /** + Returns a shallow copy of the current outgoing headers. + **/ + function getHeaders():DynamicAccess>>; + + /** + Returns an array containing the unique names of the current outgoing raw headers. + Header names are returned with their exact casing being set. + **/ + function getRawHeaderNames():Array; + + /** + Returns `true` if the header identified by `name` is currently set in the outgoing headers. + **/ + function hasHeader(name:String):Bool; + + /** + Boolean (read-only). `true` if headers were sent, otherwise `false`. + **/ + var headersSent(default, null):Bool; + + /** + Removes a header that's queued for implicit sending. + **/ + function removeHeader(name:String):Void; + + /** + Sets a single header value. If the header already exists in the to-be-sent headers, its value will be replaced. + **/ + @:overload(function(name:String, value:Array):Void {}) + function setHeader(name:String, value:String):Void; + + /** + Sets multiple header values for implicit headers. + + `headers` must be an instance of `Headers` or a key/value map-like object. + **/ + @:overload(function(headers:DynamicAccess>>):Void {}) + function setHeaders(headers:Headers):Void; + + /** + Once a socket is associated with the message and is connected, + `socket.setTimeout()` will be called with `msecs` as the first parameter. + **/ + function setTimeout(msecs:Int, ?callback:Void->Void):OutgoingMessage; + + /** + Reference to the underlying socket. + **/ + var socket(default, null):Socket; + + /** + Alias of `socket`. + **/ + var connection(default, null):Socket; +} diff --git a/src/js/node/http/Server.hx b/src/js/node/http/Server.hx index a47f7f19..020d1850 100644 --- a/src/js/node/http/Server.hx +++ b/src/js/node/http/Server.hx @@ -25,11 +25,7 @@ package js.node.http; import js.node.Buffer; import js.node.events.EventEmitter.Event; import js.node.net.Socket; -#if haxe4 import js.lib.Error; -#else -import js.Error; -#end /** Enumeration of events emitted by `http.Server` class in addition to @@ -46,11 +42,7 @@ enum abstract ServerEvent(Event) to Event { When this event is emitted and handled, the 'request' event will not be emitted. **/ - #if haxe4 var CheckContinue:ServerEvent<(request:IncomingMessage, response:ServerResponse) -> Void> = "checkContinue"; - #else - var CheckContinue:ServerEventServerResponse->Void> = "checkContinue"; - #end /** Emitted each time a request with an HTTP `Expect` header is received, where the value is not `100-continue`. @@ -58,11 +50,7 @@ enum abstract ServerEvent(Event) to Event { When this event is emitted and handled, the `'request'` event will not be emitted. **/ - #if haxe4 var CheckExpectation:ServerEvent<(request:IncomingMessage, response:ServerResponse) -> Void> = "checkExpectation"; - #else - var CheckExpectation:ServerEventServerResponse->Void> = "checkExpectation"; - #end /** If a client connection emits an `'error'` event, it will be forwarded here. @@ -72,11 +60,7 @@ enum abstract ServerEvent(Event) to Event { Default behavior is to try close the socket with a HTTP '400 Bad Request', or a HTTP '431 Request Header Fields Too Large' in the case of a `HPE_HEADER_OVERFLOW` error. If the socket is not writable it is immediately destroyed. **/ - #if haxe4 var ClientError:ServerEvent<(exception:Error, socket:Socket) -> Void> = "clientError"; - #else - var ClientError:ServerEventSocket->Void> = "clientError"; - #end /** Emitted when the server closes. @@ -90,11 +74,7 @@ enum abstract ServerEvent(Event) to Event { After this event is emitted, the request's socket will not have a `'data'` event listener, meaning it will need to be bound in order to handle data sent to the server on that socket. **/ - #if haxe4 var Connect:ServerEvent<(request:IncomingMessage, socekt:Socket, head:Buffer) -> Void> = "connect"; - #else - var Connect:ServerEventSocket->Buffer->Void> = "connect"; - #end /** This event is emitted when a new TCP stream is established. @@ -114,11 +94,7 @@ enum abstract ServerEvent(Event) to Event { Emitted each time there is a request. There may be multiple requests per connection (in the case of HTTP Keep-Alive connections). **/ - #if haxe4 var Request:ServerEvent<(request:IncomingMessage, response:ServerResponse) -> Void> = "request"; - #else - var Request:ServerEventServerResponse->Void> = "request"; - #end /** Emitted each time a client requests an HTTP upgrade. @@ -127,11 +103,14 @@ enum abstract ServerEvent(Event) to Event { After this event is emitted, the request's socket will not have a `'data'` event listener, meaning it will need to be bound in order to handle data sent to the server on that socket. **/ - #if haxe4 var Upgrade:ServerEvent<(request:IncomingMessage, socket:Socket, buffer:Buffer) -> Void> = "upgrade"; - #else - var Upgrade:ServerEventSocket->Buffer->Void> = "upgrade"; - #end + + /** + Emitted when a request could not be completed due to exceeding `server.maxRequestsPerSocket`. + + @see https://nodejs.org/api/http.html#event-droprequest + **/ + var DropRequest:ServerEvent<(request:IncomingMessage, socket:Socket) -> Void> = "dropRequest"; } /** @@ -154,6 +133,12 @@ extern class Server extends js.node.net.Server { **/ var headersTimeout:Int; + /** + Sets the timeout value in milliseconds for receiving the entire request from the client. + Default: `300000`. + **/ + var requestTimeout:Int; + /** Limits maximum incoming headers count. If set to 0, no limit will be applied. @@ -161,6 +146,12 @@ extern class Server extends js.node.net.Server { **/ var maxHeadersCount:Null; + /** + The maximum number of requests socket can handle before closing keep alive connection. + Default: `0` (no limit). + **/ + var maxRequestsPerSocket:Int; + /** Sets the timeout value for sockets, and emits a `'timeout'` event on the Server object, passing the socket as an argument, if a timeout occurs. @@ -172,6 +163,7 @@ extern class Server extends js.node.net.Server { To change the default timeout use the `--http-server-default-timeout` flag. **/ + @:overload(function(?callback:js.node.net.Socket->Void):Void {}) function setTimeout(msecs:Int, ?callback:js.node.net.Socket->Void):Void; /** @@ -201,4 +193,20 @@ extern class Server extends js.node.net.Server { Default: `5000` (5 seconds). **/ var keepAliveTimeout:Int; + + /** + Additional buffer of inactivity after `keepAliveTimeout` before destroying a keep-alive socket. + Default: `1000`. + **/ + var keepAliveTimeoutBuffer:Int; + + /** + Closes all connections connected to this server. + **/ + function closeAllConnections():Void; + + /** + Closes all connections connected to this server which are not sending a request or waiting for a response. + **/ + function closeIdleConnections():Void; } diff --git a/src/js/node/http/ServerResponse.hx b/src/js/node/http/ServerResponse.hx index b75757a5..439c7afa 100644 --- a/src/js/node/http/ServerResponse.hx +++ b/src/js/node/http/ServerResponse.hx @@ -124,6 +124,17 @@ extern class ServerResponse extends Writable { **/ var sendDate:Bool; + /** + A reference to the original HTTP `request` object. + **/ + var req(default, null):IncomingMessage; + + /** + If set to `true`, Node.js checks that `Content-Length` and the length of the body being transmitted are equal. + Default: `false`. + **/ + var strictContentLength:Bool; + /** Sets a single header value for implicit headers. If this header already exists in the to-be-sent headers, its value will be replaced. @@ -135,6 +146,27 @@ extern class ServerResponse extends Writable { @:overload(function(name:String, value:Array):Void {}) function setHeader(name:String, value:String):Void; + /** + Append a single header value to the header object. + + @see https://nodejs.org/api/http.html#outgoingmessageappendheadername-value + **/ + @:overload(function(name:String, value:Array):ServerResponse {}) + function appendHeader(name:String, value:String):ServerResponse; + + /** + Sets multiple header values for implicit headers. + + @see https://nodejs.org/api/http.html#outgoingmessagesetheadersheaders + **/ + @:overload(function(headers:DynamicAccess>>):Void {}) + function setHeaders(headers:js.node.web.Headers):Void; + + /** + Returns an array containing the unique names of the current outgoing raw headers. + **/ + function getRawHeaderNames():Array; + /** Sets the Socket's timeout value to `msecs`. If a callback is provided, then it is added as a listener on the `'timeout'` event on the response object. @@ -174,6 +206,21 @@ extern class ServerResponse extends Writable { */ function writeContinue():Void; + /** + Sends an HTTP/1.1 103 Early Hints message to the client with a Link header, + used to allow the user agent to preload/preconnect to resources while the full response is prepared. + + @see https://nodejs.org/api/http.html#responsewriteearlyhintshints-callback + **/ + function writeEarlyHints(hints:DynamicAccess>>, ?callback:Void->Void):Void; + + /** + Sends an informational 1xx response to the client. + + @see https://nodejs.org/api/http.html#responsewriteinformationstatuscode-headers-callback + **/ + function writeInformation(statusCode:Int, ?headers:DynamicAccess, ?callback:Void->Void):Void; + /** Sends a response header to the request. The status code is a 3-digit HTTP status code, like `404`. The last argument, `headers`, are the response headers. diff --git a/src/js/node/http2/Http2Stream.hx b/src/js/node/http2/Http2Stream.hx index 5ca3de9d..6b06aa2e 100644 --- a/src/js/node/http2/Http2Stream.hx +++ b/src/js/node/http2/Http2Stream.hx @@ -145,9 +145,11 @@ extern class Http2Stream extends Duplex { /** Priority signaling is no longer supported in Node.js. + + // TODO(section-3): remove once callers migrate off deprecated stream.priority **/ @:deprecated("Priority signaling is no longer supported in Node.js") - function priority(options:Dynamic):Void; + function priority(options:Any):Void; /** Sets the stream timeout value. diff --git a/src/js/node/net/BlockList.hx b/src/js/node/net/BlockList.hx index 64ed4c2b..190d938a 100644 --- a/src/js/node/net/BlockList.hx +++ b/src/js/node/net/BlockList.hx @@ -37,7 +37,7 @@ extern class BlockList { @see https://nodejs.org/api/net.html#blocklistisblocklistvalue **/ - static function isBlockList(value:Dynamic):Bool; + static function isBlockList(value:Any):Bool; /** Adds a rule to block the given IP address. diff --git a/src/js/node/net/Server.hx b/src/js/node/net/Server.hx index ae4c1e6b..feed03ee 100644 --- a/src/js/node/net/Server.hx +++ b/src/js/node/net/Server.hx @@ -25,11 +25,7 @@ package js.node.net; import haxe.extern.EitherType; import js.node.events.EventEmitter; import js.node.net.Socket.SocketAdress; -#if haxe4 import js.lib.Error; -#else -import js.Error; -#end /** Enumeration of events emitted by the `Server` objects @@ -56,6 +52,13 @@ enum abstract ServerEvent(Event) to Event { The 'close' event will be called directly following this event. See example in discussion of server.listen. **/ var Error:ServerEventVoid> = "error"; + + /** + Emitted when a connection is dropped because of `maxConnections` being reached. + + @see https://nodejs.org/api/net.html#event-drop + **/ + var Drop:ServerEvent->Void> = "drop"; } private typedef ServerListenOptionsBase = { @@ -107,7 +110,7 @@ extern class Server extends EventEmitter { The last parameter `callback` will be added as an listener for the 'listening' event. **/ @:overload(function(path:String, ?callback:Void->Void):Void {}) - @:overload(function(handle:EitherType, ?callback:Void->Void):Void {}) + @:overload(function(handle:EitherType>, ?callback:Void->Void):Void {}) @:overload(function(port:Int, ?callback:Void->Void):Void {}) @:overload(function(port:Int, backlog:Int, ?callback:Void->Void):Void {}) @:overload(function(port:Int, hostname:String, ?callback:Void->Void):Void {}) @@ -156,6 +159,14 @@ extern class Server extends EventEmitter { **/ var maxConnections:Int; + /** + If `true`, when the connection count reaches `maxConnections`, Node.js drops the connection + instead of handing it to another cluster worker. + + @see https://nodejs.org/api/net.html#serverdropmaxconnection + **/ + var dropMaxConnection:Bool; + /** The number of concurrent connections on the server. diff --git a/src/js/node/net/Socket.hx b/src/js/node/net/Socket.hx index 875bf135..da910c86 100644 --- a/src/js/node/net/Socket.hx +++ b/src/js/node/net/Socket.hx @@ -25,11 +25,7 @@ package js.node.net; import haxe.extern.EitherType; import js.node.Dns; import js.node.events.EventEmitter.Event; -#if haxe4 import js.lib.Error; -#else -import js.Error; -#end /** Enumeration of events for `Socket` objects. @@ -92,6 +88,37 @@ enum abstract SocketEvent(Event) to Event { had_error - true if the socket had a transmission error **/ var Close:SocketEventVoid> = "close"; + + /** + Emitted when a socket is ready to be used. + + Triggered immediately after `'connect'`. + **/ + var Ready:SocketEventVoid> = "ready"; + + /** + Emitted when a new connection attempt is started. + Only emitted for family-agnostic (`family: 0` / autoSelectFamily) connects. + + @see https://nodejs.org/api/net.html#event-connectionattempt + **/ + var ConnectionAttempt:SocketEvent<(ip:String, port:Int, family:Int) -> Void> = "connectionAttempt"; + + /** + Emitted when a connection attempt fails. + Only emitted for family-agnostic connects. + + @see https://nodejs.org/api/net.html#event-connectionattemptfailed + **/ + var ConnectionAttemptFailed:SocketEvent<(ip:String, port:Int, family:Int, error:Error) -> Void> = "connectionAttemptFailed"; + + /** + Emitted when a connection attempt times out. + Only emitted for family-agnostic connects. + + @see https://nodejs.org/api/net.html#event-connectionattempttimeout + **/ + var ConnectionAttemptTimeout:SocketEvent<(ip:String, port:Int, family:Int) -> Void> = "connectionAttemptTimeout"; } typedef SocketOptionsBase = { @@ -127,6 +154,25 @@ typedef SocketOptions = { allow writes on this socket (NOTE: Works only when `fd` is passed) **/ @:optional var writable:Bool; + + /** + If set to `false`, then the socket will automatically end the stream when the other end of the socket sends a FIN packet. + See `allowHalfOpen` for more information. Default: `false`. + **/ + @:optional var onread:SocketOnReadOptions; + + /** + AbortSignal used to abort an ongoing connect / read. + **/ + @:optional var signal:js.node.web.AbortSignal; +} + +/** + Optional `onread` callback options for `Socket`. +**/ +typedef SocketOnReadOptions = { + var buffer:EitherType js.node.Buffer>; + var callback:(bytesWritten:Int, buffer:js.node.Buffer) -> Bool; } /** @@ -156,6 +202,7 @@ typedef SocketConnectOptionsTcp = { /** Version of IP stack. Defaults to 4. + Use `0` for dual-stack / auto family selection. **/ @:optional var family:DnsAddressFamily; @@ -163,6 +210,46 @@ typedef SocketConnectOptionsTcp = { Custom lookup function. Defaults to `Dns.lookup`. **/ @:optional var lookup:String->DnsLookupOptions->DnsLookupCallbackSingle->Void; + + /** + Optional dns.lookup() hints. + **/ + @:optional var hints:Int; + + /** + If true, connecting sockets enable TCP keep-alive (SO_KEEPALIVE). + **/ + @:optional var keepAlive:Bool; + + /** + Initial TCP keep-alive delay in milliseconds, when `keepAlive` is enabled. + **/ + @:optional var keepAliveInitialDelay:Int; + + /** + If set to `true`, disables the Nagle algorithm. + **/ + @:optional var noDelay:Bool; + + /** + If set to `true`, enables TCP_KEEPALIVE on the connection. + **/ + @:optional var autoSelectFamily:Bool; + + /** + Timeout in milliseconds for autoSelectFamily connection attempts. + **/ + @:optional var autoSelectFamilyAttemptTimeout:Int; + + /** + Connection timeout in milliseconds. Defaults to no timeout. + **/ + @:optional var timeout:Int; + + /** + AbortSignal used to abort an ongoing connect. + **/ + @:optional var signal:js.node.web.AbortSignal; } /** @@ -173,6 +260,21 @@ typedef SocketConnectOptionsUnix = { Path the client should connect to **/ var path:String; + + /** + AbortSignal used to abort an ongoing connect. + **/ + @:optional var signal:js.node.web.AbortSignal; +} + +/** + Possible values of `socket.readyState`. +**/ +enum abstract SocketReadyState(String) from String to String { + var Opening = "opening"; + var Open = "open"; + var ReadOnly = "readOnly"; + var WriteOnly = "writeOnly"; } /** @@ -254,7 +356,6 @@ extern class Socket extends js.node.stream.Duplex { **/ // var destroyed(default, null):Bool; - #if haxe4 /** Ensures that no more I/O activity happens on this socket. Only necessary in case of errors (parse error or so). @@ -263,7 +364,18 @@ extern class Socket extends js.node.stream.Duplex { any listeners for that event will receive exception as an argument. **/ function destroy(?exception:Error):Void; - #end + + /** + Destroys the socket after all data has been written. + If there is remaining data in the write buffer, it will wait until that data has been flushed. + **/ + function destroySoon():Void; + + /** + Half-closes the socket by sending a FIN packet, then destroys it upon error / when fully closed. + Useful for HTTP/1 agents that need to reset a pooled connection. + **/ + function resetAndDestroy():Socket; /** Sets the socket to timeout after `timeout` milliseconds of inactivity on the socket. @@ -348,6 +460,42 @@ extern class Socket extends js.node.stream.Duplex { **/ var localPort(default, null):Int; + /** + The string representation of the local IP family. `'IPv4'` or `'IPv6'`. + **/ + var localFamily(default, null):SocketAdressFamily; + + /** + This property is only present when using `autoSelectFamily` and represents attempted addresses. + + @see https://nodejs.org/api/net.html#socketautoselectfamilyattemptedaddresses + **/ + var autoSelectFamilyAttemptedAddresses(default, null):Array; + + /** + If `true`, `socket.connect(options[, connectListener])` was called and has not yet finished. + **/ + var connecting(default, null):Bool; + + /** + This is `true` if the socket is not connected yet, either because `.connect()` has not yet been called + or because it is still in the process of connecting (see `connecting`). + **/ + var pending(default, null):Bool; + + /** + The socket timeout in milliseconds as set by `socket.setTimeout()`. + `undefined` if a timeout has not been set. + **/ + var timeout(default, null):Null; + + /** + This property represents the state of the connection as a string. + + @see https://nodejs.org/api/net.html#socketreadystate + **/ + var readyState(default, null):SocketReadyState; + /** The amount of received bytes. **/ diff --git a/src/js/node/tls/SecureContext.hx b/src/js/node/tls/SecureContext.hx index f304e348..5b6c950a 100644 --- a/src/js/node/tls/SecureContext.hx +++ b/src/js/node/tls/SecureContext.hx @@ -106,6 +106,36 @@ typedef SecureContextOptions = { Default: true. **/ @:optional var honorCipherOrder:Bool; + + /** + Optionally set the minimum TLS version to allow. One of `'TLSv1.3'`, `'TLSv1.2'`, `'TLSv1.1'`, or `'TLSv1'`. + **/ + @:optional var minVersion:String; + + /** + Optionally set the maximum TLS version to allow. One of `'TLSv1.3'`, `'TLSv1.2'`, `'TLSv1.1'`, or `'TLSv1'`. + **/ + @:optional var maxVersion:String; + + /** + An array of strings or a Buffer naming possible ALPN protocols. + **/ + @:optional var ALPNProtocols:EitherType>, Buffer>; + + /** + Name of an OpenSSL engine to get private key from. + **/ + @:optional var privateKeyEngine:String; + + /** + Identifier of a private key to get from an OpenSSL engine. + **/ + @:optional var privateKeyIdentifier:String; + + /** + Colon-separated list of supported signature algorithms. + **/ + @:optional var sigalgs:String; } extern class SecureContext {} diff --git a/src/js/node/tls/Server.hx b/src/js/node/tls/Server.hx index 4ebf6190..6b9b3c36 100644 --- a/src/js/node/tls/Server.hx +++ b/src/js/node/tls/Server.hx @@ -22,15 +22,13 @@ package js.node.tls; +import haxe.extern.EitherType; import js.node.Buffer; import js.node.events.EventEmitter.Event; +import js.node.tls.SecureContext; import js.node.tls.SecureContext.SecureContextOptions; import js.node.tls.TLSSocket; -#if haxe4 import js.lib.Error; -#else -import js.Error; -#end /** Enumeration of events emitted by `Server` in addition to its parent classes. @@ -49,8 +47,19 @@ enum abstract ServerEvent(Event) to Event { exception - error object securePair - the `TLSSocket` that the error originated from **/ + var TlsClientError:ServerEvent<(exception:Error, tlsSocket:TLSSocket) -> Void> = "tlsClientError"; + + /** + @deprecated Use `TlsClientError` (`tlsClientError`) instead. + **/ + @:deprecated("Use ServerEvent.TlsClientError / 'tlsClientError'") var ClientError:ServerEventTLSSocket->Void> = "clientError"; + /** + Emitted when key material is generated or received from a client for generating TLS session tickets. + **/ + var Keylog:ServerEvent<(line:Buffer, tlsSocket:TLSSocket) -> Void> = "keylog"; + /** Emitted on creation of TLS session. May be used to store sessions in external storage. @@ -62,7 +71,7 @@ enum abstract ServerEvent(Event) to Event { sessionData callback **/ - var NewSession:ServerEventBuffer->(Void->Void)->Void> = "newSession"; + var NewSession:ServerEvent<(sessionId:Buffer, sessionData:Buffer, callback:Void->Void) -> Void> = "newSession"; /** Emitted when client wants to resume previous TLS session. @@ -78,7 +87,7 @@ enum abstract ServerEvent(Event) to Event { sessionId callback **/ - var ResumeSession:ServerEvent(Error->?Buffer->Void)->Void> = "resumeSession"; + var ResumeSession:ServerEvent<(sessionId:Buffer, callback:(Null, Null) -> Void) -> Void> = "resumeSession"; /** Emitted when the client sends a certificate status request. @@ -91,7 +100,7 @@ enum abstract ServerEvent(Event) to Event { Calling `callback(err)` will result in a `socket.destroy(err)` call. **/ - var OCSPRequest:ServerEventBuffer->(Error->?Buffer->Void)->Void> = "OCSPRequest"; + var OCSPRequest:ServerEvent<(certificate:Buffer, issuer:Buffer, callback:(Null, Null) -> Void) -> Void> = "OCSPRequest"; } /** @@ -120,5 +129,11 @@ extern class Server extends js.node.net.Server { Add secure context that will be used if client request's SNI hostname is matching passed hostname (wildcards can be used). **/ - function addContext(hostname:String, credentials:SecureContextOptions):Void; + function addContext(hostname:String, credentials:EitherType):Void; + + /** + The `server.setSecureContext()` method replaces the secure context of an existing server. + Existing connections to the server are not interrupted. + **/ + function setSecureContext(options:SecureContextOptions):Void; } diff --git a/src/js/node/tls/TLSSocket.hx b/src/js/node/tls/TLSSocket.hx index 52bb5e5c..26bcfd5c 100644 --- a/src/js/node/tls/TLSSocket.hx +++ b/src/js/node/tls/TLSSocket.hx @@ -23,15 +23,13 @@ package js.node.tls; import haxe.Constraints.Function; +import haxe.extern.EitherType; import js.node.Buffer; import js.node.Tls.TlsClientOptionsBase; import js.node.Tls.TlsServerOptionsBase; import js.node.events.EventEmitter.Event; -#if haxe4 +import js.node.tls.SecureContext.SecureContextOptions; import js.lib.Error; -#else -import js.Error; -#end /** Enumeration of events emitted by `TLSSocket` objects in addition to its parent class events. @@ -49,6 +47,11 @@ enum abstract TLSSocketEvent(Event) to Event { **/ var SecureConnect:TLSSocketEventVoid> = "secureConnect"; + /** + Emitted once when the connection is established and the handshake completes (server-side companion of secureConnect). + **/ + var Secure:TLSSocketEventVoid> = "secure"; + /** This event will be emitted if `requestOCSP` option was set. @@ -58,6 +61,16 @@ enum abstract TLSSocketEvent(Event) to Event { that contains information about server's certificate revocation status. **/ var OCSPResponse:TLSSocketEventVoid> = "OCSPResponse"; + + /** + Emitted when key material is generated / received for the purpose of generating a TLS session ticket. + **/ + var Keylog:TLSSocketEventVoid> = "keylog"; + + /** + Emitted on session establishment / update with the TLS session `Buffer`. + **/ + var Session:TLSSocketEvent->Void> = "session"; } typedef TLSSocketOptions = { @@ -103,10 +116,19 @@ extern class TLSSocket extends js.node.net.Socket { var authorizationError(default, null):Null; /** - Negotiated protocol name. + Negotiated protocol name via NPN. + + @deprecated Use `alpnProtocol` instead. **/ + @:deprecated("Use alpnProtocol instead") var npnProtocol(default, null):String; + /** + String containing the selected ALPN protocol. When ALPN has no selected protocol, this is + the empty string. When ALPN has not been negotiated / disabled, this is `false`. + **/ + var alpnProtocol(default, null):EitherType; + /** Returns an object representing the peer's certificate. @@ -114,7 +136,12 @@ extern class TLSSocket extends js.node.net.Socket { If `detailed` argument is true - the full chain with issuer property will be returned, if false - only the top certificate without issuer property. **/ - function getPeerCertificate(?detailed:Bool):Dynamic; // TODO: is there a well defined structure for this? + function getPeerCertificate(?detailed:Bool):js.node.Tls.PeerCertificate; + + /** + Returns the local certificate as an object. + **/ + function getCertificate():Null; /** Returns an object representing the cipher name and the SSL/TLS protocol version of the current connection. @@ -123,7 +150,69 @@ extern class TLSSocket extends js.node.net.Socket { See SSL_CIPHER_get_name() and SSL_CIPHER_get_version() in http://www.openssl.org/docs/ssl/ssl.html#DEALING_WITH_CIPHERS for more information. **/ - function getCipher():{name:String, version:String}; + function getCipher():{name:String, standardName:String, version:String}; + + /** + Returns an object representing the type, name, and size of parameter of an ephemeral key exchange + in Perfect Forward Secrecy on a client connection. Returns empty object when key exchange is not ephemeral. + **/ + function getEphemeralKeyInfo():Null<{type:String, name:String, size:Int}>; + + /** + As the `Finished` messages are message digests of the complete handshake + (with a total of 192 bits for TLS 1.0 and more for SSL 3.0), they can + be used for external authentication procedures when the authentication provided by SSL/TLS is not desired or is not enough. + **/ + function getFinished():Null; + + /** + Returns the latest `Finished` message received from the peer as a `Buffer`. + **/ + function getPeerFinished():Null; + + /** + Returns a list of signature algorithms shared between the server and the client in order of decreasing preference. + **/ + function getSharedSigalgs():Array; + + /** + Returns `true` if the session was reused, `false` otherwise. + **/ + function isSessionReused():Bool; + + /** + Disables TLS renegotiation for this `TLSSocket` instance. + **/ + function disableRenegotiation():Void; + + /** + Enables TLS trace data for debugging. + **/ + function enableTrace():Void; + + /** + Keying material is used for validations for secure channel integrity and confidentially. + **/ + function exportKeyingMaterial(length:Int, label:String, ?context:Buffer):Buffer; + + /** + See `Socket.setKeyCert` / set credentials for the socket when using tickets for session resumption or for delayed certificate availability. + **/ + function setKeyCert(context:EitherType):Void; + + /** + Returns the peer certificate as an `X509Certificate` object. + + // TODO(section-3): type against crypto.X509Certificate once crypto audit exposes it + **/ + function getPeerX509Certificate():Any; + + /** + Returns the local certificate as an `X509Certificate` object. + + // TODO(section-3): type against crypto.X509Certificate once crypto audit exposes it + **/ + function getX509Certificate():Any; /** Initiate TLS renegotiation process. diff --git a/src/js/node/tty/ReadStream.hx b/src/js/node/tty/ReadStream.hx index 789a68d7..ba823c7b 100644 --- a/src/js/node/tty/ReadStream.hx +++ b/src/js/node/tty/ReadStream.hx @@ -29,6 +29,8 @@ package js.node.tty; **/ @:jsRequire("tty", "ReadStream") extern class ReadStream extends js.node.net.Socket { + function new(fd:Int, ?options:js.node.net.Socket.SocketOptions); + /** A boolean that is initialized to false. It represents the current "raw" state of the tty.ReadStream instance. @@ -40,5 +42,5 @@ extern class ReadStream extends js.node.net.Socket { This sets the properties of the tty.ReadStream to act either as a raw device or default. `isRaw` will be set to the resulting mode. **/ - function setRawMode(mode:Bool):Void; + function setRawMode(mode:Bool):ReadStream; } diff --git a/src/js/node/tty/WriteStream.hx b/src/js/node/tty/WriteStream.hx index 6142c8e8..8bca44ba 100644 --- a/src/js/node/tty/WriteStream.hx +++ b/src/js/node/tty/WriteStream.hx @@ -22,6 +22,8 @@ package js.node.tty; +import haxe.DynamicAccess; +import haxe.extern.EitherType; import js.node.events.EventEmitter; /** @@ -34,6 +36,15 @@ enum abstract WriteStreamEvent(Event) to EventVoid> = "resize"; } +/** + Direction for `WriteStream.clearLine`. +**/ +enum abstract WriteStreamClearLineDirection(Int) from Int to Int { + var Left = -1; + var Right = 1; + var EntireLine = 0; +} + /** A net.Socket subclass that represents the writable portion of a tty. In normal circumstances, process.stdout will be the only tty.WriteStream instance @@ -41,6 +52,8 @@ enum abstract WriteStreamEvent(Event) to EventVoid):Bool; + + /** + `writeStream.clearScreenDown()` clears this `WriteStream` from the current cursor down. + **/ + function clearScreenDown(?callback:Void->Void):Bool; + + /** + `writeStream.cursorTo()` moves this `WriteStream`'s cursor to the specified position. + **/ + @:overload(function(x:Int, callback:Void->Void):Bool {}) + function cursorTo(x:Int, ?y:Int, ?callback:Void->Void):Bool; + + /** + `writeStream.moveCursor()` moves this `WriteStream`'s cursor relative to its current position. + **/ + function moveCursor(dx:Int, dy:Int, ?callback:Void->Void):Bool; + + /** + Returns: + - `1` for 2, + - `4` for 16, + - `8` for 256, + - `24` for 16,777,216 colors supported. + **/ + function getColorDepth(?env:DynamicAccess):Int; + + /** + `writeStream.getWindowSize()` returns the size of the TTY corresponding to this `WriteStream` + as `[numColumns, numRows]`. + **/ + function getWindowSize():Array; + + /** + Returns `true` if the `writeStream` supports at least as many colors as provided in `count`. + **/ + @:overload(function(env:DynamicAccess):Bool {}) + @:overload(function(count:Int, ?env:DynamicAccess):Bool {}) + function hasColors(?count:EitherType>, ?env:DynamicAccess):Bool; } From 07043207d95028ba1328a93a10d3893c67d00dd5 Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Tue, 14 Jul 2026 18:41:48 +1000 Subject: [PATCH 36/48] Audit process/CLI/VM externs for Node 24 and Haxe 4. (#227) Align child_process, cluster, readline, repl, vm, v8, and domain with Node 24 APIs, drop Haxe 3 conditionals, and add core async_hooks, worker_threads, and wasi stubs. Co-authored-by: Cursor --- src/js/node/AsyncHooks.hx | 87 +++++++ src/js/node/ChildProcess.hx | 241 ++++++++++--------- src/js/node/Cluster.hx | 172 +++++-------- src/js/node/Domain.hx | 6 +- src/js/node/Readline.hx | 57 +---- src/js/node/ReadlinePromises.hx | 16 +- src/js/node/Repl.hx | 76 ++---- src/js/node/V8.hx | 175 ++++++++++++-- src/js/node/Vm.hx | 167 ++++++++++--- src/js/node/Wasi.hx | 90 +++++++ src/js/node/WorkerThreads.hx | 81 +++++++ src/js/node/async_hooks/AsyncHook.hx | 38 +++ src/js/node/async_hooks/AsyncLocalStorage.hx | 53 ++++ src/js/node/async_hooks/AsyncResource.hx | 85 +++++++ src/js/node/child_process/ChildProcess.hx | 147 +++++------ src/js/node/cluster/Worker.hx | 52 ++-- src/js/node/domain/Domain.hx | 6 +- src/js/node/readline/Interface.hx | 75 +++--- src/js/node/readline/PromisesInterface.hx | 8 +- src/js/node/readline/PromisesReadline.hx | 8 +- src/js/node/repl/REPLServer.hx | 47 +--- src/js/node/vm/Script.hx | 83 +++++-- src/js/node/worker_threads/MessageChannel.hx | 35 +++ src/js/node/worker_threads/MessagePort.hx | 75 ++++++ src/js/node/worker_threads/Worker.hx | 156 ++++++++++++ 25 files changed, 1420 insertions(+), 616 deletions(-) create mode 100644 src/js/node/AsyncHooks.hx create mode 100644 src/js/node/Wasi.hx create mode 100644 src/js/node/WorkerThreads.hx create mode 100644 src/js/node/async_hooks/AsyncHook.hx create mode 100644 src/js/node/async_hooks/AsyncLocalStorage.hx create mode 100644 src/js/node/async_hooks/AsyncResource.hx create mode 100644 src/js/node/worker_threads/MessageChannel.hx create mode 100644 src/js/node/worker_threads/MessagePort.hx create mode 100644 src/js/node/worker_threads/Worker.hx diff --git a/src/js/node/AsyncHooks.hx b/src/js/node/AsyncHooks.hx new file mode 100644 index 00000000..5e3c4379 --- /dev/null +++ b/src/js/node/AsyncHooks.hx @@ -0,0 +1,87 @@ +/* + * Copyright (C)2014-2020 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package js.node; + +/** + The `async_hooks` module provides an API to track asynchronous resources. + + Prefer `js.node.async_hooks.AsyncLocalStorage` for most application-level async context tracking. + `createHook` remains available but is a lower-level / less recommended API. + + @see https://nodejs.org/docs/latest-v24.x/api/async_hooks.html +**/ +@:jsRequire("async_hooks") +extern class AsyncHooks { + /** + Registers functions to be called for different lifetime events of each async operation. + **/ + static function createHook(options:AsyncHookOptions):js.node.async_hooks.AsyncHook; + + /** + Returns the `asyncId` of the current execution context. + **/ + static function executionAsyncId():Float; + + /** + Returns the resource object associated with the topmost async frame. + Using undocumented handle APIs on the returned object may crash the process. + **/ + // TODO(section-5): type executionAsyncResource beyond Dynamic when handle shapes are modeled + static function executionAsyncResource():Dynamic; + + /** + Returns the `asyncId` of the resource that caused (or "triggered") the current execution. + **/ + static function triggerAsyncId():Float; +} + +/** + Callbacks for `AsyncHooks.createHook`. +**/ +typedef AsyncHookOptions = { + /** + Called during object construction when the resource is initialized. + // TODO(section-5): type resource beyond Dynamic + **/ + @:optional var init:Float->String->Float->Dynamic->Void; + + /** + Called just before the resource's callback is called. + **/ + @:optional var before:Float->Void; + + /** + Called immediately after the resource's callback has completed. + **/ + @:optional var after:Float->Void; + + /** + Called after the resource is destroyed. + **/ + @:optional var destroy:Float->Void; + + /** + Called when the Promise-related callback is resolved. + **/ + @:optional var promiseResolve:Float->Void; +} diff --git a/src/js/node/ChildProcess.hx b/src/js/node/ChildProcess.hx index d21d1a9f..7d90eec9 100644 --- a/src/js/node/ChildProcess.hx +++ b/src/js/node/ChildProcess.hx @@ -24,24 +24,35 @@ package js.node; import haxe.DynamicAccess; import haxe.extern.EitherType; -import js.node.child_process.ChildProcess as ChildProcessObject; -#if haxe4 import js.lib.Error; -#else -import js.Error; -#end +import js.node.child_process.ChildProcess as ChildProcessObject; +import js.node.web.AbortSignal; +import js.node.url.URL; + +/** + Serialization mode for IPC messages between processes. + + @see https://nodejs.org/docs/latest-v24.x/api/child_process.html#advanced-serialization +**/ +enum abstract ChildProcessSerialization(String) from String to String { + var Json = "json"; + var Advanced = "advanced"; +} /** Common options for all `ChildProcess` methods. + + @see https://nodejs.org/docs/latest-v24.x/api/child_process.html **/ private typedef ChildProcessCommonOptions = { /** Current working directory of the child process. + Accepts a path string or WHATWG `URL` object with `file:` protocol. **/ - @:optional var cwd:String; + @:optional var cwd:EitherType; /** - Environment key-value pairs + Environment key-value pairs. **/ @:optional var env:DynamicAccess; @@ -57,12 +68,40 @@ private typedef ChildProcessCommonOptions = { /** Shell to execute the command with. - Default: '/bin/sh' on UNIX, 'cmd.exe' on Windows. + Default: `'/bin/sh'` on UNIX, `'cmd.exe'` on Windows. - The shell should understand the -c switch on UNIX or /s /c on Windows. - On Windows, command line parsing should be compatible with cmd.exe. + The shell should understand the `-c` switch on UNIX or `/s /c` on Windows. + On Windows, command line parsing should be compatible with `cmd.exe`. **/ @:optional var shell:EitherType; + + /** + Hide the subprocess console window that would normally be created on Windows. + Default: `false`. + **/ + @:optional var windowsHide:Bool; + + /** + No quoting or escaping of arguments is done on Windows. + Ignored on Unix. Default: `false`. + **/ + @:optional var windowsVerbatimArguments:Bool; + + /** + Allows aborting the child process using an `AbortSignal`. + **/ + @:optional var signal:AbortSignal; + + /** + Maximum amount of time in milliseconds the process is allowed to run. + **/ + @:optional var timeout:Int; + + /** + The signal value to be used when the spawned process will be killed + by timeout or abort signal. Default: `'SIGTERM'`. + **/ + @:optional var killSignal:EitherType; } /** @@ -75,6 +114,12 @@ private typedef ChildProcessSpawnOptionsBase = { Child's stdio configuration. **/ @:optional var stdio:ChildProcessSpawnOptionsStdio; + + /** + Specify the kind of serialization used for sending messages between processes. + Possible values are `'json'` and `'advanced'`. Default: `'json'`. + **/ + @:optional var serialization:ChildProcessSerialization; } /** @@ -84,9 +129,15 @@ typedef ChildProcessSpawnOptions = { > ChildProcessSpawnOptionsBase, /** - The child will be a process group leader. + The child will be a process group leader / can keep running after parent exits. **/ @:optional var detached:Bool; + + /** + Explicitly set the value of `argv[0]` sent to the child process. + This will be set to `command` if not specified. + **/ + @:optional var argv0:String; } /** @@ -104,29 +155,14 @@ typedef ChildProcessSpawnSyncOptions = { The value is one of the following: * 'pipe' - Create a pipe between the child process and the parent process. - The parent end of the pipe is exposed to the parent as a property on the child_process object as ChildProcess.stdio[fd]. - Pipes created for fds 0 - 2 are also available as ChildProcess.stdin, ChildProcess.stdout and ChildProcess.stderr, respectively. - - * 'ipc' - Create an IPC channel for passing messages/file descriptors between parent and child. - A ChildProcess may have at most one IPC stdio file descriptor. Setting this option enables the ChildProcess.send() method. - If the child writes JSON messages to this file descriptor, then this will trigger ChildProcess.on('message'). - If the child is a Node.js program, then the presence of an IPC channel will enable process.send() and process.on('message'). - - * 'ignore' - Do not set this file descriptor in the child. Note that Node will always open fd 0 - 2 for the processes it spawns. - When any of these is ignored node will open /dev/null and attach it to the child's fd. - - * Stream object - Share a readable or writable stream that refers to a tty, file, socket, or a pipe with the child process. - The stream's underlying file descriptor is duplicated in the child process to the fd that corresponds to the index - in the stdio array. Note that the stream must have an underlying descriptor (file streams do not until the 'open' - event has occurred). - - * Positive integer - The integer value is interpreted as a file descriptor that is is currently open in the parent process. - It is shared with the child process, similar to how Stream objects can be shared. - - * null - Use default value. For stdio fds 0, 1 and 2 (in other words, stdin, stdout, and stderr) a pipe is created. - For fd 3 and up, the default is 'ignore'. - - As a shorthand, the stdio argument may also be one of the following strings, rather than an array: + * 'ipc' - Create an IPC channel for passing messages/file descriptors. + * 'ignore' - Do not set this file descriptor in the child. + * 'inherit' - Pass through the corresponding stdio stream to/from the parent. + * Stream object - Share a readable or writable stream with the child process. + * Positive integer - A file descriptor that is currently open in the parent. + * null / undefined - Use default value. + + As a shorthand, the stdio argument may also be one of the following strings: ignore - ['ignore', 'ignore', 'ignore'] pipe - ['pipe', 'pipe', 'pipe'] inherit - [process.stdin, process.stdout, process.stderr] or [0,1,2] @@ -138,19 +174,24 @@ typedef ChildProcessSpawnOptionsStdio = EitherType>>; +// Ideal type: Array>> +// TODO(section-5): replace Array once nested EitherType arrays type-check reliably typedef ChildProcessSpawnOptionsStdioFull = Array; /** @@ -197,24 +236,14 @@ private typedef ChildProcessExecOptionsBase = { > ChildProcessCommonOptions, /** - Default: 'utf8' + Default: `'utf8'` **/ @:optional var encoding:String; - /** - If greater than 0, then it will kill the child process if it runs longer than timeout milliseconds. - **/ - @:optional var timeout:Int; - - /** - The child process is killed with `killSignal` (default: 'SIGTERM'). - **/ - @:optional var killSignal:String; - /** The largest amount of data allowed on stdout or stderr. If this value is exceeded then the child process is killed. - Default: 200*1024 + Default: `1024 * 1024` **/ @:optional var maxBuffer:Int; } @@ -240,21 +269,31 @@ typedef ChildProcessForkOptions = { > ChildProcessCommonOptions, /** - Executable used to create the child process + Executable used to create the child process. **/ @:optional var execPath:String; /** - List of string arguments passed to the executable (Default: process.execArgv) + List of string arguments passed to the executable (Default: `process.execArgv`) **/ @:optional var execArgv:Array; /** If `true`, stdin, stdout, and stderr of the child will be piped to the parent, - otherwise they will be inherited from the parent, see the "pipe" and "inherit" - options for `ChildProcessSpawnOptions.stdio` for more details (default is `false`) + otherwise they will be inherited from the parent (default is `false`). **/ @:optional var silent:Bool; + + /** + See `ChildProcessSpawnOptions.stdio`. When provided, overrides `silent`. + Must contain exactly one `'ipc'` entry when using the array form. + **/ + @:optional var stdio:ChildProcessSpawnOptionsStdio; + + /** + Specify the kind of serialization used for sending messages between processes. + **/ + @:optional var serialization:ChildProcessSerialization; } /** @@ -263,7 +302,7 @@ typedef ChildProcessForkOptions = { @:native("Error") extern class ChildProcessExecError extends Error { /** - the exit code of the child proces. + the exit code of the child process. **/ var code(default, null):Int; @@ -281,8 +320,8 @@ extern class ChildProcessExecError extends Error { and `error.code` will be the exit code of the child process, and `error.signal` will be set to the signal that terminated the process (see `ChildProcessExecError`). **/ -typedef ChildProcessExecCallback = #if (haxe_ver >= 4) (error:Null, stdout:EitherType, - stderr:EitherType) -> Void; #else Null->EitherType->EitherType->Void; #end +typedef ChildProcessExecCallback = (error:Null, stdout:EitherType, + stderr:EitherType) -> Void; /** Object returned from the `spawnSync` method. @@ -296,7 +335,7 @@ typedef ChildProcessSpawnSyncResult = { /** Array of results from stdio output **/ - var output:Array>; + var output:Array>>; /** The contents of output[1] @@ -311,12 +350,12 @@ typedef ChildProcessSpawnSyncResult = { /** The exit code of the child process **/ - var status:Int; + var status:Null; /** The signal used to kill the child process **/ - var signal:String; + var signal:Null; /** The error object if the child process failed or timed out @@ -324,19 +363,16 @@ typedef ChildProcessSpawnSyncResult = { var error:Error; } +/** + The `child_process` module enables spawning child processes. + + @see https://nodejs.org/docs/latest-v24.x/api/child_process.html +**/ @:jsRequire("child_process") extern class ChildProcess { /** Launches a new process with the given `command`, with command line arguments in `args`. If omitted, `args` defaults to an empty `Array`. - - The third argument is used to specify additional options, which defaults to: - { cwd: null, - env: process.env - } - - Note that if spawn receives an empty options object, it will result in spawning the process with an empty - environment rather than using `process.env`. This due to backwards compatibility issues with a deprecated API. **/ @:overload(function(command:String, ?options:ChildProcessSpawnOptions):ChildProcessObject {}) @:overload(function(command:String, args:Array, ?options:ChildProcessSpawnOptions):ChildProcessObject {}) @@ -344,23 +380,12 @@ extern class ChildProcess { /** Runs a command in a shell and buffers the output. - - `command` is the command to run, with space-separated arguments. - - The default `options` are: - { encoding: 'utf8', - timeout: 0, - maxBuffer: 200*1024, - killSignal: 'SIGTERM', - cwd: null, - env: null } **/ @:overload(function(command:String, options:ChildProcessExecOptions, callback:ChildProcessExecCallback):ChildProcessObject {}) static function exec(command:String, callback:ChildProcessExecCallback):ChildProcessObject; /** - This is similar to `exec` except it does not execute a subshell but rather the specified file directly. - This makes it slightly leaner than `exec` + Similar to `exec` except it does not execute a subshell but rather the specified file directly. **/ @:overload(function(file:String, args:Array, options:ChildProcessExecFileOptions, ?callback:ChildProcessExecCallback):ChildProcessObject {}) @:overload(function(file:String, options:ChildProcessExecFileOptions, ?callback:ChildProcessExecCallback):ChildProcessObject {}) @@ -368,36 +393,21 @@ extern class ChildProcess { static function execFile(file:String, ?callback:ChildProcessExecCallback):ChildProcessObject; /** - This is a special case of the `spawn` functionality for spawning Node processes. - In addition to having all the methods in a normal `ChildProcess` instance, - the returned object has a communication channel built-in. - See `send` for details. + Special case of `spawn` for spawning Node.js processes with an IPC channel. **/ - @:overload(function(modulePath:String, args:Array, options:ChildProcessForkOptions):ChildProcessObject {}) - @:overload(function(modulePath:String, options:ChildProcessForkOptions):ChildProcessObject {}) - static function fork(modulePath:String, ?args:Array):ChildProcessObject; + @:overload(function(modulePath:EitherType, args:Array, options:ChildProcessForkOptions):ChildProcessObject {}) + @:overload(function(modulePath:EitherType, options:ChildProcessForkOptions):ChildProcessObject {}) + static function fork(modulePath:EitherType, ?args:Array):ChildProcessObject; /** Synchronous version of `spawn`. - - `spawnSync` will not return until the child process has fully closed. - When a timeout has been encountered and `killSignal` is sent, the method won't return until the process - has completely exited. That is to say, if the process handles the SIGTERM signal and doesn't exit, - your process will wait until the child process has exited. **/ @:overload(function(command:String, args:Array, ?options:ChildProcessSpawnSyncOptions):ChildProcessSpawnSyncResult {}) static function spawnSync(command:String, ?options:ChildProcessSpawnSyncOptions):ChildProcessSpawnSyncResult; /** Synchronous version of `execFile`. - - `execFileSync` will not return until the child process has fully closed. - When a timeout has been encountered and `killSignal` is sent, the method won't return until the process - has completely exited. That is to say, if the process handles the SIGTERM signal and doesn't exit, - your process will wait until the child process has exited. - If the process times out, or has a non-zero exit code, this method will throw. - The Error object will contain the entire result from `spawnSync` **/ @:overload(function(command:String, ?options:ChildProcessSpawnSyncOptions):EitherType {}) @:overload(function(command:String, args:Array, ?options:ChildProcessSpawnSyncOptions):EitherType {}) @@ -405,14 +415,7 @@ extern class ChildProcess { /** Synchronous version of `exec`. - - `execSync` will not return until the child process has fully closed. - When a timeout has been encountered and `killSignal` is sent, the method won't return until the process - has completely exited. That is to say, if the process handles the SIGTERM signal and doesn't exit, - your process will wait until the child process has exited. - If the process times out, or has a non-zero exit code, this method will throw. - The Error object will contain the entire result from `spawnSync` **/ static function execSync(command:String, ?options:ChildProcessSpawnSyncOptions):EitherType; } diff --git a/src/js/node/Cluster.hx b/src/js/node/Cluster.hx index 9235377a..6aaa9232 100644 --- a/src/js/node/Cluster.hx +++ b/src/js/node/Cluster.hx @@ -23,76 +23,54 @@ package js.node; import haxe.DynamicAccess; +import js.node.ChildProcess.ChildProcessSerialization; +import js.node.ChildProcess.ChildProcessSpawnOptionsStdio; import js.node.cluster.Worker; import js.node.events.EventEmitter; +/** + @see https://nodejs.org/docs/latest-v24.x/api/cluster.html +**/ enum abstract ClusterEvent(Event) to Event { /** When a new worker is forked the cluster module will emit a 'fork' event. This can be used to log worker activity, and create your own timeout. - - Listener arguments: - * worker:Worker **/ var Fork:ClusterEventVoid> = "fork"; /** After forking a new worker, the worker should respond with an online message. - When the master receives an online message it will emit this event. - - The difference between 'fork' and 'online' is that fork is emitted when the master forks a worker, - and 'online' is emitted when the worker is running. - - Listener arguments: - * worker:Worker + When the primary receives an online message it will emit this event. **/ var Online:ClusterEventVoid> = "online"; /** After calling `listen` from a worker, when the 'listening' event is emitted on the server, - a listening event will also be emitted on cluster in the master. - - The event handler is executed with two arguments, the `worker` contains the worker object and - the `address` object contains the following connection properties: address, port and addressType. - This is very useful if the worker is listening on more than one address. - - Listener arguments: - * worker:Worker - * address:ListeningEventAddress + a listening event will also be emitted on cluster in the primary. **/ var Listening:ClusterEventListeningEventAddress->Void> = "listening"; /** Emitted after the worker IPC channel has disconnected. - - This can occur when a worker exits gracefully, is killed, - or is disconnected manually (such as with `Worker.disconnect`). - - There may be a delay between the 'disconnect' and 'exit' events. - - These events can be used to detect if the process is stuck in a cleanup - or if there are long-living connections. - - Listener arguments: - * worker:Worker **/ var Disconnect:ClusterEventVoid> = "disconnect"; /** When any of the workers die the cluster module will emit the 'exit' event. - This can be used to restart the worker by calling `Cluster.fork` again. - - Listener arguments: - * worker:Worker - * code:Int - the exit code, if it exited normally. - * signal:String - the name of the signal (eg. 'SIGHUP') that caused the process to be killed. **/ var Exit:ClusterEventInt->String->Void> = "exit"; /** - Emitted the first time that `Cluster.setupMaster` is called. + Emitted every time `setupPrimary` is called. **/ var Setup:ClusterEventVoid> = "setup"; + + /** + Emitted when any worker receives a message. + + // TODO(section-5): tighten message/handle typing beyond Dynamic + **/ + var Message:ClusterEventDynamic->Dynamic->Void> = "message"; } /** @@ -119,13 +97,9 @@ extern enum abstract ClusterSchedulingPolicy(Int) { } /** - A single instance of Node runs in a single thread. - To take advantage of multi-core systems the user will sometimes want to launch a cluster of Node processes to handle the load. - The cluster module allows you to easily create child processes that all share server ports. + The cluster module allows easy creation of child processes that all share server ports. - This feature was introduced recently, and may change in future versions. Please try it out and provide feedback. - - Also note that, on Windows, it is not yet possible to set up a named pipe server in a worker. + @see https://nodejs.org/docs/latest-v24.x/api/cluster.html **/ @:jsRequire("cluster") extern class Cluster extends EventEmitter { @@ -139,108 +113,63 @@ extern class Cluster extends EventEmitter { /** The scheduling policy, either `SCHED_RR` for round-robin or `SCHED_NONE` to leave it to the operating system. - - This is a global setting and effectively frozen once you spawn the first worker - or call `setupMaster`, whatever comes first. - - `SCHED_RR` is the default on all operating systems except Windows. - Windows will change to `SCHED_RR` once libuv is able to effectively distribute IOCP handles - without incurring a large performance hit. - - `schedulingPolicy` can also be set through the NODE_CLUSTER_SCHED_POLICY environment variable. - Valid values are "rr" and "none". **/ var schedulingPolicy:ClusterSchedulingPolicy; /** - After calling `setupMaster` (or `fork`) this settings object will contain the settings, including the default values. - - It is effectively frozen after being set, because `setupMaster` can only be called once. - - This object is not supposed to be changed or set manually, by you. + After calling `setupPrimary` (or `fork`) this settings object will contain the settings, + including the default values. **/ var settings(default, null):ClusterSettings; /** - True if the process is a master. - This is determined by the process.env.NODE_UNIQUE_ID. - If process.env.NODE_UNIQUE_ID is undefined, then `isMaster` is true. + True if the process is a primary. + Deprecated alias for `isPrimary`. **/ + @:deprecated("Use isPrimary instead") var isMaster(default, null):Bool; /** True if the process is a primary. This is determined by the process.env.NODE_UNIQUE_ID. If process.env.NODE_UNIQUE_ID is undefined, then `isPrimary` is true. - - `isMaster` is a deprecated alias of `isPrimary`. **/ var isPrimary(default, null):Bool; /** - True if the process is not a master (it is the negation of `isMaster`). + True if the process is not a primary (it is the negation of `isPrimary`). **/ var isWorker(default, null):Bool; /** - `setupMaster` is used to change the default `fork` behavior. - - Once called, the `settings` will be present in `settings`. - - Note that: - Only the first call to `setupMaster` has any effect, subsequent calls are ignored - - That because of the above, the only attribute of a worker that may be customized per-worker - is the `env` passed to `fork` - - `fork` calls `setupMaster` internally to establish the defaults, so to have any effect, - `setupMaster` must be called before any calls to `fork` + Deprecated alias for `setupPrimary`. **/ - function setupMaster(?settings:{?exec:String, ?args:Array, ?silent:Bool}):Void; + @:deprecated("Use setupPrimary instead") + function setupMaster(?settings:ClusterSettings):Void; /** Used to change the default `fork` behavior. Once called, the settings will be present in `settings`. - - `setupMaster` is a deprecated alias of `setupPrimary`. **/ - function setupPrimary(?settings:{?exec:String, ?args:Array, ?silent:Bool}):Void; + function setupPrimary(?settings:ClusterSettings):Void; /** - Spawn a new worker process. - - This can only be called from the master process. + Spawn a new worker process. This can only be called from the primary process. **/ function fork(?env:DynamicAccess):Worker; /** Calls `disconnect` on each worker in `workers`. - - When they are disconnected all internal handles will be closed, - allowing the master process to die gracefully if no other event is waiting. - - The method takes an optional `callback` argument which will be called when finished. - - This can only be called from the master process. **/ function disconnect(?callback:Void->Void):Void; /** - A reference to the current worker object. - - Not available in the master process. + A reference to the current worker object. Not available in the primary process. **/ var worker(default, null):Worker; /** A hash that stores the active worker objects, keyed by `id` field. - Makes it easy to loop through all the workers. - - It is only available in the master process. - - A worker is removed from `workers` just before the 'disconnect' or 'exit' event is emitted. - - Should you wish to reference a worker over a communication channel, using the worker's unique `id` - is the easiest way to find the worker. + It is only available in the primary process. **/ var workers(default, null):DynamicAccess; } @@ -250,33 +179,62 @@ typedef ClusterSettings = { list of string arguments passed to the node executable. Default: process.execArgv **/ - @:optional var execArgv(default, null):Array; + @:optional var execArgv:Array; /** file path to worker file. Default: process.argv[1] **/ - @:optional var exec(default, null):String; + @:optional var exec:String; /** string arguments passed to worker. Default: process.argv.slice(2) **/ - @:optional var args(default, null):Array; + @:optional var args:Array; + + /** + Current working directory of the worker process. + Default: `undefined` (inherits from parent process). + **/ + @:optional var cwd:String; + + /** + Specify the kind of serialization used for sending messages between processes. + **/ + @:optional var serialization:ChildProcessSerialization; /** whether or not to send output to parent's stdio. Default: false **/ - @:optional var silent(default, null):Bool; + @:optional var silent:Bool; + + /** + Configures the stdio of forked processes. + Must contain an `'ipc'` entry. When provided, overrides `silent`. + **/ + @:optional var stdio:ChildProcessSpawnOptionsStdio; /** Sets the user identity of the process. **/ - @:optional var uid(default, null):Int; + @:optional var uid:Int; /** Sets the group identity of the process. **/ - @:optional var gid(default, null):Int; + @:optional var gid:Int; + + /** + Sets inspector port of worker. Can be a number, or a function that returns a number. + **/ + // TODO(section-5): model inspectPort callback `() -> Int` without losing Int assignability + @:optional var inspectPort:haxe.extern.EitherTypeInt>; + + /** + Hide the forked processes console window that would normally be created on Windows. + Default: `false`. + **/ + @:optional var windowsHide:Bool; } diff --git a/src/js/node/Domain.hx b/src/js/node/Domain.hx index 63fac5f3..53d447cf 100644 --- a/src/js/node/Domain.hx +++ b/src/js/node/Domain.hx @@ -27,11 +27,15 @@ import js.node.domain.Domain as DomainObject; /** Domains provide a way to handle multiple different IO operations as a single group. + Stability: 0 - Deprecated. Prefer `js.node.AsyncHooks` / `AsyncLocalStorage` for async context tracking. + If any of the event emitters or callbacks registered to a domain emit an error event, or throw an error, then the domain object will be notified, rather than losing the context of the error in the process.on('uncaughtException') handler, or causing the program to exit immediately with an error code. + + @see https://nodejs.org/docs/latest-v24.x/api/domain.html **/ -@:deprecated +@:deprecated("The domain module is deprecated. Use AsyncLocalStorage / async_hooks instead.") @:jsRequire("domain") extern class Domain { /** diff --git a/src/js/node/Readline.hx b/src/js/node/Readline.hx index 69708579..58d33d96 100644 --- a/src/js/node/Readline.hx +++ b/src/js/node/Readline.hx @@ -32,53 +32,41 @@ import js.node.web.AbortSignal; The readline module provides an interface for reading data from a `Readable` stream (such as `process.stdin`) one line at a time. - @see https://nodejs.org/api/readline.html#readline_readline + @see https://nodejs.org/docs/latest-v24.x/api/readline.html **/ @:jsRequire("readline") extern class Readline { /** The `readline.clearLine()` method Clears current line of given `TTY` stream in a specified direction identified by `dir`. - - @see https://nodejs.org/api/readline.html#readline_readline_clearline_stream_dir_callback **/ static function clearLine(stream:IWritable, dir:ClearLineDirection, ?callback:Void->Void):Bool; /** The `readline.clearScreenDown()` method clears the given `TTY` stream from the current position of the cursor down. - - @see https://nodejs.org/api/readline.html#readline_readline_clearscreendown_stream_callback **/ static function clearScreenDown(stream:IWritable, ?callback:Void->Void):Bool; /** The `readline.createInterface()` method creates a new `readline.Interface` instance. - - @see https://nodejs.org/api/readline.html#readline_readline_createinterface_options **/ @:overload(function(input:IReadable, ?output:IWritable, ?completer:ReadlineCompleterCallback, ?terminal:Bool):Interface {}) static function createInterface(options:ReadlineOptions):Interface; /** The `readline.cursorTo()` method moves cursor to the specified position in a given `TTY` `stream`. - - @see https://nodejs.org/api/readline.html#readline_readline_cursorto_stream_x_y_callback **/ static function cursorTo(stream:IWritable, x:Int, ?y:Int, ?callback:Void->Void):Bool; /** The `readline.emitKeypressEvents()` method causes the given `Readable` stream to begin emitting `'keypress'` events corresponding to received input. - - @see https://nodejs.org/api/readline.html#readline_readline_emitkeypressevents_stream_interface **/ static function emitKeypressEvents(stream:IReadable, ?iface:Interface):Void; /** The `readline.moveCursor()` method moves the cursor relative to its current position in a given `TTY` `stream`. - - @see https://nodejs.org/api/readline.html#readline_readline_movecursor_stream_dx_dy_callback **/ static function moveCursor(stream:IWritable, dx:Int, dy:Int, ?callback:Void->Void):Bool; } @@ -86,7 +74,7 @@ extern class Readline { /** Options object used by `Readline.createInterface`. - @see https://nodejs.org/api/readline.html#readline_readline_createinterface_options + @see https://nodejs.org/docs/latest-v24.x/api/readline.html#readlinecreateinterfaceoptions **/ typedef ReadlineOptions = { /** @@ -101,77 +89,50 @@ typedef ReadlineOptions = { /** An optional function used for Tab autocompletion. - - @see https://nodejs.org/api/readline.html#readline_use_of_the_completer_function **/ @:optional var completer:ReadlineCompleterCallback; /** `true` if the `input` and `output` streams should be treated like a TTY, and have ANSI/VT100 escape codes written to it. - - Default: checking `isTTY` on the `output` stream upon instantiation. **/ @:optional var terminal:Bool; /** Initial list of history lines. - This option makes sense only if `terminal` is set to `true` by the user or by an internal `output` check, - otherwise the history caching mechanism is not initialized at all. - - Default: `[]`. **/ @:optional var history:Array; /** Maximum number of history lines retained. To disable the history set this value to `0`. - This option makes sense only if `terminal` is set to `true` by the user or by an internal `output` check, - otherwise the history caching mechanism is not initialized at all. - - Default: `30`. **/ @:optional var historySize:Int; /** - The prompt string to use. - - Default: `'> '`. + The prompt string to use. Default: `'> '`. **/ @:optional var prompt:String; /** If the delay between `\r` and `\n` exceeds `crlfDelay` milliseconds, both `\r` and `\n` will be treated as separate end-of-line input. - `crlfDelay` will be coerced to a number no less than `100`. - It can be set to `Infinity`, in which case `\r` followed by `\n` will always be considered a single newline - (which may be reasonable for reading files with `\r\n` line delimiter). - - Default: `100`. **/ - @:optional var crlfDelay:Int; + @:optional var crlfDelay:Float; /** If `true`, when a new input line added to the history list duplicates an older one, this removes the older line from the list. - - Default: `false`. **/ @:optional var removeHistoryDuplicates:Bool; /** - The duration `readline` will wait for a character (when reading an ambiguous key sequence in milliseconds one - that can both form a complete key sequence using the input read so far and can take additional input to complete - a longer key sequence). - - Default: `500`. + The duration `readline` will wait for a character (when reading an ambiguous key sequence) in milliseconds. **/ @:optional var escapeCodeTimeout:Int; /** - The number of spaces a tab is equal to (minimum 1). - - Default: `8`. + The number of spaces a tab is equal to (minimum 1). Default: `8`. **/ @:optional var tabSize:Int; @@ -182,16 +143,10 @@ typedef ReadlineOptions = { @:optional var signal:AbortSignal; } -#if haxe4 typedef ReadlineCompleterCallback = (line:String) -> Array, String>>; -#else -typedef ReadlineCompleterCallback = String->Array, String>>; -#end /** Enumeration of possible directions for `Readline.clearLine`. - - @see https://nodejs.org/api/readline.html#readline_readline_clearline_stream_dir_callback **/ enum abstract ClearLineDirection(Int) from Int to Int { /** diff --git a/src/js/node/ReadlinePromises.hx b/src/js/node/ReadlinePromises.hx index 149efb96..2a5066a4 100644 --- a/src/js/node/ReadlinePromises.hx +++ b/src/js/node/ReadlinePromises.hx @@ -23,15 +23,11 @@ package js.node; import haxe.extern.EitherType; +import js.lib.Promise; import js.node.readline.PromisesInterface; import js.node.stream.Readable.IReadable; import js.node.stream.Writable.IWritable; import js.node.web.AbortSignal; -#if haxe4 -import js.lib.Promise; -#else -import js.Promise; -#end /** Completer result: matching entries, then the substring used for matching. @@ -42,18 +38,14 @@ typedef ReadlinePromisesCompleterResult = Array, String Tab autocompletion for `readline/promises`. Unlike the callback API, the completer may return a `Promise`. **/ -#if haxe4 typedef ReadlinePromisesCompleterCallback = (line:String) -> EitherType>; -#else -typedef ReadlinePromisesCompleterCallback = String->EitherType>; -#end /** Options for `ReadlinePromises.createInterface`. Same surface as `ReadlineOptions`, but `completer` may return a `Promise`. - @see https://nodejs.org/api/readline.html#readlinepromisescreateinterfaceoptions + @see https://nodejs.org/docs/latest-v24.x/api/readline.html#readlinepromisescreateinterfaceoptions **/ typedef ReadlinePromisesOptions = { /** @@ -95,7 +87,7 @@ typedef ReadlinePromisesOptions = { /** Delay threshold for treating `\r\n` as a single newline. **/ - @:optional var crlfDelay:Int; + @:optional var crlfDelay:Float; /** If `true`, remove older duplicate history entries. @@ -121,7 +113,7 @@ typedef ReadlinePromisesOptions = { /** The `readline/promises` API provides an alternative set of interfaces that return promises. - @see https://nodejs.org/api/readline.html#promises-api + @see https://nodejs.org/docs/latest-v24.x/api/readline.html#promises-api **/ @:jsRequire("readline/promises") extern class ReadlinePromises { diff --git a/src/js/node/Repl.hx b/src/js/node/Repl.hx index d15e6fbe..08fa0686 100644 --- a/src/js/node/Repl.hx +++ b/src/js/node/Repl.hx @@ -23,132 +23,95 @@ package js.node; import haxe.DynamicAccess; +import js.lib.Error; +import js.lib.Symbol; import js.node.repl.REPLServer; import js.node.stream.Readable.IReadable; import js.node.stream.Writable.IWritable; -#if haxe4 -import js.lib.Error; -import js.lib.Symbol; -#else -import js.Error; -#end /** The `repl` module provides a Read-Eval-Print-Loop (REPL) implementation that is available both as a standalone program or includible in other applications. - @see https://nodejs.org/api/repl.html#repl_repl + @see https://nodejs.org/docs/latest-v24.x/api/repl.html **/ @:jsRequire("repl") extern class Repl { /** The `repl.start()` method creates and starts a `repl.REPLServer` instance. - - @see https://nodejs.org/api/repl.html#repl_repl_start_options **/ @:overload(function(prompt:String):REPLServer {}) - static function start(options:ReplOptions):REPLServer; + static function start(?options:ReplOptions):REPLServer; /** Evaluates expressions in sloppy mode. - - @see https://nodejs.org/api/repl.html#repl_repl_start_options **/ - #if haxe4 static final REPL_MODE_SLOPPY:Symbol; - #else - static var REPL_MODE_SLOPPY(default, never):Dynamic; - #end /** Evaluates expressions in strict mode. This is equivalent to prefacing every repl statement with `'use strict'`. - - @see https://nodejs.org/api/repl.html#repl_repl_start_options **/ - #if haxe4 static final REPL_MODE_STRICT:Symbol; - #else - static var REPL_MODE_STRICT(default, never):Dynamic; - #end } /** Options object used by `Repl.start`. - @see https://nodejs.org/api/repl.html#repl_repl_start_options + @see https://nodejs.org/docs/latest-v24.x/api/repl.html#replstartoptions **/ typedef ReplOptions = { /** - The input prompt to display. - - Default: `'> '` (with a trailing space). + The input prompt to display. Default: `'> '` (with a trailing space). **/ @:optional var prompt:String; /** - The `Readable` stream from which REPL input will be read. - - Default: `process.stdin`. + The `Readable` stream from which REPL input will be read. Default: `process.stdin`. **/ @:optional var input:IReadable; /** - The `Writable` stream to which REPL output will be written. - - Default: `process.stdout`. + The `Writable` stream to which REPL output will be written. Default: `process.stdout`. **/ @:optional var output:IWritable; /** If `true`, specifies that the `output` should be treated as a TTY terminal. - - Default: checking the value of the `isTTY` property on the `output` stream upon instantiation. **/ @:optional var terminal:Bool; /** The function to be used when evaluating each given line of input. - Default: an async wrapper for the JavaScript `eval()` function. + + // TODO(section-5): replace DynamicAccess/Dynamic result with a typed REPL context model **/ - #if haxe4 @:optional var eval:(code:String, context:DynamicAccess, file:String, cb:(error:Null, ?result:Dynamic) -> Void) -> Void; - #else - @:optional var eval:String->DynamicAccess->String->(Null->?Dynamic->Void)->Void; - #end /** If `true`, specifies that the default `writer` function should include ANSI color styling to REPL output. - If a custom `writer` function is provided then this has no effect. - - Default: checking color support on the `output` stream if the REPL instance's `terminal` value is `true`. **/ @:optional var useColors:Bool; /** - If `true`, specifies that the default evaluation function will use the JavaScript `global` as the context as - opposed to creating a new separate context for the REPL instance. - The node CLI REPL sets this value to `true`. - - Default: `false`. + If `true`, specifies that the default evaluation function will use the JavaScript `global` as the context. **/ @:optional var useGlobal:Bool; /** If `true`, specifies that the default writer will not output the return value of a command if it evaluates to `undefined`. - - Default: `false`. **/ @:optional var ignoreUndefined:Bool; /** The function to invoke to format the output of each command before writing to `output`. - Default: `util.inspect()`. + + // TODO(section-5): type writer as (value:Dynamic) -> String **/ - @:optional var writer:Dynamic->Void; + @:optional var writer:Dynamic->Dynamic; /** An optional function used for custom Tab auto completion. @@ -160,17 +123,16 @@ typedef ReplOptions = { (sloppy) mode. Acceptable values are `repl.REPL_MODE_SLOPPY` or `repl.REPL_MODE_STRICT`. **/ - #if haxe4 @:optional var replMode:Symbol; - #else - @:optional var replMode:Dynamic; - #end /** Stop evaluating the current piece of code when `SIGINT` is received, i.e. `Ctrl+C` is pressed. This cannot be used together with a custom `eval` function. - - Default: `false`. **/ @:optional var breakEvalOnSigint:Bool; + + /** + If `true`, show preview of the results when inputting. Default depends on Node version / terminal. + **/ + @:optional var preview:Bool; } diff --git a/src/js/node/V8.hx b/src/js/node/V8.hx index d6138c4b..1deb5211 100644 --- a/src/js/node/V8.hx +++ b/src/js/node/V8.hx @@ -22,43 +22,137 @@ package js.node; +import haxe.Constraints.Function; +import js.node.Buffer; +import js.node.stream.Readable.IReadable; + /** The v8 module exposes APIs that are specific to the version of V8 built into the Node.js binary. - Note: The APIs and implementation are subject to change at any time. + @see https://nodejs.org/docs/latest-v24.x/api/v8.html + + // TODO(section-5): expand Serializer/Deserializer/DefaultSerializer subclasses and CPU/Heap profile handles **/ @:jsRequire("v8") extern class V8 { + /** + Returns an integer representing a version tag derived from the V8 version, command-line flags, + and detected CPU features. This is useful for determining whether a `vm.Script` `cachedData` buffer + is compatible with this instance of V8. + **/ + static function cachedDataVersionTag():Int; + + /** + Returns an object with statistics about the V8 heap spaces. + **/ static function getHeapStatistics():V8HeapStatistics; /** Returns statistics about the V8 heap spaces, i.e. the segments which make up the V8 heap. - Neither the ordering of heap spaces, nor the availability of a heap space can be guaranteed - as the statistics are provided via the V8 `GetHeapSpaceStatistics` function and may change - from one V8 version to the next. **/ static function getHeapSpaceStatistics():Array; /** - This method can be used to programmatically set V8 command line flags. This method should be used with care. - Changing settings after the VM has started may result in unpredictable behavior, including crashes and data loss; - or it may simply do nothing. + Get statistics about code and its metadata in the heap, see V8 `GetHeapCodeAndMetadataStatistics`. + **/ + static function getHeapCodeStatistics():V8HeapCodeStatistics; - The V8 options available for a version of Node.js may be determined by running `node --v8-options`. + /** + Generates a snapshot of the current V8 heap and returns a Readable stream that may be used to + read the JSON serialized representation. + **/ + static function getHeapSnapshot(?options:V8HeapSnapshotOptions):IReadable; + + /** + Generates a snapshot of the current V8 heap and writes it to a JSON file. + Returns the filename where the snapshot was saved. + **/ + @:overload(function(?options:V8HeapSnapshotOptions):String {}) + static function writeHeapSnapshot(?filename:String, ?options:V8HeapSnapshotOptions):String; + + /** + This method can be used to programmatically set V8 command line flags. **/ static function setFlagsFromString(string:String):Void; + + /** + The `v8.stopCoverage()` method allows the user to stop collecting coverage, which was started by + `NODE_V8_COVERAGE`. While `NODE_V8_COVERAGE` can be used for collecting coverage for both + tests (via `node:test`) and application code, this method can be used to stop collecting coverage + on either side of that boundary. + **/ + static function stopCoverage():Void; + + /** + The `v8.takeCoverage()` method allows the user to write the coverage started by `NODE_V8_COVERAGE` + to disk on demand. + **/ + static function takeCoverage():Void; + + /** + This API sets a limit for near-heap-limit callback invocations. + **/ + static function setHeapSnapshotNearHeapLimit(limit:Int):Void; + + /** + Uses a `DefaultSerializer` to serialize `value` into a buffer. + **/ + static function serialize(value:Dynamic):Buffer; + + /** + Uses a `DefaultDeserializer` with default options to read a JS value from a buffer. + **/ + static function deserialize(buffer:Buffer):Dynamic; + + /** + This API will find all objects corresponding to the constructor `ctor`. + **/ + static function queryObjects(ctor:Function, ?options:{format:String}):Dynamic; + + /** + Tracks `Promise` lifecycle callbacks. Prefer `async_hooks` / `diagnostics_channel` for most apps. + + // TODO(section-5): fully type promiseHooks createHook callbacks + **/ + static var promiseHooks(default, null):V8PromiseHooks; + + /** + Startup snapshot builder API (only meaningful when building a snapshot). + **/ + static var startupSnapshot(default, null):V8StartupSnapshot; + + /** + Starts a CPU profile and returns a handle that can be used to stop it. + // TODO(section-5): type CPUProfileHandle / SyncCPUProfileHandle / HeapProfileHandle + **/ + static function startCpuProfile():V8CpuProfileHandle; + + /** + Returns whether the string is stored in one-byte (Latin-1) representation in V8. + **/ + static function isStringOneByteRepresentation(content:String):Bool; } +// TODO(section-5): v8.Serializer / Deserializer / DefaultSerializer / DefaultDeserializer / GCProfiler / getCppHeapStatistics + /** Object returned by `V8.getHeapStatistics` method. **/ typedef V8HeapStatistics = { - var total_heap_size:Int; - var total_heap_size_executable:Int; - var total_physical_size:Int; - var total_available_size:Int; - var used_heap_size:Int; - var heap_size_limit:Int; + var total_heap_size:Float; + var total_heap_size_executable:Float; + var total_physical_size:Float; + var total_available_size:Float; + var used_heap_size:Float; + var heap_size_limit:Float; + @:optional var malloced_memory:Float; + @:optional var peak_malloced_memory:Float; + @:optional var does_zap_garbage:Int; + @:optional var number_of_native_contexts:Float; + @:optional var number_of_detached_contexts:Float; + @:optional var total_global_handles_size:Float; + @:optional var used_global_handles_size:Float; + @:optional var external_memory:Float; } /** @@ -66,8 +160,53 @@ typedef V8HeapStatistics = { **/ typedef V8HeapSpaceStatistics = { var space_name:String; - var space_size:Int; - var space_used_size:Int; - var space_available_size:Int; - var physical_space_size:Int; + var space_size:Float; + var space_used_size:Float; + var space_available_size:Float; + var physical_space_size:Float; +} + +/** + Object returned by `V8.getHeapCodeStatistics` method. +**/ +typedef V8HeapCodeStatistics = { + var code_and_metadata_size:Float; + var bytecode_and_metadata_size:Float; + var external_script_source_size:Float; + @:optional var cpu_profiler_metadata_size:Float; } + +typedef V8HeapSnapshotOptions = { + /** + If true, expose internals in the heap dump. Default: `false`. + **/ + @:optional var exposeInternals:Bool; + + /** + If true, expose numeric values in artificial fields. Default: `false`. + **/ + @:optional var exposeNumericValues:Bool; +} + +typedef V8PromiseHooks = { + function onInit(init:Function):Function; + function onSettled(settled:Function):Function; + function onBefore(before:Function):Function; + function onAfter(after:Function):Function; + function createHook(callbacks:{?init:Function, ?before:Function, ?after:Function, ?settled:Function}):Function; +} + +typedef V8StartupSnapshot = { + function addSerializeCallback(callback:Function, ?data:Dynamic):Void; + function addDeserializeCallback(callback:Function, ?data:Dynamic):Void; + function setDeserializeMainFunction(callback:Function, ?data:Dynamic):Void; + function isBuildingSnapshot():Bool; +} + +/** + Minimal handle returned by `V8.startCpuProfile`. +**/ +typedef V8CpuProfileHandle = { + function stop():String; +} + diff --git a/src/js/node/Vm.hx b/src/js/node/Vm.hx index 2c4bb9f0..4e2cf052 100644 --- a/src/js/node/Vm.hx +++ b/src/js/node/Vm.hx @@ -22,10 +22,12 @@ package js.node; +import haxe.DynamicAccess; +import haxe.extern.EitherType; import js.node.vm.Script; /** - Options object used by Vm.run* methods. + Options object used by `Vm.run*` methods. **/ typedef VmRunOptions = { > ScriptOptions, @@ -33,60 +35,114 @@ typedef VmRunOptions = { } /** - Using this class JavaScript code can be compiled and - run immediately or compiled, saved, and run later. + Options for `Vm.createContext`. **/ -@:jsRequire("vm") -extern class Vm { +typedef VmCreateContextOptions = { /** - Compiles `code`, runs it and returns the result. - Running code does not have access to local scope. + Human-readable name of the newly created context. Default: `'VM Context i'`, where `i` is an ascending + numerical index of the created context. + **/ + @:optional var name:String; - `filename` is optional, it's used only in stack traces. + /** + Origin corresponding to the newly created context for display purposes. The origin should be formatted like a + URL, but with only scheme, host, and port (if necessary). Default: `''`. + **/ + @:optional var origin:String; - In case of syntax error in `code` emits the syntax error to stderr and throws an exception. + /** + If set to `true` any wrappers corresponding to `contextObject` properties may be invoked. Default: `false`. **/ - static function runInThisContext(code:String, ?options:VmRunOptions):Dynamic; + @:optional var codeGeneration:VmCodeGenerationOptions; /** - Compiles `code`, contextifies `sandbox` if passed or creates a new contextified sandbox if it's omitted, - and then runs the code with the sandbox as the global object and returns the result. + If set to `afterEvaluate`, microtasks will be run immediately after evaluating code on the context + (before returning from e.g. `runInContext`). Default: `undefined`. + **/ + @:optional var microtaskMode:String; +} - `runInNewContext` takes the same options as `runInThisContext`. +typedef VmCodeGenerationOptions = { + /** + If set to `false` any calls to `eval` or function constructors (`Function`, `GeneratorFunction`, etc) will throw + an `EvalError`. Default: `true`. + **/ + @:optional var strings:Bool; - Note that running untrusted code is a tricky business requiring great care. `runInNewContext` is quite useful, - but safely running untrusted code requires a separate process. + /** + If set to `false` any attempt to compile a WebAssembly module will throw a `WebAssembly.CompileError`. + Default: `true`. **/ - @:overload(function(code:String, ?sandbox:{}):Dynamic {}) - static function runInNewContext(code:String, sandbox:{}, ?options:VmRunOptions):Dynamic; + @:optional var wasm:Bool; +} +/** + Options for `Vm.compileFunction`. +**/ +typedef VmCompileFunctionOptions = { + > ScriptOptions, /** - Compiles `code`, then runs it in `contextifiedSandbox` and returns the result. + Provides an optional data with V8's code cache data for the supplied source. + **/ + @:optional var parsingContext:VmContext; - Running code does not have access to local scope. The `contextifiedSandbox` object must have been previously - contextified via `createContext`; it will be used as the global object for code. + /** + An array containing context extension objects. Modules and wrappers used in `code` will be available as if + they were referenced from these objects. + **/ + @:optional var contextExtensions:Array>; +} - `runInContext` takes the same options as `runInThisContext`. +/** + Options for experimental `Vm.measureMemory`. +**/ +typedef VmMeasureMemoryOptions = { + /** + `'summary'` or `'detailed'`. Default: `'summary'`. + **/ + @:optional var mode:String; - Note that running untrusted code is a tricky business requiring great care. `runInContext` is quite useful, - but safely running untrusted code requires a separate process. + /** + `'self'` or `'all'`. Default: `'self'`. **/ - static function runInContext(code:String, contextifiedSandbox:VmContext, ?options:VmRunOptions):Dynamic; + @:optional var execution:String; +} +/** + The `vm` module enables compiling and running code within V8 Virtual Machine contexts. + + @see https://nodejs.org/docs/latest-v24.x/api/vm.html + + // TODO(section-5): add vm.Module / SourceTextModule / SyntheticModule externs (large experimental surface) +**/ +@:jsRequire("vm") +extern class Vm { /** + Compiles `code`, runs it and returns the result. + Running code does not have access to local scope. - If given a sandbox object, will "contextify" that sandbox so that it can be used in calls to `runInContext` or - `Script.runInContext`. Inside scripts run as such, sandbox will be the global object, retaining all its existing - properties but also having the built-in objects and functions any standard global object has. Outside of scripts - run by the vm module, sandbox will be unchanged. + // TODO(section-5): Dynamic is intentional for arbitrary JS eval results; refine per-call sites when possible + **/ + static function runInThisContext(code:String, ?options:EitherType):Dynamic; + + /** + Compiles `code`, contextifies `sandbox` if passed or creates a new contextified sandbox if it's omitted, + and then runs the code with the sandbox as the global object and returns the result. + **/ + @:overload(function(code:String, ?sandbox:{}):Dynamic {}) + static function runInNewContext(code:String, sandbox:{}, ?options:VmRunOptions):Dynamic; - If not given a sandbox object, returns a new, empty contextified sandbox object you can use. + /** + Compiles `code`, then runs it in `contextifiedSandbox` and returns the result. + **/ + static function runInContext(code:String, contextifiedSandbox:VmContext, ?options:EitherType):Dynamic; - This function is useful for creating a sandbox that can be used to run multiple scripts, e.g. if you were - emulating a web browser it could be used to create a single sandbox representing a window's global object, - then run all