@reventlessdev/reventless-core 3.0.0-alpha.195 → 3.0.0-alpha.197

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,28 @@
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
+ # 3.0.0-alpha.197 (2026-08-01)
7
+
8
+ ### Features
9
+
10
+ * **spec:** add DateRange semantic type ([d85b6cc](https://github.com/ReventlessDev/reventless-core/commit/d85b6cc18241644905241df2abd99949dd758059))
11
+
12
+
13
+ # 3.0.0-alpha.196 (2026-07-31)
14
+
15
+ * feat(rescript)!: one Node bindings package, not two ([1258d8c](https://github.com/ReventlessDev/reventless-core/commit/1258d8c2b2ff2636b36a849fc5bdf9005c6fb0eb))
16
+ ### Features
17
+
18
+ * **spec:** add Money and a closed ISO 4217 Currency ([d4852ab](https://github.com/ReventlessDev/reventless-core/commit/d4852ab63e823e39fac793c4fa5ac31470db9655))
19
+
20
+ ### BREAKING CHANGES
21
+
22
+ * `@reventlessdev/rescript-node-streams` and
23
+ `@reventlessdev/rescript-node-zlib` are replaced by
24
+ `@reventlessdev/rescript-node`. Module names are unchanged.
25
+
26
+
27
+
6
28
  # 3.0.0-alpha.195 (2026-07-30)
7
29
 
8
30
  ### Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-core",
3
- "version": "3.0.0-alpha.195",
3
+ "version": "3.0.0-alpha.197",
4
4
  "description": "Core package for Reventless framework",
5
5
  "license": "Apache-2.0",
6
6
  "jest": {
@@ -28,17 +28,17 @@
28
28
  "dependencies": {
29
29
  "sury": "11.0.0-alpha.4",
30
30
  "uuid": "^13.0.0",
31
- "@reventlessdev/rescript-effect": "0.1.0-alpha.31",
32
- "@reventlessdev/rescript-hash-object": "1.2.0-alpha.13",
33
- "@reventlessdev/rescript-fast-csv": "1.2.0-alpha.14",
34
- "@reventlessdev/rescript-jest": "1.0.0-alpha.9",
35
- "@reventlessdev/rescript-node-streams": "1.1.0-alpha.13",
36
- "@reventlessdev/rescript-ssh2": "1.1.0-alpha.14",
37
- "@reventlessdev/rescript-uuid": "1.1.0-alpha.17",
38
- "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.17",
39
- "@reventlessdev/reventless-interop": "3.0.0-alpha.29",
40
- "@reventlessdev/reventless-spec": "3.0.0-alpha.88",
41
- "@reventlessdev/reventless-infra": "3.0.0-alpha.113"
31
+ "@reventlessdev/rescript-effect": "0.1.0-alpha.32",
32
+ "@reventlessdev/rescript-fast-csv": "2.0.0-alpha.0",
33
+ "@reventlessdev/rescript-hash-object": "1.2.0-alpha.14",
34
+ "@reventlessdev/rescript-jest": "1.0.0-alpha.10",
35
+ "@reventlessdev/rescript-node": "2.0.0-alpha.0",
36
+ "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.18",
37
+ "@reventlessdev/rescript-ssh2": "2.0.0-alpha.0",
38
+ "@reventlessdev/reventless-interop": "3.0.0-alpha.30",
39
+ "@reventlessdev/reventless-infra": "3.0.0-alpha.115",
40
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.90",
41
+ "@reventlessdev/rescript-uuid": "2.0.0-alpha.0"
42
42
  },
43
43
  "devDependencies": {
44
44
  "rescript": "12.3.0",
package/rescript.json CHANGED
@@ -29,7 +29,7 @@
29
29
  "@reventlessdev/rescript-effect",
30
30
  "@reventlessdev/rescript-fast-csv",
31
31
  "@reventlessdev/rescript-hash-object",
32
- "@reventlessdev/rescript-node-streams",
32
+ "@reventlessdev/rescript-node",
33
33
  "@reventlessdev/rescript-pulumi-pulumi",
34
34
  "@reventlessdev/rescript-ssh2",
35
35
  "@reventlessdev/rescript-uuid",
@@ -1165,16 +1165,14 @@ let getInteropMeta = (): Pulumi.Output.t<JSON.t> => {
1165
1165
  // Stack metadata export — environment, region, timestamp, actor, git SHA.
1166
1166
  // Called from both exportPluginOutputs and exportPlatformOutputs.
1167
1167
  // ---------------------------------------------------------------------------
1168
- @val external processEnv: dict<string> = "process.env"
1169
-
1170
1168
  let exportDeploymentMetadata = () => {
1171
1169
  let metadata =
1172
1170
  [
1173
1171
  ("environment", Pulumi.Pulumi.getStackName()),
1174
1172
  ("region", Pulumi.Config.make(Some("aws"))->Pulumi.Config.get("region")->Option.getOr("unknown")),
1175
1173
  ("timestamp", Date.make()->Date.toISOString),
1176
- ("gitSha", processEnv->Dict.get("GITHUB_SHA")->Option.getOr("unknown")),
1177
- ("actor", processEnv->Dict.get("GITHUB_ACTOR")->Option.getOr("unknown")),
1174
+ ("gitSha", NodeProcess.env->Dict.get("GITHUB_SHA")->Option.getOr("unknown")),
1175
+ ("actor", NodeProcess.env->Dict.get("GITHUB_ACTOR")->Option.getOr("unknown")),
1178
1176
  ]
1179
1177
  ->Dict.fromArray
1180
1178
  Pulumi.Pulumi.export(
@@ -1516,15 +1514,15 @@ let exportPluginOutputs = (pluginOutputs: Plugin.outputs) => {
1516
1514
  | Some(hook) =>
1517
1515
  // Read deployment provenance from env vars synchronously (before Output.apply).
1518
1516
  let actor =
1519
- processEnv
1517
+ NodeProcess.env
1520
1518
  ->Dict.get("GITHUB_ACTOR")
1521
- ->Option.orElse(processEnv->Dict.get("CI_COMMIT_AUTHOR"))
1522
- ->Option.orElse(processEnv->Dict.get("USER"))
1519
+ ->Option.orElse(NodeProcess.env->Dict.get("CI_COMMIT_AUTHOR"))
1520
+ ->Option.orElse(NodeProcess.env->Dict.get("USER"))
1523
1521
  ->Option.getOr("local")
1524
1522
  let deploymentId =
1525
- processEnv
1523
+ NodeProcess.env
1526
1524
  ->Dict.get("GITHUB_SHA")
1527
- ->Option.orElse(processEnv->Dict.get("CI_COMMIT_SHA"))
1525
+ ->Option.orElse(NodeProcess.env->Dict.get("CI_COMMIT_SHA"))
1528
1526
  ->Option.getOr(Date.make()->Date.toISOString)
1529
1527
  let schemaFor = name =>
1530
1528
  componentSchemaRegistry->Dict.get(name)->Option.getOr({})
@@ -2,7 +2,7 @@
2
2
 
3
3
  import * as SSH2 from "@reventlessdev/rescript-ssh2/src/SSH2.res.mjs";
4
4
  import * as Ssh2 from "ssh2";
5
- import * as Stream from "stream";
5
+ import * as Nodestream from "node:stream";
6
6
  import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
7
7
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
8
8
  import * as Effect from "effect/Effect";
@@ -122,7 +122,7 @@ function ftp(connectionParams, ftpAction) {
122
122
  let err$1 = new Error("FTPHandler: Error in Write Stream");
123
123
  sftp.emit("error", err$1);
124
124
  });
125
- return await Stream.promises.pipeline(ftpAction._0, ws);
125
+ return await Nodestream.promises.pipeline(ftpAction._0, ws);
126
126
  }
127
127
  }
128
128
  }, () => {});
@@ -677,4 +677,121 @@ describe("SuryToJsonSchema:", () => {
677
677
  ))->toEqual((Some(JSON.Encode.string("type")), Some(JSON.Encode.string("type"))))
678
678
  )
679
679
  })
680
+
681
+ // The other half of that claim, stated so it cannot be confused with it.
682
+ // `Money` is the first semantic type that is *not* a brand: it turns a number
683
+ // into an object, so the wire shape changes and stored events written against
684
+ // the old field do not decode. The assertions below are the evidence for that
685
+ // — a schema diff before and after a retype is exactly this, and it is what
686
+ // says a deployment needs an upcaster before it can take the change.
687
+ describe("Money changes the field's shape, and says so:", () => {
688
+ let json = SuryToJsonSchema.deriveObjectSchema(
689
+ S.schema(s =>
690
+ {
691
+ "price": s.matches(Reventless.Money.schema),
692
+ "quantity": s.matches(S.float),
693
+ }
694
+ )->S.castToUnknown,
695
+ )
696
+ let keyOf = (name, key) => getPropertyOf(json, name)->Option.flatMap(s => getProperty(s, key))
697
+
698
+ testSync("the field is an object, not a number", () =>
699
+ expect(keyOf("price", "type"))->toEqual(Some(JSON.Encode.string("object")))
700
+ )
701
+
702
+ testSync("it carries the money id, sourced from the type", () =>
703
+ expect((keyOf("price", "x-reventless-semantic"), keyOf("price", "x-reventless-semantic-source")))
704
+ ->toEqual((Some(JSON.Encode.string("money")), Some(JSON.Encode.string("type"))))
705
+ )
706
+
707
+ // The control: an ordinary `float` price is what the field looked like
708
+ // before, and it emits nothing at all. Without this the assertions above
709
+ // would pass just as well against a schema that had always said `money`.
710
+ testSync("a plain numeric field emits no semantic", () =>
711
+ expect((keyOf("quantity", "type"), keyOf("quantity", "x-reventless-semantic")))->toEqual((
712
+ Some(JSON.Encode.string("number")),
713
+ None,
714
+ ))
715
+ )
716
+
717
+ testSync("the amount and the currency both reach the wire", () => {
718
+ let inner = getPropertyOf(json, "price")->Option.getOr(JSON.Encode.null)
719
+ expect((
720
+ getPropertyOf(inner, "amount")->Option.flatMap(s => getProperty(s, "type")),
721
+ getPropertyOf(inner, "currency")->Option.flatMap(s => getProperty(s, "type")),
722
+ ))->toEqual((Some(JSON.Encode.string("number")), Some(JSON.Encode.string("string"))))
723
+ })
724
+
725
+ // The closed currency arrives as an enum rather than a free string, which is
726
+ // what lets a consumer build a currency picker instead of a text box — and
727
+ // what makes `"eur"` unrepresentable on the wire as well as in the source.
728
+ testSync("the currency is an enum of ISO codes", () => {
729
+ let codes =
730
+ getPropertyOf(json, "price")
731
+ ->Option.getOr(JSON.Encode.null)
732
+ ->getPropertyOf("currency")
733
+ ->Option.flatMap(s => getProperty(s, "enum"))
734
+ ->Option.flatMap(JSON.Decode.array)
735
+ ->Option.getOr([])
736
+ ->Array.filterMap(JSON.Decode.string)
737
+ expect((
738
+ codes->Array.length == Reventless.Currency.all->Array.length,
739
+ codes->Array.includes("EUR"),
740
+ codes->Array.includes("JPY"),
741
+ codes->Array.includes("eur"),
742
+ ))->toEqual((true, true, true, false))
743
+ })
744
+ })
745
+
746
+ // The second composite, and the one whose emission the reader's shape rung
747
+ // depends on. Like `Money` it turns the field into an object — same
748
+ // shape-changed evidence, same upcaster warning for a collapse. Unlike it, the
749
+ // two parts keep their own `dateTime` marker, so a walker that only understands
750
+ // date-times still sees into the composite. That last property is asserted, not
751
+ // described, because it is what lets the UI's primary rung key on the shape
752
+ // before the `dateRange` marker reaches it in a release.
753
+ describe("DateRange is an object whose parts stay date-times:", () => {
754
+ let json = SuryToJsonSchema.deriveObjectSchema(
755
+ S.schema(s =>
756
+ {
757
+ "deliveryWindow": s.matches(Reventless.DateRange.schema),
758
+ "orderedAt": s.matches(Reventless.DateTime.string),
759
+ }
760
+ )->S.castToUnknown,
761
+ )
762
+ let keyOf = (name, key) => getPropertyOf(json, name)->Option.flatMap(s => getProperty(s, key))
763
+
764
+ testSync("the field is an object, not a string", () =>
765
+ expect(keyOf("deliveryWindow", "type"))->toEqual(Some(JSON.Encode.string("object")))
766
+ )
767
+
768
+ testSync("it carries the dateRange id, sourced from the type", () =>
769
+ expect((
770
+ keyOf("deliveryWindow", "x-reventless-semantic"),
771
+ keyOf("deliveryWindow", "x-reventless-semantic-source"),
772
+ ))->toEqual((Some(JSON.Encode.string("dateRange")), Some(JSON.Encode.string("type"))))
773
+ )
774
+
775
+ // The property that is not incidental: each part keeps its own date-time
776
+ // marker under the composite. `dateTime` predates the generic marker and
777
+ // surfaces as `format: "date-time"` (not `x-reventless-semantic`), so that is
778
+ // what a walker reads to see the two instants without knowing about ranges.
779
+ testSync("both instants keep their own date-time marker", () => {
780
+ let inner = getPropertyOf(json, "deliveryWindow")->Option.getOr(JSON.Encode.null)
781
+ expect((
782
+ getPropertyOf(inner, "start")->Option.flatMap(s => getProperty(s, "format")),
783
+ getPropertyOf(inner, "end")->Option.flatMap(s => getProperty(s, "format")),
784
+ ))->toEqual((Some(JSON.Encode.string("date-time")), Some(JSON.Encode.string("date-time"))))
785
+ })
786
+
787
+ // The control, mirroring the `Money` block's plain-float case: a lone
788
+ // date-time field stays a string and never becomes an object, so the
789
+ // object-ness above is the composite's doing and not the walk's.
790
+ testSync("a lone date-time field stays a string", () =>
791
+ expect((keyOf("orderedAt", "type"), keyOf("orderedAt", "format")))->toEqual((
792
+ Some(JSON.Encode.string("string")),
793
+ Some(JSON.Encode.string("date-time")),
794
+ ))
795
+ )
796
+ })
680
797
  })
@@ -5,10 +5,13 @@ import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
5
5
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
6
6
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
7
7
  import * as Email$Reventless from "@reventlessdev/reventless-spec/src/semantic/Email.res.mjs";
8
+ import * as Money$Reventless from "@reventlessdev/reventless-spec/src/semantic/Money.res.mjs";
8
9
  import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
9
10
  import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
10
11
  import * as Percent$Reventless from "@reventlessdev/reventless-spec/src/semantic/Percent.res.mjs";
12
+ import * as Currency$Reventless from "@reventlessdev/reventless-spec/src/semantic/Currency.res.mjs";
11
13
  import * as DateTime$Reventless from "@reventlessdev/reventless-spec/src/types/DateTime.res.mjs";
14
+ import * as DateRange$Reventless from "@reventlessdev/reventless-spec/src/semantic/DateRange.res.mjs";
12
15
  import * as Reference$Reventless from "@reventlessdev/reventless-spec/src/components/Reference.res.mjs";
13
16
  import * as StorageRef$Reventless from "@reventlessdev/reventless-spec/src/semantic/StorageRef.res.mjs";
14
17
  import * as StateAnnotations$Reventless from "@reventlessdev/reventless-spec/src/components/StateAnnotations.res.mjs";
@@ -981,6 +984,96 @@ globalThis.describe("SuryToJsonSchema:", () => {
981
984
  ]);
982
985
  });
