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
|
@@ -0,0 +1,602 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __esmMin = (fn, res, err) => () => {
|
|
4
|
+
if (err) throw err[0];
|
|
5
|
+
try {
|
|
6
|
+
return fn && (res = fn(fn = 0)), res;
|
|
7
|
+
} catch (e) {
|
|
8
|
+
throw err = [e], e;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
var __exportAll = (all, no_symbols) => {
|
|
12
|
+
let target = {};
|
|
13
|
+
for (var name in all) __defProp(target, name, {
|
|
14
|
+
get: all[name],
|
|
15
|
+
enumerable: true
|
|
16
|
+
});
|
|
17
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
18
|
+
return target;
|
|
19
|
+
};
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/loader.ts
|
|
22
|
+
/**
|
|
23
|
+
* Runtime-only load of a `node:` builtin (e.g. `importNode("fs/promises")`).
|
|
24
|
+
* Uses `process.getBuiltinModule` (Node 22.3+/20.16+) so there is NO `import`
|
|
25
|
+
* syntax at all: bundlers with static analysis (webpack/vite/rollup, and
|
|
26
|
+
* Turbopack — the Next.js 16 default builder, which rejects both dynamic
|
|
27
|
+
* `` `node:${name}` `` requests and literal `import("node:*")` inside Edge
|
|
28
|
+
* graphs) have nothing to resolve or rewrite. Only the builtins actually
|
|
29
|
+
* used by this package are supported. All call sites are guarded to run on
|
|
30
|
+
* Node only; other runtimes never evaluate these branches. A `new Function`
|
|
31
|
+
* indirect-import fallback keeps older Node working without adding any
|
|
32
|
+
* statically analyzable import.
|
|
33
|
+
*/
|
|
34
|
+
function importNode(name) {
|
|
35
|
+
if (name !== "fs/promises" && name !== "path" && name !== "module") throw new Error(`wasm-html-to-image: unsupported node builtin "${name}"`);
|
|
36
|
+
const proc = globalThis.process;
|
|
37
|
+
try {
|
|
38
|
+
const mod = proc?.getBuiltinModule?.(`node:${name}`);
|
|
39
|
+
if (mod) return Promise.resolve(mod);
|
|
40
|
+
} catch {}
|
|
41
|
+
return new Function("s", "return import(s)")(`node:${name}`);
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
//#region ../../node_modules/.pnpm/worker-lib@2.2.1/node_modules/worker-lib/dist/esm/common.js
|
|
45
|
+
var FUNCTION_PLACEHOLDER, isPlaceholder, isPlainObject, getTransferables, createRegistry;
|
|
46
|
+
var init_common = __esmMin((() => {
|
|
47
|
+
FUNCTION_PLACEHOLDER = "__worker_lib_function__";
|
|
48
|
+
isPlaceholder = (v) => v && typeof v === "object" && "__worker_lib_function__" in v;
|
|
49
|
+
isPlainObject = (v) => {
|
|
50
|
+
return v && typeof v === "object" && Object.prototype.toString.call(v) === "[object Object]" && !(v instanceof Uint8Array) && !(v instanceof ArrayBuffer) && !ArrayBuffer.isView(v);
|
|
51
|
+
};
|
|
52
|
+
getTransferables = (v, result = []) => {
|
|
53
|
+
if (v instanceof ArrayBuffer) result.push(v);
|
|
54
|
+
else if (ArrayBuffer.isView(v)) result.push(v.buffer);
|
|
55
|
+
else if (Array.isArray(v)) for (const item of v) getTransferables(item, result);
|
|
56
|
+
else if (v && typeof v === "object") for (const key in v) getTransferables(v[key], result);
|
|
57
|
+
return result;
|
|
58
|
+
};
|
|
59
|
+
createRegistry = () => {
|
|
60
|
+
const callbacks = /* @__PURE__ */ new Map();
|
|
61
|
+
const callbackProxies = /* @__PURE__ */ new Map();
|
|
62
|
+
const registerCallback = (requestId, fn) => {
|
|
63
|
+
const id = `${requestId}:${Math.random().toString(36).slice(2)}`;
|
|
64
|
+
callbacks.set(id, fn);
|
|
65
|
+
return id;
|
|
66
|
+
};
|
|
67
|
+
const clearCallbacks = (requestId) => {
|
|
68
|
+
for (const key of callbacks.keys()) if (key.startsWith(`${requestId}:`)) callbacks.delete(key);
|
|
69
|
+
for (const key of callbackProxies.keys()) if (key.startsWith(`${requestId}:`)) callbackProxies.delete(key);
|
|
70
|
+
};
|
|
71
|
+
const transformArgs = (requestId, args) => {
|
|
72
|
+
if (typeof args === "function") return { [FUNCTION_PLACEHOLDER]: registerCallback(requestId, args) };
|
|
73
|
+
if (ArrayBuffer.isView(args) || args instanceof ArrayBuffer) return args;
|
|
74
|
+
if (Array.isArray(args)) return args.map((v) => transformArgs(requestId, v));
|
|
75
|
+
if (isPlainObject(args)) {
|
|
76
|
+
const result = {};
|
|
77
|
+
for (const key in args) result[key] = transformArgs(requestId, args[key]);
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
return args;
|
|
81
|
+
};
|
|
82
|
+
const resolveArgs = (requestId, args, createProxy) => {
|
|
83
|
+
if (isPlaceholder(args)) return createProxy(args[FUNCTION_PLACEHOLDER]);
|
|
84
|
+
if (Array.isArray(args)) return args.map((v) => resolveArgs(requestId, v, createProxy));
|
|
85
|
+
if (isPlainObject(args)) {
|
|
86
|
+
const result = {};
|
|
87
|
+
for (const key in args) result[key] = resolveArgs(requestId, args[key], createProxy);
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
return args;
|
|
91
|
+
};
|
|
92
|
+
return {
|
|
93
|
+
callbacks,
|
|
94
|
+
callbackProxies,
|
|
95
|
+
registerCallback,
|
|
96
|
+
clearCallbacks,
|
|
97
|
+
transformArgs,
|
|
98
|
+
resolveArgs
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
}));
|
|
102
|
+
//#endregion
|
|
103
|
+
//#region ../../node_modules/.pnpm/worker-lib@2.2.1/node_modules/worker-lib/dist/esm/pool.js
|
|
104
|
+
var requestIdCounter, exec, createWorkerPool, initUniversalWorker;
|
|
105
|
+
var init_pool = __esmMin((() => {
|
|
106
|
+
init_common();
|
|
107
|
+
requestIdCounter = 0;
|
|
108
|
+
exec = (worker, name, registry, ...args) => {
|
|
109
|
+
const requestId = requestIdCounter++;
|
|
110
|
+
const { callbacks, callbackProxies, clearCallbacks, transformArgs, resolveArgs } = registry;
|
|
111
|
+
return new Promise((resolve, reject) => {
|
|
112
|
+
const createProxy = (callbackId) => {
|
|
113
|
+
const key = `${requestId}:${callbackId}`;
|
|
114
|
+
if (callbackProxies.has(key)) return callbackProxies.get(key);
|
|
115
|
+
const proxy = (...proxyArgs) => {
|
|
116
|
+
const callId = Math.random().toString(36).slice(2);
|
|
117
|
+
return new Promise((res) => {
|
|
118
|
+
const handler = (data) => {
|
|
119
|
+
if (data.type === "callback_result" && data.payload.id === callId) {
|
|
120
|
+
worker.removeEventListener("message", handler);
|
|
121
|
+
res(resolveArgs(requestId, data.payload.result, createProxy));
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
worker.addEventListener("message", handler);
|
|
125
|
+
const transformedProxyArgs = transformArgs(requestId, proxyArgs);
|
|
126
|
+
worker.postMessage({
|
|
127
|
+
type: "callback_call",
|
|
128
|
+
payload: {
|
|
129
|
+
id: requestId,
|
|
130
|
+
callbackId,
|
|
131
|
+
args: transformedProxyArgs,
|
|
132
|
+
callId
|
|
133
|
+
}
|
|
134
|
+
}, getTransferables(transformedProxyArgs));
|
|
135
|
+
});
|
|
136
|
+
};
|
|
137
|
+
callbackProxies.set(key, proxy);
|
|
138
|
+
return proxy;
|
|
139
|
+
};
|
|
140
|
+
const messageHandler = async (data) => {
|
|
141
|
+
if (!data || typeof data !== "object") return;
|
|
142
|
+
const payload = data.payload;
|
|
143
|
+
if (payload?.id !== requestId) return;
|
|
144
|
+
if (data.type === "result") {
|
|
145
|
+
worker.removeEventListener("message", messageHandler);
|
|
146
|
+
const result = resolveArgs(requestId, payload.result, createProxy);
|
|
147
|
+
clearCallbacks(requestId);
|
|
148
|
+
resolve(result);
|
|
149
|
+
} else if (data.type === "error") {
|
|
150
|
+
worker.removeEventListener("message", messageHandler);
|
|
151
|
+
clearCallbacks(requestId);
|
|
152
|
+
reject(payload.error);
|
|
153
|
+
} else if (data.type === "callback_call") {
|
|
154
|
+
const { callbackId, args: callArgs, callId } = payload;
|
|
155
|
+
const fn = callbacks.get(callbackId);
|
|
156
|
+
if (fn) try {
|
|
157
|
+
const result = await fn(...resolveArgs(requestId, callArgs, createProxy));
|
|
158
|
+
const transformedResult = transformArgs(requestId, result);
|
|
159
|
+
worker.postMessage({
|
|
160
|
+
type: "callback_result",
|
|
161
|
+
payload: {
|
|
162
|
+
id: callId,
|
|
163
|
+
result: transformedResult
|
|
164
|
+
}
|
|
165
|
+
}, getTransferables(transformedResult));
|
|
166
|
+
} catch (e) {
|
|
167
|
+
console.error("[worker-lib] Callback execution failed:", e);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
worker.addEventListener("message", messageHandler);
|
|
172
|
+
const transformedArgs = transformArgs(requestId, args);
|
|
173
|
+
worker.postMessage({
|
|
174
|
+
type: "function",
|
|
175
|
+
payload: {
|
|
176
|
+
id: requestId,
|
|
177
|
+
name,
|
|
178
|
+
args: transformedArgs
|
|
179
|
+
}
|
|
180
|
+
}, getTransferables(transformedArgs));
|
|
181
|
+
});
|
|
182
|
+
};
|
|
183
|
+
createWorkerPool = (builder, limit = 4) => {
|
|
184
|
+
let workers = Array(limit).fill(void 0).map(() => ({}));
|
|
185
|
+
const emptyWaits = [];
|
|
186
|
+
let isEmptyWait = false;
|
|
187
|
+
const registry = createRegistry();
|
|
188
|
+
const getResolver = async () => {
|
|
189
|
+
while (true) {
|
|
190
|
+
const target = workers.find(({ resultResolver }) => !resultResolver);
|
|
191
|
+
if (target) {
|
|
192
|
+
target.resultResolver = Promise.withResolvers();
|
|
193
|
+
if (!target.worker) target.worker = await builder();
|
|
194
|
+
return target;
|
|
195
|
+
}
|
|
196
|
+
await Promise.race(workers.map(({ resultResolver }) => resultResolver?.promise));
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
const execute = async (name, ...value) => {
|
|
200
|
+
const target = await getResolver();
|
|
201
|
+
const { resultResolver } = target;
|
|
202
|
+
if (!resultResolver) throw new Error("Unexpected error");
|
|
203
|
+
exec(target.worker, name, registry, ...value).then(resultResolver.resolve).catch(resultResolver.reject).finally(() => {
|
|
204
|
+
target.resultResolver = void 0;
|
|
205
|
+
});
|
|
206
|
+
return resultResolver.promise;
|
|
207
|
+
};
|
|
208
|
+
const launchWorker = async () => {
|
|
209
|
+
return Promise.all(workers.map(async (target) => {
|
|
210
|
+
if (!target.worker) target.worker = await builder();
|
|
211
|
+
}));
|
|
212
|
+
};
|
|
213
|
+
const waitAll = async () => {
|
|
214
|
+
while (workers.find(({ resultResolver }) => resultResolver)) await Promise.all(workers.flatMap(({ resultResolver }) => resultResolver ? [resultResolver.promise] : []));
|
|
215
|
+
};
|
|
216
|
+
const waitReady = async (retryTime = 1) => {
|
|
217
|
+
const p = Promise.withResolvers();
|
|
218
|
+
emptyWaits.push(p);
|
|
219
|
+
(async () => {
|
|
220
|
+
if (!isEmptyWait) {
|
|
221
|
+
isEmptyWait = true;
|
|
222
|
+
do {
|
|
223
|
+
const actives = workers.flatMap(({ resultResolver }) => resultResolver ? [resultResolver.promise] : []);
|
|
224
|
+
if (workers.length === actives.length) await Promise.race(actives);
|
|
225
|
+
emptyWaits.shift()?.resolve();
|
|
226
|
+
if (retryTime) await new Promise((r) => setTimeout(r, retryTime));
|
|
227
|
+
else await Promise.resolve();
|
|
228
|
+
} while (emptyWaits.length);
|
|
229
|
+
isEmptyWait = false;
|
|
230
|
+
}
|
|
231
|
+
})();
|
|
232
|
+
return p.promise;
|
|
233
|
+
};
|
|
234
|
+
const close = () => {
|
|
235
|
+
for (const target of workers) {
|
|
236
|
+
target.worker?.terminate();
|
|
237
|
+
target.worker = void 0;
|
|
238
|
+
target.resultResolver = void 0;
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
const setLimit = (newLimit) => {
|
|
242
|
+
workers.forEach((w) => w.worker?.terminate());
|
|
243
|
+
workers = Array(newLimit).fill(void 0).map(() => ({}));
|
|
244
|
+
};
|
|
245
|
+
return {
|
|
246
|
+
execute,
|
|
247
|
+
waitAll,
|
|
248
|
+
waitReady,
|
|
249
|
+
close,
|
|
250
|
+
setLimit,
|
|
251
|
+
launchWorker
|
|
252
|
+
};
|
|
253
|
+
};
|
|
254
|
+
initUniversalWorker = (worker, WorkerProc) => {
|
|
255
|
+
const { callbacks, callbackProxies, registerCallback, clearCallbacks, transformArgs, resolveArgs } = createRegistry();
|
|
256
|
+
const createProxy = (callbackId, requestId) => {
|
|
257
|
+
const key = `${requestId}:${callbackId}`;
|
|
258
|
+
if (callbackProxies.has(key)) return callbackProxies.get(key);
|
|
259
|
+
const proxy = (...proxyArgs) => {
|
|
260
|
+
const callId = Math.random().toString(36).slice(2);
|
|
261
|
+
return new Promise((res) => {
|
|
262
|
+
const handler = (data) => {
|
|
263
|
+
if (data.type === "callback_result" && data.payload.id === callId) {
|
|
264
|
+
worker.removeEventListener("message", handler);
|
|
265
|
+
res(resolveArgs(requestId, data.payload.result, (cbId) => createProxy(cbId, requestId)));
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
worker.addEventListener("message", handler);
|
|
269
|
+
const transformedArgs = transformArgs(requestId, proxyArgs);
|
|
270
|
+
worker.postMessage({
|
|
271
|
+
type: "callback_call",
|
|
272
|
+
payload: {
|
|
273
|
+
id: requestId,
|
|
274
|
+
callbackId,
|
|
275
|
+
args: transformedArgs,
|
|
276
|
+
callId
|
|
277
|
+
}
|
|
278
|
+
}, getTransferables(transformedArgs));
|
|
279
|
+
});
|
|
280
|
+
};
|
|
281
|
+
callbackProxies.set(key, proxy);
|
|
282
|
+
return proxy;
|
|
283
|
+
};
|
|
284
|
+
worker.addEventListener("message", async (data) => {
|
|
285
|
+
if (!data) return;
|
|
286
|
+
if (data.type === "function") {
|
|
287
|
+
const { id, name, args } = data.payload;
|
|
288
|
+
const proc = WorkerProc[name];
|
|
289
|
+
if (proc) try {
|
|
290
|
+
const result = await proc(...resolveArgs(id, args, (cbId) => createProxy(cbId, id)));
|
|
291
|
+
const transformedResult = transformArgs(id, result);
|
|
292
|
+
worker.postMessage({
|
|
293
|
+
type: "result",
|
|
294
|
+
payload: {
|
|
295
|
+
id,
|
|
296
|
+
result: transformedResult
|
|
297
|
+
}
|
|
298
|
+
}, getTransferables(transformedResult));
|
|
299
|
+
clearCallbacks(id);
|
|
300
|
+
} catch (error) {
|
|
301
|
+
worker.postMessage({
|
|
302
|
+
type: "error",
|
|
303
|
+
payload: {
|
|
304
|
+
id,
|
|
305
|
+
error: String(error)
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
clearCallbacks(id);
|
|
309
|
+
}
|
|
310
|
+
} else if (data.type === "callback_call") {
|
|
311
|
+
const { id, callbackId, args, callId } = data.payload;
|
|
312
|
+
const fn = callbacks.get(callbackId);
|
|
313
|
+
if (fn) try {
|
|
314
|
+
const result = await fn(...resolveArgs(id, args, (cbId) => createProxy(cbId, id)));
|
|
315
|
+
const transformedResult = transformArgs(id, result);
|
|
316
|
+
worker.postMessage({
|
|
317
|
+
type: "callback_result",
|
|
318
|
+
payload: {
|
|
319
|
+
id: callId,
|
|
320
|
+
result: transformedResult
|
|
321
|
+
}
|
|
322
|
+
}, getTransferables(transformedResult));
|
|
323
|
+
} catch (e) {
|
|
324
|
+
console.error("[worker-lib] Worker-side callback failed:", e);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
worker.postMessage("ready");
|
|
329
|
+
return WorkerProc;
|
|
330
|
+
};
|
|
331
|
+
}));
|
|
332
|
+
//#endregion
|
|
333
|
+
//#region ../../node_modules/.pnpm/worker-lib@2.2.1/node_modules/worker-lib/dist/esm/index.js
|
|
334
|
+
var esm_exports = /* @__PURE__ */ __exportAll({
|
|
335
|
+
Worker: () => w,
|
|
336
|
+
createWorker: () => createWorker$1,
|
|
337
|
+
initWorker: () => initWorker
|
|
338
|
+
});
|
|
339
|
+
var w, wrapWorker, init, createWorker$1, initWorker;
|
|
340
|
+
var init_esm = __esmMin((() => {
|
|
341
|
+
init_pool();
|
|
342
|
+
w = Worker;
|
|
343
|
+
wrapWorker = (worker) => ({
|
|
344
|
+
postMessage: (message, transfer) => worker.postMessage(message, transfer),
|
|
345
|
+
addEventListener: (type, listener) => {
|
|
346
|
+
const handler = (e) => {
|
|
347
|
+
if (e instanceof MessageEvent) listener(e.data);
|
|
348
|
+
};
|
|
349
|
+
listener._handler = handler;
|
|
350
|
+
worker.addEventListener(type, handler);
|
|
351
|
+
},
|
|
352
|
+
removeEventListener: (type, listener) => {
|
|
353
|
+
const handler = listener._handler;
|
|
354
|
+
worker.removeEventListener(type, handler);
|
|
355
|
+
},
|
|
356
|
+
terminate: () => worker.terminate()
|
|
357
|
+
});
|
|
358
|
+
init = (worker) => {
|
|
359
|
+
return new Promise((resolve) => {
|
|
360
|
+
const handler = (e) => {
|
|
361
|
+
if (e.data === "ready") {
|
|
362
|
+
worker.removeEventListener("message", handler);
|
|
363
|
+
resolve(worker);
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
worker.addEventListener("message", handler);
|
|
367
|
+
});
|
|
368
|
+
};
|
|
369
|
+
createWorker$1 = (builder, limit = 4) => {
|
|
370
|
+
return createWorkerPool(async () => {
|
|
371
|
+
const result = builder();
|
|
372
|
+
const worker = result instanceof Worker ? result : new Worker(result);
|
|
373
|
+
await init(worker);
|
|
374
|
+
return wrapWorker(worker);
|
|
375
|
+
}, limit);
|
|
376
|
+
};
|
|
377
|
+
initWorker = (WorkerProc) => {
|
|
378
|
+
const worker = self;
|
|
379
|
+
return initUniversalWorker({
|
|
380
|
+
postMessage: (message, transfer) => worker.postMessage(message, transfer),
|
|
381
|
+
addEventListener: (type, listener) => {
|
|
382
|
+
const handler = (e) => {
|
|
383
|
+
if (e instanceof MessageEvent) listener(e.data);
|
|
384
|
+
};
|
|
385
|
+
listener._handler = handler;
|
|
386
|
+
worker.addEventListener(type, handler);
|
|
387
|
+
},
|
|
388
|
+
removeEventListener: (type, listener) => {
|
|
389
|
+
const handler = listener._handler;
|
|
390
|
+
worker.removeEventListener(type, handler);
|
|
391
|
+
},
|
|
392
|
+
terminate: () => {}
|
|
393
|
+
}, WorkerProc);
|
|
394
|
+
};
|
|
395
|
+
}));
|
|
396
|
+
//#endregion
|
|
397
|
+
//#region src/worker-lib-loader.ts
|
|
398
|
+
let cached;
|
|
399
|
+
/**
|
|
400
|
+
* Load worker-lib, tolerating its broken native-ESM build: the published
|
|
401
|
+
* `dist/esm/*.js` uses extensionless relative imports (`./pool`), which
|
|
402
|
+
* throw `ERR_MODULE_NOT_FOUND` under plain Node (2.2.0/2.2.1; upstream
|
|
403
|
+
* dodges this via bundler resolution). Plain Node falls back to the CJS
|
|
404
|
+
* build via `createRequire`, which resolves fine.
|
|
405
|
+
*
|
|
406
|
+
* No `@vite-ignore` on the first import so bundlers still substitute the
|
|
407
|
+
* browser build for browser targets.
|
|
408
|
+
*/
|
|
409
|
+
function loadWorkerLib() {
|
|
410
|
+
if (!cached) {
|
|
411
|
+
cached = (async () => {
|
|
412
|
+
try {
|
|
413
|
+
return await Promise.resolve().then(() => (init_esm(), esm_exports));
|
|
414
|
+
} catch {
|
|
415
|
+
return (await importNode("module")).createRequire(import.meta.url)("worker-lib");
|
|
416
|
+
}
|
|
417
|
+
})();
|
|
418
|
+
cached.catch(() => {
|
|
419
|
+
resetWorkerLib();
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
return cached;
|
|
423
|
+
}
|
|
424
|
+
/** Drop the cached load (e.g. after a failed load) so it can be retried. */
|
|
425
|
+
function resetWorkerLib() {
|
|
426
|
+
cached = void 0;
|
|
427
|
+
}
|
|
428
|
+
//#endregion
|
|
429
|
+
//#region src/diagnostics.ts
|
|
430
|
+
/**
|
|
431
|
+
* Playground-parity diagnostics (satoru-compatible shapes).
|
|
432
|
+
*
|
|
433
|
+
* `LogLevel` mirrors `satoru/log-level.ts` verbatim (numeric levels shared
|
|
434
|
+
* with the WASM `set_log_level` binding). `RenderDiagnostics` mirrors
|
|
435
|
+
* satoru's `RenderDiagnostics` so playground-style consumers can switch
|
|
436
|
+
* imports without reshaping reports.
|
|
437
|
+
*/
|
|
438
|
+
/** Log severity, shared with the WASM `set_log_level` binding. */
|
|
439
|
+
let LogLevel = /* @__PURE__ */ function(LogLevel) {
|
|
440
|
+
LogLevel[LogLevel["None"] = 0] = "None";
|
|
441
|
+
LogLevel[LogLevel["Error"] = 1] = "Error";
|
|
442
|
+
LogLevel[LogLevel["Warning"] = 2] = "Warning";
|
|
443
|
+
LogLevel[LogLevel["Info"] = 3] = "Info";
|
|
444
|
+
LogLevel[LogLevel["Debug"] = 4] = "Debug";
|
|
445
|
+
return LogLevel;
|
|
446
|
+
}({});
|
|
447
|
+
/** Machine-readable diagnostic codes (satoru parity). */
|
|
448
|
+
const DIAGNOSTIC_CODES = {
|
|
449
|
+
LIMIT_TIMEOUT: "LIMIT_TIMEOUT",
|
|
450
|
+
LIMIT_RESOURCE_SIZE: "LIMIT_RESOURCE_SIZE",
|
|
451
|
+
LIMIT_TOTAL_SIZE: "LIMIT_TOTAL_SIZE",
|
|
452
|
+
LIMIT_RESOURCE_COUNT: "LIMIT_RESOURCE_COUNT",
|
|
453
|
+
LIMIT_PROTOCOL_BLOCKED: "LIMIT_PROTOCOL_BLOCKED",
|
|
454
|
+
LIMIT_HOST_BLOCKED: "LIMIT_HOST_BLOCKED",
|
|
455
|
+
/** Extension: a resource fetch (or resolveResource hook) failed. */
|
|
456
|
+
RESOURCE_FETCH_FAILED: "RESOURCE_FETCH_FAILED"
|
|
457
|
+
};
|
|
458
|
+
//#endregion
|
|
459
|
+
//#region src/core.ts
|
|
460
|
+
const EMOJI_URL = "https://cdn.jsdelivr.net/npm/@fontsource/noto-color-emoji/files/noto-color-emoji-emoji-400-normal.woff2";
|
|
461
|
+
/**
|
|
462
|
+
* Upstream default font strategy (verbatim from satoru `DEFAULT_FONT_MAP`):
|
|
463
|
+
* generic families resolve to Google Fonts css2 URLs (fetched with a browser
|
|
464
|
+
* UA so woff2 is served); fetched at render time, nothing bundled.
|
|
465
|
+
*/
|
|
466
|
+
const DEFAULT_FONT_MAP = {
|
|
467
|
+
"sans-serif": "https://fonts.googleapis.com/css2?family=Noto+Sans+JP",
|
|
468
|
+
serif: "https://fonts.googleapis.com/css2?family=Noto+Serif+JP",
|
|
469
|
+
monospace: "https://fonts.googleapis.com/css2?family=M+PLUS+1+Code",
|
|
470
|
+
cursive: "https://fonts.googleapis.com/css2?family=Yuji+Syuku",
|
|
471
|
+
fantasy: "https://fonts.googleapis.com/css2?family=Reggae+One",
|
|
472
|
+
"Noto Color Emoji": EMOJI_URL,
|
|
473
|
+
emoji: EMOJI_URL,
|
|
474
|
+
"Noto Emoji": EMOJI_URL,
|
|
475
|
+
notocoloremoji: EMOJI_URL,
|
|
476
|
+
notoemoji: EMOJI_URL
|
|
477
|
+
};
|
|
478
|
+
//#endregion
|
|
479
|
+
//#region src/workers.ts
|
|
480
|
+
const { createWorker, Worker: Worker$1 } = await loadWorkerLib();
|
|
481
|
+
/**
|
|
482
|
+
* Create a wasm-html-to-image worker pool using worker-lib.
|
|
483
|
+
* The pooled action is the unified `render` (HTML and image inputs).
|
|
484
|
+
* Each worker thread loads its own module instance via the loader
|
|
485
|
+
* (modules cannot cross thread boundaries).
|
|
486
|
+
*
|
|
487
|
+
* @param params Initialization parameters
|
|
488
|
+
* @param params.worker Optional: Path to the worker file, a URL, or a factory function.
|
|
489
|
+
* Defaults to the pre-bundled browser worker
|
|
490
|
+
* (`./web-workers.js`, SINGLE_FILE inlined) when
|
|
491
|
+
* `window` exists, else the Node worker
|
|
492
|
+
* (`./child-workers.js`) next to this file.
|
|
493
|
+
* @param params.maxParallel Maximum number of parallel workers (default: 4)
|
|
494
|
+
* @param params.timeoutMs Optional: Default timeout in milliseconds for each render job.
|
|
495
|
+
* If a render job times out, the pool is reset to prevent hung workers.
|
|
496
|
+
*/
|
|
497
|
+
const createHtmlToImageWorker = (params) => {
|
|
498
|
+
const { worker, maxParallel = 4, timeoutMs } = params ?? {};
|
|
499
|
+
const factory = () => {
|
|
500
|
+
let w;
|
|
501
|
+
if (worker) w = typeof worker === "function" ? worker() : worker;
|
|
502
|
+
else {
|
|
503
|
+
const workerUrl = typeof window !== "undefined" ? new URL("./web-workers.js", import.meta.url) : new URL("./child-workers.js", import.meta.url);
|
|
504
|
+
if (typeof Worker$1 !== "undefined") w = new Worker$1(workerUrl, { type: "module" });
|
|
505
|
+
}
|
|
506
|
+
if (!w) throw new Error("Worker is not supported in this environment.");
|
|
507
|
+
return w;
|
|
508
|
+
};
|
|
509
|
+
const workerInstance = createWorker(factory, maxParallel);
|
|
510
|
+
let totalPendingJobs = 0;
|
|
511
|
+
let completedJobs = 0;
|
|
512
|
+
let failedJobs = 0;
|
|
513
|
+
let totalJobTimeMs = 0;
|
|
514
|
+
/**
|
|
515
|
+
* Retrieve operational stats of the worker pool.
|
|
516
|
+
*/
|
|
517
|
+
const getStats = () => ({
|
|
518
|
+
workerCount: maxParallel,
|
|
519
|
+
activeJobs: Math.min(totalPendingJobs, maxParallel),
|
|
520
|
+
queuedJobs: Math.max(0, totalPendingJobs - maxParallel),
|
|
521
|
+
completedJobs,
|
|
522
|
+
failedJobs,
|
|
523
|
+
avgJobTimeMs: completedJobs > 0 ? totalJobTimeMs / completedJobs : 0
|
|
524
|
+
});
|
|
525
|
+
/**
|
|
526
|
+
* Reset the worker pool by terminating all running workers and recreating them.
|
|
527
|
+
* Useful to clear hung workers or reset statistics.
|
|
528
|
+
*/
|
|
529
|
+
const reset = () => {
|
|
530
|
+
workerInstance.setLimit(0);
|
|
531
|
+
workerInstance.setLimit(maxParallel);
|
|
532
|
+
totalPendingJobs = 0;
|
|
533
|
+
completedJobs = 0;
|
|
534
|
+
failedJobs = 0;
|
|
535
|
+
totalJobTimeMs = 0;
|
|
536
|
+
};
|
|
537
|
+
return new Proxy(workerInstance, { get(target, prop, receiver) {
|
|
538
|
+
if (prop === "getStats") return getStats;
|
|
539
|
+
if (prop === "reset") return reset;
|
|
540
|
+
if (prop === "render") return async (options) => {
|
|
541
|
+
totalPendingJobs++;
|
|
542
|
+
const startTime = Date.now();
|
|
543
|
+
let timeoutId;
|
|
544
|
+
let timeoutPromise;
|
|
545
|
+
if (timeoutMs !== void 0 && timeoutMs > 0) timeoutPromise = new Promise((_, reject) => {
|
|
546
|
+
timeoutId = setTimeout(() => {
|
|
547
|
+
reset();
|
|
548
|
+
reject(/* @__PURE__ */ new Error(`Render timed out after ${timeoutMs}ms`));
|
|
549
|
+
}, timeoutMs);
|
|
550
|
+
});
|
|
551
|
+
let execOptions = options;
|
|
552
|
+
if (options && typeof options === "object") {
|
|
553
|
+
if (options.value instanceof Uint8Array) execOptions = {
|
|
554
|
+
...options,
|
|
555
|
+
value: options.value.slice()
|
|
556
|
+
};
|
|
557
|
+
else if (options.value instanceof ArrayBuffer) execOptions = {
|
|
558
|
+
...options,
|
|
559
|
+
value: options.value.slice(0)
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
try {
|
|
563
|
+
const executePromise = target.execute("render", execOptions);
|
|
564
|
+
const result = await (timeoutPromise ? Promise.race([executePromise, timeoutPromise]) : executePromise);
|
|
565
|
+
completedJobs++;
|
|
566
|
+
totalJobTimeMs += Date.now() - startTime;
|
|
567
|
+
return result;
|
|
568
|
+
} catch (e) {
|
|
569
|
+
failedJobs++;
|
|
570
|
+
throw e;
|
|
571
|
+
} finally {
|
|
572
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
573
|
+
totalPendingJobs--;
|
|
574
|
+
}
|
|
575
|
+
};
|
|
576
|
+
if (prop in target) return Reflect.get(target, prop, receiver);
|
|
577
|
+
} });
|
|
578
|
+
};
|
|
579
|
+
let defaultWorker;
|
|
580
|
+
/** Lazily create the shared single-slot pool on first use (not at import). */
|
|
581
|
+
function getDefaultWorker() {
|
|
582
|
+
if (!defaultWorker) defaultWorker = createHtmlToImageWorker({ maxParallel: 1 });
|
|
583
|
+
return defaultWorker;
|
|
584
|
+
}
|
|
585
|
+
const close = () => {
|
|
586
|
+
getDefaultWorker().close();
|
|
587
|
+
};
|
|
588
|
+
function render(options) {
|
|
589
|
+
return getDefaultWorker().render(options);
|
|
590
|
+
}
|
|
591
|
+
const launchWorker = () => getDefaultWorker().launchWorker();
|
|
592
|
+
const setLimit = (limit) => {
|
|
593
|
+
getDefaultWorker().setLimit(limit);
|
|
594
|
+
};
|
|
595
|
+
const waitAll = () => getDefaultWorker().waitAll();
|
|
596
|
+
const waitReady = (retryTime) => getDefaultWorker().waitReady(retryTime);
|
|
597
|
+
const reset = () => {
|
|
598
|
+
getDefaultWorker().reset();
|
|
599
|
+
};
|
|
600
|
+
const getStats = () => getDefaultWorker().getStats();
|
|
601
|
+
//#endregion
|
|
602
|
+
export { DEFAULT_FONT_MAP, DIAGNOSTIC_CODES, LogLevel, close, createHtmlToImageWorker, getStats, launchWorker, render, reset, setLimit, waitAll, waitReady };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export type { HtmlToImageWorker } from "./child-workers.js";
|
|
2
|
+
import { type HtmlToImageOptions, type RenderResult } from "./core.js";
|
|
3
|
+
export type { HtmlToImageOptions, RenderResult } from "./core.js";
|
|
4
|
+
export { DEFAULT_FONT_MAP, DIAGNOSTIC_CODES, LogLevel } from "./core.js";
|
|
5
|
+
export type { DiagnosticMessage, FontDiagnostic, RenderDiagnostics, RenderLimits, ResolveResourceHook, ResourceDiagnostic, } from "./core.js";
|
|
6
|
+
/** Operational stats of the worker pool. */
|
|
7
|
+
export interface WorkerPoolStats {
|
|
8
|
+
/** Number of workers in the pool */
|
|
9
|
+
workerCount: number;
|
|
10
|
+
/** Jobs currently being executed */
|
|
11
|
+
activeJobs: number;
|
|
12
|
+
/** Jobs waiting for a free worker */
|
|
13
|
+
queuedJobs: number;
|
|
14
|
+
/** Total jobs completed successfully since start */
|
|
15
|
+
completedJobs: number;
|
|
16
|
+
/** Total jobs that failed since start */
|
|
17
|
+
failedJobs: number;
|
|
18
|
+
/** Average time per job in milliseconds */
|
|
19
|
+
avgJobTimeMs: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Create a wasm-html-to-image worker pool using worker-lib.
|
|
23
|
+
* The pooled action is the unified `render` (HTML and image inputs).
|
|
24
|
+
* Each worker thread loads its own module instance via the loader
|
|
25
|
+
* (modules cannot cross thread boundaries).
|
|
26
|
+
*
|
|
27
|
+
* @param params Initialization parameters
|
|
28
|
+
* @param params.worker Optional: Path to the worker file, a URL, or a factory function.
|
|
29
|
+
* Defaults to the pre-bundled browser worker
|
|
30
|
+
* (`./web-workers.js`, SINGLE_FILE inlined) when
|
|
31
|
+
* `window` exists, else the Node worker
|
|
32
|
+
* (`./child-workers.js`) next to this file.
|
|
33
|
+
* @param params.maxParallel Maximum number of parallel workers (default: 4)
|
|
34
|
+
* @param params.timeoutMs Optional: Default timeout in milliseconds for each render job.
|
|
35
|
+
* If a render job times out, the pool is reset to prevent hung workers.
|
|
36
|
+
*/
|
|
37
|
+
export declare const createHtmlToImageWorker: (params?: {
|
|
38
|
+
worker?: string | URL | (() => Worker | string | URL);
|
|
39
|
+
maxParallel?: number;
|
|
40
|
+
timeoutMs?: number;
|
|
41
|
+
}) => Omit<{
|
|
42
|
+
execute: <K extends "render">(name: K, ...value: Parameters<{
|
|
43
|
+
render(options: HtmlToImageOptions): Promise<RenderResult<Uint8Array | string>>;
|
|
44
|
+
}[K]>) => Promise<Awaited<ReturnType<{
|
|
45
|
+
render(options: HtmlToImageOptions): Promise<RenderResult<Uint8Array | string>>;
|
|
46
|
+
}[K]>>>;
|
|
47
|
+
waitAll: () => Promise<void>;
|
|
48
|
+
waitReady: (retryTime?: number) => Promise<void>;
|
|
49
|
+
close: () => void;
|
|
50
|
+
setLimit: (newLimit: number) => void;
|
|
51
|
+
launchWorker: () => Promise<void[]>;
|
|
52
|
+
}, "execute"> & {
|
|
53
|
+
render(options: HtmlToImageOptions): Promise<RenderResult<Uint8Array | string>>;
|
|
54
|
+
} & {
|
|
55
|
+
/**
|
|
56
|
+
* Retrieve operational stats of the worker pool.
|
|
57
|
+
*/
|
|
58
|
+
getStats: () => WorkerPoolStats;
|
|
59
|
+
/**
|
|
60
|
+
* Reset the worker pool by terminating all running workers and recreating them.
|
|
61
|
+
*/
|
|
62
|
+
reset: () => void;
|
|
63
|
+
/**
|
|
64
|
+
* Close and terminate all workers in the pool immediately.
|
|
65
|
+
*/
|
|
66
|
+
close: () => void;
|
|
67
|
+
/**
|
|
68
|
+
* Wait for all running tasks in the pool to complete.
|
|
69
|
+
*/
|
|
70
|
+
waitAll: () => Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* Wait until there is an available worker slot in the pool.
|
|
73
|
+
*/
|
|
74
|
+
waitReady: (retryTime?: number) => Promise<void>;
|
|
75
|
+
};
|
|
76
|
+
export declare const close: () => void;
|
|
77
|
+
export declare function render(options: HtmlToImageOptions & {
|
|
78
|
+
format: "svg";
|
|
79
|
+
}): Promise<RenderResult<string>>;
|
|
80
|
+
export declare function render(options: HtmlToImageOptions): Promise<RenderResult<Uint8Array | string>>;
|
|
81
|
+
export declare const launchWorker: () => Promise<void[]>;
|
|
82
|
+
export declare const setLimit: (limit: number) => void;
|
|
83
|
+
export declare const waitAll: () => Promise<void>;
|
|
84
|
+
export declare const waitReady: (retryTime?: number) => Promise<void>;
|
|
85
|
+
export declare const reset: () => void;
|
|
86
|
+
export declare const getStats: () => WorkerPoolStats;
|