@reventlessdev/rescript-node 2.0.0-alpha.0
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 +21 -0
- package/LICENSE +202 -0
- package/README.md +288 -0
- package/package.json +26 -0
- package/rescript.json +32 -0
- package/src/NodeChildProcess.res +18 -0
- package/src/NodeChildProcess.res.mjs +2 -0
- package/src/NodeCrypto.res +42 -0
- package/src/NodeCrypto.res.mjs +2 -0
- package/src/NodeFs.res +83 -0
- package/src/NodeFs.res.mjs +9 -0
- package/src/NodeModule.res +12 -0
- package/src/NodeModule.res.mjs +2 -0
- package/src/NodeOs.res +4 -0
- package/src/NodeOs.res.mjs +2 -0
- package/src/NodePath.res +25 -0
- package/src/NodePath.res.mjs +2 -0
- package/src/NodeProcess.res +43 -0
- package/src/NodeProcess.res.mjs +2 -0
- package/src/NodeStreams.res +129 -0
- package/src/NodeStreams.res.mjs +24 -0
- package/src/NodeUrl.res +10 -0
- package/src/NodeUrl.res.mjs +2 -0
- package/src/NodeZlib.res +75 -0
- package/src/NodeZlib.res.mjs +22 -0
- package/src/example/ZlibExample.res +20 -0
- package/src/example/ZlibExample.res.mjs +19 -0
- package/src/example/test.txt.gz +0 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
|
+
|
|
6
|
+
# 2.0.0-alpha.0 (2026-07-31)
|
|
7
|
+
|
|
8
|
+
* feat(rescript)!: one Node bindings package, not two ([1258d8c](https://github.com/ReventlessDev/reventless-core/commit/1258d8c2b2ff2636b36a849fc5bdf9005c6fb0eb))
|
|
9
|
+
|
|
10
|
+
### BREAKING CHANGES
|
|
11
|
+
|
|
12
|
+
* `@reventlessdev/rescript-node-streams` and
|
|
13
|
+
`@reventlessdev/rescript-node-zlib` are replaced by
|
|
14
|
+
`@reventlessdev/rescript-node`. Module names are unchanged.
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
This package continues the version line of `@reventlessdev/rescript-node-streams` (1.1.0-alpha.13)
|
|
19
|
+
and `@reventlessdev/rescript-node-zlib` (1.1.0-alpha.14), which it replaces. Their changelogs up to
|
|
20
|
+
those versions are the history of the modules `NodeStreams` and `NodeZlib`; both module names are
|
|
21
|
+
unchanged, so the merge is a dependency swap for consumers.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright 2019-2026 Martin Lorenz
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
[](https://www.npmjs.com/package/@reventlessdev/rescript-node)
|
|
2
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
3
|
+
[](https://docs.reventless.dev)
|
|
4
|
+
|
|
5
|
+
# @reventlessdev/rescript-node
|
|
6
|
+
|
|
7
|
+
> ⚠️ **Alpha.** APIs can change without notice between releases.
|
|
8
|
+
> Pin exact versions and expect breaking changes.
|
|
9
|
+
|
|
10
|
+
ReScript bindings for the [Node.js](https://nodejs.org) standard library.
|
|
11
|
+
|
|
12
|
+
This package has **no dependencies** and performs **no work at module scope** — it is safe to import
|
|
13
|
+
from a Lambda entry point without pulling anything into the runtime module graph.
|
|
14
|
+
|
|
15
|
+
It replaces `@reventlessdev/rescript-node-streams` and `@reventlessdev/rescript-node-zlib`, which
|
|
16
|
+
were one binding surface published as two artifacts. The package is `namespace: false`, so the module
|
|
17
|
+
names are unchanged: code that used `NodeStreams` or `NodeZlib` needs only a dependency swap.
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pnpm add @reventlessdev/rescript-node
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Add it to your `rescript.json` dependencies:
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"dependencies": ["@reventlessdev/rescript-node"]
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Modules
|
|
34
|
+
|
|
35
|
+
| Module | Binds |
|
|
36
|
+
|---|---|
|
|
37
|
+
| `NodeStreams` | `node:stream`, plus the stream-shaped parts of `node:fs` and `node:readline` |
|
|
38
|
+
| `NodeZlib` | `node:zlib` |
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## `NodeStreams`
|
|
43
|
+
|
|
44
|
+
### Stream types
|
|
45
|
+
|
|
46
|
+
- **`Readable.t`** — readable streams (file read streams, HTTP requests)
|
|
47
|
+
- **`Writable.t`** — writable streams (file write streams, HTTP responses)
|
|
48
|
+
- **`Transform.t`** — transform streams (streams that modify data as it passes through)
|
|
49
|
+
- **`Duplex.t`** — duplex streams (both readable and writable)
|
|
50
|
+
|
|
51
|
+
### Readable streams
|
|
52
|
+
|
|
53
|
+
```rescript
|
|
54
|
+
module Readable = {
|
|
55
|
+
type t = readableStream
|
|
56
|
+
|
|
57
|
+
// Piping
|
|
58
|
+
let pipe: (t, writableStream) => writableStream
|
|
59
|
+
|
|
60
|
+
// Encoding
|
|
61
|
+
let setEncoding: (t, string) => t
|
|
62
|
+
|
|
63
|
+
// Event handlers
|
|
64
|
+
let onDataFromBuffer: (t, @as("data") _, buffer => unit) => t
|
|
65
|
+
let onDataFromString: (t, @as("data") _, string => unit) => t
|
|
66
|
+
let onDataFromArbitary: (t, @as("data") _, 'd => unit) => t
|
|
67
|
+
let onEnd: (t, @as("end") _, unit => unit) => t
|
|
68
|
+
let onReadable: (t, @as("readable") _, unit => unit) => t
|
|
69
|
+
let onError: (t, @as("error") _, JsExn.t => unit) => t
|
|
70
|
+
let onClose: (t, @as("close") _, unit => unit) => t
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Key events:**
|
|
75
|
+
- `data` — emitted when data is available to read
|
|
76
|
+
- `end` — emitted when no more data is available
|
|
77
|
+
- `readable` — emitted when data is ready to be read
|
|
78
|
+
- `error` — emitted on errors
|
|
79
|
+
- `close` — emitted when the stream is closed
|
|
80
|
+
|
|
81
|
+
### Writable streams
|
|
82
|
+
|
|
83
|
+
```rescript
|
|
84
|
+
module Writable = {
|
|
85
|
+
type t = writableStream
|
|
86
|
+
|
|
87
|
+
// Operations
|
|
88
|
+
let close: t => unit
|
|
89
|
+
let pipe: (t, t) => t
|
|
90
|
+
|
|
91
|
+
// Event handlers
|
|
92
|
+
let onData: (t, @as("data") _, 'd => unit) => t
|
|
93
|
+
let onDrain: (t, @as("drain") _, unit => unit) => t
|
|
94
|
+
let onFinish: (t, @as("finish") _, unit => unit) => t
|
|
95
|
+
let onPipe: (t, @as("pipe") _, readableStream => unit) => t
|
|
96
|
+
let onUnpipe: (t, @as("unpipe") _, readableStream => unit) => t
|
|
97
|
+
let onError: (t, @as("error") _, JsExn.t => unit) => t
|
|
98
|
+
let onClose: (t, @as("close") _, unit => unit) => t
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Key events:**
|
|
103
|
+
- `drain` — emitted when it is safe to resume writing after `write()` returns false
|
|
104
|
+
- `finish` — emitted after `end()` is called and all data is flushed
|
|
105
|
+
- `pipe` — emitted when a readable stream pipes into this writable
|
|
106
|
+
- `unpipe` — emitted when `unpipe()` is called on a readable stream
|
|
107
|
+
|
|
108
|
+
### Transform streams
|
|
109
|
+
|
|
110
|
+
```rescript
|
|
111
|
+
module Transform = {
|
|
112
|
+
type t
|
|
113
|
+
let pipe: (t, readableStream) => writableStream
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### File-system streams
|
|
118
|
+
|
|
119
|
+
```rescript
|
|
120
|
+
let createReadStream: string => Readable.t
|
|
121
|
+
let createWriteStream: string => Writable.t
|
|
122
|
+
let unlink: string => promise<unit>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Pipeline
|
|
126
|
+
|
|
127
|
+
The `pipeline` functions pipe streams together with automatic error handling and cleanup:
|
|
128
|
+
|
|
129
|
+
```rescript
|
|
130
|
+
// Direct pipe (no transforms)
|
|
131
|
+
let pipeline0: (Readable.t, Writable.t) => promise<unit>
|
|
132
|
+
|
|
133
|
+
// With 1 transform
|
|
134
|
+
let pipeline: (Readable.t, Transform.t, Writable.t) => promise<unit>
|
|
135
|
+
|
|
136
|
+
// With 2 transforms
|
|
137
|
+
let pipeline2: (Readable.t, Transform.t, Transform.t, Writable.t) => promise<unit>
|
|
138
|
+
|
|
139
|
+
// With 3 transforms
|
|
140
|
+
let pipeline3: (Readable.t, Transform.t, Transform.t, Transform.t, Writable.t) => promise<unit>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Readline
|
|
144
|
+
|
|
145
|
+
```rescript
|
|
146
|
+
module Readline = {
|
|
147
|
+
type t
|
|
148
|
+
type options = {input: Readable.t}
|
|
149
|
+
|
|
150
|
+
let createInterface: options => t
|
|
151
|
+
let onLine: (t, @as("line") _, string => unit) => t
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Examples
|
|
156
|
+
|
|
157
|
+
#### File copy with piping
|
|
158
|
+
|
|
159
|
+
```rescript
|
|
160
|
+
open NodeStreams
|
|
161
|
+
|
|
162
|
+
let copyFile = (source, dest) => {
|
|
163
|
+
let readStream = createReadStream(source)
|
|
164
|
+
let writeStream = createWriteStream(dest)
|
|
165
|
+
|
|
166
|
+
readStream
|
|
167
|
+
->Readable.pipe(writeStream)
|
|
168
|
+
->Writable.onFinish(() => Console.log("Copy completed"))
|
|
169
|
+
->Writable.onError(err => Console.log2("Error:", err))
|
|
170
|
+
->ignore
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
#### Reading a file line by line
|
|
175
|
+
|
|
176
|
+
```rescript
|
|
177
|
+
open NodeStreams
|
|
178
|
+
|
|
179
|
+
let processFileLines = filename => {
|
|
180
|
+
let readStream = createReadStream(filename)
|
|
181
|
+
let rl = Readline.createInterface({input: readStream})
|
|
182
|
+
|
|
183
|
+
rl->Readline.onLine(line => Console.log(line))->ignore
|
|
184
|
+
|
|
185
|
+
readStream
|
|
186
|
+
->Readable.onEnd(() => Console.log("Finished reading file"))
|
|
187
|
+
->Readable.onError(err => Console.log2("Error:", err))
|
|
188
|
+
->ignore
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
#### Reading data with an encoding
|
|
193
|
+
|
|
194
|
+
```rescript
|
|
195
|
+
open NodeStreams
|
|
196
|
+
|
|
197
|
+
let readTextFile = filename =>
|
|
198
|
+
createReadStream(filename)
|
|
199
|
+
->Readable.setEncoding("utf8")
|
|
200
|
+
->Readable.onDataFromString(chunk => Console.log2("Received chunk:", chunk))
|
|
201
|
+
->Readable.onEnd(() => Console.log("File read complete"))
|
|
202
|
+
->Readable.onError(err => Console.log2("Error reading file:", err))
|
|
203
|
+
->ignore
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
#### Safe composition with `pipeline`
|
|
207
|
+
|
|
208
|
+
```rescript
|
|
209
|
+
open NodeStreams
|
|
210
|
+
|
|
211
|
+
let copyFileWithPipeline = async (source, dest) => {
|
|
212
|
+
let readStream = createReadStream(source)
|
|
213
|
+
let writeStream = createWriteStream(dest)
|
|
214
|
+
|
|
215
|
+
try {
|
|
216
|
+
await pipeline0(readStream, writeStream)
|
|
217
|
+
Console.log("Copy completed successfully")
|
|
218
|
+
} catch {
|
|
219
|
+
| JsExn.Error(err) => Console.log2("Pipeline error:", err)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Event handling
|
|
225
|
+
|
|
226
|
+
All stream types support event-based programming. Events are registered with `on*` functions that
|
|
227
|
+
return the stream, so they chain:
|
|
228
|
+
|
|
229
|
+
```rescript
|
|
230
|
+
stream
|
|
231
|
+
->onEvent1(handler1)
|
|
232
|
+
->onEvent2(handler2)
|
|
233
|
+
->ignore
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Notes:
|
|
237
|
+
|
|
238
|
+
- Always handle `error` events, or an error becomes an uncaught exception.
|
|
239
|
+
- Prefer the `pipeline` functions over manual `pipe` chains — they clean up on failure.
|
|
240
|
+
- Readable streams switch to flowing mode as soon as a `data` handler is attached.
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## `NodeZlib`
|
|
245
|
+
|
|
246
|
+
Bindings for [`node:zlib`](https://nodejs.org/api/zlib.html). Decompression is what this covers
|
|
247
|
+
today; the compression-only options are deliberately absent from `opts` rather than bound and
|
|
248
|
+
ignored.
|
|
249
|
+
|
|
250
|
+
```rescript
|
|
251
|
+
type opts = {
|
|
252
|
+
flush?: flush,
|
|
253
|
+
finishFlush?: flush,
|
|
254
|
+
chunkSize?: int, // default 16 * 1024
|
|
255
|
+
windowBits?: int,
|
|
256
|
+
info?: bool,
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
let createUnzip: (~options: opts=?) => NodeStreams.Transform.t
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
`Constants.Flush`, `Constants.ReturnCodes`, `Constants.CompressionLevels` and
|
|
263
|
+
`Constants.CompressionStrategy` bind `zlib.constants`.
|
|
264
|
+
|
|
265
|
+
### Example
|
|
266
|
+
|
|
267
|
+
```rescript
|
|
268
|
+
open NodeZlib
|
|
269
|
+
|
|
270
|
+
let gunzip = async (source, dest) =>
|
|
271
|
+
await NodeStreams.pipeline(
|
|
272
|
+
NodeStreams.createReadStream(source),
|
|
273
|
+
createUnzip(),
|
|
274
|
+
NodeStreams.createWriteStream(dest),
|
|
275
|
+
)
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
A runnable version is in [./src/example/ZlibExample.res](./src/example/ZlibExample.res).
|
|
279
|
+
|
|
280
|
+
## Links
|
|
281
|
+
|
|
282
|
+
- 📚 Documentation — [docs.reventless.dev](https://docs.reventless.dev)
|
|
283
|
+
- 📦 Repository — [ReventlessDev/reventless-core](https://github.com/ReventlessDev/reventless-core)
|
|
284
|
+
- 📋 [Changelog](./CHANGELOG.md)
|
|
285
|
+
|
|
286
|
+
## License
|
|
287
|
+
|
|
288
|
+
[Apache-2.0](https://opensource.org/licenses/Apache-2.0)
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@reventlessdev/rescript-node",
|
|
3
|
+
"version": "2.0.0-alpha.0",
|
|
4
|
+
"description": "ReScript bindings for the Node.js standard library",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"devDependencies": {
|
|
7
|
+
"rescript": "12.3.0"
|
|
8
|
+
},
|
|
9
|
+
"peerDependencies": {
|
|
10
|
+
"rescript": "12.3.0"
|
|
11
|
+
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"registry": "https://registry.npmjs.org"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/ReventlessDev/reventless-core.git",
|
|
18
|
+
"directory": "rescript/node"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "rescript build",
|
|
22
|
+
"start": "rescript watch",
|
|
23
|
+
"clean": "rescript clean",
|
|
24
|
+
"test": "echo \"There are no tests for rescript-node.\""
|
|
25
|
+
}
|
|
26
|
+
}
|
package/rescript.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@reventlessdev/rescript-node",
|
|
3
|
+
"package-specs": {
|
|
4
|
+
"module": "esmodule",
|
|
5
|
+
"in-source": true
|
|
6
|
+
},
|
|
7
|
+
"namespace": false,
|
|
8
|
+
"suffix": ".res.mjs",
|
|
9
|
+
"ppx-flags": [],
|
|
10
|
+
"warnings": {
|
|
11
|
+
"error": "-44+101"
|
|
12
|
+
},
|
|
13
|
+
"sources": [
|
|
14
|
+
{
|
|
15
|
+
"dir": "src",
|
|
16
|
+
"subdirs": true,
|
|
17
|
+
"public": [
|
|
18
|
+
"NodeChildProcess",
|
|
19
|
+
"NodeCrypto",
|
|
20
|
+
"NodeFs",
|
|
21
|
+
"NodeModule",
|
|
22
|
+
"NodeOs",
|
|
23
|
+
"NodePath",
|
|
24
|
+
"NodeProcess",
|
|
25
|
+
"NodeStreams",
|
|
26
|
+
"NodeUrl",
|
|
27
|
+
"NodeZlib"
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"dependencies": []
|
|
32
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Bindings for
|
|
2
|
+
[`node:child_process`](https://nodejs.org/api/child_process.html). */
|
|
3
|
+
|
|
4
|
+
type execOptions = {
|
|
5
|
+
cwd?: string,
|
|
6
|
+
encoding?: string,
|
|
7
|
+
env?: dict<string>,
|
|
8
|
+
stdio?: array<string>,
|
|
9
|
+
maxBuffer?: int,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@module("node:child_process")
|
|
13
|
+
external execSync: (string, execOptions) => string = "execSync"
|
|
14
|
+
|
|
15
|
+
/** Takes the arguments as an array rather than interpolating them into a shell
|
|
16
|
+
string, so an argument containing shell metacharacters stays one argument. */
|
|
17
|
+
@module("node:child_process")
|
|
18
|
+
external execFileSync: (string, array<string>, execOptions) => string = "execFileSync"
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** Bindings for [`node:crypto`](https://nodejs.org/api/crypto.html). */
|
|
2
|
+
|
|
3
|
+
/** A Node `Buffer`. Abstract rather than aliased to `Uint8Array.t`: a digest
|
|
4
|
+
buffer is only ever fed back into `createHmac` or stringified, and giving it
|
|
5
|
+
its own type keeps those two uses from being confused with file bytes. */
|
|
6
|
+
type buffer
|
|
7
|
+
@send external bufferToString: (buffer, string) => string = "toString"
|
|
8
|
+
|
|
9
|
+
// ── Hashing ──────────────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
type hash
|
|
12
|
+
|
|
13
|
+
@module("node:crypto")
|
|
14
|
+
external createHash: string => hash = "createHash"
|
|
15
|
+
|
|
16
|
+
@send external hashUpdate: (hash, string) => hash = "update"
|
|
17
|
+
@send external hashUpdateBuffer: (hash, Uint8Array.t) => hash = "update"
|
|
18
|
+
@send external hashDigest: (hash, string) => string = "digest"
|
|
19
|
+
|
|
20
|
+
// ── HMAC ─────────────────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
type hmac
|
|
23
|
+
|
|
24
|
+
@module("node:crypto")
|
|
25
|
+
external createHmac: (string, string) => hmac = "createHmac"
|
|
26
|
+
|
|
27
|
+
/** The chained form: each round of an AWS SigV4 signing key takes the previous
|
|
28
|
+
round's raw digest as its key. */
|
|
29
|
+
@module("node:crypto")
|
|
30
|
+
external createHmacFromBuffer: (string, buffer) => hmac = "createHmac"
|
|
31
|
+
|
|
32
|
+
@send external hmacUpdate: (hmac, string) => hmac = "update"
|
|
33
|
+
@send external hmacDigest: (hmac, string) => string = "digest"
|
|
34
|
+
@send external hmacDigestBuffer: hmac => buffer = "digest"
|
|
35
|
+
|
|
36
|
+
// ── Random ───────────────────────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
@module("node:crypto")
|
|
39
|
+
external randomBytes: int => buffer = "randomBytes"
|
|
40
|
+
|
|
41
|
+
@module("node:crypto")
|
|
42
|
+
external randomUUID: unit => string = "randomUUID"
|
package/src/NodeFs.res
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/** Bindings for [`node:fs`](https://nodejs.org/api/fs.html).
|
|
2
|
+
|
|
3
|
+
Every specifier here is `node:`-prefixed. Bare `"fs"` is what bundlers alias
|
|
4
|
+
to a browser polyfill shim, and this repository bundles Lambda code archives;
|
|
5
|
+
`node:fs` is unambiguous to Node and to every bundler.
|
|
6
|
+
|
|
7
|
+
`readFileSync` bakes the encoding in rather than taking it, so it cannot be
|
|
8
|
+
passed wrongly — the `(string, string)` form is the one that permits
|
|
9
|
+
`readFileSync(path, "utf-8")`, a different string, silently. Use
|
|
10
|
+
{!readFileSyncBuffer} when the bytes are wanted rather than text. */
|
|
11
|
+
|
|
12
|
+
// ── Existence and metadata ───────────────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
@module("node:fs")
|
|
15
|
+
external existsSync: string => bool = "existsSync"
|
|
16
|
+
|
|
17
|
+
@module("node:fs")
|
|
18
|
+
external realpathSync: string => string = "realpathSync"
|
|
19
|
+
|
|
20
|
+
// ── Reading ──────────────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
@module("node:fs")
|
|
23
|
+
external readFileSync: (string, @as("utf8") _) => string = "readFileSync"
|
|
24
|
+
|
|
25
|
+
/** The same call without an encoding, which is what makes Node return the raw
|
|
26
|
+
bytes rather than a decoded string. */
|
|
27
|
+
@module("node:fs")
|
|
28
|
+
external readFileSyncBuffer: string => Uint8Array.t = "readFileSync"
|
|
29
|
+
|
|
30
|
+
// ── Writing ──────────────────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
@module("node:fs")
|
|
33
|
+
external writeFileSync: (string, string, @as("utf8") _) => unit = "writeFileSync"
|
|
34
|
+
|
|
35
|
+
// ── Directories ──────────────────────────────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
type dirent
|
|
38
|
+
@send external isDirectory: dirent => bool = "isDirectory"
|
|
39
|
+
@send external isFile: dirent => bool = "isFile"
|
|
40
|
+
@get external direntName: dirent => string = "name"
|
|
41
|
+
|
|
42
|
+
type readdirOptions = {withFileTypes: bool}
|
|
43
|
+
|
|
44
|
+
@module("node:fs")
|
|
45
|
+
external readdirSync: (string, readdirOptions) => array<dirent> = "readdirSync"
|
|
46
|
+
|
|
47
|
+
type mkdirOptions = {recursive?: bool}
|
|
48
|
+
|
|
49
|
+
@module("node:fs")
|
|
50
|
+
external mkdirSync: (string, mkdirOptions) => unit = "mkdirSync"
|
|
51
|
+
|
|
52
|
+
@module("node:fs")
|
|
53
|
+
external mkdtempSync: string => string = "mkdtempSync"
|
|
54
|
+
|
|
55
|
+
// ── Removing and copying ─────────────────────────────────────────────────────
|
|
56
|
+
|
|
57
|
+
@module("node:fs")
|
|
58
|
+
external unlinkSync: string => unit = "unlinkSync"
|
|
59
|
+
|
|
60
|
+
type rmOptions = {recursive?: bool, force?: bool}
|
|
61
|
+
|
|
62
|
+
@module("node:fs")
|
|
63
|
+
external rmSync: (string, rmOptions) => unit = "rmSync"
|
|
64
|
+
|
|
65
|
+
type cpOptions = {recursive?: bool}
|
|
66
|
+
|
|
67
|
+
@module("node:fs")
|
|
68
|
+
external cpSync: (string, string, cpOptions) => unit = "cpSync"
|
|
69
|
+
|
|
70
|
+
/** Bindings for [`node:fs/promises`](https://nodejs.org/api/fs.html#promises-api).
|
|
71
|
+
|
|
72
|
+
A separate module specifier, so a separate module here — the promise API is
|
|
73
|
+
not a wrapper this package adds over the sync one. */
|
|
74
|
+
module Promises = {
|
|
75
|
+
@module("node:fs/promises")
|
|
76
|
+
external writeFile: (string, string) => promise<unit> = "writeFile"
|
|
77
|
+
|
|
78
|
+
@module("node:fs/promises")
|
|
79
|
+
external mkdir: (string, mkdirOptions) => promise<Nullable.t<string>> = "mkdir"
|
|
80
|
+
|
|
81
|
+
@module("node:fs/promises")
|
|
82
|
+
external rm: (string, rmOptions) => promise<unit> = "rm"
|
|
83
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Bindings for [`node:module`](https://nodejs.org/api/module.html).
|
|
2
|
+
|
|
3
|
+
`createRequire` is how an ESM module gets at CommonJS resolution, which is
|
|
4
|
+
what `require.resolve` is wanted for here — locating a dependency's on-disk
|
|
5
|
+
path without importing it. */
|
|
6
|
+
|
|
7
|
+
type require
|
|
8
|
+
|
|
9
|
+
@module("node:module")
|
|
10
|
+
external createRequire: string => require = "createRequire"
|
|
11
|
+
|
|
12
|
+
@send external requireResolve: (require, string) => string = "resolve"
|
package/src/NodeOs.res
ADDED
package/src/NodePath.res
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Bindings for [`node:path`](https://nodejs.org/api/path.html).
|
|
2
|
+
|
|
3
|
+
`join` and `resolve` are variadic, which is a strict superset of the
|
|
4
|
+
two-argument form they replace, so no call site loses expressiveness. A
|
|
5
|
+
two-argument `join` and a variadic one are not duplicates of each other —
|
|
6
|
+
they are different functions with the same name, and which one a call site
|
|
7
|
+
got used to depend on which inline binding block it happened to sit near. */
|
|
8
|
+
|
|
9
|
+
@module("node:path") @variadic
|
|
10
|
+
external join: array<string> => string = "join"
|
|
11
|
+
|
|
12
|
+
@module("node:path") @variadic
|
|
13
|
+
external resolve: array<string> => string = "resolve"
|
|
14
|
+
|
|
15
|
+
@module("node:path")
|
|
16
|
+
external dirname: string => string = "dirname"
|
|
17
|
+
|
|
18
|
+
@module("node:path")
|
|
19
|
+
external basename: string => string = "basename"
|
|
20
|
+
|
|
21
|
+
@module("node:path")
|
|
22
|
+
external relative: (string, string) => string = "relative"
|
|
23
|
+
|
|
24
|
+
@module("node:path") @val
|
|
25
|
+
external sep: string = "sep"
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** Bindings for the [`process`](https://nodejs.org/api/process.html) global.
|
|
2
|
+
|
|
3
|
+
`process` is a global rather than a module specifier, so these are `@val`
|
|
4
|
+
bindings under `@scope("process")` — `@val external env: dict<string> =
|
|
5
|
+
"process.env"` and `@val @scope("process") external env: dict<string> =
|
|
6
|
+
"env"` compile to the same access, and the tree had both spellings. This is
|
|
7
|
+
the one. */
|
|
8
|
+
|
|
9
|
+
@val @scope("process")
|
|
10
|
+
external argv: array<string> = "argv"
|
|
11
|
+
|
|
12
|
+
@val @scope("process")
|
|
13
|
+
external env: dict<string> = "env"
|
|
14
|
+
|
|
15
|
+
@val @scope("process")
|
|
16
|
+
external cwd: unit => string = "cwd"
|
|
17
|
+
|
|
18
|
+
@val @scope("process")
|
|
19
|
+
external chdir: string => unit = "chdir"
|
|
20
|
+
|
|
21
|
+
@val @scope("process")
|
|
22
|
+
external exit: int => unit = "exit"
|
|
23
|
+
|
|
24
|
+
/** The standard streams. `write`, `isTTY`, `pause` and `unref` are what the
|
|
25
|
+
interactive prompts in this repository reach for; the type is abstract so it
|
|
26
|
+
can also be handed to `readline.createInterface`. */
|
|
27
|
+
type stream
|
|
28
|
+
|
|
29
|
+
@val @scope("process")
|
|
30
|
+
external stdin: stream = "stdin"
|
|
31
|
+
|
|
32
|
+
@val @scope("process")
|
|
33
|
+
external stdout: stream = "stdout"
|
|
34
|
+
|
|
35
|
+
@send external write: (stream, string) => unit = "write"
|
|
36
|
+
@send external pause: stream => unit = "pause"
|
|
37
|
+
@send external unref: stream => unit = "unref"
|
|
38
|
+
|
|
39
|
+
/** `option<bool>`, not `bool`: Node sets `isTTY` to `true` on an interactive
|
|
40
|
+
stream and leaves it **undefined** otherwise — it is never `false`. A
|
|
41
|
+
`bool`-typed binding reads that undefined as a valid `false`, which happens
|
|
42
|
+
to work and is still lying about the value. */
|
|
43
|
+
@get external isTTY: stream => option<bool> = "isTTY"
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
type readableStream
|
|
2
|
+
type writableStream
|
|
3
|
+
type buffer
|
|
4
|
+
|
|
5
|
+
module EventHandler = {
|
|
6
|
+
@send
|
|
7
|
+
external onError: ('a, @as("error") _, JsExn.t => unit) => 'a = "on"
|
|
8
|
+
@send
|
|
9
|
+
external onClose: ('a, @as("close") _, unit => unit) => 'a = "on"
|
|
10
|
+
@send external onEvent: ('a, string, 'b => unit) => 'a = "on"
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
module Writable = {
|
|
14
|
+
type t = writableStream
|
|
15
|
+
@send external close: t => unit = "end"
|
|
16
|
+
@send external pipe: (t, t) => t = "pipe"
|
|
17
|
+
|
|
18
|
+
include EventHandler // onError & onClose & onEvent
|
|
19
|
+
|
|
20
|
+
@send
|
|
21
|
+
external onData: (t, @as("data") _, 'd => unit) => t = "on"
|
|
22
|
+
|
|
23
|
+
/** If a call to stream.write(chunk) returns false, the 'drain' event will be emitted when it is appropriate to resume writing data to the stream.
|
|
24
|
+
see: https://nodejs.org/docs/latest-v8.x/api/stream.html#stream_event_drain */
|
|
25
|
+
@send
|
|
26
|
+
external onDrain: (t, @as("drain") _, unit => unit) => t = "on"
|
|
27
|
+
|
|
28
|
+
/** The 'finish' event is emitted after the stream.end() method has been called, and all data has been flushed to the underlying system.
|
|
29
|
+
see: https://nodejs.org/docs/latest-v8.x/api/stream.html#stream_event_finish */
|
|
30
|
+
@send
|
|
31
|
+
external onFinish: (t, @as("finish") _, unit => unit) => t = "on"
|
|
32
|
+
|
|
33
|
+
/** The 'pipe' event is emitted when the stream.pipe() method is called on a readable stream, adding this writable to its set of destinations.
|
|
34
|
+
see: https://nodejs.org/docs/latest-v8.x/api/stream.html#stream_event_pipe */
|
|
35
|
+
@send
|
|
36
|
+
external onPipe: (t, @as("pipe") _, readableStream => unit) => t = "on"
|
|
37
|
+
|
|
38
|
+
/** The 'unpipe' event is emitted when the stream.unpipe() method is called on a Readable stream, removing this Writable from its set of destinations.
|
|
39
|
+
This is also emitted in case this Writable stream emits an error when a Readable stream pipes into it.
|
|
40
|
+
see: https://nodejs.org/docs/latest-v8.x/api/stream.html#stream_event_unpipe */
|
|
41
|
+
@send
|
|
42
|
+
external onUnpipe: (t, @as("unpipe") _, readableStream => unit) => t = "on"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module Readable = {
|
|
46
|
+
type t = readableStream
|
|
47
|
+
@send external pipe: (t, writableStream) => writableStream = "pipe"
|
|
48
|
+
|
|
49
|
+
@send
|
|
50
|
+
external setEncoding: (t, string) => t = "setEncoding"
|
|
51
|
+
|
|
52
|
+
include EventHandler // onError & onClose & onEvent
|
|
53
|
+
|
|
54
|
+
/** The 'data' event is emitted whenever the stream is relinquishing ownership of a chunk of data to a consumer. This may occur whenever the stream is switched in flowing mode by calling readable.pipe(), readable.resume(), or by attaching a listener callback to the 'data' event. The 'data' event will also be emitted whenever the readable.read() method is called and a chunk of data is available to be returned.
|
|
55
|
+
Attaching a 'data' event listener to a stream that has not been explicitly paused will switch the stream into flowing mode. Data will then be passed as soon as it is available.
|
|
56
|
+
The listener callback will be passed the chunk of data as a string if a default encoding has been specified for the stream using the readable.setEncoding() method; otherwise the data will be passed as a Buffer.
|
|
57
|
+
see: https://nodejs.org/docs/latest-v8.x/api/stream.html#stream_event_data */
|
|
58
|
+
@send
|
|
59
|
+
external onDataFromBuffer: (t, @as("data") _, buffer => unit) => t = "on"
|
|
60
|
+
|
|
61
|
+
/** The 'data' event is emitted whenever the stream is relinquishing ownership of a chunk of data to a consumer. This may occur whenever the stream is switched in flowing mode by calling readable.pipe(), readable.resume(), or by attaching a listener callback to the 'data' event. The 'data' event will also be emitted whenever the readable.read() method is called and a chunk of data is available to be returned.
|
|
62
|
+
Attaching a 'data' event listener to a stream that has not been explicitly paused will switch the stream into flowing mode. Data will then be passed as soon as it is available.
|
|
63
|
+
The listener callback will be passed the chunk of data as a string if a default encoding has been specified for the stream using the readable.setEncoding() method; otherwise the data will be passed as a Buffer.
|
|
64
|
+
see: https://nodejs.org/docs/latest-v8.x/api/stream.html#stream_event_data */
|
|
65
|
+
@send
|
|
66
|
+
external onDataFromString: (t, @as("data") _, string => unit) => t = "on"
|
|
67
|
+
|
|
68
|
+
/** The 'data' event is emitted whenever the stream is relinquishing ownership of a chunk of data to a consumer. This may occur whenever the stream is switched in flowing mode by calling readable.pipe(), readable.resume(), or by attaching a listener callback to the 'data' event. The 'data' event will also be emitted whenever the readable.read() method is called and a chunk of data is available to be returned.
|
|
69
|
+
Attaching a 'data' event listener to a stream that has not been explicitly paused will switch the stream into flowing mode. Data will then be passed as soon as it is available.
|
|
70
|
+
The listener callback will be passed the chunk of data as a string if a default encoding has been specified for the stream using the readable.setEncoding() method; otherwise the data will be passed as a Buffer.
|
|
71
|
+
Note: The 'end' event will not be emitted unless the data is completely consumed. This can be accomplished by switching the stream into flowing mode, or by calling stream.read() repeatedly until all data has been consumed.
|
|
72
|
+
see: https://nodejs.org/docs/latest-v8.x/api/stream.html#stream_event_data */
|
|
73
|
+
@send
|
|
74
|
+
external onDataFromArbitary: (t, @as("data") _, 'd => unit) => t = "on"
|
|
75
|
+
|
|
76
|
+
/** The 'end' event is emitted when there is no more data to be consumed from the stream.
|
|
77
|
+
see: https://nodejs.org/docs/latest-v8.x/api/stream.html#stream_event_end */
|
|
78
|
+
@send
|
|
79
|
+
external onEnd: (t, @as("end") _, unit => unit) => t = "on"
|
|
80
|
+
|
|
81
|
+
/** The 'readable' event is emitted when there is data available to be read from the stream. In some cases, attaching a listener for the 'readable' event will cause some amount of data to be read into an internal buffer.
|
|
82
|
+
see: https://nodejs.org/docs/latest-v8.x/api/stream.html#stream_event_readable */
|
|
83
|
+
@send
|
|
84
|
+
external onReadable: (t, @as("readable") _, unit => unit) => t = "on"
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
module Duplex = {
|
|
88
|
+
type t
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
module Transform = {
|
|
92
|
+
type t
|
|
93
|
+
@send external pipe: (t, readableStream) => writableStream = "pipe"
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@val @module("node:fs")
|
|
97
|
+
external createWriteStream: string => Writable.t = "createWriteStream"
|
|
98
|
+
@val @module("node:fs")
|
|
99
|
+
external createReadStream: string => Readable.t = "createReadStream"
|
|
100
|
+
@val @module("node:fs")
|
|
101
|
+
external unlink: string => promise<unit> = "unlink"
|
|
102
|
+
|
|
103
|
+
module Readline = {
|
|
104
|
+
type t
|
|
105
|
+
type options = {input: Readable.t}
|
|
106
|
+
|
|
107
|
+
@val @module("node:readline")
|
|
108
|
+
external createInterface: options => t = "createInterface"
|
|
109
|
+
|
|
110
|
+
@send
|
|
111
|
+
external onLine: (t, @as("line") _, string => unit) => t = "on"
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// NOTE: This node function is variadic (=takes n transform streams)
|
|
115
|
+
// NOTE: documentation says, this returns <Stream>, we just bind to unit
|
|
116
|
+
@val @module("node:stream") @scope("promises")
|
|
117
|
+
external pipeline0: (Readable.t, Writable.t) => promise<unit> = "pipeline"
|
|
118
|
+
@val @module("node:stream") @scope("promises")
|
|
119
|
+
external pipeline: (Readable.t, Transform.t, Writable.t) => promise<unit> = "pipeline"
|
|
120
|
+
@val @module("node:stream") @scope("promises")
|
|
121
|
+
external pipeline2: (Readable.t, Transform.t, Transform.t, Writable.t) => promise<unit> = "pipeline"
|
|
122
|
+
@val @module("node:stream") @scope("promises")
|
|
123
|
+
external pipeline3: (
|
|
124
|
+
Readable.t,
|
|
125
|
+
Transform.t,
|
|
126
|
+
Transform.t,
|
|
127
|
+
Transform.t,
|
|
128
|
+
Writable.t,
|
|
129
|
+
) => promise<unit> = "pipeline"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
let EventHandler = {};
|
|
5
|
+
|
|
6
|
+
let Writable = {};
|
|
7
|
+
|
|
8
|
+
let Readable = {};
|
|
9
|
+
|
|
10
|
+
let Duplex = {};
|
|
11
|
+
|
|
12
|
+
let Transform = {};
|
|
13
|
+
|
|
14
|
+
let Readline = {};
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
EventHandler,
|
|
18
|
+
Writable,
|
|
19
|
+
Readable,
|
|
20
|
+
Duplex,
|
|
21
|
+
Transform,
|
|
22
|
+
Readline,
|
|
23
|
+
}
|
|
24
|
+
/* No side effect */
|
package/src/NodeUrl.res
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Bindings for [`node:url`](https://nodejs.org/api/url.html).
|
|
2
|
+
|
|
3
|
+
Only the two path/URL converters — the `URL` class itself is a WHATWG global
|
|
4
|
+
rather than a `node:` import, so it belongs to `rescript-web`, not here. */
|
|
5
|
+
|
|
6
|
+
@module("node:url")
|
|
7
|
+
external fileURLToPath: string => string = "fileURLToPath"
|
|
8
|
+
|
|
9
|
+
@module("node:url")
|
|
10
|
+
external pathToFileURL: string => {"href": string} = "pathToFileURL"
|
package/src/NodeZlib.res
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
type flush
|
|
2
|
+
type returnCode
|
|
3
|
+
type compressionLevel
|
|
4
|
+
type compressionStrategy
|
|
5
|
+
|
|
6
|
+
module Constants = {
|
|
7
|
+
module Flush = {
|
|
8
|
+
@val @module("node:zlib") @scope("constants")
|
|
9
|
+
external z_no_flush: flush = "Z_NO_FLUSH"
|
|
10
|
+
@val @module("node:zlib") @scope("constants")
|
|
11
|
+
external z_partial_flush: flush = "Z_PARTIAL_FLUSH"
|
|
12
|
+
@val @module("node:zlib") @scope("constants")
|
|
13
|
+
external z_sync_flush: flush = "Z_SYNC_FLUSH"
|
|
14
|
+
@val @module("node:zlib") @scope("constants")
|
|
15
|
+
external z_full_flush: flush = "Z_FULL_FLUSH"
|
|
16
|
+
@val @module("node:zlib") @scope("constants")
|
|
17
|
+
external z_finish: flush = "Z_FINISH"
|
|
18
|
+
@val @module("node:zlib") @scope("constants")
|
|
19
|
+
external z_block: flush = "Z_BLOCK"
|
|
20
|
+
@val @module("node:zlib") @scope("constants")
|
|
21
|
+
external z_trees: flush = "Z_TREES"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module ReturnCodes = {
|
|
25
|
+
@val @module("node:zlib") @scope("constants")
|
|
26
|
+
external z_ok: returnCode = "Z_OK"
|
|
27
|
+
@val @module("node:zlib") @scope("constants")
|
|
28
|
+
external z_stream_end: returnCode = "Z_STREAM_END"
|
|
29
|
+
@val @module("node:zlib") @scope("constants")
|
|
30
|
+
external z_need_dict: returnCode = "Z_NEED_DICT"
|
|
31
|
+
@val @module("node:zlib") @scope("constants")
|
|
32
|
+
external z_errno: returnCode = "Z_ERRNO"
|
|
33
|
+
@val @module("node:zlib") @scope("constants")
|
|
34
|
+
external z_stream_error: returnCode = "Z_STREAM_ERROR"
|
|
35
|
+
@val @module("node:zlib") @scope("constants")
|
|
36
|
+
external z_data_error: returnCode = "Z_DATA_ERROR"
|
|
37
|
+
@val @module("node:zlib") @scope("constants")
|
|
38
|
+
external z_mem_error: returnCode = "Z_MEM_ERROR"
|
|
39
|
+
@val @module("node:zlib") @scope("constants")
|
|
40
|
+
external z_buf_error: returnCode = "Z_BUF_ERROR"
|
|
41
|
+
@val @module("node:zlib") @scope("constants")
|
|
42
|
+
external z_version_error: returnCode = "Z_VERSION_ERROR"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module CompressionLevels = {
|
|
46
|
+
@val @module("node:zlib") @scope("constants")
|
|
47
|
+
external z_no_compression: compressionLevel = "Z"
|
|
48
|
+
@val @module("node:zlib") @scope("constants")
|
|
49
|
+
external z_best_speed: compressionLevel = "Z_BEST_SPEED"
|
|
50
|
+
@val @module("node:zlib") @scope("constants")
|
|
51
|
+
external z_best_compression: compressionLevel = "Z_BEST_COMPRESSION"
|
|
52
|
+
@val @module("node:zlib") @scope("constants")
|
|
53
|
+
external z_default_compression: compressionLevel = "Z_DEFAULT_COMPRESSION"
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module CompressionStrategy = {
|
|
57
|
+
external z_filtered: compressionStrategy = "Z_FILTERED"
|
|
58
|
+
external z_huffman_only: compressionStrategy = "Z_HUFFMAN_ONLY"
|
|
59
|
+
external z_rle: compressionStrategy = "Z_RLE"
|
|
60
|
+
external z_fixed: compressionStrategy = "Z_FIXED"
|
|
61
|
+
external z_default_strategy: compressionStrategy = "Z_DEFAULT_STRATEGY"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// NOTE: props not relevant for decompression have been skipped
|
|
66
|
+
type opts = {
|
|
67
|
+
flush?: flush,
|
|
68
|
+
finishFlush?: flush,
|
|
69
|
+
chunkSize?: int, // Default: 16 * 1024
|
|
70
|
+
windowBits?: int,
|
|
71
|
+
info?: bool,
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@val @module("node:zlib")
|
|
75
|
+
external createUnzip: (~options: opts=?) => NodeStreams.Transform.t = "createUnzip"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
let Flush = {};
|
|
5
|
+
|
|
6
|
+
let ReturnCodes = {};
|
|
7
|
+
|
|
8
|
+
let CompressionLevels = {};
|
|
9
|
+
|
|
10
|
+
let CompressionStrategy = {};
|
|
11
|
+
|
|
12
|
+
let Constants = {
|
|
13
|
+
Flush: Flush,
|
|
14
|
+
ReturnCodes: ReturnCodes,
|
|
15
|
+
CompressionLevels: CompressionLevels,
|
|
16
|
+
CompressionStrategy: CompressionStrategy
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export {
|
|
20
|
+
Constants,
|
|
21
|
+
}
|
|
22
|
+
/* No side effect */
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* To run this example:
|
|
3
|
+
* - build this package
|
|
4
|
+
* - cd into the example directory
|
|
5
|
+
* - run `node Example.bs.js`
|
|
6
|
+
* - Test was successfull if:
|
|
7
|
+
* - no exception was raised (and outputed during program execution)
|
|
8
|
+
* - testResult.txt file is now present in the example directory
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
open NodeZlib
|
|
12
|
+
|
|
13
|
+
let f = async () => {
|
|
14
|
+
let readFile = NodeStreams.createReadStream("./test.txt.gz")
|
|
15
|
+
let unzipFile = createUnzip()
|
|
16
|
+
let writeFile = NodeStreams.createWriteStream("./testResult.txt")
|
|
17
|
+
await NodeStreams.pipeline(readFile, unzipFile, writeFile)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
f()->ignore
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Nodefs from "node:fs";
|
|
4
|
+
import * as Nodezlib from "node:zlib";
|
|
5
|
+
import * as Nodestream from "node:stream";
|
|
6
|
+
|
|
7
|
+
async function f() {
|
|
8
|
+
let readFile = Nodefs.createReadStream("./test.txt.gz");
|
|
9
|
+
let unzipFile = Nodezlib.createUnzip();
|
|
10
|
+
let writeFile = Nodefs.createWriteStream("./testResult.txt");
|
|
11
|
+
return await Nodestream.promises.pipeline(readFile, unzipFile, writeFile);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
f();
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
f,
|
|
18
|
+
}
|
|
19
|
+
/* Not a pure module */
|
|
Binary file
|