lexmount 0.2.0 → 0.2.3

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.js CHANGED
@@ -37,11 +37,17 @@ __export(index_exports, {
37
37
  ContextLockedError: () => ContextLockedError,
38
38
  ContextNotFoundError: () => ContextNotFoundError,
39
39
  ContextsResource: () => ContextsResource,
40
+ ExtensionInfo: () => ExtensionInfo,
41
+ ExtensionsResource: () => ExtensionsResource,
40
42
  Lexmount: () => Lexmount,
41
43
  LexmountError: () => LexmountError,
42
44
  LexmountLogger: () => LexmountLogger,
43
45
  NetworkError: () => NetworkError,
44
46
  PaginationInfo: () => PaginationInfo,
47
+ SessionDownloadInfo: () => SessionDownloadInfo,
48
+ SessionDownloadsDeleteResponse: () => SessionDownloadsDeleteResponse,
49
+ SessionDownloadsListResponse: () => SessionDownloadsListResponse,
50
+ SessionDownloadsResource: () => SessionDownloadsResource,
45
51
  SessionInfo: () => SessionInfo,
46
52
  SessionListResponse: () => SessionListResponse,
47
53
  SessionNotFoundError: () => SessionNotFoundError,
@@ -459,13 +465,120 @@ var ContextsResource = class {
459
465
  }
460
466
  };
461
467
 
462
- // src/sessions.ts
468
+ // src/extensions.ts
469
+ var import_promises = require("fs/promises");
470
+ var import_node_path = __toESM(require("path"));
463
471
  function asRecord2(value) {
464
472
  return typeof value === "object" && value !== null ? value : {};
465
473
  }
466
474
  function getString2(value) {
467
475
  return typeof value === "string" ? value : void 0;
468
476
  }
477
+ var ExtensionInfo = class {
478
+ constructor(shape) {
479
+ this.id = shape.id;
480
+ this.name = shape.name;
481
+ this.projectId = shape.projectId;
482
+ this.createdAt = shape.createdAt;
483
+ this.updatedAt = shape.updatedAt;
484
+ }
485
+ };
486
+ var ExtensionsResource = class {
487
+ constructor(client) {
488
+ this.client = client;
489
+ }
490
+ async upload(filePath, options = {}) {
491
+ const fileBuffer = await (0, import_promises.readFile)(filePath);
492
+ const form = new FormData();
493
+ const usedProjectId = options.projectId ?? this.client.projectId;
494
+ form.append("project_id", usedProjectId);
495
+ if (options.name) {
496
+ form.append("name", options.name);
497
+ }
498
+ form.append(
499
+ "file",
500
+ new Blob([fileBuffer], { type: "application/octet-stream" }),
501
+ import_node_path.default.basename(filePath)
502
+ );
503
+ const response = await this.client._post(
504
+ `${this.client.baseUrl}/instance/v1/extension/upload`,
505
+ form,
506
+ {
507
+ headers: {
508
+ project_id: usedProjectId
509
+ }
510
+ }
511
+ );
512
+ if (response.status >= 400) {
513
+ this.handleError("upload extension", response);
514
+ }
515
+ getLogger().info(`Extension uploaded successfully: ${filePath}`);
516
+ return this.parseInfo(response.data);
517
+ }
518
+ async list(options = {}) {
519
+ const response = await this.client._post(`${this.client.baseUrl}/instance/v1/extension/list`, {
520
+ project_id: options.projectId ?? this.client.projectId,
521
+ limit: options.limit ?? 20,
522
+ offset: options.offset ?? 0
523
+ });
524
+ if (response.status >= 400) {
525
+ this.handleError("list extensions", response);
526
+ }
527
+ const result = asRecord2(response.data);
528
+ const items = Array.isArray(result.data) ? result.data : [];
529
+ return items.map((item) => this.parseInfo(item));
530
+ }
531
+ async get(extensionId, projectId) {
532
+ const response = await this.client._post(`${this.client.baseUrl}/instance/v1/extension/info`, {
533
+ project_id: projectId ?? this.client.projectId,
534
+ extension_id: extensionId
535
+ });
536
+ if (response.status >= 400) {
537
+ this.handleError("get extension", response);
538
+ }
539
+ return this.parseInfo(response.data);
540
+ }
541
+ async delete(extensionId) {
542
+ const response = await this.client._delete(
543
+ `${this.client.baseUrl}/instance/v1/extension/${extensionId}`
544
+ );
545
+ if (response.status >= 400) {
546
+ this.handleError("delete extension", response);
547
+ }
548
+ getLogger().info(`Extension deleted successfully: ${extensionId}`);
549
+ }
550
+ parseInfo(data) {
551
+ const item = asRecord2(data);
552
+ return new ExtensionInfo({
553
+ id: getString2(item.id) ?? "",
554
+ name: getString2(item.name) ?? "",
555
+ projectId: getString2(item.project_id) ?? "",
556
+ createdAt: getString2(item.created_at),
557
+ updatedAt: getString2(item.updated_at)
558
+ });
559
+ }
560
+ handleError(action, response) {
561
+ const errorData = asRecord2(response.data);
562
+ const errorMessage = getString2(errorData.message) ?? getString2(errorData.error) ?? "Unknown error";
563
+ if (response.status === 401) {
564
+ throw new AuthenticationError(
565
+ `Authentication failed: ${errorMessage}. Please check your API key and project ID.`
566
+ );
567
+ }
568
+ throw new APIError(`Failed to ${action}: ${errorMessage}`, {
569
+ statusCode: response.status,
570
+ response: response.data
571
+ });
572
+ }
573
+ };
574
+
575
+ // src/sessions.ts
576
+ function asRecord3(value) {
577
+ return typeof value === "object" && value !== null ? value : {};
578
+ }
579
+ function getString3(value) {
580
+ return typeof value === "string" ? value : void 0;
581
+ }
469
582
  function getNumber(value) {
470
583
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
471
584
  }
@@ -562,9 +675,134 @@ var SessionListResponse = class {
562
675
  return this.sessions[index];
563
676
  }
564
677
  };
