@asteroidcms/core-utils 0.1.7 → 0.1.8

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/next.cjs ADDED
@@ -0,0 +1,211 @@
1
+ 'use strict';
2
+
3
+ var Head = require('next/head');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+
6
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
7
+
8
+ var Head__default = /*#__PURE__*/_interopDefault(Head);
9
+
10
+ // src/seo/next.tsx
11
+
12
+ // src/utils/cmsImage.ts
13
+ function cmsImage(id, options) {
14
+ if (!id) return "";
15
+ const base = options.cmsUrl.replace(/\/+$/, "");
16
+ const path = (options.mediaPath ?? "/media/canonical").replace(/^\/?/, "/");
17
+ return `${base}${path}/${id}`;
18
+ }
19
+
20
+ // src/seo/seo.builders.ts
21
+ function applyTitleTemplate(config, title) {
22
+ return config.titleTemplate ? config.titleTemplate(title) : `${title} | ${config.siteName}`;
23
+ }
24
+ function buildOgImageUrl(config, params) {
25
+ if (config.getOgImageUrl) {
26
+ return config.getOgImageUrl(params);
27
+ }
28
+ const palette = config.ogImage?.palette;
29
+ if (!palette) return void 0;
30
+ const apiPath = config.ogImage?.apiPath ?? "/api/og";
31
+ const base = config.baseUrl.replace(/\/$/, "");
32
+ const searchParams = new URLSearchParams({
33
+ title: params.title,
34
+ type: params.type ?? "article",
35
+ siteName: config.siteName,
36
+ bg: palette.background,
37
+ fg: palette.foreground,
38
+ accent: palette.accent
39
+ });
40
+ if (params.subtitle?.trim()) searchParams.set("subtitle", params.subtitle.trim());
41
+ if (params.eyebrow?.trim()) searchParams.set("eyebrow", params.eyebrow.trim());
42
+ if (palette.accentMuted) searchParams.set("accentMuted", palette.accentMuted);
43
+ if (palette.mutedText) searchParams.set("muted", palette.mutedText);
44
+ return `${base}${apiPath}?${searchParams.toString()}`;
45
+ }
46
+ function buildPageSeoValues(config, options) {
47
+ const base = config.baseUrl.replace(/\/$/, "");
48
+ const path = options.path ?? "/";
49
+ const url = `${base}${path.startsWith("/") ? path : `/${path}`}`;
50
+ const description = options.description?.trim() || config.defaultDescription || `${options.title} - ${config.siteName}.`;
51
+ return {
52
+ title: applyTitleTemplate(config, options.title),
53
+ siteName: config.siteName,
54
+ twitter: config.twitter ?? "",
55
+ description,
56
+ url,
57
+ keywords: options.keywords ?? config.defaultKeywords ?? options.title,
58
+ image: options.image ?? buildOgImageUrl(config, {
59
+ title: options.title,
60
+ subtitle: description,
61
+ eyebrow: options.eyebrow,
62
+ type: options.ogType === "article" ? "article" : "listing"
63
+ }),
64
+ noindex: options.noindex ?? config.noindex,
65
+ manifestUrl: config.manifestUrl
66
+ };
67
+ }
68
+ function resolveArticleImage(post, config) {
69
+ const featuredImage = config.cmsUrl ? cmsImage(post.featured_image, { cmsUrl: config.cmsUrl }) : "";
70
+ if (featuredImage) return featuredImage;
71
+ const description = post.meta_description?.trim() || post.description?.trim() || config.defaultDescription;
72
+ return buildOgImageUrl(config, {
73
+ title: post.title,
74
+ subtitle: description,
75
+ eyebrow: config.contentLabel ?? "Article",
76
+ type: "article"
77
+ });
78
+ }
79
+ function buildArticleSeoValues(post, config, slug, options) {
80
+ const articlePath = config.articlePath ?? "/blog";
81
+ const url = `${config.baseUrl.replace(/\/$/, "")}${articlePath}/${slug}`;
82
+ const description = post.meta_description?.trim() || post.description?.trim() || config.defaultDescription || `Read the latest from ${config.siteName}.`;
83
+ return {
84
+ title: applyTitleTemplate(config, post.title),
85
+ siteName: config.siteName,
86
+ twitter: config.twitter ?? "",
87
+ description,
88
+ url,
89
+ keywords: config.defaultKeywords ?? post.title,
90
+ image: resolveArticleImage(post, config),
91
+ noindex: config.noindex,
92
+ manifestUrl: config.manifestUrl
93
+ };
94
+ }
95
+ function buildArticleListingSeoValues(config, options) {
96
+ const articlePath = config.articlePath ?? "/blog";
97
+ const base = config.baseUrl.replace(/\/$/, "");
98
+ const label = config.contentLabel ?? "Articles";
99
+ const categoryName = options?.categoryName?.trim();
100
+ const categorySlug = options?.categorySlug?.trim();
101
+ const titleText = categoryName ? `${categoryName} ${label}` : label;
102
+ const description = categoryName ? `Explore ${categoryName} ${label.toLowerCase()}, guides, and the latest updates from ${config.siteName}.` : config.defaultDescription || `Browse ${label.toLowerCase()}, insights, and the latest updates from ${config.siteName}.`;
103
+ const url = categorySlug ? `${base}${articlePath}/category/${categorySlug}` : `${base}${articlePath}`;
104
+ return {
105
+ title: applyTitleTemplate(config, titleText),
106
+ siteName: config.siteName,
107
+ twitter: config.twitter ?? "",
108
+ description,
109
+ url,
110
+ keywords: config.defaultKeywords ?? (categoryName ? `${categoryName}, ${config.siteName}` : `${config.siteName} ${label.toLowerCase()}`),
111
+ image: buildOgImageUrl(config, {
112
+ title: titleText,
113
+ subtitle: description,
114
+ eyebrow: categoryName ? "Category" : label,
115
+ type: "listing"
116
+ }),
117
+ noindex: options?.noindex ?? config.noindex,
118
+ manifestUrl: config.manifestUrl
119
+ };
120
+ }
121
+ async function generateSeoMetadata(SeoInfo) {
122
+ return {
123
+ title: SeoInfo.title,
124
+ description: SeoInfo.description,
125
+ publisher: SeoInfo.siteName,
126
+ keywords: SeoInfo.keywords,
127
+ category: SeoInfo.title,
128
+ ...SeoInfo.manifestUrl ? { manifest: SeoInfo.manifestUrl } : {},
129
+ robots: SeoInfo.noindex ? { index: false, follow: true } : { index: true, follow: true },
130
+ authors: { name: SeoInfo.siteName },
131
+ referrer: "origin",
132
+ abstract: SeoInfo.description,
133
+ alternates: { canonical: SeoInfo.url },
134
+ openGraph: {
135
+ title: SeoInfo.title,
136
+ description: SeoInfo.description,
137
+ url: SeoInfo.url,
138
+ siteName: SeoInfo.siteName,
139
+ locale: "en_US",
140
+ type: "website",
141
+ ...SeoInfo.image ? { images: [{ url: SeoInfo.image }] } : {}
142
+ },
143
+ twitter: {
144
+ title: SeoInfo.title,
145
+ description: SeoInfo.description,
146
+ site: SeoInfo.twitter || void 0,
147
+ card: SeoInfo.image ? "summary_large_image" : "summary",
148
+ ...SeoInfo.image ? { images: [SeoInfo.image] } : {}
149
+ }
150
+ };
151
+ }
152
+ async function generatePageSeoMetadata(config, options) {
153
+ const metadata = await generateSeoMetadata(buildPageSeoValues(config, options));
154
+ if (options.ogType === "article") {
155
+ return { ...metadata, openGraph: { ...metadata.openGraph, type: "article" } };
156
+ }
157
+ return metadata;
158
+ }
159
+ async function generateArticleSeoMetadata(post, config, slug) {
160
+ const seoValues = buildArticleSeoValues(post, config, slug);
161
+ const metadata = await generateSeoMetadata(seoValues);
162
+ return {
163
+ ...metadata,
164
+ openGraph: { ...metadata.openGraph, type: "article" }
165
+ };
166
+ }
167
+ async function generateArticleListingSeoMetadata(config, options) {
168
+ return generateSeoMetadata(buildArticleListingSeoValues(config, options));
169
+ }
170
+ function SEOHeadComponent({ SeoInfo }) {
171
+ return /* @__PURE__ */ jsxRuntime.jsxs(Head__default.default, { children: [
172
+ /* @__PURE__ */ jsxRuntime.jsx("title", { children: SeoInfo.title }),
173
+ /* @__PURE__ */ jsxRuntime.jsx("meta", { charSet: "utf-8" }),
174
+ /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }),
175
+ /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "description", content: SeoInfo.description }),
176
+ /* @__PURE__ */ jsxRuntime.jsx("link", { rel: "canonical", href: SeoInfo.url }),
177
+ /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "keywords", content: SeoInfo.keywords }),
178
+ /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:locale", content: "en_US" }),
179
+ /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:type", content: "website" }),
180
+ /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:title", content: SeoInfo.title }),
181
+ /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:description", content: SeoInfo.description }),
182
+ /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:url", content: SeoInfo.url }),
183
+ /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:site_name", content: SeoInfo.siteName }),
184
+ SeoInfo.image ? /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:image", content: SeoInfo.image }) : null,
185
+ /* @__PURE__ */ jsxRuntime.jsx(
186
+ "meta",
187
+ {
188
+ name: "twitter:card",
189
+ content: SeoInfo.image ? "summary_large_image" : "summary"
190
+ }
191
+ ),
192
+ /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:title", content: SeoInfo.title }),
193
+ /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:description", content: SeoInfo.description }),
194
+ SeoInfo.twitter ? /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:site", content: SeoInfo.twitter }) : null,
195
+ SeoInfo.image ? /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:image", content: SeoInfo.image }) : null,
196
+ /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "referrer", content: "origin" }),
197
+ SeoInfo.manifestUrl ? /* @__PURE__ */ jsxRuntime.jsx("link", { rel: "manifest", href: SeoInfo.manifestUrl, crossOrigin: "use-credentials" }) : null,
198
+ /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "category", content: SeoInfo.title }),
199
+ /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "robots", content: SeoInfo.noindex ? "noindex" : "index" }),
200
+ /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "author", content: SeoInfo.siteName }),
201
+ /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "revisit-after", content: "3 month" })
202
+ ] });
203
+ }
204
+
205
+ exports.SEOHeadComponent = SEOHeadComponent;
206
+ exports.generateArticleListingSeoMetadata = generateArticleListingSeoMetadata;
207
+ exports.generateArticleSeoMetadata = generateArticleSeoMetadata;
208
+ exports.generatePageSeoMetadata = generatePageSeoMetadata;
209
+ exports.generateSeoMetadata = generateSeoMetadata;
210
+ //# sourceMappingURL=next.cjs.map
211
+ //# sourceMappingURL=next.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/cmsImage.ts","../src/seo/seo.builders.ts","../src/seo/next.tsx"],"names":["Head","jsx"],"mappings":"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","file":"next.cjs","sourcesContent":["import { useAsteroidCMSConfig } from \"../provider/context\";\n\n/**\n * Build a canonical media URL from an asset id. Pass `cmsUrl` explicitly when\n * calling outside of React (e.g. SSR loaders, scripts). Inside components,\n * prefer `useCmsImage()`.\n */\nexport function cmsImage(\n id: string | undefined,\n options: { cmsUrl: string; mediaPath?: string },\n): string {\n if (!id) return \"\";\n const base = options.cmsUrl.replace(/\\/+$/, \"\");\n const path = (options.mediaPath ?? \"/media/canonical\").replace(/^\\/?/, \"/\");\n return `${base}${path}/${id}`;\n}\n\n/** Hook variant that pulls `cmsUrl`/`mediaPath` from the provider. */\nexport function useCmsImage(): (id?: string) => string {\n const { cmsUrl, mediaPath } = useAsteroidCMSConfig();\n return (id?: string) => cmsImage(id, { cmsUrl, mediaPath });\n}\n","// Pure builders that turn CMS posts + config into SEO values. Server-safe.\n\nimport { cmsImage } from \"../utils/cmsImage\";\nimport type {\n AsteroidOgImageParams,\n AsteroidPageSeoOptions,\n AsteroidSeoConfig,\n ISeoValues,\n SeoClientProps,\n} from \"./seo.config\";\n\n/** Apply the configured title template, defaulting to `${title} | ${siteName}`. */\nexport function applyTitleTemplate(\n config: AsteroidSeoConfig,\n title: string,\n): string {\n return config.titleTemplate\n ? config.titleTemplate(title)\n : `${title} | ${config.siteName}`;\n}\n\nexport function buildOgImageUrl(\n config: AsteroidSeoConfig,\n params: AsteroidOgImageParams,\n): string | undefined {\n if (config.getOgImageUrl) {\n return config.getOgImageUrl(params);\n }\n\n const palette = config.ogImage?.palette;\n if (!palette) return undefined;\n\n const apiPath = config.ogImage?.apiPath ?? \"/api/og\";\n const base = config.baseUrl.replace(/\\/$/, \"\");\n const searchParams = new URLSearchParams({\n title: params.title,\n type: params.type ?? \"article\",\n siteName: config.siteName,\n bg: palette.background,\n fg: palette.foreground,\n accent: palette.accent,\n });\n\n if (params.subtitle?.trim()) searchParams.set(\"subtitle\", params.subtitle.trim());\n if (params.eyebrow?.trim()) searchParams.set(\"eyebrow\", params.eyebrow.trim());\n if (palette.accentMuted) searchParams.set(\"accentMuted\", palette.accentMuted);\n if (palette.mutedText) searchParams.set(\"muted\", palette.mutedText);\n\n return `${base}${apiPath}?${searchParams.toString()}`;\n}\n\ntype ArticleLike = {\n title: string;\n description?: string;\n meta_description?: string;\n featured_image?: string;\n};\n\n/**\n * Generic per-page SEO builder. Use it directly for landing/marketing pages, or\n * as the base for the article/listing wrappers below. Content-type agnostic.\n */\nexport function buildPageSeoValues(\n config: AsteroidSeoConfig,\n options: AsteroidPageSeoOptions,\n): ISeoValues {\n const base = config.baseUrl.replace(/\\/$/, \"\");\n const path = options.path ?? \"/\";\n const url = `${base}${path.startsWith(\"/\") ? path : `/${path}`}`;\n const description =\n options.description?.trim() ||\n config.defaultDescription ||\n `${options.title} - ${config.siteName}.`;\n\n return {\n title: applyTitleTemplate(config, options.title),\n siteName: config.siteName,\n twitter: config.twitter ?? \"\",\n description,\n url,\n keywords: options.keywords ?? config.defaultKeywords ?? options.title,\n image:\n options.image ??\n buildOgImageUrl(config, {\n title: options.title,\n subtitle: description,\n eyebrow: options.eyebrow,\n type: options.ogType === \"article\" ? \"article\" : \"listing\",\n }),\n noindex: options.noindex ?? config.noindex,\n manifestUrl: config.manifestUrl,\n };\n}\n\nfunction resolveArticleImage<TPost extends ArticleLike>(\n post: TPost,\n config: AsteroidSeoConfig,\n): string | undefined {\n // cmsUrl is required to resolve a real featured image. Without it we fall\n // back to the generated OG image rather than throwing; server builders must\n // never crash a render.\n const featuredImage = config.cmsUrl\n ? cmsImage(post.featured_image, { cmsUrl: config.cmsUrl })\n : \"\";\n\n if (featuredImage) return featuredImage;\n\n const description =\n post.meta_description?.trim() ||\n post.description?.trim() ||\n config.defaultDescription;\n\n return buildOgImageUrl(config, {\n title: post.title,\n subtitle: description,\n eyebrow: config.contentLabel ?? \"Article\",\n type: \"article\",\n });\n}\n\n/** SEO for a single article; works for news, articles, or docs pages. */\nexport function buildArticleSeoValues<TPost extends ArticleLike>(\n post: TPost,\n config: AsteroidSeoConfig,\n slug: string,\n options?: { noindex?: boolean },\n): ISeoValues {\n const articlePath = config.articlePath ?? \"/blog\";\n const url = `${config.baseUrl.replace(/\\/$/, \"\")}${articlePath}/${slug}`;\n const description =\n post.meta_description?.trim() ||\n post.description?.trim() ||\n config.defaultDescription ||\n `Read the latest from ${config.siteName}.`;\n\n return {\n title: applyTitleTemplate(config, post.title),\n siteName: config.siteName,\n twitter: config.twitter ?? \"\",\n description,\n url,\n keywords: config.defaultKeywords ?? post.title,\n image: resolveArticleImage(post, config),\n noindex: options?.noindex ?? config.noindex,\n manifestUrl: config.manifestUrl,\n };\n}\n\n/** SEO for an article collection / category listing (any content type). */\nexport function buildArticleListingSeoValues(\n config: AsteroidSeoConfig,\n options?: { categoryName?: string; categorySlug?: string; noindex?: boolean },\n): ISeoValues {\n const articlePath = config.articlePath ?? \"/blog\";\n const base = config.baseUrl.replace(/\\/$/, \"\");\n const label = config.contentLabel ?? \"Articles\";\n const categoryName = options?.categoryName?.trim();\n const categorySlug = options?.categorySlug?.trim();\n\n const titleText = categoryName ? `${categoryName} ${label}` : label;\n\n const description = categoryName\n ? `Explore ${categoryName} ${label.toLowerCase()}, guides, and the latest updates from ${config.siteName}.`\n : config.defaultDescription ||\n `Browse ${label.toLowerCase()}, insights, and the latest updates from ${config.siteName}.`;\n\n const url = categorySlug\n ? `${base}${articlePath}/category/${categorySlug}`\n : `${base}${articlePath}`;\n\n return {\n title: applyTitleTemplate(config, titleText),\n siteName: config.siteName,\n twitter: config.twitter ?? \"\",\n description,\n url,\n keywords:\n config.defaultKeywords ??\n (categoryName\n ? `${categoryName}, ${config.siteName}`\n : `${config.siteName} ${label.toLowerCase()}`),\n image: buildOgImageUrl(config, {\n title: titleText,\n subtitle: description,\n eyebrow: categoryName ? \"Category\" : label,\n type: \"listing\",\n }),\n noindex: options?.noindex ?? config.noindex,\n manifestUrl: config.manifestUrl,\n };\n}\n\nexport function seoValuesToClientProps(values: ISeoValues): SeoClientProps {\n return {\n title: values.title,\n description: values.description,\n url: values.url,\n siteName: values.siteName,\n keywords: values.keywords,\n twitter: values.twitter,\n image: values.image,\n noindex: values.noindex,\n };\n}\n","// Next.js-only SEO helpers. Imports `next`; this module is exposed only via\n// the `@asteroidcms/core-utils/next` subpath so non-Next bundlers never resolve it.\n\nimport type { Metadata } from \"next\";\nimport Head from \"next/head\";\nimport {\n buildArticleListingSeoValues,\n buildArticleSeoValues,\n buildPageSeoValues,\n} from \"./seo.builders\";\nimport type {\n AsteroidPageSeoOptions,\n AsteroidSeoConfig,\n ISeoValues,\n} from \"./seo.config\";\n\nexport async function generateSeoMetadata(\n SeoInfo: ISeoValues,\n): Promise<Metadata> {\n return {\n title: SeoInfo.title,\n description: SeoInfo.description,\n publisher: SeoInfo.siteName,\n keywords: SeoInfo.keywords,\n category: SeoInfo.title,\n ...(SeoInfo.manifestUrl ? { manifest: SeoInfo.manifestUrl } : {}),\n robots: SeoInfo.noindex\n ? { index: false, follow: true }\n : { index: true, follow: true },\n authors: { name: SeoInfo.siteName },\n referrer: \"origin\",\n abstract: SeoInfo.description,\n alternates: { canonical: SeoInfo.url },\n openGraph: {\n title: SeoInfo.title,\n description: SeoInfo.description,\n url: SeoInfo.url,\n siteName: SeoInfo.siteName,\n locale: \"en_US\",\n type: \"website\",\n ...(SeoInfo.image ? { images: [{ url: SeoInfo.image }] } : {}),\n },\n twitter: {\n title: SeoInfo.title,\n description: SeoInfo.description,\n site: SeoInfo.twitter || undefined,\n card: SeoInfo.image ? \"summary_large_image\" : \"summary\",\n ...(SeoInfo.image ? { images: [SeoInfo.image] } : {}),\n },\n };\n}\n\n/** Generic page metadata; landing pages, marketing pages, etc. */\nexport async function generatePageSeoMetadata(\n config: AsteroidSeoConfig,\n options: AsteroidPageSeoOptions,\n): Promise<Metadata> {\n const metadata = await generateSeoMetadata(buildPageSeoValues(config, options));\n if (options.ogType === \"article\") {\n return { ...metadata, openGraph: { ...metadata.openGraph, type: \"article\" } };\n }\n return metadata;\n}\n\nexport async function generateArticleSeoMetadata<\n TPost extends {\n title: string;\n description?: string;\n meta_description?: string;\n featured_image?: string;\n },\n>(post: TPost, config: AsteroidSeoConfig, slug: string): Promise<Metadata> {\n const seoValues = buildArticleSeoValues(post, config, slug);\n const metadata = await generateSeoMetadata(seoValues);\n return {\n ...metadata,\n openGraph: { ...metadata.openGraph, type: \"article\" },\n };\n}\n\nexport async function generateArticleListingSeoMetadata(\n config: AsteroidSeoConfig,\n options?: { categoryName?: string; categorySlug?: string },\n): Promise<Metadata> {\n return generateSeoMetadata(buildArticleListingSeoValues(config, options));\n}\n\nexport function SEOHeadComponent({ SeoInfo }: { SeoInfo: ISeoValues }) {\n return (\n <Head>\n <title>{SeoInfo.title}</title>\n <meta charSet=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <meta name=\"description\" content={SeoInfo.description} />\n <link rel=\"canonical\" href={SeoInfo.url} />\n <meta name=\"keywords\" content={SeoInfo.keywords} />\n <meta property=\"og:locale\" content=\"en_US\" />\n <meta property=\"og:type\" content=\"website\" />\n <meta property=\"og:title\" content={SeoInfo.title} />\n <meta property=\"og:description\" content={SeoInfo.description} />\n <meta property=\"og:url\" content={SeoInfo.url} />\n <meta property=\"og:site_name\" content={SeoInfo.siteName} />\n {SeoInfo.image ? <meta property=\"og:image\" content={SeoInfo.image} /> : null}\n <meta\n name=\"twitter:card\"\n content={SeoInfo.image ? \"summary_large_image\" : \"summary\"}\n />\n <meta name=\"twitter:title\" content={SeoInfo.title} />\n <meta name=\"twitter:description\" content={SeoInfo.description} />\n {SeoInfo.twitter ? <meta name=\"twitter:site\" content={SeoInfo.twitter} /> : null}\n {SeoInfo.image ? <meta name=\"twitter:image\" content={SeoInfo.image} /> : null}\n <meta name=\"referrer\" content=\"origin\" />\n {SeoInfo.manifestUrl ? <link rel=\"manifest\" href={SeoInfo.manifestUrl} crossOrigin=\"use-credentials\" /> : null}\n <meta name=\"category\" content={SeoInfo.title} />\n <meta name=\"robots\" content={SeoInfo.noindex ? \"noindex\" : \"index\"} />\n <meta name=\"author\" content={SeoInfo.siteName} />\n <meta name=\"revisit-after\" content=\"3 month\" />\n </Head>\n );\n}\n"]}
@@ -0,0 +1,114 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { Metadata } from 'next';
3
+
4
+ interface ISeoValues {
5
+ title: string;
6
+ siteName: string;
7
+ twitter: string;
8
+ description: string;
9
+ url: string;
10
+ keywords: string;
11
+ image?: string;
12
+ noindex?: boolean;
13
+ manifestUrl?: string;
14
+ }
15
+ /** Theme colors used when generating dynamic OG images. */
16
+ interface AsteroidOgImagePalette {
17
+ background: string;
18
+ foreground: string;
19
+ accent: string;
20
+ accentMuted?: string;
21
+ mutedText?: string;
22
+ }
23
+ interface AsteroidOgImageParams {
24
+ title: string;
25
+ subtitle?: string;
26
+ eyebrow?: string;
27
+ type?: "article" | "listing";
28
+ }
29
+ /** Options for the generic per-page SEO builder (landing pages, etc.). */
30
+ interface AsteroidPageSeoOptions {
31
+ /** Raw page title; the config `titleTemplate` (or the `| siteName` default) is applied. */
32
+ title: string;
33
+ description?: string;
34
+ /** Path appended to `baseUrl`. Default: `/`. */
35
+ path?: string;
36
+ keywords?: string;
37
+ /** Explicit social image URL. If absent, a generated OG image is used. */
38
+ image?: string;
39
+ /** Eyebrow label for the generated OG image. */
40
+ eyebrow?: string;
41
+ /** Open Graph object type. Default: `website`. */
42
+ ogType?: "website" | "article";
43
+ /** Mark the page as `noindex` for search engines. */
44
+ noindex?: boolean;
45
+ }
46
+ /** Site-level identity used to build the Organization/WebSite JSON-LD graph. */
47
+ interface AsteroidOrganizationConfig {
48
+ logoUrl?: string;
49
+ contactEmail?: string;
50
+ contactPhone?: string;
51
+ address?: {
52
+ street?: string;
53
+ city?: string;
54
+ country?: string;
55
+ };
56
+ /** Generic list of profile URLs (LinkedIn, X, etc.). Maps to schema.org `sameAs`. */
57
+ socials?: string[];
58
+ }
59
+ /** Site-level SEO config passed into the headless Asteroid content components. */
60
+ interface AsteroidSeoConfig {
61
+ siteName: string;
62
+ baseUrl: string;
63
+ twitter?: string;
64
+ defaultDescription?: string;
65
+ defaultKeywords?: string;
66
+ /** Path prefix for the article collection (e.g. `/blog`, `/news`, `/docs`). Default: `/blog` */
67
+ articlePath?: string;
68
+ /**
69
+ * Human label for the article collection, used in default titles/eyebrows
70
+ * (e.g. "Blog", "News", "Documentation"). Default: `"Articles"`.
71
+ */
72
+ contentLabel?: string;
73
+ /**
74
+ * Override how a raw page/article title becomes the final `<title>`.
75
+ * Default: `(title) => `${title} | ${siteName}``.
76
+ */
77
+ titleTemplate?: (title: string) => string;
78
+ ogImage?: {
79
+ palette: AsteroidOgImagePalette;
80
+ /** OG image API route path. Default: `/api/og` */
81
+ apiPath?: string;
82
+ };
83
+ /** Override dynamic OG image URL generation (e.g. custom CDN or renderer). */
84
+ getOgImageUrl?: (params: AsteroidOgImageParams) => string | undefined;
85
+ /** Site identity for the Organization/WebSite JSON-LD graph. */
86
+ organization?: AsteroidOrganizationConfig;
87
+ /** Extra schema.org @graph nodes (e.g. a ProfessionalService node). */
88
+ extraJsonLdNodes?: object[];
89
+ /** CMS base URL for resolving featured-image URLs. Required server-side for featured images. */
90
+ cmsUrl?: string;
91
+ /** URL to a PWA manifest.json. When set, a `<link rel="manifest">` is emitted. */
92
+ manifestUrl?: string;
93
+ /** Site-wide default robots policy. Per-page overrides are available on builder options. */
94
+ noindex?: boolean;
95
+ }
96
+
97
+ declare function generateSeoMetadata(SeoInfo: ISeoValues): Promise<Metadata>;
98
+ /** Generic page metadata; landing pages, marketing pages, etc. */
99
+ declare function generatePageSeoMetadata(config: AsteroidSeoConfig, options: AsteroidPageSeoOptions): Promise<Metadata>;
100
+ declare function generateArticleSeoMetadata<TPost extends {
101
+ title: string;
102
+ description?: string;
103
+ meta_description?: string;
104
+ featured_image?: string;
105
+ }>(post: TPost, config: AsteroidSeoConfig, slug: string): Promise<Metadata>;
106
+ declare function generateArticleListingSeoMetadata(config: AsteroidSeoConfig, options?: {
107
+ categoryName?: string;
108
+ categorySlug?: string;
109
+ }): Promise<Metadata>;
110
+ declare function SEOHeadComponent({ SeoInfo }: {
111
+ SeoInfo: ISeoValues;
112
+ }): react_jsx_runtime.JSX.Element;
113
+
114
+ export { SEOHeadComponent, generateArticleListingSeoMetadata, generateArticleSeoMetadata, generatePageSeoMetadata, generateSeoMetadata };
package/dist/next.d.ts ADDED
@@ -0,0 +1,114 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { Metadata } from 'next';
3
+
4
+ interface ISeoValues {
5
+ title: string;
6
+ siteName: string;
7
+ twitter: string;
8
+ description: string;
9
+ url: string;
10
+ keywords: string;
11
+ image?: string;
12
+ noindex?: boolean;
13
+ manifestUrl?: string;
14
+ }
15
+ /** Theme colors used when generating dynamic OG images. */
16
+ interface AsteroidOgImagePalette {
17
+ background: string;
18
+ foreground: string;
19
+ accent: string;
20
+ accentMuted?: string;
21
+ mutedText?: string;
22
+ }
23
+ interface AsteroidOgImageParams {
24
+ title: string;
25
+ subtitle?: string;
26
+ eyebrow?: string;
27
+ type?: "article" | "listing";
28
+ }
29
+ /** Options for the generic per-page SEO builder (landing pages, etc.). */
30
+ interface AsteroidPageSeoOptions {
31
+ /** Raw page title; the config `titleTemplate` (or the `| siteName` default) is applied. */
32
+ title: string;
33
+ description?: string;
34
+ /** Path appended to `baseUrl`. Default: `/`. */
35
+ path?: string;
36
+ keywords?: string;
37
+ /** Explicit social image URL. If absent, a generated OG image is used. */
38
+ image?: string;
39
+ /** Eyebrow label for the generated OG image. */
40
+ eyebrow?: string;
41
+ /** Open Graph object type. Default: `website`. */
42
+ ogType?: "website" | "article";
43
+ /** Mark the page as `noindex` for search engines. */
44
+ noindex?: boolean;
45
+ }
46
+ /** Site-level identity used to build the Organization/WebSite JSON-LD graph. */
47
+ interface AsteroidOrganizationConfig {
48
+ logoUrl?: string;
49
+ contactEmail?: string;
50
+ contactPhone?: string;
51
+ address?: {
52
+ street?: string;
53
+ city?: string;
54
+ country?: string;
55
+ };
56
+ /** Generic list of profile URLs (LinkedIn, X, etc.). Maps to schema.org `sameAs`. */
57
+ socials?: string[];
58
+ }
59
+ /** Site-level SEO config passed into the headless Asteroid content components. */
60
+ interface AsteroidSeoConfig {
61
+ siteName: string;
62
+ baseUrl: string;
63
+ twitter?: string;
64
+ defaultDescription?: string;
65
+ defaultKeywords?: string;
66
+ /** Path prefix for the article collection (e.g. `/blog`, `/news`, `/docs`). Default: `/blog` */
67
+ articlePath?: string;
68
+ /**
69
+ * Human label for the article collection, used in default titles/eyebrows
70
+ * (e.g. "Blog", "News", "Documentation"). Default: `"Articles"`.
71
+ */
72
+ contentLabel?: string;
73
+ /**
74
+ * Override how a raw page/article title becomes the final `<title>`.
75
+ * Default: `(title) => `${title} | ${siteName}``.
76
+ */
77
+ titleTemplate?: (title: string) => string;
78
+ ogImage?: {
79
+ palette: AsteroidOgImagePalette;
80
+ /** OG image API route path. Default: `/api/og` */
81
+ apiPath?: string;
82
+ };
83
+ /** Override dynamic OG image URL generation (e.g. custom CDN or renderer). */
84
+ getOgImageUrl?: (params: AsteroidOgImageParams) => string | undefined;
85
+ /** Site identity for the Organization/WebSite JSON-LD graph. */
86
+ organization?: AsteroidOrganizationConfig;
87
+ /** Extra schema.org @graph nodes (e.g. a ProfessionalService node). */
88
+ extraJsonLdNodes?: object[];
89
+ /** CMS base URL for resolving featured-image URLs. Required server-side for featured images. */
90
+ cmsUrl?: string;
91
+ /** URL to a PWA manifest.json. When set, a `<link rel="manifest">` is emitted. */
92
+ manifestUrl?: string;
93
+ /** Site-wide default robots policy. Per-page overrides are available on builder options. */
94
+ noindex?: boolean;
95
+ }
96
+
97
+ declare function generateSeoMetadata(SeoInfo: ISeoValues): Promise<Metadata>;
98
+ /** Generic page metadata; landing pages, marketing pages, etc. */
99
+ declare function generatePageSeoMetadata(config: AsteroidSeoConfig, options: AsteroidPageSeoOptions): Promise<Metadata>;
100
+ declare function generateArticleSeoMetadata<TPost extends {
101
+ title: string;
102
+ description?: string;
103
+ meta_description?: string;
104
+ featured_image?: string;
105
+ }>(post: TPost, config: AsteroidSeoConfig, slug: string): Promise<Metadata>;
106
+ declare function generateArticleListingSeoMetadata(config: AsteroidSeoConfig, options?: {
107
+ categoryName?: string;
108
+ categorySlug?: string;
109
+ }): Promise<Metadata>;
110
+ declare function SEOHeadComponent({ SeoInfo }: {
111
+ SeoInfo: ISeoValues;
112
+ }): react_jsx_runtime.JSX.Element;
113
+
114
+ export { SEOHeadComponent, generateArticleListingSeoMetadata, generateArticleSeoMetadata, generatePageSeoMetadata, generateSeoMetadata };