@belvador/api 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { OPERATIONS, type OperationId, type OperationMeta, type OperationParams } from "./operations.gen.js";
2
- export declare const CLIENT_VERSION = "0.2.0";
2
+ export declare const CLIENT_VERSION = "0.3.0";
3
3
  export declare const DEFAULT_BASE_URL = "https://api.belvador.dev";
4
4
  /** Personal (`blv_pat_`), service (`blv_svc_`) or OAuth access (`blv_oat_`) token. */
5
5
  export declare const TOKEN_PATTERN: RegExp;
package/dist/client.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { BelvadorApiError, errorFrom } from "./errors.js";
2
2
  import { nodeErrorWord, nodeReadRequest, nodeRefusal, nodeWindowMissing, NODE_REFUSAL_STATUS, reshapeNodeAnswer, } from "./node-wire.gen.js";
3
3
  import { OPERATIONS } from "./operations.gen.js";
4
- export const CLIENT_VERSION = "0.2.0";
4
+ export const CLIENT_VERSION = "0.3.0";
5
5
  export const DEFAULT_BASE_URL = "https://api.belvador.dev";
6
6
  /** Personal (`blv_pat_`), service (`blv_svc_`) or OAuth access (`blv_oat_`) token. */
7
7
  export const TOKEN_PATTERN = /^blv_(pat|svc|oat)_[A-Za-z0-9_-]{20,200}$/;
@@ -170,7 +170,9 @@ class Client {
170
170
  method: meta.method,
171
171
  headers,
172
172
  ...(parts.body !== undefined ? { body: JSON.stringify(parts.body) } : {}),
173
- }, meta.method === "GET" || !!opts.idempotencyKey, opts.signal);
173
+ },
174
+ // Reads are safe to retry, including a read that posts a JSON body (a backtest).
175
+ meta.method === "GET" || meta.scope.endsWith(":read") || !!opts.idempotencyKey, opts.signal);
174
176
  this.#noteScopes(res);
175
177
  if (res.status === 401)
176
178
  this.#ticket = null;
@@ -26,6 +26,9 @@ export declare const NODE_REFUSAL_STATUS: {
26
26
  readonly QUERY_WINDOW_REQUIRED: 400;
27
27
  readonly QUERY_WINDOW_TOO_LARGE: 400;
28
28
  readonly INVALID_INPUT: 400;
29
+ readonly OCCURRENCE_LIMIT: 409;
30
+ readonly OCCURRENCE_DUE_INVALID: 400;
31
+ readonly MONITOR_SELECTOR_INVALID: 400;
29
32
  readonly UNAVAILABLE: 503;
30
33
  };
31
34
  export type NodeRefusalCode = keyof typeof NODE_REFUSAL_STATUS;
@@ -66,6 +66,9 @@ export const NODE_REFUSAL_STATUS = {
66
66
  QUERY_WINDOW_REQUIRED: 400,
67
67
  QUERY_WINDOW_TOO_LARGE: 400,
68
68
  INVALID_INPUT: 400,
69
+ OCCURRENCE_LIMIT: 409,
70
+ OCCURRENCE_DUE_INVALID: 400,
71
+ MONITOR_SELECTOR_INVALID: 400,
69
72
  UNAVAILABLE: 503,
70
73
  };
