@bsv/message-box-client 1.2.3 → 1.2.4
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/package.json +1 -1
- package/dist/cjs/src/MessageBoxClient.js +149 -19
- package/dist/cjs/src/MessageBoxClient.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/MessageBoxClient.js +148 -19
- package/dist/esm/src/MessageBoxClient.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/MessageBoxClient.d.ts +97 -8
- package/dist/types/src/MessageBoxClient.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/dist/umd/bundle.js +1 -1
- package/package.json +1 -1
- package/src/MessageBoxClient.ts +173 -18
package/package.json
CHANGED
package/src/MessageBoxClient.ts
CHANGED
|
@@ -1266,20 +1266,12 @@ export class MessageBoxClient {
|
|
|
1266
1266
|
// 6. Early‑out: no messages but at least one host succeeded → []
|
|
1267
1267
|
if (dedupMap.size === 0) return []
|
|
1268
1268
|
|
|
1269
|
-
const tryParse = (raw: string): any => {
|
|
1270
|
-
try {
|
|
1271
|
-
return JSON.parse(raw)
|
|
1272
|
-
} catch {
|
|
1273
|
-
return raw
|
|
1274
|
-
}
|
|
1275
|
-
}
|
|
1276
|
-
|
|
1277
1269
|
const messages: PeerMessage[] = Array.from(dedupMap.values())
|
|
1278
1270
|
|
|
1279
1271
|
for (const message of messages) {
|
|
1280
1272
|
try {
|
|
1281
1273
|
const parsedBody: unknown =
|
|
1282
|
-
typeof message.body === 'string' ? tryParse(message.body) : message.body
|
|
1274
|
+
typeof message.body === 'string' ? this.tryParse(message.body) : message.body
|
|
1283
1275
|
|
|
1284
1276
|
let messageContent: any = parsedBody
|
|
1285
1277
|
let paymentData: Payment | undefined
|
|
@@ -1292,7 +1284,7 @@ export class MessageBoxClient {
|
|
|
1292
1284
|
// Handle wrapped message format (with payment data)
|
|
1293
1285
|
const wrappedMessage = (parsedBody as any).message
|
|
1294
1286
|
messageContent = typeof wrappedMessage === 'string'
|
|
1295
|
-
? tryParse(wrappedMessage)
|
|
1287
|
+
? this.tryParse(wrappedMessage)
|
|
1296
1288
|
: wrappedMessage
|
|
1297
1289
|
paymentData = (parsedBody as any).payment
|
|
1298
1290
|
}
|
|
@@ -1365,7 +1357,7 @@ export class MessageBoxClient {
|
|
|
1365
1357
|
}, originator)
|
|
1366
1358
|
|
|
1367
1359
|
const decryptedText = Utils.toUTF8(decrypted.plaintext)
|
|
1368
|
-
message.body = tryParse(decryptedText)
|
|
1360
|
+
message.body = this.tryParse(decryptedText)
|
|
1369
1361
|
} else {
|
|
1370
1362
|
// For non-encrypted messages, use the processed content
|
|
1371
1363
|
message.body = messageContent ?? parsedBody
|
|
@@ -1388,6 +1380,37 @@ export class MessageBoxClient {
|
|
|
1388
1380
|
return messages
|
|
1389
1381
|
}
|
|
1390
1382
|
|
|
1383
|
+
/**
|
|
1384
|
+
* @method listMessagesLite
|
|
1385
|
+
* @async
|
|
1386
|
+
* @param {ListMessagesParams} params - Contains the `messageBox` to read from and the `host` to query.
|
|
1387
|
+
* @returns {Promise<PeerMessage[]>} - Returns an array of decrypted `PeerMessage` objects with minimal processing.
|
|
1388
|
+
*
|
|
1389
|
+
* @description
|
|
1390
|
+
* A lightweight variant of {@link listMessages} that fetches and decrypts messages
|
|
1391
|
+
* from a specific host without performing:
|
|
1392
|
+
* - Overlay host resolution
|
|
1393
|
+
* - Payment acceptance or internalization
|
|
1394
|
+
* - Cross-host deduplication
|
|
1395
|
+
*
|
|
1396
|
+
* This method:
|
|
1397
|
+
* - Sends a direct POST request to the specified host's `/listMessages` endpoint.
|
|
1398
|
+
* - Parses message bodies as JSON when possible.
|
|
1399
|
+
* - Decrypts messages if they contain an `encryptedMessage` field, using AES-256-GCM via BRC-2-compliant ECDH key derivation.
|
|
1400
|
+
* - Returns messages in the order provided by the host.
|
|
1401
|
+
*
|
|
1402
|
+
* This is intended for cases where you already know the host and need faster,
|
|
1403
|
+
* simpler retrieval without the additional processing overhead of `listMessages`.
|
|
1404
|
+
*
|
|
1405
|
+
* @throws {Error} If the host returns an error status or decryption fails.
|
|
1406
|
+
*
|
|
1407
|
+
* @example
|
|
1408
|
+
* const messages = await client.listMessagesLite({
|
|
1409
|
+
* messageBox: 'notifications',
|
|
1410
|
+
* host: 'https://messagebox.babbage.systems'
|
|
1411
|
+
* })
|
|
1412
|
+
* console.log(messages)
|
|
1413
|
+
*/
|
|
1391
1414
|
async listMessagesLite ({ messageBox, host }: ListMessagesParams): Promise<PeerMessage[]> {
|
|
1392
1415
|
const res = await this.authFetch.fetch(`${host as string}/listMessages`, {
|
|
1393
1416
|
method: 'POST',
|
|
@@ -1452,6 +1475,121 @@ export class MessageBoxClient {
|
|
|
1452
1475
|
return messages
|
|
1453
1476
|
}
|
|
1454
1477
|
|
|
1478
|
+
/**
|
|
1479
|
+
* @method tryParse
|
|
1480
|
+
* @private
|
|
1481
|
+
* @param {string} raw - A raw string value that may contain JSON.
|
|
1482
|
+
* @returns {any} - The parsed JavaScript object if valid JSON, or the original string if parsing fails.
|
|
1483
|
+
*
|
|
1484
|
+
* @description
|
|
1485
|
+
* Attempts to parse a string as JSON. If the string is valid JSON, returns the parsed object;
|
|
1486
|
+
* otherwise returns the original string unchanged.
|
|
1487
|
+
*
|
|
1488
|
+
* This method is used throughout the client to safely handle message bodies that may or may not be
|
|
1489
|
+
* JSON-encoded without throwing parsing errors.
|
|
1490
|
+
*
|
|
1491
|
+
* @example
|
|
1492
|
+
* tryParse('{"hello":"world"}') // → { hello: "world" }
|
|
1493
|
+
* tryParse('plain text') // → "plain text"
|
|
1494
|
+
*/
|
|
1495
|
+
tryParse (raw: string): any {
|
|
1496
|
+
try {
|
|
1497
|
+
return JSON.parse(raw)
|
|
1498
|
+
} catch {
|
|
1499
|
+
return raw
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
/**
|
|
1504
|
+
* @method acknowledgeNotification
|
|
1505
|
+
* @async
|
|
1506
|
+
* @param {PeerMessage} message - The peer message object to acknowledge.
|
|
1507
|
+
* @returns {Promise<boolean>} - Resolves to `true` if the message included a recipient payment and it was successfully internalized, otherwise `false`.
|
|
1508
|
+
*
|
|
1509
|
+
* @description
|
|
1510
|
+
* Acknowledges receipt of a specific notification message and, if applicable, processes any recipient
|
|
1511
|
+
* payment contained within it.
|
|
1512
|
+
*
|
|
1513
|
+
* This method:
|
|
1514
|
+
* 1. Calls `acknowledgeMessage()` to remove the message from the server's queue.
|
|
1515
|
+
* 2. Checks the message body for embedded payment data.
|
|
1516
|
+
* 3. If a recipient payment exists, attempts to internalize it into the wallet.
|
|
1517
|
+
*
|
|
1518
|
+
* This is a convenience wrapper for acknowledgment and payment handling specifically for messages
|
|
1519
|
+
* representing notifications.
|
|
1520
|
+
*
|
|
1521
|
+
* @example
|
|
1522
|
+
* const success = await client.acknowledgeNotification(message)
|
|
1523
|
+
* console.log(success ? 'Payment received' : 'No payment or failed')
|
|
1524
|
+
*/
|
|
1525
|
+
async acknowledgeNotification(message: PeerMessage): Promise<boolean> {
|
|
1526
|
+
await this.acknowledgeMessage({ messageIds: [message.messageId] })
|
|
1527
|
+
|
|
1528
|
+
const parsedBody: unknown =
|
|
1529
|
+
typeof message.body === 'string' ? this.tryParse(message.body) : message.body
|
|
1530
|
+
|
|
1531
|
+
let paymentData: Payment | undefined
|
|
1532
|
+
|
|
1533
|
+
if (
|
|
1534
|
+
parsedBody != null &&
|
|
1535
|
+
typeof parsedBody === 'object' &&
|
|
1536
|
+
'message' in parsedBody
|
|
1537
|
+
) {
|
|
1538
|
+
paymentData = (parsedBody as any).payment
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
// Process payment if present - server now only stores recipient payments
|
|
1542
|
+
if (paymentData?.tx != null && paymentData.outputs != null) {
|
|
1543
|
+
try {
|
|
1544
|
+
Logger.log(
|
|
1545
|
+
`[MB CLIENT] Processing recipient payment in message from ${String(message.sender)}…`
|
|
1546
|
+
)
|
|
1547
|
+
|
|
1548
|
+
// All outputs in the stored payment data are for the recipient
|
|
1549
|
+
// (delivery fees are already processed by the server)
|
|
1550
|
+
const recipientOutputs = paymentData.outputs.filter(
|
|
1551
|
+
output => output.protocol === 'wallet payment'
|
|
1552
|
+
)
|
|
1553
|
+
|
|
1554
|
+
if (recipientOutputs.length < 1) {
|
|
1555
|
+
Logger.log(
|
|
1556
|
+
'[MB CLIENT] No wallet payment outputs found in payment data'
|
|
1557
|
+
)
|
|
1558
|
+
return false
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
Logger.log(
|
|
1562
|
+
`[MB CLIENT] Internalizing ${recipientOutputs.length} recipient payment output(s)…`
|
|
1563
|
+
)
|
|
1564
|
+
|
|
1565
|
+
const internalizeResult = await this.walletClient.internalizeAction({
|
|
1566
|
+
tx: paymentData.tx,
|
|
1567
|
+
outputs: recipientOutputs,
|
|
1568
|
+
description: paymentData.description ?? 'MessageBox recipient payment'
|
|
1569
|
+
})
|
|
1570
|
+
|
|
1571
|
+
if (internalizeResult.accepted) {
|
|
1572
|
+
Logger.log(
|
|
1573
|
+
'[MB CLIENT] Successfully internalized recipient payment'
|
|
1574
|
+
)
|
|
1575
|
+
return true
|
|
1576
|
+
} else {
|
|
1577
|
+
Logger.warn(
|
|
1578
|
+
'[MB CLIENT] Recipient payment internalization was not accepted'
|
|
1579
|
+
)
|
|
1580
|
+
return false
|
|
1581
|
+
}
|
|
1582
|
+
} catch (paymentError) {
|
|
1583
|
+
Logger.error(
|
|
1584
|
+
'[MB CLIENT ERROR] Failed to internalize recipient payment:',
|
|
1585
|
+
paymentError
|
|
1586
|
+
)
|
|
1587
|
+
return false
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
return false
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1455
1593
|
/**
|
|
1456
1594
|
* @method acknowledgeMessage
|
|
1457
1595
|
* @async
|
|
@@ -1995,14 +2133,31 @@ export class MessageBoxClient {
|
|
|
1995
2133
|
return 'payment_required'
|
|
1996
2134
|
}
|
|
1997
2135
|
|
|
1998
|
-
|
|
1999
|
-
*
|
|
2000
|
-
* TODO: Consider consolidating payment generating logic with a util PeerPayClient can use as well.
|
|
2136
|
+
/**
|
|
2137
|
+
* @method createMessagePayment
|
|
2001
2138
|
* @private
|
|
2002
|
-
* @param {string} recipient - Recipient identity key
|
|
2003
|
-
* @param {MessageBoxQuote} quote -
|
|
2004
|
-
* @param {string} description - Description for the payment
|
|
2005
|
-
* @
|
|
2139
|
+
* @param {string} recipient - Recipient's identity key.
|
|
2140
|
+
* @param {MessageBoxQuote} quote - Quote object containing recipient and delivery fees.
|
|
2141
|
+
* @param {string} [description='MessageBox delivery payment'] - Description for the payment action.
|
|
2142
|
+
* @param {string} [originator] - Optional originator to use for wallet operations.
|
|
2143
|
+
* @returns {Promise<Payment>} - Payment data including the transaction and remittance outputs.
|
|
2144
|
+
*
|
|
2145
|
+
* @description
|
|
2146
|
+
* Constructs and signs a payment transaction covering both delivery and recipient fees for
|
|
2147
|
+
* message delivery, based on a previously obtained quote.
|
|
2148
|
+
*
|
|
2149
|
+
* The transaction includes:
|
|
2150
|
+
* - An optional delivery fee output for the MessageBox server.
|
|
2151
|
+
* - An optional recipient fee output for the message recipient.
|
|
2152
|
+
*
|
|
2153
|
+
* Payment remittance metadata (derivation prefix/suffix, sender identity) is embedded to allow
|
|
2154
|
+
* the payee to derive their private key and spend the output.
|
|
2155
|
+
*
|
|
2156
|
+
* @throws {Error} If no payment is required, key derivation fails, or the action creation fails.
|
|
2157
|
+
*
|
|
2158
|
+
* @example
|
|
2159
|
+
* const payment = await client.createMessagePayment(recipientKey, quote)
|
|
2160
|
+
* await client.sendMessage({ recipient, messageBox, body, payment })
|
|
2006
2161
|
*/
|
|
2007
2162
|
private async createMessagePayment(
|
|
2008
2163
|
recipient: string,
|