ccqa 1.37.0 → 1.38.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/bin/ccqa.mjs +1220 -208
- package/dist/hub-client/index.d.mts +10 -1
- package/dist/hub-client/index.mjs +48 -37
- package/dist/package.json +1 -1
- package/package.json +1 -1
|
@@ -1239,6 +1239,15 @@ interface HubClient {
|
|
|
1239
1239
|
}): Promise<void>;
|
|
1240
1240
|
deletePerspectives(project: string): Promise<void>;
|
|
1241
1241
|
}
|
|
1242
|
+
/**
|
|
1243
|
+
* One round trip against a hub, with the client's shared policy: bearer auth,
|
|
1244
|
+
* per-attempt timeout, retries for idempotent methods, `HubApiError` on the
|
|
1245
|
+
* final non-ok answer. Exported for the one caller outside the client
|
|
1246
|
+
* (`CoverageInbox`), whose POSTs are appends a duplicate cannot corrupt —
|
|
1247
|
+
* unlike the client's own POSTs — so it may opt into `retry: "post-once"`,
|
|
1248
|
+
* one extra attempt after a 5xx or network error.
|
|
1249
|
+
*/
|
|
1250
|
+
declare function hubRequest(opts: HubClientOptions, path: string, init?: RequestInit, retry?: "post-once"): Promise<Response>;
|
|
1242
1251
|
declare function createHubClient(opts: HubClientOptions): HubClient;
|
|
1243
1252
|
//#endregion
|
|
1244
|
-
export { HubApiError, HubClient, HubClientOptions, HubPromptMeta, HubVariable, PatchRunRequest, createHubClient };
|
|
1253
|
+
export { HubApiError, HubClient, HubClientOptions, HubPromptMeta, HubVariable, PatchRunRequest, createHubClient, hubRequest };
|
|
@@ -27,50 +27,61 @@ const RETRY_BACKOFF_MS = [
|
|
|
27
27
|
function sleep(ms) {
|
|
28
28
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
29
29
|
}
|
|
30
|
-
function
|
|
30
|
+
async function throwHubApiError(res) {
|
|
31
|
+
let code = "unknown_error";
|
|
32
|
+
let message = res.statusText;
|
|
33
|
+
try {
|
|
34
|
+
const body = await res.json();
|
|
35
|
+
if (body.error?.code) code = body.error.code;
|
|
36
|
+
if (body.error?.message) message = body.error.message;
|
|
37
|
+
} catch {}
|
|
38
|
+
throw new HubApiError(res.status, code, message);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* One round trip against a hub, with the client's shared policy: bearer auth,
|
|
42
|
+
* per-attempt timeout, retries for idempotent methods, `HubApiError` on the
|
|
43
|
+
* final non-ok answer. Exported for the one caller outside the client
|
|
44
|
+
* (`CoverageInbox`), whose POSTs are appends a duplicate cannot corrupt —
|
|
45
|
+
* unlike the client's own POSTs — so it may opt into `retry: "post-once"`,
|
|
46
|
+
* one extra attempt after a 5xx or network error.
|
|
47
|
+
*/
|
|
48
|
+
async function hubRequest(opts, path, init = {}, retry) {
|
|
31
49
|
const baseUrl = opts.baseUrl.replace(/\/+$/, "");
|
|
32
50
|
const doFetch = opts.fetchImpl ?? fetch;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
51
|
+
const method = (init.method ?? "GET").toUpperCase();
|
|
52
|
+
const maxAttempts = retry === "post-once" ? 2 : RETRYABLE_METHODS.has(method) ? RETRY_BACKOFF_MS.length + 1 : 1;
|
|
53
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
54
|
+
const signal = init.signal ?? AbortSignal.timeout(REQUEST_TIMEOUT_MS);
|
|
55
|
+
let res;
|
|
36
56
|
try {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const method = (init.method ?? "GET").toUpperCase();
|
|
45
|
-
const maxAttempts = RETRYABLE_METHODS.has(method) ? RETRY_BACKOFF_MS.length + 1 : 1;
|
|
46
|
-
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
47
|
-
const signal = init.signal ?? AbortSignal.timeout(REQUEST_TIMEOUT_MS);
|
|
48
|
-
let res;
|
|
49
|
-
try {
|
|
50
|
-
res = await doFetch(`${baseUrl}${path}`, {
|
|
51
|
-
...init,
|
|
52
|
-
signal,
|
|
53
|
-
headers: {
|
|
54
|
-
...opts.headers,
|
|
55
|
-
...init.headers,
|
|
56
|
-
Authorization: `Bearer ${opts.token}`
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
} catch (err) {
|
|
60
|
-
if (attempt < maxAttempts - 1) {
|
|
61
|
-
await sleep(RETRY_BACKOFF_MS[attempt]);
|
|
62
|
-
continue;
|
|
57
|
+
res = await doFetch(`${baseUrl}${path}`, {
|
|
58
|
+
...init,
|
|
59
|
+
signal,
|
|
60
|
+
headers: {
|
|
61
|
+
...opts.headers,
|
|
62
|
+
...init.headers,
|
|
63
|
+
Authorization: `Bearer ${opts.token}`
|
|
63
64
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (
|
|
67
|
-
if (res.status >= 500 && attempt < maxAttempts - 1) {
|
|
65
|
+
});
|
|
66
|
+
} catch (err) {
|
|
67
|
+
if (attempt < maxAttempts - 1) {
|
|
68
68
|
await sleep(RETRY_BACKOFF_MS[attempt]);
|
|
69
69
|
continue;
|
|
70
70
|
}
|
|
71
|
-
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
73
|
+
if (res.ok) return res;
|
|
74
|
+
if (res.status >= 500 && attempt < maxAttempts - 1) {
|
|
75
|
+
await sleep(RETRY_BACKOFF_MS[attempt]);
|
|
76
|
+
continue;
|
|
72
77
|
}
|
|
73
|
-
|
|
78
|
+
return throwHubApiError(res);
|
|
79
|
+
}
|
|
80
|
+
throw new Error("unreachable");
|
|
81
|
+
}
|
|
82
|
+
function createHubClient(opts) {
|
|
83
|
+
function request(path, init = {}) {
|
|
84
|
+
return hubRequest(opts, path, init);
|
|
74
85
|
}
|
|
75
86
|
async function json(path, init) {
|
|
76
87
|
return (await request(path, init)).json();
|
|
@@ -363,4 +374,4 @@ function toBodyInit(bytes) {
|
|
|
363
374
|
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
364
375
|
}
|
|
365
376
|
//#endregion
|
|
366
|
-
export { HubApiError, createHubClient };
|
|
377
|
+
export { HubApiError, createHubClient, hubRequest };
|
package/dist/package.json
CHANGED