@bernierllc/workflow-service
v1.0.3
Published
Orchestrates content workflow management with approval and notification systems
Downloads
29
Readme
@bernierllc/workflow-service
Orchestrates content workflow management with approval and notification systems
Features
- 🔄 Complete Workflow Orchestration - Manage content lifecycle from creation to publication
- ✅ Multi-Approver Support - Configure workflows with multiple approval requirements
- 🔔 Notification Management - Automated notifications via email, webhooks, and more
- 🎯 Role-Based Permissions - Integration with auth-service for access control
- 📊 State Machine Integration - Uses content-state-machine for reliable state transitions
- 🚀 Batch Operations - Approve or reject multiple workflows at once
- 📈 Workflow Analytics - Track workflow performance and bottlenecks
- 🔌 NeverHub Integration - Event-driven architecture with other services
Installation
npm install @bernierllc/workflow-serviceUsage
Quick Start
import { WorkflowService } from '@bernierllc/workflow-service';
import { WorkflowPriority, NotificationChannel } from '@bernierllc/workflow-service';
// Initialize the workflow service
const workflowService = new WorkflowService({
storage: {
type: 'memory' // or 'database' with connection string
},
notifications: {
enabled: true,
defaultChannels: [NotificationChannel.EMAIL],
webhookTimeout: 30000,
retryAttempts: 3,
templates: {}
},
permissions: {
requireAuth: true, // Enable permission checking
adminRoles: ['admin'],
approverRoles: ['approver', 'editor', 'admin']
},
defaults: {
approvalRequired: true,
approverCount: 2, // Require 2 approvals
autoSchedule: false,
priority: WorkflowPriority.MEDIUM,
notificationEnabled: true
}
});
// Create a workflow
const workflow = await workflowService.createWorkflow(
'article-123', // contentId
{
name: 'Blog Post Approval',
description: 'Two-stage approval for blog posts',
approvalRequired: true,
approverCount: 2,
notificationConfig: {
enabled: true,
channels: [NotificationChannel.EMAIL],
recipients: {
onSubmit: ['[email protected]'],
onApproval: ['[email protected]'],
onPublish: ['[email protected]']
}
}
},
'author-456' // creator
);
// Submit for review
const submitResult = await workflowService.submitForReview(workflow.id, 'author-456');
console.log(`Submitted: ${submitResult.newState}`); // PENDING_REVIEW
// Assign approvers and request approval
await workflowService.assignApprover(workflow.id, 'editor-789');
await workflowService.assignApprover(workflow.id, 'manager-101');
const approvalRequest = await workflowService.requestApproval(
workflow.id,
['editor-789', 'manager-101']
);
// Handle approvals
const approval1 = await workflowService.approveContent(
workflow.id,
'editor-789',
'Content is well-written and factually accurate'
);
const approval2 = await workflowService.approveContent(
workflow.id,
'manager-101',
'Approved for publication'
);
console.log(`Final state: ${approval2.newState}`); // APPROVED
// Schedule for publication
await workflowService.scheduleContent(workflow.id, {
scheduledAt: new Date('2025-01-15T10:00:00Z'),
timezone: 'UTC',
autoPublish: true,
notifyBefore: 30 // minutes
});Workflow Configuration
Basic Workflow Config
interface WorkflowConfig {
name: string;
description?: string;
approvalRequired: boolean;
approverCount: number; // Required number of approvals
autoSchedule: boolean;
notificationConfig: NotificationConfig;
customRules?: WorkflowRule[];
}Notification Configuration
const notificationConfig = {
enabled: true,
channels: [
NotificationChannel.EMAIL,
NotificationChannel.WEBHOOK,
NotificationChannel.SLACK
],
recipients: {
onSubmit: ['[email protected]'],
onApproval: ['[email protected]', '[email protected]'],
onRejection: ['[email protected]'],
onSchedule: ['[email protected]'],
onPublish: ['[email protected]', '[email protected]'],
onDeadline: ['[email protected]']
},
escalation: {
enabled: true,
timeoutHours: 24,
escalateTo: ['[email protected]'],
maxEscalations: 2
}
};API Reference
Core Workflow Methods
createWorkflow(contentId, config?, creator?)
Creates a new workflow for content management.
const workflow = await workflowService.createWorkflow('article-123', {
name: 'Article Review Workflow',
approverCount: 2,
approvalRequired: true
}, 'author-456');updateWorkflow(workflowId, updates, updater?)
Updates an existing workflow configuration.
const updatedWorkflow = await workflowService.updateWorkflow('workflow-789', {
approverCount: 3,
autoSchedule: true
}, 'manager-101');deleteWorkflow(workflowId, deleter?)
Deletes a workflow and cleans up associated state.
const deleted = await workflowService.deleteWorkflow('workflow-789', 'admin-555');State Transition Methods
submitForReview(workflowId, submitter)
Submits content for review, transitioning from DRAFT to PENDING_REVIEW.
const result = await workflowService.submitForReview('workflow-789', 'author-456');
if (result.success) {
console.log(`New state: ${result.newState}`);
}approveContent(workflowId, approver, notes?)
Approves content. May require multiple approvals based on workflow configuration.
const result = await workflowService.approveContent(
'workflow-789',
'editor-123',
'Content meets our quality standards'
);rejectContent(workflowId, rejector, reason)
Rejects content and provides feedback for revision.
const result = await workflowService.rejectContent(
'workflow-789',
'editor-123',
'Please add more sources and fact-check the statistics'
);scheduleContent(workflowId, schedule)
Schedules approved content for publication.
const result = await workflowService.scheduleContent('workflow-789', {
scheduledAt: new Date('2025-01-15T14:00:00Z'),
timezone: 'America/New_York',
autoPublish: true,
notifyBefore: 15,
recurring: {
pattern: 'weekly',
interval: 1,
daysOfWeek: [1, 3, 5], // Mon, Wed, Fri
endDate: new Date('2025-12-31T23:59:59Z')
}
});publishContent(workflowId)
Publishes scheduled content.
const result = await workflowService.publishContent('workflow-789');Approval Management
assignApprover(workflowId, approver)
Assigns an approver to a workflow.
await workflowService.assignApprover('workflow-789', 'senior-editor-456');requestApproval(workflowId, approvers)
Creates an approval request for multiple approvers.
const request = await workflowService.requestApproval('workflow-789', [
'editor-123',
'legal-456',
'marketing-789'
]);getPendingApprovals(approver)
Gets all pending approval requests for a user.
const pending = await workflowService.getPendingApprovals('editor-123');
console.log(`${pending.length} approvals pending`);Workflow Queries
getWorkflow(workflowId)
Retrieves a workflow by ID.
const workflow = await workflowService.getWorkflow('workflow-789');
if (workflow) {
console.log(`Current state: ${workflow.currentState}`);
}getWorkflowByContent(contentId)
Finds a workflow by its associated content ID.
const workflow = await workflowService.getWorkflowByContent('article-123');listWorkflows(filters?)
Lists workflows with optional filtering.
// Get all workflows assigned to a specific approver
const workflows = await workflowService.listWorkflows({
assignedTo: ['editor-123'],
state: [ContentState.PENDING_REVIEW],
priority: [WorkflowPriority.HIGH, WorkflowPriority.URGENT],
dateRange: {
start: new Date('2025-01-01'),
end: new Date('2025-01-31')
},
limit: 20,
offset: 0
});getWorkflowHistory(workflowId)
Gets the complete history of events for a workflow.
const history = await workflowService.getWorkflowHistory('workflow-789');
history.forEach(event => {
console.log(`${event.type} by ${event.actor} at ${event.timestamp}`);
if (event.reason) console.log(` Reason: ${event.reason}`);
});Batch Operations
bulkApprove(workflowIds, approver)
Approves multiple workflows at once.
const result = await workflowService.bulkApprove([
'workflow-1',
'workflow-2',
'workflow-3'
], 'manager-456');
console.log(`Approved ${result.summary.successful} out of ${result.summary.total}`);bulkReject(workflowIds, rejector, reason)
Rejects multiple workflows with the same reason.
const result = await workflowService.bulkReject([
'workflow-1',
'workflow-2'
], 'editor-123', 'Content needs major revision');Notification Management
sendWorkflowNotification(workflowId, type, recipients)
Sends custom notifications for a workflow.
await workflowService.sendWorkflowNotification(
'workflow-789',
NotificationType.EMAIL,
['[email protected]']
);Integration with Content State Machine
The workflow service uses @bernierllc/content-state-machine for reliable state transitions:
import { ContentState } from '@bernierllc/content-state-machine';
// Valid state transitions are enforced by the state machine
const states = [
ContentState.DRAFT, // Initial state
ContentState.PENDING_REVIEW, // After submission
ContentState.APPROVED, // After approval
ContentState.SCHEDULED, // After scheduling
ContentState.PUBLISHED, // After publication
ContentState.REJECTED // If rejected at any point
];Integration with Auth Service
Enable permission-based access control:
const workflowService = new WorkflowService({
// ... other config
permissions: {
requireAuth: true,
defaultPermissions: ['workflow:view'],
adminRoles: ['admin', 'workflow-admin'],
approverRoles: ['approver', 'editor', 'manager', 'admin']
},
integrations: {
auth: {
enabled: true,
provider: 'auth-service'
}
}
});
// Permissions are automatically checked for all operations
const result = await workflowService.approveContent(
'workflow-789',
'user-without-permission', // Will fail if user lacks 'workflow:approve' permission
'Approval notes'
);Notification Channels
Email Notifications
const config = {
notifications: {
enabled: true,
defaultChannels: [NotificationChannel.EMAIL],
templates: {
[WorkflowEventType.SUBMITTED]: 'Content "{{workflowName}}" submitted for review',
[WorkflowEventType.APPROVED]: 'Content "{{workflowName}}" has been approved',
[WorkflowEventType.REJECTED]: 'Content "{{workflowName}}" needs revision: {{reason}}'
}
}
};Webhook Notifications
const config = {
notifications: {
enabled: true,
defaultChannels: [NotificationChannel.WEBHOOK],
webhookTimeout: 30000,
retryAttempts: 3
},
integrations: {
webhook: {
enabled: true,
endpoints: [
'https://your-app.com/webhooks/workflow-events',
'https://analytics.company.com/workflow-webhook'
],
signatures: true // Include HMAC signatures
}
}
};Slack Integration
// Custom notification via Slack
await workflowService.sendWorkflowNotification(
'workflow-789',
NotificationType.SLACK,
['#editorial-team', '#marketing-updates']
);NeverHub Event Integration
The service publishes events to NeverHub for real-time coordination:
// Events published automatically
const events = [
'workflow.created',
'workflow.state.changed',
'workflow.approval.requested',
'workflow.approval.completed',
'workflow.notification.sent',
'workflow.escalated',
'workflow.deadline.missed'
];
// Subscribe to events from other services
const workflowService = new WorkflowService({
// ... config
integrations: {
neverhub: {
enabled: true,
events: [
WorkflowEventType.CREATED,
WorkflowEventType.APPROVED,
WorkflowEventType.PUBLISHED
]
}
}
});Advanced Configuration
Custom Workflow Rules
const customRules = [
{
id: 'urgent-priority-rule',
name: 'Urgent Priority Fast Track',
condition: {
type: 'content',
field: 'priority',
operator: 'equals',
value: WorkflowPriority.URGENT
},
action: {
type: 'assign',
parameters: {
approvers: ['[email protected]'],
skipNormalApproval: true
}
},
enabled: true
}
];
const workflow = await workflowService.createWorkflow('urgent-content', {
name: 'Urgent Content Workflow',
customRules
});Escalation Configuration
const escalationConfig = {
enabled: true,
timeoutHours: 48, // Escalate after 48 hours
escalateTo: ['[email protected]', '[email protected]'],
maxEscalations: 3 // Maximum escalation levels
};
const workflow = await workflowService.createWorkflow('important-content', {
name: 'Important Content Workflow',
notificationConfig: {
enabled: true,
channels: [NotificationChannel.EMAIL, NotificationChannel.SLACK],
recipients: { /* ... */ },
escalation: escalationConfig
}
});Error Handling
try {
const result = await workflowService.approveContent(workflowId, approver);
if (!result.success) {
console.error(`Approval failed: ${result.error}`);
// Handle specific error cases
switch (result.error) {
case 'Workflow not found':
// Handle missing workflow
break;
case 'Invalid state transition':
// Handle invalid transition
break;
case 'Insufficient permissions':
// Handle permission error
break;
}
}
} catch (error) {
console.error('Unexpected error:', error);
}Performance Considerations
Batch Operations
// Instead of individual operations
for (const workflowId of workflowIds) {
await workflowService.approveContent(workflowId, approver);
}
// Use batch operations for better performance
const result = await workflowService.bulkApprove(workflowIds, approver);Filtering and Pagination
// Use filters to reduce data transfer
const recentWorkflows = await workflowService.listWorkflows({
dateRange: {
start: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), // Last week
end: new Date()
},
state: [ContentState.PENDING_REVIEW],
limit: 50,
offset: 0
});Integration Status
- Logger: integrated - Uses @bernierllc/logger for structured logging of workflow events and state transitions
- NeverHub: integrated - Publishes workflow lifecycle events (created, state.changed, approval.requested, approval.completed, notification.sent, escalated, deadline.missed) via @bernierllc/neverhub-adapter
- Docs-Suite: ready - Complete API documentation with TypeScript interfaces, markdown format
Dependencies
@bernierllc/content-state-machine- State transition management@bernierllc/auth-service- Authentication and authorization@bernierllc/webhook-sender- Webhook delivery@bernierllc/permission-checker- Permission validation@bernierllc/neverhub-adapter- Event bus integration@bernierllc/logger- Structured logginguuid- Unique identifier generation
See Also
- @bernierllc/content-state-machine - State transition engine
- @bernierllc/auth-service - Authentication and authorization
- @bernierllc/webhook-sender - Webhook delivery system
- @bernierllc/content-scheduler-suite - Complete content scheduling solution
License
Copyright (c) 2025 Bernier LLC. All rights reserved.
