create-miden-app 1.0.6 → 1.0.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 (70) hide show
  1. package/package.json +1 -1
  2. package/template/.claude/commands/review-security.md +67 -0
  3. package/template/.claude/settings.json +1 -7
  4. package/template/.claude/settings.local.json +24 -0
  5. package/template/.claude/skills/frontend-pitfalls/SKILL.md +28 -31
  6. package/template/.claude/skills/frontend-source-guide/SKILL.md +14 -14
  7. package/template/.claude/skills/miden-concepts/SKILL.md +4 -2
  8. package/template/.claude/skills/react-sdk-patterns/SKILL.md +294 -28
  9. package/template/.claude/skills/signer-integration/SKILL.md +22 -3
  10. package/template/.claude/skills/testing-patterns/SKILL.md +201 -40
  11. package/template/.claude/skills/vite-wasm-setup/SKILL.md +20 -14
  12. package/template/.claude/skills/web-client-usage/SKILL.md +454 -0
  13. package/template/.env.example +15 -2
  14. package/template/.mcp.json +9 -0
  15. package/template/CLAUDE.md +49 -16
  16. package/template/README.md +85 -19
  17. package/template/package.json +5 -4
  18. package/template/public/packages/counter_account.masp +0 -0
  19. package/template/public/packages/increment_note.masp +0 -0
  20. package/template/src/__tests__/fixtures/accounts.ts +17 -6
  21. package/template/src/__tests__/fixtures/index.ts +1 -0
  22. package/template/src/__tests__/mocks/miden-sdk-react.ts +18 -1
  23. package/template/src/__tests__/patterns/mutation-hook.test.tsx +2 -2
  24. package/template/src/__tests__/patterns/provider-setup.test.tsx +2 -0
  25. package/template/src/components/AppContent.tsx +33 -3
  26. package/template/{create-miden-app/template/src/components/Counter.tsx → src/components/ConfiguredCounter.tsx} +7 -4
  27. package/template/src/components/Counter.tsx +12 -41
  28. package/template/src/components/__tests__/AppContent.test.tsx +192 -4
  29. package/template/src/components/__tests__/ConfiguredCounter.test.tsx +116 -0
  30. package/template/src/components/__tests__/Counter.test.tsx +24 -94
  31. package/template/src/config.ts +26 -6
  32. package/template/src/hooks/__tests__/useIncrementCounter.test.tsx +257 -0
  33. package/template/src/hooks/useIncrementCounter.ts +109 -50
  34. package/template/src/providers.tsx +20 -24
  35. package/template/vite.config.ts +1 -1
  36. package/template/vitest.config.ts +1 -2
  37. package/template/yarn.lock +761 -688
  38. package/template/create-miden-app/template/.claude/hooks/typecheck.sh +0 -27
  39. package/template/create-miden-app/template/.claude/settings.json +0 -17
  40. package/template/create-miden-app/template/.claude/skills/frontend-pitfalls/SKILL.md +0 -189
  41. package/template/create-miden-app/template/.claude/skills/frontend-source-guide/SKILL.md +0 -163
  42. package/template/create-miden-app/template/.claude/skills/miden-concepts/SKILL.md +0 -108
  43. package/template/create-miden-app/template/.claude/skills/react-sdk-patterns/SKILL.md +0 -294
  44. package/template/create-miden-app/template/.claude/skills/signer-integration/SKILL.md +0 -158
  45. package/template/create-miden-app/template/.claude/skills/vite-wasm-setup/SKILL.md +0 -128
  46. package/template/create-miden-app/template/.env.example +0 -5
  47. package/template/create-miden-app/template/CLAUDE.md +0 -116
  48. package/template/create-miden-app/template/README.md +0 -61
  49. package/template/create-miden-app/template/eslint.config.js +0 -23
  50. package/template/create-miden-app/template/index.html +0 -13
  51. package/template/create-miden-app/template/package.json +0 -34
  52. package/template/create-miden-app/template/public/vite.svg +0 -1
  53. package/template/create-miden-app/template/src/App.tsx +0 -10
  54. package/template/create-miden-app/template/src/assets/miden.svg +0 -3
  55. package/template/create-miden-app/template/src/assets/react.svg +0 -1
  56. package/template/create-miden-app/template/src/components/AppContent.css +0 -45
  57. package/template/create-miden-app/template/src/components/AppContent.tsx +0 -50
  58. package/template/create-miden-app/template/src/components/Counter.css +0 -27
  59. package/template/create-miden-app/template/src/config.ts +0 -21
  60. package/template/create-miden-app/template/src/hooks/useIncrementCounter.ts +0 -136
  61. package/template/create-miden-app/template/src/index.css +0 -75
  62. package/template/create-miden-app/template/src/lib/miden.ts +0 -9
  63. package/template/create-miden-app/template/src/main.tsx +0 -10
  64. package/template/create-miden-app/template/src/providers.tsx +0 -31
  65. package/template/create-miden-app/template/src/vite-env.d.ts +0 -1
  66. package/template/create-miden-app/template/tsconfig.app.json +0 -32
  67. package/template/create-miden-app/template/tsconfig.json +0 -7
  68. package/template/create-miden-app/template/tsconfig.node.json +0 -24
  69. package/template/create-miden-app/template/vite.config.ts +0 -17
  70. package/template/create-miden-app/template/yarn.lock +0 -1697
