@xdevplatform/chat-xdk 0.1.0 → 0.2.1
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/LICENSE +21 -0
- package/README.md +27 -23
- package/index.d.ts +372 -78
- package/index.js +208 -47
- package/package.json +6 -2
- package/pkg/chat_xdk_wasm.d.ts +124 -44
- package/pkg/chat_xdk_wasm.js +224 -223
- package/pkg/chat_xdk_wasm_bg.wasm +0 -0
- package/pkg/chat_xdk_wasm_bg.wasm.d.ts +15 -9
- package/pkg/package.json +21 -0
package/index.js
CHANGED
|
@@ -5,8 +5,14 @@
|
|
|
5
5
|
* The Rust WASM `Chat` is a pure crypto engine that never touches
|
|
6
6
|
* globalThis or module-level singletons.
|
|
7
7
|
*
|
|
8
|
-
* Each `ChatWithJuicebox` instance holds its
|
|
9
|
-
*
|
|
8
|
+
* Each `ChatWithJuicebox` instance holds its own Juicebox client, but the
|
|
9
|
+
* juicebox-sdk WASM resolves its auth-token callback through a single
|
|
10
|
+
* process-global (`JuiceboxGetAuthToken`). Every Juicebox operation re-arms
|
|
11
|
+
* that global with its own instance's token getter just before running, so
|
|
12
|
+
* *sequential* use of multiple instances in one runtime is correct.
|
|
13
|
+
* *Concurrent* Juicebox operations across instances in one process are not
|
|
14
|
+
* supported: the last-armed instance's token getter services all in-flight
|
|
15
|
+
* calls.
|
|
10
16
|
*/
|
|
11
17
|
|
|
12
18
|
function isNodeRuntime() {
|
|
@@ -46,11 +52,13 @@ function toPinBytes(pin) {
|
|
|
46
52
|
* descending run of digits. Never applied on unlock.
|
|
47
53
|
*/
|
|
48
54
|
function validatePinStrength(pinBytes) {
|
|
55
|
+
// Error messages match the native core's weak-PIN errors byte-for-byte so
|
|
56
|
+
// callers can match on them across bindings.
|
|
49
57
|
if (pinBytes.length < MIN_PIN_LENGTH) {
|
|
50
|
-
throw new Error(`
|
|
58
|
+
throw new Error(`PIN must be at least ${MIN_PIN_LENGTH} characters`);
|
|
51
59
|
}
|
|
52
60
|
if (pinBytes.every((b) => b === pinBytes[0])) {
|
|
53
|
-
throw new Error('
|
|
61
|
+
throw new Error('PIN must not be a single repeated character');
|
|
54
62
|
}
|
|
55
63
|
const allDigits = pinBytes.every((b) => b >= 0x30 && b <= 0x39);
|
|
56
64
|
let ascending = true;
|
|
@@ -60,7 +68,7 @@ function validatePinStrength(pinBytes) {
|
|
|
60
68
|
if (pinBytes[i] !== pinBytes[i - 1] - 1) descending = false;
|
|
61
69
|
}
|
|
62
70
|
if (allDigits && (ascending || descending)) {
|
|
63
|
-
throw new Error('
|
|
71
|
+
throw new Error('PIN must not be a sequential run of digits');
|
|
64
72
|
}
|
|
65
73
|
}
|
|
66
74
|
|
|
@@ -90,9 +98,15 @@ async function initWasmModule() {
|
|
|
90
98
|
let wasmModule;
|
|
91
99
|
let init;
|
|
92
100
|
if (isNodeRuntime()) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
101
|
+
// Node 18 ships WebCrypto but does not expose it on the global scope,
|
|
102
|
+
// and the wasm module's random-byte source requires globalThis.crypto.
|
|
103
|
+
if (typeof globalThis.crypto === "undefined") {
|
|
104
|
+
const { webcrypto } = await import("crypto");
|
|
105
|
+
globalThis.crypto = webcrypto;
|
|
106
|
+
}
|
|
107
|
+
// Dynamic import, not require(): the wasm glue is ESM, and require(esm)
|
|
108
|
+
// only exists on Node >= 20.19 while this package supports Node 18+.
|
|
109
|
+
wasmModule = await import("./pkg/chat_xdk_wasm.js");
|
|
96
110
|
init =
|
|
97
111
|
wasmModule?.default ||
|
|
98
112
|
wasmModule?.init ||
|
|
@@ -141,6 +155,104 @@ async function loadJuiceboxSdk() {
|
|
|
141
155
|
return await import("juicebox-sdk");
|
|
142
156
|
}
|
|
143
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Resolve the PIN guess budget for Juicebox registration, matching the
|
|
160
|
+
* native bindings: a `max_guess_count` that is a non-negative integer
|
|
161
|
+
* (including 0) applies as-is; a fractional, negative, or non-numeric value
|
|
162
|
+
* falls back to the shape default — 20 for the `sdk_config` and
|
|
163
|
+
* `key_store_token_map_json` shapes, 5 otherwise. An explicit
|
|
164
|
+
* `maxGuessCount` option overrides the config under
|
|
165
|
+
* the same integer rule. Exported for tests; `createChat` calls it
|
|
166
|
+
* internally.
|
|
167
|
+
*
|
|
168
|
+
* @param {Object} configValue - Parsed Juicebox config JSON from the X API
|
|
169
|
+
* @param {number} [maxGuessCount] - Explicit override from createChat options
|
|
170
|
+
* @returns {number} The number of PIN guesses to register with
|
|
171
|
+
*/
|
|
172
|
+
export function resolveMaxGuessCount(configValue, maxGuessCount) {
|
|
173
|
+
if (Number.isSafeInteger(maxGuessCount) && maxGuessCount >= 0) {
|
|
174
|
+
return maxGuessCount;
|
|
175
|
+
}
|
|
176
|
+
const fromConfig = configValue?.max_guess_count;
|
|
177
|
+
if (Number.isSafeInteger(fromConfig) && fromConfig >= 0) {
|
|
178
|
+
return fromConfig;
|
|
179
|
+
}
|
|
180
|
+
return typeof configValue?.sdk_config === "string" ||
|
|
181
|
+
typeof configValue?.key_store_token_map_json === "string"
|
|
182
|
+
? 20
|
|
183
|
+
: 5;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Derive the value to hand the Juicebox `Configuration` constructor from a
|
|
188
|
+
* parsed X API config. The `sdk_config` wrapper shape is unwrapped to its
|
|
189
|
+
* embedded SDK config JSON string (which the Juicebox constructor accepts
|
|
190
|
+
* directly); the X API `juicebox_config` object shape is unwrapped to its
|
|
191
|
+
* `key_store_token_map_json` string, which is used **verbatim** because it
|
|
192
|
+
* carries each realm's `public_key` and the server's register/recover
|
|
193
|
+
* thresholds — the realms require both; a raw realms config passes through
|
|
194
|
+
* unchanged; a bare `token_map` shape (an array of
|
|
195
|
+
* `{ key, value: { address } }` entries, no `key_store_token_map_json`) is
|
|
196
|
+
* converted to a realms config with majority recover threshold, the same
|
|
197
|
+
* derivation the native bindings apply. A non-array `token_map` is rejected
|
|
198
|
+
* here, with the same message the core parser uses, rather than handed to
|
|
199
|
+
* the Juicebox constructor to fail obscurely. Realm auth tokens are not
|
|
200
|
+
* taken from the config in this wrapper — they come from the `getAuthToken`
|
|
201
|
+
* callback.
|
|
202
|
+
*/
|
|
203
|
+
export function juiceboxClientConfig(configValue) {
|
|
204
|
+
if (typeof configValue?.sdk_config === "string") {
|
|
205
|
+
return configValue.sdk_config;
|
|
206
|
+
}
|
|
207
|
+
if (configValue?.key_store_token_map_json !== undefined) {
|
|
208
|
+
if (typeof configValue.key_store_token_map_json !== "string") {
|
|
209
|
+
throw new Error("key_store_token_map_json must be a string");
|
|
210
|
+
}
|
|
211
|
+
// A malformed embedded config is an error rather than a fall-through to
|
|
212
|
+
// the lossy token_map derivation, which would silently drop the realm
|
|
213
|
+
// public keys and produce configs that can never reach the recover
|
|
214
|
+
// threshold. It must parse to a JSON object — merely valid JSON like
|
|
215
|
+
// "42" or "[]" would be handed to the Juicebox constructor to fail
|
|
216
|
+
// obscurely at setup/unlock time.
|
|
217
|
+
let parsed;
|
|
218
|
+
try {
|
|
219
|
+
parsed = JSON.parse(configValue.key_store_token_map_json);
|
|
220
|
+
} catch (e) {
|
|
221
|
+
throw new Error(`Invalid key_store_token_map_json: ${e.message}`);
|
|
222
|
+
}
|
|
223
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
224
|
+
throw new Error("Invalid key_store_token_map_json: not a JSON object");
|
|
225
|
+
}
|
|
226
|
+
return configValue.key_store_token_map_json;
|
|
227
|
+
}
|
|
228
|
+
if (
|
|
229
|
+
configValue?.token_map !== undefined &&
|
|
230
|
+
configValue?.realms === undefined &&
|
|
231
|
+
!Array.isArray(configValue.token_map)
|
|
232
|
+
) {
|
|
233
|
+
throw new Error("Missing token_map or sdk_config");
|
|
234
|
+
}
|
|
235
|
+
if (Array.isArray(configValue?.token_map) && configValue?.realms === undefined) {
|
|
236
|
+
const realms = configValue.token_map.map((entry) => {
|
|
237
|
+
const id = entry?.key;
|
|
238
|
+
const address = entry?.value?.address;
|
|
239
|
+
if (typeof id !== "string" || typeof address !== "string") {
|
|
240
|
+
throw new Error(
|
|
241
|
+
"Invalid token_map entry: expected { key, value: { address } }",
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
return { id, address };
|
|
245
|
+
});
|
|
246
|
+
return {
|
|
247
|
+
realms,
|
|
248
|
+
register_threshold: realms.length,
|
|
249
|
+
recover_threshold: Math.floor(realms.length / 2) + 1,
|
|
250
|
+
pin_hashing_mode: "Standard2019",
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
return configValue;
|
|
254
|
+
}
|
|
255
|
+
|
|
144
256
|
// ChatWithJuicebox — JS wrapper that owns both a WasmChat and a Juicebox client
|
|
145
257
|
|
|
146
258
|
/**
|
|
@@ -153,18 +265,36 @@ export class ChatWithJuicebox {
|
|
|
153
265
|
#inner;
|
|
154
266
|
#juiceboxClient;
|
|
155
267
|
#numGuesses;
|
|
268
|
+
// The raw `maxGuessCount` createChat option (undefined when not provided),
|
|
269
|
+
// kept so updateConfig re-resolves the budget under the same override
|
|
270
|
+
// semantics as construction: an explicit option keeps winning.
|
|
271
|
+
#maxGuessCountOverride;
|
|
156
272
|
#JuiceboxConfiguration;
|
|
157
273
|
#JuiceboxClient;
|
|
274
|
+
// Points the process-global `JuiceboxGetAuthToken` at this instance's token
|
|
275
|
+
// getter. Called before every Juicebox network operation so sequential use
|
|
276
|
+
// of multiple instances presents the right tokens (last-armed wins).
|
|
277
|
+
#armAuthTokenHook;
|
|
158
278
|
|
|
159
|
-
constructor(
|
|
279
|
+
constructor(
|
|
280
|
+
wasmChat,
|
|
281
|
+
juiceboxClient,
|
|
282
|
+
numGuesses,
|
|
283
|
+
JuiceboxConfiguration,
|
|
284
|
+
JuiceboxClientCtor,
|
|
285
|
+
armAuthTokenHook,
|
|
286
|
+
maxGuessCountOverride,
|
|
287
|
+
) {
|
|
160
288
|
this.#inner = wasmChat;
|
|
161
289
|
this.#juiceboxClient = juiceboxClient;
|
|
162
290
|
this.#numGuesses = numGuesses;
|
|
291
|
+
this.#maxGuessCountOverride = maxGuessCountOverride;
|
|
163
292
|
this.#JuiceboxConfiguration = JuiceboxConfiguration;
|
|
164
293
|
this.#JuiceboxClient = JuiceboxClientCtor;
|
|
294
|
+
this.#armAuthTokenHook = armAuthTokenHook ?? (() => {});
|
|
165
295
|
}
|
|
166
296
|
|
|
167
|
-
// Juicebox lifecycle (
|
|
297
|
+
// Juicebox lifecycle (handled in this JS layer)
|
|
168
298
|
|
|
169
299
|
/**
|
|
170
300
|
* Register existing keys with Juicebox. Call generateKeypairs() first.
|
|
@@ -178,6 +308,7 @@ export class ChatWithJuicebox {
|
|
|
178
308
|
validatePinStrength(pinBytes);
|
|
179
309
|
const secretBytes = this.#inner.exportKeys();
|
|
180
310
|
try {
|
|
311
|
+
this.#armAuthTokenHook();
|
|
181
312
|
await this.#juiceboxClient.register(
|
|
182
313
|
pinBytes,
|
|
183
314
|
secretBytes,
|
|
@@ -202,6 +333,7 @@ export class ChatWithJuicebox {
|
|
|
202
333
|
const ownsPin = typeof pin === 'string';
|
|
203
334
|
let secretBytes;
|
|
204
335
|
try {
|
|
336
|
+
this.#armAuthTokenHook();
|
|
205
337
|
secretBytes = await this.#juiceboxClient.recover(pinBytes, new Uint8Array(0));
|
|
206
338
|
} catch (err) {
|
|
207
339
|
throw new Error(`Juicebox recovery failed: ${formatJuiceboxError(err, RECOVER_REASON_BY_VALUE)}`);
|
|
@@ -217,6 +349,7 @@ export class ChatWithJuicebox {
|
|
|
217
349
|
|
|
218
350
|
/** Delete keys from Juicebox. Warning: Irreversible. */
|
|
219
351
|
async delete() {
|
|
352
|
+
this.#armAuthTokenHook();
|
|
220
353
|
await this.#juiceboxClient.delete();
|
|
221
354
|
this.#inner.lock();
|
|
222
355
|
}
|
|
@@ -229,6 +362,7 @@ export class ChatWithJuicebox {
|
|
|
229
362
|
await this.unlock(oldPin);
|
|
230
363
|
const secretBytes = this.#inner.exportKeys();
|
|
231
364
|
try {
|
|
365
|
+
this.#armAuthTokenHook();
|
|
232
366
|
await this.#juiceboxClient.register(
|
|
233
367
|
newPinBytes,
|
|
234
368
|
secretBytes,
|
|
@@ -243,11 +377,16 @@ export class ChatWithJuicebox {
|
|
|
243
377
|
}
|
|
244
378
|
}
|
|
245
379
|
|
|
246
|
-
/**
|
|
380
|
+
/**
|
|
381
|
+
* Update Juicebox config (e.g. to refresh auth tokens). Re-creates the
|
|
382
|
+
* client and re-resolves the PIN guess budget from the new config; an
|
|
383
|
+
* explicit createChat `maxGuessCount` override keeps winning.
|
|
384
|
+
*/
|
|
247
385
|
updateConfig(juiceboxConfig) {
|
|
248
386
|
const configValue = JSON.parse(juiceboxConfig);
|
|
249
|
-
const config = new this.#JuiceboxConfiguration(configValue);
|
|
387
|
+
const config = new this.#JuiceboxConfiguration(juiceboxClientConfig(configValue));
|
|
250
388
|
this.#juiceboxClient = new this.#JuiceboxClient(config, []);
|
|
389
|
+
this.#numGuesses = resolveMaxGuessCount(configValue, this.#maxGuessCountOverride);
|
|
251
390
|
}
|
|
252
391
|
|
|
253
392
|
// Forwarded crypto methods — delegate to inner WasmChat
|
|
@@ -261,35 +400,42 @@ export class ChatWithJuicebox {
|
|
|
261
400
|
hasIdentityKey() { return this.#inner.hasIdentityKey(); }
|
|
262
401
|
lock() { return this.#inner.lock(); }
|
|
263
402
|
decryptConversationKey(b64) { return this.#inner.decryptConversationKey(b64); }
|
|
264
|
-
generateConversationKey() { return this.#inner.generateConversationKey(); }
|
|
265
403
|
extractConversationKeys(events) { return this.#inner.extractConversationKeys(events); }
|
|
266
404
|
decryptEvent(eventB64, conversationKeys, signingKeys) {
|
|
267
|
-
return this.#inner.decryptEvent(eventB64, conversationKeys, signingKeys
|
|
405
|
+
return this.#inner.decryptEvent(eventB64, conversationKeys, signingKeys);
|
|
268
406
|
}
|
|
269
407
|
decryptEvents(events, signingKeys) {
|
|
270
|
-
return this.#inner.decryptEvents(events, signingKeys
|
|
408
|
+
return this.#inner.decryptEvents(events, signingKeys);
|
|
271
409
|
}
|
|
272
410
|
sign(data) { return this.#inner.sign(data); }
|
|
273
411
|
verify(publicKeyB64, signature, data) { return this.#inner.verify(publicKeyB64, signature, data); }
|
|
274
412
|
verifyKeyBinding(identityPublicKeyB64, signingPublicKeyB64, identityPublicKeySignatureB64) {
|
|
275
413
|
return this.#inner.verifyKeyBinding(identityPublicKeyB64, signingPublicKeyB64, identityPublicKeySignatureB64);
|
|
276
414
|
}
|
|
277
|
-
encryptMessage(
|
|
278
|
-
encryptReply(
|
|
279
|
-
encryptAddReaction(
|
|
280
|
-
encryptRemoveReaction(
|
|
415
|
+
encryptMessage(params) { return this.#inner.encryptMessage(params); }
|
|
416
|
+
encryptReply(params) { return this.#inner.encryptReply(params); }
|
|
417
|
+
encryptAddReaction(params) { return this.#inner.encryptAddReaction(params); }
|
|
418
|
+
encryptRemoveReaction(params) { return this.#inner.encryptRemoveReaction(params); }
|
|
281
419
|
encryptStream(plaintext, key) { return this.#inner.encryptStream(plaintext, key); }
|
|
282
420
|
decryptStream(encrypted, key) { return this.#inner.decryptStream(encrypted, key); }
|
|
421
|
+
streamEncryptor(key) { return this.#inner.streamEncryptor(key); }
|
|
422
|
+
streamDecryptor(key) { return this.#inner.streamDecryptor(key); }
|
|
283
423
|
encrypt(plaintext, key) { return this.#inner.encrypt(plaintext, key); }
|
|
284
424
|
decrypt(ciphertext, key) { return this.#inner.decrypt(ciphertext, key); }
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
|
|
425
|
+
prepareConversationKeyChange(params) { return this.#inner.prepareConversationKeyChange(params); }
|
|
426
|
+
prepareGroupMembersChange(params) { return this.#inner.prepareGroupMembersChange(params); }
|
|
427
|
+
prepareGroupCreate(params) { return this.#inner.prepareGroupCreate(params); }
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Release the WASM-side crypto engine: clears key material (`lock()`) and
|
|
431
|
+
* frees the underlying WASM object. The instance must not be used
|
|
432
|
+
* afterwards. When reusing the instance, `lock()` alone suffices for key
|
|
433
|
+
* hygiene.
|
|
434
|
+
*/
|
|
435
|
+
free() {
|
|
436
|
+
this.#inner.lock();
|
|
437
|
+
this.#inner.free();
|
|
290
438
|
}
|
|
291
|
-
signAddMembers(...args) { return this.#inner.signAddMembers(...args); }
|
|
292
|
-
signKeyChange(...args) { return this.#inner.signKeyChange(...args); }
|
|
293
439
|
}
|
|
294
440
|
|
|
295
441
|
/**
|
|
@@ -299,7 +445,9 @@ export class ChatWithJuicebox {
|
|
|
299
445
|
* @param {string} options.juiceboxConfig - Juicebox configuration JSON from X API
|
|
300
446
|
* @param {Function} options.getAuthToken - Async function to get auth token for a realm
|
|
301
447
|
* Signature: (realmId: string) => Promise<string>
|
|
302
|
-
* @
|
|
448
|
+
* @param {number} [options.maxGuessCount] - Optional override for the PIN guess
|
|
449
|
+
* budget (see CreateChatOptions)
|
|
450
|
+
* @returns {Promise<ChatWithJuicebox>} Initialized chat instance
|
|
303
451
|
*
|
|
304
452
|
* @example
|
|
305
453
|
* const chat = await createChat({
|
|
@@ -328,21 +476,29 @@ export async function createChat(options) {
|
|
|
328
476
|
throw new Error('getAuthToken must be an async function');
|
|
329
477
|
}
|
|
330
478
|
|
|
331
|
-
// Load Juicebox WASM SDK
|
|
479
|
+
// Load Juicebox WASM SDK. Tests inject stub constructors through the
|
|
480
|
+
// internal `juiceboxModule` option so this flow runs without the
|
|
481
|
+
// juicebox-sdk WASM or network realms; it is not part of the public API.
|
|
332
482
|
let JuiceboxClientCtor;
|
|
333
483
|
let JuiceboxConfiguration;
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
484
|
+
if (options.juiceboxModule) {
|
|
485
|
+
JuiceboxClientCtor = options.juiceboxModule.Client;
|
|
486
|
+
JuiceboxConfiguration = options.juiceboxModule.Configuration;
|
|
487
|
+
} else {
|
|
488
|
+
try {
|
|
489
|
+
const juiceboxModule = await loadJuiceboxSdk();
|
|
490
|
+
JuiceboxClientCtor = juiceboxModule.Client;
|
|
491
|
+
JuiceboxConfiguration = juiceboxModule.Configuration;
|
|
492
|
+
} catch (e) {
|
|
493
|
+
throw new Error(
|
|
494
|
+
"Failed to load juicebox-sdk (an optional peer dependency). " +
|
|
495
|
+
"Install it with `npm install juicebox-sdk`, or build it from " +
|
|
496
|
+
"https://github.com/juicebox-systems/juicebox-sdk:\n" +
|
|
497
|
+
" cd juicebox-sdk/rust/sdk/bridge/wasm && wasm-pack build --target nodejs --out-dir pkg --out-name juicebox-sdk\n" +
|
|
498
|
+
" npm install /path/to/juicebox-sdk/rust/sdk/bridge/wasm/pkg\n" +
|
|
499
|
+
"Original error: " + e.message
|
|
500
|
+
);
|
|
501
|
+
}
|
|
346
502
|
}
|
|
347
503
|
if (!JuiceboxConfiguration) {
|
|
348
504
|
throw new Error('juicebox-sdk Configuration export is missing');
|
|
@@ -350,8 +506,10 @@ export async function createChat(options) {
|
|
|
350
506
|
|
|
351
507
|
// The juicebox-sdk WASM resolves `JuiceboxGetAuthToken` as a bare-name
|
|
352
508
|
// global on every register/recover/delete call (not just construction).
|
|
353
|
-
//
|
|
354
|
-
|
|
509
|
+
// The global is process-wide, so each instance re-arms it with its own
|
|
510
|
+
// token getter immediately before every Juicebox operation; concurrent
|
|
511
|
+
// Juicebox operations across instances are not supported (last-armed wins).
|
|
512
|
+
const authTokenHook = async (realmId) => {
|
|
355
513
|
const realmIdHex =
|
|
356
514
|
typeof realmId === "string"
|
|
357
515
|
? realmId
|
|
@@ -360,15 +518,16 @@ export async function createChat(options) {
|
|
|
360
518
|
.join("");
|
|
361
519
|
return await getAuthToken(realmIdHex);
|
|
362
520
|
};
|
|
521
|
+
const armAuthTokenHook = () => {
|
|
522
|
+
globalThis.JuiceboxGetAuthToken = authTokenHook;
|
|
523
|
+
};
|
|
524
|
+
armAuthTokenHook();
|
|
363
525
|
|
|
364
526
|
const configValue = JSON.parse(juiceboxConfig);
|
|
365
|
-
const config = new JuiceboxConfiguration(configValue);
|
|
527
|
+
const config = new JuiceboxConfiguration(juiceboxClientConfig(configValue));
|
|
366
528
|
const juiceboxClient = new JuiceboxClientCtor(config, []);
|
|
367
529
|
|
|
368
|
-
const numGuesses =
|
|
369
|
-
Number.isFinite(maxGuessCount) && Number(maxGuessCount) > 0
|
|
370
|
-
? Math.floor(Number(maxGuessCount))
|
|
371
|
-
: 5;
|
|
530
|
+
const numGuesses = resolveMaxGuessCount(configValue, maxGuessCount);
|
|
372
531
|
|
|
373
532
|
// Load Rust WASM crypto engine
|
|
374
533
|
const wasmModule = await initWasmModule();
|
|
@@ -380,6 +539,8 @@ export async function createChat(options) {
|
|
|
380
539
|
numGuesses,
|
|
381
540
|
JuiceboxConfiguration,
|
|
382
541
|
JuiceboxClientCtor,
|
|
542
|
+
armAuthTokenHook,
|
|
543
|
+
maxGuessCount,
|
|
383
544
|
);
|
|
384
545
|
}
|
|
385
546
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xdevplatform/chat-xdk",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "X Chat SDK - End-to-end encryption for X Direct Messages",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -8,10 +8,14 @@
|
|
|
8
8
|
"exports": {
|
|
9
9
|
".": "./index.js"
|
|
10
10
|
},
|
|
11
|
+
"engines": {
|
|
12
|
+
"node": ">=18"
|
|
13
|
+
},
|
|
11
14
|
"files": [
|
|
12
15
|
"index.js",
|
|
13
16
|
"index.d.ts",
|
|
14
|
-
"pkg
|
|
17
|
+
"pkg",
|
|
18
|
+
"LICENSE"
|
|
15
19
|
],
|
|
16
20
|
"publishConfig": {
|
|
17
21
|
"access": "public"
|