@arbidocs/sdk 0.3.109 → 0.3.110

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.
@@ -1,4 +1,4 @@
1
- import { SessionStorageProvider, ArbiClient, LoginResult, components, WebSocketServerMessage, WsTaskUpdateMessage, KeyPair } from '@arbidocs/client';
1
+ import { SessionStorageProvider, ArbiClient, LoginResult, components, WebSocketServerMessage, WsAuthResultMessage, WsTaskUpdateMessage, KeyPair } from '@arbidocs/client';
2
2
 
3
3
  /**
4
4
  * Core error types and throwing error utilities.
@@ -470,6 +470,12 @@ interface ConnectOptions {
470
470
  accessToken: string;
471
471
  onMessage: (msg: WebSocketServerMessage) => void;
472
472
  onClose?: (code: number, reason: string) => void;
473
+ /**
474
+ * Called once the server accepts auth (`auth_result { success: true }`). The
475
+ * full message is passed so callers can read fields like `new_messages`. The
476
+ * auth handshake itself is owned by the transport — this is a notification.
477
+ */
478
+ onAuthenticated?: (msg: WsAuthResultMessage) => void;
473
479
  }
474
480
  interface ReconnectOptions extends ConnectOptions {
475
481
  /**
@@ -497,6 +503,12 @@ interface ReconnectOptions extends ConnectOptions {
497
503
  onReconnecting?: (attempt: number, maxRetries: number) => void;
498
504
  onReconnected?: () => void;
499
505
  onReconnectFailed?: () => void;
506
+ /**
507
+ * Decide whether to reconnect after a given close. Return `false` to stop —
508
+ * e.g. a server-initiated single-session eviction (code 1008), where
509
+ * reconnecting would just get kicked again. Default: always reconnect.
510
+ */
511
+ shouldReconnect?: (code: number, reason: string) => boolean;
500
512
  }
