@pasosdejesus/m 0.24.0 → 0.25.0

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 (53) hide show
  1. package/dist/CHANGELOG.md +16 -1
  2. package/dist/i18n/locales/cli.d.ts.map +1 -1
  3. package/dist/i18n/locales/cli.js +2 -0
  4. package/dist/i18n/locales/cli.js.map +1 -1
  5. package/dist/m.js +7 -1
  6. package/dist/m.js.map +1 -1
  7. package/dist/tasks/e2e-summary.d.ts +13 -0
  8. package/dist/tasks/e2e-summary.d.ts.map +1 -0
  9. package/dist/tasks/e2e-summary.js +117 -0
  10. package/dist/tasks/e2e-summary.js.map +1 -0
  11. package/dist/tasks/e2e.d.ts +29 -0
  12. package/dist/tasks/e2e.d.ts.map +1 -1
  13. package/dist/tasks/e2e.js +130 -36
  14. package/dist/tasks/e2e.js.map +1 -1
  15. package/dist/tasks/e2e.test.d.ts +2 -0
  16. package/dist/tasks/e2e.test.d.ts.map +1 -0
  17. package/dist/tasks/e2e.test.js +108 -0
  18. package/dist/tasks/e2e.test.js.map +1 -0
  19. package/dist/wallet/next/components/InAppWalletSetup.d.ts +11 -0
  20. package/dist/wallet/next/components/InAppWalletSetup.d.ts.map +1 -0
  21. package/dist/wallet/next/components/InAppWalletSetup.js +55 -0
  22. package/dist/wallet/next/components/InAppWalletSetup.js.map +1 -0
  23. package/dist/wallet/next/components/InAppWalletUnlock.d.ts +9 -0
  24. package/dist/wallet/next/components/InAppWalletUnlock.d.ts.map +1 -0
  25. package/dist/wallet/next/components/InAppWalletUnlock.js +29 -0
  26. package/dist/wallet/next/components/InAppWalletUnlock.js.map +1 -0
  27. package/dist/wallet/next/components.test.d.ts +2 -0
  28. package/dist/wallet/next/components.test.d.ts.map +1 -0
  29. package/dist/wallet/next/components.test.js +122 -0
  30. package/dist/wallet/next/components.test.js.map +1 -0
  31. package/dist/wallet/next/i18n.d.ts +63 -0
  32. package/dist/wallet/next/i18n.d.ts.map +1 -0
  33. package/dist/wallet/next/i18n.js +64 -0
  34. package/dist/wallet/next/i18n.js.map +1 -0
  35. package/dist/wallet/next/index.d.ts +7 -0
  36. package/dist/wallet/next/index.d.ts.map +1 -0
  37. package/dist/wallet/next/index.js +12 -0
  38. package/dist/wallet/next/index.js.map +1 -0
  39. package/dist/wallet/next/namespaces.d.ts +17 -0
  40. package/dist/wallet/next/namespaces.d.ts.map +1 -0
  41. package/dist/wallet/next/namespaces.js +17 -0
  42. package/dist/wallet/next/namespaces.js.map +1 -0
  43. package/dist/wallet/next/useInAppWallet.d.ts +55 -0
  44. package/dist/wallet/next/useInAppWallet.d.ts.map +1 -0
  45. package/dist/wallet/next/useInAppWallet.js +261 -0
  46. package/dist/wallet/next/useInAppWallet.js.map +1 -0
  47. package/dist/wallet/next/useInAppWallet.test.d.ts +2 -0
  48. package/dist/wallet/next/useInAppWallet.test.d.ts.map +1 -0
  49. package/dist/wallet/next/useInAppWallet.test.js +261 -0
  50. package/dist/wallet/next/useInAppWallet.test.js.map +1 -0
  51. package/package.json +5 -1
  52. package/src/e2e/README.md +16 -0
  53. package/src/wallet/README.md +25 -2
