@reventlessdev/reventless-spec 3.0.0-alpha.133 → 3.0.0-alpha.134
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 +7 -0
- package/package.json +2 -2
- package/src/AnsiStyle.res +0 -1
- package/src/LogPrefix.res +4 -13
- package/src/components/AutomationSlice.res +1 -5
- package/src/components/CapabilityManifest.res +2 -3
- package/src/components/DcbDecode.res +6 -2
- package/src/components/DcbScopeInference.res +16 -12
- package/src/components/DcbTag.res +74 -46
- package/src/components/DcbValidation.res +114 -89
- package/src/components/DisplayName.res +4 -2
- package/src/components/ExtensionPoint.res +0 -1
- package/src/components/FieldDefault.res +2 -4
- package/src/components/InboundTranslationSlice.res +0 -2
- package/src/components/OutboundTranslationSlice.res +10 -8
- package/src/components/Owner.res +4 -4
- package/src/components/Plugin.res +10 -4
- package/src/components/ReadModel.res +0 -1
- package/src/components/Reference.res +2 -8
- package/src/components/Sensitive.res +4 -4
- package/src/components/StateAnnotations.res +4 -2
- package/src/components/StateChangeSlice.res +0 -2
- package/src/components/StateViewSlice.res +0 -2
- package/src/components/TaggedUnion.res +1 -2
- package/src/components/Task.res +0 -1
- package/src/components/TraitCertificate.res +2 -2
- package/src/components/TraitManifest.res +1 -1
- package/src/generator/CertifyTrait.res +2 -4
- package/src/generator/Codegen.res +77 -68
- package/src/generator/Discovery.res +29 -12
- package/src/generator/GraftTrait.res +17 -9
- package/src/generator/Pairing.res +39 -13
- package/src/generator/PlatformCodegen.res +13 -11
- package/src/generator/PlatformManifests.res +1 -3
- package/src/generator/PluginGenerator.res +0 -1
- package/src/generator/TraitManifestCli.res +7 -7
- package/src/lifecycle/CheckLifecycleModel.res +173 -145
- package/src/lifecycle/CheckLifecycleModel.res.mjs +17 -2
- package/src/semantic/Bytes.res +0 -1
- package/src/semantic/CalendarDate.res +2 -2
- package/src/semantic/Capabilities.res +4 -6
- package/src/semantic/Capabilities.res.mjs +9 -11
- package/src/semantic/CapabilityNeed.res +4 -5
- package/src/semantic/CaptionedImage.res +0 -1
- package/src/semantic/Color.res +0 -1
- package/src/semantic/Currency.res +189 -170
- package/src/semantic/DateRange.res +3 -4
- package/src/semantic/DateTime.res +5 -5
- package/src/semantic/Duration.res +2 -2
- package/src/semantic/Email.res +0 -1
- package/src/semantic/FileRef.res +2 -2
- package/src/semantic/GeoPoint.res +17 -21
- package/src/semantic/Geocoding.res +8 -9
- package/src/semantic/Geolocation.res +6 -9
- package/src/semantic/ImageRef.res +2 -2
- package/src/semantic/MemberRef.res +0 -1
- package/src/semantic/Messaging.res +141 -21
- package/src/semantic/Messaging.res.mjs +51 -8
- package/src/semantic/Money.res +23 -20
- package/src/semantic/Offload.res +6 -4
- package/src/semantic/Percent.res +3 -3
- package/src/semantic/Phone.res +0 -1
- package/src/semantic/RowImage.res +4 -3
- package/src/semantic/Semantic.res +8 -11
- package/src/semantic/StorageRef.res +17 -17
- package/src/semantic/Template.res +5 -7
- package/src/semantic/UploadableFile.res +0 -1
- package/src/semantic/UploadableImage.res +0 -1
- package/src/semantic/Url.res +3 -3
- package/src/types/Authorization.res +1 -2
- package/src/types/Handler.res +0 -1
- package/src/types/Identity.res +1 -3
- package/src/types/Lifecycle.res +7 -6
- package/src/types/Message.res +6 -4
- package/src/types/OwnerScope.res +1 -3
- package/src/types/Projection.res +9 -2
- package/src/types/StoredEvent.res +2 -4
- package/src/types/Transition.res +8 -4
- package/src/util/Util_Sury.res +1 -4
|
@@ -60,7 +60,6 @@ check-lifecycle --root . --reuse-sidecars --json
|
|
|
60
60
|
check-lifecycle --root . --update # write this app's models and golden
|
|
61
61
|
```
|
|
62
62
|
*/
|
|
63
|
-
|
|
64
63
|
// The model this script writes is folded into `pluginStructure`, so by the time
|
|
65
64
|
// it runs again the "declared" side it reads back would be its own last answer —
|
|
66
65
|
// every edge confirmed, and a real disagreement between an annotation and the
|
|
@@ -103,7 +102,8 @@ type appRoot = {label: string, dir: string}
|
|
|
103
102
|
corpus is on disk; a second pass over a warm tree buys nothing and costs the
|
|
104
103
|
multi-root build chain's habit of cleaning artifacts outside the root it is
|
|
105
104
|
building, which lands intermittently on a stale `.cmi`. */
|
|
106
|
-
let reuseSidecars =
|
|
105
|
+
let reuseSidecars =
|
|
106
|
+
NodeProcess.argv->Array.includes("--reuse-sidecars")
|
|
107
107
|
|
|
108
108
|
/** Report the run as one JSON document on stdout instead of the grouped prose.
|
|
109
109
|
|
|
@@ -117,7 +117,8 @@ let reuseSidecars = NodeProcess.argv->Array.includes("--reuse-sidecars")
|
|
|
117
117
|
repository says it said — nor fail because the two have drifted, which is a
|
|
118
118
|
fact about the repository rather than about the corpus it was asked to
|
|
119
119
|
report. A contradiction still exits non-zero, so this composes with a gate. */
|
|
120
|
-
let json =
|
|
120
|
+
let json =
|
|
121
|
+
NodeProcess.argv->Array.includes("--json")
|
|
121
122
|
|
|
122
123
|
/** The label a state carries when no row exists yet. Not a lifecycle case — no
|
|
123
124
|
enum declares it — so it is spelled in a way no constructor can name, and a
|
|
@@ -295,8 +296,7 @@ let checkSidecars = (~pluginDirs: array<string>): result<unit, string> =>
|
|
|
295
296
|
hasCorpus(~pluginDirs)
|
|
296
297
|
? Ok()
|
|
297
298
|
: Error(
|
|
298
|
-
"--reuse-sidecars was passed, but no scenario sidecar exists. Build with " ++
|
|
299
|
-
"REVENTLESS_EMIT_SIDECAR=1 first, or drop the flag.",
|
|
299
|
+
"--reuse-sidecars was passed, but no scenario sidecar exists. Build with " ++ "REVENTLESS_EMIT_SIDECAR=1 first, or drop the flag.",
|
|
300
300
|
)
|
|
301
301
|
|
|
302
302
|
/** Sidecars are gitignored build artifacts, so the harvest produces its own
|
|
@@ -405,11 +405,13 @@ let declaredOf = (structure: JSON.t): option<declared> =>
|
|
|
405
405
|
getObjs(s, key)->Array.filterMap(w =>
|
|
406
406
|
w
|
|
407
407
|
->getStr("name")
|
|
408
|
-
->Option.map(
|
|
409
|
-
name
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
408
|
+
->Option.map(
|
|
409
|
+
name => {
|
|
410
|
+
name,
|
|
411
|
+
linkedViews: getStrs(w, "linkedViews"),
|
|
412
|
+
commands: getObjs(w, "commands")->Array.filterMap(declaredCommandOf),
|
|
413
|
+
},
|
|
414
|
+
)
|
|
413
415
|
)
|
|
414
416
|
let viewsFrom = key =>
|
|
415
417
|
getObjs(s, key)->Array.filterMap(v =>
|
|
@@ -647,9 +649,7 @@ let deriveCommands = (
|
|
|
647
649
|
effective->Array.filterMap(o => o.from == noRow ? None : Some(o.from)),
|
|
648
650
|
),
|
|
649
651
|
inertStates: sortedUnique(
|
|
650
|
-
mine->Array.filterMap(o =>
|
|
651
|
-
o.outcome == Emitted || o.from == noRow ? None : Some(o.from)
|
|
652
|
-
),
|
|
652
|
+
mine->Array.filterMap(o => o.outcome == Emitted || o.from == noRow ? None : Some(o.from)),
|
|
653
653
|
),
|
|
654
654
|
targets: sortedUnique(
|
|
655
655
|
effective->Array.filterMap(o => o.to == o.from || o.to == noRow ? None : Some(o.to)),
|
|
@@ -764,8 +764,7 @@ let compare = (
|
|
|
764
764
|
add(
|
|
765
765
|
"contradicted",
|
|
766
766
|
[state],
|
|
767
|
-
`the switch declares it legal in every state, and a scenario from "${state}" ` ++
|
|
768
|
-
`shows it refused or producing nothing`,
|
|
767
|
+
`the switch declares it legal in every state, and a scenario from "${state}" ` ++ `shows it refused or producing nothing`,
|
|
769
768
|
)
|
|
770
769
|
)
|
|
771
770
|
| None =>
|
|
@@ -773,8 +772,9 @@ let compare = (
|
|
|
773
772
|
add(
|
|
774
773
|
"undeclared",
|
|
775
774
|
derived.allowedStates,
|
|
776
|
-
`scenarios show it taking effect from ${derived.allowedStates->Array.join(
|
|
777
|
-
|
|
775
|
+
`scenarios show it taking effect from ${derived.allowedStates->Array.join(
|
|
776
|
+
", ",
|
|
777
|
+
)}, ` ++ `and it declares no edge`,
|
|
778
778
|
)
|
|
779
779
|
}
|
|
780
780
|
| Some(states) =>
|
|
@@ -785,8 +785,7 @@ let compare = (
|
|
|
785
785
|
add(
|
|
786
786
|
"contradicted",
|
|
787
787
|
[state],
|
|
788
|
-
`the switch names "${state}", and a scenario from "${state}" shows it ` ++
|
|
789
|
-
`refused or producing nothing`,
|
|
788
|
+
`the switch names "${state}", and a scenario from "${state}" shows it ` ++ `refused or producing nothing`,
|
|
790
789
|
)
|
|
791
790
|
} else {
|
|
792
791
|
add("unverified", [state], `the switch names "${state}", and no scenario starts there`)
|
|
@@ -806,8 +805,9 @@ let compare = (
|
|
|
806
805
|
add(
|
|
807
806
|
"unverified",
|
|
808
807
|
states,
|
|
809
|
-
`the switch declares ${Array.length(
|
|
810
|
-
|
|
808
|
+
`the switch declares ${Array.length(
|
|
809
|
+
states,
|
|
810
|
+
)->Int.toString} state(s) and ` ++ `no scenario shows the command taking effect anywhere`,
|
|
811
811
|
)
|
|
812
812
|
}
|
|
813
813
|
}
|
|
@@ -830,8 +830,7 @@ let compare = (
|
|
|
830
830
|
add(
|
|
831
831
|
"contradicted",
|
|
832
832
|
[target, state],
|
|
833
|
-
`the switch targets "${target}", and a scenario lands in "${state}" — ` ++
|
|
834
|
-
`the published targetState carries one state, so this edge cannot be expressed`,
|
|
833
|
+
`the switch targets "${target}", and a scenario lands in "${state}" — ` ++ `the published targetState carries one state, so this edge cannot be expressed`,
|
|
835
834
|
)
|
|
836
835
|
}
|
|
837
836
|
)
|
|
@@ -933,107 +932,111 @@ let runPlugin = async (
|
|
|
933
932
|
switch await readDeclared(~pluginDir) {
|
|
934
933
|
| Error(msg) => Error(msg)
|
|
935
934
|
| Ok(declared) =>
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
)
|
|
940
|
-
|
|
941
|
-
// Every corpus, not only the ones the walk goes on to use: the kinds that
|
|
942
|
-
// are unreadable in full — extension points, automation and translation
|
|
943
|
-
// slices — are exactly the ones that sit outside the view/writable folders
|
|
944
|
-
// below, and they are the ones a coverage UI would otherwise slander.
|
|
945
|
-
corpora->Array.forEach(c => {
|
|
946
|
-
let unreadable = c.scenarios->Array.filter(s => Array.length(s.whenElements) == 0)
|
|
947
|
-
if Array.length(unreadable) > 0 {
|
|
948
|
-
opaque
|
|
949
|
-
->Array.push({
|
|
950
|
-
plugin,
|
|
951
|
-
component: c.component,
|
|
952
|
-
path: c.path,
|
|
953
|
-
scenarios: Array.length(c.scenarios),
|
|
954
|
-
unreadable: Array.length(unreadable),
|
|
955
|
-
})
|
|
956
|
-
->ignore
|
|
957
|
-
}
|
|
958
|
-
})
|
|
959
|
-
|
|
960
|
-
// Views first: a command's history cannot be labelled until the events in
|
|
961
|
-
// it have somewhere to land.
|
|
962
|
-
let mapsByView = Dict.make()
|
|
963
|
-
let ambiguities = []
|
|
964
|
-
corpora->Array.forEach(c =>
|
|
965
|
-
if isViewPath(c.path) {
|
|
966
|
-
switch declared.views->Array.find(v => v.name == c.component) {
|
|
967
|
-
| Some({lifecycleField: Some(field)}) =>
|
|
968
|
-
mapsByView->Dict.set(
|
|
969
|
-
c.component,
|
|
970
|
-
lifecycleMapFor(~scenarios=c.scenarios, ~field, ~ambiguities, ~view=c.component),
|
|
971
|
-
)
|
|
972
|
-
// A view with no lifecycle field labels nothing, and that is ordinary
|
|
973
|
-
// — most views have no lifecycle at all.
|
|
974
|
-
| _ => ()
|
|
975
|
-
}
|
|
976
|
-
}
|
|
935
|
+
let corpora =
|
|
936
|
+
filesUnder(NodePath.join([pluginDir, "tests"]), ~suffix=".gwt.json")->Array.filterMap(
|
|
937
|
+
readCorpus,
|
|
977
938
|
)
|
|
978
|
-
|
|
979
|
-
|
|
939
|
+
|
|
940
|
+
// Every corpus, not only the ones the walk goes on to use: the kinds that
|
|
941
|
+
// are unreadable in full — extension points, automation and translation
|
|
942
|
+
// slices — are exactly the ones that sit outside the view/writable folders
|
|
943
|
+
// below, and they are the ones a coverage UI would otherwise slander.
|
|
944
|
+
corpora->Array.forEach(c => {
|
|
945
|
+
let unreadable = c.scenarios->Array.filter(s => Array.length(s.whenElements) == 0)
|
|
946
|
+
if Array.length(unreadable) > 0 {
|
|
947
|
+
opaque
|
|
980
948
|
->Array.push({
|
|
981
|
-
severity: "ambiguous",
|
|
982
949
|
plugin,
|
|
983
|
-
component:
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
950
|
+
component: c.component,
|
|
951
|
+
path: c.path,
|
|
952
|
+
scenarios: Array.length(c.scenarios),
|
|
953
|
+
unreadable: Array.length(unreadable),
|
|
987
954
|
})
|
|
988
955
|
->ignore
|
|
989
|
-
|
|
956
|
+
}
|
|
957
|
+
})
|
|
990
958
|
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
959
|
+
// Views first: a command's history cannot be labelled until the events in
|
|
960
|
+
// it have somewhere to land.
|
|
961
|
+
let mapsByView = Dict.make()
|
|
962
|
+
let ambiguities = []
|
|
963
|
+
corpora->Array.forEach(c =>
|
|
964
|
+
if isViewPath(c.path) {
|
|
965
|
+
switch declared.views->Array.find(v => v.name == c.component) {
|
|
966
|
+
| Some({lifecycleField: Some(field)}) =>
|
|
967
|
+
mapsByView->Dict.set(
|
|
968
|
+
c.component,
|
|
969
|
+
lifecycleMapFor(~scenarios=c.scenarios, ~field, ~ambiguities, ~view=c.component),
|
|
970
|
+
)
|
|
971
|
+
// A view with no lifecycle field labels nothing, and that is ordinary
|
|
972
|
+
// — most views have no lifecycle at all.
|
|
973
|
+
| _ => ()
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
)
|
|
977
|
+
ambiguities->Array.forEach(((view, message)) =>
|
|
978
|
+
findings
|
|
979
|
+
->Array.push({
|
|
980
|
+
severity: "ambiguous",
|
|
981
|
+
plugin,
|
|
982
|
+
component: view,
|
|
983
|
+
command: "",
|
|
984
|
+
states: [],
|
|
985
|
+
message,
|
|
986
|
+
})
|
|
987
|
+
->ignore
|
|
988
|
+
)
|
|
989
|
+
|
|
990
|
+
let derived = []
|
|
991
|
+
corpora->Array.forEach(c =>
|
|
992
|
+
if isWritablePath(c.path) {
|
|
993
|
+
switch declared.writables->Array.find(w => w.name == c.component) {
|
|
994
|
+
| None => ()
|
|
995
|
+
| Some(writable) =>
|
|
996
|
+
// The union of the maps of every view this writable feeds. A union
|
|
997
|
+
// rather than a single view for the reason the platform's own name
|
|
998
|
+
// check uses one: a slice feeding two views is not claiming which of
|
|
999
|
+
// them a state belongs to.
|
|
1000
|
+
let map = Dict.make()
|
|
1001
|
+
writable.linkedViews->Array.forEach(view =>
|
|
1002
|
+
switch mapsByView->Dict.get(view) {
|
|
1003
|
+
| Some(m) => m->Dict.forEachWithKey((value, event) => map->Dict.set(event, value))
|
|
1004
|
+
| None => ()
|
|
1005
|
+
}
|
|
1006
|
+
)
|
|
1007
|
+
let labelled = Array.length(map->Dict.keysToArray) > 0
|
|
1008
|
+
let idFieldFor = command =>
|
|
1009
|
+
writable.commands
|
|
1010
|
+
->Array.find(c => c.command == command)
|
|
1011
|
+
->Option.flatMap(c => c.aggregateIdField)
|
|
1012
|
+
let observations = observe(~scenarios=c.scenarios, ~map, ~idFieldFor)
|
|
1013
|
+
let commands = deriveCommands(~component=c.component, ~observations, ~labelled)
|
|
1014
|
+
|
|
1015
|
+
commands->Array.forEach(d => {
|
|
1016
|
+
// Without a lifecycle map every history folds to "no row", so
|
|
1017
|
+
// there is nothing to confirm a claim against and nothing to
|
|
1018
|
+
// contradict it with. Reporting the claim as unverified is the
|
|
1019
|
+
// honest answer; running the comparison would manufacture
|
|
1020
|
+
// contradictions out of a missing map.
|
|
1021
|
+
if labelled {
|
|
1022
|
+
compare(~plugin, ~writable, ~derived=d, ~findings)
|
|
1023
|
+
}
|
|
1024
|
+
derived->Array.push(d)
|
|
1025
|
+
})
|
|
1027
1026
|
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1027
|
+
let why = labelled
|
|
1028
|
+
? "no scenario exercises the command"
|
|
1029
|
+
: `${writable.linkedViews->Array.join(
|
|
1030
|
+
", ",
|
|
1031
|
+
)} declares no lifecycle field, so its ` ++ `scenarios cannot be labelled`
|
|
1032
|
+
writable.commands->Array.forEach(cmd =>
|
|
1033
|
+
if labelled && commands->Array.some(d => d.command == cmd.command) {
|
|
1034
|
+
()
|
|
1035
|
+
} else {
|
|
1036
|
+
allUnverified(
|
|
1037
|
+
~cmd,
|
|
1038
|
+
~why,
|
|
1039
|
+
~add=(severity, states, message) =>
|
|
1037
1040
|
findings
|
|
1038
1041
|
->Array.push({
|
|
1039
1042
|
severity,
|
|
@@ -1043,14 +1046,14 @@ let runPlugin = async (
|
|
|
1043
1046
|
states,
|
|
1044
1047
|
message: `${plugin}/${writable.name}.${cmd.command}: ${message}`,
|
|
1045
1048
|
})
|
|
1046
|
-
->ignore
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
}
|
|
1049
|
+
->ignore,
|
|
1050
|
+
)
|
|
1051
|
+
}
|
|
1052
|
+
)
|
|
1051
1053
|
}
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
+
}
|
|
1055
|
+
)
|
|
1056
|
+
Ok(derived)
|
|
1054
1057
|
}
|
|
1055
1058
|
|
|
1056
1059
|
// ── The golden ──────────────────────────────────────────────────────────────
|
|
@@ -1160,6 +1163,19 @@ let reportJson = (
|
|
|
1160
1163
|
|
|
1161
1164
|
// ── The value structure assembly reads ──────────────────────────────────────
|
|
1162
1165
|
|
|
1166
|
+
/** The emitted file is committed ReScript, so it faces the tree's format gate
|
|
1167
|
+
like any other source. Laying the text out by hand would put this script and
|
|
1168
|
+
the formatter in a standing argument only one of them can win — so it emits
|
|
1169
|
+
whatever reads clearly and lets the formatter settle the layout. Raising
|
|
1170
|
+
beats falling back: unformatted source would satisfy this check and fail the
|
|
1171
|
+
format gate, which is the stand-off the call exists to end. */
|
|
1172
|
+
let formatted = (source: string): string =>
|
|
1173
|
+
NodeChildProcess.execFileSync(
|
|
1174
|
+
"pnpm",
|
|
1175
|
+
["exec", "rescript", "format", "--stdin", ".res"],
|
|
1176
|
+
{cwd: repoRoot, encoding: "utf8", input: source},
|
|
1177
|
+
)
|
|
1178
|
+
|
|
1163
1179
|
/** The same derivation as a committed ReScript value, so `buildStructure` gets
|
|
1164
1180
|
the model as data. It cannot read the corpus itself: tests are not published
|
|
1165
1181
|
with a plugin package, and metadata that read them would make deleting a test
|
|
@@ -1173,19 +1189,22 @@ let reportJson = (
|
|
|
1173
1189
|
let modelSource = (~plugin: string, ~derived: array<derivedCommand>): string => {
|
|
1174
1190
|
let saysSomething = (d: derivedCommand) =>
|
|
1175
1191
|
d.level != "" || Array.length(d.allowedStates) > 0 || Array.length(d.targets) > 0
|
|
1176
|
-
let quoted = (xs: array<string>) =>
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1192
|
+
let quoted = (xs: array<string>) => "[" ++ xs->Array.map(s => `"${s}"`)->Array.join(", ") ++ "]"
|
|
1193
|
+
let entries =
|
|
1194
|
+
derived
|
|
1195
|
+
->Array.filter(saysSomething)
|
|
1196
|
+
->byComponentThenCommand
|
|
1197
|
+
->Array.map(d => {
|
|
1198
|
+
// Omitted rather than written as an absent value: `level` is an optional
|
|
1199
|
+
// field, and a corpus that could not label this command's histories has
|
|
1200
|
+
// nothing to say about it.
|
|
1201
|
+
let level = switch d.level {
|
|
1202
|
+
| "Collection" | "Instance" => `level: Reventless.Plugin.${d.level}, `
|
|
1203
|
+
| _ => ""
|
|
1204
|
+
}
|
|
1205
|
+
` {component: "${d.component}", command: "${d.command}", ${level}` ++
|
|
1206
|
+
`allowedStates: ${quoted(d.allowedStates)}, targets: ${quoted(d.targets)}},`
|
|
1207
|
+
})
|
|
1189
1208
|
Array.flat([
|
|
1190
1209
|
[
|
|
1191
1210
|
`// AUTO-GENERATED — do not edit. Run \`pnpm run check:lifecycle:update\` to update.`,
|
|
@@ -1200,7 +1219,9 @@ let modelSource = (~plugin: string, ~derived: array<derivedCommand>): string =>
|
|
|
1200
1219
|
],
|
|
1201
1220
|
entries,
|
|
1202
1221
|
["]", ""],
|
|
1203
|
-
])
|
|
1222
|
+
])
|
|
1223
|
+
->Array.join("\n")
|
|
1224
|
+
->formatted
|
|
1204
1225
|
}
|
|
1205
1226
|
|
|
1206
1227
|
let modelPath = (~pluginDir: string) => NodePath.join([pluginDir, "src", "LifecycleModel.res"])
|
|
@@ -1234,8 +1255,9 @@ let main = async () => {
|
|
|
1234
1255
|
// an app with no plugins, and reporting "ok" for it is how that typo survives.
|
|
1235
1256
|
if Array.length(allPluginDirs) == 0 {
|
|
1236
1257
|
Console.error(
|
|
1237
|
-
`no plugins found under ${roots
|
|
1238
|
-
|
|
1258
|
+
`no plugins found under ${roots
|
|
1259
|
+
->Array.map(r => r.dir)
|
|
1260
|
+
->Array.join(", ")} — a plugin is a ` ++ `directory with both src/Plugin.res and tests/`,
|
|
1239
1261
|
)
|
|
1240
1262
|
NodeProcess.exit(1)
|
|
1241
1263
|
}
|
|
@@ -1256,8 +1278,11 @@ let main = async () => {
|
|
|
1256
1278
|
// refusing outright.
|
|
1257
1279
|
if !hasCorpus(~pluginDirs=allPluginDirs) {
|
|
1258
1280
|
Console.error(
|
|
1259
|
-
`no scenario sidecar exists under ${roots
|
|
1260
|
-
|
|
1281
|
+
`no scenario sidecar exists under ${roots
|
|
1282
|
+
->Array.map(r => r.dir)
|
|
1283
|
+
->Array.join(
|
|
1284
|
+
", ",
|
|
1285
|
+
)} after ` ++ `the build. Build that tree with REVENTLESS_EMIT_SIDECAR=1 and pass --reuse-sidecars.`,
|
|
1261
1286
|
)
|
|
1262
1287
|
NodeProcess.exit(1)
|
|
1263
1288
|
}
|
|
@@ -1308,7 +1333,9 @@ let main = async () => {
|
|
|
1308
1333
|
~drifted,
|
|
1309
1334
|
)
|
|
1310
1335
|
Console.log(
|
|
1311
|
-
`ok ${example} — ${Array.length(
|
|
1336
|
+
`ok ${example} — ${Array.length(
|
|
1337
|
+
derived,
|
|
1338
|
+
)->Int.toString} commands derived from scenarios`,
|
|
1312
1339
|
)
|
|
1313
1340
|
}
|
|
1314
1341
|
}
|
|
@@ -1336,8 +1363,9 @@ let main = async () => {
|
|
|
1336
1363
|
|
|
1337
1364
|
if Array.length(drifted) > 0 {
|
|
1338
1365
|
Console.error(
|
|
1339
|
-
`\n${Array.length(
|
|
1340
|
-
|
|
1366
|
+
`\n${Array.length(
|
|
1367
|
+
drifted,
|
|
1368
|
+
)->Int.toString} lifecycle artifact(s) changed. If the change is ` ++ `intended, re-run with --update and commit them alongside the change that moved them.`,
|
|
1341
1369
|
)
|
|
1342
1370
|
}
|
|
1343
1371
|
|
|
@@ -910,6 +910,20 @@ function reportJson(findings, opaque, derived, failures) {
|
|
|
910
910
|
]), undefined, 2) + "\n";
|
|
911
911
|
}
|
|
912
912
|
|
|
913
|
+
function formatted(source) {
|
|
914
|
+
return Nodechild_process.execFileSync("pnpm", [
|
|
915
|
+
"exec",
|
|
916
|
+
"rescript",
|
|
917
|
+
"format",
|
|
918
|
+
"--stdin",
|
|
919
|
+
".res"
|
|
920
|
+
], {
|
|
921
|
+
cwd: repoRoot,
|
|
922
|
+
encoding: "utf8",
|
|
923
|
+
input: source
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
|
|
913
927
|
function modelSource(plugin, derived) {
|
|
914
928
|
let saysSomething = d => {
|
|
915
929
|
if (d.level !== "" || d.allowedStates.length !== 0) {
|
|
@@ -932,7 +946,7 @@ function modelSource(plugin, derived) {
|
|
|
932
946
|
}
|
|
933
947
|
return ` {component: "` + d.component + `", command: "` + d.command + `", ` + level + (`allowedStates: ` + quoted(d.allowedStates) + `, targets: ` + quoted(d.targets) + `},`);
|
|
934
948
|
});
|
|
935
|
-
return [
|
|
949
|
+
return formatted([
|
|
936
950
|
[
|
|
937
951
|
`// AUTO-GENERATED — do not edit. Run \`pnpm run check:lifecycle:update\` to update.`,
|
|
938
952
|
`//`,
|
|
@@ -949,7 +963,7 @@ function modelSource(plugin, derived) {
|
|
|
949
963
|
"]",
|
|
950
964
|
""
|
|
951
965
|
]
|
|
952
|
-
].flat().join("\n");
|
|
966
|
+
].flat().join("\n"));
|
|
953
967
|
}
|
|
954
968
|
|
|
955
969
|
function modelPath(pluginDir) {
|
|
@@ -1126,6 +1140,7 @@ export {
|
|
|
1126
1140
|
goldenJson,
|
|
1127
1141
|
goldenPath,
|
|
1128
1142
|
reportJson,
|
|
1143
|
+
formatted,
|
|
1129
1144
|
modelSource,
|
|
1130
1145
|
modelPath,
|
|
1131
1146
|
writeOrCompare,
|
package/src/semantic/Bytes.res
CHANGED
|
@@ -31,7 +31,6 @@ type state = {
|
|
|
31
31
|
}
|
|
32
32
|
```
|
|
33
33
|
*/
|
|
34
|
-
|
|
35
34
|
/** The day's representation. Transparent `string`, like the rest of the branded
|
|
36
35
|
scalars: the marker refines an existing field rather than replacing it. */
|
|
37
36
|
type t = string
|
|
@@ -52,7 +51,8 @@ let fromString = (raw: string): result<t, string> =>
|
|
|
52
51
|
|
|
53
52
|
/** The sury schema for a calendar-date field. Write the field's type as
|
|
54
53
|
`Reventless.CalendarDate.t` and sury-ppx resolves it. */
|
|
55
|
-
let schema: S.t<t> =
|
|
54
|
+
let schema: S.t<t> =
|
|
55
|
+
S.string->Semantic.refined(~id=Semantic.Id.date, ~check=fromString)
|
|
56
56
|
|
|
57
57
|
/** Whether a field schema carries the calendar-date marker. */
|
|
58
58
|
let isCalendarDate = (fieldSchema: S.t<unknown>) => fieldSchema->Semantic.has(~id=Semantic.Id.date)
|
|
@@ -50,10 +50,8 @@ let none: t = {
|
|
|
50
50
|
// `UnsupportedChannel` because a caller that got this far is looking at a
|
|
51
51
|
// deployment gap, not at a fact about the recipient, and abandoning the message
|
|
52
52
|
// would record the second.
|
|
53
|
-
messaging:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
),
|
|
58
|
-
},
|
|
53
|
+
messaging: Messaging.makeProvider(~emailAndSms=[], ~pushServices=[], ~send=async (
|
|
54
|
+
~recipient as _,
|
|
55
|
+
~message as _,
|
|
56
|
+
) => Error(Unavailable("no messaging provider is configured for this platform"))),
|
|
59
57
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
+
import * as Messaging$Reventless from "./Messaging.res.mjs";
|
|
3
4
|
|
|
4
5
|
async function none_geocode(param) {
|
|
5
6
|
return {
|
|
@@ -11,16 +12,13 @@ async function none_geocode(param) {
|
|
|
11
12
|
};
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
let none_messaging = {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
TAG: "
|
|
18
|
-
_0:
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
})
|
|
23
|
-
};
|
|
15
|
+
let none_messaging = Messaging$Reventless.makeProvider([], [], async (param, param$1) => ({
|
|
16
|
+
TAG: "Error",
|
|
17
|
+
_0: {
|
|
18
|
+
TAG: "Unavailable",
|
|
19
|
+
_0: "no messaging provider is configured for this platform"
|
|
20
|
+
}
|
|
21
|
+
}));
|
|
24
22
|
|
|
25
23
|
let none = {
|
|
26
24
|
geocode: none_geocode,
|
|
@@ -30,4 +28,4 @@ let none = {
|
|
|
30
28
|
export {
|
|
31
29
|
none,
|
|
32
30
|
}
|
|
33
|
-
/*
|
|
31
|
+
/* none Not a pure module */
|
|
@@ -18,12 +18,12 @@ rather than a string a README repeats.
|
|
|
18
18
|
*/
|
|
19
19
|
type t =
|
|
20
20
|
| Geocoding
|
|
21
|
-
|
|
21
|
+
/** Sending a message to a person, reached through `Capabilities.messaging`.
|
|
22
22
|
One need however many channels the platform provisions — which channels
|
|
23
23
|
those are is a runtime answer the provider publishes, not a second
|
|
24
24
|
declaration, because a plugin that named `Sms` would fail a deploy it
|
|
25
25
|
could have run on email. */
|
|
26
|
-
Messaging
|
|
26
|
+
| Messaging
|
|
27
27
|
|
|
28
28
|
/** The spelling carried in `pluginStructure` and in `capabilities.json`.
|
|
29
29
|
Persisted structures hold strings, not enum members, so a plugin built
|
|
@@ -62,7 +62,7 @@ unprovisioned capability it never named degrades exactly as it always has, which
|
|
|
62
62
|
is the modelled outcome for a deployment that simply does not have one.
|
|
63
63
|
*/
|
|
64
64
|
let unmet = (~declared: array<(string, string)>, ~provisioned: array<t>): array<unmet> =>
|
|
65
|
-
declared->Array.filterMap(((
|
|
65
|
+
declared->Array.filterMap(((capability, component)) =>
|
|
66
66
|
switch fromString(capability) {
|
|
67
67
|
| Some(need) if !(provisioned->Array.includes(need)) => Some({need, component})
|
|
68
68
|
| _ => None
|
|
@@ -77,5 +77,4 @@ let unmetMessage = (unmet: array<unmet>): string =>
|
|
|
77
77
|
->Array.map(u => `${toString(u.need)} (${u.component})`)
|
|
78
78
|
->Array.join(", ")}.\n` ++
|
|
79
79
|
` Provision them in the platform stack and redeploy the platform first.\n` ++
|
|
80
|
-
` Without it every call answers Unavailable, the slice exhausts its retries, and a ` ++
|
|
81
|
-
`permanent verdict is recorded against data that is fine — no error anywhere.`
|
|
80
|
+
` Without it every call answers Unavailable, the slice exhausts its retries, and a ` ++ `permanent verdict is recorded against data that is fine — no error anywhere.`
|
|
@@ -59,7 +59,6 @@ value depicts, not in how a reference to it is written. It would carry no
|
|
|
59
59
|
}
|
|
60
60
|
```
|
|
61
61
|
*/
|
|
62
|
-
|
|
63
62
|
/** The record itself. No `@schema`: the derived schema would have to name a
|
|
64
63
|
store, and the store is the host field's — so `forField` is the only way to
|
|
65
64
|
a schema, and a field the ppx did not reach fails to compile rather than
|