@asteroidcms/core-utils 0.1.6 → 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/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { HttpLink, ApolloClient, InMemoryCache, ApolloLink, gql, CombinedGraphQLErrors, CombinedProtocolErrors } from '@apollo/client';
2
2
  import { SetContextLink } from '@apollo/client/link/context';
3
3
  import { ErrorLink } from '@apollo/client/link/error';
4
+ import { jsxs, jsx } from 'react/jsx-runtime';
4
5
 
5
6
  // src/apollo/createApolloClient.ts
6
7
  function createErrorLink(onError) {
@@ -1062,6 +1063,372 @@ function extractHeadingsFromElement(root, options = {}) {
1062
1063
  return out;
1063
1064
  }
1064
1065
 
1065
- export { buildCmsMutation, buildCmsQuery, cmsImage, cmsMutate, createApolloClient, extractHeadingsFromElement, extractHeadingsFromHtml, fetchCmsContent, getContentReadTime, parseRichText, removeEmptyParagraphs, slugify };
1066
+ // src/seo/seo.builders.ts
1067
+ function applyTitleTemplate(config, title) {
1068
+ return config.titleTemplate ? config.titleTemplate(title) : `${title} | ${config.siteName}`;
1069
+ }
1070
+ function buildOgImageUrl(config, params) {
1071
+ if (config.getOgImageUrl) {
1072
+ return config.getOgImageUrl(params);
1073
+ }
1074
+ const palette = config.ogImage?.palette;
1075
+ if (!palette) return void 0;
1076
+ const apiPath = config.ogImage?.apiPath ?? "/api/og";
1077
+ const base = config.baseUrl.replace(/\/$/, "");
1078
+ const searchParams = new URLSearchParams({
1079
+ title: params.title,
1080
+ type: params.type ?? "article",
1081
+ siteName: config.siteName,
1082
+ bg: palette.background,
1083
+ fg: palette.foreground,
1084
+ accent: palette.accent
1085
+ });
1086
+ if (params.subtitle?.trim()) searchParams.set("subtitle", params.subtitle.trim());
1087
+ if (params.eyebrow?.trim()) searchParams.set("eyebrow", params.eyebrow.trim());
1088
+ if (palette.accentMuted) searchParams.set("accentMuted", palette.accentMuted);
1089
+ if (palette.mutedText) searchParams.set("muted", palette.mutedText);
1090
+ return `${base}${apiPath}?${searchParams.toString()}`;
1091
+ }
1092
+ function buildPageSeoValues(config, options) {
1093
+ const base = config.baseUrl.replace(/\/$/, "");
1094
+ const path = options.path ?? "/";
1095
+ const url = `${base}${path.startsWith("/") ? path : `/${path}`}`;
1096
+ const description = options.description?.trim() || config.defaultDescription || `${options.title} - ${config.siteName}.`;
1097
+ return {
1098
+ title: applyTitleTemplate(config, options.title),
1099
+ siteName: config.siteName,
1100
+ twitter: config.twitter ?? "",
1101
+ description,
1102
+ url,
1103
+ keywords: options.keywords ?? config.defaultKeywords ?? options.title,
1104
+ image: options.image ?? buildOgImageUrl(config, {
1105
+ title: options.title,
1106
+ subtitle: description,
1107
+ eyebrow: options.eyebrow,
1108
+ type: options.ogType === "article" ? "article" : "listing"
1109
+ }),
1110
+ noindex: options.noindex ?? config.noindex,
1111
+ manifestUrl: config.manifestUrl
1112
+ };
1113
+ }
1114
+ function resolveArticleImage(post, config) {
1115
+ const featuredImage = config.cmsUrl ? cmsImage(post.featured_image, { cmsUrl: config.cmsUrl }) : "";
1116
+ if (featuredImage) return featuredImage;
1117
+ const description = post.meta_description?.trim() || post.description?.trim() || config.defaultDescription;
1118
+ return buildOgImageUrl(config, {
1119
+ title: post.title,
1120
+ subtitle: description,
1121
+ eyebrow: config.contentLabel ?? "Article",
1122
+ type: "article"
1123
+ });
1124
+ }
1125
+ function buildArticleSeoValues(post, config, slug, options) {
1126
+ const articlePath = config.articlePath ?? "/blog";
1127
+ const url = `${config.baseUrl.replace(/\/$/, "")}${articlePath}/${slug}`;
1128
+ const description = post.meta_description?.trim() || post.description?.trim() || config.defaultDescription || `Read the latest from ${config.siteName}.`;
1129
+ return {
1130
+ title: applyTitleTemplate(config, post.title),
1131
+ siteName: config.siteName,
1132
+ twitter: config.twitter ?? "",
1133
+ description,
1134
+ url,
1135
+ keywords: config.defaultKeywords ?? post.title,
1136
+ image: resolveArticleImage(post, config),
1137
+ noindex: options?.noindex ?? config.noindex,
1138
+ manifestUrl: config.manifestUrl
1139
+ };
1140
+ }
1141
+ function buildArticleListingSeoValues(config, options) {
1142
+ const articlePath = config.articlePath ?? "/blog";
1143
+ const base = config.baseUrl.replace(/\/$/, "");
1144
+ const label = config.contentLabel ?? "Articles";
1145
+ const categoryName = options?.categoryName?.trim();
1146
+ const categorySlug = options?.categorySlug?.trim();
1147
+ const titleText = categoryName ? `${categoryName} ${label}` : label;
1148
+ 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}.`;
1149
+ const url = categorySlug ? `${base}${articlePath}/category/${categorySlug}` : `${base}${articlePath}`;
1150
+ return {
1151
+ title: applyTitleTemplate(config, titleText),
1152
+ siteName: config.siteName,
1153
+ twitter: config.twitter ?? "",
1154
+ description,
1155
+ url,
1156
+ keywords: config.defaultKeywords ?? (categoryName ? `${categoryName}, ${config.siteName}` : `${config.siteName} ${label.toLowerCase()}`),
1157
+ image: buildOgImageUrl(config, {
1158
+ title: titleText,
1159
+ subtitle: description,
1160
+ eyebrow: categoryName ? "Category" : label,
1161
+ type: "listing"
1162
+ }),
1163
+ noindex: options?.noindex ?? config.noindex,
1164
+ manifestUrl: config.manifestUrl
1165
+ };
1166
+ }
1167
+ function seoValuesToClientProps(values) {
1168
+ return {
1169
+ title: values.title,
1170
+ description: values.description,
1171
+ url: values.url,
1172
+ siteName: values.siteName,
1173
+ keywords: values.keywords,
1174
+ twitter: values.twitter,
1175
+ image: values.image,
1176
+ noindex: values.noindex
1177
+ };
1178
+ }
1179
+
1180
+ // src/seo/jsonld.ts
1181
+ function buildSiteJsonLd(config) {
1182
+ const siteUrl = config.baseUrl.replace(/\/$/, "");
1183
+ const org = config.organization;
1184
+ const organizationNode = {
1185
+ "@type": "Organization",
1186
+ "@id": `${siteUrl}/#organization`,
1187
+ name: config.siteName,
1188
+ legalName: config.siteName,
1189
+ url: siteUrl
1190
+ };
1191
+ if (org?.logoUrl) {
1192
+ organizationNode.logo = { "@type": "ImageObject", url: org.logoUrl };
1193
+ }
1194
+ if (org?.contactPhone || org?.contactEmail) {
1195
+ organizationNode.contactPoint = {
1196
+ "@type": "ContactPoint",
1197
+ ...org.contactPhone ? { telephone: org.contactPhone } : {},
1198
+ ...org.contactEmail ? { email: org.contactEmail } : {},
1199
+ contactType: "customer service"
1200
+ };
1201
+ }
1202
+ if (org?.address) {
1203
+ organizationNode.address = {
1204
+ "@type": "PostalAddress",
1205
+ ...org.address.street ? { streetAddress: org.address.street } : {},
1206
+ ...org.address.city ? { addressLocality: org.address.city } : {},
1207
+ ...org.address.country ? { addressCountry: org.address.country } : {}
1208
+ };
1209
+ }
1210
+ if (org?.socials?.length) {
1211
+ organizationNode.sameAs = org.socials;
1212
+ }
1213
+ const websiteNode = {
1214
+ "@type": "WebSite",
1215
+ "@id": `${siteUrl}/#website`,
1216
+ url: siteUrl,
1217
+ name: config.siteName,
1218
+ publisher: { "@id": `${siteUrl}/#organization` },
1219
+ inLanguage: "en-US"
1220
+ };
1221
+ if (config.defaultDescription) {
1222
+ websiteNode.description = config.defaultDescription;
1223
+ }
1224
+ return {
1225
+ "@context": "https://schema.org",
1226
+ "@graph": [organizationNode, websiteNode, ...config.extraJsonLdNodes ?? []]
1227
+ };
1228
+ }
1229
+ function buildArticleJsonLd(props) {
1230
+ return {
1231
+ "@context": "https://schema.org",
1232
+ "@type": props.articleType ?? "Article",
1233
+ headline: props.title,
1234
+ description: props.description,
1235
+ url: props.url,
1236
+ ...props.image ? { image: props.image } : {},
1237
+ ...props.publishedTime ? { datePublished: props.publishedTime } : {},
1238
+ ...props.category ? { articleSection: props.category } : {},
1239
+ ...props.tags && props.tags.length > 0 ? { keywords: props.tags.join(", ") } : {},
1240
+ author: { "@type": "Person", name: props.authorName || props.siteName },
1241
+ publisher: { "@id": `${props.siteUrl}/#organization` },
1242
+ isPartOf: { "@id": `${props.siteUrl}/#website` },
1243
+ inLanguage: "en-US"
1244
+ };
1245
+ }
1246
+ function buildCollectionJsonLd(props) {
1247
+ return {
1248
+ "@context": "https://schema.org",
1249
+ "@type": "CollectionPage",
1250
+ name: props.name,
1251
+ description: props.description,
1252
+ url: props.url,
1253
+ isPartOf: { "@id": `${props.siteUrl}/#website` },
1254
+ publisher: { "@id": `${props.siteUrl}/#organization` },
1255
+ inLanguage: "en-US"
1256
+ };
1257
+ }
1258
+ function buildWebPageJsonLd(props) {
1259
+ return {
1260
+ "@context": "https://schema.org",
1261
+ "@type": "WebPage",
1262
+ name: props.name,
1263
+ description: props.description,
1264
+ url: props.url,
1265
+ isPartOf: { "@id": `${props.siteUrl}/#website` },
1266
+ publisher: { "@id": `${props.siteUrl}/#organization` },
1267
+ inLanguage: "en-US"
1268
+ };
1269
+ }
1270
+ var DEFAULT_PALETTE = {
1271
+ background: "#0C0D10",
1272
+ foreground: "#FFFFFF",
1273
+ accent: "#EDB435",
1274
+ accentMuted: "rgba(237,180,53,0.22)",
1275
+ mutedText: "rgba(255,255,255,0.65)"
1276
+ };
1277
+ function parseOgImageSearchParams(searchParams) {
1278
+ const palette = {
1279
+ background: searchParams.get("bg") ?? DEFAULT_PALETTE.background,
1280
+ foreground: searchParams.get("fg") ?? DEFAULT_PALETTE.foreground,
1281
+ accent: searchParams.get("accent") ?? DEFAULT_PALETTE.accent,
1282
+ accentMuted: searchParams.get("accentMuted") ?? DEFAULT_PALETTE.accentMuted,
1283
+ mutedText: searchParams.get("muted") ?? DEFAULT_PALETTE.mutedText
1284
+ };
1285
+ return {
1286
+ title: searchParams.get("title")?.trim() || "Articles",
1287
+ subtitle: searchParams.get("subtitle")?.trim() || void 0,
1288
+ eyebrow: searchParams.get("eyebrow")?.trim() || void 0,
1289
+ siteName: searchParams.get("siteName")?.trim() || void 0,
1290
+ variant: searchParams.get("type") === "listing" ? "listing" : "article",
1291
+ palette
1292
+ };
1293
+ }
1294
+ function truncateText(value, maxLength) {
1295
+ const trimmed = value.trim();
1296
+ if (trimmed.length <= maxLength) return trimmed;
1297
+ return `${trimmed.slice(0, maxLength - 3).trimEnd()}...`;
1298
+ }
1299
+ function toSatoriColor(color) {
1300
+ const rgbaMatch = color.match(
1301
+ /^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)(?:\s*,\s*([\d.]+))?\s*\)$/i
1302
+ );
1303
+ if (!rgbaMatch) return color;
1304
+ const [, r, g, b, a = "1"] = rgbaMatch;
1305
+ const hex = [r, g, b].map((channel) => Number(channel).toString(16).padStart(2, "0")).join("");
1306
+ const alpha = Math.round(parseFloat(a) * 255).toString(16).padStart(2, "0");
1307
+ return `#${hex}${alpha}`;
1308
+ }
1309
+ function OgImageContent({
1310
+ title,
1311
+ subtitle,
1312
+ eyebrow,
1313
+ siteName,
1314
+ palette,
1315
+ variant = "article"
1316
+ }) {
1317
+ const displayTitle = truncateText(title, 120);
1318
+ const displaySubtitle = subtitle ? truncateText(subtitle, 160) : void 0;
1319
+ const displayEyebrow = eyebrow?.trim() || (variant === "listing" ? "Articles" : "Article");
1320
+ const background = toSatoriColor(palette.background);
1321
+ const foreground = toSatoriColor(palette.foreground);
1322
+ const accent = toSatoriColor(palette.accent);
1323
+ const accentMuted = toSatoriColor(palette.accentMuted ?? palette.accent);
1324
+ const mutedText = toSatoriColor(palette.mutedText ?? palette.foreground);
1325
+ return /* @__PURE__ */ jsxs(
1326
+ "div",
1327
+ {
1328
+ style: {
1329
+ width: 1200,
1330
+ height: 630,
1331
+ display: "flex",
1332
+ flexDirection: "column",
1333
+ background,
1334
+ color: foreground,
1335
+ fontFamily: "sans-serif"
1336
+ },
1337
+ children: [
1338
+ /* @__PURE__ */ jsx("div", { style: { height: 6, background: accent } }),
1339
+ /* @__PURE__ */ jsxs(
1340
+ "div",
1341
+ {
1342
+ style: {
1343
+ display: "flex",
1344
+ flexDirection: "column",
1345
+ justifyContent: "space-between",
1346
+ flex: 1,
1347
+ padding: "66px 80px 72px"
1348
+ },
1349
+ children: [
1350
+ /* @__PURE__ */ jsx(
1351
+ "div",
1352
+ {
1353
+ style: {
1354
+ display: "flex",
1355
+ alignItems: "center",
1356
+ padding: "10px 18px",
1357
+ borderRadius: 999,
1358
+ border: `1px solid ${accentMuted}`,
1359
+ color: accent,
1360
+ fontSize: 22,
1361
+ fontWeight: 700,
1362
+ letterSpacing: 0,
1363
+ textTransform: "uppercase",
1364
+ alignSelf: "flex-start"
1365
+ },
1366
+ children: displayEyebrow
1367
+ }
1368
+ ),
1369
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", maxWidth: 980 }, children: [
1370
+ /* @__PURE__ */ jsx(
1371
+ "div",
1372
+ {
1373
+ style: {
1374
+ fontSize: displayTitle.length > 70 ? 52 : 64,
1375
+ fontWeight: 800,
1376
+ lineHeight: 1.05,
1377
+ letterSpacing: 0,
1378
+ color: foreground
1379
+ },
1380
+ children: displayTitle
1381
+ }
1382
+ ),
1383
+ displaySubtitle ? /* @__PURE__ */ jsx(
1384
+ "div",
1385
+ {
1386
+ style: {
1387
+ marginTop: 24,
1388
+ fontSize: 28,
1389
+ lineHeight: 1.4,
1390
+ color: mutedText,
1391
+ maxWidth: 900
1392
+ },
1393
+ children: displaySubtitle
1394
+ }
1395
+ ) : null
1396
+ ] }),
1397
+ siteName ? /* @__PURE__ */ jsxs(
1398
+ "div",
1399
+ {
1400
+ style: {
1401
+ display: "flex",
1402
+ alignItems: "center",
1403
+ justifyContent: "space-between",
1404
+ fontSize: 24,
1405
+ fontWeight: 700,
1406
+ color: accent
1407
+ },
1408
+ children: [
1409
+ /* @__PURE__ */ jsx("span", { children: siteName }),
1410
+ /* @__PURE__ */ jsx(
1411
+ "div",
1412
+ {
1413
+ style: {
1414
+ width: 72,
1415
+ height: 4,
1416
+ borderRadius: 999,
1417
+ background: accent
1418
+ }
1419
+ }
1420
+ )
1421
+ ]
1422
+ }
1423
+ ) : /* @__PURE__ */ jsx("div", { style: { display: "flex" } })
1424
+ ]
1425
+ }
1426
+ )
1427
+ ]
1428
+ }
1429
+ );
1430
+ }
1431
+
1432
+ export { OgImageContent, applyTitleTemplate, buildArticleJsonLd, buildArticleListingSeoValues, buildArticleSeoValues, buildCmsMutation, buildCmsQuery, buildCollectionJsonLd, buildOgImageUrl, buildPageSeoValues, buildSiteJsonLd, buildWebPageJsonLd, cmsImage, cmsMutate, createApolloClient, extractHeadingsFromElement, extractHeadingsFromHtml, fetchCmsContent, getContentReadTime, parseOgImageSearchParams, parseRichText, removeEmptyParagraphs, seoValuesToClientProps, slugify };
1066
1433
  //# sourceMappingURL=index.js.map
1067
1434
  //# sourceMappingURL=index.js.map