From 631378d1864760e43445d073320124edb269981b Mon Sep 17 00:00:00 2001 From: Browser Cash Date: Thu, 17 Sep 2026 17:29:43 -0400 Subject: [PATCH 01/11] Docs v2: rewritten documentation (preview branch; the v2 branch is the source) Co-Authored-By: Claude Fable 5.1 --- .gitignore | 2 + LICENSE | 21 - README.md | 38 +- api-reference/openapi.json | 1561 +- api-reference/overview.mdx | 42 + docs.json | 218 +- docs/account/api-keys.mdx | 25 + docs/account/billing.mdx | 44 + docs/account/dashboard.mdx | 21 + docs/features/browser-profiles.mdx | 110 - docs/features/geolocation.mdx | 53 - docs/features/node-selection.mdx | 63 - docs/features/proxies.mdx | 52 - docs/features/window-size.mdx | 80 - docs/frameworks/browser-use.mdx | 46 + docs/frameworks/crawl4ai.mdx | 39 + docs/frameworks/patchright.mdx | 133 + docs/frameworks/playwright.mdx | 56 + docs/frameworks/puppeteer.mdx | 51 + docs/frameworks/stagehand.mdx | 60 + docs/fundamentals/best-practices.mdx | 81 - .../creating-a-browser-session.mdx | 141 - .../managing-a-browser-session.mdx | 125 - docs/fundamentals/using-a-browser-session.mdx | 88 - .../viewing-a-browser-session.mdx | 104 - docs/index.mdx | 103 - docs/integrations/browser-use.mdx | 152 - docs/integrations/crawl4ai.mdx | 136 - docs/integrations/playwright.mdx | 277 - docs/integrations/puppeteer.mdx | 118 - docs/integrations/stagehand.mdx | 147 - docs/introduction/getting-started.mdx | 62 - docs/options/browser-types.mdx | 37 + docs/options/captcha-and-adblock.mdx | 40 + docs/options/extensions.mdx | 64 + docs/options/location.mdx | 45 + docs/options/node-reuse.mdx | 31 + docs/options/profiles.mdx | 66 + docs/options/proxies.mdx | 42 + docs/options/window-size.mdx | 39 + docs/sessions/connect.mdx | 71 + docs/sessions/create.mdx | 115 + docs/sessions/errors.mdx | 41 + docs/sessions/lifecycle.mdx | 148 + docs/sessions/live-view.mdx | 42 + docs/start/best-practices.mdx | 110 + docs/start/for-agents.mdx | 192 + docs/start/index.mdx | 62 + docs/start/quickstart.mdx | 131 + images/checks-passed.png | Bin 160724 -> 0 bytes images/hero-dark.png | Bin 110614 -> 0 bytes images/hero-light.png | Bin 104264 -> 0 bytes package-lock.json | 14575 ++++++++++++++++ package.json | 13 + scripts/sync-openapi.mjs | 106 + 55 files changed, 17984 insertions(+), 2235 deletions(-) create mode 100644 .gitignore delete mode 100644 LICENSE create mode 100644 api-reference/overview.mdx create mode 100644 docs/account/api-keys.mdx create mode 100644 docs/account/billing.mdx create mode 100644 docs/account/dashboard.mdx delete mode 100644 docs/features/browser-profiles.mdx delete mode 100644 docs/features/geolocation.mdx delete mode 100644 docs/features/node-selection.mdx delete mode 100644 docs/features/proxies.mdx delete mode 100644 docs/features/window-size.mdx create mode 100644 docs/frameworks/browser-use.mdx create mode 100644 docs/frameworks/crawl4ai.mdx create mode 100644 docs/frameworks/patchright.mdx create mode 100644 docs/frameworks/playwright.mdx create mode 100644 docs/frameworks/puppeteer.mdx create mode 100644 docs/frameworks/stagehand.mdx delete mode 100644 docs/fundamentals/best-practices.mdx delete mode 100644 docs/fundamentals/creating-a-browser-session.mdx delete mode 100644 docs/fundamentals/managing-a-browser-session.mdx delete mode 100644 docs/fundamentals/using-a-browser-session.mdx delete mode 100644 docs/fundamentals/viewing-a-browser-session.mdx delete mode 100644 docs/index.mdx delete mode 100644 docs/integrations/browser-use.mdx delete mode 100644 docs/integrations/crawl4ai.mdx delete mode 100644 docs/integrations/playwright.mdx delete mode 100644 docs/integrations/puppeteer.mdx delete mode 100644 docs/integrations/stagehand.mdx delete mode 100644 docs/introduction/getting-started.mdx create mode 100644 docs/options/browser-types.mdx create mode 100644 docs/options/captcha-and-adblock.mdx create mode 100644 docs/options/extensions.mdx create mode 100644 docs/options/location.mdx create mode 100644 docs/options/node-reuse.mdx create mode 100644 docs/options/profiles.mdx create mode 100644 docs/options/proxies.mdx create mode 100644 docs/options/window-size.mdx create mode 100644 docs/sessions/connect.mdx create mode 100644 docs/sessions/create.mdx create mode 100644 docs/sessions/errors.mdx create mode 100644 docs/sessions/lifecycle.mdx create mode 100644 docs/sessions/live-view.mdx create mode 100644 docs/start/best-practices.mdx create mode 100644 docs/start/for-agents.mdx create mode 100644 docs/start/index.mdx create mode 100644 docs/start/quickstart.mdx delete mode 100644 images/checks-passed.png delete mode 100644 images/hero-dark.png delete mode 100644 images/hero-light.png create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 scripts/sync-openapi.mjs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fd4f2b0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +.DS_Store diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 5411374..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2023 Mintlify - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index f245382..a0a9aa0 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,32 @@ -# Driver Docs +# Driver docs -Documentation for Driver's managed browser API. +Documentation for [docs.driver.dev](https://docs.driver.dev), written from scratch on 2026-09-17. Mintlify project: +`docs.json` is the navigation and theme, `docs/**` the guides, `api-reference/` the generated reference. -## Structure +## Principles -- `docs/index.mdx` — API overview and core session flow -- `docs/introduction/` — introductory getting-started pages -- `docs/fundamentals/` — browser session lifecycle, usage, viewer, and management -- `docs/features/` — profiles, geolocation, node targeting, proxies, and window size -- `docs/integrations/` — Playwright, Puppeteer, Browser-Use, Crawl4AI, and Stagehand -- `docs.json` — Mintlify navigation and site configuration -- `api-reference/openapi.json` — Mintlify OpenAPI reference generated from the live Driver API spec with docs-specific simplifications +- **The live API document is the ground truth.** `api-reference/openapi.json` is produced by `npm run sync` from + `https://api.driver.dev/doc` (what Scalar renders at `/scalar`). Guides describe options that exist there; nothing + else. When the API changes, run the sync and fix the guides that mention the changed field. +- **Written for engineers and agents alike.** Short pages, one job each, a table of options with defaults, one + example per language, and a "For agents" page plus `llms.txt` (generated by Mintlify) that state the rules outright. +- **Patchright is the recommended client.** Every framework page says why, and the stock Playwright and Puppeteer + pages carry the warning. +- **Best practices stay.** `docs/start/best-practices.mdx` carries the CDP-hygiene guidance forward. -The API Reference tab is generated from `api-reference/openapi.json`. The local OpenAPI file is based on `https://api.driver.dev/doc` with docs-specific corrections. +## Deprecated surface -## Preview Locally +The teracrawl scrape/search endpoints and the `consumer_distributed` browser type are deprecated and deliberately +absent: `scripts/sync-openapi.mjs` drops them from the generated reference, and no guide mentions them. + +## Working on it ```bash -npm i -g mint -mint dev +npm install # Mintlify CLI +npm run sync # refresh api-reference/openapi.json from the live API +npm run dev # http://localhost:3000 +npm run check # broken links ``` -Open `http://localhost:3000`. +Old URLs redirect to their new homes (see `redirects` in `docs.json`), so links from the dashboard and the landing +page keep working. diff --git a/api-reference/openapi.json b/api-reference/openapi.json index f9d313b..22da78a 100644 --- a/api-reference/openapi.json +++ b/api-reference/openapi.json @@ -3,7 +3,7 @@ "info": { "version": "1.0.0", "title": "Driver API", - "description": "API for creating Driver browser sessions and scraping pages.", + "description": "API for interacting with the Driver.dev browser network.", "contact": { "name": "Contact Us", "email": "hello@driver.dev", @@ -11,24 +11,249 @@ } }, "externalDocs": { - "description": "Learn more", - "url": "https://docs.driver.dev" + "url": "https://dash.browser.cash/docs", + "description": "Learn more" }, "components": { "securitySchemes": { "Bearer": { "type": "http", "scheme": "bearer", - "description": "Your Driver API key. Get one at https://app.driver.dev" + "description": "Your Driver.dev API token (browser or agent, they work interchangeably). Get one at https://app.driver.dev" + } + }, + "schemas": { + "BillingPlan": { + "type": "object", + "nullable": true, + "properties": { + "plan_key": { + "type": "string", + "description": "Stable billing plan identifier.", + "example": "advanced" + }, + "display_name": { + "type": "string", + "description": "Human-readable plan name.", + "example": "Advanced" + }, + "price_monthly_cents": { + "type": "integer", + "minimum": 0, + "description": "Monthly plan price in cents.", + "example": 4900 + }, + "included_network_mb": { + "type": "number", + "minimum": 0, + "description": "Network traffic included with the plan, in megabytes.", + "example": 100000 + }, + "overage_millicents_per_mb": { + "type": "number", + "minimum": 0, + "description": "Network overage rate in millicents per megabyte; 1,000 millicents equals one cent.", + "example": 250 + }, + "concurrent_browsers": { + "type": "integer", + "minimum": 0, + "description": "Concurrent browser allowance provided by the plan.", + "example": 20 + }, + "max_session_seconds": { + "type": "integer", + "minimum": 0, + "description": "Maximum duration of one browser session, in seconds.", + "example": 3600 + } + }, + "required": [ + "plan_key", + "display_name", + "price_monthly_cents", + "included_network_mb", + "overage_millicents_per_mb", + "concurrent_browsers", + "max_session_seconds" + ], + "description": "Current plan, or null for an unmigrated legacy account." + }, + "BillingPeriod": { + "type": "object", + "nullable": true, + "properties": { + "start_ms": { + "type": "integer", + "minimum": 0, + "description": "Current billing period start as Unix epoch milliseconds." + }, + "end_ms": { + "type": "integer", + "minimum": 0, + "description": "Current billing period end as Unix epoch milliseconds." + }, + "included_network_mb": { + "type": "number", + "minimum": 0, + "description": "Network traffic included in this billing period, in megabytes." + }, + "network_mb_used": { + "type": "number", + "minimum": 0, + "description": "Metered network traffic used during this billing period, in megabytes." + }, + "network_mb_remaining": { + "type": "number", + "description": "Included network traffic remaining in this billing period, in megabytes." + } + }, + "required": [ + "start_ms", + "end_ms", + "included_network_mb", + "network_mb_used", + "network_mb_remaining" + ], + "description": "Current usage period, or null for an unmigrated legacy account." + }, + "BillingWallet": { + "type": "object", + "properties": { + "balance_cents": { + "type": "number", + "description": "Available wallet balance in cents. This can be negative when usage exceeds prepaid credit." + }, + "balance_millicents": { + "type": "number", + "description": "Available wallet balance in millicents for exact billing calculations; 1,000 millicents equals one cent." + } + }, + "required": [ + "balance_cents", + "balance_millicents" + ], + "description": "Current prepaid wallet balance." + }, + "BillingSubscription": { + "type": "object", + "nullable": true, + "properties": { + "subscription_id": { + "type": "string", + "description": "Billing provider subscription identifier." + }, + "provider": { + "type": "string", + "nullable": true, + "enum": [ + "polar", + "nowpayments" + ], + "description": "Subscription billing provider when applicable." + }, + "status": { + "type": "string", + "description": "Current subscription lifecycle status.", + "example": "active" + }, + "current_period_start": { + "type": "integer", + "minimum": 0, + "description": "Subscription period start as Unix epoch milliseconds." + }, + "current_period_end": { + "type": "integer", + "minimum": 0, + "description": "Subscription period end as Unix epoch milliseconds." + }, + "cancel_at_period_end": { + "type": "boolean", + "description": "Whether the subscription is scheduled to cancel at the end of its current period." + } + }, + "required": [ + "subscription_id", + "status", + "current_period_start", + "current_period_end", + "cancel_at_period_end" + ], + "description": "Current subscription, or null when the account has no subscription." + }, + "BillingDetails": { + "type": "object", + "properties": { + "orgId": { + "type": "string", + "nullable": true, + "description": "WorkOS organization ID. Null for an unmigrated legacy account." + }, + "status": { + "type": "string", + "description": "Billing account status. Legacy accounts return `legacy`." + }, + "plan": { + "$ref": "#/components/schemas/BillingPlan" + }, + "period": { + "$ref": "#/components/schemas/BillingPeriod" + }, + "wallet": { + "$ref": "#/components/schemas/BillingWallet" + }, + "subscription": { + "$ref": "#/components/schemas/BillingSubscription" + } + }, + "required": [ + "orgId", + "status", + "plan", + "period", + "wallet", + "subscription" + ], + "example": { + "orgId": "org_01KNRFX3833MCT6CX3R9AWR4Z8", + "status": "active", + "plan": { + "plan_key": "advanced", + "display_name": "Advanced", + "price_monthly_cents": 4900, + "included_network_mb": 100000, + "overage_millicents_per_mb": 250, + "concurrent_browsers": 20, + "max_session_seconds": 3600 + }, + "period": { + "start_ms": 1782864000000, + "end_ms": 1785542400000, + "included_network_mb": 100000, + "network_mb_used": 12500, + "network_mb_remaining": 87500 + }, + "wallet": { + "balance_cents": 1250, + "balance_millicents": 1250000 + }, + "subscription": { + "subscription_id": "sub_example", + "provider": "polar", + "status": "active", + "current_period_start": 1782864000000, + "current_period_end": 1785542400000, + "cancel_at_period_end": false + } + } } }, - "schemas": {}, "parameters": {} }, "paths": { "/v1/browser/session": { "post": { - "description": "Create a new hosted browser session.", + "description": "Create a browser session and wait for CDP readiness. hosted, hosted_stealth, and hosted_privacy are hosted browsers; hosted_stealth applies the conservative stealth policy and hosted_privacy the aggressive one. Hosted Canadian sessions without an explicit proxy use TCPerfect. Country defaults to US; omitted locale and proxy values are resolved coherently.", "security": [ { "Bearer": [] @@ -40,25 +265,63 @@ "schema": { "type": "object", "properties": { - "type": { + "nodeId": { "type": "string", - "enum": [ - "hosted" - ], - "description": "Use hosted Driver infrastructure for the session.", - "example": "hosted" + "description": "The ID of the node to create a session on. Optional. If not provided, a random node will be selected. If provided, it takes precedence over country and type.", + "example": "this-node-id" }, "country": { "type": "string", "minLength": 2, "maxLength": 2, - "description": "Start a session in a specific country using its 2-letter ISO code. Optional.", + "description": "Two-letter country code used for placement and locale coherence. Defaults to US unless a SOCKS5 URL is supplied with neither language nor timezone, in which case Driver derives the country from the proxy exit. A supplied country must match that resolved exit. Driver otherwise selects a plausible primary language and dominant regional timezone.", "example": "US" }, - "nodeId": { + "type": { + "type": "string", + "enum": [ + "hosted", + "hosted_stealth", + "hosted_privacy" + ], + "default": "hosted", + "description": "Browser execution class. hosted is the native browser, hosted_stealth applies the conservative stealth policy, and hosted_privacy the aggressive one. Defaults to hosted.", + "example": "hosted_privacy" + }, + "url": { + "type": "string", + "minLength": 1, + "description": "Initial URL opened by the browser after launch. Optional. Forwarded to node-side launch when supported.", + "example": "https://example.com" + }, + "proxyUrl": { + "type": "string", + "description": "Explicit SOCKS5, SOCKS5H, or TCPerfect network URL for the session. A SOCKS URL supplied without language or timezone is geolocated before launch; Driver derives or validates country and uses the exit timezone. When omitted, hosted Canadian sessions use a uniformly selected public address from the production TCPerfect inventory.", + "example": "tcperfect://user:password@206.223.186.11:1194/65.97.36.3" + }, + "windowSize": { + "type": "string", + "description": "Cloaked outer browser-window size in WIDTHxHEIGHT format. Width must be 320–16384 and height 200–16384.", + "example": "1920x1040" + }, + "displaySize": { + "type": "string", + "description": "Virtual display size in WIDTHxHEIGHT format. Width must be 320–16384 and height 200–16384. Only supported by hosted types.", + "example": "1920x1080" + }, + "timezone": { + "type": "string", + "minLength": 1, + "maxLength": 128, + "description": "IANA timezone for the browser. It must be listed for the selected country. When omitted, Driver selects a dominant regional timezone and targets managed proxy geography where the provider supports regional targeting.", + "example": "America/New_York" + }, + "language": { "type": "string", - "description": "Create the hosted session on a specific previously used hosted node. Optional. Overrides country.", - "example": "node-id-from-servedBy" + "minLength": 2, + "maxLength": 64, + "description": "BCP-47 browser language listed for the selected country. US sessions accept only en-US and omit both language launch fields so the browser uses its built-in en-US default. For other countries, Driver deterministically chooses the likely primary language the browser supports. Hosted sessions provision matching TTS when available; otherwise Web Speech is disabled for coherence.", + "example": "en-US" }, "duration": { "type": "integer", @@ -68,16 +331,6 @@ "description": "The duration of the browser session in seconds. Optional. Default is 3600 seconds (1 hour). Cannot be greater than 3600 seconds (1 hour). Cannot be less than 60 seconds (1 minute).", "example": 600 }, - "windowSize": { - "type": "string", - "description": "The window size for the browser session in the format WIDTHxHEIGHT (e.g., 1920x1080). Optional.", - "example": "1920x1080" - }, - "proxyUrl": { - "type": "string", - "description": "SOCKS5 proxy URL for the session. Optional. Requires hosted sessions and cannot be used with nodeId.", - "example": "socks5://user:pass@proxy.example.com:1080" - }, "profile": { "type": "object", "properties": { @@ -89,22 +342,23 @@ "persist": { "type": "boolean", "example": true, - "description": "Whether to save browser state for reuse after the session stops." + "description": "Whether to save the browser profile from this session after its completed." } }, "required": [ "name" ], - "description": "The browser profile to use for the session. Optional." + "additionalProperties": false, + "description": "A named full browser profile. Persistent profiles are kept between sessions." }, - "fast": { + "adblock": { "type": "boolean", - "description": "Attempt faster startup when the request is eligible. Optional.", + "description": "Whether to enable ad-blocking in the browser session. Optional. Default is false.", "example": true }, "captchaSolver": { "type": "boolean", - "description": "Enable the built-in CAPTCHA solver for the session. Optional. Default is false.", + "description": "Whether to enable the built-in CAPTCHA solver for the session. Optional. Default is false.", "example": true }, "extensionIds": { @@ -112,16 +366,30 @@ "items": { "type": "string" }, - "description": "Chrome extension IDs to load into the browser session. Optional. Requires extension access for your account.", + "description": "Internal extension IDs returned by the extensions API. Supported by all hosted types. Requires account-level extension access.", "example": [ "abc123def456" ] + }, + "browserCheck": { + "type": "boolean", + "description": "Whether to run a browser validation check during session creation. Optional. Default is false.", + "example": false + }, + "note": { + "type": "string", + "maxLength": 256, + "description": "An optional note to attach to the session. Maximum 256 characters.", + "example": "Scraping product pages for client X" } + }, + "additionalProperties": false, + "example": { + "type": "hosted" } } } - }, - "required": false + } }, "responses": { "200": { @@ -178,6 +446,34 @@ "stoppedAt", "cdpUrl", "note" + ], + "example": { + "sessionId": "example-session-id", + "status": "active", + "servedBy": "this-node-id", + "createdAt": "2025-11-20T01:51:25.000Z", + "stoppedAt": null, + "cdpUrl": "wss://sessions.driver.dev/v1/consumer/example-session-id/devtools/browser/uuid", + "note": null + } + } + } + } + }, + "400": { + "description": "Invalid country, language, timezone, dimensions, proxy, type, or incompatible option combination.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Validation error" + } + }, + "required": [ + "error" ] } } @@ -202,6 +498,63 @@ } } }, + "402": { + "description": "Insufficient account balance.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Error message" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "Session or account limit reached.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Error message" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "429": { + "description": "Too many requests.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Error message" + } + }, + "required": [ + "error" + ] + } + } + } + }, "500": { "description": "Failed to create a browser session.", "content": { @@ -220,10 +573,54 @@ } } } + }, + "503": { + "description": "Browser capacity is temporarily unavailable.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Error message" + }, + "code": { + "type": "string", + "description": "Machine-readable error code" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "504": { + "description": "A machine was selected but the browser did not become CDP-ready before the startup deadline.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Startup timeout" + } + }, + "required": [ + "error" + ] + } + } + } } }, - "summary": "Create Browser Session", - "operationId": "createBrowserSession" + "tags": [ + "Sessions" + ], + "summary": "Create a session" }, "get": { "description": "Retrieve an existing browser session.", @@ -305,7 +702,17 @@ "stoppedAt", "cdpUrl", "note" - ] + ], + "example": { + "sessionId": "example-session-id", + "status": "active", + "servedBy": "this-node-id", + "createdAt": "2025-11-20T01:51:25.000Z", + "stoppedAt": null, + "bandwidthBytes": 1048576, + "cdpUrl": "wss://sessions.driver.dev/v1/consumer/example-session-id/devtools/browser/uuid", + "note": null + } } } } @@ -367,10 +774,32 @@ } } } + }, + "503": { + "description": "The session service is unavailable.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Error message", + "example": "Session service unavailable" + } + }, + "required": [ + "error" + ] + } + } + } } }, - "summary": "Get Browser Session", - "operationId": "getBrowserSession" + "tags": [ + "Sessions" + ], + "summary": "Get a session" }, "delete": { "description": "Stop an existing browser session.", @@ -407,7 +836,10 @@ }, "required": [ "success" - ] + ], + "example": { + "success": true + } } } } @@ -469,10 +901,32 @@ } } } + }, + "503": { + "description": "The session service is unavailable.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Error message", + "example": "Session service unavailable" + } + }, + "required": [ + "error" + ] + } + } + } } }, - "summary": "Stop Browser Session", - "operationId": "stopBrowserSession" + "tags": [ + "Sessions" + ], + "summary": "Stop a session" }, "patch": { "description": "Update an existing browser session. Currently supports updating the session note.", @@ -512,8 +966,7 @@ ] } } - }, - "required": false + } }, "responses": { "200": { @@ -530,7 +983,10 @@ }, "required": [ "success" - ] + ], + "example": { + "success": true + } } } } @@ -592,10 +1048,32 @@ } } } + }, + "503": { + "description": "The session service is unavailable.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Error message", + "example": "Session service unavailable" + } + }, + "required": [ + "error" + ] + } + } + } } }, - "summary": "Update Browser Session", - "operationId": "updateBrowserSession" + "tags": [ + "Sessions" + ], + "summary": "Update a session note" } }, "/v1/browser/sessions": { @@ -701,11 +1179,7 @@ "type": { "type": "string", "nullable": true, - "enum": [ - "hosted" - ], - "description": "Session infrastructure type. Public docs only expose hosted sessions.", - "example": "hosted" + "description": "The session execution type, such as 'hosted', 'hosted_stealth', 'hosted_privacy'." }, "country": { "type": "string", @@ -752,7 +1226,26 @@ "total", "totalPages", "sessions" - ] + ], + "example": { + "page": 1, + "pageSize": 20, + "total": 7, + "totalPages": 1, + "sessions": [ + { + "sessionId": "jx6qz3w68l6j1v3h1c42teogpake4t7zjtccbksrcwbilsqka8ugaw09qm0ldrx6u84igpobcrgctnjqcy1ejiphnjbwmkxh3p3dvsbmzbz70a9949aij3utp7mfjd6e", + "status": "completed", + "servedBy": "job-camera-sausage", + "type": "hosted", + "country": "US", + "createdAt": "2025-11-20T06:16:10.000Z", + "stoppedAt": "2025-11-20T07:16:11.199Z", + "bandwidthBytes": 2097152, + "note": null + } + ] + } } } } @@ -794,10 +1287,32 @@ } } } + }, + "503": { + "description": "The session service is unavailable.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Error message", + "example": "Session service unavailable" + } + }, + "required": [ + "error" + ] + } + } + } } }, - "summary": "List Browser Sessions", - "operationId": "listBrowserSessions" + "tags": [ + "Sessions" + ], + "summary": "List sessions" } }, "/v1/browser/profile": { @@ -880,8 +1395,10 @@ } } }, - "summary": "Delete Browser Profile", - "operationId": "deleteBrowserProfile" + "tags": [ + "Profiles" + ], + "summary": "Delete a profile" } }, "/v1/browser/profiles": { @@ -929,7 +1446,16 @@ }, "required": [ "profiles" - ] + ], + "example": { + "profiles": [ + { + "profileId": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", + "name": "test", + "createdAt": "2025-12-19T18:58:03.000Z" + } + ] + } } } } @@ -973,8 +1499,10 @@ } } }, - "summary": "List Browser Profiles", - "operationId": "listBrowserProfiles" + "tags": [ + "Profiles" + ], + "summary": "List profiles" } }, "/v1/account/balance": { @@ -1018,7 +1546,12 @@ }, "required": [ "balance" - ] + ], + "example": { + "balance": { + "balance_cents": 1250 + } + } } } } @@ -1082,208 +1615,167 @@ } } }, - "summary": "Get Account Balance", - "operationId": "getAccountBalance" + "tags": [ + "Account" + ], + "summary": "Get balance" } }, - "/v1/scrape": { - "post": { - "summary": "Scrape URL", - "operationId": "scrapeUrl", - "description": "Render a URL in a browser and return the page content as HTML or Markdown.", - "servers": [ + "/v1/account/billing": { + "get": { + "summary": "Get account billing details", + "description": "Returns the authenticated account’s complete billing summary, including its plan limits, current usage period, available wallet balance, and subscription state. Organization accounts use Driver Billing data. Unmigrated legacy accounts return their wallet balance with `plan`, `period`, and `subscription` set to null.", + "security": [ { - "url": "https://qk.driver.dev" + "Bearer": [] } ], + "responses": { + "200": { + "description": "Complete billing details for the authenticated account.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BillingDetails" + } + } + } + }, + "401": { + "description": "Missing or invalid token.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Error message" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "Account not found.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Error message" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "500": { + "description": "Billing details could not be returned.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Error message" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "tags": [ + "Account" + ] + } + }, + "/v1/browser/extensions": { + "post": { + "description": "Upload a Chrome extension ZIP file. The extension will be packed into CRX format and made available for use in browser sessions. Requires account-level allowlist access — returns 403 if your account is not enabled.", "security": [ { "Bearer": [] } ], "requestBody": { - "required": true, "content": { - "application/json": { - "example": { - "url": "https://example.com", - "output": "html", - "smartdoor": { - "nodes": [ - { - "type": "hosted", - "country": "US" - } - ], - "timeout": 60000, - "delay": 2500, - "retryFresh": true, - "retryCount": 2, - "refreshWhile": { - "expression": "document.body?.innerText?.includes('ERR_CONNECTION_RESET') || location.href.includes('/risk/challenge')", - "maxRefreshes": 3, - "minDelayMs": 700, - "maxDelayMs": 1300 - } - } - }, + "multipart/form-data": { "schema": { "type": "object", "properties": { - "url": { + "extension": { "type": "string", - "format": "uri", - "description": "URL to scrape.", - "example": "https://example.com" - }, - "output": { - "type": "string", - "enum": [ - "html", - "md" - ], - "description": "Response format. Defaults to html.", - "example": "html" - }, - "smartdoor": { - "type": "object", - "description": "Browser rendering, waiting, retry, and node options for the scrape.", - "properties": { - "nodes": { - "type": "array", - "description": "Preferred hosted node configurations. Multiple entries mean any match is acceptable.", - "items": { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ - "hosted" - ], - "description": "Use hosted Driver infrastructure.", - "example": "hosted" - }, - "country": { - "type": "string", - "minLength": 2, - "maxLength": 2, - "description": "Preferred 2-letter country code.", - "example": "US" - } - } - }, - "example": [ - { - "type": "hosted", - "country": "US" - } - ] - }, - "timeout": { - "type": "integer", - "minimum": 1000, - "maximum": 60000, - "description": "Overall scrape timeout in milliseconds.", - "example": 60000 - }, - "delay": { - "type": "integer", - "minimum": 0, - "maximum": 30000, - "description": "Additional delay in milliseconds after the page settles before capture.", - "example": 2500 - }, - "retryFresh": { - "type": "boolean", - "description": "Retry with a fresh browser context when a scrape attempt should be retried.", - "example": true - }, - "retryCount": { - "type": "integer", - "minimum": 0, - "description": "Maximum number of retry attempts.", - "example": 2 - }, - "refreshWhile": { - "type": "object", - "description": "Refresh the page while an in-page JavaScript expression evaluates truthy.", - "properties": { - "expression": { - "type": "string", - "description": "Synchronous JavaScript expression evaluated in the page. Refreshing continues while it returns truthy.", - "example": "location.href.includes('/risk/challenge')" - }, - "maxRefreshes": { - "type": "integer", - "minimum": 0, - "description": "Maximum number of page refreshes.", - "example": 3 - }, - "minDelayMs": { - "type": "integer", - "minimum": 0, - "description": "Minimum delay before each refresh in milliseconds.", - "example": 700 - }, - "maxDelayMs": { - "type": "integer", - "minimum": 0, - "description": "Maximum delay before each refresh in milliseconds.", - "example": 1300 - } - } - }, - "waitForSelector": { - "type": "string", - "description": "CSS selector to wait for before capture.", - "example": ".content-loaded" - }, - "waitForSelectorValue": { - "type": "string", - "description": "Text content the selector element must contain before capture.", - "example": "Ready" - }, - "waitForFunction": { - "type": "string", - "description": "Synchronous JavaScript expression that must evaluate truthy before capture.", - "example": "window.dataLoaded === true" - }, - "challengeBudget": { - "type": "integer", - "minimum": 2000, - "maximum": 30000, - "description": "How long to wait for an anti-bot challenge to resolve, in milliseconds.", - "example": 10000 - } - } + "format": "binary", + "description": "The ZIP file containing the Chrome extension." } - }, - "required": [ - "url" - ] + } } } } }, "responses": { - "200": { - "description": "Successfully scraped the URL. The response body is the scraped content.", + "201": { + "description": "Extension created successfully.", "content": { - "text/html": { - "schema": { - "type": "string" - }, - "example": "Example Domain..." - }, - "text/markdown": { - "schema": { - "type": "string" - }, - "example": "# Example Domain\n\nThis domain is for use in documentation examples." - }, - "text/plain": { + "application/json": { "schema": { - "type": "string" + "type": "object", + "properties": { + "extension": { + "type": "object", + "properties": { + "extensionId": { + "type": "string", + "description": "The internal extension ID (use this when referencing extensions in session creation)." + }, + "crxExtensionId": { + "type": "string", + "description": "The Chrome CRX extension ID." + }, + "name": { + "type": "string", + "description": "The extension name from manifest.json." + }, + "version": { + "type": "string", + "description": "The extension version from manifest.json." + }, + "createdAt": { + "type": "string", + "description": "The ISO 8601 timestamp when the extension was created." + }, + "updatedAt": { + "type": "string", + "description": "The ISO 8601 timestamp when the extension was last updated." + } + }, + "required": [ + "extensionId", + "crxExtensionId", + "name", + "version", + "createdAt" + ] + } + }, + "required": [ + "extension" + ] } } } @@ -1295,52 +1787,597 @@ "schema": { "type": "object", "properties": { - "success": { - "type": "boolean" - }, "error": { - "type": "string" + "type": "string", + "description": "Error message" } }, "required": [ - "success", "error" ] } } } }, - "500": { - "description": "Scrape failed.", + "403": { + "description": "Extensions API is not enabled for this account. Contact support to request access.", "content": { - "text/plain": { + "application/json": { "schema": { - "type": "string" - }, - "example": "Internal Server Error" - }, + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Error message" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "500": { + "description": "Server error.", + "content": { "application/json": { "schema": { "type": "object", "properties": { - "success": { - "type": "boolean" - }, "error": { - "type": "string" + "type": "string", + "description": "Error message" } - } + }, + "required": [ + "error" + ] } } } } - } - } - } - }, - "servers": [ + }, + "tags": [ + "Extensions" + ], + "summary": "Upload an extension" + }, + "get": { + "description": "List all uploaded extensions. Requires account-level allowlist access.", + "security": [ + { + "Bearer": [] + } + ], + "responses": { + "200": { + "description": "Successfully retrieved extensions.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "extensions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "extensionId": { + "type": "string", + "description": "The internal extension ID (use this when referencing extensions in session creation)." + }, + "crxExtensionId": { + "type": "string", + "description": "The Chrome CRX extension ID." + }, + "name": { + "type": "string", + "description": "The extension name from manifest.json." + }, + "version": { + "type": "string", + "description": "The extension version from manifest.json." + }, + "createdAt": { + "type": "string", + "description": "The ISO 8601 timestamp when the extension was created." + }, + "updatedAt": { + "type": "string", + "description": "The ISO 8601 timestamp when the extension was last updated." + } + }, + "required": [ + "extensionId", + "crxExtensionId", + "name", + "version", + "createdAt" + ] + } + } + }, + "required": [ + "extensions" + ] + } + } + } + }, + "401": { + "description": "Missing or invalid token.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Error message" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "Extensions API is not enabled for this account. Contact support to request access.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Error message" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "500": { + "description": "Server error.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Error message" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "tags": [ + "Extensions" + ], + "summary": "List extensions" + } + }, + "/v1/browser/extensions/{extensionId}": { + "get": { + "description": "Get details of a specific extension. Requires account-level allowlist access.", + "security": [ + { + "Bearer": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "description": "The internal extension ID.", + "example": "abc123def456" + }, + "required": true, + "description": "The internal extension ID.", + "name": "extensionId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Successfully retrieved extension.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "extension": { + "type": "object", + "properties": { + "extensionId": { + "type": "string", + "description": "The internal extension ID (use this when referencing extensions in session creation)." + }, + "crxExtensionId": { + "type": "string", + "description": "The Chrome CRX extension ID." + }, + "name": { + "type": "string", + "description": "The extension name from manifest.json." + }, + "version": { + "type": "string", + "description": "The extension version from manifest.json." + }, + "createdAt": { + "type": "string", + "description": "The ISO 8601 timestamp when the extension was created." + }, + "updatedAt": { + "type": "string", + "description": "The ISO 8601 timestamp when the extension was last updated." + } + }, + "required": [ + "extensionId", + "crxExtensionId", + "name", + "version", + "createdAt" + ] + } + }, + "required": [ + "extension" + ] + } + } + } + }, + "401": { + "description": "Missing or invalid token.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Error message" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "Extension not found.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Error message" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "500": { + "description": "Server error.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Error message" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "tags": [ + "Extensions" + ], + "summary": "Get an extension" + }, + "put": { + "description": "Update an extension by uploading a new ZIP file. The extension ID remains the same. Requires account-level allowlist access.", + "security": [ + { + "Bearer": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "description": "The internal extension ID.", + "example": "abc123def456" + }, + "required": true, + "description": "The internal extension ID.", + "name": "extensionId", + "in": "path" + } + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "extension": { + "type": "string", + "format": "binary", + "description": "The ZIP file containing the updated Chrome extension." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Extension updated successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "extension": { + "type": "object", + "properties": { + "extensionId": { + "type": "string", + "description": "The internal extension ID (use this when referencing extensions in session creation)." + }, + "crxExtensionId": { + "type": "string", + "description": "The Chrome CRX extension ID." + }, + "name": { + "type": "string", + "description": "The extension name from manifest.json." + }, + "version": { + "type": "string", + "description": "The extension version from manifest.json." + }, + "createdAt": { + "type": "string", + "description": "The ISO 8601 timestamp when the extension was created." + }, + "updatedAt": { + "type": "string", + "description": "The ISO 8601 timestamp when the extension was last updated." + } + }, + "required": [ + "extensionId", + "crxExtensionId", + "name", + "version", + "createdAt" + ] + } + }, + "required": [ + "extension" + ] + } + } + } + }, + "401": { + "description": "Missing or invalid token.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Error message" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "Extension not found.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Error message" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "500": { + "description": "Server error.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Error message" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "tags": [ + "Extensions" + ], + "summary": "Update an extension" + }, + "delete": { + "description": "Delete an uploaded extension. Requires account-level allowlist access.", + "security": [ + { + "Bearer": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "description": "The internal extension ID.", + "example": "abc123def456" + }, + "required": true, + "description": "The internal extension ID.", + "name": "extensionId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Extension deleted successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + } + }, + "required": [ + "success" + ] + } + } + } + }, + "401": { + "description": "Missing or invalid token.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Error message" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "Extension not found.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Error message" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "500": { + "description": "Server error.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Error message" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "tags": [ + "Extensions" + ], + "summary": "Delete an extension" + } + } + }, + "servers": [ + { + "url": "https://api.driver.dev", + "description": "Production" + } + ], + "tags": [ + { + "name": "Sessions" + }, + { + "name": "Profiles" + }, + { + "name": "Account" + }, { - "url": "https://api.driver.dev" + "name": "Extensions" } ] } diff --git a/api-reference/overview.mdx b/api-reference/overview.mdx new file mode 100644 index 0000000..ecfd357 --- /dev/null +++ b/api-reference/overview.mdx @@ -0,0 +1,42 @@ +--- +title: "API overview" +description: "Base URL, authentication, conventions, and the live OpenAPI document these pages are generated from." +--- + +## Base URL + +```text +https://api.driver.dev +``` + +## Authentication + +Every endpoint takes a workspace API key as a bearer token. Keys come from **Settings → API keys** in the [dashboard](https://app.driver.dev). + +```bash +Authorization: Bearer $DRIVER_API_KEY +``` + +## Conventions + +- Request and response bodies are JSON, except extension uploads, which are `multipart/form-data`. +- Identifiers that select a resource go in the query string (`?sessionId=…`, `?profileName=…`), not the path, except for extensions. +- Errors are `{ "error": "message" }` with a meaningful HTTP status; some carry a `code`. See [Errors](/docs/sessions/errors). +- Timestamps are ISO 8601 strings; money is in cents (and millicents where exactness matters); bandwidth is bytes on sessions and megabytes on billing. + +## Endpoints + +| Group | Endpoints | +|---|---| +| Sessions | `POST/GET/PATCH/DELETE /v1/browser/session`, `GET /v1/browser/sessions` | +| Profiles | `GET /v1/browser/profiles`, `DELETE /v1/browser/profile` | +| Account | `GET /v1/account/balance`, `GET /v1/account/billing` | +| Extensions | `POST/GET /v1/browser/extensions`, `GET/PUT/DELETE /v1/browser/extensions/{extensionId}` | + +## Source of truth + +These reference pages are generated from the API's own OpenAPI document, served live at `https://api.driver.dev/doc` and rendered at [api.driver.dev/scalar](https://api.driver.dev/scalar). If a page here and the live document ever disagree, the live document wins. + +## SDK + +`@browsercash/sdk` on npm wraps these endpoints for TypeScript; set `baseURL: "https://api.driver.dev"`. Python users call the endpoints directly with `requests`; the [For agents](/docs/start/for-agents) page has a small client module for both. diff --git a/docs.json b/docs.json index 0329980..6949d22 100644 --- a/docs.json +++ b/docs.json @@ -2,71 +2,118 @@ "$schema": "https://mintlify.com/docs.json", "theme": "maple", "name": "Driver", - "redirects": [ - { "source": "/", "destination": "/docs" } - ], + "description": "Real Chrome on real hardware, controlled over CDP. Hosted browser sessions, profiles, proxies and live view.", "colors": { - "primary": "#F0B15C", - "light": "#FFD38A", - "dark": "#C27C2C" + "primary": "#1F5AFF", + "light": "#3D72FF", + "dark": "#1F5AFF" + }, + "fonts": { + "family": "IBM Plex Sans", + "heading": { + "family": "IBM Plex Sans", + "weight": 600 + }, + "body": { + "family": "IBM Plex Sans", + "weight": 400 + } }, "favicon": "/favicon.svg", + "logo": { + "light": "/logo/light.svg", + "dark": "/logo/dark.svg", + "href": "https://driver.dev" + }, + "appearance": { + "default": "dark", + "strict": false + }, + "background": { + "color": { + "light": "#ffffff", + "dark": "#050505" + } + }, + "styling": { + "codeblocks": "dark" + }, "navigation": { "tabs": [ { - "tab": "Docs", + "tab": "Documentation", "groups": [ { "group": "Start", "pages": [ - "docs/index", - "docs/introduction/getting-started" + "docs/start/index", + "docs/start/quickstart", + "docs/start/for-agents", + "docs/start/best-practices" ] }, { - "group": "Browser Sessions", + "group": "Browser sessions", "pages": [ - "docs/fundamentals/creating-a-browser-session", - "docs/fundamentals/using-a-browser-session", - "docs/fundamentals/best-practices", - "docs/fundamentals/viewing-a-browser-session", - "docs/fundamentals/managing-a-browser-session" + "docs/sessions/create", + "docs/sessions/connect", + "docs/sessions/lifecycle", + "docs/sessions/live-view", + "docs/sessions/errors" ] }, { - "group": "Session Options", + "group": "Session options", "pages": [ - "docs/features/browser-profiles", - "docs/features/geolocation", - "docs/features/node-selection", - "docs/features/proxies", - "docs/features/window-size" + "docs/options/browser-types", + "docs/options/location", + "docs/options/window-size", + "docs/options/proxies", + "docs/options/profiles", + "docs/options/captcha-and-adblock", + "docs/options/extensions", + "docs/options/node-reuse" ] }, { - "group": "Integrations", + "group": "Frameworks", + "pages": [ + "docs/frameworks/patchright", + "docs/frameworks/playwright", + "docs/frameworks/puppeteer", + "docs/frameworks/browser-use", + "docs/frameworks/stagehand", + "docs/frameworks/crawl4ai" + ] + }, + { + "group": "Account", "pages": [ - "docs/integrations/playwright", - "docs/integrations/puppeteer", - "docs/integrations/browser-use", - "docs/integrations/crawl4ai", - "docs/integrations/stagehand" + "docs/account/api-keys", + "docs/account/billing", + "docs/account/dashboard" ] } ] }, { - "tab": "API Reference", + "tab": "API reference", "groups": [ { - "group": "Browser Sessions", + "group": "Overview", + "pages": [ + "api-reference/overview" + ] + }, + { + "group": "Sessions", "openapi": "api-reference/openapi.json", "pages": [ "POST /v1/browser/session", "GET /v1/browser/session", + "GET /v1/browser/sessions", "PATCH /v1/browser/session", - "DELETE /v1/browser/session", - "GET /v1/browser/sessions" + "DELETE /v1/browser/session" ] }, { @@ -81,32 +128,34 @@ "group": "Account", "openapi": "api-reference/openapi.json", "pages": [ - "GET /v1/account/balance" + "GET /v1/account/balance", + "GET /v1/account/billing" ] }, { - "group": "Scrape", + "group": "Extensions", "openapi": "api-reference/openapi.json", "pages": [ - "POST /v1/scrape" + "POST /v1/browser/extensions", + "GET /v1/browser/extensions", + "GET /v1/browser/extensions/{extensionId}", + "PUT /v1/browser/extensions/{extensionId}", + "DELETE /v1/browser/extensions/{extensionId}" ] } ] } - ], - "global": { - "anchors": [] - } - }, - "logo": { - "light": "/logo/light.svg", - "dark": "/logo/dark.svg" + ] }, "navbar": { "links": [ { "label": "Support", - "href": "mailto:alex@driver.dev" + "href": "mailto:support@driver.dev" + }, + { + "label": "Status", + "href": "https://status.driver.dev" } ], "primary": { @@ -129,7 +178,86 @@ }, "footer": { "socials": { - "github": "https://github.com/BrowserCash/docs" + "github": "https://github.com/BrowserCash", + "x": "https://x.com/driverdev" + } + }, + "redirects": [ + { + "source": "/", + "destination": "/docs/start/index" + }, + { + "source": "/docs", + "destination": "/docs/start/index" + }, + { + "source": "/docs/index", + "destination": "/docs/start/index" + }, + { + "source": "/docs/introduction/getting-started", + "destination": "/docs/start/quickstart" + }, + { + "source": "/docs/fundamentals/creating-a-browser-session", + "destination": "/docs/sessions/create" + }, + { + "source": "/docs/fundamentals/using-a-browser-session", + "destination": "/docs/sessions/connect" + }, + { + "source": "/docs/fundamentals/best-practices", + "destination": "/docs/start/best-practices" + }, + { + "source": "/docs/fundamentals/viewing-a-browser-session", + "destination": "/docs/sessions/live-view" + }, + { + "source": "/docs/fundamentals/managing-a-browser-session", + "destination": "/docs/sessions/lifecycle" + }, + { + "source": "/docs/features/browser-profiles", + "destination": "/docs/options/profiles" + }, + { + "source": "/docs/features/geolocation", + "destination": "/docs/options/location" + }, + { + "source": "/docs/features/node-selection", + "destination": "/docs/options/node-reuse" + }, + { + "source": "/docs/features/proxies", + "destination": "/docs/options/proxies" + }, + { + "source": "/docs/features/window-size", + "destination": "/docs/options/window-size" + }, + { + "source": "/docs/integrations/playwright", + "destination": "/docs/frameworks/playwright" + }, + { + "source": "/docs/integrations/puppeteer", + "destination": "/docs/frameworks/puppeteer" + }, + { + "source": "/docs/integrations/browser-use", + "destination": "/docs/frameworks/browser-use" + }, + { + "source": "/docs/integrations/crawl4ai", + "destination": "/docs/frameworks/crawl4ai" + }, + { + "source": "/docs/integrations/stagehand", + "destination": "/docs/frameworks/stagehand" } - } + ] } diff --git a/docs/account/api-keys.mdx b/docs/account/api-keys.mdx new file mode 100644 index 0000000..cce26ed --- /dev/null +++ b/docs/account/api-keys.mdx @@ -0,0 +1,25 @@ +--- +title: "API keys" +description: "Keys are per workspace, created in the dashboard, and sent as a bearer token." +--- + +Every workspace has a root key and can create additional named keys under **Settings → API keys** in the [dashboard](https://app.driver.dev). All keys of a workspace share its plan, credits and concurrency limit. + +```bash +Authorization: Bearer $DRIVER_API_KEY +``` + +## Handling keys + +- Keep the key in an environment variable named `DRIVER_API_KEY`. Do not commit it, and do not paste it into prompts, tickets or logs. +- Create one key per integration so a leaked key can be rolled without touching the others. Rolling a key invalidates the old value immediately. +- The root key cannot be deleted, only rolled. +- Keys are workspace-scoped, not user-scoped. Removing a person from the workspace does not invalidate keys they created; roll them if that matters. + +## Which workspace am I in + +A key belongs to exactly one workspace, and every session it creates is billed to that workspace and listed under its Telemetry. If you belong to several workspaces, check the workspace switcher at the top of the dashboard before creating a key. + +## Errors + +`401` means the key is missing, malformed or rolled. `402` and `403` are about the workspace's credits and plan, not the key; see [Errors](/docs/sessions/errors). diff --git a/docs/account/billing.mdx b/docs/account/billing.mdx new file mode 100644 index 0000000..f066b1b --- /dev/null +++ b/docs/account/billing.mdx @@ -0,0 +1,44 @@ +--- +title: "Billing" +description: "Sessions are metered by bandwidth against the workspace's credits. Two endpoints expose the balance and the plan." +--- + +Driver bills on **bandwidth**: the bytes a session moves, metered per megabyte and charged against the workspace's credits. A plan includes a monthly allowance and a concurrency limit; usage beyond the allowance draws on purchased credits. The exact rates and plans are on the [pricing page](https://driver.dev/pricing) and under **Billing** in the dashboard. + +Keeping sessions short and stopping them when the job is done is the whole cost-control story. `duration` on create caps what a forgotten session can cost. + +## Balance + + +```bash cURL +curl https://api.driver.dev/v1/account/balance -H "Authorization: Bearer $DRIVER_API_KEY" +``` +```typescript TypeScript +const { balance } = await fetch("https://api.driver.dev/v1/account/balance", { headers }) + .then((r) => r.json()); +``` +```python Python +balance = requests.get("https://api.driver.dev/v1/account/balance", headers=headers).json() +balance = balance["balance"] +``` + + +```json +{ "balance": { "balance_cents": 1250, "network_mb_remaining": 500, "status": "active" } } +``` + +`network_mb_remaining` is the included allowance still unused this period. Sessions are refused with `402` once the balance is negative and the allowance is gone. + +## Billing details + +`GET /v1/account/billing` returns the full picture: the plan (`plan_key`, `display_name`, `price_monthly_cents`, `included_network_mb`, `overage_millicents_per_mb`, `concurrent_browsers`, `max_session_seconds`), the current period (`start_ms`, `end_ms`, `network_mb_used`, `network_mb_remaining`), the wallet (`balance_cents`, `balance_millicents`), and the subscription (`status`, `current_period_end`, `cancel_at_period_end`) when there is one. + +```bash +curl https://api.driver.dev/v1/account/billing -H "Authorization: Bearer $DRIVER_API_KEY" +``` + +Use it to show usage in your own tooling or to stop launching sessions before you hit the limit. Amounts are cents; `millicents` fields are for exact arithmetic (1000 millicents = 1 cent). + +## Where to change things + +Plan changes, adding credits, auto top-up, invoices and the card on file all live under **Billing** in the [dashboard](https://app.driver.dev/settings/billing). There is no API for payments. diff --git a/docs/account/dashboard.mdx b/docs/account/dashboard.mdx new file mode 100644 index 0000000..7812f25 --- /dev/null +++ b/docs/account/dashboard.mdx @@ -0,0 +1,21 @@ +--- +title: "Dashboard" +description: "What lives in the dashboard: sessions and live view, the Workbench, profiles, keys, workspace members and billing." +--- + +The [dashboard](https://app.driver.dev) is the other half of the API. Everything below is per workspace; switch workspaces at the top of the sidebar. + +| Area | What you do there | +|---|---| +| **Overview** | Running browsers against the plan limit, today's sessions, bandwidth this period, credits, and a 30-day chart. **Launch browser** starts a session with options and opens its live view without writing code. | +| **Workbench** | A script editor next to a live browser and an output log. Pick a template, set country, network, profile, CAPTCHA solver and proxy, press Run. The quickest way to try options before committing them to code. | +| **Profiles** | The workspace's saved [profiles](/docs/options/profiles). Launch a session on a profile to sign in by hand. | +| **Sessions** (Telemetry) | Every session, filterable by status, with a record page per session: live view while running, then node, country, network, bandwidth, cost and timestamps. Stop one, or stop all running. | +| **Workspace** | Name, members and invitations. Admins manage members, keys and billing. | +| **API keys** | The root key and named keys: create, roll, delete. Also a ready-to-paste briefing for your coding agent. | +| **Billing** | Plan, credits, auto top-up, credit history, the billing portal for invoices and cards. | +| **Support** | Book a call or write to the team. | + +## Hand off to an agent + +The Overview and API keys pages carry a **Copy agent prompt** button. It produces a briefing for Claude Code, Cursor or any coding agent with the endpoints, the rules and, if you opt in, your API key. Pair it with the [For agents](/docs/start/for-agents) page here. diff --git a/docs/features/browser-profiles.mdx b/docs/features/browser-profiles.mdx deleted file mode 100644 index 09614ec..0000000 --- a/docs/features/browser-profiles.mdx +++ /dev/null @@ -1,110 +0,0 @@ ---- -title: "Browser Profiles" -description: "Persist browser state across hosted Driver sessions." ---- - -Browser profiles save cookies and browser state so later sessions can reuse the same login or site state. - - - Profiles are supported on hosted sessions. Use `type: "hosted"` when creating a session with a profile. - - -## Create a Session with a Profile - - - -```typescript TypeScript -const session = await client.browser.session.create({ - type: "hosted", - profile: { - name: "my-profile", - persist: true, - }, -}); -``` - -```python Python -resp = requests.post( - "https://api.driver.dev/v1/browser/session", - headers={ - "Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}", - "Content-Type": "application/json", - }, - json={ - "type": "hosted", - "profile": { - "name": "my-profile", - "persist": True, - }, - }, -) -resp.raise_for_status() -session = resp.json() -``` - -```bash cURL -curl -X POST "https://api.driver.dev/v1/browser/session" \ - -H "Authorization: Bearer $DRIVER_API_KEY" \ - -H "Content-Type: application/json" \ - -d '{"type":"hosted","profile":{"name":"my-profile","persist":true}}' -``` - - - -## Profile Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| `name` | string | Required profile name. Reuse this name to load the same profile later. | -| `persist` | boolean | Save profile changes when the session stops. | - -## List Profiles - - - -```typescript TypeScript -const { profiles } = await client.browser.profiles.list(); -``` - -```python Python -resp = requests.get( - "https://api.driver.dev/v1/browser/profiles", - headers={"Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}"}, -) -resp.raise_for_status() -print(resp.json()) -``` - -```bash cURL -curl "https://api.driver.dev/v1/browser/profiles" \ - -H "Authorization: Bearer $DRIVER_API_KEY" -``` - - - -## Delete a Profile - - - -```typescript TypeScript -const result = await client.browser.profiles.delete({ - profileName: "my-profile", -}); -``` - -```python Python -resp = requests.delete( - "https://api.driver.dev/v1/browser/profile", - headers={"Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}"}, - params={"profileName": "my-profile"}, -) -resp.raise_for_status() -print(resp.json()) -``` - -```bash cURL -curl -X DELETE "https://api.driver.dev/v1/browser/profile?profileName=my-profile" \ - -H "Authorization: Bearer $DRIVER_API_KEY" -``` - - diff --git a/docs/features/geolocation.mdx b/docs/features/geolocation.mdx deleted file mode 100644 index d053f68..0000000 --- a/docs/features/geolocation.mdx +++ /dev/null @@ -1,53 +0,0 @@ ---- -title: "Geolocation" -description: "Target specific countries for your browser sessions" ---- - -Run browser sessions from specific geographic locations by specifying a country code. - -## Setting Country - -Pass a 2-letter ISO country code when creating a session: - - - -```typescript TypeScript -const session = await client.browser.session.create({ - type: "hosted", - country: "US", // United States -}); -``` - -```python Python -resp = requests.post( - "https://api.driver.dev/v1/browser/session", - headers={ - "Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}", - "Content-Type": "application/json", - }, - json={"type": "hosted", "country": "US"} -) -resp.raise_for_status() -session = resp.json() -``` - -```bash cURL -curl -X POST https://api.driver.dev/v1/browser/session \ - -H "Authorization: Bearer $DRIVER_API_KEY" \ - -H "Content-Type: application/json" \ - -d '{"type":"hosted","country":"US"}' -``` - - - -## Availability - - - Available countries depend on node availability. If no nodes are available in - a requested country, the API will return an error. - - -## Notes - -- Country filtering is best-effort based on available nodes -- Some countries may have limited node availability diff --git a/docs/features/node-selection.mdx b/docs/features/node-selection.mdx deleted file mode 100644 index 48900d3..0000000 --- a/docs/features/node-selection.mdx +++ /dev/null @@ -1,63 +0,0 @@ ---- -title: "Node Targeting" -description: "Reuse a specific hosted node for a browser session." ---- - -Driver creates hosted sessions automatically. Most users should only pass `type: "hosted"`. - -Use `nodeId` only when you need a later session to run on the same hosted node that served a previous session. - -## Reuse a Hosted Node - -Every session response includes `servedBy`. Pass that value as `nodeId` in a later session: - - - -```typescript TypeScript -const firstSession = await client.browser.session.create({ type: "hosted" }); - -await client.browser.session.stop({ sessionId: firstSession.sessionId }); - -const nextSession = await client.browser.session.create({ - type: "hosted", - nodeId: firstSession.servedBy, -}); -``` - -```python Python -import os -import requests - -headers = { - "Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}", - "Content-Type": "application/json", -} - -first_resp = requests.post( - "https://api.driver.dev/v1/browser/session", - headers=headers, - json={"type": "hosted"}, -) -first_resp.raise_for_status() -first_session = first_resp.json() - -requests.delete( - "https://api.driver.dev/v1/browser/session", - headers=headers, - params={"sessionId": first_session["sessionId"]}, -) - -next_resp = requests.post( - "https://api.driver.dev/v1/browser/session", - headers=headers, - json={"type": "hosted", "nodeId": first_session["servedBy"]}, -) -next_resp.raise_for_status() -next_session = next_resp.json() -``` - - - - - `nodeId` overrides `country`. If the hosted node is unavailable, the request fails instead of falling back to another node. - diff --git a/docs/features/proxies.mdx b/docs/features/proxies.mdx deleted file mode 100644 index d337b30..0000000 --- a/docs/features/proxies.mdx +++ /dev/null @@ -1,52 +0,0 @@ ---- -title: "Proxies" -description: "Route a hosted browser session through your SOCKS5 proxy." ---- - -Pass `proxyUrl` when you need browser traffic to use your own SOCKS5 proxy. - -## Use a Proxy - - - -```typescript TypeScript -const session = await client.browser.session.create({ - type: "hosted", - proxyUrl: "socks5://user:pass@proxy.example.com:1080", -}); -``` - -```python Python -resp = requests.post( - "https://api.driver.dev/v1/browser/session", - headers={ - "Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}", - "Content-Type": "application/json", - }, - json={ - "type": "hosted", - "proxyUrl": "socks5://user:pass@proxy.example.com:1080", - }, -) -resp.raise_for_status() -session = resp.json() -``` - -```bash cURL -curl -X POST https://api.driver.dev/v1/browser/session \ - -H "Authorization: Bearer $DRIVER_API_KEY" \ - -H "Content-Type: application/json" \ - -d '{"type":"hosted","proxyUrl":"socks5://user:pass@proxy.example.com:1080"}' -``` - - - -## Format - -```text -socks5://username:password@host:port -``` - - - `proxyUrl` must point to a real SOCKS5 proxy reachable from Driver infrastructure. It cannot be used with `nodeId`. - diff --git a/docs/features/window-size.mdx b/docs/features/window-size.mdx deleted file mode 100644 index 2c90bd2..0000000 --- a/docs/features/window-size.mdx +++ /dev/null @@ -1,80 +0,0 @@ ---- -title: "Window Size" -description: "Configure browser viewport dimensions" ---- - -Set custom browser window dimensions for your sessions. - -## Setting Window Size - -Specify dimensions as `WIDTHxHEIGHT`: - - - -```typescript TypeScript -const session = await client.browser.session.create({ - type: "hosted", - windowSize: "1920x1080", -}); -``` - -```python Python -resp = requests.post( - "https://api.driver.dev/v1/browser/session", - headers={ - "Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}", - "Content-Type": "application/json", - }, - json={"type": "hosted", "windowSize": "1920x1080"} -) -resp.raise_for_status() -session = resp.json() -``` - -```bash cURL -curl -X POST https://api.driver.dev/v1/browser/session \ - -H "Authorization: Bearer $DRIVER_API_KEY" \ - -H "Content-Type: application/json" \ - -d '{"type":"hosted","windowSize":"1920x1080"}' -``` - - - -## Common Resolutions - -| Resolution | Aspect Ratio | Use Case | -| ----------- | ------------ | --------------- | -| `1920x1080` | 16:9 | Desktop Full HD | -| `1366x768` | 16:9 | Common laptop | -| `1536x864` | 16:9 | Scaled laptop | -| `1440x900` | 16:10 | MacBook | -| `1280x720` | 16:9 | HD | -| `390x844` | ~9:19.5 | iPhone 14 | -| `360x800` | 9:20 | Android mobile | - -## Dynamic Resizing - -You can also resize the viewport after connecting: - - - -```typescript Playwright (TypeScript) -await page.setViewportSize({ width: 1920, height: 1080 }); -``` - -```typescript Puppeteer (TypeScript) -await page.setViewport({ width: 1920, height: 1080 }); -``` - -```python Playwright (Python) -page.set_viewport_size({"width": 1920, "height": 1080}) -``` - - - -## Notes - -- Window size affects the initial browser window dimensions -- Some sites detect and respond to viewport size -- For responsive testing, combine with framework viewport controls -- Default size varies by node if not specified diff --git a/docs/frameworks/browser-use.mdx b/docs/frameworks/browser-use.mdx new file mode 100644 index 0000000..0aafc72 --- /dev/null +++ b/docs/frameworks/browser-use.mdx @@ -0,0 +1,46 @@ +--- +title: "Browser Use" +description: "Run Browser Use agents on a Driver browser by handing the agent the session's cdpUrl." +--- + +[Browser Use](https://browser-use.com) builds autonomous agents on top of a Chromium it controls. Give it a Driver session's `cdpUrl` and the agent drives real Chrome on a residential network instead of a local headless browser. + +```bash +pip install browser-use requests +``` + +```python Python +import asyncio, os, requests +from browser_use import Agent, Browser, ChatBrowserUse + +API = "https://api.driver.dev" +headers = {"Authorization": f"Bearer {os.environ['DRIVER_API_KEY']}"} + + +async def main(): + session = requests.post( + f"{API}/v1/browser/session", headers=headers, json={"country": "US", "duration": 900} + ).json() + try: + browser = Browser(cdp_url=session["cdpUrl"]) + agent = Agent( + task="Find the current price of Bitcoin", llm=ChatBrowserUse(), browser=browser + ) + result = await agent.run() + print(result) + finally: + requests.delete( + f"{API}/v1/browser/session", headers=headers, params={"sessionId": session["sessionId"]} + ) + + +asyncio.run(main()) +``` + +`ChatBrowserUse` uses a Browser Use Cloud model key (`BROWSER_USE_API_KEY`); any LangChain-style chat model works in its place. + +## Notes + +- Agents take many steps; set `duration` to cover the task (up to 3600 seconds) and stop the session in `finally`. +- Browser Use uses CDP directly and does not go through Playwright, so the [Patchright](/docs/frameworks/patchright) recommendation does not apply to it. +- Watch the agent through the session's [live view](/docs/sessions/live-view); it is the fastest way to see where an agent goes wrong. diff --git a/docs/frameworks/crawl4ai.mdx b/docs/frameworks/crawl4ai.mdx new file mode 100644 index 0000000..5296e1a --- /dev/null +++ b/docs/frameworks/crawl4ai.mdx @@ -0,0 +1,39 @@ +--- +title: "Crawl4AI" +description: "Point Crawl4AI's managed browser at a Driver session to crawl from real Chrome on a residential network." +--- + +[Crawl4AI](https://crawl4ai.com) turns pages into LLM-ready Markdown. Configure it to use a Driver session instead of launching its own browser. + +```bash +pip install crawl4ai requests +``` + +```python Python +import asyncio, os, requests +from crawl4ai import AsyncWebCrawler, BrowserConfig + +API = "https://api.driver.dev" +headers = {"Authorization": f"Bearer {os.environ['DRIVER_API_KEY']}"} + + +async def main(): + session = requests.post(f"{API}/v1/browser/session", headers=headers, json={"country": "US"}).json() + try: + config = BrowserConfig(browser_mode="custom", cdp_url=session["cdpUrl"], use_managed_browser=True) + async with AsyncWebCrawler(config=config) as crawler: + result = await crawler.arun(url="https://www.nbcnews.com/business") + print(result.markdown) + finally: + requests.delete( + f"{API}/v1/browser/session", headers=headers, params={"sessionId": session["sessionId"]} + ) + + +asyncio.run(main()) +``` + +## Notes + +- `browser_mode="custom"` with `cdp_url` tells Crawl4AI to attach to the browser you give it instead of launching one; `use_managed_browser=True` keeps control over CDP. +- Crawl several URLs in one session; creating the session is the expensive step. diff --git a/docs/frameworks/patchright.mdx b/docs/frameworks/patchright.mdx new file mode 100644 index 0000000..6d40107 --- /dev/null +++ b/docs/frameworks/patchright.mdx @@ -0,0 +1,133 @@ +--- +title: "Patchright" +description: "The recommended client for Driver: Playwright's API with the automation fingerprints removed. Node and Python." +--- + +[Patchright](https://github.com/Kaliiiiiiiiii-Vinyzu/patchright) is a patched build of Playwright. Same API, same docs, same selectors; the difference is what it sends over CDP. Use it for every Driver integration. + +## Why Patchright + +Stock Playwright (and Puppeteer) drive the browser through CDP calls that a normal Chrome never makes on its own, and anti-bot vendors look for exactly those: + +- `Runtime.enable`, which makes every page's JavaScript world observable and leaves detectable traces in the page. +- Console and binding hooks (`Runtime.addBinding`, `Page.addScriptToEvaluateOnNewDocument`) that Playwright installs for its own bookkeeping. +- Command-line switches and the `navigator.webdriver` flag in launched browsers (not relevant when connecting to Driver, but Patchright removes them anyway). + +Driver gives you a real Chrome on real hardware and a residential network. If the client then announces itself through CDP, that work is wasted and the session gets challenged or blocked. Patchright removes those signals while keeping the Playwright API, so the switch costs nothing. + +## Install + + +```bash npm +npm install patchright +``` +```bash pip +pip install patchright +``` + + +No browser download is needed; you connect to Driver's Chrome. + +## Connect + + +```typescript TypeScript +import { chromium } from "patchright"; + +const API = "https://api.driver.dev"; +const headers = { + Authorization: `Bearer ${process.env.DRIVER_API_KEY}`, + "Content-Type": "application/json", +}; + +const session = await fetch(`${API}/v1/browser/session`, { + method: "POST", + headers, + body: JSON.stringify({ country: "US" }), +}).then((r) => r.json()); + +try { + const browser = await chromium.connectOverCDP(session.cdpUrl); + const context = browser.contexts()[0] ?? (await browser.newContext()); + const page = context.pages()[0] ?? (await context.newPage()); + + await page.goto("https://example.com"); + console.log(await page.title()); + + await browser.close(); +} finally { + await fetch(`${API}/v1/browser/session?sessionId=${session.sessionId}`, { + method: "DELETE", + headers, + }); +} +``` +```python Python +import os, requests +from patchright.sync_api import sync_playwright + +API = "https://api.driver.dev" +headers = {"Authorization": f"Bearer {os.environ['DRIVER_API_KEY']}"} + +session = requests.post(f"{API}/v1/browser/session", headers=headers, json={"country": "US"}) + session = session.json() +try: + with sync_playwright() as p: + browser = p.chromium.connect_over_cdp(session["cdpUrl"]) + context = browser.contexts[0] if browser.contexts else browser.new_context() + page = context.pages[0] if context.pages else context.new_page() + + page.goto("https://example.com") + print(page.title()) + browser.close() +finally: + requests.delete( + f"{API}/v1/browser/session", headers=headers, params={"sessionId": session["sessionId"]} + ) +``` +```python Python (async) +import asyncio, os, requests +from patchright.async_api import async_playwright + +API = "https://api.driver.dev" +headers = {"Authorization": f"Bearer {os.environ['DRIVER_API_KEY']}"} + +async def main(): + session = requests.post(f"{API}/v1/browser/session", headers=headers, json={"country": "US"}) + session = session.json() + try: + async with async_playwright() as p: + browser = await p.chromium.connect_over_cdp(session["cdpUrl"]) + context = browser.contexts[0] if browser.contexts else await browser.new_context() + page = context.pages[0] if context.pages else await context.new_page() + await page.goto("https://example.com") + print(await page.title()) + await browser.close() + finally: + requests.delete( + f"{API}/v1/browser/session", headers=headers, params={"sessionId": session["sessionId"]} + ) + +asyncio.run(main()) +``` + + +## Migrating from Playwright + +Change the import. That is the whole migration. + +```diff +- import { chromium } from "playwright"; ++ import { chromium } from "patchright"; +``` + +```diff +- from playwright.sync_api import sync_playwright ++ from patchright.sync_api import sync_playwright +``` + +Remove any `chromium.launch()` and any `playwright install` step: Driver provides the browser. + +## Things Patchright does not fix + +Patchright removes the client's fingerprints. It cannot undo choices in your script that look automated: fresh contexts for every page, broad `page.route` interception, injected init scripts, overridden `navigator` properties, or clicking a thousand things per minute. The [best practices](/docs/start/best-practices) cover those. diff --git a/docs/frameworks/playwright.mdx b/docs/frameworks/playwright.mdx new file mode 100644 index 0000000..0c6ace6 --- /dev/null +++ b/docs/frameworks/playwright.mdx @@ -0,0 +1,56 @@ +--- +title: "Playwright" +description: "Stock Playwright connects to Driver over CDP, with fingerprints that get sessions blocked on protected sites. Prefer Patchright." +--- + + +Stock Playwright issues CDP commands that anti-bot systems detect (`Runtime.enable` and its bookkeeping hooks). On sites that look for automation, sessions driven by stock Playwright get challenged or blocked. Use [Patchright](/docs/frameworks/patchright) instead: same API, one import change. + + +If you have measured that your targets do not care, stock Playwright works unchanged: + + +```typescript TypeScript +import { chromium } from "playwright"; + +const browser = await chromium.connectOverCDP(session.cdpUrl); +const context = browser.contexts()[0] ?? (await browser.newContext()); +const page = context.pages()[0] ?? (await context.newPage()); +await page.goto("https://example.com"); +console.log(await page.title()); +await browser.close(); +``` +```python Python +from playwright.sync_api import sync_playwright + +with sync_playwright() as p: + browser = p.chromium.connect_over_cdp(session["cdpUrl"]) + context = browser.contexts[0] if browser.contexts else browser.new_context() + page = context.pages[0] if context.pages else context.new_page() + page.goto("https://example.com") + print(page.title()) + browser.close() +``` + + +Install with `npm install playwright` or `pip install playwright`. Do not run `playwright install`; the browser is Driver's. + +## Using the TypeScript SDK for the session calls + +The `@browsercash/sdk` package wraps the REST API. Point it at Driver's base URL: + +```typescript +import BrowsercashSDK from "@browsercash/sdk"; + +const client = new BrowsercashSDK({ + apiKey: process.env.DRIVER_API_KEY!, + baseURL: "https://api.driver.dev", +}); +const session = await client.browser.session.create({ country: "US" }); +// … connect with patchright or playwright … +await client.browser.session.stop({ sessionId: session.sessionId }); +``` + +## Everything else + +Contexts, tabs, disconnecting versus stopping, reconnecting and timeouts are the same as for Patchright; see [Connect over CDP](/docs/sessions/connect). diff --git a/docs/frameworks/puppeteer.mdx b/docs/frameworks/puppeteer.mdx new file mode 100644 index 0000000..5f0ab90 --- /dev/null +++ b/docs/frameworks/puppeteer.mdx @@ -0,0 +1,51 @@ +--- +title: "Puppeteer" +description: "puppeteer-core connects to Driver with browserWSEndpoint. Same detectability caveat as stock Playwright." +--- + + +Puppeteer, like stock Playwright, makes CDP calls that anti-bot systems fingerprint. For protected sites use [Patchright](/docs/frameworks/patchright). If your code base is Puppeteer and the targets are not hostile, the connection below works. + + +Install `puppeteer-core`, not `puppeteer`: Driver provides the browser, so nothing should be downloaded. + +```bash +npm install puppeteer-core +``` + +```typescript TypeScript +import puppeteer from "puppeteer-core"; + +const API = "https://api.driver.dev"; +const headers = { + Authorization: `Bearer ${process.env.DRIVER_API_KEY}`, + "Content-Type": "application/json", +}; + +const session = await fetch(`${API}/v1/browser/session`, { + method: "POST", + headers, + body: JSON.stringify({ country: "US" }), +}).then((r) => r.json()); + +try { + const browser = await puppeteer.connect({ browserWSEndpoint: session.cdpUrl }); + const [page = await browser.newPage()] = await browser.pages(); // reuse the open tab + + await page.goto("https://example.com"); + console.log(await page.title()); + + await browser.disconnect(); // ends your connection only +} finally { + await fetch(`${API}/v1/browser/session?sessionId=${session.sessionId}`, { + method: "DELETE", + headers, + }); +} +``` + +## Notes + +- End your connection with `browser.disconnect()`, then stop the session through the API. Do not use `browser.close()`: it tells the remote Chrome to quit, and the session record is then closed from the browser's side rather than by your stop call. +- `browser.pages()` returns the tab the session opened with. Reuse it rather than creating a fresh one. +- Viewport: prefer `windowSize` on create over `page.setViewport`, see [Window size](/docs/options/window-size). diff --git a/docs/frameworks/stagehand.mdx b/docs/frameworks/stagehand.mdx new file mode 100644 index 0000000..42477b0 --- /dev/null +++ b/docs/frameworks/stagehand.mdx @@ -0,0 +1,60 @@ +--- +title: "Stagehand" +description: "Natural-language act and extract on a Driver browser: connect Stagehand v4 to the session's cdpUrl." +--- + +[Stagehand](https://docs.stagehand.dev) adds `act`, `extract` and `observe` on top of a browser it attaches to. With Stagehand v4, connect it to a Driver session's `cdpUrl` through `localBrowser.connect`. + +```bash +npm install @browserbasehq/stagehand zod@~4.4.3 +``` + +```typescript TypeScript +import { localBrowser, Stagehand } from "@browserbasehq/stagehand"; +import { z } from "zod/v4"; + +const API = "https://api.driver.dev"; +const headers = { + Authorization: `Bearer ${process.env.DRIVER_API_KEY}`, + "Content-Type": "application/json", +}; + +const session = await fetch(`${API}/v1/browser/session`, { + method: "POST", + headers, + body: JSON.stringify({ country: "US" }), +}).then((r) => r.json()); + +try { + // Attach to Driver's Chrome; Stagehand never launches or closes a browser it did not start. + const browser = await localBrowser.connect({ cdpUrl: session.cdpUrl }); + const stagehand = await Stagehand.create({ + browser, + model: { modelName: "openai/gpt-5.6-sol", apiKey: process.env.OPENAI_API_KEY }, + }); + + const [page] = await browser.context.pages(); // the tab the session opened with + await page.goto("https://example.com"); + + const data = await stagehand.extract( + "Extract the page title and main heading", + z.object({ title: z.string(), heading: z.string() }), + ); + console.log(data); + + await stagehand.close(); // ends the Stagehand connection; the session keeps running +} finally { + await fetch(`${API}/v1/browser/session?sessionId=${session.sessionId}`, { + method: "DELETE", + headers, + }); +} +``` + +Stagehand reads no environment variables itself: pass the model provider key in `model.apiKey`. + +## Notes + +- This is the v4 API (`localBrowser.connect`, `Stagehand.create`). Stagehand 2.x used `new Stagehand({ env: "LOCAL", localBrowserLaunchOptions: { cdpUrl } })`; if you are on 2.x, that is the equivalent. +- Stagehand instruments the page to work (it loads its own extension and drives the browser over CDP), which is the kind of signal protected sites look for. On those sites, prefer plain [Patchright](/docs/frameworks/patchright) with your own extraction, or keep Stagehand to the parts of a flow that are not behind bot protection. +- Reuse the page from `browser.context.pages()` rather than opening a new one. diff --git a/docs/fundamentals/best-practices.mdx b/docs/fundamentals/best-practices.mdx deleted file mode 100644 index 58bb523..0000000 --- a/docs/fundamentals/best-practices.mdx +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: "Best Practices" -description: "Use Driver with minimal CDP noise and reliable cleanup." ---- - -These practices keep Driver examples simple, reduce unnecessary browser instrumentation, and avoid CDP event patterns that can make automation more fragile or easier to classify. - -## Prefer the Remote Browser Defaults - -Do not change browser settings unless the task requires it. - -- Reuse `browser.contexts()[0]` if it exists. -- Reuse `context.pages()[0]` if it exists. -- Keep the default viewport, user agent, locale, permissions, and browser flags. -- Avoid extra timeouts and navigation settings until a specific target needs them. - -## Avoid Local Browser Launches - -Driver provides the browser. Connect to the returned `cdpUrl` instead of launching a local browser. - -Avoid these APIs in Driver quickstarts: - -- `chromium.launch()` -- `chromium.launchPersistentContext()` -- `puppeteer.launch()` -- invented localhost CDP ports -- custom CDP hosts not returned by Driver - -You also do not need `playwright install` for the minimal Driver quickstart, because the browser runs remotely. - -## Minimize Detection-Prone CDP Hooks - -CDP clients can create noisy or unusual browser behavior when they install broad event listeners, route all network traffic, or inject scripts into every page. Use these only when the task genuinely requires them. - -Avoid by default: - -- `page.on(...)` -- `context.on(...)` -- `browser.on(...)` -- `page.route(...)` -- `context.route(...)` -- `page.exposeBinding(...)` -- `page.exposeFunction(...)` -- `page.addInitScript(...)` -- `context.addInitScript(...)` - -Prefer direct page actions and reads: - -- `page.goto(...)` -- `page.click(...)` -- `page.fill(...)` -- `page.locator(...)` -- `page.title()` -- `page.textContent(...)` - -## Debug with the Viewer First - -When a page behaves differently than expected, inspect the live session with the [browser viewer](/docs/fundamentals/viewing-a-browser-session) before adding broad CDP hooks. The viewer lets you see the remote browser state without routing network requests, injecting scripts, or subscribing to every browser event. - -## Keep Required Hooks Narrow - -Some workflows need request interception, event listeners, or initialization scripts. When they do, keep them scoped to the smallest surface possible. - -- Listen for one event type instead of every event on the browser. -- Route one URL pattern instead of all requests. -- Remove listeners and routes when the specific step is finished. -- Avoid fingerprint patches such as overriding `navigator`, user agent, WebGL, canvas, timezone, or locale unless the target task explicitly requires it. - -## Always Stop Sessions - -Use `try` / `finally` around browser work so sessions are stopped even when navigation or automation fails. - -```typescript TypeScript -const session = await client.browser.session.create({ type: "hosted" }); - -try { - // Connect and automate. -} finally { - await client.browser.session.stop({ sessionId: session.sessionId }); -} -``` diff --git a/docs/fundamentals/creating-a-browser-session.mdx b/docs/fundamentals/creating-a-browser-session.mdx deleted file mode 100644 index fdf10e8..0000000 --- a/docs/fundamentals/creating-a-browser-session.mdx +++ /dev/null @@ -1,141 +0,0 @@ ---- -title: "Create a Browser Session" -description: "Start a managed Chrome session and receive a CDP URL." ---- - -Create a browser session when you need explicit lifecycle control, session options, session history, or reconnect support. - -## Create a Session - - - -```bash cURL -curl -X POST "https://api.driver.dev/v1/browser/session" \ - -H "Authorization: Bearer $DRIVER_API_KEY" \ - -H "Content-Type: application/json" \ - -d '{"type":"hosted"}' -``` - -```python Python -import os -import requests - -resp = requests.post( - "https://api.driver.dev/v1/browser/session", - headers={ - "Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}", - "Content-Type": "application/json", - }, - json={"type": "hosted"}, -) -resp.raise_for_status() -session = resp.json() -print(session["sessionId"], session["cdpUrl"]) -``` - -```typescript TypeScript -import BrowsercashSDK from "@browsercash/sdk"; - -const client = new BrowsercashSDK({ - apiKey: process.env.DRIVER_API_KEY!, - baseURL: "https://api.driver.dev", -}); - -const session = await client.browser.session.create({ type: "hosted" }); -console.log(session.sessionId, session.cdpUrl); -``` - - - -## Response - -```json -{ - "sessionId": "example-session-id", - "status": "active", - "servedBy": "node-id", - "createdAt": "2026-06-10T01:15:17.157Z", - "stoppedAt": null, - "cdpUrl": "wss://...", - "note": null -} -``` - -## Session Options - -| Parameter | Type | Description | -| --- | --- | --- | -| `type` | string | Use `hosted`. | -| `country` | string | Two-letter ISO country code, such as `US`. | -| `nodeId` | string | Create the session on a specific previously used hosted node. Overrides `country`. | -| `duration` | integer | Session duration in seconds. Must be greater than 60 and no more than 3600. | -| `windowSize` | string | Initial browser dimensions as `WIDTHxHEIGHT`, such as `1920x1080`. | -| `proxyUrl` | string | SOCKS5 proxy URL for the session. Cannot be used with `nodeId`. | -| `profile` | object | Browser profile: `{ "name": "...", "persist": true }`. | -| `fast` | boolean | Attempts faster startup when the request is eligible. | -| `captchaSolver` | boolean | Enables the built-in CAPTCHA solver for the session. | -| `extensionIds` | string[] | Chrome extension IDs to load. Requires extension access for your account. | - - - Session notes are updated with `PATCH /v1/browser/session?sessionId=...`. Do not rely on `note` during create; current create responses return `note: null`. - - -## Example with Options - - - -```bash cURL -curl -X POST "https://api.driver.dev/v1/browser/session" \ - -H "Authorization: Bearer $DRIVER_API_KEY" \ - -H "Content-Type: application/json" \ - -d '{ - "country": "US", - "type": "hosted", - "duration": 600, - "windowSize": "1920x1080", - "profile": { - "name": "my-profile", - "persist": true - } - }' -``` - -```python Python -resp = requests.post( - "https://api.driver.dev/v1/browser/session", - headers={ - "Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}", - "Content-Type": "application/json", - }, - json={ - "country": "US", - "type": "hosted", - "duration": 600, - "windowSize": "1920x1080", - "profile": { - "name": "my-profile", - "persist": True, - }, - }, -) -``` - -```typescript TypeScript -const session = await client.browser.session.create({ - country: "US", - type: "hosted", - duration: 600, - windowSize: "1920x1080", - profile: { - name: "my-profile", - persist: true, - }, -}); -``` - - - -## Next Steps - -- [Use a Browser Session](/docs/fundamentals/using-a-browser-session) -- [Manage Browser Sessions](/docs/fundamentals/managing-a-browser-session) diff --git a/docs/fundamentals/managing-a-browser-session.mdx b/docs/fundamentals/managing-a-browser-session.mdx deleted file mode 100644 index c5ac677..0000000 --- a/docs/fundamentals/managing-a-browser-session.mdx +++ /dev/null @@ -1,125 +0,0 @@ ---- -title: "Manage Browser Sessions" -description: "List, fetch, update, and stop Driver browser sessions." ---- - -Use session management endpoints to inspect active sessions, update notes, and stop browsers when automation completes. - -## Get a Session - - - -```bash cURL -curl "https://api.driver.dev/v1/browser/session?sessionId=$SESSION_ID" \ - -H "Authorization: Bearer $DRIVER_API_KEY" -``` - -```python Python -resp = requests.get( - "https://api.driver.dev/v1/browser/session", - headers={"Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}"}, - params={"sessionId": os.getenv("SESSION_ID")}, -) -print(resp.json()) -``` - -```typescript TypeScript -const session = await client.browser.session.get({ - sessionId: process.env.SESSION_ID!, -}); -``` - - - -## List Sessions - - - -```bash cURL -curl "https://api.driver.dev/v1/browser/sessions?page=1&pageSize=20&status=active" \ - -H "Authorization: Bearer $DRIVER_API_KEY" -``` - -```python Python -resp = requests.get( - "https://api.driver.dev/v1/browser/sessions", - headers={"Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}"}, - params={"page": 1, "pageSize": 20, "status": "active"}, -) -print(resp.json()) -``` - -```typescript TypeScript -const sessions = await client.browser.session.list({ - page: 1, - pageSize: 20, - status: "active", -}); -``` - - - -## Update a Session Note - -Notes are updated after session creation. - - - -```bash cURL -curl -X PATCH "https://api.driver.dev/v1/browser/session?sessionId=$SESSION_ID" \ - -H "Authorization: Bearer $DRIVER_API_KEY" \ - -H "Content-Type: application/json" \ - -d '{"note":"Scraping product pages"}' -``` - -```python Python -resp = requests.patch( - "https://api.driver.dev/v1/browser/session", - headers={ - "Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}", - "Content-Type": "application/json", - }, - params={"sessionId": os.getenv("SESSION_ID")}, - json={"note": "Scraping product pages"}, -) -print(resp.json()) -``` - - - -## Stop a Session - -Always stop sessions when your automation completes. - - - -```bash cURL -curl -X DELETE "https://api.driver.dev/v1/browser/session?sessionId=$SESSION_ID" \ - -H "Authorization: Bearer $DRIVER_API_KEY" -``` - -```python Python -resp = requests.delete( - "https://api.driver.dev/v1/browser/session", - headers={"Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}"}, - params={"sessionId": os.getenv("SESSION_ID")}, -) -print(resp.json()) -``` - -```typescript TypeScript -const result = await client.browser.session.stop({ - sessionId: process.env.SESSION_ID!, -}); -``` - - - -## Session Statuses - -| Status | Meaning | -| --- | --- | -| `starting` | Session is being created. | -| `active` | Session is running and can accept CDP connections. | -| `completed` | Session has stopped successfully. | -| `error` | Session failed or ended with an error. | diff --git a/docs/fundamentals/using-a-browser-session.mdx b/docs/fundamentals/using-a-browser-session.mdx deleted file mode 100644 index dbd544e..0000000 --- a/docs/fundamentals/using-a-browser-session.mdx +++ /dev/null @@ -1,88 +0,0 @@ ---- -title: "Use a Browser Session" -description: "Connect to Driver with Playwright or Puppeteer." ---- - -After creating a hosted session, connect to the returned `cdpUrl` with a CDP-compatible automation client. - -## Playwright - - - -```typescript TypeScript -import { chromium } from "playwright"; -import BrowsercashSDK from "@browsercash/sdk"; - -const client = new BrowsercashSDK({ - apiKey: process.env.DRIVER_API_KEY!, - baseURL: "https://api.driver.dev", -}); - -const session = await client.browser.session.create({ type: "hosted" }); -const browser = await chromium.connectOverCDP(session.cdpUrl); - -const context = browser.contexts()[0] || (await browser.newContext()); -const page = context.pages()[0] || (await context.newPage()); - -await page.goto("https://example.com"); -console.log(await page.title()); - -await browser.close(); -await client.browser.session.stop({ sessionId: session.sessionId }); -``` - -```python Python -import os -import requests -from playwright.sync_api import sync_playwright - -resp = requests.post( - "https://api.driver.dev/v1/browser/session", - headers={"Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}"}, - json={"type": "hosted"}, -) -resp.raise_for_status() -session = resp.json() - -try: - with sync_playwright() as p: - browser = p.chromium.connect_over_cdp(session["cdpUrl"]) - context = browser.contexts[0] if browser.contexts else browser.new_context() - page = context.pages[0] if context.pages else context.new_page() - page.goto("https://example.com") - print(page.title()) - browser.close() -finally: - requests.delete( - "https://api.driver.dev/v1/browser/session", - headers={"Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}"}, - params={"sessionId": session["sessionId"]}, - ) -``` - - - -## Puppeteer - -```typescript -import puppeteer from "puppeteer-core"; - -const browser = await puppeteer.connect({ - browserWSEndpoint: session.cdpUrl, -}); - -const page = await browser.newPage(); -await page.goto("https://example.com"); -console.log(await page.title()); - -await browser.disconnect(); -``` - -## Cleanup - -Close or disconnect your automation client, then stop the Driver session through the API. Stopping the session is what releases the browser and ends billing. - -```bash -curl -X DELETE "https://api.driver.dev/v1/browser/session?sessionId=$SESSION_ID" \ - -H "Authorization: Bearer $DRIVER_API_KEY" -``` diff --git a/docs/fundamentals/viewing-a-browser-session.mdx b/docs/fundamentals/viewing-a-browser-session.mdx deleted file mode 100644 index e1e5044..0000000 --- a/docs/fundamentals/viewing-a-browser-session.mdx +++ /dev/null @@ -1,104 +0,0 @@ ---- -title: "View a Browser Session" -description: "Watch and interact with a running Driver browser session." ---- - -The live viewer lets you inspect a running browser and manually interact with it during development or debugging. - -## Open the Viewer - -Create a viewer URL from the session `cdpUrl`: - -```text -https://viewer.driver.dev?ws= -``` - - - -```typescript TypeScript -const session = await client.browser.session.create({ type: "hosted" }); -const viewerUrl = `https://viewer.driver.dev?ws=${session.cdpUrl}`; - -console.log(viewerUrl); -``` - -```python Python -viewer_url = f"https://viewer.driver.dev?ws={session['cdpUrl']}" -print(viewer_url) -``` - - - - - Keep the Driver session active while the viewer is connected. Stop the session when you are finished debugging. - - -## Viewer Parameters - -| Parameter | Description | -| --- | --- | -| `ws` | CDP WebSocket URL from the session response. | -| `theme` | Optional viewer theme: `light` or `dark`. | - -## Poll Session Status - -Use the session status to wait for a browser to become available or to detect that it has ended. - - - -```typescript TypeScript -async function waitForActiveSession(sessionId: string, timeoutMs = 30000) { - const startedAt = Date.now(); - - while (Date.now() - startedAt < timeoutMs) { - const session = await client.browser.session.get({ sessionId }); - - if (session.status === "active" && session.cdpUrl) return session; - if (session.status === "completed" || session.status === "error") { - throw new Error(`Session ended with status: ${session.status}`); - } - - await new Promise((resolve) => setTimeout(resolve, 1000)); - } - - throw new Error("Timed out waiting for session"); -} -``` - -```python Python -import os -import time -import requests - -def wait_for_active_session(session_id: str, timeout_seconds: int = 30): - started_at = time.time() - - while time.time() - started_at < timeout_seconds: - resp = requests.get( - "https://api.driver.dev/v1/browser/session", - headers={"Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}"}, - params={"sessionId": session_id}, - ) - resp.raise_for_status() - session = resp.json() - - if session["status"] == "active" and session.get("cdpUrl"): - return session - if session["status"] in ("completed", "error"): - raise RuntimeError(f"Session ended with status: {session['status']}") - - time.sleep(1) - - raise TimeoutError("Timed out waiting for session") -``` - - - -## Status Values - -| Status | Meaning | -| --- | --- | -| `starting` | Session is being created. | -| `active` | Session is running and can accept CDP connections. | -| `completed` | Session has stopped successfully. | -| `error` | Session failed or ended with an error. | diff --git a/docs/index.mdx b/docs/index.mdx deleted file mode 100644 index 71edcdb..0000000 --- a/docs/index.mdx +++ /dev/null @@ -1,103 +0,0 @@ ---- -title: "Overview" -description: "Create managed browser sessions and use Driver's API from your automation code." ---- - -Driver provides managed Chrome sessions that you control over CDP. Create a hosted session, connect with Playwright, Puppeteer, Browser-Use, Crawl4AI, Stagehand, or another CDP client, then stop the session when your automation finishes. - -## Base URL - -```text -https://api.driver.dev -``` - -Send your API key as a bearer token: - -```bash -Authorization: Bearer $DRIVER_API_KEY -``` - -## Quick Start - - - -```bash cURL -curl -X POST "https://api.driver.dev/v1/browser/session" \ - -H "Authorization: Bearer $DRIVER_API_KEY" \ - -H "Content-Type: application/json" \ - -d '{"type":"hosted"}' -``` - -```python Python -import os -import requests - -resp = requests.post( - "https://api.driver.dev/v1/browser/session", - headers={ - "Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}", - "Content-Type": "application/json", - }, - json={"type": "hosted"}, -) -resp.raise_for_status() -session = resp.json() -print(session["cdpUrl"]) -``` - -```typescript TypeScript -import BrowsercashSDK from "@browsercash/sdk"; - -const client = new BrowsercashSDK({ - apiKey: process.env.DRIVER_API_KEY!, - baseURL: "https://api.driver.dev", -}); - -const session = await client.browser.session.create({ type: "hosted" }); -console.log(session.cdpUrl); -``` - - - -## Core Flow - - - - `POST /v1/browser/session` returns a `sessionId`, status, node, and `cdpUrl`. - - - Use `cdpUrl` with Playwright, Puppeteer, Browser-Use, Crawl4AI, Stagehand, or another CDP-compatible client. - - - Navigate, click, scrape, or test from the remote browser. - - - `DELETE /v1/browser/session?sessionId=...` stops billing and releases the browser. - - - -## Current APIs - - - - Create, list, fetch, update, and stop managed browser sessions. - - - Configure hosted sessions with country, duration, window size, and profiles. - - - Persist cookies and browser state across hosted sessions. - - - View the Mintlify API reference generated from the Driver OpenAPI spec. - - - -## Account Balance - -Use the balance endpoint to check the current account balance and included network allowance. - -```bash -curl "https://api.driver.dev/v1/account/balance" \ - -H "Authorization: Bearer $DRIVER_API_KEY" -``` diff --git a/docs/integrations/browser-use.mdx b/docs/integrations/browser-use.mdx deleted file mode 100644 index 5f8155b..0000000 --- a/docs/integrations/browser-use.mdx +++ /dev/null @@ -1,152 +0,0 @@ ---- -title: "Browser-Use" -description: "Build browser agents with Driver and Browser-Use" ---- - -[Browser-Use](https://browser-use.com/) is an open-source framework for building autonomous browser agents. Combine it with Driver hosted sessions to run Browser-Use on managed Chrome infrastructure. - -## Installation - -```bash -# Create environment with uv (Python >= 3.11) -uv init -uv add browser-use requests -uv sync -``` - -## Setup - -Get your API keys: -1. [Browser-Use Cloud API key](https://cloud.browser-use.com/new-api-key) -2. [Driver API key](https://app.driver.dev) - -```bash -# .env -BROWSER_USE_API_KEY=your-browser-use-key -DRIVER_API_KEY=your-driver-key -``` - -## Quick Start - -```python Python -import os -import asyncio -import requests -from browser_use import Agent, Browser, ChatBrowserUse - - -async def run_agent(): - # Create Driver session - resp = requests.post( - "https://api.driver.dev/v1/browser/session", - headers={ - "Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}", - "Content-Type": "application/json", - }, - json={"type": "hosted"}, - ) - resp.raise_for_status() - data = resp.json() - - # Create Browser-Use browser with Driver CDP URL - browser = Browser(cdp_url=data["cdpUrl"]) - llm = ChatBrowserUse() - - # Create and run agent - agent = Agent( - task="Find the current price of Bitcoin", - llm=llm, - browser=browser, - ) - - result = await agent.run() - - # Cleanup - requests.delete( - "https://api.driver.dev/v1/browser/session", - headers={"Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}"}, - params={"sessionId": data["sessionId"]}, - ) - - return result - - -if __name__ == "__main__": - result = asyncio.run(run_agent()) - print(result) -``` - -## With Session Options - -```python Python -resp = requests.post( - "https://api.driver.dev/v1/browser/session", - headers={ - "Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}", - "Content-Type": "application/json", - }, - json={ - "country": "US", - "type": "hosted", - "windowSize": "1920x1080", - }, -) -``` - -## Complex Tasks - -```python Python -agent = Agent( - task=""" - 1. Go to Amazon.com - 2. Search for "wireless headphones" - 3. Find the top-rated product under $100 - 4. Extract the product name, price, and rating - """, - llm=llm, - browser=browser, -) -``` - -## With Persistent Profiles - -Keep login sessions across agent runs: - -```python Python -import os -import requests - -headers = { - "Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}", - "Content-Type": "application/json", -} - -# First run — login -resp = requests.post( - "https://api.driver.dev/v1/browser/session", - headers=headers, - json={ - "type": "hosted", - "profile": {"name": "my-agent-profile", "persist": True} - }, -) - -# Agent logs into a service... - -# Later runs — already logged in -resp = requests.post( - "https://api.driver.dev/v1/browser/session", - headers=headers, - json={ - "type": "hosted", - "profile": {"name": "my-agent-profile", "persist": True} - }, -) -``` - -## Why Driver + Browser-Use? - -- **Hosted browsers** — Run agents on managed Chrome sessions -- **Global reach** — Run agents from different countries -- **Persistent state** — Maintain logins across agent sessions -- **Scalability** — No local browser management diff --git a/docs/integrations/crawl4ai.mdx b/docs/integrations/crawl4ai.mdx deleted file mode 100644 index 9fa1141..0000000 --- a/docs/integrations/crawl4ai.mdx +++ /dev/null @@ -1,136 +0,0 @@ ---- -title: "Crawl4AI" -description: "Combine Driver hosted sessions with Crawl4AI." ---- - -[Crawl4AI](https://crawl4ai.com/) turns the web into clean, LLM-ready Markdown for RAG, agents, and data pipelines. Combine it with Driver hosted sessions to crawl from managed Chrome infrastructure. - -## Installation - -```bash -# Create environment with uv (Python >= 3.11) -uv init -uv add crawl4ai requests -uv sync -``` - -## Setup - -Get your API key from the [Driver dashboard](https://app.driver.dev): - -```bash -# .env -DRIVER_API_KEY=your-driver-key -``` - -## Quick Start - -```python Python -import os -import asyncio -import requests -from crawl4ai import AsyncWebCrawler, BrowserConfig - - -async def main(): - # Create Driver session - resp = requests.post( - "https://api.driver.dev/v1/browser/session", - headers={ - "Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}", - "Content-Type": "application/json", - }, - json={"type": "hosted"}, - ) - resp.raise_for_status() - data = resp.json() - - # Configure Crawl4AI to use the Driver browser - browser_config = BrowserConfig( - cdp_url=data["cdpUrl"], - use_managed_browser=True, - ) - - # Run the crawler - async with AsyncWebCrawler(config=browser_config) as crawler: - result = await crawler.arun( - url="https://www.nbcnews.com/business", - ) - print(result.markdown) - - # Cleanup - requests.delete( - "https://api.driver.dev/v1/browser/session", - headers={"Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}"}, - params={"sessionId": data["sessionId"]}, - ) - - -if __name__ == "__main__": - asyncio.run(main()) -``` - -```bash -uv run main.py -``` - -## With Session Options - -```python Python -resp = requests.post( - "https://api.driver.dev/v1/browser/session", - headers={ - "Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}", - "Content-Type": "application/json", - }, - json={ - "country": "US", - "type": "hosted", - "windowSize": "1920x1080", - }, -) -``` - -## With Persistent Profiles - -Keep cookies and session data across crawl runs: - -```python Python -import os -import requests - -headers = { - "Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}", - "Content-Type": "application/json", -} - -# First run — establish session -resp = requests.post( - "https://api.driver.dev/v1/browser/session", - headers=headers, - json={ - "type": "hosted", - "profile": {"name": "my-crawler-profile", "persist": True} - }, -) - -# Crawler runs and stores cookies... - -# Later runs — cookies and data preserved -resp = requests.post( - "https://api.driver.dev/v1/browser/session", - headers=headers, - json={ - "type": "hosted", - "profile": {"name": "my-crawler-profile", "persist": True} - }, -) -``` - -## Why Driver + Crawl4AI? - -- **Hosted browsers** — Crawl with managed Chrome sessions -- **LLM-ready output** — Clean Markdown for RAG and AI pipelines -- **Global reach** — Crawl from different countries -- **Persistent state** — Maintain sessions across crawl runs -- **Scalability** — No local browser management diff --git a/docs/integrations/playwright.mdx b/docs/integrations/playwright.mdx deleted file mode 100644 index a5b912a..0000000 --- a/docs/integrations/playwright.mdx +++ /dev/null @@ -1,277 +0,0 @@ ---- -title: "Playwright" -description: "Connect Driver sessions to Playwright" ---- - -[Playwright](https://playwright.dev) is a modern browser automation framework with auto-waiting, powerful selectors, and multi-browser support. - -## Installation - - - -```bash npm -npm install playwright @browsercash/sdk -``` - -```bash pip -pip install playwright requests -``` - - - -## Quick Start - - - -```typescript TypeScript -import { chromium } from "playwright"; -import BrowsercashSDK from "@browsercash/sdk"; - -async function run() { - const client = new BrowsercashSDK({ - apiKey: process.env.DRIVER_API_KEY!, - baseURL: "https://api.driver.dev", - }); - - // Create a Driver session - const session = await client.browser.session.create({ type: "hosted" }); - - // Connect Playwright via CDP - const browser = await chromium.connectOverCDP(session.cdpUrl); - - // Get existing context or create new one - const context = browser.contexts()[0] || (await browser.newContext()); - const page = context.pages()[0] || (await context.newPage()); - - // Automate - await page.goto("https://example.com"); - console.log(await page.title()); - - // Cleanup - await browser.close(); - await client.browser.session.stop({ sessionId: session.sessionId }); -} - -run().catch(console.error); -``` - -```python Python -import os -import requests -from playwright.sync_api import sync_playwright - -# Create a Driver session -resp = requests.post( - "https://api.driver.dev/v1/browser/session", - headers={ - "Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}", - "Content-Type": "application/json", - }, - json={"type": "hosted"} -) -session = resp.json() - -# Connect Playwright via CDP -with sync_playwright() as p: - browser = p.chromium.connect_over_cdp(session["cdpUrl"]) - - # Get existing context or create new one - context = browser.contexts[0] if browser.contexts else browser.new_context() - page = context.pages[0] if context.pages else context.new_page() - - # Automate - page.goto("https://example.com") - print(page.title()) - - browser.close() - -# Cleanup -requests.delete( - "https://api.driver.dev/v1/browser/session", - headers={"Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}"}, - params={"sessionId": session["sessionId"]} -) -``` - - - -## With Session Options - - - -```typescript TypeScript -const session = await client.browser.session.create({ - country: "US", - type: "hosted", - windowSize: "1920x1080", -}); - -const browser = await chromium.connectOverCDP(session.cdpUrl); -``` - -```python Python -resp = requests.post( - "https://api.driver.dev/v1/browser/session", - headers={ - "Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}", - "Content-Type": "application/json", - }, - json={ - "country": "US", - "type": "hosted", - "windowSize": "1920x1080" - } -) -session = resp.json() - -browser = p.chromium.connect_over_cdp(session["cdpUrl"]) -``` - - - -## Using Existing Contexts - -Driver sessions may have a pre-existing browser context. Always check before creating a new one: - - - -```typescript TypeScript -const browser = await chromium.connectOverCDP(session.cdpUrl); - -// Use existing context if available -const context = browser.contexts()[0] || (await browser.newContext()); -const page = context.pages()[0] || (await context.newPage()); -``` - -```python Python -browser = p.chromium.connect_over_cdp(session["cdpUrl"]) - -# Use existing context if available -context = browser.contexts[0] if browser.contexts else browser.new_context() -page = context.pages[0] if context.pages else context.new_page() -``` - - - -## With Persistent Profiles - - - -```typescript TypeScript -// First run — login and save profile -const session1 = await client.browser.session.create({ - type: "hosted", - profile: { name: "my-account", persist: true }, -}); - -const browser1 = await chromium.connectOverCDP(session1.cdpUrl); -const page1 = await browser1.newPage(); - -// Login flow... -await page1.goto("https://example.com/login"); -await page1.fill("#email", "user@example.com"); -await page1.fill("#password", "password"); -await page1.click('button[type="submit"]'); - -await browser1.close(); -await client.browser.session.stop({ sessionId: session1.sessionId }); - -// Later — session starts already logged in -const session2 = await client.browser.session.create({ - type: "hosted", - profile: { name: "my-account", persist: true }, -}); -``` - -```python Python -import os -import requests -from playwright.sync_api import sync_playwright - -headers = { - "Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}", - "Content-Type": "application/json", -} - -# First run — login and save profile -resp1 = requests.post( - "https://api.driver.dev/v1/browser/session", - headers=headers, - json={"type": "hosted", "profile": {"name": "my-account", "persist": True}} -) -session1 = resp1.json() - -with sync_playwright() as p: - browser1 = p.chromium.connect_over_cdp(session1["cdpUrl"]) - page1 = browser1.new_page() - - # Login flow... - page1.goto("https://example.com/login") - page1.fill("#email", "user@example.com") - page1.fill("#password", "password") - page1.click('button[type="submit"]') - - browser1.close() - -# Stop session1 -requests.delete( - "https://api.driver.dev/v1/browser/session", - headers={"Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}"}, - params={"sessionId": session1["sessionId"]} -) - -# Later — session starts already logged in -resp2 = requests.post( - "https://api.driver.dev/v1/browser/session", - headers=headers, - json={"type": "hosted", "profile": {"name": "my-account", "persist": True}} -) -session2 = resp2.json() -``` - - - -## Error Handling - - - -```typescript TypeScript -try { - const browser = await chromium.connectOverCDP(session.cdpUrl); - // ... automation -} catch (error) { - if (error.message.includes("Target closed")) { - // Session may have timed out - console.log("Session closed, creating new one..."); - } - throw error; -} finally { - await client.browser.session.stop({ sessionId: session.sessionId }); -} -``` - -```python Python -try: - browser = p.chromium.connect_over_cdp(session["cdpUrl"]) - # ... automation -except Exception as e: - if "Target closed" in str(e): - # Session may have timed out - print("Session closed, creating new one...") - raise -finally: - requests.delete( - "https://api.driver.dev/v1/browser/session", - headers={"Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}"}, - params={"sessionId": session["sessionId"]} - ) -``` - - - -## Tips - -- Use `page.waitForLoadState('networkidle')` for pages with dynamic content -- Driver sessions support all Playwright features including screenshots, PDFs, and tracing -- For long-running tasks, monitor session status and handle reconnection -- Use `browser.close()` to end your Playwright connection. Unlike Puppeteer's `browser.disconnect()`, this properly closes the CDP connection while the Driver session can still be stopped separately via the API diff --git a/docs/integrations/puppeteer.mdx b/docs/integrations/puppeteer.mdx deleted file mode 100644 index 3fc2566..0000000 --- a/docs/integrations/puppeteer.mdx +++ /dev/null @@ -1,118 +0,0 @@ ---- -title: "Puppeteer" -description: "Connect Driver sessions to Puppeteer" ---- - -[Puppeteer](https://pptr.dev) is Google's Node.js library for controlling Chrome/Chromium via the DevTools Protocol. - -## Installation - -```bash -npm install puppeteer-core @browsercash/sdk -``` - - -Use `puppeteer-core` instead of `puppeteer` — Driver provides the browser, so you don't need Puppeteer to download one. - - -## Quick Start - -```typescript TypeScript -import puppeteer from 'puppeteer-core'; -import BrowsercashSDK from '@browsercash/sdk'; - -async function run() { - const client = new BrowsercashSDK({ - apiKey: process.env.DRIVER_API_KEY!, - baseURL: "https://api.driver.dev", - }); - - // Create a Driver session - const session = await client.browser.session.create({ type: 'hosted' }); - - // Connect Puppeteer via CDP - const browser = await puppeteer.connect({ - browserWSEndpoint: session.cdpUrl, - }); - - // Automate - const page = await browser.newPage(); - await page.goto('https://example.com'); - console.log(await page.title()); - - // Cleanup - await browser.disconnect(); - await client.browser.session.stop({ sessionId: session.sessionId }); -} - -run().catch(console.error); -``` - -## With Session Options - -```typescript TypeScript -const session = await client.browser.session.create({ - country: 'GB', - type: 'hosted', - windowSize: '1366x768', -}); - -const browser = await puppeteer.connect({ - browserWSEndpoint: session.cdpUrl, -}); -``` - -## Taking Screenshots - -```typescript TypeScript -const page = await browser.newPage(); -await page.goto('https://example.com'); -await page.screenshot({ path: 'screenshot.png', fullPage: true }); -``` - -## Generating PDFs - -```typescript TypeScript -const page = await browser.newPage(); -await page.goto('https://example.com'); -await page.pdf({ path: 'page.pdf', format: 'A4' }); -``` - -## With Persistent Profiles - -```typescript TypeScript -// Create session with profile -const session = await client.browser.session.create({ - type: 'hosted', - profile: { name: 'logged-in-user', persist: true }, -}); - -const browser = await puppeteer.connect({ - browserWSEndpoint: session.cdpUrl, -}); - -// Cookies and localStorage persist across sessions -``` - -## Error Handling - -```typescript TypeScript -try { - const browser = await puppeteer.connect({ - browserWSEndpoint: session.cdpUrl, - }); - // ... automation -} catch (error) { - if (error.message.includes('Connection closed')) { - console.log('Session disconnected'); - } - throw error; -} finally { - await client.browser.session.stop({ sessionId: session.sessionId }); -} -``` - -## Tips - -- Use `browser.disconnect()` instead of `browser.close()` to keep the remote session running -- Use `hosted` sessions for predictable managed Chrome infrastructure diff --git a/docs/integrations/stagehand.mdx b/docs/integrations/stagehand.mdx deleted file mode 100644 index 144ebeb..0000000 --- a/docs/integrations/stagehand.mdx +++ /dev/null @@ -1,147 +0,0 @@ ---- -title: "Stagehand" -description: "Build AI-native browser automations with Stagehand" ---- - -[Stagehand](https://github.com/browserbase/stagehand) is an AI-native browser automation framework that uses natural language to interact with web pages. - -## Installation - -```bash -npm install @browserbasehq/stagehand @browsercash/sdk zod -``` - - - CDP URL attachment requires Stagehand 2.0 or newer. In `LOCAL` mode, Stagehand also needs an API key for your chosen model provider. - - -```bash -DRIVER_API_KEY=your-driver-key -OPENAI_API_KEY=your-openai-key -``` - -## Quick Start - -```typescript TypeScript -import { Stagehand } from '@browserbasehq/stagehand'; -import BrowsercashSDK from '@browsercash/sdk'; -import { z } from 'zod'; - -async function run() { - const client = new BrowsercashSDK({ - apiKey: process.env.DRIVER_API_KEY!, - baseURL: "https://api.driver.dev", - }); - - // Create Driver session - const session = await client.browser.session.create({ type: 'hosted' }); - - // Initialize Stagehand with Driver CDP URL - const stagehand = new Stagehand({ - env: 'LOCAL', - model: 'openai/gpt-5', - localBrowserLaunchOptions: { - cdpUrl: session.cdpUrl, - }, - }); - - await stagehand.init(); - - const page = stagehand.context.pages()[0] || await stagehand.context.newPage(); - await page.goto('https://example.com'); - - // Extract data using natural language - const data = await stagehand.extract( - 'Extract the page title and main heading', - z.object({ - title: z.string(), - heading: z.string(), - }) - ); - - console.log(data); - - // Cleanup - await stagehand.close(); - await client.browser.session.stop({ sessionId: session.sessionId }); -} - -run().catch(console.error); -``` - -## Key Features - -### Natural Language Actions - -```typescript TypeScript -// Click elements -await stagehand.act('click the sign up button'); - -// Fill forms -await stagehand.act('enter "John Doe" in the name field'); - -// Navigate -await stagehand.act('scroll down to the pricing section'); -``` - -### Structured Data Extraction - -```typescript TypeScript -const products = await stagehand.extract( - 'Extract all products with their prices', - z.array(z.object({ - name: z.string(), - price: z.number(), - inStock: z.boolean(), - })) -); -``` - -### Observe Page State - -```typescript TypeScript -const observations = await stagehand.observe('What actions can I take on this page?'); -``` - -## With Session Options - -```typescript TypeScript -const session = await client.browser.session.create({ - country: 'US', - type: 'hosted', - windowSize: '1920x1080', - profile: { name: 'stagehand-session', persist: true }, -}); -``` - -## Complex Workflows - -```typescript TypeScript -// Multi-step workflow with error handling -async function checkoutFlow(stagehand: Stagehand) { - const page = stagehand.context.pages()[0] || await stagehand.context.newPage(); - await page.goto('https://shop.example.com'); - - await stagehand.act('search for "laptop"'); - await stagehand.act('click on the first product'); - await stagehand.act('add to cart'); - await stagehand.act('go to checkout'); - - const cartSummary = await stagehand.extract( - 'Extract the cart total and item count', - z.object({ - total: z.string(), - itemCount: z.number(), - }) - ); - - return cartSummary; -} -``` - -## Why Stagehand + Driver? - -- **AI-native** — Use natural language instead of brittle selectors -- **Self-healing** — Adapts to UI changes automatically -- **Hosted browsers** — Run Stagehand on managed Chrome sessions -- **Scalable** — No local browser management needed diff --git a/docs/introduction/getting-started.mdx b/docs/introduction/getting-started.mdx deleted file mode 100644 index cb77630..0000000 --- a/docs/introduction/getting-started.mdx +++ /dev/null @@ -1,62 +0,0 @@ ---- -title: "Getting Started" -description: "Create and stop your first Driver browser session." ---- - -## Prerequisites - -- A Driver account from the [dashboard](https://app.driver.dev) -- A Driver API key - -## Use cURL - -```bash -export DRIVER_API_KEY="dr_..." - -curl -X POST "https://api.driver.dev/v1/browser/session" \ - -H "Authorization: Bearer $DRIVER_API_KEY" \ - -H "Content-Type: application/json" \ - -d '{"type":"hosted"}' -``` - -The response includes a `sessionId` and `cdpUrl`. - -```bash -curl -X DELETE "https://api.driver.dev/v1/browser/session?sessionId=$SESSION_ID" \ - -H "Authorization: Bearer $DRIVER_API_KEY" -``` - -## Use the TypeScript SDK - -The current npm package is named `@browsercash/sdk`. - -```bash -npm install @browsercash/sdk playwright -``` - -```typescript -import { chromium } from "playwright"; -import BrowsercashSDK from "@browsercash/sdk"; - -const client = new BrowsercashSDK({ - apiKey: process.env.DRIVER_API_KEY!, - baseURL: "https://api.driver.dev", -}); - -const session = await client.browser.session.create({ type: "hosted" }); -const browser = await chromium.connectOverCDP(session.cdpUrl); - -const context = browser.contexts()[0] || (await browser.newContext()); -const page = context.pages()[0] || (await context.newPage()); -await page.goto("https://example.com"); -console.log(await page.title()); - -await browser.close(); -await client.browser.session.stop({ sessionId: session.sessionId }); -``` - -## Next Steps - -- [Create a Browser Session](/docs/fundamentals/creating-a-browser-session) -- [Use a Browser Session](/docs/fundamentals/using-a-browser-session) -- [Open the API Reference](/api-reference) diff --git a/docs/options/browser-types.mdx b/docs/options/browser-types.mdx new file mode 100644 index 0000000..9356789 --- /dev/null +++ b/docs/options/browser-types.mdx @@ -0,0 +1,37 @@ +--- +title: "Browser types" +description: "The type field picks the browser class: hosted by default, with two stealth variants." +--- + +`type` on [create](/docs/sessions/create) chooses what kind of Chrome serves the session. Leave it out unless a site gives you a reason to change it. + +| Type | What it is | When to use it | +|---|---|---| +| `hosted` (default) | Real Chrome on a hosted machine, native behaviour. | Almost everything. | +| `hosted_stealth` | Same browser with a conservative stealth policy. | Sites that challenge the default and you want the least invasive change. | +| `hosted_privacy` | Same browser with the aggressive stealth policy. | Hostile targets. Try `hosted_stealth` first; more masking is not always better. Both stealth types were verified to start normally. | + + +```json Request +{ "type": "hosted_stealth", "country": "GB" } +``` +```typescript TypeScript +const session = await createSession({ type: "hosted_stealth", country: "GB" }); +``` +```python Python +session = create_session(type="hosted_stealth", country="GB") +``` + + +## What "real Chrome" means here + +Hosted sessions run consumer Chrome builds on real hardware with a real display, not a headless container. Fingerprint surfaces (GPU, fonts, media devices, timing) are what a person's machine would show, and the network is residential in the country you asked for. That is why the [best practices](/docs/start/best-practices) tell you not to patch fingerprints from your script: the browser is already genuine, and patches make it less so. + +## Choosing between the stealth policies + +The stealth policies change how the browser answers certain probes. `hosted_stealth` is the conservative set; it rarely breaks sites. `hosted_privacy` masks more and can change page behaviour on sites that rely on the masked APIs. Measure against your target rather than defaulting to the strongest setting. + +## Related + +- [Location](/docs/options/location) for country, timezone and language coherence +- [Proxies](/docs/options/proxies) when you need your own egress diff --git a/docs/options/captcha-and-adblock.mdx b/docs/options/captcha-and-adblock.mdx new file mode 100644 index 0000000..17635df --- /dev/null +++ b/docs/options/captcha-and-adblock.mdx @@ -0,0 +1,40 @@ +--- +title: "CAPTCHA solving and ad blocking" +description: "Two flags on create: captchaSolver loads Driver's solver into the session, adblock blocks ads." +--- + +## CAPTCHA solving + +Set `captchaSolver: true` and the session's Chrome loads Driver's solver extension. Challenges that appear during the session are solved in the browser without any change to your script. + + +```json Request +{ "captchaSolver": true } +``` +```typescript TypeScript +const session = await createSession({ captchaSolver: true }); +``` +```python Python +session = create_session(captchaSolver=True) +``` + + +Solving takes time; give steps that may hit a challenge a generous timeout and rely on your framework's auto-waiting rather than fixed sleeps. If a challenge is not solved, the [live view](/docs/sessions/live-view) lets a person complete it in the same session. + +## Ad blocking + +Set `adblock: true` to block ads in the session. Pages load faster and use less bandwidth, which is what a session is billed on. Some sites detect ad blockers and change behaviour, so leave it off when fidelity matters. + + +```json Request +{ "adblock": true } +``` +```typescript TypeScript +const session = await createSession({ adblock: true }); +``` +```python Python +session = create_session(adblock=True) +``` + + +Both flags default to `false`. diff --git a/docs/options/extensions.mdx b/docs/options/extensions.mdx new file mode 100644 index 0000000..18ebef5 --- /dev/null +++ b/docs/options/extensions.mdx @@ -0,0 +1,64 @@ +--- +title: "Extensions" +description: "Upload your own Chrome extensions and load them into hosted sessions. Available to accounts with extension access." +--- + + +The extensions API is enabled per account. Without access, the endpoints return `403` (`Extensions API is not enabled for this account`); write to support@driver.dev to request it. An upload without a file is rejected first with `No file uploaded. Send as multipart field "extension".` + + +## Upload an extension + +Zip the extension's directory (the one containing `manifest.json`) and upload it. Driver packs it as a CRX and returns an `extensionId` to use on create. + + +```bash cURL +curl -X POST https://api.driver.dev/v1/browser/extensions \ + -H "Authorization: Bearer $DRIVER_API_KEY" \ + -F "extension=@my-extension.zip" +# → { "extension": { "extensionId": "abc123def456", "crxExtensionId": "…", +# "name": "…", "version": "1.2.0", … } } +``` +```typescript TypeScript +const form = new FormData(); +const zip = new Blob([await fs.promises.readFile("my-extension.zip")]); +form.append("extension", zip, "my-extension.zip"); +const { extension } = await fetch("https://api.driver.dev/v1/browser/extensions", { + method: "POST", + headers: { Authorization: `Bearer ${process.env.DRIVER_API_KEY}` }, + body: form, +}).then((r) => r.json()); +``` +```python Python +with open("my-extension.zip", "rb") as f: + extension = requests.post( + "https://api.driver.dev/v1/browser/extensions", + headers={"Authorization": f"Bearer {os.environ['DRIVER_API_KEY']}"}, + files={"extension": f}, + ).json()["extension"] +``` + + +## Load it into a session + +Pass the internal `extensionId` (not the `crxExtensionId`) in `extensionIds`. Works on every browser type. + +```json +{ "extensionIds": ["abc123def456"] } +``` + +## Manage extensions + +| Call | What it does | +|---|---| +| `GET /v1/browser/extensions` | List your extensions. | +| `GET /v1/browser/extensions/{extensionId}` | One extension's details. | +| `PUT /v1/browser/extensions/{extensionId}` with a new zip | Replace the code; the id stays the same. | +| `DELETE /v1/browser/extensions/{extensionId}` | Remove it. | + +The dashboard's **Extensions** page (visible once your account has access) offers the same upload and list. + +## Notes + +- Manifest V3 extensions are expected. `name` and `version` in the response come from the manifest. +- An extension that opens tabs, injects scripts everywhere or rewrites requests changes how the browser looks to sites. Keep them narrow, for the same reasons as the [CDP hooks guidance](/docs/start/best-practices#keep-cdp-hooks-narrow). diff --git a/docs/options/location.mdx b/docs/options/location.mdx new file mode 100644 index 0000000..e24e456 --- /dev/null +++ b/docs/options/location.mdx @@ -0,0 +1,45 @@ +--- +title: "Location, timezone and language" +description: "Ask for a country and Driver makes the network, timezone and language agree. Override timezone and language only within that country." +--- + +Pass a two-letter `country` and Driver places the session on a machine egressing from that country, then picks a timezone and browser language that belong there. The three always agree, which is what sites check. + + +```json Request +{ "country": "DE" } +``` +```typescript TypeScript +const session = await createSession({ country: "DE" }); +``` +```python Python +session = create_session(country="DE") +``` + + +`country` defaults to `US`. + +## Overriding timezone and language + +Set `timezone` (IANA name) or `language` (BCP-47) when the default choice for the country is not what you want. Both must be valid for the chosen country, or the request is rejected with `400`. + +```json +{ "country": "CA", "timezone": "America/Vancouver", "language": "fr-CA" } +``` + +US sessions accept only `en-US`, and omit both language fields so Chrome uses its built-in default. For other countries, Driver picks the likely primary language when you leave `language` out. Chrome reports the nearest variant it ships: a session created with `fr-CA` reported `navigator.language` as `fr-FR` with `fr` and `en-US` as fallbacks, and the requested timezone exactly. + +Hosted sessions also provision matching text-to-speech voices where available, and disable Web Speech when a coherent voice is not, so that surface never contradicts the rest. + +## With your own proxy + +If you supply a `proxyUrl` without `timezone` or `language`, Driver geolocates the proxy's exit and derives the country and timezone from it. A `country` you supply must match that exit. See [Proxies](/docs/options/proxies). + +## Availability + +Placement is by available machines. If nothing is available in the requested country you get a `503`; back off and retry, or pick another country. + +## Related + +- [Browser types](/docs/options/browser-types) +- [Best practices](/docs/start/best-practices): do not override timezone or locale from your script diff --git a/docs/options/node-reuse.mdx b/docs/options/node-reuse.mdx new file mode 100644 index 0000000..8ebc39b --- /dev/null +++ b/docs/options/node-reuse.mdx @@ -0,0 +1,31 @@ +--- +title: "Node reuse" +description: "nodeId runs a session on the machine that served an earlier one. Rarely needed; overrides country and type." +--- + +Every session response includes `servedBy`, the id of the machine that ran it. Pass that value as `nodeId` on a later create to land on the same machine again. + + +```typescript TypeScript +const first = await createSession({ country: "US" }); +// … work, stop … +const again = await createSession({ nodeId: first.servedBy }); +``` +```python Python +first = create_session(country="US") +# ... work, stop ... +again = create_session(nodeId=first["servedBy"]) +``` + + +## When it helps + +- A site keyed something to the machine (rare; most keying is by cookies, which [profiles](/docs/options/profiles) handle better). +- Reproducing a problem that only happens on one machine. + +## Rules + +- `nodeId` takes precedence over `country` and `type`. You get that machine, wherever it is. +- If the machine is busy or gone, the request fails instead of falling back to another machine. + +For most workloads, leave `nodeId` out and let placement pick a machine in the country you asked for. diff --git a/docs/options/profiles.mdx b/docs/options/profiles.mdx new file mode 100644 index 0000000..0f83a7d --- /dev/null +++ b/docs/options/profiles.mdx @@ -0,0 +1,66 @@ +--- +title: "Profiles" +description: "Save a session's cookies and browser state under a name, and start later sessions already signed in." +--- + +A profile is a full browser profile stored under a name you choose. Start a session with `profile: { name, persist: true }`, sign in, stop the session: the state is saved. Start another session with the same name and it opens signed in. + + +```json Request +{ "profile": { "name": "shop-account", "persist": true } } +``` +```typescript TypeScript +// First run: sign in and save. +const first = await createSession({ profile: { name: "shop-account", persist: true } }); +// … sign in through the browser, then stop the session … + +// Later: the same name, already signed in. +const next = await createSession({ profile: { name: "shop-account", persist: true } }); +``` +```python Python +first = create_session(profile={"name": "shop-account", "persist": True}) +# ... sign in, stop ... +next_session = create_session(profile={"name": "shop-account", "persist": True}) +``` + + +| Field | Type | Meaning | +|---|---|---| +| `name` | string | The profile to load. Created on first use. | +| `persist` | boolean | Save the profile when the session ends. `false` loads it read-only: changes made during the session are discarded. | + +Profiles are supported on every browser type. + +## Listing and deleting + + +```bash cURL +curl https://api.driver.dev/v1/browser/profiles -H "Authorization: Bearer $DRIVER_API_KEY" +# → { "profiles": [ { "profileId": "…", "name": "shop-account", "createdAt": "…" } ] } + +curl -X DELETE "https://api.driver.dev/v1/browser/profile?profileName=shop-account" \ + -H "Authorization: Bearer $DRIVER_API_KEY" +``` +```typescript TypeScript +const { profiles } = await fetch("https://api.driver.dev/v1/browser/profiles", { headers }) + .then((r) => r.json()); +await fetch("https://api.driver.dev/v1/browser/profile?profileName=shop-account", { + method: "DELETE", + headers, +}); +``` +```python Python +profiles = requests.get("https://api.driver.dev/v1/browser/profiles", headers=headers).json() +requests.delete( + "https://api.driver.dev/v1/browser/profile", headers=headers, params={"profileName": "shop-account"} +) +``` + + +Deleting is idempotent: an unknown name also answers `{ "success": true }`. The dashboard's **Profiles** page shows the same list, and lets you launch a session on a profile to sign in by hand through the live view. + +## Practical notes + +- One profile, one session at a time. Two concurrent sessions on the same persisted profile will overwrite each other's state when they stop. +- Keep the same `country` for a profile across runs. A profile that signed in from Germany and returns from Brazil looks like a stolen session to the site. +- Profiles hold everything a browser profile holds: cookies, local storage, IndexedDB, service workers. Delete a profile when the account it belongs to is no longer needed. diff --git a/docs/options/proxies.mdx b/docs/options/proxies.mdx new file mode 100644 index 0000000..f70e83b --- /dev/null +++ b/docs/options/proxies.mdx @@ -0,0 +1,42 @@ +--- +title: "Proxies" +description: "Sessions egress from Driver's residential network by default. Pass proxyUrl to route through your own SOCKS5 or TCPerfect endpoint." +--- + +By default a session's traffic leaves from a residential connection in the requested country, and you do not configure anything. Set `proxyUrl` when you need a specific egress: your own proxy provider, a sticky IP, or a TCPerfect address. + + +```json Request +{ "proxyUrl": "socks5://user:pass@proxy.example.com:1080" } +``` +```typescript TypeScript +const session = await createSession({ proxyUrl: "socks5://user:pass@proxy.example.com:1080" }); +``` +```python Python +session = create_session(proxyUrl="socks5://user:pass@proxy.example.com:1080") +``` + + +## Accepted URLs + +| Scheme | Notes | +|---|---| +| `socks5://user:pass@host:port` | DNS resolved on the session's machine. | +| `socks5h://user:pass@host:port` | DNS resolved through the proxy. Use this when the proxy's location should also decide DNS answers. | +| `tcperfect://user:pass@host:port/exit-ip` | Driver's TCPerfect network: a proxied connection that keeps the machine's own TCP fingerprint intact. | + +The proxy must be reachable from Driver's machines. HTTP proxies are not supported. + +## Location coherence + +When you pass a SOCKS URL without `timezone` or `language`, Driver geolocates the proxy's exit before launch and derives the session's country and timezone from it, so the browser does not claim New York while its packets come from Frankfurt. If you also pass `country`, it must match the exit or the request is rejected with `400`. + +## Canada + +Hosted sessions in Canada that do not specify a proxy egress through Driver's TCPerfect inventory automatically; nothing to configure. + +## If the proxy cannot be reached + +Driver connects through the proxy before launch to find its exit. A proxy that does not answer is rejected with `400` and the message `Could not determine SOCKS5 proxy egress location; verify the proxy or specify both timezone and language.` Supplying both `timezone` and `language` skips the lookup; the session then starts even if the proxy is unreachable, and pages fail to load inside it. An `http://` URL is rejected outright. + +Credentials travel in the create request only; they are not stored on the session record. diff --git a/docs/options/window-size.mdx b/docs/options/window-size.mdx new file mode 100644 index 0000000..29daad1 --- /dev/null +++ b/docs/options/window-size.mdx @@ -0,0 +1,39 @@ +--- +title: "Window and display size" +description: "windowSize sets the outer browser window; displaySize sets the virtual display behind it." +--- + +Two fields, both `WIDTHxHEIGHT`, width 320 to 16384 and height 200 to 16384. + +| Field | What it sets | Notes | +|---|---|---| +| `windowSize` | The outer Chrome window, as the page will see it through `window.outerWidth/Height`. | The default depends on the machine. | +| `displaySize` | The virtual display the window sits on. | Usually leave it alone; set it when a site needs a screen larger than the window. | + + +```json Request +{ "windowSize": "1920x1040", "displaySize": "1920x1080" } +``` +```typescript TypeScript +const session = await createSession({ windowSize: "1920x1040" }); +``` +```python Python +session = create_session(windowSize="1920x1040") +``` + + +## Sizes people actually use + +| `windowSize` | Looks like | +|---|---| +| `1920x1040` | Full HD desktop with a taskbar | +| `1536x864` | Scaled 1080p laptop | +| `1440x900` | 13-inch MacBook | +| `1366x768` | Common budget laptop | +| `390x844` | Phone-sized window (the browser is still desktop Chrome) | + +Pick a size that fits the screen the session's machine would plausibly have. A `windowSize` larger than the display is clamped. + +## Resizing after connecting + +Prefer setting the size on create. Changing the viewport from your framework (`page.setViewportSize`) resizes only the content area and leaves `outerWidth` where it was, which is one of the mismatches sites look for. diff --git a/docs/sessions/connect.mdx b/docs/sessions/connect.mdx new file mode 100644 index 0000000..fc25f39 --- /dev/null +++ b/docs/sessions/connect.mdx @@ -0,0 +1,71 @@ +--- +title: "Connect over CDP" +description: "Attach your automation framework to a running Driver session through its cdpUrl." +--- + +`cdpUrl` is a WebSocket endpoint to the session's Chrome. Any client that speaks the Chrome DevTools Protocol can attach to it. You attach to a running browser; you never launch one. + + +Use [Patchright](/docs/frameworks/patchright) rather than stock Playwright or Puppeteer. Same API, minus the CDP calls that anti-bot systems fingerprint. Sessions driven by stock frameworks get blocked on sites that look for automation. + + +## Patchright (recommended) + + +```typescript TypeScript +import { chromium } from "patchright"; + +const browser = await chromium.connectOverCDP(session.cdpUrl); +const context = browser.contexts()[0] ?? (await browser.newContext()); +const page = context.pages()[0] ?? (await context.newPage()); + +await page.goto("https://example.com"); +console.log(await page.title()); + +await browser.close(); // closes your connection; the session keeps running until you stop it +``` +```python Python +from patchright.sync_api import sync_playwright + +with sync_playwright() as p: + browser = p.chromium.connect_over_cdp(session["cdpUrl"]) + context = browser.contexts[0] if browser.contexts else browser.new_context() + page = context.pages[0] if context.pages else context.new_page() + + page.goto("https://example.com") + print(page.title()) + browser.close() +``` + + +## Other clients + + + Works, with the caveats above. + `puppeteer-core` with `browserWSEndpoint`. + Agents on a Driver browser. + Natural-language actions over CDP. + + +## Reuse the context and tab + +The browser opens with one context and one tab. Reuse them. A session that opens fresh incognito contexts and blank pages behaves differently from a person's Chrome, and sites notice. + +```typescript +const context = browser.contexts()[0] ?? (await browser.newContext()); +const page = context.pages()[0] ?? (await context.newPage()); +``` + +If your task genuinely needs several tabs, open them from the existing context with `context.newPage()`. + +## Disconnecting versus stopping + +Closing your client (`browser.close()` in Patchright and Playwright, `browser.disconnect()` in Puppeteer) ends your connection. The session keeps running, and billing continues, until you call [`DELETE /v1/browser/session`](/docs/sessions/lifecycle#stop-a-session) or the session reaches its `duration`. You can disconnect and reconnect to the same `cdpUrl` as often as you like while the session is active. + +## Reconnecting + +If your process crashes, fetch the session with [`GET /v1/browser/session`](/docs/sessions/lifecycle#get-a-session): while `status` is `active` the `cdpUrl` is still valid and you can attach again. Profiles with `persist: true` also carry state into a brand-new session if the old one has ended. + +## Timeouts and waits + +Driver browsers are real machines on real networks, so page loads take real-world time. Rely on your framework's auto-waiting (`page.click`, `locator.waitFor`) rather than fixed sleeps, and raise the navigation timeout for slow sites instead of retrying in a loop. diff --git a/docs/sessions/create.mdx b/docs/sessions/create.mdx new file mode 100644 index 0000000..5f88751 --- /dev/null +++ b/docs/sessions/create.mdx @@ -0,0 +1,115 @@ +--- +title: "Create a session" +description: "POST /v1/browser/session starts a real Chrome and returns its CDP URL once it is reachable." +--- + +A session is one browser on one machine for up to an hour. Create it with the options you need; every field is optional and the defaults give you a hosted Chrome in the US. + + +```bash cURL +curl -X POST https://api.driver.dev/v1/browser/session \ + -H "Authorization: Bearer $DRIVER_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "country": "US", + "duration": 600, + "windowSize": "1920x1080", + "profile": { "name": "my-profile", "persist": true }, + "note": "price check, client X" + }' +``` +```typescript TypeScript +const session = await fetch("https://api.driver.dev/v1/browser/session", { + method: "POST", + headers: { + Authorization: `Bearer ${process.env.DRIVER_API_KEY}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + country: "US", + duration: 600, + windowSize: "1920x1080", + profile: { name: "my-profile", persist: true }, + note: "price check, client X", + }), +}).then((r) => r.json()); +``` +```python Python +session = requests.post( + "https://api.driver.dev/v1/browser/session", + headers={"Authorization": f"Bearer {os.environ['DRIVER_API_KEY']}"}, + json={ + "country": "US", + "duration": 600, + "windowSize": "1920x1080", + "profile": {"name": "my-profile", "persist": True}, + "note": "price check, client X", + }, +).json() +``` +```typescript TypeScript SDK +import BrowsercashSDK from "@browsercash/sdk"; + +const client = new BrowsercashSDK({ + apiKey: process.env.DRIVER_API_KEY!, + baseURL: "https://api.driver.dev", +}); +const session = await client.browser.session.create({ country: "US", duration: 600 }); +``` + + +## Response + +```json +{ + "sessionId": "0b1f5c1e6d3a4f7c9e2b8d4a6c1e3f5a", + "status": "active", + "servedBy": "adapt-ensure-swim", + "createdAt": "2026-09-17T14:02:11.000Z", + "stoppedAt": null, + "cdpUrl": "wss://…", + "note": "price check, client X" +} +``` + +The call waits until Chrome is reachable, so `cdpUrl` is normally set in the response and `status` is `active`; in our runs that took two to three seconds. The browser opens on `about:blank` (or the `url` you passed) with one context and one tab. If you ever see `starting`, poll [`GET /v1/browser/session`](/docs/sessions/lifecycle#get-a-session) until `cdpUrl` appears. + +## Options + +| Field | Type | Default | What it does | +|---|---|---|---| +| `type` | string | `hosted` | Browser class: `hosted`, `hosted_stealth` or `hosted_privacy`. See [Browser types](/docs/options/browser-types). | +| `country` | string | `US` | Two-letter country for placement, network and locale. See [Location](/docs/options/location). | +| `timezone` | string | chosen for the country | IANA timezone, must belong to the country. | +| `language` | string | chosen for the country | BCP-47 browser language, must belong to the country. | +| `duration` | integer | `3600` | Seconds before the session stops on its own, 60 to 3600. | +| `url` | string | | A URL to open right after launch. | +| `windowSize` | string | node default | Outer window size as `WIDTHxHEIGHT`. See [Window size](/docs/options/window-size). | +| `displaySize` | string | | Virtual display size as `WIDTHxHEIGHT`. | +| `proxyUrl` | string | Driver's network | Your own `socks5://`, `socks5h://` or `tcperfect://` URL. See [Proxies](/docs/options/proxies). | +| `profile` | object | | `{ "name": "…", "persist": true }` to load and save browser state. See [Profiles](/docs/options/profiles). | +| `captchaSolver` | boolean | `false` | Load Driver's CAPTCHA solver into the session. | +| `adblock` | boolean | `false` | Block ads in the session. | +| `extensionIds` | string[] | | Uploaded extension ids to load. Requires extension access. See [Extensions](/docs/options/extensions). | +| `nodeId` | string | | Run on the machine that served an earlier session. Overrides `country` and `type`. See [Node reuse](/docs/options/node-reuse). | +| `browserCheck` | boolean | `false` | Run a browser validation check during creation. | +| `note` | string | | Up to 256 characters, shown in the dashboard. | + +Options that contradict each other are rejected with `400` and a message naming the field. The common ones: a `timezone` or `language` not listed for the `country` (`Unsupported timezone "Europe/Berlin" for country US.`), a `country` that does not match a supplied proxy's exit, a proxy that cannot be reached, and out-of-range `duration`, `windowSize` or `note`. + +## Errors + +| Status | Meaning | +|---|---| +| `400` | Invalid or incompatible options. The message names the field. | +| `401` | Missing or invalid API key. | +| `402` | The workspace is out of credits. | +| `403` | Session or account limit reached (concurrency, or a feature your plan does not include). | +| `429` | Too many requests. | +| `503` | No browser capacity available right now. | +| `504` | A machine was selected but Chrome was not reachable before the startup deadline. | + +## Next + +- [Connect over CDP](/docs/sessions/connect) +- [Lifecycle](/docs/sessions/lifecycle) to get, list, label and stop sessions diff --git a/docs/sessions/errors.mdx b/docs/sessions/errors.mdx new file mode 100644 index 0000000..6fd90dc --- /dev/null +++ b/docs/sessions/errors.mdx @@ -0,0 +1,41 @@ +--- +title: "Errors" +description: "Every status the API returns, what it means, and whether retrying helps." +--- + +Errors are JSON with an `error` message. Some carry a machine-readable `code`. + +```json +{ "error": "Insufficient balance" } +``` + +## Status codes + +| Status | Meaning | Retry? | +|---|---|---| +| `400` | Invalid or incompatible options. The message names the field. | No. Fix the request. | +| `401` | Missing or invalid API key. | No. Check `Authorization: Bearer …`. | +| `402` | The workspace is out of credits. | No. Add credits in the dashboard. | +| `403` | Session or account limit reached: the workspace's concurrent-browser limit (`plan.concurrent_browsers` on `GET /v1/account/billing`), or a feature (extensions) the account is not enabled for. | Only after a session ends. | +| `404` | Session, profile or extension not found (unknown id, or one belonging to another workspace). | No. | +| `429` | Too many requests. | Yes, with backoff. | +| `500` | Something failed on Driver's side. | Once. | +| `503` | No browser capacity available right now, or the session service is unavailable. | Yes, with backoff and jitter, or tell the user. | +| `504` | A machine was selected but Chrome did not become reachable before the startup deadline. | Once. | + +## Guidance for agents and automations + +- Do not retry `402` or `403` in a loop. Surface the message; a person has to act. +- Treat `503` and `504` as transient: back off (a few seconds, growing), cap the attempts, then report. +- After a `504`, the session is not running; no stop call is needed. +- `DELETE` on a session that already ended still returns `{ "success": true }`; a `404` means the id was never a session of this workspace. + +## Framework-side failures + +Some failures show up in your framework rather than as an HTTP status: + +| Symptom | Likely cause | +|---|---| +| `Target closed` / `Browser closed` mid-run | The session reached its `duration`, or was stopped from the dashboard. Check `GET /v1/browser/session`. | +| Connection refused on `cdpUrl` | The session is not `active` yet or has ended. | +| The site shows a challenge or block page | Framework fingerprint. Switch to [Patchright](/docs/frameworks/patchright) and remove CDP hooks; see [Best practices](/docs/start/best-practices). | diff --git a/docs/sessions/lifecycle.mdx b/docs/sessions/lifecycle.mdx new file mode 100644 index 0000000..c2d9deb --- /dev/null +++ b/docs/sessions/lifecycle.mdx @@ -0,0 +1,148 @@ +--- +title: "Lifecycle" +description: "Get, list, label and stop sessions. What each status means and how long a session lives." +--- + +A session moves through `starting` → `active` → `completed`, or `error` if the browser failed. It ends when you stop it, when it reaches its `duration`, or when the machine serving it goes away. + +| Status | Meaning | +|---|---| +| `starting` | The machine is launching Chrome. `cdpUrl` is `null`. | +| `active` | Chrome is reachable at `cdpUrl`. | +| `completed` | The session was stopped or reached its duration. `stoppedAt` is set. | +| `error` | The session failed to start or ended abnormally. | + +## Get a session + +Returns the same shape as create, plus `bandwidthBytes`: `null` while the session runs, the metered total once it has ended. + + +```bash cURL +curl "https://api.driver.dev/v1/browser/session?sessionId=$SESSION_ID" \ + -H "Authorization: Bearer $DRIVER_API_KEY" +``` +```typescript TypeScript +const url = `https://api.driver.dev/v1/browser/session?sessionId=${id}`; +const session = await fetch(url, { headers }).then((r) => r.json()); +``` +```python Python +session = requests.get( + "https://api.driver.dev/v1/browser/session", headers=headers, params={"sessionId": id} +).json() +``` + + +## List sessions + +Paginated, newest first, with an optional `status` filter. + + +```bash cURL +curl "https://api.driver.dev/v1/browser/sessions?page=1&pageSize=20&status=active" \ + -H "Authorization: Bearer $DRIVER_API_KEY" +``` +```typescript TypeScript +const { sessions, total, totalPages } = await fetch( + "https://api.driver.dev/v1/browser/sessions?status=active", + { headers }, +).then((r) => r.json()); +``` +```python Python +data = requests.get( + "https://api.driver.dev/v1/browser/sessions", headers=headers, params={"status": "active"} +).json() +``` + + +Each item carries `sessionId`, `status`, `servedBy`, `type`, `country`, `createdAt`, `stoppedAt`, `bandwidthBytes` and `note`. + +## Label a session + +Notes show up in the dashboard's session list and record. Set one on create, or afterwards: + + +```bash cURL +curl -X PATCH "https://api.driver.dev/v1/browser/session?sessionId=$SESSION_ID" \ + -H "Authorization: Bearer $DRIVER_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{"note":"nightly price check, client X"}' +``` +```typescript TypeScript +await fetch(`https://api.driver.dev/v1/browser/session?sessionId=${id}`, { + method: "PATCH", + headers, + body: JSON.stringify({ note: "nightly price check, client X" }), +}); +``` +```python Python +requests.patch( + "https://api.driver.dev/v1/browser/session", + headers=headers, + params={"sessionId": id}, + json={"note": "nightly price check, client X"}, +) +``` + + +Notes are at most 256 characters. Send an empty string to clear one. A session that has ended cannot be updated (`400`, `Cannot update a terminal session`). + +## Stop a session + +Stopping releases the browser and ends billing for the session. Always do it in a `finally`. + + +```bash cURL +curl -X DELETE "https://api.driver.dev/v1/browser/session?sessionId=$SESSION_ID" \ + -H "Authorization: Bearer $DRIVER_API_KEY" +``` +```typescript TypeScript +await fetch(`https://api.driver.dev/v1/browser/session?sessionId=${id}`, { + method: "DELETE", + headers, +}); +``` +```python Python +requests.delete( + "https://api.driver.dev/v1/browser/session", headers=headers, params={"sessionId": id} +) +``` + + +Returns `{ "success": true }`. Stopping is idempotent: a session that already ended answers the same `{ "success": true }`, so a stop in a `finally` is always safe. + +## Duration + +`duration` on create is the session's lifetime in seconds, from 60 to 3600, default 3600. There is no extension call: choose a duration that covers the job, and start a new session (with a persisted [profile](/docs/options/profiles) if you need the state) when it runs out. + +## Waiting for `active` + +Create normally returns with `cdpUrl` set. If you see `starting`, poll: + + +```typescript TypeScript +async function waitForActive(id: string, timeoutMs = 30_000) { + const deadline = Date.now() + timeoutMs; + while (Date.now() < deadline) { + const s = await getSession(id); + if (s.status === "active" && s.cdpUrl) return s; + if (s.status === "completed" || s.status === "error") { + throw new Error(`session ended: ${s.status}`); + } + await new Promise((r) => setTimeout(r, 1000)); + } + throw new Error("timed out waiting for the browser"); +} +``` +```python Python +def wait_for_active(session_id, timeout=30): + deadline = time.time() + timeout + while time.time() < deadline: + s = get_session(session_id) + if s["status"] == "active" and s.get("cdpUrl"): + return s + if s["status"] in ("completed", "error"): + raise RuntimeError(f"session ended: {s['status']}") + time.sleep(1) + raise TimeoutError("timed out waiting for the browser") +``` + diff --git a/docs/sessions/live-view.mdx b/docs/sessions/live-view.mdx new file mode 100644 index 0000000..63971aa --- /dev/null +++ b/docs/sessions/live-view.mdx @@ -0,0 +1,42 @@ +--- +title: "Live view" +description: "Watch a running session, take the controls, and read the record afterwards." +--- + +Every session can be watched while it runs and inspected after it ends. Use it to see what a site is doing before you add logging or hooks, and to hand a login or a CAPTCHA to a person mid-run. + +## In the dashboard + +Open **Telemetry → Sessions** in the [dashboard](https://app.driver.dev) and pick a running session. The page shows the live browser; you can scroll, click and type in it. The **Workbench** shows the same view next to a script editor, which is the quickest way to try options before writing code. + +## From a viewer URL + +Build a viewer URL from `cdpUrl` and open it in any browser: + +```text +https://viewer.driver.dev?ws= +``` + +Paste `cdpUrl` in as it is; no encoding is needed. + + +```typescript TypeScript +const viewerUrl = `https://viewer.driver.dev?ws=${session.cdpUrl}`; +``` +```python Python +viewer_url = f"https://viewer.driver.dev?ws={session['cdpUrl']}" +``` + + +| Parameter | Description | +|---|---| +| `ws` | The session's `cdpUrl`. | +| `theme` | Optional: `light` or `dark`. | + +Anyone with the URL can drive the browser, so treat it like the API key while the session is active. It stops working when the session ends. + +## The session record + +After a session ends, its page in the dashboard keeps the record: status and duration, bandwidth and cost, the node that served it with its country, the network it used, how it was started (type, profile, note, which API key), and timestamps. Keep the `sessionId` in your own logs so a run can be found later. + +The same facts are available from [`GET /v1/browser/session`](/docs/sessions/lifecycle#get-a-session). diff --git a/docs/start/best-practices.mdx b/docs/start/best-practices.mdx new file mode 100644 index 0000000..52dd5b6 --- /dev/null +++ b/docs/start/best-practices.mdx @@ -0,0 +1,110 @@ +--- +title: "Best practices" +description: "How to keep Driver sessions unblocked and cheap: the right framework, minimal CDP noise, coherent location settings, and reliable cleanup." +--- + +Driver gives you a real browser on a real network. Most blocks come from the automation side, not the browser, and they are avoidable. These practices are the difference between a session that looks like a person and one that looks like a script. + +## Keep the logic minimal + +The shortest script that reaches the outcome is the best one. It is faster, it fails in obvious ways, and it gives sites the least to notice. Every added retry loop, event listener, request interceptor, "stealth" helper or defensive wait is a place for bugs to hide and a signal that this is not a person. Add a mechanism only after a specific site has shown you that you need it, and add the smallest one that works. + +## Optimise for speed, then verify + +Before writing browser steps, ask whether the outcome is reachable faster: + +- Go straight to the URL that has the result instead of clicking through navigation. +- Do several pages in one session rather than one session per page. Creating a session is the expensive step. +- Sign in once on a persisted [profile](/docs/options/profiles) and reuse it instead of logging in on every run. +- Never use fixed sleeps. Rely on the framework's auto-waiting and stop the session the moment the result is in hand. + +Then test the automation for real: run it against the live target through Driver and check the result, not just the exit code. Confirm the data is what you expected, the action took effect, and the session was stopped. The session record under **Telemetry → Sessions** shows duration and bandwidth, which is how you know a run was actually fast. + +## Use Patchright + +Stock Playwright and Puppeteer enable CDP domains (`Runtime.enable`, console and binding hooks) that anti-bot vendors fingerprint. [Patchright](/docs/frameworks/patchright) is Playwright with those signals removed and the same API. Use it for every Driver integration; use stock Playwright only if you have measured that your target does not care. + +## Prefer the remote browser's defaults + +Do not change browser settings unless the task requires it. + +- Reuse `browser.contexts()[0]` and `context.pages()[0]` when they exist. The browser opened with a context and a tab already. +- Keep the default viewport, user agent, locale, permissions and flags. Set [`windowSize`](/docs/options/window-size), [`country`, `timezone` and `language`](/docs/options/location) on the create call instead; Driver makes them coherent with the network the session egresses from. +- Do not add timeouts and navigation settings until a specific site needs them. + +## Never launch a browser + +Driver provides the browser. Connect to the returned `cdpUrl`. + +Not in a Driver integration: + +- `chromium.launch()`, `chromium.launchPersistentContext()`, `puppeteer.launch()` +- `playwright install`, or any browser download +- invented `localhost` CDP ports or CDP hosts not returned by Driver + +## Keep CDP hooks narrow + +Broad event listeners, request routing and injected scripts make the browser behave unusually and are detectable. Use them only when the task genuinely requires them. + +Avoid by default: + +- `page.on(...)`, `context.on(...)`, `browser.on(...)` +- `page.route(...)`, `context.route(...)` +- `page.exposeBinding(...)`, `page.exposeFunction(...)` +- `page.addInitScript(...)`, `context.addInitScript(...)` + +Prefer direct actions and reads: `page.goto`, `page.click`, `page.fill`, `page.locator`, `page.title`, `page.textContent`. + +When a hook is unavoidable, listen for one event type instead of every event, route one URL pattern instead of all requests, and remove listeners and routes as soon as the step is done. + +## Do not patch fingerprints + +The point of Driver is that the browser is genuine. Overriding `navigator`, the user agent, WebGL, canvas, timezone or locale from your script makes a real browser look fake. If you need a different location, ask for it on the create call. + +## Debug with the live view first + +When a page behaves differently than expected, open the session's [live view](/docs/sessions/live-view) and look before adding hooks or logging every event. You can take the mouse and keyboard yourself, which is often the fastest way to see what a site is doing. + +## Always stop sessions + +Wrap browser work in `try` / `finally` so the session is stopped even when navigation or extraction fails. Stopping is what releases the browser and ends billing. + + +```typescript TypeScript +const session = await createSession({ country: "US" }); +try { + // connect and automate +} finally { + await stopSession(session.sessionId); +} +``` +```python Python +session = create_session(country="US") +try: + ... # connect and automate +finally: + stop_session(session["sessionId"]) +``` + + +Set a `duration` that matches the job. Sessions end on their own at the duration (one hour by default), which caps the cost of anything you forget. + +## Reuse logins with profiles + +Signing in on every run is slow and looks suspicious. Sign in once under a named [profile](/docs/options/profiles) with `persist: true`, then start later sessions with the same name; they open already signed in. + +## Handle the errors that matter + +| Status | Meaning | What to do | +|---|---|---| +| `402` | Workspace is out of credits | Tell the user. Retrying will not help. | +| `403` | Plan limit: concurrency or feature not enabled | Wait for a session to finish, or upgrade. | +| `429` | Rate limited | Back off and retry. | +| `503` | No browser capacity right now | Back off with jitter, or tell the user. | +| `504` | Browser did not become reachable in time | Retry once. | + +The full list is on [Errors](/docs/sessions/errors). + +## Label your sessions + +Pass `note` on create, or `PATCH` it later, with something a teammate would recognise ("nightly price check, client X"). It shows up in the dashboard's session list and record. diff --git a/docs/start/for-agents.mdx b/docs/start/for-agents.mdx new file mode 100644 index 0000000..9698f0a --- /dev/null +++ b/docs/start/for-agents.mdx @@ -0,0 +1,192 @@ +--- +title: "For agents" +description: "A briefing for coding agents integrating Driver: the rules that keep sessions unblocked, the three endpoints that matter, and the mistakes to avoid." +--- + +This page is written for an AI coding agent (Claude Code, Cursor, Codex, or similar) that has been asked to integrate Driver into a project. Humans are welcome too. Everything here is also in `llms.txt` at the root of these docs. + +## The rules + +1. **Use Patchright, not stock Playwright or Puppeteer.** Install `patchright` (npm or pip) and import `chromium` from it. It has the same API as Playwright, so existing Playwright code works unchanged. Stock Playwright issues CDP commands (`Runtime.enable`, console and binding hooks) that anti-bot systems detect, and sessions get blocked. Patchright removes them. See [Why Patchright](/docs/frameworks/patchright#why-patchright). +2. **Never launch a browser.** Driver provides the browser. Connect to the `cdpUrl` from the create call with `chromium.connectOverCDP(cdpUrl)`. Do not call `chromium.launch()`, `launchPersistentContext()`, `puppeteer.launch()`, or run `playwright install`. +3. **Reuse the browser's context and tab.** After connecting, use `browser.contexts()[0]` and `context.pages()[0]`. Only create new ones if none exist. Extra contexts and stripped-down pages look like automation. +4. **Do not install broad CDP hooks.** Avoid `page.route`, `context.route`, `page.on('request')`, `page.exposeFunction`, `page.addInitScript` and `context.addInitScript` unless the task cannot be done otherwise, and then scope them as narrowly as possible. They change how the browser behaves and are detectable. +5. **Do not patch fingerprints.** The browser is real Chrome on real hardware. Overriding `navigator`, the user agent, WebGL, canvas, timezone or locale makes it look fake. Set `country`, `timezone` and `language` on the create call instead. +6. **Always stop the session.** Wrap the work in `try / finally` and call `DELETE /v1/browser/session?sessionId=…` in `finally`. Sessions bill by bandwidth and end on their own at `duration` (one hour by default), but a session left running still costs. +7. **Keep the `sessionId`.** Log it with the run. Every session has a record in the dashboard under Telemetry → Sessions with node, network, timing and cost. +8. **Handle 402 and 503 by reporting, not retrying.** 402 means the workspace is out of credits; 503 means no browser capacity right now. Surface the message to the user. Retry only 429 (rate limited) and 504 (browser did not start in time), with backoff. +9. **Stay under the workspace's concurrency limit.** `GET /v1/account/billing` reports it as `plan.concurrent_browsers`; sessions beyond it are refused with 403. + +## How to write the automation + +- **Minimal logic wins.** The shortest script that reaches the outcome is the most reliable and the least detectable. Go to the page, do the action, read the result. Do not add retries, waits, listeners, request routing or "stealth" helpers unless a specific failure on a specific site demands them, and then add the smallest thing that fixes it. +- **Fast and accurate, in that order of effort.** Before writing browser steps, ask whether the outcome is reachable faster: a direct URL instead of clicking through navigation, one session doing several pages instead of one session per page, a persisted [profile](/docs/options/profiles) instead of logging in every run. Then keep the browser steps short: no fixed sleeps, rely on the framework's auto-waiting, stop the session the moment the result is in hand. +- **Test the automation for real, every time.** Run it against the live target through Driver and check the result, not just that the script exits. Confirm the data is right, the action happened, and the session was stopped (look it up under Telemetry → Sessions). A run that "worked" but read the wrong element is worse than one that failed loudly. +- **Do not pollute the session.** Every extra CDP feature you switch on, every injected script, every overridden property is a signal. If you did not need it to reach the outcome, take it out. + +## Handoff prompt + +Give this to a coding agent along with the target and the outcome you want. It is the whole briefing. + +```text +Integrate Driver (driver.dev), hosted real-Chrome browser infrastructure, into this project. + +- Docs: https://docs.driver.dev — agents start at https://docs.driver.dev/docs/start/for-agents + Endpoints: https://api.driver.dev/scalar +- Auth: Authorization: Bearer $DRIVER_API_KEY (read from the environment; never commit it) +- Most tasks need no options: POST https://api.driver.dev/v1/browser/session with {} gives a + hosted US Chrome. Use "country" for geo-specific sites, "profile" to keep logins, "duration" + to cap cost. +- Drive the browser only with Patchright (https://github.com/Kaliiiiiiiiii-Vinyzu/patchright): + chromium.connectOverCDP(cdpUrl), then reuse browser.contexts()[0] and context.pages()[0]. + Never launch a browser. Never use stock Playwright or Puppeteer. +- No CDP or automation tactics that leak or get sites to block: no page.route, no init scripts, + no exposed functions, no fingerprint patches. Minimal automation logic is always best: + https://docs.driver.dev/docs/start/best-practices +- Check whether the outcome can be reached faster: direct URLs, one session for many pages, + a persisted profile instead of logging in. Avoid slow browser logic and fixed sleeps. +- Always stop the session in a finally: DELETE /v1/browser/session?sessionId=. + Keep the sessionId in logs. +- Test the automation against the real target and verify the result before calling it done. +``` + +## The three calls + +Base URL `https://api.driver.dev`. Every call carries `Authorization: Bearer $DRIVER_API_KEY`. Store the key in an environment variable named `DRIVER_API_KEY`; never commit it. + +| Step | Call | Notes | +|---|---|---| +| Create | `POST /v1/browser/session` with a JSON body | All fields optional. Returns `{ sessionId, status, cdpUrl, servedBy, createdAt, stoppedAt, note }`. `cdpUrl` is set when the browser is reachable; the call waits for that. | +| Use | `chromium.connectOverCDP(cdpUrl)` | From Patchright. Reuse the first context and page. | +| Stop | `DELETE /v1/browser/session?sessionId=` | Returns `{ success: true }`. | + +Useful extras: `GET /v1/browser/session?sessionId=` for status and bandwidth, `GET /v1/browser/sessions?status=active` to list, `PATCH /v1/browser/session?sessionId=` with `{ "note": "…" }` to label a run. Full options for the create body are on [Session options](/docs/options/browser-types). + +## Reference implementation + + +```typescript driver.ts +import { chromium, type Browser, type Page } from "patchright"; + +const API = "https://api.driver.dev"; + +export interface CreateOptions { + country?: string; // "US", "GB", "DE", … + duration?: number; // seconds, 60–3600 + profile?: { name: string; persist?: boolean }; + proxyUrl?: string; // socks5://user:pass@host:port + windowSize?: string; // "1920x1080" + captchaSolver?: boolean; + note?: string; +} + +export interface Session { + sessionId: string; + status: "starting" | "active" | "completed" | "error"; + cdpUrl: string | null; + servedBy: string; +} + +const headers = () => ({ + Authorization: `Bearer ${process.env.DRIVER_API_KEY}`, + "Content-Type": "application/json", +}); + +export async function createSession(opts: CreateOptions = {}): Promise { + const res = await fetch(`${API}/v1/browser/session`, { + method: "POST", + headers: headers(), + body: JSON.stringify(opts), + }); + if (!res.ok) { + const body = await res.json().catch(() => ({})); + throw new Error(`Driver ${res.status}: ${body.error ?? res.statusText}`); + } + return res.json(); +} + +export async function stopSession(sessionId: string): Promise { + await fetch(`${API}/v1/browser/session?sessionId=${encodeURIComponent(sessionId)}`, { + method: "DELETE", + headers: headers(), + }); +} + +/** Runs `fn` against a fresh Driver session and always stops it afterwards. */ +export async function withBrowser( + opts: CreateOptions, + fn: (page: Page, browser: Browser) => Promise, +): Promise { + const session = await createSession(opts); + try { + const browser = await chromium.connectOverCDP(session.cdpUrl!); + try { + const context = browser.contexts()[0] ?? (await browser.newContext()); + const page = context.pages()[0] ?? (await context.newPage()); + return await fn(page, browser); + } finally { + await browser.close(); + } + } finally { + await stopSession(session.sessionId); + } +} +``` + +```python driver.py +import os +from contextlib import contextmanager +import requests +from patchright.sync_api import sync_playwright + +API = "https://api.driver.dev" + + +def _headers(): + return {"Authorization": f"Bearer {os.environ['DRIVER_API_KEY']}"} + + +def create_session(**opts): + """opts: country, duration, profile={"name": …, "persist": True}, proxyUrl, + windowSize, captchaSolver, note.""" + r = requests.post(f"{API}/v1/browser/session", headers=_headers(), json=opts) + if not r.ok: + raise RuntimeError(f"Driver {r.status_code}: {r.json().get('error', r.reason)}") + return r.json() + + +def stop_session(session_id: str): + requests.delete(f"{API}/v1/browser/session", headers=_headers(), params={"sessionId": session_id}) + + +@contextmanager +def browser_page(**opts): + """with browser_page(country="US") as page: ... — always stops the session.""" + session = create_session(**opts) + try: + with sync_playwright() as p: + browser = p.chromium.connect_over_cdp(session["cdpUrl"]) + context = browser.contexts[0] if browser.contexts else browser.new_context() + page = context.pages[0] if context.pages else context.new_page() + try: + yield page + finally: + browser.close() + finally: + stop_session(session["sessionId"]) +``` + + +## Checklist before you finish + +- [ ] `patchright` is the dependency, not `playwright` or `puppeteer`. +- [ ] No `launch()` anywhere; the only browser entry point is `connectOverCDP(cdpUrl)`. +- [ ] The first context and page are reused. +- [ ] The session is stopped in a `finally`. +- [ ] `DRIVER_API_KEY` comes from the environment and is not committed. +- [ ] 402 and 503 are reported to the user, not retried in a loop. + +## Machine-readable resources + +- `https://docs.driver.dev/llms.txt` lists every page of these docs. +- `https://api.driver.dev/doc` is the live OpenAPI document; `https://api.driver.dev/scalar` renders it. diff --git a/docs/start/index.mdx b/docs/start/index.mdx new file mode 100644 index 0000000..73a0899 --- /dev/null +++ b/docs/start/index.mdx @@ -0,0 +1,62 @@ +--- +title: "Introduction" +description: "Driver runs real Chrome on real hardware and hands you a CDP URL. Create a session, connect your framework, stop the session." +--- + +Driver is hosted browser infrastructure. Every session is a real Chrome window on a real machine, on a residential network in the country you ask for, reachable over the Chrome DevTools Protocol in seconds. You bring the automation; Driver brings the browser. + + + + Create a session, connect with Patchright, stop it. Five minutes. + + + You are a coding agent? Start here: the rules, the endpoints, the pitfalls. + + + How to stay unblocked: framework choice, CDP hygiene, session cleanup. + + + Every endpoint, generated from the live OpenAPI document. + + + +## How it works + + + + `POST /v1/browser/session` with the options you need (country, profile, proxy, window size). The response carries a `sessionId` and a `cdpUrl`. + + + Point your framework at `cdpUrl`. Use [Patchright](/docs/frameworks/patchright) unless you have a reason not to; stock Playwright and Puppeteer leave fingerprints that get sessions blocked. + + + Navigate, fill, click, extract. The browser is a normal Chrome: no headless quirks, no emulated hardware. + + + `DELETE /v1/browser/session?sessionId=…` releases the browser. Bandwidth is what you pay for, so stop what you no longer need. + + + +## What you get + +| | | +|---|---| +| **Real Chrome** | Consumer Chrome builds on real hardware, not a headless container. Sites see a normal browser. | +| **Residential networks** | Sessions egress from residential connections in the country you choose. Bring your own SOCKS5 proxy when you need to. | +| **Profiles** | Save cookies and local state under a name and reuse them, so logins survive between sessions. | +| **CAPTCHA solving** | One flag loads Driver's solver into the session. | +| **Live view** | Watch and drive any running session from the dashboard or a viewer URL. | + +## Base URL and authentication + +All requests go to `https://api.driver.dev` with your workspace API key as a bearer token. Keys are created under **Settings → API keys** in the [dashboard](https://app.driver.dev). + +```bash +Authorization: Bearer $DRIVER_API_KEY +``` + +## Next + +- [Quickstart](/docs/start/quickstart) creates and stops a session end to end. +- [Session options](/docs/options/browser-types) explains every field you can pass on create. +- [Frameworks](/docs/frameworks/patchright) shows the connection code for Patchright, Playwright, Puppeteer, Browser Use, Stagehand and Crawl4AI. diff --git a/docs/start/quickstart.mdx b/docs/start/quickstart.mdx new file mode 100644 index 0000000..e558a37 --- /dev/null +++ b/docs/start/quickstart.mdx @@ -0,0 +1,131 @@ +--- +title: "Quickstart" +description: "Create a Driver session, connect with Patchright, read a page title, and stop the session." +--- + + + + Sign in to the [dashboard](https://app.driver.dev), open **Settings → API keys**, and create a key. Put it in your environment as `DRIVER_API_KEY`. Never commit it. + + ```bash + export DRIVER_API_KEY="dr_…" + ``` + + + + Patchright is a drop-in Playwright with the CDP fingerprints removed. Use it instead of stock Playwright, otherwise sites that watch for automation will block your session. Details in [Why Patchright](/docs/frameworks/patchright#why-patchright). + + + ```bash npm + npm install patchright + ``` + ```bash pip + pip install patchright requests + ``` + + + You do not need `playwright install` or any browser download: Driver provides the browser. + + + + + ```typescript TypeScript + import { chromium } from "patchright"; + + const API = "https://api.driver.dev"; + const headers = { + Authorization: `Bearer ${process.env.DRIVER_API_KEY}`, + "Content-Type": "application/json", + }; + + // 1. Create a session. Every field is optional; this one asks for a US browser for ten minutes. + const session = await fetch(`${API}/v1/browser/session`, { + method: "POST", + headers, + body: JSON.stringify({ country: "US", duration: 600 }), + }).then((r) => r.json()); + + try { + // 2. Connect over CDP and reuse the browser's own context and tab. + const browser = await chromium.connectOverCDP(session.cdpUrl); + const context = browser.contexts()[0] ?? (await browser.newContext()); + const page = context.pages()[0] ?? (await context.newPage()); + + // 3. Do the work. + await page.goto("https://example.com"); + console.log(await page.title()); + + await browser.close(); + } finally { + // 4. Stop the session. This is what ends billing. + await fetch(`${API}/v1/browser/session?sessionId=${session.sessionId}`, { + method: "DELETE", + headers, + }); + } + ``` + + ```python Python + import os + import requests + from patchright.sync_api import sync_playwright + + API = "https://api.driver.dev" + headers = {"Authorization": f"Bearer {os.environ['DRIVER_API_KEY']}"} + + # 1. Create a session. Every field is optional; this one asks for a US browser for ten minutes. + session = requests.post(f"{API}/v1/browser/session", headers=headers, json={"country": "US", "duration": 600}) + session.raise_for_status() + session = session.json() + + try: + # 2. Connect over CDP and reuse the browser's own context and tab. + with sync_playwright() as p: + browser = p.chromium.connect_over_cdp(session["cdpUrl"]) + context = browser.contexts[0] if browser.contexts else browser.new_context() + page = context.pages[0] if context.pages else context.new_page() + + # 3. Do the work. + page.goto("https://example.com") + print(page.title()) + browser.close() + finally: + # 4. Stop the session. This is what ends billing. + requests.delete( + f"{API}/v1/browser/session", headers=headers, params={"sessionId": session["sessionId"]} + ) + ``` + + ```bash cURL + # Create + curl -s -X POST https://api.driver.dev/v1/browser/session \ + -H "Authorization: Bearer $DRIVER_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{"country":"US","duration":600}' + # → {"sessionId":"…","status":"active","cdpUrl":"wss://…", …} + + # Stop + curl -s -X DELETE "https://api.driver.dev/v1/browser/session?sessionId=$SESSION_ID" \ + -H "Authorization: Bearer $DRIVER_API_KEY" + ``` + + + + + Every session has a page in the dashboard under **Telemetry → Sessions** with a live view while it runs and the full record afterwards: node, country, network, bandwidth, cost. See [Live view](/docs/sessions/live-view). + + + +## What just happened + +The create call returned as soon as Chrome was reachable over CDP, usually in a few seconds. `cdpUrl` is a WebSocket endpoint to that one browser. Connecting with `connectOverCDP` attaches to the running browser instead of launching one, which is why there is no `chromium.launch()` anywhere and no browser download on your machine. + +The browser opened with one context and one tab already. Reusing them keeps the session looking like a person opened Chrome and started browsing; creating extra contexts is one of the things sites notice. + +Stopping the session releases the machine and closes the bill for that session. Sessions also stop on their own at the `duration` you set, or after an hour by default. + +## Next + +- [For agents](/docs/start/for-agents) if a coding agent is doing the integration. +- [Session options](/docs/options/browser-types) for browser types, location, proxies, profiles and window size. +- [Best practices](/docs/start/best-practices) before you scale up. diff --git a/images/checks-passed.png b/images/checks-passed.png deleted file mode 100644 index 3303c773646ca12fb6852356663540e3ed048115..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 160724 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ɊgP+1Kmq9C+NW`N{6BwF_FKP% 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

f_Qe2@sz@M2$uLLxR{EU6s^ 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" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.3.0.tgz", + "integrity": "sha512-U4+70Pc5ZS9osnCBCE5Jha/ciHM+Yp+CNMNC/7HvYbNRk1Ldd+f7qO65W5qfhu/TCv+/ozljlXXe9Nj8419DMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@anthropic-ai/claude-agent-sdk": { + "version": "0.3.241", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.241.tgz", + "integrity": "sha512-pIHdCSTywFe30H0oWDCKZzC4ipBLtF5YMDRKjf6PHyARg57O4l/72v3b6QKnnefwtKKMe6uWJ1Y9lUJg/sKWyA==", + "dev": true, + "license": "SEE LICENSE IN README.md", + "optional": true, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.241", + "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.241", + "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.241", + "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.241", + "@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.241", + "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.241", + "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.241", + "@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.241" + }, + "peerDependencies": { + "@anthropic-ai/sdk": ">=0.93.0", + "@modelcontextprotocol/sdk": "^1.29.0", + "zod": "^4.0.0" + } + }, + "node_modules/@anthropic-ai/claude-agent-sdk-darwin-arm64": { + "version": "0.3.241", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.241.tgz", + "integrity": "sha512-v26ta54lKFMFEZzbOE+6p3YhKERWnDiEA6OmkSAg+3fAQHOa1+aLTKw222cfgzxgiVixwFtHMk8c63zsDd8aXQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.md", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@anthropic-ai/claude-agent-sdk-darwin-x64": { + "version": "0.3.241", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.241.tgz", + "integrity": "sha512-5jweT0vft1ZCaGSoxZHF9vJlHbx8Yxx4+x5aHAIXTd4lx7ZbT4o5buEF8kpmTeHUB+Fw9jtFIm4QDsRiBXgf+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.md", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64": { + "version": "0.3.241", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.241.tgz", + "integrity": "sha512-SxszQGffXiLzMEnAv+pJXEmQbA8haijKyRjjH/jOt1CLeMIfpjKcO9WQDv8dEA8nREWS3zJ103zjgecAF7oOQQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.md", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64-musl": { + "version": "0.3.241", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.241.tgz", + "integrity": "sha512-GslvPvSzehfCZyzOaJAt4lgodznm5zpl/LMXN8ygD12z5qnpM+I9/eFnmAaISJ0L8/vyohtlAP1jjaeR2jz1AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.md", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@anthropic-ai/claude-agent-sdk-linux-x64": { + "version": "0.3.241", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.241.tgz", + "integrity": "sha512-gJRa922Qcm7loumHcXMDFEFg//tz1aOi7Nx0sQa9I9lC1JSN8yL6i7/idzOU5Hp193tEDFOgqIMFL/yRiXg+rw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.md", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@anthropic-ai/claude-agent-sdk-linux-x64-musl": { + "version": "0.3.241", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.241.tgz", + "integrity": "sha512-kZigJ5Ug2I2G/n7Cunmwy4TGr0lOGnWrz6TkzyWiDcUmJOodoTH6GZECNarWAtETfN03AAeLfrpiz8z3hOEDqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.md", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@anthropic-ai/claude-agent-sdk-win32-arm64": { + "version": "0.3.241", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.241.tgz", + "integrity": "sha512-/3yA9jQuCvHDVlILzhtslH6kFYOvydXyMZiKwnzqM8ZfvFTNO41w8TpiFpBLseyM+4A4E8QMeTKu3L01Xyb5IQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.md", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@anthropic-ai/claude-agent-sdk-win32-x64": { + "version": "0.3.241", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.241.tgz", + "integrity": "sha512-cHYdAgORl9kynujMeYXyV1uj/hbmsBjRw9dRVkIW4/4sF7S6L4u/qDSzn1/wiNP7g2yWSJ4KbsvHDH2WWDnCBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.md", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.120.0", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.120.0.tgz", + "integrity": "sha512-ZlvmNFT/iIF6JD13rxbbMWD8nvGR0RaUp6yMQnoc+4Af0YjVVe/bIdW1XSQQsoxXAtg1NaT6Vak0LKFlJ4d37Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "json-schema-to-ts": "^3.1.1", + "standardwebhooks": "^1.0.0" + }, + "bin": { + "anthropic-ai-sdk": "bin/cli" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/@ark/schema": { + "version": "0.56.2", + "resolved": "https://registry.npmjs.org/@ark/schema/-/schema-0.56.2.tgz", + "integrity": "sha512-Qx4D2JFbBWpntiHZaTv7bGG4H/M2rigiknezKg/WVyDSaLdE4YCcWAOoFB7pjjDqHbbV2OqRfntm1nnXvwMexg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ark/util": "0.56.2" + } + }, + "node_modules/@ark/util": { + "version": "0.56.2", + "resolved": "https://registry.npmjs.org/@ark/util/-/util-0.56.2.tgz", + "integrity": "sha512-9kU2sUE38FZEGG7l3hamYMBieLYEJh2L1mrYD2eXpT+78EnQSV1bhjxJhnxGBMSTbtwpBSDNSK+K60WvaI/DTQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@asyncapi/parser": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-3.4.0.tgz", + "integrity": "sha512-Sxn74oHiZSU6+cVeZy62iPZMFMvKp4jupMFHelSICCMw1qELmUHPvuZSr+ZHDmNGgHcEpzJM5HN02kR7T4g+PQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@asyncapi/specs": "^6.8.0", + "@openapi-contrib/openapi-schema-to-json-schema": "~3.2.0", + "@stoplight/json": "3.21.0", + "@stoplight/json-ref-readers": "^1.2.2", + "@stoplight/json-ref-resolver": "^3.1.5", + "@stoplight/spectral-core": "^1.18.3", + "@stoplight/spectral-functions": "^1.7.2", + "@stoplight/spectral-parsers": "^1.0.2", + "@stoplight/spectral-ref-resolver": "^1.0.3", + "@stoplight/types": "^13.12.0", + "@types/json-schema": "^7.0.11", + "@types/urijs": "^1.19.19", + "ajv": "^8.17.1", + "ajv-errors": "^3.0.0", + "ajv-formats": "^2.1.1", + "avsc": "^5.7.5", + "js-yaml": "^4.1.0", + "jsonpath-plus": "^10.0.0", + "node-fetch": "2.6.7" + } + }, + "node_modules/@asyncapi/specs": { + "version": "6.11.1", + "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-6.11.1.tgz", + "integrity": "sha512-A3WBLqAKGoJ2+6FWFtpjBlCQ1oFCcs4GxF7zsIGvNqp/klGUHjlA3aAcZ9XMMpLGE8zPeYDz2x9FmO6DSuKraQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.11" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@canvas/image-data": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@canvas/image-data/-/image-data-1.1.0.tgz", + "integrity": "sha512-QdObRRjRbcXGmM1tmJ+MrHcaz1MftF2+W7YI+MsphnsCrmtyfS0d5qJbk0MeSbUeyM/jCb0hmnkXPsy026L7dA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/@floating-ui/core": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.8.0.tgz", + "integrity": "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@floating-ui/utils": "^0.2.12" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.8.0.tgz", + "integrity": "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@floating-ui/core": "^1.8.0", + "@floating-ui/utils": "^0.2.12" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.12.tgz", + "integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@hono/node-server": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.1.tgz", + "integrity": "sha512-ELuehkj5VCBdgEw9zs+ivkKwyzzUCSQuE96YmiPvn1ECBoZCczbFXJLeEGMTYjphP6gydh4pHMqEYPVMYUVgQg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.5" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.2.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@inquirer/ansi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", + "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz", + "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/confirm": { + "version": "5.1.21", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz", + "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", + "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/editor": { + "version": "4.2.23", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz", + "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/external-editor": "^1.0.3", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz", + "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", + "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz", + "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz", + "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz", + "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/prompts": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.9.0.tgz", + "integrity": "sha512-X7/+dG9SLpSzRkwgG5/xiIzW0oMrV3C0HOa7YHG1WnrLK+vCQHfte4k/T80059YBdei29RBC3s+pSMvPJDU9/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^4.3.0", + "@inquirer/confirm": "^5.1.19", + "@inquirer/editor": "^4.2.21", + "@inquirer/expand": "^4.0.21", + "@inquirer/input": "^4.2.5", + "@inquirer/number": "^3.0.21", + "@inquirer/password": "^4.0.21", + "@inquirer/rawlist": "^4.1.9", + "@inquirer/search": "^3.2.0", + "@inquirer/select": "^4.4.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz", + "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz", + "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz", + "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", + "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jsep-plugin/assignment": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", + "integrity": "sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.16.0" + }, + "peerDependencies": { + "jsep": "^0.4.0||^1.0.0" + } + }, + "node_modules/@jsep-plugin/regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.4.tgz", + "integrity": "sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.16.0" + }, + "peerDependencies": { + "jsep": "^0.4.0||^1.0.0" + } + }, + "node_modules/@jsep-plugin/ternary": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@jsep-plugin/ternary/-/ternary-1.1.4.tgz", + "integrity": "sha512-ck5wiqIbqdMX6WRQztBL7ASDty9YLgJ3sSAK5ZpBzXeySvFGCzIvM6UiAI4hTZ22fEcYQVV/zhUbNscggW+Ukg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.16.0" + }, + "peerDependencies": { + "jsep": "^0.4.0||^1.0.0" + } + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@mdx-js/mdx": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.1.tgz", + "integrity": "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdx": "^2.0.0", + "acorn": "^8.0.0", + "collapse-white-space": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-util-scope": "^1.0.0", + "estree-walker": "^3.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "markdown-extensions": "^2.0.0", + "recma-build-jsx": "^1.0.0", + "recma-jsx": "^1.0.0", + "recma-stringify": "^1.0.0", + "rehype-recma": "^1.0.0", + "remark-mdx": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "source-map": "^0.7.0", + "unified": "^11.0.0", + "unist-util-position-from-estree": "^2.0.0", + "unist-util-stringify-position": "^4.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mdx-js/react": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.1.tgz", + "integrity": "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdx": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=16", + "react": ">=16" + } + }, + "node_modules/@mintlify/cli": { + "version": "4.0.1502", + "resolved": "https://registry.npmjs.org/@mintlify/cli/-/cli-4.0.1502.tgz", + "integrity": "sha512-lTTiMxb1IfVonqBxm30vknzzr+i3OditUhcZAESF5482YpiiPQkhWDCyNJF2fiAWiWduiKNzTvm1VbS8xy98Bw==", + "dev": true, + "license": "Elastic-2.0", + "dependencies": { + "@inquirer/prompts": "7.9.0", + "@mintlify/common": "1.0.1150", + "@mintlify/link-rot": "3.0.1352", + "@mintlify/models": "0.0.358", + "@mintlify/prebuild": "1.0.1304", + "@mintlify/previewing": "4.0.1376", + "@mintlify/validation": "0.1.858", + "adm-zip": "0.6.0", + "chalk": "5.2.0", + "color": "4.2.3", + "detect-port": "1.5.1", + "fs-extra": "11.2.0", + "ink": "6.3.0", + "inquirer": "12.3.0", + "js-yaml": "4.3.1", + "jsonc-parser": "^3.3.1", + "mdast-util-mdx-jsx": "3.2.0", + "open": "8.4.2", + "openid-client": "6.8.2", + "posthog-node": "5.17.2", + "prosemirror-model": "^1.25.0", + "react": "19.2.3", + "remark": "^15.0.1", + "remark-frontmatter": "^5.0.0", + "remark-gfm": "^4.0.1", + "remark-math": "^6.0.0", + "remark-mdx": "^3.1.1", + "remark-stringify": "^11.0.0", + "unified": "^11.0.5", + "unist-util-visit": "5.0.0", + "yaml": "^2.8.3", + "yargs": "17.7.1", + "zod": "4.3.6" + }, + "bin": { + "mint": "bin/index.js", + "mintlify": "bin/index.js" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "@anthropic-ai/claude-agent-sdk": "0.3.241", + "@anthropic-ai/sdk": "0.120.0", + "@modelcontextprotocol/sdk": "1.30.0", + "@openai/codex-sdk": "0.149.1", + "keytar": "7.9.0" + } + }, + "node_modules/@mintlify/common": { + "version": "1.0.1150", + "resolved": "https://registry.npmjs.org/@mintlify/common/-/common-1.0.1150.tgz", + "integrity": "sha512-FBD2ChIvFhw+y5JV5ufwg4mUWlzT01wUy1gKbELe5v44qJVbow3EKi31JBgxvuw0yQEeVM8+OAg1KmSBYf3weg==", + "dev": true, + "license": "ISC", + "dependencies": { + "@asyncapi/parser": "3.4.0", + "@mintlify/mdx": "4.0.2", + "@mintlify/models": "0.0.358", + "@mintlify/openapi-parser": "0.0.8", + "@mintlify/validation": "0.1.858", + "@sindresorhus/slugify": "2.2.0", + "@types/mdast": "4.0.4", + "acorn": "8.11.2", + "acorn-jsx": "5.3.2", + "estree-util-to-js": "2.0.0", + "estree-walker": "3.0.3", + "front-matter": "4.0.2", + "hast-util-from-html": "2.0.3", + "hast-util-to-estree": "3.1.0", + "hast-util-to-html": "9.0.4", + "ignore": "7.0.5", + "iregexp-check": "^0.1.2", + "js-yaml": "4.3.1", + "json-p3": "2.2.2", + "lodash": "4.18.1", + "mdast-util-from-markdown": "2.0.2", + "mdast-util-gfm": "3.0.0", + "mdast-util-mdx": "3.0.0", + "mdast-util-mdx-jsx": "3.1.3", + "micromark-extension-gfm": "3.0.0", + "micromark-extension-mdxjs": "3.0.0", + "openapi-types": "12.1.3", + "postcss": "8.5.23", + "re2js": "^2.8.6", + "rehype-katex": "7.0.1", + "rehype-stringify": "10.0.1", + "remark": "15.0.1", + "remark-frontmatter": "5.0.0", + "remark-gfm": "4.0.0", + "remark-math": "6.0.0", + "remark-mdx": "3.1.0", + "remark-parse": "11.0.0", + "remark-rehype": "11.1.1", + "remark-smartypants": "3.0.2", + "remark-stringify": "11.0.0", + "sucrase": "3.34.0", + "tailwindcss-v3": "npm:tailwindcss@3.4.17", + "unified": "11.0.5", + "unist-builder": "4.0.0", + "unist-util-map": "4.0.0", + "unist-util-remove": "4.0.0", + "unist-util-remove-position": "5.0.0", + "unist-util-visit": "5.0.0", + "unist-util-visit-parents": "6.0.1", + "vfile": "6.0.3", + "xss": "1.0.15" + } + }, + "node_modules/@mintlify/common/node_modules/@base-ui/react": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@base-ui/react/-/react-1.8.0.tgz", + "integrity": "sha512-P0/1sxo6SBVZOklKMIedvTWqw2s2IQzi9x5bIVsXu980cuSOD4NeuRSs+/L7LZQfDkZP/uRZyGPyfFl/B1oH+Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.29.7", + "@base-ui/utils": "0.4.0", + "@floating-ui/react-dom": "^2.1.9", + "@floating-ui/utils": "^0.2.12", + "use-sync-external-store": "^1.6.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@date-fns/tz": "^1.2.0", + "@types/react": "^17 || ^18 || ^19", + "date-fns": "^4.0.0", + "react": "^17 || ^18 || ^19", + "react-dom": "^17 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "@date-fns/tz": { + "optional": true + }, + "@types/react": { + "optional": true + }, + "date-fns": { + "optional": true + } + } + }, + "node_modules/@mintlify/common/node_modules/@base-ui/utils": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@base-ui/utils/-/utils-0.4.0.tgz", + "integrity": "sha512-bO9fz25kKtPf+aZVyfQrC0PDmJdmVni31W2hCS5/Owb+inwdIL3XU26pCPRPlt4LSxZrBgLwubXQXQlKaFEZzw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.29.7", + "@floating-ui/utils": "^0.2.12", + "reselect": "^5.2.0", + "use-sync-external-store": "^1.6.0" + }, + "peerDependencies": { + "@types/react": "^17 || ^18 || ^19", + "react": "^17 || ^18 || ^19", + "react-dom": "^17 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mintlify/common/node_modules/@floating-ui/react-dom": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.9.tgz", + "integrity": "sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@floating-ui/dom": "^1.8.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@mintlify/common/node_modules/@mintlify/mdx": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@mintlify/mdx/-/mdx-4.0.2.tgz", + "integrity": "sha512-LrAE0fRxZSvB4POZ50Uy5LO0bKvWbPMciE/TKUyzRk4zbexQYYRhnzz9tdQ7zis//91oikrPnKbJxCco1UlawA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/transformers": "^3.23.0", + "@shikijs/twoslash": "^3.23.0", + "arktype": "^2.1.26", + "hast-util-to-string": "^3.0.1", + "mdast-util-from-markdown": "^2.0.2", + "mdast-util-gfm": "^3.1.0", + "mdast-util-mdx-jsx": "^3.2.0", + "mdast-util-to-hast": "^13.2.0", + "next-mdx-remote-client": "^2.1.11", + "rehype-katex": "^7.0.1", + "remark-gfm": "^4.0.0", + "remark-math": "^6.0.0", + "remark-smartypants": "^3.0.2", + "shiki": "^3.23.0", + "twoslash": "^0.3.4", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0" + }, + "peerDependencies": { + "@base-ui/react": "^1.6.0", + "react": "^19.2.1", + "react-dom": "^19.2.1" + } + }, + "node_modules/@mintlify/common/node_modules/@mintlify/mdx/node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mintlify/common/node_modules/@mintlify/mdx/node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mintlify/common/node_modules/mdast-util-mdx-jsx": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.1.3.tgz", + "integrity": "sha512-bfOjvNt+1AcbPLTFMFWY149nJz0OjmewJs3LQQ5pIyVGxP4CdOqNVJL6kTaM5c68p8q82Xv3nCyFfUnuEcH3UQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mintlify/common/node_modules/next-mdx-remote-client": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/next-mdx-remote-client/-/next-mdx-remote-client-2.1.12.tgz", + "integrity": "sha512-KDfhIk5aEM0kkZ0xQ8XBkmo4O5xmJeGWUomm40Ac/xhkt5sIDOTmTU/QFfmJGLJUJipD5o0BAHqVSIZKuXgNEw==", + "dev": true, + "license": "MPL 2.0", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@mdx-js/mdx": "^3.1.1", + "@mdx-js/react": "^3.1.1", + "@types/mdx": "^2.0.14", + "remark-mdx-remove-esm": "^1.3.2", + "serialize-error": "^13.0.1", + "vfile": "^6.0.3", + "vfile-matter": "^5.0.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "peerDependencies": { + "react": ">= 19.1.0", + "react-dom": ">= 19.1.0" + } + }, + "node_modules/@mintlify/common/node_modules/react": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.3.0.tgz", + "integrity": "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@mintlify/common/node_modules/react-dom": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.0.tgz", + "integrity": "sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "scheduler": "^0.28.0" + }, + "peerDependencies": { + "react": "^19.3.0" + } + }, + "node_modules/@mintlify/common/node_modules/remark-gfm": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.0.tgz", + "integrity": "sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mintlify/common/node_modules/remark-mdx": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.0.tgz", + "integrity": "sha512-Ngl/H3YXyBV9RcRNdlYsZujAmhsxwzxpDzpDEhFBVAGthS4GDgnctpDjgFl/ULx5UEDzqtW1cyBSNKqYYrqLBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdast-util-mdx": "^3.0.0", + "micromark-extension-mdxjs": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mintlify/common/node_modules/scheduler": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz", + "integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@mintlify/link-rot": { + "version": "3.0.1352", + "resolved": "https://registry.npmjs.org/@mintlify/link-rot/-/link-rot-3.0.1352.tgz", + "integrity": "sha512-BXlptTk7woLlRdNjfFBmLplJgq4JnEIORcqdNhP8YFDw6Ra5R57PbI6VpBAu5SwhEMliXhu0jLk8exX7Nc6otw==", + "dev": true, + "license": "Elastic-2.0", + "dependencies": { + "@mintlify/common": "1.0.1150", + "@mintlify/models": "0.0.358", + "@mintlify/prebuild": "1.0.1304", + "@mintlify/previewing": "4.0.1376", + "@mintlify/scraping": "4.0.1018", + "@mintlify/validation": "0.1.858", + "fs-extra": "11.1.0", + "unist-util-visit": "4.1.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@mintlify/link-rot/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@mintlify/link-rot/node_modules/fs-extra": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz", + "integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@mintlify/link-rot/node_modules/unist-util-is": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", + "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mintlify/link-rot/node_modules/unist-util-visit": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", + "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.1.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mintlify/link-rot/node_modules/unist-util-visit-parents": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", + "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mintlify/models": { + "version": "0.0.358", + "resolved": "https://registry.npmjs.org/@mintlify/models/-/models-0.0.358.tgz", + "integrity": "sha512-/HCFutweFr2+GiAZ1Jub4+59PSvEgKuXksrW3TPTvtOoZuXbVpRM8WgC+42SyN4HL7JWkMTgJz3UPuqRfzS1Lw==", + "dev": true, + "license": "Elastic-2.0", + "dependencies": { + "axios": "1.18.0", + "openapi-types": "12.1.3" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@mintlify/openapi-parser": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@mintlify/openapi-parser/-/openapi-parser-0.0.8.tgz", + "integrity": "sha512-9MBRq9lS4l4HITYCrqCL7T61MOb20q9IdU7HWhqYMNMM1jGO1nHjXasFy61yZ8V6gMZyyKQARGVoZ0ZrYN48Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.17.1", + "ajv-draft-04": "^1.0.0", + "ajv-formats": "^3.0.1", + "jsonpointer": "^5.0.1", + "leven": "^4.0.0", + "yaml": "^2.4.5" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@mintlify/openapi-parser/node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/@mintlify/prebuild": { + "version": "1.0.1304", + "resolved": "https://registry.npmjs.org/@mintlify/prebuild/-/prebuild-1.0.1304.tgz", + "integrity": "sha512-szLXjqAiJIIEHl+PqQ+8C62ulLodQLbeRFXQZW1BAIn+CiAdlsTH4iwJwNBowUvPlZ3OJwNTcokT+dg6upp/1w==", + "dev": true, + "license": "Elastic-2.0", + "dependencies": { + "@mintlify/common": "1.0.1150", + "@mintlify/scraping": "4.0.1018", + "@mintlify/validation": "0.1.858", + "chalk": "5.3.0", + "favicons": "7.2.0", + "fflate": "^0.8.2", + "fs-extra": "11.1.0", + "js-yaml": "4.3.1", + "openapi-types": "12.1.3", + "sharp": "0.33.5", + "sharp-ico": "0.1.5", + "uuid": "11.1.1" + } + }, + "node_modules/@mintlify/prebuild/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@mintlify/prebuild/node_modules/fs-extra": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz", + "integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@mintlify/previewing": { + "version": "4.0.1376", + "resolved": "https://registry.npmjs.org/@mintlify/previewing/-/previewing-4.0.1376.tgz", + "integrity": "sha512-a0ageOYs/eSg5il30ekroLZO8rEypvHYb8kqh5jHEgiSjvaHc018xAKiUrIfbWmwxorrfqgOc7hqmJl2y03eAw==", + "dev": true, + "license": "Elastic-2.0", + "dependencies": { + "@mintlify/common": "1.0.1150", + "@mintlify/prebuild": "1.0.1304", + "@mintlify/validation": "0.1.858", + "adm-zip": "0.6.0", + "better-opn": "3.0.2", + "chalk": "5.2.0", + "chokidar": "3.5.3", + "express": "4.22.0", + "fs-extra": "11.1.0", + "got": "13.0.0", + "ink": "6.3.0", + "ink-spinner": "5.0.0", + "is-online": "10.0.0", + "js-yaml": "4.3.1", + "react": "19.2.3", + "socket.io": "4.8.0", + "tar": "7.5.21", + "yargs": "17.7.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@mintlify/previewing/node_modules/fs-extra": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz", + "integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@mintlify/scraping": { + "version": "4.0.1018", + "resolved": "https://registry.npmjs.org/@mintlify/scraping/-/scraping-4.0.1018.tgz", + "integrity": "sha512-zF68cwAeeqA2zRSBq1Y+xVwYF7fUAU3Rv2i5us1DMcKkPA6GwyX14RxJ/BM8Ftg3591HV7hDvUqCX28gCTHZQQ==", + "dev": true, + "license": "Elastic-2.0", + "dependencies": { + "@mintlify/common": "1.0.1150", + "fast-xml-parser": "5.7.3", + "fs-extra": "11.1.1", + "graphql": "16.11.0", + "hast-util-to-mdast": "10.1.0", + "js-yaml": "4.3.1", + "mdast-util-mdx-jsx": "3.1.3", + "neotraverse": "0.6.18", + "puppeteer": "24.3.1", + "rehype-parse": "9.0.1", + "remark-gfm": "4.0.0", + "remark-mdx": "3.0.1", + "remark-parse": "11.0.0", + "remark-stringify": "11.0.0", + "unified": "11.0.5", + "unist-util-visit": "5.0.0", + "yargs": "17.7.1", + "zod": "3.24.0" + }, + "bin": { + "mintlify-scrape": "bin/cli.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@mintlify/scraping/node_modules/fs-extra": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@mintlify/scraping/node_modules/mdast-util-mdx-jsx": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.1.3.tgz", + "integrity": "sha512-bfOjvNt+1AcbPLTFMFWY149nJz0OjmewJs3LQQ5pIyVGxP4CdOqNVJL6kTaM5c68p8q82Xv3nCyFfUnuEcH3UQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mintlify/scraping/node_modules/remark-gfm": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.0.tgz", + "integrity": "sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mintlify/scraping/node_modules/remark-mdx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.0.1.tgz", + "integrity": "sha512-3Pz3yPQ5Rht2pM5R+0J2MrGoBSrzf+tJG94N+t/ilfdh8YLyyKYtidAYwTveB20BoHAcwIopOUqhcmh2F7hGYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdast-util-mdx": "^3.0.0", + "micromark-extension-mdxjs": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mintlify/scraping/node_modules/zod": { + "version": "3.24.0", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.0.tgz", + "integrity": "sha512-Hz+wiY8yD0VLA2k/+nsg2Abez674dDGTai33SwNvMPuf9uIrBC9eFgIMQxBBbHFxVXi8W+5nX9DcAh9YNSQm/w==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@mintlify/validation": { + "version": "0.1.858", + "resolved": "https://registry.npmjs.org/@mintlify/validation/-/validation-0.1.858.tgz", + "integrity": "sha512-z8/bnnWB8qPYJ7kGHr88jUXqwipcRZScGH6Oc6niXEqvQGq086RCCXN4jdd5QD3/zrsISkJ64D75Sq9CUWffyQ==", + "dev": true, + "license": "Elastic-2.0", + "dependencies": { + "@mintlify/mdx": "4.0.2", + "@mintlify/models": "0.0.358", + "fractional-indexing": "3.2.0", + "js-yaml": "4.3.1", + "lcm": "0.0.3", + "lodash": "4.18.1", + "neotraverse": "0.6.18", + "object-hash": "3.0.0", + "openapi-types": "12.1.3", + "uuid": "11.1.1", + "zod": "3.24.0", + "zod-to-json-schema": "3.20.4" + } + }, + "node_modules/@mintlify/validation/node_modules/@base-ui/react": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@base-ui/react/-/react-1.8.0.tgz", + "integrity": "sha512-P0/1sxo6SBVZOklKMIedvTWqw2s2IQzi9x5bIVsXu980cuSOD4NeuRSs+/L7LZQfDkZP/uRZyGPyfFl/B1oH+Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.29.7", + "@base-ui/utils": "0.4.0", + "@floating-ui/react-dom": "^2.1.9", + "@floating-ui/utils": "^0.2.12", + "use-sync-external-store": "^1.6.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@date-fns/tz": "^1.2.0", + "@types/react": "^17 || ^18 || ^19", + "date-fns": "^4.0.0", + "react": "^17 || ^18 || ^19", + "react-dom": "^17 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "@date-fns/tz": { + "optional": true + }, + "@types/react": { + "optional": true + }, + "date-fns": { + "optional": true + } + } + }, + "node_modules/@mintlify/validation/node_modules/@base-ui/utils": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@base-ui/utils/-/utils-0.4.0.tgz", + "integrity": "sha512-bO9fz25kKtPf+aZVyfQrC0PDmJdmVni31W2hCS5/Owb+inwdIL3XU26pCPRPlt4LSxZrBgLwubXQXQlKaFEZzw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.29.7", + "@floating-ui/utils": "^0.2.12", + "reselect": "^5.2.0", + "use-sync-external-store": "^1.6.0" + }, + "peerDependencies": { + "@types/react": "^17 || ^18 || ^19", + "react": "^17 || ^18 || ^19", + "react-dom": "^17 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mintlify/validation/node_modules/@floating-ui/react-dom": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.9.tgz", + "integrity": "sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@floating-ui/dom": "^1.8.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@mintlify/validation/node_modules/@mintlify/mdx": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@mintlify/mdx/-/mdx-4.0.2.tgz", + "integrity": "sha512-LrAE0fRxZSvB4POZ50Uy5LO0bKvWbPMciE/TKUyzRk4zbexQYYRhnzz9tdQ7zis//91oikrPnKbJxCco1UlawA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/transformers": "^3.23.0", + "@shikijs/twoslash": "^3.23.0", + "arktype": "^2.1.26", + "hast-util-to-string": "^3.0.1", + "mdast-util-from-markdown": "^2.0.2", + "mdast-util-gfm": "^3.1.0", + "mdast-util-mdx-jsx": "^3.2.0", + "mdast-util-to-hast": "^13.2.0", + "next-mdx-remote-client": "^2.1.11", + "rehype-katex": "^7.0.1", + "remark-gfm": "^4.0.0", + "remark-math": "^6.0.0", + "remark-smartypants": "^3.0.2", + "shiki": "^3.23.0", + "twoslash": "^0.3.4", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0" + }, + "peerDependencies": { + "@base-ui/react": "^1.6.0", + "react": "^19.2.1", + "react-dom": "^19.2.1" + } + }, + "node_modules/@mintlify/validation/node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mintlify/validation/node_modules/next-mdx-remote-client": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/next-mdx-remote-client/-/next-mdx-remote-client-2.1.12.tgz", + "integrity": "sha512-KDfhIk5aEM0kkZ0xQ8XBkmo4O5xmJeGWUomm40Ac/xhkt5sIDOTmTU/QFfmJGLJUJipD5o0BAHqVSIZKuXgNEw==", + "dev": true, + "license": "MPL 2.0", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@mdx-js/mdx": "^3.1.1", + "@mdx-js/react": "^3.1.1", + "@types/mdx": "^2.0.14", + "remark-mdx-remove-esm": "^1.3.2", + "serialize-error": "^13.0.1", + "vfile": "^6.0.3", + "vfile-matter": "^5.0.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "peerDependencies": { + "react": ">= 19.1.0", + "react-dom": ">= 19.1.0" + } + }, + "node_modules/@mintlify/validation/node_modules/react": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.3.0.tgz", + "integrity": "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@mintlify/validation/node_modules/react-dom": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.0.tgz", + "integrity": "sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "scheduler": "^0.28.0" + }, + "peerDependencies": { + "react": "^19.3.0" + } + }, + "node_modules/@mintlify/validation/node_modules/scheduler": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz", + "integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@mintlify/validation/node_modules/zod": { + "version": "3.24.0", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.0.tgz", + "integrity": "sha512-Hz+wiY8yD0VLA2k/+nsg2Abez674dDGTai33SwNvMPuf9uIrBC9eFgIMQxBBbHFxVXi8W+5nX9DcAh9YNSQm/w==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@mintlify/validation/node_modules/zod-to-json-schema": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.20.4.tgz", + "integrity": "sha512-Un9+kInJ2Zt63n6Z7mLqBifzzPcOyX+b+Exuzf7L1+xqck9Q2EPByyTRduV3kmSPaXaRer1JCsucubpgL1fipg==", + "dev": true, + "license": "ISC", + "peerDependencies": { + "zod": "^3.20.0" + } + }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.30.0.tgz", + "integrity": "sha512-xKd8OIzlqNzcqcNumGAa6g+PW2kjD5vrpcKOnfldAUPP3j7lnqMPwlTXQm8gF+UwH72z0lqaRbjr9hqGz0eITA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@hono/node-server": "^1.19.9 || ^2.0.5", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/body-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^2.0.0", + "debug": "^4.4.3", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", + "on-finished": "^2.4.1", + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/body-parser/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/content-disposition": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/media-typer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz", + "integrity": "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/negotiator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.1.0.tgz", + "integrity": "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "content-type": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/qs": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz", + "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/type-is": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/type-is/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@nodable/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-9uGyhaQavEUMC8AIddIjau4NsnsXhou+j5sBAGojCM1oxmQpVKTWR/9JxABD6UAv12vpIms55fPZKFQEhG6uBg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/nodable" + } + ], + "license": "MIT" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@openai/codex": { + "version": "0.149.1", + "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.149.1.tgz", + "integrity": "sha512-6q5pbcpFbJbqOpkubSDBwXmktQ55aD8eUzGzBF1zASob2DjwhBKDSNGtdZKalfrNJUdTDTPDMmzCXEXs5tMBYA==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "bin": { + "codex": "bin/codex.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "@openai/codex-darwin-arm64": "npm:@openai/codex@0.149.1-darwin-arm64", + "@openai/codex-darwin-x64": "npm:@openai/codex@0.149.1-darwin-x64", + "@openai/codex-linux-arm64": "npm:@openai/codex@0.149.1-linux-arm64", + "@openai/codex-linux-x64": "npm:@openai/codex@0.149.1-linux-x64", + "@openai/codex-win32-arm64": "npm:@openai/codex@0.149.1-win32-arm64", + "@openai/codex-win32-x64": "npm:@openai/codex@0.149.1-win32-x64" + } + }, + "node_modules/@openai/codex-darwin-arm64": { + "name": "@openai/codex", + "version": "0.149.1-darwin-arm64", + "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.149.1-darwin-arm64.tgz", + "integrity": "sha512-6X84kTCbnTgPIJ2EdcPsrvwS0Wxsqpa+bCswGmRf4BjhcQ5nPMnBC6yCAaCMj+vrbXQHj+L6sa9FaR4QkmA1qw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@openai/codex-darwin-x64": { + "name": "@openai/codex", + "version": "0.149.1-darwin-x64", + "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.149.1-darwin-x64.tgz", + "integrity": "sha512-MfLBQLfcElJL9tvj6y45qVHHMGSXCPnQOixuD3/Zq0g1BW/eFizkrGLdn48cFpc+l8cK+gt5nYG5pQYwVs6g4A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@openai/codex-linux-arm64": { + "name": "@openai/codex", + "version": "0.149.1-linux-arm64", + "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.149.1-linux-arm64.tgz", + "integrity": "sha512-OqxUfZ1TVvHd18zHPKK/8ZRlpk8Vy11mg5CMHaLxNWldTbwVImDKtSLWT+m8m4NM5Sz4PbjtZMrVT/RfpBW/mQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@openai/codex-linux-x64": { + "name": "@openai/codex", + "version": "0.149.1-linux-x64", + "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.149.1-linux-x64.tgz", + "integrity": "sha512-Of5fGYgr7tAMsyj6vhXb4/RM/UoA3Zq8BLegUBDC09UNy1XTLGYP/2XD+UX8z3qh0NDwxYdCjFIWdDNijKZggQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@openai/codex-sdk": { + "version": "0.149.1", + "resolved": "https://registry.npmjs.org/@openai/codex-sdk/-/codex-sdk-0.149.1.tgz", + "integrity": "sha512-R00Rz5327LefZggAxl28r7vFQq1vxa91OxtjZJOsQfAM/MyH8InW5qwwRu6pzUmRGp1E29XrOzm7u1TeV5Yz2A==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@openai/codex": "0.149.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@openai/codex-win32-arm64": { + "name": "@openai/codex", + "version": "0.149.1-win32-arm64", + "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.149.1-win32-arm64.tgz", + "integrity": "sha512-5K0DmOKGK9Bos627p8sK8ATHjovPK0sDyT6h9Cb+4v+5CW5SGw1HLgjGxoLfJ8g3cg6mtg/pRCXXo2L/j71UVA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@openai/codex-win32-x64": { + "name": "@openai/codex", + "version": "0.149.1-win32-x64", + "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.149.1-win32-x64.tgz", + "integrity": "sha512-G3QXGAg7nyyhqOeooAMUekBCeHd8a1QByhKcVAFyzNBaI06t6Ft7nsF+1SzFS0spuIdU4YyMi5YD26ukADBQUQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@openapi-contrib/openapi-schema-to-json-schema": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@openapi-contrib/openapi-schema-to-json-schema/-/openapi-schema-to-json-schema-3.2.0.tgz", + "integrity": "sha512-Gj6C0JwCr8arj0sYuslWXUBSP/KnUlEGnPW4qxlXvAl543oaNQgMgIgkQUA6vs5BCCvwTEiL8m/wdWzfl4UvSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + } + }, + "node_modules/@posthog/core": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.7.1.tgz", + "integrity": "sha512-kjK0eFMIpKo9GXIbts8VtAknsoZ18oZorANdtuTj1CbgS28t4ZVq//HAWhnxEuXRTrtkd+SUJ6Ux3j2Af8NCuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.6" + } + }, + "node_modules/@puppeteer/browsers": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.7.1.tgz", + "integrity": "sha512-MK7rtm8JjaxPN7Mf1JdZIZKPD2Z+W7osvrC1vjpvfOX1K0awDIHYbNi89f7eotp7eMUn2shWnt03HwVbriXtKQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.4.0", + "extract-zip": "^2.0.1", + "progress": "^2.0.3", + "proxy-agent": "^6.5.0", + "semver": "^7.7.0", + "tar-fs": "^3.0.8", + "yargs": "^17.7.2" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@puppeteer/browsers/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@puppeteer/browsers/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@puppeteer/browsers/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@puppeteer/browsers/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@puppeteer/browsers/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@puppeteer/browsers/node_modules/tar-fs": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.3.tgz", + "integrity": "sha512-/hU4AXnIdZu+Gvl1pk0oI5f5HxWsCJRtY2aFaJdk9VvyL48DWU6iU5WAIPG+wIi1YvWA6eTJvIviP/tMAZZNwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" + } + }, + "node_modules/@puppeteer/browsers/node_modules/tar-stream": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.1.tgz", + "integrity": "sha512-nqsEO8zLZJvrOMdEwkA0QdCLFbetHMn95Zqu4fKwX+hkaTWJPZZOrxx/PwtxoK0MMGQmBQNRW3CPs8IFYQz4cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "bare-fs": "^4.5.5", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/@puppeteer/browsers/node_modules/yargs": { + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@scarf/scarf": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", + "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0" + }, + "node_modules/@shikijs/core": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.23.0.tgz", + "integrity": "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.5" + } + }, + "node_modules/@shikijs/core/node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.23.0.tgz", + "integrity": "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^4.3.4" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.23.0.tgz", + "integrity": "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0", + "@shikijs/vscode-textmate": "^10.0.2" + } + }, + "node_modules/@shikijs/langs": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.23.0.tgz", + "integrity": "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0" + } + }, + "node_modules/@shikijs/themes": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.23.0.tgz", + "integrity": "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0" + } + }, + "node_modules/@shikijs/transformers": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-3.23.0.tgz", + "integrity": "sha512-F9msZVxdF+krQNSdQ4V+Ja5QemeAoTQ2jxt7nJCwhDsdF1JWS3KxIQXA3lQbyKwS3J61oHRUSv4jYWv3CkaKTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/core": "3.23.0", + "@shikijs/types": "3.23.0" + } + }, + "node_modules/@shikijs/twoslash": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/twoslash/-/twoslash-3.23.0.tgz", + "integrity": "sha512-pNaLJWMA3LU7PhT8tm9OQBZ1epy0jmdgeJzntBtr1EVXLbHxGzTj3mnf9vOdcl84l96qnlJXkJ/NGXZYBpXl5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/core": "3.23.0", + "@shikijs/types": "3.23.0", + "twoslash": "^0.3.6" + }, + "peerDependencies": { + "typescript": ">=5.5.0" + } + }, + "node_modules/@shikijs/types": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.23.0.tgz", + "integrity": "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sindresorhus/is": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@sindresorhus/slugify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/slugify/-/slugify-2.2.0.tgz", + "integrity": "sha512-9Vybc/qX8Kj6pxJaapjkFbiUJPk7MAkCh/GFCxIBnnsuYCFPIXKvnLidG8xlepht3i24L5XemUmGtrJ3UWrl6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/transliterate": "^1.0.0", + "escape-string-regexp": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@sindresorhus/transliterate": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/transliterate/-/transliterate-1.6.0.tgz", + "integrity": "sha512-doH1gimEu3A46VX6aVxpHTeHrytJAG6HgdxntYnCFiIFHEM/ZGpG8KiZGBChchjQmG0XFIBL552kBTjVcMZXwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@stablelib/base64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz", + "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@stoplight/better-ajv-errors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@stoplight/better-ajv-errors/-/better-ajv-errors-1.0.3.tgz", + "integrity": "sha512-0p9uXkuB22qGdNfy3VeEhxkU5uwvp/KrBTAbrLBURv6ilxIVwanKwjMc41lQfIVgPGcOkmLbTolfFrSsueu7zA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jsonpointer": "^5.0.0", + "leven": "^3.1.0" + }, + "engines": { + "node": "^12.20 || >= 14.13" + }, + "peerDependencies": { + "ajv": ">=8" + } + }, + "node_modules/@stoplight/better-ajv-errors/node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@stoplight/json": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/@stoplight/json/-/json-3.21.0.tgz", + "integrity": "sha512-5O0apqJ/t4sIevXCO3SBN9AHCEKKR/Zb4gaj7wYe5863jme9g02Q0n/GhM7ZCALkL+vGPTe4ZzTETP8TFtsw3g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@stoplight/ordered-object-literal": "^1.0.3", + "@stoplight/path": "^1.3.2", + "@stoplight/types": "^13.6.0", + "jsonc-parser": "~2.2.1", + "lodash": "^4.17.21", + "safe-stable-stringify": "^1.1" + }, + "engines": { + "node": ">=8.3.0" + } + }, + "node_modules/@stoplight/json-ref-readers": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@stoplight/json-ref-readers/-/json-ref-readers-1.2.2.tgz", + "integrity": "sha512-nty0tHUq2f1IKuFYsLM4CXLZGHdMn+X/IwEUIpeSOXt0QjMUbL0Em57iJUDzz+2MkWG83smIigNZ3fauGjqgdQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-fetch": "^2.6.0", + "tslib": "^1.14.1" + }, + "engines": { + "node": ">=8.3.0" + } + }, + "node_modules/@stoplight/json-ref-resolver": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@stoplight/json-ref-resolver/-/json-ref-resolver-3.1.6.tgz", + "integrity": "sha512-YNcWv3R3n3U6iQYBsFOiWSuRGE5su1tJSiX6pAPRVk7dP0L7lqCteXGzuVRQ0gMZqUl8v1P0+fAKxF6PLo9B5A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@stoplight/json": "^3.21.0", + "@stoplight/path": "^1.3.2", + "@stoplight/types": "^12.3.0 || ^13.0.0", + "@types/urijs": "^1.19.19", + "dependency-graph": "~0.11.0", + "fast-memoize": "^2.5.2", + "immer": "^9.0.6", + "lodash": "^4.17.21", + "tslib": "^2.6.0", + "urijs": "^1.19.11" + }, + "engines": { + "node": ">=8.3.0" + } + }, + "node_modules/@stoplight/json-ref-resolver/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@stoplight/json/node_modules/jsonc-parser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.2.1.tgz", + "integrity": "sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@stoplight/ordered-object-literal": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@stoplight/ordered-object-literal/-/ordered-object-literal-1.0.5.tgz", + "integrity": "sha512-COTiuCU5bgMUtbIFBuyyh2/yVVzlr5Om0v5utQDgBCuQUOPgU1DwoffkTfg4UBQOvByi5foF4w4T+H9CoRe5wg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/@stoplight/path": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@stoplight/path/-/path-1.3.2.tgz", + "integrity": "sha512-lyIc6JUlUA8Ve5ELywPC8I2Sdnh1zc1zmbYgVarhXIp9YeAB0ReeqmGEOWNtlHkbP2DAA1AL65Wfn2ncjK/jtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/@stoplight/spectral-core": { + "version": "1.23.1", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-core/-/spectral-core-1.23.1.tgz", + "integrity": "sha512-VLC8OhpO/pMJKb6IHhurxJjXO1qB56Ng1unIb8b+hNxdw0+SEcASvmR+RpjfHYX/jv/DfSaA1x8QhFBJBmqBOQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@scarf/scarf": "^1.4.0", + "@stoplight/better-ajv-errors": "1.0.3", + "@stoplight/json": "~3.21.0", + "@stoplight/path": "1.3.2", + "@stoplight/spectral-parsers": "^1.0.0", + "@stoplight/spectral-ref-resolver": "^1.0.4", + "@stoplight/spectral-runtime": "^1.1.2", + "@stoplight/types": "~13.6.0", + "@types/es-aggregate-error": "^1.0.2", + "@types/json-schema": "^7.0.11", + "ajv": "^8.18.0", + "ajv-errors": "~3.0.0", + "ajv-formats": "~2.1.1", + "es-aggregate-error": "^1.0.7", + "expr-eval-fork": "^3.0.1", + "jsonpath-plus": "^10.3.0", + "lodash": "^4.18.1", + "lodash.topath": "^4.5.2", + "minimatch": "^3.1.4", + "nimma": "0.2.3", + "pony-cause": "^1.1.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": "^16.20 || ^18.18 || >= 20.17" + } + }, + "node_modules/@stoplight/spectral-core/node_modules/@stoplight/types": { + "version": "13.6.0", + "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.6.0.tgz", + "integrity": "sha512-dzyuzvUjv3m1wmhPfq82lCVYGcXG0xUYgqnWfCq3PCVR4BKFhjdkHrnJ+jIDoMKvXb05AZP/ObQF6+NpDo29IQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.4", + "utility-types": "^3.10.0" + }, + "engines": { + "node": "^12.20 || >=14.13" + } + }, + "node_modules/@stoplight/spectral-core/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@stoplight/spectral-formats": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-formats/-/spectral-formats-1.8.5.tgz", + "integrity": "sha512-xaC0rCH0p7/bzNJsz+JgLSj+Cp6uwYGWpePQxdLkF2G6a8Zyp3OyS7umkGYNiimEwKrOjvCNNTFJpeuiENZSBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@scarf/scarf": "^1.4.0", + "@stoplight/json": "^3.17.0", + "@stoplight/spectral-core": "^1.23.0", + "@types/json-schema": "^7.0.7", + "tslib": "^2.8.1" + }, + "engines": { + "node": "^16.20 || ^18.18 || >= 20.17" + } + }, + "node_modules/@stoplight/spectral-formats/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@stoplight/spectral-functions": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-functions/-/spectral-functions-1.10.5.tgz", + "integrity": "sha512-vDCd0NJ93715bcUpZZ5vNHiyxd4cgHF6tuXsDiXOXKAByg+I1fR5/dMijEo6Ce1Lz95a+RZ22JKYhF1YuzVvuA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@scarf/scarf": "^1.4.0", + "@stoplight/better-ajv-errors": "1.0.3", + "@stoplight/json": "^3.17.1", + "@stoplight/spectral-core": "^1.23.0", + "@stoplight/spectral-formats": "^1.8.1", + "@stoplight/spectral-runtime": "^1.1.2", + "ajv": "^8.18.0", + "ajv-draft-04": "~1.0.0", + "ajv-errors": "~3.0.0", + "ajv-formats": "~2.1.1", + "lodash": "^4.18.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": "^16.20 || ^18.18 || >= 20.17" + } + }, + "node_modules/@stoplight/spectral-functions/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@stoplight/spectral-parsers": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-parsers/-/spectral-parsers-1.0.5.tgz", + "integrity": "sha512-ANDTp2IHWGvsQDAY85/jQi9ZrF4mRrA5bciNHX+PUxPr4DwS6iv4h+FVWJMVwcEYdpyoIdyL+SRmHdJfQEPmwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@stoplight/json": "~3.21.0", + "@stoplight/types": "^14.1.1", + "@stoplight/yaml": "~4.3.0", + "tslib": "^2.8.1" + }, + "engines": { + "node": "^16.20 || ^18.18 || >= 20.17" + } + }, + "node_modules/@stoplight/spectral-parsers/node_modules/@stoplight/types": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-14.1.1.tgz", + "integrity": "sha512-/kjtr+0t0tjKr+heVfviO9FrU/uGLc+QNX3fHJc19xsCNYqU7lVhaXxDmEID9BZTjG+/r9pK9xP/xU02XGg65g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.4", + "utility-types": "^3.10.0" + }, + "engines": { + "node": "^12.20 || >=14.13" + } + }, + "node_modules/@stoplight/spectral-parsers/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@stoplight/spectral-ref-resolver": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-ref-resolver/-/spectral-ref-resolver-1.0.5.tgz", + "integrity": "sha512-gj3TieX5a9zMW29z3mBlAtDOCgN3GEc1VgZnCVlr5irmR4Qi5LuECuFItAq4pTn5Zu+sW5bqutsCH7D4PkpyAA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@stoplight/json-ref-readers": "1.2.2", + "@stoplight/json-ref-resolver": "~3.1.6", + "@stoplight/spectral-runtime": "^1.1.2", + "dependency-graph": "0.11.0", + "tslib": "^2.8.1" + }, + "engines": { + "node": "^16.20 || ^18.18 || >= 20.17" + } + }, + "node_modules/@stoplight/spectral-ref-resolver/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@stoplight/spectral-runtime": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-runtime/-/spectral-runtime-1.1.6.tgz", + "integrity": "sha512-Y8rEDyMN4bSMJCrDs2shdcVHYyCnH3FvXRP4dBhha4Z8iJv+JPp7KqOV/hwVB/hWFC209upiwj2oDmLfR0qCDg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@stoplight/json": "^3.20.1", + "@stoplight/path": "^1.3.2", + "@stoplight/types": "^13.6.0", + "lodash": "^4.18.1", + "node-fetch": "^2.7.0", + "tslib": "^2.8.1" + }, + "engines": { + "node": "^16.20 || ^18.18 || >= 20.17" + } + }, + "node_modules/@stoplight/spectral-runtime/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@stoplight/spectral-runtime/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@stoplight/types": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.20.0.tgz", + "integrity": "sha512-2FNTv05If7ib79VPDA/r9eUet76jewXFH2y2K5vuge6SXbRHtWBhcaRmu+6QpF4/WRNoJj5XYRSwLGXDxysBGA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.4", + "utility-types": "^3.10.0" + }, + "engines": { + "node": "^12.20 || >=14.13" + } + }, + "node_modules/@stoplight/yaml": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@stoplight/yaml/-/yaml-4.3.0.tgz", + "integrity": "sha512-JZlVFE6/dYpP9tQmV0/ADfn32L9uFarHWxfcRhReKUnljz1ZiUM5zpX+PH8h5CJs6lao3TuFqnPm9IJJCEkE2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@stoplight/ordered-object-literal": "^1.0.5", + "@stoplight/types": "^14.1.1", + "@stoplight/yaml-ast-parser": "0.0.50", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=10.8" + } + }, + "node_modules/@stoplight/yaml-ast-parser": { + "version": "0.0.50", + "resolved": "https://registry.npmjs.org/@stoplight/yaml-ast-parser/-/yaml-ast-parser-0.0.50.tgz", + "integrity": "sha512-Pb6M8TDO9DtSVla9yXSTAxmo9GVEouq5P40DWXdOie69bXogZTkgvopCq+yEvTMA0F6PEvdJmbtTV3ccIp11VQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@stoplight/yaml/node_modules/@stoplight/types": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-14.1.1.tgz", + "integrity": "sha512-/kjtr+0t0tjKr+heVfviO9FrU/uGLc+QNX3fHJc19xsCNYqU7lVhaXxDmEID9BZTjG+/r9pK9xP/xU02XGg65g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.4", + "utility-types": "^3.10.0" + }, + "engines": { + "node": "^12.20 || >=14.13" + } + }, + "node_modules/@stoplight/yaml/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.1" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/es-aggregate-error": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/es-aggregate-error/-/es-aggregate-error-1.0.6.tgz", + "integrity": "sha512-qJ7LIFp06h1QE1aVxbVd+zJP2wdaugYXYfd6JxsyRMrYHaxb6itXPogW2tz+ylUJ1n1b+JF1PHyYCfYHm0dvUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz", + "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/katex": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.8.tgz", + "integrity": "sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdx": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.14.tgz", + "integrity": "sha512-T48PeuJtvLosNTPVhfnIp3i/n3a4g4Bad7YCq5k64D4u7NwDrAotikQ+5+sjtUvBmxCMlbo3dVL+C2dP0rWHzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/nlcst": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz", + "integrity": "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/node": { + "version": "26.6.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.1.tgz", + "integrity": "sha512-VqGJBMCtdhqkBUCcBLvywI0NJ+KLuVzgNnlBUNFOQjqVxzo2lxLUNg1DSey8+u2u6ktswSAxg+s68QLzWHNOuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~8.9.0" + } + }, + "node_modules/@types/react": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.3.0.tgz", + "integrity": "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/urijs": { + "version": "1.19.26", + "resolved": "https://registry.npmjs.org/@types/urijs/-/urijs-1.19.26.tgz", + "integrity": "sha512-wkXrVzX5yoqLnndOwFsieJA7oKM8cNkOKJtf/3vVGSUFkWDKZvFHpIl9Pvqb/T9UsawBBFMTTD8xu7sK5MWuvg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript/vfs": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.6.4.tgz", + "integrity": "sha512-PJFXFS4ZJKiJ9Qiuix6Dz/OwEIqHD7Dme1UwZhTK11vR+5dqW2ACbdndWQexBzCx+CPuMe5WBYQWCsFyGlQLlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.3" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.4.0.tgz", + "integrity": "sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/address": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", + "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/adm-zip": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.6.0.tgz", + "integrity": "sha512-XleryMhbuksdKtofnWZ9Sk+4CUTbms4Mb/EU32SZwToAyZ5RgVos/ki8n+yr0LWHOGKuakbXTuuYNHLQjhddgg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-4.0.1.tgz", + "integrity": "sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^4.0.0", + "indent-string": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-errors": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-3.0.0.tgz", + "integrity": "sha512-V3wD15YHfHz6y0KdhYFjyy9vWtEVALT9UrxfN3zqlI6dMioHnJrqOYfyPKol3oqrnCM9uwkcdCwkJ0WUcbLMTQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^8.0.1" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-escapes": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", + "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anynum": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.1.tgz", + "integrity": "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/arkregex": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/arkregex/-/arkregex-0.0.8.tgz", + "integrity": "sha512-PJcx6G1kQTgLKPUbeYlYecDRaKq15AMSGVajlKFYWlPeJRQL+j3dKE6tyMs40HZ99djS1l9Vhl3ezAHy9JBIqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ark/util": "0.56.2" + } + }, + "node_modules/arktype": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/arktype/-/arktype-2.2.3.tgz", + "integrity": "sha512-7W+0RLTUNJiBFIIZXwOQxSR8Z273IAd6IvqBeG9+gHnQKFsIx2C0iOtGTmMrPnlX4qLXyc5+ll7A0BIj9WrbTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ark/schema": "0.56.2", + "@ark/util": "0.56.2", + "arkregex": "0.0.8" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/array-iterate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-2.0.1.tgz", + "integrity": "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ast-types/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/astring": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", + "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", + "dev": true, + "license": "MIT", + "bin": { + "astring": "bin/astring" + } + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/auto-bind": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-5.0.1.tgz", + "integrity": "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/avsc": { + "version": "5.7.9", + "resolved": "https://registry.npmjs.org/avsc/-/avsc-5.7.9.tgz", + "integrity": "sha512-yOA4wFeI7ET3v32Di/sUybQ+ttP20JHSW3mxLuNGeO0uD6PPcvLrIQXSvy/rhJOWU5JrYh7U4OHplWMmtAtjMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.11" + } + }, + "node_modules/axios": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.0.tgz", + "integrity": "sha512-E32NzpYKp++W7XRe52rHiXV2ehxmh3wbdgO7MHeFM+vqxLBYHzt0ElkiImtOBxtOmyp0yoC8C6uESVV84Y2/hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/b4a": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.9.0.tgz", + "integrity": "sha512-dpfcF9fDNR6++cthXR67iyhgqWy9CBouAvIWhIntzBG6cvK/cnIPiZQjBwi/ZqjjBEDGfoNDtmB0kTjroOJ3pQ==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/bare-events": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.2.tgz", + "integrity": "sha512-AIPKioV7/Y/8KfZ3AAhjPJxLLbY49S64Ym5DakZlUg75qQiTgUq9hEJoEwa4eUezPUlXRy/i5NpsKvo9jgKmoA==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/bare-fs": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.8.1.tgz", + "integrity": "sha512-N1nnXdHZAOSstz0XiHikGS4HGMH4CnSwhqWdGQQMqqdvp4Jybm9sE3R1WVnpWVd4SFkc8ryPDBLViNLwiEqECg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" + }, + "engines": { + "bare": ">=1.28.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-path": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.1.2.tgz", + "integrity": "sha512-ZyKbsuuqK6Ag0K8pX6V5Txq6XeJRvY+wXucnFGRjiyVYP9YWDpIQugk/b+enRYrEYBJaqLzghRQpXPMR7341Nw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/bare-stream": { + "version": "2.13.4", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.4.tgz", + "integrity": "sha512-PcrQ8lVLbiJscNm1Kez+Yp4Gy4AHGcN1lzwjvf5NybWen7VvEgUfyfnXYJ2zNqWnzOfCb1Abq6lH8ti0syQszA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.8.1", + "streamx": "^2.25.0", + "teex": "^1.0.1" + }, + "peerDependencies": { + "bare-abort-controller": "*", + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + }, + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } + } + }, + "node_modules/bare-url": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.5.4.tgz", + "integrity": "sha512-Gxa7UVWBr0/edU1b+TJhn/AZvMQUj9OGspvYsaTYQrAbZA4BOTZGL3LiZxvD+CeMlDH4juwD84+eTAp/bLYW5g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-path": "^3.0.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/basic-ftp": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.3.1.tgz", + "integrity": "sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/better-opn": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/better-opn/-/better-opn-3.0.2.tgz", + "integrity": "sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "open": "^8.0.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.8", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.8.tgz", + "integrity": "sha512-JNcyFQ64OiijEkPzUBTCe+hyPXUD/3LEldGQ6iF5LR1w00mx9o7xtDWHXBY2iItjdCFGoilOLNQbH943ut7pHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.16.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/body-parser/node_modules/qs": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz", + "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/body-parser/node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/cacheable-request": { + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "^4.0.2", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chalk": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chardet": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.2.0.tgz", + "integrity": "sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==", + "dev": true, + "license": "MIT" + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/chromium-bidi": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-2.1.2.tgz", + "integrity": "sha512-vtRWBK2uImo5/W2oG6/cDkkHSm+2t6VHgnj+Rcwhb0pP74OoUb4GipyRX/T/y39gYQPhioP0DPShn+A7P6CHNw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "mitt": "^3.0.1", + "zod": "^3.24.1" + }, + "peerDependencies": { + "devtools-protocol": "*" + } + }, + "node_modules/chromium-bidi/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/clean-stack": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-4.2.0.tgz", + "integrity": "sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/code-excerpt": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/code-excerpt/-/code-excerpt-4.0.0.tgz", + "integrity": "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==", + "dev": true, + "license": "MIT", + "dependencies": { + "convert-to-spaces": "^2.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/collapse-white-space": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", + "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-to-spaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz", + "integrity": "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cosmiconfig": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz", + "integrity": "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssfilter": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz", + "integrity": "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-bmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/decode-bmp/-/decode-bmp-0.2.1.tgz", + "integrity": "sha512-NiOaGe+GN0KJqi2STf24hfMkFitDUaIoUU3eKvP/wAbLe8o6FuW5n/x7MHPR0HKvBokp6MQY/j7w8lewEeVCIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@canvas/image-data": "^1.0.0", + "to-data-view": "^1.1.0" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/decode-ico": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/decode-ico/-/decode-ico-0.4.1.tgz", + "integrity": "sha512-69NZfbKIzux1vBOd31al3XnMnH+2mqDhEgLdpygErm4d60N+UwA5Sq5WFjmEDQzumgB9fElojGwWG0vybVfFmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@canvas/image-data": "^1.0.0", + "decode-bmp": "^0.2.0", + "to-data-view": "^1.1.0" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", + "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dependency-graph": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", + "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-port": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.5.1.tgz", + "integrity": "sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "address": "^1.0.1", + "debug": "4" + }, + "bin": { + "detect": "bin/detect-port.js", + "detect-port": "bin/detect-port.js" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/devtools-protocol": { + "version": "0.0.1402036", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1402036.tgz", + "integrity": "sha512-JwAYQgEvm3yD45CHB+RmF5kMbWtXBaOGwuxa87sZogHcLCv8c/IqnThaoQ1y60d7pXWjSKWQphPEc+1rAScVdg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dns-socket": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/dns-socket/-/dns-socket-4.2.2.tgz", + "integrity": "sha512-BDeBd8najI4/lS00HSKpdFia+OvUMytaVjfzR9n5Lq8MlZRSvtbI+uLtx1+XmQFls5wFU9dssccTmQQ6nfpjdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "dns-packet": "^5.2.4" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/engine.io": { + "version": "6.6.10", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.10.tgz", + "integrity": "sha512-9/lX2bdlizlCXMHRMOIm03VBQHQYC7VvydcxtTAUJRxNW1QzM/2PMFSmr6h/lCiMHcyCP6abK+t9Q+j4vekk8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "@types/ws": "^8.5.12", + "accepts": "~1.3.4", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.4.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.21.0" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.24.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", + "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-abstract-get": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-abstract-get/-/es-abstract-get-1.0.0.tgz", + "integrity": "sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.2", + "is-callable": "^1.2.7", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-aggregate-error": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/es-aggregate-error/-/es-aggregate-error-1.0.14.tgz", + "integrity": "sha512-3YxX6rVb07B5TV11AV5wsL7nQCHXNwoHPsQC8S4AmBiqYhyNCJ5BRKXkXyDJvs8QzXN20NgRtxe3dEEQD9NLHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "globalthis": "^1.0.4", + "has-property-descriptors": "^1.0.2", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.4.tgz", + "integrity": "sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-abstract-get": "^1.0.0", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "is-callable": "^1.2.7", + "is-date-object": "^1.1.0", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-toolkit": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.52.0.tgz", + "integrity": "sha512-XTNEJQh1tY1ZJVcf6ayP/2n4ZPyaHlW2FWs7xvw5ddPuhUVjLD3olQVQS7kf58JbAB48iL0uL/jerTrjtV3lDA==", + "dev": true, + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks", + "tests/types", + "tests/browser-compat" + ] + }, + "node_modules/esast-util-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz", + "integrity": "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/esast-util-from-js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz", + "integrity": "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "acorn": "^8.0.0", + "esast-util-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-util-attach-comments": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", + "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-build-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", + "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-walker": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-scope": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-util-scope/-/estree-util-scope-1.0.1.tgz", + "integrity": "sha512-B0np3dcdxqILX5e9nEi5/Fr4K7gL4oYFVPV1zRa2e9wRCbQoZZNWOZFYyoInvXUPJXBXjss+QXlWLJChDEHDkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-to-js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", + "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "astring": "^1.8.0", + "source-map": "^0.7.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-visit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", + "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.1.tgz", + "integrity": "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, + "license": "(MIT OR WTFPL)", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/expr-eval-fork": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/expr-eval-fork/-/expr-eval-fork-3.0.3.tgz", + "integrity": "sha512-BhC+hbc5lIVjygr840n5DEkW3MQq7H9o+mc1/N7Z5uIiCFVyESLL5DIE7LNq4CYUNxy+XjA+3jRrL/h0Kt2xcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/express": { + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.0.tgz", + "integrity": "sha512-c2iPh3xp5vvCLgaHK03+mWLFPhox7j1LwyxcZwFVApEv5i0X+IjPpbT50SJJwwLpdBVfp45AkK/v+AFgv/XlfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.14.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.7.0.tgz", + "integrity": "sha512-hOwV7WOxXfjRpAM1DSJWZDXx3GhplwD8IfwuwvogD8i1Qnkgosw/H45s4ZnFAUHDAhPjlY9hLBvJhKmGMyY26g==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "^4.4.3", + "ip-address": "^10.2.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extract-zip/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-memoize": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/fast-memoize/-/fast-memoize-2.5.2.tgz", + "integrity": "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-sha256": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz", + "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==", + "dev": true, + "license": "Unlicense", + "optional": true + }, + "node_modules/fast-uri": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", + "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fast-xml-builder": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.3.1.tgz", + "integrity": "sha512-pIM/1n3ntFXKYrUZwW7QCK0gAW7XY+wzj1YMIV3tLDvPj/V+zTGJK5e3/4WJfwj0qWw2ElNXiTixda/R+3YSug==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "path-expression-matcher": "^1.6.2", + "xml-naming": "^0.3.0" + } + }, + "node_modules/fast-xml-parser": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.7.3.tgz", + "integrity": "sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "@nodable/entities": "^2.1.0", + "fast-xml-builder": "^1.1.7", + "path-expression-matcher": "^1.5.0", + "strnum": "^2.2.3" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fastq": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.3.tgz", + "integrity": "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fault": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", + "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/favicons": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/favicons/-/favicons-7.2.0.tgz", + "integrity": "sha512-k/2rVBRIRzOeom3wI9jBPaSEvoTSQEW4iM0EveBmBBKFxO8mSyyRWtDlfC3VnEfu0avmjrMzy8/ZFPSe6F71Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-html": "^1.0.3", + "sharp": "^0.33.1", + "xml2js": "^0.6.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fflate": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz", + "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==", + "dev": true, + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/form-data": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data-encoder": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.17" + } + }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fractional-indexing": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fractional-indexing/-/fractional-indexing-3.2.0.tgz", + "integrity": "sha512-PcOxmqwYCW7O2ovKRU8OoQQj2yqTfEB/yeTYk4gPid6dN5ODRfU1hXd9tTVZzax/0NkO7AxpHykvZnT1aYp/BQ==", + "dev": true, + "license": "CC0-1.0", + "engines": { + "node": "^14.13.1 || >=16.0.0" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/front-matter": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/front-matter/-/front-matter-4.0.2.tgz", + "integrity": "sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-yaml": "^3.13.1" + } + }, + "node_modules/front-matter/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/front-matter/node_modules/js-yaml": { + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.2.tgz", + "integrity": "sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.2.0.tgz", + "integrity": "sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2", + "hasown": "^2.0.4", + "is-callable": "^1.2.7", + "is-document.all": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gcd": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/gcd/-/gcd-0.0.1.tgz", + "integrity": "sha512-VNx3UEGr+ILJTiMs1+xc5SX1cMgJCrXezKPa003APUWNqQqaF6n25W8VcR7nHN6yRWbvvUTwCpZCFJeWC2kXlw==", + "dev": true, + "license": "MIT" + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.7.0.tgz", + "integrity": "sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-uri": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", + "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/got/-/got-13.0.0.tgz", + "integrity": "sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/graphql": { + "version": "16.11.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.11.0.tgz", + "integrity": "sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hast-util-embedded": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-embedded/-/hast-util-embedded-3.0.0.tgz", + "integrity": "sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-is-element": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-dom": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/hast-util-from-dom/-/hast-util-from-dom-5.0.1.tgz", + "integrity": "sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "@types/hast": "^3.0.0", + "hastscript": "^9.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", + "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html-isomorphic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hast-util-from-html-isomorphic/-/hast-util-from-html-isomorphic-2.0.0.tgz", + "integrity": "sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-from-dom": "^5.0.0", + "hast-util-from-html": "^2.0.0", + "unist-util-remove-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", + "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^7.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-has-property": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-3.0.0.tgz", + "integrity": "sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-body-ok-link": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-is-body-ok-link/-/hast-util-is-body-ok-link-3.0.1.tgz", + "integrity": "sha512-0qpnzOBLztXHbHQenVB8uNuxTnm/QBFUOmdOSsEn7GnBtyY07+ENTWVFBAnXd/zEgd9/SUG3lRY7hSIBWRgGpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-minify-whitespace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hast-util-minify-whitespace/-/hast-util-minify-whitespace-1.0.1.tgz", + "integrity": "sha512-L96fPOVpnclQE0xzdWb/D12VT5FabA7SnZOUMtL1DbXmYiHJMXZvFkIZfiMmTCNJHUeO2K9UYNXoVyfz+QHuOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-embedded": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-phrasing": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-phrasing/-/hast-util-phrasing-3.0.1.tgz", + "integrity": "sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-embedded": "^3.0.0", + "hast-util-has-property": "^3.0.0", + "hast-util-is-body-ok-link": "^3.0.0", + "hast-util-is-element": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-estree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.0.tgz", + "integrity": "sha512-lfX5g6hqVh9kjS/B9E2gSkvHH4SZNiQFiqWS0x9fENzEl+8W12RqdRxX6d/Cwxi30tPQs3bIO+aolQJNp1bIyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-attach-comments": "^3.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^0.4.0", + "unist-util-position": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-estree/node_modules/property-information": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.4.tgz", + "integrity": "sha512-wxQzXtdbhiwGAUKrnQJXlOPmHnEehzphwkK7aluUPQ+lEc1xefC8pblMgpp2w5ldBTEfveRIrADcrhGIWrlTDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-html/node_modules/property-information": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-mdast": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/hast-util-to-mdast/-/hast-util-to-mdast-10.1.0.tgz", + "integrity": "sha512-DsL/SvCK9V7+vfc6SLQ+vKIyBDXTk2KLSbfBYkH4zeF/uR1yBajHRhkzuaUSGOB1WJSTieJBdHwxlC+HLKvZZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-phrasing": "^3.0.0", + "hast-util-to-html": "^9.0.0", + "hast-util-to-text": "^4.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "mdast-util-to-string": "^4.0.0", + "rehype-minify-whitespace": "^6.0.0", + "trim-trailing-lines": "^2.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-string": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz", + "integrity": "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-text": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", + "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "unist-util-find-after": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hono": { + "version": "4.13.8", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.8.tgz", + "integrity": "sha512-/Gng7NfoykZl2pjukW5Z6+8Yxm3BPRf86GTbQnt0SbySkvax4fyL4H3HhY1cCpBGmiW9XDRFzRV+CXK2W8QudQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/http2-wrapper": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ico-endec": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ico-endec/-/ico-endec-0.1.6.tgz", + "integrity": "sha512-ZdLU38ZoED3g1j3iEyzcQj+wAkY2xfWNkymszfJPoxucIUhK7NayQ+/C4Kv0nDFMIsbtbEHldv3V8PU494/ueQ==", + "dev": true, + "license": "MPL-2.0" + }, + "node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/ink": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/ink/-/ink-6.3.0.tgz", + "integrity": "sha512-2CbJAa7XeziZYe6pDS5RVLirRY28iSGMQuEV8jRU5NQsONQNfcR/BZHHc9vkMg2lGYTHTM2pskxC1YmY28p6bQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alcalzone/ansi-tokenize": "^0.2.0", + "ansi-escapes": "^7.0.0", + "ansi-styles": "^6.2.1", + "auto-bind": "^5.0.1", + "chalk": "^5.6.0", + "cli-boxes": "^3.0.0", + "cli-cursor": "^4.0.0", + "cli-truncate": "^4.0.0", + "code-excerpt": "^4.0.0", + "es-toolkit": "^1.39.10", + "indent-string": "^5.0.0", + "is-in-ci": "^2.0.0", + "patch-console": "^2.0.0", + "react-reconciler": "^0.32.0", + "signal-exit": "^3.0.7", + "slice-ansi": "^7.1.0", + "stack-utils": "^2.0.6", + "string-width": "^7.2.0", + "type-fest": "^4.27.0", + "widest-line": "^5.0.0", + "wrap-ansi": "^9.0.0", + "ws": "^8.18.0", + "yoga-layout": "~3.2.1" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@types/react": ">=19.0.0", + "react": ">=19.0.0", + "react-devtools-core": "^4.19.1" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react-devtools-core": { + "optional": true + } + } + }, + "node_modules/ink-spinner": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ink-spinner/-/ink-spinner-5.0.0.tgz", + "integrity": "sha512-EYEasbEjkqLGyPOUc8hBJZNuC5GvXGMLu0w5gdTNskPc7Izc5vO3tdQEYnzvshucyGCBXc86ig0ujXPMWaQCdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-spinners": "^2.7.0" + }, + "engines": { + "node": ">=14.16" + }, + "peerDependencies": { + "ink": ">=4.0.0", + "react": ">=18.0.0" + } + }, + "node_modules/ink/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ink/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ink/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/inline-style-parser": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", + "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/inquirer": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-12.3.0.tgz", + "integrity": "sha512-3NixUXq+hM8ezj2wc7wC37b32/rHq1MwNZDYdvx+d6jokOD+r+i8Q4Pkylh9tISYP114A128LCX8RKhopC5RfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.2", + "@inquirer/prompts": "^7.2.1", + "@inquirer/type": "^3.0.2", + "ansi-escapes": "^4.3.2", + "mute-stream": "^2.0.0", + "run-async": "^3.0.0", + "rxjs": "^7.8.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + } + }, + "node_modules/inquirer/node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inquirer/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ip-address": { + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.2.tgz", + "integrity": "sha512-7H/2gFSIitxc0hG3nOI1glS8QLo/EHBFFLk8vEUjXY/xu0AdL8jZ9U1IzO2PUm0d2D/ofQcAifb0g6OBkt8U7w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ip-regex": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", + "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/iregexp-check": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/iregexp-check/-/iregexp-check-0.1.2.tgz", + "integrity": "sha512-RCcV2SFO5JKh+DdmWiY7yVyiZzzk6XZiHEJvTKbzVs8Z0u404gnoSqDtFqg6C5qJBEG4QseWBQz9zhI68zskEQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.17.0.tgz", + "integrity": "sha512-J/vG0zBCbIKOQFfufSwyXdMrsohyJIUNkrnmo6WZGzoM7tr/lsbfW5b2BvisL6zsyMzK9UxV9L6c7AoFbyXHOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-document.all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-document.all/-/is-document.all-1.0.0.tgz", + "integrity": "sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-in-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-in-ci/-/is-in-ci-2.0.0.tgz", + "integrity": "sha512-cFeerHriAnhrQSbpAxL37W1wcJKUUX07HyLWZCW1URJT/ra3GyUTzBgUnh24TMVfNTV2Hij2HLxkPHFZfOZy5w==", + "dev": true, + "license": "MIT", + "bin": { + "is-in-ci": "cli.js" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-ip": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-3.1.0.tgz", + "integrity": "sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-regex": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-online": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/is-online/-/is-online-10.0.0.tgz", + "integrity": "sha512-WCPdKwNDjXJJmUubf2VHLMDBkUZEtuOvpXUfUnUFbEnM6In9ByiScL4f4jKACz/fsb2qDkesFerW3snf/AYz3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "got": "^12.1.0", + "p-any": "^4.0.0", + "p-timeout": "^5.1.0", + "public-ip": "^5.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-online/node_modules/got": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", + "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/jose": { + "version": "6.2.12", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.12.tgz", + "integrity": "sha512-9NiFmJEex0sy2Dk58j2UGBSHgUs2ypF9eZSu4L6vjOX3Dp96Sw1F3uL+H+D1sx02jZZdzUT0HgvCy59CuvXcWw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsep": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz", + "integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.16.0" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-p3": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/json-p3/-/json-p3-2.2.2.tgz", + "integrity": "sha512-TmTgkx+C1ne8Wdm9dFQ4/jCeYYbSoeYJAkRpvTz6O+zWN1Dc3cKW8Mt52sa8mSRXy/+JykA5I6rmM+k8h+JRRg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-to-ts": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", + "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.18.3", + "ts-algebra": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonpath-plus": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-10.4.0.tgz", + "integrity": "sha512-T92WWatJXmhBbKsgH/0hl+jxjdXrifi5IKeMY02DWggRxX0UElcbVzPlmgLTbvsPeW1PasQ6xE2Q75stkhGbsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jsep-plugin/assignment": "^1.3.0", + "@jsep-plugin/regex": "^1.0.4", + "jsep": "^1.4.0" + }, + "bin": { + "jsonpath": "bin/jsonpath-cli.js", + "jsonpath-plus": "bin/jsonpath-cli.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/katex": { + "version": "0.16.47", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.47.tgz", + "integrity": "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==", + "dev": true, + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/keytar": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz", + "integrity": "sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-addon-api": "^4.3.0", + "prebuild-install": "^7.0.1" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/lcm": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/lcm/-/lcm-0.0.3.tgz", + "integrity": "sha512-TB+ZjoillV6B26Vspf9l2L/vKaRY/4ep3hahcyVkCGFgsTNRUQdc24bQeNFiZeoxH0vr5+7SfNRMQuPHv/1IrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "gcd": "^0.0.1" + } + }, + "node_modules/leven": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-4.1.0.tgz", + "integrity": "sha512-KZ9W9nWDT7rF7Dazg8xyLHGLrmpgq2nVNFUckhqdW3szVP6YhCpp/RAnpmVExA9JvrMynjwSLVrEj3AepHR6ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.topath": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz", + "integrity": "sha512-1/W4dM+35DwvE/iEd1M9ekewOSTlpFekhw9mhAtrwjVqUr83/ilQiyAvmg4tVX7Unkcfl1KC+i9WdaT4B6aQcg==", + "dev": true, + "license": "MIT" + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/markdown-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", + "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-frontmatter": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-2.0.1.tgz", + "integrity": "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "escape-string-regexp": "^5.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-extension-frontmatter": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.0.0.tgz", + "integrity": "sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-math": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-math/-/mdast-util-math-3.0.0.tgz", + "integrity": "sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "longest-streak": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.1.0", + "unist-util-remove-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", + "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-frontmatter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-2.0.0.tgz", + "integrity": "sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fault": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "dev": true, + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "dev": true, + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.2.tgz", + "integrity": "sha512-pRzm4kDTu0MjlmBkxmS9yYhw60nncfcEwu9NNdPFSQEFXS95ZKyIIyTSHu/o3ReBUrLKYEq+7YaXCRn/bPB4MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-math": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz", + "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/katex": "^0.16.0", + "devlop": "^1.0.0", + "katex": "^0.16.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-expression": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz", + "integrity": "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-jsx": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.2.tgz", + "integrity": "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-md": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", + "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", + "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.0.0", + "acorn-jsx": "^5.0.0", + "micromark-extension-mdx-expression": "^3.0.0", + "micromark-extension-mdx-jsx": "^3.0.0", + "micromark-extension-mdx-md": "^2.0.0", + "micromark-extension-mdxjs-esm": "^3.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs-esm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", + "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-mdx-expression": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.3.tgz", + "integrity": "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-events-to-acorn": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.3.tgz", + "integrity": "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "optional": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mint": { + "version": "4.2.899", + "resolved": "https://registry.npmjs.org/mint/-/mint-4.2.899.tgz", + "integrity": "sha512-JHT2PmzrZ3YtQqEnl+vWbhcv1r94pcaT4vlREeKcOQ2KuNiPsEUKbERAezkyh/cwByF41KXxIiHwpLIZ+MZrBw==", + "dev": true, + "license": "Elastic-2.0", + "dependencies": { + "@mintlify/cli": "4.0.1502" + }, + "bin": { + "mint": "index.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true, + "license": "MIT" + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.19", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz", + "integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neotraverse": { + "version": "0.6.18", + "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-0.6.18.tgz", + "integrity": "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/netmask": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.1.1.tgz", + "integrity": "sha512-eonl3sLUha+S1GzTPxychyhnUzKyeQkZ7jLjKrBagJgPla13F+uQ71HgpFefyHgqrjEbCPkDArxYsjY8/+gLKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/nimma": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/nimma/-/nimma-0.2.3.tgz", + "integrity": "sha512-1ZOI8J+1PKKGceo/5CT5GfQOG6H8I2BencSK06YarZ2wXwH37BSSUWldqJmMJYA5JfqDqffxDXynt6f11AyKcA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsep-plugin/regex": "^1.0.1", + "@jsep-plugin/ternary": "^1.0.2", + "astring": "^1.8.1", + "jsep": "^1.2.0" + }, + "engines": { + "node": "^12.20 || >=14.13" + }, + "optionalDependencies": { + "jsonpath-plus": "^6.0.1 || ^10.1.0", + "lodash.topath": "^4.5.2" + } + }, + "node_modules/nlcst-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz", + "integrity": "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/node-abi": { + "version": "3.96.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.96.0.tgz", + "integrity": "sha512-rebQ/lz7i0EkoLzUVSrKRzA69zMkwLp95kKMWoMDkkM00Suxz0D7zEQPwRml5fQum24mj7bPvmlgLAmu2JCiYg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/non-error": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/non-error/-/non-error-0.1.0.tgz", + "integrity": "sha512-TMB1uHiGsHRGv1uYclfhivcnf0/PdFp2pNqRxXjncaAsjYMoisaQJI+SSZCqRq+VliwRTC8tsMQfmrWjDMhkPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.1.1.tgz", + "integrity": "sha512-JYc0DPlpGWB40kH5g07gGTrYuMqV653k3uBKY6uITPWds3M0ov3GaWGp9lbE3Bzngx8+XkfzgvASb9vk9JDFXQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/oauth4webapi": { + "version": "3.8.8", + "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.8.8.tgz", + "integrity": "sha512-8N28E+a/oxfXWBgOMt+ZP/JUf/XR+IFbvkAEPP3gznXOMv9BpAAwiIj0TFNz3tGTPc0ZQ8zmWBNgN1nAys0gng==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/oniguruma-parser": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.2.tgz", + "integrity": "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==", + "dev": true, + "license": "MIT" + }, + "node_modules/oniguruma-to-es": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.6.tgz", + "integrity": "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "oniguruma-parser": "^0.12.2", + "regex": "^6.1.0", + "regex-recursion": "^6.0.2" + } + }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/openapi-types": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/openid-client": { + "version": "6.8.2", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-6.8.2.tgz", + "integrity": "sha512-uOvTCndr4udZsKihJ68H9bUICrriHdUVJ6Az+4Ns6cW55rwM5h0bjVIzDz2SxgOI84LKjFyjOFvERLzdTUROGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jose": "^6.1.3", + "oauth4webapi": "^3.8.4" + }, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/orderedmap": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz", + "integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==", + "dev": true, + "license": "MIT" + }, + "node_modules/own-keys": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.2.tgz", + "integrity": "sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-any": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-any/-/p-any-4.0.0.tgz", + "integrity": "sha512-S/B50s+pAVe0wmEZHmBs/9yJXeZ5KhHzOsgKzt0hRdgkoR3DxW9ts46fcsWi/r3VnzsnkKS7q4uimze+zjdryw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-cancelable": "^3.0.0", + "p-some": "^6.0.0" + }, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/p-some": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-some/-/p-some-6.0.0.tgz", + "integrity": "sha512-CJbQCKdfSX3fIh8/QKgS+9rjm7OBNUTmwWswAFQAhc8j1NR1dsEDETUEuVUtQHZpV+J03LqWBEwvu0g1Yn+TYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^4.0.0", + "p-cancelable": "^3.0.0" + }, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-5.1.0.tgz", + "integrity": "sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pac-proxy-agent": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", + "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.6", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "dev": true, + "license": "MIT", + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-latin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-7.0.0.tgz", + "integrity": "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "@types/unist": "^3.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-modify-children": "^4.0.0", + "unist-util-visit-children": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/patch-console": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/patch-console/-/patch-console-2.0.0.tgz", + "integrity": "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/path-expression-matcher": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.6.2.tgz", + "integrity": "sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "dev": true, + "license": "MIT" + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/pony-cause": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-1.1.1.tgz", + "integrity": "sha512-PxkIc/2ZpLiEzQXu5YRDOUgBlfGYBY8156HY5ZcRAwwonMk5W/MrJP2LLkG/hF7GEQzaHo2aS7ho6ZLCOvf+6g==", + "dev": true, + "license": "0BSD", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz", + "integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.16", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.4.tgz", + "integrity": "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/posthog-node": { + "version": "5.17.2", + "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-5.17.2.tgz", + "integrity": "sha512-lz3YJOr0Nmiz0yHASaINEDHqoV+0bC3eD8aZAG+Ky292dAnVYul+ga/dMX8KCBXg8hHfKdxw0SztYD5j6dgUqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@posthog/core": "1.7.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/property-information": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz", + "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/prosemirror-model": { + "version": "1.25.11", + "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.11.tgz", + "integrity": "sha512-QWg9RhnpLlogAmp3p96uEFrE5txQpFynd4vhBAELkwgOCWQs/X0yCzB3/hrHqiPwf91RG5KyWq6553zs9JqIOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "orderedmap": "^2.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.8.tgz", + "integrity": "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/proxy-agent": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", + "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.6", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.1.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true, + "license": "MIT" + }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/public-ip": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/public-ip/-/public-ip-5.0.0.tgz", + "integrity": "sha512-xaH3pZMni/R2BG7ZXXaWS9Wc9wFlhyDVJF47IJ+3ali0TGv+2PsckKxbmo+rnx3ZxiV2wblVhtdS3bohAP6GGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dns-socket": "^4.2.2", + "got": "^12.0.0", + "is-ip": "^3.1.0" + }, + "engines": { + "node": "^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/public-ip/node_modules/got": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", + "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/puppeteer": { + "version": "24.3.1", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.3.1.tgz", + "integrity": "sha512-k0OJ7itRwkr06owp0CP3f/PsRD7Pdw4DjoCUZvjGr+aNgS1z6n/61VajIp0uBjl+V5XAQO1v/3k9bzeZLWs9OQ==", + "deprecated": "< 24.15.0 is no longer supported", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@puppeteer/browsers": "2.7.1", + "chromium-bidi": "2.1.2", + "cosmiconfig": "^9.0.0", + "devtools-protocol": "0.0.1402036", + "puppeteer-core": "24.3.1", + "typed-query-selector": "^2.12.0" + }, + "bin": { + "puppeteer": "lib/cjs/puppeteer/node/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/puppeteer-core": { + "version": "24.3.1", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.3.1.tgz", + "integrity": "sha512-585ccfcTav4KmlSmYbwwOSeC8VdutQHn2Fuk0id/y/9OoeO7Gg5PK1aUGdZjEmos0TAq+pCpChqFurFbpNd3wA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@puppeteer/browsers": "2.7.1", + "chromium-bidi": "2.1.2", + "debug": "^4.4.0", + "devtools-protocol": "0.0.1402036", + "typed-query-selector": "^2.12.0", + "ws": "^8.18.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/qs": { + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "optional": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/re2js": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/re2js/-/re2js-2.8.6.tgz", + "integrity": "sha512-xLgQil4kIUCrAzVk9fRSkxkFNwmygLFjVxXrLc65aE1F0+Zsb8rxumFBy4XKyvgMCTL6kilDq3EZ0piE2dP/Dg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/react": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", + "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-reconciler": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.32.0.tgz", + "integrity": "sha512-2NPMOzgTlG0ZWdIf3qG+dcbLSoAc/uLfOwckc3ofy5sSK0pLJqnQLpUFxvGcN2rlXSjnVtGeeFLNimCQEj5gOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "scheduler": "^0.26.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "react": "^19.1.0" + } + }, + "node_modules/read-cache": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.2.tgz", + "integrity": "sha512-/peqiBB/n07gQGLsWaHho3WfvUyRscw0gYTsEFMhrIe/nWLkYaf5SbKYjGYqtRV3aPwykJgF2VEMo1ac4bnsGA==", + "dev": true, + "license": "MIT" + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/recma-build-jsx": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz", + "integrity": "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-util-build-jsx": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-jsx": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/recma-jsx/-/recma-jsx-1.0.1.tgz", + "integrity": "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn-jsx": "^5.0.0", + "estree-util-to-js": "^2.0.0", + "recma-parse": "^1.0.0", + "recma-stringify": "^1.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/recma-parse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-parse/-/recma-parse-1.0.0.tgz", + "integrity": "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "esast-util-from-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-stringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-stringify/-/recma-stringify-1.0.0.tgz", + "integrity": "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-util-to-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", + "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "dev": true, + "license": "MIT" + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/rehype-katex": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/rehype-katex/-/rehype-katex-7.0.1.tgz", + "integrity": "sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/katex": "^0.16.0", + "hast-util-from-html-isomorphic": "^2.0.0", + "hast-util-to-text": "^4.0.0", + "katex": "^0.16.0", + "unist-util-visit-parents": "^6.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-minify-whitespace": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/rehype-minify-whitespace/-/rehype-minify-whitespace-6.0.2.tgz", + "integrity": "sha512-Zk0pyQ06A3Lyxhe9vGtOtzz3Z0+qZ5+7icZ/PL/2x1SHPbKao5oB/g/rlc6BCTajqBb33JcOe71Ye1oFsuYbnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-minify-whitespace": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-parse": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.1.tgz", + "integrity": "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-from-html": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-recma": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rehype-recma/-/rehype-recma-1.0.0.tgz", + "integrity": "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "hast-util-to-estree": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-stringify": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz", + "integrity": "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-to-html": "^9.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark": { + "version": "15.0.1", + "resolved": "https://registry.npmjs.org/remark/-/remark-15.0.1.tgz", + "integrity": "sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-frontmatter": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-5.0.0.tgz", + "integrity": "sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-frontmatter": "^2.0.0", + "micromark-extension-frontmatter": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-math": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/remark-math/-/remark-math-6.0.0.tgz", + "integrity": "sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-math": "^3.0.0", + "micromark-extension-math": "^3.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-mdx": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.1.tgz", + "integrity": "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdast-util-mdx": "^3.0.0", + "micromark-extension-mdxjs": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-mdx-remove-esm": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/remark-mdx-remove-esm/-/remark-mdx-remove-esm-1.3.3.tgz", + "integrity": "sha512-KgGfD4JV3lKse0n/m8UxW4jK6H/9ffkCVxVAJ60aGY4uiPX/+SQCT5/lQcRGcjyQuRLDq2Df2krv6pD/qWJRVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.4", + "unist-util-remove": "^4.0.0" + }, + "peerDependencies": { + "unified": "^11" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.1.tgz", + "integrity": "sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-smartypants": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/remark-smartypants/-/remark-smartypants-3.0.2.tgz", + "integrity": "sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA==", + "dev": true, + "license": "MIT", + "dependencies": { + "retext": "^9.0.0", + "retext-smartypants": "^6.0.0", + "unified": "^11.0.4", + "unist-util-visit": "^5.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/reselect": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.3.0.tgz", + "integrity": "sha512-XGoLeRAVzUTcJ1qkxPQhDJyIZ5d6zzZD9nT7AEZOaaU9UbWclhycElmhO+VD5bFeLuzhPBaOV2oXC8uG35ZSpg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/responselike": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/retext": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/retext/-/retext-9.0.0.tgz", + "integrity": "sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "retext-latin": "^4.0.0", + "retext-stringify": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-latin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/retext-latin/-/retext-latin-4.0.0.tgz", + "integrity": "sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "parse-latin": "^7.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-smartypants": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/retext-smartypants/-/retext-smartypants-6.2.0.tgz", + "integrity": "sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-stringify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/retext-stringify/-/retext-stringify-4.0.0.tgz", + "integrity": "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/router/node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "dev": true, + "license": "MIT", + "optional": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/run-async": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", + "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/rxjs/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/safe-array-concat": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", + "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-stable-stringify": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz", + "integrity": "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==", + "dev": true, + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz", + "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/serialize-error": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-13.0.1.tgz", + "integrity": "sha512-bBZaRwLH9PN5HbLCjPId4dP5bNGEtumcErgOX952IsvOhVPrm3/AeK1y0UHA/QaPG701eg0yEnOKsCOC6X/kaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "non-error": "^0.1.0", + "type-fest": "^5.4.1" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-error/node_modules/type-fest": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.10.0.tgz", + "integrity": "sha512-NoSdpq/WEiAg5sjmBkmV/hfxv6HJH4NqPNrqjtSO5CwRmpsDfaf4begxW34KdJykH/l1yHtwBWQkCRdoXO8mPA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "dependencies": { + "tagged-tag": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, + "node_modules/sharp": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", + "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.5", + "@img/sharp-darwin-x64": "0.33.5", + "@img/sharp-libvips-darwin-arm64": "1.0.4", + "@img/sharp-libvips-darwin-x64": "1.0.4", + "@img/sharp-libvips-linux-arm": "1.0.5", + "@img/sharp-libvips-linux-arm64": "1.0.4", + "@img/sharp-libvips-linux-s390x": "1.0.4", + "@img/sharp-libvips-linux-x64": "1.0.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", + "@img/sharp-libvips-linuxmusl-x64": "1.0.4", + "@img/sharp-linux-arm": "0.33.5", + "@img/sharp-linux-arm64": "0.33.5", + "@img/sharp-linux-s390x": "0.33.5", + "@img/sharp-linux-x64": "0.33.5", + "@img/sharp-linuxmusl-arm64": "0.33.5", + "@img/sharp-linuxmusl-x64": "0.33.5", + "@img/sharp-wasm32": "0.33.5", + "@img/sharp-win32-ia32": "0.33.5", + "@img/sharp-win32-x64": "0.33.5" + } + }, + "node_modules/sharp-ico": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/sharp-ico/-/sharp-ico-0.1.5.tgz", + "integrity": "sha512-a3jODQl82NPp1d5OYb0wY+oFaPk7AvyxipIowCHk7pBsZCWgbe0yAkU2OOXdoH0ENyANhyOQbs9xkAiRHcF02Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "decode-ico": "*", + "ico-endec": "*", + "sharp": "*" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shiki": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.23.0.tgz", + "integrity": "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/core": "3.23.0", + "@shikijs/engine-javascript": "3.23.0", + "@shikijs/engine-oniguruma": "3.23.0", + "@shikijs/langs": "3.23.0", + "@shikijs/themes": "3.23.0", + "@shikijs/types": "3.23.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", + "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", + "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/slice-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socket.io": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.0.tgz", + "integrity": "sha512-8U6BEgGjQOfGz3HHTYaC/L1GaxDCJ/KM0XTkJly0EhZ5U/du9uNEZy4ZgYzEzIqlx2CMm25CrCqr1ck899eLNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.8", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.8.tgz", + "integrity": "sha512-6Oy52pbg+kvdCVvjcN+FnY7BvxZ7cIHNScbvztT/It5d0vbwoJoVZmF2gjJmnV0/4WlXRfG15zc45ySk9Ah8bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "~4.4.1", + "ws": "~8.21.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.7.tgz", + "integrity": "sha512-IH/iSeO9T6gz1KkFleGDWkG9N3dl4jXVYUtMhIqH10Md0ttMer8nUNWiP1DKuNrybD2xBrixLJdCC9J6ECoYkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.4.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socks": { + "version": "2.8.10", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.10.tgz", + "integrity": "sha512-e0VyvkVTwVYViNovRkZ9aodhxVlyoMn7eJhVUPxZ+eK9P/7CBkxvvsBOHqFPEH416726W8tLXXXjKwqgTErrCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-address": "^10.1.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/socks-proxy-agent/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/standardwebhooks": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.1.1.tgz", + "integrity": "sha512-bCbX9ZEyFkWPsRz7Bl3NuQUJohmwGSev/yhr7vhaGPlc4AfIrspIRa6cPTBuI1ItmrTDJ4d/S2hCsfe4+vQGnQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@stablelib/base64": "^1.0.0", + "fast-sha256": "^1.3.0" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/streamx": { + "version": "2.28.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.1.tgz", + "integrity": "sha512-zEzXb0s5Cds7tqMH6rhZ05lcJydCWiQPEwiNngVqzsxCc962vLY4Uw+mW7od8kDH258k2Uz/JrOkdIAAhSh9VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.11.tgz", + "integrity": "sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.2", + "es-object-atoms": "^1.1.2", + "has-property-descriptors": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.10.tgz", + "integrity": "sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strnum": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.2.tgz", + "integrity": "sha512-rDG3Ah4TV0k1hWvLSzkZtMmLN9+eS+h3knq4MP6A42Y3Yh5qGNnOUs1jJkoSr8FG5dsL28c7KgkIBzSEykqtuw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "anynum": "^1.0.1" + } + }, + "node_modules/style-to-js": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", + "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.14" + } + }, + "node_modules/style-to-js/node_modules/inline-style-parser": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", + "dev": true, + "license": "MIT" + }, + "node_modules/style-to-js/node_modules/style-to-object": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.7" + } + }, + "node_modules/style-to-object": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.4.tgz", + "integrity": "sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.1.1" + } + }, + "node_modules/sucrase": { + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz", + "integrity": "sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "7.1.6", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tagged-tag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", + "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tailwindcss-v3": { + "name": "tailwindcss", + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss-v3/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/tailwindcss-v3/node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tailwindcss-v3/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/tailwindcss-v3/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/tailwindcss-v3/node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/tar": { + "version": "7.5.21", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.21.tgz", + "integrity": "sha512-XdhtCvlMywwxpCW8YEq3lOXBJpUPTR2OHHcwLPO3HwsJqOHa2Ok/oJ7ruGzp+JrKoRPVCzJwAdEjqLW/vNRPHA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar-fs": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.5.tgz", + "integrity": "sha512-OboTd8mmMhZDNPV+UjQcK9yKAatXu2aJ+r1w4im1Otd4M4fl2hwvdoXUxIYHFTHWK/3y3FarBP70v3vwmGlOxw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-fs/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/teex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", + "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "streamx": "^2.12.5" + } + }, + "node_modules/text-decoder": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", + "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-data-view": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/to-data-view/-/to-data-view-1.1.0.tgz", + "integrity": "sha512-1eAdufMg6mwgmlojAx3QeMnzB/BTVp7Tbndi3U7ftcT2zCZadjxkkmLmd97zmaxWi+sgGcgWrokmpEoy0Dn0vQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trim-trailing-lines": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-2.1.0.tgz", + "integrity": "sha512-5UR5Biq4VlVOtzqkm2AZlgvSlDJtME46uV0br0gENbwN4l5+mMKT4b9gJKqWtuL2zAIqajGJGuvbCbcAJUZqBg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-algebra": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", + "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/twoslash": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/twoslash/-/twoslash-0.3.9.tgz", + "integrity": "sha512-rDclk+OtzuTX+tnea7DYLCkqGQ3eP0IyfD+kzUJ7t46X/NzlaxwrhecmEBNuSCuEn3V+n1PhcjUUQQ7gUJzX5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript/vfs": "^1.6.4", + "twoslash-protocol": "0.3.9" + }, + "peerDependencies": { + "typescript": "^5.5.0 || ^6.0.0" + } + }, + "node_modules/twoslash-protocol": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/twoslash-protocol/-/twoslash-protocol-0.3.9.tgz", + "integrity": "sha512-9/iwp+CXOnjFMPQuPL5PkuRbZnDoNpBvtJCLs9t8kDYkL3YHujbvnHfZA1i5fApDftVEdBw+T/4F+dH5kIzpYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.5.tgz", + "integrity": "sha512-0FHJvLPqZ7KJzp17O13jfsAjsqazgrxBu2zEK95PmUz8lv2+GjRuxUInCr2Rk9Dms3ihN21zJ929ZO43yJ95QQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.9", + "for-each": "^0.3.5", + "gopd": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.8.tgz", + "integrity": "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "for-each": "^0.3.5", + "gopd": "^1.2.0", + "is-typed-array": "^1.1.15", + "possible-typed-array-names": "^1.1.0", + "reflect.getprototypeof": "^1.0.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-query-selector": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.2.tgz", + "integrity": "sha512-EOPFbyIub4ngnEdqi2yOcNeDLaX/0jcE1JoAXQDDMIthap7FoN795lc/SHfIq2d416VufXpM8z/lD+WRm2gfOQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.9.0.tgz", + "integrity": "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==", + "dev": true, + "license": "MIT" + }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-builder": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-4.0.0.tgz", + "integrity": "sha512-wmRFnH+BLpZnTKpc5L7O67Kac89s9HMrtELpnNaE6TAobq5DTZZs5YaTQfAZBA9bFPECx2uVAPO31c+GVug8mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-find-after": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", + "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-map/-/unist-util-map-4.0.0.tgz", + "integrity": "sha512-HJs1tpkSmRJUzj6fskQrS5oYhBYlmtcvy4SepdDEEsL04FjBrgF0Mgggvxc1/qGBGgW7hRh9+UBK1aqTEnBpIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-modify-children": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-4.0.0.tgz", + "integrity": "sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "array-iterate": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", + "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-4.0.0.tgz", + "integrity": "sha512-b4gokeGId57UVRX/eVKej5gXqGlc9+trkORhFJpu9raqZkZhU0zm8Doi05+HaiBsMEIJowL+2WtQ5ItjsngPXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-children": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit-children/-/unist-util-visit-children-3.0.0.tgz", + "integrity": "sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/urijs": { + "version": "1.19.11", + "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz", + "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/use-sync-external-store": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.7.0.tgz", + "integrity": "sha512-6L+EeigHMQhdaIPNIFUKwfWJSwWFQ8gJbJ2DLOs5sDIegTwR9fRxvnM3uciHKjIZhFz+KAv2emhWMRvDmMcY8A==", + "dev": true, + "license": "MIT", + "peer": true, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/utility-types": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz", + "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.1.tgz", + "integrity": "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-matter": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/vfile-matter/-/vfile-matter-5.0.1.tgz", + "integrity": "sha512-o6roP82AiX0XfkyTHyRCMXgHfltUNlXSEqCIS80f+mbAyiQBE2fxtDVMtseyytGx75sihiJFo/zR6r/4LTs2Cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "vfile": "^6.0.0", + "yaml": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.23.tgz", + "integrity": "sha512-JMh8aK+1B/0bk/YNupICmH5MgCq6yNLKYQUfsZtSDLLCCmxUkHjUY+oOlIa90lO7lHN1woAfpenLdlKpXy9o+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/widest-line": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz", + "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", + "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-naming": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.3.0.tgz", + "integrity": "sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xss": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.15.tgz", + "integrity": "sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^2.20.3", + "cssfilter": "0.0.10" + }, + "bin": { + "xss": "bin/xss" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/xss/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/yaml": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz", + "integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yargs": { + "version": "17.7.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", + "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", + "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoga-layout": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/yoga-layout/-/yoga-layout-3.2.1.tgz", + "integrity": "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "dev": true, + "license": "ISC", + "optional": true, + "peerDependencies": { + "zod": "^3.25.28 || ^4" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..23dbc5f --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "name": "driver-docs", + "private": true, + "type": "module", + "scripts": { + "dev": "mint dev", + "sync": "node scripts/sync-openapi.mjs", + "check": "mint broken-links" + }, + "devDependencies": { + "mint": "latest" + } +} diff --git a/scripts/sync-openapi.mjs b/scripts/sync-openapi.mjs new file mode 100644 index 0000000..a9e456f --- /dev/null +++ b/scripts/sync-openapi.mjs @@ -0,0 +1,106 @@ +// Pulls the live OpenAPI document that Scalar renders at https://api.driver.dev/scalar (ground truth for what +// exists) and writes api-reference/openapi.json with the two things Mintlify needs that the source omits: +// a servers entry and tags for grouping. Run `npm run sync` before publishing; never edit the JSON by hand. +import fs from 'node:fs' + +const SOURCE = process.env.OPENAPI_URL || 'https://api.driver.dev/doc' +const doc = await fetch(SOURCE).then((r) => { if (!r.ok) throw new Error(`${SOURCE} → ${r.status}`); return r.json() }) + +doc.servers = [{ url: 'https://api.driver.dev', description: 'Production' }] +doc.info = { ...doc.info, title: 'Driver API', description: doc.info?.description || 'Hosted Chrome sessions over CDP, plus scrape and search.' } + +// Deprecated surface is not documented: the teracrawl endpoints and the consumer_distributed browser type. +for (const path of Object.keys(doc.paths)) if (path.startsWith('/v1/teracrawl')) delete doc.paths[path] +const dropDeprecatedType = (node) => { + if (Array.isArray(node)) return node.forEach(dropDeprecatedType) + if (!node || typeof node !== 'object') return + if (Array.isArray(node.enum)) node.enum = node.enum.filter((v) => v !== 'consumer_distributed') + for (const v of Object.values(node)) dropDeprecatedType(v) +} +dropDeprecatedType(doc) + +const tagFor = (path) => path.startsWith('/v1/browser/session') ? 'Sessions' + : path.startsWith('/v1/browser/profile') ? 'Profiles' + : path.startsWith('/v1/browser/extensions') ? 'Extensions' + : path.startsWith('/v1/account') ? 'Account' + : path.startsWith('/v1/teracrawl') ? 'Scrape and search' + : 'Other' +const summaries = { + 'POST /v1/browser/session': 'Create a session', + 'GET /v1/browser/session': 'Get a session', + 'PATCH /v1/browser/session': 'Update a session note', + 'DELETE /v1/browser/session': 'Stop a session', + 'GET /v1/browser/sessions': 'List sessions', + 'GET /v1/browser/profiles': 'List profiles', + 'DELETE /v1/browser/profile': 'Delete a profile', + 'GET /v1/account/balance': 'Get balance', + 'GET /v1/account/billing': 'Get billing details', + 'POST /v1/teracrawl/scrape': 'Scrape a page', + 'POST /v1/teracrawl/search': 'Search the web', + 'POST /v1/browser/extensions': 'Upload an extension', + 'GET /v1/browser/extensions': 'List extensions', + 'GET /v1/browser/extensions/{extensionId}': 'Get an extension', + 'PUT /v1/browser/extensions/{extensionId}': 'Update an extension', + 'DELETE /v1/browser/extensions/{extensionId}': 'Delete an extension', +} +const tags = new Set() +for (const [path, ops] of Object.entries(doc.paths)) { + for (const [method, op] of Object.entries(ops)) { + if (!['get', 'post', 'put', 'patch', 'delete'].includes(method)) continue + const tag = tagFor(path) + tags.add(tag) + op.tags = [tag] + op.summary ||= summaries[`${method.toUpperCase()} ${path}`] || op.summary + op.security ||= [{ Bearer: [] }] + } +} +doc.tags = [...tags].map((name) => ({ name })) + +// OpenAPI 3.0 schemas allow `example`, not an `examples` array; the source uses the latter. Keep the first one. +const fixExamples = (node) => { + if (Array.isArray(node)) return node.forEach(fixExamples) + if (!node || typeof node !== 'object') return + if (Array.isArray(node.examples) && (node.type || node.properties || node.items || node.$ref)) { + if (node.example === undefined && node.examples.length) node.example = node.examples[0] + delete node.examples + } + for (const v of Object.values(node)) fixExamples(v) +} +fixExamples(doc) + +// The source document names internal systems. Readers get product language instead. Ordered: phrases first, then words. +const SCRUB = [ + [/ consumer_distributed is the legacy route\./g, ''], + [/ consumer_distributed is the legacy distributed-browser route\./g, ''], + [/ and rejected for consumer_distributed sessions/g, ''], + [/, or 'consumer_distributed'/g, ''], + [/, 'hosted_privacy', or 'consumer_distributed'/g, ", or 'hosted_privacy'"], + [/hosted, hosted_stealth, and hosted_privacy launch on Rio; hosted_stealth enables conservative Mirage Stealth and hosted_privacy enables the preserved aggressive Stealth policy\./g, 'hosted, hosted_stealth, and hosted_privacy are hosted browsers; hosted_stealth applies the conservative stealth policy and hosted_privacy the aggressive one.'], + [/hosted uses Mirage natively, hosted_stealth uses conservative Stealth, and hosted_privacy uses the preserved aggressive Stealth policy\./g, 'hosted is the native browser, hosted_stealth applies the conservative stealth policy, and hosted_privacy the aggressive one.'], + [/Rio\/Mirage virtual display size/g, 'Virtual display size'], + [/Only supported by Rio-backed hosted types\./g, 'Only supported by hosted types.'], + [/Supported by all Rio-backed hosted types/g, 'Supported by all hosted types'], + [/Rio stores persistent profiles as portable Mirage plaintext archives\./g, 'Persistent profiles are kept between sessions.'], + [/so Mirage uses its built-in en-US default/g, 'so the browser uses its built-in en-US default'], + [/primary language supported by Mirage/g, 'primary language the browser supports'], + [/Rio selected a machine but the browser did not become CDP-ready before the startup deadline\./g, 'A machine was selected but the browser did not become CDP-ready before the startup deadline.'], + [/Rio is unavailable, so a complete account session response cannot be produced\./g, 'The session service is unavailable.'], + [/Rio session service unavailable/g, 'Session service unavailable'], + [/Lisa or its billing provider could not return billing details\./g, 'Billing details could not be returned.'], + [/wss:\/\/[a-z0-9.-]+\.tera\.space\//g, 'wss://sessions.driver.dev/'], + [/[a-z0-9.-]+\.tera\.space/g, 'driver.dev'], + [/APIGW/g, 'Driver'], + [/\bRio\b/g, 'Driver'], + [/\bMirage\b/g, 'Chrome'], + [/\bLisa\b/g, 'the session service'], +] +const scrub = (node) => { + if (Array.isArray(node)) return node.map(scrub) + if (node && typeof node === 'object') { for (const k of Object.keys(node)) node[k] = scrub(node[k]); return node } + if (typeof node === 'string') return SCRUB.reduce((acc, [re, to]) => acc.replace(re, to), node) + return node +} +scrub(doc) +fs.mkdirSync('api-reference', { recursive: true }) +fs.writeFileSync('api-reference/openapi.json', JSON.stringify(doc, null, 2) + '\n') +console.log(`wrote api-reference/openapi.json from ${SOURCE}: ${Object.keys(doc.paths).length} paths`) From 694f730524faea1f2b4840f002ed05f20ad76bed Mon Sep 17 00:00:00 2001 From: browsercash Date: Sat, 19 Sep 2026 17:47:32 -0400 Subject: [PATCH 02/11] Docs v2: battle-test fixes (preview branch; the v2 branch is the source) Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01CnA3RKAvP4gwSd9qrwBFCg --- README.md | 10 +- api-reference/openapi.json | 47 +- api-reference/overview.mdx | 8 +- docs.json | 1 + docs/account/billing.mdx | 6 +- docs/frameworks/browser-use.mdx | 9 +- docs/frameworks/crawl4ai.mdx | 8 +- docs/frameworks/patchright.mdx | 34 +- docs/frameworks/playwright.mdx | 16 - docs/frameworks/puppeteer.mdx | 29 +- docs/frameworks/stagehand.mdx | 9 +- docs/options/browser-types.mdx | 2 +- docs/options/countries.mdx | 260 ++++++ docs/options/location.mdx | 7 +- docs/options/profiles.mdx | 5 +- docs/options/window-size.mdx | 2 +- docs/sessions/connect.mdx | 4 +- docs/sessions/create.mdx | 44 +- docs/sessions/errors.mdx | 22 +- docs/sessions/lifecycle.mdx | 30 +- docs/sessions/live-view.mdx | 2 +- docs/start/best-practices.mdx | 14 +- docs/start/for-agents.mdx | 171 ++-- docs/start/index.mdx | 2 +- docs/start/quickstart.mdx | 38 +- package.json | 3 +- scripts/data/iana-timezones-by-country.json | 920 ++++++++++++++++++++ scripts/data/lpcab-country-map.json | 801 +++++++++++++++++ scripts/gen-countries.mjs | 33 + scripts/sync-openapi.mjs | 29 +- 30 files changed, 2374 insertions(+), 192 deletions(-) create mode 100644 docs/options/countries.mdx create mode 100644 scripts/data/iana-timezones-by-country.json create mode 100644 scripts/data/lpcab-country-map.json create mode 100644 scripts/gen-countries.mjs diff --git a/README.md b/README.md index a0a9aa0..9f37f2f 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,16 @@ absent: `scripts/sync-openapi.mjs` drops them from the generated reference, and npm install # Mintlify CLI npm run sync # refresh api-reference/openapi.json from the live API npm run dev # http://localhost:3000 -npm run check # broken links +npm run check # broken links (reports every link as broken on Windows; a CLI path bug, not the docs) +npm run countries # regenerate docs/options/countries.mdx from scripts/data/ (the gateway's locale catalog) ``` Old URLs redirect to their new homes (see `redirects` in `docs.json`), so links from the dashboard and the landing page keep working. + +## Battle-tested + +On 2026-09-19 five fresh agents (Node, Python, REST-only, framework migration, coding-agent handoff) integrated Driver +from this folder alone and reported every gap; the fixes are in the git history of that day. Re-run the exercise after +big changes: the reports live in the dashboard repo's docs/STATUS-LOG.md entry for that date. also corrects four things the generator gets wrong (duration bound, negative balances, PATCH 400, branding); keep +those corrections when the source changes. diff --git a/api-reference/openapi.json b/api-reference/openapi.json index 22da78a..8b5b41b 100644 --- a/api-reference/openapi.json +++ b/api-reference/openapi.json @@ -5,21 +5,20 @@ "title": "Driver API", "description": "API for interacting with the Driver.dev browser network.", "contact": { - "name": "Contact Us", - "email": "hello@driver.dev", - "url": "https://driver.dev" + "name": "Driver support", + "email": "support@driver.dev" } }, "externalDocs": { - "url": "https://dash.browser.cash/docs", - "description": "Learn more" + "url": "https://docs.driver.dev", + "description": "Driver documentation" }, "components": { "securitySchemes": { "Bearer": { "type": "http", "scheme": "bearer", - "description": "Your Driver.dev API token (browser or agent, they work interchangeably). Get one at https://app.driver.dev" + "description": "A workspace API key from Settings → API keys in the dashboard (https://app.driver.dev), sent as a bearer token." } }, "schemas": { @@ -291,7 +290,7 @@ "url": { "type": "string", "minLength": 1, - "description": "Initial URL opened by the browser after launch. Optional. Forwarded to node-side launch when supported.", + "description": "Initial URL opened by the browser after launch. Optional. Opened right after launch.", "example": "https://example.com" }, "proxyUrl": { @@ -301,19 +300,19 @@ }, "windowSize": { "type": "string", - "description": "Cloaked outer browser-window size in WIDTHxHEIGHT format. Width must be 320–16384 and height 200–16384.", + "description": "Outer browser-window size in WIDTHxHEIGHT format. Width must be 320–16384 and height 200–16384.", "example": "1920x1040" }, "displaySize": { "type": "string", - "description": "Virtual display size in WIDTHxHEIGHT format. Width must be 320–16384 and height 200–16384. Only supported by hosted types.", + "description": "Virtual display size in WIDTHxHEIGHT format. Width must be 320–16384 and height 200–16384.", "example": "1920x1080" }, "timezone": { "type": "string", "minLength": 1, "maxLength": 128, - "description": "IANA timezone for the browser. It must be listed for the selected country. When omitted, Driver selects a dominant regional timezone and targets managed proxy geography where the provider supports regional targeting.", + "description": "IANA timezone for the browser. It must be listed for the selected country. When omitted, Driver selects a dominant regional timezone and, where Driver runs a managed network there, routes the session through that region.", "example": "America/New_York" }, "language": { @@ -326,7 +325,6 @@ "duration": { "type": "integer", "minimum": 60, - "exclusiveMinimum": true, "maximum": 3600, "description": "The duration of the browser session in seconds. Optional. Default is 3600 seconds (1 hour). Cannot be greater than 3600 seconds (1 hour). Cannot be less than 60 seconds (1 minute).", "example": 600 @@ -366,7 +364,7 @@ "items": { "type": "string" }, - "description": "Internal extension IDs returned by the extensions API. Supported by all hosted types. Requires account-level extension access.", + "description": "Internal extension IDs returned by the extensions API. Requires account-level extension access.", "example": [ "abc123def456" ] @@ -453,7 +451,7 @@ "servedBy": "this-node-id", "createdAt": "2025-11-20T01:51:25.000Z", "stoppedAt": null, - "cdpUrl": "wss://sessions.driver.dev/v1/consumer/example-session-id/devtools/browser/uuid", + "cdpUrl": "wss://sessions.driver.dev/v1/session/example-session-id/devtools/browser/uuid", "note": null } } @@ -710,7 +708,7 @@ "createdAt": "2025-11-20T01:51:25.000Z", "stoppedAt": null, "bandwidthBytes": 1048576, - "cdpUrl": "wss://sessions.driver.dev/v1/consumer/example-session-id/devtools/browser/uuid", + "cdpUrl": "wss://sessions.driver.dev/v1/session/example-session-id/devtools/browser/uuid", "note": null } } @@ -991,6 +989,24 @@ } } }, + "400": { + "description": "The session has ended (\"Cannot update a completed or errored session\") or the note is longer than 256 characters.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, "401": { "description": "Missing or invalid token.", "content": { @@ -1526,8 +1542,7 @@ "properties": { "balance_cents": { "type": "integer", - "minimum": 0, - "description": "The current account balance in cents." + "description": "The current account balance in cents. Can be negative when usage exceeds prepaid credit." }, "network_mb_remaining": { "type": "integer", diff --git a/api-reference/overview.mdx b/api-reference/overview.mdx index ecfd357..ab98df2 100644 --- a/api-reference/overview.mdx +++ b/api-reference/overview.mdx @@ -22,7 +22,9 @@ Authorization: Bearer $DRIVER_API_KEY - Request and response bodies are JSON, except extension uploads, which are `multipart/form-data`. - Identifiers that select a resource go in the query string (`?sessionId=…`, `?profileName=…`), not the path, except for extensions. - Errors are `{ "error": "message" }` with a meaningful HTTP status; some carry a `code`. See [Errors](/docs/sessions/errors). -- Timestamps are ISO 8601 strings; money is in cents (and millicents where exactness matters); bandwidth is bytes on sessions and megabytes on billing. +- Session timestamps are ISO 8601 strings; billing periods use Unix epoch milliseconds (fields ending in `_ms`, and the subscription period bounds). Money is in cents (and millicents where exactness matters); bandwidth is bytes on sessions and megabytes on billing. +- Unknown fields in a request body are rejected with `400`. +- Create answers `200` with the session; only the extension upload answers `201`. ## Endpoints @@ -37,6 +39,6 @@ Authorization: Bearer $DRIVER_API_KEY These reference pages are generated from the API's own OpenAPI document, served live at `https://api.driver.dev/doc` and rendered at [api.driver.dev/scalar](https://api.driver.dev/scalar). If a page here and the live document ever disagree, the live document wins. -## SDK +## Client code -`@browsercash/sdk` on npm wraps these endpoints for TypeScript; set `baseURL: "https://api.driver.dev"`. Python users call the endpoints directly with `requests`; the [For agents](/docs/start/for-agents) page has a small client module for both. +There is no SDK to install. The endpoints are plain HTTP; the [For agents](/docs/start/for-agents#reference-implementation) page has a small client module for TypeScript and Python that you can copy. diff --git a/docs.json b/docs.json index 6949d22..349bc78 100644 --- a/docs.json +++ b/docs.json @@ -67,6 +67,7 @@ "pages": [ "docs/options/browser-types", "docs/options/location", + "docs/options/countries", "docs/options/window-size", "docs/options/proxies", "docs/options/profiles", diff --git a/docs/account/billing.mdx b/docs/account/billing.mdx index f066b1b..e91583c 100644 --- a/docs/account/billing.mdx +++ b/docs/account/billing.mdx @@ -3,7 +3,7 @@ title: "Billing" description: "Sessions are metered by bandwidth against the workspace's credits. Two endpoints expose the balance and the plan." --- -Driver bills on **bandwidth**: the bytes a session moves, metered per megabyte and charged against the workspace's credits. A plan includes a monthly allowance and a concurrency limit; usage beyond the allowance draws on purchased credits. The exact rates and plans are on the [pricing page](https://driver.dev/pricing) and under **Billing** in the dashboard. +Driver bills on **bandwidth**: the bytes a session moves, metered per megabyte and charged against the workspace's credits. A plan includes a monthly allowance and a concurrency limit; usage beyond the allowance draws on purchased credits. A running session that moves no bytes costs nothing, but it holds one of the plan's concurrent-browser slots until it stops. Keeping sessions short and stopping them when the job is done is the whole cost-control story. `duration` on create caps what a forgotten session can cost. @@ -27,11 +27,11 @@ balance = balance["balance"] { "balance": { "balance_cents": 1250, "network_mb_remaining": 500, "status": "active" } } ``` -`network_mb_remaining` is the included allowance still unused this period. Sessions are refused with `402` once the balance is negative and the allowance is gone. +`balance_cents` can go below zero. `network_mb_remaining` (the included allowance still unused this period) and `status` are present for workspace accounts and absent on a legacy account. Sessions are refused with `402` once the balance is negative and the allowance is gone. ## Billing details -`GET /v1/account/billing` returns the full picture: the plan (`plan_key`, `display_name`, `price_monthly_cents`, `included_network_mb`, `overage_millicents_per_mb`, `concurrent_browsers`, `max_session_seconds`), the current period (`start_ms`, `end_ms`, `network_mb_used`, `network_mb_remaining`), the wallet (`balance_cents`, `balance_millicents`), and the subscription (`status`, `current_period_end`, `cancel_at_period_end`) when there is one. +`GET /v1/account/billing` returns the full picture: `orgId`; `status` (`legacy` for an account not yet moved to workspace billing); the plan (`plan_key`, `display_name`, `price_monthly_cents`, `included_network_mb`, `overage_millicents_per_mb`, `concurrent_browsers`, `max_session_seconds`); the current period (`start_ms`, `end_ms`, `included_network_mb`, `network_mb_used`, `network_mb_remaining`); the wallet (`balance_cents`, which can be negative, and `balance_millicents`); and the subscription when there is one (`subscription_id`, `provider`, `status`, `current_period_start`, `current_period_end`, `cancel_at_period_end`). `plan`, `period` and `subscription` are `null` on a legacy account. Times ending in `_ms` and the subscription period bounds are Unix epoch milliseconds. ```bash curl https://api.driver.dev/v1/account/billing -H "Authorization: Bearer $DRIVER_API_KEY" diff --git a/docs/frameworks/browser-use.mdx b/docs/frameworks/browser-use.mdx index 0aafc72..3614154 100644 --- a/docs/frameworks/browser-use.mdx +++ b/docs/frameworks/browser-use.mdx @@ -18,9 +18,11 @@ headers = {"Authorization": f"Bearer {os.environ['DRIVER_API_KEY']}"} async def main(): - session = requests.post( + r = requests.post( f"{API}/v1/browser/session", headers=headers, json={"country": "US", "duration": 900} - ).json() + ) + r.raise_for_status() + session = r.json() try: browser = Browser(cdp_url=session["cdpUrl"]) agent = Agent( @@ -42,5 +44,6 @@ asyncio.run(main()) ## Notes - Agents take many steps; set `duration` to cover the task (up to 3600 seconds) and stop the session in `finally`. -- Browser Use uses CDP directly and does not go through Playwright, so the [Patchright](/docs/frameworks/patchright) recommendation does not apply to it. +- Browser Use drives the browser over CDP directly rather than through Playwright, so Patchright is not in the picture. The fingerprint concern is the same, though: it comes from the CDP commands a client issues, and an agent framework issues more of them than a plain script. Expect challenges on protected sites. +- Pin the `browser-use` version you tested with; its `Browser`/`Agent` constructors have changed between releases. Passing `cdp_url` is what stops it from launching or downloading a browser of its own. - Watch the agent through the session's [live view](/docs/sessions/live-view); it is the fastest way to see where an agent goes wrong. diff --git a/docs/frameworks/crawl4ai.mdx b/docs/frameworks/crawl4ai.mdx index 5296e1a..e9d16b1 100644 --- a/docs/frameworks/crawl4ai.mdx +++ b/docs/frameworks/crawl4ai.mdx @@ -18,7 +18,9 @@ headers = {"Authorization": f"Bearer {os.environ['DRIVER_API_KEY']}"} async def main(): - session = requests.post(f"{API}/v1/browser/session", headers=headers, json={"country": "US"}).json() + r = requests.post(f"{API}/v1/browser/session", headers=headers, json={"country": "US"}) + r.raise_for_status() + session = r.json() try: config = BrowserConfig(browser_mode="custom", cdp_url=session["cdpUrl"], use_managed_browser=True) async with AsyncWebCrawler(config=config) as crawler: @@ -35,5 +37,7 @@ asyncio.run(main()) ## Notes -- `browser_mode="custom"` with `cdp_url` tells Crawl4AI to attach to the browser you give it instead of launching one; `use_managed_browser=True` keeps control over CDP. +- `browser_mode="custom"` with `cdp_url` tells Crawl4AI to attach to the browser you give it instead of launching one; `use_managed_browser=True` makes it drive that browser over CDP rather than through a Playwright launch. Leaving the `async with` block closes Crawl4AI's connection only; the session runs until you stop it. +- Crawl4AI is Playwright underneath and injects its own scripts, so it carries the stock-Playwright fingerprint. Use it for sites that do not challenge automation. +- Pin the `crawl4ai` version you tested with. Its post-install step downloads a browser you will not use; that is harmless. - Crawl several URLs in one session; creating the session is the expensive step. diff --git a/docs/frameworks/patchright.mdx b/docs/frameworks/patchright.mdx index 6d40107..d0a97be 100644 --- a/docs/frameworks/patchright.mdx +++ b/docs/frameworks/patchright.mdx @@ -40,21 +40,25 @@ const headers = { "Content-Type": "application/json", }; -const session = await fetch(`${API}/v1/browser/session`, { +const res = await fetch(`${API}/v1/browser/session`, { method: "POST", headers, body: JSON.stringify({ country: "US" }), -}).then((r) => r.json()); +}); +if (!res.ok) throw new Error(`Driver ${res.status}: ${(await res.json()).error}`); +const session = await res.json(); try { const browser = await chromium.connectOverCDP(session.cdpUrl); - const context = browser.contexts()[0] ?? (await browser.newContext()); - const page = context.pages()[0] ?? (await context.newPage()); - - await page.goto("https://example.com"); - console.log(await page.title()); - - await browser.close(); + try { + const context = browser.contexts()[0] ?? (await browser.newContext()); + const page = context.pages()[0] ?? (await context.newPage()); + + await page.goto("https://example.com"); + console.log(await page.title()); + } finally { + await browser.close(); // your connection only; the session keeps running until you stop it + } } finally { await fetch(`${API}/v1/browser/session?sessionId=${session.sessionId}`, { method: "DELETE", @@ -70,7 +74,8 @@ API = "https://api.driver.dev" headers = {"Authorization": f"Bearer {os.environ['DRIVER_API_KEY']}"} session = requests.post(f"{API}/v1/browser/session", headers=headers, json={"country": "US"}) - session = session.json() +session.raise_for_status() +session = session.json() try: with sync_playwright() as p: browser = p.chromium.connect_over_cdp(session["cdpUrl"]) @@ -94,6 +99,7 @@ headers = {"Authorization": f"Bearer {os.environ['DRIVER_API_KEY']}"} async def main(): session = requests.post(f"{API}/v1/browser/session", headers=headers, json={"country": "US"}) + session.raise_for_status() session = session.json() try: async with async_playwright() as p: @@ -105,8 +111,10 @@ async def main(): await browser.close() finally: requests.delete( - f"{API}/v1/browser/session", headers=headers, params={"sessionId": session["sessionId"]} - ) + f"{API}/v1/browser/session", + headers=headers, + params={"sessionId": session["sessionId"]}, + ) asyncio.run(main()) ``` @@ -128,6 +136,8 @@ Change the import. That is the whole migration. Remove any `chromium.launch()` and any `playwright install` step: Driver provides the browser. +Coming from Puppeteer is a rewrite, not a rename: Patchright has Playwright's API. Either keep Puppeteer with the [connect-only setup](/docs/frameworks/puppeteer) and accept its fingerprint, or port to Patchright. + ## Things Patchright does not fix Patchright removes the client's fingerprints. It cannot undo choices in your script that look automated: fresh contexts for every page, broad `page.route` interception, injected init scripts, overridden `navigator` properties, or clicking a thousand things per minute. The [best practices](/docs/start/best-practices) cover those. diff --git a/docs/frameworks/playwright.mdx b/docs/frameworks/playwright.mdx index 0c6ace6..c51fe4e 100644 --- a/docs/frameworks/playwright.mdx +++ b/docs/frameworks/playwright.mdx @@ -35,22 +35,6 @@ with sync_playwright() as p: Install with `npm install playwright` or `pip install playwright`. Do not run `playwright install`; the browser is Driver's. -## Using the TypeScript SDK for the session calls - -The `@browsercash/sdk` package wraps the REST API. Point it at Driver's base URL: - -```typescript -import BrowsercashSDK from "@browsercash/sdk"; - -const client = new BrowsercashSDK({ - apiKey: process.env.DRIVER_API_KEY!, - baseURL: "https://api.driver.dev", -}); -const session = await client.browser.session.create({ country: "US" }); -// … connect with patchright or playwright … -await client.browser.session.stop({ sessionId: session.sessionId }); -``` - ## Everything else Contexts, tabs, disconnecting versus stopping, reconnecting and timeouts are the same as for Patchright; see [Connect over CDP](/docs/sessions/connect). diff --git a/docs/frameworks/puppeteer.mdx b/docs/frameworks/puppeteer.mdx index 5f0ab90..bff2cc9 100644 --- a/docs/frameworks/puppeteer.mdx +++ b/docs/frameworks/puppeteer.mdx @@ -22,20 +22,27 @@ const headers = { "Content-Type": "application/json", }; -const session = await fetch(`${API}/v1/browser/session`, { +const res = await fetch(`${API}/v1/browser/session`, { method: "POST", headers, body: JSON.stringify({ country: "US" }), -}).then((r) => r.json()); +}); +if (!res.ok) throw new Error(`Driver ${res.status}: ${(await res.json()).error}`); +const session = await res.json(); try { - const browser = await puppeteer.connect({ browserWSEndpoint: session.cdpUrl }); - const [page = await browser.newPage()] = await browser.pages(); // reuse the open tab - - await page.goto("https://example.com"); - console.log(await page.title()); - - await browser.disconnect(); // ends your connection only + // defaultViewport: null keeps the window's real size; Puppeteer's default is an 800x600 viewport, + // which would undo the windowSize you set on create and mismatch outerWidth. + const browser = await puppeteer.connect({ browserWSEndpoint: session.cdpUrl, defaultViewport: null }); + try { + const pages = await browser.pages(); + const page = pages[0] ?? (await browser.newPage()); // reuse the open tab + + await page.goto("https://example.com"); + console.log(await page.title()); + } finally { + await browser.disconnect(); // ends your connection only + } } finally { await fetch(`${API}/v1/browser/session?sessionId=${session.sessionId}`, { method: "DELETE", @@ -48,4 +55,6 @@ try { - End your connection with `browser.disconnect()`, then stop the session through the API. Do not use `browser.close()`: it tells the remote Chrome to quit, and the session record is then closed from the browser's side rather than by your stop call. - `browser.pages()` returns the tab the session opened with. Reuse it rather than creating a fresh one. -- Viewport: prefer `windowSize` on create over `page.setViewport`, see [Window size](/docs/options/window-size). +- Viewport: connect with `defaultViewport: null` and prefer `windowSize` on create over `page.setViewport`, see [Window size](/docs/options/window-size). +- The hooks to avoid, in Puppeteer's names: `page.setRequestInterception`, `page.evaluateOnNewDocument`, `page.exposeFunction`, `page.setUserAgent`, `page.emulateTimezone`, `page.emulate`. See [Best practices](/docs/start/best-practices). +- Pin the `puppeteer-core` version you tested with; each release targets a Chrome revision. diff --git a/docs/frameworks/stagehand.mdx b/docs/frameworks/stagehand.mdx index 42477b0..4d8f556 100644 --- a/docs/frameworks/stagehand.mdx +++ b/docs/frameworks/stagehand.mdx @@ -19,11 +19,13 @@ const headers = { "Content-Type": "application/json", }; -const session = await fetch(`${API}/v1/browser/session`, { +const res = await fetch(`${API}/v1/browser/session`, { method: "POST", headers, body: JSON.stringify({ country: "US" }), -}).then((r) => r.json()); +}); +if (!res.ok) throw new Error(`Driver ${res.status}: ${(await res.json()).error}`); +const session = await res.json(); try { // Attach to Driver's Chrome; Stagehand never launches or closes a browser it did not start. @@ -56,5 +58,6 @@ Stagehand reads no environment variables itself: pass the model provider key in ## Notes - This is the v4 API (`localBrowser.connect`, `Stagehand.create`). Stagehand 2.x used `new Stagehand({ env: "LOCAL", localBrowserLaunchOptions: { cdpUrl } })`; if you are on 2.x, that is the equivalent. -- Stagehand instruments the page to work (it loads its own extension and drives the browser over CDP), which is the kind of signal protected sites look for. On those sites, prefer plain [Patchright](/docs/frameworks/patchright) with your own extraction, or keep Stagehand to the parts of a flow that are not behind bot protection. +- Stagehand instruments the page to work (it injects scripts and drives the browser over CDP), which is the kind of signal protected sites look for. On those sites, prefer plain [Patchright](/docs/frameworks/patchright) with explicit selectors. +- Pin the Stagehand version you tested with; the connect API changed between 2.x and v4. - Reuse the page from `browser.context.pages()` rather than opening a new one. diff --git a/docs/options/browser-types.mdx b/docs/options/browser-types.mdx index 9356789..48a6373 100644 --- a/docs/options/browser-types.mdx +++ b/docs/options/browser-types.mdx @@ -9,7 +9,7 @@ description: "The type field picks the browser class: hosted by default, with tw |---|---|---| | `hosted` (default) | Real Chrome on a hosted machine, native behaviour. | Almost everything. | | `hosted_stealth` | Same browser with a conservative stealth policy. | Sites that challenge the default and you want the least invasive change. | -| `hosted_privacy` | Same browser with the aggressive stealth policy. | Hostile targets. Try `hosted_stealth` first; more masking is not always better. Both stealth types were verified to start normally. | +| `hosted_privacy` | Same browser with the aggressive stealth policy. | Hostile targets. Try `hosted_stealth` first; more masking is not always better. | ```json Request diff --git a/docs/options/countries.mdx b/docs/options/countries.mdx new file mode 100644 index 0000000..378279c --- /dev/null +++ b/docs/options/countries.mdx @@ -0,0 +1,260 @@ +--- +title: "Countries" +description: "Every country you can pass on create, with the timezones and languages it accepts. Generated from the API's locale catalog." +--- + +`country` accepts any of the 249 two-letter codes below. `timezone` must be one of that country's timezones and `language` one of its languages, or the request is rejected with `400`. Leave both out and Driver picks the first-listed timezone (or a managed-network region where it has one) and the country's primary language. US sessions always use `en-US`. See [Location](/docs/options/location) for how the three stay coherent. + +Availability of machines varies by country; a country with no machine free at the moment answers `503`. + +| Code | Country | Timezones | Languages | +|---|---|---|---| +| `AD` | Andorra | `Europe/Andorra` | `ca-ES` | +| `AE` | United Arab Emirates | `Asia/Dubai` | `ar-SA` | +| `AF` | Afghanistan | `Asia/Kabul` | `en-GB` | +| `AG` | Antigua & Barbuda | `America/Antigua` | `en-US` | +| `AI` | Anguilla | `America/Anguilla` | `en-US` | +| `AL` | Albania | `Europe/Tirane` | `en-GB` | +| `AM` | Armenia | `Asia/Yerevan` | `ru-RU` | +| `AO` | Angola | `Africa/Luanda` | `pt-PT` | +| `AQ` | Antarctica | `Antarctica/McMurdo`, `Antarctica/Casey`, `Antarctica/Davis`, `Antarctica/DumontDUrville`, `Antarctica/Mawson`, `Antarctica/Palmer`, `Antarctica/Rothera`, `Antarctica/Syowa`, `Antarctica/Troll`, `Antarctica/Vostok` | `en-GB` | +| `AR` | Argentina | `America/Argentina/Buenos_Aires`, `America/Argentina/Cordoba`, `America/Argentina/Salta`, `America/Argentina/Jujuy`, `America/Argentina/Tucuman`, `America/Argentina/Catamarca`, `America/Argentina/La_Rioja`, `America/Argentina/San_Juan`, `America/Argentina/Mendoza`, `America/Argentina/San_Luis`, `America/Argentina/Rio_Gallegos`, `America/Argentina/Ushuaia` | `es-MX` | +| `AS` | American Samoa | `Pacific/Pago_Pago` | `en-US` | +| `AT` | Austria | `Europe/Vienna` | `de-DE`, `hr-HR`, `sl-SI`, `hu-HU` | +| `AU` | Australia | `Australia/Lord_Howe`, `Antarctica/Macquarie`, `Australia/Hobart`, `Australia/Melbourne`, `Australia/Sydney`, `Australia/Broken_Hill`, `Australia/Brisbane`, `Australia/Lindeman`, `Australia/Adelaide`, `Australia/Darwin`, `Australia/Perth`, `Australia/Eucla` | `en-GB` | +| `AW` | Aruba | `America/Aruba` | `nl-NL` | +| `AX` | Åland Islands | `Europe/Mariehamn` | `sv-SE` | +| `AZ` | Azerbaijan | `Asia/Baku` | `en-GB` | +| `BA` | Bosnia & Herzegovina | `Europe/Sarajevo` | `hr-HR`, `sr-Latn-RS` | +| `BB` | Barbados | `America/Barbados` | `en-US` | +| `BD` | Bangladesh | `Asia/Dhaka` | `en-GB` | +| `BE` | Belgium | `Europe/Brussels` | `nl-NL`, `fr-FR`, `de-DE` | +| `BF` | Burkina Faso | `Africa/Ouagadougou` | `fr-FR` | +| `BG` | Bulgaria | `Europe/Sofia` | `bg-BG` | +| `BH` | Bahrain | `Asia/Bahrain` | `ar-SA` | +| `BI` | Burundi | `Africa/Bujumbura` | `fr-FR`, `en-GB` | +| `BJ` | Benin | `Africa/Porto-Novo` | `fr-FR` | +| `BL` | St. Barthélemy | `America/St_Barthelemy` | `fr-FR` | +| `BM` | Bermuda | `Atlantic/Bermuda` | `en-US` | +| `BN` | Brunei | `Asia/Brunei` | `zh-TW` | +| `BO` | Bolivia | `America/La_Paz` | `es-MX` | +| `BQ` | Caribbean Netherlands | `America/Kralendijk` | `nl-NL` | +| `BR` | Brazil | `America/Noronha`, `America/Belem`, `America/Fortaleza`, `America/Recife`, `America/Araguaina`, `America/Maceio`, `America/Bahia`, `America/Sao_Paulo`, `America/Campo_Grande`, `America/Cuiaba`, `America/Santarem`, `America/Porto_Velho`, `America/Boa_Vista`, `America/Manaus`, `America/Eirunepe`, `America/Rio_Branco` | `pt-BR` | +| `BS` | Bahamas | `America/Nassau` | `en-US` | +| `BT` | Bhutan | `Asia/Thimphu` | `en-GB` | +| `BV` | Bouvet Island | `Europe/Oslo` | `en-GB` | +| `BW` | Botswana | `Africa/Gaborone` | `en-GB` | +| `BY` | Belarus | `Europe/Minsk` | `ru-RU` | +| `BZ` | Belize | `America/Belize` | `en-US` | +| `CA` | Canada | `America/St_Johns`, `America/Halifax`, `America/Glace_Bay`, `America/Moncton`, `America/Goose_Bay`, `America/Blanc-Sablon`, `America/Toronto`, `America/Iqaluit`, `America/Atikokan`, `America/Winnipeg`, `America/Resolute`, `America/Rankin_Inlet`, `America/Regina`, `America/Swift_Current`, `America/Edmonton`, `America/Cambridge_Bay`, `America/Inuvik`, `America/Vancouver`, `America/Creston`, `America/Dawson_Creek`, `America/Fort_Nelson`, `America/Whitehorse`, `America/Dawson` | `en-US`, `fr-CA` | +| `CC` | Cocos (Keeling) Islands | `Indian/Cocos` | `en-GB` | +| `CD` | Congo - Kinshasa | `Africa/Kinshasa`, `Africa/Lubumbashi` | `fr-FR` | +| `CF` | Central African Republic | `Africa/Bangui` | `fr-FR` | +| `CG` | Congo - Brazzaville | `Africa/Brazzaville` | `fr-FR` | +| `CH` | Switzerland | `Europe/Zurich` | `de-DE`, `fr-FR`, `it-IT` | +| `CI` | Côte d’Ivoire | `Africa/Abidjan` | `fr-FR` | +| `CK` | Cook Islands | `Pacific/Rarotonga` | `en-GB` | +| `CL` | Chile | `America/Santiago`, `America/Coyhaique`, `America/Punta_Arenas`, `Pacific/Easter` | `es-MX` | +| `CM` | Cameroon | `Africa/Douala` | `fr-FR`, `en-GB` | +| `CN` | China | `Asia/Shanghai`, `Asia/Urumqi` | `zh-CN`, `ko-KR` | +| `CO` | Colombia | `America/Bogota` | `es-MX` | +| `CR` | Costa Rica | `America/Costa_Rica` | `es-MX` | +| `CU` | Cuba | `America/Havana` | `es-MX` | +| `CV` | Cape Verde | `Atlantic/Cape_Verde` | `pt-PT` | +| `CW` | Curaçao | `America/Curacao` | `nl-NL` | +| `CX` | Christmas Island | `Indian/Christmas` | `en-GB` | +| `CY` | Cyprus | `Asia/Nicosia`, `Asia/Famagusta` | `el-GR`, `tr-TR` | +| `CZ` | Czechia | `Europe/Prague` | `cs-CZ` | +| `DE` | Germany | `Europe/Berlin`, `Europe/Busingen` | `de-DE` | +| `DJ` | Djibouti | `Africa/Djibouti` | `fr-FR`, `ar-SA` | +| `DK` | Denmark | `Europe/Copenhagen` | `da-DK`, `de-DE` | +| `DM` | Dominica | `America/Dominica` | `en-US` | +| `DO` | Dominican Republic | `America/Santo_Domingo` | `es-MX` | +| `DZ` | Algeria | `Africa/Algiers` | `ar-SA`, `fr-FR` | +| `EC` | Ecuador | `America/Guayaquil`, `Pacific/Galapagos` | `es-MX` | +| `EE` | Estonia | `Europe/Tallinn` | `et-EE` | +| `EG` | Egypt | `Africa/Cairo` | `ar-SA` | +| `EH` | Western Sahara | `Africa/El_Aaiun` | `ar-SA` | +| `ER` | Eritrea | `Africa/Asmara` | `en-GB`, `ar-SA` | +| `ES` | Spain | `Europe/Madrid`, `Africa/Ceuta`, `Atlantic/Canary` | `es-ES`, `ca-ES`, `gl-ES`, `eu-ES` | +| `ET` | Ethiopia | `Africa/Addis_Ababa` | `en-GB` | +| `FI` | Finland | `Europe/Helsinki` | `fi-FI`, `sv-SE` | +| `FJ` | Fiji | `Pacific/Fiji` | `en-GB` | +| `FK` | Falkland Islands | `Atlantic/Stanley` | `en-US` | +| `FM` | Micronesia | `Pacific/Chuuk`, `Pacific/Pohnpei`, `Pacific/Kosrae` | `en-US` | +| `FO` | Faroe Islands | `Atlantic/Faroe` | `en-GB` | +| `FR` | France | `Europe/Paris` | `fr-FR` | +| `GA` | Gabon | `Africa/Libreville` | `fr-FR` | +| `GB` | United Kingdom | `Europe/London` | `en-GB` | +| `GD` | Grenada | `America/Grenada` | `en-US` | +| `GE` | Georgia | `Asia/Tbilisi` | `ru-RU` | +| `GF` | French Guiana | `America/Cayenne` | `fr-FR` | +| `GG` | Guernsey | `Europe/Guernsey` | `en-GB` | +| `GH` | Ghana | `Africa/Accra` | `en-GB` | +| `GI` | Gibraltar | `Europe/Gibraltar` | `en-GB` | +| `GL` | Greenland | `America/Nuuk`, `America/Danmarkshavn`, `America/Scoresbysund`, `America/Thule` | `da-DK` | +| `GM` | Gambia | `Africa/Banjul` | `en-GB` | +| `GN` | Guinea | `Africa/Conakry` | `fr-FR` | +| `GP` | Guadeloupe | `America/Guadeloupe` | `fr-FR` | +| `GQ` | Equatorial Guinea | `Africa/Malabo` | `es-ES`, `fr-FR`, `pt-PT` | +| `GR` | Greece | `Europe/Athens` | `el-GR` | +| `GS` | South Georgia & South Sandwich Islands | `Atlantic/South_Georgia` | `en-US` | +| `GT` | Guatemala | `America/Guatemala` | `es-MX` | +| `GU` | Guam | `Pacific/Guam` | `en-US` | +| `GW` | Guinea-Bissau | `Africa/Bissau` | `pt-PT` | +| `GY` | Guyana | `America/Guyana` | `en-US` | +| `HK` | Hong Kong SAR China | `Asia/Hong_Kong` | `zh-TW`, `en-GB` | +| `HM` | Heard & McDonald Islands | `Indian/Kerguelen` | `en-GB` | +| `HN` | Honduras | `America/Tegucigalpa` | `es-MX` | +| `HR` | Croatia | `Europe/Zagreb` | `hr-HR`, `it-IT` | +| `HT` | Haiti | `America/Port-au-Prince` | `fr-FR` | +| `HU` | Hungary | `Europe/Budapest` | `hu-HU` | +| `ID` | Indonesia | `Asia/Jakarta`, `Asia/Pontianak`, `Asia/Makassar`, `Asia/Jayapura` | `id-ID` | +| `IE` | Ireland | `Europe/Dublin` | `en-GB` | +| `IL` | Israel | `Asia/Jerusalem` | `he-IL`, `ar-SA` | +| `IM` | Isle of Man | `Europe/Isle_of_Man` | `en-GB` | +| `IN` | India | `Asia/Kolkata` | `en-GB` | +| `IO` | British Indian Ocean Territory | `Indian/Chagos` | `en-GB` | +| `IQ` | Iraq | `Asia/Baghdad` | `ar-SA` | +| `IR` | Iran | `Asia/Tehran` | `en-GB` | +| `IS` | Iceland | `Atlantic/Reykjavik` | `en-GB` | +| `IT` | Italy | `Europe/Rome` | `it-IT`, `fr-FR` | +| `JE` | Jersey | `Europe/Jersey` | `en-GB` | +| `JM` | Jamaica | `America/Jamaica` | `en-US` | +| `JO` | Jordan | `Asia/Amman` | `ar-SA` | +| `JP` | Japan | `Asia/Tokyo` | `ja-JP` | +| `KE` | Kenya | `Africa/Nairobi` | `en-GB` | +| `KG` | Kyrgyzstan | `Asia/Bishkek` | `ru-RU` | +| `KH` | Cambodia | `Asia/Phnom_Penh` | `en-GB` | +| `KI` | Kiribati | `Pacific/Tarawa`, `Pacific/Kanton`, `Pacific/Kiritimati` | `en-GB` | +| `KM` | Comoros | `Indian/Comoro` | `ar-SA`, `fr-FR` | +| `KN` | St. Kitts & Nevis | `America/St_Kitts` | `en-US` | +| `KP` | North Korea | `Asia/Pyongyang` | `ko-KR` | +| `KR` | South Korea | `Asia/Seoul` | `ko-KR` | +| `KW` | Kuwait | `Asia/Kuwait` | `ar-SA` | +| `KY` | Cayman Islands | `America/Cayman` | `en-US` | +| `KZ` | Kazakhstan | `Asia/Almaty`, `Asia/Qyzylorda`, `Asia/Qostanay`, `Asia/Aqtobe`, `Asia/Aqtau`, `Asia/Atyrau`, `Asia/Oral` | `ru-RU` | +| `LA` | Laos | `Asia/Vientiane` | `en-GB` | +| `LB` | Lebanon | `Asia/Beirut` | `ar-SA` | +| `LC` | St. Lucia | `America/St_Lucia` | `en-US` | +| `LI` | Liechtenstein | `Europe/Vaduz` | `de-DE` | +| `LK` | Sri Lanka | `Asia/Colombo` | `en-GB` | +| `LR` | Liberia | `Africa/Monrovia` | `en-GB` | +| `LS` | Lesotho | `Africa/Maseru` | `en-GB` | +| `LT` | Lithuania | `Europe/Vilnius` | `lt-LT` | +| `LU` | Luxembourg | `Europe/Luxembourg` | `fr-FR`, `de-DE` | +| `LV` | Latvia | `Europe/Riga` | `lv-LV` | +| `LY` | Libya | `Africa/Tripoli` | `ar-SA` | +| `MA` | Morocco | `Africa/Casablanca` | `ar-SA`, `fr-FR` | +| `MC` | Monaco | `Europe/Monaco` | `fr-FR` | +| `MD` | Moldova | `Europe/Chisinau` | `ro-RO` | +| `ME` | Montenegro | `Europe/Podgorica` | `sr-Latn-RS` | +| `MF` | St. Martin | `America/Marigot` | `fr-FR` | +| `MG` | Madagascar | `Indian/Antananarivo` | `fr-FR`, `en-GB` | +| `MH` | Marshall Islands | `Pacific/Majuro`, `Pacific/Kwajalein` | `en-US` | +| `MK` | North Macedonia | `Europe/Skopje` | `tr-TR` | +| `ML` | Mali | `Africa/Bamako` | `fr-FR` | +| `MM` | Myanmar (Burma) | `Asia/Yangon` | `en-GB` | +| `MN` | Mongolia | `Asia/Ulaanbaatar`, `Asia/Hovd` | `en-GB` | +| `MO` | Macao SAR China | `Asia/Macau` | `zh-TW`, `pt-PT` | +| `MP` | Northern Mariana Islands | `Pacific/Saipan` | `en-US` | +| `MQ` | Martinique | `America/Martinique` | `fr-FR` | +| `MR` | Mauritania | `Africa/Nouakchott` | `ar-SA` | +| `MS` | Montserrat | `America/Montserrat` | `en-US` | +| `MT` | Malta | `Europe/Malta` | `en-GB` | +| `MU` | Mauritius | `Indian/Mauritius` | `fr-FR`, `en-GB` | +| `MV` | Maldives | `Indian/Maldives` | `en-GB` | +| `MW` | Malawi | `Africa/Blantyre` | `en-GB` | +| `MX` | Mexico | `America/Mexico_City`, `America/Cancun`, `America/Merida`, `America/Monterrey`, `America/Matamoros`, `America/Chihuahua`, `America/Ciudad_Juarez`, `America/Ojinaga`, `America/Mazatlan`, `America/Bahia_Banderas`, `America/Hermosillo`, `America/Tijuana` | `es-MX` | +| `MY` | Malaysia | `Asia/Kuala_Lumpur`, `Asia/Kuching` | `en-GB` | +| `MZ` | Mozambique | `Africa/Maputo` | `pt-PT` | +| `NA` | Namibia | `Africa/Windhoek` | `en-GB` | +| `NC` | New Caledonia | `Pacific/Noumea` | `fr-FR` | +| `NE` | Niger | `Africa/Niamey` | `fr-FR` | +| `NF` | Norfolk Island | `Pacific/Norfolk` | `en-GB` | +| `NG` | Nigeria | `Africa/Lagos` | `en-GB` | +| `NI` | Nicaragua | `America/Managua` | `es-MX` | +| `NL` | Netherlands | `Europe/Amsterdam` | `nl-NL` | +| `NO` | Norway | `Europe/Oslo` | `nb-NO` | +| `NP` | Nepal | `Asia/Kathmandu` | `en-GB` | +| `NR` | Nauru | `Pacific/Nauru` | `en-GB` | +| `NU` | Niue | `Pacific/Niue` | `en-GB` | +| `NZ` | New Zealand | `Pacific/Auckland`, `Pacific/Chatham` | `en-GB` | +| `OM` | Oman | `Asia/Muscat` | `ar-SA` | +| `PA` | Panama | `America/Panama` | `es-MX` | +| `PE` | Peru | `America/Lima` | `es-MX` | +| `PF` | French Polynesia | `Pacific/Tahiti`, `Pacific/Marquesas`, `Pacific/Gambier` | `fr-FR` | +| `PG` | Papua New Guinea | `Pacific/Port_Moresby`, `Pacific/Bougainville` | `en-GB` | +| `PH` | Philippines | `Asia/Manila` | `en-US` | +| `PK` | Pakistan | `Asia/Karachi` | `en-GB` | +| `PL` | Poland | `Europe/Warsaw` | `pl-PL`, `de-DE`, `lt-LT` | +| `PM` | St. Pierre & Miquelon | `America/Miquelon` | `fr-FR` | +| `PN` | Pitcairn Islands | `Pacific/Pitcairn` | `en-GB` | +| `PR` | Puerto Rico | `America/Puerto_Rico` | `es-MX`, `en-US` | +| `PS` | Palestinian Territories | `Asia/Gaza`, `Asia/Hebron` | `ar-SA` | +| `PT` | Portugal | `Europe/Lisbon`, `Atlantic/Madeira`, `Atlantic/Azores` | `pt-PT` | +| `PW` | Palau | `Pacific/Palau` | `en-US` | +| `PY` | Paraguay | `America/Asuncion` | `es-MX` | +| `QA` | Qatar | `Asia/Qatar` | `ar-SA` | +| `RE` | Réunion | `Indian/Reunion` | `fr-FR` | +| `RO` | Romania | `Europe/Bucharest` | `ro-RO` | +| `RS` | Serbia | `Europe/Belgrade` | `sr-Latn-RS`, `hu-HU`, `ro-RO`, `hr-HR`, `sk-SK`, `uk-UA` | +| `RU` | Russia | `Europe/Kaliningrad`, `Europe/Moscow`, `Europe/Kirov`, `Europe/Volgograd`, `Europe/Astrakhan`, `Europe/Saratov`, `Europe/Ulyanovsk`, `Europe/Samara`, `Asia/Yekaterinburg`, `Asia/Omsk`, `Asia/Novosibirsk`, `Asia/Barnaul`, `Asia/Tomsk`, `Asia/Novokuznetsk`, `Asia/Krasnoyarsk`, `Asia/Irkutsk`, `Asia/Chita`, `Asia/Yakutsk`, `Asia/Khandyga`, `Asia/Vladivostok`, `Asia/Ust-Nera`, `Asia/Magadan`, `Asia/Sakhalin`, `Asia/Srednekolymsk`, `Asia/Kamchatka`, `Asia/Anadyr` | `ru-RU` | +| `RW` | Rwanda | `Africa/Kigali` | `en-GB`, `fr-FR` | +| `SA` | Saudi Arabia | `Asia/Riyadh` | `ar-SA` | +| `SB` | Solomon Islands | `Pacific/Guadalcanal` | `en-GB` | +| `SC` | Seychelles | `Indian/Mahe` | `fr-FR`, `en-GB` | +| `SD` | Sudan | `Africa/Khartoum` | `en-GB`, `ar-SA` | +| `SE` | Sweden | `Europe/Stockholm` | `sv-SE`, `fi-FI` | +| `SG` | Singapore | `Asia/Singapore` | `en-GB`, `zh-CN` | +| `SH` | St. Helena | `Atlantic/St_Helena` | `en-GB` | +| `SI` | Slovenia | `Europe/Ljubljana` | `sl-SI` | +| `SJ` | Svalbard & Jan Mayen | `Arctic/Longyearbyen` | `nb-NO` | +| `SK` | Slovakia | `Europe/Bratislava` | `sk-SK` | +| `SL` | Sierra Leone | `Africa/Freetown` | `en-GB` | +| `SM` | San Marino | `Europe/San_Marino` | `it-IT` | +| `SN` | Senegal | `Africa/Dakar` | `fr-FR` | +| `SO` | Somalia | `Africa/Mogadishu` | `ar-SA` | +| `SR` | Suriname | `America/Paramaribo` | `nl-NL` | +| `SS` | South Sudan | `Africa/Juba` | `en-GB` | +| `ST` | São Tomé & Príncipe | `Africa/Sao_Tome` | `pt-PT` | +| `SV` | El Salvador | `America/El_Salvador` | `es-MX` | +| `SX` | Sint Maarten | `America/Lower_Princes` | `en-US`, `nl-NL` | +| `SY` | Syria | `Asia/Damascus` | `ar-SA` | +| `SZ` | Eswatini | `Africa/Mbabane` | `en-GB` | +| `TC` | Turks & Caicos Islands | `America/Grand_Turk` | `en-US` | +| `TD` | Chad | `Africa/Ndjamena` | `ar-SA`, `fr-FR` | +| `TF` | French Southern Territories | `Indian/Kerguelen` | `fr-FR` | +| `TG` | Togo | `Africa/Lome` | `fr-FR` | +| `TH` | Thailand | `Asia/Bangkok` | `th-TH` | +| `TJ` | Tajikistan | `Asia/Dushanbe` | `ru-RU` | +| `TK` | Tokelau | `Pacific/Fakaofo` | `en-GB` | +| `TL` | Timor-Leste | `Asia/Dili` | `pt-PT` | +| `TM` | Turkmenistan | `Asia/Ashgabat` | `ru-RU` | +| `TN` | Tunisia | `Africa/Tunis` | `ar-SA`, `fr-FR` | +| `TO` | Tonga | `Pacific/Tongatapu` | `en-GB` | +| `TR` | Türkiye | `Europe/Istanbul` | `tr-TR` | +| `TT` | Trinidad & Tobago | `America/Port_of_Spain` | `en-US` | +| `TV` | Tuvalu | `Pacific/Funafuti` | `en-GB` | +| `TW` | Taiwan | `Asia/Taipei` | `zh-TW` | +| `TZ` | Tanzania | `Africa/Dar_es_Salaam` | `en-GB` | +| `UA` | Ukraine | `Europe/Simferopol`, `Europe/Kyiv` | `uk-UA`, `ru-RU` | +| `UG` | Uganda | `Africa/Kampala` | `en-GB` | +| `UM` | U.S. Outlying Islands | `Pacific/Midway`, `Pacific/Wake` | `en-US` | +| `US` | United States | `America/New_York`, `America/Detroit`, `America/Kentucky/Louisville`, `America/Kentucky/Monticello`, `America/Indiana/Indianapolis`, `America/Indiana/Vincennes`, `America/Indiana/Winamac`, `America/Indiana/Marengo`, `America/Indiana/Petersburg`, `America/Indiana/Vevay`, `America/Chicago`, `America/Indiana/Tell_City`, `America/Indiana/Knox`, `America/Menominee`, `America/North_Dakota/Center`, `America/North_Dakota/New_Salem`, `America/North_Dakota/Beulah`, `America/Denver`, `America/Boise`, `America/Phoenix`, `America/Los_Angeles`, `America/Anchorage`, `America/Juneau`, `America/Sitka`, `America/Metlakatla`, `America/Yakutat`, `America/Nome`, `America/Adak`, `Pacific/Honolulu` | `en-US` | +| `UY` | Uruguay | `America/Montevideo` | `es-MX` | +| `UZ` | Uzbekistan | `Asia/Samarkand`, `Asia/Tashkent` | `ru-RU` | +| `VA` | Vatican City | `Europe/Vatican` | `it-IT` | +| `VC` | St. Vincent & Grenadines | `America/St_Vincent` | `en-US` | +| `VE` | Venezuela | `America/Caracas` | `es-MX` | +| `VG` | British Virgin Islands | `America/Tortola` | `en-US` | +| `VI` | U.S. Virgin Islands | `America/St_Thomas` | `en-US` | +| `VN` | Vietnam | `Asia/Ho_Chi_Minh` | `vi-VN` | +| `VU` | Vanuatu | `Pacific/Efate` | `en-GB`, `fr-FR` | +| `WF` | Wallis & Futuna | `Pacific/Wallis` | `fr-FR` | +| `WS` | Samoa | `Pacific/Apia` | `en-GB` | +| `YE` | Yemen | `Asia/Aden` | `ar-SA` | +| `YT` | Mayotte | `Indian/Mayotte` | `fr-FR` | +| `ZA` | South Africa | `Africa/Johannesburg` | `en-GB` | +| `ZM` | Zambia | `Africa/Lusaka` | `en-GB` | +| `ZW` | Zimbabwe | `Africa/Harare` | `en-GB` | diff --git a/docs/options/location.mdx b/docs/options/location.mdx index e24e456..d4ccfcd 100644 --- a/docs/options/location.mdx +++ b/docs/options/location.mdx @@ -21,13 +21,13 @@ session = create_session(country="DE") ## Overriding timezone and language -Set `timezone` (IANA name) or `language` (BCP-47) when the default choice for the country is not what you want. Both must be valid for the chosen country, or the request is rejected with `400`. +Set `timezone` (IANA name) or `language` (BCP-47) when the default choice for the country is not what you want. Both must come from the chosen country's lists on [Countries](/docs/options/countries), or the request is rejected with `400` (`Unsupported timezone "Europe/Berlin" for country US.`). An unknown `country` is rejected the same way (`Unsupported country "XX".`). When you leave `timezone` out, Driver picks the country's dominant zone and, where it runs a managed network there, routes the session's traffic through that region so the network and the clock agree. ```json { "country": "CA", "timezone": "America/Vancouver", "language": "fr-CA" } ``` -US sessions accept only `en-US`, and omit both language fields so Chrome uses its built-in default. For other countries, Driver picks the likely primary language when you leave `language` out. Chrome reports the nearest variant it ships: a session created with `fr-CA` reported `navigator.language` as `fr-FR` with `fr` and `en-US` as fallbacks, and the requested timezone exactly. +US sessions always use `en-US` (any other `language` is rejected). For other countries, Driver picks the country's primary language when you leave `language` out. Chrome reports the nearest variant it ships: a session created with `fr-CA` reported `navigator.language` as `fr-FR` with `fr` and `en-US` as fallbacks. Hosted sessions also provision matching text-to-speech voices where available, and disable Web Speech when a coherent voice is not, so that surface never contradicts the rest. @@ -37,9 +37,10 @@ If you supply a `proxyUrl` without `timezone` or `language`, Driver geolocates t ## Availability -Placement is by available machines. If nothing is available in the requested country you get a `503`; back off and retry, or pick another country. +Placement is by available machines. If nothing is available in the requested country you get a `503` (with `Retry-After: 2`): back off with a cap, then tell the user or pick another country. ## Related +- [Countries](/docs/options/countries): every supported country with its timezones and languages - [Browser types](/docs/options/browser-types) - [Best practices](/docs/start/best-practices): do not override timezone or locale from your script diff --git a/docs/options/profiles.mdx b/docs/options/profiles.mdx index 0f83a7d..0eabd48 100644 --- a/docs/options/profiles.mdx +++ b/docs/options/profiles.mdx @@ -27,7 +27,7 @@ next_session = create_session(profile={"name": "shop-account", "persist": True}) | Field | Type | Meaning | |---|---|---| | `name` | string | The profile to load. Created on first use. | -| `persist` | boolean | Save the profile when the session ends. `false` loads it read-only: changes made during the session are discarded. | +| `persist` | boolean | Save the profile when the session ends. Defaults to `false`, which loads it read-only: changes made during the session are discarded. | Profiles are supported on every browser type. @@ -61,6 +61,7 @@ Deleting is idempotent: an unknown name also answers `{ "success": true }`. The ## Practical notes -- One profile, one session at a time. Two concurrent sessions on the same persisted profile will overwrite each other's state when they stop. +- One profile, one session at a time. Two concurrent sessions on the same persisted profile will overwrite each other's state when they stop; nothing refuses the second session. +- The profile is written as the session ends. Before starting the next session on the same name, wait until `GET /v1/browser/session` reports the previous one as `completed`. - Keep the same `country` for a profile across runs. A profile that signed in from Germany and returns from Brazil looks like a stolen session to the site. - Profiles hold everything a browser profile holds: cookies, local storage, IndexedDB, service workers. Delete a profile when the account it belongs to is no longer needed. diff --git a/docs/options/window-size.mdx b/docs/options/window-size.mdx index 29daad1..9ff1b09 100644 --- a/docs/options/window-size.mdx +++ b/docs/options/window-size.mdx @@ -36,4 +36,4 @@ Pick a size that fits the screen the session's machine would plausibly have. A ` ## Resizing after connecting -Prefer setting the size on create. Changing the viewport from your framework (`page.setViewportSize`) resizes only the content area and leaves `outerWidth` where it was, which is one of the mismatches sites look for. +Prefer setting the size on create. Changing the viewport from your framework (`page.setViewportSize` in Playwright, `page.setViewport` in Puppeteer) resizes only the content area and leaves `outerWidth` where it was, which is one of the mismatches sites look for. Puppeteer applies an 800x600 viewport on connect unless you pass `defaultViewport: null`, so always pass it. diff --git a/docs/sessions/connect.mdx b/docs/sessions/connect.mdx index fc25f39..670ce0d 100644 --- a/docs/sessions/connect.mdx +++ b/docs/sessions/connect.mdx @@ -6,7 +6,7 @@ description: "Attach your automation framework to a running Driver session throu `cdpUrl` is a WebSocket endpoint to the session's Chrome. Any client that speaks the Chrome DevTools Protocol can attach to it. You attach to a running browser; you never launch one. -Use [Patchright](/docs/frameworks/patchright) rather than stock Playwright or Puppeteer. Same API, minus the CDP calls that anti-bot systems fingerprint. Sessions driven by stock frameworks get blocked on sites that look for automation. +Use [Patchright](/docs/frameworks/patchright) rather than stock Playwright or Puppeteer. It is Playwright's API minus the CDP calls that anti-bot systems fingerprint, so Playwright code moves over by changing one import; Puppeteer code needs porting. Sessions driven by stock frameworks get blocked on sites that look for automation. ## Patchright (recommended) @@ -66,6 +66,8 @@ Closing your client (`browser.close()` in Patchright and Playwright, `browser.di If your process crashes, fetch the session with [`GET /v1/browser/session`](/docs/sessions/lifecycle#get-a-session): while `status` is `active` the `cdpUrl` is still valid and you can attach again. Profiles with `persist: true` also carry state into a brand-new session if the old one has ended. +Several clients can be attached at once (your script and the [live view](/docs/sessions/live-view), for instance). Two scripts driving the same tab will fight each other, so keep it to one automation client per session. `cdpUrl` is a credential: anyone who has it can drive the browser while the session is active. + ## Timeouts and waits Driver browsers are real machines on real networks, so page loads take real-world time. Rely on your framework's auto-waiting (`page.click`, `locator.waitFor`) rather than fixed sleeps, and raise the navigation timeout for slow sites instead of retrying in a loop. diff --git a/docs/sessions/create.mdx b/docs/sessions/create.mdx index 5f88751..119e9fd 100644 --- a/docs/sessions/create.mdx +++ b/docs/sessions/create.mdx @@ -19,7 +19,7 @@ curl -X POST https://api.driver.dev/v1/browser/session \ }' ``` ```typescript TypeScript -const session = await fetch("https://api.driver.dev/v1/browser/session", { +const res = await fetch("https://api.driver.dev/v1/browser/session", { method: "POST", headers: { Authorization: `Bearer ${process.env.DRIVER_API_KEY}`, @@ -32,10 +32,12 @@ const session = await fetch("https://api.driver.dev/v1/browser/session", { profile: { name: "my-profile", persist: true }, note: "price check, client X", }), -}).then((r) => r.json()); +}); +if (!res.ok) throw new Error(`Driver ${res.status}: ${(await res.json()).error}`); +const session = await res.json(); ``` ```python Python -session = requests.post( +r = requests.post( "https://api.driver.dev/v1/browser/session", headers={"Authorization": f"Bearer {os.environ['DRIVER_API_KEY']}"}, json={ @@ -45,16 +47,9 @@ session = requests.post( "profile": {"name": "my-profile", "persist": True}, "note": "price check, client X", }, -).json() -``` -```typescript TypeScript SDK -import BrowsercashSDK from "@browsercash/sdk"; - -const client = new BrowsercashSDK({ - apiKey: process.env.DRIVER_API_KEY!, - baseURL: "https://api.driver.dev", -}); -const session = await client.browser.session.create({ country: "US", duration: 600 }); +) +r.raise_for_status() +session = r.json() ``` @@ -62,7 +57,7 @@ const session = await client.browser.session.create({ country: "US", duration: 6 ```json { - "sessionId": "0b1f5c1e6d3a4f7c9e2b8d4a6c1e3f5a", + "sessionId": "k3f9…128 lowercase letters and digits…q7z2", "status": "active", "servedBy": "adapt-ensure-swim", "createdAt": "2026-09-17T14:02:11.000Z", @@ -72,22 +67,22 @@ const session = await client.browser.session.create({ country: "US", duration: 6 } ``` -The call waits until Chrome is reachable, so `cdpUrl` is normally set in the response and `status` is `active`; in our runs that took two to three seconds. The browser opens on `about:blank` (or the `url` you passed) with one context and one tab. If you ever see `starting`, poll [`GET /v1/browser/session`](/docs/sessions/lifecycle#get-a-session) until `cdpUrl` appears. +The call waits until Chrome is reachable, so `cdpUrl` is normally set in the response and `status` is `active`; in our runs that took two to three seconds. It gives up after 20 seconds (up to three minutes on some machines) and answers `504`, so allow your HTTP client at least that long. The browser opens on `about:blank` (or the `url` you passed) with one context and one tab. If you ever see `starting`, poll [`GET /v1/browser/session`](/docs/sessions/lifecycle#get-a-session) until `cdpUrl` appears. Session ids are 128 lowercase letters and digits; treat `cdpUrl` as a secret, since anyone holding it can drive the browser. ## Options | Field | Type | Default | What it does | |---|---|---|---| | `type` | string | `hosted` | Browser class: `hosted`, `hosted_stealth` or `hosted_privacy`. See [Browser types](/docs/options/browser-types). | -| `country` | string | `US` | Two-letter country for placement, network and locale. See [Location](/docs/options/location). | -| `timezone` | string | chosen for the country | IANA timezone, must belong to the country. | -| `language` | string | chosen for the country | BCP-47 browser language, must belong to the country. | -| `duration` | integer | `3600` | Seconds before the session stops on its own, 60 to 3600. | +| `country` | string | `US` | Two-letter code, one of the 249 on [Countries](/docs/options/countries). Placement, network and locale follow it. See [Location](/docs/options/location). | +| `timezone` | string | chosen for the country | IANA name from that country's list on [Countries](/docs/options/countries). | +| `language` | string | chosen for the country | BCP-47 tag from that country's list on [Countries](/docs/options/countries). US sessions are always `en-US`. | +| `duration` | integer | `3600` | Seconds before the session stops on its own, 60 to 3600 inclusive. | | `url` | string | | A URL to open right after launch. | | `windowSize` | string | node default | Outer window size as `WIDTHxHEIGHT`. See [Window size](/docs/options/window-size). | | `displaySize` | string | | Virtual display size as `WIDTHxHEIGHT`. | | `proxyUrl` | string | Driver's network | Your own `socks5://`, `socks5h://` or `tcperfect://` URL. See [Proxies](/docs/options/proxies). | -| `profile` | object | | `{ "name": "…", "persist": true }` to load and save browser state. See [Profiles](/docs/options/profiles). | +| `profile` | object | | `{ "name": "…", "persist": true }` to load and save browser state; `persist` defaults to `false` (load only). See [Profiles](/docs/options/profiles). | | `captchaSolver` | boolean | `false` | Load Driver's CAPTCHA solver into the session. | | `adblock` | boolean | `false` | Block ads in the session. | | `extensionIds` | string[] | | Uploaded extension ids to load. Requires extension access. See [Extensions](/docs/options/extensions). | @@ -95,7 +90,7 @@ The call waits until Chrome is reachable, so `cdpUrl` is normally set in the res | `browserCheck` | boolean | `false` | Run a browser validation check during creation. | | `note` | string | | Up to 256 characters, shown in the dashboard. | -Options that contradict each other are rejected with `400` and a message naming the field. The common ones: a `timezone` or `language` not listed for the `country` (`Unsupported timezone "Europe/Berlin" for country US.`), a `country` that does not match a supplied proxy's exit, a proxy that cannot be reached, and out-of-range `duration`, `windowSize` or `note`. +Unknown fields are rejected with `400`. Options that contradict each other are rejected with `400` and a message naming the field. The common ones: a `timezone` or `language` not listed for the `country` (`Unsupported timezone "Europe/Berlin" for country US.`), a `country` that does not match a supplied proxy's exit, a proxy that cannot be reached, and out-of-range `duration`, `windowSize` or `note`. ## Errors @@ -106,8 +101,11 @@ Options that contradict each other are rejected with `400` and a message naming | `402` | The workspace is out of credits. | | `403` | Session or account limit reached (concurrency, or a feature your plan does not include). | | `429` | Too many requests. | -| `503` | No browser capacity available right now. | -| `504` | A machine was selected but Chrome was not reachable before the startup deadline. | +| `500` | Something failed on Driver's side. | +| `503` | No browser capacity right now. The response carries `Retry-After: 2` and `"code": "browser_capacity_unavailable"`. | +| `504` | A machine was selected but Chrome was not reachable before the startup deadline (20 s, up to 3 min on some machines). | + +Retry guidance per status is on [Errors](/docs/sessions/errors). ## Next diff --git a/docs/sessions/errors.mdx b/docs/sessions/errors.mdx index 6fd90dc..ccc385a 100644 --- a/docs/sessions/errors.mdx +++ b/docs/sessions/errors.mdx @@ -3,30 +3,36 @@ title: "Errors" description: "Every status the API returns, what it means, and whether retrying helps." --- -Errors are JSON with an `error` message. Some carry a machine-readable `code`. +Errors are JSON with an `error` message. One carries a machine-readable `code` as well: the create call's `503`. ```json { "error": "Insufficient balance" } ``` +```json +{ "error": "No browser capacity is available right now", "code": "browser_capacity_unavailable" } +``` + +The bodies you will see most: `401` `Unauthorized: Missing or invalid Authorization header` or `Unauthorized: Invalid token`; `402` `Insufficient balance`; `403` `Session limit reached` (concurrency) or `Extensions API is not enabled for this account`; `400` names the field, for example `Unsupported timezone "Europe/Berlin" for country US.` or `windowSize must be WIDTHxHEIGHT with width 320–16384 and height 200–16384`. Unknown fields in a body are rejected with `400` too. + ## Status codes | Status | Meaning | Retry? | |---|---|---| -| `400` | Invalid or incompatible options. The message names the field. | No. Fix the request. | +| `400` | Invalid or incompatible options, an unknown field, or an update to a session that has ended. The message names the problem. | No. Fix the request. | | `401` | Missing or invalid API key. | No. Check `Authorization: Bearer …`. | | `402` | The workspace is out of credits. | No. Add credits in the dashboard. | -| `403` | Session or account limit reached: the workspace's concurrent-browser limit (`plan.concurrent_browsers` on `GET /v1/account/billing`), or a feature (extensions) the account is not enabled for. | Only after a session ends. | -| `404` | Session, profile or extension not found (unknown id, or one belonging to another workspace). | No. | -| `429` | Too many requests. | Yes, with backoff. | +| `403` | Session or account limit reached: the workspace's concurrent-browser limit (`plan.concurrent_browsers` on `GET /v1/account/billing`; sessions that are `starting` count, and a stop frees the slot as soon as it returns), or a feature (extensions) the account is not enabled for. | Only after a session ends. Never in a loop. | +| `404` | Session, extension or account not found (unknown id, or one belonging to another workspace). Deleting an unknown profile is not a 404: it answers `{ "success": true }`. | No. | +| `429` | Too many requests. The API itself does not rate-limit today; a `429` comes from the edge in front of it. | Yes, with backoff. | | `500` | Something failed on Driver's side. | Once. | -| `503` | No browser capacity available right now, or the session service is unavailable. | Yes, with backoff and jitter, or tell the user. | -| `504` | A machine was selected but Chrome did not become reachable before the startup deadline. | Once. | +| `503` | No browser capacity right now. The response carries `Retry-After: 2` and `"code": "browser_capacity_unavailable"`. Or the session service is unavailable. | Yes: back off with jitter, cap the attempts, then tell the user. | +| `504` | A machine was selected but Chrome did not become reachable before the startup deadline: 20 seconds, up to three minutes on some machines. | Once. | ## Guidance for agents and automations - Do not retry `402` or `403` in a loop. Surface the message; a person has to act. -- Treat `503` and `504` as transient: back off (a few seconds, growing), cap the attempts, then report. +- Treat `503` as transient: back off (start at the `Retry-After` of 2 seconds, grow with jitter), cap the attempts, then report. Retry `504` and `500` once. - After a `504`, the session is not running; no stop call is needed. - `DELETE` on a session that already ended still returns `{ "success": true }`; a `404` means the id was never a session of this workspace. diff --git a/docs/sessions/lifecycle.mdx b/docs/sessions/lifecycle.mdx index c2d9deb..6443947 100644 --- a/docs/sessions/lifecycle.mdx +++ b/docs/sessions/lifecycle.mdx @@ -10,11 +10,11 @@ A session moves through `starting` → `active` → `completed`, or `error` if t | `starting` | The machine is launching Chrome. `cdpUrl` is `null`. | | `active` | Chrome is reachable at `cdpUrl`. | | `completed` | The session was stopped or reached its duration. `stoppedAt` is set. | -| `error` | The session failed to start or ended abnormally. | +| `error` | The session failed to start, ended abnormally, or the machine serving it went away. `stoppedAt` is set. | ## Get a session -Returns the same shape as create, plus `bandwidthBytes`: `null` while the session runs, the metered total once it has ended. +Returns the same shape as create, plus `bandwidthBytes`: `0` while the session runs, the metered total once it has ended. ```bash cURL @@ -34,7 +34,7 @@ session = requests.get( ## List sessions -Paginated, newest first, with an optional `status` filter. +Paginated, newest first, with an optional `status` filter. `page` defaults to 1 and `pageSize` to 20 (at most 100; larger values are clamped, not rejected). A `status` outside `starting`, `active`, `completed`, `error` is a `400`. ```bash cURL @@ -54,7 +54,7 @@ data = requests.get( ``` -Each item carries `sessionId`, `status`, `servedBy`, `type`, `country`, `createdAt`, `stoppedAt`, `bandwidthBytes` and `note`. +The response carries `sessions`, `total`, `totalPages`, `page` and `pageSize`. Each item has `sessionId`, `status`, `servedBy`, `type`, `country`, `createdAt`, `stoppedAt`, `bandwidthBytes` and `note`; `type` and `country` are the only place the API reports them, since the single-session calls do not. ## Label a session @@ -84,7 +84,7 @@ requests.patch( ``` -Notes are at most 256 characters. Send an empty string to clear one. A session that has ended cannot be updated (`400`, `Cannot update a terminal session`). +Returns `{ "success": true }`. Notes are at most 256 characters (longer is a `400`). Send an empty string to clear one. A session that has ended cannot be updated (`400`, `Cannot update a completed or errored session`). ## Stop a session @@ -112,15 +112,18 @@ Returns `{ "success": true }`. Stopping is idempotent: a session that already en ## Duration -`duration` on create is the session's lifetime in seconds, from 60 to 3600, default 3600. There is no extension call: choose a duration that covers the job, and start a new session (with a persisted [profile](/docs/options/profiles) if you need the state) when it runs out. +`duration` on create is the session's lifetime in seconds, from 60 to 3600 inclusive, default 3600. There is no extension call: choose a duration that covers the job, and start a new session (with a persisted [profile](/docs/options/profiles) if you need the state) when it runs out. ## Waiting for `active` -Create normally returns with `cdpUrl` set. If you see `starting`, poll: +Create normally returns with `cdpUrl` set. If you see `starting`, poll (`getSession` / `get_session` are the GET call from the top of this page; the [reference client](/docs/start/for-agents#reference-implementation) has both): ```typescript TypeScript -async function waitForActive(id: string, timeoutMs = 30_000) { +const getSession = (id: string) => + fetch(`https://api.driver.dev/v1/browser/session?sessionId=${id}`, { headers }).then((r) => r.json()); + +async function waitForActive(id: string, timeoutMs = 60_000) { const deadline = Date.now() + timeoutMs; while (Date.now() < deadline) { const s = await getSession(id); @@ -134,7 +137,16 @@ async function waitForActive(id: string, timeoutMs = 30_000) { } ``` ```python Python -def wait_for_active(session_id, timeout=30): +import time + + +def get_session(session_id): + return requests.get( + "https://api.driver.dev/v1/browser/session", headers=headers, params={"sessionId": session_id} + ).json() + + +def wait_for_active(session_id, timeout=60): deadline = time.time() + timeout while time.time() < deadline: s = get_session(session_id) diff --git a/docs/sessions/live-view.mdx b/docs/sessions/live-view.mdx index 63971aa..e7a8733 100644 --- a/docs/sessions/live-view.mdx +++ b/docs/sessions/live-view.mdx @@ -39,4 +39,4 @@ Anyone with the URL can drive the browser, so treat it like the API key while th After a session ends, its page in the dashboard keeps the record: status and duration, bandwidth and cost, the node that served it with its country, the network it used, how it was started (type, profile, note, which API key), and timestamps. Keep the `sessionId` in your own logs so a run can be found later. -The same facts are available from [`GET /v1/browser/session`](/docs/sessions/lifecycle#get-a-session). +The API keeps the essentials: [`GET /v1/browser/session`](/docs/sessions/lifecycle#get-a-session) returns `status`, `stoppedAt` and `bandwidthBytes`, and the [list](/docs/sessions/lifecycle#list-sessions) adds `type` and `country`. Cost, network and node details are in the dashboard only. diff --git a/docs/start/best-practices.mdx b/docs/start/best-practices.mdx index 52dd5b6..4f880c7 100644 --- a/docs/start/best-practices.mdx +++ b/docs/start/best-practices.mdx @@ -46,12 +46,13 @@ Not in a Driver integration: Broad event listeners, request routing and injected scripts make the browser behave unusually and are detectable. Use them only when the task genuinely requires them. -Avoid by default: +Avoid by default (Playwright and Patchright names, with the Puppeteer equivalents): - `page.on(...)`, `context.on(...)`, `browser.on(...)` -- `page.route(...)`, `context.route(...)` -- `page.exposeBinding(...)`, `page.exposeFunction(...)` -- `page.addInitScript(...)`, `context.addInitScript(...)` +- `page.route(...)`, `context.route(...)` (Puppeteer: `page.setRequestInterception(true)`) +- `page.exposeBinding(...)`, `page.exposeFunction(...)` (Puppeteer: `page.exposeFunction`) +- `page.addInitScript(...)`, `context.addInitScript(...)` (Puppeteer: `page.evaluateOnNewDocument`) +- Puppeteer's `page.setUserAgent`, `page.emulateTimezone`, `page.emulate` and `page.setViewport`: these are the fingerprint patches the next section warns about Prefer direct actions and reads: `page.goto`, `page.click`, `page.fill`, `page.locator`, `page.title`, `page.textContent`. @@ -98,9 +99,10 @@ Signing in on every run is slow and looks suspicious. Sign in once under a named | Status | Meaning | What to do | |---|---|---| | `402` | Workspace is out of credits | Tell the user. Retrying will not help. | -| `403` | Plan limit: concurrency or feature not enabled | Wait for a session to finish, or upgrade. | +| `403` | Plan limit: concurrency or feature not enabled | Wait for a session to finish, or upgrade. Do not loop. | | `429` | Rate limited | Back off and retry. | -| `503` | No browser capacity right now | Back off with jitter, or tell the user. | +| `500` | Something failed on Driver's side | Retry once. | +| `503` | No browser capacity right now (`Retry-After: 2`) | Back off with jitter, cap the attempts, then tell the user. | | `504` | Browser did not become reachable in time | Retry once. | The full list is on [Errors](/docs/sessions/errors). diff --git a/docs/start/for-agents.mdx b/docs/start/for-agents.mdx index 9698f0a..1b1e252 100644 --- a/docs/start/for-agents.mdx +++ b/docs/start/for-agents.mdx @@ -3,51 +3,57 @@ title: "For agents" description: "A briefing for coding agents integrating Driver: the rules that keep sessions unblocked, the three endpoints that matter, and the mistakes to avoid." --- -This page is written for an AI coding agent (Claude Code, Cursor, Codex, or similar) that has been asked to integrate Driver into a project. Humans are welcome too. Everything here is also in `llms.txt` at the root of these docs. +This page is written for an AI coding agent (Claude Code, Cursor, Codex, or similar) that has been asked to integrate Driver into a project. Humans are welcome too. The published docs also serve `/llms.txt` (an index) and `/llms-full.txt` (every page). ## The rules 1. **Use Patchright, not stock Playwright or Puppeteer.** Install `patchright` (npm or pip) and import `chromium` from it. It has the same API as Playwright, so existing Playwright code works unchanged. Stock Playwright issues CDP commands (`Runtime.enable`, console and binding hooks) that anti-bot systems detect, and sessions get blocked. Patchright removes them. See [Why Patchright](/docs/frameworks/patchright#why-patchright). 2. **Never launch a browser.** Driver provides the browser. Connect to the `cdpUrl` from the create call with `chromium.connectOverCDP(cdpUrl)`. Do not call `chromium.launch()`, `launchPersistentContext()`, `puppeteer.launch()`, or run `playwright install`. -3. **Reuse the browser's context and tab.** After connecting, use `browser.contexts()[0]` and `context.pages()[0]`. Only create new ones if none exist. Extra contexts and stripped-down pages look like automation. +3. **Reuse the browser's context and tab.** After connecting, use `browser.contexts()[0]` and `context.pages()[0]` (in Python they are properties: `browser.contexts[0]`, `context.pages[0]`). Only create new ones if none exist. Extra contexts and stripped-down pages look like automation. 4. **Do not install broad CDP hooks.** Avoid `page.route`, `context.route`, `page.on('request')`, `page.exposeFunction`, `page.addInitScript` and `context.addInitScript` unless the task cannot be done otherwise, and then scope them as narrowly as possible. They change how the browser behaves and are detectable. 5. **Do not patch fingerprints.** The browser is real Chrome on real hardware. Overriding `navigator`, the user agent, WebGL, canvas, timezone or locale makes it look fake. Set `country`, `timezone` and `language` on the create call instead. -6. **Always stop the session.** Wrap the work in `try / finally` and call `DELETE /v1/browser/session?sessionId=…` in `finally`. Sessions bill by bandwidth and end on their own at `duration` (one hour by default), but a session left running still costs. +6. **Always stop the session.** Wrap the work in `try / finally` and call `DELETE /v1/browser/session?sessionId=…` in `finally`. Sessions are metered by bandwidth and end on their own at `duration` (one hour by default); a session left running holds one of the workspace's concurrency slots and keeps its `cdpUrl` live until then. Closing your client (`browser.close()`) does not stop the session. 7. **Keep the `sessionId`.** Log it with the run. Every session has a record in the dashboard under Telemetry → Sessions with node, network, timing and cost. -8. **Handle 402 and 503 by reporting, not retrying.** 402 means the workspace is out of credits; 503 means no browser capacity right now. Surface the message to the user. Retry only 429 (rate limited) and 504 (browser did not start in time), with backoff. +8. **Report what a person must fix; retry only what time fixes.** `402` (out of credits) and `403` (concurrency or plan limit): report, never loop. `503` (no capacity; the response carries `Retry-After: 2` and `"code": "browser_capacity_unavailable"`): back off with jitter, cap the attempts, then report. `504` (Chrome did not start in time) and `500`: retry once. `429`: back off. The full table is on [Errors](/docs/sessions/errors). 9. **Stay under the workspace's concurrency limit.** `GET /v1/account/billing` reports it as `plan.concurrent_browsers`; sessions beyond it are refused with 403. ## How to write the automation - **Minimal logic wins.** The shortest script that reaches the outcome is the most reliable and the least detectable. Go to the page, do the action, read the result. Do not add retries, waits, listeners, request routing or "stealth" helpers unless a specific failure on a specific site demands them, and then add the smallest thing that fixes it. - **Fast and accurate, in that order of effort.** Before writing browser steps, ask whether the outcome is reachable faster: a direct URL instead of clicking through navigation, one session doing several pages instead of one session per page, a persisted [profile](/docs/options/profiles) instead of logging in every run. Then keep the browser steps short: no fixed sleeps, rely on the framework's auto-waiting, stop the session the moment the result is in hand. -- **Test the automation for real, every time.** Run it against the live target through Driver and check the result, not just that the script exits. Confirm the data is right, the action happened, and the session was stopped (look it up under Telemetry → Sessions). A run that "worked" but read the wrong element is worse than one that failed loudly. +- **Test the automation for real, every time.** Run it against the live target through Driver and check the result, not just that the script exits. Confirm the data is right, the action happened, and the session was stopped: `GET /v1/browser/session?sessionId=…` shows `status: "completed"`, a `stoppedAt` and the metered `bandwidthBytes` (the same record is under Telemetry → Sessions in the dashboard). A run that "worked" but read the wrong element is worse than one that failed loudly. +- **Unattended jobs clean up after earlier runs.** A crash before `finally` leaves a session running until its `duration`. At start-up, `GET /v1/browser/sessions?status=active`, stop the ones your job created (match on `note`), then start the new one. - **Do not pollute the session.** Every extra CDP feature you switch on, every injected script, every overridden property is a signal. If you did not need it to reach the outcome, take it out. ## Handoff prompt -Give this to a coding agent along with the target and the outcome you want. It is the whole briefing. +Give this to a coding agent along with the target and the outcome you want. It is enough to start; the rest of this page fills in the details an agent needs once it is writing code. ```text Integrate Driver (driver.dev), hosted real-Chrome browser infrastructure, into this project. - Docs: https://docs.driver.dev — agents start at https://docs.driver.dev/docs/start/for-agents - Endpoints: https://api.driver.dev/scalar -- Auth: Authorization: Bearer $DRIVER_API_KEY (read from the environment; never commit it) + Endpoints: https://api.driver.dev/scalar (OpenAPI at https://api.driver.dev/doc) +- Auth: Authorization: Bearer $DRIVER_API_KEY (read from the environment; never commit it). + JSON bodies with Content-Type: application/json. Ids go in the query string, not the path. - Most tasks need no options: POST https://api.driver.dev/v1/browser/session with {} gives a - hosted US Chrome. Use "country" for geo-specific sites, "profile" to keep logins, "duration" - to cap cost. -- Drive the browser only with Patchright (https://github.com/Kaliiiiiiiiii-Vinyzu/patchright): - chromium.connectOverCDP(cdpUrl), then reuse browser.contexts()[0] and context.pages()[0]. - Never launch a browser. Never use stock Playwright or Puppeteer. + hosted US Chrome and returns { sessionId, status, cdpUrl }. Use "country" for geo-specific + sites, "profile" {name, persist:true} to keep logins, "duration" (60–3600 s, default 3600) + to cap how long a forgotten session lives. +- Drive the browser only with Patchright (npm install patchright / pip install patchright; no + browser download): chromium.connectOverCDP(cdpUrl), then reuse browser.contexts()[0] and + context.pages()[0]. Never launch a browser. Never use stock Playwright or Puppeteer. - No CDP or automation tactics that leak or get sites to block: no page.route, no init scripts, no exposed functions, no fingerprint patches. Minimal automation logic is always best: https://docs.driver.dev/docs/start/best-practices - Check whether the outcome can be reached faster: direct URLs, one session for many pages, a persisted profile instead of logging in. Avoid slow browser logic and fixed sleeps. - Always stop the session in a finally: DELETE /v1/browser/session?sessionId=. - Keep the sessionId in logs. -- Test the automation against the real target and verify the result before calling it done. + browser.close() only disconnects; the session runs until DELETE or its duration. Keep the + sessionId in logs. +- 402 (no credits) and 403 (limit): report. 503: back off, cap, report. 504 and 500: retry once. +- Test the automation against the real target and verify the result before calling it done: + GET /v1/browser/session?sessionId= must show status "completed". ``` ## The three calls @@ -56,11 +62,11 @@ Base URL `https://api.driver.dev`. Every call carries `Authorization: Bearer $DR | Step | Call | Notes | |---|---|---| -| Create | `POST /v1/browser/session` with a JSON body | All fields optional. Returns `{ sessionId, status, cdpUrl, servedBy, createdAt, stoppedAt, note }`. `cdpUrl` is set when the browser is reachable; the call waits for that. | +| Create | `POST /v1/browser/session` with a JSON body | All fields optional. Returns `{ sessionId, status, cdpUrl, servedBy, createdAt, stoppedAt, note }`. The call waits for Chrome (up to 20 s, on some machines up to 3 min), so `cdpUrl` is normally set and `status` is `active`; if you ever get `starting`, poll `GET` until `cdpUrl` appears. | | Use | `chromium.connectOverCDP(cdpUrl)` | From Patchright. Reuse the first context and page. | | Stop | `DELETE /v1/browser/session?sessionId=` | Returns `{ success: true }`. | -Useful extras: `GET /v1/browser/session?sessionId=` for status and bandwidth, `GET /v1/browser/sessions?status=active` to list, `PATCH /v1/browser/session?sessionId=` with `{ "note": "…" }` to label a run. Full options for the create body are on [Session options](/docs/options/browser-types). +Useful extras: `GET /v1/browser/session?sessionId=` for status and bandwidth, `GET /v1/browser/sessions?status=active` to list, `PATCH /v1/browser/session?sessionId=` with `{ "note": "…" }` to label a run. Full options for the create body are on [Create a session](/docs/sessions/create#options). ## Reference implementation @@ -71,13 +77,22 @@ import { chromium, type Browser, type Page } from "patchright"; const API = "https://api.driver.dev"; export interface CreateOptions { - country?: string; // "US", "GB", "DE", … - duration?: number; // seconds, 60–3600 + type?: "hosted" | "hosted_stealth" | "hosted_privacy"; + country?: string; // "US", "GB", "DE", … (see Countries) + timezone?: string; // IANA name from the country's list + language?: string; // BCP-47 tag from the country's list + duration?: number; // seconds, 60–3600, default 3600 + url?: string; // opened right after launch profile?: { name: string; persist?: boolean }; proxyUrl?: string; // socks5://user:pass@host:port - windowSize?: string; // "1920x1080" + windowSize?: string; // "1920x1040" + displaySize?: string; // "1920x1080" captchaSolver?: boolean; - note?: string; + adblock?: boolean; + extensionIds?: string[]; + nodeId?: string; + browserCheck?: boolean; + note?: string; // up to 256 characters } export interface Session { @@ -85,6 +100,17 @@ export interface Session { status: "starting" | "active" | "completed" | "error"; cdpUrl: string | null; servedBy: string; + createdAt: string; + stoppedAt: string | null; + note: string | null; + bandwidthBytes?: number; // on GET: 0 while running, the metered total afterwards +} + +/** Every non-2xx answer: `status` is the HTTP status, `code` the machine-readable code when there is one. */ +export class DriverError extends Error { + constructor(public status: number, message: string, public code?: string, public retryAfter?: number) { + super(`Driver ${status}: ${message}`); + } } const headers = () => ({ @@ -92,40 +118,52 @@ const headers = () => ({ "Content-Type": "application/json", }); -export async function createSession(opts: CreateOptions = {}): Promise { - const res = await fetch(`${API}/v1/browser/session`, { - method: "POST", - headers: headers(), - body: JSON.stringify(opts), - }); +async function call(method: string, path: string, body?: unknown): Promise { + const res = await fetch(`${API}${path}`, { method, headers: headers(), body: body ? JSON.stringify(body) : undefined }); if (!res.ok) { - const body = await res.json().catch(() => ({})); - throw new Error(`Driver ${res.status}: ${body.error ?? res.statusText}`); + const err = (await res.json().catch(() => ({}))) as { error?: string; code?: string }; + const retryAfter = Number(res.headers.get("retry-after")) || undefined; + throw new DriverError(res.status, err.error ?? res.statusText, err.code, retryAfter); } - return res.json(); + return res.json() as Promise; +} + +export const createSession = (opts: CreateOptions = {}) => call("POST", "/v1/browser/session", opts); +export const getSession = (id: string) => call("GET", `/v1/browser/session?sessionId=${encodeURIComponent(id)}`); + +/** Idempotent; safe in a finally. */ +export async function stopSession(id: string): Promise { + const r = await call<{ success: boolean }>("DELETE", `/v1/browser/session?sessionId=${encodeURIComponent(id)}`); + if (!r.success) throw new DriverError(500, "stop did not succeed"); } -export async function stopSession(sessionId: string): Promise { - await fetch(`${API}/v1/browser/session?sessionId=${encodeURIComponent(sessionId)}`, { - method: "DELETE", - headers: headers(), - }); +/** Create normally returns an active session; this covers the rare `starting` answer. */ +async function waitForActive(s: Session, timeoutMs = 60_000): Promise { + const deadline = Date.now() + timeoutMs; + while (!(s.status === "active" && s.cdpUrl)) { + if (s.status === "completed" || s.status === "error") throw new DriverError(500, `session ended: ${s.status}`); + if (Date.now() > deadline) throw new DriverError(504, "timed out waiting for the browser"); + await new Promise((r) => setTimeout(r, 1000)); + s = await getSession(s.sessionId); + } + return s; } /** Runs `fn` against a fresh Driver session and always stops it afterwards. */ export async function withBrowser( opts: CreateOptions, - fn: (page: Page, browser: Browser) => Promise, + fn: (page: Page, browser: Browser, session: Session) => Promise, ): Promise { const session = await createSession(opts); try { - const browser = await chromium.connectOverCDP(session.cdpUrl!); + const active = await waitForActive(session); + const browser = await chromium.connectOverCDP(active.cdpUrl!); try { const context = browser.contexts()[0] ?? (await browser.newContext()); const page = context.pages()[0] ?? (await context.newPage()); - return await fn(page, browser); + return await fn(page, browser, active); } finally { - await browser.close(); + await browser.close(); // your connection only } } finally { await stopSession(session.sessionId); @@ -135,28 +173,61 @@ export async function withBrowser( ```python driver.py import os +import time from contextlib import contextmanager + import requests from patchright.sync_api import sync_playwright API = "https://api.driver.dev" +class DriverError(RuntimeError): + """Every non-2xx answer: .status is the HTTP status, .code the machine-readable code when there is one.""" + + def __init__(self, status, message, code=None, retry_after=None): + super().__init__(f"Driver {status}: {message}") + self.status, self.code, self.retry_after = status, code, retry_after + + def _headers(): return {"Authorization": f"Bearer {os.environ['DRIVER_API_KEY']}"} -def create_session(**opts): - """opts: country, duration, profile={"name": …, "persist": True}, proxyUrl, - windowSize, captchaSolver, note.""" - r = requests.post(f"{API}/v1/browser/session", headers=_headers(), json=opts) +def _call(method, path, **kwargs): + r = requests.request(method, f"{API}{path}", headers=_headers(), timeout=200, **kwargs) if not r.ok: - raise RuntimeError(f"Driver {r.status_code}: {r.json().get('error', r.reason)}") + body = r.json() if r.headers.get("content-type", "").startswith("application/json") else {} + raise DriverError(r.status_code, body.get("error", r.reason), body.get("code"), r.headers.get("Retry-After")) return r.json() -def stop_session(session_id: str): - requests.delete(f"{API}/v1/browser/session", headers=_headers(), params={"sessionId": session_id}) +def create_session(**opts): + """opts: type, country, timezone, language, duration, url, profile={"name": …, "persist": True}, + proxyUrl, windowSize, displaySize, captchaSolver, adblock, extensionIds, nodeId, browserCheck, note.""" + return _call("POST", "/v1/browser/session", json=opts) + + +def get_session(session_id): + return _call("GET", "/v1/browser/session", params={"sessionId": session_id}) + + +def stop_session(session_id): + """Idempotent; safe in a finally.""" + _call("DELETE", "/v1/browser/session", params={"sessionId": session_id}) + + +def wait_for_active(session, timeout=60): + """Create normally returns an active session; this covers the rare 'starting' answer.""" + deadline = time.time() + timeout + while not (session["status"] == "active" and session.get("cdpUrl")): + if session["status"] in ("completed", "error"): + raise DriverError(500, f"session ended: {session['status']}") + if time.time() > deadline: + raise DriverError(504, "timed out waiting for the browser") + time.sleep(1) + session = get_session(session["sessionId"]) + return session @contextmanager @@ -164,6 +235,7 @@ def browser_page(**opts): """with browser_page(country="US") as page: ... — always stops the session.""" session = create_session(**opts) try: + session = wait_for_active(session) with sync_playwright() as p: browser = p.chromium.connect_over_cdp(session["cdpUrl"]) context = browser.contexts[0] if browser.contexts else browser.new_context() @@ -171,7 +243,7 @@ def browser_page(**opts): try: yield page finally: - browser.close() + browser.close() # your connection only finally: stop_session(session["sessionId"]) ``` @@ -184,9 +256,10 @@ def browser_page(**opts): - [ ] The first context and page are reused. - [ ] The session is stopped in a `finally`. - [ ] `DRIVER_API_KEY` comes from the environment and is not committed. -- [ ] 402 and 503 are reported to the user, not retried in a loop. +- [ ] 402 and 403 are reported to the user; 503 is backed off with a cap, never looped. +- [ ] The result was verified through `GET /v1/browser/session` (status `completed`), not assumed. ## Machine-readable resources -- `https://docs.driver.dev/llms.txt` lists every page of these docs. +- `https://docs.driver.dev/llms.txt` lists every page of these docs; `https://docs.driver.dev/llms-full.txt` is all of them in one file. Any page is available as Markdown by appending `.md` to its URL. - `https://api.driver.dev/doc` is the live OpenAPI document; `https://api.driver.dev/scalar` renders it. diff --git a/docs/start/index.mdx b/docs/start/index.mdx index 73a0899..bb0d702 100644 --- a/docs/start/index.mdx +++ b/docs/start/index.mdx @@ -58,5 +58,5 @@ Authorization: Bearer $DRIVER_API_KEY ## Next - [Quickstart](/docs/start/quickstart) creates and stops a session end to end. -- [Session options](/docs/options/browser-types) explains every field you can pass on create. +- [Create a session](/docs/sessions/create#options) explains every field you can pass on create. - [Frameworks](/docs/frameworks/patchright) shows the connection code for Patchright, Playwright, Puppeteer, Browser Use, Stagehand and Crawl4AI. diff --git a/docs/start/quickstart.mdx b/docs/start/quickstart.mdx index e558a37..80f0afc 100644 --- a/docs/start/quickstart.mdx +++ b/docs/start/quickstart.mdx @@ -25,6 +25,8 @@ description: "Create a Driver session, connect with Patchright, read a page titl You do not need `playwright install` or any browser download: Driver provides the browser. + + The TypeScript examples use top-level `await` and the global `fetch`: Node 18 or newer, in an ES module (`"type": "module"` in `package.json`, or a `.mts` file). Run a file with `npx tsx file.ts`. The Python examples need Python 3.8 or newer. @@ -39,23 +41,27 @@ description: "Create a Driver session, connect with Patchright, read a page titl }; // 1. Create a session. Every field is optional; this one asks for a US browser for ten minutes. - const session = await fetch(`${API}/v1/browser/session`, { + const res = await fetch(`${API}/v1/browser/session`, { method: "POST", headers, body: JSON.stringify({ country: "US", duration: 600 }), - }).then((r) => r.json()); + }); + if (!res.ok) throw new Error(`Driver ${res.status}: ${(await res.json()).error}`); + const session = await res.json(); try { // 2. Connect over CDP and reuse the browser's own context and tab. const browser = await chromium.connectOverCDP(session.cdpUrl); - const context = browser.contexts()[0] ?? (await browser.newContext()); - const page = context.pages()[0] ?? (await context.newPage()); - - // 3. Do the work. - await page.goto("https://example.com"); - console.log(await page.title()); - - await browser.close(); + try { + const context = browser.contexts()[0] ?? (await browser.newContext()); + const page = context.pages()[0] ?? (await context.newPage()); + + // 3. Do the work. + await page.goto("https://example.com"); + console.log(await page.title()); + } finally { + await browser.close(); // ends your connection; the session is still running + } } finally { // 4. Stop the session. This is what ends billing. await fetch(`${API}/v1/browser/session?sessionId=${session.sessionId}`, { @@ -92,8 +98,10 @@ description: "Create a Driver session, connect with Patchright, read a page titl finally: # 4. Stop the session. This is what ends billing. requests.delete( - f"{API}/v1/browser/session", headers=headers, params={"sessionId": session["sessionId"]} - ) + f"{API}/v1/browser/session", + headers=headers, + params={"sessionId": session["sessionId"]}, + ) ``` ```bash cURL @@ -112,13 +120,13 @@ description: "Create a Driver session, connect with Patchright, read a page titl - Every session has a page in the dashboard under **Telemetry → Sessions** with a live view while it runs and the full record afterwards: node, country, network, bandwidth, cost. See [Live view](/docs/sessions/live-view). + Every session has a page in the dashboard under **Telemetry → Sessions** with a live view while it runs and the full record afterwards: node, country, network, bandwidth, cost. See [Live view](/docs/sessions/live-view). Without a dashboard, `GET /v1/browser/session?sessionId=…` tells you the same essentials: `status` becomes `completed`, `stoppedAt` is set, `bandwidthBytes` is the metered total. ## What just happened -The create call returned as soon as Chrome was reachable over CDP, usually in a few seconds. `cdpUrl` is a WebSocket endpoint to that one browser. Connecting with `connectOverCDP` attaches to the running browser instead of launching one, which is why there is no `chromium.launch()` anywhere and no browser download on your machine. +The create call returned as soon as Chrome was reachable over CDP, usually in a few seconds (it waits up to 20 seconds, on some machines up to three minutes, then answers `504`). `cdpUrl` is a WebSocket endpoint to that one browser. Connecting with `connectOverCDP` attaches to the running browser instead of launching one, which is why there is no `chromium.launch()` anywhere and no browser download on your machine. The browser opened with one context and one tab already. Reusing them keeps the session looking like a person opened Chrome and started browsing; creating extra contexts is one of the things sites notice. @@ -127,5 +135,5 @@ Stopping the session releases the machine and closes the bill for that session. ## Next - [For agents](/docs/start/for-agents) if a coding agent is doing the integration. -- [Session options](/docs/options/browser-types) for browser types, location, proxies, profiles and window size. +- [Create a session](/docs/sessions/create#options) for every field you can pass: browser type, location, proxies, profiles, window size. - [Best practices](/docs/start/best-practices) before you scale up. diff --git a/package.json b/package.json index 23dbc5f..209c3c0 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "scripts": { "dev": "mint dev", "sync": "node scripts/sync-openapi.mjs", - "check": "mint broken-links" + "check": "mint broken-links", + "countries": "node scripts/gen-countries.mjs" }, "devDependencies": { "mint": "latest" diff --git a/scripts/data/iana-timezones-by-country.json b/scripts/data/iana-timezones-by-country.json new file mode 100644 index 0000000..adaf415 --- /dev/null +++ b/scripts/data/iana-timezones-by-country.json @@ -0,0 +1,920 @@ +{ + "AD": [ + "Europe/Andorra" + ], + "AE": [ + "Asia/Dubai" + ], + "AF": [ + "Asia/Kabul" + ], + "AG": [ + "America/Antigua" + ], + "AI": [ + "America/Anguilla" + ], + "AL": [ + "Europe/Tirane" + ], + "AM": [ + "Asia/Yerevan" + ], + "AO": [ + "Africa/Luanda" + ], + "AQ": [ + "Antarctica/McMurdo", + "Antarctica/Casey", + "Antarctica/Davis", + "Antarctica/DumontDUrville", + "Antarctica/Mawson", + "Antarctica/Palmer", + "Antarctica/Rothera", + "Antarctica/Syowa", + "Antarctica/Troll", + "Antarctica/Vostok" + ], + "AR": [ + "America/Argentina/Buenos_Aires", + "America/Argentina/Cordoba", + "America/Argentina/Salta", + "America/Argentina/Jujuy", + "America/Argentina/Tucuman", + "America/Argentina/Catamarca", + "America/Argentina/La_Rioja", + "America/Argentina/San_Juan", + "America/Argentina/Mendoza", + "America/Argentina/San_Luis", + "America/Argentina/Rio_Gallegos", + "America/Argentina/Ushuaia" + ], + "AS": [ + "Pacific/Pago_Pago" + ], + "AT": [ + "Europe/Vienna" + ], + "AU": [ + "Australia/Lord_Howe", + "Antarctica/Macquarie", + "Australia/Hobart", + "Australia/Melbourne", + "Australia/Sydney", + "Australia/Broken_Hill", + "Australia/Brisbane", + "Australia/Lindeman", + "Australia/Adelaide", + "Australia/Darwin", + "Australia/Perth", + "Australia/Eucla" + ], + "AW": [ + "America/Aruba" + ], + "AX": [ + "Europe/Mariehamn" + ], + "AZ": [ + "Asia/Baku" + ], + "BA": [ + "Europe/Sarajevo" + ], + "BB": [ + "America/Barbados" + ], + "BD": [ + "Asia/Dhaka" + ], + "BE": [ + "Europe/Brussels" + ], + "BF": [ + "Africa/Ouagadougou" + ], + "BG": [ + "Europe/Sofia" + ], + "BH": [ + "Asia/Bahrain" + ], + "BI": [ + "Africa/Bujumbura" + ], + "BJ": [ + "Africa/Porto-Novo" + ], + "BL": [ + "America/St_Barthelemy" + ], + "BM": [ + "Atlantic/Bermuda" + ], + "BN": [ + "Asia/Brunei" + ], + "BO": [ + "America/La_Paz" + ], + "BQ": [ + "America/Kralendijk" + ], + "BR": [ + "America/Noronha", + "America/Belem", + "America/Fortaleza", + "America/Recife", + "America/Araguaina", + "America/Maceio", + "America/Bahia", + "America/Sao_Paulo", + "America/Campo_Grande", + "America/Cuiaba", + "America/Santarem", + "America/Porto_Velho", + "America/Boa_Vista", + "America/Manaus", + "America/Eirunepe", + "America/Rio_Branco" + ], + "BS": [ + "America/Nassau" + ], + "BT": [ + "Asia/Thimphu" + ], + "BV": [ + "Europe/Oslo" + ], + "BW": [ + "Africa/Gaborone" + ], + "BY": [ + "Europe/Minsk" + ], + "BZ": [ + "America/Belize" + ], + "CA": [ + "America/St_Johns", + "America/Halifax", + "America/Glace_Bay", + "America/Moncton", + "America/Goose_Bay", + "America/Blanc-Sablon", + "America/Toronto", + "America/Iqaluit", + "America/Atikokan", + "America/Winnipeg", + "America/Resolute", + "America/Rankin_Inlet", + "America/Regina", + "America/Swift_Current", + "America/Edmonton", + "America/Cambridge_Bay", + "America/Inuvik", + "America/Vancouver", + "America/Creston", + "America/Dawson_Creek", + "America/Fort_Nelson", + "America/Whitehorse", + "America/Dawson" + ], + "CC": [ + "Indian/Cocos" + ], + "CD": [ + "Africa/Kinshasa", + "Africa/Lubumbashi" + ], + "CF": [ + "Africa/Bangui" + ], + "CG": [ + "Africa/Brazzaville" + ], + "CH": [ + "Europe/Zurich" + ], + "CI": [ + "Africa/Abidjan" + ], + "CK": [ + "Pacific/Rarotonga" + ], + "CL": [ + "America/Santiago", + "America/Coyhaique", + "America/Punta_Arenas", + "Pacific/Easter" + ], + "CM": [ + "Africa/Douala" + ], + "CN": [ + "Asia/Shanghai", + "Asia/Urumqi" + ], + "CO": [ + "America/Bogota" + ], + "CR": [ + "America/Costa_Rica" + ], + "CU": [ + "America/Havana" + ], + "CV": [ + "Atlantic/Cape_Verde" + ], + "CW": [ + "America/Curacao" + ], + "CX": [ + "Indian/Christmas" + ], + "CY": [ + "Asia/Nicosia", + "Asia/Famagusta" + ], + "CZ": [ + "Europe/Prague" + ], + "DE": [ + "Europe/Berlin", + "Europe/Busingen" + ], + "DJ": [ + "Africa/Djibouti" + ], + "DK": [ + "Europe/Copenhagen" + ], + "DM": [ + "America/Dominica" + ], + "DO": [ + "America/Santo_Domingo" + ], + "DZ": [ + "Africa/Algiers" + ], + "EC": [ + "America/Guayaquil", + "Pacific/Galapagos" + ], + "EE": [ + "Europe/Tallinn" + ], + "EG": [ + "Africa/Cairo" + ], + "EH": [ + "Africa/El_Aaiun" + ], + "ER": [ + "Africa/Asmara" + ], + "ES": [ + "Europe/Madrid", + "Africa/Ceuta", + "Atlantic/Canary" + ], + "ET": [ + "Africa/Addis_Ababa" + ], + "FI": [ + "Europe/Helsinki" + ], + "FJ": [ + "Pacific/Fiji" + ], + "FK": [ + "Atlantic/Stanley" + ], + "FM": [ + "Pacific/Chuuk", + "Pacific/Pohnpei", + "Pacific/Kosrae" + ], + "FO": [ + "Atlantic/Faroe" + ], + "FR": [ + "Europe/Paris" + ], + "GA": [ + "Africa/Libreville" + ], + "GB": [ + "Europe/London" + ], + "GD": [ + "America/Grenada" + ], + "GE": [ + "Asia/Tbilisi" + ], + "GF": [ + "America/Cayenne" + ], + "GG": [ + "Europe/Guernsey" + ], + "GH": [ + "Africa/Accra" + ], + "GI": [ + "Europe/Gibraltar" + ], + "GL": [ + "America/Nuuk", + "America/Danmarkshavn", + "America/Scoresbysund", + "America/Thule" + ], + "GM": [ + "Africa/Banjul" + ], + "GN": [ + "Africa/Conakry" + ], + "GP": [ + "America/Guadeloupe" + ], + "GQ": [ + "Africa/Malabo" + ], + "GR": [ + "Europe/Athens" + ], + "GS": [ + "Atlantic/South_Georgia" + ], + "GT": [ + "America/Guatemala" + ], + "GU": [ + "Pacific/Guam" + ], + "GW": [ + "Africa/Bissau" + ], + "GY": [ + "America/Guyana" + ], + "HK": [ + "Asia/Hong_Kong" + ], + "HM": [ + "Indian/Kerguelen" + ], + "HN": [ + "America/Tegucigalpa" + ], + "HR": [ + "Europe/Zagreb" + ], + "HT": [ + "America/Port-au-Prince" + ], + "HU": [ + "Europe/Budapest" + ], + "ID": [ + "Asia/Jakarta", + "Asia/Pontianak", + "Asia/Makassar", + "Asia/Jayapura" + ], + "IE": [ + "Europe/Dublin" + ], + "IL": [ + "Asia/Jerusalem" + ], + "IM": [ + "Europe/Isle_of_Man" + ], + "IN": [ + "Asia/Kolkata" + ], + "IO": [ + "Indian/Chagos" + ], + "IQ": [ + "Asia/Baghdad" + ], + "IR": [ + "Asia/Tehran" + ], + "IS": [ + "Atlantic/Reykjavik" + ], + "IT": [ + "Europe/Rome" + ], + "JE": [ + "Europe/Jersey" + ], + "JM": [ + "America/Jamaica" + ], + "JO": [ + "Asia/Amman" + ], + "JP": [ + "Asia/Tokyo" + ], + "KE": [ + "Africa/Nairobi" + ], + "KG": [ + "Asia/Bishkek" + ], + "KH": [ + "Asia/Phnom_Penh" + ], + "KI": [ + "Pacific/Tarawa", + "Pacific/Kanton", + "Pacific/Kiritimati" + ], + "KM": [ + "Indian/Comoro" + ], + "KN": [ + "America/St_Kitts" + ], + "KP": [ + "Asia/Pyongyang" + ], + "KR": [ + "Asia/Seoul" + ], + "KW": [ + "Asia/Kuwait" + ], + "KY": [ + "America/Cayman" + ], + "KZ": [ + "Asia/Almaty", + "Asia/Qyzylorda", + "Asia/Qostanay", + "Asia/Aqtobe", + "Asia/Aqtau", + "Asia/Atyrau", + "Asia/Oral" + ], + "LA": [ + "Asia/Vientiane" + ], + "LB": [ + "Asia/Beirut" + ], + "LC": [ + "America/St_Lucia" + ], + "LI": [ + "Europe/Vaduz" + ], + "LK": [ + "Asia/Colombo" + ], + "LR": [ + "Africa/Monrovia" + ], + "LS": [ + "Africa/Maseru" + ], + "LT": [ + "Europe/Vilnius" + ], + "LU": [ + "Europe/Luxembourg" + ], + "LV": [ + "Europe/Riga" + ], + "LY": [ + "Africa/Tripoli" + ], + "MA": [ + "Africa/Casablanca" + ], + "MC": [ + "Europe/Monaco" + ], + "MD": [ + "Europe/Chisinau" + ], + "ME": [ + "Europe/Podgorica" + ], + "MF": [ + "America/Marigot" + ], + "MG": [ + "Indian/Antananarivo" + ], + "MH": [ + "Pacific/Majuro", + "Pacific/Kwajalein" + ], + "MK": [ + "Europe/Skopje" + ], + "ML": [ + "Africa/Bamako" + ], + "MM": [ + "Asia/Yangon" + ], + "MN": [ + "Asia/Ulaanbaatar", + "Asia/Hovd" + ], + "MO": [ + "Asia/Macau" + ], + "MP": [ + "Pacific/Saipan" + ], + "MQ": [ + "America/Martinique" + ], + "MR": [ + "Africa/Nouakchott" + ], + "MS": [ + "America/Montserrat" + ], + "MT": [ + "Europe/Malta" + ], + "MU": [ + "Indian/Mauritius" + ], + "MV": [ + "Indian/Maldives" + ], + "MW": [ + "Africa/Blantyre" + ], + "MX": [ + "America/Mexico_City", + "America/Cancun", + "America/Merida", + "America/Monterrey", + "America/Matamoros", + "America/Chihuahua", + "America/Ciudad_Juarez", + "America/Ojinaga", + "America/Mazatlan", + "America/Bahia_Banderas", + "America/Hermosillo", + "America/Tijuana" + ], + "MY": [ + "Asia/Kuala_Lumpur", + "Asia/Kuching" + ], + "MZ": [ + "Africa/Maputo" + ], + "NA": [ + "Africa/Windhoek" + ], + "NC": [ + "Pacific/Noumea" + ], + "NE": [ + "Africa/Niamey" + ], + "NF": [ + "Pacific/Norfolk" + ], + "NG": [ + "Africa/Lagos" + ], + "NI": [ + "America/Managua" + ], + "NL": [ + "Europe/Amsterdam" + ], + "NO": [ + "Europe/Oslo" + ], + "NP": [ + "Asia/Kathmandu" + ], + "NR": [ + "Pacific/Nauru" + ], + "NU": [ + "Pacific/Niue" + ], + "NZ": [ + "Pacific/Auckland", + "Pacific/Chatham" + ], + "OM": [ + "Asia/Muscat" + ], + "PA": [ + "America/Panama" + ], + "PE": [ + "America/Lima" + ], + "PF": [ + "Pacific/Tahiti", + "Pacific/Marquesas", + "Pacific/Gambier" + ], + "PG": [ + "Pacific/Port_Moresby", + "Pacific/Bougainville" + ], + "PH": [ + "Asia/Manila" + ], + "PK": [ + "Asia/Karachi" + ], + "PL": [ + "Europe/Warsaw" + ], + "PM": [ + "America/Miquelon" + ], + "PN": [ + "Pacific/Pitcairn" + ], + "PR": [ + "America/Puerto_Rico" + ], + "PS": [ + "Asia/Gaza", + "Asia/Hebron" + ], + "PT": [ + "Europe/Lisbon", + "Atlantic/Madeira", + "Atlantic/Azores" + ], + "PW": [ + "Pacific/Palau" + ], + "PY": [ + "America/Asuncion" + ], + "QA": [ + "Asia/Qatar" + ], + "RE": [ + "Indian/Reunion" + ], + "RO": [ + "Europe/Bucharest" + ], + "RS": [ + "Europe/Belgrade" + ], + "RU": [ + "Europe/Kaliningrad", + "Europe/Moscow", + "Europe/Kirov", + "Europe/Volgograd", + "Europe/Astrakhan", + "Europe/Saratov", + "Europe/Ulyanovsk", + "Europe/Samara", + "Asia/Yekaterinburg", + "Asia/Omsk", + "Asia/Novosibirsk", + "Asia/Barnaul", + "Asia/Tomsk", + "Asia/Novokuznetsk", + "Asia/Krasnoyarsk", + "Asia/Irkutsk", + "Asia/Chita", + "Asia/Yakutsk", + "Asia/Khandyga", + "Asia/Vladivostok", + "Asia/Ust-Nera", + "Asia/Magadan", + "Asia/Sakhalin", + "Asia/Srednekolymsk", + "Asia/Kamchatka", + "Asia/Anadyr" + ], + "RW": [ + "Africa/Kigali" + ], + "SA": [ + "Asia/Riyadh" + ], + "SB": [ + "Pacific/Guadalcanal" + ], + "SC": [ + "Indian/Mahe" + ], + "SD": [ + "Africa/Khartoum" + ], + "SE": [ + "Europe/Stockholm" + ], + "SG": [ + "Asia/Singapore" + ], + "SH": [ + "Atlantic/St_Helena" + ], + "SI": [ + "Europe/Ljubljana" + ], + "SJ": [ + "Arctic/Longyearbyen" + ], + "SK": [ + "Europe/Bratislava" + ], + "SL": [ + "Africa/Freetown" + ], + "SM": [ + "Europe/San_Marino" + ], + "SN": [ + "Africa/Dakar" + ], + "SO": [ + "Africa/Mogadishu" + ], + "SR": [ + "America/Paramaribo" + ], + "SS": [ + "Africa/Juba" + ], + "ST": [ + "Africa/Sao_Tome" + ], + "SV": [ + "America/El_Salvador" + ], + "SX": [ + "America/Lower_Princes" + ], + "SY": [ + "Asia/Damascus" + ], + "SZ": [ + "Africa/Mbabane" + ], + "TC": [ + "America/Grand_Turk" + ], + "TD": [ + "Africa/Ndjamena" + ], + "TF": [ + "Indian/Kerguelen" + ], + "TG": [ + "Africa/Lome" + ], + "TH": [ + "Asia/Bangkok" + ], + "TJ": [ + "Asia/Dushanbe" + ], + "TK": [ + "Pacific/Fakaofo" + ], + "TL": [ + "Asia/Dili" + ], + "TM": [ + "Asia/Ashgabat" + ], + "TN": [ + "Africa/Tunis" + ], + "TO": [ + "Pacific/Tongatapu" + ], + "TR": [ + "Europe/Istanbul" + ], + "TT": [ + "America/Port_of_Spain" + ], + "TV": [ + "Pacific/Funafuti" + ], + "TW": [ + "Asia/Taipei" + ], + "TZ": [ + "Africa/Dar_es_Salaam" + ], + "UA": [ + "Europe/Simferopol", + "Europe/Kyiv" + ], + "UG": [ + "Africa/Kampala" + ], + "UM": [ + "Pacific/Midway", + "Pacific/Wake" + ], + "US": [ + "America/New_York", + "America/Detroit", + "America/Kentucky/Louisville", + "America/Kentucky/Monticello", + "America/Indiana/Indianapolis", + "America/Indiana/Vincennes", + "America/Indiana/Winamac", + "America/Indiana/Marengo", + "America/Indiana/Petersburg", + "America/Indiana/Vevay", + "America/Chicago", + "America/Indiana/Tell_City", + "America/Indiana/Knox", + "America/Menominee", + "America/North_Dakota/Center", + "America/North_Dakota/New_Salem", + "America/North_Dakota/Beulah", + "America/Denver", + "America/Boise", + "America/Phoenix", + "America/Los_Angeles", + "America/Anchorage", + "America/Juneau", + "America/Sitka", + "America/Metlakatla", + "America/Yakutat", + "America/Nome", + "America/Adak", + "Pacific/Honolulu" + ], + "UY": [ + "America/Montevideo" + ], + "UZ": [ + "Asia/Samarkand", + "Asia/Tashkent" + ], + "VA": [ + "Europe/Vatican" + ], + "VC": [ + "America/St_Vincent" + ], + "VE": [ + "America/Caracas" + ], + "VG": [ + "America/Tortola" + ], + "VI": [ + "America/St_Thomas" + ], + "VN": [ + "Asia/Ho_Chi_Minh" + ], + "VU": [ + "Pacific/Efate" + ], + "WF": [ + "Pacific/Wallis" + ], + "WS": [ + "Pacific/Apia" + ], + "YE": [ + "Asia/Aden" + ], + "YT": [ + "Indian/Mayotte" + ], + "ZA": [ + "Africa/Johannesburg" + ], + "ZM": [ + "Africa/Lusaka" + ], + "ZW": [ + "Africa/Harare" + ] +} diff --git a/scripts/data/lpcab-country-map.json b/scripts/data/lpcab-country-map.json new file mode 100644 index 0000000..c5a8be3 --- /dev/null +++ b/scripts/data/lpcab-country-map.json @@ -0,0 +1,801 @@ +{ + "AD": [ + "ca-ES" + ], + "AE": [ + "ar-SA" + ], + "AF": [ + "en-GB" + ], + "AG": [ + "en-US" + ], + "AI": [ + "en-US" + ], + "AL": [ + "en-GB" + ], + "AM": [ + "ru-RU" + ], + "AO": [ + "pt-PT" + ], + "AQ": [ + "en-GB" + ], + "AR": [ + "es-MX" + ], + "AS": [ + "en-US" + ], + "AT": [ + "de-DE", + "hr-HR", + "sl-SI", + "hu-HU" + ], + "AU": [ + "en-GB" + ], + "AW": [ + "nl-NL" + ], + "AX": [ + "sv-SE" + ], + "AZ": [ + "en-GB" + ], + "BA": [ + "hr-HR", + "sr-Latn-RS" + ], + "BB": [ + "en-US" + ], + "BD": [ + "en-GB" + ], + "BE": [ + "nl-NL", + "fr-FR", + "de-DE" + ], + "BF": [ + "fr-FR" + ], + "BG": [ + "bg-BG" + ], + "BH": [ + "ar-SA" + ], + "BI": [ + "fr-FR", + "en-GB" + ], + "BJ": [ + "fr-FR" + ], + "BL": [ + "fr-FR" + ], + "BM": [ + "en-US" + ], + "BN": [ + "zh-TW" + ], + "BO": [ + "es-MX" + ], + "BQ": [ + "nl-NL" + ], + "BR": [ + "pt-BR" + ], + "BS": [ + "en-US" + ], + "BT": [ + "en-GB" + ], + "BV": [ + "en-GB" + ], + "BW": [ + "en-GB" + ], + "BY": [ + "ru-RU" + ], + "BZ": [ + "en-US" + ], + "CA": [ + "en-US", + "fr-CA" + ], + "CC": [ + "en-GB" + ], + "CD": [ + "fr-FR" + ], + "CF": [ + "fr-FR" + ], + "CG": [ + "fr-FR" + ], + "CH": [ + "de-DE", + "fr-FR", + "it-IT" + ], + "CI": [ + "fr-FR" + ], + "CK": [ + "en-GB" + ], + "CL": [ + "es-MX" + ], + "CM": [ + "fr-FR", + "en-GB" + ], + "CN": [ + "zh-CN", + "ko-KR" + ], + "CO": [ + "es-MX" + ], + "CR": [ + "es-MX" + ], + "CU": [ + "es-MX" + ], + "CV": [ + "pt-PT" + ], + "CW": [ + "nl-NL" + ], + "CX": [ + "en-GB" + ], + "CY": [ + "el-GR", + "tr-TR" + ], + "CZ": [ + "cs-CZ" + ], + "DE": [ + "de-DE" + ], + "DJ": [ + "fr-FR", + "ar-SA" + ], + "DK": [ + "da-DK", + "de-DE" + ], + "DM": [ + "en-US" + ], + "DO": [ + "es-MX" + ], + "DZ": [ + "ar-SA", + "fr-FR" + ], + "EC": [ + "es-MX" + ], + "EE": [ + "et-EE" + ], + "EG": [ + "ar-SA" + ], + "EH": [ + "ar-SA" + ], + "ER": [ + "en-GB", + "ar-SA" + ], + "ES": [ + "es-ES", + "ca-ES", + "gl-ES", + "eu-ES" + ], + "ET": [ + "en-GB" + ], + "FI": [ + "fi-FI", + "sv-SE" + ], + "FJ": [ + "en-GB" + ], + "FK": [ + "en-US" + ], + "FM": [ + "en-US" + ], + "FO": [ + "en-GB" + ], + "FR": [ + "fr-FR" + ], + "GA": [ + "fr-FR" + ], + "GB": [ + "en-GB" + ], + "GD": [ + "en-US" + ], + "GE": [ + "ru-RU" + ], + "GF": [ + "fr-FR" + ], + "GG": [ + "en-GB" + ], + "GH": [ + "en-GB" + ], + "GI": [ + "en-GB" + ], + "GL": [ + "da-DK" + ], + "GM": [ + "en-GB" + ], + "GN": [ + "fr-FR" + ], + "GP": [ + "fr-FR" + ], + "GQ": [ + "es-ES", + "fr-FR", + "pt-PT" + ], + "GR": [ + "el-GR" + ], + "GS": [ + "en-US" + ], + "GT": [ + "es-MX" + ], + "GU": [ + "en-US" + ], + "GW": [ + "pt-PT" + ], + "GY": [ + "en-US" + ], + "HK": [ + "zh-TW", + "en-GB" + ], + "HM": [ + "en-GB" + ], + "HN": [ + "es-MX" + ], + "HR": [ + "hr-HR", + "it-IT" + ], + "HT": [ + "fr-FR" + ], + "HU": [ + "hu-HU" + ], + "ID": [ + "id-ID" + ], + "IE": [ + "en-GB" + ], + "IL": [ + "he-IL", + "ar-SA" + ], + "IM": [ + "en-GB" + ], + "IN": [ + "en-GB" + ], + "IO": [ + "en-GB" + ], + "IQ": [ + "ar-SA" + ], + "IR": [ + "en-GB" + ], + "IS": [ + "en-GB" + ], + "IT": [ + "it-IT", + "fr-FR" + ], + "JE": [ + "en-GB" + ], + "JM": [ + "en-US" + ], + "JO": [ + "ar-SA" + ], + "JP": [ + "ja-JP" + ], + "KE": [ + "en-GB" + ], + "KG": [ + "ru-RU" + ], + "KH": [ + "en-GB" + ], + "KI": [ + "en-GB" + ], + "KM": [ + "ar-SA", + "fr-FR" + ], + "KN": [ + "en-US" + ], + "KP": [ + "ko-KR" + ], + "KR": [ + "ko-KR" + ], + "KW": [ + "ar-SA" + ], + "KY": [ + "en-US" + ], + "KZ": [ + "ru-RU" + ], + "LA": [ + "en-GB" + ], + "LB": [ + "ar-SA" + ], + "LC": [ + "en-US" + ], + "LI": [ + "de-DE" + ], + "LK": [ + "en-GB" + ], + "LR": [ + "en-GB" + ], + "LS": [ + "en-GB" + ], + "LT": [ + "lt-LT" + ], + "LU": [ + "fr-FR", + "de-DE" + ], + "LV": [ + "lv-LV" + ], + "LY": [ + "ar-SA" + ], + "MA": [ + "ar-SA", + "fr-FR" + ], + "MC": [ + "fr-FR" + ], + "MD": [ + "ro-RO" + ], + "ME": [ + "sr-Latn-RS" + ], + "MF": [ + "fr-FR" + ], + "MG": [ + "fr-FR", + "en-GB" + ], + "MH": [ + "en-US" + ], + "MK": [ + "tr-TR" + ], + "ML": [ + "fr-FR" + ], + "MM": [ + "en-GB" + ], + "MN": [ + "en-GB" + ], + "MO": [ + "zh-TW", + "pt-PT" + ], + "MP": [ + "en-US" + ], + "MQ": [ + "fr-FR" + ], + "MR": [ + "ar-SA" + ], + "MS": [ + "en-US" + ], + "MT": [ + "en-GB" + ], + "MU": [ + "fr-FR", + "en-GB" + ], + "MV": [ + "en-GB" + ], + "MW": [ + "en-GB" + ], + "MX": [ + "es-MX" + ], + "MY": [ + "en-GB" + ], + "MZ": [ + "pt-PT" + ], + "NA": [ + "en-GB" + ], + "NC": [ + "fr-FR" + ], + "NE": [ + "fr-FR" + ], + "NF": [ + "en-GB" + ], + "NG": [ + "en-GB" + ], + "NI": [ + "es-MX" + ], + "NL": [ + "nl-NL" + ], + "NO": [ + "nb-NO" + ], + "NP": [ + "en-GB" + ], + "NR": [ + "en-GB" + ], + "NU": [ + "en-GB" + ], + "NZ": [ + "en-GB" + ], + "OM": [ + "ar-SA" + ], + "PA": [ + "es-MX" + ], + "PE": [ + "es-MX" + ], + "PF": [ + "fr-FR" + ], + "PG": [ + "en-GB" + ], + "PH": [ + "en-US" + ], + "PK": [ + "en-GB" + ], + "PL": [ + "pl-PL", + "de-DE", + "lt-LT" + ], + "PM": [ + "fr-FR" + ], + "PN": [ + "en-GB" + ], + "PR": [ + "es-MX", + "en-US" + ], + "PS": [ + "ar-SA" + ], + "PT": [ + "pt-PT" + ], + "PW": [ + "en-US" + ], + "PY": [ + "es-MX" + ], + "QA": [ + "ar-SA" + ], + "RE": [ + "fr-FR" + ], + "RO": [ + "ro-RO" + ], + "RS": [ + "sr-Latn-RS", + "hu-HU", + "ro-RO", + "hr-HR", + "sk-SK", + "uk-UA" + ], + "RU": [ + "ru-RU" + ], + "RW": [ + "en-GB", + "fr-FR" + ], + "SA": [ + "ar-SA" + ], + "SB": [ + "en-GB" + ], + "SC": [ + "fr-FR", + "en-GB" + ], + "SD": [ + "en-GB", + "ar-SA" + ], + "SE": [ + "sv-SE", + "fi-FI" + ], + "SG": [ + "en-GB", + "zh-CN" + ], + "SH": [ + "en-GB" + ], + "SI": [ + "sl-SI" + ], + "SJ": [ + "nb-NO" + ], + "SK": [ + "sk-SK" + ], + "SL": [ + "en-GB" + ], + "SM": [ + "it-IT" + ], + "SN": [ + "fr-FR" + ], + "SO": [ + "ar-SA" + ], + "SR": [ + "nl-NL" + ], + "SS": [ + "en-GB" + ], + "ST": [ + "pt-PT" + ], + "SV": [ + "es-MX" + ], + "SX": [ + "en-US", + "nl-NL" + ], + "SY": [ + "ar-SA" + ], + "SZ": [ + "en-GB" + ], + "TC": [ + "en-US" + ], + "TD": [ + "ar-SA", + "fr-FR" + ], + "TF": [ + "fr-FR" + ], + "TG": [ + "fr-FR" + ], + "TH": [ + "th-TH" + ], + "TJ": [ + "ru-RU" + ], + "TK": [ + "en-GB" + ], + "TL": [ + "pt-PT" + ], + "TM": [ + "ru-RU" + ], + "TN": [ + "ar-SA", + "fr-FR" + ], + "TO": [ + "en-GB" + ], + "TR": [ + "tr-TR" + ], + "TT": [ + "en-US" + ], + "TV": [ + "en-GB" + ], + "TW": [ + "zh-TW" + ], + "TZ": [ + "en-GB" + ], + "UA": [ + "uk-UA", + "ru-RU" + ], + "UG": [ + "en-GB" + ], + "UM": [ + "en-US" + ], + "US": [ + "en-US", + "es-MX" + ], + "UY": [ + "es-MX" + ], + "UZ": [ + "ru-RU" + ], + "VA": [ + "it-IT" + ], + "VC": [ + "en-US" + ], + "VE": [ + "es-MX" + ], + "VG": [ + "en-US" + ], + "VI": [ + "en-US" + ], + "VN": [ + "vi-VN" + ], + "VU": [ + "en-GB", + "fr-FR" + ], + "WF": [ + "fr-FR" + ], + "WS": [ + "en-GB" + ], + "YE": [ + "ar-SA" + ], + "YT": [ + "fr-FR" + ], + "ZA": [ + "en-GB" + ], + "ZM": [ + "en-GB" + ], + "ZW": [ + "en-GB" + ] +} diff --git a/scripts/gen-countries.mjs b/scripts/gen-countries.mjs new file mode 100644 index 0000000..272472a --- /dev/null +++ b/scripts/gen-countries.mjs @@ -0,0 +1,33 @@ +// Generates docs/options/countries.mdx from the API's locale catalog: for every supported country, the +// IANA timezones and BCP-47 languages that `timezone` and `language` accept on create. The two JSON files +// under scripts/data/ are copies of the gateway's catalog (src/data/iana-timezones-by-country.json and +// lpcab-country-map.json); refresh them when the gateway's change, then run `npm run countries`. +import fs from 'node:fs' + +const tz = JSON.parse(fs.readFileSync('scripts/data/iana-timezones-by-country.json', 'utf8')) +const lang = JSON.parse(fs.readFileSync('scripts/data/lpcab-country-map.json', 'utf8')) +const names = new Intl.DisplayNames(['en'], { type: 'region' }) + +const codes = Object.keys(tz).filter((c) => tz[c]?.length && lang[c]?.length).sort() +const rows = codes.map((c) => { + let name = c + try { name = names.of(c) || c } catch {} + const languages = c === 'US' ? ['en-US'] : lang[c] + return `| \`${c}\` | ${name} | ${tz[c].map((t) => `\`${t}\``).join(', ')} | ${languages.map((l) => `\`${l}\``).join(', ')} |` +}) + +const page = `--- +title: "Countries" +description: "Every country you can pass on create, with the timezones and languages it accepts. Generated from the API's locale catalog." +--- + +\`country\` accepts any of the ${codes.length} two-letter codes below. \`timezone\` must be one of that country's timezones and \`language\` one of its languages, or the request is rejected with \`400\`. Leave both out and Driver picks the first-listed timezone (or a managed-network region where it has one) and the country's primary language. US sessions always use \`en-US\`. See [Location](/docs/options/location) for how the three stay coherent. + +Availability of machines varies by country; a country with no machine free at the moment answers \`503\`. + +| Code | Country | Timezones | Languages | +|---|---|---|---| +${rows.join('\n')} +` +fs.writeFileSync('docs/options/countries.mdx', page) +console.log(`wrote docs/options/countries.mdx: ${codes.length} countries`) diff --git a/scripts/sync-openapi.mjs b/scripts/sync-openapi.mjs index a9e456f..9fa2b6b 100644 --- a/scripts/sync-openapi.mjs +++ b/scripts/sync-openapi.mjs @@ -68,6 +68,26 @@ const fixExamples = (node) => { } fixExamples(doc) +// Corrections to what the generator emits, verified against the gateway and session service source: +// - duration: zod's .positive().min(60) renders as minimum 60 + exclusiveMinimum true, but 60 is accepted. +// - balance_cents can go negative (the wallet may be overdrawn), so the minimum is wrong. +// - PATCH answers 400 for a session that has ended; the generator does not declare it. +// - the security description, external docs and contact name the old brand. +const create = doc.paths['/v1/browser/session'] +const duration = create?.post?.requestBody?.content?.['application/json']?.schema?.properties?.duration +if (duration) delete duration.exclusiveMinimum +const balanceCents = doc.paths['/v1/account/balance']?.get?.responses?.['200']?.content?.['application/json']?.schema?.properties?.balance?.properties?.balance_cents +if (balanceCents) { delete balanceCents.minimum; balanceCents.description = `${balanceCents.description || 'Balance in cents.'} Can be negative when usage exceeds prepaid credit.` } +if (create?.patch && !create.patch.responses['400']) { + create.patch.responses['400'] = { + description: 'The session has ended ("Cannot update a completed or errored session") or the note is longer than 256 characters.', + content: { 'application/json': { schema: { type: 'object', properties: { error: { type: 'string' } }, required: ['error'] } } }, + } +} +if (doc.components?.securitySchemes?.Bearer) doc.components.securitySchemes.Bearer.description = 'A workspace API key from Settings → API keys in the dashboard (https://app.driver.dev), sent as a bearer token.' +doc.externalDocs = { url: 'https://docs.driver.dev', description: 'Driver documentation' } +if (doc.info) doc.info.contact = { name: 'Driver support', email: 'support@driver.dev' } + // The source document names internal systems. Readers get product language instead. Ordered: phrases first, then words. const SCRUB = [ [/ consumer_distributed is the legacy route\./g, ''], @@ -78,8 +98,8 @@ const SCRUB = [ [/hosted, hosted_stealth, and hosted_privacy launch on Rio; hosted_stealth enables conservative Mirage Stealth and hosted_privacy enables the preserved aggressive Stealth policy\./g, 'hosted, hosted_stealth, and hosted_privacy are hosted browsers; hosted_stealth applies the conservative stealth policy and hosted_privacy the aggressive one.'], [/hosted uses Mirage natively, hosted_stealth uses conservative Stealth, and hosted_privacy uses the preserved aggressive Stealth policy\./g, 'hosted is the native browser, hosted_stealth applies the conservative stealth policy, and hosted_privacy the aggressive one.'], [/Rio\/Mirage virtual display size/g, 'Virtual display size'], - [/Only supported by Rio-backed hosted types\./g, 'Only supported by hosted types.'], - [/Supported by all Rio-backed hosted types/g, 'Supported by all hosted types'], + [/ Only supported by Rio-backed hosted types\./g, ''], + [/ Supported by all Rio-backed hosted types\.?/g, ''], [/Rio stores persistent profiles as portable Mirage plaintext archives\./g, 'Persistent profiles are kept between sessions.'], [/so Mirage uses its built-in en-US default/g, 'so the browser uses its built-in en-US default'], [/primary language supported by Mirage/g, 'primary language the browser supports'], @@ -89,6 +109,11 @@ const SCRUB = [ [/Lisa or its billing provider could not return billing details\./g, 'Billing details could not be returned.'], [/wss:\/\/[a-z0-9.-]+\.tera\.space\//g, 'wss://sessions.driver.dev/'], [/[a-z0-9.-]+\.tera\.space/g, 'driver.dev'], + [/Cloaked outer browser-window size/g, 'Outer browser-window size'], + [/ and targets managed proxy geography where the provider supports regional targeting/g, ' and, where Driver runs a managed network there, routes the session through that region'], + [/Forwarded to node-side launch when supported\.?/g, 'Opened right after launch.'], + [/\/v1\/consumer\//g, '/v1/session/'], + [/ \(browser or agent, they work interchangeably\)/g, ''], [/APIGW/g, 'Driver'], [/\bRio\b/g, 'Driver'], [/\bMirage\b/g, 'Chrome'], From 1b1208c479a8802f110b4a80568f72daa184e994 Mon Sep 17 00:00:00 2001 From: browsercash Date: Sat, 19 Sep 2026 17:49:14 -0400 Subject: [PATCH 03/11] Docs v2: README note (preview branch) Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01CnA3RKAvP4gwSd9qrwBFCg --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9f37f2f..55e0adf 100644 --- a/README.md +++ b/README.md @@ -36,5 +36,6 @@ page keep working. On 2026-09-19 five fresh agents (Node, Python, REST-only, framework migration, coding-agent handoff) integrated Driver from this folder alone and reported every gap; the fixes are in the git history of that day. Re-run the exercise after -big changes: the reports live in the dashboard repo's docs/STATUS-LOG.md entry for that date. also corrects four things the generator gets wrong (duration bound, negative balances, PATCH 400, branding); keep +big changes: the reports live in the dashboard repo's docs/STATUS-LOG.md entry for that date. `scripts/sync-openapi.mjs` +also corrects four things the generator gets wrong (duration bound, negative balances, PATCH 400, branding); keep those corrections when the source changes. From 3c35b4688d93e2431d1c028d113147e5cd06c471 Mon Sep 17 00:00:00 2001 From: browsercash Date: Sat, 19 Sep 2026 17:53:05 -0400 Subject: [PATCH 04/11] Docs v2: reference client meets its own checklist (preview branch) Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01CnA3RKAvP4gwSd9qrwBFCg --- docs/frameworks/patchright.mdx | 2 +- docs/frameworks/puppeteer.mdx | 2 +- docs/frameworks/stagehand.mdx | 2 +- docs/sessions/create.mdx | 2 +- docs/sessions/errors.mdx | 4 +- docs/sessions/lifecycle.mdx | 4 +- docs/start/for-agents.mdx | 88 ++++++++++++++++++++++++++++------ docs/start/quickstart.mdx | 2 +- 8 files changed, 82 insertions(+), 24 deletions(-) diff --git a/docs/frameworks/patchright.mdx b/docs/frameworks/patchright.mdx index d0a97be..419ea58 100644 --- a/docs/frameworks/patchright.mdx +++ b/docs/frameworks/patchright.mdx @@ -45,7 +45,7 @@ const res = await fetch(`${API}/v1/browser/session`, { headers, body: JSON.stringify({ country: "US" }), }); -if (!res.ok) throw new Error(`Driver ${res.status}: ${(await res.json()).error}`); +if (!res.ok) throw new Error(`Driver ${res.status}: ${(await res.json().catch(() => ({}))).error ?? res.statusText}`); const session = await res.json(); try { diff --git a/docs/frameworks/puppeteer.mdx b/docs/frameworks/puppeteer.mdx index bff2cc9..e5b0505 100644 --- a/docs/frameworks/puppeteer.mdx +++ b/docs/frameworks/puppeteer.mdx @@ -27,7 +27,7 @@ const res = await fetch(`${API}/v1/browser/session`, { headers, body: JSON.stringify({ country: "US" }), }); -if (!res.ok) throw new Error(`Driver ${res.status}: ${(await res.json()).error}`); +if (!res.ok) throw new Error(`Driver ${res.status}: ${(await res.json().catch(() => ({}))).error ?? res.statusText}`); const session = await res.json(); try { diff --git a/docs/frameworks/stagehand.mdx b/docs/frameworks/stagehand.mdx index 4d8f556..c49a8f4 100644 --- a/docs/frameworks/stagehand.mdx +++ b/docs/frameworks/stagehand.mdx @@ -24,7 +24,7 @@ const res = await fetch(`${API}/v1/browser/session`, { headers, body: JSON.stringify({ country: "US" }), }); -if (!res.ok) throw new Error(`Driver ${res.status}: ${(await res.json()).error}`); +if (!res.ok) throw new Error(`Driver ${res.status}: ${(await res.json().catch(() => ({}))).error ?? res.statusText}`); const session = await res.json(); try { diff --git a/docs/sessions/create.mdx b/docs/sessions/create.mdx index 119e9fd..c10ec87 100644 --- a/docs/sessions/create.mdx +++ b/docs/sessions/create.mdx @@ -33,7 +33,7 @@ const res = await fetch("https://api.driver.dev/v1/browser/session", { note: "price check, client X", }), }); -if (!res.ok) throw new Error(`Driver ${res.status}: ${(await res.json()).error}`); +if (!res.ok) throw new Error(`Driver ${res.status}: ${(await res.json().catch(() => ({}))).error ?? res.statusText}`); const session = await res.json(); ``` ```python Python diff --git a/docs/sessions/errors.mdx b/docs/sessions/errors.mdx index ccc385a..eb6a989 100644 --- a/docs/sessions/errors.mdx +++ b/docs/sessions/errors.mdx @@ -3,7 +3,7 @@ title: "Errors" description: "Every status the API returns, what it means, and whether retrying helps." --- -Errors are JSON with an `error` message. One carries a machine-readable `code` as well: the create call's `503`. +Errors from the API are JSON with an `error` message; one carries a machine-readable `code` as well, the create call's `503`. A `429` or `502` from the edge in front of the API may carry a non-JSON body, so read the status first and parse the body defensively. ```json { "error": "Insufficient balance" } @@ -24,7 +24,7 @@ The bodies you will see most: `401` `Unauthorized: Missing or invalid Authorizat | `402` | The workspace is out of credits. | No. Add credits in the dashboard. | | `403` | Session or account limit reached: the workspace's concurrent-browser limit (`plan.concurrent_browsers` on `GET /v1/account/billing`; sessions that are `starting` count, and a stop frees the slot as soon as it returns), or a feature (extensions) the account is not enabled for. | Only after a session ends. Never in a loop. | | `404` | Session, extension or account not found (unknown id, or one belonging to another workspace). Deleting an unknown profile is not a 404: it answers `{ "success": true }`. | No. | -| `429` | Too many requests. The API itself does not rate-limit today; a `429` comes from the edge in front of it. | Yes, with backoff. | +| `429` | Too many requests. The API itself does not rate-limit today; a `429` comes from the edge in front of it. | Yes: a few attempts, a couple of seconds apart and growing. | | `500` | Something failed on Driver's side. | Once. | | `503` | No browser capacity right now. The response carries `Retry-After: 2` and `"code": "browser_capacity_unavailable"`. Or the session service is unavailable. | Yes: back off with jitter, cap the attempts, then tell the user. | | `504` | A machine was selected but Chrome did not become reachable before the startup deadline: 20 seconds, up to three minutes on some machines. | Once. | diff --git a/docs/sessions/lifecycle.mdx b/docs/sessions/lifecycle.mdx index 6443947..4e66b7e 100644 --- a/docs/sessions/lifecycle.mdx +++ b/docs/sessions/lifecycle.mdx @@ -14,7 +14,7 @@ A session moves through `starting` → `active` → `completed`, or `error` if t ## Get a session -Returns the same shape as create, plus `bandwidthBytes`: `0` while the session runs, the metered total once it has ended. +Returns the same shape as create, plus `bandwidthBytes`: `0` while the session runs, the metered total once it has ended. The total is final by the time a stop call returns. ```bash cURL @@ -88,7 +88,7 @@ Returns `{ "success": true }`. Notes are at most 256 characters (longer is a `40 ## Stop a session -Stopping releases the browser and ends billing for the session. Always do it in a `finally`. +Stopping releases the browser and ends billing for the session. Always do it in a `finally`. A session that is still `starting` can be stopped too. ```bash cURL diff --git a/docs/start/for-agents.mdx b/docs/start/for-agents.mdx index 1b1e252..9f83ea7 100644 --- a/docs/start/for-agents.mdx +++ b/docs/start/for-agents.mdx @@ -22,7 +22,7 @@ This page is written for an AI coding agent (Claude Code, Cursor, Codex, or simi - **Minimal logic wins.** The shortest script that reaches the outcome is the most reliable and the least detectable. Go to the page, do the action, read the result. Do not add retries, waits, listeners, request routing or "stealth" helpers unless a specific failure on a specific site demands them, and then add the smallest thing that fixes it. - **Fast and accurate, in that order of effort.** Before writing browser steps, ask whether the outcome is reachable faster: a direct URL instead of clicking through navigation, one session doing several pages instead of one session per page, a persisted [profile](/docs/options/profiles) instead of logging in every run. Then keep the browser steps short: no fixed sleeps, rely on the framework's auto-waiting, stop the session the moment the result is in hand. - **Test the automation for real, every time.** Run it against the live target through Driver and check the result, not just that the script exits. Confirm the data is right, the action happened, and the session was stopped: `GET /v1/browser/session?sessionId=…` shows `status: "completed"`, a `stoppedAt` and the metered `bandwidthBytes` (the same record is under Telemetry → Sessions in the dashboard). A run that "worked" but read the wrong element is worse than one that failed loudly. -- **Unattended jobs clean up after earlier runs.** A crash before `finally` leaves a session running until its `duration`. At start-up, `GET /v1/browser/sessions?status=active`, stop the ones your job created (match on `note`), then start the new one. +- **Unattended jobs clean up after earlier runs.** A crash before `finally` leaves a session running until its `duration`. At start-up, list `status=active` and `status=starting` (`listSessions` / `list_sessions` in the reference client), stop the ones your job created (match on `note`), then start the new one. Exit non-zero when the result was not verified, so the scheduler reports the failure. - **Do not pollute the session.** Every extra CDP feature you switch on, every injected script, every overridden property is a signal. If you did not need it to reach the outcome, take it out. ## Handoff prompt @@ -58,7 +58,7 @@ Integrate Driver (driver.dev), hosted real-Chrome browser infrastructure, into t ## The three calls -Base URL `https://api.driver.dev`. Every call carries `Authorization: Bearer $DRIVER_API_KEY`. Store the key in an environment variable named `DRIVER_API_KEY`; never commit it. +Base URL `https://api.driver.dev`. Every call carries `Authorization: Bearer $DRIVER_API_KEY`. Store the key in an environment variable named `DRIVER_API_KEY`; never commit it. Node's global `fetch` has no timeout, which is right for the create call; if you add one, allow three minutes. | Step | Call | Notes | |---|---|---| @@ -128,25 +128,57 @@ async function call(method: string, path: string, body?: unknown): Promise return res.json() as Promise; } -export const createSession = (opts: CreateOptions = {}) => call("POST", "/v1/browser/session", opts); +const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); + +/** + * The error policy from the Errors page: 402 and 403 are reported at once; 503 backs off from Retry-After + * with jitter and a cap; 504 and 500 are retried once; 429 backs off a few times. + */ +export async function createSession(opts: CreateOptions = {}): Promise { + let attempt = 0; + for (;;) { + try { + return await call("POST", "/v1/browser/session", opts); + } catch (e) { + if (!(e instanceof DriverError)) throw e; + attempt++; + if (e.status === 503 && attempt <= 5) { await sleep((e.retryAfter ?? 2) * 1000 * attempt + Math.random() * 1000); continue; } + if ((e.status === 504 || e.status === 500) && attempt <= 1) continue; + if (e.status === 429 && attempt <= 3) { await sleep(2000 * attempt + Math.random() * 1000); continue; } + throw e; // 400, 401, 402, 403, or out of attempts: a person has to act + } + } +} + export const getSession = (id: string) => call("GET", `/v1/browser/session?sessionId=${encodeURIComponent(id)}`); -/** Idempotent; safe in a finally. */ +export const listSessions = (status?: Session["status"], page = 1, pageSize = 20) => + call<{ sessions: Array; total: number; totalPages: number; page: number; pageSize: number }>( + "GET", `/v1/browser/sessions?page=${page}&pageSize=${pageSize}${status ? `&status=${status}` : ""}`); + +/** + * Idempotent. In a finally, a failed stop must not replace the error you are already handling, so this + * logs instead of throwing; the session ends on its own at `duration` anyway. + */ export async function stopSession(id: string): Promise { - const r = await call<{ success: boolean }>("DELETE", `/v1/browser/session?sessionId=${encodeURIComponent(id)}`); - if (!r.success) throw new DriverError(500, "stop did not succeed"); + try { + const r = await call<{ success: boolean }>("DELETE", `/v1/browser/session?sessionId=${encodeURIComponent(id)}`); + if (!r.success) console.error(`Driver: stop of ${id} did not succeed`); + } catch (e) { + console.error(`Driver: stop of ${id} failed: ${(e as Error).message}`); + } } /** Create normally returns an active session; this covers the rare `starting` answer. */ -async function waitForActive(s: Session, timeoutMs = 60_000): Promise { +async function waitForActive(s: Session, timeoutMs = 60_000): Promise { const deadline = Date.now() + timeoutMs; while (!(s.status === "active" && s.cdpUrl)) { if (s.status === "completed" || s.status === "error") throw new DriverError(500, `session ended: ${s.status}`); if (Date.now() > deadline) throw new DriverError(504, "timed out waiting for the browser"); - await new Promise((r) => setTimeout(r, 1000)); + await sleep(1000); s = await getSession(s.sessionId); } - return s; + return s as Session & { cdpUrl: string }; } /** Runs `fn` against a fresh Driver session and always stops it afterwards. */ @@ -157,7 +189,7 @@ export async function withBrowser( const session = await createSession(opts); try { const active = await waitForActive(session); - const browser = await chromium.connectOverCDP(active.cdpUrl!); + const browser = await chromium.connectOverCDP(active.cdpUrl); try { const context = browser.contexts()[0] ?? (await browser.newContext()); const page = context.pages()[0] ?? (await context.newPage()); @@ -173,6 +205,7 @@ export async function withBrowser( ```python driver.py import os +import random import time from contextlib import contextmanager @@ -204,17 +237,42 @@ def _call(method, path, **kwargs): def create_session(**opts): """opts: type, country, timezone, language, duration, url, profile={"name": …, "persist": True}, - proxyUrl, windowSize, displaySize, captchaSolver, adblock, extensionIds, nodeId, browserCheck, note.""" - return _call("POST", "/v1/browser/session", json=opts) + proxyUrl, windowSize, displaySize, captchaSolver, adblock, extensionIds, nodeId, browserCheck, note. + Applies the error policy from the Errors page: 402/403 raise at once; 503 backs off with a cap; + 504/500 retry once; 429 backs off a few times.""" + attempt = 0 + while True: + try: + return _call("POST", "/v1/browser/session", json=opts) + except DriverError as e: + attempt += 1 + if e.status == 503 and attempt <= 5: + time.sleep(float(e.retry_after or 2) * attempt + random.random()) + elif e.status in (504, 500) and attempt <= 1: + pass + elif e.status == 429 and attempt <= 3: + time.sleep(2 * attempt + random.random()) + else: + raise # 400, 401, 402, 403, or out of attempts: a person has to act def get_session(session_id): return _call("GET", "/v1/browser/session", params={"sessionId": session_id}) +def list_sessions(status=None, page=1, page_size=20): + params = {"page": page, "pageSize": page_size} + if status: + params["status"] = status + return _call("GET", "/v1/browser/sessions", params=params) + + def stop_session(session_id): - """Idempotent; safe in a finally.""" - _call("DELETE", "/v1/browser/session", params={"sessionId": session_id}) + """Idempotent. In a finally, a failed stop must not replace the error you are handling, so this logs.""" + try: + _call("DELETE", "/v1/browser/session", params={"sessionId": session_id}) + except DriverError as e: + print(f"Driver: stop of {session_id} failed: {e}") def wait_for_active(session, timeout=60): @@ -256,7 +314,7 @@ def browser_page(**opts): - [ ] The first context and page are reused. - [ ] The session is stopped in a `finally`. - [ ] `DRIVER_API_KEY` comes from the environment and is not committed. -- [ ] 402 and 403 are reported to the user; 503 is backed off with a cap, never looped. +- [ ] 402 and 403 are reported to the user; 503 is retried only with a cap and a growing delay. - [ ] The result was verified through `GET /v1/browser/session` (status `completed`), not assumed. ## Machine-readable resources diff --git a/docs/start/quickstart.mdx b/docs/start/quickstart.mdx index 80f0afc..c186b4b 100644 --- a/docs/start/quickstart.mdx +++ b/docs/start/quickstart.mdx @@ -46,7 +46,7 @@ description: "Create a Driver session, connect with Patchright, read a page titl headers, body: JSON.stringify({ country: "US", duration: 600 }), }); - if (!res.ok) throw new Error(`Driver ${res.status}: ${(await res.json()).error}`); + if (!res.ok) throw new Error(`Driver ${res.status}: ${(await res.json().catch(() => ({}))).error ?? res.statusText}`); const session = await res.json(); try { From 2f4cd77896007ff76dce837af55c5e62f4a40e96 Mon Sep 17 00:00:00 2001 From: browsercash Date: Sat, 19 Sep 2026 18:00:27 -0400 Subject: [PATCH 05/11] Docs v2: OpenAPI declarations (preview branch) Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01CnA3RKAvP4gwSd9qrwBFCg --- api-reference/openapi.json | 65 ++++++++++++++++++++++++++++--------- api-reference/overview.mdx | 4 +-- docs/sessions/create.mdx | 2 +- docs/sessions/errors.mdx | 1 + docs/sessions/lifecycle.mdx | 2 +- scripts/sync-openapi.mjs | 26 +++++++++++++++ 6 files changed, 81 insertions(+), 19 deletions(-) diff --git a/api-reference/openapi.json b/api-reference/openapi.json index 8b5b41b..f3b2ece 100644 --- a/api-reference/openapi.json +++ b/api-reference/openapi.json @@ -399,7 +399,8 @@ "properties": { "sessionId": { "type": "string", - "description": "The ID of the created browser session." + "description": "The session id: 128 lowercase letters and digits.", + "pattern": "^[a-z0-9]{128}$" }, "status": { "type": "string", @@ -585,7 +586,10 @@ }, "code": { "type": "string", - "description": "Machine-readable error code" + "description": "Machine-readable cause; present when no browser capacity is available.", + "enum": [ + "browser_capacity_unavailable" + ] } }, "required": [ @@ -593,6 +597,14 @@ ] } } + }, + "headers": { + "Retry-After": { + "description": "Seconds to wait before trying again (2).", + "schema": { + "type": "integer" + } + } } }, "504": { @@ -650,7 +662,8 @@ "properties": { "sessionId": { "type": "string", - "description": "The ID of the created browser session." + "description": "The session id: 128 lowercase letters and digits.", + "pattern": "^[a-z0-9]{128}$" }, "status": { "type": "string", @@ -689,7 +702,7 @@ "bandwidthBytes": { "type": "integer", "minimum": 0, - "description": "The total bandwidth used by the session in bytes." + "description": "Bandwidth used by the session in bytes: 0 while it runs, the metered total once it has ended (final by the time a stop call returns)." } }, "required": [ @@ -699,7 +712,8 @@ "createdAt", "stoppedAt", "cdpUrl", - "note" + "note", + "bandwidthBytes" ], "example": { "sessionId": "example-session-id", @@ -1104,10 +1118,10 @@ { "schema": { "type": "integer", - "minimum": 0, - "exclusiveMinimum": true, + "minimum": 1, "example": 1, - "description": "The page number for pagination." + "description": "The page number for pagination.", + "default": 1 }, "required": false, "description": "The page number for pagination.", @@ -1117,13 +1131,14 @@ { "schema": { "type": "integer", - "minimum": 0, - "exclusiveMinimum": true, + "minimum": 1, "example": 20, - "description": "The number of sessions per page." + "description": "The number of sessions per page.", + "default": 20, + "maximum": 100 }, "required": false, - "description": "The number of sessions per page.", + "description": "The number of sessions per page, 1 to 100. Larger values are clamped to 100.", "name": "pageSize", "in": "query" }, @@ -1140,7 +1155,7 @@ "description": "Filter sessions by status." }, "required": false, - "description": "Filter sessions by status.", + "description": "Only sessions in exactly this status.", "name": "status", "in": "query" } @@ -1176,7 +1191,8 @@ "properties": { "sessionId": { "type": "string", - "description": "The unique session ID." + "description": "The session id: 128 lowercase letters and digits.", + "pattern": "^[a-z0-9]{128}$" }, "status": { "type": "string", @@ -1230,7 +1246,8 @@ "country", "createdAt", "stoppedAt", - "note" + "note", + "bandwidthBytes" ] }, "description": "Array of browser sessions." @@ -1266,6 +1283,24 @@ } } }, + "400": { + "description": "The status filter is not one of starting, active, completed, error.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, "401": { "description": "Missing or invalid token.", "content": { diff --git a/api-reference/overview.mdx b/api-reference/overview.mdx index ab98df2..90c4a9e 100644 --- a/api-reference/overview.mdx +++ b/api-reference/overview.mdx @@ -21,7 +21,7 @@ Authorization: Bearer $DRIVER_API_KEY - Request and response bodies are JSON, except extension uploads, which are `multipart/form-data`. - Identifiers that select a resource go in the query string (`?sessionId=…`, `?profileName=…`), not the path, except for extensions. -- Errors are `{ "error": "message" }` with a meaningful HTTP status; some carry a `code`. See [Errors](/docs/sessions/errors). +- Errors are `{ "error": "message" }` with a meaningful HTTP status; the create call's `503` also carries a `code`. See [Errors](/docs/sessions/errors). - Session timestamps are ISO 8601 strings; billing periods use Unix epoch milliseconds (fields ending in `_ms`, and the subscription period bounds). Money is in cents (and millicents where exactness matters); bandwidth is bytes on sessions and megabytes on billing. - Unknown fields in a request body are rejected with `400`. - Create answers `200` with the session; only the extension upload answers `201`. @@ -37,7 +37,7 @@ Authorization: Bearer $DRIVER_API_KEY ## Source of truth -These reference pages are generated from the API's own OpenAPI document, served live at `https://api.driver.dev/doc` and rendered at [api.driver.dev/scalar](https://api.driver.dev/scalar). If a page here and the live document ever disagree, the live document wins. +These reference pages are generated from the API's own OpenAPI document, served live at `https://api.driver.dev/doc` and rendered at [api.driver.dev/scalar](https://api.driver.dev/scalar), with a few corrections the generator gets wrong: `duration` accepts 60 (the live document says strictly more), `balance_cents` can be negative, `PATCH` answers `400` for an ended session, `GET /v1/browser/sessions` answers `400` for a bad status filter, the `503` carries `Retry-After` and a `code`, and paging has defaults and a maximum. On those points this reference is right and the live document is behind; on everything else, if the two disagree, the live document wins. ## Client code diff --git a/docs/sessions/create.mdx b/docs/sessions/create.mdx index c10ec87..32ef044 100644 --- a/docs/sessions/create.mdx +++ b/docs/sessions/create.mdx @@ -67,7 +67,7 @@ session = r.json() } ``` -The call waits until Chrome is reachable, so `cdpUrl` is normally set in the response and `status` is `active`; in our runs that took two to three seconds. It gives up after 20 seconds (up to three minutes on some machines) and answers `504`, so allow your HTTP client at least that long. The browser opens on `about:blank` (or the `url` you passed) with one context and one tab. If you ever see `starting`, poll [`GET /v1/browser/session`](/docs/sessions/lifecycle#get-a-session) until `cdpUrl` appears. Session ids are 128 lowercase letters and digits; treat `cdpUrl` as a secret, since anyone holding it can drive the browser. +The call waits until Chrome is reachable, so `cdpUrl` is normally set in the response and `status` is `active`; in our runs that took two to three seconds. It gives up after 20 seconds on most machines and after three minutes on the newest hosted machines, then answers `504`; give the create call a client timeout of at least three minutes (other calls answer within seconds). The browser opens on `about:blank` (or the `url` you passed) with one context and one tab. If you ever see `starting`, poll [`GET /v1/browser/session`](/docs/sessions/lifecycle#get-a-session) until `cdpUrl` appears. Session ids are 128 lowercase letters and digits; treat `cdpUrl` as a secret, since anyone holding it can drive the browser. ## Options diff --git a/docs/sessions/errors.mdx b/docs/sessions/errors.mdx index eb6a989..4c486d5 100644 --- a/docs/sessions/errors.mdx +++ b/docs/sessions/errors.mdx @@ -25,6 +25,7 @@ The bodies you will see most: `401` `Unauthorized: Missing or invalid Authorizat | `403` | Session or account limit reached: the workspace's concurrent-browser limit (`plan.concurrent_browsers` on `GET /v1/account/billing`; sessions that are `starting` count, and a stop frees the slot as soon as it returns), or a feature (extensions) the account is not enabled for. | Only after a session ends. Never in a loop. | | `404` | Session, extension or account not found (unknown id, or one belonging to another workspace). Deleting an unknown profile is not a 404: it answers `{ "success": true }`. | No. | | `429` | Too many requests. The API itself does not rate-limit today; a `429` comes from the edge in front of it. | Yes: a few attempts, a couple of seconds apart and growing. | +| `502` | The edge could not reach the API. Not JSON. | Once, after a couple of seconds. | | `500` | Something failed on Driver's side. | Once. | | `503` | No browser capacity right now. The response carries `Retry-After: 2` and `"code": "browser_capacity_unavailable"`. Or the session service is unavailable. | Yes: back off with jitter, cap the attempts, then tell the user. | | `504` | A machine was selected but Chrome did not become reachable before the startup deadline: 20 seconds, up to three minutes on some machines. | Once. | diff --git a/docs/sessions/lifecycle.mdx b/docs/sessions/lifecycle.mdx index 4e66b7e..b979a51 100644 --- a/docs/sessions/lifecycle.mdx +++ b/docs/sessions/lifecycle.mdx @@ -34,7 +34,7 @@ session = requests.get( ## List sessions -Paginated, newest first, with an optional `status` filter. `page` defaults to 1 and `pageSize` to 20 (at most 100; larger values are clamped, not rejected). A `status` outside `starting`, `active`, `completed`, `error` is a `400`. +Paginated, newest first, with an optional `status` filter that matches exactly one status (`starting` sessions are not in `?status=active`). `page` defaults to 1 and `pageSize` to 20 (at most 100; larger values are clamped, not rejected). A `status` outside `starting`, `active`, `completed`, `error` is a `400`. ```bash cURL diff --git a/scripts/sync-openapi.mjs b/scripts/sync-openapi.mjs index 9fa2b6b..8ba8bd4 100644 --- a/scripts/sync-openapi.mjs +++ b/scripts/sync-openapi.mjs @@ -84,6 +84,32 @@ if (create?.patch && !create.patch.responses['400']) { content: { 'application/json': { schema: { type: 'object', properties: { error: { type: 'string' } }, required: ['error'] } } }, } } +// What the prose promises but the generator leaves out, so generated clients see it too. +const errorSchema = { type: 'object', properties: { error: { type: 'string' } }, required: ['error'] } +const list = doc.paths['/v1/browser/sessions']?.get +if (list) { + for (const p of list.parameters || []) { + if (p.name === 'page') { p.schema = { ...p.schema, default: 1 }; delete p.schema.exclusiveMinimum; p.schema.minimum = 1 } + if (p.name === 'pageSize') { p.schema = { ...p.schema, default: 20, maximum: 100 }; delete p.schema.exclusiveMinimum; p.schema.minimum = 1; p.description = 'The number of sessions per page, 1 to 100. Larger values are clamped to 100.' } + if (p.name === 'status') p.description = 'Only sessions in exactly this status.' + } + if (!list.responses['400']) list.responses['400'] = { description: 'The status filter is not one of starting, active, completed, error.', content: { 'application/json': { schema: errorSchema } } } + const item = list.responses['200']?.content?.['application/json']?.schema?.properties?.sessions?.items + if (item?.properties?.bandwidthBytes && !item.required.includes('bandwidthBytes')) item.required.push('bandwidthBytes') +} +const getSession = create?.get?.responses?.['200']?.content?.['application/json']?.schema +if (getSession?.properties?.bandwidthBytes) { + if (!getSession.required.includes('bandwidthBytes')) getSession.required.push('bandwidthBytes') + getSession.properties.bandwidthBytes.description = 'Bandwidth used by the session in bytes: 0 while it runs, the metered total once it has ended (final by the time a stop call returns).' +} +const walk = (node, fn) => { if (Array.isArray(node)) node.forEach((n) => walk(n, fn)); else if (node && typeof node === 'object') { fn(node); Object.values(node).forEach((v) => walk(v, fn)) } } +walk(doc, (n) => { if (n.properties?.sessionId?.type === 'string' && !n.properties.sessionId.pattern) { n.properties.sessionId.pattern = '^[a-z0-9]{128}$'; n.properties.sessionId.description = 'The session id: 128 lowercase letters and digits.' } }) +const create503 = create?.post?.responses?.['503'] +if (create503) { + create503.headers = { 'Retry-After': { description: 'Seconds to wait before trying again (2).', schema: { type: 'integer' } } } + const code = create503.content?.['application/json']?.schema?.properties?.code + if (code) { code.enum = ['browser_capacity_unavailable']; code.description = 'Machine-readable cause; present when no browser capacity is available.' } +} if (doc.components?.securitySchemes?.Bearer) doc.components.securitySchemes.Bearer.description = 'A workspace API key from Settings → API keys in the dashboard (https://app.driver.dev), sent as a bearer token.' doc.externalDocs = { url: 'https://docs.driver.dev', description: 'Driver documentation' } if (doc.info) doc.info.contact = { name: 'Driver support', email: 'support@driver.dev' } From 5766836f852d57315d40312223838c4d72d3ba21 Mon Sep 17 00:00:00 2001 From: browsercash Date: Sat, 19 Sep 2026 18:32:13 -0400 Subject: [PATCH 06/11] Docs v2: live round (preview branch) Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01CnA3RKAvP4gwSd9qrwBFCg --- api-reference/openapi.json | 14 ++++++------ api-reference/overview.mdx | 2 +- docs/account/billing.mdx | 2 +- docs/options/location.mdx | 2 ++ docs/options/profiles.mdx | 4 +++- docs/sessions/connect.mdx | 2 +- docs/sessions/create.mdx | 6 +++--- docs/sessions/errors.mdx | 7 +++--- docs/sessions/lifecycle.mdx | 6 +++--- docs/sessions/live-view.mdx | 2 +- docs/start/for-agents.mdx | 43 +++++++++++++++++++++++++++---------- docs/start/index.mdx | 2 +- scripts/sync-openapi.mjs | 7 +++--- 13 files changed, 62 insertions(+), 37 deletions(-) diff --git a/api-reference/openapi.json b/api-reference/openapi.json index f3b2ece..b9f8102 100644 --- a/api-reference/openapi.json +++ b/api-reference/openapi.json @@ -399,8 +399,7 @@ "properties": { "sessionId": { "type": "string", - "description": "The session id: 128 lowercase letters and digits.", - "pattern": "^[a-z0-9]{128}$" + "description": "The session id: an opaque string; do not validate its format." }, "status": { "type": "string", @@ -662,8 +661,7 @@ "properties": { "sessionId": { "type": "string", - "description": "The session id: 128 lowercase letters and digits.", - "pattern": "^[a-z0-9]{128}$" + "description": "The session id: an opaque string; do not validate its format." }, "status": { "type": "string", @@ -702,7 +700,8 @@ "bandwidthBytes": { "type": "integer", "minimum": 0, - "description": "Bandwidth used by the session in bytes: 0 while it runs, the metered total once it has ended (final by the time a stop call returns)." + "description": "Bandwidth used by the session in bytes: null while it runs, the metered total once it has ended (final by the time a stop call returns).", + "nullable": true } }, "required": [ @@ -1004,7 +1003,7 @@ } }, "400": { - "description": "The session has ended (\"Cannot update a completed or errored session\") or the note is longer than 256 characters.", + "description": "The session has ended (\"Cannot update a terminal session\") or the note is longer than 256 characters.", "content": { "application/json": { "schema": { @@ -1191,8 +1190,7 @@ "properties": { "sessionId": { "type": "string", - "description": "The session id: 128 lowercase letters and digits.", - "pattern": "^[a-z0-9]{128}$" + "description": "The session id: an opaque string; do not validate its format." }, "status": { "type": "string", diff --git a/api-reference/overview.mdx b/api-reference/overview.mdx index 90c4a9e..ebd64db 100644 --- a/api-reference/overview.mdx +++ b/api-reference/overview.mdx @@ -23,7 +23,7 @@ Authorization: Bearer $DRIVER_API_KEY - Identifiers that select a resource go in the query string (`?sessionId=…`, `?profileName=…`), not the path, except for extensions. - Errors are `{ "error": "message" }` with a meaningful HTTP status; the create call's `503` also carries a `code`. See [Errors](/docs/sessions/errors). - Session timestamps are ISO 8601 strings; billing periods use Unix epoch milliseconds (fields ending in `_ms`, and the subscription period bounds). Money is in cents (and millicents where exactness matters); bandwidth is bytes on sessions and megabytes on billing. -- Unknown fields in a request body are rejected with `400`. +- Unknown fields in the create body are rejected with `400`; the update call ignores them. - Create answers `200` with the session; only the extension upload answers `201`. ## Endpoints diff --git a/docs/account/billing.mdx b/docs/account/billing.mdx index e91583c..8bf6070 100644 --- a/docs/account/billing.mdx +++ b/docs/account/billing.mdx @@ -27,7 +27,7 @@ balance = balance["balance"] { "balance": { "balance_cents": 1250, "network_mb_remaining": 500, "status": "active" } } ``` -`balance_cents` can go below zero. `network_mb_remaining` (the included allowance still unused this period) and `status` are present for workspace accounts and absent on a legacy account. Sessions are refused with `402` once the balance is negative and the allowance is gone. +`balance_cents` can go below zero. `network_mb_remaining` (the included allowance still unused this period) and `status` (the plan key, for example `free`) are present for workspace accounts and absent on a legacy account. Sessions are refused with `402` once the balance is negative and the allowance is gone. ## Billing details diff --git a/docs/options/location.mdx b/docs/options/location.mdx index d4ccfcd..95994ef 100644 --- a/docs/options/location.mdx +++ b/docs/options/location.mdx @@ -29,6 +29,8 @@ Set `timezone` (IANA name) or `language` (BCP-47) when the default choice for th US sessions always use `en-US` (any other `language` is rejected). For other countries, Driver picks the country's primary language when you leave `language` out. Chrome reports the nearest variant it ships: a session created with `fr-CA` reported `navigator.language` as `fr-FR` with `fr` and `en-US` as fallbacks. +The browser's `Accept-Language` header follows the same choice (a `de-DE` session sends `de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7`), so servers and scripts see one story. + Hosted sessions also provision matching text-to-speech voices where available, and disable Web Speech when a coherent voice is not, so that surface never contradicts the rest. ## With your own proxy diff --git a/docs/options/profiles.mdx b/docs/options/profiles.mdx index 0eabd48..9429b33 100644 --- a/docs/options/profiles.mdx +++ b/docs/options/profiles.mdx @@ -57,7 +57,7 @@ requests.delete( ``` -Deleting is idempotent: an unknown name also answers `{ "success": true }`. The dashboard's **Profiles** page shows the same list, and lets you launch a session on a profile to sign in by hand through the live view. +Deleting is idempotent: an unknown name also answers `{ "success": true }`. The list is not paginated and returns every profile in the workspace, so delete the ones you no longer need. The dashboard's **Profiles** page shows the same list, and lets you launch a session on a profile to sign in by hand through the live view. ## Practical notes @@ -65,3 +65,5 @@ Deleting is idempotent: an unknown name also answers `{ "success": true }`. The - The profile is written as the session ends. Before starting the next session on the same name, wait until `GET /v1/browser/session` reports the previous one as `completed`. - Keep the same `country` for a profile across runs. A profile that signed in from Germany and returns from Brazil looks like a stolen session to the site. - Profiles hold everything a browser profile holds: cookies, local storage, IndexedDB, service workers. Delete a profile when the account it belongs to is no longer needed. +- As in desktop Chrome, cookies without an expiry (session cookies) are dropped when the browser closes and do not carry over. Sites that keep you signed in set persistent cookies, which do; a login that only sets a session cookie has to be repeated each run. +- The next session on the profile can run on a different machine; the state travels with the name. diff --git a/docs/sessions/connect.mdx b/docs/sessions/connect.mdx index 670ce0d..dbedafe 100644 --- a/docs/sessions/connect.mdx +++ b/docs/sessions/connect.mdx @@ -66,7 +66,7 @@ Closing your client (`browser.close()` in Patchright and Playwright, `browser.di If your process crashes, fetch the session with [`GET /v1/browser/session`](/docs/sessions/lifecycle#get-a-session): while `status` is `active` the `cdpUrl` is still valid and you can attach again. Profiles with `persist: true` also carry state into a brand-new session if the old one has ended. -Several clients can be attached at once (your script and the [live view](/docs/sessions/live-view), for instance). Two scripts driving the same tab will fight each other, so keep it to one automation client per session. `cdpUrl` is a credential: anyone who has it can drive the browser while the session is active. +Several clients can be attached at once (your script and the [live view](/docs/sessions/live-view), for instance). Two scripts driving the same tab will fight each other, so keep it to one automation client per session. `cdpUrl` is a credential: anyone who has it can drive the browser while the session is active. Its hostname is a relay near the machine, not the session's network; the egress country is the one you asked for regardless of the host you connect to. ## Timeouts and waits diff --git a/docs/sessions/create.mdx b/docs/sessions/create.mdx index 32ef044..da47ee3 100644 --- a/docs/sessions/create.mdx +++ b/docs/sessions/create.mdx @@ -57,7 +57,7 @@ session = r.json() ```json { - "sessionId": "k3f9…128 lowercase letters and digits…q7z2", + "sessionId": "0b1f5c1e6d3a4f7c9e2b8d4a6c1e3f5a", "status": "active", "servedBy": "adapt-ensure-swim", "createdAt": "2026-09-17T14:02:11.000Z", @@ -67,7 +67,7 @@ session = r.json() } ``` -The call waits until Chrome is reachable, so `cdpUrl` is normally set in the response and `status` is `active`; in our runs that took two to three seconds. It gives up after 20 seconds on most machines and after three minutes on the newest hosted machines, then answers `504`; give the create call a client timeout of at least three minutes (other calls answer within seconds). The browser opens on `about:blank` (or the `url` you passed) with one context and one tab. If you ever see `starting`, poll [`GET /v1/browser/session`](/docs/sessions/lifecycle#get-a-session) until `cdpUrl` appears. Session ids are 128 lowercase letters and digits; treat `cdpUrl` as a secret, since anyone holding it can drive the browser. +The call waits until Chrome is reachable, so `cdpUrl` is normally set in the response and `status` is `active`; in our runs that took two to three seconds. It gives up after 20 seconds on most machines and after three minutes on the newest hosted machines, then answers `504`; give the create call a client timeout of at least three minutes (other calls answer within seconds). The browser opens on `about:blank` (or the `url` you passed) with one context and one tab. If you ever see `starting`, poll [`GET /v1/browser/session`](/docs/sessions/lifecycle#get-a-session) until `cdpUrl` appears. Session ids are opaque strings (32 hexadecimal characters today); do not validate their format. Treat `cdpUrl` as a secret, since anyone holding it can drive the browser. ## Options @@ -90,7 +90,7 @@ The call waits until Chrome is reachable, so `cdpUrl` is normally set in the res | `browserCheck` | boolean | `false` | Run a browser validation check during creation. | | `note` | string | | Up to 256 characters, shown in the dashboard. | -Unknown fields are rejected with `400`. Options that contradict each other are rejected with `400` and a message naming the field. The common ones: a `timezone` or `language` not listed for the `country` (`Unsupported timezone "Europe/Berlin" for country US.`), a `country` that does not match a supplied proxy's exit, a proxy that cannot be reached, and out-of-range `duration`, `windowSize` or `note`. +Unknown fields are rejected with `400` (`window_size is not supported by this endpoint`). Out-of-range values name the field and the bound (`duration: Too small: expected number to be >=60`, `note: Too big: expected string to have <=256 characters`). Options that contradict each other are rejected with `400` and a message naming the field. The common ones: a `timezone` or `language` not listed for the `country` (`Unsupported timezone "Europe/Berlin" for country US.`), a `country` that does not match a supplied proxy's exit, a proxy that cannot be reached, and out-of-range `duration`, `windowSize` or `note`. ## Errors diff --git a/docs/sessions/errors.mdx b/docs/sessions/errors.mdx index 4c486d5..809c782 100644 --- a/docs/sessions/errors.mdx +++ b/docs/sessions/errors.mdx @@ -10,7 +10,7 @@ Errors from the API are JSON with an `error` message; one carries a machine-read ``` ```json -{ "error": "No browser capacity is available right now", "code": "browser_capacity_unavailable" } +{ "error": "No eligible node found", "code": "browser_capacity_unavailable" } ``` The bodies you will see most: `401` `Unauthorized: Missing or invalid Authorization header` or `Unauthorized: Invalid token`; `402` `Insufficient balance`; `403` `Session limit reached` (concurrency) or `Extensions API is not enabled for this account`; `400` names the field, for example `Unsupported timezone "Europe/Berlin" for country US.` or `windowSize must be WIDTHxHEIGHT with width 320–16384 and height 200–16384`. Unknown fields in a body are rejected with `400` too. @@ -19,7 +19,7 @@ The bodies you will see most: `401` `Unauthorized: Missing or invalid Authorizat | Status | Meaning | Retry? | |---|---|---| -| `400` | Invalid or incompatible options, an unknown field, or an update to a session that has ended. The message names the problem. | No. Fix the request. | +| `400` | Invalid or incompatible options, an unknown field on create, a missing `sessionId`, or an update to a session that has ended. The message names the problem. | No. Fix the request. | | `401` | Missing or invalid API key. | No. Check `Authorization: Bearer …`. | | `402` | The workspace is out of credits. | No. Add credits in the dashboard. | | `403` | Session or account limit reached: the workspace's concurrent-browser limit (`plan.concurrent_browsers` on `GET /v1/account/billing`; sessions that are `starting` count, and a stop frees the slot as soon as it returns), or a feature (extensions) the account is not enabled for. | Only after a session ends. Never in a loop. | @@ -45,4 +45,5 @@ Some failures show up in your framework rather than as an HTTP status: |---|---| | `Target closed` / `Browser closed` mid-run | The session reached its `duration`, or was stopped from the dashboard. Check `GET /v1/browser/session`. | | Connection refused on `cdpUrl` | The session is not `active` yet or has ended. | -| The site shows a challenge or block page | Framework fingerprint. Switch to [Patchright](/docs/frameworks/patchright) and remove CDP hooks; see [Best practices](/docs/start/best-practices). | +| The site shows a challenge or block page | Framework fingerprint or automation tactics. Switch to [Patchright](/docs/frameworks/patchright) and remove CDP hooks; then escalate: `type: "hosted_stealth"`, `"hosted_privacy"`, `captchaSolver: true`, and finally a person through the [live view](/docs/sessions/live-view). | +| `net::ERR_SOCKS_CONNECTION_FAILED` on every navigation | The session's network egress is not reachable. Stop the session and create another, in a different country if it persists; report it if it does. | diff --git a/docs/sessions/lifecycle.mdx b/docs/sessions/lifecycle.mdx index b979a51..f325392 100644 --- a/docs/sessions/lifecycle.mdx +++ b/docs/sessions/lifecycle.mdx @@ -14,7 +14,7 @@ A session moves through `starting` → `active` → `completed`, or `error` if t ## Get a session -Returns the same shape as create, plus `bandwidthBytes`: `0` while the session runs, the metered total once it has ended. The total is final by the time a stop call returns. +Returns the same shape as create, plus `bandwidthBytes`: `null` while the session runs, the metered total once it has ended (final by the time a stop call returns; `0` for a session that moved no bytes). `cdpUrl` is `null` once the session has ended. ```bash cURL @@ -54,7 +54,7 @@ data = requests.get( ``` -The response carries `sessions`, `total`, `totalPages`, `page` and `pageSize`. Each item has `sessionId`, `status`, `servedBy`, `type`, `country`, `createdAt`, `stoppedAt`, `bandwidthBytes` and `note`; `type` and `country` are the only place the API reports them, since the single-session calls do not. +The response carries `sessions`, `total`, `totalPages`, `page` and `pageSize`. Each item has `sessionId`, `status`, `servedBy`, `type`, `country`, `createdAt`, `stoppedAt`, `bandwidthBytes` and `note`; the list is the only place the API reports `country` (`type` is `null` for sessions created with the default type), since the single-session calls do not. ## Label a session @@ -84,7 +84,7 @@ requests.patch( ``` -Returns `{ "success": true }`. Notes are at most 256 characters (longer is a `400`). Send an empty string to clear one. A session that has ended cannot be updated (`400`, `Cannot update a completed or errored session`). +Returns `{ "success": true }`. Notes are at most 256 characters (longer is a `400`). Send an empty string to clear one. A session that has ended cannot be updated (`400`, `Cannot update a terminal session`). ## Stop a session diff --git a/docs/sessions/live-view.mdx b/docs/sessions/live-view.mdx index e7a8733..2ad6078 100644 --- a/docs/sessions/live-view.mdx +++ b/docs/sessions/live-view.mdx @@ -17,7 +17,7 @@ Build a viewer URL from `cdpUrl` and open it in any browser: https://viewer.driver.dev?ws= ``` -Paste `cdpUrl` in as it is; no encoding is needed. +Paste `cdpUrl` in as it is; no encoding is needed: the viewer reads everything after `ws=`. Print the URL as soon as the session exists, before the automation starts, so the person has time to open it; it shows the tab your script is driving. ```typescript TypeScript diff --git a/docs/start/for-agents.mdx b/docs/start/for-agents.mdx index 9f83ea7..31d263b 100644 --- a/docs/start/for-agents.mdx +++ b/docs/start/for-agents.mdx @@ -13,9 +13,10 @@ This page is written for an AI coding agent (Claude Code, Cursor, Codex, or simi 4. **Do not install broad CDP hooks.** Avoid `page.route`, `context.route`, `page.on('request')`, `page.exposeFunction`, `page.addInitScript` and `context.addInitScript` unless the task cannot be done otherwise, and then scope them as narrowly as possible. They change how the browser behaves and are detectable. 5. **Do not patch fingerprints.** The browser is real Chrome on real hardware. Overriding `navigator`, the user agent, WebGL, canvas, timezone or locale makes it look fake. Set `country`, `timezone` and `language` on the create call instead. 6. **Always stop the session.** Wrap the work in `try / finally` and call `DELETE /v1/browser/session?sessionId=…` in `finally`. Sessions are metered by bandwidth and end on their own at `duration` (one hour by default); a session left running holds one of the workspace's concurrency slots and keeps its `cdpUrl` live until then. Closing your client (`browser.close()`) does not stop the session. -7. **Keep the `sessionId`.** Log it with the run. Every session has a record in the dashboard under Telemetry → Sessions with node, network, timing and cost. +7. **Keep the `sessionId`.** Log it with the run. Every session has a record in the dashboard under Telemetry → Sessions with node, network, timing and cost. If the person wants to watch, print `https://viewer.driver.dev?ws=` as soon as the session exists; see [Live view](/docs/sessions/live-view). 8. **Report what a person must fix; retry only what time fixes.** `402` (out of credits) and `403` (concurrency or plan limit): report, never loop. `503` (no capacity; the response carries `Retry-After: 2` and `"code": "browser_capacity_unavailable"`): back off with jitter, cap the attempts, then report. `504` (Chrome did not start in time) and `500`: retry once. `429`: back off. The full table is on [Errors](/docs/sessions/errors). 9. **Stay under the workspace's concurrency limit.** `GET /v1/account/billing` reports it as `plan.concurrent_browsers`; sessions beyond it are refused with 403. +10. **When a site challenges or blocks, escalate in this order.** Remove every hook and tactic; then create with `type: "hosted_stealth"`, then `"hosted_privacy"`, then add `captchaSolver: true`; when a step genuinely needs a person, give them the live view URL. See [Browser types](/docs/options/browser-types) and [CAPTCHA solving](/docs/options/captcha-and-adblock). ## How to write the automation @@ -35,23 +36,31 @@ Integrate Driver (driver.dev), hosted real-Chrome browser infrastructure, into t - Docs: https://docs.driver.dev — agents start at https://docs.driver.dev/docs/start/for-agents Endpoints: https://api.driver.dev/scalar (OpenAPI at https://api.driver.dev/doc) - Auth: Authorization: Bearer $DRIVER_API_KEY (read from the environment; never commit it). - JSON bodies with Content-Type: application/json. Ids go in the query string, not the path. + JSON bodies with Content-Type: application/json. Ids go in the query string, not the path: + GET /v1/browser/session?sessionId=, DELETE /v1/browser/session?sessionId=. - Most tasks need no options: POST https://api.driver.dev/v1/browser/session with {} gives a - hosted US Chrome and returns { sessionId, status, cdpUrl }. Use "country" for geo-specific - sites, "profile" {name, persist:true} to keep logins, "duration" (60–3600 s, default 3600) - to cap how long a forgotten session lives. + hosted US Chrome and returns { sessionId, status, cdpUrl }. Use "country" (two-letter code, + for example "DE") for geo-specific sites; "profile" {name, persist:true} to keep logins + (persistent cookies and storage carry over, session-only cookies do not, as in desktop + Chrome); "duration" (60–3600 s, default 3600) to cap how long a forgotten session lives. - Drive the browser only with Patchright (npm install patchright / pip install patchright; no browser download): chromium.connectOverCDP(cdpUrl), then reuse browser.contexts()[0] and context.pages()[0]. Never launch a browser. Never use stock Playwright or Puppeteer. - No CDP or automation tactics that leak or get sites to block: no page.route, no init scripts, no exposed functions, no fingerprint patches. Minimal automation logic is always best: https://docs.driver.dev/docs/start/best-practices +- If a site challenges or blocks: remove every hook and tactic first, then try type + "hosted_stealth", then "hosted_privacy", then captchaSolver: true; when a step needs a person, + hand them the live view. +- Live view: https://viewer.driver.dev?ws= — print it as soon as the session exists so + the user can watch; it works until the session ends. - Check whether the outcome can be reached faster: direct URLs, one session for many pages, a persisted profile instead of logging in. Avoid slow browser logic and fixed sleeps. - Always stop the session in a finally: DELETE /v1/browser/session?sessionId=. browser.close() only disconnects; the session runs until DELETE or its duration. Keep the sessionId in logs. -- 402 (no credits) and 403 (limit): report. 503: back off, cap, report. 504 and 500: retry once. +- 402 (no credits) and 403 (limit; plan.concurrent_browsers on GET /v1/account/billing): + report. 503: back off, cap, report. 504 and 500: retry once. - Test the automation against the real target and verify the result before calling it done: GET /v1/browser/session?sessionId= must show status "completed". ``` @@ -103,7 +112,7 @@ export interface Session { createdAt: string; stoppedAt: string | null; note: string | null; - bandwidthBytes?: number; // on GET: 0 while running, the metered total afterwards + bandwidthBytes?: number | null; // on GET: null while running, the metered total afterwards } /** Every non-2xx answer: `status` is the HTTP status, `code` the machine-readable code when there is one. */ @@ -170,7 +179,7 @@ export async function stopSession(id: string): Promise { } /** Create normally returns an active session; this covers the rare `starting` answer. */ -async function waitForActive(s: Session, timeoutMs = 60_000): Promise { +export async function waitForActive(s: Session, timeoutMs = 60_000): Promise { const deadline = Date.now() + timeoutMs; while (!(s.status === "active" && s.cdpUrl)) { if (s.status === "completed" || s.status === "error") throw new DriverError(500, `session ended: ${s.status}`); @@ -181,10 +190,14 @@ async function waitForActive(s: Session, timeoutMs = 60_000): Promise( opts: CreateOptions, - fn: (page: Page, browser: Browser, session: Session) => Promise, + fn: (page: Page, browser: Browser, session: Session & { cdpUrl: string }) => Promise, ): Promise { const session = await createSession(opts); try { @@ -199,6 +212,9 @@ export async function withBrowser( } } finally { await stopSession(session.sessionId); + const after = await getSession(session.sessionId).catch(() => null); + if (after?.status !== "completed") console.error(`Driver: session ${session.sessionId} is ${after?.status ?? "unknown"} after stop`); + else console.log(`Driver: session ${session.sessionId} completed, ${after.bandwidthBytes ?? 0} bytes`); } } ``` @@ -290,7 +306,8 @@ def wait_for_active(session, timeout=60): @contextmanager def browser_page(**opts): - """with browser_page(country="US") as page: ... — always stops the session.""" + """with browser_page(country="US") as page: ... — always stops the session and verifies the stop. + The session dict is on page.driver_session (id, cdpUrl for the live view: https://viewer.driver.dev?ws=).""" session = create_session(**opts) try: session = wait_for_active(session) @@ -298,12 +315,16 @@ def browser_page(**opts): browser = p.chromium.connect_over_cdp(session["cdpUrl"]) context = browser.contexts[0] if browser.contexts else browser.new_context() page = context.pages[0] if context.pages else context.new_page() + page.driver_session = session try: yield page finally: browser.close() # your connection only finally: stop_session(session["sessionId"]) + after = get_session(session["sessionId"]) + if after.get("status") != "completed": + print(f"Driver: session {session['sessionId']} is {after.get('status')} after stop") ``` diff --git a/docs/start/index.mdx b/docs/start/index.mdx index bb0d702..e120e8a 100644 --- a/docs/start/index.mdx +++ b/docs/start/index.mdx @@ -43,7 +43,7 @@ Driver is hosted browser infrastructure. Every session is a real Chrome window o |---|---| | **Real Chrome** | Consumer Chrome builds on real hardware, not a headless container. Sites see a normal browser. | | **Residential networks** | Sessions egress from residential connections in the country you choose. Bring your own SOCKS5 proxy when you need to. | -| **Profiles** | Save cookies and local state under a name and reuse them, so logins survive between sessions. | +| **Profiles** | Save cookies and local state under a name and reuse them, so persistent logins survive between sessions. | | **CAPTCHA solving** | One flag loads Driver's solver into the session. | | **Live view** | Watch and drive any running session from the dashboard or a viewer URL. | diff --git a/scripts/sync-openapi.mjs b/scripts/sync-openapi.mjs index 8ba8bd4..e9300a2 100644 --- a/scripts/sync-openapi.mjs +++ b/scripts/sync-openapi.mjs @@ -80,7 +80,7 @@ const balanceCents = doc.paths['/v1/account/balance']?.get?.responses?.['200']?. if (balanceCents) { delete balanceCents.minimum; balanceCents.description = `${balanceCents.description || 'Balance in cents.'} Can be negative when usage exceeds prepaid credit.` } if (create?.patch && !create.patch.responses['400']) { create.patch.responses['400'] = { - description: 'The session has ended ("Cannot update a completed or errored session") or the note is longer than 256 characters.', + description: 'The session has ended ("Cannot update a terminal session") or the note is longer than 256 characters.', content: { 'application/json': { schema: { type: 'object', properties: { error: { type: 'string' } }, required: ['error'] } } }, } } @@ -100,10 +100,11 @@ if (list) { const getSession = create?.get?.responses?.['200']?.content?.['application/json']?.schema if (getSession?.properties?.bandwidthBytes) { if (!getSession.required.includes('bandwidthBytes')) getSession.required.push('bandwidthBytes') - getSession.properties.bandwidthBytes.description = 'Bandwidth used by the session in bytes: 0 while it runs, the metered total once it has ended (final by the time a stop call returns).' + getSession.properties.bandwidthBytes.nullable = true + getSession.properties.bandwidthBytes.description = 'Bandwidth used by the session in bytes: null while it runs, the metered total once it has ended (final by the time a stop call returns).' } const walk = (node, fn) => { if (Array.isArray(node)) node.forEach((n) => walk(n, fn)); else if (node && typeof node === 'object') { fn(node); Object.values(node).forEach((v) => walk(v, fn)) } } -walk(doc, (n) => { if (n.properties?.sessionId?.type === 'string' && !n.properties.sessionId.pattern) { n.properties.sessionId.pattern = '^[a-z0-9]{128}$'; n.properties.sessionId.description = 'The session id: 128 lowercase letters and digits.' } }) +walk(doc, (n) => { if (n.properties?.sessionId?.type === 'string') n.properties.sessionId.description = 'The session id: an opaque string; do not validate its format.' }) const create503 = create?.post?.responses?.['503'] if (create503) { create503.headers = { 'Retry-After': { description: 'Seconds to wait before trying again (2).', schema: { type: 'integer' } } } From 68a1da979943ed2fc75326d9a32aa651247a5100 Mon Sep 17 00:00:00 2001 From: browsercash Date: Sun, 20 Sep 2026 12:08:12 -0400 Subject: [PATCH 07/11] Refresh preview from v2: browser pools --- api-reference/openapi.json | 2016 +++++++++++++++++++++++++++++++++ api-reference/overview.mdx | 9 +- docs.json | 15 + docs/account/billing.mdx | 2 +- docs/account/dashboard.mdx | 1 + docs/options/profiles.mdx | 2 +- docs/sessions/create.mdx | 3 +- docs/sessions/errors.mdx | 3 +- docs/sessions/lifecycle.mdx | 2 +- docs/sessions/pools.mdx | 255 +++++ docs/start/best-practices.mdx | 3 +- docs/start/for-agents.mdx | 45 +- docs/start/index.mdx | 2 + docs/start/quickstart.mdx | 1 + scripts/sync-openapi.mjs | 50 + 15 files changed, 2397 insertions(+), 12 deletions(-) create mode 100644 docs/sessions/pools.mdx diff --git a/api-reference/openapi.json b/api-reference/openapi.json index b9f8102..7e9fb70 100644 --- a/api-reference/openapi.json +++ b/api-reference/openapi.json @@ -2406,6 +2406,2019 @@ ], "summary": "Delete an extension" } + }, + "/v1/browser/pools": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Create a browser pool and begin warming ordinary browser sessions.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "description": "Unique in the workspace, 1 to 64 characters." + }, + "size": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": true, + "description": "Browsers to keep ready. All pools together may keep 30 warm per account." + }, + "browser": { + "type": "object", + "properties": { + "nodeId": { + "type": "string" + }, + "country": { + "type": "string", + "minLength": 2, + "maxLength": 2 + }, + "proxyUrl": { + "type": "string", + "format": "uri" + }, + "windowSize": { + "type": "string", + "pattern": "^\\d+x\\d+$/i" + }, + "displaySize": { + "type": "string", + "pattern": "^\\d+x\\d+$/i" + }, + "url": { + "type": "string", + "format": "uri" + }, + "timezone": { + "type": "string" + }, + "language": { + "type": "string" + }, + "profile": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "persist": { + "type": "boolean" + } + }, + "required": [ + "name" + ], + "additionalProperties": false + }, + "adblock": { + "type": "boolean" + }, + "captchaSolver": { + "type": "boolean" + }, + "extensionIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "note": { + "type": "string", + "maxLength": 256 + }, + "gpu": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "default": {}, + "additionalProperties": false, + "description": "The create options every browser in the pool starts with (same fields as POST /v1/browser/session, without type, duration and browserCheck). Values are not validated on create. As of 2026-09-20 country and proxyUrl are not applied to warm browsers (they egress from the default US network); url and note are." + }, + "leaseTimeoutSeconds": { + "type": "integer", + "minimum": 30, + "maximum": 86400, + "description": "How long an acquired browser may be held, in seconds (30 to 86400, default 900). The session is stopped when the lease expires." + }, + "maxReadyAgeSeconds": { + "type": "integer", + "minimum": 30, + "maximum": 86400, + "description": "How long a browser may wait ready before it is replaced with a fresh one, in seconds (30 to 86400, default 3600)." + } + }, + "required": [ + "name", + "size" + ], + "additionalProperties": false + } + } + } + }, + "responses": { + "201": { + "description": "Pool created", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "minLength": 6, + "maxLength": 80, + "example": "pool_8ca73065e49d44f3a7825743745b2948" + }, + "name": { + "type": "string" + }, + "size": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": true + }, + "status": { + "type": "string", + "enum": [ + "warming", + "ready", + "degraded", + "paused", + "deleting" + ] + }, + "ready": { + "type": "integer", + "minimum": 0 + }, + "starting": { + "type": "integer", + "minimum": 0 + }, + "leased": { + "type": "integer", + "minimum": 0 + }, + "browser": { + "type": "object", + "properties": { + "nodeId": { + "type": "string" + }, + "country": { + "type": "string", + "minLength": 2, + "maxLength": 2 + }, + "proxyUrl": { + "type": "string", + "format": "uri" + }, + "windowSize": { + "type": "string", + "pattern": "^\\d+x\\d+$/i" + }, + "displaySize": { + "type": "string", + "pattern": "^\\d+x\\d+$/i" + }, + "url": { + "type": "string", + "format": "uri" + }, + "timezone": { + "type": "string" + }, + "language": { + "type": "string" + }, + "profile": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "persist": { + "type": "boolean" + } + }, + "required": [ + "name" + ], + "additionalProperties": false + }, + "adblock": { + "type": "boolean" + }, + "captchaSolver": { + "type": "boolean" + }, + "extensionIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "note": { + "type": "string", + "maxLength": 256 + }, + "gpu": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + "leaseTimeoutSeconds": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": true + }, + "maxReadyAgeSeconds": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": true + }, + "lastError": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "size", + "status", + "ready", + "starting", + "leased", + "browser", + "leaseTimeoutSeconds", + "maxReadyAgeSeconds", + "lastError", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "Invalid request", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "Pool not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "A pool with that name already exists, or the size would exceed the warm-browser cap of the account (\"Pool capacity 32 exceeds your pool limit of 30\").", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "503": { + "description": "Browser service unavailable", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "tags": [ + "Pools" + ], + "summary": "Create a pool" + }, + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "List browser pools.", + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "pools": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "minLength": 6, + "maxLength": 80, + "example": "pool_8ca73065e49d44f3a7825743745b2948" + }, + "name": { + "type": "string" + }, + "size": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": true + }, + "status": { + "type": "string", + "enum": [ + "warming", + "ready", + "degraded", + "paused", + "deleting" + ] + }, + "ready": { + "type": "integer", + "minimum": 0 + }, + "starting": { + "type": "integer", + "minimum": 0 + }, + "leased": { + "type": "integer", + "minimum": 0 + }, + "browser": { + "type": "object", + "properties": { + "nodeId": { + "type": "string" + }, + "country": { + "type": "string", + "minLength": 2, + "maxLength": 2 + }, + "proxyUrl": { + "type": "string", + "format": "uri" + }, + "windowSize": { + "type": "string", + "pattern": "^\\d+x\\d+$/i" + }, + "displaySize": { + "type": "string", + "pattern": "^\\d+x\\d+$/i" + }, + "url": { + "type": "string", + "format": "uri" + }, + "timezone": { + "type": "string" + }, + "language": { + "type": "string" + }, + "profile": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "persist": { + "type": "boolean" + } + }, + "required": [ + "name" + ], + "additionalProperties": false + }, + "adblock": { + "type": "boolean" + }, + "captchaSolver": { + "type": "boolean" + }, + "extensionIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "note": { + "type": "string", + "maxLength": 256 + }, + "gpu": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + "leaseTimeoutSeconds": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": true + }, + "maxReadyAgeSeconds": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": true + }, + "lastError": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "size", + "status", + "ready", + "starting", + "leased", + "browser", + "leaseTimeoutSeconds", + "maxReadyAgeSeconds", + "lastError", + "createdAt", + "updatedAt" + ] + } + } + }, + "required": [ + "pools" + ] + } + } + } + }, + "400": { + "description": "Invalid request", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "Pool not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "Pool state conflict", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "503": { + "description": "Browser service unavailable", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "tags": [ + "Pools" + ], + "summary": "List pools" + } + }, + "/v1/browser/pools/{poolId}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Get browser pool state.", + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 6, + "maxLength": 80, + "example": "pool_8ca73065e49d44f3a7825743745b2948" + }, + "required": true, + "name": "poolId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "minLength": 6, + "maxLength": 80, + "example": "pool_8ca73065e49d44f3a7825743745b2948" + }, + "name": { + "type": "string" + }, + "size": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": true + }, + "status": { + "type": "string", + "enum": [ + "warming", + "ready", + "degraded", + "paused", + "deleting" + ] + }, + "ready": { + "type": "integer", + "minimum": 0 + }, + "starting": { + "type": "integer", + "minimum": 0 + }, + "leased": { + "type": "integer", + "minimum": 0 + }, + "browser": { + "type": "object", + "properties": { + "nodeId": { + "type": "string" + }, + "country": { + "type": "string", + "minLength": 2, + "maxLength": 2 + }, + "proxyUrl": { + "type": "string", + "format": "uri" + }, + "windowSize": { + "type": "string", + "pattern": "^\\d+x\\d+$/i" + }, + "displaySize": { + "type": "string", + "pattern": "^\\d+x\\d+$/i" + }, + "url": { + "type": "string", + "format": "uri" + }, + "timezone": { + "type": "string" + }, + "language": { + "type": "string" + }, + "profile": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "persist": { + "type": "boolean" + } + }, + "required": [ + "name" + ], + "additionalProperties": false + }, + "adblock": { + "type": "boolean" + }, + "captchaSolver": { + "type": "boolean" + }, + "extensionIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "note": { + "type": "string", + "maxLength": 256 + }, + "gpu": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + "leaseTimeoutSeconds": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": true + }, + "maxReadyAgeSeconds": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": true + }, + "lastError": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "size", + "status", + "ready", + "starting", + "leased", + "browser", + "leaseTimeoutSeconds", + "maxReadyAgeSeconds", + "lastError", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "Invalid request", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "Pool not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "Pool state conflict", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "503": { + "description": "Browser service unavailable", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "tags": [ + "Pools" + ], + "summary": "Get a pool" + }, + "patch": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Resize, pause, resume, or reconfigure a browser pool.", + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 6, + "maxLength": 80, + "example": "pool_8ca73065e49d44f3a7825743745b2948" + }, + "required": true, + "name": "poolId", + "in": "path" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "size": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": true + }, + "browser": { + "type": "object", + "properties": { + "nodeId": { + "type": "string" + }, + "country": { + "type": "string", + "minLength": 2, + "maxLength": 2 + }, + "proxyUrl": { + "type": "string", + "format": "uri" + }, + "windowSize": { + "type": "string", + "pattern": "^\\d+x\\d+$/i" + }, + "displaySize": { + "type": "string", + "pattern": "^\\d+x\\d+$/i" + }, + "url": { + "type": "string", + "format": "uri" + }, + "timezone": { + "type": "string" + }, + "language": { + "type": "string" + }, + "profile": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "persist": { + "type": "boolean" + } + }, + "required": [ + "name" + ], + "additionalProperties": false + }, + "adblock": { + "type": "boolean" + }, + "captchaSolver": { + "type": "boolean" + }, + "extensionIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "note": { + "type": "string", + "maxLength": 256 + }, + "gpu": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "default": {}, + "additionalProperties": false, + "description": "Replaces the whole browser template; include every option to keep. Changing it restarts the warm browsers." + }, + "leaseTimeoutSeconds": { + "type": "integer", + "minimum": 30, + "maximum": 86400 + }, + "maxReadyAgeSeconds": { + "type": "integer", + "minimum": 30, + "maximum": 86400 + }, + "paused": { + "type": "boolean", + "description": "true stops the warm browsers and refuses acquires; false warms them again." + } + }, + "additionalProperties": false + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "minLength": 6, + "maxLength": 80, + "example": "pool_8ca73065e49d44f3a7825743745b2948" + }, + "name": { + "type": "string" + }, + "size": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": true + }, + "status": { + "type": "string", + "enum": [ + "warming", + "ready", + "degraded", + "paused", + "deleting" + ] + }, + "ready": { + "type": "integer", + "minimum": 0 + }, + "starting": { + "type": "integer", + "minimum": 0 + }, + "leased": { + "type": "integer", + "minimum": 0 + }, + "browser": { + "type": "object", + "properties": { + "nodeId": { + "type": "string" + }, + "country": { + "type": "string", + "minLength": 2, + "maxLength": 2 + }, + "proxyUrl": { + "type": "string", + "format": "uri" + }, + "windowSize": { + "type": "string", + "pattern": "^\\d+x\\d+$/i" + }, + "displaySize": { + "type": "string", + "pattern": "^\\d+x\\d+$/i" + }, + "url": { + "type": "string", + "format": "uri" + }, + "timezone": { + "type": "string" + }, + "language": { + "type": "string" + }, + "profile": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "persist": { + "type": "boolean" + } + }, + "required": [ + "name" + ], + "additionalProperties": false + }, + "adblock": { + "type": "boolean" + }, + "captchaSolver": { + "type": "boolean" + }, + "extensionIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "note": { + "type": "string", + "maxLength": 256 + }, + "gpu": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + "leaseTimeoutSeconds": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": true + }, + "maxReadyAgeSeconds": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": true + }, + "lastError": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "size", + "status", + "ready", + "starting", + "leased", + "browser", + "leaseTimeoutSeconds", + "maxReadyAgeSeconds", + "lastError", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "Invalid request", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "Pool not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "Pool state conflict", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "503": { + "description": "Browser service unavailable", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "tags": [ + "Pools" + ], + "summary": "Update a pool" + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Delete a pool. Active leases require force=true.", + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 6, + "maxLength": 80, + "example": "pool_8ca73065e49d44f3a7825743745b2948" + }, + "required": true, + "name": "poolId", + "in": "path" + }, + { + "schema": { + "type": "string", + "enum": [ + "true", + "false" + ] + }, + "required": false, + "name": "force", + "in": "query", + "description": "Pass true to stop leased browsers too. Without it a pool with active leases answers 409 (\"Pool has active leases; pass force=true to stop them\")." + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + } + }, + "required": [ + "success" + ] + } + } + } + }, + "400": { + "description": "Invalid request", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "Pool not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "The pool has active leases and force was not passed.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "503": { + "description": "Browser service unavailable", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "tags": [ + "Pools" + ], + "summary": "Delete a pool" + } + }, + "/v1/browser/pools/{poolId}/acquire": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Take a ready browser. From here on the session belongs to the workspace like any other; the pool warms a replacement. Its createdAt is when the browser was warmed.", + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 6, + "maxLength": 80, + "example": "pool_8ca73065e49d44f3a7825743745b2948" + }, + "required": true, + "name": "poolId", + "in": "path" + } + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "waitMs": { + "type": "integer", + "minimum": 0, + "maximum": 30000, + "default": 0, + "description": "How long to wait for a browser to become ready when none is, in milliseconds (0 to 30000). Send {} for no wait; the body is required." + } + }, + "additionalProperties": false + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "poolId": { + "type": "string", + "minLength": 6, + "maxLength": 80, + "example": "pool_8ca73065e49d44f3a7825743745b2948" + }, + "leaseId": { + "type": "string" + }, + "acquiredAt": { + "type": "string" + }, + "expiresAt": { + "type": "string" + }, + "session": { + "type": "object", + "properties": { + "sessionId": { + "type": "string", + "description": "The session id: an opaque string; do not validate its format." + }, + "status": { + "type": "string" + }, + "servedBy": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "stoppedAt": { + "type": "string", + "nullable": true + }, + "cdpUrl": { + "type": "string", + "nullable": true + }, + "note": { + "type": "string", + "nullable": true + } + }, + "required": [ + "sessionId", + "status", + "servedBy", + "createdAt", + "stoppedAt", + "cdpUrl", + "note" + ] + } + }, + "required": [ + "poolId", + "leaseId", + "acquiredAt", + "expiresAt", + "session" + ] + } + } + } + }, + "400": { + "description": "Invalid request", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "Pool not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "No browser became ready within waitMs (\"No browser is currently ready in this pool\"), or the pool is paused.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "503": { + "description": "Browser service unavailable", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "tags": [ + "Pools" + ], + "summary": "Acquire a browser" + } + }, + "/v1/browser/pools/{poolId}/release": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "End a leased browser. The session is stopped; the answer carries status \"ended\". Idempotent: releasing the same lease again answers the same body.", + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 6, + "maxLength": 80, + "example": "pool_8ca73065e49d44f3a7825743745b2948" + }, + "required": true, + "name": "poolId", + "in": "path" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "leaseId": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "leaseId" + ], + "additionalProperties": false + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "poolId": { + "type": "string", + "minLength": 6, + "maxLength": 80, + "example": "pool_8ca73065e49d44f3a7825743745b2948" + }, + "leaseId": { + "type": "string" + }, + "sessionId": { + "type": "string", + "description": "The session id: an opaque string; do not validate its format." + }, + "status": { + "type": "string", + "enum": [ + "returned", + "ended" + ] + }, + "returnedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "poolId", + "leaseId", + "sessionId", + "status", + "returnedAt" + ] + } + } + } + }, + "400": { + "description": "Invalid request", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "Unknown pool, or a lease that does not belong to it (\"Pool lease not found\").", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "Pool state conflict", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "503": { + "description": "Browser service unavailable", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "tags": [ + "Pools" + ], + "summary": "Release a browser" + } + }, + "/v1/browser/pools/{poolId}/flush": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Stop every ready browser and warm fresh ones. Leased browsers are not touched. Answers how many were stopped.", + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 6, + "maxLength": 80, + "example": "pool_8ca73065e49d44f3a7825743745b2948" + }, + "required": true, + "name": "poolId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "stopped": { + "type": "integer" + } + }, + "required": [ + "success", + "stopped" + ] + } + } + } + }, + "400": { + "description": "Invalid request", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "Pool not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "Pool state conflict", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "503": { + "description": "Browser service unavailable", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "tags": [ + "Pools" + ], + "summary": "Flush warm browsers" + } } }, "servers": [ @@ -2426,6 +4439,9 @@ }, { "name": "Extensions" + }, + { + "name": "Pools" } ] } diff --git a/api-reference/overview.mdx b/api-reference/overview.mdx index ebd64db..37b3afd 100644 --- a/api-reference/overview.mdx +++ b/api-reference/overview.mdx @@ -20,24 +20,25 @@ Authorization: Bearer $DRIVER_API_KEY ## Conventions - Request and response bodies are JSON, except extension uploads, which are `multipart/form-data`. -- Identifiers that select a resource go in the query string (`?sessionId=…`, `?profileName=…`), not the path, except for extensions. -- Errors are `{ "error": "message" }` with a meaningful HTTP status; the create call's `503` also carries a `code`. See [Errors](/docs/sessions/errors). +- Identifiers that select a resource go in the query string (`?sessionId=…`, `?profileName=…`), not the path, except for extensions and pools (`/v1/browser/pools/{poolId}`). +- Errors are `{ "error": "message" }` with a meaningful HTTP status; the create call's `503` also carries a `code`. Pools answer `409` for state conflicts (duplicate name, over the warm cap, nothing ready, active leases). See [Errors](/docs/sessions/errors). - Session timestamps are ISO 8601 strings; billing periods use Unix epoch milliseconds (fields ending in `_ms`, and the subscription period bounds). Money is in cents (and millicents where exactness matters); bandwidth is bytes on sessions and megabytes on billing. - Unknown fields in the create body are rejected with `400`; the update call ignores them. -- Create answers `200` with the session; only the extension upload answers `201`. +- Create answers `200` with the session; the extension upload and the pool create answer `201`. ## Endpoints | Group | Endpoints | |---|---| | Sessions | `POST/GET/PATCH/DELETE /v1/browser/session`, `GET /v1/browser/sessions` | +| Pools | `POST/GET /v1/browser/pools`, `GET/PATCH/DELETE /v1/browser/pools/{poolId}`, `POST /v1/browser/pools/{poolId}/acquire`, `…/release`, `…/flush` | | Profiles | `GET /v1/browser/profiles`, `DELETE /v1/browser/profile` | | Account | `GET /v1/account/balance`, `GET /v1/account/billing` | | Extensions | `POST/GET /v1/browser/extensions`, `GET/PUT/DELETE /v1/browser/extensions/{extensionId}` | ## Source of truth -These reference pages are generated from the API's own OpenAPI document, served live at `https://api.driver.dev/doc` and rendered at [api.driver.dev/scalar](https://api.driver.dev/scalar), with a few corrections the generator gets wrong: `duration` accepts 60 (the live document says strictly more), `balance_cents` can be negative, `PATCH` answers `400` for an ended session, `GET /v1/browser/sessions` answers `400` for a bad status filter, the `503` carries `Retry-After` and a `code`, and paging has defaults and a maximum. On those points this reference is right and the live document is behind; on everything else, if the two disagree, the live document wins. +These reference pages are generated from the API's own OpenAPI document, served live at `https://api.driver.dev/doc` and rendered at [api.driver.dev/scalar](https://api.driver.dev/scalar), with a few corrections the generator gets wrong: `duration` accepts 60 (the live document says strictly more), `balance_cents` can be negative, `PATCH` answers `400` for an ended session, `GET /v1/browser/sessions` answers `400` for a bad status filter, the `503` carries `Retry-After` and a `code`, paging has defaults and a maximum, and the pool create answers `201` only. On those points this reference is right and the live document is behind; on everything else, if the two disagree, the live document wins. ## Client code diff --git a/docs.json b/docs.json index 349bc78..8c326f1 100644 --- a/docs.json +++ b/docs.json @@ -58,6 +58,7 @@ "docs/sessions/create", "docs/sessions/connect", "docs/sessions/lifecycle", + "docs/sessions/pools", "docs/sessions/live-view", "docs/sessions/errors" ] @@ -117,6 +118,20 @@ "DELETE /v1/browser/session" ] }, + { + "group": "Pools", + "openapi": "api-reference/openapi.json", + "pages": [ + "POST /v1/browser/pools", + "GET /v1/browser/pools", + "GET /v1/browser/pools/{poolId}", + "PATCH /v1/browser/pools/{poolId}", + "DELETE /v1/browser/pools/{poolId}", + "POST /v1/browser/pools/{poolId}/acquire", + "POST /v1/browser/pools/{poolId}/release", + "POST /v1/browser/pools/{poolId}/flush" + ] + }, { "group": "Profiles", "openapi": "api-reference/openapi.json", diff --git a/docs/account/billing.mdx b/docs/account/billing.mdx index 8bf6070..ce069b9 100644 --- a/docs/account/billing.mdx +++ b/docs/account/billing.mdx @@ -5,7 +5,7 @@ description: "Sessions are metered by bandwidth against the workspace's credits. Driver bills on **bandwidth**: the bytes a session moves, metered per megabyte and charged against the workspace's credits. A plan includes a monthly allowance and a concurrency limit; usage beyond the allowance draws on purchased credits. A running session that moves no bytes costs nothing, but it holds one of the plan's concurrent-browser slots until it stops. -Keeping sessions short and stopping them when the job is done is the whole cost-control story. `duration` on create caps what a forgotten session can cost. +Keeping sessions short and stopping them when the job is done is the whole cost-control story. `duration` on create caps what a forgotten session can cost. Warm browsers in a [pool](/docs/sessions/pools) are not billed and hold no concurrency slot; a browser becomes a billed, counted session when it is acquired, and its lease timeout caps how long it can be held. ## Balance diff --git a/docs/account/dashboard.mdx b/docs/account/dashboard.mdx index 7812f25..a43bbc0 100644 --- a/docs/account/dashboard.mdx +++ b/docs/account/dashboard.mdx @@ -10,6 +10,7 @@ The [dashboard](https://app.driver.dev) is the other half of the API. Everything | **Overview** | Running browsers against the plan limit, today's sessions, bandwidth this period, credits, and a 30-day chart. **Launch browser** starts a session with options and opens its live view without writing code. | | **Workbench** | A script editor next to a live browser and an output log. Pick a template, set country, network, profile, CAPTCHA solver and proxy, press Run. The quickest way to try options before committing them to code. | | **Profiles** | The workspace's saved [profiles](/docs/options/profiles). Launch a session on a profile to sign in by hand. | +| **Browser pools** | Create and manage [pools](/docs/sessions/pools) of warm browsers: size, country, profile, start URL, lease timeout; pause, flush or delete them; see how many are ready and in use. **Launch browser** on the Overview can take a browser from any ready pool. | | **Sessions** (Telemetry) | Every session, filterable by status, with a record page per session: live view while running, then node, country, network, bandwidth, cost and timestamps. Stop one, or stop all running. | | **Workspace** | Name, members and invitations. Admins manage members, keys and billing. | | **API keys** | The root key and named keys: create, roll, delete. Also a ready-to-paste briefing for your coding agent. | diff --git a/docs/options/profiles.mdx b/docs/options/profiles.mdx index 9429b33..3fff294 100644 --- a/docs/options/profiles.mdx +++ b/docs/options/profiles.mdx @@ -3,7 +3,7 @@ title: "Profiles" description: "Save a session's cookies and browser state under a name, and start later sessions already signed in." --- -A profile is a full browser profile stored under a name you choose. Start a session with `profile: { name, persist: true }`, sign in, stop the session: the state is saved. Start another session with the same name and it opens signed in. +A profile is a full browser profile stored under a name you choose. Start a session with `profile: { name, persist: true }`, sign in, stop the session: the state is saved. Start another session with the same name and it opens signed in. A [browser pool](/docs/sessions/pools) can load a profile into every warm browser too; keep `persist` off there, since several browsers writing the same profile would overwrite one another. ```json Request diff --git a/docs/sessions/create.mdx b/docs/sessions/create.mdx index da47ee3..f3287bc 100644 --- a/docs/sessions/create.mdx +++ b/docs/sessions/create.mdx @@ -3,7 +3,7 @@ title: "Create a session" description: "POST /v1/browser/session starts a real Chrome and returns its CDP URL once it is reachable." --- -A session is one browser on one machine for up to an hour. Create it with the options you need; every field is optional and the defaults give you a hosted Chrome in the US. +A session is one browser on one machine for up to an hour. Create it with the options you need; every field is optional and the defaults give you a hosted Chrome in the US. When a few seconds of start-up per browser is too slow, keep a [browser pool](/docs/sessions/pools) warm with the same options and acquire from it instead. ```bash cURL @@ -111,3 +111,4 @@ Retry guidance per status is on [Errors](/docs/sessions/errors). - [Connect over CDP](/docs/sessions/connect) - [Lifecycle](/docs/sessions/lifecycle) to get, list, label and stop sessions +- [Browser pools](/docs/sessions/pools) for warm browsers with these options preset diff --git a/docs/sessions/errors.mdx b/docs/sessions/errors.mdx index 809c782..f4f0b65 100644 --- a/docs/sessions/errors.mdx +++ b/docs/sessions/errors.mdx @@ -23,7 +23,8 @@ The bodies you will see most: `401` `Unauthorized: Missing or invalid Authorizat | `401` | Missing or invalid API key. | No. Check `Authorization: Bearer …`. | | `402` | The workspace is out of credits. | No. Add credits in the dashboard. | | `403` | Session or account limit reached: the workspace's concurrent-browser limit (`plan.concurrent_browsers` on `GET /v1/account/billing`; sessions that are `starting` count, and a stop frees the slot as soon as it returns), or a feature (extensions) the account is not enabled for. | Only after a session ends. Never in a loop. | -| `404` | Session, extension or account not found (unknown id, or one belonging to another workspace). Deleting an unknown profile is not a 404: it answers `{ "success": true }`. | No. | +| `404` | Session, extension, pool, lease or account not found (unknown id, or one belonging to another workspace). Deleting an unknown profile is not a 404: it answers `{ "success": true }`. | No. | +| `409` | Browser pools only: a duplicate pool name, a size over the account's warm-browser cap, an acquire that found nothing ready within `waitMs` (`No browser is currently ready in this pool`), an acquire on a paused pool, or a delete with active leases. See [Browser pools](/docs/sessions/pools#errors). | Only the acquire one, as for `503`: back off, cap, then fall back to a plain create or report. | | `429` | Too many requests. The API itself does not rate-limit today; a `429` comes from the edge in front of it. | Yes: a few attempts, a couple of seconds apart and growing. | | `502` | The edge could not reach the API. Not JSON. | Once, after a couple of seconds. | | `500` | Something failed on Driver's side. | Once. | diff --git a/docs/sessions/lifecycle.mdx b/docs/sessions/lifecycle.mdx index f325392..3d212d6 100644 --- a/docs/sessions/lifecycle.mdx +++ b/docs/sessions/lifecycle.mdx @@ -3,7 +3,7 @@ title: "Lifecycle" description: "Get, list, label and stop sessions. What each status means and how long a session lives." --- -A session moves through `starting` → `active` → `completed`, or `error` if the browser failed. It ends when you stop it, when it reaches its `duration`, or when the machine serving it goes away. +A session moves through `starting` → `active` → `completed`, or `error` if the browser failed. It ends when you stop it, when it reaches its `duration`, or when the machine serving it goes away. A session acquired from a [browser pool](/docs/sessions/pools) is the same object once acquired (`createdAt` is when the browser was warmed); it also ends when its lease is released or expires. | Status | Meaning | |---|---| diff --git a/docs/sessions/pools.mdx b/docs/sessions/pools.mdx new file mode 100644 index 0000000..93777cd --- /dev/null +++ b/docs/sessions/pools.mdx @@ -0,0 +1,255 @@ +--- +title: "Browser pools" +description: "Keep browsers warm and take one in under a second. Create a pool, acquire a lease, release or stop the session." +--- + +A pool keeps a number of browsers already running with the options you chose, so a job can take one instead of waiting for a create call. Acquiring from a ready pool answers in well under a second with an ordinary session; the pool warms a replacement behind it. Use a pool when start-up latency matters (an agent loop, a request handler, a burst of short jobs) or when every job needs the same start page loaded, profile attached or country. For a nightly batch that can wait a few seconds per browser, plain [create](/docs/sessions/create) is simpler. + +Pools also live in the [dashboard](https://app.driver.dev/pools) under **Browsers → Browser pools**, where **Launch browser** on the Overview can take a browser from any ready pool. + + +**Network options are not applied to pooled browsers yet.** A pool records the `country` you set and the session list reports it, but in our runs on 2026-09-20 every warm browser egressed from the default US network (a GB pool and a JP pool both answered with a US address and `America/New_York`), while a plain create with the same `country` egressed correctly. A `proxyUrl` that cannot be reached, or a country that does not exist, still warms and reports `ready`. Until this is fixed, use pools for jobs that are fine on the US network and [create sessions](/docs/sessions/create) for geo-specific or proxied ones. `url` and `note` are applied. + + +## Create a pool + + +```bash cURL +curl -X POST https://api.driver.dev/v1/browser/pools \ + -H "Authorization: Bearer $DRIVER_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "checkout-us", + "size": 3, + "browser": { "country": "US", "url": "https://shop.example.com", "note": "checkout pool" }, + "leaseTimeoutSeconds": 900, + "maxReadyAgeSeconds": 3600 + }' +``` +```typescript TypeScript +const res = await fetch("https://api.driver.dev/v1/browser/pools", { + method: "POST", + headers, + body: JSON.stringify({ + name: "checkout-us", + size: 3, + browser: { country: "US", url: "https://shop.example.com", note: "checkout pool" }, + leaseTimeoutSeconds: 900, + maxReadyAgeSeconds: 3600, + }), +}); +if (!res.ok) throw new Error(`Driver ${res.status}: ${(await res.json().catch(() => ({}))).error ?? res.statusText}`); +const pool = await res.json(); // pool.id is what the other calls take +``` +```python Python +r = requests.post( + "https://api.driver.dev/v1/browser/pools", + headers=headers, + json={ + "name": "checkout-us", + "size": 3, + "browser": {"country": "US", "url": "https://shop.example.com", "note": "checkout pool"}, + "leaseTimeoutSeconds": 900, + "maxReadyAgeSeconds": 3600, + }, +) +r.raise_for_status() +pool = r.json() # pool["id"] is what the other calls take +``` + + +The call answers `201` at once with the pool in `warming`; the browsers come up in the background and in our runs the pool was `ready` two to five seconds later. + +```json +{ + "id": "pool_63e5b6af14d344a38921d097ac6b101b", + "name": "checkout-us", + "size": 3, + "status": "warming", + "ready": 0, + "starting": 0, + "leased": 0, + "browser": { "country": "US", "url": "https://shop.example.com", "note": "checkout pool" }, + "leaseTimeoutSeconds": 900, + "maxReadyAgeSeconds": 3600, + "lastError": null, + "createdAt": "2026-09-20T15:56:56.877Z", + "updatedAt": "2026-09-20T15:56:56.877Z" +} +``` + +### Fields + +| Field | Type | Default | What it does | +|---|---|---|---| +| `name` | string | required | 1 to 64 characters, unique in the workspace (`409` `A pool with that name already exists`). | +| `size` | integer | required | How many browsers to keep ready, at least 1. All pools together may keep 30 browsers warm per account (`409` `Pool capacity 32 exceeds your pool limit of 30`); write to support to raise it. Paused pools count too, and a resize that would cross the cap is refused the same way. | +| `browser` | object | `{}` | The create options every browser in the pool starts with: `country`, `timezone`, `language`, `url`, `windowSize`, `displaySize`, `proxyUrl`, `profile`, `adblock`, `captchaSolver`, `extensionIds`, `nodeId`, `note`. Same meaning as on [Create a session](/docs/sessions/create#options). `type`, `duration` and `browserCheck` are not accepted. | +| `leaseTimeoutSeconds` | integer | `900` | How long an acquired browser may be held, 30 to 86400. When the lease expires the session is stopped. | +| `maxReadyAgeSeconds` | integer | `3600` | How long a browser may sit ready before the pool replaces it with a fresh one, 30 to 86400. The swap takes two to three seconds, during which `ready` is one lower. | + +Unknown fields, at the top level or inside `browser`, are `400` (`bogus is not supported by this endpoint`), as on create. The values inside `browser` are not checked the way a create body is: a pool with `country: "ZZ"` or an unreachable `proxyUrl` is created and reports `ready` (see the warning above). `lastError` is set when browsers fail to start. + +A pool's browsers use `profile` the way a session does. With `persist: false` (the default) every browser loads the profile read-only, which is what you want in a pool: several browsers writing the same profile at once would overwrite one another. + +## Status and counters + +| `status` | Meaning | +|---|---| +| `warming` | Just created or resumed; browsers are starting and none is ready yet. | +| `ready` | `ready` equals `size`. | +| `degraded` | Fewer ready browsers than `size`: normal right after an acquire, a flush, or a lease expiring, while replacements start. A pool that cannot start browsers stays here with `lastError` set. | +| `paused` | Warm browsers stopped and no new ones started. Acquire is refused. | +| `deleting` | Being torn down. | + +`ready`, `starting` and `leased` count the browsers in each state. Ready browsers are not sessions of the workspace yet: `GET /v1/browser/sessions` does not list them, and they hold none of the workspace's concurrency slots until acquired. + +## Acquire a browser + + +```bash cURL +curl -X POST "https://api.driver.dev/v1/browser/pools/$POOL_ID/acquire" \ + -H "Authorization: Bearer $DRIVER_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ "waitMs": 10000 }' +``` +```typescript TypeScript +const lease = await fetch(`https://api.driver.dev/v1/browser/pools/${poolId}/acquire`, { + method: "POST", + headers, + body: JSON.stringify({ waitMs: 10000 }), +}).then((r) => (r.ok ? r.json() : Promise.reject(new Error(`Driver ${r.status}`)))); +const { leaseId, session } = lease; // session.cdpUrl is live now +``` +```python Python +r = requests.post( + f"https://api.driver.dev/v1/browser/pools/{pool_id}/acquire", headers=headers, json={"waitMs": 10000} +) +r.raise_for_status() +lease = r.json() +lease_id, session = lease["leaseId"], lease["session"] +``` + + +```json +{ + "poolId": "pool_63e5b6af14d344a38921d097ac6b101b", + "leaseId": "lease_ff68375dfc3b494299cc4a1d5ab0e36a", + "acquiredAt": "2026-09-20T15:57:01.132Z", + "expiresAt": "2026-09-20T16:12:01.132Z", + "session": { + "sessionId": "51b1815315c14580bb0fc7ab327999fe", + "status": "active", + "servedBy": "fresh-alien-program", + "createdAt": "2026-09-20T15:56:56.892Z", + "stoppedAt": null, + "cdpUrl": "wss://…", + "note": "checkout pool" + } +} +``` + +`waitMs` (0 to 30000, default 0) is how long the call may wait for a browser to become ready when none is; a replacement takes two to three seconds to warm, so a wait of a few seconds usually succeeds. The body is required: send `{}` for no wait (no body is a `400` `Malformed JSON in request body`). When nothing is ready by then the answer is `409` `No browser is currently ready in this pool`; a paused pool answers `409` `Pool is paused` at once. Treat the first like a `503` on create: back off briefly, cap the attempts, then fall back to a plain create or report. + +From the acquire call on, `session` is an ordinary session of the workspace: it appears in `GET /v1/browser/sessions`, `GET /v1/browser/session?sessionId=…` works, it counts as a running browser, and the [live view](/docs/sessions/live-view) works on its `cdpUrl`. Its `createdAt` is when the browser was warmed, not when you acquired it, and the page is already on the pool's `url`. Connect to it exactly like any other session, with Patchright: + + +```typescript TypeScript +import { chromium } from "patchright"; + +const lease = await acquire(poolId, 10_000); +try { + const browser = await chromium.connectOverCDP(lease.session.cdpUrl); + try { + const page = browser.contexts()[0].pages()[0]; // already on the pool's start URL + await page.goto("https://shop.example.com/cart"); + console.log(await page.title()); + } finally { + await browser.close(); + } +} finally { + await release(poolId, lease.leaseId); +} +``` +```python Python +from patchright.sync_api import sync_playwright + +lease = acquire(pool_id, wait_ms=10000) +try: + with sync_playwright() as p: + browser = p.chromium.connect_over_cdp(lease["session"]["cdpUrl"]) + page = browser.contexts[0].pages[0] # already on the pool's start URL + page.goto("https://shop.example.com/cart") + print(page.title()) + browser.close() +finally: + release(pool_id, lease["leaseId"]) +``` + + +`acquire` and `release` here are the two calls on this page; the [reference client](/docs/start/for-agents#reference-implementation) has both. + +## Release or stop + +A leased browser ends one of three ways, and the pool warms a replacement in every case: + +- **Release** it: `POST /v1/browser/pools/{poolId}/release` with `{ "leaseId": "…" }`. The session is stopped and the answer is `{ "poolId", "leaseId", "sessionId", "status": "ended", "returnedAt" }`. Do this in a `finally`, the way you would stop a session. +- **Stop** the session with `DELETE /v1/browser/session?sessionId=…`. Same effect; the lease closes on its own within a few seconds. +- **Let the lease expire.** At `expiresAt` (`leaseTimeoutSeconds` after acquire) the session is stopped whether or not you are done, so pick a lease that covers the job, or stop early. + +Releasing is what ends billing for the session, exactly like stopping one. Releasing is idempotent (a second release of the same lease answers the same body); an unknown lease, or one from another pool, is `404` `Pool lease not found`. A browser is never handed back to the pool for another job: the next acquire always gets a fresh one. + + +```bash cURL +curl -X POST "https://api.driver.dev/v1/browser/pools/$POOL_ID/release" \ + -H "Authorization: Bearer $DRIVER_API_KEY" \ + -H "Content-Type: application/json" \ + -d "{ \"leaseId\": \"$LEASE_ID\" }" +``` +```typescript TypeScript +await fetch(`https://api.driver.dev/v1/browser/pools/${poolId}/release`, { + method: "POST", + headers, + body: JSON.stringify({ leaseId }), +}); +``` +```python Python +requests.post( + f"https://api.driver.dev/v1/browser/pools/{pool_id}/release", headers=headers, json={"leaseId": lease_id} +) +``` + + +## Manage a pool + +| Call | What it does | +|---|---| +| `GET /v1/browser/pools` | `{ "pools": [ … ] }`, every pool in the workspace with its counters. | +| `GET /v1/browser/pools/{poolId}` | One pool. | +| `PATCH /v1/browser/pools/{poolId}` | Change `name`, `size`, `browser`, `leaseTimeoutSeconds`, `maxReadyAgeSeconds`, or `paused` (`true` stops the warm browsers and refuses acquires; `false` warms them again). Every update restarts the pool's warm browsers (the pool goes back to `warming` for a few seconds; leased browsers are untouched). Always send the whole `browser` template with every option you want to keep: an update without `browser` resets the template to `{}`, so a rename or a resize on its own silently drops the country, start URL and everything else. | +| `POST /v1/browser/pools/{poolId}/flush` | Stops every ready browser and warms fresh ones. Answers `{ "success": true, "stopped": n }`. Use it after changing something browsers pick up only at start. Leased browsers are not touched. | +| `DELETE /v1/browser/pools/{poolId}` | Stops the warm browsers and removes the pool. With leased browsers the answer is `409` `Pool has active leases; pass force=true to stop them`; `?force=true` (the literal `true`; `1` is a `400`) stops those sessions too. A deleted pool is `404` afterwards. | + +Pool ids go in the path, unlike session ids, which go in the query string. + +## Errors + +| Status | Body | Meaning | +|---|---|---| +| `400` | names the field | A missing or out-of-range field (`size: Too small: expected number to be >0`, `leaseTimeoutSeconds: Too small: expected number to be >=30`), or an unknown field. | +| `404` | `Browser pool not found` | Unknown pool id, or one belonging to another workspace. | +| `409` | `A pool with that name already exists` | Names are unique per workspace. | +| `409` | `Pool capacity 32 exceeds your pool limit of 30` | The new size would put the account's warm browsers over its cap. | +| `409` | `No browser is currently ready in this pool` | Acquire waited `waitMs` and nothing became ready. Back off, cap, then fall back or report. | +| `409` | `Pool has active leases; pass force=true to stop them` | Delete while browsers are leased. | +| `503` | | The browser service is unavailable. Back off as for a create `503`. | + +## Cost and limits + +Warm browsers are not sessions of the workspace: they are not listed, not billed and hold no concurrency slot until acquired. Once acquired, a browser is a session like any other: it counts toward the workspace's concurrent-browser limit and its bandwidth is metered until it is released, stopped or its lease expires. The account-wide cap of 30 warm browsers across all pools is separate from the concurrency limit. + +## Next + +- [Lifecycle](/docs/sessions/lifecycle) for what you can do with the acquired session. +- [Create a session](/docs/sessions/create#options) for every option the `browser` template accepts. +- [For agents](/docs/start/for-agents#reference-implementation): the reference client has `acquireFromPool` and `releaseLease`. diff --git a/docs/start/best-practices.mdx b/docs/start/best-practices.mdx index 4f880c7..e26dc86 100644 --- a/docs/start/best-practices.mdx +++ b/docs/start/best-practices.mdx @@ -15,6 +15,7 @@ Before writing browser steps, ask whether the outcome is reachable faster: - Go straight to the URL that has the result instead of clicking through navigation. - Do several pages in one session rather than one session per page. Creating a session is the expensive step. +- When the start-up itself is the bottleneck (a request handler, an agent loop), keep a [browser pool](/docs/sessions/pools) warm and acquire from it: under a second instead of a few. - Sign in once on a persisted [profile](/docs/options/profiles) and reuse it instead of logging in on every run. - Never use fixed sleeps. Rely on the framework's auto-waiting and stop the session the moment the result is in hand. @@ -88,7 +89,7 @@ finally: ``` -Set a `duration` that matches the job. Sessions end on their own at the duration (one hour by default), which caps the cost of anything you forget. +Set a `duration` that matches the job. Sessions end on their own at the duration (one hour by default), which caps the cost of anything you forget. A browser taken from a [pool](/docs/sessions/pools) is released the same way, with the release call in the `finally`; its lease timeout plays the role of `duration`. ## Reuse logins with profiles diff --git a/docs/start/for-agents.mdx b/docs/start/for-agents.mdx index 31d263b..441c715 100644 --- a/docs/start/for-agents.mdx +++ b/docs/start/for-agents.mdx @@ -16,7 +16,8 @@ This page is written for an AI coding agent (Claude Code, Cursor, Codex, or simi 7. **Keep the `sessionId`.** Log it with the run. Every session has a record in the dashboard under Telemetry → Sessions with node, network, timing and cost. If the person wants to watch, print `https://viewer.driver.dev?ws=` as soon as the session exists; see [Live view](/docs/sessions/live-view). 8. **Report what a person must fix; retry only what time fixes.** `402` (out of credits) and `403` (concurrency or plan limit): report, never loop. `503` (no capacity; the response carries `Retry-After: 2` and `"code": "browser_capacity_unavailable"`): back off with jitter, cap the attempts, then report. `504` (Chrome did not start in time) and `500`: retry once. `429`: back off. The full table is on [Errors](/docs/sessions/errors). 9. **Stay under the workspace's concurrency limit.** `GET /v1/account/billing` reports it as `plan.concurrent_browsers`; sessions beyond it are refused with 403. -10. **When a site challenges or blocks, escalate in this order.** Remove every hook and tactic; then create with `type: "hosted_stealth"`, then `"hosted_privacy"`, then add `captchaSolver: true`; when a step genuinely needs a person, give them the live view URL. See [Browser types](/docs/options/browser-types) and [CAPTCHA solving](/docs/options/captcha-and-adblock). +10. **Use a browser pool when start-up latency matters.** A [pool](/docs/sessions/pools) keeps browsers warm with fixed options; `POST /v1/browser/pools/{poolId}/acquire` with `{ "waitMs": 10000 }` answers in under a second with `{ leaseId, session }`, and `session` is an ordinary session from then on. Release it with `POST /v1/browser/pools/{poolId}/release` `{ leaseId }` in the same `finally` where you would stop a session. Do not create pools on the person's behalf without asking: they hold warm browsers against an account-wide cap. +11. **When a site challenges or blocks, escalate in this order.** Remove every hook and tactic; then create with `type: "hosted_stealth"`, then `"hosted_privacy"`, then add `captchaSolver: true`; when a step genuinely needs a person, give them the live view URL. See [Browser types](/docs/options/browser-types) and [CAPTCHA solving](/docs/options/captcha-and-adblock). ## How to write the automation @@ -54,6 +55,10 @@ Integrate Driver (driver.dev), hosted real-Chrome browser infrastructure, into t hand them the live view. - Live view: https://viewer.driver.dev?ws= — print it as soon as the session exists so the user can watch; it works until the session ends. +- If a browser pool exists for the job (GET /v1/browser/pools, or the dashboard's Browser pools + page), take a browser from it instead of creating one: POST /v1/browser/pools//acquire + with {"waitMs": 10000} returns { leaseId, session }; release it in the finally with + POST /v1/browser/pools//release {"leaseId": ...}. Do not create pools unasked. - Check whether the outcome can be reached faster: direct URLs, one session for many pages, a persisted profile instead of logging in. Avoid slow browser logic and fixed sleeps. - Always stop the session in a finally: DELETE /v1/browser/session?sessionId=. @@ -75,7 +80,7 @@ Base URL `https://api.driver.dev`. Every call carries `Authorization: Bearer $DR | Use | `chromium.connectOverCDP(cdpUrl)` | From Patchright. Reuse the first context and page. | | Stop | `DELETE /v1/browser/session?sessionId=` | Returns `{ success: true }`. | -Useful extras: `GET /v1/browser/session?sessionId=` for status and bandwidth, `GET /v1/browser/sessions?status=active` to list, `PATCH /v1/browser/session?sessionId=` with `{ "note": "…" }` to label a run. Full options for the create body are on [Create a session](/docs/sessions/create#options). +Useful extras: `GET /v1/browser/session?sessionId=` for status and bandwidth, `GET /v1/browser/sessions?status=active` to list, `PATCH /v1/browser/session?sessionId=` with `{ "note": "…" }` to label a run. Full options for the create body are on [Create a session](/docs/sessions/create#options). When a [pool](/docs/sessions/pools) exists, `POST /v1/browser/pools/{poolId}/acquire` replaces the create call and `POST /v1/browser/pools/{poolId}/release` the stop; pool ids go in the path. ## Reference implementation @@ -178,6 +183,27 @@ export async function stopSession(id: string): Promise { } } +/** + * Browser pools (see Browser pools in the docs): a warm browser in under a second. The returned session is an + * ordinary session; release it (or stop it) in a finally. A 409 means nothing became ready within waitMs. + */ +export interface PoolLease { + poolId: string; + leaseId: string; + acquiredAt: string; + expiresAt: string; // the session is stopped at this time unless released earlier + session: Session & { cdpUrl: string }; +} +export const acquireFromPool = (poolId: string, waitMs = 10_000) => + call("POST", `/v1/browser/pools/${encodeURIComponent(poolId)}/acquire`, { waitMs }); +export async function releaseLease(poolId: string, leaseId: string): Promise { + try { + await call("POST", `/v1/browser/pools/${encodeURIComponent(poolId)}/release`, { leaseId }); + } catch (e) { + console.error(`Driver: release of ${leaseId} failed: ${(e as Error).message}`); + } +} + /** Create normally returns an active session; this covers the rare `starting` answer. */ export async function waitForActive(s: Session, timeoutMs = 60_000): Promise { const deadline = Date.now() + timeoutMs; @@ -291,6 +317,20 @@ def stop_session(session_id): print(f"Driver: stop of {session_id} failed: {e}") +def acquire_from_pool(pool_id, wait_ms=10000): + """Browser pools: a warm browser in under a second. Returns {poolId, leaseId, acquiredAt, expiresAt, session}; + the session is an ordinary session. A 409 means nothing became ready within wait_ms.""" + return _call("POST", f"/v1/browser/pools/{pool_id}/acquire", json={"waitMs": wait_ms}) + + +def release_lease(pool_id, lease_id): + """Ends the leased session. Idempotent; logs instead of raising, for use in a finally.""" + try: + _call("POST", f"/v1/browser/pools/{pool_id}/release", json={"leaseId": lease_id}) + except DriverError as e: + print(f"Driver: release of {lease_id} failed: {e}") + + def wait_for_active(session, timeout=60): """Create normally returns an active session; this covers the rare 'starting' answer.""" deadline = time.time() + timeout @@ -337,6 +377,7 @@ def browser_page(**opts): - [ ] `DRIVER_API_KEY` comes from the environment and is not committed. - [ ] 402 and 403 are reported to the user; 503 is retried only with a cap and a growing delay. - [ ] The result was verified through `GET /v1/browser/session` (status `completed`), not assumed. +- [ ] A browser taken from a pool is released (or stopped) in the same `finally`; no pool was created without asking. ## Machine-readable resources diff --git a/docs/start/index.mdx b/docs/start/index.mdx index e120e8a..9e511e8 100644 --- a/docs/start/index.mdx +++ b/docs/start/index.mdx @@ -44,6 +44,7 @@ Driver is hosted browser infrastructure. Every session is a real Chrome window o | **Real Chrome** | Consumer Chrome builds on real hardware, not a headless container. Sites see a normal browser. | | **Residential networks** | Sessions egress from residential connections in the country you choose. Bring your own SOCKS5 proxy when you need to. | | **Profiles** | Save cookies and local state under a name and reuse them, so persistent logins survive between sessions. | +| **Browser pools** | Keep browsers warm with fixed options and take one in under a second when a job needs it. | | **CAPTCHA solving** | One flag loads Driver's solver into the session. | | **Live view** | Watch and drive any running session from the dashboard or a viewer URL. | @@ -59,4 +60,5 @@ Authorization: Bearer $DRIVER_API_KEY - [Quickstart](/docs/start/quickstart) creates and stops a session end to end. - [Create a session](/docs/sessions/create#options) explains every field you can pass on create. +- [Browser pools](/docs/sessions/pools) when start-up latency matters: warm browsers, acquired in under a second. - [Frameworks](/docs/frameworks/patchright) shows the connection code for Patchright, Playwright, Puppeteer, Browser Use, Stagehand and Crawl4AI. diff --git a/docs/start/quickstart.mdx b/docs/start/quickstart.mdx index c186b4b..3dfb227 100644 --- a/docs/start/quickstart.mdx +++ b/docs/start/quickstart.mdx @@ -137,3 +137,4 @@ Stopping the session releases the machine and closes the bill for that session. - [For agents](/docs/start/for-agents) if a coding agent is doing the integration. - [Create a session](/docs/sessions/create#options) for every field you can pass: browser type, location, proxies, profiles, window size. - [Best practices](/docs/start/best-practices) before you scale up. +- [Browser pools](/docs/sessions/pools) when a few seconds of start-up per browser is too slow. diff --git a/scripts/sync-openapi.mjs b/scripts/sync-openapi.mjs index e9300a2..a8034ea 100644 --- a/scripts/sync-openapi.mjs +++ b/scripts/sync-openapi.mjs @@ -20,6 +20,7 @@ const dropDeprecatedType = (node) => { dropDeprecatedType(doc) const tagFor = (path) => path.startsWith('/v1/browser/session') ? 'Sessions' + : path.startsWith('/v1/browser/pools') ? 'Pools' : path.startsWith('/v1/browser/profile') ? 'Profiles' : path.startsWith('/v1/browser/extensions') ? 'Extensions' : path.startsWith('/v1/account') ? 'Account' @@ -42,6 +43,14 @@ const summaries = { 'GET /v1/browser/extensions/{extensionId}': 'Get an extension', 'PUT /v1/browser/extensions/{extensionId}': 'Update an extension', 'DELETE /v1/browser/extensions/{extensionId}': 'Delete an extension', + 'POST /v1/browser/pools': 'Create a pool', + 'GET /v1/browser/pools': 'List pools', + 'GET /v1/browser/pools/{poolId}': 'Get a pool', + 'PATCH /v1/browser/pools/{poolId}': 'Update a pool', + 'DELETE /v1/browser/pools/{poolId}': 'Delete a pool', + 'POST /v1/browser/pools/{poolId}/acquire': 'Acquire a browser', + 'POST /v1/browser/pools/{poolId}/release': 'Release a browser', + 'POST /v1/browser/pools/{poolId}/flush': 'Flush warm browsers', } const tags = new Set() for (const [path, ops] of Object.entries(doc.paths)) { @@ -111,6 +120,47 @@ if (create503) { const code = create503.content?.['application/json']?.schema?.properties?.code if (code) { code.enum = ['browser_capacity_unavailable']; code.description = 'Machine-readable cause; present when no browser capacity is available.' } } +// Pools, verified live 2026-09-20: create answers 201 (the generator lists 200 as well); the browser template is +// validated when browsers start, not on create; acquire needs a JSON body; the 409s have fixed messages. +const pools = doc.paths['/v1/browser/pools'] +const poolCreate = pools?.post +if (poolCreate) { + if (poolCreate.responses['201'] && poolCreate.responses['200']) delete poolCreate.responses['200'] + const props = poolCreate.requestBody?.content?.['application/json']?.schema?.properties + if (props) { + props.name.description = 'Unique in the workspace, 1 to 64 characters.' + props.size.description = 'Browsers to keep ready. All pools together may keep 30 warm per account.' + props.browser.description = 'The create options every browser in the pool starts with (same fields as POST /v1/browser/session, without type, duration and browserCheck). Values are not validated on create. As of 2026-09-20 country and proxyUrl are not applied to warm browsers (they egress from the default US network); url and note are.' + props.leaseTimeoutSeconds.description = 'How long an acquired browser may be held, in seconds (30 to 86400, default 900). The session is stopped when the lease expires.' + props.maxReadyAgeSeconds.description = 'How long a browser may wait ready before it is replaced with a fresh one, in seconds (30 to 86400, default 3600).' + } + poolCreate.responses['409'].description = 'A pool with that name already exists, or the size would exceed the warm-browser cap of the account ("Pool capacity 32 exceeds your pool limit of 30").' +} +const poolPatch = doc.paths['/v1/browser/pools/{poolId}']?.patch +if (poolPatch) { + const props = poolPatch.requestBody?.content?.['application/json']?.schema?.properties + if (props?.browser) props.browser.description = 'Replaces the whole browser template; include every option to keep. Changing it restarts the warm browsers.' + if (props?.paused) props.paused.description = 'true stops the warm browsers and refuses acquires; false warms them again.' +} +const poolDelete = doc.paths['/v1/browser/pools/{poolId}']?.delete +if (poolDelete) { + for (const p of poolDelete.parameters || []) if (p.name === 'force') p.description = 'Pass true to stop leased browsers too. Without it a pool with active leases answers 409 ("Pool has active leases; pass force=true to stop them").' + poolDelete.responses['409'].description = 'The pool has active leases and force was not passed.' +} +const acquire = doc.paths['/v1/browser/pools/{poolId}/acquire']?.post +if (acquire) { + const body = acquire.requestBody?.content?.['application/json']?.schema + if (body?.properties?.waitMs) { body.properties.waitMs.default = 0; body.properties.waitMs.description = 'How long to wait for a browser to become ready when none is, in milliseconds (0 to 30000). Send {} for no wait; the body is required.' } + acquire.responses['409'].description = 'No browser became ready within waitMs ("No browser is currently ready in this pool"), or the pool is paused.' + acquire.description = 'Take a ready browser. From here on the session belongs to the workspace like any other; the pool warms a replacement. Its createdAt is when the browser was warmed.' +} +const release = doc.paths['/v1/browser/pools/{poolId}/release']?.post +if (release) { + release.description = 'End a leased browser. The session is stopped; the answer carries status "ended". Idempotent: releasing the same lease again answers the same body.' + release.responses['404'].description = 'Unknown pool, or a lease that does not belong to it ("Pool lease not found").' +} +const flush = doc.paths['/v1/browser/pools/{poolId}/flush']?.post +if (flush) flush.description = 'Stop every ready browser and warm fresh ones. Leased browsers are not touched. Answers how many were stopped.' if (doc.components?.securitySchemes?.Bearer) doc.components.securitySchemes.Bearer.description = 'A workspace API key from Settings → API keys in the dashboard (https://app.driver.dev), sent as a bearer token.' doc.externalDocs = { url: 'https://docs.driver.dev', description: 'Driver documentation' } if (doc.info) doc.info.contact = { name: 'Driver support', email: 'support@driver.dev' } From d33b1cc4c4a2d5bd862d8b476ba40d1a4b8e439e Mon Sep 17 00:00:00 2001 From: browsercash Date: Sun, 20 Sep 2026 18:03:46 -0400 Subject: [PATCH 08/11] Refresh preview from v2: contact details --- api-reference/openapi.json | 2 +- docs.json | 5 +++-- docs/options/extensions.mdx | 2 +- scripts/sync-openapi.mjs | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/api-reference/openapi.json b/api-reference/openapi.json index 7e9fb70..088ec60 100644 --- a/api-reference/openapi.json +++ b/api-reference/openapi.json @@ -6,7 +6,7 @@ "description": "API for interacting with the Driver.dev browser network.", "contact": { "name": "Driver support", - "email": "support@driver.dev" + "email": "alex@driver.dev" } }, "externalDocs": { diff --git a/docs.json b/docs.json index 8c326f1..2452266 100644 --- a/docs.json +++ b/docs.json @@ -167,7 +167,7 @@ "links": [ { "label": "Support", - "href": "mailto:support@driver.dev" + "href": "mailto:alex@driver.dev" }, { "label": "Status", @@ -195,7 +195,8 @@ "footer": { "socials": { "github": "https://github.com/BrowserCash", - "x": "https://x.com/driverdev" + "x": "https://x.com/driverdotdev", + "linkedin": "https://www.linkedin.com/company/driverdev" } }, "redirects": [ diff --git a/docs/options/extensions.mdx b/docs/options/extensions.mdx index 18ebef5..cd30ff0 100644 --- a/docs/options/extensions.mdx +++ b/docs/options/extensions.mdx @@ -4,7 +4,7 @@ description: "Upload your own Chrome extensions and load them into hosted sessio --- -The extensions API is enabled per account. Without access, the endpoints return `403` (`Extensions API is not enabled for this account`); write to support@driver.dev to request it. An upload without a file is rejected first with `No file uploaded. Send as multipart field "extension".` +The extensions API is enabled per account. Without access, the endpoints return `403` (`Extensions API is not enabled for this account`); write to alex@driver.dev to request it. An upload without a file is rejected first with `No file uploaded. Send as multipart field "extension".` ## Upload an extension diff --git a/scripts/sync-openapi.mjs b/scripts/sync-openapi.mjs index a8034ea..884b873 100644 --- a/scripts/sync-openapi.mjs +++ b/scripts/sync-openapi.mjs @@ -163,7 +163,7 @@ const flush = doc.paths['/v1/browser/pools/{poolId}/flush']?.post if (flush) flush.description = 'Stop every ready browser and warm fresh ones. Leased browsers are not touched. Answers how many were stopped.' if (doc.components?.securitySchemes?.Bearer) doc.components.securitySchemes.Bearer.description = 'A workspace API key from Settings → API keys in the dashboard (https://app.driver.dev), sent as a bearer token.' doc.externalDocs = { url: 'https://docs.driver.dev', description: 'Driver documentation' } -if (doc.info) doc.info.contact = { name: 'Driver support', email: 'support@driver.dev' } +if (doc.info) doc.info.contact = { name: 'Driver support', email: 'alex@driver.dev' } // The source document names internal systems. Readers get product language instead. Ordered: phrases first, then words. const SCRUB = [ From 7939b683644e1a4603a4541614a9c79bc4d05846 Mon Sep 17 00:00:00 2001 From: browsercash Date: Sun, 20 Sep 2026 20:01:09 -0400 Subject: [PATCH 09/11] Refresh preview from v2: editorial pass --- api-reference/overview.mdx | 18 +++---- docs/account/api-keys.mdx | 16 +++--- docs/account/billing.mdx | 14 ++--- docs/account/dashboard.mdx | 22 ++++---- docs/frameworks/browser-use.mdx | 14 ++--- docs/frameworks/crawl4ai.mdx | 10 ++-- docs/frameworks/patchright.mdx | 26 +++++----- docs/frameworks/playwright.mdx | 10 ++-- docs/frameworks/puppeteer.mdx | 20 ++++---- docs/frameworks/stagehand.mdx | 14 ++--- docs/options/browser-types.mdx | 16 +++--- docs/options/captcha-and-adblock.mdx | 10 ++-- docs/options/countries.mdx | 4 +- docs/options/extensions.mdx | 18 +++---- docs/options/location.mdx | 20 ++++---- docs/options/node-reuse.mdx | 10 ++-- docs/options/profiles.mdx | 22 ++++---- docs/options/proxies.mdx | 18 +++---- docs/options/window-size.mdx | 14 ++--- docs/sessions/connect.mdx | 20 ++++---- docs/sessions/create.mdx | 46 +++++++++-------- docs/sessions/errors.mdx | 32 ++++++------ docs/sessions/lifecycle.mdx | 24 ++++----- docs/sessions/live-view.mdx | 14 +++-- docs/sessions/pools.mdx | 62 +++++++++++------------ docs/start/best-practices.mdx | 76 +++++++++++++--------------- docs/start/for-agents.mdx | 52 +++++++++---------- docs/start/index.mdx | 36 ++++++------- docs/start/quickstart.mdx | 22 ++++---- scripts/gen-countries.mjs | 4 +- 30 files changed, 340 insertions(+), 344 deletions(-) diff --git a/api-reference/overview.mdx b/api-reference/overview.mdx index 37b3afd..4865b0d 100644 --- a/api-reference/overview.mdx +++ b/api-reference/overview.mdx @@ -1,6 +1,6 @@ --- title: "API overview" -description: "Base URL, authentication, conventions, and the live OpenAPI document these pages are generated from." +description: "Base URL, authentication, conventions, and the live OpenAPI document these pages come from." --- ## Base URL @@ -19,12 +19,12 @@ Authorization: Bearer $DRIVER_API_KEY ## Conventions -- Request and response bodies are JSON, except extension uploads, which are `multipart/form-data`. -- Identifiers that select a resource go in the query string (`?sessionId=…`, `?profileName=…`), not the path, except for extensions and pools (`/v1/browser/pools/{poolId}`). -- Errors are `{ "error": "message" }` with a meaningful HTTP status; the create call's `503` also carries a `code`. Pools answer `409` for state conflicts (duplicate name, over the warm cap, nothing ready, active leases). See [Errors](/docs/sessions/errors). -- Session timestamps are ISO 8601 strings; billing periods use Unix epoch milliseconds (fields ending in `_ms`, and the subscription period bounds). Money is in cents (and millicents where exactness matters); bandwidth is bytes on sessions and megabytes on billing. -- Unknown fields in the create body are rejected with `400`; the update call ignores them. -- Create answers `200` with the session; the extension upload and the pool create answer `201`. +- Bodies are JSON, except extension uploads (`multipart/form-data`). +- Identifiers go in the query string (`?sessionId=…`, `?profileName=…`), not the path. Extensions and pools are the exception (`/v1/browser/pools/{poolId}`). +- Errors are `{ "error": "message" }` with a meaningful HTTP status. The create call's `503` also carries a `code`. Pools answer `409` for state conflicts: duplicate name, over the warm cap, nothing ready, active leases. See [Errors](/docs/sessions/errors). +- Session timestamps are ISO 8601. Billing periods are Unix epoch milliseconds (fields ending in `_ms`, plus the subscription period bounds). Money is cents, or millicents where exactness matters. Bandwidth is bytes on sessions and megabytes on billing. +- Unknown fields in the create body are a `400`. The update call ignores them. +- Create answers `200` with the session. Extension upload and pool create answer `201`. ## Endpoints @@ -38,8 +38,8 @@ Authorization: Bearer $DRIVER_API_KEY ## Source of truth -These reference pages are generated from the API's own OpenAPI document, served live at `https://api.driver.dev/doc` and rendered at [api.driver.dev/scalar](https://api.driver.dev/scalar), with a few corrections the generator gets wrong: `duration` accepts 60 (the live document says strictly more), `balance_cents` can be negative, `PATCH` answers `400` for an ended session, `GET /v1/browser/sessions` answers `400` for a bad status filter, the `503` carries `Retry-After` and a `code`, paging has defaults and a maximum, and the pool create answers `201` only. On those points this reference is right and the live document is behind; on everything else, if the two disagree, the live document wins. +These pages are generated from the live OpenAPI document at `https://api.driver.dev/doc` (rendered at [api.driver.dev/scalar](https://api.driver.dev/scalar)), with corrections where the generator is wrong: `duration` accepts 60 (the live document says strictly more), `balance_cents` can be negative, `PATCH` answers `400` for an ended session, `GET /v1/browser/sessions` answers `400` for a bad status filter, the `503` carries `Retry-After` and a `code`, paging has defaults and a maximum, and pool create answers `201` only. On those points this reference is right. On everything else the live document wins. ## Client code -There is no SDK to install. The endpoints are plain HTTP; the [For agents](/docs/start/for-agents#reference-implementation) page has a small client module for TypeScript and Python that you can copy. +There's no SDK. The endpoints are plain HTTP; [For agents](/docs/start/for-agents#reference-implementation) has a small client module for TypeScript and Python you can copy. diff --git a/docs/account/api-keys.mdx b/docs/account/api-keys.mdx index cce26ed..ab216df 100644 --- a/docs/account/api-keys.mdx +++ b/docs/account/api-keys.mdx @@ -1,9 +1,9 @@ --- title: "API keys" -description: "Keys are per workspace, created in the dashboard, and sent as a bearer token." +description: "Keys are per workspace, created in the dashboard, sent as a bearer token." --- -Every workspace has a root key and can create additional named keys under **Settings → API keys** in the [dashboard](https://app.driver.dev). All keys of a workspace share its plan, credits and concurrency limit. +Every workspace has a root key and can create named keys under **Settings → API keys** in the [dashboard](https://app.driver.dev). All keys of a workspace share its plan, credits and concurrency limit. ```bash Authorization: Bearer $DRIVER_API_KEY @@ -11,15 +11,15 @@ Authorization: Bearer $DRIVER_API_KEY ## Handling keys -- Keep the key in an environment variable named `DRIVER_API_KEY`. Do not commit it, and do not paste it into prompts, tickets or logs. -- Create one key per integration so a leaked key can be rolled without touching the others. Rolling a key invalidates the old value immediately. -- The root key cannot be deleted, only rolled. -- Keys are workspace-scoped, not user-scoped. Removing a person from the workspace does not invalidate keys they created; roll them if that matters. +- Keep the key in an environment variable named `DRIVER_API_KEY`. Don't commit it or paste it into prompts, tickets or logs. +- One key per integration, so a leaked key can be rolled without touching the others. Rolling invalidates the old value immediately. +- The root key can't be deleted, only rolled. +- Keys belong to the workspace, not the person. Removing someone from the workspace doesn't invalidate keys they created. Roll them if that matters. ## Which workspace am I in -A key belongs to exactly one workspace, and every session it creates is billed to that workspace and listed under its Telemetry. If you belong to several workspaces, check the workspace switcher at the top of the dashboard before creating a key. +A key belongs to one workspace. Every session it creates is billed to that workspace and listed under its Telemetry. If you're in several workspaces, check the switcher at the top of the dashboard before creating a key. ## Errors -`401` means the key is missing, malformed or rolled. `402` and `403` are about the workspace's credits and plan, not the key; see [Errors](/docs/sessions/errors). +`401`: the key is missing, malformed or rolled. `402` and `403` are about the workspace's credits and plan, not the key. See [Errors](/docs/sessions/errors). diff --git a/docs/account/billing.mdx b/docs/account/billing.mdx index ce069b9..5848ded 100644 --- a/docs/account/billing.mdx +++ b/docs/account/billing.mdx @@ -1,11 +1,11 @@ --- title: "Billing" -description: "Sessions are metered by bandwidth against the workspace's credits. Two endpoints expose the balance and the plan." +description: "Sessions are metered by bandwidth against the workspace's credits. Two endpoints expose balance and plan." --- -Driver bills on **bandwidth**: the bytes a session moves, metered per megabyte and charged against the workspace's credits. A plan includes a monthly allowance and a concurrency limit; usage beyond the allowance draws on purchased credits. A running session that moves no bytes costs nothing, but it holds one of the plan's concurrent-browser slots until it stops. +Driver bills on **bandwidth**: bytes a session moves, metered per megabyte against the workspace's credits. A plan includes a monthly allowance and a concurrency limit; usage past the allowance draws on purchased credits. A session that moves no bytes costs nothing but holds a concurrency slot until it stops. -Keeping sessions short and stopping them when the job is done is the whole cost-control story. `duration` on create caps what a forgotten session can cost. Warm browsers in a [pool](/docs/sessions/pools) are not billed and hold no concurrency slot; a browser becomes a billed, counted session when it is acquired, and its lease timeout caps how long it can be held. +Cost control is short sessions, stopped when the job is done. `duration` on create caps what a forgotten session can cost. Warm browsers in a [pool](/docs/sessions/pools) aren't billed and hold no slot; a browser becomes a billed, counted session when acquired, and the lease timeout caps how long it can be held. ## Balance @@ -27,18 +27,18 @@ balance = balance["balance"] { "balance": { "balance_cents": 1250, "network_mb_remaining": 500, "status": "active" } } ``` -`balance_cents` can go below zero. `network_mb_remaining` (the included allowance still unused this period) and `status` (the plan key, for example `free`) are present for workspace accounts and absent on a legacy account. Sessions are refused with `402` once the balance is negative and the allowance is gone. +`balance_cents` can go below zero. `network_mb_remaining` (allowance left this period) and `status` (the plan key, for example `free`) are present on workspace accounts and absent on legacy accounts. Sessions are refused with `402` once the balance is negative and the allowance is gone. ## Billing details -`GET /v1/account/billing` returns the full picture: `orgId`; `status` (`legacy` for an account not yet moved to workspace billing); the plan (`plan_key`, `display_name`, `price_monthly_cents`, `included_network_mb`, `overage_millicents_per_mb`, `concurrent_browsers`, `max_session_seconds`); the current period (`start_ms`, `end_ms`, `included_network_mb`, `network_mb_used`, `network_mb_remaining`); the wallet (`balance_cents`, which can be negative, and `balance_millicents`); and the subscription when there is one (`subscription_id`, `provider`, `status`, `current_period_start`, `current_period_end`, `cancel_at_period_end`). `plan`, `period` and `subscription` are `null` on a legacy account. Times ending in `_ms` and the subscription period bounds are Unix epoch milliseconds. +`GET /v1/account/billing` returns everything: `orgId`; `status` (`legacy` for an account not yet on workspace billing); the plan (`plan_key`, `display_name`, `price_monthly_cents`, `included_network_mb`, `overage_millicents_per_mb`, `concurrent_browsers`, `max_session_seconds`); the current period (`start_ms`, `end_ms`, `included_network_mb`, `network_mb_used`, `network_mb_remaining`); the wallet (`balance_cents`, can be negative, and `balance_millicents`); and the subscription if there is one (`subscription_id`, `provider`, `status`, `current_period_start`, `current_period_end`, `cancel_at_period_end`). `plan`, `period` and `subscription` are `null` on a legacy account. Fields ending in `_ms` and the subscription period bounds are Unix epoch milliseconds. ```bash curl https://api.driver.dev/v1/account/billing -H "Authorization: Bearer $DRIVER_API_KEY" ``` -Use it to show usage in your own tooling or to stop launching sessions before you hit the limit. Amounts are cents; `millicents` fields are for exact arithmetic (1000 millicents = 1 cent). +Use it for usage in your own tooling, or to stop launching before you hit the limit. Amounts are cents; `millicents` fields are for exact arithmetic (1000 millicents = 1 cent). ## Where to change things -Plan changes, adding credits, auto top-up, invoices and the card on file all live under **Billing** in the [dashboard](https://app.driver.dev/settings/billing). There is no API for payments. +Plan changes, credits, auto top-up, invoices and the card on file are under **Billing** in the [dashboard](https://app.driver.dev/settings/billing). There's no API for payments. diff --git a/docs/account/dashboard.mdx b/docs/account/dashboard.mdx index a43bbc0..5539703 100644 --- a/docs/account/dashboard.mdx +++ b/docs/account/dashboard.mdx @@ -1,22 +1,22 @@ --- title: "Dashboard" -description: "What lives in the dashboard: sessions and live view, the Workbench, profiles, keys, workspace members and billing." +description: "Sessions and live view, the Workbench, profiles, pools, keys, members and billing." --- -The [dashboard](https://app.driver.dev) is the other half of the API. Everything below is per workspace; switch workspaces at the top of the sidebar. +The [dashboard](https://app.driver.dev) is the other half of the API. Everything is per workspace; switch at the top of the sidebar. -| Area | What you do there | +| Area | What's there | |---|---| -| **Overview** | Running browsers against the plan limit, today's sessions, bandwidth this period, credits, and a 30-day chart. **Launch browser** starts a session with options and opens its live view without writing code. | -| **Workbench** | A script editor next to a live browser and an output log. Pick a template, set country, network, profile, CAPTCHA solver and proxy, press Run. The quickest way to try options before committing them to code. | -| **Profiles** | The workspace's saved [profiles](/docs/options/profiles). Launch a session on a profile to sign in by hand. | -| **Browser pools** | Create and manage [pools](/docs/sessions/pools) of warm browsers: size, country, profile, start URL, lease timeout; pause, flush or delete them; see how many are ready and in use. **Launch browser** on the Overview can take a browser from any ready pool. | -| **Sessions** (Telemetry) | Every session, filterable by status, with a record page per session: live view while running, then node, country, network, bandwidth, cost and timestamps. Stop one, or stop all running. | -| **Workspace** | Name, members and invitations. Admins manage members, keys and billing. | +| **Overview** | Running browsers against the plan limit, today's sessions, bandwidth this period, credits, a 30-day chart. **Launch browser** starts a session with options and opens its live view. No code needed. | +| **Workbench** | Script editor, live browser, output log. Pick a template, set country, network, profile, CAPTCHA solver and proxy, press Run. Try options here before writing them into code. | +| **Profiles** | The workspace's saved [profiles](/docs/options/profiles). Launch a session on one to sign in by hand. | +| **Browser pools** | [Pools](/docs/sessions/pools) of warm browsers: size, country, profile, start URL, lease timeout. Pause, flush or delete them; see how many are ready and in use. **Launch browser** on the Overview can take a browser from any ready pool. | +| **Sessions** (Telemetry) | Every session, filterable by status. Each record: live view while running, then node, country, network, bandwidth, cost and timestamps. Stop one, or stop all. | +| **Workspace** | Name, members, invitations. Admins manage members, keys and billing. | | **API keys** | The root key and named keys: create, roll, delete. Also a ready-to-paste briefing for your coding agent. | -| **Billing** | Plan, credits, auto top-up, credit history, the billing portal for invoices and cards. | +| **Billing** | Plan, credits, auto top-up, credit history, and the billing portal for invoices and cards. | | **Support** | Book a call or write to the team. | ## Hand off to an agent -The Overview and API keys pages carry a **Copy agent prompt** button. It produces a briefing for Claude Code, Cursor or any coding agent with the endpoints, the rules and, if you opt in, your API key. Pair it with the [For agents](/docs/start/for-agents) page here. +**Copy agent prompt** on the Overview and API keys pages gives Claude Code, Cursor or any coding agent the endpoints, the rules and, if you opt in, your API key. Pair it with [For agents](/docs/start/for-agents). diff --git a/docs/frameworks/browser-use.mdx b/docs/frameworks/browser-use.mdx index 3614154..784f837 100644 --- a/docs/frameworks/browser-use.mdx +++ b/docs/frameworks/browser-use.mdx @@ -1,9 +1,9 @@ --- title: "Browser Use" -description: "Run Browser Use agents on a Driver browser by handing the agent the session's cdpUrl." +description: "Run Browser Use agents on a Driver browser by passing the session's cdpUrl." --- -[Browser Use](https://browser-use.com) builds autonomous agents on top of a Chromium it controls. Give it a Driver session's `cdpUrl` and the agent drives real Chrome on a residential network instead of a local headless browser. +[Browser Use](https://browser-use.com) runs autonomous agents on a Chromium it controls. Pass it a Driver session's `cdpUrl` and it drives real Chrome on a residential network instead. ```bash pip install browser-use requests @@ -39,11 +39,11 @@ async def main(): asyncio.run(main()) ``` -`ChatBrowserUse` uses a Browser Use Cloud model key (`BROWSER_USE_API_KEY`); any LangChain-style chat model works in its place. +`ChatBrowserUse` takes a Browser Use Cloud model key (`BROWSER_USE_API_KEY`). Any LangChain-style chat model works in its place. ## Notes -- Agents take many steps; set `duration` to cover the task (up to 3600 seconds) and stop the session in `finally`. -- Browser Use drives the browser over CDP directly rather than through Playwright, so Patchright is not in the picture. The fingerprint concern is the same, though: it comes from the CDP commands a client issues, and an agent framework issues more of them than a plain script. Expect challenges on protected sites. -- Pin the `browser-use` version you tested with; its `Browser`/`Agent` constructors have changed between releases. Passing `cdp_url` is what stops it from launching or downloading a browser of its own. -- Watch the agent through the session's [live view](/docs/sessions/live-view); it is the fastest way to see where an agent goes wrong. +- Agents take many steps. Set `duration` to cover the task (up to 3600 seconds) and stop the session in `finally`. +- Browser Use talks CDP directly, not through Playwright, so Patchright doesn't apply. The fingerprint problem is the same, and an agent framework issues more CDP commands than a plain script. Expect challenges on protected sites. +- Pin the `browser-use` version you tested with. The `Browser` and `Agent` constructors have changed between releases. Passing `cdp_url` is what stops it from launching or downloading its own browser. +- Watch the agent through the session's [live view](/docs/sessions/live-view). It's the fastest way to see where it goes wrong. diff --git a/docs/frameworks/crawl4ai.mdx b/docs/frameworks/crawl4ai.mdx index e9d16b1..4463522 100644 --- a/docs/frameworks/crawl4ai.mdx +++ b/docs/frameworks/crawl4ai.mdx @@ -3,7 +3,7 @@ title: "Crawl4AI" description: "Point Crawl4AI's managed browser at a Driver session to crawl from real Chrome on a residential network." --- -[Crawl4AI](https://crawl4ai.com) turns pages into LLM-ready Markdown. Configure it to use a Driver session instead of launching its own browser. +[Crawl4AI](https://crawl4ai.com) turns pages into LLM-ready Markdown. Configure it to attach to a Driver session instead of launching its own browser. ```bash pip install crawl4ai requests @@ -37,7 +37,7 @@ asyncio.run(main()) ## Notes -- `browser_mode="custom"` with `cdp_url` tells Crawl4AI to attach to the browser you give it instead of launching one; `use_managed_browser=True` makes it drive that browser over CDP rather than through a Playwright launch. Leaving the `async with` block closes Crawl4AI's connection only; the session runs until you stop it. -- Crawl4AI is Playwright underneath and injects its own scripts, so it carries the stock-Playwright fingerprint. Use it for sites that do not challenge automation. -- Pin the `crawl4ai` version you tested with. Its post-install step downloads a browser you will not use; that is harmless. -- Crawl several URLs in one session; creating the session is the expensive step. +- `browser_mode="custom"` with `cdp_url` attaches Crawl4AI to the browser you give it. `use_managed_browser=True` makes it drive that browser over CDP instead of through a Playwright launch. Leaving the `async with` block closes Crawl4AI's connection only; the session runs until you stop it. +- Crawl4AI is Playwright underneath and injects its own scripts, so it carries the stock-Playwright fingerprint. Use it on sites that don't challenge automation. +- Pin the `crawl4ai` version you tested with. Its post-install step downloads a browser you won't use. Harmless. +- Crawl several URLs in one session. Creating the session is the expensive step. diff --git a/docs/frameworks/patchright.mdx b/docs/frameworks/patchright.mdx index 419ea58..6457dda 100644 --- a/docs/frameworks/patchright.mdx +++ b/docs/frameworks/patchright.mdx @@ -1,19 +1,19 @@ --- title: "Patchright" -description: "The recommended client for Driver: Playwright's API with the automation fingerprints removed. Node and Python." +description: "Playwright with the automation fingerprints removed. Use it for every Driver integration. Node and Python." --- -[Patchright](https://github.com/Kaliiiiiiiiii-Vinyzu/patchright) is a patched build of Playwright. Same API, same docs, same selectors; the difference is what it sends over CDP. Use it for every Driver integration. +[Patchright](https://github.com/Kaliiiiiiiiii-Vinyzu/patchright) is a patched build of Playwright. Same API, same docs, same selectors. The difference is what it sends over CDP. ## Why Patchright -Stock Playwright (and Puppeteer) drive the browser through CDP calls that a normal Chrome never makes on its own, and anti-bot vendors look for exactly those: +Stock Playwright and Puppeteer drive the browser with CDP calls a normal Chrome never makes on its own. Anti-bot vendors look for exactly those: -- `Runtime.enable`, which makes every page's JavaScript world observable and leaves detectable traces in the page. +- `Runtime.enable`, which exposes every page's JavaScript world and leaves traces in the page. - Console and binding hooks (`Runtime.addBinding`, `Page.addScriptToEvaluateOnNewDocument`) that Playwright installs for its own bookkeeping. -- Command-line switches and the `navigator.webdriver` flag in launched browsers (not relevant when connecting to Driver, but Patchright removes them anyway). +- Command-line switches and `navigator.webdriver` in launched browsers. Not relevant when connecting to Driver, but Patchright removes them anyway. -Driver gives you a real Chrome on real hardware and a residential network. If the client then announces itself through CDP, that work is wasted and the session gets challenged or blocked. Patchright removes those signals while keeping the Playwright API, so the switch costs nothing. +Driver gives you real Chrome on real hardware. If the client then announces itself over CDP, the session gets challenged anyway. Patchright removes those signals and keeps the Playwright API. ## Install @@ -26,7 +26,7 @@ pip install patchright ``` -No browser download is needed; you connect to Driver's Chrome. +No browser download. You connect to Driver's Chrome. ## Connect @@ -57,7 +57,7 @@ try { await page.goto("https://example.com"); console.log(await page.title()); } finally { - await browser.close(); // your connection only; the session keeps running until you stop it + await browser.close(); // your connection only; the session runs until you stop it } } finally { await fetch(`${API}/v1/browser/session?sessionId=${session.sessionId}`, { @@ -122,7 +122,7 @@ asyncio.run(main()) ## Migrating from Playwright -Change the import. That is the whole migration. +Change the import. That's the whole migration. ```diff - import { chromium } from "playwright"; @@ -134,10 +134,10 @@ Change the import. That is the whole migration. + from patchright.sync_api import sync_playwright ``` -Remove any `chromium.launch()` and any `playwright install` step: Driver provides the browser. +Delete any `chromium.launch()` and any `playwright install` step. Driver provides the browser. -Coming from Puppeteer is a rewrite, not a rename: Patchright has Playwright's API. Either keep Puppeteer with the [connect-only setup](/docs/frameworks/puppeteer) and accept its fingerprint, or port to Patchright. +Coming from Puppeteer is a rewrite, not a rename: Patchright has Playwright's API. Either keep Puppeteer with the [connect-only setup](/docs/frameworks/puppeteer) and accept its fingerprint, or port. -## Things Patchright does not fix +## What Patchright doesn't fix -Patchright removes the client's fingerprints. It cannot undo choices in your script that look automated: fresh contexts for every page, broad `page.route` interception, injected init scripts, overridden `navigator` properties, or clicking a thousand things per minute. The [best practices](/docs/start/best-practices) cover those. +Patchright fixes the client, not your script. A fresh context per page, broad `page.route` interception, injected init scripts, overridden `navigator` properties or a thousand clicks a minute still look like a bot. See [best practices](/docs/start/best-practices). diff --git a/docs/frameworks/playwright.mdx b/docs/frameworks/playwright.mdx index c51fe4e..9f3899d 100644 --- a/docs/frameworks/playwright.mdx +++ b/docs/frameworks/playwright.mdx @@ -1,13 +1,13 @@ --- title: "Playwright" -description: "Stock Playwright connects to Driver over CDP, with fingerprints that get sessions blocked on protected sites. Prefer Patchright." +description: "Stock Playwright connects to Driver over CDP but carries fingerprints that get sessions blocked. Prefer Patchright." --- -Stock Playwright issues CDP commands that anti-bot systems detect (`Runtime.enable` and its bookkeeping hooks). On sites that look for automation, sessions driven by stock Playwright get challenged or blocked. Use [Patchright](/docs/frameworks/patchright) instead: same API, one import change. +Stock Playwright issues CDP commands anti-bot systems detect (`Runtime.enable` and its bookkeeping hooks), and protected sites block the session. Use [Patchright](/docs/frameworks/patchright): same API, one import change. -If you have measured that your targets do not care, stock Playwright works unchanged: +If you've measured that your targets don't care, stock Playwright works unchanged: ```typescript TypeScript @@ -33,8 +33,8 @@ with sync_playwright() as p: ``` -Install with `npm install playwright` or `pip install playwright`. Do not run `playwright install`; the browser is Driver's. +Install with `npm install playwright` or `pip install playwright`. Don't run `playwright install`; the browser is Driver's. ## Everything else -Contexts, tabs, disconnecting versus stopping, reconnecting and timeouts are the same as for Patchright; see [Connect over CDP](/docs/sessions/connect). +Contexts, tabs, disconnecting versus stopping, reconnecting and timeouts work the same as with Patchright. See [Connect over CDP](/docs/sessions/connect). diff --git a/docs/frameworks/puppeteer.mdx b/docs/frameworks/puppeteer.mdx index e5b0505..be77d27 100644 --- a/docs/frameworks/puppeteer.mdx +++ b/docs/frameworks/puppeteer.mdx @@ -4,10 +4,10 @@ description: "puppeteer-core connects to Driver with browserWSEndpoint. Same det --- -Puppeteer, like stock Playwright, makes CDP calls that anti-bot systems fingerprint. For protected sites use [Patchright](/docs/frameworks/patchright). If your code base is Puppeteer and the targets are not hostile, the connection below works. +Puppeteer makes the same fingerprintable CDP calls as stock Playwright. For protected sites use [Patchright](/docs/frameworks/patchright). If your code base is Puppeteer and the targets aren't hostile, this works. -Install `puppeteer-core`, not `puppeteer`: Driver provides the browser, so nothing should be downloaded. +Install `puppeteer-core`, not `puppeteer`. Driver provides the browser; nothing should be downloaded. ```bash npm install puppeteer-core @@ -31,8 +31,8 @@ if (!res.ok) throw new Error(`Driver ${res.status}: ${(await res.json().catch(() const session = await res.json(); try { - // defaultViewport: null keeps the window's real size; Puppeteer's default is an 800x600 viewport, - // which would undo the windowSize you set on create and mismatch outerWidth. + // defaultViewport: null keeps the window's real size. Puppeteer's default 800x600 viewport + // would undo the windowSize you set on create and mismatch outerWidth. const browser = await puppeteer.connect({ browserWSEndpoint: session.cdpUrl, defaultViewport: null }); try { const pages = await browser.pages(); @@ -41,7 +41,7 @@ try { await page.goto("https://example.com"); console.log(await page.title()); } finally { - await browser.disconnect(); // ends your connection only + await browser.disconnect(); // your connection only } } finally { await fetch(`${API}/v1/browser/session?sessionId=${session.sessionId}`, { @@ -53,8 +53,8 @@ try { ## Notes -- End your connection with `browser.disconnect()`, then stop the session through the API. Do not use `browser.close()`: it tells the remote Chrome to quit, and the session record is then closed from the browser's side rather than by your stop call. -- `browser.pages()` returns the tab the session opened with. Reuse it rather than creating a fresh one. -- Viewport: connect with `defaultViewport: null` and prefer `windowSize` on create over `page.setViewport`, see [Window size](/docs/options/window-size). -- The hooks to avoid, in Puppeteer's names: `page.setRequestInterception`, `page.evaluateOnNewDocument`, `page.exposeFunction`, `page.setUserAgent`, `page.emulateTimezone`, `page.emulate`. See [Best practices](/docs/start/best-practices). -- Pin the `puppeteer-core` version you tested with; each release targets a Chrome revision. +- End your connection with `browser.disconnect()`, then stop the session through the API. `browser.close()` tells the remote Chrome to quit, so the session record closes from the browser's side instead of by your stop call. +- `browser.pages()` returns the tab the session opened with. Reuse it. +- Connect with `defaultViewport: null` and set `windowSize` on create instead of `page.setViewport`. See [Window size](/docs/options/window-size). +- Hooks to avoid, in Puppeteer's names: `page.setRequestInterception`, `page.evaluateOnNewDocument`, `page.exposeFunction`, `page.setUserAgent`, `page.emulateTimezone`, `page.emulate`. See [Best practices](/docs/start/best-practices). +- Pin the `puppeteer-core` version you tested with. Each release targets a Chrome revision. diff --git a/docs/frameworks/stagehand.mdx b/docs/frameworks/stagehand.mdx index c49a8f4..0d0a901 100644 --- a/docs/frameworks/stagehand.mdx +++ b/docs/frameworks/stagehand.mdx @@ -3,7 +3,7 @@ title: "Stagehand" description: "Natural-language act and extract on a Driver browser: connect Stagehand v4 to the session's cdpUrl." --- -[Stagehand](https://docs.stagehand.dev) adds `act`, `extract` and `observe` on top of a browser it attaches to. With Stagehand v4, connect it to a Driver session's `cdpUrl` through `localBrowser.connect`. +[Stagehand](https://docs.stagehand.dev) adds `act`, `extract` and `observe` on top of a browser it attaches to. With v4, connect it to a Driver session's `cdpUrl` through `localBrowser.connect`. ```bash npm install @browserbasehq/stagehand zod@~4.4.3 @@ -28,7 +28,7 @@ if (!res.ok) throw new Error(`Driver ${res.status}: ${(await res.json().catch(() const session = await res.json(); try { - // Attach to Driver's Chrome; Stagehand never launches or closes a browser it did not start. + // Attach to Driver's Chrome. Stagehand never launches or closes a browser it didn't start. const browser = await localBrowser.connect({ cdpUrl: session.cdpUrl }); const stagehand = await Stagehand.create({ browser, @@ -53,11 +53,11 @@ try { } ``` -Stagehand reads no environment variables itself: pass the model provider key in `model.apiKey`. +Stagehand reads no environment variables. Pass the model provider key in `model.apiKey`. ## Notes -- This is the v4 API (`localBrowser.connect`, `Stagehand.create`). Stagehand 2.x used `new Stagehand({ env: "LOCAL", localBrowserLaunchOptions: { cdpUrl } })`; if you are on 2.x, that is the equivalent. -- Stagehand instruments the page to work (it injects scripts and drives the browser over CDP), which is the kind of signal protected sites look for. On those sites, prefer plain [Patchright](/docs/frameworks/patchright) with explicit selectors. -- Pin the Stagehand version you tested with; the connect API changed between 2.x and v4. -- Reuse the page from `browser.context.pages()` rather than opening a new one. +- This is the v4 API (`localBrowser.connect`, `Stagehand.create`). On 2.x the equivalent is `new Stagehand({ env: "LOCAL", localBrowserLaunchOptions: { cdpUrl } })`. +- Stagehand injects scripts and drives the browser over CDP, which is what protected sites look for. On those sites use plain [Patchright](/docs/frameworks/patchright) with explicit selectors. +- Pin the Stagehand version you tested with. The connect API changed between 2.x and v4. +- Reuse the page from `browser.context.pages()` instead of opening a new one. diff --git a/docs/options/browser-types.mdx b/docs/options/browser-types.mdx index 48a6373..effc37f 100644 --- a/docs/options/browser-types.mdx +++ b/docs/options/browser-types.mdx @@ -1,15 +1,15 @@ --- title: "Browser types" -description: "The type field picks the browser class: hosted by default, with two stealth variants." +description: "type picks the browser class: hosted by default, plus two stealth variants." --- -`type` on [create](/docs/sessions/create) chooses what kind of Chrome serves the session. Leave it out unless a site gives you a reason to change it. +`type` on [create](/docs/sessions/create) picks which Chrome serves the session. Leave it out unless a site gives you a reason. | Type | What it is | When to use it | |---|---|---| | `hosted` (default) | Real Chrome on a hosted machine, native behaviour. | Almost everything. | -| `hosted_stealth` | Same browser with a conservative stealth policy. | Sites that challenge the default and you want the least invasive change. | -| `hosted_privacy` | Same browser with the aggressive stealth policy. | Hostile targets. Try `hosted_stealth` first; more masking is not always better. | +| `hosted_stealth` | Same browser, conservative stealth policy. | A site challenges the default and you want the smallest change. | +| `hosted_privacy` | Same browser, aggressive stealth policy. | Hostile targets. Try `hosted_stealth` first; more masking isn't always better. | ```json Request @@ -25,13 +25,13 @@ session = create_session(type="hosted_stealth", country="GB") ## What "real Chrome" means here -Hosted sessions run consumer Chrome builds on real hardware with a real display, not a headless container. Fingerprint surfaces (GPU, fonts, media devices, timing) are what a person's machine would show, and the network is residential in the country you asked for. That is why the [best practices](/docs/start/best-practices) tell you not to patch fingerprints from your script: the browser is already genuine, and patches make it less so. +Consumer Chrome on real hardware with a real display, not a headless container. GPU, fonts, media devices and timing are what a person's machine shows, and the network is residential in the country you asked for. That's why [best practices](/docs/start/best-practices) say not to patch fingerprints from your script: the browser is already genuine, and patches make it less so. ## Choosing between the stealth policies -The stealth policies change how the browser answers certain probes. `hosted_stealth` is the conservative set; it rarely breaks sites. `hosted_privacy` masks more and can change page behaviour on sites that rely on the masked APIs. Measure against your target rather than defaulting to the strongest setting. +The policies change how the browser answers certain probes. `hosted_stealth` rarely breaks sites. `hosted_privacy` masks more and can change page behaviour on sites that use the masked APIs. Measure against your target instead of defaulting to the strongest one. ## Related -- [Location](/docs/options/location) for country, timezone and language coherence -- [Proxies](/docs/options/proxies) when you need your own egress +- [Location](/docs/options/location): country, timezone and language +- [Proxies](/docs/options/proxies): your own egress diff --git a/docs/options/captcha-and-adblock.mdx b/docs/options/captcha-and-adblock.mdx index 17635df..f2b70cb 100644 --- a/docs/options/captcha-and-adblock.mdx +++ b/docs/options/captcha-and-adblock.mdx @@ -3,9 +3,11 @@ title: "CAPTCHA solving and ad blocking" description: "Two flags on create: captchaSolver loads Driver's solver into the session, adblock blocks ads." --- +Both flags default to `false`. + ## CAPTCHA solving -Set `captchaSolver: true` and the session's Chrome loads Driver's solver extension. Challenges that appear during the session are solved in the browser without any change to your script. +`captchaSolver: true` loads Driver's solver extension into the session's Chrome. Challenges that appear during the session are solved in the browser; your script doesn't change. ```json Request @@ -19,11 +21,11 @@ session = create_session(captchaSolver=True) ``` -Solving takes time; give steps that may hit a challenge a generous timeout and rely on your framework's auto-waiting rather than fixed sleeps. If a challenge is not solved, the [live view](/docs/sessions/live-view) lets a person complete it in the same session. +Solving takes time. Give steps that may hit a challenge a generous timeout and rely on your framework's auto-waiting, not fixed sleeps. If a challenge isn't solved, a person can finish it in the same session through the [live view](/docs/sessions/live-view). ## Ad blocking -Set `adblock: true` to block ads in the session. Pages load faster and use less bandwidth, which is what a session is billed on. Some sites detect ad blockers and change behaviour, so leave it off when fidelity matters. +`adblock: true` blocks ads in the session. Pages load faster and move fewer bytes, which is what you're billed on. Some sites detect ad blockers and change behaviour, so leave it off when fidelity matters. ```json Request @@ -36,5 +38,3 @@ const session = await createSession({ adblock: true }); session = create_session(adblock=True) ``` - -Both flags default to `false`. diff --git a/docs/options/countries.mdx b/docs/options/countries.mdx index 378279c..13662ca 100644 --- a/docs/options/countries.mdx +++ b/docs/options/countries.mdx @@ -3,9 +3,9 @@ title: "Countries" description: "Every country you can pass on create, with the timezones and languages it accepts. Generated from the API's locale catalog." --- -`country` accepts any of the 249 two-letter codes below. `timezone` must be one of that country's timezones and `language` one of its languages, or the request is rejected with `400`. Leave both out and Driver picks the first-listed timezone (or a managed-network region where it has one) and the country's primary language. US sessions always use `en-US`. See [Location](/docs/options/location) for how the three stay coherent. +`country` accepts any of the 249 codes below. `timezone` must be one of that country's timezones and `language` one of its languages, or the request is a `400`. Leave both out and you get the first-listed timezone (or a managed-network region where Driver has one) and the country's primary language. US sessions are always `en-US`. [Location](/docs/options/location) covers how the three stay coherent. -Availability of machines varies by country; a country with no machine free at the moment answers `503`. +Machine availability varies by country. No free machine in a country is a `503`. | Code | Country | Timezones | Languages | |---|---|---|---| diff --git a/docs/options/extensions.mdx b/docs/options/extensions.mdx index cd30ff0..d018760 100644 --- a/docs/options/extensions.mdx +++ b/docs/options/extensions.mdx @@ -1,15 +1,15 @@ --- title: "Extensions" -description: "Upload your own Chrome extensions and load them into hosted sessions. Available to accounts with extension access." +description: "Upload your own Chrome extensions and load them into hosted sessions. Enabled per account." --- -The extensions API is enabled per account. Without access, the endpoints return `403` (`Extensions API is not enabled for this account`); write to alex@driver.dev to request it. An upload without a file is rejected first with `No file uploaded. Send as multipart field "extension".` +Enabled per account. Without access the endpoints answer `403` (`Extensions API is not enabled for this account`); write to alex@driver.dev to request it. An upload without a file fails first with `No file uploaded. Send as multipart field "extension".` ## Upload an extension -Zip the extension's directory (the one containing `manifest.json`) and upload it. Driver packs it as a CRX and returns an `extensionId` to use on create. +Zip the directory containing `manifest.json` and upload it. Driver packs it as a CRX and returns an `extensionId` for create. ```bash cURL @@ -41,7 +41,7 @@ with open("my-extension.zip", "rb") as f: ## Load it into a session -Pass the internal `extensionId` (not the `crxExtensionId`) in `extensionIds`. Works on every browser type. +Pass the internal `extensionId` (not `crxExtensionId`) in `extensionIds`. Works on every browser type. ```json { "extensionIds": ["abc123def456"] } @@ -52,13 +52,13 @@ Pass the internal `extensionId` (not the `crxExtensionId`) in `extensionIds`. Wo | Call | What it does | |---|---| | `GET /v1/browser/extensions` | List your extensions. | -| `GET /v1/browser/extensions/{extensionId}` | One extension's details. | -| `PUT /v1/browser/extensions/{extensionId}` with a new zip | Replace the code; the id stays the same. | +| `GET /v1/browser/extensions/{extensionId}` | One extension. | +| `PUT /v1/browser/extensions/{extensionId}` with a new zip | Replace the code; the id stays. | | `DELETE /v1/browser/extensions/{extensionId}` | Remove it. | -The dashboard's **Extensions** page (visible once your account has access) offers the same upload and list. +The dashboard's **Extensions** page (shown once your account has access) does the same. ## Notes -- Manifest V3 extensions are expected. `name` and `version` in the response come from the manifest. -- An extension that opens tabs, injects scripts everywhere or rewrites requests changes how the browser looks to sites. Keep them narrow, for the same reasons as the [CDP hooks guidance](/docs/start/best-practices#keep-cdp-hooks-narrow). +- Manifest V3 is expected. `name` and `version` come from the manifest. +- An extension that opens tabs, injects scripts everywhere or rewrites requests changes how the browser looks to sites. Keep it narrow, for the same reasons as [CDP hooks](/docs/start/best-practices#keep-cdp-hooks-narrow). diff --git a/docs/options/location.mdx b/docs/options/location.mdx index 95994ef..36a70b8 100644 --- a/docs/options/location.mdx +++ b/docs/options/location.mdx @@ -1,9 +1,9 @@ --- title: "Location, timezone and language" -description: "Ask for a country and Driver makes the network, timezone and language agree. Override timezone and language only within that country." +description: "Pass a country; the network, timezone and language agree. Override timezone and language only within that country." --- -Pass a two-letter `country` and Driver places the session on a machine egressing from that country, then picks a timezone and browser language that belong there. The three always agree, which is what sites check. +Pass a two-letter `country` and the session runs on a machine egressing from that country, with a timezone and browser language that belong there. The three always agree, which is what sites check. Default `US`. ```json Request @@ -17,32 +17,30 @@ session = create_session(country="DE") ``` -`country` defaults to `US`. - ## Overriding timezone and language -Set `timezone` (IANA name) or `language` (BCP-47) when the default choice for the country is not what you want. Both must come from the chosen country's lists on [Countries](/docs/options/countries), or the request is rejected with `400` (`Unsupported timezone "Europe/Berlin" for country US.`). An unknown `country` is rejected the same way (`Unsupported country "XX".`). When you leave `timezone` out, Driver picks the country's dominant zone and, where it runs a managed network there, routes the session's traffic through that region so the network and the clock agree. +Set `timezone` (IANA name) or `language` (BCP-47) when the country's default isn't what you want. Both must come from that country's lists on [Countries](/docs/options/countries), or the request is a `400`: `Unsupported timezone "Europe/Berlin" for country US.` An unknown `country` fails the same way: `Unsupported country "XX".` Without `timezone`, you get the country's dominant zone, and where Driver runs a managed network there, traffic is routed through that region so the network and the clock agree. ```json { "country": "CA", "timezone": "America/Vancouver", "language": "fr-CA" } ``` -US sessions always use `en-US` (any other `language` is rejected). For other countries, Driver picks the country's primary language when you leave `language` out. Chrome reports the nearest variant it ships: a session created with `fr-CA` reported `navigator.language` as `fr-FR` with `fr` and `en-US` as fallbacks. +US sessions are always `en-US`; any other `language` is rejected. Elsewhere, leaving `language` out gets the country's primary language. Chrome reports the nearest variant it ships: `fr-CA` came back as `navigator.language` `fr-FR`, with `fr` and `en-US` as fallbacks. -The browser's `Accept-Language` header follows the same choice (a `de-DE` session sends `de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7`), so servers and scripts see one story. +`Accept-Language` follows the same choice. A `de-DE` session sends `de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7`. -Hosted sessions also provision matching text-to-speech voices where available, and disable Web Speech when a coherent voice is not, so that surface never contradicts the rest. +Hosted sessions get matching text-to-speech voices where available; where none exists, Web Speech is disabled so that surface can't contradict the rest. ## With your own proxy -If you supply a `proxyUrl` without `timezone` or `language`, Driver geolocates the proxy's exit and derives the country and timezone from it. A `country` you supply must match that exit. See [Proxies](/docs/options/proxies). +A `proxyUrl` without `timezone` or `language` makes Driver geolocate the proxy's exit and derive country and timezone from it. A `country` you pass must match that exit. See [Proxies](/docs/options/proxies). ## Availability -Placement is by available machines. If nothing is available in the requested country you get a `503` (with `Retry-After: 2`): back off with a cap, then tell the user or pick another country. +No free machine in the requested country is a `503` with `Retry-After: 2`. Back off with a cap, then tell the user or pick another country. ## Related - [Countries](/docs/options/countries): every supported country with its timezones and languages - [Browser types](/docs/options/browser-types) -- [Best practices](/docs/start/best-practices): do not override timezone or locale from your script +- [Best practices](/docs/start/best-practices): don't override timezone or locale from your script diff --git a/docs/options/node-reuse.mdx b/docs/options/node-reuse.mdx index 8ebc39b..a517a1b 100644 --- a/docs/options/node-reuse.mdx +++ b/docs/options/node-reuse.mdx @@ -3,7 +3,7 @@ title: "Node reuse" description: "nodeId runs a session on the machine that served an earlier one. Rarely needed; overrides country and type." --- -Every session response includes `servedBy`, the id of the machine that ran it. Pass that value as `nodeId` on a later create to land on the same machine again. +Every session response includes `servedBy`, the id of the machine that ran it. Pass it as `nodeId` on a later create to land on the same machine. ```typescript TypeScript @@ -20,12 +20,12 @@ again = create_session(nodeId=first["servedBy"]) ## When it helps -- A site keyed something to the machine (rare; most keying is by cookies, which [profiles](/docs/options/profiles) handle better). +- A site keyed something to the machine. Rare; most keying is by cookies, which [profiles](/docs/options/profiles) handle better. - Reproducing a problem that only happens on one machine. ## Rules -- `nodeId` takes precedence over `country` and `type`. You get that machine, wherever it is. -- If the machine is busy or gone, the request fails instead of falling back to another machine. +- `nodeId` overrides `country` and `type`. You get that machine, wherever it is. +- If the machine is busy or gone, the request fails. No fallback to another machine. -For most workloads, leave `nodeId` out and let placement pick a machine in the country you asked for. +For most workloads leave `nodeId` out and let placement pick a machine in the country you asked for. diff --git a/docs/options/profiles.mdx b/docs/options/profiles.mdx index 3fff294..4b86d6e 100644 --- a/docs/options/profiles.mdx +++ b/docs/options/profiles.mdx @@ -1,9 +1,9 @@ --- title: "Profiles" -description: "Save a session's cookies and browser state under a name, and start later sessions already signed in." +description: "Save a session's cookies and browser state under a name. Later sessions on that name start signed in." --- -A profile is a full browser profile stored under a name you choose. Start a session with `profile: { name, persist: true }`, sign in, stop the session: the state is saved. Start another session with the same name and it opens signed in. A [browser pool](/docs/sessions/pools) can load a profile into every warm browser too; keep `persist` off there, since several browsers writing the same profile would overwrite one another. +A profile is a full browser profile stored under a name you choose. Create a session with `profile: { name, persist: true }`, sign in, stop it: the state is saved. The next session with that name opens signed in. A [browser pool](/docs/sessions/pools) can load a profile into every warm browser; keep `persist` off there, or the browsers overwrite one another. ```json Request @@ -27,9 +27,9 @@ next_session = create_session(profile={"name": "shop-account", "persist": True}) | Field | Type | Meaning | |---|---|---| | `name` | string | The profile to load. Created on first use. | -| `persist` | boolean | Save the profile when the session ends. Defaults to `false`, which loads it read-only: changes made during the session are discarded. | +| `persist` | boolean | Save when the session ends. Default `false`: read-only, changes are discarded. | -Profiles are supported on every browser type. +Profiles work on every browser type. ## Listing and deleting @@ -57,13 +57,13 @@ requests.delete( ``` -Deleting is idempotent: an unknown name also answers `{ "success": true }`. The list is not paginated and returns every profile in the workspace, so delete the ones you no longer need. The dashboard's **Profiles** page shows the same list, and lets you launch a session on a profile to sign in by hand through the live view. +Delete is idempotent: an unknown name also answers `{ "success": true }`. The list isn't paginated and returns every profile in the workspace, so delete what you no longer need. The dashboard's **Profiles** page shows the same list and can launch a session on a profile so you sign in by hand through the live view. ## Practical notes -- One profile, one session at a time. Two concurrent sessions on the same persisted profile will overwrite each other's state when they stop; nothing refuses the second session. -- The profile is written as the session ends. Before starting the next session on the same name, wait until `GET /v1/browser/session` reports the previous one as `completed`. -- Keep the same `country` for a profile across runs. A profile that signed in from Germany and returns from Brazil looks like a stolen session to the site. -- Profiles hold everything a browser profile holds: cookies, local storage, IndexedDB, service workers. Delete a profile when the account it belongs to is no longer needed. -- As in desktop Chrome, cookies without an expiry (session cookies) are dropped when the browser closes and do not carry over. Sites that keep you signed in set persistent cookies, which do; a login that only sets a session cookie has to be repeated each run. -- The next session on the profile can run on a different machine; the state travels with the name. +- One profile, one session at a time. Two concurrent sessions on the same persisted profile overwrite each other's state when they stop. Nothing refuses the second. +- The profile is written as the session ends. Wait for `GET /v1/browser/session` to report `completed` before starting the next session on the same name. +- Keep the same `country` across runs. A profile that signed in from Germany and comes back from Brazil looks like a stolen session. +- A profile holds everything Chrome's does: cookies, local storage, IndexedDB, service workers. Delete it when the account is no longer needed. +- As in desktop Chrome, cookies without an expiry (session cookies) are dropped when the browser closes. Sites that keep you signed in set persistent cookies, which carry over; a login that only sets a session cookie has to be repeated each run. +- The next session on a profile can run on a different machine. The state travels with the name. diff --git a/docs/options/proxies.mdx b/docs/options/proxies.mdx index f70e83b..fb0eeca 100644 --- a/docs/options/proxies.mdx +++ b/docs/options/proxies.mdx @@ -1,9 +1,9 @@ --- title: "Proxies" -description: "Sessions egress from Driver's residential network by default. Pass proxyUrl to route through your own SOCKS5 or TCPerfect endpoint." +description: "Sessions egress from Driver's residential network by default. proxyUrl routes through your own SOCKS5 or TCPerfect endpoint." --- -By default a session's traffic leaves from a residential connection in the requested country, and you do not configure anything. Set `proxyUrl` when you need a specific egress: your own proxy provider, a sticky IP, or a TCPerfect address. +Traffic leaves from a residential connection in the requested country by default. Set `proxyUrl` when you need a specific egress: your own provider, a sticky IP, or a TCPerfect address. ```json Request @@ -22,21 +22,21 @@ session = create_session(proxyUrl="socks5://user:pass@proxy.example.com:1080") | Scheme | Notes | |---|---| | `socks5://user:pass@host:port` | DNS resolved on the session's machine. | -| `socks5h://user:pass@host:port` | DNS resolved through the proxy. Use this when the proxy's location should also decide DNS answers. | -| `tcperfect://user:pass@host:port/exit-ip` | Driver's TCPerfect network: a proxied connection that keeps the machine's own TCP fingerprint intact. | +| `socks5h://user:pass@host:port` | DNS resolved through the proxy. Use it when the proxy's location should decide DNS answers too. | +| `tcperfect://user:pass@host:port/exit-ip` | Driver's TCPerfect network: proxied, with the machine's own TCP fingerprint kept intact. | -The proxy must be reachable from Driver's machines. HTTP proxies are not supported. +The proxy must be reachable from Driver's machines. HTTP proxies aren't supported. ## Location coherence -When you pass a SOCKS URL without `timezone` or `language`, Driver geolocates the proxy's exit before launch and derives the session's country and timezone from it, so the browser does not claim New York while its packets come from Frankfurt. If you also pass `country`, it must match the exit or the request is rejected with `400`. +Given a SOCKS URL and no `timezone` or `language`, Driver geolocates the proxy's exit before launch and derives the session's country and timezone from it. The browser won't claim New York while its packets come from Frankfurt. A `country` you also pass must match the exit, or the request is a `400`. ## Canada -Hosted sessions in Canada that do not specify a proxy egress through Driver's TCPerfect inventory automatically; nothing to configure. +Hosted sessions in Canada without a proxy egress through Driver's TCPerfect inventory. Nothing to configure. ## If the proxy cannot be reached -Driver connects through the proxy before launch to find its exit. A proxy that does not answer is rejected with `400` and the message `Could not determine SOCKS5 proxy egress location; verify the proxy or specify both timezone and language.` Supplying both `timezone` and `language` skips the lookup; the session then starts even if the proxy is unreachable, and pages fail to load inside it. An `http://` URL is rejected outright. +Driver connects through the proxy before launch to find its exit. A proxy that doesn't answer is a `400`: `Could not determine SOCKS5 proxy egress location; verify the proxy or specify both timezone and language.` Passing both `timezone` and `language` skips the lookup; the session then starts even with a dead proxy, and pages fail to load inside it. An `http://` URL is rejected outright. -Credentials travel in the create request only; they are not stored on the session record. +Credentials travel in the create request only. They aren't stored on the session record. diff --git a/docs/options/window-size.mdx b/docs/options/window-size.mdx index 9ff1b09..6b3bd5b 100644 --- a/docs/options/window-size.mdx +++ b/docs/options/window-size.mdx @@ -3,12 +3,12 @@ title: "Window and display size" description: "windowSize sets the outer browser window; displaySize sets the virtual display behind it." --- -Two fields, both `WIDTHxHEIGHT`, width 320 to 16384 and height 200 to 16384. +Both fields take `WIDTHxHEIGHT`, width 320 to 16384, height 200 to 16384. | Field | What it sets | Notes | |---|---|---| -| `windowSize` | The outer Chrome window, as the page will see it through `window.outerWidth/Height`. | The default depends on the machine. | -| `displaySize` | The virtual display the window sits on. | Usually leave it alone; set it when a site needs a screen larger than the window. | +| `windowSize` | The outer Chrome window, what the page sees as `window.outerWidth/Height`. | Default depends on the machine. | +| `displaySize` | The virtual display the window sits on. | Leave it alone unless a site needs a screen larger than the window. | ```json Request @@ -29,11 +29,11 @@ session = create_session(windowSize="1920x1040") | `1920x1040` | Full HD desktop with a taskbar | | `1536x864` | Scaled 1080p laptop | | `1440x900` | 13-inch MacBook | -| `1366x768` | Common budget laptop | -| `390x844` | Phone-sized window (the browser is still desktop Chrome) | +| `1366x768` | Budget laptop | +| `390x844` | Phone-sized window (still desktop Chrome) | -Pick a size that fits the screen the session's machine would plausibly have. A `windowSize` larger than the display is clamped. +Pick a size the session's machine would plausibly have. A `windowSize` larger than the display is clamped. ## Resizing after connecting -Prefer setting the size on create. Changing the viewport from your framework (`page.setViewportSize` in Playwright, `page.setViewport` in Puppeteer) resizes only the content area and leaves `outerWidth` where it was, which is one of the mismatches sites look for. Puppeteer applies an 800x600 viewport on connect unless you pass `defaultViewport: null`, so always pass it. +Set the size on create. `page.setViewportSize` (Playwright) and `page.setViewport` (Puppeteer) resize only the content area and leave `outerWidth` alone, a mismatch sites look for. Puppeteer applies an 800x600 viewport on connect unless you pass `defaultViewport: null`, so always pass it. diff --git a/docs/sessions/connect.mdx b/docs/sessions/connect.mdx index dbedafe..30685ce 100644 --- a/docs/sessions/connect.mdx +++ b/docs/sessions/connect.mdx @@ -1,12 +1,12 @@ --- title: "Connect over CDP" -description: "Attach your automation framework to a running Driver session through its cdpUrl." +description: "Attach your framework to a running session through its cdpUrl." --- -`cdpUrl` is a WebSocket endpoint to the session's Chrome. Any client that speaks the Chrome DevTools Protocol can attach to it. You attach to a running browser; you never launch one. +`cdpUrl` is a WebSocket endpoint to the session's Chrome. Any CDP client can attach. You never launch a browser. -Use [Patchright](/docs/frameworks/patchright) rather than stock Playwright or Puppeteer. It is Playwright's API minus the CDP calls that anti-bot systems fingerprint, so Playwright code moves over by changing one import; Puppeteer code needs porting. Sessions driven by stock frameworks get blocked on sites that look for automation. +Use [Patchright](/docs/frameworks/patchright), not stock Playwright or Puppeteer. It's Playwright's API minus the CDP calls anti-bot systems fingerprint; Playwright code moves over by changing one import, Puppeteer code needs porting. Stock frameworks get blocked on sites that look for automation. ## Patchright (recommended) @@ -22,7 +22,7 @@ const page = context.pages()[0] ?? (await context.newPage()); await page.goto("https://example.com"); console.log(await page.title()); -await browser.close(); // closes your connection; the session keeps running until you stop it +await browser.close(); // your connection only; the session runs until you stop it ``` ```python Python from patchright.sync_api import sync_playwright @@ -49,25 +49,25 @@ with sync_playwright() as p: ## Reuse the context and tab -The browser opens with one context and one tab. Reuse them. A session that opens fresh incognito contexts and blank pages behaves differently from a person's Chrome, and sites notice. +The browser opens with one context and one tab. Reuse them; fresh incognito contexts and blank pages don't look like a person's Chrome. ```typescript const context = browser.contexts()[0] ?? (await browser.newContext()); const page = context.pages()[0] ?? (await context.newPage()); ``` -If your task genuinely needs several tabs, open them from the existing context with `context.newPage()`. +Need more tabs? `context.newPage()` on the existing context. ## Disconnecting versus stopping -Closing your client (`browser.close()` in Patchright and Playwright, `browser.disconnect()` in Puppeteer) ends your connection. The session keeps running, and billing continues, until you call [`DELETE /v1/browser/session`](/docs/sessions/lifecycle#stop-a-session) or the session reaches its `duration`. You can disconnect and reconnect to the same `cdpUrl` as often as you like while the session is active. +`browser.close()` (Patchright, Playwright) and `browser.disconnect()` (Puppeteer) end your connection only. The session runs, and bills, until [`DELETE /v1/browser/session`](/docs/sessions/lifecycle#stop-a-session) or its `duration`. Reconnect to the same `cdpUrl` as often as you like while it's active. ## Reconnecting -If your process crashes, fetch the session with [`GET /v1/browser/session`](/docs/sessions/lifecycle#get-a-session): while `status` is `active` the `cdpUrl` is still valid and you can attach again. Profiles with `persist: true` also carry state into a brand-new session if the old one has ended. +If your process crashes, [`GET /v1/browser/session`](/docs/sessions/lifecycle#get-a-session): while `status` is `active` the `cdpUrl` still works. If the session ended, a profile with `persist: true` carries the state into a new one. -Several clients can be attached at once (your script and the [live view](/docs/sessions/live-view), for instance). Two scripts driving the same tab will fight each other, so keep it to one automation client per session. `cdpUrl` is a credential: anyone who has it can drive the browser while the session is active. Its hostname is a relay near the machine, not the session's network; the egress country is the one you asked for regardless of the host you connect to. +Several clients can attach at once (your script and the [live view](/docs/sessions/live-view), say), but two scripts driving one tab fight each other: one automation client per session. `cdpUrl` is a credential. Its hostname is a relay near the machine, not the session's network; egress is from the country you asked for whatever host you connect to. ## Timeouts and waits -Driver browsers are real machines on real networks, so page loads take real-world time. Rely on your framework's auto-waiting (`page.click`, `locator.waitFor`) rather than fixed sleeps, and raise the navigation timeout for slow sites instead of retrying in a loop. +Real machines on real networks: page loads take real time. Use the framework's auto-waiting (`page.click`, `locator.waitFor`), not fixed sleeps, and raise the navigation timeout for slow sites instead of retrying in a loop. diff --git a/docs/sessions/create.mdx b/docs/sessions/create.mdx index f3287bc..3ff8062 100644 --- a/docs/sessions/create.mdx +++ b/docs/sessions/create.mdx @@ -1,9 +1,9 @@ --- title: "Create a session" -description: "POST /v1/browser/session starts a real Chrome and returns its CDP URL once it is reachable." +description: "POST /v1/browser/session starts a Chrome and returns its CDP URL once it's reachable." --- -A session is one browser on one machine for up to an hour. Create it with the options you need; every field is optional and the defaults give you a hosted Chrome in the US. When a few seconds of start-up per browser is too slow, keep a [browser pool](/docs/sessions/pools) warm with the same options and acquire from it instead. +One browser on one machine for up to an hour. Every field is optional; the defaults give you a hosted Chrome in the US. If a few seconds of start-up is too slow, acquire from a [browser pool](/docs/sessions/pools) instead. ```bash cURL @@ -67,48 +67,52 @@ session = r.json() } ``` -The call waits until Chrome is reachable, so `cdpUrl` is normally set in the response and `status` is `active`; in our runs that took two to three seconds. It gives up after 20 seconds on most machines and after three minutes on the newest hosted machines, then answers `504`; give the create call a client timeout of at least three minutes (other calls answer within seconds). The browser opens on `about:blank` (or the `url` you passed) with one context and one tab. If you ever see `starting`, poll [`GET /v1/browser/session`](/docs/sessions/lifecycle#get-a-session) until `cdpUrl` appears. Session ids are opaque strings (32 hexadecimal characters today); do not validate their format. Treat `cdpUrl` as a secret, since anyone holding it can drive the browser. +The call blocks until Chrome is reachable (measured: two to three seconds), so `cdpUrl` is set and `status` is `active`. It gives up after 20 seconds on most machines, three minutes on the newest, with a `504`. Set a client timeout of at least three minutes on this call; everything else answers within seconds. + +The browser opens on `about:blank`, or your `url`, with one context and one tab. If you get `starting`, poll [`GET /v1/browser/session`](/docs/sessions/lifecycle#get-a-session) until `cdpUrl` appears. + +Session ids are opaque (32 hex characters today); don't validate the format. `cdpUrl` is a credential: anyone holding it can drive the browser. ## Options | Field | Type | Default | What it does | |---|---|---|---| -| `type` | string | `hosted` | Browser class: `hosted`, `hosted_stealth` or `hosted_privacy`. See [Browser types](/docs/options/browser-types). | -| `country` | string | `US` | Two-letter code, one of the 249 on [Countries](/docs/options/countries). Placement, network and locale follow it. See [Location](/docs/options/location). | +| `type` | string | `hosted` | `hosted`, `hosted_stealth` or `hosted_privacy`. See [Browser types](/docs/options/browser-types). | +| `country` | string | `US` | Two-letter code from [Countries](/docs/options/countries). Placement, network and locale follow it. See [Location](/docs/options/location). | | `timezone` | string | chosen for the country | IANA name from that country's list on [Countries](/docs/options/countries). | | `language` | string | chosen for the country | BCP-47 tag from that country's list on [Countries](/docs/options/countries). US sessions are always `en-US`. | -| `duration` | integer | `3600` | Seconds before the session stops on its own, 60 to 3600 inclusive. | -| `url` | string | | A URL to open right after launch. | -| `windowSize` | string | node default | Outer window size as `WIDTHxHEIGHT`. See [Window size](/docs/options/window-size). | -| `displaySize` | string | | Virtual display size as `WIDTHxHEIGHT`. | +| `duration` | integer | `3600` | Seconds until the session stops on its own, 60 to 3600 inclusive. | +| `url` | string | | Opened right after launch. | +| `windowSize` | string | node default | Outer window size, `WIDTHxHEIGHT`. See [Window size](/docs/options/window-size). | +| `displaySize` | string | | Virtual display size, `WIDTHxHEIGHT`. | | `proxyUrl` | string | Driver's network | Your own `socks5://`, `socks5h://` or `tcperfect://` URL. See [Proxies](/docs/options/proxies). | -| `profile` | object | | `{ "name": "…", "persist": true }` to load and save browser state; `persist` defaults to `false` (load only). See [Profiles](/docs/options/profiles). | -| `captchaSolver` | boolean | `false` | Load Driver's CAPTCHA solver into the session. | -| `adblock` | boolean | `false` | Block ads in the session. | -| `extensionIds` | string[] | | Uploaded extension ids to load. Requires extension access. See [Extensions](/docs/options/extensions). | +| `profile` | object | | `{ "name": "…", "persist": true }` loads and saves browser state. `persist` defaults to `false` (load only). See [Profiles](/docs/options/profiles). | +| `captchaSolver` | boolean | `false` | Load Driver's CAPTCHA solver. | +| `adblock` | boolean | `false` | Block ads. | +| `extensionIds` | string[] | | Uploaded extension ids to load. Needs extension access. See [Extensions](/docs/options/extensions). | | `nodeId` | string | | Run on the machine that served an earlier session. Overrides `country` and `type`. See [Node reuse](/docs/options/node-reuse). | | `browserCheck` | boolean | `false` | Run a browser validation check during creation. | | `note` | string | | Up to 256 characters, shown in the dashboard. | -Unknown fields are rejected with `400` (`window_size is not supported by this endpoint`). Out-of-range values name the field and the bound (`duration: Too small: expected number to be >=60`, `note: Too big: expected string to have <=256 characters`). Options that contradict each other are rejected with `400` and a message naming the field. The common ones: a `timezone` or `language` not listed for the `country` (`Unsupported timezone "Europe/Berlin" for country US.`), a `country` that does not match a supplied proxy's exit, a proxy that cannot be reached, and out-of-range `duration`, `windowSize` or `note`. +Unknown fields are a `400` (`window_size is not supported by this endpoint`), as are out-of-range values (`duration: Too small: expected number to be >=60`, `note: Too big: expected string to have <=256 characters`) and options that contradict each other: a `timezone` or `language` not listed for the `country` (`Unsupported timezone "Europe/Berlin" for country US.`), a `country` that doesn't match your proxy's exit, a proxy that can't be reached. The message names the field. ## Errors | Status | Meaning | |---|---| -| `400` | Invalid or incompatible options. The message names the field. | +| `400` | Invalid or incompatible options; the message names the field. | | `401` | Missing or invalid API key. | | `402` | The workspace is out of credits. | -| `403` | Session or account limit reached (concurrency, or a feature your plan does not include). | +| `403` | Concurrency limit, or a feature your plan doesn't include. | | `429` | Too many requests. | | `500` | Something failed on Driver's side. | -| `503` | No browser capacity right now. The response carries `Retry-After: 2` and `"code": "browser_capacity_unavailable"`. | -| `504` | A machine was selected but Chrome was not reachable before the startup deadline (20 s, up to 3 min on some machines). | +| `503` | No browser capacity. Carries `Retry-After: 2` and `"code": "browser_capacity_unavailable"`. | +| `504` | A machine was selected but Chrome wasn't reachable before the startup deadline (20 s, up to 3 min on some machines). | -Retry guidance per status is on [Errors](/docs/sessions/errors). +What to retry is on [Errors](/docs/sessions/errors). ## Next - [Connect over CDP](/docs/sessions/connect) -- [Lifecycle](/docs/sessions/lifecycle) to get, list, label and stop sessions -- [Browser pools](/docs/sessions/pools) for warm browsers with these options preset +- [Lifecycle](/docs/sessions/lifecycle): get, list, label and stop sessions +- [Browser pools](/docs/sessions/pools): warm browsers with these options preset diff --git a/docs/sessions/errors.mdx b/docs/sessions/errors.mdx index f4f0b65..810c9f8 100644 --- a/docs/sessions/errors.mdx +++ b/docs/sessions/errors.mdx @@ -3,7 +3,7 @@ title: "Errors" description: "Every status the API returns, what it means, and whether retrying helps." --- -Errors from the API are JSON with an `error` message; one carries a machine-readable `code` as well, the create call's `503`. A `429` or `502` from the edge in front of the API may carry a non-JSON body, so read the status first and parse the body defensively. +Errors are JSON with an `error` message; the create call's `503` also has a `code`. A `429` or `502` from the edge in front of the API may not be JSON, so read the status first and parse the body defensively. ```json { "error": "Insufficient balance" } @@ -13,38 +13,38 @@ Errors from the API are JSON with an `error` message; one carries a machine-read { "error": "No eligible node found", "code": "browser_capacity_unavailable" } ``` -The bodies you will see most: `401` `Unauthorized: Missing or invalid Authorization header` or `Unauthorized: Invalid token`; `402` `Insufficient balance`; `403` `Session limit reached` (concurrency) or `Extensions API is not enabled for this account`; `400` names the field, for example `Unsupported timezone "Europe/Berlin" for country US.` or `windowSize must be WIDTHxHEIGHT with width 320–16384 and height 200–16384`. Unknown fields in a body are rejected with `400` too. +Common bodies: `401` `Unauthorized: Missing or invalid Authorization header` or `Unauthorized: Invalid token`; `402` `Insufficient balance`; `403` `Session limit reached` (concurrency) or `Extensions API is not enabled for this account`; `400` names the field, for example `Unsupported timezone "Europe/Berlin" for country US.` or `windowSize must be WIDTHxHEIGHT with width 320–16384 and height 200–16384`. Unknown fields in a body are a `400` too. ## Status codes | Status | Meaning | Retry? | |---|---|---| -| `400` | Invalid or incompatible options, an unknown field on create, a missing `sessionId`, or an update to a session that has ended. The message names the problem. | No. Fix the request. | +| `400` | Invalid or incompatible options, an unknown field on create, a missing `sessionId`, or an update to an ended session. The message names the problem. | No. Fix the request. | | `401` | Missing or invalid API key. | No. Check `Authorization: Bearer …`. | | `402` | The workspace is out of credits. | No. Add credits in the dashboard. | -| `403` | Session or account limit reached: the workspace's concurrent-browser limit (`plan.concurrent_browsers` on `GET /v1/account/billing`; sessions that are `starting` count, and a stop frees the slot as soon as it returns), or a feature (extensions) the account is not enabled for. | Only after a session ends. Never in a loop. | -| `404` | Session, extension, pool, lease or account not found (unknown id, or one belonging to another workspace). Deleting an unknown profile is not a 404: it answers `{ "success": true }`. | No. | +| `403` | Concurrent-browser limit (`plan.concurrent_browsers` on `GET /v1/account/billing`; `starting` sessions count, a stop frees the slot as soon as it returns), or a feature (extensions) the account isn't enabled for. | Only after a session ends. Never in a loop. | +| `404` | Session, extension, pool, lease or account not found: unknown id, or one belonging to another workspace. Deleting an unknown profile isn't a 404; it answers `{ "success": true }`. | No. | | `409` | Browser pools only: a duplicate pool name, a size over the account's warm-browser cap, an acquire that found nothing ready within `waitMs` (`No browser is currently ready in this pool`), an acquire on a paused pool, or a delete with active leases. See [Browser pools](/docs/sessions/pools#errors). | Only the acquire one, as for `503`: back off, cap, then fall back to a plain create or report. | -| `429` | Too many requests. The API itself does not rate-limit today; a `429` comes from the edge in front of it. | Yes: a few attempts, a couple of seconds apart and growing. | -| `502` | The edge could not reach the API. Not JSON. | Once, after a couple of seconds. | +| `429` | Too many requests, from the edge; the API itself doesn't rate-limit today. | Yes: a few attempts, a couple of seconds apart and growing. | +| `502` | The edge couldn't reach the API. Not JSON. | Once, after a couple of seconds. | | `500` | Something failed on Driver's side. | Once. | -| `503` | No browser capacity right now. The response carries `Retry-After: 2` and `"code": "browser_capacity_unavailable"`. Or the session service is unavailable. | Yes: back off with jitter, cap the attempts, then tell the user. | -| `504` | A machine was selected but Chrome did not become reachable before the startup deadline: 20 seconds, up to three minutes on some machines. | Once. | +| `503` | No browser capacity. Carries `Retry-After: 2` and `"code": "browser_capacity_unavailable"`. Or the session service is unavailable. | Yes: back off with jitter, cap the attempts, then tell the user. | +| `504` | A machine was selected but Chrome wasn't reachable before the startup deadline (20 s, up to 3 min on some machines). | Once. | ## Guidance for agents and automations -- Do not retry `402` or `403` in a loop. Surface the message; a person has to act. -- Treat `503` as transient: back off (start at the `Retry-After` of 2 seconds, grow with jitter), cap the attempts, then report. Retry `504` and `500` once. -- After a `504`, the session is not running; no stop call is needed. -- `DELETE` on a session that already ended still returns `{ "success": true }`; a `404` means the id was never a session of this workspace. +- Never loop on `402` or `403`. Surface the message; a person has to act. +- `503` is transient: start at the `Retry-After` of 2 seconds, grow with jitter, cap the attempts, then report. Retry `504` and `500` once. +- After a `504` nothing is running; no stop call needed. +- `DELETE` on an ended session still returns `{ "success": true }`. A `404` means the id was never a session of this workspace. ## Framework-side failures -Some failures show up in your framework rather than as an HTTP status: +Some failures show up in the framework, not as an HTTP status: | Symptom | Likely cause | |---|---| | `Target closed` / `Browser closed` mid-run | The session reached its `duration`, or was stopped from the dashboard. Check `GET /v1/browser/session`. | -| Connection refused on `cdpUrl` | The session is not `active` yet or has ended. | +| Connection refused on `cdpUrl` | The session isn't `active` yet or has ended. | | The site shows a challenge or block page | Framework fingerprint or automation tactics. Switch to [Patchright](/docs/frameworks/patchright) and remove CDP hooks; then escalate: `type: "hosted_stealth"`, `"hosted_privacy"`, `captchaSolver: true`, and finally a person through the [live view](/docs/sessions/live-view). | -| `net::ERR_SOCKS_CONNECTION_FAILED` on every navigation | The session's network egress is not reachable. Stop the session and create another, in a different country if it persists; report it if it does. | +| `net::ERR_SOCKS_CONNECTION_FAILED` on every navigation | The session's egress isn't reachable. Stop it and create another, in a different country if it persists; report it if it keeps happening. | diff --git a/docs/sessions/lifecycle.mdx b/docs/sessions/lifecycle.mdx index 3d212d6..c7d8b63 100644 --- a/docs/sessions/lifecycle.mdx +++ b/docs/sessions/lifecycle.mdx @@ -3,18 +3,18 @@ title: "Lifecycle" description: "Get, list, label and stop sessions. What each status means and how long a session lives." --- -A session moves through `starting` → `active` → `completed`, or `error` if the browser failed. It ends when you stop it, when it reaches its `duration`, or when the machine serving it goes away. A session acquired from a [browser pool](/docs/sessions/pools) is the same object once acquired (`createdAt` is when the browser was warmed); it also ends when its lease is released or expires. +`starting` → `active` → `completed`, or `error` if the browser failed. A session ends when you stop it, at its `duration`, or when its machine goes away. One acquired from a [browser pool](/docs/sessions/pools) is the same object (`createdAt` is when the browser was warmed) and also ends when its lease is released or expires. | Status | Meaning | |---|---| | `starting` | The machine is launching Chrome. `cdpUrl` is `null`. | | `active` | Chrome is reachable at `cdpUrl`. | -| `completed` | The session was stopped or reached its duration. `stoppedAt` is set. | -| `error` | The session failed to start, ended abnormally, or the machine serving it went away. `stoppedAt` is set. | +| `completed` | Stopped, or reached its duration. `stoppedAt` is set. | +| `error` | Failed to start, ended abnormally, or the machine went away. `stoppedAt` is set. | ## Get a session -Returns the same shape as create, plus `bandwidthBytes`: `null` while the session runs, the metered total once it has ended (final by the time a stop call returns; `0` for a session that moved no bytes). `cdpUrl` is `null` once the session has ended. +Same shape as create, plus `bandwidthBytes`: `null` while running, the metered total after (final by the time a stop returns; `0` if nothing moved). `cdpUrl` is `null` after the session ends. ```bash cURL @@ -34,7 +34,7 @@ session = requests.get( ## List sessions -Paginated, newest first, with an optional `status` filter that matches exactly one status (`starting` sessions are not in `?status=active`). `page` defaults to 1 and `pageSize` to 20 (at most 100; larger values are clamped, not rejected). A `status` outside `starting`, `active`, `completed`, `error` is a `400`. +Newest first. `status` matches exactly (`starting` isn't in `?status=active`); anything outside `starting`, `active`, `completed`, `error` is a `400`. `page` defaults to 1, `pageSize` to 20, max 100 (larger is clamped). ```bash cURL @@ -54,11 +54,11 @@ data = requests.get( ``` -The response carries `sessions`, `total`, `totalPages`, `page` and `pageSize`. Each item has `sessionId`, `status`, `servedBy`, `type`, `country`, `createdAt`, `stoppedAt`, `bandwidthBytes` and `note`; the list is the only place the API reports `country` (`type` is `null` for sessions created with the default type), since the single-session calls do not. +Response: `sessions`, `total`, `totalPages`, `page`, `pageSize`. Items: `sessionId`, `status`, `servedBy`, `type`, `country`, `createdAt`, `stoppedAt`, `bandwidthBytes`, `note`. This is the only place the API reports `country`; `type` is `null` for the default type. ## Label a session -Notes show up in the dashboard's session list and record. Set one on create, or afterwards: +Notes show in the dashboard's session list and record. Set one on create, or later: ```bash cURL @@ -84,11 +84,11 @@ requests.patch( ``` -Returns `{ "success": true }`. Notes are at most 256 characters (longer is a `400`). Send an empty string to clear one. A session that has ended cannot be updated (`400`, `Cannot update a terminal session`). +Returns `{ "success": true }`. Max 256 characters (longer is a `400`); an empty string clears it. An ended session can't be updated (`400`, `Cannot update a terminal session`). ## Stop a session -Stopping releases the browser and ends billing for the session. Always do it in a `finally`. A session that is still `starting` can be stopped too. +Releases the browser and ends billing. Do it in a `finally`. A `starting` session can be stopped too. ```bash cURL @@ -108,15 +108,15 @@ requests.delete( ``` -Returns `{ "success": true }`. Stopping is idempotent: a session that already ended answers the same `{ "success": true }`, so a stop in a `finally` is always safe. +Returns `{ "success": true }`, also for a session that already ended, so a stop in a `finally` is always safe. ## Duration -`duration` on create is the session's lifetime in seconds, from 60 to 3600 inclusive, default 3600. There is no extension call: choose a duration that covers the job, and start a new session (with a persisted [profile](/docs/options/profiles) if you need the state) when it runs out. +`duration` is the lifetime in seconds, 60 to 3600 inclusive, default 3600. There's no extension call: pick one that covers the job, and start a new session (with a persisted [profile](/docs/options/profiles) if you need the state) when it runs out. ## Waiting for `active` -Create normally returns with `cdpUrl` set. If you see `starting`, poll (`getSession` / `get_session` are the GET call from the top of this page; the [reference client](/docs/start/for-agents#reference-implementation) has both): +Create returns with `cdpUrl` set. If you see `starting`, poll. The [reference client](/docs/start/for-agents#reference-implementation) has both of these. ```typescript TypeScript diff --git a/docs/sessions/live-view.mdx b/docs/sessions/live-view.mdx index 2ad6078..eecad2b 100644 --- a/docs/sessions/live-view.mdx +++ b/docs/sessions/live-view.mdx @@ -3,21 +3,19 @@ title: "Live view" description: "Watch a running session, take the controls, and read the record afterwards." --- -Every session can be watched while it runs and inspected after it ends. Use it to see what a site is doing before you add logging or hooks, and to hand a login or a CAPTCHA to a person mid-run. +Watch a session while it runs, inspect it after it ends. Look before you add logging or hooks, and hand a login or CAPTCHA to a person mid-run. ## In the dashboard -Open **Telemetry → Sessions** in the [dashboard](https://app.driver.dev) and pick a running session. The page shows the live browser; you can scroll, click and type in it. The **Workbench** shows the same view next to a script editor, which is the quickest way to try options before writing code. +**Telemetry → Sessions** in the [dashboard](https://app.driver.dev), pick a running session. You can scroll, click and type in it. The **Workbench** has the same view next to a script editor for trying options before writing code. ## From a viewer URL -Build a viewer URL from `cdpUrl` and open it in any browser: - ```text https://viewer.driver.dev?ws= ``` -Paste `cdpUrl` in as it is; no encoding is needed: the viewer reads everything after `ws=`. Print the URL as soon as the session exists, before the automation starts, so the person has time to open it; it shows the tab your script is driving. +Paste `cdpUrl` in as is, no encoding; the viewer reads everything after `ws=`. Print the URL as soon as the session exists so the person has time to open it. It shows the tab your script is driving. ```typescript TypeScript @@ -33,10 +31,10 @@ viewer_url = f"https://viewer.driver.dev?ws={session['cdpUrl']}" | `ws` | The session's `cdpUrl`. | | `theme` | Optional: `light` or `dark`. | -Anyone with the URL can drive the browser, so treat it like the API key while the session is active. It stops working when the session ends. +Anyone with the URL can drive the browser; treat it like the API key. It stops working when the session ends. ## The session record -After a session ends, its page in the dashboard keeps the record: status and duration, bandwidth and cost, the node that served it with its country, the network it used, how it was started (type, profile, note, which API key), and timestamps. Keep the `sessionId` in your own logs so a run can be found later. +After a session ends its dashboard page keeps: status and duration, bandwidth and cost, node and country, network, how it was started (type, profile, note, API key), timestamps. Log the `sessionId` so you can find a run later. -The API keeps the essentials: [`GET /v1/browser/session`](/docs/sessions/lifecycle#get-a-session) returns `status`, `stoppedAt` and `bandwidthBytes`, and the [list](/docs/sessions/lifecycle#list-sessions) adds `type` and `country`. Cost, network and node details are in the dashboard only. +The API keeps the essentials: [`GET /v1/browser/session`](/docs/sessions/lifecycle#get-a-session) has `status`, `stoppedAt` and `bandwidthBytes`; the [list](/docs/sessions/lifecycle#list-sessions) adds `type` and `country`. Cost, network and node details are dashboard-only. diff --git a/docs/sessions/pools.mdx b/docs/sessions/pools.mdx index 93777cd..395ad5f 100644 --- a/docs/sessions/pools.mdx +++ b/docs/sessions/pools.mdx @@ -1,14 +1,14 @@ --- title: "Browser pools" -description: "Keep browsers warm and take one in under a second. Create a pool, acquire a lease, release or stop the session." +description: "Keep browsers warm and take one in under a second. Create a pool, acquire a lease, release or stop." --- -A pool keeps a number of browsers already running with the options you chose, so a job can take one instead of waiting for a create call. Acquiring from a ready pool answers in well under a second with an ordinary session; the pool warms a replacement behind it. Use a pool when start-up latency matters (an agent loop, a request handler, a burst of short jobs) or when every job needs the same start page loaded, profile attached or country. For a nightly batch that can wait a few seconds per browser, plain [create](/docs/sessions/create) is simpler. +A pool keeps browsers running with the options you chose. Acquire hands you one in under a second as an ordinary session, and the pool warms a replacement. Use one when start-up latency matters (an agent loop, a request handler, a burst of short jobs) or when every job wants the same start page, profile or country. A nightly batch that can wait a few seconds per browser is fine with plain [create](/docs/sessions/create). -Pools also live in the [dashboard](https://app.driver.dev/pools) under **Browsers → Browser pools**, where **Launch browser** on the Overview can take a browser from any ready pool. +Pools are also in the [dashboard](https://app.driver.dev/pools) under **Browsers → Browser pools**; **Launch browser** on the Overview can take one from any ready pool. -**Network options are not applied to pooled browsers yet.** A pool records the `country` you set and the session list reports it, but in our runs on 2026-09-20 every warm browser egressed from the default US network (a GB pool and a JP pool both answered with a US address and `America/New_York`), while a plain create with the same `country` egressed correctly. A `proxyUrl` that cannot be reached, or a country that does not exist, still warms and reports `ready`. Until this is fixed, use pools for jobs that are fine on the US network and [create sessions](/docs/sessions/create) for geo-specific or proxied ones. `url` and `note` are applied. +**Network options aren't applied to pooled browsers yet.** A pool records `country` and the session list reports it, but as of 2026-09-20 every warm browser egresses from the default US network (GB and JP pools both answered with a US address and `America/New_York`; a plain create with the same `country` egressed correctly). An unreachable `proxyUrl` or a nonexistent country still warms and reports `ready`. Until this is fixed, use pools for jobs that are fine on the US network and [create sessions](/docs/sessions/create) for geo-specific or proxied ones. `url` and `note` are applied. ## Create a pool @@ -58,7 +58,7 @@ pool = r.json() # pool["id"] is what the other calls take ``` -The call answers `201` at once with the pool in `warming`; the browsers come up in the background and in our runs the pool was `ready` two to five seconds later. +Answers `201` at once with the pool `warming`; measured, it was `ready` two to five seconds later. ```json { @@ -83,26 +83,26 @@ The call answers `201` at once with the pool in `warming`; the browsers come up | Field | Type | Default | What it does | |---|---|---|---| | `name` | string | required | 1 to 64 characters, unique in the workspace (`409` `A pool with that name already exists`). | -| `size` | integer | required | How many browsers to keep ready, at least 1. All pools together may keep 30 browsers warm per account (`409` `Pool capacity 32 exceeds your pool limit of 30`); write to support to raise it. Paused pools count too, and a resize that would cross the cap is refused the same way. | -| `browser` | object | `{}` | The create options every browser in the pool starts with: `country`, `timezone`, `language`, `url`, `windowSize`, `displaySize`, `proxyUrl`, `profile`, `adblock`, `captchaSolver`, `extensionIds`, `nodeId`, `note`. Same meaning as on [Create a session](/docs/sessions/create#options). `type`, `duration` and `browserCheck` are not accepted. | -| `leaseTimeoutSeconds` | integer | `900` | How long an acquired browser may be held, 30 to 86400. When the lease expires the session is stopped. | -| `maxReadyAgeSeconds` | integer | `3600` | How long a browser may sit ready before the pool replaces it with a fresh one, 30 to 86400. The swap takes two to three seconds, during which `ready` is one lower. | +| `size` | integer | required | Browsers to keep ready, at least 1. 30 warm per account across all pools, paused ones included (`409` `Pool capacity 32 exceeds your pool limit of 30`, also on a resize); write to support to raise it. | +| `browser` | object | `{}` | The create options every browser starts with: `country`, `timezone`, `language`, `url`, `windowSize`, `displaySize`, `proxyUrl`, `profile`, `adblock`, `captchaSolver`, `extensionIds`, `nodeId`, `note`. Same meaning as on [Create a session](/docs/sessions/create#options). `type`, `duration` and `browserCheck` aren't accepted. | +| `leaseTimeoutSeconds` | integer | `900` | How long an acquired browser may be held, 30 to 86400. The session is stopped when the lease expires. | +| `maxReadyAgeSeconds` | integer | `3600` | How long a browser may sit ready before it's swapped for a fresh one, 30 to 86400. The swap takes two to three seconds, during which `ready` is one lower. | -Unknown fields, at the top level or inside `browser`, are `400` (`bogus is not supported by this endpoint`), as on create. The values inside `browser` are not checked the way a create body is: a pool with `country: "ZZ"` or an unreachable `proxyUrl` is created and reports `ready` (see the warning above). `lastError` is set when browsers fail to start. +Unknown fields, top-level or inside `browser`, are a `400` (`bogus is not supported by this endpoint`). Values inside `browser` aren't checked the way a create body is: `country: "ZZ"` or an unreachable `proxyUrl` is accepted and the pool reports `ready` (see the warning). `lastError` is set when browsers fail to start. -A pool's browsers use `profile` the way a session does. With `persist: false` (the default) every browser loads the profile read-only, which is what you want in a pool: several browsers writing the same profile at once would overwrite one another. +`profile` works as on a session. Leave `persist` off (the default) so every browser loads it read-only; several browsers writing one profile would overwrite each other. ## Status and counters | `status` | Meaning | |---|---| -| `warming` | Just created or resumed; browsers are starting and none is ready yet. | +| `warming` | Just created or resumed; browsers are starting, none ready yet. | | `ready` | `ready` equals `size`. | -| `degraded` | Fewer ready browsers than `size`: normal right after an acquire, a flush, or a lease expiring, while replacements start. A pool that cannot start browsers stays here with `lastError` set. | -| `paused` | Warm browsers stopped and no new ones started. Acquire is refused. | +| `degraded` | Fewer ready than `size`: normal right after an acquire, a flush or a lease expiring, while replacements start. A pool that can't start browsers stays here with `lastError` set. | +| `paused` | Warm browsers stopped, none started. Acquire is refused. | | `deleting` | Being torn down. | -`ready`, `starting` and `leased` count the browsers in each state. Ready browsers are not sessions of the workspace yet: `GET /v1/browser/sessions` does not list them, and they hold none of the workspace's concurrency slots until acquired. +`ready`, `starting` and `leased` count browsers in each state. Ready browsers aren't sessions yet: `GET /v1/browser/sessions` doesn't list them and they hold no concurrency slot until acquired. ## Acquire a browser @@ -149,9 +149,9 @@ lease_id, session = lease["leaseId"], lease["session"] } ``` -`waitMs` (0 to 30000, default 0) is how long the call may wait for a browser to become ready when none is; a replacement takes two to three seconds to warm, so a wait of a few seconds usually succeeds. The body is required: send `{}` for no wait (no body is a `400` `Malformed JSON in request body`). When nothing is ready by then the answer is `409` `No browser is currently ready in this pool`; a paused pool answers `409` `Pool is paused` at once. Treat the first like a `503` on create: back off briefly, cap the attempts, then fall back to a plain create or report. +`waitMs` (0 to 30000, default 0) is how long to wait when nothing is ready; a replacement warms in two to three seconds, so a few seconds is enough. The body is required: `{}` for no wait (no body is a `400` `Malformed JSON in request body`). Nothing ready in time: `409` `No browser is currently ready in this pool`. Paused pool: `409` `Pool is paused` at once. Treat the first like a create `503`: back off, cap, then fall back to a plain create or report. -From the acquire call on, `session` is an ordinary session of the workspace: it appears in `GET /v1/browser/sessions`, `GET /v1/browser/session?sessionId=…` works, it counts as a running browser, and the [live view](/docs/sessions/live-view) works on its `cdpUrl`. Its `createdAt` is when the browser was warmed, not when you acquired it, and the page is already on the pool's `url`. Connect to it exactly like any other session, with Patchright: +From here `session` is an ordinary session: listed, readable with `GET /v1/browser/session?sessionId=…`, counted as a running browser, watchable in the [live view](/docs/sessions/live-view). `createdAt` is when the browser was warmed, not when you acquired it, and the page is already on the pool's `url`. Connect with Patchright as usual: ```typescript TypeScript @@ -187,17 +187,17 @@ finally: ``` -`acquire` and `release` here are the two calls on this page; the [reference client](/docs/start/for-agents#reference-implementation) has both. +`acquire` and `release` are the two calls on this page; the [reference client](/docs/start/for-agents#reference-implementation) has both. ## Release or stop -A leased browser ends one of three ways, and the pool warms a replacement in every case: +A leased browser ends one of three ways; the pool warms a replacement each time. -- **Release** it: `POST /v1/browser/pools/{poolId}/release` with `{ "leaseId": "…" }`. The session is stopped and the answer is `{ "poolId", "leaseId", "sessionId", "status": "ended", "returnedAt" }`. Do this in a `finally`, the way you would stop a session. -- **Stop** the session with `DELETE /v1/browser/session?sessionId=…`. Same effect; the lease closes on its own within a few seconds. -- **Let the lease expire.** At `expiresAt` (`leaseTimeoutSeconds` after acquire) the session is stopped whether or not you are done, so pick a lease that covers the job, or stop early. +- **Release**: `POST /v1/browser/pools/{poolId}/release` with `{ "leaseId": "…" }`. Stops the session; answers `{ "poolId", "leaseId", "sessionId", "status": "ended", "returnedAt" }`. Do it in a `finally`. +- **Stop** it with `DELETE /v1/browser/session?sessionId=…`. Same effect; the lease closes within a few seconds. +- **Let the lease expire.** At `expiresAt` (`leaseTimeoutSeconds` after acquire) the session is stopped whether or not you're done. Pick a lease that covers the job. -Releasing is what ends billing for the session, exactly like stopping one. Releasing is idempotent (a second release of the same lease answers the same body); an unknown lease, or one from another pool, is `404` `Pool lease not found`. A browser is never handed back to the pool for another job: the next acquire always gets a fresh one. +Release ends billing, like a stop. It's idempotent (same body twice); an unknown lease, or one from another pool, is `404` `Pool lease not found`. A browser is never handed back for another job; the next acquire gets a fresh one. ```bash cURL @@ -226,11 +226,11 @@ requests.post( |---|---| | `GET /v1/browser/pools` | `{ "pools": [ … ] }`, every pool in the workspace with its counters. | | `GET /v1/browser/pools/{poolId}` | One pool. | -| `PATCH /v1/browser/pools/{poolId}` | Change `name`, `size`, `browser`, `leaseTimeoutSeconds`, `maxReadyAgeSeconds`, or `paused` (`true` stops the warm browsers and refuses acquires; `false` warms them again). Every update restarts the pool's warm browsers (the pool goes back to `warming` for a few seconds; leased browsers are untouched). Always send the whole `browser` template with every option you want to keep: an update without `browser` resets the template to `{}`, so a rename or a resize on its own silently drops the country, start URL and everything else. | -| `POST /v1/browser/pools/{poolId}/flush` | Stops every ready browser and warms fresh ones. Answers `{ "success": true, "stopped": n }`. Use it after changing something browsers pick up only at start. Leased browsers are not touched. | -| `DELETE /v1/browser/pools/{poolId}` | Stops the warm browsers and removes the pool. With leased browsers the answer is `409` `Pool has active leases; pass force=true to stop them`; `?force=true` (the literal `true`; `1` is a `400`) stops those sessions too. A deleted pool is `404` afterwards. | +| `PATCH /v1/browser/pools/{poolId}` | Change `name`, `size`, `browser`, `leaseTimeoutSeconds`, `maxReadyAgeSeconds` or `paused` (`true` stops the warm browsers and refuses acquires; `false` warms them again). Every update restarts the warm browsers (`warming` for a few seconds; leased ones untouched). Always send the whole `browser` template: an update without it resets the template to `{}`, so a bare rename or resize drops the country, start URL and everything else. | +| `POST /v1/browser/pools/{poolId}/flush` | Stops every ready browser and warms fresh ones. Answers `{ "success": true, "stopped": n }`. Leased browsers are untouched. | +| `DELETE /v1/browser/pools/{poolId}` | Stops the warm browsers and removes the pool. With leased browsers: `409` `Pool has active leases; pass force=true to stop them`; `?force=true` (literal `true`; `1` is a `400`) stops those sessions too. Afterwards the pool is `404`. | -Pool ids go in the path, unlike session ids, which go in the query string. +Pool ids go in the path. Session ids go in the query string. ## Errors @@ -239,17 +239,17 @@ Pool ids go in the path, unlike session ids, which go in the query string. | `400` | names the field | A missing or out-of-range field (`size: Too small: expected number to be >0`, `leaseTimeoutSeconds: Too small: expected number to be >=30`), or an unknown field. | | `404` | `Browser pool not found` | Unknown pool id, or one belonging to another workspace. | | `409` | `A pool with that name already exists` | Names are unique per workspace. | -| `409` | `Pool capacity 32 exceeds your pool limit of 30` | The new size would put the account's warm browsers over its cap. | +| `409` | `Pool capacity 32 exceeds your pool limit of 30` | The new size would put the account over its warm-browser cap. | | `409` | `No browser is currently ready in this pool` | Acquire waited `waitMs` and nothing became ready. Back off, cap, then fall back or report. | | `409` | `Pool has active leases; pass force=true to stop them` | Delete while browsers are leased. | | `503` | | The browser service is unavailable. Back off as for a create `503`. | ## Cost and limits -Warm browsers are not sessions of the workspace: they are not listed, not billed and hold no concurrency slot until acquired. Once acquired, a browser is a session like any other: it counts toward the workspace's concurrent-browser limit and its bandwidth is metered until it is released, stopped or its lease expires. The account-wide cap of 30 warm browsers across all pools is separate from the concurrency limit. +Warm browsers aren't sessions: not listed, not billed, no concurrency slot. Acquired, a browser is a session like any other: it counts toward the concurrency limit and its bandwidth is metered until release, stop or lease expiry. The 30-warm cap per account is separate from the concurrency limit. ## Next -- [Lifecycle](/docs/sessions/lifecycle) for what you can do with the acquired session. -- [Create a session](/docs/sessions/create#options) for every option the `browser` template accepts. +- [Lifecycle](/docs/sessions/lifecycle): what you can do with the acquired session. +- [Create a session](/docs/sessions/create#options): every option the `browser` template accepts. - [For agents](/docs/start/for-agents#reference-implementation): the reference client has `acquireFromPool` and `releaseLease`. diff --git a/docs/start/best-practices.mdx b/docs/start/best-practices.mdx index e26dc86..f1c4f54 100644 --- a/docs/start/best-practices.mdx +++ b/docs/start/best-practices.mdx @@ -1,75 +1,71 @@ --- title: "Best practices" -description: "How to keep Driver sessions unblocked and cheap: the right framework, minimal CDP noise, coherent location settings, and reliable cleanup." +description: "How to keep sessions unblocked and cheap: the right framework, no CDP noise, coherent location, reliable cleanup." --- -Driver gives you a real browser on a real network. Most blocks come from the automation side, not the browser, and they are avoidable. These practices are the difference between a session that looks like a person and one that looks like a script. +The browser is real and the network is real. Most blocks come from the automation side, and they're avoidable. ## Keep the logic minimal -The shortest script that reaches the outcome is the best one. It is faster, it fails in obvious ways, and it gives sites the least to notice. Every added retry loop, event listener, request interceptor, "stealth" helper or defensive wait is a place for bugs to hide and a signal that this is not a person. Add a mechanism only after a specific site has shown you that you need it, and add the smallest one that works. +The shortest script that gets the result is the best one. It's faster, it fails in obvious ways, and it gives sites the least to notice. Every retry loop, event listener, request interceptor, "stealth" helper or defensive wait is a place for bugs to hide and a signal that this isn't a person. Add a mechanism after a specific site has shown you that you need it, and add the smallest one that works. -## Optimise for speed, then verify +## Go fast, then verify -Before writing browser steps, ask whether the outcome is reachable faster: +Before writing browser steps, ask whether the result is reachable faster: - Go straight to the URL that has the result instead of clicking through navigation. -- Do several pages in one session rather than one session per page. Creating a session is the expensive step. -- When the start-up itself is the bottleneck (a request handler, an agent loop), keep a [browser pool](/docs/sessions/pools) warm and acquire from it: under a second instead of a few. -- Sign in once on a persisted [profile](/docs/options/profiles) and reuse it instead of logging in on every run. -- Never use fixed sleeps. Rely on the framework's auto-waiting and stop the session the moment the result is in hand. +- Do several pages in one session. Creating a session is the expensive step. +- When start-up itself is the bottleneck (a request handler, an agent loop), keep a [browser pool](/docs/sessions/pools) warm and acquire from it. +- Sign in once on a persisted [profile](/docs/options/profiles) and reuse it. +- No fixed sleeps. Rely on the framework's auto-waiting and stop the session the moment you have the result. -Then test the automation for real: run it against the live target through Driver and check the result, not just the exit code. Confirm the data is what you expected, the action took effect, and the session was stopped. The session record under **Telemetry → Sessions** shows duration and bandwidth, which is how you know a run was actually fast. +Then run it against the live target and check the result, not the exit code: the data is right, the action took effect, the session was stopped. The record under **Telemetry → Sessions** shows duration and bandwidth, which is how you know a run was actually fast. ## Use Patchright -Stock Playwright and Puppeteer enable CDP domains (`Runtime.enable`, console and binding hooks) that anti-bot vendors fingerprint. [Patchright](/docs/frameworks/patchright) is Playwright with those signals removed and the same API. Use it for every Driver integration; use stock Playwright only if you have measured that your target does not care. +Stock Playwright and Puppeteer enable CDP domains (`Runtime.enable`, console and binding hooks) that anti-bot vendors fingerprint. [Patchright](/docs/frameworks/patchright) is Playwright with those removed and the same API. Use it for every Driver integration. Use stock Playwright only if you've measured that your target doesn't care. -## Prefer the remote browser's defaults +## Keep the browser's defaults -Do not change browser settings unless the task requires it. - -- Reuse `browser.contexts()[0]` and `context.pages()[0]` when they exist. The browser opened with a context and a tab already. -- Keep the default viewport, user agent, locale, permissions and flags. Set [`windowSize`](/docs/options/window-size), [`country`, `timezone` and `language`](/docs/options/location) on the create call instead; Driver makes them coherent with the network the session egresses from. -- Do not add timeouts and navigation settings until a specific site needs them. +- Reuse `browser.contexts()[0]` and `context.pages()[0]`. The browser opened with a context and a tab already. +- Keep the default viewport, user agent, locale, permissions and flags. Set [`windowSize`](/docs/options/window-size), [`country`, `timezone` and `language`](/docs/options/location) on the create call instead; Driver keeps them coherent with the network the session egresses from. +- Don't add timeouts and navigation settings until a specific site needs them. ## Never launch a browser -Driver provides the browser. Connect to the returned `cdpUrl`. - -Not in a Driver integration: +Driver provides the browser. Connect to the returned `cdpUrl`. Not in a Driver integration: - `chromium.launch()`, `chromium.launchPersistentContext()`, `puppeteer.launch()` - `playwright install`, or any browser download -- invented `localhost` CDP ports or CDP hosts not returned by Driver +- invented `localhost` CDP ports or CDP hosts Driver didn't return ## Keep CDP hooks narrow -Broad event listeners, request routing and injected scripts make the browser behave unusually and are detectable. Use them only when the task genuinely requires them. +Broad listeners, request routing and injected scripts make the browser behave unusually, and that's detectable. Use them only when the task genuinely needs them. -Avoid by default (Playwright and Patchright names, with the Puppeteer equivalents): +Avoid by default (Playwright and Patchright names, Puppeteer equivalents in brackets): - `page.on(...)`, `context.on(...)`, `browser.on(...)` -- `page.route(...)`, `context.route(...)` (Puppeteer: `page.setRequestInterception(true)`) -- `page.exposeBinding(...)`, `page.exposeFunction(...)` (Puppeteer: `page.exposeFunction`) -- `page.addInitScript(...)`, `context.addInitScript(...)` (Puppeteer: `page.evaluateOnNewDocument`) -- Puppeteer's `page.setUserAgent`, `page.emulateTimezone`, `page.emulate` and `page.setViewport`: these are the fingerprint patches the next section warns about +- `page.route(...)`, `context.route(...)` (`page.setRequestInterception(true)`) +- `page.exposeBinding(...)`, `page.exposeFunction(...)` (`page.exposeFunction`) +- `page.addInitScript(...)`, `context.addInitScript(...)` (`page.evaluateOnNewDocument`) +- Puppeteer's `page.setUserAgent`, `page.emulateTimezone`, `page.emulate`, `page.setViewport`: fingerprint patches, see below Prefer direct actions and reads: `page.goto`, `page.click`, `page.fill`, `page.locator`, `page.title`, `page.textContent`. -When a hook is unavoidable, listen for one event type instead of every event, route one URL pattern instead of all requests, and remove listeners and routes as soon as the step is done. +When a hook is unavoidable, listen for one event type, route one URL pattern, and remove it as soon as the step is done. -## Do not patch fingerprints +## Don't patch fingerprints The point of Driver is that the browser is genuine. Overriding `navigator`, the user agent, WebGL, canvas, timezone or locale from your script makes a real browser look fake. If you need a different location, ask for it on the create call. ## Debug with the live view first -When a page behaves differently than expected, open the session's [live view](/docs/sessions/live-view) and look before adding hooks or logging every event. You can take the mouse and keyboard yourself, which is often the fastest way to see what a site is doing. +When a page behaves unexpectedly, open the session's [live view](/docs/sessions/live-view) and look before adding hooks or logging every event. You can take the mouse and keyboard yourself, which is often the fastest way to see what a site is doing. ## Always stop sessions -Wrap browser work in `try` / `finally` so the session is stopped even when navigation or extraction fails. Stopping is what releases the browser and ends billing. +Wrap browser work in `try` / `finally` so the session is stopped even when navigation or extraction fails. Stopping releases the browser and ends billing. ```typescript TypeScript @@ -89,25 +85,25 @@ finally: ``` -Set a `duration` that matches the job. Sessions end on their own at the duration (one hour by default), which caps the cost of anything you forget. A browser taken from a [pool](/docs/sessions/pools) is released the same way, with the release call in the `finally`; its lease timeout plays the role of `duration`. +Set a `duration` that matches the job. Sessions end on their own at the duration (an hour by default), which caps the cost of anything you forget. A browser from a [pool](/docs/sessions/pools) is released the same way, in the `finally`; its lease timeout plays the role of `duration`. ## Reuse logins with profiles -Signing in on every run is slow and looks suspicious. Sign in once under a named [profile](/docs/options/profiles) with `persist: true`, then start later sessions with the same name; they open already signed in. +Signing in on every run is slow and looks suspicious. Sign in once under a named [profile](/docs/options/profiles) with `persist: true`, then start later sessions with the same name. They open signed in. ## Handle the errors that matter | Status | Meaning | What to do | |---|---|---| -| `402` | Workspace is out of credits | Tell the user. Retrying will not help. | -| `403` | Plan limit: concurrency or feature not enabled | Wait for a session to finish, or upgrade. Do not loop. | +| `402` | Out of credits | Tell the user. Retrying won't help. | +| `403` | Plan limit: concurrency or a feature not enabled | Wait for a session to finish, or upgrade. Don't loop. | | `429` | Rate limited | Back off and retry. | -| `500` | Something failed on Driver's side | Retry once. | -| `503` | No browser capacity right now (`Retry-After: 2`) | Back off with jitter, cap the attempts, then tell the user. | -| `504` | Browser did not become reachable in time | Retry once. | +| `500` | Something failed on our side | Retry once. | +| `503` | No browser capacity (`Retry-After: 2`) | Back off with jitter, cap the attempts, then tell the user. | +| `504` | Browser didn't become reachable in time | Retry once. | -The full list is on [Errors](/docs/sessions/errors). +Full list on [Errors](/docs/sessions/errors). ## Label your sessions -Pass `note` on create, or `PATCH` it later, with something a teammate would recognise ("nightly price check, client X"). It shows up in the dashboard's session list and record. +Pass `note` on create, or `PATCH` it later, with something a teammate would recognise ("nightly price check, client X"). It shows in the dashboard's session list and record. diff --git a/docs/start/for-agents.mdx b/docs/start/for-agents.mdx index 441c715..06d2bfb 100644 --- a/docs/start/for-agents.mdx +++ b/docs/start/for-agents.mdx @@ -1,35 +1,35 @@ --- title: "For agents" -description: "A briefing for coding agents integrating Driver: the rules that keep sessions unblocked, the three endpoints that matter, and the mistakes to avoid." +description: "For coding agents integrating Driver: the rules that keep sessions unblocked, the three calls, the mistakes." --- -This page is written for an AI coding agent (Claude Code, Cursor, Codex, or similar) that has been asked to integrate Driver into a project. Humans are welcome too. The published docs also serve `/llms.txt` (an index) and `/llms-full.txt` (every page). +Written for an AI coding agent (Claude Code, Cursor, Codex) that's been asked to integrate Driver into a project. Humans welcome. The docs also serve `/llms.txt` (index) and `/llms-full.txt` (every page). ## The rules -1. **Use Patchright, not stock Playwright or Puppeteer.** Install `patchright` (npm or pip) and import `chromium` from it. It has the same API as Playwright, so existing Playwright code works unchanged. Stock Playwright issues CDP commands (`Runtime.enable`, console and binding hooks) that anti-bot systems detect, and sessions get blocked. Patchright removes them. See [Why Patchright](/docs/frameworks/patchright#why-patchright). -2. **Never launch a browser.** Driver provides the browser. Connect to the `cdpUrl` from the create call with `chromium.connectOverCDP(cdpUrl)`. Do not call `chromium.launch()`, `launchPersistentContext()`, `puppeteer.launch()`, or run `playwright install`. -3. **Reuse the browser's context and tab.** After connecting, use `browser.contexts()[0]` and `context.pages()[0]` (in Python they are properties: `browser.contexts[0]`, `context.pages[0]`). Only create new ones if none exist. Extra contexts and stripped-down pages look like automation. -4. **Do not install broad CDP hooks.** Avoid `page.route`, `context.route`, `page.on('request')`, `page.exposeFunction`, `page.addInitScript` and `context.addInitScript` unless the task cannot be done otherwise, and then scope them as narrowly as possible. They change how the browser behaves and are detectable. -5. **Do not patch fingerprints.** The browser is real Chrome on real hardware. Overriding `navigator`, the user agent, WebGL, canvas, timezone or locale makes it look fake. Set `country`, `timezone` and `language` on the create call instead. -6. **Always stop the session.** Wrap the work in `try / finally` and call `DELETE /v1/browser/session?sessionId=…` in `finally`. Sessions are metered by bandwidth and end on their own at `duration` (one hour by default); a session left running holds one of the workspace's concurrency slots and keeps its `cdpUrl` live until then. Closing your client (`browser.close()`) does not stop the session. -7. **Keep the `sessionId`.** Log it with the run. Every session has a record in the dashboard under Telemetry → Sessions with node, network, timing and cost. If the person wants to watch, print `https://viewer.driver.dev?ws=` as soon as the session exists; see [Live view](/docs/sessions/live-view). -8. **Report what a person must fix; retry only what time fixes.** `402` (out of credits) and `403` (concurrency or plan limit): report, never loop. `503` (no capacity; the response carries `Retry-After: 2` and `"code": "browser_capacity_unavailable"`): back off with jitter, cap the attempts, then report. `504` (Chrome did not start in time) and `500`: retry once. `429`: back off. The full table is on [Errors](/docs/sessions/errors). -9. **Stay under the workspace's concurrency limit.** `GET /v1/account/billing` reports it as `plan.concurrent_browsers`; sessions beyond it are refused with 403. -10. **Use a browser pool when start-up latency matters.** A [pool](/docs/sessions/pools) keeps browsers warm with fixed options; `POST /v1/browser/pools/{poolId}/acquire` with `{ "waitMs": 10000 }` answers in under a second with `{ leaseId, session }`, and `session` is an ordinary session from then on. Release it with `POST /v1/browser/pools/{poolId}/release` `{ leaseId }` in the same `finally` where you would stop a session. Do not create pools on the person's behalf without asking: they hold warm browsers against an account-wide cap. -11. **When a site challenges or blocks, escalate in this order.** Remove every hook and tactic; then create with `type: "hosted_stealth"`, then `"hosted_privacy"`, then add `captchaSolver: true`; when a step genuinely needs a person, give them the live view URL. See [Browser types](/docs/options/browser-types) and [CAPTCHA solving](/docs/options/captcha-and-adblock). +1. **Patchright, not stock Playwright or Puppeteer.** Install `patchright` (npm or pip) and import `chromium` from it. Same API as Playwright, so existing code works unchanged. Stock Playwright issues CDP commands (`Runtime.enable`, console and binding hooks) that anti-bot systems detect. Patchright removes them. See [Why Patchright](/docs/frameworks/patchright#why-patchright). +2. **Never launch a browser.** Connect to the `cdpUrl` from the create call with `chromium.connectOverCDP(cdpUrl)`. No `chromium.launch()`, `launchPersistentContext()`, `puppeteer.launch()`, no `playwright install`. +3. **Reuse the browser's context and tab.** `browser.contexts()[0]` and `context.pages()[0]` (properties in Python: `browser.contexts[0]`, `context.pages[0]`). Create new ones only if none exist. Extra contexts look like automation. +4. **No broad CDP hooks.** `page.route`, `context.route`, `page.on('request')`, `page.exposeFunction`, `page.addInitScript`, `context.addInitScript`: only when the task can't be done otherwise, and then as narrow as possible. They change how the browser behaves and that's detectable. +5. **Don't patch fingerprints.** It's real Chrome on real hardware. Overriding `navigator`, the user agent, WebGL, canvas, timezone or locale makes it look fake. Set `country`, `timezone` and `language` on the create call instead. +6. **Always stop the session.** `try / finally`, with `DELETE /v1/browser/session?sessionId=…` in the `finally`. Sessions are metered by bandwidth and end on their own at `duration` (an hour by default); until then a forgotten session holds a concurrency slot and its `cdpUrl` stays live. `browser.close()` doesn't stop it. +7. **Keep the `sessionId`.** Log it with the run. Every session has a record under Telemetry → Sessions with node, network, timing and cost. If the person wants to watch, print `https://viewer.driver.dev?ws=` as soon as the session exists. See [Live view](/docs/sessions/live-view). +8. **Report what a person must fix; retry only what time fixes.** `402` (out of credits) and `403` (concurrency or plan limit): report, never loop. `503` (no capacity; `Retry-After: 2`, `"code": "browser_capacity_unavailable"`): back off with jitter, cap the attempts, then report. `504` (Chrome didn't start in time) and `500`: retry once. `429`: back off. Full table on [Errors](/docs/sessions/errors). +9. **Stay under the concurrency limit.** `GET /v1/account/billing` reports it as `plan.concurrent_browsers`; sessions beyond it get 403. +10. **Use a pool when start-up latency matters.** A [pool](/docs/sessions/pools) keeps browsers warm with fixed options. `POST /v1/browser/pools/{poolId}/acquire` with `{ "waitMs": 10000 }` answers in under a second with `{ leaseId, session }`; `session` is an ordinary session from there. Release with `POST /v1/browser/pools/{poolId}/release` `{ leaseId }` in the same `finally` where you'd stop a session. Don't create pools without asking: they hold warm browsers against an account-wide cap. +11. **When a site challenges or blocks, escalate in this order.** Remove every hook and tactic. Then `type: "hosted_stealth"`, then `"hosted_privacy"`, then `captchaSolver: true`. When a step needs a person, give them the live view URL. See [Browser types](/docs/options/browser-types) and [CAPTCHA solving](/docs/options/captcha-and-adblock). ## How to write the automation -- **Minimal logic wins.** The shortest script that reaches the outcome is the most reliable and the least detectable. Go to the page, do the action, read the result. Do not add retries, waits, listeners, request routing or "stealth" helpers unless a specific failure on a specific site demands them, and then add the smallest thing that fixes it. -- **Fast and accurate, in that order of effort.** Before writing browser steps, ask whether the outcome is reachable faster: a direct URL instead of clicking through navigation, one session doing several pages instead of one session per page, a persisted [profile](/docs/options/profiles) instead of logging in every run. Then keep the browser steps short: no fixed sleeps, rely on the framework's auto-waiting, stop the session the moment the result is in hand. -- **Test the automation for real, every time.** Run it against the live target through Driver and check the result, not just that the script exits. Confirm the data is right, the action happened, and the session was stopped: `GET /v1/browser/session?sessionId=…` shows `status: "completed"`, a `stoppedAt` and the metered `bandwidthBytes` (the same record is under Telemetry → Sessions in the dashboard). A run that "worked" but read the wrong element is worse than one that failed loudly. -- **Unattended jobs clean up after earlier runs.** A crash before `finally` leaves a session running until its `duration`. At start-up, list `status=active` and `status=starting` (`listSessions` / `list_sessions` in the reference client), stop the ones your job created (match on `note`), then start the new one. Exit non-zero when the result was not verified, so the scheduler reports the failure. -- **Do not pollute the session.** Every extra CDP feature you switch on, every injected script, every overridden property is a signal. If you did not need it to reach the outcome, take it out. +- **Minimal logic wins.** Go to the page, do the action, read the result. No retries, waits, listeners, request routing or "stealth" helpers until a specific failure on a specific site demands one, and then the smallest thing that fixes it. +- **Fast first.** Before writing browser steps: is there a direct URL? Can one session do several pages? Can a persisted [profile](/docs/options/profiles) replace logging in every run? Then keep the steps short: no fixed sleeps, use the framework's auto-waiting, stop the session the moment you have the result. +- **Test for real, every time.** Run against the live target through Driver and check the result, not the exit code: the data is right, the action happened, the session was stopped (`GET /v1/browser/session?sessionId=…` shows `status: "completed"`, `stoppedAt`, `bandwidthBytes`). A run that "worked" but read the wrong element is worse than one that failed loudly. +- **Unattended jobs clean up after earlier runs.** A crash before `finally` leaves a session running until its `duration`. At start-up, list `status=active` and `status=starting` (`listSessions` / `list_sessions` in the reference client), stop the ones your job created (match on `note`), then start the new one. Exit non-zero when the result wasn't verified. +- **Don't pollute the session.** Every CDP feature you switch on, every injected script, every overridden property is a signal. If you didn't need it, take it out. ## Handoff prompt -Give this to a coding agent along with the target and the outcome you want. It is enough to start; the rest of this page fills in the details an agent needs once it is writing code. +Give this to a coding agent with the target and the outcome you want. It's enough to start; the rest of this page is for once it's writing code. ```text Integrate Driver (driver.dev), hosted real-Chrome browser infrastructure, into this project. @@ -72,15 +72,15 @@ Integrate Driver (driver.dev), hosted real-Chrome browser infrastructure, into t ## The three calls -Base URL `https://api.driver.dev`. Every call carries `Authorization: Bearer $DRIVER_API_KEY`. Store the key in an environment variable named `DRIVER_API_KEY`; never commit it. Node's global `fetch` has no timeout, which is right for the create call; if you add one, allow three minutes. +Base URL `https://api.driver.dev`. Every call carries `Authorization: Bearer $DRIVER_API_KEY`. Keep the key in an environment variable named `DRIVER_API_KEY`. Node's global `fetch` has no timeout, which suits the create call; if you add one, allow three minutes. | Step | Call | Notes | |---|---|---| -| Create | `POST /v1/browser/session` with a JSON body | All fields optional. Returns `{ sessionId, status, cdpUrl, servedBy, createdAt, stoppedAt, note }`. The call waits for Chrome (up to 20 s, on some machines up to 3 min), so `cdpUrl` is normally set and `status` is `active`; if you ever get `starting`, poll `GET` until `cdpUrl` appears. | +| Create | `POST /v1/browser/session` with a JSON body | All fields optional. Returns `{ sessionId, status, cdpUrl, servedBy, createdAt, stoppedAt, note }`. The call waits for Chrome (up to 20 s, up to 3 min on some machines), so `cdpUrl` is set and `status` is `active`. If you get `starting`, poll `GET` until `cdpUrl` appears. | | Use | `chromium.connectOverCDP(cdpUrl)` | From Patchright. Reuse the first context and page. | | Stop | `DELETE /v1/browser/session?sessionId=` | Returns `{ success: true }`. | -Useful extras: `GET /v1/browser/session?sessionId=` for status and bandwidth, `GET /v1/browser/sessions?status=active` to list, `PATCH /v1/browser/session?sessionId=` with `{ "note": "…" }` to label a run. Full options for the create body are on [Create a session](/docs/sessions/create#options). When a [pool](/docs/sessions/pools) exists, `POST /v1/browser/pools/{poolId}/acquire` replaces the create call and `POST /v1/browser/pools/{poolId}/release` the stop; pool ids go in the path. +Also useful: `GET /v1/browser/session?sessionId=` for status and bandwidth, `GET /v1/browser/sessions?status=active` to list, `PATCH /v1/browser/session?sessionId=` with `{ "note": "…" }` to label a run. Every create option is on [Create a session](/docs/sessions/create#options). With a [pool](/docs/sessions/pools), `POST /v1/browser/pools/{poolId}/acquire` replaces the create call and `POST /v1/browser/pools/{poolId}/release` the stop; pool ids go in the path. ## Reference implementation @@ -374,12 +374,12 @@ def browser_page(**opts): - [ ] No `launch()` anywhere; the only browser entry point is `connectOverCDP(cdpUrl)`. - [ ] The first context and page are reused. - [ ] The session is stopped in a `finally`. -- [ ] `DRIVER_API_KEY` comes from the environment and is not committed. -- [ ] 402 and 403 are reported to the user; 503 is retried only with a cap and a growing delay. +- [ ] `DRIVER_API_KEY` comes from the environment and isn't committed. +- [ ] 402 and 403 are reported; 503 is retried with a cap and a growing delay. - [ ] The result was verified through `GET /v1/browser/session` (status `completed`), not assumed. -- [ ] A browser taken from a pool is released (or stopped) in the same `finally`; no pool was created without asking. +- [ ] A browser from a pool is released (or stopped) in the same `finally`; no pool was created without asking. ## Machine-readable resources -- `https://docs.driver.dev/llms.txt` lists every page of these docs; `https://docs.driver.dev/llms-full.txt` is all of them in one file. Any page is available as Markdown by appending `.md` to its URL. +- `https://docs.driver.dev/llms.txt` lists every page; `https://docs.driver.dev/llms-full.txt` is all of them in one file. Any page is Markdown with `.md` appended to its URL. - `https://api.driver.dev/doc` is the live OpenAPI document; `https://api.driver.dev/scalar` renders it. diff --git a/docs/start/index.mdx b/docs/start/index.mdx index 9e511e8..f73a181 100644 --- a/docs/start/index.mdx +++ b/docs/start/index.mdx @@ -1,19 +1,19 @@ --- title: "Introduction" -description: "Driver runs real Chrome on real hardware and hands you a CDP URL. Create a session, connect your framework, stop the session." +description: "Driver runs real Chrome on real hardware and gives you a CDP URL. Create a session, connect, do the work, stop it." --- -Driver is hosted browser infrastructure. Every session is a real Chrome window on a real machine, on a residential network in the country you ask for, reachable over the Chrome DevTools Protocol in seconds. You bring the automation; Driver brings the browser. +Driver is hosted browser infrastructure. A session is one real Chrome window on one real machine, on a residential connection in the country you pick, reachable over the Chrome DevTools Protocol a few seconds after you ask for it. You write the automation; we run the browser. Create a session, connect with Patchright, stop it. Five minutes. - You are a coding agent? Start here: the rules, the endpoints, the pitfalls. + Coding agent? Start here: the rules, the endpoints, the mistakes. - How to stay unblocked: framework choice, CDP hygiene, session cleanup. + How to stay unblocked: framework, CDP hygiene, cleanup. Every endpoint, generated from the live OpenAPI document. @@ -24,16 +24,16 @@ Driver is hosted browser infrastructure. Every session is a real Chrome window o - `POST /v1/browser/session` with the options you need (country, profile, proxy, window size). The response carries a `sessionId` and a `cdpUrl`. + `POST /v1/browser/session` with the options you need. You get a `sessionId` and a `cdpUrl`. - Point your framework at `cdpUrl`. Use [Patchright](/docs/frameworks/patchright) unless you have a reason not to; stock Playwright and Puppeteer leave fingerprints that get sessions blocked. + Point your framework at `cdpUrl`. Use [Patchright](/docs/frameworks/patchright); stock Playwright and Puppeteer leave fingerprints that get sessions blocked. - Navigate, fill, click, extract. The browser is a normal Chrome: no headless quirks, no emulated hardware. + Navigate, fill, click, extract. It's a normal Chrome: no headless quirks, no emulated hardware. - `DELETE /v1/browser/session?sessionId=…` releases the browser. Bandwidth is what you pay for, so stop what you no longer need. + `DELETE /v1/browser/session?sessionId=…`. You pay for bandwidth, so stop what you're done with. @@ -41,16 +41,16 @@ Driver is hosted browser infrastructure. Every session is a real Chrome window o | | | |---|---| -| **Real Chrome** | Consumer Chrome builds on real hardware, not a headless container. Sites see a normal browser. | -| **Residential networks** | Sessions egress from residential connections in the country you choose. Bring your own SOCKS5 proxy when you need to. | -| **Profiles** | Save cookies and local state under a name and reuse them, so persistent logins survive between sessions. | -| **Browser pools** | Keep browsers warm with fixed options and take one in under a second when a job needs it. | -| **CAPTCHA solving** | One flag loads Driver's solver into the session. | +| **Real Chrome** | Consumer Chrome on real hardware, not a headless container. | +| **Residential networks** | Sessions egress from residential connections in the country you choose. Bring your own SOCKS5 proxy if you need to. | +| **Profiles** | Save cookies and local state under a name and reuse them, so logins survive between sessions. | +| **Browser pools** | Keep browsers warm with fixed options and take one in under a second. | +| **CAPTCHA solving** | One flag loads our solver into the session. | | **Live view** | Watch and drive any running session from the dashboard or a viewer URL. | ## Base URL and authentication -All requests go to `https://api.driver.dev` with your workspace API key as a bearer token. Keys are created under **Settings → API keys** in the [dashboard](https://app.driver.dev). +Everything goes to `https://api.driver.dev` with your workspace API key as a bearer token. Keys are under **Settings → API keys** in the [dashboard](https://app.driver.dev). ```bash Authorization: Bearer $DRIVER_API_KEY @@ -58,7 +58,7 @@ Authorization: Bearer $DRIVER_API_KEY ## Next -- [Quickstart](/docs/start/quickstart) creates and stops a session end to end. -- [Create a session](/docs/sessions/create#options) explains every field you can pass on create. -- [Browser pools](/docs/sessions/pools) when start-up latency matters: warm browsers, acquired in under a second. -- [Frameworks](/docs/frameworks/patchright) shows the connection code for Patchright, Playwright, Puppeteer, Browser Use, Stagehand and Crawl4AI. +- [Quickstart](/docs/start/quickstart): create and stop a session end to end. +- [Create a session](/docs/sessions/create#options): every field you can pass. +- [Browser pools](/docs/sessions/pools): warm browsers, acquired in under a second. +- [Frameworks](/docs/frameworks/patchright): connection code for Patchright, Playwright, Puppeteer, Browser Use, Stagehand and Crawl4AI. diff --git a/docs/start/quickstart.mdx b/docs/start/quickstart.mdx index 3dfb227..f9802be 100644 --- a/docs/start/quickstart.mdx +++ b/docs/start/quickstart.mdx @@ -1,11 +1,11 @@ --- title: "Quickstart" -description: "Create a Driver session, connect with Patchright, read a page title, and stop the session." +description: "Create a session, connect with Patchright, read a page title, stop the session." --- - Sign in to the [dashboard](https://app.driver.dev), open **Settings → API keys**, and create a key. Put it in your environment as `DRIVER_API_KEY`. Never commit it. + Sign in to the [dashboard](https://app.driver.dev), open **Settings → API keys**, create a key. Put it in your environment as `DRIVER_API_KEY`. Don't commit it. ```bash export DRIVER_API_KEY="dr_…" @@ -13,7 +13,7 @@ description: "Create a Driver session, connect with Patchright, read a page titl - Patchright is a drop-in Playwright with the CDP fingerprints removed. Use it instead of stock Playwright, otherwise sites that watch for automation will block your session. Details in [Why Patchright](/docs/frameworks/patchright#why-patchright). + Patchright is Playwright with the CDP fingerprints removed. Use it instead of stock Playwright or sites that watch for automation will block the session. Details in [Why Patchright](/docs/frameworks/patchright#why-patchright). ```bash npm @@ -24,9 +24,9 @@ description: "Create a Driver session, connect with Patchright, read a page titl ``` - You do not need `playwright install` or any browser download: Driver provides the browser. + No `playwright install`, no browser download. Driver provides the browser. - The TypeScript examples use top-level `await` and the global `fetch`: Node 18 or newer, in an ES module (`"type": "module"` in `package.json`, or a `.mts` file). Run a file with `npx tsx file.ts`. The Python examples need Python 3.8 or newer. + The TypeScript examples use top-level `await` and the global `fetch`: Node 18+, in an ES module (`"type": "module"` in `package.json`, or a `.mts` file), run with `npx tsx file.ts`. Python examples need 3.8+. @@ -120,21 +120,21 @@ description: "Create a Driver session, connect with Patchright, read a page titl - Every session has a page in the dashboard under **Telemetry → Sessions** with a live view while it runs and the full record afterwards: node, country, network, bandwidth, cost. See [Live view](/docs/sessions/live-view). Without a dashboard, `GET /v1/browser/session?sessionId=…` tells you the same essentials: `status` becomes `completed`, `stoppedAt` is set, `bandwidthBytes` is the metered total. + Every session has a page under **Telemetry → Sessions** in the dashboard: live view while it runs, then the record (node, country, network, bandwidth, cost). See [Live view](/docs/sessions/live-view). From the API, `GET /v1/browser/session?sessionId=…` shows `status` `completed`, `stoppedAt`, and `bandwidthBytes` once it's done. -## What just happened +## What happened -The create call returned as soon as Chrome was reachable over CDP, usually in a few seconds (it waits up to 20 seconds, on some machines up to three minutes, then answers `504`). `cdpUrl` is a WebSocket endpoint to that one browser. Connecting with `connectOverCDP` attaches to the running browser instead of launching one, which is why there is no `chromium.launch()` anywhere and no browser download on your machine. +The create call returned once Chrome was reachable over CDP, a few seconds in. It waits up to 20 s (three minutes on some machines), then answers `504`. `cdpUrl` is a WebSocket endpoint to that one browser. `connectOverCDP` attaches to it, which is why there's no `chromium.launch()` and nothing to download. -The browser opened with one context and one tab already. Reusing them keeps the session looking like a person opened Chrome and started browsing; creating extra contexts is one of the things sites notice. +The browser came up with one context and one tab. Reusing them looks like a person who opened Chrome and started browsing. Creating extra contexts is one of the things sites notice. -Stopping the session releases the machine and closes the bill for that session. Sessions also stop on their own at the `duration` you set, or after an hour by default. +Stopping the session releases the machine and closes the bill. Sessions also stop on their own at `duration`, an hour by default. ## Next - [For agents](/docs/start/for-agents) if a coding agent is doing the integration. -- [Create a session](/docs/sessions/create#options) for every field you can pass: browser type, location, proxies, profiles, window size. +- [Create a session](/docs/sessions/create#options) for every field: browser type, location, proxies, profiles, window size. - [Best practices](/docs/start/best-practices) before you scale up. - [Browser pools](/docs/sessions/pools) when a few seconds of start-up per browser is too slow. diff --git a/scripts/gen-countries.mjs b/scripts/gen-countries.mjs index 272472a..65407d3 100644 --- a/scripts/gen-countries.mjs +++ b/scripts/gen-countries.mjs @@ -21,9 +21,9 @@ title: "Countries" description: "Every country you can pass on create, with the timezones and languages it accepts. Generated from the API's locale catalog." --- -\`country\` accepts any of the ${codes.length} two-letter codes below. \`timezone\` must be one of that country's timezones and \`language\` one of its languages, or the request is rejected with \`400\`. Leave both out and Driver picks the first-listed timezone (or a managed-network region where it has one) and the country's primary language. US sessions always use \`en-US\`. See [Location](/docs/options/location) for how the three stay coherent. +\`country\` accepts any of the ${codes.length} codes below. \`timezone\` must be one of that country's timezones and \`language\` one of its languages, or the request is a \`400\`. Leave both out and you get the first-listed timezone (or a managed-network region where Driver has one) and the country's primary language. US sessions are always \`en-US\`. [Location](/docs/options/location) covers how the three stay coherent. -Availability of machines varies by country; a country with no machine free at the moment answers \`503\`. +Machine availability varies by country. No free machine in a country is a \`503\`. | Code | Country | Timezones | Languages | |---|---|---|---| From 321ea0b9ae39b3ae0d08d44463b6f93a836c1b1b Mon Sep 17 00:00:00 2001 From: browsercash Date: Mon, 21 Sep 2026 01:10:16 -0400 Subject: [PATCH 10/11] Refresh preview from v2: pools fixes --- api-reference/openapi.json | 59 ++++++++++++++++++++++++++++++++++++-- docs/sessions/pools.mdx | 8 ++---- scripts/sync-openapi.mjs | 4 +-- 3 files changed, 60 insertions(+), 11 deletions(-) diff --git a/api-reference/openapi.json b/api-reference/openapi.json index 088ec60..de20157 100644 --- a/api-reference/openapi.json +++ b/api-reference/openapi.json @@ -2466,6 +2466,15 @@ "language": { "type": "string" }, + "acceptLang": { + "type": "string" + }, + "ttsLanguage": { + "type": "string" + }, + "disableSpeechApi": { + "type": "boolean" + }, "profile": { "type": "object", "properties": { @@ -2510,7 +2519,7 @@ }, "default": {}, "additionalProperties": false, - "description": "The create options every browser in the pool starts with (same fields as POST /v1/browser/session, without type, duration and browserCheck). Values are not validated on create. As of 2026-09-20 country and proxyUrl are not applied to warm browsers (they egress from the default US network); url and note are." + "description": "The create options every browser in the pool starts with (same fields as POST /v1/browser/session, without type, duration and browserCheck). Validated like a create body (an unsupported country is a 400). Responses echo the template with the resolved timezone and a redacted proxyUrl." }, "leaseTimeoutSeconds": { "type": "integer", @@ -2611,6 +2620,15 @@ "language": { "type": "string" }, + "acceptLang": { + "type": "string" + }, + "ttsLanguage": { + "type": "string" + }, + "disableSpeechApi": { + "type": "boolean" + }, "profile": { "type": "object", "properties": { @@ -2880,6 +2898,15 @@ "language": { "type": "string" }, + "acceptLang": { + "type": "string" + }, + "ttsLanguage": { + "type": "string" + }, + "disableSpeechApi": { + "type": "boolean" + }, "profile": { "type": "object", "properties": { @@ -3165,6 +3192,15 @@ "language": { "type": "string" }, + "acceptLang": { + "type": "string" + }, + "ttsLanguage": { + "type": "string" + }, + "disableSpeechApi": { + "type": "boolean" + }, "profile": { "type": "object", "properties": { @@ -3414,6 +3450,15 @@ "language": { "type": "string" }, + "acceptLang": { + "type": "string" + }, + "ttsLanguage": { + "type": "string" + }, + "disableSpeechApi": { + "type": "boolean" + }, "profile": { "type": "object", "properties": { @@ -3456,9 +3501,8 @@ } } }, - "default": {}, "additionalProperties": false, - "description": "Replaces the whole browser template; include every option to keep. Changing it restarts the warm browsers." + "description": "Replaces the browser template as a whole and rebuilds the warm browsers from it. Leave it out to keep the current template." }, "leaseTimeoutSeconds": { "type": "integer", @@ -3557,6 +3601,15 @@ "language": { "type": "string" }, + "acceptLang": { + "type": "string" + }, + "ttsLanguage": { + "type": "string" + }, + "disableSpeechApi": { + "type": "boolean" + }, "profile": { "type": "object", "properties": { diff --git a/docs/sessions/pools.mdx b/docs/sessions/pools.mdx index 395ad5f..30fe448 100644 --- a/docs/sessions/pools.mdx +++ b/docs/sessions/pools.mdx @@ -7,10 +7,6 @@ A pool keeps browsers running with the options you chose. Acquire hands you one Pools are also in the [dashboard](https://app.driver.dev/pools) under **Browsers → Browser pools**; **Launch browser** on the Overview can take one from any ready pool. - -**Network options aren't applied to pooled browsers yet.** A pool records `country` and the session list reports it, but as of 2026-09-20 every warm browser egresses from the default US network (GB and JP pools both answered with a US address and `America/New_York`; a plain create with the same `country` egressed correctly). An unreachable `proxyUrl` or a nonexistent country still warms and reports `ready`. Until this is fixed, use pools for jobs that are fine on the US network and [create sessions](/docs/sessions/create) for geo-specific or proxied ones. `url` and `note` are applied. - - ## Create a pool @@ -88,7 +84,7 @@ Answers `201` at once with the pool `warming`; measured, it was `ready` two to f | `leaseTimeoutSeconds` | integer | `900` | How long an acquired browser may be held, 30 to 86400. The session is stopped when the lease expires. | | `maxReadyAgeSeconds` | integer | `3600` | How long a browser may sit ready before it's swapped for a fresh one, 30 to 86400. The swap takes two to three seconds, during which `ready` is one lower. | -Unknown fields, top-level or inside `browser`, are a `400` (`bogus is not supported by this endpoint`). Values inside `browser` aren't checked the way a create body is: `country: "ZZ"` or an unreachable `proxyUrl` is accepted and the pool reports `ready` (see the warning). `lastError` is set when browsers fail to start. +Unknown fields, top-level or inside `browser`, are a `400` (`bogus is not supported by this endpoint`), and so is a country the platform doesn't serve (`Unsupported country "ZZ"`). Pool responses echo the template with what the platform resolved for it (`timezone`, and a `proxyUrl` with the credentials redacted). `lastError` is set when browsers fail to start. `profile` works as on a session. Leave `persist` off (the default) so every browser loads it read-only; several browsers writing one profile would overwrite each other. @@ -226,7 +222,7 @@ requests.post( |---|---| | `GET /v1/browser/pools` | `{ "pools": [ … ] }`, every pool in the workspace with its counters. | | `GET /v1/browser/pools/{poolId}` | One pool. | -| `PATCH /v1/browser/pools/{poolId}` | Change `name`, `size`, `browser`, `leaseTimeoutSeconds`, `maxReadyAgeSeconds` or `paused` (`true` stops the warm browsers and refuses acquires; `false` warms them again). Every update restarts the warm browsers (`warming` for a few seconds; leased ones untouched). Always send the whole `browser` template: an update without it resets the template to `{}`, so a bare rename or resize drops the country, start URL and everything else. | +| `PATCH /v1/browser/pools/{poolId}` | Change `name`, `size`, `browser`, `leaseTimeoutSeconds`, `maxReadyAgeSeconds` or `paused` (`true` stops the warm browsers and refuses acquires; `false` warms them again). Send only what changes. A new `browser` replaces the template as a whole and the warm browsers are replaced with ones built from it; a rename, resize or timeout change leaves the ready browsers alone (a resize up warms more, a resize down stops the surplus). | | `POST /v1/browser/pools/{poolId}/flush` | Stops every ready browser and warms fresh ones. Answers `{ "success": true, "stopped": n }`. Leased browsers are untouched. | | `DELETE /v1/browser/pools/{poolId}` | Stops the warm browsers and removes the pool. With leased browsers: `409` `Pool has active leases; pass force=true to stop them`; `?force=true` (literal `true`; `1` is a `400`) stops those sessions too. Afterwards the pool is `404`. | diff --git a/scripts/sync-openapi.mjs b/scripts/sync-openapi.mjs index 884b873..b19cdae 100644 --- a/scripts/sync-openapi.mjs +++ b/scripts/sync-openapi.mjs @@ -130,7 +130,7 @@ if (poolCreate) { if (props) { props.name.description = 'Unique in the workspace, 1 to 64 characters.' props.size.description = 'Browsers to keep ready. All pools together may keep 30 warm per account.' - props.browser.description = 'The create options every browser in the pool starts with (same fields as POST /v1/browser/session, without type, duration and browserCheck). Values are not validated on create. As of 2026-09-20 country and proxyUrl are not applied to warm browsers (they egress from the default US network); url and note are.' + props.browser.description = 'The create options every browser in the pool starts with (same fields as POST /v1/browser/session, without type, duration and browserCheck). Validated like a create body (an unsupported country is a 400). Responses echo the template with the resolved timezone and a redacted proxyUrl.' props.leaseTimeoutSeconds.description = 'How long an acquired browser may be held, in seconds (30 to 86400, default 900). The session is stopped when the lease expires.' props.maxReadyAgeSeconds.description = 'How long a browser may wait ready before it is replaced with a fresh one, in seconds (30 to 86400, default 3600).' } @@ -139,7 +139,7 @@ if (poolCreate) { const poolPatch = doc.paths['/v1/browser/pools/{poolId}']?.patch if (poolPatch) { const props = poolPatch.requestBody?.content?.['application/json']?.schema?.properties - if (props?.browser) props.browser.description = 'Replaces the whole browser template; include every option to keep. Changing it restarts the warm browsers.' + if (props?.browser) props.browser.description = 'Replaces the browser template as a whole and rebuilds the warm browsers from it. Leave it out to keep the current template.' if (props?.paused) props.paused.description = 'true stops the warm browsers and refuses acquires; false warms them again.' } const poolDelete = doc.paths['/v1/browser/pools/{poolId}']?.delete From badc16871a19a5e8bdb13f4fde4c9a49ccc02525 Mon Sep 17 00:00:00 2001 From: browsercash Date: Mon, 21 Sep 2026 01:24:40 -0400 Subject: [PATCH 11/11] Refresh preview from v2: dedicated IPs --- api-reference/overview.mdx | 1 + docs/account/billing.mdx | 4 +++ docs/account/dashboard.mdx | 1 + docs/options/proxies.mdx | 57 +++++++++++++++++++++++++++++++++++++- docs/sessions/errors.mdx | 8 +++--- docs/start/for-agents.mdx | 3 +- 6 files changed, 68 insertions(+), 6 deletions(-) diff --git a/api-reference/overview.mdx b/api-reference/overview.mdx index 4865b0d..2c1a756 100644 --- a/api-reference/overview.mdx +++ b/api-reference/overview.mdx @@ -33,6 +33,7 @@ Authorization: Bearer $DRIVER_API_KEY | Sessions | `POST/GET/PATCH/DELETE /v1/browser/session`, `GET /v1/browser/sessions` | | Pools | `POST/GET /v1/browser/pools`, `GET/PATCH/DELETE /v1/browser/pools/{poolId}`, `POST /v1/browser/pools/{poolId}/acquire`, `…/release`, `…/flush` | | Profiles | `GET /v1/browser/profiles`, `DELETE /v1/browser/profile` | +| Network | `GET /v1/network/ips` (dedicated IPs; next gateway release) | | Account | `GET /v1/account/balance`, `GET /v1/account/billing` | | Extensions | `POST/GET /v1/browser/extensions`, `GET/PUT/DELETE /v1/browser/extensions/{extensionId}` | diff --git a/docs/account/billing.mdx b/docs/account/billing.mdx index 5848ded..e8a0997 100644 --- a/docs/account/billing.mdx +++ b/docs/account/billing.mdx @@ -7,6 +7,10 @@ Driver bills on **bandwidth**: bytes a session moves, metered per megabyte again Cost control is short sessions, stopped when the job is done. `duration` on create caps what a forgotten session can cost. Warm browsers in a [pool](/docs/sessions/pools) aren't billed and hold no slot; a browser becomes a billed, counted session when acquired, and the lease timeout caps how long it can be held. +[Dedicated IPs](/docs/options/proxies#dedicated-ips) are the one thing not paid in credits: a subscription line on +your card at $5 per address per month, prorated when you add or release one. Bandwidth through them is metered +like any other session. + ## Balance diff --git a/docs/account/dashboard.mdx b/docs/account/dashboard.mdx index 5539703..2e89f61 100644 --- a/docs/account/dashboard.mdx +++ b/docs/account/dashboard.mdx @@ -11,6 +11,7 @@ The [dashboard](https://app.driver.dev) is the other half of the API. Everything | **Workbench** | Script editor, live browser, output log. Pick a template, set country, network, profile, CAPTCHA solver and proxy, press Run. Try options here before writing them into code. | | **Profiles** | The workspace's saved [profiles](/docs/options/profiles). Launch a session on one to sign in by hand. | | **Browser pools** | [Pools](/docs/sessions/pools) of warm browsers: size, country, profile, start URL, lease timeout. Pause, flush or delete them; see how many are ready and in use. **Launch browser** on the Overview can take a browser from any ready pool. | +| **Network** | Reserve and manage [dedicated IPs](/docs/options/proxies#dedicated-ips): country, quantity, TCP profile, label; release one; launch a session on it. | | **Sessions** (Telemetry) | Every session, filterable by status. Each record: live view while running, then node, country, network, bandwidth, cost and timestamps. Stop one, or stop all. | | **Workspace** | Name, members, invitations. Admins manage members, keys and billing. | | **API keys** | The root key and named keys: create, roll, delete. Also a ready-to-paste briefing for your coding agent. | diff --git a/docs/options/proxies.mdx b/docs/options/proxies.mdx index fb0eeca..2c0feba 100644 --- a/docs/options/proxies.mdx +++ b/docs/options/proxies.mdx @@ -1,6 +1,6 @@ --- title: "Proxies" -description: "Sessions egress from Driver's residential network by default. proxyUrl routes through your own SOCKS5 or TCPerfect endpoint." +description: "Sessions egress from Driver's residential network by default. proxyUrl routes through your own SOCKS5 or TCPerfect endpoint, or one of your dedicated IPs." --- Traffic leaves from a residential connection in the requested country by default. Set `proxyUrl` when you need a specific egress: your own provider, a sticky IP, or a TCPerfect address. @@ -24,6 +24,7 @@ session = create_session(proxyUrl="socks5://user:pass@proxy.example.com:1080") | `socks5://user:pass@host:port` | DNS resolved on the session's machine. | | `socks5h://user:pass@host:port` | DNS resolved through the proxy. Use it when the proxy's location should decide DNS answers too. | | `tcperfect://user:pass@host:port/exit-ip` | Driver's TCPerfect network: proxied, with the machine's own TCP fingerprint kept intact. | +| `dedicated://`, `dedicated://any` | One of the workspace's [dedicated IPs](#dedicated-ips). No credentials: Driver holds them. | The proxy must be reachable from Driver's machines. HTTP proxies aren't supported. @@ -40,3 +41,57 @@ Hosted sessions in Canada without a proxy egress through Driver's TCPerfect inve Driver connects through the proxy before launch to find its exit. A proxy that doesn't answer is a `400`: `Could not determine SOCKS5 proxy egress location; verify the proxy or specify both timezone and language.` Passing both `timezone` and `language` skips the lookup; the session then starts even with a dead proxy, and pages fail to load inside it. An `http://` URL is rejected outright. Credentials travel in the create request only. They aren't stored on the session record. + +## Dedicated IPs + + +API access to dedicated IPs ships with the next gateway release; the dashboard flow works today. + + +A dedicated IP is a public address only your workspace's sessions use, so its reputation is yours alone. Reserve +them under **Browsers → Network** in the [dashboard](https://app.driver.dev/network): pick a country, a quantity +and a TCP fingerprint profile (`windows11` by default). $5 per address per month, billed on your card as a +subscription line; adding or releasing mid-month is prorated. + +You never see the SOCKS5 URL behind an address. On session create, name the address: + + +```bash cURL +curl -X POST https://api.driver.dev/v1/browser/session \ + -H "Authorization: Bearer $DRIVER_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ "proxyUrl": "dedicated://65.86.104.7" }' +``` +```typescript TypeScript +// a specific address, or any of yours, optionally in one country +const session = await createSession({ proxyUrl: "dedicated://65.86.104.7" }); +const anyUs = await createSession({ proxyUrl: "dedicated://any~country-US" }); +``` +```python Python +session = create_session(proxyUrl="dedicated://65.86.104.7") +any_us = create_session(proxyUrl="dedicated://any~country-US") +``` + + +| Spec | Meaning | +|---|---| +| `dedicated://` | This address. | +| `dedicated://any` | Any address you own, chosen at random. | +| `dedicated://any~country-US` | Any of yours in that country. | +| `…?tcp=` | Override the TCP fingerprint profile for this session (`windows11`, `windows-65535`, `android-scale1`, `android-scale3`, `android-scale6`, `macos-scale6`, `macos-scale7`, `auto`). Default is the profile set on the address. | + +The address decides the session's `country` and `timezone`. Leave them out; a `country` you pass has to match. + +`GET /v1/network/ips` lists your addresses with country, city, network type, profile, label and status. Copy an +address from there, or from the Network page. + +| Status | Code | Meaning | +|---|---|---| +| `400` | `invalid_spec` | Not one of the forms above, or a `country` that contradicts the address. | +| `403` | `dedicated_ip_not_owned` | The address belongs to another workspace, or the key isn't linked to one. | +| `404` | `no_dedicated_ip` | Nothing matches: no addresses, or none in that country. | +| `503` | `dedicated_ip_unhealthy` | The address failed its last health check. Use another one and report it. | + +Release an address from the Network page; the subscription drops by one seat and the address goes back to the +pool. If a renewal payment fails, the addresses keep working for 7 days while the card is retried, then they're +released. diff --git a/docs/sessions/errors.mdx b/docs/sessions/errors.mdx index 810c9f8..0e55947 100644 --- a/docs/sessions/errors.mdx +++ b/docs/sessions/errors.mdx @@ -19,16 +19,16 @@ Common bodies: `401` `Unauthorized: Missing or invalid Authorization header` or | Status | Meaning | Retry? | |---|---|---| -| `400` | Invalid or incompatible options, an unknown field on create, a missing `sessionId`, or an update to an ended session. The message names the problem. | No. Fix the request. | +| `400` | Invalid or incompatible options, an unknown field on create, a missing `sessionId`, an update to an ended session, or a malformed dedicated IP spec (`code: invalid_spec`). The message names the problem. | No. Fix the request. | | `401` | Missing or invalid API key. | No. Check `Authorization: Bearer …`. | | `402` | The workspace is out of credits. | No. Add credits in the dashboard. | -| `403` | Concurrent-browser limit (`plan.concurrent_browsers` on `GET /v1/account/billing`; `starting` sessions count, a stop frees the slot as soon as it returns), or a feature (extensions) the account isn't enabled for. | Only after a session ends. Never in a loop. | -| `404` | Session, extension, pool, lease or account not found: unknown id, or one belonging to another workspace. Deleting an unknown profile isn't a 404; it answers `{ "success": true }`. | No. | +| `403` | Concurrent-browser limit (`plan.concurrent_browsers` on `GET /v1/account/billing`; `starting` sessions count, a stop frees the slot as soon as it returns), a feature (extensions) the account isn't enabled for, or a [dedicated IP](/docs/options/proxies#dedicated-ips) the workspace doesn't own (`code: dedicated_ip_not_owned`). | Only after a session ends. Never in a loop. | +| `404` | Session, extension, pool, lease or account not found: unknown id, or one belonging to another workspace. On create, no dedicated IP matches the spec (`code: no_dedicated_ip`). Deleting an unknown profile isn't a 404; it answers `{ "success": true }`. | No. | | `409` | Browser pools only: a duplicate pool name, a size over the account's warm-browser cap, an acquire that found nothing ready within `waitMs` (`No browser is currently ready in this pool`), an acquire on a paused pool, or a delete with active leases. See [Browser pools](/docs/sessions/pools#errors). | Only the acquire one, as for `503`: back off, cap, then fall back to a plain create or report. | | `429` | Too many requests, from the edge; the API itself doesn't rate-limit today. | Yes: a few attempts, a couple of seconds apart and growing. | | `502` | The edge couldn't reach the API. Not JSON. | Once, after a couple of seconds. | | `500` | Something failed on Driver's side. | Once. | -| `503` | No browser capacity. Carries `Retry-After: 2` and `"code": "browser_capacity_unavailable"`. Or the session service is unavailable. | Yes: back off with jitter, cap the attempts, then tell the user. | +| `503` | No browser capacity. Carries `Retry-After: 2` and `"code": "browser_capacity_unavailable"`. Or the session service is unavailable. With `code: dedicated_ip_unhealthy`, the dedicated IP failed its last health check: use another address. | Yes: back off with jitter, cap the attempts, then tell the user. | | `504` | A machine was selected but Chrome wasn't reachable before the startup deadline (20 s, up to 3 min on some machines). | Once. | ## Guidance for agents and automations diff --git a/docs/start/for-agents.mdx b/docs/start/for-agents.mdx index 06d2bfb..8452eff 100644 --- a/docs/start/for-agents.mdx +++ b/docs/start/for-agents.mdx @@ -55,6 +55,7 @@ Integrate Driver (driver.dev), hosted real-Chrome browser infrastructure, into t hand them the live view. - Live view: https://viewer.driver.dev?ws= — print it as soon as the session exists so the user can watch; it works until the session ends. +- If the workspace owns dedicated IPs (GET /v1/network/ips), pass proxyUrl "dedicated://any" (or "dedicated://") instead of a country; the address decides country and timezone. - If a browser pool exists for the job (GET /v1/browser/pools, or the dashboard's Browser pools page), take a browser from it instead of creating one: POST /v1/browser/pools//acquire with {"waitMs": 10000} returns { leaseId, session }; release it in the finally with @@ -80,7 +81,7 @@ Base URL `https://api.driver.dev`. Every call carries `Authorization: Bearer $DR | Use | `chromium.connectOverCDP(cdpUrl)` | From Patchright. Reuse the first context and page. | | Stop | `DELETE /v1/browser/session?sessionId=` | Returns `{ success: true }`. | -Also useful: `GET /v1/browser/session?sessionId=` for status and bandwidth, `GET /v1/browser/sessions?status=active` to list, `PATCH /v1/browser/session?sessionId=` with `{ "note": "…" }` to label a run. Every create option is on [Create a session](/docs/sessions/create#options). With a [pool](/docs/sessions/pools), `POST /v1/browser/pools/{poolId}/acquire` replaces the create call and `POST /v1/browser/pools/{poolId}/release` the stop; pool ids go in the path. +Also useful: `GET /v1/browser/session?sessionId=` for status and bandwidth, `GET /v1/browser/sessions?status=active` to list, `PATCH /v1/browser/session?sessionId=` with `{ "note": "…" }` to label a run. Every create option is on [Create a session](/docs/sessions/create#options). With a [pool](/docs/sessions/pools), `POST /v1/browser/pools/{poolId}/acquire` replaces the create call and `POST /v1/browser/pools/{poolId}/release` the stop; pool ids go in the path. `GET /v1/network/ips` lists the workspace's [dedicated IPs](/docs/options/proxies#dedicated-ips); pass one as `proxyUrl: "dedicated://"` (or `"dedicated://any"`) and leave `country` out. ## Reference implementation