@notabene/javascript-sdk 2.0.0-next.9 → 2.0.1
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/LICENSE.md +21 -0
- package/README.md +477 -111
- package/dist/js/notabene.js +1 -0
- package/dist/notabene.cjs +1 -1
- package/dist/notabene.js +286 -110
- package/package.json +34 -25
- package/src/__tests__/notabene.test.ts +113 -2
- package/src/arbitraries.ts +0 -13
- package/src/components/EmbeddedComponent.ts +125 -63
- package/src/components/__tests__/EmbeddedComponent.test.ts +402 -32
- package/src/ivms/types.ts +177 -140
- package/src/locales.ts +48 -0
- package/src/notabene.ts +199 -63
- package/src/types.ts +773 -150
- package/src/utils/MessageEventManager.ts +70 -9
- package/src/utils/__tests__/MessageEventManager.test.ts +29 -5
- package/src/utils/__tests__/urls.test.ts +112 -0
- package/src/utils/arbitraries.ts +219 -1
- package/src/utils/urls.ts +30 -5
- package/dist/notabene.d.ts +0 -780
- package/dist/tsdoc-metadata.json +0 -11
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import { fc, test } from '@fast-check/vitest';
|
|
2
2
|
import { describe } from 'vitest';
|
|
3
|
-
import { abitraryCallbackOptions, arbitraryComponent } from '../arbitraries';
|
|
4
3
|
import Notabene from '../notabene';
|
|
5
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
abitraryCallbackOptions,
|
|
6
|
+
arbitraryComponent,
|
|
7
|
+
arbitraryConnectionOptions,
|
|
8
|
+
arbitraryConnectionRequest,
|
|
9
|
+
arbitraryDepositRequest,
|
|
10
|
+
arbitraryDepositRequestOptions,
|
|
11
|
+
arbitraryLocale,
|
|
12
|
+
arbitraryTransactionOptions,
|
|
13
|
+
arbitraryWithdrawal,
|
|
14
|
+
} from '../utils/arbitraries';
|
|
6
15
|
|
|
7
16
|
describe('Notabene', () => {
|
|
8
17
|
describe('defaults', () => {
|
|
@@ -156,4 +165,106 @@ describe('Notabene', () => {
|
|
|
156
165
|
},
|
|
157
166
|
);
|
|
158
167
|
});
|
|
168
|
+
|
|
169
|
+
describe('createWithdrawalAssist', () => {
|
|
170
|
+
test.prop([
|
|
171
|
+
fc.string({ minLength: 3 }), // authToken
|
|
172
|
+
fc.webUrl(), // nodeUrl
|
|
173
|
+
arbitraryWithdrawal(),
|
|
174
|
+
arbitraryTransactionOptions(), // options
|
|
175
|
+
abitraryCallbackOptions(), // callbacks
|
|
176
|
+
])(
|
|
177
|
+
'Should create withdrawal assist component with correct URL',
|
|
178
|
+
(authToken, nodeUrl, value, options, callbacks) => {
|
|
179
|
+
const notabene = new Notabene({
|
|
180
|
+
nodeUrl,
|
|
181
|
+
authToken,
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const component = notabene.createWithdrawalAssist(
|
|
185
|
+
value,
|
|
186
|
+
options,
|
|
187
|
+
callbacks,
|
|
188
|
+
);
|
|
189
|
+
const url = new URL(component.url);
|
|
190
|
+
|
|
191
|
+
return (
|
|
192
|
+
url.pathname === '/withdrawal-assist' &&
|
|
193
|
+
url.hash ===
|
|
194
|
+
`#authToken=${encodeURIComponent(authToken)}&value=${encodeURIComponent(JSON.stringify(value))}&configuration=${encodeURIComponent(JSON.stringify(options))}` &&
|
|
195
|
+
url.searchParams.get('nodeUrl') === nodeUrl &&
|
|
196
|
+
url.searchParams.get('callback_url') === callbacks.callback &&
|
|
197
|
+
url.searchParams.get('redirect_uri') === callbacks.redirectUri
|
|
198
|
+
);
|
|
199
|
+
},
|
|
200
|
+
);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
describe('createDepositRequest', () => {
|
|
204
|
+
test.prop([
|
|
205
|
+
fc.string({ minLength: 3 }), // authToken
|
|
206
|
+
fc.webUrl(), // nodeUrl
|
|
207
|
+
arbitraryDepositRequest(),
|
|
208
|
+
arbitraryDepositRequestOptions(), // options
|
|
209
|
+
abitraryCallbackOptions(), // callbacks
|
|
210
|
+
])(
|
|
211
|
+
'Should create deposit assist component with correct URL',
|
|
212
|
+
(authToken, nodeUrl, value, options, callbacks) => {
|
|
213
|
+
const notabene = new Notabene({
|
|
214
|
+
nodeUrl,
|
|
215
|
+
authToken,
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
const component = notabene.createDepositRequest(
|
|
219
|
+
value,
|
|
220
|
+
options,
|
|
221
|
+
callbacks,
|
|
222
|
+
);
|
|
223
|
+
const url = new URL(component.url);
|
|
224
|
+
|
|
225
|
+
return (
|
|
226
|
+
url.pathname === '/deposit-request' &&
|
|
227
|
+
url.hash ===
|
|
228
|
+
`#authToken=${encodeURIComponent(authToken)}&value=${encodeURIComponent(JSON.stringify(value))}&configuration=${encodeURIComponent(JSON.stringify(options))}` &&
|
|
229
|
+
url.searchParams.get('nodeUrl') === nodeUrl &&
|
|
230
|
+
url.searchParams.get('callback_url') === callbacks.callback &&
|
|
231
|
+
url.searchParams.get('redirect_uri') === callbacks.redirectUri
|
|
232
|
+
);
|
|
233
|
+
},
|
|
234
|
+
);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
describe('createConnectWallet', () => {
|
|
238
|
+
test.prop([
|
|
239
|
+
fc.string({ minLength: 3 }), // authToken
|
|
240
|
+
fc.webUrl(), // nodeUrl
|
|
241
|
+
arbitraryConnectionRequest(),
|
|
242
|
+
arbitraryConnectionOptions(), // options
|
|
243
|
+
abitraryCallbackOptions(), // callbacks
|
|
244
|
+
])(
|
|
245
|
+
'Should create connect wallet component with correct URL',
|
|
246
|
+
(authToken, nodeUrl, value, options, callbacks) => {
|
|
247
|
+
const notabene = new Notabene({
|
|
248
|
+
nodeUrl,
|
|
249
|
+
authToken,
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
const component = notabene.createConnectWallet(
|
|
253
|
+
value,
|
|
254
|
+
options,
|
|
255
|
+
callbacks,
|
|
256
|
+
);
|
|
257
|
+
const url = new URL(component.url);
|
|
258
|
+
|
|
259
|
+
return (
|
|
260
|
+
url.pathname === '/connect' &&
|
|
261
|
+
url.hash ===
|
|
262
|
+
`#authToken=${encodeURIComponent(authToken)}&value=${encodeURIComponent(JSON.stringify(value))}&configuration=${encodeURIComponent(JSON.stringify(options))}` &&
|
|
263
|
+
url.searchParams.get('nodeUrl') === nodeUrl &&
|
|
264
|
+
url.searchParams.get('callback_url') === callbacks.callback &&
|
|
265
|
+
url.searchParams.get('redirect_uri') === callbacks.redirectUri
|
|
266
|
+
);
|
|
267
|
+
},
|
|
268
|
+
);
|
|
269
|
+
});
|
|
159
270
|
});
|
package/src/arbitraries.ts
CHANGED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import fc from 'fast-check';
|
|
2
|
-
import { CallbackOptions } from './types';
|
|
3
|
-
|
|
4
|
-
const COMPONENT_MATCHER = /^[a-z0-9-_]+$/;
|
|
5
|
-
// Arbitraries
|
|
6
|
-
export const arbitraryComponent = (): fc.Arbitrary<string> =>
|
|
7
|
-
fc.stringMatching(COMPONENT_MATCHER);
|
|
8
|
-
|
|
9
|
-
export const abitraryCallbackOptions = (): fc.Arbitrary<CallbackOptions> =>
|
|
10
|
-
fc.record({
|
|
11
|
-
callback: fc.webUrl(),
|
|
12
|
-
redirectUri: fc.webUrl(),
|
|
13
|
-
});
|
|
@@ -2,7 +2,6 @@ import {
|
|
|
2
2
|
CMType,
|
|
3
3
|
HMType,
|
|
4
4
|
HostMessage,
|
|
5
|
-
Transaction,
|
|
6
5
|
TransactionResponse,
|
|
7
6
|
ValidationError,
|
|
8
7
|
} from '../types';
|
|
@@ -15,25 +14,32 @@ import {
|
|
|
15
14
|
* An embedded Notabene component
|
|
16
15
|
* @public
|
|
17
16
|
*/
|
|
18
|
-
|
|
17
|
+
|
|
18
|
+
export default class EmbeddedComponent<V, O> {
|
|
19
19
|
private _url: string;
|
|
20
|
-
private _value: Partial<
|
|
20
|
+
private _value: Partial<V>;
|
|
21
|
+
private _options?: O;
|
|
21
22
|
private _errors: ValidationError[] = [];
|
|
22
23
|
|
|
23
24
|
private iframe?: HTMLIFrameElement;
|
|
24
|
-
private eventManager: MessageEventManager
|
|
25
|
-
private ready = false;
|
|
25
|
+
private eventManager: MessageEventManager<V, O>;
|
|
26
26
|
private modal?: HTMLDialogElement;
|
|
27
|
-
// private popup?: HTMLElement;
|
|
28
27
|
|
|
29
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Creates an instance of EmbeddedComponent.
|
|
30
|
+
* @param url - The URL of the embedded component
|
|
31
|
+
* @param value - The initial transaction value
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
constructor(url: string, value: Partial<V>, options?: O) {
|
|
30
35
|
this._url = url;
|
|
31
36
|
this._value = value;
|
|
32
|
-
this.
|
|
37
|
+
this._options = options;
|
|
38
|
+
this.eventManager = new MessageEventManager<V, O>();
|
|
33
39
|
this.on(CMType.INVALID, (message) => {
|
|
34
40
|
if (message.type === CMType.INVALID) {
|
|
35
41
|
this._errors = message.errors;
|
|
36
|
-
this._value = message.value as
|
|
42
|
+
this._value = message.value as V;
|
|
37
43
|
}
|
|
38
44
|
});
|
|
39
45
|
this.on(CMType.RESIZE, (message) => {
|
|
@@ -42,131 +48,187 @@ export default class EmbeddedComponent {
|
|
|
42
48
|
}
|
|
43
49
|
});
|
|
44
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Gets the URL of the embedded component
|
|
53
|
+
* @returns The URL of the embedded component
|
|
54
|
+
*/
|
|
45
55
|
|
|
46
56
|
public get url(): string {
|
|
47
57
|
return this._url;
|
|
48
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Gets the current transaction value
|
|
61
|
+
* @returns The current transaction value
|
|
62
|
+
*/
|
|
49
63
|
|
|
50
|
-
public get value(): Partial<
|
|
64
|
+
public get value(): Partial<V> {
|
|
51
65
|
return this._value;
|
|
52
66
|
}
|
|
53
67
|
|
|
68
|
+
public get options(): O | undefined {
|
|
69
|
+
return this._options;
|
|
70
|
+
}
|
|
71
|
+
|
|
54
72
|
public get errors(): ValidationError[] {
|
|
55
73
|
return this._errors;
|
|
56
74
|
}
|
|
57
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Opens the component URL in the current window
|
|
78
|
+
*/
|
|
79
|
+
|
|
58
80
|
public open() {
|
|
59
81
|
document.location.href = this.url;
|
|
60
82
|
}
|
|
61
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Mounts the component to a parent element
|
|
86
|
+
* @param parentId - The ID of the parent element
|
|
87
|
+
* @throws Will throw an error if the parent element is not found
|
|
88
|
+
*/
|
|
89
|
+
|
|
62
90
|
mount(parentId: string) {
|
|
63
91
|
const parent = document.querySelector(parentId);
|
|
64
92
|
if (!parent) throw new Error(`parentID ${parentId} not found`);
|
|
65
93
|
this.embed(parent);
|
|
66
94
|
}
|
|
67
95
|
|
|
68
|
-
|
|
96
|
+
/**
|
|
97
|
+
* Embeds the component into a parent element
|
|
98
|
+
* @param parent - The parent element to embed the component into
|
|
99
|
+
*/
|
|
100
|
+
embed(parent: Element, modal: boolean = false) {
|
|
101
|
+
// console.log(this.url);
|
|
102
|
+
this.removeEmbed();
|
|
69
103
|
this.iframe = document.createElement('iframe');
|
|
70
|
-
this.iframe.src = this.url;
|
|
104
|
+
this.iframe.src = this.url + (!modal ? '&embedded=true' : '');
|
|
105
|
+
this.iframe.allow = 'web-share; clipboard-write';
|
|
71
106
|
this.iframe.style.width = '100%';
|
|
72
|
-
this.iframe.style.height = '100px';
|
|
73
107
|
this.iframe.style.border = 'none';
|
|
74
108
|
this.iframe.style.overflow = 'hidden';
|
|
75
|
-
this.iframe.
|
|
76
|
-
this.iframe.
|
|
109
|
+
this.iframe.scrolling = 'no';
|
|
110
|
+
// this.iframe.setAttribute('allowtransparency', 'true');
|
|
111
|
+
// this.iframe.style.backgroundColor = 'transparent';
|
|
77
112
|
parent.appendChild(this.iframe);
|
|
78
113
|
|
|
79
114
|
window.addEventListener('message', (event: MessageEvent) => {
|
|
80
115
|
if (event.source !== this.iframe?.contentWindow) {
|
|
81
116
|
return;
|
|
82
117
|
}
|
|
83
|
-
console.log('received message from iframe', event.data);
|
|
118
|
+
// console.log('received message from iframe', event.data);
|
|
84
119
|
this.eventManager?.setPort(event.ports[0]);
|
|
85
|
-
this.ready = true;
|
|
86
120
|
});
|
|
87
121
|
|
|
88
122
|
this.iframe?.contentWindow?.focus();
|
|
89
123
|
}
|
|
90
124
|
|
|
91
|
-
|
|
125
|
+
removeEmbed() {
|
|
126
|
+
if (this.iframe) this.iframe.remove();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Sends a message to the embedded component
|
|
131
|
+
* @param message - The message to send
|
|
132
|
+
*/
|
|
133
|
+
send(message: HostMessage<V, O>): void {
|
|
92
134
|
this.eventManager.send(message);
|
|
93
135
|
}
|
|
94
136
|
|
|
95
|
-
|
|
96
|
-
|
|
137
|
+
/**
|
|
138
|
+
* Adds an event listener for a specific message type
|
|
139
|
+
* @param messageType - The type of message to listen for
|
|
140
|
+
* @param callback - The callback function to execute when the message is received
|
|
141
|
+
*/
|
|
142
|
+
|
|
143
|
+
on(messageType: string, callback: MessageCallback<V>): () => void {
|
|
144
|
+
return this.eventManager.on(messageType, callback);
|
|
97
145
|
}
|
|
98
146
|
|
|
99
|
-
|
|
147
|
+
/**
|
|
148
|
+
* Removes an event listener for a specific message type
|
|
149
|
+
* @param messageType - The type of message to stop listening for
|
|
150
|
+
* @param callback - The callback function to remove
|
|
151
|
+
*/
|
|
152
|
+
|
|
153
|
+
off(messageType: string, callback: MessageCallback<V>): void {
|
|
100
154
|
this.eventManager.off(messageType, callback);
|
|
101
155
|
}
|
|
102
156
|
|
|
103
|
-
|
|
157
|
+
/**
|
|
158
|
+
* Updates the transaction value and sends an update message to the component
|
|
159
|
+
* @param value - The new transaction value
|
|
160
|
+
*/
|
|
161
|
+
update(value: Partial<V>, options?: O) {
|
|
104
162
|
this._value = value;
|
|
105
|
-
|
|
163
|
+
if (options) this._options = options;
|
|
164
|
+
this.send({ type: HMType.UPDATE, value, options: this._options });
|
|
106
165
|
}
|
|
107
166
|
|
|
108
|
-
|
|
167
|
+
/**
|
|
168
|
+
* Waits for the component to complete and returns the transaction response
|
|
169
|
+
* @returns A promise that resolves with the transaction response
|
|
170
|
+
*/
|
|
171
|
+
|
|
172
|
+
completion(): Promise<TransactionResponse<V>> {
|
|
109
173
|
return new Promise((resolve, reject) => {
|
|
110
|
-
|
|
111
|
-
|
|
174
|
+
let removeComplete: undefined | (() => void) = undefined;
|
|
175
|
+
let removeError: undefined | (() => void) = undefined;
|
|
176
|
+
function cleanup() {
|
|
177
|
+
if (removeComplete) removeComplete();
|
|
178
|
+
if (removeError) removeError();
|
|
179
|
+
}
|
|
180
|
+
removeComplete = this.on(CMType.COMPLETE, (message) => {
|
|
112
181
|
resolve((message as any).response);
|
|
182
|
+
cleanup();
|
|
113
183
|
});
|
|
114
|
-
this.on('error', (message) => {
|
|
184
|
+
removeError = this.on('error', (message) => {
|
|
115
185
|
reject(new Error((message as any).message));
|
|
186
|
+
cleanup();
|
|
116
187
|
});
|
|
117
188
|
});
|
|
118
189
|
}
|
|
119
190
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
// }
|
|
191
|
+
/**
|
|
192
|
+
* Opens the component in a modal dialog
|
|
193
|
+
*/
|
|
124
194
|
|
|
125
|
-
openModal():
|
|
195
|
+
openModal(): Promise<TransactionResponse<V>> {
|
|
126
196
|
if (this.modal) {
|
|
127
197
|
this.closeModal();
|
|
128
198
|
}
|
|
129
199
|
this.modal = document.createElement('dialog');
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
// close.style.fontSize = '1rem';
|
|
138
|
-
// close.style.padding = '0.25rem 0.5rem';
|
|
139
|
-
// close.style.display = 'block';
|
|
140
|
-
// close.style.background = 'white';
|
|
141
|
-
// close.style.color = '#aaa';
|
|
142
|
-
// close.style.border = '#aaa';
|
|
143
|
-
// close.addEventListener('click', () => this.closeModal());
|
|
144
|
-
// this.modal.appendChild(close);
|
|
145
|
-
// this.modal.css;
|
|
146
|
-
// this.modal.style.position = 'fixed';
|
|
147
|
-
// this.modal.style.bottom = '0';
|
|
148
|
-
// this.modal.style.left = '0';
|
|
149
|
-
// this.modal.style.right = '0';
|
|
150
|
-
// this.modal.style.top = '0';
|
|
151
|
-
// this.modal.style.left = '0';
|
|
152
|
-
// this.modal.style.width = '100%';
|
|
153
|
-
// this.modal.style.height = '100%';
|
|
154
|
-
// this.modal.style.backgroundColor = 'transparent';
|
|
155
|
-
// this.modal.style.display = 'flex';
|
|
156
|
-
// this.modal.style.justifyContent = 'center';
|
|
157
|
-
// this.modal.style.alignItems = 'center';
|
|
200
|
+
this.modal.style.border = 'none';
|
|
201
|
+
this.modal.style.backgroundColor = 'white';
|
|
202
|
+
this.modal.style.maxWidth = '100vw';
|
|
203
|
+
this.modal.style.maxHeight = '100vh';
|
|
204
|
+
this.modal.style.width = '600px';
|
|
205
|
+
this.modal.style.height = '600px';
|
|
206
|
+
|
|
158
207
|
document.body.appendChild(this.modal);
|
|
159
|
-
this.embed(this.modal);
|
|
208
|
+
this.embed(this.modal, true);
|
|
209
|
+
this.on(CMType.CANCEL, () => {
|
|
210
|
+
this.closeModal();
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
this.on(CMType.COMPLETE, () => {
|
|
214
|
+
this.closeModal();
|
|
215
|
+
});
|
|
216
|
+
|
|
160
217
|
this.modal.showModal();
|
|
161
218
|
this.modal.addEventListener('click', () => {
|
|
162
219
|
// console.log('click');
|
|
163
220
|
this.closeModal();
|
|
164
221
|
});
|
|
222
|
+
return this.completion();
|
|
165
223
|
}
|
|
224
|
+
/**
|
|
225
|
+
* Closes the modal dialog
|
|
226
|
+
*
|
|
227
|
+
*/
|
|
166
228
|
|
|
167
|
-
|
|
229
|
+
closeModal(): void {
|
|
168
230
|
if (this.modal) {
|
|
169
|
-
console.log('closeModal');
|
|
231
|
+
// console.log('closeModal');
|
|
170
232
|
this.modal?.close();
|
|
171
233
|
this.modal.remove();
|
|
172
234
|
this.modal = undefined;
|