neuwo-api
Version:
TypeScript/JavaScript SDK client for the Neuwo content classification API
163 lines • 7.91 kB
JavaScript
;
/**
* EDGE API client for Neuwo API.
*
* This module provides a client for analysis where content is identified by URL (websites).
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.NeuwoEdgeClient = void 0;
const errors_js_1 = require("./errors.js");
const logger_js_1 = require("./logger.js");
const models_js_1 = require("./models.js");
const utils_js_1 = require("./utils.js");
/**
* Client for Neuwo EDGE API endpoints.
*
* EDGE endpoints operate over standard HTTP methods and use an EDGE API token passed as a query parameter.
* EDGE endpoints are designed for client-side integration where content is identified by URL.
* The EDGE API serves publishers who want to enrich the data of published articles.
*/
class NeuwoEdgeClient {
/**
* Initialise the EDGE API client.
*
* @param config - Client configuration
* @param config.token - EDGE API authentication token
* @param config.baseUrl - Base URL for the API server
* @param config.timeout - Request timeout in seconds (default: 60)
* @param config.defaultOrigin - Default origin header for requests
*/
constructor(config) {
if (!config.token || typeof config.token !== "string") {
throw new errors_js_1.ValueError("Token must be a non-empty string");
}
if (!config.baseUrl || typeof config.baseUrl !== "string") {
throw new errors_js_1.ValueError("Base URL must be a non-empty string");
}
const token = config.token.trim();
const baseUrl = config.baseUrl.trim().replace(/\/$/, "");
const timeout = config.timeout || NeuwoEdgeClient.DEFAULT_TIMEOUT;
this.requestHandler = new utils_js_1.RequestHandler(token, baseUrl, timeout);
this.defaultOrigin = config.defaultOrigin;
logger_js_1.logger.info(`Initialised NeuwoEdgeClient with base URL: ${baseUrl}`);
}
/**
* Retrieve AI-generated tags for a URL (raw response).
*
* Returns the raw HTTP response without parsing.
*
* @returns Raw HTTP Response object
* @throws {ValidationError} If URL is invalid
* @throws {AuthenticationError} If token is invalid
* @throws {ForbiddenError} If token lacks permissions
* @throws {NoDataAvailableError} If URL hasn't been processed yet
* @throws {ContentNotAvailableError} If tagging could not be created
* @throws {NeuwoAPIError} For other API errors
*/
async getAiTopicsRaw(params) {
(0, utils_js_1.validateUrl)(params.url);
const queryParams = {
url: params.url,
};
const headers = {};
const origin = params.origin || this.defaultOrigin;
if (origin) {
headers.Origin = origin;
}
logger_js_1.logger.info(`Getting AI topics for URL: ${params.url}`);
return this.requestHandler.request({
method: "GET",
endpoint: "/edge/GetAiTopics",
params: queryParams,
headers: Object.keys(headers).length > 0 ? headers : undefined,
});
}
/**
* Retrieve AI-generated tags and classifications for a URL.
*
* If the URL has been processed by the Neuwo crawler, returns tags, brand safety,
* marketing categories (IAB Content Taxonomy, IAB Audience Taxonomy, Google Topics),
* and smart tags for the article.
*
* When called for the first time with a specific URL or if the URL is still in the
* queue being processed, raises NoDataAvailableError (the URL is queued for
* processing, which typically takes 10-60 seconds).
*
* @returns GetAiTopicsResponse object containing tags, brand safety, marketing categories, and smart tags
* @throws {ValidationError} If URL is invalid
* @throws {AuthenticationError} If token is invalid
* @throws {ForbiddenError} If token lacks permissions
* @throws {NoDataAvailableError} If URL hasn't been processed yet
* @throws {ContentNotAvailableError} If tagging could not be created
* @throws {NeuwoAPIError} For other API errors
*/
async getAiTopics(params) {
const response = await this.getAiTopicsRaw(params);
const data = await (0, utils_js_1.parseJsonResponse)(response);
const result = models_js_1.GetAiTopicsResponse.fromApiResponse(data);
logger_js_1.logger.info(`Retrieved ${result.tags.length} tags and ${result.smartTags.length} smart tags`);
return result;
}
/**
* Retrieve AI-generated tags for a URL with automatic retry on 404.
*
* This method automatically handles the case when a URL hasn't been processed yet.
* It will wait and retry multiple times until the data is available or max retries
* is reached. Typically processing takes 10-60 seconds for new URLs.
*
* @returns GetAiTopicsResponse object containing tags, brand safety, marketing categories, and smart tags
* @throws {ValidationError} If URL is invalid
* @throws {AuthenticationError} If token is invalid
* @throws {ForbiddenError} If token lacks permissions
* @throws {NoDataAvailableError} If data not available after max retries
* @throws {ContentNotAvailableError} If tagging could not be created
* @throws {NeuwoAPIError} For other API errors
*/
async getAiTopicsWait(params) {
const maxRetries = params.maxRetries ?? 10;
const retryInterval = params.retryInterval ?? 6;
const initialDelay = params.initialDelay ?? 2;
logger_js_1.logger.info(`Will retry up to ${maxRetries} times with ${retryInterval}s interval`);
// Initial delay to give the system time to queue the request
if (initialDelay > 0) {
logger_js_1.logger.info(`Initial delay of ${initialDelay}s before first request`);
await (0, utils_js_1.sleep)(initialDelay * 1000);
}
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
logger_js_1.logger.info(`Attempt ${attempt + 1}/${maxRetries + 1} to get AI topics`);
return await this.getAiTopics({
url: params.url,
origin: params.origin,
});
}
catch (error) {
if (error instanceof errors_js_1.NoDataAvailableError) {
// Handle 404 "No data yet available" error
logger_js_1.logger.debug(`Attempt ${attempt + 1}/${maxRetries + 1}: Data not yet available`);
if (attempt >= maxRetries) {
logger_js_1.logger.error(`Max retries (${maxRetries}) reached, giving up`);
throw new errors_js_1.NoDataAvailableError(`Data not available after ${maxRetries + 1} attempts (${maxRetries * retryInterval + initialDelay}s total). ` +
`The URL may still be processing or unavailable.`);
}
// Wait before retrying
logger_js_1.logger.info(`Waiting ${retryInterval}s before retry ${attempt + 2}/${maxRetries + 1}...`);
await (0, utils_js_1.sleep)(retryInterval * 1000);
}
else if (error instanceof errors_js_1.ContentNotAvailableError) {
// Tagging could not be created - this is a permanent error, don't retry
logger_js_1.logger.error(`Content not available: ${error.message}`);
throw error;
}
else {
throw error;
}
}
}
// Should not reach here, but just in case
throw new errors_js_1.NoDataAvailableError(`Failed to get data for URL: ${params.url}`);
}
}
exports.NeuwoEdgeClient = NeuwoEdgeClient;
NeuwoEdgeClient.DEFAULT_TIMEOUT = 60;
//# sourceMappingURL=edge-client.js.map