@fedimint/core-web 0.1.0 → 0.1.2
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 +20 -9
- package/index.d.ts +1 -0
- package/index.js +15 -0
- package/package.json +12 -35
- package/dist/dts/FedimintWallet.d.ts +0 -51
- package/dist/dts/FedimintWallet.d.ts.map +0 -1
- package/dist/dts/WalletDirector.d.ts +0 -78
- package/dist/dts/WalletDirector.d.ts.map +0 -1
- package/dist/dts/index.d.ts +0 -4
- package/dist/dts/index.d.ts.map +0 -1
- package/dist/dts/services/BalanceService.d.ts +0 -15
- package/dist/dts/services/BalanceService.d.ts.map +0 -1
- package/dist/dts/services/FederationService.d.ts +0 -13
- package/dist/dts/services/FederationService.d.ts.map +0 -1
- package/dist/dts/services/LightningService.d.ts +0 -48
- package/dist/dts/services/LightningService.d.ts.map +0 -1
- package/dist/dts/services/MintService.d.ts +0 -23
- package/dist/dts/services/MintService.d.ts.map +0 -1
- package/dist/dts/services/RecoveryService.d.ts +0 -13
- package/dist/dts/services/RecoveryService.d.ts.map +0 -1
- package/dist/dts/services/WalletService.d.ts +0 -13
- package/dist/dts/services/WalletService.d.ts.map +0 -1
- package/dist/dts/services/index.d.ts +0 -7
- package/dist/dts/services/index.d.ts.map +0 -1
- package/dist/dts/transport/TransportClient.d.ts +0 -55
- package/dist/dts/transport/TransportClient.d.ts.map +0 -1
- package/dist/dts/transport/index.d.ts +0 -3
- package/dist/dts/transport/index.d.ts.map +0 -1
- package/dist/dts/transport/wasmTransport/WasmWorkerTransport.d.ts +0 -12
- package/dist/dts/transport/wasmTransport/WasmWorkerTransport.d.ts.map +0 -1
- package/dist/dts/types/index.d.ts +0 -4
- package/dist/dts/types/index.d.ts.map +0 -1
- package/dist/dts/types/transport.d.ts +0 -35
- package/dist/dts/types/transport.d.ts.map +0 -1
- package/dist/dts/types/utils.d.ts +0 -23
- package/dist/dts/types/utils.d.ts.map +0 -1
- package/dist/dts/types/wallet.d.ts +0 -241
- package/dist/dts/types/wallet.d.ts.map +0 -1
- package/dist/dts/utils/logger.d.ts +0 -24
- package/dist/dts/utils/logger.d.ts.map +0 -1
- package/dist/index.d.ts +0 -609
- package/dist/index.js +0 -2
- package/dist/index.js.map +0 -1
- package/dist/worker.js +0 -2
- package/dist/worker.js.map +0 -1
- package/src/FedimintWallet.test.ts +0 -73
- package/src/FedimintWallet.ts +0 -119
- package/src/WalletDirector.ts +0 -118
- package/src/index.ts +0 -3
- package/src/services/BalanceService.test.ts +0 -26
- package/src/services/BalanceService.ts +0 -29
- package/src/services/FederationService.test.ts +0 -58
- package/src/services/FederationService.ts +0 -216
- package/src/services/LightningService.test.ts +0 -265
- package/src/services/LightningService.ts +0 -289
- package/src/services/MintService.test.ts +0 -74
- package/src/services/MintService.ts +0 -129
- package/src/services/RecoveryService.ts +0 -28
- package/src/services/WalletService.test.ts +0 -59
- package/src/services/WalletService.ts +0 -50
- package/src/services/index.ts +0 -6
- package/src/test/TestFedimintWallet.ts +0 -31
- package/src/test/TestWalletDirector.ts +0 -14
- package/src/test/TestingService.ts +0 -79
- package/src/test/crypto.ts +0 -44
- package/src/test/fixtures.test.ts +0 -18
- package/src/test/fixtures.ts +0 -88
- package/src/transport/TransportClient.test.ts +0 -6
- package/src/transport/TransportClient.ts +0 -251
- package/src/transport/index.ts +0 -2
- package/src/transport/wasmTransport/WasmWorkerTransport.ts +0 -39
- package/src/transport/wasmTransport/worker.js +0 -167
- package/src/transport/wasmTransport/worker.test.ts +0 -90
- package/src/types/index.ts +0 -3
- package/src/types/transport.ts +0 -54
- package/src/types/utils.ts +0 -29
- package/src/types/wallet.ts +0 -298
- package/src/utils/logger.ts +0 -69
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { LightningService } from '../services'
|
|
2
|
-
import { TransportClient } from '../transport'
|
|
3
|
-
|
|
4
|
-
export const TESTING_INVITE =
|
|
5
|
-
'fed11qgqrsdnhwden5te0v9cxjtt4dekxzamxw4kz6mmjvvkhydted9ukg6r9xfsnx7th0fhn26tf093juamwv4u8gtnpwpcz7qqpyz0e327ua8geceutfrcaezwt22mk6s2rdy09kg72jrcmncng2gn0kp2m5sk'
|
|
6
|
-
|
|
7
|
-
// This is a testing service that allows for inspecting the internals
|
|
8
|
-
// of the TransportClient. It is not intended for use in production.
|
|
9
|
-
export class TestingService {
|
|
10
|
-
public TESTING_INVITE: string
|
|
11
|
-
constructor(
|
|
12
|
-
private client: TransportClient,
|
|
13
|
-
private lightning: LightningService,
|
|
14
|
-
) {
|
|
15
|
-
// Solo Mint on mutinynet
|
|
16
|
-
this.TESTING_INVITE = TESTING_INVITE
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
getRequestCounter() {
|
|
20
|
-
return this.client._getRequestCounter()
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
getRequestCallbackMap() {
|
|
24
|
-
return this.client._getRequestCallbackMap()
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async getInviteCode() {
|
|
28
|
-
const res = await fetch(`${import.meta.env.FAUCET}/connect-string`)
|
|
29
|
-
if (res.ok) {
|
|
30
|
-
return await res.text()
|
|
31
|
-
} else {
|
|
32
|
-
throw new Error(`Failed to get invite code: ${await res.text()}`)
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
private async getFaucetGatewayApi() {
|
|
37
|
-
const res = await fetch(`${import.meta.env.FAUCET}/gateway-api`)
|
|
38
|
-
if (res.ok) {
|
|
39
|
-
return await res.text()
|
|
40
|
-
} else {
|
|
41
|
-
throw new Error(`Failed to get gateway: ${await res.text()}`)
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async getFaucetGatewayInfo() {
|
|
46
|
-
await this.lightning.updateGatewayCache()
|
|
47
|
-
const gateways = await this.lightning.listGateways()
|
|
48
|
-
const api = await this.getFaucetGatewayApi()
|
|
49
|
-
const gateway = gateways.find((g) => g.info.api === api)
|
|
50
|
-
if (!gateway) {
|
|
51
|
-
throw new Error(`Gateway not found: ${api}`)
|
|
52
|
-
}
|
|
53
|
-
return gateway.info
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async payFaucetInvoice(invoice: string) {
|
|
57
|
-
const res = await fetch(`${import.meta.env.FAUCET}/pay`, {
|
|
58
|
-
method: 'POST',
|
|
59
|
-
body: invoice,
|
|
60
|
-
})
|
|
61
|
-
if (res.ok) {
|
|
62
|
-
return await res.text()
|
|
63
|
-
} else {
|
|
64
|
-
throw new Error(`Failed to pay faucet invoice: ${await res.text()}`)
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
async createFaucetInvoice(amount: number) {
|
|
69
|
-
const res = await fetch(`${import.meta.env.FAUCET}/invoice`, {
|
|
70
|
-
method: 'POST',
|
|
71
|
-
body: amount.toString(),
|
|
72
|
-
})
|
|
73
|
-
if (res.ok) {
|
|
74
|
-
return await res.text()
|
|
75
|
-
} else {
|
|
76
|
-
throw new Error(`Failed to generate faucet invoice: ${await res.text()}`)
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
package/src/test/crypto.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import * as secp256k1 from 'secp256k1'
|
|
2
|
-
|
|
3
|
-
const randomBytes = (size: number): Uint8Array => {
|
|
4
|
-
const array = new Uint8Array(size)
|
|
5
|
-
window.crypto.getRandomValues(array)
|
|
6
|
-
return array
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
interface KeyPair {
|
|
10
|
-
secretKey: string
|
|
11
|
-
publicKey: string
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const keyPair = (secretKey?: Uint8Array): KeyPair => {
|
|
15
|
-
const privateKey: Uint8Array = secretKey
|
|
16
|
-
? validatePrivateKey(secretKey)
|
|
17
|
-
: generatePrivateKey()
|
|
18
|
-
|
|
19
|
-
const publicKey = secp256k1.publicKeyCreate(privateKey)
|
|
20
|
-
|
|
21
|
-
return {
|
|
22
|
-
secretKey: Array.from(privateKey)
|
|
23
|
-
.map((b) => b.toString(16).padStart(2, '0'))
|
|
24
|
-
.join(''), // Convert Uint8Array to hex string
|
|
25
|
-
publicKey: Array.from(publicKey)
|
|
26
|
-
.map((b) => b.toString(16).padStart(2, '0'))
|
|
27
|
-
.join(''), // Convert Uint8Array to hex string
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const validatePrivateKey = (key: Uint8Array): Uint8Array => {
|
|
32
|
-
if (!secp256k1.privateKeyVerify(key)) {
|
|
33
|
-
throw new Error('Invalid private key provided')
|
|
34
|
-
}
|
|
35
|
-
return key
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const generatePrivateKey = (): Uint8Array => {
|
|
39
|
-
let key: Uint8Array
|
|
40
|
-
do {
|
|
41
|
-
key = randomBytes(32)
|
|
42
|
-
} while (!secp256k1.privateKeyVerify(key))
|
|
43
|
-
return key
|
|
44
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { expect } from 'vitest'
|
|
2
|
-
import { walletTest } from './fixtures'
|
|
3
|
-
|
|
4
|
-
walletTest('Fund wallet 1', async ({ fundedWallet }) => {
|
|
5
|
-
expect(fundedWallet).toBeDefined()
|
|
6
|
-
})
|
|
7
|
-
|
|
8
|
-
walletTest('Fund wallet 2', async ({ fundedWallet }) => {
|
|
9
|
-
expect(fundedWallet).toBeDefined()
|
|
10
|
-
})
|
|
11
|
-
|
|
12
|
-
walletTest('Fund wallet 3', async ({ fundedWallet }) => {
|
|
13
|
-
expect(fundedWallet).toBeDefined()
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
walletTest('Fund wallet 4', async ({ fundedWallet }) => {
|
|
17
|
-
expect(fundedWallet).toBeDefined()
|
|
18
|
-
})
|
package/src/test/fixtures.ts
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { expect, test } from 'vitest'
|
|
2
|
-
import { TestFedimintWallet } from './TestFedimintWallet'
|
|
3
|
-
import { TransportClient } from '../transport/TransportClient'
|
|
4
|
-
import { WasmWorkerTransport } from '../transport/wasmTransport/WasmWorkerTransport'
|
|
5
|
-
import { TestWalletDirector } from './TestWalletDirector'
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Adds Fixtures for setting up and tearing down test FedimintWallet/WalletDirector instances
|
|
9
|
-
*/
|
|
10
|
-
export const walletTest = test.extend<{
|
|
11
|
-
walletDirector: TestWalletDirector
|
|
12
|
-
wallet: TestFedimintWallet
|
|
13
|
-
fundedWallet: TestFedimintWallet
|
|
14
|
-
fundedWalletBeefy: TestFedimintWallet
|
|
15
|
-
unopenedWallet: TestFedimintWallet
|
|
16
|
-
}>({
|
|
17
|
-
walletDirector: async ({}, use) => {
|
|
18
|
-
const walletDirector = new TestWalletDirector()
|
|
19
|
-
await use(walletDirector)
|
|
20
|
-
},
|
|
21
|
-
wallet: async ({ walletDirector }, use) => {
|
|
22
|
-
const randomTestingId = Math.random().toString(36).substring(2, 15)
|
|
23
|
-
const wallet = await walletDirector.createTestWallet()
|
|
24
|
-
expect(wallet).toBeDefined()
|
|
25
|
-
const inviteCode = await wallet.testing.getInviteCode()
|
|
26
|
-
await expect(
|
|
27
|
-
wallet.joinFederation(inviteCode, randomTestingId),
|
|
28
|
-
).resolves.toBe(true)
|
|
29
|
-
|
|
30
|
-
await use(wallet)
|
|
31
|
-
|
|
32
|
-
// clear up browser resources
|
|
33
|
-
await wallet.cleanup()
|
|
34
|
-
|
|
35
|
-
// remove the wallet db
|
|
36
|
-
await new Promise((resolve) => {
|
|
37
|
-
const request = indexedDB.deleteDatabase(randomTestingId)
|
|
38
|
-
request.onsuccess = resolve
|
|
39
|
-
request.onerror = resolve
|
|
40
|
-
request.onblocked = resolve
|
|
41
|
-
})
|
|
42
|
-
},
|
|
43
|
-
|
|
44
|
-
fundedWallet: async ({ wallet }, use) => {
|
|
45
|
-
// 10K MSats
|
|
46
|
-
await wallet.fundWallet(10_000)
|
|
47
|
-
await use(wallet)
|
|
48
|
-
},
|
|
49
|
-
fundedWalletBeefy: async ({ wallet }, use) => {
|
|
50
|
-
// 1M MSats
|
|
51
|
-
await wallet.fundWallet(1_000_000)
|
|
52
|
-
await use(wallet)
|
|
53
|
-
},
|
|
54
|
-
unopenedWallet: async ({ walletDirector }, use) => {
|
|
55
|
-
const wallet = await walletDirector.createTestWallet()
|
|
56
|
-
await use(wallet)
|
|
57
|
-
},
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Adds Fixtures for setting up and tearing down a test Worker instance
|
|
62
|
-
*/
|
|
63
|
-
export const workerTest = test.extend<{
|
|
64
|
-
worker: Worker
|
|
65
|
-
clientName: string
|
|
66
|
-
transportClient: TransportClient
|
|
67
|
-
}>({
|
|
68
|
-
worker: async ({}, use) => {
|
|
69
|
-
const worker = new Worker(
|
|
70
|
-
new URL('../transport/wasmTransport/worker.js', import.meta.url),
|
|
71
|
-
{
|
|
72
|
-
type: 'module',
|
|
73
|
-
},
|
|
74
|
-
)
|
|
75
|
-
await use(worker)
|
|
76
|
-
worker.terminate()
|
|
77
|
-
},
|
|
78
|
-
clientName: async ({}, use) => {
|
|
79
|
-
const randomTestingId = Math.random().toString(36).substring(2, 15)
|
|
80
|
-
await use(randomTestingId)
|
|
81
|
-
},
|
|
82
|
-
transportClient: async ({}, use) => {
|
|
83
|
-
// TODO: figure out how to use a different transport in runtime depending on the test
|
|
84
|
-
// Ideally, we don't want to create separate fixtures for each transport
|
|
85
|
-
const transportClient = new TransportClient(new WasmWorkerTransport())
|
|
86
|
-
await use(transportClient)
|
|
87
|
-
},
|
|
88
|
-
})
|
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
CancelFunction,
|
|
3
|
-
JSONValue,
|
|
4
|
-
ModuleKind,
|
|
5
|
-
StreamError,
|
|
6
|
-
StreamResult,
|
|
7
|
-
TransportMessageType,
|
|
8
|
-
} from '../types'
|
|
9
|
-
import { Logger } from '../utils/logger'
|
|
10
|
-
import type { Transport, TransportMessage } from '../types/transport'
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Handles communication with a generic transport.
|
|
14
|
-
* Must be instantiated with a platform-specific transport. (wasm for web, react native, etc.)
|
|
15
|
-
*/
|
|
16
|
-
export class TransportClient {
|
|
17
|
-
// Generic Transport. Can be wasm, react native, node, etc.
|
|
18
|
-
private readonly transport: Transport
|
|
19
|
-
private requestCounter = 0
|
|
20
|
-
private requestCallbacks = new Map<number, (value: any) => void>()
|
|
21
|
-
private initPromise: Promise<boolean> | undefined = undefined
|
|
22
|
-
logger: Logger
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* @summary Constructor for the TransportClient
|
|
26
|
-
* @param transport - The platform-specific transport to use. (wasm for web, react native, etc.)
|
|
27
|
-
*/
|
|
28
|
-
constructor(transport: Transport) {
|
|
29
|
-
this.transport = transport
|
|
30
|
-
this.logger = new Logger(transport.logger)
|
|
31
|
-
this.transport.setMessageHandler(this.handleTransportMessage)
|
|
32
|
-
this.transport.setErrorHandler(this.handleTransportError)
|
|
33
|
-
this.logger.info('TransportClient instantiated')
|
|
34
|
-
this.logger.debug('TransportClient transport', transport)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Idempotent setup - Loads the wasm module
|
|
38
|
-
initialize() {
|
|
39
|
-
if (this.initPromise) return this.initPromise
|
|
40
|
-
this.initPromise = this.sendSingleMessage('init')
|
|
41
|
-
return this.initPromise
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
private handleLogMessage(message: TransportMessage) {
|
|
45
|
-
const { type, level, message: logMessage, ...data } = message
|
|
46
|
-
this.logger.info(String(level), String(logMessage), data)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
private handleTransportError = (error: unknown) => {
|
|
50
|
-
this.logger.error('TransportClient error', error)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
private handleTransportMessage = (message: TransportMessage) => {
|
|
54
|
-
const { type, requestId, ...data } = message
|
|
55
|
-
if (type === 'log') {
|
|
56
|
-
this.handleLogMessage(message)
|
|
57
|
-
}
|
|
58
|
-
const streamCallback =
|
|
59
|
-
requestId !== undefined ? this.requestCallbacks.get(requestId) : undefined
|
|
60
|
-
// TODO: Handle errors... maybe have another callbacks list for errors?
|
|
61
|
-
this.logger.debug('TransportClient - handleTransportMessage', message)
|
|
62
|
-
if (streamCallback) {
|
|
63
|
-
streamCallback(data) // {data: something} OR {error: something}
|
|
64
|
-
} else if (requestId !== undefined) {
|
|
65
|
-
this.logger.warn(
|
|
66
|
-
'TransportClient - handleTransportMessage - received message with no callback',
|
|
67
|
-
requestId,
|
|
68
|
-
message,
|
|
69
|
-
)
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// TODO: Handle errors... maybe have another callbacks list for errors?
|
|
74
|
-
// TODO: Handle timeouts
|
|
75
|
-
// TODO: Handle multiple errors
|
|
76
|
-
|
|
77
|
-
sendSingleMessage<
|
|
78
|
-
Response extends JSONValue = JSONValue,
|
|
79
|
-
Payload extends JSONValue = JSONValue,
|
|
80
|
-
>(type: TransportMessageType, payload?: Payload) {
|
|
81
|
-
return new Promise<Response>((resolve, reject) => {
|
|
82
|
-
const requestId = ++this.requestCounter
|
|
83
|
-
this.logger.debug(
|
|
84
|
-
'TransportClient - sendSingleMessage',
|
|
85
|
-
requestId,
|
|
86
|
-
type,
|
|
87
|
-
payload,
|
|
88
|
-
)
|
|
89
|
-
this.requestCallbacks.set(
|
|
90
|
-
requestId,
|
|
91
|
-
(response: StreamResult<Response>) => {
|
|
92
|
-
this.requestCallbacks.delete(requestId)
|
|
93
|
-
this.logger.debug(
|
|
94
|
-
'TransportClient - sendSingleMessage - response',
|
|
95
|
-
requestId,
|
|
96
|
-
response,
|
|
97
|
-
)
|
|
98
|
-
if (response.data) resolve(response.data)
|
|
99
|
-
else if (response.error) reject(response.error)
|
|
100
|
-
else
|
|
101
|
-
this.logger.warn(
|
|
102
|
-
'TransportClient - sendSingleMessage - malformed response',
|
|
103
|
-
requestId,
|
|
104
|
-
response,
|
|
105
|
-
)
|
|
106
|
-
},
|
|
107
|
-
)
|
|
108
|
-
this.transport.postMessage({ type, payload, requestId })
|
|
109
|
-
})
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* @summary Initiates an RPC stream with the specified module and method.
|
|
114
|
-
*
|
|
115
|
-
* @description
|
|
116
|
-
* This function sets up an RPC stream by sending a request to a worker and
|
|
117
|
-
* handling responses asynchronously. It ensures that unsubscription is handled
|
|
118
|
-
* correctly, even if the unsubscribe function is called before the subscription
|
|
119
|
-
* is fully established, by deferring the unsubscription attempt using `setTimeout`.
|
|
120
|
-
*
|
|
121
|
-
* The function operates in a non-blocking manner, leveraging Promises to manage
|
|
122
|
-
* asynchronous operations and callbacks to handle responses.
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
* @template Response - The expected type of the successful response.
|
|
126
|
-
* @template Body - The type of the request body.
|
|
127
|
-
* @param module - The module kind to interact with.
|
|
128
|
-
* @param method - The method name to invoke on the module.
|
|
129
|
-
* @param body - The request payload.
|
|
130
|
-
* @param onSuccess - Callback invoked with the response data on success.
|
|
131
|
-
* @param onError - Callback invoked with error information if an error occurs.
|
|
132
|
-
* @param onEnd - Optional callback invoked when the stream ends.
|
|
133
|
-
* @returns A function that can be called to cancel the subscription.
|
|
134
|
-
*
|
|
135
|
-
*/
|
|
136
|
-
rpcStream<
|
|
137
|
-
Response extends JSONValue = JSONValue,
|
|
138
|
-
Body extends JSONValue = JSONValue,
|
|
139
|
-
>(
|
|
140
|
-
module: ModuleKind,
|
|
141
|
-
method: string,
|
|
142
|
-
body: Body,
|
|
143
|
-
onSuccess: (res: Response) => void,
|
|
144
|
-
onError: (res: StreamError['error']) => void,
|
|
145
|
-
onEnd: () => void = () => {},
|
|
146
|
-
): CancelFunction {
|
|
147
|
-
const requestId = ++this.requestCounter
|
|
148
|
-
this.logger.debug(
|
|
149
|
-
'TransportClient - rpcStream',
|
|
150
|
-
requestId,
|
|
151
|
-
module,
|
|
152
|
-
method,
|
|
153
|
-
body,
|
|
154
|
-
)
|
|
155
|
-
let unsubscribe: (value: void) => void = () => {}
|
|
156
|
-
let isSubscribed = false
|
|
157
|
-
|
|
158
|
-
const unsubscribePromise = new Promise<void>((resolve) => {
|
|
159
|
-
unsubscribe = () => {
|
|
160
|
-
if (isSubscribed) {
|
|
161
|
-
// If already subscribed, resolve immediately to trigger unsubscription
|
|
162
|
-
resolve()
|
|
163
|
-
} else {
|
|
164
|
-
// If not yet subscribed, defer the unsubscribe attempt to the next event loop tick
|
|
165
|
-
// This ensures that subscription setup has time to complete
|
|
166
|
-
setTimeout(() => unsubscribe(), 0)
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
// Initiate the inner RPC stream setup asynchronously
|
|
172
|
-
this._rpcStreamInner(
|
|
173
|
-
requestId,
|
|
174
|
-
module,
|
|
175
|
-
method,
|
|
176
|
-
body,
|
|
177
|
-
onSuccess,
|
|
178
|
-
onError,
|
|
179
|
-
onEnd,
|
|
180
|
-
unsubscribePromise,
|
|
181
|
-
).then(() => {
|
|
182
|
-
isSubscribed = true
|
|
183
|
-
})
|
|
184
|
-
|
|
185
|
-
return unsubscribe
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
private async _rpcStreamInner<
|
|
189
|
-
Response extends JSONValue = JSONValue,
|
|
190
|
-
Body extends JSONValue = JSONValue,
|
|
191
|
-
>(
|
|
192
|
-
requestId: number,
|
|
193
|
-
module: ModuleKind,
|
|
194
|
-
method: string,
|
|
195
|
-
body: Body,
|
|
196
|
-
onSuccess: (res: Response) => void,
|
|
197
|
-
onError: (res: StreamError['error']) => void,
|
|
198
|
-
onEnd: () => void = () => {},
|
|
199
|
-
unsubscribePromise: Promise<void>,
|
|
200
|
-
// Unsubscribe function
|
|
201
|
-
) {
|
|
202
|
-
this.requestCallbacks.set(requestId, (response: StreamResult<Response>) => {
|
|
203
|
-
if (response.error !== undefined) {
|
|
204
|
-
onError(response.error)
|
|
205
|
-
} else if (response.data !== undefined) {
|
|
206
|
-
onSuccess(response.data)
|
|
207
|
-
} else if (response.end !== undefined) {
|
|
208
|
-
this.requestCallbacks.delete(requestId)
|
|
209
|
-
onEnd()
|
|
210
|
-
}
|
|
211
|
-
})
|
|
212
|
-
this.transport.postMessage({
|
|
213
|
-
type: 'rpc',
|
|
214
|
-
payload: { module, method, body },
|
|
215
|
-
requestId,
|
|
216
|
-
})
|
|
217
|
-
|
|
218
|
-
unsubscribePromise.then(() => {
|
|
219
|
-
this.transport.postMessage({
|
|
220
|
-
type: 'unsubscribe',
|
|
221
|
-
requestId,
|
|
222
|
-
})
|
|
223
|
-
this.requestCallbacks.delete(requestId)
|
|
224
|
-
})
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
rpcSingle<
|
|
228
|
-
Response extends JSONValue = JSONValue,
|
|
229
|
-
Error extends string = string,
|
|
230
|
-
>(module: ModuleKind, method: string, body: JSONValue) {
|
|
231
|
-
this.logger.debug('TransportClient - rpcSingle', module, method, body)
|
|
232
|
-
return new Promise<Response>((resolve, reject) => {
|
|
233
|
-
this.rpcStream<Response>(module, method, body, resolve, reject)
|
|
234
|
-
})
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
async cleanup() {
|
|
238
|
-
await this.sendSingleMessage('cleanup')
|
|
239
|
-
this.requestCounter = 0
|
|
240
|
-
this.initPromise = undefined
|
|
241
|
-
this.requestCallbacks.clear()
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// For Testing
|
|
245
|
-
_getRequestCounter() {
|
|
246
|
-
return this.requestCounter
|
|
247
|
-
}
|
|
248
|
-
_getRequestCallbackMap() {
|
|
249
|
-
return this.requestCallbacks
|
|
250
|
-
}
|
|
251
|
-
}
|
package/src/transport/index.ts
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
Transport,
|
|
3
|
-
TransportErrorHandler,
|
|
4
|
-
TransportLogger,
|
|
5
|
-
TransportMessageHandler,
|
|
6
|
-
TransportRequest,
|
|
7
|
-
} from '../../types/transport'
|
|
8
|
-
|
|
9
|
-
export class WasmWorkerTransport implements Transport {
|
|
10
|
-
private messageHandler: TransportMessageHandler = () => {}
|
|
11
|
-
private errorHandler: TransportErrorHandler = () => {}
|
|
12
|
-
private readonly worker: Worker
|
|
13
|
-
|
|
14
|
-
logger: TransportLogger = console
|
|
15
|
-
|
|
16
|
-
constructor() {
|
|
17
|
-
this.worker = new Worker(new URL('./worker.js', import.meta.url), {
|
|
18
|
-
type: 'module',
|
|
19
|
-
})
|
|
20
|
-
this.worker.onmessage = (event: MessageEvent) => {
|
|
21
|
-
this.messageHandler(event.data)
|
|
22
|
-
}
|
|
23
|
-
this.worker.onerror = (event: ErrorEvent) => {
|
|
24
|
-
this.errorHandler(event)
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
postMessage(message: TransportRequest) {
|
|
29
|
-
this.worker.postMessage(message)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
setMessageHandler(handler: TransportMessageHandler) {
|
|
33
|
-
this.messageHandler = handler
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
setErrorHandler(handler: TransportErrorHandler) {
|
|
37
|
-
this.errorHandler = handler
|
|
38
|
-
}
|
|
39
|
-
}
|
|
@@ -1,167 +0,0 @@
|
|
|
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/transport').TransportMessageType} 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: {} })
|