@@ -7,11 +7,11 @@ description: Testing conventions, mock factory, fixtures, and TDD workflow for M
7
7
 
8
8
  ## Test Stack
9
9
 
10
- - **Vitest** Test runner (extends Vite config for consistent behavior)
11
- - **@testing-library/react** Component rendering and queries
12
- - **@testing-library/user-event** User interaction simulation
13
- - **@testing-library/jest-dom** DOM assertion matchers (toBeInTheDocument, toBeDisabled, etc.)
14
- - **jsdom** Browser environment for tests
10
+ - **Vitest** - Test runner (extends Vite config for consistent behavior)
11
+ - **@testing-library/react** - Component rendering and queries
12
+ - **@testing-library/user-event** - User interaction simulation
13
+ - **@testing-library/jest-dom** - DOM assertion matchers (toBeInTheDocument, toBeDisabled, etc.)
14
+ - **jsdom** - Browser environment for tests
15
15
 
16
16
  ## Mock Factory: `@miden-sdk/react`
17
17
 
@@ -43,17 +43,17 @@ it("shows empty state", () => {
43
43
  ### Default mock return values
44
44
 
45
45
  **Query hooks** return populated data by default:
46
- - `useAccounts()` 2 wallets, 1 faucet
47
- - `useAccount()` account with 10.0 TEST token balance
48
- - `useNotes()` 1 input note, 1 consumable note
49
- - `useSyncState()` syncHeight: 12345, not syncing
50
- - `useAssetMetadata()` TEST token metadata (symbol, decimals: 8)
51
- - `useMiden()` isReady: true
46
+ - `useAccounts()` - 2 wallets, 1 faucet
47
+ - `useAccount()` - account with 10.0 TEST token balance
48
+ - `useNotes()` - 1 input note, 1 consumable note
49
+ - `useSyncState()` - syncHeight: 12345, not syncing
50
+ - `useAssetMetadata()` - TEST token metadata (symbol, decimals: 8)
51
+ - `useMiden()` - isReady: true
52
52
 
53
53
  **Mutation hooks** return idle state by default:
54
- - `useSend()` `{ send: vi.fn(), stage: "idle", isLoading: false }`
55
- - `useMint()`, `useConsume()`, `useSwap()`, `useTransaction()` similar pattern
56
- - `useCreateWallet()` `{ createWallet: vi.fn(), isCreating: false }`
54
+ - `useSend()` - `{ send: vi.fn(), stage: "idle", isLoading: false }`. Its `result` type is `SendResult { txId, note }` - distinct from `TransactionResult { transactionId }` used by `useMint`/`useConsume`/`useSwap`/`useMultiSend`/`useTransaction`.
55
+ - `useMint()`, `useConsume()`, `useSwap()`, `useTransaction()`, `useMultiSend()` - idle shape with `result: TransactionResult | null`.
56
+ - `useCreateWallet()` - `{ createWallet: vi.fn(), isCreating: false }`.
57
57
 
58
58
  ### Simulating transaction stages
59
59
 
@@ -68,10 +68,20 @@ vi.mocked(useSend).mockReturnValue({
68
68
  reset: vi.fn(),
69
69
  });
70
70
 
71
- // Show completed transaction
71
+ // Show completed transaction - useSend returns SendResult { txId, note }
72
72
  vi.mocked(useSend).mockReturnValue({
73
73
  send: vi.fn(),
74
- result: { transactionId: "0xabc123" },
74
+ result: { txId: "0xabc123", note: null },
75
+ isLoading: false,
76
+ stage: "complete",
77
+ error: null,
78
+ reset: vi.fn(),
79
+ });
80
+
81
+ // Other mutation hooks return TransactionResult { transactionId }
82
+ vi.mocked(useMint).mockReturnValue({
83
+ mint: vi.fn(),
84
+ result: { transactionId: "0xdef456" },
75
85
  isLoading: false,
76
86
  stage: "complete",
77
87
  error: null,
@@ -85,21 +95,22 @@ Realistic test data in `src/__tests__/fixtures/`:
85
95
 
86
96
  ```tsx
87
97
  import {
88
- WALLET_ID_1, // "mtst1qy35qfqdvpjx2e5zf9hkp4vr"
89
- WALLET_ID_2, // "mtst1qa7k9qjf8dp4x2e5zf9hkp5vr"
90
- FAUCET_ID, // "mtst1qx9y8zjf2dp4x2e5zf9hkp3vr"
91
- COUNTER_ID, // "mtst1aru8adnrqspgcsr3drk2n990lyc070ll"
98
+ WALLET_ID_1, // "0x0a00000000000001"
99
+ WALLET_ID_2, // "0x0a00000000000002"
100
+ FAUCET_ID, // "0x0a00000000000003"
101
+ COUNTER_ID, // "0x0a00000000000004"
92
102
  MOCK_WALLET_HEADER, // { id, nonce, storageCommitment }
93
103
  MOCK_FAUCET_HEADER, // { id, nonce, storageCommitment }
94
104
  MOCK_ASSET_BALANCE, // { assetId, amount: 1000000000n, symbol: "TEST", decimals: 8 }
95
105
  MOCK_ACCOUNT, // { id, nonce, bech32id() }
96
- MOCK_TRANSACTION_RESULT, // { transactionId: "0x..." }
97
- MOCK_NOTE_SUMMARY, // { id, assets, sender }
106
+ MOCK_TRANSACTION_RESULT, // { transactionId: "0x..." } - useMint / useConsume / useSwap / useMultiSend / useTransaction
107
+ MOCK_SEND_RESULT, // { txId: "0x...", note: null } - useSend
108
+ MOCK_NOTE_SUMMARY, // { id, assets, sender }
98
109
  } from "@/__tests__/fixtures";
99
110
  ```
100
111
 
101
112
  Key characteristics:
102
- - Account IDs use bech32 format (`mtst1...`)
113
+ - Account IDs use hex format (`0x...`) - network-agnostic test fixtures
103
114
  - Amounts are `bigint` (e.g., `1000000000n` = 10.0 with 8 decimals)
104
115
  - Asset metadata uses TEST token with 8 decimals
105
116
 
@@ -116,35 +127,185 @@ Reference tests in `src/__tests__/patterns/`:
116
127
  ### Minimum test coverage per component
117
128
 
118
129
  Every component test should cover:
119
- 1. **Success state** renders correctly with data
120
- 2. **Loading state** shows loading indicator
121
- 3. **Error state** shows error message, recovery action
122
- 4. **User interactions** buttons, forms trigger correct handler calls
130
+ 1. **Success state** - renders correctly with data
131
+ 2. **Loading state** - shows loading indicator
132
+ 3. **Error state** - shows error message, recovery action
133
+ 4. **User interactions** - buttons, forms trigger correct handler calls
134
+
135
+ ## Wallet connection state in tests
123
136
 
124
- ## Mocking the wallet adapter
137
+ This template's wallet button (`src/components/AppContent.tsx`) drives off **`useMidenFiWallet()`** from `@miden-sdk/miden-wallet-adapter-react`, not the generic `useSigner()`. The button gates on `wallet.readyState` (from `@miden-sdk/miden-wallet-adapter-base`) so the UI can render an "Install MidenFi Wallet" state before the extension is detected, rather than falling through to the adapter's Chrome-Web-Store fallback. When testing wallet-connect UI, mock both modules and override per test.
125
138
 
126
- The app uses `@miden-sdk/miden-wallet-adapter`. Mock it at the module level:
139
+ The mock factory must return the **full `WalletContextState`** shape - `useIncrementCounter` reads `address` and `requestTransaction` directly off the hook return, and the wallet button reads `wallet.readyState`. A partial mock will compile (with broad casts) and silently miss contract drift. Setup:
127
140
 
128
141
  ```tsx
129
- vi.mock("@miden-sdk/miden-wallet-adapter", () => ({
130
- WalletMultiButton: () => <button>Connect Wallet</button>,
131
- useWallet: vi.fn(() => ({
132
- address: "mtst1...",
133
- connected: true,
134
- requestTransaction: vi.fn(),
142
+ vi.mock("@miden-sdk/react", () => import("@/__tests__/mocks/miden-sdk-react"));
143
+ vi.mock("@miden-sdk/miden-wallet-adapter-react", () => ({
144
+ useMidenFiWallet: vi.fn(() => ({
145
+ autoConnect: false,
146
+ wallets: [],
147
+ wallet: null,
148
+ address: null,
149
+ publicKey: null,
150
+ connected: false,
151
+ connecting: false,
152
+ disconnecting: false,
153
+ select: vi.fn(),
154
+ connect: vi.fn(async () => undefined),
155
+ disconnect: vi.fn(async () => undefined),
156
+ requestTransaction: vi.fn(async () => "0xtx"),
157
+ requestAssets: undefined,
158
+ requestPrivateNotes: undefined,
159
+ signBytes: undefined,
160
+ importPrivateNote: undefined,
161
+ requestConsumableNotes: undefined,
162
+ waitForTransaction: undefined,
163
+ requestSend: undefined,
164
+ requestConsume: undefined,
165
+ createAccount: undefined,
135
166
  })),
136
167
  }));
168
+ vi.mock("@miden-sdk/miden-wallet-adapter-base", () => ({
169
+ WalletReadyState: {
170
+ Installed: "Installed",
171
+ NotDetected: "NotDetected",
172
+ Loadable: "Loadable",
173
+ Unsupported: "Unsupported",
174
+ },
175
+ }));
176
+
177
+ import { useMidenFiWallet } from "@miden-sdk/miden-wallet-adapter-react";
178
+ ```
179
+
180
+ Use a typed factory for per-test overrides - `WalletContextState` is the `useMidenFiWallet()` return type:
181
+
182
+ ```tsx
183
+ type WalletState = ReturnType<typeof useMidenFiWallet>;
184
+ type WalletInner = NonNullable<WalletState["wallet"]>;
185
+
186
+ function walletState(
187
+ overrides: Partial<{
188
+ readyState: "Installed" | "NotDetected" | "Loadable" | "Unsupported";
189
+ connected: boolean;
190
+ address: string | null;
191
+ requestTransaction: WalletState["requestTransaction"];
192
+ }> = {},
193
+ ): WalletState {
194
+ const {
195
+ readyState = "Installed",
196
+ connected = false,
197
+ address = connected ? "mtst1arwk88k8smzcq5p30upr6eerw5npmnyz" : null,
198
+ requestTransaction = vi.fn(async () => "0xtx"),
199
+ } = overrides;
200
+ // The inner Wallet's `adapter` is an `Adapter` (eventemitter + polling
201
+ // strategy) - we stub it structurally because the components under test
202
+ // only read `readyState` off the inner wallet object.
203
+ const innerWallet = {
204
+ adapter: {} as WalletInner["adapter"],
205
+ readyState,
206
+ } as WalletInner;
207
+ return {
208
+ autoConnect: false,
209
+ wallets: [innerWallet],
210
+ wallet: innerWallet,
211
+ address,
212
+ publicKey: null,
213
+ connected,
214
+ connecting: false,
215
+ disconnecting: false,
216
+ select: vi.fn(),
217
+ connect: vi.fn(async () => undefined),
218
+ disconnect: vi.fn(async () => undefined),
219
+ requestTransaction,
220
+ requestAssets: undefined,
221
+ requestPrivateNotes: undefined,
222
+ signBytes: undefined,
223
+ importPrivateNote: undefined,
224
+ requestConsumableNotes: undefined,
225
+ waitForTransaction: undefined,
226
+ requestSend: undefined,
227
+ requestConsume: undefined,
228
+ createAccount: undefined,
229
+ };
230
+ }
231
+
232
+ // extension not detected - shows disabled "Install MidenFi Wallet"
233
+ vi.mocked(useMidenFiWallet).mockReturnValue(
234
+ walletState({ readyState: "NotDetected" }),
235
+ );
236
+
237
+ // installed + connected with an account - shows "Disconnect Wallet"
238
+ vi.mocked(useMidenFiWallet).mockReturnValue(
239
+ walletState({ readyState: "Installed", connected: true }),
240
+ );
241
+ ```
242
+
243
+ The factory satisfies `WalletContextState` without `as unknown as` over the whole object - the only narrow `as` is the inner adapter stub, which is unavoidable until we want to construct a real `Adapter` in tests. See `src/components/__tests__/AppContent.test.tsx` for the canonical version.
244
+
245
+ For app code that needs the selected signer account for client-side flows (transaction-building hooks, etc.), `useMiden()` exposes `signerAccountId` / `signerConnected` as lower-level provider state - mock those via the `@miden-sdk/react` mock factory.
246
+
247
+ Vitest config externalizes `@miden-sdk/miden-wallet-adapter-react` to prevent broken transitive resolution.
248
+
249
+ ## Mocking Classes Called with `new` (Vitest v4)
250
+
251
+ Vitest v4 enforces that mock implementations passed to `vi.fn()` must be `function` declarations (not arrow functions) when the mocked function is invoked with `new`. Arrow functions cannot be called as constructors and will throw `TypeError: ... is not a constructor`.
252
+
253
+ ```ts
254
+ // WRONG: arrow function - throws when production code does `new MidenClient(...)`
255
+ vi.mock("@miden-sdk/miden-sdk", () => ({
256
+ MidenClient: vi.fn(() => ({ /* ... */ })),
257
+ }));
258
+
259
+ // RIGHT: function expression - usable with `new`
260
+ vi.mock("@miden-sdk/miden-sdk", () => ({
261
+ MidenClient: vi.fn(function () {
262
+ return { /* ... */ };
263
+ }),
264
+ }));
265
+ ```
266
+
267
+ This applies to any class mocked at module level that production code instantiates with `new` (`new MidenClient(...)`, `new WasmWebClient(...)`, etc.). When tests fail with `TypeError: ... is not a constructor` after a Vitest v4 upgrade, swap the arrow-function bodies for `vi.fn(function () { ... })`.
268
+
269
+ For component-level wallet adapters and hooks that are function references rather than classes (the existing `vi.mock("@miden-sdk/miden-wallet-adapter-react", ...)` example above), arrow-function mocks remain fine.
270
+
271
+ ## Testing Time-Dependent Code (Network Sync Delay)
272
+
273
+ Production code that polls or waits on chain state should accept the delay interval as an injectable parameter rather than hardcoding it. This lets tests replace the production default (e.g. `5000` ms) with `0` so the loop drains synchronously without `vi.useFakeTimers()` plumbing.
274
+
275
+ Pattern:
276
+
277
+ ```ts
278
+ // Production: optional delay parameter with a sensible default
279
+ export function pollUntilCommit(
280
+ txId: string,
281
+ intervalMs = 5000, // production default
282
+ ) {
283
+ // ... uses setTimeout(..., intervalMs) or `await sleep(intervalMs)`
284
+ }
285
+
286
+ // Tests: pass 0 to skip waits
287
+ const result = await pollUntilCommit(txId, 0);
288
+ ```
289
+
290
+ When the value comes from `src/config.ts` (e.g. `NETWORK_SYNC_DELAY_MS`), expose the same override there so tests can stub it via `vi.mock("@/config", ...)` without touching app code:
291
+
292
+ ```ts
293
+ // src/config.ts
294
+ export const NETWORK_SYNC_DELAY_MS = Number(import.meta.env.VITE_NETWORK_SYNC_DELAY_MS ?? 5000);
295
+
296
+ // test
297
+ vi.mock("@/config", () => ({ NETWORK_SYNC_DELAY_MS: 0 }));
137
298
  ```
138
299
 
139
- Vitest config externalizes `@miden-sdk/miden-wallet-adapter*` sub-packages to prevent broken transitive resolution from the reactui sub-package.
300
+ Document the production default and the test override at the call site so the contract between app code and tests is obvious.
140
301
 
141
302
  ## Automated Verification Pipeline
142
303
 
143
304
  Hooks in `.claude/settings.json` enforce quality automatically:
144
305
 
145
- 1. **PostToolUse: typecheck** `npx tsc -b --noEmit` on every `.ts`/`.tsx` edit in `src/`
146
- 2. **PostToolUse: affected tests** `npx vitest --changed --run` on every `.ts`/`.tsx` edit in `src/`
147
- 3. **Stop hook** Full `vitest --run && tsc -b --noEmit && vite build` before task completion
306
+ 1. **PostToolUse: typecheck** - `npx tsc -b --noEmit` on every `.ts`/`.tsx` edit in `src/`
307
+ 2. **PostToolUse: affected tests** - `npx vitest --changed --run` on every `.ts`/`.tsx` edit in `src/`
308
+ 3. **PostToolUse: full suite** - Full `vitest --run && tsc -b --noEmit && vite build` after each edit
148
309
 
149
310
  If any hook fails (exit code 2), the agent is blocked from proceeding until the issue is fixed.
150
311
 
@@ -163,7 +324,7 @@ If any hook fails (exit code 2), the agent is blocked from proceeding until the
163
324
 
164
325
  6. Refactor if needed
165
326
 
166
- 7. Task complete → Stop hook: full suite + build
327
+ 7. Task complete → full suite already ran after last edit
167
328
  ```
168
329
 
169
330
  ## Common Mistakes
@@ -13,42 +13,48 @@ import react from "@vitejs/plugin-react";
13
13
  import { midenVitePlugin } from "@miden-sdk/vite-plugin";
14
14
 
15
15
  export default defineConfig({
16
- plugins: [react(), midenVitePlugin()],
16
+ plugins: [react(), midenVitePlugin({ crossOriginIsolation: true })],
17
17
  });
18
18
  ```
19
19
 
20
- `midenVitePlugin()` accepts an options object:
20
+ Pass `crossOriginIsolation: true` explicitly. The Miden WASM client uses `SharedArrayBuffer` via Rust atomics, which is only available when the page is cross-origin-isolated (COOP `same-origin` + COEP `require-corp`). Don't rely on the plugin's own default — it has shifted across releases, so the template's config is source-of-truth.
21
21
 
22
- ```typescript
23
- midenVitePlugin({ crossOriginIsolation: true })
24
- // Enables COOP/COEP headers in dev server. Defaults to false to avoid breaking OAuth popups.
25
- ```
22
+ If your app must host third-party iframes, OAuth popups, or other cross-origin resources that don't emit `require-corp`, either (a) embed them via `credentialless` COEP as a workaround (see the Gotchas section below), or (b) set `crossOriginIsolation: false` and accept that Miden client operations won't work on that route.
26
23
 
27
24
  ## What midenVitePlugin() Handles
28
25
 
29
- `@miden-sdk/vite-plugin` abstracts all Miden-specific Vite configuration:
26
+ `@miden-sdk/vite-plugin` abstracts Miden-specific Vite configuration:
30
27
 
31
28
  - **WASM loading** — Configures Vite to correctly import `.wasm` modules
32
29
  - **Top-level await** — Enables top-level `await` required by the WASM SDK initialization
33
30
  - **optimizeDeps** — Excludes `@miden-sdk/miden-sdk` from pre-bundling (pre-bundling corrupts the WASM binary)
34
- - **COOP/COEP headers** — Optionally adds `Cross-Origin-Opener-Policy` and `Cross-Origin-Embedder-Policy` headers via `crossOriginIsolation` option
31
+ - **COOP/COEP headers** — Emits `Cross-Origin-Opener-Policy: same-origin` + `Cross-Origin-Embedder-Policy: require-corp` on the dev server when `crossOriginIsolation: true`
35
32
 
36
- You do not need to install or configure `vite-plugin-wasm`, `vite-plugin-top-level-await`, or dexie aliases manually.
33
+ You don't need to install or configure `vite-plugin-wasm`, `vite-plugin-top-level-await`, or dexie aliases manually.
37
34
 
38
35
  ## Required Dependencies
39
36
 
37
+ Keep all `@miden-sdk/*` runtime packages aligned. The template's `package.json` pins them as an exact-version set; upgrade all four together and re-run the full verification suite (including the wallet-confirmed increment E2E) whenever you bump.
38
+
40
39
  ```json
41
40
  {
42
41
  "dependencies": {
43
- "@miden-sdk/react": "^0.13.0",
44
- "@miden-sdk/miden-sdk": "^0.13.0"
42
+ "@miden-sdk/react": "<matches miden-sdk>",
43
+ "@miden-sdk/miden-sdk": "<authoritative version>",
44
+ "@miden-sdk/miden-wallet-adapter-base": "<may lag by a patch>",
45
+ "@miden-sdk/miden-wallet-adapter-react": "<may lag by a patch>"
45
46
  },
46
47
  "devDependencies": {
47
- "@miden-sdk/vite-plugin": "^0.13.0"
48
+ "@miden-sdk/vite-plugin": "<matches miden-sdk>"
48
49
  }
49
50
  }
50
51
  ```
51
52
 
53
+ Notes:
54
+ - **Always check `package.json` for the authoritative versions** — this skill intentionally doesn't inline them because they shift across SDK releases.
55
+ - The wallet adapter packages are versioned separately from the core SDK. Their `peerDependencies` typically allow `^<major>.<minor>.x`, so a patch-level gap between the adapter and the core SDK is expected and fine.
56
+ - When you bump, clean-install: `rm -rf node_modules yarn.lock && yarn install`. Vite's dep optimizer caches resolved SDK paths, and stale caches can surface as `ERR_BLOCKED_BY_RESPONSE` or spurious `Failed to fetch` errors on module workers.
57
+
52
58
  ## Production Deployment Headers
53
59
 
54
60
  COOP/COEP headers must be set on the production server. `midenVitePlugin({ crossOriginIsolation: true })` only affects the Vite dev server.
@@ -119,10 +125,10 @@ Standard Vite-compatible tsconfig settings work with Miden. The only actual cons
119
125
 
120
126
  | Issue | Cause | Fix |
121
127
  |-------|-------|-----|
122
- | "SharedArrayBuffer is not defined" | Missing COOP/COEP headers | Use `midenVitePlugin({ crossOriginIsolation: true })` in dev; set headers on production server |
128
+ | "SharedArrayBuffer is not defined" | COOP/COEP headers not reaching the browser | Verify `midenVitePlugin({ crossOriginIsolation: true })` is in plugins; check production server headers separately |
123
129
  | WASM module not found | SDK not configured correctly | Ensure `midenVitePlugin()` is in plugins array |
124
130
  | "Top-level await not supported" | Missing plugin setup | Ensure `midenVitePlugin()` is in plugins array |
125
- | WASM init hangs | COEP blocking WASM fetch | Check network tab for blocked requests; enable `crossOriginIsolation` |
131
+ | WASM init hangs | COEP blocking WASM fetch | Check network tab for blocked requests; verify COOP/COEP headers are present |
126
132
  | Build succeeds but WASM fails at runtime | Wrong MIME type | Serve .wasm as application/wasm |
127
133
  | "recursive use of an object" | Concurrent WASM access | Use runExclusive() from useMiden() |
128
134
  | Double initialization in dev | React StrictMode | Use MidenProvider (handles this internally) |