@bagelink/blox 1.12.21 → 1.13.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.
@@ -1 +1 @@
1
- {"version":3,"file":"CmsPageView.vue.d.ts","sourceRoot":"","sources":["../src/CmsPageView.vue"],"names":[],"mappings":";AAkRA,wBAMG"}
1
+ {"version":3,"file":"CmsPageView.vue.d.ts","sourceRoot":"","sources":["../src/CmsPageView.vue"],"names":[],"mappings":";AAgTA,wBAMG"}
package/dist/index.cjs CHANGED
@@ -121,10 +121,9 @@ const _sfc_main$2 = /* @__PURE__ */ vue.defineComponent({
121
121
  const blocks = vue.ref([]);
122
122
  const contexts = vue.ref({});
123
123
  vue.provide("contexts", contexts);
124
- const loading = vue.ref(true);
125
124
  const notFound = vue.ref(false);
126
125
  const error = vue.ref(null);
127
- function hydrateFromState(path) {
126
+ function tryHydrateFromState(path) {
128
127
  var _a, _b;
129
128
  const win = typeof window !== "undefined" ? window : typeof globalThis !== "undefined" ? globalThis : void 0;
130
129
  if (!win) return false;
@@ -135,9 +134,18 @@ const _sfc_main$2 = /* @__PURE__ */ vue.defineComponent({
135
134
  blocks.value = ((_b = (_a = entry.page) == null ? void 0 : _a.content) == null ? void 0 : _b.blocks) ?? [];
136
135
  contexts.value = entry.contexts ?? {};
137
136
  setAlternates(entry.alternates ?? {});
138
- loading.value = false;
139
137
  return true;
140
138
  }
139
+ const initialSlug = (() => {
140
+ var _a;
141
+ if (strategy) {
142
+ const hostname = typeof window !== "undefined" ? ((_a = window.location) == null ? void 0 : _a.hostname) ?? "" : "";
143
+ return strategy.detect(hostname, route.path).slug;
144
+ }
145
+ return route.path || "/";
146
+ })();
147
+ const ssrHydrated = tryHydrateFromState(initialSlug);
148
+ const loading = vue.ref(!ssrHydrated);
141
149
  async function load() {
142
150
  var _a;
143
151
  loading.value = true;
@@ -146,7 +154,10 @@ const _sfc_main$2 = /* @__PURE__ */ vue.defineComponent({
146
154
  const { locale: urlLocale, slug } = strategy ? strategy.detect(typeof window !== "undefined" ? window.location.hostname : "", route.path) : { locale: locale2.value, slug: route.path || "/" };
147
155
  if (urlLocale !== locale2.value) setLocale(urlLocale);
148
156
  setAlternates({});
149
- if (hydrateFromState(slug)) return;
157
+ if (tryHydrateFromState(slug)) {
158
+ loading.value = false;
159
+ return;
160
+ }
150
161
  try {
151
162
  const resolved = await resolvePath(slug, urlLocale);
152
163
  blocks.value = ((_a = resolved.page.content) == null ? void 0 : _a.blocks) ?? [];
@@ -164,7 +175,9 @@ const _sfc_main$2 = /* @__PURE__ */ vue.defineComponent({
164
175
  loading.value = false;
165
176
  }
166
177
  }
167
- load();
178
+ if (!ssrHydrated) {
179
+ load();
180
+ }
168
181
  vue.watch(() => route.path, load);
169
182
  return (_ctx, _cache) => {
170
183
  const _component_RouterLink = vue.resolveComponent("RouterLink");
package/dist/index.mjs CHANGED
@@ -119,10 +119,9 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
119
119
  const blocks = ref([]);
120
120
  const contexts = ref({});
121
121
  provide("contexts", contexts);
122
- const loading = ref(true);
123
122
  const notFound = ref(false);
124
123
  const error = ref(null);
125
- function hydrateFromState(path) {
124
+ function tryHydrateFromState(path) {
126
125
  var _a, _b;
127
126
  const win = typeof window !== "undefined" ? window : typeof globalThis !== "undefined" ? globalThis : void 0;
128
127
  if (!win) return false;
@@ -133,9 +132,18 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
133
132
  blocks.value = ((_b = (_a = entry.page) == null ? void 0 : _a.content) == null ? void 0 : _b.blocks) ?? [];
134
133
  contexts.value = entry.contexts ?? {};
135
134
  setAlternates(entry.alternates ?? {});
136
- loading.value = false;
137
135
  return true;
138
136
  }
137
+ const initialSlug = (() => {
138
+ var _a;
139
+ if (strategy) {
140
+ const hostname = typeof window !== "undefined" ? ((_a = window.location) == null ? void 0 : _a.hostname) ?? "" : "";
141
+ return strategy.detect(hostname, route.path).slug;
142
+ }
143
+ return route.path || "/";
144
+ })();
145
+ const ssrHydrated = tryHydrateFromState(initialSlug);
146
+ const loading = ref(!ssrHydrated);
139
147
  async function load() {
140
148
  var _a;
141
149
  loading.value = true;
@@ -144,7 +152,10 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
144
152
  const { locale: urlLocale, slug } = strategy ? strategy.detect(typeof window !== "undefined" ? window.location.hostname : "", route.path) : { locale: locale2.value, slug: route.path || "/" };
145
153
  if (urlLocale !== locale2.value) setLocale(urlLocale);
146
154
  setAlternates({});
147
- if (hydrateFromState(slug)) return;
155
+ if (tryHydrateFromState(slug)) {
156
+ loading.value = false;
157
+ return;
158
+ }
148
159
  try {
149
160
  const resolved = await resolvePath(slug, urlLocale);
150
161
  blocks.value = ((_a = resolved.page.content) == null ? void 0 : _a.blocks) ?? [];
@@ -162,7 +173,9 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
162
173
  loading.value = false;
163
174
  }
164
175
  }
165
- load();
176
+ if (!ssrHydrated) {
177
+ load();
178
+ }
166
179
  watch(() => route.path, load);
167
180
  return (_ctx, _cache) => {
168
181
  const _component_RouterLink = resolveComponent("RouterLink");
@@ -2,18 +2,43 @@ import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import process from "node:process";
4
4
  import { pathToFileURL } from "node:url";
5
- async function fetchCmsPrerenderPaths(apiBase, websiteName) {
5
+ async function fetchCmsSiteData(apiBase, websiteName) {
6
6
  const sitesRes = await fetch(`${apiBase}/cms/websites`);
7
7
  const sitesData = await sitesRes.json();
8
8
  const sites = Array.isArray(sitesData) ? sitesData : sitesData.data ?? [];
9
9
  const site = sites.find((s) => s.name === websiteName);
10
10
  if ((site == null ? void 0 : site.id) == null || site.id === "") throw new Error(`Website "${websiteName}" not found at ${apiBase}`);
11
- const pagesRes = await fetch(`${apiBase}/cms/websites/${site.id}/pages?locale=en`);
11
+ let website = site;
12
+ try {
13
+ const wRes = await fetch(`${apiBase}/cms/websites/${site.id}`);
14
+ if (wRes.ok) website = await wRes.json();
15
+ } catch {
16
+ }
17
+ const pagesRes = await fetch(`${apiBase}/cms/websites/${site.id}/pages?locale=${website.default_locale || "en"}`);
12
18
  const pagesData = await pagesRes.json();
13
19
  const pages = Array.isArray(pagesData) ? pagesData : pagesData.data ?? [];
20
+ const paths = await expandRoutes(apiBase, pages);
21
+ let redirects = [];
22
+ try {
23
+ const rRes = await fetch(`${apiBase}/cms/websites/${site.id}/redirects`);
24
+ if (rRes.ok) {
25
+ const rData = await rRes.json();
26
+ const items = Array.isArray(rData) ? rData : rData.data ?? [];
27
+ redirects = items.filter((r) => r.from_path && r.to_path);
28
+ }
29
+ } catch {
30
+ }
31
+ return { websiteId: site.id, website, paths, redirects };
32
+ }
33
+ async function fetchCmsPrerenderPaths(apiBase, websiteName) {
34
+ const { paths } = await fetchCmsSiteData(apiBase, websiteName);
35
+ return paths;
36
+ }
37
+ async function expandRoutes(apiBase, pages) {
14
38
  const routes = [];
15
39
  for (const page of pages) {
16
40
  const slug = page.slug ?? "/";
41
+ if (page.status && page.status !== "published") continue;
17
42
  if (slug.includes(":") && page.data_bindings) {
18
43
  for (const [, binding] of Object.entries(page.data_bindings)) {
19
44
  const b = binding;
@@ -27,7 +52,10 @@ async function fetchCmsPrerenderPaths(apiBase, websiteName) {
27
52
  for (const item of items) {
28
53
  const bindValue = item[b.bind_by];
29
54
  if (bindValue != null && bindValue !== "") {
30
- const concrete = slug.replace(/:([a-z_]+)/g, (_m, p) => p === b.bind_by ? String(bindValue) : String(item[p] ?? ""));
55
+ const concrete = slug.replace(
56
+ /:([a-z_]+)/g,
57
+ (_m, p) => p === b.bind_by ? String(bindValue) : String(item[p] ?? "")
58
+ );
31
59
  routes.push(concrete);
32
60
  }
33
61
  }
@@ -86,6 +114,133 @@ async function polyfillBloxSsgGlobals(options) {
86
114
  g.window.location = _win.location;
87
115
  g.window.document = _win.document;
88
116
  }
117
+ function buildPageHead(options) {
118
+ const { url, resolvedData, website, stateScript } = options;
119
+ const parts = [];
120
+ const meta = (website == null ? void 0 : website.meta) ?? {};
121
+ const page = resolvedData == null ? void 0 : resolvedData.page;
122
+ const title = (page == null ? void 0 : page.meta_title) || ((page == null ? void 0 : page.title) && meta.og_site_name ? `${page.title} · ${meta.og_site_name}` : null) || (page == null ? void 0 : page.title) || meta.default_meta_title;
123
+ if (title) {
124
+ parts.push(`<title>${esc(title)}</title>`);
125
+ }
126
+ const description = (page == null ? void 0 : page.meta_description) || meta.default_meta_description;
127
+ if (description) {
128
+ parts.push(`<meta name="description" content="${esc(description)}">`);
129
+ }
130
+ const canonicalBase = (meta.canonical_base_url || (website == null ? void 0 : website.domain) || "").replace(/\/$/, "");
131
+ if (canonicalBase) {
132
+ parts.push(`<link rel="canonical" href="${esc(canonicalBase + url)}">`);
133
+ }
134
+ const ogTitle = (page == null ? void 0 : page.meta_title) || (page == null ? void 0 : page.title) || meta.og_site_name || meta.default_meta_title;
135
+ const ogDesc = (page == null ? void 0 : page.meta_description) || meta.og_description || meta.default_meta_description;
136
+ const ogImage = (page == null ? void 0 : page.og_image) || meta.default_og_image;
137
+ const ogType = meta.og_type || "website";
138
+ if (ogTitle) parts.push(`<meta property="og:title" content="${esc(ogTitle)}">`);
139
+ if (ogDesc) parts.push(`<meta property="og:description" content="${esc(ogDesc)}">`);
140
+ if (ogImage) parts.push(`<meta property="og:image" content="${esc(ogImage)}">`);
141
+ parts.push(`<meta property="og:type" content="${esc(ogType)}">`);
142
+ if (canonicalBase) parts.push(`<meta property="og:url" content="${esc(canonicalBase + url)}">`);
143
+ if (meta.og_site_name) parts.push(`<meta property="og:site_name" content="${esc(meta.og_site_name)}">`);
144
+ const twitterCard = meta.twitter_card || "summary_large_image";
145
+ parts.push(`<meta name="twitter:card" content="${esc(twitterCard)}">`);
146
+ if (ogTitle) parts.push(`<meta name="twitter:title" content="${esc(ogTitle)}">`);
147
+ if (ogDesc) parts.push(`<meta name="twitter:description" content="${esc(ogDesc)}">`);
148
+ if (ogImage) parts.push(`<meta name="twitter:image" content="${esc(ogImage)}">`);
149
+ if (meta.twitter_site) parts.push(`<meta name="twitter:site" content="${esc(meta.twitter_site)}">`);
150
+ const alternates = resolvedData == null ? void 0 : resolvedData.alternates;
151
+ if (alternates && canonicalBase) {
152
+ for (const [locale, slug] of Object.entries(alternates)) {
153
+ parts.push(`<link rel="alternate" hreflang="${esc(locale)}" href="${esc(canonicalBase + slug)}">`);
154
+ }
155
+ const defaultSlug = alternates[(website == null ? void 0 : website.default_locale) ?? "en"] ?? url;
156
+ parts.push(`<link rel="alternate" hreflang="x-default" href="${esc(canonicalBase + defaultSlug)}">`);
157
+ }
158
+ if (meta.noindex) {
159
+ parts.push('<meta name="robots" content="noindex, nofollow">');
160
+ }
161
+ if (stateScript) {
162
+ parts.push(stateScript);
163
+ }
164
+ return parts.join("\n");
165
+ }
166
+ function buildSiteHead(website) {
167
+ if (!website) return "";
168
+ const parts = [];
169
+ const meta = website.meta ?? {};
170
+ if (website.favicon_url) {
171
+ parts.push(`<link rel="icon" href="${esc(website.favicon_url)}">`);
172
+ }
173
+ if (meta.webclip) {
174
+ parts.push(`<link rel="apple-touch-icon" sizes="180x180" href="${esc(meta.webclip)}">`);
175
+ }
176
+ if (meta.verification_google) {
177
+ parts.push(`<meta name="google-site-verification" content="${esc(meta.verification_google)}">`);
178
+ }
179
+ if (meta.verification_bing) {
180
+ parts.push(`<meta name="msvalidate.01" content="${esc(meta.verification_bing)}">`);
181
+ }
182
+ if (meta.structured_data_org) {
183
+ try {
184
+ const json = typeof meta.structured_data_org === "string" ? meta.structured_data_org : JSON.stringify(meta.structured_data_org);
185
+ parts.push(`<script type="application/ld+json">${json}${"<"}/script>`);
186
+ } catch {
187
+ }
188
+ }
189
+ const plausibleId = website.plausible_site_id;
190
+ if (plausibleId) {
191
+ parts.push(`<script defer data-domain="${esc(plausibleId)}" src="https://plausible.io/js/script.js"><${"/"}script>`);
192
+ }
193
+ return parts.join("\n");
194
+ }
195
+ function generateSitemapXml(options) {
196
+ const { renderedPaths, canonicalBase, defaultChangefreq = "weekly", defaultPriority = "0.7" } = options;
197
+ const base = canonicalBase.replace(/\/$/, "");
198
+ const urls = renderedPaths.map((p) => {
199
+ const priority = p === "/" ? "1.0" : defaultPriority;
200
+ const changefreq = p === "/" ? "daily" : defaultChangefreq;
201
+ return ` <url>
202
+ <loc>${esc(base + p)}</loc>
203
+ <changefreq>${changefreq}</changefreq>
204
+ <priority>${priority}</priority>
205
+ </url>`;
206
+ });
207
+ return `<?xml version="1.0" encoding="UTF-8"?>
208
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
209
+ ${urls.join("\n")}
210
+ </urlset>
211
+ `;
212
+ }
213
+ function generateRobotsTxt(options) {
214
+ const { robotsTxt, canonicalBase, noindex } = options;
215
+ if (noindex) {
216
+ return `User-agent: *
217
+ Disallow: /
218
+ `;
219
+ }
220
+ const base = (canonicalBase || "").replace(/\/$/, "");
221
+ const sitemapLine = base ? `
222
+ Sitemap: ${base}/sitemap.xml
223
+ ` : "";
224
+ if (robotsTxt) {
225
+ if (sitemapLine && !robotsTxt.toLowerCase().includes("sitemap:")) {
226
+ return robotsTxt.trimEnd() + "\n" + sitemapLine;
227
+ }
228
+ return robotsTxt;
229
+ }
230
+ return `User-agent: *
231
+ Allow: /
232
+ ${sitemapLine}`;
233
+ }
234
+ function generateNetlifyRedirects(redirects) {
235
+ if (redirects.length === 0) return "";
236
+ return redirects.map((r) => `${r.from_path} ${r.to_path} ${r.status_code}`).join("\n") + "\n";
237
+ }
238
+ function stripTemplateSeoTags(template) {
239
+ return template.replace(/<title>[^<]*<\/title>/gi, "<!--ssg:title-->").replace(/<meta\s[^>]*name\s*=\s*["']description["'][^>]*>/gi, "").replace(/<meta\s[^>]*property\s*=\s*["']og:[^"']*["'][^>]*>/gi, "").replace(/<meta\s[^>]*(?:property|name)\s*=\s*["']twitter:[^"']*["'][^>]*>/gi, "").replace(/<link\s[^>]*rel\s*=\s*["']apple-touch-icon["'][^>]*>/gi, "").replace(/<link\s[^>]*rel\s*=\s*["']icon["'][^>]*>/gi, "").replace(/\n\s*\n\s*\n/g, "\n\n");
240
+ }
241
+ function esc(s) {
242
+ return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
243
+ }
89
244
  async function prerender({
90
245
  root = process.cwd(),
91
246
  clientOutDir = "dist/client",
@@ -95,13 +250,21 @@ async function prerender({
95
250
  excludePaths = [],
96
251
  failFast = false,
97
252
  maxPages = 5e3,
98
- mode = "dir"
253
+ mode = "dir",
254
+ website = null,
255
+ redirects = []
99
256
  } = {}) {
100
257
  const absRoot = path.resolve(root);
101
258
  const absClient = path.resolve(absRoot, clientOutDir);
102
259
  const absServerEntry = path.resolve(absRoot, serverEntry);
103
260
  const templatePath = path.join(absClient, "index.html");
104
- const template = await fs.readFile(templatePath, "utf8");
261
+ const rawTemplate = await fs.readFile(templatePath, "utf8");
262
+ let template = stripTemplateSeoTags(rawTemplate);
263
+ const siteHead = buildSiteHead(website);
264
+ if (siteHead) {
265
+ template = template.replace("</head>", `${siteHead}
266
+ </head>`);
267
+ }
105
268
  const serverMod = await import(pathToFileURL(absServerEntry).href);
106
269
  if (typeof serverMod.render !== "function") {
107
270
  throw new TypeError(
@@ -130,7 +293,8 @@ async function prerender({
130
293
  try {
131
294
  const { html, head = "", htmlAttrs = "" } = await serverMod.render(urlPath, {
132
295
  manifest,
133
- template
296
+ template,
297
+ website
134
298
  });
135
299
  const outHtml = injectIntoTemplate(template, head, html, fontPreloads, htmlAttrs);
136
300
  const outfile = outFilePath(absClient, urlPath, mode);
@@ -148,6 +312,31 @@ async function prerender({
148
312
  `, err.stack ?? err);
149
313
  }
150
314
  }
315
+ const websiteMeta = (website == null ? void 0 : website.meta) ?? {};
316
+ const canonicalBase = (websiteMeta.canonical_base_url || (website == null ? void 0 : website.domain) || "").replace(/\/$/, "");
317
+ if (canonicalBase && rendered.length > 0) {
318
+ const sitemap = generateSitemapXml({ renderedPaths: rendered, canonicalBase });
319
+ await fs.writeFile(path.join(absClient, "sitemap.xml"), sitemap, "utf8");
320
+ console.log(` Generated sitemap.xml (${rendered.length} URLs)`);
321
+ }
322
+ const robotsTxt = generateRobotsTxt({
323
+ robotsTxt: website == null ? void 0 : website.robots_txt,
324
+ canonicalBase,
325
+ noindex: websiteMeta.noindex
326
+ });
327
+ await fs.writeFile(path.join(absClient, "robots.txt"), robotsTxt, "utf8");
328
+ console.log(" Generated robots.txt");
329
+ if (redirects.length > 0) {
330
+ const redirectsContent = generateNetlifyRedirects(redirects);
331
+ const redirectsPath = path.join(absClient, "_redirects");
332
+ let existing = "";
333
+ try {
334
+ existing = await fs.readFile(redirectsPath, "utf8");
335
+ } catch {
336
+ }
337
+ await fs.writeFile(redirectsPath, redirectsContent + existing, "utf8");
338
+ console.log(` Generated _redirects (${redirects.length} rules)`);
339
+ }
151
340
  return {
152
341
  rendered,
153
342
  failures,
@@ -233,6 +422,9 @@ function injectIntoTemplate(template, head, appHtml, fontPreloads, htmlAttrs = "
233
422
  </head>`);
234
423
  }
235
424
  if (head) {
425
+ if (/<title>/.test(head)) {
426
+ out = out.replace("<!--ssg:title-->", "");
427
+ }
236
428
  const descTagRe = /<meta\s[^>]*name\s*=\s*["']description["'][^>]*>/gi;
237
429
  const descMatches = [...head.matchAll(descTagRe)];
238
430
  if (descMatches.length > 1) {
@@ -276,7 +468,13 @@ function discoverInternalLinks(html) {
276
468
  return [...out];
277
469
  }
278
470
  export {
279
- prerender as a,
471
+ fetchCmsSiteData as a,
472
+ buildPageHead as b,
473
+ prerender as c,
474
+ buildSiteHead as d,
475
+ generateRobotsTxt as e,
280
476
  fetchCmsPrerenderPaths as f,
477
+ generateNetlifyRedirects as g,
478
+ generateSitemapXml as h,
281
479
  polyfillBloxSsgGlobals as p
282
480
  };
@@ -25,18 +25,43 @@ const fs = require("node:fs/promises");
25
25
  const path = require("node:path");
26
26
  const process = require("node:process");
27
27
  const node_url = require("node:url");
28
- async function fetchCmsPrerenderPaths(apiBase, websiteName) {
28
+ async function fetchCmsSiteData(apiBase, websiteName) {
29
29
  const sitesRes = await fetch(`${apiBase}/cms/websites`);
30
30
  const sitesData = await sitesRes.json();
31
31
  const sites = Array.isArray(sitesData) ? sitesData : sitesData.data ?? [];
32
32
  const site = sites.find((s) => s.name === websiteName);
33
33
  if ((site == null ? void 0 : site.id) == null || site.id === "") throw new Error(`Website "${websiteName}" not found at ${apiBase}`);
34
- const pagesRes = await fetch(`${apiBase}/cms/websites/${site.id}/pages?locale=en`);
34
+ let website = site;
35
+ try {
36
+ const wRes = await fetch(`${apiBase}/cms/websites/${site.id}`);
37
+ if (wRes.ok) website = await wRes.json();
38
+ } catch {
39
+ }
40
+ const pagesRes = await fetch(`${apiBase}/cms/websites/${site.id}/pages?locale=${website.default_locale || "en"}`);
35
41
  const pagesData = await pagesRes.json();
36
42
  const pages = Array.isArray(pagesData) ? pagesData : pagesData.data ?? [];
43
+ const paths = await expandRoutes(apiBase, pages);
44
+ let redirects = [];
45
+ try {
46
+ const rRes = await fetch(`${apiBase}/cms/websites/${site.id}/redirects`);
47
+ if (rRes.ok) {
48
+ const rData = await rRes.json();
49
+ const items = Array.isArray(rData) ? rData : rData.data ?? [];
50
+ redirects = items.filter((r) => r.from_path && r.to_path);
51
+ }
52
+ } catch {
53
+ }
54
+ return { websiteId: site.id, website, paths, redirects };
55
+ }
56
+ async function fetchCmsPrerenderPaths(apiBase, websiteName) {
57
+ const { paths } = await fetchCmsSiteData(apiBase, websiteName);
58
+ return paths;
59
+ }
60
+ async function expandRoutes(apiBase, pages) {
37
61
  const routes = [];
38
62
  for (const page of pages) {
39
63
  const slug = page.slug ?? "/";
64
+ if (page.status && page.status !== "published") continue;
40
65
  if (slug.includes(":") && page.data_bindings) {
41
66
  for (const [, binding] of Object.entries(page.data_bindings)) {
42
67
  const b = binding;
@@ -50,7 +75,10 @@ async function fetchCmsPrerenderPaths(apiBase, websiteName) {
50
75
  for (const item of items) {
51
76
  const bindValue = item[b.bind_by];
52
77
  if (bindValue != null && bindValue !== "") {
53
- const concrete = slug.replace(/:([a-z_]+)/g, (_m, p) => p === b.bind_by ? String(bindValue) : String(item[p] ?? ""));
78
+ const concrete = slug.replace(
79
+ /:([a-z_]+)/g,
80
+ (_m, p) => p === b.bind_by ? String(bindValue) : String(item[p] ?? "")
81
+ );
54
82
  routes.push(concrete);
55
83
  }
56
84
  }
@@ -109,6 +137,133 @@ async function polyfillBloxSsgGlobals(options) {
109
137
  g.window.location = _win.location;
110
138
  g.window.document = _win.document;
111
139
  }
140
+ function buildPageHead(options) {
141
+ const { url, resolvedData, website, stateScript } = options;
142
+ const parts = [];
143
+ const meta = (website == null ? void 0 : website.meta) ?? {};
144
+ const page = resolvedData == null ? void 0 : resolvedData.page;
145
+ const title = (page == null ? void 0 : page.meta_title) || ((page == null ? void 0 : page.title) && meta.og_site_name ? `${page.title} · ${meta.og_site_name}` : null) || (page == null ? void 0 : page.title) || meta.default_meta_title;
146
+ if (title) {
147
+ parts.push(`<title>${esc(title)}</title>`);
148
+ }
149
+ const description = (page == null ? void 0 : page.meta_description) || meta.default_meta_description;
150
+ if (description) {
151
+ parts.push(`<meta name="description" content="${esc(description)}">`);
152
+ }
153
+ const canonicalBase = (meta.canonical_base_url || (website == null ? void 0 : website.domain) || "").replace(/\/$/, "");
154
+ if (canonicalBase) {
155
+ parts.push(`<link rel="canonical" href="${esc(canonicalBase + url)}">`);
156
+ }
157
+ const ogTitle = (page == null ? void 0 : page.meta_title) || (page == null ? void 0 : page.title) || meta.og_site_name || meta.default_meta_title;
158
+ const ogDesc = (page == null ? void 0 : page.meta_description) || meta.og_description || meta.default_meta_description;
159
+ const ogImage = (page == null ? void 0 : page.og_image) || meta.default_og_image;
160
+ const ogType = meta.og_type || "website";
161
+ if (ogTitle) parts.push(`<meta property="og:title" content="${esc(ogTitle)}">`);
162
+ if (ogDesc) parts.push(`<meta property="og:description" content="${esc(ogDesc)}">`);
163
+ if (ogImage) parts.push(`<meta property="og:image" content="${esc(ogImage)}">`);
164
+ parts.push(`<meta property="og:type" content="${esc(ogType)}">`);
165
+ if (canonicalBase) parts.push(`<meta property="og:url" content="${esc(canonicalBase + url)}">`);
166
+ if (meta.og_site_name) parts.push(`<meta property="og:site_name" content="${esc(meta.og_site_name)}">`);
167
+ const twitterCard = meta.twitter_card || "summary_large_image";
168
+ parts.push(`<meta name="twitter:card" content="${esc(twitterCard)}">`);
169
+ if (ogTitle) parts.push(`<meta name="twitter:title" content="${esc(ogTitle)}">`);
170
+ if (ogDesc) parts.push(`<meta name="twitter:description" content="${esc(ogDesc)}">`);
171
+ if (ogImage) parts.push(`<meta name="twitter:image" content="${esc(ogImage)}">`);
172
+ if (meta.twitter_site) parts.push(`<meta name="twitter:site" content="${esc(meta.twitter_site)}">`);
173
+ const alternates = resolvedData == null ? void 0 : resolvedData.alternates;
174
+ if (alternates && canonicalBase) {
175
+ for (const [locale, slug] of Object.entries(alternates)) {
176
+ parts.push(`<link rel="alternate" hreflang="${esc(locale)}" href="${esc(canonicalBase + slug)}">`);
177
+ }
178
+ const defaultSlug = alternates[(website == null ? void 0 : website.default_locale) ?? "en"] ?? url;
179
+ parts.push(`<link rel="alternate" hreflang="x-default" href="${esc(canonicalBase + defaultSlug)}">`);
180
+ }
181
+ if (meta.noindex) {
182
+ parts.push('<meta name="robots" content="noindex, nofollow">');
183
+ }
184
+ if (stateScript) {
185
+ parts.push(stateScript);
186
+ }
187
+ return parts.join("\n");
188
+ }
189
+ function buildSiteHead(website) {
190
+ if (!website) return "";
191
+ const parts = [];
192
+ const meta = website.meta ?? {};
193
+ if (website.favicon_url) {
194
+ parts.push(`<link rel="icon" href="${esc(website.favicon_url)}">`);
195
+ }
196
+ if (meta.webclip) {
197
+ parts.push(`<link rel="apple-touch-icon" sizes="180x180" href="${esc(meta.webclip)}">`);
198
+ }
199
+ if (meta.verification_google) {
200
+ parts.push(`<meta name="google-site-verification" content="${esc(meta.verification_google)}">`);
201
+ }
202
+ if (meta.verification_bing) {
203
+ parts.push(`<meta name="msvalidate.01" content="${esc(meta.verification_bing)}">`);
204
+ }
205
+ if (meta.structured_data_org) {
206
+ try {
207
+ const json = typeof meta.structured_data_org === "string" ? meta.structured_data_org : JSON.stringify(meta.structured_data_org);
208
+ parts.push(`<script type="application/ld+json">${json}${"<"}/script>`);
209
+ } catch {
210
+ }
211
+ }
212
+ const plausibleId = website.plausible_site_id;
213
+ if (plausibleId) {
214
+ parts.push(`<script defer data-domain="${esc(plausibleId)}" src="https://plausible.io/js/script.js"><${"/"}script>`);
215
+ }
216
+ return parts.join("\n");
217
+ }
218
+ function generateSitemapXml(options) {
219
+ const { renderedPaths, canonicalBase, defaultChangefreq = "weekly", defaultPriority = "0.7" } = options;
220
+ const base = canonicalBase.replace(/\/$/, "");
221
+ const urls = renderedPaths.map((p) => {
222
+ const priority = p === "/" ? "1.0" : defaultPriority;
223
+ const changefreq = p === "/" ? "daily" : defaultChangefreq;
224
+ return ` <url>
225
+ <loc>${esc(base + p)}</loc>
226
+ <changefreq>${changefreq}</changefreq>
227
+ <priority>${priority}</priority>
228
+ </url>`;
229
+ });
230
+ return `<?xml version="1.0" encoding="UTF-8"?>
231
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
232
+ ${urls.join("\n")}
233
+ </urlset>
234
+ `;
235
+ }
236
+ function generateRobotsTxt(options) {
237
+ const { robotsTxt, canonicalBase, noindex } = options;
238
+ if (noindex) {
239
+ return `User-agent: *
240
+ Disallow: /
241
+ `;
242
+ }
243
+ const base = (canonicalBase || "").replace(/\/$/, "");
244
+ const sitemapLine = base ? `
245
+ Sitemap: ${base}/sitemap.xml
246
+ ` : "";
247
+ if (robotsTxt) {
248
+ if (sitemapLine && !robotsTxt.toLowerCase().includes("sitemap:")) {
249
+ return robotsTxt.trimEnd() + "\n" + sitemapLine;
250
+ }
251
+ return robotsTxt;
252
+ }
253
+ return `User-agent: *
254
+ Allow: /
255
+ ${sitemapLine}`;
256
+ }
257
+ function generateNetlifyRedirects(redirects) {
258
+ if (redirects.length === 0) return "";
259
+ return redirects.map((r) => `${r.from_path} ${r.to_path} ${r.status_code}`).join("\n") + "\n";
260
+ }
261
+ function stripTemplateSeoTags(template) {
262
+ return template.replace(/<title>[^<]*<\/title>/gi, "<!--ssg:title-->").replace(/<meta\s[^>]*name\s*=\s*["']description["'][^>]*>/gi, "").replace(/<meta\s[^>]*property\s*=\s*["']og:[^"']*["'][^>]*>/gi, "").replace(/<meta\s[^>]*(?:property|name)\s*=\s*["']twitter:[^"']*["'][^>]*>/gi, "").replace(/<link\s[^>]*rel\s*=\s*["']apple-touch-icon["'][^>]*>/gi, "").replace(/<link\s[^>]*rel\s*=\s*["']icon["'][^>]*>/gi, "").replace(/\n\s*\n\s*\n/g, "\n\n");
263
+ }
264
+ function esc(s) {
265
+ return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
266
+ }
112
267
  async function prerender({
113
268
  root = process.cwd(),
114
269
  clientOutDir = "dist/client",
@@ -118,13 +273,21 @@ async function prerender({
118
273
  excludePaths = [],
119
274
  failFast = false,
120
275
  maxPages = 5e3,
121
- mode = "dir"
276
+ mode = "dir",
277
+ website = null,
278
+ redirects = []
122
279
  } = {}) {
123
280
  const absRoot = path.resolve(root);
124
281
  const absClient = path.resolve(absRoot, clientOutDir);
125
282
  const absServerEntry = path.resolve(absRoot, serverEntry);
126
283
  const templatePath = path.join(absClient, "index.html");
127
- const template = await fs.readFile(templatePath, "utf8");
284
+ const rawTemplate = await fs.readFile(templatePath, "utf8");
285
+ let template = stripTemplateSeoTags(rawTemplate);
286
+ const siteHead = buildSiteHead(website);
287
+ if (siteHead) {
288
+ template = template.replace("</head>", `${siteHead}
289
+ </head>`);
290
+ }
128
291
  const serverMod = await import(node_url.pathToFileURL(absServerEntry).href);
129
292
  if (typeof serverMod.render !== "function") {
130
293
  throw new TypeError(
@@ -153,7 +316,8 @@ async function prerender({
153
316
  try {
154
317
  const { html, head = "", htmlAttrs = "" } = await serverMod.render(urlPath, {
155
318
  manifest,
156
- template
319
+ template,
320
+ website
157
321
  });
158
322
  const outHtml = injectIntoTemplate(template, head, html, fontPreloads, htmlAttrs);
159
323
  const outfile = outFilePath(absClient, urlPath, mode);
@@ -171,6 +335,31 @@ async function prerender({
171
335
  `, err.stack ?? err);
172
336
  }
173
337
  }
338
+ const websiteMeta = (website == null ? void 0 : website.meta) ?? {};
339
+ const canonicalBase = (websiteMeta.canonical_base_url || (website == null ? void 0 : website.domain) || "").replace(/\/$/, "");
340
+ if (canonicalBase && rendered.length > 0) {
341
+ const sitemap = generateSitemapXml({ renderedPaths: rendered, canonicalBase });
342
+ await fs.writeFile(path.join(absClient, "sitemap.xml"), sitemap, "utf8");
343
+ console.log(` Generated sitemap.xml (${rendered.length} URLs)`);
344
+ }
345
+ const robotsTxt = generateRobotsTxt({
346
+ robotsTxt: website == null ? void 0 : website.robots_txt,
347
+ canonicalBase,
348
+ noindex: websiteMeta.noindex
349
+ });
350
+ await fs.writeFile(path.join(absClient, "robots.txt"), robotsTxt, "utf8");
351
+ console.log(" Generated robots.txt");
352
+ if (redirects.length > 0) {
353
+ const redirectsContent = generateNetlifyRedirects(redirects);
354
+ const redirectsPath = path.join(absClient, "_redirects");
355
+ let existing = "";
356
+ try {
357
+ existing = await fs.readFile(redirectsPath, "utf8");
358
+ } catch {
359
+ }
360
+ await fs.writeFile(redirectsPath, redirectsContent + existing, "utf8");
361
+ console.log(` Generated _redirects (${redirects.length} rules)`);
362
+ }
174
363
  return {
175
364
  rendered,
176
365
  failures,
@@ -256,6 +445,9 @@ function injectIntoTemplate(template, head, appHtml, fontPreloads, htmlAttrs = "
256
445
  </head>`);
257
446
  }
258
447
  if (head) {
448
+ if (/<title>/.test(head)) {
449
+ out = out.replace("<!--ssg:title-->", "");
450
+ }
259
451
  const descTagRe = /<meta\s[^>]*name\s*=\s*["']description["'][^>]*>/gi;
260
452
  const descMatches = [...head.matchAll(descTagRe)];
261
453
  if (descMatches.length > 1) {
@@ -298,6 +490,12 @@ function discoverInternalLinks(html) {
298
490
  }
299
491
  return [...out];
300
492
  }
493
+ exports.buildPageHead = buildPageHead;
494
+ exports.buildSiteHead = buildSiteHead;
301
495
  exports.fetchCmsPrerenderPaths = fetchCmsPrerenderPaths;
496
+ exports.fetchCmsSiteData = fetchCmsSiteData;
497
+ exports.generateNetlifyRedirects = generateNetlifyRedirects;
498
+ exports.generateRobotsTxt = generateRobotsTxt;
499
+ exports.generateSitemapXml = generateSitemapXml;
302
500
  exports.polyfillBloxSsgGlobals = polyfillBloxSsgGlobals;
303
501
  exports.prerender = prerender;
package/dist/ssg/cli.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
  "use strict";
3
3
  const process = require("node:process");
4
- const prerender = require("../prerender-6jE_obPj.cjs");
4
+ const prerender = require("../prerender-Br-vA3A0.cjs");
5
5
  async function resolveApiBase(mode) {
6
6
  if (process.env.BAGELINK_API_URL != null && process.env.BAGELINK_API_URL !== "") {
7
7
  return { apiBase: process.env.BAGELINK_API_URL };
@@ -72,11 +72,17 @@ Environment:
72
72
  const websiteName = process.env.WEBSITE_NAME ?? configWebsiteName ?? "default";
73
73
  console.log(`Fetching routes from ${apiBase} (mode: ${mode}, site: ${websiteName})…`);
74
74
  let paths = [];
75
+ let website = null;
76
+ let redirects = [];
75
77
  try {
76
- paths = await prerender.fetchCmsPrerenderPaths(apiBase, websiteName);
78
+ const siteData = await prerender.fetchCmsSiteData(apiBase, websiteName);
79
+ paths = siteData.paths;
80
+ website = siteData.website;
81
+ redirects = siteData.redirects;
77
82
  console.log(` Found ${paths.length} CMS routes`);
83
+ if (redirects.length > 0) console.log(` Found ${redirects.length} redirects`);
78
84
  } catch (err) {
79
- console.error(" Failed to fetch CMS routes:", err.message);
85
+ console.error(" Failed to fetch CMS data:", err.message);
80
86
  paths = ["/"];
81
87
  }
82
88
  for (const p of extraPaths) {
@@ -92,7 +98,9 @@ Environment:
92
98
  crawl,
93
99
  excludePaths,
94
100
  maxPages: 5e3,
95
- mode: "file"
101
+ mode: "file",
102
+ website,
103
+ redirects
96
104
  });
97
105
  const elapsed = ((Date.now() - startTime) / 1e3).toFixed(2);
98
106
  console.log(`
package/dist/ssg/cli.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bun
2
2
  import process from "node:process";
3
- import { p as polyfillBloxSsgGlobals, f as fetchCmsPrerenderPaths, a as prerender } from "../prerender-DYmDaqcz.js";
3
+ import { p as polyfillBloxSsgGlobals, a as fetchCmsSiteData, c as prerender } from "../prerender-1I_qD2kp.js";
4
4
  async function resolveApiBase(mode) {
5
5
  if (process.env.BAGELINK_API_URL != null && process.env.BAGELINK_API_URL !== "") {
6
6
  return { apiBase: process.env.BAGELINK_API_URL };
@@ -71,11 +71,17 @@ Environment:
71
71
  const websiteName = process.env.WEBSITE_NAME ?? configWebsiteName ?? "default";
72
72
  console.log(`Fetching routes from ${apiBase} (mode: ${mode}, site: ${websiteName})…`);
73
73
  let paths = [];
74
+ let website = null;
75
+ let redirects = [];
74
76
  try {
75
- paths = await fetchCmsPrerenderPaths(apiBase, websiteName);
77
+ const siteData = await fetchCmsSiteData(apiBase, websiteName);
78
+ paths = siteData.paths;
79
+ website = siteData.website;
80
+ redirects = siteData.redirects;
76
81
  console.log(` Found ${paths.length} CMS routes`);
82
+ if (redirects.length > 0) console.log(` Found ${redirects.length} redirects`);
77
83
  } catch (err) {
78
- console.error(" Failed to fetch CMS routes:", err.message);
84
+ console.error(" Failed to fetch CMS data:", err.message);
79
85
  paths = ["/"];
80
86
  }
81
87
  for (const p of extraPaths) {
@@ -91,7 +97,9 @@ Environment:
91
97
  crawl,
92
98
  excludePaths,
93
99
  maxPages: 5e3,
94
- mode: "file"
100
+ mode: "file",
101
+ website,
102
+ redirects
95
103
  });
96
104
  const elapsed = ((Date.now() - startTime) / 1e3).toFixed(2);
97
105
  console.log(`
@@ -1,8 +1,18 @@
1
+ import { WebsiteRead } from '../api/types';
2
+ import { RedirectEntry } from './seo';
3
+ export interface CmsSiteData {
4
+ websiteId: string;
5
+ website: WebsiteRead;
6
+ paths: string[];
7
+ redirects: RedirectEntry[];
8
+ }
1
9
  /**
2
- * Collect prerender paths from the Bagelink CMS.
3
- *
4
- * Static slugs are returned as-is. Template pages (e.g. `/blog/:slug`) are
5
- * expanded into concrete paths using their `data_bindings` + the datastore.
10
+ * Fetch everything the SSG needs from the CMS in one go:
11
+ * website settings, page routes, and redirects.
12
+ */
13
+ export declare function fetchCmsSiteData(apiBase: string, websiteName: string): Promise<CmsSiteData>;
14
+ /**
15
+ * Legacy compat: fetch only prerender paths (no website/redirect data).
6
16
  */
7
17
  export declare function fetchCmsPrerenderPaths(apiBase: string, websiteName: string): Promise<string[]>;
8
18
  //# sourceMappingURL=cms-routes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cms-routes.d.ts","sourceRoot":"","sources":["../../src/ssg/cms-routes.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC3C,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,EAAE,CAAC,CAqDnB"}
1
+ {"version":3,"file":"cms-routes.d.ts","sourceRoot":"","sources":["../../src/ssg/cms-routes.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAY1C,MAAM,WAAW,WAAW;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,WAAW,CAAA;IACpB,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,SAAS,EAAE,aAAa,EAAE,CAAA;CAC1B;AAMD;;;GAGG;AACH,wBAAsB,gBAAgB,CACrC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,GACjB,OAAO,CAAC,WAAW,CAAC,CAqCtB;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAC3C,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,EAAE,CAAC,CAGnB"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const prerender = require("../prerender-6jE_obPj.cjs");
3
+ const prerender = require("../prerender-Br-vA3A0.cjs");
4
4
  const constants = require("../constants-fZvybj0k.cjs");
5
5
  const ssg_client = require("./client.cjs");
6
6
  async function renderBloxSsgPage(options) {
@@ -9,7 +9,8 @@ async function renderBloxSsgPage(options) {
9
9
  resolvedData,
10
10
  renderToString,
11
11
  createAppForUrl,
12
- stateWindowKey = constants.BLOX_STATE_WINDOW_KEY
12
+ stateWindowKey = constants.BLOX_STATE_WINDOW_KEY,
13
+ website = null
13
14
  } = options;
14
15
  const g = globalThis;
15
16
  const prevState = g[stateWindowKey];
@@ -21,8 +22,16 @@ async function renderBloxSsgPage(options) {
21
22
  await router.push(url);
22
23
  await router.isReady();
23
24
  const html = await renderToString(app);
24
- const head = resolvedData != null ? `<script>window[${JSON.stringify(stateWindowKey)}]=${JSON.stringify({ [url]: resolvedData })};${"<"}/script>` : "";
25
- return { html, head };
25
+ const stateScript = resolvedData != null ? `<script>window[${JSON.stringify(stateWindowKey)}]=${JSON.stringify({ [url]: resolvedData })};${"<"}/script>` : "";
26
+ const head = prerender.buildPageHead({
27
+ url,
28
+ resolvedData,
29
+ website,
30
+ stateScript
31
+ });
32
+ const lang = (website == null ? void 0 : website.default_locale) || "en";
33
+ const htmlAttrs = `lang="${lang}"`;
34
+ return { html, head, htmlAttrs };
26
35
  } finally {
27
36
  if (prevState !== void 0) {
28
37
  g[stateWindowKey] = prevState;
@@ -31,7 +40,13 @@ async function renderBloxSsgPage(options) {
31
40
  }
32
41
  }
33
42
  }
43
+ exports.buildPageHead = prerender.buildPageHead;
44
+ exports.buildSiteHead = prerender.buildSiteHead;
34
45
  exports.fetchCmsPrerenderPaths = prerender.fetchCmsPrerenderPaths;
46
+ exports.fetchCmsSiteData = prerender.fetchCmsSiteData;
47
+ exports.generateNetlifyRedirects = prerender.generateNetlifyRedirects;
48
+ exports.generateRobotsTxt = prerender.generateRobotsTxt;
49
+ exports.generateSitemapXml = prerender.generateSitemapXml;
35
50
  exports.polyfillBloxSsgGlobals = prerender.polyfillBloxSsgGlobals;
36
51
  exports.prerender = prerender.prerender;
37
52
  exports.BLOX_STATE_WINDOW_KEY = constants.BLOX_STATE_WINDOW_KEY;
@@ -1,13 +1,15 @@
1
- export { fetchCmsPrerenderPaths } from './cms-routes';
1
+ export { fetchCmsPrerenderPaths, fetchCmsSiteData } from './cms-routes';
2
+ export type { CmsSiteData } from './cms-routes';
2
3
  /**
3
4
  * @bagelink/blox/ssg — Static Site Generation helpers for Blox CMS sites.
4
5
  *
5
6
  * Provides everything needed to prerender a Vue + Blox app at build time:
6
7
  * - Browser global polyfills for Node.js
7
8
  * - CMS route discovery (including template page expansion via data_bindings)
8
- * - Double-render page renderer with state embedding
9
+ * - Page renderer with state embedding and full SEO head
9
10
  * - Client-side fetch interceptor for zero-API-call hydration
10
11
  * - Full prerender orchestrator with crawl support
12
+ * - SEO asset generation (sitemap.xml, robots.txt, _redirects)
11
13
  */
12
14
  export { BLOX_STATE_WINDOW_KEY } from './constants';
13
15
  export { polyfillBloxSsgGlobals } from './polyfill-node';
@@ -15,5 +17,7 @@ export { prerender } from './prerender';
15
17
  export type { PrerenderOptions, PrerenderResult, RenderContext, RenderResult } from './prerender';
16
18
  export { renderBloxSsgPage } from './render-resolved-page';
17
19
  export type { BloxSsgRouterLike } from './render-resolved-page';
20
+ export { buildPageHead, buildSiteHead, generateNetlifyRedirects, generateRobotsTxt, generateSitemapXml } from './seo';
21
+ export type { RedirectEntry, SeoPageData, WebsiteMeta } from './seo';
18
22
  export { installBloxStateCache } from './state-cache';
19
23
  //# 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,sBAAsB,EAAE,MAAM,cAAc,CAAA;AACrD;;;;;;;;;GASG;AACH,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACnD,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,qBAAqB,EAAE,MAAM,eAAe,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ssg/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AACvE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C;;;;;;;;;;GAUG;AACH,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACnD,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,kBAAkB,EAAE,MAAM,OAAO,CAAA;AACrH,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA"}
@@ -1,4 +1,5 @@
1
- import { f, p, a } from "../prerender-DYmDaqcz.js";
1
+ import { b as buildPageHead } from "../prerender-1I_qD2kp.js";
2
+ import { d, f, a, g, e, h, p, c } from "../prerender-1I_qD2kp.js";
2
3
  import { B as BLOX_STATE_WINDOW_KEY } from "../constants-BIbQhd3z.js";
3
4
  import { installBloxStateCache } from "./client.mjs";
4
5
  async function renderBloxSsgPage(options) {
@@ -7,33 +8,48 @@ async function renderBloxSsgPage(options) {
7
8
  resolvedData,
8
9
  renderToString,
9
10
  createAppForUrl,
10
- stateWindowKey = BLOX_STATE_WINDOW_KEY
11
+ stateWindowKey = BLOX_STATE_WINDOW_KEY,
12
+ website = null
11
13
  } = options;
12
- const g = globalThis;
13
- const prevState = g[stateWindowKey];
14
+ const g2 = globalThis;
15
+ const prevState = g2[stateWindowKey];
14
16
  if (resolvedData != null) {
15
- g[stateWindowKey] = { [url]: resolvedData };
17
+ g2[stateWindowKey] = { [url]: resolvedData };
16
18
  }
17
19
  try {
18
20
  const { app, router } = createAppForUrl(url);
19
21
  await router.push(url);
20
22
  await router.isReady();
21
23
  const html = await renderToString(app);
22
- const head = resolvedData != null ? `<script>window[${JSON.stringify(stateWindowKey)}]=${JSON.stringify({ [url]: resolvedData })};${"<"}/script>` : "";
23
- return { html, head };
24
+ const stateScript = resolvedData != null ? `<script>window[${JSON.stringify(stateWindowKey)}]=${JSON.stringify({ [url]: resolvedData })};${"<"}/script>` : "";
25
+ const head = buildPageHead({
26
+ url,
27
+ resolvedData,
28
+ website,
29
+ stateScript
30
+ });
31
+ const lang = (website == null ? void 0 : website.default_locale) || "en";
32
+ const htmlAttrs = `lang="${lang}"`;
33
+ return { html, head, htmlAttrs };
24
34
  } finally {
25
35
  if (prevState !== void 0) {
26
- g[stateWindowKey] = prevState;
36
+ g2[stateWindowKey] = prevState;
27
37
  } else {
28
- delete g[stateWindowKey];
38
+ delete g2[stateWindowKey];
29
39
  }
30
40
  }
31
41
  }
32
42
  export {
33
43
  BLOX_STATE_WINDOW_KEY,
44
+ buildPageHead,
45
+ d as buildSiteHead,
34
46
  f as fetchCmsPrerenderPaths,
47
+ a as fetchCmsSiteData,
48
+ g as generateNetlifyRedirects,
49
+ e as generateRobotsTxt,
50
+ h as generateSitemapXml,
35
51
  installBloxStateCache,
36
52
  p as polyfillBloxSsgGlobals,
37
- a as prerender,
53
+ c as prerender,
38
54
  renderBloxSsgPage
39
55
  };
@@ -1,3 +1,5 @@
1
+ import { WebsiteRead } from '../api/types';
2
+ import { RedirectEntry } from './seo';
1
3
  type ExcludeRule = string | RegExp | ((path: string) => boolean);
2
4
  export interface PrerenderOptions {
3
5
  root?: string;
@@ -9,10 +11,16 @@ export interface PrerenderOptions {
9
11
  failFast?: boolean;
10
12
  maxPages?: number;
11
13
  mode?: 'dir' | 'file';
14
+ /** Website settings for SEO injection. */
15
+ website?: WebsiteRead | null;
16
+ /** Redirects to write as _redirects (Netlify). */
17
+ redirects?: RedirectEntry[];
12
18
  }
13
19
  export interface RenderContext {
14
20
  manifest: Record<string, string[]> | null;
15
21
  template: string;
22
+ /** Website settings passed through to the render function. */
23
+ website?: WebsiteRead | null;
16
24
  }
17
25
  export interface RenderResult {
18
26
  html: string;
@@ -38,7 +46,9 @@ export interface PrerenderResult {
38
46
  * - "crawl" (default true) discovers internal links in rendered HTML and queues them.
39
47
  * - excludePaths can be strings, RegExp, or a predicate function.
40
48
  * - Route-level failures are logged and skipped by default.
49
+ * - Generates sitemap.xml, robots.txt, and _redirects post-render.
50
+ * - Strips hardcoded SEO tags from template and injects per-page equivalents.
41
51
  */
42
- export declare function prerender({ root, clientOutDir, serverEntry, paths, crawl, excludePaths, failFast, maxPages, mode, }?: PrerenderOptions): Promise<PrerenderResult>;
52
+ export declare function prerender({ root, clientOutDir, serverEntry, paths, crawl, excludePaths, failFast, maxPages, mode, website, redirects, }?: PrerenderOptions): Promise<PrerenderResult>;
43
53
  export {};
44
54
  //# sourceMappingURL=prerender.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prerender.d.ts","sourceRoot":"","sources":["../../src/ssg/prerender.ts"],"names":[],"mappings":"AAKA,KAAK,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,CAAA;AAEhE,MAAM,WAAW,gBAAgB;IAChC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,YAAY,CAAC,EAAE,WAAW,GAAG,WAAW,EAAE,CAAA;IAC1C,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,aAAa;IAC7B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI,CAAA;IACzC,QAAQ,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,OAAO,CAAA;CACf;AAMD,MAAM,WAAW,eAAe;IAC/B,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;IAC7D,eAAe,EAAE,MAAM,CAAA;IACvB,eAAe,EAAE,MAAM,CAAA;CACvB;AAED;;;;;;;;;GASG;AACH,wBAAsB,SAAS,CAAC,EAC/B,IAAoB,EACpB,YAA4B,EAC5B,WAA0C,EAC1C,KAAU,EACV,KAAY,EACZ,YAAiB,EACjB,QAAgB,EAChB,QAAe,EACf,IAAY,GACZ,GAAE,gBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC,CA6ElD"}
1
+ {"version":3,"file":"prerender.d.ts","sourceRoot":"","sources":["../../src/ssg/prerender.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAS1C,KAAK,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,CAAA;AAEhE,MAAM,WAAW,gBAAgB;IAChC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,YAAY,CAAC,EAAE,WAAW,GAAG,WAAW,EAAE,CAAA;IAC1C,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;IACrB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,WAAW,GAAG,IAAI,CAAA;IAC5B,kDAAkD;IAClD,SAAS,CAAC,EAAE,aAAa,EAAE,CAAA;CAC3B;AAED,MAAM,WAAW,aAAa;IAC7B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI,CAAA;IACzC,QAAQ,EAAE,MAAM,CAAA;IAChB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,WAAW,GAAG,IAAI,CAAA;CAC5B;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,OAAO,CAAA;CACf;AAMD,MAAM,WAAW,eAAe;IAC/B,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;IAC7D,eAAe,EAAE,MAAM,CAAA;IACvB,eAAe,EAAE,MAAM,CAAA;CACvB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,SAAS,CAAC,EAC/B,IAAoB,EACpB,YAA4B,EAC5B,WAA0C,EAC1C,KAAU,EACV,KAAY,EACZ,YAAiB,EACjB,QAAgB,EAChB,QAAe,EACf,IAAY,EACZ,OAAc,EACd,SAAc,GACd,GAAE,gBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC,CA0HlD"}
@@ -1,16 +1,19 @@
1
+ import { WebsiteRead } from '../api/types';
1
2
  export interface BloxSsgRouterLike {
2
3
  push: (url: string) => Promise<void>;
3
4
  isReady: () => Promise<void>;
4
5
  }
5
6
  /**
6
- * Render a Blox CMS page to static HTML with embedded state.
7
+ * Render a Blox CMS page to static HTML with embedded state and full SEO head.
7
8
  *
8
9
  * Sets `globalThis.__BLOX_STATE__` *before* rendering so that
9
10
  * `CmsPageView` can hydrate synchronously from the embedded data
10
11
  * instead of waiting for an async fetch.
11
12
  *
12
- * Returns `{ html, head }` where `head` contains a `<script>` that sets
13
- * `window.__BLOX_STATE__` for zero-API-call client hydration.
13
+ * Returns `{ html, head, htmlAttrs }` where:
14
+ * - `head` contains per-page `<title>`, meta tags, OG, canonical, hreflang,
15
+ * plus the `<script>` that sets `window.__BLOX_STATE__`
16
+ * - `htmlAttrs` contains the `lang` attribute
14
17
  */
15
18
  export declare function renderBloxSsgPage<TApp>(options: {
16
19
  url: string;
@@ -21,8 +24,11 @@ export declare function renderBloxSsgPage<TApp>(options: {
21
24
  router: BloxSsgRouterLike;
22
25
  };
23
26
  stateWindowKey?: string;
27
+ /** Website settings for SEO tags. If omitted, only state script is emitted. */
28
+ website?: WebsiteRead | null;
24
29
  }): Promise<{
25
30
  html: string;
26
31
  head?: string;
32
+ htmlAttrs?: string;
27
33
  }>;
28
34
  //# sourceMappingURL=render-resolved-page.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"render-resolved-page.d.ts","sourceRoot":"","sources":["../../src/ssg/render-resolved-page.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACpC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC5B;AAED;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,OAAO,EAAE;IACtD,GAAG,EAAE,MAAM,CAAA;IACX,YAAY,EAAE,OAAO,GAAG,IAAI,CAAA;IAC5B,cAAc,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IAC9C,eAAe,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK;QAAE,GAAG,EAAE,IAAI,CAAC;QAAC,MAAM,EAAE,iBAAiB,CAAA;KAAE,CAAA;IAC1E,cAAc,CAAC,EAAE,MAAM,CAAA;CACvB,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAqC3C"}
1
+ {"version":3,"file":"render-resolved-page.d.ts","sourceRoot":"","sources":["../../src/ssg/render-resolved-page.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAK/C,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACpC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC5B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,OAAO,EAAE;IACtD,GAAG,EAAE,MAAM,CAAA;IACX,YAAY,EAAE,OAAO,GAAG,IAAI,CAAA;IAC5B,cAAc,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IAC9C,eAAe,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK;QAAE,GAAG,EAAE,IAAI,CAAC;QAAC,MAAM,EAAE,iBAAiB,CAAA;KAAE,CAAA;IAC1E,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,WAAW,GAAG,IAAI,CAAA;CAC5B,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAkD/D"}
@@ -0,0 +1,66 @@
1
+ import { PageRead, WebsiteRead } from '../api/types';
2
+ /** Resolved page data shape (subset of PageResolveRead). */
3
+ export interface SeoPageData {
4
+ page: PageRead;
5
+ alternates?: Record<string, string>;
6
+ contexts?: Record<string, Record<string, unknown> | null>;
7
+ }
8
+ /** Website meta bag (freeform JSON stored on WebsiteRead.meta). */
9
+ export interface WebsiteMeta {
10
+ default_meta_title?: string;
11
+ default_meta_description?: string;
12
+ default_og_image?: string;
13
+ webclip?: string;
14
+ og_site_name?: string;
15
+ og_description?: string;
16
+ og_type?: string;
17
+ twitter_card?: string;
18
+ twitter_site?: string;
19
+ canonical_base_url?: string;
20
+ noindex?: boolean;
21
+ verification_google?: string;
22
+ verification_bing?: string;
23
+ structured_data_org?: unknown;
24
+ [key: string]: unknown;
25
+ }
26
+ export interface RedirectEntry {
27
+ from_path: string;
28
+ to_path: string;
29
+ status_code: number;
30
+ }
31
+ /**
32
+ * Build a complete `<head>` fragment for a single page.
33
+ *
34
+ * Includes: title, meta description, OG tags, Twitter Card, canonical,
35
+ * hreflang alternates, noindex, and the __BLOX_STATE__ script.
36
+ */
37
+ export declare function buildPageHead(options: {
38
+ url: string;
39
+ resolvedData: SeoPageData | null;
40
+ website: WebsiteRead | null;
41
+ stateScript?: string;
42
+ }): string;
43
+ /**
44
+ * Build site-wide `<head>` tags: verification, structured data, analytics, favicon.
45
+ * These replace hardcoded equivalents in the HTML template.
46
+ */
47
+ export declare function buildSiteHead(website: WebsiteRead | null): string;
48
+ export declare function generateSitemapXml(options: {
49
+ renderedPaths: string[];
50
+ canonicalBase: string;
51
+ defaultChangefreq?: string;
52
+ defaultPriority?: string;
53
+ }): string;
54
+ export declare function generateRobotsTxt(options: {
55
+ robotsTxt?: string | null;
56
+ canonicalBase?: string;
57
+ noindex?: boolean;
58
+ }): string;
59
+ export declare function generateNetlifyRedirects(redirects: RedirectEntry[]): string;
60
+ /**
61
+ * Strip hardcoded SEO tags from the HTML template so SSG can inject
62
+ * per-page equivalents. Removes: title, meta description, og:*, twitter:*,
63
+ * apple-touch-icon, and favicon.
64
+ */
65
+ export declare function stripTemplateSeoTags(template: string): string;
66
+ //# sourceMappingURL=seo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seo.d.ts","sourceRoot":"","sources":["../../src/ssg/seo.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAMzD,4DAA4D;AAC5D,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,QAAQ,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAA;CACzD;AAED,mEAAmE;AACnE,MAAM,WAAW,WAAW;IAC3B,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,wBAAwB,CAAC,EAAE,MAAM,CAAA;IACjC,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACtB;AAED,MAAM,WAAW,aAAa;IAC7B,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;CACnB;AAMD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE;IACtC,GAAG,EAAE,MAAM,CAAA;IACX,YAAY,EAAE,WAAW,GAAG,IAAI,CAAA;IAChC,OAAO,EAAE,WAAW,GAAG,IAAI,CAAA;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAA;CACpB,GAAG,MAAM,CAsET;AAMD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,GAAG,MAAM,CA4CjE;AAMD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAC3C,aAAa,EAAE,MAAM,EAAE,CAAA;IACvB,aAAa,EAAE,MAAM,CAAA;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAA;CACxB,GAAG,MAAM,CAmBT;AAMD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE;IAC1C,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,OAAO,CAAC,EAAE,OAAO,CAAA;CACjB,GAAG,MAAM,CAmBT;AAMD,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,aAAa,EAAE,GAAG,MAAM,CAK3E;AAMD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAgB7D"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bagelink/blox",
3
3
  "type": "module",
4
- "version": "1.12.21",
4
+ "version": "1.13.0",
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",