@bsv/message-box-client 2.0.1 → 2.0.3
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/dist/cjs/mod.js +1 -0
- package/dist/cjs/mod.js.map +1 -1
- package/dist/cjs/package.json +5 -5
- package/dist/cjs/src/MessageBoxClient.js +94 -29
- package/dist/cjs/src/MessageBoxClient.js.map +1 -1
- package/dist/cjs/src/RemittanceAdapter.js +137 -0
- package/dist/cjs/src/RemittanceAdapter.js.map +1 -0
- package/dist/cjs/src/__tests/RemittanceAdapter.test.js +133 -0
- package/dist/cjs/src/__tests/RemittanceAdapter.test.js.map +1 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/mod.js +1 -0
- package/dist/esm/mod.js.map +1 -1
- package/dist/esm/src/MessageBoxClient.js +94 -29
- package/dist/esm/src/MessageBoxClient.js.map +1 -1
- package/dist/esm/src/RemittanceAdapter.js +133 -0
- package/dist/esm/src/RemittanceAdapter.js.map +1 -0
- package/dist/esm/src/__tests/RemittanceAdapter.test.js +131 -0
- package/dist/esm/src/__tests/RemittanceAdapter.test.js.map +1 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/mod.d.ts +1 -0
- package/dist/types/mod.d.ts.map +1 -1
- package/dist/types/src/MessageBoxClient.d.ts +2 -0
- package/dist/types/src/MessageBoxClient.d.ts.map +1 -1
- package/dist/types/src/RemittanceAdapter.d.ts +103 -0
- package/dist/types/src/RemittanceAdapter.d.ts.map +1 -0
- package/dist/types/src/__tests/RemittanceAdapter.test.d.ts +2 -0
- package/dist/types/src/__tests/RemittanceAdapter.test.d.ts.map +1 -0
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/dist/umd/bundle.js +1 -1
- package/mod.ts +1 -0
- package/package.json +5 -5
- package/src/MessageBoxClient.ts +102 -28
- package/src/RemittanceAdapter.ts +164 -0
- package/src/__tests/RemittanceAdapter.test.ts +153 -0
package/mod.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bsv/message-box-client",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -54,8 +54,8 @@
|
|
|
54
54
|
"author": "BSV Blockchain Association",
|
|
55
55
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@bsv/auth-express-middleware": "^2.0.
|
|
58
|
-
"@bsv/payment-express-middleware": "^2.0.
|
|
57
|
+
"@bsv/auth-express-middleware": "^2.0.4",
|
|
58
|
+
"@bsv/payment-express-middleware": "^2.0.1",
|
|
59
59
|
"@eslint/js": "^9.20.0",
|
|
60
60
|
"@types/jest": "^29.5.14",
|
|
61
61
|
"@types/node": "^22.13.2",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"webpack-merge": "^6.0.1"
|
|
83
83
|
},
|
|
84
84
|
"dependencies": {
|
|
85
|
-
"@bsv/authsocket-client": "^2.0.
|
|
86
|
-
"@bsv/sdk": "^2.0.
|
|
85
|
+
"@bsv/authsocket-client": "^2.0.2",
|
|
86
|
+
"@bsv/sdk": "^2.0.4"
|
|
87
87
|
}
|
|
88
88
|
}
|
package/src/MessageBoxClient.ts
CHANGED
|
@@ -96,6 +96,8 @@ export class MessageBoxClient {
|
|
|
96
96
|
private readonly lookupResolver: LookupResolver
|
|
97
97
|
private readonly networkPreset: 'local' | 'mainnet' | 'testnet'
|
|
98
98
|
private initialized = false
|
|
99
|
+
private socketAuthenticated = false
|
|
100
|
+
private connectionInitPromise?: Promise<void>
|
|
99
101
|
protected originator?: OriginatorDomainNameStringUnder250Bytes
|
|
100
102
|
/**
|
|
101
103
|
* @constructor
|
|
@@ -131,7 +133,7 @@ export class MessageBoxClient {
|
|
|
131
133
|
} = options
|
|
132
134
|
|
|
133
135
|
const defaultHost =
|
|
134
|
-
|
|
136
|
+
networkPreset === 'testnet'
|
|
135
137
|
? DEFAULT_TESTNET_HOST
|
|
136
138
|
: DEFAULT_MAINNET_HOST
|
|
137
139
|
|
|
@@ -313,6 +315,15 @@ export class MessageBoxClient {
|
|
|
313
315
|
|
|
314
316
|
Logger.log('[MB CLIENT] Setting up WebSocket connection...')
|
|
315
317
|
|
|
318
|
+
if (this.socketAuthenticated && this.socket != null) {
|
|
319
|
+
return
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (this.connectionInitPromise != null) {
|
|
323
|
+
await this.connectionInitPromise
|
|
324
|
+
return
|
|
325
|
+
}
|
|
326
|
+
|
|
316
327
|
if (this.socket == null) {
|
|
317
328
|
const targetHost = overrideHost ?? this.host
|
|
318
329
|
if (typeof targetHost !== 'string' || targetHost.trim() === '') {
|
|
@@ -320,58 +331,115 @@ export class MessageBoxClient {
|
|
|
320
331
|
}
|
|
321
332
|
this.socket = AuthSocketClient(targetHost, { wallet: this.walletClient, originator: this.originator })
|
|
322
333
|
|
|
323
|
-
let identitySent = false
|
|
324
|
-
let authenticated = false
|
|
325
|
-
|
|
326
334
|
this.socket.on('connect', () => {
|
|
327
335
|
Logger.log('[MB CLIENT] Connected to WebSocket.')
|
|
328
336
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
this.socket?.emit('authenticated', { identityKey: this.myIdentityKey })
|
|
335
|
-
identitySent = true
|
|
336
|
-
}
|
|
337
|
+
Logger.log('[MB CLIENT] Sending authentication data:', this.myIdentityKey)
|
|
338
|
+
if (this.myIdentityKey == null || this.myIdentityKey.trim() === '') {
|
|
339
|
+
Logger.error('[MB CLIENT ERROR] Cannot send authentication: Identity key is missing!')
|
|
340
|
+
} else {
|
|
341
|
+
this.socket?.emit('authenticated', { identityKey: this.myIdentityKey })
|
|
337
342
|
}
|
|
338
343
|
})
|
|
339
344
|
|
|
340
345
|
// Listen for authentication success from the server
|
|
341
346
|
this.socket.on('authenticationSuccess', (data) => {
|
|
342
347
|
Logger.log(`[MB CLIENT] WebSocket authentication successful: ${JSON.stringify(data)}`)
|
|
343
|
-
|
|
348
|
+
this.socketAuthenticated = true
|
|
344
349
|
})
|
|
345
350
|
|
|
346
351
|
// Handle authentication failures
|
|
347
352
|
this.socket.on('authenticationFailed', (data) => {
|
|
348
353
|
Logger.error(`[MB CLIENT ERROR] WebSocket authentication failed: ${JSON.stringify(data)}`)
|
|
349
|
-
|
|
354
|
+
this.socketAuthenticated = false
|
|
350
355
|
})
|
|
351
356
|
|
|
352
357
|
this.socket.on('disconnect', () => {
|
|
353
358
|
Logger.log('[MB CLIENT] Disconnected from MessageBox server')
|
|
354
359
|
this.socket = undefined
|
|
355
|
-
|
|
356
|
-
authenticated = false
|
|
360
|
+
this.socketAuthenticated = false
|
|
357
361
|
})
|
|
358
362
|
|
|
359
363
|
this.socket.on('error', (error) => {
|
|
360
364
|
Logger.error('[MB CLIENT ERROR] WebSocket error:', error)
|
|
361
365
|
})
|
|
366
|
+
}
|
|
362
367
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
setTimeout(() => {
|
|
366
|
-
if (authenticated) {
|
|
367
|
-
Logger.log('[MB CLIENT] WebSocket fully authenticated and ready!')
|
|
368
|
-
resolve()
|
|
369
|
-
} else {
|
|
370
|
-
reject(new Error('[MB CLIENT ERROR] WebSocket authentication timed out!'))
|
|
371
|
-
}
|
|
372
|
-
}, 5000) // Timeout after 5 seconds
|
|
373
|
-
})
|
|
368
|
+
if (this.socket?.connected && !this.socketAuthenticated) {
|
|
369
|
+
this.socket.emit('authenticated', { identityKey: this.myIdentityKey })
|
|
374
370
|
}
|
|
371
|
+
|
|
372
|
+
this.connectionInitPromise = new Promise<void>((resolve, reject) => {
|
|
373
|
+
const socketAny = this.socket as any
|
|
374
|
+
let settled = false
|
|
375
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined
|
|
376
|
+
|
|
377
|
+
const finalizeResolve = (): void => {
|
|
378
|
+
if (settled) return
|
|
379
|
+
settled = true
|
|
380
|
+
if (timeoutId != null) {
|
|
381
|
+
clearTimeout(timeoutId)
|
|
382
|
+
timeoutId = undefined
|
|
383
|
+
}
|
|
384
|
+
if (typeof socketAny?.off === 'function') {
|
|
385
|
+
socketAny.off('authenticationSuccess', onSuccess)
|
|
386
|
+
socketAny.off('authenticationFailed', onFailed)
|
|
387
|
+
socketAny.off('disconnect', onDisconnectBeforeAuth)
|
|
388
|
+
}
|
|
389
|
+
this.connectionInitPromise = undefined
|
|
390
|
+
Logger.log('[MB CLIENT] WebSocket fully authenticated and ready!')
|
|
391
|
+
resolve()
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const finalizeReject = (error: Error): void => {
|
|
395
|
+
if (settled) return
|
|
396
|
+
settled = true
|
|
397
|
+
if (timeoutId != null) {
|
|
398
|
+
clearTimeout(timeoutId)
|
|
399
|
+
timeoutId = undefined
|
|
400
|
+
}
|
|
401
|
+
if (typeof socketAny?.off === 'function') {
|
|
402
|
+
socketAny.off('authenticationSuccess', onSuccess)
|
|
403
|
+
socketAny.off('authenticationFailed', onFailed)
|
|
404
|
+
socketAny.off('disconnect', onDisconnectBeforeAuth)
|
|
405
|
+
}
|
|
406
|
+
this.connectionInitPromise = undefined
|
|
407
|
+
reject(error)
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const onSuccess = (): void => {
|
|
411
|
+
this.socketAuthenticated = true
|
|
412
|
+
finalizeResolve()
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const onFailed = (): void => {
|
|
416
|
+
this.socketAuthenticated = false
|
|
417
|
+
finalizeReject(new Error('[MB CLIENT ERROR] WebSocket authentication failed!'))
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const onDisconnectBeforeAuth = (): void => {
|
|
421
|
+
this.socketAuthenticated = false
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (this.socketAuthenticated) {
|
|
425
|
+
finalizeResolve()
|
|
426
|
+
return
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
socketAny?.on('authenticationSuccess', onSuccess)
|
|
430
|
+
socketAny?.on('authenticationFailed', onFailed)
|
|
431
|
+
socketAny?.on('disconnect', onDisconnectBeforeAuth)
|
|
432
|
+
|
|
433
|
+
timeoutId = setTimeout(() => {
|
|
434
|
+
if (this.socketAuthenticated) {
|
|
435
|
+
finalizeResolve()
|
|
436
|
+
} else {
|
|
437
|
+
finalizeReject(new Error('[MB CLIENT ERROR] WebSocket authentication timed out!'))
|
|
438
|
+
}
|
|
439
|
+
}, 5000)
|
|
440
|
+
})
|
|
441
|
+
|
|
442
|
+
await this.connectionInitPromise
|
|
375
443
|
}
|
|
376
444
|
|
|
377
445
|
/**
|
|
@@ -700,10 +768,15 @@ export class MessageBoxClient {
|
|
|
700
768
|
return await new Promise((resolve, reject) => {
|
|
701
769
|
const ackEvent = `sendMessageAck-${roomId}`
|
|
702
770
|
let handled = false
|
|
771
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined
|
|
703
772
|
|
|
704
773
|
const ackHandler = (response?: SendMessageResponse): void => {
|
|
705
774
|
if (handled) return
|
|
706
775
|
handled = true
|
|
776
|
+
if (timeoutId != null) {
|
|
777
|
+
clearTimeout(timeoutId)
|
|
778
|
+
timeoutId = undefined
|
|
779
|
+
}
|
|
707
780
|
|
|
708
781
|
const socketAny = this.socket as any
|
|
709
782
|
if (typeof socketAny?.off === 'function') {
|
|
@@ -749,9 +822,10 @@ export class MessageBoxClient {
|
|
|
749
822
|
})
|
|
750
823
|
|
|
751
824
|
// Timeout: Fallback to HTTP if no acknowledgment received
|
|
752
|
-
setTimeout(() => {
|
|
825
|
+
timeoutId = setTimeout(() => {
|
|
753
826
|
if (!handled) {
|
|
754
827
|
handled = true
|
|
828
|
+
timeoutId = undefined
|
|
755
829
|
const socketAny = this.socket as any
|
|
756
830
|
if (typeof socketAny?.off === 'function') {
|
|
757
831
|
socketAny.off(ackEvent, ackHandler)
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RemittanceAdapter - Adapts MessageBoxClient to the CommsLayer interface
|
|
3
|
+
*
|
|
4
|
+
* This adapter bridges MessageBoxClient with the ts-sdk RemittanceManager by implementing
|
|
5
|
+
* the CommsLayer interface. It handles the protocol differences between the two systems,
|
|
6
|
+
* particularly around message body format (MessageBoxClient returns parsed objects,
|
|
7
|
+
* RemittanceManager expects JSON strings).
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { RemittanceAdapter } from '@bsv/message-box-client'
|
|
12
|
+
* import { RemittanceManager } from '@bsv/sdk'
|
|
13
|
+
* import { MessageBoxClient } from '@bsv/message-box-client'
|
|
14
|
+
* import { WalletClient } from '@bsv/sdk'
|
|
15
|
+
*
|
|
16
|
+
* const wallet = new WalletClient()
|
|
17
|
+
* const messageBox = new MessageBoxClient({ walletClient: wallet })
|
|
18
|
+
* const commsLayer = new RemittanceAdapter(messageBox)
|
|
19
|
+
*
|
|
20
|
+
* const manager = new RemittanceManager(
|
|
21
|
+
* {
|
|
22
|
+
* messageBox: 'remittance_inbox',
|
|
23
|
+
* remittanceModules: [new Brc29RemittanceModule()]
|
|
24
|
+
* },
|
|
25
|
+
* wallet,
|
|
26
|
+
* commsLayer
|
|
27
|
+
* )
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import { PubKeyHex } from '@bsv/sdk'
|
|
32
|
+
import type {
|
|
33
|
+
CommsLayer as SdkCommsLayer,
|
|
34
|
+
PeerMessage as SdkRemittancePeerMessage
|
|
35
|
+
} from '@bsv/sdk'
|
|
36
|
+
import type { MessageBoxClient } from './MessageBoxClient.js'
|
|
37
|
+
import type { PeerMessage as MessageBoxPeerMessage } from './types.js'
|
|
38
|
+
|
|
39
|
+
export type CommsLayer = SdkCommsLayer
|
|
40
|
+
export type RemittancePeerMessage = SdkRemittancePeerMessage
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Adapter that implements the CommsLayer interface for MessageBoxClient
|
|
44
|
+
*
|
|
45
|
+
* This class wraps MessageBoxClient to provide compatibility with the RemittanceManager
|
|
46
|
+
* communications interface. It handles format conversions, particularly ensuring message
|
|
47
|
+
* bodies are properly stringified for the RemittanceManager protocol.
|
|
48
|
+
*/
|
|
49
|
+
export class RemittanceAdapter implements SdkCommsLayer {
|
|
50
|
+
/**
|
|
51
|
+
* Creates a new RemittanceAdapter
|
|
52
|
+
* @param messageBox - The MessageBoxClient instance to adapt
|
|
53
|
+
*/
|
|
54
|
+
constructor(private readonly messageBox: MessageBoxClient) { }
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Sends a message over the store-and-forward channel
|
|
58
|
+
* @param args - Message parameters (recipient, messageBox, body)
|
|
59
|
+
* @param hostOverride - Optional host override
|
|
60
|
+
* @returns The transport message ID
|
|
61
|
+
*/
|
|
62
|
+
async sendMessage(
|
|
63
|
+
args: { recipient: PubKeyHex, messageBox: string, body: string },
|
|
64
|
+
hostOverride?: string
|
|
65
|
+
): Promise<string> {
|
|
66
|
+
const result = await this.messageBox.sendMessage({
|
|
67
|
+
recipient: args.recipient,
|
|
68
|
+
messageBox: args.messageBox,
|
|
69
|
+
body: args.body
|
|
70
|
+
}, hostOverride)
|
|
71
|
+
|
|
72
|
+
return result.messageId
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Sends a message over the live channel.
|
|
77
|
+
* MessageBoxClient handles transport fallback internally (WebSocket -> HTTP).
|
|
78
|
+
* @param args - Message parameters (recipient, messageBox, body)
|
|
79
|
+
* @param hostOverride - Optional host override
|
|
80
|
+
* @returns The transport message ID
|
|
81
|
+
*/
|
|
82
|
+
async sendLiveMessage(
|
|
83
|
+
args: { recipient: PubKeyHex, messageBox: string, body: string },
|
|
84
|
+
hostOverride?: string
|
|
85
|
+
): Promise<string> {
|
|
86
|
+
const result = await this.messageBox.sendLiveMessage({
|
|
87
|
+
recipient: args.recipient,
|
|
88
|
+
messageBox: args.messageBox,
|
|
89
|
+
body: args.body
|
|
90
|
+
}, hostOverride)
|
|
91
|
+
|
|
92
|
+
return result.messageId
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Lists pending messages for a message box
|
|
97
|
+
*
|
|
98
|
+
* Note: MessageBoxClient returns message bodies as parsed objects, but RemittanceManager
|
|
99
|
+
* expects them as JSON strings. This method handles the conversion.
|
|
100
|
+
*
|
|
101
|
+
* @param args - List parameters (messageBox, optional host)
|
|
102
|
+
* @returns Array of peer messages with stringified bodies
|
|
103
|
+
*/
|
|
104
|
+
async listMessages(args: { messageBox: string, host?: string }): Promise<RemittancePeerMessage[]> {
|
|
105
|
+
const defaultRecipient = await this.messageBox.getIdentityKey() as PubKeyHex
|
|
106
|
+
const messages = await this.messageBox.listMessages({
|
|
107
|
+
messageBox: args.messageBox,
|
|
108
|
+
host: args.host
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
return messages.map(msg => this.toRemittancePeerMessage(msg, args.messageBox, defaultRecipient))
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Acknowledges messages (deletes them from the server inbox)
|
|
116
|
+
* @param args - Array of message IDs to acknowledge
|
|
117
|
+
*/
|
|
118
|
+
async acknowledgeMessage(args: { messageIds: string[] }): Promise<void> {
|
|
119
|
+
// MessageBoxClient's acknowledgeMessage expects the same format
|
|
120
|
+
await this.messageBox.acknowledgeMessage({ messageIds: args.messageIds })
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Starts a live listener and normalizes inbound messages to the remittance PeerMessage shape.
|
|
125
|
+
*/
|
|
126
|
+
async listenForLiveMessages(args: {
|
|
127
|
+
messageBox: string
|
|
128
|
+
overrideHost?: string
|
|
129
|
+
onMessage: (msg: RemittancePeerMessage) => void
|
|
130
|
+
}): Promise<void> {
|
|
131
|
+
const defaultRecipient = await this.messageBox.getIdentityKey() as PubKeyHex
|
|
132
|
+
|
|
133
|
+
await this.messageBox.listenForLiveMessages({
|
|
134
|
+
messageBox: args.messageBox,
|
|
135
|
+
overrideHost: args.overrideHost,
|
|
136
|
+
onMessage: (msg: MessageBoxPeerMessage) => {
|
|
137
|
+
args.onMessage(this.toRemittancePeerMessage(msg, args.messageBox, defaultRecipient))
|
|
138
|
+
}
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
private toRemittancePeerMessage (
|
|
143
|
+
msg: MessageBoxPeerMessage & { recipient?: string, messageBox?: string },
|
|
144
|
+
fallbackMessageBox: string,
|
|
145
|
+
fallbackRecipient: PubKeyHex
|
|
146
|
+
): RemittancePeerMessage {
|
|
147
|
+
return {
|
|
148
|
+
messageId: msg.messageId,
|
|
149
|
+
sender: msg.sender as PubKeyHex,
|
|
150
|
+
recipient: (msg.recipient ?? fallbackRecipient) as PubKeyHex,
|
|
151
|
+
messageBox: msg.messageBox ?? fallbackMessageBox,
|
|
152
|
+
body: this.toBodyString(msg.body)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private toBodyString (body: unknown): string {
|
|
157
|
+
if (typeof body === 'string') return body
|
|
158
|
+
try {
|
|
159
|
+
return JSON.stringify(body)
|
|
160
|
+
} catch {
|
|
161
|
+
return String(body)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/* eslint-env jest */
|
|
2
|
+
import { jest } from '@jest/globals'
|
|
3
|
+
import { RemittanceAdapter } from '../RemittanceAdapter.js'
|
|
4
|
+
import type { MessageBoxClient } from '../MessageBoxClient.js'
|
|
5
|
+
|
|
6
|
+
describe('RemittanceAdapter', () => {
|
|
7
|
+
const myIdentityKey = '02b463b8ef7f03c47fba2679c7334d13e4939b8ca30dbb6bbd22e34ea3e9b1b0e4'
|
|
8
|
+
const senderKey = '03f5d7a10f8ac22f0785a54d7d30fd009a77da27812f4e2f4ac9327dfcb5f65f86'
|
|
9
|
+
|
|
10
|
+
it('delegates sendMessage and returns the transport messageId', async () => {
|
|
11
|
+
const messageBox = {
|
|
12
|
+
sendMessage: jest.fn<() => Promise<{ status: string, messageId: string }>>().mockResolvedValue({
|
|
13
|
+
status: 'success',
|
|
14
|
+
messageId: 'http-mid'
|
|
15
|
+
})
|
|
16
|
+
} as unknown as MessageBoxClient
|
|
17
|
+
|
|
18
|
+
const adapter = new RemittanceAdapter(messageBox)
|
|
19
|
+
const result = await adapter.sendMessage({
|
|
20
|
+
recipient: senderKey,
|
|
21
|
+
messageBox: 'remittance_inbox',
|
|
22
|
+
body: '{"v":1}'
|
|
23
|
+
}, 'https://override-host')
|
|
24
|
+
|
|
25
|
+
expect(result).toBe('http-mid')
|
|
26
|
+
expect(messageBox.sendMessage).toHaveBeenCalledWith({
|
|
27
|
+
recipient: senderKey,
|
|
28
|
+
messageBox: 'remittance_inbox',
|
|
29
|
+
body: '{"v":1}'
|
|
30
|
+
}, 'https://override-host')
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('delegates sendLiveMessage (live path) and returns the transport messageId', async () => {
|
|
34
|
+
const messageBox = {
|
|
35
|
+
sendLiveMessage: jest.fn<() => Promise<{ status: string, messageId: string }>>().mockResolvedValue({
|
|
36
|
+
status: 'success',
|
|
37
|
+
messageId: 'ws-mid'
|
|
38
|
+
})
|
|
39
|
+
} as unknown as MessageBoxClient
|
|
40
|
+
|
|
41
|
+
const adapter = new RemittanceAdapter(messageBox)
|
|
42
|
+
const result = await adapter.sendLiveMessage({
|
|
43
|
+
recipient: senderKey,
|
|
44
|
+
messageBox: 'remittance_inbox',
|
|
45
|
+
body: '{"v":1}'
|
|
46
|
+
}, 'https://override-host')
|
|
47
|
+
|
|
48
|
+
expect(result).toBe('ws-mid')
|
|
49
|
+
expect(messageBox.sendLiveMessage).toHaveBeenCalledWith({
|
|
50
|
+
recipient: senderKey,
|
|
51
|
+
messageBox: 'remittance_inbox',
|
|
52
|
+
body: '{"v":1}'
|
|
53
|
+
}, 'https://override-host')
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('normalizes listMessages shape for remittance and forwards host', async () => {
|
|
57
|
+
const messageBox = {
|
|
58
|
+
getIdentityKey: jest.fn<() => Promise<string>>().mockResolvedValue(myIdentityKey),
|
|
59
|
+
listMessages: jest.fn<() => Promise<any[]>>().mockResolvedValue([
|
|
60
|
+
{
|
|
61
|
+
messageId: 'm1',
|
|
62
|
+
sender: senderKey,
|
|
63
|
+
created_at: '2026-01-01T00:00:00Z',
|
|
64
|
+
updated_at: '2026-01-01T00:00:00Z',
|
|
65
|
+
body: { kind: 'invoice', amount: 1 }
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
messageId: 'm2',
|
|
69
|
+
sender: senderKey,
|
|
70
|
+
recipient: '020202020202020202020202020202020202020202020202020202020202020202',
|
|
71
|
+
messageBox: 'custom_box',
|
|
72
|
+
created_at: '2026-01-01T00:00:00Z',
|
|
73
|
+
updated_at: '2026-01-01T00:00:00Z',
|
|
74
|
+
body: '{"kind":"receipt"}'
|
|
75
|
+
}
|
|
76
|
+
])
|
|
77
|
+
} as unknown as MessageBoxClient
|
|
78
|
+
|
|
79
|
+
const adapter = new RemittanceAdapter(messageBox)
|
|
80
|
+
const result = await adapter.listMessages({
|
|
81
|
+
messageBox: 'remittance_inbox',
|
|
82
|
+
host: 'https://remote-host'
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
expect(messageBox.listMessages).toHaveBeenCalledWith({
|
|
86
|
+
messageBox: 'remittance_inbox',
|
|
87
|
+
host: 'https://remote-host'
|
|
88
|
+
})
|
|
89
|
+
expect(result).toEqual([
|
|
90
|
+
{
|
|
91
|
+
messageId: 'm1',
|
|
92
|
+
sender: senderKey,
|
|
93
|
+
recipient: myIdentityKey,
|
|
94
|
+
messageBox: 'remittance_inbox',
|
|
95
|
+
body: '{"kind":"invoice","amount":1}'
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
messageId: 'm2',
|
|
99
|
+
sender: senderKey,
|
|
100
|
+
recipient: '020202020202020202020202020202020202020202020202020202020202020202',
|
|
101
|
+
messageBox: 'custom_box',
|
|
102
|
+
body: '{"kind":"receipt"}'
|
|
103
|
+
}
|
|
104
|
+
])
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('forwards live listener setup and normalizes inbound message shape', async () => {
|
|
108
|
+
const onPaymentMessage = jest.fn()
|
|
109
|
+
let forwardedListener: ((msg: {
|
|
110
|
+
messageId: string
|
|
111
|
+
sender: string
|
|
112
|
+
body: unknown
|
|
113
|
+
created_at: string
|
|
114
|
+
updated_at: string
|
|
115
|
+
}) => void) | undefined
|
|
116
|
+
|
|
117
|
+
const messageBox = {
|
|
118
|
+
getIdentityKey: jest.fn<() => Promise<string>>().mockResolvedValue(myIdentityKey),
|
|
119
|
+
listenForLiveMessages: jest.fn().mockImplementation(async ({ onMessage }) => {
|
|
120
|
+
forwardedListener = onMessage
|
|
121
|
+
})
|
|
122
|
+
} as unknown as MessageBoxClient
|
|
123
|
+
|
|
124
|
+
const adapter = new RemittanceAdapter(messageBox)
|
|
125
|
+
await adapter.listenForLiveMessages({
|
|
126
|
+
messageBox: 'remittance_inbox',
|
|
127
|
+
overrideHost: 'https://ws-host',
|
|
128
|
+
onMessage: onPaymentMessage
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
expect(messageBox.listenForLiveMessages).toHaveBeenCalledWith({
|
|
132
|
+
messageBox: 'remittance_inbox',
|
|
133
|
+
overrideHost: 'https://ws-host',
|
|
134
|
+
onMessage: expect.any(Function)
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
forwardedListener?.({
|
|
138
|
+
messageId: 'live-1',
|
|
139
|
+
sender: senderKey,
|
|
140
|
+
created_at: '2026-01-01T00:00:00Z',
|
|
141
|
+
updated_at: '2026-01-01T00:00:00Z',
|
|
142
|
+
body: { kind: 'settlement' }
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
expect(onPaymentMessage).toHaveBeenCalledWith({
|
|
146
|
+
messageId: 'live-1',
|
|
147
|
+
sender: senderKey,
|
|
148
|
+
recipient: myIdentityKey,
|
|
149
|
+
messageBox: 'remittance_inbox',
|
|
150
|
+
body: '{"kind":"settlement"}'
|
|
151
|
+
})
|
|
152
|
+
})
|
|
153
|
+
})
|