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.mjs CHANGED
@@ -402,13 +402,120 @@ var ContextsResource = class {
402
402
  }
403
403
  };
404
404
 
405
- // src/sessions.ts
405
+ // src/extensions.ts
406
+ import { readFile } from "fs/promises";
407
+ import path from "path";
406
408
  function asRecord2(value) {
407
409
  return typeof value === "object" && value !== null ? value : {};
408
410
  }
409
411
  function getString2(value) {
410
412
  return typeof value === "string" ? value : void 0;
411
413
  }
414
+ var ExtensionInfo = class {
415
+ constructor(shape) {
416
+ this.id = shape.id;
417
+ this.name = shape.name;
418
+ this.projectId = shape.projectId;
419
+ this.createdAt = shape.createdAt;
420
+ this.updatedAt = shape.updatedAt;
421
+ }
422
+ };
423
+ var ExtensionsResource = class {
424
+ constructor(client) {
425
+ this.client = client;
426
+ }
427
+ async upload(filePath, options = {}) {
428
+ const fileBuffer = await readFile(filePath);
429
+ const form = new FormData();
430
+ const usedProjectId = options.projectId ?? this.client.projectId;
431
+ form.append("project_id", usedProjectId);
432
+ if (options.name) {
433
+ form.append("name", options.name);
434
+ }
435
+ form.append(
436
+ "file",
437
+ new Blob([fileBuffer], { type: "application/octet-stream" }),
438
+ path.basename(filePath)
439
+ );
440
+ const response = await this.client._post(
441
+ `${this.client.baseUrl}/instance/v1/extension/upload`,
442
+ form,
443
+ {
444
+ headers: {
445
+ project_id: usedProjectId
446
+ }
447
+ }
448
+ );
449
+ if (response.status >= 400) {
450
+ this.handleError("upload extension", response);
451
+ }
452
+ getLogger().info(`Extension uploaded successfully: ${filePath}`);
453
+ return this.parseInfo(response.data);
454
+ }
455
+ async list(options = {}) {
456
+ const response = await this.client._post(`${this.client.baseUrl}/instance/v1/extension/list`, {
457
+ project_id: options.projectId ?? this.client.projectId,
458
+ limit: options.limit ?? 20,
459
+ offset: options.offset ?? 0
460
+ });
461
+ if (response.status >= 400) {
462
+ this.handleError("list extensions", response);
463
+ }
464
+ const result = asRecord2(response.data);
465
+ const items = Array.isArray(result.data) ? result.data : [];
466
+ return items.map((item) => this.parseInfo(item));
467
+ }
468
+ async get(extensionId, projectId) {
469
+ const response = await this.client._post(`${this.client.baseUrl}/instance/v1/extension/info`, {
470
+ project_id: projectId ?? this.client.projectId,
471
+ extension_id: extensionId
472
+ });
473
+ if (response.status >= 400) {
474
+ this.handleError("get extension", response);
475
+ }
476
+ return this.parseInfo(response.data);
477
+ }
478
+ async delete(extensionId) {
479
+ const response = await this.client._delete(
480
+ `${this.client.baseUrl}/instance/v1/extension/${extensionId}`
481
+ );
482
+ if (response.status >= 400) {
483
+ this.handleError("delete extension", response);
484
+ }
485
+ getLogger().info(`Extension deleted successfully: ${extensionId}`);
486
+ }
487
+ parseInfo(data) {
488
+ const item = asRecord2(data);
489
+ return new ExtensionInfo({
490
+ id: getString2(item.id) ?? "",
491
+ name: getString2(item.name) ?? "",
492
+ projectId: getString2(item.project_id) ?? "",
493
+ createdAt: getString2(item.created_at),
494
+ updatedAt: getString2(item.updated_at)
495
+ });
496
+ }
497
+ handleError(action, response) {
498
+ const errorData = asRecord2(response.data);
499
+ const errorMessage = getString2(errorData.message) ?? getString2(errorData.error) ?? "Unknown error";
500
+ if (response.status === 401) {
501
+ throw new AuthenticationError(
502
+ `Authentication failed: ${errorMessage}. Please check your API key and project ID.`
503
+ );
504
+ }
505
+ throw new APIError(`Failed to ${action}: ${errorMessage}`, {
506
+ statusCode: response.status,
507
+ response: response.data
508
+ });
509
+ }
510
+ };
511
+
512
+ // src/sessions.ts
513
+ function asRecord3(value) {
514
+ return typeof value === "object" && value !== null ? value : {};
515
+ }
516
+ function getString3(value) {
517
+ return typeof value === "string" ? value : void 0;
518
+ }
412
519
  function getNumber(value) {
413
520
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
414
521
  }
