lexmount 0.2.6 → 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 +79 -2
- package/dist/index.d.ts +79 -2
- package/dist/index.js +202 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +202 -7
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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
|
|
|
@@ -665,6 +663,18 @@ var SessionDownloadsDeleteResponse = class {
|
|
|
665
663
|
this.deletedCount = shape.deletedCount;
|
|
666
664
|
}
|
|
667
665
|
};
|
|
666
|
+
var SessionTargetInfo = class {
|
|
667
|
+
constructor(shape) {
|
|
668
|
+
this.id = shape.id;
|
|
669
|
+
this.title = shape.title;
|
|
670
|
+
this.type = shape.type;
|
|
671
|
+
this.url = shape.url;
|
|
672
|
+
this.description = shape.description;
|
|
673
|
+
this.inspectUrl = shape.inspectUrl;
|
|
674
|
+
this.webSocketDebuggerUrl = shape.webSocketDebuggerUrl;
|
|
675
|
+
this.webSocketDebuggerUrlTransformed = shape.webSocketDebuggerUrlTransformed;
|
|
676
|
+
}
|
|
677
|
+
};
|
|
668
678
|
var SessionDownloadsResource = class {
|
|
669
679
|
constructor(client) {
|
|
670
680
|
this.client = client;
|
|
@@ -895,6 +905,19 @@ var SessionsResource = class {
|
|
|
895
905
|
}
|
|
896
906
|
getLogger().info(`Session deleted successfully: ${options.sessionId}`);
|
|
897
907
|
}
|
|
908
|
+
/**
|
|
909
|
+
* List target/page metadata for a session via `/json`.
|
|
910
|
+
*/
|
|
911
|
+
async listTargets(sessionId) {
|
|
912
|
+
const response = await this.client._get(`${this.client.baseUrl}/json`, {
|
|
913
|
+
session_id: sessionId
|
|
914
|
+
});
|
|
915
|
+
if (response.status >= 400) {
|
|
916
|
+
this.handleListTargetsError(response, sessionId);
|
|
917
|
+
}
|
|
918
|
+
const result = Array.isArray(response.data) ? response.data : [];
|
|
919
|
+
return result.map((item) => this.mapSessionTarget(asRecord3(item)));
|
|
920
|
+
}
|
|
898
921
|
/**
|
|
899
922
|
* Fetch the debugger WebSocket URL for a session.
|
|
900
923
|
*
|
|
@@ -990,6 +1013,24 @@ var SessionsResource = class {
|
|
|
990
1013
|
response: response.data
|
|
991
1014
|
});
|
|
992
1015
|
}
|
|
1016
|
+
handleListTargetsError(response, sessionId) {
|
|
1017
|
+
const errorData = asRecord3(response.data);
|
|
1018
|
+
const errorMessage = getString3(errorData.error) ?? getString3(errorData.message) ?? "Unknown error";
|
|
1019
|
+
if (response.status === 401) {
|
|
1020
|
+
throw new AuthenticationError(
|
|
1021
|
+
`Authentication failed: ${errorMessage}. Please check your API key and project ID.`
|
|
1022
|
+
);
|
|
1023
|
+
}
|
|
1024
|
+
if (response.status === 404) {
|
|
1025
|
+
throw new SessionNotFoundError(
|
|
1026
|
+
`Session not found: ${errorMessage}. Session ID '${sessionId}' may not exist in this project.`
|
|
1027
|
+
);
|
|
1028
|
+
}
|
|
1029
|
+
throw new APIError(`Failed to list session targets: ${errorMessage}`, {
|
|
1030
|
+
statusCode: response.status,
|
|
1031
|
+
response: response.data
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
993
1034
|
handleDeleteError(response, sessionId) {
|
|
994
1035
|
const errorData = asRecord3(response.data);
|
|
995
1036
|
const errorMessage = getString3(errorData.error) ?? getString3(errorData.message) ?? "Unknown error";
|
|
@@ -1023,6 +1064,18 @@ var SessionsResource = class {
|
|
|
1023
1064
|
client: this.client
|
|
1024
1065
|
});
|
|
1025
1066
|
}
|
|
1067
|
+
mapSessionTarget(raw) {
|
|
1068
|
+
return new SessionTargetInfo({
|
|
1069
|
+
id: getString3(raw.id) ?? "",
|
|
1070
|
+
title: getString3(raw.title) ?? "",
|
|
1071
|
+
type: getString3(raw.type) ?? "",
|
|
1072
|
+
url: getString3(raw.url) ?? "",
|
|
1073
|
+
description: getString3(raw.description) ?? "",
|
|
1074
|
+
inspectUrl: getString3(raw.inspectUrl) ?? getString3(raw.inspect_url) ?? null,
|
|
1075
|
+
webSocketDebuggerUrl: getString3(raw.webSocketDebuggerUrl) ?? getString3(raw.web_socket_debugger_url) ?? null,
|
|
1076
|
+
webSocketDebuggerUrlTransformed: getString3(raw.webSocketDebuggerUrlTransformed) ?? getString3(raw.web_socket_debugger_url_transformed) ?? null
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1026
1079
|
async _getCreatedSession(sessionId, projectId) {
|
|
1027
1080
|
try {
|
|
1028
1081
|
return await this.get(sessionId, projectId);
|
|
@@ -1040,6 +1093,7 @@ var SessionsResource = class {
|
|
|
1040
1093
|
dotenv.config();
|
|
1041
1094
|
var Lexmount = class {
|
|
1042
1095
|
constructor(config2 = {}) {
|
|
1096
|
+
this.regionResolutionAttempted = false;
|
|
1043
1097
|
this.apiKey = config2.apiKey ?? process.env.LEXMOUNT_API_KEY ?? "";
|
|
1044
1098
|
if (!this.apiKey) {
|
|
1045
1099
|
throw new ValidationError(
|
|
@@ -1053,6 +1107,7 @@ var Lexmount = class {
|
|
|
1053
1107
|
);
|
|
1054
1108
|
}
|
|
1055
1109
|
this.baseUrl = (config2.baseUrl ?? process.env.LEXMOUNT_BASE_URL ?? "https://api.lexmount.cn").replace(/\/$/, "");
|
|
1110
|
+
this.region = config2.region;
|
|
1056
1111
|
if (config2.logLevel) {
|
|
1057
1112
|
setLogLevel(config2.logLevel);
|
|
1058
1113
|
}
|
|
@@ -1070,6 +1125,7 @@ var Lexmount = class {
|
|
|
1070
1125
|
* @internal
|
|
1071
1126
|
*/
|
|
1072
1127
|
async _post(url, data, config2 = {}) {
|
|
1128
|
+
await this.ensureRegionResolved();
|
|
1073
1129
|
return this.request("POST", url, { ...config2, data });
|
|
1074
1130
|
}
|
|
1075
1131
|
/**
|
|
@@ -1078,6 +1134,7 @@ var Lexmount = class {
|
|
|
1078
1134
|
* @internal
|
|
1079
1135
|
*/
|
|
1080
1136
|
async _get(url, params, config2 = {}) {
|
|
1137
|
+
await this.ensureRegionResolved();
|
|
1081
1138
|
return this.request("GET", url, { ...config2, params });
|
|
1082
1139
|
}
|
|
1083
1140
|
/**
|
|
@@ -1086,8 +1143,51 @@ var Lexmount = class {
|
|
|
1086
1143
|
* @internal
|
|
1087
1144
|
*/
|
|
1088
1145
|
async _delete(url, data, config2 = {}) {
|
|
1146
|
+
await this.ensureRegionResolved();
|
|
1089
1147
|
return this.request("DELETE", url, { ...config2, data });
|
|
1090
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
|
+
}
|
|
1091
1191
|
/**
|
|
1092
1192
|
* Close the client.
|
|
1093
1193
|
*
|
|
@@ -1105,7 +1205,8 @@ var Lexmount = class {
|
|
|
1105
1205
|
async request(method, url, config2) {
|
|
1106
1206
|
const logger2 = getLogger();
|
|
1107
1207
|
const startTime = Date.now();
|
|
1108
|
-
|
|
1208
|
+
const requestUrl = this.rewriteUrl(url);
|
|
1209
|
+
logger2.debug(`${method} request to ${requestUrl}`);
|
|
1109
1210
|
try {
|
|
1110
1211
|
const headers = {
|
|
1111
1212
|
api_key: this.apiKey,
|
|
@@ -1115,12 +1216,14 @@ var Lexmount = class {
|
|
|
1115
1216
|
};
|
|
1116
1217
|
const response = await this.httpClient.request({
|
|
1117
1218
|
method,
|
|
1118
|
-
url,
|
|
1219
|
+
url: requestUrl,
|
|
1119
1220
|
...config2,
|
|
1120
1221
|
headers
|
|
1121
1222
|
});
|
|
1122
1223
|
const elapsed = Date.now() - startTime;
|
|
1123
|
-
logger2.debug(
|
|
1224
|
+
logger2.debug(
|
|
1225
|
+
`${method} response: status=${response.status}, duration=${elapsed.toFixed(2)}ms`
|
|
1226
|
+
);
|
|
1124
1227
|
return response;
|
|
1125
1228
|
} catch (error) {
|
|
1126
1229
|
const elapsed = Date.now() - startTime;
|
|
@@ -1159,10 +1262,101 @@ var Lexmount = class {
|
|
|
1159
1262
|
"Content-Type": "application/json"
|
|
1160
1263
|
};
|
|
1161
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
|
+
}
|
|
1162
1356
|
};
|
|
1163
1357
|
|
|
1164
1358
|
// src/index.ts
|
|
1165
|
-
var VERSION = "0.
|
|
1359
|
+
var VERSION = "0.5.1";
|
|
1166
1360
|
export {
|
|
1167
1361
|
APIError,
|
|
1168
1362
|
AuthenticationError,
|
|
@@ -1185,6 +1379,7 @@ export {
|
|
|
1185
1379
|
SessionInfo,
|
|
1186
1380
|
SessionListResponse,
|
|
1187
1381
|
SessionNotFoundError,
|
|
1382
|
+
SessionTargetInfo,
|
|
1188
1383
|
SessionsResource,
|
|
1189
1384
|
TimeoutError,
|
|
1190
1385
|
VERSION,
|