@vijaypjavvadi/bdd2pw 4.0.2 → 4.2.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/CHANGELOG.md CHANGED
@@ -9,6 +9,240 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  _Nothing yet._
11
11
 
12
+ ## [4.2.0] — 2026-09-02
13
+
14
+ ### Theme: Silent problems, made loud — DOM drift detection
15
+
16
+ Silent UI changes are the second-worst failure mode in Playwright
17
+ suites (the worst being flaky locators — which bdd2pw's healing loop
18
+ already addresses). A vendor updates their design system, a component
19
+ library rolls out a new variant, a form input's role attribute
20
+ changes — and a week later three tests start failing with `element
21
+ not found` and no obvious cause. The team spends an hour bisecting
22
+ the DOM instead of an hour fixing the UI.
23
+
24
+ v4.2 embeds a **stable sha256 of the page's accessibility tree** in
25
+ every emitted POM's header comment. On re-scaffold, bdd2pw compares
26
+ the new snapshot against the hash stored in the existing POM. If
27
+ they differ, a `DOM DRIFT` warning lands in `BDD_REVIEW.md` with
28
+ both hashes and a suggested triage path. A new `--fail-on-drift`
29
+ CLI flag turns the warning into a non-zero exit code so CI can
30
+ gate merges on silent UI regressions.
31
+
32
+ Credit for the idea: a LinkedIn commenter on the v4.1 release post
33
+ suggested "version the emitted POM against a DOM snapshot hash so a
34
+ silent UI change shows up as a diff rather than a mystery failure
35
+ next run." That's exactly what v4.2 does.
36
+
37
+ ### Added — DOM-hash utilities (`src/discovery/domHash.ts`)
38
+
39
+ - `computeDomHash(snapshot)` — deterministic sha256 of the
40
+ accessibility tree via canonical JSON stringify (sorted keys at
41
+ every object level). Identical trees always produce identical
42
+ hashes regardless of MCP's property ordering. Only the tree
43
+ contributes to the hash — url, title, and raw DOM string are
44
+ ignored so unrelated changes don't false-positive.
45
+ - `buildDomHashHeader(hash, url, at?)` — the JSDoc-formatted
46
+ `@bdd2pw` comment block prepended to every emitted POM.
47
+ - `extractDomHashFromPom(contents)` — reverse operation for drift
48
+ detection on re-scaffold.
49
+ - `prependDomHashHeader(contents, header)` — idempotent header
50
+ injection; replaces any prior `@bdd2pw` block instead of
51
+ stacking duplicates.
52
+
53
+ ### Added — POM header emission
54
+
55
+ Every emitted POM now carries a comment block above the imports:
56
+
57
+ ```typescript
58
+ /**
59
+ * @bdd2pw generated 2026-09-02T14:32:00.000Z
60
+ * @bdd2pw dom-hash a3f8c2e4d5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2
61
+ * @bdd2pw url https://saucedemo.com/
62
+ *
63
+ * The dom-hash line is used by bdd2pw to detect DOM drift on
64
+ * re-scaffold. Do not edit it by hand — regenerate with `bdd2pw
65
+ * scaffold` or `bdd2pw update-pom` to refresh.
66
+ */
67
+ import { Page } from "@playwright/test";
68
+ export class LoginPage { ... }
69
+ ```
70
+
71
+ Emission is unconditional — deterministic-only runs, LLM-fallback
72
+ runs, and `--dry-run` all produce the header when a snapshot exists.
73
+ `--no-discovery` runs (no snapshot) skip the header, and re-scaffold
74
+ against a v4.1-or-earlier POM emits an info-level `BDD_REVIEW.md`
75
+ note that says the baseline hash was just planted.
76
+
77
+ ### Added — drift detection on re-scaffold
78
+
79
+ When `scaffold()` runs in `augment` mode (existing POM present) and
80
+ the new snapshot's hash differs from the one embedded in the POM:
81
+
82
+ - A `warn`-severity `ReviewItem` is added to `BDD_REVIEW.md` with
83
+ both truncated hashes and a suggested review path.
84
+ - `ScaffoldResult.driftDetected` is set to `true` for programmatic
85
+ callers.
86
+
87
+ ### Added — `--fail-on-drift` CLI flag
88
+
89
+ `bdd2pw scaffold ... --fail-on-drift` exits with code `2` when
90
+ drift is detected, AFTER the scaffold completes writing (so
91
+ `BDD_REVIEW.md` and the refreshed POM are still on disk for the
92
+ developer to inspect). Exit code `2` is distinct from `1` (used for
93
+ other scaffold errors) so CI can special-case drift vs. real failures.
94
+
95
+ ### Added — API surface
96
+
97
+ Two new optional fields on the public types:
98
+
99
+ ```typescript
100
+ interface ScaffoldOptions {
101
+ // ... existing fields
102
+ failOnDrift?: boolean; // v4.2.0
103
+ }
104
+
105
+ interface ScaffoldResult {
106
+ // ... existing fields
107
+ driftDetected?: boolean; // v4.2.0
108
+ }
109
+ ```
110
+
111
+ ### Added — tests
112
+
113
+ - 15-case vitest suite in `tests/unit/v420DomHash.test.ts` covering
114
+ hash determinism, canonical key ordering, round-trip
115
+ extract/build, and idempotent header injection.
116
+ - 11-case Node.js smoke runner in `scripts/smoke-v420-domhash.mjs`
117
+ that bypasses vitest.
118
+
119
+ ### No breaking changes
120
+
121
+ Additive-only. Programmatic callers that don't set `failOnDrift`
122
+ see no behavioural change (drift is still detected and reported in
123
+ `BDD_REVIEW.md`, but the process exit code is unchanged). Emitted
124
+ POMs gain a comment block above the imports; all downstream
125
+ consumers (Playwright itself, `merge` mode, `updatePom`) tolerate
126
+ the comment. `updatePom`'s append-only guarantee is preserved.
127
+
128
+ ### Migration
129
+
130
+ `npm i @vijaypjavvadi/bdd2pw@4.2.0` — no code changes required. The
131
+ first re-scaffold of an existing repo plants the baseline hash and
132
+ emits an info-level `BDD_REVIEW.md` line. From the second re-scaffold
133
+ onward, DOM drift will be detected and warned about.
134
+
135
+ ## [4.1.0] — 2026-08-21
136
+
137
+ ### Theme: LLM-binding rewriter — 3/8 → 8/8 bench apps compile cleanly
138
+
139
+ v4.0 (paper release) landed 3/8 bench apps that emitted a fully
140
+ compiling Playwright TypeScript spec via the LLM fallback. The other
141
+ 5 hit specific LLM emission patterns that the v4.0.1 hallucination
142
+ gates correctly rejected — meaning the steps landed as `// TODO`
143
+ comments and the spec failed to compile.
144
+
145
+ v4.1 introduces a **binding-rewriter suite** that mechanically
146
+ transforms six well-known LLM emission patterns into valid
147
+ Playwright-idiomatic code, then runs the resulting binding through
148
+ the existing v4.0.1 gates. If any rewrite would produce compile-fail
149
+ code, the binding is dropped and the step lands as `// TODO` (the
150
+ fail-closed guarantee is unchanged). The result: **every one of the
151
+ 8 benchmark apps now emits a spec that passes `tsc --noEmit`.**
152
+
153
+ ### Added — six LLM-emission rewrite patterns
154
+
155
+ 1. **Pattern A** — `{method:"fill", args:["pomVar.field", "\"x\""]}` →
156
+ `{method:"field.fill", args:["\"x\""]}`. Catches OpenAI
157
+ gpt-4o-mini's "invented helper on the POM" shape.
158
+
159
+ 2. **Pattern B** — `{method:"fill", args:["field", "value"]}` →
160
+ `{method:"field.fill", args:["\"value\""]}`. Bare-field variant
161
+ with auto-quoting of unquoted string values (email addresses,
162
+ punctuation-bearing text, human-readable strings, bare
163
+ identifiers). Reserved literals (`true`, `false`, `null`,
164
+ `undefined`, numbers) and property-access chains
165
+ (`data.foo`, `env.USER`) are left untouched. Fixes the SauceDemo
166
+ `fill(locked_out_user)` regression where the LLM dropped the
167
+ string quotes.
168
+
169
+ 3. **Pattern C** — `{method:"fill", args:["input[name='email']", "x"]}` →
170
+ `customBody: await pomVar.page.locator("input[name='email']").fill("x")`.
171
+ Promotes a raw CSS selector arg to a `page.locator()` chain in
172
+ customBody. Tightened to require actual CSS syntax characters
173
+ (`[`, `#`, `>`, `~`, `+`, `*`, `.identifier`, `:pseudo-class`) —
174
+ plain-text button labels like `"Sign in"` are no longer
175
+ misclassified as selectors.
176
+
177
+ 4. **Pattern D** — inside `customBody`, `page.<locatorMethod>(pomVar.<field>, ...)`
178
+ → `pomVar.<field>.<locatorMethod>(...)`. Catches compound-step
179
+ expansions that accidentally used `page.fill(locator, value)`
180
+ (invalid Playwright API — `page.fill` requires a string
181
+ selector, not a Locator instance).
182
+
183
+ 5. **Pattern F — Page-matcher validation gate.** `expect(page).<matcher>`
184
+ is only valid when `<matcher>` is one of `toHaveURL`,
185
+ `toHaveTitle`, or `toHaveScreenshot`. Locator-only matchers
186
+ (`toHaveText`, `toContainText`, `toBeVisible`, etc.) emitted
187
+ against `page` are dropped and land as TODO instead of shipping
188
+ as compile-fail code.
189
+
190
+ 6. **Pattern G — bare-identifier assertion locator rewrite.**
191
+ `{assertion:{locator:"commentsList", matcher:"toContainText"}}`
192
+ → `{assertion:{locator:"loginPage.commentsList", ...}}` when
193
+ `commentsList` is a known POM field. If the bare identifier
194
+ isn't a known field, the entire assertion is dropped (Conduit
195
+ bench regression: LLM emitted `expect(commentsList)` — an
196
+ undefined reference).
197
+
198
+ ### Added — final empty-binding gate
199
+
200
+ After all rewriters run, a second empty-binding check drops bindings
201
+ that survived initial validation but had every field nuked by a
202
+ subsequent rewriter (Pattern G rejection was the primary trigger).
203
+
204
+ ### Added — tests
205
+
206
+ - 21-case vitest suite in `tests/unit/v410BindingRewriter.test.ts`
207
+ covering all six rewrite patterns plus integration paths for the
208
+ real bench emissions from OpenAI, Gemini, and Anthropic.
209
+ - 35-case Node.js smoke runner in `scripts/smoke-v410-rewriter.mjs`
210
+ that bypasses vitest and rollup native binaries — useful for
211
+ environments where the vitest transformer's platform-specific
212
+ binaries aren't installed.
213
+
214
+ ### Benchmark comparison — v4.0 vs v4.1 (`--llm openai --skip-governance`)
215
+
216
+ | App | v4.0.1 tsc | v4.1 tsc | v4.0.1 TODOs | v4.1 TODOs |
217
+ |---|---|---|---|---|
218
+ | 01-saucedemo | fail | **pass** | 4 | **0** |
219
+ | 02-the-internet | fail | **pass** | 4 | **0** |
220
+ | 03-juice-shop | pass | pass | 13 | 9 |
221
+ | 04-opencart | fail | **pass** | 4 | 5 |
222
+ | 05-magento | fail | **pass** | 6 | 4 |
223
+ | 06-conduit | fail | **pass** | 6 | 11 |
224
+ | 07-reqres-api | pass | pass | 10 | 10 |
225
+ | 08-automation-practice | pass | pass | 5 | **2** |
226
+ | **Total** | **3/8 pass** | **8/8 pass** | 52 | 41 |
227
+
228
+ TODOs may go up in a few apps because Pattern G / Pattern F now
229
+ correctly reject bindings that previously slipped through as
230
+ compile-broken code. Fewer *broken* lines shipped is the intended
231
+ tradeoff.
232
+
233
+ ### No breaking changes
234
+
235
+ Feature release. Public API surface identical to v4.0.1. Zero-config
236
+ users see no behavioural change on scaffolds that don't invoke the
237
+ LLM fallback. Deterministic mode is byte-identical to v4.0.1.
238
+
239
+ ### Migration
240
+
241
+ `npm i @vijaypjavvadi/bdd2pw@4.1.0` — no code changes required. The
242
+ rewriter is on by default and cannot be disabled (it only ever
243
+ increases the compile rate of LLM-emitted bindings; if you want a
244
+ binding rejected instead of rewritten, hand-edit the emitted spec).
245
+
12
246
  ## [4.0.2] — 2026-07-24
