@miden-sdk/miden-sdk 0.15.0-alpha.5 → 0.15.0-alpha.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/mt/{Cargo-smJQCGjz.js → Cargo-C9UbiAcT.js} +62 -6
  2. package/dist/mt/{Cargo-smJQCGjz.js.map → Cargo-C9UbiAcT.js.map} +1 -1
  3. package/dist/mt/api-types.d.ts +6 -2
  4. package/dist/mt/assets/miden_client_web.wasm +0 -0
  5. package/dist/mt/crates/miden_client_web.d.ts +31 -1
  6. package/dist/mt/eager.js +2 -2
  7. package/dist/mt/index.d.ts +2 -1
  8. package/dist/mt/index.js +299 -266
  9. package/dist/mt/index.js.map +1 -1
  10. package/dist/mt/wasm.js +1 -1
  11. package/dist/mt/workerHelpers.js +1 -1
  12. package/dist/mt/workers/{Cargo-smJQCGjz-q4GYXDiD.js → Cargo-C9UbiAcT-Z344cyB1.js} +62 -6
  13. package/dist/mt/workers/{Cargo-smJQCGjz-q4GYXDiD.js.map → Cargo-C9UbiAcT-Z344cyB1.js.map} +1 -1
  14. package/dist/mt/workers/assets/miden_client_web.wasm +0 -0
  15. package/dist/mt/workers/web-client-methods-worker.js +118 -7
  16. package/dist/mt/workers/web-client-methods-worker.js.map +1 -1
  17. package/dist/mt/workers/web-client-methods-worker.module.js +55 -2
  18. package/dist/mt/workers/web-client-methods-worker.module.js.map +1 -1
  19. package/dist/mt/workers/workerHelpers.js +1 -1
  20. package/dist/st/{Cargo-CG4XszZo.js → Cargo-OZMlHpic.js} +24 -2
  21. package/dist/st/{Cargo-CG4XszZo.js.map → Cargo-OZMlHpic.js.map} +1 -1
  22. package/dist/st/api-types.d.ts +6 -2
  23. package/dist/st/assets/miden_client_web.wasm +0 -0
  24. package/dist/st/crates/miden_client_web.d.ts +15 -1
  25. package/dist/st/eager.js +2 -2
  26. package/dist/st/index.d.ts +2 -1
  27. package/dist/st/index.js +299 -266
  28. package/dist/st/index.js.map +1 -1
  29. package/dist/st/wasm.js +1 -1
  30. package/dist/st/workers/{Cargo-CG4XszZo-S7EHAZSa.js → Cargo-OZMlHpic-DZjvJlWc.js} +24 -2
  31. package/dist/st/workers/{Cargo-CG4XszZo-S7EHAZSa.js.map → Cargo-OZMlHpic-DZjvJlWc.js.map} +1 -1
  32. package/dist/st/workers/assets/miden_client_web.wasm +0 -0
  33. package/dist/st/workers/web-client-methods-worker.js +79 -4
  34. package/dist/st/workers/web-client-methods-worker.js.map +1 -1
  35. package/dist/st/workers/web-client-methods-worker.module.js +55 -2
  36. package/dist/st/workers/web-client-methods-worker.module.js.map +1 -1
  37. package/js/client.js +24 -5
  38. package/js/node/napi-compat.js +7 -3
  39. package/js/resources/transactions.js +4 -1
  40. package/package.json +4 -4
