Nanny7's picture
initial deploy
bcf46c3
Raw
History Blame Contribute Delete
4.29 kB
/**
* 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}`);
}
}
}