@decantr/verifier 2.2.0 → 2.3.1
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 +4 -1
- package/dist/index.d.ts +12 -2
- package/dist/index.js +239 -97
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/schema/decantr-ci-report.v1.json +104 -0
package/dist/index.js
CHANGED
|
@@ -747,6 +747,7 @@ var VERIFICATION_SCHEMA_URLS = {
|
|
|
747
747
|
common: "https://decantr.ai/schemas/verification-report.common.v1.json",
|
|
748
748
|
projectAudit: "https://decantr.ai/schemas/project-audit-report.v1.json",
|
|
749
749
|
projectHealth: "https://decantr.ai/schemas/project-health-report.v1.json",
|
|
750
|
+
decantrCi: "https://decantr.ai/schemas/decantr-ci-report.v1.json",
|
|
750
751
|
evidenceBundle: "https://decantr.ai/schemas/evidence-bundle.v1.json",
|
|
751
752
|
workspaceHealth: "https://decantr.ai/schemas/workspace-health-report.v1.json",
|
|
752
753
|
fileCritique: "https://decantr.ai/schemas/file-critique-report.v1.json",
|
|
@@ -895,7 +896,7 @@ function createContractAssertions(projectRoot, audit) {
|
|
|
895
896
|
status: packManifest ? "passed" : "failed",
|
|
896
897
|
message: packManifest ? "Compiled execution pack manifest is present." : "Compiled execution pack manifest is missing.",
|
|
897
898
|
evidence: [redactEvidenceText(projectRoot, join2(contextDir, "pack-manifest.json"))],
|
|
898
|
-
suggestedFix: packManifest ? void 0 : "Run `decantr
|
|
899
|
+
suggestedFix: packManifest ? void 0 : "Run `decantr registry compile-packs decantr.essence.json --write-context` to hydrate the full context pack bundle."
|
|
899
900
|
})
|
|
900
901
|
);
|
|
901
902
|
assertions.push(
|
|
@@ -907,7 +908,7 @@ function createContractAssertions(projectRoot, audit) {
|
|
|
907
908
|
status: audit?.reviewPack ? "passed" : "failed",
|
|
908
909
|
message: audit?.reviewPack ? "Compiled review pack is present." : "Compiled review pack is missing.",
|
|
909
910
|
evidence: [redactEvidenceText(projectRoot, join2(contextDir, "review-pack.json"))],
|
|
910
|
-
suggestedFix: audit?.reviewPack ? void 0 : "Run `decantr
|
|
911
|
+
suggestedFix: audit?.reviewPack ? void 0 : "Run `decantr registry compile-packs decantr.essence.json --write-context` so critique has the compiled review contract."
|
|
911
912
|
})
|
|
912
913
|
);
|
|
913
914
|
const tokensPath = join2(projectRoot, "src", "styles", "tokens.css");
|
|
@@ -1042,6 +1043,71 @@ function loadPackManifest(projectRoot) {
|
|
|
1042
1043
|
join2(projectRoot, ".decantr", "context", "pack-manifest.json")
|
|
1043
1044
|
);
|
|
1044
1045
|
}
|
|
1046
|
+
function collectPackManifestReferences(packManifest) {
|
|
1047
|
+
const references = [];
|
|
1048
|
+
if (packManifest.scaffold) {
|
|
1049
|
+
references.push({
|
|
1050
|
+
entryId: packManifest.scaffold.id || "scaffold",
|
|
1051
|
+
kind: "scaffold",
|
|
1052
|
+
markdown: packManifest.scaffold.markdown,
|
|
1053
|
+
json: packManifest.scaffold.json
|
|
1054
|
+
});
|
|
1055
|
+
}
|
|
1056
|
+
if (packManifest.review) {
|
|
1057
|
+
references.push({
|
|
1058
|
+
entryId: packManifest.review.id || "review",
|
|
1059
|
+
kind: "review",
|
|
1060
|
+
markdown: packManifest.review.markdown,
|
|
1061
|
+
json: packManifest.review.json
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
for (const section of packManifest.sections ?? []) {
|
|
1065
|
+
references.push({
|
|
1066
|
+
entryId: section.id,
|
|
1067
|
+
kind: "section",
|
|
1068
|
+
markdown: section.markdown,
|
|
1069
|
+
json: section.json
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
for (const page of packManifest.pages ?? []) {
|
|
1073
|
+
references.push({
|
|
1074
|
+
entryId: page.id,
|
|
1075
|
+
kind: "page",
|
|
1076
|
+
markdown: page.markdown,
|
|
1077
|
+
json: page.json
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
for (const mutation of packManifest.mutations ?? []) {
|
|
1081
|
+
references.push({
|
|
1082
|
+
entryId: mutation.id,
|
|
1083
|
+
kind: "mutation",
|
|
1084
|
+
markdown: mutation.markdown,
|
|
1085
|
+
json: mutation.json
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
return references;
|
|
1089
|
+
}
|
|
1090
|
+
function collectMissingPackManifestFiles(projectRoot, packManifest = loadPackManifest(projectRoot)) {
|
|
1091
|
+
if (!packManifest) return [];
|
|
1092
|
+
const contextDir = join2(projectRoot, ".decantr", "context");
|
|
1093
|
+
const missing = [];
|
|
1094
|
+
for (const reference of collectPackManifestReferences(packManifest)) {
|
|
1095
|
+
for (const field of ["markdown", "json"]) {
|
|
1096
|
+
const fileName = reference[field];
|
|
1097
|
+
if (!fileName) continue;
|
|
1098
|
+
const absolutePath = join2(contextDir, fileName);
|
|
1099
|
+
if (existsSync2(absolutePath)) continue;
|
|
1100
|
+
missing.push({
|
|
1101
|
+
entryId: reference.entryId,
|
|
1102
|
+
kind: reference.kind,
|
|
1103
|
+
field,
|
|
1104
|
+
relativePath: `.decantr/context/${fileName}`,
|
|
1105
|
+
absolutePath
|
|
1106
|
+
});
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
return missing;
|
|
1110
|
+
}
|
|
1045
1111
|
function readTextIfExists(path) {
|
|
1046
1112
|
try {
|
|
1047
1113
|
return existsSync2(path) ? readFileSync2(path, "utf-8") : "";
|
|
@@ -1049,6 +1115,11 @@ function readTextIfExists(path) {
|
|
|
1049
1115
|
return "";
|
|
1050
1116
|
}
|
|
1051
1117
|
}
|
|
1118
|
+
function readProjectAdoptionMode(projectRoot) {
|
|
1119
|
+
const projectJson = readJsonIfExists(join2(projectRoot, ".decantr", "project.json"));
|
|
1120
|
+
const adoptionMode = projectJson?.initialized?.adoptionMode;
|
|
1121
|
+
return typeof adoptionMode === "string" ? adoptionMode : null;
|
|
1122
|
+
}
|
|
1052
1123
|
function createSourceAuditBucket() {
|
|
1053
1124
|
return { count: 0, files: [] };
|
|
1054
1125
|
}
|
|
@@ -2555,23 +2626,24 @@ function appendRuntimeAuditFindings(findings, runtimeAudit, projectRoot, topolog
|
|
|
2555
2626
|
);
|
|
2556
2627
|
}
|
|
2557
2628
|
}
|
|
2558
|
-
function appendSourceAuditFindings(findings, sourceAudit, essence, reviewPack) {
|
|
2629
|
+
function appendSourceAuditFindings(findings, sourceAudit, essence, reviewPack, adoptionMode) {
|
|
2559
2630
|
if (sourceAudit.filesChecked === 0) {
|
|
2560
2631
|
return;
|
|
2561
2632
|
}
|
|
2633
|
+
const isContractOnly = adoptionMode === "contract-only";
|
|
2562
2634
|
if (sourceAudit.inlineStyles.count > 0) {
|
|
2563
2635
|
findings.push(
|
|
2564
2636
|
makeFinding({
|
|
2565
2637
|
id: "source-inline-styles-present",
|
|
2566
2638
|
category: "Source Audit",
|
|
2567
2639
|
severity: "warn",
|
|
2568
|
-
message: "Source files still contain disallowed inline style attributes, which undermines the compiled treatment contract.",
|
|
2640
|
+
message: isContractOnly ? "Source files contain inline style attributes; contract-only projects should route static visual decisions through project-owned styling law." : "Source files still contain disallowed inline style attributes, which undermines the compiled treatment contract.",
|
|
2569
2641
|
evidence: buildSourceAuditEvidence(
|
|
2570
2642
|
sourceAudit,
|
|
2571
2643
|
sourceAudit.inlineStyles,
|
|
2572
2644
|
"Disallowed inline style attributes"
|
|
2573
2645
|
),
|
|
2574
|
-
suggestedFix: "Move static visual styling into treatments, atoms, or design-token-backed classes. Inline style remains acceptable for Decantr CSS-variable writes and truly dynamic geometry."
|
|
2646
|
+
suggestedFix: isContractOnly ? "Move static visual styling into the app design system, Tailwind/theme tokens, component variants, or accepted local rules. Keep inline style only for truly dynamic geometry." : "Move static visual styling into treatments, atoms, or design-token-backed classes. Inline style remains acceptable for Decantr CSS-variable writes and truly dynamic geometry."
|
|
2575
2647
|
})
|
|
2576
2648
|
);
|
|
2577
2649
|
}
|
|
@@ -2581,17 +2653,17 @@ function appendSourceAuditFindings(findings, sourceAudit, essence, reviewPack) {
|
|
|
2581
2653
|
id: "source-component-style-tags-present",
|
|
2582
2654
|
category: "Source Audit",
|
|
2583
2655
|
severity: "warn",
|
|
2584
|
-
message: "Source files inject component-scoped style tags or dynamic style elements, which bypass the compiled Decantr layer contract.",
|
|
2656
|
+
message: isContractOnly ? "Source files inject component-scoped style tags or dynamic style elements, which makes project-owned styling rules harder to enforce." : "Source files inject component-scoped style tags or dynamic style elements, which bypass the compiled Decantr layer contract.",
|
|
2585
2657
|
evidence: buildSourceAuditEvidence(
|
|
2586
2658
|
sourceAudit,
|
|
2587
2659
|
sourceAudit.componentStyleTags,
|
|
2588
2660
|
"Component-level style tag signals"
|
|
2589
2661
|
),
|
|
2590
|
-
suggestedFix: "Move shared keyframes, media queries, and visual rules into global.css or treatments.css so styling stays inside the reviewed Decantr layer stack."
|
|
2662
|
+
suggestedFix: isContractOnly ? "Move shared keyframes, media queries, and visual rules into the project stylesheet, component library, or accepted local pattern/rule manifest." : "Move shared keyframes, media queries, and visual rules into global.css or treatments.css so styling stays inside the reviewed Decantr layer stack."
|
|
2591
2663
|
})
|
|
2592
2664
|
);
|
|
2593
2665
|
}
|
|
2594
|
-
if (sourceAudit.localCssRuntimeSignals.count > 0) {
|
|
2666
|
+
if (!isContractOnly && sourceAudit.localCssRuntimeSignals.count > 0) {
|
|
2595
2667
|
findings.push(
|
|
2596
2668
|
makeFinding({
|
|
2597
2669
|
id: "source-local-css-runtime-stub-present",
|
|
@@ -3694,6 +3766,7 @@ async function auditProject(projectRoot) {
|
|
|
3694
3766
|
const findings = [];
|
|
3695
3767
|
const reviewPack = loadReviewPack(projectRoot);
|
|
3696
3768
|
const packManifest = loadPackManifest(projectRoot);
|
|
3769
|
+
const adoptionMode = readProjectAdoptionMode(projectRoot);
|
|
3697
3770
|
const runtimeAudit = emptyRuntimeAudit();
|
|
3698
3771
|
if (!existsSync2(essencePath)) {
|
|
3699
3772
|
findings.push(
|
|
@@ -3774,10 +3847,25 @@ async function auditProject(projectRoot) {
|
|
|
3774
3847
|
severity: "warn",
|
|
3775
3848
|
message: "Compiled execution pack manifest is missing.",
|
|
3776
3849
|
evidence: [join2(projectRoot, ".decantr", "context", "pack-manifest.json")],
|
|
3777
|
-
suggestedFix: "Run `decantr
|
|
3850
|
+
suggestedFix: "Run `decantr registry compile-packs decantr.essence.json --write-context` to hydrate scaffold, review, mutation, section, and page packs."
|
|
3778
3851
|
})
|
|
3779
3852
|
);
|
|
3780
3853
|
} else {
|
|
3854
|
+
const missingPackFiles = collectMissingPackManifestFiles(projectRoot, packManifest);
|
|
3855
|
+
if (missingPackFiles.length > 0) {
|
|
3856
|
+
findings.push(
|
|
3857
|
+
makeFinding({
|
|
3858
|
+
id: "pack-manifest-referenced-files-missing",
|
|
3859
|
+
category: "Execution Packs",
|
|
3860
|
+
severity: "warn",
|
|
3861
|
+
message: "The compiled execution pack manifest references files that are missing.",
|
|
3862
|
+
evidence: missingPackFiles.slice(0, 12).map(
|
|
3863
|
+
(missing) => `${missing.kind}:${missing.entryId}:${missing.field} -> ${missing.relativePath}`
|
|
3864
|
+
),
|
|
3865
|
+
suggestedFix: "Regenerate or hydrate the full execution pack bundle so every file named by pack-manifest.json exists beside it."
|
|
3866
|
+
})
|
|
3867
|
+
);
|
|
3868
|
+
}
|
|
3781
3869
|
if (!packManifest.scaffold) {
|
|
3782
3870
|
findings.push(
|
|
3783
3871
|
makeFinding({
|
|
@@ -3810,7 +3898,7 @@ async function auditProject(projectRoot) {
|
|
|
3810
3898
|
severity: "info",
|
|
3811
3899
|
message: "No mutation packs were found in the manifest.",
|
|
3812
3900
|
evidence: ["pack-manifest.json"],
|
|
3813
|
-
suggestedFix: "Run `decantr
|
|
3901
|
+
suggestedFix: "Run `decantr registry compile-packs decantr.essence.json --write-context` to hydrate mutation task packs."
|
|
3814
3902
|
})
|
|
3815
3903
|
);
|
|
3816
3904
|
}
|
|
@@ -3823,7 +3911,7 @@ async function auditProject(projectRoot) {
|
|
|
3823
3911
|
severity: "warn",
|
|
3824
3912
|
message: "The compiled review pack file is missing.",
|
|
3825
3913
|
evidence: [join2(projectRoot, ".decantr", "context", "review-pack.json")],
|
|
3826
|
-
suggestedFix: "
|
|
3914
|
+
suggestedFix: "Hydrate the full hosted context bundle with `decantr registry compile-packs decantr.essence.json --write-context` so critique consumers can anchor findings to the compiled review contract."
|
|
3827
3915
|
})
|
|
3828
3916
|
);
|
|
3829
3917
|
}
|
|
@@ -3837,7 +3925,7 @@ async function auditProject(projectRoot) {
|
|
|
3837
3925
|
summarizeTopology(essence, reviewPack),
|
|
3838
3926
|
sourceAudit
|
|
3839
3927
|
);
|
|
3840
|
-
appendSourceAuditFindings(findings, sourceAudit, essence, reviewPack);
|
|
3928
|
+
appendSourceAuditFindings(findings, sourceAudit, essence, reviewPack, adoptionMode);
|
|
3841
3929
|
appendStyleContractFindings(findings, styleAudit, essence);
|
|
3842
3930
|
const summary = {
|
|
3843
3931
|
errorCount: findings.filter((finding) => finding.severity === "error").length,
|
|
@@ -4554,14 +4642,17 @@ function getObjectLiteralPropertyExpression(objectLiteral, propertyName, namedEx
|
|
|
4554
4642
|
}
|
|
4555
4643
|
return null;
|
|
4556
4644
|
}
|
|
4557
|
-
|
|
4645
|
+
var MAX_OPEN_REDIRECT_RESOLUTION_DEPTH = 40;
|
|
4646
|
+
function resolveObjectLiteralExpressionAtPropertyPath(expression, sourceFile, propertyPath, namedExpressions, namedPropertyAliases, seenIdentifiers, seenFunctions, depth = 0) {
|
|
4647
|
+
if (depth > MAX_OPEN_REDIRECT_RESOLUTION_DEPTH) return null;
|
|
4558
4648
|
let resolvedObjectLiteral = resolveObjectLiteralExpression(
|
|
4559
4649
|
expression,
|
|
4560
4650
|
sourceFile,
|
|
4561
4651
|
namedExpressions,
|
|
4562
4652
|
namedPropertyAliases,
|
|
4563
4653
|
seenIdentifiers,
|
|
4564
|
-
seenFunctions
|
|
4654
|
+
seenFunctions,
|
|
4655
|
+
depth + 1
|
|
4565
4656
|
);
|
|
4566
4657
|
if (!resolvedObjectLiteral) return null;
|
|
4567
4658
|
for (const propertyName of propertyPath) {
|
|
@@ -4577,13 +4668,15 @@ function resolveObjectLiteralExpressionAtPropertyPath(expression, sourceFile, pr
|
|
|
4577
4668
|
resolvedObjectLiteral.namedExpressions,
|
|
4578
4669
|
namedPropertyAliases,
|
|
4579
4670
|
seenIdentifiers,
|
|
4580
|
-
seenFunctions
|
|
4671
|
+
seenFunctions,
|
|
4672
|
+
depth + 1
|
|
4581
4673
|
);
|
|
4582
4674
|
if (!resolvedObjectLiteral) return null;
|
|
4583
4675
|
}
|
|
4584
4676
|
return resolvedObjectLiteral;
|
|
4585
4677
|
}
|
|
4586
|
-
function resolveReturnedObjectLiteralExpression(functionLike, args, sourceFile, namedExpressions, namedPropertyAliases, seenIdentifiers, seenFunctions) {
|
|
4678
|
+
function resolveReturnedObjectLiteralExpression(functionLike, args, sourceFile, namedExpressions, namedPropertyAliases, seenIdentifiers, seenFunctions, depth = 0) {
|
|
4679
|
+
if (depth > MAX_OPEN_REDIRECT_RESOLUTION_DEPTH) return null;
|
|
4587
4680
|
const functionKey = getFunctionLikeCacheKey(functionLike);
|
|
4588
4681
|
if (seenFunctions.has(functionKey)) return null;
|
|
4589
4682
|
seenFunctions.add(functionKey);
|
|
@@ -4595,7 +4688,8 @@ function resolveReturnedObjectLiteralExpression(functionLike, args, sourceFile,
|
|
|
4595
4688
|
boundExpressions,
|
|
4596
4689
|
namedPropertyAliases,
|
|
4597
4690
|
new Set(seenIdentifiers),
|
|
4598
|
-
seenFunctions
|
|
4691
|
+
seenFunctions,
|
|
4692
|
+
depth + 1
|
|
4599
4693
|
);
|
|
4600
4694
|
if (objectLiteral) {
|
|
4601
4695
|
seenFunctions.delete(functionKey);
|
|
@@ -4605,8 +4699,9 @@ function resolveReturnedObjectLiteralExpression(functionLike, args, sourceFile,
|
|
|
4605
4699
|
seenFunctions.delete(functionKey);
|
|
4606
4700
|
return null;
|
|
4607
4701
|
}
|
|
4608
|
-
function resolveObjectLiteralExpression(expression, sourceFile, namedExpressions, namedPropertyAliases, seenIdentifiers, seenFunctions = /* @__PURE__ */ new Set()) {
|
|
4702
|
+
function resolveObjectLiteralExpression(expression, sourceFile, namedExpressions, namedPropertyAliases, seenIdentifiers, seenFunctions = /* @__PURE__ */ new Set(), depth = 0) {
|
|
4609
4703
|
if (!expression) return null;
|
|
4704
|
+
if (depth > MAX_OPEN_REDIRECT_RESOLUTION_DEPTH) return null;
|
|
4610
4705
|
if (ts.isParenthesizedExpression(expression) || ts.isAsExpression(expression) || ts.isTypeAssertionExpression(expression) || ts.isNonNullExpression(expression)) {
|
|
4611
4706
|
return resolveObjectLiteralExpression(
|
|
4612
4707
|
expression.expression,
|
|
@@ -4614,7 +4709,8 @@ function resolveObjectLiteralExpression(expression, sourceFile, namedExpressions
|
|
|
4614
4709
|
namedExpressions,
|
|
4615
4710
|
namedPropertyAliases,
|
|
4616
4711
|
seenIdentifiers,
|
|
4617
|
-
seenFunctions
|
|
4712
|
+
seenFunctions,
|
|
4713
|
+
depth + 1
|
|
4618
4714
|
);
|
|
4619
4715
|
}
|
|
4620
4716
|
if (ts.isObjectLiteralExpression(expression)) {
|
|
@@ -4631,7 +4727,8 @@ function resolveObjectLiteralExpression(expression, sourceFile, namedExpressions
|
|
|
4631
4727
|
namedExpressions,
|
|
4632
4728
|
namedPropertyAliases,
|
|
4633
4729
|
seenIdentifiers,
|
|
4634
|
-
seenFunctions
|
|
4730
|
+
seenFunctions,
|
|
4731
|
+
depth + 1
|
|
4635
4732
|
);
|
|
4636
4733
|
seenIdentifiers.delete(expression.text);
|
|
4637
4734
|
return result2;
|
|
@@ -4646,7 +4743,8 @@ function resolveObjectLiteralExpression(expression, sourceFile, namedExpressions
|
|
|
4646
4743
|
namedExpressions,
|
|
4647
4744
|
namedPropertyAliases,
|
|
4648
4745
|
seenIdentifiers,
|
|
4649
|
-
seenFunctions
|
|
4746
|
+
seenFunctions,
|
|
4747
|
+
depth + 1
|
|
4650
4748
|
);
|
|
4651
4749
|
seenIdentifiers.delete(expression.text);
|
|
4652
4750
|
return result;
|
|
@@ -4657,7 +4755,9 @@ function resolveObjectLiteralExpression(expression, sourceFile, namedExpressions
|
|
|
4657
4755
|
sourceFile,
|
|
4658
4756
|
namedExpressions,
|
|
4659
4757
|
namedPropertyAliases,
|
|
4660
|
-
|
|
4758
|
+
new Set(seenIdentifiers),
|
|
4759
|
+
seenFunctions,
|
|
4760
|
+
depth + 1
|
|
4661
4761
|
);
|
|
4662
4762
|
if (!functionResolution) return null;
|
|
4663
4763
|
return resolveReturnedObjectLiteralExpression(
|
|
@@ -4667,7 +4767,8 @@ function resolveObjectLiteralExpression(expression, sourceFile, namedExpressions
|
|
|
4667
4767
|
functionResolution.namedExpressions,
|
|
4668
4768
|
namedPropertyAliases,
|
|
4669
4769
|
seenIdentifiers,
|
|
4670
|
-
seenFunctions
|
|
4770
|
+
seenFunctions,
|
|
4771
|
+
depth + 1
|
|
4671
4772
|
);
|
|
4672
4773
|
}
|
|
4673
4774
|
if (isMemberAccessExpression(expression)) {
|
|
@@ -4680,7 +4781,8 @@ function resolveObjectLiteralExpression(expression, sourceFile, namedExpressions
|
|
|
4680
4781
|
namedExpressions,
|
|
4681
4782
|
namedPropertyAliases,
|
|
4682
4783
|
seenIdentifiers,
|
|
4683
|
-
seenFunctions
|
|
4784
|
+
seenFunctions,
|
|
4785
|
+
depth + 1
|
|
4684
4786
|
);
|
|
4685
4787
|
}
|
|
4686
4788
|
return null;
|
|
@@ -4699,7 +4801,8 @@ function findFunctionLikeOnObjectLiteral(objectLiteral, propertyName, namedFunct
|
|
|
4699
4801
|
}
|
|
4700
4802
|
return null;
|
|
4701
4803
|
}
|
|
4702
|
-
function resolveReturnedTrackedFunctionLike(functionLike, args, sourceFile, namedExpressions, namedPropertyAliases, seenIdentifiers, seenFunctions) {
|
|
4804
|
+
function resolveReturnedTrackedFunctionLike(functionLike, args, sourceFile, namedExpressions, namedPropertyAliases, seenIdentifiers, seenFunctions, depth = 0) {
|
|
4805
|
+
if (depth > MAX_OPEN_REDIRECT_RESOLUTION_DEPTH) return null;
|
|
4703
4806
|
const functionKey = getFunctionLikeCacheKey(functionLike);
|
|
4704
4807
|
if (seenFunctions.has(functionKey)) return null;
|
|
4705
4808
|
const nextSeenFunctions = new Set(seenFunctions);
|
|
@@ -4712,14 +4815,16 @@ function resolveReturnedTrackedFunctionLike(functionLike, args, sourceFile, name
|
|
|
4712
4815
|
boundExpressions,
|
|
4713
4816
|
namedPropertyAliases,
|
|
4714
4817
|
new Set(seenIdentifiers),
|
|
4715
|
-
nextSeenFunctions
|
|
4818
|
+
nextSeenFunctions,
|
|
4819
|
+
depth + 1
|
|
4716
4820
|
);
|
|
4717
4821
|
if (result) return result;
|
|
4718
4822
|
}
|
|
4719
4823
|
return null;
|
|
4720
4824
|
}
|
|
4721
|
-
function resolveTrackedOpenRedirectFunctionLike(expression, sourceFile, namedExpressions, namedPropertyAliases, seenIdentifiers, seenFunctions = /* @__PURE__ */ new Set()) {
|
|
4825
|
+
function resolveTrackedOpenRedirectFunctionLike(expression, sourceFile, namedExpressions, namedPropertyAliases, seenIdentifiers, seenFunctions = /* @__PURE__ */ new Set(), depth = 0) {
|
|
4722
4826
|
if (!expression) return null;
|
|
4827
|
+
if (depth > MAX_OPEN_REDIRECT_RESOLUTION_DEPTH) return null;
|
|
4723
4828
|
const namedFunctions = getCachedNamedFunctionLikeDeclarations(sourceFile);
|
|
4724
4829
|
const directFunctionLike = resolveFunctionLikeHandler(expression, namedFunctions);
|
|
4725
4830
|
if (directFunctionLike) {
|
|
@@ -4732,7 +4837,8 @@ function resolveTrackedOpenRedirectFunctionLike(expression, sourceFile, namedExp
|
|
|
4732
4837
|
namedExpressions,
|
|
4733
4838
|
namedPropertyAliases,
|
|
4734
4839
|
seenIdentifiers,
|
|
4735
|
-
seenFunctions
|
|
4840
|
+
seenFunctions,
|
|
4841
|
+
depth + 1
|
|
4736
4842
|
);
|
|
4737
4843
|
}
|
|
4738
4844
|
if (isCallLikeExpression(expression)) {
|
|
@@ -4741,8 +4847,9 @@ function resolveTrackedOpenRedirectFunctionLike(expression, sourceFile, namedExp
|
|
|
4741
4847
|
sourceFile,
|
|
4742
4848
|
namedExpressions,
|
|
4743
4849
|
namedPropertyAliases,
|
|
4744
|
-
|
|
4745
|
-
seenFunctions
|
|
4850
|
+
new Set(seenIdentifiers),
|
|
4851
|
+
seenFunctions,
|
|
4852
|
+
depth + 1
|
|
4746
4853
|
);
|
|
4747
4854
|
if (!functionResolution) return null;
|
|
4748
4855
|
return resolveReturnedTrackedFunctionLike(
|
|
@@ -4752,7 +4859,8 @@ function resolveTrackedOpenRedirectFunctionLike(expression, sourceFile, namedExp
|
|
|
4752
4859
|
functionResolution.namedExpressions,
|
|
4753
4860
|
namedPropertyAliases,
|
|
4754
4861
|
seenIdentifiers,
|
|
4755
|
-
seenFunctions
|
|
4862
|
+
seenFunctions,
|
|
4863
|
+
depth + 1
|
|
4756
4864
|
);
|
|
4757
4865
|
}
|
|
4758
4866
|
if (ts.isIdentifier(expression)) {
|
|
@@ -4766,7 +4874,8 @@ function resolveTrackedOpenRedirectFunctionLike(expression, sourceFile, namedExp
|
|
|
4766
4874
|
namedExpressions,
|
|
4767
4875
|
namedPropertyAliases,
|
|
4768
4876
|
seenIdentifiers,
|
|
4769
|
-
seenFunctions
|
|
4877
|
+
seenFunctions,
|
|
4878
|
+
depth + 1
|
|
4770
4879
|
);
|
|
4771
4880
|
seenIdentifiers.delete(expression.text);
|
|
4772
4881
|
if (result) return result;
|
|
@@ -4779,8 +4888,9 @@ function resolveTrackedOpenRedirectFunctionLike(expression, sourceFile, namedExp
|
|
|
4779
4888
|
propertyAlias.propertyPath.slice(0, -1),
|
|
4780
4889
|
namedExpressions,
|
|
4781
4890
|
namedPropertyAliases,
|
|
4782
|
-
|
|
4783
|
-
|
|
4891
|
+
new Set(seenIdentifiers),
|
|
4892
|
+
seenFunctions,
|
|
4893
|
+
depth + 1
|
|
4784
4894
|
);
|
|
4785
4895
|
if (!resolvedObjectLiteral2) return null;
|
|
4786
4896
|
const functionLike2 = findFunctionLikeOnObjectLiteral(
|
|
@@ -4800,8 +4910,9 @@ function resolveTrackedOpenRedirectFunctionLike(expression, sourceFile, namedExp
|
|
|
4800
4910
|
sourceFile,
|
|
4801
4911
|
namedExpressions,
|
|
4802
4912
|
namedPropertyAliases,
|
|
4803
|
-
|
|
4804
|
-
|
|
4913
|
+
new Set(seenIdentifiers),
|
|
4914
|
+
seenFunctions,
|
|
4915
|
+
depth + 1
|
|
4805
4916
|
);
|
|
4806
4917
|
if (!resolvedObjectLiteral) return null;
|
|
4807
4918
|
const functionLike = findFunctionLikeOnObjectLiteral(
|
|
@@ -10218,7 +10329,8 @@ function critiqueSource({
|
|
|
10218
10329
|
code,
|
|
10219
10330
|
reviewPack = null,
|
|
10220
10331
|
packManifest = null,
|
|
10221
|
-
treatmentsCss = ""
|
|
10332
|
+
treatmentsCss = "",
|
|
10333
|
+
adoptionMode = null
|
|
10222
10334
|
}) {
|
|
10223
10335
|
const codeLower = code.toLowerCase();
|
|
10224
10336
|
const astSignals = analyzeAstSignals(filePath, code);
|
|
@@ -10226,67 +10338,92 @@ function critiqueSource({
|
|
|
10226
10338
|
const findings = [];
|
|
10227
10339
|
const scores = [];
|
|
10228
10340
|
const antiPatternIds = new Set(reviewPack?.antiPatterns.map((entry) => entry.id) ?? []);
|
|
10341
|
+
const isContractOnly = adoptionMode === "contract-only";
|
|
10229
10342
|
const usedTreatments = TREATMENT_CLASSES.filter((token) => code.includes(token));
|
|
10230
|
-
|
|
10231
|
-
(
|
|
10232
|
-
|
|
10233
|
-
|
|
10234
|
-
|
|
10235
|
-
|
|
10236
|
-
|
|
10237
|
-
|
|
10238
|
-
|
|
10239
|
-
|
|
10240
|
-
|
|
10241
|
-
|
|
10242
|
-
|
|
10243
|
-
|
|
10244
|
-
|
|
10245
|
-
|
|
10246
|
-
|
|
10247
|
-
|
|
10248
|
-
|
|
10249
|
-
|
|
10250
|
-
|
|
10251
|
-
|
|
10252
|
-
|
|
10253
|
-
|
|
10254
|
-
|
|
10255
|
-
|
|
10256
|
-
|
|
10257
|
-
|
|
10343
|
+
if (isContractOnly) {
|
|
10344
|
+
scores.push({
|
|
10345
|
+
category: "Styling Authority",
|
|
10346
|
+
focusArea: "treatment-usage",
|
|
10347
|
+
score: 3,
|
|
10348
|
+
details: "Contract-only adoption does not require Decantr d-* treatment classes in source files.",
|
|
10349
|
+
suggestions: [
|
|
10350
|
+
"Codify project-owned component variants and local rules when button/card/surface drift needs mechanical enforcement."
|
|
10351
|
+
]
|
|
10352
|
+
});
|
|
10353
|
+
} else {
|
|
10354
|
+
const treatmentSuggestions = TREATMENT_CLASSES.filter((token) => !code.includes(token)).map(
|
|
10355
|
+
(token) => `Consider using \`${token}\` where appropriate.`
|
|
10356
|
+
);
|
|
10357
|
+
scores.push({
|
|
10358
|
+
category: "Treatment Usage",
|
|
10359
|
+
focusArea: "treatment-usage",
|
|
10360
|
+
score: scoreRatio(usedTreatments.length, TREATMENT_CLASSES.length),
|
|
10361
|
+
details: `${usedTreatments.length}/${TREATMENT_CLASSES.length} base treatments used: ${usedTreatments.join(", ") || "none"}`,
|
|
10362
|
+
suggestions: treatmentSuggestions
|
|
10363
|
+
});
|
|
10364
|
+
if (focusAreas.includes("treatment-usage") && usedTreatments.length === 0) {
|
|
10365
|
+
findings.push(
|
|
10366
|
+
makeFinding({
|
|
10367
|
+
id: "treatment-usage-missing",
|
|
10368
|
+
category: "Treatment Usage",
|
|
10369
|
+
severity: resolveSeverityFromChecks(reviewPack, "warn", [
|
|
10370
|
+
"page-pattern-contract",
|
|
10371
|
+
"section-pattern-coverage"
|
|
10372
|
+
]),
|
|
10373
|
+
message: "No Decantr treatment classes were detected in the reviewed file.",
|
|
10374
|
+
evidence: [
|
|
10375
|
+
filePath,
|
|
10376
|
+
"Expected tokens include d-interactive, d-surface, d-data, d-control, d-section, d-annotation, d-label."
|
|
10377
|
+
],
|
|
10378
|
+
file: filePath,
|
|
10379
|
+
suggestedFix: "Apply the compiled treatment vocabulary instead of hand-rolled utility styling."
|
|
10380
|
+
})
|
|
10381
|
+
);
|
|
10382
|
+
}
|
|
10258
10383
|
}
|
|
10259
10384
|
const decoratorNames = buildDecoratorInventory(treatmentsCss);
|
|
10260
10385
|
const usedDecorators = decoratorNames.filter((name) => code.includes(name));
|
|
10261
10386
|
const usesCssVars = code.includes("var(--");
|
|
10262
|
-
|
|
10263
|
-
|
|
10264
|
-
|
|
10265
|
-
|
|
10266
|
-
|
|
10267
|
-
|
|
10268
|
-
|
|
10269
|
-
|
|
10270
|
-
|
|
10271
|
-
|
|
10272
|
-
|
|
10273
|
-
|
|
10274
|
-
|
|
10275
|
-
|
|
10276
|
-
|
|
10277
|
-
|
|
10278
|
-
|
|
10279
|
-
|
|
10280
|
-
]
|
|
10281
|
-
|
|
10282
|
-
|
|
10283
|
-
|
|
10284
|
-
|
|
10285
|
-
|
|
10286
|
-
|
|
10287
|
-
|
|
10288
|
-
|
|
10289
|
-
|
|
10387
|
+
if (isContractOnly) {
|
|
10388
|
+
scores.push({
|
|
10389
|
+
category: "Theme Consistency",
|
|
10390
|
+
focusArea: "theme-consistency",
|
|
10391
|
+
score: usesCssVars ? 4 : 3,
|
|
10392
|
+
details: `Contract-only mode: Decantr decorators are not required; CSS vars: ${usesCssVars ? "yes" : "no"}.`,
|
|
10393
|
+
suggestions: [
|
|
10394
|
+
"Use the app design system, Tailwind theme, Sass variables, component variants, or accepted local rules as the styling authority."
|
|
10395
|
+
]
|
|
10396
|
+
});
|
|
10397
|
+
} else {
|
|
10398
|
+
scores.push({
|
|
10399
|
+
category: "Theme Consistency",
|
|
10400
|
+
focusArea: "theme-consistency",
|
|
10401
|
+
score: decoratorNames.length > 0 ? scoreRatio((usedDecorators.length > 0 ? 1 : 0) + (usesCssVars ? 1 : 0), 2) : usesCssVars ? 4 : 2,
|
|
10402
|
+
details: `Decorators used: ${usedDecorators.join(", ") || "none"}; CSS vars: ${usesCssVars ? "yes" : "no"}`,
|
|
10403
|
+
suggestions: [
|
|
10404
|
+
...!usesCssVars ? ["Prefer CSS variable references over hardcoded visual values."] : [],
|
|
10405
|
+
...decoratorNames.length > 0 && usedDecorators.length === 0 ? ["Use theme decorators from treatments.css when they fit the component intent."] : []
|
|
10406
|
+
]
|
|
10407
|
+
});
|
|
10408
|
+
if (focusAreas.includes("theme-consistency") && !usesCssVars && usedDecorators.length === 0) {
|
|
10409
|
+
findings.push(
|
|
10410
|
+
makeFinding({
|
|
10411
|
+
id: "theme-consistency-weak",
|
|
10412
|
+
category: "Theme Consistency",
|
|
10413
|
+
severity: resolveSeverityFromChecks(reviewPack, "warn", [
|
|
10414
|
+
"theme-consistency",
|
|
10415
|
+
"mutation-theme-contract"
|
|
10416
|
+
]),
|
|
10417
|
+
message: "The file does not appear to use theme decorators or CSS variables from the compiled contract.",
|
|
10418
|
+
evidence: [
|
|
10419
|
+
filePath,
|
|
10420
|
+
`Decorators available: ${decoratorNames.slice(0, 5).join(", ") || "none"}`
|
|
10421
|
+
],
|
|
10422
|
+
file: filePath,
|
|
10423
|
+
suggestedFix: "Anchor styling to tokens.css and treatments.css instead of local hardcoded values."
|
|
10424
|
+
})
|
|
10425
|
+
);
|
|
10426
|
+
}
|
|
10290
10427
|
}
|
|
10291
10428
|
const hasAria = codeLower.includes("aria-") || codeLower.includes("role=");
|
|
10292
10429
|
const hasFocus = codeLower.includes("focus-visible") || codeLower.includes("focusvisible");
|
|
@@ -11216,7 +11353,9 @@ function critiqueSource({
|
|
|
11216
11353
|
),
|
|
11217
11354
|
details: reviewPack ? `Compiled review contract covers ${knownRoutes} routes. Placeholder navigation targets: ${placeholderNavigationTargets}.` : packManifest ? `Pack manifest is available for ${knownRoutes} pages, but the review pack is missing. Placeholder navigation targets: ${placeholderNavigationTargets}.` : `No compiled route context was available during critique. Placeholder navigation targets: ${placeholderNavigationTargets}.`,
|
|
11218
11355
|
suggestions: [
|
|
11219
|
-
...reviewPack ? [] : [
|
|
11356
|
+
...reviewPack ? [] : [
|
|
11357
|
+
"Run `decantr registry compile-packs decantr.essence.json --write-context` so critique starts from a compiled review contract."
|
|
11358
|
+
],
|
|
11220
11359
|
...placeholderNavigationTargets > 0 ? [
|
|
11221
11360
|
"Replace placeholder href/to targets with real route destinations from the compiled contract."
|
|
11222
11361
|
] : []
|
|
@@ -11272,7 +11411,7 @@ function critiqueSource({
|
|
|
11272
11411
|
`Disallowed inline style attributes: ${astSignals.inlineStyleAttributeCount}`
|
|
11273
11412
|
],
|
|
11274
11413
|
file: filePath,
|
|
11275
|
-
suggestedFix: "Replace static inline visual values with treatments, decorators, and CSS variables from the compiled contract. Keep inline style only for Decantr CSS-variable writes and truly dynamic geometry."
|
|
11414
|
+
suggestedFix: isContractOnly ? "Replace static inline visual values with the project design system, accepted component variants, or local style rules. Keep inline style only for truly dynamic geometry." : "Replace static inline visual values with treatments, decorators, and CSS variables from the compiled contract. Keep inline style only for Decantr CSS-variable writes and truly dynamic geometry."
|
|
11276
11415
|
})
|
|
11277
11416
|
);
|
|
11278
11417
|
}
|
|
@@ -11289,7 +11428,7 @@ function critiqueSource({
|
|
|
11289
11428
|
message: "Component-scoped style tags or dynamic style elements were detected in the reviewed file.",
|
|
11290
11429
|
evidence: [filePath, `Component style tag signals: ${componentStyleTagSignals}`],
|
|
11291
11430
|
file: filePath,
|
|
11292
|
-
suggestedFix: "Move shared keyframes, media queries, and visual rules into global.css or treatments.css so the file stays aligned with the Decantr layer contract."
|
|
11431
|
+
suggestedFix: isContractOnly ? "Move shared keyframes, media queries, and visual rules into the project stylesheet, component library, or accepted local pattern/rule manifest." : "Move shared keyframes, media queries, and visual rules into global.css or treatments.css so the file stays aligned with the Decantr layer contract."
|
|
11293
11432
|
})
|
|
11294
11433
|
);
|
|
11295
11434
|
}
|
|
@@ -11942,12 +12081,14 @@ async function critiqueFile(filePath, projectRoot) {
|
|
|
11942
12081
|
const treatmentsCss = readTextIfExists(join2(projectRoot, "src", "styles", "treatments.css"));
|
|
11943
12082
|
const reviewPack = loadReviewPack(projectRoot);
|
|
11944
12083
|
const packManifest = loadPackManifest(projectRoot);
|
|
12084
|
+
const adoptionMode = readProjectAdoptionMode(projectRoot);
|
|
11945
12085
|
return critiqueSource({
|
|
11946
12086
|
filePath: resolvedPath,
|
|
11947
12087
|
code,
|
|
11948
12088
|
reviewPack,
|
|
11949
12089
|
packManifest,
|
|
11950
|
-
treatmentsCss
|
|
12090
|
+
treatmentsCss,
|
|
12091
|
+
adoptionMode
|
|
11951
12092
|
});
|
|
11952
12093
|
}
|
|
11953
12094
|
export {
|
|
@@ -11956,6 +12097,7 @@ export {
|
|
|
11956
12097
|
VERIFICATION_SCHEMA_URLS,
|
|
11957
12098
|
auditBuiltDist,
|
|
11958
12099
|
auditProject,
|
|
12100
|
+
collectMissingPackManifestFiles,
|
|
11959
12101
|
createContractAssertions,
|
|
11960
12102
|
createEvidenceBundle,
|
|
11961
12103
|
critiqueFile,
|