@fedimint/core-web 0.0.0-20250617190659
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 +21 -0
- package/README.md +16 -0
- package/dist/dts/FedimintWallet.d.ts +114 -0
- package/dist/dts/FedimintWallet.d.ts.map +1 -0
- package/dist/dts/index.d.ts +3 -0
- package/dist/dts/index.d.ts.map +1 -0
- package/dist/dts/services/BalanceService.d.ts +15 -0
- package/dist/dts/services/BalanceService.d.ts.map +1 -0
- package/dist/dts/services/FederationService.d.ts +11 -0
- package/dist/dts/services/FederationService.d.ts.map +1 -0
- package/dist/dts/services/LightningService.d.ts +47 -0
- package/dist/dts/services/LightningService.d.ts.map +1 -0
- package/dist/dts/services/MintService.d.ts +23 -0
- package/dist/dts/services/MintService.d.ts.map +1 -0
- package/dist/dts/services/RecoveryService.d.ts +13 -0
- package/dist/dts/services/RecoveryService.d.ts.map +1 -0
- package/dist/dts/services/WalletService.d.ts +8 -0
- package/dist/dts/services/WalletService.d.ts.map +1 -0
- package/dist/dts/services/index.d.ts +7 -0
- package/dist/dts/services/index.d.ts.map +1 -0
- package/dist/dts/types/index.d.ts +4 -0
- package/dist/dts/types/index.d.ts.map +1 -0
- package/dist/dts/types/utils.d.ts +23 -0
- package/dist/dts/types/utils.d.ts.map +1 -0
- package/dist/dts/types/wallet.d.ts +102 -0
- package/dist/dts/types/wallet.d.ts.map +1 -0
- package/dist/dts/types/worker.d.ts +4 -0
- package/dist/dts/types/worker.d.ts.map +1 -0
- package/dist/dts/utils/logger.d.ts +17 -0
- package/dist/dts/utils/logger.d.ts.map +1 -0
- package/dist/dts/worker/WorkerClient.d.ts +44 -0
- package/dist/dts/worker/WorkerClient.d.ts.map +1 -0
- package/dist/dts/worker/index.d.ts +2 -0
- package/dist/dts/worker/index.d.ts.map +1 -0
- package/dist/index.d.ts +392 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/worker.js +2 -0
- package/dist/worker.js.map +1 -0
- package/package.json +43 -0
- package/src/FedimintWallet.test.ts +73 -0
- package/src/FedimintWallet.ts +215 -0
- package/src/index.ts +2 -0
- package/src/services/BalanceService.test.ts +50 -0
- package/src/services/BalanceService.ts +29 -0
- package/src/services/FederationService.test.ts +58 -0
- package/src/services/FederationService.ts +24 -0
- package/src/services/LightningService.test.ts +208 -0
- package/src/services/LightningService.ts +274 -0
- package/src/services/MintService.test.ts +74 -0
- package/src/services/MintService.ts +129 -0
- package/src/services/RecoveryService.ts +28 -0
- package/src/services/WalletService.test.ts +24 -0
- package/src/services/WalletService.ts +10 -0
- package/src/services/index.ts +6 -0
- package/src/test/TestFedimintWallet.ts +26 -0
- package/src/test/TestingService.ts +79 -0
- package/src/test/crypto.ts +44 -0
- package/src/test/fixtures.test.ts +18 -0
- package/src/test/fixtures.ts +70 -0
- package/src/types/index.ts +3 -0
- package/src/types/utils.ts +29 -0
- package/src/types/wallet.ts +141 -0
- package/src/types/worker.ts +16 -0
- package/src/utils/logger.ts +61 -0
- package/src/worker/WorkerClient.test.ts +6 -0
- package/src/worker/WorkerClient.ts +236 -0
- package/src/worker/index.ts +1 -0
- package/src/worker/worker.js +167 -0
- package/src/worker/worker.test.ts +90 -0
|
@@ -0,0 +1,167 @@
|
|
|
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
|
+
/**
|
|
21
|
+
* Type definitions for the worker messages
|
|
22
|
+
*
|
|
23
|
+
* @typedef {import('../types/worker').WorkerMessageType} WorkerMessageType
|
|
24
|
+
* @typedef {{
|
|
25
|
+
* type: WorkerMessageType
|
|
26
|
+
* payload: any
|
|
27
|
+
* requestId: number
|
|
28
|
+
* }} WorkerMessage
|
|
29
|
+
* @param {{data: WorkerMessage}} event
|
|
30
|
+
*/
|
|
31
|
+
self.onmessage = async (event) => {
|
|
32
|
+
const { type, payload, requestId } = event.data
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
if (type === 'init') {
|
|
36
|
+
WasmClient = (await import('@fedimint/fedimint-client-wasm-bundler'))
|
|
37
|
+
.WasmClient
|
|
38
|
+
self.postMessage({ type: 'initialized', data: {}, requestId })
|
|
39
|
+
} else if (type === 'open') {
|
|
40
|
+
const { clientName } = payload
|
|
41
|
+
client = (await WasmClient.open(clientName)) || null
|
|
42
|
+
self.postMessage({
|
|
43
|
+
type: 'open',
|
|
44
|
+
data: { success: !!client },
|
|
45
|
+
requestId,
|
|
46
|
+
})
|
|
47
|
+
} else if (type === 'join') {
|
|
48
|
+
const { inviteCode, clientName: joinClientName } = payload
|
|
49
|
+
try {
|
|
50
|
+
client = await WasmClient.join_federation(joinClientName, inviteCode)
|
|
51
|
+
self.postMessage({
|
|
52
|
+
type: 'join',
|
|
53
|
+
data: { success: !!client },
|
|
54
|
+
requestId,
|
|
55
|
+
})
|
|
56
|
+
} catch (e) {
|
|
57
|
+
self.postMessage({ type: 'error', error: e.message, requestId })
|
|
58
|
+
}
|
|
59
|
+
} else if (type === 'previewFederation') {
|
|
60
|
+
const { inviteCode } = payload
|
|
61
|
+
try {
|
|
62
|
+
client = await WasmClient.preview_federation(inviteCode)
|
|
63
|
+
const parsed = JSON.parse(client)
|
|
64
|
+
self.postMessage({
|
|
65
|
+
type: 'previewFederation',
|
|
66
|
+
data: {
|
|
67
|
+
success: !!client,
|
|
68
|
+
config: parsed.config,
|
|
69
|
+
federation_id: parsed.federation_id,
|
|
70
|
+
},
|
|
71
|
+
requestId,
|
|
72
|
+
})
|
|
73
|
+
} catch (e) {
|
|
74
|
+
self.postMessage({ type: 'error', error: e.message, requestId })
|
|
75
|
+
}
|
|
76
|
+
} else if (type === 'rpc') {
|
|
77
|
+
const { module, method, body } = payload
|
|
78
|
+
console.log('RPC received', module, method, body)
|
|
79
|
+
if (!client) {
|
|
80
|
+
self.postMessage({
|
|
81
|
+
type: 'error',
|
|
82
|
+
error: 'WasmClient not initialized',
|
|
83
|
+
requestId,
|
|
84
|
+
})
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
const rpcHandle = await client.rpc(
|
|
88
|
+
module,
|
|
89
|
+
method,
|
|
90
|
+
JSON.stringify(body),
|
|
91
|
+
(res) => {
|
|
92
|
+
console.log('RPC response', requestId, res)
|
|
93
|
+
const data = JSON.parse(res)
|
|
94
|
+
self.postMessage({ type: 'rpcResponse', requestId, ...data })
|
|
95
|
+
|
|
96
|
+
if (data.end !== undefined) {
|
|
97
|
+
// Handle stream ending
|
|
98
|
+
const handle = streamCancelMap.get(requestId)
|
|
99
|
+
handle?.free()
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
)
|
|
103
|
+
streamCancelMap.set(requestId, rpcHandle)
|
|
104
|
+
} else if (type === 'unsubscribe') {
|
|
105
|
+
const rpcHandle = streamCancelMap.get(requestId)
|
|
106
|
+
if (rpcHandle) {
|
|
107
|
+
rpcHandle.cancel()
|
|
108
|
+
rpcHandle.free()
|
|
109
|
+
streamCancelMap.delete(requestId)
|
|
110
|
+
}
|
|
111
|
+
} else if (type === 'cleanup') {
|
|
112
|
+
console.log('cleanup message received')
|
|
113
|
+
client?.free()
|
|
114
|
+
self.postMessage({
|
|
115
|
+
type: 'cleanup',
|
|
116
|
+
data: {},
|
|
117
|
+
requestId,
|
|
118
|
+
})
|
|
119
|
+
close()
|
|
120
|
+
} else if (type === 'parseInviteCode') {
|
|
121
|
+
const { inviteCode } = payload
|
|
122
|
+
try {
|
|
123
|
+
const res = WasmClient.parse_invite_code(inviteCode)
|
|
124
|
+
const parsedRes = JSON.parse(res)
|
|
125
|
+
self.postMessage({
|
|
126
|
+
type: 'parseInviteCode',
|
|
127
|
+
data: parsedRes,
|
|
128
|
+
requestId,
|
|
129
|
+
})
|
|
130
|
+
} catch (error) {
|
|
131
|
+
self.postMessage({
|
|
132
|
+
type: 'error',
|
|
133
|
+
error: `Failed to parse invite code: ${error.message}`,
|
|
134
|
+
requestId,
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
} else if (type === 'parseBolt11Invoice') {
|
|
138
|
+
const { invoiceStr } = payload
|
|
139
|
+
try {
|
|
140
|
+
const res = WasmClient.parse_bolt11_invoice(invoiceStr)
|
|
141
|
+
const parsedRes = JSON.parse(res)
|
|
142
|
+
self.postMessage({
|
|
143
|
+
type: 'parseBolt11Invoice',
|
|
144
|
+
data: parsedRes,
|
|
145
|
+
requestId,
|
|
146
|
+
})
|
|
147
|
+
} catch (error) {
|
|
148
|
+
self.postMessage({
|
|
149
|
+
type: 'error',
|
|
150
|
+
error: `Failed to parse invoice: ${error.message}`,
|
|
151
|
+
requestId,
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
} else {
|
|
155
|
+
self.postMessage({
|
|
156
|
+
type: 'error',
|
|
157
|
+
error: 'Unknown message type',
|
|
158
|
+
requestId,
|
|
159
|
+
})
|
|
160
|
+
}
|
|
161
|
+
} catch (e) {
|
|
162
|
+
console.error('ERROR', e)
|
|
163
|
+
self.postMessage({ type: 'error', error: e, requestId })
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// self.postMessage({ type: 'init', data: {} })
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { expect } from 'vitest'
|
|
2
|
+
import { TESTING_INVITE } from '../test/TestingService'
|
|
3
|
+
import { JSONObject } from '../types'
|
|
4
|
+
import { workerTest } from '../test/fixtures'
|
|
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
|
+
})
|