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 🙏

© 2026 – Pkg Stats / Ryan Hefner

cephlo

v0.2.0

Published

A TypeScript workflow management tool for composable task execution

Downloads

29

Readme

Cephlo

A TypeScript workflow management tool for composable task execution. Cephlo helps you build and manage complex workflows by breaking them down into smaller, reusable tasks.

Features

  • 🧩 Composable task-based workflows
  • 🔄 Automatic retry handling
  • 🎯 Input and output validation, BYO framework
  • 📊 Pluggable logging and tracing
  • 🎭 Hooks for workflow events
  • ⚡ Parallel task execution
  • 🛡️ Type-safe task definitions

Installation

npm install cephlo
# or
yarn add cephlo
# or
pnpm add cephlo

Quick Start

import { defineTask, createWorkflow, runWorkflow } from 'cephlo';

// Define tasks
const fetchData = defineTask({
  name: 'fetchData',
  run: async (apiUrl: string): Promise<{ foo: string }> => {
    const response = await fetch(apiUrl);
    return response.json();
  },
  inputSchema: z.string(),
  outputSchema: z.object({
    foo: z.string(),
  }),
});

const processData = defineTask({
  name: 'processData',
  // return types from dependent tasks map to run arguments
  run: async ([fetchDataResponse]) => {
    // Process the data
    return { processed: true, foo: fetchDataResponse.foo };
  },
  deps: [fetchData] as const,
  // typebox example
  outputSchema: ajv.compile(
    Type.Object({
      processed: Type.Boolean(),
      foo: Type.String(),
    })
  ),
});

// Create a workflow
const workflow = createWorkflow({
  name: 'dataProcessing',
  tasks: [fetchData, processData],
});

// Run the workflow
const result = await runWorkflow(workflow, 'https://api.example.com/data');

if (result.status === 'completed') {
  console.log('Workflow completed successfully!');
  console.log('Processed data:', result.outputs.get('processData'));
} else {
  console.error('Workflow failed:', result.error);
}

Task Definition

Tasks are the building blocks of workflows. Each task can:

  • Accept inputs
  • Return outputs
  • Depend on other tasks
  • Define input and output schemas
    • Input schema only runs when the task has no deps
  • Handle errors
const task = defineTask({
  name: 'myTask',
  run: async (input: unknown, context?: WorkflowContext) => {
    // Task implementation
    return result;
  },
  deps: [
    /* dependent tasks */
  ],
  inputSchema: {
    /* Your favorite schema flavor */
  },
  outputSchema: {
    /* Your favorite schema flavor */
  },
});

Workflow Configuration

Configure the workflow engine with custom settings:

import { configureWorkflowEngine } from 'cephlo';

configureWorkflowEngine({
  defaultTaskTimeoutMs: 5000,
  retryPolicy: {
    maxAttempts: 3,
    backoffMs: 1000,
  },
  logger: {
    info: console.info,
    debug: console.debug,
    error: console.error,
    warn: console.warn,
  },
  validate: (data, schema) => {
    // zod
    const result = schema.safeParse(data);
    let valid = result.success;

    return {
      valid,
      data: result.data,
      error: result.error,
    };

    // typebox
    // ajv.compile called as schema definition
    const validateFn = schema;
    const valid = validateFn(data);
    return {
      valid,
      data,
      error: schema.errors,
    };
  },
});

Custom tracing

Tracing Integration

The workflow engine supports tracing through a simple adapter interface. This allows you to integrate with any tracing system that supports spans, such as OpenTelemetry or Datadog.

OpenTelemetry Integration

Here's how to integrate with OpenTelemetry:

import { configureWorkflowEngine, WorkflowSpan } from '../src/config';
import { trace, context, SpanStatusCode, Span } from '@opentelemetry/api';

// Create an OpenTelemetry span adapter
class OpenTelemetrySpan implements WorkflowSpan {
  constructor(public readonly span: Span) {}

  setAttribute(key: string, value: unknown) {
    this.span.setAttribute(key, value);
  }

  setAttributes(attributes: Record<string, unknown>) {
    Object.entries(attributes).forEach(([key, value]) => {
      this.span.setAttribute(key, value);
    });
  }

  addEvent(name: string, attributes?: Record<string, unknown>) {
    this.span.addEvent(name, attributes);
  }

  setStatus(status: { code: 'OK' | 'ERROR' | 'UNSET'; message?: string }) {
    this.span.setStatus({
      code:
        status.code === 'OK'
          ? SpanStatusCode.OK
          : status.code === 'ERROR'
          ? SpanStatusCode.ERROR
          : SpanStatusCode.UNSET,
      message: status.message,
    });
  }

  end() {
    this.span.end();
  }
}

