@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsv/message-box-client",
3
- "version": "1.2.3",
3
+ "version": "1.2.4",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -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
- * Creates payment transaction for message delivery fees
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 - Fee quote with delivery and recipient fees
2004
- * @param {string} description - Description for the payment transaction
2005
- * @returns {Promise<Payment>} Payment transaction data
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,