channel3-sdk
Version:
The official TypeScript/JavaScript SDK for Channel3 AI Shopping API
120 lines • 5.26 kB
JavaScript
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