@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.
Files changed (59) hide show
  1. package/dist/mt/{Cargo-CZopJ--X.js → Cargo-smJQCGjz.js} +1111 -768
  2. package/dist/mt/Cargo-smJQCGjz.js.map +1 -0
  3. package/dist/mt/api-types.d.ts +122 -33
  4. package/dist/mt/assets/miden_client_web.wasm +0 -0
  5. package/dist/mt/crates/miden_client_web.d.ts +376 -303
  6. package/dist/mt/docs-entry.d.ts +3 -0
  7. package/dist/mt/eager.js +7 -4
  8. package/dist/mt/eager.js.map +1 -1
  9. package/dist/mt/index.d.ts +103 -10
  10. package/dist/mt/index.js +669 -312
  11. package/dist/mt/index.js.map +1 -1
  12. package/dist/mt/wasm.js +1 -1
  13. package/dist/mt/workerHelpers.js +1 -1
  14. package/dist/mt/workers/{Cargo-CZopJ--X-SsyOTzpb.js → Cargo-smJQCGjz-q4GYXDiD.js} +1111 -768
  15. package/dist/mt/workers/Cargo-smJQCGjz-q4GYXDiD.js.map +1 -0
  16. package/dist/mt/workers/assets/miden_client_web.wasm +0 -0
  17. package/dist/mt/workers/web-client-methods-worker.js +1140 -792
  18. package/dist/mt/workers/web-client-methods-worker.js.map +1 -1
  19. package/dist/mt/workers/web-client-methods-worker.module.js +23 -19
  20. package/dist/mt/workers/web-client-methods-worker.module.js.map +1 -1
  21. package/dist/mt/workers/workerHelpers.js +1 -1
  22. package/dist/st/{Cargo-DC6jSekr.js → Cargo-CG4XszZo.js} +1105 -763
  23. package/dist/st/Cargo-CG4XszZo.js.map +1 -0
  24. package/dist/st/api-types.d.ts +122 -33
  25. package/dist/st/assets/miden_client_web.wasm +0 -0
  26. package/dist/st/crates/miden_client_web.d.ts +376 -303
  27. package/dist/st/docs-entry.d.ts +3 -0
  28. package/dist/st/eager.js +7 -4
  29. package/dist/st/eager.js.map +1 -1
  30. package/dist/st/index.d.ts +103 -10
  31. package/dist/st/index.js +669 -312
  32. package/dist/st/index.js.map +1 -1
  33. package/dist/st/wasm.js +1 -1
  34. package/dist/st/workers/{Cargo-DC6jSekr-BG7C7m56.js → Cargo-CG4XszZo-S7EHAZSa.js} +1105 -763
  35. package/dist/st/workers/Cargo-CG4XszZo-S7EHAZSa.js.map +1 -0
  36. package/dist/st/workers/assets/miden_client_web.wasm +0 -0
  37. package/dist/st/workers/web-client-methods-worker.js +1134 -787
  38. package/dist/st/workers/web-client-methods-worker.js.map +1 -1
  39. package/dist/st/workers/web-client-methods-worker.module.js +23 -19
  40. package/dist/st/workers/web-client-methods-worker.module.js.map +1 -1
  41. package/js/client.js +491 -0
  42. package/js/node/client-factory.js +117 -0
  43. package/js/node/loader.js +138 -0
  44. package/js/node/napi-compat.js +253 -0
  45. package/js/node-index.js +194 -0
  46. package/js/resources/accounts.js +222 -0
  47. package/js/resources/compiler.js +74 -0
  48. package/js/resources/keystore.js +54 -0
  49. package/js/resources/notes.js +124 -0
  50. package/js/resources/settings.js +30 -0
  51. package/js/resources/tags.js +31 -0
  52. package/js/resources/transactions.js +667 -0
  53. package/js/standalone.js +109 -0
  54. package/js/utils.js +232 -0
  55. package/package.json +17 -2
  56. package/dist/mt/Cargo-CZopJ--X.js.map +0 -1
  57. package/dist/mt/workers/Cargo-CZopJ--X-SsyOTzpb.js.map +0 -1
  58. package/dist/st/Cargo-DC6jSekr.js.map +0 -1
  59. 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
+ }