@ozura/elements 1.1.0 → 1.2.1

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 (34) hide show
  1. package/README.md +1340 -1338
  2. package/dist/frame/element-frame.html +22 -22
  3. package/dist/frame/element-frame.js +13 -2
  4. package/dist/frame/element-frame.js.map +1 -1
  5. package/dist/frame/tokenizer-frame.html +11 -11
  6. package/dist/oz-elements.esm.js +262 -243
  7. package/dist/oz-elements.esm.js.map +1 -1
  8. package/dist/oz-elements.umd.js +263 -243
  9. package/dist/oz-elements.umd.js.map +1 -1
  10. package/dist/react/index.cjs.js +138 -112
  11. package/dist/react/index.cjs.js.map +1 -1
  12. package/dist/react/index.esm.js +137 -112
  13. package/dist/react/index.esm.js.map +1 -1
  14. package/dist/react/react/index.d.ts +50 -29
  15. package/dist/react/sdk/OzVault.d.ts +6 -5
  16. package/dist/react/sdk/createSessionFetcher.d.ts +29 -0
  17. package/dist/react/sdk/index.d.ts +6 -26
  18. package/dist/react/server/index.d.ts +126 -74
  19. package/dist/react/types/index.d.ts +70 -41
  20. package/dist/server/index.cjs.js +186 -88
  21. package/dist/server/index.cjs.js.map +1 -1
  22. package/dist/server/index.esm.js +185 -89
  23. package/dist/server/index.esm.js.map +1 -1
  24. package/dist/server/sdk/OzVault.d.ts +6 -5
  25. package/dist/server/sdk/createSessionFetcher.d.ts +29 -0
  26. package/dist/server/sdk/index.d.ts +6 -26
  27. package/dist/server/server/index.d.ts +126 -74
  28. package/dist/server/types/index.d.ts +70 -41
  29. package/dist/types/sdk/OzVault.d.ts +6 -5
  30. package/dist/types/sdk/createSessionFetcher.d.ts +29 -0
  31. package/dist/types/sdk/index.d.ts +6 -26
  32. package/dist/types/server/index.d.ts +126 -74
  33. package/dist/types/types/index.d.ts +70 -41
  34. package/package.json +1 -1