// Create an OpenTelemetry tracer adapter
const otelTracer = {
  startSpan(
    name: string,
    options?: {
      attributes?: Record<string, unknown>;
      parent?: WorkflowSpan;
    }
  ): WorkflowSpan {
    const tracer = trace.getTracer('workflow-engine');

    // Create span options with parent context if provided
    const spanOptions: Parameters<ReturnType<typeof trace.getTracer>['startSpan']>[1] = {
      attributes: options?.attributes,
    };

    // If parent span is provided, create a new context with that span
    const ctx = options?.parent
      ? context.with(trace.setSpan(context.active(), (options.parent as OpenTelemetrySpan).span))
      : context.active();

    // Start the span in the appropriate context
    const span = tracer.startSpan(name, spanOptions, ctx);

    // Set the new span as active in the current context
    context.with(trace.setSpan(context.active(), span), () => {
      // Span is now active in this context
    });

    return new OpenTelemetrySpan(span);
  },

  getCurrentSpan(): WorkflowSpan | undefined {
    const span = trace.getActiveSpan();
    return span ? new OpenTelemetrySpan(span) : undefined;
  },
};

// Configure the workflow engine with OpenTelemetry
configureWorkflowEngine({
  tracer: otelTracer,
  // ... other config
});

Datadog Integration

Here's how to integrate with Datadog:

import { configureWorkflowEngine, WorkflowSpan } from '../src/config';
import tracer from 'dd-trace';

// Create a Datadog span adapter
class DatadogSpan implements WorkflowSpan {
  constructor(private span: ReturnType<typeof tracer.startSpan>) {}

  setAttribute(key: string, value: unknown) {
    this.span.setTag(key, value);
  }

  setAttributes(attributes: Record<string, unknown>) {
    Object.entries(attributes).forEach(([key, value]) => {
      this.span.setTag(key, value);
    });
  }

  addEvent(name: string, attributes?: Record<string, unknown>) {
    this.span.addTags({
      [`event.${name}`]: true,
      ...attributes,
    });
  }

  setStatus(status: { code: 'OK' | 'ERROR' | 'UNSET'; message?: string }) {
    if (status.code === 'ERROR') {
      this.span.setError(new Error(status.message));
    }
  }

  end() {
    this.span.finish();
  }
}

// Create a Datadog tracer adapter
const ddTracer = {
  startSpan(
    name: string,
    options?: {
      attributes?: Record<string, unknown>;
      parent?: WorkflowSpan;
    }
  ): WorkflowSpan {
    // If parent span is provided, use it as the parent
    const parentSpan = options?.parent ? (options.parent as DatadogSpan).span : undefined;

    // Start a new span
    const span = tracer.startSpan(name, {
      childOf: parentSpan,
      tags: options?.attributes,
    });

    return new DatadogSpan(span);
  },

  getCurrentSpan(): WorkflowSpan | undefined {
    const span = tracer.scope().active();
    return span ? new DatadogSpan(span) : undefined;
  },
};

// Configure the workflow engine with Datadog
configureWorkflowEngine({
  tracer: ddTracer,
  // ... other config
});

What Gets Traced

The workflow engine automatically traces:

  1. Workflow execution

    • Start and end of workflow
    • Input and output values
    • Success/failure status
  2. Task execution

    • Start and end of each task
    • Task name and dependencies
    • Input and output values
    • Success/failure status
  3. Error handling

    • Error messages and stack traces
    • Task failure context

Error Handling

Cephlo provides robust error handling with automatic retries:

const workflow = createWorkflow({
  name: 'errorHandling',
  tasks: [task1, task2],
  hooks: {
    onTaskError: (error, context) => {
      // Custom error handling
      // Return true to skip retry, false to allow retry
      return false;
    },
    onWorkflowError: (error, context) => {
      // Handle workflow-level errors
    },
  },
});

Hooks

Customize workflow behavior with hooks:

const workflow = createWorkflow({
  name: 'withHooks',
  tasks: [task1, task2],
  hooks: {
    onWorkflowStart: context => {
      console.log('Workflow started');
    },
    onWorkflowEnd: context => {
      console.log('Workflow ended');
    },
    onTaskStart: context => {
      console.log('Task started:', context.task.name);
    },
    onTaskEnd: (error, result, context) => {
      console.log('Task ended:', context.task.name);
    },
    onTaskSuccess: (result, context) => {
      console.log('Task succeeded:', context.task.name);
    },
    onTaskError: (error, context) => {
      console.error('Task failed:', context.task.name);
      return false; // Allow retry
    },
    onTaskRetry: context => {
      console.log('Retrying task:', context.task.name);
    },
  },
});

Development

# Install dependencies
pnpm install

# Build the project
pnpm run build

# Run tests
pnpm run test

# Run tests in watch mode
pnpm run test:watch

# Run tests with coverage
pnpm run test:coverage

# Lint the code
pnpm run lint

# Format the code
pnpm run format

License

MIT © Tj Simons