@rankcli/agent-runtime 0.0.28 → 0.0.30
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/index.d.mts +198 -163
- package/dist/index.d.ts +198 -163
- package/dist/index.js +188 -16
- package/dist/index.mjs +185 -16
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3352,7 +3352,9 @@ __export(index_exports, {
|
|
|
3352
3352
|
analyzeUrlSafety: () => analyzeUrlSafety,
|
|
3353
3353
|
analyzers: () => analyzers_exports,
|
|
3354
3354
|
applyFixes: () => applyFixes,
|
|
3355
|
+
assessReplacement: () => assessReplacement,
|
|
3355
3356
|
buildDeterministicFixes: () => buildDeterministicFixes,
|
|
3357
|
+
buildFrameworkGuidance: () => buildFrameworkGuidance,
|
|
3356
3358
|
buildGSCApiRequest: () => buildGSCApiRequest,
|
|
3357
3359
|
buildGSCRequest: () => buildGSCRequest,
|
|
3358
3360
|
calculateAIVisibilityScore: () => calculateAIVisibilityScore,
|
|
@@ -3393,6 +3395,7 @@ __export(index_exports, {
|
|
|
3393
3395
|
createPullRequest: () => createPullRequest,
|
|
3394
3396
|
createSEOCommit: () => createSEOCommit,
|
|
3395
3397
|
createSEOCommits: () => createSEOCommits,
|
|
3398
|
+
describeConcerns: () => describeConcerns,
|
|
3396
3399
|
detectDuplicates: () => detectDuplicates,
|
|
3397
3400
|
detectFramework: () => detectFramework4,
|
|
3398
3401
|
detectGitHubPages: () => detectGitHubPages,
|
|
@@ -12740,20 +12743,20 @@ function findMissingPreconnects(thirdPartyDomains, existingHints) {
|
|
|
12740
12743
|
}
|
|
12741
12744
|
})
|
|
12742
12745
|
);
|
|
12743
|
-
const
|
|
12746
|
+
const missing2 = [];
|
|
12744
12747
|
for (const domain of thirdPartyDomains) {
|
|
12745
12748
|
try {
|
|
12746
12749
|
const hostname = new URL(domain).hostname;
|
|
12747
12750
|
const recommended = CRITICAL_THIRD_PARTIES[hostname] || [domain];
|
|
12748
12751
|
for (const rec of recommended) {
|
|
12749
12752
|
if (!preconnected.has(rec)) {
|
|
12750
|
-
|
|
12753
|
+
missing2.push(rec);
|
|
12751
12754
|
}
|
|
12752
12755
|
}
|
|
12753
12756
|
} catch {
|
|
12754
12757
|
}
|
|
12755
12758
|
}
|
|
12756
|
-
return [...new Set(
|
|
12759
|
+
return [...new Set(missing2)];
|
|
12757
12760
|
}
|
|
12758
12761
|
function analyzeResourceHints(html, url) {
|
|
12759
12762
|
const issues = [];
|
|
@@ -32956,6 +32959,8 @@ function buildDeterministicFixes(input) {
|
|
|
32956
32959
|
const codes = new Set(input.issueCodes);
|
|
32957
32960
|
const has = (...wanted) => wanted.some((code) => codes.has(code));
|
|
32958
32961
|
const root = input.appRoot || "";
|
|
32962
|
+
const publicDir = input.publicDir === void 0 ? "public/" : input.publicDir.replace(/\/?$/, input.publicDir ? "/" : "");
|
|
32963
|
+
const served = `${root}${publicDir}`;
|
|
32959
32964
|
const files = [];
|
|
32960
32965
|
const addressed = [];
|
|
32961
32966
|
const base = input.siteUrl.replace(/\/$/, "");
|
|
@@ -32963,7 +32968,7 @@ function buildDeterministicFixes(input) {
|
|
|
32963
32968
|
const description = input.description ?? metaDescriptionOf(input.existing.indexHtml);
|
|
32964
32969
|
if (has("ROBOTS_TXT_MISSING", "AI_BOTS_BLOCKED", "AI_CRAWLERS_BLOCKED") && !input.existing.robotsTxt) {
|
|
32965
32970
|
files.push({
|
|
32966
|
-
path: `${
|
|
32971
|
+
path: `${served}robots.txt`,
|
|
32967
32972
|
content: generateAIFriendlyRobotsTxt(base),
|
|
32968
32973
|
message: "fix(seo): add robots.txt allowing search and AI crawlers",
|
|
32969
32974
|
action: "created"
|
|
@@ -32972,7 +32977,7 @@ function buildDeterministicFixes(input) {
|
|
|
32972
32977
|
}
|
|
32973
32978
|
if (has("SITEMAP_MISSING") && !input.existing.sitemapXml) {
|
|
32974
32979
|
files.push({
|
|
32975
|
-
path: `${
|
|
32980
|
+
path: `${served}sitemap.xml`,
|
|
32976
32981
|
content: sitemap(pages),
|
|
32977
32982
|
message: "fix(seo): add sitemap.xml",
|
|
32978
32983
|
action: "created"
|
|
@@ -32981,7 +32986,7 @@ function buildDeterministicFixes(input) {
|
|
|
32981
32986
|
}
|
|
32982
32987
|
if (has("LLMS_TXT_MISSING") && !input.existing.llmsTxt) {
|
|
32983
32988
|
files.push({
|
|
32984
|
-
path: `${
|
|
32989
|
+
path: `${served}llms.txt`,
|
|
32985
32990
|
content: llmsTxt(input.siteName, base, description, pages),
|
|
32986
32991
|
message: "fix(seo): add llms.txt for AI assistants",
|
|
32987
32992
|
action: "created"
|
|
@@ -33035,12 +33040,179 @@ function findAppRoot(files) {
|
|
|
33035
33040
|
const best = candidates[0];
|
|
33036
33041
|
return best === "" ? "" : `${best}/`;
|
|
33037
33042
|
}
|
|
33043
|
+
const PAGES_MARKERS = ["CNAME", ".nojekyll", "404.html", "site.webmanifest"];
|
|
33044
|
+
const staticRoots = real.filter((f) => f === "index.html" || f.endsWith("/index.html")).map(directoryOf).filter((dir) => PAGES_MARKERS.some((marker) => real.includes(dir ? `${dir}/${marker}` : marker)));
|
|
33045
|
+
if (staticRoots.length > 0) {
|
|
33046
|
+
staticRoots.sort((a, b) => a.split("/").length - b.split("/").length || a.localeCompare(b));
|
|
33047
|
+
return staticRoots[0] === "" ? "" : `${staticRoots[0]}/`;
|
|
33048
|
+
}
|
|
33038
33049
|
const nonRoot = [...packageDirs].filter((dir) => dir !== "");
|
|
33039
33050
|
if (packageDirs.has("") || nonRoot.length === 0) return "";
|
|
33040
33051
|
nonRoot.sort((a, b) => a.split("/").length - b.split("/").length || a.localeCompare(b));
|
|
33041
33052
|
return `${nonRoot[0]}/`;
|
|
33042
33053
|
}
|
|
33043
33054
|
|
|
33055
|
+
// src/fixer/safety.ts
|
|
33056
|
+
var HTML_EXT = /\.html?$/i;
|
|
33057
|
+
var SOURCE_EXT = /\.(ts|tsx|js|jsx|mjs|cjs|vue|svelte|astro)$/i;
|
|
33058
|
+
function attributeValues(html, pattern) {
|
|
33059
|
+
const found = /* @__PURE__ */ new Set();
|
|
33060
|
+
for (const match of html.matchAll(pattern)) {
|
|
33061
|
+
const value = match[1]?.trim();
|
|
33062
|
+
if (value) found.add(value);
|
|
33063
|
+
}
|
|
33064
|
+
return found;
|
|
33065
|
+
}
|
|
33066
|
+
function scriptSources(html) {
|
|
33067
|
+
return attributeValues(html, /<script[^>]+src=["']([^"']+)["']/gi);
|
|
33068
|
+
}
|
|
33069
|
+
function stylesheetHrefs(html) {
|
|
33070
|
+
return attributeValues(html, /<link[^>]+href=["']([^"']+)["'][^>]*>/gi);
|
|
33071
|
+
}
|
|
33072
|
+
function verificationTokens(html) {
|
|
33073
|
+
const found = /* @__PURE__ */ new Set();
|
|
33074
|
+
for (const match of html.matchAll(/<meta[^>]+name=["']([^"']*(?:site-verification|verify|verification)[^"']*)["'][^>]*>/gi)) {
|
|
33075
|
+
found.add(match[1].toLowerCase());
|
|
33076
|
+
}
|
|
33077
|
+
return found;
|
|
33078
|
+
}
|
|
33079
|
+
function importSpecifiers(source) {
|
|
33080
|
+
const found = /* @__PURE__ */ new Set();
|
|
33081
|
+
for (const match of source.matchAll(/\bfrom\s+["']([^"']+)["']/g)) found.add(match[1]);
|
|
33082
|
+
for (const match of source.matchAll(/\brequire\(\s*["']([^"']+)["']\s*\)/g)) found.add(match[1]);
|
|
33083
|
+
return found;
|
|
33084
|
+
}
|
|
33085
|
+
function missing(before, after) {
|
|
33086
|
+
return [...before].filter((value) => !after.has(value));
|
|
33087
|
+
}
|
|
33088
|
+
function xmlConcern(xml) {
|
|
33089
|
+
const stack = [];
|
|
33090
|
+
for (const match of xml.matchAll(/<\/?([A-Za-z_][\w:.-]*)[^>]*?(\/?)>/g)) {
|
|
33091
|
+
const [tag, name, selfClosing] = match;
|
|
33092
|
+
if (tag.startsWith("<?") || tag.startsWith("<!")) continue;
|
|
33093
|
+
if (tag.startsWith("</")) {
|
|
33094
|
+
if (stack.pop() !== name) return `unbalanced </${name}>`;
|
|
33095
|
+
} else if (!selfClosing) {
|
|
33096
|
+
stack.push(name);
|
|
33097
|
+
}
|
|
33098
|
+
}
|
|
33099
|
+
return stack.length ? `unclosed <${stack[stack.length - 1]}>` : null;
|
|
33100
|
+
}
|
|
33101
|
+
function sitemapUrlCount(xml) {
|
|
33102
|
+
return [...xml.matchAll(/<loc>/gi)].length;
|
|
33103
|
+
}
|
|
33104
|
+
function newestLastmod(xml) {
|
|
33105
|
+
const dates = [...xml.matchAll(/<lastmod>([^<]+)<\/lastmod>/gi)].map((m) => Date.parse(m[1].trim())).filter((t) => !Number.isNaN(t));
|
|
33106
|
+
return dates.length ? Math.max(...dates) : null;
|
|
33107
|
+
}
|
|
33108
|
+
function jsonLdConcerns(html) {
|
|
33109
|
+
const bad = [];
|
|
33110
|
+
for (const match of html.matchAll(/<script[^>]+type=["']application\/ld\+json["'][^>]*>([\s\S]*?)<\/script>/gi)) {
|
|
33111
|
+
try {
|
|
33112
|
+
JSON.parse(match[1]);
|
|
33113
|
+
} catch (error) {
|
|
33114
|
+
bad.push(error.message);
|
|
33115
|
+
}
|
|
33116
|
+
}
|
|
33117
|
+
return bad;
|
|
33118
|
+
}
|
|
33119
|
+
function assessReplacement(path3, original, replacement) {
|
|
33120
|
+
const concerns = [];
|
|
33121
|
+
const add = (code, detail) => concerns.push({ code, detail });
|
|
33122
|
+
if (/\.json$/i.test(path3) || /\.webmanifest$/i.test(path3)) {
|
|
33123
|
+
try {
|
|
33124
|
+
JSON.parse(replacement);
|
|
33125
|
+
} catch (error) {
|
|
33126
|
+
add("invalid_json", error.message);
|
|
33127
|
+
}
|
|
33128
|
+
}
|
|
33129
|
+
if (/\.xml$/i.test(path3)) {
|
|
33130
|
+
const problem = xmlConcern(replacement);
|
|
33131
|
+
if (problem) add("invalid_xml", problem);
|
|
33132
|
+
}
|
|
33133
|
+
if (HTML_EXT.test(path3)) {
|
|
33134
|
+
for (const problem of jsonLdConcerns(replacement)) add("invalid_json_ld", problem);
|
|
33135
|
+
}
|
|
33136
|
+
if (original === null) return concerns;
|
|
33137
|
+
if (replacement.length < original.length * 0.6) {
|
|
33138
|
+
add("shrunk", `${Math.round(replacement.length / original.length * 100)}% of the original length`);
|
|
33139
|
+
}
|
|
33140
|
+
if (HTML_EXT.test(path3)) {
|
|
33141
|
+
for (const tag of ["</head>", "</body>", "</html>"]) {
|
|
33142
|
+
if (original.includes(tag) && !replacement.includes(tag)) add("lost_closing_tag", tag);
|
|
33143
|
+
}
|
|
33144
|
+
const lostScripts = missing(scriptSources(original), scriptSources(replacement));
|
|
33145
|
+
if (lostScripts.length) add("lost_script", lostScripts.join(", "));
|
|
33146
|
+
const lostStyles = missing(stylesheetHrefs(original), stylesheetHrefs(replacement));
|
|
33147
|
+
if (lostStyles.length) add("lost_stylesheet", lostStyles.join(", "));
|
|
33148
|
+
const lostVerification = missing(verificationTokens(original), verificationTokens(replacement));
|
|
33149
|
+
if (lostVerification.length) add("lost_verification", lostVerification.join(", "));
|
|
33150
|
+
}
|
|
33151
|
+
if (SOURCE_EXT.test(path3)) {
|
|
33152
|
+
const lostImports = missing(importSpecifiers(original), importSpecifiers(replacement));
|
|
33153
|
+
if (lostImports.length) add("lost_import", lostImports.join(", "));
|
|
33154
|
+
}
|
|
33155
|
+
if (/sitemap[^/]*\.xml$/i.test(path3)) {
|
|
33156
|
+
const before = sitemapUrlCount(original);
|
|
33157
|
+
const after = sitemapUrlCount(replacement);
|
|
33158
|
+
if (after < before) add("fewer_sitemap_urls", `${before} -> ${after}`);
|
|
33159
|
+
const newestBefore = newestLastmod(original);
|
|
33160
|
+
const newestAfter = newestLastmod(replacement);
|
|
33161
|
+
if (newestBefore !== null && newestAfter !== null && newestAfter < newestBefore) {
|
|
33162
|
+
add(
|
|
33163
|
+
"lastmod_moved_backwards",
|
|
33164
|
+
`${new Date(newestBefore).toISOString().slice(0, 10)} -> ${new Date(newestAfter).toISOString().slice(0, 10)}`
|
|
33165
|
+
);
|
|
33166
|
+
}
|
|
33167
|
+
}
|
|
33168
|
+
return concerns;
|
|
33169
|
+
}
|
|
33170
|
+
function describeConcerns(concerns) {
|
|
33171
|
+
return concerns.map((c) => `${c.code} (${c.detail})`).join("; ");
|
|
33172
|
+
}
|
|
33173
|
+
|
|
33174
|
+
// src/fixer/framework-guidance.ts
|
|
33175
|
+
var DEFAULT_MAX_CHARS = 4e3;
|
|
33176
|
+
function checkBlock(check) {
|
|
33177
|
+
const fix = check.howToFix?.trim();
|
|
33178
|
+
return [
|
|
33179
|
+
`### ${check.title}${check.code ? ` (${check.code})` : ""}`,
|
|
33180
|
+
check.description?.trim(),
|
|
33181
|
+
fix ? `How this framework wants it fixed:
|
|
33182
|
+
${fix}` : null
|
|
33183
|
+
].filter(Boolean).join("\n");
|
|
33184
|
+
}
|
|
33185
|
+
function buildFrameworkGuidance(recipe, options = {}) {
|
|
33186
|
+
if (!recipe) return "";
|
|
33187
|
+
const budget = options.maxChars ?? DEFAULT_MAX_CHARS;
|
|
33188
|
+
const reported = new Set(options.issueCodes ?? []);
|
|
33189
|
+
const header = [
|
|
33190
|
+
`## ${recipe.display_name} conventions`,
|
|
33191
|
+
`Renders on the server by default: ${recipe.ssr_default ? "yes" : "no"}.`,
|
|
33192
|
+
recipe.seo_challenges?.length ? `Known traps in this framework:
|
|
33193
|
+
${recipe.seo_challenges.map((c) => `- ${c}`).join("\n")}` : null
|
|
33194
|
+
].filter(Boolean).join("\n");
|
|
33195
|
+
const checks = recipe.checks ?? [];
|
|
33196
|
+
const relevant = checks.filter((c) => c.code && reported.has(c.code));
|
|
33197
|
+
const rest = checks.filter((c) => !relevant.includes(c));
|
|
33198
|
+
let out = header;
|
|
33199
|
+
let included = 0;
|
|
33200
|
+
for (const check of [...relevant, ...rest]) {
|
|
33201
|
+
const block = checkBlock(check);
|
|
33202
|
+
if (out.length + block.length + 2 > budget) break;
|
|
33203
|
+
out += `
|
|
33204
|
+
|
|
33205
|
+
${block}`;
|
|
33206
|
+
included++;
|
|
33207
|
+
}
|
|
33208
|
+
if (included < checks.length) {
|
|
33209
|
+
out += `
|
|
33210
|
+
|
|
33211
|
+
_(${checks.length - included} more ${recipe.display_name} checks omitted.)_`;
|
|
33212
|
+
}
|
|
33213
|
+
return out;
|
|
33214
|
+
}
|
|
33215
|
+
|
|
33044
33216
|
// src/git/commit-helper.ts
|
|
33045
33217
|
var import_child_process2 = require("child_process");
|
|
33046
33218
|
var import_util2 = require("util");
|
|
@@ -35586,19 +35758,16 @@ __export(frameworks_exports, {
|
|
|
35586
35758
|
loadRecipe: () => loadRecipe
|
|
35587
35759
|
});
|
|
35588
35760
|
var SUPPORTED_FRAMEWORKS = [
|
|
35589
|
-
"react",
|
|
35590
|
-
"nextjs",
|
|
35591
|
-
"vue",
|
|
35592
|
-
"nuxt",
|
|
35593
35761
|
"angular",
|
|
35594
|
-
"svelte",
|
|
35595
|
-
"sveltekit",
|
|
35596
35762
|
"astro",
|
|
35597
|
-
"remix",
|
|
35598
|
-
"gatsby",
|
|
35599
|
-
"rails",
|
|
35600
|
-
"laravel",
|
|
35601
35763
|
"django",
|
|
35764
|
+
"laravel",
|
|
35765
|
+
"nextjs",
|
|
35766
|
+
"nuxt",
|
|
35767
|
+
"rails",
|
|
35768
|
+
"react",
|
|
35769
|
+
"sveltekit",
|
|
35770
|
+
"vue",
|
|
35602
35771
|
"wordpress"
|
|
35603
35772
|
];
|
|
35604
35773
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -35648,7 +35817,9 @@ var SUPPORTED_FRAMEWORKS = [
|
|
|
35648
35817
|
analyzeUrlSafety,
|
|
35649
35818
|
analyzers,
|
|
35650
35819
|
applyFixes,
|
|
35820
|
+
assessReplacement,
|
|
35651
35821
|
buildDeterministicFixes,
|
|
35822
|
+
buildFrameworkGuidance,
|
|
35652
35823
|
buildGSCApiRequest,
|
|
35653
35824
|
buildGSCRequest,
|
|
35654
35825
|
calculateAIVisibilityScore,
|
|
@@ -35689,6 +35860,7 @@ var SUPPORTED_FRAMEWORKS = [
|
|
|
35689
35860
|
createPullRequest,
|
|
35690
35861
|
createSEOCommit,
|
|
35691
35862
|
createSEOCommits,
|
|
35863
|
+
describeConcerns,
|
|
35692
35864
|
detectDuplicates,
|
|
35693
35865
|
detectFramework,
|
|
35694
35866
|
detectGitHubPages,
|
package/dist/index.mjs
CHANGED
|
@@ -9199,20 +9199,20 @@ function findMissingPreconnects(thirdPartyDomains, existingHints) {
|
|
|
9199
9199
|
}
|
|
9200
9200
|
})
|
|
9201
9201
|
);
|
|
9202
|
-
const
|
|
9202
|
+
const missing2 = [];
|
|
9203
9203
|
for (const domain of thirdPartyDomains) {
|
|
9204
9204
|
try {
|
|
9205
9205
|
const hostname = new URL(domain).hostname;
|
|
9206
9206
|
const recommended = CRITICAL_THIRD_PARTIES[hostname] || [domain];
|
|
9207
9207
|
for (const rec of recommended) {
|
|
9208
9208
|
if (!preconnected.has(rec)) {
|
|
9209
|
-
|
|
9209
|
+
missing2.push(rec);
|
|
9210
9210
|
}
|
|
9211
9211
|
}
|
|
9212
9212
|
} catch {
|
|
9213
9213
|
}
|
|
9214
9214
|
}
|
|
9215
|
-
return [...new Set(
|
|
9215
|
+
return [...new Set(missing2)];
|
|
9216
9216
|
}
|
|
9217
9217
|
function analyzeResourceHints(html, url) {
|
|
9218
9218
|
const issues = [];
|
|
@@ -29388,6 +29388,8 @@ function buildDeterministicFixes(input) {
|
|
|
29388
29388
|
const codes = new Set(input.issueCodes);
|
|
29389
29389
|
const has = (...wanted) => wanted.some((code) => codes.has(code));
|
|
29390
29390
|
const root = input.appRoot || "";
|
|
29391
|
+
const publicDir = input.publicDir === void 0 ? "public/" : input.publicDir.replace(/\/?$/, input.publicDir ? "/" : "");
|
|
29392
|
+
const served = `${root}${publicDir}`;
|
|
29391
29393
|
const files = [];
|
|
29392
29394
|
const addressed = [];
|
|
29393
29395
|
const base = input.siteUrl.replace(/\/$/, "");
|
|
@@ -29395,7 +29397,7 @@ function buildDeterministicFixes(input) {
|
|
|
29395
29397
|
const description = input.description ?? metaDescriptionOf(input.existing.indexHtml);
|
|
29396
29398
|
if (has("ROBOTS_TXT_MISSING", "AI_BOTS_BLOCKED", "AI_CRAWLERS_BLOCKED") && !input.existing.robotsTxt) {
|
|
29397
29399
|
files.push({
|
|
29398
|
-
path: `${
|
|
29400
|
+
path: `${served}robots.txt`,
|
|
29399
29401
|
content: generateAIFriendlyRobotsTxt(base),
|
|
29400
29402
|
message: "fix(seo): add robots.txt allowing search and AI crawlers",
|
|
29401
29403
|
action: "created"
|
|
@@ -29404,7 +29406,7 @@ function buildDeterministicFixes(input) {
|
|
|
29404
29406
|
}
|
|
29405
29407
|
if (has("SITEMAP_MISSING") && !input.existing.sitemapXml) {
|
|
29406
29408
|
files.push({
|
|
29407
|
-
path: `${
|
|
29409
|
+
path: `${served}sitemap.xml`,
|
|
29408
29410
|
content: sitemap(pages),
|
|
29409
29411
|
message: "fix(seo): add sitemap.xml",
|
|
29410
29412
|
action: "created"
|
|
@@ -29413,7 +29415,7 @@ function buildDeterministicFixes(input) {
|
|
|
29413
29415
|
}
|
|
29414
29416
|
if (has("LLMS_TXT_MISSING") && !input.existing.llmsTxt) {
|
|
29415
29417
|
files.push({
|
|
29416
|
-
path: `${
|
|
29418
|
+
path: `${served}llms.txt`,
|
|
29417
29419
|
content: llmsTxt(input.siteName, base, description, pages),
|
|
29418
29420
|
message: "fix(seo): add llms.txt for AI assistants",
|
|
29419
29421
|
action: "created"
|
|
@@ -29467,12 +29469,179 @@ function findAppRoot(files) {
|
|
|
29467
29469
|
const best = candidates[0];
|
|
29468
29470
|
return best === "" ? "" : `${best}/`;
|
|
29469
29471
|
}
|
|
29472
|
+
const PAGES_MARKERS = ["CNAME", ".nojekyll", "404.html", "site.webmanifest"];
|
|
29473
|
+
const staticRoots = real.filter((f) => f === "index.html" || f.endsWith("/index.html")).map(directoryOf).filter((dir) => PAGES_MARKERS.some((marker) => real.includes(dir ? `${dir}/${marker}` : marker)));
|
|
29474
|
+
if (staticRoots.length > 0) {
|
|
29475
|
+
staticRoots.sort((a, b) => a.split("/").length - b.split("/").length || a.localeCompare(b));
|
|
29476
|
+
return staticRoots[0] === "" ? "" : `${staticRoots[0]}/`;
|
|
29477
|
+
}
|
|
29470
29478
|
const nonRoot = [...packageDirs].filter((dir) => dir !== "");
|
|
29471
29479
|
if (packageDirs.has("") || nonRoot.length === 0) return "";
|
|
29472
29480
|
nonRoot.sort((a, b) => a.split("/").length - b.split("/").length || a.localeCompare(b));
|
|
29473
29481
|
return `${nonRoot[0]}/`;
|
|
29474
29482
|
}
|
|
29475
29483
|
|
|
29484
|
+
// src/fixer/safety.ts
|
|
29485
|
+
var HTML_EXT = /\.html?$/i;
|
|
29486
|
+
var SOURCE_EXT = /\.(ts|tsx|js|jsx|mjs|cjs|vue|svelte|astro)$/i;
|
|
29487
|
+
function attributeValues(html, pattern) {
|
|
29488
|
+
const found = /* @__PURE__ */ new Set();
|
|
29489
|
+
for (const match of html.matchAll(pattern)) {
|
|
29490
|
+
const value = match[1]?.trim();
|
|
29491
|
+
if (value) found.add(value);
|
|
29492
|
+
}
|
|
29493
|
+
return found;
|
|
29494
|
+
}
|
|
29495
|
+
function scriptSources(html) {
|
|
29496
|
+
return attributeValues(html, /<script[^>]+src=["']([^"']+)["']/gi);
|
|
29497
|
+
}
|
|
29498
|
+
function stylesheetHrefs(html) {
|
|
29499
|
+
return attributeValues(html, /<link[^>]+href=["']([^"']+)["'][^>]*>/gi);
|
|
29500
|
+
}
|
|
29501
|
+
function verificationTokens(html) {
|
|
29502
|
+
const found = /* @__PURE__ */ new Set();
|
|
29503
|
+
for (const match of html.matchAll(/<meta[^>]+name=["']([^"']*(?:site-verification|verify|verification)[^"']*)["'][^>]*>/gi)) {
|
|
29504
|
+
found.add(match[1].toLowerCase());
|
|
29505
|
+
}
|
|
29506
|
+
return found;
|
|
29507
|
+
}
|
|
29508
|
+
function importSpecifiers(source) {
|
|
29509
|
+
const found = /* @__PURE__ */ new Set();
|
|
29510
|
+
for (const match of source.matchAll(/\bfrom\s+["']([^"']+)["']/g)) found.add(match[1]);
|
|
29511
|
+
for (const match of source.matchAll(/\brequire\(\s*["']([^"']+)["']\s*\)/g)) found.add(match[1]);
|
|
29512
|
+
return found;
|
|
29513
|
+
}
|
|
29514
|
+
function missing(before, after) {
|
|
29515
|
+
return [...before].filter((value) => !after.has(value));
|
|
29516
|
+
}
|
|
29517
|
+
function xmlConcern(xml) {
|
|
29518
|
+
const stack = [];
|
|
29519
|
+
for (const match of xml.matchAll(/<\/?([A-Za-z_][\w:.-]*)[^>]*?(\/?)>/g)) {
|
|
29520
|
+
const [tag, name, selfClosing] = match;
|
|
29521
|
+
if (tag.startsWith("<?") || tag.startsWith("<!")) continue;
|
|
29522
|
+
if (tag.startsWith("</")) {
|
|
29523
|
+
if (stack.pop() !== name) return `unbalanced </${name}>`;
|
|
29524
|
+
} else if (!selfClosing) {
|
|
29525
|
+
stack.push(name);
|
|
29526
|
+
}
|
|
29527
|
+
}
|
|
29528
|
+
return stack.length ? `unclosed <${stack[stack.length - 1]}>` : null;
|
|
29529
|
+
}
|
|
29530
|
+
function sitemapUrlCount(xml) {
|
|
29531
|
+
return [...xml.matchAll(/<loc>/gi)].length;
|
|
29532
|
+
}
|
|
29533
|
+
function newestLastmod(xml) {
|
|
29534
|
+
const dates = [...xml.matchAll(/<lastmod>([^<]+)<\/lastmod>/gi)].map((m) => Date.parse(m[1].trim())).filter((t) => !Number.isNaN(t));
|
|
29535
|
+
return dates.length ? Math.max(...dates) : null;
|
|
29536
|
+
}
|
|
29537
|
+
function jsonLdConcerns(html) {
|
|
29538
|
+
const bad = [];
|
|
29539
|
+
for (const match of html.matchAll(/<script[^>]+type=["']application\/ld\+json["'][^>]*>([\s\S]*?)<\/script>/gi)) {
|
|
29540
|
+
try {
|
|
29541
|
+
JSON.parse(match[1]);
|
|
29542
|
+
} catch (error) {
|
|
29543
|
+
bad.push(error.message);
|
|
29544
|
+
}
|
|
29545
|
+
}
|
|
29546
|
+
return bad;
|
|
29547
|
+
}
|
|
29548
|
+
function assessReplacement(path3, original, replacement) {
|
|
29549
|
+
const concerns = [];
|
|
29550
|
+
const add = (code, detail) => concerns.push({ code, detail });
|
|
29551
|
+
if (/\.json$/i.test(path3) || /\.webmanifest$/i.test(path3)) {
|
|
29552
|
+
try {
|
|
29553
|
+
JSON.parse(replacement);
|
|
29554
|
+
} catch (error) {
|
|
29555
|
+
add("invalid_json", error.message);
|
|
29556
|
+
}
|
|
29557
|
+
}
|
|
29558
|
+
if (/\.xml$/i.test(path3)) {
|
|
29559
|
+
const problem = xmlConcern(replacement);
|
|
29560
|
+
if (problem) add("invalid_xml", problem);
|
|
29561
|
+
}
|
|
29562
|
+
if (HTML_EXT.test(path3)) {
|
|
29563
|
+
for (const problem of jsonLdConcerns(replacement)) add("invalid_json_ld", problem);
|
|
29564
|
+
}
|
|
29565
|
+
if (original === null) return concerns;
|
|
29566
|
+
if (replacement.length < original.length * 0.6) {
|
|
29567
|
+
add("shrunk", `${Math.round(replacement.length / original.length * 100)}% of the original length`);
|
|
29568
|
+
}
|
|
29569
|
+
if (HTML_EXT.test(path3)) {
|
|
29570
|
+
for (const tag of ["</head>", "</body>", "</html>"]) {
|
|
29571
|
+
if (original.includes(tag) && !replacement.includes(tag)) add("lost_closing_tag", tag);
|
|
29572
|
+
}
|
|
29573
|
+
const lostScripts = missing(scriptSources(original), scriptSources(replacement));
|
|
29574
|
+
if (lostScripts.length) add("lost_script", lostScripts.join(", "));
|
|
29575
|
+
const lostStyles = missing(stylesheetHrefs(original), stylesheetHrefs(replacement));
|
|
29576
|
+
if (lostStyles.length) add("lost_stylesheet", lostStyles.join(", "));
|
|
29577
|
+
const lostVerification = missing(verificationTokens(original), verificationTokens(replacement));
|
|
29578
|
+
if (lostVerification.length) add("lost_verification", lostVerification.join(", "));
|
|
29579
|
+
}
|
|
29580
|
+
if (SOURCE_EXT.test(path3)) {
|
|
29581
|
+
const lostImports = missing(importSpecifiers(original), importSpecifiers(replacement));
|
|
29582
|
+
if (lostImports.length) add("lost_import", lostImports.join(", "));
|
|
29583
|
+
}
|
|
29584
|
+
if (/sitemap[^/]*\.xml$/i.test(path3)) {
|
|
29585
|
+
const before = sitemapUrlCount(original);
|
|
29586
|
+
const after = sitemapUrlCount(replacement);
|
|
29587
|
+
if (after < before) add("fewer_sitemap_urls", `${before} -> ${after}`);
|
|
29588
|
+
const newestBefore = newestLastmod(original);
|
|
29589
|
+
const newestAfter = newestLastmod(replacement);
|
|
29590
|
+
if (newestBefore !== null && newestAfter !== null && newestAfter < newestBefore) {
|
|
29591
|
+
add(
|
|
29592
|
+
"lastmod_moved_backwards",
|
|
29593
|
+
`${new Date(newestBefore).toISOString().slice(0, 10)} -> ${new Date(newestAfter).toISOString().slice(0, 10)}`
|
|
29594
|
+
);
|
|
29595
|
+
}
|
|
29596
|
+
}
|
|
29597
|
+
return concerns;
|
|
29598
|
+
}
|
|
29599
|
+
function describeConcerns(concerns) {
|
|
29600
|
+
return concerns.map((c) => `${c.code} (${c.detail})`).join("; ");
|
|
29601
|
+
}
|
|
29602
|
+
|
|
29603
|
+
// src/fixer/framework-guidance.ts
|
|
29604
|
+
var DEFAULT_MAX_CHARS = 4e3;
|
|
29605
|
+
function checkBlock(check) {
|
|
29606
|
+
const fix = check.howToFix?.trim();
|
|
29607
|
+
return [
|
|
29608
|
+
`### ${check.title}${check.code ? ` (${check.code})` : ""}`,
|
|
29609
|
+
check.description?.trim(),
|
|
29610
|
+
fix ? `How this framework wants it fixed:
|
|
29611
|
+
${fix}` : null
|
|
29612
|
+
].filter(Boolean).join("\n");
|
|
29613
|
+
}
|
|
29614
|
+
function buildFrameworkGuidance(recipe, options = {}) {
|
|
29615
|
+
if (!recipe) return "";
|
|
29616
|
+
const budget = options.maxChars ?? DEFAULT_MAX_CHARS;
|
|
29617
|
+
const reported = new Set(options.issueCodes ?? []);
|
|
29618
|
+
const header = [
|
|
29619
|
+
`## ${recipe.display_name} conventions`,
|
|
29620
|
+
`Renders on the server by default: ${recipe.ssr_default ? "yes" : "no"}.`,
|
|
29621
|
+
recipe.seo_challenges?.length ? `Known traps in this framework:
|
|
29622
|
+
${recipe.seo_challenges.map((c) => `- ${c}`).join("\n")}` : null
|
|
29623
|
+
].filter(Boolean).join("\n");
|
|
29624
|
+
const checks = recipe.checks ?? [];
|
|
29625
|
+
const relevant = checks.filter((c) => c.code && reported.has(c.code));
|
|
29626
|
+
const rest = checks.filter((c) => !relevant.includes(c));
|
|
29627
|
+
let out = header;
|
|
29628
|
+
let included = 0;
|
|
29629
|
+
for (const check of [...relevant, ...rest]) {
|
|
29630
|
+
const block = checkBlock(check);
|
|
29631
|
+
if (out.length + block.length + 2 > budget) break;
|
|
29632
|
+
out += `
|
|
29633
|
+
|
|
29634
|
+
${block}`;
|
|
29635
|
+
included++;
|
|
29636
|
+
}
|
|
29637
|
+
if (included < checks.length) {
|
|
29638
|
+
out += `
|
|
29639
|
+
|
|
29640
|
+
_(${checks.length - included} more ${recipe.display_name} checks omitted.)_`;
|
|
29641
|
+
}
|
|
29642
|
+
return out;
|
|
29643
|
+
}
|
|
29644
|
+
|
|
29476
29645
|
// src/git/commit-helper.ts
|
|
29477
29646
|
import { execSync, exec as exec2 } from "child_process";
|
|
29478
29647
|
import { promisify as promisify2 } from "util";
|
|
@@ -32011,19 +32180,16 @@ __export(frameworks_exports, {
|
|
|
32011
32180
|
loadRecipe: () => loadRecipe
|
|
32012
32181
|
});
|
|
32013
32182
|
var SUPPORTED_FRAMEWORKS = [
|
|
32014
|
-
"react",
|
|
32015
|
-
"nextjs",
|
|
32016
|
-
"vue",
|
|
32017
|
-
"nuxt",
|
|
32018
32183
|
"angular",
|
|
32019
|
-
"svelte",
|
|
32020
|
-
"sveltekit",
|
|
32021
32184
|
"astro",
|
|
32022
|
-
"remix",
|
|
32023
|
-
"gatsby",
|
|
32024
|
-
"rails",
|
|
32025
|
-
"laravel",
|
|
32026
32185
|
"django",
|
|
32186
|
+
"laravel",
|
|
32187
|
+
"nextjs",
|
|
32188
|
+
"nuxt",
|
|
32189
|
+
"rails",
|
|
32190
|
+
"react",
|
|
32191
|
+
"sveltekit",
|
|
32192
|
+
"vue",
|
|
32027
32193
|
"wordpress"
|
|
32028
32194
|
];
|
|
32029
32195
|
export {
|
|
@@ -32072,7 +32238,9 @@ export {
|
|
|
32072
32238
|
analyzeUrlSafety,
|
|
32073
32239
|
analyzers_exports as analyzers,
|
|
32074
32240
|
applyFixes,
|
|
32241
|
+
assessReplacement,
|
|
32075
32242
|
buildDeterministicFixes,
|
|
32243
|
+
buildFrameworkGuidance,
|
|
32076
32244
|
buildGSCApiRequest,
|
|
32077
32245
|
buildGSCRequest,
|
|
32078
32246
|
calculateAIVisibilityScore,
|
|
@@ -32113,6 +32281,7 @@ export {
|
|
|
32113
32281
|
createPullRequest,
|
|
32114
32282
|
createSEOCommit,
|
|
32115
32283
|
createSEOCommits,
|
|
32284
|
+
describeConcerns,
|
|
32116
32285
|
detectDuplicates,
|
|
32117
32286
|
detectFramework4 as detectFramework,
|
|
32118
32287
|
detectGitHubPages,
|