@sungen/driver-ui 3.2.35 → 3.3.0-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/dist/app-profile-presets.d.ts +12 -0
  2. package/dist/app-profile-presets.d.ts.map +1 -0
  3. package/dist/app-profile-presets.js +85 -0
  4. package/dist/app-profile-presets.js.map +1 -0
  5. package/dist/index.d.ts +37 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +93 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/patterns/assertion-patterns.d.ts +13 -0
  10. package/dist/patterns/assertion-patterns.d.ts.map +1 -0
  11. package/dist/patterns/assertion-patterns.js +903 -0
  12. package/dist/patterns/assertion-patterns.js.map +1 -0
  13. package/dist/patterns/capture-patterns.d.ts +21 -0
  14. package/dist/patterns/capture-patterns.d.ts.map +1 -0
  15. package/dist/patterns/capture-patterns.js +174 -0
  16. package/dist/patterns/capture-patterns.js.map +1 -0
  17. package/dist/patterns/focus-patterns.d.ts +22 -0
  18. package/dist/patterns/focus-patterns.d.ts.map +1 -0
  19. package/dist/patterns/focus-patterns.js +79 -0
  20. package/dist/patterns/focus-patterns.js.map +1 -0
  21. package/dist/patterns/form-patterns.d.ts +6 -0
  22. package/dist/patterns/form-patterns.d.ts.map +1 -0
  23. package/dist/patterns/form-patterns.js +239 -0
  24. package/dist/patterns/form-patterns.js.map +1 -0
  25. package/dist/patterns/interaction-patterns.d.ts +20 -0
  26. package/dist/patterns/interaction-patterns.d.ts.map +1 -0
  27. package/dist/patterns/interaction-patterns.js +633 -0
  28. package/dist/patterns/interaction-patterns.js.map +1 -0
  29. package/dist/patterns/keyboard-patterns.d.ts +7 -0
  30. package/dist/patterns/keyboard-patterns.d.ts.map +1 -0
  31. package/dist/patterns/keyboard-patterns.js +57 -0
  32. package/dist/patterns/keyboard-patterns.js.map +1 -0
  33. package/dist/patterns/navigation-patterns.d.ts +6 -0
  34. package/dist/patterns/navigation-patterns.d.ts.map +1 -0
  35. package/dist/patterns/navigation-patterns.js +185 -0
  36. package/dist/patterns/navigation-patterns.js.map +1 -0
  37. package/dist/patterns/scope-patterns.d.ts +7 -0
  38. package/dist/patterns/scope-patterns.d.ts.map +1 -0
  39. package/dist/patterns/scope-patterns.js +36 -0
  40. package/dist/patterns/scope-patterns.js.map +1 -0
  41. package/dist/patterns/scroll-patterns.d.ts +7 -0
  42. package/dist/patterns/scroll-patterns.d.ts.map +1 -0
  43. package/dist/patterns/scroll-patterns.js +25 -0
  44. package/dist/patterns/scroll-patterns.js.map +1 -0
  45. package/dist/patterns/setup-patterns.d.ts +6 -0
  46. package/dist/patterns/setup-patterns.d.ts.map +1 -0
  47. package/dist/patterns/setup-patterns.js +75 -0
  48. package/dist/patterns/setup-patterns.js.map +1 -0
  49. package/dist/patterns/storage-patterns.d.ts +26 -0
  50. package/dist/patterns/storage-patterns.d.ts.map +1 -0
  51. package/dist/patterns/storage-patterns.js +142 -0
  52. package/dist/patterns/storage-patterns.js.map +1 -0
  53. package/dist/patterns/table-pagination-patterns.d.ts +42 -0
  54. package/dist/patterns/table-pagination-patterns.d.ts.map +1 -0
  55. package/dist/patterns/table-pagination-patterns.js +93 -0
  56. package/dist/patterns/table-pagination-patterns.js.map +1 -0
  57. package/dist/patterns/table-patterns.d.ts +19 -0
  58. package/dist/patterns/table-patterns.d.ts.map +1 -0
  59. package/dist/patterns/table-patterns.js +594 -0
  60. package/dist/patterns/table-patterns.js.map +1 -0
  61. package/dist/trace-identity/aria-snapshot-head.d.ts +23 -0
  62. package/dist/trace-identity/aria-snapshot-head.d.ts.map +1 -0
  63. package/dist/trace-identity/aria-snapshot-head.js +26 -0
  64. package/dist/trace-identity/aria-snapshot-head.js.map +1 -0
  65. package/dist/trace-identity/playwright-identity-resolver.d.ts +10 -0
  66. package/dist/trace-identity/playwright-identity-resolver.d.ts.map +1 -0
  67. package/dist/trace-identity/playwright-identity-resolver.js +130 -0
  68. package/dist/trace-identity/playwright-identity-resolver.js.map +1 -0
  69. package/dist/trace-identity/trace-run-record-reporter.d.ts +50 -0
  70. package/dist/trace-identity/trace-run-record-reporter.d.ts.map +1 -0
  71. package/dist/trace-identity/trace-run-record-reporter.js +146 -0
  72. package/dist/trace-identity/trace-run-record-reporter.js.map +1 -0
  73. package/dist/utils/column-header.d.ts +37 -0
  74. package/dist/utils/column-header.d.ts.map +1 -0
  75. package/dist/utils/column-header.js +46 -0
  76. package/dist/utils/column-header.js.map +1 -0
  77. package/dist/utils/row-index.d.ts +18 -0
  78. package/dist/utils/row-index.d.ts.map +1 -0
  79. package/dist/utils/row-index.js +39 -0
  80. package/dist/utils/row-index.js.map +1 -0
  81. package/dist/utils/url-path-regex.d.ts +45 -0
  82. package/dist/utils/url-path-regex.d.ts.map +1 -0
  83. package/dist/utils/url-path-regex.js +68 -0
  84. package/dist/utils/url-path-regex.js.map +1 -0
  85. package/package.json +9 -3
  86. package/src/index.ts +5 -0
  87. package/src/patterns/assertion-patterns.ts +10 -88
  88. package/src/patterns/form-patterns.ts +2 -47
  89. package/src/patterns/table-patterns.ts +3 -15
  90. package/src/trace-identity/aria-snapshot-head.ts +33 -0
  91. package/src/trace-identity/playwright-identity-resolver.ts +123 -0
  92. package/src/trace-identity/trace-run-record-reporter.ts +131 -0
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The head line of a Playwright `locator.ariaSnapshot()` → role, accessible name, heading level.
3
+ *
4
+ * Owned here on purpose (G5): the spike loaded Playwright's private aria-YAML parser by absolute
5
+ * path, which breaks on any internal move. We only ever need the FIRST node of a single element's
6
+ * snapshot, whose grammar is small and documented in Playwright's aria snapshot docs:
7
+ *
8
+ * - button "Login"
9
+ * - heading "My Account" [level=1]
10
+ * - textbox "Email": test@playground.dev (a value after the colon)
11
+ * - link "Products": (children on the next lines)
12
+ * - text: Hi, Jane (a bare text node — no role)
13
+ *
14
+ * More than one top-level node (a fragment) has no single identity.
15
+ */
16
+ export interface AriaHead {
17
+ role: string;
18
+ name: string;
19
+ level?: number;
20
+ note?: string;
21
+ }
22
+ export declare function parseAriaHead(yaml: string): AriaHead;
23
+ //# sourceMappingURL=aria-snapshot-head.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aria-snapshot-head.d.ts","sourceRoot":"","sources":["../../src/trace-identity/aria-snapshot-head.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,QAAQ;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE;AAQvF,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CASpD"}
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseAriaHead = parseAriaHead;
4
+ const LINE = /^- ([a-zA-Z]+)(?: "((?:[^"\\]|\\.)*)")?((?: \[[^\]]*\])*)(?::.*)?$/;
5
+ function unescape(raw) {
6
+ try {
7
+ return JSON.parse(`"${raw}"`);
8
+ }
9
+ catch {
10
+ return raw.replace(/\\"/g, '"');
11
+ }
12
+ }
13
+ function parseAriaHead(yaml) {
14
+ const top = yaml.split(/\r?\n/).filter((l) => l.startsWith('- '));
15
+ if (top.length !== 1)
16
+ return { role: 'generic', name: '', note: `fragment of ${top.length}` };
17
+ const m = LINE.exec(top[0].trimEnd());
18
+ if (!m)
19
+ return { role: 'generic', name: '', note: `unparsed: ${top[0].slice(0, 60)}` };
20
+ const role = m[1];
21
+ if (role === 'text')
22
+ return { role: 'text', name: '', note: 'text node' };
23
+ const level = /\[level=(\d)\]/.exec(m[3] ?? '')?.[1];
24
+ return { role, name: m[2] === undefined ? '' : unescape(m[2]), ...(level ? { level: Number(level) } : {}) };
25
+ }
26
+ //# sourceMappingURL=aria-snapshot-head.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aria-snapshot-head.js","sourceRoot":"","sources":["../../src/trace-identity/aria-snapshot-head.ts"],"names":[],"mappings":";;AAuBA,sCASC;AAfD,MAAM,IAAI,GAAG,oEAAoE,CAAC;AAElF,SAAS,QAAQ,CAAC,GAAW;IAC3B,IAAI,CAAC;QAAC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,CAAW,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAAC,CAAC;AAC7F,CAAC;AAED,SAAgB,aAAa,CAAC,IAAY;IACxC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;IAClE,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,eAAe,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC;IAC9F,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IACtC,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;IACvF,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAClB,IAAI,IAAI,KAAK,MAAM;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;IAC1E,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACrD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AAC9G,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { CapabilityDescriptor } from '@sun-asterisk/sungen';
2
+ type Resolver = NonNullable<CapabilityDescriptor['traceIdentity']>;
3
+ type Input = Parameters<Resolver>[0];
4
+ type Output = Awaited<ReturnType<Resolver>>;
5
+ export declare const RESOLVER_ID = "driver-ui/playwright-aria@1";
6
+ /** At most this many snapshots per call — a bound on work, not a sampling. */
7
+ export declare const MAX_REQUESTS = 2000;
8
+ export declare function resolveTraceIdentities(input: Input): Promise<Output>;
9
+ export {};
10
+ //# sourceMappingURL=playwright-identity-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"playwright-identity-resolver.d.ts","sourceRoot":"","sources":["../../src/trace-identity/playwright-identity-resolver.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAGjE,KAAK,QAAQ,GAAG,WAAW,CAAC,oBAAoB,CAAC,eAAe,CAAC,CAAC,CAAC;AACnE,KAAK,KAAK,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;AACrC,KAAK,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;AAM5C,eAAO,MAAM,WAAW,gCAAgC,CAAC;AAEzD,8EAA8E;AAC9E,eAAO,MAAM,YAAY,OAAO,CAAC;AAkEjC,wBAAsB,sBAAsB,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAyB1E"}
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MAX_REQUESTS = exports.RESOLVER_ID = void 0;
4
+ exports.resolveTraceIdentities = resolveTraceIdentities;
5
+ /**
6
+ * `traceIdentity` for the UI capability (SPI §4.5, DOS-14.5): who a recorded action was aimed at,
7
+ * from Playwright's OWN accessibility engine on the replayed snapshot — the phase 07 method that
8
+ * agreed with the test's locator on 98/98 oracle-bearing actions over two real sites.
9
+ *
10
+ * replay the inert HTML core rendered, in a context with JavaScript disabled, offline,
11
+ * service workers blocked, every request routed to abort AND Chromium's network
12
+ * blocklist set to `*` (non-http schemes too) — nothing leaves the machine
13
+ * identity role + name from `ariaSnapshot()` of the element Playwright marked, re-resolved with
14
+ * `getByRole(role, { name, exact })`: it must land on exactly that element
15
+ * G3 if role+name matches several elements, look for a named scope (row, landmark,
16
+ * dialog, card…) inside which it is unique; none → `low` (ambiguous)
17
+ * low any reason at all: core's caps (missing CSS, broken refs), no semantic role, empty
18
+ * name, getByRole missing the target, ambiguity, a failed snapshot
19
+ * A missing browser is an ERROR, never a silent `low` for everything.
20
+ */
21
+ const test_1 = require("@playwright/test");
22
+ const aria_snapshot_head_1 = require("./aria-snapshot-head");
23
+ const NAMELESS = new Set(['generic', 'none', 'presentation', 'text', 'fragment', 'paragraph']);
24
+ const SCOPE_ROLES = ['row', 'listitem', 'article', 'dialog', 'alertdialog', 'form', 'group', 'tabpanel', 'region', 'navigation', 'complementary', 'search', 'banner', 'contentinfo', 'main', 'menu', 'list', 'table', 'grid'];
25
+ exports.RESOLVER_ID = 'driver-ui/playwright-aria@1';
26
+ /** At most this many snapshots per call — a bound on work, not a sampling. */
27
+ exports.MAX_REQUESTS = 2000;
28
+ async function launch() {
29
+ try {
30
+ return await test_1.chromium.launch({ headless: true });
31
+ }
32
+ catch (e) {
33
+ throw new Error(`trace identity needs Chromium for Playwright — run \`npx playwright install chromium\` (${e.message.split('\n')[0]})`);
34
+ }
35
+ }
36
+ /** A fresh page in a fresh isolated context: JS off, offline, SW blocked, every request aborted, CDP blocklist `*`. */
37
+ async function isolatedPage(browser, counter) {
38
+ const context = await browser.newContext({ javaScriptEnabled: false, offline: true, serviceWorkers: 'block' });
39
+ await context.route('**/*', (route) => route.abort('blockedbyclient'));
40
+ const page = await context.newPage();
41
+ page.on('request', () => { counter.blocked++; });
42
+ const cdp = await context.newCDPSession(page);
43
+ await cdp.send('Network.enable');
44
+ await cdp.send('Network.setBlockedURLs', { urls: ['*'] });
45
+ return page;
46
+ }
47
+ const byRole = (root, role, name, level) => root.getByRole(role, { ...(name ? { name, exact: true } : {}), ...(level ? { level } : {}) });
48
+ /** A named container in which role+name is unique and lands on the target (G3). */
49
+ async function scopeFor(page, target, role, name, level) {
50
+ for (const scopeRole of SCOPE_ROLES) {
51
+ const holders = byRole(page, scopeRole, '').filter({ has: target });
52
+ const n = await holders.count();
53
+ if (!n)
54
+ continue;
55
+ const head = (0, aria_snapshot_head_1.parseAriaHead)((await holders.nth(n - 1).ariaSnapshot({ timeout: 3000 }).catch(() => '')).split('\n')[0]);
56
+ if (!head.name)
57
+ continue;
58
+ const scoped = byRole(byRole(page, scopeRole, head.name), role, name, level);
59
+ if ((await scoped.count()) === 1 && (await scoped.and(target).count()) === 1)
60
+ return { role: scopeRole, name: head.name };
61
+ }
62
+ return undefined;
63
+ }
64
+ const lowResult = (key, reasons) => ({ key, role: 'generic', name: '', identityConfidence: 'low', reasons });
65
+ async function identify(page, req) {
66
+ const low = (reasons) => lowResult(req.key, reasons);
67
+ await page.setViewportSize({ width: Math.max(1, req.viewport.width), height: Math.max(1, req.viewport.height) });
68
+ await page.setContent(req.html, { waitUntil: 'domcontentloaded', timeout: 10000 });
69
+ const target = page.locator(req.targetSelector);
70
+ const marked = await target.count();
71
+ if (marked !== 1)
72
+ return low([...req.capReasons, `marked-elements=${marked}`]);
73
+ let aria;
74
+ try {
75
+ aria = await target.ariaSnapshot({ timeout: 3000 });
76
+ }
77
+ catch (e) {
78
+ return low([...req.capReasons, `ariaSnapshot-failed: ${e.message.split('\n')[0]}`]);
79
+ }
80
+ const head = (0, aria_snapshot_head_1.parseAriaHead)(aria);
81
+ const id = { role: head.role, name: head.name, ...(head.level ? { level: head.level } : {}) };
82
+ const reasons = [...req.capReasons];
83
+ if (NAMELESS.has(head.role))
84
+ reasons.push(`no-semantic-role(${head.role})`);
85
+ if (!head.name)
86
+ reasons.push('empty-name');
87
+ let scope;
88
+ if (!NAMELESS.has(head.role)) {
89
+ const all = byRole(page, head.role, head.name, head.level);
90
+ const count = await all.count();
91
+ if ((await all.and(target).count()) !== 1)
92
+ reasons.push('getByRole-misses-target');
93
+ else if (count > 1) {
94
+ scope = head.name ? await scopeFor(page, target, head.role, head.name, head.level) : undefined;
95
+ if (!scope)
96
+ reasons.push(`ambiguous(${count})`);
97
+ }
98
+ }
99
+ return reasons.length ? { key: req.key, ...id, identityConfidence: 'low', reasons } : { key: req.key, ...id, ...(scope ? { scope } : {}), identityConfidence: 'high', reasons: [] };
100
+ }
101
+ async function resolveTraceIdentities(input) {
102
+ const requests = Array.isArray(input?.requests) ? input.requests : [];
103
+ if (requests.length > exports.MAX_REQUESTS)
104
+ throw new Error(`trace identity: ${requests.length} snapshots in one call (max ${exports.MAX_REQUESTS})`);
105
+ const browser = await launch();
106
+ try {
107
+ const counter = { blocked: 0 };
108
+ let page = await isolatedPage(browser, counter);
109
+ const results = [];
110
+ for (const req of requests) {
111
+ try {
112
+ results.push(await identify(page, req));
113
+ }
114
+ catch (e) {
115
+ // One bad snapshot is a `low` answer for that snapshot, never a crash of the whole check.
116
+ const why = e.message.split('\n')[0].slice(0, 200);
117
+ results.push(lowResult(req.key, [`replay-failed: ${why}`]));
118
+ if (page.isClosed() || /closed/i.test(why)) {
119
+ await page.context().close().catch(() => undefined);
120
+ page = await isolatedPage(browser, counter);
121
+ }
122
+ }
123
+ }
124
+ return { resolver: exports.RESOLVER_ID, browser: `chromium ${browser.version()}`, results, blockedRequests: counter.blocked };
125
+ }
126
+ finally {
127
+ await browser.close();
128
+ }
129
+ }
130
+ //# sourceMappingURL=playwright-identity-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"playwright-identity-resolver.js","sourceRoot":"","sources":["../../src/trace-identity/playwright-identity-resolver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Playwright reporter that records sungen's own RUN RECORD for trace conformance (DOS-14.6, G5).
3
+ *
4
+ * A Playwright trace carries no run id and no hash of the code that produced it, so a good trace of
5
+ * another test — or of the same test before the code was weakened — could be swapped in. This
6
+ * reporter writes, next to the traces it vouches for (`<outputDir>/sungen-run-record[.shard-i-of-n].json`):
7
+ *
8
+ * runId SUNGEN_RUN_ID when set (one id across CI shards), else a fresh UUID
9
+ * specs sha256 of every test file at run START (the code that ran)
10
+ * contract sha256 of every `.feature` and `contracts.lock.yaml` under `qa/` at run START (schema 2,
11
+ * #967): the contract the run saw. A trace proves a contract only while those bytes still
12
+ * hold — compared by content, so a touch, a checkout or a fresh clone stales nothing
13
+ * attempts per test attempt: testId, retry, status, window, and the sha256 of its trace.zip
14
+ * report where the JSON reporter wrote its report — traces are mapped THROUGH that report
15
+ * (merge-reports renames trace files; their content, and so the sha256, stays)
16
+ *
17
+ * The outputDir is wiped at the start of every Playwright run, so a record and its traces always
18
+ * come from the same run. Enable it for free / impl:code tests together with `trace: 'on'`, in a
19
+ * config with its OWN outputDir — the compiled suite clears `test-results/` on every run (#941):
20
+ *
21
+ * outputDir: 'test-results-free',
22
+ * reporter: [['json', { outputFile: 'test-results-free/results.json' }],
23
+ * ['@sungen/driver-ui/dist/trace-identity/trace-run-record-reporter.js']],
24
+ * (the compiled file — Playwright does not transpile TypeScript inside node_modules; `prepack` builds it)
25
+ * use: { trace: 'on' },
26
+ */
27
+ import type { FullConfig, Reporter, Suite, TestCase, TestResult } from '@playwright/test/reporter';
28
+ /**
29
+ * sha256 of every contract file under `<root>/qa` — each `*.feature` and `contracts.lock.yaml` — keyed
30
+ * by its posix path from `root`, sorted. Dot-directories, node_modules and symlinks are not followed.
31
+ */
32
+ export declare function contractContent(root: string): Record<string, string>;
33
+ declare class TraceRunRecordReporter implements Reporter {
34
+ private config?;
35
+ private root;
36
+ private runId;
37
+ private startedAt;
38
+ private specs;
39
+ private contract;
40
+ private results;
41
+ printsToStdio(): boolean;
42
+ onBegin(config: FullConfig, suite: Suite): void;
43
+ onTestEnd(test: TestCase, result: TestResult): void;
44
+ onEnd(): Promise<void>;
45
+ /** The JSON reporter's output file, as the reporter itself resolves it (option, then env). */
46
+ private jsonReportPath;
47
+ private rel;
48
+ }
49
+ export default TraceRunRecordReporter;
50
+ //# sourceMappingURL=trace-run-record-reporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trace-run-record-reporter.d.ts","sourceRoot":"","sources":["../../src/trace-identity/trace-run-record-reporter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAgBnG;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAcpE;AAED,cAAM,sBAAuB,YAAW,QAAQ;IAC9C,OAAO,CAAC,MAAM,CAAC,CAAa;IAC5B,OAAO,CAAC,IAAI,CAAiB;IAC7B,OAAO,CAAC,KAAK,CAA6C;IAC1D,OAAO,CAAC,SAAS,CAA4B;IAC7C,OAAO,CAAC,KAAK,CAA8B;IAC3C,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,OAAO,CAAqD;IAEpE,aAAa,IAAI,OAAO;IAExB,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IAU/C,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI;IAI7C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA2B5B,8FAA8F;IAC9F,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,GAAG;CAIZ;AAED,eAAe,sBAAsB,CAAC"}
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.contractContent = contractContent;
37
+ const node_crypto_1 = require("node:crypto");
38
+ const fs = __importStar(require("node:fs"));
39
+ const path = __importStar(require("node:path"));
40
+ const sha256File = (file) => {
41
+ try {
42
+ return (0, node_crypto_1.createHash)('sha256').update(fs.readFileSync(file)).digest('hex');
43
+ }
44
+ catch {
45
+ return null;
46
+ }
47
+ };
48
+ const posix = (p) => p.split(path.sep).join('/');
49
+ const isContractFile = (name) => name.endsWith('.feature') || name === 'contracts.lock.yaml';
50
+ /**
51
+ * sha256 of every contract file under `<root>/qa` — each `*.feature` and `contracts.lock.yaml` — keyed
52
+ * by its posix path from `root`, sorted. Dot-directories, node_modules and symlinks are not followed.
53
+ */
54
+ function contractContent(root) {
55
+ const found = [];
56
+ const walk = (dir) => {
57
+ let entries;
58
+ try {
59
+ entries = fs.readdirSync(dir, { withFileTypes: true });
60
+ }
61
+ catch {
62
+ return;
63
+ }
64
+ for (const e of entries) {
65
+ const full = path.join(dir, e.name);
66
+ if (e.isDirectory()) {
67
+ if (!e.name.startsWith('.') && e.name !== 'node_modules')
68
+ walk(full);
69
+ continue;
70
+ }
71
+ const sha = e.isFile() && isContractFile(e.name) ? sha256File(full) : null;
72
+ if (sha)
73
+ found.push([posix(path.relative(root, full)), sha]);
74
+ }
75
+ };
76
+ walk(path.join(root, 'qa'));
77
+ return Object.fromEntries(found.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)));
78
+ }
79
+ class TraceRunRecordReporter {
80
+ constructor() {
81
+ this.root = process.cwd();
82
+ this.runId = process.env.SUNGEN_RUN_ID || (0, node_crypto_1.randomUUID)();
83
+ this.startedAt = new Date().toISOString();
84
+ this.specs = {};
85
+ this.contract = {};
86
+ this.results = [];
87
+ }
88
+ printsToStdio() { return false; }
89
+ onBegin(config, suite) {
90
+ this.config = config;
91
+ if (config.configFile)
92
+ this.root = path.dirname(config.configFile);
93
+ this.contract = contractContent(this.root);
94
+ for (const test of suite.allTests()) {
95
+ const rel = this.rel(test.location.file);
96
+ if (this.specs[rel] === undefined)
97
+ this.specs[rel] = sha256File(test.location.file) ?? '';
98
+ }
99
+ }
100
+ onTestEnd(test, result) {
101
+ this.results.push({ test, result });
102
+ }
103
+ async onEnd() {
104
+ if (!this.config)
105
+ return;
106
+ const attempts = this.results.map(({ test, result }) => {
107
+ const trace = result.attachments.find((a) => a.name === 'trace' && a.path);
108
+ const sha = trace?.path ? sha256File(trace.path) : null;
109
+ return {
110
+ testId: test.id, file: this.rel(test.location.file), title: test.title, retry: result.retry, status: result.status,
111
+ startTime: result.startTime.toISOString(), duration: result.duration, trace: sha ? { sha256: sha } : null,
112
+ };
113
+ });
114
+ const shard = this.config.shard;
115
+ const record = {
116
+ schema: 2, tool: 'sungen-trace-run-record', runId: this.runId, startedAt: this.startedAt,
117
+ playwrightVersion: this.config.version, shard: shard ? `${shard.current}/${shard.total}` : null,
118
+ report: this.jsonReportPath(), specs: this.specs, contract: this.contract, attempts,
119
+ };
120
+ const outDir = this.config.projects[0]?.outputDir ?? path.join(this.root, 'test-results');
121
+ const name = shard ? `sungen-run-record.shard-${shard.current}-of-${shard.total}.json` : 'sungen-run-record.json';
122
+ try {
123
+ fs.mkdirSync(outDir, { recursive: true });
124
+ fs.writeFileSync(path.join(outDir, name), JSON.stringify(record, null, 2));
125
+ }
126
+ catch (e) {
127
+ // A missing record makes every free / impl:code test `unproven` — say so instead of hiding it.
128
+ console.error(`[sungen] could not write the trace run record: ${e instanceof Error ? e.message : String(e)}`);
129
+ }
130
+ }
131
+ /** The JSON reporter's output file, as the reporter itself resolves it (option, then env). */
132
+ jsonReportPath() {
133
+ const json = this.config?.reporter.find(([name]) => name === 'json');
134
+ const opt = json && typeof json[1]?.outputFile === 'string' ? json[1].outputFile : undefined;
135
+ const fromEnv = process.env.PLAYWRIGHT_JSON_OUTPUT_FILE
136
+ ?? (process.env.PLAYWRIGHT_JSON_OUTPUT_NAME ? path.join(process.env.PLAYWRIGHT_JSON_OUTPUT_DIR ?? '', process.env.PLAYWRIGHT_JSON_OUTPUT_NAME) : undefined);
137
+ const chosen = opt ? path.resolve(this.root, opt) : fromEnv ? path.resolve(process.cwd(), fromEnv) : undefined;
138
+ return chosen ? this.rel(chosen) : null;
139
+ }
140
+ rel(file) {
141
+ const r = path.relative(this.root, file);
142
+ return posix(r.startsWith('..') || path.isAbsolute(r) ? path.basename(file) : r);
143
+ }
144
+ }
145
+ exports.default = TraceRunRecordReporter;
146
+ //# sourceMappingURL=trace-run-record-reporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trace-run-record-reporter.js","sourceRoot":"","sources":["../../src/trace-identity/trace-run-record-reporter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import { PatternContext } from '@sun-asterisk/sungen';
2
+ /** The `columns:` map as it reaches a pattern from a resolved TABLE entry. */
3
+ interface ColumnsCarrier {
4
+ columns?: Record<string, {
5
+ index?: number;
6
+ header?: string;
7
+ } | undefined>;
8
+ }
9
+ /**
10
+ * The DOM accessible name of a table COLUMN HEADER (issue #674).
11
+ *
12
+ * `see [Category] column in [Article List] table` used to bake the bracket label straight into
13
+ * `getByRole('columnheader', { name: … })`. Only the TABLE went through resolveSelector; the column
14
+ * never did. On a Japanese DOM the header reads 記事分類, so the step could not pass and there was
15
+ * no override — QA's only escape was to rename the Gherkin reference to the Japanese header text.
16
+ * That reference is what ships in the delivered test case, and it has to stay readable in the
17
+ * project's working language: the DOM text is a property of the PAGE, the label is a property of
18
+ * the TEST. They are allowed to differ, so both need a home.
19
+ *
20
+ * Most specific declaration first:
21
+ * 1. the table entry's `columns: { <col>: { header: … } }` — scoped to this one table, and the
22
+ * same map the row-scoped cell steps already index by, so a column is described in one place;
23
+ * 2. a `<col>--columnheader` entry's `name:` — the column key's own selector, which is already
24
+ * how `click [Name] columnheader` and `[Name] columnheader is sorted …` resolve. Reading it
25
+ * here is what makes the whole columnheader surface behave the same way;
26
+ * 3. the bracket label verbatim — the historical behaviour, so an undeclared column compiles
27
+ * byte-identically to before.
28
+ *
29
+ * (2) accepts the entry ONLY when it actually resolved to a columnheader role. resolveSelector
30
+ * falls back to the BASE key when no `--columnheader` key matches, so an unrelated `category`
31
+ * combobox on the same screen would otherwise hand its accessible name to the column step — a
32
+ * silent wrong locator, worse than the bug being fixed. Auto-infer reaches this branch too, but it
33
+ * names the role after the label, so it returns exactly what (3) would.
34
+ */
35
+ export declare function resolveColumnHeaderName(columnLabel: string, context: PatternContext, tableResolved?: ColumnsCarrier): string;
36
+ export {};
37
+ //# sourceMappingURL=column-header.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"column-header.d.ts","sourceRoot":"","sources":["../../src/utils/column-header.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAqB,MAAM,sBAAsB,CAAC;AAEzE,8EAA8E;AAC9E,UAAU,cAAc;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC,CAAC;CAC3E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,cAAc,EACvB,aAAa,CAAC,EAAE,cAAc,GAC7B,MAAM,CAgBR"}
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveColumnHeaderName = resolveColumnHeaderName;
4
+ const sungen_1 = require("@sun-asterisk/sungen");
5
+ /**
6
+ * The DOM accessible name of a table COLUMN HEADER (issue #674).
7
+ *
8
+ * `see [Category] column in [Article List] table` used to bake the bracket label straight into
9
+ * `getByRole('columnheader', { name: … })`. Only the TABLE went through resolveSelector; the column
10
+ * never did. On a Japanese DOM the header reads 記事分類, so the step could not pass and there was
11
+ * no override — QA's only escape was to rename the Gherkin reference to the Japanese header text.
12
+ * That reference is what ships in the delivered test case, and it has to stay readable in the
13
+ * project's working language: the DOM text is a property of the PAGE, the label is a property of
14
+ * the TEST. They are allowed to differ, so both need a home.
15
+ *
16
+ * Most specific declaration first:
17
+ * 1. the table entry's `columns: { <col>: { header: … } }` — scoped to this one table, and the
18
+ * same map the row-scoped cell steps already index by, so a column is described in one place;
19
+ * 2. a `<col>--columnheader` entry's `name:` — the column key's own selector, which is already
20
+ * how `click [Name] columnheader` and `[Name] columnheader is sorted …` resolve. Reading it
21
+ * here is what makes the whole columnheader surface behave the same way;
22
+ * 3. the bracket label verbatim — the historical behaviour, so an undeclared column compiles
23
+ * byte-identically to before.
24
+ *
25
+ * (2) accepts the entry ONLY when it actually resolved to a columnheader role. resolveSelector
26
+ * falls back to the BASE key when no `--columnheader` key matches, so an unrelated `category`
27
+ * combobox on the same screen would otherwise hand its accessible name to the column step — a
28
+ * silent wrong locator, worse than the bug being fixed. Auto-infer reaches this branch too, but it
29
+ * names the role after the label, so it returns exactly what (3) would.
30
+ */
31
+ function resolveColumnHeaderName(columnLabel, context, tableResolved) {
32
+ const declared = tableResolved?.columns?.[(0, sungen_1.normalizeRefLabel)(columnLabel)]?.header;
33
+ if (declared && declared.trim())
34
+ return declared.trim();
35
+ try {
36
+ const resolved = context.selectorResolver.resolveSelector(columnLabel, context.featureName, 'column', 0);
37
+ if (resolved.role === 'columnheader' && resolved.name && resolved.name.trim()) {
38
+ return resolved.name.trim();
39
+ }
40
+ }
41
+ catch {
42
+ // No entry for the column key and auto-infer declined — the label is the header.
43
+ }
44
+ return columnLabel;
45
+ }
46
+ //# sourceMappingURL=column-header.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"column-header.js","sourceRoot":"","sources":["../../src/utils/column-header.ts"],"names":[],"mappings":";;AAiCA,0DAoBC;AArDD,iDAAyE;AAOzE;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,SAAgB,uBAAuB,CACrC,WAAmB,EACnB,OAAuB,EACvB,aAA8B;IAE9B,MAAM,QAAQ,GAAG,aAAa,EAAE,OAAO,EAAE,CAAC,IAAA,0BAAiB,EAAC,WAAW,CAAC,CAAC,EAAE,MAAM,CAAC;IAClF,IAAI,QAAQ,IAAI,QAAQ,CAAC,IAAI,EAAE;QAAE,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;IAExD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,OAAO,CAAC,gBAAgB,CAAC,eAAe,CACvD,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,QAAQ,EAAE,CAAC,CAC9C,CAAC;QACF,IAAI,QAAQ,CAAC,IAAI,KAAK,cAAc,IAAI,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YAC9E,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAC9B,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,iFAAiF;IACnF,CAAC;IAED,OAAO,WAAW,CAAC;AACrB,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { PatternContext } from '@sun-asterisk/sungen';
2
+ /**
3
+ * Positional-row index expression for `… table row {{n}}` steps (FR2, issue #468).
4
+ *
5
+ * QA counts rows from `conventions.table.row_base` (default 1) and the header <tr>
6
+ * usually carries role=row at getByRole('row') index 0 (`header_is_row: false`), so:
7
+ * nth index = n + (header_is_row ? 0 : 1) - row_base
8
+ *
9
+ * `rowToken` is the literal number or `{{var}}` from the step text. A runtime marker
10
+ * becomes `Number(testData.get('var'))` via the runtime-data transform, so the offset
11
+ * is emitted as ` + k` / ` - k` around it.
12
+ */
13
+ export declare function rowIndexExpr(rowToken: string, context: PatternContext, opts?: {
14
+ headerInRows?: boolean;
15
+ }): string;
16
+ /** Matches `table row 2` / `table row {{n}}` and captures the row token. */
17
+ export declare const TABLE_ROW_NTH: RegExp;
18
+ //# sourceMappingURL=row-index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"row-index.d.ts","sourceRoot":"","sources":["../../src/utils/row-index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,IAAI,GAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAO,GAC/E,MAAM,CAmBR;AAED,4EAA4E;AAC5E,eAAO,MAAM,aAAa,QAAyC,CAAC"}
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TABLE_ROW_NTH = void 0;
4
+ exports.rowIndexExpr = rowIndexExpr;
5
+ /**
6
+ * Positional-row index expression for `… table row {{n}}` steps (FR2, issue #468).
7
+ *
8
+ * QA counts rows from `conventions.table.row_base` (default 1) and the header <tr>
9
+ * usually carries role=row at getByRole('row') index 0 (`header_is_row: false`), so:
10
+ * nth index = n + (header_is_row ? 0 : 1) - row_base
11
+ *
12
+ * `rowToken` is the literal number or `{{var}}` from the step text. A runtime marker
13
+ * becomes `Number(testData.get('var'))` via the runtime-data transform, so the offset
14
+ * is emitted as ` + k` / ` - k` around it.
15
+ */
16
+ function rowIndexExpr(rowToken, context, opts = {}) {
17
+ const rowBase = context.appProfile?.tableRowBase ?? 1;
18
+ // `headerInRows: false` — the table declares a row SHAPE (`rows: 'div.job-row'`, issue #688), and a
19
+ // shape never matches the header, so `header_is_row` has nothing to correct for.
20
+ const headerInRows = opts.headerInRows ?? true;
21
+ const headerOffset = headerInRows && !context.appProfile?.tableHeaderIsRow ? 1 : 0;
22
+ const offset = headerOffset - rowBase;
23
+ const varMatch = rowToken.match(/^\{\{([^}]+)\}\}$/);
24
+ if (!varMatch)
25
+ return String(Number(rowToken) + offset);
26
+ // No fallback to the placeholder text. This value is spliced RAW into `.nth({{{rowIndexExpr}}})`,
27
+ // so `${n}` landed as a bare expression outside any template literal — a syntax error that took
28
+ // the whole spec file at parse time, not just the step, and named nothing useful. The throw
29
+ // reaches StepMapper.mapStep and becomes SG-E038 against this step alone (#781).
30
+ const expr = context.dataResolver.resolveData(varMatch[1].trim(), context.featureName);
31
+ if (/^\d+$/.test(expr))
32
+ return String(Number(expr) + offset); // inline-data mode
33
+ if (offset === 0)
34
+ return expr;
35
+ return offset > 0 ? `${expr} + ${offset}` : `${expr} - ${-offset}`;
36
+ }
37
+ /** Matches `table row 2` / `table row {{n}}` and captures the row token. */
38
+ exports.TABLE_ROW_NTH = /\btable\s+row\s+(\d+|\{\{[^}]+\}\})/i;
39
+ //# sourceMappingURL=row-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"row-index.js","sourceRoot":"","sources":["../../src/utils/row-index.ts"],"names":[],"mappings":";;;AAaA,oCAqBC;AAhCD;;;;;;;;;;GAUG;AACH,SAAgB,YAAY,CAC1B,QAAgB,EAAE,OAAuB,EAAE,OAAmC,EAAE;IAEhF,MAAM,OAAO,GAAG,OAAO,CAAC,UAAU,EAAE,YAAY,IAAI,CAAC,CAAC;IACtD,oGAAoG;IACpG,iFAAiF;IACjF,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC;IAC/C,MAAM,YAAY,GAAG,YAAY,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACnF,MAAM,MAAM,GAAG,YAAY,GAAG,OAAO,CAAC;IAEtC,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACrD,IAAI,CAAC,QAAQ;QAAE,OAAO,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,MAAM,CAAC,CAAC;IAExD,kGAAkG;IAClG,gGAAgG;IAChG,4FAA4F;IAC5F,iFAAiF;IACjF,MAAM,IAAI,GAAW,OAAO,CAAC,YAAY,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IAC/F,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,mBAAmB;IACjF,IAAI,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9B,OAAO,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,MAAM,MAAM,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;AACrE,CAAC;AAED,4EAA4E;AAC/D,QAAA,aAAa,GAAG,sCAAsC,CAAC"}