@grexx/grexxlinter 0.2.2764 → 0.2.2933
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 +104 -16
- package/lsp.js +18 -1
- package/package.json +1 -1
- package/schemas/mapping.schema.json +15 -0
- package/schemas/metadata.behavior.json +22 -0
- package/schemas/trigger.schema.json +17 -2
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
|
}
|
|
@@ -15659,6 +15696,23 @@ function pushUncovered(push, coverage, casetype) {
|
|
|
15659
15696
|
const n = coverage.uncoveredCount(casetype);
|
|
15660
15697
|
if (n > 0) push("unknown-casetype", "", casetype, `${n} rule clause(s) for "${casetype}" are behaviour-only and not checked by the schema`);
|
|
15661
15698
|
}
|
|
15699
|
+
function isInactive(node) {
|
|
15700
|
+
return !!node && typeof node === "object" && node.inactive === true;
|
|
15701
|
+
}
|
|
15702
|
+
function underInactive(root, pointer) {
|
|
15703
|
+
if (isInactive(root)) return true;
|
|
15704
|
+
let cur = root;
|
|
15705
|
+
for (const raw of pointer.split("/").slice(1)) {
|
|
15706
|
+
if (cur == null || typeof cur !== "object") return false;
|
|
15707
|
+
cur = cur[raw.replaceAll("~1", "/").replaceAll("~0", "~")];
|
|
15708
|
+
if (isInactive(cur)) return true;
|
|
15709
|
+
}
|
|
15710
|
+
return false;
|
|
15711
|
+
}
|
|
15712
|
+
function dropInactive(messages, root) {
|
|
15713
|
+
if (isInactive(root)) return [];
|
|
15714
|
+
return messages.filter((m) => !underInactive(root, m.path));
|
|
15715
|
+
}
|
|
15662
15716
|
function dedupeMessages(messages) {
|
|
15663
15717
|
const seen = /* @__PURE__ */ new Set();
|
|
15664
15718
|
return messages.filter((m) => {
|
|
@@ -15690,7 +15744,7 @@ function lintDocument(ctx, input) {
|
|
|
15690
15744
|
});
|
|
15691
15745
|
}
|
|
15692
15746
|
if (settings.reportUncovered) pushUncovered(push, ctx.coverage, casetype);
|
|
15693
|
-
const deduped = dedupeMessages(messages);
|
|
15747
|
+
const deduped = dropInactive(dedupeMessages(messages), input.value);
|
|
15694
15748
|
const ok = !deduped.some((m) => meetsThreshold(m.severity, settings.failOn));
|
|
15695
15749
|
return { file: input.file, casetype, messages: deduped, skipped: false, ok };
|
|
15696
15750
|
}
|
|
@@ -15733,13 +15787,18 @@ function parseErrorResult(file, message, settings) {
|
|
|
15733
15787
|
messages: [{ severity: "error", rule: "parse-error", casetype: null, path: "", field: null, message, file }]
|
|
15734
15788
|
};
|
|
15735
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
|
+
}
|
|
15736
15795
|
function lintOneDoc(doc, file, ctx, settings, caseRefs) {
|
|
15737
|
-
|
|
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);
|
|
15738
15798
|
const resolved = toJSOrError(doc);
|
|
15739
|
-
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);
|
|
15740
15800
|
const input = { value: resolved.value, file };
|
|
15741
15801
|
const result = lintDocument({ registry: ctx.registry, coverage: ctx.coverage, settings }, input);
|
|
15742
|
-
const repoPath = repoPathOf(ctx.root, file);
|
|
15743
15802
|
const extra = [
|
|
15744
15803
|
...referenceFindings(input.value, file, caseRefs, settings),
|
|
15745
15804
|
...repoPath === void 0 ? [] : layoutFindings(input.value, repoPath, settings).map((m) => ({ ...m, file }))
|
|
@@ -15748,7 +15807,7 @@ function lintOneDoc(doc, file, ctx, settings, caseRefs) {
|
|
|
15748
15807
|
result.messages.push(...extra);
|
|
15749
15808
|
if (result.ok && extra.some((m) => meetsThreshold(m.severity, settings.failOn))) result.ok = false;
|
|
15750
15809
|
}
|
|
15751
|
-
return result;
|
|
15810
|
+
return tagDep(result, repoPath, ctx.depMounts);
|
|
15752
15811
|
}
|
|
15753
15812
|
function lintOneFile(file, ctx, settings, caseRefs) {
|
|
15754
15813
|
const text = readFileSync2(file, "utf8");
|
|
@@ -15769,7 +15828,7 @@ function lintFiles(files, ctx, settings) {
|
|
|
15769
15828
|
// src/cli.ts
|
|
15770
15829
|
var PARALLEL_THRESHOLD = 200;
|
|
15771
15830
|
var MAX_WORKERS = 8;
|
|
15772
|
-
var VERSION = "0.2.
|
|
15831
|
+
var VERSION = "0.2.2933";
|
|
15773
15832
|
function isNewer(latest, current) {
|
|
15774
15833
|
const a = latest.split(".").map(Number);
|
|
15775
15834
|
const b = current.split(".").map(Number);
|
|
@@ -15823,12 +15882,15 @@ var BOOL_FLAGS = /* @__PURE__ */ new Map([
|
|
|
15823
15882
|
["--errors-only", (args) => {
|
|
15824
15883
|
args.errorsOnly = true;
|
|
15825
15884
|
}],
|
|
15885
|
+
["--own-only", (args) => {
|
|
15886
|
+
args.ownOnly = true;
|
|
15887
|
+
}],
|
|
15826
15888
|
["--no-update-check", (args) => {
|
|
15827
15889
|
args.noUpdateCheck = true;
|
|
15828
15890
|
}]
|
|
15829
15891
|
]);
|
|
15830
15892
|
function parseArgs(argv) {
|
|
15831
|
-
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 };
|
|
15832
15894
|
for (let i = 0; i < argv.length; i++) {
|
|
15833
15895
|
const a = argv[i];
|
|
15834
15896
|
const withValue = VALUE_FLAGS.get(a);
|
|
@@ -15885,7 +15947,9 @@ function printResult(r, quiet, errorsOnly) {
|
|
|
15885
15947
|
if (quiet && msgs.length === 0) return;
|
|
15886
15948
|
const rel = r.file ? relative2(process.cwd(), r.file) : "<input>";
|
|
15887
15949
|
const casetypeTag = r.casetype ? ` (${r.casetype})` : "";
|
|
15888
|
-
const
|
|
15950
|
+
const dep = msgs.find((m) => m.dep)?.dep;
|
|
15951
|
+
const depTag = dep ? ` [product: ${dep}]` : "";
|
|
15952
|
+
const head = `${rel}${casetypeTag}${depTag}`;
|
|
15889
15953
|
if (msgs.length === 0) {
|
|
15890
15954
|
if (!quiet) console.log(`${paint("ok", COLORS.info)} ${head}`);
|
|
15891
15955
|
return;
|
|
@@ -15896,8 +15960,18 @@ function printResult(r, quiet, errorsOnly) {
|
|
|
15896
15960
|
console.log(` ${paint(m.severity.padEnd(7), COLORS[m.severity])} ${m.rule.padEnd(16)} ${loc} ${m.message}`);
|
|
15897
15961
|
}
|
|
15898
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
|
+
}
|
|
15899
15972
|
function printSummary(results, settings) {
|
|
15900
|
-
const
|
|
15973
|
+
const active = results.filter((r) => !r.skipped);
|
|
15974
|
+
const s = summarize(active);
|
|
15901
15975
|
const skipped = results.filter((r) => r.skipped).length;
|
|
15902
15976
|
const skippedNote = skipped ? `, ${skipped} skipped` : "";
|
|
15903
15977
|
const errorsText = paint(`${s.errors} error(s)`, COLORS.error);
|
|
@@ -15906,6 +15980,11 @@ function printSummary(results, settings) {
|
|
|
15906
15980
|
`
|
|
15907
15981
|
${s.files} file(s)${skippedNote} \u2014 ${errorsText}, ${warningsText}, ${s.infos} info(s) [fail-on: ${settings.failOn}]`
|
|
15908
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
|
+
}
|
|
15909
15988
|
}
|
|
15910
15989
|
function printText(results, settings, quiet, errorsOnly) {
|
|
15911
15990
|
for (const r of results) printResult(r, quiet, errorsOnly);
|
|
@@ -15944,8 +16023,15 @@ function printReport(results, kind, errorsOnly) {
|
|
|
15944
16023
|
console.log(`${paint(String(r.count).padStart(cw), COLORS[r.severity])} ${r.casetype.padEnd(tw)} ${r.severity.padEnd(8)} ${r.message}`);
|
|
15945
16024
|
}
|
|
15946
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
|
+
}
|
|
15947
16033
|
var WORKER_FILE = fileURLToPath2(import.meta.url);
|
|
15948
|
-
async function runParallel(files, settings, schemasDir, root) {
|
|
16034
|
+
async function runParallel(files, settings, schemasDir, root, depMounts) {
|
|
15949
16035
|
const n = Math.min(MAX_WORKERS, availableParallelism(), files.length);
|
|
15950
16036
|
const shards = Array.from({ length: n }, () => []);
|
|
15951
16037
|
files.forEach((f, i) => shards[i % n].push(f));
|
|
@@ -15954,7 +16040,7 @@ async function runParallel(files, settings, schemasDir, root) {
|
|
|
15954
16040
|
const parts = await Promise.all(
|
|
15955
16041
|
shards.map(
|
|
15956
16042
|
(shard) => new Promise((resolve2, reject) => {
|
|
15957
|
-
const w = new Worker(WORKER_FILE, { workerData: { shard, settings, schemasDir, root } });
|
|
16043
|
+
const w = new Worker(WORKER_FILE, { workerData: { shard, settings, schemasDir, root, depMounts } });
|
|
15958
16044
|
w.once("message", (m) => {
|
|
15959
16045
|
void w.terminate();
|
|
15960
16046
|
resolve2(m);
|
|
@@ -15967,13 +16053,13 @@ async function runParallel(files, settings, schemasDir, root) {
|
|
|
15967
16053
|
} catch (e) {
|
|
15968
16054
|
if (process.env.GREXXLINT_DEBUG) console.error("[grexxlint] worker pool failed, inline fallback:", e);
|
|
15969
16055
|
const ctx = createNodeContext({ schemasDir });
|
|
15970
|
-
return lintFiles(files, { ...ctx, root }, settings);
|
|
16056
|
+
return lintFiles(files, { ...ctx, root, depMounts }, settings);
|
|
15971
16057
|
}
|
|
15972
16058
|
}
|
|
15973
16059
|
function runWorker() {
|
|
15974
|
-
const { shard, settings, schemasDir, root } = workerData;
|
|
16060
|
+
const { shard, settings, schemasDir, root, depMounts } = workerData;
|
|
15975
16061
|
const ctx = createNodeContext({ schemasDir });
|
|
15976
|
-
parentPort.postMessage(lintFiles(shard, { ...ctx, root }, settings));
|
|
16062
|
+
parentPort.postMessage(lintFiles(shard, { ...ctx, root, depMounts }, settings));
|
|
15977
16063
|
}
|
|
15978
16064
|
async function main() {
|
|
15979
16065
|
const args = parseArgs(process.argv.slice(2));
|
|
@@ -15987,14 +16073,16 @@ async function main() {
|
|
|
15987
16073
|
const settings = normalizeSettings(partial);
|
|
15988
16074
|
const files = discover(args.paths, settings.exclude);
|
|
15989
16075
|
const root = layoutRootOf(args.paths);
|
|
16076
|
+
const depMounts = readDepMounts(root);
|
|
15990
16077
|
const canParallel = WORKER_FILE.endsWith(".js") && files.length > PARALLEL_THRESHOLD;
|
|
15991
16078
|
let results;
|
|
15992
16079
|
if (canParallel) {
|
|
15993
|
-
results = await runParallel(files, settings, args.schemasDir, root);
|
|
16080
|
+
results = await runParallel(files, settings, args.schemasDir, root, depMounts);
|
|
15994
16081
|
} else {
|
|
15995
16082
|
const ctx = createNodeContext({ schemasDir: args.schemasDir });
|
|
15996
|
-
results = lintFiles(files, { ...ctx, root }, settings);
|
|
16083
|
+
results = lintFiles(files, { ...ctx, root, depMounts }, settings);
|
|
15997
16084
|
}
|
|
16085
|
+
if (args.ownOnly) results = withoutDepFindings(results, settings);
|
|
15998
16086
|
if (args.report) {
|
|
15999
16087
|
printReport(results, args.report, args.errorsOnly);
|
|
16000
16088
|
} else if (args.format === "json") {
|
package/lsp.js
CHANGED
|
@@ -25608,6 +25608,23 @@ function pushUncovered(push, coverage, casetype) {
|
|
|
25608
25608
|
const n = coverage.uncoveredCount(casetype);
|
|
25609
25609
|
if (n > 0) push("unknown-casetype", "", casetype, `${n} rule clause(s) for "${casetype}" are behaviour-only and not checked by the schema`);
|
|
25610
25610
|
}
|
|
25611
|
+
function isInactive(node) {
|
|
25612
|
+
return !!node && typeof node === "object" && node.inactive === true;
|
|
25613
|
+
}
|
|
25614
|
+
function underInactive(root, pointer) {
|
|
25615
|
+
if (isInactive(root)) return true;
|
|
25616
|
+
let cur = root;
|
|
25617
|
+
for (const raw of pointer.split("/").slice(1)) {
|
|
25618
|
+
if (cur == null || typeof cur !== "object") return false;
|
|
25619
|
+
cur = cur[raw.replaceAll("~1", "/").replaceAll("~0", "~")];
|
|
25620
|
+
if (isInactive(cur)) return true;
|
|
25621
|
+
}
|
|
25622
|
+
return false;
|
|
25623
|
+
}
|
|
25624
|
+
function dropInactive(messages, root) {
|
|
25625
|
+
if (isInactive(root)) return [];
|
|
25626
|
+
return messages.filter((m) => !underInactive(root, m.path));
|
|
25627
|
+
}
|
|
25611
25628
|
function dedupeMessages(messages) {
|
|
25612
25629
|
const seen = /* @__PURE__ */ new Set();
|
|
25613
25630
|
return messages.filter((m) => {
|
|
@@ -25639,7 +25656,7 @@ function lintDocument(ctx2, input) {
|
|
|
25639
25656
|
});
|
|
25640
25657
|
}
|
|
25641
25658
|
if (settings2.reportUncovered) pushUncovered(push, ctx2.coverage, casetype);
|
|
25642
|
-
const deduped = dedupeMessages(messages);
|
|
25659
|
+
const deduped = dropInactive(dedupeMessages(messages), input.value);
|
|
25643
25660
|
const ok = !deduped.some((m) => meetsThreshold(m.severity, settings2.failOn));
|
|
25644
25661
|
return { file: input.file, casetype, messages: deduped, skipped: false, ok };
|
|
25645
25662
|
}
|
package/package.json
CHANGED
|
@@ -240,6 +240,21 @@
|
|
|
240
240
|
}
|
|
241
241
|
}
|
|
242
242
|
},
|
|
243
|
+
{
|
|
244
|
+
"$comment": "A mapping always names a destination. Which field carries it depends on toAttributeType (usersession -> toKey, everything else -> toAttribute), and the generated branches already require toKey for usersession and activityAttribute for taskAttribute -- but nothing requires the ordinary toAttribute, so a mapping with no destination at all linted clean. Zero findings across packages/schemas/def-yamls-142 and apps/backend/test/fixtures/compile-6685 (1837 active mappings).",
|
|
245
|
+
"anyOf": [
|
|
246
|
+
{
|
|
247
|
+
"required": [
|
|
248
|
+
"toAttribute"
|
|
249
|
+
]
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
"required": [
|
|
253
|
+
"toKey"
|
|
254
|
+
]
|
|
255
|
+
}
|
|
256
|
+
]
|
|
257
|
+
},
|
|
243
258
|
{
|
|
244
259
|
"if": {
|
|
245
260
|
"properties": {
|
|
@@ -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",
|
|
@@ -120,7 +120,23 @@
|
|
|
120
120
|
],
|
|
121
121
|
"title": "trigger",
|
|
122
122
|
"type": "object",
|
|
123
|
+
"$comment": "Hand-maintained overlay (deep-merged by scripts/extend.mjs into extended/trigger.schema.json). A trigger always names an activity to run, whatever its `toTriggerType` -- see the allOf below. The manager exports this per toTriggerType value, but the `page` group's `when` is \"[To page] is not «empty» AND [To type] is \\\"page\\\"\", and condToSchema cannot express the not-empty conjunct (`!==` is outside EQ_OPS and `''` is in BAD_VALUES), so that one group is deferred and never reaches the schema. Stating it unconditionally covers `page`, and additionally covers a trigger with no `toTriggerType` at all -- which the per-value groups all miss, since each `if` requires the field to be present. Conditionals generated by packages/schemas/scripts/extend.mjs from rules/trigger.json.",
|
|
123
124
|
"allOf": [
|
|
125
|
+
{
|
|
126
|
+
"$comment": "Zero findings across packages/schemas/def-yamls-142 and apps/backend/test/fixtures/compile-6685 (659 active triggers).",
|
|
127
|
+
"anyOf": [
|
|
128
|
+
{
|
|
129
|
+
"required": [
|
|
130
|
+
"activityTo"
|
|
131
|
+
]
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"required": [
|
|
135
|
+
"activityTag"
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
]
|
|
139
|
+
},
|
|
124
140
|
{
|
|
125
141
|
"if": {
|
|
126
142
|
"properties": {
|
|
@@ -453,6 +469,5 @@
|
|
|
453
469
|
]
|
|
454
470
|
}
|
|
455
471
|
}
|
|
456
|
-
]
|
|
457
|
-
"$comment": "Conditionals generated by packages/schemas/scripts/extend.mjs from rules/trigger.json."
|
|
472
|
+
]
|
|
458
473
|
}
|