diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8125850d..9e9b6f07 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,10 +21,10 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/brand.dev-typescript' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Node - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: '22' @@ -43,10 +43,10 @@ jobs: contents: read id-token: write steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Node - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: '22' @@ -61,7 +61,7 @@ jobs: github.repository == 'stainless-sdks/brand.dev-typescript' && !startsWith(github.ref, 'refs/heads/stl/') id: github-oidc - uses: actions/github-script@v8 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: script: core.setOutput('github_token', await core.getIDToken()); @@ -91,10 +91,10 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/brand.dev-typescript' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Node - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: '22' diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml index 27223ca5..57aa7670 100644 --- a/.github/workflows/publish-npm.yml +++ b/.github/workflows/publish-npm.yml @@ -21,10 +21,10 @@ jobs: id-token: write steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Node - uses: actions/setup-node@v3 + uses: actions/setup-node@3235b876344d2a9aa001b8d1453c930bba69e610 # v3.9.1 with: node-version: '20' diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index 42ed9a00..039dbf90 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -12,9 +12,8 @@ jobs: if: github.repository == 'context-dot-dev/deprecated-brand-typescript-sdk' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Check release environment run: | bash ./bin/check-release-environment - diff --git a/.prettierignore b/.prettierignore index 7cc13dd1..36afd3b3 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,6 +2,7 @@ CHANGELOG.md /ecosystem-tests/*/** /node_modules /deno +/packages/mcp-server/manifest.json # don't format tsc output, will break source maps dist diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 8878352c..3a39fd8c 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.34.0" + ".": "0.35.0" } diff --git a/.stats.yml b/.stats.yml index bee881e8..5dbaf4e0 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 20 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-05a30711e18b0023520a660352d75595a050d1299bf0e3ee4a8cf55ded36aea2.yml -openapi_spec_hash: 8d0e1115a7d864f27c55cec3255d1e77 +configured_endpoints: 16 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-437213902ba1307c7c8733d00945a188a7aa6af9e5c1f7fa1a8ffcc0f692efc8.yml +openapi_spec_hash: 0141ce3e02c643164e5a09eb2f2e89b4 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/CHANGELOG.md b/CHANGELOG.md index 3503337b..c29ad82e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,75 @@ # Changelog +## 0.35.0 (2026-06-08) + +Full Changelog: [v0.34.0...v0.35.0](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/compare/v0.34.0...v0.35.0) + +### Features + +* **api:** api update ([ad88d7c](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/ad88d7c77769ee391afd53d7c19763d2af22f847)) +* **api:** api update ([fa6f180](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/fa6f1800704693777e7b29ccb33a6fdd3afac80c)) +* **api:** api update ([82c20a0](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/82c20a0e8dd954aba80d6122930211e3e71ee884)) +* **api:** api update ([2e168c3](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/2e168c3fe448d14473ca3fc977f3559f8ddbfe1f)) +* **api:** api update ([b3bf84f](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/b3bf84fcb2fc398edf72f1ff5f3649eab37212ee)) +* **api:** api update ([59b5f95](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/59b5f95b896846a31c9c7852337f125ae65439e3)) +* **api:** api update ([1b700bf](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/1b700bfe47a384d90c945b5d6e02509d665dd0fb)) +* **api:** api update ([43869b8](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/43869b83724b01e5e28a1b23f179633f3760f74b)) +* **api:** api update ([3a511e1](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/3a511e140051649ac3ce35051d91e2adb4dd2765)) +* **api:** api update ([808759c](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/808759cee7444f52175b1d2a2558468454a348c6)) +* **api:** api update ([de2b2b7](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/de2b2b76d8d33d290b120327994d047156d77094)) +* **api:** api update ([13ea0d1](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/13ea0d141a1d859ec241e5b79b852d46bd633fd9)) +* **api:** api update ([9bfd247](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/9bfd24706e0432eed34a10457507498685be4160)) +* **api:** api update ([0a31d83](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/0a31d83edb235c2b212c6d4ca1491791f5f3bd4f)) +* **api:** api update ([54a32bf](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/54a32bf4e75bf1794b277eb4ce167004ad70d026)) +* **api:** api update ([a83efa1](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/a83efa13afc3f4f6b564dcba1eebf34e1308d388)) +* **api:** api update ([0617a88](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/0617a88b23443ea0bc13498ef589ee400ef432a1)) +* **api:** api update ([65b0a20](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/65b0a202fddb6cc91d6af6c3baa02ef48fd8085b)) +* **api:** api update ([3f941ed](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/3f941edf853250925cf0b5d314affb2dad20ea8f)) +* **api:** api update ([96f634a](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/96f634afd283dfdb24186d77fd89e6d7db7654e1)) +* **api:** api update ([f774c88](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/f774c883e3a05276669b94690517703447ddff9a)) +* **api:** api update ([634a8ee](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/634a8eeec0cc6cf55fa66468c4e12edadfc8c222)) +* **api:** api update ([7fae62a](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/7fae62af58b8ebc012fd23e39d09ab939b1bd309)) +* **api:** api update ([85a4fad](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/85a4faddcc76130685e7d683999dc2712ce388a9)) +* **api:** api update ([d702889](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/d7028891158c4821acd11093fc69172b9f591db3)) +* **api:** api update ([5819866](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/5819866293d5d87b22457a7d0f2412a726788e97)) +* **api:** api update ([4b26b87](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/4b26b8765fbe347c55159e6a3b98cd26c1f2a478)) +* **api:** api update ([eddea3f](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/eddea3f1a05a07b311c01ba7d63c5ea8006a6007)) +* **api:** api update ([d3ddae8](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/d3ddae876866735ed1d74b606b13e3904aaaea73)) +* **api:** api update ([fbf3885](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/fbf38850134f41f8c5c3ffb7688654ca4c01b72e)) +* **api:** api update ([9c5127f](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/9c5127f56d1a7cecd870ed28f39299ad07435f84)) +* **api:** api update ([2951a65](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/2951a65b2f64fff438a445501b8faa095023d48b)) +* **api:** api update ([5a8b6f2](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/5a8b6f230ddf475be3a1b9d2c2e05a930a1724b8)) +* support setting headers via env ([bdd9f46](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/bdd9f4689b6a90bff41264325dbead0397f92aed)) + + +### Bug Fixes + +* **mcp:** use `pure-lockfile` when building mcp server ([4e7a040](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/4e7a0408c41c57ac35ee99e820d2caf0d6c1b0ea)) +* **typescript:** upgrade tsc-multi so that it works with Node 26 ([e85a50a](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/e85a50ac5ef776a655b7cd7a1375a8ce634d1e4b)) + + +### Chores + +* avoid formatting file that gets changed during releases ([d9e9e3e](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/d9e9e3eb316825a053b4443977927b5589f47008)) +* **format:** run eslint and prettier separately ([cf9b19f](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/cf9b19f49aa1c18f6054e89f6fe0005db34b1e93)) +* **internal:** codegen related update ([1b4fdd6](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/1b4fdd639c77e493c053113dafff49b70dfec8ca)) +* **internal:** codegen related update ([54a4dc6](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/54a4dc6a834ee5801f0bcc326a0446598121d844)) +* **internal:** fix MCP server import ordering ([18990e2](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/18990e28444884c123f8cff50c7763ff5f3562cf)) +* **internal:** more robust bootstrap script ([6b1838d](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/6b1838d94014d4ff073c38392820cfb868d2ee28)) +* **internal:** show error causes in MCP servers when running in local mode ([bdc3563](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/bdc35636b0866d6daecd3be434a46989776fe53f)) +* **internal:** update docs ordering ([3084086](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/3084086cb5f62d71f1be21a5d4ef07aca2fc7523)) +* **mcp-server:** increase local docs search result count from 5 to 10 ([39503b6](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/39503b60104352275076fefa2b821ee1a7f9a2d5)) +* redact api-key headers in debug logs ([2ab340e](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/2ab340e31211d77b67bd67dd1caffa1af2aa9be5)) +* restructure docs search code ([31bd771](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/31bd7717da192b95f66d9f6c2bf0639ed2857055)) +* **tests:** remove redundant File import ([16509b7](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/16509b726541f4de5aa89d5ef40647abd84c9dde)) + + +### Documentation + +* clarify forwards compat behavior ([4d3fdb6](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/4d3fdb6229a1508075ce688f4e918e72608be993)) +* update logging docs ([a4e5d6c](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/a4e5d6c3560d675b3a1417ca2c62a236d89079f8)) +* update with proxy auth info ([c282252](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/commit/c282252b5e88076344bf878241233118211770fa)) + ## 0.34.0 (2026-04-03) Full Changelog: [v0.33.0...v0.34.0](https://github.com/context-dot-dev/deprecated-brand-typescript-sdk/compare/v0.33.0...v0.34.0) diff --git a/api.md b/api.md index 1bdf5b06..e38e7019 100644 --- a/api.md +++ b/api.md @@ -6,7 +6,6 @@ Types: - BrandAIProductResponse - BrandAIProductsResponse - BrandAIQueryResponse -- BrandFontsResponse - BrandIdentifyFromTransactionResponse - BrandPrefetchResponse - BrandPrefetchByEmailResponse @@ -14,10 +13,7 @@ Types: - BrandRetrieveByIsinResponse - BrandRetrieveByNameResponse - BrandRetrieveByTickerResponse -- BrandRetrieveNaicsResponse - BrandRetrieveSimplifiedResponse -- BrandScreenshotResponse -- BrandStyleguideResponse - BrandWebScrapeHTMLResponse - BrandWebScrapeImagesResponse - BrandWebScrapeMdResponse @@ -29,7 +25,6 @@ Methods: - client.brand.aiProduct({ ...params }) -> BrandAIProductResponse - client.brand.aiProducts({ ...params }) -> BrandAIProductsResponse - client.brand.aiQuery({ ...params }) -> BrandAIQueryResponse -- client.brand.fonts({ ...params }) -> BrandFontsResponse - client.brand.identifyFromTransaction({ ...params }) -> BrandIdentifyFromTransactionResponse - client.brand.prefetch({ ...params }) -> BrandPrefetchResponse - client.brand.prefetchByEmail({ ...params }) -> BrandPrefetchByEmailResponse @@ -37,10 +32,7 @@ Methods: - client.brand.retrieveByIsin({ ...params }) -> BrandRetrieveByIsinResponse - client.brand.retrieveByName({ ...params }) -> BrandRetrieveByNameResponse - client.brand.retrieveByTicker({ ...params }) -> BrandRetrieveByTickerResponse -- client.brand.retrieveNaics({ ...params }) -> BrandRetrieveNaicsResponse - client.brand.retrieveSimplified({ ...params }) -> BrandRetrieveSimplifiedResponse -- client.brand.screenshot({ ...params }) -> BrandScreenshotResponse -- client.brand.styleguide({ ...params }) -> BrandStyleguideResponse - client.brand.webScrapeHTML({ ...params }) -> BrandWebScrapeHTMLResponse - client.brand.webScrapeImages({ ...params }) -> BrandWebScrapeImagesResponse - client.brand.webScrapeMd({ ...params }) -> BrandWebScrapeMdResponse diff --git a/eslint.config.mjs b/eslint.config.mjs index 41a735ec..e643b3d3 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,7 +1,6 @@ // @ts-check import tseslint from 'typescript-eslint'; import unusedImports from 'eslint-plugin-unused-imports'; -import prettier from 'eslint-plugin-prettier'; export default tseslint.config( { @@ -14,11 +13,9 @@ export default tseslint.config( plugins: { '@typescript-eslint': tseslint.plugin, 'unused-imports': unusedImports, - prettier, }, rules: { 'no-unused-vars': 'off', - 'prettier/prettier': 'error', 'unused-imports/no-unused-imports': 'error', 'no-restricted-imports': [ 'error', diff --git a/package.json b/package.json index 245c867b..fccf8c8a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "brand.dev", - "version": "0.34.0", + "version": "0.35.0", "description": "The official TypeScript library for the Brand Dev API", "author": "Brand Dev ", "types": "dist/index.d.ts", @@ -36,7 +36,6 @@ "@typescript-eslint/eslint-plugin": "8.31.1", "@typescript-eslint/parser": "8.31.1", "eslint": "^9.39.1", - "eslint-plugin-prettier": "^5.4.1", "eslint-plugin-unused-imports": "^4.1.4", "iconv-lite": "^0.6.3", "jest": "^29.4.0", @@ -44,7 +43,7 @@ "publint": "^0.2.12", "ts-jest": "^29.1.0", "ts-node": "^10.5.0", - "tsc-multi": "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.9/tsc-multi.tgz", + "tsc-multi": "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.11/tsc-multi.tgz", "tsconfig-paths": "^4.0.0", "tslib": "^2.8.1", "typescript": "5.8.3", diff --git a/packages/mcp-server/manifest.json b/packages/mcp-server/manifest.json index 3449da79..c01d1b23 100644 --- a/packages/mcp-server/manifest.json +++ b/packages/mcp-server/manifest.json @@ -1,7 +1,7 @@ { "dxt_version": "0.2", "name": "brand.dev-mcp", - "version": "0.34.0", + "version": "0.35.0", "description": "The official MCP Server for the Brand Dev API", "author": { "name": "Brand Dev", diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json index d8cec012..d596e9f8 100644 --- a/packages/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -1,6 +1,6 @@ { "name": "brand.dev-mcp", - "version": "0.34.0", + "version": "0.35.0", "description": "The official MCP Server for the Brand Dev API", "author": "Brand Dev ", "types": "dist/index.d.ts", @@ -74,7 +74,7 @@ "ts-jest": "^29.1.0", "ts-morph": "^19.0.0", "ts-node": "^10.5.0", - "tsc-multi": "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.9/tsc-multi.tgz", + "tsc-multi": "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.11/tsc-multi.tgz", "tsconfig-paths": "^4.0.0" }, "imports": { diff --git a/packages/mcp-server/src/code-tool-worker.ts b/packages/mcp-server/src/code-tool-worker.ts index b95cc961..d55e8eb3 100644 --- a/packages/mcp-server/src/code-tool-worker.ts +++ b/packages/mcp-server/src/code-tool-worker.ts @@ -111,7 +111,6 @@ const fuse = new Fuse( 'client.brand.aiProduct', 'client.brand.aiProducts', 'client.brand.aiQuery', - 'client.brand.fonts', 'client.brand.identifyFromTransaction', 'client.brand.prefetch', 'client.brand.prefetchByEmail', @@ -120,10 +119,7 @@ const fuse = new Fuse( 'client.brand.retrieveByIsin', 'client.brand.retrieveByName', 'client.brand.retrieveByTicker', - 'client.brand.retrieveNaics', 'client.brand.retrieveSimplified', - 'client.brand.screenshot', - 'client.brand.styleguide', 'client.brand.webScrapeHTML', 'client.brand.webScrapeImages', 'client.brand.webScrapeMd', @@ -204,7 +200,8 @@ function makeSdkProxy(obj: T, { path, isBelievedBad = false }: function parseError(code: string, error: unknown): string | undefined { if (!(error instanceof Error)) return; - const message = error.name ? `${error.name}: ${error.message}` : error.message; + const cause = error.cause instanceof Error ? `: ${error.cause.message}` : ''; + const message = error.name ? `${error.name}: ${error.message}${cause}` : `${error.message}${cause}`; try { // Deno uses V8; the first ":LINE:COLUMN" is the top of stack. const lineNumber = error.stack?.match(/:([0-9]+):[0-9]+/)?.[1]; diff --git a/packages/mcp-server/src/docs-search-tool.ts b/packages/mcp-server/src/docs-search-tool.ts index 38784c0f..316a9816 100644 --- a/packages/mcp-server/src/docs-search-tool.ts +++ b/packages/mcp-server/src/docs-search-tool.ts @@ -63,7 +63,7 @@ async function searchLocal(args: Record): Promise { query, language, detail, - maxResults: 5, + maxResults: 10, }).results; } diff --git a/packages/mcp-server/src/instructions.ts b/packages/mcp-server/src/instructions.ts index d580392b..02e18eb3 100644 --- a/packages/mcp-server/src/instructions.ts +++ b/packages/mcp-server/src/instructions.ts @@ -1,8 +1,8 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. import fs from 'fs/promises'; -import { readEnv } from './util'; import { getLogger } from './logger'; +import { readEnv } from './util'; const INSTRUCTIONS_CACHE_TTL_MS = 15 * 60 * 1000; // 15 minutes diff --git a/packages/mcp-server/src/local-docs-search.ts b/packages/mcp-server/src/local-docs-search.ts index b7981c45..25e94eda 100644 --- a/packages/mcp-server/src/local-docs-search.ts +++ b/packages/mcp-server/src/local-docs-search.ts @@ -58,35 +58,41 @@ const EMBEDDED_METHODS: MethodEntry[] = [ description: 'Retrieve logos, backdrops, colors, industry, description, and more from any domain', stainlessPath: '(resource) brand > (method) retrieve', qualified: 'client.brand.retrieve', - params: ['domain: string;', 'force_language?: string;', 'maxSpeed?: boolean;', 'timeoutMS?: number;'], + params: [ + 'domain: string;', + 'force_language?: string;', + 'maxAgeMs?: number;', + 'maxSpeed?: boolean;', + 'timeoutMS?: number;', + ], response: - "{ brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: object[]; resolution?: object; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: object[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: object[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: object; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }; code?: number; status?: string; }", + "{ brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: object[]; resolution?: object; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: object[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: object[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: object; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; primary_language?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }; code?: number; status?: string; }", markdown: - "## retrieve\n\n`client.brand.retrieve(domain: string, force_language?: string, maxSpeed?: boolean, timeoutMS?: number): { brand?: object; code?: number; status?: string; }`\n\n**get** `/brand/retrieve`\n\nRetrieve logos, backdrops, colors, industry, description, and more from any domain\n\n### Parameters\n\n- `domain: string`\n Domain name to retrieve brand data for (e.g., 'example.com', 'google.com'). Cannot be used with name or ticker parameters.\n\n- `force_language?: string`\n Optional parameter to force the language of the retrieved brand data. Works with all three lookup methods.\n\n- `maxSpeed?: boolean`\n Optional parameter to optimize the API call for maximum speed. When set to true, the API will skip time-consuming operations for faster response at the cost of less comprehensive data. Works with all three lookup methods.\n\n- `timeoutMS?: number`\n Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).\n\n### Returns\n\n- `{ brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: object[]; resolution?: object; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: object[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: object[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: object; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }; code?: number; status?: string; }`\n\n - `brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: { hex?: string; name?: string; }[]; resolution?: { aspect_ratio?: number; height?: number; width?: number; }; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: { industry: string; subindustry: string; }[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: { hex?: string; name?: string; }[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: { aspect_ratio?: number; height?: number; width?: number; }; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }`\n - `code?: number`\n - `status?: string`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst brand = await client.brand.retrieve({ domain: 'domain' });\n\nconsole.log(brand);\n```", + "## retrieve\n\n`client.brand.retrieve(domain: string, force_language?: string, maxAgeMs?: number, maxSpeed?: boolean, timeoutMS?: number): { brand?: object; code?: number; status?: string; }`\n\n**get** `/brand/retrieve`\n\nRetrieve logos, backdrops, colors, industry, description, and more from any domain\n\n### Parameters\n\n- `domain: string`\n Domain name to retrieve brand data for (e.g., 'example.com', 'google.com'). Cannot be used with name or ticker parameters.\n\n- `force_language?: string`\n Optional parameter to force the language of the retrieved brand data.\n\n- `maxAgeMs?: number`\n Maximum age in milliseconds for cached brand data before the API performs a hard refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 year.\n\n- `maxSpeed?: boolean`\n Optional parameter to optimize the API call for maximum speed. When set to true, the API will skip time-consuming operations for faster response at the cost of less comprehensive data. Works with all three lookup methods.\n\n- `timeoutMS?: number`\n Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).\n\n### Returns\n\n- `{ brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: object[]; resolution?: object; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: object[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: object[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: object; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; primary_language?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }; code?: number; status?: string; }`\n\n - `brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: { hex?: string; name?: string; }[]; resolution?: { aspect_ratio?: number; height?: number; width?: number; }; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: { industry: string; subindustry: string; }[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: { hex?: string; name?: string; }[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: { aspect_ratio?: number; height?: number; width?: number; }; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; primary_language?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }`\n - `code?: number`\n - `status?: string`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst brand = await client.brand.retrieve({ domain: 'domain' });\n\nconsole.log(brand);\n```", perLanguage: { - http: { - example: - 'curl https://api.brand.dev/v1/brand/retrieve \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY"', - }, - java: { - method: 'brand().retrieve', + typescript: { + method: 'client.brand.retrieve', example: - 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandRetrieveParams;\nimport com.branddev.api.models.brand.BrandRetrieveResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandRetrieveParams params = BrandRetrieveParams.builder()\n .domain("domain")\n .build();\n BrandRetrieveResponse brand = client.brand().retrieve(params);\n }\n}', + "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst brand = await client.brand.retrieve({ domain: 'domain' });\n\nconsole.log(brand.brand);", }, python: { method: 'brand.retrieve', example: 'import os\nfrom brand.dev import BrandDev\n\nclient = BrandDev(\n api_key=os.environ.get("BRAND_DEV_API_KEY"), # This is the default and can be omitted\n)\nbrand = client.brand.retrieve(\n domain="domain",\n)\nprint(brand.brand)', }, + java: { + method: 'brand().retrieve', + example: + 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandRetrieveParams;\nimport com.branddev.api.models.brand.BrandRetrieveResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandRetrieveParams params = BrandRetrieveParams.builder()\n .domain("domain")\n .build();\n BrandRetrieveResponse brand = client.brand().retrieve(params);\n }\n}', + }, ruby: { method: 'brand.retrieve', example: 'require "brand_dev"\n\nbrand_dev = BrandDev::Client.new(api_key: "My API Key")\n\nbrand = brand_dev.brand.retrieve(domain: "domain")\n\nputs(brand)', }, - typescript: { - method: 'client.brand.retrieve', + http: { example: - "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst brand = await client.brand.retrieve({ domain: 'domain' });\n\nconsole.log(brand.brand);", + 'curl https://api.brand.dev/v1/brand/retrieve \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY"', }, }, }, @@ -95,45 +101,45 @@ const EMBEDDED_METHODS: MethodEntry[] = [ endpoint: '/brand/retrieve-by-ticker', httpMethod: 'get', summary: 'Retrieve brand data by stock ticker', - description: - 'Retrieve brand information using a stock ticker symbol. This endpoint looks up the company associated with the ticker and returns its brand data.', + description: 'Retrieve brand information using a stock ticker symbol.', stainlessPath: '(resource) brand > (method) retrieve_by_ticker', qualified: 'client.brand.retrieveByTicker', params: [ 'ticker: string;', 'force_language?: string;', + 'maxAgeMs?: number;', 'maxSpeed?: boolean;', 'ticker_exchange?: string;', 'timeoutMS?: number;', ], response: - "{ brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: object[]; resolution?: object; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: object[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: object[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: object; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }; code?: number; status?: string; }", + "{ brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: object[]; resolution?: object; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: object[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: object[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: object; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; primary_language?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }; code?: number; status?: string; }", markdown: - "## retrieve_by_ticker\n\n`client.brand.retrieveByTicker(ticker: string, force_language?: string, maxSpeed?: boolean, ticker_exchange?: string, timeoutMS?: number): { brand?: object; code?: number; status?: string; }`\n\n**get** `/brand/retrieve-by-ticker`\n\nRetrieve brand information using a stock ticker symbol. This endpoint looks up the company associated with the ticker and returns its brand data.\n\n### Parameters\n\n- `ticker: string`\n Stock ticker symbol to retrieve brand data for (e.g., 'AAPL', 'GOOGL', 'BRK.A'). Must be 1-15 characters, letters/numbers/dots only.\n\n- `force_language?: string`\n Optional parameter to force the language of the retrieved brand data.\n\n- `maxSpeed?: boolean`\n Optional parameter to optimize the API call for maximum speed. When set to true, the API will skip time-consuming operations for faster response at the cost of less comprehensive data.\n\n- `ticker_exchange?: string`\n Optional stock exchange for the ticker. Defaults to NASDAQ if not specified.\n\n- `timeoutMS?: number`\n Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).\n\n### Returns\n\n- `{ brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: object[]; resolution?: object; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: object[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: object[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: object; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }; code?: number; status?: string; }`\n\n - `brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: { hex?: string; name?: string; }[]; resolution?: { aspect_ratio?: number; height?: number; width?: number; }; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: { industry: string; subindustry: string; }[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: { hex?: string; name?: string; }[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: { aspect_ratio?: number; height?: number; width?: number; }; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }`\n - `code?: number`\n - `status?: string`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.retrieveByTicker({ ticker: 'ticker' });\n\nconsole.log(response);\n```", + "## retrieve_by_ticker\n\n`client.brand.retrieveByTicker(ticker: string, force_language?: string, maxAgeMs?: number, maxSpeed?: boolean, ticker_exchange?: string, timeoutMS?: number): { brand?: object; code?: number; status?: string; }`\n\n**get** `/brand/retrieve-by-ticker`\n\nRetrieve brand information using a stock ticker symbol.\n\n### Parameters\n\n- `ticker: string`\n Stock ticker symbol to retrieve brand data for (e.g., 'AAPL', 'GOOGL', 'BRK.A'). Must be 1-15 characters, letters/numbers/dots only.\n\n- `force_language?: string`\n Optional parameter to force the language of the retrieved brand data.\n\n- `maxAgeMs?: number`\n Maximum age in milliseconds for cached brand data before the API performs a hard refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 year.\n\n- `maxSpeed?: boolean`\n Optional parameter to optimize the API call for maximum speed. When set to true, the API will skip time-consuming operations for faster response at the cost of less comprehensive data.\n\n- `ticker_exchange?: string`\n Optional stock exchange for the ticker. Defaults to NASDAQ if not specified.\n\n- `timeoutMS?: number`\n Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).\n\n### Returns\n\n- `{ brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: object[]; resolution?: object; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: object[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: object[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: object; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; primary_language?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }; code?: number; status?: string; }`\n\n - `brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: { hex?: string; name?: string; }[]; resolution?: { aspect_ratio?: number; height?: number; width?: number; }; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: { industry: string; subindustry: string; }[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: { hex?: string; name?: string; }[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: { aspect_ratio?: number; height?: number; width?: number; }; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; primary_language?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }`\n - `code?: number`\n - `status?: string`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.retrieveByTicker({ ticker: 'ticker' });\n\nconsole.log(response);\n```", perLanguage: { - http: { - example: - 'curl https://api.brand.dev/v1/brand/retrieve-by-ticker \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY"', - }, - java: { - method: 'brand().retrieveByTicker', + typescript: { + method: 'client.brand.retrieveByTicker', example: - 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandRetrieveByTickerParams;\nimport com.branddev.api.models.brand.BrandRetrieveByTickerResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandRetrieveByTickerParams params = BrandRetrieveByTickerParams.builder()\n .ticker("ticker")\n .build();\n BrandRetrieveByTickerResponse response = client.brand().retrieveByTicker(params);\n }\n}', + "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.retrieveByTicker({ ticker: 'ticker' });\n\nconsole.log(response.brand);", }, python: { method: 'brand.retrieve_by_ticker', example: 'import os\nfrom brand.dev import BrandDev\n\nclient = BrandDev(\n api_key=os.environ.get("BRAND_DEV_API_KEY"), # This is the default and can be omitted\n)\nresponse = client.brand.retrieve_by_ticker(\n ticker="ticker",\n)\nprint(response.brand)', }, + java: { + method: 'brand().retrieveByTicker', + example: + 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandRetrieveByTickerParams;\nimport com.branddev.api.models.brand.BrandRetrieveByTickerResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandRetrieveByTickerParams params = BrandRetrieveByTickerParams.builder()\n .ticker("ticker")\n .build();\n BrandRetrieveByTickerResponse response = client.brand().retrieveByTicker(params);\n }\n}', + }, ruby: { method: 'brand.retrieve_by_ticker', example: 'require "brand_dev"\n\nbrand_dev = BrandDev::Client.new(api_key: "My API Key")\n\nresponse = brand_dev.brand.retrieve_by_ticker(ticker: "ticker")\n\nputs(response)', }, - typescript: { - method: 'client.brand.retrieveByTicker', + http: { example: - "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.retrieveByTicker({ ticker: 'ticker' });\n\nconsole.log(response.brand);", + 'curl https://api.brand.dev/v1/brand/retrieve-by-ticker \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY"', }, }, }, @@ -143,38 +149,44 @@ const EMBEDDED_METHODS: MethodEntry[] = [ httpMethod: 'get', summary: 'Retrieve brand data by ISIN', description: - 'Retrieve brand information using an ISIN (International Securities Identification Number). This endpoint looks up the company associated with the ISIN and returns its brand data.', + 'Retrieve brand information using an ISIN (International Securities Identification Number). ', stainlessPath: '(resource) brand > (method) retrieve_by_isin', qualified: 'client.brand.retrieveByIsin', - params: ['isin: string;', 'force_language?: string;', 'maxSpeed?: boolean;', 'timeoutMS?: number;'], + params: [ + 'isin: string;', + 'force_language?: string;', + 'maxAgeMs?: number;', + 'maxSpeed?: boolean;', + 'timeoutMS?: number;', + ], response: - "{ brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: object[]; resolution?: object; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: object[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: object[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: object; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }; code?: number; status?: string; }", + "{ brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: object[]; resolution?: object; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: object[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: object[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: object; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; primary_language?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }; code?: number; status?: string; }", markdown: - "## retrieve_by_isin\n\n`client.brand.retrieveByIsin(isin: string, force_language?: string, maxSpeed?: boolean, timeoutMS?: number): { brand?: object; code?: number; status?: string; }`\n\n**get** `/brand/retrieve-by-isin`\n\nRetrieve brand information using an ISIN (International Securities Identification Number). This endpoint looks up the company associated with the ISIN and returns its brand data.\n\n### Parameters\n\n- `isin: string`\n ISIN (International Securities Identification Number) to retrieve brand data for (e.g., 'AU000000IMD5', 'US0378331005'). Must be exactly 12 characters: 2 letters followed by 9 alphanumeric characters and ending with a digit.\n\n- `force_language?: string`\n Optional parameter to force the language of the retrieved brand data.\n\n- `maxSpeed?: boolean`\n Optional parameter to optimize the API call for maximum speed. When set to true, the API will skip time-consuming operations for faster response at the cost of less comprehensive data.\n\n- `timeoutMS?: number`\n Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).\n\n### Returns\n\n- `{ brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: object[]; resolution?: object; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: object[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: object[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: object; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }; code?: number; status?: string; }`\n\n - `brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: { hex?: string; name?: string; }[]; resolution?: { aspect_ratio?: number; height?: number; width?: number; }; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: { industry: string; subindustry: string; }[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: { hex?: string; name?: string; }[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: { aspect_ratio?: number; height?: number; width?: number; }; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }`\n - `code?: number`\n - `status?: string`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.retrieveByIsin({ isin: 'SE60513A9993' });\n\nconsole.log(response);\n```", + "## retrieve_by_isin\n\n`client.brand.retrieveByIsin(isin: string, force_language?: string, maxAgeMs?: number, maxSpeed?: boolean, timeoutMS?: number): { brand?: object; code?: number; status?: string; }`\n\n**get** `/brand/retrieve-by-isin`\n\nRetrieve brand information using an ISIN (International Securities Identification Number). \n\n### Parameters\n\n- `isin: string`\n ISIN (International Securities Identification Number) to retrieve brand data for (e.g., 'AU000000IMD5', 'US0378331005'). Must be exactly 12 characters: 2 letters followed by 9 alphanumeric characters and ending with a digit.\n\n- `force_language?: string`\n Optional parameter to force the language of the retrieved brand data.\n\n- `maxAgeMs?: number`\n Maximum age in milliseconds for cached brand data before the API performs a hard refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 year.\n\n- `maxSpeed?: boolean`\n Optional parameter to optimize the API call for maximum speed. When set to true, the API will skip time-consuming operations for faster response at the cost of less comprehensive data.\n\n- `timeoutMS?: number`\n Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).\n\n### Returns\n\n- `{ brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: object[]; resolution?: object; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: object[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: object[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: object; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; primary_language?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }; code?: number; status?: string; }`\n\n - `brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: { hex?: string; name?: string; }[]; resolution?: { aspect_ratio?: number; height?: number; width?: number; }; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: { industry: string; subindustry: string; }[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: { hex?: string; name?: string; }[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: { aspect_ratio?: number; height?: number; width?: number; }; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; primary_language?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }`\n - `code?: number`\n - `status?: string`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.retrieveByIsin({ isin: 'SE60513A9993' });\n\nconsole.log(response);\n```", perLanguage: { - http: { - example: - 'curl https://api.brand.dev/v1/brand/retrieve-by-isin \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY"', - }, - java: { - method: 'brand().retrieveByIsin', + typescript: { + method: 'client.brand.retrieveByIsin', example: - 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandRetrieveByIsinParams;\nimport com.branddev.api.models.brand.BrandRetrieveByIsinResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandRetrieveByIsinParams params = BrandRetrieveByIsinParams.builder()\n .isin("SE60513A9993")\n .build();\n BrandRetrieveByIsinResponse response = client.brand().retrieveByIsin(params);\n }\n}', + "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.retrieveByIsin({ isin: 'SE60513A9993' });\n\nconsole.log(response.brand);", }, python: { method: 'brand.retrieve_by_isin', example: 'import os\nfrom brand.dev import BrandDev\n\nclient = BrandDev(\n api_key=os.environ.get("BRAND_DEV_API_KEY"), # This is the default and can be omitted\n)\nresponse = client.brand.retrieve_by_isin(\n isin="SE60513A9993",\n)\nprint(response.brand)', }, + java: { + method: 'brand().retrieveByIsin', + example: + 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandRetrieveByIsinParams;\nimport com.branddev.api.models.brand.BrandRetrieveByIsinResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandRetrieveByIsinParams params = BrandRetrieveByIsinParams.builder()\n .isin("SE60513A9993")\n .build();\n BrandRetrieveByIsinResponse response = client.brand().retrieveByIsin(params);\n }\n}', + }, ruby: { method: 'brand.retrieve_by_isin', example: 'require "brand_dev"\n\nbrand_dev = BrandDev::Client.new(api_key: "My API Key")\n\nresponse = brand_dev.brand.retrieve_by_isin(isin: "SE60513A9993")\n\nputs(response)', }, - typescript: { - method: 'client.brand.retrieveByIsin', + http: { example: - "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.retrieveByIsin({ isin: 'SE60513A9993' });\n\nconsole.log(response.brand);", + 'curl https://api.brand.dev/v1/brand/retrieve-by-isin \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY"', }, }, }, @@ -183,45 +195,45 @@ const EMBEDDED_METHODS: MethodEntry[] = [ endpoint: '/brand/retrieve-by-name', httpMethod: 'get', summary: 'Retrieve brand data by company name', - description: - 'Retrieve brand information using a company name. This endpoint searches for the company by name and returns its brand data.', + description: 'Retrieve brand information using a company name.', stainlessPath: '(resource) brand > (method) retrieve_by_name', qualified: 'client.brand.retrieveByName', params: [ 'name: string;', 'country_gl?: string;', 'force_language?: string;', + 'maxAgeMs?: number;', 'maxSpeed?: boolean;', 'timeoutMS?: number;', ], response: - "{ brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: object[]; resolution?: object; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: object[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: object[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: object; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }; code?: number; status?: string; }", + "{ brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: object[]; resolution?: object; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: object[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: object[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: object; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; primary_language?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }; code?: number; status?: string; }", markdown: - "## retrieve_by_name\n\n`client.brand.retrieveByName(name: string, country_gl?: string, force_language?: string, maxSpeed?: boolean, timeoutMS?: number): { brand?: object; code?: number; status?: string; }`\n\n**get** `/brand/retrieve-by-name`\n\nRetrieve brand information using a company name. This endpoint searches for the company by name and returns its brand data.\n\n### Parameters\n\n- `name: string`\n Company name to retrieve brand data for (e.g., 'Apple Inc', 'Microsoft Corporation'). Must be 3-30 characters.\n\n- `country_gl?: string`\n Optional country code (GL parameter) to specify the country. This affects the geographic location used for search queries.\n\n- `force_language?: string`\n Optional parameter to force the language of the retrieved brand data.\n\n- `maxSpeed?: boolean`\n Optional parameter to optimize the API call for maximum speed. When set to true, the API will skip time-consuming operations for faster response at the cost of less comprehensive data.\n\n- `timeoutMS?: number`\n Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).\n\n### Returns\n\n- `{ brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: object[]; resolution?: object; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: object[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: object[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: object; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }; code?: number; status?: string; }`\n\n - `brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: { hex?: string; name?: string; }[]; resolution?: { aspect_ratio?: number; height?: number; width?: number; }; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: { industry: string; subindustry: string; }[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: { hex?: string; name?: string; }[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: { aspect_ratio?: number; height?: number; width?: number; }; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }`\n - `code?: number`\n - `status?: string`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.retrieveByName({ name: 'xxx' });\n\nconsole.log(response);\n```", + "## retrieve_by_name\n\n`client.brand.retrieveByName(name: string, country_gl?: string, force_language?: string, maxAgeMs?: number, maxSpeed?: boolean, timeoutMS?: number): { brand?: object; code?: number; status?: string; }`\n\n**get** `/brand/retrieve-by-name`\n\nRetrieve brand information using a company name.\n\n### Parameters\n\n- `name: string`\n Company name to retrieve brand data for (e.g., 'Apple Inc', 'Microsoft Corporation'). Must be 3-30 characters.\n\n- `country_gl?: string`\n Optional country code hint (GL parameter) to specify the country for the company name.\n\n- `force_language?: string`\n Optional parameter to force the language of the retrieved brand data.\n\n- `maxAgeMs?: number`\n Maximum age in milliseconds for cached brand data before the API performs a hard refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 year.\n\n- `maxSpeed?: boolean`\n Optional parameter to optimize the API call for maximum speed. When set to true, the API will skip time-consuming operations for faster response at the cost of less comprehensive data.\n\n- `timeoutMS?: number`\n Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).\n\n### Returns\n\n- `{ brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: object[]; resolution?: object; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: object[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: object[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: object; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; primary_language?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }; code?: number; status?: string; }`\n\n - `brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: { hex?: string; name?: string; }[]; resolution?: { aspect_ratio?: number; height?: number; width?: number; }; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: { industry: string; subindustry: string; }[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: { hex?: string; name?: string; }[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: { aspect_ratio?: number; height?: number; width?: number; }; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; primary_language?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }`\n - `code?: number`\n - `status?: string`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.retrieveByName({ name: 'xxx' });\n\nconsole.log(response);\n```", perLanguage: { - http: { - example: - 'curl https://api.brand.dev/v1/brand/retrieve-by-name \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY"', - }, - java: { - method: 'brand().retrieveByName', + typescript: { + method: 'client.brand.retrieveByName', example: - 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandRetrieveByNameParams;\nimport com.branddev.api.models.brand.BrandRetrieveByNameResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandRetrieveByNameParams params = BrandRetrieveByNameParams.builder()\n .name("xxx")\n .build();\n BrandRetrieveByNameResponse response = client.brand().retrieveByName(params);\n }\n}', + "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.retrieveByName({ name: 'xxx' });\n\nconsole.log(response.brand);", }, python: { method: 'brand.retrieve_by_name', example: 'import os\nfrom brand.dev import BrandDev\n\nclient = BrandDev(\n api_key=os.environ.get("BRAND_DEV_API_KEY"), # This is the default and can be omitted\n)\nresponse = client.brand.retrieve_by_name(\n name="xxx",\n)\nprint(response.brand)', }, + java: { + method: 'brand().retrieveByName', + example: + 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandRetrieveByNameParams;\nimport com.branddev.api.models.brand.BrandRetrieveByNameResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandRetrieveByNameParams params = BrandRetrieveByNameParams.builder()\n .name("xxx")\n .build();\n BrandRetrieveByNameResponse response = client.brand().retrieveByName(params);\n }\n}', + }, ruby: { method: 'brand.retrieve_by_name', example: 'require "brand_dev"\n\nbrand_dev = BrandDev::Client.new(api_key: "My API Key")\n\nresponse = brand_dev.brand.retrieve_by_name(name: "xxx")\n\nputs(response)', }, - typescript: { - method: 'client.brand.retrieveByName', + http: { example: - "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.retrieveByName({ name: 'xxx' });\n\nconsole.log(response.brand);", + 'curl https://api.brand.dev/v1/brand/retrieve-by-name \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY"', }, }, }, @@ -231,38 +243,44 @@ const EMBEDDED_METHODS: MethodEntry[] = [ httpMethod: 'get', summary: 'Retrieve brand data by email address', description: - 'Retrieve brand information using an email address while detecting disposable and free email addresses. This endpoint extracts the domain from the email address and returns brand data for that domain. Disposable and free email addresses (like gmail.com, yahoo.com) will throw a 422 error.', + 'Retrieve brand information using an email address while detecting disposable and free email addresses. Disposable and free email addresses (like gmail.com, yahoo.com) will throw a 422 error.', stainlessPath: '(resource) brand > (method) retrieve_by_email', qualified: 'client.brand.retrieveByEmail', - params: ['email: string;', 'force_language?: string;', 'maxSpeed?: boolean;', 'timeoutMS?: number;'], + params: [ + 'email: string;', + 'force_language?: string;', + 'maxAgeMs?: number;', + 'maxSpeed?: boolean;', + 'timeoutMS?: number;', + ], response: - "{ brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: object[]; resolution?: object; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: object[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: object[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: object; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }; code?: number; status?: string; }", + "{ brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: object[]; resolution?: object; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: object[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: object[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: object; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; primary_language?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }; code?: number; status?: string; }", markdown: - "## retrieve_by_email\n\n`client.brand.retrieveByEmail(email: string, force_language?: string, maxSpeed?: boolean, timeoutMS?: number): { brand?: object; code?: number; status?: string; }`\n\n**get** `/brand/retrieve-by-email`\n\nRetrieve brand information using an email address while detecting disposable and free email addresses. This endpoint extracts the domain from the email address and returns brand data for that domain. Disposable and free email addresses (like gmail.com, yahoo.com) will throw a 422 error.\n\n### Parameters\n\n- `email: string`\n Email address to retrieve brand data for (e.g., 'contact@example.com'). The domain will be extracted from the email. Free email providers (gmail.com, yahoo.com, etc.) and disposable email addresses are not allowed.\n\n- `force_language?: string`\n Optional parameter to force the language of the retrieved brand data.\n\n- `maxSpeed?: boolean`\n Optional parameter to optimize the API call for maximum speed. When set to true, the API will skip time-consuming operations for faster response at the cost of less comprehensive data.\n\n- `timeoutMS?: number`\n Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).\n\n### Returns\n\n- `{ brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: object[]; resolution?: object; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: object[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: object[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: object; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }; code?: number; status?: string; }`\n\n - `brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: { hex?: string; name?: string; }[]; resolution?: { aspect_ratio?: number; height?: number; width?: number; }; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: { industry: string; subindustry: string; }[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: { hex?: string; name?: string; }[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: { aspect_ratio?: number; height?: number; width?: number; }; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }`\n - `code?: number`\n - `status?: string`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.retrieveByEmail({ email: 'dev@stainless.com' });\n\nconsole.log(response);\n```", + "## retrieve_by_email\n\n`client.brand.retrieveByEmail(email: string, force_language?: string, maxAgeMs?: number, maxSpeed?: boolean, timeoutMS?: number): { brand?: object; code?: number; status?: string; }`\n\n**get** `/brand/retrieve-by-email`\n\nRetrieve brand information using an email address while detecting disposable and free email addresses. Disposable and free email addresses (like gmail.com, yahoo.com) will throw a 422 error.\n\n### Parameters\n\n- `email: string`\n Email address to retrieve brand data for (e.g., 'contact@example.com'). The domain will be extracted from the email. Free email providers (gmail.com, yahoo.com, etc.) and disposable email addresses are not allowed.\n\n- `force_language?: string`\n Optional parameter to force the language of the retrieved brand data.\n\n- `maxAgeMs?: number`\n Maximum age in milliseconds for cached brand data before the API performs a hard refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 year.\n\n- `maxSpeed?: boolean`\n Optional parameter to optimize the API call for maximum speed. When set to true, the API will skip time-consuming operations for faster response at the cost of less comprehensive data.\n\n- `timeoutMS?: number`\n Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).\n\n### Returns\n\n- `{ brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: object[]; resolution?: object; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: object[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: object[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: object; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; primary_language?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }; code?: number; status?: string; }`\n\n - `brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: { hex?: string; name?: string; }[]; resolution?: { aspect_ratio?: number; height?: number; width?: number; }; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: { industry: string; subindustry: string; }[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: { hex?: string; name?: string; }[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: { aspect_ratio?: number; height?: number; width?: number; }; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; primary_language?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }`\n - `code?: number`\n - `status?: string`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.retrieveByEmail({ email: 'dev@stainless.com' });\n\nconsole.log(response);\n```", perLanguage: { - http: { - example: - 'curl https://api.brand.dev/v1/brand/retrieve-by-email \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY"', - }, - java: { - method: 'brand().retrieveByEmail', + typescript: { + method: 'client.brand.retrieveByEmail', example: - 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandRetrieveByEmailParams;\nimport com.branddev.api.models.brand.BrandRetrieveByEmailResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandRetrieveByEmailParams params = BrandRetrieveByEmailParams.builder()\n .email("dev@stainless.com")\n .build();\n BrandRetrieveByEmailResponse response = client.brand().retrieveByEmail(params);\n }\n}', + "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.retrieveByEmail({ email: 'dev@stainless.com' });\n\nconsole.log(response.brand);", }, python: { method: 'brand.retrieve_by_email', example: 'import os\nfrom brand.dev import BrandDev\n\nclient = BrandDev(\n api_key=os.environ.get("BRAND_DEV_API_KEY"), # This is the default and can be omitted\n)\nresponse = client.brand.retrieve_by_email(\n email="dev@stainless.com",\n)\nprint(response.brand)', }, + java: { + method: 'brand().retrieveByEmail', + example: + 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandRetrieveByEmailParams;\nimport com.branddev.api.models.brand.BrandRetrieveByEmailResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandRetrieveByEmailParams params = BrandRetrieveByEmailParams.builder()\n .email("dev@stainless.com")\n .build();\n BrandRetrieveByEmailResponse response = client.brand().retrieveByEmail(params);\n }\n}', + }, ruby: { method: 'brand.retrieve_by_email', example: 'require "brand_dev"\n\nbrand_dev = BrandDev::Client.new(api_key: "My API Key")\n\nresponse = brand_dev.brand.retrieve_by_email(email: "dev@stainless.com")\n\nputs(response)', }, - typescript: { - method: 'client.brand.retrieveByEmail', + http: { example: - "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.retrieveByEmail({ email: 'dev@stainless.com' });\n\nconsole.log(response.brand);", + 'curl https://api.brand.dev/v1/brand/retrieve-by-email \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY"', }, }, }, @@ -287,73 +305,33 @@ const EMBEDDED_METHODS: MethodEntry[] = [ 'timeoutMS?: number;', ], response: - "{ brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: object[]; resolution?: object; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: object[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: object[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: object; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }; code?: number; status?: string; }", + "{ brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: object[]; resolution?: object; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: object[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: object[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: object; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; primary_language?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }; code?: number; status?: string; }", markdown: - "## identify_from_transaction\n\n`client.brand.identifyFromTransaction(transaction_info: string, city?: string, country_gl?: string, force_language?: string, high_confidence_only?: boolean, maxSpeed?: boolean, mcc?: string, phone?: number, timeoutMS?: number): { brand?: object; code?: number; status?: string; }`\n\n**get** `/brand/transaction_identifier`\n\nEndpoint specially designed for platforms that want to identify transaction data by the transaction title.\n\n### Parameters\n\n- `transaction_info: string`\n Transaction information to identify the brand\n\n- `city?: string`\n Optional city name to prioritize when searching for the brand.\n\n- `country_gl?: string`\n Optional country code (GL parameter) to specify the country. This affects the geographic location used for search queries.\n\n- `force_language?: string`\n Optional parameter to force the language of the retrieved brand data.\n\n- `high_confidence_only?: boolean`\n When set to true, the API will perform an additional verification steps to ensure the identified brand matches the transaction with high confidence. Defaults to false.\n\n- `maxSpeed?: boolean`\n Optional parameter to optimize the API call for maximum speed. When set to true, the API will skip time-consuming operations for faster response at the cost of less comprehensive data.\n\n- `mcc?: string`\n Optional Merchant Category Code (MCC) to help identify the business category/industry. \n\n- `phone?: number`\n Optional phone number from the transaction to help verify brand match.\n\n- `timeoutMS?: number`\n Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).\n\n### Returns\n\n- `{ brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: object[]; resolution?: object; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: object[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: object[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: object; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }; code?: number; status?: string; }`\n\n - `brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: { hex?: string; name?: string; }[]; resolution?: { aspect_ratio?: number; height?: number; width?: number; }; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: { industry: string; subindustry: string; }[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: { hex?: string; name?: string; }[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: { aspect_ratio?: number; height?: number; width?: number; }; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }`\n - `code?: number`\n - `status?: string`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.identifyFromTransaction({ transaction_info: 'transaction_info' });\n\nconsole.log(response);\n```", + "## identify_from_transaction\n\n`client.brand.identifyFromTransaction(transaction_info: string, city?: string, country_gl?: string, force_language?: string, high_confidence_only?: boolean, maxSpeed?: boolean, mcc?: string, phone?: number, timeoutMS?: number): { brand?: object; code?: number; status?: string; }`\n\n**get** `/brand/transaction_identifier`\n\nEndpoint specially designed for platforms that want to identify transaction data by the transaction title.\n\n### Parameters\n\n- `transaction_info: string`\n Transaction information to identify the brand\n\n- `city?: string`\n Optional city name to prioritize when searching for the brand.\n\n- `country_gl?: string`\n Optional country code (GL parameter) to specify the country. This affects the geographic location used for search queries.\n\n- `force_language?: string`\n Optional parameter to force the language of the retrieved brand data.\n\n- `high_confidence_only?: boolean`\n When set to true, the API will perform an additional verification steps to ensure the identified brand matches the transaction with high confidence.\n\n- `maxSpeed?: boolean`\n Optional parameter to optimize the API call for maximum speed. When set to true, the API will skip time-consuming operations for faster response at the cost of less comprehensive data.\n\n- `mcc?: string`\n Optional Merchant Category Code (MCC) to help identify the business category/industry. \n\n- `phone?: number`\n Optional phone number from the transaction to help verify brand match.\n\n- `timeoutMS?: number`\n Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).\n\n### Returns\n\n- `{ brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: object[]; resolution?: object; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: object[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: object[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: object; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; primary_language?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }; code?: number; status?: string; }`\n\n - `brand?: { address?: { city?: string; country?: string; country_code?: string; postal_code?: string; state_code?: string; state_province?: string; street?: string; }; backdrops?: { colors?: { hex?: string; name?: string; }[]; resolution?: { aspect_ratio?: number; height?: number; width?: number; }; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; description?: string; domain?: string; email?: string; industries?: { eic?: { industry: string; subindustry: string; }[]; }; is_nsfw?: boolean; links?: { blog?: string; careers?: string; contact?: string; pricing?: string; privacy?: string; terms?: string; }; logos?: { colors?: { hex?: string; name?: string; }[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: { aspect_ratio?: number; height?: number; width?: number; }; type?: 'icon' | 'logo'; url?: string; }[]; phone?: string; primary_language?: string; slogan?: string; socials?: { type?: string; url?: string; }[]; stock?: { exchange?: string; ticker?: string; }; title?: string; }`\n - `code?: number`\n - `status?: string`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.identifyFromTransaction({ transaction_info: 'transaction_info' });\n\nconsole.log(response);\n```", perLanguage: { - http: { - example: - 'curl https://api.brand.dev/v1/brand/transaction_identifier \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY"', - }, - java: { - method: 'brand().identifyFromTransaction', - example: - 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandIdentifyFromTransactionParams;\nimport com.branddev.api.models.brand.BrandIdentifyFromTransactionResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandIdentifyFromTransactionParams params = BrandIdentifyFromTransactionParams.builder()\n .transactionInfo("transaction_info")\n .build();\n BrandIdentifyFromTransactionResponse response = client.brand().identifyFromTransaction(params);\n }\n}', - }, - python: { - method: 'brand.identify_from_transaction', - example: - 'import os\nfrom brand.dev import BrandDev\n\nclient = BrandDev(\n api_key=os.environ.get("BRAND_DEV_API_KEY"), # This is the default and can be omitted\n)\nresponse = client.brand.identify_from_transaction(\n transaction_info="transaction_info",\n)\nprint(response.brand)', - }, - ruby: { - method: 'brand.identify_from_transaction', - example: - 'require "brand_dev"\n\nbrand_dev = BrandDev::Client.new(api_key: "My API Key")\n\nresponse = brand_dev.brand.identify_from_transaction(transaction_info: "transaction_info")\n\nputs(response)', - }, typescript: { method: 'client.brand.identifyFromTransaction', example: "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.identifyFromTransaction({\n transaction_info: 'transaction_info',\n});\n\nconsole.log(response.brand);", }, - }, - }, - { - name: 'retrieve_naics', - endpoint: '/brand/naics', - httpMethod: 'get', - summary: 'Retrieve NAICS code for any brand', - description: 'Endpoint to classify any brand into a 2022 NAICS code.', - stainlessPath: '(resource) brand > (method) retrieve_naics', - qualified: 'client.brand.retrieveNaics', - params: ['input: string;', 'maxResults?: number;', 'minResults?: number;', 'timeoutMS?: number;'], - response: - "{ codes?: { code: string; confidence: 'high' | 'medium' | 'low'; name: string; }[]; domain?: string; status?: string; type?: string; }", - markdown: - "## retrieve_naics\n\n`client.brand.retrieveNaics(input: string, maxResults?: number, minResults?: number, timeoutMS?: number): { codes?: object[]; domain?: string; status?: string; type?: string; }`\n\n**get** `/brand/naics`\n\nEndpoint to classify any brand into a 2022 NAICS code.\n\n### Parameters\n\n- `input: string`\n Brand domain or title to retrieve NAICS code for. If a valid domain is provided in `input`, it will be used for classification, otherwise, we will search for the brand using the provided title.\n\n- `maxResults?: number`\n Maximum number of NAICS codes to return. Must be between 1 and 10. Defaults to 5.\n\n- `minResults?: number`\n Minimum number of NAICS codes to return. Must be at least 1. Defaults to 1.\n\n- `timeoutMS?: number`\n Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).\n\n### Returns\n\n- `{ codes?: { code: string; confidence: 'high' | 'medium' | 'low'; name: string; }[]; domain?: string; status?: string; type?: string; }`\n\n - `codes?: { code: string; confidence: 'high' | 'medium' | 'low'; name: string; }[]`\n - `domain?: string`\n - `status?: string`\n - `type?: string`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.retrieveNaics({ input: 'input' });\n\nconsole.log(response);\n```", - perLanguage: { - http: { + python: { + method: 'brand.identify_from_transaction', example: - 'curl https://api.brand.dev/v1/brand/naics \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY"', + 'import os\nfrom brand.dev import BrandDev\n\nclient = BrandDev(\n api_key=os.environ.get("BRAND_DEV_API_KEY"), # This is the default and can be omitted\n)\nresponse = client.brand.identify_from_transaction(\n transaction_info="transaction_info",\n)\nprint(response.brand)', }, java: { - method: 'brand().retrieveNaics', - example: - 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandRetrieveNaicsParams;\nimport com.branddev.api.models.brand.BrandRetrieveNaicsResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandRetrieveNaicsParams params = BrandRetrieveNaicsParams.builder()\n .input("input")\n .build();\n BrandRetrieveNaicsResponse response = client.brand().retrieveNaics(params);\n }\n}', - }, - python: { - method: 'brand.retrieve_naics', + method: 'brand().identifyFromTransaction', example: - 'import os\nfrom brand.dev import BrandDev\n\nclient = BrandDev(\n api_key=os.environ.get("BRAND_DEV_API_KEY"), # This is the default and can be omitted\n)\nresponse = client.brand.retrieve_naics(\n input="input",\n)\nprint(response.codes)', + 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandIdentifyFromTransactionParams;\nimport com.branddev.api.models.brand.BrandIdentifyFromTransactionResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandIdentifyFromTransactionParams params = BrandIdentifyFromTransactionParams.builder()\n .transactionInfo("transaction_info")\n .build();\n BrandIdentifyFromTransactionResponse response = client.brand().identifyFromTransaction(params);\n }\n}', }, ruby: { - method: 'brand.retrieve_naics', + method: 'brand.identify_from_transaction', example: - 'require "brand_dev"\n\nbrand_dev = BrandDev::Client.new(api_key: "My API Key")\n\nresponse = brand_dev.brand.retrieve_naics(input: "input")\n\nputs(response)', + 'require "brand_dev"\n\nbrand_dev = BrandDev::Client.new(api_key: "My API Key")\n\nresponse = brand_dev.brand.identify_from_transaction(transaction_info: "transaction_info")\n\nputs(response)', }, - typescript: { - method: 'client.brand.retrieveNaics', + http: { example: - "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.retrieveNaics({ input: 'input' });\n\nconsole.log(response.codes);", + 'curl https://api.brand.dev/v1/brand/transaction_identifier \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY"', }, }, }, @@ -377,29 +355,29 @@ const EMBEDDED_METHODS: MethodEntry[] = [ markdown: "## ai_query\n\n`client.brand.aiQuery(data_to_extract: { datapoint_description: string; datapoint_example: string; datapoint_name: string; datapoint_type: 'text' | 'number' | 'date' | 'boolean' | 'list' | 'url'; datapoint_list_type?: 'string' | 'text' | 'number' | 'date' | 'boolean' | 'list' | 'url' | 'object'; datapoint_object_schema?: object; }[], domain: string, specific_pages?: { about_us?: boolean; blog?: boolean; careers?: boolean; contact_us?: boolean; faq?: boolean; home_page?: boolean; pricing?: boolean; privacy_policy?: boolean; terms_and_conditions?: boolean; }, timeoutMS?: number): { data_extracted?: object[]; domain?: string; status?: string; urls_analyzed?: string[]; }`\n\n**post** `/brand/ai/query`\n\nUse AI to extract specific data points from a brand's website. The AI will crawl the website and extract the requested information based on the provided data points.\n\n### Parameters\n\n- `data_to_extract: { datapoint_description: string; datapoint_example: string; datapoint_name: string; datapoint_type: 'text' | 'number' | 'date' | 'boolean' | 'list' | 'url'; datapoint_list_type?: 'string' | 'text' | 'number' | 'date' | 'boolean' | 'list' | 'url' | 'object'; datapoint_object_schema?: object; }[]`\n Array of data points to extract from the website\n\n- `domain: string`\n The domain name to analyze\n\n- `specific_pages?: { about_us?: boolean; blog?: boolean; careers?: boolean; contact_us?: boolean; faq?: boolean; home_page?: boolean; pricing?: boolean; privacy_policy?: boolean; terms_and_conditions?: boolean; }`\n Optional object specifying which pages to analyze\n - `about_us?: boolean`\n Whether to analyze the about us page\n - `blog?: boolean`\n Whether to analyze the blog\n - `careers?: boolean`\n Whether to analyze the careers page\n - `contact_us?: boolean`\n Whether to analyze the contact us page\n - `faq?: boolean`\n Whether to analyze the FAQ page\n - `home_page?: boolean`\n Whether to analyze the home page\n - `pricing?: boolean`\n Whether to analyze the pricing page\n - `privacy_policy?: boolean`\n Whether to analyze the privacy policy page\n - `terms_and_conditions?: boolean`\n Whether to analyze the terms and conditions page\n\n- `timeoutMS?: number`\n Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).\n\n### Returns\n\n- `{ data_extracted?: { datapoint_name?: string; datapoint_value?: string | number | boolean | string[] | number[] | object[]; }[]; domain?: string; status?: string; urls_analyzed?: string[]; }`\n\n - `data_extracted?: { datapoint_name?: string; datapoint_value?: string | number | boolean | string[] | number[] | object[]; }[]`\n - `domain?: string`\n - `status?: string`\n - `urls_analyzed?: string[]`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.aiQuery({ data_to_extract: [{\n datapoint_description: 'datapoint_description',\n datapoint_example: 'datapoint_example',\n datapoint_name: 'datapoint_name',\n datapoint_type: 'text',\n}], domain: 'domain' });\n\nconsole.log(response);\n```", perLanguage: { - http: { - example: - 'curl https://api.brand.dev/v1/brand/ai/query \\\n -H \'Content-Type: application/json\' \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY" \\\n -d \'{\n "data_to_extract": [\n {\n "datapoint_description": "datapoint_description",\n "datapoint_example": "datapoint_example",\n "datapoint_name": "datapoint_name",\n "datapoint_type": "text"\n }\n ],\n "domain": "domain"\n }\'', - }, - java: { - method: 'brand().aiQuery', + typescript: { + method: 'client.brand.aiQuery', example: - 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandAiQueryParams;\nimport com.branddev.api.models.brand.BrandAiQueryResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandAiQueryParams params = BrandAiQueryParams.builder()\n .addDataToExtract(BrandAiQueryParams.DataToExtract.builder()\n .datapointDescription("datapoint_description")\n .datapointExample("datapoint_example")\n .datapointName("datapoint_name")\n .datapointType(BrandAiQueryParams.DataToExtract.DatapointType.TEXT)\n .build())\n .domain("domain")\n .build();\n BrandAiQueryResponse response = client.brand().aiQuery(params);\n }\n}', + "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.aiQuery({\n data_to_extract: [\n {\n datapoint_description: 'datapoint_description',\n datapoint_example: 'datapoint_example',\n datapoint_name: 'datapoint_name',\n datapoint_type: 'text',\n },\n ],\n domain: 'domain',\n});\n\nconsole.log(response.data_extracted);", }, python: { method: 'brand.ai_query', example: 'import os\nfrom brand.dev import BrandDev\n\nclient = BrandDev(\n api_key=os.environ.get("BRAND_DEV_API_KEY"), # This is the default and can be omitted\n)\nresponse = client.brand.ai_query(\n data_to_extract=[{\n "datapoint_description": "datapoint_description",\n "datapoint_example": "datapoint_example",\n "datapoint_name": "datapoint_name",\n "datapoint_type": "text",\n }],\n domain="domain",\n)\nprint(response.data_extracted)', }, + java: { + method: 'brand().aiQuery', + example: + 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandAiQueryParams;\nimport com.branddev.api.models.brand.BrandAiQueryResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandAiQueryParams params = BrandAiQueryParams.builder()\n .addDataToExtract(BrandAiQueryParams.DataToExtract.builder()\n .datapointDescription("datapoint_description")\n .datapointExample("datapoint_example")\n .datapointName("datapoint_name")\n .datapointType(BrandAiQueryParams.DataToExtract.DatapointType.TEXT)\n .build())\n .domain("domain")\n .build();\n BrandAiQueryResponse response = client.brand().aiQuery(params);\n }\n}', + }, ruby: { method: 'brand.ai_query', example: 'require "brand_dev"\n\nbrand_dev = BrandDev::Client.new(api_key: "My API Key")\n\nresponse = brand_dev.brand.ai_query(\n data_to_extract: [\n {\n datapoint_description: "datapoint_description",\n datapoint_example: "datapoint_example",\n datapoint_name: "datapoint_name",\n datapoint_type: :text\n }\n ],\n domain: "domain"\n)\n\nputs(response)', }, - typescript: { - method: 'client.brand.aiQuery', + http: { example: - "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.aiQuery({\n data_to_extract: [\n {\n datapoint_description: 'datapoint_description',\n datapoint_example: 'datapoint_example',\n datapoint_name: 'datapoint_name',\n datapoint_type: 'text',\n },\n ],\n domain: 'domain',\n});\n\nconsole.log(response.data_extracted);", + 'curl https://api.brand.dev/v1/brand/ai/query \\\n -H \'Content-Type: application/json\' \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY" \\\n -d \'{\n "data_to_extract": [\n {\n "datapoint_description": "datapoint_description",\n "datapoint_example": "datapoint_example",\n "datapoint_name": "datapoint_name",\n "datapoint_type": "text"\n }\n ],\n "domain": "domain"\n }\'', }, }, }, @@ -408,38 +386,37 @@ const EMBEDDED_METHODS: MethodEntry[] = [ endpoint: '/brand/prefetch', httpMethod: 'post', summary: 'Prefetch brand data for a domain', - description: - 'Signal that you may fetch brand data for a particular domain soon to improve latency. This endpoint does not charge credits and is available for paid customers to optimize future requests. [You must be on a paid plan to use this endpoint]', + description: 'Signal that you may fetch brand data for a particular domain soon to improve latency.', stainlessPath: '(resource) brand > (method) prefetch', qualified: 'client.brand.prefetch', params: ['domain: string;', 'timeoutMS?: number;'], response: '{ domain?: string; message?: string; status?: string; }', markdown: - "## prefetch\n\n`client.brand.prefetch(domain: string, timeoutMS?: number): { domain?: string; message?: string; status?: string; }`\n\n**post** `/brand/prefetch`\n\nSignal that you may fetch brand data for a particular domain soon to improve latency. This endpoint does not charge credits and is available for paid customers to optimize future requests. [You must be on a paid plan to use this endpoint]\n\n### Parameters\n\n- `domain: string`\n Domain name to prefetch brand data for\n\n- `timeoutMS?: number`\n Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).\n\n### Returns\n\n- `{ domain?: string; message?: string; status?: string; }`\n\n - `domain?: string`\n - `message?: string`\n - `status?: string`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.prefetch({ domain: 'domain' });\n\nconsole.log(response);\n```", + "## prefetch\n\n`client.brand.prefetch(domain: string, timeoutMS?: number): { domain?: string; message?: string; status?: string; }`\n\n**post** `/brand/prefetch`\n\nSignal that you may fetch brand data for a particular domain soon to improve latency.\n\n### Parameters\n\n- `domain: string`\n Domain name to prefetch brand data for\n\n- `timeoutMS?: number`\n Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).\n\n### Returns\n\n- `{ domain?: string; message?: string; status?: string; }`\n\n - `domain?: string`\n - `message?: string`\n - `status?: string`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.prefetch({ domain: 'domain' });\n\nconsole.log(response);\n```", perLanguage: { - http: { - example: - 'curl https://api.brand.dev/v1/brand/prefetch \\\n -H \'Content-Type: application/json\' \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY" \\\n -d \'{\n "domain": "domain"\n }\'', - }, - java: { - method: 'brand().prefetch', + typescript: { + method: 'client.brand.prefetch', example: - 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandPrefetchParams;\nimport com.branddev.api.models.brand.BrandPrefetchResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandPrefetchParams params = BrandPrefetchParams.builder()\n .domain("domain")\n .build();\n BrandPrefetchResponse response = client.brand().prefetch(params);\n }\n}', + "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.prefetch({ domain: 'domain' });\n\nconsole.log(response.domain);", }, python: { method: 'brand.prefetch', example: 'import os\nfrom brand.dev import BrandDev\n\nclient = BrandDev(\n api_key=os.environ.get("BRAND_DEV_API_KEY"), # This is the default and can be omitted\n)\nresponse = client.brand.prefetch(\n domain="domain",\n)\nprint(response.domain)', }, + java: { + method: 'brand().prefetch', + example: + 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandPrefetchParams;\nimport com.branddev.api.models.brand.BrandPrefetchResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandPrefetchParams params = BrandPrefetchParams.builder()\n .domain("domain")\n .build();\n BrandPrefetchResponse response = client.brand().prefetch(params);\n }\n}', + }, ruby: { method: 'brand.prefetch', example: 'require "brand_dev"\n\nbrand_dev = BrandDev::Client.new(api_key: "My API Key")\n\nresponse = brand_dev.brand.prefetch(domain: "domain")\n\nputs(response)', }, - typescript: { - method: 'client.brand.prefetch', + http: { example: - "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.prefetch({ domain: 'domain' });\n\nconsole.log(response.domain);", + 'curl https://api.brand.dev/v1/brand/prefetch \\\n -H \'Content-Type: application/json\' \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY" \\\n -d \'{\n "domain": "domain"\n }\'', }, }, }, @@ -449,37 +426,37 @@ const EMBEDDED_METHODS: MethodEntry[] = [ httpMethod: 'post', summary: 'Prefetch brand data by email', description: - "Signal that you may fetch brand data for a particular domain soon to improve latency. This endpoint accepts an email address, extracts the domain from it, validates that it's not a disposable or free email provider, and queues the domain for prefetching. This endpoint does not charge credits and is available for paid customers to optimize future requests. [You must be on a paid plan to use this endpoint]", + "Signal that you may fetch brand data for a particular domain soon to improve latency. This endpoint accepts an email address, extracts the domain from it, validates that it's not a disposable or free email provider, and queues the domain for prefetching.", stainlessPath: '(resource) brand > (method) prefetch_by_email', qualified: 'client.brand.prefetchByEmail', params: ['email: string;', 'timeoutMS?: number;'], response: '{ domain?: string; message?: string; status?: string; }', markdown: - "## prefetch_by_email\n\n`client.brand.prefetchByEmail(email: string, timeoutMS?: number): { domain?: string; message?: string; status?: string; }`\n\n**post** `/brand/prefetch-by-email`\n\nSignal that you may fetch brand data for a particular domain soon to improve latency. This endpoint accepts an email address, extracts the domain from it, validates that it's not a disposable or free email provider, and queues the domain for prefetching. This endpoint does not charge credits and is available for paid customers to optimize future requests. [You must be on a paid plan to use this endpoint]\n\n### Parameters\n\n- `email: string`\n Email address to prefetch brand data for. The domain will be extracted from the email. Free email providers (gmail.com, yahoo.com, etc.) and disposable email addresses are not allowed.\n\n- `timeoutMS?: number`\n Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).\n\n### Returns\n\n- `{ domain?: string; message?: string; status?: string; }`\n\n - `domain?: string`\n - `message?: string`\n - `status?: string`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.prefetchByEmail({ email: 'dev@stainless.com' });\n\nconsole.log(response);\n```", + "## prefetch_by_email\n\n`client.brand.prefetchByEmail(email: string, timeoutMS?: number): { domain?: string; message?: string; status?: string; }`\n\n**post** `/brand/prefetch-by-email`\n\nSignal that you may fetch brand data for a particular domain soon to improve latency. This endpoint accepts an email address, extracts the domain from it, validates that it's not a disposable or free email provider, and queues the domain for prefetching.\n\n### Parameters\n\n- `email: string`\n Email address to prefetch brand data for. The domain will be extracted from the email. Free email providers (gmail.com, yahoo.com, etc.) and disposable email addresses are not allowed.\n\n- `timeoutMS?: number`\n Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).\n\n### Returns\n\n- `{ domain?: string; message?: string; status?: string; }`\n\n - `domain?: string`\n - `message?: string`\n - `status?: string`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.prefetchByEmail({ email: 'dev@stainless.com' });\n\nconsole.log(response);\n```", perLanguage: { - http: { - example: - 'curl https://api.brand.dev/v1/brand/prefetch-by-email \\\n -H \'Content-Type: application/json\' \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY" \\\n -d \'{\n "email": "dev@stainless.com"\n }\'', - }, - java: { - method: 'brand().prefetchByEmail', + typescript: { + method: 'client.brand.prefetchByEmail', example: - 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandPrefetchByEmailParams;\nimport com.branddev.api.models.brand.BrandPrefetchByEmailResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandPrefetchByEmailParams params = BrandPrefetchByEmailParams.builder()\n .email("dev@stainless.com")\n .build();\n BrandPrefetchByEmailResponse response = client.brand().prefetchByEmail(params);\n }\n}', + "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.prefetchByEmail({ email: 'dev@stainless.com' });\n\nconsole.log(response.domain);", }, python: { method: 'brand.prefetch_by_email', example: 'import os\nfrom brand.dev import BrandDev\n\nclient = BrandDev(\n api_key=os.environ.get("BRAND_DEV_API_KEY"), # This is the default and can be omitted\n)\nresponse = client.brand.prefetch_by_email(\n email="dev@stainless.com",\n)\nprint(response.domain)', }, + java: { + method: 'brand().prefetchByEmail', + example: + 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandPrefetchByEmailParams;\nimport com.branddev.api.models.brand.BrandPrefetchByEmailResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandPrefetchByEmailParams params = BrandPrefetchByEmailParams.builder()\n .email("dev@stainless.com")\n .build();\n BrandPrefetchByEmailResponse response = client.brand().prefetchByEmail(params);\n }\n}', + }, ruby: { method: 'brand.prefetch_by_email', example: 'require "brand_dev"\n\nbrand_dev = BrandDev::Client.new(api_key: "My API Key")\n\nresponse = brand_dev.brand.prefetch_by_email(email: "dev@stainless.com")\n\nputs(response)', }, - typescript: { - method: 'client.brand.prefetchByEmail', + http: { example: - "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.prefetchByEmail({ email: 'dev@stainless.com' });\n\nconsole.log(response.domain);", + 'curl https://api.brand.dev/v1/brand/prefetch-by-email \\\n -H \'Content-Type: application/json\' \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY" \\\n -d \'{\n "email": "dev@stainless.com"\n }\'', }, }, }, @@ -489,166 +466,38 @@ const EMBEDDED_METHODS: MethodEntry[] = [ httpMethod: 'get', summary: 'Retrieve simplified brand data by domain', description: - 'Returns a simplified version of brand data containing only essential information: domain, title, colors, logos, and backdrops. This endpoint is optimized for faster responses and reduced data transfer.', + 'Returns a simplified version of brand data containing only essential information: domain, title, colors, logos, and backdrops. Optimized for faster responses and reduced data transfer.', stainlessPath: '(resource) brand > (method) retrieve_simplified', qualified: 'client.brand.retrieveSimplified', - params: ['domain: string;', 'timeoutMS?: number;'], + params: ['domain: string;', 'maxAgeMs?: number;', 'timeoutMS?: number;'], response: "{ brand?: { backdrops?: { colors?: object[]; resolution?: object; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; domain?: string; logos?: { colors?: object[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: object; type?: 'icon' | 'logo'; url?: string; }[]; title?: string; }; code?: number; status?: string; }", markdown: - "## retrieve_simplified\n\n`client.brand.retrieveSimplified(domain: string, timeoutMS?: number): { brand?: object; code?: number; status?: string; }`\n\n**get** `/brand/retrieve-simplified`\n\nReturns a simplified version of brand data containing only essential information: domain, title, colors, logos, and backdrops. This endpoint is optimized for faster responses and reduced data transfer.\n\n### Parameters\n\n- `domain: string`\n Domain name to retrieve simplified brand data for\n\n- `timeoutMS?: number`\n Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).\n\n### Returns\n\n- `{ brand?: { backdrops?: { colors?: object[]; resolution?: object; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; domain?: string; logos?: { colors?: object[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: object; type?: 'icon' | 'logo'; url?: string; }[]; title?: string; }; code?: number; status?: string; }`\n\n - `brand?: { backdrops?: { colors?: { hex?: string; name?: string; }[]; resolution?: { aspect_ratio?: number; height?: number; width?: number; }; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; domain?: string; logos?: { colors?: { hex?: string; name?: string; }[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: { aspect_ratio?: number; height?: number; width?: number; }; type?: 'icon' | 'logo'; url?: string; }[]; title?: string; }`\n - `code?: number`\n - `status?: string`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.retrieveSimplified({ domain: 'domain' });\n\nconsole.log(response);\n```", + "## retrieve_simplified\n\n`client.brand.retrieveSimplified(domain: string, maxAgeMs?: number, timeoutMS?: number): { brand?: object; code?: number; status?: string; }`\n\n**get** `/brand/retrieve-simplified`\n\nReturns a simplified version of brand data containing only essential information: domain, title, colors, logos, and backdrops. Optimized for faster responses and reduced data transfer.\n\n### Parameters\n\n- `domain: string`\n Domain name to retrieve simplified brand data for\n\n- `maxAgeMs?: number`\n Maximum age in milliseconds for cached brand data before the API performs a hard refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 year.\n\n- `timeoutMS?: number`\n Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).\n\n### Returns\n\n- `{ brand?: { backdrops?: { colors?: object[]; resolution?: object; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; domain?: string; logos?: { colors?: object[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: object; type?: 'icon' | 'logo'; url?: string; }[]; title?: string; }; code?: number; status?: string; }`\n\n - `brand?: { backdrops?: { colors?: { hex?: string; name?: string; }[]; resolution?: { aspect_ratio?: number; height?: number; width?: number; }; url?: string; }[]; colors?: { hex?: string; name?: string; }[]; domain?: string; logos?: { colors?: { hex?: string; name?: string; }[]; mode?: 'light' | 'dark' | 'has_opaque_background'; resolution?: { aspect_ratio?: number; height?: number; width?: number; }; type?: 'icon' | 'logo'; url?: string; }[]; title?: string; }`\n - `code?: number`\n - `status?: string`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.retrieveSimplified({ domain: 'domain' });\n\nconsole.log(response);\n```", perLanguage: { - http: { - example: - 'curl https://api.brand.dev/v1/brand/retrieve-simplified \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY"', - }, - java: { - method: 'brand().retrieveSimplified', - example: - 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandRetrieveSimplifiedParams;\nimport com.branddev.api.models.brand.BrandRetrieveSimplifiedResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandRetrieveSimplifiedParams params = BrandRetrieveSimplifiedParams.builder()\n .domain("domain")\n .build();\n BrandRetrieveSimplifiedResponse response = client.brand().retrieveSimplified(params);\n }\n}', - }, - python: { - method: 'brand.retrieve_simplified', - example: - 'import os\nfrom brand.dev import BrandDev\n\nclient = BrandDev(\n api_key=os.environ.get("BRAND_DEV_API_KEY"), # This is the default and can be omitted\n)\nresponse = client.brand.retrieve_simplified(\n domain="domain",\n)\nprint(response.brand)', - }, - ruby: { - method: 'brand.retrieve_simplified', - example: - 'require "brand_dev"\n\nbrand_dev = BrandDev::Client.new(api_key: "My API Key")\n\nresponse = brand_dev.brand.retrieve_simplified(domain: "domain")\n\nputs(response)', - }, typescript: { method: 'client.brand.retrieveSimplified', example: "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.retrieveSimplified({ domain: 'domain' });\n\nconsole.log(response.brand);", }, - }, - }, - { - name: 'styleguide', - endpoint: '/brand/styleguide', - httpMethod: 'get', - summary: 'Extract design system and styleguide from website', - description: - "Automatically extract comprehensive design system information from a brand's website including colors, typography, spacing, shadows, and UI components. Either 'domain' or 'directUrl' must be provided as a query parameter, but not both.", - stainlessPath: '(resource) brand > (method) styleguide', - qualified: 'client.brand.styleguide', - params: ['directUrl?: string;', 'domain?: string;', 'timeoutMS?: number;'], - response: - "{ code?: number; domain?: string; status?: string; styleguide?: { colors: { accent: string; background: string; text: string; }; components: { button: object; card?: object; }; elementSpacing: { lg: string; md: string; sm: string; xl: string; xs: string; }; mode: 'light' | 'dark'; shadows: { inner: string; lg: string; md: string; sm: string; xl: string; }; typography: { headings: object; p?: object; }; }; }", - markdown: - "## styleguide\n\n`client.brand.styleguide(directUrl?: string, domain?: string, timeoutMS?: number): { code?: number; domain?: string; status?: string; styleguide?: object; }`\n\n**get** `/brand/styleguide`\n\nAutomatically extract comprehensive design system information from a brand's website including colors, typography, spacing, shadows, and UI components. Either 'domain' or 'directUrl' must be provided as a query parameter, but not both.\n\n### Parameters\n\n- `directUrl?: string`\n A specific URL to fetch the styleguide from directly, bypassing domain resolution (e.g., 'https://example.com/design-system').\n\n- `domain?: string`\n Domain name to extract styleguide from (e.g., 'example.com', 'google.com'). The domain will be automatically normalized and validated.\n\n- `timeoutMS?: number`\n Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).\n\n### Returns\n\n- `{ code?: number; domain?: string; status?: string; styleguide?: { colors: { accent: string; background: string; text: string; }; components: { button: object; card?: object; }; elementSpacing: { lg: string; md: string; sm: string; xl: string; xs: string; }; mode: 'light' | 'dark'; shadows: { inner: string; lg: string; md: string; sm: string; xl: string; }; typography: { headings: object; p?: object; }; }; }`\n\n - `code?: number`\n - `domain?: string`\n - `status?: string`\n - `styleguide?: { colors: { accent: string; background: string; text: string; }; components: { button: { link?: { backgroundColor: string; borderColor: string; borderRadius: string; borderStyle: string; borderWidth: string; boxShadow: string; color: string; css: string; fontSize: string; fontWeight: number; minHeight: string; minWidth: string; padding: string; textDecoration: string; fontFallbacks?: string[]; fontFamily?: string; textDecorationColor?: string; }; primary?: { backgroundColor: string; borderColor: string; borderRadius: string; borderStyle: string; borderWidth: string; boxShadow: string; color: string; css: string; fontSize: string; fontWeight: number; minHeight: string; minWidth: string; padding: string; textDecoration: string; fontFallbacks?: string[]; fontFamily?: string; textDecorationColor?: string; }; secondary?: { backgroundColor: string; borderColor: string; borderRadius: string; borderStyle: string; borderWidth: string; boxShadow: string; color: string; css: string; fontSize: string; fontWeight: number; minHeight: string; minWidth: string; padding: string; textDecoration: string; fontFallbacks?: string[]; fontFamily?: string; textDecorationColor?: string; }; }; card?: { backgroundColor: string; borderColor: string; borderRadius: string; borderStyle: string; borderWidth: string; boxShadow: string; css: string; padding: string; textColor: string; }; }; elementSpacing: { lg: string; md: string; sm: string; xl: string; xs: string; }; mode: 'light' | 'dark'; shadows: { inner: string; lg: string; md: string; sm: string; xl: string; }; typography: { headings: { h1?: { fontFallbacks: string[]; fontFamily: string; fontSize: string; fontWeight: number; letterSpacing: string; lineHeight: string; }; h2?: { fontFallbacks: string[]; fontFamily: string; fontSize: string; fontWeight: number; letterSpacing: string; lineHeight: string; }; h3?: { fontFallbacks: string[]; fontFamily: string; fontSize: string; fontWeight: number; letterSpacing: string; lineHeight: string; }; h4?: { fontFallbacks: string[]; fontFamily: string; fontSize: string; fontWeight: number; letterSpacing: string; lineHeight: string; }; }; p?: { fontFallbacks: string[]; fontFamily: string; fontSize: string; fontWeight: number; letterSpacing: string; lineHeight: string; }; }; }`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.styleguide();\n\nconsole.log(response);\n```", - perLanguage: { - http: { - example: - 'curl https://api.brand.dev/v1/brand/styleguide \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY"', - }, - java: { - method: 'brand().styleguide', - example: - 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandStyleguideParams;\nimport com.branddev.api.models.brand.BrandStyleguideResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandStyleguideResponse response = client.brand().styleguide();\n }\n}', - }, python: { - method: 'brand.styleguide', - example: - 'import os\nfrom brand.dev import BrandDev\n\nclient = BrandDev(\n api_key=os.environ.get("BRAND_DEV_API_KEY"), # This is the default and can be omitted\n)\nresponse = client.brand.styleguide()\nprint(response.styleguide)', - }, - ruby: { - method: 'brand.styleguide', - example: - 'require "brand_dev"\n\nbrand_dev = BrandDev::Client.new(api_key: "My API Key")\n\nresponse = brand_dev.brand.styleguide\n\nputs(response)', - }, - typescript: { - method: 'client.brand.styleguide', - example: - "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.styleguide();\n\nconsole.log(response.styleguide);", - }, - }, - }, - { - name: 'screenshot', - endpoint: '/brand/screenshot', - httpMethod: 'get', - summary: 'Take screenshot of website', - description: - 'Capture a screenshot of a website. Supports both viewport (standard browser view) and full-page screenshots. Can also screenshot specific page types (login, pricing, etc.) by using heuristics to find the appropriate URL. Returns a URL to the uploaded screenshot image hosted on our CDN.', - stainlessPath: '(resource) brand > (method) screenshot', - qualified: 'client.brand.screenshot', - params: [ - 'domain: string;', - "fullScreenshot?: 'true' | 'false';", - "page?: 'login' | 'signup' | 'blog' | 'careers' | 'pricing' | 'terms' | 'privacy' | 'contact';", - "prioritize?: 'speed' | 'quality';", - ], - response: - "{ code?: number; domain?: string; screenshot?: string; screenshotType?: 'viewport' | 'fullPage'; status?: string; }", - markdown: - "## screenshot\n\n`client.brand.screenshot(domain: string, fullScreenshot?: 'true' | 'false', page?: 'login' | 'signup' | 'blog' | 'careers' | 'pricing' | 'terms' | 'privacy' | 'contact', prioritize?: 'speed' | 'quality'): { code?: number; domain?: string; screenshot?: string; screenshotType?: 'viewport' | 'fullPage'; status?: string; }`\n\n**get** `/brand/screenshot`\n\nCapture a screenshot of a website. Supports both viewport (standard browser view) and full-page screenshots. Can also screenshot specific page types (login, pricing, etc.) by using heuristics to find the appropriate URL. Returns a URL to the uploaded screenshot image hosted on our CDN.\n\n### Parameters\n\n- `domain: string`\n Domain name to take screenshot of (e.g., 'example.com', 'google.com'). The domain will be automatically normalized and validated.\n\n- `fullScreenshot?: 'true' | 'false'`\n Optional parameter to determine screenshot type. If 'true', takes a full page screenshot capturing all content. If 'false' or not provided, takes a viewport screenshot (standard browser view).\n\n- `page?: 'login' | 'signup' | 'blog' | 'careers' | 'pricing' | 'terms' | 'privacy' | 'contact'`\n Optional parameter to specify which page type to screenshot. If provided, the system will scrape the domain's links and use heuristics to find the most appropriate URL for the specified page type (30 supported languages). If not provided, screenshots the main domain landing page.\n\n- `prioritize?: 'speed' | 'quality'`\n Optional parameter to prioritize screenshot capture. If 'speed', optimizes for faster capture with basic quality. If 'quality', optimizes for higher quality with longer wait times. Defaults to 'quality' if not provided.\n\n### Returns\n\n- `{ code?: number; domain?: string; screenshot?: string; screenshotType?: 'viewport' | 'fullPage'; status?: string; }`\n\n - `code?: number`\n - `domain?: string`\n - `screenshot?: string`\n - `screenshotType?: 'viewport' | 'fullPage'`\n - `status?: string`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.screenshot({ domain: 'domain' });\n\nconsole.log(response);\n```", - perLanguage: { - http: { + method: 'brand.retrieve_simplified', example: - 'curl https://api.brand.dev/v1/brand/screenshot \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY"', + 'import os\nfrom brand.dev import BrandDev\n\nclient = BrandDev(\n api_key=os.environ.get("BRAND_DEV_API_KEY"), # This is the default and can be omitted\n)\nresponse = client.brand.retrieve_simplified(\n domain="domain",\n)\nprint(response.brand)', }, java: { - method: 'brand().screenshot', - example: - 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandScreenshotParams;\nimport com.branddev.api.models.brand.BrandScreenshotResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandScreenshotParams params = BrandScreenshotParams.builder()\n .domain("domain")\n .build();\n BrandScreenshotResponse response = client.brand().screenshot(params);\n }\n}', - }, - python: { - method: 'brand.screenshot', + method: 'brand().retrieveSimplified', example: - 'import os\nfrom brand.dev import BrandDev\n\nclient = BrandDev(\n api_key=os.environ.get("BRAND_DEV_API_KEY"), # This is the default and can be omitted\n)\nresponse = client.brand.screenshot(\n domain="domain",\n)\nprint(response.code)', + 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandRetrieveSimplifiedParams;\nimport com.branddev.api.models.brand.BrandRetrieveSimplifiedResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandRetrieveSimplifiedParams params = BrandRetrieveSimplifiedParams.builder()\n .domain("domain")\n .build();\n BrandRetrieveSimplifiedResponse response = client.brand().retrieveSimplified(params);\n }\n}', }, ruby: { - method: 'brand.screenshot', - example: - 'require "brand_dev"\n\nbrand_dev = BrandDev::Client.new(api_key: "My API Key")\n\nresponse = brand_dev.brand.screenshot(domain: "domain")\n\nputs(response)', - }, - typescript: { - method: 'client.brand.screenshot', + method: 'brand.retrieve_simplified', example: - "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.screenshot({ domain: 'domain' });\n\nconsole.log(response.code);", + 'require "brand_dev"\n\nbrand_dev = BrandDev::Client.new(api_key: "My API Key")\n\nresponse = brand_dev.brand.retrieve_simplified(domain: "domain")\n\nputs(response)', }, - }, - }, - { - name: 'fonts', - endpoint: '/brand/fonts', - httpMethod: 'get', - summary: 'Extract fonts from website', - description: - "Extract font information from a brand's website including font families, usage statistics, fallbacks, and element/word counts.", - stainlessPath: '(resource) brand > (method) fonts', - qualified: 'client.brand.fonts', - params: ['domain: string;', 'timeoutMS?: number;'], - response: - '{ code: number; domain: string; fonts: { fallbacks: string[]; font: string; num_elements: number; num_words: number; percent_elements: number; percent_words: number; uses: string[]; }[]; status: string; }', - markdown: - "## fonts\n\n`client.brand.fonts(domain: string, timeoutMS?: number): { code: number; domain: string; fonts: object[]; status: string; }`\n\n**get** `/brand/fonts`\n\nExtract font information from a brand's website including font families, usage statistics, fallbacks, and element/word counts.\n\n### Parameters\n\n- `domain: string`\n Domain name to extract fonts from (e.g., 'example.com', 'google.com'). The domain will be automatically normalized and validated.\n\n- `timeoutMS?: number`\n Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).\n\n### Returns\n\n- `{ code: number; domain: string; fonts: { fallbacks: string[]; font: string; num_elements: number; num_words: number; percent_elements: number; percent_words: number; uses: string[]; }[]; status: string; }`\n\n - `code: number`\n - `domain: string`\n - `fonts: { fallbacks: string[]; font: string; num_elements: number; num_words: number; percent_elements: number; percent_words: number; uses: string[]; }[]`\n - `status: string`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.fonts({ domain: 'domain' });\n\nconsole.log(response);\n```", - perLanguage: { http: { example: - 'curl https://api.brand.dev/v1/brand/fonts \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY"', - }, - java: { - method: 'brand().fonts', - example: - 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandFontsParams;\nimport com.branddev.api.models.brand.BrandFontsResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandFontsParams params = BrandFontsParams.builder()\n .domain("domain")\n .build();\n BrandFontsResponse response = client.brand().fonts(params);\n }\n}', - }, - python: { - method: 'brand.fonts', - example: - 'import os\nfrom brand.dev import BrandDev\n\nclient = BrandDev(\n api_key=os.environ.get("BRAND_DEV_API_KEY"), # This is the default and can be omitted\n)\nresponse = client.brand.fonts(\n domain="domain",\n)\nprint(response.code)', - }, - ruby: { - method: 'brand.fonts', - example: - 'require "brand_dev"\n\nbrand_dev = BrandDev::Client.new(api_key: "My API Key")\n\nresponse = brand_dev.brand.fonts(domain: "domain")\n\nputs(response)', - }, - typescript: { - method: 'client.brand.fonts', - example: - "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.fonts({ domain: 'domain' });\n\nconsole.log(response.code);", + 'curl https://api.brand.dev/v1/brand/retrieve-simplified \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY"', }, }, }, @@ -658,38 +507,38 @@ const EMBEDDED_METHODS: MethodEntry[] = [ httpMethod: 'post', summary: "Extract products from a brand's website", description: - "Beta feature: Extract product information from a brand's website. We will analyze the website and return a list of products with details such as name, description, image, pricing, features, and more.", + "Extract product information from a brand's website. We will analyze the website and return a list of products with details such as name, description, image, pricing, features, and more.", stainlessPath: '(resource) brand > (method) ai_products', qualified: 'client.brand.aiProducts', params: [ - '{ domain: string; maxProducts?: number; timeoutMS?: number; } | { directUrl: string; maxProducts?: number; timeoutMS?: number; };', + '{ domain: string; maxAgeMs?: number; maxProducts?: number; timeoutMS?: number; } | { directUrl: string; maxAgeMs?: number; maxProducts?: number; timeoutMS?: number; };', ], response: - "{ products?: { description: string; features: string[]; images: string[]; name: string; tags: string[]; target_audience: string[]; billing_frequency?: 'monthly' | 'yearly' | 'one_time' | 'usage_based'; category?: string; currency?: string; image_url?: string; price?: number; pricing_model?: 'per_seat' | 'flat' | 'tiered' | 'freemium' | 'custom'; url?: string; }[]; }", + "{ products?: { description: string; features: string[]; images: string[]; name: string; sku: string; tags: string[]; target_audience: string[]; billing_frequency?: 'monthly' | 'yearly' | 'one_time' | 'usage_based'; category?: string; currency?: string; image_url?: string; price?: number; pricing_model?: 'per_seat' | 'flat' | 'tiered' | 'freemium' | 'custom'; url?: string; }[]; }", perLanguage: { - http: { - example: - 'curl https://api.brand.dev/v1/brand/ai/products \\\n -H \'Content-Type: application/json\' \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY" \\\n -d \'{\n "domain": "domain"\n }\'', - }, - java: { - method: 'brand().aiProducts', + typescript: { + method: 'client.brand.aiProducts', example: - 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandAiProductsParams;\nimport com.branddev.api.models.brand.BrandAiProductsResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandAiProductsParams.Body.ByDomain params = BrandAiProductsParams.Body.ByDomain.builder()\n .domain("domain")\n .build();\n BrandAiProductsResponse response = client.brand().aiProducts(params);\n }\n}', + "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.aiProducts({ domain: 'domain' });\n\nconsole.log(response.products);", }, python: { method: 'brand.ai_products', example: 'import os\nfrom brand.dev import BrandDev\n\nclient = BrandDev(\n api_key=os.environ.get("BRAND_DEV_API_KEY"), # This is the default and can be omitted\n)\nresponse = client.brand.ai_products(\n domain="domain",\n)\nprint(response.products)', }, + java: { + method: 'brand().aiProducts', + example: + 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandAiProductsParams;\nimport com.branddev.api.models.brand.BrandAiProductsResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandAiProductsParams.Body.ByDomain params = BrandAiProductsParams.Body.ByDomain.builder()\n .domain("domain")\n .build();\n BrandAiProductsResponse response = client.brand().aiProducts(params);\n }\n}', + }, ruby: { method: 'brand.ai_products', example: 'require "brand_dev"\n\nbrand_dev = BrandDev::Client.new(api_key: "My API Key")\n\nresponse = brand_dev.brand.ai_products(body: {domain: "domain"})\n\nputs(response)', }, - typescript: { - method: 'client.brand.aiProducts', + http: { example: - "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.aiProducts({ domain: 'domain' });\n\nconsole.log(response.products);", + 'curl https://api.brand.dev/v1/brand/ai/products \\\n -H \'Content-Type: application/json\' \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY" \\\n -d \'{\n "domain": "domain"\n }\'', }, }, }, @@ -699,38 +548,38 @@ const EMBEDDED_METHODS: MethodEntry[] = [ httpMethod: 'post', summary: 'Extract a single product from a URL', description: - 'Beta feature: Given a single URL, determines if it is a product detail page, classifies the platform/product type, and extracts the product information. Supports Amazon, TikTok Shop, Etsy, and generic ecommerce sites.', + 'Given a single URL, determines if it is a product page and extracts the product information.', stainlessPath: '(resource) brand > (method) ai_product', qualified: 'client.brand.aiProduct', - params: ['url: string;', 'timeoutMS?: number;'], + params: ['url: string;', 'maxAgeMs?: number;', 'timeoutMS?: number;'], response: - "{ is_product_page?: boolean; platform?: 'amazon' | 'tiktok_shop' | 'etsy' | 'generic'; product?: { description: string; features: string[]; images: string[]; name: string; tags: string[]; target_audience: string[]; billing_frequency?: 'monthly' | 'yearly' | 'one_time' | 'usage_based'; category?: string; currency?: string; image_url?: string; price?: number; pricing_model?: 'per_seat' | 'flat' | 'tiered' | 'freemium' | 'custom'; url?: string; }; }", + "{ is_product_page?: boolean; platform?: 'amazon' | 'tiktok_shop' | 'etsy' | 'generic'; product?: { description: string; features: string[]; images: string[]; name: string; sku: string; tags: string[]; target_audience: string[]; billing_frequency?: 'monthly' | 'yearly' | 'one_time' | 'usage_based'; category?: string; currency?: string; image_url?: string; price?: number; pricing_model?: 'per_seat' | 'flat' | 'tiered' | 'freemium' | 'custom'; url?: string; }; }", markdown: - "## ai_product\n\n`client.brand.aiProduct(url: string, timeoutMS?: number): { is_product_page?: boolean; platform?: 'amazon' | 'tiktok_shop' | 'etsy' | 'generic'; product?: object; }`\n\n**post** `/brand/ai/product`\n\nBeta feature: Given a single URL, determines if it is a product detail page, classifies the platform/product type, and extracts the product information. Supports Amazon, TikTok Shop, Etsy, and generic ecommerce sites.\n\n### Parameters\n\n- `url: string`\n The product page URL to extract product data from.\n\n- `timeoutMS?: number`\n Optional timeout in milliseconds for the request. Maximum allowed value is 300000ms (5 minutes).\n\n### Returns\n\n- `{ is_product_page?: boolean; platform?: 'amazon' | 'tiktok_shop' | 'etsy' | 'generic'; product?: { description: string; features: string[]; images: string[]; name: string; tags: string[]; target_audience: string[]; billing_frequency?: 'monthly' | 'yearly' | 'one_time' | 'usage_based'; category?: string; currency?: string; image_url?: string; price?: number; pricing_model?: 'per_seat' | 'flat' | 'tiered' | 'freemium' | 'custom'; url?: string; }; }`\n\n - `is_product_page?: boolean`\n - `platform?: 'amazon' | 'tiktok_shop' | 'etsy' | 'generic'`\n - `product?: { description: string; features: string[]; images: string[]; name: string; tags: string[]; target_audience: string[]; billing_frequency?: 'monthly' | 'yearly' | 'one_time' | 'usage_based'; category?: string; currency?: string; image_url?: string; price?: number; pricing_model?: 'per_seat' | 'flat' | 'tiered' | 'freemium' | 'custom'; url?: string; }`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.aiProduct({ url: 'https://example.com' });\n\nconsole.log(response);\n```", + "## ai_product\n\n`client.brand.aiProduct(url: string, maxAgeMs?: number, timeoutMS?: number): { is_product_page?: boolean; platform?: 'amazon' | 'tiktok_shop' | 'etsy' | 'generic'; product?: object; }`\n\n**post** `/brand/ai/product`\n\nGiven a single URL, determines if it is a product page and extracts the product information.\n\n### Parameters\n\n- `url: string`\n The product page URL to extract product data from.\n\n- `maxAgeMs?: number`\n Return a cached result if a prior scrape for the same parameters exists and is younger than this many milliseconds. Defaults to 7 days (604800000 ms) when omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh.\n\n- `timeoutMS?: number`\n Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).\n\n### Returns\n\n- `{ is_product_page?: boolean; platform?: 'amazon' | 'tiktok_shop' | 'etsy' | 'generic'; product?: { description: string; features: string[]; images: string[]; name: string; sku: string; tags: string[]; target_audience: string[]; billing_frequency?: 'monthly' | 'yearly' | 'one_time' | 'usage_based'; category?: string; currency?: string; image_url?: string; price?: number; pricing_model?: 'per_seat' | 'flat' | 'tiered' | 'freemium' | 'custom'; url?: string; }; }`\n\n - `is_product_page?: boolean`\n - `platform?: 'amazon' | 'tiktok_shop' | 'etsy' | 'generic'`\n - `product?: { description: string; features: string[]; images: string[]; name: string; sku: string; tags: string[]; target_audience: string[]; billing_frequency?: 'monthly' | 'yearly' | 'one_time' | 'usage_based'; category?: string; currency?: string; image_url?: string; price?: number; pricing_model?: 'per_seat' | 'flat' | 'tiered' | 'freemium' | 'custom'; url?: string; }`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.aiProduct({ url: 'https://example.com' });\n\nconsole.log(response);\n```", perLanguage: { - http: { - example: - 'curl https://api.brand.dev/v1/brand/ai/product \\\n -H \'Content-Type: application/json\' \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY" \\\n -d \'{\n "url": "https://example.com"\n }\'', - }, - java: { - method: 'brand().aiProduct', + typescript: { + method: 'client.brand.aiProduct', example: - 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandAiProductParams;\nimport com.branddev.api.models.brand.BrandAiProductResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandAiProductParams params = BrandAiProductParams.builder()\n .url("https://example.com")\n .build();\n BrandAiProductResponse response = client.brand().aiProduct(params);\n }\n}', + "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.aiProduct({ url: 'https://example.com' });\n\nconsole.log(response.is_product_page);", }, python: { method: 'brand.ai_product', example: 'import os\nfrom brand.dev import BrandDev\n\nclient = BrandDev(\n api_key=os.environ.get("BRAND_DEV_API_KEY"), # This is the default and can be omitted\n)\nresponse = client.brand.ai_product(\n url="https://example.com",\n)\nprint(response.is_product_page)', }, + java: { + method: 'brand().aiProduct', + example: + 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandAiProductParams;\nimport com.branddev.api.models.brand.BrandAiProductResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandAiProductParams params = BrandAiProductParams.builder()\n .url("https://example.com")\n .build();\n BrandAiProductResponse response = client.brand().aiProduct(params);\n }\n}', + }, ruby: { method: 'brand.ai_product', example: 'require "brand_dev"\n\nbrand_dev = BrandDev::Client.new(api_key: "My API Key")\n\nresponse = brand_dev.brand.ai_product(url: "https://example.com")\n\nputs(response)', }, - typescript: { - method: 'client.brand.aiProduct', + http: { example: - "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.aiProduct({ url: 'https://example.com' });\n\nconsole.log(response.is_product_page);", + 'curl https://api.brand.dev/v1/brand/ai/product \\\n -H \'Content-Type: application/json\' \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY" \\\n -d \'{\n "url": "https://example.com"\n }\'', }, }, }, @@ -738,38 +587,50 @@ const EMBEDDED_METHODS: MethodEntry[] = [ name: 'web_scrape_html', endpoint: '/web/scrape/html', httpMethod: 'get', - summary: 'Scrape raw HTML from a URL', + summary: 'Scrape HTML', description: 'Scrapes the given URL and returns the raw HTML content of the page.', stainlessPath: '(resource) brand > (method) web_scrape_html', qualified: 'client.brand.webScrapeHTML', - params: ['url: string;'], - response: '{ html: string; success: true; url: string; }', + params: [ + 'url: string;', + 'excludeSelectors?: string[];', + 'headers?: object;', + 'includeFrames?: boolean;', + 'includeSelectors?: string[];', + 'maxAgeMs?: number;', + 'pdf?: { end?: number; shouldParse?: boolean; start?: number; };', + 'timeoutMS?: number;', + 'useMainContentOnly?: boolean;', + 'waitForMs?: number;', + ], + response: + "{ html: string; success: true; type: 'html' | 'xml' | 'json' | 'text' | 'csv' | 'markdown' | 'svg' | 'pdf'; url: string; }", markdown: - "## web_scrape_html\n\n`client.brand.webScrapeHTML(url: string): { html: string; success: true; url: string; }`\n\n**get** `/web/scrape/html`\n\nScrapes the given URL and returns the raw HTML content of the page.\n\n### Parameters\n\n- `url: string`\n Full URL to scrape (must include http:// or https:// protocol)\n\n### Returns\n\n- `{ html: string; success: true; url: string; }`\n\n - `html: string`\n - `success: true`\n - `url: string`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.webScrapeHTML({ url: 'https://example.com' });\n\nconsole.log(response);\n```", + "## web_scrape_html\n\n`client.brand.webScrapeHTML(url: string, excludeSelectors?: string[], headers?: object, includeFrames?: boolean, includeSelectors?: string[], maxAgeMs?: number, pdf?: { end?: number; shouldParse?: boolean; start?: number; }, timeoutMS?: number, useMainContentOnly?: boolean, waitForMs?: number): { html: string; success: true; type: 'html' | 'xml' | 'json' | 'text' | 'csv' | 'markdown' | 'svg' | 'pdf'; url: string; }`\n\n**get** `/web/scrape/html`\n\nScrapes the given URL and returns the raw HTML content of the page.\n\n### Parameters\n\n- `url: string`\n Full URL to scrape (must include http:// or https:// protocol)\n\n- `excludeSelectors?: string[]`\n CSS selectors to remove from the result. Applied after includeSelectors. Exclusion takes precedence: an element matching both is removed. Examples: \"nav\", \"footer\", \".ad-banner\", \"[aria-hidden=true]\".\n\n- `headers?: object`\n Optional outbound HTTP headers forwarded only to the target URL, sent as deep-object query params such as headers[X-Custom]=value. When provided, caching is bypassed: the result is neither read from nor written to cache.\n\n- `includeFrames?: boolean`\n When true, iframes are rendered inline into the returned HTML.\n\n- `includeSelectors?: string[]`\n CSS selectors. When provided, only matching subtrees (and their descendants) are kept and everything else is dropped. When omitted, the entire document is kept. Examples: \"article.main\", \"#content\", \"[role=main]\".\n\n- `maxAgeMs?: number`\n Return a cached result if a prior scrape for the same parameters exists and is younger than this many milliseconds. Defaults to 1 day (86400000 ms) when omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh.\n\n- `pdf?: { end?: number; shouldParse?: boolean; start?: number; }`\n PDF parsing controls. Use start/end to limit text extraction and OCR to an inclusive 1-based page range.\n - `end?: number`\n Last 1-based PDF page to parse. When omitted, parsing ends at the last page. Must be greater than or equal to start when both are provided.\n - `shouldParse?: boolean`\n When true, PDF URLs are fetched and parsed. When false, PDF URLs are skipped and a 400 WEBSITE_ACCESS_ERROR is returned.\n - `start?: number`\n First 1-based PDF page to parse. When omitted, parsing starts at the first page.\n\n- `timeoutMS?: number`\n Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).\n\n- `useMainContentOnly?: boolean`\n When true, return only the page's main content in the HTML response, excluding headers, footers, sidebars, and navigation when detectable.\n\n- `waitForMs?: number`\n Optional browser wait time in milliseconds after initial page load. Min: 0. Max: 30000 (30 seconds). \n\n### Returns\n\n- `{ html: string; success: true; type: 'html' | 'xml' | 'json' | 'text' | 'csv' | 'markdown' | 'svg' | 'pdf'; url: string; }`\n\n - `html: string`\n - `success: true`\n - `type: 'html' | 'xml' | 'json' | 'text' | 'csv' | 'markdown' | 'svg' | 'pdf'`\n - `url: string`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.webScrapeHTML({ url: 'https://example.com' });\n\nconsole.log(response);\n```", perLanguage: { - http: { - example: - 'curl https://api.brand.dev/v1/web/scrape/html \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY"', - }, - java: { - method: 'brand().webScrapeHtml', + typescript: { + method: 'client.brand.webScrapeHTML', example: - 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandWebScrapeHtmlParams;\nimport com.branddev.api.models.brand.BrandWebScrapeHtmlResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandWebScrapeHtmlParams params = BrandWebScrapeHtmlParams.builder()\n .url("https://example.com")\n .build();\n BrandWebScrapeHtmlResponse response = client.brand().webScrapeHtml(params);\n }\n}', + "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.webScrapeHTML({ url: 'https://example.com' });\n\nconsole.log(response.html);", }, python: { method: 'brand.web_scrape_html', example: 'import os\nfrom brand.dev import BrandDev\n\nclient = BrandDev(\n api_key=os.environ.get("BRAND_DEV_API_KEY"), # This is the default and can be omitted\n)\nresponse = client.brand.web_scrape_html(\n url="https://example.com",\n)\nprint(response.html)', }, + java: { + method: 'brand().webScrapeHtml', + example: + 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandWebScrapeHtmlParams;\nimport com.branddev.api.models.brand.BrandWebScrapeHtmlResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandWebScrapeHtmlParams params = BrandWebScrapeHtmlParams.builder()\n .url("https://example.com")\n .build();\n BrandWebScrapeHtmlResponse response = client.brand().webScrapeHtml(params);\n }\n}', + }, ruby: { method: 'brand.web_scrape_html', example: 'require "brand_dev"\n\nbrand_dev = BrandDev::Client.new(api_key: "My API Key")\n\nresponse = brand_dev.brand.web_scrape_html(url: "https://example.com")\n\nputs(response)', }, - typescript: { - method: 'client.brand.webScrapeHTML', + http: { example: - "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.webScrapeHTML({ url: 'https://example.com' });\n\nconsole.log(response.html);", + 'curl https://api.brand.dev/v1/web/scrape/html \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY"', }, }, }, @@ -777,44 +638,52 @@ const EMBEDDED_METHODS: MethodEntry[] = [ name: 'web_scrape_md', endpoint: '/web/scrape/markdown', httpMethod: 'get', - summary: 'Scrape URL and convert to Markdown', - description: 'Scrapes the given URL, converts the HTML content to Markdown, and returns the result.', + summary: 'Scrape Markdown', + description: 'Scrapes the given URL into LLM usable Markdown.', stainlessPath: '(resource) brand > (method) web_scrape_md', qualified: 'client.brand.webScrapeMd', params: [ 'url: string;', + 'excludeSelectors?: string[];', + 'headers?: object;', + 'includeFrames?: boolean;', 'includeImages?: boolean;', 'includeLinks?: boolean;', + 'includeSelectors?: string[];', + 'maxAgeMs?: number;', + 'pdf?: { end?: number; shouldParse?: boolean; start?: number; };', 'shortenBase64Images?: boolean;', + 'timeoutMS?: number;', 'useMainContentOnly?: boolean;', + 'waitForMs?: number;', ], response: '{ markdown: string; success: true; url: string; }', markdown: - "## web_scrape_md\n\n`client.brand.webScrapeMd(url: string, includeImages?: boolean, includeLinks?: boolean, shortenBase64Images?: boolean, useMainContentOnly?: boolean): { markdown: string; success: true; url: string; }`\n\n**get** `/web/scrape/markdown`\n\nScrapes the given URL, converts the HTML content to Markdown, and returns the result.\n\n### Parameters\n\n- `url: string`\n Full URL to scrape and convert to markdown (must include http:// or https:// protocol)\n\n- `includeImages?: boolean`\n Include image references in Markdown output\n\n- `includeLinks?: boolean`\n Preserve hyperlinks in Markdown output\n\n- `shortenBase64Images?: boolean`\n Shorten base64-encoded image data in the Markdown output\n\n- `useMainContentOnly?: boolean`\n Extract only the main content of the page, excluding headers, footers, sidebars, and navigation\n\n### Returns\n\n- `{ markdown: string; success: true; url: string; }`\n\n - `markdown: string`\n - `success: true`\n - `url: string`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.webScrapeMd({ url: 'https://example.com' });\n\nconsole.log(response);\n```", + '## web_scrape_md\n\n`client.brand.webScrapeMd(url: string, excludeSelectors?: string[], headers?: object, includeFrames?: boolean, includeImages?: boolean, includeLinks?: boolean, includeSelectors?: string[], maxAgeMs?: number, pdf?: { end?: number; shouldParse?: boolean; start?: number; }, shortenBase64Images?: boolean, timeoutMS?: number, useMainContentOnly?: boolean, waitForMs?: number): { markdown: string; success: true; url: string; }`\n\n**get** `/web/scrape/markdown`\n\nScrapes the given URL into LLM usable Markdown.\n\n### Parameters\n\n- `url: string`\n Full URL to scrape into LLM usable Markdown (must include http:// or https:// protocol)\n\n- `excludeSelectors?: string[]`\n CSS selectors to remove before conversion to Markdown. Applied after includeSelectors. Exclusion takes precedence: an element matching both is removed. Examples: "nav", "footer", ".ad-banner", "[aria-hidden=true]".\n\n- `headers?: object`\n Optional outbound HTTP headers forwarded only to the target URL, sent as deep-object query params such as headers[X-Custom]=value. When provided, caching is bypassed: the result is neither read from nor written to cache.\n\n- `includeFrames?: boolean`\n When true, the contents of iframes are rendered to Markdown.\n\n- `includeImages?: boolean`\n Include image references in Markdown output\n\n- `includeLinks?: boolean`\n Preserve hyperlinks in Markdown output\n\n- `includeSelectors?: string[]`\n CSS selectors. When provided, only matching HTML subtrees (and their descendants) are kept before conversion to Markdown. When omitted, the entire document is kept. Examples: "article.main", "#content", "[role=main]".\n\n- `maxAgeMs?: number`\n Return a cached result if a prior scrape for the same parameters exists and is younger than this many milliseconds. Defaults to 1 day (86400000 ms) when omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh.\n\n- `pdf?: { end?: number; shouldParse?: boolean; start?: number; }`\n PDF parsing controls. Use start/end to limit text extraction and OCR to an inclusive 1-based page range.\n - `end?: number`\n Last 1-based PDF page to parse. When omitted, parsing ends at the last page. Must be greater than or equal to start when both are provided.\n - `shouldParse?: boolean`\n When true, PDF URLs are fetched and parsed. When false, PDF URLs are skipped and a 400 WEBSITE_ACCESS_ERROR is returned.\n - `start?: number`\n First 1-based PDF page to parse. When omitted, parsing starts at the first page.\n\n- `shortenBase64Images?: boolean`\n Shorten base64-encoded image data in the Markdown output\n\n- `timeoutMS?: number`\n Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).\n\n- `useMainContentOnly?: boolean`\n Extract only the main content of the page, excluding headers, footers, sidebars, and navigation\n\n- `waitForMs?: number`\n Optional browser wait time in milliseconds after initial page load before converting the page to Markdown. Min: 0. Max: 30000 (30 seconds). \n\n### Returns\n\n- `{ markdown: string; success: true; url: string; }`\n\n - `markdown: string`\n - `success: true`\n - `url: string`\n\n### Example\n\n```typescript\nimport BrandDev from \'brand.dev\';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.webScrapeMd({ url: \'https://example.com\' });\n\nconsole.log(response);\n```', perLanguage: { - http: { - example: - 'curl https://api.brand.dev/v1/web/scrape/markdown \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY"', - }, - java: { - method: 'brand().webScrapeMd', + typescript: { + method: 'client.brand.webScrapeMd', example: - 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandWebScrapeMdParams;\nimport com.branddev.api.models.brand.BrandWebScrapeMdResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandWebScrapeMdParams params = BrandWebScrapeMdParams.builder()\n .url("https://example.com")\n .build();\n BrandWebScrapeMdResponse response = client.brand().webScrapeMd(params);\n }\n}', + "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.webScrapeMd({ url: 'https://example.com' });\n\nconsole.log(response.markdown);", }, python: { method: 'brand.web_scrape_md', example: 'import os\nfrom brand.dev import BrandDev\n\nclient = BrandDev(\n api_key=os.environ.get("BRAND_DEV_API_KEY"), # This is the default and can be omitted\n)\nresponse = client.brand.web_scrape_md(\n url="https://example.com",\n)\nprint(response.markdown)', }, + java: { + method: 'brand().webScrapeMd', + example: + 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandWebScrapeMdParams;\nimport com.branddev.api.models.brand.BrandWebScrapeMdResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandWebScrapeMdParams params = BrandWebScrapeMdParams.builder()\n .url("https://example.com")\n .build();\n BrandWebScrapeMdResponse response = client.brand().webScrapeMd(params);\n }\n}', + }, ruby: { method: 'brand.web_scrape_md', example: 'require "brand_dev"\n\nbrand_dev = BrandDev::Client.new(api_key: "My API Key")\n\nresponse = brand_dev.brand.web_scrape_md(url: "https://example.com")\n\nputs(response)', }, - typescript: { - method: 'client.brand.webScrapeMd', + http: { example: - "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.webScrapeMd({ url: 'https://example.com' });\n\nconsole.log(response.markdown);", + 'curl https://api.brand.dev/v1/web/scrape/markdown \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY"', }, }, }, @@ -822,40 +691,47 @@ const EMBEDDED_METHODS: MethodEntry[] = [ name: 'web_scrape_images', endpoint: '/web/scrape/images', httpMethod: 'get', - summary: 'Scrape images from a URL', + summary: 'Scrape Images', description: - 'Scrapes all images from the given URL. Extracts images from img, svg, picture/source, link, and video elements including inline SVGs, base64 data URIs, and standard URLs.', + 'Extract image assets from a web page, including standard URLs, inline SVGs, data URIs, responsive image sources, metadata, CSS backgrounds, video posters, and embeds. The base request costs 1 credit. When enrichment is enabled, the entire call costs 5 credits.', stainlessPath: '(resource) brand > (method) web_scrape_images', qualified: 'client.brand.webScrapeImages', - params: ['url: string;'], + params: [ + 'url: string;', + 'enrichment?: { classification?: boolean; hostedUrl?: boolean; maxTimePerMs?: number; resolution?: boolean; };', + 'headers?: object;', + 'maxAgeMs?: number;', + 'timeoutMS?: number;', + 'waitForMs?: number;', + ], response: - "{ images: { alt: string; element: 'img' | 'svg' | 'link' | 'source' | 'video' | 'css' | 'object' | 'meta' | 'background'; src: string; type: 'url' | 'html' | 'base64'; }[]; success: true; url: string; }", + "{ images: { alt: string; element: 'img' | 'svg' | 'link' | 'source' | 'video' | 'css' | 'object' | 'meta' | 'background'; src: string; type: 'url' | 'html' | 'base64'; enrichment?: { height?: number; mimetype?: string; type?: 'photography' | 'illustration' | 'logo' | 'wordmark' | 'icon' | 'pattern' | 'graphic' | 'other'; url?: string; width?: number; }; }[]; success: true; url: string; }", markdown: - "## web_scrape_images\n\n`client.brand.webScrapeImages(url: string): { images: object[]; success: true; url: string; }`\n\n**get** `/web/scrape/images`\n\nScrapes all images from the given URL. Extracts images from img, svg, picture/source, link, and video elements including inline SVGs, base64 data URIs, and standard URLs.\n\n### Parameters\n\n- `url: string`\n Full URL to scrape images from (must include http:// or https:// protocol)\n\n### Returns\n\n- `{ images: { alt: string; element: 'img' | 'svg' | 'link' | 'source' | 'video' | 'css' | 'object' | 'meta' | 'background'; src: string; type: 'url' | 'html' | 'base64'; }[]; success: true; url: string; }`\n\n - `images: { alt: string; element: 'img' | 'svg' | 'link' | 'source' | 'video' | 'css' | 'object' | 'meta' | 'background'; src: string; type: 'url' | 'html' | 'base64'; }[]`\n - `success: true`\n - `url: string`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.webScrapeImages({ url: 'https://example.com' });\n\nconsole.log(response);\n```", + "## web_scrape_images\n\n`client.brand.webScrapeImages(url: string, enrichment?: { classification?: boolean; hostedUrl?: boolean; maxTimePerMs?: number; resolution?: boolean; }, headers?: object, maxAgeMs?: number, timeoutMS?: number, waitForMs?: number): { images: object[]; success: true; url: string; }`\n\n**get** `/web/scrape/images`\n\nExtract image assets from a web page, including standard URLs, inline SVGs, data URIs, responsive image sources, metadata, CSS backgrounds, video posters, and embeds. The base request costs 1 credit. When enrichment is enabled, the entire call costs 5 credits.\n\n### Parameters\n\n- `url: string`\n Page URL to inspect. Must include http:// or https://.\n\n- `enrichment?: { classification?: boolean; hostedUrl?: boolean; maxTimePerMs?: number; resolution?: boolean; }`\n Optional per-image processing, sent as deep-object query params such as enrichment[resolution]=true.\n - `classification?: boolean`\n Classify each image by visual asset type.\n - `hostedUrl?: boolean`\n Host materializable images on the Brand.dev CDN and return their URL and MIME type.\n - `maxTimePerMs?: number`\n Per-image enrichment timeout in milliseconds. Default: 30000. Maximum: 60000.\n - `resolution?: boolean`\n Measure image width and height when possible.\n\n- `headers?: object`\n Optional outbound HTTP headers forwarded only to the target URL, sent as deep-object query params such as headers[X-Custom]=value. When provided, caching is bypassed: the result is neither read from nor written to cache.\n\n- `maxAgeMs?: number`\n Reuse a cached result this many milliseconds old or newer. Default: 86400000 (1 day). Set to 0 to bypass cache. Maximum: 2592000000 (30 days).\n\n- `timeoutMS?: number`\n Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).\n\n- `waitForMs?: number`\n Optional browser wait time in milliseconds after initial page load before collecting images. Min: 0. Max: 30000 (30 seconds). \n\n### Returns\n\n- `{ images: { alt: string; element: 'img' | 'svg' | 'link' | 'source' | 'video' | 'css' | 'object' | 'meta' | 'background'; src: string; type: 'url' | 'html' | 'base64'; enrichment?: { height?: number; mimetype?: string; type?: 'photography' | 'illustration' | 'logo' | 'wordmark' | 'icon' | 'pattern' | 'graphic' | 'other'; url?: string; width?: number; }; }[]; success: true; url: string; }`\n\n - `images: { alt: string; element: 'img' | 'svg' | 'link' | 'source' | 'video' | 'css' | 'object' | 'meta' | 'background'; src: string; type: 'url' | 'html' | 'base64'; enrichment?: { height?: number; mimetype?: string; type?: 'photography' | 'illustration' | 'logo' | 'wordmark' | 'icon' | 'pattern' | 'graphic' | 'other'; url?: string; width?: number; }; }[]`\n - `success: true`\n - `url: string`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.webScrapeImages({ url: 'https://example.com' });\n\nconsole.log(response);\n```", perLanguage: { - http: { - example: - 'curl https://api.brand.dev/v1/web/scrape/images \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY"', - }, - java: { - method: 'brand().webScrapeImages', + typescript: { + method: 'client.brand.webScrapeImages', example: - 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandWebScrapeImagesParams;\nimport com.branddev.api.models.brand.BrandWebScrapeImagesResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandWebScrapeImagesParams params = BrandWebScrapeImagesParams.builder()\n .url("https://example.com")\n .build();\n BrandWebScrapeImagesResponse response = client.brand().webScrapeImages(params);\n }\n}', + "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.webScrapeImages({ url: 'https://example.com' });\n\nconsole.log(response.images);", }, python: { method: 'brand.web_scrape_images', example: 'import os\nfrom brand.dev import BrandDev\n\nclient = BrandDev(\n api_key=os.environ.get("BRAND_DEV_API_KEY"), # This is the default and can be omitted\n)\nresponse = client.brand.web_scrape_images(\n url="https://example.com",\n)\nprint(response.images)', }, + java: { + method: 'brand().webScrapeImages', + example: + 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandWebScrapeImagesParams;\nimport com.branddev.api.models.brand.BrandWebScrapeImagesResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandWebScrapeImagesParams params = BrandWebScrapeImagesParams.builder()\n .url("https://example.com")\n .build();\n BrandWebScrapeImagesResponse response = client.brand().webScrapeImages(params);\n }\n}', + }, ruby: { method: 'brand.web_scrape_images', example: 'require "brand_dev"\n\nbrand_dev = BrandDev::Client.new(api_key: "My API Key")\n\nresponse = brand_dev.brand.web_scrape_images(url: "https://example.com")\n\nputs(response)', }, - typescript: { - method: 'client.brand.webScrapeImages', + http: { example: - "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.webScrapeImages({ url: 'https://example.com' });\n\nconsole.log(response.images);", + 'curl https://api.brand.dev/v1/web/scrape/images \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY"', }, }, }, @@ -863,40 +739,45 @@ const EMBEDDED_METHODS: MethodEntry[] = [ name: 'web_scrape_sitemap', endpoint: '/web/scrape/sitemap', httpMethod: 'get', - summary: 'Crawl website sitemap', - description: - 'Crawls the sitemap of the given domain and returns all discovered page URLs. Supports sitemap index files (recursive), parallel fetching with concurrency control, deduplication, and filters out non-page resources (images, PDFs, etc.).', + summary: 'Crawl Sitemap', + description: "Crawl an entire website's sitemap and return all discovered page URLs.", stainlessPath: '(resource) brand > (method) web_scrape_sitemap', qualified: 'client.brand.webScrapeSitemap', - params: ['domain: string;', 'maxLinks?: number;'], + params: [ + 'domain: string;', + 'headers?: object;', + 'maxLinks?: number;', + 'timeoutMS?: number;', + 'urlRegex?: string;', + ], response: '{ domain: string; meta: { errors: number; sitemapsDiscovered: number; sitemapsFetched: number; sitemapsSkipped: number; }; success: true; urls: string[]; }', markdown: - "## web_scrape_sitemap\n\n`client.brand.webScrapeSitemap(domain: string, maxLinks?: number): { domain: string; meta: object; success: true; urls: string[]; }`\n\n**get** `/web/scrape/sitemap`\n\nCrawls the sitemap of the given domain and returns all discovered page URLs. Supports sitemap index files (recursive), parallel fetching with concurrency control, deduplication, and filters out non-page resources (images, PDFs, etc.).\n\n### Parameters\n\n- `domain: string`\n Domain name to crawl sitemaps for (e.g., 'example.com'). The domain will be automatically normalized and validated.\n\n- `maxLinks?: number`\n Maximum number of links to return from the sitemap crawl. Defaults to 10,000. Minimum is 1, maximum is 100,000.\n\n### Returns\n\n- `{ domain: string; meta: { errors: number; sitemapsDiscovered: number; sitemapsFetched: number; sitemapsSkipped: number; }; success: true; urls: string[]; }`\n\n - `domain: string`\n - `meta: { errors: number; sitemapsDiscovered: number; sitemapsFetched: number; sitemapsSkipped: number; }`\n - `success: true`\n - `urls: string[]`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.webScrapeSitemap({ domain: 'domain' });\n\nconsole.log(response);\n```", + "## web_scrape_sitemap\n\n`client.brand.webScrapeSitemap(domain: string, headers?: object, maxLinks?: number, timeoutMS?: number, urlRegex?: string): { domain: string; meta: object; success: true; urls: string[]; }`\n\n**get** `/web/scrape/sitemap`\n\nCrawl an entire website's sitemap and return all discovered page URLs.\n\n### Parameters\n\n- `domain: string`\n Domain to build a sitemap for\n\n- `headers?: object`\n Optional outbound HTTP headers forwarded only to the target URL, sent as deep-object query params such as headers[X-Custom]=value. When provided, caching is bypassed: the result is neither read from nor written to cache.\n\n- `maxLinks?: number`\n Maximum number of links to return from the sitemap crawl. Defaults to 10,000. Minimum is 1, maximum is 100,000.\n\n- `timeoutMS?: number`\n Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).\n\n- `urlRegex?: string`\n Optional RE2-compatible regex pattern. Only URLs matching this pattern are returned and counted against maxLinks.\n\n### Returns\n\n- `{ domain: string; meta: { errors: number; sitemapsDiscovered: number; sitemapsFetched: number; sitemapsSkipped: number; }; success: true; urls: string[]; }`\n\n - `domain: string`\n - `meta: { errors: number; sitemapsDiscovered: number; sitemapsFetched: number; sitemapsSkipped: number; }`\n - `success: true`\n - `urls: string[]`\n\n### Example\n\n```typescript\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev();\n\nconst response = await client.brand.webScrapeSitemap({ domain: 'domain' });\n\nconsole.log(response);\n```", perLanguage: { - http: { - example: - 'curl https://api.brand.dev/v1/web/scrape/sitemap \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY"', - }, - java: { - method: 'brand().webScrapeSitemap', + typescript: { + method: 'client.brand.webScrapeSitemap', example: - 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandWebScrapeSitemapParams;\nimport com.branddev.api.models.brand.BrandWebScrapeSitemapResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandWebScrapeSitemapParams params = BrandWebScrapeSitemapParams.builder()\n .domain("domain")\n .build();\n BrandWebScrapeSitemapResponse response = client.brand().webScrapeSitemap(params);\n }\n}', + "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.webScrapeSitemap({ domain: 'domain' });\n\nconsole.log(response.domain);", }, python: { method: 'brand.web_scrape_sitemap', example: 'import os\nfrom brand.dev import BrandDev\n\nclient = BrandDev(\n api_key=os.environ.get("BRAND_DEV_API_KEY"), # This is the default and can be omitted\n)\nresponse = client.brand.web_scrape_sitemap(\n domain="domain",\n)\nprint(response.domain)', }, + java: { + method: 'brand().webScrapeSitemap', + example: + 'package com.branddev.api.example;\n\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandWebScrapeSitemapParams;\nimport com.branddev.api.models.brand.BrandWebScrapeSitemapResponse;\n\npublic final class Main {\n private Main() {}\n\n public static void main(String[] args) {\n BrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\n BrandWebScrapeSitemapParams params = BrandWebScrapeSitemapParams.builder()\n .domain("domain")\n .build();\n BrandWebScrapeSitemapResponse response = client.brand().webScrapeSitemap(params);\n }\n}', + }, ruby: { method: 'brand.web_scrape_sitemap', example: 'require "brand_dev"\n\nbrand_dev = BrandDev::Client.new(api_key: "My API Key")\n\nresponse = brand_dev.brand.web_scrape_sitemap(domain: "domain")\n\nputs(response)', }, - typescript: { - method: 'client.brand.webScrapeSitemap', + http: { example: - "import BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.brand.webScrapeSitemap({ domain: 'domain' });\n\nconsole.log(response.domain);", + 'curl https://api.brand.dev/v1/web/scrape/sitemap \\\n -H "Authorization: Bearer $BRAND_DEV_API_KEY"', }, }, }, @@ -904,24 +785,24 @@ const EMBEDDED_METHODS: MethodEntry[] = [ const EMBEDDED_READMES: { language: string; content: string }[] = [ { - language: 'python', + language: 'java', content: - '# Brand Dev Python API library\n\n\n[![PyPI version](https://img.shields.io/pypi/v/brand.dev.svg?label=pypi%20(stable))](https://pypi.org/project/brand.dev/)\n\nThe Brand Dev Python library provides convenient access to the Brand Dev REST API from any Python 3.9+\napplication. The library includes type definitions for all request params and response fields,\nand offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx).\n\n\n\nIt is generated with [Stainless](https://www.stainless.com/).\n\n## MCP Server\n\nUse the Brand Dev MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application.\n\n[![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=brand.dev-mcp&config=eyJuYW1lIjoiYnJhbmQuZGV2LW1jcCIsInRyYW5zcG9ydCI6Imh0dHAiLCJ1cmwiOiJodHRwczovL2JyYW5kLWRldi5zdGxtY3AuY29tIiwiaGVhZGVycyI6eyJ4LWJyYW5kLWRldi1hcGkta2V5IjoiTXkgQVBJIEtleSJ9fQ)\n[![Install in VS Code](https://img.shields.io/badge/_-Add_to_VS_Code-blue?style=for-the-badge&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCA0MCA0MCI+PHBhdGggZmlsbD0iI0VFRSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMzAuMjM1IDM5Ljg4NGEyLjQ5MSAyLjQ5MSAwIDAgMS0xLjc4MS0uNzNMMTIuNyAyNC43OGwtMy40NiAyLjYyNC0zLjQwNiAyLjU4MmExLjY2NSAxLjY2NSAwIDAgMS0xLjA4Mi4zMzggMS42NjQgMS42NjQgMCAwIDEtMS4wNDYtLjQzMWwtMi4yLTJhMS42NjYgMS42NjYgMCAwIDEgMC0yLjQ2M0w3LjQ1OCAyMCA0LjY3IDE3LjQ1MyAxLjUwNyAxNC41N2ExLjY2NSAxLjY2NSAwIDAgMSAwLTIuNDYzbDIuMi0yYTEuNjY1IDEuNjY1IDAgMCAxIDIuMTMtLjA5N2w2Ljg2MyA1LjIwOUwyOC40NTIuODQ0YTIuNDg4IDIuNDg4IDAgMCAxIDEuODQxLS43MjljLjM1MS4wMDkuNjk5LjA5MSAxLjAxOS4yNDVsOC4yMzYgMy45NjFhMi41IDIuNSAwIDAgMSAxLjQxNSAyLjI1M3YuMDk5LS4wNDVWMzMuMzd2LS4wNDUuMDk1YTIuNTAxIDIuNTAxIDAgMCAxLTEuNDE2IDIuMjU3bC04LjIzNSAzLjk2MWEyLjQ5MiAyLjQ5MiAwIDAgMS0xLjA3Ny4yNDZabS43MTYtMjguOTQ3LTExLjk0OCA5LjA2MiAxMS45NTIgOS4wNjUtLjAwNC0xOC4xMjdaIi8+PC9zdmc+)](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22brand.dev-mcp%22%2C%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fbrand-dev.stlmcp.com%22%2C%22headers%22%3A%7B%22x-brand-dev-api-key%22%3A%22My%20API%20Key%22%7D%7D)\n\n> Note: You may need to set environment variables in your MCP client.\n\n## Documentation\n\nThe REST API documentation can be found on [docs.context.dev](https://docs.context.dev/). The full API of this library can be found in [api.md](api.md).\n\n## Installation\n\n```sh\n# install from PyPI\npip install brand.dev\n```\n\n## Usage\n\nThe full API of this library can be found in [api.md](api.md).\n\n```python\nimport os\nfrom brand.dev import BrandDev\n\nclient = BrandDev(\n api_key=os.environ.get("BRAND_DEV_API_KEY"), # This is the default and can be omitted\n)\n\nbrand = client.brand.retrieve(\n domain="REPLACE_ME",\n)\nprint(brand.brand)\n```\n\nWhile you can provide an `api_key` keyword argument,\nwe recommend using [python-dotenv](https://pypi.org/project/python-dotenv/)\nto add `BRAND_DEV_API_KEY="My API Key"` to your `.env` file\nso that your API Key is not stored in source control.\n\n## Async usage\n\nSimply import `AsyncBrandDev` instead of `BrandDev` and use `await` with each API call:\n\n```python\nimport os\nimport asyncio\nfrom brand.dev import AsyncBrandDev\n\nclient = AsyncBrandDev(\n api_key=os.environ.get("BRAND_DEV_API_KEY"), # This is the default and can be omitted\n)\n\nasync def main() -> None:\n brand = await client.brand.retrieve(\n domain="REPLACE_ME",\n )\n print(brand.brand)\n\nasyncio.run(main())\n```\n\nFunctionality between the synchronous and asynchronous clients is otherwise identical.\n\n### With aiohttp\n\nBy default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend.\n\nYou can enable this by installing `aiohttp`:\n\n```sh\n# install from PyPI\npip install brand.dev[aiohttp]\n```\n\nThen you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`:\n\n```python\nimport os\nimport asyncio\nfrom brand.dev import DefaultAioHttpClient\nfrom brand.dev import AsyncBrandDev\n\nasync def main() -> None:\n async with AsyncBrandDev(\n api_key=os.environ.get("BRAND_DEV_API_KEY"), # This is the default and can be omitted\n http_client=DefaultAioHttpClient(),\n) as client:\n brand = await client.brand.retrieve(\n domain="REPLACE_ME",\n )\n print(brand.brand)\n\nasyncio.run(main())\n```\n\n\n\n## Using types\n\nNested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like:\n\n- Serializing back into JSON, `model.to_json()`\n- Converting to a dictionary, `model.to_dict()`\n\nTyped requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`.\n\n\n\n## Nested params\n\nNested parameters are dictionaries, typed using `TypedDict`, for example:\n\n```python\nfrom brand.dev import BrandDev\n\nclient = BrandDev()\n\nresponse = client.brand.ai_query(\n data_to_extract=[{\n "datapoint_description": "datapoint_description",\n "datapoint_example": "datapoint_example",\n "datapoint_name": "datapoint_name",\n "datapoint_type": "text",\n }],\n domain="domain",\n specific_pages={},\n)\nprint(response.specific_pages)\n```\n\n\n\n## Handling errors\n\nWhen the library is unable to connect to the API (for example, due to network connection problems or a timeout), a subclass of `brand.dev.APIConnectionError` is raised.\n\nWhen the API returns a non-success status code (that is, 4xx or 5xx\nresponse), a subclass of `brand.dev.APIStatusError` is raised, containing `status_code` and `response` properties.\n\nAll errors inherit from `brand.dev.APIError`.\n\n```python\nimport brand.dev\nfrom brand.dev import BrandDev\n\nclient = BrandDev()\n\ntry:\n client.brand.retrieve(\n domain="REPLACE_ME",\n )\nexcept brand.dev.APIConnectionError as e:\n print("The server could not be reached")\n print(e.__cause__) # an underlying Exception, likely raised within httpx.\nexcept brand.dev.RateLimitError as e:\n print("A 429 status code was received; we should back off a bit.")\nexcept brand.dev.APIStatusError as e:\n print("Another non-200-range status code was received")\n print(e.status_code)\n print(e.response)\n```\n\nError codes are as follows:\n\n| Status Code | Error Type |\n| ----------- | -------------------------- |\n| 400 | `BadRequestError` |\n| 401 | `AuthenticationError` |\n| 403 | `PermissionDeniedError` |\n| 404 | `NotFoundError` |\n| 422 | `UnprocessableEntityError` |\n| 429 | `RateLimitError` |\n| >=500 | `InternalServerError` |\n| N/A | `APIConnectionError` |\n\n### Retries\n\nCertain errors are automatically retried 2 times by default, with a short exponential backoff.\nConnection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict,\n429 Rate Limit, and >=500 Internal errors are all retried by default.\n\nYou can use the `max_retries` option to configure or disable retry settings:\n\n```python\nfrom brand.dev import BrandDev\n\n# Configure the default for all requests:\nclient = BrandDev(\n # default is 2\n max_retries=0,\n)\n\n# Or, configure per-request:\nclient.with_options(max_retries = 5).brand.retrieve(\n domain="REPLACE_ME",\n)\n```\n\n### Timeouts\n\nBy default requests time out after 1 minute. You can configure this with a `timeout` option,\nwhich accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/timeouts/#fine-tuning-the-configuration) object:\n\n```python\nfrom brand.dev import BrandDev\n\n# Configure the default for all requests:\nclient = BrandDev(\n # 20 seconds (default is 1 minute)\n timeout=20.0,\n)\n\n# More granular control:\nclient = BrandDev(\n timeout=httpx.Timeout(60.0, read=5.0, write=10.0, connect=2.0),\n)\n\n# Override per-request:\nclient.with_options(timeout = 5.0).brand.retrieve(\n domain="REPLACE_ME",\n)\n```\n\nOn timeout, an `APITimeoutError` is thrown.\n\nNote that requests that time out are [retried twice by default](#retries).\n\n\n\n## Advanced\n\n### Logging\n\nWe use the standard library [`logging`](https://docs.python.org/3/library/logging.html) module.\n\nYou can enable logging by setting the environment variable `BRAND_DEV_LOG` to `info`.\n\n```shell\n$ export BRAND_DEV_LOG=info\n```\n\nOr to `debug` for more verbose logging.\n\n### How to tell whether `None` means `null` or missing\n\nIn an API response, a field may be explicitly `null`, or missing entirely; in either case, its value is `None` in this library. You can differentiate the two cases with `.model_fields_set`:\n\n```py\nif response.my_field is None:\n if \'my_field\' not in response.model_fields_set:\n print(\'Got json like {}, without a "my_field" key present at all.\')\n else:\n print(\'Got json like {"my_field": null}.\')\n```\n\n### Accessing raw response data (e.g. headers)\n\nThe "raw" Response object can be accessed by prefixing `.with_raw_response.` to any HTTP method call, e.g.,\n\n```py\nfrom brand.dev import BrandDev\n\nclient = BrandDev()\nresponse = client.brand.with_raw_response.retrieve(\n domain="REPLACE_ME",\n)\nprint(response.headers.get(\'X-My-Header\'))\n\nbrand = response.parse() # get the object that `brand.retrieve()` would have returned\nprint(brand.brand)\n```\n\nThese methods return an [`APIResponse`](https://github.com/context-dot-dev/deprecated-brand-python-sdk/tree/main/src/brand/dev/_response.py) object.\n\nThe async client returns an [`AsyncAPIResponse`](https://github.com/context-dot-dev/deprecated-brand-python-sdk/tree/main/src/brand/dev/_response.py) with the same structure, the only difference being `await`able methods for reading the response content.\n\n#### `.with_streaming_response`\n\nThe above interface eagerly reads the full response body when you make the request, which may not always be what you want.\n\nTo stream the response body, use `.with_streaming_response` instead, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()` or `.parse()`. In the async client, these are async methods.\n\n```python\nwith client.brand.with_streaming_response.retrieve(\n domain="REPLACE_ME",\n) as response :\n print(response.headers.get(\'X-My-Header\'))\n\n for line in response.iter_lines():\n print(line)\n```\n\nThe context manager is required so that the response will reliably be closed.\n\n### Making custom/undocumented requests\n\nThis library is typed for convenient access to the documented API.\n\nIf you need to access undocumented endpoints, params, or response properties, the library can still be used.\n\n#### Undocumented endpoints\n\nTo make requests to undocumented endpoints, you can make requests using `client.get`, `client.post`, and other\nhttp verbs. Options on the client will be respected (such as retries) when making this request.\n\n```py\nimport httpx\n\nresponse = client.post(\n "/foo",\n cast_to=httpx.Response,\n body={"my_param": True},\n)\n\nprint(response.headers.get("x-foo"))\n```\n\n#### Undocumented request params\n\nIf you want to explicitly send an extra param, you can do so with the `extra_query`, `extra_body`, and `extra_headers` request\noptions.\n\n#### Undocumented response properties\n\nTo access undocumented response properties, you can access the extra fields like `response.unknown_prop`. You\ncan also get all the extra fields on the Pydantic model as a dict with\n[`response.model_extra`](https://docs.pydantic.dev/latest/api/base_model/#pydantic.BaseModel.model_extra).\n\n### Configuring the HTTP client\n\nYou can directly override the [httpx client](https://www.python-httpx.org/api/#client) to customize it for your use case, including:\n\n- Support for [proxies](https://www.python-httpx.org/advanced/proxies/)\n- Custom [transports](https://www.python-httpx.org/advanced/transports/)\n- Additional [advanced](https://www.python-httpx.org/advanced/clients/) functionality\n\n```python\nimport httpx\nfrom brand.dev import BrandDev, DefaultHttpxClient\n\nclient = BrandDev(\n # Or use the `BRAND_DEV_BASE_URL` env var\n base_url="http://my.test.server.example.com:8083",\n http_client=DefaultHttpxClient(proxy="http://my.test.proxy.example.com", transport=httpx.HTTPTransport(local_address="0.0.0.0")),\n)\n```\n\nYou can also customize the client on a per-request basis by using `with_options()`:\n\n```python\nclient.with_options(http_client=DefaultHttpxClient(...))\n```\n\n### Managing HTTP resources\n\nBy default the library closes underlying HTTP connections whenever the client is [garbage collected](https://docs.python.org/3/reference/datamodel.html#object.__del__). You can manually close the client using the `.close()` method if desired, or with a context manager that closes when exiting.\n\n```py\nfrom brand.dev import BrandDev\n\nwith BrandDev() as client:\n # make requests here\n ...\n\n# HTTP client is now closed\n```\n\n## Versioning\n\nThis package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:\n\n1. Changes that only affect static types, without breaking runtime behavior.\n2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_\n3. Changes that we do not expect to impact the vast majority of users in practice.\n\nWe take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.\n\nWe are keen for your feedback; please open an [issue](https://www.github.com/context-dot-dev/deprecated-brand-python-sdk/issues) with questions, bugs, or suggestions.\n\n### Determining the installed version\n\nIf you\'ve upgraded to the latest version but aren\'t seeing any new features you were expecting then your python environment is likely still using an older version.\n\nYou can determine the version that is being used at runtime with:\n\n```py\nimport brand.dev\nprint(brand.dev.__version__)\n```\n\n## Requirements\n\nPython 3.9 or higher.\n\n## Contributing\n\nSee [the contributing documentation](./CONTRIBUTING.md).\n', + '# Brand Dev Java API Library\n\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.branddev.api/brand-dev-java)](https://central.sonatype.com/artifact/com.branddev.api/brand-dev-java/0.0.1)\n[![javadoc](https://javadoc.io/badge2/com.branddev.api/brand-dev-java/0.0.1/javadoc.svg)](https://javadoc.io/doc/com.branddev.api/brand-dev-java/0.0.1)\n\n\nThe Brand Dev Java SDK provides convenient access to the [Brand Dev REST API](https://docs.context.dev/) from applications written in Java.\n\n\n\nIt is generated with [Stainless](https://www.stainless.com/).\n\n## MCP Server\n\nUse the Brand Dev MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application.\n\n[![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=brand.dev-mcp&config=eyJuYW1lIjoiYnJhbmQuZGV2LW1jcCIsInRyYW5zcG9ydCI6Imh0dHAiLCJ1cmwiOiJodHRwczovL2JyYW5kLWRldi5zdGxtY3AuY29tIiwiaGVhZGVycyI6eyJ4LWJyYW5kLWRldi1hcGkta2V5IjoiTXkgQVBJIEtleSJ9fQ)\n[![Install in VS Code](https://img.shields.io/badge/_-Add_to_VS_Code-blue?style=for-the-badge&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCA0MCA0MCI+PHBhdGggZmlsbD0iI0VFRSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMzAuMjM1IDM5Ljg4NGEyLjQ5MSAyLjQ5MSAwIDAgMS0xLjc4MS0uNzNMMTIuNyAyNC43OGwtMy40NiAyLjYyNC0zLjQwNiAyLjU4MmExLjY2NSAxLjY2NSAwIDAgMS0xLjA4Mi4zMzggMS42NjQgMS42NjQgMCAwIDEtMS4wNDYtLjQzMWwtMi4yLTJhMS42NjYgMS42NjYgMCAwIDEgMC0yLjQ2M0w3LjQ1OCAyMCA0LjY3IDE3LjQ1MyAxLjUwNyAxNC41N2ExLjY2NSAxLjY2NSAwIDAgMSAwLTIuNDYzbDIuMi0yYTEuNjY1IDEuNjY1IDAgMCAxIDIuMTMtLjA5N2w2Ljg2MyA1LjIwOUwyOC40NTIuODQ0YTIuNDg4IDIuNDg4IDAgMCAxIDEuODQxLS43MjljLjM1MS4wMDkuNjk5LjA5MSAxLjAxOS4yNDVsOC4yMzYgMy45NjFhMi41IDIuNSAwIDAgMSAxLjQxNSAyLjI1M3YuMDk5LS4wNDVWMzMuMzd2LS4wNDUuMDk1YTIuNTAxIDIuNTAxIDAgMCAxLTEuNDE2IDIuMjU3bC04LjIzNSAzLjk2MWEyLjQ5MiAyLjQ5MiAwIDAgMS0xLjA3Ny4yNDZabS43MTYtMjguOTQ3LTExLjk0OCA5LjA2MiAxMS45NTIgOS4wNjUtLjAwNC0xOC4xMjdaIi8+PC9zdmc+)](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22brand.dev-mcp%22%2C%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fbrand-dev.stlmcp.com%22%2C%22headers%22%3A%7B%22x-brand-dev-api-key%22%3A%22My%20API%20Key%22%7D%7D)\n\n> Note: You may need to set environment variables in your MCP client.\n\n\n\nThe REST API documentation can be found on [docs.context.dev](https://docs.context.dev/). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.branddev.api/brand-dev-java/0.0.1).\n\n\n\n## Installation\n\n\n\n### Gradle\n\n~~~kotlin\nimplementation("com.branddev.api:brand-dev-java:0.0.1")\n~~~\n\n### Maven\n\n~~~xml\n\n com.branddev.api\n brand-dev-java\n 0.0.1\n\n~~~\n\n\n\n## Requirements\n\nThis library requires Java 8 or later.\n\n## Usage\n\n```java\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandRetrieveParams;\nimport com.branddev.api.models.brand.BrandRetrieveResponse;\n\n// Configures using the `branddev.apiKey` and `branddev.baseUrl` system properties\n// Or configures using the `BRAND_DEV_API_KEY` and `BRAND_DEV_BASE_URL` environment variables\nBrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\nBrandRetrieveParams params = BrandRetrieveParams.builder()\n .domain("REPLACE_ME")\n .build();\nBrandRetrieveResponse brand = client.brand().retrieve(params);\n```\n\n## Client configuration\n\nConfigure the client using system properties or environment variables:\n\n```java\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\n\n// Configures using the `branddev.apiKey` and `branddev.baseUrl` system properties\n// Or configures using the `BRAND_DEV_API_KEY` and `BRAND_DEV_BASE_URL` environment variables\nBrandDevClient client = BrandDevOkHttpClient.fromEnv();\n```\n\nOr manually:\n\n```java\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\n\nBrandDevClient client = BrandDevOkHttpClient.builder()\n .apiKey("My API Key")\n .build();\n```\n\nOr using a combination of the two approaches:\n\n```java\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\n\nBrandDevClient client = BrandDevOkHttpClient.builder()\n // Configures using the `branddev.apiKey` and `branddev.baseUrl` system properties\n // Or configures using the `BRAND_DEV_API_KEY` and `BRAND_DEV_BASE_URL` environment variables\n .fromEnv()\n .apiKey("My API Key")\n .build();\n```\n\nSee this table for the available options:\n\n| Setter | System property | Environment variable | Required | Default value |\n| --------- | ------------------ | -------------------- | -------- | ---------------------------- |\n| `apiKey` | `branddev.apiKey` | `BRAND_DEV_API_KEY` | true | - |\n| `baseUrl` | `branddev.baseUrl` | `BRAND_DEV_BASE_URL` | true | `"https://api.brand.dev/v1"` |\n\nSystem properties take precedence over environment variables.\n\n> [!TIP]\n> Don\'t create more than one client in the same application. Each client has a connection pool and\n> thread pools, which are more efficient to share between requests.\n\n### Modifying configuration\n\nTo temporarily use a modified client configuration, while reusing the same connection and thread pools, call `withOptions()` on any client or service:\n\n```java\nimport com.branddev.api.client.BrandDevClient;\n\nBrandDevClient clientWithOptions = client.withOptions(optionsBuilder -> {\n optionsBuilder.baseUrl("https://example.com");\n optionsBuilder.maxRetries(42);\n});\n```\n\nThe `withOptions()` method does not affect the original client or service.\n\n## Requests and responses\n\nTo send a request to the Brand Dev API, build an instance of some `Params` class and pass it to the corresponding client method. When the response is received, it will be deserialized into an instance of a Java class.\n\nFor example, `client.brand().retrieve(...)` should be called with an instance of `BrandRetrieveParams`, and it will return an instance of `BrandRetrieveResponse`.\n\n## Immutability\n\nEach class in the SDK has an associated [builder](https://blogs.oracle.com/javamagazine/post/exploring-joshua-blochs-builder-design-pattern-in-java) or factory method for constructing it.\n\nEach class is [immutable](https://docs.oracle.com/javase/tutorial/essential/concurrency/immutable.html) once constructed. If the class has an associated builder, then it has a `toBuilder()` method, which can be used to convert it back to a builder for making a modified copy.\n\nBecause each class is immutable, builder modification will _never_ affect already built class instances.\n\n## Asynchronous execution\n\nThe default client is synchronous. To switch to asynchronous execution, call the `async()` method:\n\n```java\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandRetrieveParams;\nimport com.branddev.api.models.brand.BrandRetrieveResponse;\nimport java.util.concurrent.CompletableFuture;\n\n// Configures using the `branddev.apiKey` and `branddev.baseUrl` system properties\n// Or configures using the `BRAND_DEV_API_KEY` and `BRAND_DEV_BASE_URL` environment variables\nBrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\nBrandRetrieveParams params = BrandRetrieveParams.builder()\n .domain("REPLACE_ME")\n .build();\nCompletableFuture brand = client.async().brand().retrieve(params);\n```\n\nOr create an asynchronous client from the beginning:\n\n```java\nimport com.branddev.api.client.BrandDevClientAsync;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClientAsync;\nimport com.branddev.api.models.brand.BrandRetrieveParams;\nimport com.branddev.api.models.brand.BrandRetrieveResponse;\nimport java.util.concurrent.CompletableFuture;\n\n// Configures using the `branddev.apiKey` and `branddev.baseUrl` system properties\n// Or configures using the `BRAND_DEV_API_KEY` and `BRAND_DEV_BASE_URL` environment variables\nBrandDevClientAsync client = BrandDevOkHttpClientAsync.fromEnv();\n\nBrandRetrieveParams params = BrandRetrieveParams.builder()\n .domain("REPLACE_ME")\n .build();\nCompletableFuture brand = client.brand().retrieve(params);\n```\n\nThe asynchronous client supports the same options as the synchronous one, except most methods return `CompletableFuture`s.\n\n\n\n\n\n\n\n## Raw responses\n\nThe SDK defines methods that deserialize responses into instances of Java classes. However, these methods don\'t provide access to the response headers, status code, or the raw response body.\n\nTo access this data, prefix any HTTP method call on a client or service with `withRawResponse()`:\n\n```java\nimport com.branddev.api.core.http.Headers;\nimport com.branddev.api.core.http.HttpResponseFor;\nimport com.branddev.api.models.brand.BrandRetrieveParams;\nimport com.branddev.api.models.brand.BrandRetrieveResponse;\n\nBrandRetrieveParams params = BrandRetrieveParams.builder()\n .domain("REPLACE_ME")\n .build();\nHttpResponseFor brand = client.brand().withRawResponse().retrieve(params);\n\nint statusCode = brand.statusCode();\nHeaders headers = brand.headers();\n```\n\nYou can still deserialize the response into an instance of a Java class if needed:\n\n```java\nimport com.branddev.api.models.brand.BrandRetrieveResponse;\n\nBrandRetrieveResponse parsedBrand = brand.parse();\n```\n\n## Error handling\n\nThe SDK throws custom unchecked exception types:\n\n- [`BrandDevServiceException`](brand-dev-java-core/src/main/kotlin/com/branddev/api/errors/BrandDevServiceException.kt): Base class for HTTP errors. See this table for which exception subclass is thrown for each HTTP status code:\n\n | Status | Exception |\n | ------ | -------------------------------------------------- |\n | 400 | [`BadRequestException`](brand-dev-java-core/src/main/kotlin/com/branddev/api/errors/BadRequestException.kt) |\n | 401 | [`UnauthorizedException`](brand-dev-java-core/src/main/kotlin/com/branddev/api/errors/UnauthorizedException.kt) |\n | 403 | [`PermissionDeniedException`](brand-dev-java-core/src/main/kotlin/com/branddev/api/errors/PermissionDeniedException.kt) |\n | 404 | [`NotFoundException`](brand-dev-java-core/src/main/kotlin/com/branddev/api/errors/NotFoundException.kt) |\n | 422 | [`UnprocessableEntityException`](brand-dev-java-core/src/main/kotlin/com/branddev/api/errors/UnprocessableEntityException.kt) |\n | 429 | [`RateLimitException`](brand-dev-java-core/src/main/kotlin/com/branddev/api/errors/RateLimitException.kt) |\n | 5xx | [`InternalServerException`](brand-dev-java-core/src/main/kotlin/com/branddev/api/errors/InternalServerException.kt) |\n | others | [`UnexpectedStatusCodeException`](brand-dev-java-core/src/main/kotlin/com/branddev/api/errors/UnexpectedStatusCodeException.kt) |\n\n- [`BrandDevIoException`](brand-dev-java-core/src/main/kotlin/com/branddev/api/errors/BrandDevIoException.kt): I/O networking errors.\n\n- [`BrandDevRetryableException`](brand-dev-java-core/src/main/kotlin/com/branddev/api/errors/BrandDevRetryableException.kt): Generic error indicating a failure that could be retried by the client.\n\n- [`BrandDevInvalidDataException`](brand-dev-java-core/src/main/kotlin/com/branddev/api/errors/BrandDevInvalidDataException.kt): Failure to interpret successfully parsed data. For example, when accessing a property that\'s supposed to be required, but the API unexpectedly omitted it from the response.\n\n- [`BrandDevException`](brand-dev-java-core/src/main/kotlin/com/branddev/api/errors/BrandDevException.kt): Base class for all exceptions. Most errors will result in one of the previously mentioned ones, but completely generic errors may be thrown using the base class.\n\n\n\n## Logging\n\nEnable logging by setting the `BRAND_DEV_LOG` environment variable to `info`:\n\n```sh\nexport BRAND_DEV_LOG=info\n```\n\nOr to `debug` for more verbose logging:\n\n```sh\nexport BRAND_DEV_LOG=debug\n```\n\nOr configure the client manually using the `logLevel` method:\n\n```java\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.core.LogLevel;\n\nBrandDevClient client = BrandDevOkHttpClient.builder()\n .fromEnv()\n .logLevel(LogLevel.INFO)\n .build();\n```\n\n## ProGuard and R8\n\nAlthough the SDK uses reflection, it is still usable with [ProGuard](https://github.com/Guardsquare/proguard) and [R8](https://developer.android.com/topic/performance/app-optimization/enable-app-optimization) because `brand-dev-java-core` is published with a [configuration file](brand-dev-java-core/src/main/resources/META-INF/proguard/brand-dev-java-core.pro) containing [keep rules](https://www.guardsquare.com/manual/configuration/usage).\n\nProGuard and R8 should automatically detect and use the published rules, but you can also manually copy the keep rules if necessary.\n\n\n\n\n\n## Jackson\n\nThe SDK depends on [Jackson](https://github.com/FasterXML/jackson) for JSON serialization/deserialization. It is compatible with version 2.13.4 or higher, but depends on version 2.18.2 by default.\n\nThe SDK throws an exception if it detects an incompatible Jackson version at runtime (e.g. if the default version was overridden in your Maven or Gradle config).\n\nIf the SDK threw an exception, but you\'re _certain_ the version is compatible, then disable the version check using the `checkJacksonVersionCompatibility` on [`BrandDevOkHttpClient`](brand-dev-java-client-okhttp/src/main/kotlin/com/branddev/api/client/okhttp/BrandDevOkHttpClient.kt) or [`BrandDevOkHttpClientAsync`](brand-dev-java-client-okhttp/src/main/kotlin/com/branddev/api/client/okhttp/BrandDevOkHttpClientAsync.kt).\n\n> [!CAUTION]\n> We make no guarantee that the SDK works correctly when the Jackson version check is disabled.\n\nAlso note that there are bugs in older Jackson versions that can affect the SDK. We don\'t work around all Jackson bugs ([example](https://github.com/FasterXML/jackson-databind/issues/3240)) and expect users to upgrade Jackson for those instead.\n\n## Network options\n\n### Retries\n\nThe SDK automatically retries 2 times by default, with a short exponential backoff between requests.\n\nOnly the following error types are retried:\n- Connection errors (for example, due to a network connectivity problem)\n- 408 Request Timeout\n- 409 Conflict\n- 429 Rate Limit\n- 5xx Internal\n\nThe API may also explicitly instruct the SDK to retry or not retry a request.\n\nTo set a custom number of retries, configure the client using the `maxRetries` method:\n\n```java\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\n\nBrandDevClient client = BrandDevOkHttpClient.builder()\n .fromEnv()\n .maxRetries(4)\n .build();\n```\n\n### Timeouts\n\nRequests time out after 1 minute by default.\n\nTo set a custom timeout, configure the method call using the `timeout` method:\n\n```java\nimport com.branddev.api.models.brand.BrandRetrieveResponse;\n\nBrandRetrieveResponse brand = client.brand().retrieve(\n params, RequestOptions.builder().timeout(Duration.ofSeconds(30)).build()\n);\n```\n\nOr configure the default for all method calls at the client level:\n\n```java\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport java.time.Duration;\n\nBrandDevClient client = BrandDevOkHttpClient.builder()\n .fromEnv()\n .timeout(Duration.ofSeconds(30))\n .build();\n```\n\n### Proxies\n\nTo route requests through a proxy, configure the client using the `proxy` method:\n\n```java\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport java.net.InetSocketAddress;\nimport java.net.Proxy;\n\nBrandDevClient client = BrandDevOkHttpClient.builder()\n .fromEnv()\n .proxy(new Proxy(\n Proxy.Type.HTTP, new InetSocketAddress(\n "https://example.com", 8080\n )\n ))\n .build();\n```\n\nIf the proxy responds with `407 Proxy Authentication Required`, supply credentials by also configuring `proxyAuthenticator`:\n\n```java\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.core.http.ProxyAuthenticator;\n\nBrandDevClient client = BrandDevOkHttpClient.builder()\n .fromEnv()\n .proxy(...)\n // Or a custom implementation of `ProxyAuthenticator`.\n .proxyAuthenticator(ProxyAuthenticator.basic("username", "password"))\n .build();\n```\n\n### Connection pooling\n\nTo customize the underlying OkHttp connection pool, configure the client using the `maxIdleConnections` and `keepAliveDuration` methods:\n\n```java\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport java.time.Duration;\n\nBrandDevClient client = BrandDevOkHttpClient.builder()\n .fromEnv()\n // If `maxIdleConnections` is set, then `keepAliveDuration` must be set, and vice versa.\n .maxIdleConnections(10)\n .keepAliveDuration(Duration.ofMinutes(2))\n .build();\n```\n\nIf both options are unset, OkHttp\'s default connection pool settings are used.\n\n### HTTPS\n\n> [!NOTE]\n> Most applications should not call these methods, and instead use the system defaults. The defaults include\n> special optimizations that can be lost if the implementations are modified.\n\nTo configure how HTTPS connections are secured, configure the client using the `sslSocketFactory`, `trustManager`, and `hostnameVerifier` methods:\n\n```java\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\n\nBrandDevClient client = BrandDevOkHttpClient.builder()\n .fromEnv()\n // If `sslSocketFactory` is set, then `trustManager` must be set, and vice versa.\n .sslSocketFactory(yourSSLSocketFactory)\n .trustManager(yourTrustManager)\n .hostnameVerifier(yourHostnameVerifier)\n .build();\n```\n\n\n\n### Custom HTTP client\n\nThe SDK consists of three artifacts:\n- `brand-dev-java-core`\n - Contains core SDK logic\n - Does not depend on [OkHttp](https://square.github.io/okhttp)\n - Exposes [`BrandDevClient`](brand-dev-java-core/src/main/kotlin/com/branddev/api/client/BrandDevClient.kt), [`BrandDevClientAsync`](brand-dev-java-core/src/main/kotlin/com/branddev/api/client/BrandDevClientAsync.kt), [`BrandDevClientImpl`](brand-dev-java-core/src/main/kotlin/com/branddev/api/client/BrandDevClientImpl.kt), and [`BrandDevClientAsyncImpl`](brand-dev-java-core/src/main/kotlin/com/branddev/api/client/BrandDevClientAsyncImpl.kt), all of which can work with any HTTP client\n- `brand-dev-java-client-okhttp`\n - Depends on [OkHttp](https://square.github.io/okhttp)\n - Exposes [`BrandDevOkHttpClient`](brand-dev-java-client-okhttp/src/main/kotlin/com/branddev/api/client/okhttp/BrandDevOkHttpClient.kt) and [`BrandDevOkHttpClientAsync`](brand-dev-java-client-okhttp/src/main/kotlin/com/branddev/api/client/okhttp/BrandDevOkHttpClientAsync.kt), which provide a way to construct [`BrandDevClientImpl`](brand-dev-java-core/src/main/kotlin/com/branddev/api/client/BrandDevClientImpl.kt) and [`BrandDevClientAsyncImpl`](brand-dev-java-core/src/main/kotlin/com/branddev/api/client/BrandDevClientAsyncImpl.kt), respectively, using OkHttp\n- `brand-dev-java`\n - Depends on and exposes the APIs of both `brand-dev-java-core` and `brand-dev-java-client-okhttp`\n - Does not have its own logic\n\nThis structure allows replacing the SDK\'s default HTTP client without pulling in unnecessary dependencies.\n\n#### Customized [`OkHttpClient`](https://square.github.io/okhttp/3.x/okhttp/okhttp3/OkHttpClient.html)\n\n> [!TIP]\n> Try the available [network options](#network-options) before replacing the default client.\n\nTo use a customized `OkHttpClient`:\n\n1. Replace your [`brand-dev-java` dependency](#installation) with `brand-dev-java-core`\n2. Copy `brand-dev-java-client-okhttp`\'s [`OkHttpClient`](brand-dev-java-client-okhttp/src/main/kotlin/com/branddev/api/client/okhttp/OkHttpClient.kt) class into your code and customize it\n3. Construct [`BrandDevClientImpl`](brand-dev-java-core/src/main/kotlin/com/branddev/api/client/BrandDevClientImpl.kt) or [`BrandDevClientAsyncImpl`](brand-dev-java-core/src/main/kotlin/com/branddev/api/client/BrandDevClientAsyncImpl.kt), similarly to [`BrandDevOkHttpClient`](brand-dev-java-client-okhttp/src/main/kotlin/com/branddev/api/client/okhttp/BrandDevOkHttpClient.kt) or [`BrandDevOkHttpClientAsync`](brand-dev-java-client-okhttp/src/main/kotlin/com/branddev/api/client/okhttp/BrandDevOkHttpClientAsync.kt), using your customized client\n\n### Completely custom HTTP client\n\nTo use a completely custom HTTP client:\n\n1. Replace your [`brand-dev-java` dependency](#installation) with `brand-dev-java-core`\n2. Write a class that implements the [`HttpClient`](brand-dev-java-core/src/main/kotlin/com/branddev/api/core/http/HttpClient.kt) interface\n3. Construct [`BrandDevClientImpl`](brand-dev-java-core/src/main/kotlin/com/branddev/api/client/BrandDevClientImpl.kt) or [`BrandDevClientAsyncImpl`](brand-dev-java-core/src/main/kotlin/com/branddev/api/client/BrandDevClientAsyncImpl.kt), similarly to [`BrandDevOkHttpClient`](brand-dev-java-client-okhttp/src/main/kotlin/com/branddev/api/client/okhttp/BrandDevOkHttpClient.kt) or [`BrandDevOkHttpClientAsync`](brand-dev-java-client-okhttp/src/main/kotlin/com/branddev/api/client/okhttp/BrandDevOkHttpClientAsync.kt), using your new client class\n\n## Undocumented API functionality\n\nThe SDK is typed for convenient usage of the documented API. However, it also supports working with undocumented or not yet supported parts of the API.\n\n### Parameters\n\nTo set undocumented parameters, call the `putAdditionalHeader`, `putAdditionalQueryParam`, or `putAdditionalBodyProperty` methods on any `Params` class:\n\n```java\nimport com.branddev.api.core.JsonValue;\nimport com.branddev.api.models.brand.BrandRetrieveParams;\n\nBrandRetrieveParams params = BrandRetrieveParams.builder()\n .putAdditionalHeader("Secret-Header", "42")\n .putAdditionalQueryParam("secret_query_param", "42")\n .putAdditionalBodyProperty("secretProperty", JsonValue.from("42"))\n .build();\n```\n\nThese can be accessed on the built object later using the `_additionalHeaders()`, `_additionalQueryParams()`, and `_additionalBodyProperties()` methods.\n\nTo set undocumented parameters on _nested_ headers, query params, or body classes, call the `putAdditionalProperty` method on the nested class:\n\n```java\nimport com.branddev.api.core.JsonValue;\nimport com.branddev.api.models.brand.BrandAiQueryParams;\n\nBrandAiQueryParams params = BrandAiQueryParams.builder()\n .specificPages(BrandAiQueryParams.SpecificPages.builder()\n .putAdditionalProperty("secretProperty", JsonValue.from("42"))\n .build())\n .build();\n```\n\nThese properties can be accessed on the nested built object later using the `_additionalProperties()` method.\n\nTo set a documented parameter or property to an undocumented or not yet supported _value_, pass a [`JsonValue`](brand-dev-java-core/src/main/kotlin/com/branddev/api/core/Values.kt) object to its setter:\n\n```java\nimport com.branddev.api.models.brand.BrandRetrieveParams;\n\nBrandRetrieveParams params = BrandRetrieveParams.builder()\n .domain("REPLACE_ME")\n .build();\n```\n\nThe most straightforward way to create a [`JsonValue`](brand-dev-java-core/src/main/kotlin/com/branddev/api/core/Values.kt) is using its `from(...)` method:\n\n```java\nimport com.branddev.api.core.JsonValue;\nimport java.util.List;\nimport java.util.Map;\n\n// Create primitive JSON values\nJsonValue nullValue = JsonValue.from(null);\nJsonValue booleanValue = JsonValue.from(true);\nJsonValue numberValue = JsonValue.from(42);\nJsonValue stringValue = JsonValue.from("Hello World!");\n\n// Create a JSON array value equivalent to `["Hello", "World"]`\nJsonValue arrayValue = JsonValue.from(List.of(\n "Hello", "World"\n));\n\n// Create a JSON object value equivalent to `{ "a": 1, "b": 2 }`\nJsonValue objectValue = JsonValue.from(Map.of(\n "a", 1,\n "b", 2\n));\n\n// Create an arbitrarily nested JSON equivalent to:\n// {\n// "a": [1, 2],\n// "b": [3, 4]\n// }\nJsonValue complexValue = JsonValue.from(Map.of(\n "a", List.of(\n 1, 2\n ),\n "b", List.of(\n 3, 4\n )\n));\n```\n\nNormally a `Builder` class\'s `build` method will throw [`IllegalStateException`](https://docs.oracle.com/javase/8/docs/api/java/lang/IllegalStateException.html) if any required parameter or property is unset.\n\nTo forcibly omit a required parameter or property, pass [`JsonMissing`](brand-dev-java-core/src/main/kotlin/com/branddev/api/core/Values.kt):\n\n```java\nimport com.branddev.api.core.JsonMissing;\nimport com.branddev.api.models.brand.BrandRetrieveParams;\n\nBrandRetrieveParams params = BrandRetrieveParams.builder()\n .domain(JsonMissing.of())\n .build();\n```\n\n### Response properties\n\nTo access undocumented response properties, call the `_additionalProperties()` method:\n\n```java\nimport com.branddev.api.core.JsonValue;\nimport java.util.Map;\n\nMap additionalProperties = client.brand().retrieve(params)._additionalProperties();\nJsonValue secretPropertyValue = additionalProperties.get("secretProperty");\n\nString result = secretPropertyValue.accept(new JsonValue.Visitor<>() {\n @Override\n public String visitNull() {\n return "It\'s null!";\n }\n\n @Override\n public String visitBoolean(boolean value) {\n return "It\'s a boolean!";\n }\n\n @Override\n public String visitNumber(Number value) {\n return "It\'s a number!";\n }\n\n // Other methods include `visitMissing`, `visitString`, `visitArray`, and `visitObject`\n // The default implementation of each unimplemented method delegates to `visitDefault`, which throws by default, but can also be overridden\n});\n```\n\nTo access a property\'s raw JSON value, which may be undocumented, call its `_` prefixed method:\n\n```java\nimport com.branddev.api.core.JsonField;\nimport java.util.Optional;\n\nJsonField field = client.brand().retrieve(params)._field();\n\nif (field.isMissing()) {\n // The property is absent from the JSON response\n} else if (field.isNull()) {\n // The property was set to literal null\n} else {\n // Check if value was provided as a string\n // Other methods include `asNumber()`, `asBoolean()`, etc.\n Optional jsonString = field.asString();\n\n // Try to deserialize into a custom type\n MyClass myObject = field.asUnknown().orElseThrow().convert(MyClass.class);\n}\n```\n\n### Response validation\n\nIn rare cases, the API may return a response that doesn\'t match the expected type. For example, the SDK may expect a property to contain a `String`, but the API could return something else.\n\nBy default, the SDK will not throw an exception in this case. It will throw [`BrandDevInvalidDataException`](brand-dev-java-core/src/main/kotlin/com/branddev/api/errors/BrandDevInvalidDataException.kt) only if you directly access the property.\n\nValidating the response is _not_ forwards compatible with new types from the API for existing fields.\n\nIf you would still prefer to check that the response is completely well-typed upfront, then either call `validate()`:\n\n```java\nimport com.branddev.api.models.brand.BrandRetrieveResponse;\n\nBrandRetrieveResponse brand = client.brand().retrieve(params).validate();\n```\n\nOr configure the method call to validate the response using the `responseValidation` method:\n\n```java\nimport com.branddev.api.models.brand.BrandRetrieveResponse;\n\nBrandRetrieveResponse brand = client.brand().retrieve(\n params, RequestOptions.builder().responseValidation(true).build()\n);\n```\n\nOr configure the default for all method calls at the client level:\n\n```java\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\n\nBrandDevClient client = BrandDevOkHttpClient.builder()\n .fromEnv()\n .responseValidation(true)\n .build();\n```\n\n## FAQ\n\n### Why don\'t you use plain `enum` classes?\n\nJava `enum` classes are not trivially [forwards compatible](https://www.stainless.com/blog/making-java-enums-forwards-compatible). Using them in the SDK could cause runtime exceptions if the API is updated to respond with a new enum value.\n\n### Why do you represent fields using `JsonField` instead of just plain `T`?\n\nUsing `JsonField` enables a few features:\n\n- Allowing usage of [undocumented API functionality](#undocumented-api-functionality)\n- Lazily [validating the API response against the expected shape](#response-validation)\n- Representing absent vs explicitly null values\n\n### Why don\'t you use [`data` classes](https://kotlinlang.org/docs/data-classes.html)?\n\nIt is not [backwards compatible to add new fields to a data class](https://kotlinlang.org/docs/api-guidelines-backward-compatibility.html#avoid-using-data-classes-in-your-api) and we don\'t want to introduce a breaking change every time we add a field to a class.\n\n### Why don\'t you use checked exceptions?\n\nChecked exceptions are widely considered a mistake in the Java programming language. In fact, they were omitted from Kotlin for this reason.\n\nChecked exceptions:\n\n- Are verbose to handle\n- Encourage error handling at the wrong level of abstraction, where nothing can be done about the error\n- Are tedious to propagate due to the [function coloring problem](https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function)\n- Don\'t play well with lambdas (also due to the function coloring problem)\n\n## Semantic versioning\n\nThis package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:\n\n1. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_\n2. Changes that we do not expect to impact the vast majority of users in practice.\n\nWe take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.\n\nWe are keen for your feedback; please open an [issue](https://www.github.com/context-dot-dev/deprecated-brand-java-sdk/issues) with questions, bugs, or suggestions.\n', }, { - language: 'typescript', + language: 'python', content: - "# Brand Dev TypeScript API Library\n\n[![NPM version](https://img.shields.io/npm/v/brand.dev.svg?label=npm%20(stable))](https://npmjs.org/package/brand.dev) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/brand.dev)\n\nThis library provides convenient access to the Brand Dev REST API from server-side TypeScript or JavaScript.\n\n\n\nThe REST API documentation can be found on [docs.context.dev](https://docs.context.dev/). The full API of this library can be found in [api.md](api.md).\n\nIt is generated with [Stainless](https://www.stainless.com/).\n\n## MCP Server\n\nUse the Brand Dev MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application.\n\n[![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=brand.dev-mcp&config=eyJuYW1lIjoiYnJhbmQuZGV2LW1jcCIsInRyYW5zcG9ydCI6Imh0dHAiLCJ1cmwiOiJodHRwczovL2JyYW5kLWRldi5zdGxtY3AuY29tIiwiaGVhZGVycyI6eyJ4LWJyYW5kLWRldi1hcGkta2V5IjoiTXkgQVBJIEtleSJ9fQ)\n[![Install in VS Code](https://img.shields.io/badge/_-Add_to_VS_Code-blue?style=for-the-badge&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCA0MCA0MCI+PHBhdGggZmlsbD0iI0VFRSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMzAuMjM1IDM5Ljg4NGEyLjQ5MSAyLjQ5MSAwIDAgMS0xLjc4MS0uNzNMMTIuNyAyNC43OGwtMy40NiAyLjYyNC0zLjQwNiAyLjU4MmExLjY2NSAxLjY2NSAwIDAgMS0xLjA4Mi4zMzggMS42NjQgMS42NjQgMCAwIDEtMS4wNDYtLjQzMWwtMi4yLTJhMS42NjYgMS42NjYgMCAwIDEgMC0yLjQ2M0w3LjQ1OCAyMCA0LjY3IDE3LjQ1MyAxLjUwNyAxNC41N2ExLjY2NSAxLjY2NSAwIDAgMSAwLTIuNDYzbDIuMi0yYTEuNjY1IDEuNjY1IDAgMCAxIDIuMTMtLjA5N2w2Ljg2MyA1LjIwOUwyOC40NTIuODQ0YTIuNDg4IDIuNDg4IDAgMCAxIDEuODQxLS43MjljLjM1MS4wMDkuNjk5LjA5MSAxLjAxOS4yNDVsOC4yMzYgMy45NjFhMi41IDIuNSAwIDAgMSAxLjQxNSAyLjI1M3YuMDk5LS4wNDVWMzMuMzd2LS4wNDUuMDk1YTIuNTAxIDIuNTAxIDAgMCAxLTEuNDE2IDIuMjU3bC04LjIzNSAzLjk2MWEyLjQ5MiAyLjQ5MiAwIDAgMS0xLjA3Ny4yNDZabS43MTYtMjguOTQ3LTExLjk0OCA5LjA2MiAxMS45NTIgOS4wNjUtLjAwNC0xOC4xMjdaIi8+PC9zdmc+)](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22brand.dev-mcp%22%2C%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fbrand-dev.stlmcp.com%22%2C%22headers%22%3A%7B%22x-brand-dev-api-key%22%3A%22My%20API%20Key%22%7D%7D)\n\n> Note: You may need to set environment variables in your MCP client.\n\n## Installation\n\n```sh\nnpm install brand.dev\n```\n\n\n\n## Usage\n\nThe full API of this library can be found in [api.md](api.md).\n\n\n```js\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst brand = await client.brand.retrieve({ domain: 'REPLACE_ME' });\n\nconsole.log(brand.brand);\n```\n\n\n\n### Request & Response types\n\nThis library includes TypeScript definitions for all request params and response fields. You may import and use them like so:\n\n\n```ts\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst params: BrandDev.BrandRetrieveParams = { domain: 'REPLACE_ME' };\nconst brand: BrandDev.BrandRetrieveResponse = await client.brand.retrieve(params);\n```\n\nDocumentation for each method, request param, and response field are available in docstrings and will appear on hover in most modern editors.\n\n\n\n\n\n## Handling errors\n\nWhen the library is unable to connect to the API,\nor if the API returns a non-success status code (i.e., 4xx or 5xx response),\na subclass of `APIError` will be thrown:\n\n\n```ts\nconst brand = await client.brand.retrieve({ domain: 'REPLACE_ME' }).catch(async (err) => {\n if (err instanceof BrandDev.APIError) {\n console.log(err.status); // 400\n console.log(err.name); // BadRequestError\n console.log(err.headers); // {server: 'nginx', ...}\n } else {\n throw err;\n }\n});\n```\n\nError codes are as follows:\n\n| Status Code | Error Type |\n| ----------- | -------------------------- |\n| 400 | `BadRequestError` |\n| 401 | `AuthenticationError` |\n| 403 | `PermissionDeniedError` |\n| 404 | `NotFoundError` |\n| 422 | `UnprocessableEntityError` |\n| 429 | `RateLimitError` |\n| >=500 | `InternalServerError` |\n| N/A | `APIConnectionError` |\n\n### Retries\n\nCertain errors will be automatically retried 2 times by default, with a short exponential backoff.\nConnection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict,\n429 Rate Limit, and >=500 Internal errors will all be retried by default.\n\nYou can use the `maxRetries` option to configure or disable this:\n\n\n```js\n// Configure the default for all requests:\nconst client = new BrandDev({\n maxRetries: 0, // default is 2\n});\n\n// Or, configure per-request:\nawait client.brand.retrieve({ domain: 'REPLACE_ME' }, {\n maxRetries: 5,\n});\n```\n\n### Timeouts\n\nRequests time out after 1 minute by default. You can configure this with a `timeout` option:\n\n\n```ts\n// Configure the default for all requests:\nconst client = new BrandDev({\n timeout: 20 * 1000, // 20 seconds (default is 1 minute)\n});\n\n// Override per-request:\nawait client.brand.retrieve({ domain: 'REPLACE_ME' }, {\n timeout: 5 * 1000,\n});\n```\n\nOn timeout, an `APIConnectionTimeoutError` is thrown.\n\nNote that requests which time out will be [retried twice by default](#retries).\n\n\n\n\n\n## Advanced Usage\n\n### Accessing raw Response data (e.g., headers)\n\nThe \"raw\" `Response` returned by `fetch()` can be accessed through the `.asResponse()` method on the `APIPromise` type that all methods return.\nThis method returns as soon as the headers for a successful response are received and does not consume the response body, so you are free to write custom parsing or streaming logic.\n\nYou can also use the `.withResponse()` method to get the raw `Response` along with the parsed data.\nUnlike `.asResponse()` this method consumes the body, returning once it is parsed.\n\n\n```ts\nconst client = new BrandDev();\n\nconst response = await client.brand.retrieve({ domain: 'REPLACE_ME' }).asResponse();\nconsole.log(response.headers.get('X-My-Header'));\nconsole.log(response.statusText); // access the underlying Response object\n\nconst { data: brand, response: raw } = await client.brand\n .retrieve({ domain: 'REPLACE_ME' })\n .withResponse();\nconsole.log(raw.headers.get('X-My-Header'));\nconsole.log(brand.brand);\n```\n\n### Logging\n\n> [!IMPORTANT]\n> All log messages are intended for debugging only. The format and content of log messages\n> may change between releases.\n\n#### Log levels\n\nThe log level can be configured in two ways:\n\n1. Via the `BRAND_DEV_LOG` environment variable\n2. Using the `logLevel` client option (overrides the environment variable if set)\n\n```ts\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n logLevel: 'debug', // Show all log messages\n});\n```\n\nAvailable log levels, from most to least verbose:\n\n- `'debug'` - Show debug messages, info, warnings, and errors\n- `'info'` - Show info messages, warnings, and errors\n- `'warn'` - Show warnings and errors (default)\n- `'error'` - Show only errors\n- `'off'` - Disable all logging\n\nAt the `'debug'` level, all HTTP requests and responses are logged, including headers and bodies.\nSome authentication-related headers are redacted, but sensitive data in request and response bodies\nmay still be visible.\n\n#### Custom logger\n\nBy default, this library logs to `globalThis.console`. You can also provide a custom logger.\nMost logging libraries are supported, including [pino](https://www.npmjs.com/package/pino), [winston](https://www.npmjs.com/package/winston), [bunyan](https://www.npmjs.com/package/bunyan), [consola](https://www.npmjs.com/package/consola), [signale](https://www.npmjs.com/package/signale), and [@std/log](https://jsr.io/@std/log). If your logger doesn't work, please open an issue.\n\nWhen providing a custom logger, the `logLevel` option still controls which messages are emitted, messages\nbelow the configured level will not be sent to your logger.\n\n```ts\nimport BrandDev from 'brand.dev';\nimport pino from 'pino';\n\nconst logger = pino();\n\nconst client = new BrandDev({\n logger: logger.child({ name: 'BrandDev' }),\n logLevel: 'debug', // Send all messages to pino, allowing it to filter\n});\n```\n\n### Making custom/undocumented requests\n\nThis library is typed for convenient access to the documented API. If you need to access undocumented\nendpoints, params, or response properties, the library can still be used.\n\n#### Undocumented endpoints\n\nTo make requests to undocumented endpoints, you can use `client.get`, `client.post`, and other HTTP verbs.\nOptions on the client, such as retries, will be respected when making these requests.\n\n```ts\nawait client.post('/some/path', {\n body: { some_prop: 'foo' },\n query: { some_query_arg: 'bar' },\n});\n```\n\n#### Undocumented request params\n\nTo make requests using undocumented parameters, you may use `// @ts-expect-error` on the undocumented\nparameter. This library doesn't validate at runtime that the request matches the type, so any extra values you\nsend will be sent as-is.\n\n```ts\nclient.brand.retrieve({\n // ...\n // @ts-expect-error baz is not yet public\n baz: 'undocumented option',\n});\n```\n\nFor requests with the `GET` verb, any extra params will be in the query, all other requests will send the\nextra param in the body.\n\nIf you want to explicitly send an extra argument, you can do so with the `query`, `body`, and `headers` request\noptions.\n\n#### Undocumented response properties\n\nTo access undocumented response properties, you may access the response object with `// @ts-expect-error` on\nthe response object, or cast the response object to the requisite type. Like the request params, we do not\nvalidate or strip extra properties from the response from the API.\n\n### Customizing the fetch client\n\nBy default, this library expects a global `fetch` function is defined.\n\nIf you want to use a different `fetch` function, you can either polyfill the global:\n\n```ts\nimport fetch from 'my-fetch';\n\nglobalThis.fetch = fetch;\n```\n\nOr pass it to the client:\n\n```ts\nimport BrandDev from 'brand.dev';\nimport fetch from 'my-fetch';\n\nconst client = new BrandDev({ fetch });\n```\n\n### Fetch options\n\nIf you want to set custom `fetch` options without overriding the `fetch` function, you can provide a `fetchOptions` object when instantiating the client or making a request. (Request-specific options override client options.)\n\n```ts\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n fetchOptions: {\n // `RequestInit` options\n },\n});\n```\n\n#### Configuring proxies\n\nTo modify proxy behavior, you can provide custom `fetchOptions` that add runtime-specific proxy\noptions to requests:\n\n **Node** [[docs](https://github.com/nodejs/undici/blob/main/docs/docs/api/ProxyAgent.md#example---proxyagent-with-fetch)]\n\n```ts\nimport BrandDev from 'brand.dev';\nimport * as undici from 'undici';\n\nconst proxyAgent = new undici.ProxyAgent('http://localhost:8888');\nconst client = new BrandDev({\n fetchOptions: {\n dispatcher: proxyAgent,\n },\n});\n```\n\n **Bun** [[docs](https://bun.sh/guides/http/proxy)]\n\n```ts\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n fetchOptions: {\n proxy: 'http://localhost:8888',\n },\n});\n```\n\n **Deno** [[docs](https://docs.deno.com/api/deno/~/Deno.createHttpClient)]\n\n```ts\nimport BrandDev from 'npm:brand.dev';\n\nconst httpClient = Deno.createHttpClient({ proxy: { url: 'http://localhost:8888' } });\nconst client = new BrandDev({\n fetchOptions: {\n client: httpClient,\n },\n});\n```\n\n## Frequently Asked Questions\n\n## Semantic versioning\n\nThis package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:\n\n1. Changes that only affect static types, without breaking runtime behavior.\n2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_\n3. Changes that we do not expect to impact the vast majority of users in practice.\n\nWe take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.\n\nWe are keen for your feedback; please open an [issue](https://www.github.com/context-dot-dev/deprecated-brand-typescript-sdk/issues) with questions, bugs, or suggestions.\n\n## Requirements\n\nTypeScript >= 4.9 is supported.\n\nThe following runtimes are supported:\n\n- Web browsers (Up-to-date Chrome, Firefox, Safari, Edge, and more)\n- Node.js 20 LTS or later ([non-EOL](https://endoflife.date/nodejs)) versions.\n- Deno v1.28.0 or higher.\n- Bun 1.0 or later.\n- Cloudflare Workers.\n- Vercel Edge Runtime.\n- Jest 28 or greater with the `\"node\"` environment (`\"jsdom\"` is not supported at this time).\n- Nitro v2.6 or greater.\n\nNote that React Native is not supported at this time.\n\nIf you are interested in other runtime environments, please open or upvote an issue on GitHub.\n\n## Contributing\n\nSee [the contributing documentation](./CONTRIBUTING.md).\n", + '# Brand Dev Python API library\n\n\n[![PyPI version](https://img.shields.io/pypi/v/brand.dev.svg?label=pypi%20(stable))](https://pypi.org/project/brand.dev/)\n\nThe Brand Dev Python library provides convenient access to the Brand Dev REST API from any Python 3.9+\napplication. The library includes type definitions for all request params and response fields,\nand offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx).\n\n\n\nIt is generated with [Stainless](https://www.stainless.com/).\n\n## MCP Server\n\nUse the Brand Dev MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application.\n\n[![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=brand.dev-mcp&config=eyJuYW1lIjoiYnJhbmQuZGV2LW1jcCIsInRyYW5zcG9ydCI6Imh0dHAiLCJ1cmwiOiJodHRwczovL2JyYW5kLWRldi5zdGxtY3AuY29tIiwiaGVhZGVycyI6eyJ4LWJyYW5kLWRldi1hcGkta2V5IjoiTXkgQVBJIEtleSJ9fQ)\n[![Install in VS Code](https://img.shields.io/badge/_-Add_to_VS_Code-blue?style=for-the-badge&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCA0MCA0MCI+PHBhdGggZmlsbD0iI0VFRSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMzAuMjM1IDM5Ljg4NGEyLjQ5MSAyLjQ5MSAwIDAgMS0xLjc4MS0uNzNMMTIuNyAyNC43OGwtMy40NiAyLjYyNC0zLjQwNiAyLjU4MmExLjY2NSAxLjY2NSAwIDAgMS0xLjA4Mi4zMzggMS42NjQgMS42NjQgMCAwIDEtMS4wNDYtLjQzMWwtMi4yLTJhMS42NjYgMS42NjYgMCAwIDEgMC0yLjQ2M0w3LjQ1OCAyMCA0LjY3IDE3LjQ1MyAxLjUwNyAxNC41N2ExLjY2NSAxLjY2NSAwIDAgMSAwLTIuNDYzbDIuMi0yYTEuNjY1IDEuNjY1IDAgMCAxIDIuMTMtLjA5N2w2Ljg2MyA1LjIwOUwyOC40NTIuODQ0YTIuNDg4IDIuNDg4IDAgMCAxIDEuODQxLS43MjljLjM1MS4wMDkuNjk5LjA5MSAxLjAxOS4yNDVsOC4yMzYgMy45NjFhMi41IDIuNSAwIDAgMSAxLjQxNSAyLjI1M3YuMDk5LS4wNDVWMzMuMzd2LS4wNDUuMDk1YTIuNTAxIDIuNTAxIDAgMCAxLTEuNDE2IDIuMjU3bC04LjIzNSAzLjk2MWEyLjQ5MiAyLjQ5MiAwIDAgMS0xLjA3Ny4yNDZabS43MTYtMjguOTQ3LTExLjk0OCA5LjA2MiAxMS45NTIgOS4wNjUtLjAwNC0xOC4xMjdaIi8+PC9zdmc+)](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22brand.dev-mcp%22%2C%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fbrand-dev.stlmcp.com%22%2C%22headers%22%3A%7B%22x-brand-dev-api-key%22%3A%22My%20API%20Key%22%7D%7D)\n\n> Note: You may need to set environment variables in your MCP client.\n\n## Documentation\n\nThe REST API documentation can be found on [docs.context.dev](https://docs.context.dev/). The full API of this library can be found in [api.md](api.md).\n\n## Installation\n\n```sh\n# install from PyPI\npip install brand.dev\n```\n\n## Usage\n\nThe full API of this library can be found in [api.md](api.md).\n\n```python\nimport os\nfrom brand.dev import BrandDev\n\nclient = BrandDev(\n api_key=os.environ.get("BRAND_DEV_API_KEY"), # This is the default and can be omitted\n)\n\nbrand = client.brand.retrieve(\n domain="REPLACE_ME",\n)\nprint(brand.brand)\n```\n\nWhile you can provide an `api_key` keyword argument,\nwe recommend using [python-dotenv](https://pypi.org/project/python-dotenv/)\nto add `BRAND_DEV_API_KEY="My API Key"` to your `.env` file\nso that your API Key is not stored in source control.\n\n## Async usage\n\nSimply import `AsyncBrandDev` instead of `BrandDev` and use `await` with each API call:\n\n```python\nimport os\nimport asyncio\nfrom brand.dev import AsyncBrandDev\n\nclient = AsyncBrandDev(\n api_key=os.environ.get("BRAND_DEV_API_KEY"), # This is the default and can be omitted\n)\n\nasync def main() -> None:\n brand = await client.brand.retrieve(\n domain="REPLACE_ME",\n )\n print(brand.brand)\n\nasyncio.run(main())\n```\n\nFunctionality between the synchronous and asynchronous clients is otherwise identical.\n\n### With aiohttp\n\nBy default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend.\n\nYou can enable this by installing `aiohttp`:\n\n```sh\n# install from PyPI\npip install brand.dev[aiohttp]\n```\n\nThen you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`:\n\n```python\nimport os\nimport asyncio\nfrom brand.dev import DefaultAioHttpClient\nfrom brand.dev import AsyncBrandDev\n\nasync def main() -> None:\n async with AsyncBrandDev(\n api_key=os.environ.get("BRAND_DEV_API_KEY"), # This is the default and can be omitted\n http_client=DefaultAioHttpClient(),\n) as client:\n brand = await client.brand.retrieve(\n domain="REPLACE_ME",\n )\n print(brand.brand)\n\nasyncio.run(main())\n```\n\n\n\n## Using types\n\nNested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like:\n\n- Serializing back into JSON, `model.to_json()`\n- Converting to a dictionary, `model.to_dict()`\n\nTyped requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`.\n\n\n\n## Nested params\n\nNested parameters are dictionaries, typed using `TypedDict`, for example:\n\n```python\nfrom brand.dev import BrandDev\n\nclient = BrandDev()\n\nresponse = client.brand.ai_query(\n data_to_extract=[{\n "datapoint_description": "datapoint_description",\n "datapoint_example": "datapoint_example",\n "datapoint_name": "datapoint_name",\n "datapoint_type": "text",\n }],\n domain="domain",\n specific_pages={},\n)\nprint(response.specific_pages)\n```\n\n\n\n## Handling errors\n\nWhen the library is unable to connect to the API (for example, due to network connection problems or a timeout), a subclass of `brand.dev.APIConnectionError` is raised.\n\nWhen the API returns a non-success status code (that is, 4xx or 5xx\nresponse), a subclass of `brand.dev.APIStatusError` is raised, containing `status_code` and `response` properties.\n\nAll errors inherit from `brand.dev.APIError`.\n\n```python\nimport brand.dev\nfrom brand.dev import BrandDev\n\nclient = BrandDev()\n\ntry:\n client.brand.retrieve(\n domain="REPLACE_ME",\n )\nexcept brand.dev.APIConnectionError as e:\n print("The server could not be reached")\n print(e.__cause__) # an underlying Exception, likely raised within httpx.\nexcept brand.dev.RateLimitError as e:\n print("A 429 status code was received; we should back off a bit.")\nexcept brand.dev.APIStatusError as e:\n print("Another non-200-range status code was received")\n print(e.status_code)\n print(e.response)\n```\n\nError codes are as follows:\n\n| Status Code | Error Type |\n| ----------- | -------------------------- |\n| 400 | `BadRequestError` |\n| 401 | `AuthenticationError` |\n| 403 | `PermissionDeniedError` |\n| 404 | `NotFoundError` |\n| 422 | `UnprocessableEntityError` |\n| 429 | `RateLimitError` |\n| >=500 | `InternalServerError` |\n| N/A | `APIConnectionError` |\n\n### Retries\n\nCertain errors are automatically retried 2 times by default, with a short exponential backoff.\nConnection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict,\n429 Rate Limit, and >=500 Internal errors are all retried by default.\n\nYou can use the `max_retries` option to configure or disable retry settings:\n\n```python\nfrom brand.dev import BrandDev\n\n# Configure the default for all requests:\nclient = BrandDev(\n # default is 2\n max_retries=0,\n)\n\n# Or, configure per-request:\nclient.with_options(max_retries = 5).brand.retrieve(\n domain="REPLACE_ME",\n)\n```\n\n### Timeouts\n\nBy default requests time out after 1 minute. You can configure this with a `timeout` option,\nwhich accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/timeouts/#fine-tuning-the-configuration) object:\n\n```python\nfrom brand.dev import BrandDev\n\n# Configure the default for all requests:\nclient = BrandDev(\n # 20 seconds (default is 1 minute)\n timeout=20.0,\n)\n\n# More granular control:\nclient = BrandDev(\n timeout=httpx.Timeout(60.0, read=5.0, write=10.0, connect=2.0),\n)\n\n# Override per-request:\nclient.with_options(timeout = 5.0).brand.retrieve(\n domain="REPLACE_ME",\n)\n```\n\nOn timeout, an `APITimeoutError` is thrown.\n\nNote that requests that time out are [retried twice by default](#retries).\n\n\n\n## Advanced\n\n### Logging\n\nWe use the standard library [`logging`](https://docs.python.org/3/library/logging.html) module.\n\nYou can enable logging by setting the environment variable `BRAND_DEV_LOG` to `info`.\n\n```shell\n$ export BRAND_DEV_LOG=info\n```\n\nOr to `debug` for more verbose logging.\n\n### How to tell whether `None` means `null` or missing\n\nIn an API response, a field may be explicitly `null`, or missing entirely; in either case, its value is `None` in this library. You can differentiate the two cases with `.model_fields_set`:\n\n```py\nif response.my_field is None:\n if \'my_field\' not in response.model_fields_set:\n print(\'Got json like {}, without a "my_field" key present at all.\')\n else:\n print(\'Got json like {"my_field": null}.\')\n```\n\n### Accessing raw response data (e.g. headers)\n\nThe "raw" Response object can be accessed by prefixing `.with_raw_response.` to any HTTP method call, e.g.,\n\n```py\nfrom brand.dev import BrandDev\n\nclient = BrandDev()\nresponse = client.brand.with_raw_response.retrieve(\n domain="REPLACE_ME",\n)\nprint(response.headers.get(\'X-My-Header\'))\n\nbrand = response.parse() # get the object that `brand.retrieve()` would have returned\nprint(brand.brand)\n```\n\nThese methods return an [`APIResponse`](https://github.com/context-dot-dev/deprecated-brand-python-sdk/tree/main/src/brand/dev/_response.py) object.\n\nThe async client returns an [`AsyncAPIResponse`](https://github.com/context-dot-dev/deprecated-brand-python-sdk/tree/main/src/brand/dev/_response.py) with the same structure, the only difference being `await`able methods for reading the response content.\n\n#### `.with_streaming_response`\n\nThe above interface eagerly reads the full response body when you make the request, which may not always be what you want.\n\nTo stream the response body, use `.with_streaming_response` instead, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()` or `.parse()`. In the async client, these are async methods.\n\n```python\nwith client.brand.with_streaming_response.retrieve(\n domain="REPLACE_ME",\n) as response :\n print(response.headers.get(\'X-My-Header\'))\n\n for line in response.iter_lines():\n print(line)\n```\n\nThe context manager is required so that the response will reliably be closed.\n\n### Making custom/undocumented requests\n\nThis library is typed for convenient access to the documented API.\n\nIf you need to access undocumented endpoints, params, or response properties, the library can still be used.\n\n#### Undocumented endpoints\n\nTo make requests to undocumented endpoints, you can make requests using `client.get`, `client.post`, and other\nhttp verbs. Options on the client will be respected (such as retries) when making this request.\n\n```py\nimport httpx\n\nresponse = client.post(\n "/foo",\n cast_to=httpx.Response,\n body={"my_param": True},\n)\n\nprint(response.headers.get("x-foo"))\n```\n\n#### Undocumented request params\n\nIf you want to explicitly send an extra param, you can do so with the `extra_query`, `extra_body`, and `extra_headers` request\noptions.\n\n#### Undocumented response properties\n\nTo access undocumented response properties, you can access the extra fields like `response.unknown_prop`. You\ncan also get all the extra fields on the Pydantic model as a dict with\n[`response.model_extra`](https://docs.pydantic.dev/latest/api/base_model/#pydantic.BaseModel.model_extra).\n\n### Configuring the HTTP client\n\nYou can directly override the [httpx client](https://www.python-httpx.org/api/#client) to customize it for your use case, including:\n\n- Support for [proxies](https://www.python-httpx.org/advanced/proxies/)\n- Custom [transports](https://www.python-httpx.org/advanced/transports/)\n- Additional [advanced](https://www.python-httpx.org/advanced/clients/) functionality\n\n```python\nimport httpx\nfrom brand.dev import BrandDev, DefaultHttpxClient\n\nclient = BrandDev(\n # Or use the `BRAND_DEV_BASE_URL` env var\n base_url="http://my.test.server.example.com:8083",\n http_client=DefaultHttpxClient(proxy="http://my.test.proxy.example.com", transport=httpx.HTTPTransport(local_address="0.0.0.0")),\n)\n```\n\nYou can also customize the client on a per-request basis by using `with_options()`:\n\n```python\nclient.with_options(http_client=DefaultHttpxClient(...))\n```\n\n### Managing HTTP resources\n\nBy default the library closes underlying HTTP connections whenever the client is [garbage collected](https://docs.python.org/3/reference/datamodel.html#object.__del__). You can manually close the client using the `.close()` method if desired, or with a context manager that closes when exiting.\n\n```py\nfrom brand.dev import BrandDev\n\nwith BrandDev() as client:\n # make requests here\n ...\n\n# HTTP client is now closed\n```\n\n## Versioning\n\nThis package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:\n\n1. Changes that only affect static types, without breaking runtime behavior.\n2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_\n3. Changes that we do not expect to impact the vast majority of users in practice.\n\nWe take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.\n\nWe are keen for your feedback; please open an [issue](https://www.github.com/context-dot-dev/deprecated-brand-python-sdk/issues) with questions, bugs, or suggestions.\n\n### Determining the installed version\n\nIf you\'ve upgraded to the latest version but aren\'t seeing any new features you were expecting then your python environment is likely still using an older version.\n\nYou can determine the version that is being used at runtime with:\n\n```py\nimport brand.dev\nprint(brand.dev.__version__)\n```\n\n## Requirements\n\nPython 3.9 or higher.\n\n## Contributing\n\nSee [the contributing documentation](./CONTRIBUTING.md).\n', }, { language: 'ruby', content: - '# Brand Dev Ruby API library\n\nThe Brand Dev Ruby library provides convenient access to the Brand Dev REST API from any Ruby 3.2.0+ application. It ships with comprehensive types & docstrings in Yard, RBS, and RBI – [see below](https://github.com/context-dot-dev/deprecated-brand-ruby-sdk#Sorbet) for usage with Sorbet. The standard library\'s `net/http` is used as the HTTP transport, with connection pooling via the `connection_pool` gem.\n\n\n\nIt is generated with [Stainless](https://www.stainless.com/).\n\n## MCP Server\n\nUse the Brand Dev MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application.\n\n[![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=brand.dev-mcp&config=eyJuYW1lIjoiYnJhbmQuZGV2LW1jcCIsInRyYW5zcG9ydCI6Imh0dHAiLCJ1cmwiOiJodHRwczovL2JyYW5kLWRldi5zdGxtY3AuY29tIiwiaGVhZGVycyI6eyJ4LWJyYW5kLWRldi1hcGkta2V5IjoiTXkgQVBJIEtleSJ9fQ)\n[![Install in VS Code](https://img.shields.io/badge/_-Add_to_VS_Code-blue?style=for-the-badge&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCA0MCA0MCI+PHBhdGggZmlsbD0iI0VFRSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMzAuMjM1IDM5Ljg4NGEyLjQ5MSAyLjQ5MSAwIDAgMS0xLjc4MS0uNzNMMTIuNyAyNC43OGwtMy40NiAyLjYyNC0zLjQwNiAyLjU4MmExLjY2NSAxLjY2NSAwIDAgMS0xLjA4Mi4zMzggMS42NjQgMS42NjQgMCAwIDEtMS4wNDYtLjQzMWwtMi4yLTJhMS42NjYgMS42NjYgMCAwIDEgMC0yLjQ2M0w3LjQ1OCAyMCA0LjY3IDE3LjQ1MyAxLjUwNyAxNC41N2ExLjY2NSAxLjY2NSAwIDAgMSAwLTIuNDYzbDIuMi0yYTEuNjY1IDEuNjY1IDAgMCAxIDIuMTMtLjA5N2w2Ljg2MyA1LjIwOUwyOC40NTIuODQ0YTIuNDg4IDIuNDg4IDAgMCAxIDEuODQxLS43MjljLjM1MS4wMDkuNjk5LjA5MSAxLjAxOS4yNDVsOC4yMzYgMy45NjFhMi41IDIuNSAwIDAgMSAxLjQxNSAyLjI1M3YuMDk5LS4wNDVWMzMuMzd2LS4wNDUuMDk1YTIuNTAxIDIuNTAxIDAgMCAxLTEuNDE2IDIuMjU3bC04LjIzNSAzLjk2MWEyLjQ5MiAyLjQ5MiAwIDAgMS0xLjA3Ny4yNDZabS43MTYtMjguOTQ3LTExLjk0OCA5LjA2MiAxMS45NTIgOS4wNjUtLjAwNC0xOC4xMjdaIi8+PC9zdmc+)](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22brand.dev-mcp%22%2C%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fbrand-dev.stlmcp.com%22%2C%22headers%22%3A%7B%22x-brand-dev-api-key%22%3A%22My%20API%20Key%22%7D%7D)\n\n> Note: You may need to set environment variables in your MCP client.\n\n## Documentation\n\nDocumentation for releases of this gem can be found [on RubyDoc](https://gemdocs.org/gems/brand.dev).\n\nThe REST API documentation can be found on [docs.context.dev](https://docs.context.dev/).\n\n## Installation\n\nTo use this gem, install via Bundler by adding the following to your application\'s `Gemfile`:\n\n\n\n```ruby\ngem "brand.dev", "~> 0.0.1"\n```\n\n\n\n## Usage\n\n```ruby\nrequire "bundler/setup"\nrequire "brand_dev"\n\nbrand_dev = BrandDev::Client.new(\n api_key: ENV["BRAND_DEV_API_KEY"] # This is the default and can be omitted\n)\n\nbrand = brand_dev.brand.retrieve(domain: "REPLACE_ME")\n\nputs(brand.brand)\n```\n\n\n\n\n\n\n\n### Handling errors\n\nWhen the library is unable to connect to the API, or if the API returns a non-success status code (i.e., 4xx or 5xx response), a subclass of `BrandDev::Errors::APIError` will be thrown:\n\n```ruby\nbegin\n brand = brand_dev.brand.retrieve(domain: "REPLACE_ME")\nrescue BrandDev::Errors::APIConnectionError => e\n puts("The server could not be reached")\n puts(e.cause) # an underlying Exception, likely raised within `net/http`\nrescue BrandDev::Errors::RateLimitError => e\n puts("A 429 status code was received; we should back off a bit.")\nrescue BrandDev::Errors::APIStatusError => e\n puts("Another non-200-range status code was received")\n puts(e.status)\nend\n```\n\nError codes are as follows:\n\n| Cause | Error Type |\n| ---------------- | -------------------------- |\n| HTTP 400 | `BadRequestError` |\n| HTTP 401 | `AuthenticationError` |\n| HTTP 403 | `PermissionDeniedError` |\n| HTTP 404 | `NotFoundError` |\n| HTTP 409 | `ConflictError` |\n| HTTP 422 | `UnprocessableEntityError` |\n| HTTP 429 | `RateLimitError` |\n| HTTP >= 500 | `InternalServerError` |\n| Other HTTP error | `APIStatusError` |\n| Timeout | `APITimeoutError` |\n| Network error | `APIConnectionError` |\n\n### Retries\n\nCertain errors will be automatically retried 2 times by default, with a short exponential backoff.\n\nConnection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict, 429 Rate Limit, >=500 Internal errors, and timeouts will all be retried by default.\n\nYou can use the `max_retries` option to configure or disable this:\n\n```ruby\n# Configure the default for all requests:\nbrand_dev = BrandDev::Client.new(\n max_retries: 0 # default is 2\n)\n\n# Or, configure per-request:\nbrand_dev.brand.retrieve(domain: "REPLACE_ME", request_options: {max_retries: 5})\n```\n\n### Timeouts\n\nBy default, requests will time out after 60 seconds. You can use the timeout option to configure or disable this:\n\n```ruby\n# Configure the default for all requests:\nbrand_dev = BrandDev::Client.new(\n timeout: nil # default is 60\n)\n\n# Or, configure per-request:\nbrand_dev.brand.retrieve(domain: "REPLACE_ME", request_options: {timeout: 5})\n```\n\nOn timeout, `BrandDev::Errors::APITimeoutError` is raised.\n\nNote that requests that time out are retried by default.\n\n## Advanced concepts\n\n### BaseModel\n\nAll parameter and response objects inherit from `BrandDev::Internal::Type::BaseModel`, which provides several conveniences, including:\n\n1. All fields, including unknown ones, are accessible with `obj[:prop]` syntax, and can be destructured with `obj => {prop: prop}` or pattern-matching syntax.\n\n2. Structural equivalence for equality; if two API calls return the same values, comparing the responses with == will return true.\n\n3. Both instances and the classes themselves can be pretty-printed.\n\n4. Helpers such as `#to_h`, `#deep_to_h`, `#to_json`, and `#to_yaml`.\n\n### Making custom or undocumented requests\n\n#### Undocumented properties\n\nYou can send undocumented parameters to any endpoint, and read undocumented response properties, like so:\n\nNote: the `extra_` parameters of the same name overrides the documented parameters.\n\n```ruby\nbrand =\n brand_dev.brand.retrieve(\n domain: "REPLACE_ME",\n request_options: {\n extra_query: {my_query_parameter: value},\n extra_body: {my_body_parameter: value},\n extra_headers: {"my-header": value}\n }\n )\n\nputs(brand[:my_undocumented_property])\n```\n\n#### Undocumented request params\n\nIf you want to explicitly send an extra param, you can do so with the `extra_query`, `extra_body`, and `extra_headers` under the `request_options:` parameter when making a request, as seen in the examples above.\n\n#### Undocumented endpoints\n\nTo make requests to undocumented endpoints while retaining the benefit of auth, retries, and so on, you can make requests using `client.request`, like so:\n\n```ruby\nresponse = client.request(\n method: :post,\n path: \'/undocumented/endpoint\',\n query: {"dog": "woof"},\n headers: {"useful-header": "interesting-value"},\n body: {"hello": "world"}\n)\n```\n\n### Concurrency & connection pooling\n\nThe `BrandDev::Client` instances are threadsafe, but are only are fork-safe when there are no in-flight HTTP requests.\n\nEach instance of `BrandDev::Client` has its own HTTP connection pool with a default size of 99. As such, we recommend instantiating the client once per application in most settings.\n\nWhen all available connections from the pool are checked out, requests wait for a new connection to become available, with queue time counting towards the request timeout.\n\nUnless otherwise specified, other classes in the SDK do not have locks protecting their underlying data structure.\n\n## Sorbet\n\nThis library provides comprehensive [RBI](https://sorbet.org/docs/rbi) definitions, and has no dependency on sorbet-runtime.\n\nYou can provide typesafe request parameters like so:\n\n```ruby\nbrand_dev.brand.retrieve(domain: "REPLACE_ME")\n```\n\nOr, equivalently:\n\n```ruby\n# Hashes work, but are not typesafe:\nbrand_dev.brand.retrieve(domain: "REPLACE_ME")\n\n# You can also splat a full Params class:\nparams = BrandDev::BrandRetrieveParams.new(domain: "REPLACE_ME")\nbrand_dev.brand.retrieve(**params)\n```\n\n### Enums\n\nSince this library does not depend on `sorbet-runtime`, it cannot provide [`T::Enum`](https://sorbet.org/docs/tenum) instances. Instead, we provide "tagged symbols" instead, which is always a primitive at runtime:\n\n```ruby\n# :albanian\nputs(BrandDev::BrandRetrieveParams::ForceLanguage::ALBANIAN)\n\n# Revealed type: `T.all(BrandDev::BrandRetrieveParams::ForceLanguage, Symbol)`\nT.reveal_type(BrandDev::BrandRetrieveParams::ForceLanguage::ALBANIAN)\n```\n\nEnum parameters have a "relaxed" type, so you can either pass in enum constants or their literal value:\n\n```ruby\n# Using the enum constants preserves the tagged type information:\nbrand_dev.brand.retrieve(\n force_language: BrandDev::BrandRetrieveParams::ForceLanguage::ALBANIAN,\n # …\n)\n\n# Literal values are also permissible:\nbrand_dev.brand.retrieve(\n force_language: :albanian,\n # …\n)\n```\n\n## Versioning\n\nThis package follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions. As the library is in initial development and has a major version of `0`, APIs may change at any time.\n\nThis package considers improvements to the (non-runtime) `*.rbi` and `*.rbs` type definitions to be non-breaking changes.\n\n## Requirements\n\nRuby 3.2.0 or higher.\n\n## Contributing\n\nSee [the contributing documentation](https://github.com/context-dot-dev/deprecated-brand-ruby-sdk/tree/main/CONTRIBUTING.md).\n', + '# Brand Dev Ruby API library\n\nThe Brand Dev Ruby library provides convenient access to the Brand Dev REST API from any Ruby 3.2.0+ application. It ships with comprehensive types & docstrings in Yard, RBS, and RBI – [see below](https://github.com/context-dot-dev/deprecated-brand-ruby-sdk#Sorbet) for usage with Sorbet. The standard library\'s `net/http` is used as the HTTP transport, with connection pooling via the `connection_pool` gem.\n\n\n\nIt is generated with [Stainless](https://www.stainless.com/).\n\n## MCP Server\n\nUse the Brand Dev MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application.\n\n[![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=brand.dev-mcp&config=eyJuYW1lIjoiYnJhbmQuZGV2LW1jcCIsInRyYW5zcG9ydCI6Imh0dHAiLCJ1cmwiOiJodHRwczovL2JyYW5kLWRldi5zdGxtY3AuY29tIiwiaGVhZGVycyI6eyJ4LWJyYW5kLWRldi1hcGkta2V5IjoiTXkgQVBJIEtleSJ9fQ)\n[![Install in VS Code](https://img.shields.io/badge/_-Add_to_VS_Code-blue?style=for-the-badge&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCA0MCA0MCI+PHBhdGggZmlsbD0iI0VFRSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMzAuMjM1IDM5Ljg4NGEyLjQ5MSAyLjQ5MSAwIDAgMS0xLjc4MS0uNzNMMTIuNyAyNC43OGwtMy40NiAyLjYyNC0zLjQwNiAyLjU4MmExLjY2NSAxLjY2NSAwIDAgMS0xLjA4Mi4zMzggMS42NjQgMS42NjQgMCAwIDEtMS4wNDYtLjQzMWwtMi4yLTJhMS42NjYgMS42NjYgMCAwIDEgMC0yLjQ2M0w3LjQ1OCAyMCA0LjY3IDE3LjQ1MyAxLjUwNyAxNC41N2ExLjY2NSAxLjY2NSAwIDAgMSAwLTIuNDYzbDIuMi0yYTEuNjY1IDEuNjY1IDAgMCAxIDIuMTMtLjA5N2w2Ljg2MyA1LjIwOUwyOC40NTIuODQ0YTIuNDg4IDIuNDg4IDAgMCAxIDEuODQxLS43MjljLjM1MS4wMDkuNjk5LjA5MSAxLjAxOS4yNDVsOC4yMzYgMy45NjFhMi41IDIuNSAwIDAgMSAxLjQxNSAyLjI1M3YuMDk5LS4wNDVWMzMuMzd2LS4wNDUuMDk1YTIuNTAxIDIuNTAxIDAgMCAxLTEuNDE2IDIuMjU3bC04LjIzNSAzLjk2MWEyLjQ5MiAyLjQ5MiAwIDAgMS0xLjA3Ny4yNDZabS43MTYtMjguOTQ3LTExLjk0OCA5LjA2MiAxMS45NTIgOS4wNjUtLjAwNC0xOC4xMjdaIi8+PC9zdmc+)](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22brand.dev-mcp%22%2C%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fbrand-dev.stlmcp.com%22%2C%22headers%22%3A%7B%22x-brand-dev-api-key%22%3A%22My%20API%20Key%22%7D%7D)\n\n> Note: You may need to set environment variables in your MCP client.\n\n## Documentation\n\nDocumentation for releases of this gem can be found [on RubyDoc](https://gemdocs.org/gems/brand.dev).\n\nThe REST API documentation can be found on [docs.context.dev](https://docs.context.dev/).\n\n## Installation\n\nTo use this gem, install via Bundler by adding the following to your application\'s `Gemfile`:\n\n\n\n```ruby\ngem "brand.dev", "~> 0.0.1"\n```\n\n\n\n## Usage\n\n```ruby\nrequire "bundler/setup"\nrequire "brand_dev"\n\nbrand_dev = BrandDev::Client.new(\n api_key: ENV["BRAND_DEV_API_KEY"] # This is the default and can be omitted\n)\n\nbrand = brand_dev.brand.retrieve(domain: "REPLACE_ME")\n\nputs(brand.brand)\n```\n\n\n\n\n\n\n\n### Handling errors\n\nWhen the library is unable to connect to the API, or if the API returns a non-success status code (i.e., 4xx or 5xx response), a subclass of `BrandDev::Errors::APIError` will be thrown:\n\n```ruby\nbegin\n brand = brand_dev.brand.retrieve(domain: "REPLACE_ME")\nrescue BrandDev::Errors::APIConnectionError => e\n puts("The server could not be reached")\n puts(e.cause) # an underlying Exception, likely raised within `net/http`\nrescue BrandDev::Errors::RateLimitError => e\n puts("A 429 status code was received; we should back off a bit.")\nrescue BrandDev::Errors::APIStatusError => e\n puts("Another non-200-range status code was received")\n puts(e.status)\nend\n```\n\nError codes are as follows:\n\n| Cause | Error Type |\n| ---------------- | -------------------------- |\n| HTTP 400 | `BadRequestError` |\n| HTTP 401 | `AuthenticationError` |\n| HTTP 403 | `PermissionDeniedError` |\n| HTTP 404 | `NotFoundError` |\n| HTTP 409 | `ConflictError` |\n| HTTP 422 | `UnprocessableEntityError` |\n| HTTP 429 | `RateLimitError` |\n| HTTP >= 500 | `InternalServerError` |\n| Other HTTP error | `APIStatusError` |\n| Timeout | `APITimeoutError` |\n| Network error | `APIConnectionError` |\n\n### Retries\n\nCertain errors will be automatically retried 2 times by default, with a short exponential backoff.\n\nConnection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict, 429 Rate Limit, >=500 Internal errors, and timeouts will all be retried by default.\n\nYou can use the `max_retries` option to configure or disable this:\n\n```ruby\n# Configure the default for all requests:\nbrand_dev = BrandDev::Client.new(\n max_retries: 0 # default is 2\n)\n\n# Or, configure per-request:\nbrand_dev.brand.retrieve(domain: "REPLACE_ME", request_options: {max_retries: 5})\n```\n\n### Timeouts\n\nBy default, requests will time out after 60 seconds. You can use the timeout option to configure or disable this:\n\n```ruby\n# Configure the default for all requests:\nbrand_dev = BrandDev::Client.new(\n timeout: nil # default is 60\n)\n\n# Or, configure per-request:\nbrand_dev.brand.retrieve(domain: "REPLACE_ME", request_options: {timeout: 5})\n```\n\nOn timeout, `BrandDev::Errors::APITimeoutError` is raised.\n\nNote that requests that time out are retried by default.\n\n## Advanced concepts\n\n### BaseModel\n\nAll parameter and response objects inherit from `BrandDev::Internal::Type::BaseModel`, which provides several conveniences, including:\n\n1. All fields, including unknown ones, are accessible with `obj[:prop]` syntax, and can be destructured with `obj => {prop: prop}` or pattern-matching syntax.\n\n2. Structural equivalence for equality; if two API calls return the same values, comparing the responses with == will return true.\n\n3. Both instances and the classes themselves can be pretty-printed.\n\n4. Helpers such as `#to_h`, `#deep_to_h`, `#to_json`, and `#to_yaml`.\n\n### Making custom or undocumented requests\n\n#### Undocumented properties\n\nYou can send undocumented parameters to any endpoint, and read undocumented response properties, like so:\n\nNote: the `extra_` parameters of the same name overrides the documented parameters.\n\n```ruby\nbrand =\n brand_dev.brand.retrieve(\n domain: "REPLACE_ME",\n request_options: {\n extra_query: {my_query_parameter: value},\n extra_body: {my_body_parameter: value},\n extra_headers: {"my-header": value}\n }\n )\n\nputs(brand[:my_undocumented_property])\n```\n\n#### Undocumented request params\n\nIf you want to explicitly send an extra param, you can do so with the `extra_query`, `extra_body`, and `extra_headers` under the `request_options:` parameter when making a request, as seen in the examples above.\n\n#### Undocumented endpoints\n\nTo make requests to undocumented endpoints while retaining the benefit of auth, retries, and so on, you can make requests using `client.request`, like so:\n\n```ruby\nresponse = client.request(\n method: :post,\n path: \'/undocumented/endpoint\',\n query: {"dog": "woof"},\n headers: {"useful-header": "interesting-value"},\n body: {"hello": "world"}\n)\n```\n\n### Concurrency & connection pooling\n\nThe `BrandDev::Client` instances are threadsafe, but are only are fork-safe when there are no in-flight HTTP requests.\n\nEach instance of `BrandDev::Client` has its own HTTP connection pool with a default size of 99. As such, we recommend instantiating the client once per application in most settings.\n\nWhen all available connections from the pool are checked out, requests wait for a new connection to become available, with queue time counting towards the request timeout.\n\nUnless otherwise specified, other classes in the SDK do not have locks protecting their underlying data structure.\n\n## Sorbet\n\nThis library provides comprehensive [RBI](https://sorbet.org/docs/rbi) definitions, and has no dependency on sorbet-runtime.\n\nYou can provide typesafe request parameters like so:\n\n```ruby\nbrand_dev.brand.retrieve(domain: "REPLACE_ME")\n```\n\nOr, equivalently:\n\n```ruby\n# Hashes work, but are not typesafe:\nbrand_dev.brand.retrieve(domain: "REPLACE_ME")\n\n# You can also splat a full Params class:\nparams = BrandDev::BrandRetrieveParams.new(domain: "REPLACE_ME")\nbrand_dev.brand.retrieve(**params)\n```\n\n### Enums\n\nSince this library does not depend on `sorbet-runtime`, it cannot provide [`T::Enum`](https://sorbet.org/docs/tenum) instances. Instead, we provide "tagged symbols" instead, which is always a primitive at runtime:\n\n```ruby\n# :afrikaans\nputs(BrandDev::BrandRetrieveParams::ForceLanguage::AFRIKAANS)\n\n# Revealed type: `T.all(BrandDev::BrandRetrieveParams::ForceLanguage, Symbol)`\nT.reveal_type(BrandDev::BrandRetrieveParams::ForceLanguage::AFRIKAANS)\n```\n\nEnum parameters have a "relaxed" type, so you can either pass in enum constants or their literal value:\n\n```ruby\n# Using the enum constants preserves the tagged type information:\nbrand_dev.brand.retrieve(\n force_language: BrandDev::BrandRetrieveParams::ForceLanguage::AFRIKAANS,\n # …\n)\n\n# Literal values are also permissible:\nbrand_dev.brand.retrieve(\n force_language: :afrikaans,\n # …\n)\n```\n\n## Versioning\n\nThis package follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions. As the library is in initial development and has a major version of `0`, APIs may change at any time.\n\nThis package considers improvements to the (non-runtime) `*.rbi` and `*.rbs` type definitions to be non-breaking changes.\n\n## Requirements\n\nRuby 3.2.0 or higher.\n\n## Contributing\n\nSee [the contributing documentation](https://github.com/context-dot-dev/deprecated-brand-ruby-sdk/tree/main/CONTRIBUTING.md).\n', }, { - language: 'java', + language: 'typescript', content: - '# Brand Dev Java API Library\n\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.branddev.api/brand-dev-java)](https://central.sonatype.com/artifact/com.branddev.api/brand-dev-java/0.0.1)\n[![javadoc](https://javadoc.io/badge2/com.branddev.api/brand-dev-java/0.0.1/javadoc.svg)](https://javadoc.io/doc/com.branddev.api/brand-dev-java/0.0.1)\n\n\nThe Brand Dev Java SDK provides convenient access to the [Brand Dev REST API](https://docs.context.dev/) from applications written in Java.\n\n\n\nIt is generated with [Stainless](https://www.stainless.com/).\n\n## MCP Server\n\nUse the Brand Dev MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application.\n\n[![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=brand.dev-mcp&config=eyJuYW1lIjoiYnJhbmQuZGV2LW1jcCIsInRyYW5zcG9ydCI6Imh0dHAiLCJ1cmwiOiJodHRwczovL2JyYW5kLWRldi5zdGxtY3AuY29tIiwiaGVhZGVycyI6eyJ4LWJyYW5kLWRldi1hcGkta2V5IjoiTXkgQVBJIEtleSJ9fQ)\n[![Install in VS Code](https://img.shields.io/badge/_-Add_to_VS_Code-blue?style=for-the-badge&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCA0MCA0MCI+PHBhdGggZmlsbD0iI0VFRSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMzAuMjM1IDM5Ljg4NGEyLjQ5MSAyLjQ5MSAwIDAgMS0xLjc4MS0uNzNMMTIuNyAyNC43OGwtMy40NiAyLjYyNC0zLjQwNiAyLjU4MmExLjY2NSAxLjY2NSAwIDAgMS0xLjA4Mi4zMzggMS42NjQgMS42NjQgMCAwIDEtMS4wNDYtLjQzMWwtMi4yLTJhMS42NjYgMS42NjYgMCAwIDEgMC0yLjQ2M0w3LjQ1OCAyMCA0LjY3IDE3LjQ1MyAxLjUwNyAxNC41N2ExLjY2NSAxLjY2NSAwIDAgMSAwLTIuNDYzbDIuMi0yYTEuNjY1IDEuNjY1IDAgMCAxIDIuMTMtLjA5N2w2Ljg2MyA1LjIwOUwyOC40NTIuODQ0YTIuNDg4IDIuNDg4IDAgMCAxIDEuODQxLS43MjljLjM1MS4wMDkuNjk5LjA5MSAxLjAxOS4yNDVsOC4yMzYgMy45NjFhMi41IDIuNSAwIDAgMSAxLjQxNSAyLjI1M3YuMDk5LS4wNDVWMzMuMzd2LS4wNDUuMDk1YTIuNTAxIDIuNTAxIDAgMCAxLTEuNDE2IDIuMjU3bC04LjIzNSAzLjk2MWEyLjQ5MiAyLjQ5MiAwIDAgMS0xLjA3Ny4yNDZabS43MTYtMjguOTQ3LTExLjk0OCA5LjA2MiAxMS45NTIgOS4wNjUtLjAwNC0xOC4xMjdaIi8+PC9zdmc+)](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22brand.dev-mcp%22%2C%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fbrand-dev.stlmcp.com%22%2C%22headers%22%3A%7B%22x-brand-dev-api-key%22%3A%22My%20API%20Key%22%7D%7D)\n\n> Note: You may need to set environment variables in your MCP client.\n\n\n\nThe REST API documentation can be found on [docs.context.dev](https://docs.context.dev/). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.branddev.api/brand-dev-java/0.0.1).\n\n\n\n## Installation\n\n\n\n### Gradle\n\n~~~kotlin\nimplementation("com.branddev.api:brand-dev-java:0.0.1")\n~~~\n\n### Maven\n\n~~~xml\n\n com.branddev.api\n brand-dev-java\n 0.0.1\n\n~~~\n\n\n\n## Requirements\n\nThis library requires Java 8 or later.\n\n## Usage\n\n```java\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandRetrieveParams;\nimport com.branddev.api.models.brand.BrandRetrieveResponse;\n\n// Configures using the `branddev.apiKey` and `branddev.baseUrl` system properties\n// Or configures using the `BRAND_DEV_API_KEY` and `BRAND_DEV_BASE_URL` environment variables\nBrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\nBrandRetrieveParams params = BrandRetrieveParams.builder()\n .domain("REPLACE_ME")\n .build();\nBrandRetrieveResponse brand = client.brand().retrieve(params);\n```\n\n## Client configuration\n\nConfigure the client using system properties or environment variables:\n\n```java\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\n\n// Configures using the `branddev.apiKey` and `branddev.baseUrl` system properties\n// Or configures using the `BRAND_DEV_API_KEY` and `BRAND_DEV_BASE_URL` environment variables\nBrandDevClient client = BrandDevOkHttpClient.fromEnv();\n```\n\nOr manually:\n\n```java\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\n\nBrandDevClient client = BrandDevOkHttpClient.builder()\n .apiKey("My API Key")\n .build();\n```\n\nOr using a combination of the two approaches:\n\n```java\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\n\nBrandDevClient client = BrandDevOkHttpClient.builder()\n // Configures using the `branddev.apiKey` and `branddev.baseUrl` system properties\n // Or configures using the `BRAND_DEV_API_KEY` and `BRAND_DEV_BASE_URL` environment variables\n .fromEnv()\n .apiKey("My API Key")\n .build();\n```\n\nSee this table for the available options:\n\n| Setter | System property | Environment variable | Required | Default value |\n| --------- | ------------------ | -------------------- | -------- | ---------------------------- |\n| `apiKey` | `branddev.apiKey` | `BRAND_DEV_API_KEY` | true | - |\n| `baseUrl` | `branddev.baseUrl` | `BRAND_DEV_BASE_URL` | true | `"https://api.brand.dev/v1"` |\n\nSystem properties take precedence over environment variables.\n\n> [!TIP]\n> Don\'t create more than one client in the same application. Each client has a connection pool and\n> thread pools, which are more efficient to share between requests.\n\n### Modifying configuration\n\nTo temporarily use a modified client configuration, while reusing the same connection and thread pools, call `withOptions()` on any client or service:\n\n```java\nimport com.branddev.api.client.BrandDevClient;\n\nBrandDevClient clientWithOptions = client.withOptions(optionsBuilder -> {\n optionsBuilder.baseUrl("https://example.com");\n optionsBuilder.maxRetries(42);\n});\n```\n\nThe `withOptions()` method does not affect the original client or service.\n\n## Requests and responses\n\nTo send a request to the Brand Dev API, build an instance of some `Params` class and pass it to the corresponding client method. When the response is received, it will be deserialized into an instance of a Java class.\n\nFor example, `client.brand().retrieve(...)` should be called with an instance of `BrandRetrieveParams`, and it will return an instance of `BrandRetrieveResponse`.\n\n## Immutability\n\nEach class in the SDK has an associated [builder](https://blogs.oracle.com/javamagazine/post/exploring-joshua-blochs-builder-design-pattern-in-java) or factory method for constructing it.\n\nEach class is [immutable](https://docs.oracle.com/javase/tutorial/essential/concurrency/immutable.html) once constructed. If the class has an associated builder, then it has a `toBuilder()` method, which can be used to convert it back to a builder for making a modified copy.\n\nBecause each class is immutable, builder modification will _never_ affect already built class instances.\n\n## Asynchronous execution\n\nThe default client is synchronous. To switch to asynchronous execution, call the `async()` method:\n\n```java\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport com.branddev.api.models.brand.BrandRetrieveParams;\nimport com.branddev.api.models.brand.BrandRetrieveResponse;\nimport java.util.concurrent.CompletableFuture;\n\n// Configures using the `branddev.apiKey` and `branddev.baseUrl` system properties\n// Or configures using the `BRAND_DEV_API_KEY` and `BRAND_DEV_BASE_URL` environment variables\nBrandDevClient client = BrandDevOkHttpClient.fromEnv();\n\nBrandRetrieveParams params = BrandRetrieveParams.builder()\n .domain("REPLACE_ME")\n .build();\nCompletableFuture brand = client.async().brand().retrieve(params);\n```\n\nOr create an asynchronous client from the beginning:\n\n```java\nimport com.branddev.api.client.BrandDevClientAsync;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClientAsync;\nimport com.branddev.api.models.brand.BrandRetrieveParams;\nimport com.branddev.api.models.brand.BrandRetrieveResponse;\nimport java.util.concurrent.CompletableFuture;\n\n// Configures using the `branddev.apiKey` and `branddev.baseUrl` system properties\n// Or configures using the `BRAND_DEV_API_KEY` and `BRAND_DEV_BASE_URL` environment variables\nBrandDevClientAsync client = BrandDevOkHttpClientAsync.fromEnv();\n\nBrandRetrieveParams params = BrandRetrieveParams.builder()\n .domain("REPLACE_ME")\n .build();\nCompletableFuture brand = client.brand().retrieve(params);\n```\n\nThe asynchronous client supports the same options as the synchronous one, except most methods return `CompletableFuture`s.\n\n\n\n\n\n\n\n## Raw responses\n\nThe SDK defines methods that deserialize responses into instances of Java classes. However, these methods don\'t provide access to the response headers, status code, or the raw response body.\n\nTo access this data, prefix any HTTP method call on a client or service with `withRawResponse()`:\n\n```java\nimport com.branddev.api.core.http.Headers;\nimport com.branddev.api.core.http.HttpResponseFor;\nimport com.branddev.api.models.brand.BrandRetrieveParams;\nimport com.branddev.api.models.brand.BrandRetrieveResponse;\n\nBrandRetrieveParams params = BrandRetrieveParams.builder()\n .domain("REPLACE_ME")\n .build();\nHttpResponseFor brand = client.brand().withRawResponse().retrieve(params);\n\nint statusCode = brand.statusCode();\nHeaders headers = brand.headers();\n```\n\nYou can still deserialize the response into an instance of a Java class if needed:\n\n```java\nimport com.branddev.api.models.brand.BrandRetrieveResponse;\n\nBrandRetrieveResponse parsedBrand = brand.parse();\n```\n\n## Error handling\n\nThe SDK throws custom unchecked exception types:\n\n- [`BrandDevServiceException`](brand-dev-java-core/src/main/kotlin/com/branddev/api/errors/BrandDevServiceException.kt): Base class for HTTP errors. See this table for which exception subclass is thrown for each HTTP status code:\n\n | Status | Exception |\n | ------ | -------------------------------------------------- |\n | 400 | [`BadRequestException`](brand-dev-java-core/src/main/kotlin/com/branddev/api/errors/BadRequestException.kt) |\n | 401 | [`UnauthorizedException`](brand-dev-java-core/src/main/kotlin/com/branddev/api/errors/UnauthorizedException.kt) |\n | 403 | [`PermissionDeniedException`](brand-dev-java-core/src/main/kotlin/com/branddev/api/errors/PermissionDeniedException.kt) |\n | 404 | [`NotFoundException`](brand-dev-java-core/src/main/kotlin/com/branddev/api/errors/NotFoundException.kt) |\n | 422 | [`UnprocessableEntityException`](brand-dev-java-core/src/main/kotlin/com/branddev/api/errors/UnprocessableEntityException.kt) |\n | 429 | [`RateLimitException`](brand-dev-java-core/src/main/kotlin/com/branddev/api/errors/RateLimitException.kt) |\n | 5xx | [`InternalServerException`](brand-dev-java-core/src/main/kotlin/com/branddev/api/errors/InternalServerException.kt) |\n | others | [`UnexpectedStatusCodeException`](brand-dev-java-core/src/main/kotlin/com/branddev/api/errors/UnexpectedStatusCodeException.kt) |\n\n- [`BrandDevIoException`](brand-dev-java-core/src/main/kotlin/com/branddev/api/errors/BrandDevIoException.kt): I/O networking errors.\n\n- [`BrandDevRetryableException`](brand-dev-java-core/src/main/kotlin/com/branddev/api/errors/BrandDevRetryableException.kt): Generic error indicating a failure that could be retried by the client.\n\n- [`BrandDevInvalidDataException`](brand-dev-java-core/src/main/kotlin/com/branddev/api/errors/BrandDevInvalidDataException.kt): Failure to interpret successfully parsed data. For example, when accessing a property that\'s supposed to be required, but the API unexpectedly omitted it from the response.\n\n- [`BrandDevException`](brand-dev-java-core/src/main/kotlin/com/branddev/api/errors/BrandDevException.kt): Base class for all exceptions. Most errors will result in one of the previously mentioned ones, but completely generic errors may be thrown using the base class.\n\n\n\n## Logging\n\nThe SDK uses the standard [OkHttp logging interceptor](https://github.com/square/okhttp/tree/master/okhttp-logging-interceptor).\n\nEnable logging by setting the `BRAND_DEV_LOG` environment variable to `info`:\n\n```sh\nexport BRAND_DEV_LOG=info\n```\n\nOr to `debug` for more verbose logging:\n\n```sh\nexport BRAND_DEV_LOG=debug\n```\n\n## ProGuard and R8\n\nAlthough the SDK uses reflection, it is still usable with [ProGuard](https://github.com/Guardsquare/proguard) and [R8](https://developer.android.com/topic/performance/app-optimization/enable-app-optimization) because `brand-dev-java-core` is published with a [configuration file](brand-dev-java-core/src/main/resources/META-INF/proguard/brand-dev-java-core.pro) containing [keep rules](https://www.guardsquare.com/manual/configuration/usage).\n\nProGuard and R8 should automatically detect and use the published rules, but you can also manually copy the keep rules if necessary.\n\n\n\n\n\n## Jackson\n\nThe SDK depends on [Jackson](https://github.com/FasterXML/jackson) for JSON serialization/deserialization. It is compatible with version 2.13.4 or higher, but depends on version 2.18.2 by default.\n\nThe SDK throws an exception if it detects an incompatible Jackson version at runtime (e.g. if the default version was overridden in your Maven or Gradle config).\n\nIf the SDK threw an exception, but you\'re _certain_ the version is compatible, then disable the version check using the `checkJacksonVersionCompatibility` on [`BrandDevOkHttpClient`](brand-dev-java-client-okhttp/src/main/kotlin/com/branddev/api/client/okhttp/BrandDevOkHttpClient.kt) or [`BrandDevOkHttpClientAsync`](brand-dev-java-client-okhttp/src/main/kotlin/com/branddev/api/client/okhttp/BrandDevOkHttpClientAsync.kt).\n\n> [!CAUTION]\n> We make no guarantee that the SDK works correctly when the Jackson version check is disabled.\n\nAlso note that there are bugs in older Jackson versions that can affect the SDK. We don\'t work around all Jackson bugs ([example](https://github.com/FasterXML/jackson-databind/issues/3240)) and expect users to upgrade Jackson for those instead.\n\n## Network options\n\n### Retries\n\nThe SDK automatically retries 2 times by default, with a short exponential backoff between requests.\n\nOnly the following error types are retried:\n- Connection errors (for example, due to a network connectivity problem)\n- 408 Request Timeout\n- 409 Conflict\n- 429 Rate Limit\n- 5xx Internal\n\nThe API may also explicitly instruct the SDK to retry or not retry a request.\n\nTo set a custom number of retries, configure the client using the `maxRetries` method:\n\n```java\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\n\nBrandDevClient client = BrandDevOkHttpClient.builder()\n .fromEnv()\n .maxRetries(4)\n .build();\n```\n\n### Timeouts\n\nRequests time out after 1 minute by default.\n\nTo set a custom timeout, configure the method call using the `timeout` method:\n\n```java\nimport com.branddev.api.models.brand.BrandRetrieveResponse;\n\nBrandRetrieveResponse brand = client.brand().retrieve(\n params, RequestOptions.builder().timeout(Duration.ofSeconds(30)).build()\n);\n```\n\nOr configure the default for all method calls at the client level:\n\n```java\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport java.time.Duration;\n\nBrandDevClient client = BrandDevOkHttpClient.builder()\n .fromEnv()\n .timeout(Duration.ofSeconds(30))\n .build();\n```\n\n### Proxies\n\nTo route requests through a proxy, configure the client using the `proxy` method:\n\n```java\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport java.net.InetSocketAddress;\nimport java.net.Proxy;\n\nBrandDevClient client = BrandDevOkHttpClient.builder()\n .fromEnv()\n .proxy(new Proxy(\n Proxy.Type.HTTP, new InetSocketAddress(\n "https://example.com", 8080\n )\n ))\n .build();\n```\n\n### Connection pooling\n\nTo customize the underlying OkHttp connection pool, configure the client using the `maxIdleConnections` and `keepAliveDuration` methods:\n\n```java\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\nimport java.time.Duration;\n\nBrandDevClient client = BrandDevOkHttpClient.builder()\n .fromEnv()\n // If `maxIdleConnections` is set, then `keepAliveDuration` must be set, and vice versa.\n .maxIdleConnections(10)\n .keepAliveDuration(Duration.ofMinutes(2))\n .build();\n```\n\nIf both options are unset, OkHttp\'s default connection pool settings are used.\n\n### HTTPS\n\n> [!NOTE]\n> Most applications should not call these methods, and instead use the system defaults. The defaults include\n> special optimizations that can be lost if the implementations are modified.\n\nTo configure how HTTPS connections are secured, configure the client using the `sslSocketFactory`, `trustManager`, and `hostnameVerifier` methods:\n\n```java\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\n\nBrandDevClient client = BrandDevOkHttpClient.builder()\n .fromEnv()\n // If `sslSocketFactory` is set, then `trustManager` must be set, and vice versa.\n .sslSocketFactory(yourSSLSocketFactory)\n .trustManager(yourTrustManager)\n .hostnameVerifier(yourHostnameVerifier)\n .build();\n```\n\n\n\n### Custom HTTP client\n\nThe SDK consists of three artifacts:\n- `brand-dev-java-core`\n - Contains core SDK logic\n - Does not depend on [OkHttp](https://square.github.io/okhttp)\n - Exposes [`BrandDevClient`](brand-dev-java-core/src/main/kotlin/com/branddev/api/client/BrandDevClient.kt), [`BrandDevClientAsync`](brand-dev-java-core/src/main/kotlin/com/branddev/api/client/BrandDevClientAsync.kt), [`BrandDevClientImpl`](brand-dev-java-core/src/main/kotlin/com/branddev/api/client/BrandDevClientImpl.kt), and [`BrandDevClientAsyncImpl`](brand-dev-java-core/src/main/kotlin/com/branddev/api/client/BrandDevClientAsyncImpl.kt), all of which can work with any HTTP client\n- `brand-dev-java-client-okhttp`\n - Depends on [OkHttp](https://square.github.io/okhttp)\n - Exposes [`BrandDevOkHttpClient`](brand-dev-java-client-okhttp/src/main/kotlin/com/branddev/api/client/okhttp/BrandDevOkHttpClient.kt) and [`BrandDevOkHttpClientAsync`](brand-dev-java-client-okhttp/src/main/kotlin/com/branddev/api/client/okhttp/BrandDevOkHttpClientAsync.kt), which provide a way to construct [`BrandDevClientImpl`](brand-dev-java-core/src/main/kotlin/com/branddev/api/client/BrandDevClientImpl.kt) and [`BrandDevClientAsyncImpl`](brand-dev-java-core/src/main/kotlin/com/branddev/api/client/BrandDevClientAsyncImpl.kt), respectively, using OkHttp\n- `brand-dev-java`\n - Depends on and exposes the APIs of both `brand-dev-java-core` and `brand-dev-java-client-okhttp`\n - Does not have its own logic\n\nThis structure allows replacing the SDK\'s default HTTP client without pulling in unnecessary dependencies.\n\n#### Customized [`OkHttpClient`](https://square.github.io/okhttp/3.x/okhttp/okhttp3/OkHttpClient.html)\n\n> [!TIP]\n> Try the available [network options](#network-options) before replacing the default client.\n\nTo use a customized `OkHttpClient`:\n\n1. Replace your [`brand-dev-java` dependency](#installation) with `brand-dev-java-core`\n2. Copy `brand-dev-java-client-okhttp`\'s [`OkHttpClient`](brand-dev-java-client-okhttp/src/main/kotlin/com/branddev/api/client/okhttp/OkHttpClient.kt) class into your code and customize it\n3. Construct [`BrandDevClientImpl`](brand-dev-java-core/src/main/kotlin/com/branddev/api/client/BrandDevClientImpl.kt) or [`BrandDevClientAsyncImpl`](brand-dev-java-core/src/main/kotlin/com/branddev/api/client/BrandDevClientAsyncImpl.kt), similarly to [`BrandDevOkHttpClient`](brand-dev-java-client-okhttp/src/main/kotlin/com/branddev/api/client/okhttp/BrandDevOkHttpClient.kt) or [`BrandDevOkHttpClientAsync`](brand-dev-java-client-okhttp/src/main/kotlin/com/branddev/api/client/okhttp/BrandDevOkHttpClientAsync.kt), using your customized client\n\n### Completely custom HTTP client\n\nTo use a completely custom HTTP client:\n\n1. Replace your [`brand-dev-java` dependency](#installation) with `brand-dev-java-core`\n2. Write a class that implements the [`HttpClient`](brand-dev-java-core/src/main/kotlin/com/branddev/api/core/http/HttpClient.kt) interface\n3. Construct [`BrandDevClientImpl`](brand-dev-java-core/src/main/kotlin/com/branddev/api/client/BrandDevClientImpl.kt) or [`BrandDevClientAsyncImpl`](brand-dev-java-core/src/main/kotlin/com/branddev/api/client/BrandDevClientAsyncImpl.kt), similarly to [`BrandDevOkHttpClient`](brand-dev-java-client-okhttp/src/main/kotlin/com/branddev/api/client/okhttp/BrandDevOkHttpClient.kt) or [`BrandDevOkHttpClientAsync`](brand-dev-java-client-okhttp/src/main/kotlin/com/branddev/api/client/okhttp/BrandDevOkHttpClientAsync.kt), using your new client class\n\n## Undocumented API functionality\n\nThe SDK is typed for convenient usage of the documented API. However, it also supports working with undocumented or not yet supported parts of the API.\n\n### Parameters\n\nTo set undocumented parameters, call the `putAdditionalHeader`, `putAdditionalQueryParam`, or `putAdditionalBodyProperty` methods on any `Params` class:\n\n```java\nimport com.branddev.api.core.JsonValue;\nimport com.branddev.api.models.brand.BrandRetrieveParams;\n\nBrandRetrieveParams params = BrandRetrieveParams.builder()\n .putAdditionalHeader("Secret-Header", "42")\n .putAdditionalQueryParam("secret_query_param", "42")\n .putAdditionalBodyProperty("secretProperty", JsonValue.from("42"))\n .build();\n```\n\nThese can be accessed on the built object later using the `_additionalHeaders()`, `_additionalQueryParams()`, and `_additionalBodyProperties()` methods.\n\nTo set undocumented parameters on _nested_ headers, query params, or body classes, call the `putAdditionalProperty` method on the nested class:\n\n```java\nimport com.branddev.api.core.JsonValue;\nimport com.branddev.api.models.brand.BrandAiQueryParams;\n\nBrandAiQueryParams params = BrandAiQueryParams.builder()\n .specificPages(BrandAiQueryParams.SpecificPages.builder()\n .putAdditionalProperty("secretProperty", JsonValue.from("42"))\n .build())\n .build();\n```\n\nThese properties can be accessed on the nested built object later using the `_additionalProperties()` method.\n\nTo set a documented parameter or property to an undocumented or not yet supported _value_, pass a [`JsonValue`](brand-dev-java-core/src/main/kotlin/com/branddev/api/core/Values.kt) object to its setter:\n\n```java\nimport com.branddev.api.models.brand.BrandRetrieveParams;\n\nBrandRetrieveParams params = BrandRetrieveParams.builder()\n .domain("REPLACE_ME")\n .build();\n```\n\nThe most straightforward way to create a [`JsonValue`](brand-dev-java-core/src/main/kotlin/com/branddev/api/core/Values.kt) is using its `from(...)` method:\n\n```java\nimport com.branddev.api.core.JsonValue;\nimport java.util.List;\nimport java.util.Map;\n\n// Create primitive JSON values\nJsonValue nullValue = JsonValue.from(null);\nJsonValue booleanValue = JsonValue.from(true);\nJsonValue numberValue = JsonValue.from(42);\nJsonValue stringValue = JsonValue.from("Hello World!");\n\n// Create a JSON array value equivalent to `["Hello", "World"]`\nJsonValue arrayValue = JsonValue.from(List.of(\n "Hello", "World"\n));\n\n// Create a JSON object value equivalent to `{ "a": 1, "b": 2 }`\nJsonValue objectValue = JsonValue.from(Map.of(\n "a", 1,\n "b", 2\n));\n\n// Create an arbitrarily nested JSON equivalent to:\n// {\n// "a": [1, 2],\n// "b": [3, 4]\n// }\nJsonValue complexValue = JsonValue.from(Map.of(\n "a", List.of(\n 1, 2\n ),\n "b", List.of(\n 3, 4\n )\n));\n```\n\nNormally a `Builder` class\'s `build` method will throw [`IllegalStateException`](https://docs.oracle.com/javase/8/docs/api/java/lang/IllegalStateException.html) if any required parameter or property is unset.\n\nTo forcibly omit a required parameter or property, pass [`JsonMissing`](brand-dev-java-core/src/main/kotlin/com/branddev/api/core/Values.kt):\n\n```java\nimport com.branddev.api.core.JsonMissing;\nimport com.branddev.api.models.brand.BrandRetrieveParams;\n\nBrandRetrieveParams params = BrandRetrieveParams.builder()\n .domain(JsonMissing.of())\n .build();\n```\n\n### Response properties\n\nTo access undocumented response properties, call the `_additionalProperties()` method:\n\n```java\nimport com.branddev.api.core.JsonValue;\nimport java.util.Map;\n\nMap additionalProperties = client.brand().retrieve(params)._additionalProperties();\nJsonValue secretPropertyValue = additionalProperties.get("secretProperty");\n\nString result = secretPropertyValue.accept(new JsonValue.Visitor<>() {\n @Override\n public String visitNull() {\n return "It\'s null!";\n }\n\n @Override\n public String visitBoolean(boolean value) {\n return "It\'s a boolean!";\n }\n\n @Override\n public String visitNumber(Number value) {\n return "It\'s a number!";\n }\n\n // Other methods include `visitMissing`, `visitString`, `visitArray`, and `visitObject`\n // The default implementation of each unimplemented method delegates to `visitDefault`, which throws by default, but can also be overridden\n});\n```\n\nTo access a property\'s raw JSON value, which may be undocumented, call its `_` prefixed method:\n\n```java\nimport com.branddev.api.core.JsonField;\nimport java.util.Optional;\n\nJsonField field = client.brand().retrieve(params)._field();\n\nif (field.isMissing()) {\n // The property is absent from the JSON response\n} else if (field.isNull()) {\n // The property was set to literal null\n} else {\n // Check if value was provided as a string\n // Other methods include `asNumber()`, `asBoolean()`, etc.\n Optional jsonString = field.asString();\n\n // Try to deserialize into a custom type\n MyClass myObject = field.asUnknown().orElseThrow().convert(MyClass.class);\n}\n```\n\n### Response validation\n\nIn rare cases, the API may return a response that doesn\'t match the expected type. For example, the SDK may expect a property to contain a `String`, but the API could return something else.\n\nBy default, the SDK will not throw an exception in this case. It will throw [`BrandDevInvalidDataException`](brand-dev-java-core/src/main/kotlin/com/branddev/api/errors/BrandDevInvalidDataException.kt) only if you directly access the property.\n\nIf you would prefer to check that the response is completely well-typed upfront, then either call `validate()`:\n\n```java\nimport com.branddev.api.models.brand.BrandRetrieveResponse;\n\nBrandRetrieveResponse brand = client.brand().retrieve(params).validate();\n```\n\nOr configure the method call to validate the response using the `responseValidation` method:\n\n```java\nimport com.branddev.api.models.brand.BrandRetrieveResponse;\n\nBrandRetrieveResponse brand = client.brand().retrieve(\n params, RequestOptions.builder().responseValidation(true).build()\n);\n```\n\nOr configure the default for all method calls at the client level:\n\n```java\nimport com.branddev.api.client.BrandDevClient;\nimport com.branddev.api.client.okhttp.BrandDevOkHttpClient;\n\nBrandDevClient client = BrandDevOkHttpClient.builder()\n .fromEnv()\n .responseValidation(true)\n .build();\n```\n\n## FAQ\n\n### Why don\'t you use plain `enum` classes?\n\nJava `enum` classes are not trivially [forwards compatible](https://www.stainless.com/blog/making-java-enums-forwards-compatible). Using them in the SDK could cause runtime exceptions if the API is updated to respond with a new enum value.\n\n### Why do you represent fields using `JsonField` instead of just plain `T`?\n\nUsing `JsonField` enables a few features:\n\n- Allowing usage of [undocumented API functionality](#undocumented-api-functionality)\n- Lazily [validating the API response against the expected shape](#response-validation)\n- Representing absent vs explicitly null values\n\n### Why don\'t you use [`data` classes](https://kotlinlang.org/docs/data-classes.html)?\n\nIt is not [backwards compatible to add new fields to a data class](https://kotlinlang.org/docs/api-guidelines-backward-compatibility.html#avoid-using-data-classes-in-your-api) and we don\'t want to introduce a breaking change every time we add a field to a class.\n\n### Why don\'t you use checked exceptions?\n\nChecked exceptions are widely considered a mistake in the Java programming language. In fact, they were omitted from Kotlin for this reason.\n\nChecked exceptions:\n\n- Are verbose to handle\n- Encourage error handling at the wrong level of abstraction, where nothing can be done about the error\n- Are tedious to propagate due to the [function coloring problem](https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function)\n- Don\'t play well with lambdas (also due to the function coloring problem)\n\n## Semantic versioning\n\nThis package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:\n\n1. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_\n2. Changes that we do not expect to impact the vast majority of users in practice.\n\nWe take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.\n\nWe are keen for your feedback; please open an [issue](https://www.github.com/context-dot-dev/deprecated-brand-java-sdk/issues) with questions, bugs, or suggestions.\n', + "# Brand Dev TypeScript API Library\n\n[![NPM version](https://img.shields.io/npm/v/brand.dev.svg?label=npm%20(stable))](https://npmjs.org/package/brand.dev) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/brand.dev)\n\nThis library provides convenient access to the Brand Dev REST API from server-side TypeScript or JavaScript.\n\n\n\nThe REST API documentation can be found on [docs.context.dev](https://docs.context.dev/). The full API of this library can be found in [api.md](api.md).\n\nIt is generated with [Stainless](https://www.stainless.com/).\n\n## MCP Server\n\nUse the Brand Dev MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application.\n\n[![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=brand.dev-mcp&config=eyJuYW1lIjoiYnJhbmQuZGV2LW1jcCIsInRyYW5zcG9ydCI6Imh0dHAiLCJ1cmwiOiJodHRwczovL2JyYW5kLWRldi5zdGxtY3AuY29tIiwiaGVhZGVycyI6eyJ4LWJyYW5kLWRldi1hcGkta2V5IjoiTXkgQVBJIEtleSJ9fQ)\n[![Install in VS Code](https://img.shields.io/badge/_-Add_to_VS_Code-blue?style=for-the-badge&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCA0MCA0MCI+PHBhdGggZmlsbD0iI0VFRSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMzAuMjM1IDM5Ljg4NGEyLjQ5MSAyLjQ5MSAwIDAgMS0xLjc4MS0uNzNMMTIuNyAyNC43OGwtMy40NiAyLjYyNC0zLjQwNiAyLjU4MmExLjY2NSAxLjY2NSAwIDAgMS0xLjA4Mi4zMzggMS42NjQgMS42NjQgMCAwIDEtMS4wNDYtLjQzMWwtMi4yLTJhMS42NjYgMS42NjYgMCAwIDEgMC0yLjQ2M0w3LjQ1OCAyMCA0LjY3IDE3LjQ1MyAxLjUwNyAxNC41N2ExLjY2NSAxLjY2NSAwIDAgMSAwLTIuNDYzbDIuMi0yYTEuNjY1IDEuNjY1IDAgMCAxIDIuMTMtLjA5N2w2Ljg2MyA1LjIwOUwyOC40NTIuODQ0YTIuNDg4IDIuNDg4IDAgMCAxIDEuODQxLS43MjljLjM1MS4wMDkuNjk5LjA5MSAxLjAxOS4yNDVsOC4yMzYgMy45NjFhMi41IDIuNSAwIDAgMSAxLjQxNSAyLjI1M3YuMDk5LS4wNDVWMzMuMzd2LS4wNDUuMDk1YTIuNTAxIDIuNTAxIDAgMCAxLTEuNDE2IDIuMjU3bC04LjIzNSAzLjk2MWEyLjQ5MiAyLjQ5MiAwIDAgMS0xLjA3Ny4yNDZabS43MTYtMjguOTQ3LTExLjk0OCA5LjA2MiAxMS45NTIgOS4wNjUtLjAwNC0xOC4xMjdaIi8+PC9zdmc+)](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22brand.dev-mcp%22%2C%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fbrand-dev.stlmcp.com%22%2C%22headers%22%3A%7B%22x-brand-dev-api-key%22%3A%22My%20API%20Key%22%7D%7D)\n\n> Note: You may need to set environment variables in your MCP client.\n\n## Installation\n\n```sh\nnpm install brand.dev\n```\n\n\n\n## Usage\n\nThe full API of this library can be found in [api.md](api.md).\n\n\n```js\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst brand = await client.brand.retrieve({ domain: 'REPLACE_ME' });\n\nconsole.log(brand.brand);\n```\n\n\n\n### Request & Response types\n\nThis library includes TypeScript definitions for all request params and response fields. You may import and use them like so:\n\n\n```ts\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n apiKey: process.env['BRAND_DEV_API_KEY'], // This is the default and can be omitted\n});\n\nconst params: BrandDev.BrandRetrieveParams = { domain: 'REPLACE_ME' };\nconst brand: BrandDev.BrandRetrieveResponse = await client.brand.retrieve(params);\n```\n\nDocumentation for each method, request param, and response field are available in docstrings and will appear on hover in most modern editors.\n\n\n\n\n\n## Handling errors\n\nWhen the library is unable to connect to the API,\nor if the API returns a non-success status code (i.e., 4xx or 5xx response),\na subclass of `APIError` will be thrown:\n\n\n```ts\nconst brand = await client.brand.retrieve({ domain: 'REPLACE_ME' }).catch(async (err) => {\n if (err instanceof BrandDev.APIError) {\n console.log(err.status); // 400\n console.log(err.name); // BadRequestError\n console.log(err.headers); // {server: 'nginx', ...}\n } else {\n throw err;\n }\n});\n```\n\nError codes are as follows:\n\n| Status Code | Error Type |\n| ----------- | -------------------------- |\n| 400 | `BadRequestError` |\n| 401 | `AuthenticationError` |\n| 403 | `PermissionDeniedError` |\n| 404 | `NotFoundError` |\n| 422 | `UnprocessableEntityError` |\n| 429 | `RateLimitError` |\n| >=500 | `InternalServerError` |\n| N/A | `APIConnectionError` |\n\n### Retries\n\nCertain errors will be automatically retried 2 times by default, with a short exponential backoff.\nConnection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict,\n429 Rate Limit, and >=500 Internal errors will all be retried by default.\n\nYou can use the `maxRetries` option to configure or disable this:\n\n\n```js\n// Configure the default for all requests:\nconst client = new BrandDev({\n maxRetries: 0, // default is 2\n});\n\n// Or, configure per-request:\nawait client.brand.retrieve({ domain: 'REPLACE_ME' }, {\n maxRetries: 5,\n});\n```\n\n### Timeouts\n\nRequests time out after 1 minute by default. You can configure this with a `timeout` option:\n\n\n```ts\n// Configure the default for all requests:\nconst client = new BrandDev({\n timeout: 20 * 1000, // 20 seconds (default is 1 minute)\n});\n\n// Override per-request:\nawait client.brand.retrieve({ domain: 'REPLACE_ME' }, {\n timeout: 5 * 1000,\n});\n```\n\nOn timeout, an `APIConnectionTimeoutError` is thrown.\n\nNote that requests which time out will be [retried twice by default](#retries).\n\n\n\n\n\n## Advanced Usage\n\n### Accessing raw Response data (e.g., headers)\n\nThe \"raw\" `Response` returned by `fetch()` can be accessed through the `.asResponse()` method on the `APIPromise` type that all methods return.\nThis method returns as soon as the headers for a successful response are received and does not consume the response body, so you are free to write custom parsing or streaming logic.\n\nYou can also use the `.withResponse()` method to get the raw `Response` along with the parsed data.\nUnlike `.asResponse()` this method consumes the body, returning once it is parsed.\n\n\n```ts\nconst client = new BrandDev();\n\nconst response = await client.brand.retrieve({ domain: 'REPLACE_ME' }).asResponse();\nconsole.log(response.headers.get('X-My-Header'));\nconsole.log(response.statusText); // access the underlying Response object\n\nconst { data: brand, response: raw } = await client.brand\n .retrieve({ domain: 'REPLACE_ME' })\n .withResponse();\nconsole.log(raw.headers.get('X-My-Header'));\nconsole.log(brand.brand);\n```\n\n### Logging\n\n> [!IMPORTANT]\n> All log messages are intended for debugging only. The format and content of log messages\n> may change between releases.\n\n#### Log levels\n\nThe log level can be configured in two ways:\n\n1. Via the `BRAND_DEV_LOG` environment variable\n2. Using the `logLevel` client option (overrides the environment variable if set)\n\n```ts\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n logLevel: 'debug', // Show all log messages\n});\n```\n\nAvailable log levels, from most to least verbose:\n\n- `'debug'` - Show debug messages, info, warnings, and errors\n- `'info'` - Show info messages, warnings, and errors\n- `'warn'` - Show warnings and errors (default)\n- `'error'` - Show only errors\n- `'off'` - Disable all logging\n\nAt the `'debug'` level, all HTTP requests and responses are logged, including headers and bodies.\nSome authentication-related headers are redacted, but sensitive data in request and response bodies\nmay still be visible.\n\n#### Custom logger\n\nBy default, this library logs to `globalThis.console`. You can also provide a custom logger.\nMost logging libraries are supported, including [pino](https://www.npmjs.com/package/pino), [winston](https://www.npmjs.com/package/winston), [bunyan](https://www.npmjs.com/package/bunyan), [consola](https://www.npmjs.com/package/consola), [signale](https://www.npmjs.com/package/signale), and [@std/log](https://jsr.io/@std/log). If your logger doesn't work, please open an issue.\n\nWhen providing a custom logger, the `logLevel` option still controls which messages are emitted, messages\nbelow the configured level will not be sent to your logger.\n\n```ts\nimport BrandDev from 'brand.dev';\nimport pino from 'pino';\n\nconst logger = pino();\n\nconst client = new BrandDev({\n logger: logger.child({ name: 'BrandDev' }),\n logLevel: 'debug', // Send all messages to pino, allowing it to filter\n});\n```\n\n### Making custom/undocumented requests\n\nThis library is typed for convenient access to the documented API. If you need to access undocumented\nendpoints, params, or response properties, the library can still be used.\n\n#### Undocumented endpoints\n\nTo make requests to undocumented endpoints, you can use `client.get`, `client.post`, and other HTTP verbs.\nOptions on the client, such as retries, will be respected when making these requests.\n\n```ts\nawait client.post('/some/path', {\n body: { some_prop: 'foo' },\n query: { some_query_arg: 'bar' },\n});\n```\n\n#### Undocumented request params\n\nTo make requests using undocumented parameters, you may use `// @ts-expect-error` on the undocumented\nparameter. This library doesn't validate at runtime that the request matches the type, so any extra values you\nsend will be sent as-is.\n\n```ts\nclient.brand.retrieve({\n // ...\n // @ts-expect-error baz is not yet public\n baz: 'undocumented option',\n});\n```\n\nFor requests with the `GET` verb, any extra params will be in the query, all other requests will send the\nextra param in the body.\n\nIf you want to explicitly send an extra argument, you can do so with the `query`, `body`, and `headers` request\noptions.\n\n#### Undocumented response properties\n\nTo access undocumented response properties, you may access the response object with `// @ts-expect-error` on\nthe response object, or cast the response object to the requisite type. Like the request params, we do not\nvalidate or strip extra properties from the response from the API.\n\n### Customizing the fetch client\n\nBy default, this library expects a global `fetch` function is defined.\n\nIf you want to use a different `fetch` function, you can either polyfill the global:\n\n```ts\nimport fetch from 'my-fetch';\n\nglobalThis.fetch = fetch;\n```\n\nOr pass it to the client:\n\n```ts\nimport BrandDev from 'brand.dev';\nimport fetch from 'my-fetch';\n\nconst client = new BrandDev({ fetch });\n```\n\n### Fetch options\n\nIf you want to set custom `fetch` options without overriding the `fetch` function, you can provide a `fetchOptions` object when instantiating the client or making a request. (Request-specific options override client options.)\n\n```ts\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n fetchOptions: {\n // `RequestInit` options\n },\n});\n```\n\n#### Configuring proxies\n\nTo modify proxy behavior, you can provide custom `fetchOptions` that add runtime-specific proxy\noptions to requests:\n\n **Node** [[docs](https://github.com/nodejs/undici/blob/main/docs/docs/api/ProxyAgent.md#example---proxyagent-with-fetch)]\n\n```ts\nimport BrandDev from 'brand.dev';\nimport * as undici from 'undici';\n\nconst proxyAgent = new undici.ProxyAgent('http://localhost:8888');\nconst client = new BrandDev({\n fetchOptions: {\n dispatcher: proxyAgent,\n },\n});\n```\n\n **Bun** [[docs](https://bun.sh/guides/http/proxy)]\n\n```ts\nimport BrandDev from 'brand.dev';\n\nconst client = new BrandDev({\n fetchOptions: {\n proxy: 'http://localhost:8888',\n },\n});\n```\n\n **Deno** [[docs](https://docs.deno.com/api/deno/~/Deno.createHttpClient)]\n\n```ts\nimport BrandDev from 'npm:brand.dev';\n\nconst httpClient = Deno.createHttpClient({ proxy: { url: 'http://localhost:8888' } });\nconst client = new BrandDev({\n fetchOptions: {\n client: httpClient,\n },\n});\n```\n\n## Frequently Asked Questions\n\n## Semantic versioning\n\nThis package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:\n\n1. Changes that only affect static types, without breaking runtime behavior.\n2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_\n3. Changes that we do not expect to impact the vast majority of users in practice.\n\nWe take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.\n\nWe are keen for your feedback; please open an [issue](https://www.github.com/context-dot-dev/deprecated-brand-typescript-sdk/issues) with questions, bugs, or suggestions.\n\n## Requirements\n\nTypeScript >= 4.9 is supported.\n\nThe following runtimes are supported:\n\n- Web browsers (Up-to-date Chrome, Firefox, Safari, Edge, and more)\n- Node.js 20 LTS or later ([non-EOL](https://endoflife.date/nodejs)) versions.\n- Deno v1.28.0 or higher.\n- Bun 1.0 or later.\n- Cloudflare Workers.\n- Vercel Edge Runtime.\n- Jest 28 or greater with the `\"node\"` environment (`\"jsdom\"` is not supported at this time).\n- Nitro v2.6 or greater.\n\nNote that React Native is not supported at this time.\n\nIf you are interested in other runtime environments, please open or upvote an issue on GitHub.\n\n## Contributing\n\nSee [the contributing documentation](./CONTRIBUTING.md).\n", }, ]; diff --git a/packages/mcp-server/src/methods.ts b/packages/mcp-server/src/methods.ts index c5ab8ae0..ce712dfb 100644 --- a/packages/mcp-server/src/methods.ts +++ b/packages/mcp-server/src/methods.ts @@ -34,12 +34,6 @@ export const sdkMethods: SdkMethod[] = [ httpMethod: 'post', httpPath: '/brand/ai/query', }, - { - clientCallName: 'client.brand.fonts', - fullyQualifiedName: 'brand.fonts', - httpMethod: 'get', - httpPath: '/brand/fonts', - }, { clientCallName: 'client.brand.identifyFromTransaction', fullyQualifiedName: 'brand.identifyFromTransaction', @@ -82,30 +76,12 @@ export const sdkMethods: SdkMethod[] = [ httpMethod: 'get', httpPath: '/brand/retrieve-by-ticker', }, - { - clientCallName: 'client.brand.retrieveNaics', - fullyQualifiedName: 'brand.retrieveNaics', - httpMethod: 'get', - httpPath: '/brand/naics', - }, { clientCallName: 'client.brand.retrieveSimplified', fullyQualifiedName: 'brand.retrieveSimplified', httpMethod: 'get', httpPath: '/brand/retrieve-simplified', }, - { - clientCallName: 'client.brand.screenshot', - fullyQualifiedName: 'brand.screenshot', - httpMethod: 'get', - httpPath: '/brand/screenshot', - }, - { - clientCallName: 'client.brand.styleguide', - fullyQualifiedName: 'brand.styleguide', - httpMethod: 'get', - httpPath: '/brand/styleguide', - }, { clientCallName: 'client.brand.webScrapeHTML', fullyQualifiedName: 'brand.webScrapeHTML', diff --git a/packages/mcp-server/src/server.ts b/packages/mcp-server/src/server.ts index ad14b57e..6a1558a2 100644 --- a/packages/mcp-server/src/server.ts +++ b/packages/mcp-server/src/server.ts @@ -28,7 +28,7 @@ export const newMcpServer = async ({ new McpServer( { name: 'brand_dev_api', - version: '0.34.0', + version: '0.35.0', }, { instructions: await getInstructions({ stainlessApiKey, customInstructionsPath }), diff --git a/packages/mcp-server/src/util.ts b/packages/mcp-server/src/util.ts index 40ed5501..069a2b47 100644 --- a/packages/mcp-server/src/util.ts +++ b/packages/mcp-server/src/util.ts @@ -2,9 +2,9 @@ export const readEnv = (env: string): string | undefined => { if (typeof (globalThis as any).process !== 'undefined') { - return (globalThis as any).process.env?.[env]?.trim(); + return (globalThis as any).process.env?.[env]?.trim() || undefined; } else if (typeof (globalThis as any).Deno !== 'undefined') { - return (globalThis as any).Deno.env?.get?.(env)?.trim(); + return (globalThis as any).Deno.env?.get?.(env)?.trim() || undefined; } return; }; diff --git a/packages/mcp-server/yarn.lock b/packages/mcp-server/yarn.lock index c7e37692..c6b17b02 100644 --- a/packages/mcp-server/yarn.lock +++ b/packages/mcp-server/yarn.lock @@ -3921,9 +3921,9 @@ ts-node@^10.5.0: v8-compile-cache-lib "^3.0.1" yn "3.1.1" -"tsc-multi@https://github.com/stainless-api/tsc-multi/releases/download/v1.1.9/tsc-multi.tgz": - version "1.1.9" - resolved "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.9/tsc-multi.tgz#777f6f5d9e26bf0e94e5170990dd3a841d6707cd" +"tsc-multi@https://github.com/stainless-api/tsc-multi/releases/download/v1.1.11/tsc-multi.tgz": + version "1.1.11" + resolved "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.11/tsc-multi.tgz#010247051be13b55abdc98f787c017285149f4f2" dependencies: debug "^4.3.7" fast-glob "^3.3.2" diff --git a/scripts/bootstrap b/scripts/bootstrap index a8b69ff3..2e315f53 100755 --- a/scripts/bootstrap +++ b/scripts/bootstrap @@ -4,7 +4,7 @@ set -e cd "$(dirname "$0")/.." -if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ] && [ -t 0 ]; then +if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "${SKIP_BREW:-}" != "1" ] && [ -t 0 ]; then brew bundle check >/dev/null 2>&1 || { echo -n "==> Install Homebrew dependencies? (y/N): " read -r response diff --git a/scripts/build b/scripts/build index c310f9b9..35b052d3 100755 --- a/scripts/build +++ b/scripts/build @@ -52,6 +52,6 @@ fi # build all sub-packages for dir in packages/*; do if [ -d "$dir" ]; then - (cd "$dir" && yarn install && yarn build) + (cd "$dir" && yarn install --pure-lockfile && yarn build) fi done diff --git a/scripts/fast-format b/scripts/fast-format index 53721ac0..f1873aef 100755 --- a/scripts/fast-format +++ b/scripts/fast-format @@ -31,10 +31,7 @@ if ! [ -z "$ESLINT_FILES" ]; then fi echo "==> Running prettier --write" -# format things eslint didn't -PRETTIER_FILES="$(grep '\.\(js\|json\)$' "$FILE_LIST" || true)" -if ! [ -z "$PRETTIER_FILES" ]; then - echo "$PRETTIER_FILES" | xargs ./node_modules/.bin/prettier \ - --write --cache --cache-strategy metadata --no-error-on-unmatched-pattern \ - '!**/dist' '!**/*.ts' '!**/*.mts' '!**/*.cts' '!**/*.js' '!**/*.mjs' '!**/*.cjs' +if ! [ -z "$FILE_LIST" ]; then + cat "$FILE_LIST" | xargs ./node_modules/.bin/prettier \ + --write --cache --cache-strategy metadata --no-error-on-unmatched-pattern --ignore-unknown fi diff --git a/scripts/format b/scripts/format index 7a756401..b1b2c17a 100755 --- a/scripts/format +++ b/scripts/format @@ -8,5 +8,4 @@ echo "==> Running eslint --fix" ./node_modules/.bin/eslint --fix . echo "==> Running prettier --write" -# format things eslint didn't -./node_modules/.bin/prettier --write --cache --cache-strategy metadata . '!**/dist' '!**/*.ts' '!**/*.mts' '!**/*.cts' '!**/*.js' '!**/*.mjs' '!**/*.cjs' +./node_modules/.bin/prettier --write --cache --cache-strategy metadata . diff --git a/scripts/lint b/scripts/lint index 3ffb78a6..1f532548 100755 --- a/scripts/lint +++ b/scripts/lint @@ -4,6 +4,9 @@ set -e cd "$(dirname "$0")/.." +echo "==> Running prettier --check" +./node_modules/.bin/prettier --check . + echo "==> Running eslint" ./node_modules/.bin/eslint . diff --git a/scripts/utils/postprocess-files.cjs b/scripts/utils/postprocess-files.cjs index deae575e..a8cdeb7c 100644 --- a/scripts/utils/postprocess-files.cjs +++ b/scripts/utils/postprocess-files.cjs @@ -23,12 +23,19 @@ async function postprocess() { // strip out lib="dom", types="node", and types="react" references; these // are needed at build time, but would pollute the user's TS environment - const transformed = code.replace( + let transformed = code.replace( /^ *\/\/\/ * ' '.repeat(match.length - 1) + '\n', ); + // TypeScript's declaration emitter collapses /** @ts-ignore */ onto the same + // line as the type declaration, which doesn't work. So we convert to // @ts-ignore + // on its own line to properly suppresses errors. + if (file.endsWith('.d.ts') || file.endsWith('.d.mts') || file.endsWith('.d.cts')) { + transformed = transformed.replace(/\/\*\* @ts-ignore\b[^*]*\*\/ /gm, '// @ts-ignore\n'); + } + if (transformed !== code) { console.error(`wrote ${path.relative(process.cwd(), file)}`); await fs.promises.writeFile(file, transformed, 'utf8'); diff --git a/src/client.ts b/src/client.ts index 59d68ebd..7f92b6b6 100644 --- a/src/client.ts +++ b/src/client.ts @@ -25,8 +25,6 @@ import { BrandAIProductsResponse, BrandAIQueryParams, BrandAIQueryResponse, - BrandFontsParams, - BrandFontsResponse, BrandIdentifyFromTransactionParams, BrandIdentifyFromTransactionResponse, BrandPrefetchByEmailParams, @@ -41,16 +39,10 @@ import { BrandRetrieveByNameResponse, BrandRetrieveByTickerParams, BrandRetrieveByTickerResponse, - BrandRetrieveNaicsParams, - BrandRetrieveNaicsResponse, BrandRetrieveParams, BrandRetrieveResponse, BrandRetrieveSimplifiedParams, BrandRetrieveSimplifiedResponse, - BrandScreenshotParams, - BrandScreenshotResponse, - BrandStyleguideParams, - BrandStyleguideResponse, BrandWebScrapeHTMLParams, BrandWebScrapeHTMLResponse, BrandWebScrapeImagesParams, @@ -210,6 +202,18 @@ export class BrandDev { this.fetch = options.fetch ?? Shims.getDefaultFetch(); this.#encoder = Opts.FallbackEncoder; + const customHeadersEnv = readEnv('BRAND_DEV_CUSTOM_HEADERS'); + if (customHeadersEnv) { + const parsed: Record = {}; + for (const line of customHeadersEnv.split('\n')) { + const colon = line.indexOf(':'); + if (colon >= 0) { + parsed[line.substring(0, colon).trim()] = line.substring(colon + 1).trim(); + } + } + options.defaultHeaders = { ...parsed, ...options.defaultHeaders }; + } + this._options = options; this.apiKey = apiKey; @@ -253,9 +257,6 @@ export class BrandDev { return buildHeaders([{ Authorization: `Bearer ${this.apiKey}` }]); } - /** - * Basic re-implementation of `qs.stringify` for primitive types. - */ protected stringifyQuery(query: object | Record): string { return stringifyQuery(query); } @@ -774,7 +775,6 @@ export declare namespace BrandDev { type BrandAIProductResponse as BrandAIProductResponse, type BrandAIProductsResponse as BrandAIProductsResponse, type BrandAIQueryResponse as BrandAIQueryResponse, - type BrandFontsResponse as BrandFontsResponse, type BrandIdentifyFromTransactionResponse as BrandIdentifyFromTransactionResponse, type BrandPrefetchResponse as BrandPrefetchResponse, type BrandPrefetchByEmailResponse as BrandPrefetchByEmailResponse, @@ -782,10 +782,7 @@ export declare namespace BrandDev { type BrandRetrieveByIsinResponse as BrandRetrieveByIsinResponse, type BrandRetrieveByNameResponse as BrandRetrieveByNameResponse, type BrandRetrieveByTickerResponse as BrandRetrieveByTickerResponse, - type BrandRetrieveNaicsResponse as BrandRetrieveNaicsResponse, type BrandRetrieveSimplifiedResponse as BrandRetrieveSimplifiedResponse, - type BrandScreenshotResponse as BrandScreenshotResponse, - type BrandStyleguideResponse as BrandStyleguideResponse, type BrandWebScrapeHTMLResponse as BrandWebScrapeHTMLResponse, type BrandWebScrapeImagesResponse as BrandWebScrapeImagesResponse, type BrandWebScrapeMdResponse as BrandWebScrapeMdResponse, @@ -794,7 +791,6 @@ export declare namespace BrandDev { type BrandAIProductParams as BrandAIProductParams, type BrandAIProductsParams as BrandAIProductsParams, type BrandAIQueryParams as BrandAIQueryParams, - type BrandFontsParams as BrandFontsParams, type BrandIdentifyFromTransactionParams as BrandIdentifyFromTransactionParams, type BrandPrefetchParams as BrandPrefetchParams, type BrandPrefetchByEmailParams as BrandPrefetchByEmailParams, @@ -802,10 +798,7 @@ export declare namespace BrandDev { type BrandRetrieveByIsinParams as BrandRetrieveByIsinParams, type BrandRetrieveByNameParams as BrandRetrieveByNameParams, type BrandRetrieveByTickerParams as BrandRetrieveByTickerParams, - type BrandRetrieveNaicsParams as BrandRetrieveNaicsParams, type BrandRetrieveSimplifiedParams as BrandRetrieveSimplifiedParams, - type BrandScreenshotParams as BrandScreenshotParams, - type BrandStyleguideParams as BrandStyleguideParams, type BrandWebScrapeHTMLParams as BrandWebScrapeHTMLParams, type BrandWebScrapeImagesParams as BrandWebScrapeImagesParams, type BrandWebScrapeMdParams as BrandWebScrapeMdParams, diff --git a/src/internal/qs/LICENSE.md b/src/internal/qs/LICENSE.md new file mode 100644 index 00000000..3fda1573 --- /dev/null +++ b/src/internal/qs/LICENSE.md @@ -0,0 +1,13 @@ +BSD 3-Clause License + +Copyright (c) 2014, Nathan LaFreniere and other [contributors](https://github.com/puruvj/neoqs/graphs/contributors) All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/internal/qs/README.md b/src/internal/qs/README.md new file mode 100644 index 00000000..67ae04ec --- /dev/null +++ b/src/internal/qs/README.md @@ -0,0 +1,3 @@ +# qs + +This is a vendored version of [neoqs](https://github.com/PuruVJ/neoqs) which is a TypeScript rewrite of [qs](https://github.com/ljharb/qs), a query string library. diff --git a/src/internal/qs/formats.ts b/src/internal/qs/formats.ts new file mode 100644 index 00000000..e76a742f --- /dev/null +++ b/src/internal/qs/formats.ts @@ -0,0 +1,10 @@ +import type { Format } from './types'; + +export const default_format: Format = 'RFC3986'; +export const default_formatter = (v: PropertyKey) => String(v); +export const formatters: Record string> = { + RFC1738: (v: PropertyKey) => String(v).replace(/%20/g, '+'), + RFC3986: default_formatter, +}; +export const RFC1738 = 'RFC1738'; +export const RFC3986 = 'RFC3986'; diff --git a/src/internal/qs/index.ts b/src/internal/qs/index.ts new file mode 100644 index 00000000..c3a3620d --- /dev/null +++ b/src/internal/qs/index.ts @@ -0,0 +1,13 @@ +import { default_format, formatters, RFC1738, RFC3986 } from './formats'; + +const formats = { + formatters, + RFC1738, + RFC3986, + default: default_format, +}; + +export { stringify } from './stringify'; +export { formats }; + +export type { DefaultDecoder, DefaultEncoder, Format, ParseOptions, StringifyOptions } from './types'; diff --git a/src/internal/qs/stringify.ts b/src/internal/qs/stringify.ts new file mode 100644 index 00000000..7e71387f --- /dev/null +++ b/src/internal/qs/stringify.ts @@ -0,0 +1,385 @@ +import { encode, is_buffer, maybe_map, has } from './utils'; +import { default_format, default_formatter, formatters } from './formats'; +import type { NonNullableProperties, StringifyOptions } from './types'; +import { isArray } from '../utils/values'; + +const array_prefix_generators = { + brackets(prefix: PropertyKey) { + return String(prefix) + '[]'; + }, + comma: 'comma', + indices(prefix: PropertyKey, key: string) { + return String(prefix) + '[' + key + ']'; + }, + repeat(prefix: PropertyKey) { + return String(prefix); + }, +}; + +const push_to_array = function (arr: any[], value_or_array: any) { + Array.prototype.push.apply(arr, isArray(value_or_array) ? value_or_array : [value_or_array]); +}; + +let toISOString; + +const defaults = { + addQueryPrefix: false, + allowDots: false, + allowEmptyArrays: false, + arrayFormat: 'indices', + charset: 'utf-8', + charsetSentinel: false, + delimiter: '&', + encode: true, + encodeDotInKeys: false, + encoder: encode, + encodeValuesOnly: false, + format: default_format, + formatter: default_formatter, + /** @deprecated */ + indices: false, + serializeDate(date) { + return (toISOString ??= Function.prototype.call.bind(Date.prototype.toISOString))(date); + }, + skipNulls: false, + strictNullHandling: false, +} as NonNullableProperties; + +function is_non_nullish_primitive(v: unknown): v is string | number | boolean | symbol | bigint { + return ( + typeof v === 'string' || + typeof v === 'number' || + typeof v === 'boolean' || + typeof v === 'symbol' || + typeof v === 'bigint' + ); +} + +const sentinel = {}; + +function inner_stringify( + object: any, + prefix: PropertyKey, + generateArrayPrefix: StringifyOptions['arrayFormat'] | ((prefix: string, key: string) => string), + commaRoundTrip: boolean, + allowEmptyArrays: boolean, + strictNullHandling: boolean, + skipNulls: boolean, + encodeDotInKeys: boolean, + encoder: StringifyOptions['encoder'], + filter: StringifyOptions['filter'], + sort: StringifyOptions['sort'], + allowDots: StringifyOptions['allowDots'], + serializeDate: StringifyOptions['serializeDate'], + format: StringifyOptions['format'], + formatter: StringifyOptions['formatter'], + encodeValuesOnly: boolean, + charset: StringifyOptions['charset'], + sideChannel: WeakMap, +) { + let obj = object; + + let tmp_sc = sideChannel; + let step = 0; + let find_flag = false; + while ((tmp_sc = tmp_sc.get(sentinel)) !== void undefined && !find_flag) { + // Where object last appeared in the ref tree + const pos = tmp_sc.get(object); + step += 1; + if (typeof pos !== 'undefined') { + if (pos === step) { + throw new RangeError('Cyclic object value'); + } else { + find_flag = true; // Break while + } + } + if (typeof tmp_sc.get(sentinel) === 'undefined') { + step = 0; + } + } + + if (typeof filter === 'function') { + obj = filter(prefix, obj); + } else if (obj instanceof Date) { + obj = serializeDate?.(obj); + } else if (generateArrayPrefix === 'comma' && isArray(obj)) { + obj = maybe_map(obj, function (value) { + if (value instanceof Date) { + return serializeDate?.(value); + } + return value; + }); + } + + if (obj === null) { + if (strictNullHandling) { + return encoder && !encodeValuesOnly ? + // @ts-expect-error + encoder(prefix, defaults.encoder, charset, 'key', format) + : prefix; + } + + obj = ''; + } + + if (is_non_nullish_primitive(obj) || is_buffer(obj)) { + if (encoder) { + const key_value = + encodeValuesOnly ? prefix + // @ts-expect-error + : encoder(prefix, defaults.encoder, charset, 'key', format); + return [ + formatter?.(key_value) + + '=' + + // @ts-expect-error + formatter?.(encoder(obj, defaults.encoder, charset, 'value', format)), + ]; + } + return [formatter?.(prefix) + '=' + formatter?.(String(obj))]; + } + + const values: string[] = []; + + if (typeof obj === 'undefined') { + return values; + } + + let obj_keys; + if (generateArrayPrefix === 'comma' && isArray(obj)) { + // we need to join elements in + if (encodeValuesOnly && encoder) { + // @ts-expect-error values only + obj = maybe_map(obj, encoder); + } + obj_keys = [{ value: obj.length > 0 ? obj.join(',') || null : void undefined }]; + } else if (isArray(filter)) { + obj_keys = filter; + } else { + const keys = Object.keys(obj); + obj_keys = sort ? keys.sort(sort) : keys; + } + + const encoded_prefix = encodeDotInKeys ? String(prefix).replace(/\./g, '%2E') : String(prefix); + + const adjusted_prefix = + commaRoundTrip && isArray(obj) && obj.length === 1 ? encoded_prefix + '[]' : encoded_prefix; + + if (allowEmptyArrays && isArray(obj) && obj.length === 0) { + return adjusted_prefix + '[]'; + } + + for (let j = 0; j < obj_keys.length; ++j) { + const key = obj_keys[j]; + const value = + // @ts-ignore + typeof key === 'object' && typeof key.value !== 'undefined' ? key.value : obj[key as any]; + + if (skipNulls && value === null) { + continue; + } + + // @ts-ignore + const encoded_key = allowDots && encodeDotInKeys ? (key as any).replace(/\./g, '%2E') : key; + const key_prefix = + isArray(obj) ? + typeof generateArrayPrefix === 'function' ? + generateArrayPrefix(adjusted_prefix, encoded_key) + : adjusted_prefix + : adjusted_prefix + (allowDots ? '.' + encoded_key : '[' + encoded_key + ']'); + + sideChannel.set(object, step); + const valueSideChannel = new WeakMap(); + valueSideChannel.set(sentinel, sideChannel); + push_to_array( + values, + inner_stringify( + value, + key_prefix, + generateArrayPrefix, + commaRoundTrip, + allowEmptyArrays, + strictNullHandling, + skipNulls, + encodeDotInKeys, + // @ts-ignore + generateArrayPrefix === 'comma' && encodeValuesOnly && isArray(obj) ? null : encoder, + filter, + sort, + allowDots, + serializeDate, + format, + formatter, + encodeValuesOnly, + charset, + valueSideChannel, + ), + ); + } + + return values; +} + +function normalize_stringify_options( + opts: StringifyOptions = defaults, +): NonNullableProperties> & { indices?: boolean } { + if (typeof opts.allowEmptyArrays !== 'undefined' && typeof opts.allowEmptyArrays !== 'boolean') { + throw new TypeError('`allowEmptyArrays` option can only be `true` or `false`, when provided'); + } + + if (typeof opts.encodeDotInKeys !== 'undefined' && typeof opts.encodeDotInKeys !== 'boolean') { + throw new TypeError('`encodeDotInKeys` option can only be `true` or `false`, when provided'); + } + + if (opts.encoder !== null && typeof opts.encoder !== 'undefined' && typeof opts.encoder !== 'function') { + throw new TypeError('Encoder has to be a function.'); + } + + const charset = opts.charset || defaults.charset; + if (typeof opts.charset !== 'undefined' && opts.charset !== 'utf-8' && opts.charset !== 'iso-8859-1') { + throw new TypeError('The charset option must be either utf-8, iso-8859-1, or undefined'); + } + + let format = default_format; + if (typeof opts.format !== 'undefined') { + if (!has(formatters, opts.format)) { + throw new TypeError('Unknown format option provided.'); + } + format = opts.format; + } + const formatter = formatters[format]; + + let filter = defaults.filter; + if (typeof opts.filter === 'function' || isArray(opts.filter)) { + filter = opts.filter; + } + + let arrayFormat: StringifyOptions['arrayFormat']; + if (opts.arrayFormat && opts.arrayFormat in array_prefix_generators) { + arrayFormat = opts.arrayFormat; + } else if ('indices' in opts) { + arrayFormat = opts.indices ? 'indices' : 'repeat'; + } else { + arrayFormat = defaults.arrayFormat; + } + + if ('commaRoundTrip' in opts && typeof opts.commaRoundTrip !== 'boolean') { + throw new TypeError('`commaRoundTrip` must be a boolean, or absent'); + } + + const allowDots = + typeof opts.allowDots === 'undefined' ? + !!opts.encodeDotInKeys === true ? + true + : defaults.allowDots + : !!opts.allowDots; + + return { + addQueryPrefix: typeof opts.addQueryPrefix === 'boolean' ? opts.addQueryPrefix : defaults.addQueryPrefix, + // @ts-ignore + allowDots: allowDots, + allowEmptyArrays: + typeof opts.allowEmptyArrays === 'boolean' ? !!opts.allowEmptyArrays : defaults.allowEmptyArrays, + arrayFormat: arrayFormat, + charset: charset, + charsetSentinel: + typeof opts.charsetSentinel === 'boolean' ? opts.charsetSentinel : defaults.charsetSentinel, + commaRoundTrip: !!opts.commaRoundTrip, + delimiter: typeof opts.delimiter === 'undefined' ? defaults.delimiter : opts.delimiter, + encode: typeof opts.encode === 'boolean' ? opts.encode : defaults.encode, + encodeDotInKeys: + typeof opts.encodeDotInKeys === 'boolean' ? opts.encodeDotInKeys : defaults.encodeDotInKeys, + encoder: typeof opts.encoder === 'function' ? opts.encoder : defaults.encoder, + encodeValuesOnly: + typeof opts.encodeValuesOnly === 'boolean' ? opts.encodeValuesOnly : defaults.encodeValuesOnly, + filter: filter, + format: format, + formatter: formatter, + serializeDate: typeof opts.serializeDate === 'function' ? opts.serializeDate : defaults.serializeDate, + skipNulls: typeof opts.skipNulls === 'boolean' ? opts.skipNulls : defaults.skipNulls, + // @ts-ignore + sort: typeof opts.sort === 'function' ? opts.sort : null, + strictNullHandling: + typeof opts.strictNullHandling === 'boolean' ? opts.strictNullHandling : defaults.strictNullHandling, + }; +} + +export function stringify(object: any, opts: StringifyOptions = {}) { + let obj = object; + const options = normalize_stringify_options(opts); + + let obj_keys: PropertyKey[] | undefined; + let filter; + + if (typeof options.filter === 'function') { + filter = options.filter; + obj = filter('', obj); + } else if (isArray(options.filter)) { + filter = options.filter; + obj_keys = filter; + } + + const keys: string[] = []; + + if (typeof obj !== 'object' || obj === null) { + return ''; + } + + const generateArrayPrefix = array_prefix_generators[options.arrayFormat]; + const commaRoundTrip = generateArrayPrefix === 'comma' && options.commaRoundTrip; + + if (!obj_keys) { + obj_keys = Object.keys(obj); + } + + if (options.sort) { + obj_keys.sort(options.sort); + } + + const sideChannel = new WeakMap(); + for (let i = 0; i < obj_keys.length; ++i) { + const key = obj_keys[i]!; + + if (options.skipNulls && obj[key] === null) { + continue; + } + push_to_array( + keys, + inner_stringify( + obj[key], + key, + // @ts-expect-error + generateArrayPrefix, + commaRoundTrip, + options.allowEmptyArrays, + options.strictNullHandling, + options.skipNulls, + options.encodeDotInKeys, + options.encode ? options.encoder : null, + options.filter, + options.sort, + options.allowDots, + options.serializeDate, + options.format, + options.formatter, + options.encodeValuesOnly, + options.charset, + sideChannel, + ), + ); + } + + const joined = keys.join(options.delimiter); + let prefix = options.addQueryPrefix === true ? '?' : ''; + + if (options.charsetSentinel) { + if (options.charset === 'iso-8859-1') { + // encodeURIComponent('✓'), the "numeric entity" representation of a checkmark + prefix += 'utf8=%26%2310003%3B&'; + } else { + // encodeURIComponent('✓') + prefix += 'utf8=%E2%9C%93&'; + } + } + + return joined.length > 0 ? prefix + joined : ''; +} diff --git a/src/internal/qs/types.ts b/src/internal/qs/types.ts new file mode 100644 index 00000000..7c28dbb4 --- /dev/null +++ b/src/internal/qs/types.ts @@ -0,0 +1,71 @@ +export type Format = 'RFC1738' | 'RFC3986'; + +export type DefaultEncoder = (str: any, defaultEncoder?: any, charset?: string) => string; +export type DefaultDecoder = (str: string, decoder?: any, charset?: string) => string; + +export type BooleanOptional = boolean | undefined; + +export type StringifyBaseOptions = { + delimiter?: string; + allowDots?: boolean; + encodeDotInKeys?: boolean; + strictNullHandling?: boolean; + skipNulls?: boolean; + encode?: boolean; + encoder?: ( + str: any, + defaultEncoder: DefaultEncoder, + charset: string, + type: 'key' | 'value', + format?: Format, + ) => string; + filter?: Array | ((prefix: PropertyKey, value: any) => any); + arrayFormat?: 'indices' | 'brackets' | 'repeat' | 'comma'; + indices?: boolean; + sort?: ((a: PropertyKey, b: PropertyKey) => number) | null; + serializeDate?: (d: Date) => string; + format?: 'RFC1738' | 'RFC3986'; + formatter?: (str: PropertyKey) => string; + encodeValuesOnly?: boolean; + addQueryPrefix?: boolean; + charset?: 'utf-8' | 'iso-8859-1'; + charsetSentinel?: boolean; + allowEmptyArrays?: boolean; + commaRoundTrip?: boolean; +}; + +export type StringifyOptions = StringifyBaseOptions; + +export type ParseBaseOptions = { + comma?: boolean; + delimiter?: string | RegExp; + depth?: number | false; + decoder?: (str: string, defaultDecoder: DefaultDecoder, charset: string, type: 'key' | 'value') => any; + arrayLimit?: number; + parseArrays?: boolean; + plainObjects?: boolean; + allowPrototypes?: boolean; + allowSparse?: boolean; + parameterLimit?: number; + strictDepth?: boolean; + strictNullHandling?: boolean; + ignoreQueryPrefix?: boolean; + charset?: 'utf-8' | 'iso-8859-1'; + charsetSentinel?: boolean; + interpretNumericEntities?: boolean; + allowEmptyArrays?: boolean; + duplicates?: 'combine' | 'first' | 'last'; + allowDots?: boolean; + decodeDotInKeys?: boolean; +}; + +export type ParseOptions = ParseBaseOptions; + +export type ParsedQs = { + [key: string]: undefined | string | string[] | ParsedQs | ParsedQs[]; +}; + +// Type to remove null or undefined union from each property +export type NonNullableProperties = { + [K in keyof T]-?: Exclude; +}; diff --git a/src/internal/qs/utils.ts b/src/internal/qs/utils.ts new file mode 100644 index 00000000..4cd56579 --- /dev/null +++ b/src/internal/qs/utils.ts @@ -0,0 +1,265 @@ +import { RFC1738 } from './formats'; +import type { DefaultEncoder, Format } from './types'; +import { isArray } from '../utils/values'; + +export let has = (obj: object, key: PropertyKey): boolean => ( + (has = (Object as any).hasOwn ?? Function.prototype.call.bind(Object.prototype.hasOwnProperty)), + has(obj, key) +); + +const hex_table = /* @__PURE__ */ (() => { + const array = []; + for (let i = 0; i < 256; ++i) { + array.push('%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase()); + } + + return array; +})(); + +function compact_queue>(queue: Array<{ obj: T; prop: string }>) { + while (queue.length > 1) { + const item = queue.pop(); + if (!item) continue; + + const obj = item.obj[item.prop]; + + if (isArray(obj)) { + const compacted: unknown[] = []; + + for (let j = 0; j < obj.length; ++j) { + if (typeof obj[j] !== 'undefined') { + compacted.push(obj[j]); + } + } + + // @ts-ignore + item.obj[item.prop] = compacted; + } + } +} + +function array_to_object(source: any[], options: { plainObjects: boolean }) { + const obj = options && options.plainObjects ? Object.create(null) : {}; + for (let i = 0; i < source.length; ++i) { + if (typeof source[i] !== 'undefined') { + obj[i] = source[i]; + } + } + + return obj; +} + +export function merge( + target: any, + source: any, + options: { plainObjects?: boolean; allowPrototypes?: boolean } = {}, +) { + if (!source) { + return target; + } + + if (typeof source !== 'object') { + if (isArray(target)) { + target.push(source); + } else if (target && typeof target === 'object') { + if ((options && (options.plainObjects || options.allowPrototypes)) || !has(Object.prototype, source)) { + target[source] = true; + } + } else { + return [target, source]; + } + + return target; + } + + if (!target || typeof target !== 'object') { + return [target].concat(source); + } + + let mergeTarget = target; + if (isArray(target) && !isArray(source)) { + // @ts-ignore + mergeTarget = array_to_object(target, options); + } + + if (isArray(target) && isArray(source)) { + source.forEach(function (item, i) { + if (has(target, i)) { + const targetItem = target[i]; + if (targetItem && typeof targetItem === 'object' && item && typeof item === 'object') { + target[i] = merge(targetItem, item, options); + } else { + target.push(item); + } + } else { + target[i] = item; + } + }); + return target; + } + + return Object.keys(source).reduce(function (acc, key) { + const value = source[key]; + + if (has(acc, key)) { + acc[key] = merge(acc[key], value, options); + } else { + acc[key] = value; + } + return acc; + }, mergeTarget); +} + +export function assign_single_source(target: any, source: any) { + return Object.keys(source).reduce(function (acc, key) { + acc[key] = source[key]; + return acc; + }, target); +} + +export function decode(str: string, _: any, charset: string) { + const strWithoutPlus = str.replace(/\+/g, ' '); + if (charset === 'iso-8859-1') { + // unescape never throws, no try...catch needed: + return strWithoutPlus.replace(/%[0-9a-f]{2}/gi, unescape); + } + // utf-8 + try { + return decodeURIComponent(strWithoutPlus); + } catch (e) { + return strWithoutPlus; + } +} + +const limit = 1024; + +export const encode: ( + str: any, + defaultEncoder: DefaultEncoder, + charset: string, + type: 'key' | 'value', + format: Format, +) => string = (str, _defaultEncoder, charset, _kind, format: Format) => { + // This code was originally written by Brian White for the io.js core querystring library. + // It has been adapted here for stricter adherence to RFC 3986 + if (str.length === 0) { + return str; + } + + let string = str; + if (typeof str === 'symbol') { + string = Symbol.prototype.toString.call(str); + } else if (typeof str !== 'string') { + string = String(str); + } + + if (charset === 'iso-8859-1') { + return escape(string).replace(/%u[0-9a-f]{4}/gi, function ($0) { + return '%26%23' + parseInt($0.slice(2), 16) + '%3B'; + }); + } + + let out = ''; + for (let j = 0; j < string.length; j += limit) { + const segment = string.length >= limit ? string.slice(j, j + limit) : string; + const arr = []; + + for (let i = 0; i < segment.length; ++i) { + let c = segment.charCodeAt(i); + if ( + c === 0x2d || // - + c === 0x2e || // . + c === 0x5f || // _ + c === 0x7e || // ~ + (c >= 0x30 && c <= 0x39) || // 0-9 + (c >= 0x41 && c <= 0x5a) || // a-z + (c >= 0x61 && c <= 0x7a) || // A-Z + (format === RFC1738 && (c === 0x28 || c === 0x29)) // ( ) + ) { + arr[arr.length] = segment.charAt(i); + continue; + } + + if (c < 0x80) { + arr[arr.length] = hex_table[c]; + continue; + } + + if (c < 0x800) { + arr[arr.length] = hex_table[0xc0 | (c >> 6)]! + hex_table[0x80 | (c & 0x3f)]; + continue; + } + + if (c < 0xd800 || c >= 0xe000) { + arr[arr.length] = + hex_table[0xe0 | (c >> 12)]! + hex_table[0x80 | ((c >> 6) & 0x3f)] + hex_table[0x80 | (c & 0x3f)]; + continue; + } + + i += 1; + c = 0x10000 + (((c & 0x3ff) << 10) | (segment.charCodeAt(i) & 0x3ff)); + + arr[arr.length] = + hex_table[0xf0 | (c >> 18)]! + + hex_table[0x80 | ((c >> 12) & 0x3f)] + + hex_table[0x80 | ((c >> 6) & 0x3f)] + + hex_table[0x80 | (c & 0x3f)]; + } + + out += arr.join(''); + } + + return out; +}; + +export function compact(value: any) { + const queue = [{ obj: { o: value }, prop: 'o' }]; + const refs = []; + + for (let i = 0; i < queue.length; ++i) { + const item = queue[i]; + // @ts-ignore + const obj = item.obj[item.prop]; + + const keys = Object.keys(obj); + for (let j = 0; j < keys.length; ++j) { + const key = keys[j]!; + const val = obj[key]; + if (typeof val === 'object' && val !== null && refs.indexOf(val) === -1) { + queue.push({ obj: obj, prop: key }); + refs.push(val); + } + } + } + + compact_queue(queue); + + return value; +} + +export function is_regexp(obj: any) { + return Object.prototype.toString.call(obj) === '[object RegExp]'; +} + +export function is_buffer(obj: any) { + if (!obj || typeof obj !== 'object') { + return false; + } + + return !!(obj.constructor && obj.constructor.isBuffer && obj.constructor.isBuffer(obj)); +} + +export function combine(a: any, b: any) { + return [].concat(a, b); +} + +export function maybe_map(val: T[], fn: (v: T) => T) { + if (isArray(val)) { + const mapped = []; + for (let i = 0; i < val.length; i += 1) { + mapped.push(fn(val[i]!)); + } + return mapped; + } + return fn(val); +} diff --git a/src/internal/types.ts b/src/internal/types.ts index b668dfc0..a050513a 100644 --- a/src/internal/types.ts +++ b/src/internal/types.ts @@ -40,7 +40,6 @@ type OverloadedParameters = : T extends (...args: infer A) => unknown ? A : never; -/* eslint-disable */ /** * These imports attempt to get types from a parent package's dependencies. * Unresolved bare specifiers can trigger [automatic type acquisition][1] in some projects, which @@ -63,19 +62,18 @@ type OverloadedParameters = * * [1]: https://www.typescriptlang.org/tsconfig/#typeAcquisition */ -/** @ts-ignore For users with \@types/node */ +/** @ts-ignore For users with \@types/node */ /* prettier-ignore */ type UndiciTypesRequestInit = NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny; -/** @ts-ignore For users with undici */ +/** @ts-ignore For users with undici */ /* prettier-ignore */ type UndiciRequestInit = NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny; -/** @ts-ignore For users with \@types/bun */ +/** @ts-ignore For users with \@types/bun */ /* prettier-ignore */ type BunRequestInit = globalThis.FetchRequestInit; -/** @ts-ignore For users with node-fetch@2 */ +/** @ts-ignore For users with node-fetch@2 */ /* prettier-ignore */ type NodeFetch2RequestInit = NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny; -/** @ts-ignore For users with node-fetch@3, doesn't need file extension because types are at ./@types/index.d.ts */ +/** @ts-ignore For users with node-fetch@3, doesn't need file extension because types are at ./@types/index.d.ts */ /* prettier-ignore */ type NodeFetch3RequestInit = NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny; -/** @ts-ignore For users who use Deno */ +/** @ts-ignore For users who use Deno */ /* prettier-ignore */ type FetchRequestInit = NonNullable[1]>; -/* eslint-enable */ type RequestInits = | NotAny diff --git a/src/internal/utils/env.ts b/src/internal/utils/env.ts index 2d848007..cc5fa0fa 100644 --- a/src/internal/utils/env.ts +++ b/src/internal/utils/env.ts @@ -9,10 +9,10 @@ */ export const readEnv = (env: string): string | undefined => { if (typeof (globalThis as any).process !== 'undefined') { - return (globalThis as any).process.env?.[env]?.trim() ?? undefined; + return (globalThis as any).process.env?.[env]?.trim() || undefined; } if (typeof (globalThis as any).Deno !== 'undefined') { - return (globalThis as any).Deno.env?.get?.(env)?.trim(); + return (globalThis as any).Deno.env?.get?.(env)?.trim() || undefined; } return undefined; }; diff --git a/src/internal/utils/log.ts b/src/internal/utils/log.ts index bc741a94..83eb3815 100644 --- a/src/internal/utils/log.ts +++ b/src/internal/utils/log.ts @@ -107,6 +107,8 @@ export const formatRequestDetails = (details: { name, ( name.toLowerCase() === 'authorization' || + name.toLowerCase() === 'api-key' || + name.toLowerCase() === 'x-api-key' || name.toLowerCase() === 'cookie' || name.toLowerCase() === 'set-cookie' ) ? diff --git a/src/internal/utils/query.ts b/src/internal/utils/query.ts index 9dc06d9c..0139cacb 100644 --- a/src/internal/utils/query.ts +++ b/src/internal/utils/query.ts @@ -1,23 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { BrandDevError } from '../../core/error'; +import * as qs from '../qs/stringify'; -/** - * Basic re-implementation of `qs.stringify` for primitive types. - */ export function stringifyQuery(query: object | Record) { - return Object.entries(query) - .filter(([_, value]) => typeof value !== 'undefined') - .map(([key, value]) => { - if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { - return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`; - } - if (value === null) { - return `${encodeURIComponent(key)}=`; - } - throw new BrandDevError( - `Cannot stringify type ${typeof value}; Expected string, number, boolean, or null. If you need to pass nested query parameters, you can manually encode them, e.g. { query: { 'foo[key1]': value1, 'foo[key2]': value2 } }, and please open a GitHub issue requesting better support for your use case.`, - ); - }) - .join('&'); + return qs.stringify(query, { arrayFormat: 'comma' }); } diff --git a/src/resources/brand.ts b/src/resources/brand.ts index 1bac96a0..4c1fc6c6 100644 --- a/src/resources/brand.ts +++ b/src/resources/brand.ts @@ -14,18 +14,17 @@ export class Brand extends APIResource { } /** - * Beta feature: Given a single URL, determines if it is a product detail page, - * classifies the platform/product type, and extracts the product information. - * Supports Amazon, TikTok Shop, Etsy, and generic ecommerce sites. + * Given a single URL, determines if it is a product page and extracts the product + * information. */ aiProduct(body: BrandAIProductParams, options?: RequestOptions): APIPromise { return this._client.post('/brand/ai/product', { body, ...options }); } /** - * Beta feature: Extract product information from a brand's website. We will - * analyze the website and return a list of products with details such as name, - * description, image, pricing, features, and more. + * Extract product information from a brand's website. We will analyze the website + * and return a list of products with details such as name, description, image, + * pricing, features, and more. */ aiProducts(body: BrandAIProductsParams, options?: RequestOptions): APIPromise { return this._client.post('/brand/ai/products', { body, ...options }); @@ -40,14 +39,6 @@ export class Brand extends APIResource { return this._client.post('/brand/ai/query', { body, ...options }); } - /** - * Extract font information from a brand's website including font families, usage - * statistics, fallbacks, and element/word counts. - */ - fonts(query: BrandFontsParams, options?: RequestOptions): APIPromise { - return this._client.get('/brand/fonts', { query, ...options }); - } - /** * Endpoint specially designed for platforms that want to identify transaction data * by the transaction title. @@ -61,9 +52,7 @@ export class Brand extends APIResource { /** * Signal that you may fetch brand data for a particular domain soon to improve - * latency. This endpoint does not charge credits and is available for paid - * customers to optimize future requests. [You must be on a paid plan to use this - * endpoint] + * latency. */ prefetch(body: BrandPrefetchParams, options?: RequestOptions): APIPromise { return this._client.post('/brand/prefetch', { body, ...options }); @@ -73,9 +62,7 @@ export class Brand extends APIResource { * Signal that you may fetch brand data for a particular domain soon to improve * latency. This endpoint accepts an email address, extracts the domain from it, * validates that it's not a disposable or free email provider, and queues the - * domain for prefetching. This endpoint does not charge credits and is available - * for paid customers to optimize future requests. [You must be on a paid plan to - * use this endpoint] + * domain for prefetching. */ prefetchByEmail( body: BrandPrefetchByEmailParams, @@ -86,9 +73,8 @@ export class Brand extends APIResource { /** * Retrieve brand information using an email address while detecting disposable and - * free email addresses. This endpoint extracts the domain from the email address - * and returns brand data for that domain. Disposable and free email addresses - * (like gmail.com, yahoo.com) will throw a 422 error. + * free email addresses. Disposable and free email addresses (like gmail.com, + * yahoo.com) will throw a 422 error. */ retrieveByEmail( query: BrandRetrieveByEmailParams, @@ -99,8 +85,7 @@ export class Brand extends APIResource { /** * Retrieve brand information using an ISIN (International Securities - * Identification Number). This endpoint looks up the company associated with the - * ISIN and returns its brand data. + * Identification Number). */ retrieveByIsin( query: BrandRetrieveByIsinParams, @@ -110,8 +95,7 @@ export class Brand extends APIResource { } /** - * Retrieve brand information using a company name. This endpoint searches for the - * company by name and returns its brand data. + * Retrieve brand information using a company name. */ retrieveByName( query: BrandRetrieveByNameParams, @@ -121,8 +105,7 @@ export class Brand extends APIResource { } /** - * Retrieve brand information using a stock ticker symbol. This endpoint looks up - * the company associated with the ticker and returns its brand data. + * Retrieve brand information using a stock ticker symbol. */ retrieveByTicker( query: BrandRetrieveByTickerParams, @@ -131,20 +114,10 @@ export class Brand extends APIResource { return this._client.get('/brand/retrieve-by-ticker', { query, ...options }); } - /** - * Endpoint to classify any brand into a 2022 NAICS code. - */ - retrieveNaics( - query: BrandRetrieveNaicsParams, - options?: RequestOptions, - ): APIPromise { - return this._client.get('/brand/naics', { query, ...options }); - } - /** * Returns a simplified version of brand data containing only essential - * information: domain, title, colors, logos, and backdrops. This endpoint is - * optimized for faster responses and reduced data transfer. + * information: domain, title, colors, logos, and backdrops. Optimized for faster + * responses and reduced data transfer. */ retrieveSimplified( query: BrandRetrieveSimplifiedParams, @@ -153,29 +126,6 @@ export class Brand extends APIResource { return this._client.get('/brand/retrieve-simplified', { query, ...options }); } - /** - * Capture a screenshot of a website. Supports both viewport (standard browser - * view) and full-page screenshots. Can also screenshot specific page types (login, - * pricing, etc.) by using heuristics to find the appropriate URL. Returns a URL to - * the uploaded screenshot image hosted on our CDN. - */ - screenshot(query: BrandScreenshotParams, options?: RequestOptions): APIPromise { - return this._client.get('/brand/screenshot', { query, ...options }); - } - - /** - * Automatically extract comprehensive design system information from a brand's - * website including colors, typography, spacing, shadows, and UI components. - * Either 'domain' or 'directUrl' must be provided as a query parameter, but not - * both. - */ - styleguide( - query: BrandStyleguideParams | null | undefined = {}, - options?: RequestOptions, - ): APIPromise { - return this._client.get('/brand/styleguide', { query, ...options }); - } - /** * Scrapes the given URL and returns the raw HTML content of the page. */ @@ -187,9 +137,10 @@ export class Brand extends APIResource { } /** - * Scrapes all images from the given URL. Extracts images from img, svg, - * picture/source, link, and video elements including inline SVGs, base64 data - * URIs, and standard URLs. + * Extract image assets from a web page, including standard URLs, inline SVGs, data + * URIs, responsive image sources, metadata, CSS backgrounds, video posters, and + * embeds. The base request costs 1 credit. When enrichment is enabled, the entire + * call costs 5 credits. */ webScrapeImages( query: BrandWebScrapeImagesParams, @@ -199,17 +150,14 @@ export class Brand extends APIResource { } /** - * Scrapes the given URL, converts the HTML content to Markdown, and returns the - * result. + * Scrapes the given URL into LLM usable Markdown. */ webScrapeMd(query: BrandWebScrapeMdParams, options?: RequestOptions): APIPromise { return this._client.get('/web/scrape/markdown', { query, ...options }); } /** - * Crawls the sitemap of the given domain and returns all discovered page URLs. - * Supports sitemap index files (recursive), parallel fetching with concurrency - * control, deduplication, and filters out non-page resources (images, PDFs, etc.). + * Crawl an entire website's sitemap and return all discovered page URLs. */ webScrapeSitemap( query: BrandWebScrapeSitemapParams, @@ -296,6 +244,133 @@ export namespace BrandRetrieveResponse { */ phone?: string; + /** + * The primary language of the brand's website content. Detected from the HTML lang + * tag, page content analysis, or social media descriptions. + */ + primary_language?: + | 'afrikaans' + | 'albanian' + | 'amharic' + | 'arabic' + | 'armenian' + | 'assamese' + | 'aymara' + | 'azeri' + | 'basque' + | 'belarusian' + | 'bengali' + | 'bosnian' + | 'bulgarian' + | 'burmese' + | 'cantonese' + | 'catalan' + | 'cebuano' + | 'chinese' + | 'corsican' + | 'croatian' + | 'czech' + | 'danish' + | 'dutch' + | 'english' + | 'esperanto' + | 'estonian' + | 'farsi' + | 'fijian' + | 'finnish' + | 'french' + | 'galician' + | 'georgian' + | 'german' + | 'greek' + | 'guarani' + | 'gujarati' + | 'haitian-creole' + | 'hausa' + | 'hawaiian' + | 'hebrew' + | 'hindi' + | 'hmong' + | 'hungarian' + | 'icelandic' + | 'igbo' + | 'indonesian' + | 'irish' + | 'italian' + | 'japanese' + | 'javanese' + | 'kannada' + | 'kazakh' + | 'khmer' + | 'kinyarwanda' + | 'korean' + | 'kurdish' + | 'kyrgyz' + | 'lao' + | 'latin' + | 'latvian' + | 'lingala' + | 'lithuanian' + | 'luxembourgish' + | 'macedonian' + | 'malagasy' + | 'malay' + | 'malayalam' + | 'maltese' + | 'maori' + | 'marathi' + | 'mongolian' + | 'nepali' + | 'norwegian' + | 'odia' + | 'oromo' + | 'pashto' + | 'pidgin' + | 'polish' + | 'portuguese' + | 'punjabi' + | 'quechua' + | 'romanian' + | 'russian' + | 'samoan' + | 'scottish-gaelic' + | 'serbian' + | 'sesotho' + | 'shona' + | 'sindhi' + | 'sinhala' + | 'slovak' + | 'slovene' + | 'somali' + | 'spanish' + | 'sundanese' + | 'swahili' + | 'swedish' + | 'tagalog' + | 'tajik' + | 'tamil' + | 'tatar' + | 'telugu' + | 'thai' + | 'tibetan' + | 'tigrinya' + | 'tongan' + | 'tswana' + | 'turkish' + | 'turkmen' + | 'ukrainian' + | 'urdu' + | 'uyghur' + | 'uzbek' + | 'vietnamese' + | 'welsh' + | 'wolof' + | 'xhosa' + | 'yiddish' + | 'yoruba' + | 'zulu' + | null; + /** * The brand's slogan */ @@ -790,9 +865,40 @@ export namespace BrandRetrieveResponse { export interface Social { /** - * Type of social media, e.g., 'facebook', 'twitter' - */ - type?: string; + * Type of social media platform + */ + type?: + | 'x' + | 'facebook' + | 'instagram' + | 'linkedin' + | 'youtube' + | 'pinterest' + | 'tiktok' + | 'dribbble' + | 'github' + | 'behance' + | 'snapchat' + | 'whatsapp' + | 'telegram' + | 'line' + | 'discord' + | 'twitch' + | 'vimeo' + | 'imdb' + | 'tumblr' + | 'flickr' + | 'giphy' + | 'medium' + | 'spotify' + | 'soundcloud' + | 'tripadvisor' + | 'yelp' + | 'producthunt' + | 'reddit' + | 'crunchbase' + | 'appstore' + | 'playstore'; /** * URL of the social media page @@ -860,6 +966,11 @@ export namespace BrandAIProductResponse { */ name: string; + /** + * Stock Keeping Unit (product identifier). Null if no identifier is found. + */ + sku: string | null; + /** * Tags associated with the product */ @@ -936,6 +1047,11 @@ export namespace BrandAIProductsResponse { */ name: string; + /** + * Stock Keeping Unit (product identifier). Null if no identifier is found. + */ + sku: string | null; + /** * Tags associated with the product */ @@ -1020,67 +1136,6 @@ export namespace BrandAIQueryResponse { } } -export interface BrandFontsResponse { - /** - * HTTP status code, e.g., 200 - */ - code: number; - - /** - * The normalized domain that was processed - */ - domain: string; - - /** - * Array of font usage information - */ - fonts: Array; - - /** - * Status of the response, e.g., 'ok' - */ - status: string; -} - -export namespace BrandFontsResponse { - export interface Font { - /** - * Array of fallback font families - */ - fallbacks: Array; - - /** - * Font family name - */ - font: string; - - /** - * Number of elements using this font - */ - num_elements: number; - - /** - * Number of words using this font - */ - num_words: number; - - /** - * Percentage of elements using this font - */ - percent_elements: number; - - /** - * Percentage of words using this font - */ - percent_words: number; - - /** - * Array of CSS selectors or element types where this font is used - */ - uses: Array; - } -} - export interface BrandIdentifyFromTransactionResponse { /** * Detailed brand information @@ -1158,6 +1213,133 @@ export namespace BrandIdentifyFromTransactionResponse { */ phone?: string; + /** + * The primary language of the brand's website content. Detected from the HTML lang + * tag, page content analysis, or social media descriptions. + */ + primary_language?: + | 'afrikaans' + | 'albanian' + | 'amharic' + | 'arabic' + | 'armenian' + | 'assamese' + | 'aymara' + | 'azeri' + | 'basque' + | 'belarusian' + | 'bengali' + | 'bosnian' + | 'bulgarian' + | 'burmese' + | 'cantonese' + | 'catalan' + | 'cebuano' + | 'chinese' + | 'corsican' + | 'croatian' + | 'czech' + | 'danish' + | 'dutch' + | 'english' + | 'esperanto' + | 'estonian' + | 'farsi' + | 'fijian' + | 'finnish' + | 'french' + | 'galician' + | 'georgian' + | 'german' + | 'greek' + | 'guarani' + | 'gujarati' + | 'haitian-creole' + | 'hausa' + | 'hawaiian' + | 'hebrew' + | 'hindi' + | 'hmong' + | 'hungarian' + | 'icelandic' + | 'igbo' + | 'indonesian' + | 'irish' + | 'italian' + | 'japanese' + | 'javanese' + | 'kannada' + | 'kazakh' + | 'khmer' + | 'kinyarwanda' + | 'korean' + | 'kurdish' + | 'kyrgyz' + | 'lao' + | 'latin' + | 'latvian' + | 'lingala' + | 'lithuanian' + | 'luxembourgish' + | 'macedonian' + | 'malagasy' + | 'malay' + | 'malayalam' + | 'maltese' + | 'maori' + | 'marathi' + | 'mongolian' + | 'nepali' + | 'norwegian' + | 'odia' + | 'oromo' + | 'pashto' + | 'pidgin' + | 'polish' + | 'portuguese' + | 'punjabi' + | 'quechua' + | 'romanian' + | 'russian' + | 'samoan' + | 'scottish-gaelic' + | 'serbian' + | 'sesotho' + | 'shona' + | 'sindhi' + | 'sinhala' + | 'slovak' + | 'slovene' + | 'somali' + | 'spanish' + | 'sundanese' + | 'swahili' + | 'swedish' + | 'tagalog' + | 'tajik' + | 'tamil' + | 'tatar' + | 'telugu' + | 'thai' + | 'tibetan' + | 'tigrinya' + | 'tongan' + | 'tswana' + | 'turkish' + | 'turkmen' + | 'ukrainian' + | 'urdu' + | 'uyghur' + | 'uzbek' + | 'vietnamese' + | 'welsh' + | 'wolof' + | 'xhosa' + | 'yiddish' + | 'yoruba' + | 'zulu' + | null; + /** * The brand's slogan */ @@ -1652,9 +1834,40 @@ export namespace BrandIdentifyFromTransactionResponse { export interface Social { /** - * Type of social media, e.g., 'facebook', 'twitter' - */ - type?: string; + * Type of social media platform + */ + type?: + | 'x' + | 'facebook' + | 'instagram' + | 'linkedin' + | 'youtube' + | 'pinterest' + | 'tiktok' + | 'dribbble' + | 'github' + | 'behance' + | 'snapchat' + | 'whatsapp' + | 'telegram' + | 'line' + | 'discord' + | 'twitch' + | 'vimeo' + | 'imdb' + | 'tumblr' + | 'flickr' + | 'giphy' + | 'medium' + | 'spotify' + | 'soundcloud' + | 'tripadvisor' + | 'yelp' + | 'producthunt' + | 'reddit' + | 'crunchbase' + | 'appstore' + | 'playstore'; /** * URL of the social media page @@ -1791,6 +2004,133 @@ export namespace BrandRetrieveByEmailResponse { */ phone?: string; + /** + * The primary language of the brand's website content. Detected from the HTML lang + * tag, page content analysis, or social media descriptions. + */ + primary_language?: + | 'afrikaans' + | 'albanian' + | 'amharic' + | 'arabic' + | 'armenian' + | 'assamese' + | 'aymara' + | 'azeri' + | 'basque' + | 'belarusian' + | 'bengali' + | 'bosnian' + | 'bulgarian' + | 'burmese' + | 'cantonese' + | 'catalan' + | 'cebuano' + | 'chinese' + | 'corsican' + | 'croatian' + | 'czech' + | 'danish' + | 'dutch' + | 'english' + | 'esperanto' + | 'estonian' + | 'farsi' + | 'fijian' + | 'finnish' + | 'french' + | 'galician' + | 'georgian' + | 'german' + | 'greek' + | 'guarani' + | 'gujarati' + | 'haitian-creole' + | 'hausa' + | 'hawaiian' + | 'hebrew' + | 'hindi' + | 'hmong' + | 'hungarian' + | 'icelandic' + | 'igbo' + | 'indonesian' + | 'irish' + | 'italian' + | 'japanese' + | 'javanese' + | 'kannada' + | 'kazakh' + | 'khmer' + | 'kinyarwanda' + | 'korean' + | 'kurdish' + | 'kyrgyz' + | 'lao' + | 'latin' + | 'latvian' + | 'lingala' + | 'lithuanian' + | 'luxembourgish' + | 'macedonian' + | 'malagasy' + | 'malay' + | 'malayalam' + | 'maltese' + | 'maori' + | 'marathi' + | 'mongolian' + | 'nepali' + | 'norwegian' + | 'odia' + | 'oromo' + | 'pashto' + | 'pidgin' + | 'polish' + | 'portuguese' + | 'punjabi' + | 'quechua' + | 'romanian' + | 'russian' + | 'samoan' + | 'scottish-gaelic' + | 'serbian' + | 'sesotho' + | 'shona' + | 'sindhi' + | 'sinhala' + | 'slovak' + | 'slovene' + | 'somali' + | 'spanish' + | 'sundanese' + | 'swahili' + | 'swedish' + | 'tagalog' + | 'tajik' + | 'tamil' + | 'tatar' + | 'telugu' + | 'thai' + | 'tibetan' + | 'tigrinya' + | 'tongan' + | 'tswana' + | 'turkish' + | 'turkmen' + | 'ukrainian' + | 'urdu' + | 'uyghur' + | 'uzbek' + | 'vietnamese' + | 'welsh' + | 'wolof' + | 'xhosa' + | 'yiddish' + | 'yoruba' + | 'zulu' + | null; + /** * The brand's slogan */ @@ -2285,9 +2625,40 @@ export namespace BrandRetrieveByEmailResponse { export interface Social { /** - * Type of social media, e.g., 'facebook', 'twitter' - */ - type?: string; + * Type of social media platform + */ + type?: + | 'x' + | 'facebook' + | 'instagram' + | 'linkedin' + | 'youtube' + | 'pinterest' + | 'tiktok' + | 'dribbble' + | 'github' + | 'behance' + | 'snapchat' + | 'whatsapp' + | 'telegram' + | 'line' + | 'discord' + | 'twitch' + | 'vimeo' + | 'imdb' + | 'tumblr' + | 'flickr' + | 'giphy' + | 'medium' + | 'spotify' + | 'soundcloud' + | 'tripadvisor' + | 'yelp' + | 'producthunt' + | 'reddit' + | 'crunchbase' + | 'appstore' + | 'playstore'; /** * URL of the social media page @@ -2390,6 +2761,133 @@ export namespace BrandRetrieveByIsinResponse { */ phone?: string; + /** + * The primary language of the brand's website content. Detected from the HTML lang + * tag, page content analysis, or social media descriptions. + */ + primary_language?: + | 'afrikaans' + | 'albanian' + | 'amharic' + | 'arabic' + | 'armenian' + | 'assamese' + | 'aymara' + | 'azeri' + | 'basque' + | 'belarusian' + | 'bengali' + | 'bosnian' + | 'bulgarian' + | 'burmese' + | 'cantonese' + | 'catalan' + | 'cebuano' + | 'chinese' + | 'corsican' + | 'croatian' + | 'czech' + | 'danish' + | 'dutch' + | 'english' + | 'esperanto' + | 'estonian' + | 'farsi' + | 'fijian' + | 'finnish' + | 'french' + | 'galician' + | 'georgian' + | 'german' + | 'greek' + | 'guarani' + | 'gujarati' + | 'haitian-creole' + | 'hausa' + | 'hawaiian' + | 'hebrew' + | 'hindi' + | 'hmong' + | 'hungarian' + | 'icelandic' + | 'igbo' + | 'indonesian' + | 'irish' + | 'italian' + | 'japanese' + | 'javanese' + | 'kannada' + | 'kazakh' + | 'khmer' + | 'kinyarwanda' + | 'korean' + | 'kurdish' + | 'kyrgyz' + | 'lao' + | 'latin' + | 'latvian' + | 'lingala' + | 'lithuanian' + | 'luxembourgish' + | 'macedonian' + | 'malagasy' + | 'malay' + | 'malayalam' + | 'maltese' + | 'maori' + | 'marathi' + | 'mongolian' + | 'nepali' + | 'norwegian' + | 'odia' + | 'oromo' + | 'pashto' + | 'pidgin' + | 'polish' + | 'portuguese' + | 'punjabi' + | 'quechua' + | 'romanian' + | 'russian' + | 'samoan' + | 'scottish-gaelic' + | 'serbian' + | 'sesotho' + | 'shona' + | 'sindhi' + | 'sinhala' + | 'slovak' + | 'slovene' + | 'somali' + | 'spanish' + | 'sundanese' + | 'swahili' + | 'swedish' + | 'tagalog' + | 'tajik' + | 'tamil' + | 'tatar' + | 'telugu' + | 'thai' + | 'tibetan' + | 'tigrinya' + | 'tongan' + | 'tswana' + | 'turkish' + | 'turkmen' + | 'ukrainian' + | 'urdu' + | 'uyghur' + | 'uzbek' + | 'vietnamese' + | 'welsh' + | 'wolof' + | 'xhosa' + | 'yiddish' + | 'yoruba' + | 'zulu' + | null; + /** * The brand's slogan */ @@ -2884,9 +3382,40 @@ export namespace BrandRetrieveByIsinResponse { export interface Social { /** - * Type of social media, e.g., 'facebook', 'twitter' - */ - type?: string; + * Type of social media platform + */ + type?: + | 'x' + | 'facebook' + | 'instagram' + | 'linkedin' + | 'youtube' + | 'pinterest' + | 'tiktok' + | 'dribbble' + | 'github' + | 'behance' + | 'snapchat' + | 'whatsapp' + | 'telegram' + | 'line' + | 'discord' + | 'twitch' + | 'vimeo' + | 'imdb' + | 'tumblr' + | 'flickr' + | 'giphy' + | 'medium' + | 'spotify' + | 'soundcloud' + | 'tripadvisor' + | 'yelp' + | 'producthunt' + | 'reddit' + | 'crunchbase' + | 'appstore' + | 'playstore'; /** * URL of the social media page @@ -2989,6 +3518,133 @@ export namespace BrandRetrieveByNameResponse { */ phone?: string; + /** + * The primary language of the brand's website content. Detected from the HTML lang + * tag, page content analysis, or social media descriptions. + */ + primary_language?: + | 'afrikaans' + | 'albanian' + | 'amharic' + | 'arabic' + | 'armenian' + | 'assamese' + | 'aymara' + | 'azeri' + | 'basque' + | 'belarusian' + | 'bengali' + | 'bosnian' + | 'bulgarian' + | 'burmese' + | 'cantonese' + | 'catalan' + | 'cebuano' + | 'chinese' + | 'corsican' + | 'croatian' + | 'czech' + | 'danish' + | 'dutch' + | 'english' + | 'esperanto' + | 'estonian' + | 'farsi' + | 'fijian' + | 'finnish' + | 'french' + | 'galician' + | 'georgian' + | 'german' + | 'greek' + | 'guarani' + | 'gujarati' + | 'haitian-creole' + | 'hausa' + | 'hawaiian' + | 'hebrew' + | 'hindi' + | 'hmong' + | 'hungarian' + | 'icelandic' + | 'igbo' + | 'indonesian' + | 'irish' + | 'italian' + | 'japanese' + | 'javanese' + | 'kannada' + | 'kazakh' + | 'khmer' + | 'kinyarwanda' + | 'korean' + | 'kurdish' + | 'kyrgyz' + | 'lao' + | 'latin' + | 'latvian' + | 'lingala' + | 'lithuanian' + | 'luxembourgish' + | 'macedonian' + | 'malagasy' + | 'malay' + | 'malayalam' + | 'maltese' + | 'maori' + | 'marathi' + | 'mongolian' + | 'nepali' + | 'norwegian' + | 'odia' + | 'oromo' + | 'pashto' + | 'pidgin' + | 'polish' + | 'portuguese' + | 'punjabi' + | 'quechua' + | 'romanian' + | 'russian' + | 'samoan' + | 'scottish-gaelic' + | 'serbian' + | 'sesotho' + | 'shona' + | 'sindhi' + | 'sinhala' + | 'slovak' + | 'slovene' + | 'somali' + | 'spanish' + | 'sundanese' + | 'swahili' + | 'swedish' + | 'tagalog' + | 'tajik' + | 'tamil' + | 'tatar' + | 'telugu' + | 'thai' + | 'tibetan' + | 'tigrinya' + | 'tongan' + | 'tswana' + | 'turkish' + | 'turkmen' + | 'ukrainian' + | 'urdu' + | 'uyghur' + | 'uzbek' + | 'vietnamese' + | 'welsh' + | 'wolof' + | 'xhosa' + | 'yiddish' + | 'yoruba' + | 'zulu' + | null; + /** * The brand's slogan */ @@ -3483,9 +4139,40 @@ export namespace BrandRetrieveByNameResponse { export interface Social { /** - * Type of social media, e.g., 'facebook', 'twitter' - */ - type?: string; + * Type of social media platform + */ + type?: + | 'x' + | 'facebook' + | 'instagram' + | 'linkedin' + | 'youtube' + | 'pinterest' + | 'tiktok' + | 'dribbble' + | 'github' + | 'behance' + | 'snapchat' + | 'whatsapp' + | 'telegram' + | 'line' + | 'discord' + | 'twitch' + | 'vimeo' + | 'imdb' + | 'tumblr' + | 'flickr' + | 'giphy' + | 'medium' + | 'spotify' + | 'soundcloud' + | 'tripadvisor' + | 'yelp' + | 'producthunt' + | 'reddit' + | 'crunchbase' + | 'appstore' + | 'playstore'; /** * URL of the social media page @@ -3588,6 +4275,133 @@ export namespace BrandRetrieveByTickerResponse { */ phone?: string; + /** + * The primary language of the brand's website content. Detected from the HTML lang + * tag, page content analysis, or social media descriptions. + */ + primary_language?: + | 'afrikaans' + | 'albanian' + | 'amharic' + | 'arabic' + | 'armenian' + | 'assamese' + | 'aymara' + | 'azeri' + | 'basque' + | 'belarusian' + | 'bengali' + | 'bosnian' + | 'bulgarian' + | 'burmese' + | 'cantonese' + | 'catalan' + | 'cebuano' + | 'chinese' + | 'corsican' + | 'croatian' + | 'czech' + | 'danish' + | 'dutch' + | 'english' + | 'esperanto' + | 'estonian' + | 'farsi' + | 'fijian' + | 'finnish' + | 'french' + | 'galician' + | 'georgian' + | 'german' + | 'greek' + | 'guarani' + | 'gujarati' + | 'haitian-creole' + | 'hausa' + | 'hawaiian' + | 'hebrew' + | 'hindi' + | 'hmong' + | 'hungarian' + | 'icelandic' + | 'igbo' + | 'indonesian' + | 'irish' + | 'italian' + | 'japanese' + | 'javanese' + | 'kannada' + | 'kazakh' + | 'khmer' + | 'kinyarwanda' + | 'korean' + | 'kurdish' + | 'kyrgyz' + | 'lao' + | 'latin' + | 'latvian' + | 'lingala' + | 'lithuanian' + | 'luxembourgish' + | 'macedonian' + | 'malagasy' + | 'malay' + | 'malayalam' + | 'maltese' + | 'maori' + | 'marathi' + | 'mongolian' + | 'nepali' + | 'norwegian' + | 'odia' + | 'oromo' + | 'pashto' + | 'pidgin' + | 'polish' + | 'portuguese' + | 'punjabi' + | 'quechua' + | 'romanian' + | 'russian' + | 'samoan' + | 'scottish-gaelic' + | 'serbian' + | 'sesotho' + | 'shona' + | 'sindhi' + | 'sinhala' + | 'slovak' + | 'slovene' + | 'somali' + | 'spanish' + | 'sundanese' + | 'swahili' + | 'swedish' + | 'tagalog' + | 'tajik' + | 'tamil' + | 'tatar' + | 'telugu' + | 'thai' + | 'tibetan' + | 'tigrinya' + | 'tongan' + | 'tswana' + | 'turkish' + | 'turkmen' + | 'ukrainian' + | 'urdu' + | 'uyghur' + | 'uzbek' + | 'vietnamese' + | 'welsh' + | 'wolof' + | 'xhosa' + | 'yiddish' + | 'yoruba' + | 'zulu' + | null; + /** * The brand's slogan */ @@ -4082,9 +4896,40 @@ export namespace BrandRetrieveByTickerResponse { export interface Social { /** - * Type of social media, e.g., 'facebook', 'twitter' - */ - type?: string; + * Type of social media platform + */ + type?: + | 'x' + | 'facebook' + | 'instagram' + | 'linkedin' + | 'youtube' + | 'pinterest' + | 'tiktok' + | 'dribbble' + | 'github' + | 'behance' + | 'snapchat' + | 'whatsapp' + | 'telegram' + | 'line' + | 'discord' + | 'twitch' + | 'vimeo' + | 'imdb' + | 'tumblr' + | 'flickr' + | 'giphy' + | 'medium' + | 'spotify' + | 'soundcloud' + | 'tripadvisor' + | 'yelp' + | 'producthunt' + | 'reddit' + | 'crunchbase' + | 'appstore' + | 'playstore'; /** * URL of the social media page @@ -4110,47 +4955,6 @@ export namespace BrandRetrieveByTickerResponse { } } -export interface BrandRetrieveNaicsResponse { - /** - * Array of NAICS codes and titles. - */ - codes?: Array; - - /** - * Domain found for the brand - */ - domain?: string; - - /** - * Status of the response, e.g., 'ok' - */ - status?: string; - - /** - * Industry classification type, for naics api it will be `naics` - */ - type?: string; -} - -export namespace BrandRetrieveNaicsResponse { - export interface Code { - /** - * NAICS code - */ - code: string; - - /** - * Confidence level for how well this NAICS code matches the company description - */ - confidence: 'high' | 'medium' | 'low'; - - /** - * NAICS title - */ - name: string; - } -} - export interface BrandRetrieveSimplifiedResponse { /** * Simplified brand information @@ -4328,574 +5132,109 @@ export namespace BrandRetrieveSimplifiedResponse { } } -export interface BrandScreenshotResponse { - /** - * HTTP status code - */ - code?: number; - +export interface BrandWebScrapeHTMLResponse { /** - * The normalized domain that was processed + * The scraped content of the page. For normal pages this is the raw HTML. When the + * page is a sitemap or feed served behind an XSL stylesheet (which browsers render + * into HTML), this is the underlying XML instead — see the `type` field. */ - domain?: string; + html: string; /** - * Public URL of the uploaded screenshot image + * Indicates success */ - screenshot?: string; + success: true; /** - * Type of screenshot that was captured + * Detected content type of the returned `html` field. Sitemaps and feeds are + * surfaced as `xml`; ordinary pages are `html`. */ - screenshotType?: 'viewport' | 'fullPage'; + type: 'html' | 'xml' | 'json' | 'text' | 'csv' | 'markdown' | 'svg' | 'pdf'; /** - * Status of the response, e.g., 'ok' + * The URL that was scraped */ - status?: string; + url: string; } -export interface BrandStyleguideResponse { - /** - * HTTP status code - */ - code?: number; - +export interface BrandWebScrapeImagesResponse { /** - * The normalized domain that was processed + * Images found on the page. */ - domain?: string; + images: Array; /** - * Status of the response, e.g., 'ok' + * Always true on success. */ - status?: string; + success: true; /** - * Comprehensive styleguide data extracted from the website + * Page URL that was scraped. */ - styleguide?: BrandStyleguideResponse.Styleguide; + url: string; } -export namespace BrandStyleguideResponse { - /** - * Comprehensive styleguide data extracted from the website - */ - export interface Styleguide { - /** - * Primary colors used on the website - */ - colors: Styleguide.Colors; - +export namespace BrandWebScrapeImagesResponse { + export interface Image { /** - * UI component styles + * Image alt text, or null when unavailable. */ - components: Styleguide.Components; + alt: string | null; /** - * Spacing system used on the website + * Where the image was found. */ - elementSpacing: Styleguide.ElementSpacing; + element: 'img' | 'svg' | 'link' | 'source' | 'video' | 'css' | 'object' | 'meta' | 'background'; /** - * The primary color mode of the website design + * Original image value: URL, inline SVG or HTML, or base64 data URI. */ - mode: 'light' | 'dark'; + src: string; /** - * Shadow styles used on the website + * Format of src. */ - shadows: Styleguide.Shadows; + type: 'url' | 'html' | 'base64'; /** - * Typography styles used on the website + * Requested metadata for images that could be processed. */ - typography: Styleguide.Typography; + enrichment?: Image.Enrichment; } - export namespace Styleguide { + export namespace Image { /** - * Primary colors used on the website + * Requested metadata for images that could be processed. */ - export interface Colors { - /** - * Accent color (hex format) - */ - accent: string; - - /** - * Background color (hex format) - */ - background: string; - + export interface Enrichment { /** - * Text color (hex format) + * Image height in pixels, when measured. */ - text: string; - } + height?: number; - /** - * UI component styles - */ - export interface Components { /** - * Button component styles + * Detected MIME type, when hosted. */ - button: Components.Button; + mimetype?: string; /** - * Card component style + * Visual asset category, when classified. */ - card?: Components.Card; - } + type?: 'photography' | 'illustration' | 'logo' | 'wordmark' | 'icon' | 'pattern' | 'graphic' | 'other'; - export namespace Components { /** - * Button component styles + * Brand.dev CDN URL, when hosted. */ - export interface Button { - link?: Button.Link; - - primary?: Button.Primary; - - secondary?: Button.Secondary; - } - - export namespace Button { - export interface Link { - backgroundColor: string; - - /** - * Border color as CSS hex (#RRGGBB or #RRGGBBAA when computed border-color has - * alpha) - */ - borderColor: string; - - borderRadius: string; - - borderStyle: string; - - borderWidth: string; - - /** - * Computed box-shadow (comma-separated layers when present) - */ - boxShadow: string; - - color: string; - - /** - * Ready-to-use CSS declaration block for this component style - */ - css: string; - - fontSize: string; - - fontWeight: number; - - /** - * Sampled minimum height of the button box (typically px) - */ - minHeight: string; - - /** - * Sampled minimum width of the button box (typically px) - */ - minWidth: string; - - padding: string; - - textDecoration: string; - - /** - * Full ordered font list from computed font-family - */ - fontFallbacks?: Array; - - /** - * Primary button typeface (first in fontFallbacks) - */ - fontFamily?: string; - - /** - * Hex color of the underline when it differs from the text color - */ - textDecorationColor?: string; - } - - export interface Primary { - backgroundColor: string; - - /** - * Border color as CSS hex (#RRGGBB or #RRGGBBAA when computed border-color has - * alpha) - */ - borderColor: string; - - borderRadius: string; - - borderStyle: string; - - borderWidth: string; - - /** - * Computed box-shadow (comma-separated layers when present) - */ - boxShadow: string; - - color: string; - - /** - * Ready-to-use CSS declaration block for this component style - */ - css: string; - - fontSize: string; - - fontWeight: number; - - /** - * Sampled minimum height of the button box (typically px) - */ - minHeight: string; - - /** - * Sampled minimum width of the button box (typically px) - */ - minWidth: string; - - padding: string; - - textDecoration: string; - - /** - * Full ordered font list from computed font-family - */ - fontFallbacks?: Array; - - /** - * Primary button typeface (first in fontFallbacks) - */ - fontFamily?: string; - - /** - * Hex color of the underline when it differs from the text color - */ - textDecorationColor?: string; - } - - export interface Secondary { - backgroundColor: string; - - /** - * Border color as CSS hex (#RRGGBB or #RRGGBBAA when computed border-color has - * alpha) - */ - borderColor: string; - - borderRadius: string; - - borderStyle: string; - - borderWidth: string; - - /** - * Computed box-shadow (comma-separated layers when present) - */ - boxShadow: string; - - color: string; - - /** - * Ready-to-use CSS declaration block for this component style - */ - css: string; - - fontSize: string; - - fontWeight: number; - - /** - * Sampled minimum height of the button box (typically px) - */ - minHeight: string; - - /** - * Sampled minimum width of the button box (typically px) - */ - minWidth: string; - - padding: string; - - textDecoration: string; - - /** - * Full ordered font list from computed font-family - */ - fontFallbacks?: Array; - - /** - * Primary button typeface (first in fontFallbacks) - */ - fontFamily?: string; - - /** - * Hex color of the underline when it differs from the text color - */ - textDecorationColor?: string; - } - } - - /** - * Card component style - */ - export interface Card { - backgroundColor: string; - - /** - * Border color as CSS hex (#RRGGBB or #RRGGBBAA when computed border-color has - * alpha) - */ - borderColor: string; - - borderRadius: string; - - borderStyle: string; - - borderWidth: string; - - boxShadow: string; - - /** - * Ready-to-use CSS declaration block for this component style - */ - css: string; - - padding: string; - - textColor: string; - } - } - - /** - * Spacing system used on the website - */ - export interface ElementSpacing { - lg: string; - - md: string; - - sm: string; - - xl: string; - - xs: string; - } - - /** - * Shadow styles used on the website - */ - export interface Shadows { - inner: string; - - lg: string; - - md: string; - - sm: string; - - xl: string; - } - - /** - * Typography styles used on the website - */ - export interface Typography { - /** - * Heading styles - */ - headings: Typography.Headings; - - p?: Typography.P; - } + url?: string; - export namespace Typography { /** - * Heading styles + * Image width in pixels, when measured. */ - export interface Headings { - h1?: Headings.H1; - - h2?: Headings.H2; - - h3?: Headings.H3; - - h4?: Headings.H4; - } - - export namespace Headings { - export interface H1 { - /** - * Full ordered font list from resolved computed font-family - */ - fontFallbacks: Array; - - /** - * Primary face (first family in the computed stack) - */ - fontFamily: string; - - fontSize: string; - - fontWeight: number; - - letterSpacing: string; - - lineHeight: string; - } - - export interface H2 { - /** - * Full ordered font list from resolved computed font-family - */ - fontFallbacks: Array; - - /** - * Primary face (first family in the computed stack) - */ - fontFamily: string; - - fontSize: string; - - fontWeight: number; - - letterSpacing: string; - - lineHeight: string; - } - - export interface H3 { - /** - * Full ordered font list from resolved computed font-family - */ - fontFallbacks: Array; - - /** - * Primary face (first family in the computed stack) - */ - fontFamily: string; - - fontSize: string; - - fontWeight: number; - - letterSpacing: string; - - lineHeight: string; - } - - export interface H4 { - /** - * Full ordered font list from resolved computed font-family - */ - fontFallbacks: Array; - - /** - * Primary face (first family in the computed stack) - */ - fontFamily: string; - - fontSize: string; - - fontWeight: number; - - letterSpacing: string; - - lineHeight: string; - } - } - - export interface P { - /** - * Full ordered font list from resolved computed font-family - */ - fontFallbacks: Array; - - /** - * Primary face (first family in the computed stack) - */ - fontFamily: string; - - fontSize: string; - - fontWeight: number; - - letterSpacing: string; - - lineHeight: string; - } + width?: number; } } } -export interface BrandWebScrapeHTMLResponse { - /** - * Raw HTML content of the page - */ - html: string; - - /** - * Indicates success - */ - success: true; - - /** - * The URL that was scraped - */ - url: string; -} - -export interface BrandWebScrapeImagesResponse { - /** - * Array of scraped images - */ - images: Array; - - /** - * Indicates success - */ - success: true; - - /** - * The URL that was scraped - */ - url: string; -} - -export namespace BrandWebScrapeImagesResponse { - export interface Image { - /** - * Alt text of the image, or null if not present - */ - alt: string | null; - - /** - * The HTML element the image was found in - */ - element: 'img' | 'svg' | 'link' | 'source' | 'video' | 'css' | 'object' | 'meta' | 'background'; - - /** - * The image source - can be a URL, inline HTML (for SVGs), or a base64 data URI - */ - src: string; - - /** - * The type/format of the src value - */ - type: 'url' | 'html' | 'base64'; - } -} - export interface BrandWebScrapeMdResponse { /** * Page content converted to GitHub Flavored Markdown @@ -4970,65 +5309,137 @@ export interface BrandRetrieveParams { domain: string; /** - * Optional parameter to force the language of the retrieved brand data. Works with - * all three lookup methods. + * Optional parameter to force the language of the retrieved brand data. */ force_language?: + | 'afrikaans' | 'albanian' + | 'amharic' | 'arabic' + | 'armenian' + | 'assamese' + | 'aymara' | 'azeri' + | 'basque' + | 'belarusian' | 'bengali' + | 'bosnian' | 'bulgarian' + | 'burmese' | 'cantonese' + | 'catalan' | 'cebuano' + | 'chinese' + | 'corsican' | 'croatian' | 'czech' | 'danish' | 'dutch' | 'english' + | 'esperanto' | 'estonian' | 'farsi' + | 'fijian' | 'finnish' | 'french' + | 'galician' + | 'georgian' | 'german' + | 'greek' + | 'guarani' + | 'gujarati' + | 'haitian-creole' | 'hausa' | 'hawaiian' + | 'hebrew' | 'hindi' + | 'hmong' | 'hungarian' | 'icelandic' + | 'igbo' | 'indonesian' + | 'irish' | 'italian' + | 'japanese' + | 'javanese' + | 'kannada' | 'kazakh' + | 'khmer' + | 'kinyarwanda' | 'korean' + | 'kurdish' | 'kyrgyz' + | 'lao' | 'latin' | 'latvian' + | 'lingala' | 'lithuanian' + | 'luxembourgish' | 'macedonian' + | 'malagasy' + | 'malay' + | 'malayalam' + | 'maltese' + | 'maori' + | 'marathi' | 'mongolian' | 'nepali' | 'norwegian' + | 'odia' + | 'oromo' | 'pashto' | 'pidgin' | 'polish' | 'portuguese' + | 'punjabi' + | 'quechua' | 'romanian' | 'russian' + | 'samoan' + | 'scottish-gaelic' | 'serbian' + | 'sesotho' + | 'shona' + | 'sindhi' + | 'sinhala' | 'slovak' | 'slovene' | 'somali' | 'spanish' + | 'sundanese' | 'swahili' | 'swedish' | 'tagalog' + | 'tajik' + | 'tamil' + | 'tatar' + | 'telugu' | 'thai' + | 'tibetan' + | 'tigrinya' + | 'tongan' + | 'tswana' | 'turkish' + | 'turkmen' | 'ukrainian' | 'urdu' + | 'uyghur' | 'uzbek' | 'vietnamese' - | 'welsh'; + | 'welsh' + | 'wolof' + | 'xhosa' + | 'yiddish' + | 'yoruba' + | 'zulu'; + + /** + * Maximum age in milliseconds for cached brand data before the API performs a hard + * refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) + * are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 + * year. + */ + maxAgeMs?: number; /** * Optional parameter to optimize the API call for maximum speed. When set to true, @@ -5052,8 +5463,16 @@ export interface BrandAIProductParams { url: string; /** - * Optional timeout in milliseconds for the request. Maximum allowed value is - * 300000ms (5 minutes). + * Return a cached result if a prior scrape for the same parameters exists and is + * younger than this many milliseconds. Defaults to 7 days (604800000 ms) when + * omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. + */ + maxAgeMs?: number; + + /** + * Optional timeout in milliseconds for the request. If the request takes longer + * than this value, it will be aborted with a 408 status code. Maximum allowed + * value is 300000ms (5 minutes). */ timeoutMS?: number; } @@ -5067,14 +5486,22 @@ export declare namespace BrandAIProductsParams { */ domain: string; + /** + * Return a cached result if a prior scrape for the same parameters exists and is + * younger than this many milliseconds. Defaults to 7 days (604800000 ms) when + * omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. + */ + maxAgeMs?: number; + /** * Maximum number of products to extract. */ maxProducts?: number; /** - * Optional timeout in milliseconds for the request. Maximum allowed value is - * 300000ms (5 minutes). + * Optional timeout in milliseconds for the request. If the request takes longer + * than this value, it will be aborted with a 408 status code. Maximum allowed + * value is 300000ms (5 minutes). */ timeoutMS?: number; } @@ -5086,14 +5513,22 @@ export declare namespace BrandAIProductsParams { */ directUrl: string; + /** + * Return a cached result if a prior scrape for the same parameters exists and is + * younger than this many milliseconds. Defaults to 7 days (604800000 ms) when + * omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. + */ + maxAgeMs?: number; + /** * Maximum number of products to extract. */ maxProducts?: number; /** - * Optional timeout in milliseconds for the request. Maximum allowed value is - * 300000ms (5 minutes). + * Optional timeout in milliseconds for the request. If the request takes longer + * than this value, it will be aborted with a 408 status code. Maximum allowed + * value is 300000ms (5 minutes). */ timeoutMS?: number; } @@ -5209,21 +5644,6 @@ export namespace BrandAIQueryParams { } } -export interface BrandFontsParams { - /** - * Domain name to extract fonts from (e.g., 'example.com', 'google.com'). The - * domain will be automatically normalized and validated. - */ - domain: string; - - /** - * Optional timeout in milliseconds for the request. If the request takes longer - * than this value, it will be aborted with a 408 status code. Maximum allowed - * value is 300000ms (5 minutes). - */ - timeoutMS?: number; -} - export interface BrandIdentifyFromTransactionParams { /** * Transaction information to identify the brand @@ -5484,66 +5904,130 @@ export interface BrandIdentifyFromTransactionParams { * Optional parameter to force the language of the retrieved brand data. */ force_language?: + | 'afrikaans' | 'albanian' + | 'amharic' | 'arabic' + | 'armenian' + | 'assamese' + | 'aymara' | 'azeri' + | 'basque' + | 'belarusian' | 'bengali' + | 'bosnian' | 'bulgarian' + | 'burmese' | 'cantonese' + | 'catalan' | 'cebuano' + | 'chinese' + | 'corsican' | 'croatian' | 'czech' | 'danish' | 'dutch' | 'english' + | 'esperanto' | 'estonian' | 'farsi' + | 'fijian' | 'finnish' | 'french' + | 'galician' + | 'georgian' | 'german' + | 'greek' + | 'guarani' + | 'gujarati' + | 'haitian-creole' | 'hausa' | 'hawaiian' + | 'hebrew' | 'hindi' + | 'hmong' | 'hungarian' | 'icelandic' + | 'igbo' | 'indonesian' + | 'irish' | 'italian' + | 'japanese' + | 'javanese' + | 'kannada' | 'kazakh' + | 'khmer' + | 'kinyarwanda' | 'korean' + | 'kurdish' | 'kyrgyz' + | 'lao' | 'latin' | 'latvian' + | 'lingala' | 'lithuanian' + | 'luxembourgish' | 'macedonian' + | 'malagasy' + | 'malay' + | 'malayalam' + | 'maltese' + | 'maori' + | 'marathi' | 'mongolian' | 'nepali' | 'norwegian' + | 'odia' + | 'oromo' | 'pashto' | 'pidgin' | 'polish' | 'portuguese' + | 'punjabi' + | 'quechua' | 'romanian' | 'russian' + | 'samoan' + | 'scottish-gaelic' | 'serbian' + | 'sesotho' + | 'shona' + | 'sindhi' + | 'sinhala' | 'slovak' | 'slovene' | 'somali' | 'spanish' + | 'sundanese' | 'swahili' | 'swedish' | 'tagalog' + | 'tajik' + | 'tamil' + | 'tatar' + | 'telugu' | 'thai' + | 'tibetan' + | 'tigrinya' + | 'tongan' + | 'tswana' | 'turkish' + | 'turkmen' | 'ukrainian' | 'urdu' + | 'uyghur' | 'uzbek' | 'vietnamese' - | 'welsh'; + | 'welsh' + | 'wolof' + | 'xhosa' + | 'yiddish' + | 'yoruba' + | 'zulu'; /** * When set to true, the API will perform an additional verification steps to * ensure the identified brand matches the transaction with high confidence. - * Defaults to false. */ high_confidence_only?: boolean; @@ -5615,61 +6099,134 @@ export interface BrandRetrieveByEmailParams { * Optional parameter to force the language of the retrieved brand data. */ force_language?: + | 'afrikaans' | 'albanian' + | 'amharic' | 'arabic' + | 'armenian' + | 'assamese' + | 'aymara' | 'azeri' + | 'basque' + | 'belarusian' | 'bengali' + | 'bosnian' | 'bulgarian' + | 'burmese' | 'cantonese' + | 'catalan' | 'cebuano' + | 'chinese' + | 'corsican' | 'croatian' | 'czech' | 'danish' | 'dutch' | 'english' + | 'esperanto' | 'estonian' | 'farsi' + | 'fijian' | 'finnish' | 'french' + | 'galician' + | 'georgian' | 'german' + | 'greek' + | 'guarani' + | 'gujarati' + | 'haitian-creole' | 'hausa' | 'hawaiian' + | 'hebrew' | 'hindi' + | 'hmong' | 'hungarian' | 'icelandic' + | 'igbo' | 'indonesian' + | 'irish' | 'italian' + | 'japanese' + | 'javanese' + | 'kannada' | 'kazakh' + | 'khmer' + | 'kinyarwanda' | 'korean' + | 'kurdish' | 'kyrgyz' + | 'lao' | 'latin' | 'latvian' + | 'lingala' | 'lithuanian' + | 'luxembourgish' | 'macedonian' + | 'malagasy' + | 'malay' + | 'malayalam' + | 'maltese' + | 'maori' + | 'marathi' | 'mongolian' | 'nepali' | 'norwegian' + | 'odia' + | 'oromo' | 'pashto' | 'pidgin' | 'polish' | 'portuguese' + | 'punjabi' + | 'quechua' | 'romanian' | 'russian' + | 'samoan' + | 'scottish-gaelic' | 'serbian' + | 'sesotho' + | 'shona' + | 'sindhi' + | 'sinhala' | 'slovak' | 'slovene' | 'somali' | 'spanish' + | 'sundanese' | 'swahili' | 'swedish' | 'tagalog' + | 'tajik' + | 'tamil' + | 'tatar' + | 'telugu' | 'thai' + | 'tibetan' + | 'tigrinya' + | 'tongan' + | 'tswana' | 'turkish' + | 'turkmen' | 'ukrainian' | 'urdu' + | 'uyghur' | 'uzbek' | 'vietnamese' - | 'welsh'; + | 'welsh' + | 'wolof' + | 'xhosa' + | 'yiddish' + | 'yoruba' + | 'zulu'; + + /** + * Maximum age in milliseconds for cached brand data before the API performs a hard + * refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) + * are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 + * year. + */ + maxAgeMs?: number; /** * Optional parameter to optimize the API call for maximum speed. When set to true, @@ -5698,61 +6255,134 @@ export interface BrandRetrieveByIsinParams { * Optional parameter to force the language of the retrieved brand data. */ force_language?: + | 'afrikaans' | 'albanian' + | 'amharic' | 'arabic' + | 'armenian' + | 'assamese' + | 'aymara' | 'azeri' + | 'basque' + | 'belarusian' | 'bengali' + | 'bosnian' | 'bulgarian' + | 'burmese' | 'cantonese' + | 'catalan' | 'cebuano' + | 'chinese' + | 'corsican' | 'croatian' | 'czech' | 'danish' | 'dutch' | 'english' + | 'esperanto' | 'estonian' | 'farsi' + | 'fijian' | 'finnish' | 'french' + | 'galician' + | 'georgian' | 'german' + | 'greek' + | 'guarani' + | 'gujarati' + | 'haitian-creole' | 'hausa' | 'hawaiian' + | 'hebrew' | 'hindi' + | 'hmong' | 'hungarian' | 'icelandic' + | 'igbo' | 'indonesian' + | 'irish' | 'italian' + | 'japanese' + | 'javanese' + | 'kannada' | 'kazakh' + | 'khmer' + | 'kinyarwanda' | 'korean' + | 'kurdish' | 'kyrgyz' + | 'lao' | 'latin' | 'latvian' + | 'lingala' | 'lithuanian' + | 'luxembourgish' | 'macedonian' + | 'malagasy' + | 'malay' + | 'malayalam' + | 'maltese' + | 'maori' + | 'marathi' | 'mongolian' | 'nepali' | 'norwegian' + | 'odia' + | 'oromo' | 'pashto' | 'pidgin' | 'polish' | 'portuguese' + | 'punjabi' + | 'quechua' | 'romanian' | 'russian' + | 'samoan' + | 'scottish-gaelic' | 'serbian' + | 'sesotho' + | 'shona' + | 'sindhi' + | 'sinhala' | 'slovak' | 'slovene' | 'somali' | 'spanish' + | 'sundanese' | 'swahili' | 'swedish' | 'tagalog' + | 'tajik' + | 'tamil' + | 'tatar' + | 'telugu' | 'thai' + | 'tibetan' + | 'tigrinya' + | 'tongan' + | 'tswana' | 'turkish' + | 'turkmen' | 'ukrainian' | 'urdu' + | 'uyghur' | 'uzbek' | 'vietnamese' - | 'welsh'; + | 'welsh' + | 'wolof' + | 'xhosa' + | 'yiddish' + | 'yoruba' + | 'zulu'; + + /** + * Maximum age in milliseconds for cached brand data before the API performs a hard + * refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) + * are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 + * year. + */ + maxAgeMs?: number; /** * Optional parameter to optimize the API call for maximum speed. When set to true, @@ -5777,8 +6407,8 @@ export interface BrandRetrieveByNameParams { name: string; /** - * Optional country code (GL parameter) to specify the country. This affects the - * geographic location used for search queries. + * Optional country code hint (GL parameter) to specify the country for the company + * name. */ country_gl?: | 'ad' @@ -6025,61 +6655,134 @@ export interface BrandRetrieveByNameParams { * Optional parameter to force the language of the retrieved brand data. */ force_language?: + | 'afrikaans' | 'albanian' + | 'amharic' | 'arabic' + | 'armenian' + | 'assamese' + | 'aymara' | 'azeri' + | 'basque' + | 'belarusian' | 'bengali' + | 'bosnian' | 'bulgarian' + | 'burmese' | 'cantonese' + | 'catalan' | 'cebuano' + | 'chinese' + | 'corsican' | 'croatian' | 'czech' | 'danish' | 'dutch' | 'english' + | 'esperanto' | 'estonian' | 'farsi' + | 'fijian' | 'finnish' | 'french' + | 'galician' + | 'georgian' | 'german' + | 'greek' + | 'guarani' + | 'gujarati' + | 'haitian-creole' | 'hausa' | 'hawaiian' + | 'hebrew' | 'hindi' + | 'hmong' | 'hungarian' | 'icelandic' + | 'igbo' | 'indonesian' + | 'irish' | 'italian' + | 'japanese' + | 'javanese' + | 'kannada' | 'kazakh' + | 'khmer' + | 'kinyarwanda' | 'korean' + | 'kurdish' | 'kyrgyz' + | 'lao' | 'latin' | 'latvian' + | 'lingala' | 'lithuanian' + | 'luxembourgish' | 'macedonian' + | 'malagasy' + | 'malay' + | 'malayalam' + | 'maltese' + | 'maori' + | 'marathi' | 'mongolian' | 'nepali' | 'norwegian' + | 'odia' + | 'oromo' | 'pashto' | 'pidgin' | 'polish' | 'portuguese' + | 'punjabi' + | 'quechua' | 'romanian' | 'russian' + | 'samoan' + | 'scottish-gaelic' | 'serbian' + | 'sesotho' + | 'shona' + | 'sindhi' + | 'sinhala' | 'slovak' | 'slovene' | 'somali' | 'spanish' + | 'sundanese' | 'swahili' | 'swedish' | 'tagalog' + | 'tajik' + | 'tamil' + | 'tatar' + | 'telugu' | 'thai' + | 'tibetan' + | 'tigrinya' + | 'tongan' + | 'tswana' | 'turkish' + | 'turkmen' | 'ukrainian' | 'urdu' + | 'uyghur' | 'uzbek' | 'vietnamese' - | 'welsh'; + | 'welsh' + | 'wolof' + | 'xhosa' + | 'yiddish' + | 'yoruba' + | 'zulu'; + + /** + * Maximum age in milliseconds for cached brand data before the API performs a hard + * refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) + * are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 + * year. + */ + maxAgeMs?: number; /** * Optional parameter to optimize the API call for maximum speed. When set to true, @@ -6107,61 +6810,134 @@ export interface BrandRetrieveByTickerParams { * Optional parameter to force the language of the retrieved brand data. */ force_language?: + | 'afrikaans' | 'albanian' + | 'amharic' | 'arabic' + | 'armenian' + | 'assamese' + | 'aymara' | 'azeri' + | 'basque' + | 'belarusian' | 'bengali' + | 'bosnian' | 'bulgarian' + | 'burmese' | 'cantonese' + | 'catalan' | 'cebuano' + | 'chinese' + | 'corsican' | 'croatian' | 'czech' | 'danish' | 'dutch' | 'english' + | 'esperanto' | 'estonian' | 'farsi' + | 'fijian' | 'finnish' | 'french' + | 'galician' + | 'georgian' | 'german' + | 'greek' + | 'guarani' + | 'gujarati' + | 'haitian-creole' | 'hausa' | 'hawaiian' + | 'hebrew' | 'hindi' + | 'hmong' | 'hungarian' | 'icelandic' + | 'igbo' | 'indonesian' + | 'irish' | 'italian' + | 'japanese' + | 'javanese' + | 'kannada' | 'kazakh' + | 'khmer' + | 'kinyarwanda' | 'korean' + | 'kurdish' | 'kyrgyz' + | 'lao' | 'latin' | 'latvian' + | 'lingala' | 'lithuanian' + | 'luxembourgish' | 'macedonian' + | 'malagasy' + | 'malay' + | 'malayalam' + | 'maltese' + | 'maori' + | 'marathi' | 'mongolian' | 'nepali' | 'norwegian' + | 'odia' + | 'oromo' | 'pashto' | 'pidgin' | 'polish' | 'portuguese' + | 'punjabi' + | 'quechua' | 'romanian' | 'russian' + | 'samoan' + | 'scottish-gaelic' | 'serbian' + | 'sesotho' + | 'shona' + | 'sindhi' + | 'sinhala' | 'slovak' | 'slovene' | 'somali' | 'spanish' + | 'sundanese' | 'swahili' | 'swedish' | 'tagalog' + | 'tajik' + | 'tamil' + | 'tatar' + | 'telugu' | 'thai' + | 'tibetan' + | 'tigrinya' + | 'tongan' + | 'tswana' | 'turkish' + | 'turkmen' | 'ukrainian' | 'urdu' + | 'uyghur' | 'uzbek' | 'vietnamese' - | 'welsh'; + | 'welsh' + | 'wolof' + | 'xhosa' + | 'yiddish' + | 'yoruba' + | 'zulu'; + + /** + * Maximum age in milliseconds for cached brand data before the API performs a hard + * refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) + * are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 + * year. + */ + maxAgeMs?: number; /** * Optional parameter to optimize the API call for maximum speed. When set to true, @@ -6255,24 +7031,19 @@ export interface BrandRetrieveByTickerParams { timeoutMS?: number; } -export interface BrandRetrieveNaicsParams { - /** - * Brand domain or title to retrieve NAICS code for. If a valid domain is provided - * in `input`, it will be used for classification, otherwise, we will search for - * the brand using the provided title. - */ - input: string; - +export interface BrandRetrieveSimplifiedParams { /** - * Maximum number of NAICS codes to return. Must be between 1 and 10. Defaults - * to 5. + * Domain name to retrieve simplified brand data for */ - maxResults?: number; + domain: string; /** - * Minimum number of NAICS codes to return. Must be at least 1. Defaults to 1. + * Maximum age in milliseconds for cached brand data before the API performs a hard + * refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) + * are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 + * year. */ - minResults?: number; + maxAgeMs?: number; /** * Optional timeout in milliseconds for the request. If the request takes longer @@ -6282,11 +7053,50 @@ export interface BrandRetrieveNaicsParams { timeoutMS?: number; } -export interface BrandRetrieveSimplifiedParams { +export interface BrandWebScrapeHTMLParams { /** - * Domain name to retrieve simplified brand data for + * Full URL to scrape (must include http:// or https:// protocol) */ - domain: string; + url: string; + + /** + * CSS selectors to remove from the result. Applied after includeSelectors. + * Exclusion takes precedence: an element matching both is removed. Examples: + * "nav", "footer", ".ad-banner", "[aria-hidden=true]". + */ + excludeSelectors?: Array; + + /** + * Optional outbound HTTP headers forwarded only to the target URL, sent as + * deep-object query params such as headers[X-Custom]=value. When provided, caching + * is bypassed: the result is neither read from nor written to cache. + */ + headers?: { [key: string]: string }; + + /** + * When true, iframes are rendered inline into the returned HTML. + */ + includeFrames?: boolean; + + /** + * CSS selectors. When provided, only matching subtrees (and their descendants) are + * kept and everything else is dropped. When omitted, the entire document is kept. + * Examples: "article.main", "#content", "[role=main]". + */ + includeSelectors?: Array; + + /** + * Return a cached result if a prior scrape for the same parameters exists and is + * younger than this many milliseconds. Defaults to 1 day (86400000 ms) when + * omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. + */ + maxAgeMs?: number; + + /** + * PDF parsing controls. Use start/end to limit text extraction and OCR to an + * inclusive 1-based page range. + */ + pdf?: BrandWebScrapeHTMLParams.Pdf; /** * Optional timeout in milliseconds for the request. If the request takes longer @@ -6294,50 +7104,69 @@ export interface BrandRetrieveSimplifiedParams { * value is 300000ms (5 minutes). */ timeoutMS?: number; -} -export interface BrandScreenshotParams { /** - * Domain name to take screenshot of (e.g., 'example.com', 'google.com'). The - * domain will be automatically normalized and validated. + * When true, return only the page's main content in the HTML response, excluding + * headers, footers, sidebars, and navigation when detectable. */ - domain: string; + useMainContentOnly?: boolean; + + /** + * Optional browser wait time in milliseconds after initial page load. Min: 0. Max: + * 30000 (30 seconds). + */ + waitForMs?: number; +} +export namespace BrandWebScrapeHTMLParams { /** - * Optional parameter to determine screenshot type. If 'true', takes a full page - * screenshot capturing all content. If 'false' or not provided, takes a viewport - * screenshot (standard browser view). + * PDF parsing controls. Use start/end to limit text extraction and OCR to an + * inclusive 1-based page range. */ - fullScreenshot?: 'true' | 'false'; + export interface Pdf { + /** + * Last 1-based PDF page to parse. When omitted, parsing ends at the last page. + * Must be greater than or equal to start when both are provided. + */ + end?: number; + + /** + * When true, PDF URLs are fetched and parsed. When false, PDF URLs are skipped and + * a 400 WEBSITE_ACCESS_ERROR is returned. + */ + shouldParse?: boolean; + + /** + * First 1-based PDF page to parse. When omitted, parsing starts at the first page. + */ + start?: number; + } +} +export interface BrandWebScrapeImagesParams { /** - * Optional parameter to specify which page type to screenshot. If provided, the - * system will scrape the domain's links and use heuristics to find the most - * appropriate URL for the specified page type (30 supported languages). If not - * provided, screenshots the main domain landing page. + * Page URL to inspect. Must include http:// or https://. */ - page?: 'login' | 'signup' | 'blog' | 'careers' | 'pricing' | 'terms' | 'privacy' | 'contact'; + url: string; /** - * Optional parameter to prioritize screenshot capture. If 'speed', optimizes for - * faster capture with basic quality. If 'quality', optimizes for higher quality - * with longer wait times. Defaults to 'quality' if not provided. + * Optional per-image processing, sent as deep-object query params such as + * enrichment[resolution]=true. */ - prioritize?: 'speed' | 'quality'; -} + enrichment?: BrandWebScrapeImagesParams.Enrichment; -export interface BrandStyleguideParams { /** - * A specific URL to fetch the styleguide from directly, bypassing domain - * resolution (e.g., 'https://example.com/design-system'). + * Optional outbound HTTP headers forwarded only to the target URL, sent as + * deep-object query params such as headers[X-Custom]=value. When provided, caching + * is bypassed: the result is neither read from nor written to cache. */ - directUrl?: string; + headers?: { [key: string]: string }; /** - * Domain name to extract styleguide from (e.g., 'example.com', 'google.com'). The - * domain will be automatically normalized and validated. + * Reuse a cached result this many milliseconds old or newer. Default: 86400000 (1 + * day). Set to 0 to bypass cache. Maximum: 2592000000 (30 days). */ - domain?: string; + maxAgeMs?: number; /** * Optional timeout in milliseconds for the request. If the request takes longer @@ -6345,29 +7174,69 @@ export interface BrandStyleguideParams { * value is 300000ms (5 minutes). */ timeoutMS?: number; -} -export interface BrandWebScrapeHTMLParams { /** - * Full URL to scrape (must include http:// or https:// protocol) + * Optional browser wait time in milliseconds after initial page load before + * collecting images. Min: 0. Max: 30000 (30 seconds). */ - url: string; + waitForMs?: number; } -export interface BrandWebScrapeImagesParams { +export namespace BrandWebScrapeImagesParams { /** - * Full URL to scrape images from (must include http:// or https:// protocol) + * Optional per-image processing, sent as deep-object query params such as + * enrichment[resolution]=true. */ - url: string; + export interface Enrichment { + /** + * Classify each image by visual asset type. + */ + classification?: boolean; + + /** + * Host materializable images on the Brand.dev CDN and return their URL and MIME + * type. + */ + hostedUrl?: boolean; + + /** + * Per-image enrichment timeout in milliseconds. Default: 30000. Maximum: 60000. + */ + maxTimePerMs?: number; + + /** + * Measure image width and height when possible. + */ + resolution?: boolean; + } } export interface BrandWebScrapeMdParams { /** - * Full URL to scrape and convert to markdown (must include http:// or https:// + * Full URL to scrape into LLM usable Markdown (must include http:// or https:// * protocol) */ url: string; + /** + * CSS selectors to remove before conversion to Markdown. Applied after + * includeSelectors. Exclusion takes precedence: an element matching both is + * removed. Examples: "nav", "footer", ".ad-banner", "[aria-hidden=true]". + */ + excludeSelectors?: Array; + + /** + * Optional outbound HTTP headers forwarded only to the target URL, sent as + * deep-object query params such as headers[X-Custom]=value. When provided, caching + * is bypassed: the result is neither read from nor written to cache. + */ + headers?: { [key: string]: string }; + + /** + * When true, the contents of iframes are rendered to Markdown. + */ + includeFrames?: boolean; + /** * Include image references in Markdown output */ @@ -6378,30 +7247,107 @@ export interface BrandWebScrapeMdParams { */ includeLinks?: boolean; + /** + * CSS selectors. When provided, only matching HTML subtrees (and their + * descendants) are kept before conversion to Markdown. When omitted, the entire + * document is kept. Examples: "article.main", "#content", "[role=main]". + */ + includeSelectors?: Array; + + /** + * Return a cached result if a prior scrape for the same parameters exists and is + * younger than this many milliseconds. Defaults to 1 day (86400000 ms) when + * omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. + */ + maxAgeMs?: number; + + /** + * PDF parsing controls. Use start/end to limit text extraction and OCR to an + * inclusive 1-based page range. + */ + pdf?: BrandWebScrapeMdParams.Pdf; + /** * Shorten base64-encoded image data in the Markdown output */ shortenBase64Images?: boolean; + /** + * Optional timeout in milliseconds for the request. If the request takes longer + * than this value, it will be aborted with a 408 status code. Maximum allowed + * value is 300000ms (5 minutes). + */ + timeoutMS?: number; + /** * Extract only the main content of the page, excluding headers, footers, sidebars, * and navigation */ useMainContentOnly?: boolean; + + /** + * Optional browser wait time in milliseconds after initial page load before + * converting the page to Markdown. Min: 0. Max: 30000 (30 seconds). + */ + waitForMs?: number; +} + +export namespace BrandWebScrapeMdParams { + /** + * PDF parsing controls. Use start/end to limit text extraction and OCR to an + * inclusive 1-based page range. + */ + export interface Pdf { + /** + * Last 1-based PDF page to parse. When omitted, parsing ends at the last page. + * Must be greater than or equal to start when both are provided. + */ + end?: number; + + /** + * When true, PDF URLs are fetched and parsed. When false, PDF URLs are skipped and + * a 400 WEBSITE_ACCESS_ERROR is returned. + */ + shouldParse?: boolean; + + /** + * First 1-based PDF page to parse. When omitted, parsing starts at the first page. + */ + start?: number; + } } export interface BrandWebScrapeSitemapParams { /** - * Domain name to crawl sitemaps for (e.g., 'example.com'). The domain will be - * automatically normalized and validated. + * Domain to build a sitemap for */ domain: string; + /** + * Optional outbound HTTP headers forwarded only to the target URL, sent as + * deep-object query params such as headers[X-Custom]=value. When provided, caching + * is bypassed: the result is neither read from nor written to cache. + */ + headers?: { [key: string]: string }; + /** * Maximum number of links to return from the sitemap crawl. Defaults to 10,000. * Minimum is 1, maximum is 100,000. */ maxLinks?: number; + + /** + * Optional timeout in milliseconds for the request. If the request takes longer + * than this value, it will be aborted with a 408 status code. Maximum allowed + * value is 300000ms (5 minutes). + */ + timeoutMS?: number; + + /** + * Optional RE2-compatible regex pattern. Only URLs matching this pattern are + * returned and counted against maxLinks. + */ + urlRegex?: string; } export declare namespace Brand { @@ -6410,7 +7356,6 @@ export declare namespace Brand { type BrandAIProductResponse as BrandAIProductResponse, type BrandAIProductsResponse as BrandAIProductsResponse, type BrandAIQueryResponse as BrandAIQueryResponse, - type BrandFontsResponse as BrandFontsResponse, type BrandIdentifyFromTransactionResponse as BrandIdentifyFromTransactionResponse, type BrandPrefetchResponse as BrandPrefetchResponse, type BrandPrefetchByEmailResponse as BrandPrefetchByEmailResponse, @@ -6418,10 +7363,7 @@ export declare namespace Brand { type BrandRetrieveByIsinResponse as BrandRetrieveByIsinResponse, type BrandRetrieveByNameResponse as BrandRetrieveByNameResponse, type BrandRetrieveByTickerResponse as BrandRetrieveByTickerResponse, - type BrandRetrieveNaicsResponse as BrandRetrieveNaicsResponse, type BrandRetrieveSimplifiedResponse as BrandRetrieveSimplifiedResponse, - type BrandScreenshotResponse as BrandScreenshotResponse, - type BrandStyleguideResponse as BrandStyleguideResponse, type BrandWebScrapeHTMLResponse as BrandWebScrapeHTMLResponse, type BrandWebScrapeImagesResponse as BrandWebScrapeImagesResponse, type BrandWebScrapeMdResponse as BrandWebScrapeMdResponse, @@ -6430,7 +7372,6 @@ export declare namespace Brand { type BrandAIProductParams as BrandAIProductParams, type BrandAIProductsParams as BrandAIProductsParams, type BrandAIQueryParams as BrandAIQueryParams, - type BrandFontsParams as BrandFontsParams, type BrandIdentifyFromTransactionParams as BrandIdentifyFromTransactionParams, type BrandPrefetchParams as BrandPrefetchParams, type BrandPrefetchByEmailParams as BrandPrefetchByEmailParams, @@ -6438,10 +7379,7 @@ export declare namespace Brand { type BrandRetrieveByIsinParams as BrandRetrieveByIsinParams, type BrandRetrieveByNameParams as BrandRetrieveByNameParams, type BrandRetrieveByTickerParams as BrandRetrieveByTickerParams, - type BrandRetrieveNaicsParams as BrandRetrieveNaicsParams, type BrandRetrieveSimplifiedParams as BrandRetrieveSimplifiedParams, - type BrandScreenshotParams as BrandScreenshotParams, - type BrandStyleguideParams as BrandStyleguideParams, type BrandWebScrapeHTMLParams as BrandWebScrapeHTMLParams, type BrandWebScrapeImagesParams as BrandWebScrapeImagesParams, type BrandWebScrapeMdParams as BrandWebScrapeMdParams, diff --git a/src/resources/index.ts b/src/resources/index.ts index 7031f6b7..24b951d7 100644 --- a/src/resources/index.ts +++ b/src/resources/index.ts @@ -6,7 +6,6 @@ export { type BrandAIProductResponse, type BrandAIProductsResponse, type BrandAIQueryResponse, - type BrandFontsResponse, type BrandIdentifyFromTransactionResponse, type BrandPrefetchResponse, type BrandPrefetchByEmailResponse, @@ -14,10 +13,7 @@ export { type BrandRetrieveByIsinResponse, type BrandRetrieveByNameResponse, type BrandRetrieveByTickerResponse, - type BrandRetrieveNaicsResponse, type BrandRetrieveSimplifiedResponse, - type BrandScreenshotResponse, - type BrandStyleguideResponse, type BrandWebScrapeHTMLResponse, type BrandWebScrapeImagesResponse, type BrandWebScrapeMdResponse, @@ -26,7 +22,6 @@ export { type BrandAIProductParams, type BrandAIProductsParams, type BrandAIQueryParams, - type BrandFontsParams, type BrandIdentifyFromTransactionParams, type BrandPrefetchParams, type BrandPrefetchByEmailParams, @@ -34,10 +29,7 @@ export { type BrandRetrieveByIsinParams, type BrandRetrieveByNameParams, type BrandRetrieveByTickerParams, - type BrandRetrieveNaicsParams, type BrandRetrieveSimplifiedParams, - type BrandScreenshotParams, - type BrandStyleguideParams, type BrandWebScrapeHTMLParams, type BrandWebScrapeImagesParams, type BrandWebScrapeMdParams, diff --git a/src/version.ts b/src/version.ts index 48199984..3f1d4329 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const VERSION = '0.34.0'; // x-release-please-version +export const VERSION = '0.35.0'; // x-release-please-version diff --git a/tests/api-resources/brand.test.ts b/tests/api-resources/brand.test.ts index efd4b57f..b4a4cc3a 100644 --- a/tests/api-resources/brand.test.ts +++ b/tests/api-resources/brand.test.ts @@ -24,7 +24,8 @@ describe('resource brand', () => { test.skip('retrieve: required and optional params', async () => { const response = await client.brand.retrieve({ domain: 'domain', - force_language: 'albanian', + force_language: 'afrikaans', + maxAgeMs: 86400000, maxSpeed: true, timeoutMS: 1000, }); @@ -44,7 +45,11 @@ describe('resource brand', () => { // Mock server tests are disabled test.skip('aiProduct: required and optional params', async () => { - const response = await client.brand.aiProduct({ url: 'https://example.com', timeoutMS: 1000 }); + const response = await client.brand.aiProduct({ + url: 'https://example.com', + maxAgeMs: 0, + timeoutMS: 1000, + }); }); // Mock server tests are disabled @@ -63,6 +68,7 @@ describe('resource brand', () => { test.skip('aiProducts: required and optional params', async () => { const response = await client.brand.aiProducts({ domain: 'domain', + maxAgeMs: 0, maxProducts: 1, timeoutMS: 1000, }); @@ -119,23 +125,6 @@ describe('resource brand', () => { }); }); - // Mock server tests are disabled - test.skip('fonts: only required params', async () => { - const responsePromise = client.brand.fonts({ domain: 'domain' }); - const rawResponse = await responsePromise.asResponse(); - expect(rawResponse).toBeInstanceOf(Response); - const response = await responsePromise; - expect(response).not.toBeInstanceOf(Response); - const dataAndResponse = await responsePromise.withResponse(); - expect(dataAndResponse.data).toBe(response); - expect(dataAndResponse.response).toBe(rawResponse); - }); - - // Mock server tests are disabled - test.skip('fonts: required and optional params', async () => { - const response = await client.brand.fonts({ domain: 'domain', timeoutMS: 1000 }); - }); - // Mock server tests are disabled test.skip('identifyFromTransaction: only required params', async () => { const responsePromise = client.brand.identifyFromTransaction({ transaction_info: 'transaction_info' }); @@ -154,7 +143,7 @@ describe('resource brand', () => { transaction_info: 'transaction_info', city: 'city', country_gl: 'ad', - force_language: 'albanian', + force_language: 'afrikaans', high_confidence_only: true, maxSpeed: true, mcc: 'mcc', @@ -213,7 +202,8 @@ describe('resource brand', () => { test.skip('retrieveByEmail: required and optional params', async () => { const response = await client.brand.retrieveByEmail({ email: 'dev@stainless.com', - force_language: 'albanian', + force_language: 'afrikaans', + maxAgeMs: 86400000, maxSpeed: true, timeoutMS: 1000, }); @@ -235,7 +225,8 @@ describe('resource brand', () => { test.skip('retrieveByIsin: required and optional params', async () => { const response = await client.brand.retrieveByIsin({ isin: 'SE60513A9993', - force_language: 'albanian', + force_language: 'afrikaans', + maxAgeMs: 86400000, maxSpeed: true, timeoutMS: 1000, }); @@ -258,7 +249,8 @@ describe('resource brand', () => { const response = await client.brand.retrieveByName({ name: 'xxx', country_gl: 'ad', - force_language: 'albanian', + force_language: 'afrikaans', + maxAgeMs: 86400000, maxSpeed: true, timeoutMS: 1000, }); @@ -280,35 +272,14 @@ describe('resource brand', () => { test.skip('retrieveByTicker: required and optional params', async () => { const response = await client.brand.retrieveByTicker({ ticker: 'ticker', - force_language: 'albanian', + force_language: 'afrikaans', + maxAgeMs: 86400000, maxSpeed: true, ticker_exchange: 'AMEX', timeoutMS: 1000, }); }); - // Mock server tests are disabled - test.skip('retrieveNaics: only required params', async () => { - const responsePromise = client.brand.retrieveNaics({ input: 'input' }); - const rawResponse = await responsePromise.asResponse(); - expect(rawResponse).toBeInstanceOf(Response); - const response = await responsePromise; - expect(response).not.toBeInstanceOf(Response); - const dataAndResponse = await responsePromise.withResponse(); - expect(dataAndResponse.data).toBe(response); - expect(dataAndResponse.response).toBe(rawResponse); - }); - - // Mock server tests are disabled - test.skip('retrieveNaics: required and optional params', async () => { - const response = await client.brand.retrieveNaics({ - input: 'input', - maxResults: 1, - minResults: 1, - timeoutMS: 1000, - }); - }); - // Mock server tests are disabled test.skip('retrieveSimplified: only required params', async () => { const responsePromise = client.brand.retrieveSimplified({ domain: 'domain' }); @@ -323,58 +294,13 @@ describe('resource brand', () => { // Mock server tests are disabled test.skip('retrieveSimplified: required and optional params', async () => { - const response = await client.brand.retrieveSimplified({ domain: 'domain', timeoutMS: 1000 }); - }); - - // Mock server tests are disabled - test.skip('screenshot: only required params', async () => { - const responsePromise = client.brand.screenshot({ domain: 'domain' }); - const rawResponse = await responsePromise.asResponse(); - expect(rawResponse).toBeInstanceOf(Response); - const response = await responsePromise; - expect(response).not.toBeInstanceOf(Response); - const dataAndResponse = await responsePromise.withResponse(); - expect(dataAndResponse.data).toBe(response); - expect(dataAndResponse.response).toBe(rawResponse); - }); - - // Mock server tests are disabled - test.skip('screenshot: required and optional params', async () => { - const response = await client.brand.screenshot({ + const response = await client.brand.retrieveSimplified({ domain: 'domain', - fullScreenshot: 'true', - page: 'login', - prioritize: 'speed', + maxAgeMs: 86400000, + timeoutMS: 1000, }); }); - // Mock server tests are disabled - test.skip('styleguide', async () => { - const responsePromise = client.brand.styleguide(); - const rawResponse = await responsePromise.asResponse(); - expect(rawResponse).toBeInstanceOf(Response); - const response = await responsePromise; - expect(response).not.toBeInstanceOf(Response); - const dataAndResponse = await responsePromise.withResponse(); - expect(dataAndResponse.data).toBe(response); - expect(dataAndResponse.response).toBe(rawResponse); - }); - - // Mock server tests are disabled - test.skip('styleguide: request options and params are passed correctly', async () => { - // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error - await expect( - client.brand.styleguide( - { - directUrl: 'https://example.com', - domain: 'domain', - timeoutMS: 1000, - }, - { path: '/_stainless_unknown_path' }, - ), - ).rejects.toThrow(BrandDev.NotFoundError); - }); - // Mock server tests are disabled test.skip('webScrapeHTML: only required params', async () => { const responsePromise = client.brand.webScrapeHTML({ url: 'https://example.com' }); @@ -389,7 +315,22 @@ describe('resource brand', () => { // Mock server tests are disabled test.skip('webScrapeHTML: required and optional params', async () => { - const response = await client.brand.webScrapeHTML({ url: 'https://example.com' }); + const response = await client.brand.webScrapeHTML({ + url: 'https://example.com', + excludeSelectors: ['string'], + headers: { foo: 'J!' }, + includeFrames: true, + includeSelectors: ['string'], + maxAgeMs: 0, + pdf: { + end: 1, + shouldParse: true, + start: 1, + }, + timeoutMS: 1000, + useMainContentOnly: true, + waitForMs: 0, + }); }); // Mock server tests are disabled @@ -406,7 +347,19 @@ describe('resource brand', () => { // Mock server tests are disabled test.skip('webScrapeImages: required and optional params', async () => { - const response = await client.brand.webScrapeImages({ url: 'https://example.com' }); + const response = await client.brand.webScrapeImages({ + url: 'https://example.com', + enrichment: { + classification: true, + hostedUrl: true, + maxTimePerMs: 1, + resolution: true, + }, + headers: { foo: 'J!' }, + maxAgeMs: 0, + timeoutMS: 1000, + waitForMs: 0, + }); }); // Mock server tests are disabled @@ -425,10 +378,22 @@ describe('resource brand', () => { test.skip('webScrapeMd: required and optional params', async () => { const response = await client.brand.webScrapeMd({ url: 'https://example.com', + excludeSelectors: ['string'], + headers: { foo: 'J!' }, + includeFrames: true, includeImages: true, includeLinks: true, + includeSelectors: ['string'], + maxAgeMs: 0, + pdf: { + end: 1, + shouldParse: true, + start: 1, + }, shortenBase64Images: true, + timeoutMS: 1000, useMainContentOnly: true, + waitForMs: 0, }); }); @@ -446,6 +411,12 @@ describe('resource brand', () => { // Mock server tests are disabled test.skip('webScrapeSitemap: required and optional params', async () => { - const response = await client.brand.webScrapeSitemap({ domain: 'domain', maxLinks: 1 }); + const response = await client.brand.webScrapeSitemap({ + domain: 'domain', + headers: { foo: 'J!' }, + maxLinks: 1, + timeoutMS: 1000, + urlRegex: '^https?://[^/]+/blog/', + }); }); }); diff --git a/tests/qs/empty-keys-cases.ts b/tests/qs/empty-keys-cases.ts new file mode 100644 index 00000000..ea2c1b0a --- /dev/null +++ b/tests/qs/empty-keys-cases.ts @@ -0,0 +1,271 @@ +export const empty_test_cases = [ + { + input: '&', + with_empty_keys: {}, + stringify_output: { + brackets: '', + indices: '', + repeat: '', + }, + no_empty_keys: {}, + }, + { + input: '&&', + with_empty_keys: {}, + stringify_output: { + brackets: '', + indices: '', + repeat: '', + }, + no_empty_keys: {}, + }, + { + input: '&=', + with_empty_keys: { '': '' }, + stringify_output: { + brackets: '=', + indices: '=', + repeat: '=', + }, + no_empty_keys: {}, + }, + { + input: '&=&', + with_empty_keys: { '': '' }, + stringify_output: { + brackets: '=', + indices: '=', + repeat: '=', + }, + no_empty_keys: {}, + }, + { + input: '&=&=', + with_empty_keys: { '': ['', ''] }, + stringify_output: { + brackets: '[]=&[]=', + indices: '[0]=&[1]=', + repeat: '=&=', + }, + no_empty_keys: {}, + }, + { + input: '&=&=&', + with_empty_keys: { '': ['', ''] }, + stringify_output: { + brackets: '[]=&[]=', + indices: '[0]=&[1]=', + repeat: '=&=', + }, + no_empty_keys: {}, + }, + { + input: '=', + with_empty_keys: { '': '' }, + no_empty_keys: {}, + stringify_output: { + brackets: '=', + indices: '=', + repeat: '=', + }, + }, + { + input: '=&', + with_empty_keys: { '': '' }, + stringify_output: { + brackets: '=', + indices: '=', + repeat: '=', + }, + no_empty_keys: {}, + }, + { + input: '=&&&', + with_empty_keys: { '': '' }, + stringify_output: { + brackets: '=', + indices: '=', + repeat: '=', + }, + no_empty_keys: {}, + }, + { + input: '=&=&=&', + with_empty_keys: { '': ['', '', ''] }, + stringify_output: { + brackets: '[]=&[]=&[]=', + indices: '[0]=&[1]=&[2]=', + repeat: '=&=&=', + }, + no_empty_keys: {}, + }, + { + input: '=&a[]=b&a[1]=c', + with_empty_keys: { '': '', a: ['b', 'c'] }, + stringify_output: { + brackets: '=&a[]=b&a[]=c', + indices: '=&a[0]=b&a[1]=c', + repeat: '=&a=b&a=c', + }, + no_empty_keys: { a: ['b', 'c'] }, + }, + { + input: '=a', + with_empty_keys: { '': 'a' }, + no_empty_keys: {}, + stringify_output: { + brackets: '=a', + indices: '=a', + repeat: '=a', + }, + }, + { + input: 'a==a', + with_empty_keys: { a: '=a' }, + no_empty_keys: { a: '=a' }, + stringify_output: { + brackets: 'a==a', + indices: 'a==a', + repeat: 'a==a', + }, + }, + { + input: '=&a[]=b', + with_empty_keys: { '': '', a: ['b'] }, + stringify_output: { + brackets: '=&a[]=b', + indices: '=&a[0]=b', + repeat: '=&a=b', + }, + no_empty_keys: { a: ['b'] }, + }, + { + input: '=&a[]=b&a[]=c&a[2]=d', + with_empty_keys: { '': '', a: ['b', 'c', 'd'] }, + stringify_output: { + brackets: '=&a[]=b&a[]=c&a[]=d', + indices: '=&a[0]=b&a[1]=c&a[2]=d', + repeat: '=&a=b&a=c&a=d', + }, + no_empty_keys: { a: ['b', 'c', 'd'] }, + }, + { + input: '=a&=b', + with_empty_keys: { '': ['a', 'b'] }, + stringify_output: { + brackets: '[]=a&[]=b', + indices: '[0]=a&[1]=b', + repeat: '=a&=b', + }, + no_empty_keys: {}, + }, + { + input: '=a&foo=b', + with_empty_keys: { '': 'a', foo: 'b' }, + no_empty_keys: { foo: 'b' }, + stringify_output: { + brackets: '=a&foo=b', + indices: '=a&foo=b', + repeat: '=a&foo=b', + }, + }, + { + input: 'a[]=b&a=c&=', + with_empty_keys: { '': '', a: ['b', 'c'] }, + stringify_output: { + brackets: '=&a[]=b&a[]=c', + indices: '=&a[0]=b&a[1]=c', + repeat: '=&a=b&a=c', + }, + no_empty_keys: { a: ['b', 'c'] }, + }, + { + input: 'a[]=b&a=c&=', + with_empty_keys: { '': '', a: ['b', 'c'] }, + stringify_output: { + brackets: '=&a[]=b&a[]=c', + indices: '=&a[0]=b&a[1]=c', + repeat: '=&a=b&a=c', + }, + no_empty_keys: { a: ['b', 'c'] }, + }, + { + input: 'a[0]=b&a=c&=', + with_empty_keys: { '': '', a: ['b', 'c'] }, + stringify_output: { + brackets: '=&a[]=b&a[]=c', + indices: '=&a[0]=b&a[1]=c', + repeat: '=&a=b&a=c', + }, + no_empty_keys: { a: ['b', 'c'] }, + }, + { + input: 'a=b&a[]=c&=', + with_empty_keys: { '': '', a: ['b', 'c'] }, + stringify_output: { + brackets: '=&a[]=b&a[]=c', + indices: '=&a[0]=b&a[1]=c', + repeat: '=&a=b&a=c', + }, + no_empty_keys: { a: ['b', 'c'] }, + }, + { + input: 'a=b&a[0]=c&=', + with_empty_keys: { '': '', a: ['b', 'c'] }, + stringify_output: { + brackets: '=&a[]=b&a[]=c', + indices: '=&a[0]=b&a[1]=c', + repeat: '=&a=b&a=c', + }, + no_empty_keys: { a: ['b', 'c'] }, + }, + { + input: '[]=a&[]=b& []=1', + with_empty_keys: { '': ['a', 'b'], ' ': ['1'] }, + stringify_output: { + brackets: '[]=a&[]=b& []=1', + indices: '[0]=a&[1]=b& [0]=1', + repeat: '=a&=b& =1', + }, + no_empty_keys: { 0: 'a', 1: 'b', ' ': ['1'] }, + }, + { + input: '[0]=a&[1]=b&a[0]=1&a[1]=2', + with_empty_keys: { '': ['a', 'b'], a: ['1', '2'] }, + no_empty_keys: { 0: 'a', 1: 'b', a: ['1', '2'] }, + stringify_output: { + brackets: '[]=a&[]=b&a[]=1&a[]=2', + indices: '[0]=a&[1]=b&a[0]=1&a[1]=2', + repeat: '=a&=b&a=1&a=2', + }, + }, + { + input: '[deep]=a&[deep]=2', + with_empty_keys: { '': { deep: ['a', '2'] } }, + stringify_output: { + brackets: '[deep][]=a&[deep][]=2', + indices: '[deep][0]=a&[deep][1]=2', + repeat: '[deep]=a&[deep]=2', + }, + no_empty_keys: { deep: ['a', '2'] }, + }, + { + input: '%5B0%5D=a&%5B1%5D=b', + with_empty_keys: { '': ['a', 'b'] }, + stringify_output: { + brackets: '[]=a&[]=b', + indices: '[0]=a&[1]=b', + repeat: '=a&=b', + }, + no_empty_keys: { 0: 'a', 1: 'b' }, + }, +] satisfies { + input: string; + with_empty_keys: Record; + stringify_output: { + brackets: string; + indices: string; + repeat: string; + }; + no_empty_keys: Record; +}[]; diff --git a/tests/qs/stringify.test.ts b/tests/qs/stringify.test.ts new file mode 100644 index 00000000..74e4af88 --- /dev/null +++ b/tests/qs/stringify.test.ts @@ -0,0 +1,2232 @@ +import iconv from 'iconv-lite'; +import { stringify } from 'brand.dev/internal/qs'; +import { encode } from 'brand.dev/internal/qs/utils'; +import { StringifyOptions } from 'brand.dev/internal/qs/types'; +import { empty_test_cases } from './empty-keys-cases'; +import assert from 'assert'; + +describe('stringify()', function () { + test('stringifies a querystring object', function () { + expect(stringify({ a: 'b' })).toBe('a=b'); + expect(stringify({ a: 1 })).toBe('a=1'); + expect(stringify({ a: 1, b: 2 })).toBe('a=1&b=2'); + expect(stringify({ a: 'A_Z' })).toBe('a=A_Z'); + expect(stringify({ a: '€' })).toBe('a=%E2%82%AC'); + expect(stringify({ a: '' })).toBe('a=%EE%80%80'); + expect(stringify({ a: 'א' })).toBe('a=%D7%90'); + expect(stringify({ a: '𐐷' })).toBe('a=%F0%90%90%B7'); + }); + + test('stringifies falsy values', function () { + expect(stringify(undefined)).toBe(''); + expect(stringify(null)).toBe(''); + expect(stringify(null, { strictNullHandling: true })).toBe(''); + expect(stringify(false)).toBe(''); + expect(stringify(0)).toBe(''); + }); + + test('stringifies symbols', function () { + expect(stringify(Symbol.iterator)).toBe(''); + expect(stringify([Symbol.iterator])).toBe('0=Symbol%28Symbol.iterator%29'); + expect(stringify({ a: Symbol.iterator })).toBe('a=Symbol%28Symbol.iterator%29'); + expect(stringify({ a: [Symbol.iterator] }, { encodeValuesOnly: true, arrayFormat: 'brackets' })).toBe( + 'a[]=Symbol%28Symbol.iterator%29', + ); + }); + + test('stringifies bigints', function () { + var three = BigInt(3); + // @ts-expect-error + var encodeWithN = function (value, defaultEncoder, charset) { + var result = defaultEncoder(value, defaultEncoder, charset); + return typeof value === 'bigint' ? result + 'n' : result; + }; + + expect(stringify(three)).toBe(''); + expect(stringify([three])).toBe('0=3'); + expect(stringify([three], { encoder: encodeWithN })).toBe('0=3n'); + expect(stringify({ a: three })).toBe('a=3'); + expect(stringify({ a: three }, { encoder: encodeWithN })).toBe('a=3n'); + expect(stringify({ a: [three] }, { encodeValuesOnly: true, arrayFormat: 'brackets' })).toBe('a[]=3'); + expect( + stringify({ a: [three] }, { encodeValuesOnly: true, encoder: encodeWithN, arrayFormat: 'brackets' }), + ).toBe('a[]=3n'); + }); + + test('encodes dot in key of object when encodeDotInKeys and allowDots is provided', function () { + expect( + stringify({ 'name.obj': { first: 'John', last: 'Doe' } }, { allowDots: false, encodeDotInKeys: false }), + ).toBe('name.obj%5Bfirst%5D=John&name.obj%5Blast%5D=Doe'); + expect( + stringify({ 'name.obj': { first: 'John', last: 'Doe' } }, { allowDots: true, encodeDotInKeys: false }), + ).toBe('name.obj.first=John&name.obj.last=Doe'); + expect( + stringify({ 'name.obj': { first: 'John', last: 'Doe' } }, { allowDots: false, encodeDotInKeys: true }), + ).toBe('name%252Eobj%5Bfirst%5D=John&name%252Eobj%5Blast%5D=Doe'); + expect( + stringify({ 'name.obj': { first: 'John', last: 'Doe' } }, { allowDots: true, encodeDotInKeys: true }), + ).toBe('name%252Eobj.first=John&name%252Eobj.last=Doe'); + + // st.equal( + // stringify( + // { 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } }, + // { allowDots: false, encodeDotInKeys: false }, + // ), + // 'name.obj.subobject%5Bfirst.godly.name%5D=John&name.obj.subobject%5Blast%5D=Doe', + // 'with allowDots false and encodeDotInKeys false', + // ); + // st.equal( + // stringify( + // { 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } }, + // { allowDots: true, encodeDotInKeys: false }, + // ), + // 'name.obj.subobject.first.godly.name=John&name.obj.subobject.last=Doe', + // 'with allowDots false and encodeDotInKeys false', + // ); + // st.equal( + // stringify( + // { 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } }, + // { allowDots: false, encodeDotInKeys: true }, + // ), + // 'name%252Eobj%252Esubobject%5Bfirst.godly.name%5D=John&name%252Eobj%252Esubobject%5Blast%5D=Doe', + // 'with allowDots false and encodeDotInKeys true', + // ); + // st.equal( + // stringify( + // { 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } }, + // { allowDots: true, encodeDotInKeys: true }, + // ), + // 'name%252Eobj%252Esubobject.first%252Egodly%252Ename=John&name%252Eobj%252Esubobject.last=Doe', + // 'with allowDots true and encodeDotInKeys true', + // ); + expect( + stringify( + { 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } }, + { allowDots: false, encodeDotInKeys: false }, + ), + ).toBe('name.obj.subobject%5Bfirst.godly.name%5D=John&name.obj.subobject%5Blast%5D=Doe'); + expect( + stringify( + { 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } }, + { allowDots: true, encodeDotInKeys: false }, + ), + ).toBe('name.obj.subobject.first.godly.name=John&name.obj.subobject.last=Doe'); + expect( + stringify( + { 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } }, + { allowDots: false, encodeDotInKeys: true }, + ), + ).toBe('name%252Eobj%252Esubobject%5Bfirst.godly.name%5D=John&name%252Eobj%252Esubobject%5Blast%5D=Doe'); + expect( + stringify( + { 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } }, + { allowDots: true, encodeDotInKeys: true }, + ), + ).toBe('name%252Eobj%252Esubobject.first%252Egodly%252Ename=John&name%252Eobj%252Esubobject.last=Doe'); + }); + + test('should encode dot in key of object, and automatically set allowDots to `true` when encodeDotInKeys is true and allowDots in undefined', function () { + // st.equal( + // stringify( + // { 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } }, + // { encodeDotInKeys: true }, + // ), + // 'name%252Eobj%252Esubobject.first%252Egodly%252Ename=John&name%252Eobj%252Esubobject.last=Doe', + // 'with allowDots undefined and encodeDotInKeys true', + // ); + expect( + stringify( + { 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } }, + { encodeDotInKeys: true }, + ), + ).toBe('name%252Eobj%252Esubobject.first%252Egodly%252Ename=John&name%252Eobj%252Esubobject.last=Doe'); + }); + + test('should encode dot in key of object when encodeDotInKeys and allowDots is provided, and nothing else when encodeValuesOnly is provided', function () { + // st.equal( + // stringify( + // { 'name.obj': { first: 'John', last: 'Doe' } }, + // { + // encodeDotInKeys: true, + // allowDots: true, + // encodeValuesOnly: true, + // }, + // ), + // 'name%2Eobj.first=John&name%2Eobj.last=Doe', + // ); + expect( + stringify( + { 'name.obj': { first: 'John', last: 'Doe' } }, + { + encodeDotInKeys: true, + allowDots: true, + encodeValuesOnly: true, + }, + ), + ).toBe('name%2Eobj.first=John&name%2Eobj.last=Doe'); + + // st.equal( + // stringify( + // { 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } }, + // { allowDots: true, encodeDotInKeys: true, encodeValuesOnly: true }, + // ), + // 'name%2Eobj%2Esubobject.first%2Egodly%2Ename=John&name%2Eobj%2Esubobject.last=Doe', + // ); + expect( + stringify( + { 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } }, + { allowDots: true, encodeDotInKeys: true, encodeValuesOnly: true }, + ), + ).toBe('name%2Eobj%2Esubobject.first%2Egodly%2Ename=John&name%2Eobj%2Esubobject.last=Doe'); + }); + + test('throws when `commaRoundTrip` is not a boolean', function () { + // st['throws']( + // function () { + // stringify({}, { commaRoundTrip: 'not a boolean' }); + // }, + // TypeError, + // 'throws when `commaRoundTrip` is not a boolean', + // ); + expect(() => { + // @ts-expect-error + stringify({}, { commaRoundTrip: 'not a boolean' }); + }).toThrow(TypeError); + }); + + test('throws when `encodeDotInKeys` is not a boolean', function () { + // st['throws'](function () { + // stringify({ a: [], b: 'zz' }, { encodeDotInKeys: 'foobar' }); + // }, TypeError); + expect(() => { + // @ts-expect-error + stringify({ a: [], b: 'zz' }, { encodeDotInKeys: 'foobar' }); + }).toThrow(TypeError); + + // st['throws'](function () { + // stringify({ a: [], b: 'zz' }, { encodeDotInKeys: 0 }); + // }, TypeError); + expect(() => { + // @ts-expect-error + stringify({ a: [], b: 'zz' }, { encodeDotInKeys: 0 }); + }).toThrow(TypeError); + + // st['throws'](function () { + // stringify({ a: [], b: 'zz' }, { encodeDotInKeys: NaN }); + // }, TypeError); + expect(() => { + // @ts-expect-error + stringify({ a: [], b: 'zz' }, { encodeDotInKeys: NaN }); + }).toThrow(TypeError); + + // st['throws'](function () { + // stringify({ a: [], b: 'zz' }, { encodeDotInKeys: null }); + // }, TypeError); + expect(() => { + // @ts-expect-error + stringify({ a: [], b: 'zz' }, { encodeDotInKeys: null }); + }).toThrow(TypeError); + }); + + test('adds query prefix', function () { + // st.equal(stringify({ a: 'b' }, { addQueryPrefix: true }), '?a=b'); + expect(stringify({ a: 'b' }, { addQueryPrefix: true })).toBe('?a=b'); + }); + + test('with query prefix, outputs blank string given an empty object', function () { + // st.equal(stringify({}, { addQueryPrefix: true }), ''); + expect(stringify({}, { addQueryPrefix: true })).toBe(''); + }); + + test('stringifies nested falsy values', function () { + // st.equal(stringify({ a: { b: { c: null } } }), 'a%5Bb%5D%5Bc%5D='); + // st.equal( + // stringify({ a: { b: { c: null } } }, { strictNullHandling: true }), + // 'a%5Bb%5D%5Bc%5D', + // ); + // st.equal(stringify({ a: { b: { c: false } } }), 'a%5Bb%5D%5Bc%5D=false'); + expect(stringify({ a: { b: { c: null } } })).toBe('a%5Bb%5D%5Bc%5D='); + expect(stringify({ a: { b: { c: null } } }, { strictNullHandling: true })).toBe('a%5Bb%5D%5Bc%5D'); + expect(stringify({ a: { b: { c: false } } })).toBe('a%5Bb%5D%5Bc%5D=false'); + }); + + test('stringifies a nested object', function () { + // st.equal(stringify({ a: { b: 'c' } }), 'a%5Bb%5D=c'); + // st.equal(stringify({ a: { b: { c: { d: 'e' } } } }), 'a%5Bb%5D%5Bc%5D%5Bd%5D=e'); + expect(stringify({ a: { b: 'c' } })).toBe('a%5Bb%5D=c'); + expect(stringify({ a: { b: { c: { d: 'e' } } } })).toBe('a%5Bb%5D%5Bc%5D%5Bd%5D=e'); + }); + + test('`allowDots` option: stringifies a nested object with dots notation', function () { + // st.equal(stringify({ a: { b: 'c' } }, { allowDots: true }), 'a.b=c'); + // st.equal(stringify({ a: { b: { c: { d: 'e' } } } }, { allowDots: true }), 'a.b.c.d=e'); + expect(stringify({ a: { b: 'c' } }, { allowDots: true })).toBe('a.b=c'); + expect(stringify({ a: { b: { c: { d: 'e' } } } }, { allowDots: true })).toBe('a.b.c.d=e'); + }); + + test('stringifies an array value', function () { + // st.equal( + // stringify({ a: ['b', 'c', 'd'] }, { arrayFormat: 'indices' }), + // 'a%5B0%5D=b&a%5B1%5D=c&a%5B2%5D=d', + // 'indices => indices', + // ); + // st.equal( + // stringify({ a: ['b', 'c', 'd'] }, { arrayFormat: 'brackets' }), + // 'a%5B%5D=b&a%5B%5D=c&a%5B%5D=d', + // 'brackets => brackets', + // ); + // st.equal( + // stringify({ a: ['b', 'c', 'd'] }, { arrayFormat: 'comma' }), + // 'a=b%2Cc%2Cd', + // 'comma => comma', + // ); + // st.equal( + // stringify({ a: ['b', 'c', 'd'] }, { arrayFormat: 'comma', commaRoundTrip: true }), + // 'a=b%2Cc%2Cd', + // 'comma round trip => comma', + // ); + // st.equal( + // stringify({ a: ['b', 'c', 'd'] }), + // 'a%5B0%5D=b&a%5B1%5D=c&a%5B2%5D=d', + // 'default => indices', + // ); + expect(stringify({ a: ['b', 'c', 'd'] }, { arrayFormat: 'indices' })).toBe( + 'a%5B0%5D=b&a%5B1%5D=c&a%5B2%5D=d', + ); + expect(stringify({ a: ['b', 'c', 'd'] }, { arrayFormat: 'brackets' })).toBe( + 'a%5B%5D=b&a%5B%5D=c&a%5B%5D=d', + ); + expect(stringify({ a: ['b', 'c', 'd'] }, { arrayFormat: 'comma' })).toBe('a=b%2Cc%2Cd'); + expect(stringify({ a: ['b', 'c', 'd'] }, { arrayFormat: 'comma', commaRoundTrip: true })).toBe( + 'a=b%2Cc%2Cd', + ); + expect(stringify({ a: ['b', 'c', 'd'] })).toBe('a%5B0%5D=b&a%5B1%5D=c&a%5B2%5D=d'); + }); + + test('`skipNulls` option', function () { + // st.equal( + // stringify({ a: 'b', c: null }, { skipNulls: true }), + // 'a=b', + // 'omits nulls when asked', + // ); + expect(stringify({ a: 'b', c: null }, { skipNulls: true })).toBe('a=b'); + + // st.equal( + // stringify({ a: { b: 'c', d: null } }, { skipNulls: true }), + // 'a%5Bb%5D=c', + // 'omits nested nulls when asked', + // ); + expect(stringify({ a: { b: 'c', d: null } }, { skipNulls: true })).toBe('a%5Bb%5D=c'); + }); + + test('omits array indices when asked', function () { + // st.equal(stringify({ a: ['b', 'c', 'd'] }, { indices: false }), 'a=b&a=c&a=d'); + expect(stringify({ a: ['b', 'c', 'd'] }, { indices: false })).toBe('a=b&a=c&a=d'); + }); + + test('omits object key/value pair when value is empty array', function () { + // st.equal(stringify({ a: [], b: 'zz' }), 'b=zz'); + expect(stringify({ a: [], b: 'zz' })).toBe('b=zz'); + }); + + test('should not omit object key/value pair when value is empty array and when asked', function () { + // st.equal(stringify({ a: [], b: 'zz' }), 'b=zz'); + // st.equal(stringify({ a: [], b: 'zz' }, { allowEmptyArrays: false }), 'b=zz'); + // st.equal(stringify({ a: [], b: 'zz' }, { allowEmptyArrays: true }), 'a[]&b=zz'); + expect(stringify({ a: [], b: 'zz' })).toBe('b=zz'); + expect(stringify({ a: [], b: 'zz' }, { allowEmptyArrays: false })).toBe('b=zz'); + expect(stringify({ a: [], b: 'zz' }, { allowEmptyArrays: true })).toBe('a[]&b=zz'); + }); + + test('should throw when allowEmptyArrays is not of type boolean', function () { + // st['throws'](function () { + // stringify({ a: [], b: 'zz' }, { allowEmptyArrays: 'foobar' }); + // }, TypeError); + expect(() => { + // @ts-expect-error + stringify({ a: [], b: 'zz' }, { allowEmptyArrays: 'foobar' }); + }).toThrow(TypeError); + + // st['throws'](function () { + // stringify({ a: [], b: 'zz' }, { allowEmptyArrays: 0 }); + // }, TypeError); + expect(() => { + // @ts-expect-error + stringify({ a: [], b: 'zz' }, { allowEmptyArrays: 0 }); + }).toThrow(TypeError); + + // st['throws'](function () { + // stringify({ a: [], b: 'zz' }, { allowEmptyArrays: NaN }); + // }, TypeError); + expect(() => { + // @ts-expect-error + stringify({ a: [], b: 'zz' }, { allowEmptyArrays: NaN }); + }).toThrow(TypeError); + + // st['throws'](function () { + // stringify({ a: [], b: 'zz' }, { allowEmptyArrays: null }); + // }, TypeError); + expect(() => { + // @ts-expect-error + stringify({ a: [], b: 'zz' }, { allowEmptyArrays: null }); + }).toThrow(TypeError); + }); + + test('allowEmptyArrays + strictNullHandling', function () { + // st.equal( + // stringify({ testEmptyArray: [] }, { strictNullHandling: true, allowEmptyArrays: true }), + // 'testEmptyArray[]', + // ); + expect(stringify({ testEmptyArray: [] }, { strictNullHandling: true, allowEmptyArrays: true })).toBe( + 'testEmptyArray[]', + ); + }); + + describe('stringifies an array value with one item vs multiple items', function () { + test('non-array item', function () { + // s2t.equal( + // stringify({ a: 'c' }, { encodeValuesOnly: true, arrayFormat: 'indices' }), + // 'a=c', + // ); + // s2t.equal( + // stringify({ a: 'c' }, { encodeValuesOnly: true, arrayFormat: 'brackets' }), + // 'a=c', + // ); + // s2t.equal(stringify({ a: 'c' }, { encodeValuesOnly: true, arrayFormat: 'comma' }), 'a=c'); + // s2t.equal(stringify({ a: 'c' }, { encodeValuesOnly: true }), 'a=c'); + expect(stringify({ a: 'c' }, { encodeValuesOnly: true, arrayFormat: 'indices' })).toBe('a=c'); + expect(stringify({ a: 'c' }, { encodeValuesOnly: true, arrayFormat: 'brackets' })).toBe('a=c'); + expect(stringify({ a: 'c' }, { encodeValuesOnly: true, arrayFormat: 'comma' })).toBe('a=c'); + expect(stringify({ a: 'c' }, { encodeValuesOnly: true })).toBe('a=c'); + }); + + test('array with a single item', function () { + // s2t.equal( + // stringify({ a: ['c'] }, { encodeValuesOnly: true, arrayFormat: 'indices' }), + // 'a[0]=c', + // ); + // s2t.equal( + // stringify({ a: ['c'] }, { encodeValuesOnly: true, arrayFormat: 'brackets' }), + // 'a[]=c', + // ); + // s2t.equal( + // stringify({ a: ['c'] }, { encodeValuesOnly: true, arrayFormat: 'comma' }), + // 'a=c', + // ); + // s2t.equal( + // stringify( + // { a: ['c'] }, + // { encodeValuesOnly: true, arrayFormat: 'comma', commaRoundTrip: true }, + // ), + // 'a[]=c', + // ); // so it parses back as an array + // s2t.equal(stringify({ a: ['c'] }, { encodeValuesOnly: true }), 'a[0]=c'); + expect(stringify({ a: ['c'] }, { encodeValuesOnly: true, arrayFormat: 'indices' })).toBe('a[0]=c'); + expect(stringify({ a: ['c'] }, { encodeValuesOnly: true, arrayFormat: 'brackets' })).toBe('a[]=c'); + expect(stringify({ a: ['c'] }, { encodeValuesOnly: true, arrayFormat: 'comma' })).toBe('a=c'); + expect( + stringify({ a: ['c'] }, { encodeValuesOnly: true, arrayFormat: 'comma', commaRoundTrip: true }), + ).toBe('a[]=c'); + expect(stringify({ a: ['c'] }, { encodeValuesOnly: true })).toBe('a[0]=c'); + }); + + test('array with multiple items', function () { + // s2t.equal( + // stringify({ a: ['c', 'd'] }, { encodeValuesOnly: true, arrayFormat: 'indices' }), + // 'a[0]=c&a[1]=d', + // ); + // s2t.equal( + // stringify({ a: ['c', 'd'] }, { encodeValuesOnly: true, arrayFormat: 'brackets' }), + // 'a[]=c&a[]=d', + // ); + // s2t.equal( + // stringify({ a: ['c', 'd'] }, { encodeValuesOnly: true, arrayFormat: 'comma' }), + // 'a=c,d', + // ); + // s2t.equal( + // stringify( + // { a: ['c', 'd'] }, + // { encodeValuesOnly: true, arrayFormat: 'comma', commaRoundTrip: true }, + // ), + // 'a=c,d', + // ); + // s2t.equal(stringify({ a: ['c', 'd'] }, { encodeValuesOnly: true }), 'a[0]=c&a[1]=d'); + expect(stringify({ a: ['c', 'd'] }, { encodeValuesOnly: true, arrayFormat: 'indices' })).toBe( + 'a[0]=c&a[1]=d', + ); + expect(stringify({ a: ['c', 'd'] }, { encodeValuesOnly: true, arrayFormat: 'brackets' })).toBe( + 'a[]=c&a[]=d', + ); + expect(stringify({ a: ['c', 'd'] }, { encodeValuesOnly: true, arrayFormat: 'comma' })).toBe('a=c,d'); + expect( + stringify({ a: ['c', 'd'] }, { encodeValuesOnly: true, arrayFormat: 'comma', commaRoundTrip: true }), + ).toBe('a=c,d'); + expect(stringify({ a: ['c', 'd'] }, { encodeValuesOnly: true })).toBe('a[0]=c&a[1]=d'); + }); + + test('array with multiple items with a comma inside', function () { + // s2t.equal( + // stringify({ a: ['c,d', 'e'] }, { encodeValuesOnly: true, arrayFormat: 'comma' }), + // 'a=c%2Cd,e', + // ); + // s2t.equal(stringify({ a: ['c,d', 'e'] }, { arrayFormat: 'comma' }), 'a=c%2Cd%2Ce'); + expect(stringify({ a: ['c,d', 'e'] }, { encodeValuesOnly: true, arrayFormat: 'comma' })).toBe( + 'a=c%2Cd,e', + ); + expect(stringify({ a: ['c,d', 'e'] }, { arrayFormat: 'comma' })).toBe('a=c%2Cd%2Ce'); + + // s2t.equal( + // stringify( + // { a: ['c,d', 'e'] }, + // { encodeValuesOnly: true, arrayFormat: 'comma', commaRoundTrip: true }, + // ), + // 'a=c%2Cd,e', + // ); + // s2t.equal( + // stringify({ a: ['c,d', 'e'] }, { arrayFormat: 'comma', commaRoundTrip: true }), + // 'a=c%2Cd%2Ce', + // ); + expect( + stringify( + { a: ['c,d', 'e'] }, + { encodeValuesOnly: true, arrayFormat: 'comma', commaRoundTrip: true }, + ), + ).toBe('a=c%2Cd,e'); + expect(stringify({ a: ['c,d', 'e'] }, { arrayFormat: 'comma', commaRoundTrip: true })).toBe( + 'a=c%2Cd%2Ce', + ); + }); + }); + + test('stringifies a nested array value', function () { + expect(stringify({ a: { b: ['c', 'd'] } }, { encodeValuesOnly: true, arrayFormat: 'indices' })).toBe( + 'a[b][0]=c&a[b][1]=d', + ); + expect(stringify({ a: { b: ['c', 'd'] } }, { encodeValuesOnly: true, arrayFormat: 'brackets' })).toBe( + 'a[b][]=c&a[b][]=d', + ); + expect(stringify({ a: { b: ['c', 'd'] } }, { encodeValuesOnly: true, arrayFormat: 'comma' })).toBe( + 'a[b]=c,d', + ); + expect(stringify({ a: { b: ['c', 'd'] } }, { encodeValuesOnly: true })).toBe('a[b][0]=c&a[b][1]=d'); + }); + + test('stringifies comma and empty array values', function () { + // st.equal( + // stringify({ a: [',', '', 'c,d%'] }, { encode: false, arrayFormat: 'indices' }), + // 'a[0]=,&a[1]=&a[2]=c,d%', + // ); + // st.equal( + // stringify({ a: [',', '', 'c,d%'] }, { encode: false, arrayFormat: 'brackets' }), + // 'a[]=,&a[]=&a[]=c,d%', + // ); + // st.equal( + // stringify({ a: [',', '', 'c,d%'] }, { encode: false, arrayFormat: 'comma' }), + // 'a=,,,c,d%', + // ); + // st.equal( + // stringify({ a: [',', '', 'c,d%'] }, { encode: false, arrayFormat: 'repeat' }), + // 'a=,&a=&a=c,d%', + // ); + expect(stringify({ a: [',', '', 'c,d%'] }, { encode: false, arrayFormat: 'indices' })).toBe( + 'a[0]=,&a[1]=&a[2]=c,d%', + ); + expect(stringify({ a: [',', '', 'c,d%'] }, { encode: false, arrayFormat: 'brackets' })).toBe( + 'a[]=,&a[]=&a[]=c,d%', + ); + expect(stringify({ a: [',', '', 'c,d%'] }, { encode: false, arrayFormat: 'comma' })).toBe('a=,,,c,d%'); + expect(stringify({ a: [',', '', 'c,d%'] }, { encode: false, arrayFormat: 'repeat' })).toBe( + 'a=,&a=&a=c,d%', + ); + + // st.equal( + // stringify( + // { a: [',', '', 'c,d%'] }, + // { encode: true, encodeValuesOnly: true, arrayFormat: 'indices' }, + // ), + // 'a[0]=%2C&a[1]=&a[2]=c%2Cd%25', + // ); + // st.equal( + // stringify( + // { a: [',', '', 'c,d%'] }, + // { encode: true, encodeValuesOnly: true, arrayFormat: 'brackets' }, + // ), + // 'a[]=%2C&a[]=&a[]=c%2Cd%25', + // ); + // st.equal( + // stringify( + // { a: [',', '', 'c,d%'] }, + // { encode: true, encodeValuesOnly: true, arrayFormat: 'comma' }, + // ), + // 'a=%2C,,c%2Cd%25', + // ); + // st.equal( + // stringify( + // { a: [',', '', 'c,d%'] }, + // { encode: true, encodeValuesOnly: true, arrayFormat: 'repeat' }, + // ), + // 'a=%2C&a=&a=c%2Cd%25', + // ); + expect( + stringify({ a: [',', '', 'c,d%'] }, { encode: true, encodeValuesOnly: false, arrayFormat: 'indices' }), + ).toBe('a%5B0%5D=%2C&a%5B1%5D=&a%5B2%5D=c%2Cd%25'); + expect( + stringify({ a: [',', '', 'c,d%'] }, { encode: true, encodeValuesOnly: true, arrayFormat: 'brackets' }), + ).toBe('a[]=%2C&a[]=&a[]=c%2Cd%25'); + expect( + stringify({ a: [',', '', 'c,d%'] }, { encode: true, encodeValuesOnly: false, arrayFormat: 'comma' }), + ).toBe('a=%2C%2C%2Cc%2Cd%25'); + expect( + stringify({ a: [',', '', 'c,d%'] }, { encode: true, encodeValuesOnly: false, arrayFormat: 'repeat' }), + ).toBe('a=%2C&a=&a=c%2Cd%25'); + + // st.equal( + // stringify( + // { a: [',', '', 'c,d%'] }, + // { encode: true, encodeValuesOnly: false, arrayFormat: 'indices' }, + // ), + // 'a%5B0%5D=%2C&a%5B1%5D=&a%5B2%5D=c%2Cd%25', + // ); + // st.equal( + // stringify( + // { a: [',', '', 'c,d%'] }, + // { encode: true, encodeValuesOnly: false, arrayFormat: 'brackets' }, + // ), + // 'a%5B%5D=%2C&a%5B%5D=&a%5B%5D=c%2Cd%25', + // ); + // st.equal( + // stringify( + // { a: [',', '', 'c,d%'] }, + // { encode: true, encodeValuesOnly: false, arrayFormat: 'comma' }, + // ), + // 'a=%2C%2C%2Cc%2Cd%25', + // ); + // st.equal( + // stringify( + // { a: [',', '', 'c,d%'] }, + // { encode: true, encodeValuesOnly: false, arrayFormat: 'repeat' }, + // ), + // 'a=%2C&a=&a=c%2Cd%25', + // ); + expect( + stringify({ a: [',', '', 'c,d%'] }, { encode: true, encodeValuesOnly: false, arrayFormat: 'repeat' }), + ).toBe('a=%2C&a=&a=c%2Cd%25'); + expect( + stringify({ a: [',', '', 'c,d%'] }, { encode: true, encodeValuesOnly: false, arrayFormat: 'indices' }), + ).toBe('a%5B0%5D=%2C&a%5B1%5D=&a%5B2%5D=c%2Cd%25'); + expect( + stringify({ a: [',', '', 'c,d%'] }, { encode: true, encodeValuesOnly: true, arrayFormat: 'brackets' }), + ).toBe('a[]=%2C&a[]=&a[]=c%2Cd%25'); + expect( + stringify({ a: [',', '', 'c,d%'] }, { encode: true, encodeValuesOnly: false, arrayFormat: 'comma' }), + ).toBe('a=%2C%2C%2Cc%2Cd%25'); + expect( + stringify({ a: [',', '', 'c,d%'] }, { encode: true, encodeValuesOnly: false, arrayFormat: 'repeat' }), + ).toBe('a=%2C&a=&a=c%2Cd%25'); + }); + + test('stringifies comma and empty non-array values', function () { + // st.equal( + // stringify({ a: ',', b: '', c: 'c,d%' }, { encode: false, arrayFormat: 'indices' }), + // 'a=,&b=&c=c,d%', + // ); + // st.equal( + // stringify({ a: ',', b: '', c: 'c,d%' }, { encode: false, arrayFormat: 'brackets' }), + // 'a=,&b=&c=c,d%', + // ); + // st.equal( + // stringify({ a: ',', b: '', c: 'c,d%' }, { encode: false, arrayFormat: 'comma' }), + // 'a=,&b=&c=c,d%', + // ); + // st.equal( + // stringify({ a: ',', b: '', c: 'c,d%' }, { encode: false, arrayFormat: 'repeat' }), + // 'a=,&b=&c=c,d%', + // ); + expect(stringify({ a: ',', b: '', c: 'c,d%' }, { encode: false, arrayFormat: 'indices' })).toBe( + 'a=,&b=&c=c,d%', + ); + expect(stringify({ a: ',', b: '', c: 'c,d%' }, { encode: false, arrayFormat: 'brackets' })).toBe( + 'a=,&b=&c=c,d%', + ); + + // st.equal( + // stringify( + // { a: ',', b: '', c: 'c,d%' }, + // { encode: true, encodeValuesOnly: true, arrayFormat: 'indices' }, + // ), + // 'a=%2C&b=&c=c%2Cd%25', + // ); + // st.equal( + // stringify( + // { a: ',', b: '', c: 'c,d%' }, + // { encode: true, encodeValuesOnly: true, arrayFormat: 'brackets' }, + // ), + // 'a=%2C&b=&c=c%2Cd%25', + // ); + // st.equal( + // stringify( + // { a: ',', b: '', c: 'c,d%' }, + // { encode: true, encodeValuesOnly: true, arrayFormat: 'comma' }, + // ), + // 'a=%2C&b=&c=c%2Cd%25', + // ); + // st.equal( + // stringify( + // { a: ',', b: '', c: 'c,d%' }, + // { encode: true, encodeValuesOnly: true, arrayFormat: 'repeat' }, + // ), + // 'a=%2C&b=&c=c%2Cd%25', + // ); + expect( + stringify( + { a: ',', b: '', c: 'c,d%' }, + { encode: true, encodeValuesOnly: true, arrayFormat: 'indices' }, + ), + ).toBe('a=%2C&b=&c=c%2Cd%25'); + expect( + stringify( + { a: ',', b: '', c: 'c,d%' }, + { encode: true, encodeValuesOnly: true, arrayFormat: 'brackets' }, + ), + ).toBe('a=%2C&b=&c=c%2Cd%25'); + expect( + stringify({ a: ',', b: '', c: 'c,d%' }, { encode: true, encodeValuesOnly: true, arrayFormat: 'comma' }), + ).toBe('a=%2C&b=&c=c%2Cd%25'); + expect( + stringify( + { a: ',', b: '', c: 'c,d%' }, + { encode: true, encodeValuesOnly: true, arrayFormat: 'repeat' }, + ), + ).toBe('a=%2C&b=&c=c%2Cd%25'); + + // st.equal( + // stringify( + // { a: ',', b: '', c: 'c,d%' }, + // { encode: true, encodeValuesOnly: false, arrayFormat: 'indices' }, + // ), + // 'a=%2C&b=&c=c%2Cd%25', + // ); + // st.equal( + // stringify( + // { a: ',', b: '', c: 'c,d%' }, + // { encode: true, encodeValuesOnly: false, arrayFormat: 'brackets' }, + // ), + // 'a=%2C&b=&c=c%2Cd%25', + // ); + // st.equal( + // stringify( + // { a: ',', b: '', c: 'c,d%' }, + // { encode: true, encodeValuesOnly: false, arrayFormat: 'comma' }, + // ), + // 'a=%2C&b=&c=c%2Cd%25', + // ); + // st.equal( + // stringify( + // { a: ',', b: '', c: 'c,d%' }, + // { encode: true, encodeValuesOnly: false, arrayFormat: 'repeat' }, + // ), + // 'a=%2C&b=&c=c%2Cd%25', + // ); + expect( + stringify( + { a: ',', b: '', c: 'c,d%' }, + { encode: true, encodeValuesOnly: false, arrayFormat: 'indices' }, + ), + ).toBe('a=%2C&b=&c=c%2Cd%25'); + expect( + stringify( + { a: ',', b: '', c: 'c,d%' }, + { encode: true, encodeValuesOnly: false, arrayFormat: 'brackets' }, + ), + ).toBe('a=%2C&b=&c=c%2Cd%25'); + expect( + stringify( + { a: ',', b: '', c: 'c,d%' }, + { encode: true, encodeValuesOnly: false, arrayFormat: 'comma' }, + ), + ).toBe('a=%2C&b=&c=c%2Cd%25'); + expect( + stringify( + { a: ',', b: '', c: 'c,d%' }, + { encode: true, encodeValuesOnly: false, arrayFormat: 'repeat' }, + ), + ).toBe('a=%2C&b=&c=c%2Cd%25'); + }); + + test('stringifies a nested array value with dots notation', function () { + // st.equal( + // stringify( + // { a: { b: ['c', 'd'] } }, + // { allowDots: true, encodeValuesOnly: true, arrayFormat: 'indices' }, + // ), + // 'a.b[0]=c&a.b[1]=d', + // 'indices: stringifies with dots + indices', + // ); + // st.equal( + // stringify( + // { a: { b: ['c', 'd'] } }, + // { allowDots: true, encodeValuesOnly: true, arrayFormat: 'brackets' }, + // ), + // 'a.b[]=c&a.b[]=d', + // 'brackets: stringifies with dots + brackets', + // ); + // st.equal( + // stringify( + // { a: { b: ['c', 'd'] } }, + // { allowDots: true, encodeValuesOnly: true, arrayFormat: 'comma' }, + // ), + // 'a.b=c,d', + // 'comma: stringifies with dots + comma', + // ); + // st.equal( + // stringify({ a: { b: ['c', 'd'] } }, { allowDots: true, encodeValuesOnly: true }), + // 'a.b[0]=c&a.b[1]=d', + // 'default: stringifies with dots + indices', + // ); + expect( + stringify( + { a: { b: ['c', 'd'] } }, + { allowDots: true, encodeValuesOnly: true, arrayFormat: 'indices' }, + ), + ).toBe('a.b[0]=c&a.b[1]=d'); + expect( + stringify( + { a: { b: ['c', 'd'] } }, + { allowDots: true, encodeValuesOnly: true, arrayFormat: 'brackets' }, + ), + ).toBe('a.b[]=c&a.b[]=d'); + expect( + stringify({ a: { b: ['c', 'd'] } }, { allowDots: true, encodeValuesOnly: true, arrayFormat: 'comma' }), + ).toBe('a.b=c,d'); + expect(stringify({ a: { b: ['c', 'd'] } }, { allowDots: true, encodeValuesOnly: true })).toBe( + 'a.b[0]=c&a.b[1]=d', + ); + }); + + test('stringifies an object inside an array', function () { + // st.equal( + // stringify({ a: [{ b: 'c' }] }, { arrayFormat: 'indices', encodeValuesOnly: true }), + // 'a[0][b]=c', + // 'indices => indices', + // ); + // st.equal( + // stringify({ a: [{ b: 'c' }] }, { arrayFormat: 'repeat', encodeValuesOnly: true }), + // 'a[b]=c', + // 'repeat => repeat', + // ); + // st.equal( + // stringify({ a: [{ b: 'c' }] }, { arrayFormat: 'brackets', encodeValuesOnly: true }), + // 'a[][b]=c', + // 'brackets => brackets', + // ); + // st.equal( + // stringify({ a: [{ b: 'c' }] }, { encodeValuesOnly: true }), + // 'a[0][b]=c', + // 'default => indices', + // ); + expect(stringify({ a: [{ b: 'c' }] }, { arrayFormat: 'indices', encodeValuesOnly: true })).toBe( + 'a[0][b]=c', + ); + expect(stringify({ a: [{ b: 'c' }] }, { arrayFormat: 'repeat', encodeValuesOnly: true })).toBe('a[b]=c'); + expect(stringify({ a: [{ b: 'c' }] }, { arrayFormat: 'brackets', encodeValuesOnly: true })).toBe( + 'a[][b]=c', + ); + expect(stringify({ a: [{ b: 'c' }] }, { encodeValuesOnly: true })).toBe('a[0][b]=c'); + + // st.equal( + // stringify({ a: [{ b: { c: [1] } }] }, { arrayFormat: 'indices', encodeValuesOnly: true }), + // 'a[0][b][c][0]=1', + // 'indices => indices', + // ); + // st.equal( + // stringify({ a: [{ b: { c: [1] } }] }, { arrayFormat: 'repeat', encodeValuesOnly: true }), + // 'a[b][c]=1', + // 'repeat => repeat', + // ); + // st.equal( + // stringify({ a: [{ b: { c: [1] } }] }, { arrayFormat: 'brackets', encodeValuesOnly: true }), + // 'a[][b][c][]=1', + // 'brackets => brackets', + // ); + // st.equal( + // stringify({ a: [{ b: { c: [1] } }] }, { encodeValuesOnly: true }), + // 'a[0][b][c][0]=1', + // 'default => indices', + // ); + expect(stringify({ a: [{ b: { c: [1] } }] }, { arrayFormat: 'indices', encodeValuesOnly: true })).toBe( + 'a[0][b][c][0]=1', + ); + expect(stringify({ a: [{ b: { c: [1] } }] }, { arrayFormat: 'repeat', encodeValuesOnly: true })).toBe( + 'a[b][c]=1', + ); + expect(stringify({ a: [{ b: { c: [1] } }] }, { arrayFormat: 'brackets', encodeValuesOnly: true })).toBe( + 'a[][b][c][]=1', + ); + expect(stringify({ a: [{ b: { c: [1] } }] }, { encodeValuesOnly: true })).toBe('a[0][b][c][0]=1'); + }); + + test('stringifies an array with mixed objects and primitives', function () { + // st.equal( + // stringify({ a: [{ b: 1 }, 2, 3] }, { encodeValuesOnly: true, arrayFormat: 'indices' }), + // 'a[0][b]=1&a[1]=2&a[2]=3', + // 'indices => indices', + // ); + // st.equal( + // stringify({ a: [{ b: 1 }, 2, 3] }, { encodeValuesOnly: true, arrayFormat: 'brackets' }), + // 'a[][b]=1&a[]=2&a[]=3', + // 'brackets => brackets', + // ); + // st.equal( + // stringify({ a: [{ b: 1 }, 2, 3] }, { encodeValuesOnly: true, arrayFormat: 'comma' }), + // '???', + // 'brackets => brackets', + // { skip: 'TODO: figure out what this should do' }, + // ); + // st.equal( + // stringify({ a: [{ b: 1 }, 2, 3] }, { encodeValuesOnly: true }), + // 'a[0][b]=1&a[1]=2&a[2]=3', + // 'default => indices', + // ); + expect(stringify({ a: [{ b: 1 }, 2, 3] }, { encodeValuesOnly: true, arrayFormat: 'indices' })).toBe( + 'a[0][b]=1&a[1]=2&a[2]=3', + ); + expect(stringify({ a: [{ b: 1 }, 2, 3] }, { encodeValuesOnly: true, arrayFormat: 'brackets' })).toBe( + 'a[][b]=1&a[]=2&a[]=3', + ); + // !Skipped: Figure out what this should do + // expect( + // stringify({ a: [{ b: 1 }, 2, 3] }, { encodeValuesOnly: true, arrayFormat: 'comma' }), + // ).toBe('???'); + expect(stringify({ a: [{ b: 1 }, 2, 3] }, { encodeValuesOnly: true })).toBe('a[0][b]=1&a[1]=2&a[2]=3'); + }); + + test('stringifies an object inside an array with dots notation', function () { + // st.equal( + // stringify({ a: [{ b: 'c' }] }, { allowDots: true, encode: false, arrayFormat: 'indices' }), + // 'a[0].b=c', + // 'indices => indices', + // ); + // st.equal( + // stringify( + // { a: [{ b: 'c' }] }, + // { allowDots: true, encode: false, arrayFormat: 'brackets' }, + // ), + // 'a[].b=c', + // 'brackets => brackets', + // ); + // st.equal( + // stringify({ a: [{ b: 'c' }] }, { allowDots: true, encode: false }), + // 'a[0].b=c', + // 'default => indices', + // ); + expect(stringify({ a: [{ b: 'c' }] }, { allowDots: true, encode: false, arrayFormat: 'indices' })).toBe( + 'a[0].b=c', + ); + expect(stringify({ a: [{ b: 'c' }] }, { allowDots: true, encode: false, arrayFormat: 'brackets' })).toBe( + 'a[].b=c', + ); + expect(stringify({ a: [{ b: 'c' }] }, { allowDots: true, encode: false })).toBe('a[0].b=c'); + + // st.equal( + // stringify( + // { a: [{ b: { c: [1] } }] }, + // { allowDots: true, encode: false, arrayFormat: 'indices' }, + // ), + // 'a[0].b.c[0]=1', + // 'indices => indices', + // ); + // st.equal( + // stringify( + // { a: [{ b: { c: [1] } }] }, + // { allowDots: true, encode: false, arrayFormat: 'brackets' }, + // ), + // 'a[].b.c[]=1', + // 'brackets => brackets', + // ); + // st.equal( + // stringify({ a: [{ b: { c: [1] } }] }, { allowDots: true, encode: false }), + // 'a[0].b.c[0]=1', + // 'default => indices', + // ); + expect( + stringify({ a: [{ b: { c: [1] } }] }, { allowDots: true, encode: false, arrayFormat: 'indices' }), + ).toBe('a[0].b.c[0]=1'); + expect( + stringify({ a: [{ b: { c: [1] } }] }, { allowDots: true, encode: false, arrayFormat: 'brackets' }), + ).toBe('a[].b.c[]=1'); + expect(stringify({ a: [{ b: { c: [1] } }] }, { allowDots: true, encode: false })).toBe('a[0].b.c[0]=1'); + }); + + test('does not omit object keys when indices = false', function () { + // st.equal(stringify({ a: [{ b: 'c' }] }, { indices: false }), 'a%5Bb%5D=c'); + expect(stringify({ a: [{ b: 'c' }] }, { indices: false })).toBe('a%5Bb%5D=c'); + }); + + test('uses indices notation for arrays when indices=true', function () { + // st.equal(stringify({ a: ['b', 'c'] }, { indices: true }), 'a%5B0%5D=b&a%5B1%5D=c'); + expect(stringify({ a: ['b', 'c'] }, { indices: true })).toBe('a%5B0%5D=b&a%5B1%5D=c'); + }); + + test('uses indices notation for arrays when no arrayFormat is specified', function () { + // st.equal(stringify({ a: ['b', 'c'] }), 'a%5B0%5D=b&a%5B1%5D=c'); + expect(stringify({ a: ['b', 'c'] })).toBe('a%5B0%5D=b&a%5B1%5D=c'); + }); + + test('uses indices notation for arrays when arrayFormat=indices', function () { + // st.equal(stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' }), 'a%5B0%5D=b&a%5B1%5D=c'); + expect(stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' })).toBe('a%5B0%5D=b&a%5B1%5D=c'); + }); + + test('uses repeat notation for arrays when arrayFormat=repeat', function () { + // st.equal(stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' }), 'a=b&a=c'); + expect(stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' })).toBe('a=b&a=c'); + }); + + test('uses brackets notation for arrays when arrayFormat=brackets', function () { + // st.equal(stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' }), 'a%5B%5D=b&a%5B%5D=c'); + expect(stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' })).toBe('a%5B%5D=b&a%5B%5D=c'); + }); + + test('stringifies a complicated object', function () { + // st.equal(stringify({ a: { b: 'c', d: 'e' } }), 'a%5Bb%5D=c&a%5Bd%5D=e'); + expect(stringify({ a: { b: 'c', d: 'e' } })).toBe('a%5Bb%5D=c&a%5Bd%5D=e'); + }); + + test('stringifies an empty value', function () { + // st.equal(stringify({ a: '' }), 'a='); + // st.equal(stringify({ a: null }, { strictNullHandling: true }), 'a'); + expect(stringify({ a: '' })).toBe('a='); + expect(stringify({ a: null }, { strictNullHandling: true })).toBe('a'); + + // st.equal(stringify({ a: '', b: '' }), 'a=&b='); + // st.equal(stringify({ a: null, b: '' }, { strictNullHandling: true }), 'a&b='); + expect(stringify({ a: '', b: '' })).toBe('a=&b='); + expect(stringify({ a: null, b: '' }, { strictNullHandling: true })).toBe('a&b='); + + // st.equal(stringify({ a: { b: '' } }), 'a%5Bb%5D='); + // st.equal(stringify({ a: { b: null } }, { strictNullHandling: true }), 'a%5Bb%5D'); + // st.equal(stringify({ a: { b: null } }, { strictNullHandling: false }), 'a%5Bb%5D='); + expect(stringify({ a: { b: '' } })).toBe('a%5Bb%5D='); + expect(stringify({ a: { b: null } }, { strictNullHandling: true })).toBe('a%5Bb%5D'); + expect(stringify({ a: { b: null } }, { strictNullHandling: false })).toBe('a%5Bb%5D='); + }); + + test('stringifies an empty array in different arrayFormat', function () { + // st.equal(stringify({ a: [], b: [null], c: 'c' }, { encode: false }), 'b[0]=&c=c'); + expect(stringify({ a: [], b: [null], c: 'c' }, { encode: false })).toBe('b[0]=&c=c'); + // arrayFormat default + // st.equal( + // stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'indices' }), + // 'b[0]=&c=c', + // ); + // st.equal( + // stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'brackets' }), + // 'b[]=&c=c', + // ); + // st.equal( + // stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'repeat' }), + // 'b=&c=c', + // ); + // st.equal( + // stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'comma' }), + // 'b=&c=c', + // ); + // st.equal( + // stringify( + // { a: [], b: [null], c: 'c' }, + // { encode: false, arrayFormat: 'comma', commaRoundTrip: true }, + // ), + // 'b[]=&c=c', + // ); + expect(stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'indices' })).toBe( + 'b[0]=&c=c', + ); + expect(stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'brackets' })).toBe( + 'b[]=&c=c', + ); + expect(stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'repeat' })).toBe('b=&c=c'); + expect(stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'comma' })).toBe('b=&c=c'); + expect( + stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'comma', commaRoundTrip: true }), + ).toBe('b[]=&c=c'); + + // with strictNullHandling + // st.equal( + // stringify( + // { a: [], b: [null], c: 'c' }, + // { encode: false, arrayFormat: 'indices', strictNullHandling: true }, + // ), + // 'b[0]&c=c', + // ); + // st.equal( + // stringify( + // { a: [], b: [null], c: 'c' }, + // { encode: false, arrayFormat: 'brackets', strictNullHandling: true }, + // ), + // 'b[]&c=c', + // ); + // st.equal( + // stringify( + // { a: [], b: [null], c: 'c' }, + // { encode: false, arrayFormat: 'repeat', strictNullHandling: true }, + // ), + // 'b&c=c', + // ); + // st.equal( + // stringify( + // { a: [], b: [null], c: 'c' }, + // { encode: false, arrayFormat: 'comma', strictNullHandling: true }, + // ), + // 'b&c=c', + // ); + // st.equal( + // stringify( + // { a: [], b: [null], c: 'c' }, + // { encode: false, arrayFormat: 'comma', strictNullHandling: true, commaRoundTrip: true }, + // ), + // 'b[]&c=c', + // ); + + expect( + stringify( + { a: [], b: [null], c: 'c' }, + { encode: false, arrayFormat: 'indices', strictNullHandling: true }, + ), + ).toBe('b[0]&c=c'); + expect( + stringify( + { a: [], b: [null], c: 'c' }, + { encode: false, arrayFormat: 'brackets', strictNullHandling: true }, + ), + ).toBe('b[]&c=c'); + expect( + stringify( + { a: [], b: [null], c: 'c' }, + { encode: false, arrayFormat: 'repeat', strictNullHandling: true }, + ), + ).toBe('b&c=c'); + expect( + stringify( + { a: [], b: [null], c: 'c' }, + { encode: false, arrayFormat: 'comma', strictNullHandling: true }, + ), + ).toBe('b&c=c'); + expect( + stringify( + { a: [], b: [null], c: 'c' }, + { encode: false, arrayFormat: 'comma', strictNullHandling: true, commaRoundTrip: true }, + ), + ).toBe('b[]&c=c'); + + // with skipNulls + // st.equal( + // stringify( + // { a: [], b: [null], c: 'c' }, + // { encode: false, arrayFormat: 'indices', skipNulls: true }, + // ), + // 'c=c', + // ); + // st.equal( + // stringify( + // { a: [], b: [null], c: 'c' }, + // { encode: false, arrayFormat: 'brackets', skipNulls: true }, + // ), + // 'c=c', + // ); + // st.equal( + // stringify( + // { a: [], b: [null], c: 'c' }, + // { encode: false, arrayFormat: 'repeat', skipNulls: true }, + // ), + // 'c=c', + // ); + // st.equal( + // stringify( + // { a: [], b: [null], c: 'c' }, + // { encode: false, arrayFormat: 'comma', skipNulls: true }, + // ), + // 'c=c', + // ); + expect( + stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'indices', skipNulls: true }), + ).toBe('c=c'); + expect( + stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'brackets', skipNulls: true }), + ).toBe('c=c'); + expect( + stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'repeat', skipNulls: true }), + ).toBe('c=c'); + expect( + stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'comma', skipNulls: true }), + ).toBe('c=c'); + }); + + test('stringifies a null object', function () { + var obj = Object.create(null); + obj.a = 'b'; + // st.equal(stringify(obj), 'a=b'); + expect(stringify(obj)).toBe('a=b'); + }); + + test('returns an empty string for invalid input', function () { + // st.equal(stringify(undefined), ''); + // st.equal(stringify(false), ''); + // st.equal(stringify(null), ''); + // st.equal(stringify(''), ''); + expect(stringify(undefined)).toBe(''); + expect(stringify(false)).toBe(''); + expect(stringify(null)).toBe(''); + expect(stringify('')).toBe(''); + }); + + test('stringifies an object with a null object as a child', function () { + var obj = { a: Object.create(null) }; + + obj.a.b = 'c'; + // st.equal(stringify(obj), 'a%5Bb%5D=c'); + expect(stringify(obj)).toBe('a%5Bb%5D=c'); + }); + + test('drops keys with a value of undefined', function () { + // st.equal(stringify({ a: undefined }), ''); + expect(stringify({ a: undefined })).toBe(''); + + // st.equal( + // stringify({ a: { b: undefined, c: null } }, { strictNullHandling: true }), + // 'a%5Bc%5D', + // ); + // st.equal( + // stringify({ a: { b: undefined, c: null } }, { strictNullHandling: false }), + // 'a%5Bc%5D=', + // ); + // st.equal(stringify({ a: { b: undefined, c: '' } }), 'a%5Bc%5D='); + expect(stringify({ a: { b: undefined, c: null } }, { strictNullHandling: true })).toBe('a%5Bc%5D'); + expect(stringify({ a: { b: undefined, c: null } }, { strictNullHandling: false })).toBe('a%5Bc%5D='); + expect(stringify({ a: { b: undefined, c: '' } })).toBe('a%5Bc%5D='); + }); + + test('url encodes values', function () { + // st.equal(stringify({ a: 'b c' }), 'a=b%20c'); + expect(stringify({ a: 'b c' })).toBe('a=b%20c'); + }); + + test('stringifies a date', function () { + var now = new Date(); + var str = 'a=' + encodeURIComponent(now.toISOString()); + // st.equal(stringify({ a: now }), str); + expect(stringify({ a: now })).toBe(str); + }); + + test('stringifies the weird object from qs', function () { + // st.equal( + // stringify({ 'my weird field': '~q1!2"\'w$5&7/z8)?' }), + // 'my%20weird%20field=~q1%212%22%27w%245%267%2Fz8%29%3F', + // ); + expect(stringify({ 'my weird field': '~q1!2"\'w$5&7/z8)?' })).toBe( + 'my%20weird%20field=~q1%212%22%27w%245%267%2Fz8%29%3F', + ); + }); + + // TODO: Investigate how to to intercept in vitest + // TODO(rob) + test('skips properties that are part of the object prototype', function () { + // st.intercept(Object.prototype, 'crash', { value: 'test' }); + // @ts-expect-error + Object.prototype.crash = 'test'; + + // st.equal(stringify({ a: 'b' }), 'a=b'); + // st.equal(stringify({ a: { b: 'c' } }), 'a%5Bb%5D=c'); + expect(stringify({ a: 'b' })).toBe('a=b'); + expect(stringify({ a: { b: 'c' } })).toBe('a%5Bb%5D=c'); + }); + + test('stringifies boolean values', function () { + // st.equal(stringify({ a: true }), 'a=true'); + // st.equal(stringify({ a: { b: true } }), 'a%5Bb%5D=true'); + // st.equal(stringify({ b: false }), 'b=false'); + // st.equal(stringify({ b: { c: false } }), 'b%5Bc%5D=false'); + expect(stringify({ a: true })).toBe('a=true'); + expect(stringify({ a: { b: true } })).toBe('a%5Bb%5D=true'); + expect(stringify({ b: false })).toBe('b=false'); + expect(stringify({ b: { c: false } })).toBe('b%5Bc%5D=false'); + }); + + test('stringifies buffer values', function () { + // st.equal(stringify({ a: Buffer.from('test') }), 'a=test'); + // st.equal(stringify({ a: { b: Buffer.from('test') } }), 'a%5Bb%5D=test'); + }); + + test('stringifies an object using an alternative delimiter', function () { + // st.equal(stringify({ a: 'b', c: 'd' }, { delimiter: ';' }), 'a=b;c=d'); + expect(stringify({ a: 'b', c: 'd' }, { delimiter: ';' })).toBe('a=b;c=d'); + }); + + // We dont target environments which dont even have Buffer + // test('does not blow up when Buffer global is missing', function () { + // var restore = mockProperty(global, 'Buffer', { delete: true }); + + // var result = stringify({ a: 'b', c: 'd' }); + + // restore(); + + // st.equal(result, 'a=b&c=d'); + // st.end(); + // }); + + test('does not crash when parsing circular references', function () { + var a: any = {}; + a.b = a; + + // st['throws']( + // function () { + // stringify({ 'foo[bar]': 'baz', 'foo[baz]': a }); + // }, + // /RangeError: Cyclic object value/, + // 'cyclic values throw', + // ); + expect(() => { + stringify({ 'foo[bar]': 'baz', 'foo[baz]': a }); + }).toThrow('Cyclic object value'); + + var circular: any = { + a: 'value', + }; + circular.a = circular; + // st['throws']( + // function () { + // stringify(circular); + // }, + // /RangeError: Cyclic object value/, + // 'cyclic values throw', + // ); + expect(() => { + stringify(circular); + }).toThrow('Cyclic object value'); + + var arr = ['a']; + // st.doesNotThrow(function () { + // stringify({ x: arr, y: arr }); + // }, 'non-cyclic values do not throw'); + expect(() => { + stringify({ x: arr, y: arr }); + }).not.toThrow(); + }); + + test('non-circular duplicated references can still work', function () { + var hourOfDay = { + function: 'hour_of_day', + }; + + var p1 = { + function: 'gte', + arguments: [hourOfDay, 0], + }; + var p2 = { + function: 'lte', + arguments: [hourOfDay, 23], + }; + + // st.equal( + // stringify( + // { filters: { $and: [p1, p2] } }, + // { encodeValuesOnly: true, arrayFormat: 'indices' }, + // ), + // 'filters[$and][0][function]=gte&filters[$and][0][arguments][0][function]=hour_of_day&filters[$and][0][arguments][1]=0&filters[$and][1][function]=lte&filters[$and][1][arguments][0][function]=hour_of_day&filters[$and][1][arguments][1]=23', + // ); + // st.equal( + // stringify( + // { filters: { $and: [p1, p2] } }, + // { encodeValuesOnly: true, arrayFormat: 'brackets' }, + // ), + // 'filters[$and][][function]=gte&filters[$and][][arguments][][function]=hour_of_day&filters[$and][][arguments][]=0&filters[$and][][function]=lte&filters[$and][][arguments][][function]=hour_of_day&filters[$and][][arguments][]=23', + // ); + // st.equal( + // stringify( + // { filters: { $and: [p1, p2] } }, + // { encodeValuesOnly: true, arrayFormat: 'repeat' }, + // ), + // 'filters[$and][function]=gte&filters[$and][arguments][function]=hour_of_day&filters[$and][arguments]=0&filters[$and][function]=lte&filters[$and][arguments][function]=hour_of_day&filters[$and][arguments]=23', + // ); + expect( + stringify({ filters: { $and: [p1, p2] } }, { encodeValuesOnly: true, arrayFormat: 'indices' }), + ).toBe( + 'filters[$and][0][function]=gte&filters[$and][0][arguments][0][function]=hour_of_day&filters[$and][0][arguments][1]=0&filters[$and][1][function]=lte&filters[$and][1][arguments][0][function]=hour_of_day&filters[$and][1][arguments][1]=23', + ); + expect( + stringify({ filters: { $and: [p1, p2] } }, { encodeValuesOnly: true, arrayFormat: 'brackets' }), + ).toBe( + 'filters[$and][][function]=gte&filters[$and][][arguments][][function]=hour_of_day&filters[$and][][arguments][]=0&filters[$and][][function]=lte&filters[$and][][arguments][][function]=hour_of_day&filters[$and][][arguments][]=23', + ); + expect( + stringify({ filters: { $and: [p1, p2] } }, { encodeValuesOnly: true, arrayFormat: 'repeat' }), + ).toBe( + 'filters[$and][function]=gte&filters[$and][arguments][function]=hour_of_day&filters[$and][arguments]=0&filters[$and][function]=lte&filters[$and][arguments][function]=hour_of_day&filters[$and][arguments]=23', + ); + }); + + test('selects properties when filter=array', function () { + // st.equal(stringify({ a: 'b' }, { filter: ['a'] }), 'a=b'); + // st.equal(stringify({ a: 1 }, { filter: [] }), ''); + expect(stringify({ a: 'b' }, { filter: ['a'] })).toBe('a=b'); + expect(stringify({ a: 1 }, { filter: [] })).toBe(''); + + // st.equal( + // stringify( + // { a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' }, + // { filter: ['a', 'b', 0, 2], arrayFormat: 'indices' }, + // ), + // 'a%5Bb%5D%5B0%5D=1&a%5Bb%5D%5B2%5D=3', + // 'indices => indices', + // ); + // st.equal( + // stringify( + // { a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' }, + // { filter: ['a', 'b', 0, 2], arrayFormat: 'brackets' }, + // ), + // 'a%5Bb%5D%5B%5D=1&a%5Bb%5D%5B%5D=3', + // 'brackets => brackets', + // ); + // st.equal( + // stringify({ a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' }, { filter: ['a', 'b', 0, 2] }), + // 'a%5Bb%5D%5B0%5D=1&a%5Bb%5D%5B2%5D=3', + // 'default => indices', + // ); + expect(stringify({ a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' }, { filter: ['a', 'b', 0, 2] })).toBe( + 'a%5Bb%5D%5B0%5D=1&a%5Bb%5D%5B2%5D=3', + ); + expect( + stringify( + { a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' }, + { filter: ['a', 'b', 0, 2], arrayFormat: 'indices' }, + ), + ).toBe('a%5Bb%5D%5B0%5D=1&a%5Bb%5D%5B2%5D=3'); + expect( + stringify( + { a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' }, + { filter: ['a', 'b', 0, 2], arrayFormat: 'brackets' }, + ), + ).toBe('a%5Bb%5D%5B%5D=1&a%5Bb%5D%5B%5D=3'); + }); + + test('supports custom representations when filter=function', function () { + var calls = 0; + var obj = { a: 'b', c: 'd', e: { f: new Date(1257894000000) } }; + var filterFunc: StringifyOptions['filter'] = function (prefix, value) { + calls += 1; + if (calls === 1) { + // st.equal(prefix, '', 'prefix is empty'); + // st.equal(value, obj); + expect(prefix).toBe(''); + expect(value).toBe(obj); + } else if (prefix === 'c') { + return void 0; + } else if (value instanceof Date) { + // st.equal(prefix, 'e[f]'); + expect(prefix).toBe('e[f]'); + return value.getTime(); + } + return value; + }; + + // st.equal(stringify(obj, { filter: filterFunc }), 'a=b&e%5Bf%5D=1257894000000'); + // st.equal(calls, 5); + expect(stringify(obj, { filter: filterFunc })).toBe('a=b&e%5Bf%5D=1257894000000'); + expect(calls).toBe(5); + }); + + test('can disable uri encoding', function () { + // st.equal(stringify({ a: 'b' }, { encode: false }), 'a=b'); + // st.equal(stringify({ a: { b: 'c' } }, { encode: false }), 'a[b]=c'); + // st.equal( + // stringify({ a: 'b', c: null }, { strictNullHandling: true, encode: false }), + // 'a=b&c', + // ); + expect(stringify({ a: 'b' }, { encode: false })).toBe('a=b'); + expect(stringify({ a: { b: 'c' } }, { encode: false })).toBe('a[b]=c'); + expect(stringify({ a: 'b', c: null }, { strictNullHandling: true, encode: false })).toBe('a=b&c'); + }); + + test('can sort the keys', function () { + // @ts-expect-error + var sort: NonNullable = function (a: string, b: string) { + return a.localeCompare(b); + }; + // st.equal(stringify({ a: 'c', z: 'y', b: 'f' }, { sort: sort }), 'a=c&b=f&z=y'); + // st.equal( + // stringify({ a: 'c', z: { j: 'a', i: 'b' }, b: 'f' }, { sort: sort }), + // 'a=c&b=f&z%5Bi%5D=b&z%5Bj%5D=a', + // ); + expect(stringify({ a: 'c', z: 'y', b: 'f' }, { sort: sort })).toBe('a=c&b=f&z=y'); + expect(stringify({ a: 'c', z: { j: 'a', i: 'b' }, b: 'f' }, { sort: sort })).toBe( + 'a=c&b=f&z%5Bi%5D=b&z%5Bj%5D=a', + ); + }); + + test('can sort the keys at depth 3 or more too', function () { + // @ts-expect-error + var sort: NonNullable = function (a: string, b: string) { + return a.localeCompare(b); + }; + // st.equal( + // stringify( + // { a: 'a', z: { zj: { zjb: 'zjb', zja: 'zja' }, zi: { zib: 'zib', zia: 'zia' } }, b: 'b' }, + // { sort: sort, encode: false }, + // ), + // 'a=a&b=b&z[zi][zia]=zia&z[zi][zib]=zib&z[zj][zja]=zja&z[zj][zjb]=zjb', + // ); + // st.equal( + // stringify( + // { a: 'a', z: { zj: { zjb: 'zjb', zja: 'zja' }, zi: { zib: 'zib', zia: 'zia' } }, b: 'b' }, + // { sort: null, encode: false }, + // ), + // 'a=a&z[zj][zjb]=zjb&z[zj][zja]=zja&z[zi][zib]=zib&z[zi][zia]=zia&b=b', + // ); + expect( + stringify( + { a: 'a', z: { zj: { zjb: 'zjb', zja: 'zja' }, zi: { zib: 'zib', zia: 'zia' } }, b: 'b' }, + { sort: sort, encode: false }, + ), + ).toBe('a=a&b=b&z[zi][zia]=zia&z[zi][zib]=zib&z[zj][zja]=zja&z[zj][zjb]=zjb'); + expect( + stringify( + { a: 'a', z: { zj: { zjb: 'zjb', zja: 'zja' }, zi: { zib: 'zib', zia: 'zia' } }, b: 'b' }, + { sort: null, encode: false }, + ), + ).toBe('a=a&z[zj][zjb]=zjb&z[zj][zja]=zja&z[zi][zib]=zib&z[zi][zia]=zia&b=b'); + }); + + test('can stringify with custom encoding', function () { + // st.equal( + // stringify( + // { 県: '大阪府', '': '' }, + // { + // encoder: function (str) { + // if (str.length === 0) { + // return ''; + // } + // var buf = iconv.encode(str, 'shiftjis'); + // var result = []; + // for (var i = 0; i < buf.length; ++i) { + // result.push(buf.readUInt8(i).toString(16)); + // } + // return '%' + result.join('%'); + // }, + // }, + // ), + // '%8c%a7=%91%e5%8d%e3%95%7b&=', + // ); + expect( + stringify( + { 県: '大阪府', '': '' }, + { + encoder: function (str) { + if (str.length === 0) { + return ''; + } + var buf = iconv.encode(str, 'shiftjis'); + var result = []; + for (var i = 0; i < buf.length; ++i) { + result.push(buf.readUInt8(i).toString(16)); + } + return '%' + result.join('%'); + }, + }, + ), + ).toBe('%8c%a7=%91%e5%8d%e3%95%7b&='); + }); + + test('receives the default encoder as a second argument', function () { + // stringify( + // { a: 1, b: new Date(), c: true, d: [1] }, + // { + // encoder: function (str) { + // st.match(typeof str, /^(?:string|number|boolean)$/); + // return ''; + // }, + // }, + // ); + + stringify( + { a: 1, b: new Date(), c: true, d: [1] }, + { + encoder: function (str) { + // st.match(typeof str, /^(?:string|number|boolean)$/); + assert.match(typeof str, /^(?:string|number|boolean)$/); + return ''; + }, + }, + ); + }); + + test('receives the default encoder as a second argument', function () { + // stringify( + // { a: 1 }, + // { + // encoder: function (str, defaultEncoder) { + // st.equal(defaultEncoder, utils.encode); + // }, + // }, + // ); + + stringify( + { a: 1 }, + { + // @ts-ignore + encoder: function (_str, defaultEncoder) { + expect(defaultEncoder).toBe(encode); + }, + }, + ); + }); + + test('throws error with wrong encoder', function () { + // st['throws'](function () { + // stringify({}, { encoder: 'string' }); + // }, new TypeError('Encoder has to be a function.')); + // st.end(); + expect(() => { + // @ts-expect-error + stringify({}, { encoder: 'string' }); + }).toThrow(TypeError); + }); + + (typeof Buffer === 'undefined' ? test.skip : test)( + 'can use custom encoder for a buffer object', + function () { + // st.equal( + // stringify( + // { a: Buffer.from([1]) }, + // { + // encoder: function (buffer) { + // if (typeof buffer === 'string') { + // return buffer; + // } + // return String.fromCharCode(buffer.readUInt8(0) + 97); + // }, + // }, + // ), + // 'a=b', + // ); + expect( + stringify( + { a: Buffer.from([1]) }, + { + encoder: function (buffer) { + if (typeof buffer === 'string') { + return buffer; + } + return String.fromCharCode(buffer.readUInt8(0) + 97); + }, + }, + ), + ).toBe('a=b'); + + // st.equal( + // stringify( + // { a: Buffer.from('a b') }, + // { + // encoder: function (buffer) { + // return buffer; + // }, + // }, + // ), + // 'a=a b', + // ); + expect( + stringify( + { a: Buffer.from('a b') }, + { + encoder: function (buffer) { + return buffer; + }, + }, + ), + ).toBe('a=a b'); + }, + ); + + test('serializeDate option', function () { + var date = new Date(); + // st.equal( + // stringify({ a: date }), + // 'a=' + date.toISOString().replace(/:/g, '%3A'), + // 'default is toISOString', + // ); + expect(stringify({ a: date })).toBe('a=' + date.toISOString().replace(/:/g, '%3A')); + + var mutatedDate = new Date(); + mutatedDate.toISOString = function () { + throw new SyntaxError(); + }; + // st['throws'](function () { + // mutatedDate.toISOString(); + // }, SyntaxError); + expect(() => { + mutatedDate.toISOString(); + }).toThrow(SyntaxError); + // st.equal( + // stringify({ a: mutatedDate }), + // 'a=' + Date.prototype.toISOString.call(mutatedDate).replace(/:/g, '%3A'), + // 'toISOString works even when method is not locally present', + // ); + expect(stringify({ a: mutatedDate })).toBe( + 'a=' + Date.prototype.toISOString.call(mutatedDate).replace(/:/g, '%3A'), + ); + + var specificDate = new Date(6); + // st.equal( + // stringify( + // { a: specificDate }, + // { + // serializeDate: function (d) { + // return d.getTime() * 7; + // }, + // }, + // ), + // 'a=42', + // 'custom serializeDate function called', + // ); + expect( + stringify( + { a: specificDate }, + { + // @ts-ignore + serializeDate: function (d) { + return d.getTime() * 7; + }, + }, + ), + ).toBe('a=42'); + + // st.equal( + // stringify( + // { a: [date] }, + // { + // serializeDate: function (d) { + // return d.getTime(); + // }, + // arrayFormat: 'comma', + // }, + // ), + // 'a=' + date.getTime(), + // 'works with arrayFormat comma', + // ); + // st.equal( + // stringify( + // { a: [date] }, + // { + // serializeDate: function (d) { + // return d.getTime(); + // }, + // arrayFormat: 'comma', + // commaRoundTrip: true, + // }, + // ), + // 'a%5B%5D=' + date.getTime(), + // 'works with arrayFormat comma', + // ); + expect( + stringify( + { a: [date] }, + { + // @ts-expect-error + serializeDate: function (d) { + return d.getTime(); + }, + arrayFormat: 'comma', + }, + ), + ).toBe('a=' + date.getTime()); + expect( + stringify( + { a: [date] }, + { + // @ts-expect-error + serializeDate: function (d) { + return d.getTime(); + }, + arrayFormat: 'comma', + commaRoundTrip: true, + }, + ), + ).toBe('a%5B%5D=' + date.getTime()); + }); + + test('RFC 1738 serialization', function () { + // st.equal(stringify({ a: 'b c' }, { format: formats.RFC1738 }), 'a=b+c'); + // st.equal(stringify({ 'a b': 'c d' }, { format: formats.RFC1738 }), 'a+b=c+d'); + // st.equal( + // stringify({ 'a b': Buffer.from('a b') }, { format: formats.RFC1738 }), + // 'a+b=a+b', + // ); + expect(stringify({ a: 'b c' }, { format: 'RFC1738' })).toBe('a=b+c'); + expect(stringify({ 'a b': 'c d' }, { format: 'RFC1738' })).toBe('a+b=c+d'); + expect(stringify({ 'a b': Buffer.from('a b') }, { format: 'RFC1738' })).toBe('a+b=a+b'); + + // st.equal(stringify({ 'foo(ref)': 'bar' }, { format: formats.RFC1738 }), 'foo(ref)=bar'); + expect(stringify({ 'foo(ref)': 'bar' }, { format: 'RFC1738' })).toBe('foo(ref)=bar'); + }); + + test('RFC 3986 spaces serialization', function () { + // st.equal(stringify({ a: 'b c' }, { format: formats.RFC3986 }), 'a=b%20c'); + // st.equal(stringify({ 'a b': 'c d' }, { format: formats.RFC3986 }), 'a%20b=c%20d'); + // st.equal( + // stringify({ 'a b': Buffer.from('a b') }, { format: formats.RFC3986 }), + // 'a%20b=a%20b', + // ); + expect(stringify({ a: 'b c' }, { format: 'RFC3986' })).toBe('a=b%20c'); + expect(stringify({ 'a b': 'c d' }, { format: 'RFC3986' })).toBe('a%20b=c%20d'); + expect(stringify({ 'a b': Buffer.from('a b') }, { format: 'RFC3986' })).toBe('a%20b=a%20b'); + }); + + test('Backward compatibility to RFC 3986', function () { + // st.equal(stringify({ a: 'b c' }), 'a=b%20c'); + // st.equal(stringify({ 'a b': Buffer.from('a b') }), 'a%20b=a%20b'); + expect(stringify({ a: 'b c' })).toBe('a=b%20c'); + expect(stringify({ 'a b': Buffer.from('a b') })).toBe('a%20b=a%20b'); + }); + + test('Edge cases and unknown formats', function () { + ['UFO1234', false, 1234, null, {}, []].forEach(function (format) { + // st['throws'](function () { + // stringify({ a: 'b c' }, { format: format }); + // }, new TypeError('Unknown format option provided.')); + expect(() => { + // @ts-expect-error + stringify({ a: 'b c' }, { format: format }); + }).toThrow(TypeError); + }); + }); + + test('encodeValuesOnly', function () { + // st.equal( + // stringify( + // { a: 'b', c: ['d', 'e=f'], f: [['g'], ['h']] }, + // { encodeValuesOnly: true, arrayFormat: 'indices' }, + // ), + // 'a=b&c[0]=d&c[1]=e%3Df&f[0][0]=g&f[1][0]=h', + // 'encodeValuesOnly + indices', + // ); + // st.equal( + // stringify( + // { a: 'b', c: ['d', 'e=f'], f: [['g'], ['h']] }, + // { encodeValuesOnly: true, arrayFormat: 'brackets' }, + // ), + // 'a=b&c[]=d&c[]=e%3Df&f[][]=g&f[][]=h', + // 'encodeValuesOnly + brackets', + // ); + // st.equal( + // stringify( + // { a: 'b', c: ['d', 'e=f'], f: [['g'], ['h']] }, + // { encodeValuesOnly: true, arrayFormat: 'repeat' }, + // ), + // 'a=b&c=d&c=e%3Df&f=g&f=h', + // 'encodeValuesOnly + repeat', + // ); + expect( + stringify( + { a: 'b', c: ['d', 'e=f'], f: [['g'], ['h']] }, + { encodeValuesOnly: true, arrayFormat: 'indices' }, + ), + ).toBe('a=b&c[0]=d&c[1]=e%3Df&f[0][0]=g&f[1][0]=h'); + expect( + stringify( + { a: 'b', c: ['d', 'e=f'], f: [['g'], ['h']] }, + { encodeValuesOnly: true, arrayFormat: 'brackets' }, + ), + ).toBe('a=b&c[]=d&c[]=e%3Df&f[][]=g&f[][]=h'); + expect( + stringify( + { a: 'b', c: ['d', 'e=f'], f: [['g'], ['h']] }, + { encodeValuesOnly: true, arrayFormat: 'repeat' }, + ), + ).toBe('a=b&c=d&c=e%3Df&f=g&f=h'); + + // st.equal( + // stringify({ a: 'b', c: ['d', 'e'], f: [['g'], ['h']] }, { arrayFormat: 'indices' }), + // 'a=b&c%5B0%5D=d&c%5B1%5D=e&f%5B0%5D%5B0%5D=g&f%5B1%5D%5B0%5D=h', + // 'no encodeValuesOnly + indices', + // ); + // st.equal( + // stringify({ a: 'b', c: ['d', 'e'], f: [['g'], ['h']] }, { arrayFormat: 'brackets' }), + // 'a=b&c%5B%5D=d&c%5B%5D=e&f%5B%5D%5B%5D=g&f%5B%5D%5B%5D=h', + // 'no encodeValuesOnly + brackets', + // ); + // st.equal( + // stringify({ a: 'b', c: ['d', 'e'], f: [['g'], ['h']] }, { arrayFormat: 'repeat' }), + // 'a=b&c=d&c=e&f=g&f=h', + // 'no encodeValuesOnly + repeat', + // ); + expect(stringify({ a: 'b', c: ['d', 'e'], f: [['g'], ['h']] }, { arrayFormat: 'indices' })).toBe( + 'a=b&c%5B0%5D=d&c%5B1%5D=e&f%5B0%5D%5B0%5D=g&f%5B1%5D%5B0%5D=h', + ); + expect(stringify({ a: 'b', c: ['d', 'e'], f: [['g'], ['h']] }, { arrayFormat: 'brackets' })).toBe( + 'a=b&c%5B%5D=d&c%5B%5D=e&f%5B%5D%5B%5D=g&f%5B%5D%5B%5D=h', + ); + expect(stringify({ a: 'b', c: ['d', 'e'], f: [['g'], ['h']] }, { arrayFormat: 'repeat' })).toBe( + 'a=b&c=d&c=e&f=g&f=h', + ); + }); + + test('encodeValuesOnly - strictNullHandling', function () { + // st.equal( + // stringify({ a: { b: null } }, { encodeValuesOnly: true, strictNullHandling: true }), + // 'a[b]', + // ); + expect(stringify({ a: { b: null } }, { encodeValuesOnly: true, strictNullHandling: true })).toBe('a[b]'); + }); + + test('throws if an invalid charset is specified', function () { + // st['throws'](function () { + // stringify({ a: 'b' }, { charset: 'foobar' }); + // }, new TypeError('The charset option must be either utf-8, iso-8859-1, or undefined')); + expect(() => { + // @ts-expect-error + stringify({ a: 'b' }, { charset: 'foobar' }); + }).toThrow(TypeError); + }); + + test('respects a charset of iso-8859-1', function () { + // st.equal(stringify({ æ: 'æ' }, { charset: 'iso-8859-1' }), '%E6=%E6'); + expect(stringify({ æ: 'æ' }, { charset: 'iso-8859-1' })).toBe('%E6=%E6'); + }); + + test('encodes unrepresentable chars as numeric entities in iso-8859-1 mode', function () { + // st.equal(stringify({ a: '☺' }, { charset: 'iso-8859-1' }), 'a=%26%239786%3B'); + expect(stringify({ a: '☺' }, { charset: 'iso-8859-1' })).toBe('a=%26%239786%3B'); + }); + + test('respects an explicit charset of utf-8 (the default)', function () { + // st.equal(stringify({ a: 'æ' }, { charset: 'utf-8' }), 'a=%C3%A6'); + expect(stringify({ a: 'æ' }, { charset: 'utf-8' })).toBe('a=%C3%A6'); + }); + + test('`charsetSentinel` option', function () { + // st.equal( + // stringify({ a: 'æ' }, { charsetSentinel: true, charset: 'utf-8' }), + // 'utf8=%E2%9C%93&a=%C3%A6', + // 'adds the right sentinel when instructed to and the charset is utf-8', + // ); + expect(stringify({ a: 'æ' }, { charsetSentinel: true, charset: 'utf-8' })).toBe( + 'utf8=%E2%9C%93&a=%C3%A6', + ); + + // st.equal( + // stringify({ a: 'æ' }, { charsetSentinel: true, charset: 'iso-8859-1' }), + // 'utf8=%26%2310003%3B&a=%E6', + // 'adds the right sentinel when instructed to and the charset is iso-8859-1', + // ); + expect(stringify({ a: 'æ' }, { charsetSentinel: true, charset: 'iso-8859-1' })).toBe( + 'utf8=%26%2310003%3B&a=%E6', + ); + }); + + test('does not mutate the options argument', function () { + var options = {}; + stringify({}, options); + // st.deepEqual(options, {}); + expect(options).toEqual({}); + }); + + test('strictNullHandling works with custom filter', function () { + // @ts-expect-error + var filter = function (_prefix, value) { + return value; + }; + + var options = { strictNullHandling: true, filter: filter }; + // st.equal(stringify({ key: null }, options), 'key'); + expect(stringify({ key: null }, options)).toBe('key'); + }); + + test('strictNullHandling works with null serializeDate', function () { + var serializeDate = function () { + return null; + }; + var options = { strictNullHandling: true, serializeDate: serializeDate }; + var date = new Date(); + // st.equal(stringify({ key: date }, options), 'key'); + // @ts-expect-error + expect(stringify({ key: date }, options)).toBe('key'); + }); + + test('allows for encoding keys and values differently', function () { + // @ts-expect-error + var encoder = function (str, defaultEncoder, charset, type) { + if (type === 'key') { + return defaultEncoder(str, defaultEncoder, charset, type).toLowerCase(); + } + if (type === 'value') { + return defaultEncoder(str, defaultEncoder, charset, type).toUpperCase(); + } + throw 'this should never happen! type: ' + type; + }; + + // st.deepEqual(stringify({ KeY: 'vAlUe' }, { encoder: encoder }), 'key=VALUE'); + expect(stringify({ KeY: 'vAlUe' }, { encoder: encoder })).toBe('key=VALUE'); + }); + + test('objects inside arrays', function () { + var obj = { a: { b: { c: 'd', e: 'f' } } }; + var withArray = { a: { b: [{ c: 'd', e: 'f' }] } }; + + // st.equal( + // stringify(obj, { encode: false }), + // 'a[b][c]=d&a[b][e]=f', + // 'no array, no arrayFormat', + // ); + // st.equal( + // stringify(obj, { encode: false, arrayFormat: 'brackets' }), + // 'a[b][c]=d&a[b][e]=f', + // 'no array, bracket', + // ); + // st.equal( + // stringify(obj, { encode: false, arrayFormat: 'indices' }), + // 'a[b][c]=d&a[b][e]=f', + // 'no array, indices', + // ); + // st.equal( + // stringify(obj, { encode: false, arrayFormat: 'repeat' }), + // 'a[b][c]=d&a[b][e]=f', + // 'no array, repeat', + // ); + // st.equal( + // stringify(obj, { encode: false, arrayFormat: 'comma' }), + // 'a[b][c]=d&a[b][e]=f', + // 'no array, comma', + // ); + expect(stringify(obj, { encode: false })).toBe('a[b][c]=d&a[b][e]=f'); + expect(stringify(obj, { encode: false, arrayFormat: 'brackets' })).toBe('a[b][c]=d&a[b][e]=f'); + expect(stringify(obj, { encode: false, arrayFormat: 'indices' })).toBe('a[b][c]=d&a[b][e]=f'); + expect(stringify(obj, { encode: false, arrayFormat: 'repeat' })).toBe('a[b][c]=d&a[b][e]=f'); + expect(stringify(obj, { encode: false, arrayFormat: 'comma' })).toBe('a[b][c]=d&a[b][e]=f'); + + // st.equal( + // stringify(withArray, { encode: false }), + // 'a[b][0][c]=d&a[b][0][e]=f', + // 'array, no arrayFormat', + // ); + // st.equal( + // stringify(withArray, { encode: false, arrayFormat: 'brackets' }), + // 'a[b][][c]=d&a[b][][e]=f', + // 'array, bracket', + // ); + // st.equal( + // stringify(withArray, { encode: false, arrayFormat: 'indices' }), + // 'a[b][0][c]=d&a[b][0][e]=f', + // 'array, indices', + // ); + // st.equal( + // stringify(withArray, { encode: false, arrayFormat: 'repeat' }), + // 'a[b][c]=d&a[b][e]=f', + // 'array, repeat', + // ); + // st.equal( + // stringify(withArray, { encode: false, arrayFormat: 'comma' }), + // '???', + // 'array, comma', + // { skip: 'TODO: figure out what this should do' }, + // ); + expect(stringify(withArray, { encode: false })).toBe('a[b][0][c]=d&a[b][0][e]=f'); + expect(stringify(withArray, { encode: false, arrayFormat: 'brackets' })).toBe('a[b][][c]=d&a[b][][e]=f'); + expect(stringify(withArray, { encode: false, arrayFormat: 'indices' })).toBe('a[b][0][c]=d&a[b][0][e]=f'); + expect(stringify(withArray, { encode: false, arrayFormat: 'repeat' })).toBe('a[b][c]=d&a[b][e]=f'); + // !TODo: Figure out what this should do + // expect(stringify(withArray, { encode: false, arrayFormat: 'comma' })).toBe( + // 'a[b][c]=d&a[b][e]=f', + // ); + }); + + test('stringifies sparse arrays', function () { + // st.equal( + // stringify({ a: [, '2', , , '1'] }, { encodeValuesOnly: true, arrayFormat: 'indices' }), + // 'a[1]=2&a[4]=1', + // ); + // st.equal( + // stringify({ a: [, '2', , , '1'] }, { encodeValuesOnly: true, arrayFormat: 'brackets' }), + // 'a[]=2&a[]=1', + // ); + // st.equal( + // stringify({ a: [, '2', , , '1'] }, { encodeValuesOnly: true, arrayFormat: 'repeat' }), + // 'a=2&a=1', + // ); + expect(stringify({ a: [, '2', , , '1'] }, { encodeValuesOnly: true, arrayFormat: 'indices' })).toBe( + 'a[1]=2&a[4]=1', + ); + expect(stringify({ a: [, '2', , , '1'] }, { encodeValuesOnly: true, arrayFormat: 'brackets' })).toBe( + 'a[]=2&a[]=1', + ); + expect(stringify({ a: [, '2', , , '1'] }, { encodeValuesOnly: true, arrayFormat: 'repeat' })).toBe( + 'a=2&a=1', + ); + + // st.equal( + // stringify( + // { a: [, { b: [, , { c: '1' }] }] }, + // { encodeValuesOnly: true, arrayFormat: 'indices' }, + // ), + // 'a[1][b][2][c]=1', + // ); + // st.equal( + // stringify( + // { a: [, { b: [, , { c: '1' }] }] }, + // { encodeValuesOnly: true, arrayFormat: 'brackets' }, + // ), + // 'a[][b][][c]=1', + // ); + // st.equal( + // stringify( + // { a: [, { b: [, , { c: '1' }] }] }, + // { encodeValuesOnly: true, arrayFormat: 'repeat' }, + // ), + // 'a[b][c]=1', + // ); + expect( + stringify({ a: [, { b: [, , { c: '1' }] }] }, { encodeValuesOnly: true, arrayFormat: 'indices' }), + ).toBe('a[1][b][2][c]=1'); + expect( + stringify({ a: [, { b: [, , { c: '1' }] }] }, { encodeValuesOnly: true, arrayFormat: 'brackets' }), + ).toBe('a[][b][][c]=1'); + expect( + stringify({ a: [, { b: [, , { c: '1' }] }] }, { encodeValuesOnly: true, arrayFormat: 'repeat' }), + ).toBe('a[b][c]=1'); + + // st.equal( + // stringify( + // { a: [, [, , [, , , { c: '1' }]]] }, + // { encodeValuesOnly: true, arrayFormat: 'indices' }, + // ), + // 'a[1][2][3][c]=1', + // ); + // st.equal( + // stringify( + // { a: [, [, , [, , , { c: '1' }]]] }, + // { encodeValuesOnly: true, arrayFormat: 'brackets' }, + // ), + // 'a[][][][c]=1', + // ); + // st.equal( + // stringify( + // { a: [, [, , [, , , { c: '1' }]]] }, + // { encodeValuesOnly: true, arrayFormat: 'repeat' }, + // ), + // 'a[c]=1', + // ); + expect( + stringify({ a: [, [, , [, , , { c: '1' }]]] }, { encodeValuesOnly: true, arrayFormat: 'indices' }), + ).toBe('a[1][2][3][c]=1'); + expect( + stringify({ a: [, [, , [, , , { c: '1' }]]] }, { encodeValuesOnly: true, arrayFormat: 'brackets' }), + ).toBe('a[][][][c]=1'); + expect( + stringify({ a: [, [, , [, , , { c: '1' }]]] }, { encodeValuesOnly: true, arrayFormat: 'repeat' }), + ).toBe('a[c]=1'); + + // st.equal( + // stringify( + // { a: [, [, , [, , , { c: [, '1'] }]]] }, + // { encodeValuesOnly: true, arrayFormat: 'indices' }, + // ), + // 'a[1][2][3][c][1]=1', + // ); + // st.equal( + // stringify( + // { a: [, [, , [, , , { c: [, '1'] }]]] }, + // { encodeValuesOnly: true, arrayFormat: 'brackets' }, + // ), + // 'a[][][][c][]=1', + // ); + // st.equal( + // stringify( + // { a: [, [, , [, , , { c: [, '1'] }]]] }, + // { encodeValuesOnly: true, arrayFormat: 'repeat' }, + // ), + // 'a[c]=1', + // ); + expect( + stringify({ a: [, [, , [, , , { c: [, '1'] }]]] }, { encodeValuesOnly: true, arrayFormat: 'indices' }), + ).toBe('a[1][2][3][c][1]=1'); + expect( + stringify({ a: [, [, , [, , , { c: [, '1'] }]]] }, { encodeValuesOnly: true, arrayFormat: 'brackets' }), + ).toBe('a[][][][c][]=1'); + expect( + stringify({ a: [, [, , [, , , { c: [, '1'] }]]] }, { encodeValuesOnly: true, arrayFormat: 'repeat' }), + ).toBe('a[c]=1'); + }); + + test('encodes a very long string', function () { + var chars = []; + var expected = []; + for (var i = 0; i < 5e3; i++) { + chars.push(' ' + i); + + expected.push('%20' + i); + } + + var obj = { + foo: chars.join(''), + }; + + // st.equal( + // stringify(obj, { arrayFormat: 'bracket', charset: 'utf-8' }), + // 'foo=' + expected.join(''), + // ); + // @ts-expect-error + expect(stringify(obj, { arrayFormat: 'bracket', charset: 'utf-8' })).toBe('foo=' + expected.join('')); + }); +}); + +describe('stringifies empty keys', function () { + empty_test_cases.forEach(function (testCase) { + test('stringifies an object with empty string key with ' + testCase.input, function () { + // st.deepEqual( + // stringify(testCase.withEmptyKeys, { encode: false, arrayFormat: 'indices' }), + // testCase.stringifyOutput.indices, + // 'test case: ' + testCase.input + ', indices', + // ); + // st.deepEqual( + // stringify(testCase.withEmptyKeys, { encode: false, arrayFormat: 'brackets' }), + // testCase.stringifyOutput.brackets, + // 'test case: ' + testCase.input + ', brackets', + // ); + // st.deepEqual( + // stringify(testCase.withEmptyKeys, { encode: false, arrayFormat: 'repeat' }), + // testCase.stringifyOutput.repeat, + // 'test case: ' + testCase.input + ', repeat', + // ); + expect(stringify(testCase.with_empty_keys, { encode: false, arrayFormat: 'indices' })).toBe( + testCase.stringify_output.indices, + ); + expect(stringify(testCase.with_empty_keys, { encode: false, arrayFormat: 'brackets' })).toBe( + testCase.stringify_output.brackets, + ); + expect(stringify(testCase.with_empty_keys, { encode: false, arrayFormat: 'repeat' })).toBe( + testCase.stringify_output.repeat, + ); + }); + }); + + test('edge case with object/arrays', function () { + // st.deepEqual(stringify({ '': { '': [2, 3] } }, { encode: false }), '[][0]=2&[][1]=3'); + // st.deepEqual( + // stringify({ '': { '': [2, 3], a: 2 } }, { encode: false }), + // '[][0]=2&[][1]=3&[a]=2', + // ); + // st.deepEqual( + // stringify({ '': { '': [2, 3] } }, { encode: false, arrayFormat: 'indices' }), + // '[][0]=2&[][1]=3', + // ); + // st.deepEqual( + // stringify({ '': { '': [2, 3], a: 2 } }, { encode: false, arrayFormat: 'indices' }), + // '[][0]=2&[][1]=3&[a]=2', + // ); + expect(stringify({ '': { '': [2, 3] } }, { encode: false })).toBe('[][0]=2&[][1]=3'); + expect(stringify({ '': { '': [2, 3], a: 2 } }, { encode: false })).toBe('[][0]=2&[][1]=3&[a]=2'); + expect(stringify({ '': { '': [2, 3] } }, { encode: false, arrayFormat: 'indices' })).toBe( + '[][0]=2&[][1]=3', + ); + expect(stringify({ '': { '': [2, 3], a: 2 } }, { encode: false, arrayFormat: 'indices' })).toBe( + '[][0]=2&[][1]=3&[a]=2', + ); + }); +}); diff --git a/tests/qs/utils.test.ts b/tests/qs/utils.test.ts new file mode 100644 index 00000000..07e8e9fc --- /dev/null +++ b/tests/qs/utils.test.ts @@ -0,0 +1,169 @@ +import { combine, merge, is_buffer, assign_single_source } from 'brand.dev/internal/qs/utils'; + +describe('merge()', function () { + // t.deepEqual(merge(null, true), [null, true], 'merges true into null'); + expect(merge(null, true)).toEqual([null, true]); + + // t.deepEqual(merge(null, [42]), [null, 42], 'merges null into an array'); + expect(merge(null, [42])).toEqual([null, 42]); + + // t.deepEqual( + // merge({ a: 'b' }, { a: 'c' }), + // { a: ['b', 'c'] }, + // 'merges two objects with the same key', + // ); + expect(merge({ a: 'b' }, { a: 'c' })).toEqual({ a: ['b', 'c'] }); + + var oneMerged = merge({ foo: 'bar' }, { foo: { first: '123' } }); + // t.deepEqual( + // oneMerged, + // { foo: ['bar', { first: '123' }] }, + // 'merges a standalone and an object into an array', + // ); + expect(oneMerged).toEqual({ foo: ['bar', { first: '123' }] }); + + var twoMerged = merge({ foo: ['bar', { first: '123' }] }, { foo: { second: '456' } }); + // t.deepEqual( + // twoMerged, + // { foo: { 0: 'bar', 1: { first: '123' }, second: '456' } }, + // 'merges a standalone and two objects into an array', + // ); + expect(twoMerged).toEqual({ foo: { 0: 'bar', 1: { first: '123' }, second: '456' } }); + + var sandwiched = merge({ foo: ['bar', { first: '123', second: '456' }] }, { foo: 'baz' }); + // t.deepEqual( + // sandwiched, + // { foo: ['bar', { first: '123', second: '456' }, 'baz'] }, + // 'merges an object sandwiched by two standalones into an array', + // ); + expect(sandwiched).toEqual({ foo: ['bar', { first: '123', second: '456' }, 'baz'] }); + + var nestedArrays = merge({ foo: ['baz'] }, { foo: ['bar', 'xyzzy'] }); + // t.deepEqual(nestedArrays, { foo: ['baz', 'bar', 'xyzzy'] }); + expect(nestedArrays).toEqual({ foo: ['baz', 'bar', 'xyzzy'] }); + + var noOptionsNonObjectSource = merge({ foo: 'baz' }, 'bar'); + // t.deepEqual(noOptionsNonObjectSource, { foo: 'baz', bar: true }); + expect(noOptionsNonObjectSource).toEqual({ foo: 'baz', bar: true }); + + (typeof Object.defineProperty !== 'function' ? test.skip : test)( + 'avoids invoking array setters unnecessarily', + function () { + var setCount = 0; + var getCount = 0; + var observed: any[] = []; + Object.defineProperty(observed, 0, { + get: function () { + getCount += 1; + return { bar: 'baz' }; + }, + set: function () { + setCount += 1; + }, + }); + merge(observed, [null]); + // st.equal(setCount, 0); + // st.equal(getCount, 1); + expect(setCount).toEqual(0); + expect(getCount).toEqual(1); + observed[0] = observed[0]; + // st.equal(setCount, 1); + // st.equal(getCount, 2); + expect(setCount).toEqual(1); + expect(getCount).toEqual(2); + }, + ); +}); + +test('assign()', function () { + var target = { a: 1, b: 2 }; + var source = { b: 3, c: 4 }; + var result = assign_single_source(target, source); + + expect(result).toEqual(target); + expect(target).toEqual({ a: 1, b: 3, c: 4 }); + expect(source).toEqual({ b: 3, c: 4 }); +}); + +describe('combine()', function () { + test('both arrays', function () { + var a = [1]; + var b = [2]; + var combined = combine(a, b); + + // st.deepEqual(a, [1], 'a is not mutated'); + // st.deepEqual(b, [2], 'b is not mutated'); + // st.notEqual(a, combined, 'a !== combined'); + // st.notEqual(b, combined, 'b !== combined'); + // st.deepEqual(combined, [1, 2], 'combined is a + b'); + expect(a).toEqual([1]); + expect(b).toEqual([2]); + expect(combined).toEqual([1, 2]); + expect(a).not.toEqual(combined); + expect(b).not.toEqual(combined); + }); + + test('one array, one non-array', function () { + var aN = 1; + var a = [aN]; + var bN = 2; + var b = [bN]; + + var combinedAnB = combine(aN, b); + // st.deepEqual(b, [bN], 'b is not mutated'); + // st.notEqual(aN, combinedAnB, 'aN + b !== aN'); + // st.notEqual(a, combinedAnB, 'aN + b !== a'); + // st.notEqual(bN, combinedAnB, 'aN + b !== bN'); + // st.notEqual(b, combinedAnB, 'aN + b !== b'); + // st.deepEqual([1, 2], combinedAnB, 'first argument is array-wrapped when not an array'); + expect(b).toEqual([bN]); + expect(combinedAnB).not.toEqual(aN); + expect(combinedAnB).not.toEqual(a); + expect(combinedAnB).not.toEqual(bN); + expect(combinedAnB).not.toEqual(b); + expect(combinedAnB).toEqual([1, 2]); + + var combinedABn = combine(a, bN); + // st.deepEqual(a, [aN], 'a is not mutated'); + // st.notEqual(aN, combinedABn, 'a + bN !== aN'); + // st.notEqual(a, combinedABn, 'a + bN !== a'); + // st.notEqual(bN, combinedABn, 'a + bN !== bN'); + // st.notEqual(b, combinedABn, 'a + bN !== b'); + // st.deepEqual([1, 2], combinedABn, 'second argument is array-wrapped when not an array'); + expect(a).toEqual([aN]); + expect(combinedABn).not.toEqual(aN); + expect(combinedABn).not.toEqual(a); + expect(combinedABn).not.toEqual(bN); + expect(combinedABn).not.toEqual(b); + expect(combinedABn).toEqual([1, 2]); + }); + + test('neither is an array', function () { + var combined = combine(1, 2); + // st.notEqual(1, combined, '1 + 2 !== 1'); + // st.notEqual(2, combined, '1 + 2 !== 2'); + // st.deepEqual([1, 2], combined, 'both arguments are array-wrapped when not an array'); + expect(combined).not.toEqual(1); + expect(combined).not.toEqual(2); + expect(combined).toEqual([1, 2]); + }); +}); + +test('is_buffer()', function () { + for (const x of [null, undefined, true, false, '', 'abc', 42, 0, NaN, {}, [], function () {}, /a/g]) { + // t.equal(is_buffer(x), false, inspect(x) + ' is not a buffer'); + expect(is_buffer(x)).toEqual(false); + } + + var fakeBuffer = { constructor: Buffer }; + // t.equal(is_buffer(fakeBuffer), false, 'fake buffer is not a buffer'); + expect(is_buffer(fakeBuffer)).toEqual(false); + + var saferBuffer = Buffer.from('abc'); + // t.equal(is_buffer(saferBuffer), true, 'SaferBuffer instance is a buffer'); + expect(is_buffer(saferBuffer)).toEqual(true); + + var buffer = Buffer.from('abc'); + // t.equal(is_buffer(buffer), true, 'real Buffer instance is a buffer'); + expect(is_buffer(buffer)).toEqual(true); +}); diff --git a/tests/stringifyQuery.test.ts b/tests/stringifyQuery.test.ts index 77f661a1..704398e3 100644 --- a/tests/stringifyQuery.test.ts +++ b/tests/stringifyQuery.test.ts @@ -18,10 +18,4 @@ describe(stringifyQuery, () => { expect(stringifyQuery(input)).toEqual(expected); }); } - - for (const value of [[], {}, new Date()]) { - it(`${JSON.stringify(value)} -> `, () => { - expect(() => stringifyQuery({ value })).toThrow(`Cannot stringify type ${typeof value}`); - }); - } }); diff --git a/tests/uploads.test.ts b/tests/uploads.test.ts index 1a569976..96fb9598 100644 --- a/tests/uploads.test.ts +++ b/tests/uploads.test.ts @@ -1,7 +1,6 @@ import fs from 'fs'; import type { ResponseLike } from 'brand.dev/internal/to-file'; import { toFile } from 'brand.dev/core/uploads'; -import { File } from 'node:buffer'; class MyClass { name: string = 'foo'; diff --git a/yarn.lock b/yarn.lock index e5e2a93b..06fc1085 100644 --- a/yarn.lock +++ b/yarn.lock @@ -709,11 +709,6 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@pkgr/core@^0.2.4": - version "0.2.4" - resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.2.4.tgz#d897170a2b0ba51f78a099edccd968f7b103387c" - integrity sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw== - "@sinclair/typebox@^0.27.8": version "0.27.8" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" @@ -1220,9 +1215,9 @@ baseline-browser-mapping@^2.9.0: integrity sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg== brace-expansion@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.3.tgz#0493338bdd58e319b1039c67cf7ee439892c01d9" - integrity sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA== + version "2.1.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.1.1.tgz#c68b1c4111c76aae3a6fba55d496cee10c39dad8" + integrity sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA== dependencies: balanced-match "^1.0.0" @@ -1515,14 +1510,6 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -eslint-plugin-prettier@^5.4.1: - version "5.4.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.4.1.tgz#99b55d7dd70047886b2222fdd853665f180b36af" - integrity sha512-9dF+KuU/Ilkq27A8idRP7N2DH8iUR6qXcjF3FR2wETY21PZdBrIjwCau8oboyGj9b7etWmTGEeM8e7oOed6ZWg== - dependencies: - prettier-linter-helpers "^1.0.0" - synckit "^0.11.7" - eslint-plugin-unused-imports@^4.1.4: version "4.1.4" resolved "https://registry.yarnpkg.com/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.1.4.tgz#62ddc7446ccbf9aa7b6f1f0b00a980423cda2738" @@ -1674,11 +1661,6 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-diff@^1.1.2: - version "1.3.0" - resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" - integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== - fast-glob@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" @@ -2841,13 +2823,6 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prettier-linter-helpers@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" - integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== - dependencies: - fast-diff "^1.1.2" - prettier@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.1.1.tgz#6ba9f23165d690b6cbdaa88cb0807278f7019848" @@ -3144,13 +3119,6 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -synckit@^0.11.7: - version "0.11.8" - resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.8.tgz#b2aaae998a4ef47ded60773ad06e7cb821f55457" - integrity sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A== - dependencies: - "@pkgr/core" "^0.2.4" - test-exclude@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" @@ -3224,9 +3192,9 @@ ts-node@^10.5.0: v8-compile-cache-lib "^3.0.0" yn "3.1.1" -"tsc-multi@https://github.com/stainless-api/tsc-multi/releases/download/v1.1.9/tsc-multi.tgz": - version "1.1.9" - resolved "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.9/tsc-multi.tgz#777f6f5d9e26bf0e94e5170990dd3a841d6707cd" +"tsc-multi@https://github.com/stainless-api/tsc-multi/releases/download/v1.1.11/tsc-multi.tgz": + version "1.1.11" + resolved "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.11/tsc-multi.tgz#010247051be13b55abdc98f787c017285149f4f2" dependencies: debug "^4.3.7" fast-glob "^3.3.2"