@agora-sdk/secure-chat-core 0.1.0 → 0.3.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 (64) hide show
  1. package/dist/cjs/context/secure-chat-context.d.ts +34 -18
  2. package/dist/cjs/context/secure-chat-context.js +78 -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/useSecureHandshakes.d.ts +64 -0
  13. package/dist/cjs/hooks/useSecureHandshakes.js +215 -0
  14. package/dist/cjs/hooks/useSecureHandshakes.js.map +1 -0
  15. package/dist/cjs/hooks/useSecureMessages.d.ts +10 -11
  16. package/dist/cjs/hooks/useSecureMessages.js +99 -21
  17. package/dist/cjs/hooks/useSecureMessages.js.map +1 -1
  18. package/dist/cjs/index.d.ts +20 -14
  19. package/dist/cjs/index.js +25 -19
  20. package/dist/cjs/index.js.map +1 -1
  21. package/dist/cjs/package.json +1 -0
  22. package/dist/cjs/persistence/memory-store.d.ts +9 -0
  23. package/dist/cjs/persistence/memory-store.js +27 -0
  24. package/dist/cjs/persistence/memory-store.js.map +1 -0
  25. package/dist/cjs/persistence/repository.d.ts +36 -0
  26. package/dist/cjs/persistence/repository.js +72 -0
  27. package/dist/cjs/persistence/repository.js.map +1 -0
  28. package/dist/cjs/persistence/store.d.ts +11 -0
  29. package/dist/cjs/persistence/store.js +8 -0
  30. package/dist/cjs/persistence/store.js.map +1 -0
  31. package/dist/cjs/transport/rest.d.ts +1 -1
  32. package/dist/cjs/transport/socket.d.ts +1 -1
  33. package/dist/esm/context/secure-chat-context.d.ts +34 -18
  34. package/dist/esm/context/secure-chat-context.js +77 -22
  35. package/dist/esm/context/secure-chat-context.js.map +1 -1
  36. package/dist/esm/contract/index.js +2 -2
  37. package/dist/esm/contract/index.js.map +1 -1
  38. package/dist/esm/hooks/useSecureConversations.d.ts +1 -1
  39. package/dist/esm/hooks/useSecureConversations.js +6 -4
  40. package/dist/esm/hooks/useSecureConversations.js.map +1 -1
  41. package/dist/esm/hooks/useSecureDevice.d.ts +14 -13
  42. package/dist/esm/hooks/useSecureDevice.js +55 -22
  43. package/dist/esm/hooks/useSecureDevice.js.map +1 -1
  44. package/dist/esm/hooks/useSecureHandshakes.d.ts +64 -0
  45. package/dist/esm/hooks/useSecureHandshakes.js +212 -0
  46. package/dist/esm/hooks/useSecureHandshakes.js.map +1 -0
  47. package/dist/esm/hooks/useSecureMessages.d.ts +10 -11
  48. package/dist/esm/hooks/useSecureMessages.js +96 -18
  49. package/dist/esm/hooks/useSecureMessages.js.map +1 -1
  50. package/dist/esm/index.d.ts +20 -14
  51. package/dist/esm/index.js +10 -7
  52. package/dist/esm/index.js.map +1 -1
  53. package/dist/esm/persistence/memory-store.d.ts +9 -0
  54. package/dist/esm/persistence/memory-store.js +23 -0
  55. package/dist/esm/persistence/memory-store.js.map +1 -0
  56. package/dist/esm/persistence/repository.d.ts +36 -0
  57. package/dist/esm/persistence/repository.js +68 -0
  58. package/dist/esm/persistence/repository.js.map +1 -0
  59. package/dist/esm/persistence/store.d.ts +11 -0
  60. package/dist/esm/persistence/store.js +7 -0
  61. package/dist/esm/persistence/store.js.map +1 -0
  62. package/dist/esm/transport/rest.d.ts +1 -1
  63. package/dist/esm/transport/socket.d.ts +1 -1
  64. package/package.json +3 -3
@@ -1,39 +1,96 @@
1
1
  // useSecureMessages — load, decrypt, send, and live-receive messages in a secure conversation.
2
2
  //
