playwright-e2e-mcp 0.1.1 → 0.1.2

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 (43) hide show
  1. package/README.md +90 -38
  2. package/dist/http.d.ts +19 -1
  3. package/dist/http.js +98 -9
  4. package/dist/http.js.map +1 -1
  5. package/dist/index.js +11 -3
  6. package/dist/index.js.map +1 -1
  7. package/dist/server.d.ts +13 -0
  8. package/dist/server.js +25 -12
  9. package/dist/server.js.map +1 -1
  10. package/dist/tools/compare-visual-state.js +11 -8
  11. package/dist/tools/compare-visual-state.js.map +1 -1
  12. package/dist/tools/generate-e2e-test.js +46 -10
  13. package/dist/tools/generate-e2e-test.js.map +1 -1
  14. package/dist/tools/inspect-page.js +2 -0
  15. package/dist/tools/inspect-page.js.map +1 -1
  16. package/dist/tools/list-tests.js +8 -1
  17. package/dist/tools/list-tests.js.map +1 -1
  18. package/dist/tools/run-test.js +1 -1
  19. package/dist/tools/run-test.js.map +1 -1
  20. package/dist/tools/shared.d.ts +9 -2
  21. package/dist/tools/shared.js +135 -10
  22. package/dist/tools/shared.js.map +1 -1
  23. package/dist/tools/validate-selector.js +2 -0
  24. package/dist/tools/validate-selector.js.map +1 -1
  25. package/dist/types/index.d.ts +7 -0
  26. package/dist/utils/change-analyzer.js +31 -26
  27. package/dist/utils/change-analyzer.js.map +1 -1
  28. package/dist/utils/image-diff.js +23 -4
  29. package/dist/utils/image-diff.js.map +1 -1
  30. package/dist/utils/path-utils.d.ts +25 -3
  31. package/dist/utils/path-utils.js +159 -7
  32. package/dist/utils/path-utils.js.map +1 -1
  33. package/dist/utils/playwright-runner.d.ts +14 -10
  34. package/dist/utils/playwright-runner.js +128 -30
  35. package/dist/utils/playwright-runner.js.map +1 -1
  36. package/dist/utils/report-parser.js +7 -6
  37. package/dist/utils/report-parser.js.map +1 -1
  38. package/dist/utils/trace-reader.js +23 -6
  39. package/dist/utils/trace-reader.js.map +1 -1
  40. package/dist/utils/url-policy.d.ts +29 -0
  41. package/dist/utils/url-policy.js +99 -0
  42. package/dist/utils/url-policy.js.map +1 -0
  43. package/package.json +1 -1
@@ -1,11 +1,12 @@
1
1
  /**
2
2
  * Cross-platform path handling.
3
3
  *
4
- * All path logic here is *lexical* (string based, no fs access) so that
4
+ * Most path logic here is *lexical* (string based, no fs access) so that
5
5
  * Windows paths behave correctly when unit tests run on macOS/Linux and
6
6
  * vice versa. Backslashes are normalized everywhere, drive letters and
7
7
  * UNC prefixes are preserved, and user-supplied paths are sandboxed to
8
8
  * the project root to block "../../etc/passwd" style escapes.
9
+ * realPathLenient / assertRealPathInside add the symlink-aware check.
9
10
  */
10
11
  /** Replace backslashes with slashes, collapsing duplicate separators. */
11
12
  export declare function normalizeSeparators(p: string): string;
@@ -25,6 +26,8 @@ export declare function resolvePath(base: string, target: string): string;
25
26
  export declare function isPathInside(child: string, parent: string): boolean;
26
27
  /** Expand a leading `~` to the user's home directory. */
27
28
  export declare function expandHome(p: string): string;
29
+ /** True when any segment of `p` starts with '-' (it could be parsed as a CLI option). */
30
+ export declare function hasOptionLikeSegment(p: string): boolean;
28
31
  /**
29
32
  * Sandbox a user-supplied path to the project root.
30
33
  *
@@ -49,7 +52,26 @@ export declare function tempFilePath(prefix: string, extension?: string): string
49
52
  /** True when a filename looks like a Playwright test/spec file. */
50
53
  export declare function isTestFile(p: string): boolean;
51
54
  /**
52
- * Validate a free-form CLI argument list: rejects NUL bytes and
53
- * shell-looking metacharacters that could smuggle extra commands.
55
+ * Validate one extra Playwright CLI argument: only flags from
56
+ * ALLOWED_CLI_FLAGS get through, in `--flag` or `--flag=value` form, and
57
+ * each value must pass that flag's check.
54
58
  */
55
59
  export declare function sanitizeCliArg(arg: string): string;
60
+ /**
61
+ * Symlink-resolved form of `p`. When `p` does not exist yet (a file about
62
+ * to be written), its nearest existing ancestor is resolved and the
63
+ * missing tail re-appended, so a symlinked parent directory is still seen.
64
+ */
65
+ export declare function realPathLenient(p: string): Promise<string>;
66
+ /**
67
+ * Write `data` to `target` inside `root` without following a symlink at
68
+ * the final path component (a planted link, even a dangling one, could
69
+ * otherwise redirect the write outside the root). Parent directories are
70
+ * created and re-checked against the root after creation.
71
+ */
72
+ export declare function writeFileInsideRoot(target: string, data: string | Uint8Array, root: string): Promise<void>;
73
+ /**
74
+ * Filesystem-aware companion to sanitizeUserPath: throws INVALID_PATH when
75
+ * `target`, after resolving symlinks, is not inside `root` (also resolved).
76
+ */
77
+ export declare function assertRealPathInside(target: string, root: string): Promise<void>;
@@ -1,13 +1,16 @@
1
1
  /**
2
2
  * Cross-platform path handling.
3
3
  *
4
- * All path logic here is *lexical* (string based, no fs access) so that
4
+ * Most path logic here is *lexical* (string based, no fs access) so that
5
5
  * Windows paths behave correctly when unit tests run on macOS/Linux and
6
6
  * vice versa. Backslashes are normalized everywhere, drive letters and
7
7
  * UNC prefixes are preserved, and user-supplied paths are sandboxed to
8
8
  * the project root to block "../../etc/passwd" style escapes.
9
+ * realPathLenient / assertRealPathInside add the symlink-aware check.
9
10
  */
