@hunt-labs/bytekit-cli 0.3.0 → 0.6.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/CHANGELOG.md +230 -0
- package/README.md +187 -29
- package/dist/client.d.ts +128 -11
- package/dist/client.js +369 -95
- package/dist/commands/account.d.ts +11 -0
- package/dist/commands/account.js +10 -127
- package/dist/commands/bulk.js +36 -4
- package/dist/commands/fetch.js +91 -10
- package/dist/commands/monitors.js +92 -11
- package/dist/commands/scrape.js +131 -20
- package/dist/commands/screenshots.js +86 -1
- package/dist/commands/sitemap.js +38 -6
- package/dist/commands/usage.d.ts +13 -0
- package/dist/commands/usage.js +81 -0
- package/dist/commands/webhook-deliveries.d.ts +11 -0
- package/dist/commands/webhook-deliveries.js +59 -0
- package/dist/index.js +4 -0
- package/dist/lib/download-artifact.d.ts +30 -1
- package/dist/lib/download-artifact.js +89 -5
- package/dist/lib/parse-flags.d.ts +30 -0
- package/dist/lib/parse-flags.js +58 -0
- package/dist/lib/url-lines.d.ts +46 -8
- package/dist/lib/url-lines.js +58 -19
- package/dist/output.d.ts +47 -9
- package/dist/output.js +103 -16
- package/package.json +3 -3
- package/dist/lib/fetch-body.d.ts +0 -18
- package/dist/lib/fetch-body.js +0 -6
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ByteKit } from '@hunt-labs/bytekit-sdk';
|
|
1
|
+
import { ByteKit, ByteKitConnectionError } from '@hunt-labs/bytekit-sdk';
|
|
2
2
|
export interface GlobalOptions {
|
|
3
3
|
key?: string | undefined;
|
|
4
4
|
baseUrl?: string | undefined;
|
|
@@ -22,6 +22,24 @@ export declare const MIN_RETRY_AFTER_MS = 1000;
|
|
|
22
22
|
export declare const MAX_RETRY_AFTER_MS = 60000;
|
|
23
23
|
/** Exactly one retry. A second 429 is terminal — no loop. */
|
|
24
24
|
export declare const MAX_RETRIES = 1;
|
|
25
|
+
/**
|
|
26
|
+
* Cap on a best-effort, discard-only body drain (#2587 cr2-1). Independent of the per-attempt
|
|
27
|
+
* abort budget: the 429 drain below (`response.text().catch(() => {})`) exists ONLY so undici can
|
|
28
|
+
* return the socket to the connection pool — nothing downstream reads the drained bytes — but
|
|
29
|
+
* binding that read to the FULL per-attempt budget (120 s default, or, under `timeoutMs <= 0`,
|
|
30
|
+
* to no bound at all) let a stalled 429 body burn the whole budget, or hang forever, before a
|
|
31
|
+
* retry that needs nothing from that body. Short enough that a legitimately stalled 429 body costs
|
|
32
|
+
* the retry very little wall-clock time.
|
|
33
|
+
*
|
|
34
|
+
* #2587 cr3-2: past this cap, the read is not merely abandoned — {@link drainBestEffort} actively
|
|
35
|
+
* ABORTS its underlying request. Resolving the race without aborting anything (the earlier
|
|
36
|
+
* cr2-1 shape) let the retry proceed on schedule while the abandoned `response.text()` kept
|
|
37
|
+
* reading against a live socket forever, which — because the CLI exits via a soft
|
|
38
|
+
* `process.exitCode` (#2544) rather than `process.exit()` — kept the whole process alive
|
|
39
|
+
* indefinitely. `.catch`-guarding the read only made it safe to abandon the RESULT; it never made
|
|
40
|
+
* abandoning the underlying READ safe.
|
|
41
|
+
*/
|
|
42
|
+
export declare const DRAIN_TIMEOUT_MS = 2000;
|
|
25
43
|
/**
|
|
26
44
|
* Default CLIENT-SIDE abort budget for a single fetch attempt, in ms.
|
|
27
45
|
*
|
|
@@ -33,6 +51,49 @@ export declare const MAX_RETRIES = 1;
|
|
|
33
51
|
* exists.
|
|
34
52
|
*/
|
|
35
53
|
export declare const DEFAULT_TIMEOUT_MS = 120000;
|
|
54
|
+
/**
|
|
55
|
+
* A per-attempt abort budget: an `AbortController`, optionally fired by a `setTimeout`.
|
|
56
|
+
*
|
|
57
|
+
* The budget spans the WHOLE attempt — time-to-headers **and** the response body read (#2546).
|
|
58
|
+
* Arming it only until headers arrive left `response.text()` / `response.arrayBuffer()` unbounded,
|
|
59
|
+
* so a server that answered with headers and then stalled its body hung the CLI forever.
|
|
60
|
+
*
|
|
61
|
+
* The controller ALWAYS exists, even when `ms <= 0` — only its own timeout timer is conditional
|
|
62
|
+
* on `ms > 0`. `sendWithRetry`'s 429-drain cap ({@link DRAIN_TIMEOUT_MS}, via the returned
|
|
63
|
+
* `abort()`) needs a live controller to tear down an abandoned body read regardless of whether the
|
|
64
|
+
* OVERALL per-attempt budget is enabled — `ms <= 0` must still mean "no full-attempt timeout", not
|
|
65
|
+
* "no controller to abort a stalled drain with" (#2587 cr3-2: the earlier drain fix abandoned the
|
|
66
|
+
* read entirely rather than aborting it, so the socket — and the event loop — stayed alive
|
|
67
|
+
* indefinitely, worse than the pre-fix baseline it replaced).
|
|
68
|
+
*
|
|
69
|
+
* Deliberately NOT `AbortSignal.timeout` (which the SDK prefers): that schedules on a timer the
|
|
70
|
+
* test runner's fake timers cannot advance, so the transport's timeout would be untestable under
|
|
71
|
+
* `vi.useFakeTimers()`. CLI requests are short-lived and single-shot, so the GC advantage
|
|
72
|
+
* `AbortSignal.timeout` buys the long-running SDK does not apply here. The timer is `unref`'d so a
|
|
73
|
+
* pending budget never keeps the process alive, and `cleanup()` clears it on every exit path.
|
|
74
|
+
*
|
|
75
|
+
* Exported so `lib/download-artifact.ts` can reuse the same budget for the artifact-download path
|
|
76
|
+
* (#2587) rather than growing a second timeout implementation — that path sits entirely outside
|
|
77
|
+
* `sendWithRetry` (it is not a JSON/raw API call), so it would otherwise never inherit this bound.
|
|
78
|
+
*/
|
|
79
|
+
export declare function createAttemptBudget(ms: number): {
|
|
80
|
+
signal: AbortSignal;
|
|
81
|
+
cleanup: () => void;
|
|
82
|
+
abort: (reason: unknown) => void;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Map a `fetch` or body-read rejection (no usable HTTP payload was produced) onto the SDK's
|
|
86
|
+
* `ByteKitConnectionError` taxonomy, so a CLI failure carries the same `{name, code, cause}` a
|
|
87
|
+
* direct SDK call would.
|
|
88
|
+
*
|
|
89
|
+
* `timedOut` is true when this attempt's own budget fired the abort, which is the reliable
|
|
90
|
+
* discriminator (undici rejects with the signal's reason — or a wrapper carrying it, see
|
|
91
|
+
* {@link abortNameOf} — whose `name` we also inspect as a fallback for a caller-style AbortError).
|
|
92
|
+
*
|
|
93
|
+
* Exported so `lib/download-artifact.ts` reuses the same taxonomy for the artifact-download path
|
|
94
|
+
* (#2587) instead of hand-rolling a second error mapping.
|
|
95
|
+
*/
|
|
96
|
+
export declare function toConnectionError(err: unknown, method: string, path: string, timedOut: boolean): ByteKitConnectionError;
|
|
36
97
|
/**
|
|
37
98
|
* Parse a `Retry-After` header into a clamped millisecond delay.
|
|
38
99
|
*
|
|
@@ -42,19 +103,64 @@ export declare const DEFAULT_TIMEOUT_MS = 120000;
|
|
|
42
103
|
*/
|
|
43
104
|
export declare function parseRetryAfterMs(raw: string | null): number;
|
|
44
105
|
/**
|
|
45
|
-
* Build the CLI's
|
|
46
|
-
*
|
|
47
|
-
* Why the CLI owns the `fetch` instead of wrapping the SDK's `request()`: the SDK throws a
|
|
48
|
-
* header-less `ByteKitError` and discards the `Response`, so a delegating wrapper can see
|
|
49
|
-
* `status === 429` but can never read `Retry-After`. Owning the fetch is the only way to honor the
|
|
50
|
-
* server's advertised backoff without editing the SDK.
|
|
106
|
+
* Build the CLI's JSON transport: the SDK's `request` contract, plus the single bounded 429 retry.
|
|
51
107
|
*
|
|
52
108
|
* Every SDK resource dereferences `this.client.request` at call time, so installing this as an own
|
|
53
|
-
* property of the client (see {@link buildClient}) routes
|
|
109
|
+
* property of the client (see {@link buildClient}) routes all JSON commands through it from one
|
|
110
|
+
* seam. `/v1/fetch` does NOT ride this transport — it rides `requestRaw` (see
|
|
111
|
+
* {@link createRetryingRequestRaw}); `/v1/fetch/bulk` is a JSON job envelope and does.
|
|
54
112
|
*
|
|
55
|
-
*
|
|
113
|
+
* DECISION (#2586 dod-4): the 4th `RequestOptions` parameter of the SDK's contract is accepted
|
|
114
|
+
* here — so the signature matches `ByteKit['request']` exactly, closing the gap where a caller-typo
|
|
115
|
+
* of a 5th positional argument would silently no-op instead of a compile error — but deliberately
|
|
116
|
+
* NOT forwarded into `sendWithRetry`. Two reasons: (1) no CLI command constructs a `RequestOptions`
|
|
117
|
+
* today (no `--timeout` flag exists yet, and `signal` has no CLI-level source), so the parameter is
|
|
118
|
+
* always `undefined` in practice; (2) the CLI already owns a per-attempt abort budget with its own
|
|
119
|
+
* semantics (`createAttemptBudget`, `timeoutMs` above) that would need explicit composition with a
|
|
120
|
+
* caller `signal` to honor `RequestOptions` correctly (the SDK's `composeSignals`) — inventing that
|
|
121
|
+
* unexercised composition here, with no caller able to reach it, would be undertested surface for no
|
|
122
|
+
* present behavior change. If a future CLI flag needs per-call overrides, wire it through
|
|
123
|
+
* `sendWithRetry`'s `timeoutMs` (already per-attempt) at that point, with its own tests.
|
|
56
124
|
*/
|
|
57
125
|
export declare function createRetryingRequest(apiKey: string, baseUrl: string, timeoutMs?: number): ByteKit['request'];
|
|
126
|
+
/**
|
|
127
|
+
* Build the CLI's RAW transport: the SDK's `requestRaw` contract, plus the same bounded 429 retry.
|
|
128
|
+
*
|
|
129
|
+
* `/v1/fetch` returns the fetched content AS the body (HTML, markdown, or true binary) with its
|
|
130
|
+
* metadata in `X-Fetch-*` headers, so the SDK routes it through `requestRaw` rather than the JSON
|
|
131
|
+
* `request`. The CLI must override BOTH seams or fetch commands silently bypass the retry and the
|
|
132
|
+
* CLI's error taxonomy — the #2535 defect, where only `request` was replaced.
|
|
133
|
+
*
|
|
134
|
+
* The returned shape is the SDK's `requestRaw` contract verbatim (`{body, bytes, headers, status}`)
|
|
135
|
+
* so `FetchResource` builds an ordinary `FetchResult` from it and the CLI never forks fetch
|
|
136
|
+
* handling away from the SDK. `bytes` is byte-exact (binary-safe); `body` is the best-effort UTF-8
|
|
137
|
+
* decode, matching the SDK's own semantics.
|
|
138
|
+
*
|
|
139
|
+
* The 4th `RequestOptions` parameter is accepted but not forwarded — see the DECISION note on
|
|
140
|
+
* {@link createRetryingRequest}, which applies here verbatim. `FetchResource.create`/`get` DO thread
|
|
141
|
+
* a `requestOptions` argument through to this seam (unlike every other resource), but no CLI `fetch`
|
|
142
|
+
* command constructs one today (no `--timeout` flag), so it is still always `undefined` in practice.
|
|
143
|
+
*/
|
|
144
|
+
export declare function createRetryingRequestRaw(apiKey: string, baseUrl: string, timeoutMs?: number): ByteKit['requestRaw'];
|
|
145
|
+
/**
|
|
146
|
+
* Build the CLI's header-carrying JSON transport: the SDK's `requestWithResponse` contract
|
|
147
|
+
* (`{data, headers, status}`, #2547), plus the same bounded 429 retry every other CLI transport
|
|
148
|
+
* gets.
|
|
149
|
+
*
|
|
150
|
+
* Unused by any CLI command today — `screenshots.createWithResponse` / `getWithResponse` and
|
|
151
|
+
* `sitemap.getWithResponse` all exist on the SDK surface but no command builds on them yet. It is
|
|
152
|
+
* still installed (see {@link buildClient}) rather than left un-overridden: the transport-coverage
|
|
153
|
+
* contract test (#2586 dod-3) derives its method list from the real SDK prototype, and leaving a
|
|
154
|
+
* transport method un-overridden is exactly how #2535 shipped broken — the first command that DOES
|
|
155
|
+
* call a `*WithResponse` resource method must not silently ride the bare SDK transport and skip the
|
|
156
|
+
* CLI's retry/timeout/error taxonomy.
|
|
157
|
+
*
|
|
158
|
+
* Same body-read shape as {@link createRetryingRequest} (204/empty → `undefined`, `JSON.parse`
|
|
159
|
+
* otherwise), with `headers` and `status` threaded through from the successful response alongside
|
|
160
|
+
* the parsed `data`. The 4th `RequestOptions` parameter is accepted but not forwarded — see the
|
|
161
|
+
* DECISION note on {@link createRetryingRequest}.
|
|
162
|
+
*/
|
|
163
|
+
export declare function createRetryingRequestWithResponse(apiKey: string, baseUrl: string, timeoutMs?: number): ByteKit['requestWithResponse'];
|
|
58
164
|
/**
|
|
59
165
|
* Build an authenticated SDK client from global CLI options.
|
|
60
166
|
*
|
|
@@ -63,7 +169,18 @@ export declare function createRetryingRequest(apiKey: string, baseUrl: string, t
|
|
|
63
169
|
* baseUrl: --base-url flag → BYTEKIT_BASE_URL → DEFAULT_BASE_URL.
|
|
64
170
|
* Exits with a descriptive error when no key is available.
|
|
65
171
|
*
|
|
66
|
-
* The returned client's `request`
|
|
67
|
-
* the 429 retry
|
|
172
|
+
* The returned client's `request`, `requestRaw`, AND `requestWithResponse` are all replaced by the
|
|
173
|
+
* retrying transports, so every command inherits the 429 retry — JSON commands via `request`, the
|
|
174
|
+
* `/v1/fetch` content commands via `requestRaw`, and the header-carrying `*WithResponse` surface via
|
|
175
|
+
* `requestWithResponse`. Overriding only `request` is the #2535 defect: the SDK's fetch resource
|
|
176
|
+
* rides `requestRaw`, so fetch bypassed the retry and the CLI's error taxonomy entirely; leaving
|
|
177
|
+
* `requestWithResponse` un-overridden is the same class of defect (#2586) — it is unused by any CLI
|
|
178
|
+
* command TODAY, but a future command built on `screenshots.createWithResponse` /
|
|
179
|
+
* `sitemap.getWithResponse` would otherwise silently ride the bare SDK transport.
|
|
180
|
+
*
|
|
181
|
+
* `baseUrl` is normalized ONCE, here, before it reaches either the SDK constructor or the CLI's own
|
|
182
|
+
* transport builders below — the CLI's transports build their request URL from this local variable
|
|
183
|
+
* directly (they do not call the SDK's own normalized `send()`), so a trailing-slash flag would
|
|
184
|
+
* otherwise double-slash every request the CLI makes (#2586).
|
|
68
185
|
*/
|
|
69
186
|
export declare function buildClient(opts: GlobalOptions): ByteKit;
|