@bagelink/blox 1.15.213 → 1.15.218

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.
Files changed (44) hide show
  1. package/dist/PageRenderer.d.ts.map +1 -1
  2. package/dist/{PreviewApp-APyemzqx.cjs → PreviewApp-C2Cn1yH2.cjs} +1 -1
  3. package/dist/{PreviewApp-BKTydm92.js → PreviewApp-CNIVF6Xq.js} +1 -1
  4. package/dist/{PreviewApp.vue_vue_type_style_index_0_lang-C0ZVst7R.cjs → PreviewApp.vue_vue_type_style_index_0_lang-kv9T9yLc.cjs} +20 -12
  5. package/dist/{PreviewApp.vue_vue_type_style_index_0_lang-DHb-NJ4C.js → PreviewApp.vue_vue_type_style_index_0_lang-lotrZDHC.js} +20 -12
  6. package/dist/PreviewRenderer.d.ts.map +1 -1
  7. package/dist/api/types.d.ts +33 -2
  8. package/dist/api/types.d.ts.map +1 -1
  9. package/dist/bridge.d.ts +2 -0
  10. package/dist/bridge.d.ts.map +1 -1
  11. package/dist/composables/useGlobalBlock.d.ts +14 -21
  12. package/dist/composables/useGlobalBlock.d.ts.map +1 -1
  13. package/dist/core.d.ts.map +1 -1
  14. package/dist/defineBlock.d.ts +8 -0
  15. package/dist/defineBlock.d.ts.map +1 -1
  16. package/dist/index.cjs +16 -20
  17. package/dist/index.d.ts +3 -5
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.mjs +45 -49
  20. package/dist/manifest.d.ts +2 -0
  21. package/dist/manifest.d.ts.map +1 -1
  22. package/dist/{prerender-BNFeuap-.cjs → prerender-BkTA1QZ6.cjs} +97 -64
  23. package/dist/{prerender-Bth9K3uz.js → prerender-wIuYNW6N.js} +97 -64
  24. package/dist/{routes-C97aL4cQ.cjs → routes-0bOKkut4.cjs} +5 -91
  25. package/dist/{routes-bhjTTXfU.js → routes-CHfoa2Mk.js} +38 -124
  26. package/dist/schema.d.ts +2 -0
  27. package/dist/schema.d.ts.map +1 -1
  28. package/dist/ssg/cli.cjs +14 -1
  29. package/dist/ssg/cli.mjs +14 -1
  30. package/dist/ssg/index.cjs +2 -2
  31. package/dist/ssg/index.d.ts +1 -1
  32. package/dist/ssg/index.d.ts.map +1 -1
  33. package/dist/ssg/index.mjs +3 -3
  34. package/dist/ssg/prerender.d.ts +5 -1
  35. package/dist/ssg/prerender.d.ts.map +1 -1
  36. package/dist/ssg/seo.d.ts +13 -27
  37. package/dist/ssg/seo.d.ts.map +1 -1
  38. package/dist/symbols.d.ts +0 -11
  39. package/dist/symbols.d.ts.map +1 -1
  40. package/package.json +1 -1
  41. package/dist/GlobalBlockHost.d.ts +0 -75
  42. package/dist/GlobalBlockHost.d.ts.map +0 -1
  43. package/dist/globalRef.d.ts +0 -9
  44. package/dist/globalRef.d.ts.map +0 -1
@@ -174,10 +174,12 @@ async function polyfillBloxSsgGlobals(options) {
174
174
  g.window.location = _win.location;
175
175
  g.window.document = _win.document;
176
176
  }
