nuxt-og-image 6.10.3 → 7.0.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 (103) hide show
  1. package/README.md +10 -14
  2. package/dist/chunks/tw4.mjs +6 -6
  3. package/dist/chunks/uno.mjs +7 -7
  4. package/dist/cli.mjs +4 -4
  5. package/dist/devtools/components/og-image/RendererSelectModal.vue +1 -1
  6. package/dist/devtools/lib/og-image/fonts.ts +38 -0
  7. package/dist/devtools/lib/og-image/types.ts +7 -0
  8. package/dist/devtools/pages/og-image/index.vue +19 -14
  9. package/dist/module.d.mts +14 -10
  10. package/dist/module.d.ts +14 -10
  11. package/dist/module.json +2 -2
  12. package/dist/module.mjs +6 -6
  13. package/dist/runtime/app/client-utils.js +7 -4
  14. package/dist/runtime/app/components/Templates/Community/SaaS.satori.vue +1 -1
  15. package/dist/runtime/app/components/Templates/Community/SimpleBlog.satori.vue +1 -1
  16. package/dist/runtime/app/disabled/components.d.ts +1 -0
  17. package/dist/runtime/app/disabled/components.js +0 -0
  18. package/dist/runtime/app/disabled/composables/defineOgImage.d.ts +1 -0
  19. package/dist/runtime/app/disabled/composables/defineOgImage.js +1 -0
  20. package/dist/runtime/app/disabled/composables/defineOgImageComponent.d.ts +1 -0
  21. package/dist/runtime/app/disabled/composables/defineOgImageComponent.js +1 -0
  22. package/dist/runtime/app/disabled/composables/defineOgImageScreenshot.d.ts +1 -0
  23. package/dist/runtime/app/disabled/composables/defineOgImageScreenshot.js +1 -0
  24. package/dist/runtime/app/disabled/composables/mock.d.ts +1 -0
  25. package/dist/runtime/app/disabled/composables/mock.js +1 -0
  26. package/dist/runtime/app/disabled/index.d.ts +3 -0
  27. package/dist/runtime/app/disabled/index.js +3 -0
  28. package/dist/runtime/app/index.d.ts +4 -0
  29. package/dist/runtime/app/index.js +4 -0
  30. package/dist/runtime/app/utils/plugins.js +1 -1
  31. package/dist/runtime/app/utils.js +2 -2
  32. package/dist/runtime/server/index.d.ts +1 -0
  33. package/dist/runtime/server/index.js +1 -0
  34. package/dist/runtime/server/middleware/signing.d.ts +2 -0
  35. package/dist/runtime/server/middleware/signing.js +19 -0
  36. package/dist/runtime/server/nitro.d.ts +3 -3
  37. package/dist/runtime/server/nitro.js +4 -4
  38. package/dist/runtime/server/og-image/bindings/font-assets/dev-prerender.js +8 -49
  39. package/dist/runtime/server/og-image/bindings/font-assets/node.js +3 -2
  40. package/dist/runtime/server/og-image/bindings/satori/node.d.ts +1 -2
  41. package/dist/runtime/server/og-image/bindings/satori/wasm-fs.d.ts +1 -2
  42. package/dist/runtime/server/og-image/bindings/satori/wasm-fs.js +1 -1
  43. package/dist/runtime/server/og-image/bindings/satori/wasm.d.ts +1 -2
  44. package/dist/runtime/server/og-image/bindings/satori/wasm.js +1 -1
  45. package/dist/runtime/server/og-image/browser/screenshot.js +5 -3
  46. package/dist/runtime/server/og-image/cache/publish.d.ts +43 -0
  47. package/dist/runtime/server/og-image/cache/publish.js +91 -0
  48. package/dist/runtime/server/og-image/cache/published.d.ts +17 -0
  49. package/dist/runtime/server/og-image/cache/published.js +49 -0
  50. package/dist/runtime/server/og-image/context.js +1 -1
  51. package/dist/runtime/server/og-image/core/plugins/imageSrc.js +2 -5
  52. package/dist/runtime/server/og-image/core/transforms/emojis/index.d.ts +3 -2
  53. package/dist/runtime/server/og-image/core/vnodes.js +19 -1
  54. package/dist/runtime/server/og-image/font-source.d.ts +1 -0
  55. package/dist/runtime/server/og-image/font-source.js +1 -0
  56. package/dist/runtime/server/og-image/fonts.d.ts +1 -1
  57. package/dist/runtime/server/og-image/fonts.js +1 -1
  58. package/dist/runtime/server/og-image/satori/filters.d.ts +1 -0
  59. package/dist/runtime/server/og-image/satori/filters.js +14 -0
  60. package/dist/runtime/server/og-image/satori/instances.d.ts +1 -2
  61. package/dist/runtime/server/og-image/satori/plugins/classes.js +0 -7
  62. package/dist/runtime/server/og-image/satori/renderer.js +2 -1
  63. package/dist/runtime/server/og-image/satori/vnodes.js +1 -4
  64. package/dist/runtime/server/og-image/takumi/renderer.js +11 -47
  65. package/dist/runtime/server/og-image/utils/css.d.ts +0 -5
  66. package/dist/runtime/server/og-image/utils/css.js +0 -5
  67. package/dist/runtime/server/plugins/auto-eject-nitro3.d.ts +2 -0
  68. package/dist/runtime/server/plugins/auto-eject-nitro3.js +9 -0
  69. package/dist/runtime/server/plugins/auto-eject.js +3 -31
  70. package/dist/runtime/server/plugins/prerender.js +68 -2
  71. package/dist/runtime/server/plugins/publish.d.ts +2 -0
  72. package/dist/runtime/server/plugins/publish.js +44 -0
  73. package/dist/runtime/server/routes/debug.json.d.ts +10 -1
  74. package/dist/runtime/server/routes/debug.json.js +4 -4
  75. package/dist/runtime/server/routes/static-font.d.ts +2 -0
  76. package/dist/runtime/server/routes/static-font.js +22 -0
  77. package/dist/runtime/server/signing-secret.d.ts +8 -0
  78. package/dist/runtime/server/signing-secret.js +75 -0
  79. package/dist/runtime/server/util/auto-eject-lifecycle.d.ts +13 -0
  80. package/dist/runtime/server/util/auto-eject-lifecycle.js +31 -0
  81. package/dist/runtime/server/util/cache.d.ts +1 -0
  82. package/dist/runtime/server/util/cache.js +12 -6
  83. package/dist/runtime/server/util/cloudflare.d.ts +2 -2
  84. package/dist/runtime/server/util/eventHandlers.js +69 -3
  85. package/dist/runtime/server/util/fetchLocalAsset.js +2 -2
  86. package/dist/runtime/server/util/origin.d.ts +3 -0
  87. package/dist/runtime/server/util/origin.js +4 -0
  88. package/dist/runtime/server/util/publishWarnings.d.ts +6 -0
  89. package/dist/runtime/server/util/publishWarnings.js +6 -0
  90. package/dist/runtime/server/utils.d.ts +3 -3
  91. package/dist/runtime/server/utils.js +5 -10
  92. package/dist/runtime/shared/publish.d.ts +13 -0
  93. package/dist/runtime/shared/publish.js +17 -0
  94. package/dist/runtime/shared/urlEncoding.js +9 -4
  95. package/dist/runtime/types.d.ts +11 -16
  96. package/dist/shared/{nuxt-og-image.Bcegsloy.mjs → nuxt-og-image.DR_SxdDv.mjs} +1224 -1109
  97. package/package.json +60 -62
  98. package/skills/nuxt-og-image/SKILL.md +31 -14
  99. package/types/virtual.d.ts +8 -0
  100. package/dist/runtime/server/og-image/satori/plugins/emojis.d.ts +0 -2
  101. package/dist/runtime/server/og-image/satori/plugins/emojis.js +0 -112
  102. package/dist/runtime/server/og-image/satori/plugins/flex.d.ts +0 -2
  103. package/dist/runtime/server/og-image/satori/plugins/flex.js +0 -48
