canary-test-cli 5.10.1 → 5.11.0
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/bin/canary +0 -0
- package/dist/doctor-manifest.js +22 -0
- package/dist/doctor.js +38 -3
- package/dist/engine-checks.js +5 -3
- package/package.json +1 -1
package/bin/canary
CHANGED
|
Binary file
|
package/dist/doctor-manifest.js
CHANGED
|
@@ -37,6 +37,7 @@ exports.DEFAULT_CHECK_TIMEOUT_MS = exports.CHECK_TYPES = void 0;
|
|
|
37
37
|
exports.manifestPath = manifestPath;
|
|
38
38
|
exports.commandSucceedsHash = commandSucceedsHash;
|
|
39
39
|
exports.loadManifest = loadManifest;
|
|
40
|
+
exports.collectPersonas = collectPersonas;
|
|
40
41
|
exports.filterByPersona = filterByPersona;
|
|
41
42
|
exports.runCheck = runCheck;
|
|
42
43
|
/**
|
|
@@ -185,6 +186,27 @@ function loadManifest(cloneDir) {
|
|
|
185
186
|
}
|
|
186
187
|
return { ok: true, checks };
|
|
187
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* The distinct persona tags declared across a set of checks, in first-seen
|
|
191
|
+
* order and de-duplicated case-insensitively (original casing preserved for
|
|
192
|
+
* display). This is the discoverable persona *vocabulary* — the engine ships
|
|
193
|
+
* none of its own, so it is derived entirely from overlay manifests. Used to
|
|
194
|
+
* tell a user which `--persona` values actually mean something (issue #294).
|
|
195
|
+
*/
|
|
196
|
+
function collectPersonas(checks) {
|
|
197
|
+
const seen = new Set();
|
|
198
|
+
const out = [];
|
|
199
|
+
for (const check of checks) {
|
|
200
|
+
for (const tag of check.persona ?? []) {
|
|
201
|
+
const key = tag.toLowerCase();
|
|
202
|
+
if (!seen.has(key)) {
|
|
203
|
+
seen.add(key);
|
|
204
|
+
out.push(tag);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return out;
|
|
209
|
+
}
|
|
188
210
|
/**
|
|
189
211
|
* Keep checks that should run for `persona`: a null persona runs everything;
|
|
190
212
|
* otherwise keep checks with no persona plus those whose persona list contains
|
package/dist/doctor.js
CHANGED
|
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.unknownPersonaHint = unknownPersonaHint;
|
|
36
37
|
exports.runDoctor = runDoctor;
|
|
37
38
|
/**
|
|
38
39
|
* `canary doctor` — environment self-check (Phase 2).
|
|
@@ -60,6 +61,28 @@ function parsePersona(args) {
|
|
|
60
61
|
}
|
|
61
62
|
return null;
|
|
62
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Fail-loud hint for an unrecognized `--persona` value (issue #294). Returns
|
|
66
|
+
* null when there is nothing to say — no persona was passed, or the passed
|
|
67
|
+
* persona is part of the known vocabulary. Otherwise returns a one-line,
|
|
68
|
+
* actionable message: the engine ships no persona vocabulary, so this lists
|
|
69
|
+
* the tags overlays actually declared (or says none are defined) instead of
|
|
70
|
+
* silently running only the persona-less checks and leaving the user to
|
|
71
|
+
* guess why their filter matched nothing.
|
|
72
|
+
*/
|
|
73
|
+
function unknownPersonaHint(persona, known) {
|
|
74
|
+
if (persona === null) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
const want = persona.toLowerCase();
|
|
78
|
+
if (known.some((p) => p.toLowerCase() === want)) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
if (known.length === 0) {
|
|
82
|
+
return `--persona '${persona}' matched no checks: no overlay defines any personas, so every check already runs. Drop the flag.`;
|
|
83
|
+
}
|
|
84
|
+
return `--persona '${persona}' is not a known persona. Valid options: ${known.join(", ")}. (Omit --persona to run every check.)`;
|
|
85
|
+
}
|
|
63
86
|
function renderCheck(r) {
|
|
64
87
|
const line = ` ${SYMBOL[r.status]} ${r.label}\n`;
|
|
65
88
|
return r.status === "fail" && r.remedy ? `${line} → ${r.remedy}\n` : line;
|
|
@@ -69,7 +92,7 @@ async function overlayResults(entry, deps, persona) {
|
|
|
69
92
|
const header = `Overlay: ${entry.name}`;
|
|
70
93
|
const load = (0, doctor_manifest_js_1.loadManifest)(entry.path);
|
|
71
94
|
if (!load.ok) {
|
|
72
|
-
return { header, results: [load.failure] };
|
|
95
|
+
return { group: { header, results: [load.failure] }, loadedChecks: [] };
|
|
73
96
|
}
|
|
74
97
|
const granted = registry.consentGranted(entry, (0, doctor_manifest_js_1.commandSucceedsHash)(load.checks));
|
|
75
98
|
const checks = (0, doctor_manifest_js_1.filterByPersona)(load.checks, persona);
|
|
@@ -84,7 +107,9 @@ async function overlayResults(entry, deps, persona) {
|
|
|
84
107
|
for (const check of checks) {
|
|
85
108
|
results.push(await (0, doctor_manifest_js_1.runCheck)(check, ctx));
|
|
86
109
|
}
|
|
87
|
-
|
|
110
|
+
// Return the full (pre-filter) check set so the caller can build the
|
|
111
|
+
// persona vocabulary for a fail-loud hint on an unknown --persona.
|
|
112
|
+
return { group: { header, results }, loadedChecks: load.checks };
|
|
88
113
|
}
|
|
89
114
|
/**
|
|
90
115
|
* Run `canary doctor`. Returns a process exit code: 0 when every check passed
|
|
@@ -111,10 +136,20 @@ async function runDoctor(args, deps = {}) {
|
|
|
111
136
|
catch {
|
|
112
137
|
reg = registry.emptyRegistry();
|
|
113
138
|
}
|
|
139
|
+
const allChecks = [];
|
|
114
140
|
for (const entry of reg.overlays) {
|
|
115
|
-
|
|
141
|
+
const { group, loadedChecks } = await overlayResults(entry, deps, persona);
|
|
142
|
+
groups.push(group);
|
|
143
|
+
allChecks.push(...loadedChecks);
|
|
116
144
|
}
|
|
117
145
|
out.write("canary doctor\n");
|
|
146
|
+
// Issue #294: if the user passed a --persona that no overlay declares,
|
|
147
|
+
// tell them the valid vocabulary instead of silently filtering to only
|
|
148
|
+
// the persona-less checks and leaving them to wonder why.
|
|
149
|
+
const personaHint = unknownPersonaHint(persona, (0, doctor_manifest_js_1.collectPersonas)(allChecks));
|
|
150
|
+
if (personaHint) {
|
|
151
|
+
out.write(`\n! ${personaHint}\n`);
|
|
152
|
+
}
|
|
118
153
|
let failures = 0;
|
|
119
154
|
for (const group of groups) {
|
|
120
155
|
out.write(`\n${group.header}\n`);
|
package/dist/engine-checks.js
CHANGED
|
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.parseRegistryVersion = parseRegistryVersion;
|
|
36
37
|
exports.isOlder = isOlder;
|
|
37
38
|
exports.checkVersion = checkVersion;
|
|
38
39
|
exports.checkGit = checkGit;
|
|
@@ -72,10 +73,11 @@ function ownVersion() {
|
|
|
72
73
|
return null;
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
|
-
/**
|
|
76
|
-
function parseRegistryVersion(
|
|
76
|
+
/** String `version` from a registry body; null on parse error or non-string shape (SEC-DES-001: registry JSON is untrusted). */
|
|
77
|
+
function parseRegistryVersion(rawBody) {
|
|
77
78
|
try {
|
|
78
|
-
|
|
79
|
+
const v = JSON.parse(rawBody)?.version;
|
|
80
|
+
return typeof v === "string" ? v : null;
|
|
79
81
|
}
|
|
80
82
|
catch {
|
|
81
83
|
return null;
|