@orbinum/sdk 1.3.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -4515,9 +4515,9 @@ declare function windowSizeForCounter(counter: number): number;
4515
4515
  *
4516
4516
  * The expensive part (one ECDH per memo) runs in the injected decrypt pool —
4517
4517
  * Web Workers when the host provides a factory, a yielded loop otherwise — while
4518
- * the cheap bookkeeping (cursor, on-chain set, counters) stays here. The next
4519
- * chunk/page is prefetched while the current one decrypts, overlapping network
4520
- * and CPU.
4518
+ * the cheap bookkeeping (cursor, on-chain set, counters) stays here. Both
4519
+ * transports download through the same PREFETCH-deep sliding window
4520
+ * (`runPrefetched`), overlapping network and CPU.
4521
4521
  */
4522
4522
 
4523
4523
  /**
@@ -4552,8 +4552,11 @@ interface ScanOutcome {
4552
4552
  onChainHexes: Set<string>;
4553
4553
  /** Highest leafIndex seen — the next incremental scan resumes after it. */
4554
4554
  maxLeafIndex: number | undefined;
4555
+ /** Hints walked this call, across both transports. */
4555
4556
  scanned: number;
4557
+ /** Notes recovered that the vault did not hold yet. */
4556
4558
  found: number;
4559
+ /** Hints carrying no memo or ephemeral key — nothing to decrypt. */
4557
4560
  noMemo: number;
4558
4561
  /**
4559
4562
  * Hints whose memo did not open with this wallet's keys.
@@ -4564,6 +4567,7 @@ interface ScanOutcome {
4564
4567
  * recovers nothing while this equals the whole pool has a key problem.
4565
4568
  */
4566
4569
  decryptFailed: number;
4570
+ /** Notes recovered that the vault already held. */
4567
4571
  alreadyPresent: number;
4568
4572
  /** Hints discarded by the view-tag fast filter (no AEAD decrypt attempted). */
4569
4573
  tagFiltered: number;
package/dist/index.d.ts CHANGED
@@ -4515,9 +4515,9 @@ declare function windowSizeForCounter(counter: number): number;
4515
4515
  *
4516
4516
  * The expensive part (one ECDH per memo) runs in the injected decrypt pool —
4517
4517
  * Web Workers when the host provides a factory, a yielded loop otherwise — while
4518
- * the cheap bookkeeping (cursor, on-chain set, counters) stays here. The next
4519
- * chunk/page is prefetched while the current one decrypts, overlapping network
4520
- * and CPU.
4518
+ * the cheap bookkeeping (cursor, on-chain set, counters) stays here. Both
4519
+ * transports download through the same PREFETCH-deep sliding window
4520
+ * (`runPrefetched`), overlapping network and CPU.
4521
4521
  */
4522
4522
 
4523
4523
  /**
@@ -4552,8 +4552,11 @@ interface ScanOutcome {
4552
4552
  onChainHexes: Set<string>;
4553
4553
  /** Highest leafIndex seen — the next incremental scan resumes after it. */
4554
4554
  maxLeafIndex: number | undefined;
4555
+ /** Hints walked this call, across both transports. */
4555
4556
  scanned: number;
4557
+ /** Notes recovered that the vault did not hold yet. */
4556
4558
  found: number;
4559
+ /** Hints carrying no memo or ephemeral key — nothing to decrypt. */
4557
4560
  noMemo: number;
4558
4561
  /**
4559
4562
  * Hints whose memo did not open with this wallet's keys.
@@ -4564,6 +4567,7 @@ interface ScanOutcome {
4564
4567
  * recovers nothing while this equals the whole pool has a key problem.
4565
4568
  */
4566
4569
  decryptFailed: number;
4570
+ /** Notes recovered that the vault already held. */
4567
4571
  alreadyPresent: number;
4568
4572
  /** Hints discarded by the view-tag fast filter (no AEAD decrypt attempted). */
4569
4573
  tagFiltered: number;
