@molecule/api-ai-tools 1.8.3 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -28,19 +28,29 @@
28
28
  */
29
29
  import { locatedCommandSegments } from './command-position.js';
30
30
  import { isDiagnosisRun } from './suite-overlap.js';
31
- import { isPlaywrightRunCommand } from './utilities.js';
31
+ import { isPlaywrightRunCommand, redactSecrets } from './utilities.js';
32
32
  /** How many traces one read reports, newest first. */
33
33
  export const MAX_TRACE_ARTIFACTS = 12;
34
34
  /** How many requests one test's trace contributes. */
35
35
  export const MAX_TRACED_REQUESTS = 6;
36
36
  /** How many console errors one test's trace contributes. */
37
37
  export const MAX_TRACED_CONSOLE = 4;
38
+ /** How many of the page's own actions one test's trace contributes, the LAST ones. */
39
+ export const MAX_TRACED_ACTIONS = 40;
40
+ /** How much of a 4xx request's sent body (or its response body) the trace read keeps. */
41
+ export const MAX_TRACED_BODY_CHARS = 300;
38
42
  /** How many tests the note in a run's result names. */
39
43
  export const MAX_TRACE_NOTE_TESTS = 5;
40
44
  /** How many requests the note names per test. */
41
45
  export const MAX_TRACE_NOTE_REQUESTS = 4;
42
46
  /** How long a URL the note prints, before the middle is cut. */
43
47
  export const TRACE_NOTE_URL_CHARS = 96;
48
+ /** How much of the sent body the note prints — enough to see a `{"data":…}` wrapper. */
49
+ export const TRACE_NOTE_SENT_CHARS = 160;
50
+ /** How much of the response body the note prints — the API's first error line. */
51
+ export const TRACE_NOTE_GOT_CHARS = 120;
52
+ /** How much of a network error's own text (`net::ERR_CONNECTION_REFUSED`) the note prints. */
53
+ const TRACE_NOTE_FAILURE_CHARS = 120;
44
54
  /**
45
55
  * The Node program the sandbox runs to read a failed run's traces: `node <script> <dir>` prints one
46
56
  * `TRACE <json>` line per `trace.zip` under `<dir>` (newest first, {@link MAX_TRACE_ARTIFACTS} of
@@ -52,10 +62,17 @@ export const TRACE_NOTE_URL_CHARS = 96;
52
62
  * central directory and `zlib.inflateRawSync` — about forty lines, and the only thing that has to
53
63
  * be true of a `trace.zip` is that it IS one.
54
64
  *
55
- * A request is kept over a static asset when it failed (`status >= 400`) or never answered
56
- * (`status === 0`), and over anything else when it is the page talking to the app
57
- * (`fetch`/`xhr`/`document`): a failing test's trace holds the whole page load, and the answer is
58
- * always at the end of it.
65
+ * A request is kept over a static asset when it failed — a `status >= 400`, or no response at all
66
+ * together with Playwright's own error text (`-1` plus `_failureText`, a refused connection) — and
67
+ * over anything else when it is the page talking to the app (`fetch`/`xhr`/`document`): a failing
68
+ * test's trace holds the whole page load. A request with no response and NO error text was aborted
69
+ * or still in flight when the test ended; it is kept too, but as what it is, not as a failure.
70
+ *
71
+ * A request answered 4xx by the app's OWN API also keeps what the page SENT and the first of what
72
+ * came back: Playwright stores either inline (`text`) or in a resource entry (`_file`), and both
73
+ * are read. That is where a wrapped body shows: xr-molecule-flash-m111's helper passed
74
+ * `{ data: body }` as app-http's second argument, so every POST/PUT was `{"data":{…}}` and the API
75
+ * validated `{}`. Status alone said only "422".
59
76
  *
60
77
  * @returns The program's source, to write to a file and run.
61
78
  */
@@ -68,6 +85,8 @@ const ROOT = process.argv[2] || '/workspace'
68
85
  const MAX_TRACES = ${MAX_TRACE_ARTIFACTS}
69
86
  const MAX_REQUESTS = ${MAX_TRACED_REQUESTS}
70
87
  const MAX_CONSOLE = ${MAX_TRACED_CONSOLE}
88
+ const MAX_ACTIONS = ${MAX_TRACED_ACTIONS}
89
+ const MAX_BODY = ${MAX_TRACED_BODY_CHARS}
71
90
  const MAX_URL = 200
72
91
 
