@brandlab-digital/conformance 1.0.0-rc.28 → 1.0.0-rc.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/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +20 -91
- package/dist/cli.js.map +1 -1
- package/dist/project-config-v3.d.ts +1 -8
- package/dist/project-config-v3.d.ts.map +1 -1
- package/dist/project-config-v3.js +31 -193
- package/dist/project-config-v3.js.map +1 -1
- package/dist/project-config.d.ts +1 -1
- package/dist/project-config.d.ts.map +1 -1
- package/dist/project-config.js +1 -1
- package/dist/project-config.js.map +1 -1
- package/dist/project.d.ts +2 -9
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +44 -290
- package/dist/project.js.map +1 -1
- package/dist/release-catalog.d.ts.map +1 -1
- package/dist/release-catalog.js +0 -1687
- package/dist/release-catalog.js.map +1 -1
- package/dist/release-contract-compatibility.d.ts +57 -10
- package/dist/release-contract-compatibility.d.ts.map +1 -1
- package/dist/release-contract-compatibility.js +61 -115
- package/dist/release-contract-compatibility.js.map +1 -1
- package/dist/release-manifest.d.ts +0 -1
- package/dist/release-manifest.d.ts.map +1 -1
- package/dist/release-manifest.js +0 -7
- package/dist/release-manifest.js.map +1 -1
- package/dist/source-safety-contracts.d.ts.map +1 -1
- package/dist/source-safety-contracts.js +0 -4
- package/dist/source-safety-contracts.js.map +1 -1
- package/package.json +2 -20
- package/dist/analytics-contract.d.ts +0 -20
- package/dist/analytics-contract.d.ts.map +0 -1
- package/dist/analytics-contract.js +0 -252
- package/dist/analytics-contract.js.map +0 -1
- package/dist/project-documentation.d.ts +0 -28
- package/dist/project-documentation.d.ts.map +0 -1
- package/dist/project-documentation.js +0 -234
- package/dist/project-documentation.js.map +0 -1
- package/dist/rendered-content.d.ts +0 -38
- package/dist/rendered-content.d.ts.map +0 -1
- package/dist/rendered-content.js +0 -149
- package/dist/rendered-content.js.map +0 -1
- package/dist/verification-policy.d.ts +0 -99
- package/dist/verification-policy.d.ts.map +0 -1
- package/dist/verification-policy.js +0 -219
- package/dist/verification-policy.js.map +0 -1
|
@@ -7,8 +7,6 @@ import { createHash } from "node:crypto";
|
|
|
7
7
|
import { extname, join, relative } from "node:path";
|
|
8
8
|
import * as ts from "typescript";
|
|
9
9
|
export const BRANDLAB_CONFIG_V3_RULESET_ID = "project-config-v3@1.0.0-rc.2";
|
|
10
|
-
export const BRANDLAB_CONFIG_V4_RULESET_ID = "project-config-v4@1.0.0-rc.12";
|
|
11
|
-
export const BRANDLAB_CONFIG_V5_RULESET_ID = "project-config-v5@1.0.0-rc.19";
|
|
12
10
|
export const BRANDLAB_CONFIG_V3_PROFILES = [
|
|
13
11
|
"next-local-service",
|
|
14
12
|
"next-root-level",
|
|
@@ -34,7 +32,6 @@ const LOCATION_STRATEGIES = new Set([
|
|
|
34
32
|
"multi-location",
|
|
35
33
|
]);
|
|
36
34
|
const REVIEWS_PROVIDERS = new Set(["google-places", "project-owned"]);
|
|
37
|
-
const GOOGLE_REVIEWS_MODES = new Set(["link-only", "summary", "full"]);
|
|
38
35
|
const ROOT_CONFIG_KEYS = new Set([
|
|
39
36
|
"projectId",
|
|
40
37
|
"profile",
|
|
@@ -184,54 +181,6 @@ const FORBIDDEN_CANDIDATE_EXPORTS = [
|
|
|
184
181
|
},
|
|
185
182
|
];
|
|
186
183
|
const PROTECTED_LOCAL_PATTERNS = [
|
|
187
|
-
{
|
|
188
|
-
ruleId: "brandlab-config-v3-circular-business-evidence",
|
|
189
|
-
minimumContract: "project-config-v5",
|
|
190
|
-
surface: "business",
|
|
191
|
-
pattern: /\b(?:existing-production-config|repository-config|source-code):/i,
|
|
192
|
-
expected: "Verified business facts cite independent evidence, not the repository value being verified.",
|
|
193
|
-
remediation: "Use structured official-registry, GBP, client-document, first-party-record or public-authority evidence; otherwise mark the fact unknown.",
|
|
194
|
-
},
|
|
195
|
-
{
|
|
196
|
-
ruleId: "brandlab-config-v3-service-area-public-address",
|
|
197
|
-
minimumContract: "project-config-v5",
|
|
198
|
-
surface: "business",
|
|
199
|
-
pattern: /\bmodel\s*:\s*["']service-area["'][\s\S]{0,1200}\baddressVisibility\s*:\s*["']public["']|\baddressVisibility\s*:\s*["']public["'][\s\S]{0,1200}\bmodel\s*:\s*["']service-area["']/i,
|
|
200
|
-
expected: "Customer-location-only service-area businesses keep address facts hidden.",
|
|
201
|
-
remediation: "Use model service-area, addressVisibility hidden and an unknown or not-applicable address fact.",
|
|
202
|
-
},
|
|
203
|
-
{
|
|
204
|
-
ruleId: "brandlab-config-v3-synthetic-review-proof",
|
|
205
|
-
minimumContract: "project-config-v5",
|
|
206
|
-
surface: "location-proof",
|
|
207
|
-
pattern: /\bregion-review:[^"'`\n]*[\s\S]{0,2400}\b(?:review\?\.\w+\s*\|\||trust\s+\w+|customer feedback across)\b/i,
|
|
208
|
-
expected: "Review proof exists only when an approved review record is bound to business.proof.",
|
|
209
|
-
remediation: "Remove fallback review proof and use verified review evidence or no review proof.",
|
|
210
|
-
},
|
|
211
|
-
{
|
|
212
|
-
ruleId: "brandlab-config-v3-local-programmatic-proof-derivation",
|
|
213
|
-
minimumContract: "project-config-v5",
|
|
214
|
-
surface: "location-proof",
|
|
215
|
-
pattern: /\bfunction\s+getProofLevelFromAssetIds\s*\(/,
|
|
216
|
-
expected: "Programmatic proof levels are derived by @brandlab-digital/site-core/locations.",
|
|
217
|
-
remediation: "Remove the project-local proof classifier and declare an explicit defineLocationRouteDecision result.",
|
|
218
|
-
},
|
|
219
|
-
{
|
|
220
|
-
ruleId: "brandlab-config-v3-hardcoded-search-demand",
|
|
221
|
-
minimumContract: "project-config-v5",
|
|
222
|
-
surface: "location-quality",
|
|
223
|
-
pattern: /\bsearchDemand\s*:\s*["']medium["']/,
|
|
224
|
-
expected: "Search demand is observed from evidence or explicitly unknown.",
|
|
225
|
-
remediation: "Replace the assumed medium value with unknown or an evidence-backed observed record.",
|
|
226
|
-
},
|
|
227
|
-
{
|
|
228
|
-
ruleId: "brandlab-config-v3-hidden-focusable-content",
|
|
229
|
-
minimumContract: "project-config-v5",
|
|
230
|
-
surface: "accessibility",
|
|
231
|
-
pattern: /\baria-hidden\s*=\s*(?:\{\s*true\s*\}|["']true["'])[\s\S]{0,1200}<\s*(?:a|button|input|select|textarea)\b/i,
|
|
232
|
-
expected: "Collapsed or aria-hidden content contains no focusable controls.",
|
|
233
|
-
remediation: "Remove collapsed controls from the DOM or make every descendant unfocusable until the content is visible.",
|
|
234
|
-
},
|
|
235
184
|
{
|
|
236
185
|
ruleId: "brandlab-config-v3-local-contact-transport",
|
|
237
186
|
surface: "contact",
|
|
@@ -242,35 +191,7 @@ const PROTECTED_LOCAL_PATTERNS = [
|
|
|
242
191
|
ruleId: "brandlab-config-v3-local-contact-email",
|
|
243
192
|
surface: "contact",
|
|
244
193
|
pattern: /\b(?:class|function|const|let|var)\s+(?:ContactFormEmail|renderContactEmail)\b/,
|
|
245
|
-
expected: "Contact email rendering is owned by @brandlab-digital/site-next/contact/
|
|
246
|
-
},
|
|
247
|
-
{
|
|
248
|
-
ruleId: "brandlab-config-v3-local-google-reviews-fetch",
|
|
249
|
-
minimumContract: "project-config-v4",
|
|
250
|
-
surface: "google-reviews",
|
|
251
|
-
pattern: /\bplaces\.googleapis\.com\/v1\/places\b/,
|
|
252
|
-
expected: "Google Places fetching is owned by @brandlab-digital/site-next/google-reviews/server.",
|
|
253
|
-
},
|
|
254
|
-
{
|
|
255
|
-
ruleId: "brandlab-config-v3-local-google-reviews-renderer",
|
|
256
|
-
minimumContract: "project-config-v4",
|
|
257
|
-
surface: "google-reviews",
|
|
258
|
-
pattern: /@brandlab-digital\/site-core\/google-reviews[\s\S]{0,12000}\breviews\.map\s*\(/,
|
|
259
|
-
expected: "Google review content and attribution are rendered by the shared policy-complete UI.",
|
|
260
|
-
},
|
|
261
|
-
{
|
|
262
|
-
ruleId: "brandlab-config-v3-hardcoded-google-review-total",
|
|
263
|
-
minimumContract: "project-config-v4",
|
|
264
|
-
surface: "google-reviews",
|
|
265
|
-
pattern: /["'`][^"'`\n]{0,100}\b\d{1,6}\s+Google\s+(?:reviews?|ratings?)\b/i,
|
|
266
|
-
expected: "Rendered Google rating and review totals come from a live no-store shared snapshot.",
|
|
267
|
-
},
|
|
268
|
-
{
|
|
269
|
-
ruleId: "brandlab-config-v3-public-google-review-cache",
|
|
270
|
-
minimumContract: "project-config-v4",
|
|
271
|
-
surface: "google-reviews",
|
|
272
|
-
pattern: /\b(?:googleReviews?|userRatingCount)\b[\s\S]{0,2000}\b(?:s-maxage|stale-while-revalidate)\b|\b(?:s-maxage|stale-while-revalidate)\b[\s\S]{0,2000}\b(?:googleReviews?|userRatingCount)\b/i,
|
|
273
|
-
expected: "Google Places content is returned with private no-store headers and is never publicly cached.",
|
|
194
|
+
expected: "Contact email rendering is owned by @brandlab-digital/site-next/contact/email.",
|
|
274
195
|
},
|
|
275
196
|
{
|
|
276
197
|
ruleId: "brandlab-config-v3-local-lead-event",
|
|
@@ -286,8 +207,7 @@ const PROTECTED_LOCAL_PATTERNS = [
|
|
|
286
207
|
expected: "Breadcrumb labels and hierarchy derive from the semantic route catalog.",
|
|
287
208
|
},
|
|
288
209
|
];
|
|
289
|
-
export function inspectBrandlabConfigV3(root
|
|
290
|
-
const contractVersion = options.contractVersion ?? "project-config-v5";
|
|
210
|
+
export function inspectBrandlabConfigV3(root) {
|
|
291
211
|
const issues = [];
|
|
292
212
|
const configPath = join(root, "brandlab.config.ts");
|
|
293
213
|
const usage = collectProjectImports(root, issues);
|
|
@@ -360,11 +280,11 @@ export function inspectBrandlabConfigV3(root, options = {}) {
|
|
|
360
280
|
}));
|
|
361
281
|
return { ...usage, config: null, issues };
|
|
362
282
|
}
|
|
363
|
-
const config = validateConfigValue(value, issues
|
|
283
|
+
const config = validateConfigValue(value, issues);
|
|
364
284
|
if (config) {
|
|
365
|
-
addImportContractIssues(root, config, usage, issues
|
|
285
|
+
addImportContractIssues(root, config, usage, issues);
|
|
366
286
|
}
|
|
367
|
-
addProtectedImplementationIssues(root, issues
|
|
287
|
+
addProtectedImplementationIssues(root, issues);
|
|
368
288
|
return {
|
|
369
289
|
config,
|
|
370
290
|
imports,
|
|
@@ -462,7 +382,7 @@ function propertyName(name) {
|
|
|
462
382
|
}
|
|
463
383
|
return null;
|
|
464
384
|
}
|
|
465
|
-
function validateConfigValue(value, issues
|
|
385
|
+
function validateConfigValue(value, issues) {
|
|
466
386
|
if (!isRecord(value)) {
|
|
467
387
|
issues.push(configIssue({
|
|
468
388
|
ruleId: "brandlab-config-v3-object",
|
|
@@ -617,7 +537,7 @@ function validateConfigValue(value, issues, contractVersion) {
|
|
|
617
537
|
}));
|
|
618
538
|
}
|
|
619
539
|
else {
|
|
620
|
-
validateCapabilities(capabilities, issues
|
|
540
|
+
validateCapabilities(capabilities, issues);
|
|
621
541
|
}
|
|
622
542
|
validateSitemap(value.sitemap, issues);
|
|
623
543
|
validateExceptions(value.exceptions, issues);
|
|
@@ -629,10 +549,6 @@ function validateConfigValue(value, issues, contractVersion) {
|
|
|
629
549
|
const locations = isRecord(safeCapabilities.locations) ? safeCapabilities.locations : null;
|
|
630
550
|
const reviews = isRecord(safeCapabilities.reviews) ? safeCapabilities.reviews : null;
|
|
631
551
|
const reviewsProvider = typeof reviews?.provider === "string" ? reviews.provider : null;
|
|
632
|
-
const reviewsMode = typeof reviews?.mode === "string" ? reviews.mode : null;
|
|
633
|
-
const reviewsRouteIds = Array.isArray(reviews?.routeIds)
|
|
634
|
-
? reviews.routeIds.filter((entry) => typeof entry === "string")
|
|
635
|
-
: [];
|
|
636
552
|
if (reviewsProvider === "google-places" && !families.includes("legal")) {
|
|
637
553
|
issues.push(configIssue({
|
|
638
554
|
ruleId: "brandlab-config-v3-google-places-legal-family",
|
|
@@ -655,8 +571,6 @@ function validateConfigValue(value, issues, contractVersion) {
|
|
|
655
571
|
contactEnabled: contact !== null,
|
|
656
572
|
locationStrategy: typeof locations?.strategy === "string" ? locations.strategy : null,
|
|
657
573
|
reviewsProvider,
|
|
658
|
-
reviewsMode,
|
|
659
|
-
reviewsRouteIds,
|
|
660
574
|
},
|
|
661
575
|
};
|
|
662
576
|
}
|
|
@@ -733,7 +647,7 @@ function validateAliases(value, issues) {
|
|
|
733
647
|
}));
|
|
734
648
|
}
|
|
735
649
|
}
|
|
736
|
-
function validateCapabilities(capabilities, issues
|
|
650
|
+
function validateCapabilities(capabilities, issues) {
|
|
737
651
|
addUnknownKeyIssues(capabilities, new Set(["blog", "contact", "locations", "reviews"]), new Set(), "capabilities", issues);
|
|
738
652
|
if (capabilities.blog !== undefined) {
|
|
739
653
|
if (!isRecord(capabilities.blog)) {
|
|
@@ -803,10 +717,7 @@ function validateCapabilities(capabilities, issues, contractVersion) {
|
|
|
803
717
|
addShapeIssue("reviews", issues);
|
|
804
718
|
}
|
|
805
719
|
else {
|
|
806
|
-
addUnknownKeyIssues(capabilities.reviews,
|
|
807
|
-
contractVersion !== "project-config-v3"
|
|
808
|
-
? new Set(["provider", "mode", "routeIds", "monthlyRequestLimit"])
|
|
809
|
-
: new Set(["provider"]), new Set(), "capabilities.reviews", issues);
|
|
720
|
+
addUnknownKeyIssues(capabilities.reviews, new Set(["provider"]), new Set(), "capabilities.reviews", issues);
|
|
810
721
|
if (typeof capabilities.reviews.provider !== "string" ||
|
|
811
722
|
!REVIEWS_PROVIDERS.has(capabilities.reviews.provider)) {
|
|
812
723
|
issues.push(configIssue({
|
|
@@ -817,37 +728,6 @@ function validateCapabilities(capabilities, issues, contractVersion) {
|
|
|
817
728
|
path: "brandlab.config.ts",
|
|
818
729
|
}));
|
|
819
730
|
}
|
|
820
|
-
if (capabilities.reviews.provider === "google-places" &&
|
|
821
|
-
contractVersion !== "project-config-v3") {
|
|
822
|
-
if (typeof capabilities.reviews.mode !== "string" ||
|
|
823
|
-
!GOOGLE_REVIEWS_MODES.has(capabilities.reviews.mode)) {
|
|
824
|
-
issues.push(configIssue({
|
|
825
|
-
ruleId: "brandlab-config-v3-google-reviews-mode",
|
|
826
|
-
expected: `One Google reviews mode: ${[...GOOGLE_REVIEWS_MODES].join(", ")}.`,
|
|
827
|
-
actual: `reviews.mode is ${JSON.stringify(capabilities.reviews.mode)}.`,
|
|
828
|
-
remediation: "Select link-only, summary or full explicitly.",
|
|
829
|
-
path: "brandlab.config.ts",
|
|
830
|
-
}));
|
|
831
|
-
}
|
|
832
|
-
const routeIds = Array.isArray(capabilities.reviews.routeIds)
|
|
833
|
-
? capabilities.reviews.routeIds.filter((entry) => typeof entry === "string")
|
|
834
|
-
: [];
|
|
835
|
-
if (!Array.isArray(capabilities.reviews.routeIds) ||
|
|
836
|
-
routeIds.length === 0 ||
|
|
837
|
-
routeIds.length !== capabilities.reviews.routeIds.length ||
|
|
838
|
-
routeIds.some((routeId) => !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(routeId))) {
|
|
839
|
-
issues.push(configIssue({
|
|
840
|
-
ruleId: "brandlab-config-v3-google-reviews-routes",
|
|
841
|
-
expected: "At least one unique lowercase kebab-case review placement route ID.",
|
|
842
|
-
actual: `reviews.routeIds is ${JSON.stringify(capabilities.reviews.routeIds)}.`,
|
|
843
|
-
remediation: "Declare only the route IDs where Google review proof is intentionally displayed.",
|
|
844
|
-
path: "brandlab.config.ts",
|
|
845
|
-
}));
|
|
846
|
-
}
|
|
847
|
-
else {
|
|
848
|
-
addDuplicateIssue(routeIds, "brandlab-config-v3-google-reviews-route-duplicate", "reviews.routeIds", issues);
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
731
|
}
|
|
852
732
|
}
|
|
853
733
|
}
|
|
@@ -1025,7 +905,7 @@ function isIsoDate(value) {
|
|
|
1025
905
|
return false;
|
|
1026
906
|
return !Number.isNaN(Date.parse(value));
|
|
1027
907
|
}
|
|
1028
|
-
function addImportContractIssues(root, config, usage, issues
|
|
908
|
+
function addImportContractIssues(root, config, usage, issues) {
|
|
1029
909
|
if (config.profile === "legacy-php") {
|
|
1030
910
|
const nextEvidence = detectNextPackageBackedEvidence(root, usage);
|
|
1031
911
|
if (nextEvidence.length === 0)
|
|
@@ -1055,7 +935,7 @@ function addImportContractIssues(root, config, usage, issues, contractVersion) {
|
|
|
1055
935
|
requireStrictUsage({
|
|
1056
936
|
moduleName: "@brandlab-digital/site-core/locations",
|
|
1057
937
|
surface: "location-proof",
|
|
1058
|
-
exports: ["
|
|
938
|
+
exports: ["defineLocationRouteQuality"],
|
|
1059
939
|
}, usage, issues);
|
|
1060
940
|
}
|
|
1061
941
|
const contactEvidence = detectContactSurfaceEvidence(root, usage);
|
|
@@ -1083,12 +963,12 @@ function addImportContractIssues(root, config, usage, issues, contractVersion) {
|
|
|
1083
963
|
}
|
|
1084
964
|
if (contactRequired) {
|
|
1085
965
|
requireStrictUsage({
|
|
1086
|
-
moduleName: "@brandlab-digital/site-next/contact/
|
|
966
|
+
moduleName: "@brandlab-digital/site-next/contact/production",
|
|
1087
967
|
surface: "contact-production",
|
|
1088
|
-
exports: ["
|
|
968
|
+
exports: ["createBrandlabProductionContactRuntime"],
|
|
1089
969
|
}, usage, issues);
|
|
1090
970
|
requireStrictUsage({
|
|
1091
|
-
moduleName: "@brandlab-digital/site-next/contact",
|
|
971
|
+
moduleName: "@brandlab-digital/site-next/contact/client",
|
|
1092
972
|
surface: "contact-client",
|
|
1093
973
|
exports: ["createContactHttpSubmissionController"],
|
|
1094
974
|
}, usage, issues);
|
|
@@ -1104,37 +984,16 @@ function addImportContractIssues(root, config, usage, issues, contractVersion) {
|
|
|
1104
984
|
}, usage, issues);
|
|
1105
985
|
}
|
|
1106
986
|
if (config.capabilities.reviewsProvider === "google-places") {
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
}, usage, issues);
|
|
1118
|
-
}
|
|
1119
|
-
else if (config.capabilities.reviewsMode === "link-only") {
|
|
1120
|
-
requireStrictUsage({
|
|
1121
|
-
moduleName: "@brandlab-digital/site-next/google-reviews/ui",
|
|
1122
|
-
surface: "google-reviews-link",
|
|
1123
|
-
exports: ["GoogleReviewsLink"],
|
|
1124
|
-
}, usage, issues);
|
|
1125
|
-
}
|
|
1126
|
-
else {
|
|
1127
|
-
requireStrictUsage({
|
|
1128
|
-
moduleName: "@brandlab-digital/site-next/google-reviews/server",
|
|
1129
|
-
surface: "google-reviews-production",
|
|
1130
|
-
exports: ["createBrandlabGooglePlaceReviewsProductionRuntime"],
|
|
1131
|
-
}, usage, issues);
|
|
1132
|
-
requireStrictUsage({
|
|
1133
|
-
moduleName: "@brandlab-digital/site-next/google-reviews/client",
|
|
1134
|
-
surface: "google-reviews-display",
|
|
1135
|
-
exports: ["GooglePlaceReviewsClient"],
|
|
1136
|
-
}, usage, issues);
|
|
1137
|
-
}
|
|
987
|
+
requireStrictUsage({
|
|
988
|
+
moduleName: "@brandlab-digital/site-next/google-reviews",
|
|
989
|
+
surface: "google-reviews-route",
|
|
990
|
+
exports: ["createGooglePlaceReviewsRouteResponse"],
|
|
991
|
+
}, usage, issues);
|
|
992
|
+
requireStrictUsage({
|
|
993
|
+
moduleName: "@brandlab-digital/site-next/google-reviews",
|
|
994
|
+
surface: "google-reviews-display",
|
|
995
|
+
exports: ["GooglePlaceReviews"],
|
|
996
|
+
}, usage, issues);
|
|
1138
997
|
}
|
|
1139
998
|
else if (config.capabilities.reviewsProvider === "project-owned") {
|
|
1140
999
|
requireStrictUsage({
|
|
@@ -1143,7 +1002,7 @@ function addImportContractIssues(root, config, usage, issues, contractVersion) {
|
|
|
1143
1002
|
exports: ["preparePublicReviews"],
|
|
1144
1003
|
}, usage, issues);
|
|
1145
1004
|
}
|
|
1146
|
-
else if (
|
|
1005
|
+
else if (usage.imports.has("@brandlab-digital/site-next/google-reviews")) {
|
|
1147
1006
|
issues.push(configIssue({
|
|
1148
1007
|
ruleId: "brandlab-config-v3-google-reviews-capability",
|
|
1149
1008
|
surface: "google-reviews",
|
|
@@ -1210,8 +1069,7 @@ function detectNextPackageBackedEvidence(root, usage) {
|
|
|
1210
1069
|
function detectContactSurfaceEvidence(root, usage) {
|
|
1211
1070
|
const evidence = new Set();
|
|
1212
1071
|
for (const moduleName of usage.imports) {
|
|
1213
|
-
if (moduleName
|
|
1214
|
-
moduleName.startsWith("@brandlab-digital/site-next/contact/")) {
|
|
1072
|
+
if (moduleName.startsWith("@brandlab-digital/site-next/contact/")) {
|
|
1215
1073
|
evidence.add(`shared contact import ${moduleName}`);
|
|
1216
1074
|
}
|
|
1217
1075
|
}
|
|
@@ -1295,13 +1153,6 @@ function requireStrictUsage(requirement, usage, issues) {
|
|
|
1295
1153
|
}));
|
|
1296
1154
|
}
|
|
1297
1155
|
}
|
|
1298
|
-
function requireAnyStrictUsage(requirements, usage, issues) {
|
|
1299
|
-
const selected = requirements.find((requirement) => usage.imports.has(requirement.moduleName)) ??
|
|
1300
|
-
requirements[0];
|
|
1301
|
-
if (!selected)
|
|
1302
|
-
return;
|
|
1303
|
-
requireStrictUsage(selected, usage, issues);
|
|
1304
|
-
}
|
|
1305
1156
|
function frameworkOwnerExpectation(owner) {
|
|
1306
1157
|
if (owner === "metadata") {
|
|
1307
1158
|
return "The strict metadata builder reaches metadata or generateMetadata in an App Router page or layout entrypoint.";
|
|
@@ -1477,7 +1328,7 @@ function collectProjectImports(root, issues) {
|
|
|
1477
1328
|
return;
|
|
1478
1329
|
executableImports.add(binding.moduleName);
|
|
1479
1330
|
addExportUsage(executableExports, binding.moduleName, binding.exportName);
|
|
1480
|
-
const isSharedContactClientController = binding.moduleName === "@brandlab-digital/site-next/contact" &&
|
|
1331
|
+
const isSharedContactClientController = binding.moduleName === "@brandlab-digital/site-next/contact/client" &&
|
|
1481
1332
|
binding.exportName === "createContactHttpSubmissionController";
|
|
1482
1333
|
if (isOutputOwned(node) ||
|
|
1483
1334
|
(isSharedContactClientController && isContactClientRuntimeOwned(node))) {
|
|
@@ -2901,14 +2752,11 @@ function isRuntimeIdentifierReference(identifier) {
|
|
|
2901
2752
|
}
|
|
2902
2753
|
return true;
|
|
2903
2754
|
}
|
|
2904
|
-
function addProtectedImplementationIssues(root, issues
|
|
2755
|
+
function addProtectedImplementationIssues(root, issues) {
|
|
2905
2756
|
for (const file of walkProjectRuntimeFiles(root)) {
|
|
2906
2757
|
const relativePath = normalizePath(relative(root, file));
|
|
2907
2758
|
const source = readFileSync(file, "utf8");
|
|
2908
2759
|
for (const rule of PROTECTED_LOCAL_PATTERNS) {
|
|
2909
|
-
if ("minimumContract" in rule &&
|
|
2910
|
-
!configurationContractIncludes(contractVersion, rule.minimumContract))
|
|
2911
|
-
continue;
|
|
2912
2760
|
const match = rule.pattern.exec(source);
|
|
2913
2761
|
if (!match || match.index === undefined)
|
|
2914
2762
|
continue;
|
|
@@ -2918,11 +2766,9 @@ function addProtectedImplementationIssues(root, issues, contractVersion) {
|
|
|
2918
2766
|
surface: rule.surface,
|
|
2919
2767
|
expected: rule.expected,
|
|
2920
2768
|
actual: `A protected local implementation was found in ${relativePath}.`,
|
|
2921
|
-
remediation: "
|
|
2922
|
-
?
|
|
2923
|
-
:
|
|
2924
|
-
? "Use BrandlabRouteBreadcrumbs with the typed route catalog; keep path labels only as a development fallback."
|
|
2925
|
-
: "Keep only the thin project binding and consume the shared runtime.",
|
|
2769
|
+
remediation: rule.ruleId === "brandlab-config-v3-url-derived-breadcrumbs"
|
|
2770
|
+
? "Use BrandlabRouteBreadcrumbs with the typed route catalog; keep path labels only as a development fallback."
|
|
2771
|
+
: "Keep only the thin project binding and consume the shared runtime.",
|
|
2926
2772
|
severity: "severity" in rule ? rule.severity : "blocker",
|
|
2927
2773
|
path: relativePath,
|
|
2928
2774
|
line,
|
|
@@ -2930,14 +2776,6 @@ function addProtectedImplementationIssues(root, issues, contractVersion) {
|
|
|
2930
2776
|
}
|
|
2931
2777
|
}
|
|
2932
2778
|
}
|
|
2933
|
-
function configurationContractIncludes(actual, minimum) {
|
|
2934
|
-
const order = {
|
|
2935
|
-
"project-config-v3": 3,
|
|
2936
|
-
"project-config-v4": 4,
|
|
2937
|
-
"project-config-v5": 5,
|
|
2938
|
-
};
|
|
2939
|
-
return order[actual] >= order[minimum];
|
|
2940
|
-
}
|
|
2941
2779
|
function walkSourceFiles(root) {
|
|
2942
2780
|
if (!existsSync(root))
|
|
2943
2781
|
return [];
|