@me2em/react 0.1.0-alpha.2 → 1.0.0-rc.1

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/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  ## 📦 @me2em/react — React bindings
6
6
 
7
- React bindings for the Me2em protocol: identity lifecycle, seed phrase UX, and hierarchical handle derivation.
7
+ React bindings for the Me2eM protocol: identity lifecycle, seed phrase UX, and hierarchical handle derivation.
8
8
 
9
9
  ## Install
10
10
 
@@ -24,13 +24,13 @@ Requires React >= 18.0.0 as a peer dependency.
24
24
  ## Quick Start
25
25
 
26
26
  ```tsx
27
- import { Me2emProvider, useCreateIdentity } from '@me2em/react';
27
+ import { Me2eMProvider, useCreateIdentity } from '@me2em/react';
28
28
 
29
29
  function App() {
30
30
  return (
31
- <Me2emProvider>
31
+ <Me2eMProvider>
32
32
  <CreateIdentityScreen />
33
- </Me2emProvider>
33
+ </Me2eMProvider>
34
34
  );
35
35
  }
36
36
 
@@ -90,7 +90,7 @@ this package:
90
90
  | **Context cache** | Per-identity encrypted storage of derived Handle context. |
91
91
 
92
92
  Full glossary (including Attestation, SubHandle, Ephemeral material):
93
- see the [root README](../../README.md).
93
+ see the root [GLOSSARY.md](../../GLOSSARY.md).
94
94
 
95
95
  **This package never persists your seed.** The seed phrase exists in
96
96
  two places only — your paper and transient memory during login. What
@@ -221,4 +221,4 @@ Each identity gets a separate IndexedDB database (name = SHA-256 hash of identit
221
221
 
222
222
  Alpha. API may change between 0.1.x releases. See [packages/core/README.md](../../packages/core/README.md) for the underlying protocol.
223
223
 
224
- Roadmap: @me2em/crypto protocol mechanisms — planned
224
+ Roadmap: `@me2em/crypto` shipped (0.1.0-alpha.1); `@me2em/messenger` planned (BACKLOG BL-29).
package/dist/index.d.ts CHANGED
@@ -65,6 +65,7 @@ declare function useHandle(name: string): Handle | null;
65
65
  declare function useSubHandle(handleName: string, subName: string): SubHandle | null;
66
66
 
67
67
  interface SeedPhraseDisplayProps {
68
+ /** Seed material is reachable via React DevTools in this window; by design. */
68
69
  words: string[];
69
70
  onDismiss?: () => void;
70
71
  onCopyAllowed?: () => void;
package/dist/index.js CHANGED
@@ -53,7 +53,7 @@ import {
53
53
  } from "react";
54
54
  import {
55
55
  generateSeedPhrase,
56
- get32ByteSeedFromMnemonic
56
+ get64ByteSeedFromMnemonic
57
57
  } from "@me2em/core";
58
58
 
59
59
  // src/headless/identity-flow.ts
@@ -159,7 +159,7 @@ function useCreateIdentity() {
159
159
  const generate = useCallback2(
160
160
  (opts) => {
161
161
  const words = generateSeedPhrase(opts?.strength ?? 128);
162
- get32ByteSeedFromMnemonic(words).then((derivedSeed) => {
162
+ get64ByteSeedFromMnemonic(words).then((derivedSeed) => {
163
163
  dispatch({ type: "GENERATE", words, seed: derivedSeed });
164
164
  }).catch(() => {
165
165
  dispatch({ type: "FAIL", message: "Failed to generate seed" });
@@ -198,7 +198,7 @@ function useCreateIdentity() {
198
198
  import { useReducer as useReducer2, useCallback as useCallback3 } from "react";
199
199
  import {
200
200
  validateSeedPhrase,
201
- get32ByteSeedFromMnemonic as get32ByteSeedFromMnemonic2
201
+ get64ByteSeedFromMnemonic as get64ByteSeedFromMnemonic2
202
202
  } from "@me2em/core";
203
203
  function useImportSeed() {
204
204
  const { activateIdentity, clearIdentity, identity } = useMe2emContext();
@@ -211,7 +211,7 @@ function useImportSeed() {
211
211
  return;
212
212
  }
213
213
  try {
214
- const seed = await get32ByteSeedFromMnemonic2(words, passphrase ?? "");
214
+ const seed = await get64ByteSeedFromMnemonic2(words, passphrase ?? "");
215
215
  dispatch({ type: "IMPORT", seed });
216
216
  await activateIdentity(seed);
217
217
  } catch (err) {
@@ -371,13 +371,16 @@ function displayFlowReducer(state, event) {
371
371
 
372
372
  // src/react/SeedPhraseDisplay.tsx
373
373
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
374
+ function clipboardWriteAvailable() {
375
+ return typeof navigator !== "undefined" && !!navigator.clipboard && typeof navigator.clipboard.writeText === "function";
376
+ }
374
377
  function SeedPhraseDisplay({
375
378
  words,
376
379
  onDismiss,
377
380
  onCopyAllowed
378
381
  }) {
379
382
  const [state, dispatch] = useReducer3(displayFlowReducer, initialDisplayState);
380
- const [copyFailed, setCopyFailed] = useState4(false);
383
+ const [copyError, setCopyError] = useState4(null);
381
384
  const { status, copyCount } = state;
382
385
  const handleReveal = () => {
383
386
  dispatch({ type: "REVEAL", at: Date.now() });
@@ -387,10 +390,10 @@ function SeedPhraseDisplay({
387
390
  try {
388
391
  await navigator.clipboard.writeText(words.join(" "));
389
392
  dispatch({ type: "COPY", at: Date.now() });
390
- setCopyFailed(false);
393
+ setCopyError(null);
391
394
  onCopyAllowed?.();
392
395
  } catch {
393
- setCopyFailed(true);
396
+ setCopyError(clipboardWriteAvailable() ? "transient" : "permanent");
394
397
  }
395
398
  }
396
399
  };
@@ -414,13 +417,13 @@ function SeedPhraseDisplay({
414
417
  },
415
418
  i
416
419
  )) }),
417
- copyFailed && /* @__PURE__ */ jsx2("div", { "data-testid": "copy-error", children: "Copy failed \u2014 try again" }),
420
+ copyError && /* @__PURE__ */ jsx2("div", { "data-testid": "copy-error", "data-kind": copyError, children: copyError === "permanent" ? "Copy unavailable in this context \u2014 select the words manually" : "Copy failed \u2014 try again" }),
418
421
  /* @__PURE__ */ jsx2(
419
422
  "button",
420
423
  {
421
424
  "data-testid": "copy-all",
422
425
  onClick: handleCopy,
423
- disabled: copyCount === 1,
426
+ disabled: copyCount === 1 || copyError === "permanent",
424
427
  children: copyCount === 1 ? "Copied" : "Copy all"
425
428
  }
426
429
  ),
@@ -787,13 +790,13 @@ function SeedPhraseImport({
787
790
  }
788
791
 
789
792
  // src/react/PassphraseInput.tsx
790
- import { useCallback as useCallback6 } from "react";
793
+ import { useCallback as useCallback5 } from "react";
791
794
  import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
792
795
  function PassphraseInput({
793
796
  onPassphraseChange,
794
797
  showWarnings = true
795
798
  }) {
796
- const handleChange = useCallback6(
799
+ const handleChange = useCallback5(
797
800
  (e) => {
798
801
  const value = e.target.value.normalize("NFKC");
799
802
  onPassphraseChange(value);
@@ -815,16 +818,22 @@ function PassphraseInput({
815
818
  }
816
819
 
817
820
  // src/react/useIdentityContext.ts
818
- import { useState as useState7, useCallback as useCallback7, useEffect as useEffect5, useRef as useRef3 } from "react";
821
+ import { useState as useState6, useCallback as useCallback6, useEffect as useEffect5, useRef as useRef3 } from "react";
819
822
  function publicKeyToId(publicKey) {
820
823
  return Array.from(publicKey).map((b) => b.toString(16).padStart(2, "0")).join("");
821
824
  }
825
+ function identityToId(identity) {
826
+ return identity ? publicKeyToId(identity.getPublicKey()) : null;
827
+ }
822
828
  function useIdentityContext(storage) {
823
829
  const { identity } = useMe2emContext();
824
- const [context, setContext] = useState7(null);
825
- const [isLoaded, setIsLoaded] = useState7(false);
826
- const [error, setError] = useState7(null);
830
+ const [context, setContext] = useState6(null);
831
+ const [isLoaded, setIsLoaded] = useState6(false);
832
+ const [error, setError] = useState6(null);
827
833
  const prevIdentityIdRef = useRef3(null);
834
+ const opRef = useRef3(0);
835
+ const identityRef = useRef3(null);
836
+ identityRef.current = identity;
828
837
  useEffect5(() => {
829
838
  let cancelled = false;
830
839
  if (!identity) {
@@ -839,8 +848,9 @@ function useIdentityContext(storage) {
839
848
  });
840
849
  }
841
850
  prevIdentityIdRef.current = identityId;
851
+ const op = ++opRef.current;
842
852
  storage.openAndLoad(identityId).then((data) => {
843
- if (cancelled) return;
853
+ if (cancelled || op !== opRef.current) return;
844
854
  setContext(data);
845
855
  setIsLoaded(true);
846
856
  setError(null);
@@ -853,36 +863,47 @@ function useIdentityContext(storage) {
853
863
  cancelled = true;
854
864
  };
855
865
  }, [identity, storage]);
856
- const save = useCallback7(async (data) => {
866
+ const save = useCallback6(async (data) => {
867
+ const idAtStart = identityToId(identityRef.current);
868
+ opRef.current += 1;
857
869
  await storage.save(data);
870
+ if (identityToId(identityRef.current) !== idAtStart) return;
858
871
  setContext(data);
872
+ setError(null);
859
873
  }, [storage]);
860
- const clear = useCallback7(async () => {
874
+ const clear = useCallback6(async () => {
875
+ const idAtStart = identityToId(identityRef.current);
876
+ opRef.current += 1;
861
877
  await storage.clear();
878
+ if (identityToId(identityRef.current) !== idAtStart) return;
862
879
  setContext(null);
880
+ setError(null);
863
881
  }, [storage]);
864
- const refresh = useCallback7(async () => {
882
+ const refresh = useCallback6(async () => {
865
883
  if (!identity) return;
866
884
  const identityId = publicKeyToId(identity.getPublicKey());
885
+ const op = ++opRef.current;
867
886
  const data = await storage.openAndLoad(identityId);
887
+ if (op !== opRef.current) return;
868
888
  setContext(data);
869
889
  }, [identity, storage]);
870
890
  return { context, isLoaded, error, save, clear, refresh };
871
891
  }
872
892
 
873
893
  // src/react/useSession.ts
874
- import { useState as useState8, useEffect as useEffect6, useCallback as useCallback8, useRef as useRef4 } from "react";
894
+ import { useState as useState7, useEffect as useEffect6, useCallback as useCallback7, useRef as useRef4 } from "react";
875
895
  import { Session } from "@me2em/core";
876
896
  function useSession(handle, options, autoRenew = true) {
877
- const { identity } = useMe2emContext();
878
- const [session, setSession] = useState8(null);
879
- const [isLoaded, setIsLoaded] = useState8(false);
880
- const [error, setError] = useState8(null);
897
+ const [session, setSession] = useState7(null);
898
+ const [isLoaded, setIsLoaded] = useState7(false);
899
+ const [error, setError] = useState7(null);
881
900
  const timerRef = useRef4(null);
882
901
  const optionsRef = useRef4(options);
883
902
  const mountedRef = useRef4(true);
884
- optionsRef.current = options;
885
- const clearTimer = useCallback8(() => {
903
+ useEffect6(() => {
904
+ optionsRef.current = options;
905
+ });
906
+ const clearTimer = useCallback7(() => {
886
907
  if (timerRef.current) {
887
908
  clearTimeout(timerRef.current);
888
909
  timerRef.current = null;
@@ -895,7 +916,7 @@ function useSession(handle, options, autoRenew = true) {
895
916
  clearTimer();
896
917
  };
897
918
  }, [clearTimer]);
898
- const renew = useCallback8(() => {
919
+ const renew = useCallback7(() => {
899
920
  clearTimer();
900
921
  const opts = optionsRef.current;
901
922
  if (!handle) {
@@ -964,25 +985,7 @@ __export(headless_exports, {
964
985
  });
965
986
 
966
987
  // src/headless/context-crypto.ts
967
- function toB64(bytes) {
968
- let binary = "";
969
- for (const byte of bytes) {
970
- binary += String.fromCharCode(byte);
971
- }
972
- return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
973
- }
974
- function fromB64(s) {
975
- let base64 = s.replace(/-/g, "+").replace(/_/g, "/");
976
- while (base64.length % 4 !== 0) {
977
- base64 += "=";
978
- }
979
- const binary = atob(base64);
980
- const bytes = new Uint8Array(binary.length);
981
- for (let i = 0; i < binary.length; i++) {
982
- bytes[i] = binary.charCodeAt(i);
983
- }
984
- return bytes;
985
- }
988
+ import { base64urlEncode, base64urlDecode } from "@me2em/core";
986
989
  async function deriveCacheKey(loginMaterial, salt) {
987
990
  const base = await crypto.subtle.importKey(
988
991
  "raw",
@@ -1020,17 +1023,17 @@ async function encryptContext(key, data) {
1020
1023
  );
1021
1024
  return {
1022
1025
  v: 1,
1023
- salt: toB64(salt),
1024
- iv: toB64(iv),
1025
- ct: toB64(new Uint8Array(ct))
1026
+ salt: base64urlEncode(salt),
1027
+ iv: base64urlEncode(iv),
1028
+ ct: base64urlEncode(new Uint8Array(ct))
1026
1029
  };
1027
1030
  }
1028
1031
  async function decryptContext(key, envelope) {
1029
1032
  if (envelope.v !== 1) throw new Error("Unsupported envelope version");
1030
1033
  const pt = await crypto.subtle.decrypt(
1031
- { name: "AES-GCM", iv: fromB64(envelope.iv) },
1034
+ { name: "AES-GCM", iv: base64urlDecode(envelope.iv) },
1032
1035
  key,
1033
- fromB64(envelope.ct)
1036
+ base64urlDecode(envelope.ct)
1034
1037
  );
1035
1038
  return JSON.parse(new TextDecoder().decode(pt));
1036
1039
  }
@@ -1189,7 +1192,7 @@ var IdentityContextIDBStorage = class {
1189
1192
  if (!db) {
1190
1193
  return null;
1191
1194
  }
1192
- return new Promise((resolve, reject) => {
1195
+ return new Promise((resolve) => {
1193
1196
  const tx = db.transaction(KEYS_STORE_NAME, "readonly");
1194
1197
  const store = tx.objectStore(KEYS_STORE_NAME);
1195
1198
  const request = store.get("cache");
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/react/Me2emProvider.tsx","../src/react/context.ts","../src/react/useCreateIdentity.ts","../src/headless/identity-flow.ts","../src/react/useImportSeed.ts","../src/react/useHandle.ts","../src/react/useSubHandle.ts","../src/react/SeedPhraseDisplay.tsx","../src/headless/display-flow.ts","../src/react/SeedPhraseVerify.tsx","../src/headless/seed-words.ts","../src/headless/verify-flow.ts","../src/react/SeedPhraseImport.tsx","../src/headless/import-flow.ts","../src/react/PassphraseInput.tsx","../src/react/useIdentityContext.ts","../src/react/useSession.ts","../src/headless/index.ts","../src/headless/context-crypto.ts","../src/headless/identity-context-storage.ts"],"sourcesContent":["import {\n useState,\n useCallback,\n useMemo,\n type ReactNode,\n} from 'react';\nimport { Identity } from '@me2em/core';\nimport { Me2emContext, type Me2emContextValue } from './context.js';\n\nexport function Me2emProvider({ children }: { children: ReactNode }) {\n const [identity, setIdentity] = useState<Identity | null>(null);\n\n const activateIdentity = useCallback(async (seed: Uint8Array): Promise<boolean> => {\n try {\n const id = await Identity.fromSeed(seed);\n setIdentity(id);\n return true;\n } catch {\n return false;\n }\n }, []);\n\n const clearIdentity = useCallback(() => {\n setIdentity(null);\n }, []);\n\n const value = useMemo<Me2emContextValue>(\n () => ({ identity, hasIdentity: identity !== null, activateIdentity, clearIdentity }),\n [identity, activateIdentity, clearIdentity]\n );\n\n return <Me2emContext.Provider value={value}>{children}</Me2emContext.Provider>;\n}\n","import { createContext, useContext } from 'react';\nimport type { Identity } from '@me2em/core';\n\nexport interface Me2emContextValue {\n identity: Identity | null;\n hasIdentity: boolean;\n activateIdentity: (seed: Uint8Array) => Promise<boolean>;\n clearIdentity: () => void;\n}\n\nexport const Me2emContext = createContext<Me2emContextValue | null>(null);\n\nexport function useMe2emContext(): Me2emContextValue {\n const ctx = useContext(Me2emContext);\n if (!ctx) {\n throw new Error('Me2em hooks must be used inside <Me2emProvider>');\n }\n return ctx;\n}\n","// packages/react/src/react/useCreateIdentity.ts\nimport {\n useReducer,\n useCallback,\n type Reducer,\n} from 'react';\nimport {\n generateSeedPhrase,\n get32ByteSeedFromMnemonic,\n type SeedStrength,\n type Identity,\n} from '@me2em/core';\nimport {\n identityFlowReducer,\n initialIdentityFlowState,\n} from '../headless/identity-flow.js';\nimport type { IdentityFlowState, IdentityFlowEvent, IdentityFlowStatus } from '../headless/types.js';\nimport { useMe2emContext } from './context.js';\n\nexport interface UseCreateIdentityResult {\n status: IdentityFlowStatus;\n seedWords: string[] | null;\n identity: Identity | null;\n error: string | null;\n generate: (opts?: { strength?: SeedStrength }) => void;\n confirmWords: () => Promise<void>;\n reset: () => void;\n fail: (message: string) => void;\n}\n\nexport function useCreateIdentity(): UseCreateIdentityResult {\n const { activateIdentity, clearIdentity, identity } = useMe2emContext();\n const [state, dispatch] = useReducer<\n Reducer<IdentityFlowState, IdentityFlowEvent>\n >(identityFlowReducer, initialIdentityFlowState);\n\n const generate = useCallback(\n (opts?: { strength?: SeedStrength }) => {\n const words = generateSeedPhrase(opts?.strength ?? 128);\n get32ByteSeedFromMnemonic(words).then((derivedSeed) => {\n dispatch({ type: 'GENERATE', words, seed: derivedSeed });\n }).catch(() => {\n dispatch({ type: 'FAIL', message: 'Failed to generate seed' });\n });\n },\n []\n );\n\n const confirmWords = useCallback(async () => {\n if (state.status !== 'seed-generated' || !state.identitySeed) return;\n dispatch({ type: 'CONFIRM_WORDS' });\n const ok = await activateIdentity(state.identitySeed);\n if (!ok) {\n dispatch({ type: 'FAIL', message: 'Identity activation failed' });\n }\n }, [state.status, state.identitySeed, activateIdentity]);\n\n const reset = useCallback(() => {\n dispatch({ type: 'RESET' });\n clearIdentity();\n }, [clearIdentity]);\n\n const fail = useCallback((message: string) => {\n dispatch({ type: 'FAIL', message });\n }, []);\n\n return {\n status: state.status,\n seedWords: state.seedWords,\n identity,\n error: state.error,\n generate,\n confirmWords,\n reset,\n fail,\n };\n}\n","import type { IdentityFlowState, IdentityFlowEvent } from './types.js';\n\nexport const initialIdentityFlowState: IdentityFlowState = {\n status: 'idle',\n seedWords: null,\n identitySeed: null,\n error: null,\n};\n\nconst UNEXPECTED_EVENTS: Record<string, string> = {\n CONFIRM_WORDS: 'CONFIRM_WORDS called from idle — no seed to confirm',\n RESET: 'RESET called from idle — nothing to reset',\n};\n\nexport function identityFlowReducer(\n state: IdentityFlowState,\n event: IdentityFlowEvent\n): IdentityFlowState {\n const { type } = event;\n\n switch (state.status) {\n case 'idle': {\n if (type === 'GENERATE') {\n return {\n status: 'seed-generated',\n seedWords: event.words,\n identitySeed: event.seed,\n error: null,\n };\n }\n if (type === 'IMPORT') {\n return {\n status: 'imported',\n seedWords: null,\n identitySeed: event.seed,\n error: null,\n };\n }\n if (type === 'FAIL') {\n return {\n status: 'error',\n seedWords: null,\n identitySeed: null,\n error: event.message,\n };\n }\n if (UNEXPECTED_EVENTS[type]) {\n console.warn(UNEXPECTED_EVENTS[type]);\n return state;\n }\n return state;\n }\n\n case 'seed-generated': {\n if (type === 'CONFIRM_WORDS') {\n return {\n status: 'verified',\n seedWords: null,\n identitySeed: state.identitySeed,\n error: null,\n };\n }\n if (type === 'FAIL') {\n return {\n status: 'error',\n seedWords: null,\n identitySeed: null,\n error: event.message,\n };\n }\n if (type === 'RESET') {\n return initialIdentityFlowState;\n }\n console.warn(`Unexpected event ${type} in seed-generated`);\n return state;\n }\n\n case 'verified':\n case 'imported': {\n if (type === 'RESET') {\n return initialIdentityFlowState;\n }\n if (type === 'FAIL') {\n return {\n status: 'error',\n seedWords: null,\n identitySeed: null,\n error: event.message,\n };\n }\n console.warn(`Unexpected event ${type} in ${state.status}`);\n return state;\n }\n\n case 'error': {\n if (type === 'RESET') {\n return initialIdentityFlowState;\n }\n console.warn(`Unexpected event ${type} in error`);\n return state;\n }\n\n default:\n return state;\n }\n}\n","// packages/react/src/react/useImportSeed.ts\nimport { useReducer, useCallback, type Reducer } from 'react';\nimport {\n validateSeedPhrase,\n get32ByteSeedFromMnemonic,\n type Identity,\n} from '@me2em/core';\nimport {\n identityFlowReducer,\n initialIdentityFlowState,\n} from '../headless/identity-flow.js';\nimport type { IdentityFlowState, IdentityFlowEvent, IdentityFlowStatus } from '../headless/types.js';\nimport { useMe2emContext } from './context.js';\n\nexport interface UseImportSeedResult {\n status: IdentityFlowStatus;\n identity: Identity | null;\n error: string | null;\n importWords: (words: string[], passphrase?: string) => Promise<void>;\n reset: () => void;\n}\n\nexport function useImportSeed(): UseImportSeedResult {\n const { activateIdentity, clearIdentity, identity } = useMe2emContext();\n const [state, dispatch] = useReducer<\n Reducer<IdentityFlowState, IdentityFlowEvent>\n >(identityFlowReducer, initialIdentityFlowState);\n\n const importWords = useCallback(\n async (words: string[], passphrase?: string) => {\n const validation = validateSeedPhrase(words);\n if (!validation.isValid) {\n dispatch({ type: 'FAIL', message: validation.error ?? 'Invalid seed phrase' });\n return;\n }\n try {\n const seed = await get32ByteSeedFromMnemonic(words, passphrase ?? '');\n dispatch({ type: 'IMPORT', seed });\n await activateIdentity(seed);\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Import failed';\n dispatch({ type: 'FAIL', message });\n }\n },\n [activateIdentity]\n );\n\n const reset = useCallback(() => {\n dispatch({ type: 'RESET' });\n clearIdentity();\n }, [clearIdentity]);\n\n return {\n status: state.status,\n identity,\n error: state.error,\n importWords,\n reset,\n };\n}\n","// packages/react/src/react/useHandle.ts\nimport { useState, useEffect } from 'react';\nimport { type Handle } from '@me2em/core';\nimport { useMe2emContext } from './context.js';\n\nexport function useHandle(name: string): Handle | null {\n const { identity } = useMe2emContext();\n const [handle, setHandle] = useState<Handle | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n if (!identity) {\n setHandle(null);\n return;\n }\n // Core performs full name canonicalization (NFKC, lowercase,\n // trim) and validation — no local normalization here.\n identity.deriveHandle(name).then((h) => {\n if (!cancelled) setHandle(h);\n }).catch((err) => {\n if (!cancelled) console.error('useHandle derivation failed', err);\n });\n return () => { cancelled = true; };\n }, [identity, name]);\n\n return handle;\n}\n","import { useState, useEffect } from 'react';\nimport { type SubHandle } from '@me2em/core';\nimport { useMe2emContext } from './context.js';\n\nexport function useSubHandle(\n handleName: string,\n subName: string\n): SubHandle | null {\n const { identity } = useMe2emContext();\n const [subHandle, setSubHandle] = useState<SubHandle | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n if (!identity) {\n setSubHandle(null);\n return;\n }\n identity\n .deriveSubHandle(handleName, subName)\n .then((h) => {\n if (!cancelled) setSubHandle(h);\n })\n .catch((err) => {\n if (!cancelled) console.error('useSubHandle derivation failed', err);\n });\n return () => { cancelled = true; };\n }, [identity, handleName, subName]);\n\n return subHandle;\n}\n","// packages/react/src/react/SeedPhraseDisplay.tsx\nimport { useReducer, useState } from 'react';\nimport {\n displayFlowReducer,\n initialDisplayState,\n} from '../headless/display-flow.js';\n\nexport interface SeedPhraseDisplayProps {\n words: string[];\n onDismiss?: () => void;\n onCopyAllowed?: () => void;\n}\n\nexport function SeedPhraseDisplay({\n words,\n onDismiss,\n onCopyAllowed,\n}: SeedPhraseDisplayProps) {\n const [state, dispatch] = useReducer(displayFlowReducer, initialDisplayState);\n const [copyFailed, setCopyFailed] = useState(false);\n\n const { status, copyCount } = state;\n\n const handleReveal = () => {\n dispatch({ type: 'REVEAL', at: Date.now() });\n };\n\n const handleCopy = async () => {\n if (status === 'revealed' && copyCount === 0) {\n try {\n await (navigator as any).clipboard.writeText(words.join(' '));\n dispatch({ type: 'COPY', at: Date.now() });\n setCopyFailed(false);\n onCopyAllowed?.();\n } catch {\n setCopyFailed(true);\n }\n }\n };\n\n const handleDismiss = () => {\n dispatch({ type: 'DISMISS' });\n onDismiss?.();\n };\n\n if (status === 'hidden') {\n return (\n <button data-testid=\"reveal\" onClick={handleReveal}>\n Reveal\n </button>\n );\n }\n\n if (status === 'dismissed') {\n return (\n <div data-testid=\"dismissed\">\n Phrase dismissed\n </div>\n );\n }\n\n return (\n <div>\n <ol data-testid=\"words\">\n {words.map((word, i) => (\n <li\n key={i}\n data-testid={`word-${i}`}\n data-position={i}\n >\n {word}\n </li>\n ))}\n </ol>\n {copyFailed && (\n <div data-testid=\"copy-error\">\n Copy failed — try again\n </div>\n )}\n <button\n data-testid=\"copy-all\"\n onClick={handleCopy}\n disabled={copyCount === 1}\n >\n {copyCount === 1 ? 'Copied' : 'Copy all'}\n </button>\n <button data-testid=\"dismiss\" onClick={handleDismiss}>\n Hide\n </button>\n </div>\n );\n}\n","// packages/react/src/headless/display-flow.ts\n\nexport type DisplayStatus =\n | 'hidden'\n | 'revealed'\n | 'copied'\n | 'dismissed';\n\nexport interface DisplayState {\n status: DisplayStatus;\n copyCount: number;\n revealedAt: number | null;\n}\n\nexport type DisplayEvent =\n | { type: 'REVEAL'; at: number }\n | { type: 'COPY'; at: number }\n | { type: 'DISMISS' }\n | { type: 'RESET' };\n\nexport const initialDisplayState: DisplayState = {\n status: 'hidden',\n copyCount: 0,\n revealedAt: null,\n};\n\nexport function displayFlowReducer(\n state: DisplayState,\n event: DisplayEvent\n): DisplayState {\n const { type } = event;\n\n switch (state.status) {\n case 'hidden': {\n if (type === 'REVEAL') {\n return {\n status: 'revealed',\n copyCount: 0,\n revealedAt: event.at,\n };\n }\n if (type === 'COPY') {\n console.warn('Cannot copy while hidden');\n return state;\n }\n if (type === 'RESET') {\n return initialDisplayState;\n }\n if (type === 'DISMISS') {\n console.warn('Cannot dismiss while hidden');\n return state;\n }\n console.warn(`Unexpected event ${type} in hidden`);\n return state;\n }\n\n case 'revealed': {\n if (type === 'COPY') {\n if (state.copyCount === 0) {\n return {\n status: 'copied',\n copyCount: 1,\n revealedAt: state.revealedAt,\n };\n }\n console.warn('Already copied — duplicate copy blocked');\n return state;\n }\n if (type === 'DISMISS') {\n return {\n status: 'dismissed',\n copyCount: state.copyCount,\n revealedAt: state.revealedAt,\n };\n }\n if (type === 'RESET') {\n return initialDisplayState;\n }\n console.warn(`Unexpected event ${type} in revealed`);\n return state;\n }\n\n case 'copied': {\n if (type === 'COPY') {\n console.warn('Already copied — duplicate copy blocked');\n return state;\n }\n if (type === 'DISMISS') {\n return {\n status: 'dismissed',\n copyCount: 1,\n revealedAt: state.revealedAt,\n };\n }\n if (type === 'RESET') {\n return initialDisplayState;\n }\n console.warn(`Unexpected event ${type} in copied`);\n return state;\n }\n\n case 'dismissed': {\n if (type === 'RESET') {\n return initialDisplayState;\n }\n console.warn(`Unexpected event ${type} in dismissed`);\n return state;\n }\n\n default:\n return state;\n }\n}\n","// packages/react/src/react/SeedPhraseVerify.tsx\nimport { useReducer, useCallback, useState, useEffect, useRef } from 'react';\nimport {\n verifyFlowReducer,\n initVerifyState,\n type VerifyState,\n} from '../headless/verify-flow.js';\nimport { wordlist } from '@me2em/core';\n\nexport interface SeedPhraseVerifyProps {\n realWords: string[];\n gridSize?: number;\n wordlist?: readonly string[];\n onVerified: () => void;\n onFailed?: (attempts: number) => void;\n}\n\nconst DEFAULT_GRID_SIZE_OFFSET = 8;\n\nexport function SeedPhraseVerify({\n realWords,\n gridSize,\n wordlist: propWordlist,\n onVerified,\n onFailed,\n}: SeedPhraseVerifyProps) {\n const grid = gridSize ?? realWords.length + DEFAULT_GRID_SIZE_OFFSET;\n const allWords = propWordlist ?? wordlist;\n const baseSeed = 42;\n\n const [state, dispatch] = useReducer(\n verifyFlowReducer,\n {\n realWords,\n gridSize: grid,\n randomSeed: baseSeed,\n allWords,\n } as const,\n (params) => initVerifyState(params.realWords, params.gridSize, params.randomSeed, params.allWords)\n );\n\n const [retryCount, setRetryCount] = useState(0);\n\n const { status, grid: gridData, selected } = state;\n\n // Guard against re-entrant callbacks after status change\n const notifiedRef = useRef(false);\n\n const handleSelect = useCallback(\n (index: number) => {\n if (status === 'correct' || status === 'incorrect') return;\n if (selected.length >= gridData.correctIndices.length) return;\n dispatch({ type: 'SELECT', index });\n },\n [status, selected.length, gridData.correctIndices.length]\n );\n\n // Single effect: auto-check when enough words selected, then notify result\n useEffect(() => {\n if (notifiedRef.current) return;\n\n // Auto-check when enough words selected\n if (\n selected.length === gridData.correctIndices.length &&\n selected.length > 0 &&\n status === 'collecting'\n ) {\n dispatch({ type: 'CHECK' });\n // After dispatch, status will change synchronously in the reducer\n // but the new state won't be reflected until next render.\n // We need to check the pending result in the next effect cycle.\n return;\n }\n\n // Handle result after status update\n if (status === 'correct') {\n notifiedRef.current = true;\n onVerified();\n } else if (status === 'incorrect') {\n notifiedRef.current = true;\n onFailed?.(state.attempts);\n }\n }, [selected.length, gridData.correctIndices.length, status, state.attempts, onVerified, onFailed]);\n\n const handleRetry = () => {\n notifiedRef.current = false;\n setRetryCount((c) => c + 1);\n dispatch({\n type: 'RESET',\n realWords,\n gridSize: grid,\n randomSeed: baseSeed + retryCount + 1,\n allWords,\n });\n };\n\n return (\n <div>\n <div data-testid=\"progress\">\n Select word {selected.length + 1} of {realWords.length}\n </div>\n <ol data-testid=\"grid\">\n {gridData.words.map((word, i) => {\n const isSelected = selected.includes(i);\n const isDisabled =\n isSelected || status === 'correct' || status === 'incorrect';\n return (\n <li key={i}>\n <button\n data-testid={`cell-${i}`}\n data-word={word}\n disabled={isDisabled}\n onClick={() => handleSelect(i)}\n >\n {word}\n </button>\n </li>\n );\n })}\n </ol>\n {status === 'incorrect' && (\n <button data-testid=\"retry\" onClick={handleRetry}>\n Try again\n </button>\n )}\n </div>\n );\n}\n","// packages/react/src/headless/seed-words.ts\n\nexport function shuffleWithSeed<T>(items: T[], seed: number): T[] {\n const arr = [...items];\n let currentSeed = seed;\n function nextRand(): number {\n currentSeed = (currentSeed * 1664525 + 1013904223) >>> 0;\n return currentSeed;\n }\n for (let i = arr.length - 1; i > 0; i--) {\n const j = nextRand() % (i + 1);\n [arr[i], arr[j]] = [arr[j], arr[i]];\n }\n return arr;\n}\n\nexport function buildVerificationGrid(\n realWords: string[],\n gridSize: number,\n randomSeed: number,\n allWords: readonly string[]\n): { words: string[]; correctIndices: number[] } {\n if (gridSize < realWords.length) {\n throw new Error(\n `gridSize (${gridSize}) must be >= realWords.length (${realWords.length})`\n );\n }\n\n const shuffled = shuffleWithSeed(realWords, randomSeed);\n const realSet = new Set(realWords);\n const available = allWords.filter((w) => !realSet.has(w));\n const shuffledDecoys = shuffleWithSeed(available, randomSeed + 1);\n\n const needed = gridSize - realWords.length;\n const decoys = shuffledDecoys.slice(0, needed);\n\n const words = [...shuffled, ...decoys];\n if (words.length !== gridSize) {\n throw new Error(\n `Cannot fill grid: wordlist too small (got ${words.length}, need ${gridSize})`\n );\n }\n\n // Map each word occurrence to a DISTINCT grid position, so\n // repeated words in the phrase get their own cells.\n const positions = new Map<string, number[]>();\n words.forEach((w, i) => {\n const list = positions.get(w);\n if (list) list.push(i);\n else positions.set(w, [i]);\n });\n\n const correctIndices = realWords.map((w) => {\n const list = positions.get(w);\n if (!list || list.length === 0) {\n throw new Error(`Internal error: word \"${w}\" missing from grid`);\n }\n return list.shift() as number;\n });\n\n return { words, correctIndices };\n}\n\n/**\n * Selection is compared IN ORDER: selected[i] must equal\n * correctIndices[i]. The i-th click must be the i-th word of the\n * phrase. Repeated phrase words map to distinct grid cells.\n */\nexport function isGridSelectionCorrect(\n grid: { words: string[]; correctIndices: number[] },\n selected: number[]\n): boolean {\n if (selected.length !== grid.correctIndices.length) return false;\n return selected.every((v, i) => v === grid.correctIndices[i]);\n}\n","// packages/react/src/headless/verify-flow.ts\nimport {\n buildVerificationGrid,\n isGridSelectionCorrect,\n} from './seed-words.js';\n\nexport type VerifyStatus = 'collecting' | 'correct' | 'incorrect';\n\nexport interface VerifyState {\n status: VerifyStatus;\n grid: { words: string[]; correctIndices: number[] };\n selected: number[];\n attempts: number;\n}\n\nexport type VerifyEvent =\n | { type: 'SELECT'; index: number }\n | { type: 'DESELECT'; index: number }\n | { type: 'CHECK' }\n | {\n type: 'RESET';\n realWords: string[];\n gridSize: number;\n randomSeed: number;\n allWords: readonly string[];\n };\n\nexport function initVerifyState(\n realWords: string[],\n gridSize: number,\n randomSeed: number,\n allWords: readonly string[]\n): VerifyState {\n const grid = buildVerificationGrid(realWords, gridSize, randomSeed, allWords);\n return {\n status: 'collecting',\n grid,\n selected: [],\n attempts: 0,\n };\n}\n\nexport function verifyFlowReducer(\n state: VerifyState,\n event: VerifyEvent\n): VerifyState {\n const { type } = event;\n\n if (type === 'RESET') {\n return initVerifyState(\n event.realWords,\n event.gridSize,\n event.randomSeed,\n event.allWords\n );\n }\n\n // Terminal states: only RESET allowed\n if (state.status === 'correct' || state.status === 'incorrect') {\n console.warn(\n `Cannot ${type} in ${state.status} state — use RESET to retry`\n );\n return state;\n }\n\n // collecting state\n if (type === 'SELECT') {\n const { index } = event;\n const { grid, selected } = state;\n if (selected.includes(index)) {\n console.warn(`Index ${index} already selected — use DESELECT`);\n return state;\n }\n if (index < 0 || index >= grid.words.length) {\n console.warn(`Index ${index} out of grid bounds`);\n return state;\n }\n return { ...state, selected: [...selected, index] };\n }\n\n if (type === 'DESELECT') {\n const { index } = event;\n const idx = state.selected.indexOf(index);\n if (idx === -1) {\n console.warn(`Index ${index} not selected`);\n return state;\n }\n return {\n ...state,\n selected: state.selected.filter((_, i) => i !== idx),\n };\n }\n\n if (type === 'CHECK') {\n const { grid, selected, attempts } = state;\n if (selected.length !== grid.correctIndices.length) {\n console.warn(\n `Cannot check: selected ${selected.length}, need ${grid.correctIndices.length}`\n );\n return state;\n }\n const correct = isGridSelectionCorrect(grid, selected);\n return {\n ...state,\n status: correct ? 'correct' : 'incorrect',\n attempts: attempts + 1,\n };\n }\n\n console.warn(`Unexpected event ${type} in collecting`);\n return state;\n}\n","// packages/react/src/react/SeedPhraseImport.tsx\nimport { useReducer, useCallback, useEffect, useRef } from 'react';\nimport {\n importFlowReducer,\n initImportState,\n type ImportState,\n} from '../headless/import-flow.js';\nimport { wordlist } from '@me2em/core';\n\nexport interface SeedPhraseImportProps {\n expectedCount?: 12 | 24;\n wordlist?: readonly string[];\n onImported: (words: string[]) => void;\n}\n\nexport function SeedPhraseImport({\n expectedCount: propCount,\n wordlist: propWordlist,\n onImported,\n}: SeedPhraseImportProps) {\n const allWords = propWordlist ?? wordlist;\n\n const [state, dispatch] = useReducer(\n importFlowReducer,\n { expectedCount: propCount ?? 12 } as const,\n (params) => initImportState(params.expectedCount)\n );\n\n const count = state.expectedCount;\n\n const prevStatusRef = useRef<ImportState['status']>(state.status);\n\n const lastImportedRef = useRef<string | null>(null);\n\n // Auto-check on every word change\n useEffect(() => {\n dispatch({ type: 'CHECK' });\n }, [state.words]);\n\n // Call onImported once when transitioning to valid\n useEffect(() => {\n const prevStatus = prevStatusRef.current;\n prevStatusRef.current = state.status;\n\n if (state.status === 'valid' && prevStatus !== 'valid') {\n const key = (state.words as string[]).join('\\0');\n if (lastImportedRef.current !== key) {\n lastImportedRef.current = key;\n onImported(state.words as string[]);\n }\n }\n }, [state.status, state.words, onImported]);\n\n const handleCountChange = (n: 12 | 24) => {\n dispatch({ type: 'INIT', expectedCount: n });\n };\n\n const handleWordChange = (position: number, value: string) => {\n dispatch({ type: 'SET_WORD', position, word: value });\n };\n\n const isWordInvalid = (position: number): boolean => {\n if (state.status !== 'invalid' && state.status !== 'valid') return false;\n const word = state.words[position];\n if (word === null) return false;\n return !allWords.includes(word);\n };\n\n const isImportEnabled = state.status === 'valid';\n\n return (\n <div>\n <div>\n <button\n data-testid=\"count-12\"\n onClick={() => handleCountChange(12)}\n disabled={count === 12}\n >\n 12 words\n </button>\n <button\n data-testid=\"count-24\"\n onClick={() => handleCountChange(24)}\n disabled={count === 24}\n >\n 24 words\n </button>\n </div>\n <div data-testid=\"inputs\">\n {Array.from({ length: count }, (_, i) => (\n <input\n key={i}\n data-testid={`word-${i}`}\n aria-label={`Word ${i + 1}`}\n value={state.words[i] ?? ''}\n onChange={(e) => handleWordChange(i, e.target.value)}\n data-invalid={isWordInvalid(i) ? 'true' : undefined}\n />\n ))}\n </div>\n <button\n data-testid=\"import\"\n disabled={!isImportEnabled}\n onClick={() => dispatch({ type: 'CHECK' })}\n >\n Import\n </button>\n </div>\n );\n}\n","// packages/react/src/headless/import-flow.ts\nimport { validateSeedPhrase, type SeedStrength } from '@me2em/core';\n\nexport type ImportStatus = 'entering' | 'valid' | 'invalid';\n\nexport interface ImportState {\n status: ImportStatus;\n words: (string | null)[];\n expectedCount: 12 | 24;\n}\n\nexport type ImportEvent =\n | { type: 'INIT'; expectedCount: 12 | 24 }\n | { type: 'SET_WORD'; position: number; word: string }\n | { type: 'CLEAR_WORD'; position: number }\n | { type: 'CHECK' }\n | { type: 'RESET' };\n\nexport function initImportState(expectedCount: 12 | 24): ImportState {\n return {\n status: 'entering',\n words: Array(expectedCount).fill(null),\n expectedCount,\n };\n}\n\nexport function importFlowReducer(\n state: ImportState,\n event: ImportEvent\n): ImportState {\n const { type } = event;\n\n if (type === 'INIT') {\n return initImportState(event.expectedCount);\n }\n\n if (type === 'RESET') {\n return initImportState(state.expectedCount);\n }\n\n if (type === 'SET_WORD') {\n const { position, word } = event;\n if (position < 0 || position >= state.expectedCount) {\n console.warn(`SET_WORD: position ${position} out of bounds`);\n return state;\n }\n const normalized = word.toLowerCase().trim();\n const newWords = [...state.words] as (string | null)[];\n newWords[position] = normalized;\n return { ...state, words: newWords, status: 'entering' };\n }\n\n if (type === 'CLEAR_WORD') {\n const { position } = event;\n if (position < 0 || position >= state.expectedCount) {\n console.warn(`CLEAR_WORD: position ${position} out of bounds`);\n return state;\n }\n const newWords = [...state.words] as (string | null)[];\n newWords[position] = null;\n return { ...state, words: newWords, status: 'entering' };\n }\n\n if (type === 'CHECK') {\n const { words } = state;\n const nullIdx = words.findIndex((w) => w === null);\n if (nullIdx !== -1) {\n return {\n ...state,\n status: 'invalid',\n };\n }\n const result = validateSeedPhrase(words as string[]);\n return {\n ...state,\n status: result.isValid ? 'valid' : 'invalid',\n };\n }\n\n console.warn(`Unexpected event ${type} in import-flow`);\n return state;\n}\n","// packages/react/src/react/PassphraseInput.tsx\nimport { useState, useCallback } from 'react';\n\nexport interface PassphraseInputProps {\n onPassphraseChange: (p: string) => void;\n showWarnings?: boolean;\n}\n\nexport function PassphraseInput({\n onPassphraseChange,\n showWarnings = true,\n}: PassphraseInputProps) {\n const handleChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const value = e.target.value.normalize('NFKC');\n onPassphraseChange(value);\n },\n [onPassphraseChange]\n );\n\n return (\n <div>\n <input\n type=\"password\"\n data-testid=\"passphrase\"\n aria-label=\"BIP39 passphrase\"\n onChange={handleChange}\n />\n {showWarnings && (\n <div data-testid=\"warning\">\n Passphrase is case-sensitive and NOT recoverable. A different\n passphrase silently derives a different wallet.\n </div>\n )}\n </div>\n );\n}\n","// packages/react/src/react/useIdentityContext.ts\nimport { useState, useCallback, useEffect, useRef } from 'react';\nimport type { IdentityContextStorage, IdentityContextData } from '../headless/identity-context-types.js';\nimport { useMe2emContext } from './context.js';\n\nfunction publicKeyToId(publicKey: Uint8Array): string {\n return Array.from(publicKey)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\nexport interface UseIdentityContextResult {\n context: IdentityContextData | null;\n isLoaded: boolean;\n error: string | null;\n save: (data: IdentityContextData) => Promise<void>;\n clear: () => Promise<void>;\n /** Reload the cache for the current identity (after sync). */\n refresh: () => Promise<void>;\n}\n\nexport function useIdentityContext(\n storage: IdentityContextStorage\n): UseIdentityContextResult {\n const { identity } = useMe2emContext();\n const [context, setContext] = useState<IdentityContextData | null>(null);\n const [isLoaded, setIsLoaded] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const prevIdentityIdRef = useRef<string | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n\n if (!identity) {\n setContext(null);\n setIsLoaded(true);\n setError(null);\n return;\n }\n\n const identityId = publicKeyToId(identity.getPublicKey());\n\n // If identity changed, close old storage and open new\n if (prevIdentityIdRef.current !== null && prevIdentityIdRef.current !== identityId) {\n storage.close().catch(() => { /* ignore close errors */ });\n }\n\n prevIdentityIdRef.current = identityId;\n\n // Open storage for this identity and load cached context (atomic)\n storage.openAndLoad(identityId)\n .then((data) => {\n if (cancelled) return;\n setContext(data);\n setIsLoaded(true);\n setError(null);\n })\n .catch((err) => {\n if (cancelled) return;\n setError(err instanceof Error ? err.message : String(err));\n setIsLoaded(true);\n });\n\n return () => {\n cancelled = true;\n };\n }, [identity, storage]);\n\n const save = useCallback(async (data: IdentityContextData): Promise<void> => {\n await storage.save(data);\n setContext(data);\n }, [storage]);\n\n const clear = useCallback(async (): Promise<void> => {\n await storage.clear();\n setContext(null);\n }, [storage]);\n\n const refresh = useCallback(async (): Promise<void> => {\n if (!identity) return;\n const identityId = publicKeyToId(identity.getPublicKey());\n const data = await storage.openAndLoad(identityId);\n setContext(data);\n }, [identity, storage]);\n\n return { context, isLoaded, error, save, clear, refresh };\n}\n","// packages/react/src/react/useSession.ts\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport type { Handle } from '@me2em/core';\nimport { Session, type SessionOptions, type SessionPayload } from '@me2em/core';\nimport { useMe2emContext } from './context.js';\n\n/**\n * Options for useSession, memoized via deep comparison of\n * `handle` and `options` refs on each render.\n *\n * @category Types\n */\nexport interface UseSessionOptions {\n /** Intended audience (recipient) of this session. */\n audience: string;\n /** Permission scopes granted in this session. */\n scopes: string[];\n /** Time-to-live in seconds. */\n ttl: number;\n}\n\n/**\n * Result of the useSession hook.\n *\n * @category Types\n */\nexport interface UseSessionResult {\n /** The active session, or null if no handle or session creation failed. */\n session: Session | null;\n /** Whether the session (or its absence) has been determined. */\n isLoaded: boolean;\n /** Error string if session creation or renewal failed. */\n error: string | null;\n /** Whether the current session has expired. */\n isExpired: boolean;\n /** Manually renew the session (creates a new one with a fresh jti). */\n renew: () => void;\n}\n\n/**\n * Hook that creates and manages a signed session token for a Handle.\n *\n * When `handle` is provided, creates a Session with the given options.\n * Supports auto-renewal: when `autoRenew` is true (default), a timer\n * fires at half the session TTL to create a fresh session with a new\n * jti (the previous token remains valid until its own expiry, but the\n * lineage identity is preserved through `handle`).\n *\n * On unmount, all timers are cleaned up to prevent state updates after\n * the component is gone.\n *\n * Dependencies: `[handle, renew]` — the options object is read through\n * a ref, so passing a fresh object literal on every render does NOT\n * re-trigger session creation. Only changes to `handle` (or to the\n * renewal function identity, which follows `handle`) do.\n *\n * @param handle - The Handle to sign the session, or null.\n * @param options - Session options (audience, scopes, ttl).\n * @param autoRenew - Whether to auto-renew at half-life (default: true).\n * @returns Session state with manual `renew` control.\n *\n * @example\n * ```ts\n * const handle = useHandle('my-handle');\n * const { session, isExpired, renew } = useSession(handle, {\n * audience: 'app.example.com',\n * scopes: ['read', 'write'],\n * ttl: 3600,\n * });\n * ```\n */\nexport function useSession(\n handle: Handle | null,\n options: UseSessionOptions,\n autoRenew: boolean = true\n): UseSessionResult {\n const { identity } = useMe2emContext();\n const [session, setSession] = useState<Session | null>(null);\n const [isLoaded, setIsLoaded] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const optionsRef = useRef(options);\n const mountedRef = useRef(true);\n\n // Update options ref without triggering re-renders\n optionsRef.current = options;\n\n const clearTimer = useCallback(() => {\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n }, []);\n\n // Cleanup on unmount\n useEffect(() => {\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n clearTimer();\n };\n }, [clearTimer]);\n\n const renew = useCallback(() => {\n clearTimer();\n const opts = optionsRef.current;\n\n if (!handle) {\n if (!mountedRef.current) return;\n setSession(null);\n setIsLoaded(true);\n setError(null);\n return;\n }\n\n Session.create(handle, {\n audience: opts.audience,\n scopes: opts.scopes,\n ttl: opts.ttl,\n }).then((s) => {\n if (!mountedRef.current) return;\n setSession(s);\n setIsLoaded(true);\n setError(null);\n\n // Schedule auto-renewal at half-life\n if (autoRenew) {\n const halfLife = Math.floor((s.expiresAt * 1000 - Date.now()) / 2);\n if (halfLife > 0) {\n timerRef.current = setTimeout(() => {\n renew();\n }, Math.max(halfLife, 1000));\n }\n }\n }).catch((err) => {\n if (!mountedRef.current) return;\n setSession(null);\n setIsLoaded(true);\n setError(err instanceof Error ? err.message : String(err));\n });\n }, [handle, autoRenew, clearTimer]);\n\n // Create session when handle or options change\n useEffect(() => {\n if (mountedRef.current) {\n renew();\n }\n }, [handle, renew]);\n\n return {\n session,\n isLoaded,\n error,\n isExpired: session ? session.isExpired() : false,\n renew,\n };\n}\n","export { identityFlowReducer, initialIdentityFlowState } from './identity-flow.js';\nexport type {\n IdentityFlowState,\n IdentityFlowStatus,\n IdentityFlowEvent,\n} from './types.js';\nexport {\n buildVerificationGrid,\n isGridSelectionCorrect,\n shuffleWithSeed,\n} from './seed-words.js';\nexport {\n displayFlowReducer,\n initialDisplayState,\n} from './display-flow.js';\nexport type {\n DisplayState,\n DisplayStatus,\n DisplayEvent,\n} from './display-flow.js';\nexport {\n verifyFlowReducer,\n initVerifyState,\n} from './verify-flow.js';\nexport type {\n VerifyState,\n VerifyStatus,\n VerifyEvent,\n} from './verify-flow.js';\nexport {\n importFlowReducer,\n initImportState,\n} from './import-flow.js';\nexport type {\n ImportState,\n ImportStatus,\n ImportEvent,\n} from './import-flow.js';\nexport {\n IdentityContextIDBStorage,\n} from './identity-context-storage.js';\nexport type {\n IdentityContextData,\n IdentityContextStorage,\n} from './identity-context-types.js';\nexport {\n deriveCacheKey,\n encryptContext,\n decryptContext,\n} from './context-crypto.js';\nexport type {\n ContextEnvelope,\n} from './context-crypto.js';\n","// packages/react/src/headless/context-crypto.ts\nimport type { IdentityContextData } from './identity-context-types.js';\n\nfunction toB64(bytes: Uint8Array): string {\n let binary = '';\n for (const byte of bytes) {\n binary += String.fromCharCode(byte);\n }\n return btoa(binary)\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n}\n\nfunction fromB64(s: string): Uint8Array {\n let base64 = s\n .replace(/-/g, '+')\n .replace(/_/g, '/');\n while (base64.length % 4 !== 0) {\n base64 += '=';\n }\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n\nexport interface ContextEnvelope {\n v: 1;\n /** fresh HKDF salt этого логина (b64). Нужен только при\n * первичной деривации; persisted CryptoKey переживает\n * перезапуск без него. */\n salt: string;\n iv: string; // b64, 12B\n ct: string; // b64, ciphertext || GCM tag\n}\n\n/**\n * Derives a fresh, per-login cache key from login-time secret\n * material and wraps it as a NON-EXTRACTABLE persisted CryptoKey.\n * Raw key bytes exist only transiently during importKey.\n *\n * @param loginMaterial - high-entropy bytes available at login\n * (derived from the seed while it is still in memory).\n * @param salt - FRESH random bytes for this login. Never reuse a\n * salt across logins: the whole point is that a compromised\n * cacheKey from session N is useless in session N+1.\n */\nexport async function deriveCacheKey(\n loginMaterial: Uint8Array,\n salt: Uint8Array\n): Promise<CryptoKey> {\n const base = await crypto.subtle.importKey(\n 'raw', loginMaterial as BufferSource, 'HKDF', false, ['deriveBits']\n );\n const bits = await crypto.subtle.deriveBits(\n { name: 'HKDF', hash: 'SHA-256',\n salt: salt as BufferSource,\n info: new TextEncoder().encode('me2em/cache-wrap/v1') },\n base, 256\n );\n return crypto.subtle.importKey(\n 'raw', bits, { name: 'AES-GCM' }, false, ['encrypt', 'decrypt']\n );\n}\n\nexport async function encryptContext(\n key: CryptoKey, data: IdentityContextData\n): Promise<ContextEnvelope> {\n const iv = crypto.getRandomValues(new Uint8Array(12));\n const salt = crypto.getRandomValues(new Uint8Array(32));\n const pt = new TextEncoder().encode(JSON.stringify(data));\n const ct = await crypto.subtle.encrypt(\n { name: 'AES-GCM', iv: iv as BufferSource }, key, pt\n );\n return { v: 1, salt: toB64(salt), iv: toB64(iv),\n ct: toB64(new Uint8Array(ct)) };\n}\n\nexport async function decryptContext(\n key: CryptoKey, envelope: ContextEnvelope\n): Promise<IdentityContextData> {\n if (envelope.v !== 1) throw new Error('Unsupported envelope version');\n const pt = await crypto.subtle.decrypt(\n { name: 'AES-GCM', iv: fromB64(envelope.iv) as BufferSource },\n key, fromB64(envelope.ct) as BufferSource\n );\n return JSON.parse(new TextDecoder().decode(pt)) as IdentityContextData;\n}\n","// packages/react/src/headless/identity-context-storage.ts\nimport { encryptContext, decryptContext, type ContextEnvelope } from './context-crypto.js';\nimport type { IdentityContextData, IdentityContextStorage } from './identity-context-types.js';\nexport type { IdentityContextData };\n\nconst DB_NAME_PREFIX = 'me2em_';\nconst STORE_NAME = 'context';\nconst KEYS_STORE_NAME = 'cryptoKeys';\nconst CACHE_KEY = 'cache';\n\nfunction hashToHex(data: Uint8Array): string {\n const hexParts: string[] = [];\n for (const byte of data) {\n hexParts.push(byte.toString(16).padStart(2, '0'));\n }\n return hexParts.join('');\n}\n\nasync function sha256Hex(data: Uint8Array): Promise<string> {\n const buffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength) as ArrayBuffer;\n const digest: ArrayBuffer = await crypto.subtle.digest('SHA-256', buffer);\n return hashToHex(new Uint8Array(digest));\n}\n\nasync function dbNameForIdentity(identityId: string): Promise<string> {\n const hash = await sha256Hex(new TextEncoder().encode(identityId));\n return DB_NAME_PREFIX + hash.slice(0, 16);\n}\n\nexport class IdentityContextIDBStorage implements IdentityContextStorage {\n private db: IDBDatabase | null = null;\n private currentIdentityId: string | null = null;\n private cacheKey: CryptoKey | null = null;\n\n private async getDb(): Promise<IDBDatabase> {\n if (!this.db) {\n throw new Error('Storage not opened — call open() first');\n }\n return this.db;\n }\n\n async open(identityId: string): Promise<void> {\n if (this.currentIdentityId !== null && this.currentIdentityId !== identityId) {\n await this.close();\n }\n\n this.currentIdentityId = identityId;\n const name = await dbNameForIdentity(identityId);\n\n return new Promise<void>((resolve, reject) => {\n const request = indexedDB.open(name, 1);\n\n request.onupgradeneeded = () => {\n const db = request.result;\n if (!db.objectStoreNames.contains(STORE_NAME)) {\n db.createObjectStore(STORE_NAME, { keyPath: 'key' });\n }\n if (!db.objectStoreNames.contains(KEYS_STORE_NAME)) {\n db.createObjectStore(KEYS_STORE_NAME, { keyPath: 'key' });\n }\n };\n\n request.onsuccess = () => {\n this.db = request.result;\n resolve();\n };\n\n request.onerror = () => {\n reject(new Error('IndexedDB open failed: ' + request.error?.message));\n };\n });\n }\n\n async openAndLoad(identityId: string): Promise<IdentityContextData | null> {\n await this.open(identityId);\n const key = this.cacheKey ?? await this.loadPersistedKey();\n if (!key) {\n return null;\n }\n this.cacheKey = key;\n return this.load();\n }\n\n async save(data: IdentityContextData): Promise<void> {\n const key = this.cacheKey;\n if (!key) {\n throw new Error('No cache key set — call setCacheKey before save');\n }\n const envelope = await encryptContext(key, data);\n const db = await this.getDb();\n return new Promise<void>((resolve, reject) => {\n const tx = db.transaction(STORE_NAME, 'readwrite');\n const store = tx.objectStore(STORE_NAME);\n store.put({ key: CACHE_KEY, envelope });\n\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(new Error('Save failed: ' + tx.error?.message));\n });\n }\n\n async load(): Promise<IdentityContextData | null> {\n const db = await this.getDb();\n const key = this.cacheKey;\n if (!key) {\n return null;\n }\n return new Promise<IdentityContextData | null>((resolve, reject) => {\n const tx = db.transaction(STORE_NAME, 'readonly');\n const store = tx.objectStore(STORE_NAME);\n const request = store.get(CACHE_KEY);\n\n request.onsuccess = () => {\n const record = request.result;\n if (!record || !record.envelope) {\n resolve(null);\n return;\n }\n decryptContext(key, record.envelope)\n .then((data) => {\n if (data.identityId !== this.currentIdentityId) {\n resolve(null);\n return;\n }\n resolve(data);\n })\n .catch(() => {\n resolve(null);\n });\n };\n\n request.onerror = () => reject(new Error('Load failed: ' + request.error?.message));\n });\n }\n\n async clear(): Promise<void> {\n const db = await this.getDb();\n return new Promise<void>((resolve, reject) => {\n const tx = db.transaction(STORE_NAME, 'readwrite');\n const store = tx.objectStore(STORE_NAME);\n store.delete(CACHE_KEY);\n\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(new Error('Clear failed: ' + tx.error?.message));\n });\n }\n\n async close(): Promise<void> {\n const db = this.db;\n this.db = null;\n this.currentIdentityId = null;\n this.cacheKey = null;\n if (db) {\n db.close();\n }\n }\n\n setCacheKey(key: CryptoKey): void {\n this.cacheKey = key;\n }\n\n getCacheKey(): CryptoKey | null {\n return this.cacheKey;\n }\n\n async persistCacheKey(): Promise<void> {\n const key = this.cacheKey;\n if (!key) {\n throw new Error('No cache key to persist — call setCacheKey first');\n }\n const db = await this.getDb();\n return new Promise<void>((resolve, reject) => {\n const tx = db.transaction(KEYS_STORE_NAME, 'readwrite');\n const store = tx.objectStore(KEYS_STORE_NAME);\n store.put({ key: 'cache', cryptoKey: key });\n\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(new Error('persistCacheKey failed: ' + tx.error?.message));\n });\n }\n\n async loadPersistedKey(): Promise<CryptoKey | null> {\n const db = this.db;\n if (!db) {\n return null;\n }\n return new Promise<CryptoKey | null>((resolve, reject) => {\n const tx = db.transaction(KEYS_STORE_NAME, 'readonly');\n const store = tx.objectStore(KEYS_STORE_NAME);\n const request = store.get('cache');\n\n request.onsuccess = () => {\n const record = request.result;\n if (!record || !record.cryptoKey) {\n resolve(null);\n return;\n }\n resolve(record.cryptoKey);\n };\n\n request.onerror = () => {\n resolve(null);\n };\n });\n }\n}\n"],"mappings":"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","names":["useCallback","useCallback","useReducer","useCallback","get32ByteSeedFromMnemonic","useReducer","useCallback","get32ByteSeedFromMnemonic","useState","useState","useState","useEffect","useState","useEffect","useReducer","useState","jsx","useReducer","useState","useReducer","useCallback","useState","useEffect","jsx","jsxs","useReducer","useState","useCallback","useEffect","useReducer","useEffect","useRef","validateSeedPhrase","wordlist","jsx","jsxs","useReducer","useRef","useEffect","useCallback","jsx","jsxs","useState","useCallback","useEffect","useRef","useState","useRef","useEffect","useCallback","useState","useEffect","useCallback","useRef","useState","useRef","useCallback","useEffect"]}
1
+ {"version":3,"sources":["../src/react/Me2emProvider.tsx","../src/react/context.ts","../src/react/useCreateIdentity.ts","../src/headless/identity-flow.ts","../src/react/useImportSeed.ts","../src/react/useHandle.ts","../src/react/useSubHandle.ts","../src/react/SeedPhraseDisplay.tsx","../src/headless/display-flow.ts","../src/react/SeedPhraseVerify.tsx","../src/headless/seed-words.ts","../src/headless/verify-flow.ts","../src/react/SeedPhraseImport.tsx","../src/headless/import-flow.ts","../src/react/PassphraseInput.tsx","../src/react/useIdentityContext.ts","../src/react/useSession.ts","../src/headless/index.ts","../src/headless/context-crypto.ts","../src/headless/identity-context-storage.ts"],"sourcesContent":["import {\n useState,\n useCallback,\n useMemo,\n type ReactNode,\n} from 'react';\nimport { Identity } from '@me2em/core';\nimport { Me2emContext, type Me2emContextValue } from './context.js';\n\nexport function Me2emProvider({ children }: { children: ReactNode }) {\n const [identity, setIdentity] = useState<Identity | null>(null);\n\n const activateIdentity = useCallback(async (seed: Uint8Array): Promise<boolean> => {\n try {\n const id = await Identity.fromSeed(seed);\n setIdentity(id);\n return true;\n } catch {\n return false;\n }\n }, []);\n\n const clearIdentity = useCallback(() => {\n setIdentity(null);\n }, []);\n\n const value = useMemo<Me2emContextValue>(\n () => ({ identity, hasIdentity: identity !== null, activateIdentity, clearIdentity }),\n [identity, activateIdentity, clearIdentity]\n );\n\n return <Me2emContext.Provider value={value}>{children}</Me2emContext.Provider>;\n}\n","import { createContext, useContext } from 'react';\nimport type { Identity } from '@me2em/core';\n\nexport interface Me2emContextValue {\n identity: Identity | null;\n hasIdentity: boolean;\n activateIdentity: (seed: Uint8Array) => Promise<boolean>;\n clearIdentity: () => void;\n}\n\nexport const Me2emContext = createContext<Me2emContextValue | null>(null);\n\nexport function useMe2emContext(): Me2emContextValue {\n const ctx = useContext(Me2emContext);\n if (!ctx) {\n throw new Error('Me2em hooks must be used inside <Me2emProvider>');\n }\n return ctx;\n}\n","// packages/react/src/react/useCreateIdentity.ts\nimport {\n useReducer,\n useCallback,\n type Reducer,\n} from 'react';\nimport {\n generateSeedPhrase,\n get64ByteSeedFromMnemonic,\n type SeedStrength,\n type Identity,\n} from '@me2em/core';\nimport {\n identityFlowReducer,\n initialIdentityFlowState,\n} from '../headless/identity-flow.js';\nimport type { IdentityFlowState, IdentityFlowEvent, IdentityFlowStatus } from '../headless/types.js';\nimport { useMe2emContext } from './context.js';\n\nexport interface UseCreateIdentityResult {\n status: IdentityFlowStatus;\n seedWords: string[] | null;\n identity: Identity | null;\n error: string | null;\n generate: (opts?: { strength?: SeedStrength }) => void;\n confirmWords: () => Promise<void>;\n reset: () => void;\n fail: (message: string) => void;\n}\n\nexport function useCreateIdentity(): UseCreateIdentityResult {\n const { activateIdentity, clearIdentity, identity } = useMe2emContext();\n const [state, dispatch] = useReducer<\n Reducer<IdentityFlowState, IdentityFlowEvent>\n >(identityFlowReducer, initialIdentityFlowState);\n\n const generate = useCallback(\n (opts?: { strength?: SeedStrength }) => {\n const words = generateSeedPhrase(opts?.strength ?? 128);\n get64ByteSeedFromMnemonic(words).then((derivedSeed) => {\n dispatch({ type: 'GENERATE', words, seed: derivedSeed });\n }).catch(() => {\n dispatch({ type: 'FAIL', message: 'Failed to generate seed' });\n });\n },\n []\n );\n\n const confirmWords = useCallback(async () => {\n if (state.status !== 'seed-generated' || !state.identitySeed) return;\n dispatch({ type: 'CONFIRM_WORDS' });\n const ok = await activateIdentity(state.identitySeed);\n if (!ok) {\n dispatch({ type: 'FAIL', message: 'Identity activation failed' });\n }\n }, [state.status, state.identitySeed, activateIdentity]);\n\n const reset = useCallback(() => {\n dispatch({ type: 'RESET' });\n clearIdentity();\n }, [clearIdentity]);\n\n const fail = useCallback((message: string) => {\n dispatch({ type: 'FAIL', message });\n }, []);\n\n return {\n status: state.status,\n seedWords: state.seedWords,\n identity,\n error: state.error,\n generate,\n confirmWords,\n reset,\n fail,\n };\n}\n","import type { IdentityFlowState, IdentityFlowEvent } from './types.js';\n\nexport const initialIdentityFlowState: IdentityFlowState = {\n status: 'idle',\n seedWords: null,\n identitySeed: null,\n error: null,\n};\n\nconst UNEXPECTED_EVENTS: Record<string, string> = {\n CONFIRM_WORDS: 'CONFIRM_WORDS called from idle — no seed to confirm',\n RESET: 'RESET called from idle — nothing to reset',\n};\n\nexport function identityFlowReducer(\n state: IdentityFlowState,\n event: IdentityFlowEvent\n): IdentityFlowState {\n const { type } = event;\n\n switch (state.status) {\n case 'idle': {\n if (type === 'GENERATE') {\n return {\n status: 'seed-generated',\n seedWords: event.words,\n identitySeed: event.seed,\n error: null,\n };\n }\n if (type === 'IMPORT') {\n return {\n status: 'imported',\n seedWords: null,\n identitySeed: event.seed,\n error: null,\n };\n }\n if (type === 'FAIL') {\n return {\n status: 'error',\n seedWords: null,\n identitySeed: null,\n error: event.message,\n };\n }\n if (UNEXPECTED_EVENTS[type]) {\n console.warn(UNEXPECTED_EVENTS[type]);\n return state;\n }\n return state;\n }\n\n case 'seed-generated': {\n if (type === 'CONFIRM_WORDS') {\n return {\n status: 'verified',\n seedWords: null,\n identitySeed: state.identitySeed,\n error: null,\n };\n }\n if (type === 'FAIL') {\n return {\n status: 'error',\n seedWords: null,\n identitySeed: null,\n error: event.message,\n };\n }\n if (type === 'RESET') {\n return initialIdentityFlowState;\n }\n console.warn(`Unexpected event ${type} in seed-generated`);\n return state;\n }\n\n case 'verified':\n case 'imported': {\n if (type === 'RESET') {\n return initialIdentityFlowState;\n }\n if (type === 'FAIL') {\n return {\n status: 'error',\n seedWords: null,\n identitySeed: null,\n error: event.message,\n };\n }\n console.warn(`Unexpected event ${type} in ${state.status}`);\n return state;\n }\n\n case 'error': {\n if (type === 'RESET') {\n return initialIdentityFlowState;\n }\n console.warn(`Unexpected event ${type} in error`);\n return state;\n }\n\n default:\n return state;\n }\n}\n","// packages/react/src/react/useImportSeed.ts\nimport { useReducer, useCallback, type Reducer } from 'react';\nimport {\n validateSeedPhrase,\n get64ByteSeedFromMnemonic,\n type Identity,\n} from '@me2em/core';\nimport {\n identityFlowReducer,\n initialIdentityFlowState,\n} from '../headless/identity-flow.js';\nimport type { IdentityFlowState, IdentityFlowEvent, IdentityFlowStatus } from '../headless/types.js';\nimport { useMe2emContext } from './context.js';\n\nexport interface UseImportSeedResult {\n status: IdentityFlowStatus;\n identity: Identity | null;\n error: string | null;\n importWords: (words: string[], passphrase?: string) => Promise<void>;\n reset: () => void;\n}\n\nexport function useImportSeed(): UseImportSeedResult {\n const { activateIdentity, clearIdentity, identity } = useMe2emContext();\n const [state, dispatch] = useReducer<\n Reducer<IdentityFlowState, IdentityFlowEvent>\n >(identityFlowReducer, initialIdentityFlowState);\n\n const importWords = useCallback(\n async (words: string[], passphrase?: string) => {\n const validation = validateSeedPhrase(words);\n if (!validation.isValid) {\n dispatch({ type: 'FAIL', message: validation.error ?? 'Invalid seed phrase' });\n return;\n }\n try {\n const seed = await get64ByteSeedFromMnemonic(words, passphrase ?? '');\n dispatch({ type: 'IMPORT', seed });\n await activateIdentity(seed);\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Import failed';\n dispatch({ type: 'FAIL', message });\n }\n },\n [activateIdentity]\n );\n\n const reset = useCallback(() => {\n dispatch({ type: 'RESET' });\n clearIdentity();\n }, [clearIdentity]);\n\n return {\n status: state.status,\n identity,\n error: state.error,\n importWords,\n reset,\n };\n}\n","// packages/react/src/react/useHandle.ts\nimport { useState, useEffect } from 'react';\nimport { type Handle } from '@me2em/core';\nimport { useMe2emContext } from './context.js';\n\nexport function useHandle(name: string): Handle | null {\n const { identity } = useMe2emContext();\n const [handle, setHandle] = useState<Handle | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n if (!identity) {\n // eslint-disable-next-line react-hooks/set-state-in-effect -- clearing the stale async result when identity is gone; set-during-render refactor is a tracked post-rc.2 follow-up\n setHandle(null);\n return;\n }\n // Core performs full name canonicalization (NFKC, lowercase,\n // trim) and validation — no local normalization here.\n identity.deriveHandle(name).then((h) => {\n if (!cancelled) setHandle(h);\n }).catch((err) => {\n if (!cancelled) console.error('useHandle derivation failed', err);\n });\n return () => { cancelled = true; };\n }, [identity, name]);\n\n return handle;\n}\n","import { useState, useEffect } from 'react';\nimport { type SubHandle } from '@me2em/core';\nimport { useMe2emContext } from './context.js';\n\nexport function useSubHandle(\n handleName: string,\n subName: string\n): SubHandle | null {\n const { identity } = useMe2emContext();\n const [subHandle, setSubHandle] = useState<SubHandle | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n if (!identity) {\n // eslint-disable-next-line react-hooks/set-state-in-effect -- clearing the stale async result when identity is gone; set-during-render refactor is a tracked post-rc.2 follow-up\n setSubHandle(null);\n return;\n }\n identity\n .deriveSubHandle(handleName, subName)\n .then((h) => {\n if (!cancelled) setSubHandle(h);\n })\n .catch((err) => {\n if (!cancelled) console.error('useSubHandle derivation failed', err);\n });\n return () => { cancelled = true; };\n }, [identity, handleName, subName]);\n\n return subHandle;\n}\n","// packages/react/src/react/SeedPhraseDisplay.tsx\nimport { useReducer, useState } from 'react';\nimport {\n displayFlowReducer,\n initialDisplayState,\n} from '../headless/display-flow.js';\n\nexport interface SeedPhraseDisplayProps {\n /** Seed material is reachable via React DevTools in this window; by design. */\n words: string[];\n onDismiss?: () => void;\n onCopyAllowed?: () => void;\n}\n\n/**\n * R8: the permanent/transient line for a failed clipboard write is\n * \"can a retry in this window ever succeed?\". Without the async\n * Clipboard API (non-secure context, unsupported browser) it cannot —\n * the failure is permanent, the copy-all button is disabled and the\n * user is pointed at manual word selection. With the API present any\n * rejection (lost focus, dismissed permission prompt, OS hiccup)\n * stays transient: the button remains enabled and a fresh attempt\n * may go through.\n */\nfunction clipboardWriteAvailable(): boolean {\n return (\n typeof navigator !== 'undefined' &&\n !!navigator.clipboard &&\n typeof navigator.clipboard.writeText === 'function'\n );\n}\n\nexport function SeedPhraseDisplay({\n words,\n onDismiss,\n onCopyAllowed,\n}: SeedPhraseDisplayProps) {\n const [state, dispatch] = useReducer(displayFlowReducer, initialDisplayState);\n const [copyError, setCopyError] = useState<'transient' | 'permanent' | null>(null);\n\n const { status, copyCount } = state;\n\n const handleReveal = () => {\n dispatch({ type: 'REVEAL', at: Date.now() });\n };\n\n const handleCopy = async () => {\n if (status === 'revealed' && copyCount === 0) {\n try {\n await navigator.clipboard.writeText(words.join(' '));\n dispatch({ type: 'COPY', at: Date.now() });\n setCopyError(null);\n onCopyAllowed?.();\n } catch {\n setCopyError(clipboardWriteAvailable() ? 'transient' : 'permanent');\n }\n }\n };\n\n const handleDismiss = () => {\n dispatch({ type: 'DISMISS' });\n onDismiss?.();\n };\n\n if (status === 'hidden') {\n return (\n <button data-testid=\"reveal\" onClick={handleReveal}>\n Reveal\n </button>\n );\n }\n\n if (status === 'dismissed') {\n return (\n <div data-testid=\"dismissed\">\n Phrase dismissed\n </div>\n );\n }\n\n return (\n <div>\n <ol data-testid=\"words\">\n {words.map((word, i) => (\n <li\n key={i}\n data-testid={`word-${i}`}\n data-position={i}\n >\n {word}\n </li>\n ))}\n </ol>\n {copyError && (\n <div data-testid=\"copy-error\" data-kind={copyError}>\n {copyError === 'permanent'\n ? 'Copy unavailable in this context — select the words manually'\n : 'Copy failed — try again'}\n </div>\n )}\n <button\n data-testid=\"copy-all\"\n onClick={handleCopy}\n disabled={copyCount === 1 || copyError === 'permanent'}\n >\n {copyCount === 1 ? 'Copied' : 'Copy all'}\n </button>\n <button data-testid=\"dismiss\" onClick={handleDismiss}>\n Hide\n </button>\n </div>\n );\n}\n","// packages/react/src/headless/display-flow.ts\n\nexport type DisplayStatus =\n | 'hidden'\n | 'revealed'\n | 'copied'\n | 'dismissed';\n\nexport interface DisplayState {\n status: DisplayStatus;\n copyCount: number;\n revealedAt: number | null;\n}\n\nexport type DisplayEvent =\n | { type: 'REVEAL'; at: number }\n | { type: 'COPY'; at: number }\n | { type: 'DISMISS' }\n | { type: 'RESET' };\n\nexport const initialDisplayState: DisplayState = {\n status: 'hidden',\n copyCount: 0,\n revealedAt: null,\n};\n\nexport function displayFlowReducer(\n state: DisplayState,\n event: DisplayEvent\n): DisplayState {\n const { type } = event;\n\n switch (state.status) {\n case 'hidden': {\n if (type === 'REVEAL') {\n return {\n status: 'revealed',\n copyCount: 0,\n revealedAt: event.at,\n };\n }\n if (type === 'COPY') {\n console.warn('Cannot copy while hidden');\n return state;\n }\n if (type === 'RESET') {\n return initialDisplayState;\n }\n if (type === 'DISMISS') {\n console.warn('Cannot dismiss while hidden');\n return state;\n }\n console.warn(`Unexpected event ${type} in hidden`);\n return state;\n }\n\n case 'revealed': {\n if (type === 'COPY') {\n if (state.copyCount === 0) {\n return {\n status: 'copied',\n copyCount: 1,\n revealedAt: state.revealedAt,\n };\n }\n console.warn('Already copied — duplicate copy blocked');\n return state;\n }\n if (type === 'DISMISS') {\n return {\n status: 'dismissed',\n copyCount: state.copyCount,\n revealedAt: state.revealedAt,\n };\n }\n if (type === 'RESET') {\n return initialDisplayState;\n }\n console.warn(`Unexpected event ${type} in revealed`);\n return state;\n }\n\n case 'copied': {\n if (type === 'COPY') {\n console.warn('Already copied — duplicate copy blocked');\n return state;\n }\n if (type === 'DISMISS') {\n return {\n status: 'dismissed',\n copyCount: 1,\n revealedAt: state.revealedAt,\n };\n }\n if (type === 'RESET') {\n return initialDisplayState;\n }\n console.warn(`Unexpected event ${type} in copied`);\n return state;\n }\n\n case 'dismissed': {\n if (type === 'RESET') {\n return initialDisplayState;\n }\n console.warn(`Unexpected event ${type} in dismissed`);\n return state;\n }\n\n default:\n return state;\n }\n}\n","// packages/react/src/react/SeedPhraseVerify.tsx\nimport { useReducer, useCallback, useState, useEffect, useRef } from 'react';\nimport {\n verifyFlowReducer,\n initVerifyState,\n} from '../headless/verify-flow.js';\nimport { wordlist } from '@me2em/core';\n\nexport interface SeedPhraseVerifyProps {\n realWords: string[];\n gridSize?: number;\n wordlist?: readonly string[];\n onVerified: () => void;\n onFailed?: (attempts: number) => void;\n}\n\nconst DEFAULT_GRID_SIZE_OFFSET = 8;\n\nexport function SeedPhraseVerify({\n realWords,\n gridSize,\n wordlist: propWordlist,\n onVerified,\n onFailed,\n}: SeedPhraseVerifyProps) {\n const grid = gridSize ?? realWords.length + DEFAULT_GRID_SIZE_OFFSET;\n const allWords = propWordlist ?? wordlist;\n const baseSeed = 42;\n\n const [state, dispatch] = useReducer(\n verifyFlowReducer,\n {\n realWords,\n gridSize: grid,\n randomSeed: baseSeed,\n allWords,\n } as const,\n (params) => initVerifyState(params.realWords, params.gridSize, params.randomSeed, params.allWords)\n );\n\n const [retryCount, setRetryCount] = useState(0);\n\n const { status, grid: gridData, selected } = state;\n\n // Guard against re-entrant callbacks after status change\n const notifiedRef = useRef(false);\n\n const handleSelect = useCallback(\n (index: number) => {\n if (status === 'correct' || status === 'incorrect') return;\n if (selected.length >= gridData.correctIndices.length) return;\n dispatch({ type: 'SELECT', index });\n },\n [status, selected.length, gridData.correctIndices.length]\n );\n\n // Single effect: auto-check when enough words selected, then notify result\n useEffect(() => {\n if (notifiedRef.current) return;\n\n // Auto-check when enough words selected\n if (\n selected.length === gridData.correctIndices.length &&\n selected.length > 0 &&\n status === 'collecting'\n ) {\n dispatch({ type: 'CHECK' });\n // After dispatch, status will change synchronously in the reducer\n // but the new state won't be reflected until next render.\n // We need to check the pending result in the next effect cycle.\n return;\n }\n\n // Handle result after status update\n if (status === 'correct') {\n notifiedRef.current = true;\n onVerified();\n } else if (status === 'incorrect') {\n notifiedRef.current = true;\n onFailed?.(state.attempts);\n }\n }, [selected.length, gridData.correctIndices.length, status, state.attempts, onVerified, onFailed]);\n\n const handleRetry = () => {\n notifiedRef.current = false;\n setRetryCount((c) => c + 1);\n dispatch({\n type: 'RESET',\n realWords,\n gridSize: grid,\n randomSeed: baseSeed + retryCount + 1,\n allWords,\n });\n };\n\n return (\n <div>\n <div data-testid=\"progress\">\n Select word {selected.length + 1} of {realWords.length}\n </div>\n <ol data-testid=\"grid\">\n {gridData.words.map((word, i) => {\n const isSelected = selected.includes(i);\n const isDisabled =\n isSelected || status === 'correct' || status === 'incorrect';\n return (\n <li key={i}>\n <button\n data-testid={`cell-${i}`}\n data-word={word}\n disabled={isDisabled}\n onClick={() => handleSelect(i)}\n >\n {word}\n </button>\n </li>\n );\n })}\n </ol>\n {status === 'incorrect' && (\n <button data-testid=\"retry\" onClick={handleRetry}>\n Try again\n </button>\n )}\n </div>\n );\n}\n","// packages/react/src/headless/seed-words.ts\n\nexport function shuffleWithSeed<T>(items: T[], seed: number): T[] {\n const arr = [...items];\n let currentSeed = seed;\n function nextRand(): number {\n currentSeed = (currentSeed * 1664525 + 1013904223) >>> 0;\n return currentSeed;\n }\n for (let i = arr.length - 1; i > 0; i--) {\n const j = nextRand() % (i + 1);\n [arr[i], arr[j]] = [arr[j], arr[i]];\n }\n return arr;\n}\n\nexport function buildVerificationGrid(\n realWords: string[],\n gridSize: number,\n randomSeed: number,\n allWords: readonly string[]\n): { words: string[]; correctIndices: number[] } {\n if (gridSize < realWords.length) {\n throw new Error(\n `gridSize (${gridSize}) must be >= realWords.length (${realWords.length})`\n );\n }\n\n const shuffled = shuffleWithSeed(realWords, randomSeed);\n const realSet = new Set(realWords);\n const available = allWords.filter((w) => !realSet.has(w));\n const shuffledDecoys = shuffleWithSeed(available, randomSeed + 1);\n\n const needed = gridSize - realWords.length;\n const decoys = shuffledDecoys.slice(0, needed);\n\n const words = [...shuffled, ...decoys];\n if (words.length !== gridSize) {\n throw new Error(\n `Cannot fill grid: wordlist too small (got ${words.length}, need ${gridSize})`\n );\n }\n\n // Map each word occurrence to a DISTINCT grid position, so\n // repeated words in the phrase get their own cells.\n const positions = new Map<string, number[]>();\n words.forEach((w, i) => {\n const list = positions.get(w);\n if (list) list.push(i);\n else positions.set(w, [i]);\n });\n\n const correctIndices = realWords.map((w) => {\n const list = positions.get(w);\n if (!list || list.length === 0) {\n throw new Error(`Internal error: word \"${w}\" missing from grid`);\n }\n return list.shift() as number;\n });\n\n return { words, correctIndices };\n}\n\n/**\n * Selection is compared IN ORDER: selected[i] must equal\n * correctIndices[i]. The i-th click must be the i-th word of the\n * phrase. Repeated phrase words map to distinct grid cells.\n */\nexport function isGridSelectionCorrect(\n grid: { words: string[]; correctIndices: number[] },\n selected: number[]\n): boolean {\n if (selected.length !== grid.correctIndices.length) return false;\n return selected.every((v, i) => v === grid.correctIndices[i]);\n}\n","// packages/react/src/headless/verify-flow.ts\nimport {\n buildVerificationGrid,\n isGridSelectionCorrect,\n} from './seed-words.js';\n\nexport type VerifyStatus = 'collecting' | 'correct' | 'incorrect';\n\nexport interface VerifyState {\n status: VerifyStatus;\n grid: { words: string[]; correctIndices: number[] };\n selected: number[];\n attempts: number;\n}\n\nexport type VerifyEvent =\n | { type: 'SELECT'; index: number }\n | { type: 'DESELECT'; index: number }\n | { type: 'CHECK' }\n | {\n type: 'RESET';\n realWords: string[];\n gridSize: number;\n randomSeed: number;\n allWords: readonly string[];\n };\n\nexport function initVerifyState(\n realWords: string[],\n gridSize: number,\n randomSeed: number,\n allWords: readonly string[]\n): VerifyState {\n const grid = buildVerificationGrid(realWords, gridSize, randomSeed, allWords);\n return {\n status: 'collecting',\n grid,\n selected: [],\n attempts: 0,\n };\n}\n\nexport function verifyFlowReducer(\n state: VerifyState,\n event: VerifyEvent\n): VerifyState {\n const { type } = event;\n\n if (type === 'RESET') {\n return initVerifyState(\n event.realWords,\n event.gridSize,\n event.randomSeed,\n event.allWords\n );\n }\n\n // Terminal states: only RESET allowed\n if (state.status === 'correct' || state.status === 'incorrect') {\n console.warn(\n `Cannot ${type} in ${state.status} state — use RESET to retry`\n );\n return state;\n }\n\n // collecting state\n if (type === 'SELECT') {\n const { index } = event;\n const { grid, selected } = state;\n if (selected.includes(index)) {\n console.warn(`Index ${index} already selected — use DESELECT`);\n return state;\n }\n if (index < 0 || index >= grid.words.length) {\n console.warn(`Index ${index} out of grid bounds`);\n return state;\n }\n return { ...state, selected: [...selected, index] };\n }\n\n if (type === 'DESELECT') {\n const { index } = event;\n const idx = state.selected.indexOf(index);\n if (idx === -1) {\n console.warn(`Index ${index} not selected`);\n return state;\n }\n return {\n ...state,\n selected: state.selected.filter((_, i) => i !== idx),\n };\n }\n\n if (type === 'CHECK') {\n const { grid, selected, attempts } = state;\n if (selected.length !== grid.correctIndices.length) {\n console.warn(\n `Cannot check: selected ${selected.length}, need ${grid.correctIndices.length}`\n );\n return state;\n }\n const correct = isGridSelectionCorrect(grid, selected);\n return {\n ...state,\n status: correct ? 'correct' : 'incorrect',\n attempts: attempts + 1,\n };\n }\n\n console.warn(`Unexpected event ${type} in collecting`);\n return state;\n}\n","// packages/react/src/react/SeedPhraseImport.tsx\nimport { useReducer, useEffect, useRef } from 'react';\nimport {\n importFlowReducer,\n initImportState,\n type ImportState,\n} from '../headless/import-flow.js';\nimport { wordlist } from '@me2em/core';\n\nexport interface SeedPhraseImportProps {\n expectedCount?: 12 | 24;\n wordlist?: readonly string[];\n onImported: (words: string[]) => void;\n}\n\nexport function SeedPhraseImport({\n expectedCount: propCount,\n wordlist: propWordlist,\n onImported,\n}: SeedPhraseImportProps) {\n const allWords = propWordlist ?? wordlist;\n\n const [state, dispatch] = useReducer(\n importFlowReducer,\n { expectedCount: propCount ?? 12 } as const,\n (params) => initImportState(params.expectedCount)\n );\n\n const count = state.expectedCount;\n\n const prevStatusRef = useRef<ImportState['status']>(state.status);\n\n const lastImportedRef = useRef<string | null>(null);\n\n // Auto-check on every word change\n useEffect(() => {\n dispatch({ type: 'CHECK' });\n }, [state.words]);\n\n // Call onImported once when transitioning to valid\n useEffect(() => {\n const prevStatus = prevStatusRef.current;\n prevStatusRef.current = state.status;\n\n if (state.status === 'valid' && prevStatus !== 'valid') {\n const key = (state.words as string[]).join('\\0');\n if (lastImportedRef.current !== key) {\n lastImportedRef.current = key;\n onImported(state.words as string[]);\n }\n }\n }, [state.status, state.words, onImported]);\n\n const handleCountChange = (n: 12 | 24) => {\n dispatch({ type: 'INIT', expectedCount: n });\n };\n\n const handleWordChange = (position: number, value: string) => {\n dispatch({ type: 'SET_WORD', position, word: value });\n };\n\n const isWordInvalid = (position: number): boolean => {\n if (state.status !== 'invalid' && state.status !== 'valid') return false;\n const word = state.words[position];\n if (word === null) return false;\n return !allWords.includes(word);\n };\n\n const isImportEnabled = state.status === 'valid';\n\n return (\n <div>\n <div>\n <button\n data-testid=\"count-12\"\n onClick={() => handleCountChange(12)}\n disabled={count === 12}\n >\n 12 words\n </button>\n <button\n data-testid=\"count-24\"\n onClick={() => handleCountChange(24)}\n disabled={count === 24}\n >\n 24 words\n </button>\n </div>\n <div data-testid=\"inputs\">\n {Array.from({ length: count }, (_, i) => (\n <input\n key={i}\n data-testid={`word-${i}`}\n aria-label={`Word ${i + 1}`}\n value={state.words[i] ?? ''}\n onChange={(e) => handleWordChange(i, e.target.value)}\n data-invalid={isWordInvalid(i) ? 'true' : undefined}\n />\n ))}\n </div>\n <button\n data-testid=\"import\"\n disabled={!isImportEnabled}\n onClick={() => dispatch({ type: 'CHECK' })}\n >\n Import\n </button>\n </div>\n );\n}\n","// packages/react/src/headless/import-flow.ts\nimport { validateSeedPhrase } from '@me2em/core';\n\nexport type ImportStatus = 'entering' | 'valid' | 'invalid';\n\nexport interface ImportState {\n status: ImportStatus;\n words: (string | null)[];\n expectedCount: 12 | 24;\n}\n\nexport type ImportEvent =\n | { type: 'INIT'; expectedCount: 12 | 24 }\n | { type: 'SET_WORD'; position: number; word: string }\n | { type: 'CLEAR_WORD'; position: number }\n | { type: 'CHECK' }\n | { type: 'RESET' };\n\nexport function initImportState(expectedCount: 12 | 24): ImportState {\n return {\n status: 'entering',\n words: Array(expectedCount).fill(null),\n expectedCount,\n };\n}\n\nexport function importFlowReducer(\n state: ImportState,\n event: ImportEvent\n): ImportState {\n const { type } = event;\n\n if (type === 'INIT') {\n return initImportState(event.expectedCount);\n }\n\n if (type === 'RESET') {\n return initImportState(state.expectedCount);\n }\n\n if (type === 'SET_WORD') {\n const { position, word } = event;\n if (position < 0 || position >= state.expectedCount) {\n console.warn(`SET_WORD: position ${position} out of bounds`);\n return state;\n }\n const normalized = word.toLowerCase().trim();\n const newWords = [...state.words] as (string | null)[];\n newWords[position] = normalized;\n return { ...state, words: newWords, status: 'entering' };\n }\n\n if (type === 'CLEAR_WORD') {\n const { position } = event;\n if (position < 0 || position >= state.expectedCount) {\n console.warn(`CLEAR_WORD: position ${position} out of bounds`);\n return state;\n }\n const newWords = [...state.words] as (string | null)[];\n newWords[position] = null;\n return { ...state, words: newWords, status: 'entering' };\n }\n\n if (type === 'CHECK') {\n const { words } = state;\n const nullIdx = words.findIndex((w) => w === null);\n if (nullIdx !== -1) {\n return {\n ...state,\n status: 'invalid',\n };\n }\n const result = validateSeedPhrase(words as string[]);\n return {\n ...state,\n status: result.isValid ? 'valid' : 'invalid',\n };\n }\n\n console.warn(`Unexpected event ${type} in import-flow`);\n return state;\n}\n","// packages/react/src/react/PassphraseInput.tsx\nimport { useCallback } from 'react';\n\nexport interface PassphraseInputProps {\n onPassphraseChange: (p: string) => void;\n showWarnings?: boolean;\n}\n\nexport function PassphraseInput({\n onPassphraseChange,\n showWarnings = true,\n}: PassphraseInputProps) {\n const handleChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const value = e.target.value.normalize('NFKC');\n onPassphraseChange(value);\n },\n [onPassphraseChange]\n );\n\n return (\n <div>\n <input\n type=\"password\"\n data-testid=\"passphrase\"\n aria-label=\"BIP39 passphrase\"\n onChange={handleChange}\n />\n {showWarnings && (\n <div data-testid=\"warning\">\n Passphrase is case-sensitive and NOT recoverable. A different\n passphrase silently derives a different wallet.\n </div>\n )}\n </div>\n );\n}\n","// packages/react/src/react/useIdentityContext.ts\nimport { useState, useCallback, useEffect, useRef } from 'react';\nimport type { Identity } from '@me2em/core';\nimport type { IdentityContextStorage, IdentityContextData } from '../headless/identity-context-types.js';\nimport { useMe2emContext } from './context.js';\n\nfunction publicKeyToId(publicKey: Uint8Array): string {\n return Array.from(publicKey)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\nfunction identityToId(identity: Identity | null): string | null {\n return identity ? publicKeyToId(identity.getPublicKey()) : null;\n}\n\nexport interface UseIdentityContextResult {\n context: IdentityContextData | null;\n isLoaded: boolean;\n error: string | null;\n save: (data: IdentityContextData) => Promise<void>;\n clear: () => Promise<void>;\n /** Reload the cache for the current identity (after sync). */\n refresh: () => Promise<void>;\n}\n\nexport function useIdentityContext(\n storage: IdentityContextStorage\n): UseIdentityContextResult {\n const { identity } = useMe2emContext();\n const [context, setContext] = useState<IdentityContextData | null>(null);\n const [isLoaded, setIsLoaded] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const prevIdentityIdRef = useRef<string | null>(null);\n // Generation ticket for async state transitions. Load (identity\n // change / `refresh()`), `save()` and `clear()` each take a ticket;\n // a resolution may only commit state while its ticket is the newest\n // one. Without this, an in-flight load started before a save could\n // resolve after it and clobber the just-saved state with pre-save\n // data (out-of-order async update — observed as a rare CI flake in\n // the rerender spec; pinned there by a gated-storage test).\n const opRef = useRef(0);\n const identityRef = useRef<Identity | null>(null);\n // eslint-disable-next-line react-hooks/refs -- render-time mirror of the latest identity: save()/clear() are stable callbacks (storage-only deps) and must verify at commit time, after their await, that the identity they started for is still the active one\n identityRef.current = identity;\n\n useEffect(() => {\n let cancelled = false;\n\n if (!identity) {\n // eslint-disable-next-line react-hooks/set-state-in-effect -- clearing the stale async state when identity is gone (generation-ticket effect from the load-race fix); set-during-render refactor is a tracked post-rc.2 follow-up\n setContext(null);\n setIsLoaded(true);\n setError(null);\n return;\n }\n\n const identityId = publicKeyToId(identity.getPublicKey());\n\n // If identity changed, close old storage and open new\n if (prevIdentityIdRef.current !== null && prevIdentityIdRef.current !== identityId) {\n storage.close().catch(() => { /* ignore close errors */ });\n }\n\n prevIdentityIdRef.current = identityId;\n\n // Open storage for this identity and load cached context (atomic)\n const op = ++opRef.current;\n storage.openAndLoad(identityId)\n .then((data) => {\n if (cancelled || op !== opRef.current) return;\n setContext(data);\n setIsLoaded(true);\n setError(null);\n })\n .catch((err) => {\n if (cancelled) return;\n setError(err instanceof Error ? err.message : String(err));\n setIsLoaded(true);\n });\n\n return () => {\n cancelled = true;\n };\n }, [identity, storage]);\n\n const save = useCallback(async (data: IdentityContextData): Promise<void> => {\n const idAtStart = identityToId(identityRef.current);\n opRef.current += 1; // invalidate any in-flight load/refresh\n await storage.save(data);\n // If the identity switched while the write was in flight, state\n // ownership has moved to the new identity's load effect — committing\n // here would surface identity A's context under identity B. The\n // generation ticket above only orders this op against in-flight\n // loads; it does not know about identity switches.\n if (identityToId(identityRef.current) !== idAtStart) return;\n setContext(data);\n setError(null);\n }, [storage]);\n\n const clear = useCallback(async (): Promise<void> => {\n const idAtStart = identityToId(identityRef.current);\n opRef.current += 1; // invalidate any in-flight load/refresh\n await storage.clear();\n // Same ownership rule as save(): a clear() resolving after an\n // identity switch must not blank the new identity's freshly\n // loaded context.\n if (identityToId(identityRef.current) !== idAtStart) return;\n setContext(null);\n setError(null);\n }, [storage]);\n\n const refresh = useCallback(async (): Promise<void> => {\n if (!identity) return;\n const identityId = publicKeyToId(identity.getPublicKey());\n const op = ++opRef.current;\n const data = await storage.openAndLoad(identityId);\n if (op !== opRef.current) return;\n setContext(data);\n }, [identity, storage]);\n\n return { context, isLoaded, error, save, clear, refresh };\n}\n","// packages/react/src/react/useSession.ts\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport type { Handle } from '@me2em/core';\nimport { Session } from '@me2em/core';\n\n/**\n * Options for useSession, memoized via deep comparison of\n * `handle` and `options` refs on each render.\n *\n * @category Types\n */\nexport interface UseSessionOptions {\n /** Intended audience (recipient) of this session. */\n audience: string;\n /** Permission scopes granted in this session. */\n scopes: string[];\n /** Time-to-live in seconds. */\n ttl: number;\n}\n\n/**\n * Result of the useSession hook.\n *\n * @category Types\n */\nexport interface UseSessionResult {\n /** The active session, or null if no handle or session creation failed. */\n session: Session | null;\n /** Whether the session (or its absence) has been determined. */\n isLoaded: boolean;\n /** Error string if session creation or renewal failed. */\n error: string | null;\n /** Whether the current session has expired. */\n isExpired: boolean;\n /** Manually renew the session (creates a new one with a fresh jti). */\n renew: () => void;\n}\n\n/**\n * Hook that creates and manages a signed session token for a Handle.\n *\n * When `handle` is provided, creates a Session with the given options.\n * Supports auto-renewal: when `autoRenew` is true (default), a timer\n * fires at half the session TTL to create a fresh session with a new\n * jti (the previous token remains valid until its own expiry, but the\n * lineage identity is preserved through `handle`).\n *\n * On unmount, all timers are cleaned up to prevent state updates after\n * the component is gone.\n *\n * Dependencies: `[handle, renew]` — the options object is read through\n * a ref, so passing a fresh object literal on every render does NOT\n * re-trigger session creation. Only changes to `handle` (or to the\n * renewal function identity, which follows `handle`) do.\n *\n * @param handle - The Handle to sign the session, or null.\n * @param options - Session options (audience, scopes, ttl).\n * @param autoRenew - Whether to auto-renew at half-life (default: true).\n * @returns Session state with manual `renew` control.\n *\n * @example\n * ```ts\n * const handle = useHandle('my-handle');\n * const { session, isExpired, renew } = useSession(handle, {\n * audience: 'app.example.com',\n * scopes: ['read', 'write'],\n * ttl: 3600,\n * });\n * ```\n */\nexport function useSession(\n handle: Handle | null,\n options: UseSessionOptions,\n autoRenew: boolean = true\n): UseSessionResult {\n const [session, setSession] = useState<Session | null>(null);\n const [isLoaded, setIsLoaded] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const optionsRef = useRef(options);\n const mountedRef = useRef(true);\n\n // Mirror the latest options into the ref after every commit —\n // ref writes belong in effects, not during render (react-hooks/refs).\n useEffect(() => {\n optionsRef.current = options;\n });\n\n const clearTimer = useCallback(() => {\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n }, []);\n\n // Cleanup on unmount\n useEffect(() => {\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n clearTimer();\n };\n }, [clearTimer]);\n\n const renew = useCallback(() => {\n clearTimer();\n const opts = optionsRef.current;\n\n if (!handle) {\n if (!mountedRef.current) return;\n setSession(null);\n setIsLoaded(true);\n setError(null);\n return;\n }\n\n Session.create(handle, {\n audience: opts.audience,\n scopes: opts.scopes,\n ttl: opts.ttl,\n }).then((s) => {\n if (!mountedRef.current) return;\n setSession(s);\n setIsLoaded(true);\n setError(null);\n\n // Schedule auto-renewal at half-life\n if (autoRenew) {\n const halfLife = Math.floor((s.expiresAt * 1000 - Date.now()) / 2);\n if (halfLife > 0) {\n timerRef.current = setTimeout(() => {\n // eslint-disable-next-line react-hooks/immutability -- self-rescheduling renewal timer: the timer is cleared and re-created whenever renew itself changes (deps + clearTimer), so the captured binding is always the current one\n renew();\n }, Math.max(halfLife, 1000));\n }\n }\n }).catch((err) => {\n if (!mountedRef.current) return;\n setSession(null);\n setIsLoaded(true);\n setError(err instanceof Error ? err.message : String(err));\n });\n }, [handle, autoRenew, clearTimer]);\n\n // Create session when handle or options change\n useEffect(() => {\n if (mountedRef.current) {\n renew();\n }\n }, [handle, renew]);\n\n return {\n session,\n isLoaded,\n error,\n isExpired: session ? session.isExpired() : false,\n renew,\n };\n}\n","export { identityFlowReducer, initialIdentityFlowState } from './identity-flow.js';\nexport type {\n IdentityFlowState,\n IdentityFlowStatus,\n IdentityFlowEvent,\n} from './types.js';\nexport {\n buildVerificationGrid,\n isGridSelectionCorrect,\n shuffleWithSeed,\n} from './seed-words.js';\nexport {\n displayFlowReducer,\n initialDisplayState,\n} from './display-flow.js';\nexport type {\n DisplayState,\n DisplayStatus,\n DisplayEvent,\n} from './display-flow.js';\nexport {\n verifyFlowReducer,\n initVerifyState,\n} from './verify-flow.js';\nexport type {\n VerifyState,\n VerifyStatus,\n VerifyEvent,\n} from './verify-flow.js';\nexport {\n importFlowReducer,\n initImportState,\n} from './import-flow.js';\nexport type {\n ImportState,\n ImportStatus,\n ImportEvent,\n} from './import-flow.js';\nexport {\n IdentityContextIDBStorage,\n} from './identity-context-storage.js';\nexport type {\n IdentityContextData,\n IdentityContextStorage,\n} from './identity-context-types.js';\nexport {\n deriveCacheKey,\n encryptContext,\n decryptContext,\n} from './context-crypto.js';\nexport type {\n ContextEnvelope,\n} from './context-crypto.js';\n","// packages/react/src/headless/context-crypto.ts\nimport { base64urlEncode, base64urlDecode } from '@me2em/core';\nimport type { IdentityContextData } from './identity-context-types.js';\n\n// Base64url encoding for ContextEnvelope fields goes through the audited\n// @scure/base codec re-exported by @me2em/core (audit A3 / R4) — one\n// base64 implementation across packages, no hand-rolled alphabet handling.\n// Envelope fields are always unpadded: the only writer is encryptContext\n// below, so base64urlDecode's strict no-padding rule never bites.\n\nexport interface ContextEnvelope {\n v: 1;\n /** fresh HKDF salt этого логина (b64). Нужен только при\n * первичной деривации; persisted CryptoKey переживает\n * перезапуск без него. */\n salt: string;\n iv: string; // b64, 12B\n ct: string; // b64, ciphertext || GCM tag\n}\n\n/**\n * Derives a fresh, per-login cache key from login-time secret\n * material and wraps it as a NON-EXTRACTABLE persisted CryptoKey.\n * Raw key bytes exist only transiently during importKey.\n *\n * @param loginMaterial - high-entropy bytes available at login\n * (derived from the seed while it is still in memory).\n * @param salt - FRESH random bytes for this login. Never reuse a\n * salt across logins: the whole point is that a compromised\n * cacheKey from session N is useless in session N+1.\n */\nexport async function deriveCacheKey(\n loginMaterial: Uint8Array,\n salt: Uint8Array\n): Promise<CryptoKey> {\n const base = await crypto.subtle.importKey(\n 'raw', loginMaterial as BufferSource, 'HKDF', false, ['deriveBits']\n );\n const bits = await crypto.subtle.deriveBits(\n { name: 'HKDF', hash: 'SHA-256',\n salt: salt as BufferSource,\n info: new TextEncoder().encode('me2em/cache-wrap/v1') },\n base, 256\n );\n return crypto.subtle.importKey(\n 'raw', bits, { name: 'AES-GCM' }, false, ['encrypt', 'decrypt']\n );\n}\n\nexport async function encryptContext(\n key: CryptoKey, data: IdentityContextData\n): Promise<ContextEnvelope> {\n const iv = crypto.getRandomValues(new Uint8Array(12));\n const salt = crypto.getRandomValues(new Uint8Array(32));\n const pt = new TextEncoder().encode(JSON.stringify(data));\n const ct = await crypto.subtle.encrypt(\n { name: 'AES-GCM', iv: iv as BufferSource }, key, pt\n );\n return { v: 1, salt: base64urlEncode(salt), iv: base64urlEncode(iv),\n ct: base64urlEncode(new Uint8Array(ct)) };\n}\n\nexport async function decryptContext(\n key: CryptoKey, envelope: ContextEnvelope\n): Promise<IdentityContextData> {\n if (envelope.v !== 1) throw new Error('Unsupported envelope version');\n const pt = await crypto.subtle.decrypt(\n { name: 'AES-GCM', iv: base64urlDecode(envelope.iv) as BufferSource },\n key, base64urlDecode(envelope.ct) as BufferSource\n );\n return JSON.parse(new TextDecoder().decode(pt)) as IdentityContextData;\n}\n","// packages/react/src/headless/identity-context-storage.ts\nimport { encryptContext, decryptContext } from './context-crypto.js';\nimport type { IdentityContextData, IdentityContextStorage } from './identity-context-types.js';\nexport type { IdentityContextData };\n\nconst DB_NAME_PREFIX = 'me2em_';\nconst STORE_NAME = 'context';\nconst KEYS_STORE_NAME = 'cryptoKeys';\nconst CACHE_KEY = 'cache';\n\nfunction hashToHex(data: Uint8Array): string {\n const hexParts: string[] = [];\n for (const byte of data) {\n hexParts.push(byte.toString(16).padStart(2, '0'));\n }\n return hexParts.join('');\n}\n\nasync function sha256Hex(data: Uint8Array): Promise<string> {\n const buffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength) as ArrayBuffer;\n const digest: ArrayBuffer = await crypto.subtle.digest('SHA-256', buffer);\n return hashToHex(new Uint8Array(digest));\n}\n\nasync function dbNameForIdentity(identityId: string): Promise<string> {\n const hash = await sha256Hex(new TextEncoder().encode(identityId));\n return DB_NAME_PREFIX + hash.slice(0, 16);\n}\n\nexport class IdentityContextIDBStorage implements IdentityContextStorage {\n private db: IDBDatabase | null = null;\n private currentIdentityId: string | null = null;\n private cacheKey: CryptoKey | null = null;\n\n private async getDb(): Promise<IDBDatabase> {\n if (!this.db) {\n throw new Error('Storage not opened — call open() first');\n }\n return this.db;\n }\n\n async open(identityId: string): Promise<void> {\n if (this.currentIdentityId !== null && this.currentIdentityId !== identityId) {\n await this.close();\n }\n\n this.currentIdentityId = identityId;\n const name = await dbNameForIdentity(identityId);\n\n return new Promise<void>((resolve, reject) => {\n const request = indexedDB.open(name, 1);\n\n request.onupgradeneeded = () => {\n const db = request.result;\n if (!db.objectStoreNames.contains(STORE_NAME)) {\n db.createObjectStore(STORE_NAME, { keyPath: 'key' });\n }\n if (!db.objectStoreNames.contains(KEYS_STORE_NAME)) {\n db.createObjectStore(KEYS_STORE_NAME, { keyPath: 'key' });\n }\n };\n\n request.onsuccess = () => {\n this.db = request.result;\n resolve();\n };\n\n request.onerror = () => {\n reject(new Error('IndexedDB open failed: ' + request.error?.message));\n };\n });\n }\n\n async openAndLoad(identityId: string): Promise<IdentityContextData | null> {\n await this.open(identityId);\n const key = this.cacheKey ?? await this.loadPersistedKey();\n if (!key) {\n return null;\n }\n this.cacheKey = key;\n return this.load();\n }\n\n async save(data: IdentityContextData): Promise<void> {\n const key = this.cacheKey;\n if (!key) {\n throw new Error('No cache key set — call setCacheKey before save');\n }\n const envelope = await encryptContext(key, data);\n const db = await this.getDb();\n return new Promise<void>((resolve, reject) => {\n const tx = db.transaction(STORE_NAME, 'readwrite');\n const store = tx.objectStore(STORE_NAME);\n store.put({ key: CACHE_KEY, envelope });\n\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(new Error('Save failed: ' + tx.error?.message));\n });\n }\n\n async load(): Promise<IdentityContextData | null> {\n const db = await this.getDb();\n const key = this.cacheKey;\n if (!key) {\n return null;\n }\n return new Promise<IdentityContextData | null>((resolve, reject) => {\n const tx = db.transaction(STORE_NAME, 'readonly');\n const store = tx.objectStore(STORE_NAME);\n const request = store.get(CACHE_KEY);\n\n request.onsuccess = () => {\n const record = request.result;\n if (!record || !record.envelope) {\n resolve(null);\n return;\n }\n decryptContext(key, record.envelope)\n .then((data) => {\n if (data.identityId !== this.currentIdentityId) {\n resolve(null);\n return;\n }\n resolve(data);\n })\n .catch(() => {\n resolve(null);\n });\n };\n\n request.onerror = () => reject(new Error('Load failed: ' + request.error?.message));\n });\n }\n\n async clear(): Promise<void> {\n const db = await this.getDb();\n return new Promise<void>((resolve, reject) => {\n const tx = db.transaction(STORE_NAME, 'readwrite');\n const store = tx.objectStore(STORE_NAME);\n store.delete(CACHE_KEY);\n\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(new Error('Clear failed: ' + tx.error?.message));\n });\n }\n\n async close(): Promise<void> {\n const db = this.db;\n this.db = null;\n this.currentIdentityId = null;\n this.cacheKey = null;\n if (db) {\n db.close();\n }\n }\n\n setCacheKey(key: CryptoKey): void {\n this.cacheKey = key;\n }\n\n getCacheKey(): CryptoKey | null {\n return this.cacheKey;\n }\n\n async persistCacheKey(): Promise<void> {\n const key = this.cacheKey;\n if (!key) {\n throw new Error('No cache key to persist — call setCacheKey first');\n }\n const db = await this.getDb();\n return new Promise<void>((resolve, reject) => {\n const tx = db.transaction(KEYS_STORE_NAME, 'readwrite');\n const store = tx.objectStore(KEYS_STORE_NAME);\n store.put({ key: 'cache', cryptoKey: key });\n\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(new Error('persistCacheKey failed: ' + tx.error?.message));\n });\n }\n\n async loadPersistedKey(): Promise<CryptoKey | null> {\n const db = this.db;\n if (!db) {\n return null;\n }\n return new Promise<CryptoKey | null>((resolve) => {\n const tx = db.transaction(KEYS_STORE_NAME, 'readonly');\n const store = tx.objectStore(KEYS_STORE_NAME);\n const request = store.get('cache');\n\n request.onsuccess = () => {\n const record = request.result;\n if (!record || !record.cryptoKey) {\n resolve(null);\n return;\n }\n resolve(record.cryptoKey);\n };\n\n request.onerror = () => {\n resolve(null);\n };\n });\n }\n}\n"],"mappings":"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","names":["useCallback","useCallback","useReducer","useCallback","get64ByteSeedFromMnemonic","useReducer","useCallback","get64ByteSeedFromMnemonic","useState","useState","useState","useEffect","useState","useEffect","useReducer","useState","jsx","useReducer","useState","useReducer","useCallback","useState","useEffect","jsx","jsxs","useReducer","useState","useCallback","useEffect","useReducer","useEffect","useRef","validateSeedPhrase","wordlist","jsx","jsxs","useReducer","useRef","useEffect","useCallback","jsx","jsxs","useState","useCallback","useEffect","useRef","useState","useRef","useEffect","useCallback","useState","useEffect","useCallback","useRef"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@me2em/react",
3
- "version": "0.1.0-alpha.2",
4
- "description": "React bindings for the Me2em protocol: identity lifecycle, seed phrase UX, hierarchical handles",
3
+ "version": "1.0.0-rc.1",
4
+ "description": "React bindings for the Me2eM protocol: identity lifecycle, seed phrase UX, hierarchical handles",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -18,11 +18,14 @@
18
18
  "LICENSE"
19
19
  ],
20
20
  "license": "Apache-2.0",
21
+ "engines": {
22
+ "node": ">=22.6.0"
23
+ },
21
24
  "publishConfig": {
22
25
  "access": "public"
23
26
  },
24
27
  "dependencies": {
25
- "@me2em/core": "^0.7.0-alpha.2"
28
+ "@me2em/core": "^1.0.0-rc.2"
26
29
  },
27
30
  "peerDependencies": {
28
31
  "react": ">=18.0.0"
@@ -34,19 +37,21 @@
34
37
  "@types/react": "^18.3.0",
35
38
  "@types/react-dom": "^18.3.0",
36
39
  "@vitejs/plugin-react": "^4.3.0",
40
+ "@vitest/coverage-v8": "^4.1.11",
41
+ "fake-indexeddb": "^6.0.0",
37
42
  "jsdom": "^25.0.0",
38
43
  "react": "^18.3.0",
39
44
  "react-dom": "^18.3.0",
40
45
  "tsup": "^8.0.0",
41
46
  "typescript": "^5.6.0",
42
- "fake-indexeddb": "^6.0.0",
43
47
  "vitest": "^4.1.7"
44
48
  },
45
49
  "scripts": {
46
50
  "build": "tsup",
47
51
  "test": "vitest run",
48
- "lint": "eslint src",
52
+ "lint": "eslint .",
49
53
  "typecheck": "tsc --noEmit",
50
- "build-docs": "typedoc"
54
+ "build-docs": "typedoc",
55
+ "test:coverage": "vitest run --coverage"
51
56
  }
52
57
  }
