@reventlessdev/reventless-spec 3.0.0-alpha.142 → 3.0.0-alpha.144
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 +21 -0
- package/package.json +2 -2
- package/run-certify-trait.mjs +2 -1
- package/run-generator.mjs +2 -1
- package/run-graft-trait.mjs +2 -1
- package/run-platform-generator.mjs +2 -1
- package/run-trait-manifest.mjs +2 -1
- package/src/CliArgs.res +209 -0
- package/src/CliArgs.res.mjs +325 -0
- package/src/generator/CertifyTrait.res +96 -93
- package/src/generator/CertifyTrait.res.mjs +92 -75
- package/src/generator/GraftTrait.res +147 -148
- package/src/generator/GraftTrait.res.mjs +131 -128
- package/src/generator/PlatformGenerator.res +50 -31
- package/src/generator/PlatformGenerator.res.mjs +107 -76
- package/src/generator/PluginGenerator.res +29 -29
- package/src/generator/PluginGenerator.res.mjs +87 -55
- package/src/generator/TraitManifestCli.res +69 -68
- package/src/generator/TraitManifestCli.res.mjs +67 -51
- package/src/lifecycle/CheckLifecycleModel.res +203 -189
- package/src/lifecycle/CheckLifecycleModel.res.mjs +146 -141
- package/src/types/PrepareAccounts.res +38 -57
- package/src/types/PrepareAccounts.res.mjs +28 -82
|
@@ -83,106 +83,109 @@ let readJson = (path: string) =>
|
|
|
83
83
|
|
|
84
84
|
// ── Entry point ──────────────────────────────────────────────────────────────
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
86
|
+
type args = {traitPackage: string, host: string, report: string, out: string}
|
|
87
|
+
|
|
88
|
+
let parseArgs = (argv: array<string>): result<args, string> =>
|
|
89
|
+
CliArgs.parse(~strings=["host", "report", "out"], argv)
|
|
90
|
+
->Result.flatMap(a => a->CliArgs.atMost(1))
|
|
91
|
+
->Result.flatMap(a =>
|
|
92
|
+
switch (
|
|
93
|
+
a->CliArgs.positionals->Array.get(0),
|
|
94
|
+
a->CliArgs.string("host"),
|
|
95
|
+
a->CliArgs.string("report"),
|
|
96
|
+
a->CliArgs.string("out"),
|
|
97
|
+
) {
|
|
98
|
+
| (None | Some(""), _, _, _) => Error("<trait-package> is required.")
|
|
99
|
+
| (Some(traitPackage), Some(host), Some(report), Some(out)) =>
|
|
100
|
+
Ok({traitPackage, host, report, out})
|
|
101
|
+
| (Some(_), _, _, _) => Error("--host, --report and --out are all required.")
|
|
92
102
|
}
|
|
103
|
+
)
|
|
93
104
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
105
|
+
let cli: CliArgs.cli<args> = {bin: "certify-trait", usage, parse: parseArgs}
|
|
106
|
+
|
|
107
|
+
let main = () =>
|
|
108
|
+
CliArgs.run(cli, async ({traitPackage, host, report: reportPath, out}) => {
|
|
109
|
+
// `@scope/trait-attachments` → `Attachments_Conformance`, the same
|
|
110
|
+
// derivation `graft-trait` does for `_Scaffold`.
|
|
111
|
+
let conformanceModule =
|
|
112
|
+
traitPackage
|
|
113
|
+
->String.split("/")
|
|
114
|
+
->Array.last
|
|
115
|
+
->Option.getOr("")
|
|
116
|
+
->String.replace("trait-", "")
|
|
117
|
+
->String.split("-")
|
|
118
|
+
->Array.map(part =>
|
|
119
|
+
part->String.charAt(0)->String.toUpperCase ++
|
|
120
|
+
part->String.slice(~start=1, ~end=part->String.length)
|
|
121
|
+
)
|
|
122
|
+
->Array.join("") ++ "_Conformance"
|
|
123
|
+
let specifier = `${traitPackage}/src/${conformanceModule}.res.mjs`
|
|
124
|
+
// Resolved from the **caller's** directory: the trait is a dependency of
|
|
125
|
+
// the plugin being certified and deliberately not one of this package.
|
|
126
|
+
let modulePath = try NodeModule.createRequire(
|
|
127
|
+
NodeProcess.cwd() ++ "/index.js",
|
|
128
|
+
)->NodeModule.requireResolve(specifier) catch {
|
|
129
|
+
| _ => specifier
|
|
130
|
+
}
|
|
131
|
+
let conformance: conformanceExports = try await dynImport(
|
|
132
|
+
NodeUrl.pathToFileURL(modulePath)["href"],
|
|
133
|
+
) catch {
|
|
134
|
+
| _ =>
|
|
135
|
+
fail(
|
|
136
|
+
`${traitPackage} ships no conformance suite (looked for ${specifier}).\n` ++ ` A trait without one cannot be certified — there is nothing to prove.`,
|
|
137
|
+
)
|
|
138
|
+
%raw(`undefined`)
|
|
98
139
|
}
|
|
99
|
-
| Some(traitPackage) =>
|
|
100
|
-
switch (flag("host"), flag("report"), flag("out")) {
|
|
101
|
-
| (None, _, _) | (_, None, _) | (_, _, None) =>
|
|
102
|
-
fail("--host, --report and --out are all required.\n\n" ++ usage)
|
|
103
|
-
| (Some(host), Some(reportPath), Some(out)) => {
|
|
104
|
-
// `@scope/trait-attachments` → `Attachments_Conformance`, the same
|
|
105
|
-
// derivation `graft-trait` does for `_Scaffold`.
|
|
106
|
-
let conformanceModule =
|
|
107
|
-
traitPackage
|
|
108
|
-
->String.split("/")
|
|
109
|
-
->Array.last
|
|
110
|
-
->Option.getOr("")
|
|
111
|
-
->String.replace("trait-", "")
|
|
112
|
-
->String.split("-")
|
|
113
|
-
->Array.map(part =>
|
|
114
|
-
part->String.charAt(0)->String.toUpperCase ++
|
|
115
|
-
part->String.slice(~start=1, ~end=part->String.length)
|
|
116
|
-
)
|
|
117
|
-
->Array.join("") ++ "_Conformance"
|
|
118
|
-
let specifier = `${traitPackage}/src/${conformanceModule}.res.mjs`
|
|
119
|
-
// Resolved from the **caller's** directory: the trait is a dependency of
|
|
120
|
-
// the plugin being certified and deliberately not one of this package.
|
|
121
|
-
let modulePath = try NodeModule.createRequire(
|
|
122
|
-
NodeProcess.cwd() ++ "/index.js",
|
|
123
|
-
)->NodeModule.requireResolve(specifier) catch {
|
|
124
|
-
| _ => specifier
|
|
125
|
-
}
|
|
126
|
-
let conformance: conformanceExports = try await dynImport(
|
|
127
|
-
NodeUrl.pathToFileURL(modulePath)["href"],
|
|
128
|
-
) catch {
|
|
129
|
-
| _ =>
|
|
130
|
-
fail(
|
|
131
|
-
`${traitPackage} ships no conformance suite (looked for ${specifier}).\n` ++ ` A trait without one cannot be certified — there is nothing to prove.`,
|
|
132
|
-
)
|
|
133
|
-
%raw(`undefined`)
|
|
134
|
-
}
|
|
135
140
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
141
|
+
let suite = conformance.suiteName(host)
|
|
142
|
+
let parsed = switch readJson(reportPath)->Util_Sury.fromJson(reportSchema) {
|
|
143
|
+
| value => value
|
|
144
|
+
| exception _ =>
|
|
145
|
+
fail(`${reportPath} is not a Jest JSON report (expected \`testResults\`).`)
|
|
146
|
+
%raw(`undefined`)
|
|
147
|
+
}
|
|
143
148
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
let certificate = TraitCertificate.fromReport(
|
|
166
|
-
~trait=traitPackage,
|
|
167
|
-
~traitVersion=resolveVersion(specifier),
|
|
168
|
-
// The framework the suite ran against, read the same way — a trait
|
|
169
|
-
// is certified against a version, never in the abstract.
|
|
170
|
-
~framework=resolveVersion("@reventlessdev/reventless-spec/package.json"),
|
|
171
|
-
~host,
|
|
172
|
-
~suite,
|
|
173
|
-
~results,
|
|
174
|
-
)
|
|
175
|
-
NodeFs.writeFileSync(out, certificate->TraitCertificate.render)
|
|
176
|
-
Console.log(`certify-trait: ${certificate->TraitCertificate.summarize}`)
|
|
177
|
-
Console.log(`Wrote: ${out}`)
|
|
178
|
-
if !(certificate->TraitCertificate.verified) {
|
|
179
|
-
NodeProcess.exit(1)
|
|
180
|
-
}
|
|
149
|
+
switch parsed->assertionsFor(~suite) {
|
|
150
|
+
// The failure worth catching. A green build that never ran the suite
|
|
151
|
+
// looks exactly like one that ran it and passed, so silence is refused
|
|
152
|
+
// rather than certified as zero-of-zero.
|
|
153
|
+
| [] =>
|
|
154
|
+
fail(
|
|
155
|
+
`the report contains no suite titled "${suite}".\n` ++
|
|
156
|
+
` Either the conformance binding was never registered, or ${host} is not the ` ++ `name its Spec declares.`,
|
|
157
|
+
)
|
|
158
|
+
| results => {
|
|
159
|
+
let resolveVersion = specifier =>
|
|
160
|
+
try {
|
|
161
|
+
let entry =
|
|
162
|
+
NodeModule.createRequire(NodeProcess.cwd() ++ "/index.js")->NodeModule.requireResolve(
|
|
163
|
+
specifier,
|
|
164
|
+
)
|
|
165
|
+
PackageVersion.fromModuleUrl(NodeUrl.pathToFileURL(entry)["href"])
|
|
166
|
+
} catch {
|
|
167
|
+
| _ => "0.0.0"
|
|
181
168
|
}
|
|
169
|
+
|
|
170
|
+
let certificate = TraitCertificate.fromReport(
|
|
171
|
+
~trait=traitPackage,
|
|
172
|
+
~traitVersion=resolveVersion(specifier),
|
|
173
|
+
// The framework the suite ran against, read the same way — a trait
|
|
174
|
+
// is certified against a version, never in the abstract.
|
|
175
|
+
~framework=resolveVersion("@reventlessdev/reventless-spec/package.json"),
|
|
176
|
+
~host,
|
|
177
|
+
~suite,
|
|
178
|
+
~results,
|
|
179
|
+
)
|
|
180
|
+
NodeFs.writeFileSync(out, certificate->TraitCertificate.render)
|
|
181
|
+
Console.log(`certify-trait: ${certificate->TraitCertificate.summarize}`)
|
|
182
|
+
Console.log(`Wrote: ${out}`)
|
|
183
|
+
if !(certificate->TraitCertificate.verified) {
|
|
184
|
+
NodeProcess.exit(1)
|
|
182
185
|
}
|
|
183
186
|
}
|
|
184
187
|
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
188
|
+
})
|
|
187
189
|
|
|
188
|
-
main
|
|
190
|
+
// No top-level call: `../../run-certify-trait.mjs` invokes [main], so a test can
|
|
191
|
+
// import this module.
|
|
@@ -6,6 +6,8 @@ import * as Nodeurl from "node:url";
|
|
|
6
6
|
import * as Nodemodule from "node:module";
|
|
7
7
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
8
8
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
9
|
+
import * as Stdlib_Result from "@rescript/runtime/lib/es6/Stdlib_Result.js";
|
|
10
|
+
import * as CliArgs$Reventless from "../CliArgs.res.mjs";
|
|
9
11
|
import * as Util_Sury$Reventless from "../util/Util_Sury.res.mjs";
|
|
10
12
|
import * as PackageVersion$Reventless from "../PackageVersion.res.mjs";
|
|
11
13
|
import * as TraitCertificate$Reventless from "../components/TraitCertificate.res.mjs";
|
|
@@ -60,86 +62,99 @@ function readJson(path) {
|
|
|
60
62
|
}
|
|
61
63
|
}
|
|
62
64
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (match === undefined) {
|
|
83
|
-
return fail("--host, --report and --out are all required.\n\n" + usage);
|
|
84
|
-
}
|
|
85
|
-
if (match$1 === undefined) {
|
|
86
|
-
return fail("--host, --report and --out are all required.\n\n" + usage);
|
|
87
|
-
}
|
|
88
|
-
if (match$2 === undefined) {
|
|
89
|
-
return fail("--host, --report and --out are all required.\n\n" + usage);
|
|
90
|
-
}
|
|
91
|
-
let conformanceModule = Stdlib_Option.getOr(Stdlib_Array.last(traitPackage.split("/")), "").replace("trait-", "").split("-").map(part => part.charAt(0).toUpperCase() + part.slice(1, part.length)).join("") + "_Conformance";
|
|
92
|
-
let specifier = traitPackage + `/src/` + conformanceModule + `.res.mjs`;
|
|
93
|
-
let modulePath;
|
|
94
|
-
try {
|
|
95
|
-
modulePath = Nodemodule.createRequire(process.cwd() + "/index.js").resolve(specifier);
|
|
96
|
-
} catch (exn) {
|
|
97
|
-
modulePath = specifier;
|
|
98
|
-
}
|
|
99
|
-
let conformance;
|
|
100
|
-
try {
|
|
101
|
-
conformance = await import(Nodeurl.pathToFileURL(modulePath).href);
|
|
102
|
-
} catch (exn$1) {
|
|
103
|
-
fail(traitPackage + ` ships no conformance suite (looked for ` + specifier + `).\n A trait without one cannot be certified — there is nothing to prove.`);
|
|
104
|
-
conformance = undefined;
|
|
105
|
-
}
|
|
106
|
-
let suite = conformance.suiteName(match);
|
|
107
|
-
let parsed;
|
|
108
|
-
try {
|
|
109
|
-
parsed = Util_Sury$Reventless.fromJson(readJson(match$1), reportSchema);
|
|
110
|
-
} catch (exn$2) {
|
|
111
|
-
fail(match$1 + ` is not a Jest JSON report (expected \`testResults\`).`);
|
|
112
|
-
parsed = undefined;
|
|
113
|
-
}
|
|
114
|
-
let results = assertionsFor(parsed, suite);
|
|
115
|
-
if (results.length === 0) {
|
|
116
|
-
return fail(`the report contains no suite titled "` + suite + `".\n` + (` Either the conformance binding was never registered, or ` + match + ` is not the `) + `name its Spec declares.`);
|
|
117
|
-
}
|
|
118
|
-
let resolveVersion = specifier => {
|
|
119
|
-
try {
|
|
120
|
-
let entry = Nodemodule.createRequire(process.cwd() + "/index.js").resolve(specifier);
|
|
121
|
-
return PackageVersion$Reventless.fromModuleUrl(Nodeurl.pathToFileURL(entry).href);
|
|
122
|
-
} catch (exn) {
|
|
123
|
-
return "0.0.0";
|
|
65
|
+
function parseArgs(argv) {
|
|
66
|
+
return Stdlib_Result.flatMap(Stdlib_Result.flatMap(CliArgs$Reventless.parse([
|
|
67
|
+
"host",
|
|
68
|
+
"report",
|
|
69
|
+
"out"
|
|
70
|
+
], undefined, undefined, undefined, argv), a => CliArgs$Reventless.atMost(a, 1)), a => {
|
|
71
|
+
let match = CliArgs$Reventless.positionals(a)[0];
|
|
72
|
+
let match$1 = CliArgs$Reventless.string(a, "host");
|
|
73
|
+
let match$2 = CliArgs$Reventless.string(a, "report");
|
|
74
|
+
let match$3 = CliArgs$Reventless.string(a, "out");
|
|
75
|
+
if (match !== undefined && match !== "") {
|
|
76
|
+
if (match$1 !== undefined && match$2 !== undefined && match$3 !== undefined) {
|
|
77
|
+
return {
|
|
78
|
+
TAG: "Ok",
|
|
79
|
+
_0: {
|
|
80
|
+
traitPackage: match,
|
|
81
|
+
host: match$1,
|
|
82
|
+
report: match$2,
|
|
83
|
+
out: match$3
|
|
124
84
|
}
|
|
125
85
|
};
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
86
|
+
} else {
|
|
87
|
+
return {
|
|
88
|
+
TAG: "Error",
|
|
89
|
+
_0: "--host, --report and --out are all required."
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
return {
|
|
94
|
+
TAG: "Error",
|
|
95
|
+
_0: "<trait-package> is required."
|
|
96
|
+
};
|
|
136
97
|
}
|
|
137
|
-
}
|
|
138
|
-
console.log(usage);
|
|
139
|
-
process.exit(argv.length === 0 ? 1 : 0);
|
|
98
|
+
});
|
|
140
99
|
}
|
|
141
100
|
|
|
142
|
-
|
|
101
|
+
let cli = {
|
|
102
|
+
bin: "certify-trait",
|
|
103
|
+
usage: usage,
|
|
104
|
+
parse: parseArgs
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
function main() {
|
|
108
|
+
return CliArgs$Reventless.run(cli, undefined, undefined, async param => {
|
|
109
|
+
let out = param.out;
|
|
110
|
+
let reportPath = param.report;
|
|
111
|
+
let host = param.host;
|
|
112
|
+
let traitPackage = param.traitPackage;
|
|
113
|
+
let conformanceModule = Stdlib_Option.getOr(Stdlib_Array.last(traitPackage.split("/")), "").replace("trait-", "").split("-").map(part => part.charAt(0).toUpperCase() + part.slice(1, part.length)).join("") + "_Conformance";
|
|
114
|
+
let specifier = traitPackage + `/src/` + conformanceModule + `.res.mjs`;
|
|
115
|
+
let modulePath;
|
|
116
|
+
try {
|
|
117
|
+
modulePath = Nodemodule.createRequire(process.cwd() + "/index.js").resolve(specifier);
|
|
118
|
+
} catch (exn) {
|
|
119
|
+
modulePath = specifier;
|
|
120
|
+
}
|
|
121
|
+
let conformance;
|
|
122
|
+
try {
|
|
123
|
+
conformance = await import(Nodeurl.pathToFileURL(modulePath).href);
|
|
124
|
+
} catch (exn$1) {
|
|
125
|
+
fail(traitPackage + ` ships no conformance suite (looked for ` + specifier + `).\n A trait without one cannot be certified — there is nothing to prove.`);
|
|
126
|
+
conformance = undefined;
|
|
127
|
+
}
|
|
128
|
+
let suite = conformance.suiteName(host);
|
|
129
|
+
let parsed;
|
|
130
|
+
try {
|
|
131
|
+
parsed = Util_Sury$Reventless.fromJson(readJson(reportPath), reportSchema);
|
|
132
|
+
} catch (exn$2) {
|
|
133
|
+
fail(reportPath + ` is not a Jest JSON report (expected \`testResults\`).`);
|
|
134
|
+
parsed = undefined;
|
|
135
|
+
}
|
|
136
|
+
let results = assertionsFor(parsed, suite);
|
|
137
|
+
if (results.length === 0) {
|
|
138
|
+
return fail(`the report contains no suite titled "` + suite + `".\n` + (` Either the conformance binding was never registered, or ` + host + ` is not the `) + `name its Spec declares.`);
|
|
139
|
+
}
|
|
140
|
+
let resolveVersion = specifier => {
|
|
141
|
+
try {
|
|
142
|
+
let entry = Nodemodule.createRequire(process.cwd() + "/index.js").resolve(specifier);
|
|
143
|
+
return PackageVersion$Reventless.fromModuleUrl(Nodeurl.pathToFileURL(entry).href);
|
|
144
|
+
} catch (exn) {
|
|
145
|
+
return "0.0.0";
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
let certificate = TraitCertificate$Reventless.fromReport(traitPackage, resolveVersion(specifier), resolveVersion("@reventlessdev/reventless-spec/package.json"), host, suite, results);
|
|
149
|
+
Nodefs.writeFileSync(out, TraitCertificate$Reventless.render(certificate), "utf8");
|
|
150
|
+
console.log(`certify-trait: ` + TraitCertificate$Reventless.summarize(certificate));
|
|
151
|
+
console.log(`Wrote: ` + out);
|
|
152
|
+
if (!TraitCertificate$Reventless.verified(certificate)) {
|
|
153
|
+
process.exit(1);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}
|
|
143
158
|
|
|
144
159
|
export {
|
|
145
160
|
fail,
|
|
@@ -149,6 +164,8 @@ export {
|
|
|
149
164
|
reportSchema,
|
|
150
165
|
assertionsFor,
|
|
151
166
|
readJson,
|
|
167
|
+
parseArgs,
|
|
168
|
+
cli,
|
|
152
169
|
main,
|
|
153
170
|
}
|
|
154
171
|
/* reportAssertionSchema Not a pure module */
|