blitz-pdf 0.1.7 → 0.1.9

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 (57) hide show
  1. package/README.md +44 -5
  2. package/THIRD_PARTY_NOTICES.md +1 -1
  3. package/dist/api.d.ts +2 -1
  4. package/dist/api.d.ts.map +1 -1
  5. package/dist/api.js +28 -3
  6. package/dist/api.js.map +1 -1
  7. package/dist/browser.d.ts +2 -1
  8. package/dist/browser.d.ts.map +1 -1
  9. package/dist/browser.js +1 -1
  10. package/dist/browser.js.map +1 -1
  11. package/dist/node.d.ts +2 -1
  12. package/dist/node.d.ts.map +1 -1
  13. package/dist/node.js +6 -2
  14. package/dist/node.js.map +1 -1
  15. package/dist/types.d.ts +14 -0
  16. package/dist/types.d.ts.map +1 -1
  17. package/dist/worker-host.d.ts +12 -5
  18. package/dist/worker-host.d.ts.map +1 -1
  19. package/dist/worker-host.js +187 -20
  20. package/dist/worker-host.js.map +1 -1
  21. package/dist/worker-protocol.d.ts +5 -0
  22. package/dist/worker-protocol.d.ts.map +1 -1
  23. package/dist/worker-runtime.d.ts +1 -1
  24. package/dist/worker-runtime.d.ts.map +1 -1
  25. package/dist/worker-runtime.js +22 -6
  26. package/dist/worker-runtime.js.map +1 -1
  27. package/licenses/RUST_DEPENDENCIES.txt +34 -40
  28. package/package.json +1 -1
  29. package/src/api.ts +29 -4
  30. package/src/browser.ts +2 -1
  31. package/src/node.ts +7 -2
  32. package/src/types.ts +15 -0
  33. package/src/worker-host.ts +176 -23
  34. package/src/worker-protocol.ts +5 -5
  35. package/src/worker-runtime.ts +20 -6
  36. package/vendor/README.md +19 -5
  37. package/vendor/blitz-dom/Cargo.toml +6 -6
  38. package/vendor/blitz-dom/src/accessibility.rs +175 -2
  39. package/vendor/blitz-dom/src/config.rs +8 -10
  40. package/vendor/blitz-dom/src/document.rs +66 -3
  41. package/vendor/blitz-dom/src/events/keyboard.rs +2 -6
  42. package/vendor/blitz-dom/src/layout/construct.rs +5 -300
  43. package/vendor/blitz-dom/src/layout/inline/build.rs +309 -0
  44. package/vendor/blitz-dom/src/layout/{inline.rs → inline/layout.rs} +1 -1
  45. package/vendor/blitz-dom/src/layout/inline/mod.rs +4 -0
  46. package/vendor/blitz-dom/src/layout/mod.rs +3 -1
  47. package/vendor/blitz-dom/src/layout/replaced.rs +13 -4
  48. package/vendor/blitz-dom/src/mutator.rs +47 -0
  49. package/vendor/blitz-dom/src/node/element.rs +17 -0
  50. package/vendor/blitz-dom/src/node/mod.rs +1 -0
  51. package/vendor/blitz-dom/src/node/node.rs +46 -5
  52. package/vendor/blitz-dom/src/node/text.rs +11 -11
  53. package/vendor/blitz-dom/src/resolve.rs +2 -2
  54. package/vendor/blitz-dom/src/stylo.rs +30 -22
  55. package/vendor/blitz-dom/src/util.rs +1 -1
  56. package/vendor/blitz-paint/Cargo.toml +4 -4
  57. package/wasm/blitz_pdf_bg.wasm +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"worker-runtime.d.ts","sourceRoot":"","sources":["../src/worker-runtime.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAEvE,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC;IAC5D,SAAS,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,GAAG,IAAI,CAAC;CAC1D;AAED,iGAAiG;AACjG,wBAAgB,KAAK,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAwBpD"}
