@decocms/parity 0.11.2 → 0.11.4

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
@@ -5,6 +5,41 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
+ ## [0.11.4](https://github.com/decocms/parity/compare/v0.11.3...v0.11.4) (2026-06-17)
9
+
10
+ ### Added
11
+
12
+ * **`cartOpenedIndicator` selector key.** Used by the upcoming post-add-to-cart validator — list of selectors that, when matched and visible after clicking buy, confirm the cart actually opened (notification toast, drawer, etc). Default candidates include `[aria-label='Fechar notificação']` / `[aria-label='Fechar carrinho']` patterns from Deco TanStack plus generic role/`[role='dialog']` fallbacks.
13
+
14
+ ### Fixed
15
+
16
+ * **Deco TanStack selectors covered before the LLM is even called.** Defaults extended for the bagaggio-class storefront — patterns observed live against `bagaggio-tanstack.deco-cx.workers.dev`:
17
+ - **buyButton:** lowercase `comprar` variants + `button[type='button']:has-text('comprar')` (the CSS renders uppercase via `text-transform`; the markup is lowercase).
18
+ - **minicartTrigger:** `[aria-label='Sacola']` + lowercase aria substring.
19
+ - **cepInputPdp:** `input[name='postalCode']`, `#postalCodeInput`, `input[inputMode='numeric'][maxLength='8']`.
20
+ - **sizeSwatch:** `[aria-label*='Tamanho '][aria-label*='Disponível']` — the " - Disponível" suffix means in-stock; sold-out variants don't carry it.
21
+ * **HTML compaction passed to the LLM is now actually useful.** Both `compactHtmlForSelectors` and `compactHtmlForRecovery` now:
22
+ - Strip the Tailwind utility-class soup (`class="w-full h-12 flex items-center bg-primary ..."` becomes `class=""`) so the LLM sees semantic anchors (`data-*`, `aria-*`, `role`, semantic class names) instead of drowning in token noise.
23
+ - Strip URL-encoded JSON in `data-event` / `data-track` / `data-analytics` attrs (Deco sites carry multi-kb analytics blobs there).
24
+ - Drop inline `style=""`.
25
+ - Add `data-product-list` and `[aria-label]` to the kept-element whitelist so the Deco TanStack patterns survive the compaction.
26
+ * **LLM prompts know about Deco TanStack now.** `discover-selectors` and `recover-step` system prompts both document the bagaggio-class patterns explicitly (product card via `aria-label='view product'`, lowercase CTA text, `[aria-label='Sacola']` minicart, `[aria-label='Tamanho X - Disponível']` size swatches, `name='postalCode'` CEP input) so when defaults miss the LLM has structured guidance instead of having to re-discover the pattern each time.
27
+
28
+ ### Verification (live against bagaggio)
29
+
30
+ Before this PR: journey stopped at step 3 (`enter-pdp` — fixed in 0.11.3) and again at step 4 (`select-variant`).
31
+ After this PR: journey completes steps 1-5 (`visit-home`, `navigate-plp`, `enter-pdp`, `select-variant`, `shipping-calc-pdp`). Step 6 (`add-to-cart`) now surfaces a real bug — "Selecione um tamanho" is still visible after the variant click, meaning the variant selector matched a tab/expand element instead of the actual radio. Follow-up tracked separately.
32
+
33
+ ## [0.11.3](https://github.com/decocms/parity/compare/v0.11.2...v0.11.3) (2026-06-17)
34
+
35
+ ### Fixed
36
+
37
+ * **`productCard` defaults now match Deco TanStack PLPs (#102).** Live testing against `bagaggio-tanstack.deco-cx.workers.dev` showed every purchase-journey aborting at step 3 (`enter-pdp` → "no product card found, recovery exhausted"). Root cause: the Deco TanStack PLP uses `<a aria-label="view product" href="/<product>/p">` (no `/p/` subpath, no `[data-product-card]` attr) — none of the seven baked-in candidates matched. Added five new defaults covering the Deco TanStack pattern: `[data-product-list] a[aria-label='view product']`, `[data-product-list] a[href$='/p']`, `[data-product-list] a[href*='/p?']`, `a[aria-label='view product']`, plus path-suffix variants. Journey on bagaggio now reaches step 6 (add-to-cart) where it surfaces a real bug — variant selection — instead of bailing at step 3.
38
+
39
+ ### Changed
40
+
41
+ * **Default tier for selector-related features back to Sonnet.** PR #66 defaulted selector-discovery / step-recovery / plp-matching / pdp-matching to Haiku 4.5 for cost savings. Live testing showed Sonnet is the safer default for structural-reasoning calls — Haiku-discovered selectors didn't always match on real sites and Haiku-recovery couldn't find alternatives. `search-terms` (pure classification) stays on Haiku. Users who want the previous cheap behavior can opt in with `--llm-tier-default haiku`.
42
+
8
43
  ## [0.11.2](https://github.com/decocms/parity/compare/v0.11.1...v0.11.2) (2026-06-17)
9
44
 
10
45
  ### Added
package/README.md CHANGED
@@ -68,7 +68,7 @@ Three providers, auto-detected in this order — **none required** if you have t
68
68
  2. `OPENROUTER_API_KEY` — OpenRouter
69
69
  3. **Local `claude` CLI** — uses [`@anthropic-ai/claude-agent-sdk`](https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk); goes through your existing Claude plan. No env vars needed
70
70
 
71
- Force with `--llm <anthropic|openrouter|claude-code|none|auto>`. Override per-feature with `--llm-model selector-discovery=claude-haiku-4-5,visual-diff=claude-opus-4-7`. Default tiers: Haiku for short tasks (selector discovery, classification), Sonnet for vision/aggregation, Opus for `explain`.
71
+ Force with `--llm <anthropic|openrouter|claude-code|none|auto>`. Override per-feature with `--llm-model visual-diff=claude-opus-4-7,explain=claude-opus-4-7`, or flatten everything to one tier with `--llm-tier-default <haiku|sonnet|opus>`. Default tiers: **Sonnet** for selector discovery / recovery / matching / vision / aggregation (the structural-reasoning calls), **Haiku** for short copy-list classification (`search-terms`), **Opus** for `explain`. Cost-conscious? Try `--llm-tier-default haiku` and bump features back to Sonnet selectively if you hit selector misses.
72
72
 
73
73
  Without any provider, the CLI still runs and outputs raw check results — only the LLM-tagged tabs (Visual Diff, LLM Prompt) hide themselves and a banner explains why.
74
74
 
package/dist/cli.js CHANGED
@@ -84,11 +84,11 @@ var init_models = __esm(() => {
84
84
  "explain"
85
85
  ];
86
86
  DEFAULT_FEATURE_TIER = {
87
- "selector-discovery": "haiku",
88
- "step-recovery": "haiku",
87
+ "selector-discovery": "sonnet",
88
+ "step-recovery": "sonnet",
89
89
  "search-terms": "haiku",
90
- "plp-matching": "haiku",
91
- "pdp-matching": "haiku",
90
+ "plp-matching": "sonnet",
91
+ "pdp-matching": "sonnet",
92
92
  "section-understanding": "sonnet",
93
93
  "visual-diff": "sonnet",
94
94
  "issue-aggregation": "sonnet",
@@ -4441,6 +4441,7 @@ var ParityRc = z.object({
4441
4441
  quantityIncrement: z.string().optional(),
4442
4442
  quantityInput: z.string().optional(),
4443
4443
  minicartCount: z.string().optional(),
4444
+ cartOpenedIndicator: z.string().optional(),
4444
4445
  searchTrigger: z.string().optional(),
4445
4446
  searchInput: z.string().optional(),
4446
4447
  searchSuggestions: z.string().optional(),
@@ -11247,7 +11248,26 @@ function compactHtmlForRecovery(html, maxChars = 12000) {
11247
11248
  try {
11248
11249
  const $ = cheerio6.load(html);
11249
11250
  $("script, style, noscript, svg, picture source, link, meta").remove();
11250
- const allowed = $("header, nav, main, footer, dialog, [role='dialog'], form, button, a, input, [data-buy-button], [data-checkout], [data-minicart], [role='button']").map((_, el) => $.html(el)).get().join(`
11251
+ $("*").each((_, el) => {
11252
+ const attrs = el.attribs ?? {};
11253
+ for (const name of Object.keys(attrs)) {
11254
+ if (name === "data-event" || name === "data-track" || name === "data-analytics") {
11255
+ const value = attrs[name] ?? "";
11256
+ if (value.length > 100)
11257
+ attrs[name] = "[…]";
11258
+ }
11259
+ if (name === "style")
11260
+ delete attrs[name];
11261
+ }
11262
+ if (attrs.class) {
11263
+ const tokens = attrs.class.split(/\s+/).filter(Boolean);
11264
+ const kept = tokens.filter((t) => isSemanticClassToken(t));
11265
+ attrs.class = kept.join(" ");
11266
+ if (!attrs.class)
11267
+ delete attrs.class;
11268
+ }
11269
+ });
11270
+ const allowed = $("header, nav, main, footer, dialog, [role='dialog'], form, button, a, input, [data-buy-button], [data-checkout], [data-minicart], [data-product-list], [role='button'], [aria-label]").map((_, el) => $.html(el)).get().join(`
11251
11271
  `);
11252
11272
  const truncated = allowed.length > maxChars ? `${allowed.slice(0, maxChars)}
11253
11273
  <!-- TRUNCATED -->` : allowed;
@@ -11256,6 +11276,23 @@ function compactHtmlForRecovery(html, maxChars = 12000) {
11256
11276
  return html.slice(0, maxChars);
11257
11277
  }
11258
11278
  }
11279
+ function isSemanticClassToken(token) {
11280
+ if (token.length === 0 || token.length > 40)
11281
+ return false;
11282
+ if (token.includes(":") || token.includes("[") || token.includes("/"))
11283
+ return false;
11284
+ if (/^[a-z]{1,4}(-[a-z]{1,3})?-\d/.test(token))
11285
+ return false;
11286
+ if (/^[a-z]{1,2}\d+$/.test(token))
11287
+ return false;
11288
+ if (/^(w|h|p|m|px|py|pt|pb|pl|pr|mt|mb|ml|mr|gap|flex|grid|text|bg|border|rounded|shadow|opacity|cursor|min|max)-/i.test(token) && !/^(text|bg|border)-(primary|secondary|accent|brand|warning|error|success|muted|base|surface)$/i.test(token)) {
11289
+ return false;
11290
+ }
11291
+ if (/^(flex|grid|block|inline|hidden|relative|absolute|fixed|static|sticky|visible|invisible|truncate|uppercase|lowercase|capitalize|italic|underline|overline|line-through|no-underline|antialiased|subpixel-antialiased|whitespace|break-words|break-all|object-cover|object-contain|object-fill|object-none|object-scale-down|select-none|select-text|select-all|pointer-events-none|pointer-events-auto|appearance-none|resize-none|leading-none|font-bold|font-medium|font-semibold|font-light|tracking-wide|tracking-tight)$/.test(token)) {
11292
+ return false;
11293
+ }
11294
+ return true;
11295
+ }
11259
11296
  var RECOVERY_SYSTEM_PROMPT = `
11260
11297
  Você ajuda a recuperar passos de teste E2E (Playwright) que falharam contra sites de e-commerce.
11261
11298
 
@@ -11277,22 +11314,36 @@ REGRAS CRÍTICAS:
11277
11314
  3. **Se o step é 'shipping-calc-cart' ou 'shipping-calc-pdp':**
11278
11315
  - O alvo é um \`<input>\` (não button/link) visível com label/placeholder mencionando "CEP", "Frete", "Calcular", "Entrega", "Zip".
11279
11316
  - Prefira inputs cujo container/pai está visível (não escondido em accordion fechado).
11317
+ - **Pattern Deco TanStack**: \`<input id="postalCodeInput" name="postalCode" inputMode="numeric" maxLength="8" pattern="\\\\d{8}">\`. Selectors válidos: \`input[name='postalCode']\`, \`#postalCodeInput\`, \`input[inputMode='numeric'][maxLength='8']\`.
11280
11318
 
11281
- 4. **Seletores preferidos** (nesta ordem):
11319
+ 4. **Se o step é 'enter-pdp' ou 'navigate-plp' e o alvo é um product card:**
11320
+ - **Pattern Deco TanStack**: \`<div data-product-list><div class="card"><a aria-label="view product" href="<product-name>/p">...</a></div></div>\`.
11321
+ - Selectors válidos: \`[data-product-list] a[aria-label='view product']\`, \`a[aria-label='view product']\`, \`[data-product-list] a[href$='/p']\`.
11322
+
11323
+ 5. **Se o step é 'select-variant' (tamanho/cor obrigatório no PDP):**
11324
+ - **Pattern Deco TanStack**: \`<button aria-label="Tamanho P - Disponível">\` ou \`<button aria-label="Tamanho M - Disponível">\`. Sufixo " - Disponível" = em estoque; "Esgotado" / sem sufixo = indisponível.
11325
+ - Selectors válidos: \`[aria-label*='Tamanho '][aria-label*='Disponível']\`, \`button[aria-label*='Tamanho ']:not([disabled])\`.
11326
+ - Para cor: \`[aria-label*='Cor '][aria-label*='Disponível']\`.
11327
+
11328
+ 6. **Se o step é 'add-to-cart' / 'comprar':**
11329
+ - **Pattern Deco TanStack**: o markup é \`<button type="button">comprar</button>\` em lowercase (CSS faz uppercase via \`text-transform\`). Use \`button:has-text('comprar')\` ou \`button[type='button']:has-text('comprar')\`.
11330
+ - **NUNCA confunda com "comprar junto"** (botão de cross-sell adjacente) — qualifique pelo texto exato ou pelo \`type='submit'\`.
11331
+
11332
+ 7. **Seletores preferidos** (nesta ordem):
11282
11333
  - Text-based Playwright: \`button:has-text('Finalizar')\`, \`a:has-text('Continuar para pagamento')\`
11283
- - data-attributes específicos: \`[data-checkout-button]\`, \`[data-cart-finalize]\`, \`[data-fs-cart-button]\`
11284
- - ARIA: \`[aria-label*='finalizar' i]\`, \`[role='button'][aria-label*='checkout' i]\`
11334
+ - data-attributes específicos: \`[data-checkout-button]\`, \`[data-cart-finalize]\`, \`[data-fs-cart-button]\`, \`[data-product-list]\`
11335
+ - ARIA: \`[aria-label*='finalizar' i]\`, \`[role='button'][aria-label*='checkout' i]\`, \`[aria-label='Sacola']\` (Deco TanStack minicart)
11285
11336
  - Classes específicas de plataforma: \`.vtex-minicart-2-x-checkoutButton\`, \`.fs-cart__checkout\`
11286
11337
  - Por último, escopo hierárquico curto: \`[role='dialog'] button.cta\`, \`.minicart-drawer button[type='submit']\`
11287
11338
 
11288
- 5. **EVITE:**
11339
+ 8. **EVITE:**
11289
11340
  - IDs hash-gerados (\`#r-abc123\`)
11290
- - Tailwind utility classes encadeadas (\`.flex.items-center.bg-blue\`)
11341
+ - Tailwind utility classes encadeadas (\`.flex.items-center.bg-blue\`) — o HTML compactado já tirou essas, então se você só vê tokens utility a class foi removida e essa não é uma âncora confiável
11291
11342
  - Seletores genéricos sem qualificador (\`a[href*=...]\`, \`button[type=submit]\` sozinho)
11292
11343
  - Seletores que casam o ELEMENTO ERRADO em outra parte do DOM (ex: link de "checkout your reviews" no footer)
11293
11344
  - Seletores muito profundos (>4 níveis hierárquicos)
11294
11345
 
11295
- 6. **Quando incerto, retorne selector="" (string vazia).** É preferível que o flow declare a falha honestamente a chutar um seletor errado que apenas clica em coisa aleatória.
11346
+ 9. **Quando incerto, retorne selector="" (string vazia).** É preferível que o flow declare a falha honestamente a chutar um seletor errado que apenas clica em coisa aleatória. Em especial: se a página parece ser uma LANDING PAGE (sem buy form, sem schema:Product, sem preço) e o step pede um buy/variant/cep selector, retorne string vazia — não force uma sugestão.
11296
11347
 
11297
11348
  Responda SEMPRE via tool_use suggest_recovery.
11298
11349
  `.trim();
@@ -11440,7 +11491,13 @@ REGRAS:
11440
11491
  - Se o HTML tem classes \`vtex-\` → VTEX. Use convenções VTEX (\`.vtex-cart-item, .vtex-minicart\`).
11441
11492
  - Se tem \`fs-\` ou \`data-fs-*\` → FastStore/VTEX IO.
11442
11493
  - Se tem \`shopify-\` ou usa Liquid markup → Shopify.
11443
- - Se tem \`data-deco\`, \`data-section\`, \`data-product-card\` → Deco storefront (variantes custom).
11494
+ - Se tem \`data-deco\`, \`data-section\`, \`data-product-card\` → Deco storefront classic (Fresh/Preact).
11495
+ - Se tem \`data-product-list\`, \`<a aria-label="view product">\`, e Tailwind utility classes
11496
+ (\`card w-full card-compact ...\`) → **Deco TanStack** (React port). Patterns conhecidos:
11497
+ * product card: \`[data-product-list] a[aria-label='view product']\` ou \`a[href$='/p']\`
11498
+ * minicart trigger: \`[aria-label='Sacola']\` (note: nome em português)
11499
+ * post-add-to-cart drawer/toast: \`[aria-label='Fechar carrinho']\` ou \`[aria-label='Fechar notificação']\`
11500
+ * CTAs ficam lowercase no markup (\`comprar\`, não \`Comprar\`) com text-transform via CSS
11444
11501
  - Caso desconhecido, use heurísticas genéricas.
11445
11502
 
11446
11503
  4. Para "cep_input_pdp" e "cep_input_cart": retorne string vazia se não existir esse recurso na plataforma.
@@ -11480,6 +11537,25 @@ function compactHtmlForSelectors(html, maxChars = 30000) {
11480
11537
  const $ = cheerio7.load(html);
11481
11538
  $("script, style, noscript, svg, picture source, link[rel='stylesheet']").remove();
11482
11539
  $("[type='application/ld+json']").remove();
11540
+ $("*").each((_, el) => {
11541
+ const attrs = el.attribs ?? {};
11542
+ for (const name of Object.keys(attrs)) {
11543
+ if (name === "data-event" || name === "data-track" || name === "data-analytics") {
11544
+ const value = attrs[name] ?? "";
11545
+ if (value.length > 100)
11546
+ attrs[name] = "[…]";
11547
+ }
11548
+ if (name === "style")
11549
+ delete attrs[name];
11550
+ }
11551
+ if (attrs.class) {
11552
+ const tokens = attrs.class.split(/\s+/).filter(Boolean);
11553
+ const kept = tokens.filter((t) => isSemanticClass(t));
11554
+ attrs.class = kept.join(" ");
11555
+ if (!attrs.class)
11556
+ delete attrs.class;
11557
+ }
11558
+ });
11483
11559
  const sections = [];
11484
11560
  const head = $("head").clone();
11485
11561
  head.find("title, meta[name='generator'], meta[name='vtex'], meta[name='platform'], link[rel='canonical']").each((_, el) => {
@@ -11488,7 +11564,7 @@ function compactHtmlForSelectors(html, maxChars = 30000) {
11488
11564
  $("header, nav, [role='banner']").each((_, el) => {
11489
11565
  sections.push($.html(el));
11490
11566
  });
11491
- const shelf = $("[data-product-card], [data-deco='view-product'], article a[href*='/p/'], article a[href*='/products/']").closest("section, ul, div").first();
11567
+ const shelf = $("[data-product-card], [data-deco='view-product'], [data-product-list], article a[href*='/p/'], article a[href*='/products/'], a[aria-label='view product']").closest("section, ul, div").first();
11492
11568
  if (shelf.length > 0)
11493
11569
  sections.push($.html(shelf));
11494
11570
  $("form").each((_, el) => {
@@ -11508,6 +11584,23 @@ function compactHtmlForSelectors(html, maxChars = 30000) {
11508
11584
  return html.slice(0, maxChars);
11509
11585
  }
11510
11586
  }
11587
+ function isSemanticClass(token) {
11588
+ if (token.length === 0 || token.length > 40)
11589
+ return false;
11590
+ if (token.includes(":") || token.includes("[") || token.includes("/"))
11591
+ return false;
11592
+ if (/^[a-z]{1,4}(-[a-z]{1,3})?-\d/.test(token))
11593
+ return false;
11594
+ if (/^[a-z]{1,2}\d+$/.test(token))
11595
+ return false;
11596
+ if (/^(w|h|p|m|px|py|pt|pb|pl|pr|mt|mb|ml|mr|gap|flex|grid|text|bg|border|rounded|shadow|opacity|cursor|min|max)-/i.test(token) && !/^(text|bg|border)-(primary|secondary|accent|brand|warning|error|success|muted|base|surface)$/i.test(token)) {
11597
+ return false;
11598
+ }
11599
+ if (/^(flex|grid|block|inline|hidden|relative|absolute|fixed|static|sticky|visible|invisible|truncate|uppercase|lowercase|capitalize|italic|underline|overline|line-through|no-underline|antialiased|subpixel-antialiased|whitespace|break-words|break-all|object-cover|object-contain|object-fill|object-none|object-scale-down|select-none|select-text|select-all|pointer-events-none|pointer-events-auto|appearance-none|resize-none|leading-none|font-bold|font-medium|font-semibold|font-light|tracking-wide|tracking-tight)$/.test(token)) {
11600
+ return false;
11601
+ }
11602
+ return true;
11603
+ }
11511
11604
  async function discoverSelectorsFromUrl(url, html, opts = {}) {
11512
11605
  const host = safeHost(url);
11513
11606
  const cacheDir = opts.cacheDir ?? CACHE_DIR;
@@ -11704,6 +11797,7 @@ var SelectorKey = z2.enum([
11704
11797
  "quantityIncrement",
11705
11798
  "quantityInput",
11706
11799
  "minicartCount",
11800
+ "cartOpenedIndicator",
11707
11801
  "searchTrigger",
11708
11802
  "searchInput",
11709
11803
  "searchSuggestions",
@@ -11876,21 +11970,32 @@ var DEFAULT_SELECTORS = {
11876
11970
  "[data-deco='view-product'] a",
11877
11971
  "[data-testid='product-card'] a",
11878
11972
  ".product-card a",
11973
+ "[data-product-list] a[aria-label='view product']",
11974
+ "[data-product-list] a[href$='/p']",
11975
+ "[data-product-list] a[href*='/p?']",
11976
+ "a[aria-label='view product']",
11879
11977
  "article a[href*='/p/']",
11880
- "a[href*='/products/']"
11978
+ "a[href*='/products/']",
11979
+ "main a[href$='/p']",
11980
+ "main a[href*='/p?']"
11881
11981
  ],
11882
11982
  buyButton: [
11983
+ "[data-buy-button]",
11984
+ "[data-testid='add-to-cart']",
11985
+ "button[type='button']:has-text('comprar')",
11986
+ "button[type='submit']:has-text('comprar')",
11883
11987
  "button:has-text('Comprar')",
11988
+ "button:has-text('comprar')",
11884
11989
  "button:has-text('Adicionar')",
11885
11990
  "button:has-text('Add to cart')",
11886
11991
  "button:has-text('Add to bag')",
11887
- "[data-buy-button]",
11888
- "[data-testid='add-to-cart']",
11889
11992
  "button[type='submit']:has-text('Comprar')"
11890
11993
  ],
11891
11994
  minicartTrigger: [
11892
11995
  "[data-minicart-trigger]",
11893
11996
  "[data-testid='minicart-trigger']",
11997
+ "[aria-label='Sacola']",
11998
+ "[aria-label*='sacola' i]",
11894
11999
  "[aria-label*='carrinho' i]",
11895
12000
  "[aria-label*='cart' i]",
11896
12001
  "header button:has([data-cart-icon])",
@@ -11900,6 +12005,10 @@ var DEFAULT_SELECTORS = {
11900
12005
  "input[name='shipping-zipcode']",
11901
12006
  "input[name='zipcode']",
11902
12007
  "input[name='cep']",
12008
+ "input[name='postalCode']",
12009
+ "#postalCodeInput",
12010
+ "input[inputMode='numeric'][maxLength='8']",
12011
+ "input[pattern='\\\\d{8}']",
11903
12012
  "input[placeholder*='CEP' i]",
11904
12013
  "input[placeholder*='Postal' i]",
11905
12014
  "[data-shipping-input] input"
@@ -11930,6 +12039,9 @@ var DEFAULT_SELECTORS = {
11930
12039
  sizeSwatch: [
11931
12040
  "[data-size]:not([disabled]):not([aria-disabled='true']):not(.unavailable):not(.sold-out)",
11932
12041
  "[data-variant-size]:not([disabled]):not([aria-disabled='true']):not(.unavailable)",
12042
+ "[aria-label^='Tamanho ']:has-text('Disponível')",
12043
+ "[aria-label*='Tamanho '][aria-label*='Disponível']",
12044
+ "[aria-label*='Tamanho ']:not([disabled]):not([aria-disabled='true'])",
11933
12045
  "button[aria-label*='tamanho' i]:not([aria-disabled='true']):not([disabled])",
11934
12046
  "button[aria-label*='size' i]:not([aria-disabled='true']):not([disabled])",
11935
12047
  "[data-testid='size-selector'] button:not([disabled]):not(.unavailable)",
@@ -12034,6 +12146,21 @@ var DEFAULT_SELECTORS = {
12034
12146
  "[class*='searchSuggestions']:visible",
12035
12147
  "[class*='suggestions' i] [class*='item' i]"
12036
12148
  ],
12149
+ cartOpenedIndicator: [
12150
+ "[aria-label='Fechar notificação']:visible",
12151
+ "[aria-label*='notificação' i]:visible",
12152
+ "[role='status']:visible",
12153
+ "[role='alert']:visible",
12154
+ "[aria-label='Fechar carrinho']:visible",
12155
+ "[aria-label*='fechar carrinho' i]:visible",
12156
+ "[role='dialog']:has([class*='cart' i]):visible",
12157
+ "[role='dialog']:has([class*='minicart' i]):visible",
12158
+ "[data-cart-drawer][aria-expanded='true']",
12159
+ "[data-minicart][aria-expanded='true']",
12160
+ "[data-cart-drawer]:not([hidden]):visible",
12161
+ "[role='dialog']:has-text('adicionado')",
12162
+ "[role='dialog']:has-text('added')"
12163
+ ],
12037
12164
  cartItemRow: [
12038
12165
  "[data-cart-item]",
12039
12166
  "[data-testid='cart-item']",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decocms/parity",
3
- "version": "0.11.2",
3
+ "version": "0.11.4",
4
4
  "description": "E2E parity validator for site migrations. Compares prod vs cand and reports UI, functional, SEO, visual, and Web Vitals deltas with an LLM-ranked HTML report.",
5
5
  "type": "module",
6
6
  "license": "MIT",