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

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 +477 -375
  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 +477 -375
  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() {
1862
+ this.assertNotTerminated();
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() {
1955
1872
  this.assertNotTerminated();
1956
- return await this.#inner.syncStateWithTimeout(opts?.timeout ?? 0);
1873
+ return await this.#inner.syncNoteTransport();
1957
1874
  }
1958
1875
 
1959
1876
  /**
@@ -2623,6 +2540,34 @@ const Linking = Object.freeze({
2623
2540
  Static: "static",
2624
2541
  });
2625
2542
 
2543
+ // Method classification sets — used by scripts/check-method-classification.js to ensure
2544
+ // every WASM export is explicitly categorised. Update when adding new WASM methods.
2545
+ //
2546
+ // Naming note: "SYNC_METHODS" is a historical misnomer. This set groups methods
2547
+ // that are forwarded transparently to the underlying WASM via the Proxy in
2548
+ // `createClientProxy` — meaning they don't need an explicit JS-class wrapper
2549
+ // here. It does NOT mean "the method is synchronous"; several entries
2550
+ // (e.g. newSwapTransactionRequest, newPswapCreateTransactionRequest) are
2551
+ // `async fn` in Rust because they take the client's RNG via an async lock.
2552
+ const SYNC_METHODS = new Set([
2553
+ "buildSwapTag",
2554
+ "createCodeBuilder",
2555
+ "lastAuthError",
2556
+ "newConsumeTransactionRequest",
2557
+ "newMintTransactionRequest",
2558
+ "newPswapCancelTransactionRequest",
2559
+ "newPswapConsumeTransactionRequest",
2560
+ "newPswapCreateTransactionRequest",
2561
+ "newSendTransactionRequest",
2562
+ "newSwapTransactionRequest",
2563
+ "proveBlock",
2564
+ "serializeMockChain",
2565
+ "serializeMockNoteTransportNode",
2566
+ "setDebugMode",
2567
+ "storeIdentifier",
2568
+ "usesMockChain",
2569
+ ]);
2570
+
2626
2571
  const MOCK_STORE_NAME = "mock_client_db";
2627
2572
 
2628
2573
  const buildTypedArraysExport = (exportObject) => {
@@ -2736,7 +2681,19 @@ function createClientProxy(instance) {
2736
2681
  if (target.wasmWebClient && prop in target.wasmWebClient) {
2737
2682
  const value = target.wasmWebClient[prop];
2738
2683
  if (typeof value === "function") {
2739
- return value.bind(target.wasmWebClient);
2684
+ // SYNC_METHODS are safe to bind raw (synchronous in JS, or
2685
+ // documented exceptions). Everything else holds the WASM
2686
+ // client's internal RefCell across its awaits, so it MUST join
2687
+ // `_serializeWasmCall` — an unserialized fallback overlapping
2688
+ // any in-flight call panics with "RefCell already borrowed"
2689
+ // and poisons the instance for every later call.
2690
+ if (typeof prop === "string" && SYNC_METHODS.has(prop)) {
2691
+ return value.bind(target.wasmWebClient);
2692
+ }
2693
+ return (...args) =>
2694
+ target._serializeWasmCall(() =>
2695
+ value.apply(target.wasmWebClient, args)
2696
+ );
2740
2697
  }
2741
2698
  return value;
2742
2699
  }
@@ -2884,10 +2841,19 @@ class WebClient {
2884
2841
  this.loadedResolver = resolve;
2885
2842
  });
2886
2843
 
2887
- // Create a promise that resolves when the worker signals that it is fully initialized.
2888
- this.ready = new Promise((resolve) => {
2844
+ // Create a promise that resolves when the worker signals that it is
2845
+ // fully initialized, and rejects if initialization fails. Every
2846
+ // worker-forwarded method awaits `ready` first, so an init failure must
2847
+ // reject it — otherwise those calls would await a promise that never
2848
+ // settles and hang forever.
2849
+ this.ready = new Promise((resolve, reject) => {
2889
2850
  this.readyResolver = resolve;
2851
+ this.readyRejecter = reject;
2890
2852
  });
2853
+ // Init can fail before any caller awaits `ready`; this no-op handler
2854
+ // suppresses the unhandledrejection event without consuming the
2855
+ // rejection for real awaiters.
2856
+ this.ready.catch(() => {});
2891
2857
 
2892
2858
  // Listen for messages from the worker.
2893
2859
  this.worker.addEventListener("message", async (event) => {
@@ -2951,6 +2917,21 @@ class WebClient {
2951
2917
  } else {
2952
2918
  resolve(result);
2953
2919
  }
2920
+ return;
2921
+ }
2922
+
2923
+ // An error with no request attached comes from worker initialization
2924
+ // (INIT is the only requestId-less action that can fail). Reject
2925
+ // `ready` so queued and future method calls fail with the real cause
2926
+ // instead of awaiting forever.
2927
+ if (error && !requestId) {
2928
+ const workerError =
2929
+ error instanceof Error ? error : deserializeError(error);
2930
+ console.error(
2931
+ "WebClient: worker initialization failed:",
2932
+ workerError
2933
+ );
2934
+ this.readyRejecter(workerError);
2954
2935
  }
2955
2936
  });
2956
2937
 
@@ -3291,151 +3272,164 @@ class WebClient {
3291
3272
  }
3292
3273
 
3293
3274
  async submitNewTransaction(accountId, transactionRequest) {
3294
- try {
3295
- if (!this.worker) {
3296
- const wasmWebClient = await this.getWasmWebClient();
3297
- return await wasmWebClient.submitNewTransaction(
3298
- accountId,
3299
- transactionRequest
3300
- );
3301
- }
3275
+ return this._serializeWasmCall(async () => {
3276
+ try {
3277
+ if (!this.worker) {
3278
+ const wasmWebClient = await this.getWasmWebClient();
3279
+ return await wasmWebClient.submitNewTransaction(
3280
+ accountId,
3281
+ transactionRequest
3282
+ );
3283
+ }
3302
3284
 
3303
- const wasm = await getWasmOrThrow();
3304
- const serializedTransactionRequest = transactionRequest.serialize();
3305
- const result = await this.callMethodWithWorker(
3306
- MethodName.SUBMIT_NEW_TRANSACTION,
3307
- accountId.toString(),
3308
- serializedTransactionRequest
3309
- );
3285
+ const wasm = await getWasmOrThrow();
3286
+ const serializedTransactionRequest = transactionRequest.serialize();
3287
+ const result = await this.callMethodWithWorker(
3288
+ MethodName.SUBMIT_NEW_TRANSACTION,
3289
+ accountId.toString(),
3290
+ serializedTransactionRequest
3291
+ );
3310
3292
 
3311
- const transactionResult = wasm.TransactionResult.deserialize(
3312
- new Uint8Array(result.serializedTransactionResult)
3313
- );
3293
+ const transactionResult = wasm.TransactionResult.deserialize(
3294
+ new Uint8Array(result.serializedTransactionResult)
3295
+ );
3314
3296
 
3315
- return transactionResult.id();
3316
- } catch (error) {
3317
- console.error("INDEX.JS: Error in submitNewTransaction:", error);
3318
- throw error;
3319
- }
3297
+ return transactionResult.id();
3298
+ } catch (error) {
3299
+ console.error("INDEX.JS: Error in submitNewTransaction:", error);
3300
+ throw error;
3301
+ }
3302
+ });
3320
3303
  }
3321
3304
 
3322
3305
  async submitNewTransactionWithProver(accountId, transactionRequest, prover) {
3323
- try {
3324
- if (!this.worker) {
3325
- const wasmWebClient = await this.getWasmWebClient();
3326
- return await wasmWebClient.submitNewTransactionWithProver(
3327
- accountId,
3328
- transactionRequest,
3329
- prover
3330
- );
3331
- }
3306
+ return this._serializeWasmCall(async () => {
3307
+ try {
3308
+ if (!this.worker) {
3309
+ const wasmWebClient = await this.getWasmWebClient();
3310
+ return await wasmWebClient.submitNewTransactionWithProver(
3311
+ accountId,
3312
+ transactionRequest,
3313
+ prover
3314
+ );
3315
+ }
3332
3316
 
3333
- const wasm = await getWasmOrThrow();
3334
- const serializedTransactionRequest = transactionRequest.serialize();
3335
- const proverPayload = prover.serialize();
3336
- const result = await this.callMethodWithWorker(
3337
- MethodName.SUBMIT_NEW_TRANSACTION_WITH_PROVER,
3338
- accountId.toString(),
3339
- serializedTransactionRequest,
3340
- proverPayload
3341
- );
3317
+ const wasm = await getWasmOrThrow();
3318
+ const serializedTransactionRequest = transactionRequest.serialize();
3319
+ const proverPayload = prover.serialize();
3320
+ const result = await this.callMethodWithWorker(
3321
+ MethodName.SUBMIT_NEW_TRANSACTION_WITH_PROVER,
3322
+ accountId.toString(),
3323
+ serializedTransactionRequest,
3324
+ proverPayload
3325
+ );
3342
3326
 
3343
- const transactionResult = wasm.TransactionResult.deserialize(
3344
- new Uint8Array(result.serializedTransactionResult)
3345
- );
3327
+ const transactionResult = wasm.TransactionResult.deserialize(
3328
+ new Uint8Array(result.serializedTransactionResult)
3329
+ );
3346
3330
 
3347
- return transactionResult.id();
3348
- } catch (error) {
3349
- console.error(
3350
- "INDEX.JS: Error in submitNewTransactionWithProver:",
3351
- error
3352
- );
3353
- throw error;
3354
- }
3331
+ return transactionResult.id();
3332
+ } catch (error) {
3333
+ console.error(
3334
+ "INDEX.JS: Error in submitNewTransactionWithProver:",
3335
+ error
3336
+ );
3337
+ throw error;
3338
+ }
3339
+ });
3355
3340
  }
3356
3341
 
3357
3342
  async executeTransaction(accountId, transactionRequest) {
3358
- try {
3359
- if (!this.worker) {
3360
- const wasmWebClient = await this.getWasmWebClient();
3361
- return await wasmWebClient.executeTransaction(
3362
- accountId,
3363
- transactionRequest
3364
- );
3365
- }
3343
+ return this._serializeWasmCall(async () => {
3344
+ try {
3345
+ if (!this.worker) {
3346
+ const wasmWebClient = await this.getWasmWebClient();
3347
+ return await wasmWebClient.executeTransaction(
3348
+ accountId,
3349
+ transactionRequest
3350
+ );
3351
+ }
3366
3352
 
3367
- const wasm = await getWasmOrThrow();
3368
- const serializedTransactionRequest = transactionRequest.serialize();
3369
- const serializedResultBytes = await this.callMethodWithWorker(
3370
- MethodName.EXECUTE_TRANSACTION,
3371
- accountId.toString(),
3372
- serializedTransactionRequest
3373
- );
3353
+ const wasm = await getWasmOrThrow();
3354
+ const serializedTransactionRequest = transactionRequest.serialize();
3355
+ const serializedResultBytes = await this.callMethodWithWorker(
3356
+ MethodName.EXECUTE_TRANSACTION,
3357
+ accountId.toString(),
3358
+ serializedTransactionRequest
3359
+ );
3374
3360
 
3375
- return wasm.TransactionResult.deserialize(
3376
- new Uint8Array(serializedResultBytes)
3377
- );
3378
- } catch (error) {
3379
- console.error("INDEX.JS: Error in executeTransaction:", error);
3380
- throw error;
3381
- }
3361
+ return wasm.TransactionResult.deserialize(
3362
+ new Uint8Array(serializedResultBytes)
3363
+ );
3364
+ } catch (error) {
3365
+ console.error("INDEX.JS: Error in executeTransaction:", error);
3366
+ throw error;
3367
+ }
3368
+ });
3382
3369
  }
3383
3370
 
3384
3371
  async proveTransaction(transactionResult, prover) {
3385
- try {
3386
- if (!this.worker) {
3387
- const wasmWebClient = await this.getWasmWebClient();
3388
- return await wasmWebClient.proveTransaction(transactionResult, prover);
3389
- }
3372
+ return this._serializeWasmCall(async () => {
3373
+ try {
3374
+ if (!this.worker) {
3375
+ const wasmWebClient = await this.getWasmWebClient();
3376
+ return await wasmWebClient.proveTransaction(
3377
+ transactionResult,
3378
+ prover
3379
+ );
3380
+ }
3390
3381
 
3391
- const wasm = await getWasmOrThrow();
3392
- const serializedTransactionResult = transactionResult.serialize();
3393
- const proverPayload = prover ? prover.serialize() : null;
3382
+ const wasm = await getWasmOrThrow();
3383
+ const serializedTransactionResult = transactionResult.serialize();
3384
+ const proverPayload = prover ? prover.serialize() : null;
3394
3385
 
3395
- const serializedProvenBytes = await this.callMethodWithWorker(
3396
- MethodName.PROVE_TRANSACTION,
3397
- serializedTransactionResult,
3398
- proverPayload
3399
- );
3386
+ const serializedProvenBytes = await this.callMethodWithWorker(
3387
+ MethodName.PROVE_TRANSACTION,
3388
+ serializedTransactionResult,
3389
+ proverPayload
3390
+ );
3400
3391
 
3401
- return wasm.ProvenTransaction.deserialize(
3402
- new Uint8Array(serializedProvenBytes)
3403
- );
3404
- } catch (error) {
3405
- console.error("INDEX.JS: Error in proveTransaction:", error);
3406
- throw error;
3407
- }
3392
+ return wasm.ProvenTransaction.deserialize(
3393
+ new Uint8Array(serializedProvenBytes)
3394
+ );
3395
+ } catch (error) {
3396
+ console.error("INDEX.JS: Error in proveTransaction:", error);
3397
+ throw error;
3398
+ }
3399
+ });
3408
3400
  }
3409
3401
 
3410
3402
  async applyTransaction(transactionResult, submissionHeight) {
3411
- try {
3412
- if (!this.worker) {
3413
- const wasmWebClient = await this.getWasmWebClient();
3414
- return await wasmWebClient.applyTransaction(
3415
- transactionResult,
3403
+ return this._serializeWasmCall(async () => {
3404
+ try {
3405
+ if (!this.worker) {
3406
+ const wasmWebClient = await this.getWasmWebClient();
3407
+ return await wasmWebClient.applyTransaction(
3408
+ transactionResult,
3409
+ submissionHeight
3410
+ );
3411
+ }
3412
+
3413
+ const wasm = await getWasmOrThrow();
3414
+ const serializedTransactionResult = transactionResult.serialize();
3415
+ const serializedUpdateBytes = await this.callMethodWithWorker(
3416
+ MethodName.APPLY_TRANSACTION,
3417
+ serializedTransactionResult,
3416
3418
  submissionHeight
3417
3419
  );
3418
- }
3419
3420
 
3420
- const wasm = await getWasmOrThrow();
3421
- const serializedTransactionResult = transactionResult.serialize();
3422
- const serializedUpdateBytes = await this.callMethodWithWorker(
3423
- MethodName.APPLY_TRANSACTION,
3424
- serializedTransactionResult,
3425
- submissionHeight
3426
- );
3427
-
3428
- return wasm.TransactionStoreUpdate.deserialize(
3429
- new Uint8Array(serializedUpdateBytes)
3430
- );
3431
- } catch (error) {
3432
- console.error("INDEX.JS: Error in applyTransaction:", error);
3433
- throw error;
3434
- }
3421
+ return wasm.TransactionStoreUpdate.deserialize(
3422
+ new Uint8Array(serializedUpdateBytes)
3423
+ );
3424
+ } catch (error) {
3425
+ console.error("INDEX.JS: Error in applyTransaction:", error);
3426
+ throw error;
3427
+ }
3428
+ });
3435
3429
  }
3436
3430
 
3437
3431
  /**
3438
- * Syncs the client state with the node.
3432
+ * Syncs the client (NTL followed by chain sync, failing fast on either).
3439
3433
  *
3440
3434
  * This method coordinates concurrent sync calls using the Web Locks API when available,
3441
3435
  * with an in-process mutex fallback for older browsers. If a sync is already in progress,
@@ -3444,58 +3438,86 @@ class WebClient {
3444
3438
  * @returns {Promise<SyncSummary>} The sync summary
3445
3439
  */
3446
3440
  async syncState() {
3447
- return this.syncStateWithTimeout(0);
3441
+ const dbId = this.storeName || "default";
3442
+ const methodId = MethodName.SYNC_STATE;
3443
+
3444
+ try {
3445
+ // The sync lock coalesces concurrent sync callers; the inner
3446
+ // `_serializeWasmCall` keeps the WASM phase from racing any other
3447
+ // serialized method on this instance. Lock order is always
3448
+ // sync lock → chain, so the two can't deadlock.
3449
+ return await withSyncLock(dbId, methodId, async () =>
3450
+ this._serializeWasmCall(async () => {
3451
+ if (!this.worker) {
3452
+ const wasmWebClient = await this.getWasmWebClient();
3453
+ return await wasmWebClient.syncStateImpl();
3454
+ }
3455
+ const wasm = await getWasmOrThrow();
3456
+ const serializedSyncSummaryBytes =
3457
+ await this.callMethodWithWorker(methodId);
3458
+ return wasm.SyncSummary.deserialize(
3459
+ new Uint8Array(serializedSyncSummaryBytes)
3460
+ );
3461
+ })
3462
+ );
3463
+ } catch (error) {
3464
+ console.error("INDEX.JS: Error in syncState:", error);
3465
+ throw error;
3466
+ }
3448
3467
  }
3449
3468
 
3450
3469
  /**
3451
- * Syncs the client state with the node with an optional timeout.
3452
- *
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).
3470
+ * Fetches private notes from the Note Transport Layer.
3456
3471
  *
3457
- * @param {number} timeoutMs - Timeout in milliseconds (0 = no timeout)
3458
- * @returns {Promise<SyncSummary>} The sync summary
3472
+ * @returns {Promise<void>}
3459
3473
  */
3460
- async syncStateWithTimeout(timeoutMs = 0) {
3461
- // Use storeName as the database ID for lock coordination
3474
+ async syncNoteTransport() {
3462
3475
  const dbId = this.storeName || "default";
3476
+ const methodId = MethodName.SYNC_NOTE_TRANSPORT;
3463
3477
 
3464
3478
  try {
3465
- // Acquire the sync lock (coordinates concurrent calls)
3466
- const lockHandle = await acquireSyncLock(dbId, timeoutMs);
3479
+ await withSyncLock(dbId, methodId, async () =>
3480
+ this._serializeWasmCall(async () => {
3481
+ if (!this.worker) {
3482
+ const wasmWebClient = await this.getWasmWebClient();
3483
+ await wasmWebClient.syncNoteTransportImpl();
3484
+ } else {
3485
+ await this.callMethodWithWorker(methodId);
3486
+ }
3487
+ })
3488
+ );
3489
+ } catch (error) {
3490
+ console.error("INDEX.JS: Error in syncNoteTransport:", error);
3491
+ throw error;
3492
+ }
3493
+ }
3467
3494
 
3468
- if (!lockHandle.acquired) {
3469
- // We're coalescing - return the result from the in-progress sync
3470
- return lockHandle.coalescedResult;
3471
- }
3495
+ /**
3496
+ * Syncs on-chain state only (no NTL fetch).
3497
+ *
3498
+ * @returns {Promise<SyncSummary>}
3499
+ */
3500
+ async syncChain() {
3501
+ const dbId = this.storeName || "default";
3502
+ const methodId = MethodName.SYNC_CHAIN;
3472
3503
 
3473
- // We acquired the lock - perform the sync
3474
- try {
3475
- let result;
3476
- if (!this.worker) {
3477
- const wasmWebClient = await this.getWasmWebClient();
3478
- result = await wasmWebClient.syncStateImpl();
3479
- } else {
3504
+ try {
3505
+ return await withSyncLock(dbId, methodId, async () =>
3506
+ this._serializeWasmCall(async () => {
3507
+ if (!this.worker) {
3508
+ const wasmWebClient = await this.getWasmWebClient();
3509
+ return await wasmWebClient.syncChainImpl();
3510
+ }
3480
3511
  const wasm = await getWasmOrThrow();
3481
- const serializedSyncSummaryBytes = await this.callMethodWithWorker(
3482
- MethodName.SYNC_STATE
3483
- );
3484
- result = wasm.SyncSummary.deserialize(
3512
+ const serializedSyncSummaryBytes =
3513
+ await this.callMethodWithWorker(methodId);
3514
+ return wasm.SyncSummary.deserialize(
3485
3515
  new Uint8Array(serializedSyncSummaryBytes)
3486
3516
  );
3487
- }
3488
-
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
- }
3517
+ })
3518
+ );
3497
3519
  } catch (error) {
3498
- console.error("INDEX.JS: Error in syncState:", error);
3520
+ console.error("INDEX.JS: Error in syncChain:", error);
3499
3521
  throw error;
3500
3522
  }
3501
3523
  }
@@ -3531,9 +3553,23 @@ class MockWebClient extends WebClient {
3531
3553
  }
3532
3554
 
3533
3555
  initializeWorker() {
3556
+ // Pass `numThreads` exactly like the real INIT path: every prove runs
3557
+ // inside the worker's own WASM instance, and rayon's pool is
3558
+ // per-instance — without this, mock-client proving (including the
3559
+ // integration suite) silently runs single-threaded.
3560
+ let numThreads = 1;
3561
+ try {
3562
+ if (
3563
+ typeof self !== "undefined" &&
3564
+ self.crossOriginIsolated &&
3565
+ navigator?.hardwareConcurrency
3566
+ ) {
3567
+ numThreads = navigator.hardwareConcurrency;
3568
+ }
3569
+ } catch {}
3534
3570
  this.worker.postMessage({
3535
3571
  action: WorkerAction.INIT_MOCK,
3536
- args: [this.seed, this.logLevel],
3572
+ args: [this.seed, this.logLevel, numThreads],
3537
3573
  });
3538
3574
  }
3539
3575
 
@@ -3584,59 +3620,125 @@ class MockWebClient extends WebClient {
3584
3620
  * @returns {Promise<SyncSummary>} The sync summary
3585
3621
  */
3586
3622
  async syncState() {
3587
- return this.syncStateWithTimeout(0);
3623
+ const dbId = this.storeName || "mock";
3624
+ const methodId = MethodName.SYNC_STATE;
3625
+
3626
+ try {
3627
+ return await withSyncLock(dbId, methodId, async () =>
3628
+ this._serializeWasmCall(async () => {
3629
+ const wasmWebClient = await this.getWasmWebClient();
3630
+
3631
+ if (!this.worker) {
3632
+ return await wasmWebClient.syncStateImpl();
3633
+ }
3634
+
3635
+ const serializedMockChain = (await wasmWebClient.serializeMockChain())
3636
+ .buffer;
3637
+ const serializedMockNoteTransportNode = (
3638
+ await wasmWebClient.serializeMockNoteTransportNode()
3639
+ ).buffer;
3640
+
3641
+ const wasm = await getWasmOrThrow();
3642
+ const serializedSyncSummaryBytes = await this.callMethodWithWorker(
3643
+ MethodName.SYNC_STATE_MOCK,
3644
+ serializedMockChain,
3645
+ serializedMockNoteTransportNode
3646
+ );
3647
+ return wasm.SyncSummary.deserialize(
3648
+ new Uint8Array(serializedSyncSummaryBytes)
3649
+ );
3650
+ })
3651
+ );
3652
+ } catch (error) {
3653
+ console.error("INDEX.JS: Error in syncState:", error);
3654
+ throw error;
3655
+ }
3588
3656
  }
3589
3657
 
3590
3658
  /**
3591
- * Syncs the mock client state with an optional timeout.
3659
+ * Syncs only the on-chain mock state (no note transport fetch).
3592
3660
  *
3593
- * @param {number} timeoutMs - Timeout in milliseconds (0 = no timeout)
3594
- * @returns {Promise<SyncSummary>} The sync summary
3661
+ * In worker mode, the main-thread mock chain + note-transport-node state
3662
+ * is serialized and shipped to the worker before the sync, so a prior
3663
+ * `proveBlock()` on the main thread is reflected in the worker's WASM
3664
+ * client. The no-worker path uses the main-thread WASM client directly.
3665
+ *
3666
+ * @returns {Promise<SyncSummary>}
3595
3667
  */
3596
- async syncStateWithTimeout(timeoutMs = 0) {
3668
+ async syncChain() {
3597
3669
  const dbId = this.storeName || "mock";
3670
+ const methodId = MethodName.SYNC_CHAIN;
3598
3671
 
3599
3672
  try {
3600
- const lockHandle = await acquireSyncLock(dbId, timeoutMs);
3601
-
3602
- if (!lockHandle.acquired) {
3603
- return lockHandle.coalescedResult;
3604
- }
3673
+ return await withSyncLock(dbId, methodId, async () =>
3674
+ this._serializeWasmCall(async () => {
3675
+ const wasmWebClient = await this.getWasmWebClient();
3605
3676
 
3606
- try {
3607
- let result;
3608
- const wasmWebClient = await this.getWasmWebClient();
3677
+ if (!this.worker) {
3678
+ return await wasmWebClient.syncChainImpl();
3679
+ }
3609
3680
 
3610
- if (!this.worker) {
3611
- result = await wasmWebClient.syncStateImpl();
3612
- } else {
3613
- let serializedMockChain = (await wasmWebClient.serializeMockChain())
3681
+ const serializedMockChain = (await wasmWebClient.serializeMockChain())
3614
3682
  .buffer;
3615
- let serializedMockNoteTransportNode = (
3683
+ const serializedMockNoteTransportNode = (
3616
3684
  await wasmWebClient.serializeMockNoteTransportNode()
3617
3685
  ).buffer;
3618
3686
 
3619
3687
  const wasm = await getWasmOrThrow();
3620
-
3621
3688
  const serializedSyncSummaryBytes = await this.callMethodWithWorker(
3622
- MethodName.SYNC_STATE_MOCK,
3689
+ MethodName.SYNC_CHAIN_MOCK,
3623
3690
  serializedMockChain,
3624
3691
  serializedMockNoteTransportNode
3625
3692
  );
3626
-
3627
- result = wasm.SyncSummary.deserialize(
3693
+ return wasm.SyncSummary.deserialize(
3628
3694
  new Uint8Array(serializedSyncSummaryBytes)
3629
3695
  );
3630
- }
3696
+ })
3697
+ );
3698
+ } catch (error) {
3699
+ console.error("INDEX.JS: Error in syncChain:", error);
3700
+ throw error;
3701
+ }
3702
+ }
3631
3703
 
3632
- releaseSyncLock(dbId, result);
3633
- return result;
3634
- } catch (error) {
3635
- releaseSyncLockWithError(dbId, error);
3636
- throw error;
3637
- }
3704
+ /**
3705
+ * Syncs only the mock note-transport state (no chain fetch).
3706
+ *
3707
+ * Mirrors {@link MockWebClient#syncChain}: in worker mode, the
3708
+ * main-thread mock chain + note-transport-node state is serialized
3709
+ * and shipped to the worker first.
3710
+ *
3711
+ * @returns {Promise<void>}
3712
+ */
3713
+ async syncNoteTransport() {
3714
+ const dbId = this.storeName || "mock";
3715
+ const methodId = MethodName.SYNC_NOTE_TRANSPORT;
3716
+
3717
+ try {
3718
+ await withSyncLock(dbId, methodId, async () =>
3719
+ this._serializeWasmCall(async () => {
3720
+ const wasmWebClient = await this.getWasmWebClient();
3721
+
3722
+ if (!this.worker) {
3723
+ await wasmWebClient.syncNoteTransportImpl();
3724
+ return;
3725
+ }
3726
+
3727
+ const serializedMockChain = (await wasmWebClient.serializeMockChain())
3728
+ .buffer;
3729
+ const serializedMockNoteTransportNode = (
3730
+ await wasmWebClient.serializeMockNoteTransportNode()
3731
+ ).buffer;
3732
+
3733
+ await this.callMethodWithWorker(
3734
+ MethodName.SYNC_NOTE_TRANSPORT_MOCK,
3735
+ serializedMockChain,
3736
+ serializedMockNoteTransportNode
3737
+ );
3738
+ })
3739
+ );
3638
3740
  } catch (error) {
3639
- console.error("INDEX.JS: Error in syncState:", error);
3741
+ console.error("INDEX.JS: Error in syncNoteTransport:", error);
3640
3742
  throw error;
3641
3743
  }
3642
3744
  }
@@ -3774,5 +3876,5 @@ MidenClient._MockWasmWebClient = MockWebClient;
3774
3876
  MidenClient._getWasmOrThrow = getWasmOrThrow;
3775
3877
  _setWebClient(WebClient);
3776
3878
 
3777
- export { AccountType, AuthScheme, CompilerResource, Linking, MidenArrays, MidenClient, MockWebClient as MockWasmWebClient, MockWebClient, NoteVisibility, StorageMode, StorageResult, StorageView, WebClient as WasmWebClient, buildSwapTag, createP2IDENote, createP2IDNote, getWasmOrThrow, wordToBigInt };
3879
+ 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
3880
  //# sourceMappingURL=index.js.map