@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.
- package/dist/cjs/context/secure-chat-context.d.ts +24 -18
- package/dist/cjs/context/secure-chat-context.js +42 -23
- package/dist/cjs/context/secure-chat-context.js.map +1 -1
- package/dist/cjs/contract/index.js +2 -2
- package/dist/cjs/contract/index.js.map +1 -1
- package/dist/cjs/hooks/useSecureConversations.d.ts +1 -1
- package/dist/cjs/hooks/useSecureConversations.js +9 -7
- package/dist/cjs/hooks/useSecureConversations.js.map +1 -1
- package/dist/cjs/hooks/useSecureDevice.d.ts +14 -13
- package/dist/cjs/hooks/useSecureDevice.js +58 -25
- package/dist/cjs/hooks/useSecureDevice.js.map +1 -1
- package/dist/cjs/hooks/useSecureMessages.d.ts +10 -11
- package/dist/cjs/hooks/useSecureMessages.js +89 -21
- package/dist/cjs/hooks/useSecureMessages.js.map +1 -1
- package/dist/cjs/index.d.ts +18 -14
- package/dist/cjs/index.js +23 -19
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/persistence/memory-store.d.ts +9 -0
- package/dist/cjs/persistence/memory-store.js +27 -0
- package/dist/cjs/persistence/memory-store.js.map +1 -0
- package/dist/cjs/persistence/repository.d.ts +36 -0
- package/dist/cjs/persistence/repository.js +72 -0
- package/dist/cjs/persistence/repository.js.map +1 -0
- package/dist/cjs/persistence/store.d.ts +11 -0
- package/dist/cjs/persistence/store.js +8 -0
- package/dist/cjs/persistence/store.js.map +1 -0
- package/dist/cjs/transport/rest.d.ts +1 -1
- package/dist/cjs/transport/socket.d.ts +1 -1
- package/dist/esm/context/secure-chat-context.d.ts +24 -18
- package/dist/esm/context/secure-chat-context.js +41 -22
- package/dist/esm/context/secure-chat-context.js.map +1 -1
- package/dist/esm/contract/index.js +2 -2
- package/dist/esm/contract/index.js.map +1 -1
- package/dist/esm/hooks/useSecureConversations.d.ts +1 -1
- package/dist/esm/hooks/useSecureConversations.js +6 -4
- package/dist/esm/hooks/useSecureConversations.js.map +1 -1
- package/dist/esm/hooks/useSecureDevice.d.ts +14 -13
- package/dist/esm/hooks/useSecureDevice.js +55 -22
- package/dist/esm/hooks/useSecureDevice.js.map +1 -1
- package/dist/esm/hooks/useSecureMessages.d.ts +10 -11
- package/dist/esm/hooks/useSecureMessages.js +86 -18
- package/dist/esm/hooks/useSecureMessages.js.map +1 -1
- package/dist/esm/index.d.ts +18 -14
- package/dist/esm/index.js +9 -7
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/persistence/memory-store.d.ts +9 -0
- package/dist/esm/persistence/memory-store.js +23 -0
- package/dist/esm/persistence/memory-store.js.map +1 -0
- package/dist/esm/persistence/repository.d.ts +36 -0
- package/dist/esm/persistence/repository.js +68 -0
- package/dist/esm/persistence/repository.js.map +1 -0
- package/dist/esm/persistence/store.d.ts +11 -0
- package/dist/esm/persistence/store.js +7 -0
- package/dist/esm/persistence/store.js.map +1 -0
- package/dist/esm/transport/rest.d.ts +1 -1
- package/dist/esm/transport/socket.d.ts +1 -1
- 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}:
|
|
7
|
-
* and the active project id.
|
|
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
|
|
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
|
|
37
|
-
* `ReplykeProvider
|
|
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}
|
|
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
|
-
* <
|
|
47
|
-
* <
|
|
48
|
-
*
|
|
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
|
|
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)
|
|
10
|
-
//
|
|
11
|
-
//
|
|
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
|
|
15
|
-
const
|
|
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
|
|
19
|
-
* `ReplykeProvider
|
|
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}
|
|
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
|
-
* <
|
|
29
|
-
* <
|
|
30
|
-
*
|
|
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
|
|
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
|
|
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":";;
|
|
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,
|
|
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
|
|
11
|
-
const
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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":";AAAA,qFAAqF;AACrF,EAAE;AACF,iGAAiG;AACjG,oGAAoG;AACpG,0DAA0D;;AAyC1D,
|
|
1
|
+
{"version":3,"file":"useSecureConversations.js","sourceRoot":"","sources":["../../../src/hooks/useSecureConversations.tsx"],"names":[],"mappings":";AAAA,qFAAqF;AACrF,EAAE;AACF,iGAAiG;AACjG,oGAAoG;AACpG,0DAA0D;;AAyC1D,wDAmGC;AA1ID,iCAAyD;AAEzD,iDAAyD;AACzD,8EAAkE;AAoBlE;;;;;;;;;;;;;;;GAeG;AACH,SAAgB,sBAAsB;IACpC,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,GAAG,IAAA,sCAAa,GAAE,CAAC;IAEhE,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,IAAA,gBAAQ,EAA4B,EAAE,CAAC,CAAC;IAClF,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;IAElD,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,iBAAiB,CAAC;gBACxC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM;gBAClC,KAAK,EAAE,EAAE;aACV,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YAC/D,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAChE,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACzB,gBAAgB,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;QAC9F,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,MAAM,CAAC,CACf,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,wBAAwB,GAAG,IAAA,mBAAW,EAC1C,KAAK,EAAE,UAAkB,EAAoC,EAAE;QAC7D,6EAA6E;QAC7E,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;QACvD,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;QACjE,CAAC;QACD,MAAM,cAAc,GAAG,MAAM,OAAO,CAAC,GAAG,CACtC,WAAW,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;YAC1B,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC/C,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,UAAU,EAAE,IAAA,sBAAU,EAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;QACtE,CAAC,CAAC,CACH,CAAC;QAEF,uEAAuE;QACvE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC;QAEzE,gFAAgF;QAChF,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC;YACjD,IAAI,EAAE,IAAI;YACV,UAAU,EAAE,IAAA,oBAAQ,EAAC,KAAK,CAAC,UAAU,CAAC;YACtC,aAAa,EAAE,CAAC,UAAU,CAAC;YAC3B,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC7B,cAAc,EAAE,CAAC,CAAC,cAAc;gBAChC,OAAO,EAAE,IAAA,oBAAQ,EAAC,CAAC,CAAC,OAAO,CAAC;gBAC5B,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE;aAC9B,CAAC,CAAC;SACJ,CAAC,CAAC;QAEH,qFAAqF;QACrF,MAAM,aAAa,CAAC,YAAY,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAE5C,gBAAgB,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,YAAY,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC5F,OAAO,YAAY,CAAC;IACtB,CAAC,EACD,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,CAAC,CAC9B,CAAC;IAEF,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,OAAO,EAAE,CAAC;QACV,uDAAuD;IACzD,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,wEAAwE;IACxE,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,MAAM,SAAS,GAAG,MAAM,CAAC,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;YACvD,OAAO,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,CAAC,EAAE,CAAC,oBAAoB,EAAE,GAAG,EAAE;YACnD,OAAO,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QACH,OAAO,GAAG,EAAE;YACV,SAAS,EAAE,CAAC;YACZ,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAEtB,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,wBAAwB,EAAE,CAAC;AACjG,CAAC"}
|
|
@@ -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
|
|
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
|
|
34
|
+
* Register this client as an MLS device, persist its identity, and keep KeyPackages stocked.
|
|
32
35
|
*
|
|
33
|
-
* On
|
|
34
|
-
*
|
|
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}
|
|
39
|
-
* @returns {@link UseSecureDeviceValues}
|
|
39
|
+
* @param options - {@link UseSecureDeviceOptions}.
|
|
40
|
+
* @returns {@link UseSecureDeviceValues}.
|
|
40
41
|
*
|
|
41
42
|
* @example
|
|
42
43
|
* ```tsx
|
|
43
|
-
* const { device, register } = useSecureDevice(
|
|
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
|
|
2
|
+
// useSecureDevice — register this client as an MLS device (leaf), persist its identity, and keep its
|
|
3
|
+
// KeyPackages topped up.
|
|
3
4
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
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
|
|
14
|
-
const
|
|
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.
|
|
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
|
|
26
|
+
* Register this client as an MLS device, persist its identity, and keep KeyPackages stocked.
|
|
30
27
|
*
|
|
31
|
-
* On
|
|
32
|
-
*
|
|
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}
|
|
37
|
-
* @returns {@link UseSecureDeviceValues}
|
|
31
|
+
* @param options - {@link UseSecureDeviceOptions}.
|
|
32
|
+
* @returns {@link UseSecureDeviceValues}.
|
|
38
33
|
*
|
|
39
34
|
* @example
|
|
40
35
|
* ```tsx
|
|
41
|
-
* const { device, register } = useSecureDevice(
|
|
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,
|
|
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,
|
|
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,
|
|
85
|
-
credential: (0,
|
|
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":";AAAA,qGAAqG;AACrG,
|
|
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
|
-
/**
|
|
12
|
+
/** Override the MLS group handle. Defaults to the persisted handle via `resolveGroup`. */
|
|
13
13
|
group?: GroupHandle;
|
|
14
|
-
/**
|
|
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
|
|
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
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* `
|
|
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}
|
|
44
|
-
* @returns {@link UseSecureMessagesValues}
|
|
42
|
+
* @param options - {@link UseSecureMessagesOptions}.
|
|
43
|
+
* @returns {@link UseSecureMessagesValues}.
|
|
45
44
|
*
|
|
46
45
|
* @example
|
|
47
46
|
* ```tsx
|
|
48
|
-
* const { messages, sendMessage } = useSecureMessages(conversationId
|
|
47
|
+
* const { messages, sendMessage } = useSecureMessages(conversationId);
|
|
49
48
|
* await sendMessage("hello 💜");
|
|
50
49
|
* ```
|
|
51
50
|
*/
|