@fedimint/core-web 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/FedimintWallet.d.ts +42 -65
- package/dist/FedimintWallet.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/services/BalanceService.d.ts +34 -0
- package/dist/services/BalanceService.d.ts.map +1 -0
- package/dist/services/FederationService.d.ts +11 -0
- package/dist/services/FederationService.d.ts.map +1 -0
- package/dist/services/LightningService.d.ts +18 -0
- package/dist/services/LightningService.d.ts.map +1 -0
- package/dist/services/MintService.d.ts +15 -0
- package/dist/services/MintService.d.ts.map +1 -0
- package/dist/services/RecoveryService.d.ts +13 -0
- package/dist/services/RecoveryService.d.ts.map +1 -0
- package/dist/services/index.d.ts +6 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/types/wallet.d.ts +16 -11
- package/dist/types/wallet.d.ts.map +1 -1
- package/dist/utils/logger.d.ts +17 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/worker/WorkerClient.d.ts +44 -0
- package/dist/worker/WorkerClient.d.ts.map +1 -0
- package/dist/worker/index.d.ts +2 -0
- package/dist/worker/index.d.ts.map +1 -0
- package/dist/worker.js +1 -1
- package/dist/worker.js.map +1 -1
- package/package.json +9 -9
- package/src/FedimintWallet.test.ts +77 -0
- package/src/FedimintWallet.ts +91 -466
- package/src/services/BalanceService.test.ts +50 -0
- package/src/services/BalanceService.ts +50 -0
- package/src/services/FederationService.test.ts +58 -0
- package/src/services/FederationService.ts +22 -0
- package/src/services/LightningService.test.ts +168 -0
- package/src/services/LightningService.ts +144 -0
- package/src/services/MintService.test.ts +19 -0
- package/src/services/MintService.ts +96 -0
- package/src/services/RecoveryService.ts +26 -0
- package/src/services/index.ts +5 -0
- package/src/test/TestFedimintWallet.ts +17 -0
- package/src/test/TestingService.ts +59 -0
- package/src/test/setupTests.ts +43 -0
- package/src/types/wallet.ts +20 -13
- package/src/utils/logger.ts +61 -0
- package/src/worker/WorkerClient.ts +229 -0
- package/src/worker/index.ts +1 -0
- package/src/worker/worker.js +96 -0
- package/src/worker/worker.test.ts +90 -0
- package/node_modules/fedimint-client-wasm/fedimint_client_wasm.d.ts +0 -49
- package/node_modules/fedimint-client-wasm/fedimint_client_wasm.js +0 -4
- package/node_modules/fedimint-client-wasm/fedimint_client_wasm_bg.js +0 -1411
- package/node_modules/fedimint-client-wasm/fedimint_client_wasm_bg.wasm +0 -0
- package/node_modules/fedimint-client-wasm/package.json +0 -26
- package/src/worker.js +0 -80
- package/wasm/fedimint_client_wasm.d.ts +0 -49
- package/wasm/fedimint_client_wasm.js +0 -4
- package/wasm/fedimint_client_wasm_bg.js +0 -1411
- package/wasm/fedimint_client_wasm_bg.wasm +0 -0
- package/wasm/fedimint_client_wasm_bg.wasm.d.ts +0 -110
- package/wasm/package.json +0 -26
package/src/types/wallet.ts
CHANGED
|
@@ -47,22 +47,22 @@ type PayType = {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
type LnPayState =
|
|
50
|
-
| '
|
|
51
|
-
| '
|
|
52
|
-
| {
|
|
53
|
-
| {
|
|
54
|
-
| '
|
|
50
|
+
| 'created'
|
|
51
|
+
| 'canceled'
|
|
52
|
+
| { funded: { block_height: number } }
|
|
53
|
+
| { waiting_for_refund: { error_reason: string } }
|
|
54
|
+
| 'awaiting_change'
|
|
55
55
|
| { Success: { preimage: string } }
|
|
56
|
-
| {
|
|
57
|
-
| {
|
|
56
|
+
| { refunded: { gateway_error: string } }
|
|
57
|
+
| { unexpected_error: { error_message: string } }
|
|
58
58
|
|
|
59
59
|
type LnReceiveState =
|
|
60
|
-
| '
|
|
61
|
-
| {
|
|
62
|
-
| {
|
|
63
|
-
| '
|
|
64
|
-
| '
|
|
65
|
-
| '
|
|
60
|
+
| 'created'
|
|
61
|
+
| { waiting_for_payment: { invoice: string; timeout: number } }
|
|
62
|
+
| { canceled: { reason: string } }
|
|
63
|
+
| 'funded'
|
|
64
|
+
| 'awaiting_funds'
|
|
65
|
+
| 'claimed'
|
|
66
66
|
|
|
67
67
|
type CreateBolt11Response = {
|
|
68
68
|
operation_id: string
|
|
@@ -94,6 +94,12 @@ type StreamResult<T extends JSONValue> =
|
|
|
94
94
|
|
|
95
95
|
type CancelFunction = () => void
|
|
96
96
|
|
|
97
|
+
type ReissueExternalNotesState =
|
|
98
|
+
| 'Created'
|
|
99
|
+
| 'Issuing'
|
|
100
|
+
| 'Done'
|
|
101
|
+
| { Failed: { error: string } }
|
|
102
|
+
|
|
97
103
|
export {
|
|
98
104
|
JSONValue,
|
|
99
105
|
JSONObject,
|
|
@@ -111,4 +117,5 @@ export {
|
|
|
111
117
|
StreamResult,
|
|
112
118
|
ModuleKind,
|
|
113
119
|
CancelFunction,
|
|
120
|
+
ReissueExternalNotesState,
|
|
114
121
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const logLevels = ['debug', 'info', 'warn', 'error', 'none'] as const
|
|
2
|
+
export type LogLevel = (typeof logLevels)[number]
|
|
3
|
+
|
|
4
|
+
export class Logger {
|
|
5
|
+
private level: LogLevel
|
|
6
|
+
|
|
7
|
+
constructor(level: LogLevel = 'none') {
|
|
8
|
+
this.level = level
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
setLevel(level: LogLevel) {
|
|
12
|
+
this.level = level
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
coerceLevel(level: string): LogLevel {
|
|
16
|
+
if (logLevels.includes(level.toLocaleUpperCase() as LogLevel)) {
|
|
17
|
+
return level.toLocaleUpperCase() as LogLevel
|
|
18
|
+
}
|
|
19
|
+
return 'info'
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
log(level: string, message: string, ...args: any[]) {
|
|
23
|
+
const logLevel = this.coerceLevel(level)
|
|
24
|
+
if (!this.shouldLog(logLevel)) {
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
const consoleFn = console[logLevel]
|
|
28
|
+
consoleFn(`[${logLevel.toUpperCase()}] ${message}`, ...args)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
debug(message: string, ...args: any[]) {
|
|
32
|
+
this.log('debug', message, ...args)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
info(message: string, ...args: any[]) {
|
|
36
|
+
this.log('info', message, ...args)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
warn(message: string, ...args: any[]) {
|
|
40
|
+
this.log('warn', message, ...args)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
error(message: string, ...args: any[]) {
|
|
44
|
+
this.log('error', message, ...args)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private shouldLog(
|
|
48
|
+
messageLevel: LogLevel,
|
|
49
|
+
): messageLevel is Exclude<LogLevel, 'none'> {
|
|
50
|
+
const levels: LogLevel[] = ['debug', 'info', 'warn', 'error', 'none']
|
|
51
|
+
const messageLevelIndex = levels.indexOf(messageLevel)
|
|
52
|
+
const currentLevelIndex = levels.indexOf(this.level)
|
|
53
|
+
return (
|
|
54
|
+
currentLevelIndex <= messageLevelIndex &&
|
|
55
|
+
this.level !== 'none' &&
|
|
56
|
+
messageLevel !== 'none'
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const logger = new Logger()
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CancelFunction,
|
|
3
|
+
JSONValue,
|
|
4
|
+
ModuleKind,
|
|
5
|
+
StreamError,
|
|
6
|
+
StreamResult,
|
|
7
|
+
} from '../types/wallet'
|
|
8
|
+
import { logger } from '../utils/logger'
|
|
9
|
+
|
|
10
|
+
// Handles communication with the wasm worker
|
|
11
|
+
// TODO: Move rpc stream management to a separate "SubscriptionManager" class
|
|
12
|
+
export class WorkerClient {
|
|
13
|
+
private worker: Worker
|
|
14
|
+
private requestCounter = 0
|
|
15
|
+
private requestCallbacks = new Map<number, (value: any) => void>()
|
|
16
|
+
private initPromise: Promise<void> | null = null
|
|
17
|
+
|
|
18
|
+
constructor() {
|
|
19
|
+
// Must create the URL inside the constructor for vite
|
|
20
|
+
this.worker = new Worker(new URL('./worker.js', import.meta.url), {
|
|
21
|
+
type: 'module',
|
|
22
|
+
})
|
|
23
|
+
this.worker.onmessage = this.handleWorkerMessage.bind(this)
|
|
24
|
+
this.worker.onerror = this.handleWorkerError.bind(this)
|
|
25
|
+
logger.info('WorkerClient instantiated')
|
|
26
|
+
logger.debug('WorkerClient', this.worker)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Idempotent setup - Loads the wasm module
|
|
30
|
+
initialize() {
|
|
31
|
+
if (this.initPromise) return this.initPromise
|
|
32
|
+
this.initPromise = this.sendSingleMessage('init')
|
|
33
|
+
return this.initPromise
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private handleWorkerLogs(event: MessageEvent) {
|
|
37
|
+
const { type, level, message, ...data } = event.data
|
|
38
|
+
logger.log(level, message, ...data)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private handleWorkerError(event: ErrorEvent) {
|
|
42
|
+
logger.error('Worker error', event)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private handleWorkerMessage(event: MessageEvent) {
|
|
46
|
+
const { type, requestId, ...data } = event.data
|
|
47
|
+
if (type === 'log') {
|
|
48
|
+
this.handleWorkerLogs(event.data)
|
|
49
|
+
}
|
|
50
|
+
const streamCallback = this.requestCallbacks.get(requestId)
|
|
51
|
+
// TODO: Handle errors... maybe have another callbacks list for errors?
|
|
52
|
+
logger.debug('WorkerClient - handleWorkerMessage', event.data)
|
|
53
|
+
if (streamCallback) {
|
|
54
|
+
streamCallback(data) // {data: something} OR {error: something}
|
|
55
|
+
} else {
|
|
56
|
+
logger.warn(
|
|
57
|
+
'WorkerClient - handleWorkerMessage - received message with no callback',
|
|
58
|
+
requestId,
|
|
59
|
+
event.data,
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// TODO: Handle errors... maybe have another callbacks list for errors?
|
|
65
|
+
// TODO: Handle timeouts
|
|
66
|
+
// TODO: Handle multiple errors
|
|
67
|
+
|
|
68
|
+
sendSingleMessage(type: string, payload?: any): Promise<any> {
|
|
69
|
+
return new Promise((resolve, reject) => {
|
|
70
|
+
const requestId = ++this.requestCounter
|
|
71
|
+
logger.debug('WorkerClient - sendSingleMessage', requestId, type, payload)
|
|
72
|
+
this.requestCallbacks.set(requestId, (response) => {
|
|
73
|
+
this.requestCallbacks.delete(requestId)
|
|
74
|
+
logger.debug(
|
|
75
|
+
'WorkerClient - sendSingleMessage - response',
|
|
76
|
+
requestId,
|
|
77
|
+
response,
|
|
78
|
+
)
|
|
79
|
+
if (response.data) resolve(response.data)
|
|
80
|
+
else if (response.error) reject(response.error)
|
|
81
|
+
else
|
|
82
|
+
logger.warn(
|
|
83
|
+
'WorkerClient - sendSingleMessage - malformed response',
|
|
84
|
+
requestId,
|
|
85
|
+
response,
|
|
86
|
+
)
|
|
87
|
+
})
|
|
88
|
+
this.worker.postMessage({ type, payload, requestId })
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @summary Initiates an RPC stream with the specified module and method.
|
|
94
|
+
*
|
|
95
|
+
* @description
|
|
96
|
+
* This function sets up an RPC stream by sending a request to a worker and
|
|
97
|
+
* handling responses asynchronously. It ensures that unsubscription is handled
|
|
98
|
+
* correctly, even if the unsubscribe function is called before the subscription
|
|
99
|
+
* is fully established, by deferring the unsubscription attempt using `setTimeout`.
|
|
100
|
+
*
|
|
101
|
+
* The function operates in a non-blocking manner, leveraging Promises to manage
|
|
102
|
+
* asynchronous operations and callbacks to handle responses.
|
|
103
|
+
*
|
|
104
|
+
*
|
|
105
|
+
* @template Response - The expected type of the successful response.
|
|
106
|
+
* @template Body - The type of the request body.
|
|
107
|
+
* @param module - The module kind to interact with.
|
|
108
|
+
* @param method - The method name to invoke on the module.
|
|
109
|
+
* @param body - The request payload.
|
|
110
|
+
* @param onSuccess - Callback invoked with the response data on success.
|
|
111
|
+
* @param onError - Callback invoked with error information if an error occurs.
|
|
112
|
+
* @param onEnd - Optional callback invoked when the stream ends.
|
|
113
|
+
* @returns A function that can be called to cancel the subscription.
|
|
114
|
+
*
|
|
115
|
+
*/
|
|
116
|
+
rpcStream<
|
|
117
|
+
Response extends JSONValue = JSONValue,
|
|
118
|
+
Body extends JSONValue = JSONValue,
|
|
119
|
+
>(
|
|
120
|
+
module: ModuleKind,
|
|
121
|
+
method: string,
|
|
122
|
+
body: Body,
|
|
123
|
+
onSuccess: (res: Response) => void,
|
|
124
|
+
onError: (res: StreamError['error']) => void,
|
|
125
|
+
onEnd: () => void = () => {},
|
|
126
|
+
): CancelFunction {
|
|
127
|
+
const requestId = ++this.requestCounter
|
|
128
|
+
logger.debug('WorkerClient - rpcStream', requestId, module, method, body)
|
|
129
|
+
let unsubscribe: (value: void) => void = () => {}
|
|
130
|
+
let isSubscribed = false
|
|
131
|
+
|
|
132
|
+
const unsubscribePromise = new Promise<void>((resolve) => {
|
|
133
|
+
unsubscribe = () => {
|
|
134
|
+
if (isSubscribed) {
|
|
135
|
+
// If already subscribed, resolve immediately to trigger unsubscription
|
|
136
|
+
resolve()
|
|
137
|
+
} else {
|
|
138
|
+
// If not yet subscribed, defer the unsubscribe attempt to the next event loop tick
|
|
139
|
+
// This ensures that subscription setup has time to complete
|
|
140
|
+
setTimeout(() => unsubscribe(), 0)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
// Initiate the inner RPC stream setup asynchronously
|
|
146
|
+
this._rpcStreamInner(
|
|
147
|
+
requestId,
|
|
148
|
+
module,
|
|
149
|
+
method,
|
|
150
|
+
body,
|
|
151
|
+
onSuccess,
|
|
152
|
+
onError,
|
|
153
|
+
onEnd,
|
|
154
|
+
unsubscribePromise,
|
|
155
|
+
).then(() => {
|
|
156
|
+
isSubscribed = true
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
return unsubscribe
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private async _rpcStreamInner<
|
|
163
|
+
Response extends JSONValue = JSONValue,
|
|
164
|
+
Body extends JSONValue = JSONValue,
|
|
165
|
+
>(
|
|
166
|
+
requestId: number,
|
|
167
|
+
module: ModuleKind,
|
|
168
|
+
method: string,
|
|
169
|
+
body: Body,
|
|
170
|
+
onSuccess: (res: Response) => void,
|
|
171
|
+
onError: (res: StreamError['error']) => void,
|
|
172
|
+
onEnd: () => void = () => {},
|
|
173
|
+
unsubscribePromise: Promise<void>,
|
|
174
|
+
// Unsubscribe function
|
|
175
|
+
): Promise<void> {
|
|
176
|
+
// await this.openPromise
|
|
177
|
+
// if (!this.worker || !this._isOpen)
|
|
178
|
+
// throw new Error('FedimintWallet is not open')
|
|
179
|
+
|
|
180
|
+
this.requestCallbacks.set(requestId, (response: StreamResult<Response>) => {
|
|
181
|
+
if (response.error !== undefined) {
|
|
182
|
+
onError(response.error)
|
|
183
|
+
} else if (response.data !== undefined) {
|
|
184
|
+
onSuccess(response.data)
|
|
185
|
+
} else if (response.end !== undefined) {
|
|
186
|
+
this.requestCallbacks.delete(requestId)
|
|
187
|
+
onEnd()
|
|
188
|
+
}
|
|
189
|
+
})
|
|
190
|
+
this.worker.postMessage({
|
|
191
|
+
type: 'rpc',
|
|
192
|
+
payload: { module, method, body },
|
|
193
|
+
requestId,
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
unsubscribePromise.then(() => {
|
|
197
|
+
this.worker?.postMessage({
|
|
198
|
+
type: 'unsubscribe',
|
|
199
|
+
requestId,
|
|
200
|
+
})
|
|
201
|
+
this.requestCallbacks.delete(requestId)
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
rpcSingle<Response extends JSONValue = JSONValue>(
|
|
206
|
+
module: ModuleKind,
|
|
207
|
+
method: string,
|
|
208
|
+
body: JSONValue,
|
|
209
|
+
): Promise<Response> {
|
|
210
|
+
logger.debug('WorkerClient - rpcSingle', module, method, body)
|
|
211
|
+
return new Promise((resolve, reject) => {
|
|
212
|
+
this.rpcStream<Response>(module, method, body, resolve, reject)
|
|
213
|
+
})
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
cleanup() {
|
|
217
|
+
this.worker.terminate()
|
|
218
|
+
this.initPromise = null
|
|
219
|
+
this.requestCallbacks.clear()
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// For Testing
|
|
223
|
+
_getRequestCounter() {
|
|
224
|
+
return this.requestCounter
|
|
225
|
+
}
|
|
226
|
+
_getRequestCallbackMap() {
|
|
227
|
+
return this.requestCallbacks
|
|
228
|
+
}
|
|
229
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { WorkerClient } from './WorkerClient'
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// Web Worker for fedimint-client-wasm to run in the browser
|
|
2
|
+
|
|
3
|
+
// HACK: Fixes vitest browser runner
|
|
4
|
+
// TODO: remove once https://github.com/vitest-dev/vitest/pull/6569 lands in a release
|
|
5
|
+
globalThis.__vitest_browser_runner__ = { wrapDynamicImport: (foo) => foo() }
|
|
6
|
+
|
|
7
|
+
// dynamically imported Constructor for WasmClient
|
|
8
|
+
let WasmClient = null
|
|
9
|
+
// client instance
|
|
10
|
+
let client = null
|
|
11
|
+
|
|
12
|
+
const streamCancelMap = new Map()
|
|
13
|
+
|
|
14
|
+
const handleFree = (requestId) => {
|
|
15
|
+
streamCancelMap.delete(requestId)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
console.log('Worker - init')
|
|
19
|
+
|
|
20
|
+
self.onmessage = async (event) => {
|
|
21
|
+
const { type, payload, requestId } = event.data
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
if (type === 'init') {
|
|
25
|
+
WasmClient = (await import('@fedimint/fedimint-client-wasm-bundler'))
|
|
26
|
+
.WasmClient
|
|
27
|
+
self.postMessage({ type: 'initialized', data: {}, requestId })
|
|
28
|
+
} else if (type === 'open') {
|
|
29
|
+
const { clientName } = payload
|
|
30
|
+
client = (await WasmClient.open(clientName)) || null
|
|
31
|
+
self.postMessage({
|
|
32
|
+
type: 'open',
|
|
33
|
+
data: { success: !!client },
|
|
34
|
+
requestId,
|
|
35
|
+
})
|
|
36
|
+
} else if (type === 'join') {
|
|
37
|
+
const { inviteCode, clientName: joinClientName } = payload
|
|
38
|
+
try {
|
|
39
|
+
client = await WasmClient.join_federation(joinClientName, inviteCode)
|
|
40
|
+
self.postMessage({
|
|
41
|
+
type: 'join',
|
|
42
|
+
data: { success: !!client },
|
|
43
|
+
requestId,
|
|
44
|
+
})
|
|
45
|
+
} catch (e) {
|
|
46
|
+
self.postMessage({ type: 'error', error: e.message, requestId })
|
|
47
|
+
}
|
|
48
|
+
} else if (type === 'rpc') {
|
|
49
|
+
const { module, method, body } = payload
|
|
50
|
+
console.log('RPC received', module, method, body)
|
|
51
|
+
if (!client) {
|
|
52
|
+
self.postMessage({
|
|
53
|
+
type: 'error',
|
|
54
|
+
error: 'WasmClient not initialized',
|
|
55
|
+
requestId,
|
|
56
|
+
})
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
const rpcHandle = await client.rpc(
|
|
60
|
+
module,
|
|
61
|
+
method,
|
|
62
|
+
JSON.stringify(body),
|
|
63
|
+
(res) => {
|
|
64
|
+
console.log('RPC response', requestId, res)
|
|
65
|
+
const data = JSON.parse(res)
|
|
66
|
+
self.postMessage({ type: 'rpcResponse', requestId, ...data })
|
|
67
|
+
|
|
68
|
+
if (data.end !== undefined) {
|
|
69
|
+
// Handle stream ending
|
|
70
|
+
const handle = streamCancelMap.get(requestId)
|
|
71
|
+
handle?.free()
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
)
|
|
75
|
+
streamCancelMap.set(requestId, rpcHandle)
|
|
76
|
+
} else if (type === 'unsubscribe') {
|
|
77
|
+
const rpcHandle = streamCancelMap.get(requestId)
|
|
78
|
+
if (rpcHandle) {
|
|
79
|
+
rpcHandle.cancel()
|
|
80
|
+
rpcHandle.free()
|
|
81
|
+
streamCancelMap.delete(requestId)
|
|
82
|
+
}
|
|
83
|
+
} else {
|
|
84
|
+
self.postMessage({
|
|
85
|
+
type: 'error',
|
|
86
|
+
error: 'Unknown message type',
|
|
87
|
+
requestId,
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
} catch (e) {
|
|
91
|
+
console.error('ERROR', e)
|
|
92
|
+
self.postMessage({ type: 'error', error: e, requestId })
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// self.postMessage({ type: 'init', data: {} })
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { expect } from 'vitest'
|
|
2
|
+
import { JSONObject } from '../types/wallet'
|
|
3
|
+
import { TESTING_INVITE } from '../test/TestingService'
|
|
4
|
+
import { workerTest } from '../test/setupTests'
|
|
5
|
+
|
|
6
|
+
// Waits for a message of a given type from the worker
|
|
7
|
+
const waitForWorkerResponse = (
|
|
8
|
+
worker: Worker,
|
|
9
|
+
messageType: string,
|
|
10
|
+
): Promise<JSONObject> => {
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
worker.onmessage = (event) => {
|
|
13
|
+
if (event.data.type === messageType) {
|
|
14
|
+
resolve(event.data)
|
|
15
|
+
} else if (event.data.type === 'error') {
|
|
16
|
+
reject(event.data.error)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
worker.onerror = (error) => {
|
|
20
|
+
reject(error.message)
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
workerTest(
|
|
26
|
+
'should initialize WasmClient on init message',
|
|
27
|
+
async ({ worker }) => {
|
|
28
|
+
worker.postMessage({ type: 'init', requestId: 1 })
|
|
29
|
+
const response = await waitForWorkerResponse(worker, 'initialized')
|
|
30
|
+
expect(response.data).toEqual({})
|
|
31
|
+
},
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
workerTest(
|
|
35
|
+
'should return false on open for a new client',
|
|
36
|
+
async ({ worker, clientName }) => {
|
|
37
|
+
worker.postMessage({ type: 'init', requestId: 1 })
|
|
38
|
+
await waitForWorkerResponse(worker, 'initialized')
|
|
39
|
+
|
|
40
|
+
worker.postMessage({
|
|
41
|
+
type: 'open',
|
|
42
|
+
requestId: 2,
|
|
43
|
+
payload: { clientName },
|
|
44
|
+
})
|
|
45
|
+
const response = await waitForWorkerResponse(worker, 'open')
|
|
46
|
+
expect(response.data).toEqual({ success: false })
|
|
47
|
+
},
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
workerTest(
|
|
51
|
+
'should error on fake federation invitation',
|
|
52
|
+
async ({ worker, clientName }) => {
|
|
53
|
+
worker.postMessage({ type: 'init', requestId: 1 })
|
|
54
|
+
await waitForWorkerResponse(worker, 'initialized')
|
|
55
|
+
|
|
56
|
+
worker.postMessage({
|
|
57
|
+
type: 'join',
|
|
58
|
+
requestId: 2,
|
|
59
|
+
payload: { inviteCode: 'fakefederationinvitation', clientName },
|
|
60
|
+
})
|
|
61
|
+
try {
|
|
62
|
+
await waitForWorkerResponse(worker, 'open')
|
|
63
|
+
expect.unreachable()
|
|
64
|
+
} catch (e) {
|
|
65
|
+
expect(e).toBe('parsing failed')
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
workerTest(
|
|
71
|
+
'should handle joining a federation',
|
|
72
|
+
async ({ worker, clientName }) => {
|
|
73
|
+
worker.postMessage({ type: 'init', requestId: 1 })
|
|
74
|
+
await waitForWorkerResponse(worker, 'initialized')
|
|
75
|
+
|
|
76
|
+
worker.postMessage({
|
|
77
|
+
type: 'join',
|
|
78
|
+
requestId: 2,
|
|
79
|
+
payload: { inviteCode: TESTING_INVITE, clientName },
|
|
80
|
+
})
|
|
81
|
+
const response = await waitForWorkerResponse(worker, 'join')
|
|
82
|
+
expect(response.data).toEqual({ success: true })
|
|
83
|
+
},
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
workerTest('should handle unknown message type', async ({ worker }) => {
|
|
87
|
+
worker.postMessage({ type: 'unknown', requestId: 2 })
|
|
88
|
+
const response = await waitForWorkerResponse(worker, 'error')
|
|
89
|
+
expect(response.error).toBe('Unknown message type')
|
|
90
|
+
})
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
/* tslint:disable */
|
|
2
|
-
/* eslint-disable */
|
|
3
|
-
/**
|
|
4
|
-
*/
|
|
5
|
-
export class RpcHandle {
|
|
6
|
-
free(): void
|
|
7
|
-
/**
|
|
8
|
-
*/
|
|
9
|
-
cancel(): void
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
*/
|
|
13
|
-
export class WasmClient {
|
|
14
|
-
free(): void
|
|
15
|
-
/**
|
|
16
|
-
* Open fedimint client with already joined federation.
|
|
17
|
-
*
|
|
18
|
-
* After you have joined a federation, you can reopen the fedimint client
|
|
19
|
-
* with same client_name. Opening client with same name at same time is
|
|
20
|
-
* not supported. You can close the current client by calling
|
|
21
|
-
* `client.free()`. NOTE: The client will remain active until all the
|
|
22
|
-
* running rpc calls have finished.
|
|
23
|
-
* @param {string} client_name
|
|
24
|
-
* @returns {Promise<WasmClient | undefined>}
|
|
25
|
-
*/
|
|
26
|
-
static open(client_name: string): Promise<WasmClient | undefined>
|
|
27
|
-
/**
|
|
28
|
-
* Open a fedimint client by join a federation.
|
|
29
|
-
* @param {string} client_name
|
|
30
|
-
* @param {string} invite_code
|
|
31
|
-
* @returns {Promise<WasmClient>}
|
|
32
|
-
*/
|
|
33
|
-
static join_federation(
|
|
34
|
-
client_name: string,
|
|
35
|
-
invite_code: string,
|
|
36
|
-
): Promise<WasmClient>
|
|
37
|
-
/**
|
|
38
|
-
* Call a fedimint client rpc the responses are returned using `cb`
|
|
39
|
-
* callback. Each rpc call *can* return multiple responses by calling
|
|
40
|
-
* `cb` multiple times. The returned RpcHandle can be used to cancel the
|
|
41
|
-
* operation.
|
|
42
|
-
* @param {string} module
|
|
43
|
-
* @param {string} method
|
|
44
|
-
* @param {string} payload
|
|
45
|
-
* @param {Function} cb
|
|
46
|
-
* @returns {RpcHandle}
|
|
47
|
-
*/
|
|
48
|
-
rpc(module: string, method: string, payload: string, cb: Function): RpcHandle
|
|
49
|
-
}
|