@melra/browser-runtime 0.3.0-alpha.10 → 0.3.0-alpha.12

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 (69) hide show
  1. package/README.md +2 -1
  2. package/dist/bounded.d.ts +27 -0
  3. package/dist/bounded.d.ts.map +1 -0
  4. package/dist/bounded.js +50 -0
  5. package/dist/bounded.js.map +1 -0
  6. package/dist/browser-connection.d.ts.map +1 -1
  7. package/dist/browser-connection.js +22 -7
  8. package/dist/browser-connection.js.map +1 -1
  9. package/dist/index.d.ts +73 -3
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +307 -52
  12. package/dist/index.js.map +1 -1
  13. package/dist/pinning-proxy.d.ts +1 -1
  14. package/dist/pinning-proxy.d.ts.map +1 -1
  15. package/dist/pinning-proxy.js +1 -1
  16. package/dist/pinning-proxy.js.map +1 -1
  17. package/dist/stable-dom.d.ts +13 -1
  18. package/dist/stable-dom.d.ts.map +1 -1
  19. package/dist/stable-dom.js +22 -3
  20. package/dist/stable-dom.js.map +1 -1
  21. package/package.json +6 -5
  22. package/dist/browser-connection.test.d.ts +0 -2
  23. package/dist/browser-connection.test.d.ts.map +0 -1
  24. package/dist/browser-connection.test.js +0 -210
  25. package/dist/browser-connection.test.js.map +0 -1
  26. package/dist/dialogs.test.d.ts +0 -2
  27. package/dist/dialogs.test.d.ts.map +0 -1
  28. package/dist/dialogs.test.js +0 -165
  29. package/dist/dialogs.test.js.map +0 -1
  30. package/dist/frames.test.d.ts +0 -2
  31. package/dist/frames.test.d.ts.map +0 -1
  32. package/dist/frames.test.js +0 -157
  33. package/dist/frames.test.js.map +0 -1
  34. package/dist/history-tabs.test.d.ts +0 -2
  35. package/dist/history-tabs.test.d.ts.map +0 -1
  36. package/dist/history-tabs.test.js +0 -171
  37. package/dist/history-tabs.test.js.map +0 -1
  38. package/dist/input-fidelity.test.d.ts +0 -2
  39. package/dist/input-fidelity.test.d.ts.map +0 -1
  40. package/dist/input-fidelity.test.js +0 -260
  41. package/dist/input-fidelity.test.js.map +0 -1
  42. package/dist/network-policy.d.ts +0 -28
  43. package/dist/network-policy.d.ts.map +0 -1
  44. package/dist/network-policy.js +0 -121
  45. package/dist/network-policy.js.map +0 -1
  46. package/dist/network-policy.test.d.ts +0 -2
  47. package/dist/network-policy.test.d.ts.map +0 -1
  48. package/dist/network-policy.test.js +0 -39
  49. package/dist/network-policy.test.js.map +0 -1
  50. package/dist/pinning-proxy.test.d.ts +0 -2
  51. package/dist/pinning-proxy.test.d.ts.map +0 -1
  52. package/dist/pinning-proxy.test.js +0 -80
  53. package/dist/pinning-proxy.test.js.map +0 -1
  54. package/dist/popups-profile.test.d.ts +0 -2
  55. package/dist/popups-profile.test.d.ts.map +0 -1
  56. package/dist/popups-profile.test.js +0 -170
  57. package/dist/popups-profile.test.js.map +0 -1
  58. package/dist/see-act.test.d.ts +0 -2
  59. package/dist/see-act.test.d.ts.map +0 -1
  60. package/dist/see-act.test.js +0 -152
  61. package/dist/see-act.test.js.map +0 -1
  62. package/dist/selector.test.d.ts +0 -2
  63. package/dist/selector.test.d.ts.map +0 -1
  64. package/dist/selector.test.js +0 -34
  65. package/dist/selector.test.js.map +0 -1
  66. package/dist/stable-dom.test.d.ts +0 -2
  67. package/dist/stable-dom.test.d.ts.map +0 -1
  68. package/dist/stable-dom.test.js +0 -22
  69. package/dist/stable-dom.test.js.map +0 -1
