@prmichaelsen/task-core
v1.0.3
Published
Core business logic for task execution system
Maintainers
Readme
@prmichaelsen/task-core
Core business logic for the task execution system. Provides Zod schemas, DTOs, services, and Firebase client for task management.
Features
- 🔒 Type-Safe Schemas - Zod schemas with TypeScript inference
- 🔄 DTO Transformers - Convert internal schemas to API responses
- 🔥 Firebase Integration - Firestore service layer and client wrapper
- 📦 Tree-Shakeable - Import only what you need via subpath exports
- ✅ Well-Tested - 93% test coverage with unit and E2E tests
Installation
npm install @prmichaelsen/task-coreQuick Start
import { TaskDatabaseService } from '@prmichaelsen/task-core/services'
import { toTaskApiResponse } from '@prmichaelsen/task-core/dto'
// Create a task
const task = await TaskDatabaseService.createTask(
'user-123',
'My Task',
'Task description'
)
// Transform to API response
const apiResponse = toTaskApiResponse(task)Package Exports
This package uses subpath exports for optimal tree-shaking:
@prmichaelsen/task-core/schemas- Zod schemas and types@prmichaelsen/task-core/dto- API response DTOs and transformers@prmichaelsen/task-core/services- Firestore service layer@prmichaelsen/task-core/client- Firebase client wrapper@prmichaelsen/task-core/constants- Collection path helpers@prmichaelsen/task-core/errors- Error classes and utilities
Usage
Schemas
Define and validate task data with Zod schemas:
import { TaskSchema, MilestoneSchema, TaskItemSchema } from '@prmichaelsen/task-core/schemas'
import type { Task, Milestone, TaskItem } from '@prmichaelsen/task-core/schemas'
// Validate task data
const task = TaskSchema.parse({
id: 'task-1',
user_id: 'user-123',
title: 'My Task',
description: 'Task description',
status: 'active',
created_at: '2026-02-18T00:00:00Z',
updated_at: '2026-02-18T00:00:00Z'
})
// TypeScript types are inferred from schemas
const myTask: Task = {
id: 'task-1',
user_id: 'user-123',
title: 'My Task',
// ... TypeScript will enforce the correct shape
}DTOs (Data Transfer Objects)
Transform internal schemas to API-friendly responses:
import { toTaskApiResponse, toTaskListApiResponse } from '@prmichaelsen/task-core/dto'
import type { TaskApiResponse } from '@prmichaelsen/task-core/dto'
// Transform single task
const task = await TaskDatabaseService.getTask('user-123', 'task-1')
const apiResponse: TaskApiResponse = toTaskApiResponse(task)
// Transform task list
const tasks = await TaskDatabaseService.listTasks('user-123')
const listResponse = toTaskListApiResponse(tasks, tasks.length, 1, 10)Services
Interact with Firestore using the service layer:
import { TaskDatabaseService } from '@prmichaelsen/task-core/services'
// Initialize (optional - uses default Firestore instance)
TaskDatabaseService.initialize()
// Create a task
const task = await TaskDatabaseService.createTask(
'user-123',
'My Task',
'Task description'
)
// Get a task
const task = await TaskDatabaseService.getTask('user-123', 'task-1')
// List tasks
const tasks = await TaskDatabaseService.listTasks('user-123', {
status: 'active',
limit: 10
})
// Update task
await TaskDatabaseService.updateTask('user-123', 'task-1', {
title: 'Updated Title',
status: 'completed'
})
// Delete task
await TaskDatabaseService.deleteTask('user-123', 'task-1')
// Add milestone
await TaskDatabaseService.addMilestone('user-123', 'task-1', {
id: 'milestone-1',
name: 'Phase 1',
status: 'active',
progress: 0
})
// Add task item
await TaskDatabaseService.addTaskItem('user-123', 'task-1', {
id: 'item-1',
name: 'Subtask 1',
status: 'pending'
})Firebase Client
Use the Firebase client wrapper for multi-tenant access:
import { FirebaseClient } from '@prmichaelsen/task-core/client'
// Create client for a specific user
const client = new FirebaseClient({
userId: 'user-123',
serviceAccountPath: './service-account.json'
})
// Connect to Firebase
await client.connect()
// Create a task
const task = await client.createTask('My Task', 'Description')
// List tasks
const tasks = await client.listTasks({ status: 'active' })
// Get a task
const task = await client.getTask('task-1')
// Update task
await client.updateTask('task-1', { status: 'completed' })
// Delete task
await client.deleteTask('task-1')
// Disconnect when done
await client.disconnect()Constants
Use collection path helpers for consistent Firestore paths:
import { getUserTasks, getUserTask, getUserTaskMessages } from '@prmichaelsen/task-core/constants'
// Get collection path for user's tasks
const tasksPath = getUserTasks('user-123')
// Returns: 'users/user-123/tasks'
// Get document path for specific task
const taskPath = getUserTask('user-123', 'task-1')
// Returns: 'users/user-123/tasks/task-1'
// Get collection path for task messages
const messagesPath = getUserTaskMessages('user-123', 'task-1')
// Returns: 'users/user-123/tasks/task-1/messages'Errors
Handle errors consistently across MCP and REST implementations:
import {
TaskNotFoundError,
TaskValidationError,
TaskAuthorizationError,
isTaskError,
toTaskError
} from '@prmichaelsen/task-core/errors'
try {
const task = await TaskDatabaseService.getTask('user-123', 'task-1')
if (!task) {
throw new TaskNotFoundError('task-1', 'user-123')
}
} catch (error) {
if (isTaskError(error)) {
console.error(`Error ${error.code}: ${error.message}`)
console.error(`Status: ${error.statusCode}`)
console.error(`Details:`, error.details)
} else {
// Convert unknown errors to TaskError
const taskError = toTaskError(error)
console.error(taskError.toJSON())
}
}Available Error Classes:
TaskNotFoundError- Task doesn't exist (404)TaskValidationError- Invalid task data (400)TaskAlreadyExistsError- Duplicate task (409)TaskStateError- Invalid state transition (409)MilestoneNotFoundError- Milestone doesn't exist (404)TaskItemNotFoundError- Task item doesn't exist (404)TaskMessageNotFoundError- Message doesn't exist (404)TaskAuthorizationError- Permission denied (403)TaskDatabaseError- Database operation failed (500)TaskConfigurationError- Invalid configuration (400)TaskLimitExceededError- Limit exceeded (429)TaskOperationTimeoutError- Operation timeout (408)FirebaseConnectionError- Firebase connection failed (503)InvalidInputError- Invalid input parameter (400)
Utilities:
isTaskError(error)- Type guard to check if error is a TaskErrortoTaskError(error)- Convert any error to TaskErrorTaskErrorCodes- Constants for all error codes
API Reference
Schemas
Task Schema
id: stringuser_id: stringtitle: stringdescription: string (optional)status: 'active' | 'completed' | 'archived'progress: TaskProgress (optional)config: TaskConfig (optional)metadata: TaskMetadata (optional)milestones: Milestone[] (optional)items: TaskItem[] (optional)created_at: string (ISO 8601)updated_at: string (ISO 8601)
Milestone Schema
id: stringname: stringdescription: string (optional)status: 'active' | 'completed'progress: number (0-100)estimated_hours: number (optional)completed_at: string (optional, ISO 8601)
TaskItem Schema
id: stringname: stringdescription: string (optional)status: 'pending' | 'in_progress' | 'completed'estimated_hours: number (optional)completed_at: string (optional, ISO 8601)notes: string (optional)
Service Methods
TaskDatabaseService
initialize(db?: Firestore): void- Initialize with custom Firestore instancecreateTask(userId, title, description?): Promise<Task>- Create a new taskgetTask(userId, taskId): Promise<Task | null>- Get task by IDlistTasks(userId, options?): Promise<Task[]>- List tasks with optional filtersupdateTask(userId, taskId, updates): Promise<void>- Update task fieldsdeleteTask(userId, taskId): Promise<void>- Delete a taskaddMilestone(userId, taskId, milestone): Promise<void>- Add milestone to taskupdateMilestone(userId, taskId, milestoneId, updates): Promise<void>- Update milestoneremoveMilestone(userId, taskId, milestoneId): Promise<void>- Remove milestoneaddTaskItem(userId, taskId, item): Promise<void>- Add item to taskupdateTaskItem(userId, taskId, itemId, updates): Promise<void>- Update task itemremoveTaskItem(userId, taskId, itemId): Promise<void>- Remove task itemaddMessage(userId, taskId, message): Promise<TaskMessage>- Add message to tasklistMessages(userId, taskId, options?): Promise<TaskMessage[]>- List task messages
DTO Transformers
toTaskApiResponse(task): TaskApiResponse- Transform Task to API responsetoTaskListApiResponse(tasks, total, page, pageSize): TaskListApiResponse- Transform task listtoTaskMessageApiResponse(message): TaskMessageApiResponse- Transform messagetoTaskMessageListApiResponse(messages, total, page, pageSize): TaskMessageListApiResponse- Transform message listtoMilestoneApiResponse(milestone): MilestoneApiResponse- Transform milestonetoTaskItemApiResponse(item): TaskItemApiResponse- Transform task itemtoTaskProgressApiResponse(progress): TaskProgressApiResponse- Transform progresstoTaskConfigApiResponse(config): TaskConfigApiResponse- Transform configtoTaskMetadataApiResponse(metadata): TaskMetadataApiResponse- Transform metadata
Testing
Run Unit Tests
npm testRun E2E Tests
E2E tests require the Firestore emulator:
# Start emulator
firebase emulators:start --only firestore
# In another terminal, run E2E tests
npm run test:e2eTest Coverage
npm test -- --coverageCurrent coverage: 93% (43/46 tests passing)
Development
Build
npm run buildGenerates:
- JavaScript bundles in
dist/ - TypeScript declarations (
.d.ts) - Source maps
Watch Mode
npm run devType Check
npm run typecheckProject Structure
task-core/
├── src/
│ ├── schemas/
│ │ └── task.ts # Zod schemas
│ ├── dto/
│ │ ├── task-api.dto.ts # DTO types
│ │ ├── transformers.ts # Transform functions
│ │ ├── transformers.spec.ts # Tests
│ │ └── index.ts # Exports
│ ├── services/
│ │ ├── task-database.service.ts # Firestore service
│ │ ├── task-database.service.spec.ts # Unit tests
│ │ └── task-database.service.e2e.ts # E2E tests
│ ├── constant/
│ │ └── collections.ts # Path helpers
│ └── client.ts # Firebase client
│ └── client.spec.ts # Tests
├── dist/ # Build output
├── package.json
├── tsconfig.json
├── jest.config.js
└── esbuild.build.jsDependencies
- firebase-admin (^13.6.1) - Firebase Admin SDK for Firestore
- zod (^4.3.6) - TypeScript-first schema validation
License
MIT
Contributing
Contributions are welcome! Please ensure:
- All tests pass (
npm test) - TypeScript compiles (
npm run typecheck) - Code follows existing patterns
- Add tests for new features
Support
For issues and questions:
- GitHub Issues: Create an issue
- Documentation: This README
Changelog
See CHANGELOG.md for version history and changes.
