alchemymvc 1.1.9 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/app/conduit/socket_conduit.js +620 -620
- package/lib/app/datasource/mongo_datasource.js +75 -10
- package/lib/app/helper/backed_map.js +257 -0
- package/lib/app/helper/enum_values.js +125 -0
- package/lib/app/helper/router_helper.js +7 -1
- package/lib/app/helper/socket_helper.js +613 -613
- package/lib/app/helper_datasource/00-nosql_datasource.js +73 -19
- package/lib/app/helper_field/enum_field.js +9 -36
- package/lib/app/helper_field/schema_field.js +11 -21
- package/lib/app/helper_model/criteria.js +73 -18
- package/lib/app/helper_model/field_config.js +24 -5
- package/lib/app/helper_model/model.js +45 -7
- package/lib/app/model/alchemy_migration_model.js +33 -0
- package/lib/bootstrap.js +9 -0
- package/lib/class/datasource.js +2 -2
- package/lib/class/element.js +6 -1
- package/lib/class/field.js +7 -1
- package/lib/class/migration.js +138 -0
- package/lib/class/model.js +11 -19
- package/lib/class/schema.js +14 -12
- package/lib/core/client_alchemy.js +14 -0
- package/lib/core/socket.js +159 -159
- package/lib/init/alchemy.js +1793 -1779
- package/lib/init/functions.js +4 -1
- package/lib/init/load_functions.js +8 -2
- package/lib/init/requirements.js +101 -96
- package/package.json +13 -13
- package/CHANGELOG.md +0 -458
|
@@ -1,621 +1,621 @@
|
|
|
1
|
-
var iostream = alchemy.use('socket.io-stream'),
|
|
2
|
-
libstream = require('stream');
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* The Socket Conduit Class
|
|
6
|
-
*
|
|
7
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
8
|
-
* @since 0.2.0
|
|
9
|
-
* @version 1.1.3
|
|
10
|
-
*
|
|
11
|
-
* @param {Socker} socket
|
|
12
|
-
* @param {Object} announcement
|
|
13
|
-
*/
|
|
14
|
-
var SocketConduit = Function.inherits('Alchemy.Conduit', function Socket(socket, announcement) {
|
|
15
|
-
|
|
16
|
-
var that = this;
|
|
17
|
-
|
|
18
|
-
// Indicate this is a websocket
|
|
19
|
-
this.websocket = socket;
|
|
20
|
-
this.socket = socket;
|
|
21
|
-
|
|
22
|
-
// Store the headers
|
|
23
|
-
this.headers = socket.handshake.headers;
|
|
24
|
-
|
|
25
|
-
// Store the announcement data
|
|
26
|
-
this.announcement = announcement;
|
|
27
|
-
|
|
28
|
-
// Detect node clients
|
|
29
|
-
if (this.headers['user-agent'] == 'node-XMLHttpRequest') {
|
|
30
|
-
this.isNodeClient = true;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Cookies to send to the client
|
|
34
|
-
this.new_cookies = {};
|
|
35
|
-
this.new_cookie_header = [];
|
|
36
|
-
|
|
37
|
-
// The amount of submissions
|
|
38
|
-
this.counter = 0;
|
|
39
|
-
|
|
40
|
-
// Collection of callbacks
|
|
41
|
-
this.callbacks = {};
|
|
42
|
-
|
|
43
|
-
// Create a stream instance
|
|
44
|
-
this.stream = iostream(socket);
|
|
45
|
-
|
|
46
|
-
// Linkup storage
|
|
47
|
-
this.linkups = {};
|
|
48
|
-
|
|
49
|
-
this.parseAnnouncement();
|
|
50
|
-
if (!this.isNodeClient) this.parseRequest();
|
|
51
|
-
|
|
52
|
-
// Listen for payloads
|
|
53
|
-
socket.on('payload', function onPayload(packet) {
|
|
54
|
-
that.onPayload(packet);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
// Listen for responses
|
|
58
|
-
socket.on('response', function onResponse(packet) {
|
|
59
|
-
that.onResponse(packet);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
// Listen for linkups
|
|
63
|
-
socket.on('linkup', function onLinkup(packet) {
|
|
64
|
-
that.onLinkup(packet);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
// Listen for disconnects
|
|
68
|
-
socket.on('disconnect', function onDisconnect() {
|
|
69
|
-
|
|
70
|
-
var key;
|
|
71
|
-
|
|
72
|
-
that.emit('disconnect');
|
|
73
|
-
|
|
74
|
-
for (key in that.linkups) {
|
|
75
|
-
that.linkups[key].emit('disconnect');
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
// Listen for payloads on the stream socket
|
|
80
|
-
this.stream.on('payload', function onStreamPayload(stream, packet) {
|
|
81
|
-
packet.stream = stream;
|
|
82
|
-
that.onPayload(packet);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
// Listen for responses on the stream socket
|
|
86
|
-
this.stream.on('response', function onStreamResponse(stream, data) {
|
|
87
|
-
packet.stream = stream;
|
|
88
|
-
that.onPayload(packet);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
// Listen to data subscriptions
|
|
92
|
-
this.on('subscribe-data', function gotSubscriptionRequest(arr) {
|
|
93
|
-
|
|
94
|
-
if (!Array.isArray(arr)) {
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
that.registerBindings(arr);
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Return the client IP address
|
|
104
|
-
*
|
|
105
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
106
|
-
* @since 0.2.1
|
|
107
|
-
* @version 0.2.1
|
|
108
|
-
*/
|
|
109
|
-
SocketConduit.setProperty(function ip() {
|
|
110
|
-
|
|
111
|
-
var sock = this.socket;
|
|
112
|
-
|
|
113
|
-
if (!sock) {
|
|
114
|
-
return null;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return sock.conn.remoteAddress || null;
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Parse the request, get information from the url
|
|
122
|
-
*
|
|
123
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
124
|
-
* @since 0.2.0
|
|
125
|
-
* @version 0.2.0
|
|
126
|
-
*/
|
|
127
|
-
SocketConduit.setMethod(function parseRequest() {
|
|
128
|
-
this.parseShortcuts();
|
|
129
|
-
this.parseLanguages();
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Handle a client announcement
|
|
134
|
-
*
|
|
135
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
136
|
-
* @since 0.2.0
|
|
137
|
-
* @version 1.1.0
|
|
138
|
-
*
|
|
139
|
-
* @param {Object} data
|
|
140
|
-
*/
|
|
141
|
-
SocketConduit.setMethod(function parseAnnouncement() {
|
|
142
|
-
|
|
143
|
-
var connections,
|
|
144
|
-
data = this.announcement;
|
|
145
|
-
|
|
146
|
-
// Store the scene information
|
|
147
|
-
this.connection_type = data.connection_type;
|
|
148
|
-
this.last_update = data.last_update || Date.now();
|
|
149
|
-
this.scene_id = data.scene;
|
|
150
|
-
|
|
151
|
-
// Register the connection in the user's session
|
|
152
|
-
this.getSession().registerConnection(this);
|
|
153
|
-
|
|
154
|
-
// Tell the client we're ready
|
|
155
|
-
this.websocket.emit('ready');
|
|
156
|
-
|
|
157
|
-
if (alchemy.settings.debug) {
|
|
158
|
-
log.info('Established websocket connection to', this.ip, 'using', this.useragent.family, this.useragent.major);
|
|
159
|
-
}
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Handle an incoming linkup
|
|
164
|
-
*
|
|
165
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
166
|
-
* @since 0.2.0
|
|
167
|
-
* @version 0.4.0
|
|
168
|
-
*
|
|
169
|
-
* @param {Object} packet
|
|
170
|
-
*/
|
|
171
|
-
SocketConduit.setMethod(function onLinkup(packet) {
|
|
172
|
-
|
|
173
|
-
var controller,
|
|
174
|
-
instance,
|
|
175
|
-
section,
|
|
176
|
-
linkup,
|
|
177
|
-
router,
|
|
178
|
-
action,
|
|
179
|
-
split,
|
|
180
|
-
type,
|
|
181
|
-
temp,
|
|
182
|
-
fnc,
|
|
183
|
-
id;
|
|
184
|
-
|
|
185
|
-
temp = packet.type;
|
|
186
|
-
|
|
187
|
-
// Make sure a type is defined
|
|
188
|
-
if (!temp) {
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
id = packet.id;
|
|
193
|
-
|
|
194
|
-
// Split by at symbol to get the optional sub section
|
|
195
|
-
temp = temp.split('@');
|
|
196
|
-
|
|
197
|
-
if (temp.length == 2) {
|
|
198
|
-
section = temp[0];
|
|
199
|
-
type = temp[1];
|
|
200
|
-
|
|
201
|
-
router = Router.subSections[section];
|
|
202
|
-
|
|
203
|
-
if (!router) {
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
} else {
|
|
208
|
-
type = temp[0];
|
|
209
|
-
router = Router;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// See if any socket routes have been set
|
|
213
|
-
if (router.linkupRoutes[type]) {
|
|
214
|
-
fnc = router.linkupRoutes[type];
|
|
215
|
-
|
|
216
|
-
// Create the new linkup instance
|
|
217
|
-
linkup = new Linkup(this, type, id, packet.data);
|
|
218
|
-
|
|
219
|
-
if (typeof fnc === 'function') {
|
|
220
|
-
fnc.call(this, this, linkup);
|
|
221
|
-
} else if (typeof fnc === 'string') { // Strings like 'StaticController#index'
|
|
222
|
-
|
|
223
|
-
split = fnc.split('#');
|
|
224
|
-
controller = split[0];
|
|
225
|
-
action = split[1];
|
|
226
|
-
|
|
227
|
-
instance = Controller.get(controller, this);
|
|
228
|
-
instance.packetType = packet.type;
|
|
229
|
-
instance.doAction(action, [this, linkup, packet.data]);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// Emit it on the socket itself
|
|
234
|
-
this.emit(packet.type, linkup, null);
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Handle a client packet
|
|
239
|
-
*
|
|
240
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
241
|
-
* @since 0.2.0
|
|
242
|
-
* @version 1.1.4
|
|
243
|
-
*
|
|
244
|
-
* @param {Object} packet
|
|
245
|
-
*/
|
|
246
|
-
SocketConduit.setMethod(function onPayload(packet) {
|
|
247
|
-
|
|
248
|
-
var that = this,
|
|
249
|
-
controller,
|
|
250
|
-
instance,
|
|
251
|
-
respond,
|
|
252
|
-
action,
|
|
253
|
-
linkup,
|
|
254
|
-
route,
|
|
255
|
-
split,
|
|
256
|
-
args,
|
|
257
|
-
fnc;
|
|
258
|
-
|
|
259
|
-
if (packet.respond) {
|
|
260
|
-
respond = function respondToPayload(err, data, stream) {
|
|
261
|
-
|
|
262
|
-
var responsePacket = {
|
|
263
|
-
err : null,
|
|
264
|
-
respond_to : packet.id,
|
|
265
|
-
noData : false,
|
|
266
|
-
data : null
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
if (err) {
|
|
270
|
-
err = JSON.clone(err, 'toHawkejs');
|
|
271
|
-
responsePacket.err = JSON.toDryObject(err);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
if (data && data.constructor.name == 'IOStream') {
|
|
275
|
-
responsePacket.noData = true;
|
|
276
|
-
stream = data;
|
|
277
|
-
} else {
|
|
278
|
-
// Make sure the data is converted to client-side friendly code
|
|
279
|
-
data = JSON.clone(data, 'toHawkejs');
|
|
280
|
-
|
|
281
|
-
// Now create a DRY object
|
|
282
|
-
responsePacket.data = JSON.toDryObject(data);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
if (stream && stream.constructor.name == 'IOStream') {
|
|
286
|
-
return that.stream.emit('response', stream, responsePacket);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
that.socket.emit('response', responsePacket);
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// See if this is for a specific linkup
|
|
294
|
-
if (packet.link) {
|
|
295
|
-
linkup = this.linkups[packet.link];
|
|
296
|
-
|
|
297
|
-
// Do NOT allow packets of the 'linkup_packet' type,
|
|
298
|
-
// we use that internally for the entire packet
|
|
299
|
-
if (packet.type == 'linkup_packet') {
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
if (linkup) {
|
|
304
|
-
|
|
305
|
-
let test = linkup.simpleListeners.get(packet.type);
|
|
306
|
-
|
|
307
|
-
if (!test) {
|
|
308
|
-
console.error('Linkup', linkup, 'has no listener for', packet.type);
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
if (packet.stream) {
|
|
312
|
-
linkup.emit(packet.type, packet.data, packet.stream, respond, null);
|
|
313
|
-
} else {
|
|
314
|
-
linkup.emit(packet.type, packet.data, respond, null);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// Also emit as a linkup_packet
|
|
318
|
-
linkup.emit('linkup_packet', packet, respond, null);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// Normalize data-server-events
|
|
325
|
-
if (packet.type == 'data-server-event') {
|
|
326
|
-
|
|
327
|
-
// See if data is valid, otherwise do nothing
|
|
328
|
-
if (!packet.data || !packet.data[0]) {
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// Get the real type
|
|
333
|
-
packet.type = packet.data[0];
|
|
334
|
-
|
|
335
|
-
// Store the rest as arguments
|
|
336
|
-
packet.args = packet.data.slice(1);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// See if any socket routes have been set
|
|
340
|
-
if (Router.socketRoutes[packet.type]) {
|
|
341
|
-
route = Router.socketRoutes[packet.type];
|
|
342
|
-
|
|
343
|
-
if (packet.args) {
|
|
344
|
-
args = packet.args;
|
|
345
|
-
|
|
346
|
-
// Add "respond" callback to the bottom
|
|
347
|
-
args.push(respond);
|
|
348
|
-
} else if (packet.stream) {
|
|
349
|
-
args = [packet.data, packet.stream, respond];
|
|
350
|
-
} else {
|
|
351
|
-
args = [packet.data, respond];
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
route.callHandler(this, args);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
// Emit it on the socket itself
|
|
358
|
-
if (packet.stream) {
|
|
359
|
-
that.emit(packet.type, packet.data, packet.stream, respond, null);
|
|
360
|
-
} else {
|
|
361
|
-
that.emit(packet.type, packet.data, respond, null);
|
|
362
|
-
}
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
/**
|
|
366
|
-
* Handle a client response
|
|
367
|
-
*
|
|
368
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
369
|
-
* @since 0.2.0
|
|
370
|
-
* @version 0.2.0
|
|
371
|
-
*
|
|
372
|
-
* @param {Object} packet
|
|
373
|
-
*/
|
|
374
|
-
SocketConduit.setMethod(function onResponse(packet) {
|
|
375
|
-
|
|
376
|
-
if (typeof this.callbacks[packet.respond_to] === 'function') {
|
|
377
|
-
this.callbacks[packet.respond_to](packet.err, packet.data);
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
delete this.callbacks[packet.respond_to];
|
|
381
|
-
});
|
|
382
|
-
|
|
383
|
-
/**
|
|
384
|
-
* Submit a message to the client
|
|
385
|
-
*
|
|
386
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
387
|
-
* @since 0.0.1
|
|
388
|
-
* @version 1.0.5
|
|
389
|
-
*
|
|
390
|
-
* @param {String} type
|
|
391
|
-
* @param {Object} data
|
|
392
|
-
* @param {IOStream} stream
|
|
393
|
-
* @param {Function} callback
|
|
394
|
-
*/
|
|
395
|
-
SocketConduit.setMethod(function submit(type, data, stream, callback) {
|
|
396
|
-
|
|
397
|
-
var packet = {},
|
|
398
|
-
regular_stream;
|
|
399
|
-
|
|
400
|
-
if (alchemy.isStream(data)) {
|
|
401
|
-
callback = stream;
|
|
402
|
-
stream = data;
|
|
403
|
-
data = undefined;
|
|
404
|
-
} else if (typeof data === 'function') {
|
|
405
|
-
callback = data;
|
|
406
|
-
data = undefined;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
if (!stream || typeof stream == 'function') {
|
|
410
|
-
callback = stream;
|
|
411
|
-
stream = undefined;
|
|
412
|
-
} else if (stream && stream.constructor.name != 'IOStream') {
|
|
413
|
-
|
|
414
|
-
// Keep the regular stream
|
|
415
|
-
regular_stream = stream;
|
|
416
|
-
|
|
417
|
-
// Create an IOStream
|
|
418
|
-
stream = this.createStream();
|
|
419
|
-
|
|
420
|
-
// Pipe the regular stream into the IOStream
|
|
421
|
-
regular_stream.pipe(stream);
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
if (Array.isArray(type)) {
|
|
425
|
-
packet.link = type[0];
|
|
426
|
-
packet.type = type[1];
|
|
427
|
-
} else {
|
|
428
|
-
packet.type = type;
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
if (data && data.constructor.name == 'IOStream') {
|
|
432
|
-
stream = data;
|
|
433
|
-
packet.noData = true;
|
|
434
|
-
} else {
|
|
435
|
-
// Make sure the data is converted to client-side friendly code
|
|
436
|
-
data = JSON.clone(data, 'toHawkejs');
|
|
437
|
-
|
|
438
|
-
// Now create a DRY object
|
|
439
|
-
packet.data = JSON.toDryObject(data);
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
packet.id = 's' + (++this.counter);
|
|
443
|
-
|
|
444
|
-
if (typeof callback == 'function') {
|
|
445
|
-
this.callbacks[packet.id] = callback;
|
|
446
|
-
packet.respond = true;
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
if (stream) {
|
|
450
|
-
// Emit on the IOStream socket
|
|
451
|
-
return this.stream.emit('payload', stream, packet);
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
this.socket.emit('payload', packet);
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
/**
|
|
458
|
-
* Create a stream we can send through a websocket connection
|
|
459
|
-
*
|
|
460
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
461
|
-
* @since 0.2.0
|
|
462
|
-
* @version 0.2.0
|
|
463
|
-
*/
|
|
464
|
-
SocketConduit.setMethod(function createStream() {
|
|
465
|
-
return iostream.createStream();
|
|
466
|
-
});
|
|
467
|
-
|
|
468
|
-
/**
|
|
469
|
-
* Create a linkup to the client
|
|
470
|
-
*
|
|
471
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
472
|
-
* @since 0.2.0
|
|
473
|
-
* @version 0.2.0
|
|
474
|
-
*/
|
|
475
|
-
SocketConduit.setMethod(function linkup(type, data, cb) {
|
|
476
|
-
|
|
477
|
-
var client,
|
|
478
|
-
id;
|
|
479
|
-
|
|
480
|
-
if (typeof data == 'function') {
|
|
481
|
-
cb = data;
|
|
482
|
-
data = {};
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
id = type + '-' + Crypto.pseudoHex();
|
|
486
|
-
client = new Linkup(this, type, id, data);
|
|
487
|
-
|
|
488
|
-
this.socket.emit('linkup', {type: type, id: id, data: data});
|
|
489
|
-
|
|
490
|
-
if (cb) {
|
|
491
|
-
client.once('ready', function whenReady() {
|
|
492
|
-
cb.call(client, client);
|
|
493
|
-
});
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
return client;
|
|
497
|
-
});
|
|
498
|
-
|
|
499
|
-
/**
|
|
500
|
-
* The Linkup class
|
|
501
|
-
*
|
|
502
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
503
|
-
* @since 0.2.0
|
|
504
|
-
* @version 0.2.0
|
|
505
|
-
*
|
|
506
|
-
* @param {SocketConduit} conduit
|
|
507
|
-
* @param {String} type
|
|
508
|
-
* @param {String} id
|
|
509
|
-
* @param {Object} data
|
|
510
|
-
*/
|
|
511
|
-
var Linkup = Function.inherits('Informer', function Linkup(conduit, type, id, data) {
|
|
512
|
-
|
|
513
|
-
// The socket conduit
|
|
514
|
-
this.conduit = conduit;
|
|
515
|
-
|
|
516
|
-
// The typename
|
|
517
|
-
this.type = type;
|
|
518
|
-
|
|
519
|
-
// The unique identifier, as created by the client
|
|
520
|
-
this.id = id;
|
|
521
|
-
|
|
522
|
-
// The initial data
|
|
523
|
-
this.initialData = data;
|
|
524
|
-
|
|
525
|
-
// Make it store itself
|
|
526
|
-
conduit.linkups[id] = this;
|
|
527
|
-
|
|
528
|
-
// Tell the client it's ready
|
|
529
|
-
this.submit('ready');
|
|
530
|
-
});
|
|
531
|
-
|
|
532
|
-
/**
|
|
533
|
-
* Destroy this link
|
|
534
|
-
*
|
|
535
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
536
|
-
* @since 0.2.0
|
|
537
|
-
* @version 1.1.4
|
|
538
|
-
*/
|
|
539
|
-
Linkup.setMethod(function destroy() {
|
|
540
|
-
|
|
541
|
-
// Remove all listeners
|
|
542
|
-
this.removeAllListeners();
|
|
543
|
-
|
|
544
|
-
// Remove if from the linkups list
|
|
545
|
-
delete this.conduit.linkups[this.id];
|
|
546
|
-
});
|
|
547
|
-
|
|
548
|
-
/**
|
|
549
|
-
* Submit a message to the client
|
|
550
|
-
*
|
|
551
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
552
|
-
* @since 0.2.0
|
|
553
|
-
* @version 0.2.0
|
|
554
|
-
*
|
|
555
|
-
* @param {String} type
|
|
556
|
-
* @param {Object} data
|
|
557
|
-
* @param {IOStream} stream
|
|
558
|
-
* @param {Function} callback
|
|
559
|
-
*/
|
|
560
|
-
Linkup.setMethod(function submit(type, data, stream, callback) {
|
|
561
|
-
this.conduit.submit([this.id, type], data, stream, callback);
|
|
562
|
-
});
|
|
563
|
-
|
|
564
|
-
/**
|
|
565
|
-
* Submit a message to the server on this link and return a promise
|
|
566
|
-
*
|
|
567
|
-
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
568
|
-
* @since 1.1.2
|
|
569
|
-
* @version 1.1.2
|
|
570
|
-
*
|
|
571
|
-
* @param {String} type
|
|
572
|
-
* @param {Object} data
|
|
573
|
-
*/
|
|
574
|
-
Linkup.setMethod(function demand(type, data, stream) {
|
|
575
|
-
|
|
576
|
-
const that = this;
|
|
577
|
-
|
|
578
|
-
let pledge = new Classes.Pledge(),
|
|
579
|
-
args = [type, data];
|
|
580
|
-
|
|
581
|
-
if (stream) {
|
|
582
|
-
args.push(stream);
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
this.submit(...args, function done(err, result) {
|
|
586
|
-
|
|
587
|
-
if (err) {
|
|
588
|
-
return pledge.reject(err);
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
pledge.resolve(result);
|
|
592
|
-
});
|
|
593
|
-
|
|
594
|
-
return pledge;
|
|
595
|
-
});
|
|
596
|
-
|
|
597
|
-
/**
|
|
598
|
-
* Create a stream
|
|
599
|
-
*
|
|
600
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
601
|
-
* @since 0.2.0
|
|
602
|
-
* @version 0.2.0
|
|
603
|
-
*/
|
|
604
|
-
Linkup.setMethod(function createStream() {
|
|
605
|
-
return this.conduit.createStream();
|
|
606
|
-
});
|
|
607
|
-
|
|
608
|
-
/**
|
|
609
|
-
* Send an error to the client
|
|
610
|
-
*
|
|
611
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
612
|
-
* @since 0.2.0
|
|
613
|
-
* @version 0.2.0
|
|
614
|
-
*
|
|
615
|
-
* @param {Error} err
|
|
616
|
-
* @param {Function} callback
|
|
617
|
-
*/
|
|
618
|
-
Linkup.setMethod(function error(err, callback) {
|
|
619
|
-
console.log('Error:', err);
|
|
620
|
-
this.submit('error', {stack: err.stack, message: err.message}, callback);
|
|
1
|
+
var iostream = alchemy.use('socket.io-stream'),
|
|
2
|
+
libstream = require('stream');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The Socket Conduit Class
|
|
6
|
+
*
|
|
7
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
8
|
+
* @since 0.2.0
|
|
9
|
+
* @version 1.1.3
|
|
10
|
+
*
|
|
11
|
+
* @param {Socker} socket
|
|
12
|
+
* @param {Object} announcement
|
|
13
|
+
*/
|
|
14
|
+
var SocketConduit = Function.inherits('Alchemy.Conduit', function Socket(socket, announcement) {
|
|
15
|
+
|
|
16
|
+
var that = this;
|
|
17
|
+
|
|
18
|
+
// Indicate this is a websocket
|
|
19
|
+
this.websocket = socket;
|
|
20
|
+
this.socket = socket;
|
|
21
|
+
|
|
22
|
+
// Store the headers
|
|
23
|
+
this.headers = socket.handshake.headers;
|
|
24
|
+
|
|
25
|
+
// Store the announcement data
|
|
26
|
+
this.announcement = announcement;
|
|
27
|
+
|
|
28
|
+
// Detect node clients
|
|
29
|
+
if (this.headers['user-agent'] == 'node-XMLHttpRequest') {
|
|
30
|
+
this.isNodeClient = true;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Cookies to send to the client
|
|
34
|
+
this.new_cookies = {};
|
|
35
|
+
this.new_cookie_header = [];
|
|
36
|
+
|
|
37
|
+
// The amount of submissions
|
|
38
|
+
this.counter = 0;
|
|
39
|
+
|
|
40
|
+
// Collection of callbacks
|
|
41
|
+
this.callbacks = {};
|
|
42
|
+
|
|
43
|
+
// Create a stream instance
|
|
44
|
+
this.stream = iostream(socket);
|
|
45
|
+
|
|
46
|
+
// Linkup storage
|
|
47
|
+
this.linkups = {};
|
|
48
|
+
|
|
49
|
+
this.parseAnnouncement();
|
|
50
|
+
if (!this.isNodeClient) this.parseRequest();
|
|
51
|
+
|
|
52
|
+
// Listen for payloads
|
|
53
|
+
socket.on('payload', function onPayload(packet) {
|
|
54
|
+
that.onPayload(packet);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Listen for responses
|
|
58
|
+
socket.on('response', function onResponse(packet) {
|
|
59
|
+
that.onResponse(packet);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Listen for linkups
|
|
63
|
+
socket.on('linkup', function onLinkup(packet) {
|
|
64
|
+
that.onLinkup(packet);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Listen for disconnects
|
|
68
|
+
socket.on('disconnect', function onDisconnect() {
|
|
69
|
+
|
|
70
|
+
var key;
|
|
71
|
+
|
|
72
|
+
that.emit('disconnect');
|
|
73
|
+
|
|
74
|
+
for (key in that.linkups) {
|
|
75
|
+
that.linkups[key].emit('disconnect');
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Listen for payloads on the stream socket
|
|
80
|
+
this.stream.on('payload', function onStreamPayload(stream, packet) {
|
|
81
|
+
packet.stream = stream;
|
|
82
|
+
that.onPayload(packet);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Listen for responses on the stream socket
|
|
86
|
+
this.stream.on('response', function onStreamResponse(stream, data) {
|
|
87
|
+
packet.stream = stream;
|
|
88
|
+
that.onPayload(packet);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Listen to data subscriptions
|
|
92
|
+
this.on('subscribe-data', function gotSubscriptionRequest(arr) {
|
|
93
|
+
|
|
94
|
+
if (!Array.isArray(arr)) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
that.registerBindings(arr);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Return the client IP address
|
|
104
|
+
*
|
|
105
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
106
|
+
* @since 0.2.1
|
|
107
|
+
* @version 0.2.1
|
|
108
|
+
*/
|
|
109
|
+
SocketConduit.setProperty(function ip() {
|
|
110
|
+
|
|
111
|
+
var sock = this.socket;
|
|
112
|
+
|
|
113
|
+
if (!sock) {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return sock.conn.remoteAddress || null;
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Parse the request, get information from the url
|
|
122
|
+
*
|
|
123
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
124
|
+
* @since 0.2.0
|
|
125
|
+
* @version 0.2.0
|
|
126
|
+
*/
|
|
127
|
+
SocketConduit.setMethod(function parseRequest() {
|
|
128
|
+
this.parseShortcuts();
|
|
129
|
+
this.parseLanguages();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Handle a client announcement
|
|
134
|
+
*
|
|
135
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
136
|
+
* @since 0.2.0
|
|
137
|
+
* @version 1.1.0
|
|
138
|
+
*
|
|
139
|
+
* @param {Object} data
|
|
140
|
+
*/
|
|
141
|
+
SocketConduit.setMethod(function parseAnnouncement() {
|
|
142
|
+
|
|
143
|
+
var connections,
|
|
144
|
+
data = this.announcement;
|
|
145
|
+
|
|
146
|
+
// Store the scene information
|
|
147
|
+
this.connection_type = data.connection_type;
|
|
148
|
+
this.last_update = data.last_update || Date.now();
|
|
149
|
+
this.scene_id = data.scene;
|
|
150
|
+
|
|
151
|
+
// Register the connection in the user's session
|
|
152
|
+
this.getSession().registerConnection(this);
|
|
153
|
+
|
|
154
|
+
// Tell the client we're ready
|
|
155
|
+
this.websocket.emit('ready');
|
|
156
|
+
|
|
157
|
+
if (alchemy.settings.debug) {
|
|
158
|
+
log.info('Established websocket connection to', this.ip, 'using', this.useragent.family, this.useragent.major);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Handle an incoming linkup
|
|
164
|
+
*
|
|
165
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
166
|
+
* @since 0.2.0
|
|
167
|
+
* @version 0.4.0
|
|
168
|
+
*
|
|
169
|
+
* @param {Object} packet
|
|
170
|
+
*/
|
|
171
|
+
SocketConduit.setMethod(function onLinkup(packet) {
|
|
172
|
+
|
|
173
|
+
var controller,
|
|
174
|
+
instance,
|
|
175
|
+
section,
|
|
176
|
+
linkup,
|
|
177
|
+
router,
|
|
178
|
+
action,
|
|
179
|
+
split,
|
|
180
|
+
type,
|
|
181
|
+
temp,
|
|
182
|
+
fnc,
|
|
183
|
+
id;
|
|
184
|
+
|
|
185
|
+
temp = packet.type;
|
|
186
|
+
|
|
187
|
+
// Make sure a type is defined
|
|
188
|
+
if (!temp) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
id = packet.id;
|
|
193
|
+
|
|
194
|
+
// Split by at symbol to get the optional sub section
|
|
195
|
+
temp = temp.split('@');
|
|
196
|
+
|
|
197
|
+
if (temp.length == 2) {
|
|
198
|
+
section = temp[0];
|
|
199
|
+
type = temp[1];
|
|
200
|
+
|
|
201
|
+
router = Router.subSections[section];
|
|
202
|
+
|
|
203
|
+
if (!router) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
} else {
|
|
208
|
+
type = temp[0];
|
|
209
|
+
router = Router;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// See if any socket routes have been set
|
|
213
|
+
if (router.linkupRoutes[type]) {
|
|
214
|
+
fnc = router.linkupRoutes[type];
|
|
215
|
+
|
|
216
|
+
// Create the new linkup instance
|
|
217
|
+
linkup = new Linkup(this, type, id, packet.data);
|
|
218
|
+
|
|
219
|
+
if (typeof fnc === 'function') {
|
|
220
|
+
fnc.call(this, this, linkup);
|
|
221
|
+
} else if (typeof fnc === 'string') { // Strings like 'StaticController#index'
|
|
222
|
+
|
|
223
|
+
split = fnc.split('#');
|
|
224
|
+
controller = split[0];
|
|
225
|
+
action = split[1];
|
|
226
|
+
|
|
227
|
+
instance = Controller.get(controller, this);
|
|
228
|
+
instance.packetType = packet.type;
|
|
229
|
+
instance.doAction(action, [this, linkup, packet.data]);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Emit it on the socket itself
|
|
234
|
+
this.emit(packet.type, linkup, null);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Handle a client packet
|
|
239
|
+
*
|
|
240
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
241
|
+
* @since 0.2.0
|
|
242
|
+
* @version 1.1.4
|
|
243
|
+
*
|
|
244
|
+
* @param {Object} packet
|
|
245
|
+
*/
|
|
246
|
+
SocketConduit.setMethod(function onPayload(packet) {
|
|
247
|
+
|
|
248
|
+
var that = this,
|
|
249
|
+
controller,
|
|
250
|
+
instance,
|
|
251
|
+
respond,
|
|
252
|
+
action,
|
|
253
|
+
linkup,
|
|
254
|
+
route,
|
|
255
|
+
split,
|
|
256
|
+
args,
|
|
257
|
+
fnc;
|
|
258
|
+
|
|
259
|
+
if (packet.respond) {
|
|
260
|
+
respond = function respondToPayload(err, data, stream) {
|
|
261
|
+
|
|
262
|
+
var responsePacket = {
|
|
263
|
+
err : null,
|
|
264
|
+
respond_to : packet.id,
|
|
265
|
+
noData : false,
|
|
266
|
+
data : null
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
if (err) {
|
|
270
|
+
err = JSON.clone(err, 'toHawkejs');
|
|
271
|
+
responsePacket.err = JSON.toDryObject(err);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (data && data.constructor.name == 'IOStream') {
|
|
275
|
+
responsePacket.noData = true;
|
|
276
|
+
stream = data;
|
|
277
|
+
} else {
|
|
278
|
+
// Make sure the data is converted to client-side friendly code
|
|
279
|
+
data = JSON.clone(data, 'toHawkejs');
|
|
280
|
+
|
|
281
|
+
// Now create a DRY object
|
|
282
|
+
responsePacket.data = JSON.toDryObject(data);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (stream && stream.constructor.name == 'IOStream') {
|
|
286
|
+
return that.stream.emit('response', stream, responsePacket);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
that.socket.emit('response', responsePacket);
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// See if this is for a specific linkup
|
|
294
|
+
if (packet.link) {
|
|
295
|
+
linkup = this.linkups[packet.link];
|
|
296
|
+
|
|
297
|
+
// Do NOT allow packets of the 'linkup_packet' type,
|
|
298
|
+
// we use that internally for the entire packet
|
|
299
|
+
if (packet.type == 'linkup_packet') {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (linkup) {
|
|
304
|
+
|
|
305
|
+
let test = linkup.simpleListeners.get(packet.type);
|
|
306
|
+
|
|
307
|
+
if (!test) {
|
|
308
|
+
console.error('Linkup', linkup, 'has no listener for', packet.type);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (packet.stream) {
|
|
312
|
+
linkup.emit(packet.type, packet.data, packet.stream, respond, null);
|
|
313
|
+
} else {
|
|
314
|
+
linkup.emit(packet.type, packet.data, respond, null);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Also emit as a linkup_packet
|
|
318
|
+
linkup.emit('linkup_packet', packet, respond, null);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Normalize data-server-events
|
|
325
|
+
if (packet.type == 'data-server-event') {
|
|
326
|
+
|
|
327
|
+
// See if data is valid, otherwise do nothing
|
|
328
|
+
if (!packet.data || !packet.data[0]) {
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Get the real type
|
|
333
|
+
packet.type = packet.data[0];
|
|
334
|
+
|
|
335
|
+
// Store the rest as arguments
|
|
336
|
+
packet.args = packet.data.slice(1);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// See if any socket routes have been set
|
|
340
|
+
if (Router.socketRoutes[packet.type]) {
|
|
341
|
+
route = Router.socketRoutes[packet.type];
|
|
342
|
+
|
|
343
|
+
if (packet.args) {
|
|
344
|
+
args = packet.args;
|
|
345
|
+
|
|
346
|
+
// Add "respond" callback to the bottom
|
|
347
|
+
args.push(respond);
|
|
348
|
+
} else if (packet.stream) {
|
|
349
|
+
args = [packet.data, packet.stream, respond];
|
|
350
|
+
} else {
|
|
351
|
+
args = [packet.data, respond];
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
route.callHandler(this, args);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Emit it on the socket itself
|
|
358
|
+
if (packet.stream) {
|
|
359
|
+
that.emit(packet.type, packet.data, packet.stream, respond, null);
|
|
360
|
+
} else {
|
|
361
|
+
that.emit(packet.type, packet.data, respond, null);
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Handle a client response
|
|
367
|
+
*
|
|
368
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
369
|
+
* @since 0.2.0
|
|
370
|
+
* @version 0.2.0
|
|
371
|
+
*
|
|
372
|
+
* @param {Object} packet
|
|
373
|
+
*/
|
|
374
|
+
SocketConduit.setMethod(function onResponse(packet) {
|
|
375
|
+
|
|
376
|
+
if (typeof this.callbacks[packet.respond_to] === 'function') {
|
|
377
|
+
this.callbacks[packet.respond_to](packet.err, packet.data);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
delete this.callbacks[packet.respond_to];
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Submit a message to the client
|
|
385
|
+
*
|
|
386
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
387
|
+
* @since 0.0.1
|
|
388
|
+
* @version 1.0.5
|
|
389
|
+
*
|
|
390
|
+
* @param {String} type
|
|
391
|
+
* @param {Object} data
|
|
392
|
+
* @param {IOStream} stream
|
|
393
|
+
* @param {Function} callback
|
|
394
|
+
*/
|
|
395
|
+
SocketConduit.setMethod(function submit(type, data, stream, callback) {
|
|
396
|
+
|
|
397
|
+
var packet = {},
|
|
398
|
+
regular_stream;
|
|
399
|
+
|
|
400
|
+
if (alchemy.isStream(data)) {
|
|
401
|
+
callback = stream;
|
|
402
|
+
stream = data;
|
|
403
|
+
data = undefined;
|
|
404
|
+
} else if (typeof data === 'function') {
|
|
405
|
+
callback = data;
|
|
406
|
+
data = undefined;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (!stream || typeof stream == 'function') {
|
|
410
|
+
callback = stream;
|
|
411
|
+
stream = undefined;
|
|
412
|
+
} else if (stream && stream.constructor.name != 'IOStream') {
|
|
413
|
+
|
|
414
|
+
// Keep the regular stream
|
|
415
|
+
regular_stream = stream;
|
|
416
|
+
|
|
417
|
+
// Create an IOStream
|
|
418
|
+
stream = this.createStream();
|
|
419
|
+
|
|
420
|
+
// Pipe the regular stream into the IOStream
|
|
421
|
+
regular_stream.pipe(stream);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (Array.isArray(type)) {
|
|
425
|
+
packet.link = type[0];
|
|
426
|
+
packet.type = type[1];
|
|
427
|
+
} else {
|
|
428
|
+
packet.type = type;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
if (data && data.constructor.name == 'IOStream') {
|
|
432
|
+
stream = data;
|
|
433
|
+
packet.noData = true;
|
|
434
|
+
} else {
|
|
435
|
+
// Make sure the data is converted to client-side friendly code
|
|
436
|
+
data = JSON.clone(data, 'toHawkejs');
|
|
437
|
+
|
|
438
|
+
// Now create a DRY object
|
|
439
|
+
packet.data = JSON.toDryObject(data);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
packet.id = 's' + (++this.counter);
|
|
443
|
+
|
|
444
|
+
if (typeof callback == 'function') {
|
|
445
|
+
this.callbacks[packet.id] = callback;
|
|
446
|
+
packet.respond = true;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
if (stream) {
|
|
450
|
+
// Emit on the IOStream socket
|
|
451
|
+
return this.stream.emit('payload', stream, packet);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
this.socket.emit('payload', packet);
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Create a stream we can send through a websocket connection
|
|
459
|
+
*
|
|
460
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
461
|
+
* @since 0.2.0
|
|
462
|
+
* @version 0.2.0
|
|
463
|
+
*/
|
|
464
|
+
SocketConduit.setMethod(function createStream() {
|
|
465
|
+
return iostream.createStream();
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Create a linkup to the client
|
|
470
|
+
*
|
|
471
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
472
|
+
* @since 0.2.0
|
|
473
|
+
* @version 0.2.0
|
|
474
|
+
*/
|
|
475
|
+
SocketConduit.setMethod(function linkup(type, data, cb) {
|
|
476
|
+
|
|
477
|
+
var client,
|
|
478
|
+
id;
|
|
479
|
+
|
|
480
|
+
if (typeof data == 'function') {
|
|
481
|
+
cb = data;
|
|
482
|
+
data = {};
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
id = type + '-' + Crypto.pseudoHex();
|
|
486
|
+
client = new Linkup(this, type, id, data);
|
|
487
|
+
|
|
488
|
+
this.socket.emit('linkup', {type: type, id: id, data: data});
|
|
489
|
+
|
|
490
|
+
if (cb) {
|
|
491
|
+
client.once('ready', function whenReady() {
|
|
492
|
+
cb.call(client, client);
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
return client;
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* The Linkup class
|
|
501
|
+
*
|
|
502
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
503
|
+
* @since 0.2.0
|
|
504
|
+
* @version 0.2.0
|
|
505
|
+
*
|
|
506
|
+
* @param {SocketConduit} conduit
|
|
507
|
+
* @param {String} type
|
|
508
|
+
* @param {String} id
|
|
509
|
+
* @param {Object} data
|
|
510
|
+
*/
|
|
511
|
+
var Linkup = Function.inherits('Informer', function Linkup(conduit, type, id, data) {
|
|
512
|
+
|
|
513
|
+
// The socket conduit
|
|
514
|
+
this.conduit = conduit;
|
|
515
|
+
|
|
516
|
+
// The typename
|
|
517
|
+
this.type = type;
|
|
518
|
+
|
|
519
|
+
// The unique identifier, as created by the client
|
|
520
|
+
this.id = id;
|
|
521
|
+
|
|
522
|
+
// The initial data
|
|
523
|
+
this.initialData = data;
|
|
524
|
+
|
|
525
|
+
// Make it store itself
|
|
526
|
+
conduit.linkups[id] = this;
|
|
527
|
+
|
|
528
|
+
// Tell the client it's ready
|
|
529
|
+
this.submit('ready');
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Destroy this link
|
|
534
|
+
*
|
|
535
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
536
|
+
* @since 0.2.0
|
|
537
|
+
* @version 1.1.4
|
|
538
|
+
*/
|
|
539
|
+
Linkup.setMethod(function destroy() {
|
|
540
|
+
|
|
541
|
+
// Remove all listeners
|
|
542
|
+
this.removeAllListeners();
|
|
543
|
+
|
|
544
|
+
// Remove if from the linkups list
|
|
545
|
+
delete this.conduit.linkups[this.id];
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Submit a message to the client
|
|
550
|
+
*
|
|
551
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
552
|
+
* @since 0.2.0
|
|
553
|
+
* @version 0.2.0
|
|
554
|
+
*
|
|
555
|
+
* @param {String} type
|
|
556
|
+
* @param {Object} data
|
|
557
|
+
* @param {IOStream} stream
|
|
558
|
+
* @param {Function} callback
|
|
559
|
+
*/
|
|
560
|
+
Linkup.setMethod(function submit(type, data, stream, callback) {
|
|
561
|
+
this.conduit.submit([this.id, type], data, stream, callback);
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Submit a message to the server on this link and return a promise
|
|
566
|
+
*
|
|
567
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
568
|
+
* @since 1.1.2
|
|
569
|
+
* @version 1.1.2
|
|
570
|
+
*
|
|
571
|
+
* @param {String} type
|
|
572
|
+
* @param {Object} data
|
|
573
|
+
*/
|
|
574
|
+
Linkup.setMethod(function demand(type, data, stream) {
|
|
575
|
+
|
|
576
|
+
const that = this;
|
|
577
|
+
|
|
578
|
+
let pledge = new Classes.Pledge(),
|
|
579
|
+
args = [type, data];
|
|
580
|
+
|
|
581
|
+
if (stream) {
|
|
582
|
+
args.push(stream);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
this.submit(...args, function done(err, result) {
|
|
586
|
+
|
|
587
|
+
if (err) {
|
|
588
|
+
return pledge.reject(err);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
pledge.resolve(result);
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
return pledge;
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Create a stream
|
|
599
|
+
*
|
|
600
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
601
|
+
* @since 0.2.0
|
|
602
|
+
* @version 0.2.0
|
|
603
|
+
*/
|
|
604
|
+
Linkup.setMethod(function createStream() {
|
|
605
|
+
return this.conduit.createStream();
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* Send an error to the client
|
|
610
|
+
*
|
|
611
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
612
|
+
* @since 0.2.0
|
|
613
|
+
* @version 0.2.0
|
|
614
|
+
*
|
|
615
|
+
* @param {Error} err
|
|
616
|
+
* @param {Function} callback
|
|
617
|
+
*/
|
|
618
|
+
Linkup.setMethod(function error(err, callback) {
|
|
619
|
+
console.log('Error:', err);
|
|
620
|
+
this.submit('error', {stack: err.stack, message: err.message}, callback);
|
|
621
621
|
});
|