@xdevplatform/chat-xdk 0.2.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/index.d.ts CHANGED
@@ -496,13 +496,18 @@ export interface AttachmentInfo {
496
496
  export interface CreateChatOptions {
497
497
  /**
498
498
  * Juicebox configuration JSON from the X API.
499
- * Obtain this from: GET /2/dm/encryption/juicebox/config
499
+ * Obtain this from the `juicebox_config` field of
500
+ * GET /2/users/:id/public_keys (your own user, `public_key.fields=juicebox_config`).
500
501
  *
501
- * Accepted shapes match the native bindings: the `sdk_config` wrapper
502
- * (its embedded SDK config string is unwrapped for the Juicebox client),
503
- * the `token_map` array (converted to a realms config with majority
504
- * recover threshold), or a raw realms config. Realm auth tokens always
505
- * come from `getAuthToken`, not the config.
502
+ * Accepted shapes match the native bindings and are checked in this
503
+ * order: the `sdk_config` wrapper (its embedded SDK config string is
504
+ * unwrapped for the Juicebox client), the X API `juicebox_config` object
505
+ * (its `key_store_token_map_json` string is used verbatim so realm public
506
+ * keys and server thresholds are preserved), a bare `token_map` array
507
+ * (converted to a realms config with majority recover threshold — no
508
+ * realm public keys, so only suitable for realms that don't require
509
+ * them), or a raw realms config. Realm auth tokens always come from
510
+ * `getAuthToken`, not the config.
506
511
  */
507
512
  juiceboxConfig: string;
508
513
 
@@ -527,7 +532,8 @@ export interface CreateChatOptions {
527
532
  * A non-negative integer wins over the config, including across later
528
533
  * `updateConfig()` calls. When omitted, the config's `max_guess_count`
529
534
  * applies when it is a non-negative integer (0 included), defaulting to
530
- * 20 for the `sdk_config` shape and 5 otherwise.
535
+ * 20 for the `sdk_config` and `key_store_token_map_json` shapes and 5
536
+ * otherwise.
531
537
  */
532
538
  maxGuessCount?: number;
533
539
  }
package/index.js CHANGED
@@ -159,8 +159,9 @@ async function loadJuiceboxSdk() {
159
159
  * Resolve the PIN guess budget for Juicebox registration, matching the
160
160
  * native bindings: a `max_guess_count` that is a non-negative integer
161
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` shape, 5
163
- * otherwise. An explicit `maxGuessCount` option overrides the config under
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
164
165
  * the same integer rule. Exported for tests; `createChat` calls it
165
166
  * internally.
166
167
  *
@@ -176,25 +177,54 @@ export function resolveMaxGuessCount(configValue, maxGuessCount) {
176
177
  if (Number.isSafeInteger(fromConfig) && fromConfig >= 0) {
177
178
  return fromConfig;
178
179
  }
179
- return typeof configValue?.sdk_config === "string" ? 20 : 5;
180
+ return typeof configValue?.sdk_config === "string" ||
181
+ typeof configValue?.key_store_token_map_json === "string"
182
+ ? 20
183
+ : 5;
180
184
  }
181
185
 
182
186
  /**
183
187
  * Derive the value to hand the Juicebox `Configuration` constructor from a
184
188
  * parsed X API config. The `sdk_config` wrapper shape is unwrapped to its
185
189
  * embedded SDK config JSON string (which the Juicebox constructor accepts
186
- * directly); a raw realms config passes through unchanged; the `token_map`
187
- * shape (an array of `{ key, value: { address } }` entries) is converted to
188
- * a realms config with majority recover threshold, the same derivation the
189
- * native bindings apply. A non-array `token_map` is rejected here, with the
190
- * same message the core parser uses, rather than handed to the Juicebox
191
- * constructor to fail obscurely. Realm auth tokens are not taken from the
192
- * config in this wrapper they come from the `getAuthToken` callback.
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.
193
202
  */
194
203
  export function juiceboxClientConfig(configValue) {
195
204
  if (typeof configValue?.sdk_config === "string") {
196
205
  return configValue.sdk_config;
197
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
+ }
198
228
  if (
199
229
  configValue?.token_map !== undefined &&
200
230
  configValue?.realms === undefined &&
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xdevplatform/chat-xdk",
3
- "version": "0.2.0",
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",
Binary file
package/pkg/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "chat-xdk-wasm",
3
3
  "type": "module",
4
4
  "description": "X Chat SDK - WebAssembly bindings for JavaScript/TypeScript",
5
- "version": "0.2.0",
5
+ "version": "0.2.1",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",