@rustrak/client 0.13.0 → 0.14.0

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.cjs CHANGED
@@ -457,6 +457,11 @@ var chunkUploadCapabilitySchema = zod.z.object({
457
457
  accept: zod.z.array(zod.z.string()),
458
458
  concurrency: zod.z.number().int().optional()
459
459
  });
460
+ var assembleInputSchema = zod.z.object({
461
+ checksum: zod.z.string().min(1),
462
+ chunks: zod.z.array(zod.z.string().min(1)),
463
+ projects: zod.z.array(zod.z.string().min(1)).min(1)
464
+ });
460
465
  var assembleResponseSchema = zod.z.object({
461
466
  state: zod.z.string(),
462
467
  missingChunks: zod.z.array(zod.z.string()),
@@ -639,117 +644,330 @@ var registerRequestSchema = zod.z.object({
639
644
  password: zod.z.string().min(1)
640
645
  });
641
646
 
642
- // src/errors/base.ts
643
- var RustrakError = class extends Error {
644
- /**
645
- * Whether this error is safe to retry
646
- */
647
- retryable;
648
- /**
649
- * HTTP status code if applicable
650
- */
651
- statusCode;
652
- /**
653
- * Original error cause
654
- */
655
- cause;
656
- constructor(message, options) {
657
- super(message);
658
- this.name = this.constructor.name;
659
- this.retryable = options?.retryable ?? false;
660
- this.statusCode = options?.statusCode;
661
- this.cause = options?.cause;
662
- if (Error.captureStackTrace) {
663
- Error.captureStackTrace(this, this.constructor);
664
- }
665
- }
666
- };
647
+ // src/errors.ts
648
+ var FIELD_ERROR_CODES = [
649
+ "required",
650
+ "invalid",
651
+ "already_exists",
652
+ "too_short",
653
+ "too_long",
654
+ "custom"
655
+ ];
656
+ var SERVER_ERROR_MESSAGE = "The server failed to handle the request.";
657
+ var NETWORK_ERROR_MESSAGE = "The request could not reach the server.";
658
+ var TIMEOUT_ERROR_MESSAGE = "The request timed out.";
659
+ function isRetryable(error) {
660
+ return error.kind === "network" || error.kind === "server_error" || error.kind === "rate_limited";
661
+ }
667
662
 
668
- // src/errors/http.ts
669
- var NetworkError = class extends RustrakError {
670
- constructor(message, cause) {
671
- super(message, { retryable: true, cause });
663
+ // src/result.ts
664
+ function Ok(data) {
665
+ return { success: true, data };
666
+ }
667
+ function Err(error) {
668
+ return { success: false, error };
669
+ }
670
+ function unwrap(result) {
671
+ if (result.success) {
672
+ return result.data;
672
673
  }
673
- };
674
- var AuthenticationError = class extends RustrakError {
675
- constructor(message = "Authentication failed") {
676
- super(message, { retryable: false, statusCode: 401 });
674
+ throw new Error(
675
+ `unwrap() called on a failed Result: ${describe(result.error)}`
676
+ );
677
+ }
678
+ function unwrapOr(result, fallback) {
679
+ return result.success ? result.data : fallback;
680
+ }
681
+ function mapResult(result, fn) {
682
+ return result.success ? Ok(fn(result.data)) : result;
683
+ }
684
+ function describe(error) {
685
+ if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
686
+ return error.message;
677
687
  }
678
- };
679
- var AuthorizationError = class extends RustrakError {
680
- constructor(message = "Insufficient permissions") {
681
- super(message, { retryable: false, statusCode: 403 });
688
+ return String(error);
689
+ }
690
+ var RustrakTransportFailure = class extends Error {
691
+ rustrakError;
692
+ constructor(error) {
693
+ super(error.message);
694
+ this.name = "RustrakTransportFailure";
695
+ this.rustrakError = error;
682
696
  }
683
697
  };
684
- var NotFoundError = class extends RustrakError {
685
- constructor(resource) {
686
- super(`Resource not found: ${resource}`, {
687
- retryable: false,
688
- statusCode: 404
689
- });
698
+ function isTransportFailure(value) {
699
+ return value instanceof RustrakTransportFailure;
700
+ }
701
+ function readErrorMessage(error, status) {
702
+ const fallback = `HTTP ${status} error`;
703
+ const body = error.data;
704
+ if (body && typeof body === "object") {
705
+ const nested = typeof body.error === "object" ? body.error?.message : body.error;
706
+ return nested || body.message || fallback;
690
707
  }
691
- };
692
- var RateLimitError = class extends RustrakError {
693
- /**
694
- * Number of seconds to wait before retrying (from Retry-After header)
695
- */
696
- retryAfter;
697
- constructor(message = "Rate limit exceeded", retryAfter) {
698
- super(message, { retryable: true, statusCode: 429 });
699
- if (retryAfter !== void 0) {
700
- this.retryAfter = typeof retryAfter === "string" ? parseInt(retryAfter, 10) : retryAfter;
708
+ return fallback;
709
+ }
710
+ var KNOWN_FIELD_ERROR_CODES = new Set(FIELD_ERROR_CODES);
711
+ function isFieldErrorCode(value) {
712
+ return typeof value === "string" && KNOWN_FIELD_ERROR_CODES.has(value);
713
+ }
714
+ var warnedFieldErrorCodes = /* @__PURE__ */ new Set();
715
+ function warnUnknownFieldErrorCode(code, field) {
716
+ if (warnedFieldErrorCodes.has(code)) return;
717
+ warnedFieldErrorCodes.add(code);
718
+ console.warn(
719
+ `[@rustrak/client] Dropped a field error with an unrecognised code "${code}" (field "${field}"). This client build does not know that code, so the affected inputs degrade to a form-level error. The server is most likely newer than @rustrak/client; upgrade the client to match the server version.`
720
+ );
721
+ }
722
+ function readFieldErrors(error) {
723
+ const body = error.data;
724
+ const raw = body && typeof body === "object" ? body.error?.fields : void 0;
725
+ if (!Array.isArray(raw)) {
726
+ return void 0;
727
+ }
728
+ const fields = [];
729
+ for (const entry of raw) {
730
+ if (typeof entry !== "object" || entry === null) continue;
731
+ const { field, code, message } = entry;
732
+ if (typeof field !== "string") continue;
733
+ if (!isFieldErrorCode(code)) {
734
+ if (typeof code === "string") warnUnknownFieldErrorCode(code, field);
735
+ continue;
701
736
  }
737
+ fields.push(
738
+ code === "custom" && typeof message === "string" ? { field, code, message } : { field, code }
739
+ );
702
740
  }
703
- };
704
- var ServerError = class extends RustrakError {
705
- constructor(message, statusCode = 500) {
706
- super(message, { retryable: true, statusCode });
741
+ return fields.length > 0 ? fields : void 0;
742
+ }
743
+ function readRetryAfter(response) {
744
+ const header = response.headers.get("Retry-After")?.trim();
745
+ if (!header) {
746
+ return void 0;
707
747
  }
708
- };
709
- var BadRequestError = class extends RustrakError {
710
- constructor(message) {
711
- super(message, { retryable: false, statusCode: 400 });
748
+ if (/^\d+$/.test(header)) {
749
+ return Number.parseInt(header, 10);
712
750
  }
713
- };
714
-
715
- // src/errors/validation.ts
716
- var ValidationError = class extends RustrakError {
717
- /**
718
- * Zod validation errors
719
- */
720
- validationErrors;
721
- constructor(message, validationErrors) {
722
- super(message, { retryable: false });
723
- this.validationErrors = validationErrors;
751
+ if (/^[+-]\d+$/.test(header)) {
752
+ return void 0;
724
753
  }
725
- /**
726
- * Get a formatted string of validation errors
727
- */
728
- getValidationDetails() {
729
- if (!this.validationErrors) {
730
- return this.message;
754
+ const date = Date.parse(header);
755
+ if (Number.isNaN(date)) {
756
+ return void 0;
757
+ }
758
+ return Math.max(0, Math.ceil((date - Date.now()) / 1e3));
759
+ }
760
+ function annotate(base, fields) {
761
+ return fields === void 0 ? base : { ...base, fields };
762
+ }
763
+ function transformHttpError(error) {
764
+ const { response } = error;
765
+ const status = response.status;
766
+ if (status >= 500) {
767
+ return { kind: "server_error", status, message: SERVER_ERROR_MESSAGE };
768
+ }
769
+ const message = readErrorMessage(error, status);
770
+ const fields = readFieldErrors(error);
771
+ switch (status) {
772
+ case 400:
773
+ return annotate({ kind: "validation", status, message }, fields);
774
+ case 401:
775
+ return annotate({ kind: "unauthenticated", status, message }, fields);
776
+ case 403:
777
+ return annotate({ kind: "forbidden", status, message }, fields);
778
+ case 404:
779
+ return annotate({ kind: "not_found", status, message }, fields);
780
+ case 409:
781
+ return annotate({ kind: "conflict", status, message }, fields);
782
+ case 410:
783
+ return annotate({ kind: "gone", status, message }, fields);
784
+ case 413:
785
+ return annotate({ kind: "payload_too_large", status, message }, fields);
786
+ case 429: {
787
+ const retryAfter = readRetryAfter(response);
788
+ return annotate(
789
+ retryAfter === void 0 ? { kind: "rate_limited", status, message } : { kind: "rate_limited", status, message, retryAfter },
790
+ fields
791
+ );
731
792
  }
732
- const errors = this.validationErrors.issues.map((err) => `${err.path.map(String).join(".")}: ${err.message}`).join(", ");
733
- return `${this.message} - ${errors}`;
793
+ default:
794
+ return annotate({ kind: "client_error", status, message }, fields);
734
795
  }
735
- };
796
+ }
797
+ function createKyInstance(config) {
798
+ const headers = {
799
+ // `Accept` is set here rather than relied on from ky. The `.json()`
800
+ // shortcut sets it before the request is issued, and `BaseResource` no
801
+ // longer uses that shortcut: it awaits the `Response` and parses the body
802
+ // itself, so without this every one of the 86 methods would send `*/*` and
803
+ // any content-negotiating hop could answer with HTML.
804
+ Accept: "application/json",
805
+ "Content-Type": "application/json",
806
+ ...config.headers
807
+ };
808
+ if (config.token) {
809
+ headers.Authorization = `Bearer ${config.token}`;
810
+ }
811
+ return ky__default.default.create({
812
+ prefix: config.baseUrl,
813
+ timeout: config.timeout ?? 3e4,
814
+ credentials: "include",
815
+ retry: {
816
+ limit: config.maxRetries ?? 2,
817
+ statusCodes: [408, 500, 502, 503, 504],
818
+ methods: ["get", "post", "put", "patch", "delete"]
819
+ },
820
+ headers,
821
+ hooks: {
822
+ beforeError: [
823
+ ({ error }) => {
824
+ if (error.name === "TimeoutError") {
825
+ throw new RustrakTransportFailure({
826
+ kind: "network",
827
+ message: TIMEOUT_ERROR_MESSAGE,
828
+ reason: "timeout"
829
+ });
830
+ }
831
+ if (ky.isHTTPError(error)) {
832
+ throw new RustrakTransportFailure(transformHttpError(error));
833
+ }
834
+ throw new RustrakTransportFailure({
835
+ kind: "network",
836
+ message: NETWORK_ERROR_MESSAGE,
837
+ reason: "unreachable"
838
+ });
839
+ }
840
+ ]
841
+ }
842
+ });
843
+ }
736
844
 
737
845
  // src/resources/base.ts
846
+ var TRANSPORT_TYPE_ERROR = /terminated|aborted|socket|network|closed|reset|fetch failed/i;
847
+ function isProgrammingError(error) {
848
+ if (!(error instanceof Error)) {
849
+ return true;
850
+ }
851
+ if (error instanceof ReferenceError || error instanceof RangeError) {
852
+ return true;
853
+ }
854
+ if (error instanceof TypeError) {
855
+ return error.cause === void 0 && !TRANSPORT_TYPE_ERROR.test(error.message);
856
+ }
857
+ return false;
858
+ }
859
+ function classifyBodyFailure(error) {
860
+ if (error instanceof SyntaxError) {
861
+ return {
862
+ kind: "invalid_response",
863
+ message: "The server returned a body that is not valid JSON."
864
+ };
865
+ }
866
+ if (isProgrammingError(error)) {
867
+ return "rethrow";
868
+ }
869
+ return {
870
+ kind: "network",
871
+ message: NETWORK_ERROR_MESSAGE,
872
+ reason: error instanceof Error && error.name === "TimeoutError" ? "timeout" : "unreachable"
873
+ };
874
+ }
875
+ function discardBody(response) {
876
+ try {
877
+ void response.body?.cancel().catch(() => {
878
+ });
879
+ } catch {
880
+ }
881
+ }
738
882
  var BaseResource = class {
739
883
  http;
740
884
  constructor(http) {
741
885
  this.http = http;
742
886
  }
743
887
  /**
744
- * Validate API response against Zod schema
745
- * @throws {ValidationError} if validation fails
888
+ * Validate an API response against its Zod schema.
889
+ *
890
+ * Failure is `invalid_response`: "our own response schema did not match",
891
+ * never "the user's input was rejected". No Zod issues are attached: they
892
+ * embed the offending response data.
746
893
  */
747
894
  validate(data, schema) {
748
895
  const result = schema.safeParse(data);
749
896
  if (!result.success) {
750
- throw new ValidationError("API response validation failed", result.error);
897
+ return Err({
898
+ kind: "invalid_response",
899
+ message: "The server response did not match the expected schema."
900
+ });
901
+ }
902
+ return Ok(result.data);
903
+ }
904
+ /**
905
+ * Validate caller-supplied input before it reaches the network.
906
+ *
907
+ * Failure is `invalid_request` and has no `status`: nothing was sent.
908
+ */
909
+ validateInput(input, schema) {
910
+ const result = schema.safeParse(input);
911
+ if (!result.success) {
912
+ return Err({
913
+ kind: "invalid_request",
914
+ message: "The request input did not match the expected schema."
915
+ });
751
916
  }
752
- return result.data;
917
+ return Ok(result.data);
918
+ }
919
+ /**
920
+ * The boundary. Runs `send`, converts a transport failure into an `Err`, and
921
+ * hands the `Response` to `read`.
922
+ *
923
+ * Anything thrown that is not the internal carrier is a programming error and
924
+ * is rethrown.
925
+ */
926
+ async requestResponse(send, read) {
927
+ let response;
928
+ try {
929
+ response = await send();
930
+ } catch (error) {
931
+ if (isTransportFailure(error)) {
932
+ return Err(error.rustrakError);
933
+ }
934
+ throw error;
935
+ }
936
+ return read(response);
937
+ }
938
+ /**
939
+ * Read a JSON body, mapping a failed read onto the right `kind`.
940
+ *
941
+ * Shared with `AuthResource`, which needs the `Set-Cookie` headers off the
942
+ * raw `Response` and so cannot go through {@link BaseResource.request}.
943
+ */
944
+ async readJson(response) {
945
+ try {
946
+ return Ok(await response.json());
947
+ } catch (error) {
948
+ const failure = classifyBodyFailure(error);
949
+ if (failure === "rethrow") {
950
+ throw error;
951
+ }
952
+ return Err(failure);
953
+ }
954
+ }
955
+ /** Send a request and validate its JSON body against `schema`. */
956
+ async request(send, schema) {
957
+ return this.requestResponse(send, async (response) => {
958
+ const body = await this.readJson(response);
959
+ if (!body.success) {
960
+ return body;
961
+ }
962
+ return this.validate(body.data, schema);
963
+ });
964
+ }
965
+ /** Send a request whose response body carries no contract (204, or ignored). */
966
+ async requestVoid(send) {
967
+ return this.requestResponse(send, async (response) => {
968
+ discardBody(response);
969
+ return Ok(void 0);
970
+ });
753
971
  }
754
972
  };
755
973
 
@@ -779,46 +997,56 @@ var AgentsResource = class extends BaseResource {
779
997
  * Time-bucketed count of agent-run spans (`gen_ai.operation.type:agent`).
780
998
  */
781
999
  async getRuns(projectId, options) {
782
- const data = await this.http.get(`api/projects/${projectId}/agents/runs`, {
783
- searchParams: timeseriesSearchParams(options)
784
- }).json();
785
- return this.validate(data, zod.z.array(agentTimeseriesPointSchema));
1000
+ return this.request(
1001
+ () => this.http.get(`api/projects/${projectId}/agents/runs`, {
1002
+ searchParams: timeseriesSearchParams(options)
1003
+ }),
1004
+ zod.z.array(agentTimeseriesPointSchema)
1005
+ );
786
1006
  }
787
1007
  /**
788
1008
  * Time-bucketed avg/p95 duration for `agent`/`ai_client` spans.
789
1009
  */
790
1010
  async getDuration(projectId, options) {
791
- const data = await this.http.get(`api/projects/${projectId}/agents/duration`, {
792
- searchParams: timeseriesSearchParams(options)
793
- }).json();
794
- return this.validate(data, zod.z.array(agentDurationPointSchema));
1011
+ return this.request(
1012
+ () => this.http.get(`api/projects/${projectId}/agents/duration`, {
1013
+ searchParams: timeseriesSearchParams(options)
1014
+ }),
1015
+ zod.z.array(agentDurationPointSchema)
1016
+ );
795
1017
  }
796
1018
  /**
797
1019
  * Top models by LLM call count (`gen_ai.operation.type:ai_client`).
798
1020
  */
799
1021
  async getModelsByCalls(projectId, options) {
800
- const data = await this.http.get(`api/projects/${projectId}/agents/models/calls`, {
801
- searchParams: breakdownSearchParams(options)
802
- }).json();
803
- return this.validate(data, zod.z.array(genAiBreakdownRowSchema));
1022
+ return this.request(
1023
+ () => this.http.get(`api/projects/${projectId}/agents/models/calls`, {
1024
+ searchParams: breakdownSearchParams(options)
1025
+ }),
1026
+ zod.z.array(genAiBreakdownRowSchema)
1027
+ );
804
1028
  }
805
1029
  /**
806
1030
  * Top models by total tokens used (`gen_ai.operation.type:ai_client`).
807
1031
  */
808
1032
  async getModelsByTokens(projectId, options) {
809
- const data = await this.http.get(`api/projects/${projectId}/agents/models/tokens`, {
810
- searchParams: breakdownSearchParams(options)
811
- }).json();
812
- return this.validate(data, zod.z.array(genAiBreakdownRowSchema));
1033
+ return this.request(
1034
+ () => this.http.get(`api/projects/${projectId}/agents/models/tokens`, {
1035
+ searchParams: breakdownSearchParams(options)
1036
+ }),
1037
+ zod.z.array(genAiBreakdownRowSchema)
1038
+ );
813
1039
  }
814
1040
  /**
815
1041
  * Top tools by call count (`gen_ai.operation.type:tool`).
816
1042
  */
817
1043
  async getTools(projectId, options) {
818
- const data = await this.http.get(`api/projects/${projectId}/agents/tools`, {
819
- searchParams: breakdownSearchParams(options)
820
- }).json();
821
- return this.validate(data, zod.z.array(genAiBreakdownRowSchema));
1044
+ return this.request(
1045
+ () => this.http.get(`api/projects/${projectId}/agents/tools`, {
1046
+ searchParams: breakdownSearchParams(options)
1047
+ }),
1048
+ zod.z.array(genAiBreakdownRowSchema)
1049
+ );
822
1050
  }
823
1051
  /**
824
1052
  * Paginated per-trace_id aggregate (duration, tokens, tool usage) across
@@ -832,9 +1060,10 @@ var AgentsResource = class extends BaseResource {
832
1060
  if (options?.per_page) {
833
1061
  searchParams.per_page = String(options.per_page);
834
1062
  }
835
- const data = await this.http.get(`api/projects/${projectId}/agents/traces`, { searchParams }).json();
836
- return this.validate(
837
- data,
1063
+ return this.request(
1064
+ () => this.http.get(`api/projects/${projectId}/agents/traces`, {
1065
+ searchParams
1066
+ }),
838
1067
  offsetPaginatedResponseSchema(agentTraceSummarySchema)
839
1068
  );
840
1069
  }
@@ -844,52 +1073,83 @@ var AlertIntegrationsResource = class extends BaseResource {
844
1073
  * List all alert integrations
845
1074
  */
846
1075
  async list() {
847
- const data = await this.http.get("api/integrations").json();
848
- return this.validate(data, zod.z.array(alertIntegrationSchema));
1076
+ return this.request(
1077
+ () => this.http.get("api/integrations"),
1078
+ zod.z.array(alertIntegrationSchema)
1079
+ );
849
1080
  }
850
1081
  /**
851
1082
  * Get a single alert integration by ID
852
1083
  */
853
1084
  async get(id) {
854
- const data = await this.http.get(`api/integrations/${id}`).json();
855
- return this.validate(data, alertIntegrationSchema);
1085
+ return this.request(
1086
+ () => this.http.get(`api/integrations/${id}`),
1087
+ alertIntegrationSchema
1088
+ );
856
1089
  }
857
1090
  /**
858
1091
  * Create a new alert integration
859
1092
  */
860
1093
  async create(input) {
861
- const validatedInput = this.validate(input, createAlertIntegrationSchema);
862
- const data = await this.http.post("api/integrations", { json: validatedInput }).json();
863
- return this.validate(data, alertIntegrationSchema);
1094
+ const validatedInput = this.validateInput(
1095
+ input,
1096
+ createAlertIntegrationSchema
1097
+ );
1098
+ if (!validatedInput.success) {
1099
+ return validatedInput;
1100
+ }
1101
+ return this.request(
1102
+ () => this.http.post("api/integrations", { json: validatedInput.data }),
1103
+ alertIntegrationSchema
1104
+ );
864
1105
  }
865
1106
  /**
866
1107
  * Update an existing alert integration
867
1108
  */
868
1109
  async update(id, input) {
869
- const validatedInput = this.validate(input, updateAlertIntegrationSchema);
870
- const data = await this.http.patch(`api/integrations/${id}`, { json: validatedInput }).json();
871
- return this.validate(data, alertIntegrationSchema);
1110
+ const validatedInput = this.validateInput(
1111
+ input,
1112
+ updateAlertIntegrationSchema
1113
+ );
1114
+ if (!validatedInput.success) {
1115
+ return validatedInput;
1116
+ }
1117
+ return this.request(
1118
+ () => this.http.patch(`api/integrations/${id}`, {
1119
+ json: validatedInput.data
1120
+ }),
1121
+ alertIntegrationSchema
1122
+ );
872
1123
  }
873
1124
  /**
874
1125
  * Delete an alert integration
875
1126
  */
876
1127
  async delete(id) {
877
- await this.http.delete(`api/integrations/${id}`);
1128
+ return this.requestVoid(() => this.http.delete(`api/integrations/${id}`));
878
1129
  }
879
1130
  /**
880
1131
  * Send a test notification to verify integration configuration.
881
1132
  * For Slack bot_token integrations, routingOverride must include `channel`.
882
1133
  */
883
1134
  async test(id, routingOverride) {
884
- const body = routingOverride !== void 0 ? this.validate(
885
- { routing_override: routingOverride },
886
- testIntegrationBodySchema
887
- ) : void 0;
888
- const data = await this.http.post(
889
- `api/integrations/${id}/test`,
890
- body !== void 0 ? { json: body } : void 0
891
- ).json();
892
- return this.validate(data, testChannelResponseSchema);
1135
+ let body;
1136
+ if (routingOverride !== void 0) {
1137
+ const validatedInput = this.validateInput(
1138
+ { routing_override: routingOverride },
1139
+ testIntegrationBodySchema
1140
+ );
1141
+ if (!validatedInput.success) {
1142
+ return validatedInput;
1143
+ }
1144
+ body = validatedInput.data;
1145
+ }
1146
+ return this.request(
1147
+ () => this.http.post(
1148
+ `api/integrations/${id}/test`,
1149
+ body !== void 0 ? { json: body } : void 0
1150
+ ),
1151
+ testChannelResponseSchema
1152
+ );
893
1153
  }
894
1154
  };
895
1155
  var AlertRulesResource = class extends BaseResource {
@@ -897,39 +1157,57 @@ var AlertRulesResource = class extends BaseResource {
897
1157
  * List all alert rules for a project
898
1158
  */
899
1159
  async list(projectId) {
900
- const data = await this.http.get(`api/projects/${projectId}/alert-rules`).json();
901
- return this.validate(data, zod.z.array(alertRuleSchema));
1160
+ return this.request(
1161
+ () => this.http.get(`api/projects/${projectId}/alert-rules`),
1162
+ zod.z.array(alertRuleSchema)
1163
+ );
902
1164
  }
903
1165
  /**
904
1166
  * Get a single alert rule by ID
905
1167
  */
906
1168
  async get(projectId, ruleId) {
907
- const data = await this.http.get(`api/projects/${projectId}/alert-rules/${ruleId}`).json();
908
- return this.validate(data, alertRuleSchema);
1169
+ return this.request(
1170
+ () => this.http.get(`api/projects/${projectId}/alert-rules/${ruleId}`),
1171
+ alertRuleSchema
1172
+ );
909
1173
  }
910
1174
  /**
911
1175
  * Create a new alert rule for a project
912
1176
  */
913
1177
  async create(projectId, input) {
914
- const validatedInput = this.validate(input, createAlertRuleSchema);
915
- const data = await this.http.post(`api/projects/${projectId}/alert-rules`, { json: validatedInput }).json();
916
- return this.validate(data, alertRuleSchema);
1178
+ const validatedInput = this.validateInput(input, createAlertRuleSchema);
1179
+ if (!validatedInput.success) {
1180
+ return validatedInput;
1181
+ }
1182
+ return this.request(
1183
+ () => this.http.post(`api/projects/${projectId}/alert-rules`, {
1184
+ json: validatedInput.data
1185
+ }),
1186
+ alertRuleSchema
1187
+ );
917
1188
  }
918
1189
  /**
919
1190
  * Update an existing alert rule
920
1191
  */
921
1192
  async update(projectId, ruleId, input) {
922
- const validatedInput = this.validate(input, updateAlertRuleSchema);
923
- const data = await this.http.patch(`api/projects/${projectId}/alert-rules/${ruleId}`, {
924
- json: validatedInput
925
- }).json();
926
- return this.validate(data, alertRuleSchema);
1193
+ const validatedInput = this.validateInput(input, updateAlertRuleSchema);
1194
+ if (!validatedInput.success) {
1195
+ return validatedInput;
1196
+ }
1197
+ return this.request(
1198
+ () => this.http.patch(`api/projects/${projectId}/alert-rules/${ruleId}`, {
1199
+ json: validatedInput.data
1200
+ }),
1201
+ alertRuleSchema
1202
+ );
927
1203
  }
928
1204
  /**
929
1205
  * Delete an alert rule
930
1206
  */
931
1207
  async delete(projectId, ruleId) {
932
- await this.http.delete(`api/projects/${projectId}/alert-rules/${ruleId}`);
1208
+ return this.requestVoid(
1209
+ () => this.http.delete(`api/projects/${projectId}/alert-rules/${ruleId}`)
1210
+ );
933
1211
  }
934
1212
  /**
935
1213
  * List alert history for a project
@@ -941,12 +1219,18 @@ var AlertRulesResource = class extends BaseResource {
941
1219
  }
942
1220
  const query = searchParams.toString();
943
1221
  const url = query ? `api/projects/${projectId}/alert-history?${query}` : `api/projects/${projectId}/alert-history`;
944
- const data = await this.http.get(url).json();
945
- return this.validate(data, zod.z.array(alertHistorySchema));
1222
+ return this.request(() => this.http.get(url), zod.z.array(alertHistorySchema));
946
1223
  }
947
1224
  };
948
1225
 
949
1226
  // src/resources/auth.ts
1227
+ function readSetCookie(response) {
1228
+ if (typeof response.headers.getSetCookie === "function") {
1229
+ return response.headers.getSetCookie();
1230
+ }
1231
+ const folded = response.headers.get("set-cookie");
1232
+ return folded === null ? [] : [folded];
1233
+ }
950
1234
  var AuthResource = class extends BaseResource {
951
1235
  /**
952
1236
  * Register a new user account
@@ -955,17 +1239,17 @@ var AuthResource = class extends BaseResource {
955
1239
  * @returns LoginResult with user information and session cookies
956
1240
  */
957
1241
  async register(credentials) {
958
- const validatedInput = this.validate(credentials, registerRequestSchema);
959
- const response = await this.http.post("auth/register", {
960
- json: validatedInput
961
- });
962
- const cookies = response.headers.getSetCookie();
963
- const data = await response.json();
964
- const authResponse = this.validate(data, authResponseSchema);
965
- return {
966
- user: authResponse.user,
967
- cookies
968
- };
1242
+ const validatedInput = this.validateInput(
1243
+ credentials,
1244
+ registerRequestSchema
1245
+ );
1246
+ if (!validatedInput.success) {
1247
+ return validatedInput;
1248
+ }
1249
+ return this.requestResponse(
1250
+ () => this.http.post("auth/register", { json: validatedInput.data }),
1251
+ (response) => this.readLoginResult(response)
1252
+ );
969
1253
  }
970
1254
  /**
971
1255
  * Login with email and password
@@ -974,17 +1258,14 @@ var AuthResource = class extends BaseResource {
974
1258
  * @returns LoginResult with user information and session cookies
975
1259
  */
976
1260
  async login(credentials) {
977
- const validatedInput = this.validate(credentials, loginRequestSchema);
978
- const response = await this.http.post("auth/login", {
979
- json: validatedInput
980
- });
981
- const cookies = response.headers.getSetCookie();
982
- const data = await response.json();
983
- const authResponse = this.validate(data, authResponseSchema);
984
- return {
985
- user: authResponse.user,
986
- cookies
987
- };
1261
+ const validatedInput = this.validateInput(credentials, loginRequestSchema);
1262
+ if (!validatedInput.success) {
1263
+ return validatedInput;
1264
+ }
1265
+ return this.requestResponse(
1266
+ () => this.http.post("auth/login", { json: validatedInput.data }),
1267
+ (response) => this.readLoginResult(response)
1268
+ );
988
1269
  }
989
1270
  /**
990
1271
  * Logout the current user
@@ -992,17 +1273,28 @@ var AuthResource = class extends BaseResource {
992
1273
  * @returns Array of Set-Cookie headers (typically clearing cookies)
993
1274
  */
994
1275
  async logout() {
995
- const response = await this.http.post("auth/logout");
996
- return response.headers.getSetCookie();
1276
+ return this.requestResponse(
1277
+ () => this.http.post("auth/logout"),
1278
+ async (response) => {
1279
+ const cookies = readSetCookie(response);
1280
+ discardBody(response);
1281
+ return Ok(cookies);
1282
+ }
1283
+ );
997
1284
  }
998
1285
  /**
999
1286
  * Get current authenticated user
1000
1287
  * Requires a valid session cookie
1001
- * @returns User information
1288
+ *
1289
+ * No session is `{success: false, kind: 'unauthenticated'}`, not a `null`
1290
+ * user: it is the literal reading of the server's 401. Callers must send the
1291
+ * visitor to login on `unauthenticated` **only**. Treating `network` or
1292
+ * `server_error` the same way turns a flaky connection into a login loop.
1293
+ *
1294
+ * @returns The current user, or `unauthenticated` when there is no session
1002
1295
  */
1003
1296
  async getCurrentUser() {
1004
- const data = await this.http.get("auth/me").json();
1005
- return this.validate(data, userSchema);
1297
+ return this.request(() => this.http.get("auth/me"), userSchema);
1006
1298
  }
1007
1299
  /**
1008
1300
  * Get the details of a pending invitation by its token (public endpoint)
@@ -1011,8 +1303,10 @@ var AuthResource = class extends BaseResource {
1011
1303
  * @returns Invitation info (email, role, status, expiry)
1012
1304
  */
1013
1305
  async getInvitation(token) {
1014
- const data = await this.http.get(`auth/invitation/${token}`).json();
1015
- return this.validate(data, invitationInfoSchema);
1306
+ return this.request(
1307
+ () => this.http.get(`auth/invitation/${token}`),
1308
+ invitationInfoSchema
1309
+ );
1016
1310
  }
1017
1311
  /**
1018
1312
  * Accept a pending invitation, creating the user account and logging in
@@ -1020,17 +1314,33 @@ var AuthResource = class extends BaseResource {
1020
1314
  * @returns LoginResult with user information and session cookies
1021
1315
  */
1022
1316
  async acceptInvitation(input) {
1023
- const validatedInput = this.validate(input, acceptInvitationSchema);
1024
- const response = await this.http.post("auth/accept-invitation", {
1025
- json: validatedInput
1026
- });
1027
- const cookies = response.headers.getSetCookie();
1028
- const data = await response.json();
1029
- const authResponse = this.validate(data, authResponseSchema);
1030
- return {
1031
- user: authResponse.user,
1032
- cookies
1033
- };
1317
+ const validatedInput = this.validateInput(input, acceptInvitationSchema);
1318
+ if (!validatedInput.success) {
1319
+ return validatedInput;
1320
+ }
1321
+ return this.requestResponse(
1322
+ () => this.http.post("auth/accept-invitation", {
1323
+ json: validatedInput.data
1324
+ }),
1325
+ (response) => this.readLoginResult(response)
1326
+ );
1327
+ }
1328
+ /**
1329
+ * The three session-establishing endpoints share a body and need the
1330
+ * `Set-Cookie` headers off the raw `Response`, which is why they go through
1331
+ * `requestResponse` rather than the plain JSON path.
1332
+ */
1333
+ async readLoginResult(response) {
1334
+ const cookies = readSetCookie(response);
1335
+ const body = await this.readJson(response);
1336
+ if (!body.success) {
1337
+ return body;
1338
+ }
1339
+ const authResponse = this.validate(body.data, authResponseSchema);
1340
+ if (!authResponse.success) {
1341
+ return authResponse;
1342
+ }
1343
+ return Ok({ user: authResponse.data.user, cookies });
1034
1344
  }
1035
1345
  };
1036
1346
 
@@ -1047,17 +1357,23 @@ var EventsResource = class extends BaseResource {
1047
1357
  if (options?.cursor) {
1048
1358
  searchParams.cursor = options.cursor;
1049
1359
  }
1050
- const data = await this.http.get(`api/projects/${projectId}/issues/${issueId}/events`, {
1051
- searchParams
1052
- }).json();
1053
- return this.validate(data, paginatedResponseSchema(eventSchema));
1360
+ return this.request(
1361
+ () => this.http.get(`api/projects/${projectId}/issues/${issueId}/events`, {
1362
+ searchParams
1363
+ }),
1364
+ paginatedResponseSchema(eventSchema)
1365
+ );
1054
1366
  }
1055
1367
  /**
1056
1368
  * Get a single event by ID with full details
1057
1369
  */
1058
1370
  async get(projectId, issueId, eventId) {
1059
- const data = await this.http.get(`api/projects/${projectId}/issues/${issueId}/events/${eventId}`).json();
1060
- return this.validate(data, eventDetailSchema);
1371
+ return this.request(
1372
+ () => this.http.get(
1373
+ `api/projects/${projectId}/issues/${issueId}/events/${eventId}`
1374
+ ),
1375
+ eventDetailSchema
1376
+ );
1061
1377
  }
1062
1378
  };
1063
1379
  var serverVersionSchema = zod.z.object({
@@ -1067,8 +1383,10 @@ var serverVersionSchema = zod.z.object({
1067
1383
  // src/resources/health.ts
1068
1384
  var HealthResource = class extends BaseResource {
1069
1385
  async getVersion() {
1070
- const data = await this.http.get("health/version").json();
1071
- return this.validate(data, serverVersionSchema);
1386
+ return this.request(
1387
+ () => this.http.get("health/version"),
1388
+ serverVersionSchema
1389
+ );
1072
1390
  }
1073
1391
  };
1074
1392
 
@@ -1079,23 +1397,30 @@ var InvitationsResource = class extends BaseResource {
1079
1397
  * @param input - Email and global role for the invited user
1080
1398
  */
1081
1399
  async create(input) {
1082
- const validatedInput = this.validate(input, createInvitationSchema);
1083
- const data = await this.http.post("api/invitations", { json: validatedInput }).json();
1084
- return this.validate(data, invitationSchema);
1400
+ const validatedInput = this.validateInput(input, createInvitationSchema);
1401
+ if (!validatedInput.success) {
1402
+ return validatedInput;
1403
+ }
1404
+ return this.request(
1405
+ () => this.http.post("api/invitations", { json: validatedInput.data }),
1406
+ invitationSchema
1407
+ );
1085
1408
  }
1086
1409
  /**
1087
1410
  * List all invitations
1088
1411
  */
1089
1412
  async list() {
1090
- const data = await this.http.get("api/invitations").json();
1091
- return this.validate(data, invitationSchema.array());
1413
+ return this.request(
1414
+ () => this.http.get("api/invitations"),
1415
+ invitationSchema.array()
1416
+ );
1092
1417
  }
1093
1418
  /**
1094
1419
  * Revoke a pending invitation
1095
1420
  * @param token - Invitation token to revoke
1096
1421
  */
1097
1422
  async revoke(token) {
1098
- await this.http.delete(`api/invitations/${token}`);
1423
+ return this.requestVoid(() => this.http.delete(`api/invitations/${token}`));
1099
1424
  }
1100
1425
  };
1101
1426
  var IssuesResource = class extends BaseResource {
@@ -1122,25 +1447,34 @@ var IssuesResource = class extends BaseResource {
1122
1447
  if (options?.q) {
1123
1448
  searchParams.q = options.q;
1124
1449
  }
1125
- const data = await this.http.get(`api/projects/${projectId}/issues`, { searchParams }).json();
1126
- return this.validate(data, offsetPaginatedResponseSchema(issueSchema));
1450
+ return this.request(
1451
+ () => this.http.get(`api/projects/${projectId}/issues`, { searchParams }),
1452
+ offsetPaginatedResponseSchema(issueSchema)
1453
+ );
1127
1454
  }
1128
1455
  /**
1129
1456
  * Get a single issue by ID
1130
1457
  */
1131
1458
  async get(projectId, issueId) {
1132
- const data = await this.http.get(`api/projects/${projectId}/issues/${issueId}`).json();
1133
- return this.validate(data, issueSchema);
1459
+ return this.request(
1460
+ () => this.http.get(`api/projects/${projectId}/issues/${issueId}`),
1461
+ issueSchema
1462
+ );
1134
1463
  }
1135
1464
  /**
1136
1465
  * Update issue state (status, substatus, priority, assignment, …)
1137
1466
  */
1138
1467
  async updateState(projectId, issueId, input) {
1139
- const validatedInput = this.validate(input, updateIssueStateSchema);
1140
- const data = await this.http.patch(`api/projects/${projectId}/issues/${issueId}`, {
1141
- json: validatedInput
1142
- }).json();
1143
- return this.validate(data, issueSchema);
1468
+ const validatedInput = this.validateInput(input, updateIssueStateSchema);
1469
+ if (!validatedInput.success) {
1470
+ return validatedInput;
1471
+ }
1472
+ return this.request(
1473
+ () => this.http.patch(`api/projects/${projectId}/issues/${issueId}`, {
1474
+ json: validatedInput.data
1475
+ }),
1476
+ issueSchema
1477
+ );
1144
1478
  }
1145
1479
  /**
1146
1480
  * Resolve an issue in the next release (suppresses regression until a deploy).
@@ -1154,115 +1488,163 @@ var IssuesResource = class extends BaseResource {
1154
1488
  * Delete an issue
1155
1489
  */
1156
1490
  async delete(projectId, issueId) {
1157
- await this.http.delete(`api/projects/${projectId}/issues/${issueId}`);
1491
+ return this.requestVoid(
1492
+ () => this.http.delete(`api/projects/${projectId}/issues/${issueId}`)
1493
+ );
1158
1494
  }
1159
1495
  /**
1160
1496
  * Bulk-mutate issues (status and/or priority).
1161
1497
  */
1162
1498
  async bulkUpdate(projectId, input) {
1163
- const validatedInput = this.validate(input, bulkUpdateIssuesSchema);
1164
- const data = await this.http.put(`api/projects/${projectId}/issues`, { json: validatedInput }).json();
1165
- return this.validate(data, zod.z.object({ updated: zod.z.number().int() }));
1499
+ const validatedInput = this.validateInput(input, bulkUpdateIssuesSchema);
1500
+ if (!validatedInput.success) {
1501
+ return validatedInput;
1502
+ }
1503
+ return this.request(
1504
+ () => this.http.put(`api/projects/${projectId}/issues`, {
1505
+ json: validatedInput.data
1506
+ }),
1507
+ zod.z.object({ updated: zod.z.number().int() })
1508
+ );
1166
1509
  }
1167
1510
  /**
1168
1511
  * Bulk-delete issues.
1169
1512
  */
1170
1513
  async bulkDelete(projectId, input) {
1171
- const validatedInput = this.validate(input, bulkDeleteIssuesSchema);
1172
- const data = await this.http.delete(`api/projects/${projectId}/issues`, { json: validatedInput }).json();
1173
- return this.validate(data, zod.z.object({ deleted: zod.z.number().int() }));
1514
+ const validatedInput = this.validateInput(input, bulkDeleteIssuesSchema);
1515
+ if (!validatedInput.success) {
1516
+ return validatedInput;
1517
+ }
1518
+ return this.request(
1519
+ () => this.http.delete(`api/projects/${projectId}/issues`, {
1520
+ json: validatedInput.data
1521
+ }),
1522
+ zod.z.object({ deleted: zod.z.number().int() })
1523
+ );
1174
1524
  }
1175
1525
  /**
1176
1526
  * List the grouping hashes that map to an issue.
1177
1527
  */
1178
1528
  async getHashes(projectId, issueId) {
1179
- const data = await this.http.get(`api/projects/${projectId}/issues/${issueId}/hashes`).json();
1180
- return this.validate(data, zod.z.array(issueHashSchema));
1529
+ return this.request(
1530
+ () => this.http.get(`api/projects/${projectId}/issues/${issueId}/hashes`),
1531
+ zod.z.array(issueHashSchema)
1532
+ );
1181
1533
  }
1182
1534
  /**
1183
1535
  * List the distinct values (with counts) for a tag key across the issue.
1184
1536
  * Returns a bare list, one entry per value (Sentry-compatible shape).
1185
1537
  */
1186
1538
  async getTagValues(projectId, issueId, key) {
1187
- const data = await this.http.get(
1188
- `api/projects/${projectId}/issues/${issueId}/tags/${encodeURIComponent(key)}`
1189
- ).json();
1190
- return this.validate(data, zod.z.array(issueTagValueSchema));
1539
+ return this.request(
1540
+ () => this.http.get(
1541
+ `api/projects/${projectId}/issues/${issueId}/tags/${encodeURIComponent(key)}`
1542
+ ),
1543
+ zod.z.array(issueTagValueSchema)
1544
+ );
1191
1545
  }
1192
1546
  /**
1193
1547
  * Fetch per-issue aggregates (unique user count + top tags).
1194
1548
  */
1195
1549
  async getAggregates(projectId, issueId) {
1196
- const data = await this.http.get(`api/projects/${projectId}/issues/${issueId}/aggregates`).json();
1197
- return this.validate(data, issueAggregatesSchema);
1550
+ return this.request(
1551
+ () => this.http.get(`api/projects/${projectId}/issues/${issueId}/aggregates`),
1552
+ issueAggregatesSchema
1553
+ );
1198
1554
  }
1199
1555
  /**
1200
1556
  * Fetch a zero-filled event-count timeseries (24h or 30d).
1201
1557
  */
1202
1558
  async getStats(projectId, issueId, window = "24h") {
1203
- const data = await this.http.get(`api/projects/${projectId}/issues/${issueId}/stats`, {
1204
- searchParams: { window }
1205
- }).json();
1206
- return this.validate(data, issueStatsSchema);
1559
+ return this.request(
1560
+ () => this.http.get(`api/projects/${projectId}/issues/${issueId}/stats`, {
1561
+ searchParams: { window }
1562
+ }),
1563
+ issueStatsSchema
1564
+ );
1207
1565
  }
1208
1566
  /**
1209
1567
  * List an issue's activity log (status changes, comments/notes, …).
1210
1568
  */
1211
1569
  async getActivity(projectId, issueId) {
1212
- const data = await this.http.get(`api/projects/${projectId}/issues/${issueId}/activity`).json();
1213
- return this.validate(data, zod.z.array(activityEntrySchema));
1570
+ return this.request(
1571
+ () => this.http.get(`api/projects/${projectId}/issues/${issueId}/activity`),
1572
+ zod.z.array(activityEntrySchema)
1573
+ );
1214
1574
  }
1215
1575
  /**
1216
1576
  * Add a comment (note) to an issue.
1217
1577
  */
1218
1578
  async addComment(projectId, issueId, input) {
1219
- const validatedInput = this.validate(input, createCommentSchema);
1220
- const data = await this.http.post(`api/projects/${projectId}/issues/${issueId}/comments`, {
1221
- json: validatedInput
1222
- }).json();
1223
- return this.validate(data, activityEntrySchema);
1579
+ const validatedInput = this.validateInput(input, createCommentSchema);
1580
+ if (!validatedInput.success) {
1581
+ return validatedInput;
1582
+ }
1583
+ return this.request(
1584
+ () => this.http.post(`api/projects/${projectId}/issues/${issueId}/comments`, {
1585
+ json: validatedInput.data
1586
+ }),
1587
+ activityEntrySchema
1588
+ );
1224
1589
  }
1225
1590
  /**
1226
1591
  * Set or clear the current user's bookmark on an issue.
1227
1592
  */
1228
1593
  async setBookmark(projectId, issueId, enabled) {
1229
- const data = await this.http.put(`api/projects/${projectId}/issues/${issueId}/bookmark`, {
1230
- json: { enabled }
1231
- }).json();
1232
- return this.validate(data, zod.z.object({ is_bookmarked: zod.z.boolean() }));
1594
+ return this.request(
1595
+ () => this.http.put(`api/projects/${projectId}/issues/${issueId}/bookmark`, {
1596
+ json: { enabled }
1597
+ }),
1598
+ zod.z.object({ is_bookmarked: zod.z.boolean() })
1599
+ );
1233
1600
  }
1234
1601
  /**
1235
1602
  * Set or clear the current user's subscription to an issue.
1236
1603
  */
1237
1604
  async setSubscription(projectId, issueId, enabled) {
1238
- const data = await this.http.put(`api/projects/${projectId}/issues/${issueId}/subscription`, {
1239
- json: { enabled }
1240
- }).json();
1241
- return this.validate(data, zod.z.object({ is_subscribed: zod.z.boolean() }));
1605
+ return this.request(
1606
+ () => this.http.put(
1607
+ `api/projects/${projectId}/issues/${issueId}/subscription`,
1608
+ { json: { enabled } }
1609
+ ),
1610
+ zod.z.object({ is_subscribed: zod.z.boolean() })
1611
+ );
1242
1612
  }
1243
1613
  /**
1244
1614
  * Mark the issue as seen by the current user.
1245
1615
  */
1246
1616
  async markSeen(projectId, issueId) {
1247
- const data = await this.http.post(`api/projects/${projectId}/issues/${issueId}/seen`).json();
1248
- return this.validate(data, zod.z.object({ has_seen: zod.z.boolean() }));
1617
+ return this.request(
1618
+ () => this.http.post(`api/projects/${projectId}/issues/${issueId}/seen`),
1619
+ zod.z.object({ has_seen: zod.z.boolean() })
1620
+ );
1249
1621
  }
1250
1622
  /**
1251
1623
  * List user feedback reports for an issue.
1252
1624
  */
1253
1625
  async listUserReports(projectId, issueId) {
1254
- const data = await this.http.get(`api/projects/${projectId}/issues/${issueId}/user-reports`).json();
1255
- return this.validate(data, zod.z.array(userReportSchema));
1626
+ return this.request(
1627
+ () => this.http.get(
1628
+ `api/projects/${projectId}/issues/${issueId}/user-reports`
1629
+ ),
1630
+ zod.z.array(userReportSchema)
1631
+ );
1256
1632
  }
1257
1633
  /**
1258
1634
  * Attach a user feedback report to an issue.
1259
1635
  */
1260
1636
  async createUserReport(projectId, issueId, input) {
1261
- const validatedInput = this.validate(input, createUserReportSchema);
1262
- const data = await this.http.post(`api/projects/${projectId}/issues/${issueId}/user-reports`, {
1263
- json: validatedInput
1264
- }).json();
1265
- return this.validate(data, userReportSchema);
1637
+ const validatedInput = this.validateInput(input, createUserReportSchema);
1638
+ if (!validatedInput.success) {
1639
+ return validatedInput;
1640
+ }
1641
+ return this.request(
1642
+ () => this.http.post(
1643
+ `api/projects/${projectId}/issues/${issueId}/user-reports`,
1644
+ { json: validatedInput.data }
1645
+ ),
1646
+ userReportSchema
1647
+ );
1266
1648
  }
1267
1649
  };
1268
1650
 
@@ -1286,8 +1668,10 @@ var LogsResource = class extends BaseResource {
1286
1668
  if (options?.trace_id) {
1287
1669
  searchParams.trace_id = options.trace_id;
1288
1670
  }
1289
- const data = await this.http.get(`api/projects/${projectId}/logs`, { searchParams }).json();
1290
- return this.validate(data, offsetPaginatedResponseSchema(logSchema));
1671
+ return this.request(
1672
+ () => this.http.get(`api/projects/${projectId}/logs`, { searchParams }),
1673
+ offsetPaginatedResponseSchema(logSchema)
1674
+ );
1291
1675
  }
1292
1676
  };
1293
1677
 
@@ -1298,8 +1682,10 @@ var MembersResource = class extends BaseResource {
1298
1682
  * @param projectId - ID of the project
1299
1683
  */
1300
1684
  async list(projectId) {
1301
- const data = await this.http.get(`api/projects/${projectId}/members`).json();
1302
- return this.validate(data, projectMemberSchema.array());
1685
+ return this.request(
1686
+ () => this.http.get(`api/projects/${projectId}/members`),
1687
+ projectMemberSchema.array()
1688
+ );
1303
1689
  }
1304
1690
  /**
1305
1691
  * Add or update a project member
@@ -1307,10 +1693,15 @@ var MembersResource = class extends BaseResource {
1307
1693
  * @param input - User ID and per-project role
1308
1694
  */
1309
1695
  async upsert(projectId, input) {
1310
- const validatedInput = this.validate(input, upsertProjectMemberSchema);
1311
- await this.http.put(`api/projects/${projectId}/members`, {
1312
- json: validatedInput
1313
- });
1696
+ const validatedInput = this.validateInput(input, upsertProjectMemberSchema);
1697
+ if (!validatedInput.success) {
1698
+ return validatedInput;
1699
+ }
1700
+ return this.requestVoid(
1701
+ () => this.http.put(`api/projects/${projectId}/members`, {
1702
+ json: validatedInput.data
1703
+ })
1704
+ );
1314
1705
  }
1315
1706
  /**
1316
1707
  * Remove a member from a project
@@ -1318,7 +1709,9 @@ var MembersResource = class extends BaseResource {
1318
1709
  * @param userId - ID of the user to remove
1319
1710
  */
1320
1711
  async remove(projectId, userId) {
1321
- await this.http.delete(`api/projects/${projectId}/members/${userId}`);
1712
+ return this.requestVoid(
1713
+ () => this.http.delete(`api/projects/${projectId}/members/${userId}`)
1714
+ );
1322
1715
  }
1323
1716
  };
1324
1717
 
@@ -1343,37 +1736,51 @@ var ProjectsResource = class extends BaseResource {
1343
1736
  }
1344
1737
  const query = searchParams.toString();
1345
1738
  const url = query ? `api/projects?${query}` : "api/projects";
1346
- const data = await this.http.get(url).json();
1347
- return this.validate(data, offsetPaginatedResponseSchema(projectSchema));
1739
+ return this.request(
1740
+ () => this.http.get(url),
1741
+ offsetPaginatedResponseSchema(projectSchema)
1742
+ );
1348
1743
  }
1349
1744
  /**
1350
1745
  * Get a single project by ID
1351
1746
  */
1352
1747
  async get(id) {
1353
- const data = await this.http.get(`api/projects/${id}`).json();
1354
- return this.validate(data, projectSchema);
1748
+ return this.request(
1749
+ () => this.http.get(`api/projects/${id}`),
1750
+ projectSchema
1751
+ );
1355
1752
  }
1356
1753
  /**
1357
1754
  * Create a new project
1358
1755
  */
1359
1756
  async create(input) {
1360
- const validatedInput = this.validate(input, createProjectSchema);
1361
- const data = await this.http.post("api/projects", { json: validatedInput }).json();
1362
- return this.validate(data, projectSchema);
1757
+ const validatedInput = this.validateInput(input, createProjectSchema);
1758
+ if (!validatedInput.success) {
1759
+ return validatedInput;
1760
+ }
1761
+ return this.request(
1762
+ () => this.http.post("api/projects", { json: validatedInput.data }),
1763
+ projectSchema
1764
+ );
1363
1765
  }
1364
1766
  /**
1365
1767
  * Update an existing project
1366
1768
  */
1367
1769
  async update(id, input) {
1368
- const validatedInput = this.validate(input, updateProjectSchema);
1369
- const data = await this.http.patch(`api/projects/${id}`, { json: validatedInput }).json();
1370
- return this.validate(data, projectSchema);
1770
+ const validatedInput = this.validateInput(input, updateProjectSchema);
1771
+ if (!validatedInput.success) {
1772
+ return validatedInput;
1773
+ }
1774
+ return this.request(
1775
+ () => this.http.patch(`api/projects/${id}`, { json: validatedInput.data }),
1776
+ projectSchema
1777
+ );
1371
1778
  }
1372
1779
  /**
1373
1780
  * Delete a project
1374
1781
  */
1375
1782
  async delete(id) {
1376
- await this.http.delete(`api/projects/${id}`);
1783
+ return this.requestVoid(() => this.http.delete(`api/projects/${id}`));
1377
1784
  }
1378
1785
  };
1379
1786
  var ReleasesResource = class extends BaseResource {
@@ -1389,11 +1796,13 @@ var ReleasesResource = class extends BaseResource {
1389
1796
  if (limit !== void 0) {
1390
1797
  searchParams.limit = limit.toString();
1391
1798
  }
1392
- const data = await this.http.get(
1393
- `api/projects/${projectId}/releases/${encodeURIComponent(release)}/new-issues`,
1394
- { searchParams }
1395
- ).json();
1396
- return this.validate(data, zod.z.array(issueSchema));
1799
+ return this.request(
1800
+ () => this.http.get(
1801
+ `api/projects/${projectId}/releases/${encodeURIComponent(release)}/new-issues`,
1802
+ { searchParams }
1803
+ ),
1804
+ zod.z.array(issueSchema)
1805
+ );
1397
1806
  }
1398
1807
  };
1399
1808
 
@@ -1419,9 +1828,10 @@ var SessionsResource = class extends BaseResource {
1419
1828
  if (options?.per_page) {
1420
1829
  searchParams.per_page = String(options.per_page);
1421
1830
  }
1422
- const data = await this.http.get(`api/projects/${projectId}/sessions/stats`, { searchParams }).json();
1423
- return this.validate(
1424
- data,
1831
+ return this.request(
1832
+ () => this.http.get(`api/projects/${projectId}/sessions/stats`, {
1833
+ searchParams
1834
+ }),
1425
1835
  offsetPaginatedResponseSchema(releaseHealthRowSchema)
1426
1836
  );
1427
1837
  }
@@ -1435,8 +1845,12 @@ var SessionsResource = class extends BaseResource {
1435
1845
  if (period) {
1436
1846
  searchParams.period = period;
1437
1847
  }
1438
- const data = await this.http.get(`api/projects/${projectId}/sessions/summary`, { searchParams }).json();
1439
- return this.validate(data, sessionSummarySchema);
1848
+ return this.request(
1849
+ () => this.http.get(`api/projects/${projectId}/sessions/summary`, {
1850
+ searchParams
1851
+ }),
1852
+ sessionSummarySchema
1853
+ );
1440
1854
  }
1441
1855
  /**
1442
1856
  * Get a time-bucketed session trend for a project, aggregated across all
@@ -1453,8 +1867,12 @@ var SessionsResource = class extends BaseResource {
1453
1867
  if (interval !== void 0) {
1454
1868
  searchParams.interval = interval.toString();
1455
1869
  }
1456
- const data = await this.http.get(`api/projects/${projectId}/sessions/timeseries`, { searchParams }).json();
1457
- return this.validate(data, sessionTimeseriesSchema);
1870
+ return this.request(
1871
+ () => this.http.get(`api/projects/${projectId}/sessions/timeseries`, {
1872
+ searchParams
1873
+ }),
1874
+ sessionTimeseriesSchema
1875
+ );
1458
1876
  }
1459
1877
  };
1460
1878
 
@@ -1465,8 +1883,10 @@ var SourceMapsResource = class extends BaseResource {
1465
1883
  * Returns the upload URL, chunk size limits, and accepted artifact types.
1466
1884
  */
1467
1885
  async getChunkUploadCapability(orgSlug) {
1468
- const data = await this.http.get(`api/0/organizations/${orgSlug}/chunk-upload/`).json();
1469
- return this.validate(data, chunkUploadCapabilitySchema);
1886
+ return this.request(
1887
+ () => this.http.get(`api/0/organizations/${orgSlug}/chunk-upload/`),
1888
+ chunkUploadCapabilitySchema
1889
+ );
1470
1890
  }
1471
1891
  /**
1472
1892
  * Upload chunks as multipart/form-data, batching by `chunksPerRequest` (default 64).
@@ -1475,7 +1895,10 @@ var SourceMapsResource = class extends BaseResource {
1475
1895
  */
1476
1896
  async uploadChunks(orgSlug, chunks, chunksPerRequest = 64) {
1477
1897
  if (!Number.isInteger(chunksPerRequest) || chunksPerRequest <= 0) {
1478
- throw new Error("chunksPerRequest must be a positive integer");
1898
+ return Err({
1899
+ kind: "invalid_request",
1900
+ message: "chunksPerRequest must be a positive integer."
1901
+ });
1479
1902
  }
1480
1903
  for (let i = 0; i < chunks.length; i += chunksPerRequest) {
1481
1904
  const batch = chunks.slice(i, i + chunksPerRequest);
@@ -1483,10 +1906,16 @@ var SourceMapsResource = class extends BaseResource {
1483
1906
  for (const chunk of batch) {
1484
1907
  form.append(chunk.hash, chunk.data);
1485
1908
  }
1486
- await this.http.post(`api/0/organizations/${orgSlug}/chunk-upload/`, {
1487
- body: form
1488
- });
1909
+ const uploaded = await this.requestVoid(
1910
+ () => this.http.post(`api/0/organizations/${orgSlug}/chunk-upload/`, {
1911
+ body: form
1912
+ })
1913
+ );
1914
+ if (!uploaded.success) {
1915
+ return uploaded;
1916
+ }
1489
1917
  }
1918
+ return Ok(void 0);
1490
1919
  }
1491
1920
  /**
1492
1921
  * Trigger assembly of a previously uploaded artifact bundle.
@@ -1496,17 +1925,28 @@ var SourceMapsResource = class extends BaseResource {
1496
1925
  * still needs uploading via `uploadChunks`.
1497
1926
  */
1498
1927
  async assembleBundle(orgSlug, input) {
1499
- const data = await this.http.post(`api/0/organizations/${orgSlug}/artifactbundle/assemble/`, {
1500
- json: input
1501
- }).json();
1502
- return this.validate(data, assembleResponseSchema);
1928
+ const validatedInput = this.validateInput(input, assembleInputSchema);
1929
+ if (!validatedInput.success) {
1930
+ return validatedInput;
1931
+ }
1932
+ return this.request(
1933
+ () => this.http.post(
1934
+ `api/0/organizations/${orgSlug}/artifactbundle/assemble/`,
1935
+ { json: validatedInput.data }
1936
+ ),
1937
+ assembleResponseSchema
1938
+ );
1503
1939
  }
1504
1940
  /**
1505
1941
  * List all source map files uploaded for a project.
1506
1942
  */
1507
1943
  async list(orgSlug, projectSlug) {
1508
- const data = await this.http.get(`api/0/projects/${orgSlug}/${projectSlug}/files/source-maps/`).json();
1509
- return this.validate(data, listSourceMapsResponseSchema);
1944
+ return this.request(
1945
+ () => this.http.get(
1946
+ `api/0/projects/${orgSlug}/${projectSlug}/files/source-maps/`
1947
+ ),
1948
+ listSourceMapsResponseSchema
1949
+ );
1510
1950
  }
1511
1951
  };
1512
1952
 
@@ -1536,8 +1976,10 @@ var SpansResource = class extends BaseResource {
1536
1976
  if (options?.operation_type) {
1537
1977
  searchParams.operation_type = options.operation_type;
1538
1978
  }
1539
- const data = await this.http.get(`api/projects/${projectId}/spans`, { searchParams }).json();
1540
- return this.validate(data, offsetPaginatedResponseSchema(spanSchema));
1979
+ return this.request(
1980
+ () => this.http.get(`api/projects/${projectId}/spans`, { searchParams }),
1981
+ offsetPaginatedResponseSchema(spanSchema)
1982
+ );
1541
1983
  }
1542
1984
  };
1543
1985
 
@@ -1562,8 +2004,12 @@ var StatsResource = class extends BaseResource {
1562
2004
  if (interval !== void 0) {
1563
2005
  searchParams.interval = interval.toString();
1564
2006
  }
1565
- const data = await this.http.get(`api/projects/${projectId}/events/stats`, { searchParams }).json();
1566
- return this.validate(data, eventTimeseriesSchema);
2007
+ return this.request(
2008
+ () => this.http.get(`api/projects/${projectId}/events/stats`, {
2009
+ searchParams
2010
+ }),
2011
+ eventTimeseriesSchema
2012
+ );
1567
2013
  }
1568
2014
  /**
1569
2015
  * Get project-wide counters for a window, each paired with the same counter
@@ -1578,8 +2024,12 @@ var StatsResource = class extends BaseResource {
1578
2024
  if (period) {
1579
2025
  searchParams.period = period;
1580
2026
  }
1581
- const data = await this.http.get(`api/projects/${projectId}/stats/summary`, { searchParams }).json();
1582
- return this.validate(data, projectStatsSummarySchema);
2027
+ return this.request(
2028
+ () => this.http.get(`api/projects/${projectId}/stats/summary`, {
2029
+ searchParams
2030
+ }),
2031
+ projectStatsSummarySchema
2032
+ );
1583
2033
  }
1584
2034
  };
1585
2035
  var StorageResource = class extends BaseResource {
@@ -1588,42 +2038,60 @@ var StorageResource = class extends BaseResource {
1588
2038
  * whole-DB size, and exact source-map weight.
1589
2039
  */
1590
2040
  async getSummary() {
1591
- const data = await this.http.get("api/storage/summary").json();
1592
- return this.validate(data, storageSummarySchema);
2041
+ return this.request(
2042
+ () => this.http.get("api/storage/summary"),
2043
+ storageSummarySchema
2044
+ );
1593
2045
  }
1594
2046
  /**
1595
2047
  * Get the per-project storage breakdown (one row per project, including
1596
2048
  * empty ones).
1597
2049
  */
1598
2050
  async getProjects() {
1599
- const data = await this.http.get("api/storage/projects").json();
1600
- return this.validate(data, zod.z.array(projectStorageSchema));
2051
+ return this.request(
2052
+ () => this.http.get("api/storage/projects"),
2053
+ zod.z.array(projectStorageSchema)
2054
+ );
1601
2055
  }
1602
2056
  /**
1603
2057
  * Dry-run: count the rows a cleanup would remove. Mutates nothing — use it to
1604
2058
  * confirm impact before {@link executeCleanup}.
1605
2059
  */
1606
2060
  async previewCleanup(options) {
1607
- const json = cleanupOptionsSchema.parse(options);
1608
- const data = await this.http.post("api/storage/cleanup/preview", { json }).json();
1609
- return this.validate(data, cleanupCountsSchema);
2061
+ const validatedInput = this.validateInput(options, cleanupOptionsSchema);
2062
+ if (!validatedInput.success) {
2063
+ return validatedInput;
2064
+ }
2065
+ return this.request(
2066
+ () => this.http.post("api/storage/cleanup/preview", {
2067
+ json: validatedInput.data
2068
+ }),
2069
+ cleanupCountsSchema
2070
+ );
1610
2071
  }
1611
2072
  /**
1612
2073
  * Execute a cleanup: delete data older than `older_than_days` (optionally
1613
2074
  * scoped to one project) and remove any issue left with zero events.
1614
2075
  */
1615
2076
  async executeCleanup(options) {
1616
- const json = cleanupOptionsSchema.parse(options);
1617
- const data = await this.http.post("api/storage/cleanup", { json }).json();
1618
- return this.validate(data, cleanupCountsSchema);
2077
+ const validatedInput = this.validateInput(options, cleanupOptionsSchema);
2078
+ if (!validatedInput.success) {
2079
+ return validatedInput;
2080
+ }
2081
+ return this.request(
2082
+ () => this.http.post("api/storage/cleanup", { json: validatedInput.data }),
2083
+ cleanupCountsSchema
2084
+ );
1619
2085
  }
1620
2086
  /**
1621
2087
  * Dry-run for {@link gcSourceMaps}: count the orphaned source-map files and
1622
2088
  * bytes a GC would reclaim. Mutates nothing.
1623
2089
  */
1624
2090
  async previewGcSourceMaps() {
1625
- const data = await this.http.post("api/storage/source-maps/gc/preview").json();
1626
- return this.validate(data, sourceMapGcResultSchema);
2091
+ return this.request(
2092
+ () => this.http.post("api/storage/source-maps/gc/preview"),
2093
+ sourceMapGcResultSchema
2094
+ );
1627
2095
  }
1628
2096
  /**
1629
2097
  * Garbage-collect orphaned source maps: files no longer referenced by any
@@ -1631,8 +2099,10 @@ var StorageResource = class extends BaseResource {
1631
2099
  * referenced files.
1632
2100
  */
1633
2101
  async gcSourceMaps() {
1634
- const data = await this.http.post("api/storage/source-maps/gc").json();
1635
- return this.validate(data, sourceMapGcResultSchema);
2102
+ return this.request(
2103
+ () => this.http.post("api/storage/source-maps/gc"),
2104
+ sourceMapGcResultSchema
2105
+ );
1636
2106
  }
1637
2107
  };
1638
2108
 
@@ -1642,8 +2112,10 @@ var TeamResource = class extends BaseResource {
1642
2112
  * List all team members (users)
1643
2113
  */
1644
2114
  async list() {
1645
- const data = await this.http.get("api/team").json();
1646
- return this.validate(data, teamMemberSchema.array());
2115
+ return this.request(
2116
+ () => this.http.get("api/team"),
2117
+ teamMemberSchema.array()
2118
+ );
1647
2119
  }
1648
2120
  /**
1649
2121
  * Change a user's global role
@@ -1651,15 +2123,20 @@ var TeamResource = class extends BaseResource {
1651
2123
  * @param role - New global role to assign
1652
2124
  */
1653
2125
  async updateRole(userId, role) {
1654
- const body = this.validate({ role }, updateUserRoleSchema);
1655
- await this.http.patch(`api/team/${userId}/role`, { json: body });
2126
+ const body = this.validateInput({ role }, updateUserRoleSchema);
2127
+ if (!body.success) {
2128
+ return body;
2129
+ }
2130
+ return this.requestVoid(
2131
+ () => this.http.patch(`api/team/${userId}/role`, { json: body.data })
2132
+ );
1656
2133
  }
1657
2134
  /**
1658
2135
  * Permanently remove a user from the instance.
1659
2136
  * @param userId - ID of the user to delete
1660
2137
  */
1661
2138
  async remove(userId) {
1662
- await this.http.delete(`api/team/${userId}`);
2139
+ return this.requestVoid(() => this.http.delete(`api/team/${userId}`));
1663
2140
  }
1664
2141
  };
1665
2142
 
@@ -1669,8 +2146,10 @@ var TokensResource = class extends BaseResource {
1669
2146
  * List all auth tokens (masked)
1670
2147
  */
1671
2148
  async list() {
1672
- const data = await this.http.get("api/tokens").json();
1673
- return this.validate(data, authTokenSchema.array());
2149
+ return this.request(
2150
+ () => this.http.get("api/tokens"),
2151
+ authTokenSchema.array()
2152
+ );
1674
2153
  }
1675
2154
  /**
1676
2155
  * Get a single auth token by ID (full token value).
@@ -1679,23 +2158,30 @@ var TokensResource = class extends BaseResource {
1679
2158
  * complete token for GET /api/tokens/{id}.
1680
2159
  */
1681
2160
  async get(id) {
1682
- const data = await this.http.get(`api/tokens/${id}`).json();
1683
- return this.validate(data, authTokenCreatedSchema);
2161
+ return this.request(
2162
+ () => this.http.get(`api/tokens/${id}`),
2163
+ authTokenCreatedSchema
2164
+ );
1684
2165
  }
1685
2166
  /**
1686
2167
  * Create a new auth token
1687
2168
  * Note: The full token is only returned once during creation
1688
2169
  */
1689
2170
  async create(input) {
1690
- const validatedInput = this.validate(input, createAuthTokenSchema);
1691
- const data = await this.http.post("api/tokens", { json: validatedInput }).json();
1692
- return this.validate(data, authTokenCreatedSchema);
2171
+ const validatedInput = this.validateInput(input, createAuthTokenSchema);
2172
+ if (!validatedInput.success) {
2173
+ return validatedInput;
2174
+ }
2175
+ return this.request(
2176
+ () => this.http.post("api/tokens", { json: validatedInput.data }),
2177
+ authTokenCreatedSchema
2178
+ );
1693
2179
  }
1694
2180
  /**
1695
2181
  * Delete an auth token
1696
2182
  */
1697
2183
  async delete(id) {
1698
- await this.http.delete(`api/tokens/${id}`);
2184
+ return this.requestVoid(() => this.http.delete(`api/tokens/${id}`));
1699
2185
  }
1700
2186
  };
1701
2187
  var TransactionsResource = class extends BaseResource {
@@ -1725,11 +2211,10 @@ var TransactionsResource = class extends BaseResource {
1725
2211
  if (options?.release) {
1726
2212
  searchParams.release = options.release;
1727
2213
  }
1728
- const data = await this.http.get(`api/projects/${projectId}/transactions`, {
1729
- searchParams
1730
- }).json();
1731
- return this.validate(
1732
- data,
2214
+ return this.request(
2215
+ () => this.http.get(`api/projects/${projectId}/transactions`, {
2216
+ searchParams
2217
+ }),
1733
2218
  offsetPaginatedResponseSchema(transactionSchema)
1734
2219
  );
1735
2220
  }
@@ -1737,8 +2222,12 @@ var TransactionsResource = class extends BaseResource {
1737
2222
  * Get the indexed spans extracted from a transaction, in waterfall order.
1738
2223
  */
1739
2224
  async getSpans(projectId, transactionId) {
1740
- const data = await this.http.get(`api/projects/${projectId}/transactions/${transactionId}/spans`).json();
1741
- return this.validate(data, zod.z.array(spanSchema));
2225
+ return this.request(
2226
+ () => this.http.get(
2227
+ `api/projects/${projectId}/transactions/${transactionId}/spans`
2228
+ ),
2229
+ zod.z.array(spanSchema)
2230
+ );
1742
2231
  }
1743
2232
  /**
1744
2233
  * Get aggregate performance stats (count, p50/p95/p99 latency, failure rate)
@@ -1753,100 +2242,43 @@ var TransactionsResource = class extends BaseResource {
1753
2242
  if (options?.per_page) {
1754
2243
  searchParams.per_page = String(options.per_page);
1755
2244
  }
1756
- const data = await this.http.get(`api/projects/${projectId}/transactions/stats`, { searchParams }).json();
1757
- return this.validate(
1758
- data,
2245
+ return this.request(
2246
+ () => this.http.get(`api/projects/${projectId}/transactions/stats`, {
2247
+ searchParams
2248
+ }),
1759
2249
  offsetPaginatedResponseSchema(transactionStatsSchema)
1760
2250
  );
1761
2251
  }
1762
2252
  /**
1763
2253
  * Get aggregate stats for a single (transaction name, op) group — a direct
1764
- * lookup that works regardless of how many groups the project has. Throws
1765
- * NotFoundError when the group has no transactions.
2254
+ * lookup that works regardless of how many groups the project has. Reports
2255
+ * `not_found` when the group has no transactions.
1766
2256
  */
1767
2257
  async getStatForGroup(projectId, name, op) {
1768
2258
  const searchParams = { name };
1769
2259
  if (op) {
1770
2260
  searchParams.op = op;
1771
2261
  }
1772
- const data = await this.http.get(`api/projects/${projectId}/transactions/stats/group`, {
1773
- searchParams
1774
- }).json();
1775
- return this.validate(data, transactionStatsSchema);
2262
+ return this.request(
2263
+ () => this.http.get(`api/projects/${projectId}/transactions/stats/group`, {
2264
+ searchParams
2265
+ }),
2266
+ transactionStatsSchema
2267
+ );
1776
2268
  }
1777
2269
  /**
1778
2270
  * Get a single transaction by ID with its full Sentry payload (spans,
1779
2271
  * contexts.trace, measurements, tags) for the performance detail view.
1780
2272
  */
1781
2273
  async get(projectId, transactionId) {
1782
- const data = await this.http.get(`api/projects/${projectId}/transactions/${transactionId}`).json();
1783
- return this.validate(data, transactionDetailSchema);
2274
+ return this.request(
2275
+ () => this.http.get(
2276
+ `api/projects/${projectId}/transactions/${transactionId}`
2277
+ ),
2278
+ transactionDetailSchema
2279
+ );
1784
2280
  }
1785
2281
  };
1786
- function transformHttpError(error) {
1787
- const { response } = error;
1788
- const status = response.status;
1789
- let errorMessage = `HTTP ${status} error`;
1790
- const body = error.data;
1791
- if (body) {
1792
- const nested = typeof body.error === "object" ? body.error?.message : body.error;
1793
- errorMessage = nested || body.message || errorMessage;
1794
- }
1795
- switch (status) {
1796
- case 400:
1797
- return new BadRequestError(errorMessage);
1798
- case 401:
1799
- return new AuthenticationError(errorMessage);
1800
- case 403:
1801
- return new AuthorizationError(errorMessage);
1802
- case 404:
1803
- return new NotFoundError(errorMessage);
1804
- case 429: {
1805
- const retryAfter = response.headers.get("Retry-After");
1806
- return new RateLimitError(errorMessage, retryAfter ?? void 0);
1807
- }
1808
- case 500:
1809
- case 502:
1810
- case 503:
1811
- case 504:
1812
- return new ServerError(errorMessage, status);
1813
- default:
1814
- return new RustrakError(errorMessage, { statusCode: status });
1815
- }
1816
- }
1817
- function createKyInstance(config) {
1818
- const headers = {
1819
- "Content-Type": "application/json",
1820
- ...config.headers
1821
- };
1822
- if (config.token) {
1823
- headers.Authorization = `Bearer ${config.token}`;
1824
- }
1825
- return ky__default.default.create({
1826
- prefix: config.baseUrl,
1827
- timeout: config.timeout ?? 3e4,
1828
- credentials: "include",
1829
- retry: {
1830
- limit: config.maxRetries ?? 2,
1831
- statusCodes: [408, 500, 502, 503, 504],
1832
- methods: ["get", "post", "put", "patch", "delete"]
1833
- },
1834
- headers,
1835
- hooks: {
1836
- beforeError: [
1837
- ({ error }) => {
1838
- if (error.name === "TimeoutError") {
1839
- throw new NetworkError("Request timed out", error);
1840
- }
1841
- if (ky.isHTTPError(error)) {
1842
- throw transformHttpError(error);
1843
- }
1844
- throw new NetworkError(error.message, error);
1845
- }
1846
- ]
1847
- }
1848
- });
1849
- }
1850
2282
 
1851
2283
  // src/client.ts
1852
2284
  var RustrakClient = class {
@@ -1961,15 +2393,16 @@ var RustrakClient = class {
1961
2393
  }
1962
2394
  };
1963
2395
 
1964
- exports.AuthenticationError = AuthenticationError;
1965
- exports.AuthorizationError = AuthorizationError;
1966
- exports.BadRequestError = BadRequestError;
1967
- exports.NetworkError = NetworkError;
1968
- exports.NotFoundError = NotFoundError;
1969
- exports.RateLimitError = RateLimitError;
2396
+ exports.Err = Err;
2397
+ exports.FIELD_ERROR_CODES = FIELD_ERROR_CODES;
2398
+ exports.NETWORK_ERROR_MESSAGE = NETWORK_ERROR_MESSAGE;
2399
+ exports.Ok = Ok;
1970
2400
  exports.RustrakClient = RustrakClient;
1971
- exports.RustrakError = RustrakError;
1972
- exports.ServerError = ServerError;
1973
- exports.ValidationError = ValidationError;
2401
+ exports.SERVER_ERROR_MESSAGE = SERVER_ERROR_MESSAGE;
2402
+ exports.TIMEOUT_ERROR_MESSAGE = TIMEOUT_ERROR_MESSAGE;
2403
+ exports.isRetryable = isRetryable;
2404
+ exports.mapResult = mapResult;
2405
+ exports.unwrap = unwrap;
2406
+ exports.unwrapOr = unwrapOr;
1974
2407
  //# sourceMappingURL=index.cjs.map
1975
2408
  //# sourceMappingURL=index.cjs.map