@bagelink/blox 1.15.228 → 1.15.232

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.
@@ -329,16 +329,23 @@ function extractPrimaryContext(contexts) {
329
329
  if (!contexts || typeof contexts !== "object") return empty;
330
330
  const ctx = Object.values(contexts).find((c) => c != null);
331
331
  if (!ctx) return empty;
332
- const str = (key) => {
332
+ const str2 = (key) => {
333
333
  const v = ctx[key];
334
334
  return typeof v === "string" && v.trim() ? v.trim() : null;
335
335
  };
336
336
  return {
337
- title: str("meta_title") || str("title"),
338
- description: str("meta_description") || str("excerpt") || str("description") || str("blurb") || str("summary"),
339
- image: str("og_image") || str("cover_image_url") || str("image_url") || str("image")
337
+ title: str2("meta_title") || str2("title"),
338
+ description: str2("meta_description") || str2("excerpt") || str2("description") || str2("blurb") || str2("summary"),
339
+ image: str2("og_image") || str2("cover_image_url") || str2("image_url") || str2("image")
340
340
  };
341
341
  }
342
+ function primaryContextEntry(contexts) {
343
+ if (!contexts || typeof contexts !== "object") return null;
344
+ for (const [key, ctx] of Object.entries(contexts)) {
345
+ if (ctx != null && typeof ctx === "object") return { key, ctx };
346
+ }
347
+ return null;
348
+ }
342
349
  function esc(s) {
343
350
  return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
344
351
  }
@@ -350,6 +357,196 @@ function absUrl(value) {
350
357
  if (/^(https?:)?\/\//i.test(v) || v.startsWith("/")) return v;
351
358
  return `${FILES_BASE_URL}/${v.replace(/^\/+/, "")}`;
352
359
  }
360
+ function typeFromContextKey(key) {
361
+ const k = key.replace(/^\$/, "").toLowerCase();
362
+ if (/(post|article|blog|story|news)/.test(k)) return "Article";
363
+ if (/(product|listing|item|sku)/.test(k)) return "Product";
364
+ if (/(event|session|webinar)/.test(k)) return "Event";
365
+ return "WebPage";
366
+ }
367
+ function str(o, ...keys) {
368
+ for (const key of keys) {
369
+ const v = o[key];
370
+ if (typeof v === "string" && v.trim()) return v.trim();
371
+ }
372
+ return void 0;
373
+ }
374
+ function breadcrumbList(url, base, siteName) {
375
+ const segs = url.split("/").filter(Boolean);
376
+ const items = [
377
+ { "@type": "ListItem", "position": 1, "name": siteName || "Home", "item": base || "/" }
378
+ ];
379
+ let acc = "";
380
+ segs.forEach((seg, i) => {
381
+ acc += `/${seg}`;
382
+ items.push({
383
+ "@type": "ListItem",
384
+ "position": i + 2,
385
+ "name": decodeURIComponent(seg).replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
386
+ "item": base ? base + acc : acc
387
+ });
388
+ });
389
+ return { "@context": "https://schema.org", "@type": "BreadcrumbList", "itemListElement": items };
390
+ }
391
+ function buildJsonLd(options) {
392
+ var _a;
393
+ const { url, page, website, contextKey, context, config } = options;
394
+ const seo = (website == null ? void 0 : website.seo) ?? {};
395
+ const base = (seo.canonical_base_url || (website == null ? void 0 : website.domain) || "").replace(/\/$/, "");
396
+ const siteName = seo.site_name || void 0;
397
+ const pageUrl = base ? base + url : url;
398
+ const title = (page == null ? void 0 : page.meta_title) || (page == null ? void 0 : page.title) || str(context ?? {}, "meta_title", "title") || seo.default_meta_title;
399
+ const description = (page == null ? void 0 : page.meta_description) || str(context ?? {}, "meta_description", "excerpt", "description", "summary") || seo.default_meta_description;
400
+ const graphs = [];
401
+ const webPage = { "@context": "https://schema.org", "@type": "WebPage", "url": pageUrl };
402
+ if (title) webPage.name = title;
403
+ if (description) webPage.description = description;
404
+ if (siteName) webPage.isPartOf = { "@type": "WebSite", "name": siteName, "url": base || void 0 };
405
+ graphs.push(webPage);
406
+ if (url && url !== "/") graphs.push(breadcrumbList(url, base, siteName));
407
+ if (contextKey && context) {
408
+ const type = ((_a = config == null ? void 0 : config.typeMap) == null ? void 0 : _a[contextKey]) || typeFromContextKey(contextKey);
409
+ const entity = { "@context": "https://schema.org", "@type": type, "url": pageUrl };
410
+ const name = str(context, "title", "name", "meta_title");
411
+ const desc = str(context, "meta_description", "excerpt", "description", "summary", "blurb");
412
+ const image = str(context, "og_image", "cover_image_url", "image_url", "image");
413
+ if (name) entity.name = name;
414
+ if (desc) entity.description = desc;
415
+ if (image) entity.image = image;
416
+ if (type === "Article") {
417
+ if (siteName) entity.publisher = { "@type": "Organization", "name": siteName };
418
+ const date = str(context, "published_at", "created_at", "date");
419
+ if (date) entity.datePublished = date;
420
+ const upd = str(context, "updated_at", "modified_at");
421
+ if (upd) entity.dateModified = upd;
422
+ const author = str(context, "author", "author_name");
423
+ if (author) entity.author = { "@type": "Person", "name": author };
424
+ if (name) {
425
+ entity.headline = name;
426
+ delete entity.name;
427
+ }
428
+ } else if (type === "Product") {
429
+ const price = str(context, "price", "amount");
430
+ const currency = str(context, "currency", "currency_code") || "USD";
431
+ if (price) {
432
+ entity.offers = { "@type": "Offer", "price": price, "priceCurrency": currency, "url": pageUrl };
433
+ }
434
+ const sku = str(context, "sku", "id");
435
+ if (sku) entity.sku = sku;
436
+ } else if (type === "Event") {
437
+ const start = str(context, "start_at", "starts_at", "start_date", "date");
438
+ const end = str(context, "end_at", "ends_at", "end_date");
439
+ if (start) entity.startDate = start;
440
+ if (end) entity.endDate = end;
441
+ const loc = str(context, "location", "venue", "address");
442
+ if (loc) entity.location = { "@type": "Place", "name": loc };
443
+ }
444
+ graphs.push(entity);
445
+ }
446
+ return graphs.map((g) => `<script type="application/ld+json">${JSON.stringify(g)}${"<"}/script>`).join("\n");
447
+ }
448
+ function generateLlmsTxt(options) {
449
+ var _a;
450
+ const { website, pages, config } = options;
451
+ const seo = (website == null ? void 0 : website.seo) ?? {};
452
+ const base = (seo.canonical_base_url || (website == null ? void 0 : website.domain) || "").replace(/\/$/, "");
453
+ const siteName = seo.site_name || (website == null ? void 0 : website.name) || "Website";
454
+ const summary = seo.default_meta_description;
455
+ const visible = pages.filter((p) => !isPathExcluded(p.url, config == null ? void 0 : config.excludePaths)).sort((a, b) => a.url.localeCompare(b.url));
456
+ const lines = [`# ${siteName}`, ""];
457
+ if (summary) lines.push(`> ${summary}`, "");
458
+ if (config == null ? void 0 : config.intro) lines.push(config.intro.trim(), "");
459
+ const abs = (u) => base ? base + u : u;
460
+ const link = (p) => {
461
+ const label = p.title || p.url;
462
+ const desc = p.description ? `: ${p.description}` : "";
463
+ return `- [${label}](${abs(p.url)})${desc}`;
464
+ };
465
+ const used = /* @__PURE__ */ new Set();
466
+ for (const section of (config == null ? void 0 : config.sections) ?? []) {
467
+ const inSection = visible.filter((p) => p.url.startsWith(section.prefix));
468
+ if (inSection.length === 0) continue;
469
+ lines.push(`## ${section.title}`, "");
470
+ for (const p of inSection) {
471
+ lines.push(link(p));
472
+ used.add(p.url);
473
+ }
474
+ lines.push("");
475
+ }
476
+ const rest = visible.filter((p) => !used.has(p.url));
477
+ if (rest.length > 0) {
478
+ lines.push(((_a = config == null ? void 0 : config.sections) == null ? void 0 : _a.length) ? "## Pages" : "## Pages", "");
479
+ for (const p of rest) lines.push(link(p));
480
+ lines.push("");
481
+ }
482
+ return `${lines.join("\n").trimEnd()}
483
+ `;
484
+ }
485
+ function generateLlmsFullTxt(options) {
486
+ const { website, pages, config } = options;
487
+ const seo = (website == null ? void 0 : website.seo) ?? {};
488
+ const base = (seo.canonical_base_url || (website == null ? void 0 : website.domain) || "").replace(/\/$/, "");
489
+ const siteName = seo.site_name || (website == null ? void 0 : website.name) || "Website";
490
+ const visible = pages.filter((p) => !isPathExcluded(p.url, config == null ? void 0 : config.excludePaths)).filter((p) => p.text && p.text.trim()).sort((a, b) => a.url.localeCompare(b.url));
491
+ const blocks = [`# ${siteName}`, ""];
492
+ for (const p of visible) {
493
+ const abs = base ? base + p.url : p.url;
494
+ blocks.push(`## ${p.title || p.url}`, "", `Source: ${abs}`, "");
495
+ if (p.description) blocks.push(`> ${p.description}`, "");
496
+ blocks.push(p.text.trim(), "", "---", "");
497
+ }
498
+ return `${blocks.join("\n").trimEnd()}
499
+ `;
500
+ }
501
+ function extractPageText(html) {
502
+ if (!html) return "";
503
+ let s = html;
504
+ s = s.replace(/<(script|style|noscript|svg|template)\b[^>]*>[\s\S]*?<\/\1>/gi, " ");
505
+ s = s.replace(/<(nav|header|footer)\b[^>]*>[\s\S]*?<\/\1>/gi, " ");
506
+ s = s.replace(/<h1\b[^>]*>/gi, "\n\n# ").replace(/<h2\b[^>]*>/gi, "\n\n## ").replace(/<h3\b[^>]*>/gi, "\n\n### ").replace(/<(h4|h5|h6)\b[^>]*>/gi, "\n\n#### ");
507
+ s = s.replace(/<li\b[^>]*>/gi, "\n- ");
508
+ s = s.replace(/<(\/p|\/div|\/section|\/article|br\s*\/?|\/li|\/h[1-6])>/gi, "\n");
509
+ s = s.replace(/<[^>]+>/g, " ");
510
+ s = s.replace(/&nbsp;/g, " ").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'");
511
+ s = s.split("\n").map((l) => l.replace(/[ \t]+/g, " ").trim()).join("\n");
512
+ s = s.replace(/\n{3,}/g, "\n\n").trim();
513
+ return s;
514
+ }
515
+ function isPathExcluded(path2, patterns) {
516
+ if (!patterns || patterns.length === 0) return false;
517
+ return patterns.some((p) => {
518
+ if (p.endsWith("*")) return path2.startsWith(p.slice(0, -1));
519
+ return path2 === p;
520
+ });
521
+ }
522
+ async function mergeLlmParts(options) {
523
+ const fs2 = await import("node:fs/promises");
524
+ const path2 = await import("node:path");
525
+ const byUrl = /* @__PURE__ */ new Map();
526
+ for (const file of options.partFiles) {
527
+ try {
528
+ const raw = await fs2.readFile(file, "utf8");
529
+ const arr = JSON.parse(raw);
530
+ for (const e of arr) if (e == null ? void 0 : e.url) byUrl.set(e.url, e);
531
+ } catch {
532
+ }
533
+ }
534
+ const pages = [...byUrl.values()];
535
+ const { website, config } = options;
536
+ const llmsTxt = generateLlmsTxt({ website, pages, config });
537
+ await fs2.writeFile(path2.join(options.outDir, "llms.txt"), llmsTxt, "utf8");
538
+ if ((config == null ? void 0 : config.fullText) !== false) {
539
+ const full = generateLlmsFullTxt({ website, pages, config });
540
+ await fs2.writeFile(path2.join(options.outDir, "llms-full.txt"), full, "utf8");
541
+ }
542
+ for (const file of options.partFiles) {
543
+ try {
544
+ await fs2.unlink(file);
545
+ } catch {
546
+ }
547
+ }
548
+ return { pages: pages.length };
549
+ }
353
550
  async function prerender({
354
551
  root = process.cwd(),
355
552
  clientOutDir = "dist/client",
@@ -364,15 +561,20 @@ async function prerender({
364
561
  /**
365
562
  * How many `/resolve-path` prefetches to run in parallel, ahead of the render
366
563
  * pool. Decouples the (I/O-bound) resolve latency from the (CPU-bound) render
367
- * so pages render from an in-memory cache with no per-page round-trip. 0
368
- * disables prefetch (render resolves inline). Default 24.
564
+ * so pages render from an in-memory cache with no per-page round-trip.
565
+ *
566
+ * NOTE: this is PER PROCESS. When running N shards, the *total* load on the
567
+ * API is `N × this`, so keep it modest (a small API saturates ~5-wide). The
568
+ * sharded runner divides a global budget by the shard count. 0 disables
569
+ * prefetch (render resolves inline). Default 8.
369
570
  */
370
- resolvePrefetchConcurrency = 24,
571
+ resolvePrefetchConcurrency = 8,
371
572
  mode = "dir",
372
573
  website = null,
373
574
  websiteId = "",
374
575
  redirects = [],
375
- collections = {}
576
+ collections = {},
577
+ llm = {}
376
578
  } = {}) {
377
579
  var _a, _b;
378
580
  const absRoot = path.resolve(root);
@@ -407,6 +609,8 @@ async function prerender({
407
609
  const rendered = [];
408
610
  const noindexPaths = /* @__PURE__ */ new Set();
409
611
  const lastmod = {};
612
+ const llmPages = [];
613
+ const llmEnabled = llm.enabled !== false;
410
614
  const failures = [];
411
615
  const resolveCache = /* @__PURE__ */ new Map();
412
616
  const prefetch = serverMod.prefetchResolve;
@@ -434,14 +638,16 @@ async function prerender({
434
638
  html,
435
639
  head = "",
436
640
  htmlAttrs = "",
437
- lastmod: pageLastmod
641
+ lastmod: pageLastmod,
642
+ llm: pageLlm
438
643
  } = await serverMod.render(urlPath, {
439
644
  manifest,
440
645
  template,
441
646
  website,
442
647
  websiteId,
443
648
  collections,
444
- resolveCache
649
+ resolveCache,
650
+ llm
445
651
  });
446
652
  const outHtml = injectIntoTemplate(template, head, html, fontPreloads, htmlAttrs);
447
653
  const outfile = outFilePath(absClient, urlPath, mode);
@@ -449,6 +655,7 @@ async function prerender({
449
655
  await fs.writeFile(outfile, outHtml, "utf8");
450
656
  rendered.push(urlPath);
451
657
  if (pageLastmod) lastmod[urlPath] = pageLastmod;
658
+ if (llmEnabled && pageLlm) llmPages.push(pageLlm);
452
659
  if (/<meta[^>]+name=["']robots["'][^>]*noindex/i.test(head)) {
453
660
  noindexPaths.add(urlPath);
454
661
  }
@@ -478,7 +685,8 @@ async function prerender({
478
685
  }
479
686
  }
480
687
  }
481
- const prefetchDone = runResolvePrefetch();
688
+ await runResolvePrefetch().catch(() => {
689
+ });
482
690
  while (queue.length && !stopped && rendered.length < maxPages) {
483
691
  const workers = Array.from(
484
692
  { length: Math.min(concurrency, queue.length) },
@@ -486,8 +694,6 @@ async function prerender({
486
694
  );
487
695
  await Promise.all(workers);
488
696
  }
489
- await prefetchDone.catch(() => {
490
- });
491
697
  const canonicalBase = (((_a = website == null ? void 0 : website.seo) == null ? void 0 : _a.canonical_base_url) || (website == null ? void 0 : website.domain) || "").replace(/\/$/, "");
492
698
  if (writeSharedAssets) {
493
699
  const sitemapPaths = rendered.filter((p) => !noindexPaths.has(p));
@@ -515,6 +721,24 @@ async function prerender({
515
721
  await fs.writeFile(redirectsPath, redirectsContent + existing, "utf8");
516
722
  console.log(` Generated _redirects (${redirects.length} rules)`);
517
723
  }
724
+ if (llmEnabled && llm.writeIndex !== false && llmPages.length > 0) {
725
+ const indexable = llmPages.filter((p) => !noindexPaths.has(p.url));
726
+ const llmsTxt = generateLlmsTxt({ website, pages: indexable, config: llm });
727
+ await fs.writeFile(path.join(absClient, "llms.txt"), llmsTxt, "utf8");
728
+ console.log(` Generated llms.txt (${indexable.length} pages)`);
729
+ if (llm.fullText !== false) {
730
+ const full = generateLlmsFullTxt({ website, pages: indexable, config: llm });
731
+ await fs.writeFile(path.join(absClient, "llms-full.txt"), full, "utf8");
732
+ console.log(" Generated llms-full.txt");
733
+ }
734
+ }
735
+ }
736
+ if (llmEnabled && llm.partsFile && llmPages.length > 0) {
737
+ const indexable = llmPages.filter((p) => !noindexPaths.has(p.url));
738
+ const partPath = path.resolve(absRoot, llm.partsFile);
739
+ await fs.mkdir(path.dirname(partPath), { recursive: true });
740
+ await fs.writeFile(partPath, JSON.stringify(indexable), "utf8");
741
+ console.log(` Wrote ${indexable.length} llm page extract(s) → ${llm.partsFile}`);
518
742
  }
519
743
  return {
520
744
  rendered,
@@ -650,12 +874,18 @@ function discoverInternalLinks(html) {
650
874
  export {
651
875
  prerender as a,
652
876
  buildPageHead as b,
653
- buildSiteHead as c,
654
- fetchCmsPrerenderPaths as d,
655
- generateRobotsTxt as e,
877
+ primaryContextEntry as c,
878
+ buildJsonLd as d,
879
+ extractPageText as e,
656
880
  fetchCmsSiteData as f,
657
- generateNetlifyRedirects as g,
658
- generateSitemapXml as h,
659
- getSiteIconFlags as i,
881
+ buildSiteHead as g,
882
+ fetchCmsPrerenderPaths as h,
883
+ generateLlmsFullTxt as i,
884
+ generateLlmsTxt as j,
885
+ generateNetlifyRedirects as k,
886
+ generateRobotsTxt as l,
887
+ generateSitemapXml as m,
888
+ getSiteIconFlags as n,
889
+ mergeLlmParts as o,
660
890
  polyfillBloxSsgGlobals as p
661
891
  };