71
74
  const refuse = (code, extra = {}) => ({
@@ -85,6 +88,14 @@ export function nodeRefusal(status, error) {
85
88
  return refuse("NODE_BUSY", { retryAfter: 2 });
86
89
  if (error === "query_timeout")
87
90
  return refuse("QUERY_TIMEOUT");
91
+ if (error === "occurrence_limit")
92
+ return refuse("OCCURRENCE_LIMIT");
93
+ if (error === "due_out_of_range")
94
+ return refuse("OCCURRENCE_DUE_INVALID");
95
+ if (error === "selector_invalid")
96
+ return refuse("MONITOR_SELECTOR_INVALID");
97
+ if (error === "unknown_monitor")
98
+ return refuse("NOT_FOUND");
88
99
  if (status === 503 || status === 504 || status === 502)
89
100
  return refuse("NODE_OFFLINE");
90
101
  if (status === 400 || status === 422) {
@@ -283,6 +283,132 @@ export declare const OPERATIONS: {
283
283
  };
284
284
  readonly defaults: {};
285
285
  };
286
+ readonly "incident_rules.delete": {
287
+ readonly method: "DELETE";
288
+ readonly path: "/v1/incident-rules/{rule_id}";
289
+ readonly pathParams: readonly ["rule_id"];
290
+ readonly scope: "alerts:write";
291
+ readonly kind: "write";
292
+ readonly sensitivity: "normal";
293
+ readonly idempotency: "none";
294
+ readonly ifMatch: false;
295
+ readonly paged: false;
296
+ readonly status: 200;
297
+ readonly node: null;
298
+ readonly defaults: {};
299
+ };
300
+ readonly "incident_rules.list": {
301
+ readonly method: "GET";
302
+ readonly path: "/v1/incident-rules";
303
+ readonly pathParams: readonly [];
304
+ readonly scope: "alerts:read";
305
+ readonly kind: "read";
306
+ readonly sensitivity: "normal";
307
+ readonly idempotency: "none";
308
+ readonly ifMatch: false;
309
+ readonly paged: false;
310
+ readonly status: 200;
311
+ readonly node: null;
312
+ readonly defaults: {};
313
+ };
314
+ readonly "incident_rules.save": {
315
+ readonly method: "POST";
316
+ readonly path: "/v1/incident-rules";
317
+ readonly pathParams: readonly [];
318
+ readonly scope: "alerts:write";
319
+ readonly kind: "write";
320
+ readonly sensitivity: "normal";
321
+ readonly idempotency: "optional";
322
+ readonly ifMatch: false;
323
+ readonly paged: false;
324
+ readonly status: 200;
325
+ readonly node: null;
326
+ readonly defaults: {};
327
+ };
328
+ readonly "incidents.acknowledge": {
329
+ readonly method: "POST";
330
+ readonly path: "/v1/incidents/{incident_id}/ack";
331
+ readonly pathParams: readonly ["incident_id"];
332
+ readonly scope: "incidents:write";
333
+ readonly kind: "write";
334
+ readonly sensitivity: "normal";
335
+ readonly idempotency: "optional";
336
+ readonly ifMatch: false;
337
+ readonly paged: false;
338
+ readonly status: 200;
339
+ readonly node: null;
340
+ readonly defaults: {};
341
+ };
342
+ readonly "incidents.get": {
343
+ readonly method: "GET";
344
+ readonly path: "/v1/incidents/{incident_id}";
345
+ readonly pathParams: readonly ["incident_id"];
346
+ readonly scope: "incidents:read";
347
+ readonly kind: "read";
348
+ readonly sensitivity: "normal";
349
+ readonly idempotency: "none";
350
+ readonly ifMatch: false;
351
+ readonly paged: false;
352
+ readonly status: 200;
353
+ readonly node: null;
354
+ readonly defaults: {};
355
+ };
356
+ readonly "incidents.list": {
357
+ readonly method: "GET";
358
+ readonly path: "/v1/incidents";
359
+ readonly pathParams: readonly [];
360
+ readonly scope: "incidents:read";
361
+ readonly kind: "read";
362
+ readonly sensitivity: "normal";
363
+ readonly idempotency: "none";
364
+ readonly ifMatch: false;
365
+ readonly paged: true;
366
+ readonly status: 200;
367
+ readonly node: null;
368
+ readonly defaults: {};
369
+ };
370
+ readonly "incidents.note": {
371
+ readonly method: "POST";
372
+ readonly path: "/v1/incidents/{incident_id}/notes";
373
+ readonly pathParams: readonly ["incident_id"];
374
+ readonly scope: "incidents:write";
375
+ readonly kind: "write";
376
+ readonly sensitivity: "normal";
377
+ readonly idempotency: "optional";
378
+ readonly ifMatch: false;
379
+ readonly paged: false;
380
+ readonly status: 200;
381
+ readonly node: null;
382
+ readonly defaults: {};
383
+ };
384
+ readonly "incidents.resolve": {
385
+ readonly method: "POST";
386
+ readonly path: "/v1/incidents/{incident_id}/resolve";
387
+ readonly pathParams: readonly ["incident_id"];
388
+ readonly scope: "incidents:write";
389
+ readonly kind: "write";
390
+ readonly sensitivity: "normal";
391
+ readonly idempotency: "optional";
392
+ readonly ifMatch: false;
393
+ readonly paged: false;
394
+ readonly status: 200;
395
+ readonly node: null;
396
+ readonly defaults: {};
397
+ };
398
+ readonly "incidents.update": {
399
+ readonly method: "PATCH";
400
+ readonly path: "/v1/incidents/{incident_id}";
401
+ readonly pathParams: readonly ["incident_id"];
402
+ readonly scope: "incidents:write";
403
+ readonly kind: "write";
404
+ readonly sensitivity: "normal";
405
+ readonly idempotency: "none";
406
+ readonly ifMatch: false;
407
+ readonly paged: false;
408
+ readonly status: 200;
409
+ readonly node: null;
410
+ readonly defaults: {};
411
+ };
286
412
  readonly "ingest_keys.create": {
287
413
  readonly method: "POST";
288
414
  readonly path: "/v1/projects/{project_id}/ingest-keys";
@@ -520,6 +646,62 @@ export declare const OPERATIONS: {
520
646
  };
521
647
  readonly defaults: {};
522
648
  };
649
+ readonly "maintenance.create": {
650
+ readonly method: "POST";
651
+ readonly path: "/v1/maintenance-windows";
652
+ readonly pathParams: readonly [];
653
+ readonly scope: "monitors:write";
654
+ readonly kind: "write";
655
+ readonly sensitivity: "normal";
656
+ readonly idempotency: "required";
657
+ readonly ifMatch: false;
658
+ readonly paged: false;
659
+ readonly status: 201;
660
+ readonly node: null;
661
+ readonly defaults: {};
662
+ };
663
+ readonly "maintenance.delete": {
664
+ readonly method: "DELETE";
665
+ readonly path: "/v1/maintenance-windows/{window_id}";
666
+ readonly pathParams: readonly ["window_id"];
667
+ readonly scope: "monitors:write";
668
+ readonly kind: "write";
669
+ readonly sensitivity: "normal";
670
+ readonly idempotency: "none";
671
+ readonly ifMatch: false;
672
+ readonly paged: false;
673
+ readonly status: 200;
674
+ readonly node: null;
675
+ readonly defaults: {};
676
+ };
677
+ readonly "maintenance.list": {
678
+ readonly method: "GET";
679
+ readonly path: "/v1/maintenance-windows";
680
+ readonly pathParams: readonly [];
681
+ readonly scope: "monitors:read";
682
+ readonly kind: "read";
683
+ readonly sensitivity: "normal";
684
+ readonly idempotency: "none";
685
+ readonly ifMatch: false;
686
+ readonly paged: true;
687
+ readonly status: 200;
688
+ readonly node: null;
689
+ readonly defaults: {};
690
+ };
691
+ readonly "maintenance.update": {
692
+ readonly method: "PATCH";
693
+ readonly path: "/v1/maintenance-windows/{window_id}";
694
+ readonly pathParams: readonly ["window_id"];
695
+ readonly scope: "monitors:write";
696
+ readonly kind: "write";
697
+ readonly sensitivity: "normal";
698
+ readonly idempotency: "none";
699
+ readonly ifMatch: false;
700
+ readonly paged: false;
701
+ readonly status: 200;
702
+ readonly node: null;
703
+ readonly defaults: {};
704
+ };
523
705
  readonly "members.change_role": {
524
706
  readonly method: "PATCH";
525
707
  readonly path: "/v1/members/{user_id}";
@@ -562,6 +744,22 @@ export declare const OPERATIONS: {
562
744
  readonly node: null;
563
745
  readonly defaults: {};
564
746
  };
747
+ readonly "monitors.backtest": {
748
+ readonly method: "POST";
749
+ readonly path: "/v1/monitors/backtest";
750
+ readonly pathParams: readonly [];
751
+ readonly scope: "monitors:read";
752
+ readonly kind: "node";
753
+ readonly sensitivity: "normal";
754
+ readonly idempotency: "none";
755
+ readonly ifMatch: false;
756
+ readonly paged: false;
757
+ readonly status: 200;
758
+ readonly node: {
759
+ readonly path: "q/monitors/backtest";
760
+ };
761
+ readonly defaults: {};
762
+ };
565
763
  readonly "monitors.create": {
566
764
  readonly method: "POST";
567
765
  readonly path: "/v1/monitors";
@@ -632,6 +830,23 @@ export declare const OPERATIONS: {
632
830
  readonly node: null;
633
831
  readonly defaults: {};
634
832
  };
833
+ readonly "monitors.occurrences": {
834
+ readonly method: "GET";
835
+ readonly path: "/v1/monitors/{monitor_id}/occurrences";
836
+ readonly pathParams: readonly ["monitor_id"];
837
+ readonly scope: "monitors:read";
838
+ readonly kind: "node";
839
+ readonly sensitivity: "normal";
840
+ readonly idempotency: "none";
841
+ readonly ifMatch: false;
842
+ readonly paged: true;
843
+ readonly status: 200;
844
+ readonly node: {
845
+ readonly path: "q/monitors/{monitor_id}/occurrences";
846
+ readonly list: "occurrences";
847
+ };
848
+ readonly defaults: {};
849
+ };
635
850
  readonly "monitors.set_muted": {
636
851
  readonly method: "PUT";
637
852
  readonly path: "/v1/monitors/{monitor_id}/mute";
@@ -660,6 +875,22 @@ export declare const OPERATIONS: {
660
875
  readonly node: null;
661
876
  readonly defaults: {};
662
877
  };
878
+ readonly "monitors.signal": {
879
+ readonly method: "GET";
880
+ readonly path: "/v1/monitors/{monitor_id}/signal";
881
+ readonly pathParams: readonly ["monitor_id"];
882
+ readonly scope: "monitors:read";
883
+ readonly kind: "node";
884
+ readonly sensitivity: "normal";
885
+ readonly idempotency: "none";
886
+ readonly ifMatch: false;
887
+ readonly paged: false;
888
+ readonly status: 200;
889
+ readonly node: {
890
+ readonly path: "q/monitors/{monitor_id}/signal";
891
+ };
892
+ readonly defaults: {};
893
+ };
663
894
  readonly "monitors.update": {
664
895
  readonly method: "PATCH";
665
896
  readonly path: "/v1/monitors/{monitor_id}";
@@ -828,6 +1059,80 @@ export declare const OPERATIONS: {
828
1059
  readonly node: null;
829
1060
  readonly defaults: {};
830
1061
  };
1062
+ readonly "notification_policies.delete": {
1063
+ readonly method: "DELETE";
1064
+ readonly path: "/v1/notification-policies/{policy_id}";
1065
+ readonly pathParams: readonly ["policy_id"];
1066
+ readonly scope: "alerts:write";
1067
+ readonly kind: "write";
1068
+ readonly sensitivity: "normal";
1069
+ readonly idempotency: "none";
1070
+ readonly ifMatch: false;
1071
+ readonly paged: false;
1072
+ readonly status: 200;
1073
+ readonly node: null;
1074
+ readonly defaults: {};
1075
+ };
1076
+ readonly "notification_policies.list": {
1077
+ readonly method: "GET";
1078
+ readonly path: "/v1/notification-policies";
1079
+ readonly pathParams: readonly [];
1080
+ readonly scope: "alerts:read";
1081
+ readonly kind: "read";
1082
+ readonly sensitivity: "normal";
1083
+ readonly idempotency: "none";
1084
+ readonly ifMatch: false;
1085
+ readonly paged: false;
1086
+ readonly status: 200;
1087
+ readonly node: null;
1088
+ readonly defaults: {};
1089
+ };
1090
+ readonly "notification_policies.save": {
1091
+ readonly method: "POST";
1092
+ readonly path: "/v1/notification-policies";
1093
+ readonly pathParams: readonly [];
1094
+ readonly scope: "alerts:write";
1095
+ readonly kind: "write";
1096
+ readonly sensitivity: "normal";
1097
+ readonly idempotency: "optional";
1098
+ readonly ifMatch: false;
1099
+ readonly paged: false;
1100
+ readonly status: 200;
1101
+ readonly node: null;
1102
+ readonly defaults: {};
1103
+ };
1104
+ readonly "occurrences.cancel": {
1105
+ readonly method: "DELETE";
1106
+ readonly path: "/v1/occurrences/{monitor_slug}/{occurrence_id}";
1107
+ readonly pathParams: readonly ["monitor_slug", "occurrence_id"];
1108
+ readonly scope: "monitors:write";
1109
+ readonly kind: "node";
1110
+ readonly sensitivity: "normal";
1111
+ readonly idempotency: "none";
1112
+ readonly ifMatch: false;
1113
+ readonly paged: false;
1114
+ readonly status: 200;
1115
+ readonly node: {
1116
+ readonly path: "w/monitors/occurrences/cancel";
1117
+ };
1118
+ readonly defaults: {};
1119
+ };
1120
+ readonly "occurrences.create": {
1121
+ readonly method: "POST";
1122
+ readonly path: "/v1/occurrences";
1123
+ readonly pathParams: readonly [];
1124
+ readonly scope: "monitors:write";
1125
+ readonly kind: "node";
1126
+ readonly sensitivity: "normal";
1127
+ readonly idempotency: "none";
1128
+ readonly ifMatch: false;
1129
+ readonly paged: false;
1130
+ readonly status: 200;
1131
+ readonly node: {
1132
+ readonly path: "w/monitors/occurrences";
1133
+ };
1134
+ readonly defaults: {};
1135
+ };
831
1136
  readonly "ping_tokens.reset": {
832
1137
  readonly method: "POST";
833
1138
  readonly path: "/v1/monitors/{monitor_id}/ping-tokens/reset";
@@ -1559,8 +1864,9 @@ export interface OperationParams {
1559
1864
  is_enabled: boolean;
1560
1865
  notify_all: boolean;
1561
1866
  destination_ids: Array<string>;
1562
- monitor_kinds?: Array<"missed" | "failed" | "timeout" | "unknown">;
1867
+ monitor_kinds?: Array<"missed" | "failed" | "timeout" | "unknown" | "count" | "assertion" | "deadline" | "stalled">;
1563
1868
  slo_severities?: Array<"page" | "ticket">;
1869
+ policy_id?: string | null;
1564
1870
  };
1565
1871
  /** Turn a rule on or off. */
1566
1872
  "alert_rules.set_enabled": {
@@ -1583,9 +1889,9 @@ export interface OperationParams {
1583
1889
  limit?: number;
1584
1890
  cursor?: string;
1585
1891
  };
1586
- /** Start adding a Telegram, Slack, email or webhook destination; a person enters its secrets. */
1892
+ /** Start adding a Telegram, Slack, email, webhook, PagerDuty or incident.io destination; a person enters its secrets. */
1587
1893
  "destinations.create": {
1588
- provider: "telegram" | "slack" | "email" | "webhook";
1894
+ provider: "telegram" | "slack" | "email" | "webhook" | "pagerduty" | "incident_io";
1589
1895
  name: string;
1590
1896
  project_ids: Array<number>;
1591
1897
  locale: "vi" | "en";
@@ -1632,6 +1938,58 @@ export interface OperationParams {
1632
1938
  issue_id: number;
1633
1939
  cursor?: string;
1634
1940
  };
1941
+ /** Delete an incident rule; open incidents stay as they are. */
1942
+ "incident_rules.delete": {
1943
+ rule_id: string;
1944
+ };
1945
+ /** Rules that turn alerts into incidents: match, grouping, window and recovery wait. */
1946
+ "incident_rules.list": Record<string, never>;
1947
+ /** Create (`rule_id` null) or replace an incident rule. Admins. */
1948
+ "incident_rules.save": {
1949
+ rule_id: string | null;
1950
+ input: {
1951
+ name: string;
1952
+ is_enabled: boolean;
1953
+ position?: number;
1954
+ project_ids: Array<number>;
1955
+ environments: Array<string>;
1956
+ kinds: Array<"monitor" | "uptime" | "slo" | "issue">;
1957
+ min_severity: "critical" | "high" | "medium" | "low";
1958
+ group_by: "monitor" | "project_env";
1959
+ window_minutes: number;
1960
+ recovery_hold_minutes: number;
1961
+ };
1962
+ };
1963
+ /** Take ownership of an open incident; stops its escalation. Members and up. */
1964
+ "incidents.acknowledge": {
1965
+ incident_id: string;
1966
+ };
1967
+ /** One incident with its linked alerts, timeline (notes included) and message deliveries (`accepted` = the provider took it; only a person acknowledges). */
1968
+ "incidents.get": {
1969
+ incident_id: string;
1970
+ };
1971
+ /** Incidents (INC-n): state, severity and peak, owner, linked alert count; newest first. */
1972
+ "incidents.list": {
1973
+ limit?: number;
1974
+ cursor?: string;
1975
+ state?: "active" | "resolved" | "all";
1976
+ };
1977
+ /** Add a note to the incident timeline (plain text, ≤ 2,000 characters). */
1978
+ "incidents.note": {
1979
+ incident_id: string;
1980
+ note: string;
1981
+ };
1982
+ /** Close an incident by hand with a reason (1–500 characters); it is labelled "not verified". */
1983
+ "incidents.resolve": {
1984
+ incident_id: string;
1985
+ note: string;
1986
+ };
1987
+ /** Rename an incident or assign it to a member (`owner_user_id: null` clears it). */
1988
+ "incidents.update": {
1989
+ incident_id: string;
1990
+ title?: string;
1991
+ owner_user_id?: string | null;
1992
+ };
1635
1993
  /** Create an OTLP ingest key (`bvi_`). */
1636
1994
  "ingest_keys.create": {
1637
1995
  project_id: number;
@@ -1780,6 +2138,59 @@ export interface OperationParams {
1780
2138
  order?: "asc" | "desc";
1781
2139
  cursor?: string;
1782
2140
  };
2141
+ /** Hold back monitor notifications during a one-off or weekly window (workspace, projects or monitors); monitors keep evaluating and recording. */
2142
+ "maintenance.create": Record<string, never> & ((Record<string, unknown>) & ({
2143
+ kind: "once";
2144
+ starts_at: string;
2145
+ scope: "workspace" | "project" | "monitor";
2146
+ project_ids: Array<number>;
2147
+ monitor_ids: Array<string>;
2148
+ timezone: string;
2149
+ duration_minutes: number;
2150
+ reason: string;
2151
+ } | {
2152
+ kind: "weekly";
2153
+ weekdays: Array<number>;
2154
+ start_time: string;
2155
+ scope: "workspace" | "project" | "monitor";
2156
+ project_ids: Array<number>;
2157
+ monitor_ids: Array<string>;
2158
+ timezone: string;
2159
+ duration_minutes: number;
2160
+ reason: string;
2161
+ }));
2162
+ /** Delete a maintenance window; notifications resume at once. */
2163
+ "maintenance.delete": {
2164
+ window_id: string;
2165
+ };
2166
+ /** Maintenance windows and whether each is active now. */
2167
+ "maintenance.list": {
2168
+ limit?: number;
2169
+ cursor?: string;
2170
+ };
2171
+ /** Replace a maintenance window's scope, times and reason. */
2172
+ "maintenance.update": {
2173
+ window_id: string;
2174
+ } & ((Record<string, unknown>) & ({
2175
+ kind: "once";
2176
+ starts_at: string;
2177
+ scope: "workspace" | "project" | "monitor";
2178
+ project_ids: Array<number>;
2179
+ monitor_ids: Array<string>;
2180
+ timezone: string;
2181
+ duration_minutes: number;
2182
+ reason: string;
2183
+ } | {
2184
+ kind: "weekly";
2185
+ weekdays: Array<number>;
2186
+ start_time: string;
2187
+ scope: "workspace" | "project" | "monitor";
2188
+ project_ids: Array<number>;
2189
+ monitor_ids: Array<string>;
2190
+ timezone: string;
2191
+ duration_minutes: number;
2192
+ reason: string;
2193
+ }));
1783
2194
  /** Change a member's role. */
1784
2195
  "members.change_role": {
1785
2196
  user_id: string;
@@ -1794,10 +2205,55 @@ export interface OperationParams {
1794
2205
  "members.remove": {
1795
2206
  user_id: string;
1796
2207
  };
1797
- /** Create a cron or heartbeat monitor. A heartbeat monitor returns its ping token once. */
1798
- "monitors.create": {
2208
+ /** How often count/assertion settings would have alerted over the last 30 days of stored data (same rule as the live evaluator). */
2209
+ "monitors.backtest": {
2210
+ project: number;
2211
+ settings: {
2212
+ type: "count";
2213
+ selector: {
2214
+ signal: "event" | "check_in" | "span" | "log";
2215
+ match: Array<{
2216
+ key: string;
2217
+ op: "eq" | "ne" | "lt" | "le" | "gt" | "ge" | "in" | "exists" | "not_exists" | "prefix";
2218
+ value?: string | number | boolean | Array<string>;
2219
+ }>;
2220
+ };
2221
+ window_minutes: number;
2222
+ comparator: "lt" | "gt" | "outside";
2223
+ threshold: number;
2224
+ threshold_high: number | null;
2225
+ missing_data: "alert" | "keep" | "resolve" | "treat_as_zero";
2226
+ environments: Array<string>;
2227
+ severity: "critical" | "high" | "medium" | "low";
2228
+ recovery_threshold: number;
2229
+ } | {
2230
+ type: "assertion";
2231
+ selector: {
2232
+ signal: "event" | "check_in" | "span" | "log";
2233
+ match: Array<{
2234
+ key: string;
2235
+ op: "eq" | "ne" | "lt" | "le" | "gt" | "ge" | "in" | "exists" | "not_exists" | "prefix";
2236
+ value?: string | number | boolean | Array<string>;
2237
+ }>;
2238
+ };
2239
+ assert: Array<{
2240
+ key: string;
2241
+ op: "eq" | "ne" | "lt" | "le" | "gt" | "ge" | "in" | "exists" | "not_exists" | "prefix";
2242
+ value?: string | number | boolean | Array<string>;
2243
+ }>;
2244
+ window_minutes: number;
2245
+ threshold: number;
2246
+ missing_data: "alert" | "keep" | "resolve" | "treat_as_zero";
2247
+ environments: Array<string>;
2248
+ severity: "critical" | "high" | "medium" | "low";
2249
+ recovery_threshold: number;
2250
+ };
2251
+ };
2252
+ /** Create a cron or heartbeat monitor, or a signal monitor (`settings.type`: count, assertion, deadline, progress). A heartbeat monitor returns its ping token once. */
2253
+ "monitors.create": Record<string, never> & (({
1799
2254
  project_id: number;
1800
2255
  slug: string;
2256
+ }) & ({
1801
2257
  name: string;
1802
2258
  mode?: "check_in" | "heartbeat";
1803
2259
  kind: "crontab" | "interval" | "on_demand";
@@ -1815,7 +2271,62 @@ export interface OperationParams {
1815
2271
  failure_threshold: number;
1816
2272
  recovery_threshold: number;
1817
2273
  expected_environments: Array<string>;
1818
- };
2274
+ } | {
2275
+ name: string;
2276
+ settings: {
2277
+ type: "count";
2278
+ selector: {
2279
+ signal: "event" | "check_in" | "span" | "log";
2280
+ match: Array<{
2281
+ key: string;
2282
+ op: "eq" | "ne" | "lt" | "le" | "gt" | "ge" | "in" | "exists" | "not_exists" | "prefix";
2283
+ value?: string | number | boolean | Array<string>;
2284
+ }>;
2285
+ };
2286
+ window_minutes: number;
2287
+ comparator: "lt" | "gt" | "outside";
2288
+ threshold: number;
2289
+ threshold_high: number | null;
2290
+ missing_data: "alert" | "keep" | "resolve" | "treat_as_zero";
2291
+ environments: Array<string>;
2292
+ severity: "critical" | "high" | "medium" | "low";
2293
+ recovery_threshold: number;
2294
+ } | {
2295
+ type: "assertion";
2296
+ selector: {
2297
+ signal: "event" | "check_in" | "span" | "log";
2298
+ match: Array<{
2299
+ key: string;
2300
+ op: "eq" | "ne" | "lt" | "le" | "gt" | "ge" | "in" | "exists" | "not_exists" | "prefix";
2301
+ value?: string | number | boolean | Array<string>;
2302
+ }>;
2303
+ };
2304
+ assert: Array<{
2305
+ key: string;
2306
+ op: "eq" | "ne" | "lt" | "le" | "gt" | "ge" | "in" | "exists" | "not_exists" | "prefix";
2307
+ value?: string | number | boolean | Array<string>;
2308
+ }>;
2309
+ window_minutes: number;
2310
+ threshold: number;
2311
+ missing_data: "alert" | "keep" | "resolve" | "treat_as_zero";
2312
+ environments: Array<string>;
2313
+ severity: "critical" | "high" | "medium" | "low";
2314
+ recovery_threshold: number;
2315
+ } | {
2316
+ type: "deadline";
2317
+ occurrence_grace_minutes: number;
2318
+ progress_timeout_minutes: number | null;
2319
+ environments: Array<string>;
2320
+ severity: "critical" | "high" | "medium" | "low";
2321
+ recovery_threshold: number;
2322
+ } | {
2323
+ type: "progress";
2324
+ progress_timeout_minutes: number;
2325
+ environments: Array<string>;
2326
+ severity: "critical" | "high" | "medium" | "low";
2327
+ recovery_threshold: number;
2328
+ };
2329
+ }));
1819
2330
  /** Delete a monitor; `confirm_slug` must repeat its slug. */
1820
2331
  "monitors.delete": {
1821
2332
  monitor_id: string;
@@ -1829,11 +2340,19 @@ export interface OperationParams {
1829
2340
  "monitors.get": {
1830
2341
  monitor_id: string;
1831
2342
  };
1832
- /** Cron and heartbeat monitors. */
2343
+ /** Monitors of every type (cron, heartbeat, count, assertion, deadline, progress); `type` narrows the list. */
1833
2344
  "monitors.list": {
1834
2345
  limit?: number;
1835
2346
  cursor?: string;
1836
2347
  project_id?: number;
2348
+ type?: "job" | "count" | "assertion" | "deadline" | "progress";
2349
+ };
2350
+ /** Occurrences of a deadline monitor (pending, on time, late, missed, failed, cancelled), newest due first. */
2351
+ "monitors.occurrences": {
2352
+ monitor_id: string;
2353
+ state?: "open" | "finished" | "all";
2354
+ environment?: string;
2355
+ cursor?: string;
1837
2356
  };
1838
2357
  /** Mute or unmute a monitor's alerts. */
1839
2358
  "monitors.set_muted": {
@@ -1845,9 +2364,15 @@ export interface OperationParams {
1845
2364
  monitor_id: string;
1846
2365
  until: string | "forever" | null;
1847
2366
  };
1848
- /** Replace a monitor's schedule, margins and thresholds. */
2367
+ /** A signal monitor's minute counts (count/assertion) for up to 24 h per environment, and its open runs (progress, deadline). */
2368
+ "monitors.signal": {
2369
+ monitor_id: string;
2370
+ hours?: number;
2371
+ };
2372
+ /** Replace a monitor's schedule, margins and thresholds, or a signal monitor's settings (the type stays). */
1849
2373
  "monitors.update": {
1850
2374
  monitor_id: string;
2375
+ } & ((Record<string, unknown>) & ({
1851
2376
  name: string;
1852
2377
  mode?: "check_in" | "heartbeat";
1853
2378
  kind: "crontab" | "interval" | "on_demand";
@@ -1865,7 +2390,62 @@ export interface OperationParams {
1865
2390
  failure_threshold: number;
1866
2391
  recovery_threshold: number;
1867
2392
  expected_environments: Array<string>;
1868
- };
2393
+ } | {
2394
+ name: string;
2395
+ settings: {
2396
+ type: "count";
2397
+ selector: {
2398
+ signal: "event" | "check_in" | "span" | "log";
2399
+ match: Array<{
2400
+ key: string;
2401
+ op: "eq" | "ne" | "lt" | "le" | "gt" | "ge" | "in" | "exists" | "not_exists" | "prefix";
2402
+ value?: string | number | boolean | Array<string>;
2403
+ }>;
2404
+ };
2405
+ window_minutes: number;
2406
+ comparator: "lt" | "gt" | "outside";
2407
+ threshold: number;
2408
+ threshold_high: number | null;
2409
+ missing_data: "alert" | "keep" | "resolve" | "treat_as_zero";
2410
+ environments: Array<string>;
2411
+ severity: "critical" | "high" | "medium" | "low";
2412
+ recovery_threshold: number;
2413
+ } | {
2414
+ type: "assertion";
2415
+ selector: {
2416
+ signal: "event" | "check_in" | "span" | "log";
2417
+ match: Array<{
2418
+ key: string;
2419
+ op: "eq" | "ne" | "lt" | "le" | "gt" | "ge" | "in" | "exists" | "not_exists" | "prefix";
2420
+ value?: string | number | boolean | Array<string>;
2421
+ }>;
2422
+ };
2423
+ assert: Array<{
2424
+ key: string;
2425
+ op: "eq" | "ne" | "lt" | "le" | "gt" | "ge" | "in" | "exists" | "not_exists" | "prefix";
2426
+ value?: string | number | boolean | Array<string>;
2427
+ }>;
2428
+ window_minutes: number;
2429
+ threshold: number;
2430
+ missing_data: "alert" | "keep" | "resolve" | "treat_as_zero";
2431
+ environments: Array<string>;
2432
+ severity: "critical" | "high" | "medium" | "low";
2433
+ recovery_threshold: number;
2434
+ } | {
2435
+ type: "deadline";
2436
+ occurrence_grace_minutes: number;
2437
+ progress_timeout_minutes: number | null;
2438
+ environments: Array<string>;
2439
+ severity: "critical" | "high" | "medium" | "low";
2440
+ recovery_threshold: number;
2441
+ } | {
2442
+ type: "progress";
2443
+ progress_timeout_minutes: number;
2444
+ environments: Array<string>;
2445
+ severity: "critical" | "high" | "medium" | "low";
2446
+ recovery_threshold: number;
2447
+ };
2448
+ }));
1869
2449
  /** Start a backup now (at most once per 10 minutes). */
1870
2450
  "node.backup_now": Record<string, never>;
1871
2451
  /** Cancel a scheduled node deletion. */
@@ -1909,6 +2489,48 @@ export interface OperationParams {
1909
2489
  "node.upgrade": {
1910
2490
  node_id: string;
1911
2491
  };
2492
+ /** Delete a policy (not the default); its rules fall back to the default policy. */
2493
+ "notification_policies.delete": {
2494
+ policy_id: string;
2495
+ };
2496
+ /** Notification policies: quiet hours, digest, fallback chain and escalation. */
2497
+ "notification_policies.list": Record<string, never>;
2498
+ /** Create (`policy_id` null) or replace a policy. Critical and high are never held; chains use existing destinations only. Admins. */
2499
+ "notification_policies.save": {
2500
+ policy_id: string | null;
2501
+ is_default: boolean;
2502
+ input: {
2503
+ name: string;
2504
+ timezone: string;
2505
+ quiet_hours: {
2506
+ days: Array<number>;
2507
+ start: string;
2508
+ end: string;
2509
+ } | null;
2510
+ quiet_severities: Array<"medium" | "low">;
2511
+ digest: "off" | "hourly" | "daily";
2512
+ digest_at: string | null;
2513
+ digest_severities: Array<"medium" | "low">;
2514
+ fallback: Array<string>;
2515
+ escalate_after_minutes: number | null;
2516
+ escalation: Array<string>;
2517
+ };
2518
+ };
2519
+ /** Cancel a pending occurrence (the app moved or dropped the job); a cancelled occurrence never alerts. */
2520
+ "occurrences.cancel": {
2521
+ monitor_slug: string;
2522
+ occurrence_id: string;
2523
+ project: number;
2524
+ environment?: string;
2525
+ };
2526
+ /** Register one run a deadline monitor expects (`due_at` within 35 days); the same id again moves its due time while it is pending. */
2527
+ "occurrences.create": {
2528
+ project: number;
2529
+ monitor_slug: string;
2530
+ occurrence_id: string;
2531
+ due_at: string;
2532
+ environment?: string;
2533
+ };
1912
2534
  /** Suspected leak: stop every live ping token and issue a new one. */
1913
2535
  "ping_tokens.reset": {
1914
2536
  monitor_id: string;
@@ -254,6 +254,132 @@ export const OPERATIONS = {
254
254
  node: { path: "q/issues/{issue_id}/events", list: "events" },
255
255
  defaults: {},
256
256
  },
257
+ "incident_rules.delete": {
258
+ method: "DELETE",
259
+ path: "/v1/incident-rules/{rule_id}",
260
+ pathParams: ["rule_id"],
261
+ scope: "alerts:write",
262
+ kind: "write",
263
+ sensitivity: "normal",
264
+ idempotency: "none",
265
+ ifMatch: false,
266
+ paged: false,
267
+ status: 200,
268
+ node: null,
269
+ defaults: {},
270
+ },
271
+ "incident_rules.list": {
272
+ method: "GET",
273
+ path: "/v1/incident-rules",
274
+ pathParams: [],
275
+ scope: "alerts:read",
276
+ kind: "read",
277
+ sensitivity: "normal",
278
+ idempotency: "none",
279
+ ifMatch: false,
280
+ paged: false,
281
+ status: 200,
282
+ node: null,
283
+ defaults: {},
284
+ },
285
+ "incident_rules.save": {
286
+ method: "POST",
287
+ path: "/v1/incident-rules",
288
+ pathParams: [],
289
+ scope: "alerts:write",
290
+ kind: "write",
291
+ sensitivity: "normal",
292
+ idempotency: "optional",
293
+ ifMatch: false,
294
+ paged: false,
295
+ status: 200,
296
+ node: null,
297
+ defaults: {},
298
+ },
299
+ "incidents.acknowledge": {
300
+ method: "POST",
301
+ path: "/v1/incidents/{incident_id}/ack",
302
+ pathParams: ["incident_id"],
303
+ scope: "incidents:write",
304
+ kind: "write",
305
+ sensitivity: "normal",
306
+ idempotency: "optional",
307
+ ifMatch: false,
308
+ paged: false,
309
+ status: 200,
310
+ node: null,
311
+ defaults: {},
312
+ },
313
+ "incidents.get": {
314
+ method: "GET",
315
+ path: "/v1/incidents/{incident_id}",
316
+ pathParams: ["incident_id"],
317
+ scope: "incidents:read",
318
+ kind: "read",
319
+ sensitivity: "normal",
320
+ idempotency: "none",
321
+ ifMatch: false,
322
+ paged: false,
323
+ status: 200,
324
+ node: null,
325
+ defaults: {},
326
+ },
327
+ "incidents.list": {
328
+ method: "GET",
329
+ path: "/v1/incidents",
330
+ pathParams: [],
331
+ scope: "incidents:read",
332
+ kind: "read",
333
+ sensitivity: "normal",
334
+ idempotency: "none",
335
+ ifMatch: false,
336
+ paged: true,
337
+ status: 200,
338
+ node: null,
339
+ defaults: {},
340
+ },
341
+ "incidents.note": {
342
+ method: "POST",
343
+ path: "/v1/incidents/{incident_id}/notes",
344
+ pathParams: ["incident_id"],
345
+ scope: "incidents:write",
346
+ kind: "write",
347
+ sensitivity: "normal",
348
+ idempotency: "optional",
349
+ ifMatch: false,
350
+ paged: false,
351
+ status: 200,
352
+ node: null,
353
+ defaults: {},
354
+ },
355
+ "incidents.resolve": {
356
+ method: "POST",
357
+ path: "/v1/incidents/{incident_id}/resolve",
358
+ pathParams: ["incident_id"],
359
+ scope: "incidents:write",
360
+ kind: "write",
361
+ sensitivity: "normal",
362
+ idempotency: "optional",
363
+ ifMatch: false,
364
+ paged: false,
365
+ status: 200,
366
+ node: null,
367
+ defaults: {},
368
+ },
369
+ "incidents.update": {
370
+ method: "PATCH",
371
+ path: "/v1/incidents/{incident_id}",
372
+ pathParams: ["incident_id"],
373
+ scope: "incidents:write",
374
+ kind: "write",
375
+ sensitivity: "normal",
376
+ idempotency: "none",
377
+ ifMatch: false,
378
+ paged: false,
379
+ status: 200,
380
+ node: null,
381
+ defaults: {},
382
+ },
257
383
  "ingest_keys.create": {
258
384
  method: "POST",
259
385
  path: "/v1/projects/{project_id}/ingest-keys",
@@ -464,6 +590,62 @@ export const OPERATIONS = {
464
590
  node: { path: "q/logs", list: "logs", window: true },
465
591
  defaults: {},
466
592
  },
593
+ "maintenance.create": {
594
+ method: "POST",
595
+ path: "/v1/maintenance-windows",
596
+ pathParams: [],
597
+ scope: "monitors:write",
598
+ kind: "write",
599
+ sensitivity: "normal",
600
+ idempotency: "required",
601
+ ifMatch: false,
602
+ paged: false,
603
+ status: 201,
604
+ node: null,
605
+ defaults: {},
606
+ },
607
+ "maintenance.delete": {
608
+ method: "DELETE",
609
+ path: "/v1/maintenance-windows/{window_id}",
610
+ pathParams: ["window_id"],
611
+ scope: "monitors:write",
612
+ kind: "write",
613
+ sensitivity: "normal",
614
+ idempotency: "none",
615
+ ifMatch: false,
616
+ paged: false,
617
+ status: 200,
618
+ node: null,
619
+ defaults: {},
620
+ },
621
+ "maintenance.list": {
622
+ method: "GET",
623
+ path: "/v1/maintenance-windows",
624
+ pathParams: [],
625
+ scope: "monitors:read",
626
+ kind: "read",
627
+ sensitivity: "normal",
628
+ idempotency: "none",
629
+ ifMatch: false,
630
+ paged: true,
631
+ status: 200,
632
+ node: null,
633
+ defaults: {},
634
+ },
635
+ "maintenance.update": {
636
+ method: "PATCH",
637
+ path: "/v1/maintenance-windows/{window_id}",
638
+ pathParams: ["window_id"],
639
+ scope: "monitors:write",
640
+ kind: "write",
641
+ sensitivity: "normal",
642
+ idempotency: "none",
643
+ ifMatch: false,
644
+ paged: false,
645
+ status: 200,
646
+ node: null,
647
+ defaults: {},
648
+ },
467
649
  "members.change_role": {
468
650
  method: "PATCH",
469
651
  path: "/v1/members/{user_id}",
@@ -506,6 +688,20 @@ export const OPERATIONS = {
506
688
  node: null,
507
689
  defaults: {},
508
690
  },
691
+ "monitors.backtest": {
692
+ method: "POST",
693
+ path: "/v1/monitors/backtest",
694
+ pathParams: [],
695
+ scope: "monitors:read",
696
+ kind: "node",
697
+ sensitivity: "normal",
698
+ idempotency: "none",
699
+ ifMatch: false,
700
+ paged: false,
701
+ status: 200,
702
+ node: { path: "q/monitors/backtest" },
703
+ defaults: {},
704
+ },
509
705
  "monitors.create": {
510
706
  method: "POST",
511
707
  path: "/v1/monitors",
@@ -576,6 +772,20 @@ export const OPERATIONS = {
576
772
  node: null,
577
773
  defaults: {},
578
774
  },
775
+ "monitors.occurrences": {
776
+ method: "GET",
777
+ path: "/v1/monitors/{monitor_id}/occurrences",
778
+ pathParams: ["monitor_id"],
779
+ scope: "monitors:read",
780
+ kind: "node",
781
+ sensitivity: "normal",
782
+ idempotency: "none",
783
+ ifMatch: false,
784
+ paged: true,
785
+ status: 200,
786
+ node: { path: "q/monitors/{monitor_id}/occurrences", list: "occurrences" },
787
+ defaults: {},
788
+ },
579
789
  "monitors.set_muted": {
580
790
  method: "PUT",
581
791
  path: "/v1/monitors/{monitor_id}/mute",
@@ -604,6 +814,20 @@ export const OPERATIONS = {
604
814
  node: null,
605
815
  defaults: {},
606
816
  },
817
+ "monitors.signal": {
818
+ method: "GET",
819
+ path: "/v1/monitors/{monitor_id}/signal",
820
+ pathParams: ["monitor_id"],
821
+ scope: "monitors:read",
822
+ kind: "node",
823
+ sensitivity: "normal",
824
+ idempotency: "none",
825
+ ifMatch: false,
826
+ paged: false,
827
+ status: 200,
828
+ node: { path: "q/monitors/{monitor_id}/signal" },
829
+ defaults: {},
830
+ },
607
831
  "monitors.update": {
608
832
  method: "PATCH",
609
833
  path: "/v1/monitors/{monitor_id}",
@@ -772,6 +996,76 @@ export const OPERATIONS = {
772
996
  node: null,
773
997
  defaults: {},
774
998
  },
999
+ "notification_policies.delete": {
1000
+ method: "DELETE",
1001
+ path: "/v1/notification-policies/{policy_id}",
1002
+ pathParams: ["policy_id"],
1003
+ scope: "alerts:write",
1004
+ kind: "write",
1005
+ sensitivity: "normal",
1006
+ idempotency: "none",
1007
+ ifMatch: false,
1008
+ paged: false,
1009
+ status: 200,
1010
+ node: null,
1011
+ defaults: {},
1012
+ },
1013
+ "notification_policies.list": {
1014
+ method: "GET",
1015
+ path: "/v1/notification-policies",
1016
+ pathParams: [],
1017
+ scope: "alerts:read",
1018
+ kind: "read",
1019
+ sensitivity: "normal",
1020
+ idempotency: "none",
1021
+ ifMatch: false,
1022
+ paged: false,
1023
+ status: 200,
1024
+ node: null,
1025
+ defaults: {},
1026
+ },
1027
+ "notification_policies.save": {
1028
+ method: "POST",
1029
+ path: "/v1/notification-policies",
1030
+ pathParams: [],
1031
+ scope: "alerts:write",
1032
+ kind: "write",
1033
+ sensitivity: "normal",
1034
+ idempotency: "optional",
1035
+ ifMatch: false,
1036
+ paged: false,
1037
+ status: 200,
1038
+ node: null,
1039
+ defaults: {},
1040
+ },
1041
+ "occurrences.cancel": {
1042
+ method: "DELETE",
1043
+ path: "/v1/occurrences/{monitor_slug}/{occurrence_id}",
1044
+ pathParams: ["monitor_slug", "occurrence_id"],
1045
+ scope: "monitors:write",
1046
+ kind: "node",
1047
+ sensitivity: "normal",
1048
+ idempotency: "none",
1049
+ ifMatch: false,
1050
+ paged: false,
1051
+ status: 200,
1052
+ node: { path: "w/monitors/occurrences/cancel" },
1053
+ defaults: {},
1054
+ },
1055
+ "occurrences.create": {
1056
+ method: "POST",
1057
+ path: "/v1/occurrences",
1058
+ pathParams: [],
1059
+ scope: "monitors:write",
1060
+ kind: "node",
1061
+ sensitivity: "normal",
1062
+ idempotency: "none",
1063
+ ifMatch: false,
1064
+ paged: false,
1065
+ status: 200,
1066
+ node: { path: "w/monitors/occurrences" },
1067
+ defaults: {},
1068
+ },
775
1069
  "ping_tokens.reset": {
776
1070
  method: "POST",
777
1071
  path: "/v1/monitors/{monitor_id}/ping-tokens/reset",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@belvador/api",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Typed client for the Belvador API (api.belvador.dev, OpenAPI 3.1): every operation by operationId, problem+json errors, retries, and node reads straight from the edge with short-lived tickets.",
5
5
  "license": "MIT",
6
6
  "author": "Belvador (https://belvador.dev)",