@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.
@@ -1,308 +0,0 @@
1
- import { Metadata } from 'next';
2
-
3
- /**
4
- * Font Utilities for OG Image Generation
5
- *
6
- * Provides dynamic font loading from Google Fonts without requiring files in public/
7
- * Based on Vercel's official @vercel/og documentation
8
- */
9
- interface FontConfig {
10
- name: string;
11
- weight?: 400 | 500 | 600 | 700 | 800 | 900;
12
- style?: 'normal' | 'italic';
13
- data: ArrayBuffer;
14
- }
15
- /**
16
- * Load a Google Font dynamically
17
- *
18
- * @param font - Font family name (e.g., "Inter", "Roboto", "Manrope")
19
- * @param text - Text to optimize font for (optional, reduces file size)
20
- * @param weight - Font weight (default: 700)
21
- * @returns ArrayBuffer of font data
22
- *
23
- * @example
24
- * const fontData = await loadGoogleFont('Manrope', 'Hello World', 700);
25
- */
26
- declare function loadGoogleFont(font: string, text?: string, weight?: number): Promise<ArrayBuffer>;
27
- /**
28
- * Load multiple Google Fonts
29
- *
30
- * @param fonts - Array of font configurations to load
31
- * @returns Array of FontConfig objects ready for ImageResponse
32
- *
33
- * @example
34
- * const fonts = await loadGoogleFonts([
35
- * { family: 'Manrope', weight: 700 },
36
- * { family: 'Inter', weight: 400 }
37
- * ]);
38
- */
39
- declare function loadGoogleFonts(fonts: Array<{
40
- family: string;
41
- weight?: 400 | 500 | 600 | 700 | 800 | 900;
42
- style?: 'normal' | 'italic';
43
- text?: string;
44
- }>): Promise<FontConfig[]>;
45
- /**
46
- * Create a font loader with caching
47
- *
48
- * Useful for reusing font data across multiple OG image requests
49
- *
50
- * @example
51
- * const fontLoader = createFontLoader();
52
- * const font = await fontLoader.load('Manrope', 700);
53
- */
54
- declare function createFontLoader(): {
55
- /**
56
- * Load a font with caching
57
- */
58
- load(family: string, weight?: number, text?: string): Promise<ArrayBuffer>;
59
- /**
60
- * Clear the cache
61
- */
62
- clear(): void;
63
- /**
64
- * Get cache size
65
- */
66
- size(): number;
67
- };
68
-
69
- /**
70
- * URL Generation Helpers for OG Images
71
- *
72
- * Utilities to generate OG image URLs with proper query parameters
73
- */
74
- /**
75
- * Encode string to base64 with Unicode support
76
- * Works in both browser and Node.js environments
77
- */
78
- declare function encodeBase64(str: string): string;
79
- /**
80
- * Decode base64 string with Unicode support
81
- * Works in both browser, Node.js, and Edge Runtime environments
82
- */
83
- declare function decodeBase64(str: string): string;
84
- /**
85
- * OG Image URL parameters
86
- * All parameters can be encoded in URL via base64
87
- */
88
- interface OgImageUrlParams {
89
- /** Page title */
90
- title: string;
91
- /** Page description (optional) */
92
- description?: string;
93
- /** Site name (optional) */
94
- siteName?: string;
95
- /** Logo URL (optional) */
96
- logo?: string;
97
- /** Background type: 'gradient' or 'solid' */
98
- backgroundType?: 'gradient' | 'solid';
99
- /** Gradient start color (hex) */
100
- gradientStart?: string;
101
- /** Gradient end color (hex) */
102
- gradientEnd?: string;
103
- /** Background color (for solid type) */
104
- backgroundColor?: string;
105
- /** Title font size (px) */
106
- titleSize?: number;
107
- /** Title font weight */
108
- titleWeight?: number;
109
- /** Title text color */
110
- titleColor?: string;
111
- /** Description font size (px) */
112
- descriptionSize?: number;
113
- /** Description text color */
114
- descriptionColor?: string;
115
- /** Site name font size (px) */
116
- siteNameSize?: number;
117
- /** Site name text color */
118
- siteNameColor?: string;
119
- /** Padding (px) */
120
- padding?: number;
121
- /** Logo size (px) */
122
- logoSize?: number;
123
- /** Show logo flag */
124
- showLogo?: boolean;
125
- /** Show site name flag */
126
- showSiteName?: boolean;
127
- /** Additional custom parameters */
128
- [key: string]: string | number | boolean | undefined;
129
- }
130
- /**
131
- * Options for generating OG image URL
132
- */
133
- interface GenerateOgImageUrlOptions {
134
- /**
135
- * Base URL of the OG image API route
136
- * @default 'https://djangocfg.com/api/og'
137
- */
138
- baseUrl?: string;
139
- /**
140
- * If true, encode params as base64 for safer URLs
141
- * @default true
142
- */
143
- useBase64?: boolean;
144
- }
145
- /**
146
- * Generate OG image URL with query parameters or base64 encoding
147
- *
148
- * @param params - URL parameters for the OG image
149
- * @param options - Generation options (baseUrl, useBase64)
150
- * @returns Complete OG image URL with encoded parameters
151
- *
152
- * @example
153
- * ```typescript
154
- * // Using default baseUrl (https://djangocfg.com/api/og)
155
- * const url = generateOgImageUrl({
156
- * title: 'My Page Title',
157
- * description: 'Page description here',
158
- * });
159
- *
160
- * // With custom baseUrl
161
- * const url = generateOgImageUrl(
162
- * { title: 'My Page' },
163
- * { baseUrl: '/api/og' }
164
- * );
165
- * ```
166
- */
167
- declare function generateOgImageUrl(params: OgImageUrlParams, options?: GenerateOgImageUrlOptions): string;
168
- /**
169
- * Get absolute OG image URL from relative path
170
- *
171
- * Useful for generating absolute URLs required by Open Graph meta tags
172
- *
173
- * @param relativePath - Relative OG image path (e.g., '/api/og?title=Hello')
174
- * @param siteUrl - Base site URL (e.g., 'https://example.com')
175
- * @returns Absolute URL
176
- *
177
- * @example
178
- * ```typescript
179
- * const absolute = getAbsoluteOgImageUrl(
180
- * '/api/og?title=Hello',
181
- * 'https://example.com'
182
- * );
183
- * // Result: https://example.com/api/og?title=Hello
184
- * ```
185
- */
186
- declare function getAbsoluteOgImageUrl(relativePath: string, siteUrl: string): string;
187
- /**
188
- * Create OG image URL builder with preset configuration
189
- *
190
- * Useful when you want to reuse the same base URL and default parameters
191
- *
192
- * @param defaults - Default parameters to merge with each URL generation
193
- * @param options - Default options (baseUrl, useBase64)
194
- * @returns URL builder function
195
- *
196
- * @example
197
- * ```typescript
198
- * const buildOgUrl = createOgImageUrlBuilder(
199
- * { siteName: 'My Site', logo: '/logo.png' },
200
- * { baseUrl: '/api/og' }
201
- * );
202
- *
203
- * const url1 = buildOgUrl({ title: 'Page 1' });
204
- * const url2 = buildOgUrl({ title: 'Page 2', description: 'Custom desc' });
205
- * ```
206
- */
207
- declare function createOgImageUrlBuilder(defaults?: Partial<OgImageUrlParams>, options?: GenerateOgImageUrlOptions): (params: OgImageUrlParams) => string;
208
- /**
209
- * Parse OG image URL parameters from a URL string (legacy query params)
210
- *
211
- * @param url - Full or relative URL with query parameters
212
- * @returns Parsed parameters object
213
- *
214
- * @example
215
- * ```typescript
216
- * const params = parseOgImageUrl('/api/og?title=Hello&description=World');
217
- * // Result: { title: 'Hello', description: 'World' }
218
- * ```
219
- */
220
- declare function parseOgImageUrl(url: string): Record<string, string>;
221
- /**
222
- * Parse OG image data from base64-encoded query parameter
223
- *
224
- * Use this in your API route to decode the `data` parameter
225
- * Supports both base64 (new) and legacy query params format
226
- *
227
- * @param searchParams - URL search params or request object
228
- * @returns Parsed OG image parameters
229
- *
230
- * @example
231
- * ```typescript
232
- * // In Next.js API route (pages/api/og.ts)
233
- * export default function handler(req) {
234
- * const params = parseOgImageData(req.query);
235
- * // { title: 'Hello', description: 'World' }
236
- * }
237
- *
238
- * // In Next.js App Router (app/api/og/route.ts)
239
- * export async function GET(request: Request) {
240
- * const { searchParams } = new URL(request.url);
241
- * const params = parseOgImageData(Object.fromEntries(searchParams));
242
- * // { title: 'Hello', description: 'World' }
243
- * }
244
- * ```
245
- */
246
- declare function parseOgImageData(searchParams: Record<string, string | string[] | undefined> | URLSearchParams): Record<string, string>;
247
-
248
- /**
249
- * Metadata Utilities for OG Images
250
- *
251
- * Helpers to automatically add og:image to Next.js metadata
252
- */
253
-
254
- /**
255
- * Options for generating OG image metadata
256
- */
257
- interface AppMetadataOptions {
258
- /** Base URL of the OG image API route (e.g., '/api/og') */
259
- ogImageBaseUrl?: string;
260
- /** Site URL for absolute URLs (e.g., 'https://example.com') */
261
- siteUrl?: string;
262
- /** Default parameters to merge with page-specific params */
263
- defaultParams?: Partial<OgImageUrlParams>;
264
- /** Whether to use base64 encoding (default: true) */
265
- useBase64?: boolean;
266
- /** Favicon URL (e.g., '/favicon.png') - automatically added to metadata.icons */
267
- favicon?: string;
268
- /** Apple touch icon URL (e.g., '/apple-icon.png') - automatically added to metadata.icons */
269
- appleIcon?: string;
270
- }
271
- declare function generateAppMetadata(metadata: Metadata, ogImageParams?: Partial<OgImageUrlParams>, options?: AppMetadataOptions): Metadata;
272
- /**
273
- * Create OG image metadata generator with preset configuration
274
- *
275
- * Useful when you want to reuse the same configuration across multiple pages
276
- *
277
- * @param options - Configuration options
278
- * @returns Metadata generator function
279
- *
280
- * @example
281
- * ```typescript
282
- * // In a shared file (e.g., lib/metadata.ts)
283
- * import { createAppMetadataGenerator } from '@djangocfg/nextjs/og-image';
284
- * import { settings } from '@/core/settings';
285
- *
286
- * export const generateMetadata = createAppMetadataGenerator({
287
- * ogImageBaseUrl: '/api/og',
288
- * siteUrl: settings.app.siteUrl,
289
- * favicon: settings.app.icons.favicon,
290
- * appleIcon: settings.app.icons.logo192,
291
- * defaultParams: {
292
- * siteName: settings.app.name,
293
- * logo: settings.app.icons.logoVector,
294
- * },
295
- * });
296
- *
297
- * // In page.tsx
298
- * import { generateMetadata } from '@/lib/metadata';
299
- *
300
- * export const metadata = generateMetadata({
301
- * title: 'My Page',
302
- * description: 'Description',
303
- * });
304
- * ```
305
- */
306
- declare function createAppMetadataGenerator(options: AppMetadataOptions): (metadata: Metadata, ogImageParams?: Partial<OgImageUrlParams>) => Metadata;
307
-
308
- export { type AppMetadataOptions, type FontConfig, type OgImageUrlParams, createAppMetadataGenerator, createFontLoader, createOgImageUrlBuilder, decodeBase64, encodeBase64, generateAppMetadata, generateOgImageUrl, getAbsoluteOgImageUrl, loadGoogleFont, loadGoogleFonts, parseOgImageData, parseOgImageUrl };
@@ -1,327 +0,0 @@
1
- // src/og-image/utils/fonts.ts
2
- async function loadGoogleFont(font, text, weight = 700) {
3
- let url = `https://fonts.googleapis.com/css2?family=${font}:wght@${weight}`;
4
- if (text) {
5
- url += `&text=${encodeURIComponent(text)}`;
6
- }
7
- try {
8
- const css = await fetch(url, {
9
- headers: {
10
- // Required to get TTF format instead of WOFF2
11
- "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"
12
- }
13
- }).then((res) => res.text());
14
- const resource = css.match(/src: url\((.+)\) format\('(opentype|truetype)'\)/);
15
- if (!resource || !resource[1]) {
16
- throw new Error(`Failed to parse font URL from CSS for font: ${font}`);
17
- }
18
- const response = await fetch(resource[1]);
19
- if (response.status !== 200) {
20
- throw new Error(`Failed to fetch font data: HTTP ${response.status}`);
21
- }
22
- return await response.arrayBuffer();
23
- } catch (error) {
24
- console.error(`Error loading Google Font "${font}":`, error);
25
- throw new Error(`Failed to load font "${font}": ${error instanceof Error ? error.message : "Unknown error"}`);
26
- }
27
- }
28
- async function loadGoogleFonts(fonts) {
29
- const fontConfigs = await Promise.all(
30
- fonts.map(async ({ family, weight = 700, style = "normal", text }) => {
31
- const data = await loadGoogleFont(family, text, weight);
32
- return {
33
- name: family,
34
- weight,
35
- style,
36
- data
37
- };
38
- })
39
- );
40
- return fontConfigs;
41
- }
42
- function createFontLoader() {
43
- const cache = /* @__PURE__ */ new Map();
44
- return {
45
- /**
46
- * Load a font with caching
47
- */
48
- async load(family, weight = 700, text) {
49
- const cacheKey = `${family}-${weight}-${text || "all"}`;
50
- if (!cache.has(cacheKey)) {
51
- cache.set(cacheKey, loadGoogleFont(family, text, weight));
52
- }
53
- return cache.get(cacheKey);
54
- },
55
- /**
56
- * Clear the cache
57
- */
58
- clear() {
59
- cache.clear();
60
- },
61
- /**
62
- * Get cache size
63
- */
64
- size() {
65
- return cache.size;
66
- }
67
- };
68
- }
69
-
70
- // src/og-image/utils/url.ts
71
- var DEFAULT_OG_IMAGE_BASE_URL = "https://djangocfg.com/api/og";
72
- function encodeBase64(str) {
73
- if (typeof Buffer !== "undefined") {
74
- return Buffer.from(str, "utf-8").toString("base64");
75
- }
76
- return btoa(unescape(encodeURIComponent(str)));
77
- }
78
- function decodeBase64(str) {
79
- if (typeof Buffer !== "undefined") {
80
- return Buffer.from(str, "base64").toString("utf-8");
81
- }
82
- try {
83
- const binaryString = atob(str);
84
- return decodeURIComponent(
85
- binaryString.split("").map((c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2)).join("")
86
- );
87
- } catch (error) {
88
- return decodeURIComponent(escape(atob(str)));
89
- }
90
- }
91
- function generateOgImageUrl(params, options = {}) {
92
- const {
93
- baseUrl = DEFAULT_OG_IMAGE_BASE_URL,
94
- useBase64 = true
95
- } = options;
96
- if (useBase64) {
97
- const cleanParams = {};
98
- Object.entries(params).forEach(([key, value]) => {
99
- if (value !== void 0 && value !== null && value !== "") {
100
- cleanParams[key] = value;
101
- }
102
- });
103
- const jsonString = JSON.stringify(cleanParams);
104
- const base64Data = encodeBase64(jsonString);
105
- return `${baseUrl}/${base64Data}/`;
106
- } else {
107
- const searchParams = new URLSearchParams();
108
- Object.entries(params).forEach(([key, value]) => {
109
- if (value !== void 0 && value !== null && value !== "") {
110
- searchParams.append(key, String(value));
111
- }
112
- });
113
- const query = searchParams.toString();
114
- return query ? `${baseUrl}?${query}` : baseUrl;
115
- }
116
- }
117
- function getAbsoluteOgImageUrl(relativePath, siteUrl) {
118
- if (relativePath.startsWith("http://") || relativePath.startsWith("https://")) {
119
- return relativePath;
120
- }
121
- const cleanSiteUrl = siteUrl.replace(/\/$/, "");
122
- const cleanPath = relativePath.startsWith("/") ? relativePath : `/${relativePath}`;
123
- return `${cleanSiteUrl}${cleanPath}`;
124
- }
125
- function createOgImageUrlBuilder(defaults = {}, options = {}) {
126
- return (params) => {
127
- return generateOgImageUrl(
128
- { ...defaults, ...params },
129
- options
130
- );
131
- };
132
- }
133
- function parseOgImageUrl(url) {
134
- try {
135
- const urlObj = new URL(url, "http://dummy.com");
136
- const params = {};
137
- urlObj.searchParams.forEach((value, key) => {
138
- params[key] = value;
139
- });
140
- return params;
141
- } catch {
142
- return {};
143
- }
144
- }
145
- function parseOgImageData(searchParams) {
146
- try {
147
- let params;
148
- if (searchParams instanceof URLSearchParams) {
149
- params = {};
150
- for (const [key, value] of searchParams.entries()) {
151
- params[key] = value;
152
- }
153
- } else {
154
- params = searchParams;
155
- }
156
- if (process.env.NODE_ENV === "development") {
157
- console.log("[parseOgImageData] Input params keys:", Object.keys(params));
158
- console.log("[parseOgImageData] Input params:", params);
159
- }
160
- const dataParam = params.data;
161
- if (dataParam && typeof dataParam === "string" && dataParam.trim() !== "") {
162
- if (process.env.NODE_ENV === "development") {
163
- console.log("[parseOgImageData] Found data param, length:", dataParam.length);
164
- }
165
- try {
166
- const decoded = decodeBase64(dataParam);
167
- if (process.env.NODE_ENV === "development") {
168
- console.log("[parseOgImageData] Decoded string:", decoded.substring(0, 100));
169
- }
170
- const parsed = JSON.parse(decoded);
171
- if (process.env.NODE_ENV === "development") {
172
- console.log("[parseOgImageData] Parsed JSON:", parsed);
173
- }
174
- const result2 = {};
175
- for (const [key, value] of Object.entries(parsed)) {
176
- if (value !== void 0 && value !== null) {
177
- result2[key] = String(value);
178
- }
179
- }
180
- if (process.env.NODE_ENV === "development") {
181
- console.log("[parseOgImageData] Result:", result2);
182
- }
183
- return result2;
184
- } catch (decodeError) {
185
- console.error("[parseOgImageData] Error decoding/parsing data param:", decodeError);
186
- if (decodeError instanceof Error) {
187
- console.error("[parseOgImageData] Error message:", decodeError.message);
188
- }
189
- }
190
- } else {
191
- if (process.env.NODE_ENV === "development") {
192
- console.log("[parseOgImageData] No data param found or empty");
193
- }
194
- }
195
- const result = {};
196
- for (const [key, value] of Object.entries(params)) {
197
- if (key !== "data" && value !== void 0 && value !== null) {
198
- result[key] = Array.isArray(value) ? value[0] : String(value);
199
- }
200
- }
201
- if (process.env.NODE_ENV === "development") {
202
- console.log("[parseOgImageData] Fallback result:", result);
203
- }
204
- return result;
205
- } catch (error) {
206
- console.error("[parseOgImageData] Unexpected error:", error);
207
- return {};
208
- }
209
- }
210
-
211
- // src/og-image/utils/metadata.ts
212
- function extractTitle(metadata) {
213
- if (typeof metadata.title === "string") {
214
- return metadata.title;
215
- }
216
- if (metadata.title) {
217
- if ("default" in metadata.title) {
218
- return metadata.title.default;
219
- }
220
- if ("absolute" in metadata.title) {
221
- return metadata.title.absolute;
222
- }
223
- }
224
- return "";
225
- }
226
- function extractDescription(metadata) {
227
- if (typeof metadata.description === "string") {
228
- return metadata.description;
229
- }
230
- return "";
231
- }
232
- function getSiteUrl() {
233
- if (typeof process !== "undefined" && process.env.NEXT_PUBLIC_SITE_URL) {
234
- return process.env.NEXT_PUBLIC_SITE_URL;
235
- }
236
- return "";
237
- }
238
- function generateAppMetadata(metadata, ogImageParams, options = {}) {
239
- const {
240
- ogImageBaseUrl = "https://djangocfg.com/api/og",
241
- siteUrl: providedSiteUrl,
242
- defaultParams = {},
243
- useBase64 = true,
244
- favicon,
245
- appleIcon
246
- } = options;
247
- const siteUrl = providedSiteUrl && providedSiteUrl !== "undefined" ? providedSiteUrl : getSiteUrl();
248
- const extractedTitle = extractTitle(metadata);
249
- const extractedDescription = extractDescription(metadata);
250
- const finalOgImageParams = {
251
- ...defaultParams,
252
- title: ogImageParams?.title || extractedTitle || defaultParams.title || "",
253
- description: ogImageParams?.description || extractedDescription || defaultParams.description || "",
254
- ...ogImageParams
255
- };
256
- const imageAlt = finalOgImageParams.title || finalOgImageParams.siteName;
257
- const relativeOgImageUrl = generateOgImageUrl(
258
- finalOgImageParams,
259
- { baseUrl: ogImageBaseUrl, useBase64 }
260
- );
261
- const ogImageUrl = siteUrl ? getAbsoluteOgImageUrl(relativeOgImageUrl, siteUrl) : relativeOgImageUrl;
262
- const existingOgImages = metadata.openGraph?.images ? Array.isArray(metadata.openGraph.images) ? metadata.openGraph.images : [metadata.openGraph.images] : [];
263
- const existingTwitterImages = metadata.twitter?.images ? Array.isArray(metadata.twitter.images) ? metadata.twitter.images : [metadata.twitter.images] : [];
264
- const finalMetadata = {
265
- ...metadata,
266
- openGraph: {
267
- ...metadata.openGraph,
268
- images: [
269
- ...existingOgImages,
270
- {
271
- url: ogImageUrl,
272
- width: 1200,
273
- height: 630,
274
- alt: imageAlt
275
- }
276
- ]
277
- },
278
- twitter: {
279
- ...metadata.twitter,
280
- card: "summary_large_image",
281
- images: [
282
- ...existingTwitterImages,
283
- {
284
- url: ogImageUrl,
285
- alt: imageAlt
286
- }
287
- ]
288
- }
289
- };
290
- if (!finalMetadata.metadataBase && siteUrl) {
291
- if (siteUrl.startsWith("http://") || siteUrl.startsWith("https://")) {
292
- try {
293
- finalMetadata.metadataBase = new URL(siteUrl);
294
- } catch (e) {
295
- }
296
- }
297
- }
298
- if (favicon || appleIcon) {
299
- const existingIcons = metadata.icons && typeof metadata.icons === "object" && !Array.isArray(metadata.icons) ? metadata.icons : {};
300
- finalMetadata.icons = {
301
- ...existingIcons,
302
- ...favicon && { icon: favicon },
303
- ...appleIcon && { apple: appleIcon }
304
- };
305
- }
306
- return finalMetadata;
307
- }
308
- function createAppMetadataGenerator(options) {
309
- return (metadata, ogImageParams) => {
310
- return generateAppMetadata(metadata, ogImageParams, options);
311
- };
312
- }
313
- export {
314
- createAppMetadataGenerator,
315
- createFontLoader,
316
- createOgImageUrlBuilder,
317
- decodeBase64,
318
- encodeBase64,
319
- generateAppMetadata,
320
- generateOgImageUrl,
321
- getAbsoluteOgImageUrl,
322
- loadGoogleFont,
323
- loadGoogleFonts,
324
- parseOgImageData,
325
- parseOgImageUrl
326
- };
327
- //# sourceMappingURL=index.mjs.map