983
986
  });
987
+ globalThis.describe("Money changes the field's shape, and says so:", () => {
988
+ let json = SuryToJsonSchema$ReventlessCore.deriveObjectSchema(S.schema(s => ({
989
+ price: s.m(Money$Reventless.schema),
990
+ quantity: s.m(S.float)
991
+ })));
992
+ let keyOf = (name, key) => Stdlib_Option.flatMap(getPropertyOf(json, name), s => getProperty(s, key));
993
+ globalThis.test("the field is an object, not a number", () => {
994
+ globalThis.expect(keyOf("price", "type")).toEqual("object");
995
+ });
996
+ globalThis.test("it carries the money id, sourced from the type", () => {
997
+ globalThis.expect([
998
+ keyOf("price", "x-reventless-semantic"),
999
+ keyOf("price", "x-reventless-semantic-source")
1000
+ ]).toEqual([
1001
+ "money",
1002
+ "type"
1003
+ ]);
1004
+ });
1005
+ globalThis.test("a plain numeric field emits no semantic", () => {
1006
+ globalThis.expect([
1007
+ keyOf("quantity", "type"),
1008
+ keyOf("quantity", "x-reventless-semantic")
1009
+ ]).toEqual([
1010
+ "number",
1011
+ undefined
1012
+ ]);
1013
+ });
1014
+ globalThis.test("the amount and the currency both reach the wire", () => {
1015
+ let inner = Stdlib_Option.getOr(getPropertyOf(json, "price"), null);
1016
+ globalThis.expect([
1017
+ Stdlib_Option.flatMap(getPropertyOf(inner, "amount"), s => getProperty(s, "type")),
1018
+ Stdlib_Option.flatMap(getPropertyOf(inner, "currency"), s => getProperty(s, "type"))
1019
+ ]).toEqual([
1020
+ "number",
1021
+ "string"
1022
+ ]);
1023
+ });
1024
+ globalThis.test("the currency is an enum of ISO codes", () => {
1025
+ let codes = Stdlib_Array.filterMap(Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_Option.flatMap(getPropertyOf(Stdlib_Option.getOr(getPropertyOf(json, "price"), null), "currency"), s => getProperty(s, "enum")), Stdlib_JSON.Decode.array), []), Stdlib_JSON.Decode.string);
1026
+ globalThis.expect([
1027
+ codes.length === Currency$Reventless.all.length,
1028
+ codes.includes("EUR"),
1029
+ codes.includes("JPY"),
1030
+ codes.includes("eur")
1031
+ ]).toEqual([
1032
+ true,
1033
+ true,
1034
+ true,
1035
+ false
1036
+ ]);
1037
+ });
1038
+ });
1039
+ globalThis.describe("DateRange is an object whose parts stay date-times:", () => {
1040
+ let json = SuryToJsonSchema$ReventlessCore.deriveObjectSchema(S.schema(s => ({
1041
+ deliveryWindow: s.m(DateRange$Reventless.schema),
1042
+ orderedAt: s.m(DateTime$Reventless.string)
1043
+ })));
1044
+ let keyOf = (name, key) => Stdlib_Option.flatMap(getPropertyOf(json, name), s => getProperty(s, key));
1045
+ globalThis.test("the field is an object, not a string", () => {
1046
+ globalThis.expect(keyOf("deliveryWindow", "type")).toEqual("object");
1047
+ });
1048
+ globalThis.test("it carries the dateRange id, sourced from the type", () => {
1049
+ globalThis.expect([
1050
+ keyOf("deliveryWindow", "x-reventless-semantic"),
1051
+ keyOf("deliveryWindow", "x-reventless-semantic-source")
1052
+ ]).toEqual([
1053
+ "dateRange",
1054
+ "type"
1055
+ ]);
1056
+ });
1057
+ globalThis.test("both instants keep their own date-time marker", () => {
1058
+ let inner = Stdlib_Option.getOr(getPropertyOf(json, "deliveryWindow"), null);
1059
+ globalThis.expect([
1060
+ Stdlib_Option.flatMap(getPropertyOf(inner, "start"), s => getProperty(s, "format")),
1061
+ Stdlib_Option.flatMap(getPropertyOf(inner, "end"), s => getProperty(s, "format"))
1062
+ ]).toEqual([
1063
+ "date-time",
1064
+ "date-time"
1065
+ ]);
1066
+ });
1067
+ globalThis.test("a lone date-time field stays a string", () => {
1068
+ globalThis.expect([
1069
+ keyOf("orderedAt", "type"),
1070
+ keyOf("orderedAt", "format")
1071
+ ]).toEqual([
1072
+ "string",
1073
+ "date-time"
1074
+ ]);
1075
+ });
1076
+ });
984
1077
  });
