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