@basis-theory/web-elements 3.0.0-beta.4 → 3.0.0-beta.5

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 (108) hide show
  1. package/dist/.vite/manifest.json +22 -11
  2. package/dist/basis-theory-sdk.es.js +1 -1
  3. package/dist/basis-theory-sdk.umd.js +1 -1
  4. package/dist/basis-theory-sdk.umd.js.map +1 -1
  5. package/dist/{create-card-B5zEHyRg.js → create-card-CB_YcP_4.js} +10 -6
  6. package/dist/create-card-CB_YcP_4.js.map +1 -0
  7. package/dist/create-card-display-kBupMJwe.js +280 -0
  8. package/dist/create-card-display-kBupMJwe.js.map +1 -0
  9. package/dist/{create-copy-button-DrAz8g0m.js → create-copy-button-2XtKTEo3.js} +5 -2
  10. package/dist/create-copy-button-2XtKTEo3.js.map +1 -0
  11. package/dist/{index-BOedIhUN.js → index-Dyy7tm5Z.js} +325 -108
  12. package/dist/index-Dyy7tm5Z.js.map +1 -0
  13. package/dist/index.d.ts +1119 -0
  14. package/dist/loader/basis-theory.min.js +1 -1
  15. package/dist/sri.json +9 -9
  16. package/dist/stats.html +1 -1
  17. package/package.json +12 -12
  18. package/dist/api-client/src/api-client.d.ts +0 -11
  19. package/dist/api-client/src/api-client.d.ts.map +0 -1
  20. package/dist/api-client/src/bin-lookup-transform.d.ts +0 -35
  21. package/dist/api-client/src/bin-lookup-transform.d.ts.map +0 -1
  22. package/dist/api-client/src/http-client.d.ts +0 -24
  23. package/dist/api-client/src/http-client.d.ts.map +0 -1
  24. package/dist/api-client/src/index.d.ts +0 -9
  25. package/dist/api-client/src/index.d.ts.map +0 -1
  26. package/dist/api-client/src/services/sessions.d.ts +0 -19
  27. package/dist/api-client/src/services/sessions.d.ts.map +0 -1
  28. package/dist/api-client/src/services/token-intents.d.ts +0 -17
  29. package/dist/api-client/src/services/token-intents.d.ts.map +0 -1
  30. package/dist/api-client/src/services/tokens.d.ts +0 -19
  31. package/dist/api-client/src/services/tokens.d.ts.map +0 -1
  32. package/dist/api-client/src/types.d.ts +0 -421
  33. package/dist/api-client/src/types.d.ts.map +0 -1
  34. package/dist/create-card-B5zEHyRg.js.map +0 -1
  35. package/dist/create-copy-button-DrAz8g0m.js.map +0 -1
  36. package/dist/index-BOedIhUN.js.map +0 -1
  37. package/dist/postmessage/src/client.d.ts +0 -7
  38. package/dist/postmessage/src/client.d.ts.map +0 -1
  39. package/dist/postmessage/src/event-map.d.ts +0 -139
  40. package/dist/postmessage/src/event-map.d.ts.map +0 -1
  41. package/dist/postmessage/src/index.d.ts +0 -9
  42. package/dist/postmessage/src/index.d.ts.map +0 -1
  43. package/dist/postmessage/src/server.d.ts +0 -11
  44. package/dist/postmessage/src/server.d.ts.map +0 -1
  45. package/dist/postmessage/src/types.d.ts +0 -75
  46. package/dist/postmessage/src/types.d.ts.map +0 -1
  47. package/dist/shared/src/api-url-resolver.d.ts +0 -22
  48. package/dist/shared/src/api-url-resolver.d.ts.map +0 -1
  49. package/dist/shared/src/debug-logger.d.ts +0 -16
  50. package/dist/shared/src/debug-logger.d.ts.map +0 -1
  51. package/dist/shared/src/deep-sanitization.d.ts +0 -24
  52. package/dist/shared/src/deep-sanitization.d.ts.map +0 -1
  53. package/dist/shared/src/errors.d.ts +0 -93
  54. package/dist/shared/src/errors.d.ts.map +0 -1
  55. package/dist/shared/src/generate-id.d.ts +0 -13
  56. package/dist/shared/src/generate-id.d.ts.map +0 -1
  57. package/dist/shared/src/index.d.ts +0 -14
  58. package/dist/shared/src/index.d.ts.map +0 -1
  59. package/dist/shared/src/logger-context.d.ts +0 -25
  60. package/dist/shared/src/logger-context.d.ts.map +0 -1
  61. package/dist/shared/src/proxy-validation.d.ts +0 -93
  62. package/dist/shared/src/proxy-validation.d.ts.map +0 -1
  63. package/dist/web-elements/src/BasisTheory.d.ts +0 -47
  64. package/dist/web-elements/src/BasisTheory.d.ts.map +0 -1
  65. package/dist/web-elements/src/basis-theory.d.ts +0 -85
  66. package/dist/web-elements/src/basis-theory.d.ts.map +0 -1
  67. package/dist/web-elements/src/elements/create-card-number.d.ts +0 -15
  68. package/dist/web-elements/src/elements/create-card-number.d.ts.map +0 -1
  69. package/dist/web-elements/src/elements/create-card.d.ts +0 -50
  70. package/dist/web-elements/src/elements/create-card.d.ts.map +0 -1
  71. package/dist/web-elements/src/elements/create-copy-button.d.ts +0 -19
  72. package/dist/web-elements/src/elements/create-copy-button.d.ts.map +0 -1
  73. package/dist/web-elements/src/elements/create-cvv.d.ts +0 -11
  74. package/dist/web-elements/src/elements/create-cvv.d.ts.map +0 -1
  75. package/dist/web-elements/src/elements/create-expiry.d.ts +0 -11
  76. package/dist/web-elements/src/elements/create-expiry.d.ts.map +0 -1
  77. package/dist/web-elements/src/elements/create-text.d.ts +0 -14
  78. package/dist/web-elements/src/elements/create-text.d.ts.map +0 -1
  79. package/dist/web-elements/src/index.d.ts +0 -27
  80. package/dist/web-elements/src/index.d.ts.map +0 -1
  81. package/dist/web-elements/src/mounter/create-mounter.d.ts +0 -11
  82. package/dist/web-elements/src/mounter/create-mounter.d.ts.map +0 -1
  83. package/dist/web-elements/src/theme/default-theme.d.ts +0 -22
  84. package/dist/web-elements/src/theme/default-theme.d.ts.map +0 -1
  85. package/dist/web-elements/src/theme/inject-theme-dark.d.ts +0 -13
  86. package/dist/web-elements/src/theme/inject-theme-dark.d.ts.map +0 -1
  87. package/dist/web-elements/src/theme/inject-theme.d.ts +0 -20
  88. package/dist/web-elements/src/theme/inject-theme.d.ts.map +0 -1
  89. package/dist/web-elements/src/types/events.d.ts +0 -165
  90. package/dist/web-elements/src/types/events.d.ts.map +0 -1
  91. package/dist/web-elements/src/types/theme.d.ts +0 -91
  92. package/dist/web-elements/src/types/theme.d.ts.map +0 -1
  93. package/dist/web-elements/src/types.d.ts +0 -667
  94. package/dist/web-elements/src/types.d.ts.map +0 -1
  95. package/dist/web-elements/src/utils/create-event-dispatcher.d.ts +0 -47
  96. package/dist/web-elements/src/utils/create-event-dispatcher.d.ts.map +0 -1
  97. package/dist/web-elements/src/utils/create-iframe.d.ts +0 -10
  98. package/dist/web-elements/src/utils/create-iframe.d.ts.map +0 -1
  99. package/dist/web-elements/src/utils/resolve-element-references.d.ts +0 -20
  100. package/dist/web-elements/src/utils/resolve-element-references.d.ts.map +0 -1
  101. package/dist/web-elements/src/utils/validate-element-options.d.ts +0 -11
  102. package/dist/web-elements/src/utils/validate-element-options.d.ts.map +0 -1
  103. package/dist/web-elements/src/utils/validate-sdk-config.d.ts +0 -15
  104. package/dist/web-elements/src/utils/validate-sdk-config.d.ts.map +0 -1
  105. package/dist/web-elements/src/utils/warn-unknown-keys.d.ts +0 -15
  106. package/dist/web-elements/src/utils/warn-unknown-keys.d.ts.map +0 -1
  107. package/dist/web-elements/src/version.d.ts +0 -25
  108. package/dist/web-elements/src/version.d.ts.map +0 -1
