lexmount 0.2.7 → 0.5.1

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.mts CHANGED
@@ -106,6 +106,12 @@ interface LexmountConfig {
106
106
  * Defaults to `https://api.lexmount.cn`.
107
107
  */
108
108
  baseUrl?: string;
109
+ /**
110
+ * Region ID to select from the public endpoint catalog.
111
+ *
112
+ * If omitted, the SDK uses the catalog default region when available.
113
+ */
114
+ region?: string;
109
115
  /**
110
116
  * Request timeout in milliseconds.
111
117
  *
@@ -117,6 +123,30 @@ interface LexmountConfig {
117
123
  */
118
124
  logLevel?: LogLevel;
119
125
  }
126
+ interface RegionCatalogEntry {
127
+ region_id: string;
128
+ display_name?: string;
129
+ default?: boolean;
130
+ host: string;
131
+ tags?: string[];
132
+ probe_path?: string;
133
+ }
134
+ interface RegionInfo {
135
+ requestedRegion?: string;
136
+ selectedRegion?: string;
137
+ baseUrl: string;
138
+ endpointBaseUrl?: string;
139
+ host?: string;
140
+ resolved: boolean;
141
+ usingRegion: boolean;
142
+ }
143
+ interface CatalogInfo {
144
+ baseUrl: string;
145
+ available: boolean;
146
+ statusCode?: number;
147
+ regions: RegionCatalogEntry[];
148
+ error?: string;
149
+ }
120
150
  /**
121
151
  * Session creation options.
122
152
  */
