chaingrow-blog 0.2.2 → 0.7.1

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.
package/dist/index.js CHANGED
@@ -1,7 +1,105 @@
1
1
  import {
2
- createClient
3
- } from "./chunk-OTLGKSYD.js";
2
+ mergeBlogs,
3
+ toBlogCard
4
+ } from "./chunk-4EKET2DQ.js";
5
+ import {
6
+ SCHEMA_ORG_CONTEXT,
7
+ buildBlogJsonLd,
8
+ buildBlogPostingSchema,
9
+ buildFAQPageSchema,
10
+ createClient,
11
+ distributeMediaPositions,
12
+ interleaveMedia
13
+ } from "./chunk-N7QETVCO.js";
14
+
15
+ // src/sitemap-builder.ts
16
+ var NEWS_WINDOW_MS = 48 * 60 * 60 * 1e3;
17
+ var NEWS_MAX_ENTRIES = 1e3;
18
+ function escapeXml(s) {
19
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
20
+ }
21
+ var trimTrailingSlash = (s) => s.replace(/\/+$/, "");
22
+ var normalizePathPrefix = (s) => {
23
+ let p = s.trim();
24
+ if (!p.startsWith("/")) p = "/" + p;
25
+ while (p.length > 1 && p.endsWith("/")) p = p.slice(0, -1);
26
+ return p;
27
+ };
28
+ var buildBlogUrl = (baseUrl, pathPrefix, slug) => `${baseUrl}${pathPrefix}/${slug}`;
29
+ var isImageMedia = (m) => m.media_type === "IMAGE";
30
+ var renderImageEntry = (m) => ` <image:image>
31
+ <image:loc>${escapeXml(m.media_url)}</image:loc>
32
+ </image:image>
33
+ `;
34
+ var lastmodFor = (blog) => blog.published_at;
35
+ function buildSitemapXml(blogs, opts) {
36
+ const baseUrl = trimTrailingSlash(opts.baseUrl);
37
+ const pathPrefix = normalizePathPrefix(opts.pathPrefix ?? "/blog");
38
+ const entries = blogs.map((blog) => {
39
+ const url = buildBlogUrl(baseUrl, pathPrefix, blog.slug);
40
+ const images = (blog.generatedMedia ?? []).filter(isImageMedia).map(renderImageEntry).join("");
41
+ return ` <url>
42
+ <loc>${escapeXml(url)}</loc>
43
+ <lastmod>${escapeXml(lastmodFor(blog))}</lastmod>
44
+ ` + images + ` </url>
45
+ `;
46
+ }).join("");
47
+ return `<?xml version="1.0" encoding="UTF-8"?>
48
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">
49
+ ` + entries + `</urlset>
50
+ `;
51
+ }
52
+ function filterRecentForNews(blogs, now = /* @__PURE__ */ new Date()) {
53
+ const cutoff = now.getTime() - NEWS_WINDOW_MS;
54
+ const recent = blogs.filter((b) => {
55
+ const t = Date.parse(b.published_at);
56
+ return Number.isFinite(t) && t >= cutoff;
57
+ });
58
+ recent.sort(
59
+ (a, b) => Date.parse(b.published_at) - Date.parse(a.published_at)
60
+ );
61
+ return recent.slice(0, NEWS_MAX_ENTRIES);
62
+ }
63
+ function buildNewsSitemapXml(blogs, opts) {
64
+ const baseUrl = trimTrailingSlash(opts.baseUrl);
65
+ const pathPrefix = normalizePathPrefix(opts.pathPrefix ?? "/blog");
66
+ const publicationName = opts.publicationName;
67
+ const defaultLanguage = opts.defaultLanguage ?? "en";
68
+ const recent = filterRecentForNews(blogs, opts.now);
69
+ const entries = recent.map((blog) => {
70
+ const url = buildBlogUrl(baseUrl, pathPrefix, blog.slug);
71
+ const language = blog.language || defaultLanguage;
72
+ return ` <url>
73
+ <loc>${escapeXml(url)}</loc>
74
+ <news:news>
75
+ <news:publication>
76
+ <news:name>${escapeXml(publicationName)}</news:name>
77
+ <news:language>${escapeXml(language)}</news:language>
78
+ </news:publication>
79
+ <news:publication_date>${escapeXml(blog.published_at)}</news:publication_date>
80
+ <news:title>${escapeXml(blog.title)}</news:title>
81
+ </news:news>
82
+ </url>
83
+ `;
84
+ }).join("");
85
+ return `<?xml version="1.0" encoding="UTF-8"?>
86
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9">
87
+ ` + entries + `</urlset>
88
+ `;
89
+ }
4
90
  export {
5
- createClient
91
+ SCHEMA_ORG_CONTEXT,
92
+ buildBlogJsonLd,
93
+ buildBlogPostingSchema,
94
+ buildFAQPageSchema,
95
+ buildNewsSitemapXml,
96
+ buildSitemapXml,
97
+ createClient,
98
+ distributeMediaPositions,
99
+ escapeXml,
100
+ filterRecentForNews,
101
+ interleaveMedia,
102
+ mergeBlogs,
103
+ toBlogCard
6
104
  };