985
1078
 
986
1079
  export {
@@ -7,8 +7,7 @@ S.enableJson()
7
7
  // included — to JSON, which no-ops `bold`/`fmtComp`. The human-format
8
8
  // assertions below expect ANSI, so force text mode for this file. The
9
9
  // "JSON sink" block flips to json and restores text in its own setup.
10
- @val external _processEnv: Dict.t<string> = "process.env"
11
- _processEnv->Dict.set("REVENTLESS_LOG_FORMAT", "text")
10
+ NodeProcess.env->Dict.set("REVENTLESS_LOG_FORMAT", "text")
12
11
  Reventless.AnsiStyle.reload()
13
12
 
14
13
  describe("LogFormat", () => {
@@ -193,11 +192,11 @@ describe("JSON sink", () => {
193
192
  // Flip to json for the whole block; restore text afterwards so a re-run of the
194
193
  // file (or shared module state) keeps the human-format assertions valid.
195
194
  beforeAll(() => {
196
- _processEnv->Dict.set("REVENTLESS_LOG_FORMAT", "json")
195
+ NodeProcess.env->Dict.set("REVENTLESS_LOG_FORMAT", "json")
197
196
  Reventless.AnsiStyle.reload()
198
197
  })
199
198
  afterAll(() => {
200
- _processEnv->Dict.set("REVENTLESS_LOG_FORMAT", "text")
199
+ NodeProcess.env->Dict.set("REVENTLESS_LOG_FORMAT", "text")
201
200
  Reventless.AnsiStyle.reload()
202
201
  })
203
202
 
@@ -344,10 +343,10 @@ describe("JSON sink", () => {
344
343
  })
345
344
 
346
345
  testSync("service field is emitted when REVENTLESS_SERVICE is set", () => {
347
- _processEnv->Dict.set("REVENTLESS_SERVICE", "TestService")
346
+ NodeProcess.env->Dict.set("REVENTLESS_SERVICE", "TestService")
348
347
  let lines = captureLogs(() => log.info(~comp="Platform", "tick"))
349
348
  let got = lines->Array.get(0)->Option.flatMap(l => l->fieldOf("service"))
350
- _processEnv->Dict.delete("REVENTLESS_SERVICE")
349
+ NodeProcess.env->Dict.delete("REVENTLESS_SERVICE")
351
350
  expect(got)->toEqual(Some("TestService"))
352
351
  })
353
352
 
@@ -4,16 +4,13 @@
4
4
 
5
5
  open JestGlobals
6
6
 
7
- // Write a UTF-8 string to a file synchronously (no return value).
8
- @module("fs") external writeFileSync: (string, string) => unit = "writeFileSync"
9
-
10
7
  // Counter for unique temp file names — avoids cross-test collisions.
11
8
  let fileCounter = ref(0)
12
9
 
13
10
  let writeTempCsv = (content: string): string => {
14
11
  fileCounter := fileCounter.contents + 1
15
12
  let path = `/tmp/reventless-csvstream-test-${Int.toString(fileCounter.contents)}.csv`
16
- writeFileSync(path, content)
13
+ NodeFs.writeFileSync(path, content)
17
14
  path
18
15
  }
19
16
 
@@ -1,7 +1,7 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
- import * as Fs from "fs";
4
3
  import * as Stream from "@reventlessdev/rescript-effect/src/Stream.res.mjs";
4
+ import * as Nodefs from "node:fs";
5
5
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
6
6
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
7
7
  import * as Effect from "effect/Effect";
@@ -15,7 +15,7 @@ let fileCounter = {
15
15
  function writeTempCsv(content) {
16
16
  fileCounter.contents = fileCounter.contents + 1 | 0;
17
17
  let path = `/tmp/reventless-csvstream-test-` + fileCounter.contents.toString() + `.csv`;
18
- Fs.writeFileSync(path, content);
18
+ Nodefs.writeFileSync(path, content, "utf8");
19
19
  return path;
20
20
  }
21
21