nuxt-og-image 6.7.5 → 6.7.6

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 (61) hide show
  1. package/dist/chunks/tw4.cjs +2 -2
  2. package/dist/chunks/tw4.mjs +2 -2
  3. package/dist/chunks/uno.cjs +20 -5
  4. package/dist/chunks/uno.mjs +19 -5
  5. package/dist/module.cjs +2 -2
  6. package/dist/module.json +1 -1
  7. package/dist/module.mjs +2 -2
  8. package/dist/runtime/app/utils/plugins.js +4 -10
  9. package/dist/runtime/server/og-image/bindings/browser/chrome-launcher.d.ts +1 -1
  10. package/dist/runtime/server/og-image/bindings/browser/cloudflare.d.ts +1 -1
  11. package/dist/runtime/server/og-image/bindings/browser/on-demand.d.ts +1 -1
  12. package/dist/runtime/server/og-image/bindings/browser/playwright.d.ts +1 -1
  13. package/dist/runtime/server/og-image/bindings/font-assets/cloudflare.d.ts +1 -1
  14. package/dist/runtime/server/og-image/bindings/font-assets/cloudflare.js +1 -1
  15. package/dist/runtime/server/og-image/bindings/font-assets/dev-prerender.d.ts +1 -1
  16. package/dist/runtime/server/og-image/bindings/font-assets/dev-prerender.js +2 -2
  17. package/dist/runtime/server/og-image/bindings/font-assets/node.d.ts +1 -1
  18. package/dist/runtime/server/og-image/bindings/font-assets/node.js +1 -1
  19. package/dist/runtime/server/og-image/bindings/resvg/node-dev.js +18 -64
  20. package/dist/runtime/server/og-image/bindings/takumi/node-dev.js +26 -83
  21. package/dist/runtime/server/og-image/bindings/worker-queue.d.ts +34 -0
  22. package/dist/runtime/server/og-image/bindings/worker-queue.js +150 -0
  23. package/dist/runtime/server/og-image/browser/renderer.js +1 -1
  24. package/dist/runtime/server/og-image/context.d.ts +1 -1
  25. package/dist/runtime/server/og-image/context.js +2 -3
  26. package/dist/runtime/server/og-image/core/plugins/imageSrc.js +1 -1
  27. package/dist/runtime/server/og-image/core/transforms/emojis/local.d.ts +2 -17
  28. package/dist/runtime/server/og-image/core/transforms/emojis/local.js +1 -1
  29. package/dist/runtime/server/og-image/devtools.d.ts +1 -1
  30. package/dist/runtime/server/og-image/devtools.js +1 -1
  31. package/dist/runtime/server/og-image/fonts.d.ts +1 -1
  32. package/dist/runtime/server/og-image/templates/html.js +1 -1
  33. package/dist/runtime/server/plugins/prerender.d.ts +1 -1
  34. package/dist/runtime/server/plugins/prerender.js +2 -2
  35. package/dist/runtime/server/routes/__zero-runtime/image.d.ts +1 -1
  36. package/dist/runtime/server/routes/__zero-runtime/image.js +1 -1
  37. package/dist/runtime/server/routes/debug.json.d.ts +1 -10
  38. package/dist/runtime/server/routes/debug.json.js +1 -1
  39. package/dist/runtime/server/routes/image.d.ts +1 -1
  40. package/dist/runtime/server/routes/image.js +1 -1
  41. package/dist/runtime/server/routes/resolve.d.ts +1 -1
  42. package/dist/runtime/server/routes/resolve.js +1 -1
  43. package/dist/runtime/server/util/cache.d.ts +1 -6
  44. package/dist/runtime/server/util/cache.js +2 -2
  45. package/dist/runtime/server/util/cloudflare.d.ts +1 -1
  46. package/dist/runtime/server/util/cloudflareAssets.d.ts +1 -1
  47. package/dist/runtime/server/util/cloudflareAssets.js +1 -1
  48. package/dist/runtime/server/util/eventHandlers.d.ts +1 -1
  49. package/dist/runtime/server/util/eventHandlers.js +1 -1
  50. package/dist/runtime/server/util/fetchLocalAsset.d.ts +1 -1
  51. package/dist/runtime/server/util/kit.d.ts +7 -3
  52. package/dist/runtime/server/util/kit.js +4 -18
  53. package/dist/runtime/server/util/options.js +1 -1
  54. package/dist/runtime/server/util/query.d.ts +1 -1
  55. package/dist/runtime/server/utils.d.ts +1 -1
  56. package/dist/runtime/server/utils.js +1 -1
  57. package/dist/runtime/types.d.ts +11 -9
  58. package/dist/shared/{nuxt-og-image.D3mV8OQ9.mjs → nuxt-og-image.BIbfn5xQ.mjs} +111 -83
  59. package/dist/shared/{nuxt-og-image.Cctpuv4O.cjs → nuxt-og-image.CdpC3njh.cjs} +113 -82
  60. package/package.json +38 -27
  61. package/types/virtual.d.ts +15 -0
