@semiont/jobs 0.2.32-build.70 → 0.2.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.js +10 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -269,6 +269,8 @@ class CustomWorker extends JobWorker {
|
|
|
269
269
|
|
|
270
270
|
📝 **[Job Types Guide](./docs/JobTypes.md)** - All job type definitions and usage
|
|
271
271
|
|
|
272
|
+
🔷 **[Type System Guide](./docs/TYPES.md)** - Discriminated unions and type safety
|
|
273
|
+
|
|
272
274
|
⚙️ **[Configuration Guide](./docs/Configuration.md)** - Setup and options
|
|
273
275
|
|
|
274
276
|
## Key Features
|
package/dist/index.d.ts
CHANGED
|
@@ -108,7 +108,6 @@ interface GenerationProgress {
|
|
|
108
108
|
stage: 'fetching' | 'generating' | 'creating' | 'linking';
|
|
109
109
|
percentage: number;
|
|
110
110
|
message?: string;
|
|
111
|
-
resultResourceId?: ResourceId;
|
|
112
111
|
}
|
|
113
112
|
/**
|
|
114
113
|
* Generation job result
|
|
@@ -363,6 +362,11 @@ declare abstract class JobWorker {
|
|
|
363
362
|
* Sleep utility
|
|
364
363
|
*/
|
|
365
364
|
protected sleep(ms: number): Promise<void>;
|
|
365
|
+
/**
|
|
366
|
+
* Emit completion event (optional hook for subclasses)
|
|
367
|
+
* Override this to emit job-specific completion events (e.g., job.completed)
|
|
368
|
+
*/
|
|
369
|
+
protected emitCompletionEvent(_job: RunningJob<any, any>, _result: any): Promise<void>;
|
|
366
370
|
/**
|
|
367
371
|
* Get worker name (for logging)
|
|
368
372
|
*/
|
|
@@ -374,9 +378,10 @@ declare abstract class JobWorker {
|
|
|
374
378
|
/**
|
|
375
379
|
* Execute the job (job-specific logic)
|
|
376
380
|
* This is where the actual work happens
|
|
381
|
+
* Return the result object (or void for jobs without results)
|
|
377
382
|
* Throw an error to trigger retry logic
|
|
378
383
|
*/
|
|
379
|
-
protected abstract executeJob(job: AnyJob): Promise<
|
|
384
|
+
protected abstract executeJob(job: AnyJob): Promise<any>;
|
|
380
385
|
}
|
|
381
386
|
|
|
382
387
|
export { type AnyJob, type AssessmentDetectionJob, type AssessmentDetectionParams, type AssessmentDetectionProgress, type AssessmentDetectionResult, type CancelledJob, type CommentDetectionJob, type CommentDetectionParams, type CommentDetectionProgress, type CommentDetectionResult, type CompleteJob, type DetectionJob, type DetectionParams, type DetectionProgress, type DetectionResult, type FailedJob, type GenerationJob, type GenerationParams, type GenerationProgress, type GenerationResult, type HighlightDetectionJob, type HighlightDetectionParams, type HighlightDetectionProgress, type HighlightDetectionResult, type JobMetadata, type JobQueryFilters, JobQueue, type JobQueueConfig, type JobStatus, type JobType, JobWorker, type PendingJob, type RunningJob, type TagDetectionJob, type TagDetectionParams, type TagDetectionProgress, type TagDetectionResult, getJobQueue, initializeJobQueue, isCancelledJob, isCompleteJob, isFailedJob, isPendingJob, isRunningJob };
|
package/dist/index.js
CHANGED
|
@@ -283,15 +283,16 @@ var JobWorker = class {
|
|
|
283
283
|
};
|
|
284
284
|
await this.jobQueue.updateJob(runningJob, "pending");
|
|
285
285
|
console.log(`[${this.getWorkerName()}] \u{1F504} Processing job ${job.metadata.id} (type: ${job.metadata.type})`);
|
|
286
|
-
await this.executeJob(runningJob);
|
|
286
|
+
const result = await this.executeJob(runningJob);
|
|
287
|
+
await this.emitCompletionEvent(runningJob, result);
|
|
287
288
|
const completeJob = {
|
|
288
289
|
status: "complete",
|
|
289
290
|
metadata: runningJob.metadata,
|
|
290
291
|
params: runningJob.params,
|
|
291
292
|
startedAt: runningJob.startedAt,
|
|
292
293
|
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
293
|
-
result: {}
|
|
294
|
-
//
|
|
294
|
+
result: result ?? {}
|
|
295
|
+
// Use returned result or empty object
|
|
295
296
|
};
|
|
296
297
|
await this.jobQueue.updateJob(completeJob, "running");
|
|
297
298
|
console.log(`[${this.getWorkerName()}] \u2705 Job ${job.metadata.id} completed successfully`);
|
|
@@ -348,6 +349,12 @@ var JobWorker = class {
|
|
|
348
349
|
sleep(ms) {
|
|
349
350
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
350
351
|
}
|
|
352
|
+
/**
|
|
353
|
+
* Emit completion event (optional hook for subclasses)
|
|
354
|
+
* Override this to emit job-specific completion events (e.g., job.completed)
|
|
355
|
+
*/
|
|
356
|
+
async emitCompletionEvent(_job, _result) {
|
|
357
|
+
}
|
|
351
358
|
};
|
|
352
359
|
|
|
353
360
|
export { JobQueue, JobWorker, getJobQueue, initializeJobQueue };
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/job-queue.ts","../src/job-worker.ts"],"names":["fs","jobQueue"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Job Queue Manager\n *\n * Filesystem-based job queue with atomic operations.\n * Jobs are stored in directories by status for easy polling.\n */\n\nimport { promises as fs } from 'fs';\nimport * as path from 'path';\nimport type { AnyJob, JobStatus, JobQueryFilters, CancelledJob } from './types';\nimport type { JobId } from '@semiont/api-client';\n\nexport interface JobQueueConfig {\n dataDir: string;\n}\n\nexport class JobQueue {\n private jobsDir: string;\n\n constructor(config: JobQueueConfig) {\n this.jobsDir = path.join(config.dataDir, 'jobs');\n }\n\n /**\n * Initialize job queue directories\n */\n async initialize(): Promise<void> {\n const statuses: JobStatus[] = ['pending', 'running', 'complete', 'failed', 'cancelled'];\n\n for (const status of statuses) {\n const dir = path.join(this.jobsDir, status);\n await fs.mkdir(dir, { recursive: true });\n }\n\n console.log('[JobQueue] Initialized job directories');\n }\n\n /**\n * Create a new job\n */\n async createJob(job: AnyJob): Promise<void> {\n const jobPath = this.getJobPath(job.metadata.id, job.status);\n await fs.writeFile(jobPath, JSON.stringify(job, null, 2), 'utf-8');\n console.log(`[JobQueue] Created job ${job.metadata.id} with status ${job.status}`);\n }\n\n /**\n * Get a job by ID (searches all status directories)\n */\n async getJob(jobId: JobId): Promise<AnyJob | null> {\n const statuses: JobStatus[] = ['pending', 'running', 'complete', 'failed', 'cancelled'];\n\n for (const status of statuses) {\n const jobPath = this.getJobPath(jobId, status);\n try {\n const content = await fs.readFile(jobPath, 'utf-8');\n return JSON.parse(content) as AnyJob;\n } catch (error) {\n // File doesn't exist in this status directory, try next\n continue;\n }\n }\n\n return null;\n }\n\n /**\n * Update a job (atomic: delete old, write new)\n */\n async updateJob(job: AnyJob, oldStatus?: JobStatus): Promise<void> {\n // If oldStatus provided, delete from old location\n if (oldStatus && oldStatus !== job.status) {\n const oldPath = this.getJobPath(job.metadata.id, oldStatus);\n try {\n await fs.unlink(oldPath);\n } catch (error) {\n // Ignore if file doesn't exist\n }\n }\n\n // Write to new location\n const newPath = this.getJobPath(job.metadata.id, job.status);\n await fs.writeFile(newPath, JSON.stringify(job, null, 2), 'utf-8');\n\n if (oldStatus && oldStatus !== job.status) {\n console.log(`[JobQueue] Moved job ${job.metadata.id} from ${oldStatus} to ${job.status}`);\n } else {\n console.log(`[JobQueue] Updated job ${job.metadata.id} (status: ${job.status})`);\n }\n }\n\n /**\n * Poll for next pending job (FIFO)\n */\n async pollNextPendingJob(): Promise<AnyJob | null> {\n const pendingDir = path.join(this.jobsDir, 'pending');\n\n try {\n const files = await fs.readdir(pendingDir);\n\n if (files.length === 0) {\n return null;\n }\n\n // Sort by filename (job IDs have timestamps via nanoid)\n files.sort();\n\n const jobFile = files[0]!;\n const jobPath = path.join(pendingDir, jobFile);\n\n const content = await fs.readFile(jobPath, 'utf-8');\n return JSON.parse(content) as AnyJob;\n } catch (error) {\n console.error('[JobQueue] Error polling pending jobs:', error);\n return null;\n }\n }\n\n /**\n * List jobs with filters\n */\n async listJobs(filters: JobQueryFilters = {}): Promise<AnyJob[]> {\n const jobs: AnyJob[] = [];\n\n // Determine which status directories to scan\n const statuses: JobStatus[] = filters.status\n ? [filters.status]\n : ['pending', 'running', 'complete', 'failed', 'cancelled'];\n\n for (const status of statuses) {\n const statusDir = path.join(this.jobsDir, status);\n\n try {\n const files = await fs.readdir(statusDir);\n\n for (const file of files) {\n const jobPath = path.join(statusDir, file);\n const content = await fs.readFile(jobPath, 'utf-8');\n const job = JSON.parse(content) as AnyJob;\n\n // Apply filters\n if (filters.type && job.metadata.type !== filters.type) continue;\n if (filters.userId && job.metadata.userId !== filters.userId) continue;\n\n jobs.push(job);\n }\n } catch (error) {\n // Directory might not exist yet\n continue;\n }\n }\n\n // Sort by created descending (newest first)\n jobs.sort((a, b) => new Date(b.metadata.created).getTime() - new Date(a.metadata.created).getTime());\n\n // Apply pagination\n const offset = filters.offset || 0;\n const limit = filters.limit || 100;\n\n return jobs.slice(offset, offset + limit);\n }\n\n /**\n * Cancel a job\n */\n async cancelJob(jobId: JobId): Promise<boolean> {\n const job = await this.getJob(jobId);\n\n if (!job) {\n return false;\n }\n\n // Can only cancel pending or running jobs\n if (job.status !== 'pending' && job.status !== 'running') {\n return false;\n }\n\n const oldStatus = job.status;\n\n // Create cancelled job with proper structure\n const cancelledJob: CancelledJob<any> = {\n status: 'cancelled',\n metadata: job.metadata,\n params: job.status === 'pending' ? job.params : job.params,\n startedAt: job.status === 'running' ? job.startedAt : undefined,\n completedAt: new Date().toISOString(),\n };\n\n await this.updateJob(cancelledJob, oldStatus);\n return true;\n }\n\n /**\n * Clean up old completed/failed jobs (older than retention period)\n */\n async cleanupOldJobs(retentionHours: number = 24): Promise<number> {\n const cutoffTime = Date.now() - (retentionHours * 60 * 60 * 1000);\n let deletedCount = 0;\n\n const cleanupStatuses: JobStatus[] = ['complete', 'failed', 'cancelled'];\n\n for (const status of cleanupStatuses) {\n const statusDir = path.join(this.jobsDir, status);\n\n try {\n const files = await fs.readdir(statusDir);\n\n for (const file of files) {\n const jobPath = path.join(statusDir, file);\n const content = await fs.readFile(jobPath, 'utf-8');\n const job = JSON.parse(content) as AnyJob;\n\n if (job.status === 'complete' || job.status === 'failed' || job.status === 'cancelled') {\n const completedTime = new Date(job.completedAt).getTime();\n\n if (completedTime < cutoffTime) {\n await fs.unlink(jobPath);\n deletedCount++;\n }\n }\n }\n } catch (error) {\n console.error(`[JobQueue] Error cleaning up ${status} jobs:`, error);\n }\n }\n\n if (deletedCount > 0) {\n console.log(`[JobQueue] Cleaned up ${deletedCount} old jobs`);\n }\n\n return deletedCount;\n }\n\n /**\n * Get job file path\n */\n private getJobPath(jobId: JobId, status: JobStatus): string {\n return path.join(this.jobsDir, status, `${jobId}.json`);\n }\n\n /**\n * Get statistics about the queue\n */\n async getStats(): Promise<{\n pending: number;\n running: number;\n complete: number;\n failed: number;\n cancelled: number;\n }> {\n const stats = {\n pending: 0,\n running: 0,\n complete: 0,\n failed: 0,\n cancelled: 0\n };\n\n const statuses: JobStatus[] = ['pending', 'running', 'complete', 'failed', 'cancelled'];\n\n for (const status of statuses) {\n const statusDir = path.join(this.jobsDir, status);\n\n try {\n const files = await fs.readdir(statusDir);\n stats[status] = files.length;\n } catch (error) {\n // Directory might not exist yet\n stats[status] = 0;\n }\n }\n\n return stats;\n }\n}\n\n// Singleton instance\nlet jobQueue: JobQueue | null = null;\n\nexport function getJobQueue(): JobQueue {\n if (!jobQueue) {\n throw new Error('JobQueue not initialized. Call initializeJobQueue() first.');\n }\n return jobQueue;\n}\n\nexport async function initializeJobQueue(config: JobQueueConfig): Promise<JobQueue> {\n jobQueue = new JobQueue(config);\n await jobQueue.initialize();\n return jobQueue;\n}\n","/**\n * Job Worker Base Class\n *\n * Abstract worker that polls the job queue and processes jobs.\n * Subclasses implement specific job processing logic.\n */\n\nimport type { AnyJob, RunningJob, CompleteJob, FailedJob, PendingJob } from './types';\nimport type { JobQueue } from './job-queue';\n\nexport abstract class JobWorker {\n private running = false;\n private currentJob: AnyJob | null = null;\n private pollIntervalMs: number;\n private errorBackoffMs: number;\n protected jobQueue: JobQueue;\n\n constructor(\n jobQueue: JobQueue,\n pollIntervalMs: number = 1000,\n errorBackoffMs: number = 5000\n ) {\n this.jobQueue = jobQueue;\n this.pollIntervalMs = pollIntervalMs;\n this.errorBackoffMs = errorBackoffMs;\n }\n\n /**\n * Start the worker (polls queue in loop)\n */\n async start(): Promise<void> {\n this.running = true;\n console.log(`[${this.getWorkerName()}] Started`);\n\n while (this.running) {\n try {\n const job = await this.pollNextJob();\n\n if (job) {\n await this.processJob(job);\n } else {\n // No jobs available, wait before polling again\n await this.sleep(this.pollIntervalMs);\n }\n } catch (error) {\n console.error(`[${this.getWorkerName()}] Error in main loop:`, error);\n // Back off on error to avoid tight error loops\n await this.sleep(this.errorBackoffMs);\n }\n }\n\n console.log(`[${this.getWorkerName()}] Stopped`);\n }\n\n /**\n * Stop the worker (graceful shutdown)\n */\n async stop(): Promise<void> {\n console.log(`[${this.getWorkerName()}] Stopping...`);\n this.running = false;\n\n // Wait for current job to finish (with timeout)\n const timeout = 60000; // 60 seconds\n const startTime = Date.now();\n\n while (this.currentJob && (Date.now() - startTime) < timeout) {\n await this.sleep(100);\n }\n\n if (this.currentJob) {\n console.warn(`[${this.getWorkerName()}] Forced shutdown while processing job ${this.currentJob.metadata.id}`);\n }\n }\n\n /**\n * Poll for next job to process\n */\n private async pollNextJob(): Promise<AnyJob | null> {\n const job = await this.jobQueue.pollNextPendingJob();\n\n if (job && this.canProcessJob(job)) {\n return job;\n }\n\n return null;\n }\n\n /**\n * Process a job (handles state transitions and error handling)\n */\n private async processJob(job: AnyJob): Promise<void> {\n this.currentJob = job;\n\n try {\n // Only process pending jobs\n if (job.status !== 'pending') {\n console.warn(`[${this.getWorkerName()}] Skipping non-pending job ${job.metadata.id}`);\n return;\n }\n\n // Create running job\n const runningJob: RunningJob<any, any> = {\n status: 'running',\n metadata: job.metadata,\n params: job.params,\n startedAt: new Date().toISOString(),\n progress: {}, // Initialize with empty progress\n };\n\n await this.jobQueue.updateJob(runningJob, 'pending');\n\n console.log(`[${this.getWorkerName()}] 🔄 Processing job ${job.metadata.id} (type: ${job.metadata.type})`);\n\n // Execute job-specific logic (passing running job)\n await this.executeJob(runningJob);\n\n // Move to complete state\n const completeJob: CompleteJob<any, any> = {\n status: 'complete',\n metadata: runningJob.metadata,\n params: runningJob.params,\n startedAt: runningJob.startedAt,\n completedAt: new Date().toISOString(),\n result: {}, // Subclass should set this via updateJobProgress\n };\n\n await this.jobQueue.updateJob(completeJob, 'running');\n\n console.log(`[${this.getWorkerName()}] ✅ Job ${job.metadata.id} completed successfully`);\n\n } catch (error) {\n await this.handleJobFailure(job, error);\n } finally {\n this.currentJob = null;\n }\n }\n\n /**\n * Handle job failure (retry or move to failed)\n */\n protected async handleJobFailure(job: AnyJob, error: any): Promise<void> {\n const updatedMetadata = {\n ...job.metadata,\n retryCount: job.metadata.retryCount + 1,\n };\n\n if (updatedMetadata.retryCount < updatedMetadata.maxRetries) {\n console.log(`[${this.getWorkerName()}] Job ${job.metadata.id} failed, will retry (${updatedMetadata.retryCount}/${updatedMetadata.maxRetries})`);\n console.log(`[${this.getWorkerName()}] Error:`, error);\n\n // Move back to pending for retry\n const retryJob: PendingJob<any> = {\n status: 'pending',\n metadata: updatedMetadata,\n params: job.status === 'pending' ? job.params : job.params,\n };\n\n await this.jobQueue.updateJob(retryJob, job.status);\n\n } else {\n console.error(`[${this.getWorkerName()}] ❌ Job ${job.metadata.id} failed permanently after ${updatedMetadata.retryCount} retries`);\n console.error(`[${this.getWorkerName()}] Error:`, error);\n\n // Move to failed state\n const failedJob: FailedJob<any> = {\n status: 'failed',\n metadata: updatedMetadata,\n params: job.status === 'pending' ? job.params : job.params,\n startedAt: job.status === 'running' ? job.startedAt : undefined,\n completedAt: new Date().toISOString(),\n error: error instanceof Error ? error.message : String(error),\n };\n\n await this.jobQueue.updateJob(failedJob, job.status);\n }\n }\n\n /**\n * Update job progress (best-effort, doesn't throw)\n */\n protected async updateJobProgress(job: AnyJob): Promise<void> {\n try {\n await this.jobQueue.updateJob(job);\n } catch (error) {\n console.warn(`[${this.getWorkerName()}] Failed to update job progress:`, error);\n // Don't throw - progress updates are best-effort\n }\n }\n\n /**\n * Sleep utility\n */\n protected sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n\n // Abstract methods to be implemented by subclasses\n\n /**\n * Get worker name (for logging)\n */\n protected abstract getWorkerName(): string;\n\n /**\n * Check if this worker can process the given job\n */\n protected abstract canProcessJob(job: AnyJob): boolean;\n\n /**\n * Execute the job (job-specific logic)\n * This is where the actual work happens\n * Throw an error to trigger retry logic\n */\n protected abstract executeJob(job: AnyJob): Promise<void>;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/job-queue.ts","../src/job-worker.ts"],"names":["fs","jobQueue"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Job Queue Manager\n *\n * Filesystem-based job queue with atomic operations.\n * Jobs are stored in directories by status for easy polling.\n */\n\nimport { promises as fs } from 'fs';\nimport * as path from 'path';\nimport type { AnyJob, JobStatus, JobQueryFilters, CancelledJob } from './types';\nimport type { JobId } from '@semiont/api-client';\n\nexport interface JobQueueConfig {\n dataDir: string;\n}\n\nexport class JobQueue {\n private jobsDir: string;\n\n constructor(config: JobQueueConfig) {\n this.jobsDir = path.join(config.dataDir, 'jobs');\n }\n\n /**\n * Initialize job queue directories\n */\n async initialize(): Promise<void> {\n const statuses: JobStatus[] = ['pending', 'running', 'complete', 'failed', 'cancelled'];\n\n for (const status of statuses) {\n const dir = path.join(this.jobsDir, status);\n await fs.mkdir(dir, { recursive: true });\n }\n\n console.log('[JobQueue] Initialized job directories');\n }\n\n /**\n * Create a new job\n */\n async createJob(job: AnyJob): Promise<void> {\n const jobPath = this.getJobPath(job.metadata.id, job.status);\n await fs.writeFile(jobPath, JSON.stringify(job, null, 2), 'utf-8');\n console.log(`[JobQueue] Created job ${job.metadata.id} with status ${job.status}`);\n }\n\n /**\n * Get a job by ID (searches all status directories)\n */\n async getJob(jobId: JobId): Promise<AnyJob | null> {\n const statuses: JobStatus[] = ['pending', 'running', 'complete', 'failed', 'cancelled'];\n\n for (const status of statuses) {\n const jobPath = this.getJobPath(jobId, status);\n try {\n const content = await fs.readFile(jobPath, 'utf-8');\n return JSON.parse(content) as AnyJob;\n } catch (error) {\n // File doesn't exist in this status directory, try next\n continue;\n }\n }\n\n return null;\n }\n\n /**\n * Update a job (atomic: delete old, write new)\n */\n async updateJob(job: AnyJob, oldStatus?: JobStatus): Promise<void> {\n // If oldStatus provided, delete from old location\n if (oldStatus && oldStatus !== job.status) {\n const oldPath = this.getJobPath(job.metadata.id, oldStatus);\n try {\n await fs.unlink(oldPath);\n } catch (error) {\n // Ignore if file doesn't exist\n }\n }\n\n // Write to new location\n const newPath = this.getJobPath(job.metadata.id, job.status);\n await fs.writeFile(newPath, JSON.stringify(job, null, 2), 'utf-8');\n\n if (oldStatus && oldStatus !== job.status) {\n console.log(`[JobQueue] Moved job ${job.metadata.id} from ${oldStatus} to ${job.status}`);\n } else {\n console.log(`[JobQueue] Updated job ${job.metadata.id} (status: ${job.status})`);\n }\n }\n\n /**\n * Poll for next pending job (FIFO)\n */\n async pollNextPendingJob(): Promise<AnyJob | null> {\n const pendingDir = path.join(this.jobsDir, 'pending');\n\n try {\n const files = await fs.readdir(pendingDir);\n\n if (files.length === 0) {\n return null;\n }\n\n // Sort by filename (job IDs have timestamps via nanoid)\n files.sort();\n\n const jobFile = files[0]!;\n const jobPath = path.join(pendingDir, jobFile);\n\n const content = await fs.readFile(jobPath, 'utf-8');\n return JSON.parse(content) as AnyJob;\n } catch (error) {\n console.error('[JobQueue] Error polling pending jobs:', error);\n return null;\n }\n }\n\n /**\n * List jobs with filters\n */\n async listJobs(filters: JobQueryFilters = {}): Promise<AnyJob[]> {\n const jobs: AnyJob[] = [];\n\n // Determine which status directories to scan\n const statuses: JobStatus[] = filters.status\n ? [filters.status]\n : ['pending', 'running', 'complete', 'failed', 'cancelled'];\n\n for (const status of statuses) {\n const statusDir = path.join(this.jobsDir, status);\n\n try {\n const files = await fs.readdir(statusDir);\n\n for (const file of files) {\n const jobPath = path.join(statusDir, file);\n const content = await fs.readFile(jobPath, 'utf-8');\n const job = JSON.parse(content) as AnyJob;\n\n // Apply filters\n if (filters.type && job.metadata.type !== filters.type) continue;\n if (filters.userId && job.metadata.userId !== filters.userId) continue;\n\n jobs.push(job);\n }\n } catch (error) {\n // Directory might not exist yet\n continue;\n }\n }\n\n // Sort by created descending (newest first)\n jobs.sort((a, b) => new Date(b.metadata.created).getTime() - new Date(a.metadata.created).getTime());\n\n // Apply pagination\n const offset = filters.offset || 0;\n const limit = filters.limit || 100;\n\n return jobs.slice(offset, offset + limit);\n }\n\n /**\n * Cancel a job\n */\n async cancelJob(jobId: JobId): Promise<boolean> {\n const job = await this.getJob(jobId);\n\n if (!job) {\n return false;\n }\n\n // Can only cancel pending or running jobs\n if (job.status !== 'pending' && job.status !== 'running') {\n return false;\n }\n\n const oldStatus = job.status;\n\n // Create cancelled job with proper structure\n const cancelledJob: CancelledJob<any> = {\n status: 'cancelled',\n metadata: job.metadata,\n params: job.status === 'pending' ? job.params : job.params,\n startedAt: job.status === 'running' ? job.startedAt : undefined,\n completedAt: new Date().toISOString(),\n };\n\n await this.updateJob(cancelledJob, oldStatus);\n return true;\n }\n\n /**\n * Clean up old completed/failed jobs (older than retention period)\n */\n async cleanupOldJobs(retentionHours: number = 24): Promise<number> {\n const cutoffTime = Date.now() - (retentionHours * 60 * 60 * 1000);\n let deletedCount = 0;\n\n const cleanupStatuses: JobStatus[] = ['complete', 'failed', 'cancelled'];\n\n for (const status of cleanupStatuses) {\n const statusDir = path.join(this.jobsDir, status);\n\n try {\n const files = await fs.readdir(statusDir);\n\n for (const file of files) {\n const jobPath = path.join(statusDir, file);\n const content = await fs.readFile(jobPath, 'utf-8');\n const job = JSON.parse(content) as AnyJob;\n\n if (job.status === 'complete' || job.status === 'failed' || job.status === 'cancelled') {\n const completedTime = new Date(job.completedAt).getTime();\n\n if (completedTime < cutoffTime) {\n await fs.unlink(jobPath);\n deletedCount++;\n }\n }\n }\n } catch (error) {\n console.error(`[JobQueue] Error cleaning up ${status} jobs:`, error);\n }\n }\n\n if (deletedCount > 0) {\n console.log(`[JobQueue] Cleaned up ${deletedCount} old jobs`);\n }\n\n return deletedCount;\n }\n\n /**\n * Get job file path\n */\n private getJobPath(jobId: JobId, status: JobStatus): string {\n return path.join(this.jobsDir, status, `${jobId}.json`);\n }\n\n /**\n * Get statistics about the queue\n */\n async getStats(): Promise<{\n pending: number;\n running: number;\n complete: number;\n failed: number;\n cancelled: number;\n }> {\n const stats = {\n pending: 0,\n running: 0,\n complete: 0,\n failed: 0,\n cancelled: 0\n };\n\n const statuses: JobStatus[] = ['pending', 'running', 'complete', 'failed', 'cancelled'];\n\n for (const status of statuses) {\n const statusDir = path.join(this.jobsDir, status);\n\n try {\n const files = await fs.readdir(statusDir);\n stats[status] = files.length;\n } catch (error) {\n // Directory might not exist yet\n stats[status] = 0;\n }\n }\n\n return stats;\n }\n}\n\n// Singleton instance\nlet jobQueue: JobQueue | null = null;\n\nexport function getJobQueue(): JobQueue {\n if (!jobQueue) {\n throw new Error('JobQueue not initialized. Call initializeJobQueue() first.');\n }\n return jobQueue;\n}\n\nexport async function initializeJobQueue(config: JobQueueConfig): Promise<JobQueue> {\n jobQueue = new JobQueue(config);\n await jobQueue.initialize();\n return jobQueue;\n}\n","/**\n * Job Worker Base Class\n *\n * Abstract worker that polls the job queue and processes jobs.\n * Subclasses implement specific job processing logic.\n */\n\nimport type { AnyJob, RunningJob, CompleteJob, FailedJob, PendingJob } from './types';\nimport type { JobQueue } from './job-queue';\n\nexport abstract class JobWorker {\n private running = false;\n private currentJob: AnyJob | null = null;\n private pollIntervalMs: number;\n private errorBackoffMs: number;\n protected jobQueue: JobQueue;\n\n constructor(\n jobQueue: JobQueue,\n pollIntervalMs: number = 1000,\n errorBackoffMs: number = 5000\n ) {\n this.jobQueue = jobQueue;\n this.pollIntervalMs = pollIntervalMs;\n this.errorBackoffMs = errorBackoffMs;\n }\n\n /**\n * Start the worker (polls queue in loop)\n */\n async start(): Promise<void> {\n this.running = true;\n console.log(`[${this.getWorkerName()}] Started`);\n\n while (this.running) {\n try {\n const job = await this.pollNextJob();\n\n if (job) {\n await this.processJob(job);\n } else {\n // No jobs available, wait before polling again\n await this.sleep(this.pollIntervalMs);\n }\n } catch (error) {\n console.error(`[${this.getWorkerName()}] Error in main loop:`, error);\n // Back off on error to avoid tight error loops\n await this.sleep(this.errorBackoffMs);\n }\n }\n\n console.log(`[${this.getWorkerName()}] Stopped`);\n }\n\n /**\n * Stop the worker (graceful shutdown)\n */\n async stop(): Promise<void> {\n console.log(`[${this.getWorkerName()}] Stopping...`);\n this.running = false;\n\n // Wait for current job to finish (with timeout)\n const timeout = 60000; // 60 seconds\n const startTime = Date.now();\n\n while (this.currentJob && (Date.now() - startTime) < timeout) {\n await this.sleep(100);\n }\n\n if (this.currentJob) {\n console.warn(`[${this.getWorkerName()}] Forced shutdown while processing job ${this.currentJob.metadata.id}`);\n }\n }\n\n /**\n * Poll for next job to process\n */\n private async pollNextJob(): Promise<AnyJob | null> {\n const job = await this.jobQueue.pollNextPendingJob();\n\n if (job && this.canProcessJob(job)) {\n return job;\n }\n\n return null;\n }\n\n /**\n * Process a job (handles state transitions and error handling)\n */\n private async processJob(job: AnyJob): Promise<void> {\n this.currentJob = job;\n\n try {\n // Only process pending jobs\n if (job.status !== 'pending') {\n console.warn(`[${this.getWorkerName()}] Skipping non-pending job ${job.metadata.id}`);\n return;\n }\n\n // Create running job\n const runningJob: RunningJob<any, any> = {\n status: 'running',\n metadata: job.metadata,\n params: job.params,\n startedAt: new Date().toISOString(),\n progress: {}, // Initialize with empty progress\n };\n\n await this.jobQueue.updateJob(runningJob, 'pending');\n\n console.log(`[${this.getWorkerName()}] 🔄 Processing job ${job.metadata.id} (type: ${job.metadata.type})`);\n\n // Execute job-specific logic (passing running job) and get result\n const result = await this.executeJob(runningJob);\n\n // Allow subclasses to emit completion events with result data\n await this.emitCompletionEvent(runningJob, result);\n\n // Move to complete state with result\n const completeJob: CompleteJob<any, any> = {\n status: 'complete',\n metadata: runningJob.metadata,\n params: runningJob.params,\n startedAt: runningJob.startedAt,\n completedAt: new Date().toISOString(),\n result: result ?? {}, // Use returned result or empty object\n };\n\n await this.jobQueue.updateJob(completeJob, 'running');\n\n console.log(`[${this.getWorkerName()}] ✅ Job ${job.metadata.id} completed successfully`);\n\n } catch (error) {\n await this.handleJobFailure(job, error);\n } finally {\n this.currentJob = null;\n }\n }\n\n /**\n * Handle job failure (retry or move to failed)\n */\n protected async handleJobFailure(job: AnyJob, error: any): Promise<void> {\n const updatedMetadata = {\n ...job.metadata,\n retryCount: job.metadata.retryCount + 1,\n };\n\n if (updatedMetadata.retryCount < updatedMetadata.maxRetries) {\n console.log(`[${this.getWorkerName()}] Job ${job.metadata.id} failed, will retry (${updatedMetadata.retryCount}/${updatedMetadata.maxRetries})`);\n console.log(`[${this.getWorkerName()}] Error:`, error);\n\n // Move back to pending for retry\n const retryJob: PendingJob<any> = {\n status: 'pending',\n metadata: updatedMetadata,\n params: job.status === 'pending' ? job.params : job.params,\n };\n\n await this.jobQueue.updateJob(retryJob, job.status);\n\n } else {\n console.error(`[${this.getWorkerName()}] ❌ Job ${job.metadata.id} failed permanently after ${updatedMetadata.retryCount} retries`);\n console.error(`[${this.getWorkerName()}] Error:`, error);\n\n // Move to failed state\n const failedJob: FailedJob<any> = {\n status: 'failed',\n metadata: updatedMetadata,\n params: job.status === 'pending' ? job.params : job.params,\n startedAt: job.status === 'running' ? job.startedAt : undefined,\n completedAt: new Date().toISOString(),\n error: error instanceof Error ? error.message : String(error),\n };\n\n await this.jobQueue.updateJob(failedJob, job.status);\n }\n }\n\n /**\n * Update job progress (best-effort, doesn't throw)\n */\n protected async updateJobProgress(job: AnyJob): Promise<void> {\n try {\n await this.jobQueue.updateJob(job);\n } catch (error) {\n console.warn(`[${this.getWorkerName()}] Failed to update job progress:`, error);\n // Don't throw - progress updates are best-effort\n }\n }\n\n /**\n * Sleep utility\n */\n protected sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n\n /**\n * Emit completion event (optional hook for subclasses)\n * Override this to emit job-specific completion events (e.g., job.completed)\n */\n protected async emitCompletionEvent(_job: RunningJob<any, any>, _result: any): Promise<void> {\n // Default: do nothing\n // Subclasses can override to emit events\n }\n\n // Abstract methods to be implemented by subclasses\n\n /**\n * Get worker name (for logging)\n */\n protected abstract getWorkerName(): string;\n\n /**\n * Check if this worker can process the given job\n */\n protected abstract canProcessJob(job: AnyJob): boolean;\n\n /**\n * Execute the job (job-specific logic)\n * This is where the actual work happens\n * Return the result object (or void for jobs without results)\n * Throw an error to trigger retry logic\n */\n protected abstract executeJob(job: AnyJob): Promise<any>;\n}\n"]}
|