@agora-sdk/secure-chat-core 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/cjs/context/secure-chat-context.d.ts +24 -18
  2. package/dist/cjs/context/secure-chat-context.js +42 -23
  3. package/dist/cjs/context/secure-chat-context.js.map +1 -1
  4. package/dist/cjs/contract/index.js +2 -2
  5. package/dist/cjs/contract/index.js.map +1 -1
  6. package/dist/cjs/hooks/useSecureConversations.d.ts +1 -1
  7. package/dist/cjs/hooks/useSecureConversations.js +9 -7
  8. package/dist/cjs/hooks/useSecureConversations.js.map +1 -1
  9. package/dist/cjs/hooks/useSecureDevice.d.ts +14 -13
  10. package/dist/cjs/hooks/useSecureDevice.js +58 -25
  11. package/dist/cjs/hooks/useSecureDevice.js.map +1 -1
  12. package/dist/cjs/hooks/useSecureMessages.d.ts +10 -11
  13. package/dist/cjs/hooks/useSecureMessages.js +89 -21
  14. package/dist/cjs/hooks/useSecureMessages.js.map +1 -1
  15. package/dist/cjs/index.d.ts +18 -14
  16. package/dist/cjs/index.js +23 -19
  17. package/dist/cjs/index.js.map +1 -1
  18. package/dist/cjs/package.json +1 -0
  19. package/dist/cjs/persistence/memory-store.d.ts +9 -0
  20. package/dist/cjs/persistence/memory-store.js +27 -0
  21. package/dist/cjs/persistence/memory-store.js.map +1 -0
  22. package/dist/cjs/persistence/repository.d.ts +36 -0
  23. package/dist/cjs/persistence/repository.js +72 -0
  24. package/dist/cjs/persistence/repository.js.map +1 -0
  25. package/dist/cjs/persistence/store.d.ts +11 -0
  26. package/dist/cjs/persistence/store.js +8 -0
  27. package/dist/cjs/persistence/store.js.map +1 -0
  28. package/dist/cjs/transport/rest.d.ts +1 -1
  29. package/dist/cjs/transport/socket.d.ts +1 -1
  30. package/dist/esm/context/secure-chat-context.d.ts +24 -18
  31. package/dist/esm/context/secure-chat-context.js +41 -22
  32. package/dist/esm/context/secure-chat-context.js.map +1 -1
  33. package/dist/esm/contract/index.js +2 -2
  34. package/dist/esm/contract/index.js.map +1 -1
  35. package/dist/esm/hooks/useSecureConversations.d.ts +1 -1
  36. package/dist/esm/hooks/useSecureConversations.js +6 -4
  37. package/dist/esm/hooks/useSecureConversations.js.map +1 -1
  38. package/dist/esm/hooks/useSecureDevice.d.ts +14 -13
  39. package/dist/esm/hooks/useSecureDevice.js +55 -22
  40. package/dist/esm/hooks/useSecureDevice.js.map +1 -1
  41. package/dist/esm/hooks/useSecureMessages.d.ts +10 -11
  42. package/dist/esm/hooks/useSecureMessages.js +86 -18
  43. package/dist/esm/hooks/useSecureMessages.js.map +1 -1
  44. package/dist/esm/index.d.ts +18 -14
  45. package/dist/esm/index.js +9 -7
  46. package/dist/esm/index.js.map +1 -1
  47. package/dist/esm/persistence/memory-store.d.ts +9 -0
  48. package/dist/esm/persistence/memory-store.js +23 -0
  49. package/dist/esm/persistence/memory-store.js.map +1 -0
  50. package/dist/esm/persistence/repository.d.ts +36 -0
  51. package/dist/esm/persistence/repository.js +68 -0
  52. package/dist/esm/persistence/repository.js.map +1 -0
  53. package/dist/esm/persistence/store.d.ts +11 -0
  54. package/dist/esm/persistence/store.js +7 -0
  55. package/dist/esm/persistence/store.js.map +1 -0
  56. package/dist/esm/transport/rest.d.ts +1 -1
  57. package/dist/esm/transport/socket.d.ts +1 -1
  58. package/package.json +3 -3
@@ -1,18 +1,26 @@
1
1
  import React from "react";
2
- import { SecureChatCrypto } from "@agora-sdk/secure-chat-crypto";
3
- import { SecureChatRestClient } from "../transport/rest";
4
- import { SecureChatSocketClient } from "../transport/socket";
2
+ import { SecureChatCrypto, GroupHandle } from "@agora-sdk/secure-chat-crypto";
3
+ import { SecureChatRestClient } from "../transport/rest.js";
4
+ import { SecureChatSocketClient } from "../transport/socket.js";
5
+ import { SecureChatStore } from "../persistence/store.js";
6
+ import { SecureChatRepository } from "../persistence/repository.js";
5
7
  /**
6
- * The value exposed by {@link useSecureChat}: the shared transport clients, the injected crypto,
7
- * and the active project id. The hooks build everything else on top of these.
8
+ * The value exposed by {@link useSecureChat}: shared transport clients, the injected crypto, the
9
+ * persistence repository, the group-handle resolver, and the active project id.
8
10
  */
9
11
  export interface SecureChatContextValue {
10
12
  /** REST client for the blind Delivery Service endpoints. */
11
13
  rest: SecureChatRestClient;
12
14
  /** Realtime client for the `/secure` socket.io namespace. */
13
15
  socket: SecureChatSocketClient;
14
- /** The injected MLS crypto implementation (mock, or platform ts-mls/native). */
16
+ /** The injected MLS crypto implementation. */
15
17
  crypto: SecureChatCrypto;
18
+ /** Typed persistence over the injected store. */
19
+ repo: SecureChatRepository;
20
+ /** Resolve a conversation's MLS group handle (cache → store → importGroupState), or `null`. */
21
+ resolveGroup: (conversationId: string) => Promise<GroupHandle | null>;
22
+ /** Cache + persist a conversation's group handle (after createGroup / processWelcome). */
23
+ rememberGroup: (conversationId: string, handle: GroupHandle) => Promise<void>;
16
24
  /** The Agora project id these clients are scoped to. */
17
25
  projectId: string;
18
26
  }
