@bsv/message-box-client 1.1.9 → 1.1.11

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.
Files changed (36) hide show
  1. package/dist/cjs/package.json +5 -5
  2. package/dist/cjs/src/MessageBoxClient.js +707 -87
  3. package/dist/cjs/src/MessageBoxClient.js.map +1 -1
  4. package/dist/cjs/src/PeerPayClient.js +61 -28
  5. package/dist/cjs/src/PeerPayClient.js.map +1 -1
  6. package/dist/cjs/src/Utils/logger.js +22 -21
  7. package/dist/cjs/src/Utils/logger.js.map +1 -1
  8. package/dist/cjs/src/types/permissions.js +6 -0
  9. package/dist/cjs/src/types/permissions.js.map +1 -0
  10. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  11. package/dist/esm/src/MessageBoxClient.js +593 -12
  12. package/dist/esm/src/MessageBoxClient.js.map +1 -1
  13. package/dist/esm/src/PeerPayClient.js +1 -1
  14. package/dist/esm/src/PeerPayClient.js.map +1 -1
  15. package/dist/esm/src/Utils/logger.js +17 -19
  16. package/dist/esm/src/Utils/logger.js.map +1 -1
  17. package/dist/esm/src/types/permissions.js +5 -0
  18. package/dist/esm/src/types/permissions.js.map +1 -0
  19. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  20. package/dist/types/src/MessageBoxClient.d.ts +218 -13
  21. package/dist/types/src/MessageBoxClient.d.ts.map +1 -1
  22. package/dist/types/src/PeerPayClient.d.ts.map +1 -1
  23. package/dist/types/src/Utils/logger.d.ts +5 -8
  24. package/dist/types/src/Utils/logger.d.ts.map +1 -1
  25. package/dist/types/src/types/permissions.d.ts +75 -0
  26. package/dist/types/src/types/permissions.d.ts.map +1 -0
  27. package/dist/types/src/types.d.ts +71 -2
  28. package/dist/types/src/types.d.ts.map +1 -1
  29. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  30. package/dist/umd/bundle.js +1 -1
  31. package/package.json +5 -5
  32. package/src/MessageBoxClient.ts +732 -24
  33. package/src/PeerPayClient.ts +11 -11
  34. package/src/Utils/logger.ts +17 -19
  35. package/src/types/permissions.ts +81 -0
  36. package/src/types.ts +77 -2
@@ -22,11 +22,44 @@
22
22
  * @author Project Babbage
23
23
  * @license Open BSV License
24
24
  */
25
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
26
+ if (k2 === undefined) k2 = k;
27
+ var desc = Object.getOwnPropertyDescriptor(m, k);
28
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
29
+ desc = { enumerable: true, get: function() { return m[k]; } };
30
+ }
31
+ Object.defineProperty(o, k2, desc);
32
+ }) : (function(o, m, k, k2) {
33
+ if (k2 === undefined) k2 = k;
34
+ o[k2] = m[k];
35
+ }));
36
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
37
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
38
+ }) : function(o, v) {
39
+ o["default"] = v;
40
+ });
41
+ var __importStar = (this && this.__importStar) || (function () {
42
+ var ownKeys = function(o) {
43
+ ownKeys = Object.getOwnPropertyNames || function (o) {
44
+ var ar = [];
45
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
46
+ return ar;
47
+ };
48
+ return ownKeys(o);
49
+ };
50
+ return function (mod) {
51
+ if (mod && mod.__esModule) return mod;
52
+ var result = {};
53
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
54
+ __setModuleDefault(result, mod);
55
+ return result;
56
+ };
57
+ })();
25
58
  Object.defineProperty(exports, "__esModule", { value: true });
26
59
  exports.MessageBoxClient = void 0;
27
60
  const sdk_1 = require("@bsv/sdk");
28
61
  const authsocket_client_1 = require("@bsv/authsocket-client");
29
- const logger_js_1 = require("./Utils/logger.js");
62
+ const Logger = __importStar(require("./Utils/logger.js"));
30
63
  const DEFAULT_MAINNET_HOST = 'https://messagebox.babbage.systems';
31
64
  const DEFAULT_TESTNET_HOST = 'https://staging-messagebox.babbage.systems';
32
65
  /**
@@ -61,7 +94,7 @@ class MessageBoxClient {
61
94
  * @constructor
62
95
  * @param {Object} options - Initialization options for the MessageBoxClient.
63
96
  * @param {string} [options.host] - The base URL of the MessageBox server. If omitted, defaults to mainnet/testnet hosts.
64
- * @param {WalletClient} options.walletClient - Wallet instance used for authentication, signing, and encryption.
97
+ * @param {WalletInterface} options.walletClient - Wallet instance used for authentication, signing, and encryption.
65
98
  * @param {boolean} [options.enableLogging=false] - Whether to enable detailed debug logging to the console.
66
99
  * @param {'local' | 'mainnet' | 'testnet'} [options.networkPreset='mainnet'] - Overlay network preset used for routing and advertisement lookup.
67
100
  *
@@ -97,7 +130,7 @@ class MessageBoxClient {
97
130
  networkPreset
98
131
  });
99
132
  if (enableLogging) {
100
- logger_js_1.Logger.enable();
133
+ Logger.enable();
101
134
  }
102
135
  }
103
136
  /**
@@ -141,7 +174,7 @@ class MessageBoxClient {
141
174
  const [firstAdvertisement] = await this.queryAdvertisements(identityKey, normalizedHost);
142
175
  // 3. If none our found, anoint this host
143
176
  if (firstAdvertisement == null || ((_a = firstAdvertisement === null || firstAdvertisement === void 0 ? void 0 : firstAdvertisement.host) === null || _a === void 0 ? void 0 : _a.trim()) === '' || (firstAdvertisement === null || firstAdvertisement === void 0 ? void 0 : firstAdvertisement.host) !== normalizedHost) {
144
- logger_js_1.Logger.log('[MB CLIENT] Anointing host:', normalizedHost);
177
+ Logger.log('[MB CLIENT] Anointing host:', normalizedHost);
145
178
  const { txid } = await this.anointHost(normalizedHost);
146
179
  if (txid == null || txid.trim() === '') {
147
180
  throw new Error('Failed to anoint host: No transaction ID returned');
@@ -186,15 +219,15 @@ class MessageBoxClient {
186
219
  if (this.myIdentityKey != null && this.myIdentityKey.trim() !== '') {
187
220
  return this.myIdentityKey;
188
221
  }
189
- logger_js_1.Logger.log('[MB CLIENT] Fetching identity key...');
222
+ Logger.log('[MB CLIENT] Fetching identity key...');
190
223
  try {
191
224
  const keyResult = await this.walletClient.getPublicKey({ identityKey: true });
192
225
  this.myIdentityKey = keyResult.publicKey;
193
- logger_js_1.Logger.log(`[MB CLIENT] Identity key fetched: ${this.myIdentityKey}`);
226
+ Logger.log(`[MB CLIENT] Identity key fetched: ${this.myIdentityKey}`);
194
227
  return this.myIdentityKey;
195
228
  }
196
229
  catch (error) {
197
- logger_js_1.Logger.error('[MB CLIENT ERROR] Failed to fetch identity key:', error);
230
+ Logger.error('[MB CLIENT ERROR] Failed to fetch identity key:', error);
198
231
  throw new Error('Identity key retrieval failed');
199
232
  }
200
233
  }
@@ -237,24 +270,24 @@ class MessageBoxClient {
237
270
  */
