@basis-theory/web-elements 3.0.0-beta.2 → 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.
Files changed (59) hide show
  1. package/README.md +237 -562
  2. package/dist/.vite/manifest.json +19 -8
  3. package/dist/api-client/src/api-client.d.ts.map +1 -1
  4. package/dist/api-client/src/bin-lookup-transform.d.ts +35 -0
  5. package/dist/api-client/src/bin-lookup-transform.d.ts.map +1 -0
  6. package/dist/api-client/src/http-client.d.ts.map +1 -1
  7. package/dist/api-client/src/index.d.ts +1 -1
  8. package/dist/api-client/src/index.d.ts.map +1 -1
  9. package/dist/api-client/src/types.d.ts +114 -6
  10. package/dist/api-client/src/types.d.ts.map +1 -1
  11. package/dist/basis-theory-sdk.es.js +1 -1
  12. package/dist/basis-theory-sdk.umd.js +1 -1
  13. package/dist/basis-theory-sdk.umd.js.map +1 -1
  14. package/dist/{create-card-BuOvimjH.js → create-card-B5zEHyRg.js} +49 -15
  15. package/dist/create-card-B5zEHyRg.js.map +1 -0
  16. package/dist/create-copy-button-DrAz8g0m.js +233 -0
  17. package/dist/create-copy-button-DrAz8g0m.js.map +1 -0
  18. package/dist/{index-oREjBjga.js → index-BOedIhUN.js} +406 -54
  19. package/dist/index-BOedIhUN.js.map +1 -0
  20. package/dist/loader/basis-theory.min.js +1 -1
  21. package/dist/postmessage/src/client.d.ts.map +1 -1
  22. package/dist/postmessage/src/event-map.d.ts +4 -0
  23. package/dist/postmessage/src/event-map.d.ts.map +1 -1
  24. package/dist/postmessage/src/server.d.ts.map +1 -1
  25. package/dist/postmessage/src/types.d.ts +7 -3
  26. package/dist/postmessage/src/types.d.ts.map +1 -1
  27. package/dist/shared/src/index.d.ts +1 -0
  28. package/dist/shared/src/index.d.ts.map +1 -1
  29. package/dist/shared/src/proxy-validation.d.ts +93 -0
  30. package/dist/shared/src/proxy-validation.d.ts.map +1 -0
  31. package/dist/sri.json +9 -9
  32. package/dist/stats.html +1 -1
  33. package/dist/web-elements/src/BasisTheory.d.ts +1 -1
  34. package/dist/web-elements/src/BasisTheory.d.ts.map +1 -1
  35. package/dist/web-elements/src/basis-theory.d.ts +23 -0
  36. package/dist/web-elements/src/basis-theory.d.ts.map +1 -1
  37. package/dist/web-elements/src/elements/create-card-number.d.ts +2 -1
  38. package/dist/web-elements/src/elements/create-card-number.d.ts.map +1 -1
  39. package/dist/web-elements/src/elements/create-card.d.ts +14 -1
  40. package/dist/web-elements/src/elements/create-card.d.ts.map +1 -1
  41. package/dist/web-elements/src/elements/create-copy-button.d.ts +19 -0
  42. package/dist/web-elements/src/elements/create-copy-button.d.ts.map +1 -0
  43. package/dist/web-elements/src/elements/create-cvv.d.ts +2 -1
  44. package/dist/web-elements/src/elements/create-cvv.d.ts.map +1 -1
  45. package/dist/web-elements/src/elements/create-expiry.d.ts +2 -1
  46. package/dist/web-elements/src/elements/create-expiry.d.ts.map +1 -1
  47. package/dist/web-elements/src/elements/create-text.d.ts +2 -1
  48. package/dist/web-elements/src/elements/create-text.d.ts.map +1 -1
  49. package/dist/web-elements/src/index.d.ts +1 -1
  50. package/dist/web-elements/src/index.d.ts.map +1 -1
  51. package/dist/web-elements/src/types/events.d.ts +30 -2
  52. package/dist/web-elements/src/types/events.d.ts.map +1 -1
  53. package/dist/web-elements/src/types.d.ts +135 -5
  54. package/dist/web-elements/src/types.d.ts.map +1 -1
  55. package/dist/web-elements/src/utils/create-event-dispatcher.d.ts.map +1 -1
  56. package/dist/web-elements/src/utils/validate-element-options.d.ts.map +1 -1
  57. package/package.json +5 -5
  58. package/dist/create-card-BuOvimjH.js.map +0 -1
  59. package/dist/index-oREjBjga.js.map +0 -1
package/README.md CHANGED
@@ -1,669 +1,344 @@
1
- # @basis-theory-elements/sdk
1
+ # @basis-theory/web-elements
2
2
 
3
- Secure, PCI-compliant SDK for creating payment input elements in web applications.
3
+ Secure, PCI-compliant iframe elements for collecting sensitive data in web applications. Card data never touches your servers.
4
4
 
