nuxt-og-image 6.5.3 → 6.7.0

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 (108) hide show
  1. package/README.md +2 -0
  2. package/dist/chunks/tw4.cjs +7 -7
  3. package/dist/chunks/tw4.mjs +7 -7
  4. package/dist/chunks/uno.cjs +6 -6
  5. package/dist/chunks/uno.mjs +6 -6
  6. package/dist/cli.cjs +12 -5
  7. package/dist/cli.mjs +10 -3
  8. package/dist/devtools/components/og-image/AddComponentDialog.vue +85 -0
  9. package/dist/devtools/components/og-image/BlueskyCardRenderer.vue +134 -0
  10. package/dist/devtools/components/og-image/CreateOgImageDialog.vue +56 -0
  11. package/dist/devtools/components/og-image/DiscordCardRenderer.vue +125 -0
  12. package/dist/devtools/components/og-image/FacebookCardRenderer.vue +128 -0
  13. package/dist/devtools/components/og-image/IFrameLoader.vue +93 -0
  14. package/dist/devtools/components/og-image/ImageLoader.vue +197 -0
  15. package/dist/devtools/components/og-image/LinkedInCardRenderer.vue +100 -0
  16. package/dist/devtools/components/og-image/RendererSelectModal.vue +356 -0
  17. package/dist/devtools/components/og-image/SlackCardRenderer.vue +140 -0
  18. package/dist/devtools/components/og-image/TemplateComponentPreview.vue +186 -0
  19. package/dist/devtools/components/og-image/TwitterCardRenderer.vue +170 -0
  20. package/dist/devtools/components/og-image/WhatsAppRenderer.vue +294 -0
  21. package/dist/devtools/lib/og-image/keys.ts +8 -0
  22. package/dist/devtools/lib/og-image/og-image.ts +536 -0
  23. package/dist/devtools/lib/og-image/renderer-select.ts +3 -0
  24. package/dist/devtools/lib/og-image/rpc-types.ts +2 -0
  25. package/dist/devtools/lib/og-image/rpc.ts +73 -0
  26. package/dist/devtools/lib/og-image/runtime-types.ts +10 -0
  27. package/dist/devtools/lib/og-image/shared/urlEncoding.ts +502 -0
  28. package/dist/devtools/lib/og-image/shared.ts +30 -0
  29. package/dist/devtools/lib/og-image/templates.ts +9 -0
  30. package/dist/devtools/lib/og-image/types.ts +38 -0
  31. package/dist/devtools/lib/og-image/util/logic.ts +21 -0
  32. package/dist/devtools/nuxt.config.ts +6 -0
  33. package/dist/devtools/pages/og-image/debug.vue +32 -0
  34. package/dist/devtools/pages/og-image/docs.vue +3 -0
  35. package/dist/devtools/pages/og-image/index.vue +1682 -0
  36. package/dist/devtools/pages/og-image/templates.vue +150 -0
  37. package/dist/devtools/pages/og-image.vue +184 -0
  38. package/dist/module.cjs +5 -5
  39. package/dist/module.d.cts +5 -3
  40. package/dist/module.d.mts +5 -3
  41. package/dist/module.d.ts +5 -3
  42. package/dist/module.json +1 -1
  43. package/dist/module.mjs +5 -5
  44. package/dist/runtime/server/og-image/bindings/font-assets/cloudflare.d.ts +1 -1
  45. package/dist/runtime/server/og-image/bindings/font-assets/cloudflare.js +6 -1
  46. package/dist/runtime/server/og-image/bindings/font-assets/dev-prerender.js +13 -6
  47. package/dist/runtime/server/og-image/bindings/font-assets/external-url.d.ts +36 -0
  48. package/dist/runtime/server/og-image/bindings/font-assets/external-url.js +47 -0
  49. package/dist/runtime/server/og-image/bindings/font-assets/node.d.ts +1 -1
  50. package/dist/runtime/server/og-image/bindings/font-assets/node.js +14 -7
  51. package/dist/runtime/server/og-image/bindings/takumi/node-dev.js +7 -1
  52. package/dist/runtime/server/og-image/core/plugins/imageSrc.js +2 -2
  53. package/dist/runtime/server/og-image/fonts.d.ts +4 -0
  54. package/dist/runtime/server/og-image/fonts.js +21 -5
  55. package/dist/runtime/server/og-image/satori/instances.d.ts +1 -2
  56. package/dist/runtime/server/og-image/takumi/renderer.js +61 -19
  57. package/dist/runtime/server/util/ssrf.d.ts +10 -0
  58. package/dist/runtime/server/util/ssrf.js +9 -1
  59. package/dist/runtime/types.d.ts +13 -0
  60. package/dist/shared/{nuxt-og-image.DdbTs-xp.mjs → nuxt-og-image.CPSxZhVO.mjs} +122 -496
  61. package/dist/shared/nuxt-og-image.DM30kcDz.mjs +660 -0
  62. package/dist/shared/nuxt-og-image.RI9DgjgV.cjs +689 -0
  63. package/dist/shared/{nuxt-og-image.CfTPCtaS.cjs → nuxt-og-image.yDJBWXBt.cjs} +181 -555
  64. package/package.json +48 -45
  65. package/dist/devtools/200.html +0 -1
  66. package/dist/devtools/404.html +0 -1
  67. package/dist/devtools/_fonts/4ppnHhMi-pBsWSPo7mY0avYxlDoAg1N3PTzCwXLZ5rA-d9oibkGnTd1JL3tc_xnaVgBLYmOB8kjrK2cvZaqwj9s.woff2 +0 -0
  68. package/dist/devtools/_fonts/PV2hrQG6wq5BlIPDjdL1IcOflycaghyt5MHzlBqZtlo-lb_WexLz3VZqfTN0oi554iBH5tT2j2UFEV-XErCAS3E.woff2 +0 -0
  69. package/dist/devtools/_fonts/VE4cDVCv5MxbFM7ZLoLCGbIpNd71zhp7MDI9lmN5Y7I-xZyDYCUVrd6LV8eVGF3Um3UZjBFuUtDGtvdyTBBRYBo.woff2 +0 -0
  70. package/dist/devtools/_fonts/fVoGbnMbBFd5L9BBp9fUPavUSkZ_EmsQNSyadkT-108-U4T0khaeLQSIhtt9eVvaCEKJjtWJ4ioRJOf8hvqkWY0.woff2 +0 -0
  71. package/dist/devtools/_fonts/lQAxeCEs1R0Lw-H9XRU1RlOARQN8J6npRsPjyEDMe5s-_DUSLEkO3tKTuun_gSnDLoQPVEnpOnyqZMOw0ByZ6PA.woff2 +0 -0
  72. package/dist/devtools/_fonts/lntlqNHKLV2n82yTwMde70QqOjcfLE2XJ5oKZ3vRPWc-z6TxpIZQdWXztWLr9_OFWqt_WJJoeGtuK_-XQMZGQwE.woff2 +0 -0
  73. package/dist/devtools/_nuxt/BZ_CG9ll.js +0 -30
  74. package/dist/devtools/_nuxt/BdMalyEU.js +0 -4
  75. package/dist/devtools/_nuxt/BhkGaWEe.js +0 -3
  76. package/dist/devtools/_nuxt/Bm4Q-nTW.js +0 -3
  77. package/dist/devtools/_nuxt/BsRKQH5X.js +0 -1
  78. package/dist/devtools/_nuxt/C5797Ieg.js +0 -1
  79. package/dist/devtools/_nuxt/CBcJg2GG.js +0 -2
  80. package/dist/devtools/_nuxt/CDaXKRRu.js +0 -1
  81. package/dist/devtools/_nuxt/CM-t-6ZT.js +0 -1
  82. package/dist/devtools/_nuxt/CP0tQR2M.js +0 -1
  83. package/dist/devtools/_nuxt/CXAM78_A.js +0 -1
  84. package/dist/devtools/_nuxt/CYDI7SLa.js +0 -1
  85. package/dist/devtools/_nuxt/CkbDfutK.js +0 -152
  86. package/dist/devtools/_nuxt/Co8Tk_C2.js +0 -1
  87. package/dist/devtools/_nuxt/CqjbkMN9.js +0 -3
  88. package/dist/devtools/_nuxt/D49I50CU.js +0 -1
  89. package/dist/devtools/_nuxt/DevtoolsSection.DkT2bJJh.css +0 -1
  90. package/dist/devtools/_nuxt/DevtoolsSnippet.zV_SDojn.css +0 -1
  91. package/dist/devtools/_nuxt/HWyD49jH.js +0 -1
  92. package/dist/devtools/_nuxt/IFrameLoader.k_861Nnq.css +0 -1
  93. package/dist/devtools/_nuxt/PONEy9N-.js +0 -1
  94. package/dist/devtools/_nuxt/builds/latest.json +0 -1
  95. package/dist/devtools/_nuxt/builds/meta/0f3dbc15-8308-4956-81b9-5507cac7018c.json +0 -1
  96. package/dist/devtools/_nuxt/entry.D7u2asd2.css +0 -2
  97. package/dist/devtools/_nuxt/fira-code.Bc8wnsZt.woff2 +0 -0
  98. package/dist/devtools/_nuxt/hubot-sans.DLGyhQVu.woff2 +0 -0
  99. package/dist/devtools/_nuxt/pO9PmtPW.js +0 -6
  100. package/dist/devtools/_nuxt/pages.0Jk10xlb.css +0 -1
  101. package/dist/devtools/_nuxt/renderer-select.CN5IWe60.css +0 -1
  102. package/dist/devtools/_nuxt/templates.BByln3BG.css +0 -1
  103. package/dist/devtools/debug/index.html +0 -1
  104. package/dist/devtools/docs/index.html +0 -1
  105. package/dist/devtools/index.html +0 -1
  106. package/dist/devtools/templates/index.html +0 -1
  107. package/dist/shared/nuxt-og-image.CJa2KCie.mjs +0 -189
  108. package/dist/shared/nuxt-og-image.CMYbz66P.cjs +0 -193
@@ -1,24 +1,22 @@
1
1
  'use strict';
2
2
 
3
+ const node_crypto = require('node:crypto');
3
4
  const fs = require('node:fs');
4
5
  const promises = require('node:fs/promises');
5
6
  const node_url = require('node:url');
6
7
  const kit = require('@nuxt/kit');
