@coderule/clients 1.1.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.cjs CHANGED
@@ -259,18 +259,41 @@ init_cjs_shims();
259
259
  var SyncHttpClient = class {
260
260
  /**
261
261
  * Initialize the Sync HTTP client
262
- * @param baseUrl - Base URL of the Sync service (e.g., "http://localhost:8002")
263
- * @param jwtToken - JWT token for authentication
264
- * @param timeout - Request timeout in milliseconds (default: 60000)
262
+ * @param config.baseUrl - URI/URL for connecting to the HTTP server (e.g., "http://localhost:8002")
263
+ * @param config.timeout - Request timeout in milliseconds (default: 60000)
264
+ * @param config.jwtProvider - Provider for obtaining JWT tokens
265
265
  */
266
- constructor(baseUrl, jwtToken, timeout = 6e4) {
267
- this.baseUrl = baseUrl.replace(/\/$/, "");
268
- this.jwtToken = jwtToken;
266
+ constructor({
267
+ baseUrl = "http://localhost:8002",
268
+ timeout = 6e4,
269
+ jwtProvider
270
+ }) {
271
+ if (!jwtProvider) {
272
+ throw new Error("SyncHttpClient requires a JWT provider");
273
+ }
269
274
  this.timeout = timeout;
270
- this.headers = {
271
- Authorization: `Bearer ${jwtToken}`,
272
- "Content-Type": "application/json"
273
- };
275
+ this.jwtProvider = jwtProvider;
276
+ this.configureBase(baseUrl);
277
+ }
278
+ updateBaseUrl(baseUrl) {
279
+ this.configureBase(baseUrl);
280
+ }
281
+ configureBase(uri) {
282
+ let processedUri = uri;
283
+ if (!uri.startsWith("http://") && !uri.startsWith("https://")) {
284
+ processedUri = `http://${uri}`;
285
+ }
286
+ const url = new URL(processedUri);
287
+ if (url.pathname && url.pathname !== "/") {
288
+ this.baseUrl = `${url.protocol}//${url.host}${url.pathname}`;
289
+ } else {
290
+ this.baseUrl = `${url.protocol}//${url.host}`;
291
+ }
292
+ if (this.baseUrl.endsWith("/")) {
293
+ this.apiBase = `${this.baseUrl}sync/v1/`;
294
+ } else {
295
+ this.apiBase = `${this.baseUrl}/sync/v1/`;
296
+ }
274
297
  }
275
298
  /**
276
299
  * Check the status of a snapshot
@@ -279,13 +302,20 @@ var SyncHttpClient = class {
279
302
  * @throws Error on HTTP errors or connection errors
280
303
  */
281
304
  async checkSnapshotStatus(snapshotHash) {
282
- const url = `${this.baseUrl}/sync/v1/snapshots`;
305
+ if (!snapshotHash) {
306
+ throw new Error("Snapshot hash must be provided");
307
+ }
308
+ const jwt = await this.jwtProvider.getJWT();
309
+ const url = `${this.apiBase}snapshots`;
283
310
  try {
284
311
  const controller = new AbortController2();
285
312
  const timeoutId = setTimeout(() => controller.abort(), this.timeout);
286
313
  const response = await fetch_wrapper_default(url, {
287
314
  method: "POST",
288
- headers: this.headers,
315
+ headers: {
316
+ Authorization: `Bearer ${jwt}`,
317
+ "Content-Type": "application/json"
318
+ },
289
319
  body: JSON.stringify({ snapshot_hash: snapshotHash }),
290
320
  signal: controller.signal
291
321
  });
@@ -317,13 +347,20 @@ var SyncHttpClient = class {
317
347
  * @throws Error on HTTP errors or connection errors
318
348
  */
319
349
  async createSnapshot(snapshotHash, files) {
320
- const url = `${this.baseUrl}/sync/v1/snapshots`;
350
+ if (!snapshotHash) {
351
+ throw new Error("Snapshot hash must be provided");
352
+ }
353
+ const jwt = await this.jwtProvider.getJWT();
354
+ const url = `${this.apiBase}snapshots`;
321
355
  try {
322
356
  const controller = new AbortController2();
323
357
  const timeoutId = setTimeout(() => controller.abort(), this.timeout);
324
358
  const response = await fetch_wrapper_default(url, {
325
359
  method: "POST",
326
- headers: this.headers,
360
+ headers: {
361
+ Authorization: `Bearer ${jwt}`,
362
+ "Content-Type": "application/json"
363
+ },
327
364
  body: JSON.stringify({
328
365
  snapshot_hash: snapshotHash,
329
366
  files
@@ -364,7 +401,8 @@ var SyncHttpClient = class {
364
401
  * @throws Error on HTTP errors or connection errors
365
402
  */
366
403
  async uploadFileContent(filesContent) {
367
- const url = `${this.baseUrl}/sync/v1/files/content`;
404
+ const jwt = await this.jwtProvider.getJWT();
405
+ const url = `${this.apiBase}files/content`;
368
406
  try {
369
407
  const controller = new AbortController2();
370
408
  const timeoutId = setTimeout(() => controller.abort(), this.timeout);
@@ -374,8 +412,9 @@ var SyncHttpClient = class {
374
412
  const blob = new Blob([content], { type: "application/octet-stream" });
375
413
  formData.append(fileHash, blob, fileData.path);
376
414
  }
377
- const uploadHeaders = { ...this.headers };
378
- delete uploadHeaders["Content-Type"];
415
+ const uploadHeaders = {
416
+ Authorization: `Bearer ${jwt}`
417
+ };
379
418
  const response = await fetch_wrapper_default(url, {
380
419
  method: "POST",
381
420
  headers: uploadHeaders,
@@ -432,13 +471,13 @@ var SyncHttpClient = class {
432
471
  * @throws Error on HTTP errors or connection errors
433
472
  */
434
473
  async health() {
435
- const url = `${this.baseUrl}/sync/v1/health`;
474
+ const url = `${this.apiBase}health`;
436
475
  try {
437
476
  const controller = new AbortController2();
438
477
  const timeoutId = setTimeout(() => controller.abort(), this.timeout);
439
478
  const response = await fetch_wrapper_default(url, {
440
479
  method: "GET",
441
- headers: { Authorization: `Bearer ${this.jwtToken}` },
480
+ // No authentication required for health check
442
481
  signal: controller.signal
443
482
  });
444
483
  clearTimeout(timeoutId);
@@ -470,10 +509,28 @@ init_cjs_shims();
470
509
  var RetrievalHttpClient = class {
471
510
  /**
472
511
  * Initialize the Retrieval HTTP client
473
- * @param uri - URI/URL for connecting to the HTTP server (e.g., "http://localhost:8004")
474
- * @param timeout - Request timeout in milliseconds (default: 60000)
512
+ * @param config.baseUrl - URI/URL for connecting to the HTTP server (e.g., "http://localhost:8004")
513
+ * @param config.timeout - Request timeout in milliseconds (default: 60000)
514
+ * @param config.jwtProvider - Provider for obtaining JWT tokens
475
515
  */
476
- constructor(uri = "http://localhost:8004", timeout = 6e4) {
516
+ constructor({
517
+ baseUrl = "http://localhost:8004",
518
+ timeout = 6e4,
519
+ jwtProvider
520
+ }) {
521
+ if (!jwtProvider) {
522
+ throw new Error("RetrievalHttpClient requires a JWT provider");
523
+ }
524
+ this.timeout = timeout;
525
+ this.jwtProvider = jwtProvider;
526
+ this.configureBase(baseUrl);
527
+ console.debug(`Initialized HTTP client for ${this.baseUrl}`);
528
+ console.debug(`API base: ${this.apiBase}`);
529
+ }
530
+ updateBaseUrl(baseUrl) {
531
+ this.configureBase(baseUrl);
532
+ }
533
+ configureBase(uri) {
477
534
  let processedUri = uri;
478
535
  if (!uri.startsWith("http://") && !uri.startsWith("https://")) {
479
536
  processedUri = `http://${uri}`;
@@ -484,14 +541,11 @@ var RetrievalHttpClient = class {
484
541
  } else {
485
542
  this.baseUrl = `${url.protocol}//${url.host}`;
486
543
  }
487
- this.timeout = timeout;
488
544
  if (this.baseUrl.endsWith("/")) {
489
545
  this.apiBase = `${this.baseUrl}api/retrieval/`;
490
546
  } else {
491
547
  this.apiBase = `${this.baseUrl}/api/retrieval/`;
492
548
  }
493
- console.debug(`Initialized HTTP client for ${this.baseUrl}`);
494
- console.debug(`API base: ${this.apiBase}`);
495
549
  }
496
550
  /**
497
551
  * Check the health status of the Retrieval service
@@ -540,25 +594,22 @@ var RetrievalHttpClient = class {
540
594
  * @param snapshotHash - SHA256 hash of the codebase snapshot
541
595
  * @param queryText - Natural language query for retrieval
542
596
  * @param budgetTokens - Maximum token budget for results (default: 3000)
543
- * @param jwt - JWT token for authorization (required)
544
597
  * @param options - Optional retrieval parameters
545
598
  * @returns Retrieval results with formatted output
546
599
  * @throws Error on query failures
547
600
  */
548
- async query(snapshotHash, queryText, budgetTokens = 3e3, jwt, options) {
601
+ async query(snapshotHash, queryText, budgetTokens = 3e3, options) {
549
602
  if (!snapshotHash) {
550
603
  throw new Error("Snapshot hash must be provided");
551
604
  }
552
605
  if (!queryText) {
553
606
  throw new Error("Query text must be provided");
554
607
  }
555
- if (!jwt) {
556
- throw new Error("JWT must be provided");
557
- }
558
608
  if (budgetTokens < 100) {
559
609
  throw new Error("Budget tokens must be at least 100");
560
610
  }
561
611
  const startTime = Date.now();
612
+ const jwt = await this.jwtProvider.getJWT();
562
613
  const queryEndpoint = `${this.apiBase}query`;
563
614
  try {
564
615
  const controller = new AbortController2();
@@ -615,13 +666,11 @@ var RetrievalHttpClient = class {
615
666
  * @returns Snapshot status information
616
667
  * @throws Error on status check failures
617
668
  */
618
- async checkSnapshotStatus(snapshotHash, jwt) {
669
+ async checkSnapshotStatus(snapshotHash) {
619
670
  if (!snapshotHash) {
620
671
  throw new Error("Snapshot hash must be provided");
621
672
  }
622
- if (!jwt) {
623
- throw new Error("JWT must be provided");
624
- }
673
+ const jwt = await this.jwtProvider.getJWT();
625
674
  const statusEndpoint = `${this.apiBase}snapshots/${snapshotHash}/status`;
626
675
  try {
627
676
  const controller = new AbortController2();
@@ -666,10 +715,8 @@ var RetrievalHttpClient = class {
666
715
  * @returns true if cache cleared successfully
667
716
  * @throws Error on cache clear failures
668
717
  */
669
- async clearCache(jwt) {
670
- if (!jwt) {
671
- throw new Error("JWT must be provided");
672
- }
718
+ async clearCache() {
719
+ const jwt = await this.jwtProvider.getJWT();
673
720
  const cacheEndpoint = `${this.apiBase}cache`;
674
721
  try {
675
722
  const controller = new AbortController2();
@@ -704,10 +751,8 @@ var RetrievalHttpClient = class {
704
751
  * @returns Cache statistics
705
752
  * @throws Error on stats retrieval failures
706
753
  */
707
- async getCacheStats(jwt) {
708
- if (!jwt) {
709
- throw new Error("JWT must be provided");
710
- }
754
+ async getCacheStats() {
755
+ const jwt = await this.jwtProvider.getJWT();
711
756
  const statsEndpoint = `${this.apiBase}cache/stats`;
712
757
  try {
713
758
  const controller = new AbortController2();
@@ -753,7 +798,7 @@ var RetrievalHttpClient = class {
753
798
  * @param formatter - Output format "standard" or "compact"
754
799
  * @returns Retrieval results
755
800
  */
756
- async queryWithOptions(snapshotHash, queryText, jwt, budgetTokens = 3e3, flowStrength = 1.5, blendAlpha = 0.8, hopDepth = 2, maxIterations = 12, split = 0.8, formatter = "standard") {
801
+ async queryWithOptions(snapshotHash, queryText, budgetTokens = 3e3, flowStrength = 1.5, blendAlpha = 0.8, hopDepth = 2, maxIterations = 12, split = 0.8, formatter = "standard") {
757
802
  const options = {
758
803
  flow_strength: flowStrength,
759
804
  blend_alpha: blendAlpha,
@@ -762,7 +807,164 @@ var RetrievalHttpClient = class {
762
807
  split,
763
808
  formatter
764
809
  };
765
- return this.query(snapshotHash, queryText, budgetTokens, jwt, options);
810
+ return this.query(snapshotHash, queryText, budgetTokens, options);
811
+ }
812
+ /**
813
+ * Close the HTTP client connection (no-op for fetch)
814
+ */
815
+ close() {
816
+ }
817
+ };
818
+
819
+ // src/clients/ast-client.ts
820
+ init_cjs_shims();
821
+ var ASTHttpClient = class {
822
+ /**
823
+ * Initialize the AST HTTP client
824
+ * @param config.baseUrl - URI/URL for connecting to the HTTP server (e.g., "http://localhost:8003")
825
+ * @param config.timeout - Request timeout in milliseconds (default: 60000)
826
+ * @param config.jwtProvider - Provider for obtaining JWT tokens
827
+ */
828
+ constructor({
829
+ baseUrl = "http://localhost:8003",
830
+ timeout = 6e4,
831
+ jwtProvider
832
+ }) {
833
+ if (!jwtProvider) {
834
+ throw new Error("ASTHttpClient requires a JWT provider");
835
+ }
836
+ this.timeout = timeout;
837
+ this.jwtProvider = jwtProvider;
838
+ this.configureBase(baseUrl);
839
+ }
840
+ updateBaseUrl(baseUrl) {
841
+ this.configureBase(baseUrl);
842
+ }
843
+ configureBase(uri) {
844
+ let processedUri = uri;
845
+ if (!uri.startsWith("http://") && !uri.startsWith("https://")) {
846
+ processedUri = `http://${uri}`;
847
+ }
848
+ const url = new URL(processedUri);
849
+ if (url.pathname && url.pathname !== "/") {
850
+ this.baseUrl = `${url.protocol}//${url.host}${url.pathname}`;
851
+ } else {
852
+ this.baseUrl = `${url.protocol}//${url.host}`;
853
+ }
854
+ if (this.baseUrl.endsWith("/")) {
855
+ this.apiBase = `${this.baseUrl}api/ast/`;
856
+ } else {
857
+ this.apiBase = `${this.baseUrl}/api/ast/`;
858
+ }
859
+ }
860
+ /**
861
+ * Get file visitor rules in v2 format optimized for Chokidar v4
862
+ * @returns Visitor rules with format, include_extensions, include_filenames, exclude_dirnames
863
+ * @throws Error on HTTP errors or connection errors
864
+ */
865
+ async getVisitorRulesV2() {
866
+ const jwt = await this.jwtProvider.getJWT();
867
+ const url = `${this.apiBase}visitor-rules`;
868
+ try {
869
+ const controller = new AbortController2();
870
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
871
+ const response = await fetch_wrapper_default(url, {
872
+ method: "GET",
873
+ headers: {
874
+ Authorization: `Bearer ${jwt}`
875
+ },
876
+ signal: controller.signal
877
+ });
878
+ clearTimeout(timeoutId);
879
+ if (!response.ok) {
880
+ const errorText = await response.text();
881
+ throw new Error(
882
+ `Failed to get visitor rules v2 with status ${response.status}: ${errorText}`
883
+ );
884
+ }
885
+ const data = await response.json();
886
+ if (data.format !== "segments+exts@v2") {
887
+ throw new Error(`Unexpected visitor rules format: ${data.format}`);
888
+ }
889
+ return data;
890
+ } catch (error) {
891
+ if (error.name === "AbortError") {
892
+ throw new Error(`Request timeout after ${this.timeout}ms`);
893
+ }
894
+ console.error(`Failed to get visitor rules v2: ${error.message}`);
895
+ throw error;
896
+ }
897
+ }
898
+ /**
899
+ * Check the health status of the AST service
900
+ * @returns Health status information
901
+ * @throws Error on HTTP errors or connection errors
902
+ */
903
+ async health() {
904
+ const url = `${this.apiBase}health`;
905
+ try {
906
+ const controller = new AbortController2();
907
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
908
+ const response = await fetch_wrapper_default(url, {
909
+ method: "GET",
910
+ // No authentication required for health check
911
+ signal: controller.signal
912
+ });
913
+ clearTimeout(timeoutId);
914
+ if (!response.ok) {
915
+ const errorText = await response.text();
916
+ throw new Error(
917
+ `Health check failed with status ${response.status}: ${errorText}`
918
+ );
919
+ }
920
+ const data = await response.json();
921
+ return data;
922
+ } catch (error) {
923
+ if (error.name === "AbortError") {
924
+ throw new Error(`Request timeout after ${this.timeout}ms`);
925
+ }
926
+ console.error(`Health check failed: ${error.message}`);
927
+ throw error;
928
+ }
929
+ }
930
+ /**
931
+ * Compile visitor rules v2 for use with Chokidar v4
932
+ * This is a helper method that compiles the rules into a format
933
+ * that can be directly used with Chokidar's ignored option
934
+ * @param rules - The visitor rules v2 from the server
935
+ * @returns Compiled rules with Sets and RegExp for efficient matching
936
+ */
937
+ static compileRulesV2(rules) {
938
+ if (rules.format !== "segments+exts@v2") {
939
+ throw new Error(`Unsupported rules format: ${rules.format}`);
940
+ }
941
+ const exts = new Set(rules.include_extensions.map((e) => e.toLowerCase()));
942
+ const names = new Set(rules.include_filenames.map((n) => n.toLowerCase()));
943
+ const dirs = new Set(rules.exclude_dirnames.map((n) => n.toLowerCase()));
944
+ const escapeRe = (s) => s.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&");
945
+ const dirRe = new RegExp(
946
+ `(?:^|[\\/])(?:${[...dirs].map(escapeRe).join("|")})(?:[\\/]|$)`,
947
+ "i"
948
+ );
949
+ return { exts, names, dirRe };
950
+ }
951
+ /**
952
+ * Build Chokidar v4 `ignored` predicate from compiled rules
953
+ * @param compiled - Compiled rules from compileRulesV2
954
+ * @returns Predicate function that returns true to ignore, false to watch
955
+ */
956
+ static buildIgnoredPredicate(compiled) {
957
+ return (p, stats) => {
958
+ const posix = p.replace(/\\/g, "/");
959
+ if (compiled.dirRe.test(posix)) return true;
960
+ if (stats?.isDirectory?.()) return false;
961
+ const base = posix.split("/").pop()?.toLowerCase() || "";
962
+ if (compiled.names.has(base)) return false;
963
+ const extIndex = base.lastIndexOf(".");
964
+ const ext = extIndex > -1 ? base.slice(extIndex).toLowerCase() : "";
965
+ if (!compiled.exts.has(ext)) return true;
966
+ return false;
967
+ };
766
968
  }
767
969
  /**
768
970
  * Close the HTTP client connection (no-op for fetch)
@@ -771,7 +973,246 @@ var RetrievalHttpClient = class {
771
973
  }
772
974
  };
773
975
 
976
+ // src/utils/jwt-factory.ts
977
+ init_cjs_shims();
978
+ var DEFAULT_MIN_TTL_MS = 5e3;
979
+ var JWTFactory = class {
980
+ constructor(authClient, sourceToken, options = {}) {
981
+ this.authClient = authClient;
982
+ this.sourceToken = sourceToken;
983
+ if (!sourceToken) {
984
+ throw new Error("JWTFactory requires a non-empty source token");
985
+ }
986
+ this.minTtlMs = options.minTtlMs ?? DEFAULT_MIN_TTL_MS;
987
+ this.onTokenRefreshed = options.onTokenRefreshed;
988
+ }
989
+ setSourceToken(token) {
990
+ if (!token) {
991
+ throw new Error("JWTFactory requires a non-empty source token");
992
+ }
993
+ if (token !== this.sourceToken) {
994
+ this.sourceToken = token;
995
+ this.cache = void 0;
996
+ this.currentServerUrl = void 0;
997
+ }
998
+ }
999
+ invalidate() {
1000
+ this.cache = void 0;
1001
+ this.currentServerUrl = void 0;
1002
+ }
1003
+ async getJWT(forceRefresh = false) {
1004
+ const now = Date.now();
1005
+ if (!forceRefresh && this.cache) {
1006
+ if (now < this.cache.expiresAt) {
1007
+ if (now < this.cache.refreshAt) {
1008
+ return this.cache.token;
1009
+ }
1010
+ } else {
1011
+ this.cache = void 0;
1012
+ }
1013
+ }
1014
+ return this.refresh(now, forceRefresh);
1015
+ }
1016
+ async refresh(now, forceRefresh) {
1017
+ if (!this.refreshPromise) {
1018
+ this.refreshPromise = this.fetchNewToken().finally(() => {
1019
+ this.refreshPromise = void 0;
1020
+ });
1021
+ }
1022
+ try {
1023
+ return await this.refreshPromise;
1024
+ } catch (error) {
1025
+ if (!forceRefresh && this.cache && now < this.cache.expiresAt) {
1026
+ console.warn(
1027
+ "Failed to refresh JWT, using cached token until expiry. Reason:",
1028
+ error
1029
+ );
1030
+ return this.cache.token;
1031
+ }
1032
+ throw error;
1033
+ }
1034
+ }
1035
+ async fetchNewToken() {
1036
+ const response = await this.authClient.authenticate(this.sourceToken);
1037
+ const fetchedAt = Date.now();
1038
+ const expiresAt = this.resolveExpiryMs(
1039
+ response.jwt,
1040
+ response.expires_at,
1041
+ fetchedAt
1042
+ );
1043
+ const payload = decodeJWT(response.jwt);
1044
+ const serverUrl = this.extractServerUrl(payload);
1045
+ const halfLife = Math.max((expiresAt - fetchedAt) / 2, this.minTtlMs);
1046
+ const refreshAt = fetchedAt + halfLife;
1047
+ this.cache = {
1048
+ token: response.jwt,
1049
+ expiresAt,
1050
+ refreshAt,
1051
+ fetchedAt,
1052
+ serverUrl
1053
+ };
1054
+ this.currentServerUrl = serverUrl;
1055
+ this.emitTokenRefreshed({
1056
+ token: response.jwt,
1057
+ expiresAt,
1058
+ serverUrl
1059
+ });
1060
+ return response.jwt;
1061
+ }
1062
+ resolveExpiryMs(jwt, expiresAt, referenceTime) {
1063
+ if (expiresAt) {
1064
+ const parsed = Date.parse(expiresAt);
1065
+ if (!Number.isNaN(parsed) && parsed > referenceTime + this.minTtlMs) {
1066
+ return parsed;
1067
+ }
1068
+ }
1069
+ const decoded = decodeJWT(jwt);
1070
+ if (decoded?.exp) {
1071
+ const expMs = decoded.exp * 1e3;
1072
+ if (expMs > referenceTime + this.minTtlMs) {
1073
+ return expMs;
1074
+ }
1075
+ }
1076
+ return referenceTime + Math.max(this.minTtlMs, 6e4);
1077
+ }
1078
+ extractServerUrl(payload) {
1079
+ const possibleUrl = payload?.server_url;
1080
+ if (typeof possibleUrl === "string" && possibleUrl.trim()) {
1081
+ return possibleUrl.trim();
1082
+ }
1083
+ return void 0;
1084
+ }
1085
+ emitTokenRefreshed(info) {
1086
+ if (this.onTokenRefreshed) {
1087
+ try {
1088
+ this.onTokenRefreshed(info);
1089
+ } catch (error) {
1090
+ console.warn("JWTFactory onTokenRefreshed callback failed:", error);
1091
+ }
1092
+ }
1093
+ }
1094
+ getServerUrl() {
1095
+ return this.currentServerUrl ?? this.cache?.serverUrl;
1096
+ }
1097
+ };
1098
+
1099
+ // src/coderule-clients.ts
1100
+ init_cjs_shims();
1101
+ var DEFAULT_AUTH_BASE_URL = "https://r.coderule.ai:16803";
1102
+ var DEFAULT_SERVICE_BASE_URL = "https://s1.coderule.ai:16803";
1103
+ var DEFAULT_TIMEOUTS = {
1104
+ auth: 3e4,
1105
+ ast: 6e4,
1106
+ retrieval: 6e4,
1107
+ sync: 6e4
1108
+ };
1109
+ function resolveOverrides(options) {
1110
+ return {
1111
+ auth: options.auth,
1112
+ ast: options.ast,
1113
+ retrieval: options.retrieval,
1114
+ sync: options.sync
1115
+ };
1116
+ }
1117
+ function resolveTimeout(service, overrides) {
1118
+ return overrides[service]?.timeout ?? DEFAULT_TIMEOUTS[service];
1119
+ }
1120
+ var CoderuleClients = class {
1121
+ constructor(options) {
1122
+ if (!options?.token) {
1123
+ throw new Error("CoderuleClients requires a non-empty token");
1124
+ }
1125
+ const overrides = resolveOverrides(options);
1126
+ const baseUrl = options.baseUrl;
1127
+ const authBase = overrides.auth?.baseUrl ?? baseUrl ?? DEFAULT_AUTH_BASE_URL;
1128
+ const authTimeout = resolveTimeout("auth", overrides);
1129
+ this.auth = new AuthHttpClient(authBase, authTimeout);
1130
+ const userTokenCallback = options.jwtFactory?.onTokenRefreshed;
1131
+ const jwtOptions = {
1132
+ ...options.jwtFactory,
1133
+ onTokenRefreshed: (info) => {
1134
+ userTokenCallback?.(info);
1135
+ if (info.serverUrl) {
1136
+ this.applyServerUrl(info.serverUrl);
1137
+ }
1138
+ }
1139
+ };
1140
+ this.jwtFactory = new JWTFactory(this.auth, options.token, jwtOptions);
1141
+ this.serviceBaseLocked = {
1142
+ ast: Boolean(overrides.ast?.baseUrl),
1143
+ retrieval: Boolean(overrides.retrieval?.baseUrl),
1144
+ sync: Boolean(overrides.sync?.baseUrl)
1145
+ };
1146
+ const defaultServiceBase = baseUrl ?? DEFAULT_SERVICE_BASE_URL;
1147
+ this.ast = new ASTHttpClient({
1148
+ baseUrl: overrides.ast?.baseUrl ?? defaultServiceBase,
1149
+ timeout: resolveTimeout("ast", overrides),
1150
+ jwtProvider: this.jwtFactory
1151
+ });
1152
+ this.retrieval = new RetrievalHttpClient({
1153
+ baseUrl: overrides.retrieval?.baseUrl ?? defaultServiceBase,
1154
+ timeout: resolveTimeout("retrieval", overrides),
1155
+ jwtProvider: this.jwtFactory
1156
+ });
1157
+ this.sync = new SyncHttpClient({
1158
+ baseUrl: overrides.sync?.baseUrl ?? defaultServiceBase,
1159
+ timeout: resolveTimeout("sync", overrides),
1160
+ jwtProvider: this.jwtFactory
1161
+ });
1162
+ const initialServerUrl = this.jwtFactory.getServerUrl();
1163
+ if (initialServerUrl) {
1164
+ this.applyServerUrl(initialServerUrl);
1165
+ }
1166
+ }
1167
+ get jwt() {
1168
+ return this.jwtFactory;
1169
+ }
1170
+ async getJWT(forceRefresh = false) {
1171
+ return this.jwtFactory.getJWT(forceRefresh);
1172
+ }
1173
+ setToken(token) {
1174
+ this.jwtFactory.setSourceToken(token);
1175
+ }
1176
+ close() {
1177
+ this.ast.close();
1178
+ this.retrieval.close();
1179
+ this.sync.close();
1180
+ this.auth.close();
1181
+ }
1182
+ applyServerUrl(serverUrl) {
1183
+ const trimmed = serverUrl.trim();
1184
+ if (!trimmed || this.lastServerUrl === trimmed) {
1185
+ return;
1186
+ }
1187
+ this.lastServerUrl = trimmed;
1188
+ try {
1189
+ if (!this.serviceBaseLocked.ast) {
1190
+ this.ast.updateBaseUrl(trimmed);
1191
+ }
1192
+ } catch (error) {
1193
+ console.warn("Failed to update AST client base URL:", error);
1194
+ }
1195
+ try {
1196
+ if (!this.serviceBaseLocked.retrieval) {
1197
+ this.retrieval.updateBaseUrl(trimmed);
1198
+ }
1199
+ } catch (error) {
1200
+ console.warn("Failed to update Retrieval client base URL:", error);
1201
+ }
1202
+ try {
1203
+ if (!this.serviceBaseLocked.sync) {
1204
+ this.sync.updateBaseUrl(trimmed);
1205
+ }
1206
+ } catch (error) {
1207
+ console.warn("Failed to update Sync client base URL:", error);
1208
+ }
1209
+ }
1210
+ };
1211
+
1212
+ exports.ASTHttpClient = ASTHttpClient;
774
1213
  exports.AuthHttpClient = AuthHttpClient;
1214
+ exports.CoderuleClients = CoderuleClients;
1215
+ exports.JWTFactory = JWTFactory;
775
1216
  exports.RetrievalHttpClient = RetrievalHttpClient;
776
1217
  exports.SyncHttpClient = SyncHttpClient;
777
1218
  exports.decodeJWT = decodeJWT;