5
- Zero dependencies on RxJS · No dynamic script loading · Framework-agnostic · TypeScript-first
5
+ ## Installation
6
6
 
7
- ---
7
+ ### npm
8
8
 
9
- ## 🌍 **Platform Support**
10
-
11
- ✅ **Web Browsers Only** (v3.0)
12
-
13
- - Chrome, Firefox, Safari, Edge (desktop)
14
- - Chrome, Safari (mobile browsers)
15
- - Standard `window.postMessage` API required
16
-
17
- ❌ **Not Yet Supported:**
18
-
19
- - iOS/Android Native WebViews
20
- - React Native WebView
21
- - Electron (may work, untested)
22
-
23
- **See [v3 Documentation](../../docs/README.md) for architecture and platform roadmap.**
24
-
25
- ---
26
-
27
- ## 📦 **Installation**
9
+ ```bash
10
+ npm install @basis-theory/web-elements@beta
11
+ ```
28
12
 
29
- ### **For Bundlers (webpack, vite, rollup)**
13
+ ### yarn
30
14
 
31
15
  ```bash
32
- npm install @basis-theory-elements/sdk
16
+ yarn add @basis-theory/web-elements@beta
33
17
  ```
34
18
 
35
- ### **For Script Tags (no build step)**
19
+ ### CDN
36
20
 
37
21
  ```html
38
- <!-- CDN-hosted UMD bundle -->
39
- <script src="https://cdn.basistheory.com/elements/v3/index.umd.js"></script>
22
+ <script src="https://js.basistheory.com/3.0.0-beta.2/web-elements/basis-theory.js"></script>
40
23
  ```
41
24
 
42
- **Requirements:**
43
-
44
- - Modern browser with `iframe` and `postMessage` support
45
- - TypeScript 4.5+ (optional but recommended for ESM)
46
-
47
- ---
48
-
49
- ## 🚀 **Quick Start**
50
-
51
- ### **ESM (bundlers)**
25
+ ## Quick Start
52
26
 
53
27
  ```typescript
54
- import BasisTheory from '@basis-theory-elements/sdk';
55
-
56
- // 1. Initialize SDK
57
- const bt = BasisTheory('pk_test_YOUR_API_KEY');
58
-
59
- // 2. Create elements
60
- const cardNumber = bt.createElement('cardNumber', {
61
- placeholder: '4111 1111 1111 1111',
62
- style: {
63
- base: {
64
- color: '#000',
65
- fontSize: '16px',
66
- fontFamily: 'Arial, sans-serif',
67
- },
68
- invalid: {
69
- color: '#EA4B97',
70
- },
71
- complete: {
72
- color: '#1976d2',
73
- },
74
- },
75
- });
28
+ import BasisTheory from '@basis-theory/web-elements';
76
29
 
77
- const cvv = bt.createElement('cvv', {
78
- placeholder: '123',
79
- style: {
80
- /* ... */
81
- },
82
- });
30
+ // Initialize (synchronous -- returns immediately)
31
+ const bt = BasisTheory('pk_test_...');
83
32
 
84
- const expiry = bt.createElement('expiry', {
33
+ // Create elements
34
+ const cardNumberEl = bt.createElement('cardNumber', {
35
+ placeholder: '4242 4242 4242 4242',
36
+ });
37
+ const expiryEl = bt.createElement('expiry', {
85
38
  placeholder: 'MM/YY',
86
- style: {
87
- /* ... */
88
- },
89
39
  });
90
-
91
- // 3. Mount elements to DOM
92
- await cardNumber.mount('#card-number');
93
- await cvv.mount('#cvv');
94
- await expiry.mount('#expiry');
95
-
96
- // 4. Listen for changes
97
- cardNumber.on('change', (event) => {
98
- console.log('Card Brand:', event.detail.cardBrand);
99
- console.log('Is Valid:', event.detail.isValid);
100
- console.log('Is Complete:', event.detail.isComplete);
40
+ const cvvEl = bt.createElement('cvv', {
41
+ placeholder: '123',
101
42
  });
102
43
 
103
- cardNumber.on('ready', () => {
104
- console.log('Card number element is ready');
44
+ // Mount to DOM (async -- creates secure iframes)
45
+ await Promise.all([
46
+ cardNumberEl.mount('#card-number'),
47
+ expiryEl.mount('#expiry'),
48
+ cvvEl.mount('#cvv'),
49
+ ]);
50
+
51
+ // Listen for validation changes
52
+ cardNumberEl.on('change', (event) => {
53
+ console.log('Valid:', event.detail.isValid);
54
+ console.log('Brand:', event.detail.cardBrand);
105
55
  });
106
56
 
107
- // 5. Tokenize
108
- const token = await bt.tokenize({
109
- number: cardNumber,
110
- cvv: cvv,
111
- expiry: expiry,
57
+ // Tokenize on form submission
58
+ const token = await bt.tokens.create({
59
+ type: 'card',
60
+ data: {
61
+ number: cardNumberEl,
62
+ expiration_month: expiryEl,
63
+ expiration_year: expiryEl,
64
+ cvc: cvvEl,
65
+ },
112
66
  });
113
67
 
114
68
  console.log('Token ID:', token.id);
115
- console.log('Masked Card:', token.mask);
116
69
  ```