10
11
  import { randomBytes } from 'node:crypto';
12
+ import { constants as fsConstants } from 'node:fs';
13
+ import { lstat, mkdir, open, realpath } from 'node:fs/promises';
11
14
  import os from 'node:os';
12
15
  import path from 'node:path';
13
16
  import { PlaywrightMcpError } from '../types/index.js';
@@ -113,6 +116,12 @@ export function expandHome(p) {
113
116
  }
114
117
  return p;
115
118
  }
119
+ /** True when any segment of `p` starts with '-' (it could be parsed as a CLI option). */
120
+ export function hasOptionLikeSegment(p) {
121
+ return normalizeSeparators(p)
122
+ .split('/')
123
+ .some((segment) => segment.startsWith('-'));
124
+ }
116
125
  /**
117
126
  * Sandbox a user-supplied path to the project root.
118
127
  *
@@ -153,10 +162,16 @@ export function sanitizeUserPath(input, projectRoot) {
153
162
  const resolved = resolvePath(root, expanded);
154
163
  if (!isPathInside(resolved, root)) {
155
164
  throw new PlaywrightMcpError(`Path "${raw}" resolves outside the project root`, 'INVALID_PATH', {
156
- hint: `Only paths inside ${root} are allowed. Directory traversal (..), absolute paths outside the project, and symlinks out of the project are rejected.`,
165
+ hint: `Only paths inside ${root} are allowed. Directory traversal (..) and absolute paths outside the project are rejected.`,
157
166
  details: `resolved=${resolved}`,
158
167
  });
159
168
  }
169
+ // Checked below the root only, so a root that itself has such a segment still works.
170
+ if (hasOptionLikeSegment(relativeToRoot(root, resolved))) {
171
+ throw new PlaywrightMcpError(`Path "${raw}" has a segment starting with "-"`, 'INVALID_PATH', {
172
+ hint: 'Path segments may not start with "-": the path could be read as a command-line option.',
173
+ });
174
+ }
160
175
  return resolved;
161
176
  }
162
177
  /** Convert a normalized path to the host platform's native form. */
