@rstest/browser 0.7.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,2576 @@
1
+ import { __webpack_require__ } from "./rslib-runtime.js";
2
+ import { existsSync } from "node:fs";
3
+ import promises from "node:fs/promises";
4
+ import { fileURLToPath } from "node:url";
5
+ import { TEMP_RSTEST_OUTPUT_DIR, color, getSetupFiles, getTestEntries, isDebug, logger, rsbuild, serializableConfig } from "@rstest/core/browser";
6
+ import open_editor from "open-editor";
7
+ import { basename, dirname, join, normalize, relative, resolve as external_pathe_resolve } from "pathe";
8
+ import sirv from "sirv";
9
+ import { WebSocketServer } from "ws";
10
+ __webpack_require__.add({
11
+ "../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/index.js" (module, __unused_rspack_exports, __webpack_require__) {
12
+ const pico = __webpack_require__("../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/picomatch.js");
13
+ const utils = __webpack_require__("../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/utils.js");
14
+ function picomatch(glob, options, returnState = false) {
15
+ if (options && (null === options.windows || void 0 === options.windows)) options = {
16
+ ...options,
17
+ windows: utils.isWindows()
18
+ };
19
+ return pico(glob, options, returnState);
20
+ }
21
+ Object.assign(picomatch, pico);
22
+ module.exports = picomatch;
23
+ },
24
+ "../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/constants.js" (module) {
25
+ const WIN_SLASH = '\\\\/';
26
+ const WIN_NO_SLASH = `[^${WIN_SLASH}]`;
27
+ const DOT_LITERAL = '\\.';
28
+ const PLUS_LITERAL = '\\+';
29
+ const QMARK_LITERAL = '\\?';
30
+ const SLASH_LITERAL = '\\/';
31
+ const ONE_CHAR = '(?=.)';
32
+ const QMARK = '[^/]';
33
+ const END_ANCHOR = `(?:${SLASH_LITERAL}|$)`;
34
+ const START_ANCHOR = `(?:^|${SLASH_LITERAL})`;
35
+ const DOTS_SLASH = `${DOT_LITERAL}{1,2}${END_ANCHOR}`;
36
+ const NO_DOT = `(?!${DOT_LITERAL})`;
37
+ const NO_DOTS = `(?!${START_ANCHOR}${DOTS_SLASH})`;
38
+ const NO_DOT_SLASH = `(?!${DOT_LITERAL}{0,1}${END_ANCHOR})`;
39
+ const NO_DOTS_SLASH = `(?!${DOTS_SLASH})`;
40
+ const QMARK_NO_DOT = `[^.${SLASH_LITERAL}]`;
41
+ const STAR = `${QMARK}*?`;
42
+ const SEP = '/';
43
+ const POSIX_CHARS = {
44
+ DOT_LITERAL,
45
+ PLUS_LITERAL,
46
+ QMARK_LITERAL,
47
+ SLASH_LITERAL,
48
+ ONE_CHAR,
49
+ QMARK,
50
+ END_ANCHOR,
51
+ DOTS_SLASH,
52
+ NO_DOT,
53
+ NO_DOTS,
54
+ NO_DOT_SLASH,
55
+ NO_DOTS_SLASH,
56
+ QMARK_NO_DOT,
57
+ STAR,
58
+ START_ANCHOR,
59
+ SEP
60
+ };
61
+ const WINDOWS_CHARS = {
62
+ ...POSIX_CHARS,
63
+ SLASH_LITERAL: `[${WIN_SLASH}]`,
64
+ QMARK: WIN_NO_SLASH,
65
+ STAR: `${WIN_NO_SLASH}*?`,
66
+ DOTS_SLASH: `${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$)`,
67
+ NO_DOT: `(?!${DOT_LITERAL})`,
68
+ NO_DOTS: `(?!(?:^|[${WIN_SLASH}])${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`,
69
+ NO_DOT_SLASH: `(?!${DOT_LITERAL}{0,1}(?:[${WIN_SLASH}]|$))`,
70
+ NO_DOTS_SLASH: `(?!${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`,
71
+ QMARK_NO_DOT: `[^.${WIN_SLASH}]`,
72
+ START_ANCHOR: `(?:^|[${WIN_SLASH}])`,
73
+ END_ANCHOR: `(?:[${WIN_SLASH}]|$)`,
74
+ SEP: '\\'
75
+ };
76
+ const POSIX_REGEX_SOURCE = {
77
+ alnum: 'a-zA-Z0-9',
78
+ alpha: 'a-zA-Z',
79
+ ascii: '\\x00-\\x7F',
80
+ blank: ' \\t',
81
+ cntrl: '\\x00-\\x1F\\x7F',
82
+ digit: '0-9',
83
+ graph: '\\x21-\\x7E',
84
+ lower: 'a-z',
85
+ print: '\\x20-\\x7E ',
86
+ punct: '\\-!"#$%&\'()\\*+,./:;<=>?@[\\]^_`{|}~',
87
+ space: ' \\t\\r\\n\\v\\f',
88
+ upper: 'A-Z',
89
+ word: 'A-Za-z0-9_',
90
+ xdigit: 'A-Fa-f0-9'
91
+ };
92
+ module.exports = {
93
+ MAX_LENGTH: 65536,
94
+ POSIX_REGEX_SOURCE,
95
+ REGEX_BACKSLASH: /\\(?![*+?^${}(|)[\]])/g,
96
+ REGEX_NON_SPECIAL_CHARS: /^[^@![\].,$*+?^{}()|\\/]+/,
97
+ REGEX_SPECIAL_CHARS: /[-*+?.^${}(|)[\]]/,
98
+ REGEX_SPECIAL_CHARS_BACKREF: /(\\?)((\W)(\3*))/g,
99
+ REGEX_SPECIAL_CHARS_GLOBAL: /([-*+?.^${}(|)[\]])/g,
100
+ REGEX_REMOVE_BACKSLASH: /(?:\[.*?[^\\]\]|\\(?=.))/g,
101
+ REPLACEMENTS: {
102
+ __proto__: null,
103
+ '***': '*',
104
+ '**/**': '**',
105
+ '**/**/**': '**'
106
+ },
107
+ CHAR_0: 48,
108
+ CHAR_9: 57,
109
+ CHAR_UPPERCASE_A: 65,
110
+ CHAR_LOWERCASE_A: 97,
111
+ CHAR_UPPERCASE_Z: 90,
112
+ CHAR_LOWERCASE_Z: 122,
113
+ CHAR_LEFT_PARENTHESES: 40,
114
+ CHAR_RIGHT_PARENTHESES: 41,
115
+ CHAR_ASTERISK: 42,
116
+ CHAR_AMPERSAND: 38,
117
+ CHAR_AT: 64,
118
+ CHAR_BACKWARD_SLASH: 92,
119
+ CHAR_CARRIAGE_RETURN: 13,
120
+ CHAR_CIRCUMFLEX_ACCENT: 94,
121
+ CHAR_COLON: 58,
122
+ CHAR_COMMA: 44,
123
+ CHAR_DOT: 46,
124
+ CHAR_DOUBLE_QUOTE: 34,
125
+ CHAR_EQUAL: 61,
126
+ CHAR_EXCLAMATION_MARK: 33,
127
+ CHAR_FORM_FEED: 12,
128
+ CHAR_FORWARD_SLASH: 47,
129
+ CHAR_GRAVE_ACCENT: 96,
130
+ CHAR_HASH: 35,
131
+ CHAR_HYPHEN_MINUS: 45,
132
+ CHAR_LEFT_ANGLE_BRACKET: 60,
133
+ CHAR_LEFT_CURLY_BRACE: 123,
134
+ CHAR_LEFT_SQUARE_BRACKET: 91,
135
+ CHAR_LINE_FEED: 10,
136
+ CHAR_NO_BREAK_SPACE: 160,
137
+ CHAR_PERCENT: 37,
138
+ CHAR_PLUS: 43,
139
+ CHAR_QUESTION_MARK: 63,
140
+ CHAR_RIGHT_ANGLE_BRACKET: 62,
141
+ CHAR_RIGHT_CURLY_BRACE: 125,
142
+ CHAR_RIGHT_SQUARE_BRACKET: 93,
143
+ CHAR_SEMICOLON: 59,
144
+ CHAR_SINGLE_QUOTE: 39,
145
+ CHAR_SPACE: 32,
146
+ CHAR_TAB: 9,
147
+ CHAR_UNDERSCORE: 95,
148
+ CHAR_VERTICAL_LINE: 124,
149
+ CHAR_ZERO_WIDTH_NOBREAK_SPACE: 65279,
150
+ extglobChars (chars) {
151
+ return {
152
+ '!': {
153
+ type: 'negate',
154
+ open: '(?:(?!(?:',
155
+ close: `))${chars.STAR})`
156
+ },
157
+ '?': {
158
+ type: 'qmark',
159
+ open: '(?:',
160
+ close: ')?'
161
+ },
162
+ '+': {
163
+ type: 'plus',
164
+ open: '(?:',
165
+ close: ')+'
166
+ },
167
+ '*': {
168
+ type: 'star',
169
+ open: '(?:',
170
+ close: ')*'
171
+ },
172
+ '@': {
173
+ type: 'at',
174
+ open: '(?:',
175
+ close: ')'
176
+ }
177
+ };
178
+ },
179
+ globChars (win32) {
180
+ return true === win32 ? WINDOWS_CHARS : POSIX_CHARS;
181
+ }
182
+ };
183
+ },
184
+ "../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/parse.js" (module, __unused_rspack_exports, __webpack_require__) {
185
+ const constants = __webpack_require__("../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/constants.js");
186
+ const utils = __webpack_require__("../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/utils.js");
187
+ const { MAX_LENGTH, POSIX_REGEX_SOURCE, REGEX_NON_SPECIAL_CHARS, REGEX_SPECIAL_CHARS_BACKREF, REPLACEMENTS } = constants;
188
+ const expandRange = (args, options)=>{
189
+ if ('function' == typeof options.expandRange) return options.expandRange(...args, options);
190
+ args.sort();
191
+ const value = `[${args.join('-')}]`;
192
+ try {
193
+ new RegExp(value);
194
+ } catch (ex) {
195
+ return args.map((v)=>utils.escapeRegex(v)).join('..');
196
+ }
197
+ return value;
198
+ };
199
+ const syntaxError = (type, char)=>`Missing ${type}: "${char}" - use "\\\\${char}" to match literal characters`;
200
+ const parse = (input, options)=>{
201
+ if ('string' != typeof input) throw new TypeError('Expected a string');
202
+ input = REPLACEMENTS[input] || input;
203
+ const opts = {
204
+ ...options
205
+ };
206
+ const max = 'number' == typeof opts.maxLength ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH;
207
+ let len = input.length;
208
+ if (len > max) throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`);
209
+ const bos = {
210
+ type: 'bos',
211
+ value: '',
212
+ output: opts.prepend || ''
213
+ };
214
+ const tokens = [
215
+ bos
216
+ ];
217
+ const capture = opts.capture ? '' : '?:';
218
+ const PLATFORM_CHARS = constants.globChars(opts.windows);
219
+ const EXTGLOB_CHARS = constants.extglobChars(PLATFORM_CHARS);
220
+ const { DOT_LITERAL, PLUS_LITERAL, SLASH_LITERAL, ONE_CHAR, DOTS_SLASH, NO_DOT, NO_DOT_SLASH, NO_DOTS_SLASH, QMARK, QMARK_NO_DOT, STAR, START_ANCHOR } = PLATFORM_CHARS;
221
+ const globstar = (opts)=>`(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`;
222
+ const nodot = opts.dot ? '' : NO_DOT;
223
+ const qmarkNoDot = opts.dot ? QMARK : QMARK_NO_DOT;
224
+ let star = true === opts.bash ? globstar(opts) : STAR;
225
+ if (opts.capture) star = `(${star})`;
226
+ if ('boolean' == typeof opts.noext) opts.noextglob = opts.noext;
227
+ const state = {
228
+ input,
229
+ index: -1,
230
+ start: 0,
231
+ dot: true === opts.dot,
232
+ consumed: '',
233
+ output: '',
234
+ prefix: '',
235
+ backtrack: false,
236
+ negated: false,
237
+ brackets: 0,
238
+ braces: 0,
239
+ parens: 0,
240
+ quotes: 0,
241
+ globstar: false,
242
+ tokens
243
+ };
244
+ input = utils.removePrefix(input, state);
245
+ len = input.length;
246
+ const extglobs = [];
247
+ const braces = [];
248
+ const stack = [];
249
+ let prev = bos;
250
+ let value;
251
+ const eos = ()=>state.index === len - 1;
252
+ const peek = state.peek = (n = 1)=>input[state.index + n];
253
+ const advance = state.advance = ()=>input[++state.index] || '';
254
+ const remaining = ()=>input.slice(state.index + 1);
255
+ const consume = (value = '', num = 0)=>{
256
+ state.consumed += value;
257
+ state.index += num;
258
+ };
259
+ const append = (token)=>{
260
+ state.output += null != token.output ? token.output : token.value;
261
+ consume(token.value);
262
+ };
263
+ const negate = ()=>{
264
+ let count = 1;
265
+ while('!' === peek() && ('(' !== peek(2) || '?' === peek(3))){
266
+ advance();
267
+ state.start++;
268
+ count++;
269
+ }
270
+ if (count % 2 === 0) return false;
271
+ state.negated = true;
272
+ state.start++;
273
+ return true;
274
+ };
275
+ const increment = (type)=>{
276
+ state[type]++;
277
+ stack.push(type);
278
+ };
279
+ const decrement = (type)=>{
280
+ state[type]--;
281
+ stack.pop();
282
+ };
283
+ const push = (tok)=>{
284
+ if ('globstar' === prev.type) {
285
+ const isBrace = state.braces > 0 && ('comma' === tok.type || 'brace' === tok.type);
286
+ const isExtglob = true === tok.extglob || extglobs.length && ('pipe' === tok.type || 'paren' === tok.type);
287
+ if ('slash' !== tok.type && 'paren' !== tok.type && !isBrace && !isExtglob) {
288
+ state.output = state.output.slice(0, -prev.output.length);
289
+ prev.type = 'star';
290
+ prev.value = '*';
291
+ prev.output = star;
292
+ state.output += prev.output;
293
+ }
294
+ }
295
+ if (extglobs.length && 'paren' !== tok.type) extglobs[extglobs.length - 1].inner += tok.value;
296
+ if (tok.value || tok.output) append(tok);
297
+ if (prev && 'text' === prev.type && 'text' === tok.type) {
298
+ prev.output = (prev.output || prev.value) + tok.value;
299
+ prev.value += tok.value;
300
+ return;
301
+ }
302
+ tok.prev = prev;
303
+ tokens.push(tok);
304
+ prev = tok;
305
+ };
306
+ const extglobOpen = (type, value)=>{
307
+ const token = {
308
+ ...EXTGLOB_CHARS[value],
309
+ conditions: 1,
310
+ inner: ''
311
+ };
312
+ token.prev = prev;
313
+ token.parens = state.parens;
314
+ token.output = state.output;
315
+ const output = (opts.capture ? '(' : '') + token.open;
316
+ increment('parens');
317
+ push({
318
+ type,
319
+ value,
320
+ output: state.output ? '' : ONE_CHAR
321
+ });
322
+ push({
323
+ type: 'paren',
324
+ extglob: true,
325
+ value: advance(),
326
+ output
327
+ });
328
+ extglobs.push(token);
329
+ };
330
+ const extglobClose = (token)=>{
331
+ let output = token.close + (opts.capture ? ')' : '');
332
+ let rest;
333
+ if ('negate' === token.type) {
334
+ let extglobStar = star;
335
+ if (token.inner && token.inner.length > 1 && token.inner.includes('/')) extglobStar = globstar(opts);
336
+ if (extglobStar !== star || eos() || /^\)+$/.test(remaining())) output = token.close = `)$))${extglobStar}`;
337
+ if (token.inner.includes('*') && (rest = remaining()) && /^\.[^\\/.]+$/.test(rest)) {
338
+ const expression = parse(rest, {
339
+ ...options,
340
+ fastpaths: false
341
+ }).output;
342
+ output = token.close = `)${expression})${extglobStar})`;
343
+ }
344
+ if ('bos' === token.prev.type) state.negatedExtglob = true;
345
+ }
346
+ push({
347
+ type: 'paren',
348
+ extglob: true,
349
+ value,
350
+ output
351
+ });
352
+ decrement('parens');
353
+ };
354
+ if (false !== opts.fastpaths && !/(^[*!]|[/()[\]{}"])/.test(input)) {
355
+ let backslashes = false;
356
+ let output = input.replace(REGEX_SPECIAL_CHARS_BACKREF, (m, esc, chars, first, rest, index)=>{
357
+ if ('\\' === first) {
358
+ backslashes = true;
359
+ return m;
360
+ }
361
+ if ('?' === first) {
362
+ if (esc) return esc + first + (rest ? QMARK.repeat(rest.length) : '');
363
+ if (0 === index) return qmarkNoDot + (rest ? QMARK.repeat(rest.length) : '');
364
+ return QMARK.repeat(chars.length);
365
+ }
366
+ if ('.' === first) return DOT_LITERAL.repeat(chars.length);
367
+ if ('*' === first) {
368
+ if (esc) return esc + first + (rest ? star : '');
369
+ return star;
370
+ }
371
+ return esc ? m : `\\${m}`;
372
+ });
373
+ if (true === backslashes) output = true === opts.unescape ? output.replace(/\\/g, '') : output.replace(/\\+/g, (m)=>m.length % 2 === 0 ? '\\\\' : m ? '\\' : '');
374
+ if (output === input && true === opts.contains) {
375
+ state.output = input;
376
+ return state;
377
+ }
378
+ state.output = utils.wrapOutput(output, state, options);
379
+ return state;
380
+ }
381
+ while(!eos()){
382
+ value = advance();
383
+ if ('\u0000' === value) continue;
384
+ if ('\\' === value) {
385
+ const next = peek();
386
+ if ('/' === next && true !== opts.bash) continue;
387
+ if ('.' === next || ';' === next) continue;
388
+ if (!next) {
389
+ value += '\\';
390
+ push({
391
+ type: 'text',
392
+ value
393
+ });
394
+ continue;
395
+ }
396
+ const match = /^\\+/.exec(remaining());
397
+ let slashes = 0;
398
+ if (match && match[0].length > 2) {
399
+ slashes = match[0].length;
400
+ state.index += slashes;
401
+ if (slashes % 2 !== 0) value += '\\';
402
+ }
403
+ if (true === opts.unescape) value = advance();
404
+ else value += advance();
405
+ if (0 === state.brackets) {
406
+ push({
407
+ type: 'text',
408
+ value
409
+ });
410
+ continue;
411
+ }
412
+ }
413
+ if (state.brackets > 0 && (']' !== value || '[' === prev.value || '[^' === prev.value)) {
414
+ if (false !== opts.posix && ':' === value) {
415
+ const inner = prev.value.slice(1);
416
+ if (inner.includes('[')) {
417
+ prev.posix = true;
418
+ if (inner.includes(':')) {
419
+ const idx = prev.value.lastIndexOf('[');
420
+ const pre = prev.value.slice(0, idx);
421
+ const rest = prev.value.slice(idx + 2);
422
+ const posix = POSIX_REGEX_SOURCE[rest];
423
+ if (posix) {
424
+ prev.value = pre + posix;
425
+ state.backtrack = true;
426
+ advance();
427
+ if (!bos.output && 1 === tokens.indexOf(prev)) bos.output = ONE_CHAR;
428
+ continue;
429
+ }
430
+ }
431
+ }
432
+ }
433
+ if ('[' === value && ':' !== peek() || '-' === value && ']' === peek()) value = `\\${value}`;
434
+ if (']' === value && ('[' === prev.value || '[^' === prev.value)) value = `\\${value}`;
435
+ if (true === opts.posix && '!' === value && '[' === prev.value) value = '^';
436
+ prev.value += value;
437
+ append({
438
+ value
439
+ });
440
+ continue;
441
+ }
442
+ if (1 === state.quotes && '"' !== value) {
443
+ value = utils.escapeRegex(value);
444
+ prev.value += value;
445
+ append({
446
+ value
447
+ });
448
+ continue;
449
+ }
450
+ if ('"' === value) {
451
+ state.quotes = 1 === state.quotes ? 0 : 1;
452
+ if (true === opts.keepQuotes) push({
453
+ type: 'text',
454
+ value
455
+ });
456
+ continue;
457
+ }
458
+ if ('(' === value) {
459
+ increment('parens');
460
+ push({
461
+ type: 'paren',
462
+ value
463
+ });
464
+ continue;
465
+ }
466
+ if (')' === value) {
467
+ if (0 === state.parens && true === opts.strictBrackets) throw new SyntaxError(syntaxError('opening', '('));
468
+ const extglob = extglobs[extglobs.length - 1];
469
+ if (extglob && state.parens === extglob.parens + 1) {
470
+ extglobClose(extglobs.pop());
471
+ continue;
472
+ }
473
+ push({
474
+ type: 'paren',
475
+ value,
476
+ output: state.parens ? ')' : '\\)'
477
+ });
478
+ decrement('parens');
479
+ continue;
480
+ }
481
+ if ('[' === value) {
482
+ if (true !== opts.nobracket && remaining().includes(']')) increment('brackets');
483
+ else {
484
+ if (true !== opts.nobracket && true === opts.strictBrackets) throw new SyntaxError(syntaxError('closing', ']'));
485
+ value = `\\${value}`;
486
+ }
487
+ push({
488
+ type: 'bracket',
489
+ value
490
+ });
491
+ continue;
492
+ }
493
+ if (']' === value) {
494
+ if (true === opts.nobracket || prev && 'bracket' === prev.type && 1 === prev.value.length) {
495
+ push({
496
+ type: 'text',
497
+ value,
498
+ output: `\\${value}`
499
+ });
500
+ continue;
501
+ }
502
+ if (0 === state.brackets) {
503
+ if (true === opts.strictBrackets) throw new SyntaxError(syntaxError('opening', '['));
504
+ push({
505
+ type: 'text',
506
+ value,
507
+ output: `\\${value}`
508
+ });
509
+ continue;
510
+ }
511
+ decrement('brackets');
512
+ const prevValue = prev.value.slice(1);
513
+ if (true !== prev.posix && '^' === prevValue[0] && !prevValue.includes('/')) value = `/${value}`;
514
+ prev.value += value;
515
+ append({
516
+ value
517
+ });
518
+ if (false === opts.literalBrackets || utils.hasRegexChars(prevValue)) continue;
519
+ const escaped = utils.escapeRegex(prev.value);
520
+ state.output = state.output.slice(0, -prev.value.length);
521
+ if (true === opts.literalBrackets) {
522
+ state.output += escaped;
523
+ prev.value = escaped;
524
+ continue;
525
+ }
526
+ prev.value = `(${capture}${escaped}|${prev.value})`;
527
+ state.output += prev.value;
528
+ continue;
529
+ }
530
+ if ('{' === value && true !== opts.nobrace) {
531
+ increment('braces');
532
+ const open = {
533
+ type: 'brace',
534
+ value,
535
+ output: '(',
536
+ outputIndex: state.output.length,
537
+ tokensIndex: state.tokens.length
538
+ };
539
+ braces.push(open);
540
+ push(open);
541
+ continue;
542
+ }
543
+ if ('}' === value) {
544
+ const brace = braces[braces.length - 1];
545
+ if (true === opts.nobrace || !brace) {
546
+ push({
547
+ type: 'text',
548
+ value,
549
+ output: value
550
+ });
551
+ continue;
552
+ }
553
+ let output = ')';
554
+ if (true === brace.dots) {
555
+ const arr = tokens.slice();
556
+ const range = [];
557
+ for(let i = arr.length - 1; i >= 0; i--){
558
+ tokens.pop();
559
+ if ('brace' === arr[i].type) break;
560
+ if ('dots' !== arr[i].type) range.unshift(arr[i].value);
561
+ }
562
+ output = expandRange(range, opts);
563
+ state.backtrack = true;
564
+ }
565
+ if (true !== brace.comma && true !== brace.dots) {
566
+ const out = state.output.slice(0, brace.outputIndex);
567
+ const toks = state.tokens.slice(brace.tokensIndex);
568
+ brace.value = brace.output = '\\{';
569
+ value = output = '\\}';
570
+ state.output = out;
571
+ for (const t of toks)state.output += t.output || t.value;
572
+ }
573
+ push({
574
+ type: 'brace',
575
+ value,
576
+ output
577
+ });
578
+ decrement('braces');
579
+ braces.pop();
580
+ continue;
581
+ }
582
+ if ('|' === value) {
583
+ if (extglobs.length > 0) extglobs[extglobs.length - 1].conditions++;
584
+ push({
585
+ type: 'text',
586
+ value
587
+ });
588
+ continue;
589
+ }
590
+ if (',' === value) {
591
+ let output = value;
592
+ const brace = braces[braces.length - 1];
593
+ if (brace && 'braces' === stack[stack.length - 1]) {
594
+ brace.comma = true;
595
+ output = '|';
596
+ }
597
+ push({
598
+ type: 'comma',
599
+ value,
600
+ output
601
+ });
602
+ continue;
603
+ }
604
+ if ('/' === value) {
605
+ if ('dot' === prev.type && state.index === state.start + 1) {
606
+ state.start = state.index + 1;
607
+ state.consumed = '';
608
+ state.output = '';
609
+ tokens.pop();
610
+ prev = bos;
611
+ continue;
612
+ }
613
+ push({
614
+ type: 'slash',
615
+ value,
616
+ output: SLASH_LITERAL
617
+ });
618
+ continue;
619
+ }
620
+ if ('.' === value) {
621
+ if (state.braces > 0 && 'dot' === prev.type) {
622
+ if ('.' === prev.value) prev.output = DOT_LITERAL;
623
+ const brace = braces[braces.length - 1];
624
+ prev.type = 'dots';
625
+ prev.output += value;
626
+ prev.value += value;
627
+ brace.dots = true;
628
+ continue;
629
+ }
630
+ if (state.braces + state.parens === 0 && 'bos' !== prev.type && 'slash' !== prev.type) {
631
+ push({
632
+ type: 'text',
633
+ value,
634
+ output: DOT_LITERAL
635
+ });
636
+ continue;
637
+ }
638
+ push({
639
+ type: 'dot',
640
+ value,
641
+ output: DOT_LITERAL
642
+ });
643
+ continue;
644
+ }
645
+ if ('?' === value) {
646
+ const isGroup = prev && '(' === prev.value;
647
+ if (!isGroup && true !== opts.noextglob && '(' === peek() && '?' !== peek(2)) {
648
+ extglobOpen('qmark', value);
649
+ continue;
650
+ }
651
+ if (prev && 'paren' === prev.type) {
652
+ const next = peek();
653
+ let output = value;
654
+ if ('(' === prev.value && !/[!=<:]/.test(next) || '<' === next && !/<([!=]|\w+>)/.test(remaining())) output = `\\${value}`;
655
+ push({
656
+ type: 'text',
657
+ value,
658
+ output
659
+ });
660
+ continue;
661
+ }
662
+ if (true !== opts.dot && ('slash' === prev.type || 'bos' === prev.type)) {
663
+ push({
664
+ type: 'qmark',
665
+ value,
666
+ output: QMARK_NO_DOT
667
+ });
668
+ continue;
669
+ }
670
+ push({
671
+ type: 'qmark',
672
+ value,
673
+ output: QMARK
674
+ });
675
+ continue;
676
+ }
677
+ if ('!' === value) {
678
+ if (true !== opts.noextglob && '(' === peek()) {
679
+ if ('?' !== peek(2) || !/[!=<:]/.test(peek(3))) {
680
+ extglobOpen('negate', value);
681
+ continue;
682
+ }
683
+ }
684
+ if (true !== opts.nonegate && 0 === state.index) {
685
+ negate();
686
+ continue;
687
+ }
688
+ }
689
+ if ('+' === value) {
690
+ if (true !== opts.noextglob && '(' === peek() && '?' !== peek(2)) {
691
+ extglobOpen('plus', value);
692
+ continue;
693
+ }
694
+ if (prev && '(' === prev.value || false === opts.regex) {
695
+ push({
696
+ type: 'plus',
697
+ value,
698
+ output: PLUS_LITERAL
699
+ });
700
+ continue;
701
+ }
702
+ if (prev && ('bracket' === prev.type || 'paren' === prev.type || 'brace' === prev.type) || state.parens > 0) {
703
+ push({
704
+ type: 'plus',
705
+ value
706
+ });
707
+ continue;
708
+ }
709
+ push({
710
+ type: 'plus',
711
+ value: PLUS_LITERAL
712
+ });
713
+ continue;
714
+ }
715
+ if ('@' === value) {
716
+ if (true !== opts.noextglob && '(' === peek() && '?' !== peek(2)) {
717
+ push({
718
+ type: 'at',
719
+ extglob: true,
720
+ value,
721
+ output: ''
722
+ });
723
+ continue;
724
+ }
725
+ push({
726
+ type: 'text',
727
+ value
728
+ });
729
+ continue;
730
+ }
731
+ if ('*' !== value) {
732
+ if ('$' === value || '^' === value) value = `\\${value}`;
733
+ const match = REGEX_NON_SPECIAL_CHARS.exec(remaining());
734
+ if (match) {
735
+ value += match[0];
736
+ state.index += match[0].length;
737
+ }
738
+ push({
739
+ type: 'text',
740
+ value
741
+ });
742
+ continue;
743
+ }
744
+ if (prev && ('globstar' === prev.type || true === prev.star)) {
745
+ prev.type = 'star';
746
+ prev.star = true;
747
+ prev.value += value;
748
+ prev.output = star;
749
+ state.backtrack = true;
750
+ state.globstar = true;
751
+ consume(value);
752
+ continue;
753
+ }
754
+ let rest = remaining();
755
+ if (true !== opts.noextglob && /^\([^?]/.test(rest)) {
756
+ extglobOpen('star', value);
757
+ continue;
758
+ }
759
+ if ('star' === prev.type) {
760
+ if (true === opts.noglobstar) {
761
+ consume(value);
762
+ continue;
763
+ }
764
+ const prior = prev.prev;
765
+ const before = prior.prev;
766
+ const isStart = 'slash' === prior.type || 'bos' === prior.type;
767
+ const afterStar = before && ('star' === before.type || 'globstar' === before.type);
768
+ if (true === opts.bash && (!isStart || rest[0] && '/' !== rest[0])) {
769
+ push({
770
+ type: 'star',
771
+ value,
772
+ output: ''
773
+ });
774
+ continue;
775
+ }
776
+ const isBrace = state.braces > 0 && ('comma' === prior.type || 'brace' === prior.type);
777
+ const isExtglob = extglobs.length && ('pipe' === prior.type || 'paren' === prior.type);
778
+ if (!isStart && 'paren' !== prior.type && !isBrace && !isExtglob) {
779
+ push({
780
+ type: 'star',
781
+ value,
782
+ output: ''
783
+ });
784
+ continue;
785
+ }
786
+ while('/**' === rest.slice(0, 3)){
787
+ const after = input[state.index + 4];
788
+ if (after && '/' !== after) break;
789
+ rest = rest.slice(3);
790
+ consume('/**', 3);
791
+ }
792
+ if ('bos' === prior.type && eos()) {
793
+ prev.type = 'globstar';
794
+ prev.value += value;
795
+ prev.output = globstar(opts);
796
+ state.output = prev.output;
797
+ state.globstar = true;
798
+ consume(value);
799
+ continue;
800
+ }
801
+ if ('slash' === prior.type && 'bos' !== prior.prev.type && !afterStar && eos()) {
802
+ state.output = state.output.slice(0, -(prior.output + prev.output).length);
803
+ prior.output = `(?:${prior.output}`;
804
+ prev.type = 'globstar';
805
+ prev.output = globstar(opts) + (opts.strictSlashes ? ')' : '|$)');
806
+ prev.value += value;
807
+ state.globstar = true;
808
+ state.output += prior.output + prev.output;
809
+ consume(value);
810
+ continue;
811
+ }
812
+ if ('slash' === prior.type && 'bos' !== prior.prev.type && '/' === rest[0]) {
813
+ const end = void 0 !== rest[1] ? '|$' : '';
814
+ state.output = state.output.slice(0, -(prior.output + prev.output).length);
815
+ prior.output = `(?:${prior.output}`;
816
+ prev.type = 'globstar';
817
+ prev.output = `${globstar(opts)}${SLASH_LITERAL}|${SLASH_LITERAL}${end})`;
818
+ prev.value += value;
819
+ state.output += prior.output + prev.output;
820
+ state.globstar = true;
821
+ consume(value + advance());
822
+ push({
823
+ type: 'slash',
824
+ value: '/',
825
+ output: ''
826
+ });
827
+ continue;
828
+ }
829
+ if ('bos' === prior.type && '/' === rest[0]) {
830
+ prev.type = 'globstar';
831
+ prev.value += value;
832
+ prev.output = `(?:^|${SLASH_LITERAL}|${globstar(opts)}${SLASH_LITERAL})`;
833
+ state.output = prev.output;
834
+ state.globstar = true;
835
+ consume(value + advance());
836
+ push({
837
+ type: 'slash',
838
+ value: '/',
839
+ output: ''
840
+ });
841
+ continue;
842
+ }
843
+ state.output = state.output.slice(0, -prev.output.length);
844
+ prev.type = 'globstar';
845
+ prev.output = globstar(opts);
846
+ prev.value += value;
847
+ state.output += prev.output;
848
+ state.globstar = true;
849
+ consume(value);
850
+ continue;
851
+ }
852
+ const token = {
853
+ type: 'star',
854
+ value,
855
+ output: star
856
+ };
857
+ if (true === opts.bash) {
858
+ token.output = '.*?';
859
+ if ('bos' === prev.type || 'slash' === prev.type) token.output = nodot + token.output;
860
+ push(token);
861
+ continue;
862
+ }
863
+ if (prev && ('bracket' === prev.type || 'paren' === prev.type) && true === opts.regex) {
864
+ token.output = value;
865
+ push(token);
866
+ continue;
867
+ }
868
+ if (state.index === state.start || 'slash' === prev.type || 'dot' === prev.type) {
869
+ if ('dot' === prev.type) {
870
+ state.output += NO_DOT_SLASH;
871
+ prev.output += NO_DOT_SLASH;
872
+ } else if (true === opts.dot) {
873
+ state.output += NO_DOTS_SLASH;
874
+ prev.output += NO_DOTS_SLASH;
875
+ } else {
876
+ state.output += nodot;
877
+ prev.output += nodot;
878
+ }
879
+ if ('*' !== peek()) {
880
+ state.output += ONE_CHAR;
881
+ prev.output += ONE_CHAR;
882
+ }
883
+ }
884
+ push(token);
885
+ }
886
+ while(state.brackets > 0){
887
+ if (true === opts.strictBrackets) throw new SyntaxError(syntaxError('closing', ']'));
888
+ state.output = utils.escapeLast(state.output, '[');
889
+ decrement('brackets');
890
+ }
891
+ while(state.parens > 0){
892
+ if (true === opts.strictBrackets) throw new SyntaxError(syntaxError('closing', ')'));
893
+ state.output = utils.escapeLast(state.output, '(');
894
+ decrement('parens');
895
+ }
896
+ while(state.braces > 0){
897
+ if (true === opts.strictBrackets) throw new SyntaxError(syntaxError('closing', '}'));
898
+ state.output = utils.escapeLast(state.output, '{');
899
+ decrement('braces');
900
+ }
901
+ if (true !== opts.strictSlashes && ('star' === prev.type || 'bracket' === prev.type)) push({
902
+ type: 'maybe_slash',
903
+ value: '',
904
+ output: `${SLASH_LITERAL}?`
905
+ });
906
+ if (true === state.backtrack) {
907
+ state.output = '';
908
+ for (const token of state.tokens){
909
+ state.output += null != token.output ? token.output : token.value;
910
+ if (token.suffix) state.output += token.suffix;
911
+ }
912
+ }
913
+ return state;
914
+ };
915
+ parse.fastpaths = (input, options)=>{
916
+ const opts = {
917
+ ...options
918
+ };
919
+ const max = 'number' == typeof opts.maxLength ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH;
920
+ const len = input.length;
921
+ if (len > max) throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`);
922
+ input = REPLACEMENTS[input] || input;
923
+ const { DOT_LITERAL, SLASH_LITERAL, ONE_CHAR, DOTS_SLASH, NO_DOT, NO_DOTS, NO_DOTS_SLASH, STAR, START_ANCHOR } = constants.globChars(opts.windows);
924
+ const nodot = opts.dot ? NO_DOTS : NO_DOT;
925
+ const slashDot = opts.dot ? NO_DOTS_SLASH : NO_DOT;
926
+ const capture = opts.capture ? '' : '?:';
927
+ const state = {
928
+ negated: false,
929
+ prefix: ''
930
+ };
931
+ let star = true === opts.bash ? '.*?' : STAR;
932
+ if (opts.capture) star = `(${star})`;
933
+ const globstar = (opts)=>{
934
+ if (true === opts.noglobstar) return star;
935
+ return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`;
936
+ };
937
+ const create = (str)=>{
938
+ switch(str){
939
+ case '*':
940
+ return `${nodot}${ONE_CHAR}${star}`;
941
+ case '.*':
942
+ return `${DOT_LITERAL}${ONE_CHAR}${star}`;
943
+ case '*.*':
944
+ return `${nodot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`;
945
+ case '*/*':
946
+ return `${nodot}${star}${SLASH_LITERAL}${ONE_CHAR}${slashDot}${star}`;
947
+ case '**':
948
+ return nodot + globstar(opts);
949
+ case '**/*':
950
+ return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${ONE_CHAR}${star}`;
951
+ case '**/*.*':
952
+ return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`;
953
+ case '**/.*':
954
+ return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${DOT_LITERAL}${ONE_CHAR}${star}`;
955
+ default:
956
+ {
957
+ const match = /^(.*?)\.(\w+)$/.exec(str);
958
+ if (!match) return;
959
+ const source = create(match[1]);
960
+ if (!source) return;
961
+ return source + DOT_LITERAL + match[2];
962
+ }
963
+ }
964
+ };
965
+ const output = utils.removePrefix(input, state);
966
+ let source = create(output);
967
+ if (source && true !== opts.strictSlashes) source += `${SLASH_LITERAL}?`;
968
+ return source;
969
+ };
970
+ module.exports = parse;
971
+ },
972
+ "../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/picomatch.js" (module, __unused_rspack_exports, __webpack_require__) {
973
+ const scan = __webpack_require__("../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/scan.js");
974
+ const parse = __webpack_require__("../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/parse.js");
975
+ const utils = __webpack_require__("../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/utils.js");
976
+ const constants = __webpack_require__("../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/constants.js");
977
+ const isObject = (val)=>val && 'object' == typeof val && !Array.isArray(val);
978
+ const picomatch = (glob, options, returnState = false)=>{
979
+ if (Array.isArray(glob)) {
980
+ const fns = glob.map((input)=>picomatch(input, options, returnState));
981
+ const arrayMatcher = (str)=>{
982
+ for (const isMatch of fns){
983
+ const state = isMatch(str);
984
+ if (state) return state;
985
+ }
986
+ return false;
987
+ };
988
+ return arrayMatcher;
989
+ }
990
+ const isState = isObject(glob) && glob.tokens && glob.input;
991
+ if ('' === glob || 'string' != typeof glob && !isState) throw new TypeError('Expected pattern to be a non-empty string');
992
+ const opts = options || {};
993
+ const posix = opts.windows;
994
+ const regex = isState ? picomatch.compileRe(glob, options) : picomatch.makeRe(glob, options, false, true);
995
+ const state = regex.state;
996
+ delete regex.state;
997
+ let isIgnored = ()=>false;
998
+ if (opts.ignore) {
999
+ const ignoreOpts = {
1000
+ ...options,
1001
+ ignore: null,
1002
+ onMatch: null,
1003
+ onResult: null
1004
+ };
1005
+ isIgnored = picomatch(opts.ignore, ignoreOpts, returnState);
1006
+ }
1007
+ const matcher = (input, returnObject = false)=>{
1008
+ const { isMatch, match, output } = picomatch.test(input, regex, options, {
1009
+ glob,
1010
+ posix
1011
+ });
1012
+ const result = {
1013
+ glob,
1014
+ state,
1015
+ regex,
1016
+ posix,
1017
+ input,
1018
+ output,
1019
+ match,
1020
+ isMatch
1021
+ };
1022
+ if ('function' == typeof opts.onResult) opts.onResult(result);
1023
+ if (false === isMatch) {
1024
+ result.isMatch = false;
1025
+ return returnObject ? result : false;
1026
+ }
1027
+ if (isIgnored(input)) {
1028
+ if ('function' == typeof opts.onIgnore) opts.onIgnore(result);
1029
+ result.isMatch = false;
1030
+ return returnObject ? result : false;
1031
+ }
1032
+ if ('function' == typeof opts.onMatch) opts.onMatch(result);
1033
+ return returnObject ? result : true;
1034
+ };
1035
+ if (returnState) matcher.state = state;
1036
+ return matcher;
1037
+ };
1038
+ picomatch.test = (input, regex, options, { glob, posix } = {})=>{
1039
+ if ('string' != typeof input) throw new TypeError('Expected input to be a string');
1040
+ if ('' === input) return {
1041
+ isMatch: false,
1042
+ output: ''
1043
+ };
1044
+ const opts = options || {};
1045
+ const format = opts.format || (posix ? utils.toPosixSlashes : null);
1046
+ let match = input === glob;
1047
+ let output = match && format ? format(input) : input;
1048
+ if (false === match) {
1049
+ output = format ? format(input) : input;
1050
+ match = output === glob;
1051
+ }
1052
+ if (false === match || true === opts.capture) match = true === opts.matchBase || true === opts.basename ? picomatch.matchBase(input, regex, options, posix) : regex.exec(output);
1053
+ return {
1054
+ isMatch: Boolean(match),
1055
+ match,
1056
+ output
1057
+ };
1058
+ };
1059
+ picomatch.matchBase = (input, glob, options)=>{
1060
+ const regex = glob instanceof RegExp ? glob : picomatch.makeRe(glob, options);
1061
+ return regex.test(utils.basename(input));
1062
+ };
1063
+ picomatch.isMatch = (str, patterns, options)=>picomatch(patterns, options)(str);
1064
+ picomatch.parse = (pattern, options)=>{
1065
+ if (Array.isArray(pattern)) return pattern.map((p)=>picomatch.parse(p, options));
1066
+ return parse(pattern, {
1067
+ ...options,
1068
+ fastpaths: false
1069
+ });
1070
+ };
1071
+ picomatch.scan = (input, options)=>scan(input, options);
1072
+ picomatch.compileRe = (state, options, returnOutput = false, returnState = false)=>{
1073
+ if (true === returnOutput) return state.output;
1074
+ const opts = options || {};
1075
+ const prepend = opts.contains ? '' : '^';
1076
+ const append = opts.contains ? '' : '$';
1077
+ let source = `${prepend}(?:${state.output})${append}`;
1078
+ if (state && true === state.negated) source = `^(?!${source}).*$`;
1079
+ const regex = picomatch.toRegex(source, options);
1080
+ if (true === returnState) regex.state = state;
1081
+ return regex;
1082
+ };
1083
+ picomatch.makeRe = (input, options = {}, returnOutput = false, returnState = false)=>{
1084
+ if (!input || 'string' != typeof input) throw new TypeError('Expected a non-empty string');
1085
+ let parsed = {
1086
+ negated: false,
1087
+ fastpaths: true
1088
+ };
1089
+ if (false !== options.fastpaths && ('.' === input[0] || '*' === input[0])) parsed.output = parse.fastpaths(input, options);
1090
+ if (!parsed.output) parsed = parse(input, options);
1091
+ return picomatch.compileRe(parsed, options, returnOutput, returnState);
1092
+ };
1093
+ picomatch.toRegex = (source, options)=>{
1094
+ try {
1095
+ const opts = options || {};
1096
+ return new RegExp(source, opts.flags || (opts.nocase ? 'i' : ''));
1097
+ } catch (err) {
1098
+ if (options && true === options.debug) throw err;
1099
+ return /$^/;
1100
+ }
1101
+ };
1102
+ picomatch.constants = constants;
1103
+ module.exports = picomatch;
1104
+ },
1105
+ "../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/scan.js" (module, __unused_rspack_exports, __webpack_require__) {
1106
+ const utils = __webpack_require__("../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/utils.js");
1107
+ const { CHAR_ASTERISK, CHAR_AT, CHAR_BACKWARD_SLASH, CHAR_COMMA, CHAR_DOT, CHAR_EXCLAMATION_MARK, CHAR_FORWARD_SLASH, CHAR_LEFT_CURLY_BRACE, CHAR_LEFT_PARENTHESES, CHAR_LEFT_SQUARE_BRACKET, CHAR_PLUS, CHAR_QUESTION_MARK, CHAR_RIGHT_CURLY_BRACE, CHAR_RIGHT_PARENTHESES, CHAR_RIGHT_SQUARE_BRACKET } = __webpack_require__("../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/constants.js");
1108
+ const isPathSeparator = (code)=>code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH;
1109
+ const depth = (token)=>{
1110
+ if (true !== token.isPrefix) token.depth = token.isGlobstar ? 1 / 0 : 1;
1111
+ };
1112
+ const scan = (input, options)=>{
1113
+ const opts = options || {};
1114
+ const length = input.length - 1;
1115
+ const scanToEnd = true === opts.parts || true === opts.scanToEnd;
1116
+ const slashes = [];
1117
+ const tokens = [];
1118
+ const parts = [];
1119
+ let str = input;
1120
+ let index = -1;
1121
+ let start = 0;
1122
+ let lastIndex = 0;
1123
+ let isBrace = false;
1124
+ let isBracket = false;
1125
+ let isGlob = false;
1126
+ let isExtglob = false;
1127
+ let isGlobstar = false;
1128
+ let braceEscaped = false;
1129
+ let backslashes = false;
1130
+ let negated = false;
1131
+ let negatedExtglob = false;
1132
+ let finished = false;
1133
+ let braces = 0;
1134
+ let prev;
1135
+ let code;
1136
+ let token = {
1137
+ value: '',
1138
+ depth: 0,
1139
+ isGlob: false
1140
+ };
1141
+ const eos = ()=>index >= length;
1142
+ const peek = ()=>str.charCodeAt(index + 1);
1143
+ const advance = ()=>{
1144
+ prev = code;
1145
+ return str.charCodeAt(++index);
1146
+ };
1147
+ while(index < length){
1148
+ code = advance();
1149
+ let next;
1150
+ if (code === CHAR_BACKWARD_SLASH) {
1151
+ backslashes = token.backslashes = true;
1152
+ code = advance();
1153
+ if (code === CHAR_LEFT_CURLY_BRACE) braceEscaped = true;
1154
+ continue;
1155
+ }
1156
+ if (true === braceEscaped || code === CHAR_LEFT_CURLY_BRACE) {
1157
+ braces++;
1158
+ while(true !== eos() && (code = advance())){
1159
+ if (code === CHAR_BACKWARD_SLASH) {
1160
+ backslashes = token.backslashes = true;
1161
+ advance();
1162
+ continue;
1163
+ }
1164
+ if (code === CHAR_LEFT_CURLY_BRACE) {
1165
+ braces++;
1166
+ continue;
1167
+ }
1168
+ if (true !== braceEscaped && code === CHAR_DOT && (code = advance()) === CHAR_DOT) {
1169
+ isBrace = token.isBrace = true;
1170
+ isGlob = token.isGlob = true;
1171
+ finished = true;
1172
+ if (true === scanToEnd) continue;
1173
+ break;
1174
+ }
1175
+ if (true !== braceEscaped && code === CHAR_COMMA) {
1176
+ isBrace = token.isBrace = true;
1177
+ isGlob = token.isGlob = true;
1178
+ finished = true;
1179
+ if (true === scanToEnd) continue;
1180
+ break;
1181
+ }
1182
+ if (code === CHAR_RIGHT_CURLY_BRACE) {
1183
+ braces--;
1184
+ if (0 === braces) {
1185
+ braceEscaped = false;
1186
+ isBrace = token.isBrace = true;
1187
+ finished = true;
1188
+ break;
1189
+ }
1190
+ }
1191
+ }
1192
+ if (true === scanToEnd) continue;
1193
+ break;
1194
+ }
1195
+ if (code === CHAR_FORWARD_SLASH) {
1196
+ slashes.push(index);
1197
+ tokens.push(token);
1198
+ token = {
1199
+ value: '',
1200
+ depth: 0,
1201
+ isGlob: false
1202
+ };
1203
+ if (true === finished) continue;
1204
+ if (prev === CHAR_DOT && index === start + 1) {
1205
+ start += 2;
1206
+ continue;
1207
+ }
1208
+ lastIndex = index + 1;
1209
+ continue;
1210
+ }
1211
+ if (true !== opts.noext) {
1212
+ const isExtglobChar = code === CHAR_PLUS || code === CHAR_AT || code === CHAR_ASTERISK || code === CHAR_QUESTION_MARK || code === CHAR_EXCLAMATION_MARK;
1213
+ if (true === isExtglobChar && peek() === CHAR_LEFT_PARENTHESES) {
1214
+ isGlob = token.isGlob = true;
1215
+ isExtglob = token.isExtglob = true;
1216
+ finished = true;
1217
+ if (code === CHAR_EXCLAMATION_MARK && index === start) negatedExtglob = true;
1218
+ if (true === scanToEnd) {
1219
+ while(true !== eos() && (code = advance())){
1220
+ if (code === CHAR_BACKWARD_SLASH) {
1221
+ backslashes = token.backslashes = true;
1222
+ code = advance();
1223
+ continue;
1224
+ }
1225
+ if (code === CHAR_RIGHT_PARENTHESES) {
1226
+ isGlob = token.isGlob = true;
1227
+ finished = true;
1228
+ break;
1229
+ }
1230
+ }
1231
+ continue;
1232
+ }
1233
+ break;
1234
+ }
1235
+ }
1236
+ if (code === CHAR_ASTERISK) {
1237
+ if (prev === CHAR_ASTERISK) isGlobstar = token.isGlobstar = true;
1238
+ isGlob = token.isGlob = true;
1239
+ finished = true;
1240
+ if (true === scanToEnd) continue;
1241
+ break;
1242
+ }
1243
+ if (code === CHAR_QUESTION_MARK) {
1244
+ isGlob = token.isGlob = true;
1245
+ finished = true;
1246
+ if (true === scanToEnd) continue;
1247
+ break;
1248
+ }
1249
+ if (code === CHAR_LEFT_SQUARE_BRACKET) {
1250
+ while(true !== eos() && (next = advance())){
1251
+ if (next === CHAR_BACKWARD_SLASH) {
1252
+ backslashes = token.backslashes = true;
1253
+ advance();
1254
+ continue;
1255
+ }
1256
+ if (next === CHAR_RIGHT_SQUARE_BRACKET) {
1257
+ isBracket = token.isBracket = true;
1258
+ isGlob = token.isGlob = true;
1259
+ finished = true;
1260
+ break;
1261
+ }
1262
+ }
1263
+ if (true === scanToEnd) continue;
1264
+ break;
1265
+ }
1266
+ if (true !== opts.nonegate && code === CHAR_EXCLAMATION_MARK && index === start) {
1267
+ negated = token.negated = true;
1268
+ start++;
1269
+ continue;
1270
+ }
1271
+ if (true !== opts.noparen && code === CHAR_LEFT_PARENTHESES) {
1272
+ isGlob = token.isGlob = true;
1273
+ if (true === scanToEnd) {
1274
+ while(true !== eos() && (code = advance())){
1275
+ if (code === CHAR_LEFT_PARENTHESES) {
1276
+ backslashes = token.backslashes = true;
1277
+ code = advance();
1278
+ continue;
1279
+ }
1280
+ if (code === CHAR_RIGHT_PARENTHESES) {
1281
+ finished = true;
1282
+ break;
1283
+ }
1284
+ }
1285
+ continue;
1286
+ }
1287
+ break;
1288
+ }
1289
+ if (true === isGlob) {
1290
+ finished = true;
1291
+ if (true === scanToEnd) continue;
1292
+ break;
1293
+ }
1294
+ }
1295
+ if (true === opts.noext) {
1296
+ isExtglob = false;
1297
+ isGlob = false;
1298
+ }
1299
+ let base = str;
1300
+ let prefix = '';
1301
+ let glob = '';
1302
+ if (start > 0) {
1303
+ prefix = str.slice(0, start);
1304
+ str = str.slice(start);
1305
+ lastIndex -= start;
1306
+ }
1307
+ if (base && true === isGlob && lastIndex > 0) {
1308
+ base = str.slice(0, lastIndex);
1309
+ glob = str.slice(lastIndex);
1310
+ } else if (true === isGlob) {
1311
+ base = '';
1312
+ glob = str;
1313
+ } else base = str;
1314
+ if (base && '' !== base && '/' !== base && base !== str) {
1315
+ if (isPathSeparator(base.charCodeAt(base.length - 1))) base = base.slice(0, -1);
1316
+ }
1317
+ if (true === opts.unescape) {
1318
+ if (glob) glob = utils.removeBackslashes(glob);
1319
+ if (base && true === backslashes) base = utils.removeBackslashes(base);
1320
+ }
1321
+ const state = {
1322
+ prefix,
1323
+ input,
1324
+ start,
1325
+ base,
1326
+ glob,
1327
+ isBrace,
1328
+ isBracket,
1329
+ isGlob,
1330
+ isExtglob,
1331
+ isGlobstar,
1332
+ negated,
1333
+ negatedExtglob
1334
+ };
1335
+ if (true === opts.tokens) {
1336
+ state.maxDepth = 0;
1337
+ if (!isPathSeparator(code)) tokens.push(token);
1338
+ state.tokens = tokens;
1339
+ }
1340
+ if (true === opts.parts || true === opts.tokens) {
1341
+ let prevIndex;
1342
+ for(let idx = 0; idx < slashes.length; idx++){
1343
+ const n = prevIndex ? prevIndex + 1 : start;
1344
+ const i = slashes[idx];
1345
+ const value = input.slice(n, i);
1346
+ if (opts.tokens) {
1347
+ if (0 === idx && 0 !== start) {
1348
+ tokens[idx].isPrefix = true;
1349
+ tokens[idx].value = prefix;
1350
+ } else tokens[idx].value = value;
1351
+ depth(tokens[idx]);
1352
+ state.maxDepth += tokens[idx].depth;
1353
+ }
1354
+ if (0 !== idx || '' !== value) parts.push(value);
1355
+ prevIndex = i;
1356
+ }
1357
+ if (prevIndex && prevIndex + 1 < input.length) {
1358
+ const value = input.slice(prevIndex + 1);
1359
+ parts.push(value);
1360
+ if (opts.tokens) {
1361
+ tokens[tokens.length - 1].value = value;
1362
+ depth(tokens[tokens.length - 1]);
1363
+ state.maxDepth += tokens[tokens.length - 1].depth;
1364
+ }
1365
+ }
1366
+ state.slashes = slashes;
1367
+ state.parts = parts;
1368
+ }
1369
+ return state;
1370
+ };
1371
+ module.exports = scan;
1372
+ },
1373
+ "../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/utils.js" (__unused_rspack_module, exports, __webpack_require__) {
1374
+ const { REGEX_BACKSLASH, REGEX_REMOVE_BACKSLASH, REGEX_SPECIAL_CHARS, REGEX_SPECIAL_CHARS_GLOBAL } = __webpack_require__("../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/lib/constants.js");
1375
+ exports.isObject = (val)=>null !== val && 'object' == typeof val && !Array.isArray(val);
1376
+ exports.hasRegexChars = (str)=>REGEX_SPECIAL_CHARS.test(str);
1377
+ exports.isRegexChar = (str)=>1 === str.length && exports.hasRegexChars(str);
1378
+ exports.escapeRegex = (str)=>str.replace(REGEX_SPECIAL_CHARS_GLOBAL, '\\$1');
1379
+ exports.toPosixSlashes = (str)=>str.replace(REGEX_BACKSLASH, '/');
1380
+ exports.isWindows = ()=>{
1381
+ if ("u" > typeof navigator && navigator.platform) {
1382
+ const platform = navigator.platform.toLowerCase();
1383
+ return 'win32' === platform || 'windows' === platform;
1384
+ }
1385
+ if ("u" > typeof process && process.platform) return 'win32' === process.platform;
1386
+ return false;
1387
+ };
1388
+ exports.removeBackslashes = (str)=>str.replace(REGEX_REMOVE_BACKSLASH, (match)=>'\\' === match ? '' : match);
1389
+ exports.escapeLast = (input, char, lastIdx)=>{
1390
+ const idx = input.lastIndexOf(char, lastIdx);
1391
+ if (-1 === idx) return input;
1392
+ if ('\\' === input[idx - 1]) return exports.escapeLast(input, char, idx - 1);
1393
+ return `${input.slice(0, idx)}\\${input.slice(idx)}`;
1394
+ };
1395
+ exports.removePrefix = (input, state = {})=>{
1396
+ let output = input;
1397
+ if (output.startsWith('./')) {
1398
+ output = output.slice(2);
1399
+ state.prefix = './';
1400
+ }
1401
+ return output;
1402
+ };
1403
+ exports.wrapOutput = (input, state = {}, options = {})=>{
1404
+ const prepend = options.contains ? '' : '^';
1405
+ const append = options.contains ? '' : '$';
1406
+ let output = `${prepend}(?:${input})${append}`;
1407
+ if (true === state.negated) output = `(?:^(?!${output}).*$)`;
1408
+ return output;
1409
+ };
1410
+ exports.basename = (path, { windows } = {})=>{
1411
+ const segs = path.split(windows ? /[\\/]/ : '/');
1412
+ const last = segs[segs.length - 1];
1413
+ if ('' === last) return segs[segs.length - 2];
1414
+ return last;
1415
+ };
1416
+ }
1417
+ });
1418
+ const TYPE_REQUEST = "q";
1419
+ const TYPE_RESPONSE = "s";
1420
+ const DEFAULT_TIMEOUT = 6e4;
1421
+ function defaultSerialize(i) {
1422
+ return i;
1423
+ }
1424
+ const defaultDeserialize = defaultSerialize;
1425
+ const { clearTimeout: dist_clearTimeout, setTimeout: dist_setTimeout } = globalThis;
1426
+ const random = Math.random.bind(Math);
1427
+ function createBirpc($functions, options) {
1428
+ const { post, on, off = ()=>{}, eventNames = [], serialize = defaultSerialize, deserialize = defaultDeserialize, resolver, bind = "rpc", timeout = DEFAULT_TIMEOUT } = options;
1429
+ let $closed = false;
1430
+ const _rpcPromiseMap = /* @__PURE__ */ new Map();
1431
+ let _promiseInit;
1432
+ let rpc;
1433
+ async function _call(method, args, event, optional) {
1434
+ if ($closed) throw new Error(`[birpc] rpc is closed, cannot call "${method}"`);
1435
+ const req = {
1436
+ m: method,
1437
+ a: args,
1438
+ t: TYPE_REQUEST
1439
+ };
1440
+ if (optional) req.o = true;
1441
+ const send = async (_req)=>post(serialize(_req));
1442
+ if (event) return void await send(req);
1443
+ if (_promiseInit) try {
1444
+ await _promiseInit;
1445
+ } finally{
1446
+ _promiseInit = void 0;
1447
+ }
1448
+ let { promise, resolve, reject } = createPromiseWithResolvers();
1449
+ const id = nanoid();
1450
+ req.i = id;
1451
+ let timeoutId;
1452
+ async function handler(newReq = req) {
1453
+ if (timeout >= 0) {
1454
+ timeoutId = dist_setTimeout(()=>{
1455
+ try {
1456
+ const handleResult = options.onTimeoutError?.call(rpc, method, args);
1457
+ if (true !== handleResult) throw new Error(`[birpc] timeout on calling "${method}"`);
1458
+ } catch (e) {
1459
+ reject(e);
1460
+ }
1461
+ _rpcPromiseMap.delete(id);
1462
+ }, timeout);
1463
+ if ("object" == typeof timeoutId) timeoutId = timeoutId.unref?.();
1464
+ }
1465
+ _rpcPromiseMap.set(id, {
1466
+ resolve,
1467
+ reject,
1468
+ timeoutId,
1469
+ method
1470
+ });
1471
+ await send(newReq);
1472
+ return promise;
1473
+ }
1474
+ try {
1475
+ if (options.onRequest) await options.onRequest.call(rpc, req, handler, resolve);
1476
+ else await handler();
1477
+ } catch (e) {
1478
+ if (options.onGeneralError?.call(rpc, e) !== true) throw e;
1479
+ return;
1480
+ } finally{
1481
+ dist_clearTimeout(timeoutId);
1482
+ _rpcPromiseMap.delete(id);
1483
+ }
1484
+ return promise;
1485
+ }
1486
+ const $call = (method, ...args)=>_call(method, args, false);
1487
+ const $callOptional = (method, ...args)=>_call(method, args, false, true);
1488
+ const $callEvent = (method, ...args)=>_call(method, args, true);
1489
+ const $callRaw = (options2)=>_call(options2.method, options2.args, options2.event, options2.optional);
1490
+ const builtinMethods = {
1491
+ $call,
1492
+ $callOptional,
1493
+ $callEvent,
1494
+ $callRaw,
1495
+ $rejectPendingCalls,
1496
+ get $closed () {
1497
+ return $closed;
1498
+ },
1499
+ get $meta () {
1500
+ return options.meta;
1501
+ },
1502
+ $close,
1503
+ $functions
1504
+ };
1505
+ rpc = new Proxy({}, {
1506
+ get (_, method) {
1507
+ if (Object.prototype.hasOwnProperty.call(builtinMethods, method)) return builtinMethods[method];
1508
+ if ("then" === method && !eventNames.includes("then") && !("then" in $functions)) return;
1509
+ const sendEvent = (...args)=>_call(method, args, true);
1510
+ if (eventNames.includes(method)) {
1511
+ sendEvent.asEvent = sendEvent;
1512
+ return sendEvent;
1513
+ }
1514
+ const sendCall = (...args)=>_call(method, args, false);
1515
+ sendCall.asEvent = sendEvent;
1516
+ return sendCall;
1517
+ }
1518
+ });
1519
+ function $close(customError) {
1520
+ $closed = true;
1521
+ _rpcPromiseMap.forEach(({ reject, method })=>{
1522
+ const error = new Error(`[birpc] rpc is closed, cannot call "${method}"`);
1523
+ if (customError) {
1524
+ customError.cause ??= error;
1525
+ return reject(customError);
1526
+ }
1527
+ reject(error);
1528
+ });
1529
+ _rpcPromiseMap.clear();
1530
+ off(onMessage);
1531
+ }
1532
+ function $rejectPendingCalls(handler) {
1533
+ const entries = Array.from(_rpcPromiseMap.values());
1534
+ const handlerResults = entries.map(({ method, reject })=>{
1535
+ if (!handler) return reject(new Error(`[birpc]: rejected pending call "${method}".`));
1536
+ return handler({
1537
+ method,
1538
+ reject
1539
+ });
1540
+ });
1541
+ _rpcPromiseMap.clear();
1542
+ return handlerResults;
1543
+ }
1544
+ async function onMessage(data, ...extra) {
1545
+ let msg;
1546
+ try {
1547
+ msg = deserialize(data);
1548
+ } catch (e) {
1549
+ if (options.onGeneralError?.call(rpc, e) !== true) throw e;
1550
+ return;
1551
+ }
1552
+ if (msg.t === TYPE_REQUEST) {
1553
+ const { m: method, a: args, o: optional } = msg;
1554
+ let result, error;
1555
+ let fn = await (resolver ? resolver.call(rpc, method, $functions[method]) : $functions[method]);
1556
+ if (optional) fn ||= ()=>void 0;
1557
+ if (fn) try {
1558
+ result = await fn.apply("rpc" === bind ? rpc : $functions, args);
1559
+ } catch (e) {
1560
+ error = e;
1561
+ }
1562
+ else error = new Error(`[birpc] function "${method}" not found`);
1563
+ if (msg.i) {
1564
+ if (error && options.onError) options.onError.call(rpc, error, method, args);
1565
+ if (error && options.onFunctionError) {
1566
+ if (true === options.onFunctionError.call(rpc, error, method, args)) return;
1567
+ }
1568
+ if (!error) try {
1569
+ await post(serialize({
1570
+ t: TYPE_RESPONSE,
1571
+ i: msg.i,
1572
+ r: result
1573
+ }), ...extra);
1574
+ return;
1575
+ } catch (e) {
1576
+ error = e;
1577
+ if (options.onGeneralError?.call(rpc, e, method, args) !== true) throw e;
1578
+ }
1579
+ try {
1580
+ await post(serialize({
1581
+ t: TYPE_RESPONSE,
1582
+ i: msg.i,
1583
+ e: error
1584
+ }), ...extra);
1585
+ } catch (e) {
1586
+ if (options.onGeneralError?.call(rpc, e, method, args) !== true) throw e;
1587
+ }
1588
+ }
1589
+ } else {
1590
+ const { i: ack, r: result, e: error } = msg;
1591
+ const promise = _rpcPromiseMap.get(ack);
1592
+ if (promise) {
1593
+ dist_clearTimeout(promise.timeoutId);
1594
+ if (error) promise.reject(error);
1595
+ else promise.resolve(result);
1596
+ }
1597
+ _rpcPromiseMap.delete(ack);
1598
+ }
1599
+ }
1600
+ _promiseInit = on(onMessage);
1601
+ return rpc;
1602
+ }
1603
+ function createPromiseWithResolvers() {
1604
+ let resolve;
1605
+ let reject;
1606
+ const promise = new Promise((res, rej)=>{
1607
+ resolve = res;
1608
+ reject = rej;
1609
+ });
1610
+ return {
1611
+ promise,
1612
+ resolve,
1613
+ reject
1614
+ };
1615
+ }
1616
+ const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
1617
+ function nanoid(size = 21) {
1618
+ let id = "";
1619
+ let i = size;
1620
+ while(i--)id += urlAlphabet[64 * random() | 0];
1621
+ return id;
1622
+ }
1623
+ const picomatch = __webpack_require__("../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/index.js");
1624
+ const { createRsbuild: createRsbuild, rspack: rspack } = rsbuild;
1625
+ const hostController_dirname = dirname(fileURLToPath(import.meta.url));
1626
+ class ContainerRpcManager {
1627
+ wss;
1628
+ ws = null;
1629
+ rpc = null;
1630
+ methods;
1631
+ constructor(wss, methods){
1632
+ this.wss = wss;
1633
+ this.methods = methods;
1634
+ this.setupConnectionHandler();
1635
+ }
1636
+ updateMethods(methods) {
1637
+ this.methods = methods;
1638
+ if (this.ws && this.ws.readyState === this.ws.OPEN) this.attachWebSocket(this.ws);
1639
+ }
1640
+ setupConnectionHandler() {
1641
+ this.wss.on('connection', (ws)=>{
1642
+ logger.log(color.gray('[Browser UI] Container WebSocket connected'));
1643
+ logger.log(color.gray(`[Browser UI] Current ws: ${this.ws ? 'exists' : 'null'}, new ws: ${ws ? 'exists' : 'null'}`));
1644
+ this.attachWebSocket(ws);
1645
+ });
1646
+ }
1647
+ attachWebSocket(ws) {
1648
+ this.ws = ws;
1649
+ this.rpc = createBirpc(this.methods, {
1650
+ post: (data)=>{
1651
+ if (ws.readyState === ws.OPEN) ws.send(JSON.stringify(data));
1652
+ },
1653
+ on: (fn)=>{
1654
+ ws.on('message', (message)=>{
1655
+ try {
1656
+ const data = JSON.parse(message.toString());
1657
+ fn(data);
1658
+ } catch {}
1659
+ });
1660
+ }
1661
+ });
1662
+ ws.on('close', ()=>{
1663
+ if (this.ws === ws) {
1664
+ this.ws = null;
1665
+ this.rpc = null;
1666
+ }
1667
+ });
1668
+ }
1669
+ get isConnected() {
1670
+ return null !== this.ws && this.ws.readyState === this.ws.OPEN;
1671
+ }
1672
+ get currentWebSocket() {
1673
+ return this.ws;
1674
+ }
1675
+ reattach(ws) {
1676
+ this.attachWebSocket(ws);
1677
+ }
1678
+ async notifyTestFileUpdate(files) {
1679
+ await this.rpc?.onTestFileUpdate(files);
1680
+ }
1681
+ async reloadTestFile(testFile, testNamePattern) {
1682
+ logger.log(color.gray(`[Browser UI] reloadTestFile called, rpc: ${this.rpc ? 'exists' : 'null'}, ws: ${this.ws ? 'exists' : 'null'}`));
1683
+ if (!this.rpc) return void logger.log(color.yellow('[Browser UI] RPC not available, skipping reloadTestFile'));
1684
+ logger.log(color.gray(`[Browser UI] Calling reloadTestFile: ${testFile}`));
1685
+ await this.rpc.reloadTestFile(testFile, testNamePattern);
1686
+ }
1687
+ }
1688
+ const watchContext = {
1689
+ runtime: null,
1690
+ lastTestFiles: [],
1691
+ hooksEnabled: false,
1692
+ cleanupRegistered: false
1693
+ };
1694
+ const ensureProcessExitCode = (code)=>{
1695
+ if (void 0 === process.exitCode || 0 === process.exitCode) process.exitCode = code;
1696
+ };
1697
+ const globToRegexp = (glob)=>{
1698
+ const regex = picomatch.makeRe(glob, {
1699
+ fastpaths: false,
1700
+ noglobstar: false,
1701
+ bash: false
1702
+ });
1703
+ if (!regex) throw new Error(`Invalid glob pattern: ${glob}`);
1704
+ if (!glob.startsWith('./')) return regex;
1705
+ return new RegExp([
1706
+ '^\\.',
1707
+ glob.startsWith('./**') ? '' : '[\\\\/]',
1708
+ regex.source.substring(1)
1709
+ ].join(''));
1710
+ };
1711
+ const globPatternsToRegExp = (patterns)=>{
1712
+ const regexParts = patterns.map((pattern)=>{
1713
+ const regex = globToRegexp(pattern);
1714
+ let source = regex.source;
1715
+ if (source.startsWith('^')) source = source.substring(1);
1716
+ if (source.endsWith('$')) source = source.substring(0, source.length - 1);
1717
+ return source;
1718
+ });
1719
+ return new RegExp(`(?:${regexParts.join('|')})$`);
1720
+ };
1721
+ const excludePatternsToRegExp = (patterns)=>{
1722
+ const keywords = [];
1723
+ for (const pattern of patterns){
1724
+ const match = pattern.match(/\*\*\/\.?\{?([^/*{}]+(?:,[^/*{}]+)*)\}?\/?\*?\*?/);
1725
+ if (match) {
1726
+ const parts = match[1].split(',');
1727
+ for (const part of parts){
1728
+ const cleaned = part.replace(/^\./, '');
1729
+ if (cleaned && !keywords.includes(cleaned)) keywords.push(cleaned);
1730
+ }
1731
+ }
1732
+ }
1733
+ if (0 === keywords.length) return null;
1734
+ return new RegExp(`[\\\\/](${keywords.join('|')})[\\\\/]`);
1735
+ };
1736
+ const getRuntimeConfigFromProject = (project)=>{
1737
+ const { testNamePattern, testTimeout, passWithNoTests, retry, globals, clearMocks, resetMocks, restoreMocks, unstubEnvs, unstubGlobals, maxConcurrency, printConsoleTrace, disableConsoleIntercept, testEnvironment, hookTimeout, isolate, coverage, snapshotFormat, env, bail, logHeapUsage, chaiConfig, includeTaskLocation } = project.normalizedConfig;
1738
+ return {
1739
+ env,
1740
+ testNamePattern,
1741
+ testTimeout,
1742
+ hookTimeout,
1743
+ passWithNoTests,
1744
+ retry,
1745
+ globals,
1746
+ clearMocks,
1747
+ resetMocks,
1748
+ restoreMocks,
1749
+ unstubEnvs,
1750
+ unstubGlobals,
1751
+ maxConcurrency,
1752
+ printConsoleTrace,
1753
+ disableConsoleIntercept,
1754
+ testEnvironment,
1755
+ isolate,
1756
+ coverage,
1757
+ snapshotFormat,
1758
+ bail,
1759
+ logHeapUsage,
1760
+ chaiConfig,
1761
+ includeTaskLocation
1762
+ };
1763
+ };
1764
+ const getBrowserProjects = (context)=>context.projects.filter((project)=>project.normalizedConfig.browser.enabled);
1765
+ const collectProjectEntries = async (context)=>{
1766
+ const projectEntries = [];
1767
+ const browserProjects = getBrowserProjects(context);
1768
+ for (const project of browserProjects){
1769
+ const { normalizedConfig: { include, exclude, includeSource, setupFiles } } = project;
1770
+ const tests = await getTestEntries({
1771
+ include,
1772
+ exclude: exclude.patterns,
1773
+ includeSource,
1774
+ rootPath: context.rootPath,
1775
+ projectRoot: project.rootPath,
1776
+ fileFilters: context.fileFilters || []
1777
+ });
1778
+ const setup = getSetupFiles(setupFiles, project.rootPath);
1779
+ projectEntries.push({
1780
+ project,
1781
+ setupFiles: Object.values(setup),
1782
+ testFiles: Object.values(tests)
1783
+ });
1784
+ }
1785
+ return projectEntries;
1786
+ };
1787
+ const resolveBrowserFile = (relativePath)=>{
1788
+ const candidates = [
1789
+ external_pathe_resolve(hostController_dirname, '../src', relativePath),
1790
+ external_pathe_resolve(hostController_dirname, relativePath)
1791
+ ];
1792
+ for (const candidate of candidates)if (existsSync(candidate)) return candidate;
1793
+ throw new Error(`Unable to resolve browser client file: ${relativePath}`);
1794
+ };
1795
+ const resolveContainerDist = ()=>{
1796
+ const distPath = external_pathe_resolve(hostController_dirname, 'browser-container');
1797
+ if (existsSync(distPath)) return distPath;
1798
+ throw new Error(`Browser container build not found at ${distPath}. Please run "pnpm --filter @rstest/browser build".`);
1799
+ };
1800
+ const toSafeVarName = (name)=>name.replace(/[^a-zA-Z0-9_]/g, '_');
1801
+ const generateManifestModule = ({ manifestPath, entries })=>{
1802
+ const manifestDirPosix = normalize(dirname(manifestPath));
1803
+ const toRelativeImport = (filePath)=>{
1804
+ const posixPath = normalize(filePath);
1805
+ let relativePath = relative(manifestDirPosix, posixPath);
1806
+ if (!relativePath.startsWith('.')) relativePath = `./${relativePath}`;
1807
+ return relativePath;
1808
+ };
1809
+ const lines = [];
1810
+ lines.push('// All projects configuration');
1811
+ lines.push('export const projects = [');
1812
+ for (const { project } of entries){
1813
+ lines.push(' {');
1814
+ lines.push(` name: ${JSON.stringify(project.name)},`);
1815
+ lines.push(` environmentName: ${JSON.stringify(project.environmentName)},`);
1816
+ lines.push(` projectRoot: ${JSON.stringify(normalize(project.rootPath))},`);
1817
+ lines.push(' },');
1818
+ }
1819
+ lines.push('];');
1820
+ lines.push('');
1821
+ lines.push('// Setup loaders for each project');
1822
+ lines.push('export const projectSetupLoaders = {');
1823
+ for (const { project, setupFiles } of entries){
1824
+ lines.push(` ${JSON.stringify(project.name)}: [`);
1825
+ for (const filePath of setupFiles){
1826
+ const relativePath = toRelativeImport(filePath);
1827
+ lines.push(` () => import(${JSON.stringify(relativePath)}),`);
1828
+ }
1829
+ lines.push(' ],');
1830
+ }
1831
+ lines.push('};');
1832
+ lines.push('');
1833
+ lines.push('// Test context for each project');
1834
+ for (const { project } of entries){
1835
+ const varName = `context_${toSafeVarName(project.environmentName)}`;
1836
+ const projectRootPosix = normalize(project.rootPath);
1837
+ const includeRegExp = globPatternsToRegExp(project.normalizedConfig.include);
1838
+ const excludePatterns = project.normalizedConfig.exclude.patterns;
1839
+ const excludeRegExp = excludePatternsToRegExp(excludePatterns);
1840
+ lines.push(`const ${varName} = import.meta.webpackContext(${JSON.stringify(projectRootPosix)}, {`);
1841
+ lines.push(' recursive: true,');
1842
+ lines.push(` regExp: ${includeRegExp.toString()},`);
1843
+ if (excludeRegExp) lines.push(` exclude: ${excludeRegExp.toString()},`);
1844
+ lines.push(" mode: 'lazy',");
1845
+ lines.push('});');
1846
+ lines.push('');
1847
+ }
1848
+ lines.push('export const projectTestContexts = {');
1849
+ for (const { project } of entries){
1850
+ const varName = `context_${toSafeVarName(project.environmentName)}`;
1851
+ lines.push(` ${JSON.stringify(project.name)}: {`);
1852
+ lines.push(` getTestKeys: () => ${varName}.keys(),`);
1853
+ lines.push(` loadTest: (key) => ${varName}(key),`);
1854
+ lines.push(` projectRoot: ${JSON.stringify(normalize(project.rootPath))},`);
1855
+ lines.push(' },');
1856
+ }
1857
+ lines.push('};');
1858
+ lines.push('');
1859
+ lines.push('// Backward compatibility: export first project as default');
1860
+ lines.push('export const projectConfig = projects[0];');
1861
+ lines.push('export const setupLoaders = projectSetupLoaders[projects[0].name] || [];');
1862
+ lines.push('const _defaultCtx = projectTestContexts[projects[0].name];');
1863
+ lines.push('export const getTestKeys = () => _defaultCtx ? _defaultCtx.getTestKeys() : [];');
1864
+ lines.push('export const loadTest = (key) => _defaultCtx ? _defaultCtx.loadTest(key) : Promise.reject(new Error("No project found"));');
1865
+ return `${lines.join('\n')}\n`;
1866
+ };
1867
+ const htmlTemplate = `<!DOCTYPE html>
1868
+ <html lang="en">
1869
+ <head>
1870
+ <meta charset="UTF-8" />
1871
+ <title>Rstest Browser Runner</title>
1872
+ </head>
1873
+ <body>
1874
+ <script type="module" src="/static/js/runner.js"></script>
1875
+ </body>
1876
+ </html>
1877
+ `;
1878
+ const destroyBrowserRuntime = async (runtime)=>{
1879
+ try {
1880
+ await runtime.browser?.close?.();
1881
+ } catch {}
1882
+ try {
1883
+ await runtime.devServer?.close?.();
1884
+ } catch {}
1885
+ try {
1886
+ runtime.wss?.close();
1887
+ } catch {}
1888
+ await promises.rm(runtime.tempDir, {
1889
+ recursive: true,
1890
+ force: true
1891
+ }).catch(()=>{});
1892
+ };
1893
+ const registerWatchCleanup = ()=>{
1894
+ if (watchContext.cleanupRegistered) return;
1895
+ const cleanup = async ()=>{
1896
+ if (!watchContext.runtime) return;
1897
+ await destroyBrowserRuntime(watchContext.runtime);
1898
+ watchContext.runtime = null;
1899
+ };
1900
+ for (const signal of [
1901
+ 'SIGINT',
1902
+ 'SIGTERM'
1903
+ ])process.once(signal, ()=>{
1904
+ cleanup();
1905
+ });
1906
+ process.once('exit', ()=>{
1907
+ cleanup();
1908
+ });
1909
+ watchContext.cleanupRegistered = true;
1910
+ };
1911
+ const createBrowserRuntime = async ({ context, manifestPath, manifestSource, tempDir, isWatchMode, onTriggerRerun, containerDistPath, containerDevServer, forceHeadless })=>{
1912
+ const virtualManifestPlugin = new rspack.experiments.VirtualModulesPlugin({
1913
+ [manifestPath]: manifestSource
1914
+ });
1915
+ const optionsPlaceholder = '__RSTEST_OPTIONS_PLACEHOLDER__';
1916
+ const containerHtmlTemplate = containerDistPath ? await promises.readFile(join(containerDistPath, 'container.html'), 'utf-8') : null;
1917
+ let injectedContainerHtml = null;
1918
+ let serializedOptions = 'null';
1919
+ const setContainerOptions = (options)=>{
1920
+ serializedOptions = JSON.stringify(options).replace(/</g, '\\u003c');
1921
+ if (containerHtmlTemplate) injectedContainerHtml = containerHtmlTemplate.replace(optionsPlaceholder, serializedOptions);
1922
+ };
1923
+ const browserProjects = getBrowserProjects(context);
1924
+ const firstProject = browserProjects[0];
1925
+ const userPlugins = firstProject?.normalizedConfig.plugins || [];
1926
+ const userRsbuildConfig = firstProject?.normalizedConfig ?? {};
1927
+ const browserRuntimePath = fileURLToPath(import.meta.resolve('@rstest/core/browser-runtime'));
1928
+ const rstestInternalAliases = {
1929
+ '@rstest/browser-manifest': manifestPath,
1930
+ '@rstest/core': resolveBrowserFile('client/public.ts'),
1931
+ '@rstest/core/browser-runtime': browserRuntimePath,
1932
+ '@sinonjs/fake-timers': resolveBrowserFile('client/fakeTimersStub.ts')
1933
+ };
1934
+ const rsbuildInstance = await createRsbuild({
1935
+ callerName: 'rstest-browser',
1936
+ rsbuildConfig: {
1937
+ root: context.rootPath,
1938
+ mode: 'development',
1939
+ plugins: userPlugins,
1940
+ server: {
1941
+ printUrls: false,
1942
+ port: context.normalizedConfig.browser.port,
1943
+ strictPort: void 0 !== context.normalizedConfig.browser.port
1944
+ },
1945
+ dev: {
1946
+ client: {
1947
+ logLevel: 'error'
1948
+ }
1949
+ },
1950
+ environments: {
1951
+ web: {}
1952
+ }
1953
+ }
1954
+ });
1955
+ rsbuildInstance.addPlugins([
1956
+ {
1957
+ name: 'rstest:browser-user-config',
1958
+ setup (api) {
1959
+ api.modifyEnvironmentConfig((config, { mergeEnvironmentConfig })=>{
1960
+ const merged = mergeEnvironmentConfig(config, userRsbuildConfig, {
1961
+ source: {
1962
+ entry: {
1963
+ runner: resolveBrowserFile('client/entry.ts')
1964
+ }
1965
+ },
1966
+ resolve: {
1967
+ alias: rstestInternalAliases
1968
+ },
1969
+ output: {
1970
+ target: 'web',
1971
+ sourceMap: {
1972
+ js: 'source-map'
1973
+ }
1974
+ },
1975
+ tools: {
1976
+ rspack: (rspackConfig)=>{
1977
+ rspackConfig.mode = 'development';
1978
+ rspackConfig.lazyCompilation = {
1979
+ imports: true,
1980
+ entries: false
1981
+ };
1982
+ rspackConfig.plugins = rspackConfig.plugins || [];
1983
+ rspackConfig.plugins.push(virtualManifestPlugin);
1984
+ const browserRuntimeDir = dirname(browserRuntimePath);
1985
+ rspackConfig.module = rspackConfig.module || {};
1986
+ rspackConfig.module.rules = rspackConfig.module.rules || [];
1987
+ rspackConfig.module.rules.unshift({
1988
+ test: /\.js$/,
1989
+ include: browserRuntimeDir,
1990
+ extractSourceMap: true
1991
+ });
1992
+ if (isDebug()) logger.log(`[rstest:browser] extractSourceMap rule added for: ${browserRuntimeDir}`);
1993
+ }
1994
+ }
1995
+ });
1996
+ return merged;
1997
+ });
1998
+ }
1999
+ }
2000
+ ]);
2001
+ if (isWatchMode && onTriggerRerun) rsbuildInstance.addPlugins([
2002
+ {
2003
+ name: 'rstest:browser-watch',
2004
+ setup (api) {
2005
+ api.onBeforeDevCompile(()=>{
2006
+ if (!watchContext.hooksEnabled) return;
2007
+ logger.log(color.cyan('\nFile changed, re-running tests...\n'));
2008
+ });
2009
+ api.onAfterDevCompile(async ()=>{
2010
+ if (!watchContext.hooksEnabled) return;
2011
+ await onTriggerRerun();
2012
+ });
2013
+ }
2014
+ }
2015
+ ]);
2016
+ const devServer = await rsbuildInstance.createDevServer({
2017
+ getPortSilently: true
2018
+ });
2019
+ const serveContainer = containerDistPath ? sirv(containerDistPath, {
2020
+ dev: false,
2021
+ single: 'container.html'
2022
+ }) : null;
2023
+ const containerDevBase = containerDevServer ? new URL(containerDevServer) : null;
2024
+ const respondWithDevServerHtml = async (url, res)=>{
2025
+ if (!containerDevBase) return false;
2026
+ try {
2027
+ const target = new URL(url.pathname + url.search, containerDevBase);
2028
+ const response = await fetch(target);
2029
+ if (!response.ok) return false;
2030
+ let html = await response.text();
2031
+ html = html.replace(optionsPlaceholder, serializedOptions);
2032
+ res.statusCode = response.status;
2033
+ response.headers.forEach((value, key)=>{
2034
+ if ('content-length' === key.toLowerCase()) return;
2035
+ res.setHeader(key, value);
2036
+ });
2037
+ res.setHeader('Content-Type', 'text/html');
2038
+ res.end(html);
2039
+ return true;
2040
+ } catch (error) {
2041
+ logger.log(color.yellow(`[Browser UI] Failed to fetch container HTML from dev server: ${String(error)}`));
2042
+ return false;
2043
+ }
2044
+ };
2045
+ const proxyDevServerAsset = async (req, res)=>{
2046
+ if (!containerDevBase || !req.url) return false;
2047
+ try {
2048
+ const target = new URL(req.url, containerDevBase);
2049
+ const response = await fetch(target);
2050
+ if (!response.ok) return false;
2051
+ const buffer = Buffer.from(await response.arrayBuffer());
2052
+ res.statusCode = response.status;
2053
+ response.headers.forEach((value, key)=>{
2054
+ if ('content-length' === key.toLowerCase()) return;
2055
+ res.setHeader(key, value);
2056
+ });
2057
+ res.end(buffer);
2058
+ return true;
2059
+ } catch (error) {
2060
+ logger.log(color.yellow(`[Browser UI] Failed to proxy asset from dev server: ${String(error)}`));
2061
+ return false;
2062
+ }
2063
+ };
2064
+ devServer.middlewares.use(async (req, res, next)=>{
2065
+ if (!req.url) return void next();
2066
+ const url = new URL(req.url, 'http://localhost');
2067
+ if ('/__open-in-editor' === url.pathname) {
2068
+ const file = url.searchParams.get('file');
2069
+ if (!file) {
2070
+ res.statusCode = 400;
2071
+ res.end('Missing file');
2072
+ return;
2073
+ }
2074
+ try {
2075
+ await open_editor([
2076
+ {
2077
+ file
2078
+ }
2079
+ ]);
2080
+ res.statusCode = 204;
2081
+ res.end();
2082
+ } catch (error) {
2083
+ logger.log(color.yellow(`[Browser UI] Failed to open editor: ${String(error)}`));
2084
+ res.statusCode = 500;
2085
+ res.end('Failed to open editor');
2086
+ }
2087
+ return;
2088
+ }
2089
+ if ('/' === url.pathname || '/container.html' === url.pathname) {
2090
+ if (await respondWithDevServerHtml(url, res)) return;
2091
+ const html = injectedContainerHtml || containerHtmlTemplate?.replace(optionsPlaceholder, 'null');
2092
+ if (html) {
2093
+ res.setHeader('Content-Type', 'text/html');
2094
+ res.end(html);
2095
+ return;
2096
+ }
2097
+ res.statusCode = 502;
2098
+ res.end('Container UI is not available.');
2099
+ return;
2100
+ }
2101
+ if (url.pathname.startsWith('/container-static/')) {
2102
+ if (await proxyDevServerAsset(req, res)) return;
2103
+ if (serveContainer) return void serveContainer(req, res, next);
2104
+ res.statusCode = 502;
2105
+ res.end('Container assets are not available.');
2106
+ return;
2107
+ }
2108
+ if ('/runner.html' === url.pathname) {
2109
+ res.setHeader('Content-Type', 'text/html');
2110
+ res.end(htmlTemplate);
2111
+ return;
2112
+ }
2113
+ next();
2114
+ });
2115
+ const { port } = await devServer.listen();
2116
+ const wsPort = port + 1;
2117
+ const wss = new WebSocketServer({
2118
+ port: wsPort
2119
+ });
2120
+ logger.log(color.gray(`[Browser UI] WebSocket server started on port ${wsPort}`));
2121
+ let browserLauncher;
2122
+ const browserName = context.normalizedConfig.browser.browser;
2123
+ try {
2124
+ const playwright = await import("playwright");
2125
+ browserLauncher = playwright[browserName];
2126
+ } catch (_error) {
2127
+ wss.close();
2128
+ await devServer.close();
2129
+ throw _error;
2130
+ }
2131
+ let browser;
2132
+ try {
2133
+ browser = await browserLauncher.launch({
2134
+ headless: forceHeadless ?? context.normalizedConfig.browser.headless,
2135
+ args: 'chromium' === browserName ? [
2136
+ '--disable-popup-blocking',
2137
+ '--no-first-run',
2138
+ '--no-default-browser-check'
2139
+ ] : void 0
2140
+ });
2141
+ } catch (_error) {
2142
+ wss.close();
2143
+ await devServer.close();
2144
+ throw _error;
2145
+ }
2146
+ return {
2147
+ rsbuildInstance,
2148
+ devServer,
2149
+ browser,
2150
+ port,
2151
+ wsPort,
2152
+ manifestPath,
2153
+ tempDir,
2154
+ manifestPlugin: virtualManifestPlugin,
2155
+ setContainerOptions,
2156
+ wss
2157
+ };
2158
+ };
2159
+ const runBrowserController = async (context)=>{
2160
+ const buildStart = Date.now();
2161
+ const containerDevServerEnv = process.env.RSTEST_CONTAINER_DEV_SERVER;
2162
+ let containerDevServer;
2163
+ let containerDistPath;
2164
+ if (containerDevServerEnv) try {
2165
+ containerDevServer = new URL(containerDevServerEnv).toString();
2166
+ logger.log(color.gray(`[Browser UI] Using dev server for container: ${containerDevServer}`));
2167
+ } catch (error) {
2168
+ logger.error(color.red(`Invalid RSTEST_CONTAINER_DEV_SERVER value: ${String(error)}`));
2169
+ ensureProcessExitCode(1);
2170
+ return;
2171
+ }
2172
+ if (!containerDevServer) try {
2173
+ containerDistPath = resolveContainerDist();
2174
+ } catch (error) {
2175
+ logger.error(color.red(String(error)));
2176
+ ensureProcessExitCode(1);
2177
+ return;
2178
+ }
2179
+ const projectEntries = await collectProjectEntries(context);
2180
+ const totalTests = projectEntries.reduce((total, item)=>total + item.testFiles.length, 0);
2181
+ if (0 === totalTests) {
2182
+ const code = context.normalizedConfig.passWithNoTests ? 0 : 1;
2183
+ logger.log(color[code ? 'red' : 'yellow'](`No test files found, exiting with code ${code}.`));
2184
+ if (0 !== code) ensureProcessExitCode(code);
2185
+ return;
2186
+ }
2187
+ const isWatchMode = 'watch' === context.command;
2188
+ const tempDir = isWatchMode && watchContext.runtime ? watchContext.runtime.tempDir : isWatchMode ? join(context.rootPath, TEMP_RSTEST_OUTPUT_DIR, 'browser', 'watch') : join(context.rootPath, TEMP_RSTEST_OUTPUT_DIR, 'browser', Date.now().toString());
2189
+ const manifestPath = join(tempDir, 'manifest.ts');
2190
+ const manifestSource = generateManifestModule({
2191
+ manifestPath,
2192
+ entries: projectEntries
2193
+ });
2194
+ if (isWatchMode) watchContext.lastTestFiles = projectEntries.flatMap((entry)=>entry.testFiles.map((testPath)=>({
2195
+ testPath,
2196
+ projectName: entry.project.name
2197
+ })));
2198
+ let runtime = isWatchMode ? watchContext.runtime : null;
2199
+ let triggerRerun;
2200
+ if (!runtime) {
2201
+ try {
2202
+ runtime = await createBrowserRuntime({
2203
+ context,
2204
+ manifestPath,
2205
+ manifestSource,
2206
+ tempDir,
2207
+ isWatchMode,
2208
+ onTriggerRerun: isWatchMode ? async ()=>{
2209
+ await triggerRerun?.();
2210
+ } : void 0,
2211
+ containerDistPath,
2212
+ containerDevServer
2213
+ });
2214
+ } catch (error) {
2215
+ logger.error(error instanceof Error ? error : new Error(String(error)));
2216
+ ensureProcessExitCode(1);
2217
+ await promises.rm(tempDir, {
2218
+ recursive: true,
2219
+ force: true
2220
+ }).catch(()=>{});
2221
+ return;
2222
+ }
2223
+ if (isWatchMode) {
2224
+ watchContext.runtime = runtime;
2225
+ registerWatchCleanup();
2226
+ }
2227
+ }
2228
+ const { browser, port, wsPort, wss } = runtime;
2229
+ const buildTime = Date.now() - buildStart;
2230
+ const allTestFiles = projectEntries.flatMap((entry)=>entry.testFiles.map((testPath)=>({
2231
+ testPath: normalize(testPath),
2232
+ projectName: entry.project.name
2233
+ })));
2234
+ const browserProjectsForRuntime = getBrowserProjects(context);
2235
+ const projectRuntimeConfigs = browserProjectsForRuntime.map((project)=>({
2236
+ name: project.name,
2237
+ environmentName: project.environmentName,
2238
+ projectRoot: normalize(project.rootPath),
2239
+ runtimeConfig: serializableConfig(getRuntimeConfigFromProject(project))
2240
+ }));
2241
+ const maxTestTimeoutForRpc = Math.max(...browserProjectsForRuntime.map((p)=>p.normalizedConfig.testTimeout ?? 5000));
2242
+ const hostOptions = {
2243
+ rootPath: normalize(context.rootPath),
2244
+ projects: projectRuntimeConfigs,
2245
+ snapshot: {
2246
+ updateSnapshot: context.snapshotManager.options.updateSnapshot
2247
+ },
2248
+ runnerUrl: `http://localhost:${port}`,
2249
+ wsPort,
2250
+ debug: isDebug(),
2251
+ rpcTimeout: maxTestTimeoutForRpc
2252
+ };
2253
+ runtime.setContainerOptions(hostOptions);
2254
+ const reporterResults = [];
2255
+ const caseResults = [];
2256
+ let completedTests = 0;
2257
+ let fatalError = null;
2258
+ let resolveAllTests;
2259
+ const allTestsPromise = new Promise((resolve)=>{
2260
+ resolveAllTests = resolve;
2261
+ });
2262
+ let containerContext;
2263
+ let containerPage;
2264
+ let isNewPage = false;
2265
+ if (isWatchMode && runtime.containerPage && runtime.containerContext) {
2266
+ containerContext = runtime.containerContext;
2267
+ containerPage = runtime.containerPage;
2268
+ logger.log(color.gray('\n[Watch] Reusing existing container page\n'));
2269
+ } else {
2270
+ isNewPage = true;
2271
+ containerContext = await browser.newContext({
2272
+ viewport: null
2273
+ });
2274
+ containerPage = await containerContext.newPage();
2275
+ containerPage.on('popup', async (popup)=>{
2276
+ await popup.close().catch(()=>{});
2277
+ });
2278
+ containerContext.on('page', async (page)=>{
2279
+ if (page !== containerPage) await page.close().catch(()=>{});
2280
+ });
2281
+ if (isWatchMode) {
2282
+ runtime.containerPage = containerPage;
2283
+ runtime.containerContext = containerContext;
2284
+ }
2285
+ containerPage.on('console', (msg)=>{
2286
+ const text = msg.text();
2287
+ if (text.includes('[Container]') || text.includes('[Runner]')) logger.log(color.gray(`[Browser Console] ${text}`));
2288
+ });
2289
+ }
2290
+ const createRpcMethods = ()=>({
2291
+ async rerunTest (testFile, testNamePattern) {
2292
+ logger.log(color.cyan(`\nRe-running test: ${testFile}${testNamePattern ? ` (pattern: ${testNamePattern})` : ''}\n`));
2293
+ await rpcManager.reloadTestFile(testFile, testNamePattern);
2294
+ },
2295
+ async getTestFiles () {
2296
+ return allTestFiles;
2297
+ },
2298
+ async onTestFileStart (payload) {
2299
+ await Promise.all(context.reporters.map((reporter)=>reporter.onTestFileStart?.({
2300
+ testPath: payload.testPath,
2301
+ tests: []
2302
+ })));
2303
+ },
2304
+ async onTestCaseResult (payload) {
2305
+ caseResults.push(payload);
2306
+ await Promise.all(context.reporters.map((reporter)=>reporter.onTestCaseResult?.(payload)));
2307
+ },
2308
+ async onTestFileComplete (payload) {
2309
+ reporterResults.push(payload);
2310
+ if (payload.snapshotResult) context.snapshotManager.add(payload.snapshotResult);
2311
+ await Promise.all(context.reporters.map((reporter)=>reporter.onTestFileResult?.(payload)));
2312
+ completedTests++;
2313
+ if (completedTests >= allTestFiles.length && resolveAllTests) resolveAllTests();
2314
+ },
2315
+ async onLog (payload) {
2316
+ const log = {
2317
+ content: payload.content,
2318
+ name: payload.level,
2319
+ testPath: payload.testPath,
2320
+ type: payload.type,
2321
+ trace: payload.trace
2322
+ };
2323
+ const shouldLog = context.normalizedConfig.onConsoleLog?.(log.content) ?? true;
2324
+ if (shouldLog) await Promise.all(context.reporters.map((reporter)=>reporter.onUserConsoleLog?.(log)));
2325
+ },
2326
+ async onFatal (payload) {
2327
+ fatalError = new Error(payload.message);
2328
+ fatalError.stack = payload.stack;
2329
+ if (resolveAllTests) resolveAllTests();
2330
+ },
2331
+ async resolveSnapshotPath (testPath) {
2332
+ const snapExtension = '.snap';
2333
+ const resolver = context.normalizedConfig.resolveSnapshotPath || (()=>join(dirname(testPath), '__snapshots__', `${basename(testPath)}${snapExtension}`));
2334
+ return resolver(testPath, snapExtension);
2335
+ },
2336
+ async readSnapshotFile (filepath) {
2337
+ try {
2338
+ return await promises.readFile(filepath, 'utf-8');
2339
+ } catch {
2340
+ return null;
2341
+ }
2342
+ },
2343
+ async saveSnapshotFile (filepath, content) {
2344
+ const dir = dirname(filepath);
2345
+ await promises.mkdir(dir, {
2346
+ recursive: true
2347
+ });
2348
+ await promises.writeFile(filepath, content, 'utf-8');
2349
+ },
2350
+ async removeSnapshotFile (filepath) {
2351
+ try {
2352
+ await promises.unlink(filepath);
2353
+ } catch {}
2354
+ }
2355
+ });
2356
+ let rpcManager;
2357
+ if (isWatchMode && runtime.rpcManager) {
2358
+ rpcManager = runtime.rpcManager;
2359
+ rpcManager.updateMethods(createRpcMethods());
2360
+ const existingWs = rpcManager.currentWebSocket;
2361
+ if (existingWs) rpcManager.reattach(existingWs);
2362
+ } else {
2363
+ rpcManager = new ContainerRpcManager(wss, createRpcMethods());
2364
+ if (isWatchMode) runtime.rpcManager = rpcManager;
2365
+ }
2366
+ if (isNewPage) {
2367
+ await containerPage.goto(`http://localhost:${port}/container.html`, {
2368
+ waitUntil: 'load'
2369
+ });
2370
+ logger.log(color.cyan(`\nContainer page opened at http://localhost:${port}/container.html\n`));
2371
+ }
2372
+ const maxTestTimeout = Math.max(...browserProjectsForRuntime.map((p)=>p.normalizedConfig.testTimeout ?? 5000));
2373
+ const totalTimeoutMs = maxTestTimeout * allTestFiles.length + 30000;
2374
+ let timeoutId;
2375
+ const testTimeout = new Promise((resolve)=>{
2376
+ timeoutId = setTimeout(()=>{
2377
+ logger.log(color.yellow(`\nTest execution timeout after ${totalTimeoutMs / 1000}s. Completed: ${completedTests}/${allTestFiles.length}\n`));
2378
+ resolve();
2379
+ }, totalTimeoutMs);
2380
+ });
2381
+ const testStart = Date.now();
2382
+ await Promise.race([
2383
+ allTestsPromise,
2384
+ testTimeout
2385
+ ]);
2386
+ if (timeoutId) clearTimeout(timeoutId);
2387
+ const testTime = Date.now() - testStart;
2388
+ if (isWatchMode) triggerRerun = async ()=>{
2389
+ const newProjectEntries = await collectProjectEntries(context);
2390
+ const currentTestFiles = newProjectEntries.flatMap((entry)=>entry.testFiles.map((testPath)=>({
2391
+ testPath: normalize(testPath),
2392
+ projectName: entry.project.name
2393
+ })));
2394
+ const serialize = (files)=>JSON.stringify(files.map((f)=>`${f.projectName}:${f.testPath}`).sort());
2395
+ const filesChanged = serialize(currentTestFiles) !== serialize(watchContext.lastTestFiles);
2396
+ if (filesChanged) {
2397
+ watchContext.lastTestFiles = currentTestFiles;
2398
+ await rpcManager.notifyTestFileUpdate(currentTestFiles);
2399
+ }
2400
+ logger.log(color.cyan('Tests will be re-executed automatically\n'));
2401
+ };
2402
+ if (!isWatchMode) await destroyBrowserRuntime(runtime);
2403
+ if (fatalError) {
2404
+ logger.error(color.red(`Browser test run failed: ${fatalError.message}`));
2405
+ ensureProcessExitCode(1);
2406
+ return;
2407
+ }
2408
+ const duration = {
2409
+ totalTime: buildTime + testTime,
2410
+ buildTime,
2411
+ testTime
2412
+ };
2413
+ context.updateReporterResultState(reporterResults, caseResults);
2414
+ const isFailure = reporterResults.some((result)=>'fail' === result.status);
2415
+ if (isFailure) ensureProcessExitCode(1);
2416
+ for (const reporter of context.reporters)await reporter.onTestRunEnd?.({
2417
+ results: context.reporterResults.results,
2418
+ testResults: context.reporterResults.testResults,
2419
+ duration,
2420
+ snapshotSummary: context.snapshotManager.summary,
2421
+ getSourcemap: async ()=>null
2422
+ });
2423
+ if (isWatchMode && triggerRerun) {
2424
+ watchContext.hooksEnabled = true;
2425
+ logger.log(color.cyan('\nWatch mode enabled - will re-run tests on file changes\n'));
2426
+ }
2427
+ };
2428
+ const listBrowserTests = async (context)=>{
2429
+ const projectEntries = await collectProjectEntries(context);
2430
+ const totalTests = projectEntries.reduce((total, item)=>total + item.testFiles.length, 0);
2431
+ if (0 === totalTests) return {
2432
+ list: [],
2433
+ close: async ()=>{}
2434
+ };
2435
+ const tempDir = join(context.rootPath, TEMP_RSTEST_OUTPUT_DIR, 'browser', `list-${Date.now()}`);
2436
+ const manifestPath = join(tempDir, 'manifest.ts');
2437
+ const manifestSource = generateManifestModule({
2438
+ manifestPath,
2439
+ entries: projectEntries
2440
+ });
2441
+ let runtime;
2442
+ try {
2443
+ runtime = await createBrowserRuntime({
2444
+ context,
2445
+ manifestPath,
2446
+ manifestSource,
2447
+ tempDir,
2448
+ isWatchMode: false,
2449
+ containerDistPath: void 0,
2450
+ containerDevServer: void 0,
2451
+ forceHeadless: true
2452
+ });
2453
+ } catch (error) {
2454
+ logger.error(color.red('Failed to load Playwright. Please install "playwright" to use browser mode.'), error);
2455
+ throw error;
2456
+ }
2457
+ const { browser, port } = runtime;
2458
+ const browserProjects = getBrowserProjects(context);
2459
+ const projectRuntimeConfigs = browserProjects.map((project)=>({
2460
+ name: project.name,
2461
+ environmentName: project.environmentName,
2462
+ projectRoot: normalize(project.rootPath),
2463
+ runtimeConfig: serializableConfig(getRuntimeConfigFromProject(project))
2464
+ }));
2465
+ const maxTestTimeoutForRpc = Math.max(...browserProjects.map((p)=>p.normalizedConfig.testTimeout ?? 5000));
2466
+ const hostOptions = {
2467
+ rootPath: normalize(context.rootPath),
2468
+ projects: projectRuntimeConfigs,
2469
+ snapshot: {
2470
+ updateSnapshot: context.snapshotManager.options.updateSnapshot
2471
+ },
2472
+ mode: 'collect',
2473
+ debug: isDebug(),
2474
+ rpcTimeout: maxTestTimeoutForRpc
2475
+ };
2476
+ runtime.setContainerOptions(hostOptions);
2477
+ const collectResults = [];
2478
+ let fatalError = null;
2479
+ let collectCompleted = false;
2480
+ let resolveCollect;
2481
+ const collectPromise = new Promise((resolve)=>{
2482
+ resolveCollect = resolve;
2483
+ });
2484
+ const browserContext = await browser.newContext({
2485
+ viewport: null
2486
+ });
2487
+ const page = await browserContext.newPage();
2488
+ await page.exposeFunction('__rstest_dispatch__', (message)=>{
2489
+ switch(message.type){
2490
+ case 'collect-result':
2491
+ {
2492
+ const payload = message.payload;
2493
+ collectResults.push({
2494
+ testPath: payload.testPath,
2495
+ project: payload.project,
2496
+ tests: payload.tests
2497
+ });
2498
+ break;
2499
+ }
2500
+ case 'collect-complete':
2501
+ collectCompleted = true;
2502
+ resolveCollect?.();
2503
+ break;
2504
+ case 'fatal':
2505
+ {
2506
+ const payload = message.payload;
2507
+ fatalError = new Error(payload.message);
2508
+ fatalError.stack = payload.stack;
2509
+ resolveCollect?.();
2510
+ break;
2511
+ }
2512
+ case 'ready':
2513
+ case 'log':
2514
+ break;
2515
+ default:
2516
+ logger.debug(`[List] Unexpected message: ${message.type}`);
2517
+ }
2518
+ });
2519
+ const serializedOptions = JSON.stringify(hostOptions).replace(/</g, '\\u003c');
2520
+ await page.addInitScript(`window.__RSTEST_BROWSER_OPTIONS__ = ${serializedOptions};`);
2521
+ await page.goto(`http://localhost:${port}/runner.html`, {
2522
+ waitUntil: 'load'
2523
+ });
2524
+ const timeoutMs = 30000;
2525
+ let timeoutId;
2526
+ const timeoutPromise = new Promise((resolve)=>{
2527
+ timeoutId = setTimeout(()=>{
2528
+ if (!collectCompleted) logger.warn(color.yellow(`[List] Browser test collection timed out after ${timeoutMs}ms`));
2529
+ resolve();
2530
+ }, timeoutMs);
2531
+ });
2532
+ await Promise.race([
2533
+ collectPromise,
2534
+ timeoutPromise
2535
+ ]);
2536
+ if (timeoutId) clearTimeout(timeoutId);
2537
+ const cleanup = async ()=>{
2538
+ try {
2539
+ await page.close();
2540
+ await browserContext.close();
2541
+ } catch {}
2542
+ await destroyBrowserRuntime(runtime);
2543
+ };
2544
+ if (fatalError) {
2545
+ await cleanup();
2546
+ const errorResult = {
2547
+ testPath: '',
2548
+ project: '',
2549
+ tests: [],
2550
+ errors: [
2551
+ {
2552
+ name: 'BrowserCollectError',
2553
+ message: fatalError.message,
2554
+ stack: fatalError.stack
2555
+ }
2556
+ ]
2557
+ };
2558
+ return {
2559
+ list: [
2560
+ errorResult
2561
+ ],
2562
+ close: async ()=>{}
2563
+ };
2564
+ }
2565
+ return {
2566
+ list: collectResults,
2567
+ close: cleanup
2568
+ };
2569
+ };
2570
+ async function runBrowserTests(context) {
2571
+ await runBrowserController(context);
2572
+ }
2573
+ async function src_listBrowserTests(context) {
2574
+ return listBrowserTests(context);
2575
+ }
2576
+ export { runBrowserTests, src_listBrowserTests as listBrowserTests };