@vtex/faststore 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (232) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/dist/actions.json +181 -0
  3. package/dist/catalog.json +1543 -495
  4. package/dist/client.d.ts +2 -1
  5. package/dist/client.js +2 -1
  6. package/dist/components.d.ts +28 -7
  7. package/dist/components.js +31 -12
  8. package/dist/docs/access-code-sign-in.md +1 -1
  9. package/dist/docs/address-summary.md +24 -3
  10. package/dist/docs/alert-dialog.md +204 -0
  11. package/dist/docs/billing-address.md +284 -0
  12. package/dist/docs/card-entry.md +250 -0
  13. package/dist/docs/cardholder-document.md +142 -0
  14. package/dist/docs/client-preferences-fieldset.md +148 -0
  15. package/dist/docs/client-preferences.md +2 -1
  16. package/dist/docs/client-profile-fieldset.md +184 -0
  17. package/dist/docs/client-profile.md +1 -1
  18. package/dist/docs/document-field.md +58 -4
  19. package/dist/docs/field.md +317 -0
  20. package/dist/docs/form.md +307 -0
  21. package/dist/docs/identity-edit.md +6 -6
  22. package/dist/docs/identity-summary.md +5 -5
  23. package/dist/docs/installments.md +248 -0
  24. package/dist/docs/my-account-profile.md +1 -0
  25. package/dist/docs/order-placed-payment-summary.md +138 -0
  26. package/dist/docs/payment-method-badges.md +232 -0
  27. package/dist/docs/payment-methods.md +1 -0
  28. package/dist/docs/payment.md +218 -0
  29. package/dist/docs/phone-field.md +58 -4
  30. package/dist/docs/place-order.md +157 -0
  31. package/dist/docs/save-card.md +186 -0
  32. package/dist/docs/saved-cards.md +218 -0
  33. package/dist/docs/select.md +465 -0
  34. package/dist/docs/shipping-address.md +110 -258
  35. package/dist/index.d.ts +4 -1
  36. package/dist/internal/client.d.ts +3 -1
  37. package/dist/internal/client.js +3 -1
  38. package/dist/matter/access-code-sign-in/access-code-sign-in.js +9 -8
  39. package/dist/matter/access-code-sign-in/access-code-sign-in.js.map +1 -1
  40. package/dist/matter/billing-address/billing-address.d.ts +165 -0
  41. package/dist/matter/billing-address/billing-address.js +408 -0
  42. package/dist/matter/billing-address/billing-address.js.map +1 -0
  43. package/dist/matter/card-entry/card-capture.d.ts +25 -0
  44. package/dist/matter/card-entry/card-capture.js +25 -0
  45. package/dist/matter/card-entry/card-capture.js.map +1 -0
  46. package/dist/matter/card-entry/card-entry.d.ts +120 -0
  47. package/dist/matter/card-entry/card-entry.js +832 -0
  48. package/dist/matter/card-entry/card-entry.js.map +1 -0
  49. package/dist/matter/cardholder-document/cardholder-document.d.ts +45 -0
  50. package/dist/matter/cardholder-document/cardholder-document.js +70 -0
  51. package/dist/matter/cardholder-document/cardholder-document.js.map +1 -0
  52. package/dist/matter/client-preferences/client-preferences-fieldset.d.ts +77 -0
  53. package/dist/matter/client-preferences/client-preferences-fieldset.js +299 -0
  54. package/dist/matter/client-preferences/client-preferences-fieldset.js.map +1 -0
  55. package/dist/matter/client-preferences/client-preferences.d.ts +3 -5
  56. package/dist/matter/client-preferences/client-preferences.js +6 -6
  57. package/dist/matter/client-preferences/client-preferences.js.map +1 -1
  58. package/dist/matter/client-profile/client-profile.d.ts +4 -6
  59. package/dist/matter/client-profile/client-profile.js +9 -6
  60. package/dist/matter/client-profile/client-profile.js.map +1 -1
  61. package/dist/matter/client-profile-form/client-profile-fieldset.d.ts +119 -0
  62. package/dist/matter/client-profile-form/client-profile-fieldset.js +488 -0
  63. package/dist/matter/client-profile-form/client-profile-fieldset.js.map +1 -0
  64. package/dist/matter/identity-edit/identity-edit.js +2 -2
  65. package/dist/matter/identity-edit/identity-edit.js.map +1 -1
  66. package/dist/matter/identity-summary/identity-summary.js +2 -2
  67. package/dist/matter/identity-summary/identity-summary.js.map +1 -1
  68. package/dist/matter/installments/installments-select.d.ts +42 -0
  69. package/dist/matter/installments/installments-select.js +167 -0
  70. package/dist/matter/installments/installments-select.js.map +1 -0
  71. package/dist/matter/installments/installments.d.ts +58 -0
  72. package/dist/matter/installments/installments.js +176 -0
  73. package/dist/matter/installments/installments.js.map +1 -0
  74. package/dist/matter/installments/use-installments-selection.d.ts +29 -0
  75. package/dist/matter/installments/use-installments-selection.js +95 -0
  76. package/dist/matter/installments/use-installments-selection.js.map +1 -0
  77. package/dist/matter/my-account-profile/my-account-profile.js +4 -4
  78. package/dist/matter/my-account-profile/my-account-profile.js.map +1 -1
  79. package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.d.ts +63 -0
  80. package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js +305 -0
  81. package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js.map +1 -0
  82. package/dist/matter/payment/payment-card-capture.d.ts +19 -0
  83. package/dist/matter/payment/payment-card-capture.js +129 -0
  84. package/dist/matter/payment/payment-card-capture.js.map +1 -0
  85. package/dist/matter/payment/payment-controller.d.ts +24 -0
  86. package/dist/matter/payment/payment-controller.js +107 -0
  87. package/dist/matter/payment/payment-controller.js.map +1 -0
  88. package/dist/matter/payment/payment-method-badges.d.ts +57 -0
  89. package/dist/matter/payment/payment-method-badges.js +192 -0
  90. package/dist/matter/payment/payment-method-badges.js.map +1 -0
  91. package/dist/matter/payment/payment.d.ts +89 -0
  92. package/dist/matter/payment/payment.js +263 -0
  93. package/dist/matter/payment/payment.js.map +1 -0
  94. package/dist/matter/payment/use-payment-method-selection.d.ts +302 -0
  95. package/dist/matter/payment/use-payment-method-selection.js +458 -0
  96. package/dist/matter/payment/use-payment-method-selection.js.map +1 -0
  97. package/dist/matter/payment/use-payment-submit-gate.d.ts +111 -0
  98. package/dist/matter/payment/use-payment-submit-gate.js +144 -0
  99. package/dist/matter/payment/use-payment-submit-gate.js.map +1 -0
  100. package/dist/matter/place-order/place-order.d.ts +74 -0
  101. package/dist/matter/place-order/place-order.js +379 -0
  102. package/dist/matter/place-order/place-order.js.map +1 -0
  103. package/dist/matter/product-list/product-list.d.ts +1 -0
  104. package/dist/matter/product-list/product-list.js.map +1 -1
  105. package/dist/matter/product-shelf/product-shelf.d.ts +1 -0
  106. package/dist/matter/product-shelf/product-shelf.js +2 -0
  107. package/dist/matter/product-shelf/product-shelf.js.map +1 -1
  108. package/dist/matter/region/region.js +1 -1
  109. package/dist/matter/region/region.js.map +1 -1
  110. package/dist/matter/save-card/save-card.d.ts +49 -0
  111. package/dist/matter/save-card/save-card.js +188 -0
  112. package/dist/matter/save-card/save-card.js.map +1 -0
  113. package/dist/matter/saved-cards/saved-cards.d.ts +79 -0
  114. package/dist/matter/saved-cards/saved-cards.js +387 -0
  115. package/dist/matter/saved-cards/saved-cards.js.map +1 -0
  116. package/dist/matter/shipping-address/country-descriptors.js +20 -10
  117. package/dist/matter/shipping-address/country-descriptors.js.map +1 -1
  118. package/dist/matter/shipping-address/shipping-address.d.ts +113 -174
  119. package/dist/matter/shipping-address/shipping-address.js +480 -791
  120. package/dist/matter/shipping-address/shipping-address.js.map +1 -1
  121. package/dist/matter/variant-selector/variant-selector.js +1 -1
  122. package/dist/particles/address-summary/address-summary.d.ts +24 -0
  123. package/dist/particles/address-summary/address-summary.js +83 -2
  124. package/dist/particles/address-summary/address-summary.js.map +1 -1
  125. package/dist/particles/alert-dialog/alert-dialog.d.ts +68 -0
  126. package/dist/particles/alert-dialog/alert-dialog.js +187 -0
  127. package/dist/particles/alert-dialog/alert-dialog.js.map +1 -0
  128. package/dist/particles/checkbox/checkbox.d.ts +7 -3
  129. package/dist/particles/checkbox/checkbox.js +4 -1
  130. package/dist/particles/checkbox/checkbox.js.map +1 -1
  131. package/dist/particles/document-field/document-field.d.ts +48 -5
  132. package/dist/particles/document-field/document-field.js +189 -32
  133. package/dist/particles/document-field/document-field.js.map +1 -1
  134. package/dist/particles/field/bound-field-registry.js +52 -0
  135. package/dist/particles/field/bound-field-registry.js.map +1 -0
  136. package/dist/particles/field/field.d.ts +116 -0
  137. package/dist/particles/field/field.js +497 -6
  138. package/dist/particles/field/field.js.map +1 -1
  139. package/dist/particles/fieldset/fieldset.js +2 -2
  140. package/dist/particles/fieldset/fieldset.js.map +1 -1
  141. package/dist/particles/form/form.d.ts +104 -0
  142. package/dist/particles/form/form.js +485 -0
  143. package/dist/particles/form/form.js.map +1 -0
  144. package/dist/particles/phone-field/phone-field.d.ts +38 -10
  145. package/dist/particles/phone-field/phone-field.js +183 -39
  146. package/dist/particles/phone-field/phone-field.js.map +1 -1
  147. package/dist/particles/postal-code-field/postal-code-field.js +2 -2
  148. package/dist/particles/postal-code-field/postal-code-field.js.map +1 -1
  149. package/dist/particles/select/field-select.d.ts +43 -0
  150. package/dist/particles/select/field-select.js +150 -0
  151. package/dist/particles/select/field-select.js.map +1 -0
  152. package/dist/particles/select/select.d.ts +2 -0
  153. package/dist/particles/select/select.js.map +1 -1
  154. package/dist/particles/shared/compose-refs.js +21 -0
  155. package/dist/particles/shared/compose-refs.js.map +1 -0
  156. package/dist/schema.graphql +701 -2
  157. package/dist/sdk/actions/analytics-action-provider.d.ts +15 -0
  158. package/dist/sdk/actions/analytics-action-provider.js +19 -0
  159. package/dist/sdk/actions/analytics-action-provider.js.map +1 -0
  160. package/dist/sdk/actions/index.d.ts +3 -1
  161. package/dist/sdk/actions/index.js +3 -1
  162. package/dist/sdk/actions/payment-action-provider.d.ts +22 -0
  163. package/dist/sdk/actions/payment-action-provider.js +26 -0
  164. package/dist/sdk/actions/payment-action-provider.js.map +1 -0
  165. package/dist/sdk/actions/types.d.ts +6 -2
  166. package/dist/sdk/analytics/analytics-destination.d.ts +43 -0
  167. package/dist/sdk/analytics/analytics-destination.js +90 -0
  168. package/dist/sdk/analytics/analytics-destination.js.map +1 -0
  169. package/dist/sdk/analytics/use-analytics-actions.d.ts +57 -0
  170. package/dist/sdk/analytics/use-analytics-actions.js +108 -0
  171. package/dist/sdk/analytics/use-analytics-actions.js.map +1 -0
  172. package/dist/sdk/cart/use-cart-actions.js +1 -1
  173. package/dist/sdk/data/entity-resolution-context.js +31 -0
  174. package/dist/sdk/data/entity-resolution-context.js.map +1 -0
  175. package/dist/sdk/data/entity-subscriber.js +18 -8
  176. package/dist/sdk/data/entity-subscriber.js.map +1 -1
  177. package/dist/sdk/data/index.js +1 -0
  178. package/dist/sdk/data/page-reactive-data-root.d.ts +36 -0
  179. package/dist/sdk/data/page-reactive-data-root.js +4 -2
  180. package/dist/sdk/data/page-reactive-data-root.js.map +1 -1
  181. package/dist/sdk/data/reactive-data-provider.js +7 -3
  182. package/dist/sdk/data/reactive-data-provider.js.map +1 -1
  183. package/dist/sdk/data/reactive-list-item-data-provider.js +12 -4
  184. package/dist/sdk/data/reactive-list-item-data-provider.js.map +1 -1
  185. package/dist/sdk/data/use-reactive-data-settled.js +39 -0
  186. package/dist/sdk/data/use-reactive-data-settled.js.map +1 -0
  187. package/dist/sdk/data/use-reactive-entity.js +5 -1
  188. package/dist/sdk/data/use-reactive-entity.js.map +1 -1
  189. package/dist/sdk/generated/fragment-types.js +2 -0
  190. package/dist/sdk/generated/fragment-types.js.map +1 -1
  191. package/dist/sdk/generated/graphql.js.map +1 -1
  192. package/dist/sdk/i18n/messages/en-US.js +3 -0
  193. package/dist/sdk/i18n/messages/en-US.json +3 -0
  194. package/dist/sdk/i18n/messages/en-US.json.d.ts +3 -0
  195. package/dist/sdk/i18n/messages/es-ES.js +3 -0
  196. package/dist/sdk/i18n/messages/es-ES.json +3 -0
  197. package/dist/sdk/i18n/messages/pt-BR.js +3 -0
  198. package/dist/sdk/i18n/messages/pt-BR.json +3 -0
  199. package/dist/sdk/identity/use-identity-actions.js +3 -2
  200. package/dist/sdk/identity/use-identity-actions.js.map +1 -1
  201. package/dist/sdk/payment/gateway-origin.js +35 -0
  202. package/dist/sdk/payment/gateway-origin.js.map +1 -0
  203. package/dist/sdk/payment/gateway.d.ts +85 -0
  204. package/dist/sdk/payment/gateway.js +183 -0
  205. package/dist/sdk/payment/gateway.js.map +1 -0
  206. package/dist/sdk/payment/operations.d.ts +52 -0
  207. package/dist/sdk/payment/operations.js +223 -0
  208. package/dist/sdk/payment/operations.js.map +1 -0
  209. package/dist/sdk/payment/security-code-mask.js +71 -0
  210. package/dist/sdk/payment/security-code-mask.js.map +1 -0
  211. package/dist/sdk/payment/submission-registry.d.ts +33 -0
  212. package/dist/sdk/payment/submission-registry.js +58 -0
  213. package/dist/sdk/payment/submission-registry.js.map +1 -0
  214. package/dist/sdk/payment/use-payment-actions.d.ts +78 -0
  215. package/dist/sdk/payment/use-payment-actions.js +276 -0
  216. package/dist/sdk/payment/use-payment-actions.js.map +1 -0
  217. package/dist/sdk/payment/use-place-order.d.ts +65 -0
  218. package/dist/sdk/payment/use-place-order.js +640 -0
  219. package/dist/sdk/payment/use-place-order.js.map +1 -0
  220. package/dist/sdk/shipping/use-shipping-actions.js +1 -1
  221. package/dist/utils/loggable-error-name.js +49 -0
  222. package/dist/utils/loggable-error-name.js.map +1 -0
  223. package/dist/utils/resolve-authored-copy.js +18 -0
  224. package/dist/utils/resolve-authored-copy.js.map +1 -0
  225. package/dist/utils/substitute-template.js +33 -0
  226. package/dist/utils/substitute-template.js.map +1 -0
  227. package/package.json +2 -1
  228. package/src/styles/tailwind.css +34 -0
  229. package/dist/docs/client-profile-form.md +0 -192
  230. package/dist/matter/client-profile-form/client-profile-form.d.ts +0 -126
  231. package/dist/matter/client-profile-form/client-profile-form.js +0 -662
  232. package/dist/matter/client-profile-form/client-profile-form.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"card-entry.js","names":[],"sources":["../../../src/matter/card-entry/card-entry.tsx"],"sourcesContent":["'use client';\n\nimport { defineComponent } from '@vtex/fs-core';\nimport { type ChangeEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { applyMask } from '../../particles/document-field/document-field.js';\nimport { Field } from '../../particles/field/field.js';\nimport { Fieldset } from '../../particles/fieldset/fieldset.js';\nimport { Icon } from '../../particles/icon/icon.js';\nimport { isSecurityCodeComplete, SECURITY_CODE_MASK_STYLE } from '../../sdk/payment/security-code-mask.js';\nimport type { PaymentSubmissionPath } from '../../sdk/payment/submission-registry.js';\nimport {\n type CapturedCard,\n type CardCapture,\n type CardCaptureError,\n type CardCaptureNetwork,\n type CardCapturePreflight,\n type CardCaptureValidator,\n useCardCapture,\n useRegisterCardCapture,\n} from './card-capture.js';\n\n/**\n * The new-card entry matter (specs/card-entry.md, T7 of the Credit Card epic;\n * docs/features/credit-card/rfc.md § D2). Composes the `Field`/`Fieldset` particles and reuses\n * `document-field`'s `applyMask` progressive mask engine to capture a card number, holder name,\n * expiration, and CVV, with masks/required-ness driven by the network's server-driven validator\n * config (T1) rather than hardcoded per network.\n *\n * This is the PCI-critical core of the credit-card feature: it is the **only** component that ever\n * holds a full card number or CVV, and both live only in this component's local state — never a\n * schema prop, a fragment field, an action payload, or a value handed to another component. It reads\n * only the `card-capture` context (card-capture.tsx), so the checkout and the account compose it\n * alike: it registers a capture, builds the card *inside* that closure, and hands it to the target\n * the provider passes in — `sendPayments` at checkout, `savedCard.tokenize` in the account.\n *\n * Network detection is a pure, local computation (never waits on a server round-trip): the typed\n * digits are tested against every method's `validator.numberRegex` on every keystroke, first match\n * wins. The selection waits for a complete BIN (T190): only once the field holds eight digits does a\n * match — on a genuine transition, or once the engine has released the BIN (T174) — publish those\n * eight digits as the BIN (`setDraftBin`) and dispatch `selectMethod` to persist the selection\n * server-side — both through `card-capture`'s `selection` member, so only at checkout. Below eight digits nothing is published or sent — see specs/card-entry.md\n * Decisions 1 and 3, and specs/send-the-typed-bin-to-the.md, which adds the gate — except that the\n * first keystroke off the new-card path records that path locally with `selectNewCard()`\n * (specs/collapse-the-card-form-when-a.md, FR-10).\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type CardEntryProps = {\n /** `Fieldset.Legend` text. */\n legend?: string;\n numberLabel?: string;\n numberPlaceholder?: string;\n holderNameLabel?: string;\n holderNamePlaceholder?: string;\n expirationLabel?: string;\n expirationPlaceholder?: string;\n cvvLabel?: string;\n cvvPlaceholder?: string;\n /** Helper copy under the CVV field, rendered through `Field.Description` and linked to the\n * input via `aria-describedby`. An empty string renders nothing. Copy configuration only —\n * never the security code itself. */\n cvvDescription?: string;\n /** Shown on the number field when it is left blank. */\n numberRequiredMessage?: string;\n /** Shown on the number field for a Luhn-invalid PAN, or a Luhn-valid one that matches no\n * configured method (FR-3 — the same string covers both sites). */\n numberInvalidMessage?: string;\n /** Shown on the holder-name field when it is left blank and the detected method requires it. */\n holderNameRequiredMessage?: string;\n /** Shown on the expiration field when it is left blank and the detected method requires it. */\n expirationRequiredMessage?: string;\n /** Shown on the expiration field for a structurally malformed `MM/YY`. */\n expirationInvalidMessage?: string;\n /** Shown on the expiration field for a structurally valid `MM/YY` that has already passed. */\n expirationExpiredMessage?: string;\n /** Shown on the CVV field when it is left blank and the detected method requires it. */\n cvvRequiredMessage?: string;\n /** Shown on the CVV field for a value that fails the detected method's format. */\n cvvInvalidMessage?: string;\n /** Disables every field. */\n disabled?: boolean;\n className?: string;\n};\n\ntype FieldKey = 'number' | 'holderName' | 'expiration' | 'cvv';\ntype FieldErrors = Partial<Record<FieldKey, string>>;\ntype CardDraft = { number: string; holderName: string; expiration: string; cvv: string };\n\n/** Visual/attribute order used for first-invalid focus and the `data-fs-card-entry-field` values. */\nconst FIELD_ATTR: Record<FieldKey, string> = {\n number: 'number',\n holderName: 'holder-name',\n expiration: 'expiration',\n cvv: 'cvv',\n};\n\nconst VALIDATION_ORDER: readonly FieldKey[] = ['number', 'holderName', 'expiration', 'cvv'];\n\n// ---------------------------------------------------------------------------\n// The effective validator before any network is detected (specs/card-entry.md, Business Context\n// \"Why an effective validator exists before any network is detected\"; Documentation Gaps item 1).\n// A generic \"any new card\" default: every optional field shown and required, generic masks.\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_VALIDATOR: CardCaptureValidator = {\n numberMask: null,\n numberRegex: null,\n cvvMask: null,\n cvvRegex: null,\n cvvRequired: true,\n expirationRequired: true,\n holderNameRequired: true,\n};\n\nconst DEFAULT_NUMBER_MASK = '9999 9999 9999 9999';\nconst DEFAULT_EXPIRATION_MASK = '99/99';\nconst DEFAULT_CVV_MASK = '999';\n\n// ---------------------------------------------------------------------------\n// Masking — reuses `document-field.tsx`'s exported `applyMask` verbatim. Card number/expiration/CVV\n// are always digit-only (unlike documents, which can be alphanumeric), so slot-counting only needs\n// the `9` digit token.\n// ---------------------------------------------------------------------------\n\nfunction countDigitSlots(mask: string): number {\n return mask.split('').filter((char) => char === '9').length;\n}\n\n/** Slot capacity of a (possibly pipe-delimited) mask — the widest alternative's count (F13). */\nfunction maskCapacity(mask: string): number {\n return Math.max(...mask.split('|').map(countDigitSlots));\n}\n\n/**\n * The narrowest alternative's slot count — `maskCapacity`'s floor, and the length at which a\n * security code counts as fully typed (T173).\n *\n * The ceiling is the right answer for truncation (`maskDigits` must let the widest alternative's\n * digits through) and the wrong one for masking: one method id can cover a 3-and-4-digit family with\n * a single `'999|9999'` mask, and a shopper holding the 3-digit card of that family never reaches a\n * fourth digit — so a ceiling-derived length leaves their complete, valid code legible for the rest\n * of the flow. `SavedCardCvvField` errs on the same side for the same reason. A malformed\n * alternative with no slots (`'999|'`) floors to zero, which masks from the first digit: early,\n * never late.\n */\nfunction narrowestMaskCapacity(mask: string): number {\n return Math.min(...mask.split('|').map(countDigitSlots));\n}\n\nfunction toDigits(value: string): string {\n return value.replace(/\\D/g, '');\n}\n\n/**\n * Strips non-digits, truncates to the mask's slot capacity, then applies it progressively. A\n * pipe-delimited mask (`applyMask` already selects among the alternatives internally) needs the\n * *widest* alternative's capacity here (F13) — counting `9`s across the whole pipe-joined string, as\n * before this fix, sums every alternative's slots instead of taking the largest one, letting far more\n * digits through than any single alternative can hold. Mirrors `document-field.tsx`'s own\n * `Math.max(...mask.split('|').map(countSlots))` for the identical reason.\n */\nfunction maskDigits(rawValue: string, mask: string): string {\n const digitsOnly = toDigits(rawValue);\n return applyMask(digitsOnly.slice(0, maskCapacity(mask)), mask);\n}\n\n// ---------------------------------------------------------------------------\n// Network detection (Decision 1) — pure, local, never waits on the server.\n// ---------------------------------------------------------------------------\n\n/** A malformed store-configured regex is swallowed (treated as no match), never thrown. */\nfunction safeTest(pattern: string, value: string): boolean {\n try {\n return new RegExp(pattern).test(value);\n } catch {\n return false;\n }\n}\n\n/**\n * Relaxes a store's `numberRegex` so it matches on a BIN-length prefix instead of only once the\n * shopper has typed the network's full PAN length (F1, specs/card-entry.md Decision 1). T1's own\n * regexes are full-length anchored — the real Visa validator is `^4[0-9]{12}(?:[0-9]{3})?$` (13 or\n * 16 digits, exactly) — so testing the *whole* typed digit string against it as-is cannot resolve\n * before the last keystroke, and resolves intermittently for lengths in between (13 matches, 14–15\n * don't, 16 matches again): the badge, and every field gated on `detectedMethod`, flicker on, off,\n * and on again while the shopper is still typing. Dropping the trailing `$` and widening every\n * bounded repeat quantifier to also accept fewer repetitions (`{12}` → `{0,12}`, `{n,m}` → `{0,m}`,\n * `{n,}` → `{0,}`) keeps every *fixed* discriminating character exactly as strict as the store\n * configured (Mastercard's `[1-5]` second digit still requires that literal digit once typed), while\n * letting the length-representing tail resolve as soon as it's admissible rather than only once it's\n * complete. Live detection only — the unmodified regex is still what `validateDraft` re-tests at\n * submission time.\n */\nfunction relaxForLiveDetection(pattern: string): string {\n return pattern\n .replace(/\\$$/, '')\n .replace(/\\{(\\d+)(,)?(\\d+)?\\}/g, (_match, min: string, hasComma: string | undefined, max: string | undefined) =>\n hasComma && max === undefined ? '{0,}' : `{0,${max ?? min}}`,\n );\n}\n\n/** Every method whose *relaxed* (BIN-prefix) transform of `validator.numberRegex` admits `digits`. */\nfunction relaxedCandidates(methods: readonly CardCaptureNetwork[], digits: string): readonly CardCaptureNetwork[] {\n return methods.filter(\n (method) => method.validator.numberRegex && safeTest(relaxForLiveDetection(method.validator.numberRegex), digits),\n );\n}\n\n/**\n * First method whose *exact*, unrelaxed `validator.numberRegex` matches `digits` in full — the same\n * test `validateDraft` re-runs at submission time.\n */\nfunction exactMatch(methods: readonly CardCaptureNetwork[], digits: string): CardCaptureNetwork | null {\n return (\n methods.find((method) => method.validator.numberRegex && safeTest(method.validator.numberRegex, digits)) ?? null\n );\n}\n\n/**\n * First method whose `validator.numberRegex` admits `digits` as a BIN-level prefix, or `null`.\n *\n * N1: relaxing every store regex into a pure prefix test (F1, above) means two networks whose fixed\n * prefixes *nest* — one network's whole discriminating prefix is itself a prefix of a different,\n * longer network's real PAN — both keep \"relax-matching\" once the shorter network's fixed prefix is\n * satisfied, including at the longer network's own full, syntactically valid length, where the exact,\n * unrelaxed regex alone correctly discriminates by length. Left as pure first-match-wins over the\n * relaxed test, the answer at full PAN length silently depends on which order the store's `methods`\n * happen to arrive in — something this component does not control.\n *\n * The fix tries the **exact** regex first, across every method in `methods` order, and only falls back\n * to the relaxed (prefix) match for input too short to satisfy any exact pattern yet. This restores the\n * exact regex's authority the instant a full PAN is present, which resolves the nesting-prefix case\n * above deterministically regardless of order. **It is not, however, order-independent in general**\n * (T22, V2): two configured networks' exact patterns can both match the very same full-length input —\n * e.g. a real Visa regex and an Elo BIN range both match `4011788888888888` (Elo is issued on\n * Visa-range BINs) — and `exactMatch` below is a plain `methods.find(...)`, so **the first exact match\n * in `methods` order wins the tie**. This is pre-existing (pre-N1 code used the same `find` over exact\n * regexes) and is not introduced or fixed by N1; N1 only shrank how much of the input space stays\n * order-dependent. A caller that needs a specific tie winner must order `methods` by specificity itself\n * before calling — this function does not infer specificity from a regex. See also `resolveNumberMask`\n * below: this function alone cannot help if the field's own mask capacity has already been capped\n * narrower than the digit count an exact match needs to resolve, and see `resolveNetworkMethod` below\n * `resolveNumberMask` (T22, V3/V4) for this component's own order-independent wrapper around the\n * *relaxed*-fallback branch, which this component's own call sites use instead of calling this function\n * directly.\n */\nexport function detectNetworkMethod(methods: readonly CardCaptureNetwork[], digits: string): CardCaptureNetwork | null {\n if (!digits) return null;\n return exactMatch(methods, digits) ?? relaxedCandidates(methods, digits)[0] ?? null;\n}\n\n/**\n * The number mask to apply for the currently typed `digits` (N1, part 2 — the mechanism Part 1 alone\n * cannot fix). Masking with only the single method `detectNetworkMethod` currently resolves to can cap\n * the field's capacity below what a *different*, still-relaxed-matching method would need once more\n * digits arrive — e.g. a 14-digit network's own mask, while the shopper is mid-typing a 19-digit\n * network whose fixed prefix nests inside the shorter one's. Once that happens, every keystroke past 14\n * truncates straight back down to 14 digits, forever — the shopper can never reach the length at which\n * the exact regex (Part 1, above) would resolve correctly, trading a cosmetic mis-mask for a hard block.\n *\n * Never resolves narrower than the widest `numberMask` among every method whose *relaxed* regex still\n * matches, so the field's capacity always keeps pace with the longest network still in contention; only\n * once a network drops out of contention (or an exact match resolves) does the mask narrow back down.\n */\nfunction resolveNumberMask(methods: readonly CardCaptureNetwork[], digits: string): string {\n const candidateMasks = relaxedCandidates(methods, digits)\n .map((method) => method.validator.numberMask)\n .filter((mask): mask is string => Boolean(mask));\n if (candidateMasks.length === 0) return DEFAULT_NUMBER_MASK;\n return candidateMasks.reduce((widest, mask) => (maskCapacity(mask) > maskCapacity(widest) ? mask : widest));\n}\n\n// ---------------------------------------------------------------------------\n// V3/V4 (T22) — this component's own, order-independent resolution of \"which network is detected right\n// now,\" used at both of this component's `detectNetworkMethod` call sites (the `detectedMethod` memo\n// and `handleNumberChange`'s `matched`) instead of calling `detectNetworkMethod` with `methods` directly.\n// Never used inside `detectNetworkMethod`/`resolveNumberMask` themselves, both T7's N1, load-bearing and\n// unchanged here.\n//\n// `detectNetworkMethod`'s relaxed-match fallback resolves to the *first* still-viable candidate once no\n// exact match has resolved yet (see its own doc comment above) — keyed to `methods`' own array order.\n// Two nesting networks — one's whole discriminating prefix a prefix of the other's longer PAN,\n// Decision 1's own example — disagree about badge, mask-gate (holder name/expiration/CVV\n// required-ness), and dispatch for the *same* typed digits depending only on which the store happens to\n// list first: not a cosmetic difference, since it changes which network's `selectMethod` gets published\n// mid-typing. Reachable at HEAD (V3) — the \"never unmounts/remounts\" test below only missed it because\n// its own fixture carries a single method, so there is nothing for order to disagree about.\n//\n// A first attempt at this fix reordered `methods` by widest `numberMask` capacity before calling\n// `detectNetworkMethod`, reasoning that one deterministic global order would make the relaxed fallback\n// agree regardless of store configuration order (mirroring `resolveNumberMask`'s own widest-wins\n// philosophy). Measured (T22-1) and found unsound: whichever direction the comparator favors is right\n// for a shopper typing the network it favors and wrong for a shopper typing the *other*, nesting\n// network's own genuinely complete, shorter PAN — the reorder does not remove the order-dependence, it\n// relocates which configuration order exhibits it, leveling the previously-correct order down to the\n// previously-broken one instead of fixing the broken one. No global ordering of a shared array serves\n// both directions, because the right relaxed-fallback winner depends on the length the shopper is\n// heading for, which is unknowable mid-typing. It also fed the *reordered* array into `exactMatch`\n// (inside the same `detectNetworkMethod` call), which silently changed T22-2's exact-tie winner from\n// \"the store's own configured order\" to \"whichever tying network has the wider mask\" — one call frame\n// earlier than the relaxed-fallback problem it was trying to solve.\n//\n// The fix: only ever trust the relaxed-fallback pick when it is unambiguous — the *only* still-viable\n// candidate. When two or more configured networks are simultaneously relaxed-viable (the nesting case)\n// and neither has an exact match yet, which one \"wins\" would depend only on array order, so this returns\n// `null` (undetected) instead of guessing: the badge/validator fall back to the pre-detection default,\n// and `selectMethod` is not dispatched for either candidate, until either an exact match resolves (T7's\n// N1, tried first over `methods` in the store's own configured order, always) or the relaxed candidate\n// set narrows to exactly one. The result is identical regardless of the store's own `methods` order by\n// construction — array position is never read — rather than by a comparator's luck, and it never\n// reorders the array `exactMatch` sees, so T22-2's tie is still decided by configured order.\n//\n// This does not touch `detectNetworkMethod`'s exact-first algorithm and does not remove the\n// coincidental-exact-match residual described in its own doc comment (a *shorter* network's exact regex\n// can still coincidentally match a prefix of a *longer* network's real PAN at an intermediate typed\n// length — that specific flip is already order-independent, self-corrects once the shopper finishes\n// typing, and is provably not safe to suppress without reintroducing the F7/N2 dead-end this task's own\n// \"Out\" boundary forbids re-touching — see specs/card-entry-network-order-independence.md).\n// ---------------------------------------------------------------------------\n\n/**\n * The method this component treats as \"detected\" at the current `digits` — the single source both the\n * render-scoped badge/validator and `handleNumberChange`'s dispatch decision read from, so the two can\n * never disagree (FR-2). See the banner above for why this is not `detectNetworkMethod(methods, digits)`\n * called directly: the exact-match branch here always uses `methods` in the store's own configured\n * order (T22-2 — ties are the store's call, never a derived signal), and the relaxed-fallback branch\n * only resolves when it is unambiguous, rather than silently favoring whichever candidate array order\n * happens to put first (T22-1).\n */\nexport function resolveNetworkMethod(\n methods: readonly CardCaptureNetwork[],\n digits: string,\n): CardCaptureNetwork | null {\n if (!digits) return null;\n const exact = exactMatch(methods, digits);\n if (exact) return exact;\n const candidates = relaxedCandidates(methods, digits);\n return candidates.length === 1 ? (candidates[0] ?? null) : null;\n}\n\n// ---------------------------------------------------------------------------\n// T190 — the BIN a selection carries (specs/send-the-typed-bin-to-the.md, Decision 1). Both dispatch\n// sites, `handleNumberChange` and the return-to-new-card effect, take it from `resolveCompleteBin`, so\n// neither can send a BIN shorter than the one this component defines. Detection is not gated: it still\n// resolves from the store's own discriminating digits, on every keystroke.\n// ---------------------------------------------------------------------------\n\n/** The length this component slices its BIN to — the same eight digits `gateway.ts` sends as the\n * payment body's BIN. */\nconst BIN_LENGTH = 8;\n\n/**\n * The BIN a selection carries: the first `BIN_LENGTH` of `digits`, or `null` while `digits` holds\n * fewer — no selection is sent for a partial BIN. Callers pass the masked field's own digits, so \"the\n * gate is open\" and \"the BIN is complete\" are one fact. Exported for direct unit testing; the package\n * barrel (`src/components.ts`) does not re-export it.\n */\nexport function resolveCompleteBin(digits: string): string | null {\n return digits.length >= BIN_LENGTH ? digits.slice(0, BIN_LENGTH) : null;\n}\n\n// ---------------------------------------------------------------------------\n// N3/V4 — the re-dispatch effect's own guard, extracted to a pure predicate (T22-5) so each of its\n// three conditions has a direct, isolated test instead of relying only on the effect's own\n// component-level tests to expose a mutated one — the verification pass found two of the three\n// (`previousPath !== 'saved-card'`, `!detectedMethod`) deletable with the full suite green, one of the\n// deletions a live crash (reading `.id` off `null`) in a path none of the component-level tests happen\n// to exercise.\n// ---------------------------------------------------------------------------\n\n/**\n * Whether the N3/V4 effect should re-publish `detectedMethod` now that `draft.path` is `'new-card'`.\n *\n * - `previousPath === 'saved-card'`: only a genuine *return* from the saved-card detour re-dispatches —\n * the very first activation transitions `null` → `'new-card'` too, and `handleNumberChange`'s own\n * dispatch already covers that first selection; re-dispatching here as well would double-count it.\n * - `revealedByCardMode` (T191, FR-12): the other return — the card payment was collapsed on the\n * previous commit and card mode is what brought it back. Omitted means it was not. Both returns share\n * this one guard and the effect's one send, so they never both send for one commit.\n * - `detectedMethod !== null`: nothing to (re-)publish yet.\n * - `lastDispatched === null`: `handleNumberChange` has not already dispatched this exact commit (V4) —\n * otherwise this would double-post the identical `{ methodId, bin }` pair.\n */\nexport function shouldRedispatchOnReturnToNewCard({\n previousPath,\n revealedByCardMode = false,\n detectedMethod,\n lastDispatched,\n}: {\n previousPath: PaymentSubmissionPath | null;\n revealedByCardMode?: boolean;\n detectedMethod: CardCaptureNetwork | null;\n lastDispatched: { methodId: string; bin: string } | null;\n}): boolean {\n return (previousPath === 'saved-card' || revealedByCardMode) && detectedMethod !== null && lastDispatched === null;\n}\n\n// ---------------------------------------------------------------------------\n// Network → icon mapping. `PaymentMethod.name` is store-configured free text (T1 Decision 3); this\n// small, non-exhaustive map covers the base icon dialect's card-network entries\n// (packages/sukima/crates/sukima-icons/src/reference.rs's BASE_ICON_DIALECT). A name that maps to\n// none of these renders as text only, inside the detected-network mark itself\n// (`data-fs-card-entry-network-fallback`) — and only ever for a method network detection actually\n// resolved to, which a non-card method can never be (it has no `numberRegex`, so\n// `resolveNetworkMethod` cannot return it). A method this store merely *accepts* renders nowhere in\n// `CardEntry`: the surface that shows every accepted method is `AcceptedPaymentMethodBadges`\n// (payment-method-badges.tsx). Exported (T71) so the test suite can mechanically pin every value\n// against `reference.rs` — the map itself stays fixed; only a follow-up task grows it, alongside the\n// matching dialect entry. `payment-method-badges.tsx` imports this map directly and its own test\n// pins it byte-identical, so an edit here is never local to this file.\n// ---------------------------------------------------------------------------\n\nexport const NETWORK_ICON_BY_KEY: Record<string, string> = {\n visa: 'visa',\n mastercard: 'mastercard',\n amex: 'amex',\n americanexpress: 'amex',\n diners: 'diners',\n dinersclub: 'diners',\n elo: 'elo-card',\n};\n\nfunction networkIconName(name: string): string | null {\n const key = name.toLowerCase().replace(/[^a-z]/g, '');\n return NETWORK_ICON_BY_KEY[key] ?? null;\n}\n\n// ---------------------------------------------------------------------------\n// Validation (Decision 5) — Luhn, expiration format + not-in-past, CVV format. Exported for direct\n// unit testing, mirroring document-field.tsx's own convention (`validateCPF`, `isValidDocument`).\n// ---------------------------------------------------------------------------\n\n/**\n * Standard Luhn checksum. Rejects non-digit strings and anything shorter than 12 digits — no real\n * card network issues a PAN shorter than that.\n */\nexport function isValidLuhn(digits: string): boolean {\n if (digits.length < 12 || !/^\\d+$/.test(digits)) return false;\n\n let sum = 0;\n let double = false;\n for (let i = digits.length - 1; i >= 0; i -= 1) {\n let digit = Number(digits[i]);\n if (double) {\n digit *= 2;\n if (digit > 9) digit -= 9;\n }\n sum += digit;\n double = !double;\n }\n return sum % 10 === 0;\n}\n\nconst EXPIRATION_FORMAT = /^(0[1-9]|1[0-2])\\/(\\d{2})$/;\n\n/** `MM/YY` structural check: month 01–12, two-digit year. */\nexport function isExpirationFormatValid(value: string): boolean {\n return EXPIRATION_FORMAT.test(value);\n}\n\n/**\n * True when the printed `MM/YY` has not yet ended — cards are valid through the end of their\n * printed month. `now` is injectable for deterministic tests.\n */\nexport function isExpirationInFuture(value: string, now: Date = new Date()): boolean {\n const match = EXPIRATION_FORMAT.exec(value);\n if (!match) return false;\n\n const month = Number(match[1]);\n const year = 2000 + Number(match[2]);\n const endOfMonth = new Date(year, month, 0, 23, 59, 59, 999);\n return endOfMonth.getTime() >= now.getTime();\n}\n\n/**\n * Normalizes a 4-digit-year expiration (`MM/YYYY`, e.g. from a browser/password-manager `cc-exp`\n * autofill, or the equivalent pasted digits) down to `MM/YY` before masking (F3) — mirrors\n * `gateway.ts`'s own `toDueDate` two-slice scheme (month = first two digits, year = last two).\n * Without this, the mask's blind 4-digit slot cap truncated `122030` (from an autofilled `12/2030`)\n * to `1220` — keeping the *first* two digits of the year instead of the last two — silently turning\n * a card valid through 2030 into one that reads as already expired in 2020.\n *\n * T172, Decision 5: the recombination applies to a genuine six-digit shape **only**. Applied to any\n * count above four — F3's original rule — it also claims the far more common overflow case: the\n * field already holds a complete `MM/YY` and the shopper types one more digit, so the raw value is\n * five digits (`12/25` + `9` → `12259`). Recombining that yields `12` + `59` = `12/59`: the year's\n * first digit is dropped, the second slides down a place, and the new digit takes the tens place — a\n * value the shopper never typed as a sequence, with no keystroke ever visibly refused.\n *\n * Digit count alone still separates the three real input shapes (typed digit-by-digit ≤ 4,\n * autofill/paste of `MM/YYYY` = 6, accidental overflow = anything else). Fix round 1 (review finding\n * #1): every other length refuses the keystroke by returning `previousDigits` — whatever the field\n * already held — rather than `digits.slice(0, 4)`. Slicing the *new* raw string only reconstructs the\n * original value when the intruding digit lands at the very end; caught anywhere else (e.g. the caret\n * placed right after the `/`), the first four characters of the new string mix a surviving old digit\n * with the intruding one, silently producing a date the shopper never typed or saw form (`12/30`,\n * caret after the `/`, types `9` → raw `12930` → `slice(0, 4)` = `1293` → `12/93`). Returning\n * `previousDigits` makes the refusal caret-position independent: the displayed value is provably\n * unchanged, not merely unchanged for the one caret position a naive slice happens to get right. No\n * known browser or password manager delivers `MM/YYYY` in anything but six digits; a future variant is\n * a deliberate, reviewed addition here rather than a silent gap.\n */\nfunction normalizeExpirationDigits(digits: string, previousDigits: string): string {\n if (digits.length <= 4) return digits;\n if (digits.length === 6) return `${digits.slice(0, 2)}${digits.slice(-2)}`;\n return previousDigits;\n}\n\nfunction isValidCvv(value: string, validator: CardCaptureValidator): boolean {\n if (validator.cvvRegex) return safeTest(validator.cvvRegex, value);\n return /^\\d{3,4}$/.test(value);\n}\n\n/**\n * The resolved field-validation messages `validateDraft` renders (T31, FR-1/FR-2 —\n * specs/make-the-payment-components-authored-copy.md). One entry per distinct message: the two\n * number-invalid sites (Luhn-invalid, matches-no-configured-network) share `numberInvalid`, since they\n * render the same string today and FR-3 records that reuse as deliberate.\n */\ntype CardEntryValidationMessages = Readonly<{\n numberRequired: string;\n numberInvalid: string;\n holderNameRequired: string;\n expirationRequired: string;\n expirationInvalid: string;\n expirationExpired: string;\n cvvRequired: string;\n cvvInvalid: string;\n}>;\n\n/**\n * Whether network detection could ever resolve to `method`. A method with no `numberRegex` never can\n * (`resolveNetworkMethod`/`detectNetworkMethod` both require one): a promissory line of credit, a store\n * credit balance, or a card-group method configured without a pattern. Not the section's card\n * expression, `isCardMethod`, which counts that last one as a card (T191, Decision 1 and FR-14).\n *\n * Its one caller is `validateDraft`'s own `networkDetectionPossible` gate below (the F7/N2\n * validation-message fix) — a single call site reads as dead weight here, and it is not.\n */\nfunction isCardShapedMethod(method: CardCaptureNetwork): boolean {\n return Boolean(method.validator.numberRegex);\n}\n\nfunction validateDraft(\n draft: CardDraft,\n validator: CardCaptureValidator,\n methods: readonly CardCaptureNetwork[],\n messages: CardEntryValidationMessages,\n): FieldErrors {\n const errors: FieldErrors = {};\n const digits = toDigits(draft.number);\n // N2: only enforce the \"matches a configured network\" re-test (F7) when detection could genuinely\n // have run somewhere — i.e. at least one configured method actually carries a `numberRegex`.\n // `methods` is legitimately `[]` while the payment query is still loading\n // (use-payment-method-selection.ts's `$data?.methods ?? EMPTY_METHODS`), and `validator.numberRegex`\n // is upstream-nullable even once loaded (`mappers.ts:537`'s `validator?.regex ?? null`). F7's\n // original fix conflated \"no configured method matches these digits\" (a real error) with \"detection\n // was never possible\" (not the shopper's fault, and not the same failure), hard-blocking every card\n // — including a genuinely valid one — during that window.\n const networkDetectionPossible = methods.some(isCardShapedMethod);\n\n if (!digits) {\n errors.number = messages.numberRequired;\n } else if (!isValidLuhn(digits)) {\n errors.number = messages.numberInvalid;\n } else if (networkDetectionPossible && !(validator.numberRegex && safeTest(validator.numberRegex, digits))) {\n // F7: a Luhn-valid PAN that matches no *configured* method dead-ends downstream — `selectMethod`\n // never dispatched, so `usePlaceOrder` refuses with a generic no-selection error that points at\n // nothing — this is the one field that could have said so. N2 narrows this to only fire when\n // detection could actually have happened (see above); it must never fire merely because `methods`\n // hasn't resolved yet.\n errors.number = messages.numberInvalid;\n }\n\n if (validator.holderNameRequired && !draft.holderName.trim()) {\n errors.holderName = messages.holderNameRequired;\n }\n\n if (validator.expirationRequired) {\n if (!draft.expiration.trim()) {\n errors.expiration = messages.expirationRequired;\n } else if (!isExpirationFormatValid(draft.expiration)) {\n errors.expiration = messages.expirationInvalid;\n } else if (!isExpirationInFuture(draft.expiration)) {\n errors.expiration = messages.expirationExpired;\n }\n }\n\n if (validator.cvvRequired) {\n if (!draft.cvv.trim()) {\n errors.cvv = messages.cvvRequired;\n } else if (!isValidCvv(draft.cvv, validator)) {\n errors.cvv = messages.cvvInvalid;\n }\n }\n\n return errors;\n}\n\n// ---------------------------------------------------------------------------\n// The submission continuation's own client-synthesized error (specs/card-entry.md § The submission\n// continuation). Distinct from the gateway's own codes (`gateway.ts`) and from `usePlaceOrder`'s\n// (`use-place-order.ts`) — this one means \"the draft itself never reached the gateway.\"\n// ---------------------------------------------------------------------------\n\nexport const CARD_ENTRY_INVALID_DATA_CODE = 'INVALID_CARD_DATA';\n\nconst INVALID_CARD_DATA_ERROR: CardCaptureError = {\n code: CARD_ENTRY_INVALID_DATA_CODE,\n message: 'Enter valid card details before placing the order.',\n};\n\n/**\n * The submission and pre-flight refusal for a card form the shopper cannot currently see (T74,\n * Decision 5), and for a visible form's valid card under a persisted method that cannot take it: one\n * this section does not serve, one that is not a card (T191, FR-13), or another card network than the\n * typed number's (T191 fix round 1, finding R1). Distinct from `INVALID_CARD_DATA`: the card may be\n * perfectly valid — the section is simply not on a method this form can pay it with. A fourth\n * client-synthesized `CardCaptureError` in `matter/`, beside `CARD_ENTRY_INVALID_DATA_CODE`.\n */\nexport const CARD_ENTRY_COLLAPSED_CODE = 'CARD_ENTRY_COLLAPSED';\n\nconst CARD_ENTRY_COLLAPSED_ERROR: CardCaptureError = {\n code: CARD_ENTRY_COLLAPSED_CODE,\n message: 'This card is not available for the currently selected payment method.',\n};\n\n// ---------------------------------------------------------------------------\n// Definition\n// ---------------------------------------------------------------------------\n\nexport const CardEntryDefinition = defineComponent<CardEntryProps>({\n name: 'CardEntry',\n importPath: '@vtex/faststore/components',\n component: CardEntry,\n requiresContext: 'card-capture',\n editor: { label: 'Card Entry', category: 'commerce' },\n schema: {\n legend: { type: 'string', default: 'Card details', label: 'Legend' },\n numberLabel: { type: 'string', default: 'Card number', label: 'Number label' },\n numberPlaceholder: { type: 'string', label: 'Number placeholder' },\n // T172 (FR-6): \"cardholder\", the same noun `holderNameRequiredMessage` below already uses — the\n // label and its own validation error named the same field two different ways.\n holderNameLabel: { type: 'string', default: 'Cardholder name', label: 'Holder name label' },\n holderNamePlaceholder: { type: 'string', label: 'Holder name placeholder' },\n expirationLabel: { type: 'string', default: 'Expiration date', label: 'Expiration label' },\n expirationPlaceholder: { type: 'string', default: 'MM/YY', label: 'Expiration placeholder' },\n cvvLabel: { type: 'string', default: 'Security code', label: 'CVV label' },\n cvvPlaceholder: { type: 'string', label: 'CVV placeholder' },\n // T31 unified every default on this component onto en-US (specs/make-the-payment-components-\n // authored-copy.md) — this one already shipped en-US per requirement 12.27's own quoted copy\n // (spec mask-the-security-code-field-and.md, OQ-1 Option B), so it needed no change.\n cvvDescription: { type: 'string', default: 'The 3-4 digit number on your card', label: 'CVV description' },\n // T31 (FR-1) — eight field-validation messages, each defaulting to the literal this component\n // already hardcoded. `numberInvalidMessage` covers two sites (Luhn-invalid and\n // matches-no-configured-network, FR-3): they render the same string today, and reusing one prop\n // for both is deliberate, not an oversight.\n numberRequiredMessage: { type: 'string', default: 'Card number is required', label: 'Number required message' },\n numberInvalidMessage: { type: 'string', default: 'Enter a valid card number', label: 'Number invalid message' },\n holderNameRequiredMessage: {\n type: 'string',\n default: 'Cardholder name is required',\n label: 'Holder name required message',\n },\n expirationRequiredMessage: {\n type: 'string',\n default: 'Expiration date is required',\n label: 'Expiration required message',\n },\n expirationInvalidMessage: {\n type: 'string',\n default: 'Enter a valid expiration date',\n label: 'Expiration invalid message',\n },\n expirationExpiredMessage: {\n type: 'string',\n default: 'This card has expired',\n label: 'Expiration expired message',\n },\n cvvRequiredMessage: { type: 'string', default: 'Security code is required', label: 'CVV required message' },\n cvvInvalidMessage: { type: 'string', default: 'Enter a valid security code', label: 'CVV invalid message' },\n disabled: { type: 'boolean', default: false, label: 'Disabled' },\n },\n guideline:\n \"New-card entry: masked number with live network detection, holder name, expiration, and CVV, each shown and required per the detected network's validator config from the 'card-capture' context. Must be placed inside a card-capture provider: PaymentSectionRoot or PaymentSectionController at checkout. Owns the card values itself — it builds the card and hands it only to the submission target its provider supplies; no other component ever needs the card number or CVV. Card number and CVV are never schema props, fragment fields, or action payloads — do not add one. See card-entry.md.\",\n});\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\nexport function CardEntry({\n legend = 'Card details',\n numberLabel = 'Card number',\n numberPlaceholder,\n holderNameLabel = 'Cardholder name',\n holderNamePlaceholder,\n expirationLabel = 'Expiration date',\n expirationPlaceholder = 'MM/YY',\n cvvLabel = 'Security code',\n cvvPlaceholder,\n cvvDescription = 'The 3-4 digit number on your card',\n numberRequiredMessage = 'Card number is required',\n numberInvalidMessage = 'Enter a valid card number',\n holderNameRequiredMessage = 'Cardholder name is required',\n expirationRequiredMessage = 'Expiration date is required',\n expirationInvalidMessage = 'Enter a valid expiration date',\n expirationExpiredMessage = 'This card has expired',\n cvvRequiredMessage = 'Security code is required',\n cvvInvalidMessage = 'Enter a valid security code',\n disabled = false,\n className,\n}: CardEntryProps) {\n const { networks: methods, busy: placing, clearOn, onNetworkChange, selection } = useCardCapture();\n // The checkout's selection facts. Without `selection` (outside the checkout) nothing is dispatched,\n // the form never collapses, and no selection reason refuses a card.\n const draftPath = selection?.path ?? null;\n const draftBin = selection?.bin ?? null;\n // T191 (FR-7, Decision 2): the section's one collapse fact, shared with every card leaf.\n const collapsed = selection?.collapsed ?? false;\n const cardModeActive = selection?.cardModeActive ?? false;\n const selectedMethodId = selection?.selectedMethodId ?? null;\n const selectionOutOfScope = selection?.selectionOutOfScope ?? false;\n // T191 (FR-13, Decision 8): the persisted method is one this form cannot pay with.\n const selectionNonCard = selection?.selectionNonCard ?? false;\n const selectMethod = selection?.selectMethod;\n // Memoized so `checkDraft` below only recomputes when an authored message actually changes, not on\n // every unrelated re-render (mirrors `detectedMethod`'s own useMemo granularity).\n const validationMessages = useMemo<CardEntryValidationMessages>(\n () => ({\n numberRequired: numberRequiredMessage,\n numberInvalid: numberInvalidMessage,\n holderNameRequired: holderNameRequiredMessage,\n expirationRequired: expirationRequiredMessage,\n expirationInvalid: expirationInvalidMessage,\n expirationExpired: expirationExpiredMessage,\n cvvRequired: cvvRequiredMessage,\n cvvInvalid: cvvInvalidMessage,\n }),\n [\n numberRequiredMessage,\n numberInvalidMessage,\n holderNameRequiredMessage,\n expirationRequiredMessage,\n expirationInvalidMessage,\n expirationExpiredMessage,\n cvvRequiredMessage,\n cvvInvalidMessage,\n ],\n );\n\n const [number, setNumber] = useState('');\n const [holderName, setHolderName] = useState('');\n const [expiration, setExpiration] = useState('');\n const [cvv, setCvv] = useState('');\n const [fieldErrors, setFieldErrors] = useState<FieldErrors>({});\n\n const rootRef = useRef<HTMLDivElement>(null);\n const lastDispatchedRef = useRef<{ methodId: string; bin: string } | null>(null);\n const previousDraftPathRef = useRef(draftPath);\n const reportedNetworkIdRef = useRef<string | null>(null);\n const previousCollapsedRef = useRef(collapsed);\n\n const digits = toDigits(number);\n // V3 (T22): this component's own order-independent resolution — see `resolveNetworkMethod`'s doc\n // comment for why this is not `detectNetworkMethod(methods, digits)` called directly.\n const detectedMethod = useMemo(() => resolveNetworkMethod(methods, digits), [methods, digits]);\n const validator = detectedMethod?.validator ?? DEFAULT_VALIDATOR;\n // T191 fix round 1 (finding R1): the persisted method is not the network the typed number is detected\n // as. Only a detected network can differ, so a card is never refused because detection could not run\n // at all (N2: no configured method carries a number pattern, and nothing is ever sent for the card).\n // Nothing persisted is `usePlaceOrder`'s refusal, before any pre-flight (`use-place-order.ts:674-677`).\n const selectionOtherNetwork =\n selectedMethodId !== null && detectedMethod !== null && detectedMethod.id !== selectedMethodId;\n // T172 (Decision 1/Decision 3): the icon name for the *one* detected network, or `null` when its\n // store-configured name maps to no base-dialect icon — in which case the mark renders the name as\n // text instead. `null` here is the only signal the two branches need; whether a mark renders at\n // all is `detectedMethod` alone, never the configured-method list.\n const detectedNetworkIcon = detectedMethod ? networkIconName(detectedMethod.name) : null;\n // T173: the security-code mask engages the instant every digit is typed — the detected network's own\n // CVV length, so three digits for most networks and four for Amex — and not before. The *narrowest*\n // alternative of a pipe-delimited mask, not the widest one truncation uses: see\n // `narrowestMaskCapacity`. Derived from `cvv` here rather than held in state, so a network switch\n // mid-entry re-resolves the length against the newly detected `cvvMask` and the collapse-clear\n // effect below has nothing extra to reset.\n const cvvComplete = isSecurityCodeComplete(cvv, narrowestMaskCapacity(validator.cvvMask ?? DEFAULT_CVV_MASK));\n const detectedNetworkId = detectedMethod?.id ?? null;\n\n // Reports the resolved network by id only, never digits, so the account can apply that network's\n // document rules. Skips the initial `null`: only a change is reported.\n useEffect(() => {\n if (reportedNetworkIdRef.current === detectedNetworkId) return;\n reportedNetworkIdRef.current = detectedNetworkId;\n onNetworkChange?.(detectedNetworkId);\n }, [detectedNetworkId, onNetworkChange]);\n\n // F6/N3: the guard above is keyed on (methodId, bin) only, so returning to the new-card path after a\n // detour through a saved-card selection (T12 registers its own continuation and dispatches\n // `selectMethod({ methodId, accountId })` on the picker's behalf) would otherwise still think this\n // component's own last-typed network was \"already dispatched\" and skip re-publishing it — leaving\n // the persisted server-side selection pointed at whatever was picked in between.\n //\n // Clearing the ref alone (F6's original fix) only helps if the shopper edits the field *again* —\n // pressing a place-order CTA without re-editing would still POST this field's (still-filled) PAN\n // while the persisted selection points at whatever was picked in between (F6's mismatched charge\n // shape, verbatim). N3's fix: re-dispatch immediately, from the effect itself, the instant\n // `draft.path` transitions back to `'new-card'` — not merely clear the guard and wait.\n //\n // T191 (FR-12, Decision 7): a second return shares this effect and its guard — the card payment\n // coming back because card mode brought it back (the card option with no method to name). The\n // collapse forgot the last pair sent (below), so the field's own card is sent again, once, and only\n // with a complete BIN. A return caused by a dispatched method (a candidate or a row) is not this one:\n // T174's next-keystroke rule covers it.\n useEffect(() => {\n const previousPath = previousDraftPathRef.current;\n previousDraftPathRef.current = draftPath;\n const revealedByCardMode = previousCollapsedRef.current && !collapsed && cardModeActive;\n previousCollapsedRef.current = collapsed;\n\n if (draftPath !== 'new-card') {\n lastDispatchedRef.current = null;\n return;\n }\n // Only on a genuine *return* from the saved-card detour, and only when this field currently\n // resolves to a network. `previousPath` must be checked against `'saved-card'` specifically, not\n // merely \"not new-card\": the very first activation transitions `null` (fresh mount, no path chosen\n // yet) → `'new-card'` too, and `handleNumberChange`'s own dispatch already covers that first\n // selection — re-dispatching here as well would double-count it. This also keeps the effect a\n // no-op on every ordinary keystroke while already on new-card, even though `digits`/`detectedMethod`\n // (both in the dependency array) change on every one of those too.\n //\n // V4 (T22): the `lastDispatched === null` check (see `shouldRedispatchOnReturnToNewCard`) guards a\n // *different* double-count than the one above — mounting (or arriving) with a saved card already\n // selected means `previousPath` starts as `'saved-card'` too, so the keystroke that gives the field\n // its eighth digit (the first one that sends anything, T190) is both a `handleNumberChange` dispatch\n // (which runs first, synchronously, inside the same commit, and sets `lastDispatchedRef.current`)\n // *and* this effect's own 'saved-card' → 'new-card' transition. Without this check both fire,\n // posting the identical `{ methodId, bin }` twice. It does not suppress the genuine \"stale digits\n // already in the field, no new keystroke\" case this effect exists for: leaving `'new-card'` always\n // resets `lastDispatchedRef.current` to `null` (below), so it is still `null` here whenever nothing\n // has re-dispatched since.\n // The `!detectedMethod` check is redundant with the predicate above at runtime — it exists so\n // TypeScript narrows `detectedMethod` for the two reads below, since it cannot follow the null check\n // through a separate function call.\n if (\n !shouldRedispatchOnReturnToNewCard({\n previousPath,\n revealedByCardMode,\n detectedMethod,\n lastDispatched: lastDispatchedRef.current,\n }) ||\n !detectedMethod\n ) {\n return;\n }\n\n // T190 (FR-2): the same eight-digit gate as `handleNumberChange`. A return with a shorter number in\n // the field sends nothing and leaves `lastDispatchedRef` untouched, so the keystroke that completes\n // the BIN dispatches it from the handler instead.\n const bin = resolveCompleteBin(digits);\n if (bin === null) return;\n lastDispatchedRef.current = { methodId: detectedMethod.id, bin };\n selectMethod?.({ methodId: detectedMethod.id, bin });\n }, [draftPath, collapsed, cardModeActive, detectedMethod, digits, selectMethod]);\n\n // T74 (FR-12, Decision 8): collapsing clears the security code, and only the security code — the\n // epic-wide rule that no credit-card component retains a security code across a visibility change.\n // The number, holder name, and expiration survive deliberately, so a shopper who switches method and\n // switches back finds what they typed. Keyed on `collapsed` alone (not `draft`, per NFR-3), so this\n // runs once per transition into collapsed, never on every keystroke or unrelated re-render. Dispatches\n // nothing and never touches `draft` — this is a local-state-only clear.\n //\n // Fix pass, finding R7: clears the CVV field's own error alongside its value. Left standing, the\n // field re-expands empty while still showing whatever \"invalid\"/\"required\" message belonged to the\n // value that just got wiped — a message about a value that no longer exists.\n //\n // T191 (FR-11): collapsing also forgets the last pair this component sent. The collapse means the\n // order form moved to another method, so the card's selection no longer stands, and the return\n // effect above re-sends it when card mode brings the form back.\n useEffect(() => {\n if (collapsed) {\n setCvv('');\n setFieldErrors((prev) => {\n if (!('cvv' in prev)) return prev;\n const next = { ...prev };\n delete next.cvv;\n return next;\n });\n lastDispatchedRef.current = null;\n }\n }, [collapsed]);\n\n const focusField = useCallback((key: FieldKey) => {\n const el = rootRef.current?.querySelector<HTMLInputElement>(\n `[data-fs-card-entry-field=\"${FIELD_ATTR[key]}\"] input`,\n );\n el?.focus();\n }, []);\n\n function clearFieldError(key: FieldKey) {\n setFieldErrors((prev) => {\n if (!(key in prev)) return prev;\n const next = { ...prev };\n delete next[key];\n return next;\n });\n }\n\n function runFieldValidation(key: FieldKey, draftValue: CardDraft) {\n const errors = validateDraft(draftValue, validator, methods, validationMessages);\n setFieldErrors((prev) => {\n const next = { ...prev };\n if (errors[key]) {\n next[key] = errors[key];\n } else {\n delete next[key];\n }\n return next;\n });\n }\n\n function handleNumberChange(event: ChangeEvent<HTMLInputElement>) {\n // F4: detect from this keystroke's own raw digits *before* masking, so the mask applied to this\n // render is the newly matched method's own `numberMask` — never the previous render's\n // (render-scoped `validator`), which was always one keystroke behind and, at a mask's capacity\n // boundary, never caught up at all.\n const rawDigits = toDigits(event.target.value);\n // V3 (T22): the same order-independent resolution the render-scoped `detectedMethod` memo above\n // uses — so the badge/validator and this dispatch never disagree about which candidate, if any, is\n // unambiguous.\n const matched = resolveNetworkMethod(methods, rawDigits);\n // N1 part 2: the mask capacity comes from `resolveNumberMask`, not merely `matched`'s own mask —\n // see its doc comment for why using only `matched` can trap the shopper below the digit count the\n // exact match needs.\n const masked = maskDigits(rawDigits, resolveNumberMask(methods, rawDigits));\n setNumber(masked);\n clearFieldError('number');\n\n const nextDigits = toDigits(masked);\n // T191 (FR-10, OQ-1 (b)): the first keystroke off the new-card path records it locally, before any\n // dispatch, so a checked saved card stops being the selection at digit 1. Sends nothing: the\n // eight-digit gate below still decides every selection this component sends.\n if (nextDigits.length > 0 && draftPath !== 'new-card') selection?.selectNewCard();\n const bin = resolveCompleteBin(nextDigits);\n\n if (matched) {\n const last = lastDispatchedRef.current;\n // T174 (FR-3, Decision 2): also re-dispatch once the engine has released the BIN this field last\n // sent — a successful plain method pick (FR-1) or T162's rollback clears `draft.bin` while the\n // field still resolves to the same pair, and republishing it with `setDraftBin` alone left a draft\n // no write carries, pending forever (M-4). `draft` is the state committed before this keystroke,\n // so after an ordinary successful dispatch it equals `last.bin` and nothing extra fires.\n //\n // The BIN is published only with its dispatch. A deduped keystroke finds `draft.bin` already\n // holding it, except when the keystroke lands after a release or rollback has run but before React\n // commits it: `draft` still shows the old BIN, so the term above cannot see the release, and\n // republishing would restore the released BIN with no write carrying it. Skipping the publish lets\n // the release land, and the next keystroke re-dispatches.\n //\n // T190 (FR-1, FR-3): all of the above waits for the eight-digit gate — `bin` is `null` until the\n // masked field holds eight digits, and a keystroke short of that is handled like a deduped one: no\n // publish, no dispatch, and `lastDispatchedRef` keeps the last pair sent. Detection, the mask and\n // the network mark above still follow every keystroke.\n if (selection && bin !== null && (last?.methodId !== matched.id || last.bin !== bin || draftBin !== last.bin)) {\n selection.setDraftBin(bin);\n lastDispatchedRef.current = { methodId: matched.id, bin };\n selection.selectMethod({ methodId: matched.id, bin });\n }\n } else {\n // F5: when no method matches (ambiguous prefix or unconfigured network),\n // clear draft.bin so binConfirmation does not wedge on 'pending' — a\n // non-null draftBin with no mutation in flight never resolves.\n selection?.setDraftBin(null);\n }\n }\n\n function handleNumberBlur() {\n runFieldValidation('number', { number, holderName, expiration, cvv });\n }\n\n function handleHolderNameChange(event: ChangeEvent<HTMLInputElement>) {\n setHolderName(event.target.value);\n clearFieldError('holderName');\n }\n\n function handleHolderNameBlur() {\n runFieldValidation('holderName', { number, holderName, expiration, cvv });\n }\n\n function handleExpirationChange(event: ChangeEvent<HTMLInputElement>) {\n const digits = normalizeExpirationDigits(toDigits(event.target.value), toDigits(expiration));\n setExpiration(maskDigits(digits, DEFAULT_EXPIRATION_MASK));\n clearFieldError('expiration');\n }\n\n function handleExpirationBlur() {\n runFieldValidation('expiration', { number, holderName, expiration, cvv });\n }\n\n function handleCvvChange(event: ChangeEvent<HTMLInputElement>) {\n setCvv(maskDigits(event.target.value, validator.cvvMask ?? DEFAULT_CVV_MASK));\n clearFieldError('cvv');\n }\n\n function handleCvvBlur() {\n runFieldValidation('cvv', { number, holderName, expiration, cvv });\n }\n\n // The new-card check (T27, specs/place-order-preflight-card-validation.md FR-2/FR-3). Resolves the\n // same verdict `submit`'s own guard has always resolved, extracted so the pre-flight — which now\n // runs this first, one network leg earlier (US-1) — and the continuation below can never disagree:\n // there is one function, not two copies of the predicate (Risk R1). Keeps both of the guard's\n // existing side effects: setFieldErrors and first-invalid focus (US-2). validateDraft itself is\n // unchanged (Decision 7).\n //\n // T74 (FR-11, Decision 5): while collapsed, refuses immediately with `CARD_ENTRY_COLLAPSED` — before\n // `validateDraft` ever runs — so neither `setFieldErrors` nor `focusField` fires: there is no visible\n // field for the shopper to read an error on or move focus to. `submit` below calls this same function\n // first and returns its refusals unchanged, so both the pre-flight and the submission refuse the same\n // way with no second guard to keep in sync. Keyed on `collapsed` (fix pass, finding R2), not the raw\n // `selectionOutOfScope`: with no picker composed this form never collapses, and a visible form still\n // validates first, so a shopper typing a new card is pointed at their own fields. T191's refusal below\n // holds only until the card's own selection reaches the order form.\n //\n // T191 (FR-13, Decision 8): a visible form refuses too, once its card is valid, while the persisted\n // selection is one this section does not serve, one that is not a card, or (fix round 1, finding R1)\n // another card network than the typed number's. In each the card's own selection has not reached the\n // order form (card mode, a refused or failed selection, no picker composed, or a candidate or row\n // picked after the card was typed), and posting it would charge it under that method's payment system.\n // Validation still runs first, so an incomplete card points at its own field.\n const checkDraft = useCallback<CardCapturePreflight>(() => {\n if (collapsed) return [CARD_ENTRY_COLLAPSED_ERROR];\n\n const errors = validateDraft({ number, holderName, expiration, cvv }, validator, methods, validationMessages);\n if (Object.keys(errors).length === 0) {\n return selectionOutOfScope || selectionNonCard || selectionOtherNetwork ? [CARD_ENTRY_COLLAPSED_ERROR] : [];\n }\n\n setFieldErrors(errors);\n const firstInvalid = VALIDATION_ORDER.find((key) => errors[key]);\n if (firstInvalid) focusField(firstInvalid);\n return [INVALID_CARD_DATA_ERROR];\n }, [\n collapsed,\n number,\n holderName,\n expiration,\n cvv,\n validator,\n methods,\n focusField,\n validationMessages,\n selectionOutOfScope,\n selectionNonCard,\n selectionOtherNetwork,\n ]);\n\n // The new-card submission (specs/card-entry.md § The submission continuation). Builds the card\n // inside this closure and hands it straight to the provider's target — the only channel the card\n // ever leaves this component through. Resolves only userErrors, never the card.\n const capture = useCallback<CardCapture>(\n async (send) => {\n // Still here, and still the same function the pre-flight calls (FR-3). The pre-flight normally\n // refuses first, so this is the guard for a provider that never resolved one.\n const refusals = checkDraft();\n if (refusals.length > 0) return refusals;\n\n const card: CapturedCard = {\n number,\n securityCode: cvv,\n expirationDate: expiration,\n holderName: holderName.trim(),\n };\n\n // F12: once the target succeeds, this component's only reason to hold the PAN and CVV is gone.\n // `clearOn: 'any-outcome'` clears after a refusal or a throw too (PRD REQ-03, the account).\n let succeeded = false;\n try {\n const userErrors = await send(card);\n succeeded = userErrors.length === 0;\n return userErrors;\n } finally {\n if (succeeded || clearOn === 'any-outcome') {\n setNumber('');\n setHolderName('');\n setExpiration('');\n setCvv('');\n }\n }\n },\n [checkDraft, number, holderName, expiration, cvv, clearOn],\n );\n\n useRegisterCardCapture(capture, checkDraft);\n\n return (\n // S-F1 fix pass: `hidden` lives on this outer wrapper, which never receives `className` (or any\n // other prop a compound swatch could ever resolve a class onto) — a compound-swatch selector only\n // ever descends from the node it styles (`&_[data-fs-…]`), never ascends to an ancestor, so nothing\n // authored on the styled node below can structurally reach up and override `display` here. See the\n // inner node's own comment for why that matters: Tailwind 4 puts `[hidden]{display:none}` in\n // `@layer base` while a compound-swatch `style` array compiles entirely into `@layer utilities`, and\n // `@layer theme, base, components, utilities;` is the declared order — a later layer beats\n // specificity regardless of it, so `hidden` sitting on the styled node itself would have been\n // silently defeated by an utterly ordinary layout class (`flex`, `grid`, `block`, …) a store author\n // has every reason to write there, with no intent to defeat a collapse. Renders no `null` and\n // unmounts nothing (FR-10): every field stays mounted underneath, hidden.\n <div hidden={collapsed} data-fs-card-entry-collapse-boundary=\"\">\n <div\n ref={rootRef}\n className={className}\n // T74 (FR-10, NFR-4): `collapsed` and `disabled` compose (Decision 6) — neither implies the\n // other. The actual hide is the outer wrapper's `hidden` above (S-F1); `inert` here is\n // defense-in-depth (NFR-4) for the accessibility tree/tab order on *this* node, not a mitigation\n // for a swatch overriding `display` — nothing here mitigates that, because nothing here needs\n // to: this node never carries the attribute whose effect a swatch could override in the first\n // place. Conditionally spread (never a bare `inert={false}`) so an unrecognized-boolean-attribute\n // fallback in an older React major never renders a literal `inert=\"false\"` — which, per the HTML\n // boolean-attribute rule, would make the element inert regardless of the string value (R9;\n // harmless on this repo's pinned React, relevant to the wider peer range this package supports).\n // Ships deliberately unasserted: React already drops a bare `inert={false}` itself, so no\n // test on this pinned version can tell the two spellings apart, and the behaviour that\n // matters — a collapsed `CardEntry` is inert — is already pinned by this component's\n // `hasAttribute('inert')` assertions.\n {...(placing || collapsed ? { inert: true } : {})}\n aria-disabled={placing || undefined}\n data-fs-card-entry=\"\"\n {...(placing ? { 'data-fs-card-entry-placing': '' } : {})}\n {...(disabled ? { 'data-fs-card-entry-disabled': '' } : {})}\n {...(collapsed ? { 'data-fs-card-entry-collapsed': '' } : {})}\n >\n <Fieldset.Root\n disabled={disabled || collapsed}\n // S-F6 fix pass: `disabled` here natively disables every contained field (FieldsetRoot's own\n // guideline: \"Set disabled on the root to disable all contained inputs\"), which — unlike\n // `hidden`/`inert` — also excludes them from a real `<form>`'s serialization. Latent today (no\n // component wraps these fields in a `<form>`), but a store author who does must not get card\n // fields serialized from a form the shopper cannot see. The *presentational* hook below\n // (`data-fs-card-entry-disabled`) stays keyed on the authored `disabled` prop alone (Decision 6)\n // — this native disable is a separate, serialization-safety mechanism, not a restyle.\n data-fs-card-entry-fieldset=\"\"\n >\n <Fieldset.Legend>{legend}</Fieldset.Legend>\n\n <div data-fs-card-entry-field=\"number\">\n <Field.Root invalid={Boolean(fieldErrors.number)} disabled={disabled} data-fs-card-entry-number=\"\">\n <Field.Label>{numberLabel}</Field.Label>\n <div data-fs-card-entry-number-control=\"\">\n <Field.Control\n name=\"cardNumber\"\n inputMode=\"numeric\"\n autoComplete=\"cc-number\"\n placeholder={numberPlaceholder}\n value={number}\n onChange={handleNumberChange}\n onBlur={handleNumberBlur}\n disabled={disabled}\n />\n {/* T172: the recognized network's mark — 0 or 1, never T71's row of every accepted\n method. Stays inside `data-fs-card-entry-number-control` (the `<input>`'s own\n wrapper) so a compound swatch can position it inside the input's box with CSS\n alone, needing no adornment slot on the `field` particle. Decorative: `Icon` is\n `aria-hidden` by default and nothing here gives the mark an accessible name, so\n the network is never announced on top of the digits the shopper just typed. */}\n {detectedMethod ? (\n <div data-fs-card-entry-networks=\"\">\n <span data-fs-card-entry-network=\"\" data-fs-card-entry-network-id={detectedMethod.id}>\n {detectedNetworkIcon ? (\n <Icon name={detectedNetworkIcon} data-fs-card-entry-network-icon=\"\" />\n ) : (\n <span data-fs-card-entry-network-fallback=\"\">{detectedMethod.name}</span>\n )}\n </span>\n </div>\n ) : null}\n </div>\n {fieldErrors.number ? <Field.Error match>{fieldErrors.number}</Field.Error> : null}\n </Field.Root>\n </div>\n\n {validator.holderNameRequired ? (\n <div data-fs-card-entry-field=\"holder-name\">\n <Field.Root invalid={Boolean(fieldErrors.holderName)} disabled={disabled}>\n <Field.Label>{holderNameLabel}</Field.Label>\n <Field.Control\n name=\"holderName\"\n autoComplete=\"cc-name\"\n placeholder={holderNamePlaceholder}\n value={holderName}\n onChange={handleHolderNameChange}\n onBlur={handleHolderNameBlur}\n disabled={disabled}\n />\n {fieldErrors.holderName ? <Field.Error match>{fieldErrors.holderName}</Field.Error> : null}\n </Field.Root>\n </div>\n ) : null}\n\n {validator.expirationRequired ? (\n <div data-fs-card-entry-field=\"expiration\">\n <Field.Root invalid={Boolean(fieldErrors.expiration)} disabled={disabled}>\n <Field.Label>{expirationLabel}</Field.Label>\n <Field.Control\n name=\"expiration\"\n inputMode=\"numeric\"\n autoComplete=\"cc-exp\"\n placeholder={expirationPlaceholder}\n value={expiration}\n onChange={handleExpirationChange}\n onBlur={handleExpirationBlur}\n disabled={disabled}\n />\n {fieldErrors.expiration ? <Field.Error match>{fieldErrors.expiration}</Field.Error> : null}\n </Field.Root>\n </div>\n ) : null}\n\n {validator.cvvRequired ? (\n <div data-fs-card-entry-field=\"cvv\">\n <Field.Root invalid={Boolean(fieldErrors.cvv)} disabled={disabled}>\n <Field.Label>{cvvLabel}</Field.Label>\n <Field.Control\n name=\"cvv\"\n type=\"text\"\n inputMode=\"numeric\"\n autoComplete=\"cc-csc\"\n placeholder={cvvPlaceholder}\n value={cvv}\n onChange={handleCvvChange}\n onBlur={handleCvvBlur}\n disabled={disabled}\n // T101 — obscures the digits without `type=\"password\"` (see security-code-mask.ts\n // for why: that type made real Chrome offer to save the code as a credential).\n style={cvvComplete ? SECURITY_CODE_MASK_STYLE : undefined}\n />\n {cvvDescription ? (\n <Field.Description data-fs-card-entry-cvv-description=\"\">{cvvDescription}</Field.Description>\n ) : null}\n {fieldErrors.cvv ? <Field.Error match>{fieldErrors.cvv}</Field.Error> : null}\n </Field.Root>\n </div>\n ) : null}\n </Fieldset.Root>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ import { ComponentDefinition } from "../../core/dist/define-component.js";
2
+ import "../../core/dist/index.js";
3
+ import { ComponentPropsWithRef, Ref } from "react";
4
+ //#region src/matter/cardholder-document/cardholder-document.d.ts
5
+ /**
6
+ * The cardholder-document field (specs/cardholder-document.md, T8 of the Credit Card epic;
7
+ * docs/features/credit-card/rfc.md § D3). Renders the existing `particles/document-field`
8
+ * `DocumentField` for CPF, gated on the *selected* payment method's validator: presence follows
9
+ * `documentDisplayable`, requiredness follows `documentRequired` — two distinct, non-null flags
10
+ * (T1's read model; `use-payment-method-selection.ts`'s `PaymentSectionValidator`), read off
11
+ * `usePayment().selectedMethod` (`selectedMethodId` resolved against the scoped `methods` list,
12
+ * carrying the full validator — specs/payment-section-root.md's contract table names T8 as the
13
+ * consumer of exactly these two flags on that field).
14
+ *
15
+ * Declares no fragment of its own: T5's merged `PaymentSectionData` fragment
16
+ * (`matter/payment/payment.tsx`) already selects both flags, so this leaf reads them off context
17
+ * instead of re-selecting them. The shipped precedent for a context-only, fragment-less payment
18
+ * leaf is `SavedCardCvvField` (`matter/saved-cards/saved-cards.tsx`), deliberately absent from
19
+ * `data-component-contract.test.ts`'s fragment-registration list for the same reason.
20
+ *
21
+ * The collected value is not sensitive — the RFC's "The card-data submission channel" section
22
+ * names the cardholder document alongside the save-card flag/label as travelling on the ordinary
23
+ * `draft`, never the submission registry — and is published via `setDraftDocument` on every
24
+ * change, mirroring `CardEntry`'s own `setDraftBin` idiom (`card-entry.tsx`): no validation, no
25
+ * dispatch, just keep the shared fact current. T7's `CardEntry` (merged) already folds
26
+ * `draft.document` into its gateway card object, so this component's only job is keeping that
27
+ * value current; it never itself calls `sendPayments` or registers a submission continuation.
28
+ *
29
+ * CPF-only by design, not by omission — see this task's own spec, § "Multi-country deferral", for
30
+ * why the read model cannot yet select a document type or a country, and what widening a future
31
+ * task would need to do about it.
32
+ */
33
+ export type CardholderDocumentProps = Omit<ComponentPropsWithRef<'div'>, 'defaultValue' | 'value' | 'onChange' | 'onBlur' | 'ref'> & {
34
+ label?: string;
35
+ placeholder?: string;
36
+ errorMessage?: string;
37
+ /** Disables the input without hiding it — distinct from the data-driven presence gate below. */
38
+ disabled?: boolean;
39
+ /** Forwarded to `DocumentField`, which lands it on the `<input>`, not on the wrapper `<div>`. */
40
+ ref?: Ref<HTMLInputElement>;
41
+ };
42
+ export declare const CardholderDocumentDefinition: ComponentDefinition<CardholderDocumentProps>;
43
+ export declare function CardholderDocument({ label, placeholder, errorMessage, disabled, className, ...props }: CardholderDocumentProps): import("react/jsx-runtime").JSX.Element | null;
44
+ //#endregion
45
+ //# sourceMappingURL=cardholder-document.d.ts.map
@@ -0,0 +1,70 @@
1
+ "use client";
2
+ import { defineComponent } from "../../core/dist/define-component.js";
3
+ import "../../core/dist/index.js";
4
+ import { usePayment } from "../payment/payment.js";
5
+ import { DocumentField } from "../../particles/document-field/document-field.js";
6
+ import { isCardholderDocumentShown } from "../payment/use-payment-submit-gate.js";
7
+ import { jsx } from "react/jsx-runtime";
8
+ //#region src/matter/cardholder-document/cardholder-document.tsx
9
+ const CardholderDocumentDefinition = defineComponent({
10
+ name: "CardholderDocument",
11
+ importPath: "@vtex/faststore/components",
12
+ component: CardholderDocument,
13
+ requiresContext: "payment",
14
+ editor: {
15
+ label: "Cardholder Document",
16
+ category: "commerce"
17
+ },
18
+ schema: {
19
+ label: {
20
+ type: "string",
21
+ default: "CPF",
22
+ label: "Label"
23
+ },
24
+ placeholder: {
25
+ type: "string",
26
+ default: "000.000.000-00",
27
+ label: "Placeholder"
28
+ },
29
+ errorMessage: {
30
+ type: "string",
31
+ default: "Invalid CPF",
32
+ label: "Error message"
33
+ },
34
+ disabled: {
35
+ type: "boolean",
36
+ default: false,
37
+ label: "Disabled"
38
+ }
39
+ },
40
+ guideline: "Cardholder tax-document field for the currently selected payment method (CPF only in this build): renders only when that method's validator marks a document displayable, and marks the input required when the validator marks the document required — the particle, not this leaf, decides whether a typed value is well-formed. Place as a sibling of CardEntry and SavedCards inside a PaymentSectionRoot or PaymentSectionController; renders nothing before a method is selected or when the selected method has no document to collect. The collected value is a plain draft fact — never a schema prop, a fragment field, or an action payload — and is unrelated to the shopper's saved profile document, which this component never reads. See cardholder-document.md."
41
+ });
42
+ function CardholderDocument({ label = "CPF", placeholder = "000.000.000-00", errorMessage = "Invalid CPF", disabled = false, className, ...props }) {
43
+ const { selectedMethod, draft, setDraftDocument, placing, cardPaymentCollapsed } = usePayment();
44
+ const documentRequired = (selectedMethod?.validator)?.documentRequired ?? false;
45
+ if (!isCardholderDocumentShown({
46
+ cardPaymentCollapsed,
47
+ selectedMethod
48
+ })) return null;
49
+ return /* @__PURE__ */ jsx(DocumentField, {
50
+ ...props,
51
+ className,
52
+ inert: placing,
53
+ "aria-disabled": placing || void 0,
54
+ documentType: "CPF",
55
+ requirement: documentRequired ? "mandatory" : "optional",
56
+ defaultValue: draft.document ?? "",
57
+ label,
58
+ placeholder,
59
+ errorMessage,
60
+ disabled,
61
+ onValueChange: (value) => setDraftDocument(value || null),
62
+ "data-fs-cardholder-document": "",
63
+ ...documentRequired ? { "data-fs-cardholder-document-required": "" } : {},
64
+ ...placing ? { "data-fs-cardholder-document-placing": "" } : {}
65
+ });
66
+ }
67
+ //#endregion
68
+ export { CardholderDocument, CardholderDocumentDefinition };
69
+
70
+ //# sourceMappingURL=cardholder-document.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cardholder-document.js","names":[],"sources":["../../../src/matter/cardholder-document/cardholder-document.tsx"],"sourcesContent":["'use client';\n\nimport { defineComponent } from '@vtex/fs-core';\nimport type { ComponentPropsWithRef, Ref } from 'react';\nimport { DocumentField } from '../../particles/document-field/document-field.js';\nimport { usePayment } from '../payment/payment.js';\nimport { isCardholderDocumentShown } from '../payment/use-payment-submit-gate.js';\n\n/**\n * The cardholder-document field (specs/cardholder-document.md, T8 of the Credit Card epic;\n * docs/features/credit-card/rfc.md § D3). Renders the existing `particles/document-field`\n * `DocumentField` for CPF, gated on the *selected* payment method's validator: presence follows\n * `documentDisplayable`, requiredness follows `documentRequired` — two distinct, non-null flags\n * (T1's read model; `use-payment-method-selection.ts`'s `PaymentSectionValidator`), read off\n * `usePayment().selectedMethod` (`selectedMethodId` resolved against the scoped `methods` list,\n * carrying the full validator — specs/payment-section-root.md's contract table names T8 as the\n * consumer of exactly these two flags on that field).\n *\n * Declares no fragment of its own: T5's merged `PaymentSectionData` fragment\n * (`matter/payment/payment.tsx`) already selects both flags, so this leaf reads them off context\n * instead of re-selecting them. The shipped precedent for a context-only, fragment-less payment\n * leaf is `SavedCardCvvField` (`matter/saved-cards/saved-cards.tsx`), deliberately absent from\n * `data-component-contract.test.ts`'s fragment-registration list for the same reason.\n *\n * The collected value is not sensitive — the RFC's \"The card-data submission channel\" section\n * names the cardholder document alongside the save-card flag/label as travelling on the ordinary\n * `draft`, never the submission registry — and is published via `setDraftDocument` on every\n * change, mirroring `CardEntry`'s own `setDraftBin` idiom (`card-entry.tsx`): no validation, no\n * dispatch, just keep the shared fact current. T7's `CardEntry` (merged) already folds\n * `draft.document` into its gateway card object, so this component's only job is keeping that\n * value current; it never itself calls `sendPayments` or registers a submission continuation.\n *\n * CPF-only by design, not by omission — see this task's own spec, § \"Multi-country deferral\", for\n * why the read model cannot yet select a document type or a country, and what widening a future\n * task would need to do about it.\n */\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\nexport type CardholderDocumentProps = Omit<\n ComponentPropsWithRef<'div'>,\n 'defaultValue' | 'value' | 'onChange' | 'onBlur' | 'ref'\n> & {\n label?: string;\n placeholder?: string;\n errorMessage?: string;\n /** Disables the input without hiding it — distinct from the data-driven presence gate below. */\n disabled?: boolean;\n /** Forwarded to `DocumentField`, which lands it on the `<input>`, not on the wrapper `<div>`. */\n ref?: Ref<HTMLInputElement>;\n};\n\n// ---------------------------------------------------------------------------\n// Definition\n// ---------------------------------------------------------------------------\n\nexport const CardholderDocumentDefinition = defineComponent<CardholderDocumentProps>({\n name: 'CardholderDocument',\n importPath: '@vtex/faststore/components',\n component: CardholderDocument,\n requiresContext: 'payment',\n editor: { label: 'Cardholder Document', category: 'commerce' },\n schema: {\n label: { type: 'string', default: 'CPF', label: 'Label' },\n placeholder: { type: 'string', default: '000.000.000-00', label: 'Placeholder' },\n errorMessage: { type: 'string', default: 'Invalid CPF', label: 'Error message' },\n disabled: { type: 'boolean', default: false, label: 'Disabled' },\n },\n guideline:\n \"Cardholder tax-document field for the currently selected payment method (CPF only in this build): renders only when that method's validator marks a document displayable, and marks the input required when the validator marks the document required — the particle, not this leaf, decides whether a typed value is well-formed. Place as a sibling of CardEntry and SavedCards inside a PaymentSectionRoot or PaymentSectionController; renders nothing before a method is selected or when the selected method has no document to collect. The collected value is a plain draft fact — never a schema prop, a fragment field, or an action payload — and is unrelated to the shopper's saved profile document, which this component never reads. See cardholder-document.md.\",\n});\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\nexport function CardholderDocument({\n label = 'CPF',\n placeholder = '000.000.000-00',\n errorMessage = 'Invalid CPF',\n disabled = false,\n className,\n ...props\n}: CardholderDocumentProps) {\n const { selectedMethod, draft, setDraftDocument, placing, cardPaymentCollapsed } = usePayment();\n const validator = selectedMethod?.validator;\n const documentRequired = validator?.documentRequired ?? false;\n\n // Presence follows `documentDisplayable` alone (specs/payment-section-root.md's contract table),\n // never the entry path: the validator is resolved off the selected *method*, not off\n // `draft.path`, matching the merged T5 spec verbatim. See this task's own spec, § \"Decisions\",\n // for the measured, deliberately-not-fixed interaction this has with a saved-card selection.\n // T191 (FR-7, Decision 3): nothing renders while the card payment is collapsed either, whatever the\n // selected method's own validator says. The field re-seeds from `draft.document` when it comes back.\n // T192 (Decision 3): `isCardholderDocumentShown` is that condition, defined once — the submit gate\n // reads the same function, so it never asks for a document this field is not showing.\n if (!isCardholderDocumentShown({ cardPaymentCollapsed, selectedMethod })) return null;\n\n return (\n <DocumentField\n {...props}\n className={className}\n // T28: `Field.Root` (Base UI) forwards unknown native props onto its rendered element\n // (`document-field.tsx`'s own `{...props}` spread), so `inert`/`aria-disabled` land on the same\n // element `data-fs-cardholder-document` is on — there is no separate root of this component's own.\n inert={placing}\n aria-disabled={placing || undefined}\n documentType=\"CPF\"\n requirement={documentRequired ? 'mandatory' : 'optional'}\n // Re-seeds from the shared draft on every mount, so a shopper's already-typed value survives\n // this leaf unmounting and remounting (e.g. `documentDisplayable` flips off then on again as\n // network detection re-resolves mid-typing) instead of starting blank again. Uncontrolled —\n // `DocumentField` owns masking and blur validation from here; this component never re-applies\n // its own mask (Decision \"Uncontrolled composition, not a second masking implementation\").\n defaultValue={draft.document ?? ''}\n label={label}\n placeholder={placeholder}\n errorMessage={errorMessage}\n disabled={disabled}\n onValueChange={(value) => setDraftDocument(value || null)}\n data-fs-cardholder-document=\"\"\n {...(documentRequired ? { 'data-fs-cardholder-document-required': '' } : {})}\n {...(placing ? { 'data-fs-cardholder-document-placing': '' } : {})}\n />\n );\n}\n"],"mappings":";;;;;;;;AA0DA,MAAa,+BAA+B,gBAAyC;CACnF,MAAM;CACN,YAAY;CACZ,WAAW;CACX,iBAAiB;CACjB,QAAQ;EAAE,OAAO;EAAuB,UAAU;CAAW;CAC7D,QAAQ;EACN,OAAO;GAAE,MAAM;GAAU,SAAS;GAAO,OAAO;EAAQ;EACxD,aAAa;GAAE,MAAM;GAAU,SAAS;GAAkB,OAAO;EAAc;EAC/E,cAAc;GAAE,MAAM;GAAU,SAAS;GAAe,OAAO;EAAgB;EAC/E,UAAU;GAAE,MAAM;GAAW,SAAS;GAAO,OAAO;EAAW;CACjE;CACA,WACE;AACJ,CAAC;AAMD,SAAgB,mBAAmB,EACjC,QAAQ,OACR,cAAc,kBACd,eAAe,eACf,WAAW,OACX,WACA,GAAG,SACuB;CAC1B,MAAM,EAAE,gBAAgB,OAAO,kBAAkB,SAAS,yBAAyB,WAAW;CAE9F,MAAM,oBADY,gBAAgB,UAAA,EACE,oBAAoB;CAUxD,IAAI,CAAC,0BAA0B;EAAE;EAAsB;CAAe,CAAC,GAAG,OAAO;CAEjF,OACE,oBAAC,eAAD;EACE,GAAI;EACO;EAIX,OAAO;EACP,iBAAe,WAAW,KAAA;EAC1B,cAAa;EACb,aAAa,mBAAmB,cAAc;EAM9C,cAAc,MAAM,YAAY;EACzB;EACM;EACC;EACJ;EACV,gBAAgB,UAAU,iBAAiB,SAAS,IAAI;EACxD,+BAA4B;EAC5B,GAAK,mBAAmB,EAAE,wCAAwC,GAAG,IAAI,CAAC;EAC1E,GAAK,UAAU,EAAE,uCAAuC,GAAG,IAAI,CAAC;CACjE,CAAA;AAEL"}
@@ -0,0 +1,77 @@
1
+ import { ComponentDefinition } from "../../core/dist/define-component.js";
2
+ import "../../core/dist/index.js";
3
+ import { ComponentActions } from "../../sdk/actions/types.js";
4
+ import { ComponentPropsWithRef, ReactNode } from "react";
5
+ //#region src/matter/client-preferences/client-preferences-fieldset.d.ts
6
+ /** The `$data` slice forwarded by the reactive runtime — the ClientPreferences sibling entity, unwrapped. */
7
+ export type ClientPreferencesFieldsetData = {
8
+ optinNewsLetter?: boolean | null;
9
+ savePersonalData?: boolean | null;
10
+ } | null;
11
+ export type ClientPreferencesFieldsetProps = {
12
+ /** Placeholder rows the loading skeleton draws. Set it to the number of controls composed here. */
13
+ skeletonCount?: number;
14
+ /** Announced politely while the preferences resolve. Blank means no announcement. */
15
+ loadingLabel?: string;
16
+ /** Announced politely once the controls mount. Blank means no announcement. */
17
+ loadedLabel?: string;
18
+ /** Accessible name of the `<fieldset>` group. Blank means no name is exposed. */
19
+ groupLabel?: string;
20
+ className?: string;
21
+ children?: ReactNode;
22
+ /** `ClientPreferencesFieldsetData` slice forwarded by the reactive runtime. */
23
+ $data?: ClientPreferencesFieldsetData;
24
+ /** Compiler-injected; carries `identity.clientPreferencesUpdate`. */
25
+ $actions?: ComponentActions;
26
+ };
27
+ type PreferenceControlProps = {
28
+ label?: string;
29
+ className?: string;
30
+ children?: ReactNode;
31
+ };
32
+ export type ClientPreferencesNewsletterControlProps = PreferenceControlProps;
33
+ export type ClientPreferencesSavePersonalDataControlProps = PreferenceControlProps;
34
+ export declare const ClientPreferencesFieldsetDefinition: ComponentDefinition<ClientPreferencesFieldsetProps>;
35
+ export declare const ClientPreferencesNewsletterControlDefinition: ComponentDefinition<PreferenceControlProps>;
36
+ export declare const ClientPreferencesSavePersonalDataControlDefinition: ComponentDefinition<PreferenceControlProps>;
37
+ /**
38
+ * Registers a batched write of the `ClientPreferences` entity with the enclosing `FormRoot`
39
+ * (`registerFieldset`, namespace `clientPreferences`) on mount, unconditionally — so this fieldset keeps
40
+ * its original dispatch slot even while its own query is still resolving. Its children — the seed values
41
+ * and the field-name registry its controls need — render only once the entity has resolved; until then
42
+ * the `Fieldset.Root` carries `-pending` and renders `ClientPreferencesFieldsetSkeleton` (Decision 5).
43
+ * `dispatch` and `toPayload` are `useCallback([])`-stable and read `$actions` through a ref, so the
44
+ * registration effect runs exactly once per mount regardless of how often the parent re-renders.
45
+ *
46
+ * "Resolved" is two conditions, because `$data` alone cannot answer it. Under the reactive runtime the
47
+ * slice is defined from the first render — `useReactiveEntity` hands down `fallbackData` while the cache
48
+ * read is incomplete — so an empty server answer looks exactly like a resolved one, and the checkboxes
49
+ * would mount seeded unchecked before the shopper's saved consent arrives. `useSeedAdoption` re-seeds a
50
+ * clean checkbox with `defaultValue` on every render, but once it turns dirty no later seed ever
51
+ * reaches it again: a shopper who toggles it before the real consent arrives locks that unchecked
52
+ * seed in for good, and even a shopper who does not toggle it sees the checkbox flash to the saved
53
+ * value. `useReactiveDataSettled` supplies
54
+ * the missing half: the page query's own in-flight state, latched so it only ever goes pending →
55
+ * settled. The `$data === undefined` half still carries trees with no reactive root above them, where
56
+ * the slice arrives as a plain prop.
57
+ *
58
+ * Both ends of that wait are spoken: the fieldset carries `aria-busy` while it lasts and holds a polite
59
+ * live region that announces `loadingLabel` and then `loadedLabel`. Style the region `sr-only` — it is
60
+ * text meant for a screen reader, not for the page.
61
+ */
62
+ export declare function ClientPreferencesFieldset({ skeletonCount, loadingLabel, loadedLabel, groupLabel, className, children, $data: $dataProp, $actions: $actionsProp }: ClientPreferencesFieldsetProps): import("react/jsx-runtime").JSX.Element;
63
+ export declare namespace ClientPreferencesFieldset {
64
+ var Skeleton: typeof ClientPreferencesFieldsetSkeleton;
65
+ }
66
+ /**
67
+ * The loading state of the fieldset, rendered in place of the controls until the entity resolves and
68
+ * declared as the definition's `skeleton`. Purely visual: the wait is spoken by the fieldset's own live
69
+ * region, so this is hidden from assistive technology rather than announced twice.
70
+ */
71
+ export declare function ClientPreferencesFieldsetSkeleton({ count, className, ...props }: ComponentPropsWithRef<'div'> & {
72
+ count?: number;
73
+ }): import("react/jsx-runtime").JSX.Element;
74
+ export declare function ClientPreferencesNewsletterControl({ label, className, children }: ClientPreferencesNewsletterControlProps): import("react/jsx-runtime").JSX.Element;
75
+ export declare function ClientPreferencesSavePersonalDataControl({ label, className, children }: ClientPreferencesSavePersonalDataControlProps): import("react/jsx-runtime").JSX.Element;
76
+ //#endregion
77
+ //# sourceMappingURL=client-preferences-fieldset.d.ts.map