package/README.md CHANGED
@@ -1,19 +1,12 @@
1
1
  <img src=".github/assets/og-image-preview.png" alt="Nuxt OG Image DevTools Preview">
2
2
 
3
- <h1>nuxt-og-image</h1>
3
+ <h1><a href="https://nuxtseo.com"><img src=".github/assets/icon.svg" width="40" height="40" alt="Nuxt SEO" align="top"></a> nuxt-og-image</h1>
4
4
 
5
5
  [![npm version][npm-version-src]][npm-version-href]
6
6
  [![npm downloads][npm-downloads-src]][npm-downloads-href]
7
7
  [![License][license-src]][license-href]
8
8
  [![Nuxt][nuxt-src]][nuxt-href]
9
-
10
- <a href="https://skilld.dev/gh/nuxt-modules/og-image">
11
- <picture>
12
- <source media="(prefers-color-scheme: dark)" srcset="https://skilld.dev/b/nuxt-modules/og-image?theme=dark">
13
- <source media="(prefers-color-scheme: light)" srcset="https://skilld.dev/b/nuxt-modules/og-image?theme=light">
14
- <img alt="Skill repository on skilld.dev" src="https://skilld.dev/b/nuxt-modules/og-image?theme=light">
15
- </picture>
16
- </a>
9
+ [![Skill repository on skilld.dev][skilld-src]][skilld-href]
17
10
 
18
11
  Nuxt OG Image allows you to easily generate OG Images using either Vue components or screenshots of your pages.
19
12
 
@@ -46,7 +39,7 @@ need this module.
46
39
  Install `nuxt-og-image` dependency to your project:
47
40
 
48
41
  ```bash
49
- npx nuxi@latest module add og-image
42
+ npx nuxt module add og-image
50
43
  ```
51
44
 
52
45
  > [!TIP]