117
70
 
118
- ### **UMD (script tags)**
71
+ ### CDN Usage
119
72
 
120
73
  ```html
121
- <!DOCTYPE html>
122
- <html>
123
- <head>
124
- <!-- Load SDK from CDN -->
125
- <script src="https://cdn.basistheory.com/elements/v3/index.umd.js"></script>
126
- </head>
127
- <body>
128
- <div id="card-number"></div>
129
- <div id="cvv"></div>
130
- <div id="expiry"></div>
131
- <button id="tokenize-btn">Tokenize</button>
132
-
133
- <script>
134
- // SDK is available globally as window.BasisTheory
135
- const bt = window.BasisTheory('pk_test_YOUR_API_KEY');
136
-
137
- // Create elements
138
- const cardNumber = bt.createElement('cardNumber', {
139
- placeholder: '4111 1111 1111 1111',
140
- style: {
141
- base: { color: '#000', fontSize: '16px' },
142
- invalid: { color: '#EA4B97' },
143
- complete: { color: '#1976d2' },
144
- },
145
- });
146
-
147
- const cvv = bt.createElement('cvv', { placeholder: '123' });
148
- const expiry = bt.createElement('expiry', { placeholder: 'MM/YY' });
149
-
150
- // Mount elements
151
- cardNumber.mount('#card-number').then(() => {
152
- console.log('Card number mounted');
153
- });
154
- cvv.mount('#cvv');
155
- expiry.mount('#expiry');
156
-
157
- // Tokenize on button click
158
- document
159
- .getElementById('tokenize-btn')
160
- .addEventListener('click', async () => {
161
- const token = await bt.tokenize({
162
- number: cardNumber,
163
- cvv: cvv,
164
- expiry: expiry,
165
- });
166
- console.log('Token:', token);
167
- });
168
- </script>
169
- </body>
170
- </html>
74
+ <script src="https://js.basistheory.com/3.0.0-beta.2/web-elements/basis-theory.js"></script>
75
+ <script>
76
+ document.addEventListener('DOMContentLoaded', async () => {
77
+ const bt = BasisTheory('pk_test_...');
78
+
79
+ const cardNumberEl = bt.createElement('cardNumber');
80
+ const expiryEl = bt.createElement('expiry');
81
+ const cvvEl = bt.createElement('cvv');
82
+
83
+ await Promise.all([
84
+ cardNumberEl.mount('#card-number'),
85
+ expiryEl.mount('#expiry'),
86
+ cvvEl.mount('#cvv'),
87
+ ]);
88
+ });
89
+ </script>
171
90
  ```
172
91
 
173
- ---
92
+ ## Element Types
174
93
 
175
- ## 🎨 **Element Types**
94
+ | Type | `createElement` key | Description |
95
+ |------|---------------------|-------------|
96
+ | Card Number | `'cardNumber'` | PAN input with Luhn validation and brand detection |
97
+ | Expiry | `'expiry'` | MM/YY expiration date input |
98
+ | CVV | `'cvv'` | Card verification code input |
99
+ | Text | `'text'` | General-purpose secure text input (SSN, routing numbers, etc.) |
176
100
 
177
- ### **Card Number**
101
+ ### Shared Options
178
102
 
179
- ```typescript
180
- const cardNumber = bt.createElement('cardNumber', {
181
- placeholder: '4111 1111 1111 1111',
182
- style: {
183
- /* ... */
184
- },
185
- ariaLabel: 'Card Number',
186
- disabled: false,
187
- readOnly: false,
188
- });
103
+ All element types accept these options in `createElement()` and `element.update()`:
189
104
 
190
- // Events
191
- cardNumber.on('change', (event) => {
192
- console.log(event.detail.cardBrand); // 'visa', 'mastercard', etc.
193
- console.log(event.detail.isValid); // true/false
194
- console.log(event.detail.isPotentiallyValid); // Could become valid?
195
- console.log(event.detail.potentialBrands); // ['visa', 'mastercard']
196
- });
197
- ```
105
+ | Option | Type | Default | Description |
106
+ |--------|------|---------|-------------|
107
+ | `placeholder` | `string` | -- | Placeholder text shown when the input is empty |
108
+ | `ariaLabel` | `string` | Element-specific | ARIA label for screen readers |
109
+ | `disabled` | `boolean` | `false` | Disables the input |
110
+ | `readOnly` | `boolean` | `false` | Makes the input read-only |
198
111
 
