@connect-xyz/withdraw-js 0.34.0 → 0.36.0
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/README.md +15 -0
- package/dist/index.d.ts +244 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -68,6 +68,9 @@ const withdraw = new Withdraw({
|
|
|
68
68
|
onEvent: ({ type, data }) => {
|
|
69
69
|
console.log('Event received:', type, data);
|
|
70
70
|
},
|
|
71
|
+
onLoaded: () => {
|
|
72
|
+
console.log('Withdraw widget loaded and ready');
|
|
73
|
+
},
|
|
71
74
|
});
|
|
72
75
|
|
|
73
76
|
// Render the widget to a container element
|
|
@@ -101,6 +104,7 @@ withdraw.destroy();
|
|
|
101
104
|
| `onClose` | `() => void` | No | - | Callback when the widget is closed |
|
|
102
105
|
| `onWithdrawal` | `({ data }) => void` | No | - | Callback for withdrawal completed |
|
|
103
106
|
| `onEvent` | `({ type, data }) => void` | No | - | Callback for general events |
|
|
107
|
+
| `onLoaded` | `() => void` | No | - | Callback when the widget is loaded and ready |
|
|
104
108
|
|
|
105
109
|
### Constructor
|
|
106
110
|
|
|
@@ -119,6 +123,7 @@ Creates a new Withdraw instance with the provided configuration.
|
|
|
119
123
|
- `onClose` (function, optional): Close callback
|
|
120
124
|
- `onWithdrawal` (function, optional): Withdrawal callback
|
|
121
125
|
- `onEvent` (function, optional): General event callback
|
|
126
|
+
- `onLoaded` (function, optional): Callback when widget is loaded and ready
|
|
122
127
|
|
|
123
128
|
### Methods
|
|
124
129
|
|
|
@@ -214,6 +219,16 @@ onEvent: ({ type, data }) => {
|
|
|
214
219
|
};
|
|
215
220
|
```
|
|
216
221
|
|
|
222
|
+
### onLoaded
|
|
223
|
+
|
|
224
|
+
Called when the Withdraw widget has fully loaded and is ready for user interaction. This callback is useful for showing loading states or performing actions once the widget is initialized.
|
|
225
|
+
|
|
226
|
+
```javascript
|
|
227
|
+
onLoaded: () => {
|
|
228
|
+
// Widget is fully loaded and ready
|
|
229
|
+
};
|
|
230
|
+
```
|
|
231
|
+
|
|
217
232
|
## Browser Support
|
|
218
233
|
|
|
219
234
|
- Chrome/Edge 90+
|
package/dist/index.d.ts
CHANGED
|
@@ -32,11 +32,13 @@ declare interface BaseConfig<TEvent = AppEvent> extends CommonCallbacks<TEvent>
|
|
|
32
32
|
* JWT token used for authentication
|
|
33
33
|
*/
|
|
34
34
|
jwt: string;
|
|
35
|
+
|
|
35
36
|
/**
|
|
36
37
|
* Target environment
|
|
37
38
|
* @default 'production'
|
|
38
39
|
*/
|
|
39
40
|
env?: Environment;
|
|
41
|
+
|
|
40
42
|
/**
|
|
41
43
|
* Theme mode
|
|
42
44
|
* @default 'auto'
|
|
@@ -49,54 +51,259 @@ declare interface BaseConfig<TEvent = AppEvent> extends CommonCallbacks<TEvent>
|
|
|
49
51
|
theme?: Theme;
|
|
50
52
|
}
|
|
51
53
|
|
|
52
|
-
declare type BaseErrorMessageKeys =
|
|
54
|
+
declare type BaseErrorMessageKeys =
|
|
55
|
+
| 'ALREADY_RENDERED'
|
|
56
|
+
| 'NOT_RENDERED'
|
|
57
|
+
| 'INVALID_CONTAINER'
|
|
58
|
+
| 'SCRIPT_LOAD_FAILED'
|
|
59
|
+
| 'WEB_COMPONENT_NOT_DEFINED';
|
|
53
60
|
|
|
54
61
|
declare abstract class BaseJsSdk<Config extends BaseConfig<never> = BaseConfig> {
|
|
55
|
-
private config;
|
|
56
|
-
private state;
|
|
57
|
-
private scriptLoadingPromise
|
|
62
|
+
private config: Config;
|
|
63
|
+
private state: SdkState;
|
|
64
|
+
private scriptLoadingPromise?: Promise<void>;
|
|
65
|
+
|
|
58
66
|
protected abstract errorMessages: Record<BaseErrorMessageKeys, string>;
|
|
59
67
|
protected abstract scriptUrls: Record<string, string>;
|
|
60
68
|
protected abstract webComponentTag: string;
|
|
61
|
-
|
|
69
|
+
|
|
70
|
+
constructor(config: Config) {
|
|
71
|
+
if (!config.jwt || typeof config.jwt !== 'string') {
|
|
72
|
+
throw new Error(INVALID_JWT_ERROR_MESSAGE);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
this.config = {
|
|
76
|
+
...config,
|
|
77
|
+
env: config.env || DEFAULT_ENVIRONMENT,
|
|
78
|
+
theme: config.theme,
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
this.state = {
|
|
82
|
+
initialized: false,
|
|
83
|
+
scriptLoaded: false,
|
|
84
|
+
container: null,
|
|
85
|
+
element: null,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
62
89
|
/**
|
|
63
90
|
* Render the widget to a container element
|
|
64
91
|
* @param container - The container element to render the widget into
|
|
65
92
|
* @returns Promise that resolves when the widget is rendered
|
|
66
93
|
*/
|
|
67
|
-
render(container: HTMLElement): Promise<void
|
|
94
|
+
async render(container: HTMLElement): Promise<void> {
|
|
95
|
+
// Validate container
|
|
96
|
+
if (!container || !(container instanceof HTMLElement)) {
|
|
97
|
+
throw new Error(this.errorMessages.INVALID_CONTAINER);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Check if already rendered
|
|
101
|
+
if (this.state.initialized) {
|
|
102
|
+
throw new Error(this.errorMessages.ALREADY_RENDERED);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
// Ensure script is loaded
|
|
107
|
+
await this.ensureScriptLoaded();
|
|
108
|
+
|
|
109
|
+
// Create and append the web component
|
|
110
|
+
const element = this.createWebComponent();
|
|
111
|
+
|
|
112
|
+
// Clear the container and append the element
|
|
113
|
+
container.innerHTML = '';
|
|
114
|
+
container.appendChild(element);
|
|
115
|
+
|
|
116
|
+
// Update state
|
|
117
|
+
this.state.container = container;
|
|
118
|
+
this.state.element = element;
|
|
119
|
+
this.state.initialized = true;
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.error('Failed to render widget:', error);
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
68
126
|
/**
|
|
69
127
|
* Update the configuration of the widget
|
|
70
128
|
* @param config - Partial configuration to update
|
|
71
129
|
* @returns void
|
|
72
130
|
*/
|
|
73
|
-
updateConfig(config: Partial<Config>): void
|
|
131
|
+
updateConfig(config: Partial<Config>): void {
|
|
132
|
+
if (!this.state.initialized || !this.state.element) {
|
|
133
|
+
throw new Error(this.errorMessages.NOT_RENDERED);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const element = this.state.element as SdkElement<Config>;
|
|
137
|
+
|
|
138
|
+
Object.entries(config).forEach(([key, value]) => {
|
|
139
|
+
if (value) {
|
|
140
|
+
this.config[key as keyof Config] = value;
|
|
141
|
+
element[key as keyof Config] = value;
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
74
146
|
/**
|
|
75
147
|
* Destroy the widget and clean up resources
|
|
76
148
|
*/
|
|
77
|
-
destroy(): void
|
|
149
|
+
destroy(): void {
|
|
150
|
+
if (!this.state.initialized) {
|
|
151
|
+
return; // Nothing to destroy
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Remove the element from the container
|
|
155
|
+
if (this.state.element && this.state.element.parentNode) {
|
|
156
|
+
this.state.element.parentNode.removeChild(this.state.element);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Clear the container
|
|
160
|
+
if (this.state.container) {
|
|
161
|
+
this.state.container.innerHTML = '';
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Reset state
|
|
165
|
+
this.state.container = null;
|
|
166
|
+
this.state.element = null;
|
|
167
|
+
this.state.initialized = false;
|
|
168
|
+
}
|
|
169
|
+
|
|
78
170
|
/**
|
|
79
171
|
* Check if the widget is currently rendered
|
|
80
172
|
* @returns True if the widget is rendered, false otherwise
|
|
81
173
|
*/
|
|
82
|
-
isRendered(): boolean
|
|
174
|
+
isRendered(): boolean {
|
|
175
|
+
return this.state.initialized;
|
|
176
|
+
}
|
|
177
|
+
|
|
83
178
|
/**
|
|
84
179
|
* Get the current configuration
|
|
85
180
|
* @returns The current configuration object
|
|
86
181
|
*/
|
|
87
|
-
getConfig(): Readonly<Config
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
private
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
182
|
+
getConfig(): Readonly<Config> {
|
|
183
|
+
return { ...this.config };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private getEnvironment(): Environment {
|
|
187
|
+
return this.config.env || DEFAULT_ENVIRONMENT;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private getScriptId() {
|
|
191
|
+
return `${this.webComponentTag}-script-${this.getEnvironment()}`;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
private isScriptLoaded() {
|
|
195
|
+
return !!document.getElementById(this.getScriptId());
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private getWebComponent() {
|
|
199
|
+
return customElements.get(this.webComponentTag);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private getScriptUrl() {
|
|
203
|
+
// Support local development with Vite
|
|
204
|
+
if (typeof import.meta !== 'undefined' && import.meta.env?.['VITE_INTERNAL_BUILD'] === 'true') {
|
|
205
|
+
return import.meta.env['VITE_SCRIPT_URL'] || this.scriptUrls[this.getEnvironment()];
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return this.scriptUrls[this.getEnvironment()];
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
private async loadScript() {
|
|
212
|
+
if (this.isScriptLoaded()) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (this.scriptLoadingPromise) {
|
|
217
|
+
return this.scriptLoadingPromise;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
this.scriptLoadingPromise = new Promise<void>((resolve, reject) => {
|
|
221
|
+
const script = document.createElement('script');
|
|
222
|
+
script.id = this.getScriptId();
|
|
223
|
+
script.src = this.getScriptUrl();
|
|
224
|
+
script.type = 'module';
|
|
225
|
+
script.async = true;
|
|
226
|
+
|
|
227
|
+
script.onload = () => {
|
|
228
|
+
setTimeout(() => {
|
|
229
|
+
if (this.getWebComponent()) {
|
|
230
|
+
resolve();
|
|
231
|
+
} else {
|
|
232
|
+
reject(new Error(this.errorMessages.WEB_COMPONENT_NOT_DEFINED));
|
|
233
|
+
}
|
|
234
|
+
}, 0);
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
script.onerror = () => {
|
|
238
|
+
this.scriptLoadingPromise = undefined;
|
|
239
|
+
reject(new Error(`${this.errorMessages.SCRIPT_LOAD_FAILED} (${this.getEnvironment()})`));
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
document.head.appendChild(script);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
try {
|
|
246
|
+
await this.scriptLoadingPromise;
|
|
247
|
+
} catch (error) {
|
|
248
|
+
this.scriptLoadingPromise = undefined;
|
|
249
|
+
throw error;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return this.scriptLoadingPromise;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
private async waitForWebComponent(timeout = 5000) {
|
|
256
|
+
if (this.getWebComponent()) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return new Promise<void>((resolve, reject) => {
|
|
261
|
+
const timeoutId = setTimeout(() => {
|
|
262
|
+
reject(new Error(`Timeout waiting for ${this.webComponentTag} to be defined`));
|
|
263
|
+
}, timeout);
|
|
264
|
+
|
|
265
|
+
customElements
|
|
266
|
+
.whenDefined(this.webComponentTag)
|
|
267
|
+
.then(() => {
|
|
268
|
+
clearTimeout(timeoutId);
|
|
269
|
+
resolve();
|
|
270
|
+
})
|
|
271
|
+
.catch((error) => {
|
|
272
|
+
clearTimeout(timeoutId);
|
|
273
|
+
reject(error);
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
95
278
|
/**
|
|
96
279
|
* Load the web component script if not already loaded
|
|
97
280
|
*/
|
|
98
|
-
protected ensureScriptLoaded(): Promise<void
|
|
99
|
-
|
|
281
|
+
protected async ensureScriptLoaded(): Promise<void> {
|
|
282
|
+
if (this.state.scriptLoaded) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
try {
|
|
287
|
+
await this.loadScript();
|
|
288
|
+
await this.waitForWebComponent();
|
|
289
|
+
this.state.scriptLoaded = true;
|
|
290
|
+
} catch (error) {
|
|
291
|
+
console.error('Failed to load Connect script:', error);
|
|
292
|
+
throw error;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
private createWebComponent() {
|
|
297
|
+
const element = document.createElement(this.webComponentTag) as SdkElement<Config>;
|
|
298
|
+
|
|
299
|
+
Object.entries(this.config).forEach(([key, value]) => {
|
|
300
|
+
if (value) {
|
|
301
|
+
element[key as keyof Config] = value;
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
return element;
|
|
306
|
+
}
|
|
100
307
|
}
|
|
101
308
|
|
|
102
309
|
/**
|
|
@@ -110,6 +317,8 @@ declare type CommonCallbacks<TEvent = AppEvent> = {
|
|
|
110
317
|
onClose?: () => void;
|
|
111
318
|
/** Called when a general event occurs */
|
|
112
319
|
onEvent?: (event: TEvent) => void;
|
|
320
|
+
/** Called when the widget has loaded and is ready */
|
|
321
|
+
onLoaded?: () => void;
|
|
113
322
|
};
|
|
114
323
|
|
|
115
324
|
export declare type ConnectWithdrawElement = SdkElement<WithdrawConfig>;
|
|
@@ -154,6 +363,20 @@ declare type ErrorPayload = {
|
|
|
154
363
|
*/
|
|
155
364
|
declare type SdkElement<Config extends BaseConfig<never>> = HTMLElement & Config;
|
|
156
365
|
|
|
366
|
+
/**
|
|
367
|
+
* Internal state of the SDK
|
|
368
|
+
*/
|
|
369
|
+
declare interface SdkState {
|
|
370
|
+
/** Whether the SDK is initialized */
|
|
371
|
+
initialized: boolean;
|
|
372
|
+
/** Whether the web component script is loaded */
|
|
373
|
+
scriptLoaded: boolean;
|
|
374
|
+
/** The container element where the widget is rendered */
|
|
375
|
+
container: HTMLElement | null;
|
|
376
|
+
/** The web component element */
|
|
377
|
+
element: HTMLElement | null;
|
|
378
|
+
}
|
|
379
|
+
|
|
157
380
|
/**
|
|
158
381
|
* Theme configuration for the SDK
|
|
159
382
|
*
|
|
@@ -175,7 +398,8 @@ declare type Theme = 'auto' | 'light' | 'dark';
|
|
|
175
398
|
* onError: ({ error, reason }) => console.error(error, reason),
|
|
176
399
|
* onClose: () => console.log('Withdraw closed'),
|
|
177
400
|
* onWithdrawal: ({ data }) => console.log('Withdrawal', data),
|
|
178
|
-
* onEvent: ({ type, data }) => console.log('Event', type, data)
|
|
401
|
+
* onEvent: ({ type, data }) => console.log('Event', type, data),
|
|
402
|
+
* onLoaded: () => console.log('Withdraw widget loaded and ready')
|
|
179
403
|
* });
|
|
180
404
|
*
|
|
181
405
|
* // Render to a container
|