@rustrak/client 0.13.0 → 0.14.1

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