@djangocfg/nextjs 2.1.225 → 2.1.226
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/README.md +49 -80
- package/dist/config/index.mjs +6 -17
- package/dist/config/index.mjs.map +1 -1
- package/dist/index.d.mts +1 -6
- package/dist/index.mjs +9 -865
- package/dist/index.mjs.map +1 -1
- package/dist/og-image/index.d.mts +40 -100
- package/dist/og-image/index.mjs +46 -823
- package/dist/og-image/index.mjs.map +1 -1
- package/package.json +14 -25
- package/src/index.ts +0 -3
- package/src/og-image/README.md +142 -53
- package/src/og-image/index.ts +3 -27
- package/src/og-image/metadata.ts +67 -0
- package/src/og-image/types.ts +28 -44
- package/src/og-image/url.ts +58 -0
- package/dist/og-image/components/index.d.mts +0 -59
- package/dist/og-image/components/index.mjs +0 -338
- package/dist/og-image/components/index.mjs.map +0 -1
- package/dist/og-image/utils/index.d.mts +0 -308
- package/dist/og-image/utils/index.mjs +0 -327
- package/dist/og-image/utils/index.mjs.map +0 -1
- package/src/og-image/components/DefaultTemplate.tsx +0 -369
- package/src/og-image/components/index.ts +0 -9
- package/src/og-image/route.tsx +0 -312
- package/src/og-image/utils/fonts.ts +0 -150
- package/src/og-image/utils/index.ts +0 -28
- package/src/og-image/utils/metadata.ts +0 -269
- package/src/og-image/utils/url.ts +0 -386
package/dist/og-image/index.mjs
CHANGED
|
@@ -1,847 +1,70 @@
|
|
|
1
|
-
// src/og-image/
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
// src/og-image/components/DefaultTemplate.tsx
|
|
6
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
-
function DefaultTemplate({
|
|
8
|
-
title,
|
|
9
|
-
description,
|
|
10
|
-
siteName,
|
|
11
|
-
logo,
|
|
12
|
-
// Visibility flags
|
|
13
|
-
showLogo = true,
|
|
14
|
-
showSiteName = true,
|
|
15
|
-
// Background customization
|
|
16
|
-
backgroundType = "gradient",
|
|
17
|
-
gradientStart = "#667eea",
|
|
18
|
-
gradientEnd = "#764ba2",
|
|
19
|
-
backgroundColor = "#ffffff",
|
|
20
|
-
// Typography - Title
|
|
21
|
-
titleSize,
|
|
22
|
-
titleWeight = 800,
|
|
23
|
-
titleColor = "white",
|
|
24
|
-
// Typography - Description
|
|
25
|
-
descriptionSize = 32,
|
|
26
|
-
descriptionColor = "rgba(255, 255, 255, 0.85)",
|
|
27
|
-
// Typography - Site Name
|
|
28
|
-
siteNameSize = 28,
|
|
29
|
-
siteNameColor = "rgba(255, 255, 255, 0.95)",
|
|
30
|
-
// Layout
|
|
31
|
-
padding = 80,
|
|
32
|
-
logoSize = 48,
|
|
33
|
-
// Dev mode
|
|
34
|
-
devMode = false
|
|
35
|
-
}) {
|
|
36
|
-
const calculatedTitleSize = titleSize || (title.length > 60 ? 56 : 72);
|
|
37
|
-
const backgroundStyle = backgroundType === "gradient" ? `linear-gradient(135deg, ${gradientStart} 0%, ${gradientEnd} 100%)` : backgroundColor;
|
|
38
|
-
const gridOverlay = devMode ? /* @__PURE__ */ jsx(
|
|
39
|
-
"div",
|
|
40
|
-
{
|
|
41
|
-
style: {
|
|
42
|
-
position: "absolute",
|
|
43
|
-
top: 0,
|
|
44
|
-
left: 0,
|
|
45
|
-
right: 0,
|
|
46
|
-
bottom: 0,
|
|
47
|
-
backgroundImage: `
|
|
48
|
-
linear-gradient(rgba(0, 0, 0, 0.1) 1px, transparent 1px),
|
|
49
|
-
linear-gradient(90deg, rgba(0, 0, 0, 0.1) 1px, transparent 1px)
|
|
50
|
-
`,
|
|
51
|
-
backgroundSize: "20px 20px",
|
|
52
|
-
pointerEvents: "none",
|
|
53
|
-
zIndex: 10
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
) : null;
|
|
57
|
-
return /* @__PURE__ */ jsxs(
|
|
58
|
-
"div",
|
|
59
|
-
{
|
|
60
|
-
style: {
|
|
61
|
-
height: "100%",
|
|
62
|
-
width: "100%",
|
|
63
|
-
display: "flex",
|
|
64
|
-
flexDirection: "column",
|
|
65
|
-
alignItems: "flex-start",
|
|
66
|
-
justifyContent: "space-between",
|
|
67
|
-
background: backgroundStyle,
|
|
68
|
-
padding: `${padding}px`,
|
|
69
|
-
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
70
|
-
position: "relative"
|
|
71
|
-
},
|
|
72
|
-
children: [
|
|
73
|
-
gridOverlay,
|
|
74
|
-
(showLogo && logo || showSiteName && siteName) && /* @__PURE__ */ jsxs(
|
|
75
|
-
"div",
|
|
76
|
-
{
|
|
77
|
-
style: {
|
|
78
|
-
display: "flex",
|
|
79
|
-
alignItems: "center",
|
|
80
|
-
gap: "16px"
|
|
81
|
-
},
|
|
82
|
-
children: [
|
|
83
|
-
showLogo && logo && // eslint-disable-next-line @next/next/no-img-element
|
|
84
|
-
/* @__PURE__ */ jsx(
|
|
85
|
-
"img",
|
|
86
|
-
{
|
|
87
|
-
src: logo,
|
|
88
|
-
alt: "Logo",
|
|
89
|
-
width: logoSize,
|
|
90
|
-
height: logoSize,
|
|
91
|
-
style: {
|
|
92
|
-
borderRadius: "8px"
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
),
|
|
96
|
-
showSiteName && siteName && /* @__PURE__ */ jsx(
|
|
97
|
-
"div",
|
|
98
|
-
{
|
|
99
|
-
style: {
|
|
100
|
-
fontSize: siteNameSize,
|
|
101
|
-
fontWeight: 600,
|
|
102
|
-
color: siteNameColor,
|
|
103
|
-
letterSpacing: "-0.02em"
|
|
104
|
-
},
|
|
105
|
-
children: siteName
|
|
106
|
-
}
|
|
107
|
-
)
|
|
108
|
-
]
|
|
109
|
-
}
|
|
110
|
-
),
|
|
111
|
-
/* @__PURE__ */ jsxs(
|
|
112
|
-
"div",
|
|
113
|
-
{
|
|
114
|
-
style: {
|
|
115
|
-
display: "flex",
|
|
116
|
-
flexDirection: "column",
|
|
117
|
-
gap: "24px",
|
|
118
|
-
flex: 1,
|
|
119
|
-
justifyContent: "center"
|
|
120
|
-
},
|
|
121
|
-
children: [
|
|
122
|
-
/* @__PURE__ */ jsx(
|
|
123
|
-
"div",
|
|
124
|
-
{
|
|
125
|
-
style: {
|
|
126
|
-
fontSize: calculatedTitleSize,
|
|
127
|
-
fontWeight: titleWeight,
|
|
128
|
-
color: titleColor,
|
|
129
|
-
lineHeight: 1.1,
|
|
130
|
-
letterSpacing: "-0.03em",
|
|
131
|
-
textShadow: backgroundType === "gradient" ? "0 2px 20px rgba(0, 0, 0, 0.2)" : "none",
|
|
132
|
-
maxWidth: "100%",
|
|
133
|
-
wordWrap: "break-word"
|
|
134
|
-
},
|
|
135
|
-
children: title
|
|
136
|
-
}
|
|
137
|
-
),
|
|
138
|
-
description && /* @__PURE__ */ jsx(
|
|
139
|
-
"div",
|
|
140
|
-
{
|
|
141
|
-
style: {
|
|
142
|
-
fontSize: descriptionSize,
|
|
143
|
-
fontWeight: 400,
|
|
144
|
-
color: descriptionColor,
|
|
145
|
-
lineHeight: 1.5,
|
|
146
|
-
letterSpacing: "-0.01em",
|
|
147
|
-
maxWidth: "90%",
|
|
148
|
-
display: "-webkit-box",
|
|
149
|
-
WebkitLineClamp: 2,
|
|
150
|
-
WebkitBoxOrient: "vertical",
|
|
151
|
-
overflow: "hidden"
|
|
152
|
-
},
|
|
153
|
-
children: description
|
|
154
|
-
}
|
|
155
|
-
)
|
|
156
|
-
]
|
|
157
|
-
}
|
|
158
|
-
),
|
|
159
|
-
/* @__PURE__ */ jsx(
|
|
160
|
-
"div",
|
|
161
|
-
{
|
|
162
|
-
style: {
|
|
163
|
-
display: "flex",
|
|
164
|
-
width: "100%",
|
|
165
|
-
height: "4px",
|
|
166
|
-
background: backgroundType === "gradient" ? `linear-gradient(90deg, ${gradientStart} 0%, ${gradientEnd} 100%)` : gradientStart,
|
|
167
|
-
borderRadius: "2px"
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
)
|
|
171
|
-
]
|
|
172
|
-
}
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
|
-
function LightTemplate({
|
|
176
|
-
title,
|
|
177
|
-
description,
|
|
178
|
-
siteName,
|
|
179
|
-
logo,
|
|
180
|
-
// Visibility flags
|
|
181
|
-
showLogo = true,
|
|
182
|
-
showSiteName = true,
|
|
183
|
-
// Background customization (defaults to light theme)
|
|
184
|
-
backgroundType = "solid",
|
|
185
|
-
gradientStart = "#667eea",
|
|
186
|
-
gradientEnd = "#764ba2",
|
|
187
|
-
backgroundColor = "#ffffff",
|
|
188
|
-
// Typography - Title
|
|
189
|
-
titleSize,
|
|
190
|
-
titleWeight = 800,
|
|
191
|
-
titleColor = "#111",
|
|
192
|
-
// Typography - Description
|
|
193
|
-
descriptionSize = 32,
|
|
194
|
-
descriptionColor = "#666",
|
|
195
|
-
// Typography - Site Name
|
|
196
|
-
siteNameSize = 28,
|
|
197
|
-
siteNameColor = "#111",
|
|
198
|
-
// Layout
|
|
199
|
-
padding = 80,
|
|
200
|
-
logoSize = 48,
|
|
201
|
-
// Dev mode
|
|
202
|
-
devMode = false
|
|
203
|
-
}) {
|
|
204
|
-
const calculatedTitleSize = titleSize || (title.length > 60 ? 56 : 72);
|
|
205
|
-
const backgroundStyle = backgroundType === "gradient" ? `linear-gradient(135deg, ${gradientStart} 0%, ${gradientEnd} 100%)` : backgroundColor;
|
|
206
|
-
const gridOverlay = devMode ? /* @__PURE__ */ jsx(
|
|
207
|
-
"div",
|
|
208
|
-
{
|
|
209
|
-
style: {
|
|
210
|
-
position: "absolute",
|
|
211
|
-
top: 0,
|
|
212
|
-
left: 0,
|
|
213
|
-
right: 0,
|
|
214
|
-
bottom: 0,
|
|
215
|
-
backgroundImage: `
|
|
216
|
-
linear-gradient(rgba(0, 0, 0, 0.1) 1px, transparent 1px),
|
|
217
|
-
linear-gradient(90deg, rgba(0, 0, 0, 0.1) 1px, transparent 1px)
|
|
218
|
-
`,
|
|
219
|
-
backgroundSize: "20px 20px",
|
|
220
|
-
pointerEvents: "none",
|
|
221
|
-
zIndex: 10
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
) : null;
|
|
225
|
-
return /* @__PURE__ */ jsxs(
|
|
226
|
-
"div",
|
|
227
|
-
{
|
|
228
|
-
style: {
|
|
229
|
-
height: "100%",
|
|
230
|
-
width: "100%",
|
|
231
|
-
display: "flex",
|
|
232
|
-
flexDirection: "column",
|
|
233
|
-
alignItems: "flex-start",
|
|
234
|
-
justifyContent: "space-between",
|
|
235
|
-
background: backgroundStyle,
|
|
236
|
-
padding: `${padding}px`,
|
|
237
|
-
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
238
|
-
position: "relative"
|
|
239
|
-
},
|
|
240
|
-
children: [
|
|
241
|
-
gridOverlay,
|
|
242
|
-
(showLogo && logo || showSiteName && siteName) && /* @__PURE__ */ jsxs(
|
|
243
|
-
"div",
|
|
244
|
-
{
|
|
245
|
-
style: {
|
|
246
|
-
display: "flex",
|
|
247
|
-
alignItems: "center",
|
|
248
|
-
gap: "16px"
|
|
249
|
-
},
|
|
250
|
-
children: [
|
|
251
|
-
showLogo && logo && // eslint-disable-next-line @next/next/no-img-element
|
|
252
|
-
/* @__PURE__ */ jsx(
|
|
253
|
-
"img",
|
|
254
|
-
{
|
|
255
|
-
src: logo,
|
|
256
|
-
alt: "Logo",
|
|
257
|
-
width: logoSize,
|
|
258
|
-
height: logoSize,
|
|
259
|
-
style: {
|
|
260
|
-
borderRadius: "8px"
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
),
|
|
264
|
-
showSiteName && siteName && /* @__PURE__ */ jsx(
|
|
265
|
-
"div",
|
|
266
|
-
{
|
|
267
|
-
style: {
|
|
268
|
-
fontSize: siteNameSize,
|
|
269
|
-
fontWeight: 600,
|
|
270
|
-
color: siteNameColor,
|
|
271
|
-
letterSpacing: "-0.02em"
|
|
272
|
-
},
|
|
273
|
-
children: siteName
|
|
274
|
-
}
|
|
275
|
-
)
|
|
276
|
-
]
|
|
277
|
-
}
|
|
278
|
-
),
|
|
279
|
-
/* @__PURE__ */ jsxs(
|
|
280
|
-
"div",
|
|
281
|
-
{
|
|
282
|
-
style: {
|
|
283
|
-
display: "flex",
|
|
284
|
-
flexDirection: "column",
|
|
285
|
-
gap: "24px",
|
|
286
|
-
flex: 1,
|
|
287
|
-
justifyContent: "center"
|
|
288
|
-
},
|
|
289
|
-
children: [
|
|
290
|
-
/* @__PURE__ */ jsx(
|
|
291
|
-
"div",
|
|
292
|
-
{
|
|
293
|
-
style: {
|
|
294
|
-
fontSize: calculatedTitleSize,
|
|
295
|
-
fontWeight: titleWeight,
|
|
296
|
-
color: titleColor,
|
|
297
|
-
lineHeight: 1.1,
|
|
298
|
-
letterSpacing: "-0.03em",
|
|
299
|
-
maxWidth: "100%",
|
|
300
|
-
wordWrap: "break-word"
|
|
301
|
-
},
|
|
302
|
-
children: title
|
|
303
|
-
}
|
|
304
|
-
),
|
|
305
|
-
description && /* @__PURE__ */ jsx(
|
|
306
|
-
"div",
|
|
307
|
-
{
|
|
308
|
-
style: {
|
|
309
|
-
fontSize: descriptionSize,
|
|
310
|
-
fontWeight: 400,
|
|
311
|
-
color: descriptionColor,
|
|
312
|
-
lineHeight: 1.5,
|
|
313
|
-
letterSpacing: "-0.01em",
|
|
314
|
-
maxWidth: "90%"
|
|
315
|
-
},
|
|
316
|
-
children: description
|
|
317
|
-
}
|
|
318
|
-
)
|
|
319
|
-
]
|
|
320
|
-
}
|
|
321
|
-
),
|
|
322
|
-
/* @__PURE__ */ jsx(
|
|
323
|
-
"div",
|
|
324
|
-
{
|
|
325
|
-
style: {
|
|
326
|
-
display: "flex",
|
|
327
|
-
width: "100%",
|
|
328
|
-
height: "4px",
|
|
329
|
-
background: backgroundType === "gradient" ? `linear-gradient(90deg, ${gradientStart} 0%, ${gradientEnd} 100%)` : gradientStart,
|
|
330
|
-
borderRadius: "2px"
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
)
|
|
334
|
-
]
|
|
335
|
-
}
|
|
336
|
-
);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// src/og-image/utils/fonts.ts
|
|
340
|
-
async function loadGoogleFont(font, text, weight = 700) {
|
|
341
|
-
let url = `https://fonts.googleapis.com/css2?family=${font}:wght@${weight}`;
|
|
342
|
-
if (text) {
|
|
343
|
-
url += `&text=${encodeURIComponent(text)}`;
|
|
344
|
-
}
|
|
345
|
-
try {
|
|
346
|
-
const css = await fetch(url, {
|
|
347
|
-
headers: {
|
|
348
|
-
// Required to get TTF format instead of WOFF2
|
|
349
|
-
"User-Agent": "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; de-at) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1"
|
|
350
|
-
}
|
|
351
|
-
}).then((res) => res.text());
|
|
352
|
-
const resource = css.match(/src: url\((.+)\) format\('(opentype|truetype)'\)/);
|
|
353
|
-
if (!resource || !resource[1]) {
|
|
354
|
-
throw new Error(`Failed to parse font URL from CSS for font: ${font}`);
|
|
355
|
-
}
|
|
356
|
-
const response = await fetch(resource[1]);
|
|
357
|
-
if (response.status !== 200) {
|
|
358
|
-
throw new Error(`Failed to fetch font data: HTTP ${response.status}`);
|
|
359
|
-
}
|
|
360
|
-
return await response.arrayBuffer();
|
|
361
|
-
} catch (error) {
|
|
362
|
-
console.error(`Error loading Google Font "${font}":`, error);
|
|
363
|
-
throw new Error(`Failed to load font "${font}": ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
async function loadGoogleFonts(fonts) {
|
|
367
|
-
const fontConfigs = await Promise.all(
|
|
368
|
-
fonts.map(async ({ family, weight = 700, style = "normal", text }) => {
|
|
369
|
-
const data = await loadGoogleFont(family, text, weight);
|
|
370
|
-
return {
|
|
371
|
-
name: family,
|
|
372
|
-
weight,
|
|
373
|
-
style,
|
|
374
|
-
data
|
|
375
|
-
};
|
|
376
|
-
})
|
|
377
|
-
);
|
|
378
|
-
return fontConfigs;
|
|
379
|
-
}
|
|
380
|
-
function createFontLoader() {
|
|
381
|
-
const cache = /* @__PURE__ */ new Map();
|
|
382
|
-
return {
|
|
383
|
-
/**
|
|
384
|
-
* Load a font with caching
|
|
385
|
-
*/
|
|
386
|
-
async load(family, weight = 700, text) {
|
|
387
|
-
const cacheKey = `${family}-${weight}-${text || "all"}`;
|
|
388
|
-
if (!cache.has(cacheKey)) {
|
|
389
|
-
cache.set(cacheKey, loadGoogleFont(family, text, weight));
|
|
390
|
-
}
|
|
391
|
-
return cache.get(cacheKey);
|
|
392
|
-
},
|
|
393
|
-
/**
|
|
394
|
-
* Clear the cache
|
|
395
|
-
*/
|
|
396
|
-
clear() {
|
|
397
|
-
cache.clear();
|
|
398
|
-
},
|
|
399
|
-
/**
|
|
400
|
-
* Get cache size
|
|
401
|
-
*/
|
|
402
|
-
size() {
|
|
403
|
-
return cache.size;
|
|
404
|
-
}
|
|
405
|
-
};
|
|
1
|
+
// src/og-image/url.ts
|
|
2
|
+
function resolveOgPublicBase() {
|
|
3
|
+
if (typeof process === "undefined") return "";
|
|
4
|
+
return process.env.NEXT_PUBLIC_MEDIA_URL?.replace(/\/$/, "") ?? process.env.NEXT_PUBLIC_API_URL?.replace(/\/$/, "") ?? process.env.NEXT_PUBLIC_SITE_URL?.replace(/\/$/, "") ?? "";
|
|
406
5
|
}
|
|
407
|
-
|
|
408
|
-
// src/og-image/utils/url.ts
|
|
409
|
-
var DEFAULT_OG_IMAGE_BASE_URL = "https://djangocfg.com/api/og";
|
|
410
6
|
function encodeBase64(str) {
|
|
411
7
|
if (typeof Buffer !== "undefined") {
|
|
412
|
-
return Buffer.from(str
|
|
413
|
-
}
|
|
414
|
-
return btoa(unescape(encodeURIComponent(str)));
|
|
415
|
-
}
|
|
416
|
-
function decodeBase64(str) {
|
|
417
|
-
if (typeof Buffer !== "undefined") {
|
|
418
|
-
return Buffer.from(str, "base64").toString("utf-8");
|
|
419
|
-
}
|
|
420
|
-
try {
|
|
421
|
-
const binaryString = atob(str);
|
|
422
|
-
return decodeURIComponent(
|
|
423
|
-
binaryString.split("").map((c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2)).join("")
|
|
424
|
-
);
|
|
425
|
-
} catch (error) {
|
|
426
|
-
return decodeURIComponent(escape(atob(str)));
|
|
8
|
+
return Buffer.from(str).toString("base64url");
|
|
427
9
|
}
|
|
10
|
+
return btoa(unescape(encodeURIComponent(str))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
428
11
|
}
|
|
429
|
-
function
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
const cleanParams = {};
|
|
436
|
-
Object.entries(params).forEach(([key, value]) => {
|
|
437
|
-
if (value !== void 0 && value !== null && value !== "") {
|
|
438
|
-
cleanParams[key] = value;
|
|
439
|
-
}
|
|
440
|
-
});
|
|
441
|
-
const jsonString = JSON.stringify(cleanParams);
|
|
442
|
-
const base64Data = encodeBase64(jsonString);
|
|
443
|
-
return `${baseUrl}/${base64Data}/`;
|
|
444
|
-
} else {
|
|
445
|
-
const searchParams = new URLSearchParams();
|
|
446
|
-
Object.entries(params).forEach(([key, value]) => {
|
|
447
|
-
if (value !== void 0 && value !== null && value !== "") {
|
|
448
|
-
searchParams.append(key, String(value));
|
|
449
|
-
}
|
|
450
|
-
});
|
|
451
|
-
const query = searchParams.toString();
|
|
452
|
-
return query ? `${baseUrl}?${query}` : baseUrl;
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
function getAbsoluteOgImageUrl(relativePath, siteUrl) {
|
|
456
|
-
if (relativePath.startsWith("http://") || relativePath.startsWith("https://")) {
|
|
457
|
-
return relativePath;
|
|
458
|
-
}
|
|
459
|
-
const cleanSiteUrl = siteUrl.replace(/\/$/, "");
|
|
460
|
-
const cleanPath = relativePath.startsWith("/") ? relativePath : `/${relativePath}`;
|
|
461
|
-
return `${cleanSiteUrl}${cleanPath}`;
|
|
462
|
-
}
|
|
463
|
-
function createOgImageUrlBuilder(defaults = {}, options = {}) {
|
|
464
|
-
return (params) => {
|
|
465
|
-
return generateOgImageUrl(
|
|
466
|
-
{ ...defaults, ...params },
|
|
467
|
-
options
|
|
468
|
-
);
|
|
469
|
-
};
|
|
470
|
-
}
|
|
471
|
-
function parseOgImageUrl(url) {
|
|
472
|
-
try {
|
|
473
|
-
const urlObj = new URL(url, "http://dummy.com");
|
|
474
|
-
const params = {};
|
|
475
|
-
urlObj.searchParams.forEach((value, key) => {
|
|
476
|
-
params[key] = value;
|
|
477
|
-
});
|
|
478
|
-
return params;
|
|
479
|
-
} catch {
|
|
480
|
-
return {};
|
|
481
|
-
}
|
|
12
|
+
function cleanParams(params) {
|
|
13
|
+
return Object.fromEntries(
|
|
14
|
+
Object.entries(params).filter(
|
|
15
|
+
([, v]) => v !== void 0 && v !== null && v !== ""
|
|
16
|
+
)
|
|
17
|
+
);
|
|
482
18
|
}
|
|
483
|
-
function
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
params = {};
|
|
488
|
-
for (const [key, value] of searchParams.entries()) {
|
|
489
|
-
params[key] = value;
|
|
490
|
-
}
|
|
491
|
-
} else {
|
|
492
|
-
params = searchParams;
|
|
493
|
-
}
|
|
494
|
-
if (process.env.NODE_ENV === "development") {
|
|
495
|
-
console.log("[parseOgImageData] Input params keys:", Object.keys(params));
|
|
496
|
-
console.log("[parseOgImageData] Input params:", params);
|
|
497
|
-
}
|
|
498
|
-
const dataParam = params.data;
|
|
499
|
-
if (dataParam && typeof dataParam === "string" && dataParam.trim() !== "") {
|
|
500
|
-
if (process.env.NODE_ENV === "development") {
|
|
501
|
-
console.log("[parseOgImageData] Found data param, length:", dataParam.length);
|
|
502
|
-
}
|
|
503
|
-
try {
|
|
504
|
-
const decoded = decodeBase64(dataParam);
|
|
505
|
-
if (process.env.NODE_ENV === "development") {
|
|
506
|
-
console.log("[parseOgImageData] Decoded string:", decoded.substring(0, 100));
|
|
507
|
-
}
|
|
508
|
-
const parsed = JSON.parse(decoded);
|
|
509
|
-
if (process.env.NODE_ENV === "development") {
|
|
510
|
-
console.log("[parseOgImageData] Parsed JSON:", parsed);
|
|
511
|
-
}
|
|
512
|
-
const result2 = {};
|
|
513
|
-
for (const [key, value] of Object.entries(parsed)) {
|
|
514
|
-
if (value !== void 0 && value !== null) {
|
|
515
|
-
result2[key] = String(value);
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
if (process.env.NODE_ENV === "development") {
|
|
519
|
-
console.log("[parseOgImageData] Result:", result2);
|
|
520
|
-
}
|
|
521
|
-
return result2;
|
|
522
|
-
} catch (decodeError) {
|
|
523
|
-
console.error("[parseOgImageData] Error decoding/parsing data param:", decodeError);
|
|
524
|
-
if (decodeError instanceof Error) {
|
|
525
|
-
console.error("[parseOgImageData] Error message:", decodeError.message);
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
} else {
|
|
529
|
-
if (process.env.NODE_ENV === "development") {
|
|
530
|
-
console.log("[parseOgImageData] No data param found or empty");
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
const result = {};
|
|
534
|
-
for (const [key, value] of Object.entries(params)) {
|
|
535
|
-
if (key !== "data" && value !== void 0 && value !== null) {
|
|
536
|
-
result[key] = Array.isArray(value) ? value[0] : String(value);
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
if (process.env.NODE_ENV === "development") {
|
|
540
|
-
console.log("[parseOgImageData] Fallback result:", result);
|
|
541
|
-
}
|
|
542
|
-
return result;
|
|
543
|
-
} catch (error) {
|
|
544
|
-
console.error("[parseOgImageData] Unexpected error:", error);
|
|
545
|
-
return {};
|
|
546
|
-
}
|
|
19
|
+
function buildOgUrl(params) {
|
|
20
|
+
const b64 = encodeBase64(JSON.stringify(cleanParams(params)));
|
|
21
|
+
const base = resolveOgPublicBase();
|
|
22
|
+
return `${base}/cfg/og/${b64}/`;
|
|
547
23
|
}
|
|
548
24
|
|
|
549
|
-
// src/og-image/
|
|
550
|
-
function
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
25
|
+
// src/og-image/metadata.ts
|
|
26
|
+
function createOgMetadata(params) {
|
|
27
|
+
const url = buildOgUrl(params);
|
|
28
|
+
const image = { url, width: 1200, height: 630, alt: params.title };
|
|
29
|
+
return {
|
|
30
|
+
openGraph: {
|
|
31
|
+
images: [image]
|
|
32
|
+
},
|
|
33
|
+
twitter: {
|
|
34
|
+
card: "summary_large_image",
|
|
35
|
+
images: [url]
|
|
560
36
|
}
|
|
561
|
-
}
|
|
562
|
-
return "";
|
|
563
|
-
}
|
|
564
|
-
function extractDescription(metadata) {
|
|
565
|
-
if (typeof metadata.description === "string") {
|
|
566
|
-
return metadata.description;
|
|
567
|
-
}
|
|
568
|
-
return "";
|
|
569
|
-
}
|
|
570
|
-
function getSiteUrl() {
|
|
571
|
-
if (typeof process !== "undefined" && process.env.NEXT_PUBLIC_SITE_URL) {
|
|
572
|
-
return process.env.NEXT_PUBLIC_SITE_URL;
|
|
573
|
-
}
|
|
574
|
-
return "";
|
|
37
|
+
};
|
|
575
38
|
}
|
|
576
|
-
function
|
|
577
|
-
const {
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
defaultParams = {},
|
|
581
|
-
useBase64 = true,
|
|
582
|
-
favicon,
|
|
583
|
-
appleIcon
|
|
584
|
-
} = options;
|
|
585
|
-
const siteUrl = providedSiteUrl && providedSiteUrl !== "undefined" ? providedSiteUrl : getSiteUrl();
|
|
586
|
-
const extractedTitle = extractTitle(metadata);
|
|
587
|
-
const extractedDescription = extractDescription(metadata);
|
|
588
|
-
const finalOgImageParams = {
|
|
589
|
-
...defaultParams,
|
|
590
|
-
title: ogImageParams?.title || extractedTitle || defaultParams.title || "",
|
|
591
|
-
description: ogImageParams?.description || extractedDescription || defaultParams.description || "",
|
|
592
|
-
...ogImageParams
|
|
39
|
+
function withOgImage(metadata, params) {
|
|
40
|
+
const resolvedParams = {
|
|
41
|
+
title: extractTitle(metadata),
|
|
42
|
+
...params
|
|
593
43
|
};
|
|
594
|
-
const
|
|
595
|
-
|
|
596
|
-
finalOgImageParams,
|
|
597
|
-
{ baseUrl: ogImageBaseUrl, useBase64 }
|
|
598
|
-
);
|
|
599
|
-
const ogImageUrl = siteUrl ? getAbsoluteOgImageUrl(relativeOgImageUrl, siteUrl) : relativeOgImageUrl;
|
|
600
|
-
const existingOgImages = metadata.openGraph?.images ? Array.isArray(metadata.openGraph.images) ? metadata.openGraph.images : [metadata.openGraph.images] : [];
|
|
601
|
-
const existingTwitterImages = metadata.twitter?.images ? Array.isArray(metadata.twitter.images) ? metadata.twitter.images : [metadata.twitter.images] : [];
|
|
602
|
-
const finalMetadata = {
|
|
44
|
+
const ogMeta = createOgMetadata(resolvedParams);
|
|
45
|
+
return {
|
|
603
46
|
...metadata,
|
|
604
47
|
openGraph: {
|
|
605
48
|
...metadata.openGraph,
|
|
606
|
-
|
|
607
|
-
...existingOgImages,
|
|
608
|
-
{
|
|
609
|
-
url: ogImageUrl,
|
|
610
|
-
width: 1200,
|
|
611
|
-
height: 630,
|
|
612
|
-
alt: imageAlt
|
|
613
|
-
}
|
|
614
|
-
]
|
|
49
|
+
...ogMeta.openGraph
|
|
615
50
|
},
|
|
616
51
|
twitter: {
|
|
617
52
|
...metadata.twitter,
|
|
618
|
-
|
|
619
|
-
images: [
|
|
620
|
-
...existingTwitterImages,
|
|
621
|
-
{
|
|
622
|
-
url: ogImageUrl,
|
|
623
|
-
alt: imageAlt
|
|
624
|
-
}
|
|
625
|
-
]
|
|
626
|
-
}
|
|
627
|
-
};
|
|
628
|
-
if (!finalMetadata.metadataBase && siteUrl) {
|
|
629
|
-
if (siteUrl.startsWith("http://") || siteUrl.startsWith("https://")) {
|
|
630
|
-
try {
|
|
631
|
-
finalMetadata.metadataBase = new URL(siteUrl);
|
|
632
|
-
} catch (e) {
|
|
633
|
-
}
|
|
53
|
+
...ogMeta.twitter
|
|
634
54
|
}
|
|
635
|
-
}
|
|
636
|
-
if (favicon || appleIcon) {
|
|
637
|
-
const existingIcons = metadata.icons && typeof metadata.icons === "object" && !Array.isArray(metadata.icons) ? metadata.icons : {};
|
|
638
|
-
finalMetadata.icons = {
|
|
639
|
-
...existingIcons,
|
|
640
|
-
...favicon && { icon: favicon },
|
|
641
|
-
...appleIcon && { apple: appleIcon }
|
|
642
|
-
};
|
|
643
|
-
}
|
|
644
|
-
return finalMetadata;
|
|
645
|
-
}
|
|
646
|
-
function createAppMetadataGenerator(options) {
|
|
647
|
-
return (metadata, ogImageParams) => {
|
|
648
|
-
return generateAppMetadata(metadata, ogImageParams, options);
|
|
649
55
|
};
|
|
650
56
|
}
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
fonts: fontConfig = [],
|
|
659
|
-
size = { width: 1200, height: 630 },
|
|
660
|
-
debug = false
|
|
661
|
-
} = config;
|
|
662
|
-
async function GET(req) {
|
|
663
|
-
let searchParams = new URLSearchParams();
|
|
664
|
-
if (req.nextUrl?.searchParams && req.nextUrl.searchParams.size > 0) {
|
|
665
|
-
searchParams = req.nextUrl.searchParams;
|
|
666
|
-
} else if (req.nextUrl?.search && req.nextUrl.search.length > 1) {
|
|
667
|
-
searchParams = new URLSearchParams(req.nextUrl.search);
|
|
668
|
-
} else {
|
|
669
|
-
try {
|
|
670
|
-
const url = new URL(req.url);
|
|
671
|
-
if (url.searchParams.size > 0) {
|
|
672
|
-
searchParams = url.searchParams;
|
|
673
|
-
}
|
|
674
|
-
} catch (error) {
|
|
675
|
-
}
|
|
676
|
-
if (searchParams.size === 0 && req.url) {
|
|
677
|
-
const queryIndex = req.url.indexOf("?");
|
|
678
|
-
if (queryIndex !== -1) {
|
|
679
|
-
const queryString = req.url.substring(queryIndex + 1);
|
|
680
|
-
searchParams = new URLSearchParams(queryString);
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
if (searchParams.size === 0) {
|
|
684
|
-
const customParams = req.headers.get("x-og-search-params");
|
|
685
|
-
if (customParams) {
|
|
686
|
-
searchParams = new URLSearchParams(customParams);
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
let title = defaultProps.title || "Untitled";
|
|
691
|
-
let subtitle = defaultProps.subtitle || "";
|
|
692
|
-
let description = defaultProps.description || subtitle;
|
|
693
|
-
const dataParam = searchParams.get("data");
|
|
694
|
-
let decodedParams = {};
|
|
695
|
-
if (dataParam) {
|
|
696
|
-
try {
|
|
697
|
-
const paramsObj = { data: dataParam };
|
|
698
|
-
for (const [key, value] of searchParams.entries()) {
|
|
699
|
-
if (key !== "data") {
|
|
700
|
-
paramsObj[key] = value;
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
decodedParams = parseOgImageData(paramsObj);
|
|
704
|
-
if (decodedParams.title && typeof decodedParams.title === "string" && decodedParams.title.trim() !== "") {
|
|
705
|
-
title = decodedParams.title.trim();
|
|
706
|
-
}
|
|
707
|
-
if (decodedParams.subtitle && typeof decodedParams.subtitle === "string" && decodedParams.subtitle.trim() !== "") {
|
|
708
|
-
subtitle = decodedParams.subtitle.trim();
|
|
709
|
-
}
|
|
710
|
-
if (decodedParams.description && typeof decodedParams.description === "string" && decodedParams.description.trim() !== "") {
|
|
711
|
-
description = decodedParams.description.trim();
|
|
712
|
-
}
|
|
713
|
-
} catch (error) {
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
if (!title || title === "Untitled") {
|
|
717
|
-
const titleParam = searchParams.get("title");
|
|
718
|
-
if (titleParam) {
|
|
719
|
-
title = titleParam;
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
if (!subtitle) {
|
|
723
|
-
const subtitleParam = searchParams.get("subtitle");
|
|
724
|
-
if (subtitleParam) {
|
|
725
|
-
subtitle = subtitleParam;
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
if (!description || description === subtitle) {
|
|
729
|
-
const descParam = searchParams.get("description");
|
|
730
|
-
if (descParam) {
|
|
731
|
-
description = descParam;
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
let fonts = [];
|
|
735
|
-
if (fontConfig.length > 0) {
|
|
736
|
-
fonts = await loadGoogleFonts(fontConfig);
|
|
737
|
-
}
|
|
738
|
-
const parseValue = (value, type = "string") => {
|
|
739
|
-
if (value === void 0 || value === null || value === "") {
|
|
740
|
-
return void 0;
|
|
741
|
-
}
|
|
742
|
-
if (type === "number") {
|
|
743
|
-
const num = Number(value);
|
|
744
|
-
return isNaN(num) ? void 0 : num;
|
|
745
|
-
}
|
|
746
|
-
if (type === "boolean") {
|
|
747
|
-
if (typeof value === "boolean") return value;
|
|
748
|
-
if (typeof value === "string") {
|
|
749
|
-
return value.toLowerCase() === "true" || value === "1";
|
|
750
|
-
}
|
|
751
|
-
return Boolean(value);
|
|
752
|
-
}
|
|
753
|
-
return String(value);
|
|
754
|
-
};
|
|
755
|
-
const templateProps = {
|
|
756
|
-
...defaultProps,
|
|
757
|
-
// Content
|
|
758
|
-
title,
|
|
759
|
-
subtitle,
|
|
760
|
-
description,
|
|
761
|
-
// Override with decoded params if present
|
|
762
|
-
siteName: decodedParams.siteName || defaultProps.siteName,
|
|
763
|
-
logo: decodedParams.logo || defaultProps.logo,
|
|
764
|
-
// Background
|
|
765
|
-
backgroundType: decodedParams.backgroundType || defaultProps.backgroundType,
|
|
766
|
-
gradientStart: decodedParams.gradientStart || defaultProps.gradientStart,
|
|
767
|
-
gradientEnd: decodedParams.gradientEnd || defaultProps.gradientEnd,
|
|
768
|
-
backgroundColor: decodedParams.backgroundColor || defaultProps.backgroundColor,
|
|
769
|
-
// Typography - Title
|
|
770
|
-
titleSize: parseValue(decodedParams.titleSize, "number") ?? defaultProps.titleSize,
|
|
771
|
-
titleWeight: parseValue(decodedParams.titleWeight, "number") ?? defaultProps.titleWeight,
|
|
772
|
-
titleColor: decodedParams.titleColor || defaultProps.titleColor,
|
|
773
|
-
// Typography - Description
|
|
774
|
-
descriptionSize: parseValue(decodedParams.descriptionSize, "number") ?? defaultProps.descriptionSize,
|
|
775
|
-
descriptionColor: decodedParams.descriptionColor || defaultProps.descriptionColor,
|
|
776
|
-
// Typography - Site Name
|
|
777
|
-
siteNameSize: parseValue(decodedParams.siteNameSize, "number") ?? defaultProps.siteNameSize,
|
|
778
|
-
siteNameColor: decodedParams.siteNameColor || defaultProps.siteNameColor,
|
|
779
|
-
// Layout
|
|
780
|
-
padding: parseValue(decodedParams.padding, "number") ?? defaultProps.padding,
|
|
781
|
-
logoSize: parseValue(decodedParams.logoSize, "number") ?? defaultProps.logoSize,
|
|
782
|
-
// Visibility flags
|
|
783
|
-
showLogo: parseValue(decodedParams.showLogo, "boolean") ?? defaultProps.showLogo,
|
|
784
|
-
showSiteName: parseValue(decodedParams.showSiteName, "boolean") ?? defaultProps.showSiteName
|
|
785
|
-
};
|
|
786
|
-
return new ImageResponse(
|
|
787
|
-
/* @__PURE__ */ jsx2(Template, { ...templateProps }),
|
|
788
|
-
{
|
|
789
|
-
width: size.width,
|
|
790
|
-
height: size.height,
|
|
791
|
-
fonts,
|
|
792
|
-
debug: debug || process.env.NODE_ENV === "development"
|
|
793
|
-
}
|
|
794
|
-
);
|
|
795
|
-
}
|
|
796
|
-
return {
|
|
797
|
-
GET,
|
|
798
|
-
runtime: "edge"
|
|
799
|
-
};
|
|
800
|
-
}
|
|
801
|
-
function createOgImageDynamicRoute(config) {
|
|
802
|
-
const handler = createOgImageHandler(config);
|
|
803
|
-
const isStaticBuild = typeof process !== "undefined" && process.env.NEXT_PUBLIC_STATIC_BUILD === "true";
|
|
804
|
-
async function GET(request, context) {
|
|
805
|
-
if (isStaticBuild) {
|
|
806
|
-
return new Response("OG Image generation is not available in static export mode", {
|
|
807
|
-
status: 404,
|
|
808
|
-
headers: { "Content-Type": "text/plain" }
|
|
809
|
-
});
|
|
810
|
-
}
|
|
811
|
-
const params = await context.params;
|
|
812
|
-
const dataParam = params.data;
|
|
813
|
-
const url = new URL(request.url);
|
|
814
|
-
url.searchParams.set("data", dataParam);
|
|
815
|
-
const modifiedRequest = new NextRequest(url.toString(), {
|
|
816
|
-
method: request.method,
|
|
817
|
-
headers: request.headers
|
|
818
|
-
});
|
|
819
|
-
return handler.GET(modifiedRequest);
|
|
820
|
-
}
|
|
821
|
-
async function generateStaticParams() {
|
|
822
|
-
return [];
|
|
823
|
-
}
|
|
824
|
-
return {
|
|
825
|
-
GET,
|
|
826
|
-
generateStaticParams
|
|
827
|
-
};
|
|
57
|
+
function extractTitle(metadata) {
|
|
58
|
+
const t = metadata.title;
|
|
59
|
+
if (!t) return "";
|
|
60
|
+
if (typeof t === "string") return t;
|
|
61
|
+
if (typeof t === "object" && "absolute" in t) return t.absolute;
|
|
62
|
+
if (typeof t === "object" && "default" in t) return t.default;
|
|
63
|
+
return "";
|
|
828
64
|
}
|
|
829
65
|
export {
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
createFontLoader,
|
|
834
|
-
createOgImageDynamicRoute,
|
|
835
|
-
createOgImageHandler,
|
|
836
|
-
createOgImageUrlBuilder,
|
|
837
|
-
decodeBase64,
|
|
838
|
-
encodeBase64,
|
|
839
|
-
generateAppMetadata,
|
|
840
|
-
generateOgImageUrl,
|
|
841
|
-
getAbsoluteOgImageUrl,
|
|
842
|
-
loadGoogleFont,
|
|
843
|
-
loadGoogleFonts,
|
|
844
|
-
parseOgImageData,
|
|
845
|
-
parseOgImageUrl
|
|
66
|
+
buildOgUrl,
|
|
67
|
+
createOgMetadata,
|
|
68
|
+
withOgImage
|
|
846
69
|
};
|
|
847
70
|
//# sourceMappingURL=index.mjs.map
|