@@ -505,9 +612,134 @@ var SessionListResponse = class {
505
612
  return this.sessions[index];
506
613
  }
507
614
  };
615
+ var SessionDownloadInfo = class {
616
+ constructor(shape) {
617
+ this.id = shape.id;
618
+ this.filename = shape.filename;
619
+ this.contentType = shape.contentType;
620
+ this.size = shape.size;
621
+ this.sha256 = shape.sha256;
622
+ this.status = shape.status;
623
+ this.createdAt = shape.createdAt;
624
+ }
625
+ };
626
+ var SessionDownloadsListResponse = class {
627
+ constructor(downloads, summary) {
628
+ this.downloads = downloads;
629
+ this.summary = summary;
630
+ }
631
+ };
632
+ var SessionDownloadsDeleteResponse = class {
633
+ constructor(shape) {
634
+ this.status = shape.status;
635
+ this.deletedCount = shape.deletedCount;
636
+ }
637
+ };
638
+ var SessionDownloadsResource = class {
639
+ constructor(client) {
640
+ this.client = client;
641
+ }
642
+ async list(sessionId, projectId) {
643
+ const response = await this.client._post(
644
+ `${this.client.baseUrl}/instance/v1/sessions/${sessionId}/downloads/list`,
645
+ {
646
+ api_key: this.client.apiKey,
647
+ project_id: projectId ?? this.client.projectId
648
+ }
649
+ );
650
+ if (response.status >= 400) {
651
+ this.handleError("list session downloads", response, sessionId);
652
+ }
653
+ const result = asRecord3(response.data);
654
+ const items = Array.isArray(result.downloads) ? result.downloads : [];
655
+ const downloads = items.map((item) => {
656
+ const download = asRecord3(item);
657
+ return new SessionDownloadInfo({
658
+ id: getString3(download.id) ?? "",
659
+ filename: getString3(download.filename) ?? "",
660
+ contentType: getString3(download.content_type) ?? null,
661
+ size: getNumber(download.size) ?? 0,
662
+ sha256: getString3(download.sha256) ?? null,
663
+ status: getString3(download.status) ?? "available",
664
+ createdAt: getString3(download.created_at) ?? ""
665
+ });
666
+ });
667
+ const summary = asRecord3(result.summary);
668
+ return new SessionDownloadsListResponse(downloads, {
669
+ count: getNumber(summary.count) ?? downloads.length,
670
+ totalSize: getNumber(summary.total_size) ?? 0
671
+ });
672
+ }
673
+ async get(sessionId, downloadId, projectId) {
674
+ const response = await this.client._get(
675
+ `${this.client.baseUrl}/instance/v1/sessions/${sessionId}/downloads/${downloadId}`,
676
+ {
677
+ api_key: this.client.apiKey,
678
+ project_id: projectId ?? this.client.projectId
679
+ },
680
+ {
681
+ responseType: "arraybuffer"
682
+ }
683
+ );
684
+ if (response.status >= 400) {
685
+ this.handleError("fetch session download", response, sessionId);
686
+ }
687
+ return Buffer.from(response.data);
688
+ }
689
+ async archive(sessionId, projectId) {
690
+ const response = await this.client._get(
691
+ `${this.client.baseUrl}/instance/v1/sessions/${sessionId}/downloads/archive`,
692
+ {
693
+ api_key: this.client.apiKey,
694
+ project_id: projectId ?? this.client.projectId
695
+ },
696
+ {
697
+ responseType: "arraybuffer"
698
+ }
699
+ );
700
+ if (response.status >= 400) {
701
+ this.handleError("archive session downloads", response, sessionId);
702
+ }
703
+ return Buffer.from(response.data);
704
+ }
705
+ async delete(sessionId, projectId) {
706
+ const response = await this.client._delete(
707
+ `${this.client.baseUrl}/instance/v1/sessions/${sessionId}/downloads`,
708
+ {
709
+ api_key: this.client.apiKey,
710
+ project_id: projectId ?? this.client.projectId
711
+ }
712
+ );
713
+ if (response.status >= 400) {
714
+ this.handleError("delete session downloads", response, sessionId);
715
+ }
716
+ const result = asRecord3(response.data);
717
+ return new SessionDownloadsDeleteResponse({
718
+ status: getString3(result.status) ?? "",
719
+ deletedCount: getNumber(result.deleted_count) ?? 0
720
+ });
721
+ }
722
+ handleError(action, response, sessionId) {
723
+ const errorData = asRecord3(response.data);
724
+ const errorMessage = getString3(errorData.error) ?? getString3(errorData.message) ?? "Unknown error";
725
+ if (response.status === 401) {
726
+ throw new AuthenticationError(
727
+ `Authentication failed: ${errorMessage}. Please check your API key and project ID.`
728
+ );
729
+ }
730
+ if (response.status === 404) {
731
+ throw new SessionNotFoundError(`Session downloads not found for ${sessionId}: ${errorMessage}`);
732
+ }
733
+ throw new APIError(`Failed to ${action}: ${errorMessage}`, {
734
+ statusCode: response.status,
735
+ response: response.data
736
+ });
737
+ }
738
+ };
508
739
  var SessionsResource = class {
509
740
  constructor(client) {
510
741
  this.client = client;
742
+ this.downloads = new SessionDownloadsResource(client);
511
743
  }
512
744
  /**
513
745
  * Create a new browser session.
@@ -531,19 +763,25 @@ var SessionsResource = class {
531
763
  } else {
532
764
  getLogger().debug(`Creating session with browser_mode=${payload.browser_mode}`);
533
765
  }
766
+ if (options.extensionIds && options.extensionIds.length > 0) {
767
+ payload.extension_ids = options.extensionIds;
768
+ }
769
+ if (options.proxy) {
770
+ payload.proxy = this.normalizeProxy(options.proxy);
771
+ }
534
772
  const response = await this.client._post(url, payload);
535
773
  if (response.status >= 400) {
536
774
  this.handleCreateError(response);
537
775
  }
538
- const result = asRecord2(response.data);
539
- const sessionId = getString2(result.session_id);
776
+ const result = asRecord3(response.data);
777
+ const sessionId = getString3(result.session_id);
540
778
  if (!sessionId) {
541
779
  throw new APIError("Failed to create session: session_id missing from response", {
542
780
  statusCode: response.status,
543
781
  response: response.data
544
782
  });
545
783
  }
546
- const containerId = getString2(result.container_id) ?? null;
784
+ const containerId = getString3(result.container_id) ?? null;
547
785
  const wsUrl = await this._getWebSocketDebuggerUrl(sessionId);
548
786
  const projectId = options.projectId ?? this.client.projectId;
549
787
  getLogger().info(`Session created successfully: id=${sessionId}, container_id=${containerId}`);
@@ -576,23 +814,23 @@ var SessionsResource = class {
576
814
  if (response.status >= 400) {
577
815
  this.handleListError(response);
578
816
  }
579
- const result = asRecord2(response.data);
817
+ const result = asRecord3(response.data);
580
818
  const sessionsData = Array.isArray(result.sessions) ? result.sessions : [];
581
- const paginationData = asRecord2(result.pagination);
819
+ const paginationData = asRecord3(result.pagination);
582
820
  const sessions = sessionsData.map((item) => {
583
- const session = asRecord2(item);
584
- const sessionId = getString2(session.id) ?? "";
821
+ const session = asRecord3(item);
822
+ const sessionId = getString3(session.id) ?? "";
585
823
  return new SessionInfo({
586
824
  id: sessionId,
587
- status: getString2(session.status) ?? "active",
588
- apiKey: getString2(session.api_key) ?? this.client.apiKey,
589
- projectId: getString2(session.project_id) ?? (options.projectId ?? this.client.projectId),
590
- browserType: getString2(session.browser_type) ?? "normal",
591
- createdAt: getString2(session.created_at) ?? "",
592
- inspectUrl: getString2(session.inspect_url) ?? `${this.client.baseUrl}/inspect?session_id=${sessionId}`,
593
- containerId: getString2(session.container_id) ?? null,
594
- inspectUrlDbg: getString2(session.inspect_url_dbg),
595
- ws: getString2(session.ws) ?? null,
825
+ status: getString3(session.status) ?? "active",
826
+ apiKey: getString3(session.api_key) ?? this.client.apiKey,
827
+ projectId: getString3(session.project_id) ?? (options.projectId ?? this.client.projectId),
828
+ browserType: getString3(session.browser_type) ?? "normal",
829
+ createdAt: getString3(session.created_at) ?? "",
830
+ inspectUrl: getString3(session.inspect_url) ?? `${this.client.baseUrl}/inspect?session_id=${sessionId}`,
831
+ containerId: getString3(session.container_id) ?? null,
832
+ inspectUrlDbg: getString3(session.inspect_url_dbg),
833
+ ws: getString3(session.ws) ?? null,
596
834
  client: this.client
597
835
  });
598
836
  });
@@ -639,18 +877,34 @@ var SessionsResource = class {
639
877
  getLogger().error("Error getting WebSocket debugger URL:", response.data);
640
878
  return null;
641
879
  }
642
- const result = asRecord2(response.data);
643
- return getString2(result.webSocketDebuggerUrlTransformed) ?? getString2(result.webSocketDebuggerUrl) ?? null;
880
+ const result = asRecord3(response.data);
881
+ return getString3(result.webSocketDebuggerUrlTransformed) ?? getString3(result.webSocketDebuggerUrl) ?? null;
644
882
  } catch (error) {
645
883
  getLogger().error("Error getting WebSocket debugger URL:", error);
646
884
  return null;
647
885
  }
648
886
  }
887
+ normalizeProxy(proxy) {
888
+ if (!proxy.server) {
889
+ throw new ValidationError("proxy.server is required when proxy is provided");
890
+ }
891
+ const normalizedProxy = {
892
+ type: proxy.type ?? "external",
893
+ server: proxy.server
894
+ };
895
+ if (proxy.username) {
896
+ normalizedProxy.username = proxy.username;
897
+ }
898
+ if (proxy.password) {
899
+ normalizedProxy.password = proxy.password;
900
+ }
901
+ return normalizedProxy;
902
+ }
649
903
  handleCreateError(response) {
650
- const errorData = asRecord2(response.data);
651
- const errorMessage = getString2(errorData.error) ?? getString2(errorData.message) ?? "Unknown error";
652
- const errorCode = getString2(errorData.code);
653
- const metadata = asRecord2(errorData.metadata);
904
+ const errorData = asRecord3(response.data);
905
+ const errorMessage = getString3(errorData.error) ?? getString3(errorData.message) ?? "Unknown error";
906
+ const errorCode = getString3(errorData.code);
907
+ const metadata = asRecord3(errorData.metadata);
654
908
  if (response.status === 401) {
655
909
  throw new AuthenticationError(
656
910
  `Authentication failed: ${errorMessage}. Please check your API key and project ID.`
@@ -664,7 +918,7 @@ var SessionsResource = class {
664
918
  }
665
919
  if (response.status === 409 && errorCode === "context_locked") {
666
920
  throw new ContextLockedError(errorMessage, {
667
- activeSessionId: getString2(metadata.activeSessionId),
921
+ activeSessionId: getString3(metadata.activeSessionId),
668
922
  retryAfter: getNumber(metadata.retryAfter)
669
923
  });
670
924
  }
@@ -674,8 +928,8 @@ var SessionsResource = class {
674
928
  });
675
929
  }
676
930
  handleListError(response) {
677
- const errorData = asRecord2(response.data);
678
- const errorMessage = getString2(errorData.error) ?? getString2(errorData.message) ?? "Unknown error";
931
+ const errorData = asRecord3(response.data);
932
+ const errorMessage = getString3(errorData.error) ?? getString3(errorData.message) ?? "Unknown error";
679
933
  if (response.status === 401) {
680
934
  throw new AuthenticationError(
681
935
  `Authentication failed: ${errorMessage}. Please check your API key and project ID.`
@@ -687,8 +941,8 @@ var SessionsResource = class {
687
941
  });
688
942
  }
689
943
  handleDeleteError(response, sessionId) {
690
- const errorData = asRecord2(response.data);
691
- const errorMessage = getString2(errorData.error) ?? getString2(errorData.message) ?? "Unknown error";
944
+ const errorData = asRecord3(response.data);
945
+ const errorMessage = getString3(errorData.error) ?? getString3(errorData.message) ?? "Unknown error";
692
946
  if (response.status === 401) {
693
947
  throw new AuthenticationError(
694
948
  `Authentication failed: ${errorMessage}. Please check your API key and project ID.`
@@ -728,37 +982,35 @@ var Lexmount = class {
728
982
  }
729
983
  this.httpClient = axios.create({
730
984
  timeout: config2.timeout ?? 6e4,
731
- validateStatus: () => true,
732
- headers: {
733
- "Content-Type": "application/json"
734
- }
985
+ validateStatus: () => true
735
986
  });
736
987
  this.sessions = new SessionsResource(this);
737
988
  this.contexts = new ContextsResource(this);
989
+ this.extensions = new ExtensionsResource(this);
738
990
  }
739
991
  /**
740
992
  * Internal POST request helper with timeout and network error mapping.
741
993
  *
742
994
  * @internal
743
995
  */
744
- async _post(url, data) {
745
- return this.request("POST", url, { data });
996
+ async _post(url, data, config2 = {}) {
997
+ return this.request("POST", url, { ...config2, data });
746
998
  }
747
999
  /**
748
1000
  * Internal GET request helper with timeout and network error mapping.
749
1001
  *
750
1002
  * @internal
751
1003
  */
752
- async _get(url, params) {
753
- return this.request("GET", url, { params });
1004
+ async _get(url, params, config2 = {}) {
1005
+ return this.request("GET", url, { ...config2, params });
754
1006
  }
755
1007
  /**
756
1008
  * Internal DELETE request helper with timeout and network error mapping.
757
1009
  *
758
1010
  * @internal
759
1011
  */
760
- async _delete(url, data) {
761
- return this.request("DELETE", url, { data });
1012
+ async _delete(url, data, config2 = {}) {
1013
+ return this.request("DELETE", url, { ...config2, data });
762
1014
  }
763
1015
  /**
764
1016
  * Close the client.
@@ -779,10 +1031,17 @@ var Lexmount = class {
779
1031
  const startTime = Date.now();
780
1032
  logger2.debug(`${method} request to ${url}`);
781
1033
  try {
1034
+ const headers = {
1035
+ api_key: this.apiKey,
1036
+ project_id: this.projectId,
1037
+ ...this.getDefaultHeaders(config2.data),
1038
+ ...config2.headers ?? {}
1039
+ };
782
1040
  const response = await this.httpClient.request({
783
1041
  method,
784
1042
  url,
785
- ...config2
1043
+ ...config2,
1044
+ headers
786
1045
  });
787
1046
  const elapsed = Date.now() - startTime;
788
1047
  logger2.debug(`${method} response: status=${response.status}, duration=${elapsed.toFixed(2)}ms`);
@@ -813,6 +1072,17 @@ var Lexmount = class {
813
1072
  logger2.error(`Network error after ${elapsedMs.toFixed(2)}ms:`, axiosError.message);
814
1073
  return new NetworkError(`Network error: ${axiosError.message}`);
815
1074
  }
1075
+ getDefaultHeaders(data) {
1076
+ if (typeof data === "undefined") {
1077
+ return {};
1078
+ }
1079
+ if (typeof FormData !== "undefined" && data instanceof FormData) {
1080
+ return {};
1081
+ }
1082
+ return {
1083
+ "Content-Type": "application/json"
1084
+ };
1085
+ }
816
1086
  };
817
1087
 
818
1088
  // src/index.ts
@@ -825,11 +1095,17 @@ export {
825
1095
  ContextLockedError,
826
1096
  ContextNotFoundError,
827
1097
  ContextsResource,
1098
+ ExtensionInfo,
1099
+ ExtensionsResource,
828
1100
  Lexmount,
829
1101
  LexmountError,
830
1102
  LexmountLogger,
831
1103
  NetworkError,
832
1104
  PaginationInfo,
1105
+ SessionDownloadInfo,
1106
+ SessionDownloadsDeleteResponse,
1107
+ SessionDownloadsListResponse,
1108
+ SessionDownloadsResource,
833
1109
  SessionInfo,
834
1110
  SessionListResponse,
835
1111
  SessionNotFoundError,