199
- ### **CVV**
112
+ ### Text Element Options
200
113
 
201
- ```typescript
202
- const cvv = bt.createElement('cvv', {
203
- placeholder: '123',
204
- style: {
205
- /* ... */
206
- },
207
- });
114
+ | Option | Type | Description |
115
+ |--------|------|-------------|
116
+ | `validation` | `RegExp` | Pattern the value must match to be valid |
117
+ | `required` | `boolean` | Whether the field is required |
118
+ | `maxLength` | `number` | Maximum character length |
119
+ | `password` | `boolean` | Renders as a password field |
120
+ | `inputMode` | `string` | Mobile keyboard hint (`'numeric'`, `'tel'`, etc.) |
121
+ | `mask` | `(RegExp \| string)[]` | Character-by-character input mask |
122
+ | `transform` | `[RegExp, string]` | Transform applied before tokenization |
208
123
 
209
- // Events
210
- cvv.on('change', (event) => {
211
- console.log(event.detail.isValid);
212
- console.log(event.detail.isComplete);
213
- });
214
- ```
124
+ ### CVV Element Options
215
125
 
216
- ### **Expiry Date**
126
+ | Option | Type | Default | Description |
127
+ |--------|------|---------|-------------|
128
+ | `showToggle` | `boolean` | `false` | Renders a show/hide toggle button inside the field |
217
129
 
218
- ```typescript
219
- const expiry = bt.createElement('expiry', {
220
- placeholder: 'MM/YY',
221
- style: {
222
- /* ... */
223
- },
224
- });
225
-
226
- // Events
227
- expiry.on('change', (event) => {
228
- console.log(event.detail.isValid);
229
- console.log(event.detail.isComplete);
230
- });
231
- ```
130
+ ## Element Methods
232
131
 
233
- ---
132
+ Every element exposes the same interface:
234
133
 
235
- ## 📋 **Element Methods**
134
+ | Method | Signature | Description |
135
+ |--------|-----------|-------------|
136
+ | `mount` | `(selector: string \| HTMLElement) => Promise<void>` | Attaches the iframe to the DOM |
137
+ | `unmount` | `() => void` | Removes the iframe from the DOM |
138
+ | `update` | `(options) => Promise<void>` | Updates options on a mounted element |
139
+ | `focus` | `() => void` | Focuses the input |
140
+ | `blur` | `() => void` | Blurs the input |
141
+ | `clear` | `() => void` | Clears the current value |
142
+ | `on` | `(event, listener) => () => void` | Subscribes to an event; returns an unsubscribe function |
236
143
 
237
- All elements support the following methods:
144
+ Properties: `id` (string), `type` (ElementType), `mounted` (boolean).
238
145
 
239
- ```typescript
240
- // Mount to DOM
241
- await element.mount('#container');
242
- await element.mount(document.querySelector('#container'));
146
+ ## Events
243
147
 
244
- // Unmount from DOM
245
- element.unmount();
148
+ | Event | Fires when |
149
+ |-------|-----------|
150
+ | `ready` | Element iframe is loaded and interactive |
151
+ | `change` | Input value changes |
152
+ | `focus` | Element receives focus |
153
+ | `blur` | Element loses focus |
154
+ | `error` | An infrastructure or API error occurs |
246
155
 
247
- // Update configuration
248
- await element.update({
249
- placeholder: 'New placeholder',
250
- style: { base: { color: '#333' } },
251
- });
156
+ ### change Event Payload
252
157
 
253
- // Focus/blur input
254
- element.focus();
255
- element.blur();
158
+ All elements include these fields in `event.detail`:
256
159
 
257
- // Clear input value
258
- element.clear();
160
+ | Field | Type | Description |
161
+ |-------|------|-------------|
162
+ | `isValid` | `boolean` | Input passes all validation rules |
163
+ | `isEmpty` | `boolean` | Input is empty |
164
+ | `error` | `ValidationError \| null` | Validation error, or `null` if valid |
165
+ | `elementType` | `string` | Element type that fired the event |
166
+ | `elementId` | `string` | Unique element ID |
167
+ | `timestamp` | `number` | Unix timestamp (ms) |
259
168
 
260
- // Listen for events (returns unsubscribe function)
261
- const unsubscribe = element.on('change', (event) => {
262
- console.log(event.detail);
263
- });
264
-
265
- // Cleanup
266
- unsubscribe();
267
- ```
268
-
269
- ---
270
-
271
- ## 🎭 **Events**
272
-
273
- ### **change**
274
-
275
- Fired when the input value changes.
169
+ The `cardNumber` element adds: `cardBrand`, `last4`, `bin`, `cvvLengths`, `potentialBrands`, and `matchStrength`.
276
170
 