13
247
 
14
248
  ### Documentation-only patch — README refresh to match the actual v4.0.x feature set
package/README.md CHANGED
@@ -25,7 +25,7 @@ Most teams writing BDD test cases (via Cucumber `.feature` files, possibly autho
25
25
 
26
26
  Point it at a `.feature` file and a URL. It parses the Gherkin, scans the live page with Chromium, picks the most stable locator for every interactive element, matches each step to a POM method call, and emits a runnable Playwright TypeScript repo — including `playwright.config.ts`, `pages/login.page.ts`, `tests/login.spec.ts`, and a `BDD_REVIEW.md` listing anything a human still needs to look at.
27
27
 
28
- ## What `bdd2pw` is and isn't (as of v4.0.2)
28
+ ## What `bdd2pw` is and isn't (as of v4.2.0)
29
29
 
30
30
  `bdd2pw` is a **deterministic scaffolder with an optional governed LLM fallback across three providers**. The output is honest about its limits:
31
31
 
@@ -38,6 +38,8 @@ Point it at a `.feature` file and a URL. It parses the Gherkin, scans the live p
38
38
  - **7 opt-in domain rule packs** (v3.4 + v3.8) — banking, healthcare, insurance, retail, gov, education, telecom. Each adds ~20 industry-specific patterns. Activated via `domains: ["banking"]`; empty by default for byte-stable existing behaviour.
39
39
  - **Three-provider LLM fallback, batched per scenario** (v3.5 + v3.11 + v3.12) — one Anthropic Claude Sonnet 4.6, OpenAI gpt-4o-mini, or Google Gemini 2.5 Flash call per scenario instead of one per unmatched step. ~75% cost reduction on unmatched-heavy scaffolds. Every prompt goes through the `ai-governance` sidecar's `/sanitize` endpoint (fail-closed on 4xx/5xx/timeout — the run terminates rather than emit ungoverned code).
40
40
  - **Three hallucination-rejection gates on LLM output** (v4.0.1) — invented-helper detection, structured-pomCall guard, and bare-field-call trap. Bindings the LLM emits that would compile-fail land as `// TODO` comments with `BDD_REVIEW.md` entries instead of broken code. Locked by 19 dedicated unit tests.
41
+ - **Six-pattern LLM-binding rewriter** (v4.1) — mechanically transforms common LLM emission mistakes (invented-helper shapes, bare-field args, unquoted string values, CSS-selector args, `page.<method>` invented-helpers in customBody, Locator-only matchers hallucinated on `page`, bare-identifier assertion locators) into valid Playwright-idiomatic code. Runs *before* the v4.0.1 gates; if a rewrite would still produce broken code, the binding is rejected and lands as TODO. Bench result: 3/8 → **8/8 apps produce specs that pass `tsc --noEmit`**. Locked by 35 unit tests.
42
+ - **DOM-drift detection via POM header hash** (v4.2) — every emitted POM carries a `@bdd2pw dom-hash` comment with a stable sha256 of the page's accessibility tree. On re-scaffold, bdd2pw diffs the new snapshot against the stored hash. Silent UI changes surface as a `DOM DRIFT` warning in `BDD_REVIEW.md` with both hashes and a suggested triage path. `--fail-on-drift` turns it into a non-zero exit code so CI can gate merges on silent UI regressions.
41
43
  - **Data-driven scaffolds** (v4.0.0) — `--data <path>` injects CSV / JSON / XLSX rows into every Scenario Outline's `Examples:` table; `--gen-data --schema <path> --rows N` generates synthetic rows via Faker + optional per-column LLM calls (`llm:auto insurance claim description`); seeded (`--seed 42`) for reproducibility.
42
44
  - **`updatePom` is append-only by construction.** Re-scanning a page that already has a Page Object adds new locators only. Hand-edited method bodies, custom helper methods, custom imports are all preserved byte-identical.
43
45
  - **`--merge` mode** (v3.2) preserves `// bdd2pw:user-block id="..."` sections across regenerations so iterative locator tuning doesn't lose work.
@@ -361,20 +363,23 @@ output/
361
363
  | v3.10.0 | Heal stats | `bdd2pw heal-stats <repo>` CLI consumes `artefacts/heal-events.jsonl` and writes `heal-stats.json` with heal rate, top failing fields, top error patterns, retry latency, candidate-selector promotions. Self-healing ROI per test run. |
362
364
  | v3.11.0 | OpenAI provider | `LLMClientOptions.provider: "openai"` lands with cache + governance + batching + telemetry parity. Default model `gpt-4o-mini` (~17x cheaper than `gpt-4o` for structured-JSON tasks). |
363
365
  | v3.12.0 | Gemini provider | `LLMClientOptions.provider: "gemini"` lands with full parity. Default model `gemini-2.5-flash` ($0.10/M input — cheapest across all three providers). Three-provider parity complete. |
364
- | **v4.0.0 (current)** | Data-driven scaffolds | `--data <path>` (CSV/JSON/XLSX) and `--gen-data --schema <path>` (Faker + LLM) inject Examples rows into Scenario Outlines. `LLMClient.generateText()` added to all three providers. **Additive only — no breaking changes.** |
366
+ | v4.0.0 | Data-driven scaffolds | `--data <path>` (CSV/JSON/XLSX) and `--gen-data --schema <path>` (Faker + LLM) inject Examples rows into Scenario Outlines. `LLMClient.generateText()` added to all three providers. **Additive only — no breaking changes.** |
367
+ | v4.0.1 | LLM hallucination gates | Three-layer rejector for invented POM helpers — bindings that would compile-fail land as TODO instead of broken code. 19 unit tests lock the behaviour. |
368
+ | v4.1.0 | LLM binding rewriter | Six-pattern rewriter that mechanically fixes common LLM emission mistakes (invented helpers, bare-field args, CSS-selector args, `page.<method>` in customBody, hallucinated Page matchers, bare-identifier assertion locators). Bench result: 3/8 → **8/8 apps produce specs that pass `tsc --noEmit`**. Peer-reviewed publication in Elsevier SoftwareX. |
369
+ | **v4.2.0 (current)** | DOM-drift detection | Every emitted POM carries a `@bdd2pw dom-hash` header (sha256 of the accessibility tree). Re-scaffold diffs against the stored hash and surfaces silent UI changes in `BDD_REVIEW.md`. `--fail-on-drift` CLI flag exits non-zero when drift is detected (CI merge gate). |
365
370
 
366
- ### Next 6 months (Jun 2026 → Nov 2026, ~2 releases/month)
371
+ ### Next 6 months (Sep 2026 → Feb 2027, ~1 release/month)
367
372
 
368
373
  | Target | Version | Theme | Headline |
369
374
  |---|---|---|---|
370
- | Aug 2026 | **v3.13.0** | Auto-rules | `bdd2pw apply-proposals --interactive` — walks the human through each v3.6 proposal, validates the regex compiles + matches the sample texts, optionally appends to `stepMatcher.ts`. Closes the propose-rules loop. |
371
- | Aug 2026 | **v4.1.0** | Modern defaults | Bump default models, retire v2.x deprecated APIs, flip opt-in defaults (domains, llm-stats, healing) to opt-out. Migration guide ships alongside. |
372
- | Sep 2026 | **v4.1.0** | sel2pw merge | sel2pw migrated onto `@vijaypjavvadi/pw-emit` — three packages share one emitter. All future v3.x emitter improvements automatically flow to Selenium users. |
373
- | Sep 2026 | **v4.2.0** | Domain packs | Three more opt-in packs: **fintech** (KYC, AML, trading desks), **real-estate** (MLS, listings, escrow), **hospitality** (PMS, reservations, OTA). ~60 more rules. |
374
- | Oct 2026 | **v4.3.0** | API v2 | WebSocket assertion rules, multipart file-upload patterns, GraphQL query / mutation / subscription rules. Extends v3.0's API testing surface. |
375
- | Oct 2026 | **v4.4.0** | Coverage analyzer | `bdd2pw coverage` CLI: which deterministic rules fired on which features across a fleet, per-pack hit rate, dead-rule report, suggestion to retire unused rules. |
376
- | Nov 2026 | **v4.5.0** | CI/CD templates | Optional scaffolder output: GitHub Actions / GitLab CI / Azure Pipelines / CircleCI starter workflows wired to run the generated specs. Opt-in via `--ci <provider>`. |
377
- | Nov 2026 | **v4.6.0** | Mobile dialect | Touch / swipe / scroll-into-view rules + responsive viewport assertions. Reuses the same emitter pipeline; opt-in via `domains: ["mobile"]`. |
375
+ | Sep 2026 | **v4.3.0** | LLM quarantine lane | `@llm-generated` Playwright tag on every LLM-derived scenario + `bdd2pw quarantine-stats` CLI that reports pass rate for the LLM-fallback lane separately from the deterministic lane. Teams can filter runs with `--grep-invert @llm-generated` for a "trusted only" execution. |
376
+ | Oct 2026 | **v4.4.0** | sel2pw merge | sel2pw migrated onto `@vijaypjavvadi/pw-emit` — three packages share one emitter. All future emitter improvements automatically flow to Selenium users. |
377
+ | Oct 2026 | **v4.5.0** | Auto-rules | `bdd2pw apply-proposals --interactive` — walks the human through each v3.6 proposal, validates the regex compiles + matches the sample texts, optionally appends to `stepMatcher.ts`. Closes the propose-rules loop. |
378
+ | Nov 2026 | **v4.6.0** | Domain packs | Three more opt-in packs: **fintech** (KYC, AML, trading desks), **real-estate** (MLS, listings, escrow), **hospitality** (PMS, reservations, OTA). ~60 more rules. |
379
+ | Nov 2026 | **v4.7.0** | API v2 | WebSocket assertion rules, multipart file-upload patterns, GraphQL query / mutation / subscription rules. Extends v3.0's API testing surface. |
380
+ | Dec 2026 | **v4.8.0** | Coverage analyzer | `bdd2pw coverage` CLI: which deterministic rules fired on which features across a fleet, per-pack hit rate, dead-rule report, suggestion to retire unused rules. |
381
+ | Jan 2027 | **v4.9.0** | CI/CD templates | Optional scaffolder output: GitHub Actions / GitLab CI / Azure Pipelines / CircleCI starter workflows wired to run the generated specs. Opt-in via `--ci <provider>`. |
382
+ | Feb 2027 | **v5.0.0** | Mobile dialect | Touch / swipe / scroll-into-view rules + responsive viewport assertions. Reuses the same emitter pipeline; opt-in via `domains: ["mobile"]`. Bumps to v5 alongside a small set of default flips (llm-stats + healing become opt-out). Migration guide ships alongside. |
378
383
 
379
384
  Each release is shaped to be self-contained, ship in ~2 weeks, and keep `domains: []` byte-stable for existing users so the upgrade path stays painless.
380
385
 
package/dist/cli.js CHANGED
@@ -88,6 +88,7 @@ program
88
88
  .option("--schema <path>", "v4.0.0 — JSON schema for --gen-data. Object of {column: source} where source is 'faker.X.Y', 'llm:<prompt>', or a literal string.")
89
89
  .option("--rows <n>", "v4.0.0 — row count for --gen-data. Default 20.", "20")
90
90
  .option("--seed <n>", "v4.0.0 — Faker RNG seed for --gen-data reproducibility. Default 42.", "42")
91
+ .option("--fail-on-drift", "v4.2.0 — exit non-zero when the page's DOM has drifted since the previous scaffold (POM header dom-hash mismatch). Useful in CI to gate merges on silent UI regressions.", false)
91
92
  .action(async (feature, opts) => {
92
93
  try {
93
94
  // v2.0 — wire the actual LLM config when --llm is passed. The legacy
@@ -170,8 +171,18 @@ program
170
171
  seed: opts.seed ? Number(opts.seed) : undefined,
171
172
  }
172
173
  : undefined,
174
+ // v4.2.0 — drift gate for CI.
175
+ failOnDrift: opts.failOnDrift === true,
173
176
  });
174
177
  logger_1.logger.info({ result }, "scaffold complete");
178
+ // v4.2.0 — exit non-zero when the user asked to gate on drift
179
+ // and drift was actually detected. Runs AFTER the scaffold has
180
+ // fully written its output so users still see the drift warning
181
+ // in BDD_REVIEW.md and can review the affected fields.
182
+ if (opts.failOnDrift === true && result.driftDetected === true) {
183
+ logger_1.logger.warn("--fail-on-drift set and DOM drift detected; exiting with code 2");
184
+ process.exit(2);
185
+ }
175
186
  }
