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 🙏

© 2024 – Pkg Stats / Ryan Hefner

wagner-core

v0.2.0

Published

Dependency injector and DI-based async framework for writing modular, durable server code.

Downloads

2,228

Readme

wagner-core

Build Status Coverage Status

Wagner is primarily designed to be a more elegant take on orchestrator, hence the name. If you've used orchestrator for web apps and found it cumbersome, Wagner is for you.

API

As a dependency injector

Wagner includes a basic dependency injector that provides an API similar to AngularJS 1.x's dependency injector.

It allows you to execute async tasks based on parameter names

You register 'services' with Wagner using the factory() function. Services have a unique name - any function you pass through factory() or invoke() can list services in its parameter list.

    
    wagner.factory('bacon', function() {
      return 'bacon';
    });

    wagner.factory('breakfast', function(bacon) {
      return bacon + ' and eggs';
    });

    var result = wagner.invoke(function(breakfast) {
      assert.equal(breakfast, 'bacon and eggs');
      return breakfast;
    });

    assert.equal(result, 'bacon and eggs');
  

It allows you to use locals

A local is a value specific to a particular execution of invoke(). You can use locals like any other service.

    
    wagner.factory('eggs', function(number) {
      return 'finished making ' + number + ' eggs';
    });

    wagner.invoke(function(eggs) {
      assert.equal(eggs, 'finished making 4 eggs');
    }, { number: 4 });
  

It only executes the factory function once

Service functions are only executed once, the value is cached for all future calls to invoke().

    
    var count = 0;
    wagner.factory('eggs', function() {
      ++count;
      return 5;
    });

    assert.equal(count, 0);

    wagner.invoke(function(eggs) {
      assert.equal(eggs, 5);
      assert.equal(count, 1);
    });

    wagner.invoke(function(eggs) {
      assert.equal(count, 1);
    });

    assert.equal(count, 1);
  

It allows you to .get() a dependency

.constant(a, b) is a convenient shorthand for .factory(a, function() { return b; }

    
    wagner.constant('eggs', 6);

    wagner.task('bacon', function(eggs) {
      return Math.floor(eggs / 2);
    });

    assert.equal(wagner.get('bacon'), 3);
  

It has a .constant() function

You can also use .get() to explicitly get a dependency.

    
    wagner.constant('eggs', 5);

    wagner.invoke(function(eggs) {
      assert.equal(eggs, 5);
    });
  

As a way to reduce error-handling boilerplate

If you're a NodeJS developer, you've probably gotten sick of writing the following code:

function(error, res) { if (error) { return handleError(error); } }

The wagner.safe() function helps you make that cleaner.

It wraps callbacks to bubble up errors

wagner.safe() returns an event emitter that has a try() function. Just wrap your callbacks in a try() and all async errors get deferred to your event emitter. Like domains, but with less suck.

    
    var safe = wagner.safe();

    var asyncOpThatErrors = function(callback) {
      setTimeout(function() {
        callback('This is an error!');
      });
    };

    asyncOpThatErrors(safe.try(function(error) {
      // Never gets called: safe catches the error
      assert.ok(false);
    }));

    safe.on('error', function(error) {
      assert.equal(error, 'This is an error!');
      done();
    });
  

It catches exceptions too

The try() function also wraps your callbacks in a try/catch and emits. any exceptions. Never again will a TypeError: Cannot read property 'value' of undefined in your callback crash your server.

    
    var safe = wagner.safe();

    var asyncOpThatSucceeds = function(callback) {
      setTimeout(function() {
        callback();
      });
    };

    asyncOpThatSucceeds(safe.try(function() {
      throw 'Oops I messed up';
    }));

    safe.on('error', function(error) {
      assert.equal(error.toString(), 'Oops I messed up');
      done();
    });
  

As an async framework

Wagner also includes the ability to execute async tasks in a dependency-injection-like way. Wagner has 2 functions, invokeAsync() and task(), that enable you to write neat modular async code.

It can execute async tasks using invokeAsync()

The task() and invokeAsync() are roughly analogous to factory and invoke(). There are 3 differences:

  1. The function you pass to task() takes a callback, which it uses to pass a value to dependent tasks.
  2. The function you pass to invokeAsync() takes an error, which contains the first error that happened when executing the specified tasks.
  3. Tasks are re-executed every time you call invokeAsync(), whereas services are cached forever.
    
    wagner.task('task1', function(callback) {
      setTimeout(function() {
        callback(null, 'test');
      }, 50);
    });

    wagner.invokeAsync(function(error, task1) {
      assert.ok(!error);
      assert.equal(task1, 'test');
      done();
    });
  

It re-executes tasks on subsequent calls to invokeAsync()

    
    var called = 0;
    wagner.task('task1', function(callback) {
      ++called;
      setTimeout(function() {
        callback(null, 'test');
      }, 0);
    });

    wagner.invokeAsync(function(error, task1) {
      assert.ok(!error);
      assert.equal(task1, 'test');
      assert.equal(called, 1);

      wagner.invokeAsync(function(error, task1) {
        assert.ok(!error);
        assert.equal(task1, 'test');
        assert.equal(called, 2);
        done();
      });
    });
  

It executes tasks with maximum parallelization

Tasks are executed at most once per call to invokeAsync(), and tasks are executed with maximum parallelization. That is, as soon as all a tasks dependencies are ready, the task executes.

    
    var executed = {};
    wagner.task('readFile1', function(callback) {
      assert.equal(Object.keys(executed).length, 0);
      executed.readFile1 = true;
      callback(null, 'test');
    });

    wagner.task('processFile1', function(readFile1, callback) {
      assert.equal(Object.keys(executed).length, 1);
      assert.ok(executed.readFile1);
      setTimeout(function() {
        callback(null, 'test');
      }, 5);
    });

    wagner.task('logFile1', function(readFile1, callback) {
      assert.equal(Object.keys(executed).length, 1);
      assert.ok(executed.readFile1);
      setTimeout(function() {
        callback(null, 'test');
      }, 5);
    });

    wagner.invokeAsync(function(error, processFile1, logFile1) {
      assert.ifError(error);
      done();
    });