trough 1.0.0 copy "trough: ^1.0.0" to clipboard
trough: ^1.0.0 copied to clipboard

A middleware pipeline library for Dart, inspired by similar libraries in the JavaScript ecosystem. Create composable function pipelines for processing data in sequence.

Trough #

Dart License: MIT

Trough is a middleware pipeline library for Dart, inspired by similar libraries in the JavaScript ecosystem. It allows you to create composable function pipelines for processing data in sequence.

Features #

  • πŸ”„ Middleware Pipeline: Create composable function chains
  • ⚑ Sync & Async Support: Support for synchronous return values and asynchronous Futures
  • πŸ›‘οΈ Error Handling: Built-in error propagation and handling mechanisms
  • πŸ”— Method Chaining: Fluent API design
  • πŸ“ Strong Typing: TypeScript-style type definitions
  • πŸ§ͺ Full Test Coverage: Comprehensive test suite included

Installation #

Add the dependency to your pubspec.yaml file:

dependencies:
  trough: ^1.0.0

Then run:

dart pub get

Quick Start #

Basic Usage #

import 'package:trough/trough.dart';

void main() {
  // Create a new pipeline
  final pipeline = trough();

  // Add middleware - multiply input by 2
  pipeline.use((List<dynamic> input, [Function? next]) {
    final value = input.isNotEmpty ? input[0] as int : 0;
    next?.call(null, [value * 2]);
    return null;
  });

  // Add another middleware - add 10
  pipeline.use((List<dynamic> input, [Function? next]) {
    final value = input.isNotEmpty ? input[0] as int : 0;
    next?.call(null, [value + 10]);
    return null;
  });

  // Run the pipeline
  pipeline.run([5], (Object? error, [List<dynamic>? output]) {
    if (error != null) {
      print('Error: $error');
    } else {
      print('Result: ${output?.first}'); // Output: Result: 20 (5 * 2 + 10)
    }
  });
}

Synchronous Middleware #

void main() {
  final pipeline = trough();
  
  // Synchronous middleware that returns a value directly
  pipeline.use((List<dynamic> input, [Function? next]) {
    final text = input.isNotEmpty ? input[0] as String : '';
    return '$text World!';
  });

  pipeline.run(['Hello'], (Object? error, [List<dynamic>? output]) {
    if (error != null) {
      print('Error: $error');
    } else {
      print('Result: ${output?.first}'); // Output: Result: Hello World!
    }
  });
}

Asynchronous Middleware #

import 'dart:async';

void main() async {
  final pipeline = trough();
  
  // Asynchronous middleware
  pipeline.use((List<dynamic> input, [Function? next]) {
    return Future.delayed(Duration(milliseconds: 100), () {
      final value = input.isNotEmpty ? input[0] as int : 0;
      return value * 3;
    });
  });

  pipeline.run([7], (Object? error, [List<dynamic>? output]) {
    if (error != null) {
      print('Error: $error');
    } else {
      print('Async result: ${output?.first}'); // Output: Async result: 21
    }
  });
}

Error Handling #

void main() {
  final pipeline = trough();
  
  // Middleware that throws an error
  pipeline.use((List<dynamic> input, [Function? next]) {
    if (input.isEmpty) {
      throw Exception('Input cannot be empty');
    }
    next?.call(null, [input[0]]);
    return null;
  });

  pipeline.run([], (Object? error, [List<dynamic>? output]) {
    if (error != null) {
      print('Caught error: $error'); // Output: Caught error: Exception: Input cannot be empty
    } else {
      print('Result: ${output?.first}');
    }
  });
}

API Documentation #

Type Definitions #

Callback

typedef Callback = void Function(Object? error, [List<dynamic>? output]);

Callback function called when the pipeline completes.

Middleware

typedef Middleware = dynamic Function(List<dynamic> input, [Function? next]);

Middleware function signature. Can accept input and an optional next callback function.

Main API #

trough()