177
- function buildStructuredData(meta, locale) {
178
- const perLocale = locale ? meta[`structured_data_org_${locale}`] : void 0;
179
- const value = perLocale ?? meta.structured_data_org;
180
- if (value == null || value === "") return "";
177
+ function buildStructuredData(sd, locale) {
178
+ var _a;
179
+ if (!sd) return "";
180
+ const perLocale = locale ? (_a = sd.org_by_locale) == null ? void 0 : _a[locale] : void 0;
181
+ const value = perLocale ?? sd.org;
182
+ if (value == null || typeof value === "string" && value === "") return "";
181
183
  try {
182
184
  const json = typeof value === "string" ? value : JSON.stringify(value);
183
185
  return `<script type="application/ld+json">${json}${"<"}/script>`;
@@ -187,44 +189,46 @@ function buildStructuredData(meta, locale) {
187
189
  }
188
190
  function buildPageHead(options) {
189
191
  var _a;
190
- const { url, resolvedData, website, stateScript, locale, override } = options;
192
+ const { url, resolvedData, website, stateScript, locale, override, buildOrigin } = options;
191
193
  const parts = [];
192
- const meta = (website == null ? void 0 : website.meta) ?? {};
194
+ const seo = (website == null ? void 0 : website.seo) ?? {};
193
195
  const page = resolvedData == null ? void 0 : resolvedData.page;
194
196
  const ctx = extractPrimaryContext(resolvedData == null ? void 0 : resolvedData.contexts);
195
- const title = (override == null ? void 0 : override.title) || (page == null ? void 0 : page.meta_title) || ctx.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;
197
+ const siteName = seo.site_name || void 0;
198
+ const title = (override == null ? void 0 : override.title) || (page == null ? void 0 : page.meta_title) || ctx.title || ((page == null ? void 0 : page.title) && siteName ? `${page.title} · ${siteName}` : null) || (page == null ? void 0 : page.title) || seo.default_meta_title;
196
199
  if (title) {
197
200
  const bare = (override == null ? void 0 : override.title) || ctx.title;
198
- const fullTitle = bare && meta.og_site_name && !bare.includes(meta.og_site_name) ? `${esc(bare)} · ${esc(meta.og_site_name)}` : esc(title);
201
+ const fullTitle = bare && siteName && !bare.includes(siteName) ? `${esc(bare)} · ${esc(siteName)}` : esc(title);
199
202
  parts.push(`<title>${fullTitle}</title>`);
200
203
  }
201
- const description = (override == null ? void 0 : override.description) || (page == null ? void 0 : page.meta_description) || ctx.description || meta.default_meta_description;
204
+ const description = (override == null ? void 0 : override.description) || (page == null ? void 0 : page.meta_description) || ctx.description || seo.default_meta_description;
202
205
  if (description) {
203
206
  parts.push(`<meta name="description" content="${esc(description)}">`);
204
207
  }
205
- const canonicalBase = (meta.canonical_base_url || (website == null ? void 0 : website.domain) || "").replace(/\/$/, "");
208
+ const canonicalBase = (seo.canonical_base_url || (website == null ? void 0 : website.domain) || buildOrigin || "").replace(/\/$/, "");
206
209
  const pageCanonical = (_a = page == null ? void 0 : page.canonical_url) == null ? void 0 : _a.trim();
207
210
  if (pageCanonical) {
208
211
  parts.push(`<link rel="canonical" href="${esc(pageCanonical)}">`);
209
212
  } else if (canonicalBase) {
210
213
  parts.push(`<link rel="canonical" href="${esc(canonicalBase + url)}">`);
211
214
  }
212
- const ogTitle = (override == null ? void 0 : override.title) || (page == null ? void 0 : page.meta_title) || ctx.title || (page == null ? void 0 : page.title) || meta.og_site_name || meta.default_meta_title;
213
- const ogDesc = (override == null ? void 0 : override.description) || (page == null ? void 0 : page.meta_description) || ctx.description || meta.og_description || meta.default_meta_description;
214
- const ogImage = absUrl((override == null ? void 0 : override.image) || (page == null ? void 0 : page.og_image) || ctx.image || meta.default_og_image);
215
- const ogType = meta.og_type || "website";
215
+ const ogTitle = (override == null ? void 0 : override.title) || (page == null ? void 0 : page.meta_title) || ctx.title || (page == null ? void 0 : page.title) || siteName || seo.default_meta_title;
216
+ const ogDesc = (override == null ? void 0 : override.description) || (page == null ? void 0 : page.meta_description) || ctx.description || seo.default_meta_description;
217
+ const ogImage = absUrl((override == null ? void 0 : override.image) || (page == null ? void 0 : page.og_image) || ctx.image || seo.default_og_image);
218
+ const isDetailPage = !!((resolvedData == null ? void 0 : resolvedData.contexts) && Object.values(resolvedData.contexts).some((c) => c != null));
219
+ const ogType = isDetailPage ? "article" : "website";
216
220
  if (ogTitle) parts.push(`<meta property="og:title" content="${esc(ogTitle)}">`);
217
221
  if (ogDesc) parts.push(`<meta property="og:description" content="${esc(ogDesc)}">`);
218
222
  if (ogImage) parts.push(`<meta property="og:image" content="${esc(ogImage)}">`);
219
223
  parts.push(`<meta property="og:type" content="${esc(ogType)}">`);
220
224
  if (canonicalBase) parts.push(`<meta property="og:url" content="${esc(canonicalBase + url)}">`);
221
- if (meta.og_site_name) parts.push(`<meta property="og:site_name" content="${esc(meta.og_site_name)}">`);
222
- const twitterCard = meta.twitter_card || (ogImage ? "summary_large_image" : "summary");
225
+ if (siteName) parts.push(`<meta property="og:site_name" content="${esc(siteName)}">`);
226
+ const twitterCard = ogImage ? "summary_large_image" : "summary";
223
227
  parts.push(`<meta name="twitter:card" content="${esc(twitterCard)}">`);
224
228
  if (ogTitle) parts.push(`<meta name="twitter:title" content="${esc(ogTitle)}">`);
225
229
  if (ogDesc) parts.push(`<meta name="twitter:description" content="${esc(ogDesc)}">`);
226
230
  if (ogImage) parts.push(`<meta name="twitter:image" content="${esc(ogImage)}">`);
227
- if (meta.twitter_site) parts.push(`<meta name="twitter:site" content="${esc(meta.twitter_site)}">`);
231
+ if (seo.twitter_site) parts.push(`<meta name="twitter:site" content="${esc(seo.twitter_site)}">`);
228
232
  const alternates = (override == null ? void 0 : override.alternates) && Object.keys(override.alternates).length ? override.alternates : resolvedData == null ? void 0 : resolvedData.alternates;
229
233
  const defaultLocale = (website == null ? void 0 : website.default_locale) ?? "en";
230
234
  if (alternates && canonicalBase) {
@@ -235,10 +239,10 @@ function buildPageHead(options) {
235
239
  const defaultSlug = alternates[defaultLocale] ?? url;
236
240
  parts.push(`<link rel="alternate" hreflang="x-default" href="${esc(canonicalBase + defaultSlug)}">`);
237
241
  }
238
- if (meta.noindex || (page == null ? void 0 : page.noindex)) {
242
+ if (seo.noindex || (page == null ? void 0 : page.noindex)) {
239
243
  parts.push('<meta name="robots" content="noindex, nofollow">');
240
244
  }
241
- const structuredData = buildStructuredData(meta, locale || (website == null ? void 0 : website.default_locale));
245
+ const structuredData = buildStructuredData(website == null ? void 0 : website.structured_data, locale || (website == null ? void 0 : website.default_locale));
242
246
  if (structuredData) parts.push(structuredData);
243
247
  if (stateScript) {
244
248
  parts.push(stateScript);
@@ -246,24 +250,25 @@ function buildPageHead(options) {
246
250
  return parts.join("\n");
247
251
  }
248
252
  function buildSiteHead(website) {
253
+ var _a, _b, _c;
249
254
  if (!website) return "";
250
255
  const parts = [];
251
- const meta = website.meta ?? {};
252
- const favicon = absUrl(website.favicon_url);
256
+ const seo = website.seo ?? {};
257
+ const favicon = absUrl(seo.favicon_url);
253
258
  if (favicon) {
254
259
  parts.push(`<link rel="icon" href="${esc(favicon)}">`);
255
260
  }
256
- const webclip = absUrl(meta.webclip);
261
+ const webclip = absUrl(seo.webclip);
257
262
  if (webclip) {
258
263
  parts.push(`<link rel="apple-touch-icon" sizes="180x180" href="${esc(webclip)}">`);
259
264
  }
260
- if (meta.verification_google) {
261
- parts.push(`<meta name="google-site-verification" content="${esc(meta.verification_google)}">`);
265
+ if ((_a = website.verification) == null ? void 0 : _a.google) {
266
+ parts.push(`<meta name="google-site-verification" content="${esc(website.verification.google)}">`);
262
267
  }
263
- if (meta.verification_bing) {
264
- parts.push(`<meta name="msvalidate.01" content="${esc(meta.verification_bing)}">`);
268
+ if ((_b = website.verification) == null ? void 0 : _b.bing) {
269
+ parts.push(`<meta name="msvalidate.01" content="${esc(website.verification.bing)}">`);
265
270
  }
266
- const plausibleId = website.plausible_site_id;
271
+ const plausibleId = (_c = website.integrations) == null ? void 0 : _c.plausible_site_id;
267
272
  if (plausibleId) {
268
273
  const host = "https://analytics.bagel.to";
269
274
  parts.push(
@@ -273,13 +278,16 @@ function buildSiteHead(website) {
273
278
  return parts.join("\n");
274
279
  }
275
280
  function generateSitemapXml(options) {
276
- const { renderedPaths, canonicalBase, defaultChangefreq = "weekly", defaultPriority = "0.7" } = options;
281
+ const { renderedPaths, canonicalBase, defaultChangefreq = "weekly", defaultPriority = "0.7", lastmod = {} } = options;
277
282
  const base = canonicalBase.replace(/\/$/, "");
278
283
  const urls = renderedPaths.map((p) => {
279
284
  const priority = p === "/" ? "1.0" : defaultPriority;
280
285
  const changefreq = p === "/" ? "daily" : defaultChangefreq;
286
+ const mod = lastmod[p];
287
+ const modLine = mod ? `
288
+ <lastmod>${esc(mod)}</lastmod>` : "";
281
289
  return ` <url>
282
- <loc>${esc(base + p)}</loc>
290
+ <loc>${esc(base + p)}</loc>${modLine}
283
291
  <changefreq>${changefreq}</changefreq>
284
292
  <priority>${priority}</priority>
285
293
  </url>`;
@@ -329,10 +337,10 @@ function stripTemplateSeoTags(template, options = {}) {
329
337
  return out.replace(/\n\s*\n\s*\n/g, "\n\n");
330
338
  }
331
339
  function getSiteIconFlags(website) {
332
- const meta = (website == null ? void 0 : website.meta) ?? {};
340
+ const seo = (website == null ? void 0 : website.seo) ?? {};
333
341
  return {
334
- hasFavicon: !!absUrl(website == null ? void 0 : website.favicon_url),
335
- hasWebclip: !!absUrl(meta.webclip)
342
+ hasFavicon: !!absUrl(seo.favicon_url),
343
+ hasWebclip: !!absUrl(seo.webclip)
336
344
  };
337
345
  }
338
346
  function extractPrimaryContext(contexts) {
@@ -370,12 +378,14 @@ async function prerender({
370
378
  excludePaths = [],
371
379
  failFast = false,
372
380
  maxPages = 5e3,
381
+ concurrency = 6,
373
382
  mode = "dir",
374
383
  website = null,
375
384
  websiteId = "",
376
385
  redirects = [],
377
386
  collections = {}
378
387
  } = {}) {
388
+ var _a, _b;
379
389
  const absRoot = path.resolve(root);
380
390
  const absClient = path.resolve(absRoot, clientOutDir);
381
391
  const absServerEntry = path.resolve(absRoot, serverEntry);
@@ -407,44 +417,67 @@ async function prerender({
407
417
  const fontPreloads = await discoverFontPreloads(absClient);
408
418
  const rendered = [];
409
419
  const noindexPaths = /* @__PURE__ */ new Set();
420
+ const lastmod = {};
410
421
  const failures = [];
411
- while (queue.length) {
412
- if (rendered.length >= maxPages) break;
413
- const urlPath = queue.shift();
414
- if (urlPath == null || urlPath === "") continue;
415
- if (isExcluded(urlPath)) continue;
416
- try {
417
- const { html, head = "", htmlAttrs = "" } = await serverMod.render(urlPath, {
418
- manifest,
419
- template,
420
- website,
421
- websiteId,
422
- collections
423
- });
424
- const outHtml = injectIntoTemplate(template, head, html, fontPreloads, htmlAttrs);
425
- const outfile = outFilePath(absClient, urlPath, mode);
426
- await fs.mkdir(path.dirname(outfile), { recursive: true });
427
- await fs.writeFile(outfile, outHtml, "utf8");
428
- rendered.push(urlPath);
429
- if (/<meta[^>]+name=["']robots["'][^>]*noindex/i.test(head)) {
430
- noindexPaths.add(urlPath);
431
- }
432
- if (crawl) {
433
- const discovered = discoverInternalLinks(outHtml);
434
- for (const p of discovered) enqueue(p, queue, seen, isExcluded);
422
+ async function renderOne(urlPath) {
423
+ const {
424
+ html,
425
+ head = "",
426
+ htmlAttrs = "",
427
+ lastmod: pageLastmod
428
+ } = await serverMod.render(urlPath, {
429
+ manifest,
430
+ template,
431
+ website,
432
+ websiteId,
433
+ collections
434
+ });
435
+ const outHtml = injectIntoTemplate(template, head, html, fontPreloads, htmlAttrs);
436
+ const outfile = outFilePath(absClient, urlPath, mode);
437
+ await fs.mkdir(path.dirname(outfile), { recursive: true });
438
+ await fs.writeFile(outfile, outHtml, "utf8");
439
+ rendered.push(urlPath);
440
+ if (pageLastmod) lastmod[urlPath] = pageLastmod;
441
+ if (/<meta[^>]+name=["']robots["'][^>]*noindex/i.test(head)) {
442
+ noindexPaths.add(urlPath);
443
+ }
444
+ if (crawl) {
445
+ for (const p of discoverInternalLinks(outHtml)) {
446
+ enqueue(p, queue, seen, isExcluded);
435
447
  }
436
- } catch (err) {
437
- failures.push({ path: urlPath, error: err });
438
- if (failFast) throw err;
439
- console.warn(`[prerender] failed: ${urlPath}
448
+ }
449
+ }
450
+ let stopped = false;
451
+ async function worker() {
452
+ while (!stopped) {
453
+ if (rendered.length >= maxPages) break;
454
+ const urlPath = queue.shift();
455
+ if (urlPath === void 0) break;
456
+ if (urlPath === "" || isExcluded(urlPath)) continue;
457
+ try {
458
+ await renderOne(urlPath);
459
+ } catch (err) {
460
+ failures.push({ path: urlPath, error: err });
461
+ if (failFast) {
462
+ stopped = true;
463
+ throw err;
464
+ }
465
+ console.warn(`[prerender] failed: ${urlPath}
440
466
  `, err.stack ?? err);
467
+ }
441
468
  }
442
469
  }
443
- const websiteMeta = (website == null ? void 0 : website.meta) ?? {};
444
- const canonicalBase = (websiteMeta.canonical_base_url || (website == null ? void 0 : website.domain) || "").replace(/\/$/, "");
470
+ while (queue.length && !stopped && rendered.length < maxPages) {
471
+ const workers = Array.from(
472
+ { length: Math.min(concurrency, queue.length) },
473
+ () => worker()
474
+ );
475
+ await Promise.all(workers);
476
+ }
477
+ const canonicalBase = (((_a = website == null ? void 0 : website.seo) == null ? void 0 : _a.canonical_base_url) || (website == null ? void 0 : website.domain) || "").replace(/\/$/, "");
445
478
  const sitemapPaths = rendered.filter((p) => !noindexPaths.has(p));
446
479
  if (canonicalBase && sitemapPaths.length > 0) {
447
- const sitemap = generateSitemapXml({ renderedPaths: sitemapPaths, canonicalBase });
480
+ const sitemap = generateSitemapXml({ renderedPaths: sitemapPaths, canonicalBase, lastmod });
448
481
  await fs.writeFile(path.join(absClient, "sitemap.xml"), sitemap, "utf8");
449
482
  const skipped = noindexPaths.size > 0 ? ` (${noindexPaths.size} noindex excluded)` : "";
450
483
  console.log(` Generated sitemap.xml (${sitemapPaths.length} URLs)${skipped}`);
@@ -452,7 +485,7 @@ async function prerender({
452
485
  const robotsTxt = generateRobotsTxt({
453
486
  robotsTxt: website == null ? void 0 : website.robots_txt,
454
487
  canonicalBase,
455
- noindex: websiteMeta.noindex
488
+ noindex: (_b = website == null ? void 0 : website.seo) == null ? void 0 : _b.noindex
456
489
  });
457
490
  await fs.writeFile(path.join(absClient, "robots.txt"), robotsTxt, "utf8");
458
491
  console.log(" Generated robots.txt");
@@ -151,10 +151,12 @@ async function polyfillBloxSsgGlobals(options) {
151
151
  g.window.location = _win.location;
152
152
  g.window.document = _win.document;
153
153
  }
154
- function buildStructuredData(meta, locale) {
155
- const perLocale = locale ? meta[`structured_data_org_${locale}`] : void 0;
156
- const value = perLocale ?? meta.structured_data_org;
157
- if (value == null || value === "") return "";
154
+ function buildStructuredData(sd, locale) {
155
+ var _a;
156
+ if (!sd) return "";
157
+ const perLocale = locale ? (_a = sd.org_by_locale) == null ? void 0 : _a[locale] : void 0;
158
+ const value = perLocale ?? sd.org;
159
+ if (value == null || typeof value === "string" && value === "") return "";
158
160
  try {
159
161
  const json = typeof value === "string" ? value : JSON.stringify(value);
160
162
  return `<script type="application/ld+json">${json}${"<"}/script>`;
@@ -164,44 +166,46 @@ function buildStructuredData(meta, locale) {
164
166
  }
165
167
  function buildPageHead(options) {
166
168
  var _a;
167
- const { url, resolvedData, website, stateScript, locale, override } = options;
169
+ const { url, resolvedData, website, stateScript, locale, override, buildOrigin } = options;
168
170
  const parts = [];
169
- const meta = (website == null ? void 0 : website.meta) ?? {};
171
+ const seo = (website == null ? void 0 : website.seo) ?? {};
170
172
  const page = resolvedData == null ? void 0 : resolvedData.page;
171
173
  const ctx = extractPrimaryContext(resolvedData == null ? void 0 : resolvedData.contexts);
172
- const title = (override == null ? void 0 : override.title) || (page == null ? void 0 : page.meta_title) || ctx.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;
174
+ const siteName = seo.site_name || void 0;
175
+ const title = (override == null ? void 0 : override.title) || (page == null ? void 0 : page.meta_title) || ctx.title || ((page == null ? void 0 : page.title) && siteName ? `${page.title} · ${siteName}` : null) || (page == null ? void 0 : page.title) || seo.default_meta_title;
173
176
  if (title) {
174
177
  const bare = (override == null ? void 0 : override.title) || ctx.title;
175
- const fullTitle = bare && meta.og_site_name && !bare.includes(meta.og_site_name) ? `${esc(bare)} · ${esc(meta.og_site_name)}` : esc(title);
178
+ const fullTitle = bare && siteName && !bare.includes(siteName) ? `${esc(bare)} · ${esc(siteName)}` : esc(title);
176
179
  parts.push(`<title>${fullTitle}</title>`);
177
180
  }
178
- const description = (override == null ? void 0 : override.description) || (page == null ? void 0 : page.meta_description) || ctx.description || meta.default_meta_description;
181
+ const description = (override == null ? void 0 : override.description) || (page == null ? void 0 : page.meta_description) || ctx.description || seo.default_meta_description;
179
182
  if (description) {
180
183
  parts.push(`<meta name="description" content="${esc(description)}">`);
181
184
  }
182
- const canonicalBase = (meta.canonical_base_url || (website == null ? void 0 : website.domain) || "").replace(/\/$/, "");
185
+ const canonicalBase = (seo.canonical_base_url || (website == null ? void 0 : website.domain) || buildOrigin || "").replace(/\/$/, "");
183
186
  const pageCanonical = (_a = page == null ? void 0 : page.canonical_url) == null ? void 0 : _a.trim();
184
187
  if (pageCanonical) {
185
188
  parts.push(`<link rel="canonical" href="${esc(pageCanonical)}">`);
186
189
  } else if (canonicalBase) {
187
190
  parts.push(`<link rel="canonical" href="${esc(canonicalBase + url)}">`);
188
191
  }
189
- const ogTitle = (override == null ? void 0 : override.title) || (page == null ? void 0 : page.meta_title) || ctx.title || (page == null ? void 0 : page.title) || meta.og_site_name || meta.default_meta_title;
190
- const ogDesc = (override == null ? void 0 : override.description) || (page == null ? void 0 : page.meta_description) || ctx.description || meta.og_description || meta.default_meta_description;
191
- const ogImage = absUrl((override == null ? void 0 : override.image) || (page == null ? void 0 : page.og_image) || ctx.image || meta.default_og_image);
192
- const ogType = meta.og_type || "website";
192
+ const ogTitle = (override == null ? void 0 : override.title) || (page == null ? void 0 : page.meta_title) || ctx.title || (page == null ? void 0 : page.title) || siteName || seo.default_meta_title;
193
+ const ogDesc = (override == null ? void 0 : override.description) || (page == null ? void 0 : page.meta_description) || ctx.description || seo.default_meta_description;
194
+ const ogImage = absUrl((override == null ? void 0 : override.image) || (page == null ? void 0 : page.og_image) || ctx.image || seo.default_og_image);
195
+ const isDetailPage = !!((resolvedData == null ? void 0 : resolvedData.contexts) && Object.values(resolvedData.contexts).some((c) => c != null));
196
+ const ogType = isDetailPage ? "article" : "website";
193
197
  if (ogTitle) parts.push(`<meta property="og:title" content="${esc(ogTitle)}">`);
194
198
  if (ogDesc) parts.push(`<meta property="og:description" content="${esc(ogDesc)}">`);
195
199
  if (ogImage) parts.push(`<meta property="og:image" content="${esc(ogImage)}">`);
196
200
  parts.push(`<meta property="og:type" content="${esc(ogType)}">`);
197
201
  if (canonicalBase) parts.push(`<meta property="og:url" content="${esc(canonicalBase + url)}">`);
198
- if (meta.og_site_name) parts.push(`<meta property="og:site_name" content="${esc(meta.og_site_name)}">`);
199
- const twitterCard = meta.twitter_card || (ogImage ? "summary_large_image" : "summary");
202
+ if (siteName) parts.push(`<meta property="og:site_name" content="${esc(siteName)}">`);
203
+ const twitterCard = ogImage ? "summary_large_image" : "summary";
200
204
  parts.push(`<meta name="twitter:card" content="${esc(twitterCard)}">`);
201
205
  if (ogTitle) parts.push(`<meta name="twitter:title" content="${esc(ogTitle)}">`);
202
206
  if (ogDesc) parts.push(`<meta name="twitter:description" content="${esc(ogDesc)}">`);
203
207
  if (ogImage) parts.push(`<meta name="twitter:image" content="${esc(ogImage)}">`);
204
- if (meta.twitter_site) parts.push(`<meta name="twitter:site" content="${esc(meta.twitter_site)}">`);
208
+ if (seo.twitter_site) parts.push(`<meta name="twitter:site" content="${esc(seo.twitter_site)}">`);
205
209
  const alternates = (override == null ? void 0 : override.alternates) && Object.keys(override.alternates).length ? override.alternates : resolvedData == null ? void 0 : resolvedData.alternates;
206
210
  const defaultLocale = (website == null ? void 0 : website.default_locale) ?? "en";
207
211
  if (alternates && canonicalBase) {
@@ -212,10 +216,10 @@ function buildPageHead(options) {
212
216
  const defaultSlug = alternates[defaultLocale] ?? url;
213
217
  parts.push(`<link rel="alternate" hreflang="x-default" href="${esc(canonicalBase + defaultSlug)}">`);
214
218
  }
215
- if (meta.noindex || (page == null ? void 0 : page.noindex)) {
219
+ if (seo.noindex || (page == null ? void 0 : page.noindex)) {
216
220
  parts.push('<meta name="robots" content="noindex, nofollow">');
217
221
  }
218
- const structuredData = buildStructuredData(meta, locale || (website == null ? void 0 : website.default_locale));
222
+ const structuredData = buildStructuredData(website == null ? void 0 : website.structured_data, locale || (website == null ? void 0 : website.default_locale));
219
223
  if (structuredData) parts.push(structuredData);
220
224
  if (stateScript) {
221
225
  parts.push(stateScript);
@@ -223,24 +227,25 @@ function buildPageHead(options) {
223
227
  return parts.join("\n");
224
228
  }
225
229
  function buildSiteHead(website) {
230
+ var _a, _b, _c;
226
231
  if (!website) return "";
227
232
  const parts = [];
228
- const meta = website.meta ?? {};
229
- const favicon = absUrl(website.favicon_url);
233
+ const seo = website.seo ?? {};
234
+ const favicon = absUrl(seo.favicon_url);
230
235
  if (favicon) {
231
236
  parts.push(`<link rel="icon" href="${esc(favicon)}">`);
232
237
  }
233
- const webclip = absUrl(meta.webclip);
238
+ const webclip = absUrl(seo.webclip);
234
239
  if (webclip) {
235
240
  parts.push(`<link rel="apple-touch-icon" sizes="180x180" href="${esc(webclip)}">`);
236
241
  }
237
- if (meta.verification_google) {
238
- parts.push(`<meta name="google-site-verification" content="${esc(meta.verification_google)}">`);
242
+ if ((_a = website.verification) == null ? void 0 : _a.google) {
243
+ parts.push(`<meta name="google-site-verification" content="${esc(website.verification.google)}">`);
239
244
  }
240
- if (meta.verification_bing) {
241
- parts.push(`<meta name="msvalidate.01" content="${esc(meta.verification_bing)}">`);
245
+ if ((_b = website.verification) == null ? void 0 : _b.bing) {
246
+ parts.push(`<meta name="msvalidate.01" content="${esc(website.verification.bing)}">`);
242
247
  }
243
- const plausibleId = website.plausible_site_id;
248
+ const plausibleId = (_c = website.integrations) == null ? void 0 : _c.plausible_site_id;
244
249
  if (plausibleId) {
245
250
  const host = "https://analytics.bagel.to";
246
251
  parts.push(
@@ -250,13 +255,16 @@ function buildSiteHead(website) {
250
255
  return parts.join("\n");
251
256
  }
252
257
  function generateSitemapXml(options) {
253
- const { renderedPaths, canonicalBase, defaultChangefreq = "weekly", defaultPriority = "0.7" } = options;
258
+ const { renderedPaths, canonicalBase, defaultChangefreq = "weekly", defaultPriority = "0.7", lastmod = {} } = options;
254
259
  const base = canonicalBase.replace(/\/$/, "");
255
260
  const urls = renderedPaths.map((p) => {
256
261
  const priority = p === "/" ? "1.0" : defaultPriority;
257
262
  const changefreq = p === "/" ? "daily" : defaultChangefreq;
263
+ const mod = lastmod[p];
264
+ const modLine = mod ? `
265
+ <lastmod>${esc(mod)}</lastmod>` : "";
258
266
  return ` <url>
259
- <loc>${esc(base + p)}</loc>
267
+ <loc>${esc(base + p)}</loc>${modLine}
260
268
  <changefreq>${changefreq}</changefreq>
261
269
  <priority>${priority}</priority>
262
270
  </url>`;
@@ -306,10 +314,10 @@ function stripTemplateSeoTags(template, options = {}) {
306
314
  return out.replace(/\n\s*\n\s*\n/g, "\n\n");
307
315
  }
308
316
  function getSiteIconFlags(website) {
309
- const meta = (website == null ? void 0 : website.meta) ?? {};
317
+ const seo = (website == null ? void 0 : website.seo) ?? {};
310
318
  return {
311
- hasFavicon: !!absUrl(website == null ? void 0 : website.favicon_url),
312
- hasWebclip: !!absUrl(meta.webclip)
319
+ hasFavicon: !!absUrl(seo.favicon_url),
320
+ hasWebclip: !!absUrl(seo.webclip)
313
321
  };
314
322
  }
315
323
  function extractPrimaryContext(contexts) {
@@ -347,12 +355,14 @@ async function prerender({
347
355
  excludePaths = [],
348
356
  failFast = false,
349
357
  maxPages = 5e3,
358
+ concurrency = 6,
350
359
  mode = "dir",
351
360
  website = null,
352
361
  websiteId = "",
353
362
  redirects = [],
354
363
  collections = {}
355
364
  } = {}) {
365
+ var _a, _b;
356
366
  const absRoot = path.resolve(root);
357
367
  const absClient = path.resolve(absRoot, clientOutDir);
358
368
  const absServerEntry = path.resolve(absRoot, serverEntry);
@@ -384,44 +394,67 @@ async function prerender({
384
394
  const fontPreloads = await discoverFontPreloads(absClient);
385
395
  const rendered = [];
386
396
  const noindexPaths = /* @__PURE__ */ new Set();
397
+ const lastmod = {};
387
398
  const failures = [];
388
- while (queue.length) {
389
- if (rendered.length >= maxPages) break;
390
- const urlPath = queue.shift();
391
- if (urlPath == null || urlPath === "") continue;
392
- if (isExcluded(urlPath)) continue;
393
- try {
394
- const { html, head = "", htmlAttrs = "" } = await serverMod.render(urlPath, {
395
- manifest,
396
- template,
397
- website,
398
- websiteId,
399
- collections
400
- });
401
- const outHtml = injectIntoTemplate(template, head, html, fontPreloads, htmlAttrs);
402
- const outfile = outFilePath(absClient, urlPath, mode);
403
- await fs.mkdir(path.dirname(outfile), { recursive: true });
404
- await fs.writeFile(outfile, outHtml, "utf8");
405
- rendered.push(urlPath);
406
- if (/<meta[^>]+name=["']robots["'][^>]*noindex/i.test(head)) {
407
- noindexPaths.add(urlPath);
408
- }
409
- if (crawl) {
410
- const discovered = discoverInternalLinks(outHtml);
411
- for (const p of discovered) enqueue(p, queue, seen, isExcluded);
399
+ async function renderOne(urlPath) {
400
+ const {
401
+ html,
402
+ head = "",
403
+ htmlAttrs = "",
404
+ lastmod: pageLastmod
405
+ } = await serverMod.render(urlPath, {
406
+ manifest,
407
+ template,
408
+ website,
409
+ websiteId,
410
+ collections
411
+ });
412
+ const outHtml = injectIntoTemplate(template, head, html, fontPreloads, htmlAttrs);
413
+ const outfile = outFilePath(absClient, urlPath, mode);
414
+ await fs.mkdir(path.dirname(outfile), { recursive: true });
415
+ await fs.writeFile(outfile, outHtml, "utf8");
416
+ rendered.push(urlPath);
417
+ if (pageLastmod) lastmod[urlPath] = pageLastmod;
418
+ if (/<meta[^>]+name=["']robots["'][^>]*noindex/i.test(head)) {
419
+ noindexPaths.add(urlPath);
420
+ }
421
+ if (crawl) {
422
+ for (const p of discoverInternalLinks(outHtml)) {
423
+ enqueue(p, queue, seen, isExcluded);
412
424
  }
413
- } catch (err) {
414
- failures.push({ path: urlPath, error: err });
415
- if (failFast) throw err;
416
- console.warn(`[prerender] failed: ${urlPath}
425
+ }
426
+ }
427
+ let stopped = false;
428
+ async function worker() {
429
+ while (!stopped) {
430
+ if (rendered.length >= maxPages) break;
431
+ const urlPath = queue.shift();
432
+ if (urlPath === void 0) break;
433
+ if (urlPath === "" || isExcluded(urlPath)) continue;
434
+ try {
435
+ await renderOne(urlPath);
436
+ } catch (err) {
437
+ failures.push({ path: urlPath, error: err });
438
+ if (failFast) {
439
+ stopped = true;
440
+ throw err;
441
+ }
442
+ console.warn(`[prerender] failed: ${urlPath}
417
443
  `, err.stack ?? err);
444
+ }
418
445
  }
419
446
  }
420
- const websiteMeta = (website == null ? void 0 : website.meta) ?? {};
421
- const canonicalBase = (websiteMeta.canonical_base_url || (website == null ? void 0 : website.domain) || "").replace(/\/$/, "");
447
+ while (queue.length && !stopped && rendered.length < maxPages) {
448
+ const workers = Array.from(
449
+ { length: Math.min(concurrency, queue.length) },
450
+ () => worker()
451
+ );
452
+ await Promise.all(workers);
453
+ }
454
+ const canonicalBase = (((_a = website == null ? void 0 : website.seo) == null ? void 0 : _a.canonical_base_url) || (website == null ? void 0 : website.domain) || "").replace(/\/$/, "");
422
455
  const sitemapPaths = rendered.filter((p) => !noindexPaths.has(p));
423
456
  if (canonicalBase && sitemapPaths.length > 0) {
424
- const sitemap = generateSitemapXml({ renderedPaths: sitemapPaths, canonicalBase });
457
+ const sitemap = generateSitemapXml({ renderedPaths: sitemapPaths, canonicalBase, lastmod });
425
458
  await fs.writeFile(path.join(absClient, "sitemap.xml"), sitemap, "utf8");
426
459
  const skipped = noindexPaths.size > 0 ? ` (${noindexPaths.size} noindex excluded)` : "";
427
460
  console.log(` Generated sitemap.xml (${sitemapPaths.length} URLs)${skipped}`);
@@ -429,7 +462,7 @@ async function prerender({
429
462
  const robotsTxt = generateRobotsTxt({
430
463
  robotsTxt: website == null ? void 0 : website.robots_txt,
431
464
  canonicalBase,
432
- noindex: websiteMeta.noindex
465
+ noindex: (_b = website == null ? void 0 : website.seo) == null ? void 0 : _b.noindex
433
466
  });
434
467
  await fs.writeFile(path.join(absClient, "robots.txt"), robotsTxt, "utf8");
435
468
  console.log(" Generated robots.txt");