@brandlab-digital/conformance 0.1.0-alpha.9 → 1.0.0-rc.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/dist/cli.d.ts +19 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +128 -0
- package/dist/cli.js.map +1 -0
- package/dist/locations.d.ts +63 -0
- package/dist/locations.d.ts.map +1 -0
- package/dist/locations.js +105 -0
- package/dist/locations.js.map +1 -0
- package/dist/project-config.d.ts +31 -0
- package/dist/project-config.d.ts.map +1 -0
- package/dist/project-config.js +917 -0
- package/dist/project-config.js.map +1 -0
- package/dist/project.d.ts +67 -0
- package/dist/project.d.ts.map +1 -0
- package/dist/project.js +1039 -0
- package/dist/project.js.map +1 -0
- package/dist/release-catalog.d.ts +4 -0
- package/dist/release-catalog.d.ts.map +1 -0
- package/dist/release-catalog.js +256 -0
- package/dist/release-catalog.js.map +1 -0
- package/dist/release-manifest.d.ts +119 -0
- package/dist/release-manifest.d.ts.map +1 -0
- package/dist/release-manifest.js +445 -0
- package/dist/release-manifest.js.map +1 -0
- package/dist/schema-hygiene.d.ts +25 -0
- package/dist/schema-hygiene.d.ts.map +1 -0
- package/dist/schema-hygiene.js +19 -0
- package/dist/schema-hygiene.js.map +1 -0
- package/dist/vitest.d.ts +4 -0
- package/dist/vitest.d.ts.map +1 -1
- package/dist/vitest.js +382 -6
- package/dist/vitest.js.map +1 -1
- package/package.json +35 -1
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { type BrandlabConformanceMode } from "./project.js";
|
|
3
|
+
interface CliOptions {
|
|
4
|
+
root: string;
|
|
5
|
+
installedRelease?: string | null;
|
|
6
|
+
targetRelease?: string | null;
|
|
7
|
+
supportPolicyPath: string | null;
|
|
8
|
+
mode: BrandlabConformanceMode;
|
|
9
|
+
json: boolean;
|
|
10
|
+
help: boolean;
|
|
11
|
+
}
|
|
12
|
+
interface CliIo {
|
|
13
|
+
stdout: (value: string) => void;
|
|
14
|
+
stderr: (value: string) => void;
|
|
15
|
+
}
|
|
16
|
+
export declare function parseBrandlabConformanceArgs(argv: readonly string[]): CliOptions;
|
|
17
|
+
export declare function runBrandlabConformanceCli(argv: readonly string[], io?: CliIo): number;
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAIA,OAAO,EAGL,KAAK,uBAAuB,EAE7B,MAAM,cAAc,CAAC;AAGtB,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,IAAI,EAAE,uBAAuB,CAAC;IAC9B,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;CACf;AAED,UAAU,KAAK;IACb,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAcD,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,CA2ChF;AAED,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,EAAE,GAAE,KAGH,GACA,MAAM,CAqBR"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
import { evaluateBrandlabProject, shouldFailBrandlabConformance, } from "./project.js";
|
|
6
|
+
const HELP = `Brandlab version-aware conformance
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
brandlab-conformance --support-policy <path> [--root <path>]
|
|
10
|
+
[--installed-release <version|unadopted>]
|
|
11
|
+
[--target-release <version|none>] [--mode <local|ci|preflight>] [--json]
|
|
12
|
+
|
|
13
|
+
Exit status:
|
|
14
|
+
local/ci fail only for installed-release maintenance blockers
|
|
15
|
+
preflight also fails for target-release migration blockers
|
|
16
|
+
`;
|
|
17
|
+
export function parseBrandlabConformanceArgs(argv) {
|
|
18
|
+
const parsed = {
|
|
19
|
+
root: process.cwd(),
|
|
20
|
+
supportPolicyPath: null,
|
|
21
|
+
mode: "local",
|
|
22
|
+
json: false,
|
|
23
|
+
help: false,
|
|
24
|
+
};
|
|
25
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
26
|
+
const argument = argv[index];
|
|
27
|
+
if (argument === "--help" || argument === "-h") {
|
|
28
|
+
parsed.help = true;
|
|
29
|
+
}
|
|
30
|
+
else if (argument === "--json") {
|
|
31
|
+
parsed.json = true;
|
|
32
|
+
}
|
|
33
|
+
else if (argument === "--root") {
|
|
34
|
+
parsed.root = resolve(requireValue(argv, ++index, "--root"));
|
|
35
|
+
}
|
|
36
|
+
else if (argument === "--support-policy") {
|
|
37
|
+
parsed.supportPolicyPath = resolve(requireValue(argv, ++index, "--support-policy"));
|
|
38
|
+
}
|
|
39
|
+
else if (argument === "--installed-release") {
|
|
40
|
+
const value = requireValue(argv, ++index, "--installed-release");
|
|
41
|
+
parsed.installedRelease = value === "unadopted" ? null : value;
|
|
42
|
+
}
|
|
43
|
+
else if (argument === "--target-release") {
|
|
44
|
+
const value = requireValue(argv, ++index, "--target-release");
|
|
45
|
+
parsed.targetRelease = value === "none" ? null : value;
|
|
46
|
+
}
|
|
47
|
+
else if (argument === "--mode") {
|
|
48
|
+
const value = requireValue(argv, ++index, "--mode");
|
|
49
|
+
if (value !== "local" && value !== "ci" && value !== "preflight") {
|
|
50
|
+
throw new Error(`Invalid --mode ${value}.`);
|
|
51
|
+
}
|
|
52
|
+
parsed.mode = value;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
throw new Error(`Unknown argument ${argument}.`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (!parsed.help && parsed.supportPolicyPath === null) {
|
|
59
|
+
throw new Error("--support-policy is required.");
|
|
60
|
+
}
|
|
61
|
+
return parsed;
|
|
62
|
+
}
|
|
63
|
+
export function runBrandlabConformanceCli(argv, io = {
|
|
64
|
+
stdout: (value) => process.stdout.write(value),
|
|
65
|
+
stderr: (value) => process.stderr.write(value),
|
|
66
|
+
}) {
|
|
67
|
+
try {
|
|
68
|
+
const options = parseBrandlabConformanceArgs(argv);
|
|
69
|
+
if (options.help) {
|
|
70
|
+
io.stdout(HELP);
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
73
|
+
const result = evaluateBrandlabProject({
|
|
74
|
+
root: options.root,
|
|
75
|
+
installedRelease: options.installedRelease,
|
|
76
|
+
targetRelease: options.targetRelease,
|
|
77
|
+
mode: options.mode,
|
|
78
|
+
supportPolicy: loadSupportPolicy(options.supportPolicyPath),
|
|
79
|
+
});
|
|
80
|
+
io.stdout(options.json ? `${JSON.stringify(result, null, 2)}\n` : formatResult(result));
|
|
81
|
+
return shouldFailBrandlabConformance(result) ? 1 : 0;
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
io.stderr(`${error instanceof Error ? error.message : String(error)}\n\n${HELP}`);
|
|
85
|
+
return 2;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function loadSupportPolicy(path) {
|
|
89
|
+
if (path === null)
|
|
90
|
+
throw new Error("--support-policy is required.");
|
|
91
|
+
try {
|
|
92
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
96
|
+
throw new Error(`Unable to load support policy from ${path}: ${detail}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function formatResult(result) {
|
|
100
|
+
const lines = [
|
|
101
|
+
"Brandlab conformance",
|
|
102
|
+
`installed: ${result.installedRelease ?? "unadopted"}`,
|
|
103
|
+
`target: ${result.targetRelease ?? "none"}`,
|
|
104
|
+
`maintenance: ${formatSummary(result.maintenance)}`,
|
|
105
|
+
`migration: ${formatSummary(result.migration)}`,
|
|
106
|
+
`advisory: ${formatSummary(result.advisory)}`,
|
|
107
|
+
];
|
|
108
|
+
for (const finding of result.findings) {
|
|
109
|
+
const location = finding.path
|
|
110
|
+
? ` (${finding.path}${finding.line ? `:${finding.line}` : ""})`
|
|
111
|
+
: "";
|
|
112
|
+
lines.push(`[${finding.classification}/${finding.severity}] ${finding.ruleId}${location}: ${finding.actual}`);
|
|
113
|
+
}
|
|
114
|
+
return `${lines.join("\n")}\n`;
|
|
115
|
+
}
|
|
116
|
+
function formatSummary(summary) {
|
|
117
|
+
return `${summary.status}; ${summary.blockers} blocker(s), ${summary.warnings} warning(s), ${summary.info} info`;
|
|
118
|
+
}
|
|
119
|
+
function requireValue(argv, index, argument) {
|
|
120
|
+
const value = argv[index];
|
|
121
|
+
if (!value || value.startsWith("--"))
|
|
122
|
+
throw new Error(`${argument} requires a value.`);
|
|
123
|
+
return value;
|
|
124
|
+
}
|
|
125
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
|
|
126
|
+
process.exitCode = runBrandlabConformanceCli(process.argv.slice(2));
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Location-page conformance rules (Brandlab Location Pages Standard §11).
|
|
3
|
+
*
|
|
4
|
+
* These checks are heuristics over project source text and sitemap output.
|
|
5
|
+
* They are deliberately conservative: each rule documents its escape hatch so
|
|
6
|
+
* a compliant pattern is never impossible, only explicit.
|
|
7
|
+
*/
|
|
8
|
+
import type { SourcePattern, SourcePatternMatch, SourceTextFile } from "./vitest.js";
|
|
9
|
+
export declare const BRANDLAB_LOCATION_FORBIDDEN_SOURCE_PATTERNS: readonly SourcePattern[];
|
|
10
|
+
/**
|
|
11
|
+
* Flags `approvedIndexable*` assignments whose value is produced by code
|
|
12
|
+
* (call/map/flatMap) instead of an explicit literal list (standard §7.4).
|
|
13
|
+
*
|
|
14
|
+
* Escape hatch: statements that reference an identifier containing "frozen"
|
|
15
|
+
* are allowed — that is the documented snapshot pattern (an explicit,
|
|
16
|
+
* human-edited literal module filtered into record shape).
|
|
17
|
+
*/
|
|
18
|
+
export declare function findGeneratedServiceLocationApprovals(files: readonly SourceTextFile[]): SourcePatternMatch[];
|
|
19
|
+
export interface LocationLinkDumpOptions {
|
|
20
|
+
files: readonly SourceTextFile[];
|
|
21
|
+
/** Path prefixes that identify location URLs, e.g. ['/locations/']. */
|
|
22
|
+
locationPathPrefixes: readonly string[];
|
|
23
|
+
/** Only files whose path matches are checked. Default: footer/nav components. */
|
|
24
|
+
filePathPattern?: RegExp;
|
|
25
|
+
/** Maximum location links allowed per matching file. Default 20 (≈ regions, never all suburbs). */
|
|
26
|
+
maxLinks?: number;
|
|
27
|
+
}
|
|
28
|
+
export interface LocationLinkDumpFinding {
|
|
29
|
+
file: string;
|
|
30
|
+
linkCount: number;
|
|
31
|
+
maxLinks: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Flags footer/nav components that link a suburb dump (standard §6.6 — nav
|
|
35
|
+
* and footer carry region links only; "blocks of text that list cities and
|
|
36
|
+
* regions" is Google's own keyword-stuffing example).
|
|
37
|
+
*/
|
|
38
|
+
export declare function findLocationLinkDumps({ files, locationPathPrefixes, filePathPattern, maxLinks, }: LocationLinkDumpOptions): LocationLinkDumpFinding[];
|
|
39
|
+
/**
|
|
40
|
+
* Copy-hygiene patterns for location data modules (standard §3.8). Applied to
|
|
41
|
+
* `src/data/locations/**` and `src/data/serviceLocations/**` rendered-string
|
|
42
|
+
* sources: unresolved tokens and template wording must never reach a
|
|
43
|
+
* customer-facing surface.
|
|
44
|
+
*/
|
|
45
|
+
export declare const BRANDLAB_LOCATION_COPY_PATTERNS: readonly SourcePattern[];
|
|
46
|
+
export interface LocationUrlShareOptions {
|
|
47
|
+
/** All sitemap entry URLs (absolute or root-relative). */
|
|
48
|
+
urls: readonly string[];
|
|
49
|
+
/** Predicate identifying location-family URLs. */
|
|
50
|
+
isLocationUrl: (url: string) => boolean;
|
|
51
|
+
/** Warn when location URLs exceed this share of all URLs. Default 0.4 (standard §8.8). */
|
|
52
|
+
maxShare?: number;
|
|
53
|
+
}
|
|
54
|
+
export interface LocationUrlShareResult {
|
|
55
|
+
locationUrlCount: number;
|
|
56
|
+
totalUrlCount: number;
|
|
57
|
+
share: number;
|
|
58
|
+
maxShare: number;
|
|
59
|
+
exceeded: boolean;
|
|
60
|
+
}
|
|
61
|
+
/** Volume alarm for location-heavy sitemaps (standard §8.8). */
|
|
62
|
+
export declare function evaluateLocationUrlShare({ urls, isLocationUrl, maxShare, }: LocationUrlShareOptions): LocationUrlShareResult;
|
|
63
|
+
//# sourceMappingURL=locations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locations.d.ts","sourceRoot":"","sources":["../src/locations.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAErF,eAAO,MAAM,2CAA2C,EAAE,SAAS,aAAa,EAkB/E,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,qCAAqC,CACnD,KAAK,EAAE,SAAS,cAAc,EAAE,GAC/B,kBAAkB,EAAE,CAuBtB;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,SAAS,cAAc,EAAE,CAAC;IACjC,uEAAuE;IACvE,oBAAoB,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,iFAAiF;IACjF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mGAAmG;IACnG,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,KAAK,EACL,oBAAoB,EACpB,eAAiC,EACjC,QAAa,GACd,EAAE,uBAAuB,GAAG,uBAAuB,EAAE,CAiBrD;AAED;;;;;GAKG;AACH,eAAO,MAAM,+BAA+B,EAAE,SAAS,aAAa,EAcnE,CAAC;AAEF,MAAM,WAAW,uBAAuB;IACtC,0DAA0D;IAC1D,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,kDAAkD;IAClD,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACxC,0FAA0F;IAC1F,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,sBAAsB;IACrC,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,gEAAgE;AAChE,wBAAgB,wBAAwB,CAAC,EACvC,IAAI,EACJ,aAAa,EACb,QAAc,GACf,EAAE,uBAAuB,GAAG,sBAAsB,CAYlD"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
export const BRANDLAB_LOCATION_FORBIDDEN_SOURCE_PATTERNS = [
|
|
2
|
+
{
|
|
3
|
+
// Rating markup about the business/its services is never emitted
|
|
4
|
+
// (standard §6.2). Matches emission sites, not prose/comments.
|
|
5
|
+
name: "self-serving rating schema emission",
|
|
6
|
+
pattern: /['"]@type['"]\s*:\s*['"](?:AggregateRating|Review)['"]/,
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
name: "self-serving rating schema builder",
|
|
10
|
+
pattern: /\bbuild(?:Service)?(?:AggregateRating|Rating|Review)Schema\b/,
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
// Named constant reused as every sitemap entry's lastModified defeats
|
|
14
|
+
// the freshness signal (standard §6.3). Use per-record
|
|
15
|
+
// lastSubstantialUpdate dates instead.
|
|
16
|
+
name: "constant sitemap lastModified",
|
|
17
|
+
pattern: /lastModified\s*:\s*SITEMAP_LAST_MODIFIED\b/,
|
|
18
|
+
},
|
|
19
|
+
];
|
|
20
|
+
/**
|
|
21
|
+
* Flags `approvedIndexable*` assignments whose value is produced by code
|
|
22
|
+
* (call/map/flatMap) instead of an explicit literal list (standard §7.4).
|
|
23
|
+
*
|
|
24
|
+
* Escape hatch: statements that reference an identifier containing "frozen"
|
|
25
|
+
* are allowed — that is the documented snapshot pattern (an explicit,
|
|
26
|
+
* human-edited literal module filtered into record shape).
|
|
27
|
+
*/
|
|
28
|
+
export function findGeneratedServiceLocationApprovals(files) {
|
|
29
|
+
const matches = [];
|
|
30
|
+
const assignment = /\bapprovedIndexable\w*\s*[:=]/;
|
|
31
|
+
const generated = /[:=][^;\n]*(?:\.flatMap\(|\.map\(|\w+\(\s*\))/;
|
|
32
|
+
for (const file of files) {
|
|
33
|
+
const lines = file.text.split("\n");
|
|
34
|
+
lines.forEach((line, index) => {
|
|
35
|
+
if (!assignment.test(line))
|
|
36
|
+
return;
|
|
37
|
+
if (/frozen/i.test(line))
|
|
38
|
+
return;
|
|
39
|
+
if (!generated.test(line))
|
|
40
|
+
return;
|
|
41
|
+
matches.push({
|
|
42
|
+
file: file.path,
|
|
43
|
+
patternName: "generated service-location approvals",
|
|
44
|
+
line: index + 1,
|
|
45
|
+
column: line.search(assignment) + 1,
|
|
46
|
+
text: line.trim(),
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return matches;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Flags footer/nav components that link a suburb dump (standard §6.6 — nav
|
|
54
|
+
* and footer carry region links only; "blocks of text that list cities and
|
|
55
|
+
* regions" is Google's own keyword-stuffing example).
|
|
56
|
+
*/
|
|
57
|
+
export function findLocationLinkDumps({ files, locationPathPrefixes, filePathPattern = /(footer|nav)/i, maxLinks = 20, }) {
|
|
58
|
+
const findings = [];
|
|
59
|
+
for (const file of files) {
|
|
60
|
+
if (!filePathPattern.test(file.path))
|
|
61
|
+
continue;
|
|
62
|
+
let linkCount = 0;
|
|
63
|
+
for (const prefix of locationPathPrefixes) {
|
|
64
|
+
linkCount += file.text.split(prefix).length - 1;
|
|
65
|
+
}
|
|
66
|
+
if (linkCount > maxLinks) {
|
|
67
|
+
findings.push({ file: file.path, linkCount, maxLinks });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return findings;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Copy-hygiene patterns for location data modules (standard §3.8). Applied to
|
|
74
|
+
* `src/data/locations/**` and `src/data/serviceLocations/**` rendered-string
|
|
75
|
+
* sources: unresolved tokens and template wording must never reach a
|
|
76
|
+
* customer-facing surface.
|
|
77
|
+
*/
|
|
78
|
+
export const BRANDLAB_LOCATION_COPY_PATTERNS = [
|
|
79
|
+
{
|
|
80
|
+
name: "unresolved bracket token",
|
|
81
|
+
pattern: /\[(?:Suburb|Region|Location|Service|Business Name|Phone|Email)\]/,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: "unresolved curly token in string",
|
|
85
|
+
pattern: /['"`][^'"`\n]*\{(?:suburb|region|location|service)\}[^'"`\n]*['"`]/,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: "template wording",
|
|
89
|
+
pattern: /\b(?:lorem ipsum|placeholder copy|copy goes here|content goes here|insert copy|TODO copy|sample copy|starter copy|boilerplate copy)\b/i,
|
|
90
|
+
},
|
|
91
|
+
];
|
|
92
|
+
/** Volume alarm for location-heavy sitemaps (standard §8.8). */
|
|
93
|
+
export function evaluateLocationUrlShare({ urls, isLocationUrl, maxShare = 0.4, }) {
|
|
94
|
+
const totalUrlCount = urls.length;
|
|
95
|
+
const locationUrlCount = urls.filter((url) => isLocationUrl(url)).length;
|
|
96
|
+
const share = totalUrlCount === 0 ? 0 : locationUrlCount / totalUrlCount;
|
|
97
|
+
return {
|
|
98
|
+
locationUrlCount,
|
|
99
|
+
totalUrlCount,
|
|
100
|
+
share,
|
|
101
|
+
maxShare,
|
|
102
|
+
exceeded: share > maxShare,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=locations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locations.js","sourceRoot":"","sources":["../src/locations.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export declare const BRANDLAB_CONFIG_V2_PROFILES: readonly ["next-local-service", "next-root-level", "next-static-legacy", "legacy-php"];
|
|
2
|
+
export interface InspectedBrandlabProjectConfig {
|
|
3
|
+
projectId: string;
|
|
4
|
+
profile: (typeof BRANDLAB_CONFIG_V2_PROFILES)[number];
|
|
5
|
+
routes: {
|
|
6
|
+
families: readonly string[];
|
|
7
|
+
};
|
|
8
|
+
capabilities: {
|
|
9
|
+
blogAdapter: string | null;
|
|
10
|
+
contactEnabled: boolean;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export interface BrandlabConfigV2Issue {
|
|
14
|
+
ruleId: string;
|
|
15
|
+
surface: string;
|
|
16
|
+
severity: "blocker" | "warning" | "info";
|
|
17
|
+
evidenceKind: "static" | "behavioural" | "runtime";
|
|
18
|
+
path?: string;
|
|
19
|
+
line?: number;
|
|
20
|
+
expected: string;
|
|
21
|
+
actual: string;
|
|
22
|
+
remediation: string;
|
|
23
|
+
}
|
|
24
|
+
export interface BrandlabConfigV2Inspection {
|
|
25
|
+
config: InspectedBrandlabProjectConfig | null;
|
|
26
|
+
imports: ReadonlySet<string>;
|
|
27
|
+
executableImports: ReadonlySet<string>;
|
|
28
|
+
issues: BrandlabConfigV2Issue[];
|
|
29
|
+
}
|
|
30
|
+
export declare function inspectBrandlabConfigV2(root: string): BrandlabConfigV2Inspection;
|
|
31
|
+
//# sourceMappingURL=project-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-config.d.ts","sourceRoot":"","sources":["../src/project-config.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,2BAA2B,wFAK9B,CAAC;AA0CX,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,YAAY,EAAE;QACZ,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,cAAc,EAAE,OAAO,CAAC;KACzB,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,MAAM,EAAE,qBAAqB,EAAE,CAAC;CACjC;AAqDD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,0BAA0B,CA2FhF"}
|