@@ -0,0 +1,261 @@
1
+ 'use client';
2
+ import { useCallback, useEffect, useSyncExternalStore } from 'react';
3
+ import { createWallet, deleteWallet, detectPlatformSupport, disableBiometricUnlock, emitAccountsChanged, enableBiometricUnlock, getInAppWalletProvider, getWalletInfo, hasBiometricUnlock, hasWallet, importWallet, lockWallet, unlockWallet, unlockWithBiometric as unlockWithBiometricCore, } from '../index.js';
4
+ /**
5
+ * Auto-lock after this much inactivity, like OneKey and OKX Web3 (R-#246).
6
+ * Dropping the key means the next action asks for the gesture (or the password on
7
+ * devices without WebAuthn).
8
+ *
9
+ * One hour, not ten minutes: the operator's mobile test had to unlock again
10
+ * while donating on `/[lang]/gdcluster/ranking` less than ten minutes after
11
+ * unlocking, and OneKey waits about an hour after its own fingerprint prompt.
12
+ * The value only bounds how long the in-memory key survives a pause; moving
13
+ * funds is always gated by layer L1 (a fresh gesture), so a longer pause cannot
14
+ * make a transfer silent. An operator or the user changing the value is
15
+ * https://github.com/pasosdeJesus/learn.tg/issues/248.
16
+ */
17
+ export const INACTIVITY_LOCK_MS = 60 * 60 * 1000;
18
+ // Distinto de `USER_VERIFICATION_GRACE_MS` del core (15 min, R-#253): el auto-lock
19
+ // es por inactividad (una hora) y, al llamar a `lockWallet()`, cierra la ventana de
20
+ // gracia para que el próximo movimiento vuelva a pedir el gesto.
21
+ /**
22
+ * Minimum time between two inactivity resets. Activity events arrive in bursts
23
+ * (a scroll fires dozens of them), and restarting the timer on each one is
24
+ * wasted work on the low-end phones this audience uses.
25
+ */
26
+ const ACTIVITY_RESET_THROTTLE_MS = 5 * 1000;
27
+ const initialState = {
28
+ status: 'loading',
29
+ walletInfo: null,
30
+ error: null,
31
+ biometricAvailable: false,
32
+ biometricEnabled: false,
33
+ lockReason: 'initial',
34
+ };
35
+ // Estado compartido por todas las instancias del hook. Sin esto, cada componente
36
+ // que llama a `useInAppWallet()` tiene su propia copia: `InAppWalletSetup`
37
+ // desbloquea la billetera y el consumidor (p. ej. `WalletSelector`) sigue viendo
38
+ // `locked`, de modo que la interfaz parece no hacer nada.
39
+ let state = initialState;
40
+ let initialized = false;
41
+ const listeners = new Set();
42
+ function setState(partial) {
43
+ state = { ...state, ...partial };
44
+ for (const listener of listeners)
45
+ listener();
46
+ }
47
+ function subscribe(listener) {
48
+ listeners.add(listener);
49
+ return () => {
50
+ listeners.delete(listener);
51
+ };
52
+ }
53
+ function getSnapshot() {
54
+ return state;
55
+ }
56
+ function getServerSnapshot() {
57
+ return initialState;
58
+ }
59
+ /** Reads the stored wallet once per page load, whoever asks first. */
60
+ async function initialize() {
61
+ if (initialized)
62
+ return;
63
+ initialized = true;
64
+ if (typeof window === 'undefined' || !window.indexedDB) {
65
+ setState({ status: 'no-wallet', walletInfo: null });
66
+ return;
67
+ }
68
+ try {
69
+ const exists = await hasWallet();
70
+ if (!exists) {
71
+ setState({ status: 'no-wallet', walletInfo: null });
72
+ return;
73
+ }
74
+ const info = await getWalletInfo();
75
+ // R-#246: the unlock lives in module memory. A reload locks the wallet again,
76
+ // which is why the biometric path exists: one gesture instead of the password.
77
+ let biometricEnabled = false;
78
+ try {
79
+ biometricEnabled = await hasBiometricUnlock();
80
+ }
81
+ catch {
82
+ biometricEnabled = false;
83
+ }
84
+ let biometricAvailable = false;
85
+ if (biometricEnabled) {
86
+ biometricAvailable = true;
87
+ }
88
+ else {
89
+ try {
90
+ const support = await detectPlatformSupport();
91
+ // `prf: null` means "the engine did not say": we only know for sure after
92
+ // creating a credential, so treat it as possibly available.
93
+ biometricAvailable = support.userVerifying && support.prf !== false;
94
+ }
95
+ catch {
96
+ biometricAvailable = false;
97
+ }
98
+ }
99
+ setState({
100
+ walletInfo: info,
101
+ status: state.status === 'unlocked' ? 'unlocked' : 'locked',
102
+ biometricEnabled,
103
+ biometricAvailable,
104
+ });
105
+ }
106
+ catch {
107
+ setState({ status: 'no-wallet', walletInfo: null });
108
+ }
109
+ }
110
+ /** Test-only: forgets the shared state and the "already initialized" flag. */
111
+ export function resetInAppWalletStoreForTests() {
112
+ state = initialState;
113
+ initialized = false;
114
+ for (const listener of listeners)
115
+ listener();
116
+ }
117
+ export function useInAppWallet() {
118
+ const snapshot = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
119
+ useEffect(() => {
120
+ void initialize();
121
+ }, []);
122
+ const create = useCallback(async (password) => {
123
+ setState({ error: null });
124
+ try {
125
+ const result = await createWallet({ password });
126
+ setState({ walletInfo: result.walletInfo, status: 'unlocked' });
127
+ return result;
128
+ }
129
+ catch (e) {
130
+ setState({ error: e instanceof Error ? e.message : String(e) });
131
+ throw e;
132
+ }
133
+ }, []);
134
+ const importExisting = useCallback(async (options) => {
135
+ setState({ error: null });
136
+ try {
137
+ const info = await importWallet(options);
138
+ setState({ walletInfo: info, status: 'unlocked' });
139
+ return info;
140
+ }
141
+ catch (e) {
142
+ setState({ error: e instanceof Error ? e.message : String(e) });
143
+ throw e;
144
+ }
145
+ }, []);
146
+ const unlock = useCallback(async (password) => {
147
+ setState({ error: null });
148
+ try {
149
+ const info = await unlockWallet(password);
150
+ setState({ walletInfo: info, status: 'unlocked' });
151
+ return info;
152
+ }
153
+ catch (e) {
154
+ setState({ error: e instanceof Error ? e.message : String(e) });
155
+ throw e;
156
+ }
157
+ }, []);
158
+ const unlockWithBiometric = useCallback(async () => {
159
+ setState({ error: null });
160
+ try {
161
+ const info = await unlockWithBiometricCore();
162
+ setState({ walletInfo: info, status: 'unlocked' });
163
+ return info;
164
+ }
165
+ catch (e) {
166
+ setState({ error: e instanceof Error ? e.message : String(e) });
167
+ throw e;
168
+ }
169
+ }, []);
170
+ const enableBiometric = useCallback(async (password) => {
171
+ setState({ error: null });
172
+ try {
173
+ await enableBiometricUnlock(password);
174
+ setState({ biometricEnabled: true, biometricAvailable: true });
175
+ }
176
+ catch (e) {
177
+ setState({ error: e instanceof Error ? e.message : String(e) });
178
+ throw e;
179
+ }
180
+ }, []);
181
+ const disableBiometric = useCallback(async () => {
182
+ setState({ error: null });
183
+ try {
184
+ await disableBiometricUnlock();
185
+ setState({ biometricEnabled: false });
186
+ }
187
+ catch (e) {
188
+ setState({ error: e instanceof Error ? e.message : String(e) });
189
+ throw e;
190
+ }
191
+ }, []);
192
+ const lock = useCallback(async () => {
193
+ await lockWallet();
194
+ // R-#236: quien esté suscrito al proveedor se entera de que la sesión terminó.
195
+ emitAccountsChanged([]);
196
+ setState({ status: 'locked', lockReason: 'user' });
197
+ }, []);
198
+ const remove = useCallback(async () => {
199
+ await deleteWallet();
200
+ emitAccountsChanged([]);
201
+ setState({ walletInfo: null, status: 'no-wallet', biometricEnabled: false, lockReason: 'deleted' });
202
+ }, []);
203
+ // R-#246: auto-lock tras inactividad (como OneKey y OKX). Suelta la clave en
204
+ // memoria; NO cierra la sesión (el motivo lo distingue `WalletEventListener`).
205
+ // `visibilitychange` se escucha en `document` (no burbujea hasta `window`, que
206
+ // era el defecto que dejaba el temporizador corriendo en el teléfono), y el
207
+ // reinicio se limita a uno cada pocos segundos para que `scroll` no lo agite.
208
+ useEffect(() => {
209
+ if (snapshot.status !== 'unlocked')
210
+ return;
211
+ const lockForIdle = () => {
212
+ void lockWallet();
213
+ emitAccountsChanged([]);
214
+ setState({ status: 'locked', lockReason: 'idle' });
215
+ };
216
+ let timer = window.setTimeout(lockForIdle, INACTIVITY_LOCK_MS);
217
+ let lastReset = Date.now();
218
+ const reset = () => {
219
+ const now = Date.now();
220
+ if (now - lastReset < ACTIVITY_RESET_THROTTLE_MS)
221
+ return;
222
+ lastReset = now;
223
+ window.clearTimeout(timer);
224
+ timer = window.setTimeout(lockForIdle, INACTIVITY_LOCK_MS);
225
+ };
226
+ const events = ['pointerdown', 'keydown', 'touchstart', 'click', 'scroll'];
227
+ for (const name of events) {
228
+ window.addEventListener(name, reset, { passive: true });
229
+ }
230
+ const onVisibilityChange = () => {
231
+ lastReset = 0;
232
+ reset();
233
+ };
234
+ document.addEventListener('visibilitychange', onVisibilityChange);
235
+ return () => {
236
+ window.clearTimeout(timer);
237
+ for (const name of events)
238
+ window.removeEventListener(name, reset);
239
+ document.removeEventListener('visibilitychange', onVisibilityChange);
240
+ };
241
+ }, [snapshot.status]);
242
+ const getProvider = useCallback((rpcUrl) => getInAppWalletProvider({ rpcUrl }), []);
243
+ return {
244
+ status: snapshot.status,
245
+ walletInfo: snapshot.walletInfo,
246
+ error: snapshot.error,
247
+ biometricAvailable: snapshot.biometricAvailable,
248
+ biometricEnabled: snapshot.biometricEnabled,
249
+ lockReason: snapshot.lockReason,
250
+ create,
251
+ importExisting,
252
+ unlock,
253
+ unlockWithBiometric,
254
+ enableBiometric,
255
+ disableBiometric,
256
+ lock,
257
+ remove,
258
+ getProvider,
259
+ };
260
+ }
261
+ //# sourceMappingURL=useInAppWallet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useInAppWallet.js","sourceRoot":"","sources":["../../../src/wallet/next/useInAppWallet.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=useInAppWallet.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useInAppWallet.test.d.ts","sourceRoot":"","sources":["../../../src/wallet/next/useInAppWallet.test.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,261 @@
1
+ // @vitest-environment jsdom
2
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
+ import { act, cleanup, renderHook, waitFor } from '@testing-library/react';
4
+ afterEach(cleanup);
5
+ const walletMock = vi.hoisted(() => ({
6
+ hasWallet: vi.fn(),
7
+ getWalletInfo: vi.fn(),
8
+ createWallet: vi.fn(),
9
+ importWallet: vi.fn(),
10
+ unlockWallet: vi.fn(),
11
+ unlockWithBiometric: vi.fn(),
12
+ enableBiometricUnlock: vi.fn(),
13
+ disableBiometricUnlock: vi.fn(),
14
+ hasBiometricUnlock: vi.fn(),
15
+ detectPlatformSupport: vi.fn(),
16
+ lockWallet: vi.fn(),
17
+ deleteWallet: vi.fn(),
18
+ getInAppWalletProvider: vi.fn(),
19
+ // R-#236: avisa a quien esté suscrito al proveedor cuando la billetera se bloquea.
20
+ emitAccountsChanged: vi.fn(),
21
+ setWalletDbName: vi.fn(),
22
+ setDestinationsPrefix: vi.fn(),
23
+ setRpName: vi.fn(),
24
+ }));
25
+ vi.mock('../index.js', () => walletMock);
26
+ import { useInAppWallet, resetInAppWalletStoreForTests, INACTIVITY_LOCK_MS } from './useInAppWallet.js';
27
+ const ADDRESS = '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266';
28
+ const INFO = { address: ADDRESS, chain: 'celoSepolia', createdAt: 1 };
29
+ describe('useInAppWallet', () => {
30
+ beforeEach(() => {
31
+ vi.clearAllMocks();
32
+ resetInAppWalletStoreForTests();
33
+ walletMock.hasBiometricUnlock.mockResolvedValue(false);
34
+ walletMock.detectPlatformSupport.mockResolvedValue({ webauthn: false, userVerifying: false, prf: null });
35
+ globalThis.indexedDB = {};
36
+ window.indexedDB = {};
37
+ });
38
+ it('reports no-wallet when there is nothing stored', async () => {
39
+ walletMock.hasWallet.mockResolvedValue(false);
40
+ const { result } = renderHook(() => useInAppWallet());
41
+ await waitFor(() => expect(result.current.status).toBe('no-wallet'));
42
+ expect(result.current.walletInfo).toBeNull();
43
+ });
44
+ it('reports locked when a wallet exists', async () => {
45
+ walletMock.hasWallet.mockResolvedValue(true);
46
+ walletMock.getWalletInfo.mockResolvedValue(INFO);
47
+ const { result } = renderHook(() => useInAppWallet());
48
+ await waitFor(() => expect(result.current.status).toBe('locked'));
49
+ expect(result.current.walletInfo?.address).toBe(ADDRESS);
50
+ });
51
+ it('reports the biometric capability of the device', async () => {
52
+ walletMock.hasWallet.mockResolvedValue(true);
53
+ walletMock.getWalletInfo.mockResolvedValue(INFO);
54
+ walletMock.detectPlatformSupport.mockResolvedValue({ webauthn: true, userVerifying: true, prf: true });
55
+ const { result } = renderHook(() => useInAppWallet());
56
+ await waitFor(() => expect(result.current.status).toBe('locked'));
57
+ expect(result.current.biometricAvailable).toBe(true);
58
+ expect(result.current.biometricEnabled).toBe(false);
59
+ });
60
+ it('stays locked and password-only on devices without WebAuthn', async () => {
61
+ walletMock.hasWallet.mockResolvedValue(true);
62
+ walletMock.getWalletInfo.mockResolvedValue(INFO);
63
+ walletMock.detectPlatformSupport.mockResolvedValue({ webauthn: false, userVerifying: false, prf: null });
64
+ const { result } = renderHook(() => useInAppWallet());
65
+ await waitFor(() => expect(result.current.status).toBe('locked'));
66
+ expect(result.current.biometricAvailable).toBe(false);
67
+ });
68
+ it('reports biometric as enabled when a sealed key exists', async () => {
69
+ walletMock.hasWallet.mockResolvedValue(true);
70
+ walletMock.getWalletInfo.mockResolvedValue(INFO);
71
+ walletMock.hasBiometricUnlock.mockResolvedValue(true);
72
+ const { result } = renderHook(() => useInAppWallet());
73
+ await waitFor(() => expect(result.current.status).toBe('locked'));
74
+ expect(result.current.biometricEnabled).toBe(true);
75
+ expect(result.current.biometricAvailable).toBe(true);
76
+ });
77
+ it('unlocks with one gesture', async () => {
78
+ walletMock.hasWallet.mockResolvedValue(true);
79
+ walletMock.getWalletInfo.mockResolvedValue(INFO);
80
+ walletMock.hasBiometricUnlock.mockResolvedValue(true);
81
+ walletMock.unlockWithBiometric.mockResolvedValue(INFO);
82
+ const { result } = renderHook(() => useInAppWallet());
83
+ await waitFor(() => expect(result.current.status).toBe('locked'));
84
+ await act(async () => {
85
+ await result.current.unlockWithBiometric();
86
+ });
87
+ expect(result.current.status).toBe('unlocked');
88
+ expect(result.current.walletInfo?.address).toBe(ADDRESS);
89
+ });
90
+ it('records the error and keeps the wallet locked when the gesture fails', async () => {
91
+ walletMock.hasWallet.mockResolvedValue(true);
92
+ walletMock.getWalletInfo.mockResolvedValue(INFO);
93
+ walletMock.unlockWithBiometric.mockRejectedValue(new Error('auth-failed'));
94
+ const { result } = renderHook(() => useInAppWallet());
95
+ await waitFor(() => expect(result.current.status).toBe('locked'));
96
+ await act(async () => {
97
+ await expect(result.current.unlockWithBiometric()).rejects.toThrow('auth-failed');
98
+ });
99
+ expect(result.current.status).toBe('locked');
100
+ expect(result.current.error).toBe('auth-failed');
101
+ });
102
+ it('enables and disables the biometric unlock', async () => {
103
+ walletMock.hasWallet.mockResolvedValue(true);
104
+ walletMock.getWalletInfo.mockResolvedValue(INFO);
105
+ const { result } = renderHook(() => useInAppWallet());
106
+ await waitFor(() => expect(result.current.status).toBe('locked'));
107
+ await act(async () => {
108
+ await result.current.enableBiometric('123456');
109
+ });
110
+ expect(walletMock.enableBiometricUnlock).toHaveBeenCalledWith('123456');
111
+ expect(result.current.biometricEnabled).toBe(true);
112
+ await act(async () => {
113
+ await result.current.disableBiometric();
114
+ });
115
+ expect(result.current.biometricEnabled).toBe(false);
116
+ });
117
+ it('goes to unlocked after creating a wallet', async () => {
118
+ walletMock.hasWallet.mockResolvedValue(false);
119
+ walletMock.createWallet.mockResolvedValue({ walletInfo: INFO, mnemonic: 'one two three' });
120
+ const { result } = renderHook(() => useInAppWallet());
121
+ await waitFor(() => expect(result.current.status).toBe('no-wallet'));
122
+ await act(async () => {
123
+ await result.current.create('123456');
124
+ });
125
+ expect(result.current.status).toBe('unlocked');
126
+ expect(result.current.walletInfo?.address).toBe(ADDRESS);
127
+ });
128
+ it('locks the wallet after inactivity and marks the reason as idle', async () => {
129
+ vi.useFakeTimers();
130
+ try {
131
+ walletMock.hasWallet.mockResolvedValue(true);
132
+ walletMock.getWalletInfo.mockResolvedValue(INFO);
133
+ walletMock.unlockWallet.mockResolvedValue(INFO);
134
+ const { result } = renderHook(() => useInAppWallet());
135
+ await act(async () => {
136
+ await vi.advanceTimersByTimeAsync(0);
137
+ });
138
+ await act(async () => {
139
+ await result.current.unlock('123456');
140
+ });
141
+ expect(result.current.status).toBe('unlocked');
142
+ await act(async () => {
143
+ await vi.advanceTimersByTimeAsync(INACTIVITY_LOCK_MS + 1);
144
+ });
145
+ expect(walletMock.lockWallet).toHaveBeenCalled();
146
+ expect(result.current.status).toBe('locked');
147
+ expect(result.current.lockReason).toBe('idle');
148
+ }
149
+ finally {
150
+ vi.useRealTimers();
151
+ }
152
+ });
153
+ it('restarts the inactivity timer when the tab becomes visible again', async () => {
154
+ vi.useFakeTimers();
155
+ try {
156
+ walletMock.hasWallet.mockResolvedValue(true);
157
+ walletMock.getWalletInfo.mockResolvedValue(INFO);
158
+ walletMock.unlockWallet.mockResolvedValue(INFO);
159
+ const { result } = renderHook(() => useInAppWallet());
160
+ await act(async () => {
161
+ await vi.advanceTimersByTimeAsync(0);
162
+ });
163
+ await act(async () => {
164
+ await result.current.unlock('123456');
165
+ });
166
+ await act(async () => {
167
+ await vi.advanceTimersByTimeAsync(INACTIVITY_LOCK_MS - 1000);
168
+ });
169
+ document.dispatchEvent(new Event('visibilitychange'));
170
+ await act(async () => {
171
+ await vi.advanceTimersByTimeAsync(2000);
172
+ });
173
+ expect(result.current.status).toBe('unlocked');
174
+ await act(async () => {
175
+ await vi.advanceTimersByTimeAsync(INACTIVITY_LOCK_MS);
176
+ });
177
+ expect(result.current.status).toBe('locked');
178
+ }
179
+ finally {
180
+ vi.useRealTimers();
181
+ }
182
+ });
183
+ it('marks the reason as user when the header ✕ locks it', async () => {
184
+ walletMock.hasWallet.mockResolvedValue(true);
185
+ walletMock.getWalletInfo.mockResolvedValue(INFO);
186
+ walletMock.unlockWallet.mockResolvedValue(INFO);
187
+ const { result } = renderHook(() => useInAppWallet());
188
+ await waitFor(() => expect(result.current.status).toBe('locked'));
189
+ await act(async () => {
190
+ await result.current.unlock('123456');
191
+ });
192
+ await act(async () => {
193
+ await result.current.lock();
194
+ });
195
+ expect(result.current.lockReason).toBe('user');
196
+ });
197
+ it('goes to unlocked after importing and back to locked after locking', async () => {
198
+ walletMock.hasWallet.mockResolvedValue(false);
199
+ walletMock.importWallet.mockResolvedValue(INFO);
200
+ const { result } = renderHook(() => useInAppWallet());
201
+ await waitFor(() => expect(result.current.status).toBe('no-wallet'));
202
+ await act(async () => {
203
+ await result.current.importExisting({ mnemonic: 'one two three', password: '123456' });
204
+ });
205
+ expect(result.current.status).toBe('unlocked');
206
+ expect(walletMock.importWallet).toHaveBeenCalledWith({ mnemonic: 'one two three', password: '123456' });
207
+ await act(async () => {
208
+ await result.current.lock();
209
+ });
210
+ expect(result.current.status).toBe('locked');
211
+ expect(result.current.walletInfo?.address).toBe(ADDRESS);
212
+ });
213
+ it('records the error and rethrows when unlocking fails', async () => {
214
+ walletMock.hasWallet.mockResolvedValue(true);
215
+ walletMock.getWalletInfo.mockResolvedValue(INFO);
216
+ walletMock.unlockWallet.mockRejectedValue(new Error('Wrong password or corrupted wallet data'));
217
+ const { result } = renderHook(() => useInAppWallet());
218
+ await waitFor(() => expect(result.current.status).toBe('locked'));
219
+ await act(async () => {
220
+ await expect(result.current.unlock('000000')).rejects.toThrow(/Wrong password/);
221
+ });
222
+ expect(result.current.error).toMatch(/Wrong password/);
223
+ });
224
+ it('takes the provider from the core package', async () => {
225
+ walletMock.hasWallet.mockResolvedValue(false);
226
+ const provider = { request: vi.fn() };
227
+ walletMock.getInAppWalletProvider.mockReturnValue(provider);
228
+ const { result } = renderHook(() => useInAppWallet());
229
+ await waitFor(() => expect(result.current.status).toBe('no-wallet'));
230
+ expect(result.current.getProvider()).toBe(provider);
231
+ });
232
+ it('removes the wallet', async () => {
233
+ walletMock.hasWallet.mockResolvedValue(true);
234
+ walletMock.getWalletInfo.mockResolvedValue(INFO);
235
+ const { result } = renderHook(() => useInAppWallet());
236
+ await waitFor(() => expect(result.current.status).toBe('locked'));
237
+ await act(async () => {
238
+ await result.current.remove();
239
+ });
240
+ expect(walletMock.deleteWallet).toHaveBeenCalled();
241
+ expect(result.current.status).toBe('no-wallet');
242
+ });
243
+ it('shares the state between instances (setup and consumer agree)', async () => {
244
+ walletMock.hasWallet.mockResolvedValue(false);
245
+ walletMock.createWallet.mockResolvedValue({ walletInfo: INFO, mnemonic: 'one two three' });
246
+ const setup = renderHook(() => useInAppWallet());
247
+ const consumer = renderHook(() => useInAppWallet());
248
+ await waitFor(() => expect(consumer.result.current.status).toBe('no-wallet'));
249
+ await act(async () => {
250
+ await setup.result.current.create('123456');
251
+ });
252
+ expect(setup.result.current.status).toBe('unlocked');
253
+ expect(consumer.result.current.status).toBe('unlocked');
254
+ expect(consumer.result.current.walletInfo?.address).toBe(ADDRESS);
255
+ await act(async () => {
256
+ await consumer.result.current.lock();
257
+ });
258
+ expect(setup.result.current.status).toBe('locked');
259
+ });
260
+ });
261
+ //# sourceMappingURL=useInAppWallet.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useInAppWallet.test.js","sourceRoot":"","sources":["../../../src/wallet/next/useInAppWallet.test.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pasosdejesus/m",
3
- "version": "0.24.0",
3
+ "version": "0.25.0",
4
4
  "description": "Motor para crear y gestionar proyectos al estilo Pasos de Jesus.",