package/dist/index.js CHANGED
@@ -5880,6 +5880,7 @@ var VaultStore = class {
5880
5880
 
5881
5881
  // src/wallet/scanner/phases/collect.ts
5882
5882
  var PAGE_SIZE = 2500;
5883
+ var PREFETCH = 3;
5883
5884
  async function processHints(ctx, hints, total) {
5884
5885
  const { outcome } = ctx;
5885
5886
  const toDecrypt = [];
@@ -5927,6 +5928,24 @@ async function processHints(ctx, hints, total) {
5927
5928
  await ctx.onBatchDone?.(outcome.maxLeafIndex);
5928
5929
  ctx.onProgress?.({ scanned: outcome.scanned, total, found: outcome.found });
5929
5930
  }
5931
+ async function runPrefetched(count, fetchItem, signal, handle) {
5932
+ const inFlight = /* @__PURE__ */ new Map();
5933
+ const ensureFetching = (i) => {
5934
+ if (i < count && !inFlight.has(i)) inFlight.set(i, fetchItem(i));
5935
+ };
5936
+ try {
5937
+ for (let i = 0; i < count; i++) {
5938
+ if (signal?.aborted) throw scanAbortError();
5939
+ for (let n = i; n < i + PREFETCH; n++) ensureFetching(n);
5940
+ const item = await inFlight.get(i);
5941
+ inFlight.delete(i);
5942
+ await handle(item);
5943
+ }
5944
+ } finally {
5945
+ for (const pending of inFlight.values()) pending.catch(() => {
5946
+ });
5947
+ }
5948
+ }
5930
5949
  async function processSealedChunks(ctx, source, sinceLeafIndex) {
5931
5950
  if (!source.chunks) return null;
5932
5951
  const manifest = await source.chunks.manifest().catch(() => null);
@@ -5939,27 +5958,36 @@ async function processSealedChunks(ctx, source, sinceLeafIndex) {
5939
5958
  if (pending.length === 0) return null;
5940
5959
  const total = Math.max(manifest.total - startLeaf, 0);
5941
5960
  const fetchChunk = (i) => source.chunks.chunk(pending[i].idx, pending[i].digest);
5942
- let nextChunk = fetchChunk(0);
5943
- try {
5944
- for (let i = 0; i < pending.length; i++) {
5945
- if (ctx.signal?.aborted) throw scanAbortError();
5946
- const hints = await nextChunk;
5947
- nextChunk = i + 1 < pending.length ? fetchChunk(i + 1) : null;
5948
- await processHints(
5961
+ await runPrefetched(
5962
+ pending.length,
5963
+ fetchChunk,
5964
+ ctx.signal,
5965
+ (hints) => (
5966
+ // The first chunk may straddle the cursor drop already-scanned leaves.
5967
+ processHints(
5949
5968
  ctx,
5950
5969
  hints.filter((h) => (h.leafIndex ?? 0) >= startLeaf),
5951
5970
  total
5952
- );
5953
- }
5954
- } finally {
5955
- nextChunk?.catch(() => {
5956
- });
5957
- }
5971
+ )
5972
+ )
5973
+ );
5958
5974
  return manifest.lastSealedLeaf + 1;
5959
5975
  }
5976
+ async function processPaginatedTail(ctx, source, sinceLeafIndex) {
5977
+ const fetchPage = (page) => source.listHints({ page, limit: PAGE_SIZE, sinceLeafIndex });
5978
+ const firstPage = await fetchPage(1);
5979
+ const total = ctx.outcome.scanned + firstPage.pagination.total;
5980
+ const effectiveLimit = firstPage.pagination.limit || PAGE_SIZE;
5981
+ const totalPages = Math.max(Math.ceil(firstPage.pagination.total / effectiveLimit), 1);
5982
+ await runPrefetched(
5983
+ totalPages,
5984
+ (i) => i === 0 ? Promise.resolve(firstPage) : fetchPage(i + 1),
5985
+ ctx.signal,
5986
+ (page) => processHints(ctx, page.data, total)
5987
+ );
5988
+ }
5960
5989
  async function collectScanEntries(params) {
5961
- const { source, pool, keys, existingHexes, sinceLeafIndex, onProgress, signal, onPage } = params;
5962
- const { onBatchDone } = params;
5990
+ const { source, sinceLeafIndex, signal } = params;
5963
5991
  if (signal?.aborted) throw scanAbortError();
5964
5992
  const outcome = {
5965
5993
  scanEntries: [],
@@ -5977,14 +6005,14 @@ async function collectScanEntries(params) {
5977
6005
  };
5978
6006
  const ctx = {
5979
6007
  outcome,
5980
- pool,
5981
- keys,
5982
- existingHexes,
5983
- vaultHexes: params.vaultHexes ?? existingHexes,
5984
- onProgress,
6008
+ pool: params.pool,
6009
+ keys: params.keys,
6010
+ existingHexes: params.existingHexes,
6011
+ vaultHexes: params.vaultHexes ?? params.existingHexes,
6012
+ onProgress: params.onProgress,
5985
6013
  signal,
5986
- onPage,
5987
- onBatchDone
6014
+ onPage: params.onPage,
6015
+ onBatchDone: params.onBatchDone
5988
6016
  };
5989
6017
  let tailSince = sinceLeafIndex;
5990
6018
  try {
@@ -5995,33 +6023,7 @@ async function collectScanEntries(params) {
5995
6023
  params.onWarning?.("sealed-chunk path failed; falling back to paginated hints", err);
5996
6024
  if (outcome.maxLeafIndex !== void 0) tailSince = outcome.maxLeafIndex + 1;
5997
6025
  }
5998
- const fetchPage = (page) => source.listHints({ page, limit: PAGE_SIZE, sinceLeafIndex: tailSince });
5999
- const firstPage = await fetchPage(1);
6000
- const total = outcome.scanned + firstPage.pagination.total;
6001
- const effectiveLimit = firstPage.pagination.limit || PAGE_SIZE;
6002
- const totalPages = Math.ceil(firstPage.pagination.total / effectiveLimit);
6003
- const PREFETCH = 3;
6004
- const inFlight = /* @__PURE__ */ new Map();
6005
- const ensureFetching = (page) => {
6006
- if (page >= 2 && page <= totalPages && !inFlight.has(page)) {
6007
- inFlight.set(page, fetchPage(page));
6008
- }
6009
- };
6010
- try {
6011
- for (let p = 2; p < 2 + PREFETCH; p++) ensureFetching(p);
6012
- await processHints(ctx, firstPage.data, total);
6013
- for (let page = 2; page <= totalPages; page++) {
6014
- if (signal?.aborted) throw scanAbortError();
6015
- ensureFetching(page);
6016
- const current = await inFlight.get(page);
6017
- inFlight.delete(page);
6018
- for (let p = page + 1; p <= page + PREFETCH; p++) ensureFetching(p);
6019
- await processHints(ctx, current.data, total);
6020
- }
6021
- } finally {
6022
- for (const pending of inFlight.values()) pending.catch(() => {
6023
- });
6024
- }
6026
+ await processPaginatedTail(ctx, source, tailSince);
6025
6027
  return outcome;
6026
6028
  }
6027
6029
 
package/dist/index.mjs CHANGED
@@ -4654,6 +4654,7 @@ var VaultStore = class {
4654
4654
 
4655
4655
  // src/wallet/scanner/phases/collect.ts
4656
4656
  var PAGE_SIZE = 2500;
4657
+ var PREFETCH = 3;
4657
4658
  async function processHints(ctx, hints, total) {
4658
4659
  const { outcome } = ctx;
4659
4660
  const toDecrypt = [];
@@ -4701,6 +4702,24 @@ async function processHints(ctx, hints, total) {
4701
4702
  await ctx.onBatchDone?.(outcome.maxLeafIndex);
4702
4703
  ctx.onProgress?.({ scanned: outcome.scanned, total, found: outcome.found });
4703
4704
  }
4705
+ async function runPrefetched(count, fetchItem, signal, handle) {
4706
+ const inFlight = /* @__PURE__ */ new Map();
4707
+ const ensureFetching = (i) => {
4708
+ if (i < count && !inFlight.has(i)) inFlight.set(i, fetchItem(i));
4709
+ };
4710
+ try {
4711
+ for (let i = 0; i < count; i++) {
4712
+ if (signal?.aborted) throw scanAbortError();
4713
+ for (let n = i; n < i + PREFETCH; n++) ensureFetching(n);
4714
+ const item = await inFlight.get(i);
4715
+ inFlight.delete(i);
4716
+ await handle(item);
4717
+ }
4718
+ } finally {
4719
+ for (const pending of inFlight.values()) pending.catch(() => {
4720
+ });
4721
+ }
4722
+ }
4704
4723
  async function processSealedChunks(ctx, source, sinceLeafIndex) {
4705
4724
  if (!source.chunks) return null;
4706
4725
  const manifest = await source.chunks.manifest().catch(() => null);
@@ -4713,27 +4732,36 @@ async function processSealedChunks(ctx, source, sinceLeafIndex) {
4713
4732
  if (pending.length === 0) return null;
4714
4733
  const total = Math.max(manifest.total - startLeaf, 0);
4715
4734
  const fetchChunk = (i) => source.chunks.chunk(pending[i].idx, pending[i].digest);
4716
- let nextChunk = fetchChunk(0);
4717
- try {
4718
- for (let i = 0; i < pending.length; i++) {
4719
- if (ctx.signal?.aborted) throw scanAbortError();
4720
- const hints = await nextChunk;
4721
- nextChunk = i + 1 < pending.length ? fetchChunk(i + 1) : null;
4722
- await processHints(
4735
+ await runPrefetched(
4736
+ pending.length,
4737
+ fetchChunk,
4738
+ ctx.signal,
4739
+ (hints) => (
4740
+ // The first chunk may straddle the cursor drop already-scanned leaves.
4741
+ processHints(
4723
4742
  ctx,
4724
4743
  hints.filter((h) => (h.leafIndex ?? 0) >= startLeaf),
4725
4744
  total
4726
- );
4727
- }
4728
- } finally {
4729
- nextChunk?.catch(() => {
4730
- });
4731
- }
4745
+ )
4746
+ )
4747
+ );
4732
4748
  return manifest.lastSealedLeaf + 1;
4733
4749
  }
4750
+ async function processPaginatedTail(ctx, source, sinceLeafIndex) {
4751
+ const fetchPage = (page) => source.listHints({ page, limit: PAGE_SIZE, sinceLeafIndex });
4752
+ const firstPage = await fetchPage(1);
4753
+ const total = ctx.outcome.scanned + firstPage.pagination.total;
4754
+ const effectiveLimit = firstPage.pagination.limit || PAGE_SIZE;
4755
+ const totalPages = Math.max(Math.ceil(firstPage.pagination.total / effectiveLimit), 1);
4756
+ await runPrefetched(
4757
+ totalPages,
4758
+ (i) => i === 0 ? Promise.resolve(firstPage) : fetchPage(i + 1),
4759
+ ctx.signal,
4760
+ (page) => processHints(ctx, page.data, total)
4761
+ );
4762
+ }
4734
4763
  async function collectScanEntries(params) {
4735
- const { source, pool, keys, existingHexes, sinceLeafIndex, onProgress, signal, onPage } = params;
4736
- const { onBatchDone } = params;
4764
+ const { source, sinceLeafIndex, signal } = params;
4737
4765
  if (signal?.aborted) throw scanAbortError();
4738
4766
  const outcome = {
4739
4767
  scanEntries: [],
@@ -4751,14 +4779,14 @@ async function collectScanEntries(params) {
4751
4779
  };
4752
4780
  const ctx = {
4753
4781
  outcome,
4754
- pool,
4755
- keys,
4756
- existingHexes,
4757
- vaultHexes: params.vaultHexes ?? existingHexes,
4758
- onProgress,
4782
+ pool: params.pool,
4783
+ keys: params.keys,
4784
+ existingHexes: params.existingHexes,
4785
+ vaultHexes: params.vaultHexes ?? params.existingHexes,
4786
+ onProgress: params.onProgress,
4759
4787
  signal,
4760
- onPage,
4761
- onBatchDone
4788
+ onPage: params.onPage,
4789
+ onBatchDone: params.onBatchDone
4762
4790
  };
4763
4791
  let tailSince = sinceLeafIndex;
4764
4792
  try {
@@ -4769,33 +4797,7 @@ async function collectScanEntries(params) {
4769
4797
  params.onWarning?.("sealed-chunk path failed; falling back to paginated hints", err);
4770
4798
  if (outcome.maxLeafIndex !== void 0) tailSince = outcome.maxLeafIndex + 1;
4771
4799
  }
4772
- const fetchPage = (page) => source.listHints({ page, limit: PAGE_SIZE, sinceLeafIndex: tailSince });
4773
- const firstPage = await fetchPage(1);
4774
- const total = outcome.scanned + firstPage.pagination.total;
4775
- const effectiveLimit = firstPage.pagination.limit || PAGE_SIZE;
4776
- const totalPages = Math.ceil(firstPage.pagination.total / effectiveLimit);
4777
- const PREFETCH = 3;
4778
- const inFlight = /* @__PURE__ */ new Map();
4779
- const ensureFetching = (page) => {
4780
- if (page >= 2 && page <= totalPages && !inFlight.has(page)) {
4781
- inFlight.set(page, fetchPage(page));
4782
- }
4783
- };
4784
- try {
4785
- for (let p = 2; p < 2 + PREFETCH; p++) ensureFetching(p);
4786
- await processHints(ctx, firstPage.data, total);
4787
- for (let page = 2; page <= totalPages; page++) {
4788
- if (signal?.aborted) throw scanAbortError();
4789
- ensureFetching(page);
4790
- const current = await inFlight.get(page);
4791
- inFlight.delete(page);
4792
- for (let p = page + 1; p <= page + PREFETCH; p++) ensureFetching(p);
4793
- await processHints(ctx, current.data, total);
4794
- }
4795
- } finally {
4796
- for (const pending of inFlight.values()) pending.catch(() => {
4797
- });
4798
- }
4800
+ await processPaginatedTail(ctx, source, tailSince);
4799
4801
  return outcome;
4800
4802
  }
4801
4803
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orbinum/sdk",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "Official TypeScript SDK for Orbinum.",
5
5
  "author": "Orbinum",
6
6
  "license": "MIT",