@brandlab-digital/conformance 1.0.0-rc.11 → 1.0.0-rc.12
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/project-config-v3.d.ts +8 -1
- package/dist/project-config-v3.d.ts.map +1 -1
- package/dist/project-config-v3.js +149 -21
- 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.map +1 -1
- package/dist/project.js +29 -10
- package/dist/project.js.map +1 -1
- package/dist/release-catalog.d.ts.map +1 -1
- package/dist/release-catalog.js +77 -0
- package/dist/release-catalog.js.map +1 -1
- package/dist/release-contract-compatibility.d.ts +7 -0
- package/dist/release-contract-compatibility.d.ts.map +1 -1
- package/dist/release-contract-compatibility.js +7 -0
- package/dist/release-contract-compatibility.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export declare const BRANDLAB_CONFIG_V3_RULESET_ID = "project-config-v3@1.0.0-rc.2";
|
|
2
|
+
export declare const BRANDLAB_CONFIG_V4_RULESET_ID = "project-config-v4@1.0.0-rc.12";
|
|
3
|
+
export type BrandlabInspectableConfigurationContract = "project-config-v3" | "project-config-v4";
|
|
2
4
|
export declare const BRANDLAB_CONFIG_V3_PROFILES: readonly ["next-local-service", "next-root-level", "next-static-legacy", "legacy-php"];
|
|
3
5
|
export interface InspectedBrandlabProjectConfig {
|
|
4
6
|
projectId: string;
|
|
@@ -14,6 +16,9 @@ export interface InspectedBrandlabProjectConfig {
|
|
|
14
16
|
contactEnabled: boolean;
|
|
15
17
|
locationStrategy: string | null;
|
|
16
18
|
reviewsProvider: string | null;
|
|
19
|
+
reviewsMode: string | null;
|
|
20
|
+
reviewsRouteIds: readonly string[];
|
|
21
|
+
reviewsMonthlyRequestLimit: number | null;
|
|
17
22
|
};
|
|
18
23
|
}
|
|
19
24
|
export interface BrandlabConfigV3Issue {
|
|
@@ -37,7 +42,9 @@ export interface BrandlabConfigV3Inspection {
|
|
|
37
42
|
frameworkOwnedExports: ReadonlyMap<string, ReadonlySet<string>>;
|
|
38
43
|
issues: BrandlabConfigV3Issue[];
|
|
39
44
|
}
|
|
40
|
-
export declare function inspectBrandlabConfigV3(root: string
|
|
45
|
+
export declare function inspectBrandlabConfigV3(root: string, options?: {
|
|
46
|
+
contractVersion?: BrandlabInspectableConfigurationContract;
|
|
47
|
+
}): BrandlabConfigV3Inspection;
|
|
41
48
|
/** SHA-256 evidence for the exact canonical serializer output produced at runtime. */
|
|
42
49
|
export declare function digestBrandlabPlatformContractSerialization(serialized: string): string;
|
|
43
50
|
//# sourceMappingURL=project-config-v3.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-config-v3.d.ts","sourceRoot":"","sources":["../src/project-config-v3.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,6BAA6B,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"project-config-v3.d.ts","sourceRoot":"","sources":["../src/project-config-v3.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,6BAA6B,iCAAiC,CAAC;AAC5E,eAAO,MAAM,6BAA6B,kCAAkC,CAAC;AAC7E,MAAM,MAAM,wCAAwC,GAChD,mBAAmB,GACnB,mBAAmB,CAAC;AAExB,eAAO,MAAM,2BAA2B,wFAK9B,CAAC;AA+CX,MAAM,WAAW,8BAA8B;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,CAAC,OAAO,2BAA2B,CAAC,CAAC,MAAM,CAAC,CAAC;IACtD,MAAM,EAAE;QACN,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;KAC7B,CAAC;IACF,mBAAmB,EAAE;QACnB,oBAAoB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;KACxD,CAAC;IACF,YAAY,EAAE;QACZ,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,cAAc,EAAE,OAAO,CAAC;QACxB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;QAChC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;QACnC,0BAA0B,EAAE,MAAM,GAAG,IAAI,CAAC;KAC3C,CAAC;CACH;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;IACzC,YAAY,EAAE,QAAQ,GAAG,aAAa,GAAG,SAAS,CAAC;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,8BAA8B,GAAG,IAAI,CAAC;IAC9C,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC7B,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACvC,kBAAkB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACxC,iBAAiB,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5D,kBAAkB,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;IAC7D,qBAAqB,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;IAChE,MAAM,EAAE,qBAAqB,EAAE,CAAC;CACjC;AAuLD,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IAAE,eAAe,CAAC,EAAE,wCAAwC,CAAA;CAAO,GAC3E,0BAA0B,CA6G5B;AA6/FD,sFAAsF;AACtF,wBAAgB,2CAA2C,CAAC,UAAU,EAAE,MAAM,UAe7E"}
|
|
@@ -7,6 +7,7 @@ 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";
|
|
10
11
|
export const BRANDLAB_CONFIG_V3_PROFILES = [
|
|
11
12
|
"next-local-service",
|
|
12
13
|
"next-root-level",
|
|
@@ -32,6 +33,8 @@ const LOCATION_STRATEGIES = new Set([
|
|
|
32
33
|
"multi-location",
|
|
33
34
|
]);
|
|
34
35
|
const REVIEWS_PROVIDERS = new Set(["google-places", "project-owned"]);
|
|
36
|
+
const GOOGLE_REVIEWS_MODES = new Set(["link-only", "summary", "full"]);
|
|
37
|
+
const MAXIMUM_GOOGLE_REVIEWS_MONTHLY_REQUEST_LIMIT = 100_000;
|
|
35
38
|
const ROOT_CONFIG_KEYS = new Set([
|
|
36
39
|
"projectId",
|
|
37
40
|
"profile",
|
|
@@ -193,6 +196,34 @@ const PROTECTED_LOCAL_PATTERNS = [
|
|
|
193
196
|
pattern: /\b(?:class|function|const|let|var)\s+(?:ContactFormEmail|renderContactEmail)\b/,
|
|
194
197
|
expected: "Contact email rendering is owned by @brandlab-digital/site-next/contact/email.",
|
|
195
198
|
},
|
|
199
|
+
{
|
|
200
|
+
ruleId: "brandlab-config-v3-local-google-reviews-fetch",
|
|
201
|
+
minimumContract: "project-config-v4",
|
|
202
|
+
surface: "google-reviews",
|
|
203
|
+
pattern: /\bplaces\.googleapis\.com\/v1\/places\b/,
|
|
204
|
+
expected: "Google Places fetching is owned by @brandlab-digital/site-next/google-reviews/server.",
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
ruleId: "brandlab-config-v3-local-google-reviews-renderer",
|
|
208
|
+
minimumContract: "project-config-v4",
|
|
209
|
+
surface: "google-reviews",
|
|
210
|
+
pattern: /@brandlab-digital\/site-core\/google-reviews[\s\S]{0,12000}\breviews\.map\s*\(/,
|
|
211
|
+
expected: "Google review content and attribution are rendered by the shared policy-complete UI.",
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
ruleId: "brandlab-config-v3-hardcoded-google-review-total",
|
|
215
|
+
minimumContract: "project-config-v4",
|
|
216
|
+
surface: "google-reviews",
|
|
217
|
+
pattern: /["'`][^"'`\n]{0,100}\b\d{1,6}\s+Google\s+(?:reviews?|ratings?)\b/i,
|
|
218
|
+
expected: "Rendered Google rating and review totals come from a live no-store shared snapshot.",
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
ruleId: "brandlab-config-v3-public-google-review-cache",
|
|
222
|
+
minimumContract: "project-config-v4",
|
|
223
|
+
surface: "google-reviews",
|
|
224
|
+
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,
|
|
225
|
+
expected: "Google Places content is returned with private no-store headers and is never publicly cached.",
|
|
226
|
+
},
|
|
196
227
|
{
|
|
197
228
|
ruleId: "brandlab-config-v3-local-lead-event",
|
|
198
229
|
surface: "analytics",
|
|
@@ -207,7 +238,8 @@ const PROTECTED_LOCAL_PATTERNS = [
|
|
|
207
238
|
expected: "Breadcrumb labels and hierarchy derive from the semantic route catalog.",
|
|
208
239
|
},
|
|
209
240
|
];
|
|
210
|
-
export function inspectBrandlabConfigV3(root) {
|
|
241
|
+
export function inspectBrandlabConfigV3(root, options = {}) {
|
|
242
|
+
const contractVersion = options.contractVersion ?? "project-config-v4";
|
|
211
243
|
const issues = [];
|
|
212
244
|
const configPath = join(root, "brandlab.config.ts");
|
|
213
245
|
const usage = collectProjectImports(root, issues);
|
|
@@ -280,11 +312,11 @@ export function inspectBrandlabConfigV3(root) {
|
|
|
280
312
|
}));
|
|
281
313
|
return { ...usage, config: null, issues };
|
|
282
314
|
}
|
|
283
|
-
const config = validateConfigValue(value, issues);
|
|
315
|
+
const config = validateConfigValue(value, issues, contractVersion);
|
|
284
316
|
if (config) {
|
|
285
|
-
addImportContractIssues(root, config, usage, issues);
|
|
317
|
+
addImportContractIssues(root, config, usage, issues, contractVersion);
|
|
286
318
|
}
|
|
287
|
-
addProtectedImplementationIssues(root, issues);
|
|
319
|
+
addProtectedImplementationIssues(root, issues, contractVersion);
|
|
288
320
|
return {
|
|
289
321
|
config,
|
|
290
322
|
imports,
|
|
@@ -382,7 +414,7 @@ function propertyName(name) {
|
|
|
382
414
|
}
|
|
383
415
|
return null;
|
|
384
416
|
}
|
|
385
|
-
function validateConfigValue(value, issues) {
|
|
417
|
+
function validateConfigValue(value, issues, contractVersion) {
|
|
386
418
|
if (!isRecord(value)) {
|
|
387
419
|
issues.push(configIssue({
|
|
388
420
|
ruleId: "brandlab-config-v3-object",
|
|
@@ -537,7 +569,7 @@ function validateConfigValue(value, issues) {
|
|
|
537
569
|
}));
|
|
538
570
|
}
|
|
539
571
|
else {
|
|
540
|
-
validateCapabilities(capabilities, issues);
|
|
572
|
+
validateCapabilities(capabilities, issues, contractVersion);
|
|
541
573
|
}
|
|
542
574
|
validateSitemap(value.sitemap, issues);
|
|
543
575
|
validateExceptions(value.exceptions, issues);
|
|
@@ -549,6 +581,13 @@ function validateConfigValue(value, issues) {
|
|
|
549
581
|
const locations = isRecord(safeCapabilities.locations) ? safeCapabilities.locations : null;
|
|
550
582
|
const reviews = isRecord(safeCapabilities.reviews) ? safeCapabilities.reviews : null;
|
|
551
583
|
const reviewsProvider = typeof reviews?.provider === "string" ? reviews.provider : null;
|
|
584
|
+
const reviewsMode = typeof reviews?.mode === "string" ? reviews.mode : null;
|
|
585
|
+
const reviewsRouteIds = Array.isArray(reviews?.routeIds)
|
|
586
|
+
? reviews.routeIds.filter((entry) => typeof entry === "string")
|
|
587
|
+
: [];
|
|
588
|
+
const reviewsMonthlyRequestLimit = typeof reviews?.monthlyRequestLimit === "number"
|
|
589
|
+
? reviews.monthlyRequestLimit
|
|
590
|
+
: null;
|
|
552
591
|
if (reviewsProvider === "google-places" && !families.includes("legal")) {
|
|
553
592
|
issues.push(configIssue({
|
|
554
593
|
ruleId: "brandlab-config-v3-google-places-legal-family",
|
|
@@ -571,6 +610,9 @@ function validateConfigValue(value, issues) {
|
|
|
571
610
|
contactEnabled: contact !== null,
|
|
572
611
|
locationStrategy: typeof locations?.strategy === "string" ? locations.strategy : null,
|
|
573
612
|
reviewsProvider,
|
|
613
|
+
reviewsMode,
|
|
614
|
+
reviewsRouteIds,
|
|
615
|
+
reviewsMonthlyRequestLimit,
|
|
574
616
|
},
|
|
575
617
|
};
|
|
576
618
|
}
|
|
@@ -647,7 +689,7 @@ function validateAliases(value, issues) {
|
|
|
647
689
|
}));
|
|
648
690
|
}
|
|
649
691
|
}
|
|
650
|
-
function validateCapabilities(capabilities, issues) {
|
|
692
|
+
function validateCapabilities(capabilities, issues, contractVersion) {
|
|
651
693
|
addUnknownKeyIssues(capabilities, new Set(["blog", "contact", "locations", "reviews"]), new Set(), "capabilities", issues);
|
|
652
694
|
if (capabilities.blog !== undefined) {
|
|
653
695
|
if (!isRecord(capabilities.blog)) {
|
|
@@ -717,7 +759,10 @@ function validateCapabilities(capabilities, issues) {
|
|
|
717
759
|
addShapeIssue("reviews", issues);
|
|
718
760
|
}
|
|
719
761
|
else {
|
|
720
|
-
addUnknownKeyIssues(capabilities.reviews,
|
|
762
|
+
addUnknownKeyIssues(capabilities.reviews, capabilities.reviews.provider === "google-places" &&
|
|
763
|
+
contractVersion === "project-config-v4"
|
|
764
|
+
? new Set(["provider", "mode", "routeIds", "monthlyRequestLimit"])
|
|
765
|
+
: new Set(["provider"]), new Set(), "capabilities.reviews", issues);
|
|
721
766
|
if (typeof capabilities.reviews.provider !== "string" ||
|
|
722
767
|
!REVIEWS_PROVIDERS.has(capabilities.reviews.provider)) {
|
|
723
768
|
issues.push(configIssue({
|
|
@@ -728,6 +773,64 @@ function validateCapabilities(capabilities, issues) {
|
|
|
728
773
|
path: "brandlab.config.ts",
|
|
729
774
|
}));
|
|
730
775
|
}
|
|
776
|
+
if (capabilities.reviews.provider === "google-places" &&
|
|
777
|
+
contractVersion === "project-config-v4") {
|
|
778
|
+
if (typeof capabilities.reviews.mode !== "string" ||
|
|
779
|
+
!GOOGLE_REVIEWS_MODES.has(capabilities.reviews.mode)) {
|
|
780
|
+
issues.push(configIssue({
|
|
781
|
+
ruleId: "brandlab-config-v3-google-reviews-mode",
|
|
782
|
+
expected: `One Google reviews mode: ${[...GOOGLE_REVIEWS_MODES].join(", ")}.`,
|
|
783
|
+
actual: `reviews.mode is ${JSON.stringify(capabilities.reviews.mode)}.`,
|
|
784
|
+
remediation: "Select link-only, summary or full explicitly.",
|
|
785
|
+
path: "brandlab.config.ts",
|
|
786
|
+
}));
|
|
787
|
+
}
|
|
788
|
+
const routeIds = Array.isArray(capabilities.reviews.routeIds)
|
|
789
|
+
? capabilities.reviews.routeIds.filter((entry) => typeof entry === "string")
|
|
790
|
+
: [];
|
|
791
|
+
if (!Array.isArray(capabilities.reviews.routeIds) ||
|
|
792
|
+
routeIds.length === 0 ||
|
|
793
|
+
routeIds.length !== capabilities.reviews.routeIds.length ||
|
|
794
|
+
routeIds.some((routeId) => !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(routeId))) {
|
|
795
|
+
issues.push(configIssue({
|
|
796
|
+
ruleId: "brandlab-config-v3-google-reviews-routes",
|
|
797
|
+
expected: "At least one unique lowercase kebab-case review placement route ID.",
|
|
798
|
+
actual: `reviews.routeIds is ${JSON.stringify(capabilities.reviews.routeIds)}.`,
|
|
799
|
+
remediation: "Declare only the route IDs where Google review proof is intentionally displayed.",
|
|
800
|
+
path: "brandlab.config.ts",
|
|
801
|
+
}));
|
|
802
|
+
}
|
|
803
|
+
else {
|
|
804
|
+
addDuplicateIssue(routeIds, "brandlab-config-v3-google-reviews-route-duplicate", "reviews.routeIds", issues);
|
|
805
|
+
}
|
|
806
|
+
if (capabilities.reviews.mode === "link-only") {
|
|
807
|
+
if (capabilities.reviews.monthlyRequestLimit !== undefined) {
|
|
808
|
+
issues.push(configIssue({
|
|
809
|
+
ruleId: "brandlab-config-v3-google-reviews-link-only-quota",
|
|
810
|
+
expected: "No API request limit for link-only review proof.",
|
|
811
|
+
actual: "reviews.monthlyRequestLimit is configured for link-only mode.",
|
|
812
|
+
remediation: "Remove monthlyRequestLimit because link-only mode never calls Places.",
|
|
813
|
+
path: "brandlab.config.ts",
|
|
814
|
+
}));
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
else if (capabilities.reviews.mode === "summary" ||
|
|
818
|
+
capabilities.reviews.mode === "full") {
|
|
819
|
+
const limit = capabilities.reviews.monthlyRequestLimit;
|
|
820
|
+
if (typeof limit !== "number" ||
|
|
821
|
+
!Number.isSafeInteger(limit) ||
|
|
822
|
+
limit < 1 ||
|
|
823
|
+
limit > MAXIMUM_GOOGLE_REVIEWS_MONTHLY_REQUEST_LIMIT) {
|
|
824
|
+
issues.push(configIssue({
|
|
825
|
+
ruleId: "brandlab-config-v3-google-reviews-quota",
|
|
826
|
+
expected: `monthlyRequestLimit from 1 to ${MAXIMUM_GOOGLE_REVIEWS_MONTHLY_REQUEST_LIMIT}.`,
|
|
827
|
+
actual: `reviews.monthlyRequestLimit is ${JSON.stringify(limit)}.`,
|
|
828
|
+
remediation: "Set an explicit project-level monthly request ceiling.",
|
|
829
|
+
path: "brandlab.config.ts",
|
|
830
|
+
}));
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
}
|
|
731
834
|
}
|
|
732
835
|
}
|
|
733
836
|
}
|
|
@@ -905,7 +1008,7 @@ function isIsoDate(value) {
|
|
|
905
1008
|
return false;
|
|
906
1009
|
return !Number.isNaN(Date.parse(value));
|
|
907
1010
|
}
|
|
908
|
-
function addImportContractIssues(root, config, usage, issues) {
|
|
1011
|
+
function addImportContractIssues(root, config, usage, issues, contractVersion) {
|
|
909
1012
|
if (config.profile === "legacy-php") {
|
|
910
1013
|
const nextEvidence = detectNextPackageBackedEvidence(root, usage);
|
|
911
1014
|
if (nextEvidence.length === 0)
|
|
@@ -984,16 +1087,37 @@ function addImportContractIssues(root, config, usage, issues) {
|
|
|
984
1087
|
}, usage, issues);
|
|
985
1088
|
}
|
|
986
1089
|
if (config.capabilities.reviewsProvider === "google-places") {
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1090
|
+
if (contractVersion === "project-config-v3") {
|
|
1091
|
+
requireStrictUsage({
|
|
1092
|
+
moduleName: "@brandlab-digital/site-next/google-reviews",
|
|
1093
|
+
surface: "google-reviews-route",
|
|
1094
|
+
exports: ["createGooglePlaceReviewsRouteResponse"],
|
|
1095
|
+
}, usage, issues);
|
|
1096
|
+
requireStrictUsage({
|
|
1097
|
+
moduleName: "@brandlab-digital/site-next/google-reviews",
|
|
1098
|
+
surface: "google-reviews-display",
|
|
1099
|
+
exports: ["GooglePlaceReviews"],
|
|
1100
|
+
}, usage, issues);
|
|
1101
|
+
}
|
|
1102
|
+
else if (config.capabilities.reviewsMode === "link-only") {
|
|
1103
|
+
requireStrictUsage({
|
|
1104
|
+
moduleName: "@brandlab-digital/site-next/google-reviews/ui",
|
|
1105
|
+
surface: "google-reviews-link",
|
|
1106
|
+
exports: ["GoogleReviewsLink"],
|
|
1107
|
+
}, usage, issues);
|
|
1108
|
+
}
|
|
1109
|
+
else {
|
|
1110
|
+
requireStrictUsage({
|
|
1111
|
+
moduleName: "@brandlab-digital/site-next/google-reviews/server",
|
|
1112
|
+
surface: "google-reviews-production",
|
|
1113
|
+
exports: ["createBrandlabGooglePlaceReviewsProductionRuntime"],
|
|
1114
|
+
}, usage, issues);
|
|
1115
|
+
requireStrictUsage({
|
|
1116
|
+
moduleName: "@brandlab-digital/site-next/google-reviews/client",
|
|
1117
|
+
surface: "google-reviews-display",
|
|
1118
|
+
exports: ["GooglePlaceReviewsClient"],
|
|
1119
|
+
}, usage, issues);
|
|
1120
|
+
}
|
|
997
1121
|
}
|
|
998
1122
|
else if (config.capabilities.reviewsProvider === "project-owned") {
|
|
999
1123
|
requireStrictUsage({
|
|
@@ -1002,7 +1126,7 @@ function addImportContractIssues(root, config, usage, issues) {
|
|
|
1002
1126
|
exports: ["preparePublicReviews"],
|
|
1003
1127
|
}, usage, issues);
|
|
1004
1128
|
}
|
|
1005
|
-
else if (usage.imports.
|
|
1129
|
+
else if ([...usage.imports].some((name) => name.startsWith("@brandlab-digital/site-next/google-reviews"))) {
|
|
1006
1130
|
issues.push(configIssue({
|
|
1007
1131
|
ruleId: "brandlab-config-v3-google-reviews-capability",
|
|
1008
1132
|
surface: "google-reviews",
|
|
@@ -2752,11 +2876,15 @@ function isRuntimeIdentifierReference(identifier) {
|
|
|
2752
2876
|
}
|
|
2753
2877
|
return true;
|
|
2754
2878
|
}
|
|
2755
|
-
function addProtectedImplementationIssues(root, issues) {
|
|
2879
|
+
function addProtectedImplementationIssues(root, issues, contractVersion) {
|
|
2756
2880
|
for (const file of walkProjectRuntimeFiles(root)) {
|
|
2757
2881
|
const relativePath = normalizePath(relative(root, file));
|
|
2758
2882
|
const source = readFileSync(file, "utf8");
|
|
2759
2883
|
for (const rule of PROTECTED_LOCAL_PATTERNS) {
|
|
2884
|
+
if ("minimumContract" in rule &&
|
|
2885
|
+
rule.minimumContract === "project-config-v4" &&
|
|
2886
|
+
contractVersion !== "project-config-v4")
|
|
2887
|
+
continue;
|
|
2760
2888
|
const match = rule.pattern.exec(source);
|
|
2761
2889
|
if (!match || match.index === undefined)
|
|
2762
2890
|
continue;
|