@@ -22,6 +30,8 @@ export interface SecureChatProviderProps {
22
30
  crypto: SecureChatCrypto;
23
31
  /** Agora project id (path-scoped on every endpoint). */
24
32
  projectId: string;
33
+ /** Persistence store. Defaults to a non-persistent in-memory store when omitted. */
34
+ store?: SecureChatStore;
25
35
  /** Current access token. Re-pass on refresh; read lazily per request. */
26
36
  accessToken?: string;
27
37
  /** Override token resolution (takes precedence over `accessToken`). */
@@ -33,28 +43,24 @@ export interface SecureChatProviderProps {
33
43
  children: React.ReactNode;
34
44
  }
35
45
  /**
36
- * Provides the secure-chat transport + crypto to the `useSecure*` hooks. Render it inside a
37
- * `ReplykeProvider`: by default it inherits the API base URL and socket origin from
38
- * `@agora-sdk/core`'s runtime, and disconnects the socket on unmount.
46
+ * Provides secure-chat transport, crypto, and persistence to the `useSecure*` hooks. Render inside a
47
+ * `ReplykeProvider`; disconnects the socket on unmount.
39
48
  *
40
- * @param props - {@link SecureChatProviderProps} — the injected crypto, project id, token, and
41
- * optional base/socket URL overrides.
49
+ * @param props - {@link SecureChatProviderProps}.
42
50
  * @returns A context provider wrapping `children`.
43
51
  *
44
52
  * @example
45
53
  * ```tsx
46
- * <ReplykeProvider projectId={projectId} baseUrl={baseUrl}>
47
- * <SecureChatProvider crypto={crypto} projectId={projectId} accessToken={token}>
48
- * <Chat />
49
- * </SecureChatProvider>
50
- * </ReplykeProvider>
54
+ * <SecureChatProvider crypto={crypto} projectId={projectId} store={createIndexedDBStore()} accessToken={token}>
55
+ * <Chat />
56
+ * </SecureChatProvider>
51
57
  * ```
52
58
  */
53
- export declare function SecureChatProvider({ crypto, projectId, accessToken, getAccessToken, baseUrl, socketUrl, children, }: SecureChatProviderProps): React.JSX.Element;
59
+ export declare function SecureChatProvider({ crypto, projectId, store, accessToken, getAccessToken, baseUrl, socketUrl, children, }: SecureChatProviderProps): React.JSX.Element;
54
60
  /**
55
61
  * Access the nearest {@link SecureChatContextValue}.
56
62
  *
57
- * @returns The shared rest/socket/crypto/projectId for this provider subtree.
63
+ * @returns The shared rest/socket/crypto/repo/resolveGroup/projectId for this provider subtree.
58
64
  * @throws {Error} When called outside a `<SecureChatProvider>`.
59
65
  */
60
66
  export declare function useSecureChat(): SecureChatContextValue;
@@ -3,61 +3,80 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SecureChatProvider = SecureChatProvider;
4
4
  exports.useSecureChat = useSecureChat;
5
5
  const jsx_runtime_1 = require("react/jsx-runtime");
6
- // SecureChatProvider — wires the transport + crypto for the secure-chat hooks.
6
+ // SecureChatProvider — wires transport + crypto + persistence for the secure-chat hooks.
7
7
  //
8
8
  // Sits INSIDE a ReplykeProvider: by default it resolves the API base URL and socket origin from
9
- // @agora-sdk/core's runtime singletons (getApiBaseUrl / getSocketUrl), so whatever `baseUrl` the
10
- // app set on ReplykeProvider is honored here too. Crypto is injected (a `SecureChatCrypto`), keeping
11
- // core platform- and library-agnostic the web/native packages supply the concrete MLS impl.
9
+ // @agora-sdk/core's runtime singletons (getApiBaseUrl / getSocketUrl). Crypto AND the persistence
10
+ // store are injected, keeping core platform- and library-agnostic. The provider builds a typed
11
+ // SecureChatRepository over the store plus a cached resolveGroup/rememberGroup so the hooks become
12
+ // self-sufficient (no need to thread a GroupHandle in by hand).
12
13
  const react_1 = require("react");
13
14
  const core_1 = require("@agora-sdk/core");
14
- const rest_1 = require("../transport/rest");
15
- const socket_1 = require("../transport/socket");
15
+ const rest_js_1 = require("../transport/rest.js");
16
+ const socket_js_1 = require("../transport/socket.js");
17
+ const memory_store_js_1 = require("../persistence/memory-store.js");
18
+ const repository_js_1 = require("../persistence/repository.js");
16
19
  const SecureChatContext = (0, react_1.createContext)(null);
17
20
  /**
18
- * Provides the secure-chat transport + crypto to the `useSecure*` hooks. Render it inside a
19
- * `ReplykeProvider`: by default it inherits the API base URL and socket origin from
20
- * `@agora-sdk/core`'s runtime, and disconnects the socket on unmount.
21
+ * Provides secure-chat transport, crypto, and persistence to the `useSecure*` hooks. Render inside a
22
+ * `ReplykeProvider`; disconnects the socket on unmount.
21
23
  *
22
- * @param props - {@link SecureChatProviderProps} — the injected crypto, project id, token, and
23
- * optional base/socket URL overrides.
24
+ * @param props - {@link SecureChatProviderProps}.
24
25
  * @returns A context provider wrapping `children`.
25
26
  *
26
27
  * @example
27
28
  * ```tsx
28
- * <ReplykeProvider projectId={projectId} baseUrl={baseUrl}>
29
- * <SecureChatProvider crypto={crypto} projectId={projectId} accessToken={token}>
30
- * <Chat />
31
- * </SecureChatProvider>
32
- * </ReplykeProvider>
29
+ * <SecureChatProvider crypto={crypto} projectId={projectId} store={createIndexedDBStore()} accessToken={token}>
30
+ * <Chat />
31
+ * </SecureChatProvider>
33
32
  * ```
34
33
  */
35
- function SecureChatProvider({ crypto, projectId, accessToken, getAccessToken, baseUrl, socketUrl, children, }) {
36
- // Keep the latest token in a ref so the per-request resolver always returns the current value
37
- // without rebuilding the transport clients on every token change.
34
+ function SecureChatProvider({ crypto, projectId, store, accessToken, getAccessToken, baseUrl, socketUrl, children, }) {
38
35
  const tokenRef = (0, react_1.useRef)(accessToken);
39
36
  tokenRef.current = accessToken;
40
37
  const resolveToken = (0, react_1.useMemo)(() => getAccessToken ?? (() => tokenRef.current), [getAccessToken]);
41
- const rest = (0, react_1.useMemo)(() => new rest_1.SecureChatRestClient({
38
+ const rest = (0, react_1.useMemo)(() => new rest_js_1.SecureChatRestClient({
42
39
  projectId,
43
40
  getAccessToken: resolveToken,
44
41
  getBaseUrl: () => baseUrl ?? (0, core_1.getApiBaseUrl)(),
45
42
  }), [projectId, resolveToken, baseUrl]);
46
- const socket = (0, react_1.useMemo)(() => new socket_1.SecureChatSocketClient({
43
+ const socket = (0, react_1.useMemo)(() => new socket_js_1.SecureChatSocketClient({
47
44
  projectId,
48
45
  getAccessToken: resolveToken,
49
46
  getSocketUrl: () => socketUrl ?? (0, core_1.getSocketUrl)(),
50
47
  }), [projectId, resolveToken, socketUrl]);
48
+ const resolvedStore = (0, react_1.useMemo)(() => store ?? new memory_store_js_1.MemoryStore(), [store]);
49
+ const repo = (0, react_1.useMemo)(() => new repository_js_1.SecureChatRepository(resolvedStore), [resolvedStore]);
50
+ // In-memory GroupHandle cache, keyed by conversationId. Survives re-renders via the ref.
51
+ // NOTE: not cleared on a `store` prop swap — a store change in practice means a new provider
52
+ // instance (fresh cache), not a live prop change on the same mounted provider.
53
+ const groupCache = (0, react_1.useRef)(new Map());
54
+ const resolveGroup = (0, react_1.useCallback)(async (conversationId) => {
55
+ const cached = groupCache.current.get(conversationId);
56
+ if (cached)
57
+ return cached;
58
+ const bytes = await repo.loadGroupState(conversationId);
59
+ if (!bytes)
60
+ return null;
61
+ const handle = await crypto.importGroupState(bytes);
62
+ groupCache.current.set(conversationId, handle);
63
+ return handle;
64
+ }, [repo, crypto]);
65
+ const rememberGroup = (0, react_1.useCallback)(async (conversationId, handle) => {
66
+ groupCache.current.set(conversationId, handle);
67
+ const bytes = await crypto.exportGroupState(handle);
68
+ await repo.saveGroupState(conversationId, bytes);
69
+ }, [repo, crypto]);
51
70
  (0, react_1.useEffect)(() => {
52
71
  return () => socket.disconnect();
53
72
  }, [socket]);
54
- const value = (0, react_1.useMemo)(() => ({ rest, socket, crypto, projectId }), [rest, socket, crypto, projectId]);
73
+ const value = (0, react_1.useMemo)(() => ({ rest, socket, crypto, repo, resolveGroup, rememberGroup, projectId }), [rest, socket, crypto, repo, resolveGroup, rememberGroup, projectId]);
55
74
  return (0, jsx_runtime_1.jsx)(SecureChatContext.Provider, { value: value, children: children });
56
75
  }
57
76
  /**
58
77
  * Access the nearest {@link SecureChatContextValue}.
59
78
  *
60
- * @returns The shared rest/socket/crypto/projectId for this provider subtree.
79
+ * @returns The shared rest/socket/crypto/repo/resolveGroup/projectId for this provider subtree.
61
80
  * @throws {Error} When called outside a `<SecureChatProvider>`.
62
81
  */
63
82
  function useSecureChat() {
@@ -1 +1 @@
1
- {"version":3,"file":"secure-chat-context.js","sourceRoot":"","sources":["../../../src/context/secure-chat-context.tsx"],"names":[],"mappings":";;AAkEA,gDAiDC;AAQD,sCAMC;;AAjID,+EAA+E;AAC/E,EAAE;AACF,gGAAgG;AAChG,iGAAiG;AACjG,qGAAqG;AACrG,8FAA8F;AAE9F,iCAAqF;AACrF,0CAA8D;AAG9D,4CAAyD;AACzD,gDAA6D;AAiB7D,MAAM,iBAAiB,GAAG,IAAA,qBAAa,EAAgC,IAAI,CAAC,CAAC;AAmB7E;;;;;;;;;;;;;;;;;GAiBG;AACH,SAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,SAAS,EACT,WAAW,EACX,cAAc,EACd,OAAO,EACP,SAAS,EACT,QAAQ,GACgB;IACxB,8FAA8F;IAC9F,kEAAkE;IAClE,MAAM,QAAQ,GAAG,IAAA,cAAM,EAAqB,WAAW,CAAC,CAAC;IACzD,QAAQ,CAAC,OAAO,GAAG,WAAW,CAAC;IAE/B,MAAM,YAAY,GAAG,IAAA,eAAO,EAC1B,GAAG,EAAE,CAAC,cAAc,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,EAChD,CAAC,cAAc,CAAC,CACjB,CAAC;IAEF,MAAM,IAAI,GAAG,IAAA,eAAO,EAClB,GAAG,EAAE,CACH,IAAI,2BAAoB,CAAC;QACvB,SAAS;QACT,cAAc,EAAE,YAAY;QAC5B,UAAU,EAAE,GAAG,EAAE,CAAC,OAAO,IAAI,IAAA,oBAAa,GAAE;KAC7C,CAAC,EACJ,CAAC,SAAS,EAAE,YAAY,EAAE,OAAO,CAAC,CACnC,CAAC;IAEF,MAAM,MAAM,GAAG,IAAA,eAAO,EACpB,GAAG,EAAE,CACH,IAAI,+BAAsB,CAAC;QACzB,SAAS;QACT,cAAc,EAAE,YAAY;QAC5B,YAAY,EAAE,GAAG,EAAE,CAAC,SAAS,IAAI,IAAA,mBAAY,GAAE;KAChD,CAAC,EACJ,CAAC,SAAS,EAAE,YAAY,EAAE,SAAS,CAAC,CACrC,CAAC;IAEF,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;IACnC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAEb,MAAM,KAAK,GAAG,IAAA,eAAO,EACnB,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,EAC3C,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,CAClC,CAAC;IAEF,OAAO,uBAAC,iBAAiB,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAAG,QAAQ,GAA8B,CAAC;AAC3F,CAAC;AAED;;;;;GAKG;AACH,SAAgB,aAAa;IAC3B,MAAM,GAAG,GAAG,IAAA,kBAAU,EAAC,iBAAiB,CAAC,CAAC;IAC1C,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;IAC/E,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
1
+ {"version":3,"file":"secure-chat-context.js","sourceRoot":"","sources":["../../../src/context/secure-chat-context.tsx"],"names":[],"mappings":"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"}
@@ -1,12 +1,12 @@
1
1
  "use strict";
2
- // Secure-chat wire contract — stand-in for @agora/contract (the Apache-2.0 server contract).
2
+ // Secure-chat wire contract — stand-in for @agora-server/contract (the Apache-2.0 server contract).
3
3
  //
4
4
  // Mirrors the response models + request bodies of agora-server's
5
5
  // `packages/contract/src/secure-chat.ts`. That package is the source of truth (zod + TS); the
6
6
  // client only needs the TypeScript shapes, so this copy is types-only.
7
7
  //
8
8
  // ⚠️ Keep this byte-faithful to the server contract. The arrow is SDK → contract: once
9
- // `@agora/contract` is published, DELETE this file and `import type { ... } from "@agora/contract"`.
9
+ // `@agora-server/contract` is published, DELETE this file and `import type { ... } from "@agora-server/contract"`.
10
10
  // Do NOT publish a separate `@agora-sdk/secure-chat-contract` (that would invert the dependency —
11
11
  // see STATUS.md). Do not let this copy drift in the meantime.
12
12
  //
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/contract/index.ts"],"names":[],"mappings":";AAAA,6FAA6F;AAC7F,EAAE;AACF,iEAAiE;AACjE,8FAA8F;AAC9F,uEAAuE;AACvE,EAAE;AACF,uFAAuF;AACvF,qGAAqG;AACrG,kGAAkG;AAClG,8DAA8D;AAC9D,EAAE;AACF,gGAAgG;AAChG,0FAA0F"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/contract/index.ts"],"names":[],"mappings":";AAAA,oGAAoG;AACpG,EAAE;AACF,iEAAiE;AACjE,8FAA8F;AAC9F,uEAAuE;AACvE,EAAE;AACF,uFAAuF;AACvF,mHAAmH;AACnH,kGAAkG;AAClG,8DAA8D;AAC9D,EAAE;AACF,gGAAgG;AAChG,0FAA0F"}
@@ -1,4 +1,4 @@
1
- import { SecureConversationModel } from "../contract";
1
+ import { SecureConversationModel } from "../contract/index.js";
2
2
  /** The state and actions returned by {@link useSecureConversations}. */
3
3
  export interface UseSecureConversationsValues {
4
4
  /** The loaded conversations, newest activity first. */
@@ -7,8 +7,8 @@
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
8
  exports.useSecureConversations = useSecureConversations;
9
9
  const react_1 = require("react");
10
- const base64_1 = require("../util/base64");
11
- const secure_chat_context_1 = require("../context/secure-chat-context");
10
+ const base64_js_1 = require("../util/base64.js");
11
+ const secure_chat_context_js_1 = require("../context/secure-chat-context.js");
12
12
  /**
13
13
  * List the caller's secure conversations and start new direct messages.
14
14
  *
@@ -26,7 +26,7 @@ const secure_chat_context_1 = require("../context/secure-chat-context");
26
26
  * ```
27
27
  */
28
28
  function useSecureConversations() {
29
- const { rest, crypto, socket } = (0, secure_chat_context_1.useSecureChat)();
29
+ const { rest, crypto, socket, rememberGroup } = (0, secure_chat_context_js_1.useSecureChat)();
30
30
  const [conversations, setConversations] = (0, react_1.useState)([]);
31
31
  const [cursor, setCursor] = (0, react_1.useState)(undefined);
32
32
  const [hasMore, setHasMore] = (0, react_1.useState)(true);
@@ -69,24 +69,26 @@ function useSecureConversations() {
69
69
  }
70
70
  const initialMembers = await Promise.all(peerDevices.map(async (d) => {
71
71
  const claim = await rest.claimKeyPackage(d.id);
72
- return { deviceId: d.id, keyPackage: (0, base64_1.fromBase64)(claim.keyPackage) };
72
+ return { deviceId: d.id, keyPackage: (0, base64_js_1.fromBase64)(claim.keyPackage) };
73
73
  }));
74
74
  // 2. Create the MLS group locally; the crypto layer holds the secrets.
75
75
  const { group, welcomes } = await crypto.createGroup({ initialMembers });
76
76
  // 3. Register the conversation on the blind DS, relaying the targeted Welcomes.
77
77
  const conversation = await rest.createConversation({
78
78
  type: "dm",
79
- mlsGroupId: (0, base64_1.toBase64)(group.mlsGroupId),
79
+ mlsGroupId: (0, base64_js_1.toBase64)(group.mlsGroupId),
80
80
  memberUserIds: [peerUserId],
81
81
  welcomes: welcomes.map((w) => ({
82
82
  targetDeviceId: w.targetDeviceId,
83
- payload: (0, base64_1.toBase64)(w.payload),
83
+ payload: (0, base64_js_1.toBase64)(w.payload),
84
84
  epoch: group.epoch.toString(),
85
85
  })),
86
86
  });
87
+ // Persist + cache the creator's MLS group handle so messages resolve after a reload.
88
+ await rememberGroup(conversation.id, group);
87
89
  setConversations((prev) => [conversation, ...prev.filter((c) => c.id !== conversation.id)]);
88
90
  return conversation;
89
- }, [rest, crypto]);
91
+ }, [rest, crypto, rememberGroup]);
90
92
  (0, react_1.useEffect)(() => {
91
93
  refresh();
92
94
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -1 +1 @@
1
- {"version":3,"file":"useSecureConversations.js","sourceRoot":"","sources":["../../../src/hooks/useSecureConversations.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useSecureConversations.js","sourceRoot":"","sources":["../../../src/hooks/useSecureConversations.tsx"],"names":[],"mappings":"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"}
@@ -1,7 +1,8 @@
1
- import { SecureDeviceModel } from "../contract";
1
+ import { SecureDeviceModel } from "../contract/index.js";
2
2
  /** Options for {@link useSecureDevice}. */
3
3
  export interface UseSecureDeviceOptions {
4
- /** Stable, persisted client device id. Generated if omitted (persist it in the platform layer). */
4
+ /** Stable, persisted client device id. Generated (and persisted) if omitted. A previously
5
+ * persisted device's id takes precedence over this value when one exists on mount. */
5
6
  deviceId?: string;
6
7
  /** MLS ciphersuite to register under. Defaults to the crypto implementation's preferred suite. */
7
8
  ciphersuite?: number;
@@ -14,13 +15,15 @@ export interface UseSecureDeviceOptions {
14
15
  export interface UseSecureDeviceValues {
15
16
  /** The registered device row (its `.id` is the uuid used as targetDeviceId everywhere). */
16
17
  device: SecureDeviceModel | null;
18
+ /** True until the initial persisted-device load settles. */
19
+ loading: boolean;
17
20
  /** True while {@link UseSecureDeviceValues.register} is in flight. */
18
21
  registering: boolean;
19
- /** The last error thrown by registration or replenishment, or `null`. */
22
+ /** The last error thrown by load, registration, or replenishment, or `null`. */
20
23
  error: unknown;
21
24
  /** Last known count of unconsumed KeyPackages, or `null` until refreshed. */
22
25
  keyPackagesAvailable: number | null;
23
- /** Generate identity + register (idempotent server-side on (userId, deviceId)). */
26
+ /** Generate identity + register (idempotent server-side on (userId, deviceId)) and persist it. */
24
27
  register: () => Promise<SecureDeviceModel>;
25
28
  /** Generate + publish `count` fresh KeyPackages (default = keyPackageTarget). */
26
29
  publishKeyPackages: (count?: number) => Promise<number>;
@@ -28,20 +31,18 @@ export interface UseSecureDeviceValues {
28
31
  refreshKeyPackageCount: () => Promise<number>;
29
32
  }
30
33
  /**
31
- * Register this client as an MLS device (one device = one leaf) and keep its KeyPackages stocked.
34
+ * Register this client as an MLS device, persist its identity, and keep KeyPackages stocked.
32
35
  *
33
- * On {@link UseSecureDeviceValues.register} it generates a device identity, POSTs it to `/devices`,
34
- * and (when `autoReplenish` is on) republishes KeyPackages whenever the server emits
35
- * `secure:key-packages-low` for this device. The device's private key material must be persisted by
36
- * the platform layer — this hook only handles registration and relay.
36
+ * On mount it loads any persisted device and re-hydrates the crypto identity (stable id, no
37
+ * re-register). When none exists, await {@link UseSecureDeviceValues.register}.
37
38
  *
38
- * @param options - {@link UseSecureDeviceOptions} — device id, ciphersuite, and replenishment tuning.
39
- * @returns {@link UseSecureDeviceValues} — the device row, status flags, and register/publish actions.
39
+ * @param options - {@link UseSecureDeviceOptions}.
40
+ * @returns {@link UseSecureDeviceValues}.
40
41
  *
41
42
  * @example
42
43
  * ```tsx
43
- * const { device, register } = useSecureDevice({ keyPackageTarget: 20 });
44
- * useEffect(() => { register(); }, []);
44
+ * const { device, loading, register } = useSecureDevice();
45
+ * useEffect(() => { if (!loading && !device) register(); }, [loading, device, register]);
45
46
  * ```
46
47
  */
47
48
  export declare function useSecureDevice(options?: UseSecureDeviceOptions): UseSecureDeviceValues;
@@ -1,20 +1,18 @@
1
1
  "use strict";
2
- // useSecureDevice — register this client as an MLS device (leaf) and keep its KeyPackages topped up.
2
+ // useSecureDevice — register this client as an MLS device (leaf), persist its identity, and keep its
3
+ // KeyPackages topped up.
3
4
  //
4
- // Flow (server spec §14): generateDeviceIdentity POST /devices publish a batch of KeyPackages;
5
- // replenish on the `secure:key-packages-low` realtime signal or via the count endpoint.
6
- //
7
- // NOTE: the device's PRIVATE state (`privateState` from generateDeviceIdentity, and MLS group
8
- // state) must be persisted by the platform layer (IndexedDB on web, keystore on native — Phase 2/3).
9
- // Core only performs registration + relay; it does not persist secrets.
5
+ // On mount it re-hydrates a persisted device (stable deviceId + private state via
6
+ // crypto.importDeviceState) so a reload does NOT mint a new identity. register() generates, registers
7
+ // on the server, and persists. Replenishes on the `secure:key-packages-low` realtime signal.
10
8
  Object.defineProperty(exports, "__esModule", { value: true });
11
9
  exports.useSecureDevice = useSecureDevice;
12
10
  const react_1 = require("react");
13
- const base64_1 = require("../util/base64");
14
- const secure_chat_context_1 = require("../context/secure-chat-context");
11
+ const base64_js_1 = require("../util/base64.js");
12
+ const secure_chat_context_js_1 = require("../context/secure-chat-context.js");
15
13
  /**
16
14
  * Mint a device id when the caller doesn't supply one — `crypto.randomUUID()` when available, else a
17
- * non-cryptographic timestamp+random fallback. The platform layer should supply a persisted id.
15
+ * non-cryptographic timestamp+random fallback.
18
16
  *
19
17
  * @returns A fresh device id string.
20
18
  */
@@ -22,34 +20,64 @@ function newDeviceId() {
22
20
  const g = globalThis.crypto;
23
21
  if (g?.randomUUID)
24
22
  return g.randomUUID();
25
- // Platform layer should supply a persisted, stable device id; this is a non-crypto fallback.
26
23
  return `dev-${Date.now().toString(36)}-${Math.floor(Math.random() * 1e9).toString(36)}`;
27
24
  }
28
25
  /**
29
- * Register this client as an MLS device (one device = one leaf) and keep its KeyPackages stocked.
26
+ * Register this client as an MLS device, persist its identity, and keep KeyPackages stocked.
30
27
  *
31
- * On {@link UseSecureDeviceValues.register} it generates a device identity, POSTs it to `/devices`,
32
- * and (when `autoReplenish` is on) republishes KeyPackages whenever the server emits
33
- * `secure:key-packages-low` for this device. The device's private key material must be persisted by
34
- * the platform layer — this hook only handles registration and relay.
28
+ * On mount it loads any persisted device and re-hydrates the crypto identity (stable id, no
29
+ * re-register). When none exists, await {@link UseSecureDeviceValues.register}.
35
30
  *
36
- * @param options - {@link UseSecureDeviceOptions} — device id, ciphersuite, and replenishment tuning.
37
- * @returns {@link UseSecureDeviceValues} — the device row, status flags, and register/publish actions.
31
+ * @param options - {@link UseSecureDeviceOptions}.
32
+ * @returns {@link UseSecureDeviceValues}.
38
33
  *
39
34
  * @example
40
35
  * ```tsx
41
- * const { device, register } = useSecureDevice({ keyPackageTarget: 20 });
42
- * useEffect(() => { register(); }, []);
36
+ * const { device, loading, register } = useSecureDevice();
37
+ * useEffect(() => { if (!loading && !device) register(); }, [loading, device, register]);
43
38
  * ```
44
39
  */
45
40
  function useSecureDevice(options = {}) {
46
- const { crypto, rest, socket } = (0, secure_chat_context_1.useSecureChat)();
41
+ const { crypto, rest, socket, repo } = (0, secure_chat_context_js_1.useSecureChat)();
47
42
  const { ciphersuite, keyPackageTarget = 20, autoReplenish = true } = options;
48
43
  const [device, setDevice] = (0, react_1.useState)(null);
44
+ const [loading, setLoading] = (0, react_1.useState)(true);
49
45
  const [registering, setRegistering] = (0, react_1.useState)(false);
50
46
  const [error, setError] = (0, react_1.useState)(null);
51
47
  const [keyPackagesAvailable, setKeyPackagesAvailable] = (0, react_1.useState)(null);
52
48
  const deviceIdRef = (0, react_1.useRef)(options.deviceId ?? newDeviceId());
49
+ const registerStartedRef = (0, react_1.useRef)(false);
50
+ // On mount: re-hydrate a persisted device (stable id + private state). No persisted device ⇒
51
+ // first-run; the app calls register().
52
+ (0, react_1.useEffect)(() => {
53
+ let alive = true;
54
+ (async () => {
55
+ const persisted = await repo.loadDevice();
56
+ if (!alive || registerStartedRef.current) {
57
+ setLoading(false);
58
+ return;
59
+ }
60
+ if (persisted) {
61
+ await crypto.importDeviceState(persisted.deviceState);
62
+ // register() may have started during the await — don't overwrite its identity.
63
+ if (!alive || registerStartedRef.current) {
64
+ setLoading(false);
65
+ return;
66
+ }
67
+ deviceIdRef.current = persisted.deviceId;
68
+ setDevice(persisted.device);
69
+ }
70
+ setLoading(false);
71
+ })().catch((err) => {
72
+ if (!alive)
73
+ return;
74
+ setError(err);
75
+ setLoading(false);
76
+ });
77
+ return () => {
78
+ alive = false;
79
+ };
80
+ }, [repo, crypto]);
53
81
  const publishKeyPackages = (0, react_1.useCallback)(async (count = keyPackageTarget) => {
54
82
  if (!device)
55
83
  throw new Error("Register the device before publishing KeyPackages.");
@@ -57,7 +85,7 @@ function useSecureDevice(options = {}) {
57
85
  const published = await rest.publishKeyPackages(device.id, {
58
86
  keyPackages: bundles.map((b) => ({
59
87
  keyPackageRef: b.keyPackageRef,
60
- keyPackage: (0, base64_1.toBase64)(b.keyPackage),
88
+ keyPackage: (0, base64_js_1.toBase64)(b.keyPackage),
61
89
  ciphersuite: b.ciphersuite,
62
90
  expiresAt: b.expiresAt,
63
91
  })),
@@ -72,6 +100,7 @@ function useSecureDevice(options = {}) {
72
100
  return available;
73
101
  }, [rest, device]);
74
102
  const register = (0, react_1.useCallback)(async () => {
103
+ registerStartedRef.current = true;
75
104
  setRegistering(true);
76
105
  setError(null);
77
106
  try {
@@ -81,10 +110,13 @@ function useSecureDevice(options = {}) {
81
110
  });
82
111
  const registered = await rest.registerDevice({
83
112
  deviceId: identity.deviceId,
84
- signaturePublicKey: (0, base64_1.toBase64)(identity.signaturePublicKey),
85
- credential: (0, base64_1.toBase64)(identity.credential),
113
+ signaturePublicKey: (0, base64_js_1.toBase64)(identity.signaturePublicKey),
114
+ credential: (0, base64_js_1.toBase64)(identity.credential),
86
115
  ciphersuite: identity.ciphersuite,
87
116
  });
117
+ const deviceState = await crypto.exportDeviceState();
118
+ await repo.saveDevice({ deviceId: identity.deviceId, deviceState, device: registered });
119
+ deviceIdRef.current = identity.deviceId;
88
120
  setDevice(registered);
89
121
  return registered;
90
122
  }
@@ -95,7 +127,7 @@ function useSecureDevice(options = {}) {
95
127
  finally {
96
128
  setRegistering(false);
97
129
  }
98
- }, [crypto, rest, ciphersuite]);
130
+ }, [crypto, rest, repo, ciphersuite]);
99
131
  // Auto-replenish on the server's low-water signal for this device.
100
132
  (0, react_1.useEffect)(() => {
101
133
  if (!autoReplenish || !device)
@@ -109,6 +141,7 @@ function useSecureDevice(options = {}) {
109
141
  }, [autoReplenish, device, socket, publishKeyPackages]);
110
142
  return {
111
143
  device,
144
+ loading,
112
145
  registering,
113
146
  error,
114
147
  keyPackagesAvailable,
@@ -1 +1 @@
1
- {"version":3,"file":"useSecureDevice.js","sourceRoot":"","sources":["../../../src/hooks/useSecureDevice.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useSecureDevice.js","sourceRoot":"","sources":["../../../src/hooks/useSecureDevice.tsx"],"names":[],"mappings":";AAAA,qGAAqG;AACrG,yBAAyB;AACzB,EAAE;AACF,kFAAkF;AAClF,sGAAsG;AACtG,6FAA6F;;AAmE7F,0CAoHC;AArLD,iCAAiE;AAEjE,iDAA6C;AAC7C,8EAAkE;AAElE;;;;;GAKG;AACH,SAAS,WAAW;IAClB,MAAM,CAAC,GAAI,UAAyD,CAAC,MAAM,CAAC;IAC5E,IAAI,CAAC,EAAE,UAAU;QAAE,OAAO,CAAC,CAAC,UAAU,EAAE,CAAC;IACzC,OAAO,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;AAC1F,CAAC;AAmCD;;;;;;;;;;;;;;GAcG;AACH,SAAgB,eAAe,CAAC,UAAkC,EAAE;IAClE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,IAAA,sCAAa,GAAE,CAAC;IACvD,MAAM,EAAE,WAAW,EAAE,gBAAgB,GAAG,EAAE,EAAE,aAAa,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IAE7E,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,IAAA,gBAAQ,EAA2B,IAAI,CAAC,CAAC;IACrE,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,IAAA,gBAAQ,EAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,IAAA,gBAAQ,EAAC,KAAK,CAAC,CAAC;IACtD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,IAAA,gBAAQ,EAAU,IAAI,CAAC,CAAC;IAClD,MAAM,CAAC,oBAAoB,EAAE,uBAAuB,CAAC,GAAG,IAAA,gBAAQ,EAAgB,IAAI,CAAC,CAAC;IAEtF,MAAM,WAAW,GAAG,IAAA,cAAM,EAAS,OAAO,CAAC,QAAQ,IAAI,WAAW,EAAE,CAAC,CAAC;IACtE,MAAM,kBAAkB,GAAG,IAAA,cAAM,EAAC,KAAK,CAAC,CAAC;IAEzC,6FAA6F;IAC7F,uCAAuC;IACvC,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,IAAI,KAAK,GAAG,IAAI,CAAC;QACjB,CAAC,KAAK,IAAI,EAAE;YACV,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;YAC1C,IAAI,CAAC,KAAK,IAAI,kBAAkB,CAAC,OAAO,EAAE,CAAC;gBACzC,UAAU,CAAC,KAAK,CAAC,CAAC;gBAClB,OAAO;YACT,CAAC;YACD,IAAI,SAAS,EAAE,CAAC;gBACd,MAAM,MAAM,CAAC,iBAAiB,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;gBACtD,+EAA+E;gBAC/E,IAAI,CAAC,KAAK,IAAI,kBAAkB,CAAC,OAAO,EAAE,CAAC;oBACzC,UAAU,CAAC,KAAK,CAAC,CAAC;oBAClB,OAAO;gBACT,CAAC;gBACD,WAAW,CAAC,OAAO,GAAG,SAAS,CAAC,QAAQ,CAAC;gBACzC,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;YAC9B,CAAC;YACD,UAAU,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;YACjB,IAAI,CAAC,KAAK;gBAAE,OAAO;YACnB,QAAQ,CAAC,GAAG,CAAC,CAAC;YACd,UAAU,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC,CAAC,CAAC;QACH,OAAO,GAAG,EAAE;YACV,KAAK,GAAG,KAAK,CAAC;QAChB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IAEnB,MAAM,kBAAkB,GAAG,IAAA,mBAAW,EACpC,KAAK,EAAE,QAAgB,gBAAgB,EAAmB,EAAE;QAC1D,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;QACnF,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;QACxD,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,EAAE,EAAE;YACzD,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC/B,aAAa,EAAE,CAAC,CAAC,aAAa;gBAC9B,UAAU,EAAE,IAAA,oBAAQ,EAAC,CAAC,CAAC,UAAU,CAAC;gBAClC,WAAW,EAAE,CAAC,CAAC,WAAW;gBAC1B,SAAS,EAAE,CAAC,CAAC,SAAS;aACvB,CAAC,CAAC;SACJ,CAAC,CAAC;QACH,OAAO,SAAS,CAAC;IACnB,CAAC,EACD,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,gBAAgB,CAAC,CACzC,CAAC;IAEF,MAAM,sBAAsB,GAAG,IAAA,mBAAW,EAAC,KAAK,IAAqB,EAAE;QACrE,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;QACtF,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACxD,uBAAuB,CAAC,SAAS,CAAC,CAAC;QACnC,OAAO,SAAS,CAAC;IACnB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IAEnB,MAAM,QAAQ,GAAG,IAAA,mBAAW,EAAC,KAAK,IAAgC,EAAE;QAClE,kBAAkB,CAAC,OAAO,GAAG,IAAI,CAAC;QAClC,cAAc,CAAC,IAAI,CAAC,CAAC;QACrB,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,IAAI,CAAC;YACH,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,sBAAsB,CAAC;gBACvD,QAAQ,EAAE,WAAW,CAAC,OAAO;gBAC7B,WAAW;aACZ,CAAC,CAAC;YACH,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC;gBAC3C,QAAQ,EAAE,QAAQ,CAAC,QAAQ;gBAC3B,kBAAkB,EAAE,IAAA,oBAAQ,EAAC,QAAQ,CAAC,kBAAkB,CAAC;gBACzD,UAAU,EAAE,IAAA,oBAAQ,EAAC,QAAQ,CAAC,UAAU,CAAC;gBACzC,WAAW,EAAE,QAAQ,CAAC,WAAW;aAClC,CAAC,CAAC;YACH,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,iBAAiB,EAAE,CAAC;YACrD,MAAM,IAAI,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;YACxF,WAAW,CAAC,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC;YACxC,SAAS,CAAC,UAAU,CAAC,CAAC;YACtB,OAAO,UAAU,CAAC;QACpB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,CAAC,GAAG,CAAC,CAAC;YACd,MAAM,GAAG,CAAC;QACZ,CAAC;gBAAS,CAAC;YACT,cAAc,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC;IACH,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC;IAEtC,mEAAmE;IACnE,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,IAAI,CAAC,aAAa,IAAI,CAAC,MAAM;YAAE,OAAO;QACtC,MAAM,GAAG,GAAG,MAAM,CAAC,EAAE,CAAC,yBAAyB,EAAE,CAAC,MAAM,EAAE,EAAE;YAC1D,IAAI,MAAM,CAAC,QAAQ,KAAK,MAAM,CAAC,EAAE;gBAAE,OAAO;YAC1C,kBAAkB,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;QACH,OAAO,GAAG,CAAC;IACb,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC;IAExD,OAAO;QACL,MAAM;QACN,OAAO;QACP,WAAW;QACX,KAAK;QACL,oBAAoB;QACpB,QAAQ;QACR,kBAAkB;QAClB,sBAAsB;KACvB,CAAC;AACJ,CAAC"}
@@ -1,4 +1,4 @@
1
- import { SecureMessageModel } from "../contract";
1
+ import { SecureMessageModel } from "../contract/index.js";
2
2
  import { GroupHandle } from "@agora-sdk/secure-chat-crypto";
3
3
  /** A stored message paired with its decrypted text (when a group handle is available). */
4
4
  export interface DecryptedSecureMessage {
@@ -9,9 +9,9 @@ export interface DecryptedSecureMessage {
9
9
  }
10
10
  /** Options for {@link useSecureMessages}. */
11
11
  export interface UseSecureMessagesOptions {
12
- /** The MLS group handle for this conversation (from the platform persistence layer). */
12
+ /** Override the MLS group handle. Defaults to the persisted handle via `resolveGroup`. */
13
13
  group?: GroupHandle;
14
- /** The caller's device row id required to send (the server verifies it belongs to the caller). */
14
+ /** Override the sender device row id. Defaults to the persisted device's `.id`. */
15
15
  senderDeviceId?: string;
16
16
  }
17
17
  /** The state and actions returned by {@link useSecureMessages}. */
@@ -28,24 +28,23 @@ export interface UseSecureMessagesValues {
28
28
  loadMore: () => Promise<void>;
29
29
  /** Reload from the newest message, replacing the current list. */
30
30
  refresh: () => Promise<void>;
31
- /** Encrypt + send a text message. Requires `group` + `senderDeviceId`. */
31
+ /** Encrypt + send a text message. Requires a resolvable group + sender device. */
32
32
  sendMessage: (text: string) => Promise<void>;
33
33
  }
34
34
  /**
35
35
  * Load, decrypt, send, and live-receive messages in one secure conversation.
36
36
  *
37
- * Decryption runs through the injected `SecureChatCrypto` against the conversation's MLS
38
- * `GroupHandle`. Without a `group` in `options`, ciphertext is still listed and received (as
39
- * `plaintext: null`) and sending is disabled — letting the transport work ahead of the crypto
40
- * wiring. Joins the conversation's socket room to receive `secure:message` events live.
37
+ * Auto-resolves the MLS group handle (via `resolveGroup`) and the sender device id (from the
38
+ * persisted device) unless overridden in `options`. Joins the conversation socket room for live
39
+ * `secure:message` events.
41
40
  *
42
41
  * @param conversationId - The conversation to read and send within.
43
- * @param options - {@link UseSecureMessagesOptions} — the MLS `group` handle and `senderDeviceId`.
44
- * @returns {@link UseSecureMessagesValues} — the message list, paging state, and `sendMessage`.
42
+ * @param options - {@link UseSecureMessagesOptions}.
43
+ * @returns {@link UseSecureMessagesValues}.
45
44
  *
46
45
  * @example
47
46
  * ```tsx
48
- * const { messages, sendMessage } = useSecureMessages(conversationId, { group, senderDeviceId });
47
+ * const { messages, sendMessage } = useSecureMessages(conversationId);
49
48
  * await sendMessage("hello 💜");
50
49
  * ```
51
50
  */