aboutsummaryrefslogtreecommitdiff
path: root/node_modules/montage/core/promise-connection.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/montage/core/promise-connection.js')
-rw-r--r--node_modules/montage/core/promise-connection.js285
1 files changed, 285 insertions, 0 deletions
diff --git a/node_modules/montage/core/promise-connection.js b/node_modules/montage/core/promise-connection.js
new file mode 100644
index 00000000..dc218f11
--- /dev/null
+++ b/node_modules/montage/core/promise-connection.js
@@ -0,0 +1,285 @@
1
2var Montage = require("./core").Montage;
3var UUID = require("./uuid");
4var Promise = require("./promise").Promise;
5var PromiseQueue = require("./promise-queue").PromiseQueue;
6var CacheMap = require("./shim/structures").CacheMap;
7var logger = require("./logger").logger("promise-connections");
8var rootId = "";
9
10exports.connect = connect;
11function connect(port, local, options) {
12 return PromiseConnection.create().init(port, local, options);
13}
14
15var PromiseConnection =
16exports.PromiseConnection = Montage.create(Montage, {
17
18 init: {
19 value: function (port, local, options) {
20 options = options || {};
21 // sigil for debug message prefix
22 this.sigil = Math.random().toString(36).toUpperCase().slice(2, 4);
23 this.makeId = options.makeId || UUID.generate;
24 this.locals = CacheMap(options.max);
25 this.port = Adapter.adapt(port, options.origin);
26 this.port.forEach(function (message) {
27 this.log("receive:", message);
28 message = JSON.parse(message);
29 if (!this.receivers[message.type])
30 return;
31 if (!this.locals.has(message.to))
32 return;
33 this.receivers[message.type].call(this, message);
34 }, this);
35 this.makeLocal(rootId);
36 this.resolveLocal(rootId, local);
37 return this.makeRemote(rootId);
38 }
39 },
40
41 log: {
42 value: function (/*...args*/) {
43 logger.debug.apply(logger, ["Connection:", this.sigil].concat(Array.prototype.slice.call(arguments)))
44 }
45 },
46
47 encode: {
48 value: function (object) {
49 if (Promise.isPromise(object)) {
50 var id = this.makeId();
51 this.makeLocal(id);
52 this.resolveLocal(id, object);
53 return {"@": id};
54 } else if (Array.isArray(object)) {
55 return object.map(this.encode, this);
56 } else if (typeof object === "object") {
57 var newObject = {};
58 if (has.call(object, key)) {
59 var newKey = key;
60 if (/^[!@]$/.exec(key))
61 newKey = key + key;
62 newObject[newKey] = this.encode(object[key]);
63 }
64 return newObject;
65 } else {
66 return object;
67 }
68 }
69 },
70
71 decode: {
72 value: function (object) {
73 if (!object) {
74 return object;
75 } else if (object['!']) {
76 return Promise.reject(object['!']);
77 } else if (object['@']) {
78 return this.makeRemote(object['@']);
79 } else if (Array.isArray(object)) {
80 return object.map(this.decode, this);
81 } else if (typeof object === 'object') {
82 var newObject = {};
83 for (var key in object) {
84 if (has.call(object, key)) {
85 var newKey = key;
86 if (/^(@@|!!)$/.exec(key))
87 newKey = key.substring(1);
88 newObject[newKey] = this.decode(object[key]);
89 }
90 }
91 return newObject;
92 } else {
93 return object;
94 }
95 }
96 },
97
98 makeLocal: {
99 value: function (id) {
100 if (!this.locals.has(id)) {
101 this.locals.set(id, Promise.defer());
102 }
103 return this.locals.get(id).promise;
104 }
105 },
106
107 resolveLocal: {
108 value: function (id, value) {
109 this.log('resolve:', 'L' + JSON.stringify(id), JSON.stringify(value));
110 this.locals.get(id).resolve(value);
111 }
112 },
113
114 makeRemote: {
115 value: function (id) {
116 return RemotePromise.create().init(this, id);
117 }
118 },
119
120 receivers: {
121 value: Object.create(null, {
122
123 resolve: {
124 value: function (message) {
125 if (this.locals.has(message.to)) {
126 this.resolveLocal(message.to, this.decode(message.resolution));
127 }
128 }
129 },
130
131 send: {
132 value: function (message) {
133 var connection = this;
134 this.locals.get(message.to).promise
135 .send(message.op, this.decode(message.args))
136 .then(function (resolution) {
137 return Promise.call(function () {
138 return JSON.stringify({
139 type: 'resolve',
140 to: message.from,
141 resolution: connection.encode(resolution)
142 })
143 })
144 .fail(function (error) {
145 console.log('XXX:', error);
146 return JSON.stringify({
147 type: 'resolve',
148 to: message.from,
149 resolution: null
150 });
151 })
152 }, function (reason, error, rejection) {
153 return Promise.call(function () {
154 return JSON.stringify({
155 type: 'resolve',
156 to: message.from,
157 resolution: {'!': connection.encode(reason)}
158 })
159 })
160 .fail(function () {
161 return JSON.stringify({
162 type: 'resolve',
163 to: message.from,
164 resolution: {'!': null}
165 });
166 })
167 })
168 .then(function (envelope) {
169 connection.port.put(envelope);
170 })
171 .end();
172 }
173 }
174
175 })
176 }
177
178});
179
180var RemotePromise = Montage.create(Promise.AbstractPromise, {
181 init: {
182 value: function (connection, id) {
183 this._connection = connection;
184 this._id = id;
185 this.Promise = Promise;
186 return this;
187 }
188 },
189 _handlers: {
190 value: {}
191 },
192 _fallback: {
193 value: function (resolve, op /*...args*/) {
194 var localId = this._connection.makeId();
195 var response = this._connection.makeLocal(localId);
196 var args = Array.prototype.slice.call(arguments, 2);
197 this._connection.log('sending:', 'R' + JSON.stringify(this._id), JSON.stringify(op), JSON.stringify(args));
198 this._connection.port.put(JSON.stringify({
199 type: 'send',
200 to: this._id,
201 from: localId,
202 op: op,
203 args: this._connection.encode(args)
204 }));
205 return response;
206 }
207 }
208});
209
210var Adapter =
211exports.Adapter = Montage.create(Montage, {
212 adapt: {
213 value: function (port, origin) {
214 return this.create().init(port, origin);
215 }
216 },
217 init: {
218 value: function (port, origin) {
219 if (port.postMessage) {
220 // MessagePorts
221 this._send = function (message) {
222 // some message ports require an "origin"
223 port.postMessage(message, origin);
224 };
225