nuxt-og-image 6.4.2 → 6.4.4
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.cjs +1 -1
- package/dist/chunks/tw4.mjs +1 -1
- package/dist/chunks/uno.cjs +1 -1
- package/dist/chunks/uno.mjs +1 -1
- package/dist/devtools/200.html +1 -1
- package/dist/devtools/404.html +1 -1
- package/dist/devtools/_nuxt/{BWm573-p.js → B-y6Zfh-.js} +1 -1
- package/dist/devtools/_nuxt/{yBiBpwD7.js → C5LFIfwi.js} +6 -6
- package/dist/devtools/_nuxt/CN79P4uE.js +6 -0
- package/dist/devtools/_nuxt/{BQDcSiMf.js → CThtpBJK.js} +1 -1
- package/dist/devtools/_nuxt/{BsivBvAU.js → CYOZ55V_.js} +1 -1
- package/dist/devtools/_nuxt/{Cy0omYQh.js → ClxM7Lmy.js} +1 -1
- package/dist/devtools/_nuxt/{B6Dg3dZ6.js → CqGOSK9s.js} +1 -1
- package/dist/devtools/_nuxt/{DziLl24l.js → CwlJb64V.js} +1 -1
- package/dist/devtools/_nuxt/{BMVWkjCo.js → CxhRt3FZ.js} +1 -1
- package/dist/devtools/_nuxt/{w_tq7NMl.js → DLMIIqSE.js} +1 -1
- package/dist/devtools/_nuxt/DevtoolsSection.Be9AJQOh.css +1 -0
- package/dist/devtools/_nuxt/DevtoolsSnippet.BubiVHug.css +1 -0
- package/dist/devtools/_nuxt/{CHeKziWa.js → DjEkFT0U.js} +1 -1
- package/dist/devtools/_nuxt/builds/latest.json +1 -1
- package/dist/devtools/_nuxt/builds/meta/ca9f1307-1f7b-4a5f-b061-2c680927caac.json +1 -0
- package/dist/devtools/_nuxt/{entry.BjD2aghs.css → entry.CJ6yFnTt.css} +1 -1
- package/dist/devtools/_nuxt/{pages.DO0dnDUs.css → pages.D8s6dQja.css} +1 -1
- package/dist/devtools/_nuxt/renderer-select.cI9Vfr5y.css +1 -0
- package/dist/devtools/debug/index.html +1 -1
- package/dist/devtools/docs/index.html +1 -1
- package/dist/devtools/index.html +1 -1
- package/dist/devtools/templates/index.html +1 -1
- package/dist/module.cjs +1 -1
- package/dist/module.d.cts +11 -0
- package/dist/module.d.mts +11 -0
- package/dist/module.d.ts +11 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +1 -1
- package/dist/runtime/app/client-utils.js +24 -5
- package/dist/runtime/server/og-image/bindings/font-assets/cloudflare.js +9 -18
- package/dist/runtime/server/og-image/bindings/font-assets/dev-prerender.js +8 -4
- package/dist/runtime/server/og-image/bindings/font-assets/node.js +6 -2
- package/dist/runtime/server/og-image/browser/screenshot.d.ts +1 -1
- package/dist/runtime/server/og-image/browser/screenshot.js +6 -4
- package/dist/runtime/server/og-image/context.js +4 -0
- package/dist/runtime/server/og-image/core/plugins/imageSrc.js +106 -99
- package/dist/runtime/server/og-image/core/transforms/emojis/fetch.js +23 -12
- package/dist/runtime/server/og-image/satori/renderer.js +16 -14
- package/dist/runtime/server/og-image/takumi/renderer.js +27 -19
- package/dist/runtime/server/util/cloudflareAssets.d.ts +24 -0
- package/dist/runtime/server/util/cloudflareAssets.js +16 -0
- package/dist/runtime/server/util/eventHandlers.js +28 -5
- package/dist/runtime/server/util/fetchLocalAsset.d.ts +25 -0
- package/dist/runtime/server/util/fetchLocalAsset.js +34 -0
- package/dist/runtime/server/util/fetchTimeout.d.ts +2 -0
- package/dist/runtime/server/util/fetchTimeout.js +7 -0
- package/dist/runtime/server/util/timings.d.ts +17 -0
- package/dist/runtime/server/util/timings.js +75 -0
- package/dist/runtime/types.d.ts +3 -0
- package/dist/shared/{nuxt-og-image.BK0-aZom.mjs → nuxt-og-image.Cr3WHMk1.mjs} +16 -11
- package/dist/shared/{nuxt-og-image.C2oXAHiT.cjs → nuxt-og-image.TJuh6pW5.cjs} +17 -12
- package/package.json +9 -9
- package/dist/devtools/_nuxt/DDRo8-tD.js +0 -6
- package/dist/devtools/_nuxt/DevtoolsSection.C-PGRg5f.css +0 -1
- package/dist/devtools/_nuxt/DevtoolsSnippet.BipAyEUC.css +0 -1
- package/dist/devtools/_nuxt/builds/meta/150c0674-b387-4525-9043-e05dd343db8e.json +0 -1
- package/dist/devtools/_nuxt/renderer-select.J57nTUNW.css +0 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { getNitroOrigin } from "#site-config/server/composables";
|
|
2
1
|
import { defu } from "defu";
|
|
3
2
|
import { withBase } from "ufo";
|
|
4
3
|
import { logger } from "../../../logger.js";
|
|
4
|
+
import { fetchLocalAsset } from "../../util/fetchLocalAsset.js";
|
|
5
|
+
import { getFetchTimeout } from "../../util/fetchTimeout.js";
|
|
5
6
|
import { buildSubsetFamilyChain, extractCodepoints, getDefaultFontFamily, loadFontsForRenderer, resolveSubsetChain } from "../fonts.js";
|
|
6
7
|
import { getExtractResourceUrls, getTakumi } from "./instances.js";
|
|
7
8
|
import { createTakumiNodes } from "./nodes.js";
|
|
@@ -77,11 +78,11 @@ function rewriteFontFamilies(node, loadedFamilies, subsetChains) {
|
|
|
77
78
|
}
|
|
78
79
|
}
|
|
79
80
|
async function createImage(event, format) {
|
|
80
|
-
const { options } = event;
|
|
81
|
+
const { options, timings } = event;
|
|
81
82
|
const { fontFamilyOverride, defaultFont } = getDefaultFontFamily(options);
|
|
82
83
|
const nodes = await createTakumiNodes(event);
|
|
83
84
|
const codepoints = extractCodepoints(nodes);
|
|
84
|
-
const fonts = await loadFontsForRenderer(event, { supportedFormats: /* @__PURE__ */ new Set(["ttf", "woff2"]), component: options.component, fontFamilyOverride: fontFamilyOverride || defaultFont, codepoints });
|
|
85
|
+
const fonts = await timings.measure("font-load", () => loadFontsForRenderer(event, { supportedFormats: /* @__PURE__ */ new Set(["ttf", "woff2"]), component: options.component, fontFamilyOverride: fontFamilyOverride || defaultFont, codepoints }));
|
|
85
86
|
await event._nitro.hooks.callHook("nuxt-og-image:takumi:nodes", nodes, event);
|
|
86
87
|
const subsetChains = buildSubsetFamilyChain(fonts);
|
|
87
88
|
const state = await getTakumiState(event);
|
|
@@ -99,25 +100,32 @@ async function createImage(event, format) {
|
|
|
99
100
|
rewriteFontFamilies(nodes, state.loadedFamilies, subsetChains);
|
|
100
101
|
const extractResourceUrls = await getExtractResourceUrls();
|
|
101
102
|
const resourceUrls = await extractResourceUrls(nodes);
|
|
102
|
-
const origin = getNitroOrigin(event.e);
|
|
103
103
|
const baseURL = event.runtimeConfig.app.baseURL;
|
|
104
104
|
const fetchedResources = [];
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
105
|
+
if (resourceUrls.length) {
|
|
106
|
+
const fetchTimeout = getFetchTimeout(event.runtimeConfig);
|
|
107
|
+
const headers = { "x-nuxt-og-image": "1" };
|
|
108
|
+
await timings.measure("resource-fetch", () => Promise.all(resourceUrls.map(async (src) => {
|
|
109
|
+
let data;
|
|
110
|
+
if (src.startsWith("/")) {
|
|
111
|
+
const path = withBase(src, baseURL);
|
|
112
|
+
data = await fetchLocalAsset(event.e, path, {
|
|
113
|
+
fetchTimeout,
|
|
114
|
+
headers,
|
|
115
|
+
includeExternalFallback: true
|
|
116
|
+
});
|
|
117
|
+
} else {
|
|
118
|
+
data = await $fetch(src, {
|
|
119
|
+
responseType: "arrayBuffer",
|
|
120
|
+
signal: AbortSignal.timeout(fetchTimeout),
|
|
121
|
+
timeout: fetchTimeout,
|
|
122
|
+
headers
|
|
123
|
+
}).catch(() => void 0);
|
|
111
124
|
}
|
|
112
|
-
|
|
113
|
-
for (const url of urlsToTry) {
|
|
114
|
-
const data = await $fetch(url, { responseType: "arrayBuffer" }).catch(() => null);
|
|
115
|
-
if (data) {
|
|
125
|
+
if (data)
|
|
116
126
|
fetchedResources.push({ src, data: new Uint8Array(data) });
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
}));
|
|
127
|
+
})));
|
|
128
|
+
}
|
|
121
129
|
const maxDpr = event.runtimeConfig.security?.maxDpr || 2;
|
|
122
130
|
const maxDim = event.runtimeConfig.security?.maxDimension || 2048;
|
|
123
131
|
const dpr = Math.min(Math.max(1, options.takumi?.devicePixelRatio ?? 1), maxDpr);
|
|
@@ -128,7 +136,7 @@ async function createImage(event, format) {
|
|
|
128
136
|
fetchedResources,
|
|
129
137
|
devicePixelRatio: dpr
|
|
130
138
|
});
|
|
131
|
-
return await state.renderer.render(nodes, renderOptions);
|
|
139
|
+
return await timings.measure("render-takumi", () => state.renderer.render(nodes, renderOptions));
|
|
132
140
|
}
|
|
133
141
|
const TakumiRenderer = {
|
|
134
142
|
name: "takumi",
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { H3Event } from 'h3';
|
|
2
|
+
interface AssetsBinding {
|
|
3
|
+
fetch: (request: string | Request, init?: RequestInit) => Promise<Response>;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Access the Cloudflare Workers ASSETS binding if available.
|
|
7
|
+
*
|
|
8
|
+
* Requires either:
|
|
9
|
+
* - `nitro.cloudflare.deployConfig: true` (generates wrangler.json with ASSETS)
|
|
10
|
+
* - A wrangler.toml/json with `[assets]` configured
|
|
11
|
+
*/
|
|
12
|
+
export declare function getCloudflareAssets(event: H3Event): AssetsBinding | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Fetch a path directly from the CF Workers ASSETS binding.
|
|
15
|
+
*
|
|
16
|
+
* Prefer this over same-origin subrequests on Workers: it hits the static asset
|
|
17
|
+
* handler without billing a subrequest and without re-running the Worker's
|
|
18
|
+
* middleware chain.
|
|
19
|
+
*
|
|
20
|
+
* Returns `undefined` when no ASSETS binding is available or the asset doesn't
|
|
21
|
+
* exist (letting callers fall through to other resolution strategies).
|
|
22
|
+
*/
|
|
23
|
+
export declare function tryCloudflareAssetsFetch(event: H3Event, path: string, signal?: AbortSignal): Promise<ArrayBuffer | undefined>;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { getRequestHost } from "h3";
|
|
2
|
+
export function getCloudflareAssets(event) {
|
|
3
|
+
const assets = event.context.cloudflare?.env?.ASSETS || event.context.ASSETS;
|
|
4
|
+
return assets && typeof assets.fetch === "function" ? assets : void 0;
|
|
5
|
+
}
|
|
6
|
+
export async function tryCloudflareAssetsFetch(event, path, signal) {
|
|
7
|
+
const assets = getCloudflareAssets(event);
|
|
8
|
+
if (!assets)
|
|
9
|
+
return;
|
|
10
|
+
const origin = event.context.cloudflare?.request?.url || `https://${getRequestHost(event) || "localhost"}`;
|
|
11
|
+
const url = new URL(path, origin).href;
|
|
12
|
+
const res = await assets.fetch(url, signal ? { signal } : void 0).catch(() => null);
|
|
13
|
+
if (!res || !res.ok)
|
|
14
|
+
return;
|
|
15
|
+
return res.arrayBuffer();
|
|
16
|
+
}
|
|
@@ -8,12 +8,25 @@ import { html } from "../og-image/templates/html.js";
|
|
|
8
8
|
import { useOgImageRuntimeConfig } from "../utils.js";
|
|
9
9
|
import { useOgImageBufferCache } from "./cache.js";
|
|
10
10
|
export async function imageEventHandler(e) {
|
|
11
|
+
const reqStart = performance.now();
|
|
11
12
|
const ctx = await resolveContext(e).catch((err) => {
|
|
12
13
|
logger.error(`resolveContext error for ${e.path}:`, err?.message || err);
|
|
13
14
|
throw err;
|
|
14
15
|
});
|
|
15
16
|
if (ctx instanceof H3Error)
|
|
16
17
|
return ctx;
|
|
18
|
+
const timings = ctx.timings;
|
|
19
|
+
try {
|
|
20
|
+
return await renderOgImage(e, ctx);
|
|
21
|
+
} finally {
|
|
22
|
+
timings.record("total", performance.now() - reqStart);
|
|
23
|
+
const header = timings.header();
|
|
24
|
+
if (header)
|
|
25
|
+
setHeader(e, "Server-Timing", header);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async function renderOgImage(e, ctx) {
|
|
29
|
+
const timings = ctx.timings;
|
|
17
30
|
const { isDevToolsContextRequest, extension, renderer } = ctx;
|
|
18
31
|
const { debug, baseCacheKey, security } = useOgImageRuntimeConfig();
|
|
19
32
|
if (!import.meta.prerender && !import.meta.dev && security?.restrictRuntimeImagesToOrigin) {
|
|
@@ -99,21 +112,29 @@ export async function imageEventHandler(e) {
|
|
|
99
112
|
}
|
|
100
113
|
const buildCachedImage = import.meta.prerender ? getBuildCachedImage(ctx.options, extension) : null;
|
|
101
114
|
if (buildCachedImage) {
|
|
115
|
+
timings.record("cache-hit", 0);
|
|
102
116
|
return buildCachedImage;
|
|
103
117
|
}
|
|
118
|
+
const endCacheLookup = timings.start("cache-lookup");
|
|
104
119
|
const cacheApi = await useOgImageBufferCache(ctx, {
|
|
105
120
|
cacheMaxAgeSeconds: ctx.options.cacheMaxAgeSeconds,
|
|
106
121
|
baseCacheKey,
|
|
107
122
|
secret: security?.secret
|
|
108
|
-
});
|
|
109
|
-
if (typeof cacheApi === "undefined")
|
|
123
|
+
}).finally(endCacheLookup);
|
|
124
|
+
if (typeof cacheApi === "undefined") {
|
|
110
125
|
return;
|
|
111
|
-
|
|
126
|
+
}
|
|
127
|
+
if (cacheApi instanceof H3Error) {
|
|
112
128
|
return cacheApi;
|
|
129
|
+
}
|
|
113
130
|
let image = cacheApi.cachedItem;
|
|
131
|
+
if (image) {
|
|
132
|
+
timings.record("cache-hit", 0);
|
|
133
|
+
}
|
|
114
134
|
if (!image) {
|
|
115
|
-
const timeout = security?.renderTimeout
|
|
135
|
+
const timeout = security?.renderTimeout ?? 15e3;
|
|
116
136
|
let timer;
|
|
137
|
+
const endRender = timings.start("render-total");
|
|
117
138
|
image = await Promise.race([
|
|
118
139
|
renderer.createImage(ctx),
|
|
119
140
|
new Promise((_, reject) => {
|
|
@@ -128,9 +149,11 @@ export async function imageEventHandler(e) {
|
|
|
128
149
|
throw err;
|
|
129
150
|
}).finally(() => {
|
|
130
151
|
clearTimeout(timer);
|
|
152
|
+
endRender();
|
|
131
153
|
});
|
|
132
|
-
if (image instanceof H3Error)
|
|
154
|
+
if (image instanceof H3Error) {
|
|
133
155
|
return image;
|
|
156
|
+
}
|
|
134
157
|
if (!image) {
|
|
135
158
|
return createError({
|
|
136
159
|
statusCode: 500,
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { H3Event } from 'h3';
|
|
2
|
+
export interface FetchLocalAssetOptions {
|
|
3
|
+
fetchTimeout: number;
|
|
4
|
+
headers?: Record<string, string>;
|
|
5
|
+
/**
|
|
6
|
+
* Also attempt an external HTTP fetch to `getNitroOrigin(event) + path` when
|
|
7
|
+
* ASSETS and Nitro localFetch both miss. Enable for platforms where static
|
|
8
|
+
* files are served by edge routing that never reaches the Nitro handler
|
|
9
|
+
* (Vercel, Netlify). Disable for CF Workers where no ASSETS means no assets.
|
|
10
|
+
*/
|
|
11
|
+
includeExternalFallback?: boolean;
|
|
12
|
+
onStepFailure?: (url: string, err: unknown) => void;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Resolve a same-origin asset path to bytes, trying (in order):
|
|
16
|
+
* 1. Cloudflare Workers ASSETS binding
|
|
17
|
+
* 2. Nitro localFetch (`event.$fetch`) — resolves dynamic routes too
|
|
18
|
+
* 3. External `$fetch` to the Nitro origin (opt-in)
|
|
19
|
+
*
|
|
20
|
+
* All steps share a single `AbortSignal.timeout(fetchTimeout)` so a broken
|
|
21
|
+
* URL can't burn N× the configured budget.
|
|
22
|
+
*
|
|
23
|
+
* Returns `undefined` when every step fails or times out.
|
|
24
|
+
*/
|
|
25
|
+
export declare function fetchLocalAsset(event: H3Event, path: string, options: FetchLocalAssetOptions): Promise<ArrayBuffer | undefined>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { getNitroOrigin } from "#site-config/server/composables";
|
|
2
|
+
import { $fetch } from "ofetch";
|
|
3
|
+
import { tryCloudflareAssetsFetch } from "./cloudflareAssets.js";
|
|
4
|
+
export async function fetchLocalAsset(event, path, options) {
|
|
5
|
+
const { fetchTimeout, headers, includeExternalFallback = false, onStepFailure } = options;
|
|
6
|
+
const deadline = AbortSignal.timeout(fetchTimeout);
|
|
7
|
+
let result = await tryCloudflareAssetsFetch(event, path, deadline).catch((err) => {
|
|
8
|
+
onStepFailure?.(path, err);
|
|
9
|
+
return void 0;
|
|
10
|
+
});
|
|
11
|
+
if (result || deadline.aborted)
|
|
12
|
+
return result;
|
|
13
|
+
result = await event.$fetch(path, {
|
|
14
|
+
responseType: "arrayBuffer",
|
|
15
|
+
signal: deadline,
|
|
16
|
+
timeout: fetchTimeout,
|
|
17
|
+
headers
|
|
18
|
+
}).catch((err) => {
|
|
19
|
+
onStepFailure?.(path, err);
|
|
20
|
+
return void 0;
|
|
21
|
+
});
|
|
22
|
+
if (result || deadline.aborted || !includeExternalFallback)
|
|
23
|
+
return result;
|
|
24
|
+
const absolute = `${getNitroOrigin(event)}${path}`;
|
|
25
|
+
return await $fetch(absolute, {
|
|
26
|
+
responseType: "arrayBuffer",
|
|
27
|
+
signal: deadline,
|
|
28
|
+
timeout: fetchTimeout,
|
|
29
|
+
headers
|
|
30
|
+
}).catch((err) => {
|
|
31
|
+
onStepFailure?.(absolute, err);
|
|
32
|
+
return void 0;
|
|
33
|
+
});
|
|
34
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface TimingEntry {
|
|
2
|
+
name: string;
|
|
3
|
+
dur: number;
|
|
4
|
+
count?: number;
|
|
5
|
+
}
|
|
6
|
+
export interface Timings {
|
|
7
|
+
start: (name: string) => () => number;
|
|
8
|
+
record: (name: string, ms: number) => void;
|
|
9
|
+
measure: <T>(name: string, fn: () => Promise<T> | T) => Promise<T>;
|
|
10
|
+
entries: () => TimingEntry[];
|
|
11
|
+
header: () => string;
|
|
12
|
+
}
|
|
13
|
+
export declare function createTimings(): Timings;
|
|
14
|
+
export declare const TIMING_CTX_KEY = "_ogImageTimings";
|
|
15
|
+
export declare function getTimingsFromEvent(e: {
|
|
16
|
+
context: Record<string, any>;
|
|
17
|
+
}): Timings | undefined;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
const RE_TOKEN = /[^\w-]/g;
|
|
2
|
+
function sanitizeName(name) {
|
|
3
|
+
return name.replace(RE_TOKEN, "_");
|
|
4
|
+
}
|
|
5
|
+
export function createTimings() {
|
|
6
|
+
const totals = /* @__PURE__ */ new Map();
|
|
7
|
+
const spans = /* @__PURE__ */ new Map();
|
|
8
|
+
const record = (name, ms) => {
|
|
9
|
+
const key = sanitizeName(name);
|
|
10
|
+
const existing = totals.get(key);
|
|
11
|
+
if (existing) {
|
|
12
|
+
existing.dur += ms;
|
|
13
|
+
existing.count += 1;
|
|
14
|
+
} else {
|
|
15
|
+
totals.set(key, { dur: ms, count: 1 });
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
const start = (name) => {
|
|
19
|
+
const key = sanitizeName(name);
|
|
20
|
+
const t0 = performance.now();
|
|
21
|
+
const span = spans.get(key) ?? { open: 0, windowStart: 0, wall: 0 };
|
|
22
|
+
if (span.open === 0)
|
|
23
|
+
span.windowStart = t0;
|
|
24
|
+
span.open += 1;
|
|
25
|
+
spans.set(key, span);
|
|
26
|
+
let ended = false;
|
|
27
|
+
return () => {
|
|
28
|
+
if (ended)
|
|
29
|
+
return 0;
|
|
30
|
+
ended = true;
|
|
31
|
+
const t1 = performance.now();
|
|
32
|
+
const ms = t1 - t0;
|
|
33
|
+
span.open -= 1;
|
|
34
|
+
if (span.open === 0) {
|
|
35
|
+
span.wall += t1 - span.windowStart;
|
|
36
|
+
const existing = totals.get(key);
|
|
37
|
+
if (existing) {
|
|
38
|
+
existing.dur = span.wall;
|
|
39
|
+
existing.count += 1;
|
|
40
|
+
} else {
|
|
41
|
+
totals.set(key, { dur: span.wall, count: 1 });
|
|
42
|
+
}
|
|
43
|
+
} else {
|
|
44
|
+
const existing = totals.get(key);
|
|
45
|
+
if (existing)
|
|
46
|
+
existing.count += 1;
|
|
47
|
+
else
|
|
48
|
+
totals.set(key, { dur: 0, count: 1 });
|
|
49
|
+
}
|
|
50
|
+
return ms;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
const measure = async (name, fn) => {
|
|
54
|
+
const end = start(name);
|
|
55
|
+
try {
|
|
56
|
+
return await fn();
|
|
57
|
+
} finally {
|
|
58
|
+
end();
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
const entries = () => Array.from(totals.entries()).map(([name, v]) => ({
|
|
62
|
+
name,
|
|
63
|
+
dur: Math.round(v.dur * 1e3) / 1e3,
|
|
64
|
+
count: v.count > 1 ? v.count : void 0
|
|
65
|
+
}));
|
|
66
|
+
const header = () => entries().map(({ name, dur, count }) => {
|
|
67
|
+
const desc = count ? `;desc="n=${count}"` : "";
|
|
68
|
+
return `${name}${desc};dur=${dur}`;
|
|
69
|
+
}).join(", ");
|
|
70
|
+
return { start, record, measure, entries, header };
|
|
71
|
+
}
|
|
72
|
+
export const TIMING_CTX_KEY = "_ogImageTimings";
|
|
73
|
+
export function getTimingsFromEvent(e) {
|
|
74
|
+
return e.context[TIMING_CTX_KEY];
|
|
75
|
+
}
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type { NitroRuntimeHooks } from 'nitropack/types';
|
|
|
6
6
|
import type { SatoriOptions } from 'satori';
|
|
7
7
|
import type { JpegOptions, SharpOptions } from 'sharp';
|
|
8
8
|
import type { MaybeRefOrGetter, Ref } from 'vue';
|
|
9
|
+
import type { Timings } from './server/util/timings.js';
|
|
9
10
|
interface NitroApp {
|
|
10
11
|
hooks: Hookable<NitroRuntimeHooks>;
|
|
11
12
|
[key: string]: any;
|
|
@@ -20,6 +21,7 @@ export interface OgImageRenderEventContext {
|
|
|
20
21
|
isDevToolsContextRequest: boolean;
|
|
21
22
|
publicStoragePath: string;
|
|
22
23
|
runtimeConfig: OgImageRuntimeConfig;
|
|
24
|
+
timings: Timings;
|
|
23
25
|
_nitro: NitroApp;
|
|
24
26
|
}
|
|
25
27
|
export type IconifyEmojiIconSets = 'twemoji' | 'noto' | 'fluent-emoji' | 'fluent-emoji-flat' | 'fluent-emoji-high-contrast' | 'noto-v1' | 'emojione' | 'emojione-monotone' | 'emojione-v1' | 'streamline-emojis' | 'openmoji';
|
|
@@ -57,6 +59,7 @@ export interface OgImageRuntimeConfig {
|
|
|
57
59
|
maxDimension: number;
|
|
58
60
|
maxDpr: number;
|
|
59
61
|
renderTimeout: number;
|
|
62
|
+
imageFetchTimeout: number;
|
|
60
63
|
maxQueryParamSize: number | null;
|
|
61
64
|
restrictRuntimeImagesToOrigin: false | string[];
|
|
62
65
|
secret: string;
|
|
@@ -805,6 +805,15 @@ ${content}`, "utf-8");
|
|
|
805
805
|
return insertDefineOgImage(componentName, pageFile);
|
|
806
806
|
}
|
|
807
807
|
}, nuxt).then((rpc) => {
|
|
808
|
+
const safeBroadcast = (fn) => {
|
|
809
|
+
try {
|
|
810
|
+
const result = fn();
|
|
811
|
+
if (result && typeof result.then === "function")
|
|
812
|
+
result.catch(() => {
|
|
813
|
+
});
|
|
814
|
+
} catch {
|
|
815
|
+
}
|
|
816
|
+
};
|
|
808
817
|
let cssRefreshTimer;
|
|
809
818
|
nuxt.hook("builder:watch", (e, watchPath) => {
|
|
810
819
|
if (!e || !watchPath)
|
|
@@ -818,23 +827,18 @@ ${content}`, "utf-8");
|
|
|
818
827
|
if (isCssChange || normalizedPath.includes("uno.config")) {
|
|
819
828
|
clearTimeout(cssRefreshTimer);
|
|
820
829
|
cssRefreshTimer = setTimeout(() => {
|
|
821
|
-
rpc.broadcast.refresh()
|
|
822
|
-
});
|
|
830
|
+
safeBroadcast(() => rpc.broadcast.refresh());
|
|
823
831
|
}, 200);
|
|
824
832
|
return;
|
|
825
833
|
}
|
|
826
834
|
if ((e === "change" || e.includes("link")) && (normalizedPath.startsWith("pages") || normalizedPath.startsWith("content"))) {
|
|
827
|
-
rpc.broadcast.refreshRouteData(normalizedPath)
|
|
828
|
-
});
|
|
835
|
+
safeBroadcast(() => rpc.broadcast.refreshRouteData(normalizedPath));
|
|
829
836
|
}
|
|
830
837
|
if (options.componentDirs.some((dir) => normalizedPath.includes(dir))) {
|
|
831
|
-
if (e === "change")
|
|
832
|
-
rpc.broadcast.refresh()
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
rpc.broadcast.refreshGlobalData().catch(() => {
|
|
836
|
-
});
|
|
837
|
-
}
|
|
838
|
+
if (e === "change")
|
|
839
|
+
safeBroadcast(() => rpc.broadcast.refresh());
|
|
840
|
+
else
|
|
841
|
+
safeBroadcast(() => rpc.broadcast.refreshGlobalData());
|
|
838
842
|
}
|
|
839
843
|
});
|
|
840
844
|
});
|
|
@@ -5222,6 +5226,7 @@ export const rootDir = ${JSON.stringify(nuxt.options.rootDir)}`;
|
|
|
5222
5226
|
maxDimension: config.security?.maxDimension ?? 2048,
|
|
5223
5227
|
maxDpr: config.security?.maxDpr ?? 2,
|
|
5224
5228
|
renderTimeout: config.security?.renderTimeout ?? 15e3,
|
|
5229
|
+
imageFetchTimeout: config.security?.imageFetchTimeout ?? 3e3,
|
|
5225
5230
|
maxQueryParamSize: config.security?.maxQueryParamSize ?? (config.security?.strict ? 2048 : null),
|
|
5226
5231
|
restrictRuntimeImagesToOrigin: config.security?.restrictRuntimeImagesToOrigin === true || config.security?.strict && config.security?.restrictRuntimeImagesToOrigin == null ? [] : config.security?.restrictRuntimeImagesToOrigin || false,
|
|
5227
5232
|
secret: config.security?.secret || process.env.NUXT_OG_IMAGE_SECRET || ""
|
|
@@ -825,6 +825,15 @@ ${content}`, "utf-8");
|
|
|
825
825
|
return insertDefineOgImage(componentName, pageFile);
|
|
826
826
|
}
|
|
827
827
|
}, nuxt).then((rpc) => {
|
|
828
|
+
const safeBroadcast = (fn) => {
|
|
829
|
+
try {
|
|
830
|
+
const result = fn();
|
|
831
|
+
if (result && typeof result.then === "function")
|
|
832
|
+
result.catch(() => {
|
|
833
|
+
});
|
|
834
|
+
} catch {
|
|
835
|
+
}
|
|
836
|
+
};
|
|
828
837
|
let cssRefreshTimer;
|
|
829
838
|
nuxt.hook("builder:watch", (e, watchPath) => {
|
|
830
839
|
if (!e || !watchPath)
|
|
@@ -838,23 +847,18 @@ ${content}`, "utf-8");
|
|
|
838
847
|
if (isCssChange || normalizedPath.includes("uno.config")) {
|
|
839
848
|
clearTimeout(cssRefreshTimer);
|
|
840
849
|
cssRefreshTimer = setTimeout(() => {
|
|
841
|
-
rpc.broadcast.refresh()
|
|
842
|
-
});
|
|
850
|
+
safeBroadcast(() => rpc.broadcast.refresh());
|
|
843
851
|
}, 200);
|
|
844
852
|
return;
|
|
845
853
|
}
|
|
846
854
|
if ((e === "change" || e.includes("link")) && (normalizedPath.startsWith("pages") || normalizedPath.startsWith("content"))) {
|
|
847
|
-
rpc.broadcast.refreshRouteData(normalizedPath)
|
|
848
|
-
});
|
|
855
|
+
safeBroadcast(() => rpc.broadcast.refreshRouteData(normalizedPath));
|
|
849
856
|
}
|
|
850
857
|
if (options.componentDirs.some((dir) => normalizedPath.includes(dir))) {
|
|
851
|
-
if (e === "change")
|
|
852
|
-
rpc.broadcast.refresh()
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
rpc.broadcast.refreshGlobalData().catch(() => {
|
|
856
|
-
});
|
|
857
|
-
}
|
|
858
|
+
if (e === "change")
|
|
859
|
+
safeBroadcast(() => rpc.broadcast.refresh());
|
|
860
|
+
else
|
|
861
|
+
safeBroadcast(() => rpc.broadcast.refreshGlobalData());
|
|
858
862
|
}
|
|
859
863
|
});
|
|
860
864
|
});
|
|
@@ -4316,7 +4320,7 @@ const module$1 = kit.defineNuxtModule({
|
|
|
4316
4320
|
await onUpgrade(nuxt, options, previousVersion);
|
|
4317
4321
|
},
|
|
4318
4322
|
async setup(config, nuxt) {
|
|
4319
|
-
const _resolver = kit.createResolver((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/nuxt-og-image.
|
|
4323
|
+
const _resolver = kit.createResolver((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/nuxt-og-image.TJuh6pW5.cjs', document.baseURI).href)));
|
|
4320
4324
|
const fixSharedPath = (p) => {
|
|
4321
4325
|
if (p.includes("/shared/runtime/"))
|
|
4322
4326
|
return p.replace("/shared/runtime/", "/runtime/");
|
|
@@ -5242,6 +5246,7 @@ export const rootDir = ${JSON.stringify(nuxt.options.rootDir)}`;
|
|
|
5242
5246
|
maxDimension: config.security?.maxDimension ?? 2048,
|
|
5243
5247
|
maxDpr: config.security?.maxDpr ?? 2,
|
|
5244
5248
|
renderTimeout: config.security?.renderTimeout ?? 15e3,
|
|
5249
|
+
imageFetchTimeout: config.security?.imageFetchTimeout ?? 3e3,
|
|
5245
5250
|
maxQueryParamSize: config.security?.maxQueryParamSize ?? (config.security?.strict ? 2048 : null),
|
|
5246
5251
|
restrictRuntimeImagesToOrigin: config.security?.restrictRuntimeImagesToOrigin === true || config.security?.strict && config.security?.restrictRuntimeImagesToOrigin == null ? [] : config.security?.restrictRuntimeImagesToOrigin || false,
|
|
5247
5252
|
secret: config.security?.secret || process.env.NUXT_OG_IMAGE_SECRET || ""
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-og-image",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "6.4.
|
|
4
|
+
"version": "6.4.4",
|
|
5
5
|
"description": "Enlightened OG Image generation for Nuxt.",
|
|
6
6
|
"author": {
|
|
7
7
|
"website": "https://harlanzw.com",
|
|
@@ -112,12 +112,12 @@
|
|
|
112
112
|
"nypm": "^0.6.5",
|
|
113
113
|
"ofetch": "^1.5.1",
|
|
114
114
|
"ohash": "^2.0.11",
|
|
115
|
-
"oxc-parser": "^0.
|
|
115
|
+
"oxc-parser": "^0.126.0",
|
|
116
116
|
"oxc-walker": "^0.7.0",
|
|
117
117
|
"pathe": "^2.0.3",
|
|
118
118
|
"pkg-types": "^2.3.0",
|
|
119
119
|
"radix3": "^1.1.2",
|
|
120
|
-
"std-env": "^4.
|
|
120
|
+
"std-env": "^4.1.0",
|
|
121
121
|
"strip-literal": "^3.1.0",
|
|
122
122
|
"tinyexec": "^1.1.1",
|
|
123
123
|
"tinyglobby": "^0.2.16",
|
|
@@ -152,14 +152,14 @@
|
|
|
152
152
|
"@shikijs/langs": "^4.0.2",
|
|
153
153
|
"@shikijs/themes": "^4.0.2",
|
|
154
154
|
"@tailwindcss/vite": "^4.2.2",
|
|
155
|
-
"@takumi-rs/core": "^1.0.
|
|
156
|
-
"@takumi-rs/wasm": "^1.0.
|
|
155
|
+
"@takumi-rs/core": "^1.0.14",
|
|
156
|
+
"@takumi-rs/wasm": "^1.0.14",
|
|
157
157
|
"@unocss/nuxt": "^66.6.8",
|
|
158
158
|
"@vitejs/plugin-vue": "^6.0.6",
|
|
159
159
|
"@vueuse/nuxt": "^14.2.1",
|
|
160
160
|
"birpc": "^4.0.0",
|
|
161
161
|
"bumpp": "^11.0.1",
|
|
162
|
-
"eslint": "^10.2.
|
|
162
|
+
"eslint": "^10.2.1",
|
|
163
163
|
"eslint-plugin-harlanzw": "^0.12.1",
|
|
164
164
|
"fontless": "^0.2.1",
|
|
165
165
|
"get-image-colors": "^4.0.1",
|
|
@@ -176,12 +176,12 @@
|
|
|
176
176
|
"sharp": "^0.34.5",
|
|
177
177
|
"sirv": "^3.0.2",
|
|
178
178
|
"tailwindcss": "^4.2.2",
|
|
179
|
-
"typescript": "^6.0.
|
|
179
|
+
"typescript": "^6.0.3",
|
|
180
180
|
"unifont": "^0.7.4",
|
|
181
181
|
"unocss": "^66.6.8",
|
|
182
182
|
"vitest": "^4.1.4",
|
|
183
|
-
"vue-tsc": "^3.2.
|
|
184
|
-
"wrangler": "^4.
|
|
183
|
+
"vue-tsc": "^3.2.7",
|
|
184
|
+
"wrangler": "^4.83.0",
|
|
185
185
|
"yoga-wasm-web": "^0.3.3"
|
|
186
186
|
},
|
|
187
187
|
"scripts": {
|