7
8
  const defu = require('defu');
8
- const kit$2 = require('nuxt-site-config/kit');
9
+ const kit$1 = require('nuxt-site-config/kit');
9
10
  const ohash = require('ohash');
10
11
  const pathe = require('pathe');
11
12
  const pkgTypes = require('pkg-types');
12
13
  const stdEnv = require('std-env');
13
- const node_crypto = require('node:crypto');
14
14
  const oxcWalker = require('oxc-walker');
15
- const kit$1 = require('nuxtseo-shared/kit');
16
- const logger_js = require('../../dist/runtime/logger.js');
17
- const chromeLauncher = require('chrome-launcher');
18
- const nypm = require('nypm');
15
+ const dependencies = require('./nuxt-og-image.RI9DgjgV.cjs');
19
16
  const devtools = require('nuxtseo-shared/devtools');
20
17
  const unstorage = require('unstorage');
21
18
  const fsDriver = require('unstorage/drivers/fs-lite');
19
+ const logger_js = require('../../dist/runtime/logger.js');
22
20
  const css_js = require('../../dist/runtime/server/og-image/utils/css.js');
23
21
  const MagicString = require('magic-string');
24
22
  const stripLiteral = require('strip-literal');
@@ -29,7 +27,7 @@ const ofetch = require('ofetch');
29
27
  const ultrahtml = require('ultrahtml');
30
28
  const emojiUtils_js = require('../../dist/runtime/server/og-image/core/transforms/emojis/emoji-utils.js');
31
29
  const exsolve = require('exsolve');
32
- const fonts = require('./nuxt-og-image.CMYbz66P.cjs');
30
+ const kit$2 = require('nuxtseo-shared/kit');
33
31
 
34
32
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
35
33
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
@@ -50,279 +48,9 @@ const fs__namespace = /*#__PURE__*/_interopNamespaceCompat(fs);
50
48
  const fsDriver__default = /*#__PURE__*/_interopDefaultCompat(fsDriver);
51
49
  const MagicString__default = /*#__PURE__*/_interopDefaultCompat(MagicString);
52
50
 
53
- const isUndefinedOrTruthy = (v) => typeof v === "undefined" || v !== false;
54
- function checkLocalChrome() {
55
- if (stdEnv.isCI)
56
- return false;
57
- let hasChromeLocally = false;
58
- try {
59
- hasChromeLocally = !!chromeLauncher.Launcher.getFirstInstallation();
60
- } catch {
61
- }
62
- return hasChromeLocally;
63
- }
64
- async function hasResolvableDependency(dep) {
65
- return await kit.resolvePath(dep, { fallbackToOriginal: true }).catch(() => null).then((r) => r && r !== dep);
66
- }
67
- const VALID_RENDERER_SUFFIXES = ["satori", "browser", "takumi"];
68
- const RE_RENDERER_SUFFIX_CI = /\.?(satori|browser|takumi)$/i;
69
- const RE_RENDERER_SUFFIX = /(Satori|Browser|Takumi)$/;
70
- const RE_LEGACY_SUFFIX = /-legacy$/;
71
- const RE_WHITESPACE = /\s+/;
72
- function getRendererFromFilename(filepath) {
73
- const filename = pathe.basename(filepath).replace(".vue", "");
74
- for (const suffix of VALID_RENDERER_SUFFIXES) {
75
- if (filename.endsWith(`.${suffix}`))
76
- return suffix;
77
- }
78
- return null;
79
- }
80
- const OGIMAGE_PREFIXES = [
81
- { prefix: "OgImageCommunity", overlapWord: "Community" },
82
- { prefix: "OgImageTemplate", overlapWord: "Template" },
83
- { prefix: "OgImage", overlapWord: "Image" }
84
- ];
85
- function getRegisteredBaseNames(registeredPascalName) {
86
- const stripped = registeredPascalName.replace(RE_RENDERER_SUFFIX_CI, "").replace(RE_RENDERER_SUFFIX, "");
87
- const names = [];
88
- for (const { prefix, overlapWord } of OGIMAGE_PREFIXES) {
89
- if (!stripped.startsWith(prefix))
90
- continue;
91
- const withoutPrefix = stripped.slice(prefix.length);
92
- if (withoutPrefix) {
93
- names.push(withoutPrefix);
94
- if (withoutPrefix !== overlapWord)
95
- names.push(overlapWord + withoutPrefix);
96
- } else {
97
- names.push(overlapWord);
98
- }
99
- break;
100
- }
101
- if (names.length === 0)
102
- names.push(stripped);
103
- return names;
104
- }
105
-
106
- const NodeRuntime = {
107
- // node-server runtime
108
- browser: "on-demand",
109
- // this gets changed build start
110
- resvg: "node",
111
- satori: "node",
112
- takumi: "node",
113
- sharp: "node",
114
- // will be disabled if they're missing the dependency
115
- emoji: "local"
116
- // can bundle icons, no size constraints
117
- };
118
- const NodeDevRuntime = {
119
- ...NodeRuntime,
120
- resvg: "node-dev",
121
- // use worker to prevent crashes from killing process
122
- takumi: "node-dev"
123
- // use worker to prevent crashes from killing process
124
- };
125
- const NodePrerenderRuntime = {
126
- ...NodeRuntime,
127
- resvg: "node-dev",
128
- // use worker to prevent crashes from killing prerender process
129
- takumi: "node-dev"
130
- // use worker to prevent crashes from killing prerender process
131
- };
132
- const cloudflare = {
133
- browser: "cloudflare",
134
- resvg: "wasm",
135
- satori: "wasm",
136
- takumi: "wasm",
137
- sharp: false,
138
- emoji: "fetch",
139
- // edge size limits - use API instead of bundling 24MB icons
140
- wasm: {
141
- esmImport: true
142
- // Do NOT set lazy here — Nitro's cloudflare preset uses lazy: false because
143
- // CF Workers require WASM modules to be imported eagerly via top-level await
144
- }
145
- };
146
- const awsLambda = {
147
- browser: false,
148
- resvg: "node",
149
- satori: "node",
150
- takumi: "node",
151
- sharp: false,
152
- // 0.33.x has issues
153
- emoji: "local"
154
- // serverless has larger size limits
155
- };
156
- const WebContainer = {
157
- browser: false,
158
- resvg: "wasm-fs",
159
- satori: "wasm-fs",
160
- takumi: "wasm",
161
- sharp: false,
162
- emoji: "fetch"
163
- // webcontainer has size constraints
164
- };
165
- const RuntimeCompatibility = {
166
- "nitro-dev": NodeDevRuntime,
167
- "nitro-prerender": NodePrerenderRuntime,
168
- "node-server": NodeRuntime,
169
- "node-cluster": NodeRuntime,
170
- "node-listener": NodeRuntime,
171
- "bun": NodeRuntime,
172
- "deno-server": NodeRuntime,
173
- "deno-deploy": NodeRuntime,
174
- // node-server extending presets
175
- "alwaysdata": NodeRuntime,
176
- "cleavr": NodeRuntime,
177
- "digital-ocean": NodeRuntime,
178
- "edgio": NodeRuntime,
179
- "flight-control": NodeRuntime,
180
- "heroku": NodeRuntime,
181
- "iis-handler": NodeRuntime,
182
- "iis-node": NodeRuntime,
183
- "koyeb": NodeRuntime,
184
- "platform-sh": NodeRuntime,
185
- "render-com": NodeRuntime,
186
- "zeabur": NodeRuntime,
187
- "zerops": NodeRuntime,
188
- "stackblitz": WebContainer,
189
- "codesandbox": WebContainer,
190
- "aws-amplify": awsLambda,
191
- "aws-lambda": awsLambda,
192
- "azure": awsLambda,
193
- "azure-functions": awsLambda,
194
- "azure-swa": awsLambda,
195
- "netlify": awsLambda,
196
- "netlify-edge": {
197
- browser: false,
198
- resvg: "wasm",
199
- satori: "node",
200
- takumi: "wasm",
201
- sharp: false,
202
- emoji: "fetch",
203
- // edge size limits
204
- wasm: {
205
- rollup: {
206
- targetEnv: "auto-inline",
207
- sync: ["@resvg/resvg-wasm/index_bg.wasm"]
208
- }
209
- }
210
- },
211
- "firebase": awsLambda,
212
- "firebase-app-hosting": awsLambda,
213
- "genezio": awsLambda,
214
- "stormkit": awsLambda,
215
- "vercel": awsLambda,
216
- "vercel-edge": {
217
- browser: false,
218
- resvg: "wasm",
219
- satori: "wasm",
220
- takumi: "wasm",
221
- sharp: false,
222
- emoji: "fetch",
223
- // edge size limits - bundling 24MB icons not viable
224
- wasm: {
225
- // lowers workers kb size
226
- esmImport: true,
227
- lazy: true
228
- }
229
- },
230
- "cloudflare-pages": cloudflare,
231
- "cloudflare-pages-static": cloudflare,
232
- "cloudflare": cloudflare,
233
- "cloudflare-module": cloudflare,
234
- "cloudflare-durable": cloudflare
235
- };
236
- function resolveOgImagePreset(nitroConfig) {
237
- const nuxt = kit.useNuxt();
238
- if (nuxt.options.dev)
239
- return "nitro-dev";
240
- if (nuxt.options.nitro.static)
241
- return "nitro-prerender";
242
- return kit$1.resolveNitroPreset(nitroConfig);
243
- }
244
- function getPresetNitroPresetCompatibility(target) {
245
- const normalizedTarget = target.replace(RE_LEGACY_SUFFIX, "");
246
- let compatibility = RuntimeCompatibility[normalizedTarget];
247
- if (!compatibility) {
248
- logger_js.logger.warn(`Unknown Nitro preset "${target}" \u2014 falling back to node-server compatibility. Set ogImage.compatibility.runtime to override.`);
249
- compatibility = NodeRuntime;
250
- }
251
- return compatibility;
252
- }
253
- async function applyNitroPresetCompatibility(nitroConfig, options) {
254
- const target = resolveOgImagePreset(nitroConfig);
255
- const compatibility = getPresetNitroPresetCompatibility(target);
256
- const { resolve, detectedRenderers } = options;
257
- const satoriEnabled = detectedRenderers.has("satori");
258
- const browserEnabled = detectedRenderers.has("browser");
259
- const takumiEnabled = detectedRenderers.has("takumi");
260
- for (const renderer of detectedRenderers) {
261
- if (!compatibility[renderer]) {
262
- logger_js.logger.warn(`Renderer "${renderer}" detected but not supported on "${target}" preset. OG images using .${renderer}.vue components may fail.`);
263
- }
264
- }
265
- const emptyMock = await resolve.resolvePath("./runtime/mock/empty");
266
- nitroConfig.alias["#og-image/renderers/satori"] = satoriEnabled ? await resolve.resolvePath("./runtime/server/og-image/satori/renderer") : emptyMock;
267
- nitroConfig.alias["#og-image/renderers/browser"] = browserEnabled ? await resolve.resolvePath("./runtime/server/og-image/browser/renderer") : emptyMock;
268
- nitroConfig.alias["#og-image/renderers/takumi"] = takumiEnabled ? await resolve.resolvePath("./runtime/server/og-image/takumi/renderer") : emptyMock;
269
- const resolvedCompatibility = {};
270
- async function applyBinding(key) {
271
- let binding = options.compatibility?.[key];
272
- if (typeof binding === "undefined")
273
- binding = compatibility[key];
274
- const isRendererBinding = key === "browser" || key === "satori" || key === "takumi";
275
- if (isRendererBinding && !detectedRenderers.has(key)) {
276
- binding = false;
277
- }
278
- resolvedCompatibility[key] = binding;
279
- if (!binding && isRendererBinding) {
280
- nitroConfig.alias[`#og-image/renderers/${key}`] = emptyMock;
281
- }
282
- return {
283
- [`#og-image/bindings/${key}`]: !binding ? emptyMock : await resolve.resolvePath(`./runtime/server/og-image/bindings/${key}/${binding}`)
284
- };
285
- }
286
- const satoriPkgDir = await resolve.resolvePath("satori/package.json").then((p) => p.replace("/package.json", ""));
287
- const yogaWasmPath = `${satoriPkgDir}/yoga.wasm`;
288
- nitroConfig.alias = defu.defu(
289
- { "#og-image/yoga-wasm": `${yogaWasmPath}?module` },
290
- await applyBinding("browser"),
291
- await applyBinding("satori"),
292
- await applyBinding("takumi"),
293
- await applyBinding("resvg"),
294
- await applyBinding("sharp"),
295
- nitroConfig.alias || {}
296
- );
297
- if (Object.values(resolvedCompatibility).includes("wasm")) {
298
- nitroConfig.experimental = nitroConfig.experimental || {};
299
- nitroConfig.experimental.wasm = true;
300
- }
301
- nitroConfig.rollupConfig = nitroConfig.rollupConfig || {};
302
- nitroConfig.wasm = defu.defu(compatibility.wasm, nitroConfig.wasm);
303
- nitroConfig.virtual["#og-image/compatibility"] = () => `export default ${JSON.stringify(resolvedCompatibility)}`;
304
- kit.addTemplate({
305
- filename: "nuxt-og-image/compatibility.mjs",
306
- getContents() {
307
- return `export default ${JSON.stringify(resolvedCompatibility)}`;
308
- },
309
- options: { mode: "server" }
310
- });
311
- return resolvedCompatibility;
312
- }
313
- async function ensureDependencies(dep, nuxt = kit.useNuxt()) {
314
- const { ensureDependencyInstalled } = await import('nypm');
315
- return Promise.all(dep.map((d) => {
316
- return ensureDependencyInstalled(d, {
317
- cwd: nuxt.options.rootDir,
318
- dev: true
319
- });
320
- }));
321
- }
322
-
323
51
  const RE_REFLECT_HAS_MINIFIED = /Reflect\.has\(([\w$]+),([\w$]+)\)\?Reflect\.get\(\1,\2,([\w$]+)\):Reflect\.get\(([\w$]+),\2,\3\)/g;
