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.
Files changed (63) hide show
  1. package/dist/chunks/tw4.cjs +1 -1
  2. package/dist/chunks/tw4.mjs +1 -1
  3. package/dist/chunks/uno.cjs +1 -1
  4. package/dist/chunks/uno.mjs +1 -1
  5. package/dist/devtools/200.html +1 -1
  6. package/dist/devtools/404.html +1 -1
  7. package/dist/devtools/_nuxt/{BWm573-p.js → B-y6Zfh-.js} +1 -1
  8. package/dist/devtools/_nuxt/{yBiBpwD7.js → C5LFIfwi.js} +6 -6
  9. package/dist/devtools/_nuxt/CN79P4uE.js +6 -0
  10. package/dist/devtools/_nuxt/{BQDcSiMf.js → CThtpBJK.js} +1 -1
  11. package/dist/devtools/_nuxt/{BsivBvAU.js → CYOZ55V_.js} +1 -1
  12. package/dist/devtools/_nuxt/{Cy0omYQh.js → ClxM7Lmy.js} +1 -1
  13. package/dist/devtools/_nuxt/{B6Dg3dZ6.js → CqGOSK9s.js} +1 -1
  14. package/dist/devtools/_nuxt/{DziLl24l.js → CwlJb64V.js} +1 -1
  15. package/dist/devtools/_nuxt/{BMVWkjCo.js → CxhRt3FZ.js} +1 -1
  16. package/dist/devtools/_nuxt/{w_tq7NMl.js → DLMIIqSE.js} +1 -1
  17. package/dist/devtools/_nuxt/DevtoolsSection.Be9AJQOh.css +1 -0
  18. package/dist/devtools/_nuxt/DevtoolsSnippet.BubiVHug.css +1 -0
  19. package/dist/devtools/_nuxt/{CHeKziWa.js → DjEkFT0U.js} +1 -1
  20. package/dist/devtools/_nuxt/builds/latest.json +1 -1
  21. package/dist/devtools/_nuxt/builds/meta/ca9f1307-1f7b-4a5f-b061-2c680927caac.json +1 -0
  22. package/dist/devtools/_nuxt/{entry.BjD2aghs.css → entry.CJ6yFnTt.css} +1 -1
  23. package/dist/devtools/_nuxt/{pages.DO0dnDUs.css → pages.D8s6dQja.css} +1 -1
  24. package/dist/devtools/_nuxt/renderer-select.cI9Vfr5y.css +1 -0
  25. package/dist/devtools/debug/index.html +1 -1
  26. package/dist/devtools/docs/index.html +1 -1
  27. package/dist/devtools/index.html +1 -1
  28. package/dist/devtools/templates/index.html +1 -1
  29. package/dist/module.cjs +1 -1
  30. package/dist/module.d.cts +11 -0
  31. package/dist/module.d.mts +11 -0
  32. package/dist/module.d.ts +11 -0
  33. package/dist/module.json +1 -1
  34. package/dist/module.mjs +1 -1
  35. package/dist/runtime/app/client-utils.js +24 -5
  36. package/dist/runtime/server/og-image/bindings/font-assets/cloudflare.js +9 -18
  37. package/dist/runtime/server/og-image/bindings/font-assets/dev-prerender.js +8 -4
  38. package/dist/runtime/server/og-image/bindings/font-assets/node.js +6 -2
  39. package/dist/runtime/server/og-image/browser/screenshot.d.ts +1 -1
  40. package/dist/runtime/server/og-image/browser/screenshot.js +6 -4
  41. package/dist/runtime/server/og-image/context.js +4 -0
  42. package/dist/runtime/server/og-image/core/plugins/imageSrc.js +106 -99
  43. package/dist/runtime/server/og-image/core/transforms/emojis/fetch.js +23 -12
  44. package/dist/runtime/server/og-image/satori/renderer.js +16 -14
  45. package/dist/runtime/server/og-image/takumi/renderer.js +27 -19
  46. package/dist/runtime/server/util/cloudflareAssets.d.ts +24 -0
  47. package/dist/runtime/server/util/cloudflareAssets.js +16 -0
  48. package/dist/runtime/server/util/eventHandlers.js +28 -5
  49. package/dist/runtime/server/util/fetchLocalAsset.d.ts +25 -0
  50. package/dist/runtime/server/util/fetchLocalAsset.js +34 -0
  51. package/dist/runtime/server/util/fetchTimeout.d.ts +2 -0
  52. package/dist/runtime/server/util/fetchTimeout.js +7 -0
  53. package/dist/runtime/server/util/timings.d.ts +17 -0
  54. package/dist/runtime/server/util/timings.js +75 -0
  55. package/dist/runtime/types.d.ts +3 -0
  56. package/dist/shared/{nuxt-og-image.BK0-aZom.mjs → nuxt-og-image.Cr3WHMk1.mjs} +16 -11
  57. package/dist/shared/{nuxt-og-image.C2oXAHiT.cjs → nuxt-og-image.TJuh6pW5.cjs} +17 -12
  58. package/package.json +9 -9
  59. package/dist/devtools/_nuxt/DDRo8-tD.js +0 -6
  60. package/dist/devtools/_nuxt/DevtoolsSection.C-PGRg5f.css +0 -1
  61. package/dist/devtools/_nuxt/DevtoolsSnippet.BipAyEUC.css +0 -1
  62. package/dist/devtools/_nuxt/builds/meta/150c0674-b387-4525-9043-e05dd343db8e.json +0 -1
  63. 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
- await Promise.all(resourceUrls.map(async (src) => {
106
- const urlsToTry = [src];
107
- if (src.startsWith("/")) {
108
- urlsToTry.push(withBase(src, origin));
109
- if (baseURL && baseURL !== "/" && !src.startsWith(baseURL)) {
110
- urlsToTry.push(withBase(withBase(src, baseURL), origin));
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
- break;
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
- if (cacheApi instanceof H3Error)
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 || 15e3;
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,2 @@
1
+ import type { OgImageRuntimeConfig } from '../../types.js';
2
+ export declare function getFetchTimeout(runtimeConfig: OgImageRuntimeConfig): number;
@@ -0,0 +1,7 @@
1
+ const MIN_TIMEOUT_MS = 100;
2
+ export function getFetchTimeout(runtimeConfig) {
3
+ const value = runtimeConfig.security?.imageFetchTimeout ?? 3e3;
4
+ if (!Number.isFinite(value) || value < MIN_TIMEOUT_MS)
5
+ return MIN_TIMEOUT_MS;
6
+ return value;
7
+ }
@@ -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
+ }
@@ -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().catch(() => {
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).catch(() => {
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().catch(() => {
833
- });
834
- } else {
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().catch(() => {
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).catch(() => {
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().catch(() => {
853
- });
854
- } else {
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.C2oXAHiT.cjs', document.baseURI).href)));
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.2",
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.125.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.0.0",
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.9",
156
- "@takumi-rs/wasm": "^1.0.9",
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.0",
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.2",
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.6",
184
- "wrangler": "^4.82.2",
183
+ "vue-tsc": "^3.2.7",
184
+ "wrangler": "^4.83.0",
185
185
  "yoga-wasm-web": "^0.3.3"
186
186
  },
187
187
  "scripts": {