@reventlessdev/reventless-spec 3.0.0-alpha.140 → 3.0.0-alpha.142
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 +15 -0
- package/README.md +1 -1
- package/package.json +7 -7
- package/src/components/DcbTag.res.mjs +2 -2
- package/src/components/Plugin.res.mjs +3 -3
- package/src/components/TraitCertificate.res.mjs +3 -3
- package/src/components/TraitManifest.res.mjs +2 -2
- package/src/generator/PlatformGenerator.res.mjs +2 -2
- package/src/semantic/Bytes.res.mjs +1 -1
- package/src/semantic/CalendarDate.res +1 -1
- package/src/semantic/CalendarDate.res.mjs +1 -2
- package/src/semantic/DateTime.res +3 -3
- package/src/semantic/DateTime.res.mjs +2 -3
- package/src/semantic/Duration.res.mjs +1 -1
- package/src/semantic/Email.res +1 -1
- package/src/semantic/Email.res.mjs +1 -2
- package/src/semantic/GeoPoint.res.mjs +2 -2
- package/src/semantic/Money.res +1 -1
- package/src/semantic/Money.res.mjs +1 -1
- package/src/semantic/Offload.res.mjs +1 -1
- package/src/semantic/Percent.res.mjs +1 -1
- package/src/semantic/Semantic.res +155 -12
- package/src/semantic/Semantic.res.mjs +160 -13
- package/src/semantic/Template.res.mjs +3 -3
- package/src/semantic/Url.res +1 -1
- package/src/semantic/Url.res.mjs +1 -2
- package/src/types/AccountsManifest.res.mjs +1 -2
- package/src/types/Identity.res.mjs +1 -1
- package/src/types/Lifecycle.res.mjs +1 -1
- package/src/types/Message.res.mjs +2 -2
- package/src/types/Schedule.res.mjs +8 -8
- package/src/util/Util_Sury.res +6 -3
- package/src/util/Util_Sury.res.mjs +6 -4
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,21 @@
|
|
|
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.142 (2026-09-22)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **spec:** every semantic type says how to write a value ([b661296](https://github.com/ReventlessDev/reventless-core/commit/b661296c1b7c94109e2187efdf48f5643c507f28))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# 3.0.0-alpha.141 (2026-09-21)
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* **deps:** update rescript to 12.3.1 ([ea1ce37](https://github.com/ReventlessDev/reventless-core/commit/ea1ce377297640f6d0d360e6c84c778d59ecbcc5))
|
|
18
|
+
* **deps:** update sury to 11.0.0 ([8715b22](https://github.com/ReventlessDev/reventless-core/commit/8715b22b33856c46ba57518aad151065c123a995))
|
|
19
|
+
|
|
20
|
+
|
|
6
21
|
# 3.0.0-alpha.140 (2026-09-21)
|
|
7
22
|
|
|
8
23
|
* feat(spec)!: typed projection keys ([fc6a018](https://github.com/ReventlessDev/reventless-core/commit/fc6a01811ba788f3289e84a435a09f77fa627e1a))
|
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-spec",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.142",
|
|
4
4
|
"description": "Specifications for Reventless",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"bin": {
|
|
@@ -23,17 +23,17 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"jsonschema2graphql": "1.1.1",
|
|
26
|
-
"sury": "11.0.0
|
|
27
|
-
"sury-ppx": "11.0.0
|
|
26
|
+
"sury": "11.0.0",
|
|
27
|
+
"sury-ppx": "11.0.0",
|
|
28
28
|
"yaml": "^2.8.3",
|
|
29
|
-
"@reventlessdev/rescript-node": "2.0.0-alpha.
|
|
29
|
+
"@reventlessdev/rescript-node": "2.0.0-alpha.13"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"rescript": "12.3.
|
|
33
|
-
"@reventlessdev/rescript-jest": "1.0.0-alpha.
|
|
32
|
+
"rescript": "12.3.1",
|
|
33
|
+
"@reventlessdev/rescript-jest": "1.0.0-alpha.11"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
|
-
"rescript": "12.3.
|
|
36
|
+
"rescript": "^12.3.1"
|
|
37
37
|
},
|
|
38
38
|
"publishConfig": {
|
|
39
39
|
"registry": "https://registry.npmjs.org"
|
|
@@ -54,7 +54,7 @@ function stringForKey(key) {
|
|
|
54
54
|
return markForKey(Sury.string, key);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
let int = Sury.$Metadata_set(Sury.
|
|
57
|
+
let int = Sury.$Metadata_set(Sury.int32, dcbTagId, true);
|
|
58
58
|
|
|
59
59
|
let partition = markPartition(Sury.string);
|
|
60
60
|
|
|
@@ -88,7 +88,7 @@ let derivedPartitionTagSchema = Sury.union([
|
|
|
88
88
|
})),
|
|
89
89
|
Sury.$schema(s => ({
|
|
90
90
|
TAG: "ByEventType",
|
|
91
|
-
_0: s.m(Sury.
|
|
91
|
+
_0: s.m(Sury.record(Sury.string))
|
|
92
92
|
}))
|
|
93
93
|
]);
|
|
94
94
|
|
|
@@ -61,7 +61,7 @@ let menuEntrySchema = Sury.$schema(s => ({
|
|
|
61
61
|
label: s.m(Sury.string),
|
|
62
62
|
icon: s.m(Sury.$option(Sury.string)),
|
|
63
63
|
group: s.m(Sury.$option(Sury.string)),
|
|
64
|
-
sortOrder: s.m(Sury.
|
|
64
|
+
sortOrder: s.m(Sury.int32)
|
|
65
65
|
}));
|
|
66
66
|
|
|
67
67
|
let pageManifestEntrySchema = Sury.$schema(s => ({
|
|
@@ -100,7 +100,7 @@ let commandDefSchema = Sury.$schema(s => ({
|
|
|
100
100
|
allowedStates: s.m(Sury.$option(Sury.array(Sury.string))),
|
|
101
101
|
targetState: s.m(Sury.$option(Sury.string)),
|
|
102
102
|
allowedStatesSource: s.m(Sury.$option(Sury.string)),
|
|
103
|
-
apiExposed: s.m(Sury.$option(Sury.
|
|
103
|
+
apiExposed: s.m(Sury.$option(Sury.boolean)),
|
|
104
104
|
requiredAccess: s.m(Sury.$option(Sury.array(Sury.string))),
|
|
105
105
|
ownerField: s.m(Sury.$option(Sury.string))
|
|
106
106
|
}));
|
|
@@ -118,7 +118,7 @@ let queryableDefSchema = Sury.$schema(s => ({
|
|
|
118
118
|
ownerField: s.m(Sury.$option(Sury.string)),
|
|
119
119
|
retiredField: s.m(Sury.$option(Sury.string)),
|
|
120
120
|
retiredValues: s.m(Sury.$option(Sury.array(Sury.string))),
|
|
121
|
-
namedWhenRetired: s.m(Sury.$option(Sury.
|
|
121
|
+
namedWhenRetired: s.m(Sury.$option(Sury.boolean)),
|
|
122
122
|
visibility: s.m(Sury.$option(Sury.string)),
|
|
123
123
|
chapter: s.m(Sury.$option(Sury.string)),
|
|
124
124
|
singleQueryField: s.m(Sury.$option(Sury.string)),
|
|
@@ -5,7 +5,7 @@ import * as Util_Sury$Reventless from "../util/Util_Sury.res.mjs";
|
|
|
5
5
|
|
|
6
6
|
let assertionSchema = Sury.$schema(s => ({
|
|
7
7
|
name: s.m(Sury.string),
|
|
8
|
-
passed: s.m(Sury.
|
|
8
|
+
passed: s.m(Sury.boolean)
|
|
9
9
|
}));
|
|
10
10
|
|
|
11
11
|
let schema = Sury.$schema(s => ({
|
|
@@ -15,8 +15,8 @@ let schema = Sury.$schema(s => ({
|
|
|
15
15
|
host: s.m(Sury.string),
|
|
16
16
|
suite: s.m(Sury.string),
|
|
17
17
|
assertions: s.m(Sury.array(assertionSchema)),
|
|
18
|
-
passed: s.m(Sury.
|
|
19
|
-
failed: s.m(Sury.
|
|
18
|
+
passed: s.m(Sury.int32),
|
|
19
|
+
failed: s.m(Sury.int32)
|
|
20
20
|
}));
|
|
21
21
|
|
|
22
22
|
function verified(certificate) {
|
|
@@ -6,7 +6,7 @@ import * as Util_Sury$Reventless from "../util/Util_Sury.res.mjs";
|
|
|
6
6
|
|
|
7
7
|
let configFieldSchema = Sury.$schema(s => ({
|
|
8
8
|
name: s.m(Sury.string),
|
|
9
|
-
required: s.m(Sury.
|
|
9
|
+
required: s.m(Sury.boolean)
|
|
10
10
|
}));
|
|
11
11
|
|
|
12
12
|
let schema = Sury.$schema(s => ({
|
|
@@ -16,7 +16,7 @@ let schema = Sury.$schema(s => ({
|
|
|
16
16
|
license: s.m(Sury.string),
|
|
17
17
|
capabilities: s.m(Sury.array(Sury.string)),
|
|
18
18
|
config: s.m(Sury.array(configFieldSchema)),
|
|
19
|
-
scaffolded: s.m(Sury.
|
|
19
|
+
scaffolded: s.m(Sury.boolean)
|
|
20
20
|
}));
|
|
21
21
|
|
|
22
22
|
function render(manifest) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
-
import * as
|
|
3
|
+
import * as Sury from "sury";
|
|
4
4
|
import * as Yaml from "yaml";
|
|
5
5
|
import * as Nodefs from "node:fs";
|
|
6
6
|
import * as Nodepath from "node:path";
|
|
@@ -73,7 +73,7 @@ if (manifestArg === "") {
|
|
|
73
73
|
console.log(`Read: ` + pluginName + ` — ` + path + ` (via ` + PlatformManifests$Reventless.describeVia(param.via) + `)`);
|
|
74
74
|
let manifest;
|
|
75
75
|
try {
|
|
76
|
-
manifest =
|
|
76
|
+
manifest = Sury.parseOrThrow(JSON.parse(Nodefs.readFileSync(path, "utf8")), CapabilityManifest$Reventless.schema);
|
|
77
77
|
} catch (exn) {
|
|
78
78
|
fail(`could not parse ` + path + ` as a capability manifest`);
|
|
79
79
|
manifest = {
|
|
@@ -29,7 +29,7 @@ function fromFloat(raw) {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
let schema = Semantic$Reventless.refined(Sury.
|
|
32
|
+
let schema = Semantic$Reventless.refined(Sury.number, Semantic$Reventless.Id.bytes, fromFloat);
|
|
33
33
|
|
|
34
34
|
let units = [
|
|
35
35
|
"B",
|
|
@@ -40,7 +40,7 @@ external toString: t => string = "%identity"
|
|
|
40
40
|
|
|
41
41
|
// Sury's rule, held once. `fromString` runs it rather than restating it, and
|
|
42
42
|
// `schema` is built from `fromString`, so there is exactly one grammar here.
|
|
43
|
-
let grammar: S.t<string> = S.isoDate
|
|
43
|
+
let grammar: S.t<string> = (S.isoDate :> S.t<string>)
|
|
44
44
|
|
|
45
45
|
/** Validate a raw string as an ISO-8601 calendar day, saying why when it is not one. */
|
|
46
46
|
let fromString = (raw: string): result<t, string> =>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
-
import * as S from "sury/src/S.res.mjs";
|
|
4
3
|
import * as Sury from "sury";
|
|
5
4
|
import * as Semantic$Reventless from "./Semantic.res.mjs";
|
|
6
5
|
|
|
@@ -9,7 +8,7 @@ let grammar = Sury.isoDate;
|
|
|
9
8
|
function fromString(raw) {
|
|
10
9
|
let value;
|
|
11
10
|
try {
|
|
12
|
-
value =
|
|
11
|
+
value = Sury.parseOrThrow(raw, grammar);
|
|
13
12
|
} catch (exn) {
|
|
14
13
|
return {
|
|
15
14
|
TAG: "Error",
|
|
@@ -3,14 +3,14 @@ Marks a `string` field as an ISO-8601 instant.
|
|
|
3
3
|
|
|
4
4
|
## The grammar
|
|
5
5
|
|
|
6
|
-
Sury's `S.
|
|
6
|
+
Sury's `S.utcDateTime`, and nothing added — the same rule the branded scalars
|
|
7
7
|
follow. `S.datetime` is the other binding and is not this one: it *transforms* to
|
|
8
8
|
`Js.Date.t`, changing the field's runtime type, where this keeps the string the
|
|
9
9
|
projection wrote.
|
|
10
10
|
|
|
11
11
|
## UTC only
|
|
12
12
|
|
|
13
|
-
`S.
|
|
13
|
+
`S.utcDateTime` accepts a `Z` instant and rejects an offset — `2026-03-02T09:00:00+01:00`
|
|
14
14
|
and a bare `2026-03-02T09:00:00` both fail. That is stricter than RFC 3339 and it
|
|
15
15
|
is the strictness worth adopting: every instant the framework produces comes from
|
|
16
16
|
`Message.nowAsISOString`, which is always `Z`. "Instants are stored in UTC" is a
|
|
@@ -48,7 +48,7 @@ external toString: t => string = "%identity"
|
|
|
48
48
|
|
|
49
49
|
// Sury's rule, held once. `fromString` runs it rather than restating it, and
|
|
50
50
|
// `schema` is built from `fromString`, so there is exactly one grammar here.
|
|
51
|
-
let grammar: S.t<string> = S.
|
|
51
|
+
let grammar: S.t<string> = (S.utcDateTime :> S.t<string>)
|
|
52
52
|
|
|
53
53
|
/** Validate a raw string as a UTC ISO-8601 instant, saying why when it is not one. */
|
|
54
54
|
let fromString = (raw: string): result<t, string> =>
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
-
import * as S from "sury/src/S.res.mjs";
|
|
4
3
|
import * as Sury from "sury";
|
|
5
4
|
import * as Primitive_float from "@rescript/runtime/lib/es6/Primitive_float.js";
|
|
6
5
|
import * as Semantic$Reventless from "./Semantic.res.mjs";
|
|
7
6
|
|
|
8
|
-
let grammar = Sury.
|
|
7
|
+
let grammar = Sury.utcDateTime;
|
|
9
8
|
|
|
10
9
|
function fromString(raw) {
|
|
11
10
|
let value;
|
|
12
11
|
try {
|
|
13
|
-
value =
|
|
12
|
+
value = Sury.parseOrThrow(raw, grammar);
|
|
14
13
|
} catch (exn) {
|
|
15
14
|
return {
|
|
16
15
|
TAG: "Error",
|
package/src/semantic/Email.res
CHANGED
|
@@ -37,7 +37,7 @@ external toString: t => string = "%identity"
|
|
|
37
37
|
|
|
38
38
|
// Sury's check, held once. `fromString` runs it rather than restating it, and
|
|
39
39
|
// `schema` is built from `fromString`, so there is exactly one grammar here.
|
|
40
|
-
let grammar: S.t<string> = S.email
|
|
40
|
+
let grammar: S.t<string> = (S.email :> S.t<string>)
|
|
41
41
|
|
|
42
42
|
/** Validate a raw string as an email address, saying why when it is not one. */
|
|
43
43
|
let fromString = (raw: string): result<t, string> =>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
-
import * as S from "sury/src/S.res.mjs";
|
|
4
3
|
import * as Sury from "sury";
|
|
5
4
|
import * as Semantic$Reventless from "./Semantic.res.mjs";
|
|
6
5
|
|
|
@@ -9,7 +8,7 @@ let grammar = Sury.email;
|
|
|
9
8
|
function fromString(raw) {
|
|
10
9
|
let value;
|
|
11
10
|
try {
|
|
12
|
-
value =
|
|
11
|
+
value = Sury.parseOrThrow(raw, grammar);
|
|
13
12
|
} catch (exn) {
|
|
14
13
|
return {
|
|
15
14
|
TAG: "Error",
|
|
@@ -48,12 +48,12 @@ function validateLng(raw) {
|
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
let latSchema = S.refine(Sury.
|
|
51
|
+
let latSchema = S.refine(Sury.number, raw => {
|
|
52
52
|
let match = validateLat(raw);
|
|
53
53
|
return match.TAG === "Ok";
|
|
54
54
|
}, "expected a latitude in -90…90", undefined);
|
|
55
55
|
|
|
56
|
-
let lngSchema = S.refine(Sury.
|
|
56
|
+
let lngSchema = S.refine(Sury.number, raw => {
|
|
57
57
|
let match = validateLng(raw);
|
|
58
58
|
return match.TAG === "Ok";
|
|
59
59
|
}, "expected a longitude in -180…180", undefined);
|
package/src/semantic/Money.res
CHANGED
|
@@ -91,7 +91,7 @@ let validateAmount = (amount: float): result<float, string> =>
|
|
|
91
91
|
The check sits on the field rather than on the pair because wholeness is a
|
|
92
92
|
property of the amount: it does not need the currency, and a rule placed
|
|
93
93
|
where it needs nothing else is a rule that cannot be read wrong. (A
|
|
94
|
-
record-level refinement is available — sury 11
|
|
94
|
+
record-level refinement is available — sury 11 compiles one
|
|
95
95
|
correctly, where 11-alpha hoisted the result object above the field reads and
|
|
96
96
|
threw `Cannot access 'v0' before initialization` — it is simply not what this
|
|
97
97
|
check wants.) */
|
|
@@ -28,7 +28,7 @@ function validateAmount(amount) {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
let amountSchema = S.refine(Sury.
|
|
31
|
+
let amountSchema = S.refine(Sury.number, amount => {
|
|
32
32
|
let match = validateAmount(amount);
|
|
33
33
|
return match.TAG === "Ok";
|
|
34
34
|
}, "expected a monetary amount", undefined);
|
|
@@ -24,7 +24,7 @@ function fromFloat(raw) {
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
let schema = Semantic$Reventless.refined(Sury.
|
|
27
|
+
let schema = Semantic$Reventless.refined(Sury.number, Semantic$Reventless.Id.percent, fromFloat);
|
|
28
28
|
|
|
29
29
|
function format(p) {
|
|
30
30
|
return p.toString() + "%";
|
|
@@ -133,6 +133,11 @@ type brandedString = {
|
|
|
133
133
|
derive. Offering one as a plain type pick emits code that does not
|
|
134
134
|
compile. */
|
|
135
135
|
hasDerivableSchema: bool,
|
|
136
|
+
/** A value the module accepts, as the string itself (not as source): a tool
|
|
137
|
+
writes it as a string literal. An obvious example, never plausible data,
|
|
138
|
+
because a new scenario starts from it. For the modules without a derivable
|
|
139
|
+
schema it is a value their schema accepts for any store or collection. */
|
|
140
|
+
sample: string,
|
|
136
141
|
}
|
|
137
142
|
|
|
138
143
|
/** Every transparent-string semantic, so a consumer can ask what they are
|
|
@@ -149,18 +154,156 @@ type brandedString = {
|
|
|
149
154
|
recomputes them rather than trusting this list — so adding a module without
|
|
150
155
|
adding it here fails, and so does the ppx's copy drifting from either. */
|
|
151
156
|
let brandedStrings: array<brandedString> = [
|
|
152
|
-
{
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
{moduleName: "
|
|
159
|
-
{moduleName: "
|
|
160
|
-
|
|
161
|
-
{moduleName: "
|
|
162
|
-
{moduleName: "
|
|
163
|
-
{moduleName: "
|
|
157
|
+
{
|
|
158
|
+
moduleName: "DateTime",
|
|
159
|
+
id: Id.dateTime,
|
|
160
|
+
hasDerivableSchema: true,
|
|
161
|
+
sample: "2024-01-01T00:00:00Z",
|
|
162
|
+
},
|
|
163
|
+
{moduleName: "CalendarDate", id: Id.date, hasDerivableSchema: true, sample: "2024-01-01"},
|
|
164
|
+
{moduleName: "Email", id: Id.email, hasDerivableSchema: true, sample: "ada@example.com"},
|
|
165
|
+
// 555-0100…0199 is the North American range reserved for fiction.
|
|
166
|
+
{moduleName: "Phone", id: Id.phone, hasDerivableSchema: true, sample: "+15555550100"},
|
|
167
|
+
{moduleName: "Url", id: Id.url, hasDerivableSchema: true, sample: "https://example.com"},
|
|
168
|
+
{moduleName: "Color", id: Id.color, hasDerivableSchema: true, sample: "#1e90ff"},
|
|
169
|
+
{
|
|
170
|
+
moduleName: "FileRef",
|
|
171
|
+
id: Id.fileRef,
|
|
172
|
+
hasDerivableSchema: true,
|
|
173
|
+
sample: "https://example.com/example.pdf",
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
moduleName: "ImageRef",
|
|
177
|
+
id: Id.imageRef,
|
|
178
|
+
hasDerivableSchema: true,
|
|
179
|
+
sample: "https://example.com/example.png",
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
moduleName: "MemberRef",
|
|
183
|
+
id: Id.memberRef,
|
|
184
|
+
hasDerivableSchema: false,
|
|
185
|
+
sample: "/example/example.png",
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
moduleName: "StorageRef",
|
|
189
|
+
id: Id.storageRef,
|
|
190
|
+
hasDerivableSchema: false,
|
|
191
|
+
sample: "/example/example.txt",
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
moduleName: "UploadableFile",
|
|
195
|
+
id: Id.uploadableFile,
|
|
196
|
+
hasDerivableSchema: false,
|
|
197
|
+
sample: "/example/example.pdf",
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
moduleName: "UploadableImage",
|
|
201
|
+
id: Id.uploadableImage,
|
|
202
|
+
hasDerivableSchema: false,
|
|
203
|
+
sample: "/example/example.png",
|
|
204
|
+
},
|
|
205
|
+
]
|
|
206
|
+
|
|
207
|
+
/** How a value of a `valueType` is put together, so a tool can offer an input
|
|
208
|
+
that fits it. */
|
|
209
|
+
type valueShape =
|
|
210
|
+
/** A number literal. `integer` says whether it is written `3600` or `50.0` —
|
|
211
|
+
a float field does not accept an int literal. */
|
|
212
|
+
| Number({integer: bool})
|
|
213
|
+
/** One of a closed set of codes, each written as the module's constructor. */
|
|
214
|
+
| Codes({codes: array<string>})
|
|
215
|
+
/** A value built from named parts: `(part, module or primitive)`. A module
|
|
216
|
+
names another `semantic/` module (its `sample` fills the part); a primitive
|
|
217
|
+
is `float`, `int` or `string`. */
|
|
218
|
+
| Parts({parts: array<(string, string)>})
|
|
219
|
+
|
|
220
|
+
/** A semantic whose value is not a bare string: how to write one. */
|
|
221
|
+
type valueType = {
|
|
222
|
+
moduleName: string,
|
|
223
|
+
shape: valueShape,
|
|
224
|
+
/** A value, written as ReScript source, fully qualified: what a tool emits.
|
|
225
|
+
An obvious example, never plausible data. */
|
|
226
|
+
sample: string,
|
|
227
|
+
/** How to write one. `$part` is replaced by that part's source: the module's
|
|
228
|
+
constructor where it returns `t` (`Money.make`), a record literal where the
|
|
229
|
+
constructor returns `result`, since a test value should not need
|
|
230
|
+
unwrapping. A `Number` has the one part `$value`, a `Codes` the one part
|
|
231
|
+
`$code`. */
|
|
232
|
+
writer: string,
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** Every semantic whose `type t` is not a bare string and which types a field a
|
|
236
|
+
scenario fills. The type names cannot say how to write one — `Money.t` is a
|
|
237
|
+
record, `Duration.t` an int — so a tool reads it here instead of guessing.
|
|
238
|
+
|
|
239
|
+
`SemanticValueTypesTest` compiles every sample, runs it through its module's
|
|
240
|
+
schema, and checks `Currency`'s codes against `Currency.all`. */
|
|
241
|
+
let valueTypes: array<valueType> = [
|
|
242
|
+
{
|
|
243
|
+
moduleName: "Money",
|
|
244
|
+
shape: Parts({parts: [("amount", "float"), ("currency", "Currency")]}),
|
|
245
|
+
// Whole minor units: 1000 is 10.00 EUR.
|
|
246
|
+
sample: "Reventless.Money.make(~amount=1000.0, ~currency=Reventless.Currency.EUR)",
|
|
247
|
+
writer: "Reventless.Money.make(~amount=$amount, ~currency=$currency)",
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
moduleName: "Currency",
|
|
251
|
+
shape: Codes({codes: Currency.all->Array.map(Currency.toString)}),
|
|
252
|
+
sample: "Reventless.Currency.EUR",
|
|
253
|
+
writer: "Reventless.Currency.$code",
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
moduleName: "DateRange",
|
|
257
|
+
shape: Parts({parts: [("start", "DateTime"), ("end_", "DateTime")]}),
|
|
258
|
+
sample: `{Reventless.DateRange.start: "2024-01-01T00:00:00Z", end_: "2024-01-02T00:00:00Z"}`,
|
|
259
|
+
writer: "{Reventless.DateRange.start: $start, end_: $end_}",
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
moduleName: "GeoPoint",
|
|
263
|
+
shape: Parts({parts: [("lat", "float"), ("lng", "float")]}),
|
|
264
|
+
sample: "{Reventless.GeoPoint.lat: 0.0, lng: 0.0}",
|
|
265
|
+
writer: "{Reventless.GeoPoint.lat: $lat, lng: $lng}",
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
moduleName: "CaptionedImage",
|
|
269
|
+
// `altText` and `caption` are optional fields; the writer gives both.
|
|
270
|
+
shape: Parts({
|
|
271
|
+
parts: [("ref", "UploadableImage"), ("altText", "string"), ("caption", "string")],
|
|
272
|
+
}),
|
|
273
|
+
sample: `{Reventless.CaptionedImage.ref: "/example/example.png", altText: "An example image", caption: "An example caption"}`,
|
|
274
|
+
writer: "{Reventless.CaptionedImage.ref: $ref, altText: $altText, caption: $caption}",
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
moduleName: "Duration",
|
|
278
|
+
shape: Number({integer: true}),
|
|
279
|
+
// Seconds: one hour.
|
|
280
|
+
sample: "3600",
|
|
281
|
+
writer: "$value",
|
|
282
|
+
},
|
|
283
|
+
{moduleName: "Percent", shape: Number({integer: false}), sample: "50.0", writer: "$value"},
|
|
284
|
+
{moduleName: "Bytes", shape: Number({integer: false}), sample: "1024.0", writer: "$value"},
|
|
285
|
+
]
|
|
286
|
+
|
|
287
|
+
/** `semantic/` modules whose `type t` types a field, but whose shape
|
|
288
|
+
`valueShape` cannot express yet. `Geolocation.t` is a union of cases with
|
|
289
|
+
payloads. A tool asks for its value as ReScript source. */
|
|
290
|
+
let valueTypesWithoutWriter: array<string> = ["Geolocation"]
|
|
291
|
+
|
|
292
|
+
/** `semantic/` modules that are not a field's value: capabilities, helpers and
|
|
293
|
+
wrappers the ppx applies. Listed so the coverage test can tell a forgotten
|
|
294
|
+
module from a deliberate omission. */
|
|
295
|
+
let nonValueModules: array<string> = [
|
|
296
|
+
"Capabilities",
|
|
297
|
+
"CapabilityNeed",
|
|
298
|
+
"Geocoding",
|
|
299
|
+
"IdentityProvider",
|
|
300
|
+
"Media_Ref",
|
|
301
|
+
"Messaging",
|
|
302
|
+
"Offload",
|
|
303
|
+
"RowImage",
|
|
304
|
+
"Secrets",
|
|
305
|
+
"Semantic",
|
|
306
|
+
"Template",
|
|
164
307
|
]
|
|
165
308
|
|
|
166
309
|
let semanticId: S.Metadata.Id.t<t> = S.Metadata.Id.make(~namespace="reventless", ~name="semantic")
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import * as S from "sury/src/S.res.mjs";
|
|
4
4
|
import * as Sury from "sury";
|
|
5
5
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
|
+
import * as Currency$Reventless from "./Currency.res.mjs";
|
|
6
7
|
|
|
7
8
|
let dateTime = "dateTime";
|
|
8
9
|
|
|
@@ -59,65 +60,208 @@ let brandedStrings = [
|
|
|
59
60
|
{
|
|
60
61
|
moduleName: "DateTime",
|
|
61
62
|
id: dateTime,
|
|
62
|
-
hasDerivableSchema: true
|
|
63
|
+
hasDerivableSchema: true,
|
|
64
|
+
sample: "2024-01-01T00:00:00Z"
|
|
63
65
|
},
|
|
64
66
|
{
|
|
65
67
|
moduleName: "CalendarDate",
|
|
66
68
|
id: date,
|
|
67
|
-
hasDerivableSchema: true
|
|
69
|
+
hasDerivableSchema: true,
|
|
70
|
+
sample: "2024-01-01"
|
|
68
71
|
},
|
|
69
72
|
{
|
|
70
73
|
moduleName: "Email",
|
|
71
74
|
id: email,
|
|
72
|
-
hasDerivableSchema: true
|
|
75
|
+
hasDerivableSchema: true,
|
|
76
|
+
sample: "ada@example.com"
|
|
73
77
|
},
|
|
74
78
|
{
|
|
75
79
|
moduleName: "Phone",
|
|
76
80
|
id: phone,
|
|
77
|
-
hasDerivableSchema: true
|
|
81
|
+
hasDerivableSchema: true,
|
|
82
|
+
sample: "+15555550100"
|
|
78
83
|
},
|
|
79
84
|
{
|
|
80
85
|
moduleName: "Url",
|
|
81
86
|
id: url,
|
|
82
|
-
hasDerivableSchema: true
|
|
87
|
+
hasDerivableSchema: true,
|
|
88
|
+
sample: "https://example.com"
|
|
83
89
|
},
|
|
84
90
|
{
|
|
85
91
|
moduleName: "Color",
|
|
86
92
|
id: color,
|
|
87
|
-
hasDerivableSchema: true
|
|
93
|
+
hasDerivableSchema: true,
|
|
94
|
+
sample: "#1e90ff"
|
|
88
95
|
},
|
|
89
96
|
{
|
|
90
97
|
moduleName: "FileRef",
|
|
91
98
|
id: fileRef,
|
|
92
|
-
hasDerivableSchema: true
|
|
99
|
+
hasDerivableSchema: true,
|
|
100
|
+
sample: "https://example.com/example.pdf"
|
|
93
101
|
},
|
|
94
102
|
{
|
|
95
103
|
moduleName: "ImageRef",
|
|
96
104
|
id: imageRef,
|
|
97
|
-
hasDerivableSchema: true
|
|
105
|
+
hasDerivableSchema: true,
|
|
106
|
+
sample: "https://example.com/example.png"
|
|
98
107
|
},
|
|
99
108
|
{
|
|
100
109
|
moduleName: "MemberRef",
|
|
101
110
|
id: memberRef,
|
|
102
|
-
hasDerivableSchema: false
|
|
111
|
+
hasDerivableSchema: false,
|
|
112
|
+
sample: "/example/example.png"
|
|
103
113
|
},
|
|
104
114
|
{
|
|
105
115
|
moduleName: "StorageRef",
|
|
106
116
|
id: storageRef,
|
|
107
|
-
hasDerivableSchema: false
|
|
117
|
+
hasDerivableSchema: false,
|
|
118
|
+
sample: "/example/example.txt"
|
|
108
119
|
},
|
|
109
120
|
{
|
|
110
121
|
moduleName: "UploadableFile",
|
|
111
122
|
id: uploadableFile,
|
|
112
|
-
hasDerivableSchema: false
|
|
123
|
+
hasDerivableSchema: false,
|
|
124
|
+
sample: "/example/example.pdf"
|
|
113
125
|
},
|
|
114
126
|
{
|
|
115
127
|
moduleName: "UploadableImage",
|
|
116
128
|
id: uploadableImage,
|
|
117
|
-
hasDerivableSchema: false
|
|
129
|
+
hasDerivableSchema: false,
|
|
130
|
+
sample: "/example/example.png"
|
|
118
131
|
}
|
|
119
132
|
];
|
|
120
133
|
|
|
134
|
+
let valueTypes = [
|
|
135
|
+
{
|
|
136
|
+
moduleName: "Money",
|
|
137
|
+
shape: {
|
|
138
|
+
TAG: "Parts",
|
|
139
|
+
parts: [
|
|
140
|
+
[
|
|
141
|
+
"amount",
|
|
142
|
+
"float"
|
|
143
|
+
],
|
|
144
|
+
[
|
|
145
|
+
"currency",
|
|
146
|
+
"Currency"
|
|
147
|
+
]
|
|
148
|
+
]
|
|
149
|
+
},
|
|
150
|
+
sample: "Reventless.Money.make(~amount=1000.0, ~currency=Reventless.Currency.EUR)",
|
|
151
|
+
writer: "Reventless.Money.make(~amount=$amount, ~currency=$currency)"
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
moduleName: "Currency",
|
|
155
|
+
shape: {
|
|
156
|
+
TAG: "Codes",
|
|
157
|
+
codes: Currency$Reventless.all.map(Currency$Reventless.toString)
|
|
158
|
+
},
|
|
159
|
+
sample: "Reventless.Currency.EUR",
|
|
160
|
+
writer: "Reventless.Currency.$code"
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
moduleName: "DateRange",
|
|
164
|
+
shape: {
|
|
165
|
+
TAG: "Parts",
|
|
166
|
+
parts: [
|
|
167
|
+
[
|
|
168
|
+
"start",
|
|
169
|
+
"DateTime"
|
|
170
|
+
],
|
|
171
|
+
[
|
|
172
|
+
"end_",
|
|
173
|
+
"DateTime"
|
|
174
|
+
]
|
|
175
|
+
]
|
|
176
|
+
},
|
|
177
|
+
sample: `{Reventless.DateRange.start: "2024-01-01T00:00:00Z", end_: "2024-01-02T00:00:00Z"}`,
|
|
178
|
+
writer: "{Reventless.DateRange.start: $start, end_: $end_}"
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
moduleName: "GeoPoint",
|
|
182
|
+
shape: {
|
|
183
|
+
TAG: "Parts",
|
|
184
|
+
parts: [
|
|
185
|
+
[
|
|
186
|
+
"lat",
|
|
187
|
+
"float"
|
|
188
|
+
],
|
|
189
|
+
[
|
|
190
|
+
"lng",
|
|
191
|
+
"float"
|
|
192
|
+
]
|
|
193
|
+
]
|
|
194
|
+
},
|
|
195
|
+
sample: "{Reventless.GeoPoint.lat: 0.0, lng: 0.0}",
|
|
196
|
+
writer: "{Reventless.GeoPoint.lat: $lat, lng: $lng}"
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
moduleName: "CaptionedImage",
|
|
200
|
+
shape: {
|
|
201
|
+
TAG: "Parts",
|
|
202
|
+
parts: [
|
|
203
|
+
[
|
|
204
|
+
"ref",
|
|
205
|
+
"UploadableImage"
|
|
206
|
+
],
|
|
207
|
+
[
|
|
208
|
+
"altText",
|
|
209
|
+
"string"
|
|
210
|
+
],
|
|
211
|
+
[
|
|
212
|
+
"caption",
|
|
213
|
+
"string"
|
|
214
|
+
]
|
|
215
|
+
]
|
|
216
|
+
},
|
|
217
|
+
sample: `{Reventless.CaptionedImage.ref: "/example/example.png", altText: "An example image", caption: "An example caption"}`,
|
|
218
|
+
writer: "{Reventless.CaptionedImage.ref: $ref, altText: $altText, caption: $caption}"
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
moduleName: "Duration",
|
|
222
|
+
shape: {
|
|
223
|
+
TAG: "Number",
|
|
224
|
+
integer: true
|
|
225
|
+
},
|
|
226
|
+
sample: "3600",
|
|
227
|
+
writer: "$value"
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
moduleName: "Percent",
|
|
231
|
+
shape: {
|
|
232
|
+
TAG: "Number",
|
|
233
|
+
integer: false
|
|
234
|
+
},
|
|
235
|
+
sample: "50.0",
|
|
236
|
+
writer: "$value"
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
moduleName: "Bytes",
|
|
240
|
+
shape: {
|
|
241
|
+
TAG: "Number",
|
|
242
|
+
integer: false
|
|
243
|
+
},
|
|
244
|
+
sample: "1024.0",
|
|
245
|
+
writer: "$value"
|
|
246
|
+
}
|
|
247
|
+
];
|
|
248
|
+
|
|
249
|
+
let valueTypesWithoutWriter = ["Geolocation"];
|
|
250
|
+
|
|
251
|
+
let nonValueModules = [
|
|
252
|
+
"Capabilities",
|
|
253
|
+
"CapabilityNeed",
|
|
254
|
+
"Geocoding",
|
|
255
|
+
"IdentityProvider",
|
|
256
|
+
"Media_Ref",
|
|
257
|
+
"Messaging",
|
|
258
|
+
"Offload",
|
|
259
|
+
"RowImage",
|
|
260
|
+
"Secrets",
|
|
261
|
+
"Semantic",
|
|
262
|
+
"Template"
|
|
263
|
+
];
|
|
264
|
+
|
|
121
265
|
let semanticId = Sury.$Metadata_Id_make("reventless", "semantic");
|
|
122
266
|
|
|
123
267
|
function mark(schema, id, payloadOpt) {
|
|
@@ -257,6 +401,9 @@ function has(fieldSchema, id) {
|
|
|
257
401
|
export {
|
|
258
402
|
Id,
|
|
259
403
|
brandedStrings,
|
|
404
|
+
valueTypes,
|
|
405
|
+
valueTypesWithoutWriter,
|
|
406
|
+
nonValueModules,
|
|
260
407
|
semanticId,
|
|
261
408
|
mark,
|
|
262
409
|
refined,
|
|
@@ -269,4 +416,4 @@ export {
|
|
|
269
416
|
fieldIdentityKey,
|
|
270
417
|
has,
|
|
271
418
|
}
|
|
272
|
-
/*
|
|
419
|
+
/* valueTypes Not a pure module */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
-
import * as
|
|
3
|
+
import * as Sury from "sury";
|
|
4
4
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
5
5
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
6
|
import * as Stdlib_Result from "@rescript/runtime/lib/es6/Stdlib_Result.js";
|
|
@@ -347,7 +347,7 @@ function isWithheld(schema) {
|
|
|
347
347
|
|
|
348
348
|
function parseSafely(json, schema) {
|
|
349
349
|
try {
|
|
350
|
-
return Primitive_option.some(
|
|
350
|
+
return Primitive_option.some(Sury.parseOrThrow(json, schema));
|
|
351
351
|
} catch (exn) {
|
|
352
352
|
return;
|
|
353
353
|
}
|
|
@@ -542,4 +542,4 @@ export {
|
|
|
542
542
|
renderSource,
|
|
543
543
|
variantSchema,
|
|
544
544
|
}
|
|
545
|
-
/*
|
|
545
|
+
/* sury Not a pure module */
|
package/src/semantic/Url.res
CHANGED
|
@@ -38,7 +38,7 @@ external toString: t => string = "%identity"
|
|
|
38
38
|
// to an append-only log, so the address has to survive the round trip as the
|
|
39
39
|
// caller wrote it. `S.uri` is the string-preserving arm — it still rejects what
|
|
40
40
|
// does not parse, and returns exactly what it was given.
|
|
41
|
-
let grammar: S.t<string> = S.uri
|
|
41
|
+
let grammar: S.t<string> = (S.uri :> S.t<string>)
|
|
42
42
|
|
|
43
43
|
// Schemes are case-insensitive per RFC 3986, and `HTTPS://x` parses fine, so the
|
|
44
44
|
// allowlist has to fold case or it rejects a valid address on a technicality.
|
package/src/semantic/Url.res.mjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
-
import * as S from "sury/src/S.res.mjs";
|
|
4
3
|
import * as Sury from "sury";
|
|
5
4
|
import * as Semantic$Reventless from "./Semantic.res.mjs";
|
|
6
5
|
|
|
@@ -18,7 +17,7 @@ function hasWebScheme(raw) {
|
|
|
18
17
|
function fromString(raw) {
|
|
19
18
|
let value;
|
|
20
19
|
try {
|
|
21
|
-
value =
|
|
20
|
+
value = Sury.parseOrThrow(raw, grammar);
|
|
22
21
|
} catch (exn) {
|
|
23
22
|
return {
|
|
24
23
|
TAG: "Error",
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
-
import * as S from "sury/src/S.res.mjs";
|
|
4
3
|
import * as Sury from "sury";
|
|
5
4
|
import * as Yaml from "yaml";
|
|
6
5
|
import * as Nodefs from "node:fs";
|
|
@@ -25,7 +24,7 @@ function parseString(yamlText) {
|
|
|
25
24
|
try {
|
|
26
25
|
return {
|
|
27
26
|
TAG: "Ok",
|
|
28
|
-
_0:
|
|
27
|
+
_0: Sury.parseOrThrow(Yaml.parse(yamlText), entriesSchema)
|
|
29
28
|
};
|
|
30
29
|
} catch (raw_err) {
|
|
31
30
|
let err = Primitive_exceptions.internalToException(raw_err);
|
|
@@ -15,7 +15,7 @@ let schema = Sury.$schema(s => ({
|
|
|
15
15
|
userId: s.m(Sury.string),
|
|
16
16
|
username: s.m(Sury.string),
|
|
17
17
|
groups: s.m(Sury.array(Sury.string)),
|
|
18
|
-
claims: s.m(Sury.$option(Sury.
|
|
18
|
+
claims: s.m(Sury.$option(Sury.record(Sury.string))),
|
|
19
19
|
provider: s.m(providerSchema)
|
|
20
20
|
}));
|
|
21
21
|
|
|
@@ -16,7 +16,7 @@ let metaSchema = Sury.$schema(s => ({
|
|
|
16
16
|
causationId: s.m(Sury.$option(Sury.string)),
|
|
17
17
|
traceparent: s.m(Sury.$option(Sury.string)),
|
|
18
18
|
schemaVersion: s.m(Sury.$option(Sury.string)),
|
|
19
|
-
headers: s.m(Sury.$option(Sury.
|
|
19
|
+
headers: s.m(Sury.$option(Sury.record(Sury.string)))
|
|
20
20
|
}));
|
|
21
21
|
|
|
22
22
|
let contextSchema = Sury.$schema(s => ({
|
|
@@ -33,7 +33,7 @@ let commandJsonSchema = Sury.$schema(s => ({
|
|
|
33
33
|
id: s.m(Sury.string),
|
|
34
34
|
meta: s.m(metaSchema),
|
|
35
35
|
commandJson: s.m(Sury.json),
|
|
36
|
-
delay: s.m(Sury.$option(Sury.
|
|
36
|
+
delay: s.m(Sury.$option(Sury.int32))
|
|
37
37
|
}));
|
|
38
38
|
|
|
39
39
|
let fillMissingDefaults = (function(schema, json, scalarFills){
|
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
import * as Sury from "sury";
|
|
4
4
|
|
|
5
|
-
let yearSchema = Sury.
|
|
5
|
+
let yearSchema = Sury.int32;
|
|
6
6
|
|
|
7
|
-
let monthSchema = Sury.
|
|
7
|
+
let monthSchema = Sury.int32;
|
|
8
8
|
|
|
9
|
-
let daySchema = Sury.
|
|
9
|
+
let daySchema = Sury.int32;
|
|
10
10
|
|
|
11
|
-
let hourSchema = Sury.
|
|
11
|
+
let hourSchema = Sury.int32;
|
|
12
12
|
|
|
13
|
-
let minuteSchema = Sury.
|
|
13
|
+
let minuteSchema = Sury.int32;
|
|
14
14
|
|
|
15
15
|
let rateSchema = Sury.union([
|
|
16
16
|
Sury.$schema(s => ({
|
|
@@ -23,15 +23,15 @@ let rateSchema = Sury.union([
|
|
|
23
23
|
})),
|
|
24
24
|
Sury.$schema(s => ({
|
|
25
25
|
TAG: "Minutes",
|
|
26
|
-
_0: s.m(Sury.
|
|
26
|
+
_0: s.m(Sury.int32)
|
|
27
27
|
})),
|
|
28
28
|
Sury.$schema(s => ({
|
|
29
29
|
TAG: "Hours",
|
|
30
|
-
_0: s.m(Sury.
|
|
30
|
+
_0: s.m(Sury.int32)
|
|
31
31
|
})),
|
|
32
32
|
Sury.$schema(s => ({
|
|
33
33
|
TAG: "Days",
|
|
34
|
-
_0: s.m(Sury.
|
|
34
|
+
_0: s.m(Sury.int32)
|
|
35
35
|
})),
|
|
36
36
|
Sury.$schema(s => ({
|
|
37
37
|
TAG: "Daily",
|
package/src/util/Util_Sury.res
CHANGED
|
@@ -7,16 +7,19 @@
|
|
|
7
7
|
// See docs/plans/sury-11-migration.md.
|
|
8
8
|
|
|
9
9
|
let toJson: ('a, S.t<'a>) => JSON.t = (value, schema) =>
|
|
10
|
-
value->S.
|
|
10
|
+
value->S.convertOrThrow(~from=schema, ~to=S.json)
|
|
11
11
|
|
|
12
12
|
// `~space` defaults to compact, matching the removed `reverseConvertToJsonStringOrThrow`.
|
|
13
13
|
let toJsonString = (value: 'a, schema: S.t<'a>, ~space: int=0): string =>
|
|
14
|
-
value->S.
|
|
14
|
+
(value->S.convertOrThrow(
|
|
15
|
+
~from=schema,
|
|
16
|
+
~to=space == 0 ? S.jsonString : S.jsonStringWithSpace(space),
|
|
17
|
+
) :> string)
|
|
15
18
|
|
|
16
19
|
let fromJson: (JSON.t, S.t<'a>) => 'a = (json, schema) => json->S.parseOrThrow(~to=schema)
|
|
17
20
|
|
|
18
21
|
let fromJsonString: (string, S.t<'a>) => 'a = (str, schema) =>
|
|
19
|
-
str->S.
|
|
22
|
+
S.JsonString(str)->S.convertOrThrow(~from=S.jsonString, ~to=schema)
|
|
20
23
|
|
|
21
24
|
// Sury raises `S.Exn`, which carries `RE_EXN_ID: "S.Exn"` and so is not a `JsExn`
|
|
22
25
|
// even though it is a JS Error at runtime — the usual
|
|
@@ -6,18 +6,20 @@ import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
|
|
|
6
6
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
7
7
|
|
|
8
8
|
function toJson(value, schema) {
|
|
9
|
-
return S.
|
|
9
|
+
return S.convertOrThrow(value, schema, undefined, Sury.json);
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
function toJsonString(value, schema, spaceOpt) {
|
|
13
13
|
let space = spaceOpt !== undefined ? spaceOpt : 0;
|
|
14
|
-
return S.
|
|
14
|
+
return S.convertOrThrow(value, schema, undefined, space === 0 ? Sury.jsonString : Sury.jsonStringWithSpace(space));
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
function fromJson(json, schema) {
|
|
18
|
+
return Sury.parseOrThrow(json, schema);
|
|
19
|
+
}
|
|
18
20
|
|
|
19
21
|
function fromJsonString(str, schema) {
|
|
20
|
-
return S.
|
|
22
|
+
return S.convertOrThrow(str, Sury.jsonString, undefined, schema);
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
function exnMessage(exn, fallbackOpt) {
|