Spaces:
Paused
Paused
| /** | |
| * official TypeScript/JavaScript SDK client for the Opticparse Vision-Scrape API. | |
| * Compatible with Node.js environments and modern web browsers. | |
| */ | |
| export interface ScrapeRequest { | |
| targetUrl: string; | |
| extractionQuery: string; | |
| viewportWidth?: number; | |
| viewportHeight?: number; | |
| waitUntil?: 'networkidle' | 'load' | 'domcontentloaded'; | |
| timeout?: number; | |
| } | |
| export interface ClientConfig { | |
| /** | |
| * The API Key for authorization. | |
| * Maps to X-API-Key header for direct server connection. | |
| */ | |
| apiKey?: string; | |
| /** | |
| * Custom target URL for the Opticparse service. | |
| * Defaults to http://localhost:8000. | |
| */ | |
| apiUrl?: string; | |
| } | |
| export class OpticparseError extends Error { | |
| statusCode?: number; | |
| details?: string; | |
| constructor(message: string, statusCode?: number, details?: string) { | |
| super(message); | |
| this.name = 'OpticparseError'; | |
| this.statusCode = statusCode; | |
| this.details = details; | |
| } | |
| } | |
| export class OpticparseClient { | |
| private apiKey: string; | |
| private apiUrl: string; | |
| constructor(config: ClientConfig = {}) { | |
| // Attempt to load from Node.js environment if not provided explicitly | |
| const getEnv = (key: string): string => { | |
| return (typeof process !== 'undefined' && process.env ? process.env[key] : '') || ''; | |
| }; | |
| this.apiKey = config.apiKey || getEnv('OPTICPARSE_API_KEY'); | |
| this.apiUrl = config.apiUrl || getEnv('OPTICPARSE_API_URL') || 'http://localhost:8000'; | |
| } | |
| /** | |
| * Performs an AI-driven visual scraping operation against the target webpage. | |
| * | |
| * @param request Target site details and desired JSON format instructions | |
| * @returns The parsed JSON object containing target details | |
| * @throws {OpticparseError} on HTTP authorization issues, network timeout, or server failure. | |
| */ | |
| async scrape<T = any>(request: ScrapeRequest): Promise<T> { | |
| if (!this.apiKey) { | |
| throw new OpticparseError( | |
| 'Client misconfigured: API Key is missing. Configure via the constructor or OPTICPARSE_API_KEY environment variable.' | |
| ); | |
| } | |
| const payload = { | |
| target_url: request.targetUrl, | |
| extraction_query: request.extractionQuery, | |
| viewport_width: request.viewportWidth ?? 1280, | |
| viewport_height: request.viewportHeight ?? 800, | |
| wait_until: request.waitUntil ?? 'networkidle', | |
| timeout: request.timeout ?? 90000, | |
| }; | |
| const headers: Record<string, string> = { | |
| 'Content-Type': 'application/json', | |
| 'X-API-Key': this.apiKey, | |
| }; | |
| const controller = new AbortController(); | |
| const requestTimeout = request.timeout ?? 90000; // default 90s maximum duration | |
| const timeoutId = setTimeout(() => controller.abort(), requestTimeout); | |
| // Normalize URL pathing | |
| const endpoint = `${this.apiUrl.replace(/\/+$/, '')}/api/vision-scrape`; | |
| try { | |
| const response = await fetch(endpoint, { | |
| method: 'POST', | |
| headers, | |
| body: JSON.stringify(payload), | |
| signal: controller.signal, | |
| }); | |
| clearTimeout(timeoutId); | |
| if (!response.ok) { | |
| let errorDetails = ''; | |
| try { | |
| errorDetails = await response.text(); | |
| } catch { | |
| // ignore parsing error | |
| } | |
| if (response.status === 401 || response.status === 403) { | |
| throw new OpticparseError( | |
| 'Unauthorized: Invalid or missing API key credential.', | |
| response.status, | |
| errorDetails | |
| ); | |
| } | |
| if (response.status === 400) { | |
| throw new OpticparseError( | |
| 'Bad Request: Scraper endpoint rejected configuration options.', | |
| 400, | |
| errorDetails | |
| ); | |
| } | |
| throw new OpticparseError( | |
| `Request failed with HTTP status ${response.status}`, | |
| response.status, | |
| errorDetails | |
| ); | |
| } | |
| const data = await response.json(); | |
| return data as T; | |
| } catch (error: any) { | |
| clearTimeout(timeoutId); | |
| if (error.name === 'AbortError') { | |
| throw new OpticparseError(`Request timed out after limit of ${requestTimeout}ms`); | |
| } | |
| if (error instanceof OpticparseError) { | |
| throw error; | |
| } | |
| throw new OpticparseError(`Network request failure: ${error.message || error}`); | |
| } | |
| } | |
| } | |