@@ -15,7 +15,9 @@ const REDACT_PATTERNS = [
15
15
  /secret/i,
16
16
  /token/i,
17
17
  /ssn/i,
18
- /track/i
18
+ /track/i,
19
+ /session_key/i,
20
+ /^apikey$/i
19
21
  ];
20
22
  const isPANField = (key) => {
21
23
  return PAN_PATTERNS.some((pattern) => pattern.test(key));
@@ -140,6 +142,16 @@ const createDebugLogger = (packageName, enabled) => {
140
142
  console.log(`🔍 [${timestamp}] [${packageName}] ${action}`, sanitized);
141
143
  };
142
144
  };
145
+ const generateId = () => {
146
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
147
+ return crypto.randomUUID();
148
+ }
149
+ const random = () => Math.random().toString(36).substring(2);
150
+ const randomPart = (random() + random() + random() + random()).substring(0, 32);
151
+ return `${randomPart.substring(0, 8)}-${randomPart.substring(8, 12)}-${randomPart.substring(12, 16)}-${randomPart.substring(16, 20)}-${randomPart.substring(20, 32)}`;
152
+ };
153
+ const log = (action, metadata) => {
154
+ };
143
155
  const createBasisTheoryError = (code, message, details) => {
144
156
  const error = new Error(message);
145
157
  error.name = "BasisTheoryError";
@@ -183,8 +195,6 @@ const ConfigurationError = (message, details) => {
183
195
  error.name = "ConfigurationError";
184
196
  return error;
185
197
  };
186
- const log = (action, metadata) => {
187
- };
188
198
  const ALLOWED_PROXY_SCHEMES = /* @__PURE__ */ new Set(["https:"]);
189
199
  const ALLOWED_METHODS = /* @__PURE__ */ new Set([
190
200
  "GET",
@@ -463,9 +473,10 @@ const createPostMessageClient = (iframe, config) => {
463
473
  try {
464
474
  callback(payload);
465
475
  } catch (err) {
466
- if (config.debug) {
467
- console.error(`[PostMessage Client] Error in event subscriber for ${type}:`, err);
468
- }
476
+ log2("Error in event subscriber", {
477
+ type,
478
+ error: err.message
479
+ });
469
480
  }
470
481
  });
471
482
  } else if (!nonce) {
@@ -479,7 +490,7 @@ const createPostMessageClient = (iframe, config) => {
479
490
  const retryConfig = typeof config.retries === "number" ? { ...DEFAULT_RETRY_CONFIG, maxRetries: config.retries } : typeof config.retries === "object" ? config.retries : DEFAULT_RETRY_CONFIG;
480
491
  const sendRequestOnce = (type, payload) => {
481
492
  return new Promise((resolve, reject) => {
482
- const nonce = crypto.randomUUID();
493
+ const nonce = generateId();
483
494
  const timestamp = Date.now();
484
495
  if (!iframe.contentWindow) {
485
496
  reject(BasisTheoryError("IFRAME_NOT_READY", "Iframe not ready"));
@@ -743,6 +754,9 @@ const createCardNumberElement = (apiKey, sdkConfig, options = {}, coordinatorEle
743
754
  get mounted() {
744
755
  return isMounted;
745
756
  },
757
+ get loaded() {
758
+ return isMounted;
759
+ },
746
760
  mount: async (selector) => {
747
761
  if (isMounted) {
748
762
  throw ElementError("Element is already mounted");
@@ -954,6 +968,9 @@ const createCVVElement = (apiKey, sdkConfig, options = {}, coordinatorElementId,
954
968
  get mounted() {
955
969
  return isMounted;
956
970
  },
971
+ get loaded() {
972
+ return isMounted;
973
+ },
957
974
  mount: async (selector) => {
958
975
  if (isMounted) {
959
976
  throw ElementError("Element is already mounted", id);
@@ -1144,6 +1161,9 @@ const createExpiryElement = (apiKey, sdkConfig, options = {}, coordinatorElement
1144
1161
  get mounted() {
1145
1162
  return isMounted;
1146
1163
  },
1164
+ get loaded() {
1165
+ return isMounted;
1166
+ },
1147
1167
  mount: async (selector) => {
1148
1168
  if (isMounted) {
1149
1169
  throw ElementError("Element is already mounted", id);
@@ -1337,6 +1357,9 @@ const createTextElement = (apiKey, sdkConfig, options = {}, coordinatorElementId
1337
1357
  get mounted() {
1338
1358
  return isMounted;
1339
1359
  },
1360
+ get loaded() {
1361
+ return isMounted;
1362
+ },
1340
1363
  mount: async (selector) => {
1341
1364
  if (isMounted) {
1342
1365
  throw ElementError("Element is already mounted", id);
@@ -1712,6 +1735,7 @@ const warnUnknownKeys = (label, obj, knownKeys, deprecatedKeys) => {
1712
1735
  };
1713
1736
  const KNOWN_SDK_KEYS = /* @__PURE__ */ new Set([
1714
1737
  "elementsBaseUrl",
1738
+ "whitelabelDomain",
1715
1739
  "apiBaseUrl",
1716
1740
  "debug",
1717
1741
  "measurePerformance",
@@ -1719,7 +1743,8 @@ const KNOWN_SDK_KEYS = /* @__PURE__ */ new Set([
1719
1743
  "retryConfig",
1720
1744
  "themeMode",
1721
1745
  "theme",
1722
- "darkTheme"
1746
+ "darkTheme",
1747
+ "sessionAuthorizationUrl"
1723
1748
  ]);
1724
1749
  const DEPRECATED_SDK_KEYS = /* @__PURE__ */ new Map([
1725
1750
  ["iframeOrigin", 'Use "elementsBaseUrl" instead.']
@@ -1787,7 +1812,44 @@ const validateSDKConfig = (options = {}) => {
1787
1812
  if (!["light", "dark", "auto"].includes(themeMode)) {
1788
1813
  throw ConfigurationError('themeMode must be "light", "dark", or "auto"');
1789
1814
  }
1790
- const finalBaseUrl = baseUrl || "https://js.basistheory.com/3.0.0-beta.4/elements";
1815
+ if (options.sessionAuthorizationUrl !== void 0) {
1816
+ if (typeof options.sessionAuthorizationUrl !== "string" || options.sessionAuthorizationUrl.trim() === "") {
1817
+ throw ConfigurationError("sessionAuthorizationUrl must be a non-empty string");
1818
+ }
1819
+ try {
1820
+ const parsed = new URL(options.sessionAuthorizationUrl, window.location.origin);
1821
+ if (parsed.protocol === "http:" && parsed.hostname !== "localhost" && parsed.hostname !== "127.0.0.1") {
1822
+ throw ConfigurationError("sessionAuthorizationUrl must use https://.");
1823
+ }
1824
+ } catch (error) {
1825
+ if (error instanceof Error && error.name === "ConfigurationError") {
1826
+ throw error;
1827
+ }
1828
+ }
1829
+ }
1830
+ let resolvedBaseUrl = baseUrl;
1831
+ if (resolvedBaseUrl === void 0 && options.whitelabelDomain !== void 0) {
1832
+ const { whitelabelDomain } = options;
1833
+ if (typeof whitelabelDomain !== "string" || whitelabelDomain.trim().length === 0) {
1834
+ throw ConfigurationError("whitelabelDomain must be a non-empty string");
1835
+ }
1836
+ let domainOrigin;
1837
+ try {
1838
+ const parsed = new URL(whitelabelDomain);
1839
+ if (parsed.protocol !== "https:") {
1840
+ throw ConfigurationError("whitelabelDomain must use https://");
1841
+ }
1842
+ domainOrigin = parsed.origin;
1843
+ } catch (error) {
1844
+ if (error instanceof Error && error.name === "ConfigurationError") {
1845
+ throw error;
1846
+ }
1847
+ throw ConfigurationError("whitelabelDomain must be a valid URL");
1848
+ }
1849
+ const { pathname } = new URL("https://js.basistheory.com/3.0.0-beta.5/elements");
1850
+ resolvedBaseUrl = `${domainOrigin}${pathname}`;
1851
+ }
1852
+ const finalBaseUrl = resolvedBaseUrl || "https://js.basistheory.com/3.0.0-beta.5/elements";
1791
1853
  const iframeOrigin = new URL(finalBaseUrl).origin;
1792
1854
  return {
1793
1855
  iframeOrigin,
@@ -1803,7 +1865,9 @@ const validateSDKConfig = (options = {}) => {
1803
1865
  // Light theme or only theme
1804
1866
  darkTheme: options.darkTheme ? mergeWithDefaultTheme(options.darkTheme) : void 0,
1805
1867
  // Optional dark theme
1806
- retryConfig
1868
+ retryConfig,
1869
+ sessionAuthorizationUrl: options.sessionAuthorizationUrl
1870
+ // Optional - required for cardDisplay
1807
1871
  };
1808
1872
  };
1809
1873
  const COMMON_ELEMENT_KEYS = /* @__PURE__ */ new Set([
@@ -1841,6 +1905,12 @@ const ELEMENT_TYPE_KEYS = {
1841
1905
  "value",
1842
1906
  "text",
1843
1907
  "disabled"
1908
+ ]),
1909
+ cardDisplay: /* @__PURE__ */ new Set([
1910
+ "tokenId",
1911
+ "number",
1912
+ "cvc",
1913
+ "expiration"
1844
1914
  ])
1845
1915
  };
1846
1916
  const validateElementOptions = (type, options = {}) => {
@@ -1941,7 +2011,109 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
1941
2011
  let coordinatorClient = null;
1942
2012
  let coordinatorReadyPromise = null;
1943
2013
  let coordinatorInitStarted = false;
2014
+ let coordinatorLoadTimeout = null;
2015
+ let rejectCoordinatorLoad = null;
2016
+ let destroyed = false;
2017
+ const assertNotDestroyed = () => {
2018
+ if (destroyed) {
2019
+ throw ConfigurationError(
2020
+ "SDK instance has been destroyed. Create a new instance with BasisTheory()"
2021
+ );
2022
+ }
2023
+ };
2024
+ const guardSync = (fn) => (...args) => {
2025
+ assertNotDestroyed();
2026
+ return fn(...args);
2027
+ };
2028
+ const guardAsync = (fn) => async (...args) => {
2029
+ assertNotDestroyed();
2030
+ return fn(...args);
2031
+ };
1944
2032
  let currentThemeMode = options.themeMode || "auto";
2033
+ const createLazyElementWrapper = (lazyFactory, elementType, elementId, elementApiKey, elementConfig, elementOptions, ensureCoordinator, notifyUnmount, coordinatorRequest) => {
2034
+ let realElement = null;
2035
+ let loadPromise = null;
2036
+ const loadRealElement = async () => {
2037
+ if (!loadPromise) {
2038
+ loadPromise = (async () => {
2039
+ const factory = await lazyFactory();
2040
+ realElement = factory(
2041
+ elementApiKey,
2042
+ elementConfig,
2043
+ elementOptions,
2044
+ elementId,
2045
+ ensureCoordinator,
2046
+ notifyUnmount,
2047
+ coordinatorRequest
2048
+ );
2049
+ })();
2050
+ }
2051
+ await loadPromise;
2052
+ };
2053
+ const ensureLoaded = () => {
2054
+ if (!realElement) throw ElementError("Element not initialized");
2055
+ return realElement;
2056
+ };
2057
+ const wrapper = {
2058
+ id: elementId,
2059
+ type: elementType,
2060
+ get mounted() {
2061
+ return (realElement == null ? void 0 : realElement.mounted) ?? false;
2062
+ },
2063
+ get loaded() {
2064
+ return (realElement == null ? void 0 : realElement.loaded) ?? false;
2065
+ },
2066
+ mount: async (selector) => {
2067
+ await loadRealElement();
2068
+ return realElement.mount(selector);
2069
+ },
2070
+ unmount: () => ensureLoaded().unmount(),
2071
+ update: async (opts) => ensureLoaded().update(opts),
2072
+ focus: () => ensureLoaded().focus(),
2073
+ blur: () => ensureLoaded().blur(),
2074
+ clear: () => ensureLoaded().clear(),
2075
+ on: (eventType, listener) => {
2076
+ if (!realElement) {
2077
+ let unsubscribe = null;
2078
+ let cancelled = false;
2079
+ loadRealElement().then(() => {
2080
+ if (!cancelled) unsubscribe = realElement.on(eventType, listener);
2081
+ });
2082
+ return () => {
2083
+ cancelled = true;
2084
+ unsubscribe == null ? void 0 : unsubscribe();
2085
+ };
2086
+ }
2087
+ return realElement.on(eventType, listener);
2088
+ },
2089
+ setValueRef: async (refElement) => {
2090
+ if (!realElement) {
2091
+ await loadRealElement();
2092
+ }
2093
+ if (realElement && "setValueRef" in realElement && typeof realElement.setValueRef === "function") {
2094
+ return realElement.setValueRef(refElement);
2095
+ }
2096
+ throw ElementError("setValueRef not available on this element type");
2097
+ }
2098
+ };
2099
+ if (elementType === "card") {
2100
+ Object.defineProperties(wrapper, {
2101
+ number: {
2102
+ get: () => realElement == null ? void 0 : realElement.number,
2103
+ enumerable: true
2104
+ },
2105
+ expiryDate: {
2106
+ get: () => realElement == null ? void 0 : realElement.expiryDate,
2107
+ enumerable: true
2108
+ },
2109
+ cvc: {
2110
+ get: () => realElement == null ? void 0 : realElement.cvc,
2111
+ enumerable: true
2112
+ }
2113
+ });
2114
+ }
2115
+ return wrapper;
2116
+ };
1945
2117
  const notifyCoordinatorElementUnmount = async (elementId) => {
1946
2118
  if (!coordinatorClient) {
1947
2119
  log2("Cannot notify coordinator of unmount - coordinator not initialized", {
@@ -1961,22 +2133,36 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
1961
2133
  });
1962
2134
  }
1963
2135
  };
2136
+ const sendCoordinatorRequest = async (command, payload) => {
2137
+ if (!coordinatorClient) {
2138
+ throw CoordinatorError("Coordinator not initialized");
2139
+ }
2140
+ return coordinatorClient.sendRequest(command, payload);
2141
+ };
1964
2142
  let cardElementFactory = null;
1965
2143
  let copyButtonElementFactory = null;
2144
+ let cardDisplayElementFactory = null;
1966
2145
  const lazyCardElement = async () => {
1967
2146
  if (!cardElementFactory) {
1968
- const module = await import("./create-card-B5zEHyRg.js");
2147
+ const module = await import("./create-card-CB_YcP_4.js");
1969
2148
  cardElementFactory = module.createCardElement;
1970
2149
  }
1971
2150
  return cardElementFactory;
1972
2151
  };
1973
2152
  const lazyCopyButtonElement = async () => {
1974
2153
  if (!copyButtonElementFactory) {
1975
- const module = await import("./create-copy-button-DrAz8g0m.js");
2154
+ const module = await import("./create-copy-button-2XtKTEo3.js");
1976
2155
  copyButtonElementFactory = module.createCopyButtonElement;
1977
2156
  }
1978
2157
  return copyButtonElementFactory;
1979
2158
  };
2159
+ const lazyCardDisplayElement = async () => {
2160
+ if (!cardDisplayElementFactory) {
2161
+ const module = await import("./create-card-display-kBupMJwe.js");
2162
+ cardDisplayElementFactory = module.createCardDisplayElement;
2163
+ }
2164
+ return cardDisplayElementFactory;
2165
+ };
1980
2166
  const syncFactories = {
1981
2167
  cardNumber: createCardNumberElement,
1982
2168
  cvv: createCVVElement,
@@ -1985,7 +2171,8 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
1985
2171
  };
1986
2172
  const lazyFactories = {
1987
2173
  card: lazyCardElement,
1988
- copyButton: lazyCopyButtonElement
2174
+ copyButton: lazyCopyButtonElement,
2175
+ cardDisplay: lazyCardDisplayElement
1989
2176
  };
1990
2177
  const ensureCoordinatorInitialized = () => {
1991
2178
  if (coordinatorReadyPromise) {
@@ -2027,7 +2214,10 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
2027
2214
  url: coordinatorIframe.src
2028
2215
  });
2029
2216
  await new Promise((resolve, reject) => {
2030
- const timeout = setTimeout(() => {
2217
+ rejectCoordinatorLoad = reject;
2218
+ coordinatorLoadTimeout = setTimeout(() => {
2219
+ coordinatorLoadTimeout = null;
2220
+ rejectCoordinatorLoad = null;
2031
2221
  reject(
2032
2222
  new Error(
2033
2223
  `Coordinator load timeout (${config.timeoutMs}ms). Try increasing timeoutMs for slow connections.`
@@ -2037,13 +2227,21 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
2037
2227
  coordinatorIframe.addEventListener(
2038
2228
  "load",
2039
2229
  () => {
2040
- clearTimeout(timeout);
2230
+ if (coordinatorLoadTimeout !== null) {
2231
+ clearTimeout(coordinatorLoadTimeout);
2232
+ }
2233
+ coordinatorLoadTimeout = null;
2234
+ rejectCoordinatorLoad = null;
2041
2235
  log2("Coordinator iframe loaded");
2042
2236
  resolve();
2043
2237
  },
2044
2238
  { once: true }
2045
2239
  );
2046
2240
  });
2241
+ if (destroyed) {
2242
+ log2("Coordinator load resolved after destroy — aborting init");
2243
+ return;
2244
+ }
2047
2245
  coordinatorClient = createPostMessageClient(coordinatorIframe, {
2048
2246
  targetOrigin: new URL(config.elementsBaseUrl).origin,
2049
2247
  timeout: 3e4,
@@ -2061,6 +2259,9 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
2061
2259
  theme: config.theme,
2062
2260
  darkTheme: config.darkTheme,
2063
2261
  themeMode: config.themeMode
2262
+ // sessionAuthorizationUrl is intentionally NOT sent to the coordinator:
2263
+ // the merchant-authorization fetch now runs in the parent page (see
2264
+ // create-card-display.ts), so the coordinator never needs the URL.
2064
2265
  });
2065
2266
  log2("Coordinator initialized successfully");
2066
2267
  };
@@ -2070,7 +2271,7 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
2070
2271
  * createElement is now SYNCHRONOUS - returns Element instantly
2071
2272
  * Coordinator initialization starts on first element (but doesn't block)
2072
2273
  */
2073
- createElement: (type, elementOptions) => {
2274
+ createElement: guardSync((type, elementOptions) => {
2074
2275
  const syncFactory = syncFactories[type];
2075
2276
  const lazyFactory = lazyFactories[type];
2076
2277
  if (!syncFactory && !lazyFactory) {
@@ -2094,79 +2295,21 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
2094
2295
  elementOptions,
2095
2296
  localElementId,
2096
2297
  ensureCoordinatorInitialized,
2097
- notifyCoordinatorElementUnmount
2298
+ notifyCoordinatorElementUnmount,
2299
+ sendCoordinatorRequest
2098
2300
  );
2099
2301
  } else {
2100
- let realElement = null;
2101
- let loadPromise = null;
2102
- const loadRealElement = async () => {
2103
- if (!loadPromise) {
2104
- loadPromise = (async () => {
2105
- const factory = await lazyFactory();
2106
- realElement = factory(
2107
- apiKey,
2108
- config,
2109
- elementOptions,
2110
- localElementId,
2111
- ensureCoordinatorInitialized,
2112
- notifyCoordinatorElementUnmount
2113
- );
2114
- })();
2115
- }
2116
- await loadPromise;
2117
- };
2118
- element = {
2119
- id: localElementId,
2302
+ element = createLazyElementWrapper(
2303
+ lazyFactory,
2120
2304
  type,
2121
- get mounted() {
2122
- return (realElement == null ? void 0 : realElement.mounted) ?? false;
2123
- },
2124
- get fields() {
2125
- return realElement == null ? void 0 : realElement.fields;
2126
- },
2127
- mount: async (selector) => {
2128
- await loadRealElement();
2129
- return realElement.mount(selector);
2130
- },
2131
- unmount: () => {
2132
- if (!realElement) throw ElementError("Element not initialized");
2133
- return realElement.unmount();
2134
- },
2135
- update: async (opts) => {
2136
- if (!realElement) throw ElementError("Element not initialized");
2137
- return realElement.update(opts);
2138
- },
2139
- focus: () => {
2140
- if (!realElement) throw ElementError("Element not initialized");
2141
- return realElement.focus();
2142
- },
2143
- blur: () => {
2144
- if (!realElement) throw ElementError("Element not initialized");
2145
- return realElement.blur();
2146
- },
2147
- clear: () => {
2148
- if (!realElement) throw ElementError("Element not initialized");
2149
- return realElement.clear();
2150
- },
2151
- on: (eventType, listener) => {
2152
- if (!realElement) {
2153
- loadRealElement().then(() => realElement.on(eventType, listener));
2154
- return () => {
2155
- };
2156
- }
2157
- return realElement.on(eventType, listener);
2158
- },
2159
- // Forward setValueRef for CopyButtonElement
2160
- setValueRef: async (refElement) => {
2161
- if (!realElement) {
2162
- await loadRealElement();
2163
- }
2164
- if (realElement && "setValueRef" in realElement && typeof realElement.setValueRef === "function") {
2165
- return realElement.setValueRef(refElement);
2166
- }
2167
- throw ElementError("setValueRef not available on this element type");
2168
- }
2169
- };
2305
+ localElementId,
2306
+ apiKey,
2307
+ config,
2308
+ elementOptions,
2309
+ ensureCoordinatorInitialized,
2310
+ notifyCoordinatorElementUnmount,
2311
+ sendCoordinatorRequest
2312
+ );
2170
2313
  }
2171
2314
  elements.set(element.id, element);
2172
2315
  if (type === "cardNumber") {
@@ -2205,8 +2348,8 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
2205
2348
  });
2206
2349
  }
2207
2350
  return element;
2208
- },
2209
- tokenize: async (payload, options2) => {
2351
+ }),
2352
+ tokenize: guardAsync(async (payload, options2) => {
2210
2353
  log2("tokenize called", {
2211
2354
  hasPayload: !!payload,
2212
2355
  hasOptions: !!options2,
@@ -2243,7 +2386,7 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
2243
2386
  });
2244
2387
  throw error;
2245
2388
  }
2246
- },
2389
+ }),
2247
2390
  /**
2248
2391
  * Token CRUD operations
2249
2392
  * Supports Element references - automatically resolves to secure values
@@ -2253,7 +2396,7 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
2253
2396
  * Create a new token
2254
2397
  * Supports Element references in data (same as tokenize)
2255
2398
  */
2256
- create: async (request) => {
2399
+ create: guardAsync(async (request) => {
2257
2400
  if (!request || typeof request !== "object" || Array.isArray(request)) {
2258
2401
  throw ValidationError("Token data must be a non-null object");
2259
2402
  }
@@ -2288,7 +2431,7 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
2288
2431
  });
2289
2432
  throw error;
2290
2433
  }
2291
- },
2434
+ }),
2292
2435
  /**
2293
2436
  * Retrieve a token by ID
2294
2437
  *
@@ -2296,7 +2439,7 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
2296
2439
  * @param options - Optional configuration
2297
2440
  * @param options.apiKey - Optional session key for authorized access (from sessions.create + authorize)
2298
2441
  */
2299
- retrieve: async (id, options2) => {
2442
+ retrieve: guardAsync(async (id, options2) => {
2300
2443
  log2("tokens.retrieve called", {
2301
2444
  tokenId: id,
2302
2445
  hasSessionKey: !!(options2 == null ? void 0 : options2.apiKey)
@@ -2326,12 +2469,12 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
2326
2469
  });
2327
2470
  throw error;
2328
2471
  }
2329
- },
2472
+ }),
2330
2473
  /**
2331
2474
  * Update a token
2332
2475
  * Supports Element references in data
2333
2476
  */
2334
- update: async (id, request) => {
2477
+ update: guardAsync(async (id, request) => {
2335
2478
  log2("tokens.update called", { tokenId: id });
2336
2479
  if (!coordinatorClient) {
2337
2480
  await initializeCoordinator();
@@ -2353,7 +2496,7 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
2353
2496
  });
2354
2497
  throw error;
2355
2498
  }
2356
- },
2499
+ }),
2357
2500
  /**
2358
2501
  * Encrypt token data with client-side JWE
2359
2502
  * Uses ECDH-ES key agreement and A256GCM authenticated encryption
@@ -2364,7 +2507,7 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
2364
2507
  * @param keyId - Key identifier for decryption lookup
2365
2508
  * @returns Encrypted JWE token(s)
2366
2509
  */
2367
- encrypt: async (tokenData, publicKeyPEM, keyId) => {
2510
+ encrypt: guardAsync(async (tokenData, publicKeyPEM, keyId) => {
2368
2511
  log2("tokens.encrypt called", {
2369
2512
  isSingleToken: "type" in tokenData && "data" in tokenData,
2370
2513
  keyId
@@ -2405,14 +2548,14 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
2405
2548
  });
2406
2549
  throw error;
2407
2550
  }
2408
- }
2551
+ })
2409
2552
  },
2410
2553
  sessions: {
2411
2554
  /**
2412
2555
  * Create a new session for temporary elevated access
2413
2556
  * Note: Session must be authorized by a Private Application on your backend
2414
2557
  */
2415
- create: async () => {
2558
+ create: guardAsync(async () => {
2416
2559
  log2("sessions.create called");
2417
2560
  if (!coordinatorClient) {
2418
2561
  await initializeCoordinator();
@@ -2436,13 +2579,13 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
2436
2579
  });
2437
2580
  throw error;
2438
2581
  }
2439
- }
2582
+ })
2440
2583
  },
2441
2584
  tokenIntents: {
2442
2585
  /**
2443
2586
  * Create a token intent with element reference support
2444
2587
  */
2445
- create: async (data, options2) => {
2588
+ create: guardAsync(async (data, options2) => {
2446
2589
  log2("tokenIntents.create called", {
2447
2590
  type: data.type,
2448
2591
  hasData: !!data.data,
@@ -2475,11 +2618,11 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
2475
2618
  });
2476
2619
  throw error;
2477
2620
  }
2478
- },
2621
+ }),
2479
2622
  /**
2480
2623
  * Get a token intent by ID
2481
2624
  */
2482
- get: async (id, options2) => {
2625
+ get: guardAsync(async (id, options2) => {
2483
2626
  log2("tokenIntents.get called", {
2484
2627
  intentId: id
2485
2628
  });
@@ -2507,10 +2650,10 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
2507
2650
  });
2508
2651
  throw error;
2509
2652
  }
2510
- }
2653
+ })
2511
2654
  },
2512
2655
  proxy: (() => {
2513
- const invokeProxy = async (method, options2 = {}) => {
2656
+ const invokeProxy = guardAsync(async (method, options2 = {}) => {
2514
2657
  validateProxyOptions({ ...options2, method });
2515
2658
  log2("proxy invoked", {
2516
2659
  method,
@@ -2547,7 +2690,7 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
2547
2690
  });
2548
2691
  throw error;
2549
2692
  }
2550
- };
2693
+ });
2551
2694
  const proxyVerb = (method) => (options2) => invokeProxy(method, options2 ?? {});
2552
2695
  return {
2553
2696
  get: proxyVerb("GET"),
@@ -2557,7 +2700,7 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
2557
2700
  delete: proxyVerb("DELETE")
2558
2701
  };
2559
2702
  })(),
2560
- updateThemeMode: async (mode) => {
2703
+ updateThemeMode: guardAsync(async (mode) => {
2561
2704
  log2("updateThemeMode called", {
2562
2705
  oldMode: currentThemeMode,
2563
2706
  newMode: mode,
@@ -2596,6 +2739,79 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
2596
2739
  mode,
2597
2740
  updatedCount: Array.from(elements.values()).filter((el) => el.mounted).length
2598
2741
  });
2742
+ }),
2743
+ /**
2744
+ * Destroys this instance and removes its elements from the page. Once
2745
+ * destroyed, its other methods throw; create a new instance with
2746
+ * BasisTheory() to recover. Safe to call more than once.
2747
+ */
2748
+ destroy: async () => {
2749
+ if (destroyed) {
2750
+ return;
2751
+ }
2752
+ destroyed = true;
2753
+ log2("Destroying SDK instance", {
2754
+ elementCount: elements.size,
2755
+ hasCoordinator: coordinatorIframe !== null
2756
+ });
2757
+ for (const [id, element] of elements) {
2758
+ if (!element.mounted) {
2759
+ continue;
2760
+ }
2761
+ try {
2762
+ element.unmount();
2763
+ } catch (error) {
2764
+ log2("Failed to unmount element during destroy", {
2765
+ elementId: id,
2766
+ error: error.message
2767
+ });
2768
+ }
2769
+ }
2770
+ elements.clear();
2771
+ if (coordinatorLoadTimeout !== null) {
2772
+ clearTimeout(coordinatorLoadTimeout);
2773
+ coordinatorLoadTimeout = null;
2774
+ }
2775
+ if (rejectCoordinatorLoad) {
2776
+ rejectCoordinatorLoad(
2777
+ ConfigurationError(
2778
+ "SDK instance has been destroyed. Create a new instance with BasisTheory()"
2779
+ )
2780
+ );
2781
+ rejectCoordinatorLoad = null;
2782
+ }
2783
+ if (coordinatorClient) {
2784
+ const CACHE_CLEAR_TIMEOUT_MS = 2e3;
2785
+ const cacheCleared = coordinatorClient.sendRequest("CMD_CLEAR_ALL_CACHE", {}, { retries: 0 }).catch((error) => {
2786
+ log2("Failed to clear coordinator cache during destroy", {
2787
+ error: error.message
2788
+ });
2789
+ });
2790
+ let capTimeout;
2791
+ const cap = new Promise((resolve) => {
2792
+ capTimeout = setTimeout(() => {
2793
+ log2("Coordinator cache-clear timed out during destroy", {
2794
+ timeoutMs: CACHE_CLEAR_TIMEOUT_MS
2795
+ });
2796
+ resolve();
2797
+ }, CACHE_CLEAR_TIMEOUT_MS);
2798
+ });
2799
+ await Promise.race([cacheCleared, cap]);
2800
+ if (capTimeout !== void 0) {
2801
+ clearTimeout(capTimeout);
2802
+ }
2803
+ }
2804
+ if (coordinatorClient) {
2805
+ coordinatorClient.destroy();
2806
+ coordinatorClient = null;
2807
+ }
2808
+ if (coordinatorIframe) {
2809
+ coordinatorIframe.remove();
2810
+ coordinatorIframe = null;
2811
+ }
2812
+ coordinatorReadyPromise = null;
2813
+ coordinatorInitStarted = false;
2814
+ log2("SDK instance destroyed");
2599
2815
  }
2600
2816
  };
2601
2817
  if (queuedCommands && queuedCommands.length > 0) {
@@ -2650,8 +2866,8 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
2650
2866
  if (typeof window !== "undefined" && typeof document !== "undefined" && document.currentScript && !window.BasisTheory) {
2651
2867
  window.BasisTheory = BasisTheory;
2652
2868
  }
2653
- const version = "3.0.0-beta.4";
2654
- const buildDate = "2026-06-09T15:35:24.276Z";
2869
+ const version = "3.0.0-beta.5";
2870
+ const buildDate = "2026-07-16T14:24:00.230Z";
2655
2871
  if (typeof window !== "undefined") {
2656
2872
  window.__BasisTheorySDK__ = BasisTheory;
2657
2873
  }
@@ -2664,6 +2880,7 @@ export {
2664
2880
  createIframe as c,
2665
2881
  createMounter as d,
2666
2882
  buildDate as e,
2883
+ generateId as g,
2667
2884
  log as l,
2668
2885
  version as v
2669
2886
  };