@basis-theory/web-elements 3.0.0-beta.3 → 3.0.0-beta.4
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 +19 -8
- package/dist/api-client/src/api-client.d.ts.map +1 -1
- package/dist/api-client/src/bin-lookup-transform.d.ts +35 -0
- package/dist/api-client/src/bin-lookup-transform.d.ts.map +1 -0
- package/dist/api-client/src/http-client.d.ts.map +1 -1
- package/dist/api-client/src/index.d.ts +1 -1
- package/dist/api-client/src/index.d.ts.map +1 -1
- package/dist/api-client/src/types.d.ts +114 -6
- package/dist/api-client/src/types.d.ts.map +1 -1
- 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-B5zEHyRg.js} +49 -15
- package/dist/create-card-B5zEHyRg.js.map +1 -0
- package/dist/create-copy-button-DrAz8g0m.js +233 -0
- package/dist/create-copy-button-DrAz8g0m.js.map +1 -0
- package/dist/{index-BBfuUaLw.js → index-BOedIhUN.js} +406 -54
- package/dist/index-BOedIhUN.js.map +1 -0
- package/dist/loader/basis-theory.min.js +1 -1
- package/dist/postmessage/src/client.d.ts.map +1 -1
- package/dist/postmessage/src/event-map.d.ts +4 -0
- package/dist/postmessage/src/event-map.d.ts.map +1 -1
- package/dist/postmessage/src/server.d.ts.map +1 -1
- package/dist/postmessage/src/types.d.ts +7 -3
- package/dist/postmessage/src/types.d.ts.map +1 -1
- package/dist/shared/src/index.d.ts +1 -0
- package/dist/shared/src/index.d.ts.map +1 -1
- package/dist/shared/src/proxy-validation.d.ts +93 -0
- package/dist/shared/src/proxy-validation.d.ts.map +1 -0
- package/dist/sri.json +9 -9
- package/dist/stats.html +1 -1
- package/dist/web-elements/src/BasisTheory.d.ts +1 -1
- package/dist/web-elements/src/BasisTheory.d.ts.map +1 -1
- package/dist/web-elements/src/basis-theory.d.ts +23 -0
- package/dist/web-elements/src/basis-theory.d.ts.map +1 -1
- package/dist/web-elements/src/elements/create-card-number.d.ts +2 -1
- package/dist/web-elements/src/elements/create-card-number.d.ts.map +1 -1
- package/dist/web-elements/src/elements/create-card.d.ts +14 -1
- package/dist/web-elements/src/elements/create-card.d.ts.map +1 -1
- package/dist/web-elements/src/elements/create-copy-button.d.ts +19 -0
- package/dist/web-elements/src/elements/create-copy-button.d.ts.map +1 -0
- package/dist/web-elements/src/elements/create-cvv.d.ts +2 -1
- package/dist/web-elements/src/elements/create-cvv.d.ts.map +1 -1
- package/dist/web-elements/src/elements/create-expiry.d.ts +2 -1
- package/dist/web-elements/src/elements/create-expiry.d.ts.map +1 -1
- package/dist/web-elements/src/elements/create-text.d.ts +2 -1
- package/dist/web-elements/src/elements/create-text.d.ts.map +1 -1
- package/dist/web-elements/src/index.d.ts +1 -1
- package/dist/web-elements/src/index.d.ts.map +1 -1
- package/dist/web-elements/src/types/events.d.ts +30 -2
- package/dist/web-elements/src/types/events.d.ts.map +1 -1
- package/dist/web-elements/src/types.d.ts +135 -5
- package/dist/web-elements/src/types.d.ts.map +1 -1
- package/dist/web-elements/src/utils/create-event-dispatcher.d.ts.map +1 -1
- package/dist/web-elements/src/utils/validate-element-options.d.ts.map +1 -1
- package/package.json +5 -5
- package/dist/create-card-DipDWgph.js.map +0 -1
- package/dist/index-BBfuUaLw.js.map +0 -1
|
@@ -162,7 +162,7 @@ const BasisTheoryError = (code, message, details) => {
|
|
|
162
162
|
return error;
|
|
163
163
|
};
|
|
164
164
|
const ValidationError = (message, field, details) => {
|
|
165
|
-
const error = createBasisTheoryError("VALIDATION_ERROR", message, details);
|
|
165
|
+
const error = createBasisTheoryError("VALIDATION_ERROR", message, field ? { field, ...details } : details);
|
|
166
166
|
error.name = "ValidationError";
|
|
167
167
|
error.field = field;
|
|
168
168
|
return error;
|
|
@@ -185,6 +185,202 @@ const ConfigurationError = (message, details) => {
|
|
|
185
185
|
};
|
|
186
186
|
const log = (action, metadata) => {
|
|
187
187
|
};
|
|
188
|
+
const ALLOWED_PROXY_SCHEMES = /* @__PURE__ */ new Set(["https:"]);
|
|
189
|
+
const ALLOWED_METHODS = /* @__PURE__ */ new Set([
|
|
190
|
+
"GET",
|
|
191
|
+
"POST",
|
|
192
|
+
"PUT",
|
|
193
|
+
"PATCH",
|
|
194
|
+
"DELETE"
|
|
195
|
+
]);
|
|
196
|
+
const FORBIDDEN_PROXY_HEADERS = /* @__PURE__ */ new Set([
|
|
197
|
+
// Request smuggling prevention
|
|
198
|
+
"host",
|
|
199
|
+
"content-length",
|
|
200
|
+
"transfer-encoding",
|
|
201
|
+
"te",
|
|
202
|
+
"trailer",
|
|
203
|
+
// Connection manipulation prevention
|
|
204
|
+
"connection",
|
|
205
|
+
"upgrade",
|
|
206
|
+
"keep-alive",
|
|
207
|
+
"proxy-connection",
|
|
208
|
+
// Proxy bypass prevention
|
|
209
|
+
"via",
|
|
210
|
+
"proxy-authorization",
|
|
211
|
+
// BasisTheory credential protection
|
|
212
|
+
"bt-api-key",
|
|
213
|
+
"bt-proxy-key",
|
|
214
|
+
"bt-proxy-url",
|
|
215
|
+
"bt-idempotency-key",
|
|
216
|
+
// Forwarded headers
|
|
217
|
+
"x-forwarded-for",
|
|
218
|
+
"x-forwarded-host",
|
|
219
|
+
"x-real-ip",
|
|
220
|
+
// SDK owns serialization format — body is always JSON.stringify'd
|
|
221
|
+
"content-type"
|
|
222
|
+
]);
|
|
223
|
+
const validateProxyHeaders = (headers) => {
|
|
224
|
+
if (!headers)
|
|
225
|
+
return;
|
|
226
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
227
|
+
if (/[\r\n]/.test(value)) {
|
|
228
|
+
throw ValidationError(`Header '${key}' value contains illegal CRLF`, "headers");
|
|
229
|
+
}
|
|
230
|
+
if (/[\r\n]/.test(key)) {
|
|
231
|
+
throw ValidationError(`Header name '${key}' contains illegal CRLF`, "headers");
|
|
232
|
+
}
|
|
233
|
+
if (FORBIDDEN_PROXY_HEADERS.has(key.toLowerCase().trim())) {
|
|
234
|
+
throw ValidationError(`Header '${key}' is not allowed in proxy requests`, "headers");
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
const parseIPv4 = (hostname) => {
|
|
239
|
+
const clean = hostname.replace(/^\[|\]$/g, "");
|
|
240
|
+
if (/^(0x[\da-f]+|\d+)$/i.test(clean)) {
|
|
241
|
+
const num = clean.toLowerCase().startsWith("0x") ? parseInt(clean, 16) : parseInt(clean, 10);
|
|
242
|
+
if (num >= 0 && num <= 4294967295) {
|
|
243
|
+
return [
|
|
244
|
+
num >>> 24 & 255,
|
|
245
|
+
num >>> 16 & 255,
|
|
246
|
+
num >>> 8 & 255,
|
|
247
|
+
num & 255
|
|
248
|
+
];
|
|
249
|
+
}
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
const parts = clean.split(".");
|
|
253
|
+
if (parts.length !== 4) {
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
const octets = [];
|
|
257
|
+
for (const part of parts) {
|
|
258
|
+
let value;
|
|
259
|
+
if (part.toLowerCase().startsWith("0x")) {
|
|
260
|
+
value = parseInt(part, 16);
|
|
261
|
+
} else if (part.startsWith("0") && part.length > 1 && !/[89]/.test(part)) {
|
|
262
|
+
value = parseInt(part, 8);
|
|
263
|
+
} else {
|
|
264
|
+
value = parseInt(part, 10);
|
|
265
|
+
}
|
|
266
|
+
if (isNaN(value) || value < 0 || value > 255) {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
octets.push(value);
|
|
270
|
+
}
|
|
271
|
+
return octets;
|
|
272
|
+
};
|
|
273
|
+
const isPrivateIPv4 = (octets) => {
|
|
274
|
+
const a = octets[0];
|
|
275
|
+
const b = octets[1];
|
|
276
|
+
if (a === void 0 || b === void 0)
|
|
277
|
+
return true;
|
|
278
|
+
if (a === 127)
|
|
279
|
+
return true;
|
|
280
|
+
if (a === 10)
|
|
281
|
+
return true;
|
|
282
|
+
if (a === 172 && b >= 16 && b <= 31)
|
|
283
|
+
return true;
|
|
284
|
+
if (a === 192 && b === 168)
|
|
285
|
+
return true;
|
|
286
|
+
if (a === 169 && b === 254)
|
|
287
|
+
return true;
|
|
288
|
+
if (a === 0)
|
|
289
|
+
return true;
|
|
290
|
+
return false;
|
|
291
|
+
};
|
|
292
|
+
const isBlockedIPv6 = (hostname) => {
|
|
293
|
+
const clean = hostname.replace(/^\[|\]$/g, "").toLowerCase();
|
|
294
|
+
if (clean === "::1" || clean === "0:0:0:0:0:0:0:1" || clean === "0000:0000:0000:0000:0000:0000:0000:0001") {
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
if (clean.startsWith("fe8") || clean.startsWith("fe9") || clean.startsWith("fea") || clean.startsWith("feb")) {
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
300
|
+
if (clean.startsWith("fc") || clean.startsWith("fd")) {
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
303
|
+
const ipv4MappedDotted = clean.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/);
|
|
304
|
+
if (ipv4MappedDotted) {
|
|
305
|
+
const ipv4Part = ipv4MappedDotted[1];
|
|
306
|
+
if (ipv4Part) {
|
|
307
|
+
const ipv4Octets = parseIPv4(ipv4Part);
|
|
308
|
+
if (ipv4Octets && isPrivateIPv4(ipv4Octets)) {
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
const ipv4MappedHex = clean.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/);
|
|
314
|
+
if (ipv4MappedHex) {
|
|
315
|
+
const highPart = ipv4MappedHex[1];
|
|
316
|
+
const lowPart = ipv4MappedHex[2];
|
|
317
|
+
if (!highPart || !lowPart) {
|
|
318
|
+
return true;
|
|
319
|
+
}
|
|
320
|
+
const high = parseInt(highPart, 16);
|
|
321
|
+
const low = parseInt(lowPart, 16);
|
|
322
|
+
const octets = [
|
|
323
|
+
high >>> 8 & 255,
|
|
324
|
+
high & 255,
|
|
325
|
+
low >>> 8 & 255,
|
|
326
|
+
low & 255
|
|
327
|
+
];
|
|
328
|
+
if (isPrivateIPv4(octets)) {
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return false;
|
|
333
|
+
};
|
|
334
|
+
const validateSsrfProtection = (parsed) => {
|
|
335
|
+
const hostname = parsed.hostname.toLowerCase();
|
|
336
|
+
if (parsed.username || parsed.password) {
|
|
337
|
+
throw ValidationError("URL must not contain userinfo (username/password)", "url");
|
|
338
|
+
}
|
|
339
|
+
if (hostname === "localhost") {
|
|
340
|
+
throw ValidationError("URL must not target localhost", "url");
|
|
341
|
+
}
|
|
342
|
+
if (hostname.startsWith("[") || isBlockedIPv6(hostname)) {
|
|
343
|
+
if (isBlockedIPv6(hostname)) {
|
|
344
|
+
throw ValidationError("URL must not target loopback or link-local IPv6 addresses", "url");
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
const ipv4Octets = parseIPv4(hostname);
|
|
348
|
+
if (ipv4Octets && isPrivateIPv4(ipv4Octets)) {
|
|
349
|
+
throw ValidationError("URL must not target private or internal IP addresses", "url");
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
const validateProxyUrl = (url) => {
|
|
353
|
+
let parsed;
|
|
354
|
+
try {
|
|
355
|
+
parsed = new URL(url);
|
|
356
|
+
} catch {
|
|
357
|
+
throw ValidationError("Invalid URL format", "url");
|
|
358
|
+
}
|
|
359
|
+
if (!ALLOWED_PROXY_SCHEMES.has(parsed.protocol)) {
|
|
360
|
+
throw ValidationError(`URL scheme '${parsed.protocol}' is not allowed. Only https: is permitted`, "url");
|
|
361
|
+
}
|
|
362
|
+
validateSsrfProtection(parsed);
|
|
363
|
+
};
|
|
364
|
+
const validateProxyTarget = (url, proxyKey) => {
|
|
365
|
+
const hasUrl = typeof url === "string" && url.length > 0;
|
|
366
|
+
const hasKey = typeof proxyKey === "string" && proxyKey.length > 0;
|
|
367
|
+
if (hasUrl === hasKey) {
|
|
368
|
+
throw ValidationError("Specify exactly one of url or proxyKey", "url/proxyKey");
|
|
369
|
+
}
|
|
370
|
+
if (hasUrl) {
|
|
371
|
+
validateProxyUrl(url);
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
const validateProxyOptions = (options) => {
|
|
375
|
+
if (options !== void 0 && (typeof options !== "object" || options === null)) {
|
|
376
|
+
throw ValidationError("Proxy options must be a plain object", "options");
|
|
377
|
+
}
|
|
378
|
+
if ((options == null ? void 0 : options.method) !== void 0 && !ALLOWED_METHODS.has(options.method)) {
|
|
379
|
+
throw ValidationError(`Method '${options.method}' is not allowed`, "method");
|
|
380
|
+
}
|
|
381
|
+
validateProxyTarget(options == null ? void 0 : options.url, options == null ? void 0 : options.proxyKey);
|
|
382
|
+
validateProxyHeaders(options == null ? void 0 : options.headers);
|
|
383
|
+
};
|
|
188
384
|
const DEFAULT_RETRY_CONFIG = {
|
|
189
385
|
maxRetries: 2,
|
|
190
386
|
initialDelay: 500,
|
|
@@ -298,9 +494,10 @@ const createPostMessageClient = (iframe, config) => {
|
|
|
298
494
|
iframe.contentWindow.postMessage({ type, nonce, timestamp, payload }, config.targetOrigin);
|
|
299
495
|
});
|
|
300
496
|
};
|
|
301
|
-
const sendRequest = async (type, payload) => {
|
|
497
|
+
const sendRequest = async (type, payload, options) => {
|
|
498
|
+
const maxRetries = (options == null ? void 0 : options.retries) !== void 0 ? options.retries : retryConfig.maxRetries;
|
|
302
499
|
let lastError = null;
|
|
303
|
-
for (let attempt = 0; attempt <=
|
|
500
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
304
501
|
try {
|
|
305
502
|
return await sendRequestOnce(type, payload);
|
|
306
503
|
} catch (error) {
|
|
@@ -316,11 +513,11 @@ const createPostMessageClient = (iframe, config) => {
|
|
|
316
513
|
});
|
|
317
514
|
throw error;
|
|
318
515
|
}
|
|
319
|
-
if (attempt <
|
|
516
|
+
if (attempt < maxRetries) {
|
|
320
517
|
const delay = getRetryDelay(attempt, retryConfig);
|
|
321
518
|
log2("Retrying request", {
|
|
322
519
|
attempt: attempt + 1,
|
|
323
|
-
maxRetries
|
|
520
|
+
maxRetries,
|
|
324
521
|
delayMs: delay,
|
|
325
522
|
error: errorMessage
|
|
326
523
|
});
|
|
@@ -328,7 +525,7 @@ const createPostMessageClient = (iframe, config) => {
|
|
|
328
525
|
}
|
|
329
526
|
}
|
|
330
527
|
}
|
|
331
|
-
throw lastError || new Error(`Request failed after ${
|
|
528
|
+
throw lastError || new Error(`Request failed after ${maxRetries} retries: ${type}`);
|
|
332
529
|
};
|
|
333
530
|
const ping = async (timeout = 1e3) => {
|
|
334
531
|
const start = Date.now();
|
|
@@ -405,7 +602,7 @@ const createIframe = (config) => {
|
|
|
405
602
|
iframe.src = config.src;
|
|
406
603
|
iframe.setAttribute("sandbox", config.sandbox);
|
|
407
604
|
iframe.title = config.title;
|
|
408
|
-
iframe.setAttribute("allow", "payment *");
|
|
605
|
+
iframe.setAttribute("allow", config.allow ?? "payment *");
|
|
409
606
|
iframe.setAttribute("importance", "high");
|
|
410
607
|
iframe.style.opacity = "0";
|
|
411
608
|
return iframe;
|
|
@@ -444,7 +641,9 @@ const createEventDispatcher = (config) => {
|
|
|
444
641
|
bin: payload.bin,
|
|
445
642
|
cvvLengths: payload.cvvLengths,
|
|
446
643
|
potentialBrands: payload.potentialBrands,
|
|
447
|
-
matchStrength: payload.matchStrength
|
|
644
|
+
matchStrength: payload.matchStrength,
|
|
645
|
+
networks: payload.networks,
|
|
646
|
+
selectedNetwork: payload.selectedNetwork
|
|
448
647
|
};
|
|
449
648
|
dispatch("change", cardDetail);
|
|
450
649
|
} else if (elementType === "card") {
|
|
@@ -453,27 +652,47 @@ const createEventDispatcher = (config) => {
|
|
|
453
652
|
cardBrand: payload.cardBrand,
|
|
454
653
|
last4: payload.last4,
|
|
455
654
|
bin: payload.bin,
|
|
655
|
+
networks: payload.networks,
|
|
656
|
+
selectedNetwork: payload.selectedNetwork,
|
|
456
657
|
errors: payload.errors || null
|
|
457
658
|
});
|
|
458
659
|
} else {
|
|
459
660
|
dispatch("change", changeDetail);
|
|
460
661
|
}
|
|
461
662
|
});
|
|
663
|
+
client.subscribe(
|
|
664
|
+
"EVENT_NETWORK_CHANGED",
|
|
665
|
+
(payload) => {
|
|
666
|
+
if (elementType !== "cardNumber" && elementType !== "card") {
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
const detail = {
|
|
670
|
+
network: payload.network,
|
|
671
|
+
previousNetwork: payload.previousNetwork,
|
|
672
|
+
availableNetworks: payload.availableNetworks ?? [],
|
|
673
|
+
selectionMode: payload.selectionMode ?? "auto"
|
|
674
|
+
};
|
|
675
|
+
dispatch("networkChanged", detail);
|
|
676
|
+
}
|
|
677
|
+
);
|
|
462
678
|
client.subscribe("EVENT_FOCUS", () => {
|
|
463
679
|
dispatch("focus", {});
|
|
464
680
|
});
|
|
465
681
|
client.subscribe("EVENT_BLUR", () => {
|
|
466
682
|
dispatch("blur", {});
|
|
467
683
|
});
|
|
468
|
-
client.subscribe(
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
684
|
+
client.subscribe(
|
|
685
|
+
"EVENT_ERROR",
|
|
686
|
+
(payload) => {
|
|
687
|
+
dispatch("error", {
|
|
688
|
+
code: payload.code,
|
|
689
|
+
message: payload.message,
|
|
690
|
+
status: payload.status,
|
|
691
|
+
validationErrors: payload.validationErrors,
|
|
692
|
+
metadata: payload.metadata || {}
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
);
|
|
477
696
|
};
|
|
478
697
|
const dispatchError = (code, message, error) => {
|
|
479
698
|
dispatch("error", {
|
|
@@ -490,7 +709,7 @@ const createEventDispatcher = (config) => {
|
|
|
490
709
|
dispatchError
|
|
491
710
|
};
|
|
492
711
|
};
|
|
493
|
-
const createCardNumberElement = (apiKey, sdkConfig, options = {}, coordinatorElementId, ensureCoordinatorReady) => {
|
|
712
|
+
const createCardNumberElement = (apiKey, sdkConfig, options = {}, coordinatorElementId, ensureCoordinatorReady, notifyCoordinatorElementUnmount) => {
|
|
494
713
|
let iframe = null;
|
|
495
714
|
let client = null;
|
|
496
715
|
let mounter = null;
|
|
@@ -511,6 +730,12 @@ const createCardNumberElement = (apiKey, sdkConfig, options = {}, coordinatorEle
|
|
|
511
730
|
eventTarget
|
|
512
731
|
});
|
|
513
732
|
eventDispatcher.setupSubscriptions();
|
|
733
|
+
client.subscribe("EVENT_CONTENT_HEIGHT", (payload) => {
|
|
734
|
+
const h = payload == null ? void 0 : payload.heightPx;
|
|
735
|
+
if (iframe && typeof h === "number" && h > 0) {
|
|
736
|
+
iframe.style.height = `${Math.ceil(h)}px`;
|
|
737
|
+
}
|
|
738
|
+
});
|
|
514
739
|
};
|
|
515
740
|
return {
|
|
516
741
|
id,
|
|
@@ -587,7 +812,9 @@ const createCardNumberElement = (apiKey, sdkConfig, options = {}, coordinatorEle
|
|
|
587
812
|
placeholder: options.placeholder,
|
|
588
813
|
disabled: options.disabled,
|
|
589
814
|
readOnly: options.readOnly,
|
|
590
|
-
debug: sdkConfig.debug
|
|
815
|
+
debug: sdkConfig.debug,
|
|
816
|
+
binLookup: options.binLookup,
|
|
817
|
+
coBadge: options.coBadge
|
|
591
818
|
});
|
|
592
819
|
if (iframe) iframe.style.opacity = "1";
|
|
593
820
|
} catch (error) {
|
|
@@ -617,16 +844,17 @@ const createCardNumberElement = (apiKey, sdkConfig, options = {}, coordinatorEle
|
|
|
617
844
|
if (!isMounted) {
|
|
618
845
|
throw ElementError("Element is not mounted");
|
|
619
846
|
}
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
error: error.message
|
|
624
|
-
});
|
|
847
|
+
notifyCoordinatorElementUnmount(id).catch((error) => {
|
|
848
|
+
log("Failed to notify coordinator of element unmount", {
|
|
849
|
+
error: error.message
|
|
625
850
|
});
|
|
851
|
+
});
|
|
852
|
+
if (client) {
|
|
626
853
|
client.destroy();
|
|
627
854
|
client = null;
|
|
628
855
|
}
|
|
629
856
|
if (mounter && iframe) {
|
|
857
|
+
iframe.style.height = "";
|
|
630
858
|
mounter.unmount(iframe);
|
|
631
859
|
iframe = null;
|
|
632
860
|
mounter = null;
|
|
@@ -640,10 +868,23 @@ const createCardNumberElement = (apiKey, sdkConfig, options = {}, coordinatorEle
|
|
|
640
868
|
throw ElementError("Element must be mounted before updating");
|
|
641
869
|
}
|
|
642
870
|
const updatePayload = {
|
|
643
|
-
|
|
644
|
-
disabled: newOptions.disabled,
|
|
645
|
-
readOnly: newOptions.readOnly
|
|
871
|
+
elementId: id
|
|
646
872
|
};
|
|
873
|
+
if (newOptions.placeholder !== void 0) {
|
|
874
|
+
updatePayload.placeholder = newOptions.placeholder;
|
|
875
|
+
}
|
|
876
|
+
if (newOptions.disabled !== void 0) {
|
|
877
|
+
updatePayload.disabled = newOptions.disabled;
|
|
878
|
+
}
|
|
879
|
+
if (newOptions.readOnly !== void 0) {
|
|
880
|
+
updatePayload.readOnly = newOptions.readOnly;
|
|
881
|
+
}
|
|
882
|
+
if (newOptions.binLookup !== void 0) {
|
|
883
|
+
updatePayload.binLookup = newOptions.binLookup;
|
|
884
|
+
}
|
|
885
|
+
if (newOptions.coBadge !== void 0) {
|
|
886
|
+
updatePayload.coBadge = newOptions.coBadge;
|
|
887
|
+
}
|
|
647
888
|
if (newOptions.themeMode !== void 0) {
|
|
648
889
|
updatePayload.themeMode = newOptions.themeMode;
|
|
649
890
|
updatePayload.theme = sdkConfig.theme;
|
|
@@ -669,6 +910,12 @@ const createCardNumberElement = (apiKey, sdkConfig, options = {}, coordinatorEle
|
|
|
669
910
|
}
|
|
670
911
|
client.sendRequest("CMD_CLEAR", {});
|
|
671
912
|
},
|
|
913
|
+
setNetwork: async (network) => {
|
|
914
|
+
if (!isMounted || !client) {
|
|
915
|
+
throw ElementError("Element must be mounted before setNetwork");
|
|
916
|
+
}
|
|
917
|
+
await client.sendRequest("CMD_SET_NETWORK", { network });
|
|
918
|
+
},
|
|
672
919
|
on: (eventType, listener) => {
|
|
673
920
|
eventTarget.addEventListener(
|
|
674
921
|
eventType,
|
|
@@ -681,7 +928,7 @@ const createCardNumberElement = (apiKey, sdkConfig, options = {}, coordinatorEle
|
|
|
681
928
|
}
|
|
682
929
|
};
|
|
683
930
|
};
|
|
684
|
-
const createCVVElement = (apiKey, sdkConfig, options = {}, coordinatorElementId, ensureCoordinatorReady) => {
|
|
931
|
+
const createCVVElement = (apiKey, sdkConfig, options = {}, coordinatorElementId, ensureCoordinatorReady, notifyCoordinatorElementUnmount) => {
|
|
685
932
|
let iframe = null;
|
|
686
933
|
let client = null;
|
|
687
934
|
let mounter = null;
|
|
@@ -804,12 +1051,12 @@ const createCVVElement = (apiKey, sdkConfig, options = {}, coordinatorElementId,
|
|
|
804
1051
|
if (!isMounted) {
|
|
805
1052
|
throw ElementError("Element is not mounted");
|
|
806
1053
|
}
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
error: error.message
|
|
811
|
-
});
|
|
1054
|
+
notifyCoordinatorElementUnmount(id).catch((error) => {
|
|
1055
|
+
log("Failed to notify coordinator of element unmount", {
|
|
1056
|
+
error: error.message
|
|
812
1057
|
});
|
|
1058
|
+
});
|
|
1059
|
+
if (client) {
|
|
813
1060
|
client.destroy();
|
|
814
1061
|
client = null;
|
|
815
1062
|
}
|
|
@@ -871,7 +1118,7 @@ const createCVVElement = (apiKey, sdkConfig, options = {}, coordinatorElementId,
|
|
|
871
1118
|
}
|
|
872
1119
|
};
|
|
873
1120
|
};
|
|
874
|
-
const createExpiryElement = (apiKey, sdkConfig, options = {}, coordinatorElementId, ensureCoordinatorReady) => {
|
|
1121
|
+
const createExpiryElement = (apiKey, sdkConfig, options = {}, coordinatorElementId, ensureCoordinatorReady, notifyCoordinatorElementUnmount) => {
|
|
875
1122
|
let iframe = null;
|
|
876
1123
|
let client = null;
|
|
877
1124
|
let mounter = null;
|
|
@@ -993,12 +1240,12 @@ const createExpiryElement = (apiKey, sdkConfig, options = {}, coordinatorElement
|
|
|
993
1240
|
if (!isMounted) {
|
|
994
1241
|
throw ElementError("Element is not mounted");
|
|
995
1242
|
}
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
error: error.message
|
|
1000
|
-
});
|
|
1243
|
+
notifyCoordinatorElementUnmount(id).catch((error) => {
|
|
1244
|
+
log("Failed to notify coordinator of element unmount", {
|
|
1245
|
+
error: error.message
|
|
1001
1246
|
});
|
|
1247
|
+
});
|
|
1248
|
+
if (client) {
|
|
1002
1249
|
client.destroy();
|
|
1003
1250
|
client = null;
|
|
1004
1251
|
}
|
|
@@ -1057,7 +1304,7 @@ const createExpiryElement = (apiKey, sdkConfig, options = {}, coordinatorElement
|
|
|
1057
1304
|
}
|
|
1058
1305
|
};
|
|
1059
1306
|
};
|
|
1060
|
-
const createTextElement = (apiKey, sdkConfig, options = {}, coordinatorElementId, ensureCoordinatorReady) => {
|
|
1307
|
+
const createTextElement = (apiKey, sdkConfig, options = {}, coordinatorElementId, ensureCoordinatorReady, notifyCoordinatorElementUnmount) => {
|
|
1061
1308
|
let iframe = null;
|
|
1062
1309
|
let client = null;
|
|
1063
1310
|
let mounter = null;
|
|
@@ -1207,12 +1454,12 @@ const createTextElement = (apiKey, sdkConfig, options = {}, coordinatorElementId
|
|
|
1207
1454
|
if (!isMounted) {
|
|
1208
1455
|
throw ElementError("Element is not mounted");
|
|
1209
1456
|
}
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
error: error.message
|
|
1214
|
-
});
|
|
1457
|
+
notifyCoordinatorElementUnmount(id).catch((error) => {
|
|
1458
|
+
log("Failed to notify coordinator of element unmount", {
|
|
1459
|
+
error: error.message
|
|
1215
1460
|
});
|
|
1461
|
+
});
|
|
1462
|
+
if (client) {
|
|
1216
1463
|
client.destroy();
|
|
1217
1464
|
client = null;
|
|
1218
1465
|
}
|
|
@@ -1540,7 +1787,7 @@ const validateSDKConfig = (options = {}) => {
|
|
|
1540
1787
|
if (!["light", "dark", "auto"].includes(themeMode)) {
|
|
1541
1788
|
throw ConfigurationError('themeMode must be "light", "dark", or "auto"');
|
|
1542
1789
|
}
|
|
1543
|
-
const finalBaseUrl = baseUrl || "https://js.basistheory.com/3.0.0-beta.
|
|
1790
|
+
const finalBaseUrl = baseUrl || "https://js.basistheory.com/3.0.0-beta.4/elements";
|
|
1544
1791
|
const iframeOrigin = new URL(finalBaseUrl).origin;
|
|
1545
1792
|
return {
|
|
1546
1793
|
iframeOrigin,
|
|
@@ -1566,7 +1813,7 @@ const COMMON_ELEMENT_KEYS = /* @__PURE__ */ new Set([
|
|
|
1566
1813
|
"readOnly"
|
|
1567
1814
|
]);
|
|
1568
1815
|
const ELEMENT_TYPE_KEYS = {
|
|
1569
|
-
cardNumber: /* @__PURE__ */ new Set([...COMMON_ELEMENT_KEYS]),
|
|
1816
|
+
cardNumber: /* @__PURE__ */ new Set([...COMMON_ELEMENT_KEYS, "binLookup", "coBadge"]),
|
|
1570
1817
|
cvv: /* @__PURE__ */ new Set([...COMMON_ELEMENT_KEYS, "cardBrand", "cvvLengths"]),
|
|
1571
1818
|
// Internal coordination props
|
|
1572
1819
|
expiry: /* @__PURE__ */ new Set([...COMMON_ELEMENT_KEYS]),
|
|
@@ -1585,7 +1832,15 @@ const ELEMENT_TYPE_KEYS = {
|
|
|
1585
1832
|
"layout",
|
|
1586
1833
|
"stackAt",
|
|
1587
1834
|
"iconPosition",
|
|
1588
|
-
"cardBrands"
|
|
1835
|
+
"cardBrands",
|
|
1836
|
+
"binLookup",
|
|
1837
|
+
"coBadge"
|
|
1838
|
+
]),
|
|
1839
|
+
copyButton: /* @__PURE__ */ new Set([
|
|
1840
|
+
"allowClipboard",
|
|
1841
|
+
"value",
|
|
1842
|
+
"text",
|
|
1843
|
+
"disabled"
|
|
1589
1844
|
])
|
|
1590
1845
|
};
|
|
1591
1846
|
const validateElementOptions = (type, options = {}) => {
|
|
@@ -1636,6 +1891,14 @@ const validateElementOptions = (type, options = {}) => {
|
|
|
1636
1891
|
}
|
|
1637
1892
|
}
|
|
1638
1893
|
}
|
|
1894
|
+
if (type === "copyButton") {
|
|
1895
|
+
const copyOpts = options;
|
|
1896
|
+
if (copyOpts.allowClipboard !== true) {
|
|
1897
|
+
throw ConfigurationError(
|
|
1898
|
+
"CopyButton requires allowClipboard: true. Clipboard access for raw card data may not be PCI compliant and must be explicitly enabled."
|
|
1899
|
+
);
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1639
1902
|
if (type === "card") {
|
|
1640
1903
|
const cardOptions = options;
|
|
1641
1904
|
if (cardOptions.layout !== void 0) {
|
|
@@ -1679,14 +1942,41 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
|
|
|
1679
1942
|
let coordinatorReadyPromise = null;
|
|
1680
1943
|
let coordinatorInitStarted = false;
|
|
1681
1944
|
let currentThemeMode = options.themeMode || "auto";
|
|
1945
|
+
const notifyCoordinatorElementUnmount = async (elementId) => {
|
|
1946
|
+
if (!coordinatorClient) {
|
|
1947
|
+
log2("Cannot notify coordinator of unmount - coordinator not initialized", {
|
|
1948
|
+
elementId
|
|
1949
|
+
});
|
|
1950
|
+
return;
|
|
1951
|
+
}
|
|
1952
|
+
try {
|
|
1953
|
+
await coordinatorClient.sendRequest("CMD_BROADCAST_ELEMENT_UNMOUNT", {
|
|
1954
|
+
elementId
|
|
1955
|
+
});
|
|
1956
|
+
log2("Notified coordinator of element unmount", { elementId });
|
|
1957
|
+
} catch (error) {
|
|
1958
|
+
log2("Failed to notify coordinator of element unmount", {
|
|
1959
|
+
elementId,
|
|
1960
|
+
error: error.message
|
|
1961
|
+
});
|
|
1962
|
+
}
|
|
1963
|
+
};
|
|
1682
1964
|
let cardElementFactory = null;
|
|
1965
|
+
let copyButtonElementFactory = null;
|
|
1683
1966
|
const lazyCardElement = async () => {
|
|
1684
1967
|
if (!cardElementFactory) {
|
|
1685
|
-
const module = await import("./create-card-
|
|
1968
|
+
const module = await import("./create-card-B5zEHyRg.js");
|
|
1686
1969
|
cardElementFactory = module.createCardElement;
|
|
1687
1970
|
}
|
|
1688
1971
|
return cardElementFactory;
|
|
1689
1972
|
};
|
|
1973
|
+
const lazyCopyButtonElement = async () => {
|
|
1974
|
+
if (!copyButtonElementFactory) {
|
|
1975
|
+
const module = await import("./create-copy-button-DrAz8g0m.js");
|
|
1976
|
+
copyButtonElementFactory = module.createCopyButtonElement;
|
|
1977
|
+
}
|
|
1978
|
+
return copyButtonElementFactory;
|
|
1979
|
+
};
|
|
1690
1980
|
const syncFactories = {
|
|
1691
1981
|
cardNumber: createCardNumberElement,
|
|
1692
1982
|
cvv: createCVVElement,
|
|
@@ -1694,7 +1984,8 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
|
|
|
1694
1984
|
text: createTextElement
|
|
1695
1985
|
};
|
|
1696
1986
|
const lazyFactories = {
|
|
1697
|
-
card: lazyCardElement
|
|
1987
|
+
card: lazyCardElement,
|
|
1988
|
+
copyButton: lazyCopyButtonElement
|
|
1698
1989
|
};
|
|
1699
1990
|
const ensureCoordinatorInitialized = () => {
|
|
1700
1991
|
if (coordinatorReadyPromise) {
|
|
@@ -1802,7 +2093,8 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
|
|
|
1802
2093
|
config,
|
|
1803
2094
|
elementOptions,
|
|
1804
2095
|
localElementId,
|
|
1805
|
-
ensureCoordinatorInitialized
|
|
2096
|
+
ensureCoordinatorInitialized,
|
|
2097
|
+
notifyCoordinatorElementUnmount
|
|
1806
2098
|
);
|
|
1807
2099
|
} else {
|
|
1808
2100
|
let realElement = null;
|
|
@@ -1816,7 +2108,8 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
|
|
|
1816
2108
|
config,
|
|
1817
2109
|
elementOptions,
|
|
1818
2110
|
localElementId,
|
|
1819
|
-
ensureCoordinatorInitialized
|
|
2111
|
+
ensureCoordinatorInitialized,
|
|
2112
|
+
notifyCoordinatorElementUnmount
|
|
1820
2113
|
);
|
|
1821
2114
|
})();
|
|
1822
2115
|
}
|
|
@@ -1862,6 +2155,16 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
|
|
|
1862
2155
|
};
|
|
1863
2156
|
}
|
|
1864
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");
|
|
1865
2168
|
}
|
|
1866
2169
|
};
|
|
1867
2170
|
}
|
|
@@ -2206,6 +2509,54 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
|
|
|
2206
2509
|
}
|
|
2207
2510
|
}
|
|
2208
2511
|
},
|
|
2512
|
+
proxy: (() => {
|
|
2513
|
+
const invokeProxy = async (method, options2 = {}) => {
|
|
2514
|
+
validateProxyOptions({ ...options2, method });
|
|
2515
|
+
log2("proxy invoked", {
|
|
2516
|
+
method,
|
|
2517
|
+
hasUrl: !!options2.url,
|
|
2518
|
+
hasProxyKey: !!options2.proxyKey
|
|
2519
|
+
});
|
|
2520
|
+
if (!coordinatorClient) {
|
|
2521
|
+
log2("Waiting for coordinator initialization");
|
|
2522
|
+
await initializeCoordinator();
|
|
2523
|
+
}
|
|
2524
|
+
if (!coordinatorClient) {
|
|
2525
|
+
throw CoordinatorError("Coordinator failed to initialize");
|
|
2526
|
+
}
|
|
2527
|
+
const resolvedBody = options2.body !== void 0 ? resolveElementReferences(options2.body) : void 0;
|
|
2528
|
+
try {
|
|
2529
|
+
const result = await coordinatorClient.sendRequest(
|
|
2530
|
+
"CMD_PROXY",
|
|
2531
|
+
{
|
|
2532
|
+
method,
|
|
2533
|
+
url: options2.url,
|
|
2534
|
+
proxyKey: options2.proxyKey,
|
|
2535
|
+
headers: options2.headers,
|
|
2536
|
+
body: resolvedBody,
|
|
2537
|
+
includeResponseHeaders: options2.includeResponseHeaders
|
|
2538
|
+
},
|
|
2539
|
+
{ retries: 0 }
|
|
2540
|
+
);
|
|
2541
|
+
log2("proxy successful", { method });
|
|
2542
|
+
return result;
|
|
2543
|
+
} catch (error) {
|
|
2544
|
+
log2("proxy failed", {
|
|
2545
|
+
method,
|
|
2546
|
+
error: error.message
|
|
2547
|
+
});
|
|
2548
|
+
throw error;
|
|
2549
|
+
}
|
|
2550
|
+
};
|
|
2551
|
+
const proxyVerb = (method) => (options2) => invokeProxy(method, options2 ?? {});
|
|
2552
|
+
return {
|
|
2553
|
+
get: proxyVerb("GET"),
|
|
2554
|
+
post: proxyVerb("POST"),
|
|
2555
|
+
put: proxyVerb("PUT"),
|
|
2556
|
+
patch: proxyVerb("PATCH"),
|
|
2557
|
+
delete: proxyVerb("DELETE")
|
|
2558
|
+
};
|
|
2559
|
+
})(),
|
|
2209
2560
|
updateThemeMode: async (mode) => {
|
|
2210
2561
|
log2("updateThemeMode called", {
|
|
2211
2562
|
oldMode: currentThemeMode,
|
|
@@ -2299,13 +2650,14 @@ const BasisTheory = (apiKey, options = {}, queuedCommands) => {
|
|
|
2299
2650
|
if (typeof window !== "undefined" && typeof document !== "undefined" && document.currentScript && !window.BasisTheory) {
|
|
2300
2651
|
window.BasisTheory = BasisTheory;
|
|
2301
2652
|
}
|
|
2302
|
-
const version = "3.0.0-beta.
|
|
2303
|
-
const buildDate = "2026-
|
|
2653
|
+
const version = "3.0.0-beta.4";
|
|
2654
|
+
const buildDate = "2026-06-09T15:35:24.276Z";
|
|
2304
2655
|
if (typeof window !== "undefined") {
|
|
2305
2656
|
window.__BasisTheorySDK__ = BasisTheory;
|
|
2306
2657
|
}
|
|
2307
2658
|
export {
|
|
2308
2659
|
BasisTheory as B,
|
|
2660
|
+
ConfigurationError as C,
|
|
2309
2661
|
ElementError as E,
|
|
2310
2662
|
createPostMessageClient as a,
|
|
2311
2663
|
createEventDispatcher as b,
|