@basis-theory/web-elements 3.0.0-beta.3 → 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.
- package/dist/.vite/manifest.json +30 -8
- package/dist/basis-theory-sdk.es.js +1 -1
- package/dist/basis-theory-sdk.umd.js +1 -1
- package/dist/basis-theory-sdk.umd.js.map +1 -1
- package/dist/{create-card-DipDWgph.js → create-card-CB_YcP_4.js} +58 -20
- package/dist/create-card-CB_YcP_4.js.map +1 -0
- package/dist/create-card-display-kBupMJwe.js +280 -0
- package/dist/create-card-display-kBupMJwe.js.map +1 -0
- package/dist/create-copy-button-2XtKTEo3.js +236 -0
- package/dist/create-copy-button-2XtKTEo3.js.map +1 -0
- package/dist/{index-BBfuUaLw.js → index-Dyy7tm5Z.js} +709 -140
- package/dist/index-Dyy7tm5Z.js.map +1 -0
- package/dist/index.d.ts +1119 -0
- package/dist/loader/basis-theory.min.js +1 -1
- package/dist/sri.json +9 -9
- package/dist/stats.html +1 -1
- package/package.json +12 -12
- package/dist/api-client/src/api-client.d.ts +0 -11
- package/dist/api-client/src/api-client.d.ts.map +0 -1
- package/dist/api-client/src/http-client.d.ts +0 -24
- package/dist/api-client/src/http-client.d.ts.map +0 -1
- package/dist/api-client/src/index.d.ts +0 -9
- package/dist/api-client/src/index.d.ts.map +0 -1
- package/dist/api-client/src/services/sessions.d.ts +0 -19
- package/dist/api-client/src/services/sessions.d.ts.map +0 -1
- package/dist/api-client/src/services/token-intents.d.ts +0 -17
- package/dist/api-client/src/services/token-intents.d.ts.map +0 -1
- package/dist/api-client/src/services/tokens.d.ts +0 -19
- package/dist/api-client/src/services/tokens.d.ts.map +0 -1
- package/dist/api-client/src/types.d.ts +0 -313
- package/dist/api-client/src/types.d.ts.map +0 -1
- package/dist/create-card-DipDWgph.js.map +0 -1
- package/dist/index-BBfuUaLw.js.map +0 -1
- package/dist/postmessage/src/client.d.ts +0 -7
- package/dist/postmessage/src/client.d.ts.map +0 -1
- package/dist/postmessage/src/event-map.d.ts +0 -135
- package/dist/postmessage/src/event-map.d.ts.map +0 -1
- package/dist/postmessage/src/index.d.ts +0 -9
- package/dist/postmessage/src/index.d.ts.map +0 -1
- package/dist/postmessage/src/server.d.ts +0 -11
- package/dist/postmessage/src/server.d.ts.map +0 -1
- package/dist/postmessage/src/types.d.ts +0 -71
- package/dist/postmessage/src/types.d.ts.map +0 -1
- package/dist/shared/src/api-url-resolver.d.ts +0 -22
- package/dist/shared/src/api-url-resolver.d.ts.map +0 -1
- package/dist/shared/src/debug-logger.d.ts +0 -16
- package/dist/shared/src/debug-logger.d.ts.map +0 -1
- package/dist/shared/src/deep-sanitization.d.ts +0 -24
- package/dist/shared/src/deep-sanitization.d.ts.map +0 -1
- package/dist/shared/src/errors.d.ts +0 -93
- package/dist/shared/src/errors.d.ts.map +0 -1
- package/dist/shared/src/generate-id.d.ts +0 -13
- package/dist/shared/src/generate-id.d.ts.map +0 -1
- package/dist/shared/src/index.d.ts +0 -13
- package/dist/shared/src/index.d.ts.map +0 -1
- package/dist/shared/src/logger-context.d.ts +0 -25
- package/dist/shared/src/logger-context.d.ts.map +0 -1
- package/dist/web-elements/src/BasisTheory.d.ts +0 -47
- package/dist/web-elements/src/BasisTheory.d.ts.map +0 -1
- package/dist/web-elements/src/basis-theory.d.ts +0 -62
- package/dist/web-elements/src/basis-theory.d.ts.map +0 -1
- package/dist/web-elements/src/elements/create-card-number.d.ts +0 -14
- package/dist/web-elements/src/elements/create-card-number.d.ts.map +0 -1
- package/dist/web-elements/src/elements/create-card.d.ts +0 -37
- package/dist/web-elements/src/elements/create-card.d.ts.map +0 -1
- package/dist/web-elements/src/elements/create-cvv.d.ts +0 -10
- package/dist/web-elements/src/elements/create-cvv.d.ts.map +0 -1
- package/dist/web-elements/src/elements/create-expiry.d.ts +0 -10
- package/dist/web-elements/src/elements/create-expiry.d.ts.map +0 -1
- package/dist/web-elements/src/elements/create-text.d.ts +0 -13
- package/dist/web-elements/src/elements/create-text.d.ts.map +0 -1
- package/dist/web-elements/src/index.d.ts +0 -27
- package/dist/web-elements/src/index.d.ts.map +0 -1
- package/dist/web-elements/src/mounter/create-mounter.d.ts +0 -11
- package/dist/web-elements/src/mounter/create-mounter.d.ts.map +0 -1
- package/dist/web-elements/src/theme/default-theme.d.ts +0 -22
- package/dist/web-elements/src/theme/default-theme.d.ts.map +0 -1
- package/dist/web-elements/src/theme/inject-theme-dark.d.ts +0 -13
- package/dist/web-elements/src/theme/inject-theme-dark.d.ts.map +0 -1
- package/dist/web-elements/src/theme/inject-theme.d.ts +0 -20
- package/dist/web-elements/src/theme/inject-theme.d.ts.map +0 -1
- package/dist/web-elements/src/types/events.d.ts +0 -137
- package/dist/web-elements/src/types/events.d.ts.map +0 -1
- package/dist/web-elements/src/types/theme.d.ts +0 -91
- package/dist/web-elements/src/types/theme.d.ts.map +0 -1
- package/dist/web-elements/src/types.d.ts +0 -537
- package/dist/web-elements/src/types.d.ts.map +0 -1
- package/dist/web-elements/src/utils/create-event-dispatcher.d.ts +0 -47
- package/dist/web-elements/src/utils/create-event-dispatcher.d.ts.map +0 -1
- package/dist/web-elements/src/utils/create-iframe.d.ts +0 -10
- package/dist/web-elements/src/utils/create-iframe.d.ts.map +0 -1
- package/dist/web-elements/src/utils/resolve-element-references.d.ts +0 -20
- package/dist/web-elements/src/utils/resolve-element-references.d.ts.map +0 -1
- package/dist/web-elements/src/utils/validate-element-options.d.ts +0 -11
- package/dist/web-elements/src/utils/validate-element-options.d.ts.map +0 -1
- package/dist/web-elements/src/utils/validate-sdk-config.d.ts +0 -15
- package/dist/web-elements/src/utils/validate-sdk-config.d.ts.map +0 -1
- package/dist/web-elements/src/utils/warn-unknown-keys.d.ts +0 -15
- package/dist/web-elements/src/utils/warn-unknown-keys.d.ts.map +0 -1
- package/dist/web-elements/src/version.d.ts +0 -25
- 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";
|
|
@@ -162,7 +174,7 @@ const BasisTheoryError = (code, message, details) => {
|
|
|
162
174
|
return error;
|
|
163
175
|
};
|
|
164
176
|
const ValidationError = (message, field, details) => {
|
|
165
|
-
const error = createBasisTheoryError("VALIDATION_ERROR", message, details);
|
|
177
|
+
const error = createBasisTheoryError("VALIDATION_ERROR", message, field ? { field, ...details } : details);
|
|
166
178
|
error.name = "ValidationError";
|
|
167
179
|
error.field = field;
|
|
168
180
|
return error;
|
|
@@ -183,7 +195,201 @@ const ConfigurationError = (message, details) => {
|
|
|
183
195
|
error.name = "ConfigurationError";
|
|
184
196
|
return error;
|
|
185
197
|
};
|
|
186
|
-
const
|
|
198
|
+
const ALLOWED_PROXY_SCHEMES = /* @__PURE__ */ new Set(["https:"]);
|
|
199
|
+
const ALLOWED_METHODS = /* @__PURE__ */ new Set([
|
|
200
|
+
"GET",
|
|
201
|
+
"POST",
|
|
202
|
+
"PUT",
|
|
203
|
+
"PATCH",
|
|
204
|
+
"DELETE"
|
|
205
|
+
]);
|
|
206
|
+
const FORBIDDEN_PROXY_HEADERS = /* @__PURE__ */ new Set([
|
|
207
|
+
// Request smuggling prevention
|
|
208
|
+
"host",
|
|
209
|
+
"content-length",
|
|
210
|
+
"transfer-encoding",
|
|
211
|
+
"te",
|
|
212
|
+
"trailer",
|
|
213
|
+
// Connection manipulation prevention
|
|
214
|
+
"connection",
|
|
215
|
+
"upgrade",
|
|
216
|
+
"keep-alive",
|
|
217
|
+
"proxy-connection",
|
|
218
|
+
// Proxy bypass prevention
|
|
219
|
+
"via",
|
|
220
|
+
"proxy-authorization",
|
|
221
|
+
// BasisTheory credential protection
|
|
222
|
+
"bt-api-key",
|
|
223
|
+
"bt-proxy-key",
|
|
224
|
+
"bt-proxy-url",
|
|
225
|
+
"bt-idempotency-key",
|
|
226
|
+
// Forwarded headers
|
|
227
|
+
"x-forwarded-for",
|
|
228
|
+
"x-forwarded-host",
|
|
229
|
+
"x-real-ip",
|
|
230
|
+
// SDK owns serialization format — body is always JSON.stringify'd
|
|
231
|
+
"content-type"
|
|
232
|
+
]);
|
|
233
|
+
const validateProxyHeaders = (headers) => {
|
|
234
|
+
if (!headers)
|
|
235
|
+
return;
|
|
236
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
237
|
+
if (/[\r\n]/.test(value)) {
|
|
238
|
+
throw ValidationError(`Header '${key}' value contains illegal CRLF`, "headers");
|
|
239
|
+
}
|
|
240
|
+
if (/[\r\n]/.test(key)) {
|
|
241
|
+
throw ValidationError(`Header name '${key}' contains illegal CRLF`, "headers");
|
|
242
|
+
}
|
|
243
|
+
if (FORBIDDEN_PROXY_HEADERS.has(key.toLowerCase().trim())) {
|
|
244
|
+
throw ValidationError(`Header '${key}' is not allowed in proxy requests`, "headers");
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
const parseIPv4 = (hostname) => {
|
|
249
|
+
const clean = hostname.replace(/^\[|\]$/g, "");
|
|
250
|
+
if (/^(0x[\da-f]+|\d+)$/i.test(clean)) {
|
|
251
|
+
const num = clean.toLowerCase().startsWith("0x") ? parseInt(clean, 16) : parseInt(clean, 10);
|
|
252
|
+
if (num >= 0 && num <= 4294967295) {
|
|
253
|
+
return [
|
|
254
|
+
num >>> 24 & 255,
|
|
255
|
+
num >>> 16 & 255,
|
|
256
|
+
num >>> 8 & 255,
|
|
257
|
+
num & 255
|
|
258
|
+
];
|
|
259
|
+
}
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
const parts = clean.split(".");
|
|
263
|
+
if (parts.length !== 4) {
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
const octets = [];
|
|
267
|
+
for (const part of parts) {
|
|
268
|
+
let value;
|
|
269
|
+
if (part.toLowerCase().startsWith("0x")) {
|
|
270
|
+
value = parseInt(part, 16);
|
|
271
|
+
} else if (part.startsWith("0") && part.length > 1 && !/[89]/.test(part)) {
|
|
272
|
+
value = parseInt(part, 8);
|
|
273
|
+
} else {
|
|
274
|
+
value = parseInt(part, 10);
|
|
275
|
+
}
|
|
276
|
+
if (isNaN(value) || value < 0 || value > 255) {
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
octets.push(value);
|
|
280
|
+
}
|
|
281
|
+
return octets;
|
|
282
|
+
};
|
|
283
|
+
const isPrivateIPv4 = (octets) => {
|
|
284
|
+
const a = octets[0];
|
|
285
|
+
const b = octets[1];
|
|
286
|
+
if (a === void 0 || b === void 0)
|
|
287
|
+
return true;
|
|
288
|
+
if (a === 127)
|
|
289
|
+
return true;
|
|
290
|
+
if (a === 10)
|
|
291
|
+
return true;
|
|
292
|
+
if (a === 172 && b >= 16 && b <= 31)
|
|
293
|
+
return true;
|
|
294
|
+
if (a === 192 && b === 168)
|
|
295
|
+
return true;
|
|
296
|
+
if (a === 169 && b === 254)
|
|
297
|
+
return true;
|
|
298
|
+
if (a === 0)
|
|
299
|
+
return true;
|
|
300
|
+
return false;
|
|
301
|
+
};
|
|
302
|
+
const isBlockedIPv6 = (hostname) => {
|
|
303
|
+
const clean = hostname.replace(/^\[|\]$/g, "").toLowerCase();
|
|
304
|
+
if (clean === "::1" || clean === "0:0:0:0:0:0:0:1" || clean === "0000:0000:0000:0000:0000:0000:0000:0001") {
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
307
|
+
if (clean.startsWith("fe8") || clean.startsWith("fe9") || clean.startsWith("fea") || clean.startsWith("feb")) {
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
if (clean.startsWith("fc") || clean.startsWith("fd")) {
|
|
311
|
+
return true;
|
|
312
|
+
}
|
|
313
|
+
const ipv4MappedDotted = clean.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/);
|
|
314
|
+
if (ipv4MappedDotted) {
|
|
315
|
+
const ipv4Part = ipv4MappedDotted[1];
|
|
316
|
+
if (ipv4Part) {
|
|
317
|
+
const ipv4Octets = parseIPv4(ipv4Part);
|
|
318
|
+
if (ipv4Octets && isPrivateIPv4(ipv4Octets)) {
|
|
319
|
+
return true;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const ipv4MappedHex = clean.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/);
|
|
324
|
+
if (ipv4MappedHex) {
|
|
325
|
+
const highPart = ipv4MappedHex[1];
|
|
326
|
+
const lowPart = ipv4MappedHex[2];
|
|
327
|
+
if (!highPart || !lowPart) {
|
|
328
|
+
return true;
|
|
329
|
+
}
|
|
330
|
+
const high = parseInt(highPart, 16);
|
|
331
|
+
const low = parseInt(lowPart, 16);
|
|
332
|
+
const octets = [
|
|
333
|
+
high >>> 8 & 255,
|
|
334
|
+
high & 255,
|
|
335
|
+
low >>> 8 & 255,
|
|
336
|
+
low & 255
|
|
337
|
+
];
|
|
338
|
+
if (isPrivateIPv4(octets)) {
|
|
339
|
+
return true;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return false;
|
|
343
|
+
};
|
|
344
|
+
const validateSsrfProtection = (parsed) => {
|
|
345
|
+
const hostname = parsed.hostname.toLowerCase();
|
|
346
|
+
if (parsed.username || parsed.password) {
|
|
347
|
+
throw ValidationError("URL must not contain userinfo (username/password)", "url");
|
|
348
|
+
}
|
|
349
|
+
if (hostname === "localhost") {
|
|
350
|
+
throw ValidationError("URL must not target localhost", "url");
|
|
351
|
+
}
|
|
352
|
+
if (hostname.startsWith("[") || isBlockedIPv6(hostname)) {
|
|
353
|
+
if (isBlockedIPv6(hostname)) {
|
|
354
|
+
throw ValidationError("URL must not target loopback or link-local IPv6 addresses", "url");
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
const ipv4Octets = parseIPv4(hostname);
|
|
358
|
+
if (ipv4Octets && isPrivateIPv4(ipv4Octets)) {
|
|
359
|
+
throw ValidationError("URL must not target private or internal IP addresses", "url");
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
const validateProxyUrl = (url) => {
|
|
363
|
+
let parsed;
|
|
364
|
+
try {
|
|
365
|
+
parsed = new URL(url);
|
|
366
|
+
} catch {
|
|
367
|
+
throw ValidationError("Invalid URL format", "url");
|
|
368
|
+
}
|
|
369
|
+
if (!ALLOWED_PROXY_SCHEMES.has(parsed.protocol)) {
|
|
370
|
+
throw ValidationError(`URL scheme '${parsed.protocol}' is not allowed. Only https: is permitted`, "url");
|
|
371
|
+
}
|
|
372
|
+
validateSsrfProtection(parsed);
|
|
373
|
+
};
|
|
374
|
+
const validateProxyTarget = (url, proxyKey) => {
|
|
375
|
+
const hasUrl = typeof url === "string" && url.length > 0;
|
|
376
|
+
const hasKey = typeof proxyKey === "string" && proxyKey.length > 0;
|
|
377
|
+
if (hasUrl === hasKey) {
|
|
378
|
+
throw ValidationError("Specify exactly one of url or proxyKey", "url/proxyKey");
|
|
379
|
+
}
|
|
380
|
+
if (hasUrl) {
|
|
381
|
+
validateProxyUrl(url);
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
const validateProxyOptions = (options) => {
|
|
385
|
+
if (options !== void 0 && (typeof options !== "object" || options === null)) {
|
|
386
|
+
throw ValidationError("Proxy options must be a plain object", "options");
|
|
387
|
+
}
|
|
388
|
+
if ((options == null ? void 0 : options.method) !== void 0 && !ALLOWED_METHODS.has(options.method)) {
|
|
389
|
+
throw ValidationError(`Method '${options.method}' is not allowed`, "method");
|
|
390
|
+
}
|
|
391
|
+
validateProxyTarget(options == null ? void 0 : options.url, options == null ? void 0 : options.proxyKey);
|
|
392
|
+
validateProxyHeaders(options == null ? void 0 : options.headers);
|
|
187
393
|
};
|
|
188
394
|
const DEFAULT_RETRY_CONFIG = {
|
|
189
395
|
maxRetries: 2,
|
|
@@ -267,9 +473,10 @@ const createPostMessageClient = (iframe, config) => {
|
|
|
267
473
|
try {
|
|
268
474
|
callback(payload);
|
|
269
475
|
} catch (err) {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
476
|
+
log2("Error in event subscriber", {
|
|
477
|
+
type,
|
|
478
|
+
error: err.message
|
|
479
|
+
});
|
|
273
480
|
}
|
|
274
481
|
});
|
|
275
482
|
} else if (!nonce) {
|
|
@@ -283,7 +490,7 @@ const createPostMessageClient = (iframe, config) => {
|
|
|
283
490
|
const retryConfig = typeof config.retries === "number" ? { ...DEFAULT_RETRY_CONFIG, maxRetries: config.retries } : typeof config.retries === "object" ? config.retries : DEFAULT_RETRY_CONFIG;
|
|
284
491
|
const sendRequestOnce = (type, payload) => {
|
|
285
492
|
return new Promise((resolve, reject) => {
|
|
286
|
-
const nonce =
|
|
493
|
+
const nonce = generateId();
|
|
287
494
|
const timestamp = Date.now();
|
|
288
495
|
if (!iframe.contentWindow) {
|
|
289
496
|
reject(BasisTheoryError("IFRAME_NOT_READY", "Iframe not ready"));
|
|
@@ -298,9 +505,10 @@ const createPostMessageClient = (iframe, config) => {
|
|
|
298
505
|
iframe.contentWindow.postMessage({ type, nonce, timestamp, payload }, config.targetOrigin);
|
|
299
506
|
});
|
|
300
507
|
};
|
|
301
|
-
const sendRequest = async (type, payload) => {
|
|
508
|
+
const sendRequest = async (type, payload, options) => {
|
|
509
|
+
const maxRetries = (options == null ? void 0 : options.retries) !== void 0 ? options.retries : retryConfig.maxRetries;
|
|
302
510
|
let lastError = null;
|
|
303
|
-
for (let attempt = 0; attempt <=
|
|
511
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
304
512
|
try {
|
|
305
513
|
return await sendRequestOnce(type, payload);
|
|
306
514
|
} catch (error) {
|
|
@@ -316,11 +524,11 @@ const createPostMessageClient = (iframe, config) => {
|
|
|
316
524
|
});
|
|
317
525
|
throw error;
|
|
318
526
|
}
|
|
319
|
-
if (attempt <
|
|
527
|
+
if (attempt < maxRetries) {
|
|
320
528
|
const delay = getRetryDelay(attempt, retryConfig);
|
|
321
529
|
log2("Retrying request", {
|
|
322
530
|
attempt: attempt + 1,
|
|
323
|
-
maxRetries
|
|
531
|
+
maxRetries,
|
|
324
532
|
delayMs: delay,
|
|
325
533
|
error: errorMessage
|
|
326
534
|
});
|
|
@@ -328,7 +536,7 @@ const createPostMessageClient = (iframe, config) => {
|
|
|
328
536
|
}
|
|
329
537
|
}
|
|
330
538
|
}
|
|
331
|
-
throw lastError || new Error(`Request failed after ${
|
|
539
|
+
throw lastError || new Error(`Request failed after ${maxRetries} retries: ${type}`);
|
|
332
540
|
};
|
|
333
541
|
const ping = async (timeout = 1e3) => {
|
|
334
542
|
const start = Date.now();
|
|
@@ -405,7 +613,7 @@ const createIframe = (config) => {
|
|
|
405
613
|
iframe.src = config.src;
|
|
406
614
|
iframe.setAttribute("sandbox", config.sandbox);
|
|
407
615
|
iframe.title = config.title;
|
|
408
|
-
iframe.setAttribute("allow", "payment *");
|
|
616
|
+
iframe.setAttribute("allow", config.allow ?? "payment *");
|
|
409
617
|
iframe.setAttribute("importance", "high");
|
|
410
618
|
iframe.style.opacity = "0";
|
|
411
619
|
return iframe;
|
|
@@ -444,7 +652,9 @@ const createEventDispatcher = (config) => {
|
|
|
444
652
|
bin: payload.bin,
|
|
445
653
|
cvvLengths: payload.cvvLengths,
|
|
446
654
|
potentialBrands: payload.potentialBrands,
|
|
447
|
-
matchStrength: payload.matchStrength
|
|
655
|
+
matchStrength: payload.matchStrength,
|
|
656
|
+
networks: payload.networks,
|
|
657
|
+
selectedNetwork: payload.selectedNetwork
|
|
448
658
|
};
|
|
449
659
|
dispatch("change", cardDetail);
|
|
450
660
|
} else if (elementType === "card") {
|
|
@@ -453,27 +663,47 @@ const createEventDispatcher = (config) => {
|
|
|
453
663
|
cardBrand: payload.cardBrand,
|
|
454
664
|
last4: payload.last4,
|
|
455
665
|
bin: payload.bin,
|
|
666
|
+
networks: payload.networks,
|
|
667
|
+
selectedNetwork: payload.selectedNetwork,
|
|
456
668
|
errors: payload.errors || null
|
|
457
669
|
});
|
|
458
670
|
} else {
|
|
459
671
|
dispatch("change", changeDetail);
|
|
460
672
|
}
|
|
461
673
|
});
|
|
674
|
+
client.subscribe(
|
|
675
|
+
"EVENT_NETWORK_CHANGED",
|
|
676
|
+
(payload) => {
|
|
677
|
+
if (elementType !== "cardNumber" && elementType !== "card") {
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
const detail = {
|
|
681
|
+
network: payload.network,
|
|
682
|
+
previousNetwork: payload.previousNetwork,
|
|
683
|
+
availableNetworks: payload.availableNetworks ?? [],
|
|
684
|
+
selectionMode: payload.selectionMode ?? "auto"
|
|
685
|
+
};
|
|
686
|
+
dispatch("networkChanged", detail);
|
|
687
|
+
}
|
|
688
|
+
);
|
|
462
689
|
client.subscribe("EVENT_FOCUS", () => {
|
|
463
690
|
dispatch("focus", {});
|
|
464
691
|
});
|
|
465
692
|
client.subscribe("EVENT_BLUR", () => {
|
|
466
693
|
dispatch("blur", {});
|
|
467
694
|
});
|
|
468
|
-
client.subscribe(
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
695
|
+
client.subscribe(
|
|
696
|
+
"EVENT_ERROR",
|
|
697
|
+
(payload) => {
|
|
698
|
+
dispatch("error", {
|
|
699
|
+
code: payload.code,
|
|
700
|
+
message: payload.message,
|
|
701
|
+
status: payload.status,
|
|
702
|
+
validationErrors: payload.validationErrors,
|
|
703
|
+
metadata: payload.metadata || {}
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
);
|
|
477
707
|
};
|
|
478
708
|
const dispatchError = (code, message, error) => {
|
|
479
709
|
dispatch("error", {
|
|
@@ -490,7 +720,7 @@ const createEventDispatcher = (config) => {
|
|
|
490
720
|
dispatchError
|
|
491
721
|
};
|
|
492
722
|
};
|
|
493
|
-
const createCardNumberElement = (apiKey, sdkConfig, options = {}, coordinatorElementId, ensureCoordinatorReady) => {
|
|
723
|
+
const createCardNumberElement = (apiKey, sdkConfig, options = {}, coordinatorElementId, ensureCoordinatorReady, notifyCoordinatorElementUnmount) => {
|
|
494
724
|
let iframe = null;
|
|
495
725
|
let client = null;
|
|
496
726
|
let mounter = null;
|
|
@@ -511,6 +741,12 @@ const createCardNumberElement = (apiKey, sdkConfig, options = {}, coordinatorEle
|
|
|
511
741
|
eventTarget
|
|
512
742
|
});
|
|
513
743
|
eventDispatcher.setupSubscriptions();
|
|
744
|
+
client.subscribe("EVENT_CONTENT_HEIGHT", (payload) => {
|
|
745
|
+
const h = payload == null ? void 0 : payload.heightPx;
|
|
746
|
+
if (iframe && typeof h === "number" && h > 0) {
|
|
747
|
+
iframe.style.height = `${Math.ceil(h)}px`;
|
|
748
|
+
}
|
|
749
|
+
});
|
|
514
750
|
};
|
|
515
751
|
return {
|
|
516
752
|
id,
|
|
@@ -518,6 +754,9 @@ const createCardNumberElement = (apiKey, sdkConfig, options = {}, coordinatorEle
|
|
|
518
754
|
get mounted() {
|
|
519
755
|
return isMounted;
|
|
520
756
|
},
|
|
757
|
+
get loaded() {
|
|
758
|
+
return isMounted;
|
|
759
|
+
},
|
|
521
760
|
mount: async (selector) => {
|
|
522
761
|
if (isMounted) {
|
|
523
762
|
throw ElementError("Element is already mounted");
|
|
@@ -587,7 +826,9 @@ const createCardNumberElement = (apiKey, sdkConfig, options = {}, coordinatorEle
|
|
|
587
826
|
placeholder: options.placeholder,
|
|
588
827
|
disabled: options.disabled,
|
|
589
828
|
readOnly: options.readOnly,
|
|
590
|
-
debug: sdkConfig.debug
|
|
829
|
+
debug: sdkConfig.debug,
|
|
830
|
+
binLookup: options.binLookup,
|
|
831
|
+
coBadge: options.coBadge
|
|
591
832
|
});
|
|
592
833
|
if (iframe) iframe.style.opacity = "1";
|
|
593
834
|
} catch (error) {
|
|
@@ -617,16 +858,17 @@ const createCardNumberElement = (apiKey, sdkConfig, options = {}, coordinatorEle
|
|
|
617
858
|
if (!isMounted) {
|
|
618
859
|
throw ElementError("Element is not mounted");
|
|
619
860
|
}
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
error: error.message
|
|
624
|
-
});
|
|
861
|
+
notifyCoordinatorElementUnmount(id).catch((error) => {
|
|
862
|
+
log("Failed to notify coordinator of element unmount", {
|
|
863
|
+
error: error.message
|
|
625
864
|
});
|
|
865
|
+
});
|
|
866
|
+
if (client) {
|
|
626
867
|
client.destroy();
|
|
627
868
|
client = null;
|
|
628
869
|
}
|
|
629
870
|
if (mounter && iframe) {
|
|
871
|
+
iframe.style.height = "";
|
|
630
872
|
mounter.unmount(iframe);
|
|
631
873
|
iframe = null;
|
|
632
874
|
mounter = null;
|
|
@@ -640,10 +882,23 @@ const createCardNumberElement = (apiKey, sdkConfig, options = {}, coordinatorEle
|
|
|
640
882
|
throw ElementError("Element must be mounted before updating");
|
|
641
883
|
}
|
|
642
884
|
const updatePayload = {
|
|
643
|
-
|
|
644
|
-
disabled: newOptions.disabled,
|
|
645
|
-
readOnly: newOptions.readOnly
|
|
885
|
+
elementId: id
|
|
646
886
|
};
|
|
887
|
+
if (newOptions.placeholder !== void 0) {
|
|
888
|
+
updatePayload.placeholder = newOptions.placeholder;
|
|
889
|
+
}
|
|
890
|
+
if (newOptions.disabled !== void 0) {
|
|
891
|
+
updatePayload.disabled = newOptions.disabled;
|
|
892
|
+
}
|
|
893
|
+
if (newOptions.readOnly !== void 0) {
|
|
894
|
+
updatePayload.readOnly = newOptions.readOnly;
|
|
895
|
+
}
|
|
896
|
+
if (newOptions.binLookup !== void 0) {
|
|
897
|
+
updatePayload.binLookup = newOptions.binLookup;
|
|
898
|
+
}
|
|
899
|
+
if (newOptions.coBadge !== void 0) {
|
|
900
|
+
updatePayload.coBadge = newOptions.coBadge;
|
|
901
|
+
}
|
|
647
902
|
if (newOptions.themeMode !== void 0) {
|
|
648
903
|
updatePayload.themeMode = newOptions.themeMode;
|
|
649
904
|
updatePayload.theme = sdkConfig.theme;
|
|
@@ -669,6 +924,12 @@ const createCardNumberElement = (apiKey, sdkConfig, options = {}, coordinatorEle
|
|
|
669
924
|
}
|
|
670
925
|
client.sendRequest("CMD_CLEAR", {});
|
|
671
926
|
},
|
|
927
|
+
setNetwork: async (network) => {
|
|
928
|
+
if (!isMounted || !client) {
|
|
929
|
+
throw ElementError("Element must be mounted before setNetwork");
|
|
930
|
+
}
|
|
931
|
+
await client.sendRequest("CMD_SET_NETWORK", { network });
|
|
932
|
+
},
|
|
672
933
|
on: (eventType, listener) => {
|
|
673
934
|
eventTarget.addEventListener(
|
|
674
935
|
eventType,
|
|
@@ -681,7 +942,7 @@ const createCardNumberElement = (apiKey, sdkConfig, options = {}, coordinatorEle
|
|
|
681
942
|
}
|
|
682
943
|
};
|
|
683
944
|
};
|
|
684
|
-
const createCVVElement = (apiKey, sdkConfig, options = {}, coordinatorElementId, ensureCoordinatorReady) => {
|
|
945
|
+
const createCVVElement = (apiKey, sdkConfig, options = {}, coordinatorElementId, ensureCoordinatorReady, notifyCoordinatorElementUnmount) => {
|
|
685
946
|
let iframe = null;
|
|
686
947
|
let client = null;
|
|
687
948
|
let mounter = null;
|
|
@@ -707,6 +968,9 @@ const createCVVElement = (apiKey, sdkConfig, options = {}, coordinatorElementId,
|
|
|
707
968
|
get mounted() {
|
|
708
969
|
return isMounted;
|
|
709
970
|
},
|
|
971
|
+
get loaded() {
|
|
972
|
+
return isMounted;
|
|
973
|
+
},
|
|
710
974
|
mount: async (selector) => {
|
|
711
975
|
if (isMounted) {
|
|
712
976
|
throw ElementError("Element is already mounted", id);
|
|
@@ -804,12 +1068,12 @@ const createCVVElement = (apiKey, sdkConfig, options = {}, coordinatorElementId,
|
|
|
804
1068
|
if (!isMounted) {
|
|
805
1069
|
throw ElementError("Element is not mounted");
|
|
806
1070
|
}
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
error: error.message
|
|
811
|
-
});
|
|
1071
|
+
notifyCoordinatorElementUnmount(id).catch((error) => {
|
|
1072
|
+
log("Failed to notify coordinator of element unmount", {
|
|
1073
|
+
error: error.message
|
|
812
1074
|
});
|
|
1075
|
+
});
|
|
1076
|
+
if (client) {
|
|
813
1077
|
client.destroy();
|
|
814
1078
|
client = null;
|
|
815
1079
|
}
|
|
@@ -871,7 +1135,7 @@ const createCVVElement = (apiKey, sdkConfig, options = {}, coordinatorElementId,
|
|
|
871
1135
|
}
|
|
872
1136
|
};
|
|
873
1137
|
};
|
|
874
|
-
const createExpiryElement = (apiKey, sdkConfig, options = {}, coordinatorElementId, ensureCoordinatorReady) => {
|
|
1138
|
+
const createExpiryElement = (apiKey, sdkConfig, options = {}, coordinatorElementId, ensureCoordinatorReady, notifyCoordinatorElementUnmount) => {
|
|
875
1139
|
let iframe = null;
|
|
876
1140
|
let client = null;
|
|
877
1141
|
let mounter = null;
|
|
@@ -897,6 +1161,9 @@ const createExpiryElement = (apiKey, sdkConfig, options = {}, coordinatorElement
|
|
|
897
1161
|
get mounted() {
|
|
898
1162
|
return isMounted;
|
|
899
1163
|
},
|
|
1164
|
+
get loaded() {
|
|
1165
|
+
return isMounted;
|
|
1166
|
+
},
|
|
900
1167
|
mount: async (selector) => {
|
|
901
1168
|
if (isMounted) {
|
|
902
1169
|
throw ElementError("Element is already mounted", id);
|
|
@@ -993,12 +1260,12 @@ const createExpiryElement = (apiKey, sdkConfig, options = {}, coordinatorElement
|
|
|
993
1260
|
if (!isMounted) {
|
|
994
1261
|
throw ElementError("Element is not mounted");
|
|
995
1262
|
}
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
error: error.message
|
|
1000
|
-
});
|
|
1263
|
+
notifyCoordinatorElementUnmount(id).catch((error) => {
|
|
1264
|
+
log("Failed to notify coordinator of element unmount", {
|
|
1265
|
+
error: error.message
|
|
1001
1266
|
});
|
|
1267
|
+
});
|
|
1268
|
+
if (client) {
|
|
1002
1269
|
client.destroy();
|
|
1003
1270
|
client = null;
|
|
1004
1271
|
}
|
|
@@ -1057,7 +1324,7 @@ const createExpiryElement = (apiKey, sdkConfig, options = {}, coordinatorElement
|
|
|
1057
1324
|
}
|
|
1058
1325
|
};
|
|
1059
1326
|
};
|
|
1060
|
-
const createTextElement = (apiKey, sdkConfig, options = {}, coordinatorElementId, ensureCoordinatorReady) => {
|
|
1327
|
+
const createTextElement = (apiKey, sdkConfig, options = {}, coordinatorElementId, ensureCoordinatorReady, notifyCoordinatorElementUnmount) => {
|
|
1061
1328
|
let iframe = null;
|
|
1062
1329
|
let client = null;
|
|
1063
1330
|
let mounter = null;
|
|
@@ -1090,6 +1357,9 @@ const createTextElement = (apiKey, sdkConfig, options = {}, coordinatorElementId
|
|
|
1090
1357
|
get mounted() {
|
|
1091
1358
|
return isMounted;
|
|
1092
1359
|
},
|
|
1360
|
+
get loaded() {
|
|
1361
|
+
return isMounted;
|
|
1362
|
+
},
|
|
1093
1363
|
mount: async (selector) => {
|
|
1094
1364
|
if (isMounted) {
|
|
1095
1365
|
throw ElementError("Element is already mounted", id);
|
|
@@ -1207,12 +1477,12 @@ const createTextElement = (apiKey, sdkConfig, options = {}, coordinatorElementId
|
|
|
1207
1477
|
if (!isMounted) {
|
|
1208
1478
|
throw ElementError("Element is not mounted");
|
|
1209
1479
|
}
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
error: error.message
|
|
1214
|
-
});
|
|
1480
|
+
notifyCoordinatorElementUnmount(id).catch((error) => {
|
|
1481
|
+
log("Failed to notify coordinator of element unmount", {
|
|
1482
|
+
error: error.message
|
|
1215
1483
|
});
|
|
1484
|
+
});
|
|
1485
|
+
if (client) {
|
|
1216
1486
|
client.destroy();
|
|
1217
1487
|
client = null;
|
|
1218
1488
|
}
|
|
@@ -1465,6 +1735,7 @@ const warnUnknownKeys = (label, obj, knownKeys, deprecatedKeys) => {
|
|
|
1465
1735
|
};
|
|
1466
1736
|
const KNOWN_SDK_KEYS = /* @__PURE__ */ new Set([
|
|
1467
1737
|
"elementsBaseUrl",
|
|
1738
|
+
"whitelabelDomain",
|
|
1468
1739
|
"apiBaseUrl",
|
|
1469
1740
|
"debug",
|
|
1470
1741
|
"measurePerformance",
|
|
@@ -1472,7 +1743,8 @@ const KNOWN_SDK_KEYS = /* @__PURE__ */ new Set([
|
|
|
1472
1743
|
"retryConfig",
|
|
1473
1744
|
"themeMode",
|
|
1474
1745
|
"theme",
|
|
1475
|
-
"darkTheme"
|
|
1746
|
+
"darkTheme",
|
|
1747
|
+
"sessionAuthorizationUrl"
|
|
1476
1748
|
]);
|
|
1477
1749
|
const DEPRECATED_SDK_KEYS = /* @__PURE__ */ new Map([
|
|
1478
1750
|
["iframeOrigin", 'Use "elementsBaseUrl" instead.']
|
|
@@ -1540,7 +1812,44 @@ const validateSDKConfig = (options = {}) => {
|
|
|
1540
1812
|
if (!["light", "dark", "auto"].includes(themeMode)) {
|
|
1541
1813
|
throw ConfigurationError('themeMode must be "light", "dark", or "auto"');
|
|
1542
1814
|
}
|
|
1543
|
-
|
|
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";
|
|
1544
1853
|
const iframeOrigin = new URL(finalBaseUrl).origin;
|
|
1545
1854
|
return {
|
|
1546
1855
|
iframeOrigin,
|
|
@@ -1556,7 +1865,9 @@ const validateSDKConfig = (options = {}) => {
|
|
|
1556
1865
|
// Light theme or only theme
|
|
1557
1866
|
darkTheme: options.darkTheme ? mergeWithDefaultTheme(options.darkTheme) : void 0,
|
|
1558
1867
|
// Optional dark theme
|
|
1559
|
-
retryConfig
|
|
1868
|
+
retryConfig,
|
|
1869
|
+
sessionAuthorizationUrl: options.sessionAuthorizationUrl
|
|
1870
|
+
// Optional - required for cardDisplay
|
|
1560
1871
|
};
|
|
1561
1872
|
};
|
|
1562
1873
|
const COMMON_ELEMENT_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -1566,7 +1877,7 @@ const COMMON_ELEMENT_KEYS = /* @__PURE__ */ new Set([
|
|
|
1566
1877
|
"readOnly"
|
|
1567
1878
|
]);
|
|
1568
1879
|
const ELEMENT_TYPE_KEYS = {
|
|
1569
|
-
cardNumber: /* @__PURE__ */ new Set([...COMMON_ELEMENT_KEYS]),
|
|
1880
|
+
cardNumber: /* @__PURE__ */ new Set([...COMMON_ELEMENT_KEYS, "binLookup", "coBadge"]),
|
|
1570
1881
|
cvv: /* @__PURE__ */ new Set([...COMMON_ELEMENT_KEYS, "cardBrand", "cvvLengths"]),
|
|
1571
1882
|
// Internal coordination props
|
|
1572
1883
|
expiry: /* @__PURE__ */ new Set([...COMMON_ELEMENT_KEYS]),
|
|
@@ -1585,7 +1896,21 @@ const ELEMENT_TYPE_KEYS = {
|
|
|
1585
1896
|
"layout",
|
|
1586
1897
|
"stackAt",
|
|
1587
1898
|
"iconPosition",
|
|
1588
|
-
"cardBrands"
|
|
1899
|
+
"cardBrands",
|
|
1900
|
+
"binLookup",
|
|
1901
|
+
"coBadge"
|
|
1902
|
+
]),
|
|
1903
|
+
copyButton: /* @__PURE__ */ new Set([
|
|
1904
|
+
"allowClipboard",
|
|
1905
|
+
"value",
|
|
1906
|
+
"text",
|
|
1907
|
+
"disabled"
|
|
1908
|
+
]),
|
|
1909
|
+
cardDisplay: /* @__PURE__ */ new Set([
|
|
1910
|
+
"tokenId",
|
|
1911
|
+
"number",
|
|
1912
|
+
"cvc",
|
|
1913
|
+
"expiration"
|
|
1589
1914
|
])
|
|
1590
1915
|
};
|
|
1591
1916
|
const validateElementOptions = (type, options = {}) => {
|
|
@@ -1636,6 +1961,14 @@ const validateElementOptions = (type, options = {}) => {
|
|
|
1636
1961
|
}
|
|
1637
1962
|
}
|
|
1638
1963
|
}
|
|
1964
|
+
if (type === "copyButton") {
|
|
1965
|
+
const copyOpts = options;
|
|
1966
|
+
if (copyOpts.allowClipboard !== true) {
|
|
1967
|
+
throw ConfigurationError(
|
|
1968
|
+
"CopyButton requires allowClipboard: true. Clipboard access for raw card data may not be PCI compliant and must be explicitly enabled."
|
|
1969
|
+
);
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1639
1972
|
if (type === "card") {
|
|
1640
1973
|
const cardOptions = options;
|
|
1641
1974
|
if (cardOptions.layout !== void 0) {
|
|
@@ -1678,15 +2011,158 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
|
|
|
1678
2011
|
let coordinatorClient = null;
|
|
1679
2012
|
let coordinatorReadyPromise = null;
|
|
1680
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
|
+
};
|
|
1681
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
|
+
};
|
|
2117
|
+
const notifyCoordinatorElementUnmount = async (elementId) => {
|
|
2118
|
+
if (!coordinatorClient) {
|
|
2119
|
+
log2("Cannot notify coordinator of unmount - coordinator not initialized", {
|
|
2120
|
+
elementId
|
|
2121
|
+
});
|
|
2122
|
+
return;
|
|
2123
|
+
}
|
|
2124
|
+
try {
|
|
2125
|
+
await coordinatorClient.sendRequest("CMD_BROADCAST_ELEMENT_UNMOUNT", {
|
|
2126
|
+
elementId
|
|
2127
|
+
});
|
|
2128
|
+
log2("Notified coordinator of element unmount", { elementId });
|
|
2129
|
+
} catch (error) {
|
|
2130
|
+
log2("Failed to notify coordinator of element unmount", {
|
|
2131
|
+
elementId,
|
|
2132
|
+
error: error.message
|
|
2133
|
+
});
|
|
2134
|
+
}
|
|
2135
|
+
};
|
|
2136
|
+
const sendCoordinatorRequest = async (command, payload) => {
|
|
2137
|
+
if (!coordinatorClient) {
|
|
2138
|
+
throw CoordinatorError("Coordinator not initialized");
|
|
2139
|
+
}
|
|
2140
|
+
return coordinatorClient.sendRequest(command, payload);
|
|
2141
|
+
};
|
|
1682
2142
|
let cardElementFactory = null;
|
|
2143
|
+
let copyButtonElementFactory = null;
|
|
2144
|
+
let cardDisplayElementFactory = null;
|
|
1683
2145
|
const lazyCardElement = async () => {
|
|
1684
2146
|
if (!cardElementFactory) {
|
|
1685
|
-
const module = await import("./create-card-
|
|
2147
|
+
const module = await import("./create-card-CB_YcP_4.js");
|
|
1686
2148
|
cardElementFactory = module.createCardElement;
|
|
1687
2149
|
}
|
|
1688
2150
|
return cardElementFactory;
|
|
1689
2151
|
};
|
|
2152
|
+
const lazyCopyButtonElement = async () => {
|
|
2153
|
+
if (!copyButtonElementFactory) {
|
|
2154
|
+
const module = await import("./create-copy-button-2XtKTEo3.js");
|
|
2155
|
+
copyButtonElementFactory = module.createCopyButtonElement;
|
|
2156
|
+
}
|
|
2157
|
+
return copyButtonElementFactory;
|
|
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
|
+
};
|
|
1690
2166
|
const syncFactories = {
|
|
1691
2167
|
cardNumber: createCardNumberElement,
|
|
1692
2168
|
cvv: createCVVElement,
|
|
@@ -1694,7 +2170,9 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
|
|
|
1694
2170
|
text: createTextElement
|
|
1695
2171
|
};
|
|
1696
2172
|
const lazyFactories = {
|
|
1697
|
-
card: lazyCardElement
|
|
2173
|
+
card: lazyCardElement,
|
|
2174
|
+
copyButton: lazyCopyButtonElement,
|
|
2175
|
+
cardDisplay: lazyCardDisplayElement
|
|
1698
2176
|
};
|
|
1699
2177
|
const ensureCoordinatorInitialized = () => {
|
|
1700
2178
|
if (coordinatorReadyPromise) {
|
|
@@ -1736,7 +2214,10 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
|
|
|
1736
2214
|
url: coordinatorIframe.src
|
|
1737
2215
|
});
|
|
1738
2216
|
await new Promise((resolve, reject) => {
|
|
1739
|
-
|
|
2217
|
+
rejectCoordinatorLoad = reject;
|
|
2218
|
+
coordinatorLoadTimeout = setTimeout(() => {
|
|
2219
|
+
coordinatorLoadTimeout = null;
|
|
2220
|
+
rejectCoordinatorLoad = null;
|
|
1740
2221
|
reject(
|
|
1741
2222
|
new Error(
|
|
1742
2223
|
`Coordinator load timeout (${config.timeoutMs}ms). Try increasing timeoutMs for slow connections.`
|
|
@@ -1746,13 +2227,21 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
|
|
|
1746
2227
|
coordinatorIframe.addEventListener(
|
|
1747
2228
|
"load",
|
|
1748
2229
|
() => {
|
|
1749
|
-
|
|
2230
|
+
if (coordinatorLoadTimeout !== null) {
|
|
2231
|
+
clearTimeout(coordinatorLoadTimeout);
|
|
2232
|
+
}
|
|
2233
|
+
coordinatorLoadTimeout = null;
|
|
2234
|
+
rejectCoordinatorLoad = null;
|
|
1750
2235
|
log2("Coordinator iframe loaded");
|
|
1751
2236
|
resolve();
|
|
1752
2237
|
},
|
|
1753
2238
|
{ once: true }
|
|
1754
2239
|
);
|
|
1755
2240
|
});
|
|
2241
|
+
if (destroyed) {
|
|
2242
|
+
log2("Coordinator load resolved after destroy — aborting init");
|
|
2243
|
+
return;
|
|
2244
|
+
}
|
|
1756
2245
|
coordinatorClient = createPostMessageClient(coordinatorIframe, {
|
|
1757
2246
|
targetOrigin: new URL(config.elementsBaseUrl).origin,
|
|
1758
2247
|
timeout: 3e4,
|
|
@@ -1770,6 +2259,9 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
|
|
|
1770
2259
|
theme: config.theme,
|
|
1771
2260
|
darkTheme: config.darkTheme,
|
|
1772
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.
|
|
1773
2265
|
});
|
|
1774
2266
|
log2("Coordinator initialized successfully");
|
|
1775
2267
|
};
|
|
@@ -1779,7 +2271,7 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
|
|
|
1779
2271
|
* createElement is now SYNCHRONOUS - returns Element instantly
|
|
1780
2272
|
* Coordinator initialization starts on first element (but doesn't block)
|
|
1781
2273
|
*/
|
|
1782
|
-
createElement: (type, elementOptions) => {
|
|
2274
|
+
createElement: guardSync((type, elementOptions) => {
|
|
1783
2275
|
const syncFactory = syncFactories[type];
|
|
1784
2276
|
const lazyFactory = lazyFactories[type];
|
|
1785
2277
|
if (!syncFactory && !lazyFactory) {
|
|
@@ -1802,68 +2294,22 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
|
|
|
1802
2294
|
config,
|
|
1803
2295
|
elementOptions,
|
|
1804
2296
|
localElementId,
|
|
1805
|
-
ensureCoordinatorInitialized
|
|
2297
|
+
ensureCoordinatorInitialized,
|
|
2298
|
+
notifyCoordinatorElementUnmount,
|
|
2299
|
+
sendCoordinatorRequest
|
|
1806
2300
|
);
|
|
1807
2301
|
} else {
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
const loadRealElement = async () => {
|
|
1811
|
-
if (!loadPromise) {
|
|
1812
|
-
loadPromise = (async () => {
|
|
1813
|
-
const factory = await lazyFactory();
|
|
1814
|
-
realElement = factory(
|
|
1815
|
-
apiKey,
|
|
1816
|
-
config,
|
|
1817
|
-
elementOptions,
|
|
1818
|
-
localElementId,
|
|
1819
|
-
ensureCoordinatorInitialized
|
|
1820
|
-
);
|
|
1821
|
-
})();
|
|
1822
|
-
}
|
|
1823
|
-
await loadPromise;
|
|
1824
|
-
};
|
|
1825
|
-
element = {
|
|
1826
|
-
id: localElementId,
|
|
2302
|
+
element = createLazyElementWrapper(
|
|
2303
|
+
lazyFactory,
|
|
1827
2304
|
type,
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
return realElement.mount(selector);
|
|
1837
|
-
},
|
|
1838
|
-
unmount: () => {
|
|
1839
|
-
if (!realElement) throw ElementError("Element not initialized");
|
|
1840
|
-
return realElement.unmount();
|
|
1841
|
-
},
|
|
1842
|
-
update: async (opts) => {
|
|
1843
|
-
if (!realElement) throw ElementError("Element not initialized");
|
|
1844
|
-
return realElement.update(opts);
|
|
1845
|
-
},
|
|
1846
|
-
focus: () => {
|
|
1847
|
-
if (!realElement) throw ElementError("Element not initialized");
|
|
1848
|
-
return realElement.focus();
|
|
1849
|
-
},
|
|
1850
|
-
blur: () => {
|
|
1851
|
-
if (!realElement) throw ElementError("Element not initialized");
|
|
1852
|
-
return realElement.blur();
|
|
1853
|
-
},
|
|
1854
|
-
clear: () => {
|
|
1855
|
-
if (!realElement) throw ElementError("Element not initialized");
|
|
1856
|
-
return realElement.clear();
|
|
1857
|
-
},
|
|
1858
|
-
on: (eventType, listener) => {
|
|
1859
|
-
if (!realElement) {
|
|
1860
|
-
loadRealElement().then(() => realElement.on(eventType, listener));
|
|
1861
|
-
return () => {
|
|
1862
|
-
};
|
|
1863
|
-
}
|
|
1864
|
-
return realElement.on(eventType, listener);
|
|
1865
|
-
}
|
|
1866
|
-
};
|
|
2305
|
+
localElementId,
|
|
2306
|
+
apiKey,
|
|
2307
|
+
config,
|
|
2308
|
+
elementOptions,
|
|
2309
|
+
ensureCoordinatorInitialized,
|
|
2310
|
+
notifyCoordinatorElementUnmount,
|
|
2311
|
+
sendCoordinatorRequest
|
|
2312
|
+
);
|
|
1867
2313
|
}
|
|
1868
2314
|
elements.set(element.id, element);
|
|
1869
2315
|
if (type === "cardNumber") {
|
|
@@ -1902,8 +2348,8 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
|
|
|
1902
2348
|
});
|
|
1903
2349
|
}
|
|
1904
2350
|
return element;
|
|
1905
|
-
},
|
|
1906
|
-
tokenize: async (payload, options2) => {
|
|
2351
|
+
}),
|
|
2352
|
+
tokenize: guardAsync(async (payload, options2) => {
|
|
1907
2353
|
log2("tokenize called", {
|
|
1908
2354
|
hasPayload: !!payload,
|
|
1909
2355
|
hasOptions: !!options2,
|
|
@@ -1940,7 +2386,7 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
|
|
|
1940
2386
|
});
|
|
1941
2387
|
throw error;
|
|
1942
2388
|
}
|
|
1943
|
-
},
|
|
2389
|
+
}),
|
|
1944
2390
|
/**
|
|
1945
2391
|
* Token CRUD operations
|
|
1946
2392
|
* Supports Element references - automatically resolves to secure values
|
|
@@ -1950,7 +2396,7 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
|
|
|
1950
2396
|
* Create a new token
|
|
1951
2397
|
* Supports Element references in data (same as tokenize)
|
|
1952
2398
|
*/
|
|
1953
|
-
create: async (request) => {
|
|
2399
|
+
create: guardAsync(async (request) => {
|
|
1954
2400
|
if (!request || typeof request !== "object" || Array.isArray(request)) {
|
|
1955
2401
|
throw ValidationError("Token data must be a non-null object");
|
|
1956
2402
|
}
|
|
@@ -1985,7 +2431,7 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
|
|
|
1985
2431
|
});
|
|
1986
2432
|
throw error;
|
|
1987
2433
|
}
|
|
1988
|
-
},
|
|
2434
|
+
}),
|
|
1989
2435
|
/**
|
|
1990
2436
|
* Retrieve a token by ID
|
|
1991
2437
|
*
|
|
@@ -1993,7 +2439,7 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
|
|
|
1993
2439
|
* @param options - Optional configuration
|
|
1994
2440
|
* @param options.apiKey - Optional session key for authorized access (from sessions.create + authorize)
|
|
1995
2441
|
*/
|
|
1996
|
-
retrieve: async (id, options2) => {
|
|
2442
|
+
retrieve: guardAsync(async (id, options2) => {
|
|
1997
2443
|
log2("tokens.retrieve called", {
|
|
1998
2444
|
tokenId: id,
|
|
1999
2445
|
hasSessionKey: !!(options2 == null ? void 0 : options2.apiKey)
|
|
@@ -2023,12 +2469,12 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
|
|
|
2023
2469
|
});
|
|
2024
2470
|
throw error;
|
|
2025
2471
|
}
|
|
2026
|
-
},
|
|
2472
|
+
}),
|
|
2027
2473
|
/**
|
|
2028
2474
|
* Update a token
|
|
2029
2475
|
* Supports Element references in data
|
|
2030
2476
|
*/
|
|
2031
|
-
update: async (id, request) => {
|
|
2477
|
+
update: guardAsync(async (id, request) => {
|
|
2032
2478
|
log2("tokens.update called", { tokenId: id });
|
|
2033
2479
|
if (!coordinatorClient) {
|
|
2034
2480
|
await initializeCoordinator();
|
|
@@ -2050,7 +2496,7 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
|
|
|
2050
2496
|
});
|
|
2051
2497
|
throw error;
|
|
2052
2498
|
}
|
|
2053
|
-
},
|
|
2499
|
+
}),
|
|
2054
2500
|
/**
|
|
2055
2501
|
* Encrypt token data with client-side JWE
|
|
2056
2502
|
* Uses ECDH-ES key agreement and A256GCM authenticated encryption
|
|
@@ -2061,7 +2507,7 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
|
|
|
2061
2507
|
* @param keyId - Key identifier for decryption lookup
|
|
2062
2508
|
* @returns Encrypted JWE token(s)
|
|
2063
2509
|
*/
|
|
2064
|
-
encrypt: async (tokenData, publicKeyPEM, keyId) => {
|
|
2510
|
+
encrypt: guardAsync(async (tokenData, publicKeyPEM, keyId) => {
|
|
2065
2511
|
log2("tokens.encrypt called", {
|
|
2066
2512
|
isSingleToken: "type" in tokenData && "data" in tokenData,
|
|
2067
2513
|
keyId
|
|
@@ -2102,14 +2548,14 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
|
|
|
2102
2548
|
});
|
|
2103
2549
|
throw error;
|
|
2104
2550
|
}
|
|
2105
|
-
}
|
|
2551
|
+
})
|
|
2106
2552
|
},
|
|
2107
2553
|
sessions: {
|
|
2108
2554
|
/**
|
|
2109
2555
|
* Create a new session for temporary elevated access
|
|
2110
2556
|
* Note: Session must be authorized by a Private Application on your backend
|
|
2111
2557
|
*/
|
|
2112
|
-
create: async () => {
|
|
2558
|
+
create: guardAsync(async () => {
|
|
2113
2559
|
log2("sessions.create called");
|
|
2114
2560
|
if (!coordinatorClient) {
|
|
2115
2561
|
await initializeCoordinator();
|
|
@@ -2133,13 +2579,13 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
|
|
|
2133
2579
|
});
|
|
2134
2580
|
throw error;
|
|
2135
2581
|
}
|
|
2136
|
-
}
|
|
2582
|
+
})
|
|
2137
2583
|
},
|
|
2138
2584
|
tokenIntents: {
|
|
2139
2585
|
/**
|
|
2140
2586
|
* Create a token intent with element reference support
|
|
2141
2587
|
*/
|
|
2142
|
-
create: async (data, options2) => {
|
|
2588
|
+
create: guardAsync(async (data, options2) => {
|
|
2143
2589
|
log2("tokenIntents.create called", {
|
|
2144
2590
|
type: data.type,
|
|
2145
2591
|
hasData: !!data.data,
|
|
@@ -2172,11 +2618,11 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
|
|
|
2172
2618
|
});
|
|
2173
2619
|
throw error;
|
|
2174
2620
|
}
|
|
2175
|
-
},
|
|
2621
|
+
}),
|
|
2176
2622
|
/**
|
|
2177
2623
|
* Get a token intent by ID
|
|
2178
2624
|
*/
|
|
2179
|
-
get: async (id, options2) => {
|
|
2625
|
+
get: guardAsync(async (id, options2) => {
|
|
2180
2626
|
log2("tokenIntents.get called", {
|
|
2181
2627
|
intentId: id
|
|
2182
2628
|
});
|
|
@@ -2204,9 +2650,57 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
|
|
|
2204
2650
|
});
|
|
2205
2651
|
throw error;
|
|
2206
2652
|
}
|
|
2207
|
-
}
|
|
2653
|
+
})
|
|
2208
2654
|
},
|
|
2209
|
-
|
|
2655
|
+
proxy: (() => {
|
|
2656
|
+
const invokeProxy = guardAsync(async (method, options2 = {}) => {
|
|
2657
|
+
validateProxyOptions({ ...options2, method });
|
|
2658
|
+
log2("proxy invoked", {
|
|
2659
|
+
method,
|
|
2660
|
+
hasUrl: !!options2.url,
|
|
2661
|
+
hasProxyKey: !!options2.proxyKey
|
|
2662
|
+
});
|
|
2663
|
+
if (!coordinatorClient) {
|
|
2664
|
+
log2("Waiting for coordinator initialization");
|
|
2665
|
+
await initializeCoordinator();
|
|
2666
|
+
}
|
|
2667
|
+
if (!coordinatorClient) {
|
|
2668
|
+
throw CoordinatorError("Coordinator failed to initialize");
|
|
2669
|
+
}
|
|
2670
|
+
const resolvedBody = options2.body !== void 0 ? resolveElementReferences(options2.body) : void 0;
|
|
2671
|
+
try {
|
|
2672
|
+
const result = await coordinatorClient.sendRequest(
|
|
2673
|
+
"CMD_PROXY",
|
|
2674
|
+
{
|
|
2675
|
+
method,
|
|
2676
|
+
url: options2.url,
|
|
2677
|
+
proxyKey: options2.proxyKey,
|
|
2678
|
+
headers: options2.headers,
|
|
2679
|
+
body: resolvedBody,
|
|
2680
|
+
includeResponseHeaders: options2.includeResponseHeaders
|
|
2681
|
+
},
|
|
2682
|
+
{ retries: 0 }
|
|
2683
|
+
);
|
|
2684
|
+
log2("proxy successful", { method });
|
|
2685
|
+
return result;
|
|
2686
|
+
} catch (error) {
|
|
2687
|
+
log2("proxy failed", {
|
|
2688
|
+
method,
|
|
2689
|
+
error: error.message
|
|
2690
|
+
});
|
|
2691
|
+
throw error;
|
|
2692
|
+
}
|
|
2693
|
+
});
|
|
2694
|
+
const proxyVerb = (method) => (options2) => invokeProxy(method, options2 ?? {});
|
|
2695
|
+
return {
|
|
2696
|
+
get: proxyVerb("GET"),
|
|
2697
|
+
post: proxyVerb("POST"),
|
|
2698
|
+
put: proxyVerb("PUT"),
|
|
2699
|
+
patch: proxyVerb("PATCH"),
|
|
2700
|
+
delete: proxyVerb("DELETE")
|
|
2701
|
+
};
|
|
2702
|
+
})(),
|
|
2703
|
+
updateThemeMode: guardAsync(async (mode) => {
|
|
2210
2704
|
log2("updateThemeMode called", {
|
|
2211
2705
|
oldMode: currentThemeMode,
|
|
2212
2706
|
newMode: mode,
|
|
@@ -2245,6 +2739,79 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
|
|
|
2245
2739
|
mode,
|
|
2246
2740
|
updatedCount: Array.from(elements.values()).filter((el) => el.mounted).length
|
|
2247
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");
|
|
2248
2815
|
}
|
|
2249
2816
|
};
|
|
2250
2817
|
if (queuedCommands && queuedCommands.length > 0) {
|
|
@@ -2299,19 +2866,21 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
|
|
|
2299
2866
|
if (typeof window !== "undefined" && typeof document !== "undefined" && document.currentScript && !window.BasisTheory) {
|
|
2300
2867
|
window.BasisTheory = BasisTheory;
|
|
2301
2868
|
}
|
|
2302
|
-
const version = "3.0.0-beta.
|
|
2303
|
-
const buildDate = "2026-
|
|
2869
|
+
const version = "3.0.0-beta.5";
|
|
2870
|
+
const buildDate = "2026-07-16T14:24:00.230Z";
|
|
2304
2871
|
if (typeof window !== "undefined") {
|
|
2305
2872
|
window.__BasisTheorySDK__ = BasisTheory;
|
|
2306
2873
|
}
|
|
2307
2874
|
export {
|
|
2308
2875
|
BasisTheory as B,
|
|
2876
|
+
ConfigurationError as C,
|
|
2309
2877
|
ElementError as E,
|
|
2310
2878
|
createPostMessageClient as a,
|
|
2311
2879
|
createEventDispatcher as b,
|
|
2312
2880
|
createIframe as c,
|
|
2313
2881
|
createMounter as d,
|
|
2314
2882
|
buildDate as e,
|
|
2883
|
+
generateId as g,
|
|
2315
2884
|
log as l,
|
|
2316
2885
|
version as v
|
|
2317
2886
|
};
|