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

argosy

v3.0.0

Published

A modular, pipable, micro-service framework

Downloads

19

Readme

argosy logo

NPM version Build Status Coverage Status Sauce Test Status

A modular, pipe-able, micro-service framework.

Table of Contents

motivation

Why a framework? After building micro-services a wide variety of ways over a number of years, in small organizations and large, I wanted to standardize the approach, and bring together all the lessons learned. Argosy draws inspiration from many sources including a smorgasbord of systems (I've used in other micro-service projects) such as RabbitMQ and Zookeeper, as well as other node libraries including but not limited to dnode, rpc-stream, and seneca.

Like the micro-service model, the Argosy ecosystem consists of many small modules. These components are streams, designed to be connected together via pipes. Extending Argosy is a matter of manipulating the stream.

example

es5

var http    = require('http'),
    query   = require('querystring'),
    argosy  = require('argosy')()

// create a service queue of requests for weather
var weatherRequest = argosy.accept({
    get: 'weather',
    location: argosy.pattern.match.defined
})

// process the requests for weather
weatherRequest.process(function (msg, cb) {
    var qs = query.stringify({ q: msg.location, units: msg.units || 'imperial' })
    http.get('http://api.openweathermap.org/data/2.5/weather?' + qs, function (res) {
        var body = ''
        res.on('data', function (data) {
            body += data
        }).on('end', function () {
            cb(null, JSON.parse(body).main)
        })
    })
})

// use the service
argosy.invoke({ get: 'weather', location: 'Boston,MA' }, function (err, weather) {
    console.log(weather.temp + ' degrees (F) in Boston.')
})

// or create a convenience function using invoke.partial
var getWeather = argosy.invoke.partial({ get: 'weather', units: 'metric' })

getWeather({ location: 'Dublin,IE' }, function (err, weather) {
    console.log(weather.temp + ' degrees (C) in Dublin.')
})

// or use promises
getWeather({ location: 'London,UK' }).then(function (weather) {
    console.log(weather.temp + ' degrees (C) in London.')
})

es6+

var http    = require('http'),
    query   = require('querystring').stringify,
    request = require('request-promise'),
    co      = require('co'),
    argosy  = require('..')()

// create a service queue of requests for weather
var weatherRequest = argosy.accept({
    get: 'weather',
    location: argosy.pattern.match.defined
})

// process the requests for weather
var weatherUrl = 'http://api.openweathermap.org/data/2.5/weather?'
weatherRequest.process(co.wrap(function* ({ location: q, units = 'imperial' }) {
    var weather = yield request.get(weatherUrl + query({ q, units }))
    return JSON.parse(weather).main
}))

// we can create a convenience function with invoke.partial
var getWeather = argosy.invoke.partial({ get: 'weather', units: 'metric' })

co(function* () {
    // use invoke directly
    var boston = yield argosy.invoke({ get: 'weather', location: 'Boston,MA' })

    // or use our shiny new convenient function
    var dublin = yield getWeather({ location: 'Dublin,IE' })
    var london = yield getWeather({ location: 'London,UK' })

    console.log(boston.temp + ' degrees (F) in Boston.')
    console.log(dublin.temp + ' degrees (C) in Dublin.')
    console.log(london.temp + ' degrees (C) in London.\n')
})

Note: If your runtime doesn't offer generators or promises, you can still run the above example from the example directory via babel. Just do: npm i -g babel && babel-node example/es6.js

api

// Create a new Argosy endpoint/stream
var argosy = require('argosy')()

queue = argosy.accept(pattern)

Create a concurrent-queue that will be pushed messages that match the pattern object provided (see argosy-pattern for details on defining patterns). These messages should be processed and responded to using the process function of the queue. Responses will be sent to the requesting Argosy endpoint.

It is advised not to match the key argosy as this is reserved for internal use.

queue.process([opts,] func)

Process messages. See concurrent-queue for more information. The processor function func has a signature of msg [, cb]. The callback cb if provided should be executed with any applicable return value or error object (as 1st argument) for the invoking client, once the request has been completed. Alternatively, a promise may be returned from the processor function func, and its resolved value or rejected error will be returned to the invoking client.

argosy.invoke(msg [, cb])

Invoke a service which implements the msg object pattern. Upon completion, the callback cb, if supplied, will be called with the result or error. The argosy.invoke function also returns a promise which will resolve or reject appropriately. If the msg matches one of the patterns implemented by the argosy endpoint performing the invoke, then the invoke request will be handled locally by the the Argosy endpoint invoke was called from, otherwise the invoke request will be written to the stream's output, and the stream's input will be monitored for a response.

func = argosy.invoke.partial(partialMsg)

Return a function that represents a partial invocation. The function returned has the same signature as argosy.invoke, but when called, the msg object parameter will be merged with the partialMsg object parameter provided at the time the function was created. Otherwise, the generated function behaves identically to argosy.invoke.

argosy.subscribeRemote(subscriptions [, cb])

Accepts an array of strings, these strings are subscription topics. Valid subscription topics are:

  • services - Be notified when the remote argosy endpoint adds a service. All existing remote services will be sent immediately.

Also accepts an optional error-first callback, which will be invoked after the remote argosy endpoint has sent all existing services.

This function returns a promise.

events

Argosy exposes the following eventuates:

serviceAdded

Produces messages when any service is added to the argosy endpoint (local or remote). The structure of the message is:

{ 
    remote: true|false, 
    provider: { 
        id: uuid 
    }, 
    pattern: argosyPattern 
}

Where argosyPattern is an argosy.pattern

localServiceAdded

Produces messages when a local service is added to the argosy endpoint. The structure of the message is:

    remote: false, 
    provider: { 
        id: uuid 
    }, 
    pattern: argosyPattern 

Where argosyPattern is an argosy.pattern

remoteServiceAdded

Produces messages when a remote service is added to the argosy endpoint. The structure of the message is:

    remote: true, 
    provider: { 
        id: uuid 
    }, 
    pattern: argosyPattern 

synced

Produces messages when a remote service has advertised all subscribed resources to the local argosy endpoint.

    provider: { 
        id: uuid 
    },
    services: count

Where services is present only if the remote subscription includes "services".

pattern = argosy.pattern(object)

See also argosy-pattern.

Create an Argosy pattern, given an object containing rules. Each key in the object represents a key that is to be validated in compared message objects. These keys will be tested to have the same literal value, matching regular expression, or to be of a given type using the matching system described below. Nested keys may be matched using the dot notation. For example, {'message.count':1} equates to {message: {count: 1}}.

pattern.matches(object)

Returns true of the given object matches the pattern, or false otherwise.

argosy.pattern.match

Argosy patterns support more than literal values. The values of the pattern keys may be any of the following in addition to literal values:

  • A regular expression - values will be tested against the regular expression to determine a match
  • argosy.pattern.match.number - matches any number
  • argosy.pattern.match.string - matches any string
  • argosy.pattern.match.bool - matches true or false
  • argosy.pattern.match.array - matches any array
  • argosy.pattern.match.object - matches any truthy object
  • argosy.pattern.match.defined - matches anything other than undefined
  • argosy.pattern.match.undefined - matches undefined or missing key

connecting endpoints

One Argosy endpoint may be connected to another via pipes.

var argosy   = require('argosy'),
    service1 = argosy(),
    service2 = argosy()

service1.pipe(service2).pipe(service)

This will create a duplex connection between the two Argosy endpoints, and allow both to invoke implemented services via the other. For example:

service1.accept({ get: 'random number' }).process(function (msg, cb) {
    // do something interesting
})

service2.accept({ get: 'random letter' }).process(function (msg, cb) {
    // do something interesting
})

service1.invoke({ get: 'random letter' }, function (err, letter) {
    // this works
})

service2.invoke({ get: 'random number' }, function (err, number) {
    // so does this
})

testing

npm test [--dot | --spec] [--phantom] [--grep=pattern]

Specifying --dot or --spec will change the output from the default TAP style. Specifying --phantom will cause the tests to run in the headless phantom browser instead of node. Specifying --grep will only run the test files that match the given pattern.

browser test

npm run browser-test

This will run the tests in all browsers (specified in .zuul.yml). Be sure to educate zuul first.

coverage

npm run coverage [--html]

This will output a textual coverage report. Including --html will also open an HTML coverage report in the default browser.