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.
- package/package.json +1 -1
- package/template/.claude/commands/review-security.md +67 -0
- package/template/.claude/settings.json +1 -7
- package/template/.claude/settings.local.json +24 -0
- package/template/.claude/skills/frontend-pitfalls/SKILL.md +28 -31
- package/template/.claude/skills/frontend-source-guide/SKILL.md +14 -14
- package/template/.claude/skills/miden-concepts/SKILL.md +4 -2
- package/template/.claude/skills/react-sdk-patterns/SKILL.md +294 -28
- package/template/.claude/skills/signer-integration/SKILL.md +22 -3
- package/template/.claude/skills/testing-patterns/SKILL.md +201 -40
- package/template/.claude/skills/vite-wasm-setup/SKILL.md +20 -14
- package/template/.claude/skills/web-client-usage/SKILL.md +454 -0
- package/template/.env.example +15 -2
- package/template/.mcp.json +9 -0
- package/template/CLAUDE.md +49 -16
- package/template/README.md +85 -19
- package/template/package.json +5 -4
- package/template/public/packages/counter_account.masp +0 -0
- package/template/public/packages/increment_note.masp +0 -0
- package/template/src/__tests__/fixtures/accounts.ts +17 -6
- package/template/src/__tests__/fixtures/index.ts +1 -0
- package/template/src/__tests__/mocks/miden-sdk-react.ts +18 -1
- package/template/src/__tests__/patterns/mutation-hook.test.tsx +2 -2
- package/template/src/__tests__/patterns/provider-setup.test.tsx +2 -0
- package/template/src/components/AppContent.tsx +33 -3
- package/template/{create-miden-app/template/src/components/Counter.tsx → src/components/ConfiguredCounter.tsx} +7 -4
- package/template/src/components/Counter.tsx +12 -41
- package/template/src/components/__tests__/AppContent.test.tsx +192 -4
- package/template/src/components/__tests__/ConfiguredCounter.test.tsx +116 -0
- package/template/src/components/__tests__/Counter.test.tsx +24 -94
- package/template/src/config.ts +26 -6
- package/template/src/hooks/__tests__/useIncrementCounter.test.tsx +257 -0
- package/template/src/hooks/useIncrementCounter.ts +109 -50
- package/template/src/providers.tsx +20 -24
- package/template/vite.config.ts +1 -1
- package/template/vitest.config.ts +1 -2
- package/template/yarn.lock +761 -688
- package/template/create-miden-app/template/.claude/hooks/typecheck.sh +0 -27
- package/template/create-miden-app/template/.claude/settings.json +0 -17
- package/template/create-miden-app/template/.claude/skills/frontend-pitfalls/SKILL.md +0 -189
- package/template/create-miden-app/template/.claude/skills/frontend-source-guide/SKILL.md +0 -163
- package/template/create-miden-app/template/.claude/skills/miden-concepts/SKILL.md +0 -108
- package/template/create-miden-app/template/.claude/skills/react-sdk-patterns/SKILL.md +0 -294
- package/template/create-miden-app/template/.claude/skills/signer-integration/SKILL.md +0 -158
- package/template/create-miden-app/template/.claude/skills/vite-wasm-setup/SKILL.md +0 -128
- package/template/create-miden-app/template/.env.example +0 -5
- package/template/create-miden-app/template/CLAUDE.md +0 -116
- package/template/create-miden-app/template/README.md +0 -61
- package/template/create-miden-app/template/eslint.config.js +0 -23
- package/template/create-miden-app/template/index.html +0 -13
- package/template/create-miden-app/template/package.json +0 -34
- package/template/create-miden-app/template/public/vite.svg +0 -1
- package/template/create-miden-app/template/src/App.tsx +0 -10
- package/template/create-miden-app/template/src/assets/miden.svg +0 -3
- package/template/create-miden-app/template/src/assets/react.svg +0 -1
- package/template/create-miden-app/template/src/components/AppContent.css +0 -45
- package/template/create-miden-app/template/src/components/AppContent.tsx +0 -50
- package/template/create-miden-app/template/src/components/Counter.css +0 -27
- package/template/create-miden-app/template/src/config.ts +0 -21
- package/template/create-miden-app/template/src/hooks/useIncrementCounter.ts +0 -136
- package/template/create-miden-app/template/src/index.css +0 -75
- package/template/create-miden-app/template/src/lib/miden.ts +0 -9
- package/template/create-miden-app/template/src/main.tsx +0 -10
- package/template/create-miden-app/template/src/providers.tsx +0 -31
- package/template/create-miden-app/template/src/vite-env.d.ts +0 -1
- package/template/create-miden-app/template/tsconfig.app.json +0 -32
- package/template/create-miden-app/template/tsconfig.json +0 -7
- package/template/create-miden-app/template/tsconfig.node.json +0 -24
- package/template/create-miden-app/template/vite.config.ts +0 -17
- 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**
|
|
11
|
-
- **@testing-library/react**
|
|
12
|
-
- **@testing-library/user-event**
|
|
13
|
-
- **@testing-library/jest-dom**
|
|
14
|
-
- **jsdom**
|
|
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()`
|
|
47
|
-
- `useAccount()`
|
|
48
|
-
- `useNotes()`
|
|
49
|
-
- `useSyncState()`
|
|
50
|
-
- `useAssetMetadata()`
|
|
51
|
-
- `useMiden()`
|
|
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()`
|
|
55
|
-
- `useMint()`, `useConsume()`, `useSwap()`, `useTransaction()`
|
|
56
|
-
- `useCreateWallet()`
|
|
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: {
|
|
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, // "
|
|
89
|
-
WALLET_ID_2, // "
|
|
90
|
-
FAUCET_ID, // "
|
|
91
|
-
COUNTER_ID, // "
|
|
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
|
-
|
|
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
|
|
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**
|
|
120
|
-
2. **Loading state**
|
|
121
|
-
3. **Error state**
|
|
122
|
-
4. **User interactions**
|
|
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
|
-
|
|
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
|
|
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/
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
-
|
|
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**
|
|
146
|
-
2. **PostToolUse: affected tests**
|
|
147
|
-
3. **
|
|
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 →
|
|
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
|
-
`
|
|
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
|
-
|
|
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
|
|
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** —
|
|
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
|
|
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": "
|
|
44
|
-
"@miden-sdk/miden-sdk": "
|
|
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": "
|
|
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" |
|
|
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;
|
|
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) |
|