@djangocfg/nextjs 2.1.42 → 2.1.43

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.
@@ -2,312 +2,177 @@
2
2
  import { ImageResponse } from "next/og";
3
3
  import { NextRequest } from "next/server";
4
4
 
5
- // src/og-image/utils/fonts.ts
6
- async function loadGoogleFont(font, text, weight = 700) {
7
- let url = `https://fonts.googleapis.com/css2?family=${font}:wght@${weight}`;
8
- if (text) {
9
- url += `&text=${encodeURIComponent(text)}`;
10
- }
11
- try {
12
- const css = await fetch(url, {
13
- headers: {
14
- // Required to get TTF format instead of WOFF2
15
- "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"
16
- }
17
- }).then((res) => res.text());
18
- const resource = css.match(/src: url\((.+)\) format\('(opentype|truetype)'\)/);
19
- if (!resource || !resource[1]) {
20
- throw new Error(`Failed to parse font URL from CSS for font: ${font}`);
21
- }
22
- const response = await fetch(resource[1]);
23
- if (response.status !== 200) {
24
- throw new Error(`Failed to fetch font data: HTTP ${response.status}`);
25
- }
26
- return await response.arrayBuffer();
27
- } catch (error) {
28
- console.error(`Error loading Google Font "${font}":`, error);
29
- throw new Error(`Failed to load font "${font}": ${error instanceof Error ? error.message : "Unknown error"}`);
30
- }
31
- }
32
- async function loadGoogleFonts(fonts) {
33
- const fontConfigs = await Promise.all(
34
- fonts.map(async ({ family, weight = 700, style = "normal", text }) => {
35
- const data = await loadGoogleFont(family, text, weight);
36
- return {
37
- name: family,
38
- weight,
39
- style,
40
- data
41
- };
42
- })
43
- );
44
- return fontConfigs;
45
- }
46
- function createFontLoader() {
47
- const cache = /* @__PURE__ */ new Map();
48
- return {
49
- /**
50
- * Load a font with caching
51
- */
52
- async load(family, weight = 700, text) {
53
- const cacheKey = `${family}-${weight}-${text || "all"}`;
54
- if (!cache.has(cacheKey)) {
55
- cache.set(cacheKey, loadGoogleFont(family, text, weight));
56
- }
57
- return cache.get(cacheKey);
58
- },
59
- /**
60
- * Clear the cache
61
- */
62
- clear() {
63
- cache.clear();
64
- },
65
- /**
66
- * Get cache size
67
- */
68
- size() {
69
- return cache.size;
70
- }
71
- };
72
- }
73
-
74
- // src/og-image/utils/url.ts
75
- var DEFAULT_OG_IMAGE_BASE_URL = "https://djangocfg.com/api/og";
76
- function encodeBase64(str) {
77
- if (typeof Buffer !== "undefined") {
78
- return Buffer.from(str, "utf-8").toString("base64");
79
- }
80
- return btoa(unescape(encodeURIComponent(str)));
81
- }
82
- function decodeBase64(str) {
83
- if (typeof Buffer !== "undefined") {
84
- return Buffer.from(str, "base64").toString("utf-8");
85
- }
86
- try {
87
- const binaryString = atob(str);
88
- return decodeURIComponent(
89
- binaryString.split("").map((c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2)).join("")
90
- );
91
- } catch (error) {
92
- return decodeURIComponent(escape(atob(str)));
93
- }
94
- }
95
- function generateOgImageUrl(params, options = {}) {
96
- const {
97
- baseUrl = DEFAULT_OG_IMAGE_BASE_URL,
98
- useBase64 = true
99
- } = options;
100
- if (useBase64) {
101
- const cleanParams = {};
102
- Object.entries(params).forEach(([key, value]) => {
103
- if (value !== void 0 && value !== null && value !== "") {
104
- cleanParams[key] = value;
105
- }
106
- });
107
- const jsonString = JSON.stringify(cleanParams);
108
- const base64Data = encodeBase64(jsonString);
109
- return `${baseUrl}/${base64Data}`;
110
- } else {
111
- const searchParams = new URLSearchParams();
112
- Object.entries(params).forEach(([key, value]) => {
113
- if (value !== void 0 && value !== null && value !== "") {
114
- searchParams.append(key, String(value));
115
- }
116
- });
117
- const query = searchParams.toString();
118
- return query ? `${baseUrl}?${query}` : baseUrl;
119
- }
120
- }
121
- function getAbsoluteOgImageUrl(relativePath, siteUrl) {
122
- if (relativePath.startsWith("http://") || relativePath.startsWith("https://")) {
123
- return relativePath;
124
- }
125
- const cleanSiteUrl = siteUrl.replace(/\/$/, "");
126
- const cleanPath = relativePath.startsWith("/") ? relativePath : `/${relativePath}`;
127
- return `${cleanSiteUrl}${cleanPath}`;
128
- }
129
- function createOgImageUrlBuilder(defaults = {}, options = {}) {
130
- return (params) => {
131
- return generateOgImageUrl(
132
- { ...defaults, ...params },
133
- options
134
- );
135
- };
136
- }
137
- function parseOgImageUrl(url) {
138
- try {
139
- const urlObj = new URL(url, "http://dummy.com");
140
- const params = {};
141
- urlObj.searchParams.forEach((value, key) => {
142
- params[key] = value;
143
- });
144
- return params;
145
- } catch {
146
- return {};
147
- }
148
- }
149
- function parseOgImageData(searchParams) {
150
- try {
151
- let params;
152
- if (searchParams instanceof URLSearchParams) {
153
- params = {};
154
- for (const [key, value] of searchParams.entries()) {
155
- params[key] = value;
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
156
54
  }
157
- } else {
158
- params = searchParams;
159
- }
160
- if (process.env.NODE_ENV === "development") {
161
- console.log("[parseOgImageData] Input params keys:", Object.keys(params));
162
- console.log("[parseOgImageData] Input params:", params);
163
55
  }
164
- const dataParam = params.data;
165
- if (dataParam && typeof dataParam === "string" && dataParam.trim() !== "") {
166
- if (process.env.NODE_ENV === "development") {
167
- console.log("[parseOgImageData] Found data param, length:", dataParam.length);
168
- }
169
- try {
170
- const decoded = decodeBase64(dataParam);
171
- if (process.env.NODE_ENV === "development") {
172
- console.log("[parseOgImageData] Decoded string:", decoded.substring(0, 100));
173
- }
174
- const parsed = JSON.parse(decoded);
175
- if (process.env.NODE_ENV === "development") {
176
- console.log("[parseOgImageData] Parsed JSON:", parsed);
177
- }
178
- const result2 = {};
179
- for (const [key, value] of Object.entries(parsed)) {
180
- if (value !== void 0 && value !== null) {
181
- result2[key] = String(value);
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
+ ]
182
109
  }
183
- }
184
- if (process.env.NODE_ENV === "development") {
185
- console.log("[parseOgImageData] Result:", result2);
186
- }
187
- return result2;
188
- } catch (decodeError) {
189
- console.error("[parseOgImageData] Error decoding/parsing data param:", decodeError);
190
- if (decodeError instanceof Error) {
191
- console.error("[parseOgImageData] Error message:", decodeError.message);
192
- }
193
- }
194
- } else {
195
- if (process.env.NODE_ENV === "development") {
196
- console.log("[parseOgImageData] No data param found or empty");
197
- }
198
- }
199
- const result = {};
200
- for (const [key, value] of Object.entries(params)) {
201
- if (key !== "data" && value !== void 0 && value !== null) {
202
- result[key] = Array.isArray(value) ? value[0] : String(value);
203
- }
204
- }
205
- if (process.env.NODE_ENV === "development") {
206
- console.log("[parseOgImageData] Fallback result:", result);
207
- }
208
- return result;
209
- } catch (error) {
210
- console.error("[parseOgImageData] Unexpected error:", error);
211
- return {};
212
- }
213
- }
214
-
215
- // src/og-image/utils/metadata.ts
216
- function extractTitle(metadata) {
217
- if (typeof metadata.title === "string") {
218
- return metadata.title;
219
- }
220
- if (metadata.title) {
221
- if ("default" in metadata.title) {
222
- return metadata.title.default;
223
- }
224
- if ("absolute" in metadata.title) {
225
- return metadata.title.absolute;
226
- }
227
- }
228
- return "";
229
- }
230
- function extractDescription(metadata) {
231
- if (typeof metadata.description === "string") {
232
- return metadata.description;
233
- }
234
- return "";
235
- }
236
- function getSiteUrl() {
237
- if (typeof process !== "undefined" && process.env.NEXT_PUBLIC_SITE_URL) {
238
- return process.env.NEXT_PUBLIC_SITE_URL;
239
- }
240
- return "";
241
- }
242
- function generateOgImageMetadata(metadata, ogImageParams, options = {}) {
243
- const {
244
- ogImageBaseUrl = "https://djangocfg.com/api/og",
245
- siteUrl: providedSiteUrl,
246
- defaultParams = {},
247
- useBase64 = true
248
- } = options;
249
- const siteUrl = providedSiteUrl && providedSiteUrl !== "undefined" ? providedSiteUrl : getSiteUrl();
250
- const extractedTitle = extractTitle(metadata);
251
- const extractedDescription = extractDescription(metadata);
252
- const finalOgImageParams = {
253
- ...defaultParams,
254
- title: ogImageParams?.title || extractedTitle || defaultParams.title || "",
255
- description: ogImageParams?.description || extractedDescription || defaultParams.description || "",
256
- ...ogImageParams
257
- };
258
- const imageAlt = finalOgImageParams.title || finalOgImageParams.siteName;
259
- const relativeOgImageUrl = generateOgImageUrl(
260
- finalOgImageParams,
261
- { baseUrl: ogImageBaseUrl, useBase64 }
262
- );
263
- const ogImageUrl = siteUrl ? getAbsoluteOgImageUrl(relativeOgImageUrl, siteUrl) : relativeOgImageUrl;
264
- const existingOgImages = metadata.openGraph?.images ? Array.isArray(metadata.openGraph.images) ? metadata.openGraph.images : [metadata.openGraph.images] : [];
265
- const existingTwitterImages = metadata.twitter?.images ? Array.isArray(metadata.twitter.images) ? metadata.twitter.images : [metadata.twitter.images] : [];
266
- const finalMetadata = {
267
- ...metadata,
268
- openGraph: {
269
- ...metadata.openGraph,
270
- images: [
271
- ...existingOgImages,
272
- {
273
- url: ogImageUrl,
274
- width: 1200,
275
- height: 630,
276
- alt: imageAlt
277
- }
278
- ]
279
- },
280
- twitter: {
281
- ...metadata.twitter,
282
- card: "summary_large_image",
283
- images: [
284
- ...existingTwitterImages,
285
- {
286
- url: ogImageUrl,
287
- alt: imageAlt
288
- }
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
+ )
289
171
  ]
290
172
  }
291
- };
292
- if (!finalMetadata.metadataBase && siteUrl) {
293
- if (siteUrl.startsWith("http://") || siteUrl.startsWith("https://")) {
294
- try {
295
- finalMetadata.metadataBase = new URL(siteUrl);
296
- } catch (e) {
297
- }
298
- }
299
- }
300
- return finalMetadata;
301
- }
302
- function createOgImageMetadataGenerator(options) {
303
- return (metadata, ogImageParams) => {
304
- return generateOgImageMetadata(metadata, ogImageParams, options);
305
- };
173
+ );
306
174
  }