277
171
  ```typescript
278
- element.on('change', (event) => {
279
- console.log({
280
- isValid: event.detail.isValid, // true/false
281
- isEmpty: event.detail.isEmpty, // true/false
282
- isComplete: event.detail.isComplete, // true/false
283
- isPotentiallyValid: event.detail.isPotentiallyValid, // true/false (real-time UX)
284
- cardBrand: event.detail.cardBrand, // 'visa' | 'mastercard' | ...
285
- potentialBrands: event.detail.potentialBrands, // ['visa', 'mastercard']
286
- error: event.detail.error, // { code, message }
287
- });
172
+ cardNumberEl.on('change', (event) => {
173
+ const { isValid, cardBrand, last4 } = event.detail;
174
+ console.log(`${cardBrand} ...${last4}`, isValid);
288
175
  });
289
176
  ```
290
177
 
291
- ### **ready**
292
-
293
- Fired when the element is fully initialized and ready for user input.
294
-
295
- ```typescript
296
- element.on('ready', (event) => {
297
- console.log('Element ready:', event.detail.elementType);
298
- });
299
- ```
178
+ ### error Event Codes
300
179
 
301
- ### **focus**
180
+ | Code | Description |
181
+ |------|-------------|
182
+ | `MOUNT_ERROR` | Failed to mount the element iframe |
183
+ | `POSTMESSAGE_TIMEOUT` | PostMessage timeout |
184
+ | `IFRAME_LOAD_ERROR` | Element iframe failed to load |
185
+ | `INITIALIZATION_ERROR` | SDK or coordinator failed to initialize |
186
+ | `API_ERROR` | Basis Theory API returned an error |
187
+ | `NETWORK_ERROR` | Network request failed |
188
+ | `VALIDATION_ERROR` | Input failed client-side validation |
189
+ | `INVALID_CONFIGURATION` | Invalid SDK or element options |
190
+ | `UNKNOWN_ERROR` | Unexpected error |
302
191
 
303
- Fired when the input receives focus.
192
+ ## SDK Options
304
193
 
305
194
  ```typescript
306
- element.on('focus', (event) => {
307
- console.log('Element focused:', event.detail.elementType);
195
+ const bt = BasisTheory('pk_test_...', {
196
+ debug: false, // Enable verbose debug logging
197
+ themeMode: 'auto', // 'light' | 'dark' | 'auto'
198
+ theme: { /* ThemeTokens */ },
199
+ darkTheme: { /* ThemeTokens */ },
200
+ timeoutMs: 30000, // PostMessage timeout (ms)
308
201
  });
309
202
  ```
310
203
 
311
- ### **blur**
204
+ ## Services
312
205
 
313
- Fired when the input loses focus.
206
+ ### tokens
314
207
 
315
208
  ```typescript
316
- element.on('blur', (event) => {
317
- console.log('Element blurred:', event.detail.elementType);
209
+ // Create a token
210
+ const token = await bt.tokens.create({
211
+ type: 'card',
212
+ data: {
213
+ number: cardNumberEl,
214
+ expiration_month: expiryEl,
215
+ expiration_year: expiryEl,
216
+ cvc: cvvEl,
217
+ },
318
218
  });
319
- ```
320
219
 
321
- ### **error**
322
-
323
- Fired when an error occurs (network failures, configuration errors, etc.).
324
-
325
- ```typescript
326
- element.on('error', (event) => {
327
- console.error('Element error:', {
328
- code: event.detail.code,
329
- message: event.detail.message,
330
- details: event.detail.details,
331
- });
220
+ // Retrieve a token
221
+ const retrieved = await bt.tokens.retrieve(token.id, {
222
+ apiKey: sessionApiKey, // from an authorized session
332
223
  });
333
- ```
334
224
 
335
- ---
336
-
337
- ## 🔐 **Tokenization**
338
-
339
- ### **Single Element**
340
-
341
- ```typescript
342
- const token = await bt.tokenize({
343
- cardNumber: cardNumber,
225
+ // Update a token
226
+ await bt.tokens.update(token.id, {
227
+ metadata: { source: 'updated' },
344
228
  });
345
- ```
346
229
 
347
- ### **Multiple Elements (Composite Token)**
348
-
349
- ```typescript
350
- const token = await bt.tokenize({
351
- number: cardNumber,
352
- cvv: cvv,
353
- expiry: expiry,
354
- });
230
+ // Encrypt with client-side JWE
231
+ const encrypted = await bt.tokens.encrypt(
232
+ { type: 'card', data: { number: cardNumberEl, cvc: cvvEl } },
233
+ publicKeyPEM,
234
+ 'key-id-123'
235
+ );
355
236
  ```
356
237
 
357
- ### **TokenizeResult**
238
+ ### tokenize
358
239
 