3
- // Encryption/decryption runs through the injected `SecureChatCrypto` against the conversation's MLS
4
- // `GroupHandle`. Resolving conversationId → GroupHandle is owned by the platform persistence layer
5
- // (Phase 2: IndexedDB group state via processWelcome/importGroupState), so the caller passes the
6
- // handle in. Without it, ciphertext is still listed/received but left undecrypted (`plaintext: null`)
7
- // and sending is disabled keeping the transport usable ahead of the crypto wiring.
8
- import { useCallback, useEffect, useState } from "react";
9
- import { toBase64, fromBase64, utf8ToBytes, bytesToUtf8 } from "../util/base64";
10
- import { useSecureChat } from "../context/secure-chat-context";
3
+ // Self-sufficient once a store is wired: the MLS GroupHandle is auto-resolved from persistence via
4
+ // resolveGroup, and senderDeviceId is read from the persisted device. Both stay overridable through
5
+ // options for advanced use. Without a resolvable handle, ciphertext is still listed/received
6
+ // (plaintext: null) and sending is disabled.
7
+ import { useCallback, useEffect, useRef, useState } from "react";
8
+ import { toBase64, fromBase64, utf8ToBytes, bytesToUtf8 } from "../util/base64.js";
9
+ import { useSecureChat } from "../context/secure-chat-context.js";
11
10
  /**
12
11
  * Load, decrypt, send, and live-receive messages in one secure conversation.
13
12
  *
14
- * Decryption runs through the injected `SecureChatCrypto` against the conversation's MLS
15
- * `GroupHandle`. Without a `group` in `options`, ciphertext is still listed and received (as
16
- * `plaintext: null`) and sending is disabled — letting the transport work ahead of the crypto
17
- * wiring. Joins the conversation's socket room to receive `secure:message` events live.
13
+ * Auto-resolves the MLS group handle (via `resolveGroup`) and the sender device id (from the
14
+ * persisted device) unless overridden in `options`. Joins the conversation socket room for live
15
+ * `secure:message` events.
18
16
  *
19
17
  * @param conversationId - The conversation to read and send within.
20
- * @param options - {@link UseSecureMessagesOptions} — the MLS `group` handle and `senderDeviceId`.
21
- * @returns {@link UseSecureMessagesValues} — the message list, paging state, and `sendMessage`.
18
+ * @param options - {@link UseSecureMessagesOptions}.
19
+ * @returns {@link UseSecureMessagesValues}.
22
20
  *
23
21
  * @example
24
22
  * ```tsx
25
- * const { messages, sendMessage } = useSecureMessages(conversationId, { group, senderDeviceId });
23
+ * const { messages, sendMessage } = useSecureMessages(conversationId);
26
24
  * await sendMessage("hello 💜");
27
25
  * ```
28
26
  */