5
5
  "author": "",
6
6
  "license": "ISC",
@@ -108,6 +108,10 @@
108
108
  "import": "./dist/wallet/index.js",
109
109
  "types": "./dist/wallet/index.d.ts"
110
110
  },
111
+ "./wallet/next": {
112
+ "import": "./dist/wallet/next/index.js",
113
+ "types": "./dist/wallet/next/index.d.ts"
114
+ },
111
115
  "./wallet/storage": {
112
116
  "import": "./dist/wallet/storage/index.js",
113
117
  "types": "./dist/wallet/storage/index.d.ts"
package/src/e2e/README.md CHANGED
@@ -151,6 +151,22 @@ Lessons that cost time here (see also learn.tg's `doc/e2e-testing.md`):
151
151
  - **Receipt hashes**: the local node re-signs, so `setupFullWalletMock` re-keys the receipt
152
152
  under `keccak256(rawTransaction)` (the hash viem waits for).
153
153
 
154
+ ### Runner: retry and summary (`m/REQ/49` P3/P4)
155
+
156
+ ```sh
157
+ ./bin/m test:e2e --retry 2 --retry-delay 15000 # re-run failed specs (fresh WALLET_INDEX each)
158
+ ./bin/m test:e2e:summary /tmp/site.log /tmp/local.log # compare two runs
159
+ ```
160
+
161
+ - `--retry <N>` (default 0, or `E2E_RETRIES`): after the first pass, each failed spec is re-run
162
+ on its own up to N times; a spec is green **only by exit code**.
163
+ - `--retry-delay <ms>` (default 15000): pause before each retry round.
164
+ - `E2E_LOG_DIR` (default `/tmp`): the run writes `<dir>/e2e-<timestamp>.log` with one
165
+ `[e2e-result] <file> <ok|fail|skip>` line per spec per attempt (the machine-readable format).
166
+ - `test:e2e:summary <log…>` reads those lines (or the human format for older logs) and prints a
167
+ row per spec, one column per log, marking `<-- difiere` when a spec's state changes between
168
+ runs (environmental vs product failure).
169
+
154
170
  ## Environment Variables
155
171
 
156
172
  See [doc/e2e.md](../../../../doc/e2e.md) for full reference.
@@ -68,6 +68,29 @@ setDestinationsPrefix('learn.tg.pdj-wallet.') // localStorage
68
68
  setRpName('learn.tg') // nombre de la credencial WebAuthn
69
69
  ```
70
70
 
71
+ ## React (`./wallet/next`)
72
+
73
+ Capa React de la billetera in-app (`m/REQ/37`, graduada de `learn.tg/packages/pdj-wallet-next`):
74
+
75
+ ```tsx
76
+ import {
77
+ useInAppWallet, InAppWalletSetup, InAppWalletUnlock, configureInAppWallet,
78
+ } from '@pasosdejesus/m/wallet/next'
79
+
80
+ // Conserva tu namespace si ya tienes billeteras creadas:
81
+ configureInAppWallet({
82
+ dbName: 'learn-tg-pdj-wallet',
83
+ destinationsPrefix: 'learn.tg.pdj-wallet.',
84
+ rpName: 'learn.tg',
85
+ })
86
+
87
+ // Textos: por defecto en/es; el host puede inyectar los suyos:
88
+ <InAppWalletSetup lang="es" messages={{ createTitle: 'Abrir cartera' }} onDone={(a) => {}} />
89
+ ```
90
+
91
+ `react` es un *optional peer*: el subpath está fuera del barrel, así que importar
92
+ `@pasosdejesus/m/wallet` no arrastra React.
93
+
71
94
  ## Pruebas
72
95
 
73
96
  Mocks en `@pasosdejesus/m/test-utils` (`installLocalStorage`, `installIndexedDB`,
@@ -91,6 +114,6 @@ O a mano: `import { setupWalletTests } from '@pasosdejesus/m/test-utils'; setupW
91
114
  ## Notas
92
115
 
93
116
  - Fase 1/2 de `m/REQ/14`: primitivas, estado, storage, provider, WebAuthn (L1/L2),
94
- destinos y resolutor EIP-6963. La billetera integrada (hook/UI React) vive en el
95
- consumidor o en un futuro `@pasosdejesus/m/wallet-react`.
117
+ destinos y resolutor EIP-6963. La capa React (hook + `InAppWalletSetup`/`InAppWalletUnlock`)
118
+ vive en el subpath `./wallet/next` (`m/REQ/37`).
96
119
  - E2E con firma real: `@pasosdejesus/m/e2e` (`injectEIP6963`, `setupFullWalletMock`).