@arbidocs/client 0.4.166 → 0.4.168

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/index.cjs CHANGED
@@ -11,55 +11,67 @@ var sodium__default = /*#__PURE__*/_interopDefault(sodium);
11
11
  // src/client.ts
12
12
 
13
13
  // src/session.ts
14
- function createInitialState() {
14
+ function createInitialState(isSsoMode = false) {
15
15
  return {
16
16
  accessToken: null,
17
17
  userEmail: null,
18
18
  userExtId: null,
19
19
  selectedWorkspaceId: null,
20
- isSsoMode: false,
21
- isAuth0Authenticated: false,
22
- auth0AccessToken: null
20
+ isSsoMode,
21
+ user: null,
22
+ signedIn: false,
23
+ serverSessionKey: null
23
24
  };
24
25
  }
25
- function createSessionManager() {
26
- let state = createInitialState();
26
+ var stored = (s) => ({
27
+ accessToken: s.accessToken,
28
+ userEmail: s.userEmail,
29
+ userExtId: s.userExtId,
30
+ isSsoMode: s.isSsoMode,
31
+ user: s.user,
32
+ signedIn: s.signedIn
33
+ });
34
+ function createSessionManager(options = {}) {
35
+ const { storage } = options;
36
+ let state = { ...createInitialState(), ...storage?.load() ?? {} };
27
37
  const listeners = /* @__PURE__ */ new Set();
28
- function notify() {
29
- const snapshot = { ...state };
30
- for (const listener of listeners) {
31
- listener(snapshot);
32
- }
38
+ function update(patch) {
39
+ state = { ...state, ...patch };
40
+ storage?.save(stored(state));
41
+ const snapshot = state;
42
+ for (const listener of [...listeners]) listener(snapshot);
33
43
  }
34
44
  return {
35
- getState() {
36
- return state;
37
- },
38
- setAccessToken(token) {
39
- state = { ...state, accessToken: token };
40
- notify();
41
- },
45
+ getState: () => state,
46
+ setAccessToken: (accessToken) => update({ accessToken }),
42
47
  setUser(email, extId) {
43
- state = { ...state, userEmail: email, userExtId: extId ?? state.userExtId };
44
- notify();
45
- },
46
- setSelectedWorkspace(id) {
47
- state = { ...state, selectedWorkspaceId: id };
48
- notify();
48
+ update({
49
+ userEmail: email,
50
+ userExtId: extId ?? state.userExtId,
51
+ user: state.user ? { ...state.user, email } : null
52
+ });
49
53
  },
50
- setSsoState(opts) {
51
- state = {
52
- ...state,
53
- isSsoMode: opts.isSsoMode ?? state.isSsoMode,
54
- isAuth0Authenticated: opts.isAuth0Authenticated ?? state.isAuth0Authenticated,
55
- auth0AccessToken: opts.auth0AccessToken !== void 0 ? opts.auth0AccessToken : state.auth0AccessToken
56
- };
57
- notify();
54
+ updateUser(patch) {
55
+ if (!state.user) return;
56
+ const user = { ...state.user, ...patch };
57
+ update({ user, userEmail: user.email });
58
58
  },
59
- clear() {
60
- state = createInitialState();
61
- notify();
59
+ setSelectedWorkspace: (selectedWorkspaceId) => update({ selectedWorkspaceId }),
60
+ setSsoMode: (isSsoMode) => update({ isSsoMode }),
61
+ setServerSessionKey: (serverSessionKey) => update({ serverSessionKey }),
62
+ signIn({ accessToken, user, isSsoMode, serverSessionKey }) {
63
+ update({
64
+ accessToken,
65
+ user,
66
+ userEmail: user.email,
67
+ userExtId: user.external_id,
68
+ isSsoMode,
69
+ serverSessionKey,
70
+ signedIn: true
71
+ });
62
72
  },
73
+ // The SSO mode outlives a sign-out: it says how this deployment (or this user) signs in.
74
+ clear: () => update(createInitialState(state.isSsoMode)),
63
75
  subscribe(listener) {
64
76
  listeners.add(listener);
65
77
  return () => {
@@ -68,23 +80,41 @@ function createSessionManager() {
68
80
  }
69
81
  };
70
82
  }
83
+ var ACCESS_TOKEN_STORAGE_KEY = "arbi_access_token";
84
+ var SESSION_STORAGE_KEY = "arbi_session";
85
+ function browserSessionStateStorage() {
86
+ return {
87
+ load() {
88
+ try {
89
+ const rest = JSON.parse(window.localStorage.getItem(SESSION_STORAGE_KEY) ?? "null");
90
+ const accessToken = window.sessionStorage.getItem(ACCESS_TOKEN_STORAGE_KEY);
91
+ return { ...rest ?? {}, accessToken };
92
+ } catch {
93
+ return null;
94
+ }
95
+ },
96
+ save({ accessToken, ...rest }) {
97
+ try {
98
+ if (accessToken) window.sessionStorage.setItem(ACCESS_TOKEN_STORAGE_KEY, accessToken);
99
+ else window.sessionStorage.removeItem(ACCESS_TOKEN_STORAGE_KEY);
100
+ if (rest.signedIn || rest.isSsoMode)
101
+ window.localStorage.setItem(SESSION_STORAGE_KEY, JSON.stringify(rest));
102
+ else window.localStorage.removeItem(SESSION_STORAGE_KEY);
103
+ } catch {
104
+ }
105
+ }
106
+ };
107
+ }
71
108
  function createTokenProvider(session) {
72
109
  return {
73
110
  getAccessToken: () => session.getState().accessToken
74
111
  };
75
112
  }
76
- function createAuthStateProvider(session) {
113
+ function createAuthStateProvider(session, selectedWorkspace = () => session.getState().selectedWorkspaceId) {
77
114
  return {
78
115
  getUserEmail: () => session.getState().userEmail,
79
- getSsoState: () => {
80
- const s = session.getState();
81
- return {
82
- isSsoMode: s.isSsoMode,
83
- isAuth0Authenticated: s.isAuth0Authenticated,
84
- auth0AccessToken: s.auth0AccessToken
85
- };
86
- },
87
- getSelectedWorkspaceId: () => session.getState().selectedWorkspaceId,
116
+ isSsoMode: () => session.getState().isSsoMode,
117
+ getSelectedWorkspaceId: selectedWorkspace,
88
118
  setAccessToken: (token) => session.setAccessToken(token)
89
119
  };
90
120
  }
@@ -363,6 +393,25 @@ function parseCallFragment(hash) {
363
393
  const v = Number(params.get("v"));
364
394
  return { key, linkVersion: Number.isInteger(v) && v > 0 ? v : null };
365
395
  }
396
+ function buildCallLink(origin, callExtId, key, linkVersion) {
397
+ return `${origin}/call/${callExtId}#${buildCallFragment(key, linkVersion)}`;
398
+ }
399
+ async function sealCallKeyForPeers(key, peers, exclude) {
400
+ const out = {};
401
+ for (const peer of peers) {
402
+ if (!peer.encryption_public_key || peer.external_id === exclude) continue;
403
+ out[peer.external_id] = await sealCallKey(key, peer.encryption_public_key);
404
+ }
405
+ return out;
406
+ }
407
+ async function openCallKeyOf(call, encryptionSecretKey) {
408
+ if (!call.my_sealed_key) return null;
409
+ try {
410
+ return await openSealedCallKey(call.my_sealed_key, encryptionSecretKey);
411
+ } catch {
412
+ return null;
413
+ }
414
+ }
366
415
  var CHAT_KEY_BYTES = 32;
367
416
  async function deriveDirectChatKey(peerPublicKeyBase64, mySecretKey) {
368
417
  await initSodium();
@@ -1067,8 +1116,126 @@ function createBearerAuthMiddleware(config) {
1067
1116
  };
1068
1117
  }
1069
1118
 
1119
+ // src/workspace-keys.ts
1120
+ function createWorkspaceKeys(deps) {
1121
+ const wrapped = /* @__PURE__ */ new Map();
1122
+ const keyless = /* @__PURE__ */ new Set();
1123
+ let listing = null;
1124
+ const primed = /* @__PURE__ */ new Map();
1125
+ let primedFor = null;
1126
+ function remember(workspaces) {
1127
+ for (const ws of workspaces) {
1128
+ if (ws.wrapped_key) {
1129
+ wrapped.set(ws.external_id, ws.wrapped_key);
1130
+ keyless.delete(ws.external_id);
1131
+ } else if (ws.is_public || ws.is_member) keyless.add(ws.external_id);
1132
+ }
1133
+ return workspaces;
1134
+ }
1135
+ function readList() {
1136
+ listing ??= (async () => {
1137
+ try {
1138
+ const token = deps.getAccessToken();
1139
+ if (!token) throw new Error("Not signed in");
1140
+ const list = await deps.listWorkspaces(token);
1141
+ if (!list) throw new Error("Failed to fetch workspaces");
1142
+ remember(list);
1143
+ } finally {
1144
+ listing = null;
1145
+ }
1146
+ })();
1147
+ return listing;
1148
+ }
1149
+ async function wrappedKey(workspaceId, { refresh = false } = {}) {
1150
+ if (!refresh) {
1151
+ const cached = wrapped.get(workspaceId);
1152
+ if (cached) return { wrappedKey: cached, fromCache: true };
1153
+ if (keyless.has(workspaceId)) return { wrappedKey: null, fromCache: true };
1154
+ }
1155
+ wrapped.delete(workspaceId);
1156
+ keyless.delete(workspaceId);
1157
+ await readList();
1158
+ const fresh = wrapped.get(workspaceId);
1159
+ if (fresh) return { wrappedKey: fresh, fromCache: false };
1160
+ if (keyless.has(workspaceId)) return { wrappedKey: null, fromCache: false };
1161
+ throw new Error(`Workspace ${workspaceId} not found or has no encryption key`);
1162
+ }
1163
+ async function sealedFor(workspaceId, serverSessionKey2, refresh) {
1164
+ const stored2 = await deps.persistence.getSession();
1165
+ if (!stored2?.signingPrivateKey) throw new Error("No stored identity to unlock the workspace");
1166
+ const { wrappedKey: key, fromCache } = await wrappedKey(workspaceId, { refresh });
1167
+ if (!key) throw new Error(`Workspace ${workspaceId} is public: it has no key to deposit`);
1168
+ try {
1169
+ await deps.crypto.ensureReady();
1170
+ return await deps.crypto.sealKeyForSession(
1171
+ deps.unwrapWorkspaceKey(key, stored2.signingPrivateKey),
1172
+ serverSessionKey2
1173
+ );
1174
+ } catch (error) {
1175
+ if (!fromCache) throw error;
1176
+ return sealedFor(workspaceId, serverSessionKey2, true);
1177
+ }
1178
+ }
1179
+ async function serverSessionKey() {
1180
+ const key = deps.getServerSessionKey() ?? (await deps.persistence.getSession())?.serverSessionKey;
1181
+ if (!key) throw new Error("No server session to unlock the workspace for");
1182
+ return key;
1183
+ }
1184
+ function prime(workspaceId, { force = false } = {}) {
1185
+ const sessionKey = deps.getServerSessionKey();
1186
+ if (sessionKey !== primedFor) {
1187
+ primed.clear();
1188
+ primedFor = sessionKey;
1189
+ }
1190
+ const pending = primed.get(workspaceId);
1191
+ if (pending && !force) return pending;
1192
+ const run = (async () => {
1193
+ const token = deps.getAccessToken();
1194
+ if (!token) throw new Error("Not signed in");
1195
+ const sealed = await sealedFor(workspaceId, await serverSessionKey(), false);
1196
+ await deps.primeKey(token, workspaceId, sealed);
1197
+ })();
1198
+ primed.set(workspaceId, run);
1199
+ run.catch(() => {
1200
+ if (primed.get(workspaceId) === run) primed.delete(workspaceId);
1201
+ });
1202
+ return run;
1203
+ }
1204
+ return {
1205
+ remember,
1206
+ wrappedKey,
1207
+ prime,
1208
+ forget(workspaceIds) {
1209
+ for (const id of workspaceIds) {
1210
+ wrapped.delete(id);
1211
+ keyless.delete(id);
1212
+ primed.delete(id);
1213
+ }
1214
+ },
1215
+ clear() {
1216
+ wrapped.clear();
1217
+ keyless.clear();
1218
+ primed.clear();
1219
+ primedFor = null;
1220
+ }
1221
+ };
1222
+ }
1223
+ function workspaceKeyRequiredOf(body) {
1224
+ const detail = body?.detail;
1225
+ if (!detail || typeof detail !== "object") return null;
1226
+ const d = detail;
1227
+ return d.code === "workspace_key_required" && typeof d.workspace_ext_id === "string" ? d.workspace_ext_id : null;
1228
+ }
1229
+
1070
1230
  // src/middleware/auto-relogin.ts
1071
1231
  var NO_ACTIVE_WORKSPACE = /no active workspace/i;
1232
+ async function keyRequiredWorkspace(response) {
1233
+ try {
1234
+ return workspaceKeyRequiredOf(await response.clone().json());
1235
+ } catch {
1236
+ return null;
1237
+ }
1238
+ }
1072
1239
  async function isNoActiveWorkspace(response) {
1073
1240
  try {
1074
1241
  return NO_ACTIVE_WORKSPACE.test(await response.clone().text());
@@ -1093,6 +1260,16 @@ function createAutoReloginMiddleware(config) {
1093
1260
  }
1094
1261
  },
1095
1262
  async onResponse({ response, request }) {
1263
+ if (response.status === 409 && config.primeWorkspaceHandler) {
1264
+ const workspaceId = await keyRequiredWorkspace(response);
1265
+ if (workspaceId) {
1266
+ if (!await config.primeWorkspaceHandler(workspaceId)) return response;
1267
+ const retry = cloneCache.get(request) ?? safeClone(request);
1268
+ if (retry) return fetch(retry);
1269
+ config.onRetryCloneFailed?.();
1270
+ return response;
1271
+ }
1272
+ }
1096
1273
  if (response.status === 400 && await isNoActiveWorkspace(response)) {
1097
1274
  let token = null;
1098
1275
  let recovered = await config.reopenWorkspaceHandler?.() ?? false;
@@ -1100,10 +1277,7 @@ function createAutoReloginMiddleware(config) {
1100
1277
  token = await config.reloginHandler();
1101
1278
  recovered = token !== null;
1102
1279
  }
1103
- if (!recovered) {
1104
- config.onReloginFailed?.();
1105
- return response;
1106
- }
1280
+ if (!recovered) return response;
1107
1281
  const retry = cloneCache.get(request) ?? safeClone(request);
1108
1282
  if (retry) {
1109
1283
  if (token) retry.headers.set("Authorization", `Bearer ${token}`);
@@ -1142,8 +1316,6 @@ function createAutoReloginMiddleware(config) {
1142
1316
  console.warn("[API] Could not retry request after re-login (body already consumed)");
1143
1317
  config.onRetryCloneFailed?.();
1144
1318
  }
1145
- } else {
1146
- config.onReloginFailed?.();
1147
1319
  }
1148
1320
  }
1149
1321
  }
@@ -1169,15 +1341,20 @@ function createReloginHandler(deps) {
1169
1341
  const userEmail = deps.authState.getUserEmail();
1170
1342
  if (!userEmail) {
1171
1343
  console.warn("[API] Re-login aborted: no user email");
1344
+ deps.onReloginFailed?.("not-ready");
1172
1345
  return Promise.resolve(null);
1173
1346
  }
1347
+ const fail = (reason) => {
1348
+ deps.onReloginFailed?.(reason);
1349
+ return null;
1350
+ };
1174
1351
  reloginPromise = (async () => {
1175
1352
  try {
1176
1353
  await deps.crypto.ensureReady();
1177
- const session = await deps.sessionStorage.getSession();
1354
+ const session = await deps.persistence.getSession();
1178
1355
  if (!session) {
1179
1356
  console.warn("[API] Re-login aborted: no session");
1180
- return null;
1357
+ return fail("not-ready");
1181
1358
  }
1182
1359
  const ed25519PublicKey = session.signingPrivateKey.slice(32, 64);
1183
1360
  const encryptionKeyPair = deps.crypto.deriveEncryptionKeypair({
@@ -1187,22 +1364,17 @@ function createReloginHandler(deps) {
1187
1364
  const timestamp = Math.floor(Date.now() / 1e3);
1188
1365
  const message = `${userEmail}|${timestamp}`;
1189
1366
  const signature = deps.crypto.signMessage(message, session.signingPrivateKey);
1190
- const ssoState = deps.authState.getSsoState();
1191
- const isSsoAccount = ssoState.isSsoMode || ssoState.isAuth0Authenticated || !!ssoState.auth0AccessToken;
1192
1367
  let ssoToken = null;
1193
- if (deps.ssoTokenProvider) {
1368
+ if (deps.authState.isSsoMode()) {
1194
1369
  try {
1195
- ssoToken = await deps.ssoTokenProvider.getToken();
1370
+ ssoToken = await deps.sso?.getToken() ?? null;
1196
1371
  } catch (error) {
1197
- console.error("[API] Failed to get SSO token from provider:", error);
1372
+ console.error("[API] Failed to get SSO token:", error);
1373
+ }
1374
+ if (!ssoToken) {
1375
+ console.warn("[API] Re-login aborted: SSO account but no token available");
1376
+ return fail("refused");
1198
1377
  }
1199
- }
1200
- if (!ssoToken && ssoState.auth0AccessToken) {
1201
- ssoToken = ssoState.auth0AccessToken;
1202
- }
1203
- if (!ssoToken && isSsoAccount) {
1204
- console.warn("[API] Re-login aborted: SSO account but no token available");
1205
- return null;
1206
1378
  }
1207
1379
  const loginResult = await deps.loginProvider.login({
1208
1380
  email: userEmail,
@@ -1210,11 +1382,15 @@ function createReloginHandler(deps) {
1210
1382
  timestamp,
1211
1383
  ssoToken: ssoToken ?? void 0
1212
1384
  });
1385
+ if (loginResult === "unreachable") {
1386
+ console.warn("[API] Re-login failed: server unreachable");
1387
+ return fail("unreachable");
1388
+ }
1213
1389
  if (!loginResult) {
1214
1390
  console.warn("[API] Re-login failed");
1215
- return null;
1391
+ return fail("refused");
1216
1392
  }
1217
- await deps.sessionStorage.saveSession({
1393
+ await deps.persistence.saveSession({
1218
1394
  signingPrivateKey: session.signingPrivateKey,
1219
1395
  serverSessionKey: loginResult.sessionKey,
1220
1396
  userExtId: loginResult.userExtId,
@@ -1264,7 +1440,7 @@ function createReloginHandler(deps) {
1264
1440
  console.error("[API] Re-login error:", error);
1265
1441
  lastSuccessToken = null;
1266
1442
  lastSuccessTime = 0;
1267
- return null;
1443
+ return fail("refused");
1268
1444
  } finally {
1269
1445
  reloginPromise = null;
1270
1446
  }
@@ -1283,7 +1459,7 @@ function createWorkspaceReopener(deps) {
1283
1459
  const workspaceId = deps.authState.getSelectedWorkspaceId();
1284
1460
  const accessToken = deps.getAccessToken();
1285
1461
  if (!workspaceId || !accessToken) return false;
1286
- const session = await deps.sessionStorage.getSession();
1462
+ const session = await deps.persistence.getSession();
1287
1463
  if (!session?.signingPrivateKey || !session.serverSessionKey) return false;
1288
1464
  await deps.crypto.ensureReady();
1289
1465
  const wrappedKey = await deps.workspaceKeyRefreshProvider.getWrappedKey(
@@ -1313,29 +1489,56 @@ function createWorkspaceReopener(deps) {
1313
1489
  return inFlight;
1314
1490
  };
1315
1491
  }
1492
+ function createAllWorkspacesOpener(deps) {
1493
+ return async function openAllWorkspaces() {
1494
+ const accessToken = deps.getAccessToken();
1495
+ if (!accessToken) return;
1496
+ await deps.crypto.ensureReady();
1497
+ const session = await deps.persistence.getSession();
1498
+ if (!session?.signingPrivateKey) return;
1499
+ const sessionKey = deps.getSessionKey(accessToken);
1500
+ if (!sessionKey) return;
1501
+ const sessionKeyBytes = deps.crypto.fromBase64(sessionKey);
1502
+ let workspaces;
1503
+ try {
1504
+ workspaces = await deps.listWorkspaces(accessToken);
1505
+ } catch {
1506
+ return;
1507
+ }
1508
+ for (const ws of workspaces ?? []) {
1509
+ if (!ws.wrapped_key || !ws.external_id) continue;
1510
+ try {
1511
+ const workspaceKey = deps.unwrapWorkspaceKey(ws.wrapped_key, session.signingPrivateKey);
1512
+ const sealed = await deps.crypto.sealKeyForSession(workspaceKey, sessionKeyBytes);
1513
+ await deps.workspaceOpenProvider.openWorkspace(accessToken, ws.external_id, sealed);
1514
+ } catch (error) {
1515
+ console.warn(`[API] Failed to open workspace ${ws.external_id}:`, error);
1516
+ }
1517
+ }
1518
+ };
1519
+ }
1316
1520
 
1317
- // src/auth/register.ts
1318
- async function register(params, deps) {
1319
- const credentials = await generateRegistrationCredentials(
1320
- {
1321
- email: params.email,
1322
- verification_credential: params.verificationCode,
1323
- given_name: params.firstName,
1324
- family_name: params.lastName,
1325
- picture: params.picture
1326
- },
1327
- params.password,
1328
- deps.deploymentDomain
1329
- );
1330
- const response = await deps.fetchClient.POST("/v1/user/register", {
1331
- body: credentials.request
1332
- });
1333
- if (response.error) {
1334
- throw new Error(
1335
- `Registration failed: ${typeof response.error === "object" && response.error !== null && "detail" in response.error ? response.error.detail : "Unknown error"}`
1521
+ // src/auth/session-claims.ts
1522
+ function readSessionClaims(token) {
1523
+ const payload = token?.split(".")[1];
1524
+ if (!payload) return null;
1525
+ let claims;
1526
+ try {
1527
+ const binary = atob(payload.replace(/-/g, "+").replace(/_/g, "/"));
1528
+ claims = JSON.parse(
1529
+ new TextDecoder().decode(Uint8Array.from(binary, (c) => c.charCodeAt(0)))
1336
1530
  );
1531
+ } catch {
1532
+ return null;
1337
1533
  }
1338
- return { signingPrivateKey: credentials.signingPrivateKey };
1534
+ if (typeof claims !== "object" || claims === null) return null;
1535
+ const { sk, exp } = claims;
1536
+ const sessionKey = typeof sk === "string" && sk ? sk : null;
1537
+ return {
1538
+ sessionKey,
1539
+ sessionId: sessionKey ? sessionKey.slice(0, 12) : null,
1540
+ expiresAt: typeof exp === "number" && Number.isFinite(exp) ? exp * 1e3 : null
1541
+ };
1339
1542
  }
1340
1543
 
1341
1544
  // src/auth/identity.ts
@@ -1362,6 +1565,11 @@ async function unlockIdentityWithPassword(email, password, deploymentDomain) {
1362
1565
  }
1363
1566
 
1364
1567
  // src/auth/login.ts
1568
+ function authRequestError(what, error, status) {
1569
+ const detail = typeof error === "object" && error !== null && "detail" in error ? error.detail : void 0;
1570
+ const text = typeof detail === "string" ? detail : "Unknown error";
1571
+ return Object.assign(new Error(`${what} failed: ${text}`), { detail, status });
1572
+ }
1365
1573
  async function login(params, deps) {
1366
1574
  const credentials = await generateLoginCredentials(
1367
1575
  {
@@ -1371,7 +1579,12 @@ async function login(params, deps) {
1371
1579
  params.password,
1372
1580
  deps.deploymentDomain
1373
1581
  );
1374
- return performLogin(credentials.request, { password: credentials.passwordPrivateKey }, deps);
1582
+ return performLogin(
1583
+ credentials.request,
1584
+ { password: credentials.passwordPrivateKey },
1585
+ deps,
1586
+ params.profile
1587
+ );
1375
1588
  }
1376
1589
  async function loginWithKey(params, deps) {
1377
1590
  const credentials = generateLoginCredentialsFromKey(
@@ -1383,27 +1596,42 @@ async function loginWithKey(params, deps) {
1383
1596
  );
1384
1597
  return performLogin(credentials.request, { identity: credentials.signingPrivateKey }, deps);
1385
1598
  }
1386
- async function performLogin(request, signer, deps) {
1599
+ async function performLogin(request, signer, deps, profile) {
1387
1600
  const response = await deps.fetchClient.POST("/v1/user/login", {
1388
1601
  body: request
1389
1602
  });
1390
1603
  if (response.error || !response.data) {
1391
- throw new Error(
1392
- `Login failed: ${typeof response.error === "object" && response.error !== null && "detail" in response.error ? response.error.detail : "Unknown error"}`
1393
- );
1604
+ throw authRequestError("Login", response.error, response.response?.status);
1394
1605
  }
1395
1606
  const data = response.data;
1396
1607
  const serverSessionKey = base64ToBytes(data.session_key);
1397
1608
  const signingPrivateKey = "password" in signer ? identityFromLoginResponse(signer.password, data) : signer.identity;
1398
- await saveSession({
1609
+ await deps.signIn?.before?.({ user: data.user });
1610
+ await (deps.persistence?.saveSession ?? saveSession)({
1399
1611
  signingPrivateKey,
1400
1612
  serverSessionKey,
1401
1613
  userExtId: data.user.external_id ?? void 0,
1402
1614
  wrappedIdentityKey: data.wrapped_identity_key ?? null
1403
1615
  });
1404
- deps.session.setAccessToken(data.access_token);
1405
- deps.session.setUser(request.email, data.user.external_id ?? void 0);
1616
+ const email = request.email;
1617
+ deps.session.signIn({
1618
+ accessToken: data.access_token,
1619
+ isSsoMode: !!request.sso_token,
1620
+ serverSessionKey,
1621
+ user: {
1622
+ ...data.user,
1623
+ email,
1624
+ given_name: profile?.given_name ?? data.user.given_name ?? email.split("@")[0],
1625
+ family_name: profile?.family_name ?? data.user.family_name,
1626
+ picture: profile?.picture ?? data.user.picture ?? null,
1627
+ parent_ext_id: data.user.parent_ext_id ?? null,
1628
+ is_sso: data.user.is_sso ?? false,
1629
+ is_identity_verified: data.user.is_identity_verified ?? false
1630
+ }
1631
+ });
1632
+ deps.signIn?.after?.({ response: data, serverSessionKey });
1406
1633
  return {
1634
+ response: data,
1407
1635
  accessToken: data.access_token,
1408
1636
  userExtId: data.user.external_id ?? void 0,
1409
1637
  parentExtId: data.user.parent_ext_id ?? null,
@@ -1420,10 +1648,26 @@ async function performLogin(request, signer, deps) {
1420
1648
  };
1421
1649
  }
1422
1650
 
1423
- // src/auth/logout.ts
1424
- async function logout(session) {
1425
- session.clear();
1426
- await clearAllData();
1651
+ // src/auth/register.ts
1652
+ async function register(params, deps) {
1653
+ const credentials = await generateRegistrationCredentials(
1654
+ {
1655
+ email: params.email,
1656
+ verification_credential: params.verificationCode,
1657
+ given_name: params.firstName,
1658
+ family_name: params.lastName,
1659
+ picture: params.picture
1660
+ },
1661
+ params.password,
1662
+ deps.deploymentDomain
1663
+ );
1664
+ const response = await deps.fetchClient.POST("/v1/user/register", {
1665
+ body: credentials.request
1666
+ });
1667
+ if (response.error) {
1668
+ throw authRequestError("Registration", response.error, response.response?.status);
1669
+ }
1670
+ return { signingPrivateKey: credentials.signingPrivateKey };
1427
1671
  }
1428
1672
 
1429
1673
  // src/auth/change-password.ts
@@ -1462,29 +1706,6 @@ async function changePassword(params, deps) {
1462
1706
  return { wrappedIdentityKey: request.wrapped_identity_key };
1463
1707
  }
1464
1708
 
1465
- // src/auth/session-claims.ts
1466
- function readSessionClaims(token) {
1467
- const payload = token?.split(".")[1];
1468
- if (!payload) return null;
1469
- let claims;
1470
- try {
1471
- const binary = atob(payload.replace(/-/g, "+").replace(/_/g, "/"));
1472
- claims = JSON.parse(
1473
- new TextDecoder().decode(Uint8Array.from(binary, (c) => c.charCodeAt(0)))
1474
- );
1475
- } catch {
1476
- return null;
1477
- }
1478
- if (typeof claims !== "object" || claims === null) return null;
1479
- const { sk, exp } = claims;
1480
- const sessionKey = typeof sk === "string" && sk ? sk : null;
1481
- return {
1482
- sessionKey,
1483
- sessionId: sessionKey ? sessionKey.slice(0, 12) : null,
1484
- expiresAt: typeof exp === "number" && Number.isFinite(exp) ? exp * 1e3 : null
1485
- };
1486
- }
1487
-
1488
1709
  // src/client.ts
1489
1710
  var API_PREFIX = "/v1";
1490
1711
  var RETRYABLE_CODES = /* @__PURE__ */ new Set([
@@ -1521,24 +1742,42 @@ function createRetryFetch() {
1521
1742
  throw lastError;
1522
1743
  };
1523
1744
  }
1745
+ var HTTP_METHODS = ["GET", "PUT", "POST", "DELETE", "OPTIONS", "HEAD", "PATCH", "TRACE"];
1746
+ function readBaseUrlPerRequest(fetchClient, base) {
1747
+ const methods = fetchClient;
1748
+ for (const method of HTTP_METHODS) {
1749
+ const call = methods[method].bind(fetchClient);
1750
+ methods[method] = (url, init) => call(url, { ...init, baseUrl: base() });
1751
+ }
1752
+ const request = methods.request.bind(fetchClient);
1753
+ methods.request = (method, url, init) => request(method, url, { ...init, baseUrl: base() });
1754
+ }
1755
+ var deploymentDomainOf = ({ deploymentDomain }) => typeof deploymentDomain === "function" ? deploymentDomain() : deploymentDomain;
1524
1756
  function createArbiClient(options) {
1525
- const { fetch: fetch2, session, relogin, reopenWorkspace } = options.transport ?? createTransport(options);
1526
- const authDeps = { fetchClient: fetch2, session, deploymentDomain: options.deploymentDomain };
1757
+ const { fetch: fetch2, session, persistence, workspaceKeys, auth } = createTransport(options);
1758
+ const authDeps = {
1759
+ fetchClient: fetch2,
1760
+ session,
1761
+ get deploymentDomain() {
1762
+ return deploymentDomainOf(options);
1763
+ },
1764
+ persistence,
1765
+ signIn: options.signIn
1766
+ };
1527
1767
  return {
1528
1768
  fetch: fetch2,
1769
+ workspaceKeys,
1529
1770
  session,
1530
1771
  auth: {
1531
1772
  register: (params) => register(params, authDeps),
1532
1773
  login: (params) => login(params, authDeps),
1533
1774
  loginWithKey: (params) => loginWithKey(params, authDeps),
1534
- logout: () => logout(session),
1535
1775
  changePassword: (params) => changePassword(params, authDeps),
1536
- relogin,
1537
- reopenWorkspace
1776
+ ...auth
1538
1777
  },
1539
1778
  crypto: {
1540
1779
  initSodium,
1541
- generateUserKeypairs: (email, password) => generateUserKeypairs(email, password, options.deploymentDomain),
1780
+ generateUserKeypairs: (email, password) => generateUserKeypairs(email, password, deploymentDomainOf(options)),
1542
1781
  signMessage,
1543
1782
  sealedBoxDecrypt,
1544
1783
  sealedBoxEncrypt,
@@ -1564,24 +1803,22 @@ function createArbiClient(options) {
1564
1803
  }
1565
1804
  function createTransport(options) {
1566
1805
  const {
1567
- baseUrl,
1568
- reloginExcludePatterns = [`${API_PREFIX}/user/login`],
1806
+ reloginExcludePatterns = [`${API_PREFIX}/user/login`, `${API_PREFIX}/user/logout`],
1569
1807
  credentials = "include",
1570
- ssoTokenProvider = null,
1808
+ sso = null,
1809
+ middleware = [],
1571
1810
  onReloginSuccess,
1572
- onSessionExpired,
1573
- sessionStorage: sessionStorageOverride,
1811
+ onRetryCloneFailed,
1574
1812
  userEmail
1575
1813
  } = options;
1576
- const sessionStorage = sessionStorageOverride ?? {
1577
- getSession,
1578
- saveSession
1579
- };
1814
+ const persistence = options.persistence ?? { getSession, saveSession };
1815
+ const baseUrlOption = options.baseUrl;
1816
+ const base = typeof baseUrlOption === "function" ? baseUrlOption : () => baseUrlOption;
1580
1817
  const retryFetch = createRetryFetch();
1581
- const session = createSessionManager();
1818
+ const session = options.session ?? createSessionManager();
1582
1819
  if (userEmail) session.setUser(userEmail);
1583
1820
  const tokenProvider = createTokenProvider(session);
1584
- const authState = createAuthStateProvider(session);
1821
+ const authState = createAuthStateProvider(session, options.selectedWorkspace);
1585
1822
  const cryptoProvider = {
1586
1823
  ensureReady: initSodium,
1587
1824
  signMessage,
@@ -1590,11 +1827,10 @@ function createTransport(options) {
1590
1827
  sealKeyForSession,
1591
1828
  fromBase64: base64ToBytes
1592
1829
  };
1593
- let reloginUnreachable = false;
1594
1830
  const loginProvider = {
1595
1831
  async login(payload) {
1596
1832
  const rawFetch = createFetchClient__default.default({
1597
- baseUrl,
1833
+ baseUrl: base(),
1598
1834
  credentials,
1599
1835
  fetch: retryFetch
1600
1836
  });
@@ -1610,8 +1846,7 @@ function createTransport(options) {
1610
1846
  });
1611
1847
  } catch (error) {
1612
1848
  console.warn("[API] Login call unreachable:", error);
1613
- reloginUnreachable = true;
1614
- return null;
1849
+ return "unreachable";
1615
1850
  }
1616
1851
  if (response.error || !response.data) {
1617
1852
  console.warn("[API] Login call failed:", response.error);
@@ -1626,81 +1861,219 @@ function createTransport(options) {
1626
1861
  };
1627
1862
  }
1628
1863
  };
1629
- const workspaceKeyRefreshProvider = {
1630
- async getWrappedKey(accessToken, workspaceId) {
1631
- const workspacesResponse = await retryFetch(`${baseUrl}${API_PREFIX}/user/workspaces`, {
1632
- method: "GET",
1633
- headers: {
1634
- Authorization: `Bearer ${accessToken}`,
1635
- "Content-Type": "application/json"
1636
- },
1637
- credentials
1638
- });
1639
- if (!workspacesResponse.ok) return null;
1640
- const workspacesData = await workspacesResponse.json();
1641
- const workspace = workspacesData.find(
1642
- (w) => w.external_id === workspaceId
1864
+ async function listWorkspaces(accessToken) {
1865
+ const response = await retryFetch(`${base()}${API_PREFIX}/user/workspaces`, {
1866
+ method: "GET",
1867
+ headers: {
1868
+ Authorization: `Bearer ${accessToken}`,
1869
+ "Content-Type": "application/json"
1870
+ },
1871
+ credentials
1872
+ });
1873
+ return response.ok ? response.json() : null;
1874
+ }
1875
+ const workspaceKeys = createWorkspaceKeys({
1876
+ crypto: cryptoProvider,
1877
+ unwrapWorkspaceKey,
1878
+ persistence,
1879
+ getAccessToken: () => session.getState().accessToken,
1880
+ getServerSessionKey: () => session.getState().serverSessionKey,
1881
+ listWorkspaces,
1882
+ async primeKey(accessToken, workspaceId, sealedKey) {
1883
+ const response = await retryFetch(
1884
+ `${base()}${API_PREFIX}/workspace/${workspaceId}/prime-key`,
1885
+ {
1886
+ method: "POST",
1887
+ headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json" },
1888
+ body: JSON.stringify({ workspace_key: sealedKey }),
1889
+ credentials
1890
+ }
1643
1891
  );
1644
- return workspace?.wrapped_key ?? null;
1892
+ if (!response.ok) throw new Error(`Could not unlock the workspace (${response.status})`);
1893
+ }
1894
+ });
1895
+ let keysUser = session.getState().signedIn ? session.getState().userExtId : null;
1896
+ session.subscribe((state) => {
1897
+ const user = state.signedIn ? state.userExtId : null;
1898
+ if (user !== keysUser) workspaceKeys.clear();
1899
+ keysUser = user;
1900
+ });
1901
+ const workspaceKeyRefreshProvider = {
1902
+ async getWrappedKey(_accessToken, workspaceId) {
1903
+ return (await workspaceKeys.wrappedKey(workspaceId).catch(() => null))?.wrappedKey ?? null;
1645
1904
  }
1646
1905
  };
1647
1906
  const workspaceOpenProvider = {
1648
1907
  async openWorkspace(accessToken, workspaceId, encryptedWorkspaceKey) {
1908
+ const query = "?include_documents=false&include_conversations=false&include_tags=false";
1649
1909
  try {
1650
- const response = await retryFetch(`${baseUrl}${API_PREFIX}/workspace/${workspaceId}/open`, {
1651
- method: "POST",
1652
- headers: {
1653
- Authorization: `Bearer ${accessToken}`,
1654
- "Content-Type": "application/json"
1655
- },
1656
- body: JSON.stringify({ workspace_key: encryptedWorkspaceKey }),
1657
- credentials
1658
- });
1910
+ const response = await retryFetch(
1911
+ `${base()}${API_PREFIX}/workspace/${workspaceId}/open${query}`,
1912
+ {
1913
+ method: "POST",
1914
+ headers: {
1915
+ Authorization: `Bearer ${accessToken}`,
1916
+ "Content-Type": "application/json"
1917
+ },
1918
+ body: JSON.stringify({ workspace_key: encryptedWorkspaceKey }),
1919
+ credentials
1920
+ }
1921
+ );
1659
1922
  return response.ok ? true : null;
1660
1923
  } catch {
1661
1924
  return null;
1662
1925
  }
1663
1926
  }
1664
1927
  };
1665
- const rawReloginHandler = createReloginHandler({
1928
+ let lastFailure = null;
1929
+ const expiredListeners = /* @__PURE__ */ new Set();
1930
+ const rawRelogin = createReloginHandler({
1666
1931
  crypto: cryptoProvider,
1667
- sessionStorage,
1932
+ persistence,
1668
1933
  authState,
1669
- ssoTokenProvider,
1934
+ sso,
1670
1935
  loginProvider,
1671
1936
  workspaceKeyRefreshProvider,
1672
1937
  workspaceOpenProvider,
1673
- onReloginSuccess
1938
+ // The new server session key goes into the session before the token: whatever re-renders on
1939
+ // the token reads the key that goes with it.
1940
+ async onReloginSuccess(data) {
1941
+ session.setServerSessionKey(data.serverSessionKey);
1942
+ await onReloginSuccess?.(data);
1943
+ },
1944
+ onReloginFailed: (reason) => {
1945
+ lastFailure = reason;
1946
+ }
1674
1947
  });
1675
- const reloginHandler = () => {
1676
- reloginUnreachable = false;
1677
- return rawReloginHandler();
1678
- };
1948
+ async function relogin() {
1949
+ const tokenBefore = session.getState().accessToken;
1950
+ const token = await rawRelogin();
1951
+ if (token) return token;
1952
+ const tokenNow = session.getState().accessToken;
1953
+ if (tokenNow && tokenNow !== tokenBefore) return tokenNow;
1954
+ if (lastFailure === "refused") {
1955
+ lastFailure = null;
1956
+ session.clear();
1957
+ for (const listener of [...expiredListeners]) listener();
1958
+ }
1959
+ return null;
1960
+ }
1679
1961
  const reopenWorkspace = createWorkspaceReopener({
1680
1962
  crypto: cryptoProvider,
1681
- sessionStorage,
1963
+ persistence,
1682
1964
  authState,
1683
1965
  getAccessToken: () => session.getState().accessToken,
1684
1966
  workspaceKeyRefreshProvider,
1685
1967
  workspaceOpenProvider
1686
1968
  });
1969
+ const openAllWorkspaces = createAllWorkspacesOpener({
1970
+ crypto: cryptoProvider,
1971
+ persistence,
1972
+ getAccessToken: () => session.getState().accessToken,
1973
+ getSessionKey: (accessToken) => readSessionClaims(accessToken)?.sessionKey ?? null,
1974
+ unwrapWorkspaceKey,
1975
+ listWorkspaces: async (accessToken) => {
1976
+ const list = await listWorkspaces(accessToken);
1977
+ return list && workspaceKeys.remember(list);
1978
+ },
1979
+ workspaceOpenProvider
1980
+ });
1687
1981
  const fetchClient = createFetchClient__default.default({
1688
- baseUrl,
1982
+ baseUrl: base(),
1689
1983
  credentials,
1690
1984
  fetch: retryFetch
1691
1985
  });
1986
+ if (typeof baseUrlOption === "function") readBaseUrlPerRequest(fetchClient, base);
1692
1987
  fetchClient.use(createBearerAuthMiddleware({ tokenProvider }));
1988
+ for (const m of middleware) fetchClient.use(m);
1693
1989
  fetchClient.use(
1694
1990
  createAutoReloginMiddleware({
1695
- reloginHandler,
1991
+ reloginHandler: relogin,
1696
1992
  reloginExcludePatterns,
1697
1993
  reopenWorkspaceHandler: reopenWorkspace,
1698
- onReloginFailed: () => {
1699
- if (!reloginUnreachable && session.getState().userEmail) onSessionExpired?.();
1700
- }
1994
+ primeWorkspaceHandler: (workspaceId) => workspaceKeys.prime(workspaceId, { force: true }).then(
1995
+ () => true,
1996
+ (error) => {
1997
+ console.warn("[API] Could not unlock workspace", workspaceId, error);
1998
+ return false;
1999
+ }
2000
+ ),
2001
+ onRetryCloneFailed
1701
2002
  })
1702
2003
  );
1703
- return { fetch: fetchClient, session, relogin: reloginHandler, reopenWorkspace };
2004
+ return {
2005
+ fetch: fetchClient,
2006
+ session,
2007
+ persistence,
2008
+ workspaceKeys,
2009
+ auth: {
2010
+ relogin,
2011
+ reopenWorkspace,
2012
+ openAllWorkspaces,
2013
+ async logout({ everywhere = false } = {}) {
2014
+ if (session.getState().accessToken) {
2015
+ try {
2016
+ await fetchClient.POST("/v1/user/logout", {
2017
+ params: { query: { all_sessions: everywhere } }
2018
+ });
2019
+ } catch (error) {
2020
+ console.warn("[API] Server logout failed:", error);
2021
+ }
2022
+ }
2023
+ session.clear();
2024
+ if (everywhere) await clearAllData();
2025
+ },
2026
+ async unlock({ email, password }) {
2027
+ const saved = await persistence.getSession();
2028
+ if (!saved?.signingPrivateKey) throw new Error("no-session");
2029
+ let signingPrivateKey;
2030
+ try {
2031
+ signingPrivateKey = await unlockIdentityWithPassword(
2032
+ email,
2033
+ password,
2034
+ deploymentDomainOf(options)
2035
+ );
2036
+ } catch {
2037
+ throw new Error("bad-password");
2038
+ }
2039
+ const { publicKey } = encryptionKeypairFromSigningKey(signingPrivateKey);
2040
+ session.signIn({
2041
+ accessToken: null,
2042
+ isSsoMode: false,
2043
+ serverSessionKey: saved.serverSessionKey ?? null,
2044
+ user: {
2045
+ email,
2046
+ given_name: email.split("@")[0],
2047
+ family_name: null,
2048
+ picture: null,
2049
+ external_id: saved.userExtId ?? "",
2050
+ parent_ext_id: null,
2051
+ encryption_public_key: bytesToBase64(publicKey),
2052
+ is_sso: false,
2053
+ is_identity_verified: false,
2054
+ temporary_access_only: false
2055
+ }
2056
+ });
2057
+ },
2058
+ async restore() {
2059
+ if (!session.getState().signedIn) return false;
2060
+ const keys = await persistence.getSession().catch(() => null);
2061
+ if (session.getState().serverSessionKey) return true;
2062
+ if (keys?.signingPrivateKey && keys.serverSessionKey) {
2063
+ session.setServerSessionKey(keys.serverSessionKey);
2064
+ return true;
2065
+ }
2066
+ session.clear();
2067
+ return false;
2068
+ },
2069
+ onSessionExpired(listener) {
2070
+ expiredListeners.add(listener);
2071
+ return () => {
2072
+ expiredListeners.delete(listener);
2073
+ };
2074
+ }
2075
+ }
2076
+ };
1704
2077
  }
1705
2078
 
1706
2079
  // src/websocket.ts
@@ -1728,6 +2101,7 @@ function isMessageType(msg, type) {
1728
2101
  return msg.type === type;
1729
2102
  }
1730
2103
 
2104
+ exports.ACCESS_TOKEN_STORAGE_KEY = ACCESS_TOKEN_STORAGE_KEY;
1731
2105
  exports.API_PREFIX = API_PREFIX;
1732
2106
  exports.CALL_KEY_BYTES = CALL_KEY_BYTES;
1733
2107
  exports.CHAT_KEY_BYTES = CHAT_KEY_BYTES;
@@ -1735,11 +2109,14 @@ exports.ENCRYPTED_RECOVERY_KEY_SIZE = ENCRYPTED_RECOVERY_KEY_SIZE;
1735
2109
  exports.RECOVERY_KEY_ERRORS = RECOVERY_KEY_ERRORS;
1736
2110
  exports.SECURE_MAIL_KEY_BYTES = SECURE_MAIL_KEY_BYTES;
1737
2111
  exports.UNENCRYPTED_RECOVERY_KEY_SIZE = UNENCRYPTED_RECOVERY_KEY_SIZE;
2112
+ exports.authRequestError = authRequestError;
1738
2113
  exports.base64Decode = base64Decode;
1739
2114
  exports.base64Encode = base64Encode;
1740
2115
  exports.base64ToBytes = base64ToBytes;
1741
2116
  exports.base64UrlToBytes = base64UrlToBytes;
2117
+ exports.browserSessionStateStorage = browserSessionStateStorage;
1742
2118
  exports.buildCallFragment = buildCallFragment;
2119
+ exports.buildCallLink = buildCallLink;
1743
2120
  exports.buildWebSocketUrl = buildWebSocketUrl;
1744
2121
  exports.bytesToBase64 = bytesToBase64;
1745
2122
  exports.bytesToBase64Url = bytesToBase64Url;
@@ -1748,6 +2125,7 @@ exports.changePassword = changePassword;
1748
2125
  exports.clearAllData = clearAllData;
1749
2126
  exports.clearSession = clearSession;
1750
2127
  exports.computeSharedSecret = computeSharedSecret;
2128
+ exports.createAllWorkspacesOpener = createAllWorkspacesOpener;
1751
2129
  exports.createArbiClient = createArbiClient;
1752
2130
  exports.createAuthMessage = createAuthMessage;
1753
2131
  exports.createAutoReloginMiddleware = createAutoReloginMiddleware;
@@ -1758,6 +2136,7 @@ exports.createRecoveryFile = createRecoveryFile;
1758
2136
  exports.createRecoveryUrl = createRecoveryUrl;
1759
2137
  exports.createReloginHandler = createReloginHandler;
1760
2138
  exports.createSessionManager = createSessionManager;
2139
+ exports.createWorkspaceKeys = createWorkspaceKeys;
1761
2140
  exports.createWorkspaceReopener = createWorkspaceReopener;
1762
2141
  exports.decodeCallKey = decodeCallKey;
1763
2142
  exports.decodeSecureMailKey = decodeSecureMailKey;
@@ -1797,6 +2176,7 @@ exports.initSodium = initSodium;
1797
2176
  exports.initializeDatabase = initializeDatabase;
1798
2177
  exports.isEncryptedRecoveryKey = isEncryptedRecoveryKey;
1799
2178
  exports.isMessageType = isMessageType;
2179
+ exports.openCallKeyOf = openCallKeyOf;
1800
2180
  exports.openSealedCallKey = openSealedCallKey;
1801
2181
  exports.openSealedChatKey = openSealedChatKey;
1802
2182
  exports.openSecureMail = openSecureMail;
@@ -1808,6 +2188,7 @@ exports.prewarmKeyPairs = prewarmKeyPairs;
1808
2188
  exports.readSessionClaims = readSessionClaims;
1809
2189
  exports.saveSession = saveSession;
1810
2190
  exports.sealCallKey = sealCallKey;
2191
+ exports.sealCallKeyForPeers = sealCallKeyForPeers;
1811
2192
  exports.sealChatKey = sealChatKey;
1812
2193
  exports.sealKeyForSession = sealKeyForSession;
1813
2194
  exports.sealSecureMail = sealSecureMail;
@@ -1820,6 +2201,7 @@ exports.unwrapIdentityKey = unwrapIdentityKey;
1820
2201
  exports.unwrapWorkspaceKey = unwrapWorkspaceKey;
1821
2202
  exports.updateWrappedIdentityKey = updateWrappedIdentityKey;
1822
2203
  exports.validateRecoveryKeyFile = validateRecoveryKeyFile;
2204
+ exports.workspaceKeyRequiredOf = workspaceKeyRequiredOf;
1823
2205
  exports.wrapIdentityKey = wrapIdentityKey;
1824
2206
  //# sourceMappingURL=index.cjs.map
1825
2207
  //# sourceMappingURL=index.cjs.map