@@ -1,165 +0,0 @@
1
- // Copyright 2026 XAGI Labs Private Limited
2
- // SPDX-License-Identifier: Apache-2.0
3
- import { createServer } from "node:http";
4
- import { mkdtemp, rm } from "node:fs/promises";
5
- import { tmpdir } from "node:os";
6
- import { join } from "node:path";
7
- import { BrowserOperationSchema } from "@melra/protocol";
8
- import { afterEach, describe, expect, it } from "vitest";
9
- import { BrowserRuntime, detectBrowserExecutable } from "./index.js";
10
- /**
11
- * Playwright dismisses every dialog automatically unless a handler is
12
- * registered, and nothing registered one. A `confirm()` guarding a destructive
13
- * button was therefore answered "cancel" on the page's behalf: the click
14
- * reported success, the guarded work never ran, and the caller had no way to
15
- * tell — the one shape of failure this codebase exists to prevent.
16
- *
17
- * `beforeunload` is the same defect pointed at navigation, and `prompt()` the
18
- * same pointed at input.
19
- */
20
- const roots = [];
21
- const servers = [];
22
- afterEach(async () => {
23
- await Promise.all(servers.splice(0).map((server) => new Promise((done) => {
24
- server.close(() => done());
25
- })));
26
- await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
27
- });
28
- /**
29
- * `#delete` is the shape that matters: real work sits behind a `confirm()`, and
30
- * the page records the answer it was given. `#status` is the independent
31
- * evidence — it changes only if the dialog was accepted.
32
- */
33
- const PAGE = `<!doctype html><html><head><title>dialogs</title></head><body>
34
- <button id="delete">Delete</button>
35
- <button id="notice">Notice</button>
36
- <button id="ask">Ask</button>
37
- <div id="status">untouched</div>
38
- <div id="answer">none</div>
39
- <script>
40
- document.getElementById('delete').addEventListener('click', () => {
41
- if (confirm('Delete this record?')) {
42
- document.getElementById('status').textContent = 'deleted';
43
- } else {
44
- document.getElementById('status').textContent = 'cancelled';
45
- }
46
- });
47
- document.getElementById('notice').addEventListener('click', () => {
48
- alert('Saved.');
49
- document.getElementById('status').textContent = 'acknowledged';
50
- });
51
- document.getElementById('ask').addEventListener('click', () => {
52
- const name = prompt('Your name?');
53
- document.getElementById('answer').textContent = name === null ? 'null' : name;
54
- });
55
- </script>
56
- </body></html>`;
57
- async function fixture() {
58
- const executablePath = await detectBrowserExecutable();
59
- if (executablePath === undefined)
60
- return undefined;
61
- const server = createServer((_request, response) => {
62
- response.writeHead(200, { "content-type": "text/html; charset=utf-8" });
63
- response.end(PAGE);
64
- });
65
- servers.push(server);
66
- await new Promise((done) => {
67
- server.listen(0, "127.0.0.1", done);
68
- });
69
- const address = server.address();
70
- if (address === null || typeof address === "string") {
71
- throw new Error("fixture_server_address_unavailable");
72
- }
73
- const root = await mkdtemp(join(tmpdir(), "melra-dialogs-"));
74
- roots.push(root);
75
- return {
76
- runtime: new BrowserRuntime({
77
- workspaceRoot: root,
78
- artifactDirectory: join(root, "artifacts"),
79
- executablePath,
80
- allowedDomains: ["127.0.0.1"],
81
- allowLocalhost: true,
82
- }),
83
- url: `http://127.0.0.1:${address.port}`,
84
- };
85
- }
86
- const op = (fields) => BrowserOperationSchema.parse({ kind: "browser", ...fields });
87
- const textOf = async (runtime, selector) => {
88
- const inspected = await runtime.execute(op({ action: "inspect", target: { selector } }));
89
- return String(inspected.text ?? "").trim();
90
- };
91
- describe("browser dialogs", () => {
92
- it("reports the confirm it answered instead of silently cancelling", async () => {
93
- const context = await fixture();
94
- if (context === undefined)
95
- return;
96
- const { runtime, url } = context;
97
- try {
98
- await runtime.execute(op({ action: "navigate", url }));
99
- const clicked = await runtime.execute(op({ action: "click", target: { selector: "#delete" } }));
100
- // The click must say a dialog happened. Without this the caller cannot
101
- // distinguish "the button did its work" from "a dialog ate it".
102
- expect(clicked.dialogs).toEqual([
103
- expect.objectContaining({
104
- type: "confirm",
105
- message: "Delete this record?",
106
- }),
107
- ]);
108
- // And the page must agree with whatever answer was reported.
109
- const answered = clicked.dialogs[0]
110
- ?.accepted;
111
- expect(await textOf(runtime, "#status")).toBe(answered === true ? "deleted" : "cancelled");
112
- }
113
- finally {
114
- await runtime.close();
115
- }
116
- }, 60_000);
117
- it("lets an alert through so the handler after it still runs", async () => {
118
- const context = await fixture();
119
- if (context === undefined)
120
- return;
121
- const { runtime, url } = context;
122
- try {
123
- await runtime.execute(op({ action: "navigate", url }));
124
- const clicked = await runtime.execute(op({ action: "click", target: { selector: "#notice" } }));
125
- expect(clicked.dialogs).toEqual([
126
- expect.objectContaining({ type: "alert", message: "Saved." }),
127
- ]);
128
- expect(await textOf(runtime, "#status")).toBe("acknowledged");
129
- }
130
- finally {
131
- await runtime.close();
132
- }
133
- }, 60_000);
134
- it("records a prompt rather than pretending the page asked nothing", async () => {
135
- const context = await fixture();
136
- if (context === undefined)
137
- return;
138
- const { runtime, url } = context;
139
- try {
140
- await runtime.execute(op({ action: "navigate", url }));
141
- const clicked = await runtime.execute(op({ action: "click", target: { selector: "#ask" } }));
142
- expect(clicked.dialogs).toEqual([
143
- expect.objectContaining({ type: "prompt", message: "Your name?" }),
144
- ]);
145
- }
146
- finally {
147
- await runtime.close();
148
- }
149
- }, 60_000);
150
- it("leaves actions that raise no dialog unchanged", async () => {
151
- const context = await fixture();
152
- if (context === undefined)
153
- return;
154
- const { runtime, url } = context;
155
- try {
156
- await runtime.execute(op({ action: "navigate", url }));
157
- const inspected = await runtime.execute(op({ action: "inspect" }));
158
- expect(inspected.dialogs).toBeUndefined();
159
- }
160
- finally {
161
- await runtime.close();
162
- }
163
- }, 60_000);
164
- });
165
- //# sourceMappingURL=dialogs.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"dialogs.test.js","sourceRoot":"","sources":["../src/dialogs.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=frames.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"frames.test.d.ts","sourceRoot":"","sources":["../src/frames.test.ts"],"names":[],"mappings":""}
@@ -1,157 +0,0 @@
1
- // Copyright 2026 XAGI Labs Private Limited
2
- // SPDX-License-Identifier: Apache-2.0
3
- import { createServer } from "node:http";
4
- import { mkdtemp, rm } from "node:fs/promises";
5
- import { tmpdir } from "node:os";
6
- import { join } from "node:path";
7
- import { BrowserOperationSchema } from "@melra/protocol";
8
- import { afterEach, describe, expect, it } from "vitest";
9
- import { BrowserRuntime, captchaReport, detectBrowserExecutable } from "./index.js";
10
- /**
11
- * Consent banners, cookie walls, and captcha widgets live in iframes, and every
12
- * locator used to be built from the page — which only searches the main
13
- * document. An agent could see the banner in a screenshot and had no way to
14
- * click it. These tests drive real frames, so they fail if that regresses.
15
- */
16
- const BANNER = `<!doctype html><html><body>
17
- <button id="accept">Accept all cookies</button>
18
- </body></html>`;
19
- /** Attaches itself after a delay, so a wait started before it exists must find it. */
20
- const LATE = `<!doctype html><html><body><p>loading</p><script>
21
- setTimeout(() => {
22
- document.body.innerHTML = '<button id="late">Continue</button>';
23
- }, 400);
24
- </script></body></html>`;
25
- const PAGE = `<!doctype html><html><head><title>Framed</title></head><body>
26
- <h1>Article</h1>
27
- <iframe src="/banner" title="consent"></iframe>
28
- <iframe src="/late" title="late"></iframe>
29
- </body></html>`;
30
- const roots = [];
31
- const servers = [];
32
- afterEach(async () => {
33
- await Promise.all(servers.splice(0).map((server) => new Promise((done) => {
34
- server.close(() => done());
35
- })));
36
- await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
37
- });
38
- async function fixture() {
39
- const executablePath = await detectBrowserExecutable();
40
- if (executablePath === undefined)
41
- return undefined;
42
- const server = createServer((request, response) => {
43
- const body = request.url === "/banner" ? BANNER : request.url === "/late" ? LATE : PAGE;
44
- response.writeHead(200, { "content-type": "text/html; charset=utf-8" });
45
- response.end(body);
46
- });
47
- servers.push(server);
48
- await new Promise((done) => {
49
- server.listen(0, "127.0.0.1", done);
50
- });
51
- const address = server.address();
52
- if (address === null || typeof address === "string") {
53
- throw new Error("fixture_server_address_unavailable");
54
- }
55
- const root = await mkdtemp(join(tmpdir(), "melra-frames-"));
56
- roots.push(root);
57
- return {
58
- runtime: new BrowserRuntime({
59
- workspaceRoot: root,
60
- artifactDirectory: join(root, "artifacts"),
61
- executablePath,
62
- allowedDomains: ["127.0.0.1"],
63
- allowLocalhost: true,
64
- }),
65
- url: `http://127.0.0.1:${address.port}`,
66
- };
67
- }
68
- describe("frame-aware targeting", () => {
69
- it("lists and clicks an element inside an iframe", async () => {
70
- const context = await fixture();
71
- if (context === undefined)
72
- return;
73
- const { runtime, url } = context;
74
- try {
75
- const observed = await runtime.execute(BrowserOperationSchema.parse({ kind: "browser", action: "navigate", url }));
76
- const elements = observed.elements;
77
- const accept = elements.find((item) => item.name === "Accept all cookies");
78
- // The banner is in a child document, so it is absent from the page text
79
- // but must still be listed, addressable, and attributed to its frame.
80
- expect(String(observed.text)).not.toContain("Accept all cookies");
81
- expect(accept).toBeDefined();
82
- expect(accept?.frame).toContain("/banner");
83
- expect(elements.every((item) => typeof item.index === "number")).toBe(true);
84
- expect(new Set(elements.map((item) => item.index)).size).toBe(elements.length);
85
- // The whole point: a target resolved without naming the frame.
86
- const clicked = await runtime.execute(BrowserOperationSchema.parse({
87
- kind: "browser",
88
- action: "click",
89
- target: { text: "Accept all cookies" },
90
- }));
91
- expect(clicked.success).toBe(true);
92
- }
93
- finally {
94
- await runtime.close();
95
- }
96
- });
97
- it("waits for a target that appears in a frame after the wait starts", async () => {
98
- const context = await fixture();
99
- if (context === undefined)
100
- return;
101
- const { runtime, url } = context;
102
- try {
103
- await runtime.execute(BrowserOperationSchema.parse({ kind: "browser", action: "navigate", url }));
104
- const waited = await runtime.execute(BrowserOperationSchema.parse({
105
- kind: "browser",
106
- action: "wait",
107
- target: { text: "Continue" },
108
- state: "visible",
109
- timeoutMs: 5_000,
110
- }));
111
- expect(waited.waited).toBe(true);
112
- }
113
- finally {
114
- await runtime.close();
115
- }
116
- });
117
- it("does not report a target as hidden merely because other frames lack it", async () => {
118
- const context = await fixture();
119
- if (context === undefined)
120
- return;
121
- const { runtime, url } = context;
122
- try {
123
- await runtime.execute(BrowserOperationSchema.parse({ kind: "browser", action: "navigate", url }));
124
- // The banner is visible in exactly one of three frames. A per-candidate
125
- // "some frame is missing it" test would call that hidden immediately.
126
- await expect(runtime.execute(BrowserOperationSchema.parse({
127
- kind: "browser",
128
- action: "wait",
129
- target: { text: "Accept all cookies" },
130
- state: "hidden",
131
- timeoutMs: 700,
132
- }))).rejects.toThrow(/browser_wait_timeout:hidden/);
133
- }
134
- finally {
135
- await runtime.close();
136
- }
137
- });
138
- });
139
- describe("captchaReport", () => {
140
- it("names the vendor of a human-verification widget", () => {
141
- const report = captchaReport([
142
- "http://127.0.0.1:8080/",
143
- "https://www.google.com/recaptcha/api2/anchor?k=abc",
144
- "https://challenges.cloudflare.com/cdn-cgi/challenge-platform/turnstile",
145
- ]);
146
- const captcha = report.captcha;
147
- expect(captcha.present).toBe(true);
148
- expect(captcha.vendors).toEqual(["recaptcha", "turnstile"]);
149
- });
150
- it("stays silent on a page with no challenge", () => {
151
- expect(captchaReport(["https://example.com/", "https://cdn.example.com/ad"])).toEqual({});
152
- });
153
- it("does not match a lookalike host outside the vendor domain", () => {
154
- expect(captchaReport(["https://hcaptcha.com.evil.test/x"])).toEqual({});
155
- });
156
- });
157
- //# sourceMappingURL=frames.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"frames.test.js","sourceRoot":"","sources":["../src/frames.test.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,sCAAsC;AAEtC,OAAO,EAAE,YAAY,EAAe,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAEpF;;;;;GAKG;AACH,MAAM,MAAM,GAAG;;eAEA,CAAC;AAEhB,sFAAsF;AACtF,MAAM,IAAI,GAAG;;;;wBAIW,CAAC;AAEzB,MAAM,IAAI,GAAG;;;;eAIE,CAAC;AAEhB,MAAM,KAAK,GAAa,EAAE,CAAC;AAC3B,MAAM,OAAO,GAAa,EAAE,CAAC;AAE7B,SAAS,CAAC,KAAK,IAAI,EAAE;IACnB,MAAM,OAAO,CAAC,GAAG,CACf,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CACnB,CAAC,MAAM,EAAE,EAAE,CACT,IAAI,OAAO,CAAO,CAAC,IAAI,EAAE,EAAE;QACzB,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7B,CAAC,CAAC,CACL,CACF,CAAC;IACF,MAAM,OAAO,CAAC,GAAG,CACf,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAC1E,CAAC;AACJ,CAAC,CAAC,CAAC;AASH,KAAK,UAAU,OAAO;IACpB,MAAM,cAAc,GAAG,MAAM,uBAAuB,EAAE,CAAC;IACvD,IAAI,cAAc,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACnD,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE;QAChD,MAAM,IAAI,GACR,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QAC7E,QAAQ,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,0BAA0B,EAAE,CAAC,CAAC;QACxE,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACrB,MAAM,IAAI,OAAO,CAAO,CAAC,IAAI,EAAE,EAAE;QAC/B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;IACjC,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;IACxD,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC;IAC5D,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,OAAO;QACL,OAAO,EAAE,IAAI,cAAc,CAAC;YAC1B,aAAa,EAAE,IAAI;YACnB,iBAAiB,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC;YAC1C,cAAc;YACd,cAAc,EAAE,CAAC,WAAW,CAAC;YAC7B,cAAc,EAAE,IAAI;SACrB,CAAC;QACF,GAAG,EAAE,oBAAoB,OAAO,CAAC,IAAI,EAAE;KACxC,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,EAAE,CAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;QAC5D,MAAM,OAAO,GAAG,MAAM,OAAO,EAAE,CAAC;QAChC,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO;QAClC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,OAAO,CACpC,sBAAsB,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAC3E,CAAC;YACF,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAqB,CAAC;YAChD,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,oBAAoB,CAAC,CAAC;YAC3E,wEAAwE;YACxE,sEAAsE;YACtE,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;YAClE,MAAM,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;YAC7B,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;YAC3C,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC5E,MAAM,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAE/E,+DAA+D;YAC/D,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,OAAO,CACnC,sBAAsB,CAAC,KAAK,CAAC;gBAC3B,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,OAAO;gBACf,MAAM,EAAE,EAAE,IAAI,EAAE,oBAAoB,EAAE;aACvC,CAAC,CACH,CAAC;YACF,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrC,CAAC;gBAAS,CAAC;YACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACxB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,KAAK,IAAI,EAAE;QAChF,MAAM,OAAO,GAAG,MAAM,OAAO,EAAE,CAAC;QAChC,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO;QAClC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,OAAO,CAAC,OAAO,CACnB,sBAAsB,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAC3E,CAAC;YACF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,CAClC,sBAAsB,CAAC,KAAK,CAAC;gBAC3B,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE;gBAC5B,KAAK,EAAE,SAAS;gBAChB,SAAS,EAAE,KAAK;aACjB,CAAC,CACH,CAAC;YACF,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnC,CAAC;gBAAS,CAAC;YACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACxB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wEAAwE,EAAE,KAAK,IAAI,EAAE;QACtF,MAAM,OAAO,GAAG,MAAM,OAAO,EAAE,CAAC;QAChC,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO;QAClC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,OAAO,CAAC,OAAO,CACnB,sBAAsB,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAC3E,CAAC;YACF,wEAAwE;YACxE,sEAAsE;YACtE,MAAM,MAAM,CACV,OAAO,CAAC,OAAO,CACb,sBAAsB,CAAC,KAAK,CAAC;gBAC3B,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,EAAE,IAAI,EAAE,oBAAoB,EAAE;gBACtC,KAAK,EAAE,QAAQ;gBACf,SAAS,EAAE,GAAG;aACf,CAAC,CACH,CACF,CAAC,OAAO,CAAC,OAAO,CAAC,6BAA6B,CAAC,CAAC;QACnD,CAAC;gBAAS,CAAC;YACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACxB,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,MAAM,GAAG,aAAa,CAAC;YAC3B,wBAAwB;YACxB,oDAAoD;YACpD,wEAAwE;SACzE,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,CAAC,OAAkD,CAAC;QAC1E,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,aAAa,CAAC,CAAC,sBAAsB,EAAE,4BAA4B,CAAC,CAAC,CAAC,CAAC,OAAO,CACnF,EAAE,CACH,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,MAAM,CAAC,aAAa,CAAC,CAAC,kCAAkC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=history-tabs.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"history-tabs.test.d.ts","sourceRoot":"","sources":["../src/history-tabs.test.ts"],"names":[],"mappings":""}
@@ -1,171 +0,0 @@
1
- // Copyright 2026 XAGI Labs Private Limited
2
- // SPDX-License-Identifier: Apache-2.0
3
- import { createServer } from "node:http";
4
- import { mkdtemp, rm } from "node:fs/promises";
5
- import { tmpdir } from "node:os";
6
- import { join } from "node:path";
7
- import { BrowserOperationSchema } from "@melra/protocol";
8
- import { afterEach, describe, expect, it } from "vitest";
9
- import { BrowserRuntime, detectBrowserExecutable } from "./index.js";
10
- /**
11
- * A session could only ever move forward, in one tab. There was no `back`, so a
12
- * wrong click was unrecoverable without re-navigating from scratch, and `tabs`
13
- * reported which page was `active` while nothing could change it — the
14
- * `tabIndex` schema field existed but was read only by `close`.
15
- */
16
- const roots = [];
17
- const servers = [];
18
- afterEach(async () => {
19
- await Promise.all(servers.splice(0).map((server) => new Promise((done) => {
20
- server.close(() => done());
21
- })));
22
- await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
23
- });
24
- /** Serves /one, /two, /three, each naming itself in the title and body. */
25
- async function fixture() {
26
- const executablePath = await detectBrowserExecutable();
27
- if (executablePath === undefined)
28
- return undefined;
29
- const server = createServer((request, response) => {
30
- const name = (request.url ?? "/").slice(1) || "one";
31
- response.writeHead(200, { "content-type": "text/html; charset=utf-8" });
32
- response.end(`<!doctype html><html><head><title>${name}</title></head><body><h1>${name}</h1></body></html>`);
33
- });
34
- servers.push(server);
35
- await new Promise((done) => {
36
- server.listen(0, "127.0.0.1", done);
37
- });
38
- const address = server.address();
39
- if (address === null || typeof address === "string") {
40
- throw new Error("fixture_server_address_unavailable");
41
- }
42
- const root = await mkdtemp(join(tmpdir(), "melra-history-tabs-"));
43
- roots.push(root);
44
- return {
45
- runtime: new BrowserRuntime({
46
- workspaceRoot: root,
47
- artifactDirectory: join(root, "artifacts"),
48
- executablePath,
49
- allowedDomains: ["127.0.0.1"],
50
- allowLocalhost: true,
51
- }),
52
- url: `http://127.0.0.1:${address.port}`,
53
- };
54
- }
55
- const op = (fields) => BrowserOperationSchema.parse({ kind: "browser", ...fields });
56
- describe("browser history", () => {
57
- it("moves back and forward through the history stack", async () => {
58
- const context = await fixture();
59
- if (context === undefined)
60
- return;
61
- const { runtime, url } = context;
62
- try {
63
- await runtime.execute(op({ action: "navigate", url: `${url}/one` }));
64
- await runtime.execute(op({ action: "navigate", url: `${url}/two` }));
65
- const back = await runtime.execute(op({ action: "back" }));
66
- expect(back.moved).toBe(true);
67
- expect(back.title).toBe("one");
68
- const forward = await runtime.execute(op({ action: "forward" }));
69
- expect(forward.moved).toBe(true);
70
- expect(forward.title).toBe("two");
71
- }
72
- finally {
73
- await runtime.close();
74
- }
75
- }, 120_000);
76
- it("reports a move with no HTTP response, and the end of the stack, differently", async () => {
77
- const context = await fixture();
78
- if (context === undefined)
79
- return;
80
- const { runtime, url } = context;
81
- try {
82
- await runtime.execute(op({ action: "navigate", url: `${url}/one` }));
83
- // A page starts on `about:blank`, which is a real history entry but
84
- // produces no HTTP response, so Playwright reports the same `null` it
85
- // reports for "nowhere to go". This moved.
86
- const blank = await runtime.execute(op({ action: "back" }));
87
- expect(blank.moved).toBe(true);
88
- expect(blank.status).toBe(null);
89
- expect(blank.url).toBe("about:blank");
90
- // Now there genuinely is nothing before it. "Go back unless already at
91
- // the start" is a normal thing to want, so this reports rather than
92
- // throws, and it must not look like the case above.
93
- const end = await runtime.execute(op({ action: "back" }));
94
- expect(end.moved).toBe(false);
95
- expect(end.url).toBe("about:blank");
96
- }
97
- finally {
98
- await runtime.close();
99
- }
100
- }, 120_000);
101
- it("reloads the current page in place", async () => {
102
- const context = await fixture();
103
- if (context === undefined)
104
- return;
105
- const { runtime, url } = context;
106
- try {
107
- await runtime.execute(op({ action: "navigate", url: `${url}/three` }));
108
- const reloaded = await runtime.execute(op({ action: "reload" }));
109
- expect(reloaded.status).toBe(200);
110
- expect(reloaded.url).toBe(`${url}/three`);
111
- }
112
- finally {
113
- await runtime.close();
114
- }
115
- }, 120_000);
116
- });
117
- describe("browser tabs", () => {
118
- it("opens a tab, switches between tabs, and closes one", async () => {
119
- const context = await fixture();
120
- if (context === undefined)
121
- return;
122
- const { runtime, url } = context;
123
- try {
124
- await runtime.execute(op({ action: "navigate", url: `${url}/one` }));
125
- const opened = await runtime.execute(op({ action: "tab_new", url: `${url}/two` }));
126
- expect(opened.opened).toBe(true);
127
- expect(opened.index).toBe(1);
128
- // A new tab becomes the active one, so the next action lands on it.
129
- expect((await runtime.execute(op({ action: "inspect" }))).title).toBe("two");
130
- const switched = await runtime.execute(op({ action: "tab_switch", tabIndex: 0 }));
131
- expect(switched.switched).toBe(true);
132
- expect(switched.tabs.find((tab) => tab.active)?.index).toBe(0);
133
- expect((await runtime.execute(op({ action: "inspect" }))).title).toBe("one");
134
- const closed = await runtime.execute(op({ action: "close", tabIndex: 1 }));
135
- expect(closed.tabs.length).toBe(1);
136
- }
137
- finally {
138
- await runtime.close();
139
- }
140
- }, 120_000);
141
- it("refuses a tab index that does not exist", async () => {
142
- const context = await fixture();
143
- if (context === undefined)
144
- return;
145
- const { runtime, url } = context;
146
- try {
147
- await runtime.execute(op({ action: "navigate", url: `${url}/one` }));
148
- await expect(runtime.execute(op({ action: "tab_switch", tabIndex: 7 }))).rejects.toThrow("browser_tab_not_found");
149
- await expect(runtime.execute(op({ action: "tab_switch" }))).rejects.toThrow("browser_tab_switch_requires_tab_index");
150
- }
151
- finally {
152
- await runtime.close();
153
- }
154
- }, 120_000);
155
- it("blocks a disallowed domain when opening a tab", async () => {
156
- const context = await fixture();
157
- if (context === undefined)
158
- return;
159
- const { runtime, url } = context;
160
- try {
161
- await runtime.execute(op({ action: "navigate", url: `${url}/one` }));
162
- // `tab_new` takes a url, so it has to run the same guard `navigate` does
163
- // rather than becoming a way around the allowlist.
164
- await expect(runtime.execute(op({ action: "tab_new", url: "http://169.254.169.254/latest" }))).rejects.toThrow();
165
- }
166
- finally {
167
- await runtime.close();
168
- }
169
- }, 120_000);
170
- });
171
- //# sourceMappingURL=history-tabs.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"history-tabs.test.js","sourceRoot":"","sources":["../src/history-tabs.test.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,sCAAsC;AAEtC,OAAO,EAAE,YAAY,EAAe,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAErE;;;;;GAKG;AACH,MAAM,KAAK,GAAa,EAAE,CAAC;AAC3B,MAAM,OAAO,GAAa,EAAE,CAAC;AAE7B,SAAS,CAAC,KAAK,IAAI,EAAE;IACnB,MAAM,OAAO,CAAC,GAAG,CACf,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CACnB,CAAC,MAAM,EAAE,EAAE,CACT,IAAI,OAAO,CAAO,CAAC,IAAI,EAAE,EAAE;QACzB,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7B,CAAC,CAAC,CACL,CACF,CAAC;IACF,MAAM,OAAO,CAAC,GAAG,CACf,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAC1E,CAAC;AACJ,CAAC,CAAC,CAAC;AASH,2EAA2E;AAC3E,KAAK,UAAU,OAAO;IACpB,MAAM,cAAc,GAAG,MAAM,uBAAuB,EAAE,CAAC;IACvD,IAAI,cAAc,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACnD,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE;QAChD,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC;QACpD,QAAQ,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,0BAA0B,EAAE,CAAC,CAAC;QACxE,QAAQ,CAAC,GAAG,CACV,qCAAqC,IAAI,4BAA4B,IAAI,qBAAqB,CAC/F,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACrB,MAAM,IAAI,OAAO,CAAO,CAAC,IAAI,EAAE,EAAE;QAC/B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;IACjC,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;IACxD,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,qBAAqB,CAAC,CAAC,CAAC;IAClE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,OAAO;QACL,OAAO,EAAE,IAAI,cAAc,CAAC;YAC1B,aAAa,EAAE,IAAI;YACnB,iBAAiB,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC;YAC1C,cAAc;YACd,cAAc,EAAE,CAAC,WAAW,CAAC;YAC7B,cAAc,EAAE,IAAI;SACrB,CAAC;QACF,GAAG,EAAE,oBAAoB,OAAO,CAAC,IAAI,EAAE;KACxC,CAAC;AACJ,CAAC;AAED,MAAM,EAAE,GAAG,CAAC,MAA+B,EAAE,EAAE,CAC7C,sBAAsB,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;AAE/D,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,EAAE,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;QAChE,MAAM,OAAO,GAAG,MAAM,OAAO,EAAE,CAAC;QAChC,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO;QAClC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,GAAG,MAAM,EAAE,CAAC,CAAC,CAAC;YACrE,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,GAAG,MAAM,EAAE,CAAC,CAAC,CAAC;YAErE,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;YAC3D,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAE/B,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;YACjE,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpC,CAAC;gBAAS,CAAC;YACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACxB,CAAC;IACH,CAAC,EAAE,OAAO,CAAC,CAAC;IAEZ,EAAE,CAAC,6EAA6E,EAAE,KAAK,IAAI,EAAE;QAC3F,MAAM,OAAO,GAAG,MAAM,OAAO,EAAE,CAAC;QAChC,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO;QAClC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,GAAG,MAAM,EAAE,CAAC,CAAC,CAAC;YAErE,oEAAoE;YACpE,sEAAsE;YACtE,2CAA2C;YAC3C,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;YAC5D,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC/B,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAChC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAEtC,uEAAuE;YACvE,oEAAoE;YACpE,oDAAoD;YACpD,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;YAC1D,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC9B,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACtC,CAAC;gBAAS,CAAC;YACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACxB,CAAC;IACH,CAAC,EAAE,OAAO,CAAC,CAAC;IAEZ,EAAE,CAAC,mCAAmC,EAAE,KAAK,IAAI,EAAE;QACjD,MAAM,OAAO,GAAG,MAAM,OAAO,EAAE,CAAC;QAChC,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO;QAClC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,GAAG,QAAQ,EAAE,CAAC,CAAC,CAAC;YACvE,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;YACjE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAClC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,GAAG,QAAQ,CAAC,CAAC;QAC5C,CAAC;gBAAS,CAAC;YACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACxB,CAAC;IACH,CAAC,EAAE,OAAO,CAAC,CAAC;AACd,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QAClE,MAAM,OAAO,GAAG,MAAM,OAAO,EAAE,CAAC;QAChC,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO;QAClC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,GAAG,MAAM,EAAE,CAAC,CAAC,CAAC;YAErE,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,CAClC,EAAE,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,GAAG,GAAG,MAAM,EAAE,CAAC,CAC7C,CAAC;YACF,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC7B,oEAAoE;YACpE,MAAM,CAAC,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAE7E,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YAClF,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACrC,MAAM,CAAE,QAAQ,CAAC,IAAc,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC1E,MAAM,CAAC,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAE7E,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YAC3E,MAAM,CAAE,MAAM,CAAC,IAAc,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAChD,CAAC;gBAAS,CAAC;YACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACxB,CAAC;IACH,CAAC,EAAE,OAAO,CAAC,CAAC;IAEZ,EAAE,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;QACvD,MAAM,OAAO,GAAG,MAAM,OAAO,EAAE,CAAC;QAChC,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO;QAClC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,GAAG,MAAM,EAAE,CAAC,CAAC,CAAC;YACrE,MAAM,MAAM,CACV,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,CAC3D,CAAC,OAAO,CAAC,OAAO,CAAC,uBAAuB,CAAC,CAAC;YAC3C,MAAM,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CACzE,uCAAuC,CACxC,CAAC;QACJ,CAAC;gBAAS,CAAC;YACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACxB,CAAC;IACH,CAAC,EAAE,OAAO,CAAC,CAAC;IAEZ,EAAE,CAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;QAC7D,MAAM,OAAO,GAAG,MAAM,OAAO,EAAE,CAAC;QAChC,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO;QAClC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,GAAG,MAAM,EAAE,CAAC,CAAC,CAAC;YACrE,yEAAyE;YACzE,mDAAmD;YACnD,MAAM,MAAM,CACV,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,+BAA+B,EAAE,CAAC,CAAC,CACjF,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACtB,CAAC;gBAAS,CAAC;YACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACxB,CAAC;IACH,CAAC,EAAE,OAAO,CAAC,CAAC;AACd,CAAC,CAAC,CAAC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=input-fidelity.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"input-fidelity.test.d.ts","sourceRoot":"","sources":["../src/input-fidelity.test.ts"],"names":[],"mappings":""}