@@ -78,14 +71,17 @@ npx nuxi@latest module add og-image
78
71
  Licensed under the [MIT license](https://github.com/nuxt-modules/og-image/blob/main/LICENSE.md).
79
72
 
80
73
  <!-- Badges -->
81
- [npm-version-src]: https://img.shields.io/npm/v/nuxt-og-image/latest.svg?style=flat&colorA=18181B&colorB=28CF8D
74
+ [npm-version-src]: https://img.shields.io/npm/v/nuxt-og-image/latest.svg?style=flat&labelColor=16152b&color=00a63e
82
75
  [npm-version-href]: https://npmjs.com/package/nuxt-og-image
83
76
 
84
- [npm-downloads-src]: https://img.shields.io/npm/dm/nuxt-og-image.svg?style=flat&colorA=18181B&colorB=28CF8D
77
+ [npm-downloads-src]: https://img.shields.io/npm/dm/nuxt-og-image.svg?style=flat&labelColor=16152b&color=00a63e
85
78
  [npm-downloads-href]: https://npmjs.com/package/nuxt-og-image
86
79
 
87
- [license-src]: https://img.shields.io/github/license/nuxt-modules/og-image.svg?style=flat&colorA=18181B&colorB=28CF8D
80
+ [license-src]: https://img.shields.io/github/license/nuxt-modules/og-image.svg?style=flat&labelColor=16152b&color=00a63e
88
81
  [license-href]: https://github.com/nuxt-modules/og-image/blob/main/LICENSE.md
89
82
 
90
- [nuxt-src]: https://img.shields.io/badge/Nuxt-18181B?logo=nuxt
83
+ [nuxt-src]: https://img.shields.io/badge/Nuxt-16152b?logo=nuxt&style=flat
91
84
  [nuxt-href]: https://nuxt.com
85
+
86
+ [skilld-src]: https://skilld.dev/b/nuxt-modules/og-image?style=flat&labelColor=16152b&color=00a63e&logoColor=ffffff
87
+ [skilld-href]: https://skilld.dev/gh/nuxt-modules/og-image
@@ -2,25 +2,24 @@ import { readFile } from 'node:fs/promises';
2
2
  import { pathToFileURL } from 'node:url';
3
3
  import { resolveModulePath } from 'exsolve';
4
4
  import { dirname, join } from 'pathe';
5
- import { e as extractVariantBaseClasses, r as resolveVariantPrefixes, h as resolveExtractedVars, l as resolveVarsDeep, s as simplifyCss, p as postProcessStyles, m as loadLightningCss, a as extractCssVars, c as extractUniversalVars, d as extractPerClassVars, g as extractVarsFromCss, f as extractClassStyles } from '../shared/nuxt-og-image.Bcegsloy.mjs';
6
- import 'node:crypto';
5
+ import { e as extractVariantBaseClasses, r as resolveVariantPrefixes, h as resolveExtractedVars, l as resolveVarsDeep, s as simplifyCss, p as postProcessStyles, m as loadLightningCss, a as extractCssVars, c as extractUniversalVars, d as extractPerClassVars, g as extractVarsFromCss, f as extractClassStyles } from '../shared/nuxt-og-image.DR_SxdDv.mjs';
7
6
  import 'node:fs';
7
+ import 'node:module';
8
8
  import '@nuxt/kit';
9
9
  import 'defu';
10
10
  import 'fnv1a-64';
11
- import 'nuxt-site-config/kit';
12
11
  import 'nuxtseo-shared/kit';
13
- import 'pkg-types';
12
+ import 'node:crypto';
14
13
  import 'magic-string';
15
14
  import 'oxc-walker';
16
15
  import '../../dist/runtime/logger.js';
17
- import 'chrome-launcher';
18
16
  import 'std-env';
17
+ import '../../dist/runtime/server/og-image/utils/css.js';
19
18
  import 'nypm';
20
19
  import 'nuxtseo-shared/devtools';
21
20
  import 'unstorage';
22
21
  import 'unstorage/drivers/fs-lite';
23
- import '../../dist/runtime/server/og-image/utils/css.js';
22
+ import 'nuxt-site-config/kit';
24
23
  import 'strip-literal';
25
24
  import 'unplugin';
26
25
  import '@vue/compiler-sfc';
@@ -30,6 +29,7 @@ import '../../dist/runtime/server/og-image/core/transforms/emojis/emoji-utils.js
30
29
  import '../shared/nuxt-og-image.CJa2KCie.mjs';
31
30
  import 'magicast';
32
31
  import 'magicast/helpers';
32
+ import '../../dist/runtime/shared/publish.js';
33
33
 
34
34
  const RE_GRADIENT_STOP_PREFIX = /^(from|via|to)-/;
35
35
  const RE_COLOR_SHADE = /^(.+)-(\d+)$/;
@@ -1,27 +1,26 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import { defu } from 'defu';
3
3
  import { logger } from '../../dist/runtime/logger.js';
4
- import { e as extractVariantBaseClasses, s as simplifyCss, a as extractCssVars, b as extractPropertyInitialValues, c as extractUniversalVars, d as extractPerClassVars, f as extractClassStyles, p as postProcessStyles, r as resolveVariantPrefixes, i as importOptionalPeer, g as extractVarsFromCss, h as resolveExtractedVars } from '../shared/nuxt-og-image.Bcegsloy.mjs';
5
- export { j as importResolvedModule, k as resolveOptionalModulePath } from '../shared/nuxt-og-image.Bcegsloy.mjs';
6
- import 'node:crypto';
4
+ import { e as extractVariantBaseClasses, s as simplifyCss, a as extractCssVars, b as extractPropertyInitialValues, c as extractUniversalVars, d as extractPerClassVars, f as extractClassStyles, p as postProcessStyles, r as resolveVariantPrefixes, i as importOptionalPeer, g as extractVarsFromCss, h as resolveExtractedVars } from '../shared/nuxt-og-image.DR_SxdDv.mjs';
5
+ export { j as importResolvedModule, k as resolveOptionalModulePath } from '../shared/nuxt-og-image.DR_SxdDv.mjs';
7
6
  import 'node:fs';
7
+ import 'node:module';
8
8
  import 'node:url';
9
9
  import '@nuxt/kit';
10
10
  import 'fnv1a-64';
11
- import 'nuxt-site-config/kit';
12
11
  import 'nuxtseo-shared/kit';
13
12
  import 'pathe';
14
- import 'pkg-types';
13
+ import 'node:crypto';
15
14
  import 'magic-string';
16
15
  import 'oxc-walker';
17
16
  import 'exsolve';
18
- import 'chrome-launcher';
19
17
  import 'std-env';
18
+ import '../../dist/runtime/server/og-image/utils/css.js';
20
19
  import 'nypm';
21
20
  import 'nuxtseo-shared/devtools';
22
21
  import 'unstorage';
23
22
  import 'unstorage/drivers/fs-lite';
24
- import '../../dist/runtime/server/og-image/utils/css.js';
23
+ import 'nuxt-site-config/kit';
25
24
  import 'strip-literal';
26
25
  import 'unplugin';
27
26
  import '@vue/compiler-sfc';
@@ -31,6 +30,7 @@ import '../../dist/runtime/server/og-image/core/transforms/emojis/emoji-utils.js
31
30
  import '../shared/nuxt-og-image.CJa2KCie.mjs';
32
31
  import 'magicast';
33
32
  import 'magicast/helpers';
33
+ import '../../dist/runtime/shared/publish.js';
34
34
 
35
35
  const RE_SVG_DATA_URL = /url\("data:image\/svg\+xml[;,]([^"]+)"\)/;
