npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@shelchin/task-manager

v0.1.0

Published

Tree-based task management with split storage, Merkle tree support, pause/resume, and Svelte 5 reactivity

Readme

@shelchin/task-manager

A tree-based task management library with Merkle verification, persistence, and Svelte 5 reactivity.

Features

  • Tree-based Structure - Organize tasks hierarchically with unlimited nesting depth
  • Pause/Resume/Cancel - Full lifecycle control at any time
  • Parallel & Serial Execution - Configurable concurrency per task
  • IndexedDB Persistence - Tasks survive browser refreshes
  • Merkle Tree Verification - Cryptographic integrity checking
  • Svelte 5 Reactive Store - Built-in reactive state management
  • Event-Driven Architecture - Real-time progress updates
  • Automatic Retry - Exponential backoff for failed tasks
  • TypeScript Support - Full type safety with generics

Installation

bun add @shelchin/task-manager
# or
npm install @shelchin/task-manager

Peer Dependencies

  • svelte ^5.0.0

Quick Start

Basic Usage

import { createTaskManager, createIndexedDBStorage } from '@shelchin/task-manager';

// Create a task manager with IndexedDB storage
const manager = createTaskManager({
	storage: createIndexedDBStorage('my-app-tasks')
});

// Create a task with subtasks
const task = await manager.create({
	name: 'Process Files',
	children: [
		{ name: 'File 1', data: { path: '/a.jpg' } },
		{ name: 'File 2', data: { path: '/b.png' } },
		{ name: 'File 3', data: { path: '/c.pdf' } }
	]
});

// Execute with a custom executor
await manager.execute(task.id, async (ctx) => {
	// Access task data
	const { path } = ctx.data;

	// Check for pause
	if (ctx.isPaused()) return;

	// Report progress (0-100)
	await ctx.progress(50);

	// Do the work
	await processFile(path);

	// Mark as complete
	await ctx.complete({ processed: true });
});

Using the Svelte Store

<script lang="ts">
	import { onMount } from 'svelte';
	import { createTaskStore, createIndexedDBStorage } from '@shelchin/task-manager';

	const store = createTaskStore({
		storage: createIndexedDBStorage('my-tasks')
	});

	onMount(() => {
		store.init();
	});
</script>

<!-- Reactive values auto-update -->
<p>Total: {store.allRoots.length}</p>
<p>Running: {store.runningRoots.length}</p>
<p>Completed: {store.completedRoots.length}</p>

