satoru-render 1.0.11 → 1.0.13
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/CHANGELOG.md +113 -0
- package/README.md +76 -9
- package/dist/cli.js +60 -0
- package/dist/core.d.ts +105 -0
- package/dist/core.js +227 -33
- package/dist/resources.d.ts +60 -0
- package/dist/resources.js +175 -0
- package/dist/satoru-single.js +0 -0
- package/dist/satoru.js +2 -2
- package/dist/satoru.wasm +0 -0
- package/dist/web-workers.js +473 -247
- package/dist/workers-parent.js +316 -37
- package/dist/workers.d.ts +39 -4
- package/dist/workers.js +104 -5
- package/package.json +15 -7
package/dist/workers.js
CHANGED
|
@@ -9,8 +9,18 @@ export * from "./log-level.js";
|
|
|
9
9
|
* Defaults to the bundled workers.js in the same directory.
|
|
10
10
|
* @param params.maxParallel Maximum number of parallel workers
|
|
11
11
|
*/
|
|
12
|
+
/**
|
|
13
|
+
* Create a Satoru worker proxy using worker-lib.
|
|
14
|
+
* @param params Initialization parameters
|
|
15
|
+
* @param params.worker Optional: Path to the worker file, a URL, or a factory function.
|
|
16
|
+
* Defaults to the bundled workers.js in the same directory.
|
|
17
|
+
* @param params.maxParallel Maximum number of parallel workers (default: 4)
|
|
18
|
+
* @param params.timeoutMs Optional: Default timeout in milliseconds for each render job.
|
|
19
|
+
* If a render job times out, the pool is reset to prevent hung workers.
|
|
20
|
+
* @param params.onWorkerLog Optional: Global callback to intercept logs emitted by the worker threads.
|
|
21
|
+
*/
|
|
12
22
|
export const createSatoruWorker = (params) => {
|
|
13
|
-
const { worker, maxParallel = 4 } = params ?? {};
|
|
23
|
+
const { worker, maxParallel = 4, timeoutMs, onWorkerLog } = params ?? {};
|
|
14
24
|
const factory = () => {
|
|
15
25
|
let w;
|
|
16
26
|
if (worker) {
|
|
@@ -29,20 +39,109 @@ export const createSatoruWorker = (params) => {
|
|
|
29
39
|
return w;
|
|
30
40
|
};
|
|
31
41
|
const workerInstance = createWorker(factory, maxParallel);
|
|
42
|
+
let totalPendingJobs = 0;
|
|
43
|
+
let completedJobs = 0;
|
|
44
|
+
let failedJobs = 0;
|
|
45
|
+
let totalJobTimeMs = 0;
|
|
46
|
+
/**
|
|
47
|
+
* Retrieve operational stats of the worker pool.
|
|
48
|
+
*/
|
|
49
|
+
const getStats = () => ({
|
|
50
|
+
workerCount: maxParallel,
|
|
51
|
+
activeJobs: Math.min(totalPendingJobs, maxParallel),
|
|
52
|
+
queuedJobs: Math.max(0, totalPendingJobs - maxParallel),
|
|
53
|
+
completedJobs,
|
|
54
|
+
failedJobs,
|
|
55
|
+
avgJobTimeMs: completedJobs > 0 ? totalJobTimeMs / completedJobs : 0,
|
|
56
|
+
});
|
|
57
|
+
/**
|
|
58
|
+
* Reset the worker pool by terminating all running workers and recreating them.
|
|
59
|
+
* Useful to clear hung workers or reset statistics.
|
|
60
|
+
*/
|
|
61
|
+
const reset = () => {
|
|
62
|
+
workerInstance.setLimit(0);
|
|
63
|
+
workerInstance.setLimit(maxParallel);
|
|
64
|
+
totalPendingJobs = 0;
|
|
65
|
+
completedJobs = 0;
|
|
66
|
+
failedJobs = 0;
|
|
67
|
+
totalJobTimeMs = 0;
|
|
68
|
+
};
|
|
32
69
|
const proxy = new Proxy(workerInstance, {
|
|
33
70
|
get(target, prop, receiver) {
|
|
71
|
+
if (prop === "getStats") {
|
|
72
|
+
return getStats;
|
|
73
|
+
}
|
|
74
|
+
if (prop === "reset") {
|
|
75
|
+
return reset;
|
|
76
|
+
}
|
|
34
77
|
if (prop === "render") {
|
|
35
78
|
return async (options) => {
|
|
36
|
-
|
|
79
|
+
totalPendingJobs++;
|
|
80
|
+
const startTime = Date.now();
|
|
81
|
+
const jobTimeoutMs = options.limits?.timeoutMs ?? timeoutMs;
|
|
82
|
+
// Merge the user's specific onLog callback with the global onWorkerLog callback
|
|
83
|
+
const originalOnLog = options.onLog;
|
|
84
|
+
const mergedOnLog = (level, message) => {
|
|
85
|
+
if (originalOnLog) {
|
|
86
|
+
originalOnLog(level, message);
|
|
87
|
+
}
|
|
88
|
+
if (onWorkerLog) {
|
|
89
|
+
onWorkerLog(level, message);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
const mergedOptions = {
|
|
93
|
+
...options,
|
|
94
|
+
onLog: mergedOnLog,
|
|
95
|
+
};
|
|
96
|
+
let timeoutId;
|
|
97
|
+
let timeoutPromise;
|
|
98
|
+
if (jobTimeoutMs !== undefined && jobTimeoutMs > 0) {
|
|
99
|
+
timeoutPromise = new Promise((_, reject) => {
|
|
100
|
+
timeoutId = setTimeout(() => {
|
|
101
|
+
// If a job times out, terminate and recreate workers to recover from a potential hang
|
|
102
|
+
reset();
|
|
103
|
+
reject(new Error(`Render timed out after ${jobTimeoutMs}ms`));
|
|
104
|
+
}, jobTimeoutMs);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
const executePromise = target.execute("render", mergedOptions);
|
|
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
|
+
}
|
|
37
126
|
};
|
|
38
127
|
}
|
|
39
128
|
if (prop in target) {
|
|
40
129
|
return Reflect.get(target, prop, receiver);
|
|
41
130
|
}
|
|
42
|
-
return (...args) =>
|
|
131
|
+
return async (...args) => {
|
|
132
|
+
totalPendingJobs++;
|
|
133
|
+
try {
|
|
134
|
+
return await target.execute(prop, ...args);
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
totalPendingJobs--;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
43
140
|
},
|
|
44
141
|
});
|
|
45
142
|
return proxy;
|
|
46
143
|
};
|
|
47
|
-
const
|
|
48
|
-
export { close, render, launchWorker, setLimit, waitAll, waitReady };
|
|
144
|
+
const defaultWorker = createSatoruWorker({ maxParallel: 1 });
|
|
145
|
+
export const { close, render, launchWorker, setLimit, waitAll, waitReady } = defaultWorker;
|
|
146
|
+
export const reset = () => defaultWorker.reset();
|
|
147
|
+
export const getStats = () => defaultWorker.getStats();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "satoru-render",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.13",
|
|
4
4
|
"description": "High-fidelity HTML/CSS to SVG/PNG/PDF converter running in WebAssembly",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -8,10 +8,6 @@
|
|
|
8
8
|
"bin": {
|
|
9
9
|
"satoru-render": "./dist/cli.js"
|
|
10
10
|
},
|
|
11
|
-
"scripts": {
|
|
12
|
-
"build": "tsc -b && rolldown -c rolldown.config.js",
|
|
13
|
-
"deploy": "npm publish"
|
|
14
|
-
},
|
|
15
11
|
"exports": {
|
|
16
12
|
".": {
|
|
17
13
|
"workerd": {
|
|
@@ -64,6 +60,10 @@
|
|
|
64
60
|
"types": "./dist/tailwind.d.ts",
|
|
65
61
|
"import": "./dist/tailwind.js"
|
|
66
62
|
},
|
|
63
|
+
"./resources": {
|
|
64
|
+
"types": "./dist/resources.d.ts",
|
|
65
|
+
"import": "./dist/resources.js"
|
|
66
|
+
},
|
|
67
67
|
"./jsdom": {
|
|
68
68
|
"types": "./dist/jsdom.d.ts",
|
|
69
69
|
"import": "./dist/jsdom.js"
|
|
@@ -86,6 +86,9 @@
|
|
|
86
86
|
"tailwind": [
|
|
87
87
|
"./dist/tailwind.d.ts"
|
|
88
88
|
],
|
|
89
|
+
"resources": [
|
|
90
|
+
"./dist/resources.d.ts"
|
|
91
|
+
],
|
|
89
92
|
"jsdom": [
|
|
90
93
|
"./dist/jsdom.d.ts"
|
|
91
94
|
],
|
|
@@ -97,7 +100,8 @@
|
|
|
97
100
|
"files": [
|
|
98
101
|
"dist",
|
|
99
102
|
"README.md",
|
|
100
|
-
"LICENSE"
|
|
103
|
+
"LICENSE",
|
|
104
|
+
"CHANGELOG.md"
|
|
101
105
|
],
|
|
102
106
|
"repository": {
|
|
103
107
|
"type": "git",
|
|
@@ -164,5 +168,9 @@
|
|
|
164
168
|
"@unocss/preset-wind4": {
|
|
165
169
|
"optional": true
|
|
166
170
|
}
|
|
171
|
+
},
|
|
172
|
+
"scripts": {
|
|
173
|
+
"build": "tsc -b && rolldown -c rolldown.config.js",
|
|
174
|
+
"deploy": "npm publish"
|
|
167
175
|
}
|
|
168
|
-
}
|
|
176
|
+
}
|