nuxt-og-image 6.9.0 → 6.9.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/chunks/tw4.mjs +1 -1
- package/dist/chunks/uno.mjs +2 -2
- package/dist/module.d.mts +1 -1
- package/dist/module.d.ts +1 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +1 -1
- package/dist/runtime/app/composables/defineOgImageScreenshot.js +4 -1
- package/dist/runtime/server/plugins/prerender.js +4 -3
- package/dist/runtime/shared/urlEncoding.d.ts +9 -3
- package/dist/runtime/shared/urlEncoding.js +27 -10
- package/dist/runtime/types.d.ts +2 -1
- package/dist/shared/{nuxt-og-image.CTD0ZPNd.mjs → nuxt-og-image.CNS1IVAi.mjs} +33 -11
- package/package.json +1 -1
package/dist/chunks/tw4.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { readFile } from 'node:fs/promises';
|
|
|
2
2
|
import { pathToFileURL } from 'node:url';
|
|
3
3
|
import { resolveModulePath } from 'exsolve';
|
|
4
4
|
import { dirname, join } from 'pathe';
|
|
5
|
-
import { e as extractVariantBaseClasses, r as resolveVariantPrefixes, h as resolveExtractedVars, l as resolveVarsDeep, s as simplifyCss, p as postProcessStyles, m as loadLightningCss, a as extractCssVars, c as extractUniversalVars, d as extractPerClassVars, g as extractVarsFromCss, f as extractClassStyles } from '../shared/nuxt-og-image.
|
|
5
|
+
import { e as extractVariantBaseClasses, r as resolveVariantPrefixes, h as resolveExtractedVars, l as resolveVarsDeep, s as simplifyCss, p as postProcessStyles, m as loadLightningCss, a as extractCssVars, c as extractUniversalVars, d as extractPerClassVars, g as extractVarsFromCss, f as extractClassStyles } from '../shared/nuxt-og-image.CNS1IVAi.mjs';
|
|
6
6
|
import 'node:crypto';
|
|
7
7
|
import 'node:fs';
|
|
8
8
|
import '@nuxt/kit';
|
package/dist/chunks/uno.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import { defu } from 'defu';
|
|
3
3
|
import { logger } from '../../dist/runtime/logger.js';
|
|
4
|
-
import { e as extractVariantBaseClasses, s as simplifyCss, a as extractCssVars, b as extractPropertyInitialValues, c as extractUniversalVars, d as extractPerClassVars, f as extractClassStyles, p as postProcessStyles, r as resolveVariantPrefixes, i as importOptionalPeer, g as extractVarsFromCss, h as resolveExtractedVars } from '../shared/nuxt-og-image.
|
|
5
|
-
export { j as importResolvedModule, k as resolveOptionalModulePath } from '../shared/nuxt-og-image.
|
|
4
|
+
import { e as extractVariantBaseClasses, s as simplifyCss, a as extractCssVars, b as extractPropertyInitialValues, c as extractUniversalVars, d as extractPerClassVars, f as extractClassStyles, p as postProcessStyles, r as resolveVariantPrefixes, i as importOptionalPeer, g as extractVarsFromCss, h as resolveExtractedVars } from '../shared/nuxt-og-image.CNS1IVAi.mjs';
|
|
5
|
+
export { j as importResolvedModule, k as resolveOptionalModulePath } from '../shared/nuxt-og-image.CNS1IVAi.mjs';
|
|
6
6
|
import 'node:crypto';
|
|
7
7
|
import 'node:fs';
|
|
8
8
|
import 'node:url';
|
package/dist/module.d.mts
CHANGED
|
@@ -142,7 +142,7 @@ interface ModuleOptions {
|
|
|
142
142
|
/**
|
|
143
143
|
* Browser renderer configuration.
|
|
144
144
|
*
|
|
145
|
-
*
|
|
145
|
+
* Set to true to enable defineOgImageScreenshot(), or configure a browser provider.
|
|
146
146
|
* For Cloudflare deployments, specify the browser binding name.
|
|
147
147
|
*
|
|
148
148
|
* @example { provider: 'cloudflare', binding: 'BROWSER' }
|
package/dist/module.d.ts
CHANGED
|
@@ -142,7 +142,7 @@ interface ModuleOptions {
|
|
|
142
142
|
/**
|
|
143
143
|
* Browser renderer configuration.
|
|
144
144
|
*
|
|
145
|
-
*
|
|
145
|
+
* Set to true to enable defineOgImageScreenshot(), or configure a browser provider.
|
|
146
146
|
* For Cloudflare deployments, specify the browser binding name.
|
|
147
147
|
*
|
|
148
148
|
* @example { provider: 'cloudflare', binding: 'BROWSER' }
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -10,7 +10,7 @@ import 'nuxtseo-shared/kit';
|
|
|
10
10
|
import 'pathe';
|
|
11
11
|
import 'pkg-types';
|
|
12
12
|
import 'std-env';
|
|
13
|
-
export { n as default } from './shared/nuxt-og-image.
|
|
13
|
+
export { n as default } from './shared/nuxt-og-image.CNS1IVAi.mjs';
|
|
14
14
|
import '../dist/runtime/logger.js';
|
|
15
15
|
import 'magic-string';
|
|
16
16
|
import 'oxc-walker';
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import { useRouter } from "#app";
|
|
1
|
+
import { createError, useRouter } from "#app";
|
|
2
|
+
import { useOgImageRuntimeConfig } from "../utils.js";
|
|
2
3
|
import { defineOgImageRaw } from "./_defineOgImageRaw.js";
|
|
3
4
|
export function defineOgImageScreenshot(options = {}) {
|
|
5
|
+
if (!useOgImageRuntimeConfig().browserEnabled)
|
|
6
|
+
throw createError({ statusCode: 500, statusMessage: "[nuxt-og-image] Screenshots require browser opt-in. Set ogImage.browser to true or a browser provider in nuxt.config.ts." });
|
|
4
7
|
const router = useRouter();
|
|
5
8
|
const route = router.currentRoute.value?.path || "/";
|
|
6
9
|
const { delay, mask, selector, colorScheme, ...rest } = options;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { parse } from "devalue";
|
|
2
|
-
import { parseURL } from "ufo";
|
|
2
|
+
import { parseURL, withoutBase } from "ufo";
|
|
3
3
|
import { appendResponseHeader } from "#nuxtseo/h3";
|
|
4
|
-
import { defineNitroPlugin } from "#nuxtseo/nitro";
|
|
4
|
+
import { defineNitroPlugin, useRuntimeConfig } from "#nuxtseo/nitro";
|
|
5
5
|
import { prerenderOptionsCache } from "#og-image-cache";
|
|
6
6
|
import { createSitePathResolver } from "#site-config/server/composables/utils";
|
|
7
7
|
import { isInternalRoute } from "../../shared.js";
|
|
@@ -37,9 +37,10 @@ export default defineNitroPlugin(async (nitro) => {
|
|
|
37
37
|
});
|
|
38
38
|
const key = resolvePathCacheKey(ctx.event, resolvePathWithBase(path));
|
|
39
39
|
await prerenderOptionsCache.setItem(key, payloads);
|
|
40
|
+
const pagePath = withoutBase(path, useRuntimeConfig().app.baseURL);
|
|
40
41
|
for (const [_ogKey, opt] of payloads) {
|
|
41
42
|
if (opt._hash) {
|
|
42
|
-
await prerenderOptionsCache.setItem(`hash:${opt._hash}`, opt);
|
|
43
|
+
await prerenderOptionsCache.setItem(`hash:${opt._hash}`, { ...opt, _path: pagePath });
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
46
|
const prerenderPaths = ctx.event.context._ogImagePrerenderPaths;
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Generate a deterministic hash from options object
|
|
3
|
-
* Excludes _path so images with same options can be cached across pages
|
|
3
|
+
* Excludes _path so images with same options can be cached across pages,
|
|
4
|
+
* except for PageScreenshot, which renders the page itself.
|
|
4
5
|
* Optionally includes componentHash and version for cache busting
|
|
6
|
+
*
|
|
7
|
+
* Uses the first 64 bits of a SHA-256 digest over a canonical (key-sorted)
|
|
8
|
+
* serialization. The previous 32-bit rolling hash collided on large sites,
|
|
9
|
+
* silently overwriting other pages' prerendered images.
|
|
5
10
|
*/
|
|
6
11
|
export declare function hashOgImageOptions(options: Record<string, any>, componentHash?: string, version?: string): string;
|
|
7
12
|
/**
|
|
@@ -25,7 +30,8 @@ export interface BuildOgImageUrlResult {
|
|
|
25
30
|
/**
|
|
26
31
|
* Build full OG image URL
|
|
27
32
|
*
|
|
28
|
-
*
|
|
33
|
+
* Static URLs fall back to hash mode when encoded params exceed MAX_PATH_LENGTH
|
|
34
|
+
* or hold a character outside [A-Za-z0-9_.~-]:
|
|
29
35
|
* - Returns short path: /_og/s/o_<hash>.png
|
|
30
36
|
* - Returns hash in result for cache storage
|
|
31
37
|
*
|
|
@@ -34,7 +40,7 @@ export interface BuildOgImageUrlResult {
|
|
|
34
40
|
* @param isStatic - Whether this is a static/prerendered image
|
|
35
41
|
* @param defaults - Optional defaults to skip from URL (keeps URLs shorter)
|
|
36
42
|
* @example buildOgImageUrl({ width: 1200, props: { title: 'Hello' } }, 'png', true)
|
|
37
|
-
* // Returns: { url: "/_og/s/
|
|
43
|
+
* // Returns: { url: "/_og/s/o_<hash>.png", hash: "<hash>" }
|
|
38
44
|
*/
|
|
39
45
|
export declare function buildOgImageUrl(options: Record<string, any>, extension?: string, isStatic?: boolean, defaults?: Record<string, any>, secret?: string): BuildOgImageUrlResult;
|
|
40
46
|
/**
|
|
@@ -19,6 +19,7 @@ const RE_COMMA_PARAM_SEPARATOR = /,(?=\w+_)/;
|
|
|
19
19
|
const RE_SIGNATURE_SUFFIX = /,s_[^,]+$/;
|
|
20
20
|
const RE_NON_ASCII = /[^\u0000-\u007F]/;
|
|
21
21
|
const RE_WINDOWS_RESERVED_FILENAME_CHARACTERS = /[<>:"/\\|?*\u0000-\u001F]/;
|
|
22
|
+
const RE_NOT_UNRESERVED = /[^\w.~-]/;
|
|
22
23
|
const PARAM_ALIASES = {
|
|
23
24
|
w: "width",
|
|
24
25
|
h: "height",
|
|
@@ -85,19 +86,35 @@ function b64Decode(str) {
|
|
|
85
86
|
}
|
|
86
87
|
return Buffer.from(padded, "base64").toString("utf8");
|
|
87
88
|
}
|
|
88
|
-
function
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
89
|
+
function canonicalJson(value) {
|
|
90
|
+
if (value === null || typeof value !== "object")
|
|
91
|
+
return JSON.stringify(value) ?? "null";
|
|
92
|
+
if (Array.isArray(value))
|
|
93
|
+
return `[${value.map((item) => canonicalJson(item)).join(",")}]`;
|
|
94
|
+
const record = value;
|
|
95
|
+
const entries = Object.keys(record).sort().filter((key) => record[key] !== void 0).map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`);
|
|
96
|
+
return `{${entries.join(",")}}`;
|
|
97
|
+
}
|
|
98
|
+
const B64URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
99
|
+
function base64UrlToHex(input) {
|
|
100
|
+
let hex = "";
|
|
101
|
+
let buffer = 0;
|
|
102
|
+
let bits = 0;
|
|
103
|
+
for (const char of input) {
|
|
104
|
+
buffer = buffer << 6 | B64URL_ALPHABET.indexOf(char);
|
|
105
|
+
bits += 6;
|
|
106
|
+
if (bits >= 8) {
|
|
107
|
+
bits -= 8;
|
|
108
|
+
hex += (buffer >> bits & 255).toString(16).padStart(2, "0");
|
|
109
|
+
}
|
|
94
110
|
}
|
|
95
|
-
return
|
|
111
|
+
return hex;
|
|
96
112
|
}
|
|
97
113
|
export function hashOgImageOptions(options, componentHash, version) {
|
|
98
|
-
const { _path, _hash, ...
|
|
114
|
+
const { _path, _hash, ...rest } = options;
|
|
115
|
+
const hashableOptions = rest.component === "PageScreenshot" ? { ...rest, _path } : rest;
|
|
99
116
|
const hashInput = componentHash || version ? [hashableOptions, componentHash || "", version || ""] : hashableOptions;
|
|
100
|
-
return
|
|
117
|
+
return base64UrlToHex(digest(canonicalJson(hashInput))).slice(0, 16);
|
|
101
118
|
}
|
|
102
119
|
export function encodeOgImageParams(options, defaults) {
|
|
103
120
|
const parts = [];
|
|
@@ -233,7 +250,7 @@ export function decodeOgImageParams(encoded) {
|
|
|
233
250
|
export function buildOgImageUrl(options, extension = "png", isStatic = false, defaults, secret) {
|
|
234
251
|
const encoded = encodeOgImageParams(options, defaults);
|
|
235
252
|
const prefix = isStatic ? "/_og/s" : "/_og/d";
|
|
236
|
-
if (isStatic && (encoded.length > MAX_PATH_LENGTH ||
|
|
253
|
+
if (isStatic && (encoded.length > MAX_PATH_LENGTH || RE_NOT_UNRESERVED.test(encoded))) {
|
|
237
254
|
const hash = hashOgImageOptions(options);
|
|
238
255
|
return {
|
|
239
256
|
url: `${prefix}/o_${hash}.${extension}`,
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -31,6 +31,7 @@ export interface OgImageIconsData {
|
|
|
31
31
|
height: number;
|
|
32
32
|
}
|
|
33
33
|
export interface OgImageRuntimeConfig {
|
|
34
|
+
browserEnabled: boolean;
|
|
34
35
|
version: string;
|
|
35
36
|
satoriOptions: SatoriOptions;
|
|
36
37
|
resvgOptions: ResvgRenderOptions;
|
|
@@ -77,7 +78,7 @@ export interface OgImageRuntimeConfig {
|
|
|
77
78
|
}
|
|
78
79
|
export type RendererType = 'satori' | 'browser' | 'takumi';
|
|
79
80
|
export type BrowserProvider = 'playwright' | 'chrome-launcher' | 'on-demand' | 'cloudflare';
|
|
80
|
-
export type BrowserConfig =
|
|
81
|
+
export type BrowserConfig = boolean | {
|
|
81
82
|
provider: BrowserProvider;
|
|
82
83
|
/** Required when provider is 'cloudflare' - the wrangler binding name */
|
|
83
84
|
binding?: string;
|
|
@@ -446,6 +446,12 @@ async function setupBuildHandler(config, resolve, getDetectedRenderers, getCompa
|
|
|
446
446
|
metadata: getCompatibilityMeta()
|
|
447
447
|
});
|
|
448
448
|
nuxt.hooks.hook("nitro:init", async (nitro) => {
|
|
449
|
+
nitro.hooks.hook("prerender:config", (prerenderConfig) => {
|
|
450
|
+
if (prerenderConfig.virtual?.["playwright-core"]) {
|
|
451
|
+
const { "playwright-core": _, ...virtual } = prerenderConfig.virtual;
|
|
452
|
+
prerenderConfig.virtual = virtual;
|
|
453
|
+
}
|
|
454
|
+
});
|
|
449
455
|
await applyNitroPresetCompatibility(nitro.options, {
|
|
450
456
|
compatibility: config.compatibility?.runtime,
|
|
451
457
|
resolve,
|
|
@@ -651,6 +657,14 @@ const PROVIDER_DEPENDENCIES = [
|
|
|
651
657
|
}
|
|
652
658
|
}
|
|
653
659
|
];
|
|
660
|
+
function resolveAutoDetectedProvider(input) {
|
|
661
|
+
if (input.hasUserComponents)
|
|
662
|
+
return { preferred: null, fallbackToDefault: false };
|
|
663
|
+
const preferred = input.installedProviders.find((p) => p === "takumi") ?? input.installedProviders[0] ?? null;
|
|
664
|
+
if (preferred)
|
|
665
|
+
return { preferred, fallbackToDefault: false };
|
|
666
|
+
return { preferred: null, fallbackToDefault: true };
|
|
667
|
+
}
|
|
654
668
|
async function getInstalledProviders() {
|
|
655
669
|
const installed = [];
|
|
656
670
|
for (const provider of PROVIDER_DEPENDENCIES) {
|
|
@@ -5657,19 +5671,25 @@ ${familyInfo}`);
|
|
|
5657
5671
|
}
|
|
5658
5672
|
}
|
|
5659
5673
|
}
|
|
5674
|
+
if (config.browser)
|
|
5675
|
+
ogImageComponentCtx.detectedRenderers.add("browser");
|
|
5660
5676
|
if (!nuxt.options._prepare && !hasUserComponents) {
|
|
5661
|
-
const
|
|
5662
|
-
|
|
5677
|
+
const { preferred, fallbackToDefault } = resolveAutoDetectedProvider({
|
|
5678
|
+
hasUserComponents,
|
|
5679
|
+
installedProviders: (await getInstalledProviders()).map((p) => p.provider)
|
|
5680
|
+
});
|
|
5663
5681
|
if (preferred) {
|
|
5664
|
-
ogImageComponentCtx.detectedRenderers.add(preferred
|
|
5665
|
-
logger.debug(`Using ${preferred
|
|
5666
|
-
} else if (
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5682
|
+
ogImageComponentCtx.detectedRenderers.add(preferred);
|
|
5683
|
+
logger.debug(`Using ${preferred} renderer`);
|
|
5684
|
+
} else if (fallbackToDefault) {
|
|
5685
|
+
if (nuxt.options.dev && !nuxt.options._prepare && canPromptInteractively()) {
|
|
5686
|
+
const renderer = await promptForRendererSelection();
|
|
5687
|
+
ogImageComponentCtx.detectedRenderers.add(renderer);
|
|
5688
|
+
logger.debug(`Using ${renderer} renderer`);
|
|
5689
|
+
} else {
|
|
5690
|
+
ogImageComponentCtx.detectedRenderers.add("takumi");
|
|
5691
|
+
logger.warn(`No OG image renderer dependency detected. Defaulting to \`takumi\` (non-interactive environment). Install \`${TAKUMI_CORE_PACKAGE}\`, or add a renderer component (e.g. components/OgImage/Default.satori.vue) to choose explicitly.`);
|
|
5692
|
+
}
|
|
5673
5693
|
}
|
|
5674
5694
|
}
|
|
5675
5695
|
const availableRenderers = /* @__PURE__ */ new Set();
|
|
@@ -6075,6 +6095,7 @@ export const staticFontCacheDir = ${JSON.stringify(getStaticFontCacheDir(nuxt.op
|
|
|
6075
6095
|
// @ts-expect-error runtime type
|
|
6076
6096
|
isNuxtContentDocumentDriven: !!nuxt.options.content?.documentDriven,
|
|
6077
6097
|
cssFramework: cssFramework || "none",
|
|
6098
|
+
browserEnabled: !!config.browser,
|
|
6078
6099
|
// Browser renderer config for cloudflare binding access
|
|
6079
6100
|
browser: typeof config.browser === "object" ? {
|
|
6080
6101
|
provider: config.browser.provider,
|
|
@@ -6109,6 +6130,7 @@ export const staticFontCacheDir = ${JSON.stringify(getStaticFontCacheDir(nuxt.op
|
|
|
6109
6130
|
"nuxt-og-image": {
|
|
6110
6131
|
defaults: runtimeConfig.defaults,
|
|
6111
6132
|
includeTwitter: runtimeConfig.includeTwitter,
|
|
6133
|
+
browserEnabled: runtimeConfig.browserEnabled,
|
|
6112
6134
|
hasServerRuntime: !nuxt.options._generate && !nuxt.options.nitro?.static
|
|
6113
6135
|
}
|
|
6114
6136
|
};
|