-
Notifications
You must be signed in to change notification settings - Fork 12
/
index.js
127 lines (114 loc) · 3.22 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
var through = require('through')
var serialize = require('stream-serializer')()
function get(obj, path) {
if(Array.isArray(path)) {
for(var i in path)
obj = obj[path[i]]
return obj
}
return obj[path]
}
module.exports = function (obj, opts) {
if('boolean' == typeof opts) opts = { raw: opts }
opts = opts || {}
var cbs = {}, count = 1, local = obj || {}
var flattenError = opts.flattenError || function (err) {
if(!(err instanceof Error)) return err
var err2 = { message: err.message }
for(var k in err)
err2[k] = err[k]
return err2
}
function expandError(err) {
if (!err || !err.message) return err
var err2 = new Error(err.message)
for(var k in err)
err2[k] = err[k]
return err2
}
if(obj) {
local = {}
function callable (k) {
return function (args, cb) {
return obj[k].apply(obj, cb ? args.concat(cb) : args)
}
}
for(var k in obj)
local[k] = callable(k)
}
var s = through(function (data) {
//write - on incoming call
data = data.slice()
var i = data.pop(), args = data.pop(), name = data.pop()
//if(~i) then there was no callback.
if (args[0]) args[0] = expandError(args[0])
if(name != null) {
var called = 0
var cb = function () {
if (called++) return
var args = [].slice.call(arguments)
args[0] = flattenError(args[0])
if(~i) s.emit('data', [args, i]) //responses don't have a name.
}
try {
local[name].call(obj, args, cb)
} catch (err) {
if(~i) s.emit('data', [[flattenError(err)], i])
}
} else if(!cbs[i]) {
//there is no callback with that id.
//either one end mixed up the id or
//it was called twice.
//log this error, but don't throw.
//this process shouldn't crash because another did wrong
s.emit('invalid callback id')
return console.error('ERROR: unknown callback id: '+i, data)
} else {
//call the callback.
var cb = cbs[i]
delete cbs[i] //delete cb before calling it, incase cb throws.
cb.apply(null, args)
}
})
var rpc = s.rpc = function (name, args, cb) {
if(cb) cbs[++count] = cb
if('string' !== typeof name)
throw new Error('name *must* be string')
s.emit('data', [name, args, cb ? count : -1])
if(cb && count == 9007199254740992) count = 0 //reset if max
//that is 900 million million.
//if you reach that, dm me,
//i'll buy you a beer. @dominictarr
}
function keys (obj) {
var keys = []
for(var k in obj) keys.push(k)
return keys
}
s.createRemoteCall = function (name) {
return function () {
var args = [].slice.call(arguments)
var cb = ('function' == typeof args[args.length - 1])
? args.pop()
: null
rpc(name, args, cb)
}
}
s.createLocalCall = function (name, fn) {
local[name] = fn
}
s.wrap = function (remote, _path) {
_path = _path || []
var w = {}
;(Array.isArray(remote) ? remote
: 'string' == typeof remote ? [remote]
: remote = keys(remote)
).forEach(function (k) {
w[k] = s.createRemoteCall(k)
})
return w
}
if(opts.raw)
return s
return serialize(s)
}