UNPKG

channel3-sdk

Version:

The official TypeScript/JavaScript SDK for Channel3 AI Shopping API

120 lines 5.26 kB
import fetch from 'cross-fetch'; import { Channel3Error, Channel3AuthenticationError, Channel3ValidationError, Channel3NotFoundError, Channel3ServerError, Channel3ConnectionError, } from './exceptions'; import { Channel3ApiApi, Configuration } from './generated'; export class Channel3Client { constructor(config) { this.timeoutMs = 30000; if (!config.apiKey) { const envApiKey = typeof process !== 'undefined' ? process.env?.CHANNEL3_API_KEY : undefined; if (!envApiKey) { throw new Error('No API key provided. Set CHANNEL3_API_KEY environment variable or pass apiKey in config.'); } this.apiKey = envApiKey; } else { this.apiKey = config.apiKey; } const configuration = new Configuration({ basePath: (typeof process !== 'undefined' && process.env && process.env.CHANNEL3_BASE_PATH) || 'https://api.trychannel3.com', apiKey: async () => this.apiKey, fetchApi: fetch, }); this.api = new Channel3ApiApi(configuration); } createTimeoutOverride() { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs); const initOverride = async ({ init }) => ({ ...init, signal: controller.signal, }); const clear = () => clearTimeout(timeoutId); return { initOverride, clear }; } async handleGeneratedCall(fn) { const { initOverride, clear } = this.createTimeoutOverride(); try { const result = await fn(initOverride); clear(); return result; } catch (error) { clear(); await this.rethrowAsChannel3Error(error); throw error; } } async rethrowAsChannel3Error(error) { if (error && typeof error === 'object' && 'name' in error) { const err = error; if (err.name === 'ResponseError' && err.response) { const status = err.response.status; let responseData = null; try { responseData = await err.response.clone().json(); } catch { try { const text = await err.response.clone().text(); responseData = { detail: text }; } catch { responseData = null; } } const url = err.response.url; const errorMessage = responseData?.detail || `Request failed with status ${status}`; switch (status) { case 401: throw new Channel3AuthenticationError('Invalid or missing API key', status, responseData); case 404: throw new Channel3NotFoundError(errorMessage, status, responseData); case 422: throw new Channel3ValidationError(`Validation error: ${errorMessage}`, status, responseData); case 500: throw new Channel3ServerError('Internal server error', status, responseData); default: throw new Channel3Error(`Request to ${url} failed: ${errorMessage}`, status, responseData); } } if (err.name === 'FetchError') { throw new Channel3ConnectionError(`Request failed: ${err.message}`); } if (err.name === 'AbortError') { throw new Channel3ConnectionError('Request timed out'); } } if (error instanceof Channel3Error) { throw error; } if (error instanceof Error) { throw new Channel3ConnectionError(`Request failed: ${error.message}`); } throw new Channel3Error(`Unexpected error: ${String(error)}`); } async search(options = {}) { const products = await this.handleGeneratedCall((initOverride) => this.api.searchV0SearchPost({ searchRequest: options }, initOverride)); return products; } async getProduct(productId) { if (!productId || !productId.trim()) { throw new Error('productId cannot be empty'); } const product = await this.handleGeneratedCall((initOverride) => this.api.getProductDetailV0ProductsProductIdGet({ productId }, initOverride)); return product; } async getBrands(options = {}) { const result = await this.handleGeneratedCall((initOverride) => this.api.getBrandsV0BrandsGet({ query: options.query, page: options.page, size: options.size }, initOverride)); return result; } async getBrand(brandId) { if (!brandId || !brandId.trim()) { throw new Error('brandId cannot be empty'); } const brand = await this.handleGeneratedCall((initOverride) => this.api.getBrandDetailV0BrandsBrandIdGet({ brandId }, initOverride)); return brand; } } export { Channel3Client as AsyncChannel3Client }; //# sourceMappingURL=client.js.map