@reventlessdev/rescript-node 2.0.0-alpha.0 → 2.0.0-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,77 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # 2.0.0-alpha.10 (2026-09-08)
7
+
8
+ ### Features
9
+
10
+ * **seed:** a run says which identity its token carries, not which one it asked for ([063da61](https://github.com/ReventlessDev/reventless-core/commit/063da6110a2d89333bd3d99f4c7cb1bc579d78f0))
11
+
12
+
13
+ # 2.0.0-alpha.9 (2026-09-04)
14
+
15
+ ### Features
16
+
17
+ * **local:** serve the event tap over a socket, on by default ([1babf96](https://github.com/ReventlessDev/reventless-core/commit/1babf960f032805418e3ac8f25d03ceb3dcd8d20))
18
+
19
+
20
+ # 2.0.0-alpha.8 (2026-08-18)
21
+
22
+ ### Features
23
+
24
+ * **core:** read a command's lifecycle guard off the GWT corpus, and check [@transition](https://github.com/transition) against it ([057c898](https://github.com/ReventlessDev/reventless-core/commit/057c898761917bd6fc5eb9ac6867c8125709eab4))
25
+
26
+
27
+ # 2.0.0-alpha.7 (2026-08-15)
28
+
29
+ ### Features
30
+
31
+ * **local:** serve ui-hints.json edits without restarting the platform ([b29b104](https://github.com/ReventlessDev/reventless-core/commit/b29b1044f31f620071423c618486daef9692c614))
32
+
33
+
34
+ # 2.0.0-alpha.6 (2026-08-14)
35
+
36
+ ### Features
37
+
38
+ * **local:** let the seed tools address a platform, not a guessed file ([98862ad](https://github.com/ReventlessDev/reventless-core/commit/98862adaa4111f605553e4a78bc52a8a488a4ef3))
39
+
40
+
41
+ # 2.0.0-alpha.5 (2026-08-13)
42
+
43
+ ### Features
44
+
45
+ * **rescript:** bind import.meta so no module reaches for %raw ([c7bd00c](https://github.com/ReventlessDev/reventless-core/commit/c7bd00c0d49ca06cb26a0891a85921219eb87b12))
46
+
47
+
48
+ # 2.0.0-alpha.4 (2026-08-11)
49
+
50
+ **Note:** Version bump only for package @reventlessdev/rescript-node
51
+
52
+
53
+
54
+
55
+
56
+ # 2.0.0-alpha.3 (2026-08-09)
57
+
58
+ ### Features
59
+
60
+ * **core,aws:** bundle a runtime extension's companion packages, guard imports at deploy ([e975175](https://github.com/ReventlessDev/reventless-core/commit/e9751758f51582a8e46db362219f725bb5f1bcde))
61
+
62
+
63
+ # 2.0.0-alpha.2 (2026-08-05)
64
+
65
+ ### Features
66
+
67
+ * **local:** persist the object store beside the SQLite database ([f37e4a1](https://github.com/ReventlessDev/reventless-core/commit/f37e4a1ad3191a97f14f3db7e2ead0e2b27b46c2))
68
+
69
+
70
+ # 2.0.0-alpha.1 (2026-08-02)
71
+
72
+ ### Features
73
+
74
+ * **rescript-node:** add NodeCrypto.sha256Hex convenience ([047d2a2](https://github.com/ReventlessDev/reventless-core/commit/047d2a297cee4738b9daf50b1c988532db8e13df))
75
+
76
+
6
77
  # 2.0.0-alpha.0 (2026-07-31)
7
78
 
8
79
  * feat(rescript)!: one Node bindings package, not two ([1258d8c](https://github.com/ReventlessDev/reventless-core/commit/1258d8c2b2ff2636b36a849fc5bdf9005c6fb0eb))
package/README.md CHANGED
@@ -34,9 +34,252 @@ Add it to your `rescript.json` dependencies:
34
34
 
35
35
  | Module | Binds |
36
36
  |---|---|
37
+ | `NodeBuffer` | the global `Buffer` |
38
+ | `NodeChildProcess` | `node:child_process` |
39
+ | `NodeCrypto` | `node:crypto` |
40
+ | `NodeFs` | `node:fs`, plus `node:fs/promises` as `NodeFs.Promises` |
41
+ | `NodeImportMeta` | `import.meta` |
42
+ | `NodeModule` | `node:module` |
43
+ | `NodeOs` | `node:os` |
44
+ | `NodePath` | `node:path` |
45
+ | `NodeProcess` | the `process` global |
37
46
  | `NodeStreams` | `node:stream`, plus the stream-shaped parts of `node:fs` and `node:readline` |
47
+ | `NodeUrl` | `node:url` |
38
48
  | `NodeZlib` | `node:zlib` |
39
49
 
50
+ Every module specifier is `node:`-prefixed. Bare `"fs"` is what bundlers alias to a browser polyfill
51
+ shim, and this repository bundles Lambda code archives; `node:fs` is unambiguous to Node and to every
52
+ bundler.
53
+
54
+ Coverage is demand-driven: these bind what the Reventless packages actually call, not the whole of
55
+ each Node module. Where a Node API has an encoding or option argument that is only ever passed one
56
+ way here, the binding bakes it in rather than accepting it — `NodeFs.readFileSync` is UTF-8 by
57
+ construction, so no call site can pass `"utf-8"` and get a different, silently wrong, encoding.
58
+
59
+ ---
60
+
61
+ ## `NodeBuffer`
62
+
63
+ `t` aliases `Uint8Array.t` rather than being abstract: a Node `Buffer` *is* a `Uint8Array` subclass,
64
+ so stream chunks, `NodeFs.readFileSyncBuffer` results and `NodeFs.writeFileSyncBuffer` arguments are
65
+ all the same values and flow between those calls without a cast.
66
+
67
+ ```rescript
68
+ type t = Uint8Array.t
69
+
70
+ let concat: array<t> => t // assemble a body from its `data` chunks
71
+ let fromStringUtf8: string => t
72
+ let toStringUtf8: t => string
73
+ ```
74
+
75
+ ---
76
+
77
+ ## `NodeChildProcess`
78
+
79
+ ```rescript
80
+ type execOptions = {cwd?: string, encoding?: string, env?: dict<string>, stdio?: array<string>, maxBuffer?: int}
81
+
82
+ let execSync: (string, execOptions) => string
83
+ let execFileSync: (string, array<string>, execOptions) => string
84
+
85
+ type childProcess
86
+ type spawnOptions = {cwd?: string, env?: dict<string>, stdio?: array<string>}
87
+
88
+ let spawn: (string, array<string>, spawnOptions) => childProcess
89
+ let exitCode: childProcess => Nullable.t<int>
90
+ let kill: (childProcess, string) => bool
91
+ ```
92
+
93
+ Prefer `execFileSync` over `execSync`: it takes the arguments as an array rather than interpolating
94
+ them into a shell string, so an argument containing shell metacharacters stays one argument.
95
+
96
+ `spawn` returns while the child is still alive, which is the reason to reach for it over
97
+ `execFileSync`. `exitCode` is `null` until the child exits — the one honest way to ask "is it still
98
+ alive?" without holding an event listener, so a caller polling for readiness can tell a slow start
99
+ from a process that already died. `kill` returns whether the signal was delivered; `false` means the
100
+ process was already gone, which is not an error.
101
+
102
+ `spawnOptions.env` **replaces** the child's environment rather than extending it. Pass a copy of
103
+ `NodeProcess.env` with the additions applied when the child still needs `PATH` and friends.
104
+
105
+ ---
106
+
107
+ ## `NodeCrypto`
108
+
109
+ ```rescript
110
+ type buffer // abstract: only ever re-keyed or stringified
111
+ let bufferToString: (buffer, string) => string
112
+
113
+ type hash
114
+ let createHash: string => hash
115
+ let hashUpdate: (hash, string) => hash
116
+ let hashUpdateBuffer: (hash, Uint8Array.t) => hash
117
+ let hashDigest: (hash, string) => string
118
+ let sha256Hex: string => string // SHA-256 of a UTF-8 string, hex-encoded
119
+
120
+ type hmac
121
+ let createHmac: (string, string) => hmac
122
+ let createHmacFromBuffer: (string, buffer) => hmac
123
+ let hmacUpdate: (hmac, string) => hmac
124
+ let hmacDigest: (hmac, string) => string
125
+ let hmacDigestBuffer: hmac => buffer
126
+
127
+ let randomBytes: int => buffer
128
+ let randomUUID: unit => string
129
+ ```
130
+
131
+ `sha256Hex` is the content hash content-addressed stores key their objects on (`sha256/<hash>`): the
132
+ same bytes always yield the same digest, so an upload is idempotent and deduplicating.
133
+ `createHmacFromBuffer` is the chained form — each round of an AWS SigV4 signing key takes the
134
+ previous round's raw digest as its key.
135
+
136
+ ---
137
+
138
+ ## `NodeFs`
139
+
140
+ ```rescript
141
+ let existsSync: string => bool
142
+ let realpathSync: string => string
143
+
144
+ let readFileSync: string => string // UTF-8 baked in
145
+ let readFileSyncBuffer: string => Uint8Array.t
146
+ let writeFileSync: (string, string) => unit // UTF-8 baked in
147
+ let writeFileSyncBuffer: (string, Uint8Array.t) => unit
148
+
149
+ type dirent
150
+ let isDirectory: dirent => bool
151
+ let isFile: dirent => bool
152
+ let direntName: dirent => string
153
+
154
+ type readdirOptions = {withFileTypes: bool}
155
+ type mkdirOptions = {recursive?: bool}
156
+ type rmOptions = {recursive?: bool, force?: bool}
157
+ type cpOptions = {recursive?: bool}
158
+
159
+ let readdirSync: (string, readdirOptions) => array<dirent>
160
+ let mkdirSync: (string, mkdirOptions) => unit
161
+ let mkdtempSync: string => string
162
+ let unlinkSync: string => unit
163
+ let rmSync: (string, rmOptions) => unit
164
+ let cpSync: (string, string, cpOptions) => unit
165
+
166
+ module Promises = {
167
+ let writeFile: (string, string) => promise<unit>
168
+ let mkdir: (string, mkdirOptions) => promise<Nullable.t<string>>
169
+ let rm: (string, rmOptions) => promise<unit>
170
+ }
171
+ ```
172
+
173
+ The `*Buffer` variants are the same Node calls without an encoding, which is what makes Node return
174
+ raw bytes rather than a decoded string. `Promises` is a separate module because `node:fs/promises` is
175
+ a separate specifier — not a wrapper this package adds over the sync calls.
176
+
177
+ ---
178
+
179
+ ## `NodeImportMeta`
180
+
181
+ ```rescript
182
+ let url: string // import.meta.url
183
+ let dirname: string // import.meta.dirname
184
+ let filename: string // import.meta.filename
185
+ ```
186
+
187
+ These resolve to the location of the module that *reads* them, which is what makes binding a
188
+ per-module value in a shared package sound at all: `@val` externals are inlined at the use site
189
+ rather than re-exported by this one. The corollary is that there are no helpers here — a function
190
+ defined in this module would report *this* file's location, so compute from the values at the call
191
+ site instead.
192
+
193
+ ```rescript
194
+ // The file sitting next to the module that asks for it.
195
+ let hintsFile = NodePath.resolve([NodeImportMeta.dirname, "../ui-hints.json"])
196
+ ```
197
+
198
+ `dirname` and `filename` are Node's own additions to `import.meta` and are defined only for `file:`
199
+ URLs; a bundler that rewrites modules to CommonJS drops them. `url` is the portable form and the one
200
+ to reach for when either could apply.
201
+
202
+ ---
203
+
204
+ ## `NodeModule`
205
+
206
+ ```rescript
207
+ type require
208
+ let createRequire: string => require
209
+ let requireResolve: (require, string) => string
210
+ let builtinModules: array<string>
211
+ ```
212
+
213
+ `createRequire` is how an ESM module gets at CommonJS resolution, which is what `require.resolve` is
214
+ wanted for: locating a dependency's on-disk path without importing it. `builtinModules` lists Node's
215
+ built-ins, both bare and `node:`-only entries such as `node:test`, plus subpath forms like
216
+ `fs/promises`.
217
+
218
+ ---
219
+
220
+ ## `NodeOs`
221
+
222
+ ```rescript
223
+ let tmpdir: unit => string
224
+ ```
225
+
226
+ ---
227
+
228
+ ## `NodePath`
229
+
230
+ ```rescript
231
+ let join: array<string> => string // variadic
232
+ let resolve: array<string> => string // variadic
233
+ let dirname: string => string
234
+ let basename: string => string
235
+ let relative: (string, string) => string
236
+ let sep: string
237
+ ```
238
+
239
+ `join` and `resolve` are variadic, a strict superset of the two-argument forms they replace, so no
240
+ call site loses expressiveness. A two-argument `join` and a variadic one are not duplicates — they
241
+ are different functions with the same name, and which one a call site got used to depend on which
242
+ inline binding block it happened to sit near.
243
+
244
+ ---
245
+
246
+ ## `NodeProcess`
247
+
248
+ ```rescript
249
+ let argv: array<string>
250
+ let env: dict<string>
251
+ let cwd: unit => string
252
+ let chdir: string => unit
253
+ let exit: int => unit
254
+
255
+ type stream
256
+ let stdin: stream
257
+ let stdout: stream
258
+ let write: (stream, string) => unit
259
+ let pause: stream => unit
260
+ let unref: stream => unit
261
+ let isTTY: stream => option<bool>
262
+ ```
263
+
264
+ `process` is a global rather than a module specifier, so these are `@val` bindings under
265
+ `@scope("process")`.
266
+
267
+ `isTTY` is `option<bool>`, not `bool`: Node sets it to `true` on an interactive stream and leaves it
268
+ **undefined** otherwise — it is never `false`. A `bool`-typed binding reads that undefined as a valid
269
+ `false`, which happens to work and is still lying about the value.
270
+
271
+ ---
272
+
273
+ ## `NodeUrl`
274
+
275
+ ```rescript
276
+ let fileURLToPath: string => string
277
+ let pathToFileURL: string => {"href": string}
278
+ ```
279
+
280
+ Only the two path/URL converters. The `URL` class itself is a WHATWG global rather than a `node:`
281
+ import, so it belongs to `rescript-web`, not here.
282
+
40
283
  ---
41
284
 
42
285
  ## `NodeStreams`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/rescript-node",
3
- "version": "2.0.0-alpha.0",
3
+ "version": "2.0.0-alpha.10",
4
4
  "description": "ReScript bindings for the Node.js standard library",
5
5
  "license": "Apache-2.0",
6
6
  "devDependencies": {
package/rescript.json CHANGED
@@ -15,9 +15,11 @@
15
15
  "dir": "src",
16
16
  "subdirs": true,
17
17
  "public": [
18
+ "NodeBuffer",
18
19
  "NodeChildProcess",
19
20
  "NodeCrypto",
20
21
  "NodeFs",
22
+ "NodeImportMeta",
21
23
  "NodeModule",
22
24
  "NodeOs",
23
25
  "NodePath",
@@ -0,0 +1,30 @@
1
+ /** Bindings for Node's global [`Buffer`](https://nodejs.org/api/buffer.html).
2
+
3
+ `t` aliases `Uint8Array.t` rather than being abstract: a Node `Buffer` *is* a
4
+ `Uint8Array` subclass, so the bytes a stream hands over, the bytes
5
+ {!NodeFs.readFileSyncBuffer} returns, and the bytes {!NodeFs.writeFileSyncBuffer}
6
+ takes are all the same values. Aliasing lets them flow between those calls
7
+ without a cast that would exist only to satisfy the type checker. */
8
+ type t = Uint8Array.t
9
+
10
+ /** Joins byte chunks into one buffer — how a request body is assembled from the
11
+ chunks its `data` events deliver. */
12
+ @val @scope("Buffer")
13
+ external concat: array<t> => t = "concat"
14
+
15
+ /** Bakes the encoding in for the same reason {!NodeFs.readFileSync} does: the
16
+ `(string, string)` form permits a different, silently wrong, encoding name. */
17
+ @val @scope("Buffer")
18
+ external fromStringUtf8: (string, @as("utf8") _) => t = "from"
19
+
20
+ /** Base64url, the alphabet a bearer token's segments are encoded in. Decoding
21
+ never throws — a segment that is not base64url yields whatever bytes it can,
22
+ so callers judge the result by whether it parses. */
23
+ @val @scope("Buffer")
24
+ external fromStringBase64Url: (string, @as("base64url") _) => t = "from"
25
+
26
+ @send
27
+ external toStringUtf8: (t, @as("utf8") _) => string = "toString"
28
+
29
+ @send
30
+ external toStringBase64Url: (t, @as("base64url") _) => string = "toString"
@@ -0,0 +1,2 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+ /* This output is empty. Its source's type definitions, externals and/or unused code got optimized away. */
@@ -16,3 +16,32 @@ external execSync: (string, execOptions) => string = "execSync"
16
16
  string, so an argument containing shell metacharacters stays one argument. */
17
17
  @module("node:child_process")
18
18
  external execFileSync: (string, array<string>, execOptions) => string = "execFileSync"
19
+
20
+ /** A running child process. Unlike the `*Sync` calls above, `spawn` returns
21
+ while the child is still alive, so the caller keeps working alongside it —
22
+ which is the whole reason to reach for this over `execFileSync`. */
23
+ type childProcess
24
+
25
+ type spawnOptions = {
26
+ cwd?: string,
27
+ /** Replaces the child's environment entirely rather than extending it. Pass
28
+ a copy of `NodeProcess.env` with the additions applied when the child
29
+ still needs PATH and friends. */
30
+ env?: dict<string>,
31
+ /** Per-descriptor disposition: `"ignore"`, `"inherit"`, or `"pipe"`, in
32
+ stdin/stdout/stderr order. */
33
+ stdio?: array<string>,
34
+ }
35
+
36
+ @module("node:child_process")
37
+ external spawn: (string, array<string>, spawnOptions) => childProcess = "spawn"
38
+
39
+ /** `null` while the child is running, its exit code once it has exited. The
40
+ one honest way to ask "is it still alive?" without holding an event
41
+ listener — a caller polling for readiness checks this to tell a slow start
42
+ from a process that already died. */
43
+ @get external exitCode: childProcess => Nullable.t<int> = "exitCode"
44
+
45
+ /** Signal the child. Returns whether the signal was delivered — `false` once
46
+ the process is already gone, which is not an error. */
47
+ @send external kill: (childProcess, string) => bool = "kill"
@@ -17,6 +17,12 @@ external createHash: string => hash = "createHash"
17
17
  @send external hashUpdateBuffer: (hash, Uint8Array.t) => hash = "update"
18
18
  @send external hashDigest: (hash, string) => string = "digest"
19
19
 
20
+ /** SHA-256 of a UTF-8 string, hex-encoded. The content hash content-addressed
21
+ stores key their objects on (`sha256/<hash>`): the same bytes always yield the
22
+ same digest, so an upload is idempotent and deduplicating. */
23
+ let sha256Hex = (input: string): string =>
24
+ createHash("sha256")->hashUpdate(input)->hashDigest("hex")
25
+
20
26
  // ── HMAC ─────────────────────────────────────────────────────────────────────
21
27
 
22
28
  type hmac
@@ -1,2 +1,12 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
- /* This output is empty. Its source's type definitions, externals and/or unused code got optimized away. */
2
+
3
+ import * as Nodecrypto from "node:crypto";
4
+
5
+ function sha256Hex(input) {
6
+ return Nodecrypto.createHash("sha256").update(input).digest("hex");
7
+ }
8
+
9
+ export {
10
+ sha256Hex,
11
+ }
12
+ /* node:crypto Not a pure module */
package/src/NodeFs.res CHANGED
@@ -17,6 +17,15 @@ external existsSync: string => bool = "existsSync"
17
17
  @module("node:fs")
18
18
  external realpathSync: string => string = "realpathSync"
19
19
 
20
+ /** Set a file's access and modification times, in seconds since the epoch.
21
+
22
+ The reason this exists here rather than as a shell-out to `touch`: a build
23
+ whose compiler caches on source mtime cannot be made to re-run a side effect
24
+ of compilation — emitting a sidecar, say — by any argument passed to it. The
25
+ input has to look newer. */
26
+ @module("node:fs")
27
+ external utimesSync: (string, float, float) => unit = "utimesSync"
28
+
20
29
  // ── Reading ──────────────────────────────────────────────────────────────────
21
30
 
22
31
  @module("node:fs")
@@ -32,6 +41,40 @@ external readFileSyncBuffer: string => Uint8Array.t = "readFileSync"
32
41
  @module("node:fs")
33
42
  external writeFileSync: (string, string, @as("utf8") _) => unit = "writeFileSync"
34
43
 
44
+ /** The byte-oriented companion to {!writeFileSync}, mirroring
45
+ {!readFileSyncBuffer}: no encoding to bake in, because the payload is
46
+ already bytes. */
47
+ @module("node:fs")
48
+ external writeFileSyncBuffer: (string, Uint8Array.t) => unit = "writeFileSync"
49
+
50
+ // ── Watching ─────────────────────────────────────────────────────────────────
51
+
52
+ /** A handle from {!watch}. Held so it can be closed, and so it can be `unref`ed
53
+ — an active watcher keeps the event loop alive, which turns a stray watch in
54
+ a test into a run that never exits. */
55
+ type watcher
56
+
57
+ /** Stop watching. */
58
+ @send external watcherClose: watcher => unit = "close"
59
+
60
+ /** Take the watcher off the event loop's reference count, so it never by itself
61
+ keeps the process running. Returns the same watcher, as Node does. */
62
+ @send external watcherUnref: watcher => watcher = "unref"
63
+
64
+ /** [`fs.watch`](https://nodejs.org/api/fs.html#fswatchfilename-options-listener).
65
+
66
+ The listener takes the event type (`"rename"` or `"change"`) and the
67
+ basename, which Node may report as null on some platforms — hence
68
+ `Nullable.t`.
69
+
70
+ **Watch the directory, not the file**, when the file is one an editor
71
+ writes: a save that replaces rather than rewrites (write-temp-then-rename,
72
+ which is how most editors save atomically) gives the path a new inode, and a
73
+ watcher registered on the old one goes silent with no error. Watching the
74
+ containing directory and filtering on the basename survives that. */
75
+ @module("node:fs")
76
+ external watch: (string, (string, Nullable.t<string>) => unit) => watcher = "watch"
77
+
35
78
  // ── Directories ──────────────────────────────────────────────────────────────
36
79
 
37
80
  type dirent
@@ -0,0 +1,20 @@
1
+ /** Bindings for [`import.meta`](https://nodejs.org/api/esm.html#importmeta).
2
+
3
+ `@val` externals are inlined at the use site rather than re-exported by this
4
+ module, so each of these resolves to the location of the module that *reads*
5
+ it — which is the only reason binding a per-module value from a shared
6
+ package is sound. Reading one through a wrapper function defined here would
7
+ return this file's location instead, so there are no helpers below.
8
+
9
+ `dirname` and `filename` are Node's own additions and are defined only for
10
+ `file:` URLs; a bundler that rewrites modules to CommonJS drops them. `url`
11
+ is the portable form and the one to reach for when either could apply. */
12
+
13
+ @val @scope(("import", "meta"))
14
+ external url: string = "url"
15
+
16
+ @val @scope(("import", "meta"))
17
+ external dirname: string = "dirname"
18
+
19
+ @val @scope(("import", "meta"))
20
+ external filename: string = "filename"
@@ -0,0 +1,2 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+ /* This output is empty. Its source's type definitions, externals and/or unused code got optimized away. */
@@ -10,3 +10,8 @@ type require
10
10
  external createRequire: string => require = "createRequire"
11
11
 
12
12
  @send external requireResolve: (require, string) => string = "resolve"
13
+
14
+ /** The names of Node's built-in modules (both bare and some `node:`-only
15
+ entries like `node:test`), plus subpath forms such as `fs/promises`. */
16
+ @module("node:module")
17
+ external builtinModules: array<string> = "builtinModules"
@@ -0,0 +1,45 @@
1
+ /** Bindings for [`node:net`](https://nodejs.org/api/net.html) — TCP servers and
2
+ the sockets they accept. */
3
+
4
+ type socket
5
+ type server
6
+
7
+ /** A connected client. `write` returns the backpressure flag, which a caller
8
+ that only ever sends short lines can ignore. */
9
+ @send external write: (socket, string) => bool = "write"
10
+ @send external destroySocket: socket => unit = "destroy"
11
+
12
+ /** `setEncoding` makes the `data` frames arrive as strings rather than Buffers,
13
+ which is what a line-oriented consumer wants. */
14
+ @send external setEncoding: (socket, string) => unit = "setEncoding"
15
+
16
+ @send external onSocketData: (socket, @as("data") _, string => unit) => unit = "on"
17
+ @send external onSocketClose: (socket, @as("close") _, unit => unit) => unit = "on"
18
+
19
+ /** A socket that errors is gone; without a listener the error is thrown and
20
+ takes the process with it, so every accepted socket needs one. */
21
+ @send external onSocketError: (socket, @as("error") _, JsExn.t => unit) => unit = "on"
22
+
23
+ /** `keepAlive` is deliberately absent: a caller that wants it sets it per
24
+ socket, and defaulting it changes the behaviour of every consumer. */
25
+ @module("node:net")
26
+ external createServer: (socket => unit) => server = "createServer"
27
+
28
+ /** Binding to an explicit host is what keeps a server off every interface —
29
+ pass `"127.0.0.1"` for loopback-only. */
30
+ @send external listen: (server, int, string, unit => unit) => unit = "listen"
31
+
32
+ @send external onServerError: (server, @as("error") _, JsExn.t => unit) => unit = "on"
33
+ @send external closeServer: (server, unit => unit) => unit = "close"
34
+
35
+ /** The port actually bound — the only way to learn it after listening on `0`.
36
+ `Nullable`, because a server that is not listening reports `null`. */
37
+ @send external address: server => Nullable.t<{"port": int}> = "address"
38
+
39
+ /** Lets the process exit while the server is still listening — a diagnostic
40
+ listener must not be the reason a CLI hangs at the end of its work. */
41
+ @send external unref: server => unit = "unref"
42
+
43
+ /** Connect to a listening server. The callback fires once the connection is up. */
44
+ @module("node:net")
45
+ external connect: (int, string, unit => unit) => socket = "createConnection"
@@ -0,0 +1,2 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+ /* This output is empty. Its source's type definitions, externals and/or unused code got optimized away. */
@@ -21,6 +21,30 @@ external chdir: string => unit = "chdir"
21
21
  @val @scope("process")
22
22
  external exit: int => unit = "exit"
23
23
 
24
+ @val @scope("process")
25
+ external pid: int = "pid"
26
+
27
+ /** Signalling another process — or, with signal `0`, asking whether it is still
28
+ there without disturbing it. Throws when the pid is gone (`ESRCH`) or not
29
+ ours to signal (`EPERM`), so a liveness check is a `try`. */
30
+ @val @scope("process")
31
+ external kill: (int, int) => unit = "kill"
32
+
33
+ /** `process.on` for the two shutdown paths a CLI has to clean up on.
34
+
35
+ Split into an `exit` binding and a signal binding because the handlers are
36
+ not interchangeable: `exit` fires with the exit code and may only do
37
+ synchronous work, while a signal handler receives the signal name and fires
38
+ *before* the process is committed to leaving — code that registers one and
39
+ expects the other's timing gets a cleanup that never runs. Signals are a
40
+ polyvariant so a typo is a compile error rather than a handler that is never
41
+ called. */
42
+ @val @scope("process")
43
+ external onExit: (@as("exit") _, int => unit) => unit = "on"
44
+
45
+ @val @scope("process")
46
+ external onSignal: ([#SIGINT | #SIGTERM | #SIGHUP], unit => unit) => unit = "on"
47
+
24
48
  /** The standard streams. `write`, `isTTY`, `pause` and `unref` are what the
25
49
  interactive prompts in this repository reach for; the type is abstract so it
26
50
  can also be handed to `readline.createInterface`. */