@@ -192,20 +207,157 @@ export function tempFilePath(prefix, extension = '.json') {
192
207
  export function isTestFile(p) {
193
208
  return TEST_FILE_RE.test(normalizeSeparators(p));
194
209
  }
210
+ const intIn = (min, max) => (value) => /^\d+$/.test(value) && Number(value) >= min && Number(value) <= max;
211
+ const oneOf = (...values) => (value) => values.includes(value);
212
+ /** No leading '-' (would be read as another option), no control characters. */
213
+ const plainText = (value) => value !== '' && !value.startsWith('-') && !/[\x00-\x1f\x7f]/.test(value);
214
+ /**
215
+ * Playwright CLI flags callers may pass through `args`, each with the only
216
+ * values it accepts. Anything that points Playwright at another file or
217
+ * directory (--config, --output, --tsconfig, --reporter…), blocks the run
218
+ * (--ui, --debug) or hands a value to another program unchecked is absent.
219
+ * Value-taking flags must use `--flag=value`, so a bare flag can never
220
+ * swallow the arguments appended after it.
221
+ */
222
+ const ALLOWED_CLI_FLAGS = new Map([
223
+ ['--headed', null],
224
+ ['-x', null],
225
+ ['--fail-on-flaky-tests', null],
226
+ ['--forbid-only', null],
227
+ ['--fully-parallel', null],
228
+ ['--pass-with-no-tests', null],
229
+ ['--quiet', null],
230
+ ['--ignore-snapshots', null],
231
+ ['--no-deps', null],
232
+ ['--list', null],
233
+ ['--project', { test: (v) => plainText(v) && /^[\w .@:+/-]+$/.test(v), describe: 'a project name' }],
234
+ ['--repeat-each', { test: intIn(1, 100), describe: 'an integer 1-100' }],
235
+ ['--max-failures', { test: intIn(0, 1000), describe: 'an integer 0-1000' }],
236
+ ['--global-timeout', { test: intIn(0, 3_600_000), describe: 'milliseconds, at most 3600000' }],
237
+ ['--timeout', { test: intIn(0, 3_600_000), describe: 'milliseconds, at most 3600000' }],
238
+ ['--retries', { test: intIn(0, 10), describe: 'an integer 0-10' }],
239
+ ['--workers', { test: (v) => intIn(1, 64)(v) || /^([1-9]\d?|100)%$/.test(v), describe: '1-64 or a percentage' }],
240
+ ['-j', { test: (v) => intIn(1, 64)(v) || /^([1-9]\d?|100)%$/.test(v), describe: '1-64 or a percentage' }],
241
+ ['--shard', { test: (v) => /^[1-9]\d{0,3}\/[1-9]\d{0,3}$/.test(v), describe: 'current/total, e.g. 1/3' }],
242
+ ['--grep-invert', { test: plainText, describe: 'a pattern not starting with "-"' }],
243
+ [
244
+ '--trace',
245
+ {
246
+ test: oneOf('on', 'off', 'on-first-retry', 'on-all-retries', 'retain-on-failure', 'retain-on-first-failure', 'retain-on-failure-and-retries'),
247
+ describe: 'a Playwright trace mode',
248
+ },
249
+ ],
250
+ ['--update-snapshots', { optional: true, test: oneOf('all', 'changed', 'missing', 'none'), describe: 'all, changed, missing or none' }],
251
+ ['-u', { optional: true, test: oneOf('all', 'changed', 'missing', 'none'), describe: 'all, changed, missing or none' }],
252
+ // The value is handed to `git diff`, so it must be a plain ref, never an option.
253
+ [
254
+ '--only-changed',
255
+ { optional: true, test: (v) => /^[A-Za-z0-9._/~^@{}][A-Za-z0-9._/~^@{}-]*$/.test(v) && v.length <= 200, describe: 'a git ref' },
256
+ ],
257
+ ]);
195
258
  /**
196
- * Validate a free-form CLI argument list: rejects NUL bytes and
197
- * shell-looking metacharacters that could smuggle extra commands.
259
+ * Validate one extra Playwright CLI argument: only flags from
260
+ * ALLOWED_CLI_FLAGS get through, in `--flag` or `--flag=value` form, and
261
+ * each value must pass that flag's check.
198
262
  */
199
263
  export function sanitizeCliArg(arg) {
200
264
  const raw = typeof arg === 'string' ? arg.trim() : '';
201
265
  if (raw === '') {
202
266
  throw new PlaywrightMcpError('Argument must not be empty', 'INVALID_PATH');
203
267
  }
204
- if (/[\0;&|><`$]/.test(raw)) {
205
- throw new PlaywrightMcpError(`Argument "${raw}" contains forbidden shell characters`, 'INVALID_PATH', {
206
- hint: 'Arguments are executed directly (no shell); remove ; & | > < ` $ characters.',
268
+ if (/[\0;&|><`$]/.test(raw) || /[\x00-\x1f\x7f]/.test(raw)) {
269
+ throw new PlaywrightMcpError(`Argument "${raw}" contains forbidden characters`, 'INVALID_PATH', {
270
+ hint: 'Arguments are executed directly (no shell); remove control characters and ; & | > < ` $.',
271
+ });
272
+ }
273
+ const eq = raw.indexOf('=');
274
+ const flag = eq === -1 ? raw : raw.slice(0, eq);
275
+ const value = eq === -1 ? undefined : raw.slice(eq + 1);
276
+ if (!ALLOWED_CLI_FLAGS.has(flag)) {
277
+ throw new PlaywrightMcpError(`Argument "${raw}" is not an allowed Playwright flag`, 'INVALID_PATH', {
278
+ hint: `Allowed flags: ${[...ALLOWED_CLI_FLAGS.keys()].join(', ')}. Pass test files via testFiles and the config via config.`,
279
+ });
280
+ }
281
+ const check = ALLOWED_CLI_FLAGS.get(flag) ?? null;
282
+ if (check === null) {
283
+ if (value !== undefined) {
284
+ throw new PlaywrightMcpError(`Flag "${flag}" does not take a value`, 'INVALID_PATH');
285
+ }
286
+ return raw;
287
+ }
288
+ if (value === undefined) {
289
+ if (check.optional)
290
+ return raw;
291
+ throw new PlaywrightMcpError(`Flag "${flag}" needs a value: use ${flag}=<value>`, 'INVALID_PATH', {
292
+ hint: `${flag} takes ${check.describe}.`,
293
+ });
294
+ }
295
+ if (!check.test(value)) {
296
+ throw new PlaywrightMcpError(`Invalid value for ${flag}: "${value}"`, 'INVALID_PATH', {
297
+ hint: `${flag} takes ${check.describe}.`,
207
298
  });
208
299
  }
209
300
  return raw;
210
301
  }
302
+ /**
303
+ * Symlink-resolved form of `p`. When `p` does not exist yet (a file about
304
+ * to be written), its nearest existing ancestor is resolved and the
305
+ * missing tail re-appended, so a symlinked parent directory is still seen.
306
+ */
307
+ export async function realPathLenient(p) {
308
+ const normalized = normalizePath(p);
309
+ const tail = [];
310
+ let current = normalized;
311
+ for (;;) {
312
+ try {
313
+ const real = normalizePath(await realpath(toNativePath(current)));
314
+ return tail.length === 0 ? real : normalizePath(`${real}/${tail.reverse().join('/')}`);
315
+ }
316
+ catch {
317
+ const parent = normalizePath(path.posix.dirname(current));
318
+ if (parent === current || parent === '' || parent === '.')
319
+ return normalized;
320
+ tail.push(path.posix.basename(current));
321
+ current = parent;
322
+ }
323
+ }
324
+ }
325
+ /**
326
+ * Write `data` to `target` inside `root` without following a symlink at
327
+ * the final path component (a planted link, even a dangling one, could
328
+ * otherwise redirect the write outside the root). Parent directories are
329
+ * created and re-checked against the root after creation.
330
+ */
331
+ export async function writeFileInsideRoot(target, data, root) {
332
+ const native = toNativePath(target);
333
+ const info = await lstat(native).catch(() => null);
334
+ if (info?.isSymbolicLink()) {
335
+ throw new PlaywrightMcpError(`Refusing to write through a symlink: ${normalizePath(target)}`, 'INVALID_PATH', {
336
+ hint: 'Replace the symlink with a regular file or pick another path.',
337
+ });
338
+ }
339
+ await mkdir(path.dirname(native), { recursive: true });
340
+ await assertRealPathInside(path.posix.dirname(normalizePath(target)), root);
341
+ // O_NOFOLLOW closes the gap between the lstat above and the open (POSIX only).
342
+ const flags = fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_TRUNC | (fsConstants.O_NOFOLLOW ?? 0);
343
+ const handle = await open(native, flags, 0o644);
344
+ try {
345
+ await handle.writeFile(data);
346
+ }
347
+ finally {
348
+ await handle.close();
349
+ }
350
+ }
351
+ /**
352
+ * Filesystem-aware companion to sanitizeUserPath: throws INVALID_PATH when
353
+ * `target`, after resolving symlinks, is not inside `root` (also resolved).
354
+ */
355
+ export async function assertRealPathInside(target, root) {
356
+ const [realTarget, realRoot] = await Promise.all([realPathLenient(target), realPathLenient(root)]);
357
+ if (!isPathInside(realTarget, realRoot)) {
358
+ throw new PlaywrightMcpError(`Path "${normalizePath(target)}" resolves outside the project root`, 'INVALID_PATH', {
359
+ hint: `A symlink points outside ${normalizePath(root)}. Only paths inside the project root are allowed.`,
360
+ });
361
+ }
362
+ }
211
363
  //# sourceMappingURL=path-utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"path-utils.js","sourceRoot":"","sources":["../../src/utils/path-utils.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"path-utils.js","sourceRoot":"","sources":["../../src/utils/path-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,SAAS,IAAI,WAAW,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,MAAM,QAAQ,GAAG,cAAc,CAAC;AAChC,yEAAyE;AACzE,MAAM,MAAM,GAAG,+BAA+B,CAAC;AAE/C,MAAM,YAAY,GAAG,kDAAkD,CAAC;AAExE,yEAAyE;AACzE,MAAM,UAAU,mBAAmB,CAAC,CAAS;IAC3C,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACtC,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IAClD,IAAI,KAAK,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,SAAS,EAAE,CAAC;IAC/D,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,aAAa,CAAC,CAAS;IACrC,MAAM,CAAC,GAAG,mBAAmB,CAAC,CAAC,CAAC,CAAC;IACjC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,iBAAiB,CAAC,CAAS;IAClC,MAAM,CAAC,GAAG,mBAAmB,CAAC,CAAC,CAAC,CAAC;IACjC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AAChD,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,cAAc,CAAC,CAAS;IACtC,MAAM,CAAC,GAAG,mBAAmB,CAAC,CAAC,CAAC,CAAC;IACjC,OAAO,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAC/C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,CAAS;IACrC,MAAM,CAAC,GAAG,mBAAmB,CAAC,CAAC,CAAC,CAAC;IAEjC,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QACrB,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ;QAC9B,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;SAAM,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9B,IAAI,GAAG,IAAI,CAAC;QACZ,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;SAAM,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7B,IAAI,GAAG,GAAG,CAAC;QACX,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;IAED,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACtC,IAAI,OAAO,KAAK,EAAE,IAAI,OAAO,KAAK,GAAG;YAAE,SAAS;QAChD,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YAChC,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;gBACnC,GAAG,CAAC,GAAG,EAAE,CAAC;YACZ,CAAC;iBAAM,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;gBACvB,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjB,CAAC;YACD,SAAS;QACX,CAAC;QACD,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACpB,CAAC;IAED,OAAO,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC9B,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,MAAc;IACtD,MAAM,gBAAgB,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IAC/C,IAAI,cAAc,CAAC,gBAAgB,CAAC;QAAE,OAAO,gBAAgB,CAAC;IAC9D,MAAM,cAAc,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IAC3C,IAAI,cAAc,KAAK,EAAE;QAAE,OAAO,kBAAkB,CAAC,gBAAgB,CAAC,CAAC;IACvE,OAAO,aAAa,CAAC,GAAG,cAAc,IAAI,gBAAgB,EAAE,CAAC,CAAC;AAChE,CAAC;AAED,SAAS,kBAAkB,CAAC,CAAS;IACnC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AAC5B,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,YAAY,CAAC,KAAa,EAAE,MAAc;IACxD,MAAM,CAAC,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAC/B,MAAM,CAAC,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IACvC,MAAM,WAAW,GAAG,iBAAiB,CAAC,CAAC,CAAC,IAAI,iBAAiB,CAAC,CAAC,CAAC,CAAC;IACjE,MAAM,EAAE,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7C,MAAM,EAAE,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7C,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAC3B,MAAM,MAAM,GAAG,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC;IAChD,OAAO,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,UAAU,CAAC,CAAS;IAClC,IAAI,CAAC,KAAK,GAAG;QAAE,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC;IACnC,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9C,OAAO,EAAE,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,oBAAoB,CAAC,CAAS;IAC5C,OAAO,mBAAmB,CAAC,CAAC,CAAC;SAC1B,KAAK,CAAC,GAAG,CAAC;SACV,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;AAChD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAc,EAAE,WAAmB;IAClE,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,kBAAkB,CAAC,uBAAuB,EAAE,cAAc,EAAE;YACpE,IAAI,EAAE,uEAAuE;SAC9E,CAAC,CAAC;IACL,CAAC;IACD,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IACzB,IAAI,GAAG,KAAK,EAAE,EAAE,CAAC;QACf,MAAM,IAAI,kBAAkB,CAAC,wBAAwB,EAAE,cAAc,EAAE;YACrE,IAAI,EAAE,uEAAuE;SAC9E,CAAC,CAAC;IACL,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,kBAAkB,CAAC,2BAA2B,EAAE,cAAc,EAAE;YACxE,IAAI,EAAE,kCAAkC;SACzC,CAAC,CAAC;IACL,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,kBAAkB,CAAC,IAAI,GAAG,2CAA2C,EAAE,cAAc,EAAE;YAC/F,IAAI,EAAE,iDAAiD;SACxD,CAAC,CAAC;IACL,CAAC;IAED,MAAM,IAAI,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC;IACxC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,kBAAkB,CAAC,uCAAuC,WAAW,GAAG,EAAE,cAAc,CAAC,CAAC;IACtG,CAAC;IAED,MAAM,QAAQ,GAAG,aAAa,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAE7C,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,kBAAkB,CAAC,SAAS,GAAG,qCAAqC,EAAE,cAAc,EAAE;YAC9F,IAAI,EAAE,qBAAqB,IAAI,6FAA6F;YAC5H,OAAO,EAAE,YAAY,QAAQ,EAAE;SAChC,CAAC,CAAC;IACL,CAAC;IACD,qFAAqF;IACrF,IAAI,oBAAoB,CAAC,cAAc,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC;QACzD,MAAM,IAAI,kBAAkB,CAAC,SAAS,GAAG,mCAAmC,EAAE,cAAc,EAAE;YAC5F,IAAI,EAAE,wFAAwF;SAC/F,CAAC,CAAC;IACL,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,YAAY,CAAC,CAAS;IACpC,MAAM,UAAU,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;IACpC,OAAO,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;AAC5G,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,WAAW,CAAC,CAAS;IACnC,OAAO,aAAa,CAAC,CAAC,CAAC,CAAC;AAC1B,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,CAAS;IACpD,MAAM,CAAC,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;IAC3B,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC;IAClC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACvB,MAAM,IAAI,GAAG,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;IAC3C,OAAO,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC9B,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,GAAG,QAAkB;IACjE,OAAO,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC/C,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,YAAY,CAAC,MAAc,EAAE,SAAS,GAAG,OAAO;IAC9D,MAAM,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;IAC9E,OAAO,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,GAAG,MAAM,IAAI,MAAM,GAAG,SAAS,EAAE,CAAC,CAAC,CAAC;AAClF,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,UAAU,CAAC,CAAS;IAClC,OAAO,YAAY,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC;AACnD,CAAC;AAKD,MAAM,KAAK,GAAG,CAAC,GAAW,EAAE,GAAW,EAAE,EAAE,CAAC,CAAC,KAAa,EAAE,EAAE,CAC5D,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC;AACtE,MAAM,KAAK,GAAG,CAAC,GAAG,MAAgB,EAAE,EAAE,CAAC,CAAC,KAAa,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACjF,+EAA+E;AAC/E,MAAM,SAAS,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,KAAK,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAE9G;;;;;;;GAOG;AACH,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAyB;IACxD,CAAC,UAAU,EAAE,IAAI,CAAC;IAClB,CAAC,IAAI,EAAE,IAAI,CAAC;IACZ,CAAC,uBAAuB,EAAE,IAAI,CAAC;IAC/B,CAAC,eAAe,EAAE,IAAI,CAAC;IACvB,CAAC,kBAAkB,EAAE,IAAI,CAAC;IAC1B,CAAC,sBAAsB,EAAE,IAAI,CAAC;IAC9B,CAAC,SAAS,EAAE,IAAI,CAAC;IACjB,CAAC,oBAAoB,EAAE,IAAI,CAAC;IAC5B,CAAC,WAAW,EAAE,IAAI,CAAC;IACnB,CAAC,QAAQ,EAAE,IAAI,CAAC;IAChB,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IACpG,CAAC,eAAe,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,QAAQ,EAAE,kBAAkB,EAAE,CAAC;IACxE,CAAC,gBAAgB,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAC3E,CAAC,kBAAkB,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,EAAE,QAAQ,EAAE,+BAA+B,EAAE,CAAC;IAC9F,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,EAAE,QAAQ,EAAE,+BAA+B,EAAE,CAAC;IACvF,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAClE,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,sBAAsB,EAAE,CAAC;IAChH,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,sBAAsB,EAAE,CAAC;IACzG,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,8BAA8B,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,yBAAyB,EAAE,CAAC;IACzG,CAAC,eAAe,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,iCAAiC,EAAE,CAAC;IACnF;QACE,SAAS;QACT;YACE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,+BAA+B,CAAC;YAC7I,QAAQ,EAAE,yBAAyB;SACpC;KACF;IACD,CAAC,oBAAoB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,+BAA+B,EAAE,CAAC;IACvI,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,+BAA+B,EAAE,CAAC;IACvH,iFAAiF;IACjF;QACE,gBAAgB;QAChB,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,4CAA4C,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,GAAG,EAAE,QAAQ,EAAE,WAAW,EAAE;KAChI;CACF,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,MAAM,GAAG,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACtD,IAAI,GAAG,KAAK,EAAE,EAAE,CAAC;QACf,MAAM,IAAI,kBAAkB,CAAC,4BAA4B,EAAE,cAAc,CAAC,CAAC;IAC7E,CAAC;IACD,IAAI,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,kBAAkB,CAAC,aAAa,GAAG,iCAAiC,EAAE,cAAc,EAAE;YAC9F,IAAI,EAAE,0FAA0F;SACjG,CAAC,CAAC;IACL,CAAC;IACD,MAAM,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC5B,MAAM,IAAI,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAChD,MAAM,KAAK,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IACxD,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,kBAAkB,CAAC,aAAa,GAAG,qCAAqC,EAAE,cAAc,EAAE;YAClG,IAAI,EAAE,kBAAkB,CAAC,GAAG,iBAAiB,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,4DAA4D;SAC7H,CAAC,CAAC;IACL,CAAC;IACD,MAAM,KAAK,GAAG,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;IAClD,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,MAAM,IAAI,kBAAkB,CAAC,SAAS,IAAI,yBAAyB,EAAE,cAAc,CAAC,CAAC;QACvF,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IACD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,IAAI,KAAK,CAAC,QAAQ;YAAE,OAAO,GAAG,CAAC;QAC/B,MAAM,IAAI,kBAAkB,CAAC,SAAS,IAAI,wBAAwB,IAAI,UAAU,EAAE,cAAc,EAAE;YAChG,IAAI,EAAE,GAAG,IAAI,UAAU,KAAK,CAAC,QAAQ,GAAG;SACzC,CAAC,CAAC;IACL,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,kBAAkB,CAAC,qBAAqB,IAAI,MAAM,KAAK,GAAG,EAAE,cAAc,EAAE;YACpF,IAAI,EAAE,GAAG,IAAI,UAAU,KAAK,CAAC,QAAQ,GAAG;SACzC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,CAAS;IAC7C,MAAM,UAAU,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;IACpC,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,IAAI,OAAO,GAAG,UAAU,CAAC;IACzB,SAAS,CAAC;QACR,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,QAAQ,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YAClE,OAAO,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,GAAG,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACzF,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;YAC1D,IAAI,MAAM,KAAK,OAAO,IAAI,MAAM,KAAK,EAAE,IAAI,MAAM,KAAK,GAAG;gBAAE,OAAO,UAAU,CAAC;YAC7E,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;YACxC,OAAO,GAAG,MAAM,CAAC;QACnB,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,MAAc,EAAE,IAAyB,EAAE,IAAY;IAC/F,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IACnD,IAAI,IAAI,EAAE,cAAc,EAAE,EAAE,CAAC;QAC3B,MAAM,IAAI,kBAAkB,CAAC,wCAAwC,aAAa,CAAC,MAAM,CAAC,EAAE,EAAE,cAAc,EAAE;YAC5G,IAAI,EAAE,+DAA+D;SACtE,CAAC,CAAC;IACL,CAAC;IACD,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,MAAM,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAC5E,+EAA+E;IAC/E,MAAM,KAAK,GAAG,WAAW,CAAC,QAAQ,GAAG,WAAW,CAAC,OAAO,GAAG,WAAW,CAAC,OAAO,GAAG,CAAC,WAAW,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC;IAC/G,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IAChD,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,MAAc,EAAE,IAAY;IACrE,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACnG,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,QAAQ,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,kBAAkB,CAAC,SAAS,aAAa,CAAC,MAAM,CAAC,qCAAqC,EAAE,cAAc,EAAE;YAChH,IAAI,EAAE,4BAA4B,aAAa,CAAC,IAAI,CAAC,mDAAmD;SACzG,CAAC,CAAC;IACL,CAAC;AACH,CAAC"}
@@ -37,6 +37,20 @@ export interface ProcessOutcome {
37
37
  * Children are tracked so shutdown/disconnect can never orphan them.
38
38
  */
39
39
  export declare function runProcess(command: string, args: string[], options: RunProcessOptions): Promise<ProcessOutcome>;
40
+ /**
41
+ * Cap how many children may run at once; 0 means no cap. The HTTP bridge
42
+ * sets one (PW_MCP_MAX_CHILDREN, default 4) so remote callers cannot
43
+ * start an unbounded number of browsers and test runs.
44
+ */
45
+ export declare function setMaxChildren(limit: number): void;
46
+ /**
47
+ * When on (the HTTP bridge turns it on), children get only an allowlisted
48
+ * environment, so code run on behalf of a remote caller cannot read
49
+ * deployment credentials.
50
+ */
51
+ export declare function setScrubChildEnv(enabled: boolean): void;
52
+ /** Environment for a spawned child: process.env plus overrides, allowlisted when scrubbing. */
53
+ export declare function childEnv(overrides?: Record<string, string>): NodeJS.ProcessEnv;
40
54
  /**
41
55
  * Force-kill every child still running (and its tree). Called on
42
56
  * shutdown and when the MCP client disconnects so no browser or test
@@ -51,13 +65,3 @@ export declare function killActiveChildren(): number;
51
65
  export declare function runTests(options: RunTestOptions): Promise<RunTestResult>;
52
66
  /** List tests via `playwright test --list`, falling back to file scan. */
53
67
  export declare function listTests(options: ListTestsOptions): Promise<ListTestsResult>;
54
- export interface ServerProbe {
55
- reachable: boolean;
56
- status?: number;
57
- error?: string;
58
- }
59
- /**
60
- * Quick reachability check for the app under test, used to tell the
61
- * user "start your dev server" instead of surfacing ECONNREFUSED.
62
- */
63
- export declare function probeServer(url: string, timeoutMs?: number): Promise<ServerProbe>;
@@ -14,7 +14,7 @@ import path from 'node:path';
14
14
  import { PlaywrightMcpError } from '../types/index.js';
15
15
  import { findTestFiles, missingPlaywrightMessage, detectProject, } from './project-detector.js';
16
16
  import { extractJsonFromText, extractTestTitles, diagnoseOutput, parseReportJson, parseProjectNames, } from './report-parser.js';
17
- import { isAbsolutePath, isPathInside, normalizePath, relativeToRoot, sanitizeCliArg, tempFilePath, toNativePath, } from './path-utils.js';
17
+ import { hasOptionLikeSegment, isAbsolutePath, isPathInside, normalizePath, relativeToRoot, sanitizeCliArg, tempFilePath, toNativePath, } from './path-utils.js';
18
18
  import { logger } from './logger.js';
19
19
  const DEFAULT_RUN_TIMEOUT_MS = 120_000;
20
20
  const DEFAULT_LIST_TIMEOUT_MS = 60_000;
@@ -145,6 +145,21 @@ export function runProcess(command, args, options) {
145
145
  chunks.shift();
146
146
  }
147
147
  };
148
+ if (maxChildren > 0 && activeChildren.size >= maxChildren) {
149
+ resolve({
150
+ code: null,
151
+ signalName: null,
152
+ stdout: '',
153
+ stderr: '',
154
+ timedOut: false,
155
+ aborted: false,
156
+ spawnError: {
157
+ code: 'EBUSY',
158
+ message: `Too many runs in progress (limit ${maxChildren}). Try again when one finishes.`,
159
+ },
160
+ });
161
+ return;
162
+ }
148
163
  let child;
149
164
  try {
150
165
  child = spawn(command, args, {
@@ -238,6 +253,86 @@ export function runProcess(command, args, options) {
238
253
  }
239
254
  /** Every child spawned by runProcess that has not settled yet. */
240
255
  const activeChildren = new Set();
256
+ let maxChildren = 0;
257
+ /**
258
+ * Cap how many children may run at once; 0 means no cap. The HTTP bridge
259
+ * sets one (PW_MCP_MAX_CHILDREN, default 4) so remote callers cannot
260
+ * start an unbounded number of browsers and test runs.
261
+ */
262
+ export function setMaxChildren(limit) {
263
+ maxChildren = Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : 0;
264
+ }
265
+ let scrubChildEnv = false;
266
+ /**
267
+ * When on (the HTTP bridge turns it on), children get only an allowlisted
268
+ * environment, so code run on behalf of a remote caller cannot read
269
+ * deployment credentials.
270
+ */
271
+ export function setScrubChildEnv(enabled) {
272
+ scrubChildEnv = enabled;
273
+ }
274
+ /** Variables a child keeps in scrubbed mode (compared upper-case). */
275
+ const CHILD_ENV_ALLOW = new Set([
276
+ 'PATH',
277
+ 'PATHEXT',
278
+ 'HOME',
279
+ 'USERPROFILE',
280
+ 'TMPDIR',
281
+ 'TEMP',
282
+ 'TMP',
283
+ 'LANG',
284
+ 'LANGUAGE',
285
+ 'TZ',
286
+ 'CI',
287
+ 'SYSTEMROOT',
288
+ 'SYSTEMDRIVE',
289
+ 'WINDIR',
290
+ 'COMSPEC',
291
+ 'APPDATA',
292
+ 'LOCALAPPDATA',
293
+ 'PROGRAMDATA',
294
+ 'PROGRAMFILES',
295
+ 'PROGRAMFILES(X86)',
296
+ 'XDG_CACHE_HOME',
297
+ 'XDG_CONFIG_HOME',
298
+ 'XDG_RUNTIME_DIR',
299
+ 'DISPLAY',
300
+ ]);
301
+ const CHILD_ENV_ALLOW_PREFIXES = ['LC_', 'NPM_CONFIG_', 'PLAYWRIGHT_'];
302
+ /** Even allowlisted names are dropped when they look like a secret. */
303
+ const SECRET_ENV_RE = /(TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|PRIVATE|API_?KEY|ACCESS_?KEY|AUTH|SESSION|COOKIE|DATABASE_URL|_DSN$)/i;
304
+ /** Names listed in PW_MCP_PASSTHROUGH_ENV (comma-separated), never the bridge token. */
305
+ function passthroughNames() {
306
+ return new Set((process.env.PW_MCP_PASSTHROUGH_ENV ?? '')
307
+ .split(',')
308
+ .map((name) => name.trim().toUpperCase())
309
+ .filter((name) => name && name !== 'PW_MCP_HTTP_TOKEN'));
310
+ }
311
+ /** A URL with userinfo (`scheme://user:pass@host`), e.g. an authenticated proxy. */
312
+ const URL_CREDENTIALS_RE = /:\/\/[^/\s@]*@/;
313
+ function keepInScrubbedEnv(key, value, passthrough) {
314
+ const upper = key.toUpperCase();
315
+ if (upper === 'PW_MCP_HTTP_TOKEN')
316
+ return false;
317
+ if (passthrough.has(upper))
318
+ return true;
319
+ if (SECRET_ENV_RE.test(key))
320
+ return false;
321
+ if (value !== undefined && URL_CREDENTIALS_RE.test(value))
322
+ return false;
323
+ return CHILD_ENV_ALLOW.has(upper) || CHILD_ENV_ALLOW_PREFIXES.some((prefix) => upper.startsWith(prefix));
324
+ }
325
+ /** Environment for a spawned child: process.env plus overrides, allowlisted when scrubbing. */
326
+ export function childEnv(overrides = {}) {
327
+ const env = {};
328
+ const passthrough = scrubChildEnv ? passthroughNames() : new Set();
329
+ for (const [key, value] of Object.entries(process.env)) {
330
+ if (scrubChildEnv && !keepInScrubbedEnv(key, value, passthrough))
331
+ continue;
332
+ env[key] = value;
333
+ }
334
+ return { ...env, ...overrides };
335
+ }
241
336
  /**
242
337
  * Force-kill every child still running (and its tree). Called on
243
338
  * shutdown and when the MCP client disconnects so no browser or test
@@ -260,6 +355,13 @@ function sanitizeTestFile(file, root) {
260
355
  hint: `Only test paths inside ${root} are allowed.`,
261
356
  });
262
357
  }
358
+ // Test files go on the command line as positionals; a segment like
359
+ // "--output=/x" would be parsed by Playwright as an option.
360
+ if (hasOptionLikeSegment(relativeToRoot(root, resolved))) {
361
+ throw new PlaywrightMcpError(`Test path "${file}" has a segment starting with "-"`, 'INVALID_PATH', {
362
+ hint: 'Rename the file or folder so no path segment starts with "-".',
363
+ });
364
+ }
263
365
  return resolved;
264
366
  }
265
367
  /** Best-effort `retries: N` from the config source text. */
@@ -383,13 +485,18 @@ export async function runTests(options) {
383
485
  args.push('--trace=retain-on-failure');
384
486
  for (const extra of options.extraArgs ?? [])
385
487
  args.push(sanitizeCliArg(extra));
488
+ // The JSON reporter must be the only reporter and come last: results are
489
+ // parsed from its output. sanitizeCliArg rejects --reporter; this keeps
490
+ // the invariant if that ever changes.
491
+ if (args.some((arg) => arg.startsWith('--reporter'))) {
492
+ throw new PlaywrightMcpError('Caller arguments may not set --reporter', 'INVALID_PATH');
493
+ }
386
494
  args.push('--reporter=json');
387
- const env = {
388
- ...process.env,
495
+ const env = childEnv({
389
496
  PLAYWRIGHT_JSON_OUTPUT_NAME: reportPath,
390
497
  FORCE_COLOR: '0',
391
498
  PW_TEST_HTML_REPORT_OPEN: 'never',
392
- };
499
+ });
393
500
  const timeoutMs = clampInt(options.timeoutMs, 1_000, 3_600_000) ?? DEFAULT_RUN_TIMEOUT_MS;
394
501
  const startedAt = Date.now();
395
502
  logger.debug('Starting Playwright run', { args: args.join(' '), cwd: root, timeoutMs });
@@ -425,7 +532,9 @@ export async function runTests(options) {
425
532
  hint =
426
533
  outcome.spawnError.code === 'ENOENT'
427
534
  ? 'Node.js could not be located on PATH. Reinstall Node >= 18 and retry.'
428
- : `Failed to start Playwright: ${outcome.spawnError.message}`;
535
+ : outcome.spawnError.code === 'EBUSY'
536
+ ? 'The server is at its limit of concurrent runs; retry when one finishes.'
537
+ : `Failed to start Playwright: ${outcome.spawnError.message}`;
429
538
  }
430
539
  else if (outcome.aborted) {
431
540
  errorKind = 'CLIENT_DISCONNECT';
@@ -486,17 +595,23 @@ async function runPlaywrightList(cli, options, configPath) {
486
595
  if (configPath)
487
596
  args.push('--config', toNativePath(configPath));
488
597
  args.push('--reporter=json');
489
- const env = {
490
- ...process.env,
598
+ const env = childEnv({
491
599
  PLAYWRIGHT_JSON_OUTPUT_NAME: listReportPath,
492
600
  FORCE_COLOR: '0',
493
- };
601
+ });
494
602
  const outcome = await runProcess(process.execPath, [cli, ...args], {
495
603
  cwd: root,
496
604
  env,
497
605
  timeoutMs: DEFAULT_LIST_TIMEOUT_MS,
498
606
  signal: options.signal,
499
607
  });
608
+ if (outcome.spawnError) {
609
+ throw new PlaywrightMcpError(outcome.spawnError.message, 'SPAWN_FAILED', {
610
+ hint: outcome.spawnError.code === 'EBUSY'
611
+ ? 'The server is at its limit of concurrent runs; retry when one finishes.'
612
+ : 'Could not start Playwright.',
613
+ });
614
+ }
500
615
  const raw = (await readFileText(listReportPath)) ?? extractJsonFromText(outcome.stdout) ?? undefined;
501
616
  if (raw === undefined) {
502
617
  const diagnosis = diagnoseOutput(outcome.stderr || outcome.stdout);
@@ -525,8 +640,11 @@ export async function listTests(options) {
525
640
  throw new PlaywrightMcpError(`Config file not found: ${configPath}`, 'NO_CONFIG');
526
641
  }
527
642
  }
528
- const cli = await resolvePlaywrightCli(root);
529
- if (cli === null) {
643
+ const cli = options.noSpawn ? null : await resolvePlaywrightCli(root);
644
+ if (options.noSpawn) {
645
+ // Source scan only (restricted callers); not an error.
646
+ }
647
+ else if (cli === null) {
530
648
  const detection = await detectProject(root);
531
649
  const { message, hint } = missingPlaywrightMessage(detection);
532
650
  error = { kind: 'NO_PLAYWRIGHT', message, hint };
@@ -571,24 +689,4 @@ export async function listTests(options) {
571
689
  error,
572
690
  };
573
691
  }
574
- /**
575
- * Quick reachability check for the app under test, used to tell the
576
- * user "start your dev server" instead of surfacing ECONNREFUSED.
577
- */
578
- export async function probeServer(url, timeoutMs = 2_000) {
579
- try {
580
- const response = await fetch(url, {
581
- method: 'GET',
582
- redirect: 'follow',
583
- signal: AbortSignal.timeout(timeoutMs),
584
- });
585
- return { reachable: true, status: response.status };
586
- }
587
- catch (err) {
588
- return {
589
- reachable: false,
590
- error: err instanceof Error ? err.message : String(err),
591
- };
592
- }
593
- }
594
692
  //# sourceMappingURL=playwright-runner.js.map