678
+ var SessionDownloadInfo = class {
679
+ constructor(shape) {
680
+ this.id = shape.id;
681
+ this.filename = shape.filename;
682
+ this.contentType = shape.contentType;
683
+ this.size = shape.size;
684
+ this.sha256 = shape.sha256;
685
+ this.status = shape.status;
686
+ this.createdAt = shape.createdAt;
687
+ }
688
+ };
689
+ var SessionDownloadsListResponse = class {
690
+ constructor(downloads, summary) {
691
+ this.downloads = downloads;
692
+ this.summary = summary;
693
+ }
694
+ };
695
+ var SessionDownloadsDeleteResponse = class {
696
+ constructor(shape) {
697
+ this.status = shape.status;
698
+ this.deletedCount = shape.deletedCount;
699
+ }
700
+ };
701
+ var SessionDownloadsResource = class {
702
+ constructor(client) {
703
+ this.client = client;
704
+ }
705
+ async list(sessionId, projectId) {
706
+ const response = await this.client._post(
707
+ `${this.client.baseUrl}/instance/v1/sessions/${sessionId}/downloads/list`,
708
+ {
709
+ api_key: this.client.apiKey,
710
+ project_id: projectId ?? this.client.projectId
711
+ }
712
+ );
713
+ if (response.status >= 400) {
714
+ this.handleError("list session downloads", response, sessionId);
715
+ }
716
+ const result = asRecord3(response.data);
717
+ const items = Array.isArray(result.downloads) ? result.downloads : [];
718
+ const downloads = items.map((item) => {
719
+ const download = asRecord3(item);
720
+ return new SessionDownloadInfo({
721
+ id: getString3(download.id) ?? "",
722
+ filename: getString3(download.filename) ?? "",
723
+ contentType: getString3(download.content_type) ?? null,
724
+ size: getNumber(download.size) ?? 0,
725
+ sha256: getString3(download.sha256) ?? null,
726
+ status: getString3(download.status) ?? "available",
727
+ createdAt: getString3(download.created_at) ?? ""
728
+ });
729
+ });
730
+ const summary = asRecord3(result.summary);
731
+ return new SessionDownloadsListResponse(downloads, {
732
+ count: getNumber(summary.count) ?? downloads.length,
733
+ totalSize: getNumber(summary.total_size) ?? 0
734
+ });
735
+ }
736
+ async get(sessionId, downloadId, projectId) {
737
+ const response = await this.client._get(
738
+ `${this.client.baseUrl}/instance/v1/sessions/${sessionId}/downloads/${downloadId}`,
739
+ {
740
+ api_key: this.client.apiKey,
741
+ project_id: projectId ?? this.client.projectId
742
+ },
743
+ {
744
+ responseType: "arraybuffer"
745
+ }
746
+ );
747
+ if (response.status >= 400) {
748
+ this.handleError("fetch session download", response, sessionId);
749
+ }
750
+ return Buffer.from(response.data);
751
+ }
752
+ async archive(sessionId, projectId) {
753
+ const response = await this.client._get(
754
+ `${this.client.baseUrl}/instance/v1/sessions/${sessionId}/downloads/archive`,
755
+ {
756
+ api_key: this.client.apiKey,
757
+ project_id: projectId ?? this.client.projectId
758
+ },
759
+ {
760
+ responseType: "arraybuffer"
761
+ }
762
+ );
763
+ if (response.status >= 400) {
764
+ this.handleError("archive session downloads", response, sessionId);
765
+ }
766
+ return Buffer.from(response.data);
767
+ }
768
+ async delete(sessionId, projectId) {
769
+ const response = await this.client._delete(
770
+ `${this.client.baseUrl}/instance/v1/sessions/${sessionId}/downloads`,
771
+ {
772
+ api_key: this.client.apiKey,
773
+ project_id: projectId ?? this.client.projectId
774
+ }
775
+ );
776
+ if (response.status >= 400) {
777
+ this.handleError("delete session downloads", response, sessionId);
778
+ }
779
+ const result = asRecord3(response.data);
780
+ return new SessionDownloadsDeleteResponse({
781
+ status: getString3(result.status) ?? "",
782
+ deletedCount: getNumber(result.deleted_count) ?? 0
783
+ });
784
+ }
785
+ handleError(action, response, sessionId) {
786
+ const errorData = asRecord3(response.data);
787
+ const errorMessage = getString3(errorData.error) ?? getString3(errorData.message) ?? "Unknown error";
788
+ if (response.status === 401) {
789
+ throw new AuthenticationError(
790
+ `Authentication failed: ${errorMessage}. Please check your API key and project ID.`
791
+ );
792
+ }
793
+ if (response.status === 404) {
794
+ throw new SessionNotFoundError(`Session downloads not found for ${sessionId}: ${errorMessage}`);
795
+ }
796
+ throw new APIError(`Failed to ${action}: ${errorMessage}`, {
797
+ statusCode: response.status,
798
+ response: response.data
799
+ });
800
+ }
801
+ };
565
802
  var SessionsResource = class {
566
803
  constructor(client) {
567
804
  this.client = client;
805
+ this.downloads = new SessionDownloadsResource(client);
568
806
  }
569
807
  /**
570
808
  * Create a new browser session.
@@ -588,19 +826,25 @@ var SessionsResource = class {
588
826
  } else {
589
827
  getLogger().debug(`Creating session with browser_mode=${payload.browser_mode}`);
590
828
  }
829
+ if (options.extensionIds && options.extensionIds.length > 0) {
830
+ payload.extension_ids = options.extensionIds;
831
+ }
832
+ if (options.proxy) {
833
+ payload.proxy = this.normalizeProxy(options.proxy);
834
+ }
591
835
  const response = await this.client._post(url, payload);
592
836
  if (response.status >= 400) {
593
837
  this.handleCreateError(response);
594
838
  }
595
- const result = asRecord2(response.data);
596
- const sessionId = getString2(result.session_id);
839
+ const result = asRecord3(response.data);
840
+ const sessionId = getString3(result.session_id);
597
841
  if (!sessionId) {
598
842
  throw new APIError("Failed to create session: session_id missing from response", {
599
843
  statusCode: response.status,
600
844
  response: response.data
601
845
  });
602
846
  }
603
- const containerId = getString2(result.container_id) ?? null;
847
+ const containerId = getString3(result.container_id) ?? null;
604
848
  const wsUrl = await this._getWebSocketDebuggerUrl(sessionId);
605
849
  const projectId = options.projectId ?? this.client.projectId;
606
850
  getLogger().info(`Session created successfully: id=${sessionId}, container_id=${containerId}`);
@@ -633,23 +877,23 @@ var SessionsResource = class {
633
877
  if (response.status >= 400) {
634
878
  this.handleListError(response);
635
879
  }
636
- const result = asRecord2(response.data);
880
+ const result = asRecord3(response.data);
637
881
  const sessionsData = Array.isArray(result.sessions) ? result.sessions : [];
638
- const paginationData = asRecord2(result.pagination);
882
+ const paginationData = asRecord3(result.pagination);
639
883
  const sessions = sessionsData.map((item) => {
640
- const session = asRecord2(item);
641
- const sessionId = getString2(session.id) ?? "";
884
+ const session = asRecord3(item);
885
+ const sessionId = getString3(session.id) ?? "";
642
886
  return new SessionInfo({
643
887
  id: sessionId,
644
- status: getString2(session.status) ?? "active",
645
- apiKey: getString2(session.api_key) ?? this.client.apiKey,
646
- projectId: getString2(session.project_id) ?? (options.projectId ?? this.client.projectId),
647
- browserType: getString2(session.browser_type) ?? "normal",
648
- createdAt: getString2(session.created_at) ?? "",
649
- inspectUrl: getString2(session.inspect_url) ?? `${this.client.baseUrl}/inspect?session_id=${sessionId}`,
650
- containerId: getString2(session.container_id) ?? null,
651
- inspectUrlDbg: getString2(session.inspect_url_dbg),
652
- ws: getString2(session.ws) ?? null,
888
+ status: getString3(session.status) ?? "active",
889
+ apiKey: getString3(session.api_key) ?? this.client.apiKey,
890
+ projectId: getString3(session.project_id) ?? (options.projectId ?? this.client.projectId),
891
+ browserType: getString3(session.browser_type) ?? "normal",
892
+ createdAt: getString3(session.created_at) ?? "",
893
+ inspectUrl: getString3(session.inspect_url) ?? `${this.client.baseUrl}/inspect?session_id=${sessionId}`,
894
+ containerId: getString3(session.container_id) ?? null,
895
+ inspectUrlDbg: getString3(session.inspect_url_dbg),
896
+ ws: getString3(session.ws) ?? null,
653
897
  client: this.client
654
898
  });
655
899
  });
@@ -696,18 +940,34 @@ var SessionsResource = class {
696
940
  getLogger().error("Error getting WebSocket debugger URL:", response.data);
697
941
  return null;
698
942
  }
699
- const result = asRecord2(response.data);
700
- return getString2(result.webSocketDebuggerUrlTransformed) ?? getString2(result.webSocketDebuggerUrl) ?? null;
943
+ const result = asRecord3(response.data);
944
+ return getString3(result.webSocketDebuggerUrlTransformed) ?? getString3(result.webSocketDebuggerUrl) ?? null;
701
945
  } catch (error) {
702
946
  getLogger().error("Error getting WebSocket debugger URL:", error);
703
947
  return null;
704
948
  }
705
949
  }
950
+ normalizeProxy(proxy) {
951
+ if (!proxy.server) {
952
+ throw new ValidationError("proxy.server is required when proxy is provided");
953
+ }
954
+ const normalizedProxy = {
955
+ type: proxy.type ?? "external",
956
+ server: proxy.server
957
+ };
958
+ if (proxy.username) {
959
+ normalizedProxy.username = proxy.username;
960
+ }
961
+ if (proxy.password) {
962
+ normalizedProxy.password = proxy.password;
963
+ }
964
+ return normalizedProxy;
965
+ }
706
966
  handleCreateError(response) {
707
- const errorData = asRecord2(response.data);
708
- const errorMessage = getString2(errorData.error) ?? getString2(errorData.message) ?? "Unknown error";
709
- const errorCode = getString2(errorData.code);
710
- const metadata = asRecord2(errorData.metadata);
967
+ const errorData = asRecord3(response.data);
968
+ const errorMessage = getString3(errorData.error) ?? getString3(errorData.message) ?? "Unknown error";
969
+ const errorCode = getString3(errorData.code);
970
+ const metadata = asRecord3(errorData.metadata);
711
971
  if (response.status === 401) {
712
972
  throw new AuthenticationError(
713
973
  `Authentication failed: ${errorMessage}. Please check your API key and project ID.`
@@ -721,7 +981,7 @@ var SessionsResource = class {
721
981
  }
722
982
  if (response.status === 409 && errorCode === "context_locked") {
723
983
  throw new ContextLockedError(errorMessage, {
724
- activeSessionId: getString2(metadata.activeSessionId),
984
+ activeSessionId: getString3(metadata.activeSessionId),
725
985
  retryAfter: getNumber(metadata.retryAfter)
726
986
  });
727
987
  }
@@ -731,8 +991,8 @@ var SessionsResource = class {
731
991
  });
732
992
  }
733
993
  handleListError(response) {
734
- const errorData = asRecord2(response.data);
735
- const errorMessage = getString2(errorData.error) ?? getString2(errorData.message) ?? "Unknown error";
994
+ const errorData = asRecord3(response.data);
995
+ const errorMessage = getString3(errorData.error) ?? getString3(errorData.message) ?? "Unknown error";
736
996
  if (response.status === 401) {
737
997
  throw new AuthenticationError(
738
998
  `Authentication failed: ${errorMessage}. Please check your API key and project ID.`
@@ -744,8 +1004,8 @@ var SessionsResource = class {
744
1004
  });
745
1005
  }
746
1006
  handleDeleteError(response, sessionId) {
747
- const errorData = asRecord2(response.data);
748
- const errorMessage = getString2(errorData.error) ?? getString2(errorData.message) ?? "Unknown error";
1007
+ const errorData = asRecord3(response.data);
1008
+ const errorMessage = getString3(errorData.error) ?? getString3(errorData.message) ?? "Unknown error";
749
1009
  if (response.status === 401) {
750
1010
  throw new AuthenticationError(
751
1011
  `Authentication failed: ${errorMessage}. Please check your API key and project ID.`
@@ -785,37 +1045,35 @@ var Lexmount = class {
785
1045
  }
786
1046
  this.httpClient = import_axios2.default.create({
787
1047
  timeout: config2.timeout ?? 6e4,
788
- validateStatus: () => true,
789
- headers: {
790
- "Content-Type": "application/json"
791
- }
1048
+ validateStatus: () => true
792
1049
  });
793
1050
  this.sessions = new SessionsResource(this);
794
1051
  this.contexts = new ContextsResource(this);
1052
+ this.extensions = new ExtensionsResource(this);
795
1053
  }
796
1054
  /**
797
1055
  * Internal POST request helper with timeout and network error mapping.
798
1056
  *
799
1057
  * @internal
800
1058
  */
801
- async _post(url, data) {
802
- return this.request("POST", url, { data });
1059
+ async _post(url, data, config2 = {}) {
1060
+ return this.request("POST", url, { ...config2, data });
803
1061
  }
804
1062
  /**
805
1063
  * Internal GET request helper with timeout and network error mapping.
806
1064
  *
807
1065
  * @internal
808
1066
  */
809
- async _get(url, params) {
810
- return this.request("GET", url, { params });
1067
+ async _get(url, params, config2 = {}) {
1068
+ return this.request("GET", url, { ...config2, params });
811
1069
  }
812
1070
  /**
813
1071
  * Internal DELETE request helper with timeout and network error mapping.
814
1072
  *
815
1073
  * @internal
816
1074
  */
817
- async _delete(url, data) {
818
- return this.request("DELETE", url, { data });
1075
+ async _delete(url, data, config2 = {}) {
1076
+ return this.request("DELETE", url, { ...config2, data });
819
1077
  }
820
1078
  /**
821
1079
  * Close the client.
@@ -836,10 +1094,17 @@ var Lexmount = class {
836
1094
  const startTime = Date.now();
837
1095
  logger2.debug(`${method} request to ${url}`);
838
1096
  try {
1097
+ const headers = {
1098
+ api_key: this.apiKey,
1099
+ project_id: this.projectId,
1100
+ ...this.getDefaultHeaders(config2.data),
1101
+ ...config2.headers ?? {}
1102
+ };
839
1103
  const response = await this.httpClient.request({
840
1104
  method,
841
1105
  url,
842
- ...config2
1106
+ ...config2,
1107
+ headers
843
1108
  });
844
1109
  const elapsed = Date.now() - startTime;
845
1110
  logger2.debug(`${method} response: status=${response.status}, duration=${elapsed.toFixed(2)}ms`);
@@ -870,6 +1135,17 @@ var Lexmount = class {
870
1135
  logger2.error(`Network error after ${elapsedMs.toFixed(2)}ms:`, axiosError.message);
871
1136
  return new NetworkError(`Network error: ${axiosError.message}`);
872
1137
  }
1138
+ getDefaultHeaders(data) {
1139
+ if (typeof data === "undefined") {
1140
+ return {};
1141
+ }
1142
+ if (typeof FormData !== "undefined" && data instanceof FormData) {
1143
+ return {};
1144
+ }
1145
+ return {
1146
+ "Content-Type": "application/json"
1147
+ };
1148
+ }
873
1149
  };
874
1150
 
875
1151
  // src/index.ts
@@ -883,11 +1159,17 @@ var VERSION = "0.2.0";
883
1159
  ContextLockedError,
884
1160
  ContextNotFoundError,
885
1161
  ContextsResource,
1162
+ ExtensionInfo,
1163
+ ExtensionsResource,
886
1164
  Lexmount,
887
1165
  LexmountError,
888
1166
  LexmountLogger,
889
1167
  NetworkError,
890
1168
  PaginationInfo,
1169
+ SessionDownloadInfo,
1170
+ SessionDownloadsDeleteResponse,
1171
+ SessionDownloadsListResponse,
1172
+ SessionDownloadsResource,
891
1173
  SessionInfo,
892
1174
  SessionListResponse,
893
1175
  SessionNotFoundError,