73
92
  function walk(dir, depth, out) {
@@ -140,6 +159,22 @@ function messageOf(value) {
140
159
  return null
141
160
  }
142
161
 
162
+ // A request/response body exactly as Playwright stored it: inline on the value, or in a resource
163
+ // entry the value names. Bounded, and binary is dropped — a JSON body is what this is for, and a
164
+ // multipart upload would print bytes.
165
+ function bodyText(buf, entries, value, max) {
166
+ if (!value || typeof value !== 'object') return ''
167
+ const inline = typeof value.text === 'string' ? value.text : ''
168
+ if (inline) return inline.slice(0, max)
169
+ if (typeof value._file !== 'string') return ''
170
+ const entry = entries.find(function (e) { return e.name === value._file })
171
+ if (!entry) return ''
172
+ const data = zipData(buf, entry)
173
+ if (!data) return ''
174
+ const text = data.toString('utf8').slice(0, max)
175
+ return text.indexOf('\\u0000') === -1 ? text : ''
176
+ }
177
+
143
178
  let origin = ''
144
179
  function pathOf(url) {
145
180
  try {
@@ -188,7 +223,10 @@ for (const one of found.slice(0, MAX_TRACES)) {
188
223
  method: String(request.method).toUpperCase(),
189
224
  path: String(request.url).slice(0, MAX_URL),
190
225
  status: Number(response.status) || 0,
226
+ failure: typeof response._failureText === 'string' ? response._failureText : undefined,
191
227
  type: type,
228
+ postData: request.postData,
229
+ content: response.content,
192
230
  })
193
231
  }
194
232
  if (!origin) {
@@ -196,12 +234,27 @@ for (const one of found.slice(0, MAX_TRACES)) {
196
234
  origin = first ? originOf(first.path) : ''
197
235
  }
198
236
  for (const r of requests) r.path = pathOf(r.path).slice(0, MAX_URL)
237
+ // A 4xx from the app's own API is where a body SHAPE is diagnosed: keep what the page sent and
238
+ // what came back (bounded). A relative path here means the same origin as the traced document.
239
+ for (const r of requests) {
240
+ if (r.status < 400 || r.status >= 500) continue
241
+ if (/^https?:/i.test(r.path)) continue
242
+ const sent = bodyText(buf, entries, r.postData, MAX_BODY)
243
+ const got = bodyText(buf, entries, r.content, MAX_BODY)
244
+ if (sent) r.sent = sent
245
+ if (got) r.got = got
246
+ }
247
+ // A failed request: a 4xx/5xx, or no response at all WITH Playwright's error text (a refused
248
+ // connection). No response and no text is an aborted/pending request, not a failure.
249
+ function failedRequest(r) {
250
+ return r.status >= 400 || (r.status <= 0 && typeof r.failure === 'string')
251
+ }
199
252
  const failing = []
200
253
  const api = []
201
254
  const rest = []
202
255
  for (let i = 0; i < requests.length; i++) {
203
256
  const r = requests[i]
204
- if (r.status === 0 || r.status >= 400) failing.push(i)
257
+ if (failedRequest(r)) failing.push(i)
205
258
  else if (r.type === 'fetch' || r.type === 'xhr' || r.type === 'document') api.push(i)
206
259
  else rest.push(i)
207
260
  }
@@ -209,15 +262,41 @@ for (const one of found.slice(0, MAX_TRACES)) {
209
262
  const chosen = []
210
263
  const seen = new Set()
211
264
  for (const i of keep.sort(function (a, b) {
212
- const wrong = requests[a].status === 0 || requests[a].status >= 400 ? 0 : 1
213
- const other = requests[b].status === 0 || requests[b].status >= 400 ? 0 : 1
265
+ const wrong = failedRequest(requests[a]) ? 0 : 1
266
+ const other = failedRequest(requests[b]) ? 0 : 1
214
267
  return wrong === other ? a - b : wrong - other
215
268
  })) {
216
269
  const r = requests[i]
217
270
  const key = r.method + ' ' + r.path + ' ' + r.status
218
271
  if (seen.has(key)) continue
219
272
  seen.add(key)
220
- chosen.push({ method: r.method, path: r.path, status: r.status })
273
+ chosen.push({
274
+ method: r.method,
275
+ path: r.path,
276
+ status: r.status,
277
+ failure: r.failure,
278
+ sent: r.sent,
279
+ got: r.got,
280
+ })
281
+ }
282
+ // The page's own actions, timed. before/after pair on the call id, and only the LIBRARY
283
+ // traces carry a class and a method (Frame.goto, Locator.fill); the test runner's own
284
+ // trace times its hooks and fixtures with class Test, which is not a page action.
285
+ const ended = {}
286
+ for (const entry of trace) {
287
+ if (entry && entry.type === 'after' && typeof entry.callId === 'string' && typeof entry.endTime === 'number')
288
+ ended[entry.callId] = entry.endTime
289
+ }
290
+ const actions = []
291
+ for (const entry of trace) {
292
+ if (!entry || entry.type !== 'before') continue
293
+ if (typeof entry.class !== 'string' || typeof entry.method !== 'string') continue
294
+ if (entry.class === 'Test' || typeof entry.startTime !== 'number') continue
295
+ actions.push({
296
+ api: entry.class + '.' + entry.method,
297
+ start: entry.startTime,
298
+ end: typeof ended[entry.callId] === 'number' ? ended[entry.callId] : null,
299
+ })
221
300
  }
222
301
  const consoleErrors = []
223
302
  let spec = null
@@ -241,13 +320,17 @@ for (const one of found.slice(0, MAX_TRACES)) {
241
320
  // of its own about it, that is the line worth reading.
242
321
  const specific = consoleErrors.filter(function (text) { return !/^Failed to load resource/.test(text) })
243
322
  const keptConsole = specific.length > 0 ? specific : consoleErrors
244
- if (chosen.length === 0 && keptConsole.length === 0) continue
323
+ if (chosen.length === 0 && keptConsole.length === 0 && actions.length === 0) continue
245
324
  process.stdout.write('TRACE ' + JSON.stringify({
246
325
  dir: path.relative(ROOT, path.dirname(one.file)),
247
326
  spec: spec,
248
327
  line: line,
249
- requests: chosen.slice(-MAX_REQUESTS),
328
+ // Failures first, per the sort above; the keep-window is the HEAD of that order. Taking the
329
+ // tail here dropped every failed request as soon as a page made more than MAX_REQUESTS of them
330
+ // plus the answered ones (xr-molecule-flash-m112's note listed only -1s and 200s).
331
+ requests: chosen.slice(0, MAX_REQUESTS),
250
332
  console: keptConsole.slice(0, MAX_CONSOLE).map(function (text) { return text.slice(0, 300) }),
333
+ actions: actions.slice(-MAX_ACTIONS),
251
334
  }) + '\\n')
252
335
  }
253
336
  `;
@@ -269,6 +352,7 @@ export function parseTraceArtifacts(stdout) {
269
352
  const raw = JSON.parse(line.slice('TRACE '.length));
270
353
  const requests = Array.isArray(raw.requests) ? raw.requests : [];
271
354
  const consoleMessages = Array.isArray(raw.console) ? raw.console : [];
355
+ const actions = Array.isArray(raw.actions) ? raw.actions : [];
272
356
  out.push({
273
357
  spec: typeof raw.spec === 'string' ? raw.spec : null,
274
358
  line: typeof raw.line === 'number' ? raw.line : 0,
@@ -278,8 +362,19 @@ export function parseTraceArtifacts(stdout) {
278
362
  method: String(r.method ?? ''),
279
363
  path: String(r.path ?? ''),
280
364
  status: Number(r.status ?? 0) || 0,
365
+ ...(typeof r.failure === 'string' && r.failure ? { failure: r.failure } : {}),
366
+ ...(typeof r.sent === 'string' && r.sent ? { sent: r.sent } : {}),
367
+ ...(typeof r.got === 'string' && r.got ? { got: r.got } : {}),
281
368
  })),
282
369
  console: consoleMessages.map(String),
370
+ actions: actions
371
+ .filter((a) => typeof a === 'object' && a !== null)
372
+ .filter((a) => typeof a.api === 'string' && Number.isFinite(Number(a.start)))
373
+ .map((a) => ({
374
+ api: String(a.api),
375
+ start: Number(a.start),
376
+ end: a.end === null || a.end === undefined ? null : Number(a.end),
377
+ })),
283
378
  });
284
379
  }
285
380
  catch (_error) {
@@ -294,12 +389,141 @@ function whereOf(item) {
294
389
  return 'a failing test';
295
390
  return item.line > 0 ? `${item.spec}:${item.line}` : item.spec;
296
391
  }
297
- /** One request as the note writes it: `POST /api/articles/x/favorite → 401`. */
298
- function requestLine(request) {
392
+ /** One body as the note writes it: one line, bounded. */
393
+ function bodyLine(text, max) {
394
+ const oneLine = text.replace(/\s+/g, ' ').trim();
395
+ return oneLine.length > max ? `${oneLine.slice(0, max)}…` : oneLine;
396
+ }
397
+ /** One request named the way the note names it — `GET /api/tags`, its URL bounded. */
398
+ function requestLabel(request) {
299
399
  const url = request.path.length > TRACE_NOTE_URL_CHARS
300
400
  ? `${request.path.slice(0, TRACE_NOTE_URL_CHARS)}…`
301
401
  : request.path;
302
- return `${request.method} ${url} → ${request.status === 0 ? 'no response' : request.status}`;
402
+ return `${request.method} ${url}`;
403
+ }
404
+ /**
405
+ * One request as the note writes it: `POST /api/articles/x/favorite → 401`, plus what a 4xx
406
+ * request sent and got — `POST /api/users → 422 sent {"data":{…}} got {"errors":{…}}`.
407
+ */
408
+ function requestLine(request) {
409
+ // Playwright records `-1` for every request it never got a response for, so `-1` is never a
410
+ // status to print: a request that failed without one carries its own error text, and one with
411
+ // neither was still in flight when the test ended (xr-molecule-flash-m112 +30:45).
412
+ const status = request.status > 0
413
+ ? String(request.status)
414
+ : request.failure
415
+ ? `no response (${bodyLine(redactSecrets(request.failure), TRACE_NOTE_FAILURE_CHARS)})`
416
+ : 'no response';
417
+ const head = `${requestLabel(request)} → ${status}`;
418
+ // The body a page sends to its own API can carry a credential (a registration's password, a
419
+ // token in a form) — the note is egress, so it goes through the same redactor command output
420
+ // does. The parsed trace keeps the body; only the text the model reads is masked.
421
+ const sent = request.sent
422
+ ? ` sent ${bodyLine(redactSecrets(request.sent), TRACE_NOTE_SENT_CHARS)}`
423
+ : '';
424
+ const got = request.got
425
+ ? ` got ${bodyLine(redactSecrets(request.got), TRACE_NOTE_GOT_CHARS)}`
426
+ : '';
427
+ return `${head}${sent}${got}`;
428
+ }
429
+ /** Whether a sent body is an object whose only top-level key is `data` — the wrapped-body trap. */
430
+ function wrappedInData(text) {
431
+ try {
432
+ const parsed = JSON.parse(text);
433
+ return (typeof parsed === 'object' &&
434
+ parsed !== null &&
435
+ !Array.isArray(parsed) &&
436
+ Object.keys(parsed).length === 1 &&
437
+ 'data' in parsed);
438
+ }
439
+ catch (_error) {
440
+ // The body was cut at MAX_TRACED_BODY_CHARS, so it need not parse; the wrapper is still the
441
+ // first thing in it — `{"data":…}` with no other top-level key before the cut.
442
+ return /^\{\s*"data"\s*:/.test(text.trim());
443
+ }
444
+ }
445
+ /** Whether a trace recorded a request the server actually failed: a 4xx/5xx, or a network error. */
446
+ function failedRequest(request) {
447
+ return request.status >= 400 || (request.status <= 0 && request.failure !== undefined);
448
+ }
449
+ /**
450
+ * Whether a trace recorded NO response and no reason for it — a request that was aborted or still
451
+ * in flight when the test ended. It is not evidence that the server failed (m112 +30:45).
452
+ */
453
+ function unansweredRequest(request) {
454
+ return request.status <= 0 && request.failure === undefined;
455
+ }
456
+ /**
457
+ * How far a trace's last recorded step can sit after an earlier walk's failing test and still be
458
+ * that test. The trace names the LINE of the last step it recorded (the click, the wait), while the
459
+ * run's own summary names the test's DECLARATION line, so a row that repeats a failure sits a few
460
+ * lines below the declaration it repeats: m112's favorite test is declared at
461
+ * `articles.spec.ts:123` and its last step, the click that changed nothing, was on `:133`.
462
+ */
463
+ const RECURRENCE_LINE_WINDOW = 40;
464
+ /** A `file:line` split, with a leading `./` dropped: the two readers spell the same file apart. */
465
+ function splitLocation(location) {
466
+ const trimmed = location.trim().replace(/^\.\//, '');
467
+ const match = /^(.*):(\d+)$/.exec(trimmed);
468
+ if (match === null)
469
+ return { file: trimmed, line: 0 };
470
+ return { file: match[1] ?? trimmed, line: Number(match[2]) };
471
+ }
472
+ /** Whether two locations name the same file — one may be absolute where the other is relative. */
473
+ function sameLocationFile(left, right) {
474
+ return left === right || left.endsWith(`/${right}`) || right.endsWith(`/${left}`);
475
+ }
476
+ /**
477
+ * Whether this trace's test is one an EARLIER walk of the suite failed.
478
+ *
479
+ * The earlier walk's locations come from the run's own summary — the test's DECLARATION line —
480
+ * while the trace names the last step's line, so a repeat is the same file with its step inside
481
+ * {@link RECURRENCE_LINE_WINDOW} lines after the NEAREST earlier declaration before it (the test
482
+ * body it belongs to). A line BEFORE the declaration is a hook, not the test: m112's
483
+ * `url-navigation.spec.ts:107` is the Pagination suite's `afterEach`, which four different tests
484
+ * share. A trace that names no spec cannot be placed at all.
485
+ *
486
+ * @param item - The trace.
487
+ * @param earlier - The failing `file:line`s earlier walks of the suite left.
488
+ * @returns True when the trace repeats one of them.
489
+ */
490
+ function repeatsEarlierFailure(item, earlier) {
491
+ if (item.spec === null)
492
+ return false;
493
+ for (const location of earlier) {
494
+ const before = splitLocation(location);
495
+ if (!sameLocationFile(item.spec, before.file))
496
+ continue;
497
+ // Either side without a line is the same file alone, which is all that can be compared.
498
+ if (item.line === 0 || before.line === 0)
499
+ return true;
500
+ if (before.line <= item.line && item.line - before.line <= RECURRENCE_LINE_WINDOW)
501
+ return true;
502
+ }
503
+ return false;
504
+ }
505
+ /**
506
+ * Whether a trace holds evidence the note can explain: a request that got a RECORDED response, a
507
+ * real failure (4xx/5xx or a network error), or a console error. A request with no response is
508
+ * not evidence (m112 +53:46): four of the note's five rows were traces of one `afterEach` line
509
+ * whose every request was `-1`, so the favorite cause — repeated in all three walks — stayed
510
+ * hidden in "… and 7 more traced failures".
511
+ */
512
+ function traceRowEvidence(item) {
513
+ return item.console.length > 0 || item.requests.some((request) => !unansweredRequest(request));
514
+ }
515
+ /**
516
+ * The note's row order: a row with a REAL failure first, then a row whose failure an earlier walk
517
+ * also had, then the rest. A recurrence is the only evidence a row carries when its requests were
518
+ * all answered (m112's favorite cause: the click's own request answered, the button never
519
+ * changed).
520
+ */
521
+ function traceRowTier(item, earlier) {
522
+ if (item.requests.some(failedRequest))
523
+ return 0;
524
+ if (repeatsEarlierFailure(item, earlier))
525
+ return 1;
526
+ return 2;
303
527
  }
304
528
  /**
305
529
  * The note a failed suite run's result carries: what each failing test's page actually sent, and
@@ -311,20 +535,75 @@ function requestLine(request) {
311
535
  * {@link MAX_TRACE_NOTE_TESTS} tests with {@link MAX_TRACE_NOTE_REQUESTS} requests each, because it
312
536
  * rides in a result the model pays for.
313
537
  *
538
+ * The three kinds are kept apart, because they mean different things. A FAILED request (4xx/5xx, or
539
+ * a network error with Playwright's own text) is a cause. A request with NO RESPONSE RECORDED —
540
+ * Playwright's `-1`, no error text — was still in flight when the test ended (the page closed under
541
+ * it); listing it as a failure sent a run hunting an API that never answered anything wrong
542
+ * (xr-molecule-flash-m112 +30:45). The rest are the page's last answered requests.
543
+ *
544
+ * A 4xx request carries what the page SENT beside its status, so a wrapped body is visible in one
545
+ * line rather than inferred from the status; when one of those bodies is an object whose only key
546
+ * is `data`, the note names the call shape that produces it (xr-molecule-flash-m111 +11:36).
547
+ *
548
+ * WHICH rows it spends is evidence, not artifact order (xr-molecule-flash-m112 +53:46): a row
549
+ * needs a recorded response, a real failure or a console error ({@link traceRowEvidence}), rows are
550
+ * deduplicated by the test's location, and they are ordered real failure → repeated from an
551
+ * earlier walk → the rest ({@link traceRowTier}). Without that the note spent all five rows on four
552
+ * traces of one `afterEach` line whose every request was `-1`, and left the favorite cause — the
553
+ * one failure that repeated in every walk — in its "… and N more".
554
+ *
314
555
  * @param items - The traces {@link parseTraceArtifacts} read.
315
556
  * @param target - Where the run went — the deployed host, as the note names it.
316
- * @returns The note, or null when no trace held a request or a console error.
557
+ * @param earlierFailures - The failing `file:line`s earlier walks of this suite left, so a repeat
558
+ * is ranked above a one-off. Empty when the caller knows of no earlier walk.
559
+ * @returns The note, or null when no trace held a recorded response, a real failure or a console
560
+ * error.
317
561
  */
318
- export function failureTraceNote(items, target) {
319
- const withEvidence = items.filter((item) => item.requests.length > 0 || item.console.length > 0);
320
- if (withEvidence.length === 0)
562
+ export function failureTraceNote(items, target, earlierFailures = []) {
563
+ const candidates = items.filter(traceRowEvidence);
564
+ if (candidates.length === 0)
321
565
  return null;
566
+ // Ranked, then deduplicated BY LOCATION — in that order, so the row kept for a location is the
567
+ // one carrying the strongest evidence (m112's duplicate `afterEach` rows).
568
+ const ranked = candidates
569
+ .map((item, index) => ({ item, index, tier: traceRowTier(item, earlierFailures) }))
570
+ .sort((left, right) => left.tier - right.tier || left.index - right.index);
571
+ const traced = [];
572
+ const seen = new Set();
573
+ for (const { item, index } of ranked) {
574
+ // A trace that names no spec cannot be placed; it is never collapsed onto another one.
575
+ const where = item.spec === null ? `#${index}` : whereOf(item);
576
+ if (seen.has(where))
577
+ continue;
578
+ seen.add(where);
579
+ traced.push(item);
580
+ if (traced.length === MAX_TRACE_NOTE_TESTS)
581
+ break;
582
+ }
583
+ const wrapped = traced.some((item) => item.requests.some((request) => request.sent !== undefined && wrappedInData(request.sent)));
584
+ // The "one of these requests failing" answer is only honest when a listed request really failed.
585
+ const anyFailed = traced.some((item) => item.requests.some(failedRequest));
322
586
  const rows = [];
323
- for (const [index, item] of withEvidence.slice(0, MAX_TRACE_NOTE_TESTS).entries()) {
324
- const requests = item.requests.slice(-MAX_TRACE_NOTE_REQUESTS).map(requestLine);
587
+ for (const [index, item] of traced.entries()) {
588
+ const failed = item.requests.filter(failedRequest);
589
+ const unanswered = item.requests.filter(unansweredRequest);
590
+ const answered = item.requests.filter((request) => !failedRequest(request) && !unansweredRequest(request));
591
+ let room = MAX_TRACE_NOTE_REQUESTS;
325
592
  const parts = [];
326
- if (requests.length > 0)
327
- parts.push(requests.join('; '));
593
+ if (failed.length > 0) {
594
+ const shown = failed.slice(0, room);
595
+ room -= shown.length;
596
+ parts.push(`failed: ${shown.map(requestLine).join('; ')}`);
597
+ }
598
+ if (unanswered.length > 0 && room > 0) {
599
+ const shown = unanswered.slice(0, room);
600
+ room -= shown.length;
601
+ parts.push(`no response recorded — still in flight when the test ended, so not a server failure: ` +
602
+ shown.map(requestLabel).join('; '));
603
+ }
604
+ if (answered.length > 0 && room > 0) {
605
+ parts.push(`answered: ${answered.slice(0, room).map(requestLine).join('; ')}`);
606
+ }
328
607
  if (item.console.length > 0)
329
608
  parts.push(`console: ${item.console
330
609
  .slice(0, 2)
@@ -332,14 +611,27 @@ export function failureTraceNote(items, target) {
332
611
  .join(' | ')}`);
333
612
  rows.push(` ${index + 1}. ${whereOf(item)} — ${parts.join(' — ')}`);
334
613
  }
335
- const more = withEvidence.length > MAX_TRACE_NOTE_TESTS
336
- ? `\n … and ${withEvidence.length - MAX_TRACE_NOTE_TESTS} more traced failures`
614
+ // Distinct locations the note did not name: the repeats collapse, so the count is what is left
615
+ // to look at, not how many zips Playwright wrote.
616
+ const distinct = new Set(candidates.map((item, index) => (item.spec === null ? `#${index}` : whereOf(item))));
617
+ const more = distinct.size > traced.length
618
+ ? `\n … and ${distinct.size - traced.length} more traced failures`
337
619
  : '';
338
620
  return (`The platform read the failing tests' own Playwright traces from the run against ${target} ` +
339
- `(the last requests each page made and what came back):\n${rows.join('\n')}${more}\n` +
340
- 'A click that changed nothing is usually one of these requests failing, not the selector: ' +
341
- 'read the handler this route reaches in your own source, and stop `catch`-ing the request of a ' +
342
- 'user action silently — log it.');
621
+ `(what each page sent and got: the requests that failed, the ones that got no response, and ` +
622
+ `its last answered ones):\n` +
623
+ `${rows.join('\n')}${more}\n` +
624
+ (anyFailed
625
+ ? 'A click that changed nothing is usually one of these requests failing, not the selector: ' +
626
+ 'read the handler this route reaches in your own source, and stop `catch`-ing the request of a ' +
627
+ 'user action silently — log it.'
628
+ : 'No listed request failed — none got a 4xx/5xx or a network error — so the cause is in the ' +
629
+ 'page, not the API: read the state the failing test waited on.') +
630
+ (wrapped
631
+ ? ' The API received a body wrapped in `data`: app-http’s `post(url, data, config)` / ' +
632
+ '`put(url, data, config)` / `patch(url, data, config)` take the body as their SECOND ' +
633
+ "argument — `post('/users', { user })`, not `post('/users', { data: { user } })`."
634
+ : ''));
343
635
  }
344
636
  /** The Playwright trace modes that leave a zip behind for a FAILING test, whatever the retries. */
345
637
  const RECORDING_TRACE_MODES = ['on', 'retain-on-failure'];
@@ -4,6 +4,7 @@
4
4
  *
5
5
  * @module
6
6
  */
7
+ import { type RunFailureKind } from './failure-kind.js';
7
8
  /**
8
9
  * Shell-safe quoting using single quotes. Unlike JSON.stringify (double quotes),
9
10
  * single-quoted strings prevent command substitution ($(), backticks) and variable expansion.
@@ -906,6 +907,20 @@ export interface FailureCause {
906
907
  * for a duration group, whose blocks carried no bodies.
907
908
  */
908
909
  evidence: string[];
910
+ /**
911
+ * The suite's own per-test budget in ms, when the cause is the test-budget kill
912
+ * (`Test timeout of 15000ms exceeded`). The signature normalizes the number away
913
+ * ({@link normalizedSignature}) so equal causes group, and that number is the one fact that
914
+ * predicts a test spending its budget on a repeated action (xr-molecule-flash-m110 +20:44).
915
+ */
916
+ budgetMs?: number;
917
+ /**
918
+ * The layer the failure came from ({@link classifyRunFailure}). A `connection` cause never
919
+ * reached the app, so it is NOT a cause the app's code can fix: the note says to re-run it
920
+ * once and read the app's log on a repeat, instead of sending a fix at a handler
921
+ * (xr-molecule-flash-m110 +18:42 rewrote a CORS middleware for a 20 s connection timeout).
922
+ */
923
+ kind: RunFailureKind;
909
924
  }
