@reventlessdev/reventless-spec 3.0.0-alpha.135 → 3.0.0-alpha.136
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 +18 -0
- package/package.json +2 -2
- package/src/components/DcbScopeInference.res +224 -73
- package/src/components/DcbScopeInference.res.mjs +187 -49
- package/src/components/DcbTag.res +142 -186
- package/src/components/DcbTag.res.mjs +137 -128
- package/src/components/DcbValidation.res +50 -0
- package/src/components/DcbValidation.res.mjs +51 -0
- package/src/generator/Codegen.res +2 -1
- package/src/generator/Codegen.res.mjs +1 -1
- package/src/lifecycle/CheckLifecycleModel.res +114 -4
- package/src/lifecycle/CheckLifecycleModel.res.mjs +148 -4
- package/src/types/AccountsManifest.res +6 -0
- package/src/types/AccountsManifest.res.mjs +2 -1
|
@@ -489,6 +489,109 @@ function deriveCommands(component, observations, labelled) {
|
|
|
489
489
|
});
|
|
490
490
|
}
|
|
491
491
|
|
|
492
|
+
function shownOutcomeOf(s) {
|
|
493
|
+
let match = s.thenKind;
|
|
494
|
+
switch (match) {
|
|
495
|
+
case "error" :
|
|
496
|
+
case "event" :
|
|
497
|
+
break;
|
|
498
|
+
case "noEvent" :
|
|
499
|
+
return [
|
|
500
|
+
"noEvent",
|
|
501
|
+
[]
|
|
502
|
+
];
|
|
503
|
+
default:
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
return [
|
|
507
|
+
s.thenKind,
|
|
508
|
+
s.thenElements.map(e => e.name)
|
|
509
|
+
];
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function byPluginComponentCommand(xs) {
|
|
513
|
+
return xs.toSorted((a, b) => {
|
|
514
|
+
let c = Primitive_string.compare(a.plugin, b.plugin);
|
|
515
|
+
if (c !== 0) {
|
|
516
|
+
return c;
|
|
517
|
+
}
|
|
518
|
+
let c$1 = Primitive_string.compare(a.component, b.component);
|
|
519
|
+
if (c$1 !== 0) {
|
|
520
|
+
return c$1;
|
|
521
|
+
} else {
|
|
522
|
+
return Primitive_string.compare(a.command, b.command);
|
|
523
|
+
}
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
function commandOutcomes(plugin, corpora) {
|
|
528
|
+
let entries = [];
|
|
529
|
+
corpora.forEach(c => {
|
|
530
|
+
c.scenarios.forEach(s => {
|
|
531
|
+
let match = s.whenKind;
|
|
532
|
+
let match$1 = s.whenElements[0];
|
|
533
|
+
let match$2 = shownOutcomeOf(s);
|
|
534
|
+
if (match !== "command") {
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
if (match$1 === undefined) {
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
if (match$2 === undefined) {
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
let names = match$2[1];
|
|
544
|
+
let kind = match$2[0];
|
|
545
|
+
let e = entries.find(e => {
|
|
546
|
+
if (e.component === c.component) {
|
|
547
|
+
return e.command === match$1.name;
|
|
548
|
+
} else {
|
|
549
|
+
return false;
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
let entry;
|
|
553
|
+
if (e !== undefined) {
|
|
554
|
+
entry = e;
|
|
555
|
+
} else {
|
|
556
|
+
let e_component = c.component;
|
|
557
|
+
let e_command = match$1.name;
|
|
558
|
+
let e_outcomes = [];
|
|
559
|
+
let e$1 = {
|
|
560
|
+
plugin: plugin,
|
|
561
|
+
component: e_component,
|
|
562
|
+
command: e_command,
|
|
563
|
+
outcomes: e_outcomes
|
|
564
|
+
};
|
|
565
|
+
entries.push(e$1);
|
|
566
|
+
entry = e$1;
|
|
567
|
+
}
|
|
568
|
+
let o = entry.outcomes.find(o => {
|
|
569
|
+
if (o.kind === kind) {
|
|
570
|
+
return Primitive_object.equal(o.names, names);
|
|
571
|
+
} else {
|
|
572
|
+
return false;
|
|
573
|
+
}
|
|
574
|
+
});
|
|
575
|
+
if (o !== undefined) {
|
|
576
|
+
if (!o.scenarios.includes(s.title)) {
|
|
577
|
+
o.scenarios.push(s.title);
|
|
578
|
+
return;
|
|
579
|
+
} else {
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
} else {
|
|
583
|
+
entry.outcomes.push({
|
|
584
|
+
kind: kind,
|
|
585
|
+
names: names,
|
|
586
|
+
scenarios: [s.title]
|
|
587
|
+
});
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
});
|
|
592
|
+
return byPluginComponentCommand(entries);
|
|
593
|
+
}
|
|
594
|
+
|
|
492
595
|
function allUnverified(cmd, add, why) {
|
|
493
596
|
let states = cmd.allowedStates;
|
|
494
597
|
if (states !== undefined && states.length !== 0) {
|
|
@@ -659,7 +762,7 @@ function isWritablePath(path) {
|
|
|
659
762
|
}
|
|
660
763
|
}
|
|
661
764
|
|
|
662
|
-
async function runPlugin(plugin, pluginDir, findings, opaque) {
|
|
765
|
+
async function runPlugin(plugin, pluginDir, findings, opaque, outcomes) {
|
|
663
766
|
let msg = await readDeclared(pluginDir);
|
|
664
767
|
if (msg.TAG !== "Ok") {
|
|
665
768
|
return {
|
|
@@ -669,6 +772,7 @@ async function runPlugin(plugin, pluginDir, findings, opaque) {
|
|
|
669
772
|
}
|
|
670
773
|
let declared = msg._0;
|
|
671
774
|
let corpora = Stdlib_Array.filterMap(filesUnder(Nodepath.join(pluginDir, "tests"), ".gwt.json"), readCorpus);
|
|
775
|
+
outcomes.push(...commandOutcomes(plugin, corpora));
|
|
672
776
|
corpora.forEach(c => {
|
|
673
777
|
let unreadable = c.scenarios.filter(s => s.whenElements.length === 0);
|
|
674
778
|
if (unreadable.length !== 0) {
|
|
@@ -805,7 +909,7 @@ function goldenPath(root) {
|
|
|
805
909
|
return Nodepath.join(root.dir, "schema", "lifecycle-model.json");
|
|
806
910
|
}
|
|
807
911
|
|
|
808
|
-
function reportJson(findings, opaque, derived, failures) {
|
|
912
|
+
function reportJson(findings, opaque, derived, outcomes, failures) {
|
|
809
913
|
let findingJson = f => Object.fromEntries([
|
|
810
914
|
[
|
|
811
915
|
"verdict",
|
|
@@ -891,6 +995,38 @@ function reportJson(findings, opaque, derived, failures) {
|
|
|
891
995
|
o.unreadable
|
|
892
996
|
]
|
|
893
997
|
]);
|
|
998
|
+
let shownOutcomeJson = o => Object.fromEntries([
|
|
999
|
+
[
|
|
1000
|
+
"kind",
|
|
1001
|
+
o.kind
|
|
1002
|
+
],
|
|
1003
|
+
[
|
|
1004
|
+
"names",
|
|
1005
|
+
o.names.map(prim => prim)
|
|
1006
|
+
],
|
|
1007
|
+
[
|
|
1008
|
+
"scenarios",
|
|
1009
|
+
o.scenarios.map(prim => prim)
|
|
1010
|
+
]
|
|
1011
|
+
]);
|
|
1012
|
+
let commandOutcomesJson = e => Object.fromEntries([
|
|
1013
|
+
[
|
|
1014
|
+
"plugin",
|
|
1015
|
+
e.plugin
|
|
1016
|
+
],
|
|
1017
|
+
[
|
|
1018
|
+
"component",
|
|
1019
|
+
e.component
|
|
1020
|
+
],
|
|
1021
|
+
[
|
|
1022
|
+
"command",
|
|
1023
|
+
e.command
|
|
1024
|
+
],
|
|
1025
|
+
[
|
|
1026
|
+
"outcomes",
|
|
1027
|
+
e.outcomes.map(shownOutcomeJson)
|
|
1028
|
+
]
|
|
1029
|
+
]);
|
|
894
1030
|
return JSON.stringify(Object.fromEntries([
|
|
895
1031
|
[
|
|
896
1032
|
"version",
|
|
@@ -904,6 +1040,10 @@ function reportJson(findings, opaque, derived, failures) {
|
|
|
904
1040
|
"commands",
|
|
905
1041
|
derived.map(commandJson)
|
|
906
1042
|
],
|
|
1043
|
+
[
|
|
1044
|
+
"outcomes",
|
|
1045
|
+
byPluginComponentCommand(outcomes).map(commandOutcomesJson)
|
|
1046
|
+
],
|
|
907
1047
|
[
|
|
908
1048
|
"opaque",
|
|
909
1049
|
opaque.map(opaqueJson)
|
|
@@ -995,6 +1135,7 @@ function writeOrCompare(path, actual, label, drifted) {
|
|
|
995
1135
|
async function main() {
|
|
996
1136
|
let findings = [];
|
|
997
1137
|
let opaque = [];
|
|
1138
|
+
let outcomes = [];
|
|
998
1139
|
let failures = [];
|
|
999
1140
|
let drifted = [];
|
|
1000
1141
|
let allDerived = [];
|
|
@@ -1024,7 +1165,7 @@ async function main() {
|
|
|
1024
1165
|
if (pluginDir !== undefined) {
|
|
1025
1166
|
let plugin = Nodepath.basename(pluginDir);
|
|
1026
1167
|
let qualified = example + `/` + plugin;
|
|
1027
|
-
let commands = await runPlugin(qualified, pluginDir, findings, opaque);
|
|
1168
|
+
let commands = await runPlugin(qualified, pluginDir, findings, opaque, outcomes);
|
|
1028
1169
|
if (commands.TAG === "Ok") {
|
|
1029
1170
|
let commands$1 = commands._0;
|
|
1030
1171
|
commands$1.forEach(c => {
|
|
@@ -1057,7 +1198,7 @@ async function main() {
|
|
|
1057
1198
|
let of_ = severity => findings.filter(f => f.severity === severity);
|
|
1058
1199
|
let contradicted = of_("contradicted");
|
|
1059
1200
|
if (json) {
|
|
1060
|
-
console.log(reportJson(findings, opaque, allDerived, failures));
|
|
1201
|
+
console.log(reportJson(findings, opaque, allDerived, outcomes, failures));
|
|
1061
1202
|
} else {
|
|
1062
1203
|
[
|
|
1063
1204
|
"contradicted",
|
|
@@ -1131,6 +1272,9 @@ export {
|
|
|
1131
1272
|
sameRow,
|
|
1132
1273
|
observe,
|
|
1133
1274
|
deriveCommands,
|
|
1275
|
+
shownOutcomeOf,
|
|
1276
|
+
byPluginComponentCommand,
|
|
1277
|
+
commandOutcomes,
|
|
1134
1278
|
allUnverified,
|
|
1135
1279
|
compare,
|
|
1136
1280
|
pluginDirsIn,
|
|
@@ -27,6 +27,11 @@ provisioned" on both platforms.
|
|
|
27
27
|
`userId` is what the platform stamps on rows this account writes. Locally it
|
|
28
28
|
defaults to the username; on AWS it is the `sub` the pool minted, so it can only
|
|
29
29
|
be filled in after the account exists.
|
|
30
|
+
|
|
31
|
+
`demoOwner` names the person this account plays in a demo's data, for a seed to
|
|
32
|
+
find it by. It exists because the name a pool accepts is not always the demo's:
|
|
33
|
+
a pool that signs in on an email address takes `shopper@example.com`, not
|
|
34
|
+
`shopper`.
|
|
30
35
|
*/
|
|
31
36
|
@schema
|
|
32
37
|
type entry = {
|
|
@@ -34,6 +39,7 @@ type entry = {
|
|
|
34
39
|
password: string,
|
|
35
40
|
groups: array<string>,
|
|
36
41
|
userId?: string,
|
|
42
|
+
demoOwner?: string,
|
|
37
43
|
}
|
|
38
44
|
|
|
39
45
|
let entriesSchema = S.array(entrySchema)
|
|
@@ -15,7 +15,8 @@ let entrySchema = Sury.$schema(s => ({
|
|
|
15
15
|
username: s.m(Sury.string),
|
|
16
16
|
password: s.m(Sury.string),
|
|
17
17
|
groups: s.m(Sury.array(Sury.string)),
|
|
18
|
-
userId: s.m(Sury.$option(Sury.string))
|
|
18
|
+
userId: s.m(Sury.$option(Sury.string)),
|
|
19
|
+
demoOwner: s.m(Sury.$option(Sury.string))
|
|
19
20
|
}));
|
|
20
21
|
|
|
21
22
|
let entriesSchema = Sury.array(entrySchema);
|