@@ -1,31 +1,12 @@
1
1
  // packages/react/src/headless/context-crypto.ts
2
+ import { base64urlEncode, base64urlDecode } from '@me2em/core';
2
3
  import type { IdentityContextData } from './identity-context-types.js';
3
4
 
4
- function toB64(bytes: Uint8Array): string {
5
- let binary = '';
6
- for (const byte of bytes) {
7
- binary += String.fromCharCode(byte);
8
- }
9
- return btoa(binary)
10
- .replace(/\+/g, '-')
11
- .replace(/\//g, '_')
12
- .replace(/=+$/, '');
13
- }
14
-
15
- function fromB64(s: string): Uint8Array {
16
- let base64 = s
17
- .replace(/-/g, '+')
18
- .replace(/_/g, '/');
19
- while (base64.length % 4 !== 0) {
20
- base64 += '=';
21
- }
22
- const binary = atob(base64);
23
- const bytes = new Uint8Array(binary.length);
24
- for (let i = 0; i < binary.length; i++) {
25
- bytes[i] = binary.charCodeAt(i);
26
- }
27
- return bytes;
28
- }
5
+ // Base64url encoding for ContextEnvelope fields goes through the audited
6
+ // @scure/base codec re-exported by @me2em/core (audit A3 / R4) — one
7
+ // base64 implementation across packages, no hand-rolled alphabet handling.
8
+ // Envelope fields are always unpadded: the only writer is encryptContext
9
+ // below, so base64urlDecode's strict no-padding rule never bites.
29
10
 
30
11
  export interface ContextEnvelope {
31
12
  v: 1;
@@ -75,8 +56,8 @@ export async function encryptContext(
75
56
  const ct = await crypto.subtle.encrypt(
76
57
  { name: 'AES-GCM', iv: iv as BufferSource }, key, pt
77
58
  );
78
- return { v: 1, salt: toB64(salt), iv: toB64(iv),
79
- ct: toB64(new Uint8Array(ct)) };
59
+ return { v: 1, salt: base64urlEncode(salt), iv: base64urlEncode(iv),
60
+ ct: base64urlEncode(new Uint8Array(ct)) };
80
61
  }
81
62
 
82
63
  export async function decryptContext(
@@ -84,8 +65,8 @@ export async function decryptContext(
84
65
  ): Promise<IdentityContextData> {
85
66
  if (envelope.v !== 1) throw new Error('Unsupported envelope version');
86
67
  const pt = await crypto.subtle.decrypt(
87
- { name: 'AES-GCM', iv: fromB64(envelope.iv) as BufferSource },
88
- key, fromB64(envelope.ct) as BufferSource
68
+ { name: 'AES-GCM', iv: base64urlDecode(envelope.iv) as BufferSource },
69
+ key, base64urlDecode(envelope.ct) as BufferSource
89
70
  );
90
71
  return JSON.parse(new TextDecoder().decode(pt)) as IdentityContextData;
91
72
  }
@@ -1,5 +1,5 @@
1
1
  // packages/react/src/headless/identity-context-storage.ts
2
- import { encryptContext, decryptContext, type ContextEnvelope } from './context-crypto.js';
2
+ import { encryptContext, decryptContext } from './context-crypto.js';
3
3
  import type { IdentityContextData, IdentityContextStorage } from './identity-context-types.js';
4
4
  export type { IdentityContextData };
5
5
 
@@ -183,7 +183,7 @@ export class IdentityContextIDBStorage implements IdentityContextStorage {
183
183
  if (!db) {
184
184
  return null;
185
185
  }
186
- return new Promise<CryptoKey | null>((resolve, reject) => {
186
+ return new Promise<CryptoKey | null>((resolve) => {
187
187
  const tx = db.transaction(KEYS_STORE_NAME, 'readonly');
188
188
  const store = tx.objectStore(KEYS_STORE_NAME);
189
189
  const request = store.get('cache');
@@ -1,5 +1,5 @@
1
1
  // packages/react/src/headless/import-flow.ts
2
- import { validateSeedPhrase, type SeedStrength } from '@me2em/core';
2
+ import { validateSeedPhrase } from '@me2em/core';
3
3
 
4
4
  export type ImportStatus = 'entering' | 'valid' | 'invalid';
5
5
 
@@ -1,5 +1,5 @@
1
1
  // packages/react/src/react/PassphraseInput.tsx
2
- import { useState, useCallback } from 'react';
2
+ import { useCallback } from 'react';
3
3
 
4
4
  export interface PassphraseInputProps {
5
5
  onPassphraseChange: (p: string) => void;
@@ -6,18 +6,37 @@ import {
6
6
  } from '../headless/display-flow.js';
7
7
 
8
8
  export interface SeedPhraseDisplayProps {
9
+ /** Seed material is reachable via React DevTools in this window; by design. */
9
10
  words: string[];
10
11
  onDismiss?: () => void;
11
12
  onCopyAllowed?: () => void;
12
13
  }
13
14
 
15
+ /**
16
+ * R8: the permanent/transient line for a failed clipboard write is
17
+ * "can a retry in this window ever succeed?". Without the async
18
+ * Clipboard API (non-secure context, unsupported browser) it cannot —
19
+ * the failure is permanent, the copy-all button is disabled and the
20
+ * user is pointed at manual word selection. With the API present any
21
+ * rejection (lost focus, dismissed permission prompt, OS hiccup)
22
+ * stays transient: the button remains enabled and a fresh attempt
23
+ * may go through.
24
+ */
25
+ function clipboardWriteAvailable(): boolean {
26
+ return (
27
+ typeof navigator !== 'undefined' &&
28
+ !!navigator.clipboard &&
29
+ typeof navigator.clipboard.writeText === 'function'
30
+ );
31
+ }
32
+
14
33
  export function SeedPhraseDisplay({
15
34
  words,
16
35
  onDismiss,
17
36
  onCopyAllowed,
18
37
  }: SeedPhraseDisplayProps) {
19
38
  const [state, dispatch] = useReducer(displayFlowReducer, initialDisplayState);
20
- const [copyFailed, setCopyFailed] = useState(false);
39
+ const [copyError, setCopyError] = useState<'transient' | 'permanent' | null>(null);
21
40
 
22
41
  const { status, copyCount } = state;
23
42
 
@@ -28,12 +47,12 @@ export function SeedPhraseDisplay({
28
47
  const handleCopy = async () => {
29
48
  if (status === 'revealed' && copyCount === 0) {
30
49
  try {
31
- await (navigator as any).clipboard.writeText(words.join(' '));
50
+ await navigator.clipboard.writeText(words.join(' '));
32
51
  dispatch({ type: 'COPY', at: Date.now() });
33
- setCopyFailed(false);
52
+ setCopyError(null);
34
53
  onCopyAllowed?.();
35
54
  } catch {
36
- setCopyFailed(true);
55
+ setCopyError(clipboardWriteAvailable() ? 'transient' : 'permanent');
37
56
  }
38
57
  }
39
58
  };
@@ -72,15 +91,17 @@ export function SeedPhraseDisplay({
72
91
  </li>
73
92
  ))}
74
93
  </ol>
75
- {copyFailed && (
76
- <div data-testid="copy-error">
77
- Copy failed — try again
94
+ {copyError && (
95
+ <div data-testid="copy-error" data-kind={copyError}>
96
+ {copyError === 'permanent'
97
+ ? 'Copy unavailable in this context — select the words manually'
98
+ : 'Copy failed — try again'}
78
99
  </div>
79
100
  )}
80
101
  <button
81
102
  data-testid="copy-all"
82
103
  onClick={handleCopy}
83
- disabled={copyCount === 1}
104
+ disabled={copyCount === 1 || copyError === 'permanent'}
84
105
  >
85
106
  {copyCount === 1 ? 'Copied' : 'Copy all'}
86
107
  </button>
@@ -1,5 +1,5 @@
1
1
  // packages/react/src/react/SeedPhraseImport.tsx
2
- import { useReducer, useCallback, useEffect, useRef } from 'react';
2
+ import { useReducer, useEffect, useRef } from 'react';
3
3
  import {
4
4
  importFlowReducer,
5
5
  initImportState,
@@ -3,7 +3,6 @@ import { useReducer, useCallback, useState, useEffect, useRef } from 'react';
3
3
  import {
4
4
  verifyFlowReducer,
5
5
  initVerifyState,
6
- type VerifyState,
7
6
  } from '../headless/verify-flow.js';
8
7
  import { wordlist } from '@me2em/core';
9
8
 
@@ -6,7 +6,7 @@ import {
6
6
  } from 'react';
7
7
  import {
8
8
  generateSeedPhrase,
9
- get32ByteSeedFromMnemonic,
9
+ get64ByteSeedFromMnemonic,
10
10
  type SeedStrength,
11
11
  type Identity,
12
12
  } from '@me2em/core';
@@ -37,7 +37,7 @@ export function useCreateIdentity(): UseCreateIdentityResult {
37
37
  const generate = useCallback(
38
38
  (opts?: { strength?: SeedStrength }) => {
39
39
  const words = generateSeedPhrase(opts?.strength ?? 128);
40
- get32ByteSeedFromMnemonic(words).then((derivedSeed) => {
40
+ get64ByteSeedFromMnemonic(words).then((derivedSeed) => {
41
41
  dispatch({ type: 'GENERATE', words, seed: derivedSeed });
42
42
  }).catch(() => {
43
43
  dispatch({ type: 'FAIL', message: 'Failed to generate seed' });
@@ -10,6 +10,7 @@ export function useHandle(name: string): Handle | null {
10
10
  useEffect(() => {
11
11
  let cancelled = false;
12
12
  if (!identity) {
13
+ // eslint-disable-next-line react-hooks/set-state-in-effect -- clearing the stale async result when identity is gone; set-during-render refactor is a tracked post-rc.2 follow-up
13
14
  setHandle(null);
14
15
  return;
15
16
  }
@@ -1,5 +1,6 @@
1
1
  // packages/react/src/react/useIdentityContext.ts
2
2
  import { useState, useCallback, useEffect, useRef } from 'react';
3
+ import type { Identity } from '@me2em/core';
3
4
  import type { IdentityContextStorage, IdentityContextData } from '../headless/identity-context-types.js';
4
5
  import { useMe2emContext } from './context.js';
5
6
 
@@ -9,6 +10,10 @@ function publicKeyToId(publicKey: Uint8Array): string {
9
10
  .join('');
10
11
  }
11
12
 
13
+ function identityToId(identity: Identity | null): string | null {
14
+ return identity ? publicKeyToId(identity.getPublicKey()) : null;
15
+ }
16
+
12
17
  export interface UseIdentityContextResult {
13
18
  context: IdentityContextData | null;
14
19
  isLoaded: boolean;
@@ -28,11 +33,23 @@ export function useIdentityContext(
28
33
  const [error, setError] = useState<string | null>(null);
29
34
 
30
35
  const prevIdentityIdRef = useRef<string | null>(null);
36
+ // Generation ticket for async state transitions. Load (identity
37
+ // change / `refresh()`), `save()` and `clear()` each take a ticket;
38
+ // a resolution may only commit state while its ticket is the newest
39
+ // one. Without this, an in-flight load started before a save could
40
+ // resolve after it and clobber the just-saved state with pre-save
41
+ // data (out-of-order async update — observed as a rare CI flake in
42
+ // the rerender spec; pinned there by a gated-storage test).
43
+ const opRef = useRef(0);
44
+ const identityRef = useRef<Identity | null>(null);
45
+ // eslint-disable-next-line react-hooks/refs -- render-time mirror of the latest identity: save()/clear() are stable callbacks (storage-only deps) and must verify at commit time, after their await, that the identity they started for is still the active one
46
+ identityRef.current = identity;
31
47
 
32
48
  useEffect(() => {
33
49
  let cancelled = false;
34
50
 
35
51
  if (!identity) {
52
+ // eslint-disable-next-line react-hooks/set-state-in-effect -- clearing the stale async state when identity is gone (generation-ticket effect from the load-race fix); set-during-render refactor is a tracked post-rc.2 follow-up
36
53
  setContext(null);
37
54
  setIsLoaded(true);
38
55
  setError(null);
@@ -49,9 +66,10 @@ export function useIdentityContext(
49
66
  prevIdentityIdRef.current = identityId;
50
67
 
51
68
  // Open storage for this identity and load cached context (atomic)
69
+ const op = ++opRef.current;
52
70
  storage.openAndLoad(identityId)
53
71
  .then((data) => {
54
- if (cancelled) return;
72
+ if (cancelled || op !== opRef.current) return;
55
73
  setContext(data);
56
74
  setIsLoaded(true);
57
75
  setError(null);
@@ -68,19 +86,37 @@ export function useIdentityContext(
68
86
  }, [identity, storage]);
69
87
 
70
88
  const save = useCallback(async (data: IdentityContextData): Promise<void> => {
89
+ const idAtStart = identityToId(identityRef.current);
90
+ opRef.current += 1; // invalidate any in-flight load/refresh
71
91
  await storage.save(data);
92
+ // If the identity switched while the write was in flight, state
93
+ // ownership has moved to the new identity's load effect — committing
94
+ // here would surface identity A's context under identity B. The
95
+ // generation ticket above only orders this op against in-flight
96
+ // loads; it does not know about identity switches.
97
+ if (identityToId(identityRef.current) !== idAtStart) return;
72
98
  setContext(data);
99
+ setError(null);
73
100
  }, [storage]);
74
101
 
75
102
  const clear = useCallback(async (): Promise<void> => {
103
+ const idAtStart = identityToId(identityRef.current);
104
+ opRef.current += 1; // invalidate any in-flight load/refresh
76
105
  await storage.clear();
106
+ // Same ownership rule as save(): a clear() resolving after an
107
+ // identity switch must not blank the new identity's freshly
108
+ // loaded context.
109
+ if (identityToId(identityRef.current) !== idAtStart) return;
77
110
  setContext(null);
111
+ setError(null);
78
112
  }, [storage]);
79
113
 
80
114
  const refresh = useCallback(async (): Promise<void> => {
81
115
  if (!identity) return;
82
116
  const identityId = publicKeyToId(identity.getPublicKey());
117
+ const op = ++opRef.current;
83
118
  const data = await storage.openAndLoad(identityId);
119
+ if (op !== opRef.current) return;
84
120
  setContext(data);
85
121
  }, [identity, storage]);
86
122
 
@@ -2,7 +2,7 @@
2
2
  import { useReducer, useCallback, type Reducer } from 'react';
3
3
  import {
4
4
  validateSeedPhrase,
5
- get32ByteSeedFromMnemonic,
5
+ get64ByteSeedFromMnemonic,
6
6
  type Identity,
7
7
  } from '@me2em/core';
8
8
  import {
@@ -34,7 +34,7 @@ export function useImportSeed(): UseImportSeedResult {
34
34
  return;
35
35
  }
36
36
  try {
37
- const seed = await get32ByteSeedFromMnemonic(words, passphrase ?? '');
37
+ const seed = await get64ByteSeedFromMnemonic(words, passphrase ?? '');
38
38
  dispatch({ type: 'IMPORT', seed });
39
39
  await activateIdentity(seed);
40
40
  } catch (err) {
@@ -1,8 +1,7 @@
1
1
  // packages/react/src/react/useSession.ts
2
2
  import { useState, useEffect, useCallback, useRef } from 'react';
3
3
  import type { Handle } from '@me2em/core';
4
- import { Session, type SessionOptions, type SessionPayload } from '@me2em/core';
5
- import { useMe2emContext } from './context.js';
4
+ import { Session } from '@me2em/core';
6
5
 
7
6
  /**
8
7
  * Options for useSession, memoized via deep comparison of
@@ -74,7 +73,6 @@ export function useSession(
74
73
  options: UseSessionOptions,
75
74
  autoRenew: boolean = true
76
75
  ): UseSessionResult {
77
- const { identity } = useMe2emContext();
78
76
  const [session, setSession] = useState<Session | null>(null);
79
77
  const [isLoaded, setIsLoaded] = useState(false);
80
78
  const [error, setError] = useState<string | null>(null);
@@ -82,8 +80,11 @@ export function useSession(
82
80
  const optionsRef = useRef(options);
83
81
  const mountedRef = useRef(true);
84
82
 
85
- // Update options ref without triggering re-renders
86
- optionsRef.current = options;
83
+ // Mirror the latest options into the ref after every commit —
84
+ // ref writes belong in effects, not during render (react-hooks/refs).
85
+ useEffect(() => {
86
+ optionsRef.current = options;
87
+ });
87
88
 
88
89
  const clearTimer = useCallback(() => {
89
90
  if (timerRef.current) {
@@ -128,6 +129,7 @@ export function useSession(
128
129
  const halfLife = Math.floor((s.expiresAt * 1000 - Date.now()) / 2);
129
130
  if (halfLife > 0) {
130
131
  timerRef.current = setTimeout(() => {
132
+ // eslint-disable-next-line react-hooks/immutability -- self-rescheduling renewal timer: the timer is cleared and re-created whenever renew itself changes (deps + clearTimer), so the captured binding is always the current one
131
133
  renew();
132
134
  }, Math.max(halfLife, 1000));
133
135
  }
@@ -12,6 +12,7 @@ export function useSubHandle(
12
12
  useEffect(() => {
13
13
  let cancelled = false;
14
14
  if (!identity) {
15
+ // eslint-disable-next-line react-hooks/set-state-in-effect -- clearing the stale async result when identity is gone; set-during-render refactor is a tracked post-rc.2 follow-up
15
16
  setSubHandle(null);
16
17
  return;
17
18
  }