@snehal96/unimail
Version:
Unified email fetching & document extraction layer for modern web apps
136 lines (135 loc) • 4.91 kB
JavaScript
/**
* Common streaming utility for email adapters
* Provides shared functionality for streaming emails in batches
*/
export class EmailStreamService {
/**
* Creates an async generator that yields email batches
* This is the core streaming implementation that adapters can use
*/
static async *createEmailStream(fetchPageFn, options) {
const batchSize = options.batchSize || 50;
const maxEmails = options.maxEmails;
let pageToken = options.pageToken;
let totalProcessed = 0;
let hasMore = true;
while (hasMore) {
try {
// Determine how many emails to fetch in this request
let requestSize = batchSize;
if (maxEmails && (totalProcessed + requestSize) > maxEmails) {
requestSize = maxEmails - totalProcessed;
}
if (requestSize <= 0) {
break;
}
// Fetch the page
const response = await fetchPageFn(pageToken, requestSize);
if (!response.emails || response.emails.length === 0) {
break;
}
// Yield this batch
yield response.emails;
totalProcessed += response.emails.length;
pageToken = response.nextPageToken;
hasMore = !!pageToken;
// Check if we've reached the maximum
if (maxEmails && totalProcessed >= maxEmails) {
break;
}
}
catch (error) {
console.error('Error in email stream:', error);
throw error;
}
}
}
/**
* Executes email streaming with callbacks for progress tracking
*/
static async processEmailStream(streamGenerator, callbacks) {
const summary = {
totalProcessed: 0,
totalBatches: 0,
errors: 0,
startTime: new Date(),
endTime: new Date(),
duration: 0
};
try {
let batchNumber = 0;
for await (const emailBatch of streamGenerator) {
batchNumber++;
summary.totalBatches = batchNumber;
summary.totalProcessed += emailBatch.length;
const progress = {
current: summary.totalProcessed,
batchCount: batchNumber,
// Note: total and estimatedRemaining would need to be set by the adapter
// since we don't have access to that information at this level
};
try {
// Call the batch callback
if (callbacks.onBatch) {
await callbacks.onBatch(emailBatch, progress);
}
// Call the progress callback
if (callbacks.onProgress) {
await callbacks.onProgress(progress);
}
}
catch (error) {
summary.errors++;
if (callbacks.onError) {
await callbacks.onError(error, progress);
}
else {
// Re-throw if no error handler is provided
throw error;
}
}
}
}
finally {
summary.endTime = new Date();
summary.duration = summary.endTime.getTime() - summary.startTime.getTime();
if (callbacks.onComplete) {
await callbacks.onComplete(summary);
}
}
}
/**
* Utility to create a stream batch object with metadata
*/
static createStreamBatch(emails, batchNumber, progress, isLastBatch = false) {
return {
emails,
batchNumber,
progress,
isLastBatch
};
}
/**
* Utility to calculate estimated remaining emails
*/
static calculateEstimatedRemaining(totalCount, currentProcessed) {
if (!totalCount || totalCount <= currentProcessed) {
return undefined;
}
return totalCount - currentProcessed;
}
/**
* Utility to validate streaming options
*/
static validateStreamOptions(options) {
if (options.batchSize && options.batchSize <= 0) {
throw new Error('batchSize must be greater than 0');
}
if (options.maxEmails && options.maxEmails <= 0) {
throw new Error('maxEmails must be greater than 0');
}
if (options.batchSize && options.batchSize > 1000) {
console.warn('Warning: Large batch sizes may cause memory issues. Consider using smaller batches.');
}
}
}