@ozura/elements 1.3.1-next.74 → 1.3.1-next.76

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.
@@ -1065,6 +1065,8 @@ class OzVault {
1065
1065
  this.bankTokenizeResolvers = new Map();
1066
1066
  // Track completion state per element for auto-advance (only fire on transition)
1067
1067
  this.completionState = new Map();
1068
+ // Latest per-field state for getFieldStates(), keyed by frameId.
1069
+ this.fieldStates = new Map();
1068
1070
  this.tokenizerFrame = null;
1069
1071
  this.tokenizerWindow = null;
1070
1072
  this.tokenizerReady = false;
@@ -1280,6 +1282,24 @@ class OzVault {
1280
1282
  return false;
1281
1283
  return els.every(el => this.completionState.get(el.frameId) === true);
1282
1284
  }
1285
+ /**
1286
+ * Snapshot of every created field's latest state, keyed by element type.
1287
+ * Card and bank fields are combined (their type keys never collide). Useful for
1288
+ * gating UI, rendering per-field errors, or showing the card brand without
1289
+ * wiring an onChange listener per element. Returns shallow copies so callers
1290
+ * cannot mutate the vault's internal state.
1291
+ */
1292
+ getFieldStates() {
1293
+ var _a, _b;
1294
+ const snapshot = {};
1295
+ for (const [type, el] of this.elementsByType) {
1296
+ snapshot[type] = Object.assign({}, ((_a = this.fieldStates.get(el.frameId)) !== null && _a !== void 0 ? _a : { empty: true, complete: false, valid: false }));
1297
+ }
1298
+ for (const [type, el] of this.bankElementsByType) {
1299
+ snapshot[type] = Object.assign({}, ((_b = this.fieldStates.get(el.frameId)) !== null && _b !== void 0 ? _b : { empty: true, complete: false, valid: false }));
1300
+ }
1301
+ return snapshot;
1302
+ }
1283
1303
  /**
1284
1304
  * `true` while a `createToken()` or `createBankToken()` call is in progress
1285
1305
  * (including the transparent wax-key refresh phase). Use this to keep the pay
@@ -1332,6 +1352,7 @@ class OzVault {
1332
1352
  if (existing) {
1333
1353
  this.elements.delete(existing.frameId);
1334
1354
  this.completionState.delete(existing.frameId);
1355
+ this.fieldStates.delete(existing.frameId);
1335
1356
  existing.destroy();
1336
1357
  }
1337
1358
  const el = new OzElement(type, options, this.vaultId, this.frameBaseUrl, this.fonts, this.resolvedAppearance, () => {
@@ -1339,9 +1360,11 @@ class OzVault {
1339
1360
  // don't grow unboundedly in SPA scenarios with repeated mount/unmount cycles.
1340
1361
  this.elements.delete(el.frameId);
1341
1362
  this.completionState.delete(el.frameId);
1363
+ this.fieldStates.delete(el.frameId);
1342
1364
  }, this._debug);
1343
1365
  this.elements.set(el.frameId, el);
1344
1366
  typeMap.set(type, el);
1367
+ this.fieldStates.set(el.frameId, { empty: true, complete: false, valid: false });
1345
1368
  return el;
1346
1369
  }
1347
1370
  /**
@@ -1641,6 +1664,10 @@ class OzVault {
1641
1664
  for (const frameId of this.completionState.keys()) {
1642
1665
  this.completionState.set(frameId, false);
1643
1666
  }
1667
+ // Mirror for field states: every created field returns to its default.
1668
+ for (const frameId of this.fieldStates.keys()) {
1669
+ this.fieldStates.set(frameId, { empty: true, complete: false, valid: false });
1670
+ }
1644
1671
  // NOTE: _tokenizeSuccessCount is intentionally NOT reset.
1645
1672
  // It reflects real server-side wax key budget consumption. Zeroing it
1646
1673
  // would desync the proactive refresh logic from the vault's state and
@@ -1688,6 +1715,7 @@ class OzVault {
1688
1715
  this.elementsByType.clear();
1689
1716
  this.bankElementsByType.clear();
1690
1717
  this.completionState.clear();
1718
+ this.fieldStates.clear();
1691
1719
  this._tokenizeSuccessCount = 0;
1692
1720
  (_a = this.tokenizerFrame) === null || _a === void 0 ? void 0 : _a.remove();
1693
1721
  this.tokenizerFrame = null;
@@ -1849,6 +1877,10 @@ class OzVault {
1849
1877
  // the previous session and justCompleted never fires, breaking auto-advance.
1850
1878
  if (msg.type === 'OZ_FRAME_READY') {
1851
1879
  this.completionState.set(frameId, false);
1880
+ // Mirror the completion reset: a reloaded iframe starts empty, so the
1881
+ // field state must return to default rather than serving pre-reload data
1882
+ // (e.g. a stale card brand) until the next OZ_CHANGE arrives.
1883
+ this.fieldStates.set(frameId, { empty: true, complete: false, valid: false });
1852
1884
  if (msg.__ozVersion !== PROTOCOL_VERSION) {
1853
1885
  console.warn(`[OzVault] Protocol version mismatch on element frame "${frameId}" — ` +
1854
1886
  `SDK expects v${PROTOCOL_VERSION}, frame reported v${typeof msg.__ozVersion === 'number' ? msg.__ozVersion : '(none)'}. ` +
@@ -1890,8 +1922,14 @@ class OzVault {
1890
1922
  var _a, _b, _c;
1891
1923
  const complete = msg.complete;
1892
1924
  const valid = msg.valid;
1925
+ // Narrow rather than blind-cast: a malformed/legacy frame that omits `empty`
1926
+ // must not write `undefined` into the public FieldState contract.
1927
+ const empty = typeof msg.empty === 'boolean' ? msg.empty : true;
1893
1928
  const wasComplete = (_a = this.completionState.get(el.frameId)) !== null && _a !== void 0 ? _a : false;
1894
1929
  this.completionState.set(el.frameId, complete && valid);
1930
+ this.fieldStates.set(el.frameId, Object.assign(Object.assign({ empty,
1931
+ complete,
1932
+ valid }, (typeof msg.error === 'string' ? { error: msg.error } : {})), (typeof msg.cardBrand === 'string' ? { cardBrand: msg.cardBrand } : {})));
1895
1933
  // Require valid too — avoids advancing at 13 digits for unknown-brand cards
1896
1934
  // where isComplete() fires before the user has finished typing.
1897
1935
  const justCompleted = complete && valid && !wasComplete;
@@ -2480,6 +2518,21 @@ function useOzElements() {
2480
2518
  const isTokenizing = (_c = vault === null || vault === void 0 ? void 0 : vault.isTokenizing) !== null && _c !== void 0 ? _c : false;
2481
2519
  return { createToken, createBankToken, reset, ready, initError, tokenizeCount, isComplete, isBankComplete, isTokenizing };
2482
2520
  }
2521
+ /**
2522
+ * Reactive snapshot of every created field's state, keyed by element type
2523
+ * (e.g. `cardNumber`, `cvv`, `accountNumber`). Each entry is
2524
+ * `{ empty, complete, valid, error?, cardBrand? }`. Recomputes whenever any
2525
+ * field fires a change event. Must be called inside an `<OzElements>` provider;
2526
+ * returns `{}` otherwise.
2527
+ *
2528
+ * @example
2529
+ * const states = useFieldStates();
2530
+ * if (states.cvv?.error) showCvvError(states.cvv.error);
2531
+ */
2532
+ function useFieldStates() {
2533
+ const { vault, changeTick } = useContext(OzContext);
2534
+ return useMemo(() => { var _a; return (_a = vault === null || vault === void 0 ? void 0 : vault.getFieldStates()) !== null && _a !== void 0 ? _a : {}; }, [vault, changeTick]);
2535
+ }
2483
2536
  const SKELETON_STYLE = {
2484
2537
  height: 46,
2485
2538
  borderRadius: 6,
@@ -2777,5 +2830,5 @@ function OzBankCard({ style, styles, classNames, labels, labelStyle, labelClassN
2777
2830
  return (jsxs("div", { className: className, style: { width: '100%', display: 'flex', flexDirection: 'column', gap: gapStr }, children: [jsxs("div", { children: [renderLabel(labels === null || labels === void 0 ? void 0 : labels.accountNumber), jsx(OzBankAccountNumber, { style: mergeStyles(style, styles === null || styles === void 0 ? void 0 : styles.accountNumber), className: classNames === null || classNames === void 0 ? void 0 : classNames.accountNumber, placeholder: (_a = placeholders === null || placeholders === void 0 ? void 0 : placeholders.accountNumber) !== null && _a !== void 0 ? _a : 'Account number', disabled: disabled, onChange: (e) => { fieldState.current.accountNumber = e; emitChange(); }, onFocus: () => { var _a; return (_a = onFocusRef.current) === null || _a === void 0 ? void 0 : _a.call(onFocusRef, 'accountNumber'); }, onBlur: () => { var _a; return (_a = onBlurRef.current) === null || _a === void 0 ? void 0 : _a.call(onBlurRef, 'accountNumber'); }, onReady: readyHandlers['accountNumber'] })] }), jsxs("div", { children: [renderLabel(labels === null || labels === void 0 ? void 0 : labels.routingNumber), jsx(OzBankRoutingNumber, { style: mergeStyles(style, styles === null || styles === void 0 ? void 0 : styles.routingNumber), className: classNames === null || classNames === void 0 ? void 0 : classNames.routingNumber, placeholder: (_b = placeholders === null || placeholders === void 0 ? void 0 : placeholders.routingNumber) !== null && _b !== void 0 ? _b : 'Routing number', disabled: disabled, onChange: (e) => { fieldState.current.routingNumber = e; emitChange(); }, onFocus: () => { var _a; return (_a = onFocusRef.current) === null || _a === void 0 ? void 0 : _a.call(onFocusRef, 'routingNumber'); }, onBlur: () => { var _a; return (_a = onBlurRef.current) === null || _a === void 0 ? void 0 : _a.call(onBlurRef, 'routingNumber'); }, onReady: readyHandlers['routingNumber'] })] }), errorNode] }));
2778
2831
  }
2779
2832
 
2780
- export { OzBankAccountNumber, OzBankCard, OzBankRoutingNumber, OzCard, OzCardNumber, OzCvv, OzElements, OzExpiry, createSessionFetcher as createFetchWaxKey, createSessionFetcher, useOzElements };
2833
+ export { OzBankAccountNumber, OzBankCard, OzBankRoutingNumber, OzCard, OzCardNumber, OzCvv, OzElements, OzExpiry, createSessionFetcher as createFetchWaxKey, createSessionFetcher, useFieldStates, useOzElements };
2781
2834
  //# sourceMappingURL=index.esm.js.map