JSPM

@bernierllc/workflow-service

1.4.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 32
  • Score
    100M100P100Q57951F
  • License MIT

Orchestrates content workflow management with approval and notification systems

Package Exports

  • @bernierllc/workflow-service
  • @bernierllc/workflow-service/dist/index.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@bernierllc/workflow-service) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

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-service

Usage

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: ['editor@company.com'],
        onApproval: ['author@company.com'],
        onPublish: ['marketing@company.com']
      }
    }
  },
  '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: ['editors@company.com'],
    onApproval: ['author@company.com', 'manager@company.com'],
    onRejection: ['author@company.com'],
    onSchedule: ['marketing@company.com'],
    onPublish: ['marketing@company.com', 'analytics@company.com'],
    onDeadline: ['manager@company.com']
  },
  escalation: {
    enabled: true,
    timeoutHours: 24,
    escalateTo: ['director@company.com'],
    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,
  ['stakeholder@company.com']
);

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: ['director@company.com'],
        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: ['director@company.com', 'ceo@company.com'],
  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 logging
  • uuid - Unique identifier generation

See Also

License

Copyright (c) 2025 Bernier LLC. All rights reserved.