@coderule/clients 1.2.0 → 1.4.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.
package/dist/index.d.ts CHANGED
@@ -23,6 +23,38 @@ interface JWTPayload {
23
23
  }
24
24
  declare function decodeJWT(jwtToken: string): JWTPayload | null;
25
25
 
26
+ interface JWTProvider {
27
+ getJWT(forceRefresh?: boolean): Promise<string>;
28
+ }
29
+ interface TokenRefreshInfo {
30
+ token: string;
31
+ expiresAt: number;
32
+ serverUrl?: string;
33
+ }
34
+ interface JWTFactoryOptions {
35
+ minTtlMs?: number;
36
+ onTokenRefreshed?: (info: TokenRefreshInfo) => void;
37
+ }
38
+ declare class JWTFactory implements JWTProvider {
39
+ private readonly authClient;
40
+ private sourceToken;
41
+ private cache?;
42
+ private refreshPromise?;
43
+ private readonly minTtlMs;
44
+ private readonly onTokenRefreshed?;
45
+ private currentServerUrl?;
46
+ constructor(authClient: AuthHttpClient, sourceToken: string, options?: JWTFactoryOptions);
47
+ setSourceToken(token: string): void;
48
+ invalidate(): void;
49
+ getJWT(forceRefresh?: boolean): Promise<string>;
50
+ private refresh;
51
+ private fetchNewToken;
52
+ private resolveExpiryMs;
53
+ private extractServerUrl;
54
+ private emitTokenRefreshed;
55
+ getServerUrl(): string | undefined;
56
+ }
57
+
26
58
  interface SnapshotStatus$1 {
27
59
  status: 'NOT_FOUND' | 'READY' | 'INDEXING' | 'PENDING' | 'FAILED' | 'MISSING_CONTENT';
28
60
  snapshot_hash?: string;
@@ -50,17 +82,25 @@ interface HealthResponse$1 {
50
82
  version?: string;
51
83
  [key: string]: any;
52
84
  }
85
+ interface SyncClientConfig {
86
+ baseUrl?: string;
87
+ timeout?: number;
88
+ jwtProvider: JWTProvider;
89
+ }
53
90
  declare class SyncHttpClient {
54
91
  private baseUrl;
55
92
  private timeout;
56
93
  private apiBase;
57
- constructor(uri?: string, timeout?: number);
58
- checkSnapshotStatus(snapshotHash: string, jwt: string): Promise<SnapshotStatus$1>;
59
- createSnapshot(snapshotHash: string, files: FileInfo[], jwt: string): Promise<SnapshotStatus$1>;
94
+ private readonly jwtProvider;
95
+ constructor({ baseUrl, timeout, jwtProvider, }: SyncClientConfig);
96
+ updateBaseUrl(baseUrl: string): void;
97
+ private configureBase;
98
+ checkSnapshotStatus(snapshotHash: string): Promise<SnapshotStatus$1>;
99
+ createSnapshot(snapshotHash: string, files: FileInfo[]): Promise<SnapshotStatus$1>;
60
100
  uploadFileContent(filesContent: Map<string, {
61
101
  path: string;
62
102
  content: Buffer | string;
63
- }>, jwt: string): Promise<UploadResult>;
103
+ }>): Promise<UploadResult>;
64
104
  static calculateFileHash(filePath: string, content: Buffer | string): string;
65
105
  static calculateSnapshotHash(fileHashes: string[]): string;
66
106
  health(): Promise<HealthResponse$1>;
@@ -101,17 +141,25 @@ interface CacheStats {
101
141
  ttl: number;
102
142
  snapshots: string[];
103
143
  }
