@rustrak/client 0.12.3 → 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/README.md +123 -37
- package/dist/index.cjs +897 -422
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +225 -144
- package/dist/index.d.ts +225 -144
- package/dist/index.js +888 -414
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -357,6 +357,28 @@ var upsertProjectMemberSchema = z.object({
|
|
|
357
357
|
user_id: z.number().int(),
|
|
358
358
|
role: projectRoleSchema
|
|
359
359
|
});
|
|
360
|
+
var metricDeltaSchema = z.object({
|
|
361
|
+
current: z.number().int(),
|
|
362
|
+
/**
|
|
363
|
+
* The same window immediately before this one. Null only for all-time
|
|
364
|
+
* requests, where a `0` would render as a misleading "+100%".
|
|
365
|
+
*/
|
|
366
|
+
previous: z.number().int().nullable()
|
|
367
|
+
});
|
|
368
|
+
var projectListStatsSchema = z.object({
|
|
369
|
+
/**
|
|
370
|
+
* Distinct issues active in each bucket, oldest first. A shape, not a
|
|
371
|
+
* total: an issue firing all day appears in every bucket it touched.
|
|
372
|
+
*/
|
|
373
|
+
trend: z.array(z.number().int()),
|
|
374
|
+
events: metricDeltaSchema,
|
|
375
|
+
/** Issues first seen in the window. Drives the "getting worse" signal. */
|
|
376
|
+
new_issues: metricDeltaSchema,
|
|
377
|
+
/** Unresolved issues right now, independent of the window. */
|
|
378
|
+
open_issues: z.number().int(),
|
|
379
|
+
/** The subset of `open_issues` at `fatal` level. */
|
|
380
|
+
fatal_issues: z.number().int()
|
|
381
|
+
});
|
|
360
382
|
var projectSchema = z.object({
|
|
361
383
|
id: z.number().int(),
|
|
362
384
|
name: z.string(),
|
|
@@ -367,15 +389,31 @@ var projectSchema = z.object({
|
|
|
367
389
|
digested_event_count: z.number().int(),
|
|
368
390
|
created_at: dateTimeSchema,
|
|
369
391
|
updated_at: dateTimeSchema,
|
|
370
|
-
platform: z.string().nullable()
|
|
392
|
+
platform: z.string().nullable(),
|
|
393
|
+
stats: projectListStatsSchema.optional()
|
|
371
394
|
});
|
|
372
395
|
var createProjectSchema = z.object({
|
|
373
396
|
name: z.string().min(1),
|
|
374
|
-
|
|
397
|
+
/**
|
|
398
|
+
* Slugified server-side. Supplying it means the user chose it, so a taken
|
|
399
|
+
* slug is a 409. Omitting it derives one from the name and silently
|
|
400
|
+
* de-duplicates, since nobody chose that value.
|
|
401
|
+
*/
|
|
402
|
+
slug: z.string().min(1).optional(),
|
|
403
|
+
/**
|
|
404
|
+
* Validated server-side against SELECTABLE_PLATFORMS. Omitting it leaves the
|
|
405
|
+
* project eligible for platform auto-detection from its first event.
|
|
406
|
+
*/
|
|
407
|
+
platform: z.string().min(1).optional()
|
|
375
408
|
});
|
|
376
409
|
var updateProjectSchema = z.object({
|
|
377
410
|
name: z.string().min(1).optional(),
|
|
378
|
-
platform: z.string().min(1).optional()
|
|
411
|
+
platform: z.string().min(1).optional(),
|
|
412
|
+
/**
|
|
413
|
+
* Slugified server-side. Unlike on create, a taken slug is a 409 rather than
|
|
414
|
+
* being silently de-duplicated.
|
|
415
|
+
*/
|
|
416
|
+
slug: z.string().min(1).optional()
|
|
379
417
|
});
|
|
380
418
|
var releaseHealthRowSchema = z.object({
|
|
381
419
|
release: z.string(),
|
|
@@ -413,6 +451,11 @@ var chunkUploadCapabilitySchema = z.object({
|
|
|
413
451
|
accept: z.array(z.string()),
|
|
414
452
|
concurrency: z.number().int().optional()
|
|
415
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
|
+
});
|
|
416
459
|
var assembleResponseSchema = z.object({
|
|
417
460
|
state: z.string(),
|
|
418
461
|
missingChunks: z.array(z.string()),
|
|
@@ -437,15 +480,15 @@ var eventTimeseriesPointSchema = z.object({
|
|
|
437
480
|
info: z.number().int()
|
|
438
481
|
});
|
|
439
482
|
var eventTimeseriesSchema = z.array(eventTimeseriesPointSchema);
|
|
440
|
-
var
|
|
483
|
+
var metricDeltaSchema2 = z.object({
|
|
441
484
|
current: z.number().int(),
|
|
442
485
|
previous: z.number().int().nullable()
|
|
443
486
|
});
|
|
444
487
|
var projectStatsSummarySchema = z.object({
|
|
445
488
|
/** Resolved window in hours, or `null` for all time. */
|
|
446
489
|
period_hours: z.number().int().nullable(),
|
|
447
|
-
events:
|
|
448
|
-
new_issues:
|
|
490
|
+
events: metricDeltaSchema2,
|
|
491
|
+
new_issues: metricDeltaSchema2,
|
|
449
492
|
/** Currently unresolved issues, independent of the window. */
|
|
450
493
|
open_issues: z.number().int()
|
|
451
494
|
});
|
|
@@ -595,117 +638,330 @@ var registerRequestSchema = z.object({
|
|
|
595
638
|
password: z.string().min(1)
|
|
596
639
|
});
|
|
597
640
|
|
|
598
|
-
// src/errors
|
|
599
|
-
var
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
super(message);
|
|
614
|
-
this.name = this.constructor.name;
|
|
615
|
-
this.retryable = options?.retryable ?? false;
|
|
616
|
-
this.statusCode = options?.statusCode;
|
|
617
|
-
this.cause = options?.cause;
|
|
618
|
-
if (Error.captureStackTrace) {
|
|
619
|
-
Error.captureStackTrace(this, this.constructor);
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
};
|
|
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
|
+
}
|
|
623
656
|
|
|
624
|
-
// src/
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
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;
|
|
628
667
|
}
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
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;
|
|
633
681
|
}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
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;
|
|
638
690
|
}
|
|
639
691
|
};
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
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;
|
|
646
701
|
}
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
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;
|
|
657
730
|
}
|
|
731
|
+
fields.push(
|
|
732
|
+
code === "custom" && typeof message === "string" ? { field, code, message } : { field, code }
|
|
733
|
+
);
|
|
658
734
|
}
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
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;
|
|
663
741
|
}
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
constructor(message) {
|
|
667
|
-
super(message, { retryable: false, statusCode: 400 });
|
|
742
|
+
if (/^\d+$/.test(header)) {
|
|
743
|
+
return Number.parseInt(header, 10);
|
|
668
744
|
}
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
// src/errors/validation.ts
|
|
672
|
-
var ValidationError = class extends RustrakError {
|
|
673
|
-
/**
|
|
674
|
-
* Zod validation errors
|
|
675
|
-
*/
|
|
676
|
-
validationErrors;
|
|
677
|
-
constructor(message, validationErrors) {
|
|
678
|
-
super(message, { retryable: false });
|
|
679
|
-
this.validationErrors = validationErrors;
|
|
745
|
+
if (/^[+-]\d+$/.test(header)) {
|
|
746
|
+
return void 0;
|
|
680
747
|
}
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
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
|
+
);
|
|
687
786
|
}
|
|
688
|
-
|
|
689
|
-
|
|
787
|
+
default:
|
|
788
|
+
return annotate({ kind: "client_error", status, message }, fields);
|
|
690
789
|
}
|
|
691
|
-
}
|
|
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
|
+
}
|
|
692
838
|
|
|
693
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
|
+
}
|
|
694
876
|
var BaseResource = class {
|
|
695
877
|
http;
|
|
696
878
|
constructor(http) {
|
|
697
879
|
this.http = http;
|
|
698
880
|
}
|
|
699
881
|
/**
|
|
700
|
-
* Validate API response against Zod schema
|
|
701
|
-
*
|
|
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.
|
|
702
887
|
*/
|
|
703
888
|
validate(data, schema) {
|
|
704
889
|
const result = schema.safeParse(data);
|
|
705
890
|
if (!result.success) {
|
|
706
|
-
|
|
891
|
+
return Err({
|
|
892
|
+
kind: "invalid_response",
|
|
893
|
+
message: "The server response did not match the expected schema."
|
|
894
|
+
});
|
|
707
895
|
}
|
|
708
|
-
return result.data;
|
|
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
|
+
});
|
|
910
|
+
}
|
|
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
|
+
});
|
|
709
965
|
}
|
|
710
966
|
};
|
|
711
967
|
|
|
@@ -735,46 +991,56 @@ var AgentsResource = class extends BaseResource {
|
|
|
735
991
|
* Time-bucketed count of agent-run spans (`gen_ai.operation.type:agent`).
|
|
736
992
|
*/
|
|
737
993
|
async getRuns(projectId, options) {
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
994
|
+
return this.request(
|
|
995
|
+
() => this.http.get(`api/projects/${projectId}/agents/runs`, {
|
|
996
|
+
searchParams: timeseriesSearchParams(options)
|
|
997
|
+
}),
|
|
998
|
+
z.array(agentTimeseriesPointSchema)
|
|
999
|
+
);
|
|
742
1000
|
}
|
|
743
1001
|
/**
|
|
744
1002
|
* Time-bucketed avg/p95 duration for `agent`/`ai_client` spans.
|
|
745
1003
|
*/
|
|
746
1004
|
async getDuration(projectId, options) {
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
1005
|
+
return this.request(
|
|
1006
|
+
() => this.http.get(`api/projects/${projectId}/agents/duration`, {
|
|
1007
|
+
searchParams: timeseriesSearchParams(options)
|
|
1008
|
+
}),
|
|
1009
|
+
z.array(agentDurationPointSchema)
|
|
1010
|
+
);
|
|
751
1011
|
}
|
|
752
1012
|
/**
|
|
753
1013
|
* Top models by LLM call count (`gen_ai.operation.type:ai_client`).
|
|
754
1014
|
*/
|
|
755
1015
|
async getModelsByCalls(projectId, options) {
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
1016
|
+
return this.request(
|
|
1017
|
+
() => this.http.get(`api/projects/${projectId}/agents/models/calls`, {
|
|
1018
|
+
searchParams: breakdownSearchParams(options)
|
|
1019
|
+
}),
|
|
1020
|
+
z.array(genAiBreakdownRowSchema)
|
|
1021
|
+
);
|
|
760
1022
|
}
|
|
761
1023
|
/**
|
|
762
1024
|
* Top models by total tokens used (`gen_ai.operation.type:ai_client`).
|
|
763
1025
|
*/
|
|
764
1026
|
async getModelsByTokens(projectId, options) {
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
1027
|
+
return this.request(
|
|
1028
|
+
() => this.http.get(`api/projects/${projectId}/agents/models/tokens`, {
|
|
1029
|
+
searchParams: breakdownSearchParams(options)
|
|
1030
|
+
}),
|
|
1031
|
+
z.array(genAiBreakdownRowSchema)
|
|
1032
|
+
);
|
|
769
1033
|
}
|
|
770
1034
|
/**
|
|
771
1035
|
* Top tools by call count (`gen_ai.operation.type:tool`).
|
|
772
1036
|
*/
|
|
773
1037
|
async getTools(projectId, options) {
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
1038
|
+
return this.request(
|
|
1039
|
+
() => this.http.get(`api/projects/${projectId}/agents/tools`, {
|
|
1040
|
+
searchParams: breakdownSearchParams(options)
|
|
1041
|
+
}),
|
|
1042
|
+
z.array(genAiBreakdownRowSchema)
|
|
1043
|
+
);
|
|
778
1044
|
}
|
|
779
1045
|
/**
|
|
780
1046
|
* Paginated per-trace_id aggregate (duration, tokens, tool usage) across
|
|
@@ -788,9 +1054,10 @@ var AgentsResource = class extends BaseResource {
|
|
|
788
1054
|
if (options?.per_page) {
|
|
789
1055
|
searchParams.per_page = String(options.per_page);
|
|
790
1056
|
}
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
1057
|
+
return this.request(
|
|
1058
|
+
() => this.http.get(`api/projects/${projectId}/agents/traces`, {
|
|
1059
|
+
searchParams
|
|
1060
|
+
}),
|
|
794
1061
|
offsetPaginatedResponseSchema(agentTraceSummarySchema)
|
|
795
1062
|
);
|
|
796
1063
|
}
|
|
@@ -800,52 +1067,83 @@ var AlertIntegrationsResource = class extends BaseResource {
|
|
|
800
1067
|
* List all alert integrations
|
|
801
1068
|
*/
|
|
802
1069
|
async list() {
|
|
803
|
-
|
|
804
|
-
|
|
1070
|
+
return this.request(
|
|
1071
|
+
() => this.http.get("api/integrations"),
|
|
1072
|
+
z.array(alertIntegrationSchema)
|
|
1073
|
+
);
|
|
805
1074
|
}
|
|
806
1075
|
/**
|
|
807
1076
|
* Get a single alert integration by ID
|
|
808
1077
|
*/
|
|
809
1078
|
async get(id) {
|
|
810
|
-
|
|
811
|
-
|
|
1079
|
+
return this.request(
|
|
1080
|
+
() => this.http.get(`api/integrations/${id}`),
|
|
1081
|
+
alertIntegrationSchema
|
|
1082
|
+
);
|
|
812
1083
|
}
|
|
813
1084
|
/**
|
|
814
1085
|
* Create a new alert integration
|
|
815
1086
|
*/
|
|
816
1087
|
async create(input) {
|
|
817
|
-
const validatedInput = this.
|
|
818
|
-
|
|
819
|
-
|
|
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
|
+
);
|
|
820
1099
|
}
|
|
821
1100
|
/**
|
|
822
1101
|
* Update an existing alert integration
|
|
823
1102
|
*/
|
|
824
1103
|
async update(id, input) {
|
|
825
|
-
const validatedInput = this.
|
|
826
|
-
|
|
827
|
-
|
|
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
|
+
);
|
|
828
1117
|
}
|
|
829
1118
|
/**
|
|
830
1119
|
* Delete an alert integration
|
|
831
1120
|
*/
|
|
832
1121
|
async delete(id) {
|
|
833
|
-
|
|
1122
|
+
return this.requestVoid(() => this.http.delete(`api/integrations/${id}`));
|
|
834
1123
|
}
|
|
835
1124
|
/**
|
|
836
1125
|
* Send a test notification to verify integration configuration.
|
|
837
1126
|
* For Slack bot_token integrations, routingOverride must include `channel`.
|
|
838
1127
|
*/
|
|
839
1128
|
async test(id, routingOverride) {
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
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
|
+
);
|
|
849
1147
|
}
|
|
850
1148
|
};
|
|
851
1149
|
var AlertRulesResource = class extends BaseResource {
|
|
@@ -853,39 +1151,57 @@ var AlertRulesResource = class extends BaseResource {
|
|
|
853
1151
|
* List all alert rules for a project
|
|
854
1152
|
*/
|
|
855
1153
|
async list(projectId) {
|
|
856
|
-
|
|
857
|
-
|
|
1154
|
+
return this.request(
|
|
1155
|
+
() => this.http.get(`api/projects/${projectId}/alert-rules`),
|
|
1156
|
+
z.array(alertRuleSchema)
|
|
1157
|
+
);
|
|
858
1158
|
}
|
|
859
1159
|
/**
|
|
860
1160
|
* Get a single alert rule by ID
|
|
861
1161
|
*/
|
|
862
1162
|
async get(projectId, ruleId) {
|
|
863
|
-
|
|
864
|
-
|
|
1163
|
+
return this.request(
|
|
1164
|
+
() => this.http.get(`api/projects/${projectId}/alert-rules/${ruleId}`),
|
|
1165
|
+
alertRuleSchema
|
|
1166
|
+
);
|
|
865
1167
|
}
|
|
866
1168
|
/**
|
|
867
1169
|
* Create a new alert rule for a project
|
|
868
1170
|
*/
|
|
869
1171
|
async create(projectId, input) {
|
|
870
|
-
const validatedInput = this.
|
|
871
|
-
|
|
872
|
-
|
|
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
|
+
);
|
|
873
1182
|
}
|
|
874
1183
|
/**
|
|
875
1184
|
* Update an existing alert rule
|
|
876
1185
|
*/
|
|
877
1186
|
async update(projectId, ruleId, input) {
|
|
878
|
-
const validatedInput = this.
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
}
|
|
882
|
-
return this.
|
|
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
|
+
);
|
|
883
1197
|
}
|
|
884
1198
|
/**
|
|
885
1199
|
* Delete an alert rule
|
|
886
1200
|
*/
|
|
887
1201
|
async delete(projectId, ruleId) {
|
|
888
|
-
|
|
1202
|
+
return this.requestVoid(
|
|
1203
|
+
() => this.http.delete(`api/projects/${projectId}/alert-rules/${ruleId}`)
|
|
1204
|
+
);
|
|
889
1205
|
}
|
|
890
1206
|
/**
|
|
891
1207
|
* List alert history for a project
|
|
@@ -897,12 +1213,18 @@ var AlertRulesResource = class extends BaseResource {
|
|
|
897
1213
|
}
|
|
898
1214
|
const query = searchParams.toString();
|
|
899
1215
|
const url = query ? `api/projects/${projectId}/alert-history?${query}` : `api/projects/${projectId}/alert-history`;
|
|
900
|
-
|
|
901
|
-
return this.validate(data, z.array(alertHistorySchema));
|
|
1216
|
+
return this.request(() => this.http.get(url), z.array(alertHistorySchema));
|
|
902
1217
|
}
|
|
903
1218
|
};
|
|
904
1219
|
|
|
905
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
|
+
}
|
|
906
1228
|
var AuthResource = class extends BaseResource {
|
|
907
1229
|
/**
|
|
908
1230
|
* Register a new user account
|
|
@@ -911,17 +1233,17 @@ var AuthResource = class extends BaseResource {
|
|
|
911
1233
|
* @returns LoginResult with user information and session cookies
|
|
912
1234
|
*/
|
|
913
1235
|
async register(credentials) {
|
|
914
|
-
const validatedInput = this.
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
return
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
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
|
+
);
|
|
925
1247
|
}
|
|
926
1248
|
/**
|
|
927
1249
|
* Login with email and password
|
|
@@ -930,17 +1252,14 @@ var AuthResource = class extends BaseResource {
|
|
|
930
1252
|
* @returns LoginResult with user information and session cookies
|
|
931
1253
|
*/
|
|
932
1254
|
async login(credentials) {
|
|
933
|
-
const validatedInput = this.
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
user: authResponse.user,
|
|
942
|
-
cookies
|
|
943
|
-
};
|
|
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
|
+
);
|
|
944
1263
|
}
|
|
945
1264
|
/**
|
|
946
1265
|
* Logout the current user
|
|
@@ -948,17 +1267,28 @@ var AuthResource = class extends BaseResource {
|
|
|
948
1267
|
* @returns Array of Set-Cookie headers (typically clearing cookies)
|
|
949
1268
|
*/
|
|
950
1269
|
async logout() {
|
|
951
|
-
|
|
952
|
-
|
|
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
|
+
);
|
|
953
1278
|
}
|
|
954
1279
|
/**
|
|
955
1280
|
* Get current authenticated user
|
|
956
1281
|
* Requires a valid session cookie
|
|
957
|
-
*
|
|
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
|
|
958
1289
|
*/
|
|
959
1290
|
async getCurrentUser() {
|
|
960
|
-
|
|
961
|
-
return this.validate(data, userSchema);
|
|
1291
|
+
return this.request(() => this.http.get("auth/me"), userSchema);
|
|
962
1292
|
}
|
|
963
1293
|
/**
|
|
964
1294
|
* Get the details of a pending invitation by its token (public endpoint)
|
|
@@ -967,8 +1297,10 @@ var AuthResource = class extends BaseResource {
|
|
|
967
1297
|
* @returns Invitation info (email, role, status, expiry)
|
|
968
1298
|
*/
|
|
969
1299
|
async getInvitation(token) {
|
|
970
|
-
|
|
971
|
-
|
|
1300
|
+
return this.request(
|
|
1301
|
+
() => this.http.get(`auth/invitation/${token}`),
|
|
1302
|
+
invitationInfoSchema
|
|
1303
|
+
);
|
|
972
1304
|
}
|
|
973
1305
|
/**
|
|
974
1306
|
* Accept a pending invitation, creating the user account and logging in
|
|
@@ -976,17 +1308,33 @@ var AuthResource = class extends BaseResource {
|
|
|
976
1308
|
* @returns LoginResult with user information and session cookies
|
|
977
1309
|
*/
|
|
978
1310
|
async acceptInvitation(input) {
|
|
979
|
-
const validatedInput = this.
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
}
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
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 });
|
|
990
1338
|
}
|
|
991
1339
|
};
|
|
992
1340
|
|
|
@@ -1003,17 +1351,23 @@ var EventsResource = class extends BaseResource {
|
|
|
1003
1351
|
if (options?.cursor) {
|
|
1004
1352
|
searchParams.cursor = options.cursor;
|
|
1005
1353
|
}
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1354
|
+
return this.request(
|
|
1355
|
+
() => this.http.get(`api/projects/${projectId}/issues/${issueId}/events`, {
|
|
1356
|
+
searchParams
|
|
1357
|
+
}),
|
|
1358
|
+
paginatedResponseSchema(eventSchema)
|
|
1359
|
+
);
|
|
1010
1360
|
}
|
|
1011
1361
|
/**
|
|
1012
1362
|
* Get a single event by ID with full details
|
|
1013
1363
|
*/
|
|
1014
1364
|
async get(projectId, issueId, eventId) {
|
|
1015
|
-
|
|
1016
|
-
|
|
1365
|
+
return this.request(
|
|
1366
|
+
() => this.http.get(
|
|
1367
|
+
`api/projects/${projectId}/issues/${issueId}/events/${eventId}`
|
|
1368
|
+
),
|
|
1369
|
+
eventDetailSchema
|
|
1370
|
+
);
|
|
1017
1371
|
}
|
|
1018
1372
|
};
|
|
1019
1373
|
var serverVersionSchema = z.object({
|
|
@@ -1023,8 +1377,10 @@ var serverVersionSchema = z.object({
|
|
|
1023
1377
|
// src/resources/health.ts
|
|
1024
1378
|
var HealthResource = class extends BaseResource {
|
|
1025
1379
|
async getVersion() {
|
|
1026
|
-
|
|
1027
|
-
|
|
1380
|
+
return this.request(
|
|
1381
|
+
() => this.http.get("health/version"),
|
|
1382
|
+
serverVersionSchema
|
|
1383
|
+
);
|
|
1028
1384
|
}
|
|
1029
1385
|
};
|
|
1030
1386
|
|
|
@@ -1035,23 +1391,30 @@ var InvitationsResource = class extends BaseResource {
|
|
|
1035
1391
|
* @param input - Email and global role for the invited user
|
|
1036
1392
|
*/
|
|
1037
1393
|
async create(input) {
|
|
1038
|
-
const validatedInput = this.
|
|
1039
|
-
|
|
1040
|
-
|
|
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
|
+
);
|
|
1041
1402
|
}
|
|
1042
1403
|
/**
|
|
1043
1404
|
* List all invitations
|
|
1044
1405
|
*/
|
|
1045
1406
|
async list() {
|
|
1046
|
-
|
|
1047
|
-
|
|
1407
|
+
return this.request(
|
|
1408
|
+
() => this.http.get("api/invitations"),
|
|
1409
|
+
invitationSchema.array()
|
|
1410
|
+
);
|
|
1048
1411
|
}
|
|
1049
1412
|
/**
|
|
1050
1413
|
* Revoke a pending invitation
|
|
1051
1414
|
* @param token - Invitation token to revoke
|
|
1052
1415
|
*/
|
|
1053
1416
|
async revoke(token) {
|
|
1054
|
-
|
|
1417
|
+
return this.requestVoid(() => this.http.delete(`api/invitations/${token}`));
|
|
1055
1418
|
}
|
|
1056
1419
|
};
|
|
1057
1420
|
var IssuesResource = class extends BaseResource {
|
|
@@ -1078,25 +1441,34 @@ var IssuesResource = class extends BaseResource {
|
|
|
1078
1441
|
if (options?.q) {
|
|
1079
1442
|
searchParams.q = options.q;
|
|
1080
1443
|
}
|
|
1081
|
-
|
|
1082
|
-
|
|
1444
|
+
return this.request(
|
|
1445
|
+
() => this.http.get(`api/projects/${projectId}/issues`, { searchParams }),
|
|
1446
|
+
offsetPaginatedResponseSchema(issueSchema)
|
|
1447
|
+
);
|
|
1083
1448
|
}
|
|
1084
1449
|
/**
|
|
1085
1450
|
* Get a single issue by ID
|
|
1086
1451
|
*/
|
|
1087
1452
|
async get(projectId, issueId) {
|
|
1088
|
-
|
|
1089
|
-
|
|
1453
|
+
return this.request(
|
|
1454
|
+
() => this.http.get(`api/projects/${projectId}/issues/${issueId}`),
|
|
1455
|
+
issueSchema
|
|
1456
|
+
);
|
|
1090
1457
|
}
|
|
1091
1458
|
/**
|
|
1092
1459
|
* Update issue state (status, substatus, priority, assignment, …)
|
|
1093
1460
|
*/
|
|
1094
1461
|
async updateState(projectId, issueId, input) {
|
|
1095
|
-
const validatedInput = this.
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
}
|
|
1099
|
-
return this.
|
|
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
|
+
);
|
|
1100
1472
|
}
|
|
1101
1473
|
/**
|
|
1102
1474
|
* Resolve an issue in the next release (suppresses regression until a deploy).
|
|
@@ -1110,115 +1482,163 @@ var IssuesResource = class extends BaseResource {
|
|
|
1110
1482
|
* Delete an issue
|
|
1111
1483
|
*/
|
|
1112
1484
|
async delete(projectId, issueId) {
|
|
1113
|
-
|
|
1485
|
+
return this.requestVoid(
|
|
1486
|
+
() => this.http.delete(`api/projects/${projectId}/issues/${issueId}`)
|
|
1487
|
+
);
|
|
1114
1488
|
}
|
|
1115
1489
|
/**
|
|
1116
1490
|
* Bulk-mutate issues (status and/or priority).
|
|
1117
1491
|
*/
|
|
1118
1492
|
async bulkUpdate(projectId, input) {
|
|
1119
|
-
const validatedInput = this.
|
|
1120
|
-
|
|
1121
|
-
|
|
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
|
+
);
|
|
1122
1503
|
}
|
|
1123
1504
|
/**
|
|
1124
1505
|
* Bulk-delete issues.
|
|
1125
1506
|
*/
|
|
1126
1507
|
async bulkDelete(projectId, input) {
|
|
1127
|
-
const validatedInput = this.
|
|
1128
|
-
|
|
1129
|
-
|
|
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
|
+
);
|
|
1130
1518
|
}
|
|
1131
1519
|
/**
|
|
1132
1520
|
* List the grouping hashes that map to an issue.
|
|
1133
1521
|
*/
|
|
1134
1522
|
async getHashes(projectId, issueId) {
|
|
1135
|
-
|
|
1136
|
-
|
|
1523
|
+
return this.request(
|
|
1524
|
+
() => this.http.get(`api/projects/${projectId}/issues/${issueId}/hashes`),
|
|
1525
|
+
z.array(issueHashSchema)
|
|
1526
|
+
);
|
|
1137
1527
|
}
|
|
1138
1528
|
/**
|
|
1139
1529
|
* List the distinct values (with counts) for a tag key across the issue.
|
|
1140
1530
|
* Returns a bare list, one entry per value (Sentry-compatible shape).
|
|
1141
1531
|
*/
|
|
1142
1532
|
async getTagValues(projectId, issueId, key) {
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1533
|
+
return this.request(
|
|
1534
|
+
() => this.http.get(
|
|
1535
|
+
`api/projects/${projectId}/issues/${issueId}/tags/${encodeURIComponent(key)}`
|
|
1536
|
+
),
|
|
1537
|
+
z.array(issueTagValueSchema)
|
|
1538
|
+
);
|
|
1147
1539
|
}
|
|
1148
1540
|
/**
|
|
1149
1541
|
* Fetch per-issue aggregates (unique user count + top tags).
|
|
1150
1542
|
*/
|
|
1151
1543
|
async getAggregates(projectId, issueId) {
|
|
1152
|
-
|
|
1153
|
-
|
|
1544
|
+
return this.request(
|
|
1545
|
+
() => this.http.get(`api/projects/${projectId}/issues/${issueId}/aggregates`),
|
|
1546
|
+
issueAggregatesSchema
|
|
1547
|
+
);
|
|
1154
1548
|
}
|
|
1155
1549
|
/**
|
|
1156
1550
|
* Fetch a zero-filled event-count timeseries (24h or 30d).
|
|
1157
1551
|
*/
|
|
1158
1552
|
async getStats(projectId, issueId, window = "24h") {
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1553
|
+
return this.request(
|
|
1554
|
+
() => this.http.get(`api/projects/${projectId}/issues/${issueId}/stats`, {
|
|
1555
|
+
searchParams: { window }
|
|
1556
|
+
}),
|
|
1557
|
+
issueStatsSchema
|
|
1558
|
+
);
|
|
1163
1559
|
}
|
|
1164
1560
|
/**
|
|
1165
1561
|
* List an issue's activity log (status changes, comments/notes, …).
|
|
1166
1562
|
*/
|
|
1167
1563
|
async getActivity(projectId, issueId) {
|
|
1168
|
-
|
|
1169
|
-
|
|
1564
|
+
return this.request(
|
|
1565
|
+
() => this.http.get(`api/projects/${projectId}/issues/${issueId}/activity`),
|
|
1566
|
+
z.array(activityEntrySchema)
|
|
1567
|
+
);
|
|
1170
1568
|
}
|
|
1171
1569
|
/**
|
|
1172
1570
|
* Add a comment (note) to an issue.
|
|
1173
1571
|
*/
|
|
1174
1572
|
async addComment(projectId, issueId, input) {
|
|
1175
|
-
const validatedInput = this.
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
}
|
|
1179
|
-
return this.
|
|
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
|
+
);
|
|
1180
1583
|
}
|
|
1181
1584
|
/**
|
|
1182
1585
|
* Set or clear the current user's bookmark on an issue.
|
|
1183
1586
|
*/
|
|
1184
1587
|
async setBookmark(projectId, issueId, enabled) {
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
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
|
+
);
|
|
1189
1594
|
}
|
|
1190
1595
|
/**
|
|
1191
1596
|
* Set or clear the current user's subscription to an issue.
|
|
1192
1597
|
*/
|
|
1193
1598
|
async setSubscription(projectId, issueId, enabled) {
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
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
|
+
);
|
|
1198
1606
|
}
|
|
1199
1607
|
/**
|
|
1200
1608
|
* Mark the issue as seen by the current user.
|
|
1201
1609
|
*/
|
|
1202
1610
|
async markSeen(projectId, issueId) {
|
|
1203
|
-
|
|
1204
|
-
|
|
1611
|
+
return this.request(
|
|
1612
|
+
() => this.http.post(`api/projects/${projectId}/issues/${issueId}/seen`),
|
|
1613
|
+
z.object({ has_seen: z.boolean() })
|
|
1614
|
+
);
|
|
1205
1615
|
}
|
|
1206
1616
|
/**
|
|
1207
1617
|
* List user feedback reports for an issue.
|
|
1208
1618
|
*/
|
|
1209
1619
|
async listUserReports(projectId, issueId) {
|
|
1210
|
-
|
|
1211
|
-
|
|
1620
|
+
return this.request(
|
|
1621
|
+
() => this.http.get(
|
|
1622
|
+
`api/projects/${projectId}/issues/${issueId}/user-reports`
|
|
1623
|
+
),
|
|
1624
|
+
z.array(userReportSchema)
|
|
1625
|
+
);
|
|
1212
1626
|
}
|
|
1213
1627
|
/**
|
|
1214
1628
|
* Attach a user feedback report to an issue.
|
|
1215
1629
|
*/
|
|
1216
1630
|
async createUserReport(projectId, issueId, input) {
|
|
1217
|
-
const validatedInput = this.
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
}
|
|
1221
|
-
return this.
|
|
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
|
+
);
|
|
1222
1642
|
}
|
|
1223
1643
|
};
|
|
1224
1644
|
|
|
@@ -1242,8 +1662,10 @@ var LogsResource = class extends BaseResource {
|
|
|
1242
1662
|
if (options?.trace_id) {
|
|
1243
1663
|
searchParams.trace_id = options.trace_id;
|
|
1244
1664
|
}
|
|
1245
|
-
|
|
1246
|
-
|
|
1665
|
+
return this.request(
|
|
1666
|
+
() => this.http.get(`api/projects/${projectId}/logs`, { searchParams }),
|
|
1667
|
+
offsetPaginatedResponseSchema(logSchema)
|
|
1668
|
+
);
|
|
1247
1669
|
}
|
|
1248
1670
|
};
|
|
1249
1671
|
|
|
@@ -1254,8 +1676,10 @@ var MembersResource = class extends BaseResource {
|
|
|
1254
1676
|
* @param projectId - ID of the project
|
|
1255
1677
|
*/
|
|
1256
1678
|
async list(projectId) {
|
|
1257
|
-
|
|
1258
|
-
|
|
1679
|
+
return this.request(
|
|
1680
|
+
() => this.http.get(`api/projects/${projectId}/members`),
|
|
1681
|
+
projectMemberSchema.array()
|
|
1682
|
+
);
|
|
1259
1683
|
}
|
|
1260
1684
|
/**
|
|
1261
1685
|
* Add or update a project member
|
|
@@ -1263,10 +1687,15 @@ var MembersResource = class extends BaseResource {
|
|
|
1263
1687
|
* @param input - User ID and per-project role
|
|
1264
1688
|
*/
|
|
1265
1689
|
async upsert(projectId, input) {
|
|
1266
|
-
const validatedInput = this.
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
}
|
|
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
|
+
);
|
|
1270
1699
|
}
|
|
1271
1700
|
/**
|
|
1272
1701
|
* Remove a member from a project
|
|
@@ -1274,7 +1703,9 @@ var MembersResource = class extends BaseResource {
|
|
|
1274
1703
|
* @param userId - ID of the user to remove
|
|
1275
1704
|
*/
|
|
1276
1705
|
async remove(projectId, userId) {
|
|
1277
|
-
|
|
1706
|
+
return this.requestVoid(
|
|
1707
|
+
() => this.http.delete(`api/projects/${projectId}/members/${userId}`)
|
|
1708
|
+
);
|
|
1278
1709
|
}
|
|
1279
1710
|
};
|
|
1280
1711
|
|
|
@@ -1294,39 +1725,56 @@ var ProjectsResource = class extends BaseResource {
|
|
|
1294
1725
|
if (options?.order) {
|
|
1295
1726
|
searchParams.set("order", options.order);
|
|
1296
1727
|
}
|
|
1728
|
+
if (options?.stats_period) {
|
|
1729
|
+
searchParams.set("stats_period", options.stats_period);
|
|
1730
|
+
}
|
|
1297
1731
|
const query = searchParams.toString();
|
|
1298
1732
|
const url = query ? `api/projects?${query}` : "api/projects";
|
|
1299
|
-
|
|
1300
|
-
|
|
1733
|
+
return this.request(
|
|
1734
|
+
() => this.http.get(url),
|
|
1735
|
+
offsetPaginatedResponseSchema(projectSchema)
|
|
1736
|
+
);
|
|
1301
1737
|
}
|
|
1302
1738
|
/**
|
|
1303
1739
|
* Get a single project by ID
|
|
1304
1740
|
*/
|
|
1305
1741
|
async get(id) {
|
|
1306
|
-
|
|
1307
|
-
|
|
1742
|
+
return this.request(
|
|
1743
|
+
() => this.http.get(`api/projects/${id}`),
|
|
1744
|
+
projectSchema
|
|
1745
|
+
);
|
|
1308
1746
|
}
|
|
1309
1747
|
/**
|
|
1310
1748
|
* Create a new project
|
|
1311
1749
|
*/
|
|
1312
1750
|
async create(input) {
|
|
1313
|
-
const validatedInput = this.
|
|
1314
|
-
|
|
1315
|
-
|
|
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
|
+
);
|
|
1316
1759
|
}
|
|
1317
1760
|
/**
|
|
1318
1761
|
* Update an existing project
|
|
1319
1762
|
*/
|
|
1320
1763
|
async update(id, input) {
|
|
1321
|
-
const validatedInput = this.
|
|
1322
|
-
|
|
1323
|
-
|
|
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
|
+
);
|
|
1324
1772
|
}
|
|
1325
1773
|
/**
|
|
1326
1774
|
* Delete a project
|
|
1327
1775
|
*/
|
|
1328
1776
|
async delete(id) {
|
|
1329
|
-
|
|
1777
|
+
return this.requestVoid(() => this.http.delete(`api/projects/${id}`));
|
|
1330
1778
|
}
|
|
1331
1779
|
};
|
|
1332
1780
|
var ReleasesResource = class extends BaseResource {
|
|
@@ -1342,11 +1790,13 @@ var ReleasesResource = class extends BaseResource {
|
|
|
1342
1790
|
if (limit !== void 0) {
|
|
1343
1791
|
searchParams.limit = limit.toString();
|
|
1344
1792
|
}
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
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
|
+
);
|
|
1350
1800
|
}
|
|
1351
1801
|
};
|
|
1352
1802
|
|
|
@@ -1372,9 +1822,10 @@ var SessionsResource = class extends BaseResource {
|
|
|
1372
1822
|
if (options?.per_page) {
|
|
1373
1823
|
searchParams.per_page = String(options.per_page);
|
|
1374
1824
|
}
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1825
|
+
return this.request(
|
|
1826
|
+
() => this.http.get(`api/projects/${projectId}/sessions/stats`, {
|
|
1827
|
+
searchParams
|
|
1828
|
+
}),
|
|
1378
1829
|
offsetPaginatedResponseSchema(releaseHealthRowSchema)
|
|
1379
1830
|
);
|
|
1380
1831
|
}
|
|
@@ -1388,8 +1839,12 @@ var SessionsResource = class extends BaseResource {
|
|
|
1388
1839
|
if (period) {
|
|
1389
1840
|
searchParams.period = period;
|
|
1390
1841
|
}
|
|
1391
|
-
|
|
1392
|
-
|
|
1842
|
+
return this.request(
|
|
1843
|
+
() => this.http.get(`api/projects/${projectId}/sessions/summary`, {
|
|
1844
|
+
searchParams
|
|
1845
|
+
}),
|
|
1846
|
+
sessionSummarySchema
|
|
1847
|
+
);
|
|
1393
1848
|
}
|
|
1394
1849
|
/**
|
|
1395
1850
|
* Get a time-bucketed session trend for a project, aggregated across all
|
|
@@ -1406,8 +1861,12 @@ var SessionsResource = class extends BaseResource {
|
|
|
1406
1861
|
if (interval !== void 0) {
|
|
1407
1862
|
searchParams.interval = interval.toString();
|
|
1408
1863
|
}
|
|
1409
|
-
|
|
1410
|
-
|
|
1864
|
+
return this.request(
|
|
1865
|
+
() => this.http.get(`api/projects/${projectId}/sessions/timeseries`, {
|
|
1866
|
+
searchParams
|
|
1867
|
+
}),
|
|
1868
|
+
sessionTimeseriesSchema
|
|
1869
|
+
);
|
|
1411
1870
|
}
|
|
1412
1871
|
};
|
|
1413
1872
|
|
|
@@ -1418,8 +1877,10 @@ var SourceMapsResource = class extends BaseResource {
|
|
|
1418
1877
|
* Returns the upload URL, chunk size limits, and accepted artifact types.
|
|
1419
1878
|
*/
|
|
1420
1879
|
async getChunkUploadCapability(orgSlug) {
|
|
1421
|
-
|
|
1422
|
-
|
|
1880
|
+
return this.request(
|
|
1881
|
+
() => this.http.get(`api/0/organizations/${orgSlug}/chunk-upload/`),
|
|
1882
|
+
chunkUploadCapabilitySchema
|
|
1883
|
+
);
|
|
1423
1884
|
}
|
|
1424
1885
|
/**
|
|
1425
1886
|
* Upload chunks as multipart/form-data, batching by `chunksPerRequest` (default 64).
|
|
@@ -1428,7 +1889,10 @@ var SourceMapsResource = class extends BaseResource {
|
|
|
1428
1889
|
*/
|
|
1429
1890
|
async uploadChunks(orgSlug, chunks, chunksPerRequest = 64) {
|
|
1430
1891
|
if (!Number.isInteger(chunksPerRequest) || chunksPerRequest <= 0) {
|
|
1431
|
-
|
|
1892
|
+
return Err({
|
|
1893
|
+
kind: "invalid_request",
|
|
1894
|
+
message: "chunksPerRequest must be a positive integer."
|
|
1895
|
+
});
|
|
1432
1896
|
}
|
|
1433
1897
|
for (let i = 0; i < chunks.length; i += chunksPerRequest) {
|
|
1434
1898
|
const batch = chunks.slice(i, i + chunksPerRequest);
|
|
@@ -1436,10 +1900,16 @@ var SourceMapsResource = class extends BaseResource {
|
|
|
1436
1900
|
for (const chunk of batch) {
|
|
1437
1901
|
form.append(chunk.hash, chunk.data);
|
|
1438
1902
|
}
|
|
1439
|
-
await this.
|
|
1440
|
-
|
|
1441
|
-
|
|
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
|
+
}
|
|
1442
1911
|
}
|
|
1912
|
+
return Ok(void 0);
|
|
1443
1913
|
}
|
|
1444
1914
|
/**
|
|
1445
1915
|
* Trigger assembly of a previously uploaded artifact bundle.
|
|
@@ -1449,17 +1919,28 @@ var SourceMapsResource = class extends BaseResource {
|
|
|
1449
1919
|
* still needs uploading via `uploadChunks`.
|
|
1450
1920
|
*/
|
|
1451
1921
|
async assembleBundle(orgSlug, input) {
|
|
1452
|
-
const
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
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
|
+
);
|
|
1456
1933
|
}
|
|
1457
1934
|
/**
|
|
1458
1935
|
* List all source map files uploaded for a project.
|
|
1459
1936
|
*/
|
|
1460
1937
|
async list(orgSlug, projectSlug) {
|
|
1461
|
-
|
|
1462
|
-
|
|
1938
|
+
return this.request(
|
|
1939
|
+
() => this.http.get(
|
|
1940
|
+
`api/0/projects/${orgSlug}/${projectSlug}/files/source-maps/`
|
|
1941
|
+
),
|
|
1942
|
+
listSourceMapsResponseSchema
|
|
1943
|
+
);
|
|
1463
1944
|
}
|
|
1464
1945
|
};
|
|
1465
1946
|
|
|
@@ -1489,8 +1970,10 @@ var SpansResource = class extends BaseResource {
|
|
|
1489
1970
|
if (options?.operation_type) {
|
|
1490
1971
|
searchParams.operation_type = options.operation_type;
|
|
1491
1972
|
}
|
|
1492
|
-
|
|
1493
|
-
|
|
1973
|
+
return this.request(
|
|
1974
|
+
() => this.http.get(`api/projects/${projectId}/spans`, { searchParams }),
|
|
1975
|
+
offsetPaginatedResponseSchema(spanSchema)
|
|
1976
|
+
);
|
|
1494
1977
|
}
|
|
1495
1978
|
};
|
|
1496
1979
|
|
|
@@ -1515,8 +1998,12 @@ var StatsResource = class extends BaseResource {
|
|
|
1515
1998
|
if (interval !== void 0) {
|
|
1516
1999
|
searchParams.interval = interval.toString();
|
|
1517
2000
|
}
|
|
1518
|
-
|
|
1519
|
-
|
|
2001
|
+
return this.request(
|
|
2002
|
+
() => this.http.get(`api/projects/${projectId}/events/stats`, {
|
|
2003
|
+
searchParams
|
|
2004
|
+
}),
|
|
2005
|
+
eventTimeseriesSchema
|
|
2006
|
+
);
|
|
1520
2007
|
}
|
|
1521
2008
|
/**
|
|
1522
2009
|
* Get project-wide counters for a window, each paired with the same counter
|
|
@@ -1531,8 +2018,12 @@ var StatsResource = class extends BaseResource {
|
|
|
1531
2018
|
if (period) {
|
|
1532
2019
|
searchParams.period = period;
|
|
1533
2020
|
}
|
|
1534
|
-
|
|
1535
|
-
|
|
2021
|
+
return this.request(
|
|
2022
|
+
() => this.http.get(`api/projects/${projectId}/stats/summary`, {
|
|
2023
|
+
searchParams
|
|
2024
|
+
}),
|
|
2025
|
+
projectStatsSummarySchema
|
|
2026
|
+
);
|
|
1536
2027
|
}
|
|
1537
2028
|
};
|
|
1538
2029
|
var StorageResource = class extends BaseResource {
|
|
@@ -1541,42 +2032,60 @@ var StorageResource = class extends BaseResource {
|
|
|
1541
2032
|
* whole-DB size, and exact source-map weight.
|
|
1542
2033
|
*/
|
|
1543
2034
|
async getSummary() {
|
|
1544
|
-
|
|
1545
|
-
|
|
2035
|
+
return this.request(
|
|
2036
|
+
() => this.http.get("api/storage/summary"),
|
|
2037
|
+
storageSummarySchema
|
|
2038
|
+
);
|
|
1546
2039
|
}
|
|
1547
2040
|
/**
|
|
1548
2041
|
* Get the per-project storage breakdown (one row per project, including
|
|
1549
2042
|
* empty ones).
|
|
1550
2043
|
*/
|
|
1551
2044
|
async getProjects() {
|
|
1552
|
-
|
|
1553
|
-
|
|
2045
|
+
return this.request(
|
|
2046
|
+
() => this.http.get("api/storage/projects"),
|
|
2047
|
+
z.array(projectStorageSchema)
|
|
2048
|
+
);
|
|
1554
2049
|
}
|
|
1555
2050
|
/**
|
|
1556
2051
|
* Dry-run: count the rows a cleanup would remove. Mutates nothing — use it to
|
|
1557
2052
|
* confirm impact before {@link executeCleanup}.
|
|
1558
2053
|
*/
|
|
1559
2054
|
async previewCleanup(options) {
|
|
1560
|
-
const
|
|
1561
|
-
|
|
1562
|
-
|
|
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
|
+
);
|
|
1563
2065
|
}
|
|
1564
2066
|
/**
|
|
1565
2067
|
* Execute a cleanup: delete data older than `older_than_days` (optionally
|
|
1566
2068
|
* scoped to one project) and remove any issue left with zero events.
|
|
1567
2069
|
*/
|
|
1568
2070
|
async executeCleanup(options) {
|
|
1569
|
-
const
|
|
1570
|
-
|
|
1571
|
-
|
|
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
|
+
);
|
|
1572
2079
|
}
|
|
1573
2080
|
/**
|
|
1574
2081
|
* Dry-run for {@link gcSourceMaps}: count the orphaned source-map files and
|
|
1575
2082
|
* bytes a GC would reclaim. Mutates nothing.
|
|
1576
2083
|
*/
|
|
1577
2084
|
async previewGcSourceMaps() {
|
|
1578
|
-
|
|
1579
|
-
|
|
2085
|
+
return this.request(
|
|
2086
|
+
() => this.http.post("api/storage/source-maps/gc/preview"),
|
|
2087
|
+
sourceMapGcResultSchema
|
|
2088
|
+
);
|
|
1580
2089
|
}
|
|
1581
2090
|
/**
|
|
1582
2091
|
* Garbage-collect orphaned source maps: files no longer referenced by any
|
|
@@ -1584,8 +2093,10 @@ var StorageResource = class extends BaseResource {
|
|
|
1584
2093
|
* referenced files.
|
|
1585
2094
|
*/
|
|
1586
2095
|
async gcSourceMaps() {
|
|
1587
|
-
|
|
1588
|
-
|
|
2096
|
+
return this.request(
|
|
2097
|
+
() => this.http.post("api/storage/source-maps/gc"),
|
|
2098
|
+
sourceMapGcResultSchema
|
|
2099
|
+
);
|
|
1589
2100
|
}
|
|
1590
2101
|
};
|
|
1591
2102
|
|
|
@@ -1595,8 +2106,10 @@ var TeamResource = class extends BaseResource {
|
|
|
1595
2106
|
* List all team members (users)
|
|
1596
2107
|
*/
|
|
1597
2108
|
async list() {
|
|
1598
|
-
|
|
1599
|
-
|
|
2109
|
+
return this.request(
|
|
2110
|
+
() => this.http.get("api/team"),
|
|
2111
|
+
teamMemberSchema.array()
|
|
2112
|
+
);
|
|
1600
2113
|
}
|
|
1601
2114
|
/**
|
|
1602
2115
|
* Change a user's global role
|
|
@@ -1604,15 +2117,20 @@ var TeamResource = class extends BaseResource {
|
|
|
1604
2117
|
* @param role - New global role to assign
|
|
1605
2118
|
*/
|
|
1606
2119
|
async updateRole(userId, role) {
|
|
1607
|
-
const body = this.
|
|
1608
|
-
|
|
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
|
+
);
|
|
1609
2127
|
}
|
|
1610
2128
|
/**
|
|
1611
2129
|
* Permanently remove a user from the instance.
|
|
1612
2130
|
* @param userId - ID of the user to delete
|
|
1613
2131
|
*/
|
|
1614
2132
|
async remove(userId) {
|
|
1615
|
-
|
|
2133
|
+
return this.requestVoid(() => this.http.delete(`api/team/${userId}`));
|
|
1616
2134
|
}
|
|
1617
2135
|
};
|
|
1618
2136
|
|
|
@@ -1622,8 +2140,10 @@ var TokensResource = class extends BaseResource {
|
|
|
1622
2140
|
* List all auth tokens (masked)
|
|
1623
2141
|
*/
|
|
1624
2142
|
async list() {
|
|
1625
|
-
|
|
1626
|
-
|
|
2143
|
+
return this.request(
|
|
2144
|
+
() => this.http.get("api/tokens"),
|
|
2145
|
+
authTokenSchema.array()
|
|
2146
|
+
);
|
|
1627
2147
|
}
|
|
1628
2148
|
/**
|
|
1629
2149
|
* Get a single auth token by ID (full token value).
|
|
@@ -1632,23 +2152,30 @@ var TokensResource = class extends BaseResource {
|
|
|
1632
2152
|
* complete token for GET /api/tokens/{id}.
|
|
1633
2153
|
*/
|
|
1634
2154
|
async get(id) {
|
|
1635
|
-
|
|
1636
|
-
|
|
2155
|
+
return this.request(
|
|
2156
|
+
() => this.http.get(`api/tokens/${id}`),
|
|
2157
|
+
authTokenCreatedSchema
|
|
2158
|
+
);
|
|
1637
2159
|
}
|
|
1638
2160
|
/**
|
|
1639
2161
|
* Create a new auth token
|
|
1640
2162
|
* Note: The full token is only returned once during creation
|
|
1641
2163
|
*/
|
|
1642
2164
|
async create(input) {
|
|
1643
|
-
const validatedInput = this.
|
|
1644
|
-
|
|
1645
|
-
|
|
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
|
+
);
|
|
1646
2173
|
}
|
|
1647
2174
|
/**
|
|
1648
2175
|
* Delete an auth token
|
|
1649
2176
|
*/
|
|
1650
2177
|
async delete(id) {
|
|
1651
|
-
|
|
2178
|
+
return this.requestVoid(() => this.http.delete(`api/tokens/${id}`));
|
|
1652
2179
|
}
|
|
1653
2180
|
};
|
|
1654
2181
|
var TransactionsResource = class extends BaseResource {
|
|
@@ -1678,11 +2205,10 @@ var TransactionsResource = class extends BaseResource {
|
|
|
1678
2205
|
if (options?.release) {
|
|
1679
2206
|
searchParams.release = options.release;
|
|
1680
2207
|
}
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
data,
|
|
2208
|
+
return this.request(
|
|
2209
|
+
() => this.http.get(`api/projects/${projectId}/transactions`, {
|
|
2210
|
+
searchParams
|
|
2211
|
+
}),
|
|
1686
2212
|
offsetPaginatedResponseSchema(transactionSchema)
|
|
1687
2213
|
);
|
|
1688
2214
|
}
|
|
@@ -1690,8 +2216,12 @@ var TransactionsResource = class extends BaseResource {
|
|
|
1690
2216
|
* Get the indexed spans extracted from a transaction, in waterfall order.
|
|
1691
2217
|
*/
|
|
1692
2218
|
async getSpans(projectId, transactionId) {
|
|
1693
|
-
|
|
1694
|
-
|
|
2219
|
+
return this.request(
|
|
2220
|
+
() => this.http.get(
|
|
2221
|
+
`api/projects/${projectId}/transactions/${transactionId}/spans`
|
|
2222
|
+
),
|
|
2223
|
+
z.array(spanSchema)
|
|
2224
|
+
);
|
|
1695
2225
|
}
|
|
1696
2226
|
/**
|
|
1697
2227
|
* Get aggregate performance stats (count, p50/p95/p99 latency, failure rate)
|
|
@@ -1706,99 +2236,43 @@ var TransactionsResource = class extends BaseResource {
|
|
|
1706
2236
|
if (options?.per_page) {
|
|
1707
2237
|
searchParams.per_page = String(options.per_page);
|
|
1708
2238
|
}
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
2239
|
+
return this.request(
|
|
2240
|
+
() => this.http.get(`api/projects/${projectId}/transactions/stats`, {
|
|
2241
|
+
searchParams
|
|
2242
|
+
}),
|
|
1712
2243
|
offsetPaginatedResponseSchema(transactionStatsSchema)
|
|
1713
2244
|
);
|
|
1714
2245
|
}
|
|
1715
2246
|
/**
|
|
1716
2247
|
* Get aggregate stats for a single (transaction name, op) group — a direct
|
|
1717
|
-
* lookup that works regardless of how many groups the project has.
|
|
1718
|
-
*
|
|
2248
|
+
* lookup that works regardless of how many groups the project has. Reports
|
|
2249
|
+
* `not_found` when the group has no transactions.
|
|
1719
2250
|
*/
|
|
1720
2251
|
async getStatForGroup(projectId, name, op) {
|
|
1721
2252
|
const searchParams = { name };
|
|
1722
2253
|
if (op) {
|
|
1723
2254
|
searchParams.op = op;
|
|
1724
2255
|
}
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
2256
|
+
return this.request(
|
|
2257
|
+
() => this.http.get(`api/projects/${projectId}/transactions/stats/group`, {
|
|
2258
|
+
searchParams
|
|
2259
|
+
}),
|
|
2260
|
+
transactionStatsSchema
|
|
2261
|
+
);
|
|
1729
2262
|
}
|
|
1730
2263
|
/**
|
|
1731
2264
|
* Get a single transaction by ID with its full Sentry payload (spans,
|
|
1732
2265
|
* contexts.trace, measurements, tags) for the performance detail view.
|
|
1733
2266
|
*/
|
|
1734
2267
|
async get(projectId, transactionId) {
|
|
1735
|
-
|
|
1736
|
-
|
|
2268
|
+
return this.request(
|
|
2269
|
+
() => this.http.get(
|
|
2270
|
+
`api/projects/${projectId}/transactions/${transactionId}`
|
|
2271
|
+
),
|
|
2272
|
+
transactionDetailSchema
|
|
2273
|
+
);
|
|
1737
2274
|
}
|
|
1738
2275
|
};
|
|
1739
|
-
function transformHttpError(error) {
|
|
1740
|
-
const { response } = error;
|
|
1741
|
-
const status = response.status;
|
|
1742
|
-
let errorMessage = `HTTP ${status} error`;
|
|
1743
|
-
const body = error.data;
|
|
1744
|
-
if (body) {
|
|
1745
|
-
errorMessage = body.error || body.message || errorMessage;
|
|
1746
|
-
}
|
|
1747
|
-
switch (status) {
|
|
1748
|
-
case 400:
|
|
1749
|
-
return new BadRequestError(errorMessage);
|
|
1750
|
-
case 401:
|
|
1751
|
-
return new AuthenticationError(errorMessage);
|
|
1752
|
-
case 403:
|
|
1753
|
-
return new AuthorizationError(errorMessage);
|
|
1754
|
-
case 404:
|
|
1755
|
-
return new NotFoundError(errorMessage);
|
|
1756
|
-
case 429: {
|
|
1757
|
-
const retryAfter = response.headers.get("Retry-After");
|
|
1758
|
-
return new RateLimitError(errorMessage, retryAfter ?? void 0);
|
|
1759
|
-
}
|
|
1760
|
-
case 500:
|
|
1761
|
-
case 502:
|
|
1762
|
-
case 503:
|
|
1763
|
-
case 504:
|
|
1764
|
-
return new ServerError(errorMessage, status);
|
|
1765
|
-
default:
|
|
1766
|
-
return new RustrakError(errorMessage, { statusCode: status });
|
|
1767
|
-
}
|
|
1768
|
-
}
|
|
1769
|
-
function createKyInstance(config) {
|
|
1770
|
-
const headers = {
|
|
1771
|
-
"Content-Type": "application/json",
|
|
1772
|
-
...config.headers
|
|
1773
|
-
};
|
|
1774
|
-
if (config.token) {
|
|
1775
|
-
headers.Authorization = `Bearer ${config.token}`;
|
|
1776
|
-
}
|
|
1777
|
-
return ky.create({
|
|
1778
|
-
prefix: config.baseUrl,
|
|
1779
|
-
timeout: config.timeout ?? 3e4,
|
|
1780
|
-
credentials: "include",
|
|
1781
|
-
retry: {
|
|
1782
|
-
limit: config.maxRetries ?? 2,
|
|
1783
|
-
statusCodes: [408, 500, 502, 503, 504],
|
|
1784
|
-
methods: ["get", "post", "put", "patch", "delete"]
|
|
1785
|
-
},
|
|
1786
|
-
headers,
|
|
1787
|
-
hooks: {
|
|
1788
|
-
beforeError: [
|
|
1789
|
-
({ error }) => {
|
|
1790
|
-
if (error.name === "TimeoutError") {
|
|
1791
|
-
throw new NetworkError("Request timed out", error);
|
|
1792
|
-
}
|
|
1793
|
-
if (isHTTPError(error)) {
|
|
1794
|
-
throw transformHttpError(error);
|
|
1795
|
-
}
|
|
1796
|
-
throw new NetworkError(error.message, error);
|
|
1797
|
-
}
|
|
1798
|
-
]
|
|
1799
|
-
}
|
|
1800
|
-
});
|
|
1801
|
-
}
|
|
1802
2276
|
|
|
1803
2277
|
// src/client.ts
|
|
1804
2278
|
var RustrakClient = class {
|
|
@@ -1913,6 +2387,6 @@ var RustrakClient = class {
|
|
|
1913
2387
|
}
|
|
1914
2388
|
};
|
|
1915
2389
|
|
|
1916
|
-
export {
|
|
2390
|
+
export { Err, FIELD_ERROR_CODES, NETWORK_ERROR_MESSAGE, Ok, RustrakClient, SERVER_ERROR_MESSAGE, TIMEOUT_ERROR_MESSAGE, isRetryable, mapResult, unwrap, unwrapOr };
|
|
1917
2391
|
//# sourceMappingURL=index.js.map
|
|
1918
2392
|
//# sourceMappingURL=index.js.map
|