@@ -838,6 +838,85 @@ function validateBilling(billing) {
838
838
  */
839
839
  const PROTOCOL_VERSION = 1;
840
840
 
841
+ /**
842
+ * Creates a `getSessionKey` callback for `OzVault.create()` and `<OzElements>`.
843
+ *
844
+ * This is the recommended way to wire the SDK to your backend session endpoint.
845
+ * If you don't need custom headers or auth logic, pass `sessionUrl` directly to
846
+ * `OzVault.create()` or `<OzElements>` — it calls this helper internally.
847
+ *
848
+ * The callback POSTs `{ sessionId }` to `url` and reads `sessionKey` (or the
849
+ * legacy `waxKey`) from the JSON response, so it is compatible with both the
850
+ * new `createSessionMiddleware` and the old `createMintWaxMiddleware` backends.
851
+ *
852
+ * Each call enforces a **10-second timeout**. On pure network failures
853
+ * (offline, DNS, connection refused) the request is retried **once after 750ms**.
854
+ * HTTP 4xx/5xx errors are never retried — they indicate misconfiguration.
855
+ *
856
+ * @param url - Absolute or relative URL of your session endpoint, e.g. `'/api/oz-session'`.
857
+ *
858
+ * @example
859
+ * // Simplest — just pass sessionUrl, no need to call this directly
860
+ * const vault = await OzVault.create({ pubKey: 'pk_live_...', sessionUrl: '/api/oz-session' });
861
+ *
862
+ * @example
863
+ * // Manual — use when you need custom headers
864
+ * const vault = await OzVault.create({
865
+ * pubKey: 'pk_live_...',
866
+ * getSessionKey: createSessionFetcher('/api/oz-session'),
867
+ * });
868
+ */
869
+ function createSessionFetcher(url) {
870
+ const TIMEOUT_MS = 10000;
871
+ // Each attempt gets its own AbortController so a timeout on attempt 1 does
872
+ // not bleed into the retry.
873
+ const attemptFetch = (sessionId) => {
874
+ const controller = new AbortController();
875
+ const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
876
+ return fetch(url, {
877
+ method: 'POST',
878
+ headers: { 'Content-Type': 'application/json' },
879
+ body: JSON.stringify({ sessionId }),
880
+ signal: controller.signal,
881
+ }).finally(() => clearTimeout(timer));
882
+ };
883
+ return async (sessionId) => {
884
+ let res;
885
+ try {
886
+ res = await attemptFetch(sessionId);
887
+ }
888
+ catch (firstErr) {
889
+ // Timeout/abort — don't retry, we already waited the full duration.
890
+ if (firstErr instanceof Error && (firstErr.name === 'AbortError' || firstErr.name === 'TimeoutError')) {
891
+ throw new OzError(`Session endpoint timed out after ${TIMEOUT_MS / 1000}s (${url})`, undefined, 'timeout');
892
+ }
893
+ // Pure network error — retry once after a short pause.
894
+ await new Promise(resolve => setTimeout(resolve, 750));
895
+ try {
896
+ res = await attemptFetch(sessionId);
897
+ }
898
+ catch (retryErr) {
899
+ const msg = retryErr instanceof Error ? retryErr.message : 'Network error';
900
+ throw new OzError(`Could not reach session endpoint (${url}): ${msg}`, undefined, 'network');
901
+ }
902
+ }
903
+ const data = await res.json().catch(() => ({}));
904
+ if (!res.ok) {
905
+ throw new OzError(typeof data.error === 'string' && data.error
906
+ ? data.error
907
+ : `Session endpoint returned HTTP ${res.status}`, undefined, res.status >= 500 ? 'server' : res.status === 401 || res.status === 403 ? 'auth' : 'validation');
908
+ }
909
+ // Accept both new `sessionKey` and legacy `waxKey` for backward compatibility
910
+ // with backends that haven't migrated to createSessionMiddleware yet.
911
+ const key = (typeof data.sessionKey === 'string' ? data.sessionKey : '') ||
912
+ (typeof data.waxKey === 'string' ? data.waxKey : '');
913
+ if (!key.trim()) {
914
+ throw new OzError('Session endpoint response is missing sessionKey. Check your /api/oz-session implementation.', undefined, 'validation');
915
+ }
916
+ return key;
917
+ };
918
+ }
919
+
841
920
  function isCardMetadata(v) {
842
921
  return !!v && typeof v === 'object' && typeof v.last4 === 'string';
843
922
  }
@@ -877,7 +956,7 @@ class OzVault {
877
956
  * @internal
878
957
  */
879
958
  constructor(options, waxKey, tokenizationSessionId) {
880
- var _a, _b, _c, _d;
959
+ var _a, _b, _c, _d, _e;
881
960
  this.elements = new Map();
882
961
  this.elementsByType = new Map();
883
962
  this.bankElementsByType = new Map();
@@ -911,7 +990,12 @@ class OzVault {
911
990
  this.fonts = (_a = options.fonts) !== null && _a !== void 0 ? _a : [];
912
991
  this.resolvedAppearance = resolveAppearance(options.appearance);
913
992
  this.vaultId = `vault-${uuid()}`;
914
- this._maxTokenizeCalls = (_b = options.maxTokenizeCalls) !== null && _b !== void 0 ? _b : 3;
993
+ // sessionLimit takes precedence over legacy maxTokenizeCalls.
994
+ // null means unlimited — use Infinity so the ">=" check never triggers.
995
+ const rawLimit = options.sessionLimit !== undefined
996
+ ? options.sessionLimit
997
+ : ((_b = options.maxTokenizeCalls) !== null && _b !== void 0 ? _b : 3);
998
+ this._maxTokenizeCalls = rawLimit === null ? Infinity : rawLimit;
915
999
  this._debug = (_c = options.debug) !== null && _c !== void 0 ? _c : false;
916
1000
  this.boundHandleMessage = this.handleMessage.bind(this);
917
1001
  window.addEventListener('message', this.boundHandleMessage);
@@ -927,7 +1011,8 @@ class OzVault {
927
1011
  }
928
1012
  }, timeout);
929
1013
  }
930
- this._onWaxRefresh = options.onWaxRefresh;
1014
+ // onSessionRefresh takes precedence over legacy onWaxRefresh
1015
+ this._onWaxRefresh = (_e = options.onSessionRefresh) !== null && _e !== void 0 ? _e : options.onWaxRefresh;
931
1016
  this._onReady = options.onReady;
932
1017
  this.log('vault created', { vaultId: this.vaultId, frameBaseUrl: this.frameBaseUrl, maxTokenizeCalls: this._maxTokenizeCalls });
933
1018
  }
