@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,48 +1,95 @@
1
1
  "use strict";
2
2
  // useSecureMessages — load, decrypt, send, and live-receive messages in a secure conversation.
3
3
  //
4
- // Encryption/decryption runs through the injected `SecureChatCrypto` against the conversation's MLS
5
- // `GroupHandle`. Resolving conversationId → GroupHandle is owned by the platform persistence layer
6
- // (Phase 2: IndexedDB group state via processWelcome/importGroupState), so the caller passes the
7
- // handle in. Without it, ciphertext is still listed/received but left undecrypted (`plaintext: null`)
8
- // and sending is disabled — keeping the transport usable ahead of the crypto wiring.
4
+ // Self-sufficient once a store is wired: the MLS GroupHandle is auto-resolved from persistence via
5
+ // resolveGroup, and senderDeviceId is read from the persisted device. Both stay overridable through
6
+ // options for advanced use. Without a resolvable handle, ciphertext is still listed/received
7
+ // (plaintext: null) and sending is disabled.
9
8
  Object.defineProperty(exports, "__esModule", { value: true });
10
9
  exports.useSecureMessages = useSecureMessages;
11
10
  const react_1 = require("react");
12
- const base64_1 = require("../util/base64");
13
- 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");
14
13
  /**
15
14
  * Load, decrypt, send, and live-receive messages in one secure conversation.
16
15
  *
17
- * Decryption runs through the injected `SecureChatCrypto` against the conversation's MLS
18
- * `GroupHandle`. Without a `group` in `options`, ciphertext is still listed and received (as
19
- * `plaintext: null`) and sending is disabled — letting the transport work ahead of the crypto
20
- * wiring. Joins the conversation's socket room to receive `secure:message` events live.
16
+ * Auto-resolves the MLS group handle (via `resolveGroup`) and the sender device id (from the
17
+ * persisted device) unless overridden in `options`. Joins the conversation socket room for live
18
+ * `secure:message` events.
21
19
  *
22
20
  * @param conversationId - The conversation to read and send within.
23
- * @param options - {@link UseSecureMessagesOptions} — the MLS `group` handle and `senderDeviceId`.
24
- * @returns {@link UseSecureMessagesValues} — the message list, paging state, and `sendMessage`.
21
+ * @param options - {@link UseSecureMessagesOptions}.
22
+ * @returns {@link UseSecureMessagesValues}.
25
23
  *
26
24
  * @example
27
25
  * ```tsx
28
- * const { messages, sendMessage } = useSecureMessages(conversationId, { group, senderDeviceId });
26
+ * const { messages, sendMessage } = useSecureMessages(conversationId);
29
27
  * await sendMessage("hello 💜");
30
28
  * ```
31
29
  */