Creates a new pipeline instance.

Returns: Pipeline - A new pipeline instance

Pipeline.use(Middleware middleware)

Adds middleware to the pipeline.

Parameters:

  • middleware: The middleware function to add

Returns: Pipeline - Returns the pipeline instance for method chaining

Pipeline.run(List<dynamic> values, Callback callback)

Runs all middleware in the pipeline.

Parameters:

  • values: Input values passed to the first middleware
  • callback: Callback function called when the pipeline completes

wrap(Middleware middleware, Callback callback)

Helper function to wrap middleware with a uniform interface.

Parameters:

  • middleware: The middleware function to wrap
  • callback: Callback function called on completion

Returns: Function - The wrapped function

Middleware Patterns #

1. Using next Callback #

pipeline.use((List<dynamic> input, [Function? next]) {
  // Process input
  final processed = processInput(input);
  
  // Call next to continue the pipeline
  next?.call(null, [processed]);
  return null;
});

2. Direct Return Value (Synchronous) #

pipeline.use((List<dynamic> input, [Function? next]) {
  final processed = processInput(input);
  return processed; // Return processed value directly
});

3. Return Future (Asynchronous) #

pipeline.use((List<dynamic> input, [Function? next]) {
  return processInputAsync(input); // Return Future
});

4. Error Handling #

pipeline.use((List<dynamic> input, [Function? next]) {
  try {
    final processed = processInput(input);
    next?.call(null, [processed]);
  } catch (error) {
    next?.call(error); // Pass error along
  }
  return null;
});

Real-World Examples #

Text Processing Pipeline #

final textProcessor = trough()
  ..use((List<dynamic> input, [Function? next]) {
    // Trim whitespace
    final text = (input[0] as String).trim();
    next?.call(null, [text]);
    return null;
  })
  ..use((List<dynamic> input, [Function? next]) {
    // Convert to lowercase
    final text = (input[0] as String).toLowerCase();
    next?.call(null, [text]);
    return null;
  })
  ..use((List<dynamic> input, [Function? next]) {
    // Replace special characters
    final text = (input[0] as String).replaceAll(RegExp(r'[^\w\s]'), '');
    next?.call(null, [text]);
    return null;
  });

textProcessor.run(['  Hello, World!  '], (error, output) {
  print(output?.first); // Output: "hello world"
});

Data Validation Pipeline #

final validator = trough()
  ..use((List<dynamic> input, [Function? next]) {
    // Check if input is empty
    if (input.isEmpty || input[0] == null) {
      throw Exception('Input cannot be empty');
    }
    next?.call(null, input);
    return null;
  })
  ..use((List<dynamic> input, [Function? next]) {
    // Check type
    if (input[0] is! String) {
      throw Exception('Input must be a string');
    }
    next?.call(null, input);
    return null;
  })
  ..use((List<dynamic> input, [Function? next]) {
    // Check length
    final text = input[0] as String;
    if (text.length < 3) {
      throw Exception('Input must be at least 3 characters long');
    }
    next?.call(null, ['Validation passed: $text']);
    return null;
  });

Development #

Run Tests #

dart test

Run Example #

dart run example/trough_example.dart

Code Formatting #

dart format .

Static Analysis #

dart analyze

License #

This project is licensed under the MIT License. See the LICENSE file for details.

Contributing #

Contributions are welcome! Please feel free to submit Pull Requests or create Issues.


Note: This library's design is inspired by similar libraries in the JavaScript ecosystem, particularly wooorm/trough.

0
likes
150
points
48
downloads

Publisher

unverified uploader

Weekly Downloads

A middleware pipeline library for Dart, inspired by similar libraries in the JavaScript ecosystem. Create composable function pipelines for processing data in sequence.

Repository (GitHub)
View/report issues

Topics

#middleware #pipeline #processing #functional #async

Documentation

Documentation
API reference

License

MIT (license)

More

Packages that depend on trough