@reventlessdev/reventless-spec 3.0.0-alpha.117 → 3.0.0-alpha.119
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 +26 -0
- package/package.json +3 -3
- 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 +8 -11
- package/src/components/Plugin.res.mjs +133 -134
- package/src/components/Reference.res.mjs +5 -5
- package/src/components/StateAnnotations.res +23 -0
- package/src/components/StateAnnotations.res.mjs +3 -3
- package/src/components/TaggedUnion.res +238 -0
- package/src/components/TaggedUnion.res.mjs +193 -0
- 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,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
A variant used as a **field** of a queryable's state — one fact with several
|
|
3
|
+
shapes, rather than several fields that have to be kept in step by hand.
|
|
4
|
+
|
|
5
|
+
```rescript
|
|
6
|
+
@schema
|
|
7
|
+
type geolocation =
|
|
8
|
+
| Pending({requestedFor: string})
|
|
9
|
+
| Located({point: Reventless.GeoPoint.t})
|
|
10
|
+
| Unresolvable({reason: string})
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The value is stored the way sury encodes it — `{"TAG":"Located","point":{…}}` —
|
|
14
|
+
and reaches GraphQL as a union of one object type per arm. Both halves need the
|
|
15
|
+
*same* name for the union, and neither can derive it from the other: the SDL
|
|
16
|
+
emitter walks a schema it reaches through a field path, and the write path has
|
|
17
|
+
only the schema in hand. So the name is carried **on the schema**, set once at
|
|
18
|
+
the declaration by `named` — the ppx does it for a variant a state record uses,
|
|
19
|
+
and a framework type carrying a union does it beside its `Semantic.mark`.
|
|
20
|
+
|
|
21
|
+
A union with no name is not emitted as one. It falls through to the IR's
|
|
22
|
+
`Unknown`, which is a `String` in the SDL — the behaviour that predates this
|
|
23
|
+
module — except that it is now reported at deploy time instead of silently.
|
|
24
|
+
*/
|
|
25
|
+
let unionNameId: S.Metadata.Id.t<string> = S.Metadata.Id.make(
|
|
26
|
+
~namespace="reventless",
|
|
27
|
+
~name="taggedUnionName",
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
Names a union, so that the type emitted for it and the `__typename` stamped into
|
|
32
|
+
every stored value of it agree.
|
|
33
|
+
|
|
34
|
+
Written at the declaration, never at the field: two fields holding the same union
|
|
35
|
+
hold the same type, and naming it per field is what produces one GraphQL type per
|
|
36
|
+
field path — the mistake `semanticCompositeNames` exists to undo for `Money`.
|
|
37
|
+
*/
|
|
38
|
+
let named = (~name: string, schema: S.t<'a>): S.t<'a> =>
|
|
39
|
+
schema->S.Metadata.set(~id=unionNameId, name)
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
The name a union field's schema carries.
|
|
43
|
+
|
|
44
|
+
Read straight off the field's schema, wrapper included: sury's `option` keeps the
|
|
45
|
+
metadata of what it wraps, which matters because it does *not* keep the union as
|
|
46
|
+
a nested schema — `option<t>` flattens the arms and the `undefined` into one
|
|
47
|
+
`anyOf`, leaving nothing inside to consult. `TaggedUnionTest` pins that, since a
|
|
48
|
+
sury release that stopped preserving it would turn every optional union field
|
|
49
|
+
into a `String` with no compile error anywhere.
|
|
50
|
+
*/
|
|
51
|
+
let getName = (schema: S.t<unknown>): option<string> => S.Metadata.get(schema, ~id=unionNameId)
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
The GraphQL type emitted for one arm: the union's name with the arm's own
|
|
55
|
+
appended (`Geolocation` + `Located` = `GeolocationLocated`).
|
|
56
|
+
|
|
57
|
+
One derivation, used by the SDL emitter and by the write-time stamp. A second
|
|
58
|
+
spelling of this rule anywhere is a `__typename` that resolves to no member,
|
|
59
|
+
which GraphQL reports as a null — and a null in a non-nullable field takes its
|
|
60
|
+
parent with it.
|
|
61
|
+
*/
|
|
62
|
+
let memberTypeName = (~union: string, ~arm: string): string => union ++ arm
|
|
63
|
+
|
|
64
|
+
/** The key a stored union value carries its member type under. */
|
|
65
|
+
let typenameKey = "__typename"
|
|
66
|
+
|
|
67
|
+
/** One arm: the constructor name sury writes into `TAG`, and the arm's schema. */
|
|
68
|
+
type arm = {tag: string, schema: S.t<unknown>}
|
|
69
|
+
|
|
70
|
+
// A payload sury named rather than the author: `| Located(GeoPoint.t)` encodes to
|
|
71
|
+
// `{"TAG":"Located","_0":{…}}`, and `_0` would be published as an SDL field name
|
|
72
|
+
// and as a stored key. The ppx refuses the shape at its declaration; this refuses
|
|
73
|
+
// it again for a union declared where the ppx cannot see it, by declining to
|
|
74
|
+
// classify the union at all.
|
|
75
|
+
let isPositionalName = (name: string): bool =>
|
|
76
|
+
name->String.startsWith("_") &&
|
|
77
|
+
name->String.length > 1 &&
|
|
78
|
+
name
|
|
79
|
+
->String.slice(~start=1, ~end=name->String.length)
|
|
80
|
+
->String.split("")
|
|
81
|
+
->Array.every(c => c >= "0" && c <= "9")
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
The arms of a schema that is a union of tagged objects, or `None`.
|
|
85
|
+
|
|
86
|
+
Refuses three shapes, all for reasons that are GraphQL's rather than sury's, and
|
|
87
|
+
all of which encode and decode perfectly well:
|
|
88
|
+
|
|
89
|
+
- a payload-less arm (`| Pending`), which sury writes as the bare string
|
|
90
|
+
`"Pending"` — a union member must be an object type;
|
|
91
|
+
- an arm with no field of its own (`| Pending({})`), which would imply a member
|
|
92
|
+
type with zero fields;
|
|
93
|
+
- a positional payload, whose field name is the compiler's `_0`.
|
|
94
|
+
|
|
95
|
+
Declining leaves the field an `Unknown`, which is reported where it is emitted.
|
|
96
|
+
*/
|
|
97
|
+
let armsOf = (schema: S.t<unknown>): option<array<arm>> =>
|
|
98
|
+
switch schema {
|
|
99
|
+
| AnyOf({anyOf}) =>
|
|
100
|
+
let members = anyOf->Array.filter(v =>
|
|
101
|
+
switch v {
|
|
102
|
+
| Null(_) | Undefined(_) => false
|
|
103
|
+
| _ => true
|
|
104
|
+
}
|
|
105
|
+
)
|
|
106
|
+
if members->Array.length < 2 {
|
|
107
|
+
None
|
|
108
|
+
} else {
|
|
109
|
+
let arms = members->Array.filterMap(member =>
|
|
110
|
+
switch member {
|
|
111
|
+
| Object({properties}) =>
|
|
112
|
+
switch properties->Dict.get("TAG") {
|
|
113
|
+
| Some(String({const: ?Some(tag)})) =>
|
|
114
|
+
let fields = properties->Dict.toArray->Array.filter(((name, _)) => name !== "TAG")
|
|
115
|
+
if (
|
|
116
|
+
fields->Array.length == 0 ||
|
|
117
|
+
fields->Array.some(((name, _)) => isPositionalName(name))
|
|
118
|
+
) {
|
|
119
|
+
None
|
|
120
|
+
} else {
|
|
121
|
+
Some({tag, schema: member})
|
|
122
|
+
}
|
|
123
|
+
| _ => None
|
|
124
|
+
}
|
|
125
|
+
| _ => None
|
|
126
|
+
}
|
|
127
|
+
)
|
|
128
|
+
arms->Array.length == members->Array.length ? Some(arms) : None
|
|
129
|
+
}
|
|
130
|
+
| _ => None
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** A named union of tagged objects: its name and its arms, or `None`. */
|
|
134
|
+
let classify = (schema: S.t<unknown>): option<(string, array<arm>)> =>
|
|
135
|
+
switch (getName(schema), armsOf(schema)) {
|
|
136
|
+
| (Some(name), Some(arms)) => Some((name, arms))
|
|
137
|
+
| _ => None
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
Whether a schema is a union of tagged objects that carries no name — the one case
|
|
142
|
+
worth telling a deploy about, since it is a union the author meant and the SDL
|
|
143
|
+
cannot emit.
|
|
144
|
+
*/
|
|
145
|
+
let isUnnamedUnion = (schema: S.t<unknown>): bool =>
|
|
146
|
+
getName(schema)->Option.isNone && armsOf(schema)->Option.isSome
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
Stamps `__typename` into every union value inside an encoded row, in place.
|
|
150
|
+
|
|
151
|
+
Written once at save rather than by each read door: both AppSync and graphql-js
|
|
152
|
+
resolve a union member from `__typename` on the value they are handed, and the
|
|
153
|
+
AppSync resolvers hand back the stored item unchanged. The doors that would each
|
|
154
|
+
have to stamp number fourteen across three backends, and the live change channel
|
|
155
|
+
— which carries the row as raw JSON, past the typed field entirely — is reachable
|
|
156
|
+
from none of them. Stamping here is one place, and it is the only one both
|
|
157
|
+
channels share.
|
|
158
|
+
|
|
159
|
+
Driven by the schema, so a row of a view with no union field is walked and left
|
|
160
|
+
byte-identical.
|
|
161
|
+
*/
|
|
162
|
+
let rec stampInto = (~schema: S.t<unknown>, json: JSON.t): unit =>
|
|
163
|
+
switch classify(schema) {
|
|
164
|
+
| Some((name, arms)) =>
|
|
165
|
+
switch json->JSON.Decode.object {
|
|
166
|
+
| Some(obj) =>
|
|
167
|
+
switch obj->Dict.get("TAG")->Option.flatMap(JSON.Decode.string) {
|
|
168
|
+
| Some(tag) =>
|
|
169
|
+
obj->Dict.set(typenameKey, JSON.Encode.string(memberTypeName(~union=name, ~arm=tag)))
|
|
170
|
+
// An arm's own fields may hold unions too, so the arm is walked as the
|
|
171
|
+
// record it is — the union case above cannot recurse into it, since a
|
|
172
|
+
// union's schema says nothing about which arm this value took.
|
|
173
|
+
switch arms->Array.find(a => a.tag === tag) {
|
|
174
|
+
| Some({schema: armSchema}) => stampMembers(~schema=armSchema, json)
|
|
175
|
+
| None => ()
|
|
176
|
+
}
|
|
177
|
+
| None => ()
|
|
178
|
+
}
|
|
179
|
+
| None => ()
|
|
180
|
+
}
|
|
181
|
+
| None => stampMembers(~schema, json)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
and stampMembers = (~schema: S.t<unknown>, json: JSON.t): unit =>
|
|
185
|
+
switch schema {
|
|
186
|
+
| Object({properties}) =>
|
|
187
|
+
switch json->JSON.Decode.object {
|
|
188
|
+
| Some(obj) =>
|
|
189
|
+
properties
|
|
190
|
+
->Dict.toArray
|
|
191
|
+
->Array.forEach(((name, propSchema)) =>
|
|
192
|
+
switch obj->Dict.get(name) {
|
|
193
|
+
| Some(value) => stampInto(~schema=propSchema, value)
|
|
194
|
+
| None => ()
|
|
195
|
+
}
|
|
196
|
+
)
|
|
197
|
+
| None => ()
|
|
198
|
+
}
|
|
199
|
+
| Array({items, additionalItems}) =>
|
|
200
|
+
let itemSchema = switch items->Array.get(0) {
|
|
201
|
+
| Some(itemSchema) => Some(itemSchema)
|
|
202
|
+
| None =>
|
|
203
|
+
switch additionalItems {
|
|
204
|
+
| Schema(s) => Some(s)
|
|
205
|
+
| _ => None
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
switch (itemSchema, json->JSON.Decode.array) {
|
|
209
|
+
| (Some(itemSchema), Some(values)) =>
|
|
210
|
+
values->Array.forEach(value => stampInto(~schema=itemSchema, value))
|
|
211
|
+
| _ => ()
|
|
212
|
+
}
|
|
213
|
+
| _ =>
|
|
214
|
+
// An optional field wraps its schema in a union with `undefined`; the value
|
|
215
|
+
// that reached us is the inner one either way.
|
|
216
|
+
switch Semantic.unwrapOptional(schema) {
|
|
217
|
+
| Some(inner) => stampInto(~schema=inner, json)
|
|
218
|
+
| None => ()
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
The union fields an object schema declares, named and unnamed alike, with the
|
|
224
|
+
name where there is one.
|
|
225
|
+
|
|
226
|
+
The SDL emitter and the deploy-time report both need to say *which field* — a
|
|
227
|
+
report that names only the view leaves the author grepping.
|
|
228
|
+
*/
|
|
229
|
+
let fieldsOf = (schema: S.t<unknown>): array<(string, option<string>)> =>
|
|
230
|
+
switch schema {
|
|
231
|
+
| Object({properties}) =>
|
|
232
|
+
properties
|
|
233
|
+
->Dict.toArray
|
|
234
|
+
->Array.filterMap(((name, propSchema)) =>
|
|
235
|
+
armsOf(propSchema)->Option.isSome ? Some((name, getName(propSchema))) : None
|
|
236
|
+
)
|
|
237
|
+
| _ => []
|
|
238
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Sury from "sury";
|
|
4
|
+
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
5
|
+
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
6
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
7
|
+
import * as Semantic$Reventless from "../semantic/Semantic.res.mjs";
|
|
8
|
+
|
|
9
|
+
let unionNameId = Sury.$Metadata_Id_make("reventless", "taggedUnionName");
|
|
10
|
+
|
|
11
|
+
function named(name, schema) {
|
|
12
|
+
return Sury.$Metadata_set(schema, unionNameId, name);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getName(schema) {
|
|
16
|
+
return Sury.$Metadata_get(schema, unionNameId);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function memberTypeName(union, arm) {
|
|
20
|
+
return union + arm;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let typenameKey = "__typename";
|
|
24
|
+
|
|
25
|
+
function isPositionalName(name) {
|
|
26
|
+
if (name.startsWith("_") && name.length > 1) {
|
|
27
|
+
return name.slice(1, name.length).split("").every(c => {
|
|
28
|
+
if (c >= "0") {
|
|
29
|
+
return c <= "9";
|
|
30
|
+
} else {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
} else {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function armsOf(schema) {
|
|
40
|
+
if (schema.type !== "anyOf") {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
let members = schema.anyOf.filter(v => {
|
|
44
|
+
switch (v.type) {
|
|
45
|
+
case "null" :
|
|
46
|
+
case "undefined" :
|
|
47
|
+
return false;
|
|
48
|
+
default:
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
if (members.length < 2) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
let arms = Stdlib_Array.filterMap(members, member => {
|
|
56
|
+
if (member.type !== "object") {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
let properties = member.properties;
|
|
60
|
+
let match = properties["TAG"];
|
|
61
|
+
if (match === undefined) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (match.type !== "string") {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
let tag = match.const;
|
|
68
|
+
if (tag === undefined) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
let fields = Object.entries(properties).filter(param => param[0] !== "TAG");
|
|
72
|
+
if (fields.length === 0 || fields.some(param => isPositionalName(param[0]))) {
|
|
73
|
+
return;
|
|
74
|
+
} else {
|
|
75
|
+
return {
|
|
76
|
+
tag: tag,
|
|
77
|
+
schema: member
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
if (arms.length === members.length) {
|
|
82
|
+
return arms;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function classify(schema) {
|
|
87
|
+
let match = Sury.$Metadata_get(schema, unionNameId);
|
|
88
|
+
let match$1 = armsOf(schema);
|
|
89
|
+
if (match !== undefined && match$1 !== undefined) {
|
|
90
|
+
return [
|
|
91
|
+
match,
|
|
92
|
+
match$1
|
|
93
|
+
];
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function isUnnamedUnion(schema) {
|
|
98
|
+
if (Stdlib_Option.isNone(Sury.$Metadata_get(schema, unionNameId))) {
|
|
99
|
+
return Stdlib_Option.isSome(armsOf(schema));
|
|
100
|
+
} else {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function stampInto(schema, json) {
|
|
106
|
+
let match = classify(schema);
|
|
107
|
+
if (match === undefined) {
|
|
108
|
+
return stampMembers(schema, json);
|
|
109
|
+
}
|
|
110
|
+
let obj = Stdlib_JSON.Decode.object(json);
|
|
111
|
+
if (obj === undefined) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
let tag = Stdlib_Option.flatMap(obj["TAG"], Stdlib_JSON.Decode.string);
|
|
115
|
+
if (tag === undefined) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
obj[typenameKey] = match[0] + tag;
|
|
119
|
+
let match$1 = match[1].find(a => a.tag === tag);
|
|
120
|
+
if (match$1 !== undefined) {
|
|
121
|
+
return stampMembers(match$1.schema, json);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function stampMembers(schema, json) {
|
|
126
|
+
switch (schema.type) {
|
|
127
|
+
case "array" :
|
|
128
|
+
let additionalItems = schema.additionalItems;
|
|
129
|
+
let itemSchema = schema.items[0];
|
|
130
|
+
let itemSchema$1 = itemSchema !== undefined ? itemSchema : (
|
|
131
|
+
additionalItems === "strip" || additionalItems === "strict" ? undefined : additionalItems
|
|
132
|
+
);
|
|
133
|
+
let match = Stdlib_JSON.Decode.array(json);
|
|
134
|
+
if (itemSchema$1 !== undefined && match !== undefined) {
|
|
135
|
+
match.forEach(value => stampInto(itemSchema$1, value));
|
|
136
|
+
return;
|
|
137
|
+
} else {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
case "object" :
|
|
141
|
+
let obj = Stdlib_JSON.Decode.object(json);
|
|
142
|
+
if (obj !== undefined) {
|
|
143
|
+
Object.entries(schema.properties).forEach(param => {
|
|
144
|
+
let value = obj[param[0]];
|
|
145
|
+
if (value !== undefined) {
|
|
146
|
+
return stampInto(param[1], value);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
return;
|
|
150
|
+
} else {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
default:
|
|
154
|
+
let inner = Semantic$Reventless.unwrapOptional(schema);
|
|
155
|
+
if (inner !== undefined) {
|
|
156
|
+
return stampInto(inner, json);
|
|
157
|
+
} else {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function fieldsOf(schema) {
|
|
164
|
+
if (schema.type === "object") {
|
|
165
|
+
return Stdlib_Array.filterMap(Object.entries(schema.properties), param => {
|
|
166
|
+
let propSchema = param[1];
|
|
167
|
+
if (Stdlib_Option.isSome(armsOf(propSchema))) {
|
|
168
|
+
return [
|
|
169
|
+
param[0],
|
|
170
|
+
Sury.$Metadata_get(propSchema, unionNameId)
|
|
171
|
+
];
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
} else {
|
|
175
|
+
return [];
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export {
|
|
180
|
+
unionNameId,
|
|
181
|
+
named,
|
|
182
|
+
getName,
|
|
183
|
+
memberTypeName,
|
|
184
|
+
typenameKey,
|
|
185
|
+
isPositionalName,
|
|
186
|
+
armsOf,
|
|
187
|
+
classify,
|
|
188
|
+
isUnnamedUnion,
|
|
189
|
+
stampInto,
|
|
190
|
+
stampMembers,
|
|
191
|
+
fieldsOf,
|
|
192
|
+
}
|
|
193
|
+
/* unionNameId Not a pure module */
|
|
@@ -73,17 +73,16 @@ let splitKey = (key: string): option<(string, string)> =>
|
|
|
73
73
|
key->String.slice(~start=i + 1),
|
|
74
74
|
))
|
|
75
75
|
|
|
76
|
-
// The provenance comment
|
|
77
|
-
//
|
|
76
|
+
// The provenance comment names the store as the field spells it — taken from
|
|
77
|
+
// the manifest, which recorded it at the one place the owning plugin was
|
|
78
78
|
// unambiguous. Inferring it here is what this replaced: the manifest carries a
|
|
79
79
|
// deploy-manifest entry name and the key carries the registered name, and no
|
|
80
80
|
// rule relates the two (`platform-inspector` against `PlatformInspector` is an
|
|
81
|
-
// ordinary pairing), so any comparison here eventually
|
|
81
|
+
// ordinary pairing), so any comparison here eventually names a string that is
|
|
82
82
|
// in no source file.
|
|
83
83
|
//
|
|
84
|
-
// A site with no recorded
|
|
85
|
-
// field, which is the comment's job; it just makes no claim
|
|
86
|
-
// text it cannot see.
|
|
84
|
+
// A site with no recorded store predates the recording. It still names the
|
|
85
|
+
// field, which is the comment's job; it just makes no claim it cannot support.
|
|
87
86
|
let renderEntry = (entry: unionEntry): result<array<string>, string> =>
|
|
88
87
|
switch splitKey(entry.key) {
|
|
89
88
|
| None =>
|
|
@@ -94,8 +93,13 @@ let renderEntry = (entry: unionEntry): result<array<string>, string> =>
|
|
|
94
93
|
let comments =
|
|
95
94
|
entry.declaredBy->Array.map(site => {
|
|
96
95
|
let site_ = ` // ${site.pluginName}: ${site.component}.${site.field}`
|
|
96
|
+
// `→ store` rather than `@storageRef("store")`: a field may name its
|
|
97
|
+
// store by annotation *or* by being named, and the manifest carries
|
|
98
|
+
// the store either way without saying which. Naming the annotation
|
|
99
|
+
// would send a reader looking for source text a derived field has not
|
|
100
|
+
// got.
|
|
97
101
|
switch site.annotation {
|
|
98
|
-
| Some(annotation) => `${site_}
|
|
102
|
+
| Some(annotation) => `${site_} → ${annotation}`
|
|
99
103
|
| None => site_
|
|
100
104
|
}
|
|
101
105
|
})
|
|
@@ -115,13 +119,12 @@ aggregate is keyed by the registered name — so a second plugin registering an
|
|
|
115
119
|
existing name is not rejected but read as a **new version of the first**, and
|
|
116
120
|
silently supersedes it in the registry.
|
|
117
121
|
|
|
118
|
-
Ownership is read from the
|
|
119
|
-
separates a duplicate from legitimate sharing: an **unqualified**
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
trip this.
|
|
122
|
+
Ownership is read from the store as the field states it, which is the one signal
|
|
123
|
+
that separates a duplicate from legitimate sharing: an **unqualified**
|
|
124
|
+
`productImages` means "a store of my own plugin", so the deployable that stated
|
|
125
|
+
it is the plugin the key names. A **qualified** `Catalog.productImages` points at
|
|
126
|
+
someone else's store and says nothing about who owns it — that is the sanctioned
|
|
127
|
+
cross-plugin form and must not trip this.
|
|
125
128
|
|
|
126
129
|
Partial by construction: it can only see plugins that declare a store, because
|
|
127
130
|
capability manifests are the only per-plugin input the generator reads. A
|
|
@@ -59,7 +59,7 @@ function renderEntry(entry) {
|
|
|
59
59
|
let site_ = ` // ` + site.pluginName + `: ` + site.component + `.` + site.field;
|
|
60
60
|
let annotation = site.annotation;
|
|
61
61
|
if (annotation !== undefined) {
|
|
62
|
-
return site_ + `
|
|
62
|
+
return site_ + ` → ` + annotation;
|
|
63
63
|
} else {
|
|
64
64
|
return site_;
|
|
65
65
|
}
|
|
@@ -95,7 +95,7 @@ let () = {
|
|
|
95
95
|
)
|
|
96
96
|
let manifest = try NodeFs.readFileSync(path)
|
|
97
97
|
->JSON.parseOrThrow
|
|
98
|
-
->S.parseOrThrow(CapabilityManifest.schema) catch {
|
|
98
|
+
->S.parseOrThrow(~to=CapabilityManifest.schema) catch {
|
|
99
99
|
| _ => {
|
|
100
100
|
fail(`could not parse ${path} as a capability manifest`)
|
|
101
101
|
({capabilities: []}: CapabilityManifest.t)
|
|
@@ -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) {
|
|
@@ -29,7 +29,7 @@ function fromFloat(raw) {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
let schema = Semantic$Reventless.refined(
|
|
32
|
+
let schema = Semantic$Reventless.refined(Sury.float, Semantic$Reventless.Id.bytes, fromFloat);
|
|
33
33
|
|
|
34
34
|
export {
|
|
35
35
|
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 hex = /^#([0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
|
|
@@ -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.color, fromString);
|
|
23
23
|
|
|
24
24
|
export {
|
|
25
25
|
hex,
|