@reventlessdev/reventless-spec 3.0.0-alpha.116 → 3.0.0-alpha.118
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 +29 -0
- package/package.json +4 -4
- package/schema/platform-api.graphql +11 -2
- package/src/components/CapabilityManifest.res +1 -1
- package/src/components/CapabilityManifest.res.mjs +13 -12
- package/src/components/DcbDecode.res +8 -8
- package/src/components/DcbDecode.res.mjs +9 -8
- package/src/components/DcbTag.res +43 -42
- package/src/components/DcbTag.res.mjs +90 -87
- package/src/components/DcbValidation.res +6 -6
- package/src/components/DcbValidation.res.mjs +6 -6
- package/src/components/DisplayName.res.mjs +3 -3
- package/src/components/Owner.res +1 -1
- package/src/components/Owner.res.mjs +7 -7
- package/src/components/Plugin.res +28 -11
- package/src/components/Plugin.res.mjs +134 -134
- package/src/components/Reference.res.mjs +5 -5
- package/src/components/StateAnnotations.res +44 -0
- package/src/components/StateAnnotations.res.mjs +3 -3
- package/src/generator/PlatformCodegen.res +17 -14
- package/src/generator/PlatformCodegen.res.mjs +1 -1
- package/src/generator/PlatformGenerator.res +1 -1
- package/src/semantic/Bytes.res.mjs +2 -2
- package/src/semantic/Color.res.mjs +2 -2
- package/src/semantic/Currency.res.mjs +167 -167
- package/src/semantic/DateRange.res.mjs +2 -2
- package/src/semantic/Duration.res.mjs +2 -2
- package/src/semantic/Email.res +2 -2
- package/src/semantic/Email.res.mjs +3 -2
- package/src/semantic/FileRef.res +28 -0
- package/src/semantic/FileRef.res.mjs +17 -0
- package/src/semantic/GeoPoint.res +14 -10
- package/src/semantic/GeoPoint.res.mjs +10 -17
- package/src/semantic/ImageRef.res +51 -0
- package/src/semantic/ImageRef.res.mjs +17 -0
- package/src/semantic/Media_Ref.res +33 -0
- package/src/semantic/Media_Ref.res.mjs +37 -0
- package/src/semantic/Money.res +7 -5
- package/src/semantic/Money.res.mjs +6 -9
- package/src/semantic/Offload.res +29 -43
- package/src/semantic/Offload.res.mjs +18 -49
- package/src/semantic/Percent.res.mjs +2 -2
- package/src/semantic/Phone.res.mjs +2 -2
- package/src/semantic/Semantic.res +24 -6
- package/src/semantic/Semantic.res.mjs +13 -12
- package/src/semantic/StorageRef.res +33 -19
- package/src/semantic/StorageRef.res.mjs +14 -14
- package/src/semantic/UploadableFile.res +30 -0
- package/src/semantic/UploadableFile.res.mjs +23 -0
- package/src/semantic/UploadableImage.res +56 -0
- package/src/semantic/UploadableImage.res.mjs +23 -0
- package/src/semantic/Url.res +8 -2
- package/src/semantic/Url.res.mjs +3 -2
- package/src/types/Authorization.res.mjs +7 -7
- package/src/types/DateTime.res.mjs +2 -2
- package/src/types/Id.res.mjs +6 -4
- package/src/types/Identity.res +0 -1
- package/src/types/Identity.res.mjs +12 -14
- package/src/types/Message.res +29 -10
- package/src/types/Message.res.mjs +56 -37
- package/src/types/ReadConsistency.res.mjs +5 -5
- package/src/types/Schedule.res.mjs +37 -37
- package/src/types/StoredEvent.res +2 -2
- package/src/types/StoredEvent.res.mjs +11 -10
- package/src/types/Visibility.res.mjs +4 -4
- package/src/util/Util_Sury.res +32 -0
- package/src/util/Util_Sury.res.mjs +39 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// The grammar `ImageRef` and `FileRef` share. They differ in what the value
|
|
2
|
+
// depicts, not in how a reference to it is written, so the rule lives once here
|
|
3
|
+
// rather than twice with a chance to disagree.
|
|
4
|
+
|
|
5
|
+
/** Is this an absolute web address? Schemes fold case per RFC 3986. */
|
|
6
|
+
let hasWebScheme = (raw: string): bool => {
|
|
7
|
+
let lower = String.toLowerCase(raw)
|
|
8
|
+
String.startsWith(lower, "http://") || String.startsWith(lower, "https://")
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
Validate a reference to a resource the platform does not own: an `http`/`https`
|
|
13
|
+
URL, or an origin-relative path. `~what` names the kind in the rejection so the
|
|
14
|
+
message reads as the field's own.
|
|
15
|
+
|
|
16
|
+
An origin-relative path is admitted because a value may legitimately have been
|
|
17
|
+
minted by this platform earlier and simply not be declared as stored here —
|
|
18
|
+
refusing it would make the unowned types unable to hold their own history.
|
|
19
|
+
*/
|
|
20
|
+
let check = (~what: string, raw: string): result<string, string> =>
|
|
21
|
+
if raw === "" {
|
|
22
|
+
Error(`expected a ${what} reference, got an empty string`)
|
|
23
|
+
} else if hasWebScheme(raw) {
|
|
24
|
+
Ok(raw)
|
|
25
|
+
} else if String.startsWith(raw, "/") && !String.startsWith(raw, "//") {
|
|
26
|
+
Ok(raw)
|
|
27
|
+
} else {
|
|
28
|
+
Error(
|
|
29
|
+
`expected an http:// or https:// address, or an origin-relative path, got ${Semantic.showString(
|
|
30
|
+
raw,
|
|
31
|
+
)}. A data: URI is not a ${what} reference — it inlines the bytes into the event log, permanently.`,
|
|
32
|
+
)
|
|
33
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Semantic$Reventless from "./Semantic.res.mjs";
|
|
4
|
+
|
|
5
|
+
function hasWebScheme(raw) {
|
|
6
|
+
let lower = raw.toLowerCase();
|
|
7
|
+
if (lower.startsWith("http://")) {
|
|
8
|
+
return true;
|
|
9
|
+
} else {
|
|
10
|
+
return lower.startsWith("https://");
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function check(what, raw) {
|
|
15
|
+
if (raw === "") {
|
|
16
|
+
return {
|
|
17
|
+
TAG: "Error",
|
|
18
|
+
_0: `expected a ` + what + ` reference, got an empty string`
|
|
19
|
+
};
|
|
20
|
+
} else if (hasWebScheme(raw) || raw.startsWith("/") && !raw.startsWith("//")) {
|
|
21
|
+
return {
|
|
22
|
+
TAG: "Ok",
|
|
23
|
+
_0: raw
|
|
24
|
+
};
|
|
25
|
+
} else {
|
|
26
|
+
return {
|
|
27
|
+
TAG: "Error",
|
|
28
|
+
_0: `expected an http:// or https:// address, or an origin-relative path, got ` + Semantic$Reventless.showString(raw) + `. A data: URI is not a ` + what + ` reference — it inlines the bytes into the event log, permanently.`
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export {
|
|
34
|
+
hasWebScheme,
|
|
35
|
+
check,
|
|
36
|
+
}
|
|
37
|
+
/* Semantic-Reventless Not a pure module */
|
package/src/semantic/Money.res
CHANGED
|
@@ -79,11 +79,13 @@ let validateAmount = (amount: float): result<float, string> =>
|
|
|
79
79
|
`Cannot access 'v0' before initialization`). Refining the field is both the
|
|
80
80
|
honest placement and the one that works. */
|
|
81
81
|
let amountSchema: S.t<float> =
|
|
82
|
-
S.float->S.refine(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
S.float->S.refine(
|
|
83
|
+
amount =>
|
|
84
|
+
switch validateAmount(amount) {
|
|
85
|
+
| Ok(_) => true
|
|
86
|
+
| Error(_) => false
|
|
87
|
+
},
|
|
88
|
+
~error="expected a monetary amount",
|
|
87
89
|
)
|
|
88
90
|
|
|
89
91
|
@schema
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as S from "sury/src/S.res.mjs";
|
|
4
|
+
import * as Sury from "sury";
|
|
4
5
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
5
6
|
import * as Stdlib_Result from "@rescript/runtime/lib/es6/Stdlib_Result.js";
|
|
6
7
|
import * as Currency$Reventless from "./Currency.res.mjs";
|
|
@@ -27,16 +28,12 @@ function validateAmount(amount) {
|
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
let amountSchema = S.refine(
|
|
31
|
-
let
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
} else {
|
|
35
|
-
return s.fail(why._0, undefined);
|
|
36
|
-
}
|
|
37
|
-
}));
|
|
31
|
+
let amountSchema = S.refine(Sury.float, amount => {
|
|
32
|
+
let match = validateAmount(amount);
|
|
33
|
+
return match.TAG === "Ok";
|
|
34
|
+
}, "expected a monetary amount", undefined);
|
|
38
35
|
|
|
39
|
-
let schema =
|
|
36
|
+
let schema = Sury.$schema(s => ({
|
|
40
37
|
amount: s.m(amountSchema),
|
|
41
38
|
currency: s.m(Currency$Reventless.schema)
|
|
42
39
|
}));
|
package/src/semantic/Offload.res
CHANGED
|
@@ -63,17 +63,6 @@ type payload<'a> =
|
|
|
63
63
|
| Inline('a)
|
|
64
64
|
| Offloaded(offloadedRef)
|
|
65
65
|
|
|
66
|
-
/** `nullableAsOption` emits `T | undefined | null`, which fails
|
|
67
|
-
`jsonableValidation` inside an event union; `js_nullable` emits `T | null`,
|
|
68
|
-
which is JSON-safe there. Same reason `Plugin.res` reaches for it. */
|
|
69
|
-
@module("sury/src/Sury.res.mjs")
|
|
70
|
-
external _jsNullable: (S.t<'a>, unit) => S.t<option<'a>> = "js_nullable"
|
|
71
|
-
|
|
72
|
-
/** The codec builds on `S.json`, which sury 11 gates behind an explicit enable.
|
|
73
|
-
Doing it here (at module load, before any `schema` call) makes the primitive
|
|
74
|
-
self-contained: importing `Offload` is enough, no consumer has to remember. */
|
|
75
|
-
S.enableJson()
|
|
76
|
-
|
|
77
66
|
/** The object key under which an `Offloaded` value hides. Reserved: no ReScript
|
|
78
67
|
record field encodes to a key starting with `$`, so it cannot collide with an
|
|
79
68
|
inline payload's own fields. */
|
|
@@ -86,37 +75,30 @@ Parameterised by the inner value's schema because the `Inline` arm round-trips
|
|
|
86
75
|
through it. The `Offloaded` arm round-trips through `offloadedRefSchema` under the
|
|
87
76
|
sentinel key. See the module doc for why this is untagged.
|
|
88
77
|
*/
|
|
78
|
+
// Both arms are *declared* (`S.object` / `S.shape`) rather than hand-written as
|
|
79
|
+
// `S.transform` pairs, and that is load-bearing, not a style choice. A transform
|
|
80
|
+
// arm is opaque: sury cannot see what it accepts, so it must offer every value to
|
|
81
|
+
// the arm's own serializer and let it reject. Two things follow, both of which bit
|
|
82
|
+
// this codec on 11.0.0-rc.0 and neither of which the type checker can see:
|
|
83
|
+
//
|
|
84
|
+
// - an absent optional value reaches the arms as a raw `undefined`, and the first
|
|
85
|
+
// serializer to read its payload dereferences it — an uncatchable `TypeError`;
|
|
86
|
+
// - a json-typed arm cannot chain into a non-JSON target, so encoding an
|
|
87
|
+
// `Offloaded` value to `S.jsonString` fails where encoding it to `S.json` works.
|
|
88
|
+
//
|
|
89
|
+
// A declared arm has neither problem: sury derives both directions from the shape,
|
|
90
|
+
// discriminates on it, and never runs user code to find out. One transform arm is
|
|
91
|
+
// enough to bring both failures back, so keep every arm declarative.
|
|
89
92
|
let schema = (inner: S.t<'a>): S.t<payload<'a>> => {
|
|
90
|
-
// Offloaded arm:
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
let offloadedArm = S.json->S.transform(s => {
|
|
94
|
-
parser: json =>
|
|
95
|
-
switch json->JSON.Decode.object->Option.flatMap(dict => dict->Dict.get(sentinelKey)) {
|
|
96
|
-
| Some(refJson) => Offloaded(refJson->S.parseJsonOrThrow(offloadedRefSchema))
|
|
97
|
-
| None => s.fail("not an offloaded reference")
|
|
98
|
-
},
|
|
99
|
-
serializer: payload =>
|
|
100
|
-
switch payload {
|
|
101
|
-
| Offloaded(ref) =>
|
|
102
|
-
Dict.fromArray([(sentinelKey, ref->S.reverseConvertToJsonOrThrow(offloadedRefSchema))])
|
|
103
|
-
->JSON.Encode.object
|
|
104
|
-
| Inline(_) => s.fail("not an offloaded reference")
|
|
105
|
-
},
|
|
106
|
-
})
|
|
93
|
+
// Offloaded arm: an object carrying the reserved sentinel key, tried first so an
|
|
94
|
+
// offloaded value is never mistaken for an inline one.
|
|
95
|
+
let offloadedArm = S.object(s => Offloaded(s.field(sentinelKey, offloadedRefSchema)))
|
|
107
96
|
// Inline arm: the inner schema applied through sury's own pipeline, so it
|
|
108
97
|
// inherits whatever tolerance the surrounding decode uses — the lifecycle
|
|
109
98
|
// Message decoder heals older payloads with missing fields. This is why the
|
|
110
99
|
// codec is a union rather than one json-transform with a nested
|
|
111
100
|
// parseJsonOrThrow: a nested parse runs strict and breaks the frozen corpus.
|
|
112
|
-
let inlineArm = inner->S.
|
|
113
|
-
parser: value => Inline(value),
|
|
114
|
-
serializer: payload =>
|
|
115
|
-
switch payload {
|
|
116
|
-
| Inline(value) => value
|
|
117
|
-
| Offloaded(_) => s.fail("not an inline value")
|
|
118
|
-
},
|
|
119
|
-
})
|
|
101
|
+
let inlineArm = inner->S.shape(value => Inline(value))
|
|
120
102
|
S.union([offloadedArm, inlineArm])
|
|
121
103
|
}
|
|
122
104
|
|
|
@@ -136,10 +118,14 @@ let forStore = (
|
|
|
136
118
|
schema(inner)->Semantic.mark(~id=Semantic.Id.offload, ~payload=StoredIn({plugin, store, threshold}))
|
|
137
119
|
|
|
138
120
|
/**
|
|
139
|
-
The codec wrapped for an **optional** field
|
|
140
|
-
|
|
141
|
-
|
|
121
|
+
The codec wrapped for an **optional** field, plus the `StoredIn` marker. This is
|
|
122
|
+
the common case: offloadable fields are usually optional (absent for older
|
|
123
|
+
protocol versions, say). The marker sits on the outer schema, where
|
|
142
124
|
`Semantic.get` reads it first.
|
|
125
|
+
|
|
126
|
+
`nullAsOption` keeps the wire form `T | null` — byte-identical to every stored
|
|
127
|
+
message — and makes the union advertise `has.null`, which is what
|
|
128
|
+
`Message.fillMissingDefaults` reads to heal an absent field to `None` on replay.
|
|
143
129
|
*/
|
|
144
130
|
let optionSchema = (
|
|
145
131
|
~plugin: option<string>=?,
|
|
@@ -147,7 +133,7 @@ let optionSchema = (
|
|
|
147
133
|
~threshold: option<int>=?,
|
|
148
134
|
inner: S.t<'a>,
|
|
149
135
|
): S.t<option<payload<'a>>> =>
|
|
150
|
-
|
|
136
|
+
S.nullAsOption(schema(inner))->Semantic.mark(
|
|
151
137
|
~id=Semantic.Id.offload,
|
|
152
138
|
~payload=StoredIn({plugin, store, threshold}),
|
|
153
139
|
)
|
|
@@ -158,7 +144,7 @@ let optionSchema = (
|
|
|
158
144
|
e.g. the plugin read model, whose DynamoDB write path marshals the raw value
|
|
159
145
|
and would otherwise persist the variant's runtime `{TAG, _0}` shape. */
|
|
160
146
|
let toJson = (inner: S.t<'a>, payload: payload<'a>): JSON.t =>
|
|
161
|
-
payload->
|
|
147
|
+
payload->Util_Sury.toJson(schema(inner))
|
|
162
148
|
|
|
163
149
|
/** The inline value, if this payload is `Inline`. `None` for `Offloaded` — a
|
|
164
150
|
caller that must handle both arms uses `resolve` (async, fetches the ref); a
|
|
@@ -202,7 +188,7 @@ let prepare = (
|
|
|
202
188
|
~hash: string => string,
|
|
203
189
|
~upload: (~key: string, ~bytes: string) => promise<unit>,
|
|
204
190
|
): promise<payload<'a>> => {
|
|
205
|
-
let bytes = value->
|
|
191
|
+
let bytes = value->Util_Sury.toJsonString(schema)
|
|
206
192
|
if bytes->String.length < threshold {
|
|
207
193
|
Promise.resolve(Inline(value))
|
|
208
194
|
} else {
|
|
@@ -227,7 +213,7 @@ let resolve = (payload: payload<'a>, ~schema: S.t<'a>, ~fetch: string => promise
|
|
|
227
213
|
switch payload {
|
|
228
214
|
| Inline(value) => Promise.resolve(value)
|
|
229
215
|
| Offloaded({key}) =>
|
|
230
|
-
fetch(key)->Promise.then(bytes => Promise.resolve(bytes->
|
|
216
|
+
fetch(key)->Promise.then(bytes => Promise.resolve(bytes->Util_Sury.fromJsonString(schema)))
|
|
231
217
|
}
|
|
232
218
|
|
|
233
219
|
/**
|
|
@@ -1,61 +1,30 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
-
import * as
|
|
4
|
-
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
3
|
+
import * as Sury from "sury";
|
|
5
4
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
5
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
7
6
|
import * as Semantic$Reventless from "./Semantic.res.mjs";
|
|
8
|
-
import * as
|
|
7
|
+
import * as Util_Sury$Reventless from "../util/Util_Sury.res.mjs";
|
|
9
8
|
|
|
10
|
-
let offloadedRefSchema =
|
|
11
|
-
store: s.m(
|
|
12
|
-
key: s.m(
|
|
13
|
-
hash: s.m(
|
|
14
|
-
bytes: s.m(
|
|
9
|
+
let offloadedRefSchema = Sury.$schema(s => ({
|
|
10
|
+
store: s.m(Sury.string),
|
|
11
|
+
key: s.m(Sury.string),
|
|
12
|
+
hash: s.m(Sury.string),
|
|
13
|
+
bytes: s.m(Sury.int)
|
|
15
14
|
}));
|
|
16
15
|
|
|
17
|
-
S.enableJson();
|
|
18
|
-
|
|
19
16
|
let sentinelKey = "$offload";
|
|
20
17
|
|
|
21
18
|
function schema(inner) {
|
|
22
|
-
let offloadedArm =
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (refJson !== undefined) {
|
|
26
|
-
return {
|
|
27
|
-
TAG: "Offloaded",
|
|
28
|
-
_0: S.parseJsonOrThrow(refJson, offloadedRefSchema)
|
|
29
|
-
};
|
|
30
|
-
} else {
|
|
31
|
-
return s.fail("not an offloaded reference", undefined);
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
s: payload => {
|
|
35
|
-
if (payload.TAG === "Inline") {
|
|
36
|
-
return s.fail("not an offloaded reference", undefined);
|
|
37
|
-
} else {
|
|
38
|
-
return Object.fromEntries([[
|
|
39
|
-
sentinelKey,
|
|
40
|
-
S.reverseConvertToJsonOrThrow(payload._0, offloadedRefSchema)
|
|
41
|
-
]]);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
19
|
+
let offloadedArm = Sury.object(s => ({
|
|
20
|
+
TAG: "Offloaded",
|
|
21
|
+
_0: s.f(sentinelKey, offloadedRefSchema)
|
|
44
22
|
}));
|
|
45
|
-
let inlineArm =
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
_0: value
|
|
49
|
-
}),
|
|
50
|
-
s: payload => {
|
|
51
|
-
if (payload.TAG === "Inline") {
|
|
52
|
-
return payload._0;
|
|
53
|
-
} else {
|
|
54
|
-
return s.fail("not an inline value", undefined);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
23
|
+
let inlineArm = Sury.shape(inner, value => ({
|
|
24
|
+
TAG: "Inline",
|
|
25
|
+
_0: value
|
|
57
26
|
}));
|
|
58
|
-
return
|
|
27
|
+
return Sury.union([
|
|
59
28
|
offloadedArm,
|
|
60
29
|
inlineArm
|
|
61
30
|
]);
|
|
@@ -73,7 +42,7 @@ function forStore(plugin, store, threshold, inner) {
|
|
|
73
42
|
}
|
|
74
43
|
|
|
75
44
|
function optionSchema(plugin, store, threshold, inner) {
|
|
76
|
-
return Semantic$Reventless.mark(
|
|
45
|
+
return Semantic$Reventless.mark(Sury.$nullAsOption(schema(inner)), Semantic$Reventless.Id.offload, {
|
|
77
46
|
TAG: "StoredIn",
|
|
78
47
|
_0: {
|
|
79
48
|
plugin: plugin,
|
|
@@ -84,7 +53,7 @@ function optionSchema(plugin, store, threshold, inner) {
|
|
|
84
53
|
}
|
|
85
54
|
|
|
86
55
|
function toJson(inner, payload) {
|
|
87
|
-
return
|
|
56
|
+
return Util_Sury$Reventless.toJson(payload, schema(inner));
|
|
88
57
|
}
|
|
89
58
|
|
|
90
59
|
function getInline(payload) {
|
|
@@ -94,7 +63,7 @@ function getInline(payload) {
|
|
|
94
63
|
}
|
|
95
64
|
|
|
96
65
|
function prepare(value, schema, store, threshold, hash, upload) {
|
|
97
|
-
let bytes =
|
|
66
|
+
let bytes = Util_Sury$Reventless.toJsonString(value, schema, undefined);
|
|
98
67
|
if (bytes.length < threshold) {
|
|
99
68
|
return Promise.resolve({
|
|
100
69
|
TAG: "Inline",
|
|
@@ -118,7 +87,7 @@ function resolve(payload, schema, fetch) {
|
|
|
118
87
|
if (payload.TAG === "Inline") {
|
|
119
88
|
return Promise.resolve(payload._0);
|
|
120
89
|
} else {
|
|
121
|
-
return fetch(payload._0.key).then(bytes => Promise.resolve(
|
|
90
|
+
return fetch(payload._0.key).then(bytes => Promise.resolve(Util_Sury$Reventless.fromJsonString(bytes, schema)));
|
|
122
91
|
}
|
|
123
92
|
}
|
|
124
93
|
|
|
@@ -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 Semantic$Reventless from "./Semantic.res.mjs";
|
|
5
5
|
|
|
6
6
|
function fromFloat(raw) {
|
|
@@ -24,7 +24,7 @@ function fromFloat(raw) {
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
let schema = Semantic$Reventless.refined(
|
|
27
|
+
let schema = Semantic$Reventless.refined(Sury.float, Semantic$Reventless.Id.percent, fromFloat);
|
|
28
28
|
|
|
29
29
|
export {
|
|
30
30
|
fromFloat,
|
|
@@ -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 Semantic$Reventless from "./Semantic.res.mjs";
|
|
5
5
|
|
|
6
6
|
let e164 = /^\+[1-9]\d{0,14}$/;
|
|
@@ -19,7 +19,7 @@ function fromString(raw) {
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
let schema = Semantic$Reventless.refined(
|
|
22
|
+
let schema = Semantic$Reventless.refined(Sury.string, Semantic$Reventless.Id.phone, fromString);
|
|
23
23
|
|
|
24
24
|
export {
|
|
25
25
|
e164,
|
|
@@ -57,6 +57,18 @@ module Id = {
|
|
|
57
57
|
// value rather than an always-a-ref path string.
|
|
58
58
|
let offload = "offload"
|
|
59
59
|
|
|
60
|
+
// The uploadable family: the same `StoredIn` declaration as `storageRef`,
|
|
61
|
+
// plus a statement about what the value *is*, so one declaration picks both a
|
|
62
|
+
// renderer and an upload endpoint. The store is derived from the field name by
|
|
63
|
+
// the ppx rather than written on the type.
|
|
64
|
+
let uploadableImage = "uploadableImage"
|
|
65
|
+
let uploadableFile = "uploadableFile"
|
|
66
|
+
|
|
67
|
+
// The same content facts with no store attached, for values the platform
|
|
68
|
+
// reads but does not own. Nothing is provisioned for these.
|
|
69
|
+
let imageRef = "imageRef"
|
|
70
|
+
let fileRef = "fileRef"
|
|
71
|
+
|
|
60
72
|
// The branded scalars. Each refines a `string` or a number without changing
|
|
61
73
|
// its shape, so a field gains one of these without anything stored changing.
|
|
62
74
|
let email = "email"
|
|
@@ -102,13 +114,19 @@ hand-rolling a second check beside it. Deriving it here makes that structural:
|
|
|
102
114
|
there is one place a grammar can be written, so there is nowhere for a second
|
|
103
115
|
one to drift.
|
|
104
116
|
*/
|
|
117
|
+
// sury's refiner is a predicate with a fixed message, so the per-value reason
|
|
118
|
+
// `check` returns is not threaded into the schema error; call the scalar's own
|
|
119
|
+
// `fromString`/`fromFloat` directly when the caller needs to report which rule
|
|
120
|
+
// the value broke.
|
|
105
121
|
let refined = (base: S.t<'a>, ~id: string, ~check: 'a => result<'a, string>): S.t<'a> =>
|
|
106
122
|
base
|
|
107
|
-
->S.refine(
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
123
|
+
->S.refine(
|
|
124
|
+
value =>
|
|
125
|
+
switch check(value) {
|
|
126
|
+
| Ok(_) => true
|
|
127
|
+
| Error(_) => false
|
|
128
|
+
},
|
|
129
|
+
~error=`expected a valid ${id}`,
|
|
112
130
|
)
|
|
113
131
|
->mark(~id)
|
|
114
132
|
|
|
@@ -131,7 +149,7 @@ that could stand for the whole.
|
|
|
131
149
|
*/
|
|
132
150
|
let unwrapOptional = (schema: S.t<unknown>): option<S.t<unknown>> =>
|
|
133
151
|
switch schema {
|
|
134
|
-
|
|
|
152
|
+
| AnyOf({anyOf}) =>
|
|
135
153
|
switch anyOf->Array.filter(v =>
|
|
136
154
|
switch v {
|
|
137
155
|
| Null(_) | Undefined(_) => false
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as S from "sury/src/S.res.mjs";
|
|
4
|
+
import * as Sury from "sury";
|
|
4
5
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
5
6
|
|
|
6
7
|
let Id = {
|
|
@@ -8,6 +9,10 @@ let Id = {
|
|
|
8
9
|
reference: "reference",
|
|
9
10
|
storageRef: "storageRef",
|
|
10
11
|
offload: "offload",
|
|
12
|
+
uploadableImage: "uploadableImage",
|
|
13
|
+
uploadableFile: "uploadableFile",
|
|
14
|
+
imageRef: "imageRef",
|
|
15
|
+
fileRef: "fileRef",
|
|
11
16
|
email: "email",
|
|
12
17
|
phone: "phone",
|
|
13
18
|
url: "url",
|
|
@@ -20,25 +25,21 @@ let Id = {
|
|
|
20
25
|
geoPoint: "geoPoint"
|
|
21
26
|
};
|
|
22
27
|
|
|
23
|
-
let semanticId =
|
|
28
|
+
let semanticId = Sury.$Metadata_Id_make("reventless", "semantic");
|
|
24
29
|
|
|
25
30
|
function mark(schema, id, payloadOpt) {
|
|
26
31
|
let payload = payloadOpt !== undefined ? payloadOpt : "Plain";
|
|
27
|
-
return
|
|
32
|
+
return Sury.$Metadata_set(schema, semanticId, {
|
|
28
33
|
id: id,
|
|
29
34
|
payload: payload
|
|
30
35
|
});
|
|
31
36
|
}
|
|
32
37
|
|
|
33
38
|
function refined(base, id, check) {
|
|
34
|
-
return mark(S.refine(base,
|
|
35
|
-
let
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
} else {
|
|
39
|
-
return s.fail(why._0, undefined);
|
|
40
|
-
}
|
|
41
|
-
})), id, undefined);
|
|
39
|
+
return mark(S.refine(base, value => {
|
|
40
|
+
let match = check(value);
|
|
41
|
+
return match.TAG === "Ok";
|
|
42
|
+
}, `expected a valid ` + id, undefined), id, undefined);
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
function showString(raw) {
|
|
@@ -46,7 +47,7 @@ function showString(raw) {
|
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
function unwrapOptional(schema) {
|
|
49
|
-
if (schema.type !== "
|
|
50
|
+
if (schema.type !== "anyOf") {
|
|
50
51
|
return;
|
|
51
52
|
}
|
|
52
53
|
let match = schema.anyOf.filter(v => {
|
|
@@ -66,7 +67,7 @@ function unwrapOptional(schema) {
|
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
function getFrom(schema) {
|
|
69
|
-
let found =
|
|
70
|
+
let found = Sury.$Metadata_get(schema, semanticId);
|
|
70
71
|
if (found !== undefined) {
|
|
71
72
|
return found;
|
|
72
73
|
} else {
|
|
@@ -89,6 +89,35 @@ let fromString = (raw: string): result<t, string> =>
|
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
/**
|
|
93
|
+
The refinement a ref-holding field validates against, carrying no semantic yet.
|
|
94
|
+
The `Uploadable*` types re-label this rather than restating the grammar, so a
|
|
95
|
+
re-labelling cannot validate differently from what it re-labels.
|
|
96
|
+
*/
|
|
97
|
+
// The empty string is admitted as the "no object" sentinel. The fields this
|
|
98
|
+
// marks are non-optional today, and a producer with nothing to reference —
|
|
99
|
+
// a supplier feed carrying no image, say — already writes `""` to mean
|
|
100
|
+
// absence. Rejecting it here would break a legitimate existing value and
|
|
101
|
+
// force an event-schema change, which this marker exists to avoid: it
|
|
102
|
+
// refines an existing `string` field without altering what is stored.
|
|
103
|
+
//
|
|
104
|
+
// Note this is a strictly weaker guarantee than `fromString`, which stays
|
|
105
|
+
// exact. Making these fields properly optional would let the sentinel go.
|
|
106
|
+
//
|
|
107
|
+
// sury's refiner is a predicate with a fixed message, so the per-value reason
|
|
108
|
+
// `fromString` returns is not threaded through; call `fromString` directly
|
|
109
|
+
// when the caller needs to report which rule the value broke.
|
|
110
|
+
let refinement: S.t<t> =
|
|
111
|
+
S.string->S.refine(
|
|
112
|
+
value =>
|
|
113
|
+
value === "" ||
|
|
114
|
+
switch fromString(value) {
|
|
115
|
+
| Ok(_) => true
|
|
116
|
+
| Error(_) => false
|
|
117
|
+
},
|
|
118
|
+
~error=`expected an origin-relative storage ref: "/" followed by a prefix and an object path, or "" for no object`,
|
|
119
|
+
)
|
|
120
|
+
|
|
92
121
|
/**
|
|
93
122
|
The sury schema for a field holding a ref into a named store.
|
|
94
123
|
|
|
@@ -96,25 +125,10 @@ Prefer the `@storageRef("<store>")` ppx shorthand over writing this by hand.
|
|
|
96
125
|
Qualify the store as `"<plugin>.<store>"` to point at another plugin's store.
|
|
97
126
|
*/
|
|
98
127
|
let forStore = (~plugin: option<string>=?, ~store: string): S.t<t> =>
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
// marks are non-optional today, and a producer with nothing to reference —
|
|
103
|
-
// a supplier feed carrying no image, say — already writes `""` to mean
|
|
104
|
-
// absence. Rejecting it here would break a legitimate existing value and
|
|
105
|
-
// force an event-schema change, which this marker exists to avoid: it
|
|
106
|
-
// refines an existing `string` field without altering what is stored.
|
|
107
|
-
//
|
|
108
|
-
// Note this is a strictly weaker guarantee than `fromString`, which stays
|
|
109
|
-
// exact. Making these fields properly optional would let the sentinel go.
|
|
110
|
-
if value !== "" {
|
|
111
|
-
switch fromString(value) {
|
|
112
|
-
| Ok(_) => ()
|
|
113
|
-
| Error(why) => s.fail(why)
|
|
114
|
-
}
|
|
115
|
-
}
|
|
128
|
+
refinement->Semantic.mark(
|
|
129
|
+
~id=Semantic.Id.storageRef,
|
|
130
|
+
~payload=StoredIn({plugin, store, threshold: None}),
|
|
116
131
|
)
|
|
117
|
-
->Semantic.mark(~id=Semantic.Id.storageRef, ~payload=StoredIn({plugin, store, threshold: None}))
|
|
118
132
|
|
|
119
133
|
/** The store a field's schema declares its refs live in, if any. */
|
|
120
134
|
let getStore = (schema: S.t<'a>): option<Semantic.storeTarget> =>
|
|
@@ -152,7 +166,7 @@ let getFieldStore = (schema: S.t<'a>): option<(Semantic.storeTarget, arity)> =>
|
|
|
152
166
|
// and a tuple's positional schemas in `items`; read both so the marker is
|
|
153
167
|
// found wherever the element sits.
|
|
154
168
|
switch items->Array.get(0) {
|
|
155
|
-
| Some(
|
|
169
|
+
| Some(itemSchema) => getStore(itemSchema)
|
|
156
170
|
| None =>
|
|
157
171
|
switch additionalItems {
|
|
158
172
|
| Schema(itemSchema) => getStore(itemSchema)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as S from "sury/src/S.res.mjs";
|
|
4
|
+
import * as Sury from "sury";
|
|
4
5
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
5
6
|
import * as Semantic$Reventless from "./Semantic.res.mjs";
|
|
6
7
|
|
|
@@ -44,18 +45,16 @@ function fromString(raw) {
|
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
|
|
48
|
+
let refinement = S.refine(Sury.string, value => {
|
|
49
|
+
if (value === "") {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
let match = fromString(value);
|
|
53
|
+
return match.TAG === "Ok";
|
|
54
|
+
}, `expected an origin-relative storage ref: "/" followed by a prefix and an object path, or "" for no object`, undefined);
|
|
55
|
+
|
|
47
56
|
function forStore(plugin, store) {
|
|
48
|
-
return Semantic$Reventless.mark(
|
|
49
|
-
if (value === "") {
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
let why = fromString(value);
|
|
53
|
-
if (why.TAG === "Ok") {
|
|
54
|
-
return;
|
|
55
|
-
} else {
|
|
56
|
-
return s.fail(why._0, undefined);
|
|
57
|
-
}
|
|
58
|
-
})), Semantic$Reventless.Id.storageRef, {
|
|
57
|
+
return Semantic$Reventless.mark(refinement, Semantic$Reventless.Id.storageRef, {
|
|
59
58
|
TAG: "StoredIn",
|
|
60
59
|
_0: {
|
|
61
60
|
plugin: plugin,
|
|
@@ -90,9 +89,9 @@ function getFieldStore(schema) {
|
|
|
90
89
|
return;
|
|
91
90
|
}
|
|
92
91
|
let additionalItems = schema.additionalItems;
|
|
93
|
-
let
|
|
92
|
+
let itemSchema = schema.items[0];
|
|
94
93
|
let tmp;
|
|
95
|
-
tmp =
|
|
94
|
+
tmp = itemSchema !== undefined ? getStore(itemSchema) : (
|
|
96
95
|
additionalItems === "strip" || additionalItems === "strict" ? undefined : getStore(additionalItems)
|
|
97
96
|
);
|
|
98
97
|
return Stdlib_Option.map(tmp, target => [
|
|
@@ -104,8 +103,9 @@ function getFieldStore(schema) {
|
|
|
104
103
|
export {
|
|
105
104
|
segmentIsSafe,
|
|
106
105
|
fromString,
|
|
106
|
+
refinement,
|
|
107
107
|
forStore,
|
|
108
108
|
getStore,
|
|
109
109
|
getFieldStore,
|
|
110
110
|
}
|
|
111
|
-
/*
|
|
111
|
+
/* refinement Not a pure module */
|