1
+ {"version":3,"file":"worker-runtime.d.ts","sourceRoot":"","sources":["../src/worker-runtime.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAEvE,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC;IAC5D,SAAS,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,GAAG,IAAI,CAAC;CAC1D;AAED,wFAAwF;AACxF,wBAAgB,KAAK,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAsCpD"}
@@ -1,34 +1,50 @@
1
1
  import { initSync } from "../wasm/blitz_pdf.js";
2
2
  import { render } from "./render.js";
3
- /** Worker side of `renderPdfInWorker`: one render per worker; asset bytes come from the host. */
3
+ /** One document at a time; retain the initialized engine and its bundled-font cache. */
4
4
  export function serve(endpoint) {
5
+ let initialized = false;
6
+ let activeId;
5
7
  let receiveAssets;
6
8
  endpoint.onMessage(async (message) => {
7
9
  if (message.type === "assets") {
10
+ if (message.id !== activeId)
11
+ return;
8
12
  const receive = receiveAssets;
9
13
  receiveAssets = undefined;
10
14
  receive?.(message.data);
11
15
  return;
12
16
  }
17
+ if (activeId !== undefined) {
18
+ endpoint.post({ type: "error", id: message.id, error: new Error("Worker already has an active document") }, []);
19
+ return;
20
+ }
21
+ activeId = message.id;
13
22
  try {
14
- initSync({ module: message.module });
23
+ if (!initialized) {
24
+ initSync({ module: message.module });
25
+ initialized = true;
26
+ }
15
27
  const load = message.external
16
28
  ? (urls) => new Promise(resolve => {
17
29
  receiveAssets = resolve;
18
- endpoint.post({ type: "assets", urls }, []);
30
+ endpoint.post({ type: "assets", id: message.id, urls }, []);
19
31
  })
20
32
  : undefined;
21
33
  const pdf = await render(message.html, message.options, load);
22
- endpoint.post({ type: "done", pdf }, [pdf.buffer]);
34
+ endpoint.post({ type: "done", id: message.id, pdf }, [pdf.buffer]);
23
35
  }
24
36
  catch (error) {
25
37
  try {
26
- endpoint.post({ type: "error", error }, []);
38
+ endpoint.post({ type: "error", id: message.id, error }, []);
27
39
  }
28
40
  catch {
29
- endpoint.post({ type: "error", error: new Error(String(error)) }, []);
41
+ endpoint.post({ type: "error", id: message.id, error: new Error(String(error)) }, []);
30
42
  }
31
43
  }
44
+ finally {
45
+ activeId = undefined;
46
+ receiveAssets = undefined;
47
+ }
32
48
  });
33
49
  }
34
50
  //# sourceMappingURL=worker-runtime.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"worker-runtime.js","sourceRoot":"","sources":["../src/worker-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAQrC,iGAAiG;AACjG,MAAM,UAAU,KAAK,CAAC,QAAwB;IAC5C,IAAI,aAAyD,CAAC;IAC9D,QAAQ,CAAC,SAAS,CAAC,KAAK,EAAC,OAAO,EAAC,EAAE;QACjC,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC9B,MAAM,OAAO,GAAG,aAAa,CAAC;YAC9B,aAAa,GAAG,SAAS,CAAC;YAC1B,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACxB,OAAO;QACT,CAAC;QACD,IAAI,CAAC;YACH,QAAQ,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;YACrC,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ;gBAC3B,CAAC,CAAC,CAAC,IAAc,EAAE,EAAE,CAAC,IAAI,OAAO,CAAe,OAAO,CAAC,EAAE;oBACxD,aAAa,GAAG,OAAO,CAAC;oBACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;gBAC9C,CAAC,CAAC;gBACF,CAAC,CAAC,SAAS,CAAC;YACd,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YAC9D,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAqB,CAAC,CAAC,CAAC;QACpE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC;gBAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;YAAC,CAAC;YACpD,MAAM,CAAC;gBAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YAAC,CAAC;QAClF,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"worker-runtime.js","sourceRoot":"","sources":["../src/worker-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAQrC,wFAAwF;AACxF,MAAM,UAAU,KAAK,CAAC,QAAwB;IAC5C,IAAI,WAAW,GAAG,KAAK,CAAC;IACxB,IAAI,QAA4B,CAAC;IACjC,IAAI,aAAyD,CAAC;IAC9D,QAAQ,CAAC,SAAS,CAAC,KAAK,EAAC,OAAO,EAAC,EAAE;QACjC,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC9B,IAAI,OAAO,CAAC,EAAE,KAAK,QAAQ;gBAAE,OAAO;YACpC,MAAM,OAAO,GAAG,aAAa,CAAC;YAC9B,aAAa,GAAG,SAAS,CAAC;YAC1B,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACxB,OAAO;QACT,CAAC;QACD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,uCAAuC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YAChH,OAAO;QACT,CAAC;QACD,QAAQ,GAAG,OAAO,CAAC,EAAE,CAAC;QACtB,IAAI,CAAC;YACH,IAAI,CAAC,WAAW,EAAE,CAAC;gBACjB,QAAQ,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;gBACrC,WAAW,GAAG,IAAI,CAAC;YACrB,CAAC;YACD,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ;gBAC3B,CAAC,CAAC,CAAC,IAAc,EAAE,EAAE,CAAC,IAAI,OAAO,CAAe,OAAO,CAAC,EAAE;oBACxD,aAAa,GAAG,OAAO,CAAC;oBACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;gBAC9D,CAAC,CAAC;gBACF,CAAC,CAAC,SAAS,CAAC;YACd,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YAC9D,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAqB,CAAC,CAAC,CAAC;QACpF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC;gBAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;YAAC,CAAC;YACpE,MAAM,CAAC;gBAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YAAC,CAAC;QAClG,CAAC;gBAAS,CAAC;YACT,QAAQ,GAAG,SAAS,CAAC;YACrB,aAAa,GAAG,SAAS,CAAC;QAC5B,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -49,19 +49,19 @@ bitflags 2.13.2
49
49
  License: MIT OR Apache-2.0
50
50
  Source: https://github.com/bitflags/bitflags
51
51
 
52
- blitz-dom 0.3.0-beta.3
52
+ blitz-dom 0.3.0-beta.4
53
53
  License: MIT OR Apache-2.0
54
54
  Source: https://github.com/dioxuslabs/blitz
55
55
 
56
- blitz-html 0.3.0-beta.3
56
+ blitz-html 0.3.0-beta.4
57
57
  License: MIT OR Apache-2.0
58
58
  Source: https://github.com/dioxuslabs/blitz
59
59
 
60
- blitz-paint 0.3.0-beta.3
60
+ blitz-paint 0.3.0-beta.4
61
61
  License: MIT OR Apache-2.0
62
62
  Source: https://github.com/dioxuslabs/blitz
63
63
 
64
- blitz-traits 0.3.0-beta.3
64
+ blitz-traits 0.3.0-beta.4
65
65
  License: MIT OR Apache-2.0
66
66
  Source: https://github.com/dioxuslabs/blitz
67
67
 
@@ -121,7 +121,7 @@ core_maths 0.1.1
121
121
  License: MIT
122
122
  Source: https://github.com/robertbastian/core_maths
123
123
 
124
- crc32fast 1.5.2
124
+ crc32fast 1.5.3
125
125
  License: MIT OR Apache-2.0
126
126
  Source: https://github.com/srijs/rust-crc32fast
127
127
 
@@ -197,7 +197,7 @@ encoding_rs 0.8.42
197
197
  License: (Apache-2.0 OR MIT) AND BSD-3-Clause
198
198
  Source: https://github.com/hsivonen/encoding_rs
199
199
 
200
- equivalent 1.0.2
200
+ equivalent 1.0.3
201
201
  License: Apache-2.0 OR MIT
202
202
  Source: https://github.com/indexmap-rs/equivalent
203
203
 
@@ -233,7 +233,7 @@ font-types 0.11.3
233
233
  License: MIT OR Apache-2.0
234
234
  Source: https://github.com/googlefonts/fontations
235
235
 
236
- font-types 0.12.6
236
+ font-types 0.12.7
237
237
  License: MIT OR Apache-2.0
238
238
  Source: https://github.com/googlefonts/fontations
239
239
 
@@ -385,9 +385,9 @@ js-sys 0.3.106
385
385
  License: MIT OR Apache-2.0
386
386
  Source: https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/js-sys
387
387
 
388
- keyboard-types 0.7.0
388
+ keyboard-types 0.8.3
389
389
  License: MIT OR Apache-2.0
390
- Source: https://github.com/pyfisch/keyboard-types
390
+ Source: https://github.com/rust-windowing/keyboard-types
391
391
 
392
392
  krilla 0.8.2
393
393
  License: MIT OR Apache-2.0
@@ -397,7 +397,7 @@ kurbo 0.13.1
397
397
  License: Apache-2.0 OR MIT
398
398
  Source: https://github.com/linebender/kurbo
399
399
 
400
- libc 0.2.190
400
+ libc 0.2.191
401
401
  License: MIT OR Apache-2.0
402
402
  Source: https://github.com/rust-lang/libc
403
403
 
@@ -721,7 +721,7 @@ smallvec 1.16.3
721
721
  License: MIT OR Apache-2.0
722
722
  Source: https://github.com/servo/rust-smallvec
723
723
 
724
- smol_str 0.3.6
724
+ smol_str 0.3.7
725
725
  License: MIT OR Apache-2.0
726
726
  Source: https://github.com/rust-lang/rust-analyzer/tree/master/lib/smol_str
727
727
 
@@ -777,7 +777,7 @@ stylo_static_prefs 0.22.0
777
777
  License: MPL-2.0
778
778
  Source: https://github.com/servo/stylo
779
779
 
780
- stylo_taffy 0.3.0-beta.3
780
+ stylo_taffy 0.3.0-beta.4
781
781
  License: MIT OR Apache-2.0 OR MPL-2.0
782
782
  Source: https://github.com/dioxuslabs/blitz
783
783
 
@@ -877,10 +877,6 @@ unicode-script 0.5.8
877
877
  License: MIT OR Apache-2.0
878
878
  Source: https://github.com/unicode-rs/unicode-script
879
879
 
880
- unicode-segmentation 1.13.3
881
- License: MIT OR Apache-2.0
882
- Source: https://github.com/unicode-rs/unicode-segmentation
883
-
884
880
  unicode-vo 0.1.0
885
881
  License: MIT/Apache-2.0
886
882
  Source: https://github.com/RazrFalcon/unicode-vo
@@ -1263,7 +1259,7 @@ serde_bytes 0.11.19 (LICENSE-MIT)
1263
1259
  serde_core 1.0.229 (LICENSE-MIT)
1264
1260
  serde_derive 1.0.229 (LICENSE-MIT)
1265
1261
  servo_arc 0.5.0 (LICENSE-MIT)
1266
- smol_str 0.3.6 (LICENSE-MIT)
1262
+ smol_str 0.3.7 (LICENSE-MIT)
1267
1263
  stylo_malloc_size_of 0.22.0 (LICENSE-MIT)
1268
1264
  syn 2.0.119 (LICENSE-MIT)
1269
1265
  syn 3.0.9 (LICENSE-MIT)
@@ -1361,7 +1357,7 @@ crossbeam-utils 0.8.23 (LICENSE-APACHE)
1361
1357
  data-url 0.3.2 (LICENSE-APACHE)
1362
1358
  displaydoc 0.2.7 (LICENSE-APACHE)
1363
1359
  either 1.19.0 (LICENSE-APACHE)
1364
- equivalent 1.0.2 (LICENSE-APACHE)
1360
+ equivalent 1.0.3 (LICENSE-APACHE)
1365
1361
  euclid 0.22.14 (LICENSE-APACHE)
1366
1362
  fastrand 2.5.0 (LICENSE-APACHE)
1367
1363
  flate2 1.1.10 (LICENSE-APACHE)
@@ -1404,7 +1400,7 @@ servo_arc 0.5.0 (LICENSE-APACHE)
1404
1400
  simplecss 0.2.2 (LICENSE-APACHE)
1405
1401
  smallbitvec 2.6.1 (LICENSE-APACHE)
1406
1402
  smallvec 1.16.3 (LICENSE-APACHE)
1407
- smol_str 0.3.6 (LICENSE-APACHE)
1403
+ smol_str 0.3.7 (LICENSE-APACHE)
1408
1404
  stable_deref_trait 1.2.1 (LICENSE-APACHE)
1409
1405
  string_cache 0.11.0 (LICENSE-APACHE)
1410
1406
  string_cache_codegen 0.11.2 (LICENSE-APACHE)
@@ -1413,7 +1409,6 @@ svgtypes 0.16.1 (LICENSE-APACHE)
1413
1409
  tendril 0.5.1 (LICENSE-APACHE)
1414
1410
  thread_local 1.1.10 (LICENSE-APACHE)
1415
1411
  unicode-bidi 0.3.18 (LICENSE-APACHE)
1416
- unicode-segmentation 1.13.3 (LICENSE-APACHE)
1417
1412
  unicode-vo 0.1.0 (LICENSE-APACHE)
1418
1413
  url 2.5.8 (LICENSE-APACHE)
1419
1414
  version_check 0.9.5 (LICENSE-APACHE)
@@ -2016,13 +2011,13 @@ DEALINGS IN THE SOFTWARE.
2016
2011
 
2017
2012
 
2018
2013
  ========================================================================
2019
- blitz-dom 0.3.0-beta.3 (LICENSE-APACHE)
2020
- blitz-html 0.3.0-beta.3 (LICENSE-APACHE)
2021
- blitz-paint 0.3.0-beta.3 (LICENSE-APACHE)
2022
- blitz-traits 0.3.0-beta.3 (LICENSE-APACHE)
2014
+ blitz-dom 0.3.0-beta.4 (LICENSE-APACHE)
2015
+ blitz-html 0.3.0-beta.4 (LICENSE-APACHE)
2016
+ blitz-paint 0.3.0-beta.4 (LICENSE-APACHE)
2017
+ blitz-traits 0.3.0-beta.4 (LICENSE-APACHE)
2023
2018
  debug_timer 0.1.3 (LICENSE-APACHE)
2024
2019
  rustc-hash 2.1.3 (LICENSE-APACHE)
2025
- stylo_taffy 0.3.0-beta.3 (LICENSE-APACHE)
2020
+ stylo_taffy 0.3.0-beta.4 (LICENSE-APACHE)
2026
2021
  ========================================================================
2027
2022
 
2028
2023
  Apache License
@@ -2203,13 +2198,13 @@ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
2203
2198
  END OF TERMS AND CONDITIONS
2204
2199
 
2205
2200
  ========================================================================
2206
- blitz-dom 0.3.0-beta.3 (LICENSE-MIT)
2207
- blitz-html 0.3.0-beta.3 (LICENSE-MIT)
2208
- blitz-paint 0.3.0-beta.3 (LICENSE-MIT)
2209
- blitz-traits 0.3.0-beta.3 (LICENSE-MIT)
2201
+ blitz-dom 0.3.0-beta.4 (LICENSE-MIT)
2202
+ blitz-html 0.3.0-beta.4 (LICENSE-MIT)
2203
+ blitz-paint 0.3.0-beta.4 (LICENSE-MIT)
2204
+ blitz-traits 0.3.0-beta.4 (LICENSE-MIT)
2210
2205
  debug_timer 0.1.3 (LICENSE-MIT)
2211
2206
  rustc-hash 2.1.3 (LICENSE-MIT)
2212
- stylo_taffy 0.3.0-beta.3 (LICENSE-MIT)
2207
+ stylo_taffy 0.3.0-beta.4 (LICENSE-MIT)
2213
2208
  ========================================================================
2214
2209
 
2215
2210
  Permission is hereby granted, free of charge, to any
@@ -2946,7 +2941,7 @@ SOFTWARE.
2946
2941
  ========================================================================
2947
2942
  core_detect 1.0.0 (LICENSE-APACHE)
2948
2943
  gif 0.14.2 (LICENSE-APACHE)
2949
- keyboard-types 0.7.0 (LICENSE-APACHE)
2944
+ keyboard-types 0.8.3 (LICENSE-APACHE)
2950
2945
  weezl 0.1.12 (LICENSE-APACHE)
2951
2946
  ========================================================================
2952
2947
 
@@ -3210,7 +3205,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3210
3205
  SOFTWARE.
3211
3206
 
3212
3207
  ========================================================================
3213
- crc32fast 1.5.2 (LICENSE-APACHE)
3208
+ crc32fast 1.5.3 (LICENSE-APACHE)
3214
3209
  quick-error 2.0.1 (LICENSE-APACHE)
3215
3210
  ========================================================================
3216
3211
 
@@ -3419,7 +3414,7 @@ quick-error 2.0.1 (LICENSE-APACHE)
3419
3414
 
3420
3415
 
3421
3416
  ========================================================================
3422
- crc32fast 1.5.2 (LICENSE-MIT)
3417
+ crc32fast 1.5.3 (LICENSE-MIT)
3423
3418
  ========================================================================
3424
3419
 
3425
3420
  MIT License
@@ -4204,7 +4199,7 @@ SOFTWARE.
4204
4199
  ========================================================================
4205
4200
  dtoa 1.0.11 (LICENSE-APACHE)
4206
4201
  itoa 1.0.18 (LICENSE-APACHE)
4207
- libc 0.2.190 (LICENSE-APACHE)
4202
+ libc 0.2.191 (LICENSE-APACHE)
4208
4203
  parley_engine 0.12.0 (LICENSE-APACHE)
4209
4204
  pdf-writer 0.15.0 (LICENSE-APACHE)
4210
4205
  proc-macro2 1.0.107 (LICENSE-APACHE)
@@ -5095,7 +5090,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
5095
5090
 
5096
5091
 
5097
5092
  ========================================================================
5098
- equivalent 1.0.2 (LICENSE-MIT)
5093
+ equivalent 1.0.3 (LICENSE-MIT)
5099
5094
  ========================================================================
5100
5095
 
5101
5096
  Copyright (c) 2016--2023
@@ -5602,7 +5597,7 @@ DEALINGS IN THE SOFTWARE.
5602
5597
 
5603
5598
 
5604
5599
  ========================================================================
5605
- font-types 0.12.6 (LICENSE-APACHE)
5600
+ font-types 0.12.7 (LICENSE-APACHE)
5606
5601
  read-fonts 0.41.0 (LICENSE-APACHE)
5607
5602
  skrifa 0.44.0 (LICENSE-APACHE)
5608
5603
  ========================================================================
@@ -5677,7 +5672,7 @@ limitations under the License.
5677
5672
 
5678
5673
 
5679
5674
  ========================================================================
5680
- font-types 0.12.6 (LICENSE-MIT)
5675
+ font-types 0.12.7 (LICENSE-MIT)
5681
5676
  read-fonts 0.41.0 (LICENSE-MIT)
5682
5677
  skrifa 0.44.0 (LICENSE-MIT)
5683
5678
  ========================================================================
@@ -6158,7 +6153,6 @@ DEALINGS IN THE SOFTWARE.
6158
6153
  ========================================================================
6159
6154
  heck 0.5.0 (LICENSE-MIT)
6160
6155
  unicode-bidi 0.3.18 (LICENSE-MIT)
6161
- unicode-segmentation 1.13.3 (LICENSE-MIT)
6162
6156
  ========================================================================
6163
6157
 
6164
6158
  Copyright (c) 2015 The Rust Project Developers
@@ -6677,7 +6671,7 @@ DEALINGS IN THE SOFTWARE.
6677
6671
 
6678
6672
 
6679
6673
  ========================================================================
6680
- keyboard-types 0.7.0 (LICENSE-MIT)
6674
+ keyboard-types 0.8.3 (LICENSE-MIT)
6681
6675
  ========================================================================
6682
6676
 
6683
6677
  Copyright (c) 2017 Pyfisch
@@ -6760,7 +6754,7 @@ DEALINGS IN THE SOFTWARE.
6760
6754
 
6761
6755
 
6762
6756
  ========================================================================
6763
- libc 0.2.190 (LICENSE-MIT)
6757
+ libc 0.2.191 (LICENSE-MIT)
6764
6758
  ========================================================================
6765
6759
 
6766
6760
  Copyright (c) The Rust Project Developers
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blitz-pdf",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Render HTML and CSS to PDF with Blitz and WebAssembly in Node.js, Bun, and browsers.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/api.ts CHANGED
@@ -3,8 +3,8 @@ import { createAssetLoader } from "./assets.js";
3
3
  import { normalizeOptions } from "./options.js";
4
4
  import { render, validateHtml } from "./render.js";
5
5
  import { compileWasm } from "./wasm.js";
6
- import { renderInWorker, type SpawnWorker } from "./worker-host.js";
7
- import type { InitOptions, PdfOptions, WasmSource } from "./types.js";
6
+ import { createWorkerPool, renderInWorker, type SpawnWorker } from "./worker-host.js";
7
+ import type { InitOptions, PdfOptions, PdfWorkerPool, PdfWorkerPoolOptions, WasmSource } from "./types.js";
8
8
 
9
9
  export interface ApiConfig {
10
10
  defaultSource: () => Promise<WasmSource> | WasmSource;
@@ -43,7 +43,32 @@ export function createApi({ defaultSource, prepare = async source => source, spa
43
43
  return render(html, normalized, assetResolver && createAssetLoader(assetResolver));
44
44
  }
45
45
 
46
- /** Like `renderPdf`, but layout and serialization run in a new worker. An `assets` function still runs on the calling thread. */
46
+ /** Create an owned pool, with independent concurrency and shutdown. No workers start until needed. */
47
+ function createPdfWorkerPool(options: PdfWorkerPoolOptions = {}): PdfWorkerPool {
48
+ const pool = createWorkerPool(spawnWorker, options);
49
+ // Include compilation in accepted work so close also drains renders waiting for WASM.
50
+ const pending = new Set<Promise<Uint8Array>>();
51
+ let closing: Promise<void> | undefined;
52
+ return {
53
+ renderPdf(html: string, options: PdfOptions = {}): Promise<Uint8Array> {
54
+ if (closing) return Promise.reject(new Error("PDF worker pool is closed"));
55
+ const work = (async () => {
56
+ validateHtml(html);
57
+ const { assetResolver, ...normalized } = normalizeOptions(options);
58
+ return pool.render(await compile(), html, normalized, assetResolver);
59
+ })();
60
+ pending.add(work);
61
+ void work.then(() => pending.delete(work), () => pending.delete(work));
62
+ return work;
63
+ },
64
+ close(): Promise<void> {
65
+ closing ??= Promise.allSettled([...pending]).then(() => pool.close());
66
+ return closing;
67
+ },
68
+ };
69
+ }
70
+
71
+ /** Render in a fresh worker and release its engine before settling. */
47
72
  async function renderPdfInWorker(html: string, options: PdfOptions = {}): Promise<Uint8Array> {
48
73
  validateHtml(html);
49
74
  const { assetResolver, ...normalized } = normalizeOptions(options);
@@ -51,5 +76,5 @@ export function createApi({ defaultSource, prepare = async source => source, spa
51
76
  }
52
77
 
53
78
  async function getEngineVersion(): Promise<string> { await init(); return engine_version(); }
54
- return { init, renderPdf, renderPdfInWorker, getWasmModule, getEngineVersion };
79
+ return { init, renderPdf, renderPdfInWorker, createPdfWorkerPool, getWasmModule, getEngineVersion };
55
80
  }
package/src/browser.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { createApi } from "./api.js";
2
2
 
3
3
  export type { AssetData, AssetMap, AssetResolver, AttachmentRelationship, BinaryData, InitOptions, Length, PageFormat, PageNumberOptions, PdfAttachment, PdfDate, PdfMetadata, PdfOptions, PdfXmpProperty, PdfXmpSchema, WasmSource } from "./types.js";
4
- export const { init, renderPdf, renderPdfInWorker, getWasmModule, getEngineVersion } = createApi({
4
+ export type { PdfWorkerPool, PdfWorkerPoolOptions } from "./types.js";
5
+ export const { init, renderPdf, renderPdfInWorker, createPdfWorkerPool, getWasmModule, getEngineVersion } = createApi({
5
6
  defaultSource: () => new URL("../wasm/blitz_pdf_bg.wasm", import.meta.url),
6
7
  spawnWorker: events => {
7
8
  const worker = new Worker(new URL("./worker.js", import.meta.url), { type: "module" });
package/src/node.ts CHANGED
@@ -3,7 +3,8 @@ import { Worker } from "node:worker_threads";
3
3
  import { createApi } from "./api.js";
4
4
 
5
5
  export type { AssetData, AssetMap, AssetResolver, AttachmentRelationship, BinaryData, InitOptions, Length, PageFormat, PageNumberOptions, PdfAttachment, PdfDate, PdfMetadata, PdfOptions, PdfXmpProperty, PdfXmpSchema, WasmSource } from "./types.js";
6
- export const { init, renderPdf, renderPdfInWorker, getWasmModule, getEngineVersion } = createApi({
6
+ export type { PdfWorkerPool, PdfWorkerPoolOptions } from "./types.js";
7
+ export const { init, renderPdf, renderPdfInWorker, createPdfWorkerPool, getWasmModule, getEngineVersion } = createApi({
7
8
  defaultSource: () => readFile(new URL("../wasm/blitz_pdf_bg.wasm", import.meta.url)),
8
9
  prepare: async source => {
9
10
  if (source instanceof URL && source.protocol === "file:") return readFile(source);
@@ -16,6 +17,10 @@ export const { init, renderPdf, renderPdfInWorker, getWasmModule, getEngineVersi
16
17
  worker.on("error", events.error);
17
18
  worker.on("messageerror", events.error);
18
19
  worker.on("exit", code => events.error(new Error(`Render worker exited with code ${code}`)));
19
- return { post: (message, transfer) => worker.postMessage(message, transfer), terminate: () => void worker.terminate() };
20
+ return {
21
+ post: (message, transfer) => worker.postMessage(message, transfer),
22
+ terminate: async () => { await worker.terminate(); },
23
+ ref: () => { worker.ref(); }, unref: () => { worker.unref(); },
24
+ };
20
25
  },
21
26
  });
package/src/types.ts CHANGED
@@ -123,3 +123,18 @@ export interface PdfOptions {
123
123
  /** Reject larger documents. Default: 100; maximum: 1000. */
124
124
  maxPages?: number;
125
125
  }
126
+ /** Limits for a pool of reusable, independent PDF engines. */
127
+ export interface PdfWorkerPoolOptions {
128
+ /** Maximum simultaneous renders. Default: 4. */
129
+ maxWorkers?: number;
130
+ /** Release an unused worker after this many milliseconds. Default: 30000; 0 disables reuse. */
131
+ idleTimeoutMs?: number;
132
+ /** Recreate a worker after this many documents to bound retained engine memory. Default: 100. */
133
+ maxJobsPerWorker?: number;
134
+ }
135
+
136
+ export interface PdfWorkerPool {
137
+ renderPdf(html: string, options?: PdfOptions): Promise<Uint8Array>;
138
+ /** Stop accepting work, finish accepted renders, and release every worker. Idempotent. */
139
+ close(): Promise<void>;
140
+ }
@@ -1,45 +1,198 @@
1
1
  import { createAssetLoader } from "./assets.js";
2
2
  import type { RenderOptions } from "./render.js";
3
- import type { AssetResolver } from "./types.js";
3
+ import type { AssetResolver, PdfWorkerPoolOptions } from "./types.js";
4
4
  import type { HostMessage, WorkerMessage } from "./worker-protocol.js";
5
5
 
6
6
  export interface WorkerEvents {
7
7
  message(message: WorkerMessage): void;
8
- /** Crashes, unreadable messages, or an exit before the render settled. */
8
+ /** Crashes, unreadable messages, or an unexpected exit, including while idle. */
9
9
  error(error: unknown): void;
10
10
  }
11
11
 
12
12
  export interface WorkerHandle {
13
13
  post(message: HostMessage, transfer: ArrayBuffer[]): void;
14
- terminate(): void;
14
+ terminate(): void | Promise<void>;
15
+ ref?(): void;
16
+ unref?(): void;
15
17
  }
16
18
 
17
19
  export type SpawnWorker = (events: WorkerEvents) => WorkerHandle;
18
20
 
19
- /** Renders in a fresh worker that instantiates the precompiled module, then terminates it. */
21
+ /** One engine per document; wait for termination before releasing the caller's slot. */
20
22
  export function renderInWorker(spawn: SpawnWorker, module: WebAssembly.Module, html: string,
21
- options: RenderOptions, resolver: AssetResolver | undefined): Promise<Uint8Array> {
22
- return new Promise<Uint8Array>((resolve, reject) => {
23
+ options: RenderOptions, resolver?: AssetResolver): Promise<Uint8Array> {
24
+ return new Promise((resolve, reject) => {
23
25
  const load = resolver && createAssetLoader(resolver);
26
+ let worker: WorkerHandle | undefined;
24
27
  let settled = false;
25
- const settle = (callback: () => void) => {
28
+ function finish(callback: () => void) {
26
29
  if (settled) return;
27
30
  settled = true;
28
- worker.terminate();
29
- callback();
30
- };
31
- const worker: WorkerHandle = spawn({
32
- message(message) {
33
- if (message.type === "done") settle(() => resolve(message.pdf));
34
- else if (message.type === "error") settle(() => reject(message.error));
35
- else if (!load) settle(() => reject(new Error("Render worker requested assets without a resolver")));
36
- else load(message.urls).then(
37
- data => { if (!settled) worker.post({ type: "assets", data }, data.map(bytes => bytes.buffer as ArrayBuffer)); },
38
- (error: unknown) => settle(() => reject(error)),
39
- );
40
- },
41
- error(error) { settle(() => reject(error)); },
42
- });
43
- worker.post({ type: "render", module, html, options, external: load !== undefined }, []);
31
+ void Promise.resolve().then(() => worker?.terminate()).then(callback, reject);
32
+ }
33
+ const fail = (error: unknown) => finish(() => reject(error));
34
+ try {
35
+ worker = spawn({
36
+ message(message) {
37
+ if (settled || message.id !== 1) return;
38
+ if (message.type === "done") finish(() => resolve(message.pdf));
39
+ else if (message.type === "error") fail(message.error);
40
+ else if (!load) fail(new Error("Render worker requested assets without a resolver"));
41
+ else void load(message.urls).then(data => {
42
+ if (settled) return;
43
+ try {
44
+ worker!.post({ type: "assets", id: 1, data }, data.map(bytes => bytes.buffer as ArrayBuffer));
45
+ } catch (error) { fail(error); }
46
+ }, fail);
47
+ },
48
+ error: fail,
49
+ });
50
+ if (!settled) worker.post({ type: "render", id: 1, module, html, options, external: load !== undefined }, []);
51
+ } catch (error) { fail(error); }
44
52
  });
45
53
  }
54
+
55
+ interface Job {
56
+ id: number;
57
+ message: Extract<HostMessage, { type: "render" }>;
58
+ load: ReturnType<typeof createAssetLoader> | undefined;
59
+ resolve(pdf: Uint8Array): void;
60
+ reject(error: unknown): void;
61
+ }
62
+
63
+ interface Slot {
64
+ worker?: WorkerHandle;
65
+ job?: Job;
66
+ timer?: ReturnType<typeof setTimeout>;
67
+ completed: number;
68
+ retired: boolean;
69
+ }
70
+
71
+ function integer(value: number, min: number, max: number, name: string): number {
72
+ if (!Number.isSafeInteger(value) || value < min || value > max) {
73
+ throw new RangeError(`${name} must be an integer from ${min} to ${max}`);
74
+ }
75
+ return value;
76
+ }
77
+
78
+ /** Jobs have their own resolver and document; only the initialized engine survives. */
79
+ export function createWorkerPool(spawn: SpawnWorker, options: PdfWorkerPoolOptions = {}) {
80
+ const maxWorkers = integer(options.maxWorkers ?? 4, 1, Number.MAX_SAFE_INTEGER, "maxWorkers");
81
+ const idleTimeoutMs = integer(options.idleTimeoutMs ?? 30_000, 0, 2_147_483_647, "idleTimeoutMs");
82
+ const maxJobs = integer(options.maxJobsPerWorker ?? 100, 1, Number.MAX_SAFE_INTEGER, "maxJobsPerWorker");
83
+ const slots = new Set<Slot>();
84
+ const queue: Job[] = [];
85
+ const terminating = new Set<Promise<void>>();
86
+ let nextId = 0;
87
+ let closing: Promise<void> | undefined;
88
+ let resolveClosed: (() => void) | undefined;
89
+ let pumping = false;
90
+
91
+ function checkClosed() {
92
+ if (closing && queue.length === 0 && slots.size === 0 && terminating.size === 0) resolveClosed?.();
93
+ }
94
+
95
+ function retire(slot: Slot) {
96
+ if (slot.retired) return;
97
+ slot.retired = true;
98
+ clearTimeout(slot.timer);
99
+ slots.delete(slot);
100
+ // Observe asynchronous termination before close resolves; late exit events are ignored.
101
+ const done = Promise.resolve().then(() => slot.worker?.terminate()).then(() => {}, () => {});
102
+ terminating.add(done);
103
+ void done.then(() => { terminating.delete(done); checkClosed(); });
104
+ }
105
+
106
+ function fail(slot: Slot, error: unknown) {
107
+ if (slot.retired) return;
108
+ const job = slot.job;
109
+ delete slot.job;
110
+ retire(slot);
111
+ job?.reject(error);
112
+ pump();
113
+ }
114
+
115
+ function release(slot: Slot) {
116
+ slot.completed++;
117
+ if (slot.completed >= maxJobs || idleTimeoutMs === 0 || (closing && queue.length === 0)) {
118
+ retire(slot);
119
+ } else if (queue.length === 0) {
120
+ slot.worker?.unref?.();
121
+ slot.timer = setTimeout(() => { retire(slot); checkClosed(); }, idleTimeoutMs);
122
+ // A warm worker and its eviction timer must not keep a CLI alive.
123
+ slot.timer.unref?.();
124
+ }
125
+ pump();
126
+ }
127
+
128
+ function message(slot: Slot, value: WorkerMessage) {
129
+ const job = slot.job;
130
+ if (slot.retired || !job || value.id !== job.id) return;
131
+ if (value.type === "done") {
132
+ delete slot.job;
133
+ release(slot);
134
+ job.resolve(value.pdf);
135
+ } else if (value.type === "error") {
136
+ fail(slot, value.error);
137
+ } else if (!job.load) {
138
+ fail(slot, new Error("Render worker requested assets without a resolver"));
139
+ } else {
140
+ void job.load(value.urls).then(data => {
141
+ // Resolver promises can finish after a crash or retirement. Never send them to another job.
142
+ if (slot.retired || slot.job !== job) return;
143
+ try {
144
+ slot.worker!.post({ type: "assets", id: job.id, data }, data.map(bytes => bytes.buffer as ArrayBuffer));
145
+ } catch (error) { fail(slot, error); }
146
+ }, error => { if (!slot.retired && slot.job === job) fail(slot, error); });
147
+ }
148
+ }
149
+
150
+ function pump() {
151
+ if (pumping) return;
152
+ pumping = true;
153
+ try {
154
+ while (queue.length > 0) {
155
+ let slot = [...slots].find(candidate => !candidate.job);
156
+ if (!slot && slots.size >= maxWorkers) break;
157
+ const job = queue.shift()!;
158
+ if (!slot) {
159
+ slot = { completed: 0, retired: false, job };
160
+ slots.add(slot);
161
+ const created = slot;
162
+ try {
163
+ created.worker = spawn({ message: value => message(created, value), error: error => fail(created, error) });
164
+ } catch (error) { fail(created, error); continue; }
165
+ } else {
166
+ clearTimeout(slot.timer);
167
+ delete slot.timer;
168
+ slot.job = job;
169
+ }
170
+ try {
171
+ slot.worker!.ref?.();
172
+ slot.worker!.post(job.message, []);
173
+ } catch (error) { fail(slot, error); }
174
+ }
175
+ if (closing && queue.length === 0) for (const slot of slots) if (!slot.job) retire(slot);
176
+ } finally { pumping = false; }
177
+ checkClosed();
178
+ }
179
+
180
+ return {
181
+ render(module: WebAssembly.Module, html: string, options: RenderOptions, resolver?: AssetResolver): Promise<Uint8Array> {
182
+ if (closing) return Promise.reject(new Error("PDF worker pool is closed"));
183
+ return new Promise((resolve, reject) => {
184
+ const id = ++nextId;
185
+ queue.push({ id, message: { type: "render", id, module, html, options, external: resolver !== undefined },
186
+ load: resolver ? createAssetLoader(resolver) : undefined, resolve, reject });
187
+ pump();
188
+ });
189
+ },
190
+ close(): Promise<void> {
191
+ if (!closing) {
192
+ closing = new Promise(resolve => { resolveClosed = resolve; });
193
+ pump();
194
+ }
195
+ return closing;
196
+ },
197
+ };
198
+ }