Example #1
0
const lineLengthStream = function (options) {
	options = options || {}
	let tabSize = new Array((options.tabSize || 4) + 1).join(' ')

	return connect( // todo: seems to be unelegant
		new byline.LineStream({keepEmptyLines: true}),
		objectify(function (line, _, cb) {
			this.push(lineLength(line.toString(), tabSize))
			cb()
		})
	)
}
Example #2
0
module.exports = function argosyClient () {
    var sequence    = 0,
        outstanding = [],
        input       = split(),
        parse       = objectify(function (chunk, enc, cb) { cb(null, JSON.parse(chunk)) }),
        output      = objectify.deobj(function (msg, enc, cb) { cb(null, JSON.stringify(msg) + '\n') }),
        responses   = filter.obj(function (msg) {
            return (msg.type === 'response' && msg.headers.client.id === client.id)
        })

    var processMessage = through2.obj(function parse(msg, enc, cb) {
        outstanding.filter(function (pending) {
            return pending.seq === msg.headers.client.seq
        }).forEach(function (pending) {
            if (msg.error) pending.reject(assign(new Error(msg.error.message), { remoteStack: msg.error.stack }))
            else pending.resolve(msg.body)
        })
        cb()
    })

    var client = pipeline(input, parse, responses, processMessage, output)
    client.id = uuid()
    client.invoke = function invoke (msgBody, cb) {
        var request = { type: 'request', headers: { client: { id: client.id, seq: sequence++ } }, body: msgBody },
            cb      = cb || function () {}

        var done = new Promise(function (resolve, reject) {
            outstanding.push({ seq: request.headers.client.seq, resolve: resolve, reject: reject })
            output.write(request)
        })
        done.then(function (body) {
            setImmediate(cb.bind(undefined, null, body))
        })
        done.catch(function (err) {
            setImmediate(cb.bind(undefined, err))
        })
        return done
    }
    client.invoke.partial = function invokePartial (partialBody) {
        return function partialInvoke (msgBody, cb) {
            return client.invoke(assign({}, partialBody, msgBody), cb)
        }
    }

    return client
}
Example #3
0
module.exports = function argosy (options) {
    options = assign({ id: uuid() }, options)

    var requestSeq     = 0,
        localServices  = [],
        remoteServices = [],
        outstanding    = [],
        input          = split(),
        parse          = objectify(function (chunk, enc, cb) { cb(null, JSON.parse(chunk)) }),
        output         = objectify.deobj(function (msg, enc, cb) { cb(null, JSON.stringify(msg) + '\n') })

    var processMessage = through2.obj(function parseMessage (msg, enc, cb) {
        switch (msg.type) {
            case 'request':
                queue(msg.body, function done (err, result) {
                    var reply = { type: 'response', headers: assign(msg.headers, { servicer: { id: options.id } }), body: result }
                    if (err) reply.error = { message: err.message, stack: err.stack }
                    processMessage.push(reply)
                })
                break
            case 'response':
                outstanding.filter(function (pending) {
                    return (msg.headers.consumer.id === options.id && pending.seq === msg.headers.consumer.seq)
                }).forEach(function (pending) {
                    if (msg.error) pending.reject(assign(new Error(msg.error.message), { remoteStack: msg.error.stack }))
                    else pending.resolve(msg.body)
                })
                break
            case 'subscribe':
                var syncMessage = { id: options.id }
                if (~msg.body.indexOf('services')) {
                    stream.localServiceAdded.removeConsumer(announceService)
                    stream.localServiceAdded(announceService)
                    localServices.forEach(announceService)
                    syncMessage.services = localServices.length
                }
                output.write({ type: 'synced', body: syncMessage })
                break
            case 'announce-service':
                remoteServices.push({ provider: msg.body.provider, pattern: pattern.decode(msg.body.pattern) })
                stream.serviceAdded.produce({ remote: true, provider: msg.body.provider, pattern: pattern.decode(msg.body.pattern) })
                break
            case 'synced':
                stream.synced.produce(msg.body)
                break
        }
        cb()
    })

    var stream = assign(pipeline(input, parse, processMessage, output), { id: options.id })

    stream.accept = function accept (rules) {
        var p = pattern(rules),
            q = cq()

        localServices.push({ pattern: p, queue: q })
        stream.serviceAdded.produce({ local: true, provider: { id: options.id }, pattern: p })
        return q
    }
    stream.invoke = function invoke (msgBody, cb) {
        var done = (serviceable(msgBody, localServices))
            ? queue(msgBody) // if we implement it ourself, stay in-process
            : stream.invoke.remote(msgBody) // otherwise, head out to sea
        return after(done, cb)
    }
    stream.invoke.remote = function invokeRemote (msgBody, cb) {
        var request = { type: 'request', headers: { consumer: { id: options.id, seq: requestSeq++ } }, body: msgBody }

        var done = new Promise(function (resolve, reject) {
            outstanding.push({ seq: request.headers.consumer.seq, resolve: resolve, reject: reject })
            output.write(request)
        })
        return after(done, cb)
    }
    stream.invoke.partial = function invokePartial (partialBody) {
        return function partialInvoke (msgBody, cb) {
            if (typeof msgBody === 'function') {
                cb = msgBody
                msgBody = {}
            }
            return stream.invoke(assign({}, partialBody, msgBody), cb)
        }
    }
    stream.subscribeRemote = function subscribeRemote (msgBody, cb) {
        assert(Array.isArray(msgBody), 'subscribeRemote requires an array of subscriptions')
        output.write({ type: 'subscribe', body: msgBody })
        return after(once(stream.synced), cb)
    }
    stream.synced = eventuate()
    stream.serviceAdded = eventuate()
    stream.remoteServiceAdded = filter(stream.serviceAdded, function (svc) { return svc.remote })
    stream.localServiceAdded = filter(stream.serviceAdded, function (svc) { return svc.local })
    stream.pattern = pattern

    Object.defineProperties(stream, {
        services: { get: function () {
            return localServices.map(function (svc) {
                return { local: true, provider: { id: options.id }, pattern: svc.pattern }
            }).concat(remoteServices.map(function (svc) {
                return { remote: true, provider: svc.provider, pattern: svc.pattern }
            }))
        }}
    })

    // can the message be routed to a service
    function serviceable (msgBody, services) {
        return (services.length && services.some(function acceptsMessage (svc) {
            return svc.pattern.matches(msgBody)
        }))
    }

    // find the right queue
    function queue (msgBody, cb) {
        var service = find(localServices, function (svc) {
            return svc.pattern.matches(msgBody)
        })
        if (!service) return cb(new Error('not implemented: ' + JSON.stringify(msgBody)))
        return service.queue(msgBody, cb)
    }

    function announceService (svc) {
        output.write({ type: 'announce-service', body: { provider: { id: options.id }, pattern: svc.pattern.encode() } })
    }

    return stream
}