buninu-browser 0.1.0 → 0.2.1

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 (64) hide show
  1. package/AGENTS.md +17 -0
  2. package/LICENSES/brotli.txt +19 -0
  3. package/README.md +41 -7
  4. package/THIRD_PARTY_NOTICES.md +26 -4
  5. package/buninu-browser.js +10 -2
  6. package/lib/audio/pcm.js +16 -0
  7. package/lib/audio/piano.js +109 -0
  8. package/lib/audio/pulse-client.js +413 -0
  9. package/lib/cdp-server.js +28 -7
  10. package/lib/document-types.js +308 -0
  11. package/lib/download-directory.js +28 -0
  12. package/lib/download.js +63 -0
  13. package/lib/happy-dom/selector-matcher.js +83 -0
  14. package/lib/headless-shell.js +165 -26
  15. package/lib/input/hit-test.js +57 -8
  16. package/lib/layout/text-layout.js +290 -41
  17. package/lib/network/network-service.js +177 -40
  18. package/lib/network/resolver.js +16 -0
  19. package/lib/paint/display-list.js +25 -10
  20. package/lib/render-tree/index.js +58 -0
  21. package/lib/renderer/bindings.js +543 -55
  22. package/lib/renderer/flags.js +8 -5
  23. package/lib/renderer/host.js +103 -7
  24. package/lib/renderer/main.js +35 -3
  25. package/lib/renderer/module-transform.js +30 -23
  26. package/lib/renderer/page-realm.js +228 -24
  27. package/lib/renderer/page-renderer.js +1264 -221
  28. package/lib/screenshot-cli.js +61 -0
  29. package/lib/style/computed-style.js +558 -229
  30. package/lib/style/selector-keys.js +302 -0
  31. package/lib/style/selector-traits.js +240 -0
  32. package/lib/user-agent.js +12 -0
  33. package/package.json +1 -1
  34. package/patches/happy-dom/0003-defer-script-events-to-renderer.patch +40 -0
  35. package/patches/happy-dom/0004-insert-adjacent-html-in-order.patch +19 -0
  36. package/patches/happy-dom/0005-invalidate-matches-on-structure-changes.patch +43 -0
  37. package/patches/happy-dom/README.md +12 -0
  38. package/patches/ses/0001-allow-regexp-own-tostring.patch +13 -0
  39. package/patches/ses/README.md +16 -0
  40. package/test/computed-style.test.js +113 -1
  41. package/test/document-types.test.js +111 -0
  42. package/test/download.test.js +192 -0
  43. package/test/fixtures/tone.mp3 +0 -0
  44. package/test/flex.test.js +11 -0
  45. package/test/happy-dom-matches.test.js +25 -0
  46. package/test/headless-shell.test.js +56 -0
  47. package/test/hit-test.test.js +37 -0
  48. package/test/log.test.js +4 -1
  49. package/test/network-service.test.js +67 -0
  50. package/test/page-scripts.test.js +437 -29
  51. package/test/piano.test.js +65 -0
  52. package/test/positioning.test.js +11 -0
  53. package/test/pulse-client.test.js +130 -0
  54. package/test/render-tree.test.js +36 -1
  55. package/test/resolver.test.js +12 -1
  56. package/test/screenshot-cli.test.js +19 -0
  57. package/test/selector-keys.test.js +39 -0
  58. package/test/selector-traits.test.js +19 -0
  59. package/test/text-layout.test.js +91 -1
  60. package/test/user-agent.test.js +42 -0
  61. package/vendor/happy-dom/packages/happy-dom/src/nodes/element/Element.ts +3 -6
  62. package/vendor/happy-dom/packages/happy-dom/src/nodes/html-script-element/HTMLScriptElement.ts +2 -20
  63. package/vendor/happy-dom/packages/happy-dom/src/query-selector/QuerySelector.ts +15 -3
  64. package/vendor/ses/dist/ses.mjs +2 -0
@@ -0,0 +1,61 @@
1
+ import { resolve } from "node:path";
2
+ import { pathToFileURL } from "node:url";
3
+ import { createBrowser } from "./headless-shell.js";
4
+
5
+ export function screenshotTargetUrl(input, { platform = process.platform, resolvePath = resolve, fileURL = pathToFileURL } = {}) {
6
+ const windowsPath = platform === "win32" && (/^[a-z]:/i.test(input) || input.startsWith("\\\\") || input.startsWith("//"));
7
+ if (windowsPath) return fileURL(resolvePath(input)).href;
8
+ if (/^[a-z][a-z\d+.-]*:/i.test(input)) return input;
9
+ if (input.includes("/") || input.startsWith(".") || (platform === "win32" && input.includes("\\"))) {
10
+ return fileURL(resolvePath(input)).href;
11
+ }
12
+ return `https://${input}`;
13
+ }
14
+
15
+ /** Chromium-style one-shot PNG capture, using the same renderer as CDP. */
16
+ export async function runScreenshot(args) {
17
+ const screenshot = args.find((arg) => arg === "--screenshot" || arg.startsWith("--screenshot="));
18
+ const filename = screenshot === "--screenshot" ? "screenshot.png" : screenshot.slice("--screenshot=".length);
19
+ const extension = filename.match(/\.(png|jpe?g|webp)$/i)?.[1]?.toLowerCase();
20
+ if (!extension) throw new Error("--screenshot filename must end in .png, .jpg, .jpeg, or .webp");
21
+ const format = extension === "jpg" ? "jpeg" : extension;
22
+
23
+ const size = args.find((arg) => arg.startsWith("--window-size="))?.slice("--window-size=".length);
24
+ const waitArg = args.find((arg) => arg.startsWith("--screenshot-wait="))?.slice("--screenshot-wait=".length);
25
+ const waitMs = waitArg === undefined ? 0 : Number(waitArg);
26
+ if (waitArg !== undefined && (!/^\d+$/.test(waitArg) || !Number.isSafeInteger(waitMs))) {
27
+ throw new Error("--screenshot-wait must be a nonnegative number of milliseconds");
28
+ }
29
+ const match = size?.match(/^(\d+)[,x](\d+)$/i);
30
+ if (size && (!match || !Number.isSafeInteger(Number(match[1])) || !Number.isSafeInteger(Number(match[2]))
31
+ || Number(match[1]) < 1 || Number(match[2]) < 1)) {
32
+ throw new Error("--window-size must be WIDTH,HEIGHT with positive integers");
33
+ }
34
+
35
+ const urlArg = args.find((arg) => !arg.startsWith("-"));
36
+ if (!urlArg) throw new Error("--screenshot requires a URL");
37
+ const url = screenshotTargetUrl(urlArg);
38
+
39
+ const browser = await createBrowser({
40
+ allowHostJs: args.includes("--dangerously-allow-host-js"),
41
+ mobile: args.includes("--mobile"),
42
+ spareRenderer: false,
43
+ });
44
+ try {
45
+ if (match) await browser.context.resize(Number(match[1]), Number(match[2]));
46
+ await browser.context.navigate(url);
47
+ if (!browser.context.lifecycleState().includes("load")) {
48
+ await new Promise((done) => {
49
+ const unsubscribe = browser.context.onLifecycle((name) => {
50
+ if (name === "load") { unsubscribe(); done(); }
51
+ });
52
+ });
53
+ }
54
+ if (waitMs) await Bun.sleep(waitMs);
55
+ const data = await browser.context.screenshot({ format });
56
+ await Bun.write(filename, Buffer.from(data, "base64"));
57
+ console.error(`Screenshot saved to ${resolve(filename)}`);
58
+ } finally {
59
+ browser.close();
60
+ }
61
+ }