359
240
  ```typescript
360
- interface TokenizeResult {
361
- id: string; // Token ID
362
- type: 'card' | 'token'; // Token type
363
- mask?: string; // Masked card (e.g., "411111******1111")
364
- brand?: string; // Card brand (e.g., "visa")
365
- expiry?: {
366
- month: number;
367
- year: number;
368
- };
369
- metadata?: Record<string, unknown>;
370
- }
371
- ```
372
-
373
- ---
374
-
375
- ## 🎨 **Styling**
376
-
377
- ### **Style States**
378
-
379
- ```typescript
380
- interface ElementStyle {
381
- base?: CSSProperties; // Default state
382
- invalid?: CSSProperties; // When input is invalid
383
- complete?: CSSProperties; // When input is valid and complete
384
- empty?: CSSProperties; // When input is empty
385
- }
386
- ```
387
-
388
- ### **Supported CSS Properties**
389
-
390
- ```typescript
391
- interface CSSProperties {
392
- color?: string;
393
- backgroundColor?: string;
394
- fontSize?: string;
395
- fontFamily?: string;
396
- fontWeight?: string;
397
- lineHeight?: string;
398
- letterSpacing?: string;
399
- textAlign?: string;
400
- padding?: string;
401
- margin?: string;
402
- border?: string;
403
- borderRadius?: string;
404
- boxShadow?: string;
405
- // ... more (see API Reference section below for complete list)
406
- }
407
- ```
408
-
409
- ### **Example**
410
-
411
- ```typescript
412
- const cardNumber = bt.createElement('cardNumber', {
413
- style: {
414
- base: {
415
- color: '#000',
416
- fontSize: '16px',
417
- fontFamily: 'Arial, sans-serif',
418
- backgroundColor: '#fff',
419
- padding: '12px',
420
- border: '1px solid #ddd',
421
- borderRadius: '4px',
422
- },
423
- invalid: {
424
- color: '#EA4B97',
425
- borderColor: '#EA4B97',
426
- },
427
- complete: {
428
- color: '#1976d2',
429
- borderColor: '#1976d2',
430
- },
431
- empty: {
432
- color: '#999',
433
- },
434
- },
241
+ const result = await bt.tokenize({
242
+ number: cardNumberEl,
243
+ cvv: cvvEl,
244
+ expiry: expiryEl,
435
245
  });
436
246
  ```
437
247
 
438
- ---
439
-
440
- ## ⚙️ **Configuration**
441
-
442
- ### **SDK Options**
248
+ ### tokenIntents
443
249
 
444
250
  ```typescript
445
- const bt = BasisTheory('pk_test_YOUR_API_KEY', {
446
- elementsBaseUrl: 'https://js.basistheory.com/v3/elements', // Default (auto-configured)
447
- debug: false, // Enable debug logging
448
- measurePerformance: false, // Enable performance monitoring
449
- timeoutMs: 30000, // PostMessage timeout (30s default)
450
- retryConfig: {
451
- maxRetries: 3,
452
- delayMs: 1000,
453
- backoffFactor: 2,
251
+ const intent = await bt.tokenIntents.create({
252
+ type: 'card',
253
+ data: {
254
+ number: cardNumberEl,
255
+ expiration_month: expiryEl,
256
+ expiration_year: expiryEl,
257
+ cvc: cvvEl,
454
258
  },
455
- themeMode: 'auto', // 'light' | 'dark' | 'auto'
456
- theme: { /* theme tokens */ },
457
- darkTheme: { /* dark theme tokens */ },
458
259
  });
459
- ```
460
-
461
- ### **Element Options**
462
260
 
463
- ```typescript
464
- interface ElementOptions {
465
- placeholder?: string; // Placeholder text
466
- style?: ElementStyle; // Styling for different states
467
- ariaLabel?: string; // ARIA label for accessibility
468
- disabled?: boolean; // Disable input
469
- readOnly?: boolean; // Make input read-only
470
- }
261
+ const retrieved = await bt.tokenIntents.get(intent.id);
471
262
  ```
472
263
 
473
- ---
474
-
475
- ## 🧪 **Testing**
476
-
477
- ### **Unit Testing**
264
+ ### sessions
478
265
 
479
266
  ```typescript
480
- import { render } from '@testing-library/react';
481
- import BasisTheory from '@basis-theory-elements/sdk';
482
-
483
- test('should create and mount element', async () => {
484
- const { container } = render(<div id="card-number" />);
485
-
486
- const bt = BasisTheory('pk_test_123');
487
- const cardNumber = bt.createElement('cardNumber');
267
+ const session = await bt.sessions.create();
268
+ // Send session.nonce to your backend to authorize
269
+ // Backend returns an apiKey for elevated access
488
270
 
489
- await cardNumber.mount('#card-number');
490
-
491
- expect(cardNumber.mounted).toBe(true);
492
- expect(container.querySelector('iframe')).toBeInTheDocument();
271
+ const token = await bt.tokens.retrieve(tokenId, {
272
+ apiKey: sessionApiKey,
493
273
  });
494
274
  ```
