@grexx/grexxlinter 0.2.2770 → 0.2.2992
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/cli.js +86 -15
- package/package.json +1 -1
- package/schemas/directRole.schema.json +7 -1
- package/schemas/metadata.behavior.json +22 -0
package/cli.js
CHANGED
|
@@ -15052,6 +15052,35 @@ import { createRequire } from "node:module";
|
|
|
15052
15052
|
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
15053
15053
|
import { fileURLToPath } from "node:url";
|
|
15054
15054
|
|
|
15055
|
+
// src/deps.ts
|
|
15056
|
+
var DEPS_MANIFEST_PATH = "studio.deps.yaml";
|
|
15057
|
+
function defaultDepMount(name) {
|
|
15058
|
+
return `vendor/${name}`;
|
|
15059
|
+
}
|
|
15060
|
+
var TRAILING_SLASHES = /\/{1,256}$/;
|
|
15061
|
+
function depMountsOf(manifest) {
|
|
15062
|
+
const deps = manifest?.dependencies;
|
|
15063
|
+
if (!Array.isArray(deps)) return [];
|
|
15064
|
+
const out = [];
|
|
15065
|
+
for (const entry of deps) {
|
|
15066
|
+
const { name, mount } = entry ?? {};
|
|
15067
|
+
if (typeof name !== "string" || name === "") continue;
|
|
15068
|
+
const raw = typeof mount === "string" && mount !== "" ? mount : defaultDepMount(name);
|
|
15069
|
+
const normalized = raw.replace(TRAILING_SLASHES, "");
|
|
15070
|
+
if (normalized === "") continue;
|
|
15071
|
+
out.push({ name, mount: normalized });
|
|
15072
|
+
}
|
|
15073
|
+
return out;
|
|
15074
|
+
}
|
|
15075
|
+
function depOfPath(path, mounts) {
|
|
15076
|
+
let best;
|
|
15077
|
+
for (const m of mounts) {
|
|
15078
|
+
if (!path.startsWith(`${m.mount}/`)) continue;
|
|
15079
|
+
if (best === void 0 || m.mount.length > best.mount.length) best = m;
|
|
15080
|
+
}
|
|
15081
|
+
return best?.name;
|
|
15082
|
+
}
|
|
15083
|
+
|
|
15055
15084
|
// src/coverage.ts
|
|
15056
15085
|
function condKey(c) {
|
|
15057
15086
|
if (!c) return null;
|
|
@@ -15342,6 +15371,14 @@ function layoutRootOf(paths) {
|
|
|
15342
15371
|
if (paths.length !== 1) return void 0;
|
|
15343
15372
|
return statSync(paths[0]).isDirectory() ? paths[0] : void 0;
|
|
15344
15373
|
}
|
|
15374
|
+
function readDepMounts(root) {
|
|
15375
|
+
if (root === void 0) return [];
|
|
15376
|
+
try {
|
|
15377
|
+
return depMountsOf((0, import_yaml.parse)(readFileSync(join(root, DEPS_MANIFEST_PATH), "utf8")));
|
|
15378
|
+
} catch {
|
|
15379
|
+
return [];
|
|
15380
|
+
}
|
|
15381
|
+
}
|
|
15345
15382
|
function repoPathOf(root, file) {
|
|
15346
15383
|
return root === void 0 ? void 0 : relative(root, file).split(sep).join("/");
|
|
15347
15384
|
}
|
|
@@ -15750,13 +15787,18 @@ function parseErrorResult(file, message, settings) {
|
|
|
15750
15787
|
messages: [{ severity: "error", rule: "parse-error", casetype: null, path: "", field: null, message, file }]
|
|
15751
15788
|
};
|
|
15752
15789
|
}
|
|
15790
|
+
function tagDep(result, repoPath, mounts) {
|
|
15791
|
+
const dep = repoPath === void 0 || mounts === void 0 ? void 0 : depOfPath(repoPath, mounts);
|
|
15792
|
+
if (dep !== void 0) for (const m of result.messages) m.dep = dep;
|
|
15793
|
+
return result;
|
|
15794
|
+
}
|
|
15753
15795
|
function lintOneDoc(doc, file, ctx, settings, caseRefs) {
|
|
15754
|
-
|
|
15796
|
+
const repoPath = repoPathOf(ctx.root, file);
|
|
15797
|
+
if (doc.errors.length > 0) return tagDep(parseErrorResult(file, doc.errors[0].message, settings), repoPath, ctx.depMounts);
|
|
15755
15798
|
const resolved = toJSOrError(doc);
|
|
15756
|
-
if (resolved.error !== void 0) return parseErrorResult(file, resolved.error, settings);
|
|
15799
|
+
if (resolved.error !== void 0) return tagDep(parseErrorResult(file, resolved.error, settings), repoPath, ctx.depMounts);
|
|
15757
15800
|
const input = { value: resolved.value, file };
|
|
15758
15801
|
const result = lintDocument({ registry: ctx.registry, coverage: ctx.coverage, settings }, input);
|
|
15759
|
-
const repoPath = repoPathOf(ctx.root, file);
|
|
15760
15802
|
const extra = [
|
|
15761
15803
|
...referenceFindings(input.value, file, caseRefs, settings),
|
|
15762
15804
|
...repoPath === void 0 ? [] : layoutFindings(input.value, repoPath, settings).map((m) => ({ ...m, file }))
|
|
@@ -15765,7 +15807,7 @@ function lintOneDoc(doc, file, ctx, settings, caseRefs) {
|
|
|
15765
15807
|
result.messages.push(...extra);
|
|
15766
15808
|
if (result.ok && extra.some((m) => meetsThreshold(m.severity, settings.failOn))) result.ok = false;
|
|
15767
15809
|
}
|
|
15768
|
-
return result;
|
|
15810
|
+
return tagDep(result, repoPath, ctx.depMounts);
|
|
15769
15811
|
}
|
|
15770
15812
|
function lintOneFile(file, ctx, settings, caseRefs) {
|
|
15771
15813
|
const text = readFileSync2(file, "utf8");
|
|
@@ -15786,7 +15828,7 @@ function lintFiles(files, ctx, settings) {
|
|
|
15786
15828
|
// src/cli.ts
|
|
15787
15829
|
var PARALLEL_THRESHOLD = 200;
|
|
15788
15830
|
var MAX_WORKERS = 8;
|
|
15789
|
-
var VERSION = "0.2.
|
|
15831
|
+
var VERSION = "0.2.2992";
|
|
15790
15832
|
function isNewer(latest, current) {
|
|
15791
15833
|
const a = latest.split(".").map(Number);
|
|
15792
15834
|
const b = current.split(".").map(Number);
|
|
@@ -15840,12 +15882,15 @@ var BOOL_FLAGS = /* @__PURE__ */ new Map([
|
|
|
15840
15882
|
["--errors-only", (args) => {
|
|
15841
15883
|
args.errorsOnly = true;
|
|
15842
15884
|
}],
|
|
15885
|
+
["--own-only", (args) => {
|
|
15886
|
+
args.ownOnly = true;
|
|
15887
|
+
}],
|
|
15843
15888
|
["--no-update-check", (args) => {
|
|
15844
15889
|
args.noUpdateCheck = true;
|
|
15845
15890
|
}]
|
|
15846
15891
|
]);
|
|
15847
15892
|
function parseArgs(argv) {
|
|
15848
|
-
const args = { paths: [], off: [], format: "text", quiet: false, errorsOnly: false, noUpdateCheck: false };
|
|
15893
|
+
const args = { paths: [], off: [], format: "text", quiet: false, errorsOnly: false, ownOnly: false, noUpdateCheck: false };
|
|
15849
15894
|
for (let i = 0; i < argv.length; i++) {
|
|
15850
15895
|
const a = argv[i];
|
|
15851
15896
|
const withValue = VALUE_FLAGS.get(a);
|
|
@@ -15902,7 +15947,9 @@ function printResult(r, quiet, errorsOnly) {
|
|
|
15902
15947
|
if (quiet && msgs.length === 0) return;
|
|
15903
15948
|
const rel = r.file ? relative2(process.cwd(), r.file) : "<input>";
|
|
15904
15949
|
const casetypeTag = r.casetype ? ` (${r.casetype})` : "";
|
|
15905
|
-
const
|
|
15950
|
+
const dep = msgs.find((m) => m.dep)?.dep;
|
|
15951
|
+
const depTag = dep ? ` [product: ${dep}]` : "";
|
|
15952
|
+
const head = `${rel}${casetypeTag}${depTag}`;
|
|
15906
15953
|
if (msgs.length === 0) {
|
|
15907
15954
|
if (!quiet) console.log(`${paint("ok", COLORS.info)} ${head}`);
|
|
15908
15955
|
return;
|
|
@@ -15913,8 +15960,18 @@ function printResult(r, quiet, errorsOnly) {
|
|
|
15913
15960
|
console.log(` ${paint(m.severity.padEnd(7), COLORS[m.severity])} ${m.rule.padEnd(16)} ${loc} ${m.message}`);
|
|
15914
15961
|
}
|
|
15915
15962
|
}
|
|
15963
|
+
function depFindingCounts(results) {
|
|
15964
|
+
const by = /* @__PURE__ */ new Map();
|
|
15965
|
+
for (const r of results) {
|
|
15966
|
+
for (const m of r.messages) {
|
|
15967
|
+
if (m.dep) by.set(m.dep, (by.get(m.dep) ?? 0) + 1);
|
|
15968
|
+
}
|
|
15969
|
+
}
|
|
15970
|
+
return by;
|
|
15971
|
+
}
|
|
15916
15972
|
function printSummary(results, settings) {
|
|
15917
|
-
const
|
|
15973
|
+
const active = results.filter((r) => !r.skipped);
|
|
15974
|
+
const s = summarize(active);
|
|
15918
15975
|
const skipped = results.filter((r) => r.skipped).length;
|
|
15919
15976
|
const skippedNote = skipped ? `, ${skipped} skipped` : "";
|
|
15920
15977
|
const errorsText = paint(`${s.errors} error(s)`, COLORS.error);
|
|
@@ -15923,6 +15980,11 @@ function printSummary(results, settings) {
|
|
|
15923
15980
|
`
|
|
15924
15981
|
${s.files} file(s)${skippedNote} \u2014 ${errorsText}, ${warningsText}, ${s.infos} info(s) [fail-on: ${settings.failOn}]`
|
|
15925
15982
|
);
|
|
15983
|
+
const byDep = depFindingCounts(active);
|
|
15984
|
+
if (byDep.size > 0) {
|
|
15985
|
+
const total = [...byDep.values()].reduce((a, b) => a + b, 0);
|
|
15986
|
+
console.log(`${total} of those are in ${byDep.size} installed marketplace product(s) \u2014 run with --own-only to drop them`);
|
|
15987
|
+
}
|
|
15926
15988
|
}
|
|
15927
15989
|
function printText(results, settings, quiet, errorsOnly) {
|
|
15928
15990
|
for (const r of results) printResult(r, quiet, errorsOnly);
|
|
@@ -15961,8 +16023,15 @@ function printReport(results, kind, errorsOnly) {
|
|
|
15961
16023
|
console.log(`${paint(String(r.count).padStart(cw), COLORS[r.severity])} ${r.casetype.padEnd(tw)} ${r.severity.padEnd(8)} ${r.message}`);
|
|
15962
16024
|
}
|
|
15963
16025
|
}
|
|
16026
|
+
function withoutDepFindings(results, settings) {
|
|
16027
|
+
return results.map((r) => {
|
|
16028
|
+
const messages = r.messages.filter((m) => !m.dep);
|
|
16029
|
+
if (messages.length === r.messages.length) return r;
|
|
16030
|
+
return { ...r, messages, ok: !messages.some((m) => meetsThreshold(m.severity, settings.failOn)) };
|
|
16031
|
+
});
|
|
16032
|
+
}
|
|
15964
16033
|
var WORKER_FILE = fileURLToPath2(import.meta.url);
|
|
15965
|
-
async function runParallel(files, settings, schemasDir, root) {
|
|
16034
|
+
async function runParallel(files, settings, schemasDir, root, depMounts) {
|
|
15966
16035
|
const n = Math.min(MAX_WORKERS, availableParallelism(), files.length);
|
|
15967
16036
|
const shards = Array.from({ length: n }, () => []);
|
|
15968
16037
|
files.forEach((f, i) => shards[i % n].push(f));
|
|
@@ -15971,7 +16040,7 @@ async function runParallel(files, settings, schemasDir, root) {
|
|
|
15971
16040
|
const parts = await Promise.all(
|
|
15972
16041
|
shards.map(
|
|
15973
16042
|
(shard) => new Promise((resolve2, reject) => {
|
|
15974
|
-
const w = new Worker(WORKER_FILE, { workerData: { shard, settings, schemasDir, root } });
|
|
16043
|
+
const w = new Worker(WORKER_FILE, { workerData: { shard, settings, schemasDir, root, depMounts } });
|
|
15975
16044
|
w.once("message", (m) => {
|
|
15976
16045
|
void w.terminate();
|
|
15977
16046
|
resolve2(m);
|
|
@@ -15984,13 +16053,13 @@ async function runParallel(files, settings, schemasDir, root) {
|
|
|
15984
16053
|
} catch (e) {
|
|
15985
16054
|
if (process.env.GREXXLINT_DEBUG) console.error("[grexxlint] worker pool failed, inline fallback:", e);
|
|
15986
16055
|
const ctx = createNodeContext({ schemasDir });
|
|
15987
|
-
return lintFiles(files, { ...ctx, root }, settings);
|
|
16056
|
+
return lintFiles(files, { ...ctx, root, depMounts }, settings);
|
|
15988
16057
|
}
|
|
15989
16058
|
}
|
|
15990
16059
|
function runWorker() {
|
|
15991
|
-
const { shard, settings, schemasDir, root } = workerData;
|
|
16060
|
+
const { shard, settings, schemasDir, root, depMounts } = workerData;
|
|
15992
16061
|
const ctx = createNodeContext({ schemasDir });
|
|
15993
|
-
parentPort.postMessage(lintFiles(shard, { ...ctx, root }, settings));
|
|
16062
|
+
parentPort.postMessage(lintFiles(shard, { ...ctx, root, depMounts }, settings));
|
|
15994
16063
|
}
|
|
15995
16064
|
async function main() {
|
|
15996
16065
|
const args = parseArgs(process.argv.slice(2));
|
|
@@ -16004,14 +16073,16 @@ async function main() {
|
|
|
16004
16073
|
const settings = normalizeSettings(partial);
|
|
16005
16074
|
const files = discover(args.paths, settings.exclude);
|
|
16006
16075
|
const root = layoutRootOf(args.paths);
|
|
16076
|
+
const depMounts = readDepMounts(root);
|
|
16007
16077
|
const canParallel = WORKER_FILE.endsWith(".js") && files.length > PARALLEL_THRESHOLD;
|
|
16008
16078
|
let results;
|
|
16009
16079
|
if (canParallel) {
|
|
16010
|
-
results = await runParallel(files, settings, args.schemasDir, root);
|
|
16080
|
+
results = await runParallel(files, settings, args.schemasDir, root, depMounts);
|
|
16011
16081
|
} else {
|
|
16012
16082
|
const ctx = createNodeContext({ schemasDir: args.schemasDir });
|
|
16013
|
-
results = lintFiles(files, { ...ctx, root }, settings);
|
|
16083
|
+
results = lintFiles(files, { ...ctx, root, depMounts }, settings);
|
|
16014
16084
|
}
|
|
16085
|
+
if (args.ownOnly) results = withoutDepFindings(results, settings);
|
|
16015
16086
|
if (args.report) {
|
|
16016
16087
|
printReport(results, args.report, args.errorsOnly);
|
|
16017
16088
|
} else if (args.format === "json") {
|
package/package.json
CHANGED
|
@@ -58,6 +58,12 @@
|
|
|
58
58
|
},
|
|
59
59
|
"securityprofile": {
|
|
60
60
|
"$ref": "common.schema.json#/$defs/caseIdOrFileReference"
|
|
61
|
+
},
|
|
62
|
+
"aiAgents": {
|
|
63
|
+
"items": {
|
|
64
|
+
"$ref": "common.schema.json#/$defs/caseIdOrFileReference"
|
|
65
|
+
},
|
|
66
|
+
"type": "array"
|
|
61
67
|
}
|
|
62
68
|
},
|
|
63
69
|
"required": [
|
|
@@ -67,5 +73,5 @@
|
|
|
67
73
|
],
|
|
68
74
|
"title": "directRole",
|
|
69
75
|
"type": "object",
|
|
70
|
-
"$comment": "Conditionals generated by packages/schemas/scripts/extend.mjs from rules/directRole.json."
|
|
76
|
+
"$comment": "Hand-maintained overlay (deep-merged by scripts/extend.mjs into extended/directRole.schema.json). The direct-role form writes aiAgents, but L2's directRole schema does not declare it yet (platformRole does, identically). Declared here until L2 MR !4395 lands and l2-base/ is re-synced; then this file is redundant and should be deleted. Conditionals generated by packages/schemas/scripts/extend.mjs from rules/directRole.json."
|
|
71
77
|
}
|
|
@@ -121,6 +121,17 @@
|
|
|
121
121
|
},
|
|
122
122
|
"cookieConsentEnabled": {
|
|
123
123
|
"baseline": "MUST_NOT",
|
|
124
|
+
"requiredWhen": [
|
|
125
|
+
{
|
|
126
|
+
"op": "cmp",
|
|
127
|
+
"field": "type",
|
|
128
|
+
"fieldName": "Type metadata",
|
|
129
|
+
"operator": "==",
|
|
130
|
+
"value": "cookieconsent",
|
|
131
|
+
"valueIsField": false,
|
|
132
|
+
"isEmpty": false
|
|
133
|
+
}
|
|
134
|
+
],
|
|
124
135
|
"enabledWhen": [
|
|
125
136
|
{
|
|
126
137
|
"op": "cmp",
|
|
@@ -185,6 +196,17 @@
|
|
|
185
196
|
},
|
|
186
197
|
"cookieConsentType": {
|
|
187
198
|
"baseline": "MUST_NOT",
|
|
199
|
+
"requiredWhen": [
|
|
200
|
+
{
|
|
201
|
+
"op": "cmp",
|
|
202
|
+
"field": "type",
|
|
203
|
+
"fieldName": "Type metadata",
|
|
204
|
+
"operator": "==",
|
|
205
|
+
"value": "cookieconsent",
|
|
206
|
+
"valueIsField": false,
|
|
207
|
+
"isEmpty": false
|
|
208
|
+
}
|
|
209
|
+
],
|
|
188
210
|
"enabledWhen": [
|
|
189
211
|
{
|
|
190
212
|
"op": "cmp",
|