Skip to content

Commit 4d6815b

Browse files
Merge pull request #2 from litepacks/copilot/add-general-statistics-dashboard
Add CLI for real-time workflow statistics monitoring with filtering
2 parents 2786ec9 + 5f49649 commit 4d6815b

File tree

6 files changed

+392
-1
lines changed

6 files changed

+392
-1
lines changed

README.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ A lightweight SQLite-based workflow tracker for Node.js applications.
1010
- Step tracking
1111
- Identifier-based workflow lookup
1212
- Workflow statistics
13+
- **CLI tool for real-time statistics monitoring**
1314
- SQLite-based storage
1415
- TypeScript support
1516
- Bulk operations support
@@ -95,6 +96,70 @@ const frequentSteps = liteflow.getMostFrequentSteps(5);
9596
const stepDurations = liteflow.getAverageStepDuration();
9697
```
9798

99+
## CLI Usage
100+
101+
Liteflow includes a powerful CLI tool for monitoring workflow statistics in real-time.
102+
103+
### Installation
104+
105+
After installing the package, the CLI is available as `liteflow`:
106+
107+
```bash
108+
npm install -g liteflow
109+
# or use npx
110+
npx liteflow stats --db ./path/to/database.db
111+
```
112+
113+
### Commands
114+
115+
#### `stats` - Display Workflow Statistics
116+
117+
Display general workflow statistics with various filtering and monitoring options:
118+
119+
```bash
120+
# Basic usage - show statistics
121+
liteflow stats --db ./liteflow.db
122+
123+
# Show verbose output with workflow details
124+
liteflow stats --db ./liteflow.db --verbose
125+
126+
# Filter by workflow status
127+
liteflow stats --db ./liteflow.db --status pending
128+
liteflow stats --db ./liteflow.db --status completed
129+
liteflow stats --db ./liteflow.db --status failed
130+
131+
# Filter by identifier
132+
liteflow stats --db ./liteflow.db --key userId --value 1001
133+
134+
# Real-time monitoring (refreshes every 2 seconds)
135+
liteflow stats --db ./liteflow.db --watch
136+
137+
# Real-time monitoring with custom interval (5 seconds)
138+
liteflow stats --db ./liteflow.db --watch --interval 5
139+
140+
# Combine filters with real-time monitoring
141+
liteflow stats --db ./liteflow.db --status pending --watch --verbose
142+
```
143+
144+
#### CLI Options
145+
146+
- `-d, --db <path>` - Path to database file (default: `./liteflow.db`)
147+
- `-w, --watch` - Enable real-time monitoring (refresh every 2 seconds)
148+
- `-i, --interval <seconds>` - Refresh interval for watch mode in seconds (default: `2`)
149+
- `-s, --status <status>` - Filter by status (`pending`, `completed`, `failed`)
150+
- `-k, --key <key>` - Filter by identifier key
151+
- `-v, --value <value>` - Filter by identifier value
152+
- `--verbose` - Show detailed information including workflows and steps
153+
- `-h, --help` - Display help information
154+
155+
### CLI Output
156+
157+
The CLI displays:
158+
159+
1. **General Statistics**: Total workflows, completed, pending, failed counts, and average steps per workflow
160+
2. **Workflow List** (with `--verbose` or filters): Detailed list of workflows with status, start time, and duration
161+
3. **Most Frequent Steps**: Top 5 most frequently executed steps across all workflows
162+
98163
## API Reference
99164

100165
### `Liteflow(dbPath: string)`

jest.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module.exports = {
22
preset: 'ts-jest',
33
testEnvironment: 'node',
4-
testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
4+
testMatch: ['**/__tests__/**/*.test.ts', '**/?(*.)+(spec|test).ts'],
55
moduleFileExtensions: ['ts', 'js', 'json', 'node'],
66
collectCoverage: true,
77
coverageDirectory: 'coverage',

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
"description": "A lightweight SQLite-based workflow tracker",
55
"main": "dist/index.js",
66
"types": "dist/index.d.ts",
7+
"bin": {
8+
"liteflow": "dist/cli.js"
9+
},
710
"scripts": {
811
"build": "tsc",
912
"test": "jest",
@@ -20,6 +23,9 @@
2023
"license": "MIT",
2124
"dependencies": {
2225
"better-sqlite3": "^8.6.0",
26+
"chalk": "^5.6.2",
27+
"cli-table3": "^0.6.5",
28+
"commander": "^14.0.2",
2329
"uuid": "^9.0.0"
2430
},
2531
"devDependencies": {

src/__tests__/cli.test.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { Liteflow } from '../index'
2+
import * as path from 'path'
3+
import * as fs from 'fs'
4+
import { execSync } from 'child_process'
5+
6+
describe('CLI', () => {
7+
const testDbPath = path.join(__dirname, 'cli-test.db')
8+
const cliPath = 'node dist/cli.js'
9+
10+
beforeEach(() => {
11+
// Clean up any existing test database
12+
if (fs.existsSync(testDbPath)) {
13+
fs.unlinkSync(testDbPath)
14+
}
15+
16+
// Create a fresh test database with sample data
17+
const liteflow = new Liteflow(testDbPath)
18+
liteflow.init()
19+
20+
const workflow1 = liteflow.startWorkflow('test-workflow-1', [
21+
{ key: 'testId', value: '1' }
22+
])
23+
workflow1.addStep('step1', { data: 'test' })
24+
workflow1.complete()
25+
26+
const workflow2 = liteflow.startWorkflow('test-workflow-2', [
27+
{ key: 'testId', value: '2' }
28+
])
29+
workflow2.addStep('step1', { data: 'test' })
30+
// Leave this one pending
31+
})
32+
33+
afterEach(() => {
34+
// Clean up test database
35+
if (fs.existsSync(testDbPath)) {
36+
fs.unlinkSync(testDbPath)
37+
}
38+
})
39+
40+
it('should display help information', () => {
41+
const output = execSync(`${cliPath} --help`).toString()
42+
expect(output).toContain('CLI tool for tracking workflow statistics')
43+
expect(output).toContain('stats')
44+
})
45+
46+
it('should display stats command help', () => {
47+
const output = execSync(`${cliPath} stats --help`).toString()
48+
expect(output).toContain('Display general workflow statistics')
49+
expect(output).toContain('--db')
50+
expect(output).toContain('--watch')
51+
expect(output).toContain('--status')
52+
})
53+
54+
it('should display basic statistics', () => {
55+
const output = execSync(`${cliPath} stats --db ${testDbPath}`).toString()
56+
expect(output).toContain('Liteflow Statistics Dashboard')
57+
expect(output).toContain('Total Workflows')
58+
expect(output).toContain('Completed')
59+
expect(output).toContain('Pending')
60+
})
61+
62+
it('should filter by status', () => {
63+
const output = execSync(`${cliPath} stats --db ${testDbPath} --status pending`).toString()
64+
expect(output).toContain('Liteflow Statistics Dashboard')
65+
expect(output).toContain('Workflows (pending)')
66+
})
67+
68+
it('should show verbose output', () => {
69+
const output = execSync(`${cliPath} stats --db ${testDbPath} --verbose`).toString()
70+
expect(output).toContain('Liteflow Statistics Dashboard')
71+
expect(output).toContain('test-workflow')
72+
expect(output).toContain('Showing')
73+
})
74+
75+
it('should filter by identifier', () => {
76+
const output = execSync(`${cliPath} stats --db ${testDbPath} --key testId --value 1`).toString()
77+
expect(output).toContain('Liteflow Statistics Dashboard')
78+
expect(output).toContain('Showing 1 of 1 workflows')
79+
})
80+
81+
it('should handle non-existent database gracefully', () => {
82+
try {
83+
execSync(`${cliPath} stats --db /nonexistent/path.db`, { stdio: 'pipe' })
84+
} catch (error: any) {
85+
const output = error.stderr?.toString() || error.stdout?.toString() || ''
86+
expect(output).toContain('Error')
87+
}
88+
})
89+
})

src/__tests__/create-test-data.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { Liteflow } from '../index'
2+
import * as path from 'path'
3+
import * as fs from 'fs'
4+
5+
// Create a test database with sample data
6+
const dbPath = path.join(__dirname, '../../test-cli.db')
7+
8+
// Remove existing database if it exists
9+
if (fs.existsSync(dbPath)) {
10+
fs.unlinkSync(dbPath)
11+
}
12+
13+
const liteflow = new Liteflow(dbPath)
14+
liteflow.init()
15+
16+
console.log('Creating sample data...')
17+
18+
// Create some workflows with different statuses
19+
const workflow1 = liteflow.startWorkflow('user-registration', [
20+
{ key: 'userId', value: '1001' }
21+
])
22+
workflow1.addStep('validate-email', { email: 'user1@example.com' })
23+
workflow1.addStep('create-account', { accountId: 'acc-1001' })
24+
workflow1.addStep('send-welcome-email', { sent: true })
25+
workflow1.complete()
26+
27+
const workflow2 = liteflow.startWorkflow('order-processing', [
28+
{ key: 'orderId', value: '2001' }
29+
])
30+
workflow2.addStep('validate-order', { orderId: '2001' })
31+
workflow2.addStep('check-inventory', { available: true })
32+
workflow2.addStep('process-payment', { amount: 99.99 })
33+
workflow2.addStep('ship-order', { trackingNumber: 'TRK-2001' })
34+
workflow2.complete()
35+
36+
const workflow3 = liteflow.startWorkflow('user-registration', [
37+
{ key: 'userId', value: '1002' }
38+
])
39+
workflow3.addStep('validate-email', { email: 'user2@example.com' })
40+
workflow3.addStep('create-account', { accountId: 'acc-1002' })
41+
// This workflow is still pending
42+
43+
const workflow4 = liteflow.startWorkflow('order-processing', [
44+
{ key: 'orderId', value: '2002' }
45+
])
46+
workflow4.addStep('validate-order', { orderId: '2002' })
47+
workflow4.addStep('check-inventory', { available: false })
48+
workflow4.fail('Insufficient inventory')
49+
50+
const workflow5 = liteflow.startWorkflow('data-export', [
51+
{ key: 'exportId', value: '3001' }
52+
])
53+
workflow5.addStep('fetch-data', { rowCount: 1000 })
54+
workflow5.addStep('transform-data', { format: 'csv' })
55+
workflow5.addStep('upload-to-s3', { bucket: 'exports' })
56+
workflow5.complete()
57+
58+
const workflow6 = liteflow.startWorkflow('user-registration', [
59+
{ key: 'userId', value: '1003' }
60+
])
61+
workflow6.addStep('validate-email', { email: 'user3@example.com' })
62+
// Another pending workflow
63+
64+
console.log('Sample data created successfully!')
65+
console.log(`Database location: ${dbPath}`)
66+
console.log('\nYou can now test the CLI with:')
67+
console.log(` node dist/cli.js stats --db ${dbPath}`)
68+
console.log(` node dist/cli.js stats --db ${dbPath} --verbose`)
69+
console.log(` node dist/cli.js stats --db ${dbPath} --status pending`)
70+
console.log(` node dist/cli.js stats --db ${dbPath} --watch`)

0 commit comments

Comments
 (0)