@agora-sdk/secure-chat-core 0.3.0 → 0.5.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/backup/passphrase-strength.d.ts +22 -0
- package/dist/cjs/backup/passphrase-strength.js +75 -0
- package/dist/cjs/backup/passphrase-strength.js.map +1 -0
- package/dist/cjs/context/secure-chat-context.d.ts +12 -1
- package/dist/cjs/context/secure-chat-context.js +3 -1
- package/dist/cjs/context/secure-chat-context.js.map +1 -1
- package/dist/cjs/contract/index.d.ts +2 -150
- package/dist/cjs/contract/index.js +11 -10
- package/dist/cjs/contract/index.js.map +1 -1
- package/dist/cjs/hooks/useSecureBackup.d.ts +67 -0
- package/dist/cjs/hooks/useSecureBackup.js +207 -0
- package/dist/cjs/hooks/useSecureBackup.js.map +1 -0
- package/dist/cjs/hooks/useSecureDevice.d.ts +21 -1
- package/dist/cjs/hooks/useSecureDevice.js +46 -5
- package/dist/cjs/hooks/useSecureDevice.js.map +1 -1
- package/dist/cjs/hooks/useSecureMessages.d.ts +16 -3
- package/dist/cjs/hooks/useSecureMessages.js +38 -15
- package/dist/cjs/hooks/useSecureMessages.js.map +1 -1
- package/dist/cjs/hooks/useSecureSafetyNumber.d.ts +30 -0
- package/dist/cjs/hooks/useSecureSafetyNumber.js +82 -0
- package/dist/cjs/hooks/useSecureSafetyNumber.js.map +1 -0
- package/dist/cjs/index.d.ts +12 -1
- package/dist/cjs/index.js +16 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/transport/socket.d.ts +15 -3
- package/dist/cjs/transport/socket.js +4 -2
- package/dist/cjs/transport/socket.js.map +1 -1
- package/dist/cjs/util/padding.d.ts +39 -0
- package/dist/cjs/util/padding.js +80 -0
- package/dist/cjs/util/padding.js.map +1 -0
- package/dist/cjs/util/safety-number.d.ts +27 -0
- package/dist/cjs/util/safety-number.js +84 -0
- package/dist/cjs/util/safety-number.js.map +1 -0
- package/dist/esm/backup/passphrase-strength.d.ts +22 -0
- package/dist/esm/backup/passphrase-strength.js +72 -0
- package/dist/esm/backup/passphrase-strength.js.map +1 -0
- package/dist/esm/context/secure-chat-context.d.ts +12 -1
- package/dist/esm/context/secure-chat-context.js +3 -1
- package/dist/esm/context/secure-chat-context.js.map +1 -1
- package/dist/esm/contract/index.d.ts +2 -150
- package/dist/esm/contract/index.js +11 -10
- package/dist/esm/contract/index.js.map +1 -1
- package/dist/esm/hooks/useSecureBackup.d.ts +67 -0
- package/dist/esm/hooks/useSecureBackup.js +204 -0
- package/dist/esm/hooks/useSecureBackup.js.map +1 -0
- package/dist/esm/hooks/useSecureDevice.d.ts +21 -1
- package/dist/esm/hooks/useSecureDevice.js +46 -5
- package/dist/esm/hooks/useSecureDevice.js.map +1 -1
- package/dist/esm/hooks/useSecureMessages.d.ts +16 -3
- package/dist/esm/hooks/useSecureMessages.js +38 -15
- package/dist/esm/hooks/useSecureMessages.js.map +1 -1
- package/dist/esm/hooks/useSecureSafetyNumber.d.ts +30 -0
- package/dist/esm/hooks/useSecureSafetyNumber.js +79 -0
- package/dist/esm/hooks/useSecureSafetyNumber.js.map +1 -0
- package/dist/esm/index.d.ts +12 -1
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/transport/socket.d.ts +15 -3
- package/dist/esm/transport/socket.js +4 -2
- package/dist/esm/transport/socket.js.map +1 -1
- package/dist/esm/util/padding.d.ts +39 -0
- package/dist/esm/util/padding.js +75 -0
- package/dist/esm/util/padding.js.map +1 -0
- package/dist/esm/util/safety-number.d.ts +27 -0
- package/dist/esm/util/safety-number.js +81 -0
- package/dist/esm/util/safety-number.js.map +1 -0
- package/package.json +3 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSecureBackup.js","sourceRoot":"","sources":["../../../src/hooks/useSecureBackup.tsx"],"names":[],"mappings":"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"}
|
|
@@ -8,7 +8,20 @@ export interface UseSecureDeviceOptions {
|
|
|
8
8
|
ciphersuite?: number;
|
|
9
9
|
/** How many KeyPackages to publish on registration and replenish toward. Default 20. */
|
|
10
10
|
keyPackageTarget?: number;
|
|
11
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Low-water mark: when the server's available count drops **below** this, a top-up refills to
|
|
13
|
+
* {@link UseSecureDeviceOptions.keyPackageTarget}. KeyPackages are single-use, so this guards
|
|
14
|
+
* against exhaustion (peers unable to add this device). Default `ceil(keyPackageTarget / 2)` (10 for
|
|
15
|
+
* the default target of 20). Only governs the proactive/manual path — the server's
|
|
16
|
+
* `secure:key-packages-low` signal always tops up, since the server already judged the stock low.
|
|
17
|
+
*/
|
|
18
|
+
keyPackageLowWater?: number;
|
|
19
|
+
/**
|
|
20
|
+
* Auto-replenish without app involvement. Default true. When true the hook subscribes to
|
|
21
|
+
* `secure:key-packages-low` and runs a one-shot proactive count check once the device is ready. When
|
|
22
|
+
* false, both automatic paths are disabled but {@link UseSecureDeviceValues.checkAndReplenish}
|
|
23
|
+
* still works on demand.
|
|
24
|
+
*/
|
|
12
25
|
autoReplenish?: boolean;
|
|
13
26
|
}
|
|
14
27
|
/** The state and actions returned by {@link useSecureDevice}. */
|
|
@@ -29,6 +42,13 @@ export interface UseSecureDeviceValues {
|
|
|
29
42
|
publishKeyPackages: (count?: number) => Promise<number>;
|
|
30
43
|
/** Re-query the server for the available KeyPackage count and update `keyPackagesAvailable`. */
|
|
31
44
|
refreshKeyPackageCount: () => Promise<number>;
|
|
45
|
+
/**
|
|
46
|
+
* Refresh the server count and, if it's below the low-water mark, top up to `keyPackageTarget`
|
|
47
|
+
* (publishing only the deficit). Safe to call before {@link UseSecureDeviceValues.register} — it
|
|
48
|
+
* no-ops to 0 when there's no device and never throws for that case — so an app can wire it to a
|
|
49
|
+
* window-focus / app-foreground handler.
|
|
50
|
+
*/
|
|
51
|
+
checkAndReplenish: () => Promise<number>;
|
|
32
52
|
}
|
|
33
53
|
/**
|
|
34
54
|
* Register this client as an MLS device, persist its identity, and keep KeyPackages stocked.
|
|
@@ -4,7 +4,14 @@
|
|
|
4
4
|
//
|
|
5
5
|
// On mount it re-hydrates a persisted device (stable deviceId + private state via
|
|
6
6
|
// crypto.importDeviceState) so a reload does NOT mint a new identity. register() generates, registers
|
|
7
|
-
// on the server, and persists.
|
|
7
|
+
// on the server, and persists.
|
|
8
|
+
//
|
|
9
|
+
// KeyPackages are single-use (the server consumes one per group-add), so running dry means peers can't
|
|
10
|
+
// add this device. The hook keeps the stock topped up to `keyPackageTarget` from three triggers: the
|
|
11
|
+
// server's `secure:key-packages-low` realtime signal, a one-shot proactive count check once the device
|
|
12
|
+
// is ready (self-heals a client that missed the signal while offline), and an app-callable
|
|
13
|
+
// `checkAndReplenish()` (e.g. on window focus). Each top-up publishes only the DEFICIT to the target
|
|
14
|
+
// (using the actual available count), not a blind full batch.
|
|
8
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
16
|
exports.useSecureDevice = useSecureDevice;
|
|
10
17
|
const react_1 = require("react");
|
|
@@ -40,6 +47,7 @@ function newDeviceId() {
|
|
|
40
47
|
function useSecureDevice(options = {}) {
|
|
41
48
|
const { crypto, rest, socket, repo } = (0, secure_chat_context_js_1.useSecureChat)();
|
|
42
49
|
const { ciphersuite, keyPackageTarget = 20, autoReplenish = true } = options;
|
|
50
|
+
const keyPackageLowWater = options.keyPackageLowWater ?? Math.ceil(keyPackageTarget / 2);
|
|
43
51
|
const [device, setDevice] = (0, react_1.useState)(null);
|
|
44
52
|
const [loading, setLoading] = (0, react_1.useState)(true);
|
|
45
53
|
const [registering, setRegistering] = (0, react_1.useState)(false);
|
|
@@ -47,6 +55,8 @@ function useSecureDevice(options = {}) {
|
|
|
47
55
|
const [keyPackagesAvailable, setKeyPackagesAvailable] = (0, react_1.useState)(null);
|
|
48
56
|
const deviceIdRef = (0, react_1.useRef)(options.deviceId ?? newDeviceId());
|
|
49
57
|
const registerStartedRef = (0, react_1.useRef)(false);
|
|
58
|
+
// Guards the one-shot proactive count check so it runs once per device-ready, not on every render.
|
|
59
|
+
const proactiveCheckedRef = (0, react_1.useRef)(false);
|
|
50
60
|
// On mount: re-hydrate a persisted device (stable id + private state). No persisted device ⇒
|
|
51
61
|
// first-run; the app calls register().
|
|
52
62
|
(0, react_1.useEffect)(() => {
|
|
@@ -99,6 +109,25 @@ function useSecureDevice(options = {}) {
|
|
|
99
109
|
setKeyPackagesAvailable(available);
|
|
100
110
|
return available;
|
|
101
111
|
}, [rest, device]);
|
|
112
|
+
// Publish only the shortfall (target − available) to refill to the target; nothing if already
|
|
113
|
+
// at/above it. Optimistically bumps the local count so the UI reflects the refill without a re-query.
|
|
114
|
+
// Callers guarantee `device` exists (publishKeyPackages throws otherwise).
|
|
115
|
+
const replenishToTarget = (0, react_1.useCallback)(async (available) => {
|
|
116
|
+
const deficit = keyPackageTarget - available;
|
|
117
|
+
if (deficit <= 0)
|
|
118
|
+
return 0;
|
|
119
|
+
const published = await publishKeyPackages(deficit);
|
|
120
|
+
setKeyPackagesAvailable(available + published);
|
|
121
|
+
return published;
|
|
122
|
+
}, [keyPackageTarget, publishKeyPackages]);
|
|
123
|
+
const checkAndReplenish = (0, react_1.useCallback)(async () => {
|
|
124
|
+
if (!device)
|
|
125
|
+
return 0; // not registered yet — no-op (app may call this eagerly on focus)
|
|
126
|
+
const available = await refreshKeyPackageCount();
|
|
127
|
+
if (available >= keyPackageLowWater)
|
|
128
|
+
return 0;
|
|
129
|
+
return replenishToTarget(available);
|
|
130
|
+
}, [device, refreshKeyPackageCount, keyPackageLowWater, replenishToTarget]);
|
|
102
131
|
const register = (0, react_1.useCallback)(async () => {
|
|
103
132
|
registerStartedRef.current = true;
|
|
104
133
|
setRegistering(true);
|
|
@@ -128,17 +157,28 @@ function useSecureDevice(options = {}) {
|
|
|
128
157
|
setRegistering(false);
|
|
129
158
|
}
|
|
130
159
|
}, [crypto, rest, repo, ciphersuite]);
|
|
131
|
-
// Auto-replenish on the server's low-water signal for this device.
|
|
160
|
+
// Auto-replenish on the server's low-water signal for this device. We top up to the target using the
|
|
161
|
+
// count the signal reports (not a blind full batch), and trust the server's "low" verdict — the
|
|
162
|
+
// client `keyPackageLowWater` only gates the proactive path below.
|
|
132
163
|
(0, react_1.useEffect)(() => {
|
|
133
164
|
if (!autoReplenish || !device)
|
|
134
165
|
return;
|
|
135
166
|
const off = socket.on("secure:key-packages-low", (signal) => {
|
|
136
167
|
if (signal.deviceId !== device.id)
|
|
137
|
-
return;
|
|
138
|
-
|
|
168
|
+
return; // device.id is the server ROW id, not the deviceId
|
|
169
|
+
setKeyPackagesAvailable(signal.available);
|
|
170
|
+
replenishToTarget(signal.available).catch(setError);
|
|
139
171
|
});
|
|
140
172
|
return off;
|
|
141
|
-
}, [autoReplenish, device, socket,
|
|
173
|
+
}, [autoReplenish, device, socket, replenishToTarget]);
|
|
174
|
+
// One-shot proactive top-up once the device is ready (covers both register and rehydrate-on-mount),
|
|
175
|
+
// so a client that missed the realtime signal while offline self-heals on next load.
|
|
176
|
+
(0, react_1.useEffect)(() => {
|
|
177
|
+
if (!autoReplenish || !device || proactiveCheckedRef.current)
|
|
178
|
+
return;
|
|
179
|
+
proactiveCheckedRef.current = true;
|
|
180
|
+
checkAndReplenish().catch(setError);
|
|
181
|
+
}, [autoReplenish, device, checkAndReplenish]);
|
|
142
182
|
return {
|
|
143
183
|
device,
|
|
144
184
|
loading,
|
|
@@ -148,6 +188,7 @@ function useSecureDevice(options = {}) {
|
|
|
148
188
|
register,
|
|
149
189
|
publishKeyPackages,
|
|
150
190
|
refreshKeyPackageCount,
|
|
191
|
+
checkAndReplenish,
|
|
151
192
|
};
|
|
152
193
|
}
|
|
153
194
|
//# sourceMappingURL=useSecureDevice.js.map
|
|
@@ -1 +1 @@
|
|
|
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,
|
|
1
|
+
{"version":3,"file":"useSecureDevice.js","sourceRoot":"","sources":["../../../src/hooks/useSecureDevice.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,24 @@
|
|
|
1
1
|
import { SecureMessageModel } from "../contract/index.js";
|
|
2
|
-
import { GroupHandle } from "@agora-sdk/secure-chat-crypto";
|
|
3
|
-
/**
|
|
2
|
+
import { GroupHandle, type SecureDecryptFailureReason } from "@agora-sdk/secure-chat-crypto";
|
|
3
|
+
/**
|
|
4
|
+
* Decryption outcome for a stored message:
|
|
5
|
+
* - `ok` — decrypted + authenticated; `plaintext` is set.
|
|
6
|
+
* - `pending` — not decryptable yet (no group handle, or the message's epoch is ahead of ours); it will
|
|
7
|
+
* be retried when the group advances. `plaintext` is null.
|
|
8
|
+
* - `rejected` — fails closed: the MLS core rejected it (replay, over-window gap, bad auth, malformed,
|
|
9
|
+
* too-old epoch). NEVER retried; `plaintext` is null and `rejectedReason` says why.
|
|
10
|
+
*/
|
|
11
|
+
export type SecureMessageStatus = "ok" | "pending" | "rejected";
|
|
12
|
+
/** A stored message paired with its decryption outcome. */
|
|
4
13
|
export interface DecryptedSecureMessage {
|
|
5
14
|
/** The raw message row from the server (still holds the base64 ciphertext). */
|
|
6
15
|
model: SecureMessageModel;
|
|
7
|
-
/** Decrypted text, or null when
|
|
16
|
+
/** Decrypted text, or null when {@link DecryptedSecureMessage.status} is `pending` or `rejected`. */
|
|
8
17
|
plaintext: string | null;
|
|
18
|
+
/** Decryption outcome — drives fail-closed handling and what the UI renders. */
|
|
19
|
+
status: SecureMessageStatus;
|
|
20
|
+
/** When `status` is `rejected`, why the MLS core refused the message. */
|
|
21
|
+
rejectedReason?: SecureDecryptFailureReason;
|
|
9
22
|
}
|
|
10
23
|
/** Options for {@link useSecureMessages}. */
|
|
11
24
|
export interface UseSecureMessagesOptions {
|
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.useSecureMessages = useSecureMessages;
|
|
10
10
|
const react_1 = require("react");
|
|
11
|
+
const secure_chat_crypto_1 = require("@agora-sdk/secure-chat-crypto");
|
|
11
12
|
const base64_js_1 = require("../util/base64.js");
|
|
13
|
+
const padding_js_1 = require("../util/padding.js");
|
|
12
14
|
const secure_chat_context_js_1 = require("../context/secure-chat-context.js");
|
|
13
15
|
/**
|
|
14
16
|
* Load, decrypt, send, and live-receive messages in one secure conversation.
|
|
@@ -28,7 +30,7 @@ const secure_chat_context_js_1 = require("../context/secure-chat-context.js");
|
|
|
28
30
|
* ```
|
|
29
31
|
*/
|
|
30
32
|
function useSecureMessages(conversationId, options = {}) {
|
|
31
|
-
const { rest, crypto, socket, repo, resolveGroup, getGroupVersion, subscribeGroupChange } = (0, secure_chat_context_js_1.useSecureChat)();
|
|
33
|
+
const { rest, crypto, socket, repo, resolveGroup, getGroupVersion, subscribeGroupChange, padding } = (0, secure_chat_context_js_1.useSecureChat)();
|
|
32
34
|
const [messages, setMessages] = (0, react_1.useState)([]);
|
|
33
35
|
const [before, setBefore] = (0, react_1.useState)(undefined);
|
|
34
36
|
const [hasMore, setHasMore] = (0, react_1.useState)(true);
|
|
@@ -95,15 +97,34 @@ function useSecureMessages(conversationId, options = {}) {
|
|
|
95
97
|
};
|
|
96
98
|
}, [options.senderDeviceId, repo]);
|
|
97
99
|
const decrypt = (0, react_1.useCallback)(async (model) => {
|
|
100
|
+
// No handle yet (still resolving) → retryable once it arrives.
|
|
98
101
|
if (!group)
|
|
99
|
-
return { model, plaintext: null };
|
|
102
|
+
return { model, plaintext: null, status: "pending" };
|
|
103
|
+
let plaintext;
|
|
100
104
|
try {
|
|
101
|
-
|
|
102
|
-
|
|
105
|
+
({ plaintext } = await crypto.decryptMessage(group, (0, base64_js_1.fromBase64)(model.ciphertext)));
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
// Classify, don't conflate. A message from an epoch we HAVEN'T reached yet is legitimately
|
|
109
|
+
// buffered (a future Commit will advance us, then this re-decrypts). Anything else that fails
|
|
110
|
+
// at an epoch we HAVE reached is a terminal rejection — the MLS core refused it (replay,
|
|
111
|
+
// over-window gap, bad auth, malformed, too-old epoch). Fail closed: never show it as text and
|
|
112
|
+
// never silently retry it forever (the old behavior masked replays/forgeries as "pending").
|
|
113
|
+
if (BigInt(model.epoch) > group.epoch) {
|
|
114
|
+
return { model, plaintext: null, status: "pending" };
|
|
115
|
+
}
|
|
116
|
+
const rejectedReason = err instanceof secure_chat_crypto_1.SecureChatDecryptError ? err.reason : "unknown";
|
|
117
|
+
return { model, plaintext: null, status: "rejected", rejectedReason };
|
|
118
|
+
}
|
|
119
|
+
// Decrypt + MLS authentication succeeded, so the bytes are from a real group member. Strip the
|
|
120
|
+
// size-bucket padding frame (see util/padding). A bad frame here is NOT a decrypt failure — it's a
|
|
121
|
+
// framing/version mismatch from an authenticated sender — so fail closed as "malformed" rather
|
|
122
|
+
// than rendering raw padded bytes as text.
|
|
123
|
+
try {
|
|
124
|
+
return { model, plaintext: (0, base64_js_1.bytesToUtf8)((0, padding_js_1.unpadPlaintext)(plaintext)), status: "ok" };
|
|
103
125
|
}
|
|
104
126
|
catch {
|
|
105
|
-
|
|
106
|
-
return { model, plaintext: null };
|
|
127
|
+
return { model, plaintext: null, status: "rejected", rejectedReason: "malformed" };
|
|
107
128
|
}
|
|
108
129
|
}, [crypto, group]);
|
|
109
130
|
const load = (0, react_1.useCallback)(async (reset) => {
|
|
@@ -145,30 +166,32 @@ function useSecureMessages(conversationId, options = {}) {
|
|
|
145
166
|
throw new Error("Cannot send: no MLS group handle for this conversation.");
|
|
146
167
|
if (!senderDeviceId)
|
|
147
168
|
throw new Error("Cannot send: senderDeviceId is required.");
|
|
148
|
-
|
|
169
|
+
// Pad the plaintext to a size bucket BEFORE encryption so the ciphertext length leaks less
|
|
170
|
+
// (the receiver strips the frame in `decrypt`). See util/padding for the framing.
|
|
171
|
+
const { ciphertext, epoch } = await crypto.encryptMessage(group, (0, padding_js_1.padPlaintext)((0, base64_js_1.utf8ToBytes)(text), padding));
|
|
149
172
|
const sent = await rest.sendMessage(conversationId, {
|
|
150
173
|
ciphertext: (0, base64_js_1.toBase64)(ciphertext),
|
|
151
174
|
epoch: epoch.toString(),
|
|
152
175
|
senderDeviceId,
|
|
153
176
|
});
|
|
154
177
|
// Optimistic: we know our own plaintext without a round-trip through decrypt.
|
|
155
|
-
setMessages((prev) => [{ model: sent, plaintext: text }, ...prev]);
|
|
156
|
-
}, [crypto, rest, conversationId, group, senderDeviceId]);
|
|
178
|
+
setMessages((prev) => [{ model: sent, plaintext: text, status: "ok" }, ...prev]);
|
|
179
|
+
}, [crypto, rest, conversationId, group, senderDeviceId, padding]);
|
|
157
180
|
(0, react_1.useEffect)(() => {
|
|
158
181
|
refresh();
|
|
159
182
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
160
183
|
}, [conversationId]);
|
|
161
|
-
//
|
|
162
|
-
//
|
|
163
|
-
//
|
|
164
|
-
//
|
|
184
|
+
// Re-decrypt buffered rows when the group handle advances. On reload the first page loads while
|
|
185
|
+
// resolveGroup is still in flight (those rows come back `pending`); a Commit/join also advances the
|
|
186
|
+
// epoch, letting previously-ahead rows decrypt. Retry ONLY `pending` rows — never `rejected` ones, so
|
|
187
|
+
// a replay/forgery the core already refused isn't retried on every epoch bump. `ok` rows are kept.
|
|
165
188
|
(0, react_1.useEffect)(() => {
|
|
166
189
|
if (!group)
|
|
167
190
|
return;
|
|
168
|
-
if (!messagesRef.current.some((m) => m.
|
|
191
|
+
if (!messagesRef.current.some((m) => m.status === "pending"))
|
|
169
192
|
return;
|
|
170
193
|
let alive = true;
|
|
171
|
-
Promise.all(messagesRef.current.map((m) => (m.
|
|
194
|
+
Promise.all(messagesRef.current.map((m) => (m.status === "pending" ? decrypt(m.model) : Promise.resolve(m)))).then((next) => {
|
|
172
195
|
if (alive)
|
|
173
196
|
setMessages(next);
|
|
174
197
|
});
|
|
@@ -1 +1 @@
|
|
|
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;;
|
|
1
|
+
{"version":3,"file":"useSecureMessages.js","sourceRoot":"","sources":["../../../src/hooks/useSecureMessages.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type SafetyNumber } from "../util/safety-number.js";
|
|
2
|
+
/** The state and actions returned by {@link useSecureSafetyNumber}. */
|
|
3
|
+
export interface UseSecureSafetyNumberValues {
|
|
4
|
+
/** The derived safety number, or `null` until ready / when the conversation is not a resolvable DM. */
|
|
5
|
+
safetyNumber: SafetyNumber | null;
|
|
6
|
+
/** True while the number is being (re)computed. */
|
|
7
|
+
loading: boolean;
|
|
8
|
+
/** The last error from resolving the roster or computing, or `null`. */
|
|
9
|
+
error: unknown;
|
|
10
|
+
/** Force a recompute (e.g. after the user re-verifies). */
|
|
11
|
+
refresh: () => void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Compute the out-of-band key-verification safety number for a direct-message conversation.
|
|
15
|
+
*
|
|
16
|
+
* Resolves the conversation's MLS group, reads its two members' public signature keys, and derives a
|
|
17
|
+
* symmetric {@link SafetyNumber}. Recomputes when the conversation's group handle advances (e.g. a
|
|
18
|
+
* processed Commit changes the roster). Returns `safetyNumber: null` when the group is unresolved or is
|
|
19
|
+
* not a 2-member DM.
|
|
20
|
+
*
|
|
21
|
+
* @param conversationId - The conversation to compute the safety number for.
|
|
22
|
+
* @returns {@link UseSecureSafetyNumberValues}.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```tsx
|
|
26
|
+
* const { safetyNumber } = useSecureSafetyNumber(conversationId);
|
|
27
|
+
* // render safetyNumber?.groups for the user to compare with their peer
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export declare function useSecureSafetyNumber(conversationId: string): UseSecureSafetyNumberValues;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// useSecureSafetyNumber — derive the key-verification safety number for a DM conversation.
|
|
3
|
+
//
|
|
4
|
+
// Where it sits in the blind-server model: the server is untrusted and could swap a peer's KeyPackage
|
|
5
|
+
// (active MITM on a TOFU system). This hook reads the two devices' PUBLIC identity keys from the local
|
|
6
|
+
// MLS roster (crypto.exportGroupIdentities) and computes a symmetric, human-comparable safety number
|
|
7
|
+
// (see util/safety-number) the two users confirm out-of-band. Headless: the styled UI lives in the app
|
|
8
|
+
// / demo. DM-focused — for anything other than a 2-member group it returns null rather than guess.
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.useSecureSafetyNumber = useSecureSafetyNumber;
|
|
11
|
+
const react_1 = require("react");
|
|
12
|
+
const safety_number_js_1 = require("../util/safety-number.js");
|
|
13
|
+
const secure_chat_context_js_1 = require("../context/secure-chat-context.js");
|
|
14
|
+
/**
|
|
15
|
+
* Compute the out-of-band key-verification safety number for a direct-message conversation.
|
|
16
|
+
*
|
|
17
|
+
* Resolves the conversation's MLS group, reads its two members' public signature keys, and derives a
|
|
18
|
+
* symmetric {@link SafetyNumber}. Recomputes when the conversation's group handle advances (e.g. a
|
|
19
|
+
* processed Commit changes the roster). Returns `safetyNumber: null` when the group is unresolved or is
|
|
20
|
+
* not a 2-member DM.
|
|
21
|
+
*
|
|
22
|
+
* @param conversationId - The conversation to compute the safety number for.
|
|
23
|
+
* @returns {@link UseSecureSafetyNumberValues}.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```tsx
|
|
27
|
+
* const { safetyNumber } = useSecureSafetyNumber(conversationId);
|
|
28
|
+
* // render safetyNumber?.groups for the user to compare with their peer
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
function useSecureSafetyNumber(conversationId) {
|
|
32
|
+
const { crypto, repo, resolveGroup, getGroupVersion, subscribeGroupChange } = (0, secure_chat_context_js_1.useSecureChat)();
|
|
33
|
+
const [safetyNumber, setSafetyNumber] = (0, react_1.useState)(null);
|
|
34
|
+
const [loading, setLoading] = (0, react_1.useState)(true);
|
|
35
|
+
const [error, setError] = (0, react_1.useState)(null);
|
|
36
|
+
const [tick, setTick] = (0, react_1.useState)(0);
|
|
37
|
+
// Recompute when this conversation's group handle advances (a roster change), without diffing handles.
|
|
38
|
+
const [groupVersion, setGroupVersion] = (0, react_1.useState)(0);
|
|
39
|
+
(0, react_1.useEffect)(() => {
|
|
40
|
+
return subscribeGroupChange(() => setGroupVersion(getGroupVersion(conversationId)));
|
|
41
|
+
}, [subscribeGroupChange, getGroupVersion, conversationId]);
|
|
42
|
+
const refresh = (0, react_1.useCallback)(() => setTick((t) => t + 1), []);
|
|
43
|
+
// Guards the per-run state writes against an out-of-date async resolve (conversation switch / unmount).
|
|
44
|
+
const runIdRef = (0, react_1.useRef)(0);
|
|
45
|
+
(0, react_1.useEffect)(() => {
|
|
46
|
+
const runId = ++runIdRef.current;
|
|
47
|
+
setLoading(true);
|
|
48
|
+
setError(null);
|
|
49
|
+
(async () => {
|
|
50
|
+
const group = await resolveGroup(conversationId);
|
|
51
|
+
if (!group)
|
|
52
|
+
return null;
|
|
53
|
+
const [persisted, members] = await Promise.all([
|
|
54
|
+
repo.loadDevice(),
|
|
55
|
+
crypto.exportGroupIdentities(group),
|
|
56
|
+
]);
|
|
57
|
+
// DM only: need exactly the local device + one peer. Anything else → no safety number.
|
|
58
|
+
if (members.length !== 2 || !persisted)
|
|
59
|
+
return null;
|
|
60
|
+
const local = members.find((m) => m.deviceId === persisted.deviceId);
|
|
61
|
+
const remote = members.find((m) => m.deviceId !== persisted.deviceId);
|
|
62
|
+
if (!local || !remote)
|
|
63
|
+
return null;
|
|
64
|
+
return (0, safety_number_js_1.computeSafetyNumber)(local, remote);
|
|
65
|
+
})()
|
|
66
|
+
.then((sn) => {
|
|
67
|
+
if (runIdRef.current === runId) {
|
|
68
|
+
setSafetyNumber(sn);
|
|
69
|
+
setLoading(false);
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
.catch((err) => {
|
|
73
|
+
if (runIdRef.current === runId) {
|
|
74
|
+
setError(err);
|
|
75
|
+
setSafetyNumber(null);
|
|
76
|
+
setLoading(false);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}, [crypto, repo, resolveGroup, conversationId, groupVersion, tick]);
|
|
80
|
+
return { safetyNumber, loading, error, refresh };
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=useSecureSafetyNumber.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSecureSafetyNumber.js","sourceRoot":"","sources":["../../../src/hooks/useSecureSafetyNumber.tsx"],"names":[],"mappings":"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"}
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -8,14 +8,25 @@ export { useSecureMessages } from "./hooks/useSecureMessages.js";
|
|
|
8
8
|
export type { UseSecureMessagesOptions, UseSecureMessagesValues, DecryptedSecureMessage, } from "./hooks/useSecureMessages.js";
|
|
9
9
|
export { useSecureHandshakes } from "./hooks/useSecureHandshakes.js";
|
|
10
10
|
export type { UseSecureHandshakesOptions, UseSecureHandshakesValues, } from "./hooks/useSecureHandshakes.js";
|
|
11
|
+
export { useSecureBackup } from "./hooks/useSecureBackup.js";
|
|
12
|
+
export type { UseSecureBackupValues } from "./hooks/useSecureBackup.js";
|
|
13
|
+
export { useSecureSafetyNumber } from "./hooks/useSecureSafetyNumber.js";
|
|
14
|
+
export type { UseSecureSafetyNumberValues } from "./hooks/useSecureSafetyNumber.js";
|
|
15
|
+
export { estimatePassphraseStrength } from "./backup/passphrase-strength.js";
|
|
16
|
+
export type { PassphraseStrength } from "./backup/passphrase-strength.js";
|
|
11
17
|
export { SecureChatRestClient } from "./transport/rest.js";
|
|
12
18
|
export type { SecureChatRestConfig } from "./transport/rest.js";
|
|
13
19
|
export { SecureChatSocketClient } from "./transport/socket.js";
|
|
14
20
|
export type { SecureSocket, SecureServerEvents, SecureClientEvents, SecureChatSocketConfig, } from "./transport/socket.js";
|
|
15
|
-
export type { SecureChatCrypto, DeviceIdentity, KeyPackageBundle, GroupHandle, TargetedWelcome, CommitResult, PassphraseBackup, } from "@agora-sdk/secure-chat-crypto";
|
|
21
|
+
export type { SecureChatCrypto, DeviceIdentity, KeyPackageBundle, GroupHandle, GroupMemberIdentity, TargetedWelcome, CommitResult, PassphraseBackup, SecureDecryptFailureReason, } from "@agora-sdk/secure-chat-crypto";
|
|
22
|
+
export { SecureChatDecryptError } from "@agora-sdk/secure-chat-crypto";
|
|
16
23
|
export type * from "./contract/index.js";
|
|
17
24
|
export type { SecureChatStore } from "./persistence/store.js";
|
|
18
25
|
export { MemoryStore } from "./persistence/memory-store.js";
|
|
19
26
|
export { SecureChatRepository } from "./persistence/repository.js";
|
|
20
27
|
export type { PersistedDevice } from "./persistence/repository.js";
|
|
21
28
|
export { toBase64, fromBase64, utf8ToBytes, bytesToUtf8 } from "./util/base64.js";
|
|
29
|
+
export { padPlaintext, unpadPlaintext, nextBucket } from "./util/padding.js";
|
|
30
|
+
export type { PaddingPolicy } from "./util/padding.js";
|
|
31
|
+
export { computeSafetyNumber } from "./util/safety-number.js";
|
|
32
|
+
export type { SafetyNumber } from "./util/safety-number.js";
|
package/dist/cjs/index.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
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.SecureChatRepository = exports.MemoryStore = exports.SecureChatSocketClient = exports.SecureChatRestClient = exports.useSecureHandshakes = exports.useSecureMessages = exports.useSecureConversations = exports.useSecureDevice = exports.useSecureChat = exports.SecureChatProvider = void 0;
|
|
8
|
+
exports.computeSafetyNumber = exports.nextBucket = exports.unpadPlaintext = exports.padPlaintext = exports.bytesToUtf8 = exports.utf8ToBytes = exports.fromBase64 = exports.toBase64 = exports.SecureChatRepository = exports.MemoryStore = exports.SecureChatDecryptError = exports.SecureChatSocketClient = exports.SecureChatRestClient = exports.estimatePassphraseStrength = exports.useSecureSafetyNumber = exports.useSecureBackup = exports.useSecureHandshakes = exports.useSecureMessages = exports.useSecureConversations = exports.useSecureDevice = exports.useSecureChat = exports.SecureChatProvider = void 0;
|
|
9
9
|
// ── context / provider ──────────────────────────────────────────────────────
|
|
10
10
|
var secure_chat_context_js_1 = require("./context/secure-chat-context.js");
|
|
11
11
|
Object.defineProperty(exports, "SecureChatProvider", { enumerable: true, get: function () { return secure_chat_context_js_1.SecureChatProvider; } });
|
|
@@ -19,11 +19,20 @@ var useSecureMessages_js_1 = require("./hooks/useSecureMessages.js");
|
|
|
19
19
|
Object.defineProperty(exports, "useSecureMessages", { enumerable: true, get: function () { return useSecureMessages_js_1.useSecureMessages; } });
|
|
20
20
|
var useSecureHandshakes_js_1 = require("./hooks/useSecureHandshakes.js");
|
|
21
21
|
Object.defineProperty(exports, "useSecureHandshakes", { enumerable: true, get: function () { return useSecureHandshakes_js_1.useSecureHandshakes; } });
|
|
22
|
+
var useSecureBackup_js_1 = require("./hooks/useSecureBackup.js");
|
|
23
|
+
Object.defineProperty(exports, "useSecureBackup", { enumerable: true, get: function () { return useSecureBackup_js_1.useSecureBackup; } });
|
|
24
|
+
var useSecureSafetyNumber_js_1 = require("./hooks/useSecureSafetyNumber.js");
|
|
25
|
+
Object.defineProperty(exports, "useSecureSafetyNumber", { enumerable: true, get: function () { return useSecureSafetyNumber_js_1.useSecureSafetyNumber; } });
|
|
26
|
+
// ── backup (passphrase strength meter) ────────────────────────────────────────
|
|
27
|
+
var passphrase_strength_js_1 = require("./backup/passphrase-strength.js");
|
|
28
|
+
Object.defineProperty(exports, "estimatePassphraseStrength", { enumerable: true, get: function () { return passphrase_strength_js_1.estimatePassphraseStrength; } });
|
|
22
29
|
// ── transport (for advanced / non-React use) ─────────────────────────────────
|
|
23
30
|
var rest_js_1 = require("./transport/rest.js");
|
|
24
31
|
Object.defineProperty(exports, "SecureChatRestClient", { enumerable: true, get: function () { return rest_js_1.SecureChatRestClient; } });
|
|
25
32
|
var socket_js_1 = require("./transport/socket.js");
|
|
26
33
|
Object.defineProperty(exports, "SecureChatSocketClient", { enumerable: true, get: function () { return socket_js_1.SecureChatSocketClient; } });
|
|
34
|
+
var secure_chat_crypto_1 = require("@agora-sdk/secure-chat-crypto");
|
|
35
|
+
Object.defineProperty(exports, "SecureChatDecryptError", { enumerable: true, get: function () { return secure_chat_crypto_1.SecureChatDecryptError; } });
|
|
27
36
|
var memory_store_js_1 = require("./persistence/memory-store.js");
|
|
28
37
|
Object.defineProperty(exports, "MemoryStore", { enumerable: true, get: function () { return memory_store_js_1.MemoryStore; } });
|
|
29
38
|
var repository_js_1 = require("./persistence/repository.js");
|
|
@@ -34,4 +43,10 @@ Object.defineProperty(exports, "toBase64", { enumerable: true, get: function ()
|
|
|
34
43
|
Object.defineProperty(exports, "fromBase64", { enumerable: true, get: function () { return base64_js_1.fromBase64; } });
|
|
35
44
|
Object.defineProperty(exports, "utf8ToBytes", { enumerable: true, get: function () { return base64_js_1.utf8ToBytes; } });
|
|
36
45
|
Object.defineProperty(exports, "bytesToUtf8", { enumerable: true, get: function () { return base64_js_1.bytesToUtf8; } });
|
|
46
|
+
var padding_js_1 = require("./util/padding.js");
|
|
47
|
+
Object.defineProperty(exports, "padPlaintext", { enumerable: true, get: function () { return padding_js_1.padPlaintext; } });
|
|
48
|
+
Object.defineProperty(exports, "unpadPlaintext", { enumerable: true, get: function () { return padding_js_1.unpadPlaintext; } });
|
|
49
|
+
Object.defineProperty(exports, "nextBucket", { enumerable: true, get: function () { return padding_js_1.nextBucket; } });
|
|
50
|
+
var safety_number_js_1 = require("./util/safety-number.js");
|
|
51
|
+
Object.defineProperty(exports, "computeSafetyNumber", { enumerable: true, get: function () { return safety_number_js_1.computeSafetyNumber; } });
|
|
37
52
|
//# sourceMappingURL=index.js.map
|
package/dist/cjs/index.js.map
CHANGED
|
@@ -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,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;AAM1B,yEAAqE;AAA5D,6HAAA,mBAAmB,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;AAM1B,yEAAqE;AAA5D,6HAAA,mBAAmB,OAAA;AAK5B,iEAA6D;AAApD,qHAAA,eAAe,OAAA;AAExB,6EAAyE;AAAhE,iIAAA,qBAAqB,OAAA;AAG9B,iFAAiF;AACjF,0EAA6E;AAApE,oIAAA,0BAA0B,OAAA;AAGnC,gFAAgF;AAChF,+CAA2D;AAAlD,+GAAA,oBAAoB,OAAA;AAE7B,mDAA+D;AAAtD,mHAAA,sBAAsB,OAAA;AAoB/B,oEAAuE;AAA9D,4HAAA,sBAAsB,OAAA;AAO/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;AACvD,gDAA6E;AAApE,0GAAA,YAAY,OAAA;AAAE,4GAAA,cAAc,OAAA;AAAE,wGAAA,UAAU,OAAA;AAEjD,4DAA8D;AAArD,uHAAA,mBAAmB,OAAA"}
|
|
@@ -26,10 +26,22 @@ export interface SecureServerEvents {
|
|
|
26
26
|
userId: string;
|
|
27
27
|
}) => void;
|
|
28
28
|
}
|
|
29
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* Client → server events.
|
|
31
|
+
*
|
|
32
|
+
* @remarks
|
|
33
|
+
* Payloads are **objects**, not bare ids — the server destructures `{ conversationId }` /
|
|
34
|
+
* `{ deviceId }` off the first argument (agora-server `realtime/secure-socket.ts`). Emitting a bare
|
|
35
|
+
* string lands as `undefined` after destructuring (and, on a `null` payload, throws server-side), so
|
|
36
|
+
* the join silently fails. Keep these shapes in lockstep with the server's `SecureClientToServerEvents`.
|
|
37
|
+
*/
|
|
30
38
|
export interface SecureClientEvents {
|
|
31
|
-
"join:secure-conversation": (
|
|
32
|
-
|
|
39
|
+
"join:secure-conversation": (payload: {
|
|
40
|
+
conversationId: string;
|
|
41
|
+
}) => void;
|
|
42
|
+
"join:secure-device": (payload: {
|
|
43
|
+
deviceId: string;
|
|
44
|
+
}) => void;
|
|
33
45
|
}
|
|
34
46
|
/** A socket.io `Socket` typed with the secure-chat event maps in both directions. */
|
|
35
47
|
export type SecureSocket = Socket<SecureServerEvents, SecureClientEvents>;
|
|
@@ -46,11 +46,13 @@ class SecureChatSocketClient {
|
|
|
46
46
|
}
|
|
47
47
|
/** Join a conversation room (membership-gated server-side) to receive its broadcasts. */
|
|
48
48
|
joinConversation(conversationId) {
|
|
49
|
-
|
|
49
|
+
// Object payload — the server destructures `{ conversationId }`; a bare string would arrive as
|
|
50
|
+
// `undefined` and the join would silently no-op (see SecureClientEvents).
|
|
51
|
+
this.connect().emit("join:secure-conversation", { conversationId });
|
|
50
52
|
}
|
|
51
53
|
/** Explicitly join a device room (ownership-verified). Owned devices auto-join on connect. */
|
|
52
54
|
joinDevice(deviceId) {
|
|
53
|
-
this.connect().emit("join:secure-device", deviceId);
|
|
55
|
+
this.connect().emit("join:secure-device", { deviceId });
|
|
54
56
|
}
|
|
55
57
|
/**
|
|
56
58
|
* Subscribe to a server → client event, auto-connecting if needed.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socket.js","sourceRoot":"","sources":["../../../src/transport/socket.ts"],"names":[],"mappings":";AAAA,8FAA8F;AAC9F,EAAE;AACF,iGAAiG;AACjG,kGAAkG;AAClG,8FAA8F;AAC9F,kCAAkC;;;AAElC,uDAA8C;
|
|
1
|
+
{"version":3,"file":"socket.js","sourceRoot":"","sources":["../../../src/transport/socket.ts"],"names":[],"mappings":";AAAA,8FAA8F;AAC9F,EAAE;AACF,iGAAiG;AACjG,kGAAkG;AAClG,8FAA8F;AAC9F,kCAAkC;;;AAElC,uDAA8C;AA6C9C;;;;GAIG;AACH,MAAa,sBAAsB;IAGjC,YAA6B,MAA8B;QAA9B,WAAM,GAAN,MAAM,CAAwB;QAFnD,WAAM,GAAwB,IAAI,CAAC;IAEmB,CAAC;IAE/D;;;;OAIG;IACH,OAAO;QACL,IAAI,IAAI,CAAC,MAAM,EAAE,SAAS;YAAE,OAAO,IAAI,CAAC,MAAM,CAAC;QAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC7D,IAAI,CAAC,MAAM,GAAG,IAAA,qBAAE,EAAC,GAAG,MAAM,SAAS,EAAE;YACnC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,EAAE;YAC7C,KAAK,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE;YAC3C,UAAU,EAAE,CAAC,WAAW,CAAC;YACzB,WAAW,EAAE,IAAI;SAClB,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,kGAAkG;IAClG,UAAU;QACR,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC;QAC1B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;IACrB,CAAC;IAED,kGAAkG;IAClG,IAAI,GAAG;QACL,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,yFAAyF;IACzF,gBAAgB,CAAC,cAAsB;QACrC,+FAA+F;QAC/F,0EAA0E;QAC1E,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,0BAA0B,EAAE,EAAE,cAAc,EAAE,CAAC,CAAC;IACtE,CAAC;IAED,8FAA8F;IAC9F,UAAU,CAAC,QAAgB;QACzB,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED;;;;;;OAMG;IACH,EAAE,CAAqC,KAAQ,EAAE,OAA8B;QAC7E,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QACzB,4FAA4F;QAC5F,CAAC,CAAC,EAAE,CAAC,KAAc,EAAE,OAAgB,CAAC,CAAC;QACvC,OAAO,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,KAAc,EAAE,OAAgB,CAAC,CAAC;IACvD,CAAC;CACF;AA1DD,wDA0DC"}
|