@juxtapose/core 0.4.0 → 0.6.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/dist/agent/handoff.d.ts +8 -1
- package/dist/agent/handoff.js +75 -7
- package/dist/agent/handoff.js.map +1 -1
- package/dist/analysis/affected.d.ts +37 -0
- package/dist/analysis/affected.js +97 -22
- package/dist/analysis/affected.js.map +1 -1
- package/dist/analysis/cause.d.ts +32 -0
- package/dist/analysis/cause.js +187 -0
- package/dist/analysis/cause.js.map +1 -0
- package/dist/baseline/approve.d.ts +24 -0
- package/dist/baseline/approve.js +54 -0
- package/dist/baseline/approve.js.map +1 -0
- package/dist/baseline/git-store.d.ts +4 -0
- package/dist/baseline/git-store.js +22 -0
- package/dist/baseline/git-store.js.map +1 -1
- package/dist/browser/acquire.d.ts +15 -0
- package/dist/browser/acquire.js +26 -0
- package/dist/browser/acquire.js.map +1 -0
- package/dist/browser/browser.d.ts +53 -0
- package/dist/browser/browser.js +122 -0
- package/dist/browser/browser.js.map +1 -0
- package/dist/browser/cdp.d.ts +81 -0
- package/dist/browser/cdp.js +230 -0
- package/dist/browser/cdp.js.map +1 -0
- package/dist/browser/daemon/client.d.ts +145 -0
- package/dist/browser/daemon/client.js +764 -0
- package/dist/browser/daemon/client.js.map +1 -0
- package/dist/browser/daemon/state.d.ts +99 -0
- package/dist/browser/daemon/state.js +297 -0
- package/dist/browser/daemon/state.js.map +1 -0
- package/dist/browser/daemon/supervisor-main.d.ts +1 -0
- package/dist/browser/daemon/supervisor-main.js +54 -0
- package/dist/browser/daemon/supervisor-main.js.map +1 -0
- package/dist/browser/daemon/supervisor.d.ts +37 -0
- package/dist/browser/daemon/supervisor.js +777 -0
- package/dist/browser/daemon/supervisor.js.map +1 -0
- package/dist/{capture/launch.d.ts → browser/flags.d.ts} +19 -1
- package/dist/browser/flags.js +146 -0
- package/dist/browser/flags.js.map +1 -0
- package/dist/browser/index.d.ts +14 -0
- package/dist/browser/index.js +14 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/install.d.ts +33 -0
- package/dist/browser/install.js +167 -0
- package/dist/browser/install.js.map +1 -0
- package/dist/browser/launch.d.ts +37 -0
- package/dist/browser/launch.js +254 -0
- package/dist/browser/launch.js.map +1 -0
- package/dist/browser/page.d.ts +125 -0
- package/dist/browser/page.js +564 -0
- package/dist/browser/page.js.map +1 -0
- package/dist/browser/pins.d.ts +12 -0
- package/dist/browser/pins.js +57 -0
- package/dist/browser/pins.js.map +1 -0
- package/dist/browser/private-dir.d.ts +7 -0
- package/dist/browser/private-dir.js +24 -0
- package/dist/browser/private-dir.js.map +1 -0
- package/dist/browser/resolve.d.ts +31 -0
- package/dist/browser/resolve.js +126 -0
- package/dist/browser/resolve.js.map +1 -0
- package/dist/browser/selectors.d.ts +10 -0
- package/dist/browser/selectors.js +60 -0
- package/dist/browser/selectors.js.map +1 -0
- package/dist/browser/transport.d.ts +51 -0
- package/dist/browser/transport.js +124 -0
- package/dist/browser/transport.js.map +1 -0
- package/dist/browser/types.d.ts +4 -0
- package/dist/browser/types.js +2 -0
- package/dist/browser/types.js.map +1 -0
- package/dist/browser/zip.d.ts +27 -0
- package/dist/browser/zip.js +241 -0
- package/dist/browser/zip.js.map +1 -0
- package/dist/capture/capture.d.ts +7 -1
- package/dist/capture/capture.js +25 -8
- package/dist/capture/capture.js.map +1 -1
- package/dist/capture/env.d.ts +1 -1
- package/dist/capture/pool.d.ts +1 -1
- package/dist/capture/pool.js +41 -8
- package/dist/capture/pool.js.map +1 -1
- package/dist/capture/settle.d.ts +1 -1
- package/dist/capture/settle.js +2 -0
- package/dist/capture/settle.js.map +1 -1
- package/dist/capture/snap.d.ts +43 -0
- package/dist/capture/snap.js +103 -0
- package/dist/capture/snap.js.map +1 -0
- package/dist/capture/structure.d.ts +77 -0
- package/dist/capture/structure.js +498 -0
- package/dist/capture/structure.js.map +1 -0
- package/dist/diff/compare.d.ts +19 -0
- package/dist/diff/compare.js +19 -0
- package/dist/diff/compare.js.map +1 -0
- package/dist/diff/crops.d.ts +26 -0
- package/dist/diff/crops.js +92 -0
- package/dist/diff/crops.js.map +1 -0
- package/dist/diff/structural/classify.d.ts +29 -0
- package/dist/diff/structural/classify.js +113 -0
- package/dist/diff/structural/classify.js.map +1 -0
- package/dist/diff/structural/coverage.d.ts +11 -0
- package/dist/diff/structural/coverage.js +37 -0
- package/dist/diff/structural/coverage.js.map +1 -0
- package/dist/diff/structural/deltas.d.ts +29 -0
- package/dist/diff/structural/deltas.js +218 -0
- package/dist/diff/structural/deltas.js.map +1 -0
- package/dist/diff/structural/disambiguate.d.ts +3 -0
- package/dist/diff/structural/disambiguate.js +89 -0
- package/dist/diff/structural/disambiguate.js.map +1 -0
- package/dist/diff/structural/label.d.ts +15 -0
- package/dist/diff/structural/label.js +64 -0
- package/dist/diff/structural/label.js.map +1 -0
- package/dist/diff/structural/match.d.ts +13 -0
- package/dist/diff/structural/match.js +135 -0
- package/dist/diff/structural/match.js.map +1 -0
- package/dist/diff/structural/shift.d.ts +2 -0
- package/dist/diff/structural/shift.js +144 -0
- package/dist/diff/structural/shift.js.map +1 -0
- package/dist/diff/structural/summary.d.ts +31 -0
- package/dist/diff/structural/summary.js +420 -0
- package/dist/diff/structural/summary.js.map +1 -0
- package/dist/diff/structural.d.ts +40 -0
- package/dist/diff/structural.js +219 -0
- package/dist/diff/structural.js.map +1 -0
- package/dist/index.d.ts +28 -4
- package/dist/index.js +32 -3
- package/dist/index.js.map +1 -1
- package/dist/platform.d.ts +1 -1
- package/dist/platforms/urls.d.ts +3 -0
- package/dist/platforms/urls.js +89 -0
- package/dist/platforms/urls.js.map +1 -0
- package/dist/report/comment.js +9 -3
- package/dist/report/comment.js.map +1 -1
- package/dist/report/format.d.ts +18 -0
- package/dist/report/format.js +20 -0
- package/dist/report/format.js.map +1 -1
- package/dist/report/html.js +2 -2
- package/dist/report/html.js.map +1 -1
- package/dist/run/groups.d.ts +2 -0
- package/dist/run/groups.js +45 -0
- package/dist/run/groups.js.map +1 -0
- package/dist/run/run.d.ts +6 -2
- package/dist/run/run.js +74 -6
- package/dist/run/run.js.map +1 -1
- package/dist/shell-quote.d.ts +2 -0
- package/dist/shell-quote.js +10 -0
- package/dist/shell-quote.js.map +1 -0
- package/dist/types.d.ts +201 -5
- package/dist/types.js +0 -2
- package/dist/types.js.map +1 -1
- package/dist/url-platform.d.ts +2 -2
- package/dist/url-platform.js +2 -2
- package/dist/url-platform.js.map +1 -1
- package/package.json +6 -10
- package/dist/capture/launch.js +0 -56
- package/dist/capture/launch.js.map +0 -1
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates `dir` 0700 if missing, then checks it with lstat: a real directory
|
|
3
|
+
* (not a symlink), owned by `uid`, with no group/other write bits. Never
|
|
4
|
+
* chmod/chown's: a directory we did not create is refused, not repaired.
|
|
5
|
+
* Without a uid (Windows) only the symlink/directory check applies.
|
|
6
|
+
*/
|
|
7
|
+
export declare function ensurePrivateDir(dir: string, uid: number | undefined, refuse: (why: string) => Error): Promise<void>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// A directory only this user can write, verified rather than assumed. Shared
|
|
2
|
+
// by the Chrome profile root (launch.ts) and the daemon state dir
|
|
3
|
+
// (daemon/state.ts): both hold things another local user must not be able to
|
|
4
|
+
// plant, swap or read through (Ruling 17).
|
|
5
|
+
import { lstat, mkdir } from 'node:fs/promises';
|
|
6
|
+
/**
|
|
7
|
+
* Creates `dir` 0700 if missing, then checks it with lstat: a real directory
|
|
8
|
+
* (not a symlink), owned by `uid`, with no group/other write bits. Never
|
|
9
|
+
* chmod/chown's: a directory we did not create is refused, not repaired.
|
|
10
|
+
* Without a uid (Windows) only the symlink/directory check applies.
|
|
11
|
+
*/
|
|
12
|
+
export async function ensurePrivateDir(dir, uid, refuse) {
|
|
13
|
+
await mkdir(dir, { recursive: true, mode: 0o700 });
|
|
14
|
+
const st = await lstat(dir);
|
|
15
|
+
if (st.isSymbolicLink() || !st.isDirectory())
|
|
16
|
+
throw refuse('it is not a real directory');
|
|
17
|
+
if (uid !== undefined) {
|
|
18
|
+
if (st.uid !== uid)
|
|
19
|
+
throw refuse('it is owned by another user');
|
|
20
|
+
if ((st.mode & 0o022) !== 0)
|
|
21
|
+
throw refuse('it is writable by other users');
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=private-dir.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"private-dir.js","sourceRoot":"","sources":["../../src/browser/private-dir.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,kEAAkE;AAClE,6EAA6E;AAC7E,2CAA2C;AAE3C,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAEhD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,GAAW,EACX,GAAuB,EACvB,MAA8B;IAE9B,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACnD,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5B,IAAI,EAAE,CAAC,cAAc,EAAE,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE;QAC1C,MAAM,MAAM,CAAC,4BAA4B,CAAC,CAAC;IAC7C,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,IAAI,EAAE,CAAC,GAAG,KAAK,GAAG;YAAE,MAAM,MAAM,CAAC,6BAA6B,CAAC,CAAC;QAChE,IAAI,CAAC,EAAE,CAAC,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC;YAAE,MAAM,MAAM,CAAC,+BAA+B,CAAC,CAAC;IAC7E,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type ChromePlatform } from './pins.js';
|
|
2
|
+
export interface ResolveOptions {
|
|
3
|
+
flag?: string;
|
|
4
|
+
env?: NodeJS.ProcessEnv;
|
|
5
|
+
platform?: NodeJS.Platform;
|
|
6
|
+
arch?: string;
|
|
7
|
+
homedir?: string;
|
|
8
|
+
exists?: (p: string) => boolean;
|
|
9
|
+
isDirectory?: (p: string) => boolean;
|
|
10
|
+
}
|
|
11
|
+
export type ChromeSource = 'flag' | 'env' | 'jux-cache' | 'ms-playwright';
|
|
12
|
+
export interface ResolvedChrome {
|
|
13
|
+
path: string;
|
|
14
|
+
source: ChromeSource;
|
|
15
|
+
}
|
|
16
|
+
export declare const INSTALL_MARKER = ".complete";
|
|
17
|
+
export declare const NO_BROWSER_MESSAGE = "jux: no browser. Run `npx -p @juxtapose/cli jux browser install`, or set JUX_CHROME=<path to a Chromium headless shell>.";
|
|
18
|
+
/** The no-browser message, with the platform explanation when there is no build to install. */
|
|
19
|
+
export declare function noBrowserMessage(opts?: ResolveOptions): string;
|
|
20
|
+
export declare function juxCacheDir(opts?: ResolveOptions): string;
|
|
21
|
+
/**
|
|
22
|
+
* Where the warm-browser daemon keeps its socket, state and lock. Defaults to
|
|
23
|
+
* the cache dir (so existing setups do not move); JUX_STATE_DIR moves it alone,
|
|
24
|
+
* e.g. to a short path when the cache dir is too long for a unix socket.
|
|
25
|
+
*/
|
|
26
|
+
export declare function juxStateDir(opts?: ResolveOptions): string;
|
|
27
|
+
export declare function chromeInstallDir(cacheDir: string, version?: string): string;
|
|
28
|
+
/** Mirrors the zip's top-level folder so `extractZip` needs no renaming. */
|
|
29
|
+
export declare function chromeBinaryPath(installDir: string, platform: ChromePlatform): string;
|
|
30
|
+
export declare function msPlaywrightBinaryPath(opts?: ResolveOptions): string;
|
|
31
|
+
export declare function resolveChrome(opts?: ResolveOptions): ResolvedChrome | null;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// packages/core/src/browser/resolve.ts
|
|
2
|
+
//
|
|
3
|
+
// Where the browser comes from, in order: --chrome, JUX_CHROME, our cache,
|
|
4
|
+
// Playwright's cache. The last one is not a courtesy: it is the same bytes
|
|
5
|
+
// (pins.ts), and most early users already have it, so the upgrade costs them
|
|
6
|
+
// no download.
|
|
7
|
+
import { existsSync, statSync } from 'node:fs';
|
|
8
|
+
import { homedir as osHomedir } from 'node:os';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { CHROME_VERSION, MS_PLAYWRIGHT_REVISION, chromePlatform, } from './pins.js';
|
|
11
|
+
/** Paths follow the target platform, not the host, so tests can assert win32 strings anywhere. */
|
|
12
|
+
const pathFor = (platform) => platform === 'win32' ? path.win32 : path.posix;
|
|
13
|
+
const isDirectoryOnDisk = (p) => {
|
|
14
|
+
try {
|
|
15
|
+
return statSync(p).isDirectory();
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
export const INSTALL_MARKER = '.complete';
|
|
22
|
+
export const NO_BROWSER_MESSAGE = 'jux: no browser. Run `npx -p @juxtapose/cli jux browser install`, or set JUX_CHROME=<path to a Chromium headless shell>.';
|
|
23
|
+
/** The no-browser message, with the platform explanation when there is no build to install. */
|
|
24
|
+
export function noBrowserMessage(opts = {}) {
|
|
25
|
+
try {
|
|
26
|
+
chromePlatform(opts.platform ?? process.platform, opts.arch ?? process.arch);
|
|
27
|
+
return NO_BROWSER_MESSAGE;
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
return err.message;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function osCacheDir(platform, env, home) {
|
|
34
|
+
const p = pathFor(platform);
|
|
35
|
+
if (platform === 'darwin')
|
|
36
|
+
return p.join(home, 'Library', 'Caches');
|
|
37
|
+
if (platform === 'win32')
|
|
38
|
+
return env.LOCALAPPDATA || p.join(home, 'AppData', 'Local');
|
|
39
|
+
return env.XDG_CACHE_HOME || p.join(home, '.cache');
|
|
40
|
+
}
|
|
41
|
+
export function juxCacheDir(opts = {}) {
|
|
42
|
+
const env = opts.env ?? process.env;
|
|
43
|
+
const platform = opts.platform ?? process.platform;
|
|
44
|
+
const p = pathFor(platform);
|
|
45
|
+
if (env.JUX_CACHE_DIR)
|
|
46
|
+
return p.resolve(env.JUX_CACHE_DIR);
|
|
47
|
+
return p.join(osCacheDir(platform, env, opts.homedir ?? osHomedir()), 'jux');
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Where the warm-browser daemon keeps its socket, state and lock. Defaults to
|
|
51
|
+
* the cache dir (so existing setups do not move); JUX_STATE_DIR moves it alone,
|
|
52
|
+
* e.g. to a short path when the cache dir is too long for a unix socket.
|
|
53
|
+
*/
|
|
54
|
+
export function juxStateDir(opts = {}) {
|
|
55
|
+
const env = opts.env ?? process.env;
|
|
56
|
+
if (env.JUX_STATE_DIR)
|
|
57
|
+
return pathFor(opts.platform ?? process.platform).resolve(env.JUX_STATE_DIR);
|
|
58
|
+
return juxCacheDir(opts);
|
|
59
|
+
}
|
|
60
|
+
/** A cache dir with a drive letter or backslashes is a Windows path. */
|
|
61
|
+
const platformOfCacheDir = (cacheDir) => /^[A-Za-z]:|\\/.test(cacheDir) ? 'win32' : 'linux';
|
|
62
|
+
export function chromeInstallDir(cacheDir, version = CHROME_VERSION) {
|
|
63
|
+
return pathFor(platformOfCacheDir(cacheDir)).join(cacheDir, 'chrome-headless-shell', version);
|
|
64
|
+
}
|
|
65
|
+
/** Mirrors the zip's top-level folder so `extractZip` needs no renaming. */
|
|
66
|
+
export function chromeBinaryPath(installDir, platform) {
|
|
67
|
+
const exe = platform === 'win64' ? 'chrome-headless-shell.exe' : 'chrome-headless-shell';
|
|
68
|
+
const p = platform === 'win64' ? path.win32 : path.posix;
|
|
69
|
+
return p.join(installDir, `chrome-headless-shell-${platform}`, exe);
|
|
70
|
+
}
|
|
71
|
+
export function msPlaywrightBinaryPath(opts = {}) {
|
|
72
|
+
const env = opts.env ?? process.env;
|
|
73
|
+
const platform = opts.platform ?? process.platform;
|
|
74
|
+
const cft = chromePlatform(platform, opts.arch ?? process.arch);
|
|
75
|
+
const p = pathFor(platform);
|
|
76
|
+
const root = env.PLAYWRIGHT_BROWSERS_PATH ||
|
|
77
|
+
p.join(osCacheDir(platform, env, opts.homedir ?? osHomedir()), 'ms-playwright');
|
|
78
|
+
return chromeBinaryPath(p.join(root, `chromium_headless_shell-${MS_PLAYWRIGHT_REVISION}`), cft);
|
|
79
|
+
}
|
|
80
|
+
export function resolveChrome(opts = {}) {
|
|
81
|
+
const env = opts.env ?? process.env;
|
|
82
|
+
const platform = opts.platform ?? process.platform;
|
|
83
|
+
const p = pathFor(platform);
|
|
84
|
+
const exists = opts.exists ?? existsSync;
|
|
85
|
+
const isDirectory = opts.isDirectory ?? isDirectoryOnDisk;
|
|
86
|
+
// Resolved so a daemon or child with another cwd sees the same file; an
|
|
87
|
+
// existing directory would only fail later at spawn, with a worse message.
|
|
88
|
+
const explicit = (raw, label) => {
|
|
89
|
+
const abs = p.resolve(raw);
|
|
90
|
+
if (!exists(abs))
|
|
91
|
+
throw new Error(`jux: ${label} does not exist.`);
|
|
92
|
+
if (isDirectory(abs))
|
|
93
|
+
throw new Error(`jux: ${label} is a directory, not a browser executable.`);
|
|
94
|
+
return abs;
|
|
95
|
+
};
|
|
96
|
+
if (opts.flag)
|
|
97
|
+
return { path: explicit(opts.flag, `--chrome ${opts.flag}`), source: 'flag' };
|
|
98
|
+
// An empty JUX_CHROME (`JUX_CHROME= jux ...`) is unset, not a path.
|
|
99
|
+
if (env.JUX_CHROME)
|
|
100
|
+
return {
|
|
101
|
+
path: explicit(env.JUX_CHROME, `JUX_CHROME=${env.JUX_CHROME}`),
|
|
102
|
+
source: 'env',
|
|
103
|
+
};
|
|
104
|
+
let cft;
|
|
105
|
+
try {
|
|
106
|
+
cft = chromePlatform(platform, opts.arch ?? process.arch);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
const installDir = chromeInstallDir(juxCacheDir(opts));
|
|
112
|
+
const ours = chromeBinaryPath(installDir, cft);
|
|
113
|
+
if (exists(ours) && exists(p.join(installDir, INSTALL_MARKER)))
|
|
114
|
+
return { path: ours, source: 'jux-cache' };
|
|
115
|
+
// PLAYWRIGHT_BROWSERS_PATH=0 is Playwright's "install inside node_modules"
|
|
116
|
+
// sentinel, not a directory; joining it would probe a relative "0/" path.
|
|
117
|
+
if (env.PLAYWRIGHT_BROWSERS_PATH === '0')
|
|
118
|
+
return null;
|
|
119
|
+
const theirs = msPlaywrightBinaryPath(opts);
|
|
120
|
+
// Playwright writes INSTALLATION_COMPLETE last, like our marker.
|
|
121
|
+
const theirMarker = p.join(p.dirname(p.dirname(theirs)), 'INSTALLATION_COMPLETE');
|
|
122
|
+
if (exists(theirs) && exists(theirMarker))
|
|
123
|
+
return { path: theirs, source: 'ms-playwright' };
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=resolve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/browser/resolve.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function isCssSelector(selector: string): boolean;
|
|
2
|
+
export declare function assertCssSelector(selector: string): void;
|
|
3
|
+
/** In-page: every match of `selector` in the document and its open shadow roots. */
|
|
4
|
+
export declare const QUERY_ALL_SOURCE = "function (selector) {\n const out = [];\n const walk = (root) => {\n for (const el of root.querySelectorAll(selector)) out.push(el);\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);\n let node = walker.currentNode;\n while (node) {\n if (node.shadowRoot) walk(node.shadowRoot);\n node = walker.nextNode();\n }\n };\n walk(document);\n return out;\n}";
|
|
5
|
+
/**
|
|
6
|
+
* `(function (els, a0, a1…) { body })((queryAll)(selector), arg0, arg1…)`
|
|
7
|
+
* — one builder for every "do X to all matches" evaluate, so the selector
|
|
8
|
+
* and arguments are JSON-escaped in exactly one place.
|
|
9
|
+
*/
|
|
10
|
+
export declare function queryAllExpression(selector: string, body: string, ...args: unknown[]): string;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// packages/core/src/browser/selectors.ts
|
|
2
|
+
//
|
|
3
|
+
// Masks, structure.ts's mask marking and waitForSelector resolve selectors
|
|
4
|
+
// in the page. Playwright's locator engine also accepted `text=`, `>>`,
|
|
5
|
+
// `:has-text()` and friends; those are refused up front so a baseline is
|
|
6
|
+
// never captured unmasked because a selector silently matched nothing
|
|
7
|
+
// (spec §Decisions 5; a documented breaking change in the changeset).
|
|
8
|
+
//
|
|
9
|
+
// Open shadow roots are pierced: each root's own matches first, then its
|
|
10
|
+
// shadow trees. Mask boxes are all the same opaque colour, so their order
|
|
11
|
+
// cannot change a pixel; closed roots are invisible to Playwright too.
|
|
12
|
+
const PLAYWRIGHT_ONLY = [
|
|
13
|
+
/^[a-zA-Z_-]+=/, // text=, css=, xpath=, id=, nth=, _react=, _vue= (engine prefix)
|
|
14
|
+
/^internal:/,
|
|
15
|
+
/>>/,
|
|
16
|
+
/:(has-text|text|text-is|text-matches|visible|nth-match|light|above|below|left-of|right-of|near)\(/,
|
|
17
|
+
/:visible\b/,
|
|
18
|
+
];
|
|
19
|
+
// Quoted strings are attribute values, never syntax: `[title=">>"]` is CSS.
|
|
20
|
+
// Emptying them (quotes kept) leaves `text="x"` and `:has-text("x")` caught.
|
|
21
|
+
const QUOTED = /"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/g;
|
|
22
|
+
export function isCssSelector(selector) {
|
|
23
|
+
const syntax = selector.replace(QUOTED, '""');
|
|
24
|
+
return !PLAYWRIGHT_ONLY.some((re) => re.test(syntax));
|
|
25
|
+
}
|
|
26
|
+
export function assertCssSelector(selector) {
|
|
27
|
+
if (!isCssSelector(selector)) {
|
|
28
|
+
throw new Error(`jux: selector '${selector}' is not a CSS selector`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** In-page: every match of `selector` in the document and its open shadow roots. */
|
|
32
|
+
export const QUERY_ALL_SOURCE = `function (selector) {
|
|
33
|
+
const out = [];
|
|
34
|
+
const walk = (root) => {
|
|
35
|
+
for (const el of root.querySelectorAll(selector)) out.push(el);
|
|
36
|
+
const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
|
|
37
|
+
let node = walker.currentNode;
|
|
38
|
+
while (node) {
|
|
39
|
+
if (node.shadowRoot) walk(node.shadowRoot);
|
|
40
|
+
node = walker.nextNode();
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
walk(document);
|
|
44
|
+
return out;
|
|
45
|
+
}`;
|
|
46
|
+
/**
|
|
47
|
+
* `(function (els, a0, a1…) { body })((queryAll)(selector), arg0, arg1…)`
|
|
48
|
+
* — one builder for every "do X to all matches" evaluate, so the selector
|
|
49
|
+
* and arguments are JSON-escaped in exactly one place.
|
|
50
|
+
*/
|
|
51
|
+
export function queryAllExpression(selector, body, ...args) {
|
|
52
|
+
const names = args.map((_, i) => `a${i}`);
|
|
53
|
+
const params = ['els', ...names].join(', ');
|
|
54
|
+
const values = [
|
|
55
|
+
`(${QUERY_ALL_SOURCE})(${JSON.stringify(selector)})`,
|
|
56
|
+
...args.map((a) => JSON.stringify(a)),
|
|
57
|
+
].join(', ');
|
|
58
|
+
return `(function (${params}) { ${body} })(${values})`;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=selectors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selectors.js","sourceRoot":"","sources":["../../src/browser/selectors.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,EAAE;AACF,2EAA2E;AAC3E,wEAAwE;AACxE,yEAAyE;AACzE,sEAAsE;AACtE,sEAAsE;AACtE,EAAE;AACF,yEAAyE;AACzE,0EAA0E;AAC1E,uEAAuE;AAEvE,MAAM,eAAe,GAAG;IACtB,eAAe,EAAE,iEAAiE;IAClF,YAAY;IACZ,IAAI;IACJ,mGAAmG;IACnG,YAAY;CACb,CAAC;AAEF,4EAA4E;AAC5E,6EAA6E;AAC7E,MAAM,MAAM,GAAG,sCAAsC,CAAC;AAEtD,MAAM,UAAU,aAAa,CAAC,QAAgB;IAC5C,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC9C,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,kBAAkB,QAAQ,yBAAyB,CAAC,CAAC;IACvE,CAAC;AACH,CAAC;AAED,oFAAoF;AACpF,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;EAa9B,CAAC;AAEH;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAChC,QAAgB,EAChB,IAAY,EACZ,GAAG,IAAe;IAElB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5C,MAAM,MAAM,GAAG;QACb,IAAI,gBAAgB,KAAK,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG;QACpD,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;KACtC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACb,OAAO,cAAc,MAAM,OAAO,IAAI,OAAO,MAAM,GAAG,CAAC;AACzD,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type net from 'node:net';
|
|
2
|
+
export interface Transport {
|
|
3
|
+
send(message: string): void;
|
|
4
|
+
onMessage: ((message: string) => void) | null;
|
|
5
|
+
onClose: ((reason?: string) => void) | null;
|
|
6
|
+
close(): void;
|
|
7
|
+
}
|
|
8
|
+
/** A frame grew past the framer's cap before its NUL arrived. */
|
|
9
|
+
export declare class FrameTooLargeError extends Error {
|
|
10
|
+
constructor(limit: number);
|
|
11
|
+
}
|
|
12
|
+
export declare class NullFramer {
|
|
13
|
+
private readonly maxFrameChars;
|
|
14
|
+
private parts;
|
|
15
|
+
private partsChars;
|
|
16
|
+
private readonly decoder;
|
|
17
|
+
/**
|
|
18
|
+
* `maxFrameChars` bounds one frame. Chrome's pipe is trusted and uncapped;
|
|
19
|
+
* the supervisor caps its clients so one that never sends a NUL cannot grow
|
|
20
|
+
* the daemon's memory without bound. Past the cap, push() throws.
|
|
21
|
+
*/
|
|
22
|
+
constructor(maxFrameChars?: number);
|
|
23
|
+
push(chunk: Buffer | string, onFrame: (frame: string) => void): void;
|
|
24
|
+
}
|
|
25
|
+
/** Framing, close-once and callback plumbing shared by both transports. */
|
|
26
|
+
export declare abstract class FramedTransport implements Transport {
|
|
27
|
+
onMessage: ((message: string) => void) | null;
|
|
28
|
+
onClose: ((reason?: string) => void) | null;
|
|
29
|
+
private readonly framer;
|
|
30
|
+
private closed;
|
|
31
|
+
protected abstract write(data: string): void;
|
|
32
|
+
protected abstract destroy(): void;
|
|
33
|
+
protected feed(chunk: Buffer | string): void;
|
|
34
|
+
send(message: string): void;
|
|
35
|
+
close(): void;
|
|
36
|
+
protected emitClose(reason: string): void;
|
|
37
|
+
}
|
|
38
|
+
export declare class PipeTransport extends FramedTransport {
|
|
39
|
+
private readonly writable;
|
|
40
|
+
private readonly readable;
|
|
41
|
+
constructor(writable: NodeJS.WritableStream, readable: NodeJS.ReadableStream);
|
|
42
|
+
private readonly onData;
|
|
43
|
+
protected write(data: string): void;
|
|
44
|
+
protected destroy(): void;
|
|
45
|
+
}
|
|
46
|
+
export declare class SocketTransport extends FramedTransport {
|
|
47
|
+
private readonly socket;
|
|
48
|
+
constructor(socket: net.Socket);
|
|
49
|
+
protected write(data: string): void;
|
|
50
|
+
protected destroy(): void;
|
|
51
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// packages/core/src/browser/transport.ts
|
|
2
|
+
//
|
|
3
|
+
// Two byte pipes, one framing. Chrome's --remote-debugging-pipe speaks
|
|
4
|
+
// NUL-terminated JSON on fds 3 (in) and 4 (out); the warm-browser supervisor
|
|
5
|
+
// (PR 2) speaks the same framing over a local socket so one CdpConnection
|
|
6
|
+
// serves both.
|
|
7
|
+
import { StringDecoder } from 'node:string_decoder';
|
|
8
|
+
/** A frame grew past the framer's cap before its NUL arrived. */
|
|
9
|
+
export class FrameTooLargeError extends Error {
|
|
10
|
+
constructor(limit) {
|
|
11
|
+
super(`jux: a frame exceeded ${limit} characters`);
|
|
12
|
+
this.name = 'FrameTooLargeError';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export class NullFramer {
|
|
16
|
+
maxFrameChars;
|
|
17
|
+
// Pieces of the frame in progress. Joined once when its NUL arrives and only
|
|
18
|
+
// the new chunk is scanned: re-slicing one growing string made a 20 MB frame
|
|
19
|
+
// in 64 KB chunks cost 339 ms (40 MB: 1.5 s), quadratic in frame size.
|
|
20
|
+
parts = [];
|
|
21
|
+
partsChars = 0;
|
|
22
|
+
// Chunk boundaries can fall inside a multi-byte UTF-8 character; a plain
|
|
23
|
+
// toString() per chunk would corrupt it into U+FFFD.
|
|
24
|
+
decoder = new StringDecoder('utf8');
|
|
25
|
+
/**
|
|
26
|
+
* `maxFrameChars` bounds one frame. Chrome's pipe is trusted and uncapped;
|
|
27
|
+
* the supervisor caps its clients so one that never sends a NUL cannot grow
|
|
28
|
+
* the daemon's memory without bound. Past the cap, push() throws.
|
|
29
|
+
*/
|
|
30
|
+
constructor(maxFrameChars = Infinity) {
|
|
31
|
+
this.maxFrameChars = maxFrameChars;
|
|
32
|
+
}
|
|
33
|
+
push(chunk, onFrame) {
|
|
34
|
+
const text = typeof chunk === 'string' ? chunk : this.decoder.write(chunk);
|
|
35
|
+
let start = 0;
|
|
36
|
+
let end;
|
|
37
|
+
while ((end = text.indexOf('\0', start)) >= 0) {
|
|
38
|
+
this.parts.push(text.slice(start, end));
|
|
39
|
+
this.partsChars += end - start;
|
|
40
|
+
if (this.partsChars > this.maxFrameChars)
|
|
41
|
+
throw new FrameTooLargeError(this.maxFrameChars);
|
|
42
|
+
const frame = this.parts.length === 1 ? this.parts[0] : this.parts.join('');
|
|
43
|
+
this.parts = [];
|
|
44
|
+
this.partsChars = 0;
|
|
45
|
+
start = end + 1;
|
|
46
|
+
onFrame(frame);
|
|
47
|
+
}
|
|
48
|
+
if (start < text.length) {
|
|
49
|
+
this.parts.push(text.slice(start));
|
|
50
|
+
this.partsChars += text.length - start;
|
|
51
|
+
if (this.partsChars > this.maxFrameChars)
|
|
52
|
+
throw new FrameTooLargeError(this.maxFrameChars);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/** Framing, close-once and callback plumbing shared by both transports. */
|
|
57
|
+
export class FramedTransport {
|
|
58
|
+
onMessage = null;
|
|
59
|
+
onClose = null;
|
|
60
|
+
framer = new NullFramer();
|
|
61
|
+
closed = false;
|
|
62
|
+
feed(chunk) {
|
|
63
|
+
this.framer.push(chunk, (f) => this.onMessage?.(f));
|
|
64
|
+
}
|
|
65
|
+
// Outgoing writes are unbounded on purpose: CDP commands are small and the
|
|
66
|
+
// driver awaits replies, so the write queue stays short. The PR 2 supervisor
|
|
67
|
+
// proxies arbitrary client traffic and must honor write() back-pressure
|
|
68
|
+
// (drain) itself before it forwards large payloads.
|
|
69
|
+
send(message) {
|
|
70
|
+
if (this.closed)
|
|
71
|
+
return;
|
|
72
|
+
this.write(message + '\0');
|
|
73
|
+
}
|
|
74
|
+
close() {
|
|
75
|
+
this.destroy();
|
|
76
|
+
this.emitClose('closed');
|
|
77
|
+
}
|
|
78
|
+
emitClose(reason) {
|
|
79
|
+
if (this.closed)
|
|
80
|
+
return;
|
|
81
|
+
this.closed = true;
|
|
82
|
+
this.onClose?.(reason);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export class PipeTransport extends FramedTransport {
|
|
86
|
+
writable;
|
|
87
|
+
readable;
|
|
88
|
+
constructor(writable, readable) {
|
|
89
|
+
super();
|
|
90
|
+
this.writable = writable;
|
|
91
|
+
this.readable = readable;
|
|
92
|
+
readable.on('data', this.onData);
|
|
93
|
+
readable.on('close', () => this.emitClose('pipe closed'));
|
|
94
|
+
readable.on('error', (e) => this.emitClose(e.message));
|
|
95
|
+
writable.on('error', (e) => this.emitClose(e.message));
|
|
96
|
+
}
|
|
97
|
+
onData = (chunk) => this.feed(chunk);
|
|
98
|
+
write(data) {
|
|
99
|
+
this.writable.write(data);
|
|
100
|
+
}
|
|
101
|
+
destroy() {
|
|
102
|
+
// The process owns the fds (launch.ts closes them with the process); we
|
|
103
|
+
// only stop listening so a closed transport delivers nothing more.
|
|
104
|
+
this.readable.removeListener('data', this.onData);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
export class SocketTransport extends FramedTransport {
|
|
108
|
+
socket;
|
|
109
|
+
constructor(socket) {
|
|
110
|
+
super();
|
|
111
|
+
this.socket = socket;
|
|
112
|
+
socket.setNoDelay(true);
|
|
113
|
+
socket.on('data', (chunk) => this.feed(chunk));
|
|
114
|
+
socket.on('close', () => this.emitClose('socket closed'));
|
|
115
|
+
socket.on('error', (e) => this.emitClose(e.message));
|
|
116
|
+
}
|
|
117
|
+
write(data) {
|
|
118
|
+
this.socket.write(data);
|
|
119
|
+
}
|
|
120
|
+
destroy() {
|
|
121
|
+
this.socket.destroy();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../../src/browser/transport.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/browser/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface ZipEntry {
|
|
2
|
+
name: string;
|
|
3
|
+
/** POSIX mode from the external attributes (0 when the zip has none). */
|
|
4
|
+
mode: number;
|
|
5
|
+
isDirectory: boolean;
|
|
6
|
+
data: Buffer;
|
|
7
|
+
}
|
|
8
|
+
export interface ZipLimits {
|
|
9
|
+
maxEntryBytes?: number;
|
|
10
|
+
maxTotalBytes?: number;
|
|
11
|
+
}
|
|
12
|
+
export declare const MAX_ENTRY_BYTES: number;
|
|
13
|
+
export declare const MAX_TOTAL_BYTES: number;
|
|
14
|
+
export declare function readZip(buf: Buffer, limits?: ZipLimits): ZipEntry[];
|
|
15
|
+
export declare function extractZip(buf: Buffer, dir: string, limits?: ZipLimits): Promise<string[]>;
|
|
16
|
+
export interface InstallOptions extends ZipLimits {
|
|
17
|
+
/** File written (inside the temp dir, before the swap) so a completed install is always marked. */
|
|
18
|
+
marker?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Extract into a sibling temp dir, then swap it into `finalDir`, so a crash
|
|
22
|
+
* mid-extract never leaves a half-written browser at the path resolve reads.
|
|
23
|
+
* Any existing `finalDir` (dir, file or symlink) is renamed aside first and
|
|
24
|
+
* removed after, so there is no window with nothing in place. If a concurrent
|
|
25
|
+
* installer wins the final rename, its target is complete and ours is dropped.
|
|
26
|
+
*/
|
|
27
|
+
export declare function installZip(buf: Buffer, finalDir: string, opts?: InstallOptions): Promise<string[]>;
|