@antzsoft/chat-core 1.1.4 → 1.1.5
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/chunk-3QYXS3IW.js +415 -0
- package/dist/chunk-3QYXS3IW.js.map +1 -0
- package/dist/index.cjs +32 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -619
- package/dist/index.d.ts +3 -619
- package/dist/index.js +31 -385
- package/dist/index.js.map +1 -1
- package/dist/internal-CACK6gGJ.d.cts +624 -0
- package/dist/internal-CACK6gGJ.d.ts +624 -0
- package/dist/internal.cjs +209 -0
- package/dist/internal.cjs.map +1 -0
- package/dist/internal.d.cts +1 -0
- package/dist/internal.d.ts +1 -0
- package/dist/internal.js +7 -0
- package/dist/internal.js.map +1 -0
- package/package.json +6 -1
package/dist/index.js
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
clearTransitSession,
|
|
3
|
+
configureTransit,
|
|
4
|
+
decryptPayload,
|
|
5
|
+
encryptPayload,
|
|
6
|
+
getApiClient,
|
|
7
|
+
getCompressionStrategy,
|
|
8
|
+
getSessionKey,
|
|
9
|
+
initApiClient,
|
|
10
|
+
isTransitEnabled,
|
|
11
|
+
isTransitEnvelope,
|
|
12
|
+
setApiClientInstance,
|
|
13
|
+
setTransitSession,
|
|
14
|
+
storageApi,
|
|
15
|
+
uploadBatch
|
|
16
|
+
} from "./chunk-3QYXS3IW.js";
|
|
1
17
|
import {
|
|
2
18
|
useChatStore
|
|
3
19
|
} from "./chunk-TB52RCSF.js";
|
|
@@ -61,299 +77,6 @@ function resolveConfig(config) {
|
|
|
61
77
|
};
|
|
62
78
|
}
|
|
63
79
|
|
|
64
|
-
// src/compression/compress.ts
|
|
65
|
-
var GZIP_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
66
|
-
"text/plain",
|
|
67
|
-
"text/csv",
|
|
68
|
-
"text/markdown",
|
|
69
|
-
"text/x-markdown",
|
|
70
|
-
"text/xml",
|
|
71
|
-
"application/xml",
|
|
72
|
-
"text/yaml",
|
|
73
|
-
"text/x-yaml",
|
|
74
|
-
"application/x-yaml",
|
|
75
|
-
"application/rtf",
|
|
76
|
-
"text/rtf",
|
|
77
|
-
"application/json",
|
|
78
|
-
"image/svg+xml"
|
|
79
|
-
]);
|
|
80
|
-
var IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
81
|
-
"image/jpeg",
|
|
82
|
-
"image/png",
|
|
83
|
-
"image/gif",
|
|
84
|
-
"image/webp",
|
|
85
|
-
"image/bmp",
|
|
86
|
-
"image/tiff"
|
|
87
|
-
]);
|
|
88
|
-
var SKIP_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
89
|
-
"video/mp4",
|
|
90
|
-
"video/webm",
|
|
91
|
-
"video/quicktime",
|
|
92
|
-
"audio/mpeg",
|
|
93
|
-
"audio/wav",
|
|
94
|
-
"audio/ogg",
|
|
95
|
-
"audio/webm",
|
|
96
|
-
"audio/mp4",
|
|
97
|
-
"application/zip",
|
|
98
|
-
"application/pdf",
|
|
99
|
-
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
100
|
-
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
101
|
-
"application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
|
102
|
-
]);
|
|
103
|
-
function getCompressionStrategy(mimeType, config) {
|
|
104
|
-
if (SKIP_MIME_TYPES.has(mimeType)) return "skip";
|
|
105
|
-
if (IMAGE_MIME_TYPES.has(mimeType)) return "image";
|
|
106
|
-
if (config.compressDocuments && GZIP_MIME_TYPES.has(mimeType)) return "gzip";
|
|
107
|
-
return "skip";
|
|
108
|
-
}
|
|
109
|
-
async function compressFile(file, platformCompressFn, config) {
|
|
110
|
-
const noop = {
|
|
111
|
-
...file,
|
|
112
|
-
originalSize: file.size,
|
|
113
|
-
compressed: false,
|
|
114
|
-
compressionAlgorithm: "none"
|
|
115
|
-
};
|
|
116
|
-
if (!config.enabled || !platformCompressFn) return noop;
|
|
117
|
-
const strategy = getCompressionStrategy(file.type, config);
|
|
118
|
-
if (strategy === "skip") return noop;
|
|
119
|
-
try {
|
|
120
|
-
return await platformCompressFn(file, config);
|
|
121
|
-
} catch {
|
|
122
|
-
return noop;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// src/api/client.ts
|
|
127
|
-
import axios from "axios";
|
|
128
|
-
|
|
129
|
-
// src/crypto/transit.ts
|
|
130
|
-
async function encryptPayload(data, sessionKey) {
|
|
131
|
-
const iv = globalThis.crypto.getRandomValues(new Uint8Array(12));
|
|
132
|
-
const plaintext = new TextEncoder().encode(JSON.stringify(data));
|
|
133
|
-
const encrypted = await globalThis.crypto.subtle.encrypt(
|
|
134
|
-
{ name: "AES-GCM", iv },
|
|
135
|
-
sessionKey,
|
|
136
|
-
plaintext
|
|
137
|
-
);
|
|
138
|
-
const ct = encrypted.slice(0, encrypted.byteLength - 16);
|
|
139
|
-
const tag = encrypted.slice(encrypted.byteLength - 16);
|
|
140
|
-
return {
|
|
141
|
-
v: 1,
|
|
142
|
-
iv: bufToB64(iv),
|
|
143
|
-
tag: bufToB64(tag),
|
|
144
|
-
ct: bufToB64(ct)
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
async function decryptPayload(envelope, sessionKey) {
|
|
148
|
-
const iv = b64ToBuf(envelope.iv);
|
|
149
|
-
const tag = b64ToBuf(envelope.tag);
|
|
150
|
-
const ct = b64ToBuf(envelope.ct);
|
|
151
|
-
const combined = new Uint8Array(ct.byteLength + tag.byteLength);
|
|
152
|
-
combined.set(new Uint8Array(ct), 0);
|
|
153
|
-
combined.set(new Uint8Array(tag), ct.byteLength);
|
|
154
|
-
const decrypted = await globalThis.crypto.subtle.decrypt(
|
|
155
|
-
{ name: "AES-GCM", iv: new Uint8Array(iv) },
|
|
156
|
-
sessionKey,
|
|
157
|
-
combined
|
|
158
|
-
);
|
|
159
|
-
return JSON.parse(new TextDecoder().decode(decrypted));
|
|
160
|
-
}
|
|
161
|
-
function isTransitEnvelope(v) {
|
|
162
|
-
return typeof v === "object" && v !== null && v.v === 1 && typeof v.iv === "string" && typeof v.tag === "string" && typeof v.ct === "string";
|
|
163
|
-
}
|
|
164
|
-
function bufToB64(buf) {
|
|
165
|
-
const bytes = buf instanceof Uint8Array ? buf : new Uint8Array(buf);
|
|
166
|
-
let str = "";
|
|
167
|
-
bytes.forEach((b) => {
|
|
168
|
-
str += String.fromCharCode(b);
|
|
169
|
-
});
|
|
170
|
-
return btoa(str);
|
|
171
|
-
}
|
|
172
|
-
function b64ToBuf(b64) {
|
|
173
|
-
const bin = atob(b64);
|
|
174
|
-
const buf = new Uint8Array(bin.length);
|
|
175
|
-
for (let i = 0; i < bin.length; i++) buf[i] = bin.charCodeAt(i);
|
|
176
|
-
return buf.buffer;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// src/crypto/session.ts
|
|
180
|
-
var _KEY = /* @__PURE__ */ Symbol.for("__antz_chat_transit__");
|
|
181
|
-
function getState() {
|
|
182
|
-
const g = globalThis;
|
|
183
|
-
if (!g[_KEY]) {
|
|
184
|
-
g[_KEY] = {
|
|
185
|
-
session: null,
|
|
186
|
-
sessionEverEstablished: false,
|
|
187
|
-
readyResolve: null,
|
|
188
|
-
readyPromise: null,
|
|
189
|
-
transitConfigured: null
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
return g[_KEY];
|
|
193
|
-
}
|
|
194
|
-
function configureTransit(enabled) {
|
|
195
|
-
const s = getState();
|
|
196
|
-
s.transitConfigured = enabled;
|
|
197
|
-
if (!enabled) {
|
|
198
|
-
s.readyResolve?.();
|
|
199
|
-
s.readyResolve = null;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
function waitForTransitReady() {
|
|
203
|
-
const s = getState();
|
|
204
|
-
if (!s.transitConfigured) return Promise.resolve();
|
|
205
|
-
if (s.session) return Promise.resolve();
|
|
206
|
-
if (s.sessionEverEstablished) return Promise.resolve();
|
|
207
|
-
if (!s.readyPromise) {
|
|
208
|
-
s.readyPromise = new Promise((resolve) => {
|
|
209
|
-
s.readyResolve = resolve;
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
return s.readyPromise;
|
|
213
|
-
}
|
|
214
|
-
function setTransitSession(session) {
|
|
215
|
-
const s = getState();
|
|
216
|
-
s.session = session;
|
|
217
|
-
s.sessionEverEstablished = true;
|
|
218
|
-
s.readyResolve?.();
|
|
219
|
-
s.readyResolve = null;
|
|
220
|
-
}
|
|
221
|
-
function clearTransitSession() {
|
|
222
|
-
const s = getState();
|
|
223
|
-
s.session = null;
|
|
224
|
-
s.readyPromise = null;
|
|
225
|
-
s.readyResolve = null;
|
|
226
|
-
}
|
|
227
|
-
function isTransitEnabled() {
|
|
228
|
-
return getState().session?.enabled === true;
|
|
229
|
-
}
|
|
230
|
-
function getSessionKey() {
|
|
231
|
-
return getState().session?.sessionKey ?? null;
|
|
232
|
-
}
|
|
233
|
-
function getSessionId() {
|
|
234
|
-
return getState().session?.sessionId ?? null;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// src/api/client.ts
|
|
238
|
-
var _tokenStore = null;
|
|
239
|
-
var _config = null;
|
|
240
|
-
var _avatarSent = false;
|
|
241
|
-
function initApiClient(config, tokenStore) {
|
|
242
|
-
_config = config;
|
|
243
|
-
_tokenStore = tokenStore;
|
|
244
|
-
_avatarSent = false;
|
|
245
|
-
const client = axios.create({
|
|
246
|
-
baseURL: config.apiUrl,
|
|
247
|
-
headers: { "Content-Type": "application/json" }
|
|
248
|
-
});
|
|
249
|
-
configureTransit(config.transitEncryption);
|
|
250
|
-
client.interceptors.request.use(async (req) => {
|
|
251
|
-
const token = _tokenStore?.getAccessToken();
|
|
252
|
-
if (token) req.headers["Authorization"] = `Bearer ${token}`;
|
|
253
|
-
if (_config?.userId) req.headers["x-user-id"] = _config.userId;
|
|
254
|
-
if (_config?.tenantId) req.headers["X-Tenant-ID"] = _config.tenantId;
|
|
255
|
-
if (token && !_avatarSent && _config?.avatar) {
|
|
256
|
-
if (_config.avatar.base64) req.headers["x-avatar-base64"] = _config.avatar.base64;
|
|
257
|
-
else if (_config.avatar.url) req.headers["x-avatar-url"] = _config.avatar.url;
|
|
258
|
-
_avatarSent = true;
|
|
259
|
-
}
|
|
260
|
-
await waitForTransitReady();
|
|
261
|
-
if (isTransitEnabled()) {
|
|
262
|
-
const sessionId = getSessionId();
|
|
263
|
-
const key = getSessionKey();
|
|
264
|
-
if (sessionId && key) {
|
|
265
|
-
req.headers["x-transit-session"] = sessionId;
|
|
266
|
-
if (req.data !== void 0 && req.data !== null) {
|
|
267
|
-
const envelope = await encryptPayload(req.data, key);
|
|
268
|
-
req.data = envelope;
|
|
269
|
-
req.headers["x-transit-encrypted"] = "1";
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
return req;
|
|
274
|
-
});
|
|
275
|
-
let isRefreshing = false;
|
|
276
|
-
let refreshQueue = [];
|
|
277
|
-
client.interceptors.response.use(
|
|
278
|
-
async (response) => {
|
|
279
|
-
if (isTransitEnabled()) {
|
|
280
|
-
const key = getSessionKey();
|
|
281
|
-
if (key) {
|
|
282
|
-
if (isTransitEnvelope(response.data)) {
|
|
283
|
-
response.data = await decryptPayload(response.data, key);
|
|
284
|
-
} else if (response.data?.data && isTransitEnvelope(response.data.data)) {
|
|
285
|
-
response.data.data = await decryptPayload(response.data.data, key);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
if (response.data && typeof response.data === "object" && "success" in response.data && "data" in response.data) {
|
|
290
|
-
response.data = response.data.data;
|
|
291
|
-
}
|
|
292
|
-
return response;
|
|
293
|
-
},
|
|
294
|
-
async (error) => {
|
|
295
|
-
if (isTransitEnabled() && error.response?.data) {
|
|
296
|
-
const key = getSessionKey();
|
|
297
|
-
if (key) {
|
|
298
|
-
try {
|
|
299
|
-
if (isTransitEnvelope(error.response.data)) {
|
|
300
|
-
error.response.data = await decryptPayload(error.response.data, key);
|
|
301
|
-
} else if (error.response.data?.data && isTransitEnvelope(error.response.data.data)) {
|
|
302
|
-
error.response.data.data = await decryptPayload(error.response.data.data, key);
|
|
303
|
-
}
|
|
304
|
-
} catch {
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
const original = error.config;
|
|
309
|
-
if (error.response?.status === 401 && !original._retry) {
|
|
310
|
-
const refreshToken = _tokenStore?.getRefreshToken();
|
|
311
|
-
if (!refreshToken) {
|
|
312
|
-
_tokenStore?.clearTokens();
|
|
313
|
-
return Promise.reject(error);
|
|
314
|
-
}
|
|
315
|
-
if (isRefreshing) {
|
|
316
|
-
return new Promise((resolve) => {
|
|
317
|
-
refreshQueue.push((newToken) => {
|
|
318
|
-
original.headers["Authorization"] = `Bearer ${newToken}`;
|
|
319
|
-
resolve(client(original));
|
|
320
|
-
});
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
original._retry = true;
|
|
324
|
-
isRefreshing = true;
|
|
325
|
-
try {
|
|
326
|
-
const { data } = await axios.post(
|
|
327
|
-
`${_config.apiUrl}/auth/refresh`,
|
|
328
|
-
{ refreshToken }
|
|
329
|
-
);
|
|
330
|
-
const tokens = data.data ?? data;
|
|
331
|
-
_tokenStore?.setTokens(tokens);
|
|
332
|
-
refreshQueue.forEach((cb) => cb(tokens.accessToken));
|
|
333
|
-
refreshQueue = [];
|
|
334
|
-
original.headers["Authorization"] = `Bearer ${tokens.accessToken}`;
|
|
335
|
-
return client(original);
|
|
336
|
-
} catch {
|
|
337
|
-
_tokenStore?.clearTokens();
|
|
338
|
-
return Promise.reject(error);
|
|
339
|
-
} finally {
|
|
340
|
-
isRefreshing = false;
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
return Promise.reject(error);
|
|
344
|
-
}
|
|
345
|
-
);
|
|
346
|
-
return client;
|
|
347
|
-
}
|
|
348
|
-
var _instance = null;
|
|
349
|
-
function setApiClientInstance(instance) {
|
|
350
|
-
_instance = instance;
|
|
351
|
-
}
|
|
352
|
-
function getApiClient() {
|
|
353
|
-
if (!_instance) throw new Error("[AntzChat] API client not initialized. Call initApiClient first.");
|
|
354
|
-
return _instance;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
80
|
// src/api/auth.ts
|
|
358
81
|
var authApi = {
|
|
359
82
|
async login(credentials) {
|
|
@@ -524,7 +247,8 @@ function normalizeLastMessage(lastMsg) {
|
|
|
524
247
|
status: lastMsg.status ?? "sent",
|
|
525
248
|
isEdited: false,
|
|
526
249
|
sentAt: lastMsg.sentAt ?? "",
|
|
527
|
-
createdAt: lastMsg.sentAt ?? ""
|
|
250
|
+
createdAt: lastMsg.sentAt ?? "",
|
|
251
|
+
...lastMsg.senderName && { senderName: lastMsg.senderName }
|
|
528
252
|
};
|
|
529
253
|
}
|
|
530
254
|
function normalizeConversation(conv) {
|
|
@@ -642,93 +366,6 @@ var conversationsApi = {
|
|
|
642
366
|
}
|
|
643
367
|
};
|
|
644
368
|
|
|
645
|
-
// src/api/storage.ts
|
|
646
|
-
var storageApi = {
|
|
647
|
-
async requestPresignedUrl(payload) {
|
|
648
|
-
const { data } = await getApiClient().post("/storage/presigned-url", payload);
|
|
649
|
-
return data;
|
|
650
|
-
},
|
|
651
|
-
async requestPresignedUrlBatch(files) {
|
|
652
|
-
const { data } = await getApiClient().post("/storage/presigned-url/batch", { files });
|
|
653
|
-
return data;
|
|
654
|
-
},
|
|
655
|
-
async confirmUpload(fileId) {
|
|
656
|
-
const { data } = await getApiClient().post(`/storage/confirm/${fileId}`);
|
|
657
|
-
return data;
|
|
658
|
-
},
|
|
659
|
-
async getFile(fileId) {
|
|
660
|
-
const { data } = await getApiClient().get(`/storage/files/${fileId}`);
|
|
661
|
-
return data;
|
|
662
|
-
},
|
|
663
|
-
async getFileUrl(fileId, expiresIn) {
|
|
664
|
-
const { data } = await getApiClient().get(`/storage/files/${fileId}/url`, {
|
|
665
|
-
params: expiresIn ? { expiresIn } : {}
|
|
666
|
-
});
|
|
667
|
-
return data;
|
|
668
|
-
},
|
|
669
|
-
async deleteFile(fileId) {
|
|
670
|
-
await getApiClient().delete(`/storage/files/${fileId}`);
|
|
671
|
-
},
|
|
672
|
-
async getConversationFiles(conversationId, params = {}) {
|
|
673
|
-
const { data } = await getApiClient().get(
|
|
674
|
-
`/storage/conversations/${conversationId}/files`,
|
|
675
|
-
{ params }
|
|
676
|
-
);
|
|
677
|
-
return data;
|
|
678
|
-
},
|
|
679
|
-
async getMyFiles(params = {}) {
|
|
680
|
-
const { data } = await getApiClient().get("/storage/my-files", { params });
|
|
681
|
-
return data;
|
|
682
|
-
}
|
|
683
|
-
};
|
|
684
|
-
async function uploadBatch(files, platformUploadFn, conversationId, onProgress, platformCompressFn, compressionConfig) {
|
|
685
|
-
const compressedFiles = await Promise.all(
|
|
686
|
-
files.map((f) => compressFile(f, platformCompressFn, compressionConfig ?? { enabled: false, imageQuality: 0.85, imageMaxDimension: 1920, compressDocuments: true }))
|
|
687
|
-
);
|
|
688
|
-
const requests = compressedFiles.map((f) => ({
|
|
689
|
-
filename: f.name,
|
|
690
|
-
mimeType: f.type,
|
|
691
|
-
size: f.size,
|
|
692
|
-
conversationId,
|
|
693
|
-
...f.compressed && {
|
|
694
|
-
metadata: {
|
|
695
|
-
compressed: f.compressed,
|
|
696
|
-
originalSize: f.originalSize,
|
|
697
|
-
compressionAlgorithm: f.compressionAlgorithm
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
}));
|
|
701
|
-
const { urls, errors: requestErrors } = await storageApi.requestPresignedUrlBatch(requests);
|
|
702
|
-
const progressMap = {};
|
|
703
|
-
const reportProgress = () => {
|
|
704
|
-
if (!onProgress) return;
|
|
705
|
-
const vals = Object.values(progressMap);
|
|
706
|
-
const avg = vals.reduce((s, v) => s + v, 0) / Math.max(vals.length, 1);
|
|
707
|
-
onProgress(Math.round(avg));
|
|
708
|
-
};
|
|
709
|
-
const successful = [];
|
|
710
|
-
const failed = [...requestErrors];
|
|
711
|
-
await Promise.all(
|
|
712
|
-
urls.map(async (presigned, idx) => {
|
|
713
|
-
const file = compressedFiles[idx];
|
|
714
|
-
progressMap[idx] = 0;
|
|
715
|
-
try {
|
|
716
|
-
await platformUploadFn(presigned, file, (pct) => {
|
|
717
|
-
progressMap[idx] = Math.round(pct * 0.9);
|
|
718
|
-
reportProgress();
|
|
719
|
-
});
|
|
720
|
-
const result = await storageApi.confirmUpload(presigned.fileId);
|
|
721
|
-
progressMap[idx] = 100;
|
|
722
|
-
reportProgress();
|
|
723
|
-
successful.push(result);
|
|
724
|
-
} catch (err) {
|
|
725
|
-
failed.push({ filename: file.name, error: err.message });
|
|
726
|
-
}
|
|
727
|
-
})
|
|
728
|
-
);
|
|
729
|
-
return { successful, failed };
|
|
730
|
-
}
|
|
731
|
-
|
|
732
369
|
// src/api/devices.ts
|
|
733
370
|
var devicesApi = {
|
|
734
371
|
/**
|
|
@@ -838,14 +475,14 @@ async function performHandshake(algo, serverKeys, socketHandshakeAuth) {
|
|
|
838
475
|
["deriveBits"]
|
|
839
476
|
);
|
|
840
477
|
const pubRaw = await globalThis.crypto.subtle.exportKey("raw", ephemeral.publicKey);
|
|
841
|
-
socketHandshakeAuth["transitEphemeralPub"] =
|
|
478
|
+
socketHandshakeAuth["transitEphemeralPub"] = bufToB64(pubRaw);
|
|
842
479
|
socketHandshakeAuth["transitAlgo"] = algo;
|
|
843
480
|
const ephemeralPriv = ephemeral.privateKey;
|
|
844
481
|
return (sessionId) => deriveSessionKey(ephemeralPriv, algo, serverKeys, sessionId);
|
|
845
482
|
}
|
|
846
483
|
async function deriveSessionKey(ephemeralPriv, algo, serverKeys, sessionId) {
|
|
847
484
|
const serverPubB64 = algo === "x25519" ? serverKeys.x25519 : serverKeys.p256;
|
|
848
|
-
const serverPubRaw =
|
|
485
|
+
const serverPubRaw = b64ToBuf(serverPubB64);
|
|
849
486
|
const keyAlgoParams = algo === "x25519" ? { name: "X25519" } : { name: "ECDH", namedCurve: "P-256" };
|
|
850
487
|
const serverPubKey = await globalThis.crypto.subtle.importKey(
|
|
851
488
|
"raw",
|
|
@@ -876,7 +513,7 @@ async function deriveSessionKey(ephemeralPriv, algo, serverKeys, sessionId) {
|
|
|
876
513
|
["encrypt", "decrypt"]
|
|
877
514
|
);
|
|
878
515
|
}
|
|
879
|
-
function
|
|
516
|
+
function bufToB64(buf) {
|
|
880
517
|
const bytes = new Uint8Array(buf);
|
|
881
518
|
let str = "";
|
|
882
519
|
bytes.forEach((b) => {
|
|
@@ -884,7 +521,7 @@ function bufToB642(buf) {
|
|
|
884
521
|
});
|
|
885
522
|
return btoa(str);
|
|
886
523
|
}
|
|
887
|
-
function
|
|
524
|
+
function b64ToBuf(b64) {
|
|
888
525
|
const bin = atob(b64);
|
|
889
526
|
const buf = new Uint8Array(bin.length);
|
|
890
527
|
for (let i = 0; i < bin.length; i++) buf[i] = bin.charCodeAt(i);
|
|
@@ -893,6 +530,7 @@ function b64ToBuf2(b64) {
|
|
|
893
530
|
|
|
894
531
|
// src/socket/socket.ts
|
|
895
532
|
var _socket = null;
|
|
533
|
+
var _connectingPromise = null;
|
|
896
534
|
var _status = "disconnected";
|
|
897
535
|
var _statusListeners = /* @__PURE__ */ new Set();
|
|
898
536
|
var _getToken = null;
|
|
@@ -966,6 +604,13 @@ function secureOn(socket, event, handler) {
|
|
|
966
604
|
}
|
|
967
605
|
async function connectSocket(config, getToken) {
|
|
968
606
|
if (_socket && !_socket.disconnected) return _socket;
|
|
607
|
+
if (_connectingPromise) return _connectingPromise;
|
|
608
|
+
_connectingPromise = _doConnect(config, getToken).finally(() => {
|
|
609
|
+
_connectingPromise = null;
|
|
610
|
+
});
|
|
611
|
+
return _connectingPromise;
|
|
612
|
+
}
|
|
613
|
+
async function _doConnect(config, getToken) {
|
|
969
614
|
_getToken = getToken;
|
|
970
615
|
_userId = config.userId;
|
|
971
616
|
_tenantId = config.tenantId;
|
|
@@ -1092,6 +737,7 @@ function createSecureSocketProxy(socket) {
|
|
|
1092
737
|
});
|
|
1093
738
|
}
|
|
1094
739
|
function disconnectSocket() {
|
|
740
|
+
_connectingPromise = null;
|
|
1095
741
|
if (_socket) {
|
|
1096
742
|
_socket.disconnect();
|
|
1097
743
|
_socket = null;
|