@arkade-os/sdk 0.4.7 → 0.4.8

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.
@@ -105,7 +105,7 @@ class SeedIdentity {
105
105
  static fromSeed(seed, opts) {
106
106
  const descriptor = hasDescriptor(opts)
107
107
  ? opts.descriptor
108
- : buildDescriptor(seed, opts.isMainnet);
108
+ : buildDescriptor(seed, opts.isMainnet ?? true);
109
109
  return new SeedIdentity(seed, descriptor);
110
110
  }
111
111
  async xOnlyPublicKey() {
@@ -194,7 +194,7 @@ class MnemonicIdentity extends SeedIdentity {
194
194
  const seed = (0, bip39_1.mnemonicToSeedSync)(phrase, passphrase);
195
195
  const descriptor = hasDescriptor(opts)
196
196
  ? opts.descriptor
197
- : buildDescriptor(seed, opts.isMainnet);
197
+ : buildDescriptor(seed, opts.isMainnet ?? true);
198
198
  return new MnemonicIdentity(seed, descriptor);
199
199
  }
200
200
  }
package/dist/cjs/index.js CHANGED
@@ -36,8 +36,9 @@ var __importStar = (this && this.__importStar) || (function () {
36
36
  };
37
37
  })();
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.hasBoardingTxExpired = exports.waitForIncomingFunds = exports.verifyTapscriptSignatures = exports.buildOffchainTx = exports.ConditionWitness = exports.VtxoTaprootTree = exports.VtxoTreeExpiry = exports.CosignerPublicKey = exports.getArkPsbtFields = exports.setArkPsbtField = exports.ArkPsbtFieldKeyType = exports.ArkPsbtFieldKey = exports.TapTreeCoder = exports.CLTVMultisigTapscript = exports.ConditionMultisigTapscript = exports.ConditionCSVMultisigTapscript = exports.CSVMultisigTapscript = exports.MultisigTapscript = exports.decodeTapscript = exports.ServiceWorkerReadonlyWallet = exports.ServiceWorkerWallet = exports.WalletMessageHandler = exports.MessageBus = exports.setupServiceWorker = exports.SettlementEventType = exports.ChainTxType = exports.IndexerTxType = exports.TxType = exports.VHTLC = exports.VtxoScript = exports.DelegateVtxo = exports.DefaultVtxo = exports.ArkAddress = exports.RestIndexerProvider = exports.RestArkProvider = exports.EsploraProvider = exports.ESPLORA_URL = exports.RestDelegatorProvider = exports.DelegatorManagerImpl = exports.VtxoManager = exports.Ramps = exports.OnchainWallet = exports.ReadonlyDescriptorIdentity = exports.MnemonicIdentity = exports.SeedIdentity = exports.ReadonlySingleKey = exports.SingleKey = exports.ReadonlyWallet = exports.Wallet = exports.asset = void 0;
40
- exports.isArkContract = exports.contractFromArkContractWithAddress = exports.contractFromArkContract = exports.decodeArkContract = exports.encodeArkContract = exports.VHTLCContractHandler = exports.DelegateContractHandler = exports.DefaultContractHandler = exports.contractHandlers = exports.ContractWatcher = exports.ContractManager = exports.getSequence = exports.isExpired = exports.isSubdust = exports.isSpendable = exports.isRecoverable = exports.buildForfeitTx = exports.validateConnectorsTxGraph = exports.validateVtxoTxGraph = exports.Batch = exports.maybeArkError = exports.ArkError = exports.Transaction = exports.Unroll = exports.P2A = exports.TxTree = exports.BIP322 = exports.Intent = exports.ContractRepositoryImpl = exports.WalletRepositoryImpl = exports.rollbackMigration = exports.getMigrationStatus = exports.requiresMigration = exports.migrateWalletRepository = exports.MIGRATION_KEY = exports.InMemoryContractRepository = exports.InMemoryWalletRepository = exports.IndexedDBContractRepository = exports.IndexedDBWalletRepository = exports.openDatabase = exports.closeDatabase = exports.networks = exports.ArkNote = exports.isValidArkAddress = exports.isVtxoExpiringSoon = exports.combineTapscriptSigs = void 0;
39
+ exports.VtxoTaprootTree = exports.VtxoTreeExpiry = exports.CosignerPublicKey = exports.getArkPsbtFields = exports.setArkPsbtField = exports.ArkPsbtFieldKeyType = exports.ArkPsbtFieldKey = exports.TapTreeCoder = exports.CLTVMultisigTapscript = exports.ConditionMultisigTapscript = exports.ConditionCSVMultisigTapscript = exports.CSVMultisigTapscript = exports.MultisigTapscript = exports.decodeTapscript = exports.ServiceWorkerReadonlyWallet = exports.ServiceWorkerWallet = exports.ServiceWorkerTimeoutError = exports.MessageBusNotInitializedError = exports.DelegatorNotConfiguredError = exports.ReadonlyWalletError = exports.WalletNotInitializedError = exports.WalletMessageHandler = exports.MessageBus = exports.setupServiceWorker = exports.SettlementEventType = exports.ChainTxType = exports.IndexerTxType = exports.TxType = exports.VHTLC = exports.VtxoScript = exports.DelegateVtxo = exports.DefaultVtxo = exports.ArkAddress = exports.RestIndexerProvider = exports.RestArkProvider = exports.EsploraProvider = exports.ESPLORA_URL = exports.RestDelegatorProvider = exports.DelegatorManagerImpl = exports.VtxoManager = exports.Ramps = exports.OnchainWallet = exports.ReadonlyDescriptorIdentity = exports.MnemonicIdentity = exports.SeedIdentity = exports.ReadonlySingleKey = exports.SingleKey = exports.ReadonlyWallet = exports.Wallet = exports.asset = void 0;
40
+ exports.contractFromArkContractWithAddress = exports.contractFromArkContract = exports.decodeArkContract = exports.encodeArkContract = exports.VHTLCContractHandler = exports.DelegateContractHandler = exports.DefaultContractHandler = exports.contractHandlers = exports.ContractWatcher = exports.ContractManager = exports.getSequence = exports.isExpired = exports.isSubdust = exports.isSpendable = exports.isRecoverable = exports.buildForfeitTx = exports.validateConnectorsTxGraph = exports.validateVtxoTxGraph = exports.Batch = exports.maybeArkError = exports.ArkError = exports.Transaction = exports.Unroll = exports.P2A = exports.TxTree = exports.BIP322 = exports.Intent = exports.ContractRepositoryImpl = exports.WalletRepositoryImpl = exports.rollbackMigration = exports.getMigrationStatus = exports.requiresMigration = exports.migrateWalletRepository = exports.MIGRATION_KEY = exports.InMemoryContractRepository = exports.InMemoryWalletRepository = exports.IndexedDBContractRepository = exports.IndexedDBWalletRepository = exports.openDatabase = exports.closeDatabase = exports.networks = exports.ArkNote = exports.isValidArkAddress = exports.isVtxoExpiringSoon = exports.combineTapscriptSigs = exports.hasBoardingTxExpired = exports.waitForIncomingFunds = exports.verifyTapscriptSignatures = exports.buildOffchainTx = exports.ConditionWitness = void 0;
41
+ exports.isArkContract = void 0;
41
42
  const transaction_1 = require("./utils/transaction");
