@belvador/api 0.0.0-stage → 0.1.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/LICENSE +27 -0
- package/README.md +54 -2
- package/dist/client.d.ts +70 -0
- package/dist/client.js +290 -0
- package/dist/errors.d.ts +36 -0
- package/dist/errors.js +73 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/node-wire.gen.d.ts +41 -0
- package/dist/node-wire.gen.js +100 -0
- package/dist/operations.gen.d.ts +2285 -0
- package/dist/operations.gen.js +1447 -0
- package/package.json +41 -4
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// GENERATED by scripts/api/client.ts from src/shared/contracts/api-ops/node-wire.ts. Do not edit; run `bun scripts/api/client.ts`.
|
|
2
|
+
// Node reads on the wire (spec 04 §2–§4), shared by the gateway (`services/api/src/node-op.ts`)
|
|
3
|
+
// and clients that read the node directly with a ticket (`@belvador/api`, which gets a copy of
|
|
4
|
+
// this file from `scripts/api/client.ts`; a test keeps the copy equal). Arguments are REST wire
|
|
5
|
+
// names (snake_case); `start`/`end` travel to the node as `from`/`to`. No imports: the copy must
|
|
6
|
+
// compile on its own.
|
|
7
|
+
const PLACEHOLDER = /\{([a-z_]+)\}/g;
|
|
8
|
+
function queryValue(v) {
|
|
9
|
+
if (v === undefined || v === null)
|
|
10
|
+
return null;
|
|
11
|
+
if (Array.isArray(v))
|
|
12
|
+
return v.map(String).join(",");
|
|
13
|
+
if (typeof v === "object")
|
|
14
|
+
return JSON.stringify(v);
|
|
15
|
+
return String(v);
|
|
16
|
+
}
|
|
17
|
+
/** Wire arguments → the node path (placeholders filled) and query string; null when a path value is missing. */
|
|
18
|
+
export function nodeReadRequest(route, wire) {
|
|
19
|
+
const names = new Set();
|
|
20
|
+
let missing = false;
|
|
21
|
+
const path = route.path.replace(PLACEHOLDER, (_, name) => {
|
|
22
|
+
names.add(name);
|
|
23
|
+
const v = wire[name];
|
|
24
|
+
if (v === undefined || v === null || v === "")
|
|
25
|
+
missing = true;
|
|
26
|
+
return encodeURIComponent(String(v ?? ""));
|
|
27
|
+
});
|
|
28
|
+
if (missing)
|
|
29
|
+
return null;
|
|
30
|
+
const qs = new URLSearchParams();
|
|
31
|
+
for (const [k, v] of Object.entries(wire)) {
|
|
32
|
+
if (names.has(k))
|
|
33
|
+
continue;
|
|
34
|
+
const s = queryValue(v);
|
|
35
|
+
if (s === null)
|
|
36
|
+
continue;
|
|
37
|
+
qs.set(k === "start" ? "from" : k === "end" ? "to" : k, s);
|
|
38
|
+
}
|
|
39
|
+
const query = qs.toString();
|
|
40
|
+
return { path, query };
|
|
41
|
+
}
|
|
42
|
+
/** Window routes need both ends; the node and the gateway judge the size against the plan. */
|
|
43
|
+
export function nodeWindowMissing(route, wire) {
|
|
44
|
+
return !!route.window && (!wire["start"] || !wire["end"]);
|
|
45
|
+
}
|
|
46
|
+
/** A node list answer as the API's page: `{data, next_cursor, complete, ...rest}`. */
|
|
47
|
+
export function reshapeNodeAnswer(list, body) {
|
|
48
|
+
if (!list || !body || typeof body !== "object" || Array.isArray(body))
|
|
49
|
+
return body;
|
|
50
|
+
const { [list]: rows, next_cursor, complete, search_partial, partial, ...rest } = body;
|
|
51
|
+
return {
|
|
52
|
+
data: Array.isArray(rows) ? rows : [],
|
|
53
|
+
next_cursor: typeof next_cursor === "string" && next_cursor ? next_cursor : null,
|
|
54
|
+
complete: typeof complete === "boolean" ? complete : !(search_partial === true || partial === true),
|
|
55
|
+
...rest,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/** HTTP status of the codes a node read can end in (equal to `apiHttpStatus`; a test holds them equal). */
|
|
59
|
+
export const NODE_REFUSAL_STATUS = {
|
|
60
|
+
NOT_FOUND: 404,
|
|
61
|
+
API_SCOPE_INSUFFICIENT: 403,
|
|
62
|
+
API_FORBIDDEN: 403,
|
|
63
|
+
NODE_BUSY: 429,
|
|
64
|
+
QUERY_TIMEOUT: 422,
|
|
65
|
+
NODE_OFFLINE: 503,
|
|
66
|
+
QUERY_WINDOW_REQUIRED: 400,
|
|
67
|
+
QUERY_WINDOW_TOO_LARGE: 400,
|
|
68
|
+
INVALID_INPUT: 400,
|
|
69
|
+
UNAVAILABLE: 503,
|
|
70
|
+
};
|
|
71
|
+
const refuse = (code, extra = {}) => ({
|
|
72
|
+
code,
|
|
73
|
+
status: NODE_REFUSAL_STATUS[code],
|
|
74
|
+
...extra,
|
|
75
|
+
});
|
|
76
|
+
/** A node error (status + its fixed `error` word) → the API error code; nothing else from the payload. */
|
|
77
|
+
export function nodeRefusal(status, error) {
|
|
78
|
+
if (status === 404 || error === "project_denied")
|
|
79
|
+
return refuse("NOT_FOUND");
|
|
80
|
+
if (error === "scope")
|
|
81
|
+
return refuse("API_SCOPE_INSUFFICIENT");
|
|
82
|
+
if (error === "environment_restricted")
|
|
83
|
+
return refuse("API_FORBIDDEN");
|
|
84
|
+
if (status === 429 || error === "busy")
|
|
85
|
+
return refuse("NODE_BUSY", { retryAfter: 2 });
|
|
86
|
+
if (error === "query_timeout")
|
|
87
|
+
return refuse("QUERY_TIMEOUT");
|
|
88
|
+
if (status === 503 || status === 504 || status === 502)
|
|
89
|
+
return refuse("NODE_OFFLINE");
|
|
90
|
+
if (status === 400 || status === 422) {
|
|
91
|
+
if (error === "too_broad" || error === "window_too_large")
|
|
92
|
+
return refuse("QUERY_WINDOW_TOO_LARGE");
|
|
93
|
+
return /^[a-z_]{1,40}$/.test(error) ? refuse("INVALID_INPUT", { detail: error }) : refuse("INVALID_INPUT");
|
|
94
|
+
}
|
|
95
|
+
return refuse("UNAVAILABLE");
|
|
96
|
+
}
|
|
97
|
+
/** The node's `error` word from a parsed body ("" when absent). */
|
|
98
|
+
export function nodeErrorWord(body) {
|
|
99
|
+
return body && typeof body === "object" ? String(body.error ?? "") : "";
|
|
100
|
+
}
|