@crawlbrulee/sdk 0.4.0 โ†’ 0.7.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 CHANGED
@@ -1,17 +1,28 @@
1
- # @crawlbrulee/sdk
1
+ # ๐Ÿฎ crawlbrulee js/ts sdk
2
2
 
3
- The official TypeScript / JavaScript SDK for the [crawlbrulee](https://crawlbrulee.com) web-scraping API.
3
+ [![npm](https://img.shields.io/npm/v/@crawlbrulee/sdk?style=flat-square&label=npm)](https://www.npmjs.com/package/@crawlbrulee/sdk)
4
+ [![types](https://img.shields.io/npm/types/@crawlbrulee/sdk?style=flat-square&label=types)](https://www.npmjs.com/package/@crawlbrulee/sdk)
5
+ [![license](https://img.shields.io/npm/l/@crawlbrulee/sdk?style=flat-square&label=license)](./LICENSE)
4
6
 
5
- - Fully typed.
7
+ the official js/ts sdk for the [crawlbrulee](https://crawlbrulee.com) web-scraping api โ€” published to npm as [`@crawlbrulee/sdk`](https://www.npmjs.com/package/@crawlbrulee/sdk). you
8
+ send a url, you get back markdown, cleaned html, links, images, metadata, or a screenshot.
9
+
10
+ - fully typed.
6
11
  - ESM + CommonJS, ships its own `.d.ts`.
7
- - Zero runtime dependencies โ€” just `fetch`.
8
- - Works on Node.js 22+, modern Deno, Bun, and runtimes where `fetch` is available.
12
+ - zero runtime dependencies โ€” just `fetch`.
13
+ - works on Node.js 22+, modern Deno, Bun, and runtimes where `fetch` is available.
14
+
15
+ this readme covers the sdk itself โ€” the client, the types, and the js-side ergonomics. for how
16
+ the api behaves โ€” endpoints, parameters, and error semantics โ€” please see our
17
+ [api docs](https://crawlbrulee.com/docs).
18
+
19
+ > **status:** v0.7.0 (beta). the api surface is stabilizing โ€” expect minor breaking changes between 0.x releases.
9
20
 
10
- > **Status:** v0.4.0 (beta). API surface is stabilizing โ€” expect minor breaking changes between 0.x releases.
21
+ **get a free api key** โ†’ [dashboard.crawlbrulee.com](https://dashboard.crawlbrulee.com)
11
22
 
12
23
  ---
13
24
 
14
- ## Install
25
+ ## install
15
26
 
16
27
  ```bash
17
28
  pnpm add @crawlbrulee/sdk
@@ -21,12 +32,12 @@ npm install @crawlbrulee/sdk
21
32
  yarn add @crawlbrulee/sdk
22
33
  ```
23
34
 
24
- ## Quickstart
35
+ ## quickstart
25
36
 
26
37
  ```ts
27
38
  import { Crawlbrulee } from '@crawlbrulee/sdk'
28
39
 
29
- const crawlbrulee = new Crawlbrulee({ apiKey: 'cble_โ€ฆ' })
40
+ const crawlbrulee = new Crawlbrulee({ apiKey: 'cwbl_โ€ฆ' })
30
41
  // or read CRAWLBRULEE_API_KEY from the environment:
31
42
  const crawlbrulee = Crawlbrulee.fromEnv()
32
43
 
@@ -41,22 +52,38 @@ console.log(page.metadata?.title) // structured <head> metadata
41
52
  console.log(page.response_meta.usage.credits, 'credits charged') // usage accounting
42
53
  ```
43
54
 
44
- ### Configuration
55
+ ### authentication
45
56
 
46
- | Option | Default | Description |
47
- | ----------- | ---------------- | ------------------------------------------------------------------------------------------ |
48
- | `apiKey` | โ€” | API key, sent as `Authorization: Bearer โ€ฆ`. **Required** โ€” or use `Crawlbrulee.fromEnv()`. |
49
- | `timeoutMs` | `0` (no timeout) | Per-request timeout (covers headers + body). A per-call `timeoutMs` overrides this. |
57
+ every request carries your api key as `Authorization: Bearer <key>`. give it to the sdk one of two ways:
50
58
 
51
- `Crawlbrulee.fromEnv(overrides?)` reads the API key from `CRAWLBRULEE_API_KEY` and forwards any other option through `overrides`.
59
+ ```ts
60
+ // explicit โ€” pass the key directly
61
+ const crawlbrulee = new Crawlbrulee({ apiKey: 'cwbl_โ€ฆ' })
62
+
63
+ // from the environment โ€” reads CRAWLBRULEE_API_KEY
64
+ const crawlbrulee = Crawlbrulee.fromEnv()
65
+ ```
66
+
67
+ `Crawlbrulee.fromEnv(overrides?)` reads the key from `CRAWLBRULEE_API_KEY` and forwards any other option through
68
+ `overrides` (e.g. `Crawlbrulee.fromEnv({ timeoutMs: 30_000 })`). it throws if the variable is unset or empty. keys are
69
+ minted in the dashboard; see [authentication](https://crawlbrulee.com/docs/authentication) for how the api consumes them.
70
+
71
+ ### configuration
72
+
73
+ | option | default | description |
74
+ | ----------- | ----------------------------- | --------------------------------------------------------------------------------------------------- |
75
+ | `apiKey` | โ€” | api key, sent as `Authorization: Bearer โ€ฆ`. **required** โ€” or use `Crawlbrulee.fromEnv()`. |
76
+ | `baseUrl` | `https://api.crawlbrulee.com` | override the target host (local dev / staging). trailing slashes are stripped. |
77
+ | `timeoutMs` | `0` (no timeout) | per-request timeout in milliseconds (covers headers + body). a per-call `timeoutMs` overrides this. |
52
78
 
53
79
  ---
54
80
 
55
- ## API reference
81
+ ## api reference
56
82
 
57
- All methods return a `Promise` that resolves to the parsed JSON response, or rejects with a [`CrawlbruleeError`](#errors) subclass.
83
+ all methods return a `Promise` that resolves to the parsed json response, or rejects with a [`CrawlbruleeError`](#errors)
84
+ subclass.
58
85
 
59
- Every method accepts an optional second argument with per-call overrides:
86
+ every method accepts an optional second argument with per-call overrides:
60
87
 
61
88
  ```ts
62
89
  crawlbrulee.scrape(request, {
@@ -65,11 +92,11 @@ crawlbrulee.scrape(request, {
65
92
  })
66
93
  ```
67
94
 
68
- ### Scraping
95
+ ### scraping
69
96
 
70
97
  #### `crawlbrulee.scrape(request, options?)`
71
98
 
72
- Synchronously scrape a URL. The request blocks until the server is done.
99
+ scrape a url synchronously. the request blocks until the server is done.
73
100
 
74
101
  ```ts
75
102
  const page = await crawlbrulee.scrape({
@@ -93,37 +120,62 @@ const page = await crawlbrulee.scrape({
93
120
  })
94
121
  ```
95
122
 
96
- The response carries the extracted content alongside structured `metadata` (the parsed `<head>` tags โ€” `title`, `description`, OG/Twitter fields, โ€ฆ) and a `response_meta` envelope:
123
+ the response carries the extracted content alongside structured `metadata` (the parsed `<head>` tags โ€” `title`,
124
+ `description`, OG/Twitter fields, โ€ฆ) and a `response_meta` envelope:
97
125
 
98
126
  ```ts
99
127
  page.metadata?.title // structured <head> metadata (when extract.metadata, on by default)
100
128
 
101
129
  page.response_meta.usage.credits // credits charged โ€” 0 on a cache hit
102
- page.response_meta.usage.proxy // the resolved proxy tier actually used: 'none' | 'basic' | 'advanced' (never 'auto')
130
+ page.response_meta.usage.proxy // the resolved proxy tier actually used: 'basic' | 'advanced' (never 'auto')
103
131
  page.response_meta.usage.cache_hit // true when the result was served from cache
104
132
  ```
105
133
 
106
- See [`ScrapeRequest`](src/types/scrape.ts) and [`ScrapeResponse`](src/types/scrape.ts) for every field, with inline documentation.
134
+ notes:
135
+
136
+ - **`proxy`**: defaults to `auto` when omitted โ€” it starts at the basic tier and escalates to advanced on failure,
137
+ billed at the delivered tier. pass `'basic'` or `'advanced'` to pin a tier. on the response,
138
+ `response_meta.usage.proxy` reports the tier we resolved and used โ€” never `'auto'`. see
139
+ [proxies & location](https://crawlbrulee.com/docs/proxies) for what each tier does.
140
+ - **`screenshot`**: custom `viewport.width`/`height` are integers in `[16, 10000]` and `device_scale_factor` is in
141
+ `[1, 4]`; out-of-range values are rejected with a `400`. full capture options:
142
+ [screenshots](https://crawlbrulee.com/docs/scrape/screenshots).
143
+ - **`extract.images`**: urls preserve their query string and resolve document-relative `src`s against the full page url
144
+ (browser parity) โ€” the same rules as `links`. every extract field is documented under
145
+ [extraction](https://crawlbrulee.com/docs/scrape/extraction).
146
+ - **`warnings`**: when we complete a scrape but something is worth flagging โ€” e.g. `screenshot_truncated` when a long
147
+ page exceeded the scrolling-screenshot height cap โ€” the codes land on `page.warnings`. they're stable, so you can switch
148
+ on them. fresh scrapes only; cache hits omit warnings.
149
+ - **`unsupported_fields`**: if you request an extract that doesn't apply to the content type (e.g. `markdown` of a pdf),
150
+ that field name comes back on `page.unsupported_fields` and the rest of your payload is still returned.
151
+
152
+ see [`ScrapeRequest`](src/types/scrape.ts) and [`ScrapeResponse`](src/types/scrape.ts) for every field, with inline
153
+ documentation โ€” and the [scrape endpoint](https://crawlbrulee.com/docs/scrape) reference for the api-side contract those
154
+ types mirror.
107
155
 
108
156
  #### `crawlbrulee.scrapeAsync(request, options?)`
109
157
 
110
- Submit a scrape job in the background. Returns immediately with a `job_id`.
158
+ submit a scrape job in the background. returns immediately with a `job_id`.
111
159
 
112
160
  ```ts
113
161
  const { job_id } = await crawlbrulee.scrapeAsync({ url: 'https://example.com' })
114
162
  ```
115
163
 
164
+ pass a `webhook` to be notified on completion instead of polling โ€” see [webhooks](#webhooks).
165
+
116
166
  #### `crawlbrulee.getScrapeStatus(jobId, options?)`
117
167
 
118
- Look up the current status of an async job โ€” `pending`, `running`, `done`, or `failed`.
168
+ look up the current state of an async job โ€” `pending`, `running`, `done`, or `failed`. the response carries `job_id` and
169
+ `created_at` (snake_case, straight off the wire). once the job is `done`, it also carries usage accounting on
170
+ `response_meta.usage` (`credits`, `proxy`, `cache_hit`).
119
171
 
120
172
  #### `crawlbrulee.getScrapeResult(jobId, options?)`
121
173
 
122
- Fetch the result of a completed async job. Throws if the job hasn't finished yet.
174
+ fetch the result of a completed async job. throws if the job hasn't finished yet.
123
175
 
124
176
  #### `crawlbrulee.waitForScrape(jobId, options?)`
125
177
 
126
- Poll an async job until it reaches a terminal state, then return the scrape result.
178
+ poll an async job until it reaches a terminal state, then return the scrape result.
127
179
 
128
180
  ```ts
129
181
  const { job_id } = await crawlbrulee.scrapeAsync({ url: 'https://example.com' })
@@ -134,13 +186,16 @@ const page = await crawlbrulee.waitForScrape(job_id, {
134
186
  })
135
187
  ```
136
188
 
137
- Throws a `CrawlbruleeError` with `errorName: 'job_failed'` if the job ends in `failed`, or `errorName: 'request_timeout'` if the wait expires.
189
+ throws a `CrawlbruleeError` with `errorName: 'job_failed'` if the job ends in `failed`, or `errorName: 'request_timeout'`
190
+ if the wait expires. the job lifecycle itself โ€” states, retention, and when to prefer async over sync โ€” is documented
191
+ under [async scrape](https://crawlbrulee.com/docs/scrape/async).
138
192
 
139
- ### Mapping
193
+ ### mapping
140
194
 
141
195
  #### `crawlbrulee.map(request, options?)`
142
196
 
143
- Build (or return a cached) link map for a website. Combines sitemap discovery with the freshest cached homepage scrape when available.
197
+ build (or return a cached) link map for a website. combines sitemap discovery with the freshest cached homepage scrape
198
+ when available.
144
199
 
145
200
  ```ts
146
201
  const result = await crawlbrulee.map({
@@ -156,25 +211,41 @@ console.log(result.links.length, 'urls on page 1 of', result.response_meta.pagin
156
211
  console.log(result.response_meta.usage.credits, 'credits charged') // usage accounting, alongside pagination + truncation
157
212
  ```
158
213
 
159
- ### Account
214
+ `result.response_meta` carries `usage` (credits / resolved `proxy` / `cache_hit`) alongside the map-specific `pagination`
215
+ and `truncation` blocks. see the [map endpoint](https://crawlbrulee.com/docs/map) for discovery rules and pagination
216
+ semantics.
217
+
218
+ ### account
160
219
 
161
220
  #### `crawlbrulee.usage(options?)`
162
221
 
163
- Return the current billing-cycle snapshot โ€” total/used/available credits, used quota percentage, max concurrency, and the cycle reset timestamp.
222
+ return the current billing-cycle snapshot โ€” `total_credits`, `used_credits`, `available_credits`, `used_quota_percent`,
223
+ `max_concurrency`, and the `usage_reset` timestamp.
164
224
 
165
225
  #### `crawlbrulee.whoami(options?)`
166
226
 
167
- Return the organization name and identifying details of the API token used to authenticate the request.
227
+ return the organization name and token identity behind the api key (`organization_name`, `token_name`, and a
228
+ safe-to-display `token_preview`). use it to confirm which key is in play before a destructive operation.
229
+
230
+ what a call costs, and how credits are counted, is documented under
231
+ [credits & pricing](https://crawlbrulee.com/docs/credits-and-pricing).
168
232
 
169
233
  ---
170
234
 
171
- ## Webhooks
235
+ ## webhooks
236
+
237
+ when an async scrape job finishes, crawlbrulee can `POST` a `scrape.complete` webhook to your configured endpoint. the
238
+ sdk ships two helpers for it.
172
239
 
173
- When an async scrape job finishes, crawlbrulee can `POST` a `scrape.complete` webhook to your configured endpoint. The SDK ships two helpers for it.
240
+ the delivery contract and payload shape live under [webhooks](https://crawlbrulee.com/docs/scrape/webhooks); the
241
+ signature scheme is specified in [webhook verification](https://crawlbrulee.com/docs/webhook-verification). what follows
242
+ is how this sdk helps you consume them.
174
243
 
175
- ### Triggering a webhook (`scrapeAsync`)
244
+ ### triggering a webhook (`scrapeAsync`)
176
245
 
177
- Pass a `webhook` to `scrapeAsync` to have the API deliver a single signed `scrape.complete` `POST` when the job reaches a terminal state. This is **async-only** โ€” the synchronous `scrape()` response _is_ the notification, so it does not accept a `webhook`.
246
+ pass a `webhook` to `scrapeAsync` to have us deliver a single signed `scrape.complete` `POST` when the job reaches a
247
+ terminal state. this is **async-only** โ€” the synchronous `scrape()` response _is_ the notification, so it does not accept
248
+ a `webhook`.
178
249
 
179
250
  ```ts
180
251
  const { job_id } = await crawlbrulee.scrapeAsync({
@@ -189,11 +260,16 @@ const { job_id } = await crawlbrulee.scrapeAsync({
189
260
  })
190
261
  ```
191
262
 
192
- Configure the signing secret used for these deliveries in the dashboard (**Account โ†’ Webhooks**); there is no per-request secret. When the delivery arrives, verify it with [`verifyWebhookSignature`](#verifywebhooksignatureoptions) and read your `metadata` back from `webhook.data.metadata`. The delivery also carries usage accounting on `webhook.data.response_meta.usage` (`credits`, `proxy`, `cache_hit`). See [`AsyncScrapeWebhook`](src/types/scrape.ts) for the full field documentation.
263
+ configure the signing secret used for these deliveries in the dashboard (**account โ†’ webhooks**). there is no per-request
264
+ secret - when the delivery arrives, verify it with [`verifyWebhookSignature`](#verifywebhooksignatureoptions) and read your `metadata` back from
265
+ `webhook.data.metadata`. the delivery also carries usage accounting on `webhook.data.response_meta.usage` (`credits`,
266
+ `proxy`, `cache_hit`). see [`AsyncScrapeWebhook`](src/types/scrape.ts) for the full field documentation.
193
267
 
194
268
  ### `verifyWebhookSignature(options)`
195
269
 
196
- A standalone, network-free helper (built on Web Crypto, so it runs on Node.js 22+, browsers, Bun, Deno, and edge) that verifies the `X-Cwbl-Signature` header. **It returns a result object rather than throwing** โ€” a failed verification is normal control flow.
270
+ a standalone, network-free helper (built on Web Crypto, so it runs on Node.js 22+, browsers, Bun, Deno, and edge) that
271
+ verifies the `X-Cwbl-Signature` header. **it returns a result object rather than throwing** โ€” a failed verification is
272
+ normal control flow.
197
273
 
198
274
  ```ts
199
275
  import { verifyWebhookSignature } from '@crawlbrulee/sdk'
@@ -212,11 +288,15 @@ if (result.verified) {
212
288
  }
213
289
  ```
214
290
 
215
- During a **signing-secret rotation grace window** the API sends a second `X-Cwbl-Signature-Rotated` header signed with the previous secret. `verifyWebhookSignature` tries your `secret` against the primary header first, then the rotated one, and reports which matched via `signedWith` โ€” so verification keeps working whether you still hold the old secret or have already rotated to the new one.
291
+ during a **signing-secret rotation grace window** we send a second `X-Cwbl-Signature-Rotated` header signed with the
292
+ previous secret. `verifyWebhookSignature` tries your `secret` against the primary header first, then the rotated one,
293
+ and reports which matched via `signedWith` โ€” so verification keeps working whether you still hold the old secret or have
294
+ already rotated to the new one.
216
295
 
217
296
  ### `crawlbrulee.fetchScrapeResultFromWebhook(webhook, options?)`
218
297
 
219
- Given a verified `scrape.complete` body, fetch the scrape result. Returns `getScrapeResult(job_id)` for a `success` job; throws a `CrawlbruleeError` for `failed` (carrying the failure message) or `cancelled` jobs.
298
+ given a verified `scrape.complete` body, fetch the scrape result. returns `getScrapeResult(job_id)` for a `success` job;
299
+ throws a `CrawlbruleeError` for `failed` (carrying the failure message) or `cancelled` jobs.
220
300
 
221
301
  ```ts
222
302
  import { Crawlbrulee, verifyWebhookSignature, type ScrapeCompleteWebhook } from '@crawlbrulee/sdk'
@@ -240,28 +320,30 @@ app.post('/webhooks/crawlbrulee', async (req, res) => {
240
320
  })
241
321
  ```
242
322
 
243
- Always verify the signature **before** parsing or trusting the body. The `X-Cwbl-Event-Id` header (also `webhook.event_id`) is a stable id you can use to de-duplicate deliveries.
323
+ always verify the signature **before** parsing or trusting the body. the `X-Cwbl-Event-Id` header (also
324
+ `webhook.event_id`) is a stable id you can use to de-duplicate deliveries.
244
325
 
245
326
  ---
246
327
 
247
- ## Errors
328
+ ## errors
248
329
 
249
- Every failure raised by the SDK extends [`CrawlbruleeError`](src/errors.ts). Typed subclasses are exported for the most actionable cases:
330
+ every failure raised by the sdk extends [`CrawlbruleeError`](src/errors.ts). typed subclasses are exported for the most actionable
331
+ cases:
250
332
 
251
- | Class | When it's raised |
333
+ | class | when it's raised |
252
334
  | ---------------------- | ---------------------------------------------------------------------------------------------------- |
253
- | `AuthenticationError` | 401 / 403 responses (missing, invalid, or unauthorized API key). |
254
- | `RateLimitError` | 429 responses. Exposes `retryAfterMs` and `limitedBy` when the server provided them. |
255
- | `UsageAllocationError` | The org's plan limit was hit. Exposes `reason` (`credit_limit`, `concurrency_limit`, โ€ฆ) and `usage`. |
335
+ | `AuthenticationError` | 401 / 403 responses (missing, invalid, or unauthorized api key). |
336
+ | `RateLimitError` | 429 responses. exposes `retryAfterMs` and `limitedBy` when the server provided them. |
337
+ | `UsageAllocationError` | the org's plan limit was hit. exposes `reason` (`credit_limit`, `concurrency_limit`, โ€ฆ) and `usage`. |
256
338
  | `ValidationError` | 4xx caused by a bad request (`invalid_url`, `url_too_long`, `blocked_url`, โ€ฆ). |
257
339
  | `NotFoundError` | 404 responses (e.g. unknown async `jobId`). |
258
- | `TransportError` | Network failures, aborts, non-JSON responses, request body read failures. |
259
- | `CrawlbruleeError` | Base class โ€” used for any other API error. Always has `status`, `errorName`, `message`. |
340
+ | `TransportError` | network failures, aborts, non-json responses, request body read failures. |
341
+ | `CrawlbruleeError` | base class โ€” used for any other api error. always has `status`, `errorName`, `message`. |
260
342
 
261
343
  ```ts
262
344
  import { Crawlbrulee, RateLimitError, UsageAllocationError } from '@crawlbrulee/sdk'
263
345
 
264
- const crawlbrulee = new Crawlbrulee({ apiKey: 'cble_โ€ฆ' })
346
+ const crawlbrulee = new Crawlbrulee({ apiKey: 'cwbl_โ€ฆ' })
265
347
  try {
266
348
  await crawlbrulee.scrape({ url: 'https://example.com' })
267
349
  } catch (err) {
@@ -276,13 +358,13 @@ try {
276
358
  }
277
359
  ```
278
360
 
279
- For exhaustive branching, switch on `err.errorName` โ€” the literal-typed union is exported as `ApiErrorName`.
361
+ for exhaustive branching, switch on `err.errorName` โ€” the literal-typed union is exported as `ApiErrorName`. the
362
+ `isCrawlbruleeError(err)` type guard narrows an `unknown` to the base error. the api docs carry the canonical
363
+ [error reference](https://crawlbrulee.com/docs/errors) โ€” every `errorName`, what causes it, and how to recover.
280
364
 
281
- ---
282
-
283
- ## Cancellation and timeouts
365
+ ## cancellation and timeouts
284
366
 
285
- Every method accepts an `AbortSignal`:
367
+ every method accepts an `AbortSignal`:
286
368
 
287
369
  ```ts
288
370
  const controller = new AbortController()
@@ -291,11 +373,12 @@ const page = crawlbrulee.scrape({ url: 'https://slow.example.com' }, { signal: c
291
373
  setTimeout(() => controller.abort(), 5_000)
292
374
  ```
293
375
 
294
- The per-call `timeoutMs` and the caller's signal are composed โ€” whichever fires first wins.
376
+ the per-call `timeoutMs` and the caller's signal are composed โ€” whichever fires first wins. a fired timeout surfaces as
377
+ a `TransportError` with `errorName: 'request_timeout'`; an aborted signal as `errorName: 'client_closed_request'`.
295
378
 
296
379
  ---
297
380
 
298
- ## Development
381
+ ## development
299
382
 
300
383
  ```bash
301
384
  pnpm install
@@ -305,4 +388,20 @@ pnpm lint # eslint
305
388
  pnpm build # tsup โ†’ dist/
306
389
  ```
307
390
 
308
- The SDK has zero runtime dependencies on purpose. Please keep it that way when contributing.
391
+ the sdk has zero runtime dependencies on purpose. please keep it that way when contributing.
392
+
393
+ ## part of the crawlbrulee toolkit
394
+
395
+ one api, many ways to call it:
396
+
397
+ - **[js/ts sdk](https://github.com/crawlbrulee/crawlbrulee-js)** โ€” `@crawlbrulee/sdk` (this one)
398
+ - **[python sdk](https://github.com/crawlbrulee/crawlbrulee-py)** โ€” `crawlbrulee` on pypi
399
+ - **[cli](https://github.com/crawlbrulee/crawlbrulee-cli)** โ€” `npx crawlbrulee`
400
+ - **[mcp server](https://github.com/crawlbrulee/crawlbrulee-mcp)** โ€” `@crawlbrulee/mcp`, for ai agents
401
+ - **[agent skills](https://github.com/crawlbrulee/crawlbrulee-skills)** โ€” for skills-aware coding agents
402
+
403
+ docs: [crawlbrulee.com/docs](https://crawlbrulee.com/docs) ยท dashboard: [dashboard.crawlbrulee.com](https://dashboard.crawlbrulee.com)
404
+
405
+ ## license
406
+
407
+ [AGPL-3.0-only](./LICENSE)
package/dist/index.cjs CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  // src/config.ts
4
4
  var DEFAULT_BASE_URL = "https://api.crawlbrulee.com";
5
- var DEFAULT_REQUEST_TIMEOUT_MS = 6e4;
5
+ var DEFAULT_REQUEST_TIMEOUT_MS = 0;
6
6
  var ENV_API_KEY = "CRAWLBRULEE_API_KEY";
7
- var USER_AGENT = "@crawlbrulee/sdk/0.4.0 (node)";
7
+ var USER_AGENT = "@crawlbrulee/sdk/0.7.0 (node)";
8
8
 
9
9
  // src/errors.ts
10
10
  var CrawlbruleeError = class extends Error {
@@ -134,7 +134,7 @@ var CwblInstrumentation = {
134
134
  const g = globalThis;
135
135
  if (typeof g.fetch !== "function") {
136
136
  throw new CrawlbruleeError(
137
- "No global fetch is available in this runtime. crawlbrulee requires Node.js 20+, Bun, Deno, or a modern browser/edge runtime.",
137
+ "No global fetch is available in this runtime. crawlbrulee requires Node.js 22+, Bun, Deno, or a modern browser/edge runtime.",
138
138
  { status: 0, errorName: null }
139
139
  );
140
140
  }
@@ -159,7 +159,7 @@ var HttpClient = class {
159
159
  this.baseUrl = stripTrailingSlash(options.baseUrl ?? CwblInstrumentation.getBaseUrl());
160
160
  this.apiKey = options.apiKey;
161
161
  this.fetch = CwblInstrumentation.getFetch();
162
- this.timeoutMs = options.timeoutMs ?? 0;
162
+ this.timeoutMs = options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
163
163
  }
164
164
  /** Send a `GET` request and parse the response as `T`. */
165
165
  get(path, options) {
@@ -679,7 +679,7 @@ function getSubtle() {
679
679
  const subtle = globalThis.crypto?.subtle;
680
680
  if (!subtle) {
681
681
  throw new Error(
682
- "Web Crypto (globalThis.crypto.subtle) is not available in this runtime. crawlbrulee webhook verification requires Node.js 20+, Bun, Deno, or a modern browser/edge runtime."
682
+ "Web Crypto (globalThis.crypto.subtle) is not available in this runtime. crawlbrulee webhook verification requires Node.js 22+, Bun, Deno, or a modern browser/edge runtime."
683
683
  );
684
684
  }
685
685
  return subtle;