@bagelink/blox 1.15.533 → 1.15.539

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.
@@ -21,12 +21,81 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
21
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
22
  mod
23
23
  ));
24
+ const process$1 = require("node:process");
24
25
  const node_child_process = require("node:child_process");
25
26
  const fs = require("node:fs/promises");
26
27
  const path = require("node:path");
27
- const process$1 = require("node:process");
28
28
  const node_url = require("node:url");
29
29
  const pageTitle = require("./pageTitle-nIl1cDfD.cjs");
30
+ const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
31
+ const PAYLOAD_FIELDS = /* @__PURE__ */ new Set(["version", "source", "website_id", "grant"]);
32
+ function parseJsonObject(value) {
33
+ let parsed;
34
+ try {
35
+ parsed = JSON.parse(value);
36
+ } catch {
37
+ throw new Error("Invalid Blox build hook payload");
38
+ }
39
+ if (parsed === null || Array.isArray(parsed) || typeof parsed !== "object") {
40
+ throw new Error("Invalid Blox build hook payload");
41
+ }
42
+ return parsed;
43
+ }
44
+ function parseBloxBuildGrantBody(body) {
45
+ const trimmed = body.trim();
46
+ if (!trimmed) throw new Error("Missing Blox build hook payload");
47
+ let payload;
48
+ try {
49
+ payload = parseJsonObject(trimmed);
50
+ } catch {
51
+ let decoded;
52
+ try {
53
+ decoded = decodeURIComponent(trimmed);
54
+ } catch {
55
+ throw new Error("Invalid Blox build hook payload");
56
+ }
57
+ payload = parseJsonObject(decoded);
58
+ }
59
+ if (Object.keys(payload).some((field) => !PAYLOAD_FIELDS.has(field))) {
60
+ throw new Error("Invalid Blox build hook payload fields");
61
+ }
62
+ if (payload.version !== 1 || payload.source !== "blox" || typeof payload.website_id !== "string" || !UUID_PATTERN.test(payload.website_id) || typeof payload.grant !== "string" || payload.grant.trim() === "") {
63
+ throw new Error("Invalid Blox build hook payload");
64
+ }
65
+ return {
66
+ version: 1,
67
+ source: "blox",
68
+ websiteId: payload.website_id,
69
+ grant: payload.grant
70
+ };
71
+ }
72
+ function isLocalOrTest(mode, env2) {
73
+ return mode === "localhost" || mode === "development" || mode === "test" || env2.NODE_ENV === "development" || env2.NODE_ENV === "test";
74
+ }
75
+ function resolveBloxBuildGrant(options = {}) {
76
+ var _a, _b, _c, _d, _e;
77
+ const env2 = options.env ?? process$1.env;
78
+ if (isLocalOrTest(options.mode, env2)) {
79
+ const websiteId = (_a = env2.BLOX_WEBSITE_ID) == null ? void 0 : _a.trim();
80
+ const grant2 = (_b = env2.BLOX_BUILD_TOKEN) == null ? void 0 : _b.trim();
81
+ if (!websiteId && !grant2) return void 0;
82
+ if (!websiteId || !UUID_PATTERN.test(websiteId) || !grant2) {
83
+ throw new Error("Local Blox build overrides require a valid BLOX_WEBSITE_ID and BLOX_BUILD_TOKEN");
84
+ }
85
+ return { version: 1, source: "blox", websiteId, grant: grant2 };
86
+ }
87
+ if (!((_c = env2.INCOMING_HOOK_URL) == null ? void 0 : _c.trim()) || !((_d = env2.INCOMING_HOOK_TITLE) == null ? void 0 : _d.trim())) {
88
+ throw new Error("Blox production builds require Netlify incoming hook metadata");
89
+ }
90
+ const body = env2.INCOMING_HOOK_BODY;
91
+ if (body == null) throw new Error("Blox production builds require a hook payload");
92
+ const grant = parseBloxBuildGrantBody(body);
93
+ const configuredWebsiteId = (_e = env2.BLOX_WEBSITE_ID) == null ? void 0 : _e.trim();
94
+ if (configuredWebsiteId && configuredWebsiteId !== grant.websiteId) {
95
+ throw new Error("Blox build hook website does not match the configured website");
96
+ }
97
+ return grant;
98
+ }
30
99
  class BloxBuildResponseError extends Error {
31
100
  constructor(status, url, statusText) {
32
101
  super(`Build API ${status} ${statusText}: ${url}`);
@@ -41,6 +110,7 @@ function env(name) {
41
110
  }
42
111
  function getBloxBuildRequestOptions(overrides = {}) {
43
112
  return {
113
+ websiteId: overrides.websiteId,
44
114
  token: overrides.token ?? env("BLOX_BUILD_TOKEN"),
45
115
  tokenHeader: overrides.tokenHeader ?? env("BLOX_BUILD_TOKEN_HEADER") ?? "Authorization",
46
116
  tokenScheme: overrides.tokenScheme ?? env("BLOX_BUILD_TOKEN_SCHEME") ?? "Bearer",
@@ -111,7 +181,7 @@ function expandLocalePaths(paths, defaultLocale, supportedLocales) {
111
181
  }
112
182
  async function fetchCmsSiteData(apiBase, websiteName, requestOptions = {}) {
113
183
  var _a, _b, _c;
114
- const websiteId = typeof process !== "undefined" ? (_b = (_a = process.env) == null ? void 0 : _a.BLOX_WEBSITE_ID) == null ? void 0 : _b.trim() : void 0;
184
+ const websiteId = requestOptions.websiteId ?? (typeof process !== "undefined" ? (_b = (_a = process.env) == null ? void 0 : _a.BLOX_WEBSITE_ID) == null ? void 0 : _b.trim() : void 0);
115
185
  if (websiteId) {
116
186
  const bundle = await bloxBuildJson(
117
187
  `${apiBase}/blox-build/websites/${websiteId}/published`,
@@ -134,9 +204,11 @@ async function fetchCmsSiteData(apiBase, websiteName, requestOptions = {}) {
134
204
  collections: extractCollectionRefs(bundle.pages)
135
205
  };
136
206
  }
137
- const sitesData = await bloxBuildJson(`${apiBase}/blox/websites`, {}, requestOptions);
138
- const sites = Array.isArray(sitesData) ? sitesData : sitesData.data ?? [];
139
- const site = sites.find((s) => s.name === websiteName);
207
+ const site = await bloxBuildJson(
208
+ `${apiBase}/blox/sites/${encodeURIComponent(websiteName)}`,
209
+ {},
210
+ requestOptions
211
+ );
140
212
  if ((site == null ? void 0 : site.id) == null || site.id === "") throw new Error(`Website "${websiteName}" not found at ${apiBase}`);
141
213
  const website = await bloxBuildJson(
142
214
  `${apiBase}/blox/websites/${site.id}`,
@@ -192,6 +264,7 @@ async function fetchCmsPrerenderPaths(apiBase, websiteName, requestOptions = {})
192
264
  }
193
265
  async function expandRoutes(apiBase, pages, requestOptions, locale) {
194
266
  const routes = [];
267
+ const grantedWebsiteId = requestOptions.websiteId;
195
268
  for (const page of pages) {
196
269
  const slug = page.slug ?? "/";
197
270
  if (page.status && page.status !== "published") continue;
@@ -201,12 +274,13 @@ async function expandRoutes(apiBase, pages, requestOptions, locale) {
201
274
  if (b.adapter === "datastore" && b.collection != null && b.collection !== "" && b.store != null && b.store !== "" && b.bind_by != null && b.bind_by !== "") {
202
275
  const q = b.filter ? `&q=${encodeURIComponent(b.filter)}` : "";
203
276
  const localeParam = locale ? `&locale=${encodeURIComponent(locale)}` : "";
277
+ const collectionPath = grantedWebsiteId ? `/blox-build/websites/${grantedWebsiteId}/collections/${b.store}/${b.collection}` : `/datastore/${b.store}/collections/${b.collection}`;
204
278
  const itemsData = await bloxBuildJson(
205
- `${apiBase}/datastore/${b.store}/collections/${b.collection}?limit=200${localeParam}${q}`,
279
+ `${apiBase}${collectionPath}?limit=200${localeParam}${q}`,
206
280
  {},
207
281
  requestOptions
208
282
  );
209
- const items = Array.isArray(itemsData) ? itemsData : itemsData.data ?? [];
283
+ const items = Array.isArray(itemsData) ? itemsData : itemsData.items ?? itemsData.data ?? [];
210
284
  for (const item of items) {
211
285
  const bindValue = item[b.bind_by];
212
286
  if (typeof bindValue !== "string" || bindValue === "") continue;
@@ -272,9 +346,9 @@ async function resolveManifestProvenance() {
272
346
  commit
273
347
  };
274
348
  }
275
- async function uploadBloxManifest(apiBase, websiteId) {
276
- if (!environment("BLOX_BUILD_TOKEN")) {
277
- throw new Error("BLOX_BUILD_TOKEN is required to upload the Blox manifest");
349
+ async function uploadBloxManifest(apiBase, websiteId, requestOptions = {}) {
350
+ if (!requestOptions.token && !environment("BLOX_BUILD_TOKEN")) {
351
+ throw new Error("A Blox build grant is required to upload the Blox manifest");
278
352
  }
279
353
  const distDirectory = path.resolve(process$1.cwd(), "dist");
280
354
  const resolvedManifestPath = path.resolve(distDirectory, ".blox-manifest.json");
@@ -289,7 +363,8 @@ async function uploadBloxManifest(apiBase, websiteId) {
289
363
  method: "PUT",
290
364
  headers: { "Content-Type": "application/json" },
291
365
  body: JSON.stringify(payload)
292
- }
366
+ },
367
+ requestOptions
293
368
  );
294
369
  }
295
370
  async function polyfillBloxSsgGlobals(options) {
@@ -500,6 +575,45 @@ function localeSitemapPath(locale) {
500
575
  }
501
576
  return `/sitemap-${locale}.xml`;
502
577
  }
578
+ function generateSitemapArtifacts(options) {
579
+ const indexableRoutes = options.routes.filter((route) => !route.noindex);
580
+ const renderedPaths = indexableRoutes.map((route) => route.path);
581
+ const lastmod = Object.fromEntries(
582
+ indexableRoutes.flatMap((route) => route.lastmod ? [[route.path, route.lastmod]] : [])
583
+ );
584
+ if (options.splitByLocale === false) {
585
+ return [{
586
+ path: "/sitemap.xml",
587
+ content: generateSitemapXml({ renderedPaths, canonicalBase: options.canonicalBase, lastmod })
588
+ }];
589
+ }
590
+ const pathsByLocale = splitSitemapPathsByLocale({
591
+ renderedPaths,
592
+ defaultLocale: options.defaultLocale,
593
+ supportedLocales: options.supportedLocales
594
+ });
595
+ const localeArtifacts = [...pathsByLocale].map(([locale, paths]) => ({
596
+ path: localeSitemapPath(locale),
597
+ content: generateSitemapXml({
598
+ renderedPaths: paths,
599
+ canonicalBase: options.canonicalBase,
600
+ lastmod
601
+ })
602
+ }));
603
+ return [
604
+ {
605
+ path: "/sitemap.xml",
606
+ content: generateSitemapIndexXml({
607
+ canonicalBase: options.canonicalBase,
608
+ sitemapPaths: [
609
+ ...localeArtifacts.map((artifact) => artifact.path),
610
+ ...options.additionalSitemapPaths ?? []
611
+ ]
612
+ })
613
+ },
614
+ ...localeArtifacts
615
+ ];
616
+ }
503
617
  function generateRobotsTxt(options) {
504
618
  const { robotsTxt, canonicalBase, noindex } = options;
505
619
  if (noindex) {
@@ -978,38 +1092,25 @@ async function prerender({
978
1092
  if (writeSharedAssets) {
979
1093
  const sitemapPaths = rendered.filter((p) => !noindexPaths.has(p));
980
1094
  if (canonicalBase && sitemapPaths.length > 0) {
981
- if (((_b = website == null ? void 0 : website.seo) == null ? void 0 : _b.sitemap_split_by_locale) !== false) {
982
- const pathsByLocale = splitSitemapPathsByLocale({
983
- renderedPaths: sitemapPaths,
984
- defaultLocale: (website == null ? void 0 : website.default_locale) ?? "en",
985
- supportedLocales: (website == null ? void 0 : website.supported_locales) ?? [(website == null ? void 0 : website.default_locale) ?? "en"]
986
- });
987
- const sitemapFiles = [];
988
- for (const [locale, localePaths] of pathsByLocale) {
989
- const sitemapPath = localeSitemapPath(locale);
990
- await fs.writeFile(
991
- path.join(absClient, sitemapPath.slice(1)),
992
- generateSitemapXml({ renderedPaths: localePaths, canonicalBase, lastmod }),
993
- "utf8"
994
- );
995
- sitemapFiles.push(sitemapPath);
996
- }
997
- await fs.writeFile(
998
- path.join(absClient, "sitemap.xml"),
999
- generateSitemapIndexXml({ canonicalBase, sitemapPaths: sitemapFiles }),
1000
- "utf8"
1001
- );
1002
- console.log(` Generated sitemap.xml (${sitemapFiles.length} locales, ${sitemapPaths.length} URLs)`);
1003
- } else {
1095
+ const artifacts = generateSitemapArtifacts({
1096
+ routes: sitemapPaths.map((routePath) => ({
1097
+ path: routePath,
1098
+ lastmod: lastmod[routePath]
1099
+ })),
1100
+ canonicalBase,
1101
+ defaultLocale: (website == null ? void 0 : website.default_locale) ?? "en",
1102
+ supportedLocales: (website == null ? void 0 : website.supported_locales) ?? [(website == null ? void 0 : website.default_locale) ?? "en"],
1103
+ splitByLocale: (_b = website == null ? void 0 : website.seo) == null ? void 0 : _b.sitemap_split_by_locale
1104
+ });
1105
+ for (const artifact of artifacts) {
1004
1106
  await fs.writeFile(
1005
- path.join(absClient, "sitemap.xml"),
1006
- generateSitemapXml({ renderedPaths: sitemapPaths, canonicalBase, lastmod }),
1107
+ path.join(absClient, artifact.path.slice(1)),
1108
+ artifact.content,
1007
1109
  "utf8"
1008
1110
  );
1009
- console.log(` Generated sitemap.xml (${sitemapPaths.length} URLs)`);
1010
1111
  }
1011
1112
  const skipped = noindexPaths.size > 0 ? ` (${noindexPaths.size} noindex excluded)` : "";
1012
- if (skipped) console.log(` Sitemap exclusions:${skipped}`);
1113
+ console.log(` Generated sitemap.xml (${artifacts.length} files, ${sitemapPaths.length} URLs)${skipped}`);
1013
1114
  }
1014
1115
  const robotsTxt = generateRobotsTxt({
1015
1116
  robotsTxt: website == null ? void 0 : website.robots_txt,
@@ -1296,6 +1397,7 @@ exports.generateLlmsFullTxt = generateLlmsFullTxt;
1296
1397
  exports.generateLlmsTxt = generateLlmsTxt;
1297
1398
  exports.generateNetlifyRedirects = generateNetlifyRedirects;
1298
1399
  exports.generateRobotsTxt = generateRobotsTxt;
1400
+ exports.generateSitemapArtifacts = generateSitemapArtifacts;
1299
1401
  exports.generateSitemapIndexXml = generateSitemapIndexXml;
1300
1402
  exports.generateSitemapXml = generateSitemapXml;
1301
1403
  exports.getBloxBuildRequestOptions = getBloxBuildRequestOptions;
@@ -1303,9 +1405,11 @@ exports.getSiteIconFlags = getSiteIconFlags;
1303
1405
  exports.llmArtifacts = llmArtifacts;
1304
1406
  exports.localeSitemapPath = localeSitemapPath;
1305
1407
  exports.mergeLlmParts = mergeLlmParts;
1408
+ exports.parseBloxBuildGrantBody = parseBloxBuildGrantBody;
1306
1409
  exports.polyfillBloxSsgGlobals = polyfillBloxSsgGlobals;
1307
1410
  exports.prerender = prerender;
1308
1411
  exports.primaryContextEntry = primaryContextEntry;
1412
+ exports.resolveBloxBuildGrant = resolveBloxBuildGrant;
1309
1413
  exports.resolveManifestProvenance = resolveManifestProvenance;
1310
1414
  exports.splitSitemapPathsByLocale = splitSitemapPathsByLocale;
1311
1415
  exports.uploadBloxManifest = uploadBloxManifest;
@@ -176,6 +176,7 @@ function bloxPlugin(options = {}) {
176
176
  let outDir = "dist";
177
177
  let isSsrBuild = false;
178
178
  let vuePlugin;
179
+ let resolvedAliases;
179
180
  plugins.push({
180
181
  name: "vite-plugin-blox-manifest",
181
182
  apply: "build",
@@ -184,6 +185,7 @@ function bloxPlugin(options = {}) {
184
185
  outDir = resolved.build.outDir;
185
186
  isSsrBuild = !!resolved.build.ssr;
186
187
  vuePlugin = resolved.plugins.find((plugin) => plugin.name === "vite:vue");
188
+ resolvedAliases = resolved.resolve.alias;
187
189
  },
188
190
  resolveId(id) {
189
191
  if (id === VIRTUAL_ENTRY) return RESOLVED_ENTRY;
@@ -212,6 +214,7 @@ function bloxPlugin(options = {}) {
212
214
  configFile: false,
213
215
  logLevel: "silent",
214
216
  server: { middlewareMode: true, hmr: false },
217
+ resolve: { alias: resolvedAliases },
215
218
  plugins: [
216
219
  {
217
220
  name: "vite-plugin-blox-manifest-entry",
@@ -1 +1 @@
1
- {"version":3,"file":"vite-plugin.d.ts","sourceRoot":"","sources":["../src/vite-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAc,MAAM,MAAM,CAAA;AAM9C,MAAM,WAAW,iBAAiB;IACjC;;;;;OAKG;IACH,GAAG,CAAC,EAAE,OAAO,CAAA;IACb;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB;;;;;;;;;;;;;OAaG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,MAAM,EAAE,CAogBpE"}
1
+ {"version":3,"file":"vite-plugin.d.ts","sourceRoot":"","sources":["../src/vite-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAc,MAAM,MAAM,CAAA;AAM9C,MAAM,WAAW,iBAAiB;IACjC;;;;;OAKG;IACH,GAAG,CAAC,EAAE,OAAO,CAAA;IACb;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB;;;;;;;;;;;;;OAaG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,MAAM,EAAE,CAugBpE"}
@@ -152,6 +152,7 @@ function bloxPlugin(options = {}) {
152
152
  let outDir = "dist";
153
153
  let isSsrBuild = false;
154
154
  let vuePlugin;
155
+ let resolvedAliases;
155
156
  plugins.push({
156
157
  name: "vite-plugin-blox-manifest",
157
158
  apply: "build",
@@ -160,6 +161,7 @@ function bloxPlugin(options = {}) {
160
161
  outDir = resolved.build.outDir;
161
162
  isSsrBuild = !!resolved.build.ssr;
162
163
  vuePlugin = resolved.plugins.find((plugin) => plugin.name === "vite:vue");
164
+ resolvedAliases = resolved.resolve.alias;
163
165
  },
164
166
  resolveId(id) {
165
167
  if (id === VIRTUAL_ENTRY) return RESOLVED_ENTRY;
@@ -188,6 +190,7 @@ function bloxPlugin(options = {}) {
188
190
  configFile: false,
189
191
  logLevel: "silent",
190
192
  server: { middlewareMode: true, hmr: false },
193
+ resolve: { alias: resolvedAliases },
191
194
  plugins: [
192
195
  {
193
196
  name: "vite-plugin-blox-manifest-entry",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bagelink/blox",
3
3
  "type": "module",
4
- "version": "1.15.533",
4
+ "version": "1.15.539",
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",
@@ -28,7 +28,7 @@
28
28
  "static-pages"
29
29
  ],
30
30
  "bin": {
31
- "blox-ssg": "./dist/ssg/cli.mjs"
31
+ "blox-ssg": "dist/ssg/cli.mjs"
32
32
  },
33
33
  "sideEffects": [
34
34
  "**/*.css",