@arbidocs/client 0.5.25 → 0.5.27

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
@@ -1116,6 +1116,67 @@ function createBearerAuthMiddleware(config) {
1116
1116
  };
1117
1117
  }
1118
1118
 
1119
+ // src/session-gate.ts
1120
+ function abortError() {
1121
+ return new DOMException("The session ended.", "AbortError");
1122
+ }
1123
+ function isAbortError(error) {
1124
+ for (let e = error, depth = 0; e && typeof e === "object" && depth < 5; depth++) {
1125
+ if (e.name === "AbortError") return true;
1126
+ e = e.cause;
1127
+ }
1128
+ return false;
1129
+ }
1130
+ function eitherSignal(a, b) {
1131
+ if (!b) return a;
1132
+ if (typeof AbortSignal.any === "function") return AbortSignal.any([a, b]);
1133
+ const controller = new AbortController();
1134
+ const abort = (signal) => () => controller.abort(signal.reason);
1135
+ if (a.aborted || b.aborted) controller.abort((a.aborted ? a : b).reason);
1136
+ else {
1137
+ a.addEventListener("abort", abort(a), { once: true });
1138
+ b.addEventListener("abort", abort(b), { once: true });
1139
+ }
1140
+ return controller.signal;
1141
+ }
1142
+ function createSessionGate() {
1143
+ let controller = new AbortController();
1144
+ let closed = false;
1145
+ return {
1146
+ get signal() {
1147
+ return controller.signal;
1148
+ },
1149
+ get closed() {
1150
+ return closed;
1151
+ },
1152
+ close() {
1153
+ closed = true;
1154
+ },
1155
+ abort() {
1156
+ controller.abort(abortError());
1157
+ },
1158
+ reopen() {
1159
+ if (controller.signal.aborted) controller = new AbortController();
1160
+ closed = false;
1161
+ }
1162
+ };
1163
+ }
1164
+ function gatedFetch(gate, fetch2) {
1165
+ return (input, init) => {
1166
+ if (gate.closed) return Promise.reject(abortError());
1167
+ const own = init?.signal ?? (input instanceof Request ? input.signal : void 0);
1168
+ return fetch2(input, { ...init, signal: eitherSignal(gate.signal, own) });
1169
+ };
1170
+ }
1171
+ function createSessionGateMiddleware(gate) {
1172
+ return {
1173
+ onRequest({ request }) {
1174
+ const signal = gate.closed ? AbortSignal.abort(abortError()) : gate.signal;
1175
+ return new Request(request, { signal: eitherSignal(signal, request.signal) });
1176
+ }
1177
+ };
1178
+ }
1179
+
1119
1180
  // src/workspace-keys.ts
