@hunt-labs/bytekit-sdk 0.5.0 → 0.6.1
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 +115 -2
- package/README.md +48 -1
- package/dist/client.d.ts +86 -2
- package/dist/client.js +72 -3
- package/dist/generated/api.d.ts +83 -30
- package/dist/index.d.ts +1 -1
- package/dist/resources/account.js +5 -4
- package/dist/resources/bulk.js +4 -3
- package/dist/resources/fetch.js +3 -2
- package/dist/resources/monitors.js +6 -5
- package/dist/resources/scrape.js +3 -2
- package/dist/resources/screenshots.js +5 -4
- package/dist/resources/sitemap.js +3 -2
- package/dist/resources/usage.js +3 -2
- package/dist/resources/webhooks.js +3 -2
- package/dist/types.d.ts +6 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,113 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
8
8
|
This file records only the delta between versions. For how to use the package
|
|
9
9
|
today, see [README.md](./README.md), which remains the authoritative usage guide.
|
|
10
10
|
|
|
11
|
+
## [0.6.1] - 2026-08-10
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- Version-alignment release after the 2026-08-10 publish rescue: the published 0.6.0 artifact was built from the `main` promotion HEAD, which already contained every change documented under the 0.6.0 heading — but that commit postdates the one that introduced the 0.6.0 version string, so the `check-release-drift` gate (#2680) correctly reported shipped drift on every subsequent CI run (#2829). 0.6.1 realigns manifest ↔ registry ↔ version-introducing commit. **No consumer-visible change relative to the published 0.6.0.**
|
|
16
|
+
|
|
17
|
+
## [0.6.0] - 2026-08-07
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- **`ByteKitError.details`** — the API error envelope's structured rejection reason is now
|
|
22
|
+
reachable from the thrown error. A `422 validation_error` carries
|
|
23
|
+
`{ formErrors, fieldErrors }`, so a caller can finally read WHICH request field was
|
|
24
|
+
rejected rather than only that the request was invalid; a `400 invalid_url` carries a flat
|
|
25
|
+
`{ url, field, reason }`. Typed as `ByteKitErrorDetails | undefined` where
|
|
26
|
+
`ByteKitErrorDetails = Record<string, unknown>` (also exported), deliberately loose because
|
|
27
|
+
the contents vary by error code — narrow the member you read. Strictly ADDITIVE: `details`
|
|
28
|
+
is an optional FOURTH constructor argument, so existing three-argument
|
|
29
|
+
`new ByteKitError(status, code, message)` construction is unchanged, and `status` / `code` /
|
|
30
|
+
`message` are byte-identical. Populated only from the canonical `{ error: { … } }`
|
|
31
|
+
envelope — a flat vendor-masked body (`/v1/search` `502 {"error":"search_provider_error"}`)
|
|
32
|
+
carries no structured detail and grows none. `http_status` and `failed_at` are deliberately
|
|
33
|
+
NOT exposed: `http_status` would duplicate the existing `status` property, and a duplicated
|
|
34
|
+
field that can disagree with its twin is a bug surface. (#2685)
|
|
35
|
+
|
|
36
|
+
- A `tsc --noEmit`-checked parity pin in `src/types.ts` ties the hand-authored search types
|
|
37
|
+
(`SearchOpts`, `SearchResult`, `SearchResultImage`, `SearchResponse` — the SDK's only
|
|
38
|
+
hand-authored request/response types) to the generated `operations['createSearch']`
|
|
39
|
+
request and `200` response. It asserts key-set and optionality parity in both directions
|
|
40
|
+
and assignability in each meaningful direction, so a future spec change that search's
|
|
41
|
+
types do not follow fails the build instead of shipping. Value types are erased before the
|
|
42
|
+
parity comparison, so a deliberate hand-authored refinement (a branded id, a narrowed
|
|
43
|
+
union) cannot trip it. Type-level only — the assertions erase at emit and add nothing to
|
|
44
|
+
`dist/`. (#2678)
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
|
|
48
|
+
- **README: the `fetch` sample's `res.cache` type is corrected to include `null`.** The
|
|
49
|
+
documented type read `"hit" | "miss" | "bypass"`, but `FetchResult.cache` is
|
|
50
|
+
`FetchCacheStatus | null` (null when the response carried no recognizable `X-Fetch-Cache`
|
|
51
|
+
header) — the one strict-`tsc` error a real consumption file copied from the README ever
|
|
52
|
+
produced. The line is now a TYPED ASSIGNMENT rather than a comment, so the README compile
|
|
53
|
+
guard (`src/__tests__/readme-compiles.test.ts`) typechecks it: comment-vs-type drift on
|
|
54
|
+
this field now fails the build instead of shipping. Documentation only — no runtime or type
|
|
55
|
+
change. (#2685)
|
|
56
|
+
|
|
57
|
+
- **CHANGELOG: the `[0.1.0]` heading's malformed `- 2026` date is now `- 2026-04-24`**, the
|
|
58
|
+
commit date of `caf17e56b`, which introduced `packages/sdk-typescript/package.json` at
|
|
59
|
+
version `0.1.0`. Every other heading in this file already carried a full ISO date; this one
|
|
60
|
+
broke the Keep a Changelog `## [x.y.z] - YYYY-MM-DD` form. (#2685)
|
|
61
|
+
|
|
62
|
+
- **Test-only: nine SDK test files mocked wire bodies the API can never send.** A
|
|
63
|
+
`monitors.create` 200 (the operation declares 201 as its only success arm), a scrape 200
|
|
64
|
+
carrying `data: { markdown }` (the envelope has no `data` member — content lives under
|
|
65
|
+
`formats`), a `screenshots.getWithResponse` mock asserting `X-Screenshot-*` headers that
|
|
66
|
+
`GET /v1/screenshots/{id}` declares on no arm, error bodies omitting the `Error` schema's
|
|
67
|
+
required `status: 'failed'` / `http_status` / `failed_at`, and twelve `402`/`429` arms on
|
|
68
|
+
operations that declare neither status. Each is rewritten to a spec-valid shape with a
|
|
69
|
+
comment naming the arm it now matches, and every test keeps its original assertion target.
|
|
70
|
+
No shipped code changed. (#2685)
|
|
71
|
+
|
|
72
|
+
- **`SearchResult.snippet` is now optional (`snippet?: string`) — BREAKING AT COMPILE TIME.**
|
|
73
|
+
`type: 'images'` search results omit `snippet` entirely (the spec has always declared a
|
|
74
|
+
result's `required` set as `[position, title, url]`), but the SDK typed it as a required
|
|
75
|
+
`string`. `hit.snippet.length` therefore compiled under strict TypeScript and threw
|
|
76
|
+
`TypeError: Cannot read properties of undefined` on every images hit — the TypeScript twin
|
|
77
|
+
of the Python SDK's #2579 images `KeyError`. **Consumers reading `.snippet` unguarded now
|
|
78
|
+
see `string | undefined`** and must narrow it (`if (typeof hit.snippet === 'string')` or
|
|
79
|
+
`hit.snippet ?? ''`), exactly as they already do for `date`. No runtime behavior and no
|
|
80
|
+
wire bytes change: the field was already absent from those payloads. `web`/`news` results
|
|
81
|
+
are unaffected — they still carry `snippet`, and a narrowed read yields the same string.
|
|
82
|
+
(#2678)
|
|
83
|
+
|
|
84
|
+
- **Path parameters are now percent-encoded.** Every id-bearing method built its request path
|
|
85
|
+
with a raw template literal, so a crafted or interpolated id silently retargeted the
|
|
86
|
+
request: `scrape.get('sc_/../../account')` addressed `GET /v1/account` once WHATWG URL
|
|
87
|
+
parsing collapsed the `../` segments, and `monitors.get('mon_x?admin=1#frag')` turned the id
|
|
88
|
+
into a real query string plus fragment. The branded id types constrain only the PREFIX of an
|
|
89
|
+
id, never its tail, so they were never a defence. An id now travels as one percent-encoded
|
|
90
|
+
path segment — a malformed one 404s server-side instead of reaching a different resource.
|
|
91
|
+
Ports the Python SDK's #2593 fix. (#2677)
|
|
92
|
+
|
|
93
|
+
### Changed
|
|
94
|
+
|
|
95
|
+
- Request paths are assembled by a single internal `buildPath()` helper in `client.ts` rather
|
|
96
|
+
than by per-resource template literals. It encodes SEGMENTS only — every fixed segment and
|
|
97
|
+
every `/` separator stays literal — and encodes each value exactly once, so an id that
|
|
98
|
+
already contains a `%` is not double-encoded. **A well-formed id (`sc_<hex>`, `mon_<hex>`, …)
|
|
99
|
+
produces a byte-identical URL to before**, on every id-bearing method: encoding is a no-op
|
|
100
|
+
for those characters, so no existing call changes on the wire. (#2677)
|
|
101
|
+
|
|
102
|
+
- Four of the guards shipped above could not fail on the change they appeared to protect, and
|
|
103
|
+
now can. `buildPath()`'s two `TypeError` throws — the fail-loud that replaced the
|
|
104
|
+
compile-time checking a template literal used to give — each survived its own removal with
|
|
105
|
+
the whole suite green, and are now pinned by tests separated by input shape rather than by
|
|
106
|
+
message text. The in-suite path-helper gate read its directory non-recursively while the
|
|
107
|
+
shell grep it reproduces is `-r`, so an offender one directory down was invisible to it; it
|
|
108
|
+
now walks the whole of `src/`, a strict superset of the shell gate's set. The `images`
|
|
109
|
+
search-result literal's key ABSENCE (no `snippet`, no `date`) was held only by a
|
|
110
|
+
one-directional subtype check, which an extra key cannot break; it is now pinned by explicit
|
|
111
|
+
key-absence assertions plus an exact key-set assertion. And the spec's `error.details` —
|
|
112
|
+
read at one site in `client.ts` off a hand-written inline type — is now tied to the
|
|
113
|
+
generated schema by a PRESENCE pin, so dropping the field from `docs/api/openapi.yaml` fails
|
|
114
|
+
the build. Every pin is key-level, never value-level, so a deliberate hand-authored value
|
|
115
|
+
refinement still cannot trip it. Test- and type-level only: no runtime behavior, no exported
|
|
116
|
+
type, and nothing in `dist/` changes. (#2793)
|
|
117
|
+
|
|
11
118
|
## [0.5.0] - 2026-07-29
|
|
12
119
|
|
|
13
120
|
### Added
|
|
@@ -152,7 +259,12 @@ today, see [README.md](./README.md), which remains the authoritative usage guide
|
|
|
152
259
|
rebuilding the shared `dist`), which is now fixed. No consumer-facing changes
|
|
153
260
|
beyond 0.2.1.
|
|
154
261
|
|
|
155
|
-
## [0.2.1] - 2026-07-16
|
|
262
|
+
## [0.2.1] - 2026-07-16 [NEVER PUBLISHED]
|
|
263
|
+
|
|
264
|
+
> **Never published to npm.** This version was cut in-tree but no `@hunt-labs/bytekit-sdk@0.2.1`
|
|
265
|
+
> tarball exists — `npm i @hunt-labs/bytekit-sdk@0.2.1` fails. Everything below shipped to users in
|
|
266
|
+
> **0.2.2**, which is the first published release containing it. Reconciled by issue #2680; the
|
|
267
|
+
> `release-drift` CI gate now blocks a new phantom heading from being introduced.
|
|
156
268
|
|
|
157
269
|
### Changed
|
|
158
270
|
|
|
@@ -212,7 +324,7 @@ against 0.1.0 — read each one before upgrading.
|
|
|
212
324
|
- Publish-ready packaging metadata: types-first `exports`, `engines`, `keywords`,
|
|
213
325
|
`homepage`, `bugs`, `README.md`, `LICENSE`, and an MIT `license` field.
|
|
214
326
|
|
|
215
|
-
## [0.1.0] - 2026
|
|
327
|
+
## [0.1.0] - 2026-04-24
|
|
216
328
|
|
|
217
329
|
### Added
|
|
218
330
|
|
|
@@ -220,6 +332,7 @@ against 0.1.0 — read each one before upgrading.
|
|
|
220
332
|
(scrape, screenshot, search, and related endpoints), published under the original
|
|
221
333
|
`@rapidcrawl/sdk` name.
|
|
222
334
|
|
|
335
|
+
[0.6.0]: https://github.com/Hunt-Labs-Inc/ByteKit/tree/main/packages/sdk-typescript
|
|
223
336
|
[0.5.0]: https://github.com/Hunt-Labs-Inc/ByteKit/tree/main/packages/sdk-typescript
|
|
224
337
|
[0.4.0]: https://github.com/Hunt-Labs-Inc/ByteKit/tree/main/packages/sdk-typescript
|
|
225
338
|
[0.3.0]: https://github.com/Hunt-Labs-Inc/ByteKit/tree/main/packages/sdk-typescript
|
package/README.md
CHANGED
|
@@ -77,16 +77,24 @@ binary content (images, PDFs, etc. — `/v1/fetch` supports raw passthrough)
|
|
|
77
77
|
use `bytes` instead, which is byte-for-byte identical to the wire content:
|
|
78
78
|
|
|
79
79
|
```ts
|
|
80
|
+
import type { FetchCacheStatus } from '@hunt-labs/bytekit-sdk';
|
|
81
|
+
|
|
80
82
|
const res = await client.fetch.create({ url: 'https://example.com' });
|
|
81
83
|
|
|
82
84
|
console.log(res.body); // raw content (e.g. "<!doctype html>…") — UTF-8 decoded
|
|
83
85
|
console.log(res.bytes); // Uint8Array — byte-exact, safe for binary content
|
|
84
86
|
console.log(res.id); // X-Fetch-ID, e.g. "ft_0123abcd…"
|
|
85
87
|
console.log(res.creditsCharged); // X-Fetch-Credits-Charged, as a number
|
|
86
|
-
console.log(res.cache); // "hit" | "miss" | "bypass"
|
|
87
88
|
console.log(res.custom); // base64 X-Fetch-Custom echo, or null
|
|
88
89
|
console.log(res.contentType); // response Content-Type, e.g. "text/html"
|
|
89
90
|
|
|
91
|
+
// `cache` is NULLABLE — null when the response carried no X-Fetch-Cache header
|
|
92
|
+
// (or an unrecognized value), so narrow it before use. The annotation is
|
|
93
|
+
// deliberate: the README compile guard typechecks this line, so the documented
|
|
94
|
+
// type can no longer drift from the real one.
|
|
95
|
+
const cache: FetchCacheStatus | null = res.cache; // "hit" | "miss" | "bypass" | null
|
|
96
|
+
if (cache !== null) console.log(cache);
|
|
97
|
+
|
|
90
98
|
// Anything not surfaced as a named field is on res.headers:
|
|
91
99
|
console.log(res.headers.get('X-Fetch-Waf-Vendor'));
|
|
92
100
|
```
|
|
@@ -97,6 +105,9 @@ console.log(res.headers.get('X-Fetch-Waf-Vendor'));
|
|
|
97
105
|
const results = await client.search.query({ query: 'ai web scraping', limit: 10 });
|
|
98
106
|
for (const hit of results.results) {
|
|
99
107
|
console.log(hit.position, hit.title, hit.url);
|
|
108
|
+
// `snippet` and `date` are present for `web`/`news` results and omitted
|
|
109
|
+
// entirely for `type: 'images'` — read them behind a guard.
|
|
110
|
+
if (typeof hit.snippet === 'string') console.log(hit.snippet);
|
|
100
111
|
}
|
|
101
112
|
```
|
|
102
113
|
|
|
@@ -239,6 +250,42 @@ try {
|
|
|
239
250
|
}
|
|
240
251
|
```
|
|
241
252
|
|
|
253
|
+
### Reading which field was rejected
|
|
254
|
+
|
|
255
|
+
`err.details` carries the API envelope's structured rejection reason when the
|
|
256
|
+
response supplied one, and is `undefined` otherwise. Its type is
|
|
257
|
+
`ByteKitErrorDetails | undefined`, where `ByteKitErrorDetails` is
|
|
258
|
+
`Record<string, unknown>` — deliberately loose, because the contents vary by
|
|
259
|
+
error code:
|
|
260
|
+
|
|
261
|
+
- a `422 validation_error` carries `{ formErrors, fieldErrors }`, and
|
|
262
|
+
`fieldErrors` names exactly which request field failed;
|
|
263
|
+
- a `400 invalid_url` carries a flat `{ url, field, reason }`.
|
|
264
|
+
|
|
265
|
+
Narrow the member you want before using it:
|
|
266
|
+
|
|
267
|
+
```ts
|
|
268
|
+
import { ByteKit, ByteKitError } from '@hunt-labs/bytekit-sdk';
|
|
269
|
+
|
|
270
|
+
try {
|
|
271
|
+
await client.scrape.create({ url: 'https://example.com' });
|
|
272
|
+
} catch (err) {
|
|
273
|
+
if (err instanceof ByteKitError && err.status === 422) {
|
|
274
|
+
const fieldErrors = err.details?.['fieldErrors'];
|
|
275
|
+
if (typeof fieldErrors === 'object' && fieldErrors !== null) {
|
|
276
|
+
// e.g. { formats: ["Invalid enum value. Expected 'markdown' | …"] }
|
|
277
|
+
console.error(Object.keys(fieldErrors));
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Note that `details` is populated only from the API's canonical
|
|
284
|
+
`{ error: { … } }` envelope. A few endpoints answer with a flat, vendor-masked
|
|
285
|
+
body instead (`/v1/search` returns `502 {"error":"search_provider_error"}`);
|
|
286
|
+
those carry no structured detail, so `err.details` stays `undefined` there —
|
|
287
|
+
`err.code` is still the machine-readable reason.
|
|
288
|
+
|
|
242
289
|
## Documentation
|
|
243
290
|
|
|
244
291
|
Full API reference: [bytekit.com/docs](https://bytekit.com/docs).
|
package/dist/client.d.ts
CHANGED
|
@@ -69,11 +69,57 @@ export interface ApiResponse<T> {
|
|
|
69
69
|
}
|
|
70
70
|
/** Why a request never produced an HTTP response. */
|
|
71
71
|
export type ConnectionErrorCode = 'timeout' | 'aborted' | 'connection';
|
|
72
|
+
/**
|
|
73
|
+
* Structured, machine-readable detail about WHY a request was rejected — the
|
|
74
|
+
* `error.details` member of the API's canonical error envelope
|
|
75
|
+
* (`docs/api/openapi.yaml` §components.schemas.Error, issue #2685).
|
|
76
|
+
*
|
|
77
|
+
* The spec declares the field with no schema at all ("Optional structured details
|
|
78
|
+
* (e.g. which fields failed validation)"), because its contents vary by error code.
|
|
79
|
+
* Two shapes are live today and both must survive unchanged:
|
|
80
|
+
*
|
|
81
|
+
* - A 422 `validation_error` carries zod's `flatten()` output —
|
|
82
|
+
* `{ formErrors: string[], fieldErrors: Record<string, string[]> }` — which is
|
|
83
|
+
* the shape that names WHICH field failed.
|
|
84
|
+
* - A 400 `invalid_url` carries a flat record — `{ url, field, reason }` — with
|
|
85
|
+
* none of zod's keys.
|
|
86
|
+
*
|
|
87
|
+
* It is therefore typed as a loose record rather than a union of the shapes seen
|
|
88
|
+
* so far: a narrower type would have to drop or mangle the next variant the API
|
|
89
|
+
* adds, and the SDK must never lose information the envelope carried. Read a
|
|
90
|
+
* member and narrow it yourself:
|
|
91
|
+
*
|
|
92
|
+
* ```ts
|
|
93
|
+
* const fieldErrors = err.details?.['fieldErrors'];
|
|
94
|
+
* if (fieldErrors !== undefined && typeof fieldErrors === 'object') { … }
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export type ByteKitErrorDetails = Record<string, unknown>;
|
|
72
98
|
/** Thrown when the API responds with a 4xx or 5xx status code. */
|
|
73
99
|
export declare class ByteKitError extends Error {
|
|
74
100
|
readonly status: number;
|
|
75
101
|
readonly code: string;
|
|
76
|
-
|
|
102
|
+
/**
|
|
103
|
+
* The envelope's `error.details`, when it carried one — `undefined` otherwise.
|
|
104
|
+
*
|
|
105
|
+
* Populated ONLY from the canonical `{ error: { … } }` envelope. A flat
|
|
106
|
+
* vendor-masked body (`/v1/search` 502 `{"error":"search_provider_error"}`)
|
|
107
|
+
* has no envelope to read a detail from, so it never grows one (#2685 E3).
|
|
108
|
+
*
|
|
109
|
+
* A `details` that is not a JSON object — which the untyped spec field
|
|
110
|
+
* technically permits — is dropped rather than surfaced, because the declared
|
|
111
|
+
* type promises something indexable and a value that contradicts its own type
|
|
112
|
+
* is worse than an absent one.
|
|
113
|
+
*
|
|
114
|
+
* `declare` is load-bearing. `target: ES2022` turns on `useDefineForClassFields`,
|
|
115
|
+
* so a plain `details?: …` declaration would emit a class field and DEFINE the
|
|
116
|
+
* key as `undefined` on every instance — every error the SDK has ever thrown
|
|
117
|
+
* would silently grow a `details` key, changing `'details' in err`, spreads, and
|
|
118
|
+
* `JSON.stringify` output for errors that carry none. `declare` emits no field,
|
|
119
|
+
* so the key exists only when the envelope really supplied one.
|
|
120
|
+
*/
|
|
121
|
+
readonly details?: ByteKitErrorDetails;
|
|
122
|
+
constructor(status: number, code: string, message: string, details?: ByteKitErrorDetails);
|
|
77
123
|
}
|
|
78
124
|
/**
|
|
79
125
|
* Thrown when the request never produced an HTTP response at all — DNS, socket, TLS,
|
|
@@ -88,6 +134,43 @@ export declare class ByteKitConnectionError extends Error {
|
|
|
88
134
|
cause?: unknown;
|
|
89
135
|
});
|
|
90
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* Assemble an API request path from a fixed template and caller-supplied dynamic segments
|
|
139
|
+
* (issue #2677).
|
|
140
|
+
*
|
|
141
|
+
* This is the SINGLE place the SDK turns a caller-supplied id into a URL, mirroring the
|
|
142
|
+
* Python SDK's single-site fix (#2593). Every resource routes through it; no resource
|
|
143
|
+
* interpolates an id into a path template of its own. Before it existed, every path was a
|
|
144
|
+
* raw template literal, so a crafted or interpolated id silently retargeted the request —
|
|
145
|
+
* `scrape.get('sc_/../../account')` addressed `GET /v1/account` once WHATWG URL parsing
|
|
146
|
+
* collapsed the `../` segments, and an id carrying `?` or `#` grew a real query string and
|
|
147
|
+
* fragment. Branded id types (`sc_${string}`) constrain the PREFIX, never the tail, so they
|
|
148
|
+
* were never a defence.
|
|
149
|
+
*
|
|
150
|
+
* `template` is an OpenAPI-style path whose `{name}` placeholders are the only parts that
|
|
151
|
+
* get encoded. Everything else — including every `/` separator and every fixed segment —
|
|
152
|
+
* is emitted verbatim: the helper encodes SEGMENTS, never the assembled path. Lifting the
|
|
153
|
+
* encoding to the whole string would turn `/v1/scrape/{id}` into one giant escaped segment
|
|
154
|
+
* and break every request.
|
|
155
|
+
*
|
|
156
|
+
* Each value is passed through `encodeURIComponent` EXACTLY once, so an id that already
|
|
157
|
+
* contains a `%` becomes `%25…` rather than being double-encoded.
|
|
158
|
+
*
|
|
159
|
+
* `query` is appended verbatim after the path. It is the caller's job to hand over an
|
|
160
|
+
* ALREADY-ENCODED string — `toQueryString()` and `URLSearchParams.toString()` both do that
|
|
161
|
+
* — because a query string is a composed `?a=1&b=2` structure whose separators must survive,
|
|
162
|
+
* exactly as the path's separators do.
|
|
163
|
+
*
|
|
164
|
+
* A `{name}` with no matching value, or a value with no matching `{name}`, throws a
|
|
165
|
+
* `TypeError`: a typo in either half would otherwise ship a literal `{id}` in the URL or
|
|
166
|
+
* silently drop the id.
|
|
167
|
+
*/
|
|
168
|
+
export declare function buildPath(template: string, options?: {
|
|
169
|
+
/** Dynamic path segments by placeholder name. Each is percent-encoded exactly once. */
|
|
170
|
+
readonly segments?: Readonly<Record<string, string>>;
|
|
171
|
+
/** An already-encoded query string, including its leading `?`. Appended verbatim. */
|
|
172
|
+
readonly query?: string;
|
|
173
|
+
}): string;
|
|
91
174
|
export declare class ByteKit {
|
|
92
175
|
private readonly apiKey;
|
|
93
176
|
private readonly baseUrl;
|
|
@@ -125,7 +208,8 @@ export declare class ByteKit {
|
|
|
125
208
|
* - `ByteKitConnectionError` when no HTTP response was produced at all
|
|
126
209
|
* (DNS/socket/TLS failure, client-side timeout, or caller abort) — including one
|
|
127
210
|
* that fires while the body is streaming.
|
|
128
|
-
* - `ByteKitError` on any 4xx/5xx, carrying the API's `code`/`message
|
|
211
|
+
* - `ByteKitError` on any 4xx/5xx, carrying the API's `code`/`message` — plus the
|
|
212
|
+
* envelope's structured `details` when it carried one (#2685).
|
|
129
213
|
*
|
|
130
214
|
* The body is read through a continuation each caller supplies — JSON text for
|
|
131
215
|
* `request()`, raw bytes for `requestRaw()`. This is the single place that owns auth,
|
package/dist/client.js
CHANGED
|
@@ -21,11 +21,16 @@ const DEFAULT_TIMEOUT_MS = 120_000;
|
|
|
21
21
|
export class ByteKitError extends Error {
|
|
22
22
|
status;
|
|
23
23
|
code;
|
|
24
|
-
constructor(status, code, message) {
|
|
24
|
+
constructor(status, code, message, details) {
|
|
25
25
|
super(message);
|
|
26
26
|
this.name = 'ByteKitError';
|
|
27
27
|
this.status = status;
|
|
28
28
|
this.code = code;
|
|
29
|
+
// Assigned conditionally under `exactOptionalPropertyTypes`: a three-argument
|
|
30
|
+
// construction leaves the instance without the key at all, exactly as before.
|
|
31
|
+
if (details !== undefined) {
|
|
32
|
+
this.details = details;
|
|
33
|
+
}
|
|
29
34
|
}
|
|
30
35
|
}
|
|
31
36
|
/**
|
|
@@ -44,6 +49,59 @@ export class ByteKitConnectionError extends Error {
|
|
|
44
49
|
}
|
|
45
50
|
}
|
|
46
51
|
const NOOP = () => { };
|
|
52
|
+
/** A `{name}` placeholder in a {@link buildPath} template. */
|
|
53
|
+
const PATH_PLACEHOLDER = /\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
|
|
54
|
+
/**
|
|
55
|
+
* Assemble an API request path from a fixed template and caller-supplied dynamic segments
|
|
56
|
+
* (issue #2677).
|
|
57
|
+
*
|
|
58
|
+
* This is the SINGLE place the SDK turns a caller-supplied id into a URL, mirroring the
|
|
59
|
+
* Python SDK's single-site fix (#2593). Every resource routes through it; no resource
|
|
60
|
+
* interpolates an id into a path template of its own. Before it existed, every path was a
|
|
61
|
+
* raw template literal, so a crafted or interpolated id silently retargeted the request —
|
|
62
|
+
* `scrape.get('sc_/../../account')` addressed `GET /v1/account` once WHATWG URL parsing
|
|
63
|
+
* collapsed the `../` segments, and an id carrying `?` or `#` grew a real query string and
|
|
64
|
+
* fragment. Branded id types (`sc_${string}`) constrain the PREFIX, never the tail, so they
|
|
65
|
+
* were never a defence.
|
|
66
|
+
*
|
|
67
|
+
* `template` is an OpenAPI-style path whose `{name}` placeholders are the only parts that
|
|
68
|
+
* get encoded. Everything else — including every `/` separator and every fixed segment —
|
|
69
|
+
* is emitted verbatim: the helper encodes SEGMENTS, never the assembled path. Lifting the
|
|
70
|
+
* encoding to the whole string would turn `/v1/scrape/{id}` into one giant escaped segment
|
|
71
|
+
* and break every request.
|
|
72
|
+
*
|
|
73
|
+
* Each value is passed through `encodeURIComponent` EXACTLY once, so an id that already
|
|
74
|
+
* contains a `%` becomes `%25…` rather than being double-encoded.
|
|
75
|
+
*
|
|
76
|
+
* `query` is appended verbatim after the path. It is the caller's job to hand over an
|
|
77
|
+
* ALREADY-ENCODED string — `toQueryString()` and `URLSearchParams.toString()` both do that
|
|
78
|
+
* — because a query string is a composed `?a=1&b=2` structure whose separators must survive,
|
|
79
|
+
* exactly as the path's separators do.
|
|
80
|
+
*
|
|
81
|
+
* A `{name}` with no matching value, or a value with no matching `{name}`, throws a
|
|
82
|
+
* `TypeError`: a typo in either half would otherwise ship a literal `{id}` in the URL or
|
|
83
|
+
* silently drop the id.
|
|
84
|
+
*/
|
|
85
|
+
export function buildPath(template, options) {
|
|
86
|
+
const segments = options?.segments;
|
|
87
|
+
const filled = new Set();
|
|
88
|
+
const path = template.replace(PATH_PLACEHOLDER, (_match, name) => {
|
|
89
|
+
const value = segments === undefined ? undefined : segments[name];
|
|
90
|
+
if (value === undefined) {
|
|
91
|
+
throw new TypeError(`ByteKit: no value supplied for path segment "${name}" in "${template}"`);
|
|
92
|
+
}
|
|
93
|
+
filled.add(name);
|
|
94
|
+
return encodeURIComponent(String(value));
|
|
95
|
+
});
|
|
96
|
+
if (segments !== undefined) {
|
|
97
|
+
for (const name of Object.keys(segments)) {
|
|
98
|
+
if (!filled.has(name)) {
|
|
99
|
+
throw new TypeError(`ByteKit: path template "${template}" has no "{${name}}" placeholder`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return `${path}${options?.query ?? ''}`;
|
|
104
|
+
}
|
|
47
105
|
/**
|
|
48
106
|
* Validate a timeout option. `undefined` passes through; anything that is not a
|
|
49
107
|
* non-negative finite number throws a `TypeError` naming the option as the caller
|
|
@@ -279,7 +337,8 @@ export class ByteKit {
|
|
|
279
337
|
* - `ByteKitConnectionError` when no HTTP response was produced at all
|
|
280
338
|
* (DNS/socket/TLS failure, client-side timeout, or caller abort) — including one
|
|
281
339
|
* that fires while the body is streaming.
|
|
282
|
-
* - `ByteKitError` on any 4xx/5xx, carrying the API's `code`/`message
|
|
340
|
+
* - `ByteKitError` on any 4xx/5xx, carrying the API's `code`/`message` — plus the
|
|
341
|
+
* envelope's structured `details` when it carried one (#2685).
|
|
283
342
|
*
|
|
284
343
|
* The body is read through a continuation each caller supplies — JSON text for
|
|
285
344
|
* `request()`, raw bytes for `requestRaw()`. This is the single place that owns auth,
|
|
@@ -323,6 +382,7 @@ export class ByteKit {
|
|
|
323
382
|
if (!response.ok) {
|
|
324
383
|
let code = 'internal_error';
|
|
325
384
|
let message = response.statusText;
|
|
385
|
+
let details;
|
|
326
386
|
try {
|
|
327
387
|
const json = (await response.json());
|
|
328
388
|
// Canonical unified envelope. Tested first, so it always wins: the flat
|
|
@@ -332,6 +392,15 @@ export class ByteKit {
|
|
|
332
392
|
if (typeof json.error === 'object' && json.error !== null) {
|
|
333
393
|
code = json.error.code ?? code;
|
|
334
394
|
message = json.error.message ?? message;
|
|
395
|
+
// Structured rejection reason (issue #2685). This is the ONLY site
|
|
396
|
+
// that reads it: the flat branch below has no envelope to read from
|
|
397
|
+
// and must never grow a phantom `details` (E3). Arrays and scalars
|
|
398
|
+
// are rejected because `ByteKitErrorDetails` promises an indexable
|
|
399
|
+
// record — see the type's doc comment.
|
|
400
|
+
const raw = json.error.details;
|
|
401
|
+
if (typeof raw === 'object' && raw !== null && !Array.isArray(raw)) {
|
|
402
|
+
details = raw;
|
|
403
|
+
}
|
|
335
404
|
}
|
|
336
405
|
else if (typeof json.error === 'string' && json.error.length > 0) {
|
|
337
406
|
// Flat vendor-masked shape, e.g. /v1/search 502
|
|
@@ -347,7 +416,7 @@ export class ByteKit {
|
|
|
347
416
|
// leave defaults — including when the error body itself stalls and the
|
|
348
417
|
// budget aborts the read (#2546), so a stuck 500 still surfaces its status.
|
|
349
418
|
}
|
|
350
|
-
throw new ByteKitError(response.status, code, message);
|
|
419
|
+
throw new ByteKitError(response.status, code, message, details);
|
|
351
420
|
}
|
|
352
421
|
try {
|
|
353
422
|
return await readBody(response);
|
package/dist/generated/api.d.ts
CHANGED
|
@@ -330,7 +330,19 @@ export interface paths {
|
|
|
330
330
|
path?: never;
|
|
331
331
|
cookie?: never;
|
|
332
332
|
};
|
|
333
|
-
/**
|
|
333
|
+
/**
|
|
334
|
+
* Get fetch bulk job status
|
|
335
|
+
* @description Returns the job envelope plus its per-URL items.
|
|
336
|
+
*
|
|
337
|
+
* **Downloading item content.** Each completed item's `content_url` is a
|
|
338
|
+
* **pre-signed** URL, minted fresh on every read of this endpoint and valid
|
|
339
|
+
* for **600 seconds (10 minutes)** — the same signing lifetime as the
|
|
340
|
+
* `image_url` returned by `GET /v1/bulk/{id}/screenshots`. The underlying
|
|
341
|
+
* objects are private, so an unsigned URL returns `401`: download the
|
|
342
|
+
* content inside that window, or re-poll this endpoint for a fresh
|
|
343
|
+
* signature. Items that are not `completed`, and completed items with no
|
|
344
|
+
* stored object, carry `null` or the unsigned stored URL instead.
|
|
345
|
+
*/
|
|
334
346
|
get: operations["getFetchBulk"];
|
|
335
347
|
put?: never;
|
|
336
348
|
post?: never;
|
|
@@ -407,7 +419,20 @@ export interface paths {
|
|
|
407
419
|
path?: never;
|
|
408
420
|
cookie?: never;
|
|
409
421
|
};
|
|
410
|
-
/**
|
|
422
|
+
/**
|
|
423
|
+
* List bulk job items
|
|
424
|
+
* @description Returns the bulk job's items, discriminated by `type`.
|
|
425
|
+
*
|
|
426
|
+
* **Downloading item artifacts.** `image_url` (`type: screenshot`) and both
|
|
427
|
+
* `content_url` and `markdown_url` (`type: scrape`) are **pre-signed** URLs,
|
|
428
|
+
* minted fresh on every read of this endpoint and valid for **600 seconds
|
|
429
|
+
* (10 minutes)** —
|
|
430
|
+
* the same signing lifetime as `content_url` on `GET /v1/fetch/bulk/{id}`.
|
|
431
|
+
* The underlying objects are private, so an unsigned URL returns `401`:
|
|
432
|
+
* download inside that window, or re-poll this endpoint for a fresh
|
|
433
|
+
* signature. Items that are not `completed`, and completed items with no
|
|
434
|
+
* stored object, carry `null` or the unsigned stored URL instead.
|
|
435
|
+
*/
|
|
411
436
|
get: operations["listBulkScreenshots"];
|
|
412
437
|
put?: never;
|
|
413
438
|
post?: never;
|
|
@@ -772,9 +797,9 @@ export interface paths {
|
|
|
772
797
|
get?: never;
|
|
773
798
|
put?: never;
|
|
774
799
|
/**
|
|
775
|
-
*
|
|
776
|
-
* @description
|
|
777
|
-
*
|
|
800
|
+
* Legacy cancellation compatibility terminal
|
|
801
|
+
* @description Returns 410. Customer cancellation is initiated through the hosted
|
|
802
|
+
* Billing Portal and projected through Stripe webhooks.
|
|
778
803
|
*/
|
|
779
804
|
post: operations["cancelSubscription"];
|
|
780
805
|
delete?: never;
|
|
@@ -3113,13 +3138,41 @@ export interface operations {
|
|
|
3113
3138
|
"application/json": components["schemas"]["Error"];
|
|
3114
3139
|
};
|
|
3115
3140
|
};
|
|
3116
|
-
/**
|
|
3141
|
+
/**
|
|
3142
|
+
* @description Validation error, or a screenshot that failed inside the sync hold.
|
|
3143
|
+
* The two carry DIFFERENT bodies (issue #2679):
|
|
3144
|
+
*
|
|
3145
|
+
* - A request-validation failure returns the unified `Error` envelope
|
|
3146
|
+
* with code `validation_error`.
|
|
3147
|
+
* - A capture that reached a terminal `failed` state inside the sync
|
|
3148
|
+
* hold returns the full `ScreenshotResponse` resource, whose
|
|
3149
|
+
* `error_code`/`error_message` carry the real failure (`blocked`,
|
|
3150
|
+
* `timeout`, …). This is the canonical shape for that case: the
|
|
3151
|
+
* resource body is what carries the `ss_` handle, `poll_url` and
|
|
3152
|
+
* `expires_at` a client needs to redeem the failed capture, none of
|
|
3153
|
+
* which fit the `Error` envelope.
|
|
3154
|
+
*
|
|
3155
|
+
* Discriminate on the shape of the top-level `error` key. The envelope
|
|
3156
|
+
* form carries `error` as an OBJECT (`{ code, message, http_status,
|
|
3157
|
+
* failed_at }`); the resource form has no `error` key at all, and
|
|
3158
|
+
* carries the flat `error_code`/`error_message` pair plus `id` instead.
|
|
3159
|
+
*
|
|
3160
|
+
* Do NOT discriminate on `status`. Both forms carry
|
|
3161
|
+
* `"status": "failed"` on this response — `Error.status` is a required
|
|
3162
|
+
* property with `enum: [failed]` — so it separates nothing here.
|
|
3163
|
+
*
|
|
3164
|
+
* `X-Screenshot-ID` accompanies the resource form only: a
|
|
3165
|
+
* request-validation failure is raised before a screenshot row exists,
|
|
3166
|
+
* so no screenshot headers travel with it.
|
|
3167
|
+
*/
|
|
3117
3168
|
422: {
|
|
3118
3169
|
headers: {
|
|
3170
|
+
/** @description Stable screenshot identifier (matches `id` in the resource body). Emitted only on the failed-capture variant; absent on a request-validation failure. */
|
|
3171
|
+
"X-Screenshot-ID"?: string;
|
|
3119
3172
|
[name: string]: unknown;
|
|
3120
3173
|
};
|
|
3121
3174
|
content: {
|
|
3122
|
-
"application/json": components["schemas"]["Error"];
|
|
3175
|
+
"application/json": components["schemas"]["Error"] | components["schemas"]["ScreenshotResponse"];
|
|
3123
3176
|
};
|
|
3124
3177
|
};
|
|
3125
3178
|
/** @description Rate limit (`rate_limited`) or concurrency limit (`concurrency_limit`) exceeded. Carries a `Retry-After` header — seconds derived from the same clock as `X-RateLimit-Reset` for the rate-limit variant, a fixed small back-off for the concurrency variant. */
|
|
@@ -3947,7 +4000,10 @@ export interface operations {
|
|
|
3947
4000
|
webhook_url: string;
|
|
3948
4001
|
/** @description Per-URL fetch items, oldest first, capped at 500 (listBulkFetchItemsWithLatestAttempt). */
|
|
3949
4002
|
items: {
|
|
3950
|
-
/**
|
|
4003
|
+
/**
|
|
4004
|
+
* @description Scrape-row id, `sc_`-prefixed. Fetch-bulk items are rows of the same resource sync `/v1/scrape` returns, so they share its prefix. Not to be confused with the `ft_`-prefixed `X-Fetch-ID` correlation header on sync `/v1/fetch`, which identifies a request rather than a stored item.
|
|
4005
|
+
* @example sc_01j9abc123
|
|
4006
|
+
*/
|
|
3951
4007
|
id: string;
|
|
3952
4008
|
/** Format: uri */
|
|
3953
4009
|
url: string;
|
|
@@ -3955,6 +4011,7 @@ export interface operations {
|
|
|
3955
4011
|
status: "pending" | "processing" | "completed" | "failed" | "cancelled";
|
|
3956
4012
|
content_type: string | null;
|
|
3957
4013
|
content_length: number | null;
|
|
4014
|
+
/** @description Pre-signed download URL for the fetched body, valid for 600 seconds (10 minutes) from the moment this response was generated — matching the signing TTL of `image_url` on `GET /v1/bulk/{id}/screenshots`. A new signature is minted on every poll. Null for items with no stored body; a completed item whose object cannot be signed falls back to the unsigned stored URL, which returns 401 on download. */
|
|
3958
4015
|
content_url: string | null;
|
|
3959
4016
|
waf_vendor: string | null;
|
|
3960
4017
|
cache_hit: boolean;
|
|
@@ -4412,7 +4469,7 @@ export interface operations {
|
|
|
4412
4469
|
format?: string;
|
|
4413
4470
|
/** @description `type: recording` only. */
|
|
4414
4471
|
video_url?: string | null;
|
|
4415
|
-
/** @description `type: scrape` only. */
|
|
4472
|
+
/** @description `type: scrape` only. Pre-signed download URL for the scraped body, valid for 600 seconds (10 minutes) from the moment this response was generated — matching the signing TTL of `image_url` above and of `content_url` on `GET /v1/fetch/bulk/{id}`. A new signature is minted on every poll. Null for items with no stored body; a completed item whose object cannot be signed falls back to the unsigned stored URL, which returns 401 on download. */
|
|
4416
4473
|
content_url?: string | null;
|
|
4417
4474
|
/** @description `type: scrape` only. */
|
|
4418
4475
|
content_type?: string | null;
|
|
@@ -4420,7 +4477,7 @@ export interface operations {
|
|
|
4420
4477
|
content_length?: number | null;
|
|
4421
4478
|
/** @description `type: scrape` only. Always null in bulk listings — fetch the body via `markdown_url`. */
|
|
4422
4479
|
markdown?: string | null;
|
|
4423
|
-
/** @description `type: scrape` only. */
|
|
4480
|
+
/** @description `type: scrape` only. Pre-signed download URL for the markdown rendering of the page, valid for 600 seconds (10 minutes) from the moment this response was generated — the same signing TTL as `content_url` and `image_url` above. A new signature is minted on every poll. Null for items with no stored markdown; a completed item whose object cannot be signed falls back to the unsigned stored URL, which returns 401 on download. */
|
|
4424
4481
|
markdown_url?: string | null;
|
|
4425
4482
|
}[];
|
|
4426
4483
|
};
|
|
@@ -5210,10 +5267,10 @@ export interface operations {
|
|
|
5210
5267
|
/** @description Plan base price in cents. */
|
|
5211
5268
|
plan_price_cents: number;
|
|
5212
5269
|
/**
|
|
5213
|
-
* @description Derived plan category. "payg" for pay-as-you-go plans, "
|
|
5270
|
+
* @description Derived plan category. "payg" for pay-as-you-go plans, "subscription" for all recurring paid plans.
|
|
5214
5271
|
* @enum {string}
|
|
5215
5272
|
*/
|
|
5216
|
-
plan_type: "payg" | "
|
|
5273
|
+
plan_type: "payg" | "subscription";
|
|
5217
5274
|
/** @description Total top-up spend in cents for this billing period. Present only in dual/bytes billing mode. */
|
|
5218
5275
|
topup_spent_cents?: number;
|
|
5219
5276
|
/** @description Remaining top-up bytes: max(0, topup_bytes − max(0, bytes_used − included_bytes − rollover_bytes)). Present only in dual/bytes billing mode. */
|
|
@@ -5418,7 +5475,7 @@ export interface operations {
|
|
|
5418
5475
|
error_message?: string | null;
|
|
5419
5476
|
/** @description Prefixed key id (ak_) that issued the request. */
|
|
5420
5477
|
api_key_id?: string | null;
|
|
5421
|
-
/** @description Stored content
|
|
5478
|
+
/** @description Stored content, image or video URL — null when the row has no stored object. For a `completed` row with a stored object this is a **pre-signed** download URL, minted fresh on every read of this endpoint and valid for **600 seconds (10 minutes)** — the same signing lifetime as `content_url` on `GET /v1/fetch/bulk/{id}` and `image_url` on `GET /v1/bulk/{id}/screenshots`. The underlying objects are private, so an unsigned URL returns 401: download inside that window, or re-poll this endpoint for a fresh signature. Rows that are not `completed`, and completed rows whose object cannot be signed, fall back to the unsigned stored URL, which returns 401 on download. */
|
|
5422
5479
|
artifact_url?: string | null;
|
|
5423
5480
|
/** @description The original request options. */
|
|
5424
5481
|
options?: {
|
|
@@ -5488,6 +5545,8 @@ export interface operations {
|
|
|
5488
5545
|
requestBody: {
|
|
5489
5546
|
content: {
|
|
5490
5547
|
"application/json": {
|
|
5548
|
+
/** @enum {string} */
|
|
5549
|
+
purpose: "payment_method_update" | "invoice_history" | "subscription_cancel";
|
|
5491
5550
|
/** Format: uri */
|
|
5492
5551
|
return_url: string;
|
|
5493
5552
|
};
|
|
@@ -5515,6 +5574,15 @@ export interface operations {
|
|
|
5515
5574
|
"application/json": components["schemas"]["Error"];
|
|
5516
5575
|
};
|
|
5517
5576
|
};
|
|
5577
|
+
/** @description Cancellation disclosure revision is absent or stale. */
|
|
5578
|
+
409: {
|
|
5579
|
+
headers: {
|
|
5580
|
+
[name: string]: unknown;
|
|
5581
|
+
};
|
|
5582
|
+
content: {
|
|
5583
|
+
"application/json": components["schemas"]["Error"];
|
|
5584
|
+
};
|
|
5585
|
+
};
|
|
5518
5586
|
};
|
|
5519
5587
|
};
|
|
5520
5588
|
registerPayg: {
|
|
@@ -5714,23 +5782,8 @@ export interface operations {
|
|
|
5714
5782
|
};
|
|
5715
5783
|
};
|
|
5716
5784
|
responses: {
|
|
5717
|
-
/** @description
|
|
5718
|
-
|
|
5719
|
-
headers: {
|
|
5720
|
-
[name: string]: unknown;
|
|
5721
|
-
};
|
|
5722
|
-
content: {
|
|
5723
|
-
"application/json": {
|
|
5724
|
-
cancelled: boolean;
|
|
5725
|
-
/** @enum {string} */
|
|
5726
|
-
refund: "prorated";
|
|
5727
|
-
/** Format: date-time */
|
|
5728
|
-
grace_period_expires_at: string;
|
|
5729
|
-
};
|
|
5730
|
-
};
|
|
5731
|
-
};
|
|
5732
|
-
/** @description No subscription or already cancelled. */
|
|
5733
|
-
400: {
|
|
5785
|
+
/** @description Cancellation moved to hosted Billing Portal. */
|
|
5786
|
+
410: {
|
|
5734
5787
|
headers: {
|
|
5735
5788
|
[name: string]: unknown;
|
|
5736
5789
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { ByteKit, ByteKitError, ByteKitConnectionError } from './client.js';
|
|
2
|
-
export type { ByteKitOptions, RequestOptions, ApiResponse, ConnectionErrorCode } from './client.js';
|
|
2
|
+
export type { ByteKitOptions, RequestOptions, ApiResponse, ConnectionErrorCode, ByteKitErrorDetails, } from './client.js';
|
|
3
3
|
export { V01_METHODS } from './allowlist.js';
|
|
4
4
|
export type { V01Method } from './allowlist.js';
|
|
5
5
|
export type { ScrapeId, ScreenshotId, BulkId, MonitorId, SitemapId, ApiKeyId, WebhookDeliveryId, SearchType, SearchDateRange, SearchOpts, SearchResult, SearchResultImage, SearchResponse, } from './types.js';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { buildPath } from '../client.js';
|
|
1
2
|
export class AccountResource {
|
|
2
3
|
client;
|
|
3
4
|
apiKeys;
|
|
@@ -35,11 +36,11 @@ class ApiKeysResource {
|
|
|
35
36
|
}
|
|
36
37
|
/** GET /v1/account/api-keys/{id} — get an API key by ID */
|
|
37
38
|
get(id, requestOptions) {
|
|
38
|
-
return this.client.request('GET',
|
|
39
|
+
return this.client.request('GET', buildPath('/v1/account/api-keys/{id}', { segments: { id } }), undefined, requestOptions);
|
|
39
40
|
}
|
|
40
41
|
/** PATCH /v1/account/api-keys/{id} — update an API key */
|
|
41
42
|
update(id, opts, requestOptions) {
|
|
42
|
-
return this.client.request('PATCH',
|
|
43
|
+
return this.client.request('PATCH', buildPath('/v1/account/api-keys/{id}', { segments: { id } }), opts, requestOptions);
|
|
43
44
|
}
|
|
44
45
|
/**
|
|
45
46
|
* DELETE /v1/account/api-keys/{id} — revoke an API key (idempotent).
|
|
@@ -53,10 +54,10 @@ class ApiKeysResource {
|
|
|
53
54
|
* method resolves cleanly on the gateway's empty 204 body.
|
|
54
55
|
*/
|
|
55
56
|
revoke(id, requestOptions) {
|
|
56
|
-
return this.client.request('DELETE',
|
|
57
|
+
return this.client.request('DELETE', buildPath('/v1/account/api-keys/{id}', { segments: { id } }), undefined, requestOptions);
|
|
57
58
|
}
|
|
58
59
|
/** POST /v1/account/api-keys/{id}/reveal — reveal the plaintext API key value */
|
|
59
60
|
reveal(id, requestOptions) {
|
|
60
|
-
return this.client.request('POST',
|
|
61
|
+
return this.client.request('POST', buildPath('/v1/account/api-keys/{id}/reveal', { segments: { id } }), undefined, requestOptions);
|
|
61
62
|
}
|
|
62
63
|
}
|
package/dist/resources/bulk.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { buildPath } from '../client.js';
|
|
1
2
|
export class BulkResource {
|
|
2
3
|
client;
|
|
3
4
|
screenshots;
|
|
@@ -16,11 +17,11 @@ export class BulkResource {
|
|
|
16
17
|
}
|
|
17
18
|
/** GET /v1/bulk/{id} — get a bulk job status */
|
|
18
19
|
get(id, requestOptions) {
|
|
19
|
-
return this.client.request('GET',
|
|
20
|
+
return this.client.request('GET', buildPath('/v1/bulk/{id}', { segments: { id } }), undefined, requestOptions);
|
|
20
21
|
}
|
|
21
22
|
/** DELETE /v1/bulk/{id} — cancel an in-flight bulk job */
|
|
22
23
|
cancel(id, requestOptions) {
|
|
23
|
-
return this.client.request('DELETE',
|
|
24
|
+
return this.client.request('DELETE', buildPath('/v1/bulk/{id}', { segments: { id } }), undefined, requestOptions);
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
27
|
class BulkScreenshotsResource {
|
|
@@ -30,6 +31,6 @@ class BulkScreenshotsResource {
|
|
|
30
31
|
}
|
|
31
32
|
/** GET /v1/bulk/{id}/screenshots — list screenshots in a bulk job */
|
|
32
33
|
list(id, requestOptions) {
|
|
33
|
-
return this.client.request('GET',
|
|
34
|
+
return this.client.request('GET', buildPath('/v1/bulk/{id}/screenshots', { segments: { id } }), undefined, requestOptions);
|
|
34
35
|
}
|
|
35
36
|
}
|
package/dist/resources/fetch.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { buildPath } from '../client.js';
|
|
1
2
|
/**
|
|
2
3
|
* The query params `GET /v1/fetch` accepts, in serialization order.
|
|
3
4
|
*
|
|
@@ -80,7 +81,7 @@ export class FetchResource {
|
|
|
80
81
|
if (value !== undefined)
|
|
81
82
|
params.set(key, String(value));
|
|
82
83
|
}
|
|
83
|
-
return toFetchResult(await this.client.requestRaw('GET',
|
|
84
|
+
return toFetchResult(await this.client.requestRaw('GET', buildPath('/v1/fetch', { query: `?${params.toString()}` }), undefined, requestOptions));
|
|
84
85
|
}
|
|
85
86
|
}
|
|
86
87
|
class FetchBulkResource {
|
|
@@ -94,6 +95,6 @@ class FetchBulkResource {
|
|
|
94
95
|
}
|
|
95
96
|
/** GET /v1/fetch/bulk/{id} — get a bulk fetch job */
|
|
96
97
|
get(id, requestOptions) {
|
|
97
|
-
return this.client.request('GET',
|
|
98
|
+
return this.client.request('GET', buildPath('/v1/fetch/bulk/{id}', { segments: { id } }), undefined, requestOptions);
|
|
98
99
|
}
|
|
99
100
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { toQueryString } from './query-string.js';
|
|
2
|
+
import { buildPath } from '../client.js';
|
|
2
3
|
export class MonitorsResource {
|
|
3
4
|
client;
|
|
4
5
|
captures;
|
|
@@ -20,15 +21,15 @@ export class MonitorsResource {
|
|
|
20
21
|
* serializes the bare `/v1/monitors` URL unchanged — existing zero-arg callers are unaffected.
|
|
21
22
|
*/
|
|
22
23
|
list(params, requestOptions) {
|
|
23
|
-
return this.client.request('GET',
|
|
24
|
+
return this.client.request('GET', buildPath('/v1/monitors', { query: toQueryString(params) }), undefined, requestOptions);
|
|
24
25
|
}
|
|
25
26
|
/** GET /v1/monitors/{id} — get a monitor by ID */
|
|
26
27
|
get(id, requestOptions) {
|
|
27
|
-
return this.client.request('GET',
|
|
28
|
+
return this.client.request('GET', buildPath('/v1/monitors/{id}', { segments: { id } }), undefined, requestOptions);
|
|
28
29
|
}
|
|
29
30
|
/** PATCH /v1/monitors/{id} — update a monitor */
|
|
30
31
|
update(id, opts, requestOptions) {
|
|
31
|
-
return this.client.request('PATCH',
|
|
32
|
+
return this.client.request('PATCH', buildPath('/v1/monitors/{id}', { segments: { id } }), opts, requestOptions);
|
|
32
33
|
}
|
|
33
34
|
/**
|
|
34
35
|
* DELETE /v1/monitors/{id} — delete a monitor.
|
|
@@ -39,7 +40,7 @@ export class MonitorsResource {
|
|
|
39
40
|
* body to `undefined` before it would parse as JSON (#2450).
|
|
40
41
|
*/
|
|
41
42
|
delete(id, requestOptions) {
|
|
42
|
-
return this.client.request('DELETE',
|
|
43
|
+
return this.client.request('DELETE', buildPath('/v1/monitors/{id}', { segments: { id } }), undefined, requestOptions);
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
46
|
class MonitorCapturesResource {
|
|
@@ -55,6 +56,6 @@ class MonitorCapturesResource {
|
|
|
55
56
|
* zero-arg callers are unaffected (inverse case).
|
|
56
57
|
*/
|
|
57
58
|
list(id, params, requestOptions) {
|
|
58
|
-
return this.client.request('GET',
|
|
59
|
+
return this.client.request('GET', buildPath('/v1/monitors/{id}/captures', { segments: { id }, query: toQueryString(params) }), undefined, requestOptions);
|
|
59
60
|
}
|
|
60
61
|
}
|
package/dist/resources/scrape.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { buildPath } from '../client.js';
|
|
1
2
|
export class ScrapeResource {
|
|
2
3
|
client;
|
|
3
4
|
bulk;
|
|
@@ -35,7 +36,7 @@ export class ScrapeResource {
|
|
|
35
36
|
* gateway and is out of this ticket's scope.
|
|
36
37
|
*/
|
|
37
38
|
get(id, requestOptions) {
|
|
38
|
-
return this.client.request('GET',
|
|
39
|
+
return this.client.request('GET', buildPath('/v1/scrape/{id}', { segments: { id } }), undefined, requestOptions);
|
|
39
40
|
}
|
|
40
41
|
}
|
|
41
42
|
class ScrapeBulkResource {
|
|
@@ -54,6 +55,6 @@ class ScrapeBulkResource {
|
|
|
54
55
|
}
|
|
55
56
|
/** GET /v1/scrape/bulk/{id} — get a bulk scrape job */
|
|
56
57
|
get(id, requestOptions) {
|
|
57
|
-
return this.client.request('GET',
|
|
58
|
+
return this.client.request('GET', buildPath('/v1/scrape/bulk/{id}', { segments: { id } }), undefined, requestOptions);
|
|
58
59
|
}
|
|
59
60
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { buildPath } from '../client.js';
|
|
1
2
|
export class ScreenshotsResource {
|
|
2
3
|
client;
|
|
3
4
|
constructor(client) {
|
|
@@ -17,7 +18,7 @@ export class ScreenshotsResource {
|
|
|
17
18
|
*/
|
|
18
19
|
create(opts, params, requestOptions) {
|
|
19
20
|
const suffix = params?.async === true ? '?async=true' : '';
|
|
20
|
-
return this.client.request('POST',
|
|
21
|
+
return this.client.request('POST', buildPath('/v1/screenshots', { query: suffix }), opts, requestOptions);
|
|
21
22
|
}
|
|
22
23
|
/**
|
|
23
24
|
* GET /v1/screenshots/{id} — poll a screenshot by ID.
|
|
@@ -26,7 +27,7 @@ export class ScreenshotsResource {
|
|
|
26
27
|
* (issue #2027), so narrow the result before reading `image_url`.
|
|
27
28
|
*/
|
|
28
29
|
get(id, requestOptions) {
|
|
29
|
-
return this.client.request('GET',
|
|
30
|
+
return this.client.request('GET', buildPath('/v1/screenshots/{id}', { segments: { id } }), undefined, requestOptions);
|
|
30
31
|
}
|
|
31
32
|
/**
|
|
32
33
|
* POST /v1/screenshots, returning the header set alongside the body (#2547).
|
|
@@ -42,7 +43,7 @@ export class ScreenshotsResource {
|
|
|
42
43
|
*/
|
|
43
44
|
createWithResponse(opts, params, requestOptions) {
|
|
44
45
|
const suffix = params?.async === true ? '?async=true' : '';
|
|
45
|
-
return this.client.requestWithResponse('POST',
|
|
46
|
+
return this.client.requestWithResponse('POST', buildPath('/v1/screenshots', { query: suffix }), opts, requestOptions);
|
|
46
47
|
}
|
|
47
48
|
/**
|
|
48
49
|
* GET /v1/screenshots/{id}, returning the header set alongside the body (#2547).
|
|
@@ -53,6 +54,6 @@ export class ScreenshotsResource {
|
|
|
53
54
|
* `X-Raw-Bytes`, `X-Billable-Bytes` and `X-Billing-Multiplier` headers are on `headers`.
|
|
54
55
|
*/
|
|
55
56
|
getWithResponse(id, requestOptions) {
|
|
56
|
-
return this.client.requestWithResponse('GET',
|
|
57
|
+
return this.client.requestWithResponse('GET', buildPath('/v1/screenshots/{id}', { segments: { id } }), undefined, requestOptions);
|
|
57
58
|
}
|
|
58
59
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { buildPath } from '../client.js';
|
|
1
2
|
export class SitemapResource {
|
|
2
3
|
client;
|
|
3
4
|
constructor(client) {
|
|
@@ -14,7 +15,7 @@ export class SitemapResource {
|
|
|
14
15
|
}
|
|
15
16
|
/** GET /v1/sitemap/{id} — get a sitemap job by ID */
|
|
16
17
|
get(id, requestOptions) {
|
|
17
|
-
return this.client.request('GET',
|
|
18
|
+
return this.client.request('GET', buildPath('/v1/sitemap/{id}', { segments: { id } }), undefined, requestOptions);
|
|
18
19
|
}
|
|
19
20
|
/**
|
|
20
21
|
* GET /v1/sitemap/{id}, returning the header set alongside the body (#2547).
|
|
@@ -27,6 +28,6 @@ export class SitemapResource {
|
|
|
27
28
|
* `X-Billable-Bytes` on this endpoint's 200; every one of them is on `headers`.
|
|
28
29
|
*/
|
|
29
30
|
getWithResponse(id, requestOptions) {
|
|
30
|
-
return this.client.requestWithResponse('GET',
|
|
31
|
+
return this.client.requestWithResponse('GET', buildPath('/v1/sitemap/{id}', { segments: { id } }), undefined, requestOptions);
|
|
31
32
|
}
|
|
32
33
|
}
|
package/dist/resources/usage.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { toQueryString } from './query-string.js';
|
|
2
|
+
import { buildPath } from '../client.js';
|
|
2
3
|
export class UsageResource {
|
|
3
4
|
client;
|
|
4
5
|
constructor(client) {
|
|
@@ -18,13 +19,13 @@ export class UsageResource {
|
|
|
18
19
|
* Default range is the last 30 days; max range is 366 days.
|
|
19
20
|
*/
|
|
20
21
|
daily(params, requestOptions) {
|
|
21
|
-
return this.client.request('GET',
|
|
22
|
+
return this.client.request('GET', buildPath('/v1/usage/daily', { query: toQueryString(params) }), undefined, requestOptions);
|
|
22
23
|
}
|
|
23
24
|
/**
|
|
24
25
|
* GET /v1/usage/by-endpoint — get per-endpoint bandwidth breakdown.
|
|
25
26
|
* Default range is the last 30 days; max range is 366 days.
|
|
26
27
|
*/
|
|
27
28
|
byEndpoint(params, requestOptions) {
|
|
28
|
-
return this.client.request('GET',
|
|
29
|
+
return this.client.request('GET', buildPath('/v1/usage/by-endpoint', { query: toQueryString(params) }), undefined, requestOptions);
|
|
29
30
|
}
|
|
30
31
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { toQueryString } from './query-string.js';
|
|
2
|
+
import { buildPath } from '../client.js';
|
|
2
3
|
export class WebhooksResource {
|
|
3
4
|
client;
|
|
4
5
|
constructor(client) {
|
|
@@ -11,10 +12,10 @@ export class WebhooksResource {
|
|
|
11
12
|
* optional caller `signal`; both override the constructor's `timeoutMs` default.
|
|
12
13
|
*/
|
|
13
14
|
list(params, requestOptions) {
|
|
14
|
-
return this.client.request('GET',
|
|
15
|
+
return this.client.request('GET', buildPath('/v1/webhook-deliveries', { query: toQueryString(params) }), undefined, requestOptions);
|
|
15
16
|
}
|
|
16
17
|
/** POST /v1/webhook-deliveries/{id}/retry — requeue a failed or exhausted delivery. */
|
|
17
18
|
retry(id, requestOptions) {
|
|
18
|
-
return this.client.request('POST',
|
|
19
|
+
return this.client.request('POST', buildPath('/v1/webhook-deliveries/{id}/retry', { segments: { id } }), undefined, requestOptions);
|
|
19
20
|
}
|
|
20
21
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -64,8 +64,12 @@ export interface SearchResult {
|
|
|
64
64
|
position: number;
|
|
65
65
|
title: string;
|
|
66
66
|
url: string;
|
|
67
|
-
/**
|
|
68
|
-
|
|
67
|
+
/**
|
|
68
|
+
* Short text excerpt for the result. Present for `web` and `news` results;
|
|
69
|
+
* omitted entirely (`undefined`) for `images` results — read it behind a
|
|
70
|
+
* guard, exactly like `date` (#2678).
|
|
71
|
+
*/
|
|
72
|
+
snippet?: string;
|
|
69
73
|
/**
|
|
70
74
|
* Published date when the provider reports one, else `null`. Present for
|
|
71
75
|
* `web`/`news` results; omitted entirely (`undefined`) for `images` results.
|