Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ A lightweight SQLite-based workflow tracker for Node.js applications.
- Step tracking
- Identifier-based workflow lookup
- Workflow statistics
- **CLI tool for real-time statistics monitoring**
- SQLite-based storage
- TypeScript support
- Bulk operations support
Expand Down Expand Up @@ -95,6 +96,70 @@ const frequentSteps = liteflow.getMostFrequentSteps(5);
const stepDurations = liteflow.getAverageStepDuration();
```

## CLI Usage

Liteflow includes a powerful CLI tool for monitoring workflow statistics in real-time.

### Installation

After installing the package, the CLI is available as `liteflow`:

```bash
npm install -g liteflow
# or use npx
npx liteflow stats --db ./path/to/database.db
```

### Commands

#### `stats` - Display Workflow Statistics

Display general workflow statistics with various filtering and monitoring options:

```bash
# Basic usage - show statistics
liteflow stats --db ./liteflow.db

# Show verbose output with workflow details
liteflow stats --db ./liteflow.db --verbose

# Filter by workflow status
liteflow stats --db ./liteflow.db --status pending
liteflow stats --db ./liteflow.db --status completed
liteflow stats --db ./liteflow.db --status failed

# Filter by identifier
liteflow stats --db ./liteflow.db --key userId --value 1001

# Real-time monitoring (refreshes every 2 seconds)
liteflow stats --db ./liteflow.db --watch

# Real-time monitoring with custom interval (5 seconds)
liteflow stats --db ./liteflow.db --watch --interval 5