144
+ interface RetrievalClientConfig {
145
+ baseUrl?: string;
146
+ timeout?: number;
147
+ jwtProvider: JWTProvider;
148
+ }
104
149
  declare class RetrievalHttpClient {
105
150
  private baseUrl;
106
151
  private timeout;
107
152
  private apiBase;
108
- constructor(uri?: string, timeout?: number);
153
+ private readonly jwtProvider;
154
+ constructor({ baseUrl, timeout, jwtProvider, }: RetrievalClientConfig);
155
+ updateBaseUrl(baseUrl: string): void;
156
+ private configureBase;
109
157
  healthCheck(): Promise<HealthResponse>;
110
- query(snapshotHash: string, queryText: string, budgetTokens: number | undefined, jwt: string, options?: RetrievalOptions): Promise<RetrievalResult>;
111
- checkSnapshotStatus(snapshotHash: string, jwt: string): Promise<SnapshotStatus>;
112
- clearCache(jwt: string): Promise<boolean>;
113
- getCacheStats(jwt: string): Promise<CacheStats>;
114
- queryWithOptions(snapshotHash: string, queryText: string, jwt: string, budgetTokens?: number, flowStrength?: number, blendAlpha?: number, hopDepth?: number, maxIterations?: number, split?: number, formatter?: 'standard' | 'compact'): Promise<RetrievalResult>;
158
+ query(snapshotHash: string, queryText: string, budgetTokens?: number, options?: RetrievalOptions): Promise<RetrievalResult>;
159
+ checkSnapshotStatus(snapshotHash: string): Promise<SnapshotStatus>;
160
+ clearCache(): Promise<boolean>;
161
+ getCacheStats(): Promise<CacheStats>;
162
+ queryWithOptions(snapshotHash: string, queryText: string, budgetTokens?: number, flowStrength?: number, blendAlpha?: number, hopDepth?: number, maxIterations?: number, split?: number, formatter?: 'standard' | 'compact'): Promise<RetrievalResult>;
115
163
  close(): void;
116
164
  }
117
165
 
@@ -127,12 +175,20 @@ interface HealthStatus {
127
175
  frontends_loaded: number;
128
176
  info: Record<string, string>;
129
177
  }