36
36
  const RE_UTF8_PREFIX = /^utf8,/;
package/dist/cli.mjs CHANGED
@@ -646,11 +646,11 @@ async function installNuxtFonts() {
646
646
  spinner.start("Adding @nuxt/fonts module...");
647
647
  const { exec } = await import('tinyexec');
648
648
  try {
649
- await exec("npx", ["nuxi", "module", "add", "@nuxt/fonts"], { nodeOptions: { cwd } });
649
+ await exec("npx", ["nuxt", "module", "add", "@nuxt/fonts"], { nodeOptions: { cwd } });
650
650
  spinner.stop("@nuxt/fonts module added");
651
651
  } catch {
652
652
  spinner.stop("Failed to add @nuxt/fonts");
653
- p.log.warn("Run manually: npx nuxi module add @nuxt/fonts");
653
+ p.log.warn("Run manually: npx nuxt module add @nuxt/fonts");
654
654
  }
655
655
  }
656
656
  async function runMigrate(args2) {
@@ -858,7 +858,7 @@ async function runMigrate(args2) {
858
858
  if (!p.isCancel(addFonts) && addFonts) {
859
859
  await installNuxtFonts();
860
860
  } else {
861
- p.log.info("Skipped @nuxt/fonts installation. Add manually: npx nuxi module add @nuxt/fonts");
861
+ p.log.info("Skipped @nuxt/fonts installation. Add manually: npx nuxt module add @nuxt/fonts");
862
862
  }
863
863
  }
864
864
  }
@@ -1343,7 +1343,7 @@ function generateSecret() {
1343
1343
  p.log.step(`Secret: ${secret}`);
1344
1344
  p.log.message("");
1345
1345
  p.log.message("Set the environment variable:");
1346
- p.log.message(` NUXT_OG_IMAGE_SECRET=${secret}`);
1346
+ p.log.message(` NUXT_APP_SECRET=${secret}`);
1347
1347
  p.log.message("");
1348
1348
  p.log.message("The secret is automatically picked up via runtime config.");
1349
1349
  p.outro("");
@@ -109,7 +109,7 @@ const rendererConfigs = computed<RendererConfig[]>(() => {
109
109
  ...(hasSharp ? [fmt('jpg')] : []),
110
110
  ],
111
111
  disabledReason: !hasTakumi ? 'Module not installed' : null,
112
- installCommand: !hasTakumi ? 'npx nuxi module add nuxt-og-image-takumi' : null,
112
+ installCommand: !hasTakumi ? 'npx nuxt-og-image enable takumi' : null,
113
113
  },
114
114
  ]
115
115
  })
@@ -0,0 +1,38 @@
1
+ import { withBase } from 'ufo'
2
+
3
+ export interface FontPreviewFace {
4
+ family: string
5
+ weight: number
6
+ style: string
7
+ src: string
8
+ subset?: string
9
+ unicodeRange?: string
10
+ weightRange?: [number, number]
11
+ size?: number
12
+ }
13
+
14
+ export function getFontPreviewFaces(available: FontPreviewFace[], resolved: FontPreviewFace[]): FontPreviewFace[] {
15
+ const seen = new Set<string>()
16
+ return [...available, ...resolved].filter((face) => {
17
+ const key = JSON.stringify([face.family, face.weight, face.style, face.src, face.unicodeRange])
18
+ if (seen.has(key))
19
+ return false
20
+ seen.add(key)
21
+ return true
22
+ })
23
+ }
24
+
25
+ export function createFontPreviewCss(faces: FontPreviewFace[], origin: string): string {
26
+ const seen = new Set<string>()
27
+ const appUrl = origin ? new URL(origin) : undefined
28
+ return faces.flatMap((face) => {
29
+ const weight = face.weightRange?.join(' ') || String(face.weight)
30
+ const key = JSON.stringify([face.family, weight, face.style, face.src, face.unicodeRange])
31
+ if (seen.has(key))
32
+ return []
33
+ seen.add(key)
34
+ const path = appUrl && face.src.startsWith('/_og-static-fonts/') ? withBase(face.src, appUrl.pathname) : face.src
35
+ const src = appUrl ? new URL(path, appUrl).href : path
36
+ return [`@font-face { font-family: ${JSON.stringify(`ogp-${face.family}`)}; src: url(${JSON.stringify(src)}); font-weight: ${weight}; font-style: ${face.style};${face.unicodeRange ? ` unicode-range: ${face.unicodeRange};` : ''} font-display: swap; }`]
37
+ }).join('\n')
38
+ }
@@ -33,6 +33,13 @@ export interface GlobalDebugResponse {
33
33
  componentNames: OgImageComponent[]
34
34
  siteConfigUrl?: string
35
35
  compatibility?: RuntimeCompatibilitySchema
36
+ origin?: string
37
+ fontRequirements?: {
38
+ weights: number[]
39
+ styles: string[]
40
+ families: string[]
41
+ hasDynamicBindings: boolean
42
+ }
36
43
  resolvedFonts?: FontConfig[]
37
44
  availableFonts?: FontConfig[]
38
45
  }