910
925
  /**
911
926
  * Every failure a run names, grouped by the cause behind it — one group per cause, with the
@@ -949,17 +964,22 @@ export declare function failuresByCause(log: string, excluded?: readonly string[
949
964
  * exactly those tests ({@link rerunOfRunningSuite}) instead of leaving the executor to re-run the
950
965
  * whole suite.
951
966
  *
967
+ * A run whose failures are ALL {@link NO_BASE_URL_ERROR} is not a set of causes at all: it is one
968
+ * configuration failure, said in one line ({@link noBaseUrlConfigNote}) rather than a list that
969
+ * reads as ten defects in the app.
970
+ *
952
971
  * @param log - The finished run's output.
953
972
  * @param excluded - The test titles the request carves out, if the caller knows them.
954
973
  * @param run - How the run was started, when the caller knows it: the narrowed re-run is built
955
- * from `command`/`cwd`, and `log` (the path the output was written to) is what the "left out"
956
- * sentence greps when more causes exist than the result has room for.
974
+ * from `command`/`cwd`/`config`, and `log` (the path the output was written to) is what the
975
+ * "left out" sentence greps when more causes exist than the result has room for.
957
976
  * @returns The note, or null.
958
977
  */
959
978
  export declare function failureCausesNote(log: string, excluded?: readonly string[], run?: {
960
979
  command: string;
961
980
  cwd?: string;
962
981
  log?: string;
982
+ config?: string | null;
963
983
  }): string | null;
