@belvador/api 0.1.0 → 0.2.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 +4 -0
- package/dist/client.d.ts +1 -1
- package/dist/client.js +1 -1
- package/dist/errors.d.ts +15 -0
- package/dist/errors.js +35 -0
- package/dist/index.d.ts +1 -1
- package/dist/operations.gen.d.ts +18 -0
- package/dist/operations.gen.js +14 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -44,6 +44,10 @@ try {
|
|
|
44
44
|
Create one in Settings → API tokens. The client never puts the token in an error message.
|
|
45
45
|
- **Errors** are RFC 9457 problem+json, surfaced as `BelvadorApiError` with a stable `code`,
|
|
46
46
|
`status`, `scope` (for `API_SCOPE_INSUFFICIENT`), `retryAfter` and `requestId`.
|
|
47
|
+
- **Approvals**: sensitive operations (keys, members, tokens, alert destinations, node changes) throw
|
|
48
|
+
`APPROVAL_REQUIRED` with `e.approval` = `{ id, url, expiresAt, state }`. A person opens `url` on
|
|
49
|
+
belvador.dev and approves with their authenticator code; call again with the same `idempotencyKey`
|
|
50
|
+
(or `call("approvals.get", { approval_id })`) to read the outcome. Only https `/approve/<id>` links are kept.
|
|
47
51
|
- **Retries**: reads, and writes sent with an `idempotencyKey`, are retried on 429 / 502–504 and network
|
|
48
52
|
errors (`retries`, default 2), honouring `Retry-After` up to 10 s. `NODE_OFFLINE` is not retried.
|
|
49
53
|
- **Scopes**: `await belvador.scopes()` returns the token's effective scopes (the `Belvador-Scopes` header).
|
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
|
+
export declare const CLIENT_VERSION = "0.2.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.
|
|
4
|
+
export const CLIENT_VERSION = "0.2.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}$/;
|
package/dist/errors.d.ts
CHANGED
|
@@ -20,6 +20,11 @@ export declare class BelvadorApiError extends Error {
|
|
|
20
20
|
pointer: string;
|
|
21
21
|
code: string;
|
|
22
22
|
}> | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* `APPROVAL_REQUIRED` (403): the approval a person gives on belvador.dev. Retry the same call
|
|
25
|
+
* with the same Idempotency-Key after they approved to get the stored result.
|
|
26
|
+
*/
|
|
27
|
+
readonly approval: Approval | undefined;
|
|
23
28
|
constructor(code: string, status: number, extra?: {
|
|
24
29
|
title?: string;
|
|
25
30
|
detail?: string;
|
|
@@ -30,7 +35,17 @@ export declare class BelvadorApiError extends Error {
|
|
|
30
35
|
pointer: string;
|
|
31
36
|
code: string;
|
|
32
37
|
}>;
|
|
38
|
+
approval?: Approval;
|
|
33
39
|
});
|
|
34
40
|
}
|
|
41
|
+
export type ApprovalState = "pending" | "approved" | "executed" | "rejected" | "expired" | "failed";
|
|
42
|
+
export type Approval = {
|
|
43
|
+
id: string;
|
|
44
|
+
url: string;
|
|
45
|
+
expiresAt: string;
|
|
46
|
+
state: ApprovalState;
|
|
47
|
+
};
|
|
48
|
+
/** Approval members of a problem body; only an https `/approve/<id>` link is accepted. */
|
|
49
|
+
export declare function approvalFrom(body: Record<string, unknown>): Approval | undefined;
|
|
35
50
|
/** An error answer → BelvadorApiError (problem+json fields, scope challenge, Retry-After). */
|
|
36
51
|
export declare function errorFrom(res: Response): Promise<BelvadorApiError>;
|
package/dist/errors.js
CHANGED
|
@@ -17,6 +17,11 @@ export class BelvadorApiError extends Error {
|
|
|
17
17
|
requestId;
|
|
18
18
|
/** Field errors: JSON pointers into the request with a code. */
|
|
19
19
|
errors;
|
|
20
|
+
/**
|
|
21
|
+
* `APPROVAL_REQUIRED` (403): the approval a person gives on belvador.dev. Retry the same call
|
|
22
|
+
* with the same Idempotency-Key after they approved to get the stored result.
|
|
23
|
+
*/
|
|
24
|
+
approval;
|
|
20
25
|
constructor(code, status, extra = {}) {
|
|
21
26
|
super(extra.title ? `${code}: ${extra.title}` : extra.detail ? `${code}: ${extra.detail}` : code);
|
|
22
27
|
this.code = code;
|
|
@@ -27,8 +32,35 @@ export class BelvadorApiError extends Error {
|
|
|
27
32
|
this.retryAfter = extra.retryAfter;
|
|
28
33
|
this.requestId = extra.requestId;
|
|
29
34
|
this.errors = extra.errors;
|
|
35
|
+
this.approval = extra.approval;
|
|
30
36
|
}
|
|
31
37
|
}
|
|
38
|
+
const APPROVAL_STATES = ["pending", "approved", "executed", "rejected", "expired", "failed"];
|
|
39
|
+
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
40
|
+
/** Approval members of a problem body; only an https `/approve/<id>` link is accepted. */
|
|
41
|
+
export function approvalFrom(body) {
|
|
42
|
+
const id = body["approval_id"];
|
|
43
|
+
const url = body["approval_url"];
|
|
44
|
+
const exp = body["expires_at"];
|
|
45
|
+
const state = body["state"];
|
|
46
|
+
if (typeof id !== "string" || !UUID.test(id) || typeof url !== "string" || url.length > 300)
|
|
47
|
+
return undefined;
|
|
48
|
+
let u;
|
|
49
|
+
try {
|
|
50
|
+
u = new URL(url);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
const local = u.protocol === "http:" && (u.hostname === "localhost" || u.hostname === "127.0.0.1");
|
|
56
|
+
if ((u.protocol !== "https:" && !local) || !u.pathname.endsWith(`/approve/${id}`))
|
|
57
|
+
return undefined;
|
|
58
|
+
if (typeof exp !== "string" || Number.isNaN(Date.parse(exp)))
|
|
59
|
+
return undefined;
|
|
60
|
+
if (typeof state !== "string" || !APPROVAL_STATES.includes(state))
|
|
61
|
+
return undefined;
|
|
62
|
+
return { id, url: u.toString(), expiresAt: exp, state: state };
|
|
63
|
+
}
|
|
32
64
|
const str = (v, max = 500) => typeof v === "string" && v ? v.slice(0, max) : undefined;
|
|
33
65
|
/** An error answer → BelvadorApiError (problem+json fields, scope challenge, Retry-After). */
|
|
34
66
|
export async function errorFrom(res) {
|
|
@@ -69,5 +101,8 @@ export async function errorFrom(res) {
|
|
|
69
101
|
extra.requestId = requestId;
|
|
70
102
|
if (errors?.length)
|
|
71
103
|
extra.errors = errors;
|
|
104
|
+
const approval = approvalFrom(body);
|
|
105
|
+
if (approval)
|
|
106
|
+
extra.approval = approval;
|
|
72
107
|
return new BelvadorApiError(code, res.status, extra);
|
|
73
108
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { CLIENT_VERSION, createClient, DEFAULT_BASE_URL, requestParts, SCOPES_HEADER, TOKEN_PATTERN, type ApiResult, type BelvadorClient, type CallOptions, type ClientOptions, type Page, type PagedOperationId, } from "./client.js";
|
|
2
|
-
export { BelvadorApiError } from "./errors.js";
|
|
2
|
+
export { BelvadorApiError, type Approval, type ApprovalState } from "./errors.js";
|
|
3
3
|
export { API_VERSION, OPERATIONS, type HttpMethod, type OperationId, type OperationMeta, type OperationParams, } from "./operations.gen.js";
|
package/dist/operations.gen.d.ts
CHANGED
|
@@ -110,6 +110,20 @@ export declare const OPERATIONS: {
|
|
|
110
110
|
readonly node: null;
|
|
111
111
|
readonly defaults: {};
|
|
112
112
|
};
|
|
113
|
+
readonly "approvals.get": {
|
|
114
|
+
readonly method: "GET";
|
|
115
|
+
readonly path: "/v1/approvals/{approval_id}";
|
|
116
|
+
readonly pathParams: readonly ["approval_id"];
|
|
117
|
+
readonly scope: "org:read";
|
|
118
|
+
readonly kind: "read";
|
|
119
|
+
readonly sensitivity: "normal";
|
|
120
|
+
readonly idempotency: "none";
|
|
121
|
+
readonly ifMatch: false;
|
|
122
|
+
readonly paged: false;
|
|
123
|
+
readonly status: 200;
|
|
124
|
+
readonly node: null;
|
|
125
|
+
readonly defaults: {};
|
|
126
|
+
};
|
|
113
127
|
readonly "audit.list": {
|
|
114
128
|
readonly method: "GET";
|
|
115
129
|
readonly path: "/v1/audit-log";
|
|
@@ -1560,6 +1574,10 @@ export interface OperationParams {
|
|
|
1560
1574
|
state?: "open" | "resolved" | "all";
|
|
1561
1575
|
monitor_id?: string;
|
|
1562
1576
|
};
|
|
1577
|
+
/** State of an approval this token asked for (admins: any in the workspace): pending, approved, executed, rejected, expired or failed, with its result ids. */
|
|
1578
|
+
"approvals.get": {
|
|
1579
|
+
approval_id: string;
|
|
1580
|
+
};
|
|
1563
1581
|
/** Audit log, newest first (admins). */
|
|
1564
1582
|
"audit.list": {
|
|
1565
1583
|
limit?: number;
|
package/dist/operations.gen.js
CHANGED
|
@@ -86,6 +86,20 @@ export const OPERATIONS = {
|
|
|
86
86
|
node: null,
|
|
87
87
|
defaults: {},
|
|
88
88
|
},
|
|
89
|
+
"approvals.get": {
|
|
90
|
+
method: "GET",
|
|
91
|
+
path: "/v1/approvals/{approval_id}",
|
|
92
|
+
pathParams: ["approval_id"],
|
|
93
|
+
scope: "org:read",
|
|
94
|
+
kind: "read",
|
|
95
|
+
sensitivity: "normal",
|
|
96
|
+
idempotency: "none",
|
|
97
|
+
ifMatch: false,
|
|
98
|
+
paged: false,
|
|
99
|
+
status: 200,
|
|
100
|
+
node: null,
|
|
101
|
+
defaults: {},
|
|
102
|
+
},
|
|
89
103
|
"audit.list": {
|
|
90
104
|
method: "GET",
|
|
91
105
|
path: "/v1/audit-log",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@belvador/api",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.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)",
|