@bagelink/blox 1.15.649 → 1.15.653

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.
@@ -26,7 +26,7 @@ const node_child_process = require("node:child_process");
26
26
  const os = require("node:os");
27
27
  const path = require("node:path");
28
28
  const process = require("node:process");
29
- const llmArtifacts = require("./validate-output-CT_gD7Dl.cjs");
29
+ const llmArtifacts = require("./validate-output-BdzD7fIN.cjs");
30
30
  const AUTO_SHARD_CAP = Number(process.env.BLOX_SSG_SHARD_CAP) || 6;
31
31
  const PREFETCH_BUDGET = Number(process.env.BLOX_SSG_PREFETCH_BUDGET) || 24;
32
32
  const SEQUENTIAL_SHARDS = process.env.BLOX_SSG_SEQUENTIAL === "1";
@@ -166,7 +166,7 @@ async function runSharded(opts) {
166
166
  }
167
167
  if (process.env.BLOX_SSG_LLM !== "0") {
168
168
  try {
169
- const { mergeLlmParts } = await Promise.resolve().then(() => require("./validate-output-CT_gD7Dl.cjs")).then((n) => n.llmArtifacts);
169
+ const { mergeLlmParts } = await Promise.resolve().then(() => require("./validate-output-BdzD7fIN.cjs")).then((n) => n.llmArtifacts);
170
170
  const mergeWebsite = website;
171
171
  const partFiles = Array.from({ length: total }, (_, i) => path.join(llmPartsDir, `part.${i}.json`));
172
172
  const { pages } = await mergeLlmParts({
@@ -2,7 +2,7 @@ import { spawn } from "node:child_process";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
4
  import process from "node:process";
5
- import { f as fetchCmsSiteData, g as generateSitemapArtifacts } from "./validate-output-CD1Eq_Jy.js";
5
+ import { f as fetchCmsSiteData, g as generateSitemapArtifacts } from "./validate-output-CgacFje9.js";
6
6
  const AUTO_SHARD_CAP = Number(process.env.BLOX_SSG_SHARD_CAP) || 6;
7
7
  const PREFETCH_BUDGET = Number(process.env.BLOX_SSG_PREFETCH_BUDGET) || 24;
8
8
  const SEQUENTIAL_SHARDS = process.env.BLOX_SSG_SEQUENTIAL === "1";
@@ -142,7 +142,7 @@ async function runSharded(opts) {
142
142
  }
143
143
  if (process.env.BLOX_SSG_LLM !== "0") {
144
144
  try {
145
- const { mergeLlmParts } = await import("./validate-output-CD1Eq_Jy.js").then((n) => n.A);
145
+ const { mergeLlmParts } = await import("./validate-output-CgacFje9.js").then((n) => n.C);
146
146
  const mergeWebsite = website;
147
147
  const partFiles = Array.from({ length: total }, (_, i) => path.join(llmPartsDir, `part.${i}.json`));
148
148
  const { pages } = await mergeLlmParts({
package/dist/ssg/cli.cjs CHANGED
@@ -3,7 +3,7 @@
3
3
  const process = require("node:process");
4
4
  const fs = require("node:fs/promises");
5
5
  const ssg_buildGrant = require("./build-grant.cjs");
6
- const llmArtifacts = require("../validate-output-CT_gD7Dl.cjs");
6
+ const llmArtifacts = require("../validate-output-BdzD7fIN.cjs");
7
7
  function installFetchTimeout() {
8
8
  const ms = Number(process.env.BLOX_SSG_FETCH_TIMEOUT_MS) || 2e4;
9
9
  const original = globalThis.fetch;
@@ -58,6 +58,7 @@ async function main() {
58
58
  let routesCmd = "";
59
59
  let siteDataFile = "";
60
60
  let validateOutDir = "";
61
+ let validateOutput = process.env.BLOX_SSG_VALIDATE !== "0";
61
62
  const passthroughArgs = [];
62
63
  const excludePaths = ["/_blox_preview"];
63
64
  for (const a of argv) {
@@ -77,6 +78,8 @@ async function main() {
77
78
  validateOutDir = "dist";
78
79
  } else if (a.startsWith("--validate=")) {
79
80
  validateOutDir = a.slice("--validate=".length);
81
+ } else if (a === "--no-validate") {
82
+ validateOutput = false;
80
83
  } else if (a.startsWith("--shard=")) {
81
84
  const [i, n] = a.slice("--shard=".length).split("/").map(Number);
82
85
  if (Number.isInteger(i) && Number.isInteger(n) && n > 0 && i >= 0 && i < n) {
@@ -101,7 +104,10 @@ Options:
101
104
  --shard=<i/n> (internal) Render only this shard's slice — set by --shards.
102
105
  --routes-cmd=<cmd> Shell command whose stdout (whitespace-separated) is the
103
106
  route list. For router-driven sites with no CMS pages.
104
- --validate[=dir] Validate generated SSG assets (default directory: dist).
107
+ --validate[=dir] Validate generated SSG assets (default directory: dist)
108
+ and exit without prerendering.
109
+ --no-validate Skip the automatic output validation a normal build runs.
110
+ (Also: BLOX_SSG_VALIDATE=0.)
105
111
  --help, -h Show this help
106
112
 
107
113
  Environment:
@@ -141,7 +147,7 @@ Environment:
141
147
  if (shardsOpt !== null || routesCmd) {
142
148
  const { apiBase: apiBase2, websiteName: configWebsiteName2 } = await resolveApiBase(mode);
143
149
  const websiteName2 = process.env.WEBSITE_NAME ?? configWebsiteName2 ?? "default";
144
- const { runSharded } = await Promise.resolve().then(() => require("../shard-runner-DElB_rGY.cjs"));
150
+ const { runSharded } = await Promise.resolve().then(() => require("../shard-runner-C4JE4ywf.cjs"));
145
151
  const code = await runSharded({
146
152
  shards: shardsOpt ?? "auto",
147
153
  mode,
@@ -280,6 +286,10 @@ ${"=".repeat(60)}`);
280
286
  process.exitCode = 1;
281
287
  }
282
288
  }
289
+ if (validateOutput && shardTotal === 1) {
290
+ await llmArtifacts.validateInternalLinks("dist");
291
+ console.log("[blox-ssg] validated internal links");
292
+ }
283
293
  if (productionBuild && shardTotal === 1) {
284
294
  const uploadWebsiteId = ((_b = process.env.BLOX_WEBSITE_ID) == null ? void 0 : _b.trim()) || websiteId;
285
295
  const manifestExists = await fs.stat("dist/.blox-manifest.json").then(() => true, () => false);
package/dist/ssg/cli.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  import process from "node:process";
3
3
  import fs from "node:fs/promises";
4
4
  import { resolveBloxBuildGrant } from "./build-grant.mjs";
5
- import { v as validateSsgOutput, u as uploadBloxManifest, p as polyfillBloxSsgGlobals, f as fetchCmsSiteData, b as buildCollectionResolver, a as prerender } from "../validate-output-CD1Eq_Jy.js";
5
+ import { v as validateSsgOutput, u as uploadBloxManifest, p as polyfillBloxSsgGlobals, f as fetchCmsSiteData, b as buildCollectionResolver, a as prerender, c as validateInternalLinks } from "../validate-output-CgacFje9.js";
6
6
  function installFetchTimeout() {
7
7
  const ms = Number(process.env.BLOX_SSG_FETCH_TIMEOUT_MS) || 2e4;
8
8
  const original = globalThis.fetch;
@@ -57,6 +57,7 @@ async function main() {
57
57
  let routesCmd = "";
58
58
  let siteDataFile = "";
59
59
  let validateOutDir = "";
60
+ let validateOutput = process.env.BLOX_SSG_VALIDATE !== "0";
60
61
  const passthroughArgs = [];
61
62
  const excludePaths = ["/_blox_preview"];
62
63
  for (const a of argv) {
@@ -76,6 +77,8 @@ async function main() {
76
77
  validateOutDir = "dist";
77
78
  } else if (a.startsWith("--validate=")) {
78
79
  validateOutDir = a.slice("--validate=".length);
80
+ } else if (a === "--no-validate") {
81
+ validateOutput = false;
79
82
  } else if (a.startsWith("--shard=")) {
80
83
  const [i, n] = a.slice("--shard=".length).split("/").map(Number);
81
84
  if (Number.isInteger(i) && Number.isInteger(n) && n > 0 && i >= 0 && i < n) {
@@ -100,7 +103,10 @@ Options:
100
103
  --shard=<i/n> (internal) Render only this shard's slice — set by --shards.
101
104
  --routes-cmd=<cmd> Shell command whose stdout (whitespace-separated) is the
102
105
  route list. For router-driven sites with no CMS pages.
103
- --validate[=dir] Validate generated SSG assets (default directory: dist).
106
+ --validate[=dir] Validate generated SSG assets (default directory: dist)
107
+ and exit without prerendering.
108
+ --no-validate Skip the automatic output validation a normal build runs.
109
+ (Also: BLOX_SSG_VALIDATE=0.)
104
110
  --help, -h Show this help
105
111
 
106
112
  Environment:
@@ -140,7 +146,7 @@ Environment:
140
146
  if (shardsOpt !== null || routesCmd) {
141
147
  const { apiBase: apiBase2, websiteName: configWebsiteName2 } = await resolveApiBase(mode);
142
148
  const websiteName2 = process.env.WEBSITE_NAME ?? configWebsiteName2 ?? "default";
143
- const { runSharded } = await import("../shard-runner-UWTanGn2.js");
149
+ const { runSharded } = await import("../shard-runner-CtJLkov_.js");
144
150
  const code = await runSharded({
145
151
  shards: shardsOpt ?? "auto",
146
152
  mode,
@@ -279,6 +285,10 @@ ${"=".repeat(60)}`);
279
285
  process.exitCode = 1;
280
286
  }
281
287
  }
288
+ if (validateOutput && shardTotal === 1) {
289
+ await validateInternalLinks("dist");
290
+ console.log("[blox-ssg] validated internal links");
291
+ }
282
292
  if (productionBuild && shardTotal === 1) {
283
293
  const uploadWebsiteId = ((_b = process.env.BLOX_WEBSITE_ID) == null ? void 0 : _b.trim()) || websiteId;
284
294
  const manifestExists = await fs.stat("dist/.blox-manifest.json").then(() => true, () => false);
@@ -22,7 +22,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
22
22
  mod
23
23
  ));
24
24
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
25
- const llmArtifacts = require("../validate-output-CT_gD7Dl.cjs");
25
+ const llmArtifacts = require("../validate-output-BdzD7fIN.cjs");
26
26
  const ssg_buildGrant = require("./build-grant.cjs");
27
27
  const ssg_client = require("../client-AoDTJJXJ.cjs");
28
28
  const vue = require("vue");
@@ -445,6 +445,7 @@ exports.prerender = llmArtifacts.prerender;
445
445
  exports.resolveManifestProvenance = llmArtifacts.resolveManifestProvenance;
446
446
  exports.splitSitemapPathsByLocale = llmArtifacts.splitSitemapPathsByLocale;
447
447
  exports.uploadBloxManifest = llmArtifacts.uploadBloxManifest;
448
+ exports.validateInternalLinks = llmArtifacts.validateInternalLinks;
448
449
  exports.validateSsgOutput = llmArtifacts.validateSsgOutput;
449
450
  exports.parseBloxBuildGrantBody = ssg_buildGrant.parseBloxBuildGrantBody;
450
451
  exports.resolveBloxBuildGrant = ssg_buildGrant.resolveBloxBuildGrant;
@@ -31,6 +31,6 @@ export { buildJsonLd, extractPageText, generateLlmsFullTxt, generateLlmsTxt, mer
31
31
  export type { LlmArtifactsConfig, LlmPageEntry } from './llm-artifacts';
32
32
  export { resolveManifestProvenance, uploadBloxManifest } from './manifest-upload';
33
33
  export type { ManifestProvenance, ManifestUpload } from './manifest-upload';
34
- export { validateSsgOutput } from './validate-output';
34
+ export { validateInternalLinks, validateSsgOutput } from './validate-output';
35
35
  export type { ValidateSsgOutputOptions } from './validate-output';
36
36
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ssg/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC/C,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC9D,OAAO,EAAE,sBAAsB,EAAE,aAAa,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAA;AACrH,OAAO,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAC9E,YAAY,EAAE,cAAc,EAAE,4BAA4B,EAAE,MAAM,eAAe,CAAA;AACjF,YAAY,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAA;AAC9D;;;;;;;;;;GAUG;AACH,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAA;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AACrD,YAAY,EAAE,YAAY,EAAE,yBAAyB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAClG,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAC9F,YAAY,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AACjG,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC1D,YAAY,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC/D,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,OAAO,CAAA;AACxO,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,OAAO,CAAA;AACtF,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,mBAAmB,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACnH,YAAY,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACvE,OAAO,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AACjF,YAAY,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAC3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACrD,YAAY,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ssg/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC/C,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC9D,OAAO,EAAE,sBAAsB,EAAE,aAAa,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAA;AACrH,OAAO,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAC9E,YAAY,EAAE,cAAc,EAAE,4BAA4B,EAAE,MAAM,eAAe,CAAA;AACjF,YAAY,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAA;AAC9D;;;;;;;;;;GAUG;AACH,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAA;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AACrD,YAAY,EAAE,YAAY,EAAE,yBAAyB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAClG,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAC9F,YAAY,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AACjG,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC1D,YAAY,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC/D,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,OAAO,CAAA;AACxO,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,OAAO,CAAA;AACtF,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,mBAAmB,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACnH,YAAY,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACvE,OAAO,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AACjF,YAAY,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAC3E,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAC5E,YAAY,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAA"}
@@ -1,5 +1,5 @@
1
- import { c as buildPageHead, d as primaryContextEntry, e as buildJsonLd, h as extractPageText, B as BloxBuildResponseError, i as bloxBuildJson } from "../validate-output-CD1Eq_Jy.js";
2
- import { j, k, f, l, m, n, o, g, q, r, s, t, w, x, p, a, y, z, u, v } from "../validate-output-CD1Eq_Jy.js";
1
+ import { d as buildPageHead, e as primaryContextEntry, h as buildJsonLd, i as extractPageText, B as BloxBuildResponseError, j as bloxBuildJson } from "../validate-output-CgacFje9.js";
2
+ import { k, l, f, m, n, o, q, g, r, s, t, w, x, y, p, a, z, A, u, c, v } from "../validate-output-CgacFje9.js";
3
3
  import { parseBloxBuildGrantBody, resolveBloxBuildGrant } from "./build-grant.mjs";
4
4
  import { a as BLOX_HYDRATION_WINDOW_KEY } from "../client-Ce9fRCGH.js";
5
5
  import { createSSRApp, h, Suspense } from "vue";
@@ -402,34 +402,35 @@ export {
402
402
  BLOX_HYDRATION_WINDOW_KEY,
403
403
  BloxBuildResponseError,
404
404
  bloxBuildJson,
405
- j as bloxBuildRequest,
405
+ k as bloxBuildRequest,
406
406
  buildJsonLd,
407
407
  buildPageHead,
408
- k as buildSiteHead,
408
+ l as buildSiteHead,
409
409
  createBloxSSREntry,
410
410
  extractPageText,
411
411
  f as fetchCmsSiteData,
412
412
  fetchDatastoreSnapshot,
413
- l as generateLlmsFullTxt,
414
- m as generateLlmsTxt,
415
- n as generateNetlifyRedirects,
416
- o as generateRobotsTxt,
413
+ m as generateLlmsFullTxt,
414
+ n as generateLlmsTxt,
415
+ o as generateNetlifyRedirects,
416
+ q as generateRobotsTxt,
417
417
  g as generateSitemapArtifacts,
418
- q as generateSitemapIndexXml,
419
- r as generateSitemapXml,
420
- s as getBloxBuildRequestOptions,
421
- t as getSiteIconFlags,
422
- w as localeSitemapPath,
423
- x as mergeLlmParts,
418
+ r as generateSitemapIndexXml,
419
+ s as generateSitemapXml,
420
+ t as getBloxBuildRequestOptions,
421
+ w as getSiteIconFlags,
422
+ x as localeSitemapPath,
423
+ y as mergeLlmParts,
424
424
  parseBloxBuildGrantBody,
425
425
  p as polyfillBloxSsgGlobals,
426
426
  a as prerender,
427
427
  querySnapshotRows,
428
428
  renderBloxSsgPage,
429
429
  resolveBloxBuildGrant,
430
- y as resolveManifestProvenance,
430
+ z as resolveManifestProvenance,
431
431
  snapshotRows,
432
- z as splitSitemapPathsByLocale,
432
+ A as splitSitemapPathsByLocale,
433
433
  u as uploadBloxManifest,
434
+ c as validateInternalLinks,
434
435
  v as validateSsgOutput
435
436
  };
@@ -1 +1 @@
1
- {"version":3,"file":"prerender.d.ts","sourceRoot":"","sources":["../../src/ssg/prerender.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"prerender.d.ts","sourceRoot":"","sources":["../../src/ssg/prerender.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,12 @@
1
+ /**
2
+ * Fail when any prerendered page contains a relative internal link.
3
+ *
4
+ * A prerendered site is a tree of static files, so `<a href="contact">` on
5
+ * `/about` resolves to `/about/contact` for the browser — navigation silently
6
+ * breaks. This is the one check a normal build runs automatically (see
7
+ * `cli.ts`); the fuller `validateSsgOutput` stays opt-in via `--validate`.
8
+ */
9
+ export declare function validateInternalLinks(outDir: string): Promise<void>;
1
10
  export interface ValidateSsgOutputOptions {
2
11
  outDir: string;
3
12
  requireFiles?: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"validate-output.d.ts","sourceRoot":"","sources":["../../src/ssg/validate-output.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,wBAAwB;IACxC,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB;;;;;;;OAOG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC5B;AAOD,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CA2DxF"}
1
+ {"version":3,"file":"validate-output.d.ts","sourceRoot":"","sources":["../../src/ssg/validate-output.ts"],"names":[],"mappings":"AASA;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBzE;AAED,MAAM,WAAW,wBAAwB;IACxC,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB;;;;;;;OAOG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC5B;AAqBD,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmExF"}
@@ -924,7 +924,7 @@ async function prerender({
924
924
  redirects = [],
925
925
  llm = {}
926
926
  } = {}) {
927
- var _a, _b, _c;
927
+ var _a, _b, _c, _d, _e, _f;
928
928
  const absRoot = path.resolve(root);
929
929
  const absClient = path.resolve(absRoot, clientOutDir);
930
930
  const absServerEntry = path.resolve(absRoot, serverEntry);
@@ -1079,7 +1079,7 @@ async function prerender({
1079
1079
  const canonicalBase = (((_a = website == null ? void 0 : website.seo) == null ? void 0 : _a.canonical_base_url) || (website == null ? void 0 : website.domain) || "").replace(/\/$/, "");
1080
1080
  if (writeSharedAssets) {
1081
1081
  const sitemapPaths = rendered.filter((p) => !noindexPaths.has(p));
1082
- if (canonicalBase && sitemapPaths.length > 0) {
1082
+ if (!((_b = website == null ? void 0 : website.seo) == null ? void 0 : _b.noindex) && canonicalBase && sitemapPaths.length > 0) {
1083
1083
  const artifacts = generateSitemapArtifacts({
1084
1084
  routes: sitemapPaths.map((routePath) => ({
1085
1085
  path: routePath,
@@ -1088,7 +1088,7 @@ async function prerender({
1088
1088
  canonicalBase,
1089
1089
  defaultLocale: (website == null ? void 0 : website.default_locale) ?? "en",
1090
1090
  supportedLocales: (website == null ? void 0 : website.supported_locales) ?? [(website == null ? void 0 : website.default_locale) ?? "en"],
1091
- splitByLocale: (_b = website == null ? void 0 : website.seo) == null ? void 0 : _b.sitemap_split_by_locale
1091
+ splitByLocale: (_c = website == null ? void 0 : website.seo) == null ? void 0 : _c.sitemap_split_by_locale
1092
1092
  });
1093
1093
  for (const artifact of artifacts) {
1094
1094
  await fs.writeFile(
@@ -1103,10 +1103,28 @@ async function prerender({
1103
1103
  const robotsTxt = generateRobotsTxt({
1104
1104
  robotsTxt: website == null ? void 0 : website.robots_txt,
1105
1105
  canonicalBase,
1106
- noindex: (_c = website == null ? void 0 : website.seo) == null ? void 0 : _c.noindex
1106
+ noindex: (_d = website == null ? void 0 : website.seo) == null ? void 0 : _d.noindex
1107
1107
  });
1108
1108
  await fs.writeFile(path.join(absClient, "robots.txt"), robotsTxt, "utf8");
1109
1109
  console.log(" Generated robots.txt");
1110
+ if ((_e = website == null ? void 0 : website.seo) == null ? void 0 : _e.noindex) {
1111
+ const headersPath = path.join(absClient, "_headers");
1112
+ let existing = "";
1113
+ try {
1114
+ existing = await fs.readFile(headersPath, "utf8");
1115
+ } catch {
1116
+ }
1117
+ if (!existing.includes("X-Robots-Tag")) {
1118
+ await fs.writeFile(
1119
+ headersPath,
1120
+ `/*
1121
+ X-Robots-Tag: noindex, nofollow
1122
+ ${existing}`,
1123
+ "utf8"
1124
+ );
1125
+ console.log(" Generated _headers (X-Robots-Tag: noindex)");
1126
+ }
1127
+ }
1110
1128
  if (redirects.length > 0) {
1111
1129
  const redirectsContent = generateNetlifyRedirects(redirects);
1112
1130
  const redirectsPath = path.join(absClient, "_redirects");
@@ -1118,7 +1136,7 @@ async function prerender({
1118
1136
  await fs.writeFile(redirectsPath, redirectsContent + existing, "utf8");
1119
1137
  console.log(` Generated _redirects (${redirects.length} rules)`);
1120
1138
  }
1121
- if (llmEnabled && llm.writeIndex !== false && llmPages.length > 0) {
1139
+ if (!((_f = website == null ? void 0 : website.seo) == null ? void 0 : _f.noindex) && llmEnabled && llm.writeIndex !== false && llmPages.length > 0) {
1122
1140
  const indexable = llmPages.filter((p) => !noindexPaths.has(p.url));
1123
1141
  const llmsTxt = generateLlmsTxt({ website, pages: indexable, config: llm });
1124
1142
  await fs.writeFile(path.join(absClient, "llms.txt"), llmsTxt, "utf8");
@@ -1320,8 +1338,33 @@ const sitemapIndexEntry = /<sitemap>\s*<loc>[^<]+<\/loc>/gm;
1320
1338
  const sitemapLoc = /<loc>([^<]+)<\/loc>/;
1321
1339
  const loadingShell = /class=["'][^"']*\bblox-initial-loading\b/;
1322
1340
  const embeddedCmsState = /\b__BLOX_HYDRATION__\b/;
1341
+ async function validateInternalLinks(outDir) {
1342
+ const root = path.resolve(outDir);
1343
+ const failures = [];
1344
+ for (const file of await collectHtmlFiles(root)) {
1345
+ const name = path.relative(root, file);
1346
+ const html = await fs.readFile(file, "utf8");
1347
+ for (const href of relativeInternalLinks(html)) {
1348
+ failures.push(
1349
+ `${name}: relative internal link "${href}" — resolves against this page (e.g. "/about" + "contact" → "/about/contact"). Use a root-relative path ("/contact") or an absolute URL.`
1350
+ );
1351
+ }
1352
+ }
1353
+ if (failures.length) {
1354
+ throw new Error(`Invalid Blox SSG output:
1355
+ ${failures.map((item) => ` - ${item}`).join("\n")}`);
1356
+ }
1357
+ }
1323
1358
  const anchorHref = /<a\b[^>]*\shref=["']([^"']*)["']/gi;
1324
1359
  const ABSOLUTE_OR_SPECIAL = /^(?:[a-z][a-z0-9+.-]*:|\/\/|\/|#|\?|$)/i;
1360
+ async function readSiteNoindex(outDir) {
1361
+ try {
1362
+ const robots = await fs.readFile(path.join(outDir, "robots.txt"), "utf8");
1363
+ return /^\s*Disallow:\s*\/\s*$/im.test(robots);
1364
+ } catch {
1365
+ return false;
1366
+ }
1367
+ }
1325
1368
  async function validateSsgOutput(options) {
1326
1369
  const outDir = path.resolve(options.outDir);
1327
1370
  const failures = [];
@@ -1357,10 +1400,13 @@ async function validateSsgOutput(options) {
1357
1400
  }
1358
1401
  if (!htmlFiles.length) failures.push("output contains no HTML files");
1359
1402
  if (!hydratedFiles) failures.push("output contains no prerendered Blox hydration state");
1403
+ const siteNoindex = await readSiteNoindex(outDir);
1404
+ const optionalWhenNoindex = /* @__PURE__ */ new Set(["sitemap.xml", "llms.txt", "llms-full.txt"]);
1360
1405
  for (const required of requiredFiles) {
1406
+ if (siteNoindex && optionalWhenNoindex.has(required)) continue;
1361
1407
  try {
1362
1408
  const content = await fs.readFile(path.join(outDir, required), "utf8");
1363
- if (required === "robots.txt" && /^Disallow:\s*\/\s*$/im.test(content)) {
1409
+ if (required === "robots.txt" && !siteNoindex && /^Disallow:\s*\/\s*$/im.test(content)) {
1364
1410
  failures.push("robots.txt blocks the entire site");
1365
1411
  }
1366
1412
  if (required === "llms.txt" && !/^- \[[^\]]+\]\([^)]+\)/m.test(content)) {
@@ -1451,4 +1497,5 @@ exports.primaryContextEntry = primaryContextEntry;
1451
1497
  exports.resolveManifestProvenance = resolveManifestProvenance;
1452
1498
  exports.splitSitemapPathsByLocale = splitSitemapPathsByLocale;
1453
1499
  exports.uploadBloxManifest = uploadBloxManifest;
1500
+ exports.validateInternalLinks = validateInternalLinks;
1454
1501
  exports.validateSsgOutput = validateSsgOutput;
@@ -901,7 +901,7 @@ async function prerender({
901
901
  redirects = [],
902
902
  llm = {}
903
903
  } = {}) {
904
- var _a, _b, _c;
904
+ var _a, _b, _c, _d, _e, _f;
905
905
  const absRoot = path.resolve(root);
906
906
  const absClient = path.resolve(absRoot, clientOutDir);
907
907
  const absServerEntry = path.resolve(absRoot, serverEntry);
@@ -1056,7 +1056,7 @@ async function prerender({
1056
1056
  const canonicalBase = (((_a = website == null ? void 0 : website.seo) == null ? void 0 : _a.canonical_base_url) || (website == null ? void 0 : website.domain) || "").replace(/\/$/, "");
1057
1057
  if (writeSharedAssets) {
1058
1058
  const sitemapPaths = rendered.filter((p) => !noindexPaths.has(p));
1059
- if (canonicalBase && sitemapPaths.length > 0) {
1059
+ if (!((_b = website == null ? void 0 : website.seo) == null ? void 0 : _b.noindex) && canonicalBase && sitemapPaths.length > 0) {
1060
1060
  const artifacts = generateSitemapArtifacts({
1061
1061
  routes: sitemapPaths.map((routePath) => ({
1062
1062
  path: routePath,
@@ -1065,7 +1065,7 @@ async function prerender({
1065
1065
  canonicalBase,
1066
1066
  defaultLocale: (website == null ? void 0 : website.default_locale) ?? "en",
1067
1067
  supportedLocales: (website == null ? void 0 : website.supported_locales) ?? [(website == null ? void 0 : website.default_locale) ?? "en"],
1068
- splitByLocale: (_b = website == null ? void 0 : website.seo) == null ? void 0 : _b.sitemap_split_by_locale
1068
+ splitByLocale: (_c = website == null ? void 0 : website.seo) == null ? void 0 : _c.sitemap_split_by_locale
1069
1069
  });
1070
1070
  for (const artifact of artifacts) {
1071
1071
  await fs.writeFile(
@@ -1080,10 +1080,28 @@ async function prerender({
1080
1080
  const robotsTxt = generateRobotsTxt({
1081
1081
  robotsTxt: website == null ? void 0 : website.robots_txt,
1082
1082
  canonicalBase,
1083
- noindex: (_c = website == null ? void 0 : website.seo) == null ? void 0 : _c.noindex
1083
+ noindex: (_d = website == null ? void 0 : website.seo) == null ? void 0 : _d.noindex
1084
1084
  });
1085
1085
  await fs.writeFile(path.join(absClient, "robots.txt"), robotsTxt, "utf8");
1086
1086
  console.log(" Generated robots.txt");
1087
+ if ((_e = website == null ? void 0 : website.seo) == null ? void 0 : _e.noindex) {
1088
+ const headersPath = path.join(absClient, "_headers");
1089
+ let existing = "";
1090
+ try {
1091
+ existing = await fs.readFile(headersPath, "utf8");
1092
+ } catch {
1093
+ }
1094
+ if (!existing.includes("X-Robots-Tag")) {
1095
+ await fs.writeFile(
1096
+ headersPath,
1097
+ `/*
1098
+ X-Robots-Tag: noindex, nofollow
1099
+ ${existing}`,
1100
+ "utf8"
1101
+ );
1102
+ console.log(" Generated _headers (X-Robots-Tag: noindex)");
1103
+ }
1104
+ }
1087
1105
  if (redirects.length > 0) {
1088
1106
  const redirectsContent = generateNetlifyRedirects(redirects);
1089
1107
  const redirectsPath = path.join(absClient, "_redirects");
@@ -1095,7 +1113,7 @@ async function prerender({
1095
1113
  await fs.writeFile(redirectsPath, redirectsContent + existing, "utf8");
1096
1114
  console.log(` Generated _redirects (${redirects.length} rules)`);
1097
1115
  }
1098
- if (llmEnabled && llm.writeIndex !== false && llmPages.length > 0) {
1116
+ if (!((_f = website == null ? void 0 : website.seo) == null ? void 0 : _f.noindex) && llmEnabled && llm.writeIndex !== false && llmPages.length > 0) {
1099
1117
  const indexable = llmPages.filter((p) => !noindexPaths.has(p.url));
1100
1118
  const llmsTxt = generateLlmsTxt({ website, pages: indexable, config: llm });
1101
1119
  await fs.writeFile(path.join(absClient, "llms.txt"), llmsTxt, "utf8");
@@ -1297,8 +1315,33 @@ const sitemapIndexEntry = /<sitemap>\s*<loc>[^<]+<\/loc>/gm;
1297
1315
  const sitemapLoc = /<loc>([^<]+)<\/loc>/;
1298
1316
  const loadingShell = /class=["'][^"']*\bblox-initial-loading\b/;
1299
1317
  const embeddedCmsState = /\b__BLOX_HYDRATION__\b/;
1318
+ async function validateInternalLinks(outDir) {
1319
+ const root = path.resolve(outDir);
1320
+ const failures = [];
1321
+ for (const file of await collectHtmlFiles(root)) {
1322
+ const name = path.relative(root, file);
1323
+ const html = await fs.readFile(file, "utf8");
1324
+ for (const href of relativeInternalLinks(html)) {
1325
+ failures.push(
1326
+ `${name}: relative internal link "${href}" — resolves against this page (e.g. "/about" + "contact" → "/about/contact"). Use a root-relative path ("/contact") or an absolute URL.`
1327
+ );
1328
+ }
1329
+ }
1330
+ if (failures.length) {
1331
+ throw new Error(`Invalid Blox SSG output:
1332
+ ${failures.map((item) => ` - ${item}`).join("\n")}`);
1333
+ }
1334
+ }
1300
1335
  const anchorHref = /<a\b[^>]*\shref=["']([^"']*)["']/gi;
1301
1336
  const ABSOLUTE_OR_SPECIAL = /^(?:[a-z][a-z0-9+.-]*:|\/\/|\/|#|\?|$)/i;
1337
+ async function readSiteNoindex(outDir) {
1338
+ try {
1339
+ const robots = await fs.readFile(path.join(outDir, "robots.txt"), "utf8");
1340
+ return /^\s*Disallow:\s*\/\s*$/im.test(robots);
1341
+ } catch {
1342
+ return false;
1343
+ }
1344
+ }
1302
1345
  async function validateSsgOutput(options) {
1303
1346
  const outDir = path.resolve(options.outDir);
1304
1347
  const failures = [];
@@ -1334,10 +1377,13 @@ async function validateSsgOutput(options) {
1334
1377
  }
1335
1378
  if (!htmlFiles.length) failures.push("output contains no HTML files");
1336
1379
  if (!hydratedFiles) failures.push("output contains no prerendered Blox hydration state");
1380
+ const siteNoindex = await readSiteNoindex(outDir);
1381
+ const optionalWhenNoindex = /* @__PURE__ */ new Set(["sitemap.xml", "llms.txt", "llms-full.txt"]);
1337
1382
  for (const required of requiredFiles) {
1383
+ if (siteNoindex && optionalWhenNoindex.has(required)) continue;
1338
1384
  try {
1339
1385
  const content = await fs.readFile(path.join(outDir, required), "utf8");
1340
- if (required === "robots.txt" && /^Disallow:\s*\/\s*$/im.test(content)) {
1386
+ if (required === "robots.txt" && !siteNoindex && /^Disallow:\s*\/\s*$/im.test(content)) {
1341
1387
  failures.push("robots.txt blocks the entire site");
1342
1388
  }
1343
1389
  if (required === "llms.txt" && !/^- \[[^\]]+\]\([^)]+\)/m.test(content)) {
@@ -1402,32 +1448,33 @@ async function validateSitemap(outDir, content) {
1402
1448
  return failures;
1403
1449
  }
1404
1450
  export {
1405
- llmArtifacts as A,
1451
+ splitSitemapPathsByLocale as A,
1406
1452
  BloxBuildResponseError as B,
1453
+ llmArtifacts as C,
1407
1454
  prerender as a,
1408
1455
  buildCollectionResolver as b,
1409
- buildPageHead as c,
1410
- primaryContextEntry as d,
1411
- buildJsonLd as e,
1456
+ validateInternalLinks as c,
1457
+ buildPageHead as d,
1458
+ primaryContextEntry as e,
1412
1459
  fetchCmsSiteData as f,
1413
1460
  generateSitemapArtifacts as g,
1414
- extractPageText as h,
1415
- bloxBuildJson as i,
1416
- bloxBuildRequest as j,
1417
- buildSiteHead as k,
1418
- generateLlmsFullTxt as l,
1419
- generateLlmsTxt as m,
1420
- generateNetlifyRedirects as n,
1421
- generateRobotsTxt as o,
1461
+ buildJsonLd as h,
1462
+ extractPageText as i,
1463
+ bloxBuildJson as j,
1464
+ bloxBuildRequest as k,
1465
+ buildSiteHead as l,
1466
+ generateLlmsFullTxt as m,
1467
+ generateLlmsTxt as n,
1468
+ generateNetlifyRedirects as o,
1422
1469
  polyfillBloxSsgGlobals as p,
1423
- generateSitemapIndexXml as q,
1424
- generateSitemapXml as r,
1425
- getBloxBuildRequestOptions as s,
1426
- getSiteIconFlags as t,
1470
+ generateRobotsTxt as q,
1471
+ generateSitemapIndexXml as r,
1472
+ generateSitemapXml as s,
1473
+ getBloxBuildRequestOptions as t,
1427
1474
  uploadBloxManifest as u,
1428
1475
  validateSsgOutput as v,
1429
- localeSitemapPath as w,
1430
- mergeLlmParts as x,
1431
- resolveManifestProvenance as y,
1432
- splitSitemapPathsByLocale as z
1476
+ getSiteIconFlags as w,
1477
+ localeSitemapPath as x,
1478
+ mergeLlmParts as y,
1479
+ resolveManifestProvenance as z
1433
1480
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bagelink/blox",
3
3
  "type": "module",
4
- "version": "1.15.649",
4
+ "version": "1.15.653",
5
5
  "description": "Blox page builder library for drag-and-drop page building and static data management",
6
6
  "author": {
7
7
  "name": "Bagel Studio",