@@ -4,17 +4,17 @@ const promises = require('node:fs/promises');
4
4
  const node_url = require('node:url');
5
5
  const exsolve = require('exsolve');
6
6
  const pathe = require('pathe');
7
- const module$1 = require('../shared/nuxt-og-image.Cctpuv4O.cjs');
7
+ const module$1 = require('../shared/nuxt-og-image.CdpC3njh.cjs');
8
8
  require('node:crypto');
9
9
  require('node:fs');
10
10
  require('@nuxt/kit');
11
11
  require('defu');
12
12
  require('fnv1a-64');
13
13
  require('nuxt-site-config/kit');
14
+ require('nuxtseo-shared/kit');
14
15
  require('pkg-types');
15
16
  require('std-env');
16
17
  require('oxc-walker');
17
- require('nuxtseo-shared/kit');
18
18
  require('../../dist/runtime/logger.js');
19
19
  require('chrome-launcher');
20
20
  require('nypm');
@@ -2,17 +2,17 @@ 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, i as resolveVarsDeep, s as simplifyCss, p as postProcessStyles, l as loadLightningCss, a as extractCssVars, c as extractUniversalVars, d as extractPerClassVars, g as extractVarsFromCss, f as extractClassStyles } from '../shared/nuxt-og-image.D3mV8OQ9.mjs';
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.BIbfn5xQ.mjs';
6
6
  import 'node:crypto';
7
7
  import 'node:fs';
8
8
  import '@nuxt/kit';
9
9
  import 'defu';
10
10
  import 'fnv1a-64';
11
11
  import 'nuxt-site-config/kit';
12
+ import 'nuxtseo-shared/kit';
12
13
  import 'pkg-types';
13
14
  import 'std-env';
14
15
  import 'oxc-walker';
15
- import 'nuxtseo-shared/kit';
16
16
  import '../../dist/runtime/logger.js';
17
17
  import 'chrome-launcher';
18
18
  import 'nypm';
@@ -3,18 +3,18 @@
3
3
  const promises = require('node:fs/promises');
4
4
  const defu = require('defu');
5
5
  const logger_js = require('../../dist/runtime/logger.js');
6
- const module$1 = require('../shared/nuxt-og-image.Cctpuv4O.cjs');
6
+ const module$1 = require('../shared/nuxt-og-image.CdpC3njh.cjs');
7
7
  require('node:crypto');
8
8
  require('node:fs');
9
9
  require('node:url');
10
10
  require('@nuxt/kit');
11
11
  require('fnv1a-64');
12
12
  require('nuxt-site-config/kit');
13
+ require('nuxtseo-shared/kit');
13
14
  require('pathe');
14
15
  require('pkg-types');
15
16
  require('std-env');
16
17
  require('oxc-walker');
17
- require('nuxtseo-shared/kit');
18
18
  require('chrome-launcher');
19
19
  require('nypm');
20
20
  require('nuxtseo-shared/devtools');
