@ia-qa/pal 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  **A QA pal next to your team — not instead of it.**
4
4
 
5
- One command walks your web app, maps every page, checks it, and compares it with the last tour. Then it tells you four things, in the order you act on them: what is **broken**, what **needs a decision**, what is **ready to repair**, and what it **could not see**.
5
+ One command walks your web app, maps every page, checks it, and compares it with the last tour — and, when you declare your end-to-end suite, reads it too. Then it tells you four things, in the order you act on them: what is **broken**, what **needs a decision**, what is **ready to repair**, and what it **could not see**.
6
6
 
7
7
  - **Local.** A headless browser on your machine. Nothing is uploaded, no account, no LLM.
8
8
  - **Deterministic.** Verdicts come from [`@ia-qa/self-healing`](https://www.npmjs.com/package/@ia-qa/self-healing) — the same engine as `ia-qa-heal diff` and `ia-qa-heal check`, so the two can never disagree.
@@ -23,7 +23,7 @@ npx playwright install chromium
23
23
  npx ia-qa-pal tour
24
24
  ```
25
25
 
26
- **Not set up yet?** In a terminal, the first `tour` asks two questions — where the app lives, and how it logs in — then reads its pages from `sitemap.xml` (or, without one, follows its links: same host, GET only, at most 50 pages), shows you exactly what it will write, and writes it only when you say yes. Nothing it asks takes a password.
26
+ **Not set up yet?** In a terminal, the first `tour` asks where the app lives and how it logs in — and, when it finds a test suite in the project, whether to read it — then reads its pages from `sitemap.xml` (or, without one, follows its links: same host, GET only, at most 50 pages), shows you exactly what it will write, and writes it only when you say yes. Nothing it asks takes a password.
27
27
 
28
28
  From CI or an agent, the same setup with flags:
29
29
 
@@ -60,11 +60,54 @@ ia-qa-pal tour — https://app.example · level 1 (with a login)
60
60
 
61
61
  The time estimate only appears once a batch has been explored on your app: it is the pace measured there, never a constant.
62
62
 
63
+ ## What fails a tour, and what does not
64
+
65
+ - **Drift on what a page shows when it loads** — a control lost, ambiguous, or a selector now reaching another element — fails the tour.
66
+ - **Drift on what is reached only by clicking** — a profile opened from a ranking, a button that exists in one state of the app — is listed under *To decide* with the clicks that reached it, and does **not** fail the tour: on an app with live data it may simply depend on what the click landed on. `--strict` makes it fail too.
67
+ - **A label that differs only by a number** (`Cart (3)` → `Cart (5)`, `Activity60` → `Activity30`) comes with the `nameMask` to paste into `.ia-qa/config.json`; the element stays under contract, only its label stops being compared.
68
+ - **A change you intended**: review it, then make it the reference for those pages only —
69
+
70
+ ```bash
71
+ npx ia-qa-pal accept settings checkout # or --all: every page the last tour reported
72
+ ```
73
+
74
+ ## With your test suite
75
+
76
+ Declare it at setup (`--tests tests/`, or answer yes when the tour finds it) and every tour also reads it:
77
+
78
+ - **🔧 Ready to repair** — when a selector your tests write stops reaching its element while the element is still there, the rewrite is listed with `file:line` and the command that applies it after you review it. pal never edits your tests.
79
+ - **⛔ To decide** — locator names your tests use that nothing on the mapped pages carries, and renamed labels your tests locate by name.
80
+ - **🔍 Not seen** — `ia-qa-pal tour --suite` runs the `testCommand` from your config once, with the capture hook, and names the pages your suite **never visits**. The capture is kept apart from the tour's contracts. Your specs need the hook for this — one import:
81
+
82
+ ```js
83
+ import { test, expect } from '@ia-qa/self-healing/capture';
84
+ ```
85
+
86
+ Without it, the report says the suite ran but recorded no page — never that it visits nothing.
87
+
88
+ ## In the background, and for agents
89
+
90
+ ```bash
91
+ npx ia-qa-pal tour --background # returns at once
92
+ npx ia-qa-pal status # phase and progress, then the report
93
+ ```
94
+
95
+ One tour runs per project; a second is refused while the first is alive, and a tour whose process died is reported as interrupted, never as running.
96
+
97
+ `ia-qa-pal-mcp` is the same thing as a local stdio MCP server — `pal_project`, `pal_setup`, `pal_tour` (always in the background, so no client times out) and `pal_status`. No tool takes a command, and there is no login tool.
98
+
99
+ ```json
100
+ { "mcpServers": { "ia-qa-pal": { "command": "npx", "args": ["-y", "-p", "@ia-qa/pal", "ia-qa-pal-mcp"] } } }
101
+ ```
102
+
63
103
  ### Options
64
104
 
65
105
  | flag | |
66
106
  |---|---|
67
107
  | `--more` | explore the next batch in depth instead of a regression tour |
108
+ | `--suite` | also run your test command once, with the capture hook |
109
+ | `--background` | start detached and return at once |
110
+ | `--strict` | also fail on drift reached only by clicking |
68
111
  | `--batch <n>` | pages per depth batch (default 10) |
69
112
  | `--json` | the whole report as one JSON document on stdout; progress stays on stderr |
70
113
  | `--session <file>` | a Playwright `storageState` to reuse |
@@ -74,9 +117,9 @@ The time estimate only appears once a batch has been explored on your app: it is
74
117
 
75
118
  | code | meaning |
76
119
  |---|---|
77
- | `0` | nothing needs a decision (warnings never fail a tour) |
78
- | `1` | a dead link, or drift that blocks (an element lost, ambiguous, or a selector now pointing at another element) |
79
- | `2` | could not answer: not set up, nothing could be mapped, or the tour refused to run |
120
+ | `0` | nothing needs a decision (warnings and proposed repairs never fail a tour) |
121
+ | `1` | a dead link, drift on what pages show on load (an element lost, ambiguous, or a selector now pointing at another element), a test about to act on the wrong element, or your suite failed when run with `--suite` |
122
+ | `2` | could not answer: not set up, nothing could be mapped, the tour refused to run, or one is already running |
80
123
 
81
124
  Read `2` as "no answer", never as "the app is broken".
82
125
 
@@ -85,8 +128,5 @@ Read `2` as "no answer", never as "the app is broken".
85
128
  - **Only your app.** Its pages load whatever they load in any browser.
86
129
  - **Exploration is read-only.** While menus, tabs and dialogs are opened, every non-GET request is blocked, and controls named like actions (log out, delete, pay…) are never clicked.
87
130
  - **The link check** requests your app's own links with GET, skipping any URL that acts (`/logout`, `/delete`…).
88
- - **Files** live in `.ia-qa/`: contracts, baseline, and `pal/state.json` (pace and already-reported findings). A saved session there holds live cookies — it is gitignored and never leaves your machine.
89
-
90
- ## Not yet
91
-
92
- Reading your test suite (coverage of what it does not test, repairs proposed as a patch), a local MCP server for agents, and background exploration are planned next.
131
+ - **Your test command** runs only with `--suite` (or `suite: true` from an agent), and it is always the one in your config — never a string passed on the call.
132
+ - **Files** live in `.ia-qa/`: contracts, baseline, `usage.json` (your suite's locators), and `pal/` (the measured pace, findings already reported, the current run and its log). A saved session there holds live cookies — it is gitignored and never leaves your machine.
package/TUTORIAL.md CHANGED
@@ -33,10 +33,10 @@ In your project folder:
33
33
  npx ia-qa-pal tour
34
34
  ```
35
35
 
36
- The project is not set up yet, so the tour asks two questions first.
36
+ The project is not set up yet, so the tour asks first: where the app lives, how it logs in, and — only when it finds a test suite in the folder — whether to read it (section 7).
37
37
 
38
38
  ```
39
- This project is not set up yet — two questions first.
39
+ This project is not set up yet — a few questions first.
40
40
 
41
41
  Where does the app live? (https://…) https://www.ia-qa.com
42
42
  Does the app ask you to log in?
@@ -96,10 +96,10 @@ The report has at most four sections, and each one appears only when it holds so
96
96
 
97
97
  | section | what it holds | what you do |
98
98
  |---|---|---|
99
- | 🔴 **Broken in the app** | links your app itself answers with an error | fix the link |
100
- | ⛔ **To decide** | drift that blocks, and findings seen for the first time | a person looks |
101
- | 🔧 **Ready to repair** | *(coming with test-suite support)* | |
102
- | 🔍 **Not seen** | pages not explored, not mapped, only partly explored, links it could not verify | decide whether it matters |
99
+ | 🔴 **Broken** | links your app itself answers with an error; your suite failing under `--suite` | fix it |
100
+ | ⛔ **To decide** | drift, and findings seen for the first time | a person looks — then fixes, or `accept`s |
101
+ | 🔧 **Ready to repair** | with your test suite declared: selector rewrites for your tests (section 7) | review, then apply with `ia-qa-heal fix` |
102
+ | 🔍 **Not seen** | pages not explored, not mapped, not compared, only partly explored, links it could not verify, pages your suite never visits | decide whether it matters |
103
103
 
104
104
  Three things are worth knowing about this first report:
105
105
 
@@ -128,15 +128,47 @@ ia-qa-pal tour — https://www.nodemented.com · level 1 (with a login)
128
128
 
129
129
  Each page is captured again **the way its baseline was captured** — on the surface, or opened in depth — and compared with it. A finding already reported is counted, not listed again: a list that never shrinks stops being read by the third tour.
130
130
 
131
- When something moved (illustrative):
131
+ When something moved — a real tour of a browser game with live data, five minutes after its baseline:
132
132
 
133
133
  ```
134
- ⛔ BLOCK · 1937 ok · 0 renamed · 1 lost · 0 ambiguous · 0 rebound
134
+ ia-qa-pal tour — https://project-1jpdv.vercel.app · level 2 (with your test suite)
135
+ ⛔ BLOCK · 1471 ok · 0 renamed · 2 lost · 0 ambiguous · 1 rebound
136
+ 17 of 17 pages under contract · 10 explored in depth of 16 pages with closed controls
135
137
 
136
138
  ⛔ To decide
137
- drift settings: 1 lost · 0 ambiguous · 0 rebound
139
+ drift jeu-journal: button "📡Activité60" rebound
140
+ behind 2 elements reached only by clicking changed — they may depend on the data the click lands on:
141
+ jeu-chantier: button "👽 Intercepter" lost, after "🌌Galaxie"
142
+ jeu-chantier: button "Reset" lost, after "🌌Galaxie"
143
+ If these changes are intended: ia-qa-pal accept jeu-journal jeu-chantier
144
+ 61 warnings already reported — not listed again.
145
+
146
+ 💡 1 label differs only by a number — a counter or a clock that changes on its own, and it is holding the verdict.
147
+ e.g. "📡Activité60" → "📡Activité30". To stop comparing those labels (the element stays under contract), add to .ia-qa/config.json:
148
+ "nameMask": ["📡Activité*"]
138
149
  ```
139
150
 
151
+ Two kinds of drift, and they do not weigh the same:
152
+
153
+ - **What a page shows when it loads** fails the tour. Here, `📡Activité60` is a counter that moved on its own — the 💡 line gives the `nameMask` to paste once.
154
+ - **What is reached only by clicking** is listed with the clicks that reached it and does **not** fail the tour. "Intercepter" exists only while the game has something to intercept: the tour did nothing wrong, the data changed. `--strict` makes these fail too.
155
+
156
+ With the `nameMask` added, the next tour of the same game:
157
+
158
+ ```
159
+ ⚠️ Drift only behind interaction — not failing the tour (--strict would) · 1472 ok · 0 renamed · 2 lost · 0 ambiguous · 0 rebound
160
+ ```
161
+
162
+ and it exits `0`, still listing the two buttons under *To decide*.
163
+
164
+ When a change **is** intended — you renamed a button, removed a page section — review it and make it the reference for those pages only:
165
+
166
+ ```bash
167
+ npx ia-qa-pal accept jeu-chantier # or --all: every page the last tour reported
168
+ ```
169
+
170
+ Nothing else in the baseline moves. An agent never runs this for you: accepting drift is the decision the report hands to a person.
171
+
140
172
  What the words mean:
141
173
 
142
174
  | word | meaning |
@@ -158,9 +190,9 @@ The control stays under contract; only its label is no longer compared.
158
190
 
159
191
  | code | meaning |
160
192
  |---|---|
161
- | `0` | nothing needs a decision — warnings never fail a tour |
162
- | `1` | a dead link, or drift that blocks |
163
- | `2` | no answer: not set up, nothing could be mapped, or the tour refused to run |
193
+ | `0` | nothing needs a decision — warnings, proposed repairs and drift behind interaction never fail a tour |
194
+ | `1` | a dead link, drift on what pages show on load, a test about to act on the wrong element, or your suite failed under `--suite` |
195
+ | `2` | no answer: not set up, nothing could be mapped, the tour refused to run, or one is already running |
164
196
 
165
197
  Read `2` as "could not answer", never as "the app is broken".
166
198
 
@@ -179,7 +211,7 @@ npx ia-qa-pal tour --more
179
211
 
180
212
  `--more` explores the next batch and adds those pages to the baseline. Run it when you have the time — the estimate told you how much. `--batch <n>` changes the batch size.
181
213
 
182
- It **refuses** when the last tour ended on BLOCK:
214
+ It **refuses** when the last tour ended on drift that fails (accept or fix it first — `ia-qa-pal accept` clears it):
183
215
 
184
216
  ```
185
217
  ⏸ Did not run — The last tour ended on BLOCK. Exploring now would promote those pages as they are today and accept that drift unseen — resolve it, then run `ia-qa-pal tour` again.
@@ -217,12 +249,78 @@ npx ia-qa-pal setup --url https://staging.shop.example --login session --session
217
249
 
218
250
  ---
219
251
 
220
- ## 7. In CI
252
+ ## 7. With your test suite
253
+
254
+ A tour already tells you what changed in the app. With your end-to-end suite declared, it also tells you what that change does to **your tests**.
255
+
256
+ Declare it once — the setup offers it when it finds a `tests/`, `e2e/` or `cypress/e2e/` folder with specs, or with flags:
257
+
258
+ ```bash
259
+ npx ia-qa-pal setup --url https://staging.shop.example --login none --tests tests --yes
260
+ ```
261
+
262
+ The command that runs the suite is detected from `package.json` (`test:e2e`, `e2e`…) or a `playwright.config`; set it yourself with `--test-command "npm run test:e2e"`.
263
+
264
+ From then on, every tour reads the suite's locators before capturing, and the report gains three things.
265
+
266
+ **🔧 Ready to repair.** A selector your tests write stopped reaching its element — the class changed, a `data-testid` was added — while the element itself is still on the page (illustrative):
267
+
268
+ ```
269
+ 🔧 Ready to repair (1 replacement in 1 file)
270
+ tests/settings.spec.js:17 .btn-primary → #prefs > button:nth-of-type(1)
271
+ Apply after review: npx -p @ia-qa/self-healing ia-qa-heal fix (pal never edits your tests)
272
+ ```
273
+
274
+ pal only lists it. `ia-qa-heal fix` computes the same rewrite and applies it — review it with `git diff` before committing. A rewrite that could break a passing test (two pages wanting the same string to become two different things, an XPath inside `By.xpath`) is left out and named, never guessed.
275
+
276
+ **⛔ To decide.** Locator names your tests use that nothing on the mapped pages carries — `getByRole('button', { name: 'Save' })` when no button is called Save anymore — with `file:line`. It is advisory: the name may live on a page or in a state the tour did not map. And when a label your tests locate by name was renamed, the tour says so.
277
+
278
+ **🔍 Which pages your suite never visits.** This one needs the suite to run:
279
+
280
+ ```bash
281
+ npx ia-qa-pal tour --suite
282
+ ```
283
+
284
+ It runs the `testCommand` from your config once, with the capture hook armed, and keeps only the list of pages the suite really opened — its capture never mixes with the tour's contracts. The answer stays in the report until the next `--suite` (illustrative):
285
+
286
+ ```
287
+ 🔍 Not seen
288
+ your suite never visits 14 of 17 pages (run of 2026-09-15): faq, jeu-galaxie, jeu-classement, …
289
+ ```
290
+
291
+ Your specs need the capture hook — one import, a drop-in for Playwright's own:
292
+
293
+ ```js
294
+ import { test, expect } from '@ia-qa/self-healing/capture';
295
+ ```
296
+
297
+ Without it, the report says the suite ran but recorded no page, and names the import. It never says your suite visits nothing. If the suite fails during `--suite`, the tour reports it under **Broken** and exits `1`.
298
+
299
+ ---
300
+
301
+ ## 8. In the background
302
+
303
+ A tour on a large app takes a while. Start it and get your terminal back:
304
+
305
+ ```bash
306
+ npx ia-qa-pal tour --background
307
+ npx ia-qa-pal status
308
+ ```
309
+
310
+ ```
311
+ ⏳ A tour is running (pid 34136, since 2026-09-15T14:04:32Z) — depth 4/10 · jeu-planete
312
+ ```
313
+
314
+ When it is done, `status` prints the report. Only one tour runs per project: a second is refused while the first is alive, because two tours would write the same contracts. A tour whose process died before finishing is reported as **interrupted** — never as still running, never as a result. Its output is in `.ia-qa/pal/run.log`.
315
+
316
+ ---
317
+
318
+ ## 9. In CI
221
319
 
222
320
  Build the baseline on your machine, commit it, and let CI compare.
223
321
 
224
322
  ```bash
225
- git add .ia-qa/config.json .ia-qa/baseline .ia-qa/pal
323
+ git add .ia-qa/config.json .ia-qa/baseline .ia-qa/pal/state.json
226
324
  git commit -m "ia-qa-pal baseline"
227
325
  ```
228
326
 
@@ -241,13 +339,29 @@ Never commit `.ia-qa/session.json`.
241
339
 
242
340
  ---
243
341
 
244
- ## 8. With an AI agent
342
+ ## 10. With an AI agent
245
343
 
246
- An agent can drive `ia-qa-pal` from a shell. Three rules it must follow — they are also in `ia-qa-pal --help`:
344
+ An agent can drive `ia-qa-pal` from a shell, or through its local MCP server:
345
+
346
+ ```json
347
+ { "mcpServers": { "ia-qa-pal": { "command": "npx", "args": ["-y", "-p", "@ia-qa/pal", "ia-qa-pal-mcp"] } } }
348
+ ```
349
+
350
+ | tool | what it does |
351
+ |---|---|
352
+ | `pal_project` | is the project set up, its level, its baseline, a tour running? |
353
+ | `pal_setup` | writes the config from **your** answers — never overwrites one |
354
+ | `pal_tour` | starts a tour in the background and returns at once, so no client times out |
355
+ | `pal_status` | progress, then the full report |
356
+
357
+ No tool takes a command — your test command comes from your config, which you wrote — and there is no login tool.
358
+
359
+ The rules the agent must follow are also in `ia-qa-pal --help`:
247
360
 
248
361
  - **Read "Not seen".** A clean tour over part of an app is not a clean app, and the agent must say which part was not seen.
249
362
  - **A first tour is not a pass.** It established the baseline.
250
363
  - **Never answer a setup question for you, and never run `ia-qa-heal login`.** That command waits for a person at a browser; the agent gives it to you instead.
364
+ - **Never apply a proposed repair without your review, and never add `--suite` without asking** — it runs your suite on your machine.
251
365
 
252
366
  Asked to tour a project that is not set up, `ia-qa-pal tour --json` answers with what is missing rather than guessing:
253
367
 
@@ -264,31 +378,34 @@ Asked to tour a project that is not set up, `ia-qa-pal tour --json` answers with
264
378
 
265
379
  ---
266
380
 
267
- ## 9. What it touches
381
+ ## 11. What it touches
268
382
 
269
383
  - **Only your app.** Its pages load whatever they load in any browser (fonts, analytics…).
270
384
  - **Exploration is read-only.** While menus, tabs and dialogs are opened, every non-GET request is blocked, and controls named like actions (log out, delete, pay, send…) are never clicked.
271
385
  - **The link check** requests your app's own links with GET and skips any URL that acts.
272
- - **Files** live in `.ia-qa/`: `config.json`, `mapping/` (today), `baseline/` (the reference), `pal/state.json` (the measured pace and the findings already reported).
386
+ - **Your test files are read, never written.** Your test command runs only with `--suite`.
387
+ - **Files** live in `.ia-qa/`: `config.json`, `mapping/` (today), `baseline/` (the reference), `usage.json` (your suite's locators), `pal/state.json` (the measured pace, the findings already reported, the last `--suite` run), `pal/run.json` and `pal/run.log` (the current or last tour).
273
388
 
274
389
  ---
275
390
 
276
- ## 10. When something looks wrong
391
+ ## 12. When something looks wrong
277
392
 
278
393
  **A page is "not mapped" with a timeout.** One slow page costs that page, not the tour. It stays in **Not seen**; if its drift is shown, the report marks it as an older capture.
279
394
 
280
- **Every page behind the login is "not mapped".** The session expired, or never worked. Log in again with `ia-qa-heal login`, then run the tour.
395
+ **"N pages landed on a login page instead".** The session expired, or never worked. Log in again with `ia-qa-heal login`, then run the tour. Drift on those pages is from an older capture: it is not counted, and `accept` refuses them until a tour captures them again.
396
+
397
+ **"not compared — its contract file cannot be read".** A file under `.ia-qa/baseline/` or `.ia-qa/mapping/` is corrupt. Restore it from git, or run the tour again to re-capture it.
281
398
 
282
- **`renamed` on a label you did not touch.** A counter, a date or generated content. Add a `nameMask` for it (section 4).
399
+ **`renamed` or `rebound` on a label you did not touch.** A counter, a date or generated content. The report gives the `nameMask` to paste (section 4).
400
+
401
+ **Drift behind interaction on every tour.** Those elements depend on the app's data. They never fail the tour unless you pass `--strict`; if they are noise you never want to read, `ia-qa-pal accept` the page after the data settles, or keep the page on the surface.
283
402
 
284
403
  **"only partly explored (click budget reached)".** The page has more closed controls than one exploration opens (150 clicks). Its contract covers part of the page, and the report says so.
285
404
 
286
- ---
405
+ **"A tour is already running" but nothing is.** Run `ia-qa-pal status`: a tour whose process died shows as interrupted, and the next tour starts normally.
287
406
 
288
- ## 11. Coming next
407
+ **"your suite ran but recorded no page".** Your specs do not import the capture hook (section 7).
289
408
 
290
- - **Your test suite**: which pages it never visits, and repairs to its selectors proposed as a patch — never applied without you.
291
- - **A local MCP server** so agents call `tour` as a tool.
292
- - **Background exploration**, followed from the local console.
409
+ ---
293
410
 
294
411
  `ia-qa-pal` is built on [`@ia-qa/self-healing`](https://www.npmjs.com/package/@ia-qa/self-healing): the verdicts it prints are the ones `ia-qa-heal diff` and `ia-qa-heal check` compute, so the two never disagree.
@@ -0,0 +1,20 @@
1
+ /**
2
+ * A person looked at the drift and it is what they wanted: a new label, a removed button, data
3
+ * that moved. Only those pages' latest captures become the reference — nothing else in `mapping/`
4
+ * is promoted, and nothing is accepted that the last tour did not capture.
5
+ *
6
+ * Deliberately not an MCP tool: accepting drift is the decision the whole report hands to a person.
7
+ */
8
+ export interface AcceptOutcome {
9
+ accepted: string[];
10
+ skipped: Array<{
11
+ page: string;
12
+ reason: string;
13
+ }>;
14
+ refused?: string;
15
+ }
16
+ /** The pages whose drift the last finished tour reported. */
17
+ export declare function driftedPages(dir: string): string[];
18
+ export declare function acceptPages(dirGiven: string, pagesGiven: string[], opts?: {
19
+ all?: boolean;
20
+ }): Promise<AcceptOutcome>;
package/dist/accept.js ADDED
@@ -0,0 +1,119 @@
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.driftedPages = driftedPages;
37
+ exports.acceptPages = acceptPages;
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
40
+ const self_healing_1 = require("@ia-qa/self-healing");
41
+ const run_1 = require("./run");
42
+ const state_1 = require("./state");
43
+ /** The pages whose drift the last finished tour reported. */
44
+ function driftedPages(dir) {
45
+ const drift = (0, run_1.readRun)(dir)?.report?.drift;
46
+ if (!drift)
47
+ return [];
48
+ return [...new Set([...drift.onLoad, ...drift.behindInteraction].map((f) => f.page).concat(drift.suitePages))];
49
+ }
50
+ async function acceptPages(dirGiven, pagesGiven, opts = {}) {
51
+ const dir = path.resolve(dirGiven);
52
+ if (!fs.existsSync(path.join(dir, '.ia-qa', 'config.json'))) {
53
+ return { accepted: [], skipped: [], refused: 'This project is not set up yet — there is no .ia-qa/config.json.' };
54
+ }
55
+ if ((0, run_1.activeRun)(dir))
56
+ return { accepted: [], skipped: [], refused: 'A tour is running on this project — accept after it finishes (ia-qa-pal status).' };
57
+ (0, self_healing_1.setBaseDir)(dir);
58
+ const configured = new Set((0, self_healing_1.loadConfig)().pages.map((p) => p.name));
59
+ const wanted = opts.all ? driftedPages(dir) : pagesGiven;
60
+ if (wanted.length === 0) {
61
+ return { accepted: [], skipped: [], refused: opts.all ? 'The last tour reported no drift to accept.' : 'Name the pages to accept, or --all for every page the last tour reported.' };
62
+ }
63
+ const ready = [];
64
+ const skipped = [];
65
+ const notRecaptured = new Set(((0, run_1.readRun)(dir)?.report?.pages.notMapped ?? []).map((m) => m.page));
66
+ for (const page of wanted) {
67
+ if (!configured.has(page)) {
68
+ skipped.push({ page, reason: 'not a configured page' });
69
+ continue;
70
+ }
71
+ if (notRecaptured.has(page)) {
72
+ skipped.push({ page, reason: 'the last tour could not re-capture it — accepting would promote an older capture; run a tour first' });
73
+ continue;
74
+ }
75
+ let current;
76
+ try {
77
+ current = (0, self_healing_1.loadMapping)(page, (0, self_healing_1.mappingDir)());
78
+ }
79
+ catch {
80
+ skipped.push({ page, reason: 'no capture yet — run a tour first' });
81
+ continue;
82
+ }
83
+ try {
84
+ if ((0, self_healing_1.loadMapping)(page, (0, self_healing_1.baselineDir)()).capturedAt === current.capturedAt) {
85
+ skipped.push({ page, reason: 'the baseline already is its latest capture' });
86
+ continue;
87
+ }
88
+ }
89
+ catch {
90
+ /* not in the baseline yet: accepting it adds it */
91
+ }
92
+ ready.push(page);
93
+ }
94
+ if (ready.length > 0) {
95
+ const { log, info, warn } = console;
96
+ console.log = console.info = console.warn = (...a) => console.error(...a);
97
+ const before = process.exitCode;
98
+ process.exitCode = undefined;
99
+ try {
100
+ await (0, self_healing_1.runBaseline)([], { pages: ready });
101
+ if (process.exitCode)
102
+ return { accepted: [], skipped, refused: `baseline refused to promote ${ready.join(', ')}` };
103
+ }
104
+ finally {
105
+ process.exitCode = before;
106
+ console.log = log;
107
+ console.info = info;
108
+ console.warn = warn;
109
+ }
110
+ const remaining = driftedPages(dir).filter((p) => !ready.includes(p));
111
+ const state = (0, state_1.loadState)();
112
+ if (state.lastVerdict === 'BLOCK' && remaining.length === 0) {
113
+ delete state.lastVerdict;
114
+ (0, state_1.saveState)(state);
115
+ }
116
+ }
117
+ return { accepted: ready, skipped };
118
+ }
119
+ //# sourceMappingURL=accept.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accept.js","sourceRoot":"","sources":["../src/accept.ts"],"names":[],"mappings":"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"}