495
275
 
496
- ### **E2E Testing (Cypress)**
497
-
498
- ```typescript
499
- cy.visit('/payment-form.html');
500
-
501
- cy.window().then((win) => {
502
- const bt = win.BasisTheory('pk_test_123');
503
- const cardNumber = bt.createElement('cardNumber');
504
-
505
- return cardNumber.mount('#card-number');
506
- });
507
-
508
- cy.get('iframe').should('exist');
509
- cy.iframe().find('input').type('4111111111111111');
510
- cy.contains('Card Brand: visa');
511
- ```
512
-
513
- ---
514
-
515
- ## 🔒 **Security**
516
-
517
- ### **PCI-DSS Compliance**
518
-
519
- ✅ **Stateless Elements** - No sensitive data persisted in SDK
520
- ✅ **Iframe Isolation** - Payment inputs sandboxed in secure iframes
521
- ✅ **Origin Validation** - PostMessage communication validates sender origin
522
- ✅ **No Direct PAN Access** - SDK never touches raw card numbers
523
- ✅ **Secure Tokenization** - Values retrieved ephemerally from iframe
524
-
525
- ### **Best Practices**
526
-
527
- - ✅ **Use HTTPS** in production
528
- - ✅ **Validate API key** is for correct environment (test vs production)
529
- - ✅ **Don't log sensitive data** (card numbers, CVVs, etc.)
530
- - ✅ **Implement CSP headers** to prevent XSS
531
- - ✅ **Use subresource integrity** for CDN-hosted iframes
532
-
533
- **See [Security Architecture](../../docs/user-guide/security.md) for detailed security measures.**
534
-
535
- ---
536
-
537
- ## 🏗️ **Architecture**
538
-
539
- The v3 SDK is built with **composition over inheritance**:
540
-
541
- ```
542
- @basis-theory-elements/sdk # Public API
543
- ├── BasisTheory.ts # Main SDK factory
544
- ├── elements/
545
- │ ├── CardNumberElement.ts # Card number element
546
- │ ├── CVVElement.ts # CVV element
547
- │ └── ExpiryElement.ts # Expiry date element
548
- ├── mounter/
549
- │ └── ElementMounter.ts # DOM mounting logic
550
- └── utils/
551
- ├── iframe.ts # Iframe creation utilities
552
- └── config.ts # Configuration validation
553
-
554
- @basis-theory-elements/postmessage # Communication layer
555
- ├── client.ts # PostMessage client (parent side)
556
- ├── server.ts # PostMessage server (iframe side)
557
- └── types.ts # Message type definitions
558
-
559
- @basis-theory-elements/validation # Validation logic
560
- ├── validators/
561
- │ ├── card-number.ts # Card number validation
562
- │ ├── cvv.ts # CVV validation
563
- │ └── expiry.ts # Expiry date validation
564
- └── types.ts # Validation type definitions
565
- ```
566
-
567
- **Benefits:**
568
-
569
- - ✅ **Single Responsibility** - Each module does one thing well
570
- - ✅ **Independently Testable** - Test each module in isolation
571
- - ✅ **Reusable** - Other applications can use `@postmessage` or `@validation` without SDK
572
- - ✅ **Clear Dependencies** - No circular references, clean module boundaries
573
-
574
- **See [Coordinator Architecture](../../docs/architecture/coordinator.md) for detailed implementation.**
575
-
576
- ---
577
-
578
- ## 📚 **Documentation**
579
-
580
- - [Quick Start](./README.md) - This document
581
- - [v3 Documentation](../../docs/README.md) - Complete architecture and implementation details
582
- - [v2 to v3 Migration](../../docs/user-guide/migration.md) - Migration guide and improvements
583
-
584
- **Related Packages:**
585
-
586
- - [PostMessage Module](../postmessage/README.md) - Cross-iframe communication
587
- - [Validation Module](../validation/README.md) - Payment data validation
588
-
589
- ---
276
+ ## Theming
590
277
 
591
- ## 🤔 **Why Not Use v2 SDK?**
592
-
593
- The v2 SDK (`@basis-theory/basis-theory-js`) has **accidental complexity**:
594
-
595
- 1. ❌ **Dynamic script loading** - CDN dependency, hard to test
596
- 2. ❌ **Global namespace pollution** - `window.BasisTheoryElements`
597
- 3. ❌ **RxJS dependency** - 35KB+ for simple pub-sub
598
- 4. ❌ **1147-line god class** - `BaseElement` does everything
599
- 5. ❌ **DataElement anti-pattern** - Hidden coordinator iframe
600
- 6. ❌ **Complex initialization** - 10+ async steps to initialize
601
- 7. ❌ **Over-abstraction** - Too many layers for PostMessage
602
-
603
- **v3 fixes all of these:**
604
-
605
- - ✅ **Direct ESM import** - no dynamic loading
606
- - ✅ **Zero global pollution** - standard module imports
607
- - ✅ **Zero RxJS dependency** - native `EventTarget`
608
- - ✅ **Composition over inheritance** - focused modules
609
- - ✅ **No hidden iframes** - SDK client handles coordination
610
- - ✅ **Simple initialization** - one-step, synchronous
611
- - ✅ **Clear separation of concerns** - mounting, messaging, validation are independent
612
-
613
- **See [v2 to v3 Migration](../../docs/user-guide/migration.md) for detailed comparison.**
614
-
615
- ---
616
-
617
- ## 🐛 **Troubleshooting**
618
-
619
- ### **Element Not Mounting**
278
+ Pass design tokens at initialization and switch modes at runtime:
620
279
 