32
30
  function useSecureMessages(conversationId, options = {}) {
33
- const { rest, crypto, socket } = (0, secure_chat_context_1.useSecureChat)();
34
- const { group, senderDeviceId } = options;
31
+ const { rest, crypto, socket, repo, resolveGroup } = (0, secure_chat_context_js_1.useSecureChat)();
35
32
  const [messages, setMessages] = (0, react_1.useState)([]);
36
33
  const [before, setBefore] = (0, react_1.useState)(undefined);
37
34
  const [hasMore, setHasMore] = (0, react_1.useState)(true);
38
35
  const [loading, setLoading] = (0, react_1.useState)(false);
39
36
  const [error, setError] = (0, react_1.useState)(null);
37
+ const [group, setGroup] = (0, react_1.useState)(options.group ?? null);
38
+ const [senderDeviceId, setSenderDeviceId] = (0, react_1.useState)(options.senderDeviceId);
39
+ // Latest messages, read by the "decrypt history once the group resolves" effect below without
40
+ // making `messages` one of its deps (which would loop).
41
+ const messagesRef = (0, react_1.useRef)(messages);
42
+ messagesRef.current = messages;
43
+ // Resolve the group handle: explicit override, else persisted state.
44
+ (0, react_1.useEffect)(() => {
45
+ if (options.group) {
46
+ setGroup(options.group);
47
+ return;
48
+ }
49
+ // Clear any stale handle from the previous conversation before re-resolving, so live messages
50
+ // for the new conversation never decrypt against the old group during the async window.
51
+ setGroup(null);
52
+ let alive = true;
53
+ resolveGroup(conversationId)
54
+ .then((g) => {
55
+ if (alive)
56
+ setGroup(g);
57
+ })
58
+ .catch(() => {
59
+ if (alive)
60
+ setGroup(null);
61
+ });
62
+ return () => {
63
+ alive = false;
64
+ };
65
+ }, [options.group, conversationId, resolveGroup]);
66
+ // Resolve the sender device id: explicit override, else persisted device row.
67
+ (0, react_1.useEffect)(() => {
68
+ if (options.senderDeviceId) {
69
+ setSenderDeviceId(options.senderDeviceId);
70
+ return;
71
+ }
72
+ let alive = true;
73
+ repo
74
+ .loadDevice()
75
+ .then((d) => {
76
+ if (alive)
77
+ setSenderDeviceId(d?.device?.id ?? undefined);
78
+ })
79
+ .catch(() => {
80
+ if (alive)
81
+ setSenderDeviceId(undefined);
82
+ });
83
+ return () => {
84
+ alive = false;
85
+ };
86
+ }, [options.senderDeviceId, repo]);
40
87
  const decrypt = (0, react_1.useCallback)(async (model) => {
41
88
  if (!group)
42
89
  return { model, plaintext: null };
43
90
  try {
44
- const { plaintext } = await crypto.decryptMessage(group, (0, base64_1.fromBase64)(model.ciphertext));
45
- return { model, plaintext: (0, base64_1.bytesToUtf8)(plaintext) };
91
+ const { plaintext } = await crypto.decryptMessage(group, (0, base64_js_1.fromBase64)(model.ciphertext));
92
+ return { model, plaintext: (0, base64_js_1.bytesToUtf8)(plaintext) };
46
93
  }
47
94
  catch {
48
95
  // Buffer/skip: epoch not yet reached, or undecryptable. Surface ciphertext without text.
@@ -81,13 +128,16 @@ function useSecureMessages(conversationId, options = {}) {
81
128
  await load(false);
82
129
  }, [hasMore, loading, load]);
83
130
  const sendMessage = (0, react_1.useCallback)(async (text) => {
131
+ // Assumes the crypto identity is already hydrated (mount `useSecureDevice` under the same
132
+ // provider): the crypto layer tags the sender from the restored device identity, so after a
133
+ // reload the first send must wait for useSecureDevice's importDeviceState to complete.
84
134
  if (!group)
85
135
  throw new Error("Cannot send: no MLS group handle for this conversation.");
86
136
  if (!senderDeviceId)
87
137
  throw new Error("Cannot send: senderDeviceId is required.");
88
- const { ciphertext, epoch } = await crypto.encryptMessage(group, (0, base64_1.utf8ToBytes)(text));
138
+ const { ciphertext, epoch } = await crypto.encryptMessage(group, (0, base64_js_1.utf8ToBytes)(text));
89
139
  const sent = await rest.sendMessage(conversationId, {
90
- ciphertext: (0, base64_1.toBase64)(ciphertext),
140
+ ciphertext: (0, base64_js_1.toBase64)(ciphertext),
91
141
  epoch: epoch.toString(),
92
142
  senderDeviceId,
93
143
  });
@@ -98,13 +148,31 @@ function useSecureMessages(conversationId, options = {}) {
98
148
  refresh();
99
149
  // eslint-disable-next-line react-hooks/exhaustive-deps
100
150
  }, [conversationId]);
151
+ // Decrypt history that was listed before the group handle resolved. On reload, the first page loads
152
+ // while resolveGroup is still in flight, so those rows come back `plaintext: null`; once the handle
153
+ // arrives (decrypt is recreated with it), re-decrypt the still-undecrypted rows in place — no
154
+ // re-fetch, scroll/pagination preserved.
155
+ (0, react_1.useEffect)(() => {
156
+ if (!group)
157
+ return;
158
+ if (!messagesRef.current.some((m) => m.plaintext === null))
159
+ return;
160
+ let alive = true;
161
+ Promise.all(messagesRef.current.map((m) => (m.plaintext === null ? decrypt(m.model) : Promise.resolve(m)))).then((next) => {
162
+ if (alive)
163
+ setMessages(next);
164
+ });
165
+ return () => {
166
+ alive = false;
167
+ };
168
+ }, [group, decrypt]);
101
169
  // Live receive: join the conversation room and decrypt inbound ciphertext.
102
170
  (0, react_1.useEffect)(() => {
103
171
  socket.joinConversation(conversationId);
104
172
  const off = socket.on("secure:message", (model) => {
105
173
  if (model.conversationId !== conversationId)
106
174
  return;
107
- decrypt(model).then((m) => setMessages((prev) => prev.some((p) => p.model.id === m.model.id) ? prev : [m, ...prev]));
175
+ decrypt(model).then((m) => setMessages((prev) => (prev.some((p) => p.model.id === m.model.id) ? prev : [m, ...prev])));
108
176
  });
109
177
  return off;
110
178
  }, [socket, conversationId, decrypt]);
@@ -1 +1 @@
1
- {"version":3,"file":"useSecureMessages.js","sourceRoot":"","sources":["../../../src/hooks/useSecureMessages.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useSecureMessages.js","sourceRoot":"","sources":["../../../src/hooks/useSecureMessages.tsx"],"names":[],"mappings":";AAAA,+FAA+F;AAC/F,EAAE;AACF,mGAAmG;AACnG,oGAAoG;AACpG,6FAA6F;AAC7F,6CAA6C;;AA2D7C,8CAoKC;AA7ND,iCAAiE;AAGjE,iDAAmF;AACnF,8EAAkE;AAoClE;;;;;;;;;;;;;;;;GAgBG;AACH,SAAgB,iBAAiB,CAC/B,cAAsB,EACtB,UAAoC,EAAE;IAEtC,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,IAAA,sCAAa,GAAE,CAAC;IAErE,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,IAAA,gBAAQ,EAA2B,EAAE,CAAC,CAAC;IACvE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,IAAA,gBAAQ,EAAqB,SAAS,CAAC,CAAC;IACpE,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,IAAA,gBAAQ,EAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,IAAA,gBAAQ,EAAC,KAAK,CAAC,CAAC;IAC9C,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,IAAA,gBAAQ,EAAU,IAAI,CAAC,CAAC;IAClD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,IAAA,gBAAQ,EAAqB,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;IAC9E,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,GAAG,IAAA,gBAAQ,EAAqB,OAAO,CAAC,cAAc,CAAC,CAAC;IAEjG,8FAA8F;IAC9F,wDAAwD;IACxD,MAAM,WAAW,GAAG,IAAA,cAAM,EAA2B,QAAQ,CAAC,CAAC;IAC/D,WAAW,CAAC,OAAO,GAAG,QAAQ,CAAC;IAE/B,qEAAqE;IACrE,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAClB,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YACxB,OAAO;QACT,CAAC;QACD,8FAA8F;QAC9F,wFAAwF;QACxF,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,IAAI,KAAK,GAAG,IAAI,CAAC;QACjB,YAAY,CAAC,cAAc,CAAC;aACzB,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;YACV,IAAI,KAAK;gBAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;QACzB,CAAC,CAAC;aACD,KAAK,CAAC,GAAG,EAAE;YACV,IAAI,KAAK;gBAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QACL,OAAO,GAAG,EAAE;YACV,KAAK,GAAG,KAAK,CAAC;QAChB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,cAAc,EAAE,YAAY,CAAC,CAAC,CAAC;IAElD,8EAA8E;IAC9E,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;YAC3B,iBAAiB,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;YAC1C,OAAO;QACT,CAAC;QACD,IAAI,KAAK,GAAG,IAAI,CAAC;QACjB,IAAI;aACD,UAAU,EAAE;aACZ,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;YACV,IAAI,KAAK;gBAAE,iBAAiB,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,SAAS,CAAC,CAAC;QAC3D,CAAC,CAAC;aACD,KAAK,CAAC,GAAG,EAAE;YACV,IAAI,KAAK;gBAAE,iBAAiB,CAAC,SAAS,CAAC,CAAC;QAC1C,CAAC,CAAC,CAAC;QACL,OAAO,GAAG,EAAE;YACV,KAAK,GAAG,KAAK,CAAC;QAChB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC,CAAC;IAEnC,MAAM,OAAO,GAAG,IAAA,mBAAW,EACzB,KAAK,EAAE,KAAyB,EAAmC,EAAE;QACnE,IAAI,CAAC,KAAK;YAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;QAC9C,IAAI,CAAC;YACH,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,IAAA,sBAAU,EAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;YACvF,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,IAAA,uBAAW,EAAC,SAAS,CAAC,EAAE,CAAC;QACtD,CAAC;QAAC,MAAM,CAAC;YACP,yFAAyF;YACzF,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;QACpC,CAAC;IACH,CAAC,EACD,CAAC,MAAM,EAAE,KAAK,CAAC,CAChB,CAAC;IAEF,MAAM,IAAI,GAAG,IAAA,mBAAW,EACtB,KAAK,EAAE,KAAc,EAAE,EAAE;QACvB,UAAU,CAAC,IAAI,CAAC,CAAC;QACjB,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE;gBACnD,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM;gBAClC,KAAK,EAAE,EAAE;aACV,CAAC,CAAC;YACH,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;YAChE,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YACvD,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAC9C,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACzB,8DAA8D;YAC9D,WAAW,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;QACvE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,CAAC,GAAG,CAAC,CAAC;QAChB,CAAC;gBAAS,CAAC;YACT,UAAU,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC;IACH,CAAC,EACD,CAAC,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,CAAC,CACxC,CAAC;IAEF,MAAM,OAAO,GAAG,IAAA,mBAAW,EAAC,KAAK,IAAI,EAAE;QACrC,SAAS,CAAC,SAAS,CAAC,CAAC;QACrB,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;IACnB,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,MAAM,QAAQ,GAAG,IAAA,mBAAW,EAAC,KAAK,IAAI,EAAE;QACtC,IAAI,CAAC,OAAO,IAAI,OAAO;YAAE,OAAO;QAChC,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC;IACpB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;IAE7B,MAAM,WAAW,GAAG,IAAA,mBAAW,EAC7B,KAAK,EAAE,IAAY,EAAiB,EAAE;QACpC,0FAA0F;QAC1F,4FAA4F;QAC5F,uFAAuF;QACvF,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;QACvF,IAAI,CAAC,cAAc;YAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;QACjF,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,IAAA,uBAAW,EAAC,IAAI,CAAC,CAAC,CAAC;QACpF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,cAAc,EAAE;YAClD,UAAU,EAAE,IAAA,oBAAQ,EAAC,UAAU,CAAC;YAChC,KAAK,EAAE,KAAK,CAAC,QAAQ,EAAE;YACvB,cAAc;SACf,CAAC,CAAC;QACH,8EAA8E;QAC9E,WAAW,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;IACrE,CAAC,EACD,CAAC,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,cAAc,CAAC,CACtD,CAAC;IAEF,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,OAAO,EAAE,CAAC;QACV,uDAAuD;IACzD,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC;IAErB,oGAAoG;IACpG,oGAAoG;IACpG,8FAA8F;IAC9F,yCAAyC;IACzC,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,IAAI,CAAC;YAAE,OAAO;QACnE,IAAI,KAAK,GAAG,IAAI,CAAC;QACjB,OAAO,CAAC,GAAG,CACT,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAC/F,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;YACd,IAAI,KAAK;gBAAE,WAAW,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;QACH,OAAO,GAAG,EAAE;YACV,KAAK,GAAG,KAAK,CAAC;QAChB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;IAErB,2EAA2E;IAC3E,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,MAAM,CAAC,gBAAgB,CAAC,cAAc,CAAC,CAAC;QACxC,MAAM,GAAG,GAAG,MAAM,CAAC,EAAE,CAAC,gBAAgB,EAAE,CAAC,KAAK,EAAE,EAAE;YAChD,IAAI,KAAK,CAAC,cAAc,KAAK,cAAc;gBAAE,OAAO;YACpD,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CACxB,WAAW,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAC3F,CAAC;QACJ,CAAC,CAAC,CAAC;QACH,OAAO,GAAG,CAAC;IACb,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,CAAC,CAAC,CAAC;IAEtC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;AAC/E,CAAC"}
@@ -1,15 +1,19 @@
1
- export { SecureChatProvider, useSecureChat } from "./context/secure-chat-context";
2
- export type { SecureChatProviderProps, SecureChatContextValue, } from "./context/secure-chat-context";
3
- export { useSecureDevice } from "./hooks/useSecureDevice";
4
- export type { UseSecureDeviceOptions, UseSecureDeviceValues } from "./hooks/useSecureDevice";
5
- export { useSecureConversations } from "./hooks/useSecureConversations";
6
- export type { UseSecureConversationsValues } from "./hooks/useSecureConversations";
7
- export { useSecureMessages } from "./hooks/useSecureMessages";
8
- export type { UseSecureMessagesOptions, UseSecureMessagesValues, DecryptedSecureMessage, } from "./hooks/useSecureMessages";
9
- export { SecureChatRestClient } from "./transport/rest";
10
- export type { SecureChatRestConfig } from "./transport/rest";
11
- export { SecureChatSocketClient } from "./transport/socket";
12
- export type { SecureSocket, SecureServerEvents, SecureClientEvents, SecureChatSocketConfig, } from "./transport/socket";
1
+ export { SecureChatProvider, useSecureChat } from "./context/secure-chat-context.js";
2
+ export type { SecureChatProviderProps, SecureChatContextValue, } from "./context/secure-chat-context.js";
3
+ export { useSecureDevice } from "./hooks/useSecureDevice.js";
4
+ export type { UseSecureDeviceOptions, UseSecureDeviceValues } from "./hooks/useSecureDevice.js";
5
+ export { useSecureConversations } from "./hooks/useSecureConversations.js";
6
+ export type { UseSecureConversationsValues } from "./hooks/useSecureConversations.js";
7
+ export { useSecureMessages } from "./hooks/useSecureMessages.js";
8
+ export type { UseSecureMessagesOptions, UseSecureMessagesValues, DecryptedSecureMessage, } from "./hooks/useSecureMessages.js";
9
+ export { SecureChatRestClient } from "./transport/rest.js";
10
+ export type { SecureChatRestConfig } from "./transport/rest.js";
11
+ export { SecureChatSocketClient } from "./transport/socket.js";
12
+ export type { SecureSocket, SecureServerEvents, SecureClientEvents, SecureChatSocketConfig, } from "./transport/socket.js";
13
13
  export type { SecureChatCrypto, DeviceIdentity, KeyPackageBundle, GroupHandle, TargetedWelcome, CommitResult, PassphraseBackup, } from "@agora-sdk/secure-chat-crypto";
14
- export type * from "./contract";
15
- export { toBase64, fromBase64, utf8ToBytes, bytesToUtf8 } from "./util/base64";
14
+ export type * from "./contract/index.js";
15
+ export type { SecureChatStore } from "./persistence/store.js";
16
+ export { MemoryStore } from "./persistence/memory-store.js";
17
+ export { SecureChatRepository } from "./persistence/repository.js";
18
+ export type { PersistedDevice } from "./persistence/repository.js";
19
+ export { toBase64, fromBase64, utf8ToBytes, bytesToUtf8 } from "./util/base64.js";
package/dist/cjs/index.js CHANGED
@@ -5,27 +5,31 @@
5
5
  // injection of a `SecureChatCrypto`. Platform packages (@agora-sdk/secure-chat-react-js, etc.)
6
6
  // re-export this and add the concrete crypto + persistence.
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.bytesToUtf8 = exports.utf8ToBytes = exports.fromBase64 = exports.toBase64 = exports.SecureChatSocketClient = exports.SecureChatRestClient = exports.useSecureMessages = exports.useSecureConversations = exports.useSecureDevice = exports.useSecureChat = exports.SecureChatProvider = void 0;
8
+ exports.bytesToUtf8 = exports.utf8ToBytes = exports.fromBase64 = exports.toBase64 = exports.SecureChatRepository = exports.MemoryStore = exports.SecureChatSocketClient = exports.SecureChatRestClient = exports.useSecureMessages = exports.useSecureConversations = exports.useSecureDevice = exports.useSecureChat = exports.SecureChatProvider = void 0;
9
9
  // ── context / provider ──────────────────────────────────────────────────────
10
- var secure_chat_context_1 = require("./context/secure-chat-context");
11
- Object.defineProperty(exports, "SecureChatProvider", { enumerable: true, get: function () { return secure_chat_context_1.SecureChatProvider; } });
12
- Object.defineProperty(exports, "useSecureChat", { enumerable: true, get: function () { return secure_chat_context_1.useSecureChat; } });
10
+ var secure_chat_context_js_1 = require("./context/secure-chat-context.js");
11
+ Object.defineProperty(exports, "SecureChatProvider", { enumerable: true, get: function () { return secure_chat_context_js_1.SecureChatProvider; } });
12
+ Object.defineProperty(exports, "useSecureChat", { enumerable: true, get: function () { return secure_chat_context_js_1.useSecureChat; } });
13
13
  // ── hooks ────────────────────────────────────────────────────────────────────
14
- var useSecureDevice_1 = require("./hooks/useSecureDevice");
15
- Object.defineProperty(exports, "useSecureDevice", { enumerable: true, get: function () { return useSecureDevice_1.useSecureDevice; } });
16
- var useSecureConversations_1 = require("./hooks/useSecureConversations");
17
- Object.defineProperty(exports, "useSecureConversations", { enumerable: true, get: function () { return useSecureConversations_1.useSecureConversations; } });
18
- var useSecureMessages_1 = require("./hooks/useSecureMessages");
19
- Object.defineProperty(exports, "useSecureMessages", { enumerable: true, get: function () { return useSecureMessages_1.useSecureMessages; } });
14
+ var useSecureDevice_js_1 = require("./hooks/useSecureDevice.js");
15
+ Object.defineProperty(exports, "useSecureDevice", { enumerable: true, get: function () { return useSecureDevice_js_1.useSecureDevice; } });
16
+ var useSecureConversations_js_1 = require("./hooks/useSecureConversations.js");
17
+ Object.defineProperty(exports, "useSecureConversations", { enumerable: true, get: function () { return useSecureConversations_js_1.useSecureConversations; } });
18
+ var useSecureMessages_js_1 = require("./hooks/useSecureMessages.js");
19
+ Object.defineProperty(exports, "useSecureMessages", { enumerable: true, get: function () { return useSecureMessages_js_1.useSecureMessages; } });
20
20
  // ── transport (for advanced / non-React use) ─────────────────────────────────
21
- var rest_1 = require("./transport/rest");
22
- Object.defineProperty(exports, "SecureChatRestClient", { enumerable: true, get: function () { return rest_1.SecureChatRestClient; } });
23
- var socket_1 = require("./transport/socket");
24
- Object.defineProperty(exports, "SecureChatSocketClient", { enumerable: true, get: function () { return socket_1.SecureChatSocketClient; } });
21
+ var rest_js_1 = require("./transport/rest.js");
22
+ Object.defineProperty(exports, "SecureChatRestClient", { enumerable: true, get: function () { return rest_js_1.SecureChatRestClient; } });
23
+ var socket_js_1 = require("./transport/socket.js");
24
+ Object.defineProperty(exports, "SecureChatSocketClient", { enumerable: true, get: function () { return socket_js_1.SecureChatSocketClient; } });
25
+ var memory_store_js_1 = require("./persistence/memory-store.js");
26
+ Object.defineProperty(exports, "MemoryStore", { enumerable: true, get: function () { return memory_store_js_1.MemoryStore; } });
27
+ var repository_js_1 = require("./persistence/repository.js");
28
+ Object.defineProperty(exports, "SecureChatRepository", { enumerable: true, get: function () { return repository_js_1.SecureChatRepository; } });
25
29
  // ── utils ────────────────────────────────────────────────────────────────────
26
- var base64_1 = require("./util/base64");
27
- Object.defineProperty(exports, "toBase64", { enumerable: true, get: function () { return base64_1.toBase64; } });
28
- Object.defineProperty(exports, "fromBase64", { enumerable: true, get: function () { return base64_1.fromBase64; } });
29
- Object.defineProperty(exports, "utf8ToBytes", { enumerable: true, get: function () { return base64_1.utf8ToBytes; } });
30
- Object.defineProperty(exports, "bytesToUtf8", { enumerable: true, get: function () { return base64_1.bytesToUtf8; } });
30
+ var base64_js_1 = require("./util/base64.js");
31
+ Object.defineProperty(exports, "toBase64", { enumerable: true, get: function () { return base64_js_1.toBase64; } });
32
+ Object.defineProperty(exports, "fromBase64", { enumerable: true, get: function () { return base64_js_1.fromBase64; } });
33
+ Object.defineProperty(exports, "utf8ToBytes", { enumerable: true, get: function () { return base64_js_1.utf8ToBytes; } });
34
+ Object.defineProperty(exports, "bytesToUtf8", { enumerable: true, get: function () { return base64_js_1.bytesToUtf8; } });
31
35
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAAA,8FAA8F;AAC9F,EAAE;AACF,6FAA6F;AAC7F,+FAA+F;AAC/F,4DAA4D;;;AAE5D,+EAA+E;AAC/E,qEAAkF;AAAzE,yHAAA,kBAAkB,OAAA;AAAE,oHAAA,aAAa,OAAA;AAM1C,gFAAgF;AAChF,2DAA0D;AAAjD,kHAAA,eAAe,OAAA;AAExB,yEAAwE;AAA/D,gIAAA,sBAAsB,OAAA;AAE/B,+DAA8D;AAArD,sHAAA,iBAAiB,OAAA;AAO1B,gFAAgF;AAChF,yCAAwD;AAA/C,4GAAA,oBAAoB,OAAA;AAE7B,6CAA4D;AAAnD,gHAAA,sBAAsB,OAAA;AAsB/B,gFAAgF;AAChF,wCAA+E;AAAtE,kGAAA,QAAQ,OAAA;AAAE,oGAAA,UAAU,OAAA;AAAE,qGAAA,WAAW,OAAA;AAAE,qGAAA,WAAW,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAAA,8FAA8F;AAC9F,EAAE;AACF,6FAA6F;AAC7F,+FAA+F;AAC/F,4DAA4D;;;AAE5D,+EAA+E;AAC/E,2EAAqF;AAA5E,4HAAA,kBAAkB,OAAA;AAAE,uHAAA,aAAa,OAAA;AAM1C,gFAAgF;AAChF,iEAA6D;AAApD,qHAAA,eAAe,OAAA;AAExB,+EAA2E;AAAlE,mIAAA,sBAAsB,OAAA;AAE/B,qEAAiE;AAAxD,yHAAA,iBAAiB,OAAA;AAO1B,gFAAgF;AAChF,+CAA2D;AAAlD,+GAAA,oBAAoB,OAAA;AAE7B,mDAA+D;AAAtD,mHAAA,sBAAsB,OAAA;AAwB/B,iEAA4D;AAAnD,8GAAA,WAAW,OAAA;AACpB,6DAAmE;AAA1D,qHAAA,oBAAoB,OAAA;AAG7B,gFAAgF;AAChF,8CAAkF;AAAzE,qGAAA,QAAQ,OAAA;AAAE,uGAAA,UAAU,OAAA;AAAE,wGAAA,WAAW,OAAA;AAAE,wGAAA,WAAW,OAAA"}
@@ -0,0 +1 @@
1
+ {"type":"commonjs"}
@@ -0,0 +1,9 @@
1
+ import type { SecureChatStore } from "./store.js";
2
+ /** A `Map`-backed {@link SecureChatStore}. Non-persistent; the default when no store is injected. */
3
+ export declare class MemoryStore implements SecureChatStore {
4
+ private readonly map;
5
+ get(key: string): Promise<Uint8Array | null>;
6
+ set(key: string, value: Uint8Array): Promise<void>;
7
+ delete(key: string): Promise<void>;
8
+ list(prefix: string): Promise<string[]>;
9
+ }
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ // In-memory SecureChatStore — the provider default and the unit-test backing store.
3
+ //
4
+ // Not persistent: state is lost on reload. Platforms inject a durable store (IndexedDB on web) for
5
+ // real persistence; this keeps core usable in tests and SSR without one.
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.MemoryStore = void 0;
8
+ /** A `Map`-backed {@link SecureChatStore}. Non-persistent; the default when no store is injected. */
9
+ class MemoryStore {
10
+ constructor() {
11
+ this.map = new Map();
12
+ }
13
+ async get(key) {
14
+ return this.map.get(key) ?? null;
15
+ }
16
+ async set(key, value) {
17
+ this.map.set(key, value);
18
+ }
19
+ async delete(key) {
20
+ this.map.delete(key);
21
+ }
22
+ async list(prefix) {
23
+ return [...this.map.keys()].filter((k) => k.startsWith(prefix));
24
+ }
25
+ }
26
+ exports.MemoryStore = MemoryStore;
27
+ //# sourceMappingURL=memory-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-store.js","sourceRoot":"","sources":["../../../src/persistence/memory-store.ts"],"names":[],"mappings":";AAAA,oFAAoF;AACpF,EAAE;AACF,mGAAmG;AACnG,yEAAyE;;;AAIzE,qGAAqG;AACrG,MAAa,WAAW;IAAxB;QACmB,QAAG,GAAG,IAAI,GAAG,EAAsB,CAAC;IAiBvD,CAAC;IAfC,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,KAAiB;QACtC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,MAAc;QACvB,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IAClE,CAAC;CACF;AAlBD,kCAkBC"}
@@ -0,0 +1,36 @@
1
+ import type { SecureDeviceModel } from "../contract/index.js";
2
+ import type { SecureChatStore } from "./store.js";
3
+ /** The persisted device record: stable id, opaque crypto device-state, and the server row. */
4
+ export interface PersistedDevice {
5
+ /** Stable MLS device id; survives reload. */
6
+ deviceId: string;
7
+ /** Opaque bytes from `crypto.exportDeviceState()`. */
8
+ deviceState: Uint8Array;
9
+ /** The registered server row (rehydrates UI without a refetch), or `null` if not yet registered. */
10
+ device: SecureDeviceModel | null;
11
+ }
12
+ /** Typed persistence for device identity, per-conversation group state, and the handshake cursor. */
13
+ export declare class SecureChatRepository {
14
+ private readonly store;
15
+ constructor(store: SecureChatStore);
16
+ /** Load the persisted device record, or `null` if none saved (first run / evicted). */
17
+ loadDevice(): Promise<PersistedDevice | null>;
18
+ /** Persist (replace) the device record. */
19
+ saveDevice(d: PersistedDevice): Promise<void>;
20
+ /** Remove the persisted device record. */
21
+ clearDevice(): Promise<void>;
22
+ /** Load opaque MLS group state for a conversation, or `null` if none. */
23
+ loadGroupState(conversationId: string): Promise<Uint8Array | null>;
24
+ /** Persist opaque MLS group state for a conversation. */
25
+ saveGroupState(conversationId: string, state: Uint8Array): Promise<void>;
26
+ /** Remove persisted group state for a conversation. */
27
+ deleteGroupState(conversationId: string): Promise<void>;
28
+ /** List the conversation ids that have persisted group state. */
29
+ listGroupConversationIds(): Promise<string[]>;
30
+ /** Load the persisted handshake delivery cursor (`seq`), or `null`. */
31
+ loadHandshakeCursor(): Promise<string | null>;
32
+ /** Persist the handshake delivery cursor (`seq`). */
33
+ saveHandshakeCursor(seq: string): Promise<void>;
34
+ /** Wipe all persisted secure-chat state (sign-out / device revoke). */
35
+ clearAll(): Promise<void>;
36
+ }
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ // SecureChatRepository — typed façade over a SecureChatStore.
3
+ //
4
+ // The ONLY place in the SDK that knows the persistence key strings and how each record is
5
+ // serialized. Binary fields are base64-wrapped inside JSON; opaque crypto blobs (group/device
6
+ // state) are stored as-is. Built by SecureChatProvider over the injected store.
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.SecureChatRepository = void 0;
9
+ const base64_js_1 = require("../util/base64.js");
10
+ const DEVICE_KEY = "device";
11
+ const GROUP_PREFIX = "group:";
12
+ const CURSOR_KEY = "handshake:cursor";
13
+ /** Typed persistence for device identity, per-conversation group state, and the handshake cursor. */
14
+ class SecureChatRepository {
15
+ constructor(store) {
16
+ this.store = store;
17
+ }
18
+ /** Load the persisted device record, or `null` if none saved (first run / evicted). */
19
+ async loadDevice() {
20
+ const bytes = await this.store.get(DEVICE_KEY);
21
+ if (!bytes)
22
+ return null;
23
+ const rec = JSON.parse((0, base64_js_1.bytesToUtf8)(bytes));
24
+ return { deviceId: rec.deviceId, deviceState: (0, base64_js_1.fromBase64)(rec.deviceState), device: rec.device };
25
+ }
26
+ /** Persist (replace) the device record. */
27
+ async saveDevice(d) {
28
+ const rec = {
29
+ deviceId: d.deviceId,
30
+ deviceState: (0, base64_js_1.toBase64)(d.deviceState),
31
+ device: d.device,
32
+ };
33
+ await this.store.set(DEVICE_KEY, (0, base64_js_1.utf8ToBytes)(JSON.stringify(rec)));
34
+ }
35
+ /** Remove the persisted device record. */
36
+ async clearDevice() {
37
+ await this.store.delete(DEVICE_KEY);
38
+ }
39
+ /** Load opaque MLS group state for a conversation, or `null` if none. */
40
+ async loadGroupState(conversationId) {
41
+ return this.store.get(GROUP_PREFIX + conversationId);
42
+ }
43
+ /** Persist opaque MLS group state for a conversation. */
44
+ async saveGroupState(conversationId, state) {
45
+ await this.store.set(GROUP_PREFIX + conversationId, state);
46
+ }
47
+ /** Remove persisted group state for a conversation. */
48
+ async deleteGroupState(conversationId) {
49
+ await this.store.delete(GROUP_PREFIX + conversationId);
50
+ }
51
+ /** List the conversation ids that have persisted group state. */
52
+ async listGroupConversationIds() {
53
+ const keys = await this.store.list(GROUP_PREFIX);
54
+ return keys.map((k) => k.slice(GROUP_PREFIX.length));
55
+ }
56
+ /** Load the persisted handshake delivery cursor (`seq`), or `null`. */
57
+ async loadHandshakeCursor() {
58
+ const bytes = await this.store.get(CURSOR_KEY);
59
+ return bytes ? (0, base64_js_1.bytesToUtf8)(bytes) : null;
60
+ }
61
+ /** Persist the handshake delivery cursor (`seq`). */
62
+ async saveHandshakeCursor(seq) {
63
+ await this.store.set(CURSOR_KEY, (0, base64_js_1.utf8ToBytes)(seq));
64
+ }
65
+ /** Wipe all persisted secure-chat state (sign-out / device revoke). */
66
+ async clearAll() {
67
+ const keys = await this.store.list("");
68
+ await Promise.all(keys.map((k) => this.store.delete(k)));
69
+ }
70
+ }
71
+ exports.SecureChatRepository = SecureChatRepository;
72
+ //# sourceMappingURL=repository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository.js","sourceRoot":"","sources":["../../../src/persistence/repository.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /** A platform-agnostic async key→blob store. Implementations: `MemoryStore`, `createIndexedDBStore`. */
2
+ export interface SecureChatStore {
3
+ /** Read the bytes at `key`, or `null` if absent. */
4
+ get(key: string): Promise<Uint8Array | null>;
5
+ /** Write `value` at `key`, overwriting any existing value. */
6
+ set(key: string, value: Uint8Array): Promise<void>;
7
+ /** Remove `key` if present (no-op when absent). */
8
+ delete(key: string): Promise<void>;
9
+ /** Return all keys that start with `prefix` (use `""` for every key). */
10
+ list(prefix: string): Promise<string[]>;
11
+ }
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ // The persistence seam — a dumb, async key→blob store.
3
+ //
4
+ // This is ALL a platform must implement: web ships an IndexedDB-backed store, native swaps a
5
+ // keystore later. The SDK owns the key schema and (de)serialization on top of this (see
6
+ // ./repository.ts); the store itself never interprets keys or values. Values are opaque bytes.
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/persistence/store.ts"],"names":[],"mappings":";AAAA,uDAAuD;AACvD,EAAE;AACF,6FAA6F;AAC7F,wFAAwF;AACxF,+FAA+F"}
@@ -1,4 +1,4 @@
1
- import { AddSecureMemberBody, CreateSecureConversationBody, PublishKeyPackagesBody, RegisterDeviceBody, RemoveSecureMemberBody, SecureConversationMemberModel, SecureConversationModel, SecureDeviceModel, SecureHandshakeModel, SecureKeyBackupModel, SecureKeyPackageClaim, SecureMessageModel, SendSecureMessageBody, UploadKeyBackupBody } from "../contract";
1
+ import { AddSecureMemberBody, CreateSecureConversationBody, PublishKeyPackagesBody, RegisterDeviceBody, RemoveSecureMemberBody, SecureConversationMemberModel, SecureConversationModel, SecureDeviceModel, SecureHandshakeModel, SecureKeyBackupModel, SecureKeyPackageClaim, SecureMessageModel, SendSecureMessageBody, UploadKeyBackupBody } from "../contract/index.js";
2
2
  /**
3
3
  * Configuration for {@link SecureChatRestClient}. The base URL and access token are read through
4
4
  * resolver callbacks rather than captured once, so a late-set `baseUrl` or a refreshed token take
@@ -1,5 +1,5 @@
1
1
  import { Socket } from "socket.io-client";
2
- import { SecureHandshakeModel, SecureMessageModel } from "../contract";
2
+ import { SecureHandshakeModel, SecureMessageModel } from "../contract/index.js";
3
3
  /** Server → client events on the `/secure` namespace (§10). */
4
4
  export interface SecureServerEvents {
5
5
  "secure:message": (message: SecureMessageModel) => void;
@@ -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;