diff --git a/openapi_v2.yaml b/openapi_v2.yaml index 5b4e205..6e0f57d 100644 --- a/openapi_v2.yaml +++ b/openapi_v2.yaml @@ -8,11 +8,11 @@ info: **Authentication:** All API requests must be authenticated with a Bearer Token. Provide your API key in the `Authorization` header: `Authorization: Bearer YOUR_API_KEY`. - version: "2.3.0" + version: '2.3.0' contact: name: Firecrawl Team email: team@firecrawl.dev - url: "https://firecrawl.dev" + url: 'https://firecrawl.dev' servers: - url: https://api.firecrawl.dev/v2 description: Production Server @@ -42,43 +42,42 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/ScrapeRequestV2" + $ref: '#/components/schemas/ScrapeRequestV2' examples: llm_extraction_example: summary: LLM Extraction value: - url: "https://mendable.ai" + url: 'https://mendable.ai' pageOptions: onlyMainContent: true extractorOptions: - mode: "llm-extraction" - extractionPrompt: "Extract the pricing information and features for each plan." + mode: 'llm-extraction' + extractionPrompt: 'Extract the pricing information and features for each plan.' extractionSchema: - type: "object" + type: 'object' properties: plans: - type: "array" + type: 'array' items: - type: "object" + type: 'object' properties: - name: { type: "string" } - price: { type: "string" } - features: - { type: "array", items: { type: "string" } } + name: { type: 'string' } + price: { type: 'string' } + features: { type: 'array', items: { type: 'string' } } markdown_example: summary: Markdown Content value: - url: "https://firecrawl.dev/blog/how-to-scrape-amazon" + url: 'https://firecrawl.dev/blog/how-to-scrape-amazon' pageOptions: onlyMainContent: true responses: - "200": + '200': description: Successfully scraped the URL. content: application/json: schema: - $ref: "#/components/schemas/ScrapeResponse" - "202": + $ref: '#/components/schemas/ScrapeResponse' + '202': description: Scrape job accepted for asynchronous processing (when a webhook is provided). content: application/json: @@ -88,17 +87,17 @@ paths: jobId: type: string description: The ID of the accepted job. - example: "c6a2c5a0-4a6a-4f9a-9b8a-3e2b1f1c7a6d" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "422": - $ref: "#/components/responses/UnprocessableEntity" - "429": - $ref: "#/components/responses/TooManyRequests" - "500": - $ref: "#/components/responses/InternalServerError" + example: 'c6a2c5a0-4a6a-4f9a-9b8a-3e2b1f1c7a6d' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '422': + $ref: '#/components/responses/UnprocessableEntity' + '429': + $ref: '#/components/responses/TooManyRequests' + '500': + $ref: '#/components/responses/InternalServerError' /crawl: post: @@ -114,17 +113,17 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/CrawlRequestV2" + $ref: '#/components/schemas/CrawlRequestV2' examples: basic_crawl: summary: Basic Crawl value: - url: "https://firecrawl.dev" + url: 'https://firecrawl.dev' crawlerOptions: maxDepth: 2 maxPages: 10 responses: - "200": + '200': description: Crawl job started successfully. content: application/json: @@ -134,17 +133,17 @@ paths: jobId: type: string description: The ID for the initiated crawl job. - example: "e8a3b5c0-4b6a-4f9a-9b8a-3e2b1f1c7a6d" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "422": - $ref: "#/components/responses/UnprocessableEntity" - "429": - $ref: "#/components/responses/TooManyRequests" - "500": - $ref: "#/components/responses/InternalServerError" + example: 'e8a3b5c0-4b6a-4f9a-9b8a-3e2b1f1c7a6d' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '422': + $ref: '#/components/responses/UnprocessableEntity' + '429': + $ref: '#/components/responses/TooManyRequests' + '500': + $ref: '#/components/responses/InternalServerError' /crawl/status/{jobId}: get: @@ -163,22 +162,22 @@ paths: schema: type: string format: uuid - example: "e8a3b5c0-4b6a-4f9a-9b8a-3e2b1f1c7a6d" + example: 'e8a3b5c0-4b6a-4f9a-9b8a-3e2b1f1c7a6d' responses: - "200": + '200': description: The current status of the crawl job. content: application/json: schema: - $ref: "#/components/schemas/CrawlStatusResponse" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - "429": - $ref: "#/components/responses/TooManyRequests" - "500": - $ref: "#/components/responses/InternalServerError" + $ref: '#/components/schemas/CrawlStatusResponse' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/TooManyRequests' + '500': + $ref: '#/components/responses/InternalServerError' /crawl/cancel: post: @@ -201,9 +200,9 @@ paths: jobId: type: string description: The ID of the crawl job to cancel. - example: "e8a3b5c0-4b6a-4f9a-9b8a-3e2b1f1c7a6d" + example: 'e8a3b5c0-4b6a-4f9a-9b8a-3e2b1f1c7a6d' responses: - "200": + '200': description: The job was successfully cancelled. content: application/json: @@ -212,15 +211,15 @@ paths: properties: status: type: string - example: "cancelled" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - "429": - $ref: "#/components/responses/TooManyRequests" - "500": - $ref: "#/components/responses/InternalServerError" + example: 'cancelled' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/TooManyRequests' + '500': + $ref: '#/components/responses/InternalServerError' /search: post: @@ -236,26 +235,26 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/SearchRequest" + $ref: '#/components/schemas/SearchRequest' responses: - "200": + '200': description: Search completed successfully. content: application/json: schema: type: array items: - $ref: "#/components/schemas/SearchResult" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "422": - $ref: "#/components/responses/UnprocessableEntity" - "429": - $ref: "#/components/responses/TooManyRequests" - "500": - $ref: "#/components/responses/InternalServerError" + $ref: '#/components/schemas/SearchResult' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '422': + $ref: '#/components/responses/UnprocessableEntity' + '429': + $ref: '#/components/responses/TooManyRequests' + '500': + $ref: '#/components/responses/InternalServerError' /map: post: @@ -279,9 +278,9 @@ paths: type: string format: uri description: The URL to fetch the sitemap for. - example: "https://firecrawl.dev" + example: 'https://firecrawl.dev' responses: - "200": + '200': description: Sitemap data retrieved successfully. content: application/json: @@ -290,16 +289,16 @@ paths: items: type: string format: uri - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "422": - $ref: "#/components/responses/UnprocessableEntity" - "429": - $ref: "#/components/responses/TooManyRequests" - "500": - $ref: "#/components/responses/InternalServerError" + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '422': + $ref: '#/components/responses/UnprocessableEntity' + '429': + $ref: '#/components/responses/TooManyRequests' + '500': + $ref: '#/components/responses/InternalServerError' /batch-scrape: post: @@ -323,26 +322,26 @@ paths: type: string format: uri scrapeOptions: - $ref: "#/components/schemas/ScrapeRequestV2" + $ref: '#/components/schemas/ScrapeRequestV2' responses: - "200": + '200': description: Successfully scraped all URLs. content: application/json: schema: type: array items: - $ref: "#/components/schemas/ScrapeResponse" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "422": - $ref: "#/components/responses/UnprocessableEntity" - "429": - $ref: "#/components/responses/TooManyRequests" - "500": - $ref: "#/components/responses/InternalServerError" + $ref: '#/components/schemas/ScrapeResponse' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '422': + $ref: '#/components/responses/UnprocessableEntity' + '429': + $ref: '#/components/responses/TooManyRequests' + '500': + $ref: '#/components/responses/InternalServerError' /extract: post: @@ -358,24 +357,24 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/ExtractRequest" + $ref: '#/components/schemas/ExtractRequest' responses: - "200": + '200': description: Successfully extracted data. content: application/json: schema: - $ref: "#/components/schemas/ExtractResponse" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "422": - $ref: "#/components/responses/UnprocessableEntity" - "429": - $ref: "#/components/responses/TooManyRequests" - "500": - $ref: "#/components/responses/InternalServerError" + $ref: '#/components/schemas/ExtractResponse' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '422': + $ref: '#/components/responses/UnprocessableEntity' + '429': + $ref: '#/components/responses/TooManyRequests' + '500': + $ref: '#/components/responses/InternalServerError' components: securitySchemes: @@ -383,44 +382,44 @@ components: type: http scheme: bearer bearerFormat: JWT - description: "API Key for authentication. Get yours from [firecrawl.dev](https://firecrawl.dev)." + description: 'API Key for authentication. Get yours from [firecrawl.dev](https://firecrawl.dev).' responses: BadRequest: description: The request was malformed or contained invalid parameters. content: application/json: schema: - $ref: "#/components/schemas/GenericError" + $ref: '#/components/schemas/GenericError' Unauthorized: description: Authentication failed. The API key is missing, invalid, or expired. content: application/json: schema: - $ref: "#/components/schemas/GenericError" + $ref: '#/components/schemas/GenericError' NotFound: description: The requested resource could not be found. content: application/json: schema: - $ref: "#/components/schemas/GenericError" + $ref: '#/components/schemas/GenericError' UnprocessableEntity: description: The request was well-formed but could not be processed due to validation errors. content: application/json: schema: - $ref: "#/components/schemas/ValidationError" + $ref: '#/components/schemas/ValidationError' TooManyRequests: description: The rate limit for your plan has been exceeded. content: application/json: schema: - $ref: "#/components/schemas/GenericError" + $ref: '#/components/schemas/GenericError' InternalServerError: description: An unexpected error occurred on the server. content: application/json: schema: - $ref: "#/components/schemas/GenericError" + $ref: '#/components/schemas/GenericError' schemas: GenericError: type: object @@ -428,14 +427,14 @@ components: error: type: string description: A message describing the error. - example: "Invalid API Key" + example: 'Invalid API Key' ValidationError: type: object properties: error: type: string - example: "Validation failed" + example: 'Validation failed' details: type: array items: @@ -445,10 +444,10 @@ components: type: array items: type: string - example: ["crawlerOptions", "maxDepth"] + example: ['crawlerOptions', 'maxDepth'] message: type: string - example: "Number must be less than or equal to 10" + example: 'Number must be less than or equal to 10' ScrapeRequestV2: type: object @@ -459,7 +458,7 @@ components: type: string format: uri description: The URL to scrape. - example: "https://firecrawl.dev" + example: 'https://firecrawl.dev' pageOptions: type: object properties: @@ -485,7 +484,7 @@ components: type: string description: Custom headers to use for the request. example: - User-Agent: "My-Custom-Scraper/1.0" + User-Agent: 'My-Custom-Scraper/1.0' extractorOptions: type: object properties: @@ -494,19 +493,19 @@ components: enum: - llm-extraction description: The extraction mode. - example: "llm-extraction" + example: 'llm-extraction' extractionPrompt: type: string description: A specific prompt to guide the LLM for data extraction. - example: "Extract the main title and the first paragraph of the article." + example: 'Extract the main title and the first paragraph of the article.' extractionSchema: type: object description: The JSON schema for the data to be extracted. example: - type: "object" + type: 'object' properties: - title: { type: "string" } - first_paragraph: { type: "string" } + title: { type: 'string' } + first_paragraph: { type: 'string' } scraperOptions: type: object properties: @@ -536,9 +535,9 @@ components: type: object description: Metadata extracted from the page (title, description, etc.). example: - title: "Firecrawl | Build reliable crawlers and scrapers" - description: "Turn any website into clean, structured data." - ogTitle: "Firecrawl" + title: 'Firecrawl | Build reliable crawlers and scrapers' + description: 'Turn any website into clean, structured data.' + ogTitle: 'Firecrawl' screenshotUrl: type: string format: uri @@ -549,7 +548,7 @@ components: sourceURL: type: string format: uri - example: "https://firecrawl.dev" + example: 'https://firecrawl.dev' CrawlRequestV2: type: object @@ -560,7 +559,7 @@ components: type: string format: uri description: The starting URL for the crawl. - example: "https://firecrawl.dev/blog" + example: 'https://firecrawl.dev/blog' crawlerOptions: type: object properties: @@ -569,13 +568,13 @@ components: items: type: string description: Glob patterns to include in the crawl. - example: ["/blog/**"] + example: ['/blog/**'] excludes: type: array items: type: string description: Glob patterns to exclude from the crawl. - example: ["/blog/old-posts/**"] + example: ['/blog/old-posts/**'] maxDepth: type: integer description: The maximum depth to crawl relative to the starting URL. @@ -619,7 +618,7 @@ components: status: type: string enum: [crawling, completed, failed] - example: "completed" + example: 'completed' total: type: integer description: Total number of pages found. @@ -631,7 +630,7 @@ components: data: type: array items: - $ref: "#/components/schemas/ScrapeResponse" + $ref: '#/components/schemas/ScrapeResponse' SearchRequest: type: object @@ -641,18 +640,18 @@ components: query: type: string description: The search query. - example: "What is Firecrawl?" + example: 'What is Firecrawl?' searchOptions: type: object properties: provider: type: string enum: [google, serper, searchapi, searxng] - default: "google" + default: 'google' region: type: string description: The geographic region for the search. - example: "us-east-1" + example: 'us-east-1' maxResults: type: integer description: The maximum number of search results to return. @@ -663,17 +662,17 @@ components: properties: title: type: string - example: "Firecrawl - Turn any website into clean data" + example: 'Firecrawl - Turn any website into clean data' url: type: string format: uri - example: "https://firecrawl.dev" + example: 'https://firecrawl.dev' description: type: string - example: "Firecrawl is a powerful tool for web scraping and crawling..." + example: 'Firecrawl is a powerful tool for web scraping and crawling...' content: type: string - example: "Firecrawl helps developers turn websites into structured data..." + example: 'Firecrawl helps developers turn websites into structured data...' ExtractRequest: type: object @@ -695,10 +694,10 @@ components: type: object description: The JSON schema to which the extracted data should conform. example: - type: "object" + type: 'object' properties: - author: { type: "string" } - published_date: { type: "string", format: "date" } + author: { type: 'string' } + published_date: { type: 'string', format: 'date' } extractionPrompt: type: string description: A natural language prompt to guide the extraction process. @@ -706,7 +705,7 @@ components: type: string enum: - llm-extraction - default: "llm-extraction" + default: 'llm-extraction' ExtractResponse: type: object @@ -717,5 +716,5 @@ components: type: object description: The extracted data matching the provided schema. example: - author: "John Doe" - published_date: "2024-01-15" + author: 'John Doe' + published_date: '2024-01-15' diff --git a/src/api/v2Client.ts b/src/api/v2Client.ts new file mode 100644 index 0000000..56b946c --- /dev/null +++ b/src/api/v2Client.ts @@ -0,0 +1,64 @@ +import axios, { type AxiosRequestConfig, type AxiosResponse } from 'axios'; +import { getApiConfig } from '@/config/api'; + +/** + * Builds the absolute URL for a V2 API request. + * + * @param {string} path - The endpoint path without the leading base URL. + * @returns {string} The absolute URL for the request. + */ +function buildUrl(path: string): string { + const config = getApiConfig(); + const basePath = (config.basePath ?? '').replace(/\/+$/, ''); + const trimmedPath = path.replace(/^\/+/, ''); + return `${basePath}/${trimmedPath}`; +} + +/** + * Builds the Axios configuration shared by all V2 requests. + * + * @param {AxiosRequestConfig} [override] - Optional overrides for the request. + * @returns {AxiosRequestConfig} The Axios configuration with headers and overrides merged. + */ +function buildRequestConfig(override?: AxiosRequestConfig): AxiosRequestConfig { + const config = getApiConfig(); + const baseHeaders = (config.baseOptions?.headers ?? {}) as Record; + const headers = { + 'Content-Type': 'application/json', + ...baseHeaders, + ...override?.headers, + }; + return { + ...override, + headers, + }; +} + +/** + * Sends a POST request to a V2 API endpoint. + * + * @param {string} path - The endpoint path relative to the API base URL. + * @param {unknown} payload - The request payload. + * @param {AxiosRequestConfig} [config] - Optional Axios configuration overrides. + * @returns {Promise>} The Axios response promise. + */ +export function postV2( + path: string, + payload: unknown, + config?: AxiosRequestConfig, +): Promise> { + const url = buildUrl(path); + return axios.post(url, payload, buildRequestConfig(config)); +} + +/** + * Sends a GET request to a V2 API endpoint. + * + * @param {string} path - The endpoint path relative to the API base URL. + * @param {AxiosRequestConfig} [config] - Optional Axios configuration overrides. + * @returns {Promise>} The Axios response promise. + */ +export function getV2(path: string, config?: AxiosRequestConfig): Promise> { + const url = buildUrl(path); + return axios.get(url, buildRequestConfig(config)); +} diff --git a/src/components/ApiKeyInput.vue b/src/components/ApiKeyInput.vue index 375d51a..7bba50e 100644 --- a/src/components/ApiKeyInput.vue +++ b/src/components/ApiKeyInput.vue @@ -16,14 +16,17 @@ the Firecrawl dashboard +
+ + + Select the API version that matches your backend deployment. +
- + Leave blank to use the default URL.
@@ -36,8 +39,10 @@ diff --git a/src/components/MapViewV1.vue b/src/components/MapViewV1.vue new file mode 100644 index 0000000..a0926d9 --- /dev/null +++ b/src/components/MapViewV1.vue @@ -0,0 +1,297 @@ + + + + + diff --git a/src/components/MapViewV2.vue b/src/components/MapViewV2.vue new file mode 100644 index 0000000..c135064 --- /dev/null +++ b/src/components/MapViewV2.vue @@ -0,0 +1,123 @@ + + + + + diff --git a/src/components/SearchView.vue b/src/components/SearchView.vue index c684a7e..d837eb3 100644 --- a/src/components/SearchView.vue +++ b/src/components/SearchView.vue @@ -1,450 +1,20 @@ - - - diff --git a/src/components/SearchViewV1.vue b/src/components/SearchViewV1.vue new file mode 100644 index 0000000..c684a7e --- /dev/null +++ b/src/components/SearchViewV1.vue @@ -0,0 +1,450 @@ + + + + + + diff --git a/src/components/SearchViewV2.vue b/src/components/SearchViewV2.vue new file mode 100644 index 0000000..78d4f08 --- /dev/null +++ b/src/components/SearchViewV2.vue @@ -0,0 +1,188 @@ + + + + + diff --git a/src/config/api.ts b/src/config/api.ts index 6deda21..de0f7d0 100644 --- a/src/config/api.ts +++ b/src/config/api.ts @@ -1,30 +1,84 @@ import { Configuration } from '../api-client/configuration.js'; +import { getStoredApiVersion, setStoredApiVersion, type ApiVersion } from './apiVersion.js'; + +const BASE_URL_STORAGE_KEYS: Record = { + v1: 'firecrawl_base_url_v1', + v2: 'firecrawl_base_url_v2', +}; +const LEGACY_BASE_URL_KEY = 'firecrawl_base_url'; +const DEFAULT_BASE_URLS: Record = { + v1: 'https://api.firecrawl.dev/v1', + v2: 'https://api.firecrawl.dev/v2', +}; +const API_KEY_STORAGE_KEY = 'firecrawl_api_key'; /** - * Returns the base URL for the Firecrawl API. + * Retrieves the base URL stored for the given API version. + * Falls back to the legacy storage key when no version-specific value exists. * - * @returns {string} The base URL from local storage, environment variables or a - * default value. + * @param {ApiVersion} version - The API version for which the base URL is requested. + * @returns {string | null} The stored base URL or null if none is stored. */ -const getBaseUrl = (): string => { - return ( - localStorage.getItem('firecrawl_base_url') || - import.meta.env.VITE_FIRECRAWL_API_BASE_URL || - 'https://api.firecrawl.dev/v1' - ); -}; +function readStoredBaseUrl(version: ApiVersion): string | null { + if (typeof window === 'undefined' || !window.localStorage) { + return null; + } + const versionSpecific = window.localStorage.getItem(BASE_URL_STORAGE_KEYS[version]); + if (versionSpecific && versionSpecific.trim()) { + return versionSpecific.trim(); + } + const legacyValue = window.localStorage.getItem(LEGACY_BASE_URL_KEY); + if (legacyValue && legacyValue.trim()) { + return legacyValue.trim(); + } + return null; +} /** - * Returns the API key for the Firecrawl API - * @returns {string} The API key from local storage, environment variables, or empty string + * Retrieves the base URL configured through environment variables. + * + * @param {ApiVersion} version - The API version for which the environment value should be read. + * @returns {string | null} The environment base URL or null when not configured. + */ +function readEnvBaseUrl(version: ApiVersion): string | null { + const env = import.meta.env as Record; + if (version === 'v2') { + const envValue = env.VITE_FIRECRAWL_API_BASE_URL_V2 || env.VITE_FIRECRAWL_API_BASE_URL; + return envValue && envValue.trim() ? envValue.trim() : null; + } + const envValue = env.VITE_FIRECRAWL_API_BASE_URL; + return envValue && envValue.trim() ? envValue.trim() : null; +} + +/** + * Resolves the base URL for the provided API version following the priority: + * stored value, environment variable, and finally the default URL. + * + * @param {ApiVersion} version - The API version whose base URL should be resolved. + * @returns {string} The base URL to use for API calls. + */ +function resolveBaseUrl(version: ApiVersion): string { + return readStoredBaseUrl(version) || readEnvBaseUrl(version) || DEFAULT_BASE_URLS[version]; +} + +/** + * Retrieves the stored API key or falls back to the environment configuration. + * + * @returns {string} The API key to use for authenticated requests. */ const getApiKey = (): string => { - return localStorage.getItem('firecrawl_api_key') || import.meta.env.VITE_FIRECRAWL_API_KEY || ''; + if (typeof window !== 'undefined' && window.localStorage) { + const storedKey = window.localStorage.getItem(API_KEY_STORAGE_KEY); + if (storedKey !== null) { + return storedKey; + } + } + const env = import.meta.env as Record; + return env.VITE_FIRECRAWL_API_KEY || ''; }; -// Initializes the API configuration with base URL, API key, and default headers. const apiConfig = new Configuration({ - basePath: getBaseUrl(), + basePath: resolveBaseUrl(getStoredApiVersion()), apiKey: getApiKey, baseOptions: { headers: { @@ -33,17 +87,13 @@ const apiConfig = new Configuration({ }, }); -// Dynamically adds the Authorization header if an API key is available. -// This ensures that authenticated requests are properly handled. - -// Dynamically add the Authorization header if an API key is available -const apiKey = getApiKey(); -if (apiKey) { +const initialKey = getApiKey(); +if (initialKey) { apiConfig.baseOptions = { ...apiConfig.baseOptions, headers: { ...apiConfig.baseOptions?.headers, - Authorization: `Bearer ${apiKey}`, + Authorization: `Bearer ${initialKey}`, }, }; } @@ -51,20 +101,40 @@ if (apiKey) { /** * Updates the API configuration at runtime. * - * @param baseUrl - Optional new base URL for the API. - * @param apiKeyValue - Optional new API key. + * @param {string | undefined} baseUrl - Optional new base URL for the API. + * @param {string | undefined} apiKeyValue - Optional new API key. + * @param {ApiVersion | undefined} version - Optional API version to switch to. + * @returns {void} */ -export function updateApiConfig(baseUrl?: string, apiKeyValue?: string): void { - if (typeof baseUrl === 'string') { - localStorage.setItem('firecrawl_base_url', baseUrl); - apiConfig.basePath = baseUrl || 'https://api.firecrawl.dev/v1'; +export function updateApiConfig( + baseUrl?: string, + apiKeyValue?: string, + version?: ApiVersion, +): void { + const activeVersion = version ?? getStoredApiVersion(); + if (version) { + setStoredApiVersion(version); + } + + if (typeof baseUrl === 'string' && typeof window !== 'undefined' && window.localStorage) { + const trimmedUrl = baseUrl.trim(); + if (trimmedUrl) { + window.localStorage.setItem(BASE_URL_STORAGE_KEYS[activeVersion], trimmedUrl); + } else { + window.localStorage.removeItem(BASE_URL_STORAGE_KEYS[activeVersion]); + } + window.localStorage.removeItem(LEGACY_BASE_URL_KEY); } - if (typeof apiKeyValue === 'string') { - localStorage.setItem('firecrawl_api_key', apiKeyValue); + + if (typeof apiKeyValue === 'string' && typeof window !== 'undefined' && window.localStorage) { + window.localStorage.setItem(API_KEY_STORAGE_KEY, apiKeyValue); apiConfig.apiKey = apiKeyValue; } - const key = apiKeyValue ?? getApiKey(); + const resolvedUrl = resolveBaseUrl(activeVersion); + apiConfig.basePath = resolvedUrl; + + const key = typeof apiKeyValue === 'string' ? apiKeyValue : getApiKey(); apiConfig.baseOptions = { headers: { 'Content-Type': 'application/json', @@ -76,10 +146,39 @@ export function updateApiConfig(baseUrl?: string, apiKeyValue?: string): void { /** * Retrieve the current API configuration instance. * - * @returns The active API configuration. + * @returns {Configuration} The active API configuration. */ export function getApiConfig(): Configuration { return apiConfig; } +/** + * Retrieves the base URL for a specific API version. + * + * @param {ApiVersion} version - The version whose base URL should be returned. + * @returns {string} The resolved base URL. + */ +export function getBaseUrlForVersion(version: ApiVersion): string { + return resolveBaseUrl(version); +} + +/** + * Retrieves the base URL for the currently active API version. + * + * @returns {string} The base URL configured for the active version. + */ +export function getBaseUrl(): string { + return resolveBaseUrl(getStoredApiVersion()); +} + +/** + * Provides the default base URL for the provided API version. + * + * @param {ApiVersion} version - The API version whose default URL is required. + * @returns {string} The default base URL for the version. + */ +export function getDefaultBaseUrl(version: ApiVersion): string { + return DEFAULT_BASE_URLS[version]; +} + export default apiConfig; diff --git a/src/config/apiVersion.ts b/src/config/apiVersion.ts new file mode 100644 index 0000000..5ae5085 --- /dev/null +++ b/src/config/apiVersion.ts @@ -0,0 +1,59 @@ +import { ref, type Ref } from 'vue'; + +export type ApiVersion = 'v1' | 'v2'; + +const STORAGE_KEY = 'firecrawl_api_version'; +const DEFAULT_VERSION: ApiVersion = 'v1'; + +/** + * Reads the stored API version from local storage. + * + * @returns {ApiVersion} The stored API version or the default when unavailable. + */ +function readStoredVersion(): ApiVersion { + if (typeof window === 'undefined' || !window.localStorage) { + return DEFAULT_VERSION; + } + const storedValue = window.localStorage.getItem(STORAGE_KEY); + return storedValue === 'v2' ? 'v2' : DEFAULT_VERSION; +} + +const versionRef: Ref = ref(readStoredVersion()); + +/** + * Retrieves the currently stored API version. + * + * @returns {ApiVersion} The active API version. + */ +export function getStoredApiVersion(): ApiVersion { + return versionRef.value; +} + +/** + * Persists the API version in local storage and updates the shared reference. + * + * @param {ApiVersion} version - The API version to persist. + * @returns {void} + */ +export function setStoredApiVersion(version: ApiVersion): void { + if (typeof window !== 'undefined' && window.localStorage) { + window.localStorage.setItem(STORAGE_KEY, version); + } + versionRef.value = version; +} + +/** + * Provides reactive accessors for the API version value. + * + * @returns {{ apiVersion: Ref; setApiVersion: (version: ApiVersion) => void }} + * The reactive reference and the setter function. + */ +export function useApiVersion(): { + apiVersion: Ref; + setApiVersion: (version: ApiVersion) => void; +} { + return { + apiVersion: versionRef, + setApiVersion: setStoredApiVersion, + }; +} diff --git a/src/plugins/api.ts b/src/plugins/api.ts index 6a9b05c..11e9ac0 100644 --- a/src/plugins/api.ts +++ b/src/plugins/api.ts @@ -8,6 +8,7 @@ import { SearchApi, } from '../api-client/index.js'; import apiConfig, { getApiConfig, updateApiConfig } from '../config/api.js'; +import type { ApiVersion } from '../config/apiVersion.js'; /** * Vue plugin responsible for registering and providing various Firecrawl API clients @@ -50,9 +51,10 @@ const apiPlugin = { * * @param baseUrl - Optional new base URL. * @param apiKey - Optional new API key. + * @param version - Optional API version selector. */ -export function refreshApiClients(baseUrl?: string, apiKey?: string): void { - updateApiConfig(baseUrl, apiKey); +export function refreshApiClients(baseUrl?: string, apiKey?: string, version?: ApiVersion): void { + updateApiConfig(baseUrl, apiKey, version); apiClients.billing = new BillingApi(getApiConfig()); apiClients.crawling = new CrawlingApi(getApiConfig()); apiClients.extraction = new ExtractionApi(getApiConfig()); diff --git a/src/views/CrawlView.vue b/src/views/CrawlView.vue index 596a510..2a09081 100644 --- a/src/views/CrawlView.vue +++ b/src/views/CrawlView.vue @@ -1,1786 +1,20 @@ - - - diff --git a/src/views/CrawlViewV1.vue b/src/views/CrawlViewV1.vue new file mode 100644 index 0000000..596a510 --- /dev/null +++ b/src/views/CrawlViewV1.vue @@ -0,0 +1,1786 @@ + + + + + diff --git a/src/views/CrawlViewV2.vue b/src/views/CrawlViewV2.vue new file mode 100644 index 0000000..276e71b --- /dev/null +++ b/src/views/CrawlViewV2.vue @@ -0,0 +1,486 @@ + + + + + diff --git a/src/views/ExtractView.vue b/src/views/ExtractView.vue index 49ac635..98173db 100644 --- a/src/views/ExtractView.vue +++ b/src/views/ExtractView.vue @@ -1,250 +1,20 @@ - - diff --git a/src/views/ExtractViewV1.vue b/src/views/ExtractViewV1.vue new file mode 100644 index 0000000..49ac635 --- /dev/null +++ b/src/views/ExtractViewV1.vue @@ -0,0 +1,250 @@ + + + + + diff --git a/src/views/ExtractViewV2.vue b/src/views/ExtractViewV2.vue new file mode 100644 index 0000000..697833c --- /dev/null +++ b/src/views/ExtractViewV2.vue @@ -0,0 +1,255 @@ + + + + + diff --git a/src/views/ScrapeView.vue b/src/views/ScrapeView.vue index f346f0b..f2c48b8 100644 --- a/src/views/ScrapeView.vue +++ b/src/views/ScrapeView.vue @@ -1,793 +1,20 @@ - - - diff --git a/src/views/ScrapeViewV1.vue b/src/views/ScrapeViewV1.vue new file mode 100644 index 0000000..f346f0b --- /dev/null +++ b/src/views/ScrapeViewV1.vue @@ -0,0 +1,793 @@ + + + + + diff --git a/src/views/ScrapeViewV2.vue b/src/views/ScrapeViewV2.vue new file mode 100644 index 0000000..c9229e9 --- /dev/null +++ b/src/views/ScrapeViewV2.vue @@ -0,0 +1,395 @@ + + + + +