# Combine filters with real-time monitoring
liteflow stats --db ./liteflow.db --status pending --watch --verbose
```

#### CLI Options

- `-d, --db <path>` - Path to database file (default: `./liteflow.db`)
- `-w, --watch` - Enable real-time monitoring (refresh every 2 seconds)
- `-i, --interval <seconds>` - Refresh interval for watch mode in seconds (default: `2`)
- `-s, --status <status>` - Filter by status (`pending`, `completed`, `failed`)
- `-k, --key <key>` - Filter by identifier key
- `-v, --value <value>` - Filter by identifier value
- `--verbose` - Show detailed information including workflows and steps
- `-h, --help` - Display help information

### CLI Output

The CLI displays:

1. **General Statistics**: Total workflows, completed, pending, failed counts, and average steps per workflow
2. **Workflow List** (with `--verbose` or filters): Detailed list of workflows with status, start time, and duration
3. **Most Frequent Steps**: Top 5 most frequently executed steps across all workflows

## API Reference

### `Liteflow(dbPath: string)`
Expand Down
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
testMatch: ['**/__tests__/**/*.test.ts', '**/?(*.)+(spec|test).ts'],
moduleFileExtensions: ['ts', 'js', 'json', 'node'],
collectCoverage: true,
coverageDirectory: 'coverage',
Expand Down
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"description": "A lightweight SQLite-based workflow tracker",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"bin": {
"liteflow": "dist/cli.js"
},
"scripts": {
"build": "tsc",
"test": "jest",
Expand All @@ -20,6 +23,9 @@
"license": "MIT",
"dependencies": {
"better-sqlite3": "^8.6.0",
"chalk": "^5.6.2",
"cli-table3": "^0.6.5",
"commander": "^14.0.2",
"uuid": "^9.0.0"
},
"devDependencies": {
Expand Down
89 changes: 89 additions & 0 deletions src/__tests__/cli.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { Liteflow } from '../index'
import * as path from 'path'
import * as fs from 'fs'
import { execSync } from 'child_process'

describe('CLI', () => {
const testDbPath = path.join(__dirname, 'cli-test.db')
const cliPath = 'node dist/cli.js'

beforeEach(() => {
// Clean up any existing test database
if (fs.existsSync(testDbPath)) {
fs.unlinkSync(testDbPath)
}

// Create a fresh test database with sample data
const liteflow = new Liteflow(testDbPath)
liteflow.init()

const workflow1 = liteflow.startWorkflow('test-workflow-1', [
{ key: 'testId', value: '1' }
])
workflow1.addStep('step1', { data: 'test' })
workflow1.complete()

const workflow2 = liteflow.startWorkflow('test-workflow-2', [
{ key: 'testId', value: '2' }
])
workflow2.addStep('step1', { data: 'test' })
// Leave this one pending
})

afterEach(() => {
// Clean up test database
if (fs.existsSync(testDbPath)) {
fs.unlinkSync(testDbPath)
}
})

it('should display help information', () => {
const output = execSync(`${cliPath} --help`).toString()
expect(output).toContain('CLI tool for tracking workflow statistics')
expect(output).toContain('stats')
})

it('should display stats command help', () => {
const output = execSync(`${cliPath} stats --help`).toString()
expect(output).toContain('Display general workflow statistics')
expect(output).toContain('--db')
expect(output).toContain('--watch')
expect(output).toContain('--status')
})

it('should display basic statistics', () => {
const output = execSync(`${cliPath} stats --db ${testDbPath}`).toString()
expect(output).toContain('Liteflow Statistics Dashboard')
expect(output).toContain('Total Workflows')
expect(output).toContain('Completed')
expect(output).toContain('Pending')
})

it('should filter by status', () => {
const output = execSync(`${cliPath} stats --db ${testDbPath} --status pending`).toString()
expect(output).toContain('Liteflow Statistics Dashboard')
expect(output).toContain('Workflows (pending)')
})

it('should show verbose output', () => {
const output = execSync(`${cliPath} stats --db ${testDbPath} --verbose`).toString()
expect(output).toContain('Liteflow Statistics Dashboard')
expect(output).toContain('test-workflow')
expect(output).toContain('Showing')
})

it('should filter by identifier', () => {
const output = execSync(`${cliPath} stats --db ${testDbPath} --key testId --value 1`).toString()
expect(output).toContain('Liteflow Statistics Dashboard')
expect(output).toContain('Showing 1 of 1 workflows')
})

it('should handle non-existent database gracefully', () => {
try {
execSync(`${cliPath} stats --db /nonexistent/path.db`, { stdio: 'pipe' })
} catch (error: any) {
const output = error.stderr?.toString() || error.stdout?.toString() || ''
expect(output).toContain('Error')
}
})
})
70 changes: 70 additions & 0 deletions src/__tests__/create-test-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { Liteflow } from '../index'
import * as path from 'path'
import * as fs from 'fs'

// Create a test database with sample data
const dbPath = path.join(__dirname, '../../test-cli.db')

// Remove existing database if it exists
if (fs.existsSync(dbPath)) {
fs.unlinkSync(dbPath)
}

const liteflow = new Liteflow(dbPath)
liteflow.init()

console.log('Creating sample data...')

// Create some workflows with different statuses
const workflow1 = liteflow.startWorkflow('user-registration', [
{ key: 'userId', value: '1001' }
])
workflow1.addStep('validate-email', { email: 'user1@example.com' })
workflow1.addStep('create-account', { accountId: 'acc-1001' })
workflow1.addStep('send-welcome-email', { sent: true })
workflow1.complete()

const workflow2 = liteflow.startWorkflow('order-processing', [
{ key: 'orderId', value: '2001' }
])
workflow2.addStep('validate-order', { orderId: '2001' })
workflow2.addStep('check-inventory', { available: true })
workflow2.addStep('process-payment', { amount: 99.99 })
workflow2.addStep('ship-order', { trackingNumber: 'TRK-2001' })
workflow2.complete()

const workflow3 = liteflow.startWorkflow('user-registration', [
{ key: 'userId', value: '1002' }
])
workflow3.addStep('validate-email', { email: 'user2@example.com' })
workflow3.addStep('create-account', { accountId: 'acc-1002' })
// This workflow is still pending

const workflow4 = liteflow.startWorkflow('order-processing', [
{ key: 'orderId', value: '2002' }
])
workflow4.addStep('validate-order', { orderId: '2002' })
workflow4.addStep('check-inventory', { available: false })
workflow4.fail('Insufficient inventory')

const workflow5 = liteflow.startWorkflow('data-export', [
{ key: 'exportId', value: '3001' }
])
workflow5.addStep('fetch-data', { rowCount: 1000 })
workflow5.addStep('transform-data', { format: 'csv' })
workflow5.addStep('upload-to-s3', { bucket: 'exports' })
workflow5.complete()

const workflow6 = liteflow.startWorkflow('user-registration', [
{ key: 'userId', value: '1003' }
])
workflow6.addStep('validate-email', { email: 'user3@example.com' })
// Another pending workflow

console.log('Sample data created successfully!')
console.log(`Database location: ${dbPath}`)
console.log('\nYou can now test the CLI with:')
console.log(` node dist/cli.js stats --db ${dbPath}`)
console.log(` node dist/cli.js stats --db ${dbPath} --verbose`)
console.log(` node dist/cli.js stats --db ${dbPath} --status pending`)
console.log(` node dist/cli.js stats --db ${dbPath} --watch`)
Loading