238
271
  async initializeConnection() {
239
272
  await this.assertInitialized();
240
- logger_js_1.Logger.log('[MB CLIENT] initializeConnection() STARTED');
273
+ Logger.log('[MB CLIENT] initializeConnection() STARTED');
241
274
  if (this.myIdentityKey == null || this.myIdentityKey.trim() === '') {
242
- logger_js_1.Logger.log('[MB CLIENT] Fetching identity key...');
275
+ Logger.log('[MB CLIENT] Fetching identity key...');
243
276
  try {
244
277
  const keyResult = await this.walletClient.getPublicKey({ identityKey: true });
245
278
  this.myIdentityKey = keyResult.publicKey;
246
- logger_js_1.Logger.log(`[MB CLIENT] Identity key fetched successfully: ${this.myIdentityKey}`);
279
+ Logger.log(`[MB CLIENT] Identity key fetched successfully: ${this.myIdentityKey}`);
247
280
  }
248
281
  catch (error) {
249
- logger_js_1.Logger.error('[MB CLIENT ERROR] Failed to fetch identity key:', error);
282
+ Logger.error('[MB CLIENT ERROR] Failed to fetch identity key:', error);
250
283
  throw new Error('Identity key retrieval failed');
251
284
  }
252
285
  }
253
286
  if (this.myIdentityKey == null || this.myIdentityKey.trim() === '') {
254
- logger_js_1.Logger.error('[MB CLIENT ERROR] Identity key is still missing after retrieval!');
287
+ Logger.error('[MB CLIENT ERROR] Identity key is still missing after retrieval!');
255
288
  throw new Error('Identity key is missing');
256
289
  }
257
- logger_js_1.Logger.log('[MB CLIENT] Setting up WebSocket connection...');
290
+ Logger.log('[MB CLIENT] Setting up WebSocket connection...');
258
291
  if (this.socket == null) {
259
292
  if (typeof this.host !== 'string' || this.host.trim() === '') {
260
293
  throw new Error('Cannot initialize WebSocket: Host is not set');
@@ -264,11 +297,11 @@ class MessageBoxClient {
264
297
  let authenticated = false;
265
298
  this.socket.on('connect', () => {
266
299
  var _a;
267
- logger_js_1.Logger.log('[MB CLIENT] Connected to WebSocket.');
300
+ Logger.log('[MB CLIENT] Connected to WebSocket.');
268
301
  if (!identitySent) {
269
- logger_js_1.Logger.log('[MB CLIENT] Sending authentication data:', this.myIdentityKey);
302
+ Logger.log('[MB CLIENT] Sending authentication data:', this.myIdentityKey);
270
303
  if (this.myIdentityKey == null || this.myIdentityKey.trim() === '') {
271
- logger_js_1.Logger.error('[MB CLIENT ERROR] Cannot send authentication: Identity key is missing!');
304
+ Logger.error('[MB CLIENT ERROR] Cannot send authentication: Identity key is missing!');
272
305
  }
273
306
  else {
274
307
  (_a = this.socket) === null || _a === void 0 ? void 0 : _a.emit('authenticated', { identityKey: this.myIdentityKey });
@@ -278,28 +311,28 @@ class MessageBoxClient {
278
311
  });
279
312
  // Listen for authentication success from the server
280
313
  this.socket.on('authenticationSuccess', (data) => {
281
- logger_js_1.Logger.log(`[MB CLIENT] WebSocket authentication successful: ${JSON.stringify(data)}`);
314
+ Logger.log(`[MB CLIENT] WebSocket authentication successful: ${JSON.stringify(data)}`);
282
315
  authenticated = true;
283
316
  });
284
317
  // Handle authentication failures
285
318
  this.socket.on('authenticationFailed', (data) => {
286
- logger_js_1.Logger.error(`[MB CLIENT ERROR] WebSocket authentication failed: ${JSON.stringify(data)}`);
319
+ Logger.error(`[MB CLIENT ERROR] WebSocket authentication failed: ${JSON.stringify(data)}`);
287
320
  authenticated = false;
288
321
  });
289
322
  this.socket.on('disconnect', () => {
290
- logger_js_1.Logger.log('[MB CLIENT] Disconnected from MessageBox server');
323
+ Logger.log('[MB CLIENT] Disconnected from MessageBox server');
291
324
  this.socket = undefined;
292
325
  identitySent = false;
293
326
  authenticated = false;
294
327
  });
295
328
  this.socket.on('error', (error) => {
296
- logger_js_1.Logger.error('[MB CLIENT ERROR] WebSocket error:', error);
329
+ Logger.error('[MB CLIENT ERROR] WebSocket error:', error);
297
330
  });
298
331
  // Wait for authentication confirmation before proceeding
299
332
  await new Promise((resolve, reject) => {
300
333
  setTimeout(() => {
301
334
  if (authenticated) {
302
- logger_js_1.Logger.log('[MB CLIENT] WebSocket fully authenticated and ready!');
335
+ Logger.log('[MB CLIENT] WebSocket fully authenticated and ready!');
303
336
  resolve();
304
337
  }
305
338
  else {
@@ -330,7 +363,7 @@ class MessageBoxClient {
330
363
  async resolveHostForRecipient(identityKey) {
331
364
  const advertisementTokens = await this.queryAdvertisements(identityKey);
332
365
  if (advertisementTokens.length === 0) {
333
- logger_js_1.Logger.warn(`[MB CLIENT] No advertisements for ${identityKey}, using default host ${this.host}`);
366
+ Logger.warn(`[MB CLIENT] No advertisements for ${identityKey}, using default host ${this.host}`);
334
367
  return this.host;
335
368
  }
336
369
  // Return the first host found
@@ -355,7 +388,7 @@ class MessageBoxClient {
355
388
  query
356
389
  });
357
390
  if (result.type !== 'output-list') {
358
- throw new Error(`Unexpected result type: ${result.type}`);
391
+ throw new Error(`Unexpected result type: ${String(result.type)}`);
359
392
  }
360
393
  for (const output of result.outputs) {
361
394
  try {
@@ -380,7 +413,7 @@ class MessageBoxClient {
380
413
  }
381
414
  }
382
415
  catch (err) {
383
- logger_js_1.Logger.error('[MB CLIENT ERROR] _queryAdvertisements failed:', err);
416
+ Logger.error('[MB CLIENT ERROR] _queryAdvertisements failed:', err);
384
417
  }
385
418
  return hosts;
386
419
  }
@@ -406,10 +439,10 @@ class MessageBoxClient {
406
439
  async joinRoom(messageBox) {
407
440
  var _a, _b;
408
441
  await this.assertInitialized();
409
- logger_js_1.Logger.log(`[MB CLIENT] Attempting to join WebSocket room: ${messageBox}`);
442
+ Logger.log(`[MB CLIENT] Attempting to join WebSocket room: ${messageBox}`);
410
443
  // Ensure WebSocket connection is established first
411
444
  if (this.socket == null) {
412
- logger_js_1.Logger.log('[MB CLIENT] No WebSocket connection. Initializing...');
445
+ Logger.log('[MB CLIENT] No WebSocket connection. Initializing...');
413
446
  await this.initializeConnection();
414
447
  }
415
448
  if (this.myIdentityKey == null || this.myIdentityKey.trim() === '') {
@@ -417,17 +450,17 @@ class MessageBoxClient {
417
450
  }
418
451
  const roomId = `${(_a = this.myIdentityKey) !== null && _a !== void 0 ? _a : ''}-${messageBox}`;
419
452
  if (this.joinedRooms.has(roomId)) {
420
- logger_js_1.Logger.log(`[MB CLIENT] Already joined WebSocket room: ${roomId}`);
453
+ Logger.log(`[MB CLIENT] Already joined WebSocket room: ${roomId}`);
421
454
  return;
422
455
  }
423
456
  try {
424
- logger_js_1.Logger.log(`[MB CLIENT] Joining WebSocket room: ${roomId}`);
457
+ Logger.log(`[MB CLIENT] Joining WebSocket room: ${roomId}`);
425
458
  await ((_b = this.socket) === null || _b === void 0 ? void 0 : _b.emit('joinRoom', roomId));
426
459
  this.joinedRooms.add(roomId);
427
- logger_js_1.Logger.log(`[MB CLIENT] Successfully joined room: ${roomId}`);
460
+ Logger.log(`[MB CLIENT] Successfully joined room: ${roomId}`);
428
461
  }
429
462
  catch (error) {
430
- logger_js_1.Logger.error(`[MB CLIENT ERROR] Failed to join WebSocket room: ${roomId}`, error);
463
+ Logger.error(`[MB CLIENT ERROR] Failed to join WebSocket room: ${roomId}`, error);
431
464
  }
432
465
  }
433
466
  /**
@@ -461,7 +494,7 @@ class MessageBoxClient {
461
494
  async listenForLiveMessages({ onMessage, messageBox }) {
462
495
  var _a;
463
496
  await this.assertInitialized();
464
- logger_js_1.Logger.log(`[MB CLIENT] Setting up listener for WebSocket room: ${messageBox}`);
497
+ Logger.log(`[MB CLIENT] Setting up listener for WebSocket room: ${messageBox}`);
465
498
  // Ensure WebSocket connection and room join
466
499
  await this.joinRoom(messageBox);
467
500
  // Ensure identity key is available before creating roomId
@@ -469,10 +502,10 @@ class MessageBoxClient {
469
502
  throw new Error('[MB CLIENT ERROR] Identity key is missing. Cannot construct room ID.');
470
503
  }
471
504
  const roomId = `${this.myIdentityKey}-${messageBox}`;
472
- logger_js_1.Logger.log(`[MB CLIENT] Listening for messages in room: ${roomId}`);
505
+ Logger.log(`[MB CLIENT] Listening for messages in room: ${roomId}`);
473
506
  (_a = this.socket) === null || _a === void 0 ? void 0 : _a.on(`sendMessage-${roomId}`, (message) => {
474
507
  void (async () => {
475
- logger_js_1.Logger.log(`[MB CLIENT] Received message in room ${roomId}:`, message);
508
+ Logger.log(`[MB CLIENT] Received message in room ${roomId}:`, message);
476
509
  try {
477
510
  let parsedBody = message.body;
478
511
  if (typeof parsedBody === 'string') {
@@ -486,7 +519,7 @@ class MessageBoxClient {
486
519
  if (parsedBody != null &&
487
520
  typeof parsedBody === 'object' &&
488
521
  typeof parsedBody.encryptedMessage === 'string') {
489
- logger_js_1.Logger.log(`[MB CLIENT] Decrypting message from ${String(message.sender)}...`);
522
+ Logger.log(`[MB CLIENT] Decrypting message from ${String(message.sender)}...`);
490
523
  const decrypted = await this.walletClient.decrypt({
491
524
  protocolID: [1, 'messagebox'],
492
525
  keyID: '1',
@@ -496,7 +529,7 @@ class MessageBoxClient {
496
529
  message.body = sdk_1.Utils.toUTF8(decrypted.plaintext);
497
530
  }
498
531
  else {
499
- logger_js_1.Logger.log('[MB CLIENT] Message is not encrypted.');
532
+ Logger.log('[MB CLIENT] Message is not encrypted.');
500
533
  message.body = typeof parsedBody === 'string'
501
534
  ? parsedBody
502
535
  : (() => { try {
@@ -508,7 +541,7 @@ class MessageBoxClient {
508
541
  }
509
542
  }
510
543
  catch (err) {
511
- logger_js_1.Logger.error('[MB CLIENT ERROR] Failed to parse or decrypt live message:', err);
544
+ Logger.error('[MB CLIENT ERROR] Failed to parse or decrypt live message:', err);
512
545
  message.body = '[Error: Failed to decrypt or parse message]';
513
546
  }
514
547
  onMessage(message);
@@ -544,7 +577,7 @@ class MessageBoxClient {
544
577
  * body: { amount: 1000 }
545
578
  * })
546
579
  */
547
- async sendLiveMessage({ recipient, messageBox, body, messageId, skipEncryption }) {
580
+ async sendLiveMessage({ recipient, messageBox, body, messageId, skipEncryption, checkPermissions }) {
548
581
  await this.assertInitialized();
549
582
  if (recipient == null || recipient.trim() === '') {
550
583
  throw new Error('[MB CLIENT ERROR] Recipient identity key is required');
@@ -559,7 +592,7 @@ class MessageBoxClient {
559
592
  await this.joinRoom(messageBox);
560
593
  // Fallback to HTTP if WebSocket is not connected
561
594
  if (this.socket == null || !this.socket.connected) {
562
- logger_js_1.Logger.warn('[MB CLIENT WARNING] WebSocket not connected, falling back to HTTP');
595
+ Logger.warn('[MB CLIENT WARNING] WebSocket not connected, falling back to HTTP');
563
596
  const targetHost = await this.resolveHostForRecipient(recipient);
564
597
  return await this.sendMessage({ recipient, messageBox, body }, targetHost);
565
598
  }
@@ -574,11 +607,11 @@ class MessageBoxClient {
574
607
  finalMessageId = messageId !== null && messageId !== void 0 ? messageId : Array.from(hmac.hmac).map(b => b.toString(16).padStart(2, '0')).join('');
575
608
  }
576
609
  catch (error) {
577
- logger_js_1.Logger.error('[MB CLIENT ERROR] Failed to generate HMAC:', error);
610
+ Logger.error('[MB CLIENT ERROR] Failed to generate HMAC:', error);
578
611
  throw new Error('Failed to generate message identifier.');
579
612
  }
580
613
  const roomId = `${recipient}-${messageBox}`;
581
- logger_js_1.Logger.log(`[MB CLIENT] Sending WebSocket message to room: ${roomId}`);
614
+ Logger.log(`[MB CLIENT] Sending WebSocket message to room: ${roomId}`);
582
615
  let outgoingBody;
583
616
  if (skipEncryption === true) {
584
617
  outgoingBody = typeof body === 'string' ? body : JSON.stringify(body);
@@ -606,15 +639,16 @@ class MessageBoxClient {
606
639
  if (typeof (socketAny === null || socketAny === void 0 ? void 0 : socketAny.off) === 'function') {
607
640
  socketAny.off(ackEvent, ackHandler);
608
641
  }
609
- logger_js_1.Logger.log('[MB CLIENT] Received WebSocket acknowledgment:', response);
642
+ Logger.log('[MB CLIENT] Received WebSocket acknowledgment:', response);
610
643
  if (response == null || response.status !== 'success') {
611
- logger_js_1.Logger.warn('[MB CLIENT] WebSocket message failed or returned unexpected response. Falling back to HTTP.');
644
+ Logger.warn('[MB CLIENT] WebSocket message failed or returned unexpected response. Falling back to HTTP.');
612
645
  const fallbackMessage = {
613
646
  recipient,
614
647
  messageBox,
615
648
  body,
616
649
  messageId: finalMessageId,
617
- skipEncryption
650
+ skipEncryption,
651
+ checkPermissions
618
652
  };
619
653
  this.resolveHostForRecipient(recipient)
620
654
  .then(async (host) => {
@@ -624,7 +658,7 @@ class MessageBoxClient {
624
658
  .catch(reject);
625
659
  }
626
660
  else {
627
- logger_js_1.Logger.log('[MB CLIENT] Message sent successfully via WebSocket:', response);
661
+ Logger.log('[MB CLIENT] Message sent successfully via WebSocket:', response);
628
662
  resolve(response);
629
663
  }
630
664
  };
@@ -647,13 +681,14 @@ class MessageBoxClient {
647
681
  if (typeof (socketAny === null || socketAny === void 0 ? void 0 : socketAny.off) === 'function') {
648
682
  socketAny.off(ackEvent, ackHandler);
649
683
  }
650
- logger_js_1.Logger.warn('[CLIENT] WebSocket acknowledgment timed out, falling back to HTTP');
684
+ Logger.warn('[CLIENT] WebSocket acknowledgment timed out, falling back to HTTP');
651
685
  const fallbackMessage = {
652
686
  recipient,
653
687
  messageBox,
654
688
  body,
655
689
  messageId: finalMessageId,
656
- skipEncryption
690
+ skipEncryption,
691
+ checkPermissions
657
692
  };
658
693
  this.resolveHostForRecipient(recipient)
659
694
  .then(async (host) => {
@@ -683,14 +718,14 @@ class MessageBoxClient {
683
718
  async leaveRoom(messageBox) {
684
719
  await this.assertInitialized();
685
720
  if (this.socket == null) {
686
- logger_js_1.Logger.warn('[MB CLIENT] Attempted to leave a room but WebSocket is not connected.');
721
+ Logger.warn('[MB CLIENT] Attempted to leave a room but WebSocket is not connected.');
687
722
  return;
688
723
  }
689
724
  if (this.myIdentityKey == null || this.myIdentityKey.trim() === '') {
690
725
  throw new Error('[MB CLIENT ERROR] Identity key is not defined');
691
726
  }
692
727
  const roomId = `${this.myIdentityKey}-${messageBox}`;
693
- logger_js_1.Logger.log(`[MB CLIENT] Leaving WebSocket room: ${roomId}`);
728
+ Logger.log(`[MB CLIENT] Leaving WebSocket room: ${roomId}`);
694
729
  this.socket.emit('leaveRoom', roomId);
695
730
  // Ensure the room is removed from tracking
696
731
  this.joinedRooms.delete(roomId);
@@ -711,12 +746,12 @@ class MessageBoxClient {
711
746
  async disconnectWebSocket() {
712
747
  await this.assertInitialized();
713
748
  if (this.socket != null) {
714
- logger_js_1.Logger.log('[MB CLIENT] Closing WebSocket connection...');
749
+ Logger.log('[MB CLIENT] Closing WebSocket connection...');
715
750
  this.socket.disconnect();
716
751
  this.socket = undefined;
717
752
  }
718
753
  else {
719
- logger_js_1.Logger.log('[MB CLIENT] No active WebSocket connection to close.');
754
+ Logger.log('[MB CLIENT] No active WebSocket connection to close.');
720
755
  }
721
756
  }
722
757
  /**
@@ -758,6 +793,33 @@ class MessageBoxClient {
758
793
  if (message.body == null || (typeof message.body === 'string' && message.body.trim().length === 0)) {
759
794
  throw new Error('Every message must have a body!');
760
795
  }
796
+ // Optional permission checking for backwards compatibility
797
+ let paymentData;
798
+ if (message.checkPermissions === true) {
799
+ try {
800
+ Logger.log('[MB CLIENT] Checking permissions and fees for message...');
801
+ // Get quote to check if payment is required
802
+ const quote = await this.getMessageBoxQuote({
803
+ recipient: message.recipient,
804
+ messageBox: message.messageBox
805
+ });
806
+ if (quote.recipientFee === -1) {
807
+ throw new Error('You have been blocked from sending messages to this recipient.');
808
+ }
809
+ if (quote.recipientFee > 0 || quote.deliveryFee > 0) {
810
+ const requiredPayment = quote.recipientFee + quote.deliveryFee;
811
+ if (requiredPayment > 0) {
812
+ Logger.log(`[MB CLIENT] Creating payment of ${requiredPayment} sats for message...`);
813
+ // Create payment using helper method
814
+ paymentData = await this.createMessagePayment(message.recipient, quote, overrideHost);
815
+ Logger.log('[MB CLIENT] Payment data prepared:', paymentData);
816
+ }
817
+ }
818
+ }
819
+ catch (error) {
820
+ throw new Error(`Permission check failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
821
+ }
822
+ }
761
823
  let messageId;
762
824
  try {
763
825
  const hmac = await this.walletClient.createHmac({
@@ -769,7 +831,7 @@ class MessageBoxClient {
769
831
  messageId = (_a = message.messageId) !== null && _a !== void 0 ? _a : Array.from(hmac.hmac).map(b => b.toString(16).padStart(2, '0')).join('');
770
832
  }
771
833
  catch (error) {
772
- logger_js_1.Logger.error('[MB CLIENT ERROR] Failed to generate HMAC:', error);
834
+ Logger.error('[MB CLIENT ERROR] Failed to generate HMAC:', error);
773
835
  throw new Error('Failed to generate message identifier.');
774
836
  }
775
837
  let finalBody;
@@ -790,20 +852,21 @@ class MessageBoxClient {
790
852
  ...message,
791
853
  messageId,
792
854
  body: finalBody
793
- }
855
+ },
856
+ ...(paymentData != null && { payment: paymentData })
794
857
  };
795
858
  try {
796
859
  const finalHost = overrideHost !== null && overrideHost !== void 0 ? overrideHost : await this.resolveHostForRecipient(message.recipient);
797
- logger_js_1.Logger.log('[MB CLIENT] Sending HTTP request to:', `${finalHost}/sendMessage`);
798
- logger_js_1.Logger.log('[MB CLIENT] Request Body:', JSON.stringify(requestBody, null, 2));
860
+ Logger.log('[MB CLIENT] Sending HTTP request to:', `${finalHost}/sendMessage`);
861
+ Logger.log('[MB CLIENT] Request Body:', JSON.stringify(requestBody, null, 2));
799
862
  if (this.myIdentityKey == null || this.myIdentityKey === '') {
800
863
  try {
801
864
  const keyResult = await this.walletClient.getPublicKey({ identityKey: true });
802
865
  this.myIdentityKey = keyResult.publicKey;
803
- logger_js_1.Logger.log(`[MB CLIENT] Fetched identity key before sending request: ${this.myIdentityKey}`);
866
+ Logger.log(`[MB CLIENT] Fetched identity key before sending request: ${this.myIdentityKey}`);
804
867
  }
805
868
  catch (error) {
806
- logger_js_1.Logger.error('[MB CLIENT ERROR] Failed to fetch identity key:', error);
869
+ Logger.error('[MB CLIENT ERROR] Failed to fetch identity key:', error);
807
870
  throw new Error('Identity key retrieval failed');
808
871
  }
809
872
  }
@@ -818,20 +881,20 @@ class MessageBoxClient {
818
881
  throw new Error('[MB CLIENT ERROR] Response body has already been used!');
819
882
  }
820
883
  const parsedResponse = await response.json();
821
- logger_js_1.Logger.log('[MB CLIENT] Raw Response Body:', parsedResponse);
884
+ Logger.log('[MB CLIENT] Raw Response Body:', parsedResponse);
822
885
  if (!response.ok) {
823
- logger_js_1.Logger.error(`[MB CLIENT ERROR] Failed to send message. HTTP ${response.status}: ${response.statusText}`);
886
+ Logger.error(`[MB CLIENT ERROR] Failed to send message. HTTP ${response.status}: ${response.statusText}`);
824
887
  throw new Error(`Message sending failed: HTTP ${response.status} - ${response.statusText}`);
825
888
  }
826
889
  if (parsedResponse.status !== 'success') {
827
- logger_js_1.Logger.error(`[MB CLIENT ERROR] Server returned an error: ${String(parsedResponse.description)}`);
890
+ Logger.error(`[MB CLIENT ERROR] Server returned an error: ${String(parsedResponse.description)}`);
828
891
  throw new Error((_b = parsedResponse.description) !== null && _b !== void 0 ? _b : 'Unknown error from server.');
829
892
  }
830
- logger_js_1.Logger.log('[MB CLIENT] Message successfully sent.');
893
+ Logger.log('[MB CLIENT] Message successfully sent.');
831
894
  return { ...parsedResponse, messageId };
832
895
  }
833
896
  catch (error) {
834
- logger_js_1.Logger.error('[MB CLIENT ERROR] Network or timeout error:', error);
897
+ Logger.error('[MB CLIENT ERROR] Network or timeout error:', error);
835
898
  const errorMessage = error instanceof Error ? error.message : 'Unknown error';
836
899
  throw new Error(`Failed to send message: ${errorMessage}`);
837
900
  }
@@ -860,26 +923,26 @@ class MessageBoxClient {
860
923
  * const { txid } = await client.anointHost('https://my-messagebox.io')
861
924
  */
862
925
  async anointHost(host) {
863
- logger_js_1.Logger.log('[MB CLIENT] Starting anointHost...');
926
+ Logger.log('[MB CLIENT] Starting anointHost...');
864
927
  try {
865
928
  if (!host.startsWith('http')) {
866
929
  throw new Error('Invalid host URL');
867
930
  }
868
931
  const identityKey = await this.getIdentityKey();
869
- logger_js_1.Logger.log('[MB CLIENT] Fields - Identity:', identityKey, 'Host:', host);
932
+ Logger.log('[MB CLIENT] Fields - Identity:', identityKey, 'Host:', host);
870
933
  const fields = [
871
934
  sdk_1.Utils.toArray(identityKey, 'hex'),
872
935
  sdk_1.Utils.toArray(host, 'utf8')
873
936
  ];
874
937
  const pushdrop = new sdk_1.PushDrop(this.walletClient);
875
- logger_js_1.Logger.log('Fields:', fields.map(a => sdk_1.Utils.toHex(a)));
876
- logger_js_1.Logger.log('ProtocolID:', [1, 'messagebox advertisement']);
877
- logger_js_1.Logger.log('KeyID:', '1');
878
- logger_js_1.Logger.log('SignAs:', 'self');
879
- logger_js_1.Logger.log('anyoneCanSpend:', false);
880
- logger_js_1.Logger.log('forSelf:', true);
938
+ Logger.log('Fields:', fields.map(a => sdk_1.Utils.toHex(a)));
939
+ Logger.log('ProtocolID:', [1, 'messagebox advertisement']);
940
+ Logger.log('KeyID:', '1');
941
+ Logger.log('SignAs:', 'self');
942
+ Logger.log('anyoneCanSpend:', false);
943
+ Logger.log('forSelf:', true);
881
944
  const script = await pushdrop.lock(fields, [1, 'messagebox advertisement'], '1', 'anyone', true);
882
- logger_js_1.Logger.log('[MB CLIENT] PushDrop script:', script.toASM());
945
+ Logger.log('[MB CLIENT] PushDrop script:', script.toASM());
883
946
  const { tx, txid } = await this.walletClient.createAction({
884
947
  description: 'Anoint host for overlay routing',
885
948
  outputs: [{
@@ -890,13 +953,13 @@ class MessageBoxClient {
890
953
  }],
891
954
  options: { randomizeOutputs: false, acceptDelayedBroadcast: false }
892
955
  });
893
- logger_js_1.Logger.log('[MB CLIENT] Transaction created:', txid);
956
+ Logger.log('[MB CLIENT] Transaction created:', txid);
894
957
  if (tx !== undefined) {
895
958
  const broadcaster = new sdk_1.TopicBroadcaster(['tm_messagebox'], {
896
959
  networkPreset: this.networkPreset
897
960
  });
898
961
  const result = await broadcaster.broadcast(sdk_1.Transaction.fromAtomicBEEF(tx));
899
- logger_js_1.Logger.log('[MB CLIENT] Advertisement broadcast succeeded. TXID:', result.txid);
962
+ Logger.log('[MB CLIENT] Advertisement broadcast succeeded. TXID:', result.txid);
900
963
  if (typeof result.txid !== 'string') {
901
964
  throw new Error('Anoint failed: broadcast did not return a txid');
902
965
  }
@@ -905,7 +968,7 @@ class MessageBoxClient {
905
968
  throw new Error('Anoint failed: failed to create action!');
906
969
  }
907
970
  catch (err) {
908
- logger_js_1.Logger.error('[MB CLIENT ERROR] anointHost threw:', err);
971
+ Logger.error('[MB CLIENT ERROR] anointHost threw:', err);
909
972
  throw err;
910
973
  }
911
974
  }
@@ -923,7 +986,7 @@ class MessageBoxClient {
923
986
  * const { txid } = await client.revokeHost('https://my-messagebox.io')
924
987
  */
925
988
  async revokeHostAdvertisement(advertisementToken) {
926
- logger_js_1.Logger.log('[MB CLIENT] Starting revokeHost...');
989
+ Logger.log('[MB CLIENT] Starting revokeHost...');
927
990
  const outpoint = `${advertisementToken.txid}.${advertisementToken.outputIndex}`;
928
991
  try {
929
992
  const { signableTransaction } = await this.walletClient.createAction({
@@ -965,14 +1028,14 @@ class MessageBoxClient {
965
1028
  networkPreset: this.networkPreset
966
1029
  });
967
1030
  const result = await broadcaster.broadcast(sdk_1.Transaction.fromAtomicBEEF(signedTx));
968
- logger_js_1.Logger.log('[MB CLIENT] Revocation broadcast succeeded. TXID:', result.txid);
1031
+ Logger.log('[MB CLIENT] Revocation broadcast succeeded. TXID:', result.txid);
969
1032
  if (typeof result.txid !== 'string') {
970
1033
  throw new Error('Revoke failed: broadcast did not return a txid');
971
1034
  }
972
1035
  return { txid: result.txid };
973
1036
  }
974
1037
  catch (err) {
975
- logger_js_1.Logger.error('[MB CLIENT ERROR] revokeHost threw:', err);
1038
+ Logger.error('[MB CLIENT ERROR] revokeHost threw:', err);
976
1039
  throw err;
977
1040
  }
978
1041
  }
@@ -988,9 +1051,16 @@ class MessageBoxClient {
988
1051
  *
989
1052
  * Each message is:
990
1053
  * - Parsed and, if encrypted, decrypted using AES-256-GCM via BRC-2-compliant ECDH key derivation and symmetric encryption.
1054
+ * - Automatically processed for payments: if the message includes recipient fee payments, they are internalized using `walletClient.internalizeAction()`.
991
1055
  * - Returned as a normalized `PeerMessage` with readable string body content.
992
1056
  *
993
- * Decryption automatically derives a shared secret using the sender’s identity key and the receiver’s child private key.
1057
+ * Payment Processing:
1058
+ * - Detects messages that include payment data (from paid message delivery).
1059
+ * - Automatically internalizes recipient payment outputs, allowing you to receive payments without additional API calls.
1060
+ * - Only recipient payments are stored with messages - delivery fees are already processed by the server.
1061
+ * - Continues processing messages even if payment internalization fails.
1062
+ *
1063
+ * Decryption automatically derives a shared secret using the sender's identity key and the receiver's child private key.
994
1064
  * If the sender is the same as the recipient, the `counterparty` is set to `'self'`.
995
1065
  *
996
1066
  * @throws {Error} If no messageBox is specified, the request fails, or the server returns an error.
@@ -998,8 +1068,10 @@ class MessageBoxClient {
998
1068
  * @example
999
1069
  * const messages = await client.listMessages({ messageBox: 'inbox' })
1000
1070
  * messages.forEach(msg => console.log(msg.sender, msg.body))
1071
+ * // Payments included with messages are automatically received
1001
1072
  */
1002
1073
  async listMessages({ messageBox, host }) {
1074
+ var _a, _b;
1003
1075
  await this.assertInitialized();
1004
1076
  if (messageBox.trim() === '') {
1005
1077
  throw new Error('MessageBox cannot be empty');
@@ -1013,7 +1085,7 @@ class MessageBoxClient {
1013
1085
  const fetchFromHost = async (host) => {
1014
1086
  var _a;
1015
1087
  try {
1016
- logger_js_1.Logger.log(`[MB CLIENT] Listing messages from ${host}…`);
1088
+ Logger.log(`[MB CLIENT] Listing messages from ${host}…`);
1017
1089
  const res = await this.authFetch.fetch(`${host}/listMessages`, {
1018
1090
  method: 'POST',
1019
1091
  headers: { 'Content-Type': 'application/json' },
@@ -1027,7 +1099,7 @@ class MessageBoxClient {
1027
1099
  return data.messages;
1028
1100
  }
1029
1101
  catch (err) {
1030
- logger_js_1.Logger.log(`[MB CLIENT DEBUG] listMessages failed for ${host}:`, err);
1102
+ Logger.log(`[MB CLIENT DEBUG] listMessages failed for ${host}:`, err);
1031
1103
  throw err; // re-throw to be caught in the settled promise
1032
1104
  }
1033
1105
  };
@@ -1070,25 +1142,67 @@ class MessageBoxClient {
1070
1142
  for (const message of messages) {
1071
1143
  try {
1072
1144
  const parsedBody = typeof message.body === 'string' ? tryParse(message.body) : message.body;
1145
+ let messageContent = parsedBody;
1146
+ let paymentData;
1073
1147
  if (parsedBody != null &&
1074
1148
  typeof parsedBody === 'object' &&
1075
- typeof parsedBody.encryptedMessage === 'string') {
1076
- logger_js_1.Logger.log(`[MB CLIENT] Decrypting message from ${String(message.sender)}…`);
1149
+ 'message' in parsedBody) {
1150
+ messageContent = (_a = parsedBody.message) === null || _a === void 0 ? void 0 : _a.body;
1151
+ paymentData = parsedBody.payment;
1152
+ }
1153
+ // Process payment if present - server now only stores recipient payments
1154
+ if ((paymentData === null || paymentData === void 0 ? void 0 : paymentData.tx) != null && paymentData.outputs != null) {
1155
+ try {
1156
+ Logger.log(`[MB CLIENT] Processing recipient payment in message from ${String(message.sender)}…`);
1157
+ // All outputs in the stored payment data are for the recipient
1158
+ // (delivery fees are already processed by the server)
1159
+ const recipientOutputs = paymentData.outputs.filter(output => output.protocol === 'wallet payment');
1160
+ if (recipientOutputs.length > 0) {
1161
+ Logger.log(`[MB CLIENT] Internalizing ${recipientOutputs.length} recipient payment output(s)…`);
1162
+ const internalizeResult = await this.walletClient.internalizeAction({
1163
+ tx: paymentData.tx,
1164
+ outputs: recipientOutputs,
1165
+ description: (_b = paymentData.description) !== null && _b !== void 0 ? _b : 'MessageBox recipient payment'
1166
+ });
1167
+ if (internalizeResult.accepted) {
1168
+ Logger.log('[MB CLIENT] Successfully internalized recipient payment');
1169
+ }
1170
+ else {
1171
+ Logger.warn('[MB CLIENT] Recipient payment internalization was not accepted');
1172
+ }
1173
+ }
1174
+ else {
1175
+ Logger.log('[MB CLIENT] No wallet payment outputs found in payment data');
1176
+ }
1177
+ }
1178
+ catch (paymentError) {
1179
+ Logger.error('[MB CLIENT ERROR] Failed to internalize recipient payment:', paymentError);
1180
+ // Continue processing the message even if payment fails
1181
+ }
1182
+ }
1183
+ // Handle message decryption
1184
+ if (messageContent != null &&
1185
+ typeof messageContent === 'object' &&
1186
+ typeof messageContent.encryptedMessage === 'string') {
1187
+ Logger.log(`[MB CLIENT] Decrypting message from ${String(message.sender)}…`);
1077
1188
  const decrypted = await this.walletClient.decrypt({
1078
1189
  protocolID: [1, 'messagebox'],
1079
1190
  keyID: '1',
1080
1191
  counterparty: message.sender,
1081
- ciphertext: sdk_1.Utils.toArray(parsedBody.encryptedMessage, 'base64')
1192
+ ciphertext: sdk_1.Utils.toArray(messageContent.encryptedMessage, 'base64')
1082
1193
  });
1083
1194
  const decryptedText = sdk_1.Utils.toUTF8(decrypted.plaintext);
1084
1195
  message.body = tryParse(decryptedText);
1085
1196
  }
1086
1197
  else {
1087
- message.body = parsedBody;
1198
+ // Handle both old format (direct content) and new format (message.body)
1199
+ message.body = messageContent != null
1200
+ ? (typeof messageContent === 'string' ? messageContent : messageContent)
1201
+ : parsedBody;
1088
1202
  }
1089
1203
  }
1090
1204
  catch (err) {
1091
- logger_js_1.Logger.error('[MB CLIENT ERROR] Failed to parse or decrypt message in list:', err);
1205
+ Logger.error('[MB CLIENT ERROR] Failed to parse or decrypt message in list:', err);
1092
1206
  message.body = '[Error: Failed to decrypt or parse message]';
1093
1207
  }
1094
1208
  }
@@ -1123,7 +1237,7 @@ class MessageBoxClient {
1123
1237
  if (!Array.isArray(messageIds) || messageIds.length === 0) {
1124
1238
  throw new Error('Message IDs array cannot be empty');
1125
1239
  }
1126
- logger_js_1.Logger.log(`[MB CLIENT] Acknowledging messages ${JSON.stringify(messageIds)}…`);
1240
+ Logger.log(`[MB CLIENT] Acknowledging messages ${JSON.stringify(messageIds)}…`);
1127
1241
  let hosts = host != null ? [host] : [];
1128
1242
  if (hosts.length === 0) {
1129
1243
  // 1. Determine all hosts (advertised + default)
@@ -1144,11 +1258,11 @@ class MessageBoxClient {
1144
1258
  const data = await res.json();
1145
1259
  if (data.status === 'error')
1146
1260
  throw new Error(data.description);
1147
- logger_js_1.Logger.log(`[MB CLIENT] Acknowledged on ${host}`);
1261
+ Logger.log(`[MB CLIENT] Acknowledged on ${host}`);
1148
1262
  return data.status;
1149
1263
  }
1150
1264
  catch (err) {
1151
- logger_js_1.Logger.warn(`[MB CLIENT WARN] acknowledgeMessage failed for ${host}:`, err);
1265
+ Logger.warn(`[MB CLIENT WARN] acknowledgeMessage failed for ${host}:`, err);
1152
1266
  return null;
1153
1267
  }
1154
1268
  };
@@ -1166,6 +1280,512 @@ class MessageBoxClient {
1166
1280
  }
1167
1281
  throw new Error(`Failed to acknowledge messages on all hosts: ${errs.map(e => String(e)).join('; ')}`);
1168
1282
  }
1283
+ // ===========================
1284
+ // PERMISSION MANAGEMENT METHODS
1285
+ // ===========================
1286
+ /**
1287
+ * @method setMessageBoxPermission
1288
+ * @async
1289
+ * @param {SetMessageBoxPermissionParams} params - Permission configuration
1290
+ * @param {string} [overrideHost] - Optional host override
1291
+ * @returns {Promise<void>} Permission status after setting
1292
+ *
1293
+ * @description
1294
+ * Sets permission for receiving messages in a specific messageBox.
1295
+ * Can set sender-specific permissions or box-wide defaults.
1296
+ *
1297
+ * @example
1298
+ * // Set box-wide default: allow notifications for 10 sats
1299
+ * await client.setMessageBoxPermission({ messageBox: 'notifications', recipientFee: 10 })
1300
+ *
1301
+ * // Block specific sender
1302
+ * await client.setMessageBoxPermission({
1303
+ * messageBox: 'notifications',
1304
+ * sender: '03abc123...',
1305
+ * recipientFee: -1
1306
+ * })
1307
+ */
1308
+ async setMessageBoxPermission(params, overrideHost) {
1309
+ await this.assertInitialized();
1310
+ const finalHost = overrideHost !== null && overrideHost !== void 0 ? overrideHost : this.host;
1311
+ Logger.log('[MB CLIENT] Setting messageBox permission...');
1312
+ const response = await this.authFetch.fetch(`${finalHost}/permissions/set`, {
1313
+ method: 'POST',
1314
+ headers: { 'Content-Type': 'application/json' },
1315
+ body: JSON.stringify({
1316
+ messageBox: params.messageBox,
1317
+ recipientFee: params.recipientFee,
1318
+ ...(params.sender != null && { sender: params.sender })
1319
+ })
1320
+ });
1321
+ if (!response.ok) {
1322
+ const errorData = await response.json().catch(() => ({}));
1323
+ throw new Error(`Failed to set permission: HTTP ${response.status} - ${String(errorData.description) !== '' ? String(errorData.description) : response.statusText}`);
1324
+ }
1325
+ const { status, description } = await response.json();
1326
+ if (status === 'error') {
1327
+ throw new Error(description !== null && description !== void 0 ? description : 'Failed to set permission');
1328
+ }
1329
+ }
1330
+ /**
1331
+ * @method getMessageBoxPermission
1332
+ * @async
1333
+ * @param {GetMessageBoxPermissionParams} params - Permission query parameters
1334
+ * @param {string} [overrideHost] - Optional host override
1335
+ * @returns {Promise<MessageBoxPermission | null>} Permission data (null if not set)
1336
+ *
1337
+ * @description
1338
+ * Gets current permission data for a sender/messageBox combination.
1339
+ * Returns null if no permission is set.
1340
+ *
1341
+ * @example
1342
+ * const status = await client.getMessageBoxPermission({
1343
+ * recipient: '03def456...',
1344
+ * messageBox: 'notifications',
1345
+ * sender: '03abc123...'
1346
+ * })
1347
+ */
1348
+ async getMessageBoxPermission(params, overrideHost) {
1349
+ var _a;
1350
+ await this.assertInitialized();
1351
+ const finalHost = overrideHost !== null && overrideHost !== void 0 ? overrideHost : await this.resolveHostForRecipient(params.recipient);
1352
+ const queryParams = new URLSearchParams({
1353
+ recipient: params.recipient,
1354
+ messageBox: params.messageBox,
1355
+ ...(params.sender != null && { sender: params.sender })
1356
+ });
1357
+ Logger.log('[MB CLIENT] Getting messageBox permission...');
1358
+ const response = await this.authFetch.fetch(`${finalHost}/permissions/get?${queryParams.toString()}`, {
1359
+ method: 'GET'
1360
+ });
1361
+ if (!response.ok) {
1362
+ const errorData = await response.json().catch(() => ({}));
1363
+ throw new Error(`Failed to get permission: HTTP ${response.status} - ${String(errorData.description) !== '' ? String(errorData.description) : response.statusText}`);
1364
+ }
1365
+ const data = await response.json();
1366
+ if (data.status === 'error') {
1367
+ throw new Error((_a = data.description) !== null && _a !== void 0 ? _a : 'Failed to get permission');
1368
+ }
1369
+ return data.permission;
1370
+ }
1371
+ /**
1372
+ * @method getMessageBoxQuote
1373
+ * @async
1374
+ * @param {GetQuoteParams} params - Quote request parameters
1375
+ * @returns {Promise<MessageBoxQuote>} Fee quote and permission status
1376
+ *
1377
+ * @description
1378
+ * Gets a fee quote for sending a message, including delivery and recipient fees.
1379
+ *
1380
+ * @example
1381
+ * const quote = await client.getMessageBoxQuote({
1382
+ * recipient: '03def456...',
1383
+ * messageBox: 'notifications'
1384
+ * })
1385
+ */
1386
+ async getMessageBoxQuote(params, overrideHost) {
1387
+ var _a;
1388
+ await this.assertInitialized();
1389
+ const finalHost = overrideHost !== null && overrideHost !== void 0 ? overrideHost : await this.resolveHostForRecipient(params.recipient);
1390
+ const queryParams = new URLSearchParams({
1391
+ recipient: params.recipient,
1392
+ messageBox: params.messageBox
1393
+ });
1394
+ Logger.log('[MB CLIENT] Getting messageBox quote...');
1395
+ const response = await this.authFetch.fetch(`${finalHost}/permissions/quote?${queryParams.toString()}`, {
1396
+ method: 'GET'
1397
+ });
1398
+ if (!response.ok) {
1399
+ const errorData = await response.json().catch(() => ({}));
1400
+ throw new Error(`Failed to get quote: HTTP ${response.status} - ${(_a = String(errorData.description)) !== null && _a !== void 0 ? _a : response.statusText}`);
1401
+ }
1402
+ const { status, description, quote } = await response.json();
1403
+ if (status === 'error') {
1404
+ throw new Error(description !== null && description !== void 0 ? description : 'Failed to get quote');
1405
+ }
1406
+ const deliveryAgentIdentityKey = response.headers.get('x-bsv-auth-identity-key');
1407
+ if (deliveryAgentIdentityKey == null) {
1408
+ throw new Error('Failed to get quote: Delivery agent did not provide their identity key');
1409
+ }
1410
+ return {
1411
+ recipientFee: quote.recipientFee,
1412
+ deliveryFee: quote.deliveryFee,
1413
+ deliveryAgentIdentityKey
1414
+ };
1415
+ }
1416
+ /**
1417
+ * @method listMessageBoxPermissions
1418
+ * @async
1419
+ * @param {ListPermissionsParams} [params] - Optional filtering and pagination parameters
1420
+ * @returns {Promise<MessageBoxPermission[]>} List of current permissions
1421
+ *
1422
+ * @description
1423
+ * Lists permissions for the authenticated user's messageBoxes with optional pagination.
1424
+ *
1425
+ * @example
1426
+ * // List all permissions
1427
+ * const all = await client.listMessageBoxPermissions()
1428
+ *
1429
+ * // List only notification permissions with pagination
1430
+ * const notifications = await client.listMessageBoxPermissions({
1431
+ * messageBox: 'notifications',
1432
+ * limit: 50,
1433
+ * offset: 0
1434
+ * })
1435
+ */
1436
+ async listMessageBoxPermissions(params, overrideHost) {
1437
+ var _a;
1438
+ await this.assertInitialized();
1439
+ const finalHost = overrideHost !== null && overrideHost !== void 0 ? overrideHost : this.host;
1440
+ const queryParams = new URLSearchParams();
1441
+ if ((params === null || params === void 0 ? void 0 : params.messageBox) != null) {
1442
+ queryParams.set('message_box', params.messageBox);
1443
+ }
1444
+ if ((params === null || params === void 0 ? void 0 : params.limit) !== undefined) {
1445
+ queryParams.set('limit', params.limit.toString());
1446
+ }
1447
+ if ((params === null || params === void 0 ? void 0 : params.offset) !== undefined) {
1448
+ queryParams.set('offset', params.offset.toString());
1449
+ }
1450
+ Logger.log('[MB CLIENT] Listing messageBox permissions with params:', queryParams.toString());
1451
+ const response = await this.authFetch.fetch(`${finalHost}/permissions/list?${queryParams.toString()}`, {
1452
+ method: 'GET'
1453
+ });
1454
+ if (!response.ok) {
1455
+ const errorData = await response.json().catch(() => ({}));
1456
+ throw new Error(`Failed to list permissions: HTTP ${response.status} - ${String(errorData.description) !== '' ? String(errorData.description) : response.statusText}`);
1457
+ }
1458
+ const data = await response.json();
1459
+ if (data.status === 'error') {
1460
+ throw new Error((_a = data.description) !== null && _a !== void 0 ? _a : 'Failed to list permissions');
1461
+ }
1462
+ return data.permissions.map((p) => ({
1463
+ sender: p.sender,
1464
+ messageBox: p.message_box,
1465
+ recipientFee: p.recipient_fee,
1466
+ status: MessageBoxClient.getStatusFromFee(p.recipient_fee),
1467
+ createdAt: p.created_at,
1468
+ updatedAt: p.updated_at
1469
+ }));
1470
+ }
1471
+ // ===========================
1472
+ // NOTIFICATION CONVENIENCE METHODS
1473
+ // ===========================
1474
+ /**
1475
+ * @method allowNotificationsFromPeer
1476
+ * @async
1477
+ * @param {PubKeyHex} identityKey - Sender's identity key to allow
1478
+ * @param {number} [recipientFee=0] - Fee to charge (0 for always allow)
1479
+ * @param {string} [overrideHost] - Optional host override
1480
+ * @returns {Promise<void>} Permission status after allowing
1481
+ *
1482
+ * @description
1483
+ * Convenience method to allow notifications from a specific peer.
1484
+ *
1485
+ * @example
1486
+ * await client.allowNotificationsFromPeer('03abc123...') // Always allow
1487
+ * await client.allowNotificationsFromPeer('03def456...', 5) // Allow for 5 sats
1488
+ */
1489
+ async allowNotificationsFromPeer(identityKey, recipientFee = 0, overrideHost) {
1490
+ await this.setMessageBoxPermission({
1491
+ messageBox: 'notifications',
1492
+ sender: identityKey,
1493
+ recipientFee
1494
+ }, overrideHost);
1495
+ }
1496
+ /**
1497
+ * @method denyNotificationsFromPeer
1498
+ * @async
1499
+ * @param {PubKeyHex} identityKey - Sender's identity key to block
1500
+ * @returns {Promise<void>} Permission status after denying
1501
+ *
1502
+ * @description
1503
+ * Convenience method to block notifications from a specific peer.
1504
+ *
1505
+ * @example
1506
+ * await client.denyNotificationsFromPeer('03spam123...')
1507
+ */
1508
+ async denyNotificationsFromPeer(identityKey, overrideHost) {
1509
+ await this.setMessageBoxPermission({
1510
+ messageBox: 'notifications',
1511
+ sender: identityKey,
1512
+ recipientFee: -1
1513
+ }, overrideHost);
1514
+ }
1515
+ /**
1516
+ * @method checkPeerNotificationStatus
1517
+ * @async
1518
+ * @param {PubKeyHex} identityKey - Sender's identity key to check
1519
+ * @returns {Promise<MessageBoxPermission>} Current permission status
1520
+ *
1521
+ * @description
1522
+ * Convenience method to check notification permission for a specific peer.
1523
+ *
1524
+ * @example
1525
+ * const status = await client.checkPeerNotificationStatus('03abc123...')
1526
+ * console.log(status.allowed) // true/false
1527
+ */
1528
+ async checkPeerNotificationStatus(identityKey, overrideHost) {
1529
+ const myIdentityKey = await this.getIdentityKey();
1530
+ return await this.getMessageBoxPermission({
1531
+ recipient: myIdentityKey,
1532
+ messageBox: 'notifications',
1533
+ sender: identityKey
1534
+ }, overrideHost);
1535
+ }
1536
+ /**
1537
+ * @method listPeerNotifications
1538
+ * @async
1539
+ * @returns {Promise<MessageBoxPermission[]>} List of notification permissions
1540
+ *
1541
+ * @description
1542
+ * Convenience method to list all notification permissions.
1543
+ *
1544
+ * @example
1545
+ * const notifications = await client.listPeerNotifications()
1546
+ */
1547
+ async listPeerNotifications(overrideHost) {
1548
+ return await this.listMessageBoxPermissions({ messageBox: 'notifications' }, overrideHost);
1549
+ }
1550
+ /**
1551
+ * @method sendNotification
1552
+ * @async
1553
+ * @param {PubKeyHex} recipient - Recipient's identity key
1554
+ * @param {string | object} body - Notification content
1555
+ * @param {string} [overrideHost] - Optional host override
1556
+ * @returns {Promise<SendMessageResponse>} Send result
1557
+ *
1558
+ * @description
1559
+ * Convenience method to send a notification with automatic quote fetching and payment handling.
1560
+ * Automatically determines the required payment amount and creates the payment if needed.
1561
+ *
1562
+ * @example
1563
+ * // Send notification (auto-determines payment needed)
1564
+ * await client.sendNotification('03def456...', 'Hello!')
1565
+ *
1566
+ * // Send with maximum payment limit for safety
1567
+ * await client.sendNotification('03def456...', { title: 'Alert', body: 'Important update' }, 50)
1568
+ */
1569
+ async sendNotification(recipient, body, overrideHost) {
1570
+ await this.assertInitialized();
1571
+ // Use sendMessage with permission checking enabled
1572
+ // This eliminates duplication of quote fetching and payment logic
1573
+ return await this.sendMessage({
1574
+ recipient,
1575
+ messageBox: 'notifications',
1576
+ body,
1577
+ checkPermissions: true
1578
+ }, overrideHost);
1579
+ }
1580
+ /**
1581
+ * Register a device for FCM push notifications.
1582
+ *
1583
+ * @async
1584
+ * @param {DeviceRegistrationParams} params - Device registration parameters
1585
+ * @param {string} [overrideHost] - Optional host override
1586
+ * @returns {Promise<DeviceRegistrationResponse>} Registration response
1587
+ *
1588
+ * @description
1589
+ * Registers a device with the message box server to receive FCM push notifications.
1590
+ * The FCM token is obtained from Firebase SDK on the client side.
1591
+ *
1592
+ * @example
1593
+ * const result = await client.registerDevice({
1594
+ * fcmToken: 'eBo8F...',
1595
+ * platform: 'ios',
1596
+ * deviceId: 'iPhone15Pro'
1597
+ * })
1598
+ */
1599
+ async registerDevice(params, overrideHost) {
1600
+ var _a, _b, _c, _d, _e;
1601
+ await this.assertInitialized();
1602
+ if (params.fcmToken == null || params.fcmToken.trim() === '') {
1603
+ throw new Error('fcmToken is required and must be a non-empty string');
1604
+ }
1605
+ // Validate platform if provided
1606
+ const validPlatforms = ['ios', 'android', 'web'];
1607
+ if (params.platform != null && !validPlatforms.includes(params.platform)) {
1608
+ throw new Error('platform must be one of: ios, android, web');
1609
+ }
1610
+ const finalHost = overrideHost !== null && overrideHost !== void 0 ? overrideHost : this.host;
1611
+ Logger.log('[MB CLIENT] Registering device for FCM notifications...');
1612
+ const response = await this.authFetch.fetch(`${finalHost}/registerDevice`, {
1613
+ method: 'POST',
1614
+ headers: { 'Content-Type': 'application/json' },
1615
+ body: JSON.stringify({
1616
+ fcmToken: params.fcmToken.trim(),
1617
+ deviceId: (_b = (_a = params.deviceId) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : undefined,
1618
+ platform: (_c = params.platform) !== null && _c !== void 0 ? _c : undefined
1619
+ })
1620
+ });
1621
+ if (!response.ok) {
1622
+ const errorData = await response.json().catch(() => ({}));
1623
+ const description = (_d = String(errorData.description)) !== null && _d !== void 0 ? _d : response.statusText;
1624
+ throw new Error(`Failed to register device: HTTP ${response.status} - ${description}`);
1625
+ }
1626
+ const data = await response.json();
1627
+ if (data.status === 'error') {
1628
+ throw new Error((_e = data.description) !== null && _e !== void 0 ? _e : 'Failed to register device');
1629
+ }
1630
+ Logger.log('[MB CLIENT] Device registered successfully');
1631
+ return {
1632
+ status: data.status,
1633
+ message: data.message,
1634
+ deviceId: data.deviceId
1635
+ };
1636
+ }
1637
+ /**
1638
+ * List all registered devices for push notifications.
1639
+ *
1640
+ * @async
1641
+ * @param {string} [overrideHost] - Optional host override
1642
+ * @returns {Promise<RegisteredDevice[]>} Array of registered devices
1643
+ *
1644
+ * @description
1645
+ * Retrieves all devices registered by the authenticated user for FCM push notifications.
1646
+ * Only shows devices belonging to the current user (authenticated via AuthFetch).
1647
+ *
1648
+ * @example
1649
+ * const devices = await client.listRegisteredDevices()
1650
+ * console.log(`Found ${devices.length} registered devices`)
1651
+ * devices.forEach(device => {
1652
+ * console.log(`Device: ${device.platform} - ${device.fcmToken}`)
1653
+ * })
1654
+ */
1655
+ async listRegisteredDevices(overrideHost) {
1656
+ var _a, _b;
1657
+ await this.assertInitialized();
1658
+ const finalHost = overrideHost !== null && overrideHost !== void 0 ? overrideHost : this.host;
1659
+ Logger.log('[MB CLIENT] Listing registered devices...');
1660
+ const response = await this.authFetch.fetch(`${finalHost}/devices`, {
1661
+ method: 'GET'
1662
+ });
1663
+ if (!response.ok) {
1664
+ const errorData = await response.json().catch(() => ({}));
1665
+ const description = (_a = String(errorData.description)) !== null && _a !== void 0 ? _a : response.statusText;
1666
+ throw new Error(`Failed to list devices: HTTP ${response.status} - ${description}`);
1667
+ }
1668
+ const data = await response.json();
1669
+ if (data.status === 'error') {
1670
+ throw new Error((_b = data.description) !== null && _b !== void 0 ? _b : 'Failed to list devices');
1671
+ }
1672
+ Logger.log(`[MB CLIENT] Found ${data.devices.length} registered devices`);
1673
+ return data.devices;
1674
+ }
1675
+ // ===========================
1676
+ // PRIVATE HELPER METHODS
1677
+ // ===========================
1678
+ static getStatusFromFee(fee) {
1679
+ if (fee === -1)
1680
+ return 'blocked';
1681
+ if (fee === 0)
1682
+ return 'always_allow';
1683
+ return 'payment_required';
1684
+ }
1685
+ /**
1686
+ * Creates payment transaction for message delivery fees
1687
+ * TODO: Consider consolidating payment generating logic with a util PeerPayClient can use as well.
1688
+ * @private
1689
+ * @param {string} recipient - Recipient identity key
1690
+ * @param {MessageBoxQuote} quote - Fee quote with delivery and recipient fees
1691
+ * @param {string} description - Description for the payment transaction
1692
+ * @returns {Promise<Payment>} Payment transaction data
1693
+ */
1694
+ async createMessagePayment(recipient, quote, description = 'MessageBox delivery payment') {
1695
+ if (quote.recipientFee <= 0 && quote.deliveryFee <= 0) {
1696
+ throw new Error('No payment required');
1697
+ }
1698
+ Logger.log(`[MB CLIENT] Creating payment transaction for ${quote.recipientFee} sats (delivery: ${quote.deliveryFee}, recipient: ${quote.recipientFee})`);
1699
+ const outputs = [];
1700
+ const createActionOutputs = [];
1701
+ // Get sender identity key for remittance data
1702
+ const senderIdentityKey = await this.getIdentityKey();
1703
+ // Add server delivery fee output if > 0
1704
+ let outputIndex = 0;
1705
+ if (quote.deliveryFee > 0) {
1706
+ const derivationPrefix = sdk_1.Utils.toBase64((0, sdk_1.Random)(32));
1707
+ const derivationSuffix = sdk_1.Utils.toBase64((0, sdk_1.Random)(32));
1708
+ // Get host's derived public key
1709
+ const { publicKey: derivedKeyResult } = await this.walletClient.getPublicKey({
1710
+ protocolID: [2, '3241645161d8'],
1711
+ keyID: `${derivationPrefix} ${derivationSuffix}`,
1712
+ counterparty: quote.deliveryAgentIdentityKey
1713
+ });
1714
+ // Create locking script using host's public key
1715
+ const lockingScript = new sdk_1.P2PKH().lock(sdk_1.PublicKey.fromString(derivedKeyResult).toAddress()).toHex();
1716
+ // Add to createAction outputs
1717
+ createActionOutputs.push({
1718
+ satoshis: quote.deliveryFee,
1719
+ lockingScript,
1720
+ outputDescription: 'MessageBox server delivery fee',
1721
+ customInstructions: JSON.stringify({
1722
+ derivationPrefix,
1723
+ derivationSuffix,
1724
+ recipientIdentityKey: quote.deliveryAgentIdentityKey
1725
+ })
1726
+ });
1727
+ outputs.push({
1728
+ outputIndex: outputIndex++,
1729
+ protocol: 'wallet payment',
1730
+ paymentRemittance: {
1731
+ derivationPrefix,
1732
+ derivationSuffix,
1733
+ senderIdentityKey
1734
+ }
1735
+ });
1736
+ }
1737
+ // Add recipient fee output if > 0
1738
+ if (quote.recipientFee > 0) {
1739
+ const derivationPrefix = sdk_1.Utils.toBase64((0, sdk_1.Random)(32));
1740
+ const derivationSuffix = sdk_1.Utils.toBase64((0, sdk_1.Random)(32));
1741
+ // Get a derived public key for the recipient that "anyone" can verify
1742
+ const anyoneWallet = new sdk_1.ProtoWallet('anyone');
1743
+ const { publicKey: derivedKeyResult } = await anyoneWallet.getPublicKey({
1744
+ protocolID: [2, '3241645161d8'],
1745
+ keyID: `${derivationPrefix} ${derivationSuffix}`,
1746
+ counterparty: recipient
1747
+ });
1748
+ if (derivedKeyResult == null || derivedKeyResult.trim() === '') {
1749
+ throw new Error('Failed to derive recipient\'s public key');
1750
+ }
1751
+ // Create locking script using recipient's public key
1752
+ const lockingScript = new sdk_1.P2PKH().lock(sdk_1.PublicKey.fromString(derivedKeyResult).toAddress()).toHex();
1753
+ // Add to createAction outputs
1754
+ createActionOutputs.push({
1755
+ satoshis: quote.recipientFee,
1756
+ lockingScript,
1757
+ outputDescription: 'Recipient message fee',
1758
+ customInstructions: JSON.stringify({
1759
+ derivationPrefix,
1760
+ derivationSuffix,
1761
+ recipientIdentityKey: recipient
1762
+ })
1763
+ });
1764
+ outputs.push({
1765
+ outputIndex: outputIndex++,
1766
+ protocol: 'wallet payment',
1767
+ paymentRemittance: {
1768
+ derivationPrefix,
1769
+ derivationSuffix,
1770
+ senderIdentityKey
1771
+ }
1772
+ });
1773
+ }
1774
+ const { tx } = await this.walletClient.createAction({
1775
+ description,
1776
+ outputs: createActionOutputs,
1777
+ options: { randomizeOutputs: false, acceptDelayedBroadcast: false }
1778
+ });
1779
+ if (tx == null) {
1780
+ throw new Error('Failed to create payment transaction');
1781
+ }
1782
+ return {
1783
+ tx,
1784
+ outputs,
1785
+ description
1786
+ // labels
1787
+ };
1788
+ }
1169
1789
  }
1170
1790
  exports.MessageBoxClient = MessageBoxClient;
1171
1791
  //# sourceMappingURL=MessageBoxClient.js.map