@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.
Files changed (47) hide show
  1. package/dist/cli.d.ts +0 -3
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +20 -91
  4. package/dist/cli.js.map +1 -1
  5. package/dist/project-config-v3.d.ts +1 -8
  6. package/dist/project-config-v3.d.ts.map +1 -1
  7. package/dist/project-config-v3.js +31 -193
  8. package/dist/project-config-v3.js.map +1 -1
  9. package/dist/project-config.d.ts +1 -1
  10. package/dist/project-config.d.ts.map +1 -1
  11. package/dist/project-config.js +1 -1
  12. package/dist/project-config.js.map +1 -1
  13. package/dist/project.d.ts +2 -9
  14. package/dist/project.d.ts.map +1 -1
  15. package/dist/project.js +44 -290
  16. package/dist/project.js.map +1 -1
  17. package/dist/release-catalog.d.ts.map +1 -1
  18. package/dist/release-catalog.js +0 -1687
  19. package/dist/release-catalog.js.map +1 -1
  20. package/dist/release-contract-compatibility.d.ts +57 -10
  21. package/dist/release-contract-compatibility.d.ts.map +1 -1
  22. package/dist/release-contract-compatibility.js +61 -115
  23. package/dist/release-contract-compatibility.js.map +1 -1
  24. package/dist/release-manifest.d.ts +0 -1
  25. package/dist/release-manifest.d.ts.map +1 -1
  26. package/dist/release-manifest.js +0 -7
  27. package/dist/release-manifest.js.map +1 -1
  28. package/dist/source-safety-contracts.d.ts.map +1 -1
  29. package/dist/source-safety-contracts.js +0 -4
  30. package/dist/source-safety-contracts.js.map +1 -1
  31. package/package.json +2 -20
  32. package/dist/analytics-contract.d.ts +0 -20
  33. package/dist/analytics-contract.d.ts.map +0 -1
  34. package/dist/analytics-contract.js +0 -252
  35. package/dist/analytics-contract.js.map +0 -1
  36. package/dist/project-documentation.d.ts +0 -28
  37. package/dist/project-documentation.d.ts.map +0 -1
  38. package/dist/project-documentation.js +0 -234
  39. package/dist/project-documentation.js.map +0 -1
  40. package/dist/rendered-content.d.ts +0 -38
  41. package/dist/rendered-content.d.ts.map +0 -1
  42. package/dist/rendered-content.js +0 -149
  43. package/dist/rendered-content.js.map +0 -1
  44. package/dist/verification-policy.d.ts +0 -99
  45. package/dist/verification-policy.d.ts.map +0 -1
  46. package/dist/verification-policy.js +0 -219
  47. 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/server.",
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, options = {}) {
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, contractVersion);
283
+ const config = validateConfigValue(value, issues);
364
284
  if (config) {
365
- addImportContractIssues(root, config, usage, issues, contractVersion);
285
+ addImportContractIssues(root, config, usage, issues);
366
286
  }
367
- addProtectedImplementationIssues(root, issues, contractVersion);
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, contractVersion) {
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, contractVersion);
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, contractVersion) {
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, capabilities.reviews.provider === "google-places" &&
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, contractVersion) {
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: ["defineLocationRouteDecision", "defineLocationRouteQuality"],
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/server",
966
+ moduleName: "@brandlab-digital/site-next/contact/production",
1087
967
  surface: "contact-production",
1088
- exports: ["createBrandlabContactRuntime"],
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
- if (contractVersion === "project-config-v3") {
1108
- requireStrictUsage({
1109
- moduleName: "@brandlab-digital/site-next/google-reviews",
1110
- surface: "google-reviews-route",
1111
- exports: ["createGooglePlaceReviewsRouteResponse"],
1112
- }, usage, issues);
1113
- requireStrictUsage({
1114
- moduleName: "@brandlab-digital/site-next/google-reviews",
1115
- surface: "google-reviews-display",
1116
- exports: ["GooglePlaceReviews"],
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 ([...usage.imports].some((name) => name.startsWith("@brandlab-digital/site-next/google-reviews"))) {
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 === "@brandlab-digital/site-next/contact" ||
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, contractVersion) {
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: "remediation" in rule
2922
- ? rule.remediation
2923
- : rule.ruleId === "brandlab-config-v3-url-derived-breadcrumbs"
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 [];