Package Exports
- catsa-janga
Readme
catsa-janga
A utility library for automatically saving and restoring progress in long-running Bun applications.
Features
- 💾 Automatically saves progress data to a file
- 🔄 Restores progress data when restarting a process
- 🛑 Handles process termination signals (SIGINT, SIGTERM)
- ⚠️ Captures unhandled exceptions and promise rejections
- 🔍 Lightweight with no external dependencies
- 📝 TypeScript support
Installation
# Using Bun
bun add catsa-janga
# Using npm
npm install catsa-jangaUsage
Basic Example - Using createWithRestore
The recommended way to use CatsaJanga is with the createWithRestore method:
import { CatsaJanga } from 'catsa-janga';
// Define your data structure
type MyData = {
items: string[];
processedCount: number;
timestamp: Date;
};
// Create and restore in one step
const { saver, data } = await CatsaJanga.createWithRestore<MyData>({
getData: () => ({
...data,
timestamp: new Date(), // Update timestamp on save
}),
logger: console, // Use console as logger
outputFile: 'progress.json',
initialData: {
items: [],
processedCount: 0,
timestamp: new Date(),
},
});
// Your long-running process
async function processItems() {
for (let i = 0; i < 1000; i++) {
// Simulate work
await new Promise((resolve) => setTimeout(resolve, 100));
// Update data
data.items.push(`Item ${i}`);
data.processedCount++;
// Optionally save progress periodically
if (i % 100 === 0) {
await saver.saveProgress();
}
}
// Save final progress
await saver.saveProgress();
}
// Start processing
await processItems();Alternative Approach - Using Object.assign
If you prefer to have more control over initialization, you can use the traditional method:
import { CatsaJanga } from 'catsa-janga';
// Define your data structure
type MyData = {
items: string[];
processedCount: number;
timestamp: Date;
};
// Create initial data
const data: MyData = {
items: [],
processedCount: 0,
timestamp: new Date(),
};
// Create a progress saver
const saver = new CatsaJanga<MyData>({
getData: () => ({
...data,
timestamp: new Date(), // Update timestamp on save
}),
logger: console, // Use console as logger
outputFile: 'progress.json',
});
// Restore previous progress if available
const restored = await saver.restore();
Object.assign(data, restored);
// Your long-running process
async function processItems() {
for (let i = 0; i < 1000; i++) {
// Simulate work
await new Promise((resolve) => setTimeout(resolve, 100));
// Update data
data.items.push(`Item ${i}`);
data.processedCount++;
// Optionally save progress periodically
if (i % 100 === 0) {
await saver.saveProgress();
}
}
// Save final progress
await saver.saveProgress();
}
// Start processing
await processItems();Real-world example
Here's how you might use it in a scraping or OCR application:
import { CatsaJanga } from 'catsa-janga';
// Define your data structure
type OCRResult = {
pages: Array<{ page: number; text: string }>;
timestamp: Date;
};
// Create initial data
const initialData: OCRResult = {
pages: [],
timestamp: new Date(),
};
// Create and restore in one step
const { saver, data } = await CatsaJanga.createWithRestore<OCRResult>({
getData: () => ({
...data,
pages: data.pages.toSorted((a, b) => a.page - b.page),
timestamp: new Date(),
}),
logger: console,
outputFile: 'ocr-progress.json',
initialData,
});
// Process pages (if we crash, we'll resume where we left off)
for (const file of files) {
if (data.pages.some((p) => p.page === file.pageNumber)) {
console.log(`Skipping already processed page ${file.pageNumber}`);
continue;
}
const result = await processOCR(file);
data.pages.push({ page: file.pageNumber, text: result });
// Save periodically
if (data.pages.length % 10 === 0) {
await saver.saveProgress();
}
}
// Final save
await saver.saveProgress();API
CatsaJanga
The main class for saving and restoring progress.
Constructor
constructor(options: CatsaJangaOptions<T>)Options:
getData: Function that returns the current data to savelogger: Logger object with at leastinfoanderrormethods (compatible with console, Pino, etc.)outputFile: File path where progress will be savedinitialData(optional): Initial data to use if no saved data exists
Methods
restore
async restore(): Promise<T | undefined>Checks for an existing progress file and restores data if present. Returns the restored data, the initialData (if provided and no saved data exists), or undefined.
saveProgress
async saveProgress(): Promise<void>Saves the current progress to the output file.
createWithRestore (static)
static async createWithRestore<T>(options: CatsaJangaOptions<T>): Promise<{ saver: CatsaJanga<T>, data: T }>Creates a CatsaJanga instance and immediately restores data if available. Returns both the saver instance and the restored or initial data.
Interfaces
Logger
interface Logger {
error: (message: string, ...args: any[]) => void;
info: (message: string, ...args: any[]) => void;
warn?: (message: string, ...args: any[]) => void;
}Compatible with console, Pino loggers, and other similar logging libraries.
CatsaJangaOptions
interface CatsaJangaOptions<T> {
getData: () => T;
logger: Logger;
outputFile: string;
initialData?: T;
}Error Handling
catsa-janga automatically registers handlers for:
SIGINT(Ctrl+C)SIGTERM(process termination)uncaughtException(unhandled errors)unhandledRejection(unhandled promise rejections)
When any of these events occur, the progress will be saved before the process exits.
License
MIT