package/dist/mt/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import loadWasm from './wasm.js';
2
- export { Account, AccountArray, AccountBuilder, AccountBuilderResult, AccountCode, AccountComponent, AccountComponentCode, AccountDelta, AccountFile, AccountHeader, AccountId, AccountIdArray, AccountInterface, AccountProof, AccountReader, AccountStatus, AccountStorage, AccountStorageDelta, AccountStorageMode, AccountStorageRequirements, AccountVaultDelta, Address, AdviceInputs, AdviceMap, AssetVault, AuthFalcon512RpoMultisigConfig, AuthSecretKey, BasicFungibleFaucetComponent, BlockHeader, CodeBuilder, CommittedNote, ConsumableNoteRecord, Endpoint, ExecutedTransaction, Felt, FeltArray, FetchedAccount, FetchedNote, FlattenedU8Vec, ForeignAccount, ForeignAccountArray, FungibleAsset, FungibleAssetDelta, FungibleAssetDeltaItem, GetProceduresResultItem, InputNote, InputNoteRecord, InputNoteState, InputNotes, IntoUnderlyingByteSource, IntoUnderlyingSink, IntoUnderlyingSource, JsAccountUpdate, JsStateSyncUpdate, JsStorageMapEntry, JsStorageSlot, JsVaultAsset, Library, MerklePath, NetworkId, NetworkNoteStatusInfo, NetworkType, Note, NoteAndArgs, NoteAndArgsArray, NoteArray, NoteAssets, NoteAttachment, NoteAttachmentScheme, NoteConsumability, NoteConsumptionStatus, NoteDetails, NoteDetailsAndTag, NoteDetailsAndTagArray, NoteExecutionHint, NoteExportFormat, NoteFile, NoteFilter, NoteFilterTypes, NoteHeader, NoteId, NoteIdAndArgs, NoteIdAndArgsArray, NoteInclusionProof, NoteLocation, NoteMetadata, NoteRecipient, NoteRecipientArray, NoteScript, NoteStorage, NoteSyncBlock, NoteSyncInfo, NoteTag, NoteType, OutputNote, OutputNoteArray, OutputNoteRecord, OutputNoteState, OutputNotes, Package, PartialNote, Poseidon2, ProcedureThreshold, Program, ProvenTransaction, PublicKey, RpcClient, Rpo256, SerializedInputNoteData, SerializedOutputNoteData, SerializedTransactionData, Signature, SigningInputs, SigningInputsType, SlotAndKeys, SparseMerklePath, StorageMap, StorageMapEntry, StorageMapEntryJs, StorageMapInfo, StorageMapUpdate, StorageSlot, StorageSlotArray, SyncSummary, TestUtils, TokenSymbol, TransactionArgs, TransactionFilter, TransactionId, TransactionProver, TransactionRecord, TransactionRequest, TransactionRequestBuilder, TransactionResult, TransactionScript, TransactionScriptInputPair, TransactionScriptInputPairArray, TransactionStatus, TransactionStoreUpdate, TransactionSummary, WebClient, WebKeystoreApi, Word, createAuthFalcon512RpoMultisig, exportStore, importStore, initSync, initThreadPool, parallelSumBench, rayonThreadCount, sequentialSumBench, setupLogging, wbg_rayon_PoolBuilder, wbg_rayon_start_worker } from './Cargo-smJQCGjz.js';
2
+ export { Account, AccountArray, AccountBuilder, AccountBuilderResult, AccountCode, AccountComponent, AccountComponentCode, AccountDelta, AccountFile, AccountHeader, AccountId, AccountIdArray, AccountInterface, AccountProof, AccountReader, AccountStatus, AccountStorage, AccountStorageDelta, AccountStorageMode, AccountStorageRequirements, AccountVaultDelta, Address, AdviceInputs, AdviceMap, AssetVault, AuthFalcon512RpoMultisigConfig, AuthSecretKey, BasicFungibleFaucetComponent, BlockHeader, CodeBuilder, CommittedNote, ConsumableNoteRecord, Endpoint, ExecutedTransaction, Felt, FeltArray, FetchedAccount, FetchedNote, FlattenedU8Vec, ForeignAccount, ForeignAccountArray, FungibleAsset, FungibleAssetDelta, FungibleAssetDeltaItem, GetProceduresResultItem, InputNote, InputNoteRecord, InputNoteState, InputNotes, IntoUnderlyingByteSource, IntoUnderlyingSink, IntoUnderlyingSource, JsAccountUpdate, JsStateSyncUpdate, JsStorageMapEntry, JsStorageSlot, JsVaultAsset, Library, MerklePath, NetworkId, NetworkNoteStatusInfo, NetworkType, Note, NoteAndArgs, NoteAndArgsArray, NoteArray, NoteAssets, NoteAttachment, NoteAttachmentScheme, NoteConsumability, NoteConsumptionStatus, NoteDetails, NoteDetailsAndTag, NoteDetailsAndTagArray, NoteExecutionHint, NoteExportFormat, NoteFile, NoteFilter, NoteFilterTypes, NoteHeader, NoteId, NoteIdAndArgs, NoteIdAndArgsArray, NoteInclusionProof, NoteLocation, NoteMetadata, NoteRecipient, NoteRecipientArray, NoteScript, NoteStorage, NoteSyncBlock, NoteSyncInfo, NoteTag, NoteType, OutputNote, OutputNoteArray, OutputNoteRecord, OutputNoteState, OutputNotes, Package, PartialNote, Poseidon2, ProcedureThreshold, Program, ProvenTransaction, PublicKey, RpcClient, Rpo256, SerializedInputNoteData, SerializedOutputNoteData, SerializedTransactionData, Signature, SigningInputs, SigningInputsType, SlotAndKeys, SparseMerklePath, StorageMap, StorageMapEntry, StorageMapEntryJs, StorageMapInfo, StorageMapUpdate, StorageSlot, StorageSlotArray, SyncSummary, TestUtils, TokenSymbol, TransactionArgs, TransactionFilter, TransactionId, TransactionProver, TransactionRecord, TransactionRequest, TransactionRequestBuilder, TransactionResult, TransactionScript, TransactionScriptInputPair, TransactionScriptInputPairArray, TransactionStatus, TransactionStoreUpdate, TransactionSummary, WebClient, WebKeystoreApi, Word, createAuthFalcon512RpoMultisig, exportStore, importStore, initSync, initThreadPool, mtProbeAsync, mtProbeSync, parallelSumBench, rayonThreadCount, sequentialSumBench, setupLogging, wbg_rayon_PoolBuilder, wbg_rayon_start_worker } from './Cargo-C9UbiAcT.js';
3
3
 