{#each store.allRoots as task (task.id)}
	<div>
		{task.name} - {task.progress}% ({task.status})
	</div>
{/each}

API Reference

TaskManager

The core class for task management.

import { createTaskManager } from '@shelchin/task-manager';

const manager = createTaskManager(config);

Configuration

interface TaskManagerConfig {
	storage?: StorageAdapter; // Custom storage (default: IndexedDB)
	dbName?: string; // Database name (default: 'TaskManager')
	retry?: {
		maxAttempts?: number; // Default: 3
		baseDelayMs?: number; // Default: 1000
		maxDelayMs?: number; // Default: 10000
	};
	cleanupDays?: number; // Auto-cleanup after N days (default: 7)
}

Methods

| Method | Description | | -------------------------------- | ------------------------------- | | create(options) | Create a new task tree | | execute(taskId, executor) | Execute a task | | pause(taskId, reason?) | Pause a running task | | resume(taskId, executor) | Resume a paused task | | cancel(taskId) | Cancel a task | | delete(taskId) | Delete a task and all its nodes | | getRoot(taskId) | Get task root by ID | | getAllRoots() | Get all task roots | | getNodes(taskId) | Get all nodes for a task | | getLeaves(taskId) | Get leaf nodes only | | getChildren(taskId, parentId?) | Get children of a node | | getMerkleRoot(taskId) | Get the Merkle root hash | | getMerkleProof(taskId, leafId) | Generate a Merkle proof | | verifyProof(proof) | Verify a Merkle proof | | cleanup(olderThanDays?) | Remove old completed tasks | | close() | Close storage connection | | clear() | Clear all data | | on(event, handler) | Subscribe to events | | off(event, handler) | Unsubscribe from events |

Task Creation

interface CreateTaskOptions<T> {
	name: string; // Task name
	type?: string; // Task type identifier
	concurrency?: number; // Parallel execution (default: 1 = serial)
	metadata?: Record<string, unknown>;
	children?: CreateNodeOptions<T>[];
}

interface CreateNodeOptions<T> {
	name: string;
	mode?: 'progressive' | 'atomic'; // Execution mode (default: 'progressive')
	data?: T; // Execution data
	executor?: string; // Executor name (for registry)
	maxAttempts?: number; // Retry attempts
	children?: CreateNodeOptions<T>[];
}

Task Modes

| Mode | Description | Use Case | | ------------- | ----------------------------------------- | ----------------------------- | | progressive | Shows progress 0-100% during execution | File uploads, data processing | | atomic | No progress, just waiting → complete/fail | API calls, simple operations |

Execution Context

Executors receive a context object:

interface ExecutionContext<T> {
	node: TaskNode<T>; // Current node being executed
	data: T; // Node's execution data

	isPaused(): boolean; // Check if task is paused
	signal: AbortSignal; // Abort signal for cancellation

	progress(percent: number): Promise<void>; // Report progress (0-100)
	complete(result?: unknown): Promise<void>; // Mark as completed
	fail(error: string): Promise<void>; // Mark as failed
	pauseTask(reason?: string): Promise<void>; // Pause entire task
}

Executor Registry

For tasks with different executor types:

import type { ExecutorRegistry } from '@shelchin/task-manager';

const executors: ExecutorRegistry<MyData> = {
	uploadFile: async (ctx) => {
		await uploadFile(ctx.data.file);
		await ctx.complete();
	},
	processData: async (ctx) => {
		const result = await process(ctx.data);
		await ctx.complete(result);
	}
};

// Create task with executor references
const task = await manager.create({
	name: 'Mixed Tasks',
	children: [
		{ name: 'Upload', executor: 'uploadFile', data: { file: 'a.jpg' } },
		{ name: 'Process', executor: 'processData', data: { id: 123 } }
	]
});

// Execute with registry
await manager.execute(task.id, executors);

Parallel Execution

// Serial execution (default)
const task = await manager.create({
	name: 'Serial Task',
	concurrency: 1, // Execute one at a time
	children: [
		/* ... */
	]
});

// Parallel execution
const task = await manager.create({
	name: 'Parallel Task',
	concurrency: 5, // Execute 5 at a time
	children: [
		/* ... */
	]
});

// Execute all in parallel
const task = await manager.create({
	name: 'All Parallel',
	concurrency: Infinity,
	children: [
		/* ... */
	]
});

Events

// Subscribe to events
manager.on('start', (event, { root, node }) => {
	console.log(`Task ${node.name} started`);
});

manager.on('progress', (event, { root, node }) => {
	console.log(`Task ${node.name}: ${node.progress}%`);
});

manager.on('complete', (event, { root, node }) => {
	console.log(`Task ${node.name} completed`);
});

// Available events: 'start' | 'progress' | 'complete' | 'fail' | 'pause' | 'resume' | 'cancel'

Merkle Tree Verification

Every task has a Merkle root computed from its leaf nodes, enabling cryptographic verification.

// Get Merkle root
const merkleRoot = await manager.getMerkleRoot(task.id);

// Generate proof for a specific leaf
const leaves = await manager.getLeaves(task.id);
const proof = await manager.getMerkleProof(task.id, leaves[0].id);

// Verify the proof
const isValid = await manager.verifyProof(proof);
console.log('Proof valid:', isValid);

You can also use the low-level Merkle utilities:

import {
	computeLeafHash,
	buildMerkleRoot,
	generateMerkleProof,
	verifyMerkleProof,
	getMerkleTreeDepth
} from '@shelchin/task-manager';

TaskStore (Svelte 5)

A reactive store wrapper around TaskManager.

import { createTaskStore } from '@shelchin/task-manager';

const store = createTaskStore(config);

// Initialize (load from storage)
await store.init();

// Reactive derived values
store.allRoots;        // TaskRoot[]
store.pendingRoots;    // TaskRoot[]
store.runningRoots;    // TaskRoot[]
store.pausedRoots;     // TaskRoot[]
store.completedRoots;  // TaskRoot[]
store.failedRoots;     // TaskRoot[]
store.recoverableRoots; // Tasks that can be resumed
store.isLoading;       // boolean
store.isInitialized;   // boolean

// Aggregated stats
store.stats; // { total, completed, failed, pending, completionRate }

// All TaskManager methods are available
await store.create({ name: 'Task', children: [...] });
await store.execute(taskId, executor);
await store.pause(taskId);
await store.resume(taskId, executor);
await store.delete(taskId);
await store.clearCompleted();
await store.clearFailed();
await store.refreshAll();

Storage Adapters

IndexedDB (Default)

import { createIndexedDBStorage } from '@shelchin/task-manager';

const storage = createIndexedDBStorage('my-database');
const manager = createTaskManager({ storage });

Memory (Testing)

import { createMemoryStorage } from '@shelchin/task-manager';

const storage = createMemoryStorage();
const manager = createTaskManager({ storage });

Custom Storage

Implement the StorageAdapter interface:

interface StorageAdapter {
	// Root operations
	saveRoot(root: TaskRoot): Promise<void>;
	getRoot(id: string): Promise<TaskRoot | null>;
	getAllRoots(): Promise<TaskRoot[]>;
	deleteRoot(id: string): Promise<void>;

	// Node operations
	saveNode(node: TaskNode): Promise<void>;
	saveNodes(nodes: TaskNode[]): Promise<void>;
	getNode(id: string): Promise<TaskNode | null>;
	getNodesByRoot(rootId: string): Promise<TaskNode[]>;
	getChildren(parentId: string | null, rootId: string): Promise<TaskNode[]>;
	getLeaves(rootId: string): Promise<TaskNode[]>;
	deleteNodesByRoot(rootId: string): Promise<void>;

	// Batch operations
	updateNodeStatus(id: string, status: TaskStatus, updates?: Partial<TaskNode>): Promise<void>;

	// Lifecycle
	close(): Promise<void>;
	clear(): Promise<void>;
}

Type Definitions

TaskRoot

interface TaskRoot {
	id: string;
	name: string;
	type: string;
	status: TaskStatus;
	progress: number; // 0-100
	concurrency: number;
	stats: {
		total: number; // Total leaf nodes
		completed: number;
		failed: number;
	};
	merkleRoot: string | null;
	createdAt: number;
	updatedAt: number;
	startedAt?: number;
	completedAt?: number;
	metadata?: Record<string, unknown>;
}

TaskNode

interface TaskNode<T = unknown> {
	id: string;
	rootId: string;
	parentId: string | null;
	name: string;
	status: TaskStatus;
	progress: number;
	mode: TaskMode;
	depth: number;
	index: number;
	isLeaf: boolean;
	childCount: number;
	hash: string; // Merkle hash
	data?: T;
	executor?: string;
	result?: unknown;
	error?: string;
	attempts: number;
	maxAttempts: number;
	createdAt: number;
	updatedAt: number;
	startedAt?: number;
	completedAt?: number;
}

TaskStatus

type TaskStatus = 'pending' | 'running' | 'paused' | 'completed' | 'failed' | 'cancelled';

Task Status Lifecycle

pending → running → completed
                  → failed (auto-retry until maxAttempts)
                  → paused → running (resume)
                  → cancelled

Architecture

Storage Design

The library uses a split storage model for efficiency:

  • TaskRoot - Lightweight metadata stored separately for fast list queries
  • TaskNode - Full node data stored by root ID for batch loading

Data Flow

Create Task → Build Nodes → Compute Merkle Root → Save to Storage
    ↓
Execute → Load Nodes → Run Executor → Update Progress → Emit Events
    ↓
Complete/Fail → Update Stats → Update Merkle Root → Save

Development

Project Structure

packages/task-manager/
├── src/
│   ├── index.ts           # Public API exports
│   ├── types.ts           # Type definitions
│   ├── task-manager.ts    # Core TaskManager class
│   ├── merkle.ts          # Merkle tree utilities
│   ├── storage/
│   │   ├── indexeddb.ts   # IndexedDB adapter
│   │   └── memory.ts      # Memory adapter (testing)
│   └── stores/
│       └── task-store.svelte.ts  # Svelte 5 reactive store
├── README.md
├── package.json
└── vitest.config.ts

Testing

cd packages/task-manager

# Run all tests
bun run test

# Run with coverage
bun run test:coverage

# Run in watch mode
bun run test -- --watch

Building

# Build the package
bun run build

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Run tests: bun run test
  5. Submit a pull request

License

MIT