semanticpen
Version:
AI Article Writer & SEO Blog Generator SDK - Professional TypeScript/JavaScript library for automated content creation, AI-powered article generation, and SEO blog writing with SemanticPen
170 lines • 6.61 kB
JavaScript
import { ValidationError } from '../errors';
export class StatusPoller {
constructor(articleService) {
this.defaultConfig = {
interval: 5000,
maxAttempts: 60,
backoffMultiplier: 1.2
};
this.articleService = articleService;
}
async pollUntilComplete(articleId, config, callbacks) {
const pollingConfig = { ...this.defaultConfig, ...config };
this.validatePollingConfig(pollingConfig);
const startTime = Date.now();
let currentInterval = pollingConfig.interval;
let attempts = 0;
while (attempts < pollingConfig.maxAttempts) {
attempts++;
try {
const article = await this.articleService.getArticle(articleId);
if (callbacks?.onProgress) {
callbacks.onProgress(attempts, article.status, article);
}
if (this.isComplete(article)) {
const totalTime = Date.now() - startTime;
if (article.status === 'finished' && callbacks?.onComplete) {
callbacks.onComplete(article);
}
return {
success: article.status === 'finished',
article,
error: article.status === 'failed' ? article.error_message : undefined,
attempts,
totalTime
};
}
if (attempts < pollingConfig.maxAttempts) {
await this.sleep(currentInterval);
if (pollingConfig.backoffMultiplier && pollingConfig.backoffMultiplier > 1) {
currentInterval = Math.floor(currentInterval * pollingConfig.backoffMultiplier);
currentInterval = Math.min(currentInterval, 30000);
}
}
}
catch (error) {
if (callbacks?.onError) {
callbacks.onError(error.message || 'Polling error', attempts);
}
if (error.statusCode === 404 && attempts < pollingConfig.maxAttempts) {
await this.sleep(currentInterval);
continue;
}
return {
success: false,
error: error.message || 'Failed to poll article status',
attempts,
totalTime: Date.now() - startTime
};
}
}
const totalTime = Date.now() - startTime;
if (callbacks?.onTimeout) {
callbacks.onTimeout(attempts, totalTime);
}
return {
success: false,
error: `Polling timed out after ${attempts} attempts (${totalTime}ms)`,
attempts,
totalTime
};
}
async pollMultipleArticles(articleIds, config, callbacks) {
if (!articleIds || articleIds.length === 0) {
throw new ValidationError('Article IDs array cannot be empty');
}
const pollingPromises = articleIds.map(articleId => this.pollUntilComplete(articleId, config, callbacks));
return Promise.all(pollingPromises);
}
async waitForGenerationComplete(articleIds, config, onProgress) {
const startTime = Date.now();
const completed = [];
const failed = [];
const results = await this.pollMultipleArticles(articleIds, config, {
onComplete: (article) => {
completed.push(article);
if (onProgress) {
onProgress(completed.length, articleIds.length, [...completed]);
}
},
onError: (error, attempt) => {
}
});
results.forEach((result, index) => {
const articleId = articleIds[index];
if (result.success && result.article) {
if (!completed.find(a => a.id === articleId)) {
completed.push(result.article);
}
}
else {
failed.push({
articleId,
error: result.error || 'Unknown error'
});
}
});
return {
completed,
failed,
totalTime: Date.now() - startTime
};
}
async getMultipleStatus(articleIds) {
const completed = [];
const pending = [];
const failed = [];
const notFound = [];
const promises = articleIds.map(async (articleId) => {
try {
const article = await this.articleService.getArticle(articleId);
switch (article.status.toLowerCase()) {
case 'finished':
completed.push(article);
break;
case 'failed':
failed.push(article);
break;
default:
pending.push(article);
}
}
catch (error) {
if (error.statusCode === 404) {
notFound.push(articleId);
}
else {
failed.push({
id: articleId,
status: 'failed',
error_message: error.message
});
}
}
});
await Promise.all(promises);
return { completed, pending, failed, notFound };
}
isComplete(article) {
const completedStatuses = ['finished', 'failed'];
return completedStatuses.includes(article.status.toLowerCase());
}
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
validatePollingConfig(config) {
if (config.interval < 1000) {
throw new ValidationError('Polling interval must be at least 1000ms');
}
if (config.maxAttempts < 1) {
throw new ValidationError('Max attempts must be at least 1');
}
if (config.backoffMultiplier && config.backoffMultiplier < 1) {
throw new ValidationError('Backoff multiplier must be >= 1');
}
if (config.interval < 2000 && config.maxAttempts > 10) {
console.warn('[SemanticPen SDK] Warning: Very aggressive polling detected. Consider increasing interval or reducing max attempts.');
}
}
}
//# sourceMappingURL=StatusPoller.js.map