501
513
  interface ReconnectableWsConnection {
502
514
  close: () => void;
@@ -511,7 +523,10 @@ declare function connectWebSocket(options: ConnectOptions): Promise<WsConnection
511
523
  /**
512
524
  * Connect WebSocket with automatic reconnection on disconnect.
513
525
  *
514
- * Initial connection throws on failure (same as connectWebSocket).
526
+ * The initial connection is auth-symmetric with the reconnect loop: if the first
527
+ * attempt is rejected for auth reasons and `refreshAuth` is provided, it re-logs
528
+ * in and retries once with the fresh token before throwing. Any other failure —
529
+ * or a still-failing retry — throws to the caller.
515
530
  * After successful auth, disconnects trigger exponential backoff reconnection:
516
531
  * - a fresh token is resolved before every attempt (see `getAccessToken`), so a
517
532
  * token rotated elsewhere is picked up rather than reusing a stale one;
@@ -1630,6 +1645,18 @@ declare class Arbi {
1630
1645
  get isLoggedIn(): boolean;
1631
1646
  /** Check if a workspace is selected. */
1632
1647
  get hasWorkspace(): boolean;
1648
+ /**
1649
+ * Live connection info for opening a raw transport (e.g. a WebSocket).
1650
+ *
1651
+ * Reads the access token from the session on each call, so a token refreshed
1652
+ * elsewhere is reflected on the next (re)connect. Returns `null` when there is
1653
+ * no client/token yet (not logged in) rather than throwing, so callers can
1654
+ * poll for readiness instead of guarding against exceptions.
1655
+ */
1656
+ getConnectionInfo(): {
1657
+ baseUrl: string;
1658
+ accessToken: string;
1659
+ } | null;
1633
1660
  private getAuthHeaders;
1634
1661
  readonly projects: {
1635
1662
  list: () => Promise<{
@@ -1,4 +1,4 @@
1
- import { SessionStorageProvider, ArbiClient, LoginResult, components, WebSocketServerMessage, WsTaskUpdateMessage, KeyPair } from '@arbidocs/client';
1
+ import { SessionStorageProvider, ArbiClient, LoginResult, components, WebSocketServerMessage, WsAuthResultMessage, WsTaskUpdateMessage, KeyPair } from '@arbidocs/client';
2
2
 
3
3
  /**
4
4
  * Core error types and throwing error utilities.
@@ -470,6 +470,12 @@ interface ConnectOptions {
470
470
  accessToken: string;
471
471
  onMessage: (msg: WebSocketServerMessage) => void;
472
472
  onClose?: (code: number, reason: string) => void;
473
+ /**
474
+ * Called once the server accepts auth (`auth_result { success: true }`). The
475
+ * full message is passed so callers can read fields like `new_messages`. The
476
+ * auth handshake itself is owned by the transport — this is a notification.
477
+ */
478
+ onAuthenticated?: (msg: WsAuthResultMessage) => void;
473
479
  }
474
480
  interface ReconnectOptions extends ConnectOptions {
475
481
  /**
@@ -497,6 +503,12 @@ interface ReconnectOptions extends ConnectOptions {
497
503
  onReconnecting?: (attempt: number, maxRetries: number) => void;
498
504
  onReconnected?: () => void;
499
505
  onReconnectFailed?: () => void;
506
+ /**
507
+ * Decide whether to reconnect after a given close. Return `false` to stop —
508
+ * e.g. a server-initiated single-session eviction (code 1008), where
509
+ * reconnecting would just get kicked again. Default: always reconnect.
510
+ */
511
+ shouldReconnect?: (code: number, reason: string) => boolean;
500
512
  }
501
513
  interface ReconnectableWsConnection {
502
514
  close: () => void;
@@ -511,7 +523,10 @@ declare function connectWebSocket(options: ConnectOptions): Promise<WsConnection
511
523
  /**
512
524
  * Connect WebSocket with automatic reconnection on disconnect.
513
525
  *
514
- * Initial connection throws on failure (same as connectWebSocket).
526
+ * The initial connection is auth-symmetric with the reconnect loop: if the first
527
+ * attempt is rejected for auth reasons and `refreshAuth` is provided, it re-logs
528
+ * in and retries once with the fresh token before throwing. Any other failure —
529
+ * or a still-failing retry — throws to the caller.
515
530
  * After successful auth, disconnects trigger exponential backoff reconnection:
516
531
  * - a fresh token is resolved before every attempt (see `getAccessToken`), so a
517
532
  * token rotated elsewhere is picked up rather than reusing a stale one;
@@ -1630,6 +1645,18 @@ declare class Arbi {
1630
1645
  get isLoggedIn(): boolean;
1631
1646
  /** Check if a workspace is selected. */
1632
1647
  get hasWorkspace(): boolean;
1648
+ /**
1649
+ * Live connection info for opening a raw transport (e.g. a WebSocket).
1650
+ *
1651
+ * Reads the access token from the session on each call, so a token refreshed
1652
+ * elsewhere is reflected on the next (re)connect. Returns `null` when there is
1653
+ * no client/token yet (not logged in) rather than throwing, so callers can
1654
+ * poll for readiness instead of guarding against exceptions.
1655
+ */
1656
+ getConnectionInfo(): {
1657
+ baseUrl: string;
1658
+ accessToken: string;
1659
+ } | null;
1633
1660
  private getAuthHeaders;
1634
1661
  readonly projects: {
1635
1662
  list: () => Promise<{
package/dist/browser.cjs CHANGED
@@ -4043,7 +4043,7 @@ var WebSocketAuthError = class extends Error {
4043
4043
  }
4044
4044
  };
4045
4045
  function connectWebSocket(options) {
4046
- const { baseUrl, accessToken, onMessage, onClose } = options;
4046
+ const { baseUrl, accessToken, onMessage, onClose, onAuthenticated } = options;
4047
4047
  const url = client.buildWebSocketUrl(baseUrl);
4048
4048
  return new Promise((resolve, reject) => {
4049
4049
  const ws = new WebSocket(url);
@@ -4066,6 +4066,7 @@ function connectWebSocket(options) {
4066
4066
  clearTimeout(timeout);
4067
4067
  if (msg.success) {
4068
4068
  authenticated = true;
4069
+ onAuthenticated?.(msg);
4069
4070
  resolve({ close: () => ws.close() });
4070
4071
  } else {
4071
4072
  ws.close();
@@ -4099,6 +4100,8 @@ async function connectWithReconnect(options) {
4099
4100
  onReconnectFailed,
4100
4101
  onClose,
4101
4102
  onMessage,
4103
+ onAuthenticated,
4104
+ shouldReconnect,
4102
4105
  baseUrl,
4103
4106
  accessToken
4104
4107
  } = options;
@@ -4113,10 +4116,11 @@ async function connectWithReconnect(options) {
4113
4116
  baseUrl,
4114
4117
  accessToken: token,
4115
4118
  onMessage,
4119
+ onAuthenticated,
4116
4120
  onClose: (code, reason) => {
4117
4121
  currentConnection = null;
4118
4122
  onClose?.(code, reason);
4119
- if (!closed) void scheduleReconnect();
4123
+ if (!closed && (shouldReconnect?.(code, reason) ?? true)) void scheduleReconnect();
4120
4124
  }
4121
4125
  });
4122
4126
  };
@@ -4147,7 +4151,14 @@ async function connectWithReconnect(options) {
4147
4151
  }
4148
4152
  if (!closed) onReconnectFailed?.();
4149
4153
  };
4150
- currentConnection = await connectOnce();
4154
+ try {
4155
+ currentConnection = await connectOnce();
4156
+ } catch (err) {
4157
+ if (!(err instanceof WebSocketAuthError) || !refreshAuth) throw err;
4158
+ await refreshAuth().catch(() => {
4159
+ });
4160
+ currentConnection = await connectOnce();
4161
+ }
4151
4162
  return {
4152
4163
  close: () => {
4153
4164
  closed = true;
@@ -5556,6 +5567,19 @@ var Arbi = class {
5556
5567
  get hasWorkspace() {
5557
5568
  return this.currentWorkspaceId !== null;
5558
5569
  }
5570
+ /**
5571
+ * Live connection info for opening a raw transport (e.g. a WebSocket).
5572
+ *
5573
+ * Reads the access token from the session on each call, so a token refreshed
5574
+ * elsewhere is reflected on the next (re)connect. Returns `null` when there is
5575
+ * no client/token yet (not logged in) rather than throwing, so callers can
5576
+ * poll for readiness instead of guarding against exceptions.
5577
+ */
5578
+ getConnectionInfo() {
5579
+ const accessToken = this.client?.session.getState().accessToken;
5580
+ if (!accessToken) return null;
5581
+ return { baseUrl: this.options.url, accessToken };
5582
+ }
5559
5583
  // ── Auth headers (for raw fetch operations) ───────────────────────────
5560
5584
  getAuthHeaders() {
5561
5585
  const client = this.requireClient();