42
43
  Object.defineProperty(exports, "Transaction", { enumerable: true, get: function () { return transaction_1.Transaction; } });
43
44
  const singleKey_1 = require("./identity/singleKey");
@@ -175,3 +176,9 @@ Object.defineProperty(exports, "closeDatabase", { enumerable: true, get: functio
175
176
  Object.defineProperty(exports, "openDatabase", { enumerable: true, get: function () { return manager_1.openDatabase; } });
176
177
  const wallet_message_handler_1 = require("./wallet/serviceWorker/wallet-message-handler");
177
178
  Object.defineProperty(exports, "WalletMessageHandler", { enumerable: true, get: function () { return wallet_message_handler_1.WalletMessageHandler; } });
179
+ Object.defineProperty(exports, "WalletNotInitializedError", { enumerable: true, get: function () { return wallet_message_handler_1.WalletNotInitializedError; } });
180
+ Object.defineProperty(exports, "ReadonlyWalletError", { enumerable: true, get: function () { return wallet_message_handler_1.ReadonlyWalletError; } });
181
+ Object.defineProperty(exports, "DelegatorNotConfiguredError", { enumerable: true, get: function () { return wallet_message_handler_1.DelegatorNotConfiguredError; } });
182
+ const errors_2 = require("./worker/errors");
183
+ Object.defineProperty(exports, "MessageBusNotInitializedError", { enumerable: true, get: function () { return errors_2.MessageBusNotInitializedError; } });
184
+ Object.defineProperty(exports, "ServiceWorkerTimeoutError", { enumerable: true, get: function () { return errors_2.ServiceWorkerTimeoutError; } });
@@ -1,9 +1,30 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.WalletMessageHandler = exports.DEFAULT_MESSAGE_TAG = void 0;
3
+ exports.WalletMessageHandler = exports.DEFAULT_MESSAGE_TAG = exports.DelegatorNotConfiguredError = exports.ReadonlyWalletError = exports.WalletNotInitializedError = void 0;
4
4
  const indexer_1 = require("../../providers/indexer");
5
5
  const index_1 = require("../index");
6
6
  const utils_1 = require("../utils");
7
+ class WalletNotInitializedError extends Error {
8
+ constructor() {
9
+ super("Wallet handler not initialized");
10
+ this.name = "WalletNotInitializedError";
11
+ }
12
+ }
13
+ exports.WalletNotInitializedError = WalletNotInitializedError;
14
+ class ReadonlyWalletError extends Error {
15
+ constructor() {
16
+ super("Read-only wallet: operation requires signing");
17
+ this.name = "ReadonlyWalletError";
18
+ }
19
+ }
20
+ exports.ReadonlyWalletError = ReadonlyWalletError;
21
+ class DelegatorNotConfiguredError extends Error {
22
+ constructor() {
23
+ super("Delegator not configured");
24
+ this.name = "DelegatorNotConfiguredError";
25
+ }
26
+ }
27
+ exports.DelegatorNotConfiguredError = DelegatorNotConfiguredError;
7
28
  exports.DEFAULT_MESSAGE_TAG = "WALLET_UPDATER";
8
29
  class WalletMessageHandler {
9
30
  /**
@@ -24,7 +45,31 @@ class WalletMessageHandler {
24
45
  this.walletRepository = repositories.walletRepository;
25
46
  }
26
47
  async stop() {
27
- // optional cleanup and persistence
48
+ if (this.incomingFundsSubscription) {
49
+ this.incomingFundsSubscription();
50
+ this.incomingFundsSubscription = undefined;
51
+ }
52
+ if (this.contractEventsSubscription) {
53
+ this.contractEventsSubscription();
54
+ this.contractEventsSubscription = undefined;
55
+ }
56
+ // Dispose the wallet to stop VtxoManager background tasks
57
+ // (auto-renewal, boarding UTXO polling) and ContractWatcher.
58
+ try {
59
+ if (this.wallet) {
60
+ await this.wallet.dispose();
61
+ }
62
+ else if (this.readonlyWallet) {
63
+ await this.readonlyWallet.dispose();
64
+ }
65
+ }
66
+ catch (_) {
67
+ // best-effort teardown
68
+ }
69
+ this.wallet = undefined;
70
+ this.readonlyWallet = undefined;
71
+ this.arkProvider = undefined;
72
+ this.indexerProvider = undefined;
28
73
  }
29
74
  async tick(_now) {
30
75
  const results = await Promise.allSettled(this.onNextTick.map((fn) => fn()));
@@ -47,7 +92,7 @@ class WalletMessageHandler {
47
92
  }
48
93
  requireWallet() {
49
94
  if (!this.wallet) {
50
- throw new Error("Read-only wallet: operation requires signing");
95
+ throw new ReadonlyWalletError();
51
96
  }
52
97
  return this.wallet;
53
98
  }
@@ -69,7 +114,7 @@ class WalletMessageHandler {
69
114
  if (!this.readonlyWallet) {
70
115
  return this.tagged({
71
116
  id,
72
- error: new Error("Wallet handler not initialized"),
117
+ error: new WalletNotInitializedError(),
73
118
  });
74
119
  }
75
120
  try {
@@ -297,7 +342,7 @@ class WalletMessageHandler {
297
342
  const wallet = this.requireWallet();
298
343
  const delegatorManager = await wallet.getDelegatorManager();
299
344
  if (!delegatorManager) {
300
- throw new Error("Delegator not configured");
345
+ throw new DelegatorNotConfiguredError();
301
346
  }
302
347
  const info = await delegatorManager.getDelegateInfo();
303
348
  return this.tagged({
@@ -306,6 +351,83 @@ class WalletMessageHandler {
306
351
  payload: { info },
307
352
  });
308
353
  }
354
+ case "RECOVER_VTXOS": {
355
+ const wallet = this.requireWallet();
356
+ const vtxoManager = await wallet.getVtxoManager();
357
+ const txid = await vtxoManager.recoverVtxos((e) => {
358
+ this.scheduleForNextTick(() => this.tagged({
359
+ id,
360
+ type: "RECOVER_VTXOS_EVENT",
361
+ payload: e,
362
+ }));
363
+ });
364
+ return this.tagged({
365
+ id,
366
+ type: "RECOVER_VTXOS_SUCCESS",
367
+ payload: { txid },
368
+ });
369
+ }
370
+ case "GET_RECOVERABLE_BALANCE": {
371
+ const wallet = this.requireWallet();
372
+ const vtxoManager = await wallet.getVtxoManager();
373
+ const balance = await vtxoManager.getRecoverableBalance();
374
+ return this.tagged({
375
+ id,
376
+ type: "RECOVERABLE_BALANCE",
377
+ payload: {
378
+ recoverable: balance.recoverable.toString(),
379
+ subdust: balance.subdust.toString(),
380
+ includesSubdust: balance.includesSubdust,
381
+ vtxoCount: balance.vtxoCount,
382
+ },
383
+ });
384
+ }
385
+ case "GET_EXPIRING_VTXOS": {
386
+ const wallet = this.requireWallet();
387
+ const vtxoManager = await wallet.getVtxoManager();
388
+ const vtxos = await vtxoManager.getExpiringVtxos(message.payload.thresholdMs);
389
+ return this.tagged({
390
+ id,
391
+ type: "EXPIRING_VTXOS",
392
+ payload: { vtxos },
393
+ });
394
+ }
395
+ case "RENEW_VTXOS": {
396
+ const wallet = this.requireWallet();
397
+ const vtxoManager = await wallet.getVtxoManager();
398
+ const txid = await vtxoManager.renewVtxos((e) => {
399
+ this.scheduleForNextTick(() => this.tagged({
400
+ id,
401
+ type: "RENEW_VTXOS_EVENT",
402
+ payload: e,
403
+ }));
404
+ });
405
+ return this.tagged({
406
+ id,
407
+ type: "RENEW_VTXOS_SUCCESS",
408
+ payload: { txid },
409
+ });
410
+ }
411
+ case "GET_EXPIRED_BOARDING_UTXOS": {
412
+ const wallet = this.requireWallet();
413
+ const vtxoManager = await wallet.getVtxoManager();
414
+ const utxos = await vtxoManager.getExpiredBoardingUtxos();
415
+ return this.tagged({
416
+ id,
417
+ type: "EXPIRED_BOARDING_UTXOS",
418
+ payload: { utxos },
419
+ });
420
+ }
421
+ case "SWEEP_EXPIRED_BOARDING_UTXOS": {
422
+ const wallet = this.requireWallet();
423
+ const vtxoManager = await wallet.getVtxoManager();
424
+ const txid = await vtxoManager.sweepExpiredBoardingUtxos();
425
+ return this.tagged({
426
+ id,
427
+ type: "SWEEP_EXPIRED_BOARDING_UTXOS_SUCCESS",
428
+ payload: { txid },
429
+ });
430
+ }
309
431
  default:
310
432
  console.error("Unknown message type", message);
311
433
  throw new Error("Unknown message");
@@ -481,6 +603,17 @@ class WalletMessageHandler {
481
603
  }
482
604
  });
483
605
  await this.ensureContractEventBroadcasting();
606
+ // Eagerly start the VtxoManager so its background tasks (auto-renewal,
607
+ // boarding UTXO polling/sweep) run inside the service worker without
608
+ // waiting for a client to send a vtxo-manager message first.
609
+ if (this.wallet) {
610
+ try {
611
+ await this.wallet.getVtxoManager();
612
+ }
613
+ catch (error) {
614
+ console.error("Error starting VtxoManager:", error);
615
+ }
616
+ }
484
617
  }
485
618
  async handleSettle(message) {
486
619
  const wallet = this.requireWallet();
@@ -523,7 +656,7 @@ class WalletMessageHandler {
523
656
  const wallet = this.requireWallet();
524
657
  const delegatorManager = await wallet.getDelegatorManager();
525
658
  if (!delegatorManager) {
526
- throw new Error("Delegator not configured");
659
+ throw new DelegatorNotConfiguredError();
527
660
  }
528
661
  const { vtxoOutpoints, destination, delegateAt } = message.payload;
529
662
  const allVtxos = await wallet.getVtxos();
@@ -550,7 +683,7 @@ class WalletMessageHandler {
550
683
  }
551
684
  async handleGetVtxos(message) {
552
685
  if (!this.readonlyWallet) {
553
- throw new Error("Wallet handler not initialized");
686
+ throw new WalletNotInitializedError();
554
687
  }
555
688
  const vtxos = await this.getSpendableVtxos();
556
689
  const dustAmount = this.readonlyWallet.dustAmount;
@@ -6,6 +6,37 @@ const utils_1 = require("../../worker/browser/utils");
6
6
  const repositories_1 = require("../../repositories");
7
7
  const wallet_message_handler_1 = require("./wallet-message-handler");
8
8
  const utils_2 = require("../utils");
9
+ const errors_1 = require("../../worker/errors");
10
+ // Check by error name instead of instanceof because postMessage uses the
11
+ // structured clone algorithm which strips the prototype chain — the page
12
+ // receives a plain Error, not the original MessageBusNotInitializedError.
13
+ function isMessageBusNotInitializedError(error) {
14
+ return (error instanceof Error && error.name === "MessageBusNotInitializedError");
15
+ }
16
+ const DEDUPABLE_REQUEST_TYPES = new Set([
17
+ "GET_ADDRESS",
18
+ "GET_BALANCE",
19
+ "GET_BOARDING_ADDRESS",
20
+ "GET_BOARDING_UTXOS",
21
+ "GET_STATUS",
22
+ "GET_TRANSACTION_HISTORY",
23
+ "IS_CONTRACT_MANAGER_WATCHING",
24
+ "GET_DELEGATE_INFO",
25
+ "GET_RECOVERABLE_BALANCE",
26
+ "GET_EXPIRED_BOARDING_UTXOS",
27
+ "GET_VTXOS",
28
+ "GET_CONTRACTS",
29
+ "GET_CONTRACTS_WITH_VTXOS",
30
+ "GET_SPENDABLE_PATHS",
31
+ "GET_ALL_SPENDING_PATHS",
32
+ "GET_ASSET_DETAILS",
33
+ "GET_EXPIRING_VTXOS",
34
+ "RELOAD_WALLET",
35
+ ]);
36
+ function getRequestDedupKey(request) {
37
+ const { id, tag, ...rest } = request;
38
+ return JSON.stringify(rest);
39
+ }
9
40
  const isPrivateKeyIdentity = (identity) => {
10
41
  return typeof identity.toHex === "function";
11
42
  };
@@ -82,7 +113,7 @@ const initializeMessageBus = (serviceWorker, config, timeoutMs = 2000) => {
82
113
  };
83
114
  const timeoutId = setTimeout(() => {
84
115
  cleanup();
85
- reject(new Error("MessageBus timed out!"));
116
+ reject(new errors_1.ServiceWorkerTimeoutError("MessageBus timed out"));
86
117
  }, timeoutMs);
87
118
  navigator.serviceWorker.addEventListener("message", onMessage);
88
119
  serviceWorker.postMessage(initCmd);
@@ -98,6 +129,8 @@ class ServiceWorkerReadonlyWallet {
98
129
  this.initConfig = null;
99
130
  this.initWalletPayload = null;
100
131
  this.reinitPromise = null;
132
+ this.pingPromise = null;
133
+ this.inflightRequests = new Map();
101
134
  this.identity = identity;
102
135
  this.walletRepository = walletRepository;
103
136
  this.contractRepository = contractRepository;
@@ -191,7 +224,7 @@ class ServiceWorkerReadonlyWallet {
191
224
  };
192
225
  const timeoutId = setTimeout(() => {
193
226
  cleanup();
194
- reject(new Error(`Service worker message timed out (${request.type})`));
227
+ reject(new errors_1.ServiceWorkerTimeoutError(`Service worker message timed out (${request.type})`));
195
228
  }, 30000);
196
229
  const messageHandler = (event) => {
197
230
  const response = event.data;
@@ -210,18 +243,137 @@ class ServiceWorkerReadonlyWallet {
210
243
  this.serviceWorker.postMessage(request);
211
244
  });
212
245
  }
246
+ // Like sendMessageDirect but supports streaming responses: intermediate
247
+ // messages are forwarded via onEvent while the promise resolves on the
248
+ // first response for which isComplete returns true. The timeout resets
249
+ // on every intermediate event so long-running but progressing operations
250
+ // don't time out prematurely.
251
+ sendMessageStreaming(request, onEvent, isComplete) {
252
+ return new Promise((resolve, reject) => {
253
+ const resetTimeout = () => {
254
+ clearTimeout(timeoutId);
255
+ timeoutId = setTimeout(() => {
256
+ cleanup();
257
+ reject(new errors_1.ServiceWorkerTimeoutError(`Service worker message timed out (${request.type})`));
258
+ }, 30000);
259
+ };
260
+ const cleanup = () => {
261
+ clearTimeout(timeoutId);
262
+ navigator.serviceWorker.removeEventListener("message", messageHandler);
263
+ };
264
+ let timeoutId;
265
+ resetTimeout();
266
+ const messageHandler = (event) => {
267
+ const response = event.data;
268
+ if (request.id !== response.id)
269
+ return;
270
+ if (response.error) {
271
+ cleanup();
272
+ reject(response.error);
273
+ return;
274
+ }
275
+ if (isComplete(response)) {
276
+ cleanup();
277
+ resolve(response);
278
+ }
279
+ else {
280
+ resetTimeout();
281
+ onEvent(response);
282
+ }
283
+ };
284
+ navigator.serviceWorker.addEventListener("message", messageHandler);
285
+ this.serviceWorker.postMessage(request);
286
+ });
287
+ }
288
+ async sendMessage(request) {
289
+ if (!DEDUPABLE_REQUEST_TYPES.has(request.type)) {
290
+ return this.sendMessageWithRetry(request);
291
+ }
292
+ const key = getRequestDedupKey(request);
293
+ const existing = this.inflightRequests.get(key);
294
+ if (existing)
295
+ return existing;
296
+ const promise = this.sendMessageWithRetry(request).finally(() => {
297
+ this.inflightRequests.delete(key);
298
+ });
299
+ this.inflightRequests.set(key, promise);
300
+ return promise;
301
+ }
302
+ pingServiceWorker() {
303
+ if (this.pingPromise)
304
+ return this.pingPromise;
305
+ this.pingPromise = new Promise((resolve, reject) => {
306
+ const pingId = (0, utils_2.getRandomId)();
307
+ const cleanup = () => {
308
+ clearTimeout(timeoutId);
309
+ navigator.serviceWorker.removeEventListener("message", onMessage);
310
+ };
311
+ const timeoutId = setTimeout(() => {
312
+ cleanup();
313
+ reject(new errors_1.ServiceWorkerTimeoutError("Service worker ping timed out"));
314
+ }, 2000);
315
+ const onMessage = (event) => {
316
+ if (event.data?.id === pingId && event.data?.tag === "PONG") {
317
+ cleanup();
318
+ resolve();
319
+ }
320
+ };
321
+ navigator.serviceWorker.addEventListener("message", onMessage);
322
+ this.serviceWorker.postMessage({
323
+ id: pingId,
324
+ tag: "PING",
325
+ });
326
+ }).finally(() => {
327
+ this.pingPromise = null;
328
+ });
329
+ return this.pingPromise;
330
+ }
213
331
  // send a message, retrying up to 2 times if the service worker was
214
332
  // killed and restarted by the OS (mobile browsers do this aggressively)
215
- async sendMessage(request) {
333
+ async sendMessageWithRetry(request) {
334
+ // Skip the preflight ping during the initial INIT_WALLET call:
335
+ // create() hasn't set initConfig yet, so reinitialize() would throw.
336
+ if (this.initConfig) {
337
+ try {
338
+ await this.pingServiceWorker();
339
+ }
340
+ catch {
341
+ await this.reinitialize();
342
+ }
343
+ }
216
344
  const maxRetries = 2;
217
345
  for (let attempt = 0;; attempt++) {
218
346
  try {
219
347
  return await this.sendMessageDirect(request);
220
348
  }
221
349
  catch (error) {
222
- const isNotInitialized = typeof error?.message === "string" &&
223
- error.message.includes("MessageBus not initialized");
224
- if (!isNotInitialized || attempt >= maxRetries) {
350
+ if (!isMessageBusNotInitializedError(error) ||
351
+ attempt >= maxRetries) {
352
+ throw error;
353
+ }
354
+ await this.reinitialize();
355
+ }
356
+ }
357
+ }
358
+ // Like sendMessage but for streaming responses — retries with
359
+ // reinitialize when the service worker has been killed/restarted.
360
+ async sendMessageWithEvents(request, onEvent, isComplete) {
361
+ if (this.initConfig) {
362
+ try {
363
+ await this.pingServiceWorker();
364
+ }
365
+ catch {
366
+ await this.reinitialize();
367
+ }
368
+ }
369
+ const maxRetries = 2;
370
+ for (let attempt = 0;; attempt++) {
371
+ try {
372
+ return await this.sendMessageStreaming(request, onEvent, isComplete);
373
+ }
374
+ catch (error) {
375
+ if (!isMessageBusNotInitializedError(error) ||
376
+ attempt >= maxRetries) {
225
377
  throw error;
226
378
  }
227
379
  await this.reinitialize();
@@ -675,34 +827,8 @@ class ServiceWorkerWallet extends ServiceWorkerReadonlyWallet {
675
827
  payload: { params },
676
828
  };
677
829
  try {
678
- return new Promise((resolve, reject) => {
679
- const messageHandler = (event) => {
680
- const response = event.data;
681
- if (response.id !== message.id) {
682
- return;
683
- }
684
- if (response.error) {
685
- navigator.serviceWorker.removeEventListener("message", messageHandler);
686
- reject(response.error);
687
- return;
688
- }
689
- switch (response.type) {
690
- case "SETTLE_EVENT":
691
- if (callback) {
692
- callback(response.payload);
693
- }
694
- break;
695
- case "SETTLE_SUCCESS":
696
- navigator.serviceWorker.removeEventListener("message", messageHandler);
697
- resolve(response.payload.txid);
698
- break;
699
- default:
700
- console.error(`Unexpected response type for SETTLE request: ${response.type}`);
701
- }
702
- };
703
- navigator.serviceWorker.addEventListener("message", messageHandler);
704
- this.serviceWorker.postMessage(message);
705
- });
830
+ const response = await this.sendMessageWithEvents(message, (resp) => callback?.(resp.payload), (resp) => resp.type === "SETTLE_SUCCESS");
831
+ return response.payload.txid;
706
832
  }
707
833
  catch (error) {
708
834
  throw new Error(`Settlement failed: ${error}`);
@@ -776,5 +902,109 @@ class ServiceWorkerWallet extends ServiceWorkerReadonlyWallet {
776
902
  };
777
903
  return manager;
778
904
  }
905
+ async getVtxoManager() {
906
+ const wallet = this;
907
+ const messageTag = this.messageTag;
908
+ const manager = {
909
+ async recoverVtxos(eventCallback) {
910
+ const message = {
911
+ tag: messageTag,
912
+ type: "RECOVER_VTXOS",
913
+ id: (0, utils_2.getRandomId)(),
914
+ };
915
+ try {
916
+ const response = await wallet.sendMessageWithEvents(message, (resp) => eventCallback?.(resp.payload), (resp) => resp.type === "RECOVER_VTXOS_SUCCESS");
917
+ return response.payload.txid;
918
+ }
919
+ catch (e) {
920
+ throw new Error(`Failed to recover vtxos: ${e}`);
921
+ }
922
+ },
923
+ async getRecoverableBalance() {
924
+ const message = {
925
+ tag: messageTag,
926
+ type: "GET_RECOVERABLE_BALANCE",
927
+ id: (0, utils_2.getRandomId)(),
928
+ };
929
+ try {
930
+ const response = await wallet.sendMessage(message);
931
+ const payload = response
932
+ .payload;
933
+ return {
934
+ recoverable: BigInt(payload.recoverable),
935
+ subdust: BigInt(payload.subdust),
936
+ includesSubdust: payload.includesSubdust,
937
+ vtxoCount: payload.vtxoCount,
938
+ };
939
+ }
940
+ catch (e) {
941
+ throw new Error(`Failed to get recoverable balance: ${e}`);
942
+ }
943
+ },
944
+ async getExpiringVtxos(thresholdMs) {
945
+ const message = {
946
+ tag: messageTag,
947
+ type: "GET_EXPIRING_VTXOS",
948
+ id: (0, utils_2.getRandomId)(),
949
+ payload: { thresholdMs },
950
+ };
951
+ try {
952
+ const response = await wallet.sendMessage(message);
953
+ return response.payload.vtxos;
954
+ }
955
+ catch (e) {
956
+ throw new Error(`Failed to get expiring vtxos: ${e}`);
957
+ }
958
+ },
959
+ async renewVtxos(eventCallback) {
960
+ const message = {
961
+ tag: messageTag,
962
+ type: "RENEW_VTXOS",
963
+ id: (0, utils_2.getRandomId)(),
964
+ };
965
+ try {
966
+ const response = await wallet.sendMessageWithEvents(message, (resp) => eventCallback?.(resp.payload), (resp) => resp.type === "RENEW_VTXOS_SUCCESS");
967
+ return response.payload.txid;
968
+ }
969
+ catch (e) {
970
+ throw new Error(`Failed to renew vtxos: ${e}`);
971
+ }
972
+ },
973
+ async getExpiredBoardingUtxos() {
974
+ const message = {
975
+ tag: messageTag,
976
+ type: "GET_EXPIRED_BOARDING_UTXOS",
977
+ id: (0, utils_2.getRandomId)(),
978
+ };
979
+ try {
980
+ const response = await wallet.sendMessage(message);
981
+ return response.payload
982
+ .utxos;
983
+ }
984
+ catch (e) {
985
+ throw new Error(`Failed to get expired boarding utxos: ${e}`);
986
+ }
987
+ },
988
+ async sweepExpiredBoardingUtxos() {
989
+ const message = {
990
+ tag: messageTag,
991
+ type: "SWEEP_EXPIRED_BOARDING_UTXOS",
992
+ id: (0, utils_2.getRandomId)(),
993
+ };
994
+ try {
995
+ const response = await wallet.sendMessage(message);
996
+ return response
997
+ .payload.txid;
998
+ }
999
+ catch (e) {
1000
+ throw new Error(`Failed to sweep expired boarding utxos: ${e}`);
1001
+ }
1002
+ },
1003
+ async dispose() {
1004
+ return;
1005
+ },
1006
+ };
1007
+ return manager;
1008
+ }
779
1009
  }
780
1010
  exports.ServiceWorkerWallet = ServiceWorkerWallet;