307
-
308
- // src/og-image/components/DefaultTemplate.tsx
309
- import { jsx, jsxs } from "react/jsx-runtime";
310
- function DefaultTemplate({
175
+ function LightTemplate({
311
176
  title,
312
177
  description,
313
178
  siteName,
@@ -315,21 +180,21 @@ function DefaultTemplate({
315
180
  // Visibility flags
316
181
  showLogo = true,
317
182
  showSiteName = true,
318
- // Background customization
319
- backgroundType = "gradient",
183
+ // Background customization (defaults to light theme)
184
+ backgroundType = "solid",
320
185
  gradientStart = "#667eea",
321
186
  gradientEnd = "#764ba2",
322
187
  backgroundColor = "#ffffff",
323
188
  // Typography - Title
324
189
  titleSize,
325
190
  titleWeight = 800,
326
- titleColor = "white",
191
+ titleColor = "#111",
327
192
  // Typography - Description
328
193
  descriptionSize = 32,
329
- descriptionColor = "rgba(255, 255, 255, 0.85)",
194
+ descriptionColor = "#666",
330
195
  // Typography - Site Name
331
196
  siteNameSize = 28,
332
- siteNameColor = "rgba(255, 255, 255, 0.95)",
197
+ siteNameColor = "#111",
333
198
  // Layout
334
199
  padding = 80,
335
200
  logoSize = 48,
@@ -431,7 +296,6 @@ function DefaultTemplate({
431
296
  color: titleColor,
432
297
  lineHeight: 1.1,
433
298
  letterSpacing: "-0.03em",
434
- textShadow: backgroundType === "gradient" ? "0 2px 20px rgba(0, 0, 0, 0.2)" : "none",
435
299
  maxWidth: "100%",
436
300
  wordWrap: "break-word"
437
301
  },
@@ -447,11 +311,7 @@ function DefaultTemplate({
447
311
  color: descriptionColor,
448
312
  lineHeight: 1.5,
449
313
  letterSpacing: "-0.01em",
450
- maxWidth: "90%",
451
- display: "-webkit-box",
452
- WebkitLineClamp: 2,
453
- WebkitBoxOrient: "vertical",
454
- overflow: "hidden"
314
+ maxWidth: "90%"
455
315
  },
456
316
  children: description
457
317
  }
@@ -475,168 +335,308 @@ function DefaultTemplate({
475
335
  }
476
336
  );
477
337
  }
478
- function LightTemplate({
479
- title,
480
- description,
481
- siteName,
482
- logo,
483
- // Visibility flags
484
- showLogo = true,
485
- showSiteName = true,
486
- // Background customization (defaults to light theme)
487
- backgroundType = "solid",
488
- gradientStart = "#667eea",
489
- gradientEnd = "#764ba2",
490
- backgroundColor = "#ffffff",
491
- // Typography - Title
492
- titleSize,
493
- titleWeight = 800,
494
- titleColor = "#111",
495
- // Typography - Description
496
- descriptionSize = 32,
497
- descriptionColor = "#666",
498
- // Typography - Site Name
499
- siteNameSize = 28,
500
- siteNameColor = "#111",
501
- // Layout
502
- padding = 80,
503
- logoSize = 48,
504
- // Dev mode
505
- devMode = false
506
- }) {
507
- const calculatedTitleSize = titleSize || (title.length > 60 ? 56 : 72);
508
- const backgroundStyle = backgroundType === "gradient" ? `linear-gradient(135deg, ${gradientStart} 0%, ${gradientEnd} 100%)` : backgroundColor;
509
- const gridOverlay = devMode ? /* @__PURE__ */ jsx(
510
- "div",
511
- {
512
- style: {
513
- position: "absolute",
514
- top: 0,
515
- left: 0,
516
- right: 0,
517
- bottom: 0,
518
- backgroundImage: `
519
- linear-gradient(rgba(0, 0, 0, 0.1) 1px, transparent 1px),
520
- linear-gradient(90deg, rgba(0, 0, 0, 0.1) 1px, transparent 1px)
521
- `,
522
- backgroundSize: "20px 20px",
523
- pointerEvents: "none",
524
- zIndex: 10
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
+ };
406
+ }
407
+
408
+ // src/og-image/utils/url.ts
409
+ var DEFAULT_OG_IMAGE_BASE_URL = "https://djangocfg.com/api/og";
410
+ function encodeBase64(str) {
411
+ if (typeof Buffer !== "undefined") {
412
+ return Buffer.from(str, "utf-8").toString("base64");
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)));
427
+ }
428
+ }
429
+ function generateOgImageUrl(params, options = {}) {
430
+ const {
431
+ baseUrl = DEFAULT_OG_IMAGE_BASE_URL,
432
+ useBase64 = true
433
+ } = options;
434
+ if (useBase64) {
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
+ }
482
+ }
483
+ function parseOgImageData(searchParams) {
484
+ try {
485
+ let params;
486
+ if (searchParams instanceof URLSearchParams) {
487
+ params = {};
488
+ for (const [key, value] of searchParams.entries()) {
489
+ params[key] = value;
525
490
  }
491
+ } else {
492
+ params = searchParams;
526
493
  }
527
- ) : null;
528
- return /* @__PURE__ */ jsxs(
529
- "div",
530
- {
531
- style: {
532
- height: "100%",
533
- width: "100%",
534
- display: "flex",
535
- flexDirection: "column",
536
- alignItems: "flex-start",
537
- justifyContent: "space-between",
538
- background: backgroundStyle,
539
- padding: `${padding}px`,
540
- fontFamily: "system-ui, -apple-system, sans-serif",
541
- position: "relative"
542
- },
543
- children: [
544
- gridOverlay,
545
- (showLogo && logo || showSiteName && siteName) && /* @__PURE__ */ jsxs(
546
- "div",
547
- {
548
- style: {
549
- display: "flex",
550
- alignItems: "center",
551
- gap: "16px"
552
- },
553
- children: [
554
- showLogo && logo && // eslint-disable-next-line @next/next/no-img-element
555
- /* @__PURE__ */ jsx(
556
- "img",
557
- {
558
- src: logo,
559
- alt: "Logo",
560
- width: logoSize,
561
- height: logoSize,
562
- style: {
563
- borderRadius: "8px"
564
- }
565
- }
566
- ),
567
- showSiteName && siteName && /* @__PURE__ */ jsx(
568
- "div",
569
- {
570
- style: {
571
- fontSize: siteNameSize,
572
- fontWeight: 600,
573
- color: siteNameColor,
574
- letterSpacing: "-0.02em"
575
- },
576
- children: siteName
577
- }
578
- )
579
- ]
580
- }
581
- ),
582
- /* @__PURE__ */ jsxs(
583
- "div",
584
- {
585
- style: {
586
- display: "flex",
587
- flexDirection: "column",
588
- gap: "24px",
589
- flex: 1,
590
- justifyContent: "center"
591
- },
592
- children: [
593
- /* @__PURE__ */ jsx(
594
- "div",
595
- {
596
- style: {
597
- fontSize: calculatedTitleSize,
598
- fontWeight: titleWeight,
599
- color: titleColor,
600
- lineHeight: 1.1,
601
- letterSpacing: "-0.03em",
602
- maxWidth: "100%",
603
- wordWrap: "break-word"
604
- },
605
- children: title
606
- }
607
- ),
608
- description && /* @__PURE__ */ jsx(
609
- "div",
610
- {
611
- style: {
612
- fontSize: descriptionSize,
613
- fontWeight: 400,
614
- color: descriptionColor,
615
- lineHeight: 1.5,
616
- letterSpacing: "-0.01em",
617
- maxWidth: "90%"
618
- },
619
- children: description
620
- }
621
- )
622
- ]
623
- }
624
- ),
625
- /* @__PURE__ */ jsx(
626
- "div",
627
- {
628
- style: {
629
- display: "flex",
630
- width: "100%",
631
- height: "4px",
632
- background: backgroundType === "gradient" ? `linear-gradient(90deg, ${gradientStart} 0%, ${gradientEnd} 100%)` : gradientStart,
633
- borderRadius: "2px"
634
- }
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);
635
516
  }
636
- )
637
- ]
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
+ }
547
+ }
548
+
549
+ // src/og-image/utils/metadata.ts
550
+ function extractTitle(metadata) {
551
+ if (typeof metadata.title === "string") {
552
+ return metadata.title;
553
+ }
554
+ if (metadata.title) {
555
+ if ("default" in metadata.title) {
556
+ return metadata.title.default;
557
+ }
558
+ if ("absolute" in metadata.title) {
559
+ return metadata.title.absolute;
638
560
  }
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 "";
575
+ }
576
+ function generateOgImageMetadata(metadata, ogImageParams, options = {}) {
577
+ const {
578
+ ogImageBaseUrl = "https://djangocfg.com/api/og",
579
+ siteUrl: providedSiteUrl,
580
+ defaultParams = {},
581
+ useBase64 = true
582
+ } = options;
583
+ const siteUrl = providedSiteUrl && providedSiteUrl !== "undefined" ? providedSiteUrl : getSiteUrl();
584
+ const extractedTitle = extractTitle(metadata);
585
+ const extractedDescription = extractDescription(metadata);
586
+ const finalOgImageParams = {
587
+ ...defaultParams,
588
+ title: ogImageParams?.title || extractedTitle || defaultParams.title || "",
589
+ description: ogImageParams?.description || extractedDescription || defaultParams.description || "",
590
+ ...ogImageParams
591
+ };
592
+ const imageAlt = finalOgImageParams.title || finalOgImageParams.siteName;
593
+ const relativeOgImageUrl = generateOgImageUrl(
594
+ finalOgImageParams,
595
+ { baseUrl: ogImageBaseUrl, useBase64 }
639
596
  );
597
+ const ogImageUrl = siteUrl ? getAbsoluteOgImageUrl(relativeOgImageUrl, siteUrl) : relativeOgImageUrl;
598
+ const existingOgImages = metadata.openGraph?.images ? Array.isArray(metadata.openGraph.images) ? metadata.openGraph.images : [metadata.openGraph.images] : [];
599
+ const existingTwitterImages = metadata.twitter?.images ? Array.isArray(metadata.twitter.images) ? metadata.twitter.images : [metadata.twitter.images] : [];
600
+ const finalMetadata = {
601
+ ...metadata,
602
+ openGraph: {
603
+ ...metadata.openGraph,
604
+ images: [
605
+ ...existingOgImages,
606
+ {
607
+ url: ogImageUrl,
608
+ width: 1200,
609
+ height: 630,
610
+ alt: imageAlt
611
+ }
612
+ ]
613
+ },
614
+ twitter: {
615
+ ...metadata.twitter,
616
+ card: "summary_large_image",
617
+ images: [
618
+ ...existingTwitterImages,
619
+ {
620
+ url: ogImageUrl,
621
+ alt: imageAlt
622
+ }
623
+ ]
624
+ }
625
+ };
626
+ if (!finalMetadata.metadataBase && siteUrl) {
627
+ if (siteUrl.startsWith("http://") || siteUrl.startsWith("https://")) {
628
+ try {
629
+ finalMetadata.metadataBase = new URL(siteUrl);
630
+ } catch (e) {
631
+ }
632
+ }
633
+ }
634
+ return finalMetadata;
635
+ }
636
+ function createOgImageMetadataGenerator(options) {
637
+ return (metadata, ogImageParams) => {
638
+ return generateOgImageMetadata(metadata, ogImageParams, options);
639
+ };
640
640
  }
641
641
 
642
642
  // src/og-image/route.tsx