@bsv/wallet-toolbox-client 2.8.0 → 2.10.0

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.
@@ -4081,7 +4081,7 @@ var EntitySyncState = class EntitySyncState extends EntityBase {
4081
4081
  log += formatSyncSection("OUTPUTS", c.outputs, (r) => `${r.outputId} ${r.txid}.${r.vout} ${r.transactionId} ${r.spendable ? "spendable" : ""} sats:${r.satoshis}`);
4082
4082
  return log;
4083
4083
  }
4084
- async processSyncChunk(writer, args, chunk) {
4084
+ async processSyncChunk(writer, args, chunk, trx) {
4085
4085
  const mes = [
4086
4086
  new MergeEntity(chunk.provenTxs, EntityProvenTx.mergeFind, this.syncMap.provenTx),
4087
4087
  new MergeEntity(chunk.outputBaskets, EntityOutputBasket.mergeFind, this.syncMap.outputBasket),
@@ -4102,16 +4102,16 @@ var EntitySyncState = class EntitySyncState extends EntityBase {
4102
4102
  let done = true;
4103
4103
  if (chunk.user != null) {
4104
4104
  const ei = chunk.user;
4105
- const { found, eo } = await EntityUser.mergeFind(writer, this.userId, ei);
4105
+ const { found, eo } = await EntityUser.mergeFind(writer, this.userId, ei, trx);
4106
4106
  if (found) {
4107
- if (await eo.mergeExisting(writer, args.since, ei)) {
4107
+ if (await eo.mergeExisting(writer, args.since, ei, void 0, trx)) {
4108
4108
  maxUpdated_at = maxDate(maxUpdated_at, ei.updated_at);
4109
4109
  updates++;
4110
4110
  }
4111
4111
  }
4112
4112
  }
4113
4113
  for (const me of mes) {
4114
- const r = await me.merge(args.since, writer, this.userId, this.syncMap);
4114
+ const r = await me.merge(args.since, writer, this.userId, this.syncMap, trx);
4115
4115
  me.esm.count += me.stateArray?.length || 0;
4116
4116
  updates += r.updates;
4117
4117
  inserts += r.inserts;
@@ -4122,7 +4122,7 @@ var EntitySyncState = class EntitySyncState extends EntityBase {
4122
4122
  this.when = maxUpdated_at;
4123
4123
  for (const me of mes) me.esm.count = 0;
4124
4124
  }
4125
- await this.updateStorage(writer, false);
4125
+ await this.updateStorage(writer, false, trx);
4126
4126
  return {
4127
4127
  done,
4128
4128
  maxUpdated_at,
@@ -8254,28 +8254,70 @@ function verifyActionBatchManifestDigest(manifest) {
8254
8254
  //#endregion
8255
8255
  //#region ../src/utility/beefForTxids.ts
8256
8256
  /**
8257
- * Return the minimal subgraph needed to prove the requested transactions.
8257
+ * Return a minimal BEEF when the source contains data outside the requested
8258
+ * transaction dependency closure. Return undefined when no pruning is needed.
8258
8259
  *
8259
- * Parents are added before children so the resulting BEEF preserves dependency
8260
- * order. Shared ancestors and bumps are merged only once.
8260
+ * This form lets forwarding clients retain the caller's original bytes in the
8261
+ * common no-op case instead of rebuilding and reserializing an equivalent BEEF.
8262
+ */
8263
+ function pruneBeefForTxids(source, txids) {
8264
+ const selection = selectTransactions(source, txids);
8265
+ if (selection.transactions.size === source.txs.length && selection.bumpIndexes.size === source.bumps.length) return;
8266
+ return copySelection(source, selection);
8267
+ }
8268
+ /**
8269
+ * Return an independent minimal BEEF needed to prove the requested transactions.
8270
+ *
8271
+ * Transactions remain in source order and are sorted by Beef when serialized.
8272
+ * The source is indexed and walked once, using an explicit stack so a hostile
8273
+ * dependency depth cannot exhaust the JavaScript call stack.
8261
8274
  */
8262
8275
  function beefForTxids(source, txids) {
8263
- const beef = new _bsv_sdk.Beef();
8276
+ return copySelection(source, selectTransactions(source, txids));
8277
+ }
8278
+ function selectTransactions(source, txids) {
8279
+ const byTxid = /* @__PURE__ */ new Map();
8280
+ for (const tx of source.txs) byTxid.set(tx.txid, tx);
8281
+ const transactions = /* @__PURE__ */ new Set();
8282
+ const bumpIndexes = /* @__PURE__ */ new Set();
8264
8283
  const visited = /* @__PURE__ */ new Set();
8265
- const visit = (txid) => {
8266
- if (visited.has(txid)) return;
8284
+ const stack = [...txids];
8285
+ while (stack.length > 0) {
8286
+ const txid = stack.pop();
8287
+ if (txid == null || visited.has(txid)) continue;
8267
8288
  visited.add(txid);
8268
- const sourceTx = source.findTxid(txid);
8269
- if (sourceTx == null) return;
8270
- if (sourceTx.tx != null) {
8271
- for (const input of sourceTx.tx.inputs) if (input.sourceTXID != null) visit(input.sourceTXID);
8272
- }
8273
- if (sourceTx.bumpIndex != null) beef.mergeBump(source.bumps[sourceTx.bumpIndex]);
8274
- beef.mergeBeefTx(sourceTx);
8289
+ const tx = byTxid.get(txid);
8290
+ if (tx == null) continue;
8291
+ transactions.add(tx);
8292
+ const bumpIndex = tx.bumpIndex;
8293
+ if (bumpIndex != null && Number.isSafeInteger(bumpIndex) && bumpIndex >= 0 && bumpIndex < source.bumps.length) bumpIndexes.add(bumpIndex);
8294
+ for (const inputTxid of tx.inputTxids) if (!visited.has(inputTxid)) stack.push(inputTxid);
8295
+ }
8296
+ return {
8297
+ transactions,
8298
+ bumpIndexes
8275
8299
  };
8276
- for (const txid of txids) visit(txid);
8300
+ }
8301
+ function copySelection(source, selection) {
8302
+ const beef = new _bsv_sdk.Beef(source.version);
8303
+ const bumpIndexMap = /* @__PURE__ */ new Map();
8304
+ for (let index = 0; index < source.bumps.length; index++) {
8305
+ if (!selection.bumpIndexes.has(index)) continue;
8306
+ bumpIndexMap.set(index, beef.bumps.length);
8307
+ beef.bumps.push(cloneMerklePath(source.bumps[index]));
8308
+ }
8309
+ for (const sourceTx of source.txs) {
8310
+ if (!selection.transactions.has(sourceTx)) continue;
8311
+ const bumpIndex = sourceTx.bumpIndex == null ? void 0 : bumpIndexMap.get(sourceTx.bumpIndex);
8312
+ const rawTx = sourceTx.rawTxUint8Array;
8313
+ const copy = rawTx == null ? _bsv_sdk.BeefTx.fromTxid(sourceTx.txid, bumpIndex) : new _bsv_sdk.BeefTx(Uint8Array.from(rawTx), bumpIndex, Array.from(sourceTx.inputTxids));
8314
+ beef.txs.push(copy);
8315
+ }
8277
8316
  return beef;
8278
8317
  }
8318
+ function cloneMerklePath(source) {
8319
+ return new _bsv_sdk.MerklePath(source.blockHeight, source.path.map((level) => level.map((leaf) => ({ ...leaf }))), false, false);
8320
+ }
8279
8321
  //#endregion
8280
8322
  //#region ../src/storage/methods/offsetKey.ts
8281
8323
  function keyOffsetToHashedSecret(pub, keyOffset) {
@@ -10973,7 +11015,7 @@ function validateRequiredOutputs(storage, userId, vargs) {
10973
11015
  * @returns {xinputs} extended validated required inputs.
10974
11016
  */
10975
11017
  async function validateRequiredInputs(storage, userId, vargs) {
10976
- const beef = new _bsv_sdk.Beef();
11018
+ let beef = new _bsv_sdk.Beef();
10977
11019
  if (vargs.inputs.length === 0) return {
10978
11020
  storageBeef: beef,
10979
11021
  beef,
@@ -10999,6 +11041,7 @@ async function validateRequiredInputs(storage, userId, vargs) {
10999
11041
  inputsByTxid[input.outpoint.txid] ||= [];
11000
11042
  inputsByTxid[input.outpoint.txid].push(input);
11001
11043
  }
11044
+ beef = beefForTxids(beef, Object.keys(inputsByTxid));
11002
11045
  const localKnownInputTxids = {};
11003
11046
  for (const [txid, txInputs] of Object.entries(inputsByTxid)) localKnownInputTxids[txid] = txInputs.every((input) => {
11004
11047
  const output = preloadedOutputsByOutpoint[`${input.outpoint.txid}.${input.outpoint.vout}`];
@@ -15260,11 +15303,19 @@ var StorageProvider = class StorageProvider extends StorageReaderWriter {
15260
15303
  return await this.updateOutput(output.outputId, { basketId: void 0 });
15261
15304
  }
15262
15305
  async processSyncChunk(args, chunk) {
15263
- const user = verifyTruthy(await this.findUserByIdentityKey(args.identityKey));
15264
- return await new EntitySyncState(verifyOne(await this.findSyncStates({ partial: {
15265
- storageIdentityKey: args.fromStorageIdentityKey,
15266
- userId: user.userId
15267
- } }))).processSyncChunk(this, args, chunk);
15306
+ return await this.transaction(async (trx) => {
15307
+ const user = verifyTruthy(verifyOneOrNone(await this.findUsers({
15308
+ partial: { identityKey: args.identityKey },
15309
+ trx
15310
+ })));
15311
+ return await new EntitySyncState(verifyOne(await this.findSyncStates({
15312
+ partial: {
15313
+ storageIdentityKey: args.fromStorageIdentityKey,
15314
+ userId: user.userId
15315
+ },
15316
+ trx
15317
+ }))).processSyncChunk(this, args, chunk, trx);
15318
+ });
15268
15319
  }
15269
15320
  /**
15270
15321
  * Handles storage changes when a valid MerklePath and mined block header are found for a ProvenTxReq txid.
@@ -17329,8 +17380,10 @@ var StorageIdb = class extends StorageProvider {
17329
17380
  await tx.done;
17330
17381
  return r;
17331
17382
  } catch (err) {
17332
- tx.abort();
17333
- await tx.done;
17383
+ try {
17384
+ tx.abort();
17385
+ await tx.done;
17386
+ } catch {}
17334
17387
  throw err;
17335
17388
  }
17336
17389
  }
@@ -18297,6 +18350,25 @@ var StorageClientBase = class {
18297
18350
  * @returns `StorageCreateActionResults` supporting additional wallet processing to yield `createAction` results.
18298
18351
  */
18299
18352
  async createAction(auth, args) {
18353
+ if (args.inputBEEF != null) if (args.inputs.length === 0) args = {
18354
+ ...args,
18355
+ inputBEEF: void 0
18356
+ };
18357
+ else {
18358
+ let source;
18359
+ try {
18360
+ source = _bsv_sdk.Beef.fromBinary(args.inputBEEF);
18361
+ } catch {
18362
+ source = void 0;
18363
+ }
18364
+ if (source != null) {
18365
+ const pruned = pruneBeefForTxids(source, args.inputs.map((input) => input.outpoint.txid));
18366
+ if (pruned != null) args = {
18367
+ ...args,
18368
+ inputBEEF: pruned.toBinary()
18369
+ };
18370
+ }
18371
+ }
18300
18372
  return await this.rpcCall("createAction", [auth, args]);
18301
18373
  }
18302
18374
  /**
@@ -19597,6 +19669,24 @@ function isLiveBlockHeader(header) {
19597
19669
  return "chainWork" in header && typeof header.previousHash === "string";
19598
19670
  }
19599
19671
  //#endregion
19672
+ //#region ../src/services/chaintracker/chaintracks/Api/BulkFileDataValidatorApi.ts
19673
+ /**
19674
+ * Identifies deterministic rejection of the supplied immutable bytes.
19675
+ * Operational failures such as worker crashes and queue saturation deliberately
19676
+ * use ordinary errors so callers preserve the cache entry and avoid downloading
19677
+ * a replacement that cannot be validated.
19678
+ *
19679
+ * @public
19680
+ */
19681
+ var BulkFileDataValidationError = class extends Error {
19682
+ data;
19683
+ constructor(message, data) {
19684
+ super(message);
19685
+ this.data = data;
19686
+ this.name = "BulkFileDataValidationError";
19687
+ }
19688
+ };
19689
+ //#endregion
19600
19690
  //#region ../src/services/chaintracker/chaintracks/util/HeightRange.ts
19601
19691
  /**
19602
19692
  * Represents a range of block heights.
@@ -19815,6 +19905,8 @@ var Chaintracks = class {
19815
19905
  lastPresentHeight = -1;
19816
19906
  lastPresentHeightMsecs = 0;
19817
19907
  lastPresentHeightMaxAge = 60 * 1e3;
19908
+ presentHeightRefresh;
19909
+ mainLoopHeartbeatMsecs = 0;
19818
19910
  lock = new SingleWriterMultiReaderLock();
19819
19911
  sourceStatus = /* @__PURE__ */ new Map();
19820
19912
  constructor(options) {
@@ -19852,12 +19944,31 @@ var Chaintracks = class {
19852
19944
  return this.chain;
19853
19945
  }
19854
19946
  /**
19855
- * Caches and returns most recently sourced value if less than one minute old.
19856
- * @returns the current externally available chain height (via bulk ingestors).
19947
+ * Returns the last known valid height immediately and refreshes stale state
19948
+ * once in the background. Cold start waits for the single shared refresh.
19857
19949
  */
19858
19950
  async getPresentHeight() {
19859
19951
  const now = Date.now();
19860
19952
  if (this.lastPresentHeight >= 0 && now - this.lastPresentHeightMsecs < this.lastPresentHeightMaxAge) return this.lastPresentHeight;
19953
+ if (this.lastPresentHeight >= 0) {
19954
+ this.refreshPresentHeight().catch((error) => {
19955
+ this.log(`Background present-height refresh failed: ${WalletError.fromUnknown(error).message}`);
19956
+ });
19957
+ return this.lastPresentHeight;
19958
+ }
19959
+ return await this.refreshPresentHeight();
19960
+ }
19961
+ async refreshPresentHeight() {
19962
+ if (this.presentHeightRefresh != null) return await this.presentHeightRefresh;
19963
+ const refresh = this.loadPresentHeight();
19964
+ this.presentHeightRefresh = refresh;
19965
+ try {
19966
+ return await refresh;
19967
+ } finally {
19968
+ if (this.presentHeightRefresh === refresh) this.presentHeightRefresh = void 0;
19969
+ }
19970
+ }
19971
+ async loadPresentHeight() {
19861
19972
  for (const [index, bulk] of this.bulkIngestors.entries()) {
19862
19973
  const source = this.sourceName("bulk", index, bulk);
19863
19974
  try {
@@ -19865,7 +19976,7 @@ var Chaintracks = class {
19865
19976
  if (presentHeight != null && Number.isInteger(presentHeight) && presentHeight >= 0) {
19866
19977
  this.markSourceSuccess(source, "bulk");
19867
19978
  this.lastPresentHeight = presentHeight;
19868
- this.lastPresentHeightMsecs = now;
19979
+ this.lastPresentHeightMsecs = Date.now();
19869
19980
  return presentHeight;
19870
19981
  }
19871
19982
  } catch (uerr) {
@@ -19880,7 +19991,7 @@ var Chaintracks = class {
19880
19991
  const localHeight = Math.max(ranges.bulk.maxHeight, ranges.live.maxHeight);
19881
19992
  if (localHeight >= 0) {
19882
19993
  this.lastPresentHeight = localHeight;
19883
- this.lastPresentHeightMsecs = now;
19994
+ this.lastPresentHeightMsecs = Date.now();
19884
19995
  return localHeight;
19885
19996
  }
19886
19997
  } catch (error) {
@@ -19891,6 +20002,19 @@ var Chaintracks = class {
19891
20002
  async currentHeight() {
19892
20003
  return await this.getPresentHeight();
19893
20004
  }
20005
+ /** Returns local process state without locks, storage reads, or network I/O. */
20006
+ getAvailabilitySnapshot() {
20007
+ return {
20008
+ available: this.available,
20009
+ startupError: this.startupError?.message,
20010
+ presentHeight: this.lastPresentHeight >= 0 ? this.lastPresentHeight : void 0,
20011
+ presentHeightUpdatedAt: this.lastPresentHeightMsecs > 0 ? new Date(this.lastPresentHeightMsecs).toISOString() : void 0,
20012
+ presentHeightRefreshInFlight: this.presentHeightRefresh != null,
20013
+ mainLoopHeartbeatAt: this.mainLoopHeartbeatMsecs > 0 ? new Date(this.mainLoopHeartbeatMsecs).toISOString() : void 0,
20014
+ sources: Array.from(this.sourceStatus.values()).map((status) => ({ ...status })),
20015
+ bulkData: this.storage.bulkManager.getStats()
20016
+ };
20017
+ }
19894
20018
  async subscribeHeaders(listener) {
19895
20019
  const ID = randomBytesBase64(8);
19896
20020
  this.callbacks.header[ID] = listener;
@@ -19955,6 +20079,7 @@ var Chaintracks = class {
19955
20079
  for (const liveIn of this.liveIngestors) await liveIn.shutdown();
19956
20080
  for (const bulkIn of this.bulkIngestors) await bulkIn.shutdown();
19957
20081
  await Promise.all(this.promises);
20082
+ await this.storage.bulkManager.destroy();
19958
20083
  await this.storage.destroy();
19959
20084
  this.available = false;
19960
20085
  this.stopMainThread = false;
@@ -20236,9 +20361,11 @@ var Chaintracks = class {
20236
20361
  const syncCheckRepeatMsecs = 1800 * 1e3;
20237
20362
  while (!this.stopMainThread) try {
20238
20363
  const now = Date.now();
20364
+ this.mainLoopHeartbeatMsecs = now;
20239
20365
  lastSyncCheck = now;
20240
20366
  lastBulkSync = await this.runBulkSyncIfNeeded(now, lastBulkSync, cdnSyncRepeatMsecs);
20241
20367
  await this.processLiveHeaderQueue(lastSyncCheck, syncCheckRepeatMsecs);
20368
+ this.mainLoopHeartbeatMsecs = Date.now();
20242
20369
  } catch (error_) {
20243
20370
  const e = WalletError.fromUnknown(error_);
20244
20371
  if (this.available) this.log(`Error occurred during chaintracks main thread processing: ${e.stack || e.message}`);
@@ -20250,7 +20377,7 @@ var Chaintracks = class {
20250
20377
  }
20251
20378
  /** Returns (potentially updated) lastBulkSync timestamp. */
20252
20379
  async runBulkSyncIfNeeded(now, lastBulkSync, cdnSyncRepeatMsecs) {
20253
- const presentHeight = await this.getPresentHeight();
20380
+ const presentHeight = await this.refreshPresentHeight();
20254
20381
  const before = await this.storage.getAvailableHeightRanges();
20255
20382
  let skipBulkSync = !before.live.isEmpty && before.live.maxHeight >= presentHeight - this.addLiveRecursionLimit / 2;
20256
20383
  if (skipBulkSync && now - lastBulkSync > cdnSyncRepeatMsecs) skipBulkSync = false;
@@ -21399,12 +21526,12 @@ var ChaintracksFetch = class {
21399
21526
  this.maxRetryMsecs = positiveSafeInteger(options.maxRetryMsecs, DEFAULT_MAX_RETRY_MSECS, "maxRetryMsecs");
21400
21527
  this.random = options.random ?? Math.random;
21401
21528
  }
21402
- async download(url, maxResponseBytes) {
21529
+ async download(url, maxResponseBytes, options) {
21403
21530
  const responseLimit = maxResponseBytes == null ? this.maxResponseBytes : Math.min(this.maxResponseBytes, positiveSafeInteger(maxResponseBytes, this.maxResponseBytes, "maxResponseBytes"));
21404
21531
  return await this.requestBytes(url, {
21405
21532
  method: "GET",
21406
21533
  headers: { Accept: "application/octet-stream" }
21407
- }, "download", responseLimit);
21534
+ }, "download", responseLimit, options);
21408
21535
  }
21409
21536
  async fetchJson(url) {
21410
21537
  const bytes = await this.requestBytes(url, {
@@ -21413,8 +21540,9 @@ var ChaintracksFetch = class {
21413
21540
  }, "fetch JSON", this.maxResponseBytes);
21414
21541
  return JSON.parse(new TextDecoder().decode(bytes));
21415
21542
  }
21416
- async requestBytes(url, init, kind, maxResponseBytes) {
21543
+ async requestBytes(url, init, kind, maxResponseBytes, downloadOptions) {
21417
21544
  for (let retry = 0;; retry++) {
21545
+ if (retry > 0) await downloadOptions?.beforeRetry?.(retry + 1);
21418
21546
  const controller = new AbortController();
21419
21547
  const timeout = setTimeout(() => controller.abort(), this.timeoutMsecs);
21420
21548
  try {
@@ -21490,6 +21618,38 @@ var ChaintracksFetch = class {
21490
21618
  }
21491
21619
  };
21492
21620
  //#endregion
21621
+ //#region ../src/services/chaintracker/chaintracks/util/InlineBulkFileDataValidator.ts
21622
+ /**
21623
+ * Portable complete-object validator. Node services should normally inject
21624
+ * `NodeBulkFileDataValidator`; browser and mobile consumers retain this
21625
+ * dependency-free fallback.
21626
+ *
21627
+ * @public
21628
+ */
21629
+ var InlineBulkFileDataValidator = class {
21630
+ async validate(request) {
21631
+ try {
21632
+ const expectedLength = request.count * 80;
21633
+ if (request.data.length !== expectedLength) throw new WERR_INVALID_PARAMETER("file.data", `bulk file ${request.fileName} data length ${request.data.length} does not match expected count ${request.count}`);
21634
+ const fileHash = asString(_bsv_sdk.Hash.sha256(asArray(request.data)), "base64");
21635
+ if (request.fileHash != null && fileHash !== request.fileHash) throw new WERR_INVALID_PARAMETER("fileHash", `a match for retrieved data for ${request.fileName}`);
21636
+ const { lastHeaderHash, lastChainWork } = validateBufferOfHeaders(request.data, request.prevHash, 0, request.count, request.prevChainWork);
21637
+ if (request.lastHash && request.lastHash !== lastHeaderHash) throw new WERR_INVALID_PARAMETER("file.lastHash", `expected ${request.lastHash} but got ${lastHeaderHash}`);
21638
+ if (request.lastChainWork && request.lastChainWork !== lastChainWork) throw new WERR_INVALID_PARAMETER("file.lastChainWork", `expected ${request.lastChainWork} but got ${lastChainWork}`);
21639
+ if (request.firstHeight === 0 && request.chain != null) validateGenesisHeader(request.data, request.chain);
21640
+ return {
21641
+ data: request.data,
21642
+ fileHash,
21643
+ lastHeaderHash,
21644
+ lastChainWork
21645
+ };
21646
+ } catch (error) {
21647
+ if (error instanceof BulkFileDataValidationError) throw error;
21648
+ throw new BulkFileDataValidationError(error instanceof Error ? error.message : String(error), request.data);
21649
+ }
21650
+ }
21651
+ };
21652
+ //#endregion
21493
21653
  //#region ../src/services/chaintracker/chaintracks/util/BulkFileDataManager.ts
21494
21654
  /**
21495
21655
  * Manages bulk file data (typically 8MB chunks of 100,000 headers each).
@@ -21515,6 +21675,7 @@ var BulkFileDataManager = class BulkFileDataManager {
21515
21675
  fileHashToIndex = {};
21516
21676
  lock = new SingleWriterMultiReaderLock();
21517
21677
  inFlightLoads = /* @__PURE__ */ new Map();
21678
+ failedLoads = /* @__PURE__ */ new Map();
21518
21679
  storage;
21519
21680
  stats = {
21520
21681
  memoryHits: 0,
@@ -21524,7 +21685,8 @@ var BulkFileDataManager = class BulkFileDataManager {
21524
21685
  persistentCacheRejects: 0,
21525
21686
  coalescedLoads: 0,
21526
21687
  downloads: 0,
21527
- downloadedBytes: 0
21688
+ downloadedBytes: 0,
21689
+ loadBackoffs: 0
21528
21690
  };
21529
21691
  chain;
21530
21692
  maxPerFile;
@@ -21533,6 +21695,8 @@ var BulkFileDataManager = class BulkFileDataManager {
21533
21695
  fromKnownSourceUrl;
21534
21696
  cache;
21535
21697
  downloadBudget;
21698
+ validator;
21699
+ failedLoadRetryMsecs;
21536
21700
  constructor(options) {
21537
21701
  const resolvedOptions = typeof options === "object" ? options : BulkFileDataManager.createDefaultOptions(options);
21538
21702
  this.chain = resolvedOptions.chain;
@@ -21542,10 +21706,17 @@ var BulkFileDataManager = class BulkFileDataManager {
21542
21706
  this.fetch = resolvedOptions.fetch;
21543
21707
  this.cache = resolvedOptions.cache;
21544
21708
  this.downloadBudget = resolvedOptions.downloadBudget;
21709
+ this.validator = resolvedOptions.validator ?? new InlineBulkFileDataValidator();
21710
+ this.failedLoadRetryMsecs = resolvedOptions.failedLoadRetryMsecs ?? 30 * 1e3;
21711
+ if (!Number.isSafeInteger(this.failedLoadRetryMsecs) || this.failedLoadRetryMsecs < 0) throw new WERR_INVALID_PARAMETER("failedLoadRetryMsecs", "a non-negative safe integer");
21545
21712
  this.deleteBulkFilesNoLock();
21546
21713
  }
21547
21714
  getStats() {
21548
- return { ...this.stats };
21715
+ return {
21716
+ ...this.stats,
21717
+ validation: this.validator.getStats?.(),
21718
+ downloadBudget: this.downloadBudget?.snapshot?.()
21719
+ };
21549
21720
  }
21550
21721
  async deleteBulkFiles() {
21551
21722
  return await this.lock.withWriteLock(async () => this.deleteBulkFilesNoLock());
@@ -21764,9 +21935,29 @@ var BulkFileDataManager = class BulkFileDataManager {
21764
21935
  });
21765
21936
  }
21766
21937
  async getDataFromFile(file, offset, length) {
21767
- const bfd = this.getBfdForHeight(file.firstHeight);
21768
- if (bfd == null || bfd.count < file.count) throw new WERR_INVALID_PARAMETER("file", `a match for ${file.firstHeight}, ${file.count} in the BulkFileDataManager.`);
21769
- return await this.lock.withReadLock(async () => await this.getDataFromFileNoLock(bfd, offset, length));
21938
+ const resolved = await this.lock.withReadLock(async () => {
21939
+ const resolved = this.getBfdForHeight(file.firstHeight);
21940
+ if (resolved == null || resolved.count < file.count) throw new WERR_INVALID_PARAMETER("file", `a match for ${file.firstHeight}, ${file.count} in the BulkFileDataManager.`);
21941
+ return {
21942
+ current: resolved,
21943
+ snapshot: snapshotBfd(resolved)
21944
+ };
21945
+ });
21946
+ return await this.getDataFromSnapshot(resolved.current, resolved.snapshot, offset, length);
21947
+ }
21948
+ async getDataFromSnapshot(original, snapshot, offset, length) {
21949
+ const data = await this.getDataFromFileNoLock(snapshot, offset, length);
21950
+ if (snapshot.data != null) await this.lock.withWriteLock(async () => {
21951
+ if (this.bfds.includes(original) && original.fileHash === snapshot.fileHash && original.firstHeight === snapshot.firstHeight && original.count === snapshot.count) {
21952
+ original.data = snapshot.data;
21953
+ original.validated = true;
21954
+ original.lastHash = snapshot.lastHash;
21955
+ original.lastChainWork = snapshot.lastChainWork;
21956
+ original.mru = Date.now();
21957
+ this.ensureMaxRetained();
21958
+ }
21959
+ });
21960
+ return data;
21770
21961
  }
21771
21962
  async getDataFromFileNoLock(bfd, offset, length) {
21772
21963
  const fileLength = bfd.count * 80;
@@ -21777,15 +21968,19 @@ var BulkFileDataManager = class BulkFileDataManager {
21777
21968
  return (await this.ensureData(bfd)).slice(offset, offset + length);
21778
21969
  }
21779
21970
  async findHeaderForHeightOrUndefined(height) {
21780
- return await this.lock.withReadLock(async () => {
21971
+ const resolved = await this.lock.withReadLock(async () => {
21781
21972
  if (!Number.isInteger(height) || height < 0) throw new WERR_INVALID_PARAMETER("height", `a non-negative integer (${height}).`);
21782
21973
  const file = this.bfds.find((f) => f.firstHeight <= height && f.firstHeight + f.count > height);
21783
21974
  if (file == null) return void 0;
21784
- const offset = (height - file.firstHeight) * 80;
21785
- const data = await this.getDataFromFileNoLock(file, offset, 80);
21786
- if (data == null) return void 0;
21787
- return deserializeBlockHeader(data, height, 0);
21975
+ return {
21976
+ current: file,
21977
+ snapshot: snapshotBfd(file),
21978
+ offset: (height - file.firstHeight) * 80
21979
+ };
21788
21980
  });
21981
+ if (resolved == null) return void 0;
21982
+ const data = await this.getDataFromSnapshot(resolved.current, resolved.snapshot, resolved.offset, 80);
21983
+ return data == null ? void 0 : deserializeBlockHeader(data, height, 0);
21789
21984
  }
21790
21985
  async getFileForHeight(height) {
21791
21986
  return await this.lock.withReadLock(async () => {
@@ -21840,22 +22035,30 @@ var BulkFileDataManager = class BulkFileDataManager {
21840
22035
  return bfd;
21841
22036
  }
21842
22037
  async validateBfdData(bfd, expectedFileHash) {
21843
- await this.ensureData(bfd);
21844
- if (bfd.data?.length !== bfd.count * 80) throw new WERR_INVALID_PARAMETER("file.data", `bulk file ${bfd.fileName} data length ${bfd.data?.length} does not match expected count ${bfd.count}`);
21845
- bfd.fileHash = asString(_bsv_sdk.Hash.sha256(asArray(bfd.data)), "base64");
21846
- if (expectedFileHash && expectedFileHash !== bfd.fileHash) throw new WERR_INVALID_PARAMETER("file.fileHash", `expected ${expectedFileHash} but got ${bfd.fileHash}`);
21847
- this.validateBfdHeaders(bfd);
22038
+ const data = await this.ensureData(bfd);
22039
+ bfd.data = await this.validateRetrievedData(bfd, data, expectedFileHash);
21848
22040
  }
21849
- validateBfdHeaders(bfd) {
22041
+ async validateBfdHeaders(bfd, expectedFileHash = bfd.fileHash) {
21850
22042
  const pbf = bfd.firstHeight > 0 ? this.getBfdForHeight(bfd.firstHeight - 1) : void 0;
21851
22043
  const prevHash = pbf?.lastHash ?? "00".repeat(32);
21852
22044
  const prevChainWork = pbf?.lastChainWork ?? "00".repeat(32);
21853
- const { lastHeaderHash, lastChainWork } = validateBufferOfHeaders(bfd.data, prevHash, 0, void 0, prevChainWork);
21854
- if (bfd.lastHash && bfd.lastHash !== lastHeaderHash) throw new WERR_INVALID_PARAMETER("file.lastHash", `expected ${bfd.lastHash} but got ${lastHeaderHash}`);
21855
- if (bfd.lastChainWork && bfd.lastChainWork !== lastChainWork) throw new WERR_INVALID_PARAMETER("file.lastChainWork", `expected ${bfd.lastChainWork} but got ${lastChainWork}`);
21856
- bfd.lastHash = lastHeaderHash;
21857
- bfd.lastChainWork = lastChainWork;
21858
- if (bfd.firstHeight === 0) validateGenesisHeader(bfd.data, bfd.chain);
22045
+ const result = await this.validator.validate({
22046
+ fileName: bfd.fileName,
22047
+ data: bfd.data,
22048
+ count: bfd.count,
22049
+ fileHash: expectedFileHash,
22050
+ firstHeight: bfd.firstHeight,
22051
+ prevHash,
22052
+ prevChainWork,
22053
+ lastHash: bfd.lastHash,
22054
+ lastChainWork: bfd.lastChainWork,
22055
+ chain: bfd.chain
22056
+ });
22057
+ bfd.data = result.data;
22058
+ bfd.fileHash = result.fileHash;
22059
+ bfd.lastHash = result.lastHeaderHash;
22060
+ bfd.lastChainWork = result.lastChainWork;
22061
+ return result.data;
21859
22062
  }
21860
22063
  async ReValidate() {
21861
22064
  return await this.lock.withReadLock(async () => await this.ReValidateNoLock());
@@ -22046,62 +22249,96 @@ var BulkFileDataManager = class BulkFileDataManager {
22046
22249
  this.ensureMaxRetained();
22047
22250
  return data;
22048
22251
  }
22252
+ const failed = this.failedLoads.get(key);
22253
+ if (failed != null) {
22254
+ if (Date.now() < failed.retryAt) {
22255
+ this.stats.loadBackoffs++;
22256
+ throw failed.error;
22257
+ }
22258
+ this.failedLoads.delete(key);
22259
+ }
22049
22260
  const load = this.loadAndValidateData(bfd);
22050
22261
  this.inFlightLoads.set(key, load);
22051
22262
  try {
22052
22263
  const data = await load;
22053
22264
  bfd.data = data;
22054
22265
  bfd.validated = true;
22266
+ this.failedLoads.delete(key);
22055
22267
  bfd.mru = Date.now();
22056
22268
  this.ensureMaxRetained();
22057
22269
  return data;
22270
+ } catch (error) {
22271
+ const resolved = error instanceof Error ? error : new Error(String(error));
22272
+ this.failedLoads.set(key, {
22273
+ retryAt: Date.now() + this.failedLoadRetryMsecs,
22274
+ error: resolved
22275
+ });
22276
+ throw resolved;
22058
22277
  } finally {
22059
22278
  if (this.inFlightLoads.get(key) === load) this.inFlightLoads.delete(key);
22060
22279
  }
22061
22280
  }
22062
22281
  async loadAndValidateData(bfd) {
22063
- if (this.storage != null && bfd.fileId) {
22064
- const stored = await this.storage.getBulkFileData(bfd.fileId);
22065
- if (stored == null) throw new WERR_INVALID_PARAMETER("fileId", `valid, data not found for fileId ${bfd.fileId}`);
22066
- this.validateRetrievedData(bfd, stored);
22067
- this.stats.storageHits++;
22068
- return stored;
22069
- }
22070
- if (this.cache != null) {
22071
- const cached = await this.cache.get(bfd);
22072
- if (cached != null) try {
22073
- this.validateRetrievedData(bfd, cached);
22074
- this.stats.persistentCacheHits++;
22075
- return cached;
22076
- } catch (error) {
22077
- this.stats.persistentCacheRejects++;
22078
- await this.cache.delete?.(bfd);
22079
- this.log(`Rejected corrupt bulk-header cache entry ${bfd.fileName}: ${String(error)}`);
22080
- }
22081
- else this.stats.persistentCacheMisses++;
22082
- }
22083
- if (this.fetch != null && bfd.sourceUrl) {
22084
- const expectedBytes = bfd.count * 80;
22085
- await this.downloadBudget?.consume(expectedBytes);
22086
- const url = this.fetch.pathJoin(bfd.sourceUrl, bfd.fileName);
22087
- const downloaded = await this.fetch.download(url, expectedBytes);
22088
- if (downloaded == null) throw new WERR_INVALID_PARAMETER("sourceUrl", `data not found for sourceUrl ${url}`);
22089
- this.validateRetrievedData(bfd, downloaded);
22090
- this.stats.downloads++;
22091
- this.stats.downloadedBytes += downloaded.length;
22092
- await this.cache?.set(bfd, downloaded);
22093
- return downloaded;
22094
- }
22282
+ const stored = await this.loadFromStorage(bfd);
22283
+ if (stored != null) return stored;
22284
+ const cached = await this.loadFromCache(bfd);
22285
+ if (cached != null) return cached;
22286
+ const downloaded = await this.loadFromRemote(bfd);
22287
+ if (downloaded != null) return downloaded;
22095
22288
  throw new WERR_INVALID_PARAMETER("data", `defined. Unable to retrieve data for ${bfd.fileName}`);
22096
22289
  }
22097
- validateRetrievedData(bfd, data) {
22098
- if (data.length !== bfd.count * 80) throw new WERR_INVALID_PARAMETER("file.data", `bulk file ${bfd.fileName} data length ${data.length} does not match expected count ${bfd.count}`);
22099
- if (asString(_bsv_sdk.Hash.sha256(asArray(data)), "base64") !== bfd.fileHash) throw new WERR_INVALID_PARAMETER("fileHash", `a match for retrieved data for ${bfd.fileName}`);
22290
+ async loadFromStorage(bfd) {
22291
+ if (this.storage == null || !bfd.fileId) return void 0;
22292
+ const stored = await this.storage.getBulkFileData(bfd.fileId);
22293
+ if (stored == null) throw new WERR_INVALID_PARAMETER("fileId", `valid, data not found for fileId ${bfd.fileId}`);
22294
+ const validated = await this.validateRetrievedData(bfd, stored);
22295
+ this.stats.storageHits++;
22296
+ return validated;
22297
+ }
22298
+ async loadFromCache(bfd) {
22299
+ if (this.cache == null) return void 0;
22300
+ const cached = await this.cache.get(bfd);
22301
+ if (cached == null) {
22302
+ this.stats.persistentCacheMisses++;
22303
+ return;
22304
+ }
22305
+ try {
22306
+ const validated = await this.validateRetrievedData(bfd, cached);
22307
+ this.stats.persistentCacheHits++;
22308
+ await this.cache.promoteValidated?.(bfd, validated);
22309
+ return validated;
22310
+ } catch (error) {
22311
+ if (!(error instanceof BulkFileDataValidationError)) throw error;
22312
+ this.stats.persistentCacheRejects++;
22313
+ let rejectedData = error.data;
22314
+ if (!(rejectedData instanceof Uint8Array) && cached.byteLength > 0) rejectedData = cached;
22315
+ await this.cache.quarantine?.(bfd, String(error), rejectedData);
22316
+ this.log(`Rejected corrupt bulk-header cache entry ${bfd.fileName}: ${String(error)}`);
22317
+ return;
22318
+ }
22319
+ }
22320
+ async loadFromRemote(bfd) {
22321
+ if (this.fetch == null || !bfd.sourceUrl) return void 0;
22322
+ const expectedBytes = bfd.count * 80;
22323
+ await this.downloadBudget?.consume(expectedBytes);
22324
+ const url = this.fetch.pathJoin(bfd.sourceUrl, bfd.fileName);
22325
+ const downloaded = await this.fetch.download(url, expectedBytes, { beforeRetry: async () => await this.downloadBudget?.consume(expectedBytes) });
22326
+ if (downloaded == null) throw new WERR_INVALID_PARAMETER("sourceUrl", `data not found for sourceUrl ${url}`);
22327
+ const validated = await this.validateRetrievedData(bfd, downloaded);
22328
+ this.stats.downloads++;
22329
+ this.stats.downloadedBytes += validated.length;
22330
+ await this.cache?.set(bfd, validated);
22331
+ return validated;
22332
+ }
22333
+ async validateRetrievedData(bfd, data, expectedFileHash = bfd.fileHash) {
22100
22334
  const candidate = {
22101
22335
  ...bfd,
22102
22336
  data
22103
22337
  };
22104
- this.validateBfdHeaders(candidate);
22338
+ const validated = await this.validateBfdHeaders(candidate, expectedFileHash);
22339
+ bfd.lastHash = candidate.lastHash;
22340
+ bfd.lastChainWork = candidate.lastChainWork;
22341
+ return validated;
22105
22342
  }
22106
22343
  ensureMaxRetained() {
22107
22344
  if (this.maxRetained === void 0) return;
@@ -22137,17 +22374,24 @@ var BulkFileDataManager = class BulkFileDataManager {
22137
22374
  i++;
22138
22375
  const data = await reader.read();
22139
22376
  if (data == null || data.length === 0) break;
22140
- const last = validateBufferOfHeaders(data, lastHeaderHash, 0, void 0, lastChainWork);
22141
- await toFs.writeFile(toPath(i), data);
22142
- const fileHash = asString(_bsv_sdk.Hash.sha256(asArray(data)), "base64");
22377
+ const validated = await this.validator.validate({
22378
+ fileName: toFileName(i),
22379
+ data,
22380
+ count: data.length / 80,
22381
+ firstHeight,
22382
+ prevHash: lastHeaderHash,
22383
+ prevChainWork: lastChainWork,
22384
+ chain
22385
+ });
22386
+ await toFs.writeFile(toPath(i), validated.data);
22143
22387
  const file = {
22144
22388
  chain,
22145
- count: data.length / 80,
22146
- fileHash,
22389
+ count: validated.data.length / 80,
22390
+ fileHash: validated.fileHash,
22147
22391
  fileName: toFileName(i),
22148
22392
  firstHeight,
22149
- lastChainWork: last.lastChainWork,
22150
- lastHash: last.lastHeaderHash,
22393
+ lastChainWork: validated.lastChainWork,
22394
+ lastHash: validated.lastHeaderHash,
22151
22395
  prevChainWork: lastChainWork,
22152
22396
  prevHash: lastHeaderHash,
22153
22397
  sourceUrl
@@ -22159,7 +22403,16 @@ var BulkFileDataManager = class BulkFileDataManager {
22159
22403
  }
22160
22404
  await toFs.writeFile(toJsonPath(), asUint8Array(JSON.stringify(toBulkFiles), "utf8"));
22161
22405
  }
22406
+ async destroy() {
22407
+ await this.validator.destroy?.();
22408
+ }
22162
22409
  };
22410
+ function snapshotBfd(file) {
22411
+ return {
22412
+ ...file,
22413
+ data: file.data
22414
+ };
22415
+ }
22163
22416
  function selectBulkHeaderFiles(files, chain, maxPerFile) {
22164
22417
  const r = [];
22165
22418
  let height = 0;
@@ -27028,7 +27281,8 @@ function createDefaultBulkFileDataManager(params) {
27028
27281
  maxRetained: params.maxRetained,
27029
27282
  fromKnownSourceUrl: params.cdnUrl,
27030
27283
  cache: params.sources.bulkFileCache,
27031
- downloadBudget: params.sources.bulkFileDownloadBudget
27284
+ downloadBudget: params.sources.bulkFileDownloadBudget,
27285
+ validator: params.sources.bulkFileDataValidator
27032
27286
  });
27033
27287
  }
27034
27288
  function createDefaultChaintracksStorageOptions(params) {
@@ -27553,6 +27807,7 @@ var FixedWindowBulkFileDownloadBudget = class {
27553
27807
  return {
27554
27808
  maxBytes: this.maxBytes,
27555
27809
  consumedBytes: this.consumedBytes,
27810
+ remainingBytes: this.maxBytes - this.consumedBytes,
27556
27811
  windowStartedAt: this.windowStartedAt,
27557
27812
  windowMsecs: this.windowMsecs
27558
27813
  };
@@ -30784,6 +31039,7 @@ var SetupClient = class SetupClient {
30784
31039
  };
30785
31040
  //#endregion
30786
31041
  //#region ../src/CWIStyleWalletManager.ts
31042
+ const CWI_COMPONENT = "wallet-toolbox.cwi-manager";
30787
31043
  /**
30788
31044
  * Number of rounds used in PBKDF2 for deriving password keys.
30789
31045
  */
@@ -31016,11 +31272,11 @@ var OverlayUMPTokenInteractor = class {
31016
31272
  * @param hash The 32-byte SHA-256 hash of the presentation key.
31017
31273
  * @returns A UMPToken object (including currentOutpoint) if found, otherwise undefined.
31018
31274
  */
31019
- async findByPresentationKeyHash(hash) {
31020
- return await this.findToken({
31275
+ async findByPresentationKeyHash(hash, options) {
31276
+ return this.findToken({
31021
31277
  service: "ls_users",
31022
31278
  query: { presentationHash: _bsv_sdk.Utils.toHex(hash) }
31023
- }, "presentation");
31279
+ }, "presentation", options);
31024
31280
  }
31025
31281
  /**
31026
31282
  * Finds a UMP token on-chain by the given recovery key hash, if it exists.
@@ -31029,13 +31285,13 @@ var OverlayUMPTokenInteractor = class {
31029
31285
  * @param hash The 32-byte SHA-256 hash of the recovery key.
31030
31286
  * @returns A UMPToken object (including currentOutpoint) if found, otherwise undefined.
31031
31287
  */
31032
- async findByRecoveryKeyHash(hash) {
31033
- return await this.findToken({
31288
+ async findByRecoveryKeyHash(hash, options) {
31289
+ return this.findToken({
31034
31290
  service: "ls_users",
31035
31291
  query: { recoveryHash: _bsv_sdk.Utils.toHex(hash) }
31036
- }, "recovery");
31292
+ }, "recovery", options);
31037
31293
  }
31038
- async findToken(question, lookupKind) {
31294
+ async findToken(question, lookupKind, options) {
31039
31295
  const correlationId = this.telemetry.enabled === true ? this.telemetry.createCorrelationId() : void 0;
31040
31296
  const startedAt = Date.now();
31041
31297
  this.telemetry.capture({
@@ -31052,34 +31308,39 @@ var OverlayUMPTokenInteractor = class {
31052
31308
  correlationId
31053
31309
  });
31054
31310
  } catch (error) {
31055
- const diagnostics = this.emptyLookupDiagnostics(correlationId);
31056
- this.captureLookupFailure(lookupKind, "lookup-unavailable", diagnostics, startedAt, error);
31311
+ const diagnostics = this.emptyStats(correlationId);
31312
+ this.lookupFailed(lookupKind, "lookup-unavailable", diagnostics, startedAt, error);
31057
31313
  throw new UMPTokenLookupError("lookup-unavailable", diagnostics, { cause: error });
31058
31314
  }
31059
- const diagnostics = this.toLookupDiagnostics(resolution);
31315
+ const diagnostics = this.diagnosticsFor(resolution);
31060
31316
  const tokens = this.parseLookupAnswers(resolution.answer);
31061
31317
  const expectedHash = question.query[lookupKind === "presentation" ? "presentationHash" : "recoveryHash"].toLowerCase();
31062
31318
  const matchingTokens = tokens.filter((token) => _bsv_sdk.Utils.toHex(lookupKind === "presentation" ? token.presentationHash : token.recoveryHash).toLowerCase() === expectedHash);
31063
31319
  if (matchingTokens.length > 1) {
31064
31320
  const newest = this.resolveNewestToken(matchingTokens, resolution.answer.outputs);
31065
31321
  if (newest != null) {
31066
- this.captureLookupCompleted(lookupKind, "found", diagnostics, startedAt, { supersededTokens: matchingTokens.length - 1 });
31322
+ this.lookupDone(lookupKind, "found", diagnostics, startedAt, { supersededTokens: matchingTokens.length - 1 });
31067
31323
  return newest;
31068
31324
  }
31325
+ const pinned = options?.pinnedOutpoint ? matchingTokens.find((token) => token.currentOutpoint === options.pinnedOutpoint) : void 0;
31326
+ if (pinned != null) {
31327
+ this.lookupDone(lookupKind, "found", diagnostics, startedAt);
31328
+ return pinned;
31329
+ }
31069
31330
  const reason = "token-ambiguous";
31070
- this.captureLookupFailure(lookupKind, reason, diagnostics, startedAt);
31331
+ this.lookupFailed(lookupKind, reason, diagnostics, startedAt);
31071
31332
  throw new UMPTokenLookupError(reason, diagnostics);
31072
31333
  }
31073
31334
  if (matchingTokens.length === 1) {
31074
- this.captureLookupCompleted(lookupKind, "found", diagnostics, startedAt);
31335
+ this.lookupDone(lookupKind, "found", diagnostics, startedAt);
31075
31336
  return matchingTokens[0];
31076
31337
  }
31077
31338
  if (resolution.progress.emptyHosts > 0) {
31078
- this.captureLookupCompleted(lookupKind, "not-found", diagnostics, startedAt);
31339
+ this.lookupDone(lookupKind, "not-found", diagnostics, startedAt);
31079
31340
  return;
31080
31341
  }
31081
31342
  const reason = resolution.answer.outputs.length > 0 ? "token-malformed" : "lookup-incomplete";
31082
- this.captureLookupFailure(lookupKind, reason, diagnostics, startedAt);
31343
+ this.lookupFailed(lookupKind, reason, diagnostics, startedAt);
31083
31344
  throw new UMPTokenLookupError(reason, diagnostics);
31084
31345
  }
31085
31346
  /**
@@ -31110,7 +31371,7 @@ var OverlayUMPTokenInteractor = class {
31110
31371
  spent: /* @__PURE__ */ new Set()
31111
31372
  };
31112
31373
  evidence.txs.push(tx);
31113
- this.collectSpentOutpoints(tx, evidence.spent, /* @__PURE__ */ new Set());
31374
+ this.collectSpends(tx, evidence.spent, /* @__PURE__ */ new Set());
31114
31375
  evidenceByCandidate.set(outpoint, evidence);
31115
31376
  } catch {}
31116
31377
  if (evidenceByCandidate.size !== candidates.size) return void 0;
@@ -31119,7 +31380,7 @@ var OverlayUMPTokenInteractor = class {
31119
31380
  const provenContinuations = survivors.filter((outpoint) => {
31120
31381
  const evidence = evidenceByCandidate.get(outpoint);
31121
31382
  const token = candidates.get(outpoint);
31122
- return evidence != null && token != null && evidence.txs.some((tx) => this.consumesSameIdentityToken(tx, token));
31383
+ return evidence != null && token != null && evidence.txs.some((tx) => this.consumesIdentity(tx, token));
31123
31384
  });
31124
31385
  if (provenContinuations.length !== 1) return void 0;
31125
31386
  return candidates.get(provenContinuations[0]);
@@ -31130,7 +31391,7 @@ var OverlayUMPTokenInteractor = class {
31130
31391
  * hash — on-chain proof that the candidate is an update of a same-identity
31131
31392
  * predecessor rather than an independently minted token.
31132
31393
  */
31133
- consumesSameIdentityToken(tx, token) {
31394
+ consumesIdentity(tx, token) {
31134
31395
  const presentationHash = _bsv_sdk.Utils.toHex(token.presentationHash);
31135
31396
  const recoveryHash = _bsv_sdk.Utils.toHex(token.recoveryHash);
31136
31397
  for (const input of tx.inputs) {
@@ -31156,7 +31417,7 @@ var OverlayUMPTokenInteractor = class {
31156
31417
  * renditions are absent from the lookup answer. Iterative so arbitrarily
31157
31418
  * long update chains cannot exhaust the call stack.
31158
31419
  */
31159
- collectSpentOutpoints(tx, spent, visited) {
31420
+ collectSpends(tx, spent, visited) {
31160
31421
  const pending = [tx];
31161
31422
  while (pending.length > 0) {
31162
31423
  const current = pending.pop();
@@ -31171,7 +31432,7 @@ var OverlayUMPTokenInteractor = class {
31171
31432
  }
31172
31433
  }
31173
31434
  }
31174
- emptyLookupDiagnostics(correlationId) {
31435
+ emptyStats(correlationId) {
31175
31436
  return {
31176
31437
  hostCount: 0,
31177
31438
  completedHosts: 0,
@@ -31184,7 +31445,7 @@ var OverlayUMPTokenInteractor = class {
31184
31445
  ...correlationId !== void 0 ? { correlationId } : {}
31185
31446
  };
31186
31447
  }
31187
- toLookupDiagnostics(resolution) {
31448
+ diagnosticsFor(resolution) {
31188
31449
  const progress = resolution.progress;
31189
31450
  return {
31190
31451
  hostCount: progress.hostCount,
@@ -31198,7 +31459,7 @@ var OverlayUMPTokenInteractor = class {
31198
31459
  ...progress.correlationId !== void 0 ? { correlationId: progress.correlationId } : {}
31199
31460
  };
31200
31461
  }
31201
- lookupDiagnosticAttributes(diagnostics) {
31462
+ lookupAttrs(diagnostics) {
31202
31463
  return {
31203
31464
  hostCount: diagnostics.hostCount,
31204
31465
  completedHosts: diagnostics.completedHosts,
@@ -31210,7 +31471,7 @@ var OverlayUMPTokenInteractor = class {
31210
31471
  outputCount: diagnostics.outputCount
31211
31472
  };
31212
31473
  }
31213
- captureLookupCompleted(lookupKind, result, diagnostics, startedAt, extraAttributes = {}) {
31474
+ lookupDone(lookupKind, result, diagnostics, startedAt, extraAttributes = {}) {
31214
31475
  this.telemetry.capture({
31215
31476
  name: "wallet-toolbox.ump.lookup.completed",
31216
31477
  component: "wallet-toolbox.ump",
@@ -31220,12 +31481,12 @@ var OverlayUMPTokenInteractor = class {
31220
31481
  lookupKind,
31221
31482
  result,
31222
31483
  durationMs: Date.now() - startedAt,
31223
- ...this.lookupDiagnosticAttributes(diagnostics),
31484
+ ...this.lookupAttrs(diagnostics),
31224
31485
  ...extraAttributes
31225
31486
  }
31226
31487
  });
31227
31488
  }
31228
- captureLookupFailure(lookupKind, reason, diagnostics, startedAt, error) {
31489
+ lookupFailed(lookupKind, reason, diagnostics, startedAt, error) {
31229
31490
  this.telemetry.capture({
31230
31491
  name: "wallet-toolbox.ump.lookup.indeterminate",
31231
31492
  component: "wallet-toolbox.ump",
@@ -31235,7 +31496,7 @@ var OverlayUMPTokenInteractor = class {
31235
31496
  lookupKind,
31236
31497
  reason,
31237
31498
  durationMs: Date.now() - startedAt,
31238
- ...this.lookupDiagnosticAttributes(diagnostics)
31499
+ ...this.lookupAttrs(diagnostics)
31239
31500
  },
31240
31501
  error
31241
31502
  });
@@ -31253,27 +31514,27 @@ var OverlayUMPTokenInteractor = class {
31253
31514
  * @returns The outpoint of the newly created UMP token (e.g. "abcd1234...ef.0").
31254
31515
  */
31255
31516
  async buildAndSend(wallet, adminOriginator, token, oldTokenToConsume) {
31256
- const fields = this.buildUMPTokenFields(token);
31517
+ const fields = this.tokenFields(token);
31257
31518
  const tokenOutput = [{
31258
31519
  lockingScript: (await new _bsv_sdk.PushDrop(wallet, adminOriginator).lock(fields, [2, "admin user management token"], "1", "self", true, true)).toHex(),
31259
31520
  satoshis: 1,
31260
31521
  outputDescription: "New UMP token output"
31261
31522
  }];
31262
- const { resolvedOldToken, inputToken } = await this.resolveOldTokenInput(oldTokenToConsume);
31523
+ const { resolvedOldToken, inputToken } = await this.resolveOldInput(oldTokenToConsume);
31263
31524
  const inputs = resolvedOldToken?.currentOutpoint ? [{
31264
31525
  outpoint: resolvedOldToken.currentOutpoint,
31265
31526
  unlockingScriptLength: 73,
31266
31527
  inputDescription: "Consume old UMP token"
31267
31528
  }] : [];
31268
- const createResult = await this.createUMPAction(wallet, adminOriginator, inputs, tokenOutput, inputToken, resolvedOldToken);
31269
- if (!createResult.signableTransaction) return await this.broadcastFinishedUMPAction(createResult);
31529
+ const createResult = await this.createAction(wallet, adminOriginator, inputs, tokenOutput, inputToken, resolvedOldToken);
31530
+ if (!createResult.signableTransaction) return this.broadcastFinal(createResult);
31270
31531
  const reference = createResult.signableTransaction.reference;
31271
31532
  const partialTx = _bsv_sdk.Transaction.fromBEEF(createResult.signableTransaction.tx);
31272
- if (resolvedOldToken?.currentOutpoint) return await this.signAndBroadcastWithOldToken(wallet, adminOriginator, reference, partialTx);
31273
- return await this.signAndBroadcastNewToken(wallet, adminOriginator, reference);
31533
+ if (resolvedOldToken?.currentOutpoint) return this.renewToken(wallet, adminOriginator, reference, partialTx);
31534
+ return this.broadcastNew(wallet, adminOriginator, reference);
31274
31535
  }
31275
31536
  /** Assembles the ordered number[][] fields array from a UMPToken. */
31276
- buildUMPTokenFields(token) {
31537
+ tokenFields(token) {
31277
31538
  const fields = [];
31278
31539
  fields[0] = token.passwordSalt;
31279
31540
  fields[1] = token.passwordPresentationPrimary;
@@ -31300,20 +31561,20 @@ var OverlayUMPTokenInteractor = class {
31300
31561
  return fields;
31301
31562
  }
31302
31563
  /** Looks up the old token on the overlay; returns undefined resolved token if not found. */
31303
- async resolveOldTokenInput(oldTokenToConsume) {
31564
+ async resolveOldInput(oldTokenToConsume) {
31304
31565
  if (!oldTokenToConsume?.currentOutpoint) return {
31305
31566
  resolvedOldToken: void 0,
31306
31567
  inputToken: void 0
31307
31568
  };
31308
31569
  const inputToken = await this.findByOutpoint(oldTokenToConsume.currentOutpoint);
31309
- if (inputToken == null) throw new Error("The previous UMP token could not be resolved; refusing to publish a duplicate token.");
31570
+ if (inputToken == null) throw new Error("Previous UMP token unavailable; update refused.");
31310
31571
  return {
31311
31572
  resolvedOldToken: oldTokenToConsume,
31312
31573
  inputToken
31313
31574
  };
31314
31575
  }
31315
31576
  /** Creates the UMP action without dropping a required old-token input on failure. */
31316
- async createUMPAction(wallet, adminOriginator, inputs, outputs, inputToken, resolvedOldToken) {
31577
+ async createAction(wallet, adminOriginator, inputs, outputs, inputToken, resolvedOldToken) {
31317
31578
  try {
31318
31579
  return await wallet.createAction({
31319
31580
  description: resolvedOldToken == null ? "Create new UMP token" : "Renew UMP token (consume old, create new)",
@@ -31340,43 +31601,43 @@ var OverlayUMPTokenInteractor = class {
31340
31601
  }
31341
31602
  }
31342
31603
  /** Handles a fully-finalized (no signable tx) createAction result — broadcasts and returns outpoint. */
31343
- async broadcastFinishedUMPAction(createResult) {
31604
+ async broadcastFinal(createResult) {
31344
31605
  const finalTxid = createResult.txid || (createResult.tx != null ? _bsv_sdk.Transaction.fromAtomicBEEF(createResult.tx).id("hex") : void 0);
31345
- if (!finalTxid) throw new Error("No signableTransaction and no final TX found.");
31346
- if (createResult.tx == null) throw new Error("No final TX data to broadcast.");
31606
+ if (!finalTxid) throw new Error("UMP transaction was not finalized.");
31607
+ if (createResult.tx == null) throw new Error("UMP transaction data missing.");
31347
31608
  const broadcastTx = _bsv_sdk.Transaction.fromAtomicBEEF(createResult.tx);
31348
31609
  const result = await this.broadcaster.broadcast(broadcastTx);
31349
- this.assertSuccessfulBroadcast(result, "create-finalized");
31610
+ this.assertBroadcast(result, "create-finalized");
31350
31611
  return `${finalTxid}.0`;
31351
31612
  }
31352
31613
  /** Signs the old-token input and broadcasts — used during UMP token renewal. */
31353
- async signAndBroadcastWithOldToken(wallet, adminOriginator, reference, partialTx) {
31614
+ async renewToken(wallet, adminOriginator, reference, partialTx) {
31354
31615
  const unlockingScript = await new _bsv_sdk.PushDrop(wallet, adminOriginator).unlock([2, "admin user management token"], "1", "self").sign(partialTx, 0);
31355
31616
  const signResult = await wallet.signAction({
31356
31617
  reference,
31357
31618
  spends: { 0: { unlockingScript: unlockingScript.toHex() } }
31358
31619
  }, adminOriginator);
31359
31620
  const finalTxid = signResult.txid || (signResult.tx == null ? "" : _bsv_sdk.Transaction.fromAtomicBEEF(signResult.tx).id("hex"));
31360
- if (!finalTxid) throw new Error("Could not finalize transaction for renewed UMP token.");
31361
- if (signResult.tx == null) throw new Error("Final transaction data missing after signing renewed UMP token.");
31621
+ if (!finalTxid) throw new Error("Could not finalize renewed UMP token.");
31622
+ if (signResult.tx == null) throw new Error("Renewed UMP token transaction data missing.");
31362
31623
  const result = await this.broadcaster.broadcast(_bsv_sdk.Transaction.fromAtomicBEEF(signResult.tx));
31363
- this.assertSuccessfulBroadcast(result, "renew");
31624
+ this.assertBroadcast(result, "renew");
31364
31625
  return `${finalTxid}.0`;
31365
31626
  }
31366
31627
  /** Signs without input spending and broadcasts — used when creating a brand-new UMP token. */
31367
- async signAndBroadcastNewToken(wallet, adminOriginator, reference) {
31628
+ async broadcastNew(wallet, adminOriginator, reference) {
31368
31629
  const signResult = await wallet.signAction({
31369
31630
  reference,
31370
31631
  spends: {}
31371
31632
  }, adminOriginator);
31372
31633
  const finalTxid = signResult.txid || (signResult.tx == null ? "" : _bsv_sdk.Transaction.fromAtomicBEEF(signResult.tx).id("hex"));
31373
- if (!finalTxid) throw new Error("Failed to finalize new UMP token transaction.");
31374
- if (signResult.tx == null) throw new Error("Final transaction data missing after signing new UMP token.");
31634
+ if (!finalTxid) throw new Error("Could not finalize new UMP token.");
31635
+ if (signResult.tx == null) throw new Error("New UMP token transaction data missing.");
31375
31636
  const result = await this.broadcaster.broadcast(_bsv_sdk.Transaction.fromAtomicBEEF(signResult.tx));
31376
- this.assertSuccessfulBroadcast(result, "create");
31637
+ this.assertBroadcast(result, "create");
31377
31638
  return `${finalTxid}.0`;
31378
31639
  }
31379
- assertSuccessfulBroadcast(result, operation) {
31640
+ assertBroadcast(result, operation) {
31380
31641
  const succeeded = result.status === "success";
31381
31642
  this.telemetry.capture({
31382
31643
  name: succeeded ? "wallet-toolbox.ump.broadcast.completed" : "wallet-toolbox.ump.broadcast.failed",
@@ -31405,12 +31666,12 @@ var OverlayUMPTokenInteractor = class {
31405
31666
  if (answer.type !== "output-list" || answer.outputs.length === 0) return [];
31406
31667
  const tokens = [];
31407
31668
  for (const output of answer.outputs) {
31408
- const token = this.parseLookupOutput(output);
31669
+ const token = this.parseOutput(output);
31409
31670
  if (token != null) tokens.push(token);
31410
31671
  }
31411
31672
  return tokens;
31412
31673
  }
31413
- parseLookupOutput(output) {
31674
+ parseOutput(output) {
31414
31675
  try {
31415
31676
  const tx = _bsv_sdk.Transaction.fromBEEF(output.beef);
31416
31677
  const txOutput = tx.outputs[output.outputIndex];
@@ -31460,14 +31721,14 @@ var OverlayUMPTokenInteractor = class {
31460
31721
  correlationId
31461
31722
  });
31462
31723
  } catch (error) {
31463
- const diagnostics = this.emptyLookupDiagnostics(correlationId);
31464
- this.captureLookupFailure("outpoint", "lookup-unavailable", diagnostics, startedAt, error);
31724
+ const diagnostics = this.emptyStats(correlationId);
31725
+ this.lookupFailed("outpoint", "lookup-unavailable", diagnostics, startedAt, error);
31465
31726
  throw new UMPTokenLookupError("lookup-unavailable", diagnostics, { cause: error });
31466
31727
  }
31467
31728
  if (resolution.answer.outputs.length === 0) {
31468
31729
  if (resolution.progress.emptyHosts === 0) {
31469
- const diagnostics = this.toLookupDiagnostics(resolution);
31470
- this.captureLookupFailure("outpoint", "lookup-incomplete", diagnostics, startedAt);
31730
+ const diagnostics = this.diagnosticsFor(resolution);
31731
+ this.lookupFailed("outpoint", "lookup-incomplete", diagnostics, startedAt);
31471
31732
  throw new UMPTokenLookupError("lookup-incomplete", diagnostics);
31472
31733
  }
31473
31734
  return;
@@ -31609,20 +31870,20 @@ var CWIStyleWalletManager = class {
31609
31870
  if (this._initSnapshot !== void 0) {
31610
31871
  this.telemetry.capture({
31611
31872
  name: "wallet-toolbox.snapshot.initialization.started",
31612
- component: "wallet-toolbox.cwi-manager",
31873
+ component: CWI_COMPONENT,
31613
31874
  severity: "debug"
31614
31875
  });
31615
31876
  try {
31616
31877
  await this.loadSnapshot(this._initSnapshot);
31617
31878
  this.telemetry.capture({
31618
31879
  name: "wallet-toolbox.snapshot.initialization.completed",
31619
- component: "wallet-toolbox.cwi-manager",
31880
+ component: CWI_COMPONENT,
31620
31881
  severity: "info"
31621
31882
  });
31622
31883
  } catch (error) {
31623
31884
  this.telemetry.capture({
31624
31885
  name: "wallet-toolbox.snapshot.initialization.failed",
31625
- component: "wallet-toolbox.cwi-manager",
31886
+ component: CWI_COMPONENT,
31626
31887
  severity: "error",
31627
31888
  error
31628
31889
  });
@@ -31631,9 +31892,11 @@ var CWIStyleWalletManager = class {
31631
31892
  }
31632
31893
  }
31633
31894
  /**
31634
- * Provides the presentation key.
31895
+ * Provides the presentation key. A WAB operator pin may be supplied by the
31896
+ * authentication manager; normal lookup and lineage resolution always run
31897
+ * before this ambiguity-only fallback.
31635
31898
  */
31636
- async providePresentationKey(key) {
31899
+ async providePresentationKey(key, lookupOptions) {
31637
31900
  if (this.authenticated) throw new Error("User is already authenticated");
31638
31901
  if (this.authenticationMode === "recovery-key-and-password") throw new Error("Presentation key is not needed in this mode");
31639
31902
  if (key.length !== 32 || key.some((byte) => !Number.isInteger(byte) || byte < 0 || byte > 255)) throw new TypeError("Presentation key must contain exactly 32 bytes.");
@@ -31642,17 +31905,17 @@ var CWIStyleWalletManager = class {
31642
31905
  const startedAt = Date.now();
31643
31906
  this.telemetry.capture({
31644
31907
  name: "wallet-toolbox.authentication.account-lookup.started",
31645
- component: "wallet-toolbox.cwi-manager",
31908
+ component: CWI_COMPONENT,
31646
31909
  severity: "debug",
31647
31910
  attributes: { lookupKind: "presentation" }
31648
31911
  });
31649
31912
  let token;
31650
31913
  try {
31651
- token = await this.UMPTokenInteractor.findByPresentationKeyHash(hash);
31914
+ token = await this.UMPTokenInteractor.findByPresentationKeyHash(hash, lookupOptions);
31652
31915
  } catch (error) {
31653
31916
  this.telemetry.capture({
31654
31917
  name: "wallet-toolbox.authentication.account-lookup.failed",
31655
- component: "wallet-toolbox.cwi-manager",
31918
+ component: CWI_COMPONENT,
31656
31919
  severity: "warn",
31657
31920
  attributes: {
31658
31921
  lookupKind: "presentation",
@@ -31672,7 +31935,7 @@ var CWIStyleWalletManager = class {
31672
31935
  }
31673
31936
  this.telemetry.capture({
31674
31937
  name: "wallet-toolbox.authentication.account-lookup.completed",
31675
- component: "wallet-toolbox.cwi-manager",
31938
+ component: CWI_COMPONENT,
31676
31939
  severity: "info",
31677
31940
  attributes: {
31678
31941
  lookupKind: "presentation",
@@ -31688,11 +31951,11 @@ var CWIStyleWalletManager = class {
31688
31951
  if (this.authenticated) throw new Error("User is already authenticated");
31689
31952
  if (this.authenticationMode === "presentation-key-and-recovery-key") throw new Error("Password is not needed in this mode");
31690
31953
  if (this.authenticationFlow === "unknown") throw new Error("Determine account status with a presentation or recovery key before providing a password.");
31691
- if (this.authenticationFlow === "existing-user") await this.handleExistingUserPassword(password);
31692
- else await this.handleNewUserPassword(password);
31954
+ if (this.authenticationFlow === "existing-user") await this.unlockExisting(password);
31955
+ else await this.createNewUser(password);
31693
31956
  }
31694
31957
  /** Handles the password step for an existing user — derives keys, sets up infrastructure. */
31695
- async handleExistingUserPassword(password) {
31958
+ async unlockExisting(password) {
31696
31959
  if (this.currentUMPToken == null) throw new Error("Provide presentation or recovery key first.");
31697
31960
  const derivedPasswordKey = await derivePasswordKey(this.currentUMPToken, _bsv_sdk.Utils.toArray(password, "utf8"));
31698
31961
  let rootPrimaryKey;
@@ -31705,11 +31968,11 @@ var CWIStyleWalletManager = class {
31705
31968
  rootPrimaryKey = new _bsv_sdk.SymmetricKey(this.XOR(this.recoveryKey, derivedPasswordKey)).decrypt(this.currentUMPToken.passwordRecoveryPrimary);
31706
31969
  rootPrivilegedKey = new _bsv_sdk.SymmetricKey(this.XOR(rootPrimaryKey, derivedPasswordKey)).decrypt(this.currentUMPToken.passwordPrimaryPrivileged);
31707
31970
  }
31708
- await this.setupRootInfrastructure(rootPrimaryKey, rootPrivilegedKey);
31971
+ await this.setupRoot(rootPrimaryKey, rootPrivilegedKey);
31709
31972
  await this.switchProfile(this.activeProfileId);
31710
31973
  }
31711
31974
  /** Handles the password step for a new user — generates keys, builds UMP token, publishes on-chain. */
31712
- async handleNewUserPassword(password) {
31975
+ async createNewUser(password) {
31713
31976
  if (this.authenticationMode !== "presentation-key-and-password") throw new Error("New-user flow requires presentation key and password mode.");
31714
31977
  if (this.presentationKey == null) throw new Error("No presentation key provided for new-user flow.");
31715
31978
  const recoveryKey = (0, _bsv_sdk.Random)(32);
@@ -31748,14 +32011,14 @@ var CWIStyleWalletManager = class {
31748
32011
  passwordKdf: this.kdfConfig
31749
32012
  };
31750
32013
  this.currentUMPToken = newToken;
31751
- await this.setupRootInfrastructure(rootPrimaryKey);
32014
+ await this.setupRoot(rootPrimaryKey);
31752
32015
  await this.switchProfile(DEFAULT_PROFILE_ID);
31753
32016
  if (this.newWalletFunder != null && this.underlying != null) try {
31754
32017
  await this.newWalletFunder(this.presentationKey, this.underlying, this.adminOriginator);
31755
32018
  } catch (error) {
31756
32019
  this.telemetry.capture({
31757
32020
  name: "wallet-toolbox.authentication.new-wallet-funding.failed",
31758
- component: "wallet-toolbox.cwi-manager",
32021
+ component: CWI_COMPONENT,
31759
32022
  severity: "error",
31760
32023
  error: /* @__PURE__ */ new Error("New wallet funding failed.")
31761
32024
  });
@@ -31786,7 +32049,7 @@ var CWIStyleWalletManager = class {
31786
32049
  const xorKey = this.XOR(this.presentationKey, recoveryKey);
31787
32050
  const rootPrimaryKey = new _bsv_sdk.SymmetricKey(xorKey).decrypt(this.currentUMPToken.presentationRecoveryPrimary);
31788
32051
  const rootPrivilegedKey = new _bsv_sdk.SymmetricKey(xorKey).decrypt(this.currentUMPToken.presentationRecoveryPrivileged);
31789
- await this.setupRootInfrastructure(rootPrimaryKey, rootPrivilegedKey);
32052
+ await this.setupRoot(rootPrimaryKey, rootPrivilegedKey);
31790
32053
  await this.switchProfile(this.activeProfileId);
31791
32054
  }
31792
32055
  }
@@ -31817,7 +32080,7 @@ var CWIStyleWalletManager = class {
31817
32080
  if (snapshot.length > 16777216) throw new Error("Snapshot exceeds the maximum supported size.");
31818
32081
  this.telemetry.capture({
31819
32082
  name: "wallet-toolbox.snapshot.saved",
31820
- component: "wallet-toolbox.cwi-manager",
32083
+ component: CWI_COMPONENT,
31821
32084
  severity: "info",
31822
32085
  attributes: {
31823
32086
  formatVersion: 2,
@@ -31855,12 +32118,12 @@ var CWIStyleWalletManager = class {
31855
32118
  const tokenBytes = payloadReader.read(tokenLen);
31856
32119
  const token = this.deserializeUMPToken(tokenBytes);
31857
32120
  this.currentUMPToken = token;
31858
- await this.setupRootInfrastructure(rootPrimaryKey);
32121
+ await this.setupRoot(rootPrimaryKey);
31859
32122
  await this.switchProfile(activeProfileId);
31860
32123
  this.authenticationFlow = "existing-user";
31861
32124
  this.telemetry.capture({
31862
32125
  name: "wallet-toolbox.snapshot.loaded",
31863
- component: "wallet-toolbox.cwi-manager",
32126
+ component: CWI_COMPONENT,
31864
32127
  severity: "info",
31865
32128
  attributes: {
31866
32129
  formatVersion: version,
@@ -31871,7 +32134,7 @@ var CWIStyleWalletManager = class {
31871
32134
  this.destroy();
31872
32135
  this.telemetry.capture({
31873
32136
  name: "wallet-toolbox.snapshot.load-failed",
31874
- component: "wallet-toolbox.cwi-manager",
32137
+ component: CWI_COMPONENT,
31875
32138
  severity: "error",
31876
32139
  error
31877
32140
  });
@@ -31888,7 +32151,7 @@ var CWIStyleWalletManager = class {
31888
32151
  if (refreshed == null) return false;
31889
32152
  if (refreshed.currentOutpoint && currentToken.currentOutpoint && refreshed.currentOutpoint === currentToken.currentOutpoint) return false;
31890
32153
  this.currentUMPToken = refreshed;
31891
- await this.setupRootInfrastructure(this.rootPrimaryKey);
32154
+ await this.setupRoot(this.rootPrimaryKey);
31892
32155
  this.saveSnapshot();
31893
32156
  return true;
31894
32157
  }
@@ -31947,7 +32210,7 @@ var CWIStyleWalletManager = class {
31947
32210
  createdAt: Math.floor(Date.now() / 1e3)
31948
32211
  };
31949
32212
  this.profiles.push(newProfile);
31950
- await this.updateAuthFactors(this.currentUMPToken.passwordSalt, await this.getFactor("passwordKey"), await this.getFactor("presentationKey"), await this.getFactor("recoveryKey"), this.rootPrimaryKey, await this.getFactor("privilegedKey"), this.profiles);
32213
+ await this.updateFactors(this.currentUMPToken.passwordSalt, await this.getFactor("passwordKey"), await this.getFactor("presentationKey"), await this.getFactor("recoveryKey"), this.rootPrimaryKey, await this.getFactor("privilegedKey"), this.profiles);
31951
32214
  return newProfile.id;
31952
32215
  }
31953
32216
  /**
@@ -31964,7 +32227,7 @@ var CWIStyleWalletManager = class {
31964
32227
  if (profileIndex === -1) throw new Error("Profile not found.");
31965
32228
  this.profiles.splice(profileIndex, 1);
31966
32229
  if (this.activeProfileId.every((x, i) => x === profileId[i])) await this.switchProfile(DEFAULT_PROFILE_ID);
31967
- await this.updateAuthFactors(this.currentUMPToken.passwordSalt, await this.getFactor("passwordKey"), await this.getFactor("presentationKey"), await this.getFactor("recoveryKey"), this.rootPrimaryKey, await this.getFactor("privilegedKey"), this.profiles);
32230
+ await this.updateFactors(this.currentUMPToken.passwordSalt, await this.getFactor("passwordKey"), await this.getFactor("presentationKey"), await this.getFactor("recoveryKey"), this.rootPrimaryKey, await this.getFactor("privilegedKey"), this.profiles);
31968
32231
  }
31969
32232
  /**
31970
32233
  * Switches the active profile. This re-derives keys and rebuilds the underlying wallet.
@@ -32005,14 +32268,14 @@ var CWIStyleWalletManager = class {
32005
32268
  const recoveryKey = await this.getFactor("recoveryKey");
32006
32269
  const presentationKey = await this.getFactor("presentationKey");
32007
32270
  const rootPrivilegedKey = await this.getFactor("privilegedKey");
32008
- await this.updateAuthFactors(passwordSalt, newPasswordKey, presentationKey, recoveryKey, this.rootPrimaryKey, rootPrivilegedKey, this.profiles);
32271
+ await this.updateFactors(passwordSalt, newPasswordKey, presentationKey, recoveryKey, this.rootPrimaryKey, rootPrivilegedKey, this.profiles);
32009
32272
  }
32010
32273
  /**
32011
32274
  * Retrieves the current recovery key. Requires privileged access.
32012
32275
  */
32013
32276
  async getRecoveryKey() {
32014
32277
  if (!this.authenticated || this.currentUMPToken == null || this.rootPrivilegedKeyManager == null) throw new Error("Not authenticated or missing required data.");
32015
- return await this.getFactor("recoveryKey");
32278
+ return this.getFactor("recoveryKey");
32016
32279
  }
32017
32280
  /**
32018
32281
  * Changes the user's recovery key. Prompts user to save the new key.
@@ -32024,7 +32287,7 @@ var CWIStyleWalletManager = class {
32024
32287
  const rootPrivilegedKey = await this.getFactor("privilegedKey");
32025
32288
  const newRecoveryKey = (0, _bsv_sdk.Random)(32);
32026
32289
  await this.recoveryKeySaver(newRecoveryKey);
32027
- await this.updateAuthFactors(this.currentUMPToken.passwordSalt, passwordKey, presentationKey, newRecoveryKey, this.rootPrimaryKey, rootPrivilegedKey, this.profiles);
32290
+ await this.updateFactors(this.currentUMPToken.passwordSalt, passwordKey, presentationKey, newRecoveryKey, this.rootPrimaryKey, rootPrivilegedKey, this.profiles);
32028
32291
  }
32029
32292
  /**
32030
32293
  * Changes the user's presentation key.
@@ -32035,7 +32298,7 @@ var CWIStyleWalletManager = class {
32035
32298
  const recoveryKey = await this.getFactor("recoveryKey");
32036
32299
  const passwordKey = await this.getFactor("passwordKey");
32037
32300
  const rootPrivilegedKey = await this.getFactor("privilegedKey");
32038
- await this.updateAuthFactors(this.currentUMPToken.passwordSalt, passwordKey, newPresentationKey, recoveryKey, this.rootPrimaryKey, rootPrivilegedKey, this.profiles);
32301
+ await this.updateFactors(this.currentUMPToken.passwordSalt, passwordKey, newPresentationKey, recoveryKey, this.rootPrimaryKey, rootPrivilegedKey, this.profiles);
32039
32302
  if (this.presentationKey != null) this.presentationKey = newPresentationKey;
32040
32303
  }
32041
32304
  /**
@@ -32081,7 +32344,7 @@ var CWIStyleWalletManager = class {
32081
32344
  } catch (error) {
32082
32345
  this.telemetry.capture({
32083
32346
  name: "wallet-toolbox.authentication.factor-decryption.failed",
32084
- component: "wallet-toolbox.cwi-manager",
32347
+ component: CWI_COMPONENT,
32085
32348
  severity: "error",
32086
32349
  attributes: { factor: factorName },
32087
32350
  error
@@ -32094,7 +32357,7 @@ var CWIStyleWalletManager = class {
32094
32357
  * Recomputes UMP token fields with updated factors and profiles, then publishes the update.
32095
32358
  * This operation requires the *root* privileged key and the *default* profile wallet.
32096
32359
  */
32097
- async updateAuthFactors(passwordSalt, passwordKey, presentationKey, recoveryKey, rootPrimaryKey, rootPrivilegedKey, profiles) {
32360
+ async updateFactors(passwordSalt, passwordKey, presentationKey, recoveryKey, rootPrimaryKey, rootPrivilegedKey, profiles) {
32098
32361
  if (!this.authenticated || this.rootPrimaryKey == null || this.currentUMPToken == null) throw new Error("Wallet is not properly authenticated or missing data for update.");
32099
32362
  const oldTokenToConsume = { ...this.currentUMPToken };
32100
32363
  if (!oldTokenToConsume.currentOutpoint) throw new Error("Cannot update UMP token: Old token has no outpoint.");
@@ -32145,7 +32408,7 @@ var CWIStyleWalletManager = class {
32145
32408
  if (!currentActiveId.every((x) => x === 0)) {
32146
32409
  this.telemetry.capture({
32147
32410
  name: "wallet-toolbox.ump.profile-switch.started",
32148
- component: "wallet-toolbox.cwi-manager",
32411
+ component: CWI_COMPONENT,
32149
32412
  severity: "debug",
32150
32413
  attributes: { reason: "token-update" }
32151
32414
  });
@@ -32161,7 +32424,7 @@ var CWIStyleWalletManager = class {
32161
32424
  await this.switchProfile(currentActiveId);
32162
32425
  this.telemetry.capture({
32163
32426
  name: "wallet-toolbox.ump.profile-switch.completed",
32164
- component: "wallet-toolbox.cwi-manager",
32427
+ component: CWI_COMPONENT,
32165
32428
  severity: "debug",
32166
32429
  attributes: { reason: "token-update" }
32167
32430
  });
@@ -32288,9 +32551,9 @@ var CWIStyleWalletManager = class {
32288
32551
  * @param rootPrimaryKey The user's root primary key (32 bytes).
32289
32552
  * @param ephemeralRootPrivilegedKey Optional root privileged key (e.g., during recovery flows).
32290
32553
  */
32291
- async setupRootInfrastructure(rootPrimaryKey, ephemeralRootPrivilegedKey) {
32554
+ async setupRoot(rootKey, ephemeralRootPrivilegedKey) {
32292
32555
  if (this.currentUMPToken == null) throw new Error("A UMP token must exist before setting up root infrastructure!");
32293
- this.rootPrimaryKey = rootPrimaryKey;
32556
+ this.rootPrimaryKey = rootKey;
32294
32557
  let oneTimePrivilegedKey = ephemeralRootPrivilegedKey == null ? void 0 : new _bsv_sdk.PrivateKey(ephemeralRootPrivilegedKey);
32295
32558
  this.rootPrivilegedKeyManager = new PrivilegedKeyManager(async (reason) => {
32296
32559
  if (oneTimePrivilegedKey != null) {
@@ -32311,7 +32574,7 @@ var CWIStyleWalletManager = class {
32311
32574
  });
32312
32575
  this.profiles = [];
32313
32576
  if (this.currentUMPToken.profilesEncrypted != null && this.currentUMPToken.profilesEncrypted.length > 0) try {
32314
- const decryptedProfileBytes = new _bsv_sdk.SymmetricKey(rootPrimaryKey).decrypt(this.currentUMPToken.profilesEncrypted);
32577
+ const decryptedProfileBytes = new _bsv_sdk.SymmetricKey(rootKey).decrypt(this.currentUMPToken.profilesEncrypted);
32315
32578
  const profilesJson = _bsv_sdk.Utils.toUTF8(decryptedProfileBytes);
32316
32579
  const profiles = JSON.parse(profilesJson);
32317
32580
  if (!Array.isArray(profiles) || profiles.length > 1e3 || !profiles.every(isValidProfile)) throw new Error("Decrypted profile data is invalid or exceeds supported bounds.");
@@ -32320,7 +32583,7 @@ var CWIStyleWalletManager = class {
32320
32583
  this.profiles = [];
32321
32584
  this.telemetry.capture({
32322
32585
  name: "wallet-toolbox.profile.load-failed",
32323
- component: "wallet-toolbox.cwi-manager",
32586
+ component: CWI_COMPONENT,
32324
32587
  severity: "error",
32325
32588
  error
32326
32589
  });
@@ -32329,98 +32592,98 @@ var CWIStyleWalletManager = class {
32329
32592
  }
32330
32593
  this.authenticated = true;
32331
32594
  }
32332
- checkAuthAndUnderlying(originator) {
32595
+ assertReady(originator) {
32333
32596
  if (!this.authenticated) throw new Error("User is not authenticated.");
32334
32597
  if (this.underlying == null) throw new Error("Underlying wallet for the active profile is not initialized.");
32335
32598
  if (originator === this.adminOriginator) throw new Error("External applications are not allowed to use the admin originator.");
32336
32599
  }
32337
32600
  async getPublicKey(args, originator) {
32338
- this.checkAuthAndUnderlying(originator);
32339
- return await this.underlying.getPublicKey(args, originator);
32601
+ this.assertReady(originator);
32602
+ return this.underlying.getPublicKey(args, originator);
32340
32603
  }
32341
32604
  async revealCounterpartyKeyLinkage(args, originator) {
32342
- this.checkAuthAndUnderlying(originator);
32343
- return await this.underlying.revealCounterpartyKeyLinkage(args, originator);
32605
+ this.assertReady(originator);
32606
+ return this.underlying.revealCounterpartyKeyLinkage(args, originator);
32344
32607
  }
32345
32608
  async revealSpecificKeyLinkage(args, originator) {
32346
- this.checkAuthAndUnderlying(originator);
32347
- return await this.underlying.revealSpecificKeyLinkage(args, originator);
32609
+ this.assertReady(originator);
32610
+ return this.underlying.revealSpecificKeyLinkage(args, originator);
32348
32611
  }
32349
32612
  async encrypt(args, originator) {
32350
- this.checkAuthAndUnderlying(originator);
32351
- return await this.underlying.encrypt(args, originator);
32613
+ this.assertReady(originator);
32614
+ return this.underlying.encrypt(args, originator);
32352
32615
  }
32353
32616
  async decrypt(args, originator) {
32354
- this.checkAuthAndUnderlying(originator);
32355
- return await this.underlying.decrypt(args, originator);
32617
+ this.assertReady(originator);
32618
+ return this.underlying.decrypt(args, originator);
32356
32619
  }
32357
32620
  async createHmac(args, originator) {
32358
- this.checkAuthAndUnderlying(originator);
32359
- return await this.underlying.createHmac(args, originator);
32621
+ this.assertReady(originator);
32622
+ return this.underlying.createHmac(args, originator);
32360
32623
  }
32361
32624
  async verifyHmac(args, originator) {
32362
- this.checkAuthAndUnderlying(originator);
32363
- return await this.underlying.verifyHmac(args, originator);
32625
+ this.assertReady(originator);
32626
+ return this.underlying.verifyHmac(args, originator);
32364
32627
  }
32365
32628
  async createSignature(args, originator) {
32366
- this.checkAuthAndUnderlying(originator);
32367
- return await this.underlying.createSignature(args, originator);
32629
+ this.assertReady(originator);
32630
+ return this.underlying.createSignature(args, originator);
32368
32631
  }
32369
32632
  async verifySignature(args, originator) {
32370
- this.checkAuthAndUnderlying(originator);
32371
- return await this.underlying.verifySignature(args, originator);
32633
+ this.assertReady(originator);
32634
+ return this.underlying.verifySignature(args, originator);
32372
32635
  }
32373
32636
  async createAction(args, originator) {
32374
- this.checkAuthAndUnderlying(originator);
32375
- return await this.underlying.createAction(args, originator);
32637
+ this.assertReady(originator);
32638
+ return this.underlying.createAction(args, originator);
32376
32639
  }
32377
32640
  async signAction(args, originator) {
32378
- this.checkAuthAndUnderlying(originator);
32379
- return await this.underlying.signAction(args, originator);
32641
+ this.assertReady(originator);
32642
+ return this.underlying.signAction(args, originator);
32380
32643
  }
32381
32644
  async abortAction(args, originator) {
32382
- this.checkAuthAndUnderlying(originator);
32383
- return await this.underlying.abortAction(args, originator);
32645
+ this.assertReady(originator);
32646
+ return this.underlying.abortAction(args, originator);
32384
32647
  }
32385
32648
  async listActions(args, originator) {
32386
- this.checkAuthAndUnderlying(originator);
32387
- return await this.underlying.listActions(args, originator);
32649
+ this.assertReady(originator);
32650
+ return this.underlying.listActions(args, originator);
32388
32651
  }
32389
32652
  async internalizeAction(args, originator) {
32390
- this.checkAuthAndUnderlying(originator);
32391
- return await this.underlying.internalizeAction(args, originator);
32653
+ this.assertReady(originator);
32654
+ return this.underlying.internalizeAction(args, originator);
32392
32655
  }
32393
32656
  async listOutputs(args, originator) {
32394
- this.checkAuthAndUnderlying(originator);
32395
- return await this.underlying.listOutputs(args, originator);
32657
+ this.assertReady(originator);
32658
+ return this.underlying.listOutputs(args, originator);
32396
32659
  }
32397
32660
  async relinquishOutput(args, originator) {
32398
- this.checkAuthAndUnderlying(originator);
32399
- return await this.underlying.relinquishOutput(args, originator);
32661
+ this.assertReady(originator);
32662
+ return this.underlying.relinquishOutput(args, originator);
32400
32663
  }
32401
32664
  async acquireCertificate(args, originator) {
32402
- this.checkAuthAndUnderlying(originator);
32403
- return await this.underlying.acquireCertificate(args, originator);
32665
+ this.assertReady(originator);
32666
+ return this.underlying.acquireCertificate(args, originator);
32404
32667
  }
32405
32668
  async listCertificates(args, originator) {
32406
- this.checkAuthAndUnderlying(originator);
32407
- return await this.underlying.listCertificates(args, originator);
32669
+ this.assertReady(originator);
32670
+ return this.underlying.listCertificates(args, originator);
32408
32671
  }
32409
32672
  async proveCertificate(args, originator) {
32410
- this.checkAuthAndUnderlying(originator);
32411
- return await this.underlying.proveCertificate(args, originator);
32673
+ this.assertReady(originator);
32674
+ return this.underlying.proveCertificate(args, originator);
32412
32675
  }
32413
32676
  async relinquishCertificate(args, originator) {
32414
- this.checkAuthAndUnderlying(originator);
32415
- return await this.underlying.relinquishCertificate(args, originator);
32677
+ this.assertReady(originator);
32678
+ return this.underlying.relinquishCertificate(args, originator);
32416
32679
  }
32417
32680
  async discoverByIdentityKey(args, originator) {
32418
- this.checkAuthAndUnderlying(originator);
32419
- return await this.underlying.discoverByIdentityKey(args, originator);
32681
+ this.assertReady(originator);
32682
+ return this.underlying.discoverByIdentityKey(args, originator);
32420
32683
  }
32421
32684
  async discoverByAttributes(args, originator) {
32422
- this.checkAuthAndUnderlying(originator);
32423
- return await this.underlying.discoverByAttributes(args, originator);
32685
+ this.assertReady(originator);
32686
+ return this.underlying.discoverByAttributes(args, originator);
32424
32687
  }
32425
32688
  async isAuthenticated(_, originator) {
32426
32689
  if (!this.authenticated) throw new Error("User is not authenticated.");
@@ -32430,23 +32693,23 @@ var CWIStyleWalletManager = class {
32430
32693
  async waitForAuthentication(_, originator) {
32431
32694
  if (originator === this.adminOriginator) throw new Error("External applications are not allowed to use the admin originator.");
32432
32695
  while (!this.authenticated || this.underlying == null) await new Promise((resolve) => setTimeout(resolve, 100));
32433
- return await this.underlying.waitForAuthentication({}, originator);
32696
+ return this.underlying.waitForAuthentication({}, originator);
32434
32697
  }
32435
32698
  async getHeight(_, originator) {
32436
- this.checkAuthAndUnderlying(originator);
32437
- return await this.underlying.getHeight({}, originator);
32699
+ this.assertReady(originator);
32700
+ return this.underlying.getHeight({}, originator);
32438
32701
  }
32439
32702
  async getHeaderForHeight(args, originator) {
32440
- this.checkAuthAndUnderlying(originator);
32441
- return await this.underlying.getHeaderForHeight(args, originator);
32703
+ this.assertReady(originator);
32704
+ return this.underlying.getHeaderForHeight(args, originator);
32442
32705
  }
32443
32706
  async getNetwork(_, originator) {
32444
- this.checkAuthAndUnderlying(originator);
32445
- return await this.underlying.getNetwork({}, originator);
32707
+ this.assertReady(originator);
32708
+ return this.underlying.getNetwork({}, originator);
32446
32709
  }
32447
32710
  async getVersion(_, originator) {
32448
- this.checkAuthAndUnderlying(originator);
32449
- return await this.underlying.getVersion({}, originator);
32711
+ this.assertReady(originator);
32712
+ return this.underlying.getVersion({}, originator);
32450
32713
  }
32451
32714
  };
32452
32715
  //#endregion
@@ -32790,6 +33053,8 @@ const DEFAULT_MAX_RESPONSE_BYTES = 1024 * 1024;
32790
33053
  const MAX_CONFIGURED_TIMEOUT_MS = 12e4;
32791
33054
  const MAX_CONFIGURED_REQUEST_BYTES = 10 * 1024 * 1024;
32792
33055
  const MAX_CONFIGURED_RESPONSE_BYTES = 10 * 1024 * 1024;
33056
+ const WAB_COMPONENT = "wallet-toolbox.wab-transport";
33057
+ const WAB_REQUEST_EVENT = "wallet-toolbox.wab.request.";
32793
33058
  const defaultFetch = typeof globalThis !== "undefined" && typeof globalThis.fetch === "function" ? globalThis.fetch.bind(globalThis) : void 0;
32794
33059
  /**
32795
33060
  * A privacy-safe WAB transport failure. Response bodies and request payloads
@@ -32828,8 +33093,8 @@ function normalizeServerUrl(serverUrl) {
32828
33093
  } catch {
32829
33094
  throw new WABClientError("WAB_INVALID_CONFIGURATION", "WAB server URL must be an absolute URL.", false);
32830
33095
  }
32831
- if (parsed.username !== "" || parsed.password !== "" || parsed.search !== "" || parsed.hash !== "") throw new WABClientError("WAB_INVALID_CONFIGURATION", "WAB server URL must not contain credentials, a query, or a fragment.", false);
32832
- if (parsed.protocol !== "https:" && !(parsed.protocol === "http:" && isLocalHostname(parsed.hostname))) throw new WABClientError("WAB_INVALID_CONFIGURATION", "WAB server URL must use HTTPS. Plain HTTP is allowed only for localhost development.", false);
33096
+ if (parsed.username !== "" || parsed.password !== "" || parsed.search !== "" || parsed.hash !== "") throw new WABClientError("WAB_INVALID_CONFIGURATION", "WAB URL cannot include credentials, query, or fragment.", false);
33097
+ if (parsed.protocol !== "https:" && !(parsed.protocol === "http:" && isLocalHostname(parsed.hostname))) throw new WABClientError("WAB_INVALID_CONFIGURATION", "WAB URL requires HTTPS except on localhost.", false);
32833
33098
  let pathname = parsed.pathname;
32834
33099
  while (pathname.endsWith("/")) pathname = pathname.slice(0, -1);
32835
33100
  return {
@@ -32839,7 +33104,7 @@ function normalizeServerUrl(serverUrl) {
32839
33104
  }
32840
33105
  function normalizePositiveInteger(value, fallback, maximum, name) {
32841
33106
  const resolved = value ?? fallback;
32842
- if (!Number.isInteger(resolved) || resolved <= 0 || resolved > maximum) throw new WABClientError("WAB_INVALID_CONFIGURATION", `${name} must be a positive integer no greater than ${maximum}.`, false);
33107
+ if (!Number.isInteger(resolved) || resolved <= 0 || resolved > maximum) throw new WABClientError("WAB_INVALID_CONFIGURATION", `${name} must be an integer from 1 to ${maximum}.`, false);
32843
33108
  return resolved;
32844
33109
  }
32845
33110
  function assertSafePath(path) {
@@ -32864,20 +33129,20 @@ var WABTransport = class {
32864
33129
  serverUrl;
32865
33130
  serverOrigin;
32866
33131
  telemetry;
32867
- fetchClient;
32868
- timeoutMs;
32869
- maxRequestBytes;
32870
- maxResponseBytes;
33132
+ fetcher;
33133
+ timeout;
33134
+ requestLimit;
33135
+ responseLimit;
32871
33136
  constructor(serverUrl, options = {}) {
32872
33137
  const normalized = normalizeServerUrl(serverUrl);
32873
33138
  this.serverUrl = normalized.baseUrl;
32874
33139
  this.serverOrigin = normalized.origin;
32875
- const fetchClient = options.fetch ?? defaultFetch;
32876
- if (typeof fetchClient !== "function") throw new WABClientError("WAB_INVALID_CONFIGURATION", "WABClient requires a fetch implementation.", false);
32877
- this.fetchClient = fetchClient;
32878
- this.timeoutMs = normalizePositiveInteger(options.timeoutMs, DEFAULT_TIMEOUT_MS, MAX_CONFIGURED_TIMEOUT_MS, "timeoutMs");
32879
- this.maxRequestBytes = normalizePositiveInteger(options.maxRequestBytes, DEFAULT_MAX_REQUEST_BYTES, MAX_CONFIGURED_REQUEST_BYTES, "maxRequestBytes");
32880
- this.maxResponseBytes = normalizePositiveInteger(options.maxResponseBytes, DEFAULT_MAX_RESPONSE_BYTES, MAX_CONFIGURED_RESPONSE_BYTES, "maxResponseBytes");
33140
+ const fetcher = options.fetch ?? defaultFetch;
33141
+ if (typeof fetcher !== "function") throw new WABClientError("WAB_INVALID_CONFIGURATION", "WABClient requires a fetch implementation.", false);
33142
+ this.fetcher = fetcher;
33143
+ this.timeout = normalizePositiveInteger(options.timeoutMs, DEFAULT_TIMEOUT_MS, MAX_CONFIGURED_TIMEOUT_MS, "timeoutMs");
33144
+ this.requestLimit = normalizePositiveInteger(options.maxRequestBytes, DEFAULT_MAX_REQUEST_BYTES, MAX_CONFIGURED_REQUEST_BYTES, "maxRequestBytes");
33145
+ this.responseLimit = normalizePositiveInteger(options.maxResponseBytes, DEFAULT_MAX_RESPONSE_BYTES, MAX_CONFIGURED_RESPONSE_BYTES, "maxResponseBytes");
32881
33146
  this.telemetry = new _bsv_sdk.Telemetry(options.telemetry);
32882
33147
  }
32883
33148
  createCorrelationId() {
@@ -32885,38 +33150,9 @@ var WABTransport = class {
32885
33150
  return isSafeCorrelationId(correlationId) ? correlationId : new _bsv_sdk.Telemetry().createCorrelationId();
32886
33151
  }
32887
33152
  async request(path, options) {
32888
- const metadata = this.createRequestMetadata(path, options);
32889
- this.captureRequestStarted(metadata);
32890
- const body = this.encodeRequestBody(options, metadata);
32891
- const timeout = this.startRequestTimeout(metadata.errorContext);
32892
- const response = await this.fetchResponse(metadata, body, timeout);
32893
- const responseContext = this.createResponseContext(response, metadata);
32894
- this.assertSuccessfulResponse(response, responseContext, metadata, timeout);
32895
- const responseText = await this.readResponseText(response, responseContext, metadata, timeout);
32896
- const parsed = this.parseResponseObject(responseText, response, responseContext, metadata);
32897
- this.telemetry.capture({
32898
- name: "wallet-toolbox.wab.request.completed",
32899
- component: "wallet-toolbox.wab-transport",
32900
- severity: "info",
32901
- correlationId: metadata.correlationId,
32902
- attributes: {
32903
- operation: metadata.operation,
32904
- method: metadata.method,
32905
- route: metadata.path,
32906
- serverOrigin: this.serverOrigin,
32907
- status: response.status,
32908
- endpointMarkerPresent: responseContext.endpointMarkerPresent,
32909
- responseCorrelationMatched: responseContext.responseCorrelationMatched,
32910
- responseBytes: new TextEncoder().encode(responseText).byteLength,
32911
- durationMs: Date.now() - metadata.startedAt
32912
- }
32913
- });
32914
- return parsed;
32915
- }
32916
- createRequestMetadata(path, options) {
32917
33153
  assertSafePath(path);
32918
33154
  const correlationId = options.correlationId != null && isSafeCorrelationId(options.correlationId) ? options.correlationId : this.createCorrelationId();
32919
- return {
33155
+ const metadata = {
32920
33156
  method: options.method ?? "POST",
32921
33157
  path,
32922
33158
  operation: options.operation,
@@ -32928,46 +33164,73 @@ var WABTransport = class {
32928
33164
  route: path
32929
33165
  }
32930
33166
  };
32931
- }
32932
- captureRequestStarted(metadata) {
32933
33167
  this.telemetry.capture({
32934
- name: "wallet-toolbox.wab.request.started",
32935
- component: "wallet-toolbox.wab-transport",
33168
+ name: `${WAB_REQUEST_EVENT}started`,
33169
+ component: WAB_COMPONENT,
32936
33170
  severity: "debug",
33171
+ correlationId,
33172
+ attributes: {
33173
+ operation: metadata.operation,
33174
+ method: metadata.method,
33175
+ route: path,
33176
+ serverOrigin: this.serverOrigin
33177
+ }
33178
+ });
33179
+ const body = this.bodyFor(options, metadata);
33180
+ const timeout = this.startTimer(metadata.errorContext);
33181
+ const response = await this.fetch(metadata, body, timeout);
33182
+ const responseContext = {
33183
+ ...metadata.errorContext,
33184
+ endpointMarkerPresent: isWabResponse(response),
33185
+ responseCorrelationMatched: response.headers.get("X-Correlation-ID") === correlationId
33186
+ };
33187
+ this.checkResponse(response, responseContext, metadata, timeout);
33188
+ const responseText = await this.readText(response, responseContext, metadata, timeout);
33189
+ const parsed = this.parse(responseText, response, responseContext, metadata);
33190
+ this.telemetry.capture({
33191
+ name: `${WAB_REQUEST_EVENT}completed`,
33192
+ component: WAB_COMPONENT,
33193
+ severity: "info",
32937
33194
  correlationId: metadata.correlationId,
32938
33195
  attributes: {
32939
33196
  operation: metadata.operation,
32940
33197
  method: metadata.method,
32941
33198
  route: metadata.path,
32942
- serverOrigin: this.serverOrigin
33199
+ serverOrigin: this.serverOrigin,
33200
+ status: response.status,
33201
+ endpointMarkerPresent: responseContext.endpointMarkerPresent,
33202
+ responseCorrelationMatched: responseContext.responseCorrelationMatched,
33203
+ responseBytes: new TextEncoder().encode(responseText).byteLength,
33204
+ durationMs: Date.now() - metadata.startedAt
32943
33205
  }
32944
33206
  });
33207
+ return parsed;
32945
33208
  }
32946
- encodeRequestBody(options, metadata) {
33209
+ bodyFor(options, metadata) {
32947
33210
  let body;
32948
33211
  try {
32949
33212
  body = options.body === void 0 ? void 0 : JSON.stringify(options.body);
32950
33213
  } catch (cause) {
32951
- const error = new WABClientError("WAB_INVALID_REQUEST", "WAB request payload could not be encoded.", false, void 0, {
33214
+ const error = new WABClientError("WAB_INVALID_REQUEST", "WAB request encoding failed.", false, void 0, {
32952
33215
  ...metadata.errorContext,
32953
33216
  cause
32954
33217
  });
32955
- this.captureRequestFailure(metadata, error);
33218
+ this.report(metadata, error);
32956
33219
  throw error;
32957
33220
  }
32958
33221
  if (options.body !== void 0 && body === void 0) {
32959
- const error = new WABClientError("WAB_INVALID_REQUEST", "WAB request payload must be JSON-serializable.", false, void 0, metadata.errorContext);
32960
- this.captureRequestFailure(metadata, error);
33222
+ const error = new WABClientError("WAB_INVALID_REQUEST", "WAB request is not JSON-serializable.", false, void 0, metadata.errorContext);
33223
+ this.report(metadata, error);
32961
33224
  throw error;
32962
33225
  }
32963
- if (body != null && new TextEncoder().encode(body).byteLength > this.maxRequestBytes) {
32964
- const error = new WABClientError("WAB_REQUEST_TOO_LARGE", "WAB request exceeded the configured size limit.", false, void 0, metadata.errorContext);
32965
- this.captureRequestFailure(metadata, error);
33226
+ if (body != null && new TextEncoder().encode(body).byteLength > this.requestLimit) {
33227
+ const error = new WABClientError("WAB_REQUEST_TOO_LARGE", "WAB request exceeds its size limit.", false, void 0, metadata.errorContext);
33228
+ this.report(metadata, error);
32966
33229
  throw error;
32967
33230
  }
32968
33231
  return body;
32969
33232
  }
32970
- startRequestTimeout(errorContext) {
33233
+ startTimer(errorContext) {
32971
33234
  const timeout = {
32972
33235
  controller: new AbortController(),
32973
33236
  timedOut: false
@@ -32977,12 +33240,12 @@ var WABTransport = class {
32977
33240
  timeout.timedOut = true;
32978
33241
  timeout.controller.abort();
32979
33242
  reject(new WABClientError("WAB_TIMEOUT", "WAB request timed out.", true, void 0, errorContext));
32980
- }, this.timeoutMs);
33243
+ }, this.timeout);
32981
33244
  });
32982
33245
  return timeout;
32983
33246
  }
32984
- async fetchResponse(metadata, body, timeout) {
32985
- const requestPromise = Promise.resolve().then(() => this.fetchClient(`${this.serverUrl}${metadata.path}`, {
33247
+ async fetch(metadata, body, timeout) {
33248
+ const requestPromise = Promise.resolve().then(() => this.fetcher(`${this.serverUrl}${metadata.path}`, {
32986
33249
  method: metadata.method,
32987
33250
  headers: {
32988
33251
  Accept: "application/json",
@@ -33006,33 +33269,26 @@ var WABTransport = class {
33006
33269
  ...metadata.errorContext,
33007
33270
  cause
33008
33271
  });
33009
- else error = new WABClientError("WAB_NETWORK_ERROR", "WAB request failed before receiving a response.", true, void 0, {
33272
+ else error = new WABClientError("WAB_NETWORK_ERROR", "WAB request failed before response.", true, void 0, {
33010
33273
  ...metadata.errorContext,
33011
33274
  cause
33012
33275
  });
33013
- this.captureRequestFailure(metadata, error);
33276
+ this.report(metadata, error);
33014
33277
  throw error;
33015
33278
  }
33016
33279
  }
33017
- createResponseContext(response, metadata) {
33018
- return {
33019
- ...metadata.errorContext,
33020
- endpointMarkerPresent: isWabResponse(response),
33021
- responseCorrelationMatched: response.headers.get("X-Correlation-ID") === metadata.correlationId
33022
- };
33023
- }
33024
- assertSuccessfulResponse(response, responseContext, metadata, timeout) {
33280
+ checkResponse(response, responseContext, metadata, timeout) {
33025
33281
  if (response.ok) return;
33026
33282
  if (timeout.timer !== void 0) clearTimeout(timeout.timer);
33027
33283
  const endpointMismatch = response.status === 404 && responseContext.endpointMarkerPresent !== true;
33028
- const error = new WABClientError(endpointMismatch ? "WAB_ENDPOINT_MISMATCH" : "WAB_HTTP_ERROR", endpointMismatch ? "Configured WAB endpoint did not return a compatible WAB response." : `WAB request failed with HTTP status ${response.status}.`, isRetryableStatus(response.status), response.status, responseContext);
33029
- this.captureRequestFailure(metadata, error);
33284
+ const error = new WABClientError(endpointMismatch ? "WAB_ENDPOINT_MISMATCH" : "WAB_HTTP_ERROR", endpointMismatch ? "WAB endpoint is incompatible." : `WAB request failed with HTTP status ${response.status}.`, isRetryableStatus(response.status), response.status, responseContext);
33285
+ this.report(metadata, error);
33030
33286
  response.body?.cancel().catch(() => {});
33031
33287
  throw error;
33032
33288
  }
33033
- async readResponseText(response, responseContext, metadata, timeout) {
33289
+ async readText(response, responseContext, metadata, timeout) {
33034
33290
  try {
33035
- return await Promise.race([this.readBoundedResponse(response, responseContext), timeout.promise]);
33291
+ return await Promise.race([this.read(response, responseContext), timeout.promise]);
33036
33292
  } catch (cause) {
33037
33293
  let error;
33038
33294
  if (cause instanceof WABClientError) error = cause;
@@ -33040,17 +33296,17 @@ var WABTransport = class {
33040
33296
  ...responseContext,
33041
33297
  cause
33042
33298
  });
33043
- else error = new WABClientError("WAB_INVALID_RESPONSE", "WAB response could not be read.", true, response.status, {
33299
+ else error = new WABClientError("WAB_INVALID_RESPONSE", "WAB response read failed.", true, response.status, {
33044
33300
  ...responseContext,
33045
33301
  cause
33046
33302
  });
33047
- this.captureRequestFailure(metadata, error);
33303
+ this.report(metadata, error);
33048
33304
  throw error;
33049
33305
  } finally {
33050
33306
  if (timeout.timer !== void 0) clearTimeout(timeout.timer);
33051
33307
  }
33052
33308
  }
33053
- parseResponseObject(responseText, response, responseContext, metadata) {
33309
+ parse(responseText, response, responseContext, metadata) {
33054
33310
  let parsed;
33055
33311
  try {
33056
33312
  parsed = JSON.parse(responseText);
@@ -33059,37 +33315,32 @@ var WABTransport = class {
33059
33315
  ...responseContext,
33060
33316
  cause
33061
33317
  });
33062
- this.captureRequestFailure(metadata, error);
33318
+ this.report(metadata, error);
33063
33319
  throw error;
33064
33320
  }
33065
33321
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
33066
33322
  const error = new WABClientError("WAB_INVALID_RESPONSE", "WAB response must be a JSON object.", true, response.status, responseContext);
33067
- this.captureRequestFailure(metadata, error);
33323
+ this.report(metadata, error);
33068
33324
  throw error;
33069
33325
  }
33070
33326
  return parsed;
33071
33327
  }
33072
- captureRequestFailure(metadata, error) {
33073
- this.captureFailure(metadata.operation, metadata.method, metadata.path, metadata.correlationId, metadata.startedAt, error);
33074
- }
33075
- async readBoundedResponse(response, responseContext) {
33076
- await this.rejectOversizedDeclaredResponse(response, responseContext);
33077
- const reader = response.body?.getReader();
33078
- if (reader == null) return await this.readBoundedArrayBuffer(response, responseContext);
33079
- return await this.readBoundedStream(reader, response, responseContext);
33080
- }
33081
- async rejectOversizedDeclaredResponse(response, responseContext) {
33328
+ async read(response, responseContext) {
33082
33329
  const contentLength = Number(response.headers.get("content-length"));
33083
- if (!Number.isFinite(contentLength) || contentLength <= this.maxResponseBytes) return;
33084
- await this.cancelResponseBody(response);
33085
- throw this.responseTooLargeError(response, responseContext);
33330
+ if (Number.isFinite(contentLength) && contentLength > this.responseLimit) {
33331
+ await this.stopBody(response);
33332
+ throw this.sizeError(response, responseContext);
33333
+ }
33334
+ const reader = response.body?.getReader();
33335
+ if (reader == null) return this.readBuffer(response, responseContext);
33336
+ return this.readStream(reader, response, responseContext);
33086
33337
  }
33087
- async readBoundedArrayBuffer(response, responseContext) {
33338
+ async readBuffer(response, responseContext) {
33088
33339
  const bytes = new Uint8Array(await response.arrayBuffer());
33089
- if (bytes.byteLength > this.maxResponseBytes) throw this.responseTooLargeError(response, responseContext);
33340
+ if (bytes.byteLength > this.responseLimit) throw this.sizeError(response, responseContext);
33090
33341
  return new TextDecoder().decode(bytes);
33091
33342
  }
33092
- async readBoundedStream(reader, response, responseContext) {
33343
+ async readStream(reader, response, responseContext) {
33093
33344
  const chunks = [];
33094
33345
  let total = 0;
33095
33346
  while (true) {
@@ -33097,15 +33348,12 @@ var WABTransport = class {
33097
33348
  if (done) break;
33098
33349
  if (value == null) continue;
33099
33350
  total += value.byteLength;
33100
- if (total > this.maxResponseBytes) {
33101
- await this.cancelResponseReader(reader);
33102
- throw this.responseTooLargeError(response, responseContext);
33351
+ if (total > this.responseLimit) {
33352
+ await this.stopReader(reader);
33353
+ throw this.sizeError(response, responseContext);
33103
33354
  }
33104
33355
  chunks.push(value);
33105
33356
  }
33106
- return this.decodeChunks(chunks, total);
33107
- }
33108
- decodeChunks(chunks, total) {
33109
33357
  const bytes = new Uint8Array(total);
33110
33358
  let offset = 0;
33111
33359
  for (const chunk of chunks) {
@@ -33114,35 +33362,35 @@ var WABTransport = class {
33114
33362
  }
33115
33363
  return new TextDecoder().decode(bytes);
33116
33364
  }
33117
- responseTooLargeError(response, responseContext) {
33118
- return new WABClientError("WAB_RESPONSE_TOO_LARGE", "WAB response exceeded the configured size limit.", false, response.status, responseContext);
33365
+ sizeError(response, responseContext) {
33366
+ return new WABClientError("WAB_RESPONSE_TOO_LARGE", "WAB response exceeds its size limit.", false, response.status, responseContext);
33119
33367
  }
33120
- async cancelResponseBody(response) {
33368
+ async stopBody(response) {
33121
33369
  try {
33122
33370
  await response.body?.cancel();
33123
33371
  } catch {}
33124
33372
  }
33125
- async cancelResponseReader(reader) {
33373
+ async stopReader(reader) {
33126
33374
  try {
33127
33375
  await reader.cancel();
33128
33376
  } catch {}
33129
33377
  }
33130
- captureFailure(operation, method, path, correlationId, startedAt, error) {
33378
+ report(metadata, error) {
33131
33379
  this.telemetry.capture({
33132
- name: "wallet-toolbox.wab.request.failed",
33133
- component: "wallet-toolbox.wab-transport",
33380
+ name: `${WAB_REQUEST_EVENT}failed`,
33381
+ component: WAB_COMPONENT,
33134
33382
  severity: error.retryable ? "warn" : "error",
33135
- correlationId,
33383
+ correlationId: metadata.correlationId,
33136
33384
  attributes: {
33137
- operation,
33138
- method,
33139
- route: path,
33385
+ operation: metadata.operation,
33386
+ method: metadata.method,
33387
+ route: metadata.path,
33140
33388
  serverOrigin: this.serverOrigin,
33141
33389
  retryable: error.retryable,
33142
33390
  ...error.status !== void 0 ? { status: error.status } : {},
33143
33391
  ...error.endpointMarkerPresent !== void 0 ? { endpointMarkerPresent: error.endpointMarkerPresent } : {},
33144
33392
  ...error.responseCorrelationMatched !== void 0 ? { responseCorrelationMatched: error.responseCorrelationMatched } : {},
33145
- durationMs: Date.now() - startedAt
33393
+ durationMs: Date.now() - metadata.startedAt
33146
33394
  },
33147
33395
  error
33148
33396
  });
@@ -33251,8 +33499,8 @@ var WABClient = class {
33251
33499
  constructor(serverUrl, options = {}) {
33252
33500
  this.transport = new WABTransport(serverUrl, options);
33253
33501
  }
33254
- async getInfo() {
33255
- return await this.transport.request("/info", {
33502
+ getInfo() {
33503
+ return this.transport.request("/info", {
33256
33504
  method: "GET",
33257
33505
  operation: "get-info"
33258
33506
  });
@@ -33262,15 +33510,15 @@ var WABClient = class {
33262
33510
  }
33263
33511
  async startAuthMethod(authMethod, presentationKey, payload, correlationId) {
33264
33512
  assertHexIdentifier(presentationKey, "presentationKey");
33265
- return await authMethod.startAuth(this.transport.serverUrl, presentationKey, payload, this.transport, correlationId);
33513
+ return authMethod.startAuth(this.transport.serverUrl, presentationKey, payload, this.transport, correlationId);
33266
33514
  }
33267
33515
  async completeAuthMethod(authMethod, presentationKey, payload, correlationId) {
33268
33516
  assertHexIdentifier(presentationKey, "presentationKey");
33269
- return await authMethod.completeAuth(this.transport.serverUrl, presentationKey, payload, this.transport, correlationId);
33517
+ return authMethod.completeAuth(this.transport.serverUrl, presentationKey, payload, this.transport, correlationId);
33270
33518
  }
33271
33519
  async listLinkedMethods(presentationKey) {
33272
33520
  assertHexIdentifier(presentationKey, "presentationKey");
33273
- return await this.transport.request("/user/linkedMethods", {
33521
+ return this.transport.request("/user/linkedMethods", {
33274
33522
  operation: "list-linked-methods",
33275
33523
  body: { presentationKey }
33276
33524
  });
@@ -33278,7 +33526,7 @@ var WABClient = class {
33278
33526
  async unlinkMethod(presentationKey, authMethodId) {
33279
33527
  assertHexIdentifier(presentationKey, "presentationKey");
33280
33528
  if (!Number.isSafeInteger(authMethodId) || authMethodId <= 0) throw new TypeError("authMethodId must be a positive safe integer.");
33281
- return await this.transport.request("/user/unlinkMethod", {
33529
+ return this.transport.request("/user/unlinkMethod", {
33282
33530
  operation: "unlink-method",
33283
33531
  body: {
33284
33532
  presentationKey,
@@ -33288,14 +33536,14 @@ var WABClient = class {
33288
33536
  }
33289
33537
  async requestFaucet(presentationKey) {
33290
33538
  assertHexIdentifier(presentationKey, "presentationKey");
33291
- return await this.transport.request("/faucet/request", {
33539
+ return this.transport.request("/faucet/request", {
33292
33540
  operation: "request-faucet",
33293
33541
  body: { presentationKey }
33294
33542
  });
33295
33543
  }
33296
33544
  async deleteUser(presentationKey) {
33297
33545
  assertHexIdentifier(presentationKey, "presentationKey");
33298
- return await this.transport.request("/user/delete", {
33546
+ return this.transport.request("/user/delete", {
33299
33547
  operation: "delete-user",
33300
33548
  body: { presentationKey }
33301
33549
  });
@@ -33304,7 +33552,7 @@ var WABClient = class {
33304
33552
  assertMethodType(methodType);
33305
33553
  assertHexIdentifier(userIdHash, "userIdHash");
33306
33554
  const normalizedPayload = normalizeAuthPayload(methodType, payload);
33307
- return await this.transport.request("/auth/start", {
33555
+ return this.transport.request("/auth/start", {
33308
33556
  operation: "start-share-auth",
33309
33557
  body: {
33310
33558
  methodType,
@@ -33317,7 +33565,7 @@ var WABClient = class {
33317
33565
  assertMethodType(methodType);
33318
33566
  assertHexIdentifier(userIdHash, "userIdHash");
33319
33567
  const normalizedPayload = normalizeAuthPayload(methodType, payload);
33320
- return await this.transport.request("/share/store", {
33568
+ return this.transport.request("/share/store", {
33321
33569
  operation: "store-share",
33322
33570
  body: {
33323
33571
  methodType,
@@ -33331,7 +33579,7 @@ var WABClient = class {
33331
33579
  assertMethodType(methodType);
33332
33580
  assertHexIdentifier(userIdHash, "userIdHash");
33333
33581
  const normalizedPayload = normalizeAuthPayload(methodType, payload);
33334
- return await this.transport.request("/share/retrieve", {
33582
+ return this.transport.request("/share/retrieve", {
33335
33583
  operation: "retrieve-share",
33336
33584
  body: {
33337
33585
  methodType,
@@ -33344,7 +33592,7 @@ var WABClient = class {
33344
33592
  assertMethodType(methodType);
33345
33593
  assertHexIdentifier(userIdHash, "userIdHash");
33346
33594
  const normalizedPayload = normalizeAuthPayload(methodType, payload);
33347
- return await this.transport.request("/share/update", {
33595
+ return this.transport.request("/share/update", {
33348
33596
  operation: "update-share",
33349
33597
  body: {
33350
33598
  methodType,
@@ -33358,7 +33606,7 @@ var WABClient = class {
33358
33606
  assertMethodType(methodType);
33359
33607
  assertHexIdentifier(userIdHash, "userIdHash");
33360
33608
  const normalizedPayload = normalizeAuthPayload(methodType, payload);
33361
- return await this.transport.request("/share/delete", {
33609
+ return this.transport.request("/share/delete", {
33362
33610
  operation: "delete-share-user",
33363
33611
  body: {
33364
33612
  methodType,
@@ -33372,9 +33620,13 @@ var WABClient = class {
33372
33620
  //#region ../src/WalletAuthenticationManager.ts
33373
33621
  const DEFAULT_AUTH_SESSION_TTL_MS = 600 * 1e3;
33374
33622
  const MAX_AUTH_SESSION_TTL_MS = 3600 * 1e3;
33623
+ const AUTH_COMPONENT = "wallet-toolbox.authentication-manager";
33624
+ const AUTH_EVENT = "wallet-toolbox.authentication.";
33625
+ const EXISTING_USER = "existing-user";
33626
+ const NEW_USER = "new-user";
33375
33627
  var WABAccountContinuityError = class extends Error {
33376
33628
  code = "WERR_WAB_ACCOUNT_CONTINUITY";
33377
- constructor(message = "WAB and UMP account state did not agree. Retry or use account recovery.") {
33629
+ constructor(message = "WAB and UMP accounts disagree; retry or recover.") {
33378
33630
  super(message);
33379
33631
  this.name = "WABAccountContinuityError";
33380
33632
  }
@@ -33389,6 +33641,7 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
33389
33641
  wabClient;
33390
33642
  authMethod;
33391
33643
  authSession;
33644
+ phoneChangeSession;
33392
33645
  authSessionTtlMs;
33393
33646
  constructor(...[adminOriginator, walletBuilder, interactor, recoveryKeySaver, passwordRetriever, wabClient, authMethod, stateSnapshot, options = {}]) {
33394
33647
  super(adminOriginator, walletBuilder, interactor, recoveryKeySaver, passwordRetriever, async (presentationKey, wallet, adminOriginator) => {
@@ -33411,7 +33664,7 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
33411
33664
  description: "Fund wallet",
33412
33665
  options: { acceptDelayedBroadcast: false }
33413
33666
  }, adminOriginator);
33414
- if (faucetRedeemTXCreationResult.signableTransaction == null) throw new Error("Faucet redemption did not return a signableTransaction");
33667
+ if (faucetRedeemTXCreationResult.signableTransaction == null) throw new Error("Faucet redemption was not signable.");
33415
33668
  const faucetRedeemTX = _bsv_sdk.Transaction.fromAtomicBEEF(faucetRedeemTXCreationResult.signableTransaction.tx);
33416
33669
  const faucetRedemptionPuzzle = new _bsv_sdk.RPuzzle();
33417
33670
  const randomRedemptionPrivateKey = _bsv_sdk.PrivateKey.fromRandom();
@@ -33444,7 +33697,7 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
33444
33697
  * using the chosen AuthMethodInteractor.
33445
33698
  */
33446
33699
  async startAuth(payload) {
33447
- if (this.authMethod == null) throw new Error("No AuthMethod selected in WalletAuthenticationManager");
33700
+ if (this.authMethod == null) throw new Error("No WAB authentication method selected.");
33448
33701
  const authMethod = this.authMethod;
33449
33702
  if (this.authenticated) throw new Error("User is already authenticated");
33450
33703
  this.cancelAuth();
@@ -33457,8 +33710,8 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
33457
33710
  ...correlationId !== void 0 ? { correlationId } : {}
33458
33711
  };
33459
33712
  this.telemetry.capture({
33460
- name: "wallet-toolbox.authentication.wab-start.started",
33461
- component: "wallet-toolbox.authentication-manager",
33713
+ name: `${AUTH_EVENT}wab-start.started`,
33714
+ component: AUTH_COMPONENT,
33462
33715
  severity: "debug",
33463
33716
  correlationId,
33464
33717
  attributes: { methodType: authMethod.methodType }
@@ -33470,8 +33723,8 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
33470
33723
  throw new Error(message);
33471
33724
  }
33472
33725
  this.telemetry.capture({
33473
- name: "wallet-toolbox.authentication.wab-start.completed",
33474
- component: "wallet-toolbox.authentication-manager",
33726
+ name: `${AUTH_EVENT}wab-start.completed`,
33727
+ component: AUTH_COMPONENT,
33475
33728
  severity: "info",
33476
33729
  correlationId,
33477
33730
  attributes: { methodType: authMethod.methodType }
@@ -33479,8 +33732,8 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
33479
33732
  } catch (error) {
33480
33733
  this.cancelAuth();
33481
33734
  this.telemetry.capture({
33482
- name: "wallet-toolbox.authentication.wab-start.failed",
33483
- component: "wallet-toolbox.authentication-manager",
33735
+ name: `${AUTH_EVENT}wab-start.failed`,
33736
+ component: AUTH_COMPONENT,
33484
33737
  severity: "warn",
33485
33738
  correlationId,
33486
33739
  attributes: { methodType: authMethod.methodType },
@@ -33493,22 +33746,22 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
33493
33746
  * Completes the WAB-based flow, retrieving the final presentationKey from WAB if successful.
33494
33747
  */
33495
33748
  async completeAuth(payload) {
33496
- if (this.authMethod == null || this.authSession == null) throw new Error("No AuthMethod selected in WalletAuthenticationManager or startAuth has yet to be called.");
33749
+ if (this.authMethod == null || this.authSession == null) throw new Error("Start WAB authentication first.");
33497
33750
  const authMethod = this.authMethod;
33498
33751
  if (this.authSession.methodType !== authMethod.methodType) {
33499
33752
  this.cancelAuth();
33500
- throw new Error("The selected authentication method changed. Start authentication again.");
33753
+ throw new Error("WAB authentication method changed; restart.");
33501
33754
  }
33502
33755
  if (Date.now() >= this.authSession.expiresAt) {
33503
33756
  this.cancelAuth();
33504
- throw new Error("The WAB authentication session expired. Start authentication again.");
33757
+ throw new Error("WAB authentication expired; restart.");
33505
33758
  }
33506
33759
  const session = this.authSession;
33507
33760
  const result = await this.wabClient.completeAuthMethod(authMethod, session.presentationKey, payload, session.correlationId);
33508
33761
  if (result.success !== true || result.presentationKey == null || result.presentationKey.length === 0) {
33509
33762
  this.telemetry.capture({
33510
- name: "wallet-toolbox.authentication.wab-complete.rejected",
33511
- component: "wallet-toolbox.authentication-manager",
33763
+ name: `${AUTH_EVENT}wab-complete.rejected`,
33764
+ component: AUTH_COMPONENT,
33512
33765
  severity: "warn",
33513
33766
  correlationId: session.correlationId,
33514
33767
  attributes: { methodType: session.methodType }
@@ -33523,11 +33776,11 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
33523
33776
  this.cancelAuth();
33524
33777
  const wabAccountStatus = this.inferAccountStatus(result, session.presentationKey);
33525
33778
  try {
33526
- await this.providePresentationKey(_bsv_sdk.Utils.toArray(result.presentationKey, "hex"));
33779
+ await this.provideWABPresentationKey(result, wabAccountStatus);
33527
33780
  } catch (error) {
33528
33781
  this.telemetry.capture({
33529
- name: "wallet-toolbox.authentication.ump-continuity.failed",
33530
- component: "wallet-toolbox.authentication-manager",
33782
+ name: `${AUTH_EVENT}ump-continuity.failed`,
33783
+ component: AUTH_COMPONENT,
33531
33784
  severity: "warn",
33532
33785
  correlationId: session.correlationId,
33533
33786
  attributes: {
@@ -33538,18 +33791,18 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
33538
33791
  });
33539
33792
  throw error;
33540
33793
  }
33541
- if (wabAccountStatus === "existing-user" && this.authenticationFlow !== "existing-user") {
33794
+ if (wabAccountStatus === EXISTING_USER && this.authenticationFlow !== EXISTING_USER) {
33542
33795
  super.destroy();
33543
33796
  const error = new WABAccountContinuityError();
33544
33797
  this.telemetry.capture({
33545
- name: "wallet-toolbox.authentication.account-continuity.mismatch",
33546
- component: "wallet-toolbox.authentication-manager",
33798
+ name: `${AUTH_EVENT}account-continuity.mismatch`,
33799
+ component: AUTH_COMPONENT,
33547
33800
  severity: "error",
33548
33801
  correlationId: session.correlationId,
33549
33802
  attributes: {
33550
33803
  methodType: session.methodType,
33551
33804
  wabAccountStatus,
33552
- umpAccountStatus: "new-user"
33805
+ umpAccountStatus: NEW_USER
33553
33806
  },
33554
33807
  error
33555
33808
  });
@@ -33557,8 +33810,8 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
33557
33810
  }
33558
33811
  const continuity = wabAccountStatus === this.authenticationFlow ? "matched" : "ump-existing";
33559
33812
  this.telemetry.capture({
33560
- name: "wallet-toolbox.authentication.completed",
33561
- component: "wallet-toolbox.authentication-manager",
33813
+ name: `${AUTH_EVENT}completed`,
33814
+ component: AUTH_COMPONENT,
33562
33815
  severity: continuity === "matched" ? "info" : "warn",
33563
33816
  correlationId: session.correlationId,
33564
33817
  attributes: {
@@ -33572,23 +33825,131 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
33572
33825
  cancelAuth() {
33573
33826
  this.authSession = void 0;
33574
33827
  }
33828
+ readPendingPhoneChange(result) {
33829
+ const presentationKey = result.pendingPresentationKey;
33830
+ const changeId = result.pendingPhoneChangeId;
33831
+ if (presentationKey === void 0 && changeId === void 0) return void 0;
33832
+ if (!/^[0-9a-fA-F]{64}$/.test(presentationKey ?? "") || !Number.isSafeInteger(changeId) || changeId <= 0) throw new WABAccountContinuityError("WAB returned invalid pending phone-change data.");
33833
+ return {
33834
+ presentationKey,
33835
+ changeId
33836
+ };
33837
+ }
33838
+ async provideWABPresentationKey(result, wabAccountStatus) {
33839
+ const umpTokenOutpoint = typeof result.umpTokenOutpoint === "string" ? result.umpTokenOutpoint : void 0;
33840
+ const lookupOptions = umpTokenOutpoint == null ? void 0 : { pinnedOutpoint: umpTokenOutpoint };
33841
+ const pending = this.readPendingPhoneChange(result);
33842
+ let usePending = false;
33843
+ try {
33844
+ await this.providePresentationKey(_bsv_sdk.Utils.toArray(result.presentationKey, "hex"), lookupOptions);
33845
+ } catch (error) {
33846
+ if (pending == null) throw error;
33847
+ usePending = true;
33848
+ }
33849
+ if (pending != null && (usePending || wabAccountStatus === EXISTING_USER && this.authenticationFlow !== EXISTING_USER)) {
33850
+ await this.providePresentationKey(_bsv_sdk.Utils.toArray(pending.presentationKey, "hex"), lookupOptions);
33851
+ if (this.authenticationFlow === EXISTING_USER) await this.finalizePendingPhoneChange(result.presentationKey, pending);
33852
+ }
33853
+ }
33854
+ async finalizePendingPhoneChange(currentPresentationKey, pending) {
33855
+ const finalized = await this.phoneChange("finalize", {
33856
+ changeId: pending.changeId,
33857
+ presentationKey: currentPresentationKey,
33858
+ newPresentationKey: pending.presentationKey
33859
+ });
33860
+ if (finalized.success !== true || finalized.changeId !== pending.changeId) throw new WABAccountContinuityError(finalized.message || "WAB could not finalize the pending phone change.");
33861
+ }
33862
+ /**
33863
+ * Starts OTP verification for a replacement phone number. The same number
33864
+ * is valid and intentionally produces a fresh presentation key/hash.
33865
+ */
33866
+ async startPhoneNumberChange(phoneNumber) {
33867
+ if (!this.authenticated) throw new Error("Not authenticated");
33868
+ const normalizedPhone = phoneNumber.trim();
33869
+ const currentPresentationKey = _bsv_sdk.Utils.toHex(await this.getFactor("presentationKey"));
33870
+ const response = await this.phoneChange("start", {
33871
+ presentationKey: currentPresentationKey,
33872
+ phoneNumber: normalizedPhone
33873
+ });
33874
+ if (response.success !== true) throw new Error(response.message || "Phone change failed");
33875
+ this.phoneChangeSession = {
33876
+ phoneNumber: normalizedPhone,
33877
+ presentationKey: currentPresentationKey
33878
+ };
33879
+ }
33880
+ /**
33881
+ * Completes phone verification and stages the WAB association before
33882
+ * publishing the UMP key rotation. WAB retains both the current and pending
33883
+ * presentation keys until finalization, so either side of an interrupted
33884
+ * transition remains recoverable on the next verified login.
33885
+ */
33886
+ async completePhoneNumberChange(otp) {
33887
+ const session = this.phoneChangeSession;
33888
+ if (session == null) throw new Error("No phone change");
33889
+ if (session.changeToken == null) {
33890
+ const authorization = await this.phoneChange("complete", {
33891
+ presentationKey: session.presentationKey,
33892
+ phoneNumber: session.phoneNumber,
33893
+ otp: otp.trim()
33894
+ });
33895
+ if (authorization.success !== true) throw new Error(authorization.message || "Phone change failed");
33896
+ if (/^[0-9a-fA-F]{64}$/.test(authorization.pendingPresentationKey ?? "") && Number.isSafeInteger(authorization.pendingPhoneChangeId) && authorization.pendingPhoneChangeId > 0) {
33897
+ session.newKey = _bsv_sdk.Utils.toArray(authorization.pendingPresentationKey, "hex");
33898
+ session.changeId = authorization.pendingPhoneChangeId;
33899
+ } else if (typeof authorization.changeToken === "string" && authorization.changeToken.length > 0) session.changeToken = authorization.changeToken;
33900
+ else throw new Error(authorization.message || "Phone change failed");
33901
+ }
33902
+ session.newKey ??= (0, _bsv_sdk.Random)(32);
33903
+ if (session.changeId == null) {
33904
+ const committed = await this.phoneChange("commit", {
33905
+ changeToken: session.changeToken,
33906
+ presentationKey: session.presentationKey,
33907
+ newPresentationKey: _bsv_sdk.Utils.toHex(session.newKey)
33908
+ });
33909
+ if (committed.success !== true || !Number.isSafeInteger(committed.changeId) || committed.changeId <= 0) throw new Error(committed.message || "Phone change failed");
33910
+ session.changeId = committed.changeId;
33911
+ }
33912
+ const changeId = session.changeId;
33913
+ if (session.umpUpdated !== true) {
33914
+ await this.changePresentationKey(session.newKey);
33915
+ session.umpUpdated = true;
33916
+ }
33917
+ const finalized = await this.phoneChange("finalize", {
33918
+ changeId,
33919
+ presentationKey: session.presentationKey,
33920
+ newPresentationKey: _bsv_sdk.Utils.toHex(session.newKey)
33921
+ });
33922
+ if (finalized.success !== true || finalized.changeId !== changeId) throw new Error(finalized.message || "Phone change failed");
33923
+ this.phoneChangeSession = void 0;
33924
+ return { changeId };
33925
+ }
33926
+ cancelPhoneNumberChange() {
33927
+ this.phoneChangeSession = void 0;
33928
+ }
33575
33929
  destroy() {
33576
33930
  this.cancelAuth();
33931
+ this.cancelPhoneNumberChange();
33577
33932
  super.destroy();
33578
33933
  }
33934
+ phoneChange(phase, body) {
33935
+ return this.wabClient.transport.request(`/auth/phone-change/${phase}`, {
33936
+ operation: "phone-change",
33937
+ body
33938
+ });
33939
+ }
33579
33940
  inferAccountStatus(result, temporaryPresentationKey) {
33580
33941
  if (result.presentationKey == null) throw new WABAccountContinuityError("WAB did not return a presentation key.");
33581
33942
  const keyMatchesTemporary = this.constantTimeHexEqual(result.presentationKey, temporaryPresentationKey);
33582
33943
  const rawAccountStatus = result.accountStatus;
33583
- if (rawAccountStatus !== void 0 && rawAccountStatus !== "new-user" && rawAccountStatus !== "existing-user") throw new WABAccountContinuityError("WAB returned an invalid account-continuity status.");
33944
+ if (rawAccountStatus !== void 0 && rawAccountStatus !== NEW_USER && rawAccountStatus !== EXISTING_USER) throw new WABAccountContinuityError("WAB returned an invalid account status.");
33584
33945
  const rawExistingUser = result.existingUser;
33585
- if (rawExistingUser !== void 0 && typeof rawExistingUser !== "boolean") throw new WABAccountContinuityError("WAB returned an invalid existing-user status.");
33586
- if (rawAccountStatus !== void 0 && rawExistingUser !== void 0 && rawAccountStatus === "existing-user" !== rawExistingUser) throw new WABAccountContinuityError("WAB returned contradictory account-continuity statuses.");
33946
+ if (rawExistingUser !== void 0 && typeof rawExistingUser !== "boolean") throw new WABAccountContinuityError("WAB returned invalid existing-user data.");
33947
+ if (rawAccountStatus !== void 0 && rawExistingUser !== void 0 && rawAccountStatus === EXISTING_USER !== rawExistingUser) throw new WABAccountContinuityError("WAB returned conflicting account status.");
33587
33948
  let compatibilityStatus;
33588
- if (typeof rawExistingUser === "boolean") compatibilityStatus = rawExistingUser ? "existing-user" : "new-user";
33949
+ if (typeof rawExistingUser === "boolean") compatibilityStatus = rawExistingUser ? EXISTING_USER : NEW_USER;
33589
33950
  const explicitStatus = rawAccountStatus ?? compatibilityStatus;
33590
- if (explicitStatus === "new-user" && !keyMatchesTemporary || explicitStatus === "existing-user" && keyMatchesTemporary) throw new WABAccountContinuityError("WAB returned contradictory account-continuity data.");
33591
- return explicitStatus ?? (keyMatchesTemporary ? "new-user" : "existing-user");
33951
+ if (explicitStatus === NEW_USER && !keyMatchesTemporary || explicitStatus === EXISTING_USER && keyMatchesTemporary) throw new WABAccountContinuityError("WAB returned conflicting account status.");
33952
+ return explicitStatus ?? (keyMatchesTemporary ? NEW_USER : EXISTING_USER);
33592
33953
  }
33593
33954
  constantTimeHexEqual(left, right) {
33594
33955
  if (left.length !== right.length) return false;
@@ -36828,6 +37189,7 @@ exports.BHServiceClient = BHServiceClient;
36828
37189
  exports.BRC153_REFERENCE_PREFIX = BRC153_REFERENCE_PREFIX;
36829
37190
  exports.BulkFileDataManager = BulkFileDataManager;
36830
37191
  exports.BulkFileDataReader = BulkFileDataReader;
37192
+ exports.BulkFileDataValidationError = BulkFileDataValidationError;
36831
37193
  exports.BulkFilesReader = BulkFilesReader;
36832
37194
  exports.BulkFilesReaderFs = BulkFilesReaderFs;
36833
37195
  exports.BulkFilesReaderStorage = BulkFilesReaderStorage;
@@ -36876,6 +37238,7 @@ exports.EntityUser = EntityUser;
36876
37238
  exports.FixedWindowBulkFileDownloadBudget = FixedWindowBulkFileDownloadBudget;
36877
37239
  exports.GoChaintracksServiceClient = GoChaintracksServiceClient;
36878
37240
  exports.HeightRange = HeightRange;
37241
+ exports.InlineBulkFileDataValidator = InlineBulkFileDataValidator;
36879
37242
  exports.KDF_MAX_HASH_LENGTH = KDF_MAX_HASH_LENGTH;
36880
37243
  exports.LEGACY_MANAGED_CHANGE_MINIMUM_SATOSHIS = LEGACY_MANAGED_CHANGE_MINIMUM_SATOSHIS;
36881
37244
  exports.LiveIngestorBase = LiveIngestorBase;