176
187
  catch (err) {
177
188
  logger_1.logger.error({ err }, "scaffold failed");
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;AACA;;;GAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEH,yCAAoC;AACpC,2CAA6B;AAC7B,mCAAuD;AACvD,+BAAqC;AACrC,mDAAuD;AACvD,2CAAwC;AAExC,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,QAAQ,CAAC;KACd,WAAW,CACV,4EAA4E,CAC7E;KACA,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC,CAAC;AAE/C,OAAO;KACJ,OAAO,CAAC,UAAU,CAAC;KACnB,WAAW,CACV,iFAAiF,CAClF;KACA,QAAQ,CAAC,WAAW,EAAE,iCAAiC,CAAC;KACxD,cAAc,CAAC,aAAa,EAAE,4BAA4B,CAAC;KAC3D,cAAc,CAAC,eAAe,EAAE,qCAAqC,CAAC;KACtE,cAAc,CAAC,cAAc,EAAE,gDAAgD,CAAC;KAChF,MAAM,CAAC,gBAAgB,EAAE,sDAAsD,CAAC;KAChF,MAAM,CAAC,wBAAwB,EAAE,sCAAsC,CAAC;KACxE,MAAM,CAAC,UAAU,EAAE,8BAA8B,EAAE,KAAK,CAAC;KACzD,MAAM,CAAC,wBAAwB,EAAE,+DAA+D,CAAC;KACjG,MAAM,CACL,gBAAgB,EAChB,8FAA8F,CAC/F;KACA,MAAM,CAAC,kBAAkB,EAAE,uFAAuF,CAAC;KACnH,MAAM,CACL,wBAAwB,EACxB,wGAAwG,EACxG,uBAAuB,CACxB;KACA,MAAM,CAAC,qBAAqB,EAAE,6GAA6G,CAAC;KAC5I,MAAM,CAAC,qBAAqB,EAAE,0EAA0E,EAAE,IAAI,CAAC;KAC/G,MAAM,CAAC,oBAAoB,EAAE,0FAA0F,CAAC;KACxH,MAAM,CAAC,uBAAuB,EAAE,0FAA0F,EAAE,KAAK,CAAC;KAClI,MAAM,CAAC,2BAA2B,EAAE,6GAA6G,EAAE,OAAO,CAAC;KAC3J,MAAM,CAAC,+BAA+B,EAAE,8DAA8D,EAAE,OAAO,CAAC;KAChH,MAAM,CAAC,iCAAiC,EAAE,4DAA4D,EAAE,OAAO,CAAC;KAChH,MAAM,CAAC,mBAAmB,EAAE,6CAA6C,CAAC;KAC1E,MAAM,CAAC,WAAW,EAAE,2BAA2B,EAAE,KAAK,CAAC;KACvD,MAAM,CAAC,eAAe,EAAE,wBAAwB,CAAC;KACjD,MAAM,CAAC,aAAa,EAAE,uCAAuC,EAAE,KAAK,CAAC;KACrE,MAAM,CAAC,SAAS,EAAE,+BAA+B,EAAE,KAAK,CAAC;KACzD,MAAM,CACL,gBAAgB,EAChB,kNAAkN,EAClN,KAAK,CACN;KACA,MAAM,CACL,aAAa,EACb,yHAAyH,EACzH,KAAK,CACN;IACD,8DAA8D;IAC9D,mEAAmE;IACnE,mEAAmE;IACnE,oEAAoE;KACnE,MAAM,CACL,kBAAkB,EAClB,kIAAkI,CACnI;IACD,2CAA2C;KAC1C,MAAM,CACL,eAAe,EACf,oNAAoN,CACrN;KACA,MAAM,CACL,YAAY,EACZ,qHAAqH,EACrH,KAAK,CACN;KACA,MAAM,CACL,iBAAiB,EACjB,mIAAmI,CACpI;KACA,MAAM,CACL,YAAY,EACZ,gDAAgD,EAChD,IAAI,CACL;KACA,MAAM,CACL,YAAY,EACZ,qEAAqE,EACrE,IAAI,CACL;KACA,MAAM,CAAC,KAAK,EAAE,OAAe,EAAE,IAAI,EAAE,EAAE;IACtC,IAAI,CAAC;QACH,qEAAqE;QACrE,uEAAuE;QACvE,4DAA4D;QAC5D,MAAM,WAAW,GACf,IAAI,CAAC,GAAG,KAAK,WAAW,IAAI,IAAI,CAAC,GAAG,KAAK,QAAQ,IAAI,IAAI,CAAC,GAAG,KAAK,QAAQ;YACxE,CAAC,CAAC,IAAI,CAAC,GAAG;YACV,CAAC,CAAC,IAAI,CAAC;QACX,MAAM,SAAS,GAAG,WAAW;YAC3B,CAAC,CAAC,CAAC;gBACG,QAAQ,EAAE,WAAgD;gBAC1D,KAAK,EAAE,IAAI,CAAC,QAAQ;gBACpB,aAAa,EAAE,IAAI,CAAC,aAAa;gBACjC,QAAQ,EAAE,IAAI,CAAC,WAAW;oBACxB,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC;oBAC1B,CAAC,CAAC,SAAS;gBACb,SAAS,EAAE,IAAI,CAAC,QAAQ;gBACxB,cAAc,EAAE,IAAI,CAAC,iBAAiB;gBACtC,mBAAmB;gBACnB,aAAa,EAAE,IAAI,CAAC,gBAAgB;oBAClC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC;oBAC/B,CAAC,CAAC,SAAS;gBACb,iBAAiB,EAAE,IAAI,CAAC,oBAAoB;oBAC1C,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC;oBACnC,CAAC,CAAC,SAAS;gBACb,mBAAmB,EAAE,IAAI,CAAC,sBAAsB;oBAC9C,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC;oBACrC,CAAC,CAAC,SAAS;aACd,CAAC;YACN,CAAC,CAAC,SAAS,CAAC;QACd,+DAA+D;QAC/D,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC9B,OAAO,CAAC,IAAI,CACV,8EAA8E,CAC/E,CAAC;QACJ,CAAC;QACD,gDAAgD;QAChD,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjC,OAAO,CAAC,IAAI,CACV,oEAAoE,CACrE,CAAC;QACJ,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,IAAA,gBAAQ,EAAC;YAC5B,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;YAC9B,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;YAC7B,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC1D,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,SAAS;YACT,aAAa,EAAE,IAAI,CAAC,aAAa;YACjC,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,UAAU,EAAE,IAAI,CAAC,UAAU,KAAK,KAAK;YACrC,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,6DAA6D;YAC7D,WAAW,EAAE,IAAI,CAAC,SAAS,KAAK,KAAK;YACrC,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,mEAAmE;YACnE,OAAO,EAAE,IAAI,CAAC,OAAO;gBACnB,CAAC,CAAC,IAAI,CAAC,OAAO;qBACT,KAAK,CAAC,GAAG,CAAC;qBACV,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;qBAC5B,MAAM,CAAC,OAAO,CAAC;gBACpB,CAAC,CAAC,SAAS;YACb,6BAA6B;YAC7B,QAAQ,EAAE,IAAI,CAAC,QAAQ,KAAK,IAAI;YAChC,4DAA4D;YAC5D,gEAAgE;YAChE,4BAA4B;YAC5B,UAAU,EAAE,IAAI,CAAC,IAAI;gBACnB,CAAC,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;gBAC1D,CAAC,CAAC,IAAI,CAAC,OAAO;oBACZ,CAAC,CAAC;wBACE,IAAI,EAAE,WAAoB;wBAC1B,UAAU,EAAE,IAAI,CAAC,MAAM;4BACrB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC;4BAC3B,CAAC,CAAC,SAAS;wBACb,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;wBAC/C,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;qBAChD;oBACH,CAAC,CAAC,SAAS;SAChB,CAAC,CAAC;QACH,eAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,mBAAmB,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,eAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,iBAAiB,CAAC,CAAC;QACzC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;IACjC,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,SAAS,CAAC;KAClB,WAAW,CAAC,qEAAqE,CAAC;KAClF,QAAQ,CAAC,WAAW,EAAE,iCAAiC,CAAC;KACxD,cAAc,CAAC,aAAa,EAAE,aAAa,CAAC;KAC5C,MAAM,CAAC,wBAAwB,EAAE,sCAAsC,CAAC;KACxE,MAAM,CAAC,UAAU,EAAE,8BAA8B,EAAE,KAAK,CAAC;KACzD,MAAM,CAAC,KAAK,EAAE,OAAe,EAAE,IAAI,EAAE,EAAE;IACtC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,IAAA,eAAO,EAAC;YAC3B,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;YAC9B,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,MAAM,EAAE,IAAI,CAAC,MAAM;SACpB,CAAC,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,eAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,gBAAgB,CAAC,CAAC;QACxC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;IACjC,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,YAAY,CAAC;KACrB,WAAW,CAAC,oEAAoE,CAAC;KACjF,cAAc,CAAC,eAAe,EAAE,wBAAwB,CAAC;KACzD,cAAc,CAAC,aAAa,EAAE,gBAAgB,CAAC;KAC/C,cAAc,CAAC,cAAc,EAAE,6BAA6B,CAAC;KAC7D,MAAM,CAAC,wBAAwB,EAAE,sCAAsC,CAAC;KACxE,MAAM,CAAC,UAAU,EAAE,8BAA8B,EAAE,KAAK,CAAC;KACzD,MAAM,CAAC,mBAAmB,EAAE,6CAA6C,CAAC;KAC1E,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;IACrB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,IAAA,iBAAS,EAAC;YAC7B,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;YAC7B,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,SAAS,EAAE,IAAI,CAAC,SAAS;SAC1B,CAAC,CAAC;QACH,eAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,qBAAqB,CAAC,CAAC;IACjD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,eAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,mBAAmB,CAAC,CAAC;QAC3C,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;IACjC,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,gCAAgC,CAAC;KAC7C,MAAM,CAAC,eAAe,EAAE,mBAAmB,EAAE,MAAM,CAAC;KACpD,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;IACrB,MAAM,EAAE,WAAW,EAAE,GAAG,wDAAa,UAAU,GAAC,CAAC;IACjD,MAAM,WAAW,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACjD,CAAC,CAAC,CAAC;AAEL;;GAEG;AACH,SAAS,WAAW,CAAC,GAAY;IAC/B,MAAM,IAAI,GAAI,GAAyB,EAAE,IAAI,IAAI,EAAE,CAAC;IACpD,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,mBAAmB;YACtB,OAAO,CAAC,CAAC;QACX,KAAK,iBAAiB;YACpB,OAAO,CAAC,CAAC;QACX,KAAK,UAAU,CAAC;QAChB,KAAK,iBAAiB;YACpB,OAAO,CAAC,CAAC;QACX,KAAK,yBAAyB;YAC5B,OAAO,CAAC,CAAC;QACX;YACE,OAAO,CAAC,CAAC;IACb,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,OAAO;KACJ,OAAO,CAAC,eAAe,CAAC;KACxB,WAAW,CACV,yGAAyG,CAC1G;KACA,QAAQ,CACP,SAAS,EACT,kEAAkE,CACnE;KACA,MAAM,CACL,cAAc,EACd,iEAAiE,CAClE;KACA,MAAM,CACL,wBAAwB,EACxB,qDAAqD,EACrD,GAAG,CACJ;KACA,MAAM,CAAC,KAAK,EAAE,KAAa,EAAE,GAA8C,EAAE,EAAE;IAC9E,MAAM,MAAM,GAAG,MAAM,IAAA,kBAAY,EAAC;QAChC,SAAS,EAAE,KAAK;QAChB,UAAU,EAAE,GAAG,CAAC,GAAG;QACnB,cAAc,EAAE,GAAG,CAAC,cAAc;YAChC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC;YAC5B,CAAC,CAAC,SAAS;KACd,CAAC,CAAC;IACH,eAAM,CAAC,IAAI,CACT;QACE,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;QACzC,eAAe,EAAE,MAAM,CAAC,eAAe;KACxC,EACD,yBAAyB,CAC1B,CAAC;IACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,KAAK,MAAM,CAAC,gBAAgB,2BAA2B,MAAM,CAAC,UAAU,GAAG;QACzE,SAAS,MAAM,CAAC,eAAe,wBAAwB,CAC1D,CAAC;AACJ,CAAC,CAAC,CAAC;AAEL;;;;;;;GAOG;AACH,OAAO;KACJ,OAAO,CAAC,YAAY,CAAC;KACrB,WAAW,CACV,8EAA8E,CAC/E;KACA,QAAQ,CACP,SAAS,EACT,mFAAmF,CACpF;KACA,MAAM,CACL,cAAc,EACd,sEAAsE,CACvE;KACA,MAAM,CACL,WAAW,EACX,2GAA2G,EAC3G,IAAI,CACL;KACA,MAAM,CAAC,KAAK,EAAE,KAAa,EAAE,GAAmC,EAAE,EAAE;IACnE,MAAM,MAAM,GAAG,MAAM,IAAA,4BAAgB,EAAC;QACpC,SAAS,EAAE,KAAK;QAChB,UAAU,EAAE,GAAG,CAAC,GAAG;QACnB,IAAI,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS;KAC5C,CAAC,CAAC;IACH,eAAM,CAAC,IAAI,CACT;QACE,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,WAAW,EAAE,MAAM,CAAC,WAAW;KAChC,EACD,sBAAsB,CACvB,CAAC;IACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,uBAAuB,MAAM,CAAC,UAAU,YAAY,MAAM,CAAC,WAAW,eAAe,CACtF,CAAC;AACJ,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IAC7C,eAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,OAAO,CAAC,CAAC;IAC/B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * v4.2.0 — DOM snapshot hashing for POM drift detection.
3
+ *
4
+ * Every emitted POM carries a stable sha256 of the accessibility tree it
5
+ * was generated from. On re-scaffold, bdd2pw compares the new snapshot's
6
+ * hash against the one embedded in the existing POM. If they differ, the
7
+ * UI drifted since the last scaffold — and any downstream test failure
8
+ * suddenly has a specific, actionable explanation: "the page shape
9
+ * changed, here are the affected fields."
10
+ *
11
+ * Detection triggers a `DOM DRIFT` entry in `BDD_REVIEW.md`. When
12
+ * `--fail-on-drift` is set (or `failOnDrift: true` programmatically),
13
+ * `scaffold()` returns `driftDetected: true` and the CLI exits with a
14
+ * non-zero code so CI catches silent UI regressions.
15
+ *
16
+ * Why hash the accessibility tree (not the raw DOM):
17
+ * - The tree is what drives POM field selection — irrelevant DOM shifts
18
+ * (whitespace, comment nodes, class-name churn) shouldn't trigger a
19
+ * drift warning.
20
+ * - Deterministic canonical JSON output means identical trees always
21
+ * produce identical hashes, regardless of key order.
22
+ */
23
+ import type { PageSnapshot } from "./mcpClient";
24
+ /**
25
+ * Compute the sha256 hash of a page snapshot's accessibility tree.
26
+ * Returned as lowercase hex (64 chars).
27
+ */
28
+ export declare function computeDomHash(snapshot: PageSnapshot): string;
29
+ /**
30
+ * Build the POM-header comment that carries the DOM hash. Emitted at the
31
+ * top of every generated POM immediately after any `import` block by
32
+ * `index.ts`.
33
+ *
34
+ * Uses a machine-parseable format so `extractDomHashFromPom` can find
35
+ * the value on re-scaffold. The `bdd2pw:dom-hash` tag is unique enough
36
+ * that a naive substring match works.
37
+ */
38
+ export declare function buildDomHashHeader(hash: string, url: string, generatedAt?: Date): string;
39
+ /**
40
+ * Extract the DOM hash from an existing POM file's contents. Returns
41
+ * undefined if no hash header is present (older POM files predating
42
+ * v4.2, or files where the header was hand-removed).
43
+ */
44
+ export declare function extractDomHashFromPom(pomContents: string): string | undefined;
45
+ /**
46
+ * Prepend the DOM-hash header to a POM file's contents. Placed above
47
+ * any existing text (which typically starts with `import` statements).
48
+ * If a header is already present (idempotent re-emit), it is replaced
49
+ * rather than duplicated.
50
+ */
51
+ export declare function prependDomHashHeader(pomContents: string, header: string): string;
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ /**
3
+ * v4.2.0 — DOM snapshot hashing for POM drift detection.
4
+ *
5
+ * Every emitted POM carries a stable sha256 of the accessibility tree it
6
+ * was generated from. On re-scaffold, bdd2pw compares the new snapshot's
7
+ * hash against the one embedded in the existing POM. If they differ, the
8
+ * UI drifted since the last scaffold — and any downstream test failure
9
+ * suddenly has a specific, actionable explanation: "the page shape
10
+ * changed, here are the affected fields."
11
+ *
12
+ * Detection triggers a `DOM DRIFT` entry in `BDD_REVIEW.md`. When
13
+ * `--fail-on-drift` is set (or `failOnDrift: true` programmatically),
14
+ * `scaffold()` returns `driftDetected: true` and the CLI exits with a
15
+ * non-zero code so CI catches silent UI regressions.
16
+ *
17
+ * Why hash the accessibility tree (not the raw DOM):
18
+ * - The tree is what drives POM field selection — irrelevant DOM shifts
19
+ * (whitespace, comment nodes, class-name churn) shouldn't trigger a
20
+ * drift warning.
21
+ * - Deterministic canonical JSON output means identical trees always
22
+ * produce identical hashes, regardless of key order.
23
+ */
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.computeDomHash = computeDomHash;
26
+ exports.buildDomHashHeader = buildDomHashHeader;
27
+ exports.extractDomHashFromPom = extractDomHashFromPom;
28
+ exports.prependDomHashHeader = prependDomHashHeader;
29
+ const node_crypto_1 = require("node:crypto");
30
+ /**
31
+ * Canonical JSON stringify — sorted keys at every object level so
32
+ * identical accessibility trees always hash to the same value even if
33
+ * MCP delivered them with different property ordering.
34
+ */
35
+ function canonicalStringify(value) {
36
+ if (value === null)
37
+ return "null";
38
+ if (typeof value !== "object")
39
+ return JSON.stringify(value);
40
+ if (Array.isArray(value)) {
41
+ return "[" + value.map(canonicalStringify).join(",") + "]";
42
+ }
43
+ const obj = value;
44
+ const keys = Object.keys(obj).sort();
45
+ const parts = keys.map((k) => JSON.stringify(k) + ":" + canonicalStringify(obj[k]));
46
+ return "{" + parts.join(",") + "}";
47
+ }
48
+ /**
49
+ * Compute the sha256 hash of a page snapshot's accessibility tree.
50
+ * Returned as lowercase hex (64 chars).
51
+ */
52
+ function computeDomHash(snapshot) {
53
+ const canonical = canonicalStringify(snapshot.accessibilityTree);
54
+ return (0, node_crypto_1.createHash)("sha256").update(canonical, "utf8").digest("hex");
55
+ }
56
+ /**
57
+ * Build the POM-header comment that carries the DOM hash. Emitted at the
58
+ * top of every generated POM immediately after any `import` block by
59
+ * `index.ts`.
60
+ *
61
+ * Uses a machine-parseable format so `extractDomHashFromPom` can find
62
+ * the value on re-scaffold. The `bdd2pw:dom-hash` tag is unique enough
63
+ * that a naive substring match works.
64
+ */
65
+ function buildDomHashHeader(hash, url, generatedAt = new Date()) {
66
+ return [
67
+ "/**",
68
+ " * @bdd2pw generated " + generatedAt.toISOString(),
69
+ " * @bdd2pw dom-hash " + hash,
70
+ " * @bdd2pw url " + url,
71
+ " *",
72
+ " * The dom-hash line is used by bdd2pw to detect DOM drift on",
73
+ " * re-scaffold. Do not edit it by hand — regenerate with `bdd2pw",
74
+ " * scaffold` or `bdd2pw update-pom` to refresh.",
75
+ " */",
76
+ "",
77
+ ].join("\n");
78
+ }
79
+ /**
80
+ * Extract the DOM hash from an existing POM file's contents. Returns
81
+ * undefined if no hash header is present (older POM files predating
82
+ * v4.2, or files where the header was hand-removed).
83
+ */
84
+ function extractDomHashFromPom(pomContents) {
85
+ const match = pomContents.match(/@bdd2pw dom-hash ([a-f0-9]{64})/);
86
+ return match ? match[1] : undefined;
87
+ }
88
+ /**
89
+ * Prepend the DOM-hash header to a POM file's contents. Placed above
90
+ * any existing text (which typically starts with `import` statements).
91
+ * If a header is already present (idempotent re-emit), it is replaced
92
+ * rather than duplicated.
93
+ */
94
+ function prependDomHashHeader(pomContents, header) {
95
+ // Strip any existing @bdd2pw header block first (idempotent).
96
+ const stripped = pomContents.replace(/^\/\*\*[\s\S]*?@bdd2pw dom-hash[\s\S]*?\*\/\s*\n*/, "");
97
+ return header + stripped;
98
+ }
99
+ //# sourceMappingURL=domHash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domHash.js","sourceRoot":"","sources":["../../src/discovery/domHash.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;;AA4BH,wCAGC;AAWD,gDAiBC;AAOD,sDAKC;AAQD,oDAUC;AAvFD,6CAAyC;AAGzC;;;;GAIG;AACH,SAAS,kBAAkB,CAAC,KAAc;IACxC,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IAClC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC5D,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IAC7D,CAAC;IACD,MAAM,GAAG,GAAG,KAAgC,CAAC;IAC7C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACrC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CACpB,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAC5D,CAAC;IACF,OAAO,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;AACrC,CAAC;AAED;;;GAGG;AACH,SAAgB,cAAc,CAAC,QAAsB;IACnD,MAAM,SAAS,GAAG,kBAAkB,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC;IACjE,OAAO,IAAA,wBAAU,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACtE,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,kBAAkB,CAChC,IAAY,EACZ,GAAW,EACX,cAAoB,IAAI,IAAI,EAAE;IAE9B,OAAO;QACL,KAAK;QACL,uBAAuB,GAAG,WAAW,CAAC,WAAW,EAAE;QACnD,sBAAsB,GAAG,IAAI;QAC7B,iBAAiB,GAAG,GAAG;QACvB,IAAI;QACJ,+DAA+D;QAC/D,kEAAkE;QAClE,iDAAiD;QACjD,KAAK;QACL,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,SAAgB,qBAAqB,CACnC,WAAmB;IAEnB,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACnE,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACtC,CAAC;AAED;;;;;GAKG;AACH,SAAgB,oBAAoB,CAClC,WAAmB,EACnB,MAAc;IAEd,8DAA8D;IAC9D,MAAM,QAAQ,GAAG,WAAW,CAAC,OAAO,CAClC,mDAAmD,EACnD,EAAE,CACH,CAAC;IACF,OAAO,MAAM,GAAG,QAAQ,CAAC;AAC3B,CAAC"}
package/dist/index.js CHANGED
@@ -60,6 +60,7 @@ const locatorPicker_1 = require("./transformers/locatorPicker");
60
60
  const pomResolver_1 = require("./transformers/pomResolver");
61
61
  const stepMatcher_1 = require("./transformers/stepMatcher");
62
62
  const facade_1 = require("./emitters/facade");
63
+ const domHash_1 = require("./discovery/domHash");
63
64
  const tscRunner_1 = require("./validate/tscRunner");
64
65
  const reviewReport_1 = require("./reports/reviewReport");
65
66
  const metaSidecar_1 = require("./reports/metaSidecar");
@@ -148,6 +149,10 @@ async function scaffold(opts) {
148
149
  // lands and rule-only steps (`I am on the page`, `should be redirected`)
149
150
  // still produce useful output even when the URL isn't reachable.
150
151
  let elements = [];
152
+ // v4.2.0 — DOM-hash tracking. Snapshot is hoisted here so the hash can
153
+ // be embedded in the POM header (§8 below). undefined if discovery
154
+ // failed or was skipped, which suppresses the header.
155
+ let capturedSnapshot;
151
156
  if (opts.noDiscovery) {
152
157
  reviewItems.push({
153
158
  severity: "info",
@@ -156,13 +161,13 @@ async function scaffold(opts) {
156
161
  }
157
162
  else {
158
163
  try {
159
- const snapshot = await (0, mcpClient_1.scanPage)({
164
+ capturedSnapshot = await (0, mcpClient_1.scanPage)({
160
165
  url: opts.url,
161
166
  storageState: opts.storageState,
162
167
  headed: opts.headed,
163
168
  snapshotFile: opts.snapshotFile,
164
169
  });
165
- elements = (0, snapshotParser_1.parseSnapshot)(snapshot);
170
+ elements = (0, snapshotParser_1.parseSnapshot)(capturedSnapshot);
166
171
  }
167
172
  catch (err) {
168
173
  reviewItems.push({
@@ -460,6 +465,40 @@ async function scaffold(opts) {
460
465
  }
461
466
  }
462
467
  const filesWritten = [...scaffoldResult.filesWritten];
468
+ // v4.2.0 — drift detection. Runs BEFORE the emit so a warning lands
469
+ // even if the emit itself fails partway through. `driftDetected` is
470
+ // surfaced in the return value for --fail-on-drift CI behaviour.
471
+ let driftDetected = false;
472
+ const currentDomHash = capturedSnapshot ? (0, domHash_1.computeDomHash)(capturedSnapshot) : undefined;
473
+ if (currentDomHash && decision.existing) {
474
+ const existingPomContents = await fs
475
+ .readFile(decision.existing.filePath, "utf8")
476
+ .catch(() => undefined);
477
+ if (existingPomContents) {
478
+ const previousHash = (0, domHash_1.extractDomHashFromPom)(existingPomContents);
479
+ if (previousHash && previousHash !== currentDomHash) {
480
+ driftDetected = true;
481
+ reviewItems.push({
482
+ severity: "warn",
483
+ message: "DOM DRIFT detected: the page's accessibility tree has changed since the last scaffold. " +
484
+ "Previous hash " +
485
+ previousHash.slice(0, 12) +
486
+ "… → new hash " +
487
+ currentDomHash.slice(0, 12) +
488
+ "…. Any pinned locators may now point at moved or missing elements.",
489
+ suggestion: "Review the diff between the last snapshot and the current one, then re-run the affected specs to confirm behaviour. Pass --fail-on-drift in CI to gate merges on this signal.",
490
+ file: finalPom.filePath,
491
+ });
492
+ }
493
+ else if (!previousHash) {
494
+ reviewItems.push({
495
+ severity: "info",
496
+ message: "Existing POM predates v4.2 dom-hash tracking. First scaffold with v4.2+ will embed a baseline hash; future runs will detect drift.",
497
+ file: finalPom.filePath,
498
+ });
499
+ }
500
+ }
501
+ }
463
502
  if (!opts.dryRun) {
464
503
  // 8) Emit Page Object
465
504
  const pomEmitMode = decision.decision === "CREATE" ? "create" : "augment";
@@ -472,8 +511,15 @@ async function scaffold(opts) {
472
511
  selfHealingShim: opts.selfHealing,
473
512
  });
474
513
  reviewItems.push(...pomEmit.warnings.map((w) => ({ ...w, file: finalPom.filePath })));
514
+ // v4.2.0 — prepend DOM-hash header if we have a snapshot. Header is
515
+ // idempotent (replaces any prior header block from a previous
516
+ // scaffold) so re-emitting doesn't stack duplicates.
517
+ let pomContents = pomEmit.contents;
518
+ if (currentDomHash) {
519
+ pomContents = (0, domHash_1.prependDomHashHeader)(pomContents, (0, domHash_1.buildDomHashHeader)(currentDomHash, opts.url));
520
+ }
475
521
  await fs.ensureDir(path.dirname(finalPom.filePath));
476
- await fs.writeFile(finalPom.filePath, pomEmit.contents, "utf8");
522
+ await fs.writeFile(finalPom.filePath, pomContents, "utf8");
477
523
  filesWritten.push(finalPom.filePath);
478
524
  // 9) Emit spec file. pageObjectFileName returns "login.page.ts"; strip
479
525
  // the trailing ".ts" for both the spec file stem and the import path.
@@ -577,7 +623,7 @@ async function scaffold(opts) {
577
623
  // Close the SQLite cache handle if we opened one.
578
624
  if (llm?.close)
579
625
  await llm.close().catch(() => undefined);
580
- return { filesWritten, reviewItems, tscErrorCount, reviewReportPath };
626
+ return { filesWritten, reviewItems, tscErrorCount, reviewReportPath, driftDetected };
581
627
  }
582
628
  /**
583
629
  * Construct the final POM IR based on the resolver decision.