@@ -935,51 +1020,63 @@ class OzVault {
935
1020
  * Creates and returns a ready `OzVault` instance.
936
1021
  *
937
1022
  * Internally this:
938
- * 1. Generates a `tokenizationSessionId` (UUID).
1023
+ * 1. Generates a session UUID.
939
1024
  * 2. Starts loading the hidden tokenizer iframe immediately.
940
- * 3. Calls `options.fetchWaxKey(tokenizationSessionId)` concurrently — your
941
- * backend mints a session-bound wax key from the vault and returns it.
942
- * 4. Resolves with the vault instance once the wax key is stored. The iframe
1025
+ * 3. Fetches a session key from your backend concurrently — either via
1026
+ * `sessionUrl` (simplest), `getSessionKey` (custom headers/auth), or the
1027
+ * deprecated `fetchWaxKey` callback.
1028
+ * 4. Resolves with the vault instance once the session key is stored. The iframe
943
1029
  * has been loading the whole time, so `isReady` may already be true or
944
1030
  * will fire shortly after.
945
1031
  *
946
1032
  * The returned vault is ready to create elements immediately. `createToken()`
947
1033
  * additionally requires `vault.isReady` (tokenizer iframe loaded).
948
1034
  *
949
- * @throws {OzError} if `fetchWaxKey` throws, returns a non-string value, or returns an empty/whitespace-only string.
1035
+ * @throws {OzError} if the session fetch fails, times out, or returns an empty string.
950
1036
  */
951
1037
  static async create(options, signal) {
952
1038
  if (!options.pubKey || !options.pubKey.trim()) {
953
1039
  throw new OzError('pubKey is required in options. Obtain your public key from the Ozura admin.');
954
1040
  }
955
- if (typeof options.fetchWaxKey !== 'function') {
956
- throw new OzError('fetchWaxKey must be a function. See OzVault.create() docs for the expected signature.');
1041
+ // Normalize the session callback. Priority: sessionUrl > getSessionKey > fetchWaxKey (deprecated).
1042
+ // This allows merchants to use the clean new API without touching legacy code.
1043
+ let resolvedFetchKey;
1044
+ if (options.sessionUrl) {
1045
+ resolvedFetchKey = createSessionFetcher(options.sessionUrl);
1046
+ }
1047
+ else if (typeof options.getSessionKey === 'function') {
1048
+ resolvedFetchKey = options.getSessionKey;
1049
+ }
1050
+ else if (typeof options.fetchWaxKey === 'function') {
1051
+ resolvedFetchKey = options.fetchWaxKey;
1052
+ }
1053
+ else {
1054
+ throw new OzError('A session URL or callback is required. Pass sessionUrl, getSessionKey, or fetchWaxKey to OzVault.create().');
957
1055
  }
958
1056
  const tokenizationSessionId = uuid();
959
1057
  // Construct the vault immediately — this mounts the tokenizer iframe so it
960
- // starts loading while fetchWaxKey is in flight. The waxKey field starts
961
- // empty and is set below before create() returns.
1058
+ // starts loading while the session fetch is in flight.
962
1059
  const vault = new OzVault(options, '', tokenizationSessionId);
963
1060
  // If the caller provides an AbortSignal (e.g. React useEffect cleanup),
964
1061
  // destroy the vault immediately on abort so the tokenizer iframe and message
965
- // listener are removed synchronously rather than waiting for fetchWaxKey to
966
- // settle. This eliminates the brief double-iframe window in React StrictMode.
1062
+ // listener are removed synchronously rather than waiting for the session fetch
1063
+ // to settle. This eliminates the brief double-iframe window in React StrictMode.
967
1064
  const onAbort = () => vault.destroy();
968
1065
  signal === null || signal === void 0 ? void 0 : signal.addEventListener('abort', onAbort, { once: true });
969
1066
  let waxKey;
970
1067
  try {
971
- waxKey = await options.fetchWaxKey(tokenizationSessionId);
1068
+ waxKey = await resolvedFetchKey(tokenizationSessionId);
972
1069
  }
973
1070
  catch (err) {
974
1071
  signal === null || signal === void 0 ? void 0 : signal.removeEventListener('abort', onAbort);
975
1072
  vault.destroy();
976
1073
  if (signal === null || signal === void 0 ? void 0 : signal.aborted)
977
1074
  throw new OzError('OzVault.create() was cancelled.');
978
- // Preserve errorCode/retryable from OzError (e.g. timeout/network from createFetchWaxKey)
1075
+ // Preserve errorCode/retryable from OzError (e.g. timeout/network from createSessionFetcher)
979
1076
  // so callers can distinguish transient failures from config errors.
980
1077
  const originalCode = err instanceof OzError ? err.errorCode : undefined;
981
1078
  const msg = err instanceof Error ? err.message : 'Unknown error';
982
- throw new OzError(`fetchWaxKey threw an error: ${msg}`, undefined, originalCode);
1079
+ throw new OzError(`Session fetch threw an error: ${msg}`, undefined, originalCode);
983
1080
  }
984
1081
  signal === null || signal === void 0 ? void 0 : signal.removeEventListener('abort', onAbort);
985
1082
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
@@ -988,11 +1085,11 @@ class OzVault {
988
1085
  }
989
1086
  if (typeof waxKey !== 'string' || !waxKey.trim()) {
990
1087
  vault.destroy();
991
- throw new OzError('fetchWaxKey must return a non-empty wax key string. Check your mint endpoint.');
1088
+ throw new OzError('Session fetch returned an empty key. Check your session endpoint response — it must return { sessionKey: "..." }.');
992
1089
  }
993
1090
  // Static methods can access private fields of instances of the same class.
994
1091
  vault.waxKey = waxKey;
995
- vault._storedFetchWaxKey = options.fetchWaxKey;
1092
+ vault._storedFetchWaxKey = resolvedFetchKey;
996
1093
  // If the tokenizer iframe fired OZ_FRAME_READY before fetchWaxKey resolved,
997
1094
  // the OZ_INIT sent at that point had an empty waxKey. Send a follow-up now
998
1095
  // so the tokenizer has the key stored before any createToken() call.
@@ -1658,9 +1755,9 @@ class OzVault {
1658
1755
  // key without waiting for a vault rejection.
1659
1756
  this._tokenizeSuccessCount++;
1660
1757
  if (this._tokenizeSuccessCount >= this._maxTokenizeCalls) {
1661
- this.log('proactive wax key refresh triggered', { tokenizeSuccessCount: this._tokenizeSuccessCount, maxTokenizeCalls: this._maxTokenizeCalls });
1758
+ this.log('proactive session key refresh triggered', { tokenizeSuccessCount: this._tokenizeSuccessCount, maxTokenizeCalls: this._maxTokenizeCalls });
1662
1759
  this.refreshWaxKey().catch((err) => {
1663
- console.warn('[OzVault] Post-budget wax key refresh failed:', err instanceof Error ? err.message : err);
1760
+ console.warn('[OzVault] Post-budget session key refresh failed:', err instanceof Error ? err.message : err);
1664
1761
  });
1665
1762
  }
1666
1763
  }
@@ -1818,9 +1915,9 @@ class OzVault {
1818
1915
  // Same proactive refresh logic as card tokenization.
1819
1916
  this._tokenizeSuccessCount++;
1820
1917
  if (this._tokenizeSuccessCount >= this._maxTokenizeCalls) {
1821
- this.log('proactive wax key refresh triggered', { tokenizeSuccessCount: this._tokenizeSuccessCount, maxTokenizeCalls: this._maxTokenizeCalls });
1918
+ this.log('proactive session key refresh triggered', { tokenizeSuccessCount: this._tokenizeSuccessCount, maxTokenizeCalls: this._maxTokenizeCalls });
1822
1919
  this.refreshWaxKey().catch((err) => {
1823
- console.warn('[OzVault] Post-budget wax key refresh failed:', err instanceof Error ? err.message : err);
1920
+ console.warn('[OzVault] Post-budget session key refresh failed:', err instanceof Error ? err.message : err);
1824
1921
  });
1825
1922
  }
1826
1923
  }
@@ -1906,84 +2003,6 @@ class OzVault {
1906
2003
  }
1907
2004
  }
1908
2005
 
1909
- /**
1910
- * Creates a ready-to-use `fetchWaxKey` callback for `OzVault.create()` and `<OzElements>`.
1911
- *
1912
- * Calls your backend mint endpoint with `{ sessionId }` and returns the wax key string.
1913
- * Throws on non-OK responses or a missing `waxKey` field so the vault can surface the
1914
- * error through its normal error path.
1915
- *
1916
- * Each call enforces a 10-second per-attempt timeout. On a pure network-level
1917
- * failure (connection refused, DNS failure, etc.) the call is retried once after
1918
- * 750ms before throwing. HTTP errors (4xx/5xx) are never retried — they indicate
1919
- * an endpoint misconfiguration or an invalid key, not a transient failure.
1920
- *
1921
- * The mint endpoint is typically the one-line `createMintWaxHandler` / `createMintWaxMiddleware`
1922
- * from `@ozura/elements/server`.
1923
- *
1924
- * @param mintUrl - Absolute or relative URL of your wax-key mint endpoint, e.g. `'/api/mint-wax'`.
1925
- *
1926
- * @example
1927
- * // Vanilla JS
1928
- * const vault = await OzVault.create({
1929
- * pubKey: 'pk_live_...',
1930
- * fetchWaxKey: createFetchWaxKey('/api/mint-wax'),
1931
- * });
1932
- *
1933
- * @example
1934
- * // React
1935
- * <OzElements pubKey="pk_live_..." fetchWaxKey={createFetchWaxKey('/api/mint-wax')}>
1936
- */
1937
- function createFetchWaxKey(mintUrl) {
1938
- const TIMEOUT_MS = 10000;
1939
- // Each attempt gets its own AbortController so a timeout on attempt 1 does
1940
- // not bleed into the retry. Uses AbortController + setTimeout instead of
1941
- // AbortSignal.timeout() to support environments without that API.
1942
- const attemptFetch = (sessionId) => {
1943
- const controller = new AbortController();
1944
- const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
1945
- return fetch(mintUrl, {
1946
- method: 'POST',
1947
- headers: { 'Content-Type': 'application/json' },
1948
- body: JSON.stringify({ sessionId }),
1949
- signal: controller.signal,
1950
- }).finally(() => clearTimeout(timer));
1951
- };
1952
- return async (sessionId) => {
1953
- let res;
1954
- try {
1955
- res = await attemptFetch(sessionId);
1956
- }
1957
- catch (firstErr) {
1958
- // Abort/timeout should not be retried — the server received nothing or
1959
- // we already waited the full timeout duration.
1960
- if (firstErr instanceof Error && (firstErr.name === 'AbortError' || firstErr.name === 'TimeoutError')) {
1961
- throw new OzError(`Wax key mint timed out after ${TIMEOUT_MS / 1000}s (${mintUrl})`, undefined, 'timeout');
1962
- }
1963
- // Pure network error (offline, DNS, connection refused) — retry once
1964
- // after a short pause in case of a transient blip.
1965
- await new Promise(resolve => setTimeout(resolve, 750));
1966
- try {
1967
- res = await attemptFetch(sessionId);
1968
- }
1969
- catch (retryErr) {
1970
- const msg = retryErr instanceof Error ? retryErr.message : 'Network error';
1971
- throw new OzError(`Could not reach wax key mint endpoint (${mintUrl}): ${msg}`, undefined, 'network');
1972
- }
1973
- }
1974
- const data = await res.json().catch(() => ({}));
1975
- if (!res.ok) {
1976
- throw new OzError(typeof data.error === 'string' && data.error
1977
- ? data.error
1978
- : `Wax key mint failed (HTTP ${res.status})`, undefined, res.status >= 500 ? 'server' : res.status === 401 || res.status === 403 ? 'auth' : 'validation');
1979
- }
1980
- if (typeof data.waxKey !== 'string' || !data.waxKey.trim()) {
1981
- throw new OzError('Mint endpoint response is missing waxKey. Check your /api/mint-wax implementation.', undefined, 'validation');
1982
- }
1983
- return data.waxKey;
1984
- };
1985
- }
1986
-
1987
2006
  const OzContext = createContext({
1988
2007
  vault: null,
1989
2008
  initError: null,
@@ -2000,7 +2019,7 @@ const OzContext = createContext({
2000
2019
  * All `<OzCardNumber />`, `<OzExpiry />`, and `<OzCvv />` children must be
2001
2020
  * rendered inside this provider.
2002
2021
  */
2003
- function OzElements({ fetchWaxKey, pubKey, frameBaseUrl, fonts, onLoadError, loadTimeoutMs, onWaxRefresh, onReady, appearance, maxTokenizeCalls, debug, children }) {
2022
+ function OzElements({ sessionUrl, getSessionKey, fetchWaxKey, pubKey, frameBaseUrl, fonts, onLoadError, loadTimeoutMs, onSessionRefresh, onWaxRefresh, onReady, appearance, sessionLimit, maxTokenizeCalls, debug, children }) {
2004
2023
  const [vault, setVault] = useState(null);
2005
2024
  const [initError, setInitError] = useState(null);
2006
2025
  const [mountedCount, setMountedCount] = useState(0);
@@ -2008,13 +2027,14 @@ function OzElements({ fetchWaxKey, pubKey, frameBaseUrl, fonts, onLoadError, loa
2008
2027
  const [tokenizeCount, setTokenizeCount] = useState(0);
2009
2028
  const onLoadErrorRef = useRef(onLoadError);
2010
2029
  onLoadErrorRef.current = onLoadError;
2011
- const onWaxRefreshRef = useRef(onWaxRefresh);
2012
- onWaxRefreshRef.current = onWaxRefresh;
2030
+ const onWaxRefreshRef = useRef(onSessionRefresh !== null && onSessionRefresh !== void 0 ? onSessionRefresh : onWaxRefresh);
2031
+ onWaxRefreshRef.current = onSessionRefresh !== null && onSessionRefresh !== void 0 ? onSessionRefresh : onWaxRefresh;
2013
2032
  const onReadyRef = useRef(onReady);
2014
2033
  onReadyRef.current = onReady;
2015
- // Keep a ref to fetchWaxKey so changes don't trigger vault recreation
2016
- const fetchWaxKeyRef = useRef(fetchWaxKey);
2017
- fetchWaxKeyRef.current = fetchWaxKey;
2034
+ // Keep a ref to the session callback so changes don't trigger vault recreation.
2035
+ // Priority mirrors OzVault.create(): sessionUrl > getSessionKey > fetchWaxKey.
2036
+ const getSessionKeyRef = useRef(getSessionKey !== null && getSessionKey !== void 0 ? getSessionKey : fetchWaxKey);
2037
+ getSessionKeyRef.current = getSessionKey !== null && getSessionKey !== void 0 ? getSessionKey : fetchWaxKey;
2018
2038
  const appearanceKey = useMemo(() => appearance ? JSON.stringify(appearance) : '', [appearance]);
2019
2039
  const fontsKey = useMemo(() => fonts ? JSON.stringify(fonts) : '', [fonts]);
2020
2040
  useEffect(() => {
@@ -2041,7 +2061,11 @@ function OzElements({ fetchWaxKey, pubKey, frameBaseUrl, fonts, onLoadError, loa
2041
2061
  // synchronously rather than waiting for the promise to settle. Without this,
2042
2062
  // two hidden iframes and two window listeners briefly coexist.
2043
2063
  const abortController = new AbortController();
2044
- OzVault.create(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ pubKey, fetchWaxKey: (sessionId) => fetchWaxKeyRef.current(sessionId) }, (frameBaseUrl ? { frameBaseUrl } : {})), (parsedFonts ? { fonts: parsedFonts } : {})), (parsedAppearance ? { appearance: parsedAppearance } : {})), (onLoadErrorRef.current ? { onLoadError: fireLoadError, loadTimeoutMs } : {})), {
2064
+ OzVault.create(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ pubKey }, (sessionUrl
2065
+ ? { sessionUrl }
2066
+ : getSessionKeyRef.current
2067
+ ? { getSessionKey: (sessionId) => getSessionKeyRef.current(sessionId) }
2068
+ : {})), (frameBaseUrl ? { frameBaseUrl } : {})), (parsedFonts ? { fonts: parsedFonts } : {})), (parsedAppearance ? { appearance: parsedAppearance } : {})), (onLoadErrorRef.current ? { onLoadError: fireLoadError, loadTimeoutMs } : {})), {
2045
2069
  // Always install onWaxRefresh internally so we can reset tokenizeCount
2046
2070
  // when any wax key refresh occurs (reactive TTL expiry, post-budget
2047
2071
  // proactive, or visibility-change proactive). Without this the React
@@ -2063,11 +2087,12 @@ function OzElements({ fetchWaxKey, pubKey, frameBaseUrl, fonts, onLoadError, loa
2063
2087
  // the retry tokenization completes (a full fetchWaxKey + tokenize round-
2064
2088
  // trip separates them), so the count correctly resets to 0 then rises to
2065
2089
  // 1 after the retry notifyTokenize fires.
2066
- onWaxRefresh: () => {
2090
+ onSessionRefresh: () => {
2067
2091
  var _a;
2068
2092
  Promise.resolve().then(() => setTokenizeCount(0));
2069
2093
  (_a = onWaxRefreshRef.current) === null || _a === void 0 ? void 0 : _a.call(onWaxRefreshRef);
2070
- }, onReady: () => { var _a; return (_a = onReadyRef.current) === null || _a === void 0 ? void 0 : _a.call(onReadyRef); } }), (maxTokenizeCalls !== undefined ? { maxTokenizeCalls } : {})), (debug ? { debug: true } : {})), abortController.signal).then(v => {
2094
+ }, onReady: () => { var _a; return (_a = onReadyRef.current) === null || _a === void 0 ? void 0 : _a.call(onReadyRef); } }), (sessionLimit !== undefined ? { sessionLimit }
2095
+ : maxTokenizeCalls !== undefined ? { maxTokenizeCalls } : {})), (debug ? { debug: true } : {})), abortController.signal).then(v => {
2071
2096
  if (cancelled) {
2072
2097
  v.destroy();
2073
2098
  return;
@@ -2100,7 +2125,7 @@ function OzElements({ fetchWaxKey, pubKey, frameBaseUrl, fonts, onLoadError, loa
2100
2125
  setVault(null);
2101
2126
  setInitError(null);
2102
2127
  };
2103
- }, [pubKey, frameBaseUrl, loadTimeoutMs, appearanceKey, fontsKey, maxTokenizeCalls, debug]);
2128
+ }, [pubKey, sessionUrl, frameBaseUrl, loadTimeoutMs, appearanceKey, fontsKey, sessionLimit, maxTokenizeCalls, debug]);
2104
2129
  const notifyMount = useCallback(() => setMountedCount(n => n + 1), []);
2105
2130
  const notifyReady = useCallback(() => setReadyCount(n => n + 1), []);
2106
2131
  const notifyUnmount = useCallback(() => {
@@ -2436,5 +2461,5 @@ function OzBankCard({ style, styles, classNames, labels, labelStyle, labelClassN
2436
2461
  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] }));
2437
2462
  }
2438
2463
 
2439
- export { OzBankAccountNumber, OzBankCard, OzBankRoutingNumber, OzCard, OzCardNumber, OzCvv, OzElements, OzExpiry, createFetchWaxKey, useOzElements };
2464
+ export { OzBankAccountNumber, OzBankCard, OzBankRoutingNumber, OzCard, OzCardNumber, OzCvv, OzElements, OzExpiry, createSessionFetcher as createFetchWaxKey, createSessionFetcher, useOzElements };
2440
2465
  //# sourceMappingURL=index.esm.js.map