@@ -1,11 +1,13 @@
1
1
  <script lang="ts" setup>
2
2
  import { useHead } from 'nuxt/app'
3
- import { hasProductionUrl, previewSource, productionUrl } from 'nuxtseo-layer-devtools/composables/state'
3
+ import { hasProductionUrl, host, previewSource, productionUrl } from 'nuxtseo-layer-devtools/composables/state'
4
4
  import { withHttps } from 'ufo'
5
5
  import { computed, reactive, ref, watch } from 'vue'
6
+ import { createFontPreviewCss, getFontPreviewFaces } from '../../lib/og-image/fonts'
6
7
  import { useOgImage } from '../../lib/og-image/og-image'
7
8
  import { RendererSelectDialogPromise } from '../../lib/og-image/renderer-select'
8
9
  import { isConnectionFailed, isFallbackMode } from '../../lib/og-image/rpc'
10
+ import { optionsOverrides } from '../../lib/og-image/util/logic'
9
11
 
10
12
  const RE_SINGLE_QUOTE = /'/g
11
13
  const RE_VUE_FILENAME = /[^/]+\.vue$/
@@ -226,7 +228,7 @@ const fontFiles = computed<FontFileEntry[]>(() => {
226
228
  const resolved = globalDebug.value?.resolvedFonts || []
227
229
  if (!available.length && !resolved.length)
228
230
  return []
229
- const source = available.length ? available : resolved
231
+ const source = getFontPreviewFaces(available, resolved)
230
232
  // Build set of resolved keys for loaded check
231
233
  const resolvedKeys = new Set(resolved.map((f: any) => `${f.family}-${f.weight}-${f.style}`))
232
234
  // Count subsets per family+weight+style and collect subset names
@@ -234,7 +236,7 @@ const fontFiles = computed<FontFileEntry[]>(() => {
234
236
  for (const f of source) {
235
237
  const key = `${f.family}-${f.weight}-${f.style}`
236
238
  const subs = subsetMap.get(key) || []
237
- if (f.subset)
239
+ if (f.subset && !subs.includes(f.subset))
238
240
  subs.push(f.subset)
239
241
  subsetMap.set(key, subs)
240
242
  }
@@ -268,9 +270,13 @@ const fontFiles = computed<FontFileEntry[]>(() => {
268
270
  const fontFamilySizes = computed(() => {
269
271
  const resolved = globalDebug.value?.resolvedFonts || []
270
272
  const sizes = new Map<string, number>()
273
+ const seen = new Set<string>()
271
274
  for (const f of resolved) {
272
- if (f.size)
275
+ const key = JSON.stringify([f.family, f.src])
276
+ if (f.size && !seen.has(key)) {
277
+ seen.add(key)
273
278
  sizes.set(f.family, (sizes.get(f.family) || 0) + f.size)
279
+ }
274
280
  }
275
281
  return sizes
276
282
  })
@@ -296,15 +302,8 @@ const unresolvedFamilies = computed(() => {
296
302
  })
297
303
 
298
304
  const fontFaceCss = computed(() => {
299
- const rules: string[] = []
300
- const seen = new Set<string>()
301
- for (const f of fontFiles.value) {
302
- if (seen.has(f.family))
303
- continue
304
- seen.add(f.family)
305
- rules.push(`@font-face { font-family: 'ogp-${f.family}'; src: url('${f.src}'); font-display: swap; }`)
306
- }
307
- return rules.join('\n')
305
+ const faces = getFontPreviewFaces(globalDebug.value?.availableFonts || [], globalDebug.value?.resolvedFonts || [])
306
+ return createFontPreviewCss(faces, host.value || globalDebug.value?.origin || '')
308
307
  })
309
308
 
310
309
  useHead({ style: computed(() => fontFaceCss.value ? [fontFaceCss.value] : []) })
@@ -315,7 +314,13 @@ function applyFontOverride(family: string) {
315
314
  const next = fontOverride.value === family ? '' : family
316
315
  fontOverride.value = next
317
316
  hasMadeChanges.value = true
318
- updateProps({ fontFamily: next || undefined })
317
+ const props = { ...optionsOverrides.value.props }
318
+ if (next)
319
+ props.fontFamily = next
320
+ else
321
+ delete props.fontFamily
322
+ optionsOverrides.value = { ...optionsOverrides.value, props }
323
+ refreshSources()
319
324
  }
320
325
 
321
326
  const metaOverrides = reactive({
package/dist/module.d.mts CHANGED
@@ -46,7 +46,7 @@ interface ModuleOptions {
46
46
  debug: boolean;
47
47
  /**
48
48
  * Configure the runtime cache storage for generated OG images.
49
- * - `true` - Use Nitro's default cache storage (default)
49
+ * - `true` - Use configured Nitro cache storage, or a 64 MiB bounded memory cache (default)
50
50
  * - `false` - Disable caching
51
51
  * - `string` - Use a custom storage mount key (e.g., `'redis'`). You must mount the storage yourself via a Nitro plugin.
52
52
  * - `object` - Provide a driver config that the module will mount for you (build-time only)
@@ -96,6 +96,14 @@ interface ModuleOptions {
96
96
  buildCache?: boolean | {
97
97
  base?: string;
98
98
  };
99
+ /**
100
+ * Publish rendered images to a mounted Nitro storage driver.
101
+ * Image objects have no TTL. cacheMaxAgeSeconds limits manifest reuse.
102
+ */
103
+ publish?: {
104
+ storage: string;
105
+ baseURL: string;
106
+ };
99
107
  /**
100
108
  * Warn about OG Image components missing renderer suffix in dev mode.
101
109
  * Set to false to suppress warnings for legacy/test components.
@@ -192,21 +200,17 @@ interface ModuleOptions {
192
200
  */
193
201
  restrictRuntimeImagesToOrigin?: boolean | string[];
194
202
  /**
195
- * Secret for URL signing. When set, all runtime OG image URLs include a
203
+ * Disable URL signing with false. Otherwise, all runtime OG image URLs include a
196
204
  * keyed hash signature and the handler rejects requests with missing or
197
205
  * invalid signatures.
198
206
  *
199
- * Leave unset to auto-generate a per-build secret (signing on by default).
200
- * Set an explicit, stable string for rolling or multi-instance deploys, or
201
- * `false` to disable signing entirely. Generate one with:
202
- * `npx nuxt-og-image generate-secret`
203
- *
204
- * Required (explicitly) when `strict` is enabled.
207
+ * Leave unset to derive a secret from Nuxt runtimeConfig.appSecret.
208
+ * Set NUXT_APP_SECRET in production. Use false to disable signing.
205
209
  */
206
- secret?: string | false;
210
+ secret?: false;
207
211
  /**
208
212
  * Enable strict security mode. When enabled:
209
- * - `secret` is required (URL signing)
213
+ * - URL signing is required through Nuxt appSecret
210
214
  * - The `html` option is disabled (prevents SSRF via inline HTML injection)
211
215
  * - `maxQueryParamSize` defaults to `2048`
212
216
  * - `restrictRuntimeImagesToOrigin` defaults to `true`
package/dist/module.d.ts CHANGED
@@ -46,7 +46,7 @@ interface ModuleOptions {
46
46
  debug: boolean;
47
47
  /**
48
48
  * Configure the runtime cache storage for generated OG images.
49
- * - `true` - Use Nitro's default cache storage (default)
49
+ * - `true` - Use configured Nitro cache storage, or a 64 MiB bounded memory cache (default)
50
50
  * - `false` - Disable caching
51
51
  * - `string` - Use a custom storage mount key (e.g., `'redis'`). You must mount the storage yourself via a Nitro plugin.
52
52
  * - `object` - Provide a driver config that the module will mount for you (build-time only)
@@ -96,6 +96,14 @@ interface ModuleOptions {
96
96
  buildCache?: boolean | {
97
97
  base?: string;
98
98
  };
99
+ /**
100
+ * Publish rendered images to a mounted Nitro storage driver.
101
+ * Image objects have no TTL. cacheMaxAgeSeconds limits manifest reuse.
102
+ */
103
+ publish?: {
104
+ storage: string;
105
+ baseURL: string;
106
+ };
99
107
  /**
100
108
  * Warn about OG Image components missing renderer suffix in dev mode.
101
109
  * Set to false to suppress warnings for legacy/test components.
@@ -192,21 +200,17 @@ interface ModuleOptions {
192
200
  */
193
201
  restrictRuntimeImagesToOrigin?: boolean | string[];
194
202
  /**
195
- * Secret for URL signing. When set, all runtime OG image URLs include a
203
+ * Disable URL signing with false. Otherwise, all runtime OG image URLs include a
196
204
  * keyed hash signature and the handler rejects requests with missing or
197
205
  * invalid signatures.
198
206
  *
199
- * Leave unset to auto-generate a per-build secret (signing on by default).
200
- * Set an explicit, stable string for rolling or multi-instance deploys, or
201
- * `false` to disable signing entirely. Generate one with:
202
- * `npx nuxt-og-image generate-secret`
203
- *
204
- * Required (explicitly) when `strict` is enabled.
207
+ * Leave unset to derive a secret from Nuxt runtimeConfig.appSecret.
208
+ * Set NUXT_APP_SECRET in production. Use false to disable signing.
205
209
  */
206
- secret?: string | false;
210
+ secret?: false;
207
211
  /**
208
212
  * Enable strict security mode. When enabled:
209
- * - `secret` is required (URL signing)
213
+ * - URL signing is required through Nuxt appSecret
210
214
  * - The `html` option is disabled (prevents SSRF via inline HTML injection)
211
215
  * - `maxQueryParamSize` defaults to `2048`
212
216
  * - `restrictRuntimeImagesToOrigin` defaults to `true`
package/dist/module.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "nuxt-og-image",
3
3
  "compatibility": {
4
- "nuxt": ">=3.16.0"
4
+ "nuxt": "^4.6.1 || ^5.0.0-0"
5
5
  },
6
6
  "configKey": "ogImage",
7
- "version": "6.10.3",
7
+ "version": "7.0.0",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.3",
10
10
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -1,26 +1,26 @@
1
- import 'node:crypto';
2
1
  import 'node:fs';
3
2
  import 'node:fs/promises';
3
+ import 'node:module';
4
4
  import 'node:url';
5
5
  import '@nuxt/kit';
6
6
  import 'defu';
7
7
  import 'fnv1a-64';
8
- import 'nuxt-site-config/kit';
9
8
  import 'nuxtseo-shared/kit';
10
9
  import 'pathe';
11
- import 'pkg-types';
12
- export { n as default } from './shared/nuxt-og-image.Bcegsloy.mjs';
10
+ export { n as default } from './shared/nuxt-og-image.DR_SxdDv.mjs';
13
11
  import '../dist/runtime/logger.js';
12
+ import '../dist/runtime/shared/publish.js';
13
+ import 'node:crypto';
14
14
  import 'magic-string';
15
15
  import 'oxc-walker';
16
16
  import 'exsolve';
17
- import 'chrome-launcher';
18
17
  import 'std-env';
18
+ import '../dist/runtime/server/og-image/utils/css.js';
19
19
  import 'nypm';
20
20
  import 'nuxtseo-shared/devtools';
21
21
  import 'unstorage';
22
22
  import 'unstorage/drivers/fs-lite';
23
- import '../dist/runtime/server/og-image/utils/css.js';
23
+ import 'nuxt-site-config/kit';
24
24
  import 'strip-literal';
25
25
  import 'unplugin';
26
26
  import '@vue/compiler-sfc';
@@ -2,8 +2,9 @@ import { defu } from "defu";
2
2
  import { injectHead, useHead, useRuntimeConfig } from "nuxt/app";
3
3
  import { joinURL, withQuery } from "ufo";
4
4
  import { toValue } from "vue";
5
+ import { hasServerRuntime } from "#build/nuxt-og-image/client-config.mjs";
5
6
  import { componentNames } from "#build/nuxt-og-image/components.mjs";
6
- import { createSitePathResolver } from "#site-config/app/composables/utils";
7
+ import { createSitePathResolver } from "#site-config/app";
7
8
  import { buildOgImageUrl, generateMeta, separateProps } from "../shared.js";
8
9
  const clientEntriesByHead = /* @__PURE__ */ new WeakMap();
9
10
  function registerClientOgHead(ogKey, input, options) {
@@ -86,7 +87,7 @@ export function clientProcessOgImageOptions(input, route, basePath) {
86
87
  paths.push(url);
87
88
  continue;
88
89
  }
89
- if (publicCfg.hasServerRuntime) {
90
+ if (hasServerRuntime) {
90
91
  const finalUrl2 = buildResolverUrl(baseURL, basePath, ogKey, route.query);
91
92
  registerClientOgHead(ogKey, { meta: generateMeta(toValue(resolveImageUrl(finalUrl2)), validOptions, metaOptions) }, { tagPriority: "high" });
92
93
  paths.push(finalUrl2);
@@ -101,8 +102,10 @@ export function clientProcessOgImageOptions(input, route, basePath) {
101
102
  urlOpts._componentHash = component.hash;
102
103
  const result = buildOgImageUrl(urlOpts, extension, true, defaults, void 0);
103
104
  const resolvedUrl = joinURL("/", baseURL, result.url);
104
- const finalUrl = opts._query && Object.keys(opts._query).length ? withQuery(resolvedUrl, { _query: opts._query }) : resolvedUrl;
105
- registerClientOgHead(ogKey, { meta: generateMeta(toValue(resolveImageUrl(finalUrl)), opts, metaOptions) }, { processTemplateParams: true, tagPriority: "high" });
105
+ const query = opts._query && Object.keys(opts._query).length ? { _query: opts._query } : {};
106
+ const finalUrl = withQuery(resolvedUrl, query);
107
+ const imageUrl = rc.app.cdnURL ? withQuery(joinURL(rc.app.cdnURL, result.url), query) : toValue(resolveImageUrl(finalUrl));
108
+ registerClientOgHead(ogKey, { meta: generateMeta(imageUrl, opts, metaOptions) }, { processTemplateParams: true, tagPriority: "high" });
106
109
  paths.push(finalUrl);
107
110
  }
108
111
  return paths;
@@ -1,5 +1,5 @@
1
1
  <script setup>
2
- import { useSiteConfig } from "#site-config/app/composables";
2
+ import { useSiteConfig } from "#site-config/app";
3
3
  const props = defineProps({
4
4
  colorMode: { type: String, required: false, default: "light" },
5
5
  title: { type: String, required: false },
@@ -1,7 +1,7 @@
1
1
  <script setup>
2
2
  import { parseURL } from "ufo";
3
3
  import { computed } from "vue";
4
- import { useSiteConfig } from "#site-config/app/composables";
4
+ import { useSiteConfig } from "#site-config/app";
5
5
  const props = defineProps({
6
6
  colorMode: { type: String, required: false, default: "light" },
7
7
  title: { type: String, required: false, default: "title" },
@@ -0,0 +1 @@
1
+ export type OgImageComponents = Record<string, Record<string, unknown>>;
File without changes
@@ -0,0 +1 @@
1
+ export { defineOgImage } from '../../composables/mock.js';
@@ -0,0 +1 @@
1
+ export { defineOgImage } from "../../composables/mock.js";
@@ -0,0 +1 @@
1
+ export { defineOgImageComponent } from '../../composables/mock.js';
@@ -0,0 +1 @@
1
+ export { defineOgImageComponent } from "../../composables/mock.js";
@@ -0,0 +1 @@
1
+ export { defineOgImageScreenshot } from '../../composables/mock.js';
@@ -0,0 +1 @@
1
+ export { defineOgImageScreenshot } from "../../composables/mock.js";
@@ -0,0 +1 @@
1
+ export { defineOgImage, defineOgImageComponent, defineOgImageScreenshot } from '../../composables/mock.js';
@@ -0,0 +1 @@
1
+ export { defineOgImage, defineOgImageComponent, defineOgImageScreenshot } from "../../composables/mock.js";
@@ -0,0 +1,3 @@
1
+ export { defineOgImage } from './composables/defineOgImage.js';
2
+ export { defineOgImageComponent } from './composables/defineOgImageComponent.js';
3
+ export { defineOgImageScreenshot } from './composables/defineOgImageScreenshot.js';
@@ -0,0 +1,3 @@
1
+ export { defineOgImage } from "./composables/defineOgImage.js";
2
+ export { defineOgImageComponent } from "./composables/defineOgImageComponent.js";
3
+ export { defineOgImageScreenshot } from "./composables/defineOgImageScreenshot.js";
@@ -0,0 +1,4 @@
1
+ export { defineOgImage } from './composables/defineOgImage.js';
2
+ export { defineOgImageComponent } from './composables/defineOgImageComponent.js';
3
+ export { defineOgImageScreenshot } from './composables/defineOgImageScreenshot.js';
4
+ export { getOgImagePath } from './utils.js';
@@ -0,0 +1,4 @@
1
+ export { defineOgImage } from "./composables/defineOgImage.js";
2
+ export { defineOgImageComponent } from "./composables/defineOgImageComponent.js";
3
+ export { defineOgImageScreenshot } from "./composables/defineOgImageScreenshot.js";
4
+ export { getOgImagePath } from "./utils.js";
@@ -4,7 +4,7 @@ import { createNitroRouteRuleMatcher } from "nuxtseo-shared/server";
4
4
  import { parseURL } from "ufo";
5
5
  import { toValue } from "vue";
6
6
  import { defineNuxtPlugin, useRequestEvent } from "#app";
7
- import { withSiteUrl } from "#site-config/app/composables";
7
+ import { withSiteUrl } from "#site-config/app";
8
8
  import { createOgImageMeta, getOgImagePath } from "../../app/utils.js";
9
9
  import { isInternalRoute } from "../../shared.js";
10
10
  const RE_COMMA = /,/g;
@@ -4,7 +4,7 @@ import { useHead, useRequestEvent, useRuntimeConfig } from "nuxt/app";
4
4
  import { joinURL, withQuery } from "ufo";
5
5
  import { isRef, toValue } from "vue";
6
6
  import { componentNames } from "#build/nuxt-og-image/components.mjs";
7
- import { createSitePathResolver, withSiteUrl } from "#site-config/app/composables/utils";
7
+ import { createSitePathResolver, withSiteUrl } from "#site-config/app";
8
8
  import { buildOgImageUrl, generateMeta, separateProps } from "../shared.js";
9
9
  function resolveUnrefHeadInput(input) {
10
10
  if (input == null)
@@ -242,7 +242,7 @@ export function useOgImageRuntimeConfig() {
242
242
  const serverCfg = c["nuxt-og-image"] || {};
243
243
  const publicCfg = c.public?.["nuxt-og-image"] || {};
244
244
  const merged = { defaults: {}, ...publicCfg, ...serverCfg };
245
- const overrideSecret = c.ogImage?.secret;
245
+ const overrideSecret = event?.context._ogImageSigningSecret;
246
246
  if (overrideSecret)
247
247
  merged.security = { ...merged.security || {}, secret: overrideSecret };
248
248
  merged.app = { baseURL: c.app.baseURL };
@@ -0,0 +1 @@
1
+ export { getOgImageUrl } from './nitro.js';
@@ -0,0 +1 @@
1
+ export { getOgImageUrl } from "./nitro.js";
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,19 @@
1
+ import { createError, useRuntimeConfig } from "nuxt/server";
2
+ import { joinURL } from "ufo";
3
+ import { defineEventHandler, getRequestURL } from "#nuxtseo/h3";
4
+ import { rendersImages } from "#og-image-virtual/signing.mjs";
5
+ import { initializeOgImageSigning, isOgImageEndpointPath, reportSigningFailure } from "../signing-secret.js";
6
+ export default defineEventHandler(async (event) => {
7
+ if (!rendersImages)
8
+ return;
9
+ const baseURL = useRuntimeConfig().app.baseURL;
10
+ const pathname = getRequestURL(event).pathname;
11
+ if (pathname === "/__nuxt_error" || pathname === joinURL(baseURL, "__nuxt_error"))
12
+ return;
13
+ const failure = await initializeOgImageSigning(event).then(() => void 0, (error) => error);
14
+ if (failure === void 0)
15
+ return;
16
+ reportSigningFailure(failure);
17
+ if (import.meta.prerender || isOgImageEndpointPath(pathname, baseURL))
18
+ throw createError({ status: 500, message: "[nuxt-og-image] URL signing needs NUXT_APP_SECRET with at least 32 characters.", cause: failure });
19
+ });