621
280
  ```typescript
622
- // Check if container exists
623
- const container = document.querySelector('#card-number');
624
- if (!container) {
625
- console.error('Container not found');
626
- }
627
-
628
- // Check iframe origin
629
- const bt = BasisTheory('pk_test_123', {
630
- iframeOrigin: 'https://elements.basistheory.com', // Must be correct
281
+ const bt = BasisTheory('pk_test_...', {
282
+ themeMode: 'auto',
283
+ theme: {
284
+ colors: {
285
+ primary: '#007bff',
286
+ error: '#dc3545',
287
+ success: '#28a745',
288
+ text: { primary: '#1a1a1a', placeholder: '#6c757d' },
289
+ background: { default: '#ffffff' },
290
+ border: { default: '#dee2e6', focus: '#007bff' },
291
+ },
292
+ typography: {
293
+ fontFamily: 'Inter, sans-serif',
294
+ fontSize: { base: '16px' },
295
+ fontWeight: { normal: '400' },
296
+ },
297
+ spacing: { sm: '8px', md: '12px', lg: '16px' },
298
+ borders: {
299
+ radius: { base: '6px' },
300
+ width: { base: '1px' },
301
+ },
302
+ },
631
303
  });
632
304
 
633
- // Enable debug logging
634
- const bt = BasisTheory('pk_test_123', {
635
- debug: true, // See PostMessage logs in console
636
- });
305
+ // Switch theme mode at runtime
306
+ await bt.updateThemeMode('dark');
637
307
  ```
638
308
 
639
- ### **PostMessage Timeout**
309
+ ## TypeScript
640
310
 
641
- ```typescript
642
- // Increase timeout for slow networks
643
- const bt = BasisTheory('pk_test_123', {
644
- timeoutMs: 10000, // 10 seconds
645
- });
646
- ```
647
-
648
- ### **CORS Errors**
311
+ The package ships full type definitions. Key exports:
649
312
 
650
313
  ```typescript
651
- // Ensure iframe origin matches server
652
- const bt = BasisTheory('pk_test_123', {
653
- iframeOrigin: 'https://elements.basistheory.com', // Must match server
654
- });
314
+ import type {
315
+ BasisTheorySDK,
316
+ Element,
317
+ ElementType,
318
+ ElementOptions,
319
+ SDKOptions,
320
+ ChangeEventDetail,
321
+ CardNumberChangeEventDetail,
322
+ ReadyEventDetail,
323
+ ErrorEventDetail,
324
+ ErrorCode,
325
+ ValidationError,
326
+ TokenizeResult,
327
+ Session,
328
+ } from '@basis-theory/web-elements';
655
329
  ```
656
330
 
657
- ---
658
-
659
- ## 📄 **License**
331
+ ## Documentation
660
332
 
661
- MIT License - see [LICENSE](../../LICENSE) for details
333
+ Full documentation: [https://developers.basistheory.com/docs/sdks/web/web-elements/](https://developers.basistheory.com/docs/sdks/web/web-elements/)
662
334
 
663
- ---
335
+ - [Getting Started](https://developers.basistheory.com/docs/sdks/web/web-elements/v3/getting-started)
336
+ - [Element Types](https://developers.basistheory.com/docs/sdks/web/web-elements/v3/components)
337
+ - [Events](https://developers.basistheory.com/docs/sdks/web/web-elements/v3/events)
338
+ - [Services](https://developers.basistheory.com/docs/sdks/web/web-elements/v3/services)
339
+ - [Theming](https://developers.basistheory.com/docs/sdks/web/web-elements/v3/theming)
340
+ - [Migration Guide (v2 to v3)](https://developers.basistheory.com/docs/sdks/web/web-elements/v3/migration)
664
341
 
665
- ## 🤝 **Support**
342
+ ## License
666
343
 
667
- - 📖 **Documentation**: [v3 Docs](../../docs/README.md)
668
- - 🐛 **Issues**: [GitHub Issues](https://github.com/basis-theory/elements/issues)
669
- - 💬 **Community**: [Slack](https://community.basistheory.com)
344
+ Apache-2.0