324
52
  const RE_WASM_IMPORT = /import\("(\.\/wasm\/[^"]+\.wasm)"\)/g;
325
- async function setupBuildHandler(config, resolve, getDetectedRenderers, nuxt = kit.useNuxt()) {
53
+ async function setupBuildHandler(config, resolve, getDetectedRenderers, getCompatibilityMeta = () => ({}), nuxt = kit.useNuxt()) {
326
54
  nuxt.options.nitro.storage = nuxt.options.nitro.storage || {};
327
55
  if (typeof config.runtimeCacheStorage === "object")
328
56
  nuxt.options.nitro.storage["nuxt-og-image"] = config.runtimeCacheStorage;
@@ -342,9 +70,9 @@ async function setupBuildHandler(config, resolve, getDetectedRenderers, nuxt = k
342
70
  });
343
71
  nuxt.hooks.hook("nitro:init", async (nitro) => {
344
72
  const renderers = getDetectedRenderers();
345
- await applyNitroPresetCompatibility(nitro.options, { compatibility: config.compatibility?.runtime, resolve, detectedRenderers: renderers });
346
- const target = resolveOgImagePreset(nitro.options);
347
- const normalizedTarget = target.replace(RE_LEGACY_SUFFIX, "");
73
+ await dependencies.applyNitroPresetCompatibility(nitro.options, { compatibility: config.compatibility?.runtime, resolve, detectedRenderers: renderers, metadata: getCompatibilityMeta() });
74
+ const target = dependencies.resolveOgImagePreset(nitro.options);
75
+ const normalizedTarget = target.replace(dependencies.RE_LEGACY_SUFFIX, "");
348
76
  const isEdgePreset = ["cloudflare", "cloudflare-pages", "cloudflare-pages-static", "cloudflare-module", "cloudflare-durable", "vercel-edge", "netlify-edge"].includes(normalizedTarget);
349
77
  const isCloudflarePreset = normalizedTarget.startsWith("cloudflare");
350
78
  const isCloudflarePagesOrModule = ["cloudflare-pages", "cloudflare-pages-static", "cloudflare-module", "cloudflare-durable"].includes(normalizedTarget);
@@ -356,7 +84,7 @@ async function setupBuildHandler(config, resolve, getDetectedRenderers, nuxt = k
356
84
  nitro.options.cloudflare.pages.routes.exclude.push("/_og/s/*");
357
85
  }
358
86
  nitro.hooks.hook("compiled", async (_nitro) => {
359
- const compatibility = getPresetNitroPresetCompatibility(target);
87
+ const compatibility = dependencies.getPresetNitroPresetCompatibility(target);
360
88
  if (!isEdgePreset)
361
89
  return;
362
90
  const configuredEntry = nitro.options.rollupConfig?.output.entryFileNames;
@@ -475,194 +203,19 @@ function patchWebAssemblyInstantiate(code) {
475
203
  return code;
476
204
  }
477
205
 
478
- const PROVIDER_DEPENDENCIES = [
479
- {
480
- name: "satori",
481
- description: "SVG-based renderer using Satori",
482
- bindings: {
483
- "node": [
484
- { name: "satori", description: "HTML to SVG renderer" },
485
- { name: "@resvg/resvg-js", description: "SVG to PNG converter (native)" }
486
- ],
487
- "wasm": [
488
- { name: "satori", description: "HTML to SVG renderer" },
489
- { name: "@resvg/resvg-wasm", description: "SVG to PNG converter (WASM)" }
490
- ],
491
- "wasm-fs": [
492
- { name: "satori", description: "HTML to SVG renderer" },
493
- { name: "@resvg/resvg-wasm", description: "SVG to PNG converter (WASM)" }
494
- ]
495
- }
496
- },
497
- {
498
- name: "takumi",
499
- description: "Rust-based high-performance renderer (recommended)",
500
- bindings: {
501
- "node": [
502
- { name: "@takumi-rs/core", description: "Native Takumi renderer" }
503
- ],
504
- "wasm": [
505
- { name: "@takumi-rs/wasm", description: "WASM Takumi renderer" }
506
- ],
507
- "wasm-fs": [
508
- { name: "@takumi-rs/wasm", description: "WASM Takumi renderer" }
509
- ]
510
- }
511
- },
512
- {
513
- name: "browser",
514
- description: "Browser-based screenshot renderer",
515
- bindings: {
516
- node: [
517
- { name: "playwright-core", description: "Headless browser automation" }
518
- ]
519
- }
520
- }
521
- ];
522
- async function getInstalledProviders() {
523
- const installed = [];
524
- for (const provider of PROVIDER_DEPENDENCIES) {
525
- if (provider.bindings.node) {
526
- const allNodeInstalled = await Promise.all(
527
- provider.bindings.node.map((dep) => hasResolvableDependency(dep.name))
528
- );
529
- if (allNodeInstalled.every(Boolean)) {
530
- installed.push({ provider: provider.name, binding: "node" });
531
- continue;
532
- }
533
- }
534
- if (provider.bindings.wasm) {
535
- const allWasmInstalled = await Promise.all(
536
- provider.bindings.wasm.map((dep) => hasResolvableDependency(dep.name))
537
- );
538
- if (allWasmInstalled.every(Boolean)) {
539
- installed.push({ provider: provider.name, binding: "wasm" });
540
- }
541
- }
542
- }
543
- return installed;
544
- }
545
- async function getMissingDependencies(provider, binding = "node") {
546
- const providerDef = PROVIDER_DEPENDENCIES.find((p) => p.name === provider);
547
- if (!providerDef)
548
- return [];
549
- const deps = providerDef.bindings[binding] || providerDef.bindings.node || [];
550
- const missing = [];
551
- for (const dep of deps) {
552
- if (!await hasResolvableDependency(dep.name))
553
- missing.push(dep.name);
554
- }
555
- return missing;
556
- }
557
- function getProviderDependencies(provider, binding = "node") {
558
- const providerDef = PROVIDER_DEPENDENCIES.find((p) => p.name === provider);
559
- if (!providerDef)
560
- return [];
561
- const deps = providerDef.bindings[binding] || providerDef.bindings.node || [];
562
- return deps.map((d) => d.name);
563
- }
564
- function getRecommendedBindingFromPreset(provider) {
565
- const preset = resolveOgImagePreset();
566
- const compatibility = getPresetNitroPresetCompatibility(preset);
567
- return getRecommendedBinding(provider, compatibility);
568
- }
569
- function getRecommendedBinding(provider, compatibility) {
570
- if (provider === "satori") {
571
- const resvgBinding = compatibility.resvg;
572
- if (resvgBinding === "wasm-fs")
573
- return "wasm-fs";
574
- if (resvgBinding === "wasm")
575
- return "wasm";
576
- return "node";
577
- }
578
- if (provider === "takumi") {
579
- const takumiBinding = compatibility.takumi;
580
- if (takumiBinding === "wasm")
581
- return "wasm";
582
- return "node";
583
- }
584
- if (provider === "browser") {
585
- return "node";
586
- }
587
- return "node";
588
- }
589
- async function ensureProviderDependencies(provider, binding, nuxt) {
590
- const missing = await getMissingDependencies(provider, binding);
591
- if (missing.length === 0)
592
- return { success: true, installed: [] };
593
- const pm = await nypm.detectPackageManager(nuxt.options.rootDir);
594
- const pmName = pm?.name || "npm";
595
- logger_js.logger.info(`Installing ${provider} dependencies: ${missing.join(", ")}`);
596
- const installed = [];
597
- for (const pkg of missing) {
598
- const success = await nypm.addDependency(pkg, {
599
- cwd: nuxt.options.rootDir,
600
- dev: false
601
- }).then(() => {
602
- installed.push(pkg);
603
- return true;
604
- }).catch(() => {
605
- logger_js.logger.error(`Failed to install ${pkg}. Run manually: ${pmName} add ${pkg}`);
606
- return false;
607
- });
608
- if (!success)
609
- return { success: false, installed };
610
- }
611
- return { success: true, installed };
612
- }
613
- function canPromptInteractively(env = {
614
- hasTTY: stdEnv.hasTTY,
615
- hasStdinTTY: Boolean(process.stdin?.isTTY),
616
- isAgent: stdEnv.isAgent,
617
- isCI: stdEnv.isCI
618
- }) {
619
- return env.hasTTY && env.hasStdinTTY && !env.isAgent && !env.isCI;
620
- }
621
- async function promptForRendererSelection() {
622
- logger_js.logger.info("Welcome to Nuxt OG Image! No renderer dependencies detected.");
623
- const renderer = await logger_js.logger.prompt("Which renderer would you like to use?", {
624
- type: "select",
625
- options: PROVIDER_DEPENDENCIES.map((p) => p.name),
626
- initial: "takumi"
627
- });
628
- return renderer || "takumi";
629
- }
630
- async function validateProviderSetup(renderer, compatibility) {
631
- const issues = [];
632
- const binding = getRecommendedBinding(renderer, compatibility);
633
- const missing = await getMissingDependencies(renderer, binding);
634
- if (missing.length > 0) {
635
- issues.push(`Missing ${renderer} dependencies: ${missing.join(", ")}`);
636
- }
637
- if (renderer === "satori") {
638
- const hasResvgNode = await hasResolvableDependency("@resvg/resvg-js");
639
- const hasResvgWasm = await hasResolvableDependency("@resvg/resvg-wasm");
640
- if (!hasResvgNode && !hasResvgWasm) {
641
- issues.push("Satori requires either @resvg/resvg-js (node) or @resvg/resvg-wasm to render PNGs");
642
- }
643
- }
644
- if (renderer === "browser") {
645
- const hasPlaywright = await hasResolvableDependency("playwright-core");
646
- if (!hasPlaywright && binding === "node") {
647
- issues.push("Browser renderer requires playwright-core");
648
- }
649
- }
650
- return { valid: issues.length === 0, issues };
651
- }
652
-
653
- function setupDevHandler(options, resolve, getDetectedRenderers, nuxt = kit.useNuxt()) {
206
+ function setupDevHandler(options, resolve, getDetectedRenderers, getCompatibilityMeta = () => ({}), nuxt = kit.useNuxt()) {
654
207
  nuxt.hooks.hook("nitro:init", async (nitro) => {
655
208
  const detectedRenderers = new Set(getDetectedRenderers());
656
- const targetCompatibility = getPresetNitroPresetCompatibility(resolveOgImagePreset(nitro.options));
209
+ const targetCompatibility = dependencies.getPresetNitroPresetCompatibility(dependencies.resolveOgImagePreset(nitro.options));
657
210
  for (const renderer of ["satori", "takumi", "browser"]) {
658
211
  if (!detectedRenderers.has(renderer)) {
659
- const binding = getRecommendedBinding(renderer, targetCompatibility);
660
- const missing = await getMissingDependencies(renderer, binding);
212
+ const binding = dependencies.getRecommendedBinding(renderer, targetCompatibility);
213
+ const missing = await dependencies.getMissingDependencies(renderer, binding);
661
214
  if (missing.length === 0)
662
215
  detectedRenderers.add(renderer);
663
216
  }
664
217
  }
665
- await applyNitroPresetCompatibility(nitro.options, { compatibility: options.compatibility?.dev, resolve, detectedRenderers });
218
+ await dependencies.applyNitroPresetCompatibility(nitro.options, { compatibility: options.compatibility?.dev, resolve, detectedRenderers, metadata: getCompatibilityMeta() });
666
219
  });
667
220
  }
668
221
 
@@ -788,9 +341,9 @@ ${content}`, "utf-8");
788
341
  if (nameWithoutExt.includes(".")) {
789
342
  dotNotationName = `${nameWithoutExt}.vue`;
790
343
  } else {
791
- const rendererMatch = nameWithoutExt.match(RE_RENDERER_SUFFIX);
344
+ const rendererMatch = nameWithoutExt.match(dependencies.RE_RENDERER_SUFFIX);
792
345
  const renderer = rendererMatch?.[1]?.toLowerCase() || "satori";
793
- const baseName = nameWithoutExt.replace(RE_RENDERER_SUFFIX, "");
346
+ const baseName = nameWithoutExt.replace(dependencies.RE_RENDERER_SUFFIX, "");
794
347
  dotNotationName = `${baseName}.${renderer}.vue`;
795
348
  }
796
349
  const dir = pathe.join(nuxt.options.srcDir, "components", dirName || "");
@@ -1471,7 +1024,7 @@ async function downlevelColor(prop, value) {
1471
1024
  function convertIndividualTransforms(styles) {
1472
1025
  const parts = [];
1473
1026
  if (styles.translate) {
1474
- const vals = styles.translate.trim().split(RE_WHITESPACE);
1027
+ const vals = styles.translate.trim().split(dependencies.RE_WHITESPACE);
1475
1028
  parts.push(vals.length === 2 ? `translate(${vals[0]}, ${vals[1]})` : `translateX(${vals[0]})`);
1476
1029
  delete styles.translate;
1477
1030
  }
@@ -1480,7 +1033,7 @@ function convertIndividualTransforms(styles) {
1480
1033
  delete styles.rotate;
1481
1034
  }
1482
1035
  if (styles.scale) {
1483
- const vals = styles.scale.trim().split(RE_WHITESPACE).filter(Boolean);
1036
+ const vals = styles.scale.trim().split(dependencies.RE_WHITESPACE).filter(Boolean);
1484
1037
  parts.push(`scale(${vals.join(", ")})`);
1485
1038
  delete styles.scale;
1486
1039
  }
@@ -2113,7 +1666,7 @@ function resolveWeightsFromFontEntry(fontWeight, requestedWeights) {
2113
1666
  min = Number(fontWeight[0]);
2114
1667
  max = Number(fontWeight[1]);
2115
1668
  } else if (typeof fontWeight === "string") {
2116
- const parts = fontWeight.split(RE_WHITESPACE).map(Number);
1669
+ const parts = fontWeight.split(dependencies.RE_WHITESPACE).map(Number);
2117
1670
  if (parts.length >= 2 && !Number.isNaN(parts[0]) && !Number.isNaN(parts[1])) {
2118
1671
  min = parts[0];
2119
1672
  max = parts[1];
@@ -2452,7 +2005,7 @@ ${familyBreakdown}`);
2452
2005
 
2453
2006
  function setupGenerateHandler(_options, resolve, _getDetectedRenderers, nuxt = kit.useNuxt()) {
2454
2007
  nuxt.hooks.hook("nitro:config", async (nitroConfig) => {
2455
- await applyNitroPresetCompatibility(nitroConfig, {
2008
+ await dependencies.applyNitroPresetCompatibility(nitroConfig, {
2456
2009
  compatibility: {
2457
2010
  browser: false,
2458
2011
  satori: false,
@@ -2466,10 +2019,10 @@ function setupGenerateHandler(_options, resolve, _getDetectedRenderers, nuxt = k
2466
2019
  }
2467
2020
 
2468
2021
  const ONE_WEEK_MS = 7 * 24 * 60 * 60 * 1e3;
2469
- function setupPrerenderHandler(options, resolve, getDetectedRenderers, nuxt = kit.useNuxt()) {
2022
+ function setupPrerenderHandler(options, resolve, getDetectedRenderers, getCompatibilityMeta = () => ({}), nuxt = kit.useNuxt()) {
2470
2023
  nuxt.hooks.hook("nitro:init", async (nitro) => {
2471
2024
  nitro.hooks.hook("prerender:config", async (nitroConfig) => {
2472
- await applyNitroPresetCompatibility(nitroConfig, { compatibility: options.compatibility?.prerender, resolve, detectedRenderers: getDetectedRenderers() });
2025
+ await dependencies.applyNitroPresetCompatibility(nitroConfig, { compatibility: options.compatibility?.prerender, resolve, detectedRenderers: getDetectedRenderers(), metadata: getCompatibilityMeta() });
2473
2026
  nitroConfig.wasm = nitroConfig.wasm || {};
2474
2027
  nitroConfig.wasm.esmImport = false;
2475
2028
  nitroConfig.prerender = nitroConfig.prerender || {};
@@ -2557,6 +2110,15 @@ function extractPropNamesFromVue(code) {
2557
2110
  }
2558
2111
  }
2559
2112
 
2113
+ function resolveSigningSecret(configSecret, envSecret, generate) {
2114
+ if (configSecret === false)
2115
+ return { secret: "", hasExplicit: false, optOut: true, generated: false };
2116
+ const explicit = configSecret || "" || (envSecret || "");
2117
+ if (explicit)
2118
+ return { secret: explicit, hasExplicit: true, optOut: false, generated: false };
2119
+ return { secret: generate(), hasExplicit: false, optOut: false, generated: true };
2120
+ }
2121
+
2560
2122
  function isVue(id, opts = {}) {
2561
2123
  const { search } = ufo.parseURL(decodeURIComponent(node_url.pathToFileURL(id).href));
2562
2124
  if (id.endsWith(".vue") && !search) {
@@ -2671,7 +2233,7 @@ async function extractFontRequirementsFromVue(code) {
2671
2233
  const el = node;
2672
2234
  for (const prop of el.props) {
2673
2235
  if (prop.type === ATTRIBUTE_NODE$1 && prop.name === "class" && prop.value) {
2674
- for (const cls of prop.value.content.split(RE_WHITESPACE)) {
2236
+ for (const cls of prop.value.content.split(dependencies.RE_WHITESPACE)) {
2675
2237
  extractFontWeightFromClass(cls, weights, styles);
2676
2238
  extractFontFamilyFromClass(cls, familyClasses, familyNames);
2677
2239
  }
@@ -2998,7 +2560,7 @@ async function transformVueTemplate(code, options) {
2998
2560
  };
2999
2561
  for (const prop of el.props) {
3000
2562
  if (prop.type === ATTRIBUTE_NODE && prop.name === "class" && prop.value) {
3001
- collector.classes = prop.value.content.split(RE_WHITESPACE).filter(Boolean);
2563
+ collector.classes = prop.value.content.split(dependencies.RE_WHITESPACE).filter(Boolean);
3002
2564
  collector.classLoc = {
3003
2565
  start: prop.loc.start.offset,
3004
2566
  end: prop.loc.end.offset
@@ -3491,7 +3053,7 @@ const AssetTransformPlugin = unplugin.createUnplugin((options) => {
3491
3053
  const [fullMatch, , attrsStr, classValue] = elMatch;
3492
3054
  if (!classValue)
3493
3055
  continue;
3494
- const classes = classValue.split(RE_WHITESPACE).filter(Boolean);
3056
+ const classes = classValue.split(dependencies.RE_WHITESPACE).filter(Boolean);
3495
3057
  let iconPrefix;
3496
3058
  let iconName;
3497
3059
  for (const cls of classes) {
@@ -3710,7 +3272,7 @@ const AssetTransformPlugin = unplugin.createUnplugin((options) => {
3710
3272
  const classAttr = el.attributes?.class;
3711
3273
  if (!classAttr)
3712
3274
  return;
3713
- const elClasses = classAttr.split(RE_WHITESPACE).filter(Boolean);
3275
+ const elClasses = classAttr.split(dependencies.RE_WHITESPACE).filter(Boolean);
3714
3276
  const matchedStyles = {};
3715
3277
  const consumedClasses = /* @__PURE__ */ new Set();
3716
3278
  for (const cls of elClasses) {
@@ -4214,15 +3776,19 @@ async function onInstall(nuxt) {
4214
3776
  if (nuxt.options._prepare)
4215
3777
  return;
4216
3778
  if (stdEnv.isCI) {
4217
- const installedProviders2 = await getInstalledProviders();
3779
+ const installedProviders2 = await dependencies.getInstalledProviders();
4218
3780
  if (installedProviders2.length === 0) {
4219
3781
  throw new Error(
4220
- "[nuxt-og-image] No OG image provider dependencies found. Install a provider before running in CI:\n npm add @resvg/resvg-js satori yoga-wasm-web # for satori\n npm add playwright-core # for browser\nSee: https://nuxtseo.com/og-image/getting-started"
3782
+ `[nuxt-og-image] No OG image provider dependencies found. Install a provider before running in CI:
3783
+ npm add ${dependencies.TAKUMI_CORE_INSTALL_SPEC} # for takumi
3784
+ npm add @resvg/resvg-js satori yoga-wasm-web # for satori
3785
+ npm add playwright-core # for browser
3786
+ See: https://nuxtseo.com/og-image/getting-started`
4221
3787
  );
4222
3788
  }
4223
3789
  return;
4224
3790
  }
4225
- if (!canPromptInteractively()) {
3791
+ if (!dependencies.canPromptInteractively()) {
4226
3792
  logger_js.logger.info("Skipping interactive onboarding (non-interactive environment). Defaulting to the takumi renderer.");
4227
3793
  return;
4228
3794
  }
@@ -4232,7 +3798,7 @@ async function onInstall(nuxt) {
4232
3798
  logger_js.logger.info("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
4233
3799
  logger_js.logger.info("");
4234
3800
  const state = {};
4235
- const installedProviders = await getInstalledProviders();
3801
+ const installedProviders = await dependencies.getInstalledProviders();
4236
3802
  if (installedProviders.length > 0) {
4237
3803
  logger_js.logger.info("Detected installed providers:");
4238
3804
  for (const { provider, binding } of installedProviders) {
@@ -4250,7 +3816,7 @@ async function onInstall(nuxt) {
4250
3816
  if (!state.selectedRenderer) {
4251
3817
  const rendererChoice = await logger_js.logger.prompt("Select a renderer:", {
4252
3818
  type: "select",
4253
- options: PROVIDER_DEPENDENCIES.map((p) => ({
3819
+ options: dependencies.PROVIDER_DEPENDENCIES.map((p) => ({
4254
3820
  label: p.name,
4255
3821
  value: p.name,
4256
3822
  hint: p.description
@@ -4263,8 +3829,8 @@ async function onInstall(nuxt) {
4263
3829
  state.selectedRenderer = rendererChoice;
4264
3830
  }
4265
3831
  if (!state.selectedBinding) {
4266
- const recommendedBinding = getRecommendedBindingFromPreset(state.selectedRenderer);
4267
- const providerDef = PROVIDER_DEPENDENCIES.find((p) => p.name === state.selectedRenderer);
3832
+ const recommendedBinding = dependencies.getRecommendedBindingFromPreset(state.selectedRenderer);
3833
+ const providerDef = dependencies.PROVIDER_DEPENDENCIES.find((p) => p.name === state.selectedRenderer);
4268
3834
  const hasNodeBinding = !!providerDef?.bindings.node;
4269
3835
  const hasWasmBinding = !!providerDef?.bindings.wasm || !!providerDef?.bindings["wasm-fs"];
4270
3836
  if (hasNodeBinding && hasWasmBinding) {
@@ -4289,10 +3855,10 @@ async function onInstall(nuxt) {
4289
3855
  state.selectedBinding = hasNodeBinding ? "node" : "wasm";
4290
3856
  }
4291
3857
  }
4292
- const missing = await getMissingDependencies(state.selectedRenderer, state.selectedBinding);
4293
- if (missing.length > 0) {
3858
+ const missingInstallSpecs = await dependencies.getMissingDependencyInstallSpecs(state.selectedRenderer, state.selectedBinding);
3859
+ if (missingInstallSpecs.length > 0) {
4294
3860
  logger_js.logger.info(`Required dependencies for ${state.selectedRenderer} (${state.selectedBinding}):`);
4295
- for (const pkg of missing) {
3861
+ for (const pkg of missingInstallSpecs) {
4296
3862
  logger_js.logger.info(` - ${pkg}`);
4297
3863
  }
4298
3864
  const installDeps = await logger_js.logger.prompt("Install missing dependencies?", {
@@ -4300,7 +3866,7 @@ async function onInstall(nuxt) {
4300
3866
  initial: true
4301
3867
  });
4302
3868
  if (installDeps) {
4303
- const result = await ensureProviderDependencies(
3869
+ const result = await dependencies.ensureProviderDependencies(
4304
3870
  state.selectedRenderer,
4305
3871
  state.selectedBinding,
4306
3872
  nuxt
@@ -4309,11 +3875,11 @@ async function onInstall(nuxt) {
4309
3875
  logger_js.logger.success(`Installed: ${result.installed.join(", ")}`);
4310
3876
  } else {
4311
3877
  logger_js.logger.error("Failed to install some dependencies. Install manually:");
4312
- const allDeps = getProviderDependencies(state.selectedRenderer, state.selectedBinding);
3878
+ const allDeps = dependencies.getProviderDependencyInstallSpecs(state.selectedRenderer, state.selectedBinding);
4313
3879
  logger_js.logger.info(` npm add ${allDeps.join(" ")}`);
4314
3880
  }
4315
3881
  } else {
4316
- const allDeps = getProviderDependencies(state.selectedRenderer, state.selectedBinding);
3882
+ const allDeps = dependencies.getProviderDependencyInstallSpecs(state.selectedRenderer, state.selectedBinding);
4317
3883
  logger_js.logger.warn("Dependencies required. Install manually:");
4318
3884
  logger_js.logger.info(` npm add ${allDeps.join(" ")}`);
4319
3885
  }
@@ -4327,14 +3893,14 @@ async function onInstall(nuxt) {
4327
3893
  if (createComponent) {
4328
3894
  await createStarterComponent(nuxt);
4329
3895
  }
4330
- const hasSharp = await hasResolvableDependency("sharp");
3896
+ const hasSharp = await dependencies.hasResolvableDependency("sharp");
4331
3897
  if (!hasSharp && state.selectedRenderer === "satori") {
4332
3898
  const wantJpeg = await logger_js.logger.prompt("Install sharp for JPEG output support?", {
4333
3899
  type: "confirm",
4334
3900
  initial: false
4335
3901
  });
4336
3902
  if (wantJpeg) {
4337
- await ensureProviderDependencies("satori", "node", nuxt).catch(() => logger_js.logger.warn("Failed to install sharp, JPEG output unavailable"));
3903
+ await dependencies.ensureProviderDependencies("satori", "node", nuxt).catch(() => logger_js.logger.warn("Failed to install sharp, JPEG output unavailable"));
4338
3904
  }
4339
3905
  }
4340
3906
  logger_js.logger.info("");
@@ -4369,7 +3935,7 @@ async function onUpgrade(nuxt, _options, previousVersion) {
4369
3935
  issues.push(`Config \`defaults.component\` is deprecated: Removed \u2014 rename component to OgImage/Default.{renderer}.vue`);
4370
3936
  }
4371
3937
  if ("fonts" in ogImageConfig && !nuxt.options._prepare && !stdEnv.isCI) {
4372
- await fonts.promptFontsMigration(nuxt.options.rootDir);
3938
+ await dependencies.promptFontsMigration(nuxt.options.rootDir);
4373
3939
  }
4374
3940
  }
4375
3941
  if (issues.length > 0) {
@@ -4386,17 +3952,17 @@ async function onUpgrade(nuxt, _options, previousVersion) {
4386
3952
  const ogComponentDir = pathe.join(nuxt.options.srcDir, "components", "OgImage");
4387
3953
  if (fs.existsSync(ogComponentDir)) {
4388
3954
  for (const file of fs.readdirSync(ogComponentDir).filter((f) => f.endsWith(".vue"))) {
4389
- const renderer = getRendererFromFilename(file);
3955
+ const renderer = dependencies.getRendererFromFilename(file);
4390
3956
  if (renderer)
4391
3957
  detectedRenderers.add(renderer);
4392
3958
  }
4393
3959
  }
4394
3960
  if (detectedRenderers.size === 0)
4395
3961
  detectedRenderers.add("satori");
4396
- const preset = resolveOgImagePreset();
4397
- const compatibility = getPresetNitroPresetCompatibility(preset);
3962
+ const preset = dependencies.resolveOgImagePreset();
3963
+ const compatibility = dependencies.getPresetNitroPresetCompatibility(preset);
4398
3964
  for (const renderer of detectedRenderers) {
4399
- const validation = await validateProviderSetup(renderer, compatibility);
3965
+ const validation = await dependencies.validateProviderSetup(renderer, compatibility);
4400
3966
  if (!validation.valid) {
4401
3967
  logger_js.logger.warn(`Provider setup issues for ${renderer}:`);
4402
3968
  for (const issue of validation.issues) {
@@ -4455,7 +4021,7 @@ function registerTypeTemplates(ctx) {
4455
4021
  const sortedDirs = config.componentDirs.sort((a, b) => b.length - a.length);
4456
4022
  const baseNameRenderers = /* @__PURE__ */ new Map();
4457
4023
  for (const component of componentCtx.components) {
4458
- for (const baseName of getRegisteredBaseNames(component.pascalName)) {
4024
+ for (const baseName of dependencies.getRegisteredBaseNames(component.pascalName)) {
4459
4025
  const renderers = baseNameRenderers.get(baseName) || [];
4460
4026
  renderers.push(component.renderer);
4461
4027
  baseNameRenderers.set(baseName, renderers);
@@ -4466,7 +4032,7 @@ function registerTypeTemplates(ctx) {
4466
4032
  const importType = getComponentTypeExpression(component, nuxt);
4467
4033
  const strippedName = sortedDirs.reduce((n, dir) => n.replace(new RegExp(`^${dir}`), ""), component.pascalName);
4468
4034
  const renderer = component.renderer;
4469
- const baseNames = getRegisteredBaseNames(component.pascalName);
4035
+ const baseNames = dependencies.getRegisteredBaseNames(component.pascalName);
4470
4036
  lines.push(` '${strippedName}': ${importType}`);
4471
4037
  const seen = /* @__PURE__ */ new Set([strippedName]);
4472
4038
  for (const baseName of baseNames) {
@@ -4548,8 +4114,8 @@ declare module '#og-image-virtual/build-dir.mjs' {
4548
4114
  }
4549
4115
 
4550
4116
  declare module '#og-image/compatibility' {
4551
- import type { RuntimeCompatibilitySchema } from '${typesPath}'
4552
- const compatibility: Partial<Omit<RuntimeCompatibilitySchema, 'wasm'>>
4117
+ import type { RuntimeCompatibilityPayload } from '${typesPath}'
4118
+ const compatibility: RuntimeCompatibilityPayload
4553
4119
  export default compatibility
4554
4120
  }
4555
4121
  declare module '#og-image-virtual/tw4-theme.mjs' {
@@ -4665,6 +4231,44 @@ const RE_INLINE_CONFIG = /const\s+inlineConfig\s*=\s*(\{[\s\S]*?\n\})/;
4665
4231
  const RE_PASCAL_TO_KEBAB = /([a-z])([A-Z])/g;
4666
4232
  const RE_TILDE_PREFIX = /^~\//;
4667
4233
  const IS_MODULE_DEVELOPMENT = undefined.endsWith(".ts");
4234
+ async function findPackageVersionFromEntry(pkg, resolver) {
4235
+ const entry = await resolver.resolvePath(pkg, { fallbackToOriginal: false }).catch(() => null);
4236
+ if (!entry || entry === pkg)
4237
+ return;
4238
+ let dir = pathe.dirname(entry);
4239
+ while (dir && dir !== pathe.dirname(dir)) {
4240
+ const pkgPath = pathe.join(dir, "package.json");
4241
+ if (fs.existsSync(pkgPath)) {
4242
+ const pkgJson = await pkgTypes.readPackageJSON(pkgPath).catch(() => null);
4243
+ if (pkgJson?.name === pkg)
4244
+ return pkgJson.version;
4245
+ }
4246
+ dir = pathe.dirname(dir);
4247
+ }
4248
+ }
4249
+ function normalizeTakumiVersion(version) {
4250
+ const major = version ? Number.parseInt(version.split(".")[0], 10) : Number.NaN;
4251
+ if (major >= 2)
4252
+ return 2;
4253
+ if (major === 1)
4254
+ return 1;
4255
+ }
4256
+ async function detectTakumiVersion(resolver, binding) {
4257
+ const packages = binding === "wasm" ? ["@takumi-rs/wasm", "@takumi-rs/core"] : ["@takumi-rs/core", "@takumi-rs/wasm"];
4258
+ const versions = (await Promise.all(packages.map(async (pkg) => {
4259
+ const version = await findPackageVersionFromEntry(pkg, resolver);
4260
+ const major = normalizeTakumiVersion(version);
4261
+ return version && major ? { pkg, version, major } : void 0;
4262
+ }))).filter(Boolean);
4263
+ const detectedMajors = new Set(versions.map(({ major }) => major));
4264
+ if (detectedMajors.size > 1) {
4265
+ const preferred = versions[0];
4266
+ logger_js.logger.warn(
4267
+ `Mismatched @takumi-rs package major versions detected (${versions.map(({ pkg, version }) => `${pkg}@${version}`).join(", ")}). Using ${preferred.pkg}@${preferred.version} for Takumi v${preferred.major} compatibility.`
4268
+ );
4269
+ }
4270
+ return versions[0]?.major;
4271
+ }
4668
4272
  function isProviderEnabledForEnv(provider, nuxt, config) {
4669
4273
  return nuxt.options.dev && config.compatibility?.dev?.[provider] !== false || !nuxt.options.dev && (config.compatibility?.runtime?.[provider] !== false || config.compatibility?.prerender?.[provider] !== false);
4670
4274
  }
@@ -4713,7 +4317,7 @@ const module$1 = kit.defineNuxtModule({
4713
4317
  await onUpgrade(nuxt, options, previousVersion);
4714
4318
  },
4715
4319
  async setup(config, nuxt) {
4716
- 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.CfTPCtaS.cjs', document.baseURI).href)));
4320
+ 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.yDJBWXBt.cjs', document.baseURI).href)));
4717
4321
  const fixSharedPath = (p) => {
4718
4322
  return p.replace(/\/shared\/runtime(\/|$)/, "/runtime$1").replace(/\/shared\/client(\/|$)/, "/client$1").replace(/\/shared\/devtools(\/|$)/, "/devtools$1");
4719
4323
  };
@@ -4744,23 +4348,30 @@ const module$1 = kit.defineNuxtModule({
4744
4348
  if (config.debug && !nuxt.options.dev) {
4745
4349
  logger_js.logger.warn("`ogImage.debug` is enabled in production. This exposes the `/_og/debug.json` endpoint and should not be enabled in production. Disable it before deploying.");
4746
4350
  }
4747
- const hasSecret = !!(config.security?.secret || process.env.NUXT_OG_IMAGE_SECRET);
4748
- if (config.security?.strict && !hasSecret) {
4351
+ const signing = resolveSigningSecret(
4352
+ config.security?.secret,
4353
+ process.env.NUXT_OG_IMAGE_SECRET,
4354
+ () => node_crypto.randomBytes(32).toString("base64url")
4355
+ );
4356
+ const resolvedSecret = signing.secret;
4357
+ if (config.security?.strict && !signing.hasExplicit) {
4749
4358
  throw new Error("[nuxt-og-image] `security.strict` requires a signing secret. Generate one with: npx nuxt-og-image generate-secret");
4750
4359
  }
4751
- if (nuxt.options.dev && !config.zeroRuntime && !hasSecret) {
4752
- logger_js.logger.warn([
4753
- "OG image URLs are not signed. Anyone can craft arbitrary image generation requests.",
4754
- "",
4755
- "Set a signing secret via env variable:",
4756
- " NUXT_OG_IMAGE_SECRET=<secret>",
4757
- "",
4758
- " Generate one with: npx nuxt-og-image generate-secret",
4759
- "",
4760
- "Or enable zero-runtime mode to disable dynamic generation entirely:",
4761
- " ogImage: { zeroRuntime: true }"
4762
- ].join("\n"));
4763
- }
4360
+ nuxt.hook("nitro:init", (nitro) => {
4361
+ const hasServerRuntime = !nitro.options.static && !nuxt.options._generate;
4362
+ if (!nuxt.options.dev || config.zeroRuntime || !hasServerRuntime)
4363
+ return;
4364
+ if (signing.optOut) {
4365
+ logger_js.logger.warn("OG image URL signing is disabled (`security.secret: false`). Anyone can craft arbitrary image generation requests.");
4366
+ } else if (signing.generated) {
4367
+ logger_js.logger.warn([
4368
+ "OG image URLs are signed with an auto-generated secret that changes every build.",
4369
+ "This is fine for single-instance deploys; for rolling or multi-instance deploys set a stable secret:",
4370
+ " NUXT_OG_IMAGE_SECRET=<secret>",
4371
+ " Generate one with: npx nuxt-og-image generate-secret"
4372
+ ].join("\n"));
4373
+ }
4374
+ });
4764
4375
  const ogImageConfig = config;
4765
4376
  for (const key of Object.keys(REMOVED_CONFIG)) {
4766
4377
  if (key !== "chromium-node" && key !== "browser-node" && key in ogImageConfig && ogImageConfig[key] !== void 0) {
@@ -4798,9 +4409,10 @@ Or pin to a specific version in package.json:
4798
4409
  }
4799
4410
  nuxt.options.alias["#og-image"] = resolve("./runtime");
4800
4411
  nuxt.options.alias["#og-image-cache"] = resolve("./runtime/server/og-image/cache/lru");
4801
- const preset = resolveOgImagePreset(nuxt.options.nitro);
4802
- const targetCompatibility = getPresetNitroPresetCompatibility(preset);
4803
- const normalizedPreset = preset.replace(RE_LEGACY_SUFFIX, "");
4412
+ const preset = dependencies.resolveOgImagePreset(nuxt.options.nitro);
4413
+ const targetCompatibility = dependencies.getPresetNitroPresetCompatibility(preset);
4414
+ const runtimeCompatibilityMeta = {};
4415
+ const normalizedPreset = preset.replace(dependencies.RE_LEGACY_SUFFIX, "");
4804
4416
  const isCloudflareWorkers = ["cloudflare", "cloudflare-module", "cloudflare-durable"].includes(normalizedPreset);
4805
4417
  if (isCloudflareWorkers) {
4806
4418
  nuxt.hooks.hook("nitro:init", (nitro) => {
@@ -5071,7 +4683,7 @@ ${familyInfo}`);
5071
4683
  }
5072
4684
  } else if (hasConfiguredJpegs) {
5073
4685
  logger_js.logger.warn("You have enabled `JPEG` images. These require the `sharp` dependency which is missing, installing it for you.");
5074
- await ensureDependencies(["sharp"]);
4686
+ await dependencies.ensureDependencies(["sharp"]);
5075
4687
  logger_js.logger.warn("Support for `sharp` is limited so check the compatibility guide.");
5076
4688
  attemptSharpUsage = true;
5077
4689
  }
@@ -5085,8 +4697,8 @@ ${familyInfo}`);
5085
4697
  }
5086
4698
  }
5087
4699
  if (isProviderEnabledForEnv("browser", nuxt, config)) {
5088
- const hasChromeLocally = checkLocalChrome();
5089
- const hasPlaywrightDependency = await hasResolvableDependency("playwright");
4700
+ const hasChromeLocally = dependencies.checkLocalChrome();
4701
+ const hasPlaywrightDependency = await dependencies.hasResolvableDependency("playwright");
5090
4702
  const browserCompatibilityFlags = {
5091
4703
  prerender: config.compatibility?.prerender?.browser,
5092
4704
  dev: config.compatibility?.dev?.browser,
@@ -5103,7 +4715,7 @@ ${familyInfo}`);
5103
4715
  if (!browserConfig.binding) {
5104
4716
  throw new Error("[nuxt-og-image] `ogImage.browser.binding` is required when provider is cloudflare");
5105
4717
  }
5106
- const hasCloudfarePuppeteer = await hasResolvableDependency("@cloudflare/puppeteer");
4718
+ const hasCloudfarePuppeteer = await dependencies.hasResolvableDependency("@cloudflare/puppeteer");
5107
4719
  if (!hasCloudfarePuppeteer) {
5108
4720
  throw new Error(
5109
4721
  "[nuxt-og-image] Missing @cloudflare/puppeteer dependency. Install it with: pnpm add @cloudflare/puppeteer"
@@ -5113,12 +4725,12 @@ ${familyInfo}`);
5113
4725
  browserBinding.prerender = hasPlaywrightDependency ? "playwright" : hasChromeLocally ? "chrome-launcher" : "on-demand";
5114
4726
  browserBinding.runtime = "cloudflare";
5115
4727
  } else if (nuxt.options.dev) {
5116
- if (isUndefinedOrTruthy(browserCompatibilityFlags.dev))
4728
+ if (dependencies.isUndefinedOrTruthy(browserCompatibilityFlags.dev))
5117
4729
  browserBinding.dev = hasChromeLocally ? "chrome-launcher" : hasPlaywrightDependency ? "playwright" : "on-demand";
5118
4730
  } else {
5119
- if (isUndefinedOrTruthy(browserCompatibilityFlags.prerender))
4731
+ if (dependencies.isUndefinedOrTruthy(browserCompatibilityFlags.prerender))
5120
4732
  browserBinding.prerender = hasChromeLocally ? "chrome-launcher" : hasPlaywrightDependency ? "playwright" : "on-demand";
5121
- if (isUndefinedOrTruthy(browserCompatibilityFlags.runtime))
4733
+ if (dependencies.isUndefinedOrTruthy(browserCompatibilityFlags.runtime))
5122
4734
  browserBinding.runtime = hasPlaywrightDependency ? "playwright" : null;
5123
4735
  }
5124
4736
  config.compatibility = defu.defu(config.compatibility, {
@@ -5127,7 +4739,7 @@ ${familyInfo}`);
5127
4739
  prerender: { browser: browserBinding.prerender }
5128
4740
  });
5129
4741
  }
5130
- await kit$2.installNuxtSiteConfig();
4742
+ await kit$1.installNuxtSiteConfig();
5131
4743
  nuxt.options.experimental.componentIslands ||= true;
5132
4744
  if (config.debug || nuxt.options.dev) {
5133
4745
  kit.addServerHandler({
@@ -5240,7 +4852,7 @@ ${familyInfo}`);
5240
4852
  if (isUserDir && fs__namespace.existsSync(resolvedPath)) {
5241
4853
  const files = fs__namespace.readdirSync(resolvedPath).filter((f) => f.endsWith(".vue"));
5242
4854
  for (const file of files) {
5243
- const renderer = getRendererFromFilename(file);
4855
+ const renderer = dependencies.getRendererFromFilename(file);
5244
4856
  if (renderer) {
5245
4857
  ogImageComponentCtx.detectedRenderers.add(renderer);
5246
4858
  hasUserComponents = true;
@@ -5249,42 +4861,45 @@ ${familyInfo}`);
5249
4861
  }
5250
4862
  }
5251
4863
  if (!nuxt.options._prepare && !hasUserComponents) {
5252
- const installedProviders = await getInstalledProviders();
4864
+ const installedProviders = await dependencies.getInstalledProviders();
5253
4865
  const preferred = installedProviders.find((p) => p.provider === "takumi") || installedProviders[0];
5254
4866
  if (preferred) {
5255
4867
  ogImageComponentCtx.detectedRenderers.add(preferred.provider);
5256
4868
  logger_js.logger.debug(`Using ${preferred.provider} renderer`);
5257
- } else if (nuxt.options.dev && !nuxt.options._prepare && canPromptInteractively()) {
5258
- const renderer = await promptForRendererSelection();
4869
+ } else if (nuxt.options.dev && !nuxt.options._prepare && dependencies.canPromptInteractively()) {
4870
+ const renderer = await dependencies.promptForRendererSelection();
5259
4871
  ogImageComponentCtx.detectedRenderers.add(renderer);
5260
4872
  logger_js.logger.debug(`Using ${renderer} renderer`);
5261
4873
  } else {
5262
4874
  ogImageComponentCtx.detectedRenderers.add("takumi");
5263
- logger_js.logger.warn("No OG image renderer dependency detected. Defaulting to `takumi` (non-interactive environment). Install `@takumi-rs/core`, or add a renderer component (e.g. components/OgImage/Default.satori.vue) to choose explicitly.");
4875
+ logger_js.logger.warn(`No OG image renderer dependency detected. Defaulting to \`takumi\` (non-interactive environment). Install \`${dependencies.TAKUMI_CORE_INSTALL_SPEC}\`, or add a renderer component (e.g. components/OgImage/Default.satori.vue) to choose explicitly.`);
5264
4876
  }
5265
4877
  }
5266
4878
  const availableRenderers = /* @__PURE__ */ new Set();
5267
4879
  if (!nuxt.options._prepare) {
5268
4880
  if (!config.zeroRuntime) {
5269
4881
  for (const renderer of ogImageComponentCtx.detectedRenderers) {
5270
- const binding = getRecommendedBinding(renderer, targetCompatibility);
5271
- const missing = await getMissingDependencies(renderer, binding);
4882
+ const binding = dependencies.getRecommendedBinding(renderer, targetCompatibility);
4883
+ const missing = await dependencies.getMissingDependencies(renderer, binding);
5272
4884
  if (missing.length === 0) {
5273
4885
  availableRenderers.add(renderer);
5274
4886
  } else if (nuxt.options.dev && !nuxt.options._prepare) {
5275
- logger_js.logger.warn(`${renderer} renderer requires: ${missing.join(", ")}`);
5276
- const { success } = await ensureProviderDependencies(renderer, binding, nuxt);
4887
+ const installSpecs = await dependencies.getMissingDependencyInstallSpecs(renderer, binding);
4888
+ logger_js.logger.warn(`${renderer} renderer requires: ${installSpecs.join(", ")}`);
4889
+ const { success } = await dependencies.ensureProviderDependencies(renderer, binding, nuxt);
5277
4890
  if (success) {
5278
4891
  availableRenderers.add(renderer);
5279
4892
  } else if (stdEnv.isAgent) {
5280
- throw new Error(`[nuxt-og-image] Failed to install ${renderer} dependencies: ${missing.join(", ")}. Install manually: npx nypm add ${missing.join(" ")}`);
4893
+ throw new Error(`[nuxt-og-image] Failed to install ${renderer} dependencies: ${missing.join(", ")}. Install manually: npx nypm add ${installSpecs.join(" ")}`);
5281
4894
  } else {
5282
4895
  logger_js.logger.error(`Failed to install ${renderer} dependencies. Templates using this renderer won't work.`);
5283
4896
  }
5284
4897
  } else if (stdEnv.isAgent) {
5285
- throw new Error(`[nuxt-og-image] ${renderer} renderer missing dependencies: ${missing.join(", ")}. Install with: npx nypm add ${missing.join(" ")}`);
4898
+ const installSpecs = await dependencies.getMissingDependencyInstallSpecs(renderer, binding);
4899
+ throw new Error(`[nuxt-og-image] ${renderer} renderer missing dependencies: ${missing.join(", ")}. Install with: npx nypm add ${installSpecs.join(" ")}`);
5286
4900
  } else {
5287
- logger_js.logger.error(`${renderer} renderer missing dependencies: ${missing.join(", ")}. Install with: npx nypm add ${missing.join(" ")}`);
4901
+ const installSpecs = await dependencies.getMissingDependencyInstallSpecs(renderer, binding);
4902
+ logger_js.logger.error(`${renderer} renderer missing dependencies: ${missing.join(", ")}. Install with: npx nypm add ${installSpecs.join(" ")}`);
5288
4903
  }
5289
4904
  if (renderer === "satori" && availableRenderers.has(renderer) && binding !== "node") {
5290
4905
  logger_js.logger.warn("ReSVG native binding not available. Falling back to WASM version, this may slow down PNG rendering.");
@@ -5304,11 +4919,19 @@ ${familyInfo}`);
5304
4919
  availableRenderers.add(renderer);
5305
4920
  }
5306
4921
  }
4922
+ runtimeCompatibilityMeta.takumiVersion = await detectTakumiVersion(
4923
+ resolver,
4924
+ dependencies.getRecommendedBinding("takumi", targetCompatibility)
4925
+ );
4926
+ if (ogImageComponentCtx.detectedRenderers.has("takumi") && !runtimeCompatibilityMeta.takumiVersion) {
4927
+ logger_js.logger.warn("Unable to detect @takumi-rs version. Falling back to Takumi v1 renderer compatibility.");
4928
+ runtimeCompatibilityMeta.takumiVersion = 1;
4929
+ }
5307
4930
  if (nuxt.options.dev) {
5308
4931
  const communityRenderers = /* @__PURE__ */ new Set();
5309
4932
  for (const renderer of ["satori", "takumi", "browser"]) {
5310
- const binding = getRecommendedBinding(renderer, targetCompatibility);
5311
- const missing = await getMissingDependencies(renderer, binding);
4933
+ const binding = dependencies.getRecommendedBinding(renderer, targetCompatibility);
4934
+ const missing = await dependencies.getMissingDependencies(renderer, binding);
5312
4935
  if (missing.length === 0)
5313
4936
  communityRenderers.add(renderer);
5314
4937
  }
@@ -5339,7 +4962,7 @@ ${familyInfo}`);
5339
4962
  if (valid && fs__namespace.existsSync(component.filePath)) {
5340
4963
  if (!component.filePath.endsWith(".vue"))
5341
4964
  return;
5342
- const renderer = getRendererFromFilename(component.filePath);
4965
+ const renderer = dependencies.getRendererFromFilename(component.filePath);
5343
4966
  if (!renderer) {
5344
4967
  invalidComponents.push(component.filePath);
5345
4968
  return;
@@ -5370,18 +4993,18 @@ ${familyInfo}`);
5370
4993
  const communityDir = resolve("./runtime/app/components/Templates/Community");
5371
4994
  if (fs__namespace.existsSync(communityDir)) {
5372
4995
  fs__namespace.readdirSync(communityDir).filter((f) => f.endsWith(".vue")).forEach((file) => {
5373
- const renderer = getRendererFromFilename(file);
4996
+ const renderer = dependencies.getRendererFromFilename(file);
5374
4997
  if (!renderer)
5375
4998
  return;
5376
4999
  const pascalName = file.replace(".vue", "").split(".").map((s, i) => i === 0 ? s : s.charAt(0).toUpperCase() + s.slice(1)).join("");
5377
5000
  const filePath = resolve(communityDir);
5378
- const communityBaseNames = getRegisteredBaseNames(pascalName);
5001
+ const communityBaseNames = dependencies.getRegisteredBaseNames(pascalName);
5379
5002
  if (ogImageComponentCtx.components.some((c) => {
5380
5003
  if (c.pascalName === pascalName)
5381
5004
  return true;
5382
5005
  if (c.renderer !== renderer)
5383
5006
  return false;
5384
- const existingBaseNames = getRegisteredBaseNames(c.pascalName);
5007
+ const existingBaseNames = dependencies.getRegisteredBaseNames(c.pascalName);
5385
5008
  return communityBaseNames.some((cb) => existingBaseNames.includes(cb));
5386
5009
  })) {
5387
5010
  return;
@@ -5402,10 +5025,12 @@ ${familyInfo}`);
5402
5025
  if (nuxt.options.dev) {
5403
5026
  for (const renderer of ogImageComponentCtx.detectedRenderers) {
5404
5027
  if (!availableRenderers.has(renderer)) {
5405
- const binding = getRecommendedBinding(renderer, targetCompatibility);
5406
- getMissingDependencies(renderer, binding).then((missing) => {
5407
- if (missing.length > 0)
5408
- logger_js.logger.warn(`New ${renderer} component detected but dependencies missing: ${missing.join(", ")}. Install with: npx nypm add ${missing.join(" ")}`);
5028
+ const binding = dependencies.getRecommendedBinding(renderer, targetCompatibility);
5029
+ dependencies.getMissingDependencies(renderer, binding).then(async (missing) => {
5030
+ if (missing.length > 0) {
5031
+ const installSpecs = await dependencies.getMissingDependencyInstallSpecs(renderer, binding);
5032
+ logger_js.logger.warn(`New ${renderer} component detected but dependencies missing: ${missing.join(", ")}. Install with: npx nypm add ${installSpecs.join(" ")}`);
5033
+ }
5409
5034
  });
5410
5035
  }
5411
5036
  }
@@ -5451,7 +5076,7 @@ Run: npx nuxt-og-image migrate v6`;
5451
5076
  maxAge: 60 * 60 * 24 * 365
5452
5077
  });
5453
5078
  nuxt.options.nitro.virtual["#og-image-virtual/public-assets.mjs"] = async () => {
5454
- const normalizedPreset2 = preset.replace(RE_LEGACY_SUFFIX, "");
5079
+ const normalizedPreset2 = preset.replace(dependencies.RE_LEGACY_SUFFIX, "");
5455
5080
  const isCloudflare = normalizedPreset2 === "cloudflare" || normalizedPreset2.startsWith("cloudflare-");
5456
5081
  if (isCloudflare) {
5457
5082
  const devBinding2 = resolver.resolve("./runtime/server/og-image/bindings/font-assets/dev-prerender");
@@ -5607,7 +5232,7 @@ export const rootDir = ${JSON.stringify(nuxt.options.rootDir)}`;
5607
5232
  const buildCacheDir = config.buildCache ? pathe.join(nuxt.options.rootDir, buildCachePath) : void 0;
5608
5233
  nuxt.hooks.hook("modules:done", async () => {
5609
5234
  const hasColorModeModule = kit.hasNuxtModule("@nuxtjs/color-mode");
5610
- const colorModeOptions = hasColorModeModule ? await kit$1.getNuxtModuleOptions("@nuxtjs/color-mode") : {};
5235
+ const colorModeOptions = hasColorModeModule ? await kit$2.getNuxtModuleOptions("@nuxtjs/color-mode") : {};
5611
5236
  let colorPreference = colorModeOptions.preference;
5612
5237
  if (!colorPreference || colorPreference === "system")
5613
5238
  colorPreference = colorModeOptions.fallback;
@@ -5643,7 +5268,7 @@ export const rootDir = ${JSON.stringify(nuxt.options.rootDir)}`;
5643
5268
  imageFetchTimeout: config.security?.imageFetchTimeout ?? 3e3,
5644
5269
  maxQueryParamSize: config.security?.maxQueryParamSize ?? (config.security?.strict ? 2048 : null),
5645
5270
  restrictRuntimeImagesToOrigin: config.security?.restrictRuntimeImagesToOrigin === true || config.security?.strict && config.security?.restrictRuntimeImagesToOrigin == null ? [] : config.security?.restrictRuntimeImagesToOrigin || false,
5646
- secret: config.security?.secret || process.env.NUXT_OG_IMAGE_SECRET || ""
5271
+ secret: resolvedSecret
5647
5272
  }
5648
5273
  };
5649
5274
  if (nuxt.options.dev) {
@@ -5657,7 +5282,7 @@ export const rootDir = ${JSON.stringify(nuxt.options.rootDir)}`;
5657
5282
  const existingOgImageCfg = nuxt.options.runtimeConfig.ogImage;
5658
5283
  nuxt.options.runtimeConfig.ogImage = {
5659
5284
  ...existingOgImageCfg && typeof existingOgImageCfg === "object" ? existingOgImageCfg : {},
5660
- secret: existingOgImageCfg?.secret || config.security?.secret || process.env.NUXT_OG_IMAGE_SECRET || ""
5285
+ secret: existingOgImageCfg?.secret || (signing.hasExplicit ? signing.secret : "")
5661
5286
  };
5662
5287
  nuxt.options.runtimeConfig.public = {
5663
5288
  ...nuxt.options.runtimeConfig.public,
@@ -5678,8 +5303,9 @@ export const rootDir = ${JSON.stringify(nuxt.options.rootDir)}`;
5678
5303
  };
5679
5304
  });
5680
5305
  const getDetectedRenderers = () => ogImageComponentCtx.detectedRenderers;
5306
+ const getCompatibilityMeta = () => runtimeCompatibilityMeta;
5681
5307
  if (nuxt.options.dev) {
5682
- setupDevHandler(config, resolver, getDetectedRenderers);
5308
+ setupDevHandler(config, resolver, getDetectedRenderers, getCompatibilityMeta);
5683
5309
  setupDevToolsUI(config, resolve, nuxt, cssFramework || "none");
5684
5310
  const useNitro = new Promise((resolveNitro) => {
5685
5311
  nuxt.hooks.hook("nitro:init", resolveNitro);
@@ -5710,14 +5336,14 @@ export const rootDir = ${JSON.stringify(nuxt.options.rootDir)}`;
5710
5336
  await nitro.hooks.callHook("rollup:reload");
5711
5337
  }
5712
5338
  });
5713
- } else if (kit$1.isNuxtGenerate()) {
5339
+ } else if (kit$2.isNuxtGenerate()) {
5714
5340
  setupGenerateHandler(config, resolver);
5715
5341
  } else if (nuxt.options.build) {
5716
- await setupBuildHandler(config, resolver, getDetectedRenderers);
5342
+ await setupBuildHandler(config, resolver, getDetectedRenderers, getCompatibilityMeta);
5717
5343
  }
5718
5344
  if (nuxt.options.build)
5719
5345
  kit.addServerPlugin(resolve("./runtime/server/plugins/prerender"));
5720
- setupPrerenderHandler(config, resolver, getDetectedRenderers);
5346
+ setupPrerenderHandler(config, resolver, getDetectedRenderers, getCompatibilityMeta);
5721
5347
  if (nuxt.options.dev && hasWarnings()) {
5722
5348
  nuxt.hook("ready", () => {
5723
5349
  emitWarnings();