29
27
  export function useSecureMessages(conversationId, options = {}) {
30
- const { rest, crypto, socket } = useSecureChat();
31
- const { group, senderDeviceId } = options;
28
+ const { rest, crypto, socket, repo, resolveGroup, getGroupVersion, subscribeGroupChange } = useSecureChat();
32
29
  const [messages, setMessages] = useState([]);
33
30
  const [before, setBefore] = useState(undefined);
34
31
  const [hasMore, setHasMore] = useState(true);
35
32
  const [loading, setLoading] = useState(false);
36
33
  const [error, setError] = useState(null);
34
+ const [group, setGroup] = useState(options.group ?? null);
35
+ const [senderDeviceId, setSenderDeviceId] = useState(options.senderDeviceId);
36
+ // Bumps when THIS conversation's group handle advances (a join or a processed Commit, driven by
37
+ // useSecureHandshakes calling rememberGroup). Feeds the group-resolve effect's deps so we re-resolve
38
+ // the now-current handle and flush buffered (plaintext:null) rows.
39
+ const [groupVersion, setGroupVersion] = useState(0);
40
+ // Latest messages, read by the "decrypt history once the group resolves" effect below without
41
+ // making `messages` one of its deps (which would loop).
42
+ const messagesRef = useRef(messages);
43
+ messagesRef.current = messages;
44
+ // Subscribe to provider group-change signals; only a change to OUR conversation's version updates
45
+ // state (React bails on an unchanged primitive), so unrelated conversations don't re-resolve us.
46
+ useEffect(() => {
47
+ return subscribeGroupChange(() => setGroupVersion(getGroupVersion(conversationId)));
48
+ }, [subscribeGroupChange, getGroupVersion, conversationId]);
49
+ // Resolve the group handle: explicit override, else persisted state.
50
+ useEffect(() => {
51
+ if (options.group) {
52
+ setGroup(options.group);
53
+ return;
54
+ }
55
+ // Clear any stale handle from the previous conversation before re-resolving, so live messages
56
+ // for the new conversation never decrypt against the old group during the async window.
57
+ setGroup(null);
58
+ let alive = true;
59
+ resolveGroup(conversationId)
60
+ .then((g) => {
61
+ if (alive)
62
+ setGroup(g);
63
+ })
64
+ .catch(() => {
65
+ if (alive)
66
+ setGroup(null);
67
+ });
68
+ return () => {
69
+ alive = false;
70
+ };
71
+ // `groupVersion` re-runs this when a Commit/join advances the handle → flushes buffered rows.
72
+ }, [options.group, conversationId, resolveGroup, groupVersion]);
73
+ // Resolve the sender device id: explicit override, else persisted device row.
74
+ useEffect(() => {
75
+ if (options.senderDeviceId) {
76
+ setSenderDeviceId(options.senderDeviceId);
77
+ return;
78
+ }
79
+ let alive = true;
80
+ repo
81
+ .loadDevice()
82
+ .then((d) => {
83
+ if (alive)
84
+ setSenderDeviceId(d?.device?.id ?? undefined);
85
+ })
86
+ .catch(() => {
87
+ if (alive)
88
+ setSenderDeviceId(undefined);
89
+ });
90
+ return () => {
91
+ alive = false;
92
+ };
93
+ }, [options.senderDeviceId, repo]);
37
94
  const decrypt = useCallback(async (model) => {
38
95
  if (!group)
39
96
  return { model, plaintext: null };
@@ -78,6 +135,9 @@ export function useSecureMessages(conversationId, options = {}) {
78
135
  await load(false);
79
136
  }, [hasMore, loading, load]);
80
137
  const sendMessage = useCallback(async (text) => {
138
+ // Assumes the crypto identity is already hydrated (mount `useSecureDevice` under the same
139
+ // provider): the crypto layer tags the sender from the restored device identity, so after a
140
+ // reload the first send must wait for useSecureDevice's importDeviceState to complete.
81
141
  if (!group)
82
142
  throw new Error("Cannot send: no MLS group handle for this conversation.");
83
143
  if (!senderDeviceId)
@@ -95,13 +155,31 @@ export function useSecureMessages(conversationId, options = {}) {
95
155
  refresh();
96
156
  // eslint-disable-next-line react-hooks/exhaustive-deps
97
157
  }, [conversationId]);
158
+ // Decrypt history that was listed before the group handle resolved. On reload, the first page loads
159
+ // while resolveGroup is still in flight, so those rows come back `plaintext: null`; once the handle
160
+ // arrives (decrypt is recreated with it), re-decrypt the still-undecrypted rows in place — no
161
+ // re-fetch, scroll/pagination preserved.
162
+ useEffect(() => {
163
+ if (!group)
164
+ return;
165
+ if (!messagesRef.current.some((m) => m.plaintext === null))
166
+ return;
167
+ let alive = true;
168
+ Promise.all(messagesRef.current.map((m) => (m.plaintext === null ? decrypt(m.model) : Promise.resolve(m)))).then((next) => {
169
+ if (alive)
170
+ setMessages(next);
171
+ });
172
+ return () => {
173
+ alive = false;
174
+ };
175
+ }, [group, decrypt]);
98
176
  // Live receive: join the conversation room and decrypt inbound ciphertext.
99
177
  useEffect(() => {
100
178
  socket.joinConversation(conversationId);
101
179
  const off = socket.on("secure:message", (model) => {
102
180
  if (model.conversationId !== conversationId)
103
181
  return;
104
- decrypt(model).then((m) => setMessages((prev) => prev.some((p) => p.model.id === m.model.id) ? prev : [m, ...prev]));
182
+ decrypt(model).then((m) => setMessages((prev) => (prev.some((p) => p.model.id === m.model.id) ? prev : [m, ...prev])));
105
183
  });
106
184
  return off;
107
185
  }, [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":"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"}
@@ -1,15 +1,21 @@
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 { useSecureHandshakes } from "./hooks/useSecureHandshakes.js";
10
+ export type { UseSecureHandshakesOptions, UseSecureHandshakesValues, } from "./hooks/useSecureHandshakes.js";
11
+ export { SecureChatRestClient } from "./transport/rest.js";
12
+ export type { SecureChatRestConfig } from "./transport/rest.js";
13
+ export { SecureChatSocketClient } from "./transport/socket.js";
14
+ export type { SecureSocket, SecureServerEvents, SecureClientEvents, SecureChatSocketConfig, } from "./transport/socket.js";
13
15
  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";
16
+ export type * from "./contract/index.js";
17
+ export type { SecureChatStore } from "./persistence/store.js";
18
+ export { MemoryStore } from "./persistence/memory-store.js";
19
+ export { SecureChatRepository } from "./persistence/repository.js";
20
+ export type { PersistedDevice } from "./persistence/repository.js";
21
+ export { toBase64, fromBase64, utf8ToBytes, bytesToUtf8 } from "./util/base64.js";
package/dist/esm/index.js CHANGED
@@ -4,14 +4,17 @@
4
4
  // injection of a `SecureChatCrypto`. Platform packages (@agora-sdk/secure-chat-react-js, etc.)
5
5
  // re-export this and add the concrete crypto + persistence.
6
6
  // ── context / provider ──────────────────────────────────────────────────────
7
- export { SecureChatProvider, useSecureChat } from "./context/secure-chat-context";
7
+ export { SecureChatProvider, useSecureChat } from "./context/secure-chat-context.js";
8
8
  // ── hooks ────────────────────────────────────────────────────────────────────
9
- export { useSecureDevice } from "./hooks/useSecureDevice";
10
- export { useSecureConversations } from "./hooks/useSecureConversations";
11
- export { useSecureMessages } from "./hooks/useSecureMessages";
9
+ export { useSecureDevice } from "./hooks/useSecureDevice.js";
10
+ export { useSecureConversations } from "./hooks/useSecureConversations.js";
11
+ export { useSecureMessages } from "./hooks/useSecureMessages.js";
12
+ export { useSecureHandshakes } from "./hooks/useSecureHandshakes.js";
12
13
  // ── transport (for advanced / non-React use) ─────────────────────────────────
13
- export { SecureChatRestClient } from "./transport/rest";
14
- export { SecureChatSocketClient } from "./transport/socket";
14
+ export { SecureChatRestClient } from "./transport/rest.js";
15
+ export { SecureChatSocketClient } from "./transport/socket.js";
16
+ export { MemoryStore } from "./persistence/memory-store.js";
17
+ export { SecureChatRepository } from "./persistence/repository.js";
15
18
  // ── utils ────────────────────────────────────────────────────────────────────
16
- export { toBase64, fromBase64, utf8ToBytes, bytesToUtf8 } from "./util/base64";
19
+ export { toBase64, fromBase64, utf8ToBytes, bytesToUtf8 } from "./util/base64.js";
17
20
  //# 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,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAMlF,gFAAgF;AAChF,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE1D,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAExE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAO9D,gFAAgF;AAChF,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAsB5D,gFAAgF;AAChF,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
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,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAMrF,gFAAgF;AAChF,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAE7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAE3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAMjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAMrE,gFAAgF;AAChF,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAE3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAwB/D,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAGnE,gFAAgF;AAChF,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC"}
@@ -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,23 @@
1
+ // In-memory SecureChatStore — the provider default and the unit-test backing store.
2
+ //
3
+ // Not persistent: state is lost on reload. Platforms inject a durable store (IndexedDB on web) for
4
+ // real persistence; this keeps core usable in tests and SSR without one.
5
+ /** A `Map`-backed {@link SecureChatStore}. Non-persistent; the default when no store is injected. */
6
+ export class MemoryStore {
7
+ constructor() {
8
+ this.map = new Map();
9
+ }
10
+ async get(key) {
11
+ return this.map.get(key) ?? null;
12
+ }
13
+ async set(key, value) {
14
+ this.map.set(key, value);
15
+ }
16
+ async delete(key) {
17
+ this.map.delete(key);
18
+ }
19
+ async list(prefix) {
20
+ return [...this.map.keys()].filter((k) => k.startsWith(prefix));
21
+ }
22
+ }
23
+ //# 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,MAAM,OAAO,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"}
@@ -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,68 @@
1
+ // SecureChatRepository — typed façade over a SecureChatStore.
2
+ //
3
+ // The ONLY place in the SDK that knows the persistence key strings and how each record is
4
+ // serialized. Binary fields are base64-wrapped inside JSON; opaque crypto blobs (group/device
5
+ // state) are stored as-is. Built by SecureChatProvider over the injected store.
6
+ import { toBase64, fromBase64, utf8ToBytes, bytesToUtf8 } from "../util/base64.js";
7
+ const DEVICE_KEY = "device";
8
+ const GROUP_PREFIX = "group:";
9
+ const CURSOR_KEY = "handshake:cursor";
10
+ /** Typed persistence for device identity, per-conversation group state, and the handshake cursor. */
11
+ export class SecureChatRepository {
12
+ constructor(store) {
13
+ this.store = store;
14
+ }
15
+ /** Load the persisted device record, or `null` if none saved (first run / evicted). */
16
+ async loadDevice() {
17
+ const bytes = await this.store.get(DEVICE_KEY);
18
+ if (!bytes)
19
+ return null;
20
+ const rec = JSON.parse(bytesToUtf8(bytes));
21
+ return { deviceId: rec.deviceId, deviceState: fromBase64(rec.deviceState), device: rec.device };
22
+ }
23
+ /** Persist (replace) the device record. */
24
+ async saveDevice(d) {
25
+ const rec = {
26
+ deviceId: d.deviceId,
27
+ deviceState: toBase64(d.deviceState),
28
+ device: d.device,
29
+ };
30
+ await this.store.set(DEVICE_KEY, utf8ToBytes(JSON.stringify(rec)));
31
+ }
32
+ /** Remove the persisted device record. */
33
+ async clearDevice() {
34
+ await this.store.delete(DEVICE_KEY);
35
+ }
36
+ /** Load opaque MLS group state for a conversation, or `null` if none. */
37
+ async loadGroupState(conversationId) {
38
+ return this.store.get(GROUP_PREFIX + conversationId);
39
+ }
40
+ /** Persist opaque MLS group state for a conversation. */
41
+ async saveGroupState(conversationId, state) {
42
+ await this.store.set(GROUP_PREFIX + conversationId, state);
43
+ }
44
+ /** Remove persisted group state for a conversation. */
45
+ async deleteGroupState(conversationId) {
46
+ await this.store.delete(GROUP_PREFIX + conversationId);
47
+ }
48
+ /** List the conversation ids that have persisted group state. */
49
+ async listGroupConversationIds() {
50
+ const keys = await this.store.list(GROUP_PREFIX);
51
+ return keys.map((k) => k.slice(GROUP_PREFIX.length));
52
+ }
53
+ /** Load the persisted handshake delivery cursor (`seq`), or `null`. */
54
+ async loadHandshakeCursor() {
55
+ const bytes = await this.store.get(CURSOR_KEY);
56
+ return bytes ? bytesToUtf8(bytes) : null;
57
+ }
58
+ /** Persist the handshake delivery cursor (`seq`). */
59
+ async saveHandshakeCursor(seq) {
60
+ await this.store.set(CURSOR_KEY, utf8ToBytes(seq));
61
+ }
62
+ /** Wipe all persisted secure-chat state (sign-out / device revoke). */
63
+ async clearAll() {
64
+ const keys = await this.store.list("");
65
+ await Promise.all(keys.map((k) => this.store.delete(k)));
66
+ }
67
+ }
68
+ //# 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,7 @@
1
+ // The persistence seam — a dumb, async key→blob store.
2
+ //
3
+ // This is ALL a platform must implement: web ships an IndexedDB-backed store, native swaps a
4
+ // keystore later. The SDK owns the key schema and (de)serialization on top of this (see
5
+ // ./repository.ts); the store itself never interprets keys or values. Values are opaque bytes.
6
+ export {};
7
+ //# 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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agora-sdk/secure-chat-core",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "private": false,
5
5
  "license": "Apache-2.0",
6
6
  "author": "Agora SDK Plus, maintained by Jenova Marie",
@@ -37,7 +37,7 @@
37
37
  "dependencies": {
38
38
  "axios": "^1.4.0",
39
39
  "socket.io-client": "^4.8.1",
40
- "@agora-sdk/secure-chat-crypto": "0.1.1"
40
+ "@agora-sdk/secure-chat-crypto": "0.3.0"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "@agora-sdk/core": "^1.2.2",
@@ -45,7 +45,7 @@
45
45
  },
46
46
  "scripts": {
47
47
  "build:esm": "tsc -p tsconfig.esm.json",
48
- "build:cjs": "tsc -p tsconfig.cjs.json",
48
+ "build:cjs": "tsc -p tsconfig.cjs.json && echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json",
49
49
  "build": "rimraf dist && pnpm run build:esm && pnpm run build:cjs",
50
50
  "prepublish": "pnpm run build"
51
51
  }