wasm-html-to-image 0.1.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.
- package/LICENSE +21 -0
- package/README.md +326 -0
- package/dist/browser-workers.d.ts +12 -0
- package/dist/browser-workers.js +26 -0
- package/dist/child-workers.d.ts +11 -0
- package/dist/child-workers.js +19 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +87 -0
- package/dist/core.d.ts +168 -0
- package/dist/core.js +824 -0
- package/dist/diagnostics.d.ts +93 -0
- package/dist/diagnostics.js +28 -0
- package/dist/edge-light.d.ts +18 -0
- package/dist/edge-light.js +70 -0
- package/dist/encode-args.d.ts +10 -0
- package/dist/encode-args.js +30 -0
- package/dist/html-to-image-single.js +0 -0
- package/dist/html-to-image-wasm.js +3 -0
- package/dist/html-to-image.js +2 -0
- package/dist/html-to-image.wasm +0 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +14 -0
- package/dist/input.d.ts +23 -0
- package/dist/input.js +90 -0
- package/dist/loader.d.ts +132 -0
- package/dist/loader.js +199 -0
- package/dist/preact.d.ts +10 -0
- package/dist/preact.js +13 -0
- package/dist/react.d.ts +9 -0
- package/dist/react.js +11 -0
- package/dist/single.d.ts +27 -0
- package/dist/single.js +22 -0
- package/dist/tailwind.d.ts +1 -0
- package/dist/tailwind.js +6 -0
- package/dist/wasm-payload.d.ts +2 -0
- package/dist/wasm-payload.js +39 -0
- package/dist/web-workers.js +4411 -0
- package/dist/worker-actions.d.ts +10 -0
- package/dist/worker-actions.js +13 -0
- package/dist/worker-lib-loader.d.ts +19 -0
- package/dist/worker-lib-loader.js +34 -0
- package/dist/workerd.d.ts +18 -0
- package/dist/workerd.js +65 -0
- package/dist/workers-dummy.d.ts +12 -0
- package/dist/workers-dummy.js +6 -0
- package/dist/workers-parent.js +602 -0
- package/dist/workers.d.ts +86 -0
- package/dist/workers.js +163 -0
- package/package.json +166 -0
package/dist/workers.js
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { loadWorkerLib } from "./worker-lib-loader.js";
|
|
2
|
+
// Playground parity: diagnostics surface is usable through the pool —
|
|
3
|
+
// function-valued options (`onLog`/`onDiagnostics`/`resolveResource`) are
|
|
4
|
+
// forwarded untouched by the `render` proxy below (worker-lib RPC).
|
|
5
|
+
export { DEFAULT_FONT_MAP, DIAGNOSTIC_CODES, LogLevel } from "./core.js";
|
|
6
|
+
// worker-lib's published ESM is broken under plain Node (see
|
|
7
|
+
// worker-lib-loader.ts); top-level await keeps call sites sync-shaped.
|
|
8
|
+
const { createWorker, Worker } = await loadWorkerLib();
|
|
9
|
+
/**
|
|
10
|
+
* Create a wasm-html-to-image worker pool using worker-lib.
|
|
11
|
+
* The pooled action is the unified `render` (HTML and image inputs).
|
|
12
|
+
* Each worker thread loads its own module instance via the loader
|
|
13
|
+
* (modules cannot cross thread boundaries).
|
|
14
|
+
*
|
|
15
|
+
* @param params Initialization parameters
|
|
16
|
+
* @param params.worker Optional: Path to the worker file, a URL, or a factory function.
|
|
17
|
+
* Defaults to the pre-bundled browser worker
|
|
18
|
+
* (`./web-workers.js`, SINGLE_FILE inlined) when
|
|
19
|
+
* `window` exists, else the Node worker
|
|
20
|
+
* (`./child-workers.js`) next to this file.
|
|
21
|
+
* @param params.maxParallel Maximum number of parallel workers (default: 4)
|
|
22
|
+
* @param params.timeoutMs Optional: Default timeout in milliseconds for each render job.
|
|
23
|
+
* If a render job times out, the pool is reset to prevent hung workers.
|
|
24
|
+
*/
|
|
25
|
+
export const createHtmlToImageWorker = (params) => {
|
|
26
|
+
const { worker, maxParallel = 4, timeoutMs } = params ?? {};
|
|
27
|
+
const factory = () => {
|
|
28
|
+
let w;
|
|
29
|
+
if (worker) {
|
|
30
|
+
w = typeof worker === "function" ? worker() : worker;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
// child-workers.js = Node worker; web-workers.js = pre-bundled
|
|
34
|
+
// browser worker (SINGLE_FILE inlined, no .wasm fetch needed).
|
|
35
|
+
const workerUrl = typeof window !== "undefined"
|
|
36
|
+
? new URL("./web-workers.js", import.meta.url)
|
|
37
|
+
: new URL("./child-workers.js", import.meta.url);
|
|
38
|
+
if (typeof Worker !== "undefined") {
|
|
39
|
+
w = new Worker(workerUrl, { type: "module" });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (!w)
|
|
43
|
+
throw new Error("Worker is not supported in this environment.");
|
|
44
|
+
return w;
|
|
45
|
+
};
|
|
46
|
+
const workerInstance = createWorker(factory, maxParallel);
|
|
47
|
+
let totalPendingJobs = 0;
|
|
48
|
+
let completedJobs = 0;
|
|
49
|
+
let failedJobs = 0;
|
|
50
|
+
let totalJobTimeMs = 0;
|
|
51
|
+
/**
|
|
52
|
+
* Retrieve operational stats of the worker pool.
|
|
53
|
+
*/
|
|
54
|
+
const getStats = () => ({
|
|
55
|
+
workerCount: maxParallel,
|
|
56
|
+
activeJobs: Math.min(totalPendingJobs, maxParallel),
|
|
57
|
+
queuedJobs: Math.max(0, totalPendingJobs - maxParallel),
|
|
58
|
+
completedJobs,
|
|
59
|
+
failedJobs,
|
|
60
|
+
avgJobTimeMs: completedJobs > 0 ? totalJobTimeMs / completedJobs : 0,
|
|
61
|
+
});
|
|
62
|
+
/**
|
|
63
|
+
* Reset the worker pool by terminating all running workers and recreating them.
|
|
64
|
+
* Useful to clear hung workers or reset statistics.
|
|
65
|
+
*/
|
|
66
|
+
const reset = () => {
|
|
67
|
+
workerInstance.setLimit(0);
|
|
68
|
+
workerInstance.setLimit(maxParallel);
|
|
69
|
+
totalPendingJobs = 0;
|
|
70
|
+
completedJobs = 0;
|
|
71
|
+
failedJobs = 0;
|
|
72
|
+
totalJobTimeMs = 0;
|
|
73
|
+
};
|
|
74
|
+
const proxy = new Proxy(workerInstance, {
|
|
75
|
+
get(target, prop, receiver) {
|
|
76
|
+
if (prop === "getStats") {
|
|
77
|
+
return getStats;
|
|
78
|
+
}
|
|
79
|
+
if (prop === "reset") {
|
|
80
|
+
return reset;
|
|
81
|
+
}
|
|
82
|
+
if (prop === "render") {
|
|
83
|
+
return async (options) => {
|
|
84
|
+
totalPendingJobs++;
|
|
85
|
+
const startTime = Date.now();
|
|
86
|
+
let timeoutId;
|
|
87
|
+
let timeoutPromise;
|
|
88
|
+
if (timeoutMs !== undefined && timeoutMs > 0) {
|
|
89
|
+
timeoutPromise = new Promise((_, reject) => {
|
|
90
|
+
timeoutId = setTimeout(() => {
|
|
91
|
+
// If a job times out, terminate and recreate workers to recover from a potential hang
|
|
92
|
+
reset();
|
|
93
|
+
reject(new Error(`Render timed out after ${timeoutMs}ms`));
|
|
94
|
+
}, timeoutMs);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
// Protect caller's buffer from being detached if worker-lib transfers TypedArrays
|
|
98
|
+
let execOptions = options;
|
|
99
|
+
if (options && typeof options === "object") {
|
|
100
|
+
if (options.value instanceof Uint8Array) {
|
|
101
|
+
execOptions = { ...options, value: options.value.slice() };
|
|
102
|
+
}
|
|
103
|
+
else if (options.value instanceof ArrayBuffer) {
|
|
104
|
+
execOptions = { ...options, value: options.value.slice(0) };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
const executePromise = target.execute("render", execOptions);
|
|
109
|
+
const result = await (timeoutPromise
|
|
110
|
+
? Promise.race([executePromise, timeoutPromise])
|
|
111
|
+
: executePromise);
|
|
112
|
+
completedJobs++;
|
|
113
|
+
totalJobTimeMs += Date.now() - startTime;
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
catch (e) {
|
|
117
|
+
failedJobs++;
|
|
118
|
+
throw e;
|
|
119
|
+
}
|
|
120
|
+
finally {
|
|
121
|
+
if (timeoutId) {
|
|
122
|
+
clearTimeout(timeoutId);
|
|
123
|
+
}
|
|
124
|
+
totalPendingJobs--;
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
if (prop in target) {
|
|
129
|
+
return Reflect.get(target, prop, receiver);
|
|
130
|
+
}
|
|
131
|
+
// No generic `execute` forwarding: only the explicit pool API above
|
|
132
|
+
// (render/getStats/reset) plus worker-lib's own methods (close,
|
|
133
|
+
// launchWorker, setLimit, waitAll, waitReady) are available.
|
|
134
|
+
// Returning undefined (instead of throwing) keeps `await pool` working.
|
|
135
|
+
return undefined;
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
return proxy;
|
|
139
|
+
};
|
|
140
|
+
let defaultWorker;
|
|
141
|
+
/** Lazily create the shared single-slot pool on first use (not at import). */
|
|
142
|
+
function getDefaultWorker() {
|
|
143
|
+
if (!defaultWorker) {
|
|
144
|
+
defaultWorker = createHtmlToImageWorker({ maxParallel: 1 });
|
|
145
|
+
}
|
|
146
|
+
return defaultWorker;
|
|
147
|
+
}
|
|
148
|
+
export const close = () => {
|
|
149
|
+
getDefaultWorker().close();
|
|
150
|
+
};
|
|
151
|
+
export function render(options) {
|
|
152
|
+
return getDefaultWorker().render(options);
|
|
153
|
+
}
|
|
154
|
+
export const launchWorker = () => getDefaultWorker().launchWorker();
|
|
155
|
+
export const setLimit = (limit) => {
|
|
156
|
+
getDefaultWorker().setLimit(limit);
|
|
157
|
+
};
|
|
158
|
+
export const waitAll = () => getDefaultWorker().waitAll();
|
|
159
|
+
export const waitReady = (retryTime) => getDefaultWorker().waitReady(retryTime);
|
|
160
|
+
export const reset = () => {
|
|
161
|
+
getDefaultWorker().reset();
|
|
162
|
+
};
|
|
163
|
+
export const getStats = () => getDefaultWorker().getStats();
|
package/package.json
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "wasm-html-to-image",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "HTML to image converter on a single unified WASM module",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/single.js",
|
|
7
|
+
"types": "./dist/single.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"wasm-html-to-image": "./dist/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"author": "SoraKumo <info@croud.jp>",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/SoraKumo001/wasm-html-to-image.git",
|
|
15
|
+
"directory": "packages/html-to-image"
|
|
16
|
+
},
|
|
17
|
+
"homepage": "https://sorakumo001.github.io/wasm-html-to-image/",
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/SoraKumo001/wasm-html-to-image/issues"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"wasm",
|
|
23
|
+
"webassembly",
|
|
24
|
+
"html-to-image",
|
|
25
|
+
"html2image",
|
|
26
|
+
"skia",
|
|
27
|
+
"litehtml",
|
|
28
|
+
"image-optimization",
|
|
29
|
+
"screenshot",
|
|
30
|
+
"ogp",
|
|
31
|
+
"avif",
|
|
32
|
+
"webp",
|
|
33
|
+
"pdf",
|
|
34
|
+
"thumbhash",
|
|
35
|
+
"cloudflare-workers",
|
|
36
|
+
"edge-runtime"
|
|
37
|
+
],
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=20.0.0"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsc -b && node scripts/compress-wasm.mjs && rolldown -c rolldown.config.js",
|
|
43
|
+
"prepublishOnly": "npm run build",
|
|
44
|
+
"test": "vitest run",
|
|
45
|
+
"example:cli": "tsx src/cli.ts --help"
|
|
46
|
+
},
|
|
47
|
+
"exports": {
|
|
48
|
+
".": {
|
|
49
|
+
"workerd": {
|
|
50
|
+
"types": "./dist/workerd.d.ts",
|
|
51
|
+
"import": "./dist/workerd.js"
|
|
52
|
+
},
|
|
53
|
+
"deno": {
|
|
54
|
+
"types": "./dist/single.d.ts",
|
|
55
|
+
"import": "./dist/single.js"
|
|
56
|
+
},
|
|
57
|
+
"single": {
|
|
58
|
+
"types": "./dist/single.d.ts",
|
|
59
|
+
"import": "./dist/single.js"
|
|
60
|
+
},
|
|
61
|
+
"types": "./dist/single.d.ts",
|
|
62
|
+
"import": "./dist/single.js",
|
|
63
|
+
"default": "./dist/single.js"
|
|
64
|
+
},
|
|
65
|
+
"./index": {
|
|
66
|
+
"types": "./dist/index.d.ts",
|
|
67
|
+
"import": "./dist/index.js",
|
|
68
|
+
"default": "./dist/index.js"
|
|
69
|
+
},
|
|
70
|
+
"./single": {
|
|
71
|
+
"types": "./dist/single.d.ts",
|
|
72
|
+
"import": "./dist/single.js",
|
|
73
|
+
"default": "./dist/single.js"
|
|
74
|
+
},
|
|
75
|
+
"./workerd": {
|
|
76
|
+
"workerd": {
|
|
77
|
+
"types": "./dist/workerd.d.ts",
|
|
78
|
+
"import": "./dist/workerd.js",
|
|
79
|
+
"default": "./dist/workerd.js"
|
|
80
|
+
},
|
|
81
|
+
"edge-light": {
|
|
82
|
+
"types": "./dist/edge-light.d.ts",
|
|
83
|
+
"import": "./dist/edge-light.js",
|
|
84
|
+
"default": "./dist/edge-light.js"
|
|
85
|
+
},
|
|
86
|
+
"types": "./dist/workerd.d.ts",
|
|
87
|
+
"import": "./dist/workerd.js",
|
|
88
|
+
"default": "./dist/workerd.js"
|
|
89
|
+
},
|
|
90
|
+
"./edge-light": {
|
|
91
|
+
"types": "./dist/edge-light.d.ts",
|
|
92
|
+
"import": "./dist/edge-light.js",
|
|
93
|
+
"default": "./dist/edge-light.js"
|
|
94
|
+
},
|
|
95
|
+
"./workers": {
|
|
96
|
+
"types": "./dist/workers.d.ts",
|
|
97
|
+
"workerd": "./dist/workers-dummy.js",
|
|
98
|
+
"node": "./dist/workers.js",
|
|
99
|
+
"browser": "./dist/workers-parent.js",
|
|
100
|
+
"default": "./dist/workers-dummy.js"
|
|
101
|
+
},
|
|
102
|
+
"./html-to-image.js": {
|
|
103
|
+
"import": "./dist/html-to-image.js",
|
|
104
|
+
"default": "./dist/html-to-image.js"
|
|
105
|
+
},
|
|
106
|
+
"./react": {
|
|
107
|
+
"types": "./dist/react.d.ts",
|
|
108
|
+
"import": "./dist/react.js"
|
|
109
|
+
},
|
|
110
|
+
"./preact": {
|
|
111
|
+
"types": "./dist/preact.d.ts",
|
|
112
|
+
"import": "./dist/preact.js"
|
|
113
|
+
},
|
|
114
|
+
"./tailwind": {
|
|
115
|
+
"types": "./dist/tailwind.d.ts",
|
|
116
|
+
"import": "./dist/tailwind.js"
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
"files": [
|
|
120
|
+
"dist",
|
|
121
|
+
"README.md",
|
|
122
|
+
"LICENSE"
|
|
123
|
+
],
|
|
124
|
+
"license": "MIT",
|
|
125
|
+
"dependencies": {
|
|
126
|
+
"commander": "15.0.0",
|
|
127
|
+
"worker-lib": "2.2.1"
|
|
128
|
+
},
|
|
129
|
+
"peerDependencies": {
|
|
130
|
+
"@unocss/core": "^66.6.5",
|
|
131
|
+
"@unocss/preset-wind4": "^66.6.5",
|
|
132
|
+
"preact": "^10.28.0",
|
|
133
|
+
"preact-render-to-string": "^6.6.0",
|
|
134
|
+
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
|
135
|
+
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
|
136
|
+
},
|
|
137
|
+
"peerDependenciesMeta": {
|
|
138
|
+
"@unocss/core": {
|
|
139
|
+
"optional": true
|
|
140
|
+
},
|
|
141
|
+
"@unocss/preset-wind4": {
|
|
142
|
+
"optional": true
|
|
143
|
+
},
|
|
144
|
+
"preact": {
|
|
145
|
+
"optional": true
|
|
146
|
+
},
|
|
147
|
+
"preact-render-to-string": {
|
|
148
|
+
"optional": true
|
|
149
|
+
},
|
|
150
|
+
"react": {
|
|
151
|
+
"optional": true
|
|
152
|
+
},
|
|
153
|
+
"react-dom": {
|
|
154
|
+
"optional": true
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
"devDependencies": {
|
|
158
|
+
"@types/node": "26.4.1",
|
|
159
|
+
"@types/react": "^19.2.18",
|
|
160
|
+
"@types/react-dom": "^19.2.7",
|
|
161
|
+
"rolldown": "^1.2.7",
|
|
162
|
+
"tsx": "^4.23.13",
|
|
163
|
+
"typescript": "^7.0.2",
|
|
164
|
+
"vitest": "^5.0.0"
|
|
165
|
+
}
|
|
166
|
+
}
|