@miden-sdk/miden-sdk 0.14.11 → 0.15.0-alpha.5
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/mt/{Cargo-CZopJ--X.js → Cargo-smJQCGjz.js} +1111 -768
- package/dist/mt/Cargo-smJQCGjz.js.map +1 -0
- package/dist/mt/api-types.d.ts +122 -33
- package/dist/mt/assets/miden_client_web.wasm +0 -0
- package/dist/mt/crates/miden_client_web.d.ts +376 -303
- package/dist/mt/docs-entry.d.ts +3 -0
- package/dist/mt/eager.js +7 -4
- package/dist/mt/eager.js.map +1 -1
- package/dist/mt/index.d.ts +103 -10
- package/dist/mt/index.js +669 -312
- package/dist/mt/index.js.map +1 -1
- package/dist/mt/wasm.js +1 -1
- package/dist/mt/workerHelpers.js +1 -1
- package/dist/mt/workers/{Cargo-CZopJ--X-SsyOTzpb.js → Cargo-smJQCGjz-q4GYXDiD.js} +1111 -768
- package/dist/mt/workers/Cargo-smJQCGjz-q4GYXDiD.js.map +1 -0
- package/dist/mt/workers/assets/miden_client_web.wasm +0 -0
- package/dist/mt/workers/web-client-methods-worker.js +1140 -792
- package/dist/mt/workers/web-client-methods-worker.js.map +1 -1
- package/dist/mt/workers/web-client-methods-worker.module.js +23 -19
- package/dist/mt/workers/web-client-methods-worker.module.js.map +1 -1
- package/dist/mt/workers/workerHelpers.js +1 -1
- package/dist/st/{Cargo-DC6jSekr.js → Cargo-CG4XszZo.js} +1105 -763
- package/dist/st/Cargo-CG4XszZo.js.map +1 -0
- package/dist/st/api-types.d.ts +122 -33
- package/dist/st/assets/miden_client_web.wasm +0 -0
- package/dist/st/crates/miden_client_web.d.ts +376 -303
- package/dist/st/docs-entry.d.ts +3 -0
- package/dist/st/eager.js +7 -4
- package/dist/st/eager.js.map +1 -1
- package/dist/st/index.d.ts +103 -10
- package/dist/st/index.js +669 -312
- package/dist/st/index.js.map +1 -1
- package/dist/st/wasm.js +1 -1
- package/dist/st/workers/{Cargo-DC6jSekr-BG7C7m56.js → Cargo-CG4XszZo-S7EHAZSa.js} +1105 -763
- package/dist/st/workers/Cargo-CG4XszZo-S7EHAZSa.js.map +1 -0
- package/dist/st/workers/assets/miden_client_web.wasm +0 -0
- package/dist/st/workers/web-client-methods-worker.js +1134 -787
- package/dist/st/workers/web-client-methods-worker.js.map +1 -1
- package/dist/st/workers/web-client-methods-worker.module.js +23 -19
- package/dist/st/workers/web-client-methods-worker.module.js.map +1 -1
- package/js/client.js +491 -0
- package/js/node/client-factory.js +117 -0
- package/js/node/loader.js +138 -0
- package/js/node/napi-compat.js +253 -0
- package/js/node-index.js +194 -0
- package/js/resources/accounts.js +222 -0
- package/js/resources/compiler.js +74 -0
- package/js/resources/keystore.js +54 -0
- package/js/resources/notes.js +124 -0
- package/js/resources/settings.js +30 -0
- package/js/resources/tags.js +31 -0
- package/js/resources/transactions.js +667 -0
- package/js/standalone.js +109 -0
- package/js/utils.js +232 -0
- package/package.json +17 -2
- package/dist/mt/Cargo-CZopJ--X.js.map +0 -1
- package/dist/mt/workers/Cargo-CZopJ--X-SsyOTzpb.js.map +0 -1
- package/dist/st/Cargo-DC6jSekr.js.map +0 -1
- package/dist/st/workers/Cargo-DC6jSekr-BG7C7m56.js.map +0 -1
package/js/client.js
ADDED
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
import { AccountsResource } from "./resources/accounts.js";
|
|
2
|
+
import { TransactionsResource } from "./resources/transactions.js";
|
|
3
|
+
import { NotesResource } from "./resources/notes.js";
|
|
4
|
+
import { TagsResource } from "./resources/tags.js";
|
|
5
|
+
import { SettingsResource } from "./resources/settings.js";
|
|
6
|
+
import { CompilerResource } from "./resources/compiler.js";
|
|
7
|
+
import { KeystoreResource } from "./resources/keystore.js";
|
|
8
|
+
import { hashSeed } from "./utils.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* MidenClient wraps the existing proxy-wrapped WebClient with a resource-based API.
|
|
12
|
+
*
|
|
13
|
+
* Resource classes receive the proxy client and call its methods, handling all type
|
|
14
|
+
* conversions (string -> AccountId, number -> BigInt, string -> enum).
|
|
15
|
+
*/
|
|
16
|
+
export class MidenClient {
|
|
17
|
+
// Injected by index.js to resolve circular imports
|
|
18
|
+
static _WasmWebClient = null;
|
|
19
|
+
static _MockWasmWebClient = null;
|
|
20
|
+
static _getWasmOrThrow = null;
|
|
21
|
+
|
|
22
|
+
#inner;
|
|
23
|
+
#getWasm;
|
|
24
|
+
#terminated = false;
|
|
25
|
+
#defaultProver = null;
|
|
26
|
+
#isMock = false;
|
|
27
|
+
|
|
28
|
+
constructor(inner, getWasm, defaultProver) {
|
|
29
|
+
this.#inner = inner;
|
|
30
|
+
this.#getWasm = getWasm;
|
|
31
|
+
this.#defaultProver = defaultProver ?? null;
|
|
32
|
+
|
|
33
|
+
this.accounts = new AccountsResource(inner, getWasm, this);
|
|
34
|
+
this.transactions = new TransactionsResource(inner, getWasm, this);
|
|
35
|
+
this.notes = new NotesResource(inner, getWasm, this);
|
|
36
|
+
this.tags = new TagsResource(inner, getWasm, this);
|
|
37
|
+
this.settings = new SettingsResource(inner, getWasm, this);
|
|
38
|
+
this.compile = new CompilerResource(inner, getWasm, this);
|
|
39
|
+
this.keystore = new KeystoreResource(inner, this);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Escape hatch: runs `fn` with exclusive access to the proxied JS
|
|
44
|
+
* WebClient that backs this MidenClient.
|
|
45
|
+
*
|
|
46
|
+
* The proxy forwards missing properties to the underlying wasm-bindgen
|
|
47
|
+
* `WebClient`, so `fn` can reach lower-level methods like
|
|
48
|
+
* `executeTransaction`, `proveTransaction[WithProver]`,
|
|
49
|
+
* `submitProvenTransaction`, `applyTransaction`,
|
|
50
|
+
* `newSendTransactionRequest`, `newConsumeTransactionRequest`, etc.
|
|
51
|
+
*
|
|
52
|
+
* Intended for advanced consumers that need to split the bundled
|
|
53
|
+
* execute → prove → submit → apply pipeline across contexts — for example,
|
|
54
|
+
* a Chrome MV3 extension that runs `executeTransaction` in its service
|
|
55
|
+
* worker, dispatches the prove step to a `chrome.offscreen` document
|
|
56
|
+
* (where wasm-bindgen-rayon can spawn a real thread pool), then runs
|
|
57
|
+
* `submitProvenTransaction` + `applyTransaction` back in the SW.
|
|
58
|
+
*
|
|
59
|
+
* The callback runs inside `_serializeWasmCall`, so the WASM RefCell is
|
|
60
|
+
* held for the duration of `fn`. Concurrent SDK calls (sync, other
|
|
61
|
+
* transactions, etc.) queue on the same chain and run after `fn`
|
|
62
|
+
* settles. Without this serialization, raw inner-client access would
|
|
63
|
+
* race the proxy's chain and trip wasm-bindgen's "recursive use of an
|
|
64
|
+
* object detected" panic.
|
|
65
|
+
*
|
|
66
|
+
* Re-entrancy: while `fn` is running, the underlying client's
|
|
67
|
+
* `_withInnerLockDepth` counter is bumped so that `_serializeWasmCall`
|
|
68
|
+
* invocations made BY `fn` (or any proxy-dispatched method it calls)
|
|
69
|
+
* run inline rather than enqueuing on the chain. Without this, every
|
|
70
|
+
* `await inner.X(...)` inside `fn` would enqueue behind the outer
|
|
71
|
+
* `_withInnerWebClient` slot which is itself awaiting `fn` —
|
|
72
|
+
* a classic re-entrant-lock deadlock. The depth counter restores the
|
|
73
|
+
* intent of the docstring above: the lock is held for the duration
|
|
74
|
+
* of `fn`, and inner-client calls "borrow" that already-held lock
|
|
75
|
+
* instead of trying to re-acquire it.
|
|
76
|
+
*
|
|
77
|
+
* SAFETY CONTRACT for re-entrancy: callers MUST hold an external
|
|
78
|
+
* mutex preventing concurrent access to this same client instance
|
|
79
|
+
* via other code paths during `fn`. The chain still serializes
|
|
80
|
+
* against external callers — they queue behind the outer slot — but
|
|
81
|
+
* if an external task runs during one of `fn`'s awaits and calls
|
|
82
|
+
* into the SDK, it will see `_withInnerLockDepth > 0` and run
|
|
83
|
+
* inline, racing wasm-bindgen's borrow check. The wallet pattern
|
|
84
|
+
* (own outer mutex around `_withInnerWebClient`) satisfies this.
|
|
85
|
+
*
|
|
86
|
+
* Stability: marked `@internal`. The shape of the proxied client is
|
|
87
|
+
* intentionally not part of the documented public API and may change
|
|
88
|
+
* between SDK versions. If you depend on this method, pin the SDK
|
|
89
|
+
* version and test the lower-level surface carefully on each upgrade.
|
|
90
|
+
* If your use case is common enough to warrant a stable public API,
|
|
91
|
+
* file an issue.
|
|
92
|
+
*
|
|
93
|
+
* @internal
|
|
94
|
+
* @template T
|
|
95
|
+
* @param {(inner: object) => Promise<T>} fn - Async callback receiving
|
|
96
|
+
* the proxied JS WebClient. Must not return references that escape
|
|
97
|
+
* the callback's lifetime (the lock is released on settle).
|
|
98
|
+
* @returns {Promise<T>} The resolved value of `fn`.
|
|
99
|
+
*/
|
|
100
|
+
_withInnerWebClient(fn) {
|
|
101
|
+
this.assertNotTerminated();
|
|
102
|
+
if (typeof fn !== "function") {
|
|
103
|
+
throw new TypeError("_withInnerWebClient: fn must be a function");
|
|
104
|
+
}
|
|
105
|
+
const inner = this.#inner;
|
|
106
|
+
return inner._serializeWasmCall(async () => {
|
|
107
|
+
inner._withInnerLockDepth = (inner._withInnerLockDepth || 0) + 1;
|
|
108
|
+
try {
|
|
109
|
+
return await fn(inner);
|
|
110
|
+
} finally {
|
|
111
|
+
inner._withInnerLockDepth--;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Creates and initializes a new MidenClient.
|
|
118
|
+
*
|
|
119
|
+
* If no `rpcUrl` is provided, defaults to testnet with full configuration
|
|
120
|
+
* (RPC, prover, note transport, autoSync).
|
|
121
|
+
*
|
|
122
|
+
* @param {ClientOptions} [options] - Client configuration options.
|
|
123
|
+
* @returns {Promise<MidenClient>} A fully initialized client.
|
|
124
|
+
*/
|
|
125
|
+
static async create(options) {
|
|
126
|
+
if (!options?.rpcUrl) {
|
|
127
|
+
return MidenClient.createTestnet(options);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const getWasm = MidenClient._getWasmOrThrow;
|
|
131
|
+
const WebClientClass = MidenClient._WasmWebClient;
|
|
132
|
+
|
|
133
|
+
if (!WebClientClass || !getWasm) {
|
|
134
|
+
throw new Error(
|
|
135
|
+
"MidenClient not initialized. Import from the SDK package entry point."
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const seed = options?.seed ? await hashSeed(options.seed) : undefined;
|
|
140
|
+
|
|
141
|
+
const rpcUrl = resolveRpcUrl(options?.rpcUrl);
|
|
142
|
+
const noteTransportUrl = resolveNoteTransportUrl(options?.noteTransportUrl);
|
|
143
|
+
|
|
144
|
+
// `useWorker: false` opts out of the Web Worker shim that wraps every
|
|
145
|
+
// WASM call. The shim exists to keep the main thread responsive in
|
|
146
|
+
// browser/extension contexts, but it serializes the prover via
|
|
147
|
+
// `TransactionProver.serialize()` — a format that has no encoding for
|
|
148
|
+
// `newCallbackProver(jsFn)` and silently downgrades it to `"local"`.
|
|
149
|
+
// Mobile/Tauri/native-prover consumers must pass `useWorker: false`.
|
|
150
|
+
const useWorker = options?.useWorker;
|
|
151
|
+
let inner;
|
|
152
|
+
if (options?.keystore) {
|
|
153
|
+
inner = await WebClientClass.createClientWithExternalKeystore(
|
|
154
|
+
rpcUrl,
|
|
155
|
+
noteTransportUrl,
|
|
156
|
+
seed,
|
|
157
|
+
options?.storeName,
|
|
158
|
+
options.keystore.getKey,
|
|
159
|
+
options.keystore.insertKey,
|
|
160
|
+
options.keystore.sign,
|
|
161
|
+
options?.debugMode,
|
|
162
|
+
useWorker
|
|
163
|
+
);
|
|
164
|
+
} else {
|
|
165
|
+
inner = await WebClientClass.createClient(
|
|
166
|
+
rpcUrl,
|
|
167
|
+
noteTransportUrl,
|
|
168
|
+
seed,
|
|
169
|
+
options?.storeName,
|
|
170
|
+
options?.debugMode,
|
|
171
|
+
useWorker
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
let defaultProver = null;
|
|
176
|
+
if (options?.proverUrl) {
|
|
177
|
+
const wasm = await getWasm();
|
|
178
|
+
defaultProver = resolveProver(options.proverUrl, wasm);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const client = new MidenClient(inner, getWasm, defaultProver);
|
|
182
|
+
|
|
183
|
+
if (options?.autoSync) {
|
|
184
|
+
await client.sync();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return client;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Creates a client preconfigured for testnet use.
|
|
192
|
+
*
|
|
193
|
+
* Defaults: rpcUrl "testnet", proverUrl "testnet", noteTransportUrl "testnet", autoSync true.
|
|
194
|
+
* All defaults can be overridden via options.
|
|
195
|
+
*
|
|
196
|
+
* @param {ClientOptions} [options] - Options to override defaults.
|
|
197
|
+
* @returns {Promise<MidenClient>} A fully initialized testnet client.
|
|
198
|
+
*/
|
|
199
|
+
static async createTestnet(options) {
|
|
200
|
+
return MidenClient.create({
|
|
201
|
+
rpcUrl: "testnet",
|
|
202
|
+
proverUrl: "testnet",
|
|
203
|
+
noteTransportUrl: "testnet",
|
|
204
|
+
autoSync: true,
|
|
205
|
+
...options,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Creates a client preconfigured for devnet use.
|
|
211
|
+
*
|
|
212
|
+
* Defaults: rpcUrl "devnet", proverUrl "devnet", noteTransportUrl "devnet", autoSync true.
|
|
213
|
+
* All defaults can be overridden via options.
|
|
214
|
+
*
|
|
215
|
+
* @param {ClientOptions} [options] - Options to override defaults.
|
|
216
|
+
* @returns {Promise<MidenClient>} A fully initialized devnet client.
|
|
217
|
+
*/
|
|
218
|
+
static async createDevnet(options) {
|
|
219
|
+
return MidenClient.create({
|
|
220
|
+
rpcUrl: "devnet",
|
|
221
|
+
proverUrl: "devnet",
|
|
222
|
+
noteTransportUrl: "devnet",
|
|
223
|
+
autoSync: true,
|
|
224
|
+
...options,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Resolves once the WASM module is initialized and safe to use.
|
|
230
|
+
*
|
|
231
|
+
* Idempotent and shared across callers: the underlying loader memoizes the
|
|
232
|
+
* in-flight promise, so concurrent `ready()` calls await the same
|
|
233
|
+
* initialization and post-init callers resolve immediately from a cached
|
|
234
|
+
* module. Safe to call from `MidenProvider`, tutorial helpers, and any
|
|
235
|
+
* other consumer simultaneously.
|
|
236
|
+
*
|
|
237
|
+
* Useful on the `/lazy` entry (e.g. Next.js / Capacitor), where no
|
|
238
|
+
* top-level await runs at import time. On the default (eager) entry this
|
|
239
|
+
* is redundant — importing the module already awaits WASM — but calling it
|
|
240
|
+
* is still harmless.
|
|
241
|
+
*
|
|
242
|
+
* @returns {Promise<void>} Resolves when WASM is initialized.
|
|
243
|
+
*/
|
|
244
|
+
static async ready() {
|
|
245
|
+
const getWasm = MidenClient._getWasmOrThrow;
|
|
246
|
+
if (!getWasm) {
|
|
247
|
+
throw new Error(
|
|
248
|
+
"MidenClient not initialized. Import from the SDK package entry point."
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
await getWasm();
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Creates a mock client for testing.
|
|
256
|
+
*
|
|
257
|
+
* @param {MockOptions} [options] - Mock client options.
|
|
258
|
+
* @returns {Promise<MidenClient>} A mock client.
|
|
259
|
+
*/
|
|
260
|
+
static async createMock(options) {
|
|
261
|
+
const getWasm = MidenClient._getWasmOrThrow;
|
|
262
|
+
const MockWebClientClass = MidenClient._MockWasmWebClient;
|
|
263
|
+
|
|
264
|
+
if (!MockWebClientClass || !getWasm) {
|
|
265
|
+
throw new Error(
|
|
266
|
+
"MidenClient not initialized. Import from the SDK package entry point."
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const seed = options?.seed ? await hashSeed(options.seed) : undefined;
|
|
271
|
+
|
|
272
|
+
const inner = await MockWebClientClass.createClient(
|
|
273
|
+
options?.serializedMockChain,
|
|
274
|
+
options?.serializedNoteTransport,
|
|
275
|
+
seed
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
const client = new MidenClient(inner, getWasm, null);
|
|
279
|
+
client.#isMock = true;
|
|
280
|
+
return client;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/** Returns the client-level default prover (set from ClientOptions.proverUrl). */
|
|
284
|
+
get defaultProver() {
|
|
285
|
+
return this.#defaultProver;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Syncs the client state with the Miden node.
|
|
290
|
+
*
|
|
291
|
+
* @param {object} [opts] - Sync options.
|
|
292
|
+
* @param {number} [opts.timeout] - Timeout in milliseconds (0 = no timeout).
|
|
293
|
+
* @returns {Promise<SyncSummary>} The sync summary.
|
|
294
|
+
*/
|
|
295
|
+
async sync(opts) {
|
|
296
|
+
this.assertNotTerminated();
|
|
297
|
+
return await this.#inner.syncStateWithTimeout(opts?.timeout ?? 0);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Returns the current sync height.
|
|
302
|
+
*
|
|
303
|
+
* @returns {Promise<number>} The current sync height.
|
|
304
|
+
*/
|
|
305
|
+
async getSyncHeight() {
|
|
306
|
+
this.assertNotTerminated();
|
|
307
|
+
return await this.#inner.getSyncHeight();
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Resolves once every serialized WASM call that was already on the
|
|
312
|
+
* internal `_serializeWasmCall` chain when `waitForIdle()` was called
|
|
313
|
+
* (execute, submit, prove, apply, sync, or account creation) has
|
|
314
|
+
* settled. Use this from callers that need to perform a non-WASM-side
|
|
315
|
+
* action — e.g. clearing an in-memory auth key on wallet lock — after
|
|
316
|
+
* the kernel finishes, so its auth callback doesn't race with the key
|
|
317
|
+
* being cleared.
|
|
318
|
+
*
|
|
319
|
+
* Does NOT wait for calls enqueued after `waitForIdle()` returns —
|
|
320
|
+
* intentional, so a caller can drain and proceed without being blocked
|
|
321
|
+
* indefinitely by concurrent workload.
|
|
322
|
+
*
|
|
323
|
+
* Caveat for `syncState`: `syncStateWithTimeout` awaits the sync lock
|
|
324
|
+
* (`acquireSyncLock`, which uses Web Locks) BEFORE putting its WASM
|
|
325
|
+
* call onto the chain, so a `syncState` that is queued on the sync
|
|
326
|
+
* lock — but has not yet begun its WASM phase — is not visible to
|
|
327
|
+
* `waitForIdle` and will not be awaited. Other methods (`newWallet`,
|
|
328
|
+
* `executeTransaction`, etc.) route through the chain synchronously
|
|
329
|
+
* on call and are always observed.
|
|
330
|
+
*
|
|
331
|
+
* Safe to call at any time; returns immediately if nothing was in
|
|
332
|
+
* flight.
|
|
333
|
+
*
|
|
334
|
+
* @returns {Promise<void>}
|
|
335
|
+
*/
|
|
336
|
+
async waitForIdle() {
|
|
337
|
+
this.assertNotTerminated();
|
|
338
|
+
await this.#inner.waitForIdle();
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Returns the raw JS value that the most recent sign-callback invocation
|
|
343
|
+
* threw, or `null` if the last sign call succeeded (or no call has
|
|
344
|
+
* happened yet).
|
|
345
|
+
*
|
|
346
|
+
* Useful for recovering structured metadata (e.g. a `reason: 'locked'`
|
|
347
|
+
* property) that the kernel-level `auth::request` diagnostic would
|
|
348
|
+
* otherwise erase. Call immediately after catching a failed
|
|
349
|
+
* `transactions.submit` / `transactions.send` / `transactions.consume`.
|
|
350
|
+
*
|
|
351
|
+
* Meaningful only with `useWorker: false`: under the worker shim the
|
|
352
|
+
* sign callback fires against the worker's WASM keystore, while this
|
|
353
|
+
* accessor reads the main-thread instance — which never signed — so it
|
|
354
|
+
* returns `null`. Consumers that need this signal (e.g. external
|
|
355
|
+
* keystores with lock-aware sign callbacks) already require
|
|
356
|
+
* `useWorker: false` for the callback to be reachable at all.
|
|
357
|
+
*
|
|
358
|
+
* @returns {any} The raw thrown value, or `null`.
|
|
359
|
+
*/
|
|
360
|
+
lastAuthError() {
|
|
361
|
+
this.assertNotTerminated();
|
|
362
|
+
return this.#inner.lastAuthError();
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Terminates the underlying Web Worker. After this, all method calls will throw.
|
|
367
|
+
*/
|
|
368
|
+
terminate() {
|
|
369
|
+
this.#terminated = true;
|
|
370
|
+
this.#inner.terminate?.();
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
[Symbol.dispose]() {
|
|
374
|
+
this.terminate();
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
async [Symbol.asyncDispose]() {
|
|
378
|
+
this.terminate();
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Returns the identifier of the underlying store (e.g. IndexedDB database name, file path).
|
|
383
|
+
*
|
|
384
|
+
* @returns {string} The store identifier.
|
|
385
|
+
*/
|
|
386
|
+
async storeIdentifier() {
|
|
387
|
+
this.assertNotTerminated();
|
|
388
|
+
return await this.#inner.storeIdentifier();
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// ── Mock-only methods ──
|
|
392
|
+
|
|
393
|
+
/** Advances the mock chain by one block. Only available on mock clients. */
|
|
394
|
+
proveBlock() {
|
|
395
|
+
this.assertNotTerminated();
|
|
396
|
+
this.#assertMock("proveBlock");
|
|
397
|
+
return this.#inner.proveBlock();
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/** Returns true if this client uses a mock chain. */
|
|
401
|
+
usesMockChain() {
|
|
402
|
+
return this.#isMock;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/** Serializes the mock chain state for snapshot/restore in tests. */
|
|
406
|
+
serializeMockChain() {
|
|
407
|
+
this.assertNotTerminated();
|
|
408
|
+
this.#assertMock("serializeMockChain");
|
|
409
|
+
return this.#inner.serializeMockChain();
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/** Serializes the mock note transport node state. */
|
|
413
|
+
serializeMockNoteTransportNode() {
|
|
414
|
+
this.assertNotTerminated();
|
|
415
|
+
this.#assertMock("serializeMockNoteTransportNode");
|
|
416
|
+
return this.#inner.serializeMockNoteTransportNode();
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// ── Internal ──
|
|
420
|
+
|
|
421
|
+
/** @internal Throws if the client has been terminated. */
|
|
422
|
+
assertNotTerminated() {
|
|
423
|
+
if (this.#terminated) {
|
|
424
|
+
throw new Error("Client terminated");
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
#assertMock(method) {
|
|
429
|
+
if (!this.#isMock) {
|
|
430
|
+
throw new Error(`${method}() is only available on mock clients`);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const RPC_URLS = {
|
|
436
|
+
testnet: "https://rpc.testnet.miden.io",
|
|
437
|
+
devnet: "https://rpc.devnet.miden.io",
|
|
438
|
+
localhost: "http://localhost:57291",
|
|
439
|
+
local: "http://localhost:57291",
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Resolves an rpcUrl shorthand or raw URL into a concrete endpoint string.
|
|
444
|
+
*
|
|
445
|
+
* @param {string | undefined} rpcUrl - "testnet", "devnet", "localhost", "local", or a raw URL.
|
|
446
|
+
* @returns {string | undefined} A fully qualified URL, or undefined to use the SDK default.
|
|
447
|
+
*/
|
|
448
|
+
function resolveRpcUrl(rpcUrl) {
|
|
449
|
+
if (!rpcUrl) return undefined;
|
|
450
|
+
return RPC_URLS[rpcUrl.trim().toLowerCase()] ?? rpcUrl;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
const PROVER_URLS = {
|
|
454
|
+
devnet: "https://tx-prover.devnet.miden.io",
|
|
455
|
+
testnet: "https://tx-prover.testnet.miden.io",
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
const NOTE_TRANSPORT_URLS = {
|
|
459
|
+
testnet: "https://transport.miden.io",
|
|
460
|
+
devnet: "https://transport.devnet.miden.io",
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Resolves a noteTransportUrl shorthand or raw URL into a concrete endpoint string.
|
|
465
|
+
*
|
|
466
|
+
* @param {string | undefined} noteTransportUrl - "testnet", "devnet", or a raw URL.
|
|
467
|
+
* @returns {string | undefined} A fully qualified URL, or undefined if omitted.
|
|
468
|
+
*/
|
|
469
|
+
function resolveNoteTransportUrl(noteTransportUrl) {
|
|
470
|
+
if (!noteTransportUrl) return undefined;
|
|
471
|
+
return (
|
|
472
|
+
NOTE_TRANSPORT_URLS[noteTransportUrl.trim().toLowerCase()] ??
|
|
473
|
+
noteTransportUrl
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Resolves a proverUrl shorthand or raw URL into a TransactionProver.
|
|
479
|
+
*
|
|
480
|
+
* @param {string} proverUrl - "local", "devnet", "testnet", or a raw URL.
|
|
481
|
+
* @param {object} wasm - Loaded WASM module.
|
|
482
|
+
* @returns {object} A TransactionProver instance.
|
|
483
|
+
*/
|
|
484
|
+
function resolveProver(proverUrl, wasm) {
|
|
485
|
+
const normalized = proverUrl.trim().toLowerCase();
|
|
486
|
+
if (normalized === "local") {
|
|
487
|
+
return wasm.TransactionProver.newLocalProver();
|
|
488
|
+
}
|
|
489
|
+
const remoteUrl = PROVER_URLS[normalized] ?? proverUrl;
|
|
490
|
+
return wasm.TransactionProver.newRemoteProver(remoteUrl, undefined);
|
|
491
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js-specific client factories that match the browser SDK's interface.
|
|
3
|
+
*
|
|
4
|
+
* The browser SDK creates clients with IndexedDB store names.
|
|
5
|
+
* Node.js uses SQLite file paths and filesystem keystores.
|
|
6
|
+
* These factories bridge the difference so MidenClient.create() works on both.
|
|
7
|
+
*/
|
|
8
|
+
import path from "path";
|
|
9
|
+
import fs from "fs";
|
|
10
|
+
import os from "os";
|
|
11
|
+
import { wrapClient, normalizeArg } from "./napi-compat.js";
|
|
12
|
+
|
|
13
|
+
let _counter = 0;
|
|
14
|
+
|
|
15
|
+
function createTempDir(label) {
|
|
16
|
+
const dir = path.join(
|
|
17
|
+
os.tmpdir(),
|
|
18
|
+
`miden-${label}-${process.pid}-${Date.now()}-${++_counter}`
|
|
19
|
+
);
|
|
20
|
+
fs.mkdirSync(path.join(dir, "keystore"), { recursive: true });
|
|
21
|
+
return dir;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Returns a deterministic data directory for a given store name.
|
|
26
|
+
* Uses ~/.miden/stores/<storeName>/ so data persists across runs.
|
|
27
|
+
*/
|
|
28
|
+
function defaultDataDir(storeName) {
|
|
29
|
+
const dir = path.join(os.homedir(), ".miden", "stores", storeName);
|
|
30
|
+
fs.mkdirSync(path.join(dir, "keystore"), { recursive: true });
|
|
31
|
+
return dir;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function normBytes(val) {
|
|
35
|
+
if (val instanceof Uint8Array || Buffer.isBuffer(val)) return Array.from(val);
|
|
36
|
+
return val;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Creates the WasmWebClient factory for Node.js.
|
|
41
|
+
*
|
|
42
|
+
* Matches the browser interface:
|
|
43
|
+
* WasmWebClient.createClient(rpcUrl, noteTransportUrl, seed, storeName, debugMode)
|
|
44
|
+
* WasmWebClient.createClientWithExternalKeystore(rpcUrl, noteTransportUrl, seed, storeName, getKey, insertKey, sign, debugMode)
|
|
45
|
+
* WasmWebClient.buildSwapTag(...)
|
|
46
|
+
*
|
|
47
|
+
* @param {object} rawSdk - The raw napi SDK module.
|
|
48
|
+
* @param {object} [options]
|
|
49
|
+
* @param {string} [options.dataDir] - Base directory for SQLite stores. Defaults to os.tmpdir().
|
|
50
|
+
*/
|
|
51
|
+
export function createWasmWebClient(rawSdk, options) {
|
|
52
|
+
return {
|
|
53
|
+
buildSwapTag: (...args) =>
|
|
54
|
+
rawSdk.WebClient.buildSwapTag(...args.map(normalizeArg)),
|
|
55
|
+
|
|
56
|
+
createClient: async (
|
|
57
|
+
rpcUrl,
|
|
58
|
+
noteTransportUrl,
|
|
59
|
+
seed,
|
|
60
|
+
storeName,
|
|
61
|
+
debugMode
|
|
62
|
+
) => {
|
|
63
|
+
const dir = options?.dataDir
|
|
64
|
+
? path.join(options.dataDir, storeName || "default")
|
|
65
|
+
: storeName
|
|
66
|
+
? defaultDataDir(storeName)
|
|
67
|
+
: createTempDir("client");
|
|
68
|
+
|
|
69
|
+
const client = new rawSdk.WebClient();
|
|
70
|
+
await client.createClient(
|
|
71
|
+
rpcUrl ?? null,
|
|
72
|
+
noteTransportUrl ?? null,
|
|
73
|
+
normBytes(seed) ?? null,
|
|
74
|
+
path.join(dir, `${storeName || "store"}.db`),
|
|
75
|
+
path.join(dir, "keystore"),
|
|
76
|
+
debugMode ?? false
|
|
77
|
+
);
|
|
78
|
+
return wrapClient(client, storeName);
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
createClientWithExternalKeystore: async () => {
|
|
82
|
+
throw new Error(
|
|
83
|
+
"External keystores are not supported on Node.js. " +
|
|
84
|
+
"The Node.js backend uses a filesystem keystore automatically."
|
|
85
|
+
);
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Creates the MockWasmWebClient factory for Node.js.
|
|
92
|
+
*
|
|
93
|
+
* Matches the browser interface:
|
|
94
|
+
* MockWasmWebClient.createClient(serializedMockChain, serializedNoteTransport, seed)
|
|
95
|
+
*
|
|
96
|
+
* @param {object} rawSdk - The raw napi SDK module.
|
|
97
|
+
*/
|
|
98
|
+
export function createMockWasmWebClient(rawSdk) {
|
|
99
|
+
return {
|
|
100
|
+
createClient: async (
|
|
101
|
+
serializedMockChain,
|
|
102
|
+
serializedNoteTransport,
|
|
103
|
+
seed
|
|
104
|
+
) => {
|
|
105
|
+
const dir = createTempDir("mock");
|
|
106
|
+
const client = new rawSdk.WebClient();
|
|
107
|
+
await client.createMockClient(
|
|
108
|
+
path.join(dir, "store.db"),
|
|
109
|
+
path.join(dir, "keystore"),
|
|
110
|
+
normBytes(seed) ?? null,
|
|
111
|
+
normBytes(serializedMockChain) ?? null,
|
|
112
|
+
normBytes(serializedNoteTransport) ?? null
|
|
113
|
+
);
|
|
114
|
+
return wrapClient(client, "mock");
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|