@@ -42,6 +42,8 @@ const RE_HEIGHT_ATTR = /\bheight=['"](\d+)['"]/;
42
42
  const RE_SVG_BODY = /<svg[^>]*>([\s\S]*)<\/svg>/;
43
43
  let unoConfig = null;
44
44
  let rootDir = null;
45
+ let unocssCorePath;
46
+ let unocssConfigPath;
45
47
  let generator = null;
46
48
  let pendingGenerator = null;
47
49
  let cachedExtracted = null;
@@ -60,6 +62,15 @@ function clearUnoCache() {
60
62
  pendingGenerator = null;
61
63
  cachedExtracted = null;
62
64
  }
65
+ function setUnoModulePaths(options) {
66
+ const nextCorePath = options?.unocssCorePath;
67
+ const nextConfigPath = options?.unocssConfigPath;
68
+ if (nextCorePath === unocssCorePath && nextConfigPath === unocssConfigPath)
69
+ return;
70
+ unocssCorePath = nextCorePath;
71
+ unocssConfigPath = nextConfigPath;
72
+ clearUnoCache();
73
+ }
63
74
  async function getGenerator() {
64
75
  if (generator)
65
76
  return generator;
@@ -68,17 +79,18 @@ async function getGenerator() {
68
79
  pendingGenerator = (async () => {
69
80
  let loadedConfig;
70
81
  try {
71
- const { loadConfig } = await import('@unocss/config');
82
+ const { loadConfig } = await module$1.importOptionalPeer("@unocss/config", unocssConfigPath, "UnoCSS class extraction with OG images");
72
83
  const result = await loadConfig(rootDir || process.cwd());
73
84
  loadedConfig = result.config;
74
85
  } catch (e) {
75
- logger_js.logger.warn("[og-image UnoCSS] Failed to load uno.config.ts:", e.message);
86
+ if (!unoConfig)
87
+ logger_js.logger.warn("[og-image UnoCSS] Failed to load uno.config.ts:", e.message);
76
88
  }
77
89
  const finalConfig = loadedConfig ? defu.defu(unoConfig || {}, loadedConfig) : unoConfig;
78
90
  if (!finalConfig) {
79
91
  throw new Error("UnoCSS config not found. Ensure uno.config.ts exists or @unocss/nuxt is loaded.");
80
92
  }
81
- const { createGenerator } = await import('@unocss/core');
93
+ const { createGenerator } = await module$1.importOptionalPeer("@unocss/core", unocssCorePath, "UnoCSS class extraction with OG images");
82
94
  generator = await createGenerator(finalConfig);
83
95
  return generator;
84
96
  })();
@@ -116,6 +128,7 @@ function resolveUserVars(extracted, rootAttrs) {
116
128
  return module$1.resolveExtractedVars(extracted, rootAttrs);
117
129
  }
118
130
  function createUnoProvider(options) {
131
+ setUnoModulePaths(options);
119
132
  return {
120
133
  name: "unocss",
121
134
  async resolveClassesToStyles(classes, context, rootAttrs) {
@@ -227,6 +240,8 @@ function createUnoProvider(options) {
227
240
  };
228
241
  }
229
242
 
243
+ exports.importResolvedModule = module$1.importResolvedModule;
244
+ exports.resolveOptionalModulePath = module$1.resolveOptionalModulePath;
230
245
  exports.clearUnoCache = clearUnoCache;
231
246
  exports.clearUnoUserVarsCache = clearUnoUserVarsCache;
232
247
  exports.createUnoProvider = createUnoProvider;
@@ -1,18 +1,19 @@
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, g as extractVarsFromCss, h as resolveExtractedVars } from '../shared/nuxt-og-image.D3mV8OQ9.mjs';
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.BIbfn5xQ.mjs';
5
+ export { j as importResolvedModule, k as resolveOptionalModulePath } from '../shared/nuxt-og-image.BIbfn5xQ.mjs';
5
6
  import 'node:crypto';
6
7
  import 'node:fs';
7
8
  import 'node:url';
8
9
  import '@nuxt/kit';
9
10
  import 'fnv1a-64';
10
11
  import 'nuxt-site-config/kit';
12
+ import 'nuxtseo-shared/kit';
11
13
  import 'pathe';
12
14
  import 'pkg-types';
13
15
  import 'std-env';
14
16
  import 'oxc-walker';
15
- import 'nuxtseo-shared/kit';
16
17
  import 'chrome-launcher';
17
18
  import 'nypm';
18
19
  import 'nuxtseo-shared/devtools';
@@ -40,6 +41,8 @@ const RE_HEIGHT_ATTR = /\bheight=['"](\d+)['"]/;
40
41
  const RE_SVG_BODY = /<svg[^>]*>([\s\S]*)<\/svg>/;
41
42
  let unoConfig = null;
42
43
  let rootDir = null;
44
+ let unocssCorePath;
45
+ let unocssConfigPath;
43
46
  let generator = null;
44
47
  let pendingGenerator = null;
45
48
  let cachedExtracted = null;
@@ -58,6 +61,15 @@ function clearUnoCache() {
58
61
  pendingGenerator = null;
59
62
  cachedExtracted = null;
60
63
  }
64
+ function setUnoModulePaths(options) {
65
+ const nextCorePath = options?.unocssCorePath;
66
+ const nextConfigPath = options?.unocssConfigPath;
67
+ if (nextCorePath === unocssCorePath && nextConfigPath === unocssConfigPath)
68
+ return;
69
+ unocssCorePath = nextCorePath;
70
+ unocssConfigPath = nextConfigPath;
71
+ clearUnoCache();
72
+ }
61
73
  async function getGenerator() {
62
74
  if (generator)
63
75
  return generator;
@@ -66,17 +78,18 @@ async function getGenerator() {
66
78
  pendingGenerator = (async () => {
67
79
  let loadedConfig;
68
80
  try {
69
- const { loadConfig } = await import('@unocss/config');
81
+ const { loadConfig } = await importOptionalPeer("@unocss/config", unocssConfigPath, "UnoCSS class extraction with OG images");
70
82
  const result = await loadConfig(rootDir || process.cwd());
71
83
  loadedConfig = result.config;
72
84
  } catch (e) {
73
- logger.warn("[og-image UnoCSS] Failed to load uno.config.ts:", e.message);
85
+ if (!unoConfig)
86
+ logger.warn("[og-image UnoCSS] Failed to load uno.config.ts:", e.message);
74
87
  }
75
88
  const finalConfig = loadedConfig ? defu(unoConfig || {}, loadedConfig) : unoConfig;
76
89
  if (!finalConfig) {
77
90
  throw new Error("UnoCSS config not found. Ensure uno.config.ts exists or @unocss/nuxt is loaded.");
78
91
  }
79
- const { createGenerator } = await import('@unocss/core');
92
+ const { createGenerator } = await importOptionalPeer("@unocss/core", unocssCorePath, "UnoCSS class extraction with OG images");
80
93
  generator = await createGenerator(finalConfig);
81
94
  return generator;
82
95
  })();
@@ -114,6 +127,7 @@ function resolveUserVars(extracted, rootAttrs) {
114
127
  return resolveExtractedVars(extracted, rootAttrs);
115
128
  }
116
129
  function createUnoProvider(options) {
130
+ setUnoModulePaths(options);
117
131
  return {
118
132
  name: "unocss",
119
133
  async resolveClassesToStyles(classes, context, rootAttrs) {
package/dist/module.cjs CHANGED
@@ -8,11 +8,11 @@ require('@nuxt/kit');
8
8
  require('defu');
9
9
  require('fnv1a-64');
10
10
  require('nuxt-site-config/kit');
11
+ require('nuxtseo-shared/kit');
11
12
  require('pathe');
12
13
  require('pkg-types');
13
14
  require('std-env');
14
- const module$1 = require('./shared/nuxt-og-image.Cctpuv4O.cjs');
15
- require('nuxtseo-shared/kit');
15
+ const module$1 = require('./shared/nuxt-og-image.CdpC3njh.cjs');
16
16
  require('../dist/runtime/logger.js');
17
17
  require('oxc-walker');
18
18
  require('chrome-launcher');
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "nuxt": ">=3.16.0"
5
5
  },
6
6
  "configKey": "ogImage",
7
- "version": "6.7.5",
7
+ "version": "6.7.6",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.3",
10
10
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -6,11 +6,11 @@ import '@nuxt/kit';
6
6
  import 'defu';
7
7
  import 'fnv1a-64';
8
8
  import 'nuxt-site-config/kit';
9
+ import 'nuxtseo-shared/kit';
9
10
  import 'pathe';
10
11
  import 'pkg-types';
11
12
  import 'std-env';
12
- export { m as default } from './shared/nuxt-og-image.D3mV8OQ9.mjs';
13
- import 'nuxtseo-shared/kit';
13
+ export { n as default } from './shared/nuxt-og-image.BIbfn5xQ.mjs';
14
14
  import '../dist/runtime/logger.js';
15
15
  import 'oxc-walker';
16
16
  import 'chrome-launcher';
@@ -1,7 +1,7 @@
1
1
  import { TemplateParamsPlugin } from "@unhead/vue/plugins";
2
2
  import { defu } from "defu";
3
- import { createRouter as createRadixRouter, toRouteMatcher } from "radix3";
4
- import { parseURL, withoutBase } from "ufo";
3
+ import { createNitroRouteRuleMatcher } from "nuxtseo-shared/server";
4
+ import { parseURL } from "ufo";
5
5
  import { toValue } from "vue";
6
6
  import { defineNuxtPlugin, useRequestEvent } from "#app";
7
7
  import { withSiteUrl } from "#site-config/app/composables";
@@ -60,14 +60,8 @@ export const routeRuleOgImage = defineNuxtPlugin((nuxtApp) => {
60
60
  const path = parseURL(e?.path || "").pathname;
61
61
  if (isInternalRoute(path))
62
62
  return;
63
- const _routeRulesMatcher = toRouteMatcher(
64
- createRadixRouter({ routes: ssrContext?.runtimeConfig?.nitro?.routeRules })
65
- );
66
- const matchedRules = _routeRulesMatcher.matchAll(
67
- withoutBase(path.split("?")?.[0] || "", ssrContext?.runtimeConfig?.app.baseURL || "")
68
- ).reverse();
69
- const combinedRules = defu({}, ...matchedRules);
70
- let routeRules = combinedRules?.ogImage;
63
+ const matchRouteRules = createNitroRouteRuleMatcher(ssrContext?.runtimeConfig || {});
64
+ let routeRules = matchRouteRules(path).ogImage;
71
65
  if (typeof routeRules === "undefined")
72
66
  return;
73
67
  if (routeRules === false) {
@@ -1,3 +1,3 @@
1
- import type { H3Event } from 'h3';
2
1
  import type { Browser } from 'playwright-core';
2
+ import type { H3Event } from '#nuxtseo/h3';
3
3
  export declare function createBrowser(_event?: H3Event): Promise<Browser | void>;
@@ -1,4 +1,4 @@
1
- import type { H3Event } from 'h3';
1
+ import type { H3Event } from '#nuxtseo/h3';
2
2
  interface Browser {
3
3
  connected: boolean;
4
4
  newPage: () => Promise<any>;
@@ -1,3 +1,3 @@
1
- import type { H3Event } from 'h3';
2
1
  import type { Browser } from 'playwright-core';
2
+ import type { H3Event } from '#nuxtseo/h3';
3
3
  export declare function createBrowser(_event?: H3Event): Promise<Browser | void>;
@@ -1,3 +1,3 @@
1
- import type { H3Event } from 'h3';
2
1
  import type { Browser } from 'playwright-core';
2
+ import type { H3Event } from '#nuxtseo/h3';
3
3
  export declare function createBrowser(_event?: H3Event): Promise<Browser | void>;
@@ -1,3 +1,3 @@
1
- import type { H3Event } from 'h3';
1
+ import type { H3Event } from '#nuxtseo/h3';
2
2
  import type { FontConfig } from '../../../../types.js';
3
3
  export declare function resolve(event: H3Event, font: FontConfig): Promise<Buffer<ArrayBufferLike>>;
@@ -1,5 +1,5 @@
1
- import { useRuntimeConfig } from "nitropack/runtime";
2
1
  import { withBase } from "ufo";
2
+ import { useRuntimeConfig } from "#nuxtseo/nitro";
3
3
  import { getSiteConfig } from "#site-config/server/composables";
4
4
  import { getCloudflareAssets } from "../../../util/cloudflareAssets.js";
5
5
  import { fetchLocalAsset } from "../../../util/fetchLocalAsset.js";
@@ -1,3 +1,3 @@
1
- import type { H3Event } from 'h3';
1
+ import type { H3Event } from '#nuxtseo/h3';
2
2
  import type { FontConfig } from '../../../../types.js';
3
3
  export declare function resolve(event: H3Event, font: FontConfig): Promise<Buffer>;
@@ -1,8 +1,8 @@
1
1
  import { readFile } from "node:fs/promises";
2
- import { getRequestURL } from "h3";
3
- import { useRuntimeConfig } from "nitropack/runtime";
4
2
  import { join } from "pathe";
5
3
  import { withBase } from "ufo";
4
+ import { getRequestURL } from "#nuxtseo/h3";
5
+ import { useRuntimeConfig } from "#nuxtseo/nitro";
6
6
  import { buildDir, rootDir } from "#og-image-virtual/build-dir.mjs";
7
7
  import { getSiteConfig } from "#site-config/server/composables";
8
8
  import { getFetchTimeout } from "../../../util/fetchTimeout.js";
@@ -1,3 +1,3 @@
1
- import type { H3Event } from 'h3';
1
+ import type { H3Event } from '#nuxtseo/h3';
2
2
  import type { FontConfig } from '../../../../types.js';
3
3
  export declare function resolve(event: H3Event, font: FontConfig): Promise<Buffer<ArrayBufferLike>>;
@@ -1,5 +1,5 @@
1
- import { useRuntimeConfig } from "nitropack/runtime";
2
1
  import { withBase } from "ufo";
2
+ import { useRuntimeConfig } from "#nuxtseo/nitro";
3
3
  import { getNitroOrigin, getSiteConfig } from "#site-config/server/composables";
4
4
  import { getFetchTimeout } from "../../../util/fetchTimeout.js";
5
5
  import { fetchWithRedirectValidation } from "../../../util/ssrf.js";
@@ -1,4 +1,5 @@
1
1
  import { Worker } from "node:worker_threads";
2
+ import { createWorkerQueue, parseWorkerTimeout } from "../worker-queue.js";
2
3
  const workerCode = `
3
4
  const { createRequire } = require('node:module')
4
5
  const _require = createRequire(process.cwd() + '/')
@@ -12,78 +13,31 @@ parentPort.on('message', ({ id, svg, options }) => {
12
13
  // Always slice to create a standard ArrayBuffer \u2014 native addon buffers
13
14
  // use external memory that can't be transferred via postMessage
14
15
  const ab = png.buffer.slice(png.byteOffset, png.byteOffset + png.byteLength)
15
- parentPort.postMessage({ id, png: ab }, [ab])
16
+ parentPort.postMessage({ _tag: 'response', id, payload: { _tag: 'success', png: ab } }, [ab])
16
17
  } catch (err) {
17
- parentPort.postMessage({ id, error: err?.message || String(err) })
18
+ parentPort.postMessage({ _tag: 'response', id, payload: { _tag: 'failure', error: err?.message || String(err) } })
18
19
  }
19
20
  })
21
+ parentPort.postMessage({ _tag: 'ready' })
20
22
  `;
21
- let worker = null;
22
- let requestId = 0;
23
- const pending = /* @__PURE__ */ new Map();
24
- function killWorker() {
25
- if (!worker)
26
- return;
27
- worker.terminate();
28
- worker = null;
29
- for (const [id, p] of pending) {
30
- clearTimeout(p.timer);
31
- pending.delete(id);
32
- p.reject(new Error("Resvg worker terminated"));
33
- }
23
+ const workerTimeout = parseWorkerTimeout(process.env.NUXT_OG_IMAGE_WORKER_TIMEOUT);
24
+ const workerQueue = createWorkerQueue({
25
+ createWorker: () => new Worker(workerCode, { eval: true }),
26
+ executionTimeout: workerTimeout,
27
+ startupTimeout: workerTimeout,
28
+ label: "resvg"
29
+ });
30
+ function renderPng(svg, options) {
31
+ return workerQueue.enqueue(({ id }) => ({ id, svg, options })).then((result) => {
32
+ if (result._tag === "failure")
33
+ throw new Error(result.error);
34
+ return Buffer.from(result.png);
35
+ });
34
36
  }
35
37
  const signalKey = Symbol.for("og-image:resvg-worker-cleanup");
36
38
  if (!globalThis[signalKey]) {
37
39
  globalThis[signalKey] = true;
38
- process.on("exit", killWorker);
39
- }
40
- function createWorker() {
41
- const w = new Worker(workerCode, { eval: true });
42
- w.on("message", ({ id, png, error }) => {
43
- const p = pending.get(id);
44
- if (p) {
45
- clearTimeout(p.timer);
46
- pending.delete(id);
47
- if (error)
48
- p.reject(new Error(error));
49
- else
50
- p.resolve(Buffer.from(png));
51
- }
52
- });
53
- w.on("error", (err) => {
54
- for (const [id, p] of pending) {
55
- clearTimeout(p.timer);
56
- pending.delete(id);
57
- p.reject(err);
58
- }
59
- worker = null;
60
- });
61
- w.on("exit", (code) => {
62
- if (code !== 0) {
63
- for (const [id, p] of pending) {
64
- clearTimeout(p.timer);
65
- pending.delete(id);
66
- p.reject(new Error(`Resvg worker exited with code ${code}`));
67
- }
68
- }
69
- worker = null;
70
- });
71
- w.unref();
72
- return w;
73
- }
74
- function renderPng(svg, options) {
75
- return new Promise((resolve, reject) => {
76
- if (!worker)
77
- worker = createWorker();
78
- const id = ++requestId;
79
- const timer = setTimeout(() => {
80
- pending.delete(id);
81
- reject(new Error("resvg worker timed out \u2014 killing worker"));
82
- killWorker();
83
- }, 3e4);
84
- pending.set(id, { resolve, reject, timer });
85
- worker.postMessage({ id, svg, options });
86
- });
40
+ process.on("exit", workerQueue.shutdown);
87
41
  }
88
42
  class ResvgWorkerProxy {
89
43
  svg;
@@ -1,4 +1,5 @@
1
1
  import { Worker } from "node:worker_threads";
2
+ import { createWorkerQueue, parseWorkerTimeout } from "../worker-queue.js";
2
3
  import { extractResourceUrls as extractTakumiResourceUrls } from "./resource-urls.js";
3
4
  const workerCode = `
4
5
  const { createRequire } = require('node:module')
@@ -30,85 +31,24 @@ parentPort.on('message', async ({ id, type, newFonts, nodes, options }) => {
30
31
  // Always slice to create a standard ArrayBuffer \u2014 native addon buffers
31
32
  // use external memory that can't be transferred via postMessage
32
33
  const ab = image.buffer.slice(image.byteOffset, image.byteOffset + image.byteLength)
33
- parentPort.postMessage({ id, image: ab, fontWarnings }, [ab])
34
+ parentPort.postMessage({ _tag: 'response', id, payload: { _tag: 'success', image: ab, fontWarnings } }, [ab])
34
35
  } catch (err) {
35
- parentPort.postMessage({ id, error: err?.message || String(err) })
36
+ parentPort.postMessage({ _tag: 'response', id, payload: { _tag: 'failure', error: err?.message || String(err) } })
36
37
  }
37
38
  })
39
+ parentPort.postMessage({ _tag: 'ready' })
38
40
  `;
39
- let worker = null;
40
- let workerGeneration = 0;
41
- let requestId = 0;
42
- const pending = /* @__PURE__ */ new Map();
43
- function killWorker() {
44
- if (!worker)
45
- return;
46
- worker.terminate();
47
- worker = null;
48
- for (const [id, p] of pending) {
49
- clearTimeout(p.timer);
50
- pending.delete(id);
51
- p.reject(new Error("Takumi worker terminated"));
52
- }
53
- }
41
+ const workerTimeout = parseWorkerTimeout(process.env.NUXT_OG_IMAGE_WORKER_TIMEOUT);
42
+ const workerQueue = createWorkerQueue({
43
+ createWorker: () => new Worker(workerCode, { eval: true }),
44
+ executionTimeout: workerTimeout,
45
+ startupTimeout: workerTimeout,
46
+ label: "takumi"
47
+ });
54
48
  const signalKey = Symbol.for("og-image:takumi-worker-cleanup");
55
49
  if (!globalThis[signalKey]) {
56
50
  globalThis[signalKey] = true;
57
- process.on("exit", killWorker);
58
- }
59
- function createWorker() {
60
- workerGeneration++;
61
- const w = new Worker(workerCode, { eval: true });
62
- w.on("message", ({ id, image, urls, error, fontWarnings }) => {
63
- const p = pending.get(id);
64
- if (p) {
65
- clearTimeout(p.timer);
66
- pending.delete(id);
67
- if (error)
68
- p.reject(new Error(error));
69
- else if (urls !== void 0)
70
- p.resolve(urls);
71
- else
72
- p.resolve({ image: Buffer.from(image), fontWarnings });
73
- }
74
- });
75
- w.on("error", (err) => {
76
- for (const [id, p] of pending) {
77
- clearTimeout(p.timer);
78
- pending.delete(id);
79
- p.reject(err);
80
- }
81
- worker = null;
82
- });
83
- w.on("exit", (code) => {
84
- if (code !== 0) {
85
- for (const [id, p] of pending) {
86
- clearTimeout(p.timer);
87
- pending.delete(id);
88
- p.reject(new Error(`Takumi worker exited with code ${code}`));
89
- }
90
- }
91
- worker = null;
92
- });
93
- w.unref();
94
- return w;
95
- }
96
- function ensureWorker() {
97
- if (!worker)
98
- worker = createWorker();
99
- }
100
- function postToWorker(msg, timeoutMs = 3e4) {
101
- return new Promise((resolve, reject) => {
102
- ensureWorker();
103
- const id = ++requestId;
104
- const timer = setTimeout(() => {
105
- pending.delete(id);
106
- reject(new Error("takumi worker timed out \u2014 killing worker"));
107
- killWorker();
108
- }, timeoutMs);
109
- pending.set(id, { resolve, reject, timer });
110
- worker.postMessage({ id, ...msg });
111
- });
51
+ process.on("exit", workerQueue.shutdown);
112
52
  }
113
53
  function extractResourceUrls(nodes) {
114
54
  return Promise.resolve(extractTakumiResourceUrls(nodes));
@@ -130,21 +70,24 @@ class RendererWorkerProxy {
130
70
  this.loadFont(font);
131
71
  }
132
72
  render(nodes, options) {
133
- ensureWorker();
134
- let fontsToSend;
135
- if (this.syncedGeneration !== workerGeneration) {
136
- fontsToSend = [...this.allFonts];
137
- this.pendingFonts = [];
138
- } else {
139
- fontsToSend = this.pendingFonts.splice(0);
140
- }
141
- this.syncedGeneration = workerGeneration;
142
- return postToWorker({ type: "render", newFonts: fontsToSend, nodes, options }).then((result) => {
73
+ return workerQueue.enqueue(({ id, generation }) => {
74
+ let fontsToSend;
75
+ if (this.syncedGeneration !== generation) {
76
+ fontsToSend = [...this.allFonts];
77
+ this.pendingFonts = [];
78
+ } else {
79
+ fontsToSend = this.pendingFonts.splice(0);
80
+ }
81
+ this.syncedGeneration = generation;
82
+ return { id, type: "render", newFonts: fontsToSend, nodes, options };
83
+ }).then((result) => {
84
+ if (result._tag === "failure")
85
+ throw new Error(result.error);
143
86
  if (result.fontWarnings?.length) {
144
87
  for (const w of result.fontWarnings)
145
88
  console.warn(`[nuxt-og-image] Failed to load font "${w.name}" (weight: ${w.weight}) into takumi renderer: ${w.error}`);
146
89
  }
147
- return result.image;
90
+ return Buffer.from(result.image);
148
91
  });
149
92
  }
150
93
  }
@@ -0,0 +1,34 @@
1
+ interface WorkerHandle {
2
+ on: {
3
+ (event: 'message', listener: (message: unknown) => void): WorkerHandle;
4
+ (event: 'error', listener: (error: Error) => void): WorkerHandle;
5
+ (event: 'exit', listener: (code: number) => void): WorkerHandle;
6
+ };
7
+ postMessage: (message: unknown) => void;
8
+ removeAllListeners: () => WorkerHandle;
9
+ terminate: () => Promise<number>;
10
+ unref: () => void;
11
+ }
12
+ interface QueueOptions {
13
+ createWorker: () => WorkerHandle;
14
+ executionTimeout: number;
15
+ startupTimeout: number;
16
+ label: string;
17
+ maxAttempts?: number;
18
+ }
19
+ interface Job<TMessage, TResponse> {
20
+ id: number;
21
+ buildMessage: (context: {
22
+ id: number;
23
+ generation: number;
24
+ }) => TMessage;
25
+ resolve: (response: TResponse) => void;
26
+ reject: (error: Error) => void;
27
+ attempts: number;
28
+ }
29
+ export declare function parseWorkerTimeout(value: string | undefined, fallback?: number): number;
30
+ export declare function createWorkerQueue<TMessage, TResponse>(options: QueueOptions): {
31
+ enqueue: (buildMessage: Job<TMessage, TResponse>["buildMessage"]) => Promise<TResponse>;
32
+ shutdown: () => void;
33
+ };
34
+ export {};