doover-js 0.1.2 → 0.2.0-alpha.0

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.
@@ -0,0 +1,178 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DooverTokenAuth = void 0;
4
+ const doover_auth_1 = require("./doover-auth");
5
+ const errors_1 = require("./errors");
6
+ const jwt_1 = require("./jwt");
7
+ /** Buffer in milliseconds before expiry at which we trigger a refresh (30 s). */
8
+ const REFRESH_BUFFER_MS = 30000;
9
+ class DooverTokenAuth extends doover_auth_1.DooverAuth {
10
+ constructor(options) {
11
+ super();
12
+ this.refreshInFlight = null;
13
+ this.token = options.token ?? null;
14
+ this.tokenExpires = this.resolveExpiry(options.tokenExpires, options.token ?? null);
15
+ this.refreshToken = options.refreshToken ?? null;
16
+ this.refreshTokenId = options.refreshTokenId ?? null;
17
+ this.authServerUrl = options.authServerUrl ?? null;
18
+ this.authServerClientId = options.authServerClientId ?? null;
19
+ this.fetchImpl = options.fetchImpl ?? fetch;
20
+ }
21
+ // ------------------------------------------------------------------
22
+ // DooverAuth interface
23
+ // ------------------------------------------------------------------
24
+ async getHttpHeaders() {
25
+ if (!this.token) {
26
+ return {};
27
+ }
28
+ return { Authorization: `Bearer ${this.token}` };
29
+ }
30
+ getFetchCredentials() {
31
+ return "omit";
32
+ }
33
+ async prepareWebSocket(url, canUseHeaders) {
34
+ if (!this.token) {
35
+ return { url };
36
+ }
37
+ if (canUseHeaders) {
38
+ return {
39
+ url,
40
+ headers: { Authorization: `Bearer ${this.token}` },
41
+ };
42
+ }
43
+ // Append token as a query parameter for browser-style WebSocket.
44
+ const parsed = new URL(url);
45
+ parsed.searchParams.set("token", this.token);
46
+ return { url: parsed.toString() };
47
+ }
48
+ setToken(token, tokenExpires) {
49
+ this.token = token;
50
+ this.tokenExpires = this.resolveExpiry(tokenExpires, token);
51
+ }
52
+ async ensureReady() {
53
+ if (!this.needsRefresh()) {
54
+ return;
55
+ }
56
+ // Deduplicate concurrent refresh attempts.
57
+ if (!this.refreshInFlight) {
58
+ this.refreshInFlight = this.refresh().finally(() => {
59
+ this.refreshInFlight = null;
60
+ });
61
+ }
62
+ return this.refreshInFlight;
63
+ }
64
+ // ------------------------------------------------------------------
65
+ // Profile attachment override — load refresh metadata from profile
66
+ // ------------------------------------------------------------------
67
+ attachProfile(profile, configManager) {
68
+ super.attachProfile(profile, configManager);
69
+ // Back-fill fields from the profile where not already set.
70
+ if (!this.token && profile.token) {
71
+ this.token = profile.token;
72
+ }
73
+ if (!this.tokenExpires && profile.tokenExpires) {
74
+ this.tokenExpires = new Date(profile.tokenExpires);
75
+ }
76
+ if (!this.tokenExpires && this.token) {
77
+ this.tokenExpires = (0, jwt_1.decodeTokenExpiry)(this.token);
78
+ }
79
+ if (!this.refreshToken && profile.refreshToken) {
80
+ this.refreshToken = profile.refreshToken;
81
+ }
82
+ if (!this.refreshTokenId && profile.refreshTokenId) {
83
+ this.refreshTokenId = profile.refreshTokenId;
84
+ }
85
+ if (!this.authServerUrl && profile.authServerUrl) {
86
+ this.authServerUrl = profile.authServerUrl;
87
+ }
88
+ if (!this.authServerClientId && profile.authServerClientId) {
89
+ this.authServerClientId = profile.authServerClientId;
90
+ }
91
+ }
92
+ // ------------------------------------------------------------------
93
+ // Internal
94
+ // ------------------------------------------------------------------
95
+ needsRefresh() {
96
+ if (!this.token) {
97
+ return true;
98
+ }
99
+ if (!this.tokenExpires) {
100
+ return false;
101
+ }
102
+ return this.tokenExpires.getTime() - Date.now() < REFRESH_BUFFER_MS;
103
+ }
104
+ async refresh() {
105
+ if (!this.authServerUrl ||
106
+ !this.refreshToken ||
107
+ !this.authServerClientId) {
108
+ throw new errors_1.DooverAuthError("Cannot refresh token: missing authServerUrl, refreshToken, or authServerClientId");
109
+ }
110
+ const url = new URL(`${this.authServerUrl}/oauth2/token`);
111
+ url.searchParams.set("grant_type", "refresh_token");
112
+ if (this.token) {
113
+ url.searchParams.set("access_token", this.token);
114
+ }
115
+ url.searchParams.set("refresh_token", this.refreshToken);
116
+ url.searchParams.set("client_id", this.authServerClientId);
117
+ let response;
118
+ try {
119
+ response = await this.fetchImpl(url.toString(), {
120
+ method: "POST",
121
+ });
122
+ }
123
+ catch (err) {
124
+ throw new errors_1.DooverAuthError(`Token refresh request failed: ${err instanceof Error ? err.message : String(err)}`);
125
+ }
126
+ if (!response.ok) {
127
+ throw new errors_1.DooverAuthError(`Token refresh failed with status ${response.status}`);
128
+ }
129
+ const body = (await response.json());
130
+ if (!body.access_token) {
131
+ throw new errors_1.DooverAuthError("Token refresh response did not contain an access_token");
132
+ }
133
+ this.token = body.access_token;
134
+ this.tokenExpires =
135
+ typeof body.expires_in === "number"
136
+ ? new Date(Date.now() + body.expires_in * 1000)
137
+ : (0, jwt_1.decodeTokenExpiry)(body.access_token);
138
+ if (body.refresh_token) {
139
+ this.refreshToken = body.refresh_token;
140
+ }
141
+ // Persist back to profile / config manager.
142
+ this.persistToProfile();
143
+ }
144
+ persistToProfile() {
145
+ if (!this.profile) {
146
+ return;
147
+ }
148
+ this.profile.token = this.token;
149
+ this.profile.tokenExpires = this.tokenExpires
150
+ ? this.tokenExpires.toISOString()
151
+ : null;
152
+ if (this.refreshToken) {
153
+ this.profile.refreshToken = this.refreshToken;
154
+ }
155
+ if (this.configManager) {
156
+ try {
157
+ this.configManager.create(this.profile);
158
+ this.configManager.write();
159
+ }
160
+ catch (err) {
161
+ throw new errors_1.DooverAuthError(`Failed to persist refreshed token: ${err instanceof Error ? err.message : String(err)}`);
162
+ }
163
+ }
164
+ }
165
+ resolveExpiry(explicit, token) {
166
+ if (explicit instanceof Date) {
167
+ return explicit;
168
+ }
169
+ if (typeof explicit === "number") {
170
+ return new Date(explicit);
171
+ }
172
+ if (token) {
173
+ return (0, jwt_1.decodeTokenExpiry)(token);
174
+ }
175
+ return null;
176
+ }
177
+ }
178
+ exports.DooverTokenAuth = DooverTokenAuth;
@@ -0,0 +1,3 @@
1
+ export declare class DooverAuthError extends Error {
2
+ constructor(message: string);
3
+ }
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DooverAuthError = void 0;
4
+ class DooverAuthError extends Error {
5
+ constructor(message) {
6
+ super(message);
7
+ this.name = "DooverAuthError";
8
+ }
9
+ }
10
+ exports.DooverAuthError = DooverAuthError;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Decode the `exp` claim from a JWT without verifying the signature.
3
+ * Returns the expiry as a `Date`, or `null` if the token has no `exp` claim
4
+ * or cannot be decoded.
5
+ */
6
+ export declare function decodeTokenExpiry(token: string): Date | null;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.decodeTokenExpiry = decodeTokenExpiry;
4
+ /**
5
+ * Decode the `exp` claim from a JWT without verifying the signature.
6
+ * Returns the expiry as a `Date`, or `null` if the token has no `exp` claim
7
+ * or cannot be decoded.
8
+ */
9
+ function decodeTokenExpiry(token) {
10
+ try {
11
+ const parts = token.split(".");
12
+ if (parts.length < 2) {
13
+ return null;
14
+ }
15
+ // Base64url → Base64 → decode
16
+ const base64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
17
+ const json = typeof atob === "function"
18
+ ? atob(base64)
19
+ : Buffer.from(base64, "base64").toString("utf-8");
20
+ const payload = JSON.parse(json);
21
+ if (typeof payload.exp !== "number") {
22
+ return null;
23
+ }
24
+ return new Date(payload.exp * 1000);
25
+ }
26
+ catch {
27
+ return null;
28
+ }
29
+ }
@@ -8,10 +8,12 @@ import { NotificationsApi } from "../apis/notifications-api";
8
8
  import { PermissionsApi } from "../apis/permissions-api";
