@lownoise-studio/rendershield 0.1.4 → 0.3.0

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.js CHANGED
@@ -3,18 +3,21 @@ import { cmdInit } from "./commands/init.js";
3
3
  import { cmdBuild } from "./commands/build.js";
4
4
  import { cmdVerify } from "./commands/verify.js";
5
5
  function printHelp() {
6
- console.log(`
7
- RenderShield (v0) — boring bot-aware prerendering.
8
-
9
- Usage:
10
- rendershield init
11
- rendershield build
12
- rendershield verify
13
-
14
- Notes:
15
- - Config file: rendershield.config.json
16
- - Content: content/blog/*.md (frontmatter required)
17
- - Output: dist-prerender/
6
+ console.log(`
7
+ RenderShield (v0) — boring bot-aware prerendering.
8
+
9
+ Usage:
10
+ rendershield init
11
+ rendershield build
12
+ rendershield verify [--prod <url>]
13
+
14
+ verify Print curl commands for local/build output.
15
+ verify --prod Fetch URL as bot and human; verify bot sees full HTML and contract fields.
16
+
17
+ Notes:
18
+ - Config file: rendershield.config.json
19
+ - Content: content/<collection>/**/*.md (frontmatter required)
20
+ - Output: dist-prerender/
18
21
  `);
19
22
  }
20
23
  async function main() {
@@ -33,7 +36,10 @@ async function main() {
33
36
  return;
34
37
  }
35
38
  if (cmd === "verify") {
36
- await cmdVerify();
39
+ const args = process.argv.slice(3);
40
+ const prodIdx = args.indexOf("--prod");
41
+ const prodUrl = prodIdx >= 0 && args[prodIdx + 1] ? args[prodIdx + 1].trim() : undefined;
42
+ await cmdVerify(undefined, prodUrl ? { prodUrl } : undefined);
37
43
  return;
38
44
  }
39
45
  console.error(`Unknown command: ${cmd}\n`);
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD,SAAS,SAAS;IAChB,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;CAYb,CAAC,CAAC;AACH,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;IAEpC,IAAI,CAAC,GAAG,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC7C,SAAS,EAAE,CAAC;QACZ,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,CAAC;QACH,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;YACnB,MAAM,OAAO,EAAE,CAAC;YAChB,OAAO;QACT,CAAC;QACD,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;YACpB,MAAM,QAAQ,EAAE,CAAC;YACjB,OAAO;QACT,CAAC;QACD,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;YACrB,MAAM,SAAS,EAAE,CAAC;YAClB,OAAO;QACT,CAAC;QAED,OAAO,CAAC,KAAK,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;QAC3C,SAAS,EAAE,CAAC;QACZ,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,MAAM,GAAG,GAAG,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,OAAO,CAAC,KAAK,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAAC;QAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD,SAAS,SAAS;IAChB,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;CAeb,CAAC,CAAC;AACH,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;IAEpC,IAAI,CAAC,GAAG,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC7C,SAAS,EAAE,CAAC;QACZ,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,CAAC;QACH,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;YACnB,MAAM,OAAO,EAAE,CAAC;YAChB,OAAO;QACT,CAAC;QACD,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;YACpB,MAAM,QAAQ,EAAE,CAAC;YACjB,OAAO;QACT,CAAC;QACD,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;YACrB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACnC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YACvC,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,IAAI,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;YACzF,MAAM,SAAS,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;YAC9D,OAAO;QACT,CAAC;QAED,OAAO,CAAC,KAAK,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;QAC3C,SAAS,EAAE,CAAC;QACZ,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,MAAM,GAAG,GAAG,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,OAAO,CAAC,KAAK,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAAC;QAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC"}
@@ -12,8 +12,103 @@ function routeToOutDir(outDirAbs, routePath) {
12
12
  const clean = routePath.replace(/^\//, "");
13
13
  return path.join(outDirAbs, clean);
14
14
  }
15
+ /**
16
+ * Validates output path before any destructive operation (fs.remove).
17
+ * Pass: outDir is a subdirectory inside project root; no symlink escape.
18
+ * Fail: "/", "C:\", "..", "../", or outDir (or any of its existing parents) resolving outside project.
19
+ * Policy: strict — build hard-fails before any delete attempt.
20
+ * Segment-safe: uses path.relative(root, out) only; no prefix startsWith.
21
+ */
22
+ async function validateOutputPath(outDir, cwd) {
23
+ const cwdAbs = path.resolve(cwd);
24
+ let cwdReal;
25
+ try {
26
+ cwdReal = await fs.realpath(cwdAbs);
27
+ }
28
+ catch (err) {
29
+ const msg = err instanceof Error ? err.message : String(err);
30
+ throw new Error(`Cannot resolve project root: ${cwdAbs}. ${msg}`);
31
+ }
32
+ const outDirAbs = path.resolve(cwdAbs, outDir);
33
+ // Order: realpath → normalize → then case-fold for comparisons
34
+ const normalizedCwd = path.normalize(cwdReal);
35
+ const normalizedOut = path.normalize(outDirAbs);
36
+ // Segment-safe: use path.relative only (no prefix startsWith on full path). Cross-platform: relative()
37
+ // rarely returns absolute on Windows; startsWith("..") and path.isAbsolute(rel) cover escapes.
38
+ const relative = path.relative(normalizedCwd, normalizedOut);
39
+ if (relative.startsWith("..") || relative === ".." || path.isAbsolute(relative)) {
40
+ throw new Error(`Output directory "${outDir}" resolves outside project root. Use a relative path within the project.`);
41
+ }
42
+ // Reject: root filesystem (/, C:\, C:/) — early reject; real safety is "inside root" above
43
+ const rootPaths = ["/", "c:\\", "c:/"];
44
+ const outLower = normalizedOut.toLowerCase();
45
+ if (rootPaths.includes(outLower)) {
46
+ throw new Error(`Output directory "${outDir}" resolves to root filesystem. This is not allowed for safety.`);
47
+ }
48
+ // Reject: output dir equals project root (would delete entire project). Case-fold after normalize.
49
+ const cwdLower = normalizedCwd.toLowerCase();
50
+ if (outLower === cwdLower) {
51
+ throw new Error(`Output directory "${outDir}" cannot be the project root. Use a subdirectory (e.g. dist-prerender).`);
52
+ }
53
+ if (await fs.pathExists(outDirAbs)) {
54
+ // Path exists: resolve symlinks and ensure real path is inside root
55
+ let outDirReal;
56
+ try {
57
+ outDirReal = await fs.realpath(outDirAbs);
58
+ }
59
+ catch {
60
+ outDirReal = outDirAbs;
61
+ }
62
+ const outDirRealNorm = path.normalize(outDirReal);
63
+ const relativeReal = path.relative(normalizedCwd, outDirRealNorm);
64
+ if (relativeReal.startsWith("..") || relativeReal === ".." || path.isAbsolute(relativeReal)) {
65
+ throw new Error(`Output directory "${outDir}" resolves (via symlink) outside project root. Use a path that does not escape the project.`);
66
+ }
67
+ }
68
+ else {
69
+ // Path does not exist: walk up to nearest existing parent, realpath it, ensure it stays inside root.
70
+ // Guards e.g. outDir "dist-link/prerender-new" where dist-link is a symlink to /.
71
+ // If we never find an existing parent (or the only one is root), we must reject — do not treat as "fine."
72
+ let current = normalizedOut;
73
+ const rootDir = path.normalize(path.parse(normalizedCwd).root);
74
+ let foundParentInsideRoot = false;
75
+ while (current) {
76
+ if (await fs.pathExists(current)) {
77
+ let parentReal;
78
+ try {
79
+ parentReal = await fs.realpath(current);
80
+ }
81
+ catch {
82
+ parentReal = current;
83
+ }
84
+ const parentRealNorm = path.normalize(parentReal);
85
+ // Nearest existing parent is filesystem root → outside project, reject
86
+ const parentLower = parentRealNorm.toLowerCase();
87
+ if (rootPaths.includes(parentLower)) {
88
+ throw new Error(`Output directory "${outDir}" has a parent that resolves to filesystem root. Use a path inside the project.`);
89
+ }
90
+ const relParent = path.relative(normalizedCwd, parentRealNorm);
91
+ if (relParent.startsWith("..") || relParent === ".." || path.isAbsolute(relParent)) {
92
+ throw new Error(`Output directory "${outDir}" has a parent that resolves (via symlink) outside project root. Use a path that does not escape the project.`);
93
+ }
94
+ foundParentInsideRoot = true;
95
+ break;
96
+ }
97
+ const parent = path.dirname(current);
98
+ if (parent === current)
99
+ break;
100
+ current = parent;
101
+ }
102
+ if (!foundParentInsideRoot) {
103
+ // No existing parent found before hitting dirname loop stop (shouldn't happen on a normal FS)
104
+ throw new Error(`Output directory "${outDir}" could not be validated: no existing parent path found. Use a path inside the project.`);
105
+ }
106
+ }
107
+ }
15
108
  export async function cmdBuild(cwd = process.cwd()) {
16
109
  const cfg = await loadConfig(cwd);
110
+ // Validate output path before any destructive operations
111
+ await validateOutputPath(cfg.output.outDir, cwd);
17
112
  const outDirAbs = path.join(cwd, cfg.output.outDir);
18
113
  // Clean output (boring + deterministic)
19
114
  await fs.remove(outDirAbs);
@@ -32,6 +127,7 @@ export async function cmdBuild(cwd = process.cwd()) {
32
127
  html,
33
128
  outFile,
34
129
  routePath: doc.routePath,
130
+ sourcePath: doc.sourcePath,
35
131
  });
36
132
  await fs.writeFile(outFile, html, "utf8");
37
133
  }
@@ -1 +1 @@
1
- {"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import fs from "fs-extra";
2
2
  import path from "node:path";
3
3
  import { loadConfig } from "../core/loadConfig.js";
4
+ import { checkPrerenderContract } from "../core/validateOutput.js";
4
5
  function joinUrl(base, routePath) {
5
6
  const b = base.endsWith("/") ? base.slice(0, -1) : base;
6
7
  const p = routePath.startsWith("/") ? routePath : `/${routePath}`;
@@ -43,58 +44,148 @@ function indexHtmlPathToRoute(outDirAbs, indexPathAbs) {
43
44
  const normalized = noFile.split(path.sep).join("/").replace(/\/+$/, "");
44
45
  return "/" + normalized.replace(/^\/+/, "");
45
46
  }
46
- export async function cmdVerify(cwd = process.cwd()) {
47
+ const BOT_UA = "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)";
48
+ const HUMAN_UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
49
+ /** Heuristic: likely SPA shell if body has almost no visible content and no article. */
50
+ function looksLikeSpaShell(html) {
51
+ const bodyMatch = html.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
52
+ const bodyHtml = bodyMatch ? bodyMatch[1] : html;
53
+ const noScript = bodyHtml
54
+ .replace(/<script[\s\S]*?<\/script>/gi, " ")
55
+ .replace(/<style[\s\S]*?<\/style>/gi, " ")
56
+ .replace(/<noscript[\s\S]*?<\/noscript>/gi, " ");
57
+ const text = noScript.replace(/<\/?[^>]+>/g, " ").replace(/\s+/g, " ").trim();
58
+ if (text.length < 150) {
59
+ return { likely: true, reason: `Body text very short (${text.length} chars); likely app shell.` };
60
+ }
61
+ if (!/<article\b/i.test(html)) {
62
+ return { likely: true, reason: "No <article> present; may be SPA shell." };
63
+ }
64
+ const rootOnly = /<body[^>]*>\s*<div[^>]*id=["'](?:root|app|__next)["'][^>]*>\s*<\/div>\s*<\/body>/i.test(html.replace(/\s+/g, " "));
65
+ if (rootOnly) {
66
+ return { likely: true, reason: "Single root div (e.g. #root, #app) with no content." };
67
+ }
68
+ return { likely: false };
69
+ }
70
+ export async function cmdVerify(cwd = process.cwd(), options) {
71
+ if (options?.prodUrl) {
72
+ await runVerifyProd(options.prodUrl);
73
+ return;
74
+ }
47
75
  const cfg = await loadConfig(cwd);
48
76
  const outDirAbs = path.join(cwd, cfg.output.outDir);
49
77
  const exists = await fs.pathExists(outDirAbs);
50
78
  if (!exists) {
51
- console.log(`
52
- RenderShield verify
53
-
54
- No prerender output directory found: ${cfg.output.outDir}/
55
-
56
- Run:
57
- node dist/cli.js build
79
+ console.log(`
80
+ RenderShield verify
81
+
82
+ No prerender output directory found: ${cfg.output.outDir}/
83
+
84
+ Run:
85
+ node dist/cli.js build
58
86
  `);
59
87
  return;
60
88
  }
61
89
  const firstIndex = await findFirstIndexHtml(outDirAbs);
62
90
  if (!firstIndex) {
63
- console.log(`
64
- RenderShield verify
65
-
66
- No prerendered pages found inside: ${cfg.output.outDir}/
67
-
68
- Run:
69
- node dist/cli.js build
91
+ console.log(`
92
+ RenderShield verify
93
+
94
+ No prerendered pages found inside: ${cfg.output.outDir}/
95
+
96
+ Run:
97
+ node dist/cli.js build
70
98
  `);
71
99
  return;
72
100
  }
73
101
  const routePath = indexHtmlPathToRoute(outDirAbs, firstIndex);
74
102
  const url = joinUrl(cfg.site.canonicalBase, routePath);
75
- console.log(`
76
- RenderShield verify
77
-
78
- Using:
79
- canonicalBase: ${cfg.site.canonicalBase}
80
- routePath: ${routePath}
81
- output file: ${path.relative(cwd, firstIndex)}
82
-
83
- Smoke tests:
84
-
85
- 1) Human (usually SPA shell):
86
- curl -s ${url} | grep -i "<title>"
87
-
88
- 2) Bot (should see prerendered, route-specific title):
89
- curl -s -H "User-Agent: Googlebot" ${url} | grep -i "<title>"
90
-
91
- 3) Debug headers (Worker must be routed + proxy ON):
92
- curl -I -H "User-Agent: GPTBot" ${url}
93
-
94
- Expected headers (if debugHeaders enabled):
95
- X-Bot-Detected: true
96
- X-Prerender: true
97
- X-Final-Path: ${routePath.replace(/\/?$/, "/index.html")}
103
+ console.log(`
104
+ RenderShield verify
105
+
106
+ Using:
107
+ canonicalBase: ${cfg.site.canonicalBase}
108
+ routePath: ${routePath}
109
+ output file: ${path.relative(cwd, firstIndex)}
110
+
111
+ Smoke tests:
112
+
113
+ 1) Human (usually SPA shell):
114
+ curl -s ${url} | grep -i "<title>"
115
+
116
+ 2) Bot (should see prerendered, route-specific title):
117
+ curl -s -H "User-Agent: Googlebot" ${url} | grep -i "<title>"
118
+
119
+ 3) Debug headers (Worker must be routed + proxy ON):
120
+ curl -I -H "User-Agent: GPTBot" ${url}
121
+
122
+ Expected: x-rendershield: bot-hit (proves Worker served prerender to bot).
123
+ If debugHeaders enabled: X-Bot-Detected, X-Prerender, X-Final-Path.
98
124
  `);