964
984
  /**
965
985
  * The command that re-runs the tests at `locations` the way the run IN FLIGHT ran them: the same
@@ -973,8 +993,8 @@ export declare function failureCausesNote(log: string, excluded?: readonly strin
973
993
  * the same app and database, and that is exactly what is refused while the run walks
974
994
  * ({@link suiteRunInTheWay}).
975
995
  *
976
- * @param run - The run in flight: the command it was started with, as it was run, and the
977
- * directory that command ran in.
996
+ * @param run - The run in flight: the command it was started with, as it was run, the directory
997
+ * that command ran in, and the config it loaded when the caller knows it.
978
998
  * @param locations - The failing `file:line`s to re-run, as the run's log printed them.
979
999
  * @param excluded - The test titles the request carves out: the re-run inverts them out
980
1000
  * (`--grep-invert`) so it cannot report them again.
@@ -983,6 +1003,7 @@ export declare function failureCausesNote(log: string, excluded?: readonly strin
983
1003
  export declare function rerunOfRunningSuite(run: {
984
1004
  command: string;
985
1005
  cwd?: string;
1006
+ config?: string | null;
986
1007
  }, locations: readonly string[], excluded?: readonly string[]): string | null;
987
1008
  /**
988
1009
  * The sentence that names a cause repeating among a running suite's failures, mid-run.
@@ -1 +1 @@
1
- {"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../src/utilities.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../src/utilities.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAW3E;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAS5C;AAED;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,GAAI,GAAG,MAAM,KAAG,MAEE,CAAA;AAuGhD;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAK/C;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAErD;AAKD;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAsCzD;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGnD;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,GAAG,MAAM,CAUpF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAElE;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWnE;AAuDD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,8BAA8B,0CAA0C,CAAA;AAErF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG;IAC3D,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB,CAuJA;AAsBD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAK1D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAG/D;AAmID,wEAAwE;AACxE,MAAM,WAAW,gBAAgB;IAC/B,0BAA0B;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,6FAA6F;IAC7F,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAoB3E;AAED,oFAAoF;AACpF,MAAM,WAAW,yBAAyB;IACxC;;;OAGG;IACH,KAAK,EAAE,OAAO,CAAA;IACd;;;;;OAKG;IACH,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,yBAAyB,CAkBpF;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEjE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GACb;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAalC;AA4BD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAuBlE;AAID;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAQrE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAOxE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAQpD;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGvD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGpD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAU/D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAGxD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,GACX,MAAM,CAKR;AAkCD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAIvE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAU1E;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI9E;AAID,yCAAyC;AACzC,eAAO,MAAM,aAAa,QAAkB,CAAA;AAC5C,8CAA8C;AAC9C,eAAO,MAAM,cAAc,QAAmB,CAAA;AAC9C,kDAAkD;AAClD,eAAO,MAAM,eAAe,QAAa,CAAA;AACzC;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,KAAK,CAAA;AAE1C;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CA6BhG;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAY5E;AAED;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,KAAK,CAAA;AAEtC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,mBAAmB,QAAS,CAAA;AAEzC;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,OAAQ,CAAA;AAE/C;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB,QAAmD,CAAA;AAErF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,QAAwB,CAAA;AAE3D;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,qBAAqB,QAAY,CAAA;AAE9C,qEAAqE;AACrE,eAAO,MAAM,sBAAsB,MAAM,CAAA;AAKzC,6EAA6E;AAC7E,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,SAAa,GAAG,MAAM,GAAG,IAAI,CAyC3E;AAED,0BAA0B;AAC1B,eAAO,MAAM,kBAAkB,KAAK,CAAA;AACpC,wBAAwB;AACxB,eAAO,MAAM,gBAAgB,MAAM,CAAA;AAEnC;;;;;;;GAOG;AACH,eAAO,MAAM,4BAA4B,mHAW/B,CAAA;AAEV;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAG7D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAOnE;AAmFD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAiBjD;AAED,kEAAkE;AAClE,eAAO,MAAM,qBAAqB,KAAK,CAAA;AAEvC,qEAAqE;AACrE,eAAO,MAAM,kBAAkB,OAAO,CAAA;AAEtC;;;GAGG;AACH,eAAO,MAAM,uBAAuB,0KAC4G,CAAA;AAEhJ;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAM3E;AAED,gEAAgE;AAChE,eAAO,MAAM,0BAA0B,QAAS,CAAA;AAEhD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAGrE;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB,IAAI,CAAA;AAEtC,0DAA0D;AAC1D,MAAM,WAAW,UAAU;IACzB,gGAAgG;IAChG,KAAK,EAAE,MAAM,CAAA;IACb,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAA;IACb,qFAAqF;IACrF,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CACvB;AAED,0DAA0D;AAC1D,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAA;IACb,0FAA0F;IAC1F,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,sFAAsF;IACtF,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,sDAAsD;IACtD,KAAK,EAAE,UAAU,EAAE,CAAA;CACpB;AAiSD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CA+CrE;AAED,2FAA2F;AAC3F,eAAO,MAAM,wBAAwB,IAAI,CAAA;AAEzC;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,OAAO,CAAA;AAErC;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,QAA2B,CAAA;AAE5D;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,KAAK,CAAA;AAKvC,oFAAoF;AACpF,eAAO,MAAM,0BAA0B,IAAI,CAAA;AA8D3C;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,IAAI,CAAA;AAEtC,oFAAoF;AACpF,MAAM,WAAW,cAAc;IAC7B,wCAAwC;IACxC,KAAK,EAAE,MAAM,CAAA;IACb,6EAA6E;IAC7E,KAAK,EAAE,MAAM,EAAE,CAAA;CAChB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,GAAG,cAAc,GAAG,IAAI,CAiB1F;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,gBAAgB,EACvB,SAAS,EAAE,cAAc,GAAG,IAAI,GAAG,SAAS,GAC3C,cAAc,GAAG,IAAI,CAGvB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAQ7F;AAED,2DAA2D;AAC3D,MAAM,WAAW,YAAY;IAC3B,gGAAgG;IAChG,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,yFAAyF;IACzF,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,kEAAkE;IAClE,KAAK,EAAE,UAAU,EAAE,CAAA;IACnB;;;;OAIG;IACH,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;OAKG;IACH,IAAI,EAAE,cAAc,CAAA;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,GAAE,SAAS,MAAM,EAAO,GAAG,YAAY,EAAE,CAE7F;AAwMD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,MAAM,EACX,QAAQ,GAAE,SAAS,MAAM,EAAO,EAChC,GAAG,CAAC,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC5E,MAAM,GAAG,IAAI,CAoEf;AA4FD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EAC9D,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,QAAQ,GAAE,SAAS,MAAM,EAAO,GAC/B,MAAM,GAAG,IAAI,CAOf;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,gBAAgB,EACvB,CAAC,EAAE;IACD,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,CAAC,EAAE,cAAc,GAAG,IAAI,CAAA;IACjC,GAAG,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CACxC,GACA,MAAM,CAkGR;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,MAAM,GAAG,IAAI,CAsCf;AAED,qEAAqE;AACrE,eAAO,MAAM,eAAe,QAA0C,CAAA;AAEtE;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAgB7D;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE;IACxC,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;CACjB,GAAG,MAAM,GAAG,IAAI,CAKhB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CACvB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAChB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAgBvC"}