9
9
  import { ProcessorsApi } from "../apis/processors-api";
10
10
  import { TurnApi } from "../apis/turn-api";
11
+ import type { DooverAuth } from "../auth/doover-auth";
11
12
  import { GatewayClient } from "../gateway/gateway-client";
12
13
  import { RestClient, type DooverClientConfig } from "../http/rest-client";
13
14
  import { DooverDataProvider } from "../viewer/doover-data-provider";
14
15
  export declare class DooverClient {
16
+ readonly auth: DooverAuth;
15
17
  readonly rest: RestClient;
16
18
  readonly viewer: DooverDataProvider;
17
19
  readonly channels: ChannelsApi;
@@ -11,13 +11,26 @@ const notifications_api_1 = require("../apis/notifications-api");
11
11
  const permissions_api_1 = require("../apis/permissions-api");
12
12
  const processors_api_1 = require("../apis/processors-api");
13
13
  const turn_api_1 = require("../apis/turn-api");
14
+ const build_auth_1 = require("../auth/build-auth");
14
15
  const gateway_client_1 = require("../gateway/gateway-client");
15
16
  const rest_client_1 = require("../http/rest-client");
16
17
  const doover_data_provider_1 = require("../viewer/doover-data-provider");
17
18
  class DooverClient {
18
19
  constructor(config) {
19
- this.rest = new rest_client_1.RestClient(config);
20
- this.viewer = new doover_data_provider_1.DooverDataProvider(config);
20
+ this.auth = (0, build_auth_1.buildAuth)({
21
+ auth: config.auth,
22
+ profile: config.profile,
23
+ configManager: config.configManager,
24
+ token: config.token,
25
+ tokenExpires: config.tokenExpires,
26
+ refreshToken: config.refreshToken,
27
+ refreshTokenId: config.refreshTokenId,
28
+ authServerUrl: config.authServerUrl,
29
+ authServerClientId: config.authServerClientId,
30
+ fetchImpl: config.fetchImpl,
31
+ });
32
+ this.rest = new rest_client_1.RestClient(config, this.auth);
33
+ this.viewer = new doover_data_provider_1.DooverDataProvider(config, this.auth);
21
34
  this.channels = new channels_api_1.ChannelsApi(this.rest);
22
35
  this.messages = new messages_api_1.MessagesApi(this.rest);
23
36
  this.aggregates = new aggregates_api_1.AggregatesApi(this.rest);
@@ -28,7 +41,7 @@ class DooverClient {
28
41
  this.processors = new processors_api_1.ProcessorsApi(this.rest);
29
42
  this.turn = new turn_api_1.TurnApi(this.rest);
30
43
  this.agents = new agents_api_1.AgentsApi(this.rest);
31
- this.gateway = new gateway_client_1.GatewayClient(config);
44
+ this.gateway = new gateway_client_1.GatewayClient(config, this.auth);
32
45
  }
33
46
  }
34
47
  exports.DooverClient = DooverClient;
@@ -1,3 +1,4 @@
1
+ import type { DooverAuth } from "../auth/doover-auth";
1
2
  import type { DooverClientConfig } from "../http/rest-client";
2
3
  import type { GatewayListenerMap, WebSocketSession } from "./types";
3
4
  import type { ChannelRef, JSONValue } from "../types/common";
@@ -11,8 +12,9 @@ export declare class GatewayClient {
11
12
  private reconnectTimer;
12
13
  private listeners;
13
14
  private subscriptions;
14
- constructor(config: DooverClientConfig);
15
- connect(): void;
15
+ private readonly auth;
16
+ constructor(config: DooverClientConfig, auth?: DooverAuth);
17
+ connect(): Promise<void>;
16
18
  disconnect(code?: number, reason?: string): void;
17
19
  on<K extends keyof GatewayListenerMap>(eventName: K, listener: GatewayListenerMap[K]): void;
18
20
  off<K extends keyof GatewayListenerMap>(eventName: K, listener: GatewayListenerMap[K]): void;
@@ -4,7 +4,7 @@ exports.GatewayClient = void 0;
4
4
  const errors_1 = require("../http/errors");
5
5
  const snowflake_1 = require("../utils/snowflake");
6
6
  class GatewayClient {
7
- constructor(config) {
7
+ constructor(config, auth) {
8
8
  this.config = config;
9
9
  this.socket = null;
10
10
  this.session = null;
@@ -29,13 +29,29 @@ class GatewayClient {
29
29
  heartbeatAck: new Set(),
30
30
  };
31
31
  this.subscriptions = new Map();
32
+ this.auth = auth ?? null;
32
33
  }
33
- connect() {
34
+ async connect() {
34
35
  if (this.socket && this.socket.readyState <= WebSocket.OPEN) {
35
36
  return;
36
37
  }
37
- const WebSocketImpl = this.config.webSocketImpl ?? WebSocket;
38
- this.socket = new WebSocketImpl(this.config.dataWssUrl);
38
+ if (this.auth) {
39
+ await this.auth.ensureReady();
40
+ }
41
+ const hasFactory = !!this.config.webSocketFactory;
42
+ const wsParams = this.auth
43
+ ? await this.auth.prepareWebSocket(this.config.dataWssUrl, hasFactory)
44
+ : { url: this.config.dataWssUrl };
45
+ if (this.config.webSocketFactory) {
46
+ this.socket = this.config.webSocketFactory({
47
+ url: wsParams.url,
48
+ headers: wsParams.headers,
49
+ });
50
+ }
51
+ else {
52
+ const WebSocketImpl = this.config.webSocketImpl ?? WebSocket;
53
+ this.socket = new WebSocketImpl(wsParams.url);
54
+ }
39
55
  this.socket.onopen = () => this.emit("open");
40
56
  this.socket.onclose = (event) => {
41
57
  this.stopHeartbeat();
@@ -66,7 +82,7 @@ class GatewayClient {
66
82
  diff_only: options?.diff_only ?? false,
67
83
  });
68
84
  if (!this.isConnected()) {
69
- this.connect();
85
+ void this.connect();
70
86
  return;
71
87
  }
72
88
  this.send({
@@ -93,7 +109,7 @@ class GatewayClient {
93
109
  }
94
110
  syncChannel(channel) {
95
111
  if (!this.isConnected()) {
96
- this.connect();
112
+ void this.connect();
97
113
  return;
98
114
  }
99
115
  this.send({
@@ -106,7 +122,7 @@ class GatewayClient {
106
122
  }
107
123
  sendOneShotMessage(channel, data) {
108
124
  if (!this.isConnected()) {
109
- this.connect();
125
+ void this.connect();
110
126
  return;
111
127
  }
112
128
  this.send({
@@ -248,7 +264,7 @@ class GatewayClient {
248
264
  }
249
265
  this.reconnectTimer = setTimeout(() => {
250
266
  this.reconnectTimer = null;
251
- this.connect();
267
+ void this.connect();
252
268
  }, 1000);
253
269
  }
254
270
  channelKey(channel) {
@@ -1,4 +1,6 @@
1
- export interface DooverClientConfig {
1
+ import type { AuthConfig } from "../auth/build-auth";
2
+ import type { DooverAuth } from "../auth/doover-auth";
3
+ export interface DooverClientConfig extends AuthConfig {
2
4
  dataRestUrl: string;
3
5
  controlApiUrl: string;
4
6
  dataWssUrl: string;
@@ -7,6 +9,10 @@ export interface DooverClientConfig {
7
9
  impersonateUserStorageKey?: string;
8
10
  fetchImpl?: typeof fetch;
9
11
  webSocketImpl?: typeof WebSocket;
12
+ webSocketFactory?: (options: {
13
+ url: string;
14
+ headers?: Record<string, string>;
15
+ }) => WebSocket;
10
16
  }
11
17
  export interface RequestOptions {
12
18
  baseUrl?: string;
@@ -20,8 +26,9 @@ export interface RequestOptions {
20
26
  type QueryValue = string | number | boolean | null | undefined | Array<string | number | boolean>;
21
27
  type QueryParams = Record<string, QueryValue> | object;
22
28
  export declare class RestClient {
23
- readonly config: Required<Pick<DooverClientConfig, "dataRestUrl" | "controlApiUrl" | "dataWssUrl" | "sharing" | "impersonateUserStorageKey">> & Pick<DooverClientConfig, "organisationId" | "fetchImpl" | "webSocketImpl">;
24
- constructor(config: DooverClientConfig);
29
+ readonly config: Required<Pick<DooverClientConfig, "dataRestUrl" | "controlApiUrl" | "dataWssUrl" | "sharing" | "impersonateUserStorageKey">> & Pick<DooverClientConfig, "organisationId" | "fetchImpl" | "webSocketImpl" | "webSocketFactory">;
30
+ readonly auth: DooverAuth | null;
31
+ constructor(config: DooverClientConfig, auth?: DooverAuth);
25
32
  request<T>(options: RequestOptions): Promise<T>;
26
33
  get<T>(path: string, query?: RequestOptions["query"], baseUrl?: string): Promise<T>;
27
34
  post<T>(path: string, body?: RequestOptions["body"], query?: RequestOptions["query"], baseUrl?: string): Promise<T>;
@@ -3,19 +3,30 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RestClient = void 0;
4
4
  const errors_1 = require("./errors");
5
5
  class RestClient {
6
- constructor(config) {
6
+ constructor(config, auth) {
7
7
  this.config = {
8
8
  ...config,
9
9
  sharing: config.sharing ?? "internal",
10
10
  organisationId: config.organisationId ?? null,
11
11
  impersonateUserStorageKey: config.impersonateUserStorageKey ?? "impersonate_user_id",
12
12
  };
13
+ this.auth = auth ?? null;
13
14
  }
14
15
  async request(options) {
16
+ if (this.auth) {
17
+ await this.auth.ensureReady();
18
+ }
15
19
  const method = options.method ?? "GET";
16
20
  const url = this.buildUrl(options.baseUrl ?? this.config.dataRestUrl, options.path, options.query);
17
21
  const headers = new Headers(options.headers);
18
22
  const body = this.normalizeBody(options.body, headers);
23
+ // Merge auth headers.
24
+ if (this.auth) {
25
+ const authHeaders = await this.auth.getHttpHeaders();
26
+ for (const [key, value] of Object.entries(authHeaders)) {
27
+ headers.set(key, value);
28
+ }
29
+ }
19
30
  if (!options.omitSharingHeader) {
20
31
  headers.set("X-Doover-Sharing", this.config.sharing);
21
32
  }
@@ -26,12 +37,15 @@ class RestClient {
26
37
  if (impersonated) {
27
38
  headers.set("X-Doover-Assume", impersonated);
28
39
  }
40
+ const credentials = this.auth
41
+ ? this.auth.getFetchCredentials()
42
+ : "include";
29
43
  const fetchImpl = this.config.fetchImpl ?? fetch;
30
44
  const response = await fetchImpl(url, {
31
45
  method,
32
46
  headers,
33
47
  body,
34
- credentials: "include",
48
+ credentials,
35
49
  });
36
50
  const payload = await this.parseResponse(response);
37
51
  if (!response.ok) {
package/dist/index.d.ts CHANGED
@@ -9,6 +9,15 @@ export { NotificationsApi } from "./apis/notifications-api";
9
9
  export { PermissionsApi } from "./apis/permissions-api";
10
10
  export { ProcessorsApi } from "./apis/processors-api";
11
11
  export { TurnApi } from "./apis/turn-api";
12
+ export { DooverAuth } from "./auth/doover-auth";
13
+ export { CookieAuth } from "./auth/cookie-auth";
14
+ export { DooverTokenAuth } from "./auth/doover-token-auth";
15
+ export { AuthProfile } from "./auth/auth-profile";
16
+ export type { AuthProfileData } from "./auth/auth-profile";
17
+ export type { AuthProfileStore } from "./auth/auth-store";
18
+ export { DooverAuthError } from "./auth/errors";
19
+ export { buildAuth } from "./auth/build-auth";
20
+ export type { AuthConfig } from "./auth/build-auth";
12
21
  export { GatewayClient } from "./gateway/gateway-client";
13
22
  export type * from "./gateway/types";
14
23
  export { RestClient } from "./http/rest-client";
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.generateSnowflakeIdAtTime = exports.extractSnowflakeId = exports.addTimestampToMessage = exports.getIdentifierFromPath = exports.DooverDataProvider = exports.DooverValidationError = exports.DooverGatewayError = exports.DooverApiError = exports.RestClient = exports.GatewayClient = exports.TurnApi = exports.ProcessorsApi = exports.PermissionsApi = exports.NotificationsApi = exports.MessagesApi = exports.ConnectionsApi = exports.ChannelsApi = exports.AlarmsApi = exports.AggregatesApi = exports.AgentsApi = exports.DooverClient = void 0;
3
+ exports.generateSnowflakeIdAtTime = exports.extractSnowflakeId = exports.addTimestampToMessage = exports.getIdentifierFromPath = exports.DooverDataProvider = exports.DooverValidationError = exports.DooverGatewayError = exports.DooverApiError = exports.RestClient = exports.GatewayClient = exports.buildAuth = exports.DooverAuthError = exports.AuthProfile = exports.DooverTokenAuth = exports.CookieAuth = exports.DooverAuth = exports.TurnApi = exports.ProcessorsApi = exports.PermissionsApi = exports.NotificationsApi = exports.MessagesApi = exports.ConnectionsApi = exports.ChannelsApi = exports.AlarmsApi = exports.AggregatesApi = exports.AgentsApi = exports.DooverClient = void 0;
4
4
  var doover_client_1 = require("./client/doover-client");
5
5
  Object.defineProperty(exports, "DooverClient", { enumerable: true, get: function () { return doover_client_1.DooverClient; } });
6
6
  var agents_api_1 = require("./apis/agents-api");
@@ -23,14 +23,26 @@ var processors_api_1 = require("./apis/processors-api");
23
23
  Object.defineProperty(exports, "ProcessorsApi", { enumerable: true, get: function () { return processors_api_1.ProcessorsApi; } });
24
24
  var turn_api_1 = require("./apis/turn-api");
25
25
  Object.defineProperty(exports, "TurnApi", { enumerable: true, get: function () { return turn_api_1.TurnApi; } });
26
+ var doover_auth_1 = require("./auth/doover-auth");
27
+ Object.defineProperty(exports, "DooverAuth", { enumerable: true, get: function () { return doover_auth_1.DooverAuth; } });
28
+ var cookie_auth_1 = require("./auth/cookie-auth");
29
+ Object.defineProperty(exports, "CookieAuth", { enumerable: true, get: function () { return cookie_auth_1.CookieAuth; } });
30
+ var doover_token_auth_1 = require("./auth/doover-token-auth");
31
+ Object.defineProperty(exports, "DooverTokenAuth", { enumerable: true, get: function () { return doover_token_auth_1.DooverTokenAuth; } });
32
+ var auth_profile_1 = require("./auth/auth-profile");
33
+ Object.defineProperty(exports, "AuthProfile", { enumerable: true, get: function () { return auth_profile_1.AuthProfile; } });
34
+ var errors_1 = require("./auth/errors");
35
+ Object.defineProperty(exports, "DooverAuthError", { enumerable: true, get: function () { return errors_1.DooverAuthError; } });
36
+ var build_auth_1 = require("./auth/build-auth");
37
+ Object.defineProperty(exports, "buildAuth", { enumerable: true, get: function () { return build_auth_1.buildAuth; } });
26
38
  var gateway_client_1 = require("./gateway/gateway-client");
27
39
  Object.defineProperty(exports, "GatewayClient", { enumerable: true, get: function () { return gateway_client_1.GatewayClient; } });
28
40
  var rest_client_1 = require("./http/rest-client");
29
41
  Object.defineProperty(exports, "RestClient", { enumerable: true, get: function () { return rest_client_1.RestClient; } });
30
- var errors_1 = require("./http/errors");
31
- Object.defineProperty(exports, "DooverApiError", { enumerable: true, get: function () { return errors_1.DooverApiError; } });
32
- Object.defineProperty(exports, "DooverGatewayError", { enumerable: true, get: function () { return errors_1.DooverGatewayError; } });
33
- Object.defineProperty(exports, "DooverValidationError", { enumerable: true, get: function () { return errors_1.DooverValidationError; } });
42
+ var errors_2 = require("./http/errors");
43
+ Object.defineProperty(exports, "DooverApiError", { enumerable: true, get: function () { return errors_2.DooverApiError; } });
44
+ Object.defineProperty(exports, "DooverGatewayError", { enumerable: true, get: function () { return errors_2.DooverGatewayError; } });
45
+ Object.defineProperty(exports, "DooverValidationError", { enumerable: true, get: function () { return errors_2.DooverValidationError; } });
34
46
  var doover_data_provider_1 = require("./viewer/doover-data-provider");
35
47
  Object.defineProperty(exports, "DooverDataProvider", { enumerable: true, get: function () { return doover_data_provider_1.DooverDataProvider; } });
36
48
  var path_parsing_1 = require("./viewer/path-parsing");
@@ -0,0 +1,34 @@
1
+ import { AuthProfile } from "../auth/auth-profile";
2
+ import type { AuthProfileStore } from "../auth/auth-store";
3
+ /**
4
+ * File-backed profile store compatible with pydoover's config format.
5
+ *
6
+ * Reads `~/.doover/config` (or a custom path) synchronously on construction,
7
+ * matching Python's eager-read model.
8
+ *
9
+ * Behaviour:
10
+ * - Doover2 `[profile=...]` blocks are parsed into `AuthProfile` entries.
11
+ * - Legacy Doover1 `[...]` blocks and unrecognised raw blocks are preserved
12
+ * verbatim in their original order and written back unchanged.
13
+ * - `write()` creates the `~/.doover` directory if missing.
14
+ */
15
+ export declare class ConfigManager implements AuthProfileStore {
16
+ currentProfile: string | null;
17
+ current: AuthProfile | null;
18
+ /** Parsed Doover2 profiles keyed by profile name. */
19
+ private profiles;
20
+ /**
21
+ * Ordered list of *blocks* in the file as they were originally read. Each
22
+ * entry is either a `{ type: "managed"; name: string }` reference into
23
+ * `this.profiles`, or a `{ type: "raw"; text: string }` blob that is
24
+ * preserved verbatim.
25
+ */
26
+ private blocks;
27
+ private readonly configPath;
28
+ constructor(configPath?: string);
29
+ get(profileName: string): AuthProfile | undefined;
30
+ create(entry: AuthProfile): void;
31
+ delete(profileName: string): void;
32
+ write(): void;
33
+ private read;
34
+ }