@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 +22 -0
- package/package.json +12 -12
- package/rescript.json +1 -1
- package/src/plugin/component/Plugin_Helpers.res +7 -9
- package/src/util/FTPHandler.res.mjs +2 -2
- package/tests/api/SuryToJsonSchemaTest.res +117 -0
- package/tests/api/SuryToJsonSchemaTest.res.mjs +93 -0
- package/tests/logger/LogFormatTest.res +5 -6
- package/tests/util/CsvStreamTest.res +1 -4
- package/tests/util/CsvStreamTest.res.mjs +2 -2
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.
|
|
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.
|
|
32
|
-
"@reventlessdev/rescript-
|
|
33
|
-
"@reventlessdev/rescript-
|
|
34
|
-
"@reventlessdev/rescript-jest": "1.0.0-alpha.
|
|
35
|
-
"@reventlessdev/rescript-node
|
|
36
|
-
"@reventlessdev/rescript-
|
|
37
|
-
"@reventlessdev/rescript-
|
|
38
|
-
"@reventlessdev/
|
|
39
|
-
"@reventlessdev/reventless-
|
|
40
|
-
"@reventlessdev/reventless-spec": "3.0.0-alpha.
|
|
41
|
-
"@reventlessdev/
|
|
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
|
|
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",
|
|
1177
|
-
("actor",
|
|
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
|
-
|
|
1517
|
+
NodeProcess.env
|
|
1520
1518
|
->Dict.get("GITHUB_ACTOR")
|
|
1521
|
-
->Option.orElse(
|
|
1522
|
-
->Option.orElse(
|
|
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
|
-
|
|
1523
|
+
NodeProcess.env
|
|
1526
1524
|
->Dict.get("GITHUB_SHA")
|
|
1527
|
-
->Option.orElse(
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
195
|
+
NodeProcess.env->Dict.set("REVENTLESS_LOG_FORMAT", "json")
|
|
197
196
|
Reventless.AnsiStyle.reload()
|
|
198
197
|
})
|
|
199
198
|
afterAll(() => {
|
|
200
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
18
|
+
Nodefs.writeFileSync(path, content, "utf8");
|
|
19
19
|
return path;
|
|
20
20
|
}
|
|
21
21
|
|