7
105
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
1
+ {"version":3,"sources":["../src/sitemap-builder.ts"],"sourcesContent":["import type { Blog, EmbeddedMedia } from './types';\n\nconst NEWS_WINDOW_MS = 48 * 60 * 60 * 1000;\nconst NEWS_MAX_ENTRIES = 1000;\n\nexport type SitemapBuildOptions = {\n baseUrl: string;\n pathPrefix?: string;\n};\n\nexport type NewsSitemapBuildOptions = SitemapBuildOptions & {\n publicationName: string;\n defaultLanguage?: string;\n now?: Date;\n};\n\nexport function escapeXml(s: string): string {\n return s\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&apos;');\n}\n\nconst trimTrailingSlash = (s: string): string => s.replace(/\\/+$/, '');\n\nconst normalizePathPrefix = (s: string): string => {\n let p = s.trim();\n if (!p.startsWith('/')) p = '/' + p;\n while (p.length > 1 && p.endsWith('/')) p = p.slice(0, -1);\n return p;\n};\n\nconst buildBlogUrl = (\n baseUrl: string,\n pathPrefix: string,\n slug: string,\n): string => `${baseUrl}${pathPrefix}/${slug}`;\n\nconst isImageMedia = (m: EmbeddedMedia): boolean => m.media_type === 'IMAGE';\n\nconst renderImageEntry = (m: EmbeddedMedia): string =>\n ` <image:image>\\n` +\n ` <image:loc>${escapeXml(m.media_url)}</image:loc>\\n` +\n ` </image:image>\\n`;\n\nconst lastmodFor = (blog: Blog): string => blog.published_at;\n\nexport function buildSitemapXml(\n blogs: Blog[],\n opts: SitemapBuildOptions,\n): string {\n const baseUrl = trimTrailingSlash(opts.baseUrl);\n const pathPrefix = normalizePathPrefix(opts.pathPrefix ?? '/blog');\n\n const entries = blogs\n .map((blog) => {\n const url = buildBlogUrl(baseUrl, pathPrefix, blog.slug);\n const images = (blog.generatedMedia ?? [])\n .filter(isImageMedia)\n .map(renderImageEntry)\n .join('');\n return (\n ` <url>\\n` +\n ` <loc>${escapeXml(url)}</loc>\\n` +\n ` <lastmod>${escapeXml(lastmodFor(blog))}</lastmod>\\n` +\n images +\n ` </url>\\n`\n );\n })\n .join('');\n\n return (\n `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n` +\n `<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\" ` +\n `xmlns:image=\"http://www.google.com/schemas/sitemap-image/1.1\">\\n` +\n entries +\n `</urlset>\\n`\n );\n}\n\n/** Generic over anything carrying `published_at` so custom blog shapes\n * (or intersections with extra fields) pass through without a cast. */\nexport function filterRecentForNews<T extends Pick<Blog, 'published_at'>>(\n blogs: T[],\n now: Date = new Date(),\n): T[] {\n const cutoff = now.getTime() - NEWS_WINDOW_MS;\n const recent = blogs.filter((b) => {\n const t = Date.parse(b.published_at);\n return Number.isFinite(t) && t >= cutoff;\n });\n recent.sort(\n (a, b) => Date.parse(b.published_at) - Date.parse(a.published_at),\n );\n return recent.slice(0, NEWS_MAX_ENTRIES);\n}\n\nexport function buildNewsSitemapXml(\n blogs: Blog[],\n opts: NewsSitemapBuildOptions,\n): string {\n const baseUrl = trimTrailingSlash(opts.baseUrl);\n const pathPrefix = normalizePathPrefix(opts.pathPrefix ?? '/blog');\n const publicationName = opts.publicationName;\n const defaultLanguage = opts.defaultLanguage ?? 'en';\n\n const recent = filterRecentForNews(blogs, opts.now);\n\n const entries = recent\n .map((blog) => {\n const url = buildBlogUrl(baseUrl, pathPrefix, blog.slug);\n const language = blog.language || defaultLanguage;\n return (\n ` <url>\\n` +\n ` <loc>${escapeXml(url)}</loc>\\n` +\n ` <news:news>\\n` +\n ` <news:publication>\\n` +\n ` <news:name>${escapeXml(publicationName)}</news:name>\\n` +\n ` <news:language>${escapeXml(language)}</news:language>\\n` +\n ` </news:publication>\\n` +\n ` <news:publication_date>${escapeXml(blog.published_at)}</news:publication_date>\\n` +\n ` <news:title>${escapeXml(blog.title)}</news:title>\\n` +\n ` </news:news>\\n` +\n ` </url>\\n`\n );\n })\n .join('');\n\n return (\n `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n` +\n `<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\" ` +\n `xmlns:news=\"http://www.google.com/schemas/sitemap-news/0.9\">\\n` +\n entries +\n `</urlset>\\n`\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AAEA,IAAM,iBAAiB,KAAK,KAAK,KAAK;AACtC,IAAM,mBAAmB;AAalB,SAAS,UAAU,GAAmB;AAC3C,SAAO,EACJ,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,QAAQ,EACtB,QAAQ,MAAM,QAAQ;AAC3B;AAEA,IAAM,oBAAoB,CAAC,MAAsB,EAAE,QAAQ,QAAQ,EAAE;AAErE,IAAM,sBAAsB,CAAC,MAAsB;AACjD,MAAI,IAAI,EAAE,KAAK;AACf,MAAI,CAAC,EAAE,WAAW,GAAG,EAAG,KAAI,MAAM;AAClC,SAAO,EAAE,SAAS,KAAK,EAAE,SAAS,GAAG,EAAG,KAAI,EAAE,MAAM,GAAG,EAAE;AACzD,SAAO;AACT;AAEA,IAAM,eAAe,CACnB,SACA,YACA,SACW,GAAG,OAAO,GAAG,UAAU,IAAI,IAAI;AAE5C,IAAM,eAAe,CAAC,MAA8B,EAAE,eAAe;AAErE,IAAM,mBAAmB,CAAC,MACxB;AAAA,mBACoB,UAAU,EAAE,SAAS,CAAC;AAAA;AAAA;AAG5C,IAAM,aAAa,CAAC,SAAuB,KAAK;AAEzC,SAAS,gBACd,OACA,MACQ;AACR,QAAM,UAAU,kBAAkB,KAAK,OAAO;AAC9C,QAAM,aAAa,oBAAoB,KAAK,cAAc,OAAO;AAEjE,QAAM,UAAU,MACb,IAAI,CAAC,SAAS;AACb,UAAM,MAAM,aAAa,SAAS,YAAY,KAAK,IAAI;AACvD,UAAM,UAAU,KAAK,kBAAkB,CAAC,GACrC,OAAO,YAAY,EACnB,IAAI,gBAAgB,EACpB,KAAK,EAAE;AACV,WACE;AAAA,WACY,UAAU,GAAG,CAAC;AAAA,eACV,UAAU,WAAW,IAAI,CAAC,CAAC;AAAA,IAC3C,SACA;AAAA;AAAA,EAEJ,CAAC,EACA,KAAK,EAAE;AAEV,SACE;AAAA;AAAA,IAGA,UACA;AAAA;AAEJ;AAIO,SAAS,oBACd,OACA,MAAY,oBAAI,KAAK,GAChB;AACL,QAAM,SAAS,IAAI,QAAQ,IAAI;AAC/B,QAAM,SAAS,MAAM,OAAO,CAAC,MAAM;AACjC,UAAM,IAAI,KAAK,MAAM,EAAE,YAAY;AACnC,WAAO,OAAO,SAAS,CAAC,KAAK,KAAK;AAAA,EACpC,CAAC;AACD,SAAO;AAAA,IACL,CAAC,GAAG,MAAM,KAAK,MAAM,EAAE,YAAY,IAAI,KAAK,MAAM,EAAE,YAAY;AAAA,EAClE;AACA,SAAO,OAAO,MAAM,GAAG,gBAAgB;AACzC;AAEO,SAAS,oBACd,OACA,MACQ;AACR,QAAM,UAAU,kBAAkB,KAAK,OAAO;AAC9C,QAAM,aAAa,oBAAoB,KAAK,cAAc,OAAO;AACjE,QAAM,kBAAkB,KAAK;AAC7B,QAAM,kBAAkB,KAAK,mBAAmB;AAEhD,QAAM,SAAS,oBAAoB,OAAO,KAAK,GAAG;AAElD,QAAM,UAAU,OACb,IAAI,CAAC,SAAS;AACb,UAAM,MAAM,aAAa,SAAS,YAAY,KAAK,IAAI;AACvD,UAAM,WAAW,KAAK,YAAY;AAClC,WACE;AAAA,WACY,UAAU,GAAG,CAAC;AAAA;AAAA;AAAA,qBAGJ,UAAU,eAAe,CAAC;AAAA,yBACtB,UAAU,QAAQ,CAAC;AAAA;AAAA,+BAEb,UAAU,KAAK,YAAY,CAAC;AAAA,oBACvC,UAAU,KAAK,KAAK,CAAC;AAAA;AAAA;AAAA;AAAA,EAI9C,CAAC,EACA,KAAK,EAAE;AAEV,SACE;AAAA;AAAA,IAGA,UACA;AAAA;AAEJ;","names":[]}