@decantr/verifier 2.3.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 +2 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.js +238 -97
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -896,7 +896,7 @@ function createContractAssertions(projectRoot, audit) {
|
|
|
896
896
|
status: packManifest ? "passed" : "failed",
|
|
897
897
|
message: packManifest ? "Compiled execution pack manifest is present." : "Compiled execution pack manifest is missing.",
|
|
898
898
|
evidence: [redactEvidenceText(projectRoot, join2(contextDir, "pack-manifest.json"))],
|
|
899
|
-
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."
|
|
900
900
|
})
|
|
901
901
|
);
|
|
902
902
|
assertions.push(
|
|
@@ -908,7 +908,7 @@ function createContractAssertions(projectRoot, audit) {
|
|
|
908
908
|
status: audit?.reviewPack ? "passed" : "failed",
|
|
909
909
|
message: audit?.reviewPack ? "Compiled review pack is present." : "Compiled review pack is missing.",
|
|
910
910
|
evidence: [redactEvidenceText(projectRoot, join2(contextDir, "review-pack.json"))],
|
|
911
|
-
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."
|
|
912
912
|
})
|
|
913
913
|
);
|
|
914
914
|
const tokensPath = join2(projectRoot, "src", "styles", "tokens.css");
|
|
@@ -1043,6 +1043,71 @@ function loadPackManifest(projectRoot) {
|
|
|
1043
1043
|
join2(projectRoot, ".decantr", "context", "pack-manifest.json")
|
|
1044
1044
|
);
|
|
1045
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
|
+
}
|
|
1046
1111
|
function readTextIfExists(path) {
|
|
1047
1112
|
try {
|
|
1048
1113
|
return existsSync2(path) ? readFileSync2(path, "utf-8") : "";
|
|
@@ -1050,6 +1115,11 @@ function readTextIfExists(path) {
|
|
|
1050
1115
|
return "";
|
|
1051
1116
|
}
|
|
1052
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
|
+
}
|
|
1053
1123
|
function createSourceAuditBucket() {
|
|
1054
1124
|
return { count: 0, files: [] };
|
|
1055
1125
|
}
|
|
@@ -2556,23 +2626,24 @@ function appendRuntimeAuditFindings(findings, runtimeAudit, projectRoot, topolog
|
|
|
2556
2626
|
);
|
|
2557
2627
|
}
|
|
2558
2628
|
}
|
|
2559
|
-
function appendSourceAuditFindings(findings, sourceAudit, essence, reviewPack) {
|
|
2629
|
+
function appendSourceAuditFindings(findings, sourceAudit, essence, reviewPack, adoptionMode) {
|
|
2560
2630
|
if (sourceAudit.filesChecked === 0) {
|
|
2561
2631
|
return;
|
|
2562
2632
|
}
|
|
2633
|
+
const isContractOnly = adoptionMode === "contract-only";
|
|
2563
2634
|
if (sourceAudit.inlineStyles.count > 0) {
|
|
2564
2635
|
findings.push(
|
|
2565
2636
|
makeFinding({
|
|
2566
2637
|
id: "source-inline-styles-present",
|
|
2567
2638
|
category: "Source Audit",
|
|
2568
2639
|
severity: "warn",
|
|
2569
|
-
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.",
|
|
2570
2641
|
evidence: buildSourceAuditEvidence(
|
|
2571
2642
|
sourceAudit,
|
|
2572
2643
|
sourceAudit.inlineStyles,
|
|
2573
2644
|
"Disallowed inline style attributes"
|
|
2574
2645
|
),
|
|
2575
|
-
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."
|
|
2576
2647
|
})
|
|
2577
2648
|
);
|
|
2578
2649
|
}
|
|
@@ -2582,17 +2653,17 @@ function appendSourceAuditFindings(findings, sourceAudit, essence, reviewPack) {
|
|
|
2582
2653
|
id: "source-component-style-tags-present",
|
|
2583
2654
|
category: "Source Audit",
|
|
2584
2655
|
severity: "warn",
|
|
2585
|
-
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.",
|
|
2586
2657
|
evidence: buildSourceAuditEvidence(
|
|
2587
2658
|
sourceAudit,
|
|
2588
2659
|
sourceAudit.componentStyleTags,
|
|
2589
2660
|
"Component-level style tag signals"
|
|
2590
2661
|
),
|
|
2591
|
-
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."
|
|
2592
2663
|
})
|
|
2593
2664
|
);
|
|
2594
2665
|
}
|
|
2595
|
-
if (sourceAudit.localCssRuntimeSignals.count > 0) {
|
|
2666
|
+
if (!isContractOnly && sourceAudit.localCssRuntimeSignals.count > 0) {
|
|
2596
2667
|
findings.push(
|
|
2597
2668
|
makeFinding({
|
|
2598
2669
|
id: "source-local-css-runtime-stub-present",
|
|
@@ -3695,6 +3766,7 @@ async function auditProject(projectRoot) {
|
|
|
3695
3766
|
const findings = [];
|
|
3696
3767
|
const reviewPack = loadReviewPack(projectRoot);
|
|
3697
3768
|
const packManifest = loadPackManifest(projectRoot);
|
|
3769
|
+
const adoptionMode = readProjectAdoptionMode(projectRoot);
|
|
3698
3770
|
const runtimeAudit = emptyRuntimeAudit();
|
|
3699
3771
|
if (!existsSync2(essencePath)) {
|
|
3700
3772
|
findings.push(
|
|
@@ -3775,10 +3847,25 @@ async function auditProject(projectRoot) {
|
|
|
3775
3847
|
severity: "warn",
|
|
3776
3848
|
message: "Compiled execution pack manifest is missing.",
|
|
3777
3849
|
evidence: [join2(projectRoot, ".decantr", "context", "pack-manifest.json")],
|
|
3778
|
-
suggestedFix: "Run `decantr
|
|
3850
|
+
suggestedFix: "Run `decantr registry compile-packs decantr.essence.json --write-context` to hydrate scaffold, review, mutation, section, and page packs."
|
|
3779
3851
|
})
|
|
3780
3852
|
);
|
|
3781
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
|
+
}
|
|
3782
3869
|
if (!packManifest.scaffold) {
|
|
3783
3870
|
findings.push(
|
|
3784
3871
|
makeFinding({
|
|
@@ -3811,7 +3898,7 @@ async function auditProject(projectRoot) {
|
|
|
3811
3898
|
severity: "info",
|
|
3812
3899
|
message: "No mutation packs were found in the manifest.",
|
|
3813
3900
|
evidence: ["pack-manifest.json"],
|
|
3814
|
-
suggestedFix: "Run `decantr
|
|
3901
|
+
suggestedFix: "Run `decantr registry compile-packs decantr.essence.json --write-context` to hydrate mutation task packs."
|
|
3815
3902
|
})
|
|
3816
3903
|
);
|
|
3817
3904
|
}
|
|
@@ -3824,7 +3911,7 @@ async function auditProject(projectRoot) {
|
|
|
3824
3911
|
severity: "warn",
|
|
3825
3912
|
message: "The compiled review pack file is missing.",
|
|
3826
3913
|
evidence: [join2(projectRoot, ".decantr", "context", "review-pack.json")],
|
|
3827
|
-
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."
|
|
3828
3915
|
})
|
|
3829
3916
|
);
|
|
3830
3917
|
}
|
|
@@ -3838,7 +3925,7 @@ async function auditProject(projectRoot) {
|
|
|
3838
3925
|
summarizeTopology(essence, reviewPack),
|
|
3839
3926
|
sourceAudit
|
|
3840
3927
|
);
|
|
3841
|
-
appendSourceAuditFindings(findings, sourceAudit, essence, reviewPack);
|
|
3928
|
+
appendSourceAuditFindings(findings, sourceAudit, essence, reviewPack, adoptionMode);
|
|
3842
3929
|
appendStyleContractFindings(findings, styleAudit, essence);
|
|
3843
3930
|
const summary = {
|
|
3844
3931
|
errorCount: findings.filter((finding) => finding.severity === "error").length,
|
|
@@ -4555,14 +4642,17 @@ function getObjectLiteralPropertyExpression(objectLiteral, propertyName, namedEx
|
|
|
4555
4642
|
}
|
|
4556
4643
|
return null;
|
|
4557
4644
|
}
|
|
4558
|
-
|
|
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;
|
|
4559
4648
|
let resolvedObjectLiteral = resolveObjectLiteralExpression(
|
|
4560
4649
|
expression,
|
|
4561
4650
|
sourceFile,
|
|
4562
4651
|
namedExpressions,
|
|
4563
4652
|
namedPropertyAliases,
|
|
4564
4653
|
seenIdentifiers,
|
|
4565
|
-
seenFunctions
|
|
4654
|
+
seenFunctions,
|
|
4655
|
+
depth + 1
|
|
4566
4656
|
);
|
|
4567
4657
|
if (!resolvedObjectLiteral) return null;
|
|
4568
4658
|
for (const propertyName of propertyPath) {
|
|
@@ -4578,13 +4668,15 @@ function resolveObjectLiteralExpressionAtPropertyPath(expression, sourceFile, pr
|
|
|
4578
4668
|
resolvedObjectLiteral.namedExpressions,
|
|
4579
4669
|
namedPropertyAliases,
|
|
4580
4670
|
seenIdentifiers,
|
|
4581
|
-
seenFunctions
|
|
4671
|
+
seenFunctions,
|
|
4672
|
+
depth + 1
|
|
4582
4673
|
);
|
|
4583
4674
|
if (!resolvedObjectLiteral) return null;
|
|
4584
4675
|
}
|
|
4585
4676
|
return resolvedObjectLiteral;
|
|
4586
4677
|
}
|
|
4587
|
-
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;
|
|
4588
4680
|
const functionKey = getFunctionLikeCacheKey(functionLike);
|
|
4589
4681
|
if (seenFunctions.has(functionKey)) return null;
|
|
4590
4682
|
seenFunctions.add(functionKey);
|
|
@@ -4596,7 +4688,8 @@ function resolveReturnedObjectLiteralExpression(functionLike, args, sourceFile,
|
|
|
4596
4688
|
boundExpressions,
|
|
4597
4689
|
namedPropertyAliases,
|
|
4598
4690
|
new Set(seenIdentifiers),
|
|
4599
|
-
seenFunctions
|
|
4691
|
+
seenFunctions,
|
|
4692
|
+
depth + 1
|
|
4600
4693
|
);
|
|
4601
4694
|
if (objectLiteral) {
|
|
4602
4695
|
seenFunctions.delete(functionKey);
|
|
@@ -4606,8 +4699,9 @@ function resolveReturnedObjectLiteralExpression(functionLike, args, sourceFile,
|
|
|
4606
4699
|
seenFunctions.delete(functionKey);
|
|
4607
4700
|
return null;
|
|
4608
4701
|
}
|
|
4609
|
-
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) {
|
|
4610
4703
|
if (!expression) return null;
|
|
4704
|
+
if (depth > MAX_OPEN_REDIRECT_RESOLUTION_DEPTH) return null;
|
|
4611
4705
|
if (ts.isParenthesizedExpression(expression) || ts.isAsExpression(expression) || ts.isTypeAssertionExpression(expression) || ts.isNonNullExpression(expression)) {
|
|
4612
4706
|
return resolveObjectLiteralExpression(
|
|
4613
4707
|
expression.expression,
|
|
@@ -4615,7 +4709,8 @@ function resolveObjectLiteralExpression(expression, sourceFile, namedExpressions
|
|
|
4615
4709
|
namedExpressions,
|
|
4616
4710
|
namedPropertyAliases,
|
|
4617
4711
|
seenIdentifiers,
|
|
4618
|
-
seenFunctions
|
|
4712
|
+
seenFunctions,
|
|
4713
|
+
depth + 1
|
|
4619
4714
|
);
|
|
4620
4715
|
}
|
|
4621
4716
|
if (ts.isObjectLiteralExpression(expression)) {
|
|
@@ -4632,7 +4727,8 @@ function resolveObjectLiteralExpression(expression, sourceFile, namedExpressions
|
|
|
4632
4727
|
namedExpressions,
|
|
4633
4728
|
namedPropertyAliases,
|
|
4634
4729
|
seenIdentifiers,
|
|
4635
|
-
seenFunctions
|
|
4730
|
+
seenFunctions,
|
|
4731
|
+
depth + 1
|
|
4636
4732
|
);
|
|
4637
4733
|
seenIdentifiers.delete(expression.text);
|
|
4638
4734
|
return result2;
|
|
@@ -4647,7 +4743,8 @@ function resolveObjectLiteralExpression(expression, sourceFile, namedExpressions
|
|
|
4647
4743
|
namedExpressions,
|
|
4648
4744
|
namedPropertyAliases,
|
|
4649
4745
|
seenIdentifiers,
|
|
4650
|
-
seenFunctions
|
|
4746
|
+
seenFunctions,
|
|
4747
|
+
depth + 1
|
|
4651
4748
|
);
|
|
4652
4749
|
seenIdentifiers.delete(expression.text);
|
|
4653
4750
|
return result;
|
|
@@ -4658,7 +4755,9 @@ function resolveObjectLiteralExpression(expression, sourceFile, namedExpressions
|
|
|
4658
4755
|
sourceFile,
|
|
4659
4756
|
namedExpressions,
|
|
4660
4757
|
namedPropertyAliases,
|
|
4661
|
-
|
|
4758
|
+
new Set(seenIdentifiers),
|
|
4759
|
+
seenFunctions,
|
|
4760
|
+
depth + 1
|
|
4662
4761
|
);
|
|
4663
4762
|
if (!functionResolution) return null;
|
|
4664
4763
|
return resolveReturnedObjectLiteralExpression(
|
|
@@ -4668,7 +4767,8 @@ function resolveObjectLiteralExpression(expression, sourceFile, namedExpressions
|
|
|
4668
4767
|
functionResolution.namedExpressions,
|
|
4669
4768
|
namedPropertyAliases,
|
|
4670
4769
|
seenIdentifiers,
|
|
4671
|
-
seenFunctions
|
|
4770
|
+
seenFunctions,
|
|
4771
|
+
depth + 1
|
|
4672
4772
|
);
|
|
4673
4773
|
}
|
|
4674
4774
|
if (isMemberAccessExpression(expression)) {
|
|
@@ -4681,7 +4781,8 @@ function resolveObjectLiteralExpression(expression, sourceFile, namedExpressions
|
|
|
4681
4781
|
namedExpressions,
|
|
4682
4782
|
namedPropertyAliases,
|
|
4683
4783
|
seenIdentifiers,
|
|
4684
|
-
seenFunctions
|
|
4784
|
+
seenFunctions,
|
|
4785
|
+
depth + 1
|
|
4685
4786
|
);
|
|
4686
4787
|
}
|
|
4687
4788
|
return null;
|
|
@@ -4700,7 +4801,8 @@ function findFunctionLikeOnObjectLiteral(objectLiteral, propertyName, namedFunct
|
|
|
4700
4801
|
}
|
|
4701
4802
|
return null;
|
|
4702
4803
|
}
|
|
4703
|
-
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;
|
|
4704
4806
|
const functionKey = getFunctionLikeCacheKey(functionLike);
|
|
4705
4807
|
if (seenFunctions.has(functionKey)) return null;
|
|
4706
4808
|
const nextSeenFunctions = new Set(seenFunctions);
|
|
@@ -4713,14 +4815,16 @@ function resolveReturnedTrackedFunctionLike(functionLike, args, sourceFile, name
|
|
|
4713
4815
|
boundExpressions,
|
|
4714
4816
|
namedPropertyAliases,
|
|
4715
4817
|
new Set(seenIdentifiers),
|
|
4716
|
-
nextSeenFunctions
|
|
4818
|
+
nextSeenFunctions,
|
|
4819
|
+
depth + 1
|
|
4717
4820
|
);
|
|
4718
4821
|
if (result) return result;
|
|
4719
4822
|
}
|
|
4720
4823
|
return null;
|
|
4721
4824
|
}
|
|
4722
|
-
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) {
|
|
4723
4826
|
if (!expression) return null;
|
|
4827
|
+
if (depth > MAX_OPEN_REDIRECT_RESOLUTION_DEPTH) return null;
|
|
4724
4828
|
const namedFunctions = getCachedNamedFunctionLikeDeclarations(sourceFile);
|
|
4725
4829
|
const directFunctionLike = resolveFunctionLikeHandler(expression, namedFunctions);
|
|
4726
4830
|
if (directFunctionLike) {
|
|
@@ -4733,7 +4837,8 @@ function resolveTrackedOpenRedirectFunctionLike(expression, sourceFile, namedExp
|
|
|
4733
4837
|
namedExpressions,
|
|
4734
4838
|
namedPropertyAliases,
|
|
4735
4839
|
seenIdentifiers,
|
|
4736
|
-
seenFunctions
|
|
4840
|
+
seenFunctions,
|
|
4841
|
+
depth + 1
|
|
4737
4842
|
);
|
|
4738
4843
|
}
|
|
4739
4844
|
if (isCallLikeExpression(expression)) {
|
|
@@ -4742,8 +4847,9 @@ function resolveTrackedOpenRedirectFunctionLike(expression, sourceFile, namedExp
|
|
|
4742
4847
|
sourceFile,
|
|
4743
4848
|
namedExpressions,
|
|
4744
4849
|
namedPropertyAliases,
|
|
4745
|
-
|
|
4746
|
-
seenFunctions
|
|
4850
|
+
new Set(seenIdentifiers),
|
|
4851
|
+
seenFunctions,
|
|
4852
|
+
depth + 1
|
|
4747
4853
|
);
|
|
4748
4854
|
if (!functionResolution) return null;
|
|
4749
4855
|
return resolveReturnedTrackedFunctionLike(
|
|
@@ -4753,7 +4859,8 @@ function resolveTrackedOpenRedirectFunctionLike(expression, sourceFile, namedExp
|
|
|
4753
4859
|
functionResolution.namedExpressions,
|
|
4754
4860
|
namedPropertyAliases,
|
|
4755
4861
|
seenIdentifiers,
|
|
4756
|
-
seenFunctions
|
|
4862
|
+
seenFunctions,
|
|
4863
|
+
depth + 1
|
|
4757
4864
|
);
|
|
4758
4865
|
}
|
|
4759
4866
|
if (ts.isIdentifier(expression)) {
|
|
@@ -4767,7 +4874,8 @@ function resolveTrackedOpenRedirectFunctionLike(expression, sourceFile, namedExp
|
|
|
4767
4874
|
namedExpressions,
|
|
4768
4875
|
namedPropertyAliases,
|
|
4769
4876
|
seenIdentifiers,
|
|
4770
|
-
seenFunctions
|
|
4877
|
+
seenFunctions,
|
|
4878
|
+
depth + 1
|
|
4771
4879
|
);
|
|
4772
4880
|
seenIdentifiers.delete(expression.text);
|
|
4773
4881
|
if (result) return result;
|
|
@@ -4780,8 +4888,9 @@ function resolveTrackedOpenRedirectFunctionLike(expression, sourceFile, namedExp
|
|
|
4780
4888
|
propertyAlias.propertyPath.slice(0, -1),
|
|
4781
4889
|
namedExpressions,
|
|
4782
4890
|
namedPropertyAliases,
|
|
4783
|
-
|
|
4784
|
-
|
|
4891
|
+
new Set(seenIdentifiers),
|
|
4892
|
+
seenFunctions,
|
|
4893
|
+
depth + 1
|
|
4785
4894
|
);
|
|
4786
4895
|
if (!resolvedObjectLiteral2) return null;
|
|
4787
4896
|
const functionLike2 = findFunctionLikeOnObjectLiteral(
|
|
@@ -4801,8 +4910,9 @@ function resolveTrackedOpenRedirectFunctionLike(expression, sourceFile, namedExp
|
|
|
4801
4910
|
sourceFile,
|
|
4802
4911
|
namedExpressions,
|
|
4803
4912
|
namedPropertyAliases,
|
|
4804
|
-
|
|
4805
|
-
|
|
4913
|
+
new Set(seenIdentifiers),
|
|
4914
|
+
seenFunctions,
|
|
4915
|
+
depth + 1
|
|
4806
4916
|
);
|
|
4807
4917
|
if (!resolvedObjectLiteral) return null;
|
|
4808
4918
|
const functionLike = findFunctionLikeOnObjectLiteral(
|
|
@@ -10219,7 +10329,8 @@ function critiqueSource({
|
|
|
10219
10329
|
code,
|
|
10220
10330
|
reviewPack = null,
|
|
10221
10331
|
packManifest = null,
|
|
10222
|
-
treatmentsCss = ""
|
|
10332
|
+
treatmentsCss = "",
|
|
10333
|
+
adoptionMode = null
|
|
10223
10334
|
}) {
|
|
10224
10335
|
const codeLower = code.toLowerCase();
|
|
10225
10336
|
const astSignals = analyzeAstSignals(filePath, code);
|
|
@@ -10227,67 +10338,92 @@ function critiqueSource({
|
|
|
10227
10338
|
const findings = [];
|
|
10228
10339
|
const scores = [];
|
|
10229
10340
|
const antiPatternIds = new Set(reviewPack?.antiPatterns.map((entry) => entry.id) ?? []);
|
|
10341
|
+
const isContractOnly = adoptionMode === "contract-only";
|
|
10230
10342
|
const usedTreatments = TREATMENT_CLASSES.filter((token) => code.includes(token));
|
|
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
|
-
|
|
10258
|
-
|
|
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
|
+
}
|
|
10259
10383
|
}
|
|
10260
10384
|
const decoratorNames = buildDecoratorInventory(treatmentsCss);
|
|
10261
10385
|
const usedDecorators = decoratorNames.filter((name) => code.includes(name));
|
|
10262
10386
|
const usesCssVars = code.includes("var(--");
|
|
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
|
-
|
|
10290
|
-
|
|
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
|
+
}
|
|
10291
10427
|
}
|
|
10292
10428
|
const hasAria = codeLower.includes("aria-") || codeLower.includes("role=");
|
|
10293
10429
|
const hasFocus = codeLower.includes("focus-visible") || codeLower.includes("focusvisible");
|
|
@@ -11217,7 +11353,9 @@ function critiqueSource({
|
|
|
11217
11353
|
),
|
|
11218
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}.`,
|
|
11219
11355
|
suggestions: [
|
|
11220
|
-
...reviewPack ? [] : [
|
|
11356
|
+
...reviewPack ? [] : [
|
|
11357
|
+
"Run `decantr registry compile-packs decantr.essence.json --write-context` so critique starts from a compiled review contract."
|
|
11358
|
+
],
|
|
11221
11359
|
...placeholderNavigationTargets > 0 ? [
|
|
11222
11360
|
"Replace placeholder href/to targets with real route destinations from the compiled contract."
|
|
11223
11361
|
] : []
|
|
@@ -11273,7 +11411,7 @@ function critiqueSource({
|
|
|
11273
11411
|
`Disallowed inline style attributes: ${astSignals.inlineStyleAttributeCount}`
|
|
11274
11412
|
],
|
|
11275
11413
|
file: filePath,
|
|
11276
|
-
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."
|
|
11277
11415
|
})
|
|
11278
11416
|
);
|
|
11279
11417
|
}
|
|
@@ -11290,7 +11428,7 @@ function critiqueSource({
|
|
|
11290
11428
|
message: "Component-scoped style tags or dynamic style elements were detected in the reviewed file.",
|
|
11291
11429
|
evidence: [filePath, `Component style tag signals: ${componentStyleTagSignals}`],
|
|
11292
11430
|
file: filePath,
|
|
11293
|
-
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."
|
|
11294
11432
|
})
|
|
11295
11433
|
);
|
|
11296
11434
|
}
|
|
@@ -11943,12 +12081,14 @@ async function critiqueFile(filePath, projectRoot) {
|
|
|
11943
12081
|
const treatmentsCss = readTextIfExists(join2(projectRoot, "src", "styles", "treatments.css"));
|
|
11944
12082
|
const reviewPack = loadReviewPack(projectRoot);
|
|
11945
12083
|
const packManifest = loadPackManifest(projectRoot);
|
|
12084
|
+
const adoptionMode = readProjectAdoptionMode(projectRoot);
|
|
11946
12085
|
return critiqueSource({
|
|
11947
12086
|
filePath: resolvedPath,
|
|
11948
12087
|
code,
|
|
11949
12088
|
reviewPack,
|
|
11950
12089
|
packManifest,
|
|
11951
|
-
treatmentsCss
|
|
12090
|
+
treatmentsCss,
|
|
12091
|
+
adoptionMode
|
|
11952
12092
|
});
|
|
11953
12093
|
}
|
|
11954
12094
|
export {
|
|
@@ -11957,6 +12097,7 @@ export {
|
|
|
11957
12097
|
VERIFICATION_SCHEMA_URLS,
|
|
11958
12098
|
auditBuiltDist,
|
|
11959
12099
|
auditProject,
|
|
12100
|
+
collectMissingPackManifestFiles,
|
|
11960
12101
|
createContractAssertions,
|
|
11961
12102
|
createEvidenceBundle,
|
|
11962
12103
|
critiqueFile,
|