@tetherto/dev-websites-core 0.2.0 → 0.2.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/cms/index.d.mts +3 -0
- package/dist/cms/index.mjs +3 -0
- package/dist/cms-5bS91IRZ.mjs +1419 -0
- package/dist/cms-5bS91IRZ.mjs.map +1 -0
- package/dist/i18n/index.d.mts +2 -0
- package/dist/i18n/index.mjs +2 -0
- package/dist/i18n-Dy3VrbBw.mjs +10 -0
- package/dist/i18n-Dy3VrbBw.mjs.map +1 -0
- package/dist/index-9B8SfwdR.d.mts +214 -0
- package/dist/index-9B8SfwdR.d.mts.map +1 -0
- package/dist/index-D36IEwUg.d.mts +8 -0
- package/dist/index-D36IEwUg.d.mts.map +1 -0
- package/dist/index-Dyi7DDoO.d.mts +23 -0
- package/dist/index-Dyi7DDoO.d.mts.map +1 -0
- package/dist/index-tTs9h9BS.d.mts +63 -0
- package/dist/index-tTs9h9BS.d.mts.map +1 -0
- package/dist/index.d.mts +6 -0
- package/dist/index.mjs +7 -0
- package/dist/logger-BPURnavq.mjs +44 -0
- package/dist/logger-BPURnavq.mjs.map +1 -0
- package/dist/readMediaBuffer-1C3QTpkO.d.mts +15 -0
- package/dist/readMediaBuffer-1C3QTpkO.d.mts.map +1 -0
- package/dist/readMediaBuffer-DWkTbYVn.mjs +62 -0
- package/dist/readMediaBuffer-DWkTbYVn.mjs.map +1 -0
- package/dist/seo/index.d.mts +2 -0
- package/dist/seo/index.mjs +2 -0
- package/dist/seo-e5XjgQ7l.mjs +124 -0
- package/dist/seo-e5XjgQ7l.mjs.map +1 -0
- package/dist/utils/index.d.mts +2 -0
- package/dist/utils/index.mjs +3 -0
- package/dist/utils-CVVQQltg.mjs +18 -0
- package/dist/utils-CVVQQltg.mjs.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { t as createLogger } from "./logger-BPURnavq.mjs";
|
|
2
|
+
import { readFile } from "fs/promises";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { GetObjectCommand, S3Client } from "@aws-sdk/client-s3";
|
|
5
|
+
//#region src/cms/payload/readMediaBuffer.ts
|
|
6
|
+
/**
|
|
7
|
+
* Read a Payload-managed Media file's raw bytes, bypassing the public HTTP
|
|
8
|
+
* surface. Uses S3 when `AWS_S3_BUCKET` is set; otherwise reads from
|
|
9
|
+
* `<cwd>/media/<filename>` (Payload's default upload staticDir for `media`).
|
|
10
|
+
*/
|
|
11
|
+
const log = createLogger("readPayloadMedia");
|
|
12
|
+
let cachedS3Client = null;
|
|
13
|
+
function isS3Enabled() {
|
|
14
|
+
return Boolean(process.env.AWS_S3_BUCKET);
|
|
15
|
+
}
|
|
16
|
+
function getS3Client() {
|
|
17
|
+
if (cachedS3Client) return cachedS3Client;
|
|
18
|
+
const endpoint = process.env.AWS_S3_ENDPOINT?.trim();
|
|
19
|
+
const clientConfig = {
|
|
20
|
+
region: process.env.AWS_S3_REGION ?? "us-east-1",
|
|
21
|
+
...endpoint ? { endpoint } : {},
|
|
22
|
+
...process.env.AWS_S3_FORCE_PATH_STYLE === "true" ? { forcePathStyle: true } : {}
|
|
23
|
+
};
|
|
24
|
+
const accessKeyId = process.env.AWS_ACCESS_KEY_ID;
|
|
25
|
+
const secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;
|
|
26
|
+
if (accessKeyId && secretAccessKey) clientConfig.credentials = {
|
|
27
|
+
accessKeyId,
|
|
28
|
+
secretAccessKey
|
|
29
|
+
};
|
|
30
|
+
cachedS3Client = new S3Client(clientConfig);
|
|
31
|
+
return cachedS3Client;
|
|
32
|
+
}
|
|
33
|
+
async function readFromS3(filename) {
|
|
34
|
+
const bucket = process.env.AWS_S3_BUCKET;
|
|
35
|
+
if (!bucket) throw new Error("AWS_S3_BUCKET is not set — cannot read media from S3");
|
|
36
|
+
const response = await getS3Client().send(new GetObjectCommand({
|
|
37
|
+
Bucket: bucket,
|
|
38
|
+
Key: filename
|
|
39
|
+
}));
|
|
40
|
+
if (!response.Body) throw new Error(`S3 returned an empty body for media file: ${filename}`);
|
|
41
|
+
const bytes = await response.Body.transformToByteArray();
|
|
42
|
+
return Buffer.from(bytes);
|
|
43
|
+
}
|
|
44
|
+
async function readFromDisk(filename) {
|
|
45
|
+
const filePath = join(process.cwd(), "media", filename);
|
|
46
|
+
return readFile(filePath);
|
|
47
|
+
}
|
|
48
|
+
async function readPayloadMediaBuffer(media) {
|
|
49
|
+
const filename = media.filename?.trim();
|
|
50
|
+
if (!filename) throw new Error("Media document has no filename");
|
|
51
|
+
const source = isS3Enabled() ? "s3" : "disk";
|
|
52
|
+
log.debug(`reading media filename=${filename} source=${source}`);
|
|
53
|
+
return source === "s3" ? readFromS3(filename) : readFromDisk(filename);
|
|
54
|
+
}
|
|
55
|
+
async function readPayloadMediaAsDataUrl(media) {
|
|
56
|
+
const buffer = await readPayloadMediaBuffer(media);
|
|
57
|
+
return `data:${media.mimeType?.trim() || "image/jpeg"};base64,${buffer.toString("base64")}`;
|
|
58
|
+
}
|
|
59
|
+
//#endregion
|
|
60
|
+
export { readPayloadMediaBuffer as n, readPayloadMediaAsDataUrl as t };
|
|
61
|
+
|
|
62
|
+
//# sourceMappingURL=readMediaBuffer-DWkTbYVn.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readMediaBuffer-DWkTbYVn.mjs","names":[],"sources":["../src/cms/payload/readMediaBuffer.ts"],"sourcesContent":["/**\n * Read a Payload-managed Media file's raw bytes, bypassing the public HTTP\n * surface. Uses S3 when `AWS_S3_BUCKET` is set; otherwise reads from\n * `<cwd>/media/<filename>` (Payload's default upload staticDir for `media`).\n */\n\nimport { readFile } from 'fs/promises'\nimport { join } from 'path'\n\nimport { GetObjectCommand, S3Client, type S3ClientConfig } from '@aws-sdk/client-s3'\n\nimport { createLogger } from '../../utils/logger/logger.js'\n\nconst log = createLogger('readPayloadMedia')\n\nexport type PayloadMedia = {\n filename?: string | null\n mimeType?: string | null\n}\n\nlet cachedS3Client: S3Client | null = null\n\nfunction isS3Enabled(): boolean {\n return Boolean(process.env.AWS_S3_BUCKET)\n}\n\nfunction getS3Client(): S3Client {\n if (cachedS3Client) return cachedS3Client\n const endpoint = process.env.AWS_S3_ENDPOINT?.trim()\n const clientConfig: S3ClientConfig = {\n region: process.env.AWS_S3_REGION ?? 'us-east-1',\n ...(endpoint ? { endpoint } : {}),\n ...(process.env.AWS_S3_FORCE_PATH_STYLE === 'true' ? { forcePathStyle: true } : {}),\n }\n const accessKeyId = process.env.AWS_ACCESS_KEY_ID\n const secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY\n if (accessKeyId && secretAccessKey) {\n clientConfig.credentials = { accessKeyId, secretAccessKey }\n }\n cachedS3Client = new S3Client(clientConfig)\n return cachedS3Client\n}\n\nasync function readFromS3(filename: string): Promise<Buffer> {\n const bucket = process.env.AWS_S3_BUCKET\n if (!bucket) {\n throw new Error('AWS_S3_BUCKET is not set — cannot read media from S3')\n }\n const client = getS3Client()\n const response = await client.send(new GetObjectCommand({ Bucket: bucket, Key: filename }))\n if (!response.Body) {\n throw new Error(`S3 returned an empty body for media file: ${filename}`)\n }\n\n const bytes = await response.Body.transformToByteArray()\n return Buffer.from(bytes)\n}\n\nasync function readFromDisk(filename: string): Promise<Buffer> {\n const filePath = join(process.cwd(), 'media', filename)\n return readFile(filePath)\n}\n\nexport async function readPayloadMediaBuffer(media: PayloadMedia): Promise<Buffer> {\n const filename = media.filename?.trim()\n if (!filename) {\n throw new Error('Media document has no filename')\n }\n const source = isS3Enabled() ? 's3' : 'disk'\n log.debug(`reading media filename=${filename} source=${source}`)\n return source === 's3' ? readFromS3(filename) : readFromDisk(filename)\n}\n\nexport async function readPayloadMediaAsDataUrl(media: PayloadMedia): Promise<string> {\n const buffer = await readPayloadMediaBuffer(media)\n const mimeType = media.mimeType?.trim() || 'image/jpeg'\n return `data:${mimeType};base64,${buffer.toString('base64')}`\n}\n"],"mappings":";;;;;;;;;;AAaA,MAAM,MAAM,aAAa,kBAAkB;AAO3C,IAAI,iBAAkC;AAEtC,SAAS,cAAuB;CAC9B,OAAO,QAAQ,QAAQ,IAAI,aAAa;AAC1C;AAEA,SAAS,cAAwB;CAC/B,IAAI,gBAAgB,OAAO;CAC3B,MAAM,WAAW,QAAQ,IAAI,iBAAiB,KAAK;CACnD,MAAM,eAA+B;EACnC,QAAQ,QAAQ,IAAI,iBAAiB;EACrC,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;EAC/B,GAAI,QAAQ,IAAI,4BAA4B,SAAS,EAAE,gBAAgB,KAAK,IAAI,CAAC;CACnF;CACA,MAAM,cAAc,QAAQ,IAAI;CAChC,MAAM,kBAAkB,QAAQ,IAAI;CACpC,IAAI,eAAe,iBACjB,aAAa,cAAc;EAAE;EAAa;CAAgB;CAE5D,iBAAiB,IAAI,SAAS,YAAY;CAC1C,OAAO;AACT;AAEA,eAAe,WAAW,UAAmC;CAC3D,MAAM,SAAS,QAAQ,IAAI;CAC3B,IAAI,CAAC,QACH,MAAM,IAAI,MAAM,sDAAsD;CAGxE,MAAM,WAAW,MADF,YACa,CAAC,CAAC,KAAK,IAAI,iBAAiB;EAAE,QAAQ;EAAQ,KAAK;CAAS,CAAC,CAAC;CAC1F,IAAI,CAAC,SAAS,MACZ,MAAM,IAAI,MAAM,6CAA6C,UAAU;CAGzE,MAAM,QAAQ,MAAM,SAAS,KAAK,qBAAqB;CACvD,OAAO,OAAO,KAAK,KAAK;AAC1B;AAEA,eAAe,aAAa,UAAmC;CAC7D,MAAM,WAAW,KAAK,QAAQ,IAAI,GAAG,SAAS,QAAQ;CACtD,OAAO,SAAS,QAAQ;AAC1B;AAEA,eAAsB,uBAAuB,OAAsC;CACjF,MAAM,WAAW,MAAM,UAAU,KAAK;CACtC,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,gCAAgC;CAElD,MAAM,SAAS,YAAY,IAAI,OAAO;CACtC,IAAI,MAAM,0BAA0B,SAAS,UAAU,QAAQ;CAC/D,OAAO,WAAW,OAAO,WAAW,QAAQ,IAAI,aAAa,QAAQ;AACvE;AAEA,eAAsB,0BAA0B,OAAsC;CACpF,MAAM,SAAS,MAAM,uBAAuB,KAAK;CAEjD,OAAO,QADU,MAAM,UAAU,KAAK,KAAK,aACnB,UAAU,OAAO,SAAS,QAAQ;AAC5D"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as createMetadataFactory, i as OgImage, n as CreateMetadataFactoryOptions, o as GetSiteUrlOptions, r as CreateMetadataInput, s as getSiteUrl, t as getOgBackgroundDataUrl } from "../index-tTs9h9BS.mjs";
|
|
2
|
+
export { type CreateMetadataFactoryOptions, type CreateMetadataInput, type GetSiteUrlOptions, type OgImage, createMetadataFactory, getOgBackgroundDataUrl, getSiteUrl };
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { t as createLogger } from "./logger-BPURnavq.mjs";
|
|
2
|
+
import { t as readPayloadMediaAsDataUrl } from "./readMediaBuffer-DWkTbYVn.mjs";
|
|
3
|
+
//#region src/seo/site-url.ts
|
|
4
|
+
const log$1 = createLogger("siteUrl");
|
|
5
|
+
/** Resolve the public site origin from env (`NEXT_PUBLIC_SITE_URL` / `VERCEL_URL`). */
|
|
6
|
+
function getSiteUrl(options = {}) {
|
|
7
|
+
const fallback = options.fallback ?? "http://localhost:3000";
|
|
8
|
+
const fromEnv = process.env.NEXT_PUBLIC_SITE_URL;
|
|
9
|
+
if (fromEnv) return fromEnv.replace(/\/$/, "");
|
|
10
|
+
if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`.replace(/\/$/, "");
|
|
11
|
+
if (process.env.NODE_ENV === "production") log$1.warn(`NEXT_PUBLIC_SITE_URL and VERCEL_URL are unset — falling back to ${fallback}. Canonical and Open Graph URLs will be incorrect.`);
|
|
12
|
+
return fallback.replace(/\/$/, "");
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/seo/metadata.ts
|
|
16
|
+
const ABSOLUTE_URL_RE = /^https?:\/\//i;
|
|
17
|
+
function toAbsolute(value, base) {
|
|
18
|
+
if (ABSOLUTE_URL_RE.test(value)) return value;
|
|
19
|
+
return new URL(value, base).toString();
|
|
20
|
+
}
|
|
21
|
+
function createMetadataFactory(options) {
|
|
22
|
+
const localePrefix = options.localePrefix ?? "as-needed";
|
|
23
|
+
const resolveSiteUrl = options.getSiteUrl ?? (() => getSiteUrl());
|
|
24
|
+
function createMetadata(input) {
|
|
25
|
+
const siteName = input.siteName ?? options.siteName;
|
|
26
|
+
const defaultDescription = input.defaultDescription ?? options.defaultDescription;
|
|
27
|
+
const description = input.description?.trim() || defaultDescription;
|
|
28
|
+
const pathname = input.pathname ?? "/";
|
|
29
|
+
const base = resolveSiteUrl();
|
|
30
|
+
const canonical = input.canonicalUrl ? toAbsolute(input.canonicalUrl, base) : new URL(pathname, base).toString();
|
|
31
|
+
const locale = input.locale ?? options.defaultLocale;
|
|
32
|
+
const ogLocale = options.getOpenGraphLocale(locale);
|
|
33
|
+
const languages = {};
|
|
34
|
+
for (const l of options.locales) {
|
|
35
|
+
const isDefault = l === options.defaultLocale;
|
|
36
|
+
const localizedPath = localePrefix === "as-needed" && isDefault ? pathname : `/${l}${pathname}`;
|
|
37
|
+
languages[l] = new URL(localizedPath, base).toString();
|
|
38
|
+
}
|
|
39
|
+
languages["x-default"] = new URL(pathname, base).toString();
|
|
40
|
+
const images = input.images?.map((img) => ({
|
|
41
|
+
...img,
|
|
42
|
+
url: toAbsolute(img.url, base)
|
|
43
|
+
}));
|
|
44
|
+
const ogType = input.ogType ?? "website";
|
|
45
|
+
const robots = input.noIndex || input.noFollow ? {
|
|
46
|
+
index: !input.noIndex,
|
|
47
|
+
follow: !input.noFollow
|
|
48
|
+
} : void 0;
|
|
49
|
+
return {
|
|
50
|
+
title: input.title,
|
|
51
|
+
description,
|
|
52
|
+
alternates: {
|
|
53
|
+
canonical,
|
|
54
|
+
languages
|
|
55
|
+
},
|
|
56
|
+
openGraph: {
|
|
57
|
+
type: ogType,
|
|
58
|
+
title: input.title,
|
|
59
|
+
description,
|
|
60
|
+
url: canonical,
|
|
61
|
+
siteName,
|
|
62
|
+
locale: ogLocale,
|
|
63
|
+
...images ? { images } : {},
|
|
64
|
+
...ogType === "article" && input.article ? {
|
|
65
|
+
publishedTime: input.article.publishedTime,
|
|
66
|
+
modifiedTime: input.article.modifiedTime,
|
|
67
|
+
expirationTime: input.article.expirationTime,
|
|
68
|
+
section: input.article.section,
|
|
69
|
+
tags: input.article.tags,
|
|
70
|
+
authors: input.article.authors
|
|
71
|
+
} : {}
|
|
72
|
+
},
|
|
73
|
+
twitter: {
|
|
74
|
+
card: "summary_large_image",
|
|
75
|
+
title: input.title,
|
|
76
|
+
description,
|
|
77
|
+
...images ? { images: images.map((i) => i.url) } : {},
|
|
78
|
+
...input.twitterCreator ? { creator: input.twitterCreator } : {}
|
|
79
|
+
},
|
|
80
|
+
...robots ? { robots } : {}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function createRootMetadata(overrides) {
|
|
84
|
+
const base = resolveSiteUrl();
|
|
85
|
+
const siteName = overrides?.siteName ?? options.siteName;
|
|
86
|
+
const description = overrides?.defaultDescription ?? options.defaultDescription;
|
|
87
|
+
return {
|
|
88
|
+
metadataBase: new URL(base),
|
|
89
|
+
title: {
|
|
90
|
+
default: siteName,
|
|
91
|
+
template: `%s | ${siteName}`
|
|
92
|
+
},
|
|
93
|
+
description
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
createMetadata,
|
|
98
|
+
createRootMetadata
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
//#endregion
|
|
102
|
+
//#region src/seo/og-background.ts
|
|
103
|
+
/**
|
|
104
|
+
* Resolves a Payload Media document into a base64 data URL for Satori OG images.
|
|
105
|
+
* Server-only — reads via disk/S3, not the public HTTP media URL.
|
|
106
|
+
*/
|
|
107
|
+
const log = createLogger("ogBackground");
|
|
108
|
+
async function getOgBackgroundDataUrl(media) {
|
|
109
|
+
if (!media || !media.filename) return void 0;
|
|
110
|
+
try {
|
|
111
|
+
return await readPayloadMediaAsDataUrl({
|
|
112
|
+
filename: media.filename,
|
|
113
|
+
mimeType: media.mimeType
|
|
114
|
+
});
|
|
115
|
+
} catch (error) {
|
|
116
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
117
|
+
log.warn(`Failed to load OG background for media ${media.filename}: ${reason}`);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
//#endregion
|
|
122
|
+
export { createMetadataFactory as n, getSiteUrl as r, getOgBackgroundDataUrl as t };
|
|
123
|
+
|
|
124
|
+
//# sourceMappingURL=seo-e5XjgQ7l.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seo-e5XjgQ7l.mjs","names":["log"],"sources":["../src/seo/site-url.ts","../src/seo/metadata.ts","../src/seo/og-background.ts"],"sourcesContent":["import { createLogger } from '../utils/logger/logger.js'\n\nconst log = createLogger('siteUrl')\n\nexport type GetSiteUrlOptions = {\n /** Fallback when no env URL is set. Defaults to `http://localhost:3000`. */\n fallback?: string\n}\n\n/** Resolve the public site origin from env (`NEXT_PUBLIC_SITE_URL` / `VERCEL_URL`). */\nexport function getSiteUrl(options: GetSiteUrlOptions = {}): string {\n const fallback = options.fallback ?? 'http://localhost:3000'\n const fromEnv = process.env.NEXT_PUBLIC_SITE_URL\n if (fromEnv) return fromEnv.replace(/\\/$/, '')\n if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`.replace(/\\/$/, '')\n // A localhost origin in production means canonical/OG URLs will be wrong — surface it loudly.\n if (process.env.NODE_ENV === 'production') {\n log.warn(\n `NEXT_PUBLIC_SITE_URL and VERCEL_URL are unset — falling back to ${fallback}. Canonical and Open Graph URLs will be incorrect.`\n )\n }\n return fallback.replace(/\\/$/, '')\n}\n","import { getSiteUrl } from './site-url.js'\n\nimport type { Metadata } from 'next'\n\nexport type OgImage = {\n url: string\n width?: number\n height?: number\n alt?: string\n}\n\nexport type CreateMetadataInput = {\n title: string\n description?: string\n pathname?: string\n canonicalUrl?: string\n noIndex?: boolean\n noFollow?: boolean\n locale?: string\n images?: OgImage[]\n ogType?: 'website' | 'article'\n article?: {\n publishedTime?: string\n modifiedTime?: string\n expirationTime?: string\n section?: string\n tags?: string[]\n authors?: string[]\n }\n twitterCreator?: string\n /** Per-call overrides (e.g. localized site name). */\n siteName?: string\n defaultDescription?: string\n}\n\nexport type CreateMetadataFactoryOptions = {\n siteName: string\n defaultDescription: string\n locales: readonly string[]\n defaultLocale: string\n /** Maps locale → Open Graph locale tag (e.g. `en` → `en_US`). */\n getOpenGraphLocale: (locale: string) => string\n localePrefix?: 'always' | 'as-needed' | 'never'\n getSiteUrl?: () => string\n}\n\nconst ABSOLUTE_URL_RE = /^https?:\\/\\//i\n\nfunction toAbsolute(value: string, base: string): string {\n if (ABSOLUTE_URL_RE.test(value)) return value\n return new URL(value, base).toString()\n}\n\nexport function createMetadataFactory(options: CreateMetadataFactoryOptions) {\n const localePrefix = options.localePrefix ?? 'as-needed'\n const resolveSiteUrl = options.getSiteUrl ?? (() => getSiteUrl())\n\n function createMetadata(input: CreateMetadataInput): Metadata {\n const siteName = input.siteName ?? options.siteName\n const defaultDescription = input.defaultDescription ?? options.defaultDescription\n const description = input.description?.trim() || defaultDescription\n const pathname = input.pathname ?? '/'\n const base = resolveSiteUrl()\n const canonical = input.canonicalUrl\n ? toAbsolute(input.canonicalUrl, base)\n : new URL(pathname, base).toString()\n\n const locale = input.locale ?? options.defaultLocale\n const ogLocale = options.getOpenGraphLocale(locale)\n\n const languages: Record<string, string> = {}\n for (const l of options.locales) {\n const isDefault = l === options.defaultLocale\n const localizedPath =\n localePrefix === 'as-needed' && isDefault ? pathname : `/${l}${pathname}`\n languages[l] = new URL(localizedPath, base).toString()\n }\n languages['x-default'] = new URL(pathname, base).toString()\n\n const images: OgImage[] | undefined = input.images?.map((img) => ({\n ...img,\n url: toAbsolute(img.url, base),\n }))\n\n const ogType = input.ogType ?? 'website'\n\n const robots =\n input.noIndex || input.noFollow\n ? {\n index: !input.noIndex,\n follow: !input.noFollow,\n }\n : undefined\n\n return {\n title: input.title,\n description,\n alternates: {\n canonical,\n languages,\n },\n openGraph: {\n type: ogType,\n title: input.title,\n description,\n url: canonical,\n siteName,\n locale: ogLocale,\n ...(images ? { images } : {}),\n ...(ogType === 'article' && input.article\n ? {\n publishedTime: input.article.publishedTime,\n modifiedTime: input.article.modifiedTime,\n expirationTime: input.article.expirationTime,\n section: input.article.section,\n tags: input.article.tags,\n authors: input.article.authors,\n }\n : {}),\n },\n twitter: {\n card: 'summary_large_image',\n title: input.title,\n description,\n ...(images ? { images: images.map((i) => i.url) } : {}),\n ...(input.twitterCreator ? { creator: input.twitterCreator } : {}),\n },\n ...(robots ? { robots } : {}),\n }\n }\n\n function createRootMetadata(overrides?: {\n siteName?: string\n defaultDescription?: string\n }): Metadata {\n const base = resolveSiteUrl()\n const siteName = overrides?.siteName ?? options.siteName\n const description = overrides?.defaultDescription ?? options.defaultDescription\n return {\n metadataBase: new URL(base),\n title: {\n default: siteName,\n template: `%s | ${siteName}`,\n },\n description,\n }\n }\n\n return { createMetadata, createRootMetadata }\n}\n","/**\n * Resolves a Payload Media document into a base64 data URL for Satori OG images.\n * Server-only — reads via disk/S3, not the public HTTP media URL.\n */\n\nimport { createLogger } from '../utils/logger/logger.js'\nimport { readPayloadMediaAsDataUrl } from '../cms/payload/readMediaBuffer.js'\n\nimport type { PayloadMedia } from '../cms/payload/readMediaBuffer.js'\n\nconst log = createLogger('ogBackground')\n\nexport async function getOgBackgroundDataUrl(\n media: PayloadMedia | null | undefined\n): Promise<string | undefined> {\n if (!media || !media.filename) return undefined\n try {\n return await readPayloadMediaAsDataUrl({\n filename: media.filename,\n mimeType: media.mimeType,\n })\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error)\n log.warn(`Failed to load OG background for media ${media.filename}: ${reason}`)\n return undefined\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region src/utils/stripShikiPreBackground.ts
|
|
2
|
+
function stripShikiPreBackground(html) {
|
|
3
|
+
return html.replace(/<pre([^>]*)style="([^"]*)"([^>]*)>/, (_, before, styles, after) => {
|
|
4
|
+
const nextStyles = styles.replace(/background-color:[^;"]*;?/gi, "").replace(/(^|;)\s*;+/g, "$1").trim().replace(/^;|;$/g, "");
|
|
5
|
+
if (!nextStyles) return `<pre${before}${after}>`;
|
|
6
|
+
return `<pre${before}style="${nextStyles}"${after}>`;
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/utils/toPlainValue.ts
|
|
11
|
+
function toPlainValue(value) {
|
|
12
|
+
if (value == null) return value;
|
|
13
|
+
return JSON.parse(JSON.stringify(value));
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
export { stripShikiPreBackground as n, toPlainValue as t };
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=utils-CVVQQltg.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils-CVVQQltg.mjs","names":[],"sources":["../src/utils/stripShikiPreBackground.ts","../src/utils/toPlainValue.ts"],"sourcesContent":["export function stripShikiPreBackground(html: string): string {\n return html.replace(/<pre([^>]*)style=\"([^\"]*)\"([^>]*)>/, (_, before, styles, after) => {\n const nextStyles = styles\n .replace(/background-color:[^;\"]*;?/gi, '')\n .replace(/(^|;)\\s*;+/g, '$1')\n .trim()\n .replace(/^;|;$/g, '')\n\n if (!nextStyles) {\n return `<pre${before}${after}>`\n }\n\n return `<pre${before}style=\"${nextStyles}\"${after}>`\n })\n}\n","export function toPlainValue<T>(value: T): T {\n if (value == null) return value\n return JSON.parse(JSON.stringify(value)) as T\n}\n"],"mappings":";AAAA,SAAgB,wBAAwB,MAAsB;CAC5D,OAAO,KAAK,QAAQ,uCAAuC,GAAG,QAAQ,QAAQ,UAAU;EACtF,MAAM,aAAa,OAChB,QAAQ,+BAA+B,EAAE,CAAC,CAC1C,QAAQ,eAAe,IAAI,CAAC,CAC5B,KAAK,CAAC,CACN,QAAQ,UAAU,EAAE;EAEvB,IAAI,CAAC,YACH,OAAO,OAAO,SAAS,MAAM;EAG/B,OAAO,OAAO,OAAO,SAAS,WAAW,GAAG,MAAM;CACpD,CAAC;AACH;;;ACdA,SAAgB,aAAgB,OAAa;CAC3C,IAAI,SAAS,MAAM,OAAO;CAC1B,OAAO,KAAK,MAAM,KAAK,UAAU,KAAK,CAAC;AACzC"}
|
package/package.json
CHANGED