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.mjs CHANGED
@@ -1,7 +1,5 @@
1
1
  // src/client.ts
2
- import {
3
- isAxiosError
4
- } from "axios";
2
+ import { isAxiosError } from "axios";
5
3
  import axios from "axios";
6
4
  import * as dotenv from "dotenv";
7
5
 
@@ -1095,6 +1093,7 @@ var SessionsResource = class {
1095
1093
  dotenv.config();
1096
1094
  var Lexmount = class {
1097
1095
  constructor(config2 = {}) {
1096
+ this.regionResolutionAttempted = false;
1098
1097
  this.apiKey = config2.apiKey ?? process.env.LEXMOUNT_API_KEY ?? "";
1099
1098
  if (!this.apiKey) {
1100
1099
  throw new ValidationError(
@@ -1108,6 +1107,7 @@ var Lexmount = class {
1108
1107
  );
1109
1108
  }
1110
1109
  this.baseUrl = (config2.baseUrl ?? process.env.LEXMOUNT_BASE_URL ?? "https://api.lexmount.cn").replace(/\/$/, "");
1110
+ this.region = config2.region;
1111
1111
  if (config2.logLevel) {
1112
1112
  setLogLevel(config2.logLevel);
1113
1113
  }
@@ -1125,6 +1125,7 @@ var Lexmount = class {
1125
1125
  * @internal
1126
1126
  */
1127
1127
  async _post(url, data, config2 = {}) {
1128
+ await this.ensureRegionResolved();
1128
1129
  return this.request("POST", url, { ...config2, data });
1129
1130
  }
1130
1131
  /**
@@ -1133,6 +1134,7 @@ var Lexmount = class {
1133
1134
  * @internal
1134
1135
  */
1135
1136
  async _get(url, params, config2 = {}) {
1137
+ await this.ensureRegionResolved();
1136
1138
  return this.request("GET", url, { ...config2, params });
1137
1139
  }
1138
1140
  /**
@@ -1141,8 +1143,51 @@ var Lexmount = class {
1141
1143
  * @internal
1142
1144
  */
1143
1145
  async _delete(url, data, config2 = {}) {
1146
+ await this.ensureRegionResolved();
1144
1147
  return this.request("DELETE", url, { ...config2, data });
1145
1148
  }
1149
+ async regionInfo() {
1150
+ await this.ensureRegionResolved();
1151
+ return {
1152
+ requestedRegion: this.region,
1153
+ selectedRegion: this.selectedRegion,
1154
+ baseUrl: this.baseUrl,
1155
+ endpointBaseUrl: this.endpointBaseUrl,
1156
+ host: this.selectedHost,
1157
+ resolved: this.regionResolutionAttempted,
1158
+ usingRegion: this.selectedRegion !== void 0
1159
+ };
1160
+ }
1161
+ async catalogInfo() {
1162
+ try {
1163
+ const response = await this.httpClient.get(
1164
+ `${this.baseUrl}/v1/regions/catalog`
1165
+ );
1166
+ if (response.status !== 200) {
1167
+ return {
1168
+ baseUrl: this.baseUrl,
1169
+ available: false,
1170
+ statusCode: response.status,
1171
+ regions: []
1172
+ };
1173
+ }
1174
+ const regions = Array.isArray(response.data?.regions) ? response.data.regions : [];
1175
+ return {
1176
+ baseUrl: this.baseUrl,
1177
+ available: Array.isArray(response.data?.regions),
1178
+ statusCode: response.status,
1179
+ regions,
1180
+ error: Array.isArray(response.data?.regions) ? void 0 : "invalid catalog regions"
1181
+ };
1182
+ } catch (error) {
1183
+ return {
1184
+ baseUrl: this.baseUrl,
1185
+ available: false,
1186
+ regions: [],
1187
+ error: error instanceof Error ? error.message : String(error)
1188
+ };
1189
+ }
1190
+ }
1146
1191
  /**
1147
1192
  * Close the client.
1148
1193
  *
@@ -1160,7 +1205,8 @@ var Lexmount = class {
1160
1205
  async request(method, url, config2) {
1161
1206
  const logger2 = getLogger();
1162
1207
  const startTime = Date.now();
1163
- logger2.debug(`${method} request to ${url}`);
1208
+ const requestUrl = this.rewriteUrl(url);
1209
+ logger2.debug(`${method} request to ${requestUrl}`);
1164
1210
  try {
1165
1211
  const headers = {
1166
1212
  api_key: this.apiKey,
@@ -1170,12 +1216,14 @@ var Lexmount = class {
1170
1216
  };
1171
1217
  const response = await this.httpClient.request({
1172
1218
  method,
1173
- url,
1219
+ url: requestUrl,
1174
1220
  ...config2,
1175
1221
  headers
1176
1222
  });
1177
1223
  const elapsed = Date.now() - startTime;
1178
- logger2.debug(`${method} response: status=${response.status}, duration=${elapsed.toFixed(2)}ms`);
1224
+ logger2.debug(
1225
+ `${method} response: status=${response.status}, duration=${elapsed.toFixed(2)}ms`
1226
+ );
1179
1227
  return response;
1180
1228
  } catch (error) {
1181
1229
  const elapsed = Date.now() - startTime;
@@ -1214,10 +1262,101 @@ var Lexmount = class {
1214
1262
  "Content-Type": "application/json"
1215
1263
  };
1216
1264
  }
1265
+ async ensureRegionResolved() {
1266
+ if (this.regionResolutionAttempted) {
1267
+ return;
1268
+ }
1269
+ if (!this.regionResolutionPromise) {
1270
+ this.regionResolutionPromise = this.resolveRegion().finally(() => {
1271
+ this.regionResolutionAttempted = true;
1272
+ this.regionResolutionPromise = void 0;
1273
+ });
1274
+ }
1275
+ await this.regionResolutionPromise;
1276
+ }
1277
+ async resolveRegion() {
1278
+ const logger2 = getLogger();
1279
+ try {
1280
+ const response = await this.httpClient.get(
1281
+ `${this.baseUrl}/v1/regions/catalog`
1282
+ );
1283
+ if (response.status !== 200 || !Array.isArray(response.data?.regions)) {
1284
+ logger2.debug(
1285
+ `Region catalog unavailable, falling back to baseUrl: status=${response.status}`
1286
+ );
1287
+ return;
1288
+ }
1289
+ const selected = this.selectCatalogRegion(response.data.regions);
1290
+ if (!selected) {
1291
+ if (this.region) {
1292
+ logger2.warn(`Requested region not found in catalog: ${this.region}`);
1293
+ } else {
1294
+ logger2.debug("No default catalog region, falling back to baseUrl");
1295
+ }
1296
+ return;
1297
+ }
1298
+ const endpointBaseUrl = await this.probeRegionHost(selected);
1299
+ if (!endpointBaseUrl) {
1300
+ logger2.debug("Region probe failed, falling back to baseUrl");
1301
+ return;
1302
+ }
1303
+ this.selectedRegion = selected.region_id;
1304
+ this.selectedHost = selected.host;
1305
+ this.endpointBaseUrl = endpointBaseUrl;
1306
+ } catch (error) {
1307
+ logger2.debug("Region resolution failed, falling back to baseUrl:", error);
1308
+ }
1309
+ }
1310
+ selectCatalogRegion(regions) {
1311
+ if (this.region) {
1312
+ return regions.find((region) => region.region_id === this.region);
1313
+ }
1314
+ const defaultRegions = regions.filter((region) => region.default === true);
1315
+ return defaultRegions.length === 1 ? defaultRegions[0] : void 0;
1316
+ }
1317
+ async probeRegionHost(region) {
1318
+ const logger2 = getLogger();
1319
+ const host = String(region.host || "").trim();
1320
+ if (!host) {
1321
+ return void 0;
1322
+ }
1323
+ if (!this.isAllowedCatalogHost(host)) {
1324
+ logger2.warn(`Ignoring invalid region catalog host: ${host}`);
1325
+ return void 0;
1326
+ }
1327
+ const endpointBaseUrl = `https://${host}`;
1328
+ const probePath = String(region.probe_path || "/v1/region/probe").replace(/^(?!\/)/, "/");
1329
+ try {
1330
+ const response = await this.httpClient.get(`${endpointBaseUrl}${probePath}`);
1331
+ return response.status === 200 ? endpointBaseUrl : void 0;
1332
+ } catch {
1333
+ return void 0;
1334
+ }
1335
+ }
1336
+ isAllowedCatalogHost(host) {
1337
+ let parsed;
1338
+ try {
1339
+ parsed = new URL(`https://${host}`);
1340
+ } catch {
1341
+ return false;
1342
+ }
1343
+ if (parsed.username || parsed.password || parsed.port || parsed.pathname !== "/" || parsed.search || parsed.hash) {
1344
+ return false;
1345
+ }
1346
+ const hostname = parsed.hostname.toLowerCase();
1347
+ const allowedDomains = ["lexmount.cn", "lexmount.com", "lexmount.net"];
1348
+ return allowedDomains.some((domain) => hostname === domain || hostname.endsWith(`.${domain}`));
1349
+ }
1350
+ rewriteUrl(url) {
1351
+ if (!this.endpointBaseUrl || !url.startsWith(this.baseUrl)) {
1352
+ return url;
1353
+ }
1354
+ return `${this.endpointBaseUrl}${url.slice(this.baseUrl.length)}`;
1355
+ }
1217
1356
  };
1218
1357
 
1219
1358
  // src/index.ts
1220
- var VERSION = "0.2.5";
1359
+ var VERSION = "0.5.1";
1221
1360
  export {
1222
1361
  APIError,
1223
1362
  AuthenticationError,