forked from guybrush/dnode-protocol
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
125 lines (108 loc) · 3.36 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
var EventEmitter = require('events').EventEmitter;
var scrubber = require('./lib/scrub');
var objectKeys = require('./lib/keys');
var forEach = require('./lib/foreach');
var isEnumerable = require('./lib/is_enum');
module.exports = function (cons, opts) {
return new Proto(cons, opts);
};
(function () { // browsers bleh
for (var key in EventEmitter.prototype) {
Proto.prototype[key] = EventEmitter.prototype[key];
}
})();
function Proto (cons, opts) {
var self = this;
EventEmitter.call(self);
if (!opts) opts = {};
self.remote = {};
self.callbacks = { local : [], remote : [] };
self.wrap = opts.wrap;
self.unwrap = opts.unwrap;
self.scrubber = scrubber(self.callbacks.local);
if (typeof cons === 'function') {
self.instance = new cons(self.remote, self);
}
else self.instance = cons || {};
}
Proto.prototype.start = function () {
this.request('methods', [ this.instance ]);
};
Proto.prototype.cull = function (id) {
delete this.callbacks.remote[id];
this.emit('request', {
method : 'cull',
arguments : [ id ]
});
};
Proto.prototype.request = function (method, args) {
var scrub = this.scrubber.scrub(args);
this.emit('request', {
method : method,
arguments : scrub.arguments,
callbacks : scrub.callbacks,
links : scrub.links
});
};
Proto.prototype.handle = function (req) {
var self = this;
var args = self.scrubber.unscrub(req, function (id) {
if (self.callbacks.remote[id] === undefined) {
// create a new function only if one hasn't already been created
// for a particular id
var cb = function () {
self.request(id, [].slice.apply(arguments));
};
self.callbacks.remote[id] = self.wrap ? self.wrap(cb, id) : cb;
return cb;
}
return self.unwrap
? self.unwrap(self.callbacks.remote[id], id)
: self.callbacks.remote[id]
;
});
if (req.method === 'methods') {
self.handleMethods(args[0]);
}
else if (req.method === 'cull') {
forEach(args, function (id) {
delete self.callbacks.local[id];
});
}
else if (typeof req.method === 'string') {
if (isEnumerable(self.instance, req.method)) {
self.apply(self.instance[req.method], args);
}
else {
self.emit('fail', new Error(
'request for non-enumerable method: ' + req.method
));
}
}
else if (typeof req.method == 'number') {
var fn = self.callbacks.local[req.method];
if (!fn) {
self.emit('fail', new Error('no such method'));
}
else self.apply(fn, args);
}
};
Proto.prototype.handleMethods = function (methods) {
var self = this;
if (typeof methods != 'object') {
methods = {};
}
// copy since assignment discards the previous refs
forEach(objectKeys(self.remote), function (key) {
delete self.remote[key];
});
forEach(objectKeys(methods), function (key) {
self.remote[key] = methods[key];
});
self.emit('remote', self.remote);
self.emit('ready');
};
Proto.prototype.apply = function (f, args) {
try { f.apply(undefined, args) }
catch (err) { this.emit('error', err) }
};