99
125
  }
126
+ async function runVerifyProd(url) {
127
+ const normalizedUrl = url.startsWith("http") ? url : `https://${url}`;
128
+ let botHtml;
129
+ let humanHtml;
130
+ let botStatus;
131
+ let humanStatus;
132
+ let xRenderShield;
133
+ try {
134
+ const [botRes, humanRes] = await Promise.all([
135
+ fetch(normalizedUrl, {
136
+ headers: { "User-Agent": BOT_UA },
137
+ redirect: "follow",
138
+ }),
139
+ fetch(normalizedUrl, {
140
+ headers: { "User-Agent": HUMAN_UA },
141
+ redirect: "follow",
142
+ }),
143
+ ]);
144
+ botStatus = botRes.status;
145
+ humanStatus = humanRes.status;
146
+ xRenderShield = botRes.headers.get("x-rendershield");
147
+ botHtml = await botRes.text();
148
+ humanHtml = await humanRes.text();
149
+ }
150
+ catch (err) {
151
+ const msg = err instanceof Error ? err.message : String(err);
152
+ throw new Error(`RenderShield verify --prod: failed to fetch ${normalizedUrl}. ${msg}`);
153
+ }
154
+ // Prove routing: Worker must set x-rendershield: bot-hit for bot requests. No inference.
155
+ const routingOk = xRenderShield === "bot-hit";
156
+ if (xRenderShield === "bot-fallback") {
157
+ throw new Error(`RenderShield verify --prod: bot request received x-rendershield: bot-fallback. ` +
158
+ `Prerender origin returned non-200; Worker fell back to SPA. Fix deployment or origin so bots get prerendered HTML.`);
159
+ }
160
+ if (!routingOk) {
161
+ throw new Error(`RenderShield verify --prod: expected x-rendershield: bot-hit (proving Worker routed bot to prerender). ` +
162
+ `Got: ${xRenderShield ?? "(missing)"}. Ensure the Worker is deployed and bound to this route.`);
163
+ }
164
+ const contract = checkPrerenderContract(botHtml, {
165
+ routePath: normalizedUrl,
166
+ outFile: normalizedUrl,
167
+ });
168
+ const humanSpa = looksLikeSpaShell(humanHtml);
169
+ // Report
170
+ console.log(`
171
+ RenderShield verify --prod
172
+ URL: ${normalizedUrl}
173
+
174
+ Fetch:
175
+ Bot (Googlebot): ${botStatus} (${botHtml.length} bytes) x-rendershield: ${xRenderShield ?? "(none)"}
176
+ Human (Chrome): ${humanStatus} (${humanHtml.length} bytes)
177
+
178
+ Routing: x-rendershield: bot-hit (Worker served prerendered HTML to bot)
179
+
180
+ Bot contract (title, meta, canonical, OG, JSON-LD, article):
181
+ ${contract.ok ? "PASS — all required fields present." : "FAIL — missing or invalid:"}
182
+ ${contract.missing.length > 0 ? contract.missing.map((m) => ` - ${m}`).join("\n") : ""}
183
+
184
+ Human response:
185
+ ${humanSpa.likely ? `Likely SPA shell: ${humanSpa.reason ?? "unknown"}` : "Has substantial content (not a minimal SPA shell)."}
186
+ `);
187
+ if (!contract.ok) {
188
+ throw new Error(`Production URL did not satisfy bot contract. Missing: ${contract.missing.join("; ")}`);
189
+ }
190
+ }
100
191
  //# sourceMappingURL=verify.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/commands/verify.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/commands/verify.ts"],"names":[],"mappings":"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"}
