UNPKG

@testdog/ai

Version:

SDK for integrating the Testdog AI Video Intelligence API

193 lines (190 loc) 7.89 kB
// src/client.ts import axios from "axios"; import { jwtDecode } from "jwt-decode"; // src/errors.ts var SdkError = class _SdkError extends Error { constructor(message) { super(message); this.name = "SdkError"; Object.setPrototypeOf(this, _SdkError.prototype); } }; var SdkAuthenticationError = class _SdkAuthenticationError extends SdkError { constructor(message) { super(message); this.name = "SdkAuthenticationError"; Object.setPrototypeOf(this, _SdkAuthenticationError.prototype); } }; var SdkRequestError = class _SdkRequestError extends SdkError { constructor(message, statusCode, details) { super(message); this.name = "SdkRequestError"; this.statusCode = statusCode; this.details = details; Object.setPrototypeOf(this, _SdkRequestError.prototype); } }; var SdkInputError = class _SdkInputError extends SdkError { constructor(message) { super(message); this.name = "SdkInputError"; Object.setPrototypeOf(this, _SdkInputError.prototype); } }; // src/client.ts var DEFAULT_API_BASE_URL = "https://ai.sheryians.com/api/v1"; var DEFAULT_IFRAME_BASE_URL = "https://ai.sheryians.com/source/chat"; var TOKEN_EXPIRY_BUFFER_SECONDS = 60; var testdogAI = class { // Store expiry timestamp (in seconds) /** * Initializes the testdogAI SDK. * @param config - SDK configuration including accessKey and secretKey. * @param config.accessKey - The access key for API authentication. * @param config.secretKey - The secret key for API authentication. * @param config.apiBaseUrl - Optional base URL for the API */ constructor(config) { this.apiAccessToken = null; this.apiAccessTokenExpiry = null; this.accessKey = config.accessKey; this.secretKey = config.secretKey; this.iframeBaseUrl = config.iframeBaseUrl || DEFAULT_IFRAME_BASE_URL; this.apiClient = axios.create({ baseURL: config.apiBaseUrl || DEFAULT_API_BASE_URL, headers: { "Content-Type": "application/json" }, withCredentials: true // Enable cookies with cross-origin requests }); } /** * Retrieves a valid API Access Token, fetching a new one if necessary. * @returns The API Access Token. * @throws {SdkAuthenticationError} If fetching the token fails due to invalid credentials. * @throws {SdkRequestError} If the API request fails for other reasons. */ async getApiAccessToken() { var _a, _b, _c, _d, _e, _f; const nowInSeconds = Math.floor(Date.now() / 1e3); if (this.apiAccessToken && this.apiAccessTokenExpiry && this.apiAccessTokenExpiry > nowInSeconds + TOKEN_EXPIRY_BUFFER_SECONDS) { return this.apiAccessToken; } console.debug("SDK: API Access Token expired or missing, fetching new token..."); try { const response = await this.apiClient.post( "/customers/generateAccessToken", {}, // Empty body { headers: { // Send keys in headers as expected by your backend endpoint "accessKey": this.accessKey, "secretKey": this.secretKey // Use the stored secret key here }, withCredentials: true // Ensure cookies are sent with this request } ); const token = response.data.token; if (!token) { throw new SdkAuthenticationError("Failed to retrieve API access token from response."); } this.apiAccessToken = token; try { const decoded = jwtDecode(token); this.apiAccessTokenExpiry = decoded.exp; console.debug(`SDK: New API Access Token obtained, expires at ${new Date(decoded.exp * 1e3).toISOString()}`); } catch (decodeError) { console.error("SDK: Failed to decode new API access token:", decodeError); this.apiAccessToken = null; this.apiAccessTokenExpiry = null; throw new SdkAuthenticationError("Received invalid API access token format."); } return this.apiAccessToken; } catch (error) { this.apiAccessToken = null; this.apiAccessTokenExpiry = null; if (axios.isAxiosError(error)) { const axiosError = error; if (((_a = axiosError.response) == null ? void 0 : _a.status) === 401 || ((_b = axiosError.response) == null ? void 0 : _b.status) === 400) { console.error("SDK Authentication Error:", ((_c = axiosError.response) == null ? void 0 : _c.data) || axiosError.message); throw new SdkAuthenticationError("Invalid accessKey or secretKey."); } console.error("SDK API Request Error (fetching access token):", ((_d = axiosError.response) == null ? void 0 : _d.data) || axiosError.message); throw new SdkRequestError( `Failed to fetch API access token: ${axiosError.message}`, (_e = axiosError.response) == null ? void 0 : _e.status, (_f = axiosError.response) == null ? void 0 : _f.data ); } console.error("SDK Unexpected Error (fetching access token):", error); throw new SdkError("An unexpected error occurred while fetching the API access token."); } } /** * Generates a secure URL for the AI chat iframe. * @param options - Options including studentId, studentName, sourceId, and optionally iframeBaseUrl. * @returns The generated iframe URL containing a short-lived token. * @throws {SdkInputError} If required options are missing. * @throws {SdkAuthenticationError} If SDK authentication fails. * @throws {SdkRequestError} If the API request to generate the iframe token fails. */ async generateIframeUrl(options) { var _a, _b, _c, _d, _e, _f; const { studentId, studentName, sourceId } = options; if (!studentId || !studentName || !sourceId) { throw new SdkInputError("studentId, studentName, and sourceId are required to generate the iframe URL."); } try { const apiToken = await this.getApiAccessToken(); const response = await this.apiClient.post( "/iframe/generate-url-token", { studentId, studentName, sourceId }, { headers: { "Authorization": `Bearer ${apiToken}` // Use the API Access Token } } ); const iframeToken = response.data.iframeToken; if (!iframeToken) { throw new SdkRequestError("Failed to retrieve iframe token from response."); } const targetIframeBaseUrl = options.iframeBaseUrl || this.iframeBaseUrl; const iframeUrl = `${targetIframeBaseUrl}?token=${encodeURIComponent(iframeToken)}`; return iframeUrl; } catch (error) { if (error instanceof SdkAuthenticationError || error instanceof SdkInputError) { throw error; } if (axios.isAxiosError(error)) { const axiosError = error; console.error("SDK API Request Error (generating iframe token):", ((_a = axiosError.response) == null ? void 0 : _a.data) || axiosError.message); if (((_b = axiosError.response) == null ? void 0 : _b.status) === 401 || ((_c = axiosError.response) == null ? void 0 : _c.status) === 403) { this.apiAccessToken = null; this.apiAccessTokenExpiry = null; throw new SdkAuthenticationError(`API access token rejected (status: ${(_d = axiosError.response) == null ? void 0 : _d.status}). Please retry.`); } throw new SdkRequestError( `Failed to generate iframe token: ${axiosError.message}`, (_e = axiosError.response) == null ? void 0 : _e.status, (_f = axiosError.response) == null ? void 0 : _f.data ); } console.error("SDK Unexpected Error (generating iframe URL):", error); throw new SdkError("An unexpected error occurred while generating the iframe URL."); } } }; export { SdkAuthenticationError, SdkError, SdkInputError, SdkRequestError, testdogAI };