@getcodesentinel/codesentinel 1.17.1 → 1.17.3
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 +1 -0
- package/dist/index.js +120 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -416,6 +416,7 @@ Score direction:
|
|
|
416
416
|
|
|
417
417
|
- `risk.riskScore`: higher means higher risk (worse).
|
|
418
418
|
- `health.healthScore`: higher means better health posture.
|
|
419
|
+
- Report views also include derived tiers: `riskTier` and `healthTier`.
|
|
419
420
|
- `health.trace`: per-dimension factor traces with normalized metrics and evidence.
|
|
420
421
|
|
|
421
422
|
Health v2 dimensions and weights:
|
package/dist/index.js
CHANGED
|
@@ -703,6 +703,14 @@ var DEFAULT_OPTIONS = {
|
|
|
703
703
|
maxEntryBytes: 4 * 1024 * 1024,
|
|
704
704
|
sweepIntervalWrites: 25
|
|
705
705
|
};
|
|
706
|
+
var DEFAULT_BUCKET = "default";
|
|
707
|
+
var normalizeBucket = (value) => {
|
|
708
|
+
const trimmed = value.trim().toLowerCase();
|
|
709
|
+
if (trimmed.length === 0) {
|
|
710
|
+
return DEFAULT_BUCKET;
|
|
711
|
+
}
|
|
712
|
+
return trimmed.replace(/[^a-z0-9_-]/g, "_");
|
|
713
|
+
};
|
|
706
714
|
var FileCacheStore = class {
|
|
707
715
|
constructor(directoryPath, options = DEFAULT_OPTIONS) {
|
|
708
716
|
this.directoryPath = directoryPath;
|
|
@@ -712,13 +720,22 @@ var FileCacheStore = class {
|
|
|
712
720
|
inFlightWrites = /* @__PURE__ */ new Map();
|
|
713
721
|
writesSinceSweep = 0;
|
|
714
722
|
sweepPromise = Promise.resolve();
|
|
723
|
+
bucketForKey(key) {
|
|
724
|
+
const configured = this.options.bucketForKey?.(key);
|
|
725
|
+
if (configured === void 0) {
|
|
726
|
+
return DEFAULT_BUCKET;
|
|
727
|
+
}
|
|
728
|
+
return normalizeBucket(configured);
|
|
729
|
+
}
|
|
715
730
|
toEntryPath(key) {
|
|
731
|
+
const bucket = this.bucketForKey(key);
|
|
716
732
|
const digest = createHash("sha256").update(key).digest("hex");
|
|
717
|
-
return join3(this.directoryPath, `${digest}.json`);
|
|
733
|
+
return join3(this.directoryPath, bucket, `${digest}.json`);
|
|
718
734
|
}
|
|
719
735
|
async writeEntry(key, entry) {
|
|
720
736
|
const filePath = this.toEntryPath(key);
|
|
721
|
-
|
|
737
|
+
const bucketPath = join3(this.directoryPath, this.bucketForKey(key));
|
|
738
|
+
await mkdir(bucketPath, { recursive: true });
|
|
722
739
|
const tempPath = `${filePath}.tmp`;
|
|
723
740
|
const payload = {
|
|
724
741
|
key,
|
|
@@ -745,34 +762,74 @@ var FileCacheStore = class {
|
|
|
745
762
|
await this.sweepPromise;
|
|
746
763
|
}
|
|
747
764
|
async evictToSizeLimit() {
|
|
765
|
+
const nowMs = Date.now();
|
|
748
766
|
let entries;
|
|
749
767
|
try {
|
|
750
|
-
const
|
|
768
|
+
const bucketEntries = await readdir(this.directoryPath, { withFileTypes: true });
|
|
769
|
+
const bucketNames = bucketEntries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
751
770
|
entries = (await Promise.all(
|
|
752
|
-
|
|
753
|
-
const
|
|
754
|
-
const
|
|
755
|
-
return
|
|
771
|
+
bucketNames.map(async (bucket) => {
|
|
772
|
+
const bucketPath = join3(this.directoryPath, bucket);
|
|
773
|
+
const files = await readdir(bucketPath, { withFileTypes: true });
|
|
774
|
+
return await Promise.all(
|
|
775
|
+
files.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map(async (entry) => {
|
|
776
|
+
const path = join3(bucketPath, entry.name);
|
|
777
|
+
const info = await stat(path);
|
|
778
|
+
return {
|
|
779
|
+
path,
|
|
780
|
+
size: info.size,
|
|
781
|
+
mtimeMs: info.mtimeMs,
|
|
782
|
+
bucket
|
|
783
|
+
};
|
|
784
|
+
})
|
|
785
|
+
);
|
|
756
786
|
})
|
|
757
|
-
)).filter((entry) => Number.isFinite(entry.size) && entry.size > 0);
|
|
787
|
+
)).flat().filter((entry) => Number.isFinite(entry.size) && entry.size > 0);
|
|
758
788
|
} catch {
|
|
759
789
|
return;
|
|
760
790
|
}
|
|
791
|
+
if (this.options.maxAgeMsByBucket !== void 0) {
|
|
792
|
+
const retained = [];
|
|
793
|
+
let deletedAny2 = false;
|
|
794
|
+
for (const entry of entries) {
|
|
795
|
+
const maxAgeMs = this.options.maxAgeMsByBucket[entry.bucket];
|
|
796
|
+
const expired = typeof maxAgeMs === "number" && Number.isFinite(maxAgeMs) && nowMs - entry.mtimeMs > maxAgeMs;
|
|
797
|
+
if (!expired) {
|
|
798
|
+
retained.push(entry);
|
|
799
|
+
continue;
|
|
800
|
+
}
|
|
801
|
+
try {
|
|
802
|
+
await unlink(entry.path);
|
|
803
|
+
deletedAny2 = true;
|
|
804
|
+
} catch {
|
|
805
|
+
retained.push(entry);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
entries = retained;
|
|
809
|
+
if (deletedAny2) {
|
|
810
|
+
this.byKey.clear();
|
|
811
|
+
}
|
|
812
|
+
}
|
|
761
813
|
let totalBytes = entries.reduce((sum, entry) => sum + entry.size, 0);
|
|
762
814
|
if (totalBytes <= this.options.maxBytes) {
|
|
763
815
|
return;
|
|
764
816
|
}
|
|
765
817
|
entries.sort((a, b) => a.mtimeMs - b.mtimeMs);
|
|
818
|
+
let deletedAny = false;
|
|
766
819
|
for (const entry of entries) {
|
|
767
820
|
if (totalBytes <= this.options.maxBytes) {
|
|
768
821
|
break;
|
|
769
822
|
}
|
|
770
823
|
try {
|
|
771
824
|
await unlink(entry.path);
|
|
825
|
+
deletedAny = true;
|
|
772
826
|
totalBytes -= entry.size;
|
|
773
827
|
} catch {
|
|
774
828
|
}
|
|
775
829
|
}
|
|
830
|
+
if (deletedAny) {
|
|
831
|
+
this.byKey.clear();
|
|
832
|
+
}
|
|
776
833
|
}
|
|
777
834
|
async get(key) {
|
|
778
835
|
if (this.byKey.has(key)) {
|
|
@@ -808,7 +865,7 @@ var FileCacheStore = class {
|
|
|
808
865
|
};
|
|
809
866
|
var SIX_HOURS_MS = 6 * 60 * 60 * 1e3;
|
|
810
867
|
var ONE_DAY_MS = 24 * 60 * 60 * 1e3;
|
|
811
|
-
var DEFAULT_MAX_BYTES =
|
|
868
|
+
var DEFAULT_MAX_BYTES = 20 * 1024 * 1024;
|
|
812
869
|
var DEFAULT_MAX_ENTRY_BYTES = 4 * 1024 * 1024;
|
|
813
870
|
var DEFAULT_SWEEP_INTERVAL_WRITES = 25;
|
|
814
871
|
var cacheStoreSingleton;
|
|
@@ -835,6 +892,8 @@ var getNpmMetadataCacheStore = () => {
|
|
|
835
892
|
return cacheStoreSingleton;
|
|
836
893
|
}
|
|
837
894
|
const path = join4(resolveCodesentinelCacheDir(process.env), "npm-metadata-v2");
|
|
895
|
+
const packumentTtlMs = getPackumentCacheTtlMs();
|
|
896
|
+
const downloadsTtlMs = getWeeklyDownloadsCacheTtlMs();
|
|
838
897
|
cacheStoreSingleton = new FileCacheStore(path, {
|
|
839
898
|
maxBytes: parsePositiveIntegerFromEnv(
|
|
840
899
|
process.env["CODESENTINEL_CACHE_MAX_BYTES"],
|
|
@@ -847,7 +906,20 @@ var getNpmMetadataCacheStore = () => {
|
|
|
847
906
|
sweepIntervalWrites: parsePositiveIntegerFromEnv(
|
|
848
907
|
process.env["CODESENTINEL_CACHE_SWEEP_INTERVAL_WRITES"],
|
|
849
908
|
DEFAULT_SWEEP_INTERVAL_WRITES
|
|
850
|
-
)
|
|
909
|
+
),
|
|
910
|
+
bucketForKey: (key) => {
|
|
911
|
+
if (key.startsWith("npm:downloads:last-week:")) {
|
|
912
|
+
return "downloads";
|
|
913
|
+
}
|
|
914
|
+
if (key.startsWith("npm:packument:")) {
|
|
915
|
+
return "packument";
|
|
916
|
+
}
|
|
917
|
+
return "default";
|
|
918
|
+
},
|
|
919
|
+
maxAgeMsByBucket: {
|
|
920
|
+
downloads: downloadsTtlMs,
|
|
921
|
+
packument: packumentTtlMs
|
|
922
|
+
}
|
|
851
923
|
});
|
|
852
924
|
return cacheStoreSingleton;
|
|
853
925
|
};
|
|
@@ -1700,6 +1772,21 @@ var toRiskTier = (score) => {
|
|
|
1700
1772
|
}
|
|
1701
1773
|
return "very_high";
|
|
1702
1774
|
};
|
|
1775
|
+
var toHealthTier = (score) => {
|
|
1776
|
+
if (score < 20) {
|
|
1777
|
+
return "critical";
|
|
1778
|
+
}
|
|
1779
|
+
if (score < 40) {
|
|
1780
|
+
return "weak";
|
|
1781
|
+
}
|
|
1782
|
+
if (score < 60) {
|
|
1783
|
+
return "fair";
|
|
1784
|
+
}
|
|
1785
|
+
if (score < 80) {
|
|
1786
|
+
return "good";
|
|
1787
|
+
}
|
|
1788
|
+
return "excellent";
|
|
1789
|
+
};
|
|
1703
1790
|
var factorLabelById = {
|
|
1704
1791
|
"repository.structural": "Structural complexity",
|
|
1705
1792
|
"repository.evolution": "Change volatility",
|
|
@@ -1936,6 +2023,7 @@ var createReport = (snapshot, diff) => {
|
|
|
1936
2023
|
riskScore: snapshot.analysis.risk.riskScore,
|
|
1937
2024
|
normalizedScore: snapshot.analysis.risk.normalizedScore,
|
|
1938
2025
|
riskTier: toRiskTier(snapshot.analysis.risk.riskScore),
|
|
2026
|
+
healthTier: toHealthTier(snapshot.analysis.health.healthScore),
|
|
1939
2027
|
confidence: repositoryConfidence(snapshot),
|
|
1940
2028
|
dimensionScores: repositoryDimensionScores(snapshot)
|
|
1941
2029
|
},
|
|
@@ -2003,6 +2091,7 @@ var renderTextReport = (report) => {
|
|
|
2003
2091
|
lines.push(` riskScore: ${report.repository.riskScore}`);
|
|
2004
2092
|
lines.push(` normalizedScore: ${report.repository.normalizedScore}`);
|
|
2005
2093
|
lines.push(` riskTier: ${report.repository.riskTier}`);
|
|
2094
|
+
lines.push(` healthTier: ${report.repository.healthTier}`);
|
|
2006
2095
|
lines.push(` confidence: ${report.repository.confidence ?? "n/a"}`);
|
|
2007
2096
|
lines.push("");
|
|
2008
2097
|
lines.push("Dimension Scores (0-100)");
|
|
@@ -2096,6 +2185,7 @@ var renderMarkdownReport = (report) => {
|
|
|
2096
2185
|
lines.push(`- riskScore: \`${report.repository.riskScore}\``);
|
|
2097
2186
|
lines.push(`- normalizedScore: \`${report.repository.normalizedScore}\``);
|
|
2098
2187
|
lines.push(`- riskTier: \`${report.repository.riskTier}\``);
|
|
2188
|
+
lines.push(`- healthTier: \`${report.repository.healthTier}\``);
|
|
2099
2189
|
lines.push(`- confidence: \`${report.repository.confidence ?? "n/a"}\``);
|
|
2100
2190
|
lines.push("");
|
|
2101
2191
|
lines.push("## Dimension Scores (0-100)");
|
|
@@ -2826,6 +2916,21 @@ import { dirname as dirname2, resolve as resolve5 } from "path";
|
|
|
2826
2916
|
import { fileURLToPath } from "url";
|
|
2827
2917
|
|
|
2828
2918
|
// src/application/format-analyze-output.ts
|
|
2919
|
+
var toHealthTier2 = (score) => {
|
|
2920
|
+
if (score < 20) {
|
|
2921
|
+
return "critical";
|
|
2922
|
+
}
|
|
2923
|
+
if (score < 40) {
|
|
2924
|
+
return "weak";
|
|
2925
|
+
}
|
|
2926
|
+
if (score < 60) {
|
|
2927
|
+
return "fair";
|
|
2928
|
+
}
|
|
2929
|
+
if (score < 80) {
|
|
2930
|
+
return "good";
|
|
2931
|
+
}
|
|
2932
|
+
return "excellent";
|
|
2933
|
+
};
|
|
2829
2934
|
var createSummaryShape = (summary) => ({
|
|
2830
2935
|
targetPath: summary.structural.targetPath,
|
|
2831
2936
|
structural: summary.structural.metrics,
|
|
@@ -2865,6 +2970,7 @@ var createSummaryShape = (summary) => ({
|
|
|
2865
2970
|
},
|
|
2866
2971
|
health: {
|
|
2867
2972
|
healthScore: summary.health.healthScore,
|
|
2973
|
+
healthTier: toHealthTier2(summary.health.healthScore),
|
|
2868
2974
|
normalizedScore: summary.health.normalizedScore,
|
|
2869
2975
|
dimensions: summary.health.dimensions,
|
|
2870
2976
|
topIssues: summary.health.topIssues.slice(0, 5)
|
|
@@ -7295,6 +7401,7 @@ var renderReportHighlightsText = (report) => {
|
|
|
7295
7401
|
lines.push(` healthScore: ${report.health.healthScore}`);
|
|
7296
7402
|
lines.push(` normalizedScore: ${report.repository.normalizedScore}`);
|
|
7297
7403
|
lines.push(` riskTier: ${report.repository.riskTier}`);
|
|
7404
|
+
lines.push(` healthTier: ${report.repository.healthTier}`);
|
|
7298
7405
|
lines.push("");
|
|
7299
7406
|
lines.push("Top Hotspots");
|
|
7300
7407
|
for (const hotspot of report.hotspots.slice(0, 5)) {
|
|
@@ -7311,6 +7418,7 @@ var renderReportHighlightsMarkdown = (report) => {
|
|
|
7311
7418
|
lines.push(`- healthScore: \`${report.health.healthScore}\``);
|
|
7312
7419
|
lines.push(`- normalizedScore: \`${report.repository.normalizedScore}\``);
|
|
7313
7420
|
lines.push(`- riskTier: \`${report.repository.riskTier}\``);
|
|
7421
|
+
lines.push(`- healthTier: \`${report.repository.healthTier}\``);
|
|
7314
7422
|
lines.push("");
|
|
7315
7423
|
lines.push("## Top Hotspots");
|
|
7316
7424
|
for (const hotspot of report.hotspots.slice(0, 5)) {
|
|
@@ -7328,6 +7436,7 @@ var renderCompactText = (report, explainSummary) => {
|
|
|
7328
7436
|
lines.push(` riskScore: ${report.repository.riskScore}`);
|
|
7329
7437
|
lines.push(` healthScore: ${report.health.healthScore}`);
|
|
7330
7438
|
lines.push(` riskTier: ${report.repository.riskTier}`);
|
|
7439
|
+
lines.push(` healthTier: ${report.repository.healthTier}`);
|
|
7331
7440
|
lines.push(
|
|
7332
7441
|
` dimensions: structural=${report.repository.dimensionScores.structural ?? "n/a"}, evolution=${report.repository.dimensionScores.evolution ?? "n/a"}, external=${report.repository.dimensionScores.external ?? "n/a"}, interactions=${report.repository.dimensionScores.interactions ?? "n/a"}`
|
|
7333
7442
|
);
|
|
@@ -7354,6 +7463,7 @@ var renderCompactMarkdown = (report, explainSummary) => {
|
|
|
7354
7463
|
lines.push(`- riskScore: \`${report.repository.riskScore}\``);
|
|
7355
7464
|
lines.push(`- healthScore: \`${report.health.healthScore}\``);
|
|
7356
7465
|
lines.push(`- riskTier: \`${report.repository.riskTier}\``);
|
|
7466
|
+
lines.push(`- healthTier: \`${report.repository.healthTier}\``);
|
|
7357
7467
|
lines.push(
|
|
7358
7468
|
`- dimensions: structural=\`${report.repository.dimensionScores.structural ?? "n/a"}\`, evolution=\`${report.repository.dimensionScores.evolution ?? "n/a"}\`, external=\`${report.repository.dimensionScores.external ?? "n/a"}\`, interactions=\`${report.repository.dimensionScores.interactions ?? "n/a"}\``
|
|
7359
7469
|
);
|