connections-arkitect 0.3.4
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/README.md +104 -0
- package/arkitect.config.example.json +10 -0
- package/bin/arkitect.mjs +135 -0
- package/package.json +42 -0
- package/spine/justify-existence.example.json +8 -0
- package/src/checks/agnostic/rules-docs.mjs +92 -0
- package/src/checks/code/dependency-freshness.mjs +82 -0
- package/src/checks/code/no-secrets-committed.mjs +112 -0
- package/src/checks/code/oversized-files.mjs +74 -0
- package/src/checks/code/sibling-consensus.mjs +69 -0
- package/src/checks/code/tsconfig-conformance.mjs +98 -0
- package/src/checks/code/veteran-would-mock.mjs +699 -0
- package/src/checks/hosted/db-justify-existence.mjs +237 -0
- package/src/checks/hosted/db-required-roles.mjs +76 -0
- package/src/core/config.mjs +50 -0
- package/src/core/discovery.mjs +41 -0
- package/src/core/finding.mjs +40 -0
- package/src/core/fs-walk.mjs +24 -0
- package/src/core/hosted/judge.mjs +42 -0
- package/src/core/hosted/spine.mjs +63 -0
- package/src/core/hosted/vault-exec.mjs +61 -0
- package/src/core/index.mjs +15 -0
- package/src/core/init.mjs +64 -0
- package/src/core/oracle/family-conformance.mjs +77 -0
- package/src/core/project-detect.mjs +126 -0
- package/src/core/runner.mjs +369 -0
- package/src/core/sarif.mjs +63 -0
- package/src/engines/surface/surface-size-coverage-engine.mjs +199 -0
- package/src/update/self-update.mjs +90 -0
- package/your-checks/README.md +33 -0
- package/your-checks/code/example-check.mjs +24 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// CORE check — generic. Advisory by default (surfaces debt without blocking a fork whose files are
|
|
2
|
+
// already large). A user can flip it to gating in arkitect.config.json.
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { relative, extname } from "node:path";
|
|
5
|
+
import { createFinding } from "../../core/finding.mjs";
|
|
6
|
+
import { walkFiles } from "../../core/fs-walk.mjs";
|
|
7
|
+
|
|
8
|
+
const CODE_EXT = new Set([
|
|
9
|
+
".ts",
|
|
10
|
+
".tsx",
|
|
11
|
+
".js",
|
|
12
|
+
".jsx",
|
|
13
|
+
".mjs",
|
|
14
|
+
".cjs",
|
|
15
|
+
".py",
|
|
16
|
+
".rs",
|
|
17
|
+
".go",
|
|
18
|
+
".rb",
|
|
19
|
+
".java",
|
|
20
|
+
".php",
|
|
21
|
+
".vue",
|
|
22
|
+
".css",
|
|
23
|
+
".scss",
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
export const audit = {
|
|
27
|
+
id: "oversized-files",
|
|
28
|
+
title: "Oversized source files",
|
|
29
|
+
category: "maintainability",
|
|
30
|
+
domain: "code",
|
|
31
|
+
requires: {},
|
|
32
|
+
gating: false,
|
|
33
|
+
defaultConfig: { warnLines: 800, errorLines: 2500 },
|
|
34
|
+
async run(ctx) {
|
|
35
|
+
const { root, checkConfig } = ctx;
|
|
36
|
+
const findings = [];
|
|
37
|
+
for (const file of walkFiles(root)) {
|
|
38
|
+
if (!CODE_EXT.has(extname(file))) continue;
|
|
39
|
+
let lines;
|
|
40
|
+
try {
|
|
41
|
+
lines = readFileSync(file, "utf8").split("\n").length;
|
|
42
|
+
} catch {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (lines >= checkConfig.errorLines)
|
|
46
|
+
findings.push(
|
|
47
|
+
createFinding({
|
|
48
|
+
id: "oversized",
|
|
49
|
+
title: "File far too large",
|
|
50
|
+
severity: "error",
|
|
51
|
+
file: relative(root, file),
|
|
52
|
+
line: lines,
|
|
53
|
+
message: `${lines} lines (≥ ${checkConfig.errorLines})`,
|
|
54
|
+
}),
|
|
55
|
+
);
|
|
56
|
+
else if (lines >= checkConfig.warnLines)
|
|
57
|
+
findings.push(
|
|
58
|
+
createFinding({
|
|
59
|
+
id: "oversized",
|
|
60
|
+
title: "Large file",
|
|
61
|
+
severity: "warning",
|
|
62
|
+
file: relative(root, file),
|
|
63
|
+
line: lines,
|
|
64
|
+
message: `${lines} lines (≥ ${checkConfig.warnLines})`,
|
|
65
|
+
}),
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
const errors = findings.filter((f) => f.severity === "error").length;
|
|
69
|
+
const report = findings.length
|
|
70
|
+
? `# Oversized files\n\n${findings.map((f) => `- ${f.severity.toUpperCase()} ${f.file} — ${f.message}`).join("\n")}\n\nErrors: ${errors}\nWarnings: ${findings.length - errors}\n`
|
|
71
|
+
: "";
|
|
72
|
+
return { failed: errors > 0, findings, report };
|
|
73
|
+
},
|
|
74
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// CORE check — the CONFORMANCE ORACLE (consensus mode), distilled to a generic, project-agnostic shape.
|
|
2
|
+
// A denylist enumerates known-bad and is forever one bug behind. An oracle declares what AGREEMENT looks
|
|
3
|
+
// like across a FAMILY of sibling artifacts and flags the odd-one-out — catching divergence nobody named.
|
|
4
|
+
// Default family: every package.json; default fingerprint: the `license` field. Both are configurable.
|
|
5
|
+
// Refactored to delegate consensus logic to runFamilyConformance (keeps identical behavior + output).
|
|
6
|
+
import { readFileSync } from "node:fs";
|
|
7
|
+
import { relative, basename } from "node:path";
|
|
8
|
+
import { createFinding } from "../../core/finding.mjs";
|
|
9
|
+
import { walkFiles } from "../../core/fs-walk.mjs";
|
|
10
|
+
import { runFamilyConformance } from "../../core/oracle/family-conformance.mjs";
|
|
11
|
+
|
|
12
|
+
function getPath(obj, path) {
|
|
13
|
+
return path.split(".").reduce((o, k) => (o == null ? undefined : o[k]), obj);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const audit = {
|
|
17
|
+
id: "sibling-consensus",
|
|
18
|
+
title: "Sibling files agree (consensus oracle)",
|
|
19
|
+
category: "consistency",
|
|
20
|
+
domain: "code",
|
|
21
|
+
requires: { ecosystems: ["npm"] },
|
|
22
|
+
gating: false,
|
|
23
|
+
defaultConfig: { family: "package.json", field: "license", minFamily: 3 },
|
|
24
|
+
async run(ctx) {
|
|
25
|
+
const { root, checkConfig } = ctx;
|
|
26
|
+
|
|
27
|
+
// Collect family members.
|
|
28
|
+
const members = [];
|
|
29
|
+
for (const file of walkFiles(root)) {
|
|
30
|
+
if (basename(file) === checkConfig.family) members.push({ path: file });
|
|
31
|
+
}
|
|
32
|
+
if (members.length < checkConfig.minFamily) return { failed: false, findings: [], report: "" };
|
|
33
|
+
|
|
34
|
+
// project() extracts the fingerprint for a member (the configured field from the JSON file).
|
|
35
|
+
const project = (member) => {
|
|
36
|
+
try {
|
|
37
|
+
return getPath(JSON.parse(readFileSync(member.path, "utf8")), checkConfig.field) ?? null;
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const { findings: divergences, consensusOrReference } = await runFamilyConformance({ family: members, project, mode: "consensus" });
|
|
44
|
+
|
|
45
|
+
if (divergences === null || consensusOrReference === null) {
|
|
46
|
+
// No clear majority — nothing to flag.
|
|
47
|
+
return { failed: false, findings: [], report: "" };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const majorityKey = JSON.stringify(consensusOrReference);
|
|
51
|
+
const majorityCount = members.length - divergences.length;
|
|
52
|
+
|
|
53
|
+
const findings = divergences.map((d) =>
|
|
54
|
+
createFinding({
|
|
55
|
+
id: "consensus-divergence",
|
|
56
|
+
title: `Diverges from sibling consensus on '${checkConfig.field}'`,
|
|
57
|
+
severity: "warning",
|
|
58
|
+
file: relative(root, d.member.path),
|
|
59
|
+
message: `'${checkConfig.field}' = ${JSON.stringify(d.actual)}; ${majorityCount}/${members.length} siblings agree on ${majorityKey}`,
|
|
60
|
+
fix: `Set '${checkConfig.field}' to ${majorityKey} to match its siblings (or justify the difference).`,
|
|
61
|
+
}),
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const report = findings.length
|
|
65
|
+
? `# Sibling consensus — ${checkConfig.family} · ${checkConfig.field}\n\nMajority (${majorityCount}/${members.length}): ${majorityKey}\n\n${findings.map((f) => `- WARN ${f.file} — ${f.message}`).join("\n")}\n\nErrors: 0\nWarnings: ${findings.length}\n`
|
|
66
|
+
: "";
|
|
67
|
+
return { failed: false, findings, report };
|
|
68
|
+
},
|
|
69
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// CORE check — conformance oracle in REFERENCE mode applied to tsconfig.json compilerOptions.
|
|
2
|
+
// Walks all tsconfig.json files; the reference (default: root tsconfig.json) defines the expected values
|
|
3
|
+
// for any compilerOption key it declares. Any tsconfig whose values for those shared keys differ from the
|
|
4
|
+
// reference is flagged. Uses runFamilyConformance in reference mode — catches drift nobody has named.
|
|
5
|
+
import { readFileSync } from "node:fs";
|
|
6
|
+
import { relative, join, basename } from "node:path";
|
|
7
|
+
import { createFinding } from "../../core/finding.mjs";
|
|
8
|
+
import { walkFiles } from "../../core/fs-walk.mjs";
|
|
9
|
+
import { runFamilyConformance } from "../../core/oracle/family-conformance.mjs";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Parse a tsconfig.json — returns { compilerOptions: {...} } or null on failure.
|
|
13
|
+
*/
|
|
14
|
+
function parseTsconfig(file) {
|
|
15
|
+
try {
|
|
16
|
+
return JSON.parse(readFileSync(file, "utf8"));
|
|
17
|
+
} catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const audit = {
|
|
23
|
+
id: "tsconfig-conformance",
|
|
24
|
+
title: "tsconfig.json files conform to reference compilerOptions",
|
|
25
|
+
category: "conformance",
|
|
26
|
+
domain: "code",
|
|
27
|
+
requires: { ecosystems: ["npm"] },
|
|
28
|
+
gating: false,
|
|
29
|
+
defaultConfig: { referenceFile: "tsconfig.json" }, // relative to root
|
|
30
|
+
async run(ctx) {
|
|
31
|
+
const { root, checkConfig } = ctx;
|
|
32
|
+
const refPath = join(root, checkConfig.referenceFile);
|
|
33
|
+
|
|
34
|
+
// Parse the reference to extract its compilerOptions keys — these are the axes we judge.
|
|
35
|
+
const refParsed = parseTsconfig(refPath);
|
|
36
|
+
const refCompilerOptions = refParsed?.compilerOptions || {};
|
|
37
|
+
const refKeys = Object.keys(refCompilerOptions);
|
|
38
|
+
if (refKeys.length === 0) {
|
|
39
|
+
// No compilerOptions in reference — nothing to judge.
|
|
40
|
+
return { failed: false, findings: [], report: "" };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Collect all tsconfig.json files (including the reference — the oracle skips it).
|
|
44
|
+
const members = [];
|
|
45
|
+
for (const file of walkFiles(root)) {
|
|
46
|
+
if (basename(file) === "tsconfig.json") members.push({ path: file });
|
|
47
|
+
}
|
|
48
|
+
if (members.length === 0) return { failed: false, findings: [], report: "" };
|
|
49
|
+
|
|
50
|
+
// Fingerprint = the subset of compilerOptions present in the REFERENCE — so we only compare apples to apples.
|
|
51
|
+
const project = (member) => {
|
|
52
|
+
const parsed = parseTsconfig(member.path);
|
|
53
|
+
const co = parsed?.compilerOptions || {};
|
|
54
|
+
// Build a sub-object with ONLY the keys from the reference's compilerOptions.
|
|
55
|
+
const subset = {};
|
|
56
|
+
for (const k of refKeys) {
|
|
57
|
+
if (Object.prototype.hasOwnProperty.call(co, k)) {
|
|
58
|
+
subset[k] = co[k];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return subset;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Reference fingerprint is the subset of its OWN compilerOptions keys.
|
|
65
|
+
const { findings: divergences } = await runFamilyConformance({ family: members, project, mode: "reference", referenceId: refPath });
|
|
66
|
+
|
|
67
|
+
if (!divergences || divergences.length === 0) {
|
|
68
|
+
return { failed: false, findings: [], report: "" };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const findings = [];
|
|
72
|
+
for (const d of divergences) {
|
|
73
|
+
const relPath = relative(root, d.member.path);
|
|
74
|
+
// Enumerate only the keys that actually differ.
|
|
75
|
+
const expected = d.expected || {};
|
|
76
|
+
const actual = d.actual || {};
|
|
77
|
+
const allKeys = new Set([...Object.keys(expected), ...Object.keys(actual)]);
|
|
78
|
+
const diffKeys = [...allKeys].filter((k) => JSON.stringify(expected[k]) !== JSON.stringify(actual[k]));
|
|
79
|
+
for (const k of diffKeys) {
|
|
80
|
+
findings.push(
|
|
81
|
+
createFinding({
|
|
82
|
+
id: "tsconfig-drift",
|
|
83
|
+
title: `tsconfig compilerOption '${k}' diverges from reference`,
|
|
84
|
+
severity: "warning",
|
|
85
|
+
file: relPath,
|
|
86
|
+
message: `compilerOptions.${k}: expected ${JSON.stringify(expected[k])}, got ${JSON.stringify(actual[k])} (reference: ${checkConfig.referenceFile})`,
|
|
87
|
+
fix: `Set compilerOptions.${k} to ${JSON.stringify(expected[k])} to match ${checkConfig.referenceFile}, or remove it from the reference if it should not be enforced globally.`,
|
|
88
|
+
}),
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const report = findings.length
|
|
94
|
+
? `# tsconfig conformance\n\nReference: ${checkConfig.referenceFile}\n\n${findings.map((f) => `- WARN ${f.file} — ${f.message}`).join("\n")}\n\nErrors: 0\nWarnings: ${findings.length}\n`
|
|
95
|
+
: "";
|
|
96
|
+
return { failed: false, findings, report };
|
|
97
|
+
},
|
|
98
|
+
};
|