@@ -592,6 +622,12 @@ declare class Lexmount {
592
622
  * API base URL.
593
623
  */
594
624
  readonly baseUrl: string;
625
+ readonly region?: string;
626
+ selectedRegion?: string;
627
+ private endpointBaseUrl?;
628
+ private selectedHost?;
629
+ private regionResolutionAttempted;
630
+ private regionResolutionPromise?;
595
631
  /**
596
632
  * Session operations.
597
633
  */
@@ -624,6 +660,8 @@ declare class Lexmount {
624
660
  * @internal
625
661
  */
626
662
  _delete<T = unknown>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>;
663
+ regionInfo(): Promise<RegionInfo>;
664
+ catalogInfo(): Promise<CatalogInfo>;
627
665
  /**
628
666
  * Close the client.
629
667
  *
@@ -637,6 +675,12 @@ declare class Lexmount {
637
675
  private request;
638
676
  private normalizeRequestError;
639
677
  private getDefaultHeaders;
678
+ private ensureRegionResolved;
679
+ private resolveRegion;
680
+ private selectCatalogRegion;
681
+ private probeRegionHost;
682
+ private isAllowedCatalogHost;
683
+ private rewriteUrl;
640
684
  }
641
685
 
642
686
  /**
@@ -704,6 +748,6 @@ declare class TimeoutError extends LexmountError {
704
748
  /**
705
749
  * Lexmount Node.js SDK.
706
750
  */
707
- declare const VERSION = "0.2.5";
751
+ declare const VERSION = "0.5.1";
708
752
 
709
- export { APIError, AuthenticationError, type BrowserMode, type ContextAccessMode, type ContextCreateOptions, ContextInfo, type ContextListOptions, ContextListResponse, ContextLockedError, type ContextMetadata, ContextNotFoundError, type ContextStatus, ContextsResource, ExtensionInfo, type ExtensionInfoShape, type ExtensionListOptions, type ExtensionUploadOptions, ExtensionsResource, type ForceReleaseResponse, Lexmount, type LexmountConfig, LexmountError, LexmountLogger, type LogLevel, NetworkError, PaginationInfo, type PaginationInfoShape, type SessionContext, type SessionCreateOptions, type SessionCreateResponse, type SessionDeleteOptions, SessionDownloadInfo, type SessionDownloadInfoShape, SessionDownloadsDeleteResponse, type SessionDownloadsDeleteResponseShape, SessionDownloadsListResponse, type SessionDownloadsListResponseShape, SessionDownloadsResource, SessionInfo, type SessionListOptions, SessionListResponse, SessionNotFoundError, type SessionProxyConfig, type SessionStatus, SessionTargetInfo, type SessionTargetInfoShape, SessionsResource, TimeoutError, VERSION, ValidationError, Lexmount as default, disableLogging, enableLogging, getLogger, setLogLevel };
753
+ export { APIError, AuthenticationError, type BrowserMode, type CatalogInfo, type ContextAccessMode, type ContextCreateOptions, ContextInfo, type ContextListOptions, ContextListResponse, ContextLockedError, type ContextMetadata, ContextNotFoundError, type ContextStatus, ContextsResource, ExtensionInfo, type ExtensionInfoShape, type ExtensionListOptions, type ExtensionUploadOptions, ExtensionsResource, type ForceReleaseResponse, Lexmount, type LexmountConfig, LexmountError, LexmountLogger, type LogLevel, NetworkError, PaginationInfo, type PaginationInfoShape, type RegionCatalogEntry, type RegionInfo, type SessionContext, type SessionCreateOptions, type SessionCreateResponse, type SessionDeleteOptions, SessionDownloadInfo, type SessionDownloadInfoShape, SessionDownloadsDeleteResponse, type SessionDownloadsDeleteResponseShape, SessionDownloadsListResponse, type SessionDownloadsListResponseShape, SessionDownloadsResource, SessionInfo, type SessionListOptions, SessionListResponse, SessionNotFoundError, type SessionProxyConfig, type SessionStatus, SessionTargetInfo, type SessionTargetInfoShape, SessionsResource, TimeoutError, VERSION, ValidationError, Lexmount as default, disableLogging, enableLogging, getLogger, setLogLevel };
package/dist/index.d.ts CHANGED
@@ -106,6 +106,12 @@ interface LexmountConfig {
106
106
  * Defaults to `https://api.lexmount.cn`.
107
107
  */
108
108
  baseUrl?: string;
109
+ /**
110
+ * Region ID to select from the public endpoint catalog.
111
+ *
112
+ * If omitted, the SDK uses the catalog default region when available.
113
+ */
114
+ region?: string;
109
115
  /**
110
116
  * Request timeout in milliseconds.
111
117
  *
@@ -117,6 +123,30 @@ interface LexmountConfig {
117
123
  */
118
124
  logLevel?: LogLevel;
119
125
  }
126
+ interface RegionCatalogEntry {
127
+ region_id: string;
128
+ display_name?: string;
129
+ default?: boolean;
130
+ host: string;
131
+ tags?: string[];
132
+ probe_path?: string;
133
+ }
134
+ interface RegionInfo {
135
+ requestedRegion?: string;
136
+ selectedRegion?: string;
137
+ baseUrl: string;
138
+ endpointBaseUrl?: string;
139
+ host?: string;
140
+ resolved: boolean;
141
+ usingRegion: boolean;
142
+ }
143
+ interface CatalogInfo {
144
+ baseUrl: string;
145
+ available: boolean;
146
+ statusCode?: number;
147
+ regions: RegionCatalogEntry[];
148
+ error?: string;
149
+ }
120
150
  /**
121
151
  * Session creation options.
122
152
  */
@@ -592,6 +622,12 @@ declare class Lexmount {
592
622
  * API base URL.
593
623
  */
594
624
  readonly baseUrl: string;
625
+ readonly region?: string;
626
+ selectedRegion?: string;
627
+ private endpointBaseUrl?;
628
+ private selectedHost?;
629
+ private regionResolutionAttempted;
630
+ private regionResolutionPromise?;
595
631
  /**
596
632
  * Session operations.
597
633
  */
@@ -624,6 +660,8 @@ declare class Lexmount {
624
660
  * @internal
625
661
  */
626
662
  _delete<T = unknown>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>;
663
+ regionInfo(): Promise<RegionInfo>;
664
+ catalogInfo(): Promise<CatalogInfo>;
627
665
  /**
628
666
  * Close the client.
629
667
  *
@@ -637,6 +675,12 @@ declare class Lexmount {
637
675
  private request;
638
676
  private normalizeRequestError;
639
677
  private getDefaultHeaders;
678
+ private ensureRegionResolved;
679
+ private resolveRegion;
680
+ private selectCatalogRegion;
681
+ private probeRegionHost;
682
+ private isAllowedCatalogHost;
683
+ private rewriteUrl;
640
684
  }
641
685
 
642
686
  /**
@@ -704,6 +748,6 @@ declare class TimeoutError extends LexmountError {
704
748
  /**
705
749
  * Lexmount Node.js SDK.
706
750
  */
707
- declare const VERSION = "0.2.5";
751
+ declare const VERSION = "0.5.1";
708
752
 
709
- export { APIError, AuthenticationError, type BrowserMode, type ContextAccessMode, type ContextCreateOptions, ContextInfo, type ContextListOptions, ContextListResponse, ContextLockedError, type ContextMetadata, ContextNotFoundError, type ContextStatus, ContextsResource, ExtensionInfo, type ExtensionInfoShape, type ExtensionListOptions, type ExtensionUploadOptions, ExtensionsResource, type ForceReleaseResponse, Lexmount, type LexmountConfig, LexmountError, LexmountLogger, type LogLevel, NetworkError, PaginationInfo, type PaginationInfoShape, type SessionContext, type SessionCreateOptions, type SessionCreateResponse, type SessionDeleteOptions, SessionDownloadInfo, type SessionDownloadInfoShape, SessionDownloadsDeleteResponse, type SessionDownloadsDeleteResponseShape, SessionDownloadsListResponse, type SessionDownloadsListResponseShape, SessionDownloadsResource, SessionInfo, type SessionListOptions, SessionListResponse, SessionNotFoundError, type SessionProxyConfig, type SessionStatus, SessionTargetInfo, type SessionTargetInfoShape, SessionsResource, TimeoutError, VERSION, ValidationError, Lexmount as default, disableLogging, enableLogging, getLogger, setLogLevel };
753
+ export { APIError, AuthenticationError, type BrowserMode, type CatalogInfo, type ContextAccessMode, type ContextCreateOptions, ContextInfo, type ContextListOptions, ContextListResponse, ContextLockedError, type ContextMetadata, ContextNotFoundError, type ContextStatus, ContextsResource, ExtensionInfo, type ExtensionInfoShape, type ExtensionListOptions, type ExtensionUploadOptions, ExtensionsResource, type ForceReleaseResponse, Lexmount, type LexmountConfig, LexmountError, LexmountLogger, type LogLevel, NetworkError, PaginationInfo, type PaginationInfoShape, type RegionCatalogEntry, type RegionInfo, type SessionContext, type SessionCreateOptions, type SessionCreateResponse, type SessionDeleteOptions, SessionDownloadInfo, type SessionDownloadInfoShape, SessionDownloadsDeleteResponse, type SessionDownloadsDeleteResponseShape, SessionDownloadsListResponse, type SessionDownloadsListResponseShape, SessionDownloadsResource, SessionInfo, type SessionListOptions, SessionListResponse, SessionNotFoundError, type SessionProxyConfig, type SessionStatus, SessionTargetInfo, type SessionTargetInfoShape, SessionsResource, TimeoutError, VERSION, ValidationError, Lexmount as default, disableLogging, enableLogging, getLogger, setLogLevel };
package/dist/index.js CHANGED
@@ -1159,6 +1159,7 @@ var SessionsResource = class {
1159
1159
  dotenv.config();
1160
1160
  var Lexmount = class {
1161
1161
  constructor(config2 = {}) {
1162
+ this.regionResolutionAttempted = false;
1162
1163
  this.apiKey = config2.apiKey ?? process.env.LEXMOUNT_API_KEY ?? "";
1163
1164
  if (!this.apiKey) {
1164
1165
  throw new ValidationError(
@@ -1172,6 +1173,7 @@ var Lexmount = class {
1172
1173
  );
1173
1174
  }
1174
1175
  this.baseUrl = (config2.baseUrl ?? process.env.LEXMOUNT_BASE_URL ?? "https://api.lexmount.cn").replace(/\/$/, "");
1176
+ this.region = config2.region;
1175
1177
  if (config2.logLevel) {
1176
1178
  setLogLevel(config2.logLevel);
1177
1179
  }
@@ -1189,6 +1191,7 @@ var Lexmount = class {
1189
1191
  * @internal
1190
1192
  */
1191
1193
  async _post(url, data, config2 = {}) {
1194
+ await this.ensureRegionResolved();
1192
1195
  return this.request("POST", url, { ...config2, data });
1193
1196
  }
1194
1197
  /**
@@ -1197,6 +1200,7 @@ var Lexmount = class {
1197
1200
  * @internal
1198
1201
  */
1199
1202
  async _get(url, params, config2 = {}) {
1203
+ await this.ensureRegionResolved();
1200
1204
  return this.request("GET", url, { ...config2, params });
1201
1205
  }
1202
1206
  /**
@@ -1205,8 +1209,51 @@ var Lexmount = class {
1205
1209
  * @internal
1206
1210
  */
1207
1211
  async _delete(url, data, config2 = {}) {
1212
+ await this.ensureRegionResolved();
1208
1213
  return this.request("DELETE", url, { ...config2, data });
1209
1214
  }
1215
+ async regionInfo() {
1216
+ await this.ensureRegionResolved();
1217
+ return {
1218
+ requestedRegion: this.region,
1219
+ selectedRegion: this.selectedRegion,
1220
+ baseUrl: this.baseUrl,
1221
+ endpointBaseUrl: this.endpointBaseUrl,
1222
+ host: this.selectedHost,
1223
+ resolved: this.regionResolutionAttempted,
1224
+ usingRegion: this.selectedRegion !== void 0
1225
+ };
1226
+ }
1227
+ async catalogInfo() {
1228
+ try {
1229
+ const response = await this.httpClient.get(
1230
+ `${this.baseUrl}/v1/regions/catalog`
1231
+ );
1232
+ if (response.status !== 200) {
1233
+ return {
1234
+ baseUrl: this.baseUrl,
1235
+ available: false,
1236
+ statusCode: response.status,
1237
+ regions: []
1238
+ };
1239
+ }
1240
+ const regions = Array.isArray(response.data?.regions) ? response.data.regions : [];
1241
+ return {
1242
+ baseUrl: this.baseUrl,
1243
+ available: Array.isArray(response.data?.regions),
1244
+ statusCode: response.status,
1245
+ regions,
1246
+ error: Array.isArray(response.data?.regions) ? void 0 : "invalid catalog regions"
1247
+ };
1248
+ } catch (error) {
1249
+ return {
1250
+ baseUrl: this.baseUrl,
1251
+ available: false,
1252
+ regions: [],
1253
+ error: error instanceof Error ? error.message : String(error)
1254
+ };
1255
+ }
1256
+ }
1210
1257
  /**
1211
1258
  * Close the client.
1212
1259
  *
@@ -1224,7 +1271,8 @@ var Lexmount = class {
1224
1271
  async request(method, url, config2) {
1225
1272
  const logger2 = getLogger();
1226
1273
  const startTime = Date.now();
1227
- logger2.debug(`${method} request to ${url}`);
1274
+ const requestUrl = this.rewriteUrl(url);
1275
+ logger2.debug(`${method} request to ${requestUrl}`);
1228
1276
  try {
1229
1277
  const headers = {
1230
1278
  api_key: this.apiKey,
@@ -1234,12 +1282,14 @@ var Lexmount = class {
1234
1282
  };
1235
1283
  const response = await this.httpClient.request({
1236
1284
  method,
1237
- url,
1285
+ url: requestUrl,
1238
1286
  ...config2,
1239
1287
  headers
1240
1288
  });
1241
1289
  const elapsed = Date.now() - startTime;
1242
- logger2.debug(`${method} response: status=${response.status}, duration=${elapsed.toFixed(2)}ms`);
1290
+ logger2.debug(
1291
+ `${method} response: status=${response.status}, duration=${elapsed.toFixed(2)}ms`
1292
+ );
1243
1293
  return response;
1244
1294
  } catch (error) {
1245
1295
  const elapsed = Date.now() - startTime;
@@ -1278,10 +1328,101 @@ var Lexmount = class {
1278
1328
  "Content-Type": "application/json"
1279
1329
  };
1280
1330
  }
1331
+ async ensureRegionResolved() {
1332
+ if (this.regionResolutionAttempted) {
1333
+ return;
1334
+ }
1335
+ if (!this.regionResolutionPromise) {
1336
+ this.regionResolutionPromise = this.resolveRegion().finally(() => {
1337
+ this.regionResolutionAttempted = true;
1338
+ this.regionResolutionPromise = void 0;
1339
+ });
1340
+ }
1341
+ await this.regionResolutionPromise;
1342
+ }
1343
+ async resolveRegion() {
1344
+ const logger2 = getLogger();
1345
+ try {
1346
+ const response = await this.httpClient.get(
1347
+ `${this.baseUrl}/v1/regions/catalog`
1348
+ );
1349
+ if (response.status !== 200 || !Array.isArray(response.data?.regions)) {
1350
+ logger2.debug(
1351
+ `Region catalog unavailable, falling back to baseUrl: status=${response.status}`
1352
+ );
1353
+ return;
1354
+ }
1355
+ const selected = this.selectCatalogRegion(response.data.regions);
1356
+ if (!selected) {
1357
+ if (this.region) {
1358
+ logger2.warn(`Requested region not found in catalog: ${this.region}`);
1359
+ } else {
1360
+ logger2.debug("No default catalog region, falling back to baseUrl");
1361
+ }
1362
+ return;
1363
+ }
1364
+ const endpointBaseUrl = await this.probeRegionHost(selected);
1365
+ if (!endpointBaseUrl) {
1366
+ logger2.debug("Region probe failed, falling back to baseUrl");
1367
+ return;
1368
+ }
1369
+ this.selectedRegion = selected.region_id;
1370
+ this.selectedHost = selected.host;
1371
+ this.endpointBaseUrl = endpointBaseUrl;
1372
+ } catch (error) {
1373
+ logger2.debug("Region resolution failed, falling back to baseUrl:", error);
1374
+ }
1375
+ }
1376
+ selectCatalogRegion(regions) {
1377
+ if (this.region) {
1378
+ return regions.find((region) => region.region_id === this.region);
1379
+ }
1380
+ const defaultRegions = regions.filter((region) => region.default === true);
1381
+ return defaultRegions.length === 1 ? defaultRegions[0] : void 0;
1382
+ }
1383
+ async probeRegionHost(region) {
1384
+ const logger2 = getLogger();
1385
+ const host = String(region.host || "").trim();
1386
+ if (!host) {
1387
+ return void 0;
1388
+ }
1389
+ if (!this.isAllowedCatalogHost(host)) {
1390
+ logger2.warn(`Ignoring invalid region catalog host: ${host}`);
1391
+ return void 0;
1392
+ }
1393
+ const endpointBaseUrl = `https://${host}`;
1394
+ const probePath = String(region.probe_path || "/v1/region/probe").replace(/^(?!\/)/, "/");
1395
+ try {
1396
+ const response = await this.httpClient.get(`${endpointBaseUrl}${probePath}`);
1397
+ return response.status === 200 ? endpointBaseUrl : void 0;
1398
+ } catch {
1399
+ return void 0;
1400
+ }
1401
+ }
1402
+ isAllowedCatalogHost(host) {
1403
+ let parsed;
1404
+ try {
1405
+ parsed = new URL(`https://${host}`);
1406
+ } catch {
1407
+ return false;
1408
+ }
1409
+ if (parsed.username || parsed.password || parsed.port || parsed.pathname !== "/" || parsed.search || parsed.hash) {
1410
+ return false;
1411
+ }
1412
+ const hostname = parsed.hostname.toLowerCase();
1413
+ const allowedDomains = ["lexmount.cn", "lexmount.com", "lexmount.net"];
1414
+ return allowedDomains.some((domain) => hostname === domain || hostname.endsWith(`.${domain}`));
1415
+ }
1416
+ rewriteUrl(url) {
1417
+ if (!this.endpointBaseUrl || !url.startsWith(this.baseUrl)) {
1418
+ return url;
1419
+ }
1420
+ return `${this.endpointBaseUrl}${url.slice(this.baseUrl.length)}`;
1421
+ }
1281
1422
  };
1282
1423
 
1283
1424
  // src/index.ts
1284
- var VERSION = "0.2.5";
1425
+ var VERSION = "0.5.1";
1285
1426
  // Annotate the CommonJS export names for ESM import in node:
1286
1427
  0 && (module.exports = {
1287
1428
  APIError,