lexmount 0.2.7 → 0.5.2
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 +69 -2
- package/dist/index.d.ts +69 -2
- package/dist/index.js +210 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +211 -8
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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
|
*/
|
|
@@ -141,6 +171,28 @@ interface SessionCreateOptions {
|
|
|
141
171
|
* Optional upstream proxy configuration.
|
|
142
172
|
*/
|
|
143
173
|
proxy?: SessionProxyConfig;
|
|
174
|
+
/**
|
|
175
|
+
* Forward weak_lock to the API when creating sessions with read-write contexts.
|
|
176
|
+
*/
|
|
177
|
+
weakLock?: boolean;
|
|
178
|
+
/**
|
|
179
|
+
* Use the async create API (`POST /instance/v2`) and poll until active.
|
|
180
|
+
*
|
|
181
|
+
* Defaults to true. Set to false to use the legacy synchronous `POST /instance`.
|
|
182
|
+
*/
|
|
183
|
+
asyncCreate?: boolean;
|
|
184
|
+
/**
|
|
185
|
+
* Delay between async create status polls in milliseconds.
|
|
186
|
+
*
|
|
187
|
+
* Defaults to 1000.
|
|
188
|
+
*/
|
|
189
|
+
pollIntervalMs?: number;
|
|
190
|
+
/**
|
|
191
|
+
* Maximum time to wait for async create activation in milliseconds.
|
|
192
|
+
*
|
|
193
|
+
* Defaults to 600000.
|
|
194
|
+
*/
|
|
195
|
+
pollTimeoutMs?: number;
|
|
144
196
|
}
|
|
145
197
|
/**
|
|
146
198
|
* Session list options.
|
|
@@ -536,6 +588,7 @@ declare class SessionsResource {
|
|
|
536
588
|
* Create a new browser session.
|
|
537
589
|
*/
|
|
538
590
|
create(options?: SessionCreateOptions): Promise<SessionCreateResponse>;
|
|
591
|
+
private createAsync;
|
|
539
592
|
/**
|
|
540
593
|
* Fetch one session by id.
|
|
541
594
|
*
|
|
@@ -592,6 +645,12 @@ declare class Lexmount {
|
|
|
592
645
|
* API base URL.
|
|
593
646
|
*/
|
|
594
647
|
readonly baseUrl: string;
|
|
648
|
+
readonly region?: string;
|
|
649
|
+
selectedRegion?: string;
|
|
650
|
+
private endpointBaseUrl?;
|
|
651
|
+
private selectedHost?;
|
|
652
|
+
private regionResolutionAttempted;
|
|
653
|
+
private regionResolutionPromise?;
|
|
595
654
|
/**
|
|
596
655
|
* Session operations.
|
|
597
656
|
*/
|
|
@@ -624,6 +683,8 @@ declare class Lexmount {
|
|
|
624
683
|
* @internal
|
|
625
684
|
*/
|
|
626
685
|
_delete<T = unknown>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>;
|
|
686
|
+
regionInfo(): Promise<RegionInfo>;
|
|
687
|
+
catalogInfo(): Promise<CatalogInfo>;
|
|
627
688
|
/**
|
|
628
689
|
* Close the client.
|
|
629
690
|
*
|
|
@@ -637,6 +698,12 @@ declare class Lexmount {
|
|
|
637
698
|
private request;
|
|
638
699
|
private normalizeRequestError;
|
|
639
700
|
private getDefaultHeaders;
|
|
701
|
+
private ensureRegionResolved;
|
|
702
|
+
private resolveRegion;
|
|
703
|
+
private selectCatalogRegion;
|
|
704
|
+
private probeRegionHost;
|
|
705
|
+
private isAllowedCatalogHost;
|
|
706
|
+
private rewriteUrl;
|
|
640
707
|
}
|
|
641
708
|
|
|
642
709
|
/**
|
|
@@ -704,6 +771,6 @@ declare class TimeoutError extends LexmountError {
|
|
|
704
771
|
/**
|
|
705
772
|
* Lexmount Node.js SDK.
|
|
706
773
|
*/
|
|
707
|
-
declare const VERSION = "0.2
|
|
774
|
+
declare const VERSION = "0.5.2";
|
|
708
775
|
|
|
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 };
|
|
776
|
+
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
|
*/
|
|
@@ -141,6 +171,28 @@ interface SessionCreateOptions {
|
|
|
141
171
|
* Optional upstream proxy configuration.
|
|
142
172
|
*/
|
|
143
173
|
proxy?: SessionProxyConfig;
|
|
174
|
+
/**
|
|
175
|
+
* Forward weak_lock to the API when creating sessions with read-write contexts.
|
|
176
|
+
*/
|
|
177
|
+
weakLock?: boolean;
|
|
178
|
+
/**
|
|
179
|
+
* Use the async create API (`POST /instance/v2`) and poll until active.
|
|
180
|
+
*
|
|
181
|
+
* Defaults to true. Set to false to use the legacy synchronous `POST /instance`.
|
|
182
|
+
*/
|
|
183
|
+
asyncCreate?: boolean;
|
|
184
|
+
/**
|
|
185
|
+
* Delay between async create status polls in milliseconds.
|
|
186
|
+
*
|
|
187
|
+
* Defaults to 1000.
|
|
188
|
+
*/
|
|
189
|
+
pollIntervalMs?: number;
|
|
190
|
+
/**
|
|
191
|
+
* Maximum time to wait for async create activation in milliseconds.
|
|
192
|
+
*
|
|
193
|
+
* Defaults to 600000.
|
|
194
|
+
*/
|
|
195
|
+
pollTimeoutMs?: number;
|
|
144
196
|
}
|
|
145
197
|
/**
|
|
146
198
|
* Session list options.
|
|
@@ -536,6 +588,7 @@ declare class SessionsResource {
|
|
|
536
588
|
* Create a new browser session.
|
|
537
589
|
*/
|
|
538
590
|
create(options?: SessionCreateOptions): Promise<SessionCreateResponse>;
|
|
591
|
+
private createAsync;
|
|
539
592
|
/**
|
|
540
593
|
* Fetch one session by id.
|
|
541
594
|
*
|
|
@@ -592,6 +645,12 @@ declare class Lexmount {
|
|
|
592
645
|
* API base URL.
|
|
593
646
|
*/
|
|
594
647
|
readonly baseUrl: string;
|
|
648
|
+
readonly region?: string;
|
|
649
|
+
selectedRegion?: string;
|
|
650
|
+
private endpointBaseUrl?;
|
|
651
|
+
private selectedHost?;
|
|
652
|
+
private regionResolutionAttempted;
|
|
653
|
+
private regionResolutionPromise?;
|
|
595
654
|
/**
|
|
596
655
|
* Session operations.
|
|
597
656
|
*/
|
|
@@ -624,6 +683,8 @@ declare class Lexmount {
|
|
|
624
683
|
* @internal
|
|
625
684
|
*/
|
|
626
685
|
_delete<T = unknown>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>;
|
|
686
|
+
regionInfo(): Promise<RegionInfo>;
|
|
687
|
+
catalogInfo(): Promise<CatalogInfo>;
|
|
627
688
|
/**
|
|
628
689
|
* Close the client.
|
|
629
690
|
*
|
|
@@ -637,6 +698,12 @@ declare class Lexmount {
|
|
|
637
698
|
private request;
|
|
638
699
|
private normalizeRequestError;
|
|
639
700
|
private getDefaultHeaders;
|
|
701
|
+
private ensureRegionResolved;
|
|
702
|
+
private resolveRegion;
|
|
703
|
+
private selectCatalogRegion;
|
|
704
|
+
private probeRegionHost;
|
|
705
|
+
private isAllowedCatalogHost;
|
|
706
|
+
private rewriteUrl;
|
|
640
707
|
}
|
|
641
708
|
|
|
642
709
|
/**
|
|
@@ -704,6 +771,6 @@ declare class TimeoutError extends LexmountError {
|
|
|
704
771
|
/**
|
|
705
772
|
* Lexmount Node.js SDK.
|
|
706
773
|
*/
|
|
707
|
-
declare const VERSION = "0.2
|
|
774
|
+
declare const VERSION = "0.5.2";
|
|
708
775
|
|
|
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 };
|
|
776
|
+
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
|
@@ -620,6 +620,11 @@ function normalizeContextMode(mode) {
|
|
|
620
620
|
}
|
|
621
621
|
return "read_only";
|
|
622
622
|
}
|
|
623
|
+
function delay(ms) {
|
|
624
|
+
return new Promise((resolve) => {
|
|
625
|
+
setTimeout(resolve, ms);
|
|
626
|
+
});
|
|
627
|
+
}
|
|
623
628
|
var PaginationInfo = class {
|
|
624
629
|
constructor(shape) {
|
|
625
630
|
this.currentPage = shape.currentPage;
|
|
@@ -851,7 +856,6 @@ var SessionsResource = class {
|
|
|
851
856
|
* Create a new browser session.
|
|
852
857
|
*/
|
|
853
858
|
async create(options = {}) {
|
|
854
|
-
const url = `${this.client.baseUrl}/instance`;
|
|
855
859
|
const payload = {
|
|
856
860
|
api_key: this.client.apiKey,
|
|
857
861
|
project_id: options.projectId ?? this.client.projectId,
|
|
@@ -875,6 +879,13 @@ var SessionsResource = class {
|
|
|
875
879
|
if (options.proxy) {
|
|
876
880
|
payload.proxy = this.normalizeProxy(options.proxy);
|
|
877
881
|
}
|
|
882
|
+
if (options.weakLock) {
|
|
883
|
+
payload.weak_lock = true;
|
|
884
|
+
}
|
|
885
|
+
if (options.asyncCreate !== false) {
|
|
886
|
+
return this.createAsync(payload, options);
|
|
887
|
+
}
|
|
888
|
+
const url = `${this.client.baseUrl}/instance`;
|
|
878
889
|
const response = await this.client._post(url, payload);
|
|
879
890
|
if (response.status >= 400) {
|
|
880
891
|
this.handleCreateError(response);
|
|
@@ -905,6 +916,59 @@ var SessionsResource = class {
|
|
|
905
916
|
client: this.client
|
|
906
917
|
});
|
|
907
918
|
}
|
|
919
|
+
async createAsync(payload, options) {
|
|
920
|
+
const response = await this.client._post(`${this.client.baseUrl}/instance/v2`, payload);
|
|
921
|
+
if (response.status >= 400) {
|
|
922
|
+
this.handleCreateError(response);
|
|
923
|
+
}
|
|
924
|
+
const result = asRecord3(response.data);
|
|
925
|
+
const sessionId = getString3(result.session_id);
|
|
926
|
+
if (!sessionId) {
|
|
927
|
+
throw new APIError("Failed to create session: session_id missing from response", {
|
|
928
|
+
statusCode: response.status,
|
|
929
|
+
response: response.data
|
|
930
|
+
});
|
|
931
|
+
}
|
|
932
|
+
const projectId = options.projectId ?? this.client.projectId;
|
|
933
|
+
const pollIntervalMs = options.pollIntervalMs ?? 1e3;
|
|
934
|
+
const pollTimeoutMs = options.pollTimeoutMs ?? 6e5;
|
|
935
|
+
const deadline = Date.now() + pollTimeoutMs;
|
|
936
|
+
while (Date.now() < deadline) {
|
|
937
|
+
const info = await this.get(sessionId, projectId);
|
|
938
|
+
if (info.status === "active") {
|
|
939
|
+
const wsUrl = await this._getWebSocketDebuggerUrl(sessionId);
|
|
940
|
+
getLogger().info(`Session created successfully (async): id=${sessionId}`);
|
|
941
|
+
return new SessionInfo({
|
|
942
|
+
id: sessionId,
|
|
943
|
+
status: "active",
|
|
944
|
+
apiKey: this.client.apiKey,
|
|
945
|
+
projectId,
|
|
946
|
+
browserType: info.browserType || (options.browserMode ?? "normal"),
|
|
947
|
+
createdAt: info.createdAt,
|
|
948
|
+
inspectUrl: info.inspectUrl,
|
|
949
|
+
containerId: info.containerId,
|
|
950
|
+
ws: wsUrl ?? info.ws,
|
|
951
|
+
client: this.client
|
|
952
|
+
});
|
|
953
|
+
}
|
|
954
|
+
if (info.status === "create_failed") {
|
|
955
|
+
throw new APIError("Session creation failed", {
|
|
956
|
+
statusCode: 500,
|
|
957
|
+
response: { session_id: sessionId, status: info.status }
|
|
958
|
+
});
|
|
959
|
+
}
|
|
960
|
+
if (info.status === "closed") {
|
|
961
|
+
throw new APIError("Session closed before activation", {
|
|
962
|
+
statusCode: 500,
|
|
963
|
+
response: { session_id: sessionId, status: info.status }
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
await delay(pollIntervalMs);
|
|
967
|
+
}
|
|
968
|
+
throw new TimeoutError(
|
|
969
|
+
`Timed out waiting for session ${sessionId} to become active after ${pollTimeoutMs}ms`
|
|
970
|
+
);
|
|
971
|
+
}
|
|
908
972
|
/**
|
|
909
973
|
* Fetch one session by id.
|
|
910
974
|
*
|
|
@@ -1159,6 +1223,7 @@ var SessionsResource = class {
|
|
|
1159
1223
|
dotenv.config();
|
|
1160
1224
|
var Lexmount = class {
|
|
1161
1225
|
constructor(config2 = {}) {
|
|
1226
|
+
this.regionResolutionAttempted = false;
|
|
1162
1227
|
this.apiKey = config2.apiKey ?? process.env.LEXMOUNT_API_KEY ?? "";
|
|
1163
1228
|
if (!this.apiKey) {
|
|
1164
1229
|
throw new ValidationError(
|
|
@@ -1172,6 +1237,7 @@ var Lexmount = class {
|
|
|
1172
1237
|
);
|
|
1173
1238
|
}
|
|
1174
1239
|
this.baseUrl = (config2.baseUrl ?? process.env.LEXMOUNT_BASE_URL ?? "https://api.lexmount.cn").replace(/\/$/, "");
|
|
1240
|
+
this.region = config2.region;
|
|
1175
1241
|
if (config2.logLevel) {
|
|
1176
1242
|
setLogLevel(config2.logLevel);
|
|
1177
1243
|
}
|
|
@@ -1189,6 +1255,7 @@ var Lexmount = class {
|
|
|
1189
1255
|
* @internal
|
|
1190
1256
|
*/
|
|
1191
1257
|
async _post(url, data, config2 = {}) {
|
|
1258
|
+
await this.ensureRegionResolved();
|
|
1192
1259
|
return this.request("POST", url, { ...config2, data });
|
|
1193
1260
|
}
|
|
1194
1261
|
/**
|
|
@@ -1197,6 +1264,7 @@ var Lexmount = class {
|
|
|
1197
1264
|
* @internal
|
|
1198
1265
|
*/
|
|
1199
1266
|
async _get(url, params, config2 = {}) {
|
|
1267
|
+
await this.ensureRegionResolved();
|
|
1200
1268
|
return this.request("GET", url, { ...config2, params });
|
|
1201
1269
|
}
|
|
1202
1270
|
/**
|
|
@@ -1205,8 +1273,51 @@ var Lexmount = class {
|
|
|
1205
1273
|
* @internal
|
|
1206
1274
|
*/
|
|
1207
1275
|
async _delete(url, data, config2 = {}) {
|
|
1276
|
+
await this.ensureRegionResolved();
|
|
1208
1277
|
return this.request("DELETE", url, { ...config2, data });
|
|
1209
1278
|
}
|
|
1279
|
+
async regionInfo() {
|
|
1280
|
+
await this.ensureRegionResolved();
|
|
1281
|
+
return {
|
|
1282
|
+
requestedRegion: this.region,
|
|
1283
|
+
selectedRegion: this.selectedRegion,
|
|
1284
|
+
baseUrl: this.baseUrl,
|
|
1285
|
+
endpointBaseUrl: this.endpointBaseUrl,
|
|
1286
|
+
host: this.selectedHost,
|
|
1287
|
+
resolved: this.regionResolutionAttempted,
|
|
1288
|
+
usingRegion: this.selectedRegion !== void 0
|
|
1289
|
+
};
|
|
1290
|
+
}
|
|
1291
|
+
async catalogInfo() {
|
|
1292
|
+
try {
|
|
1293
|
+
const response = await this.httpClient.get(
|
|
1294
|
+
`${this.baseUrl}/v1/regions/catalog`
|
|
1295
|
+
);
|
|
1296
|
+
if (response.status !== 200) {
|
|
1297
|
+
return {
|
|
1298
|
+
baseUrl: this.baseUrl,
|
|
1299
|
+
available: false,
|
|
1300
|
+
statusCode: response.status,
|
|
1301
|
+
regions: []
|
|
1302
|
+
};
|
|
1303
|
+
}
|
|
1304
|
+
const regions = Array.isArray(response.data?.regions) ? response.data.regions : [];
|
|
1305
|
+
return {
|
|
1306
|
+
baseUrl: this.baseUrl,
|
|
1307
|
+
available: Array.isArray(response.data?.regions),
|
|
1308
|
+
statusCode: response.status,
|
|
1309
|
+
regions,
|
|
1310
|
+
error: Array.isArray(response.data?.regions) ? void 0 : "invalid catalog regions"
|
|
1311
|
+
};
|
|
1312
|
+
} catch (error) {
|
|
1313
|
+
return {
|
|
1314
|
+
baseUrl: this.baseUrl,
|
|
1315
|
+
available: false,
|
|
1316
|
+
regions: [],
|
|
1317
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1318
|
+
};
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1210
1321
|
/**
|
|
1211
1322
|
* Close the client.
|
|
1212
1323
|
*
|
|
@@ -1224,7 +1335,8 @@ var Lexmount = class {
|
|
|
1224
1335
|
async request(method, url, config2) {
|
|
1225
1336
|
const logger2 = getLogger();
|
|
1226
1337
|
const startTime = Date.now();
|
|
1227
|
-
|
|
1338
|
+
const requestUrl = this.rewriteUrl(url);
|
|
1339
|
+
logger2.debug(`${method} request to ${requestUrl}`);
|
|
1228
1340
|
try {
|
|
1229
1341
|
const headers = {
|
|
1230
1342
|
api_key: this.apiKey,
|
|
@@ -1234,12 +1346,14 @@ var Lexmount = class {
|
|
|
1234
1346
|
};
|
|
1235
1347
|
const response = await this.httpClient.request({
|
|
1236
1348
|
method,
|
|
1237
|
-
url,
|
|
1349
|
+
url: requestUrl,
|
|
1238
1350
|
...config2,
|
|
1239
1351
|
headers
|
|
1240
1352
|
});
|
|
1241
1353
|
const elapsed = Date.now() - startTime;
|
|
1242
|
-
logger2.debug(
|
|
1354
|
+
logger2.debug(
|
|
1355
|
+
`${method} response: status=${response.status}, duration=${elapsed.toFixed(2)}ms`
|
|
1356
|
+
);
|
|
1243
1357
|
return response;
|
|
1244
1358
|
} catch (error) {
|
|
1245
1359
|
const elapsed = Date.now() - startTime;
|
|
@@ -1278,10 +1392,101 @@ var Lexmount = class {
|
|
|
1278
1392
|
"Content-Type": "application/json"
|
|
1279
1393
|
};
|
|
1280
1394
|
}
|
|
1395
|
+
async ensureRegionResolved() {
|
|
1396
|
+
if (this.regionResolutionAttempted) {
|
|
1397
|
+
return;
|
|
1398
|
+
}
|
|
1399
|
+
if (!this.regionResolutionPromise) {
|
|
1400
|
+
this.regionResolutionPromise = this.resolveRegion().finally(() => {
|
|
1401
|
+
this.regionResolutionAttempted = true;
|
|
1402
|
+
this.regionResolutionPromise = void 0;
|
|
1403
|
+
});
|
|
1404
|
+
}
|
|
1405
|
+
await this.regionResolutionPromise;
|
|
1406
|
+
}
|
|
1407
|
+
async resolveRegion() {
|
|
1408
|
+
const logger2 = getLogger();
|
|
1409
|
+
try {
|
|
1410
|
+
const response = await this.httpClient.get(
|
|
1411
|
+
`${this.baseUrl}/v1/regions/catalog`
|
|
1412
|
+
);
|
|
1413
|
+
if (response.status !== 200 || !Array.isArray(response.data?.regions)) {
|
|
1414
|
+
logger2.debug(
|
|
1415
|
+
`Region catalog unavailable, falling back to baseUrl: status=${response.status}`
|
|
1416
|
+
);
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1419
|
+
const selected = this.selectCatalogRegion(response.data.regions);
|
|
1420
|
+
if (!selected) {
|
|
1421
|
+
if (this.region) {
|
|
1422
|
+
logger2.warn(`Requested region not found in catalog: ${this.region}`);
|
|
1423
|
+
} else {
|
|
1424
|
+
logger2.debug("No default catalog region, falling back to baseUrl");
|
|
1425
|
+
}
|
|
1426
|
+
return;
|
|
1427
|
+
}
|
|
1428
|
+
const endpointBaseUrl = await this.probeRegionHost(selected);
|
|
1429
|
+
if (!endpointBaseUrl) {
|
|
1430
|
+
logger2.debug("Region probe failed, falling back to baseUrl");
|
|
1431
|
+
return;
|
|
1432
|
+
}
|
|
1433
|
+
this.selectedRegion = selected.region_id;
|
|
1434
|
+
this.selectedHost = selected.host;
|
|
1435
|
+
this.endpointBaseUrl = endpointBaseUrl;
|
|
1436
|
+
} catch (error) {
|
|
1437
|
+
logger2.debug("Region resolution failed, falling back to baseUrl:", error);
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
selectCatalogRegion(regions) {
|
|
1441
|
+
if (this.region) {
|
|
1442
|
+
return regions.find((region) => region.region_id === this.region);
|
|
1443
|
+
}
|
|
1444
|
+
const defaultRegions = regions.filter((region) => region.default === true);
|
|
1445
|
+
return defaultRegions.length === 1 ? defaultRegions[0] : void 0;
|
|
1446
|
+
}
|
|
1447
|
+
async probeRegionHost(region) {
|
|
1448
|
+
const logger2 = getLogger();
|
|
1449
|
+
const host = String(region.host || "").trim();
|
|
1450
|
+
if (!host) {
|
|
1451
|
+
return void 0;
|
|
1452
|
+
}
|
|
1453
|
+
if (!this.isAllowedCatalogHost(host)) {
|
|
1454
|
+
logger2.warn(`Ignoring invalid region catalog host: ${host}`);
|
|
1455
|
+
return void 0;
|
|
1456
|
+
}
|
|
1457
|
+
const endpointBaseUrl = `https://${host}`;
|
|
1458
|
+
const probePath = String(region.probe_path || "/v1/region/probe").replace(/^(?!\/)/, "/");
|
|
1459
|
+
try {
|
|
1460
|
+
const response = await this.httpClient.get(`${endpointBaseUrl}${probePath}`);
|
|
1461
|
+
return response.status === 200 ? endpointBaseUrl : void 0;
|
|
1462
|
+
} catch {
|
|
1463
|
+
return void 0;
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
isAllowedCatalogHost(host) {
|
|
1467
|
+
let parsed;
|
|
1468
|
+
try {
|
|
1469
|
+
parsed = new URL(`https://${host}`);
|
|
1470
|
+
} catch {
|
|
1471
|
+
return false;
|
|
1472
|
+
}
|
|
1473
|
+
if (parsed.username || parsed.password || parsed.port || parsed.pathname !== "/" || parsed.search || parsed.hash) {
|
|
1474
|
+
return false;
|
|
1475
|
+
}
|
|
1476
|
+
const hostname = parsed.hostname.toLowerCase();
|
|
1477
|
+
const allowedDomains = ["lexmount.cn", "lexmount.com", "lexmount.net"];
|
|
1478
|
+
return allowedDomains.some((domain) => hostname === domain || hostname.endsWith(`.${domain}`));
|
|
1479
|
+
}
|
|
1480
|
+
rewriteUrl(url) {
|
|
1481
|
+
if (!this.endpointBaseUrl || !url.startsWith(this.baseUrl)) {
|
|
1482
|
+
return url;
|
|
1483
|
+
}
|
|
1484
|
+
return `${this.endpointBaseUrl}${url.slice(this.baseUrl.length)}`;
|
|
1485
|
+
}
|
|
1281
1486
|
};
|
|
1282
1487
|
|
|
1283
1488
|
// src/index.ts
|
|
1284
|
-
var VERSION = "0.2
|
|
1489
|
+
var VERSION = "0.5.2";
|
|
1285
1490
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1286
1491
|
0 && (module.exports = {
|
|
1287
1492
|
APIError,
|