@@ -4,74 +4,81 @@ function jsStringArray(arr) {
4
4
  export function generateWorkerJs(cfg) {
5
5
  const patterns = cfg.worker.botUserAgentPatterns.map((p) => p.toLowerCase());
6
6
  const rewriteBases = cfg.worker.rewriteRouteBases;
7
- return `/**
8
- * RenderShield Worker (generated)
9
- * Serves prerendered /index.html to bots on selected route bases.
10
- */
11
-
12
- const BOT_SUBSTRINGS = ${jsStringArray(patterns)};
13
- const REWRITE_BASES = ${jsStringArray(rewriteBases)};
14
-
15
- function isBot(ua) {
16
- const s = (ua || "").toLowerCase();
17
- return BOT_SUBSTRINGS.some((sub) => s.includes(sub));
18
- }
19
-
20
- function shouldRewrite(pathname) {
21
- return REWRITE_BASES.some((base) => pathname.startsWith(base));
22
- }
23
-
24
- function toIndexHtml(pathname) {
25
- let p = pathname;
26
- if (!p.endsWith("/")) p += "/";
27
- return p + "index.html";
28
- }
29
-
30
- export default {
31
- async fetch(request, env, ctx) {
32
- const url = new URL(request.url);
33
- const ua = request.headers.get("User-Agent") || "";
34
- const bot = isBot(ua);
35
-
36
- const isGetLike = request.method === "GET" || request.method === "HEAD";
37
- const rewrite = bot && isGetLike && shouldRewrite(url.pathname);
38
-
39
- try {
40
- if (!rewrite) return fetch(request);
41
-
42
- const origin = ${JSON.stringify(cfg.worker.lovableOrigin)};
43
- const finalPath = toIndexHtml(url.pathname);
44
- const originUrl = origin + finalPath + url.search;
45
-
46
- const headers = new Headers();
47
- headers.set("Accept", "text/html");
48
- headers.set("User-Agent", ua);
49
-
50
- const resp = await fetch(originUrl, { method: "GET", headers });
51
-
52
- if (!resp.ok) {
53
- const fallbackUrl = origin + url.pathname + url.search;
54
- const fb = await fetch(fallbackUrl, { method: "GET", headers });
55
- const out = new Response(fb.body, fb);
56
- ${cfg.worker.debugHeaders ? `out.headers.set("X-Bot-Detected", "true");
57
- out.headers.set("X-Prerender-Fallback", "true");
58
- out.headers.set("X-Requested-Path", url.pathname);` : ""}
59
- return out;
60
- }
61
-
62
- const out = new Response(resp.body, resp);
63
- ${cfg.worker.debugHeaders ? `out.headers.set("X-Bot-Detected", "true");
64
- out.headers.set("X-Prerender", "true");
65
- out.headers.set("X-Final-Path", finalPath);` : ""}
66
- return out;
67
- } catch (err) {
68
- return new Response("Worker error: " + (err && err.message ? err.message : String(err)), {
69
- status: 500,
70
- headers: { "Content-Type": "text/plain; charset=utf-8" }
71
- });
72
- }
73
- }
74
- };
7
+ return `/**
8
+ * RenderShield Worker (generated)
9
+ * Serves prerendered /index.html to bots on selected route bases.
10
+ */
11
+
12
+ const BOT_SUBSTRINGS = ${jsStringArray(patterns)};
13
+ const REWRITE_BASES = ${jsStringArray(rewriteBases)};
14
+
15
+ function isBot(ua) {
16
+ const s = (ua || "").toLowerCase();
17
+ return BOT_SUBSTRINGS.some((sub) => s.includes(sub));
18
+ }
19
+
20
+ function shouldRewrite(pathname) {
21
+ return REWRITE_BASES.some((base) => pathname.startsWith(base));
22
+ }
23
+
24
+ function toIndexHtml(pathname) {
25
+ let p = pathname;
26
+ if (!p.endsWith("/")) p += "/";
27
+ return p + "index.html";
28
+ }
29
+
30
+ export default {
31
+ async fetch(request, env, ctx) {
32
+ const url = new URL(request.url);
33
+ const ua = request.headers.get("User-Agent") || "";
34
+ const bot = isBot(ua);
35
+
36
+ const isGetLike = request.method === "GET" || request.method === "HEAD";
37
+ const rewrite = bot && isGetLike && shouldRewrite(url.pathname);
38
+
39
+ try {
40
+ if (!rewrite) {
41
+ const passResp = await fetch(request);
42
+ const out = new Response(passResp.body, passResp);
43
+ out.headers.set("x-rendershield", "pass-through");
44
+ return out;
45
+ }
46
+
47
+ const origin = ${JSON.stringify(cfg.worker.lovableOrigin)};
48
+ const finalPath = toIndexHtml(url.pathname);
49
+ const originUrl = origin + finalPath + url.search;
50
+
51
+ const headers = new Headers();
52
+ headers.set("Accept", "text/html");
53
+ headers.set("User-Agent", ua);
54
+
55
+ const resp = await fetch(originUrl, { method: "GET", headers });
56
+
57
+ if (!resp.ok) {
58
+ const fallbackUrl = origin + url.pathname + url.search;
59
+ const fb = await fetch(fallbackUrl, { method: "GET", headers });
60
+ const out = new Response(fb.body, fb);
61
+ out.headers.set("x-rendershield", "bot-fallback");
62
+ ${cfg.worker.debugHeaders ? `out.headers.set("X-Bot-Detected", "true");
63
+ out.headers.set("X-Prerender-Fallback", "true");
64
+ out.headers.set("X-Requested-Path", url.pathname);` : ""}
65
+ return out;
66
+ }
67
+
68
+ const out = new Response(resp.body, resp);
69
+ out.headers.set("x-rendershield", "bot-hit");
70
+ ${cfg.worker.debugHeaders ? `out.headers.set("X-Bot-Detected", "true");
71
+ out.headers.set("X-Prerender", "true");
72
+ out.headers.set("X-Final-Path", finalPath);` : ""}
73
+ return out;
74
+ } catch (err) {
75
+ return new Response("Worker error: " + (err && err.message ? err.message : String(err)), {
76
+ status: 500,
77
+ headers: { "Content-Type": "text/plain; charset=utf-8" }
78
+ });
79
+ }
80
+ }
81
+ };
75
82
  `;
76
83
  }
77
84
  //# sourceMappingURL=generateWorker.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"generateWorker.js","sourceRoot":"","sources":["../../src/core/generateWorker.ts"],"names":[],"mappings":"AAEA,SAAS,aAAa,CAAC,GAAa;IAClC,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,GAAuB;IACtD,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IAC7E,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,iBAAiB,CAAC;IAElD,OAAO;;;;;yBAKgB,aAAa,CAAC,QAAQ,CAAC;wBACxB,aAAa,CAAC,YAAY,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uBA6B5B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC;;;;;;;;;;;;;;UAcrD,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;;2DAEuB,CAAC,CAAC,CAAC,EAAE;;;;;QAKxD,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;;kDAEgB,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;CAUtD,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"generateWorker.js","sourceRoot":"","sources":["../../src/core/generateWorker.ts"],"names":[],"mappings":"AAEA,SAAS,aAAa,CAAC,GAAa;IAClC,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,GAAuB;IACtD,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IAC7E,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,iBAAiB,CAAC;IAElD,OAAO;;;;;yBAKgB,aAAa,CAAC,QAAQ,CAAC;wBACxB,aAAa,CAAC,YAAY,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uBAkC5B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC;;;;;;;;;;;;;;;UAerD,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;;2DAEuB,CAAC,CAAC,CAAC,EAAE;;;;;;QAMxD,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;;kDAEgB,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;CAUtD,CAAC;AACF,CAAC"}
@@ -18,6 +18,45 @@ function coerceBoolFlag(parsed, key, defaultEnabled) {
18
18
  }
19
19
  return { enabled: v.enabled };
20
20
  }
21
+ /**
22
+ * Worker config policy: strict mode.
23
+ * When worker.enabled is true, all of lovableOrigin, rewriteRouteBases, and
24
+ * botUserAgentPatterns are required and must be non-empty. No defaults are
25
+ * supplied — build fails with a crisp error.
26
+ * lovableOrigin: must be http: or https: only (no file:, javascript:, protocol-relative).
27
+ * botUserAgentPatterns: substring match only (not regex); empty strings rejected to avoid overmatching.
28
+ */
29
+ function validateWorkerWhenEnabled(parsed) {
30
+ if (!parsed.worker?.lovableOrigin || typeof parsed.worker.lovableOrigin !== "string") {
31
+ throw new Error(`worker.lovableOrigin is required when worker.enabled is true. Example: "https://your-site.lovable.app"`);
32
+ }
33
+ let url;
34
+ try {
35
+ url = new URL(parsed.worker.lovableOrigin);
36
+ }
37
+ catch {
38
+ throw new Error(`worker.lovableOrigin must be a valid URL. Got: "${parsed.worker.lovableOrigin}"`);
39
+ }
40
+ const scheme = url.protocol.toLowerCase();
41
+ if (scheme !== "http:" && scheme !== "https:") {
42
+ throw new Error(`worker.lovableOrigin must use http or https. Got: "${url.protocol}"`);
43
+ }
44
+ if (!Array.isArray(parsed.worker?.rewriteRouteBases) ||
45
+ parsed.worker.rewriteRouteBases.length === 0) {
46
+ throw new Error(`worker.rewriteRouteBases must be a non-empty array when worker.enabled is true. Example: ["/blog/"]`);
47
+ }
48
+ if (!Array.isArray(parsed.worker?.botUserAgentPatterns) ||
49
+ parsed.worker.botUserAgentPatterns.length === 0) {
50
+ throw new Error(`worker.botUserAgentPatterns must be a non-empty array when worker.enabled is true. Example: ["googlebot", "bingbot"]`);
51
+ }
52
+ // Substring match only; empty string would match every User-Agent
53
+ for (let i = 0; i < parsed.worker.botUserAgentPatterns.length; i++) {
54
+ const p = parsed.worker.botUserAgentPatterns[i];
55
+ if (typeof p !== "string" || p.trim() === "") {
56
+ throw new Error(`worker.botUserAgentPatterns[${i}] must be a non-empty string (substring match). Empty or invalid entry would overmatch.`);
57
+ }
58
+ }
59
+ }
21
60
  export async function loadConfig(cwd = process.cwd()) {
22
61
  const p = path.join(cwd, CONFIG_NAME);
23
62
  const exists = await fs.pathExists(p);
@@ -54,7 +93,20 @@ export async function loadConfig(cwd = process.cwd()) {
54
93
  // Validate + default these optional sections to prevent TypeErrors later
55
94
  parsed.sitemap = coerceBoolFlag(parsed, "sitemap", true);
56
95
  parsed.robots = coerceBoolFlag(parsed, "robots", true);
57
- parsed.worker = coerceBoolFlag(parsed, "worker", true);
96
+ const workerFlag = coerceBoolFlag(parsed, "worker", true);
97
+ parsed.worker = workerFlag;
98
+ if (parsed.worker.enabled) {
99
+ validateWorkerWhenEnabled(parsed);
100
+ }
101
+ else {
102
+ parsed.worker = {
103
+ enabled: false,
104
+ lovableOrigin: "",
105
+ rewriteRouteBases: [],
106
+ botUserAgentPatterns: [],
107
+ debugHeaders: false,
108
+ };
109
+ }
58
110
  return parsed;
59
111
  }
60
112
  //# sourceMappingURL=loadConfig.js.map