npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@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-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: ['[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 logging
  • uuid - Unique identifier generation

See Also

License

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