178
+ interface ASTClientConfig {
179
+ baseUrl?: string;
180
+ timeout?: number;
181
+ jwtProvider: JWTProvider;
182
+ }
130
183
  declare class ASTHttpClient {
131
184
  private baseUrl;
132
185
  private timeout;
133
186
  private apiBase;
134
- constructor(uri?: string, timeout?: number);
135
- getVisitorRulesV2(jwt: string): Promise<VisitorRulesV2>;
187
+ private readonly jwtProvider;
188
+ constructor({ baseUrl, timeout, jwtProvider, }: ASTClientConfig);
189
+ updateBaseUrl(baseUrl: string): void;
190
+ private configureBase;
191
+ getVisitorRulesV2(): Promise<VisitorRulesV2>;
136
192
  health(): Promise<HealthStatus>;
137
193
  static compileRulesV2(rules: VisitorRulesV2): {
138
194
  exts: Set<string>;
@@ -143,6 +199,36 @@ declare class ASTHttpClient {
143
199
  close(): void;
144
200
  }
145
201
 
202
+ type ServiceKey = 'auth' | 'ast' | 'retrieval' | 'sync';
203
+ interface ServiceConfig {
204
+ baseUrl?: string;
205
+ timeout?: number;
206
+ }
207
+ interface CoderuleClientOptions {
208
+ token: string;
209
+ baseUrl?: string;
210
+ auth?: ServiceConfig;
211
+ ast?: ServiceConfig;
212
+ retrieval?: ServiceConfig;
213
+ sync?: ServiceConfig;
214
+ jwtFactory?: JWTFactoryOptions;
215
+ }
216
+ declare class CoderuleClients {
217
+ readonly auth: AuthHttpClient;
218
+ readonly ast: ASTHttpClient;
219
+ readonly retrieval: RetrievalHttpClient;
220
+ readonly sync: SyncHttpClient;
221
+ readonly jwtFactory: JWTFactory;
222
+ private readonly serviceBaseLocked;
223
+ private lastServerUrl?;
224
+ constructor(options: CoderuleClientOptions);
225
+ get jwt(): JWTProvider;
226
+ getJWT(forceRefresh?: boolean): Promise<string>;
227
+ setToken(token: string): void;
228
+ close(): void;
229
+ private applyServerUrl;
230
+ }
231
+
146
232
  declare const fetch: typeof globalThis.fetch;
147
233
 
148
- export { ASTHttpClient, AuthHttpClient, type HealthStatus, RetrievalHttpClient, SyncHttpClient, type VisitorRulesV2, decodeJWT, fetch };
234
+ export { type ASTClientConfig, ASTHttpClient, AuthHttpClient, type CoderuleClientOptions, CoderuleClients, type HealthStatus, JWTFactory, type JWTFactoryOptions, type JWTProvider, type RetrievalClientConfig, RetrievalHttpClient, type ServiceConfig, type ServiceKey, type SyncClientConfig, SyncHttpClient, type TokenRefreshInfo, type VisitorRulesV2, decodeJWT, fetch };
package/dist/index.js CHANGED
@@ -237,10 +237,26 @@ init_esm_shims();
237
237
  var SyncHttpClient = class {
238
238
  /**
239
239
  * Initialize the Sync HTTP client
240
- * @param uri - URI/URL for connecting to the HTTP server (e.g., "http://localhost:8002")
241
- * @param timeout - Request timeout in milliseconds (default: 60000)
240
+ * @param config.baseUrl - URI/URL for connecting to the HTTP server (e.g., "http://localhost:8002")
241
+ * @param config.timeout - Request timeout in milliseconds (default: 60000)
242
+ * @param config.jwtProvider - Provider for obtaining JWT tokens
242
243
  */
243
- constructor(uri = "http://localhost:8002", timeout = 6e4) {
244
+ constructor({
245
+ baseUrl = "http://localhost:8002",
246
+ timeout = 6e4,
247
+ jwtProvider
248
+ }) {
249
+ if (!jwtProvider) {
250
+ throw new Error("SyncHttpClient requires a JWT provider");
251
+ }
252
+ this.timeout = timeout;
253
+ this.jwtProvider = jwtProvider;
254
+ this.configureBase(baseUrl);
255
+ }
256
+ updateBaseUrl(baseUrl) {
257
+ this.configureBase(baseUrl);
258
+ }
259
+ configureBase(uri) {
244
260
  let processedUri = uri;
245
261
  if (!uri.startsWith("http://") && !uri.startsWith("https://")) {
246
262
  processedUri = `http://${uri}`;
@@ -251,7 +267,6 @@ var SyncHttpClient = class {
251
267
  } else {
252
268
  this.baseUrl = `${url.protocol}//${url.host}`;
253
269
  }
254
- this.timeout = timeout;
255
270
  if (this.baseUrl.endsWith("/")) {
256
271
  this.apiBase = `${this.baseUrl}sync/v1/`;
257
272
  } else {
@@ -261,14 +276,14 @@ var SyncHttpClient = class {
261
276
  /**
262
277
  * Check the status of a snapshot
263
278
  * @param snapshotHash - SHA256 hash of the snapshot
264
- * @param jwt - JWT token for authorization (required)
265
279
  * @returns Snapshot status information
266
280
  * @throws Error on HTTP errors or connection errors
267
281
  */
268
- async checkSnapshotStatus(snapshotHash, jwt) {
269
- if (!jwt) {
270
- throw new Error("JWT must be provided");
282
+ async checkSnapshotStatus(snapshotHash) {
283
+ if (!snapshotHash) {
284
+ throw new Error("Snapshot hash must be provided");
271
285
  }
286
+ const jwt = await this.jwtProvider.getJWT();
272
287
  const url = `${this.apiBase}snapshots`;
273
288
  try {
274
289
  const controller = new AbortController2();
@@ -306,14 +321,14 @@ var SyncHttpClient = class {
306
321
  * Create a new snapshot or get its status if it exists
307
322
  * @param snapshotHash - SHA256 hash of the snapshot
308
323
  * @param files - List of file information with 'file_path' and 'file_hash'
309
- * @param jwt - JWT token for authorization (required)
310
324
  * @returns Snapshot creation result or status
311
325
  * @throws Error on HTTP errors or connection errors
312
326
  */
313
- async createSnapshot(snapshotHash, files, jwt) {
314
- if (!jwt) {
315
- throw new Error("JWT must be provided");
327
+ async createSnapshot(snapshotHash, files) {
328
+ if (!snapshotHash) {
329
+ throw new Error("Snapshot hash must be provided");
316
330
  }
331
+ const jwt = await this.jwtProvider.getJWT();
317
332
  const url = `${this.apiBase}snapshots`;
318
333
  try {
319
334
  const controller = new AbortController2();
@@ -360,14 +375,11 @@ var SyncHttpClient = class {
360
375
  /**
361
376
  * Upload file content to the service
362
377
  * @param filesContent - Map of file_hash to object with 'path' and 'content'
363
- * @param jwt - JWT token for authorization (required)
364
378
  * @returns Upload result with counts
365
379
  * @throws Error on HTTP errors or connection errors
366
380
  */
367
- async uploadFileContent(filesContent, jwt) {
368
- if (!jwt) {
369
- throw new Error("JWT must be provided");
370
- }
381
+ async uploadFileContent(filesContent) {
382
+ const jwt = await this.jwtProvider.getJWT();
371
383
  const url = `${this.apiBase}files/content`;
372
384
  try {
373
385
  const controller = new AbortController2();
@@ -475,10 +487,28 @@ init_esm_shims();
475
487
  var RetrievalHttpClient = class {
476
488
  /**
477
489
  * Initialize the Retrieval HTTP client
478
- * @param uri - URI/URL for connecting to the HTTP server (e.g., "http://localhost:8004")
479
- * @param timeout - Request timeout in milliseconds (default: 60000)
490
+ * @param config.baseUrl - URI/URL for connecting to the HTTP server (e.g., "http://localhost:8004")
491
+ * @param config.timeout - Request timeout in milliseconds (default: 60000)
492
+ * @param config.jwtProvider - Provider for obtaining JWT tokens
480
493
  */
481
- constructor(uri = "http://localhost:8004", timeout = 6e4) {
494
+ constructor({
495
+ baseUrl = "http://localhost:8004",
496
+ timeout = 6e4,
497
+ jwtProvider
498
+ }) {
499
+ if (!jwtProvider) {
500
+ throw new Error("RetrievalHttpClient requires a JWT provider");
501
+ }
502
+ this.timeout = timeout;
503
+ this.jwtProvider = jwtProvider;
504
+ this.configureBase(baseUrl);
505
+ console.debug(`Initialized HTTP client for ${this.baseUrl}`);
506
+ console.debug(`API base: ${this.apiBase}`);
507
+ }
508
+ updateBaseUrl(baseUrl) {
509
+ this.configureBase(baseUrl);
510
+ }
511
+ configureBase(uri) {
482
512
  let processedUri = uri;
483
513
  if (!uri.startsWith("http://") && !uri.startsWith("https://")) {
484
514
  processedUri = `http://${uri}`;
@@ -489,14 +519,11 @@ var RetrievalHttpClient = class {
489
519
  } else {
490
520
  this.baseUrl = `${url.protocol}//${url.host}`;
491
521
  }
492
- this.timeout = timeout;
493
522
  if (this.baseUrl.endsWith("/")) {
494
523
  this.apiBase = `${this.baseUrl}api/retrieval/`;
495
524
  } else {
496
525
  this.apiBase = `${this.baseUrl}/api/retrieval/`;
497
526
  }
498
- console.debug(`Initialized HTTP client for ${this.baseUrl}`);
499
- console.debug(`API base: ${this.apiBase}`);
500
527
  }
501
528
  /**
502
529
  * Check the health status of the Retrieval service
@@ -545,25 +572,22 @@ var RetrievalHttpClient = class {
545
572
  * @param snapshotHash - SHA256 hash of the codebase snapshot
546
573
  * @param queryText - Natural language query for retrieval
547
574
  * @param budgetTokens - Maximum token budget for results (default: 3000)
548
- * @param jwt - JWT token for authorization (required)
549
575
  * @param options - Optional retrieval parameters
550
576
  * @returns Retrieval results with formatted output
551
577
  * @throws Error on query failures
552
578
  */
553
- async query(snapshotHash, queryText, budgetTokens = 3e3, jwt, options) {
579
+ async query(snapshotHash, queryText, budgetTokens = 3e3, options) {
554
580
  if (!snapshotHash) {
555
581
  throw new Error("Snapshot hash must be provided");
556
582
  }
557
583
  if (!queryText) {
558
584
  throw new Error("Query text must be provided");
559
585
  }
560
- if (!jwt) {
561
- throw new Error("JWT must be provided");
562
- }
563
586
  if (budgetTokens < 100) {
564
587
  throw new Error("Budget tokens must be at least 100");
565
588
  }
566
589
  const startTime = Date.now();
590
+ const jwt = await this.jwtProvider.getJWT();
567
591
  const queryEndpoint = `${this.apiBase}query`;
568
592
  try {
569
593
  const controller = new AbortController2();
@@ -620,13 +644,11 @@ var RetrievalHttpClient = class {
620
644
  * @returns Snapshot status information
621
645
  * @throws Error on status check failures
622
646
  */
623
- async checkSnapshotStatus(snapshotHash, jwt) {
647
+ async checkSnapshotStatus(snapshotHash) {
624
648
  if (!snapshotHash) {
625
649
  throw new Error("Snapshot hash must be provided");
626
650
  }
627
- if (!jwt) {
628
- throw new Error("JWT must be provided");
629
- }
651
+ const jwt = await this.jwtProvider.getJWT();
630
652
  const statusEndpoint = `${this.apiBase}snapshots/${snapshotHash}/status`;
631
653
  try {
632
654
  const controller = new AbortController2();
@@ -671,10 +693,8 @@ var RetrievalHttpClient = class {
671
693
  * @returns true if cache cleared successfully
672
694
  * @throws Error on cache clear failures
673
695
  */
674
- async clearCache(jwt) {
675
- if (!jwt) {
676
- throw new Error("JWT must be provided");
677
- }
696
+ async clearCache() {
697
+ const jwt = await this.jwtProvider.getJWT();
678
698
  const cacheEndpoint = `${this.apiBase}cache`;
679
699
  try {
680
700
  const controller = new AbortController2();
@@ -709,10 +729,8 @@ var RetrievalHttpClient = class {
709
729
  * @returns Cache statistics
710
730
  * @throws Error on stats retrieval failures
711
731
  */
712
- async getCacheStats(jwt) {
713
- if (!jwt) {
714
- throw new Error("JWT must be provided");
715
- }
732
+ async getCacheStats() {
733
+ const jwt = await this.jwtProvider.getJWT();
716
734
  const statsEndpoint = `${this.apiBase}cache/stats`;
717
735
  try {
718
736
  const controller = new AbortController2();
@@ -758,7 +776,7 @@ var RetrievalHttpClient = class {
758
776
  * @param formatter - Output format "standard" or "compact"
759
777
  * @returns Retrieval results
760
778
  */
761
- async queryWithOptions(snapshotHash, queryText, jwt, budgetTokens = 3e3, flowStrength = 1.5, blendAlpha = 0.8, hopDepth = 2, maxIterations = 12, split = 0.8, formatter = "standard") {
779
+ async queryWithOptions(snapshotHash, queryText, budgetTokens = 3e3, flowStrength = 1.5, blendAlpha = 0.8, hopDepth = 2, maxIterations = 12, split = 0.8, formatter = "standard") {
762
780
  const options = {
763
781
  flow_strength: flowStrength,
764
782
  blend_alpha: blendAlpha,
@@ -767,7 +785,7 @@ var RetrievalHttpClient = class {
767
785
  split,
768
786
  formatter
769
787
  };
770
- return this.query(snapshotHash, queryText, budgetTokens, jwt, options);
788
+ return this.query(snapshotHash, queryText, budgetTokens, options);
771
789
  }
772
790
  /**
773
791
  * Close the HTTP client connection (no-op for fetch)
@@ -781,10 +799,26 @@ init_esm_shims();
781
799
  var ASTHttpClient = class {
782
800
  /**
783
801
  * Initialize the AST HTTP client
784
- * @param uri - URI/URL for connecting to the HTTP server (e.g., "http://localhost:8003")
785
- * @param timeout - Request timeout in milliseconds (default: 60000)
802
+ * @param config.baseUrl - URI/URL for connecting to the HTTP server (e.g., "http://localhost:8003")
803
+ * @param config.timeout - Request timeout in milliseconds (default: 60000)
804
+ * @param config.jwtProvider - Provider for obtaining JWT tokens
786
805
  */
787
- constructor(uri = "http://localhost:8003", timeout = 6e4) {
806
+ constructor({
807
+ baseUrl = "http://localhost:8003",
808
+ timeout = 6e4,
809
+ jwtProvider
810
+ }) {
811
+ if (!jwtProvider) {
812
+ throw new Error("ASTHttpClient requires a JWT provider");
813
+ }
814
+ this.timeout = timeout;
815
+ this.jwtProvider = jwtProvider;
816
+ this.configureBase(baseUrl);
817
+ }
818
+ updateBaseUrl(baseUrl) {
819
+ this.configureBase(baseUrl);
820
+ }
821
+ configureBase(uri) {
788
822
  let processedUri = uri;
789
823
  if (!uri.startsWith("http://") && !uri.startsWith("https://")) {
790
824
  processedUri = `http://${uri}`;
@@ -795,7 +829,6 @@ var ASTHttpClient = class {
795
829
  } else {
796
830
  this.baseUrl = `${url.protocol}//${url.host}`;
797
831
  }
798
- this.timeout = timeout;
799
832
  if (this.baseUrl.endsWith("/")) {
800
833
  this.apiBase = `${this.baseUrl}api/ast/`;
801
834
  } else {
@@ -804,14 +837,11 @@ var ASTHttpClient = class {
804
837
  }
805
838
  /**
806
839
  * Get file visitor rules in v2 format optimized for Chokidar v4
807
- * @param jwt - JWT token for authorization (required)
808
840
  * @returns Visitor rules with format, include_extensions, include_filenames, exclude_dirnames
809
841
  * @throws Error on HTTP errors or connection errors
810
842
  */
811
- async getVisitorRulesV2(jwt) {
812
- if (!jwt) {
813
- throw new Error("JWT must be provided");
814
- }
843
+ async getVisitorRulesV2() {
844
+ const jwt = await this.jwtProvider.getJWT();
815
845
  const url = `${this.apiBase}visitor-rules`;
816
846
  try {
817
847
  const controller = new AbortController2();
@@ -921,6 +951,242 @@ var ASTHttpClient = class {
921
951
  }
922
952
  };
923
953
 
924
- export { ASTHttpClient, AuthHttpClient, RetrievalHttpClient, SyncHttpClient, decodeJWT, fetch_wrapper_default as fetch };
954
+ // src/utils/jwt-factory.ts
955
+ init_esm_shims();
956
+ var DEFAULT_MIN_TTL_MS = 5e3;
957
+ var JWTFactory = class {
958
+ constructor(authClient, sourceToken, options = {}) {
959
+ this.authClient = authClient;
960
+ this.sourceToken = sourceToken;
961
+ if (!sourceToken) {
962
+ throw new Error("JWTFactory requires a non-empty source token");
963
+ }
964
+ this.minTtlMs = options.minTtlMs ?? DEFAULT_MIN_TTL_MS;
965
+ this.onTokenRefreshed = options.onTokenRefreshed;
966
+ }
967
+ setSourceToken(token) {
968
+ if (!token) {
969
+ throw new Error("JWTFactory requires a non-empty source token");
970
+ }
971
+ if (token !== this.sourceToken) {
972
+ this.sourceToken = token;
973
+ this.cache = void 0;
974
+ this.currentServerUrl = void 0;
975
+ }
976
+ }
977
+ invalidate() {
978
+ this.cache = void 0;
979
+ this.currentServerUrl = void 0;
980
+ }
981
+ async getJWT(forceRefresh = false) {
982
+ const now = Date.now();
983
+ if (!forceRefresh && this.cache) {
984
+ if (now < this.cache.expiresAt) {
985
+ if (now < this.cache.refreshAt) {
986
+ return this.cache.token;
987
+ }
988
+ } else {
989
+ this.cache = void 0;
990
+ }
991
+ }
992
+ return this.refresh(now, forceRefresh);
993
+ }
994
+ async refresh(now, forceRefresh) {
995
+ if (!this.refreshPromise) {
996
+ this.refreshPromise = this.fetchNewToken().finally(() => {
997
+ this.refreshPromise = void 0;
998
+ });
999
+ }
1000
+ try {
1001
+ return await this.refreshPromise;
1002
+ } catch (error) {
1003
+ if (!forceRefresh && this.cache && now < this.cache.expiresAt) {
1004
+ console.warn(
1005
+ "Failed to refresh JWT, using cached token until expiry. Reason:",
1006
+ error
1007
+ );
1008
+ return this.cache.token;
1009
+ }
1010
+ throw error;
1011
+ }
1012
+ }
1013
+ async fetchNewToken() {
1014
+ const response = await this.authClient.authenticate(this.sourceToken);
1015
+ const fetchedAt = Date.now();
1016
+ const expiresAt = this.resolveExpiryMs(
1017
+ response.jwt,
1018
+ response.expires_at,
1019
+ fetchedAt
1020
+ );
1021
+ const payload = decodeJWT(response.jwt);
1022
+ const serverUrl = this.extractServerUrl(payload);
1023
+ const halfLife = Math.max((expiresAt - fetchedAt) / 2, this.minTtlMs);
1024
+ const refreshAt = fetchedAt + halfLife;
1025
+ this.cache = {
1026
+ token: response.jwt,
1027
+ expiresAt,
1028
+ refreshAt,
1029
+ fetchedAt,
1030
+ serverUrl
1031
+ };
1032
+ this.currentServerUrl = serverUrl;
1033
+ this.emitTokenRefreshed({
1034
+ token: response.jwt,
1035
+ expiresAt,
1036
+ serverUrl
1037
+ });
1038
+ return response.jwt;
1039
+ }
1040
+ resolveExpiryMs(jwt, expiresAt, referenceTime) {
1041
+ if (expiresAt) {
1042
+ const parsed = Date.parse(expiresAt);
1043
+ if (!Number.isNaN(parsed) && parsed > referenceTime + this.minTtlMs) {
1044
+ return parsed;
1045
+ }
1046
+ }
1047
+ const decoded = decodeJWT(jwt);
1048
+ if (decoded?.exp) {
1049
+ const expMs = decoded.exp * 1e3;
1050
+ if (expMs > referenceTime + this.minTtlMs) {
1051
+ return expMs;
1052
+ }
1053
+ }
1054
+ return referenceTime + Math.max(this.minTtlMs, 6e4);
1055
+ }
1056
+ extractServerUrl(payload) {
1057
+ const possibleUrl = payload?.server_url;
1058
+ if (typeof possibleUrl === "string" && possibleUrl.trim()) {
1059
+ return possibleUrl.trim();
1060
+ }
1061
+ return void 0;
1062
+ }
1063
+ emitTokenRefreshed(info) {
1064
+ if (this.onTokenRefreshed) {
1065
+ try {
1066
+ this.onTokenRefreshed(info);
1067
+ } catch (error) {
1068
+ console.warn("JWTFactory onTokenRefreshed callback failed:", error);
1069
+ }
1070
+ }
1071
+ }
1072
+ getServerUrl() {
1073
+ return this.currentServerUrl ?? this.cache?.serverUrl;
1074
+ }
1075
+ };
1076
+
1077
+ // src/coderule-clients.ts
1078
+ init_esm_shims();
1079
+ var DEFAULT_AUTH_BASE_URL = "https://r.coderule.ai:16803";
1080
+ var DEFAULT_SERVICE_BASE_URL = "https://s1.coderule.ai:16803";
1081
+ var DEFAULT_TIMEOUTS = {
1082
+ auth: 3e4,
1083
+ ast: 6e4,
1084
+ retrieval: 6e4,
1085
+ sync: 6e4
1086
+ };
1087
+ function resolveOverrides(options) {
1088
+ return {
1089
+ auth: options.auth,
1090
+ ast: options.ast,
1091
+ retrieval: options.retrieval,
1092
+ sync: options.sync
1093
+ };
1094
+ }
1095
+ function resolveTimeout(service, overrides) {
1096
+ return overrides[service]?.timeout ?? DEFAULT_TIMEOUTS[service];
1097
+ }
1098
+ var CoderuleClients = class {
1099
+ constructor(options) {
1100
+ if (!options?.token) {
1101
+ throw new Error("CoderuleClients requires a non-empty token");
1102
+ }
1103
+ const overrides = resolveOverrides(options);
1104
+ const baseUrl = options.baseUrl;
1105
+ const authBase = overrides.auth?.baseUrl ?? baseUrl ?? DEFAULT_AUTH_BASE_URL;
1106
+ const authTimeout = resolveTimeout("auth", overrides);
1107
+ this.auth = new AuthHttpClient(authBase, authTimeout);
1108
+ const userTokenCallback = options.jwtFactory?.onTokenRefreshed;
1109
+ const jwtOptions = {
1110
+ ...options.jwtFactory,
1111
+ onTokenRefreshed: (info) => {
1112
+ userTokenCallback?.(info);
1113
+ if (info.serverUrl) {
1114
+ this.applyServerUrl(info.serverUrl);
1115
+ }
1116
+ }
1117
+ };
1118
+ this.jwtFactory = new JWTFactory(this.auth, options.token, jwtOptions);
1119
+ this.serviceBaseLocked = {
1120
+ ast: Boolean(overrides.ast?.baseUrl),
1121
+ retrieval: Boolean(overrides.retrieval?.baseUrl),
1122
+ sync: Boolean(overrides.sync?.baseUrl)
1123
+ };
1124
+ const defaultServiceBase = baseUrl ?? DEFAULT_SERVICE_BASE_URL;
1125
+ this.ast = new ASTHttpClient({
1126
+ baseUrl: overrides.ast?.baseUrl ?? defaultServiceBase,
1127
+ timeout: resolveTimeout("ast", overrides),
1128
+ jwtProvider: this.jwtFactory
1129
+ });
1130
+ this.retrieval = new RetrievalHttpClient({
1131
+ baseUrl: overrides.retrieval?.baseUrl ?? defaultServiceBase,
1132
+ timeout: resolveTimeout("retrieval", overrides),
1133
+ jwtProvider: this.jwtFactory
1134
+ });
1135
+ this.sync = new SyncHttpClient({
1136
+ baseUrl: overrides.sync?.baseUrl ?? defaultServiceBase,
1137
+ timeout: resolveTimeout("sync", overrides),
1138
+ jwtProvider: this.jwtFactory
1139
+ });
1140
+ const initialServerUrl = this.jwtFactory.getServerUrl();
1141
+ if (initialServerUrl) {
1142
+ this.applyServerUrl(initialServerUrl);
1143
+ }
1144
+ }
1145
+ get jwt() {
1146
+ return this.jwtFactory;
1147
+ }
1148
+ async getJWT(forceRefresh = false) {
1149
+ return this.jwtFactory.getJWT(forceRefresh);
1150
+ }
1151
+ setToken(token) {
1152
+ this.jwtFactory.setSourceToken(token);
1153
+ }
1154
+ close() {
1155
+ this.ast.close();
1156
+ this.retrieval.close();
1157
+ this.sync.close();
1158
+ this.auth.close();
1159
+ }
1160
+ applyServerUrl(serverUrl) {
1161
+ const trimmed = serverUrl.trim();
1162
+ if (!trimmed || this.lastServerUrl === trimmed) {
1163
+ return;
1164
+ }
1165
+ this.lastServerUrl = trimmed;
1166
+ try {
1167
+ if (!this.serviceBaseLocked.ast) {
1168
+ this.ast.updateBaseUrl(trimmed);
1169
+ }
1170
+ } catch (error) {
1171
+ console.warn("Failed to update AST client base URL:", error);
1172
+ }
1173
+ try {
1174
+ if (!this.serviceBaseLocked.retrieval) {
1175
+ this.retrieval.updateBaseUrl(trimmed);
1176
+ }
1177
+ } catch (error) {
1178
+ console.warn("Failed to update Retrieval client base URL:", error);
1179
+ }
1180
+ try {
1181
+ if (!this.serviceBaseLocked.sync) {
1182
+ this.sync.updateBaseUrl(trimmed);
1183
+ }
1184
+ } catch (error) {
1185
+ console.warn("Failed to update Sync client base URL:", error);
1186
+ }
1187
+ }
1188
+ };
1189
+
1190
+ export { ASTHttpClient, AuthHttpClient, CoderuleClients, JWTFactory, RetrievalHttpClient, SyncHttpClient, decodeJWT, fetch_wrapper_default as fetch };
925
1191
  //# sourceMappingURL=index.js.map
926
1192
  //# sourceMappingURL=index.js.map