1120
1181
  function createWorkspaceKeys(deps) {
1121
1182
  const wrapped = /* @__PURE__ */ new Map();
@@ -1912,6 +1973,8 @@ function createTransport(options) {
1912
1973
  const baseUrlOption = options.baseUrl;
1913
1974
  const base = typeof baseUrlOption === "function" ? baseUrlOption : () => baseUrlOption;
1914
1975
  const retryFetch = createRetryFetch();
1976
+ const gate = createSessionGate();
1977
+ const sessionFetch = gatedFetch(gate, retryFetch);
1915
1978
  const session = options.session ?? createSessionManager();
1916
1979
  if (userEmail) session.setUser(userEmail);
1917
1980
  const tokenProvider = createTokenProvider(session);
@@ -1929,7 +1992,7 @@ function createTransport(options) {
1929
1992
  const rawFetch = createFetchClient__default.default({
1930
1993
  baseUrl: base(),
1931
1994
  credentials,
1932
- fetch: retryFetch
1995
+ fetch: sessionFetch
1933
1996
  });
1934
1997
  let response;
1935
1998
  try {
@@ -1942,7 +2005,7 @@ function createTransport(options) {
1942
2005
  }
1943
2006
  });
1944
2007
  } catch (error) {
1945
- console.warn("[API] Login call unreachable:", error);
2008
+ if (!isAbortError(error)) console.warn("[API] Login call unreachable:", error);
1946
2009
  return "unreachable";
1947
2010
  }
1948
2011
  if (response.error || !response.data) {
@@ -1959,7 +2022,7 @@ function createTransport(options) {
1959
2022
  }
1960
2023
  };
1961
2024
  async function listWorkspaces(accessToken) {
1962
- const response = await retryFetch(`${base()}${API_PREFIX}/user/workspaces`, {
2025
+ const response = await sessionFetch(`${base()}${API_PREFIX}/user/workspaces`, {
1963
2026
  method: "GET",
1964
2027
  headers: {
1965
2028
  Authorization: `Bearer ${accessToken}`,
@@ -1977,7 +2040,7 @@ function createTransport(options) {
1977
2040
  getServerSessionKey: () => session.getState().serverSessionKey,
1978
2041
  listWorkspaces,
1979
2042
  async primeKey(accessToken, workspaceId, sealedKey) {
1980
- const response = await retryFetch(
2043
+ const response = await sessionFetch(
1981
2044
  `${base()}${API_PREFIX}/workspace/${workspaceId}/prime-key`,
1982
2045
  {
1983
2046
  method: "POST",
@@ -2008,7 +2071,7 @@ function createTransport(options) {
2008
2071
  async openWorkspace(accessToken, workspaceId, encryptedWorkspaceKey) {
2009
2072
  const query = "?include_documents=false&include_conversations=false&include_tags=false";
2010
2073
  try {
2011
- const response = await retryFetch(
2074
+ const response = await sessionFetch(
2012
2075
  `${base()}${API_PREFIX}/workspace/${workspaceId}/open${query}`,
2013
2076
  {
2014
2077
  method: "POST",
@@ -2038,6 +2101,7 @@ function createTransport(options) {
2038
2101
  };
2039
2102
  let lastFailure = null;
2040
2103
  const expiredListeners = /* @__PURE__ */ new Set();
2104
+ const signingOutListeners = /* @__PURE__ */ new Set();
2041
2105
  const rawRelogin = createReloginHandler({
2042
2106
  crypto: cryptoProvider,
2043
2107
  persistence,
@@ -2057,6 +2121,7 @@ function createTransport(options) {
2057
2121
  }
2058
2122
  });
2059
2123
  async function relogin() {
2124
+ if (gate.closed) return null;
2060
2125
  const tokenBefore = session.getState().accessToken;
2061
2126
  const token = await rawRelogin();
2062
2127
  if (token) return token;
@@ -2078,7 +2143,7 @@ function createTransport(options) {
2078
2143
  workspaceOpenProvider: rawWorkspaceOpenProvider
2079
2144
  });
2080
2145
  let reopening = null;
2081
- const reopenWorkspace = () => reopening ??= workspaceOpens.run(() => authState.getSelectedWorkspaceId(), reopenSelected).finally(() => {
2146
+ const reopenWorkspace = () => gate.closed ? Promise.resolve(false) : reopening ??= workspaceOpens.run(() => authState.getSelectedWorkspaceId(), reopenSelected).finally(() => {
2082
2147
  reopening = null;
2083
2148
  });
2084
2149
  const openEveryWorkspace = createAllWorkspacesOpener({
@@ -2101,6 +2166,7 @@ function createTransport(options) {
2101
2166
  fetch: retryFetch
2102
2167
  });
2103
2168
  if (typeof baseUrlOption === "function") readBaseUrlPerRequest(fetchClient, base);
2169
+ fetchClient.use(createSessionGateMiddleware(gate));
2104
2170
  fetchClient.use(createBearerAuthMiddleware({ tokenProvider }));
2105
2171
  for (const m of middleware) fetchClient.use(m);
2106
2172
  fetchClient.use(
@@ -2128,18 +2194,39 @@ function createTransport(options) {
2128
2194
  relogin,
2129
2195
  reopenWorkspace,
2130
2196
  openAllWorkspaces,
2197
+ onSigningOut(listener) {
2198
+ signingOutListeners.add(listener);
2199
+ return () => {
2200
+ signingOutListeners.delete(listener);
2201
+ };
2202
+ },
2203
+ isSigningOut: () => gate.closed,
2204
+ sessionSignal: () => gate.signal,
2131
2205
  async logout({ everywhere = false } = {}) {
2132
- if (session.getState().accessToken) {
2206
+ gate.close();
2207
+ for (const listener of [...signingOutListeners]) {
2133
2208
  try {
2134
- await fetchClient.POST("/v1/user/logout", {
2135
- params: { query: { all_sessions: everywhere } }
2136
- });
2209
+ listener();
2137
2210
  } catch (error) {
2138
- console.warn("[API] Server logout failed:", error);
2211
+ console.warn("[API] A sign-out listener failed:", error);
2139
2212
  }
2140
2213
  }
2141
- session.clear();
2142
- if (everywhere) await clearAllData();
2214
+ gate.abort();
2215
+ try {
2216
+ const accessToken = session.getState().accessToken;
2217
+ if (accessToken) {
2218
+ const query = `?all_sessions=${everywhere}`;
2219
+ await retryFetch(`${base()}${API_PREFIX}/user/logout${query}`, {
2220
+ method: "POST",
2221
+ headers: { Authorization: `Bearer ${accessToken}` },
2222
+ credentials
2223
+ }).catch((error) => console.warn("[API] Server logout failed:", error));
2224
+ }
2225
+ session.clear();
2226
+ if (everywhere) await clearAllData();
2227
+ } finally {
2228
+ gate.reopen();
2229
+ }
2143
2230
  },
2144
2231
  async unlock({ email, password }) {
2145
2232
  const saved = await persistence.getSession();
@@ -2294,6 +2381,7 @@ exports.hasSession = hasSession;
2294
2381
  exports.identityFromLoginResponse = identityFromLoginResponse;
2295
2382
  exports.initSodium = initSodium;
2296
2383
  exports.initializeDatabase = initializeDatabase;
2384
+ exports.isAbortError = isAbortError;
2297
2385
  exports.isEncryptedRecoveryKey = isEncryptedRecoveryKey;
2298
2386
  exports.isMessageType = isMessageType;
2299
2387
  exports.openCallKeyOf = openCallKeyOf;