@reventlessdev/rescript-node 2.0.0-alpha.4 → 2.0.0-alpha.5
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 +7 -0
- package/README.md +243 -0
- package/package.json +1 -1
- package/rescript.json +2 -0
- package/src/NodeImportMeta.res +20 -0
- package/src/NodeImportMeta.res.mjs +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
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.5 (2026-08-13)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **rescript:** bind import.meta so no module reaches for %raw ([c7bd00c](https://github.com/ReventlessDev/reventless-core/commit/c7bd00c0d49ca06cb26a0891a85921219eb87b12))
|
|
11
|
+
|
|
12
|
+
|
|
6
13
|
# 2.0.0-alpha.4 (2026-08-11)
|
|
7
14
|
|
|
8
15
|
**Note:** Version bump only for package @reventlessdev/rescript-node
|
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
package/rescript.json
CHANGED
|
@@ -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"
|