nuxt-og-image 6.7.5 → 6.7.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +6 -0
- package/dist/chunks/decode.mjs +1087 -0
- package/dist/chunks/tw4.mjs +2 -2
- package/dist/chunks/uno.mjs +19 -5
- package/dist/module.d.ts +1 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +2 -2
- package/dist/runtime/app/utils/plugins.js +4 -10
- package/dist/runtime/server/og-image/bindings/browser/chrome-launcher.d.ts +1 -1
- package/dist/runtime/server/og-image/bindings/browser/cloudflare.d.ts +1 -1
- package/dist/runtime/server/og-image/bindings/browser/on-demand.d.ts +1 -1
- package/dist/runtime/server/og-image/bindings/browser/playwright.d.ts +1 -1
- package/dist/runtime/server/og-image/bindings/font-assets/cloudflare.d.ts +1 -1
- package/dist/runtime/server/og-image/bindings/font-assets/cloudflare.js +1 -1
- package/dist/runtime/server/og-image/bindings/font-assets/dev-prerender.d.ts +1 -1
- package/dist/runtime/server/og-image/bindings/font-assets/dev-prerender.js +6 -7
- package/dist/runtime/server/og-image/bindings/font-assets/node.d.ts +2 -2
- package/dist/runtime/server/og-image/bindings/font-assets/node.js +2 -3
- package/dist/runtime/server/og-image/bindings/resvg/node-dev.js +18 -64
- package/dist/runtime/server/og-image/bindings/takumi/node-dev.js +26 -83
- package/dist/runtime/server/og-image/bindings/worker-queue.d.ts +34 -0
- package/dist/runtime/server/og-image/bindings/worker-queue.js +150 -0
- package/dist/runtime/server/og-image/browser/renderer.js +1 -1
- package/dist/runtime/server/og-image/browser/screenshot.js +2 -1
- package/dist/runtime/server/og-image/context.d.ts +1 -1
- package/dist/runtime/server/og-image/context.js +2 -3
- package/dist/runtime/server/og-image/core/plugins/imageSrc.js +1 -1
- package/dist/runtime/server/og-image/core/transforms/emojis/local.d.ts +2 -17
- package/dist/runtime/server/og-image/core/transforms/emojis/local.js +1 -1
- package/dist/runtime/server/og-image/devtools.d.ts +1 -1
- package/dist/runtime/server/og-image/devtools.js +1 -1
- package/dist/runtime/server/og-image/font-source.d.ts +3 -12
- package/dist/runtime/server/og-image/font-source.js +1 -4
- package/dist/runtime/server/og-image/fonts.d.ts +1 -8
- package/dist/runtime/server/og-image/fonts.js +1 -1
- package/dist/runtime/server/og-image/takumi/renderer.js +2 -2
- package/dist/runtime/server/og-image/templates/html.js +1 -1
- package/dist/runtime/server/plugins/prerender.d.ts +1 -1
- package/dist/runtime/server/plugins/prerender.js +2 -2
- package/dist/runtime/server/routes/__zero-runtime/image.d.ts +1 -1
- package/dist/runtime/server/routes/__zero-runtime/image.js +1 -1
- package/dist/runtime/server/routes/debug.json.d.ts +1 -10
- package/dist/runtime/server/routes/debug.json.js +1 -1
- package/dist/runtime/server/routes/image.d.ts +1 -1
- package/dist/runtime/server/routes/image.js +1 -1
- package/dist/runtime/server/routes/resolve.d.ts +1 -1
- package/dist/runtime/server/routes/resolve.js +3 -2
- package/dist/runtime/server/util/cache.d.ts +1 -6
- package/dist/runtime/server/util/cache.js +2 -2
- package/dist/runtime/server/util/cloudflare.d.ts +1 -1
- package/dist/runtime/server/util/cloudflareAssets.d.ts +1 -1
- package/dist/runtime/server/util/cloudflareAssets.js +1 -1
- package/dist/runtime/server/util/eventHandlers.d.ts +1 -1
- package/dist/runtime/server/util/eventHandlers.js +1 -1
- package/dist/runtime/server/util/fetchLocalAsset.d.ts +2 -2
- package/dist/runtime/server/util/fetchLocalAsset.js +2 -1
- package/dist/runtime/server/util/kit.d.ts +7 -3
- package/dist/runtime/server/util/kit.js +4 -18
- package/dist/runtime/server/util/options.js +1 -1
- package/dist/runtime/server/util/query.d.ts +1 -1
- package/dist/runtime/server/utils.d.ts +1 -1
- package/dist/runtime/server/utils.js +1 -1
- package/dist/runtime/types.d.ts +12 -10
- package/dist/shared/{nuxt-og-image.D3mV8OQ9.mjs → nuxt-og-image.BMHzg1Vz.mjs} +334 -155
- package/package.json +49 -39
- package/types/virtual.d.ts +15 -0
- package/dist/chunks/tw4.cjs +0 -493
- package/dist/chunks/uno.cjs +0 -234
- package/dist/cli.cjs +0 -1345
- package/dist/cli.d.cts +0 -1
- package/dist/content.cjs +0 -21
- package/dist/content.d.cts +0 -16
- package/dist/module.cjs +0 -39
- package/dist/module.d.cts +0 -219
- package/dist/shared/nuxt-og-image.CMYbz66P.cjs +0 -193
- package/dist/shared/nuxt-og-image.Cctpuv4O.cjs +0 -5867
package/dist/chunks/tw4.mjs
CHANGED
|
@@ -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,
|
|
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.BMHzg1Vz.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';
|
package/dist/chunks/uno.mjs
CHANGED
|
@@ -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.
|
|
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.BMHzg1Vz.mjs';
|
|
5
|
+
export { j as importResolvedModule, k as resolveOptionalModulePath } from '../shared/nuxt-og-image.BMHzg1Vz.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
|
|
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
|
-
|
|
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
|
|
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.d.ts
CHANGED
package/dist/module.json
CHANGED
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 {
|
|
13
|
-
import 'nuxtseo-shared/kit';
|
|
13
|
+
export { n as default } from './shared/nuxt-og-image.BMHzg1Vz.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 {
|
|
4
|
-
import { parseURL
|
|
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
|
|
64
|
-
|
|
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,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,9 +1,9 @@
|
|
|
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";
|
|
6
|
-
import {
|
|
4
|
+
import { getRequestURL } from "#nuxtseo/h3";
|
|
5
|
+
import { fetchWithEvent, useRuntimeConfig } from "#nuxtseo/nitro";
|
|
6
|
+
import { buildDir, rootDir, staticFontCacheDir } from "#og-image-virtual/build-dir.mjs";
|
|
7
7
|
import { getSiteConfig } from "#site-config/server/composables";
|
|
8
8
|
import { getFetchTimeout } from "../../../util/fetchTimeout.js";
|
|
9
9
|
import { fetchWithRedirectValidation } from "../../../util/ssrf.js";
|
|
@@ -43,7 +43,7 @@ export async function resolve(event, font) {
|
|
|
43
43
|
if (import.meta.prerender) {
|
|
44
44
|
if (path.startsWith("/_og-static-fonts/")) {
|
|
45
45
|
const filename2 = path.slice("/_og-static-fonts/".length);
|
|
46
|
-
const cached = await readOptionalFile(join(
|
|
46
|
+
const cached = await readOptionalFile(join(staticFontCacheDir, filename2)) || await readOptionalFile(join(rootDir, ".output", "public", "_og-static-fonts", filename2));
|
|
47
47
|
if (cached?.length)
|
|
48
48
|
return cached;
|
|
49
49
|
}
|
|
@@ -67,7 +67,7 @@ export async function resolve(event, font) {
|
|
|
67
67
|
}
|
|
68
68
|
if (path.startsWith("/_og-static-fonts/")) {
|
|
69
69
|
const filename = path.slice("/_og-static-fonts/".length);
|
|
70
|
-
const cached = await readOptionalFile(join(
|
|
70
|
+
const cached = await readOptionalFile(join(staticFontCacheDir, filename));
|
|
71
71
|
if (cached?.length)
|
|
72
72
|
return cached;
|
|
73
73
|
}
|
|
@@ -99,8 +99,7 @@ export async function resolve(event, font) {
|
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
const fullPath = withBase(path, app.baseURL);
|
|
102
|
-
const
|
|
103
|
-
const arrayBuffer = await fetchArrayBuffer(fullPath, {
|
|
102
|
+
const arrayBuffer = await fetchWithEvent(event, fullPath, {
|
|
104
103
|
responseType: "arrayBuffer",
|
|
105
104
|
timeout
|
|
106
105
|
});
|
|
@@ -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
|
-
export declare function resolve(event: H3Event, font: FontConfig): Promise<Buffer<
|
|
3
|
+
export declare function resolve(event: H3Event, font: FontConfig): Promise<Buffer<any>>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { useRuntimeConfig } from "nitropack/runtime";
|
|
2
1
|
import { withBase } from "ufo";
|
|
2
|
+
import { fetchWithEvent, 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";
|
|
@@ -21,8 +21,7 @@ export async function resolve(event, font) {
|
|
|
21
21
|
if (ab) {
|
|
22
22
|
return Buffer.from(ab);
|
|
23
23
|
}
|
|
24
|
-
const
|
|
25
|
-
const arrayBuffer = await fetchArrayBuffer(fullPath, {
|
|
24
|
+
const arrayBuffer = await fetchWithEvent(event, fullPath, {
|
|
26
25
|
responseType: "arrayBuffer",
|
|
27
26
|
timeout
|
|
28
27
|
});
|
|
@@ -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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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",
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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",
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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 {};
|