4
4
  const WorkerAction = Object.freeze({
5
5
  INIT: "init",
@@ -26,20 +26,24 @@ const MethodName = Object.freeze({
26
26
  SUBMIT_NEW_TRANSACTION_WITH_PROVER_MOCK: "submitNewTransactionWithProverMock",
27
27
  SYNC_STATE: "syncState",
28
28
  SYNC_STATE_MOCK: "syncStateMock",
29
+ SYNC_CHAIN: "syncChain",
30
+ SYNC_CHAIN_MOCK: "syncChainMock",
31
+ SYNC_NOTE_TRANSPORT: "syncNoteTransport",
32
+ SYNC_NOTE_TRANSPORT_MOCK: "syncNoteTransportMock",
29
33
  });
30
34
 
31
35
  /**
32
36
  * Sync Lock Module
33
37
  *
34
- * Provides coordination for concurrent syncState() calls using the Web Locks API
35
- * with an in-process mutex fallback for older browsers.
38
+ * Coordinates concurrent sync calls using the Web Locks API.
36
39
  *
37
40
  * Behavior:
38
- * - Uses "coalescing": if a sync is in progress, subsequent callers wait and receive
39
- * the same result
40
- * - Web Locks for cross-tab coordination (Chrome 69+, Safari 15.4+)
41
- * - In-process mutex fallback when Web Locks unavailable
42
- * - Optional timeout support
41
+ * - Same-method coalescing: if a sync of the same method is in progress,
42
+ * subsequent callers share its result promise
43
+ * - Different-method serialization: different methods (e.g. syncState vs
44
+ * syncNoteTransport) wait for each other via the Web Lock, or via an
45
+ * in-process per-dbId promise chain when Web Locks are unavailable
46
+ * - Web Locks also serialize across tabs (Chrome 69+, Safari 15.4+)
43
47
  */
44
48
 
45
49
  /**
@@ -53,195 +57,86 @@ function hasWebLocks() {
53
57
  );
54
58
  }
55
59
 
56
- /**
57
- * Internal state for tracking in-progress syncs and waiters per database.
58
- */
59
- const syncStates = new Map();
60
+ // Coalesce map keyed by `${dbId}:${methodId}` -> in-flight promise.
61
+ const inFlight = new Map();
60
62
 
61
- /**
62
- * Get or create sync state for a database.
63
- */
64
- function getSyncState(dbId) {
65
- let state = syncStates.get(dbId);
66
- if (!state) {
67
- state = {
68
- inProgress: false,
69
- result: null,
70
- error: null,
71
- waiters: [],
72
- releaseLock: null,
73
- syncGeneration: 0,
74
- };
75
- syncStates.set(dbId, state);
76
- }
77
- return state;
78
- }
63
+ // Per-dbId promise tail used to serialize cross-method calls when Web Locks
64
+ // are unavailable. Each new task chains onto the current tail so different
65
+ // methods on the same dbId run sequentially within the tab.
66
+ const fallbackTails = new Map();
79
67
 
80
68
  /**
81
- * Acquire a sync lock for the given database.
82
- *
83
- * If a sync is already in progress:
84
- * - Returns { acquired: false, coalescedResult } after waiting for the result
85
- *
86
- * If no sync is in progress:
87
- * - Returns { acquired: true } and the caller should perform the sync,
88
- * then call releaseSyncLock() or releaseSyncLockWithError()
69
+ * Build the coalesce-map key for an in-flight sync of `(dbId, methodId)`.
89
70
  *
90
- * @param {string} dbId - The database ID to lock
91
- * @param {number} timeoutMs - Optional timeout in milliseconds (0 = no timeout)
92
- * @returns {Promise<{acquired: boolean, coalescedResult?: any}>}
71
+ * @param {string} dbId
72
+ * @param {string} methodId
73
+ * @returns {string}
93
74
  */
94
- async function acquireSyncLock(dbId, timeoutMs = 0) {
95
- const state = getSyncState(dbId);
96
-
97
- // If a sync is already in progress, wait for it to complete (coalescing)
98
- if (state.inProgress) {
99
- return new Promise((resolve, reject) => {
100
- let timeoutId;
101
- if (timeoutMs > 0) {
102
- timeoutId = setTimeout(() => {
103
- const idx = state.waiters.findIndex((w) => w.resolve === onResult);
104
- if (idx !== -1) {
105
- state.waiters.splice(idx, 1);
106
- }
107
- reject(new Error("Sync lock acquisition timed out"));
108
- }, timeoutMs);
109
- }
110
-
111
- const onResult = (result) => {
112
- /* v8 ignore next 1 -- timeoutId only set when timeoutMs>0 AND another sync is in progress; combo rare in tests */
113
- if (timeoutId) clearTimeout(timeoutId);
114
- resolve({ acquired: false, coalescedResult: result });
115
- };
116
-
117
- const onError = (error) => {
118
- if (timeoutId) clearTimeout(timeoutId);
119
- reject(error);
120
- };
121
-
122
- state.waiters.push({ resolve: onResult, reject: onError });
123
- });
124
- }
125
-
126
- // Mark sync as in progress and increment generation
127
- state.inProgress = true;
128
- state.result = null;
129
- state.error = null;
130
- state.syncGeneration++;
131
- const currentGeneration = state.syncGeneration;
132
-
133
- // Try to acquire Web Lock if available
134
- if (hasWebLocks()) {
135
- const lockName = `miden-sync-${dbId}`;
136
-
137
- return new Promise((resolve, reject) => {
138
- let timeoutId;
139
- let timedOut = false;
140
-
141
- if (timeoutMs > 0) {
142
- timeoutId = setTimeout(() => {
143
- timedOut = true;
144
- if (state.syncGeneration === currentGeneration) {
145
- state.inProgress = false;
146
- const error = new Error("Sync lock acquisition timed out");
147
- for (const waiter of state.waiters) {
148
- waiter.reject(error);
149
- }
150
- state.waiters = [];
151
- }
152
- reject(new Error("Sync lock acquisition timed out"));
153
- }, timeoutMs);
154
- }
155
-
156
- navigator.locks
157
- .request(lockName, { mode: "exclusive" }, async () => {
158
- /* v8 ignore next 3 -- race: lock granted after timeout or newer generation */
159
- if (timedOut || state.syncGeneration !== currentGeneration) {
160
- return;
161
- }
162
-
163
- if (timeoutId) clearTimeout(timeoutId);
164
-
165
- return new Promise((releaseLock) => {
166
- state.releaseLock = releaseLock;
167
- resolve({ acquired: true });
168
- });
169
- })
170
- .catch((err) => {
171
- /* v8 ignore next 5 -- catch path requires Web Locks rejection combined with
172
- optional timeout; tested via "rejects when Web Locks request rejects" but
173
- the timeoutId-set branch needs Web Locks + timeout simultaneously */
174
- if (timeoutId) clearTimeout(timeoutId);
175
- if (state.syncGeneration === currentGeneration) {
176
- state.inProgress = false;
177
- }
178
- reject(err instanceof Error ? err : new Error(String(err)));
179
- });
180
- });
181
- } else {
182
- // Fallback: no Web Locks, just use in-process state
183
- return { acquired: true };
184
- }
75
+ function coalesceKey(dbId, methodId) {
76
+ return `${dbId}:${methodId}`;
185
77
  }
186
78
 
187
79
  /**
188
- * Release the sync lock with a successful result.
189
- *
190
- * This notifies all waiting callers with the result and releases the lock.
80
+ * Run `fn` while holding the per-db Web Lock. When Web Locks are unavailable,
81
+ * serializes `fn` against any other in-flight call on the same `dbId` via an
82
+ * in-process promise chain the wasm-bindgen `WebClient` uses a synchronous
83
+ * `RefCell` for interior mutability in the browser, so overlapping
84
+ * cross-method borrows would throw "recursive use of an object detected
85
+ * which would lead to unsafe aliasing in rust".
191
86
  *
192
- * @param {string} dbId - The database ID
193
- * @param {any} result - The sync result to pass to waiters
87
+ * @param {string} dbId
88
+ * @param {() => Promise<T>} fn
89
+ * @returns {Promise<T>}
90
+ * @template T
194
91
  */
195
- function releaseSyncLock(dbId, result) {
196
- const state = getSyncState(dbId);
197
-
198
- if (!state.inProgress) {
199
- console.warn("releaseSyncLock called but no sync was in progress");
200
- return;
201
- }
202
-
203
- state.result = result;
204
- state.inProgress = false;
205
-
206
- for (const waiter of state.waiters) {
207
- waiter.resolve(result);
208
- }
209
- state.waiters = [];
210
-
211
- if (state.releaseLock) {
212
- state.releaseLock();
213
- state.releaseLock = null;
92
+ function runUnderLock(dbId, fn) {
93
+ if (!hasWebLocks()) {
94
+ const prev = fallbackTails.get(dbId) ?? Promise.resolve();
95
+ const next = prev.catch(() => {}).then(fn);
96
+ const guarded = next.catch(() => {});
97
+ fallbackTails.set(dbId, guarded);
98
+ guarded.then(() => {
99
+ // Drop the slot only if no successor chained onto this tail.
100
+ if (fallbackTails.get(dbId) === guarded) fallbackTails.delete(dbId);
101
+ });
102
+ return next;
214
103
  }
104
+ return navigator.locks.request(
105
+ `miden-sync-${dbId}`,
106
+ { mode: "exclusive" },
107
+ fn
108
+ );
215
109
  }
216
110
 
217
111
  /**
218
- * Release the sync lock due to an error.
112
+ * Run `fn` under the sync lock for (dbId, methodId).
219
113
  *
220
- * This notifies all waiting callers that the sync failed.
114
+ * Concurrent calls with the same (dbId, methodId) share the same promise
115
+ * (coalescing). Concurrent calls on the same dbId with different methodIds
116
+ * serialize via the Web Lock.
221
117
  *
222
- * @param {string} dbId - The database ID
223
- * @param {Error} error - The error to pass to waiters
118
+ * @param {string} dbId - Database ID
119
+ * @param {string} methodId - Method identifier (see MethodName constants)
120
+ * @param {() => Promise<T>} fn - Work to run under the lock
121
+ * @returns {Promise<T>}
224
122
  */
225
- function releaseSyncLockWithError(dbId, error) {
226
- const state = getSyncState(dbId);
227
-
228
- if (!state.inProgress) {
229
- console.warn("releaseSyncLockWithError called but no sync was in progress");
230
- return;
231
- }
232
-
233
- state.error = error;
234
- state.inProgress = false;
235
-
236
- for (const waiter of state.waiters) {
237
- waiter.reject(error);
238
- }
239
- state.waiters = [];
240
-
241
- if (state.releaseLock) {
242
- state.releaseLock();
243
- state.releaseLock = null;
244
- }
123
+ function withSyncLock(dbId, methodId, fn) {
124
+ const key = coalesceKey(dbId, methodId);
125
+
126
+ let work = inFlight.get(key);
127
+ if (!work) {
128
+ work = runUnderLock(dbId, fn);
129
+ inFlight.set(key, work);
130
+ // Swallow on the derived promise so a rejection here doesn't surface as
131
+ // an unhandled rejection; the caller still sees the error through `work`.
132
+ work
133
+ .finally(() => {
134
+ if (inFlight.get(key) === work) inFlight.delete(key);
135
+ })
136
+ .catch(() => {});
137
+ }
138
+
139
+ return work;
245
140
  }
246
141
 
247
142
  /**
@@ -1128,7 +1023,10 @@ class TransactionsResource {
1128
1023
  }
1129
1024
 
1130
1025
  try {
1131
- await this.#inner.syncStateWithTimeout(0);
1026
+ // Chain-only sync is sufficient: confirmation only needs on-chain
1027
+ // state, and skipping NTL keeps polling alive when the note
1028
+ // transport endpoint is unavailable.
1029
+ await this.#inner.syncChain();
1132
1030
  } catch {
1133
1031
  // Sync may fail transiently; continue polling
1134
1032
  }
@@ -1945,15 +1843,34 @@ class MidenClient {
1945
1843
  }
1946
1844
 
1947
1845
  /**
1948
- * Syncs the client state with the Miden node.
1846
+ * Syncs the client: fetches private notes from the Note Transport Layer, then syncs on-chain
1847
+ * state with the Miden node. Fails fast on either.
1949
1848
  *
1950
- * @param {object} [opts] - Sync options.
1951
- * @param {number} [opts.timeout] - Timeout in milliseconds (0 = no timeout).
1952
1849
  * @returns {Promise<SyncSummary>} The sync summary.
1953
1850
  */
1954
- async sync(opts) {
1851
+ async sync() {
1852
+ this.assertNotTerminated();
1853
+ return await this.#inner.syncState();
1854
+ }
1855
+
1856
+ /**
1857
+ * Syncs on-chain state only (no NTL fetch).
1858
+ *
1859
+ * @returns {Promise<SyncSummary>}
1860
+ */
1861
+ async syncChain() {
1955
1862
  this.assertNotTerminated();
1956
- return await this.#inner.syncStateWithTimeout(opts?.timeout ?? 0);
1863
+ return await this.#inner.syncChain();
1864
+ }
1865
+
1866
+ /**
1867
+ * Fetches private notes from the Note Transport Layer.
1868
+ *
1869
+ * @returns {Promise<void>}
1870
+ */
1871
+ async syncNoteTransport() {
1872
+ this.assertNotTerminated();
1873
+ return await this.#inner.syncNoteTransport();
1957
1874
  }
1958
1875
 
1959
1876
  /**
@@ -2884,10 +2801,19 @@ class WebClient {
2884
2801
  this.loadedResolver = resolve;
2885
2802
  });
2886
2803
 
2887
- // Create a promise that resolves when the worker signals that it is fully initialized.
2888
- this.ready = new Promise((resolve) => {
2804
+ // Create a promise that resolves when the worker signals that it is
2805
+ // fully initialized, and rejects if initialization fails. Every
2806
+ // worker-forwarded method awaits `ready` first, so an init failure must
2807
+ // reject it — otherwise those calls would await a promise that never
2808
+ // settles and hang forever.
2809
+ this.ready = new Promise((resolve, reject) => {
2889
2810
  this.readyResolver = resolve;
2811
+ this.readyRejecter = reject;
2890
2812
  });
2813
+ // Init can fail before any caller awaits `ready`; this no-op handler
2814
+ // suppresses the unhandledrejection event without consuming the
2815
+ // rejection for real awaiters.
2816
+ this.ready.catch(() => {});
2891
2817
 
2892
2818
  // Listen for messages from the worker.
2893
2819
  this.worker.addEventListener("message", async (event) => {
@@ -2951,6 +2877,21 @@ class WebClient {
2951
2877
  } else {
2952
2878
  resolve(result);
2953
2879
  }
2880
+ return;
2881
+ }
2882
+
2883
+ // An error with no request attached comes from worker initialization
2884
+ // (INIT is the only requestId-less action that can fail). Reject
2885
+ // `ready` so queued and future method calls fail with the real cause
2886
+ // instead of awaiting forever.
2887
+ if (error && !requestId) {
2888
+ const workerError =
2889
+ error instanceof Error ? error : deserializeError(error);
2890
+ console.error(
2891
+ "WebClient: worker initialization failed:",
2892
+ workerError
2893
+ );
2894
+ this.readyRejecter(workerError);
2954
2895
  }
2955
2896
  });
2956
2897
 
@@ -3435,7 +3376,7 @@ class WebClient {
3435
3376
  }
3436
3377
 
3437
3378
  /**
3438
- * Syncs the client state with the node.
3379
+ * Syncs the client (NTL followed by chain sync, failing fast on either).
3439
3380
  *
3440
3381
  * This method coordinates concurrent sync calls using the Web Locks API when available,
3441
3382
  * with an in-process mutex fallback for older browsers. If a sync is already in progress,
@@ -3444,58 +3385,76 @@ class WebClient {
3444
3385
  * @returns {Promise<SyncSummary>} The sync summary
3445
3386
  */
3446
3387
  async syncState() {
3447
- return this.syncStateWithTimeout(0);
3388
+ const dbId = this.storeName || "default";
3389
+ const methodId = MethodName.SYNC_STATE;
3390
+
3391
+ try {
3392
+ return await withSyncLock(dbId, methodId, async () => {
3393
+ if (!this.worker) {
3394
+ const wasmWebClient = await this.getWasmWebClient();
3395
+ return await wasmWebClient.syncStateImpl();
3396
+ }
3397
+ const wasm = await getWasmOrThrow();
3398
+ const serializedSyncSummaryBytes =
3399
+ await this.callMethodWithWorker(methodId);
3400
+ return wasm.SyncSummary.deserialize(
3401
+ new Uint8Array(serializedSyncSummaryBytes)
3402
+ );
3403
+ });
3404
+ } catch (error) {
3405
+ console.error("INDEX.JS: Error in syncState:", error);
3406
+ throw error;
3407
+ }
3448
3408
  }
3449
3409
 
3450
3410
  /**
3451
- * Syncs the client state with the node with an optional timeout.
3411
+ * Fetches private notes from the Note Transport Layer.
3452
3412
  *
3453
- * This method coordinates concurrent sync calls using the Web Locks API when available,
3454
- * with an in-process mutex fallback for older browsers. If a sync is already in progress,
3455
- * subsequent callers will wait and receive the same result (coalescing behavior).
3456
- *
3457
- * @param {number} timeoutMs - Timeout in milliseconds (0 = no timeout)
3458
- * @returns {Promise<SyncSummary>} The sync summary
3413
+ * @returns {Promise<void>}
3459
3414
  */
3460
- async syncStateWithTimeout(timeoutMs = 0) {
3461
- // Use storeName as the database ID for lock coordination
3415
+ async syncNoteTransport() {
3462
3416
  const dbId = this.storeName || "default";
3417
+ const methodId = MethodName.SYNC_NOTE_TRANSPORT;
3463
3418
 
3464
3419
  try {
3465
- // Acquire the sync lock (coordinates concurrent calls)
3466
- const lockHandle = await acquireSyncLock(dbId, timeoutMs);
3467
-
3468
- if (!lockHandle.acquired) {
3469
- // We're coalescing - return the result from the in-progress sync
3470
- return lockHandle.coalescedResult;
3471
- }
3472
-
3473
- // We acquired the lock - perform the sync
3474
- try {
3475
- let result;
3420
+ await withSyncLock(dbId, methodId, async () => {
3476
3421
  if (!this.worker) {
3477
3422
  const wasmWebClient = await this.getWasmWebClient();
3478
- result = await wasmWebClient.syncStateImpl();
3423
+ await wasmWebClient.syncNoteTransportImpl();
3479
3424
  } else {
3480
- const wasm = await getWasmOrThrow();
3481
- const serializedSyncSummaryBytes = await this.callMethodWithWorker(
3482
- MethodName.SYNC_STATE
3483
- );
3484
- result = wasm.SyncSummary.deserialize(
3485
- new Uint8Array(serializedSyncSummaryBytes)
3486
- );
3425
+ await this.callMethodWithWorker(methodId);
3487
3426
  }
3427
+ });
3428
+ } catch (error) {
3429
+ console.error("INDEX.JS: Error in syncNoteTransport:", error);
3430
+ throw error;
3431
+ }
3432
+ }
3488
3433
 
3489
- // Release the lock with the result
3490
- releaseSyncLock(dbId, result);
3491
- return result;
3492
- } catch (error) {
3493
- // Release the lock with the error
3494
- releaseSyncLockWithError(dbId, error);
3495
- throw error;
3496
- }
3434
+ /**
3435
+ * Syncs on-chain state only (no NTL fetch).
3436
+ *
3437
+ * @returns {Promise<SyncSummary>}
3438
+ */
3439
+ async syncChain() {
3440
+ const dbId = this.storeName || "default";
3441
+ const methodId = MethodName.SYNC_CHAIN;
3442
+
3443
+ try {
3444
+ return await withSyncLock(dbId, methodId, async () => {
3445
+ if (!this.worker) {
3446
+ const wasmWebClient = await this.getWasmWebClient();
3447
+ return await wasmWebClient.syncChainImpl();
3448
+ }
3449
+ const wasm = await getWasmOrThrow();
3450
+ const serializedSyncSummaryBytes =
3451
+ await this.callMethodWithWorker(methodId);
3452
+ return wasm.SyncSummary.deserialize(
3453
+ new Uint8Array(serializedSyncSummaryBytes)
3454
+ );
3455
+ });
3497
3456
  } catch (error) {
3498
- console.error("INDEX.JS: Error in syncState:", error);
3457
+ console.error("INDEX.JS: Error in syncChain:", error);
3499
3458
  throw error;
3500
3459
  }
3501
3460
  }
@@ -3531,9 +3490,23 @@ class MockWebClient extends WebClient {
3531
3490
  }
3532
3491
 
3533
3492
  initializeWorker() {
3493
+ // Pass `numThreads` exactly like the real INIT path: every prove runs
3494
+ // inside the worker's own WASM instance, and rayon's pool is
3495
+ // per-instance — without this, mock-client proving (including the
3496
+ // integration suite) silently runs single-threaded.
3497
+ let numThreads = 1;
3498
+ try {
3499
+ if (
3500
+ typeof self !== "undefined" &&
3501
+ self.crossOriginIsolated &&
3502
+ navigator?.hardwareConcurrency
3503
+ ) {
3504
+ numThreads = navigator.hardwareConcurrency;
3505
+ }
3506
+ } catch {}
3534
3507
  this.worker.postMessage({
3535
3508
  action: WorkerAction.INIT_MOCK,
3536
- args: [this.seed, this.logLevel],
3509
+ args: [this.seed, this.logLevel, numThreads],
3537
3510
  });
3538
3511
  }
3539
3512
 
@@ -3584,59 +3557,119 @@ class MockWebClient extends WebClient {
3584
3557
  * @returns {Promise<SyncSummary>} The sync summary
3585
3558
  */
3586
3559
  async syncState() {
3587
- return this.syncStateWithTimeout(0);
3560
+ const dbId = this.storeName || "mock";
3561
+ const methodId = MethodName.SYNC_STATE;
3562
+
3563
+ try {
3564
+ return await withSyncLock(dbId, methodId, async () => {
3565
+ const wasmWebClient = await this.getWasmWebClient();
3566
+
3567
+ if (!this.worker) {
3568
+ return await wasmWebClient.syncStateImpl();
3569
+ }
3570
+
3571
+ const serializedMockChain = (await wasmWebClient.serializeMockChain())
3572
+ .buffer;
3573
+ const serializedMockNoteTransportNode = (
3574
+ await wasmWebClient.serializeMockNoteTransportNode()
3575
+ ).buffer;
3576
+
3577
+ const wasm = await getWasmOrThrow();
3578
+ const serializedSyncSummaryBytes = await this.callMethodWithWorker(
3579
+ MethodName.SYNC_STATE_MOCK,
3580
+ serializedMockChain,
3581
+ serializedMockNoteTransportNode
3582
+ );
3583
+ return wasm.SyncSummary.deserialize(
3584
+ new Uint8Array(serializedSyncSummaryBytes)
3585
+ );
3586
+ });
3587
+ } catch (error) {
3588
+ console.error("INDEX.JS: Error in syncState:", error);
3589
+ throw error;
3590
+ }
3588
3591
  }
3589
3592
 
3590
3593
  /**
3591
- * Syncs the mock client state with an optional timeout.
3594
+ * Syncs only the on-chain mock state (no note transport fetch).
3592
3595
  *
3593
- * @param {number} timeoutMs - Timeout in milliseconds (0 = no timeout)
3594
- * @returns {Promise<SyncSummary>} The sync summary
3596
+ * In worker mode, the main-thread mock chain + note-transport-node state
3597
+ * is serialized and shipped to the worker before the sync, so a prior
3598
+ * `proveBlock()` on the main thread is reflected in the worker's WASM
3599
+ * client. The no-worker path uses the main-thread WASM client directly.
3600
+ *
3601
+ * @returns {Promise<SyncSummary>}
3595
3602
  */
3596
- async syncStateWithTimeout(timeoutMs = 0) {
3603
+ async syncChain() {
3597
3604
  const dbId = this.storeName || "mock";
3605
+ const methodId = MethodName.SYNC_CHAIN;
3598
3606
 
3599
3607
  try {
3600
- const lockHandle = await acquireSyncLock(dbId, timeoutMs);
3608
+ return await withSyncLock(dbId, methodId, async () => {
3609
+ const wasmWebClient = await this.getWasmWebClient();
3601
3610
 
3602
- if (!lockHandle.acquired) {
3603
- return lockHandle.coalescedResult;
3604
- }
3611
+ if (!this.worker) {
3612
+ return await wasmWebClient.syncChainImpl();
3613
+ }
3605
3614
 
3606
- try {
3607
- let result;
3615
+ const serializedMockChain = (await wasmWebClient.serializeMockChain())
3616
+ .buffer;
3617
+ const serializedMockNoteTransportNode = (
3618
+ await wasmWebClient.serializeMockNoteTransportNode()
3619
+ ).buffer;
3620
+
3621
+ const wasm = await getWasmOrThrow();
3622
+ const serializedSyncSummaryBytes = await this.callMethodWithWorker(
3623
+ MethodName.SYNC_CHAIN_MOCK,
3624
+ serializedMockChain,
3625
+ serializedMockNoteTransportNode
3626
+ );
3627
+ return wasm.SyncSummary.deserialize(
3628
+ new Uint8Array(serializedSyncSummaryBytes)
3629
+ );
3630
+ });
3631
+ } catch (error) {
3632
+ console.error("INDEX.JS: Error in syncChain:", error);
3633
+ throw error;
3634
+ }
3635
+ }
3636
+
3637
+ /**
3638
+ * Syncs only the mock note-transport state (no chain fetch).
3639
+ *
3640
+ * Mirrors {@link MockWebClient#syncChain}: in worker mode, the
3641
+ * main-thread mock chain + note-transport-node state is serialized
3642
+ * and shipped to the worker first.
3643
+ *
3644
+ * @returns {Promise<void>}
3645
+ */
3646
+ async syncNoteTransport() {
3647
+ const dbId = this.storeName || "mock";
3648
+ const methodId = MethodName.SYNC_NOTE_TRANSPORT;
3649
+
3650
+ try {
3651
+ await withSyncLock(dbId, methodId, async () => {
3608
3652
  const wasmWebClient = await this.getWasmWebClient();
3609
3653
 
3610
3654
  if (!this.worker) {
3611
- result = await wasmWebClient.syncStateImpl();
3612
- } else {
3613
- let serializedMockChain = (await wasmWebClient.serializeMockChain())
3614
- .buffer;
3615
- let serializedMockNoteTransportNode = (
3616
- await wasmWebClient.serializeMockNoteTransportNode()
3617
- ).buffer;
3618
-
3619
- const wasm = await getWasmOrThrow();
3620
-
3621
- const serializedSyncSummaryBytes = await this.callMethodWithWorker(
3622
- MethodName.SYNC_STATE_MOCK,
3623
- serializedMockChain,
3624
- serializedMockNoteTransportNode
3625
- );
3626
-
3627
- result = wasm.SyncSummary.deserialize(
3628
- new Uint8Array(serializedSyncSummaryBytes)
3629
- );
3655
+ await wasmWebClient.syncNoteTransportImpl();
3656
+ return;
3630
3657
  }
3631
3658
 
3632
- releaseSyncLock(dbId, result);
3633
- return result;
3634
- } catch (error) {
3635
- releaseSyncLockWithError(dbId, error);
3636
- throw error;
3637
- }
3659
+ const serializedMockChain = (await wasmWebClient.serializeMockChain())
3660
+ .buffer;
3661
+ const serializedMockNoteTransportNode = (
3662
+ await wasmWebClient.serializeMockNoteTransportNode()
3663
+ ).buffer;
3664
+
3665
+ await this.callMethodWithWorker(
3666
+ MethodName.SYNC_NOTE_TRANSPORT_MOCK,
3667
+ serializedMockChain,
3668
+ serializedMockNoteTransportNode
3669
+ );
3670
+ });
3638
3671
  } catch (error) {
3639
- console.error("INDEX.JS: Error in syncState:", error);
3672
+ console.error("INDEX.JS: Error in syncNoteTransport:", error);
3640
3673
  throw error;
3641
3674
  }
3642
3675
  }
@@ -3774,5 +3807,5 @@ MidenClient._MockWasmWebClient = MockWebClient;
3774
3807
  MidenClient._getWasmOrThrow = getWasmOrThrow;
3775
3808
  _setWebClient(WebClient);
3776
3809
 
3777
- export { AccountType, AuthScheme, CompilerResource, Linking, MidenArrays, MidenClient, MockWebClient as MockWasmWebClient, MockWebClient, NoteVisibility, StorageMode, StorageResult, StorageView, WebClient as WasmWebClient, buildSwapTag, createP2IDENote, createP2IDNote, getWasmOrThrow, wordToBigInt };
3810
+ export { AccountType, AuthScheme, CompilerResource, Linking, MidenArrays, MidenClient, MockWebClient as MockWasmWebClient, MockWebClient, NoteVisibility, StorageMode, StorageResult, StorageView, WebClient as WasmWebClient, buildSwapTag, createP2IDENote, createP2IDNote, getWasmOrThrow, withSyncLock, wordToBigInt };
3778
3811
  //# sourceMappingURL=index.js.map