total5 0.0.1 → 0.0.2

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.
@@ -0,0 +1,3352 @@
1
+ // Total.js FlowStream module
2
+ // The MIT License
3
+ // Copyright 2021-2023 (c) Peter Širka <petersirka@gmail.com>
4
+
5
+ 'use strict';
6
+
7
+ if (!global.F)
8
+ require('./index');
9
+
10
+ const W = F.Worker;
11
+ const Fork = F.Child.fork;
12
+ const VERSION = 32;
13
+ const NOTIFYPATH = '/notify/';
14
+
15
+ var isFLOWSTREAMWORKER = false;
16
+ var Parent = W.parentPort;
17
+ var CALLBACKS = {};
18
+ var FLOWS = {};
19
+ var PROXIES = {};
20
+ var TMS = {};
21
+ var RPC = {};
22
+ var CALLBACKID = 1;
23
+ var ASFILES = true;
24
+ var isrunning = false;
25
+
26
+ /*
27
+ var instance = MODULE('flowstream').init({ components: {}, design: {}, variables: {}, variables2: {} }, true/false);
28
+
29
+ Module exports:
30
+ module.init(meta [isworker]);
31
+ module.socket(meta, socket, check(client) => true);
32
+ module.input([flowstreamid], [id], data);
33
+ module.trigger(flowstreamid, id, data);
34
+ module.refresh([flowstreamid], [type]);
35
+ module.rpc(name, callback);
36
+ module.exec(id, opt);
37
+
38
+ Methods:
39
+ instance.trigger(id, data);
40
+ instance.destroy();
41
+ instance.input([flowstreamid], [fromid], [toid], data);
42
+ instance.add(id, body, [callback]);
43
+ instance.rem(id, [callback]);
44
+ instance.components(callback);
45
+ instance.refresh([type]);
46
+ instance.io(callback);
47
+ instance.ioread(flowstreamid, id, callback);
48
+ instance.reconfigure(id, config);
49
+ instance.variables(variables);
50
+ instance.variables2(variables);
51
+ instance.pause(is);
52
+ instance.socket(socket);
53
+ instance.exec(opt, callback);
54
+ instance.cmd(path, data);
55
+ instance.httprequest(opt, callback);
56
+ instance.eval(msg, callback);
57
+
58
+ Delegates:
59
+ instance.onsave(data);
60
+ instance.ondone();
61
+ instance.onerror(err, type, instanceid, componentid);
62
+ instance.output(fid, data, tfsid, tid);
63
+ instance.onhttproute(url, remove);
64
+ instance.ondestroy();
65
+
66
+ Extended Flow instances by:
67
+ instance.save();
68
+ instance.toinput(data, [flowstreamid], [id]);
69
+ instance.output(data, [flowstreamid], [id]);
70
+ instance.reconfigure(config);
71
+ instance.newflowstream(meta, isworker);
72
+ instance.input = function(data) {}
73
+ */
74
+
75
+ function Instance(instance, id) {
76
+ var self = this;
77
+ self.httproutes = {};
78
+ self.version = VERSION;
79
+ self.id = id;
80
+ self.flow = instance;
81
+ // this.onoutput = null;
82
+ }
83
+
84
+ Instance.prototype = {
85
+
86
+ get stats() {
87
+ return this.worker ? this.worker.stats : this.flow.stats;
88
+ },
89
+
90
+ get worker() {
91
+ return this.flow;
92
+ },
93
+
94
+ get stream() {
95
+ return this.flow;
96
+ }
97
+ };
98
+
99
+ Instance.prototype.postMessage = function(msg) {
100
+ this.flow.postMessage && this.flow.postMessage(msg);
101
+ };
102
+
103
+ Instance.prototype.httprequest = function(opt, callback) {
104
+
105
+ // opt.route {String} a URL address
106
+ // opt.params {Object}
107
+ // opt.query {Object}
108
+ // opt.body {Object}
109
+ // opt.headers {Object}
110
+ // opt.files {Array Object}
111
+ // opt.url {String}
112
+ // opt.callback {Function(err, meta)}
113
+
114
+ if (opt.callback) {
115
+ callback = opt.callback;
116
+ opt.callback = undefined;
117
+ }
118
+
119
+ var self = this;
120
+ if (self.flow.isworkerthread) {
121
+ var callbackid = callback ? (CALLBACKID++) : -1;
122
+ if (callbackid !== -1)
123
+ CALLBACKS[callbackid] = { id: self.flow.id, callback: callback };
124
+ self.flow.postMessage2({ TYPE: 'stream/httprequest', data: opt, callbackid: callbackid });
125
+ } else
126
+ httprequest(self.flow, opt, callback);
127
+
128
+ return self;
129
+ };
130
+
131
+ // Can't be used in the FlowStream component
132
+ Instance.prototype.httprouting = function() {
133
+
134
+ var instance = this;
135
+
136
+ instance.onhttproute = function(url, remove) {
137
+
138
+ // GET / #upload #5000 #10000
139
+ // - #flag means a flag name
140
+ // - first #number is timeout
141
+ // - second #number is max. limit for the payload
142
+
143
+ var flags = [];
144
+ var limit = 0;
145
+ var timeout = 0;
146
+ var id = url;
147
+
148
+ url = url.replace(/#[a-z0-9]+/g, function(text) {
149
+ text = text.substring(1);
150
+ if ((/^\d+$/).test(text)) {
151
+ if (timeout)
152
+ limit = +text;
153
+ else
154
+ timeout = +text;
155
+ } else
156
+ flags.push(text);
157
+ return '';
158
+ }).trim();
159
+
160
+ var route;
161
+
162
+ if (remove) {
163
+ route = instance.httproutes[id];
164
+ if (route) {
165
+ route.remove();
166
+ delete instance.httproutes[id];
167
+ }
168
+ return;
169
+ }
170
+
171
+ if (instance.httproutes[id])
172
+ instance.httproutes[id].remove();
173
+
174
+ if (timeout)
175
+ flags.push(timeout);
176
+
177
+ route = ROUTE(url, function() {
178
+
179
+ var self = this;
180
+ var opt = {};
181
+
182
+ opt.route = id;
183
+ opt.params = self.params;
184
+ opt.query = self.query;
185
+ opt.body = self.body;
186
+ opt.files = self.files;
187
+ opt.headers = self.headers;
188
+ opt.url = self.url;
189
+ opt.ip = self.ip;
190
+ opt.cookies = {};
191
+
192
+ var cookie = self.headers.cookie;
193
+ if (cookie) {
194
+ var arr = cookie.split(';');
195
+ for (var i = 0; i < arr.length; i++) {
196
+ var line = arr[i].trim();
197
+ var index = line.indexOf('=');
198
+ if (index !== -1) {
199
+ try {
200
+ opt.cookies[line.substring(0, index)] = decodeURIComponent(line.substring(index + 1));
201
+ } catch (e) {}
202
+ }
203
+ }
204
+ }
205
+
206
+ instance.httprequest(opt, function(meta) {
207
+
208
+ if (meta.status)
209
+ self.status = meta.status;
210
+
211
+ if (meta.headers) {
212
+ for (var key in meta.headers)
213
+ self.response.headers[key] = meta.headers[key];
214
+ }
215
+
216
+ if (meta.cookies && meta.cookies instanceof Array) {
217
+ for (var item of meta.cookies) {
218
+ var name = item.name || item.id;
219
+ var value = item.value;
220
+ var expiration = item.expiration || item.expires || item.expire;
221
+ if (name && value && expiration)
222
+ self.cookie(name, value, expiration, item.options || item.config);
223
+ }
224
+ }
225
+
226
+ var data = meta.body || meta.data || meta.payload;
227
+ switch (meta.type) {
228
+ case 'error':
229
+ self.invalid(meta.body);
230
+ break;
231
+ case 'text':
232
+ case 'plain':
233
+ self.text(data);
234
+ break;
235
+ case 'html':
236
+ self.html(data);
237
+ break;
238
+ case 'xml':
239
+ self.binary(Buffer.from(data, 'utf8'), 'text/xml');
240
+ break;
241
+ case 'json':
242
+ self.json(data);
243
+ break;
244
+ case 'empty':
245
+ self.empty();
246
+ break;
247
+ default:
248
+ if (meta.filename) {
249
+ var stream = F.Fs.createReadStream(meta.filename);
250
+ self.stream(stream, meta.type, meta.download);
251
+ meta.remove && F.cleanup(stream, () => F.Fs.unlink(meta.filename, NOOP));
252
+ } else {
253
+ if (typeof(data) === 'string')
254
+ self.binary(Buffer.from(data, 'base64'), meta.type);
255
+ else
256
+ self.json(data);
257
+ }
258
+ break;
259
+ }
260
+
261
+ }, flags, limit);
262
+ });
263
+
264
+ instance.httproutes[id] = route;
265
+ };
266
+
267
+ return instance;
268
+ };
269
+
270
+ Instance.prototype.cmd = function(path, data) {
271
+
272
+ var self = this;
273
+ if (self.flow.isworkerthread) {
274
+ self.flow.postMessage2({ TYPE: 'stream/cmd', path: path, data: data });
275
+ } else {
276
+ var fn = path.indexOf('.') === - 1 ? global[path] : F.TUtils.get(global, path);
277
+ if (typeof(fn) === 'function')
278
+ fn(data);
279
+ }
280
+
281
+ return self;
282
+ };
283
+
284
+ Instance.prototype.send = function(id, data, callback) {
285
+ var self = this;
286
+ if (self.flow.isworkerthread) {
287
+ var callbackid = callback ? (CALLBACKID++) : -1;
288
+ if (callbackid !== -1)
289
+ CALLBACKS[callbackid] = { id: self.flow.id, callback: callback };
290
+ self.flow.postMessage2({ TYPE: 'stream/send', id: id, data: data, callbackid: callbackid });
291
+ } else
292
+ send(self.flow, id, data, callback);
293
+ };
294
+
295
+ Instance.prototype.exec = function(opt, callback) {
296
+
297
+ // opt.id = instance_ID
298
+ // opt.callback = function(err, msg)
299
+ // opt.uid = String/Number; --> returned back
300
+ // opt.ref = String/Number; --> returned back
301
+ // opt.repo = {}; --> returned back
302
+ // opt.data = {}; --> returned back
303
+ // opt.vars = {};
304
+ // opt.timeout = Number;
305
+
306
+ if (callback && !opt.callback)
307
+ opt.callback = callback;
308
+
309
+ var self = this;
310
+ if (self.flow.isworkerthread) {
311
+ var callbackid = opt.callback ? (CALLBACKID++) : -1;
312
+ if (callbackid !== -1)
313
+ CALLBACKS[callbackid] = { id: self.flow.id, callback: opt.callback };
314
+ self.flow.postMessage2({ TYPE: 'stream/exec', id: opt.id, uid: opt.uid, ref: opt.ref, vars: opt.vars, repo: opt.repo, data: opt.data, timeout: opt.timeout, callbackid: callbackid });
315
+ } else
316
+ exec(self.flow, opt);
317
+
318
+ return self;
319
+ };
320
+
321
+ function execfn(self, name, id, data) {
322
+ var flow = self.flow;
323
+ if (flow.isworkerthread)
324
+ flow.postMessage2({ TYPE: 'stream/' + name, id: id, data: data });
325
+ else {
326
+ if (!flow.paused) {
327
+ if (id[0] === '@') {
328
+ id = id.substring(1);
329
+ for (let key in flow.meta.flow) {
330
+ let com = flow.meta.flow[key];
331
+ if (com.component === id && com[name])
332
+ com[name](data);
333
+ }
334
+ } else if (id[0] === '#') {
335
+ id = id.substring(1);
336
+ for (let key in flow.meta.flow) {
337
+ let com = flow.meta.flow[key];
338
+ if (com.module.name === id && com[name])
339
+ com[name](data);
340
+ }
341
+ } else {
342
+ let com = flow.meta.flow[id];
343
+ if (com && com[name])
344
+ com[name](data);
345
+ }
346
+ }
347
+ }
348
+ }
349
+
350
+ // Performs trigger
351
+ Instance.prototype.trigger = function(id, data) {
352
+ execfn(this, 'trigger', id, data);
353
+ return this;
354
+ };
355
+
356
+ // Notifies instance
357
+ Instance.prototype.notify = function(id, data) {
358
+ execfn(this, 'notify', id, data);
359
+ return this;
360
+ };
361
+
362
+
363
+ // Performs pause
364
+ Instance.prototype.pause = function(is) {
365
+ var self = this;
366
+ var flow = self.flow;
367
+ if (flow.isworkerthread)
368
+ flow.postMessage2({ TYPE: 'stream/pause', is: is });
369
+ else
370
+ flow.pause(is == null ? !flow.paused : is);
371
+ return self;
372
+ };
373
+
374
+ // Asssigns UI websocket the the FlowStream
375
+ Instance.prototype.socket = function(socket) {
376
+ var self = this;
377
+ exports.socket(self.flow, socket);
378
+ return self;
379
+ };
380
+
381
+ Instance.prototype.eval = function(msg, callback) {
382
+ var self = this;
383
+ if (self.flow.isworkerthread) {
384
+ var callbackid = callback ? (CALLBACKID++) : -1;
385
+ if (callback)
386
+ CALLBACKS[callbackid] = { id: self.flow.id, callback: callback };
387
+ self.flow.postMessage2({ TYPE: 'ui/message', data: msg, callbackid: callbackid });
388
+ } else
389
+ self.flow.proxy.message(msg, -1, callback);
390
+ return self;
391
+ };
392
+
393
+ Instance.prototype.restart = function() {
394
+ var self = this;
395
+ self.flow.$socket && self.flow.$socket.destroy();
396
+ self.flow.$client && self.flow.$client.destroy();
397
+ if (self.flow.terminate)
398
+ self.flow.terminate();
399
+ else
400
+ self.flow.kill(9);
401
+ };
402
+
403
+ Instance.prototype.remove = function() {
404
+ F.TFlow.remove(this.id);
405
+ };
406
+
407
+ // Destroys the Flow
408
+ Instance.prototype.kill = Instance.prototype.destroy = function() {
409
+
410
+ var self = this;
411
+
412
+ setTimeout(() => exports.refresh(self.id, 'destroy'), 500);
413
+ self.flow.$destroyed = true;
414
+ self.flow.$terminated = true;
415
+
416
+ if (self.flow.isworkerthread) {
417
+
418
+ self.postMessage({ TYPE: 'stream/destroy' });
419
+ setTimeout(self => self.flow.terminate ? self.flow.terminate() : self.flow.kill(9), 1000, self);
420
+
421
+ if (PROXIES[self.id]) {
422
+ PROXIES[self.id].remove();
423
+ delete PROXIES[self.id];
424
+ }
425
+
426
+ } else {
427
+ if (self.flow.sockets) {
428
+ for (var key in self.flow.sockets)
429
+ self.flow.sockets[key].destroy();
430
+ }
431
+ self.flow.destroy();
432
+ }
433
+
434
+ self.flow.$socket && self.flow.$socket.destroy();
435
+ self.flow.$client && self.flow.$client.destroy();
436
+
437
+ for (var key in CALLBACKS) {
438
+ if (CALLBACKS[key].id === self.id)
439
+ delete CALLBACKS[key];
440
+ }
441
+
442
+ if (self.httproutes) {
443
+ for (var key in self.httproutes)
444
+ self.httproutes[key].remove();
445
+ }
446
+
447
+ self.ondestroy && self.ondestroy();
448
+ delete FLOWS[self.id];
449
+ };
450
+
451
+ // Sends data to the speficic input
452
+ // "@id" sends to all component with "id"
453
+ // "id" sends to instance with "id"
454
+ Instance.prototype.input = function(flowstreamid, fromid, toid, data, reference) {
455
+
456
+ var self = this;
457
+ var flow = self.flow;
458
+
459
+ if (flow.isworkerthread) {
460
+ flow.postMessage2({ TYPE: 'stream/input', flowstreamid: flowstreamid, fromid: fromid, id: toid, data: data, reference: reference });
461
+ return self;
462
+ }
463
+
464
+ if (toid) {
465
+ if (toid[0] === '@') {
466
+ var tmpid = toid.substring(1);
467
+ for (let key in flow.meta.flow) {
468
+ let tmp = flow.meta.flow[key];
469
+ if (tmp.input && tmp.component === tmpid)
470
+ tmp.input(flowstreamid, fromid, data, reference);
471
+ }
472
+ } else {
473
+ let tmp = flow.meta.flow[toid];
474
+ if (tmp) {
475
+ tmp.input && tmp.input(flowstreamid, fromid, data, reference);
476
+ } else {
477
+ for (let key in flow.meta.flow) {
478
+ let tmp = flow.meta.flow[key];
479
+ if (tmp.input && tmp.config.name === toid)
480
+ tmp.input(flowstreamid, fromid, data, reference);
481
+ }
482
+ }
483
+ }
484
+ } else {
485
+ // Send to all inputs
486
+ for (let key in flow.meta.flow) {
487
+ var f = flow.meta.flow[key];
488
+ var c = flow.meta.components[f.component];
489
+ if (f.input && c.type === 'input2')
490
+ f.input(flowstreamid, fromid, data, reference);
491
+ }
492
+ }
493
+
494
+ return self;
495
+ };
496
+
497
+ // Adds a new component
498
+ Instance.prototype.add = function(id, body, callback) {
499
+ var self = this;
500
+ if (self.flow.isworkerthread) {
501
+ var callbackid = callback ? (CALLBACKID++) : -1;
502
+ if (callback)
503
+ CALLBACKS[callbackid] = { id: self.flow.id, callback: callback };
504
+ self.flow.postMessage2({ TYPE: 'stream/add', id: id, data: body, callbackid: callbackid });
505
+ } else
506
+ self.flow.add(id, body, callback, ASFILES);
507
+ return self;
508
+ };
509
+
510
+ // Removes specific component
511
+ Instance.prototype.rem = function(id, callback) {
512
+ var self = this;
513
+ if (self.flow.isworkerthread) {
514
+ var callbackid = callback ? (CALLBACKID++) : -1;
515
+ if (callback)
516
+ CALLBACKS[callbackid] = { id: self.flow.id, callback: callback };
517
+ self.flow.postMessage2({ TYPE: 'stream/rem', id: id, callbackid: callbackid });
518
+ } else
519
+ self.flow.unregister(id, callback);
520
+ return self;
521
+ };
522
+
523
+ // Reads all components
524
+ Instance.prototype.components = function(callback) {
525
+
526
+ var self = this;
527
+
528
+ if (self.flow.isworkerthread) {
529
+ var callbackid = CALLBACKID++;
530
+ CALLBACKS[callbackid] = { id: self.flow.id, callback: callback };
531
+ self.flow.postMessage2({ TYPE: 'stream/components', callbackid: callbackid });
532
+ } else
533
+ callback(null, self.flow.components(true));
534
+
535
+ return self;
536
+ };
537
+
538
+ function readmeta(meta) {
539
+ var obj = {};
540
+ obj.id = meta.id;
541
+ obj.name = meta.name;
542
+ obj.version = meta.version;
543
+ obj.icon = meta.icon;
544
+ obj.color = meta.color;
545
+ obj.reference = meta.reference;
546
+ obj.group = meta.group;
547
+ obj.author = meta.author;
548
+ return obj;
549
+ }
550
+
551
+ function readinstance(flow, id) {
552
+ var tmp = flow.meta.flow[id];
553
+ if (tmp) {
554
+ var com = flow.meta.components[tmp.component];
555
+ if (com) {
556
+ if ((com.type === 'output' || com.type === 'input' || com.type === 'config'))
557
+ return { id: id, componentid: tmp.component, component: com.name, name: tmp.config.name || com.name, schema: com.schemaid ? com.schemaid[1] : undefined, icon: com.icon, color: com.color, type: com.type, readme: tmp.config.readme, outputs: tmp.outputs, inputs: tmp.inputs };
558
+ } else
559
+ flow.clean();
560
+ }
561
+ }
562
+
563
+ // Reads all inputs, outputs, publish, subscribe instances
564
+ Instance.prototype.io = function(id, callback) {
565
+
566
+ var self = this;
567
+
568
+ if (self.flow.isworkerthread) {
569
+ var callbackid = CALLBACKID++;
570
+ CALLBACKS[callbackid] = { id: self.flow.id, callback: callback };
571
+ self.flow.postMessage2({ TYPE: 'stream/io', id: id, callbackid: callbackid });
572
+ return self;
573
+ }
574
+
575
+ var flow = self.flow;
576
+
577
+ if (id) {
578
+ var obj = null;
579
+ if (flow.meta.flow[id])
580
+ callback(null, readinstance(flow, id));
581
+ else
582
+ callback();
583
+ return;
584
+ }
585
+
586
+ var arr = [];
587
+
588
+ for (var key in flow.meta.flow) {
589
+ var obj = readinstance(flow, key);
590
+ obj && arr.push(obj);
591
+ }
592
+
593
+ callback(null, arr);
594
+ };
595
+
596
+ // Reconfigures a component
597
+ Instance.prototype.reconfigure = function(id, config) {
598
+ var self = this;
599
+ if (self.flow.isworkerthread)
600
+ self.flow.postMessage2({ TYPE: 'stream/reconfigure', id: id, data: config });
601
+ else
602
+ self.flow.reconfigure(id, config);
603
+ return self;
604
+ };
605
+
606
+ Instance.prototype.reload = function(data) {
607
+ var self = this;
608
+ var flow = self.flow;
609
+
610
+ if (PROXIES[data.id]) {
611
+ PROXIES[data.id].remove();
612
+ delete PROXIES[data.id];
613
+ }
614
+
615
+ if (flow.isworkerthread) {
616
+
617
+ if (data.proxypath) {
618
+
619
+ if (!data.unixsocket) {
620
+ data.unixsocket = flow.$schema.unixsocket || makeunixsocket(data.id);
621
+ flow.$schema.unixsocket = data.unixsocket;
622
+ }
623
+
624
+ PROXIES[data.id] = F.proxy(data.proxypath, data.unixsocket);
625
+ }
626
+
627
+ for (let key in data)
628
+ flow.$schema[key] = data[key];
629
+
630
+ self.proxypath = data.proxypath;
631
+ flow.postMessage2({ TYPE: 'stream/rewrite', data: data });
632
+
633
+ } else {
634
+ for (let key in data)
635
+ flow.$schema[key] = data[key];
636
+ flow.variables = data.variables;
637
+ if (data.variables2)
638
+ flow.variables2 = data.variables2;
639
+ flow.rewrite(data, () => flow.proxy.refreshmeta());
640
+ }
641
+ return self;
642
+ };
643
+
644
+ Instance.prototype.refresh = function(id, type, data, restart) {
645
+ var self = this;
646
+ var flow = self.flow;
647
+
648
+ if (flow.isworkerthread) {
649
+
650
+ for (var key in data)
651
+ flow.$schema[key] = data[key];
652
+
653
+ if (restart) {
654
+ if (flow.terminate)
655
+ flow.terminate();
656
+ else
657
+ flow.kill(9);
658
+ } else
659
+ flow.postMessage2({ TYPE: 'stream/refresh', id: id, type: type, data: data });
660
+
661
+ } else {
662
+
663
+ if (type === 'meta' && data) {
664
+ for (var key in data)
665
+ flow.$schema[key] = data[key];
666
+ flow.proxy.refreshmeta();
667
+ }
668
+
669
+ for (var key in flow.meta.flow) {
670
+ var instance = flow.meta.flow[key];
671
+ instance.flowstream && instance.flowstream(id, type);
672
+ }
673
+ }
674
+ };
675
+
676
+ // Updates variables
677
+ Instance.prototype.variables = function(variables) {
678
+
679
+ var self = this;
680
+ var flow = self.flow;
681
+
682
+ if (flow.isworkerthread) {
683
+ flow.$schema.variables = variables;
684
+ flow.postMessage2({ TYPE: 'stream/variables', data: variables });
685
+ } else {
686
+ flow.variables = variables;
687
+ for (var key in flow.meta.flow) {
688
+ var instance = flow.meta.flow[key];
689
+ instance.variables && instance.variables(flow.variables);
690
+ instance.vary && instance.vary('variables');
691
+ }
692
+ flow.proxy.online && flow.proxy.send({ TYPE: 'flow/variables', data: variables });
693
+ flow.save();
694
+ }
695
+ return self;
696
+ };
697
+
698
+ // Updates global variables
699
+ Instance.prototype.variables2 = function(variables) {
700
+
701
+ var self = this;
702
+ var flow = self.flow;
703
+
704
+ if (flow.isworkerthread) {
705
+ flow.$schema.variables2 = variables;
706
+ flow.postMessage2({ TYPE: 'stream/variables2', data: variables });
707
+ } else {
708
+ flow.variables2 = variables;
709
+ for (var key in flow.meta.flow) {
710
+ var instance = flow.meta.flow[key];
711
+ instance.variables2 && instance.variables2(flow.variables2);
712
+ instance.vary && instance.vary('variables2');
713
+ }
714
+ flow.save();
715
+ }
716
+ return self;
717
+ };
718
+
719
+ Instance.prototype.export = function(callback) {
720
+ var self = this;
721
+ var flow = self.flow;
722
+ if (flow.isworkerthread) {
723
+ var callbackid = callback ? (CALLBACKID++) : -1;
724
+ CALLBACKS[callbackid] = { id: self.flow.id, callback: callback };
725
+ self.flow.postMessage2({ TYPE: 'stream/export', callbackid: callbackid });
726
+ } else
727
+ callback(null, self.flow.export2());
728
+ return self;
729
+ };
730
+
731
+ // Initializes FlowStream
732
+ exports.init = function(meta, isworker, callback, nested) {
733
+ return isworker ? init_worker(meta, isworker, callback) : init_current(meta, callback, nested);
734
+ };
735
+
736
+ exports.exec = function(id, opt) {
737
+ var fs = FLOWS[id];
738
+ if (fs)
739
+ fs.exec(id, opt);
740
+ else if (opt.callback)
741
+ opt.callback(404);
742
+ };
743
+
744
+ exports.eval = function(id, opt) {
745
+ var fs = FLOWS[id];
746
+ if (fs)
747
+ fs.eval(id, opt);
748
+ else if (opt.callback)
749
+ opt.callback(404);
750
+ };
751
+
752
+ exports.input = function(ffsid, fid, tfsid, tid, data, reference) {
753
+ if (tfsid) {
754
+ var fs = FLOWS[tfsid];
755
+ fs && fs.$instance.input(ffsid, fid, tid, data, reference);
756
+ } else {
757
+ for (var key in FLOWS) {
758
+ var flow = FLOWS[key];
759
+ flow.$instance.input(ffsid, fid, tid, data, reference);
760
+ }
761
+ }
762
+ };
763
+
764
+ exports.trigger = function(flowstreamid, id, data) {
765
+ var fs = FLOWS[flowstreamid];
766
+ fs && fs.trigger(id, data);
767
+ };
768
+
769
+ exports.refresh = function(id, type) {
770
+ for (var key in FLOWS) {
771
+ var flow = FLOWS[key];
772
+ flow.$instance.refresh(id, type);
773
+ }
774
+ };
775
+
776
+ exports.rpc = function(name, callback) {
777
+ RPC[name] = callback;
778
+ };
779
+
780
+ exports.version = VERSION;
781
+
782
+ function send(self, id, data, callback) {
783
+
784
+ var index = id.lastIndexOf('/');
785
+ var input = '';
786
+
787
+ if (index !== -1) {
788
+ input = id.substring(index + 1);
789
+ id = id.substring(0, index);
790
+ }
791
+
792
+ var instances = self.meta.flow;
793
+ var instance = null;
794
+
795
+ if (id[0] === '@') {
796
+ id = id.substring(1);
797
+ for (let key in instances) {
798
+ if (instances[key].component === id) {
799
+ instance = instances[key];
800
+ break;
801
+ }
802
+ }
803
+ } else if (id[0] === '#') {
804
+ id = id.substring(1);
805
+ for (let key in instances) {
806
+ if (instances[key].module.name === id) {
807
+ instance = instances[key];
808
+ break;
809
+ }
810
+ }
811
+ } else {
812
+ if (instances[id])
813
+ instance = instances[id];
814
+ }
815
+
816
+ if (!instance) {
817
+ if (callback) {
818
+ if (Parent) {
819
+ let opt = {};
820
+ opt.callbackid = callback;
821
+ opt.data = { error: 404 };
822
+ Parent.postMessage(opt);
823
+ } else
824
+ callback(404);
825
+ }
826
+ return;
827
+ }
828
+
829
+ var msg = instance.newmessage(data);
830
+
831
+ msg.input = input;
832
+
833
+ callback && msg.on('end', function(msg) {
834
+
835
+ let output = {};
836
+
837
+ output.error = msg.error;
838
+ output.repo = msg.repo;
839
+ output.data = msg.data;
840
+ output.count = msg.count;
841
+ output.cloned = msg.cloned;
842
+ output.duration = Date.now() - msg.duration;
843
+ output.meta = { id: instance.id, component: instance.component };
844
+
845
+ if (Parent) {
846
+ let opt = {};
847
+ opt.TYPE = 'stream/send';
848
+ opt.callbackid = callback;
849
+ opt.data = output;
850
+ Parent.postMessage(opt);
851
+ } else
852
+ callback(output.error, output);
853
+ });
854
+
855
+ instance.message(msg);
856
+ }
857
+
858
+ function exec(self, opt) {
859
+
860
+ var target = [];
861
+ var instances = self.meta.flow;
862
+ var id;
863
+
864
+ if (opt.id[0] === '@') {
865
+ id = opt.id.substring(1);
866
+ for (let key in instances) {
867
+ if (instances[key].component === id)
868
+ target.push(instances[key]);
869
+ }
870
+ } else if (opt.id[0] === '#') {
871
+ id = opt.id.substring(1);
872
+ for (let key in instances) {
873
+ if (instances[key].module.name === id)
874
+ target.push(instances[key]);
875
+ }
876
+ } else {
877
+ if (instances[opt.id])
878
+ target.push(instances[opt.id]);
879
+ }
880
+
881
+ target.wait(function(instance, next) {
882
+ var msg = instance && instance.message ? instance.newmessage() : null;
883
+ if (msg) {
884
+
885
+ if (opt.vars)
886
+ msg.vars = opt.vars;
887
+
888
+ if (opt.repo)
889
+ msg.repo = opt.repo;
890
+
891
+ msg.data = opt.data == null ? {} : opt.data;
892
+
893
+ if (opt.callbackid !== -1) {
894
+ msg.on('end', function(msg) {
895
+
896
+ var output = {};
897
+ output.uid = opt.uid;
898
+ output.ref = opt.ref;
899
+ output.error = msg.error;
900
+ output.repo = msg.repo;
901
+ output.data = msg.data;
902
+ output.count = msg.count;
903
+ output.cloned = msg.cloned;
904
+ output.duration = Date.now() - msg.duration;
905
+ output.meta = { id: instance.id, component: instance.component };
906
+
907
+ if (Parent) {
908
+ if (opt.callbackid !== -1) {
909
+ opt.repo = undefined;
910
+ opt.vars = undefined;
911
+ opt.data = output;
912
+ Parent.postMessage(opt);
913
+ }
914
+ } else if (opt.callback)
915
+ opt.callback(output.error, output);
916
+ });
917
+ }
918
+
919
+ if (opt.timeout)
920
+ msg.totaltimeout(opt.timeout);
921
+
922
+ instance.message(msg);
923
+
924
+ } else if (opt.callback) {
925
+ opt.callback(404);
926
+ } else if (Parent && opt.callbackid !== -1) {
927
+ opt.repo = undefined;
928
+ opt.vars = undefined;
929
+ opt.data = { error: 404 };
930
+ Parent.postMessage(opt);
931
+ }
932
+
933
+ setImmediate(next);
934
+
935
+ }, function() {
936
+ if (!target.length) {
937
+ if (opt.callback) {
938
+ opt.callback(404);
939
+ } else if (Parent && opt.callbackid !== -1) {
940
+ opt.repo = undefined;
941
+ opt.vars = undefined;
942
+ opt.data = { error: 404 };
943
+ Parent.postMessage(opt);
944
+ }
945
+ }
946
+ });
947
+ }
948
+
949
+ function rpc(name, data, callback) {
950
+ var fn = RPC[name];
951
+ if (fn)
952
+ fn(data, callback);
953
+ else
954
+ callback('Invalid remote procedure name');
955
+ }
956
+
957
+ function httprequest(self, opt, callback) {
958
+ if (self.httproutes[opt.route]) {
959
+ self.httproutes[opt.route].callback(opt, function(data) {
960
+ // data.status {Number}
961
+ // data.headers {Object}
962
+ // data.body {Buffer}
963
+ if (Parent)
964
+ Parent.postMessage({ TYPE: 'stream/httpresponse', data: data, callbackid: callback });
965
+ else
966
+ callback(data);
967
+ });
968
+ } else {
969
+ if (Parent)
970
+ Parent.postMessage({ TYPE: 'stream/httpresponse', data: { type: 'error', body: 404 }, callbackid: callback });
971
+ else
972
+ callback({ type: 'error', body: 404 });
973
+ }
974
+ }
975
+
976
+ function killprocess() {
977
+ // console.error('Main process doesn\'t respond');
978
+ process.exit(1);
979
+ }
980
+
981
+ function init_current(meta, callback, nested) {
982
+
983
+ initrunning();
984
+
985
+ if (!meta.directory)
986
+ meta.directory = F.path.root('flowstream');
987
+
988
+ // Due to C/C++ modules
989
+ if (W.workerData || meta.sandbox)
990
+ F.config.$node_modules = F.path.join(meta.directory, meta.id, 'node_modules');
991
+
992
+ ASFILES = meta.asfiles === true;
993
+
994
+ var flow = MAKEFLOWSTREAM(meta);
995
+ FLOWS[meta.id] = flow;
996
+
997
+ if (isFLOWSTREAMWORKER) {
998
+ if (meta.unixsocket && meta.proxypath) {
999
+ if (!F.isWindows)
1000
+ F.Fs.unlink(meta.unixsocket, NOOP);
1001
+ F.http({ load: 'none', unixsocket: meta.unixsocket, config: { $stats: false, $sourcemap: false }});
1002
+ } else {
1003
+ F.config.$sourcemap = false;
1004
+ F.config.$stats = false;
1005
+ F.load('none');
1006
+ }
1007
+ }
1008
+
1009
+ flow.env = meta.env;
1010
+ flow.origin = meta.origin;
1011
+ flow.proxypath = meta.proxypath || '';
1012
+ flow.proxy.online = false;
1013
+ flow.proxy.ping = 0;
1014
+
1015
+ if (meta.import) {
1016
+ var tmp = meta.import.split(/,|;/).trim();
1017
+ for (var m of tmp) {
1018
+ var mod = require(F.path.root(m));
1019
+ mod.install && mod.install(flow);
1020
+ mod.init && mod.init(flow);
1021
+ }
1022
+ }
1023
+
1024
+ if (meta.initscript) {
1025
+ try {
1026
+ new Function('instance', meta.initscript)(flow);
1027
+ } catch (e) {
1028
+ flow.error(e, 'initscript');
1029
+ }
1030
+ }
1031
+
1032
+ flow.$instance = new Instance(flow, meta.id);
1033
+
1034
+ flow.$instance.output = function(fid, data, tfsid, tid, reference) {
1035
+ exports.input(meta.id, fid, tfsid, tid, data, reference);
1036
+ };
1037
+
1038
+ if (!nested && Parent) {
1039
+ Parent.on('message', function(msg) {
1040
+
1041
+ var id;
1042
+
1043
+ switch (msg.TYPE) {
1044
+
1045
+ case 'ping':
1046
+ flow.proxy.ping && clearTimeout(flow.proxy.ping);
1047
+ flow.proxy.ping = setTimeout(killprocess, 10000);
1048
+ break;
1049
+
1050
+ case 'stream/destroy':
1051
+ flow.destroy();
1052
+ break;
1053
+
1054
+ case 'stream/export':
1055
+ msg.data = flow.export2();
1056
+ Parent.postMessage(msg);
1057
+ break;
1058
+
1059
+ case 'stream/reconfigure':
1060
+ flow.reconfigure(msg.id, msg.data);
1061
+ break;
1062
+
1063
+ case 'stream/httprequest':
1064
+ httprequest(flow, msg.data, msg.callbackid);
1065
+ break;
1066
+
1067
+ case 'stream/cmd':
1068
+ var fn = msg.path.indexOf('.') === - 1 ? global[msg.path] : F.TUtils.get(global, msg.path);
1069
+ if (fn && typeof(fn) === 'function')
1070
+ fn(msg.data);
1071
+ break;
1072
+
1073
+ case 'stream/send':
1074
+ send(flow, msg.id, msg.data, msg.callbackid);
1075
+ break;
1076
+
1077
+ case 'stream/exec':
1078
+ exec(flow, msg);
1079
+ break;
1080
+
1081
+ case 'stream/eval':
1082
+ if (msg.callbackid) {
1083
+ flow.proxy.message(msg, function(response) {
1084
+ msg.data = response;
1085
+ Parent.postMessage(msg);
1086
+ });
1087
+ } else
1088
+ flow.proxy.message(msg);
1089
+ break;
1090
+
1091
+ case 'stream/notify':
1092
+ case 'stream/trigger':
1093
+ id = msg.id;
1094
+ var type = msg.TYPE.substring(7);
1095
+ if (!flow.paused) {
1096
+ if (id[0] === '@') {
1097
+ id = id.substring(1);
1098
+ for (var key in flow.meta.flow) {
1099
+ var com = flow.meta.flow[key];
1100
+ if (com.component === id && com[type])
1101
+ com[type](msg.data);
1102
+ }
1103
+ } else if (id[0] === '#') {
1104
+ id = id.substring(1);
1105
+ for (var key in flow.meta.flow) {
1106
+ var com = flow.meta.flow[key];
1107
+ if (com.module.name === id && com[type])
1108
+ com[type](msg.data);
1109
+ }
1110
+ } else {
1111
+ var com = flow.meta.flow[id];
1112
+ if (com && com[type])
1113
+ com[type](msg.data);
1114
+ }
1115
+ }
1116
+ break;
1117
+
1118
+ case 'stream/pause':
1119
+ flow.pause(msg.is == null ? !flow.paused : msg.is);
1120
+ flow.save();
1121
+ break;
1122
+
1123
+ case 'stream/rewrite':
1124
+ for (var key in msg.data)
1125
+ flow.$schema[key] = msg.data[key];
1126
+
1127
+ flow.rewrite(msg.data, function() {
1128
+
1129
+ // @err {Error}
1130
+
1131
+ flow.proxy.refreshmeta();
1132
+
1133
+ if (flow.proxy.online) {
1134
+ flow.proxy.send({ TYPE: 'flow/components', data: flow.components(true) });
1135
+ flow.proxy.send({ TYPE: 'flow/design', data: flow.export() });
1136
+ flow.proxy.send({ TYPE: 'flow/variables', data: flow.variables });
1137
+ }
1138
+
1139
+ });
1140
+ break;
1141
+
1142
+ case 'stream/refresh':
1143
+
1144
+ if (msg.type === 'meta' && msg.data) {
1145
+ for (var key in msg.data)
1146
+ flow.$schema[key] = msg.data[key];
1147
+ flow.proxy.refreshmeta();
1148
+ }
1149
+
1150
+ for (var key in flow.meta.flow) {
1151
+ var instance = flow.meta.flow[key];
1152
+ instance.flowstream && instance.flowstream(msg.id, msg.type);
1153
+ }
1154
+ break;
1155
+
1156
+ case 'stream/io2':
1157
+ case 'stream/rpcresponse':
1158
+ var cb = CALLBACKS[msg.callbackid];
1159
+ if (cb) {
1160
+ delete CALLBACKS[msg.callbackid];
1161
+ cb.callback(msg.error, msg.data);
1162
+ }
1163
+ break;
1164
+
1165
+ case 'stream/components':
1166
+ msg.data = flow.components(true);
1167
+ Parent.postMessage(msg);
1168
+ break;
1169
+
1170
+ case 'stream/io':
1171
+
1172
+ if (msg.id) {
1173
+ msg.data = readinstance(flow, msg.id);
1174
+ } else {
1175
+ var arr = [];
1176
+ for (var key in flow.meta.flow) {
1177
+ let tmp = readinstance(flow, key);
1178
+ if (tmp)
1179
+ arr.push(tmp);
1180
+ }
1181
+ msg.data = arr;
1182
+ }
1183
+
1184
+ Parent.postMessage(msg);
1185
+ break;
1186
+
1187
+ case 'stream/input':
1188
+
1189
+ if (msg.id) {
1190
+ if (msg.id[0] === '@') {
1191
+ id = msg.id.substring(1);
1192
+ for (let key in flow.meta.flow) {
1193
+ let tmp = flow.meta.flow[key];
1194
+ if (tmp.input && tmp.component === id)
1195
+ tmp.input(msg.flowstreamid, msg.fromid, msg.data, msg.reference);
1196
+ }
1197
+ } else {
1198
+ let tmp = flow.meta.flow[msg.id];
1199
+ if (tmp) {
1200
+ tmp.input && tmp.input(msg.flowstreamid, msg.fromid, msg.data, msg.reference);
1201
+ } else {
1202
+ for (let key in flow.meta.flow) {
1203
+ let tmp = flow.meta.flow[key];
1204
+ if (tmp.input && tmp.config.name === msg.id)
1205
+ tmp.input(msg.flowstreamid, msg.fromid, msg.data, msg.reference);
1206
+ }
1207
+ }
1208
+ }
1209
+ } else {
1210
+ for (let key in flow.meta.flow) {
1211
+ var f = flow.meta.flow[key];
1212
+ var c = flow.meta.components[f.component];
1213
+ if (f.input && c.type === 'input2')
1214
+ f.input(msg.flowstreamid, msg.fromid, msg.data, msg.reference);
1215
+ }
1216
+ }
1217
+ break;
1218
+
1219
+ case 'stream/add':
1220
+ flow.add(msg.id, msg.data, function(err) {
1221
+ msg.error = err ? err.toString() : null;
1222
+ if (msg.callbackid !== -1)
1223
+ Parent.postMessage(msg);
1224
+ flow.save();
1225
+ }, ASFILES);
1226
+ break;
1227
+
1228
+ case 'stream/rem':
1229
+ flow.unregister(msg.id, function(err) {
1230
+ msg.error = err ? err.toString() : null;
1231
+ if (msg.callbackid !== -1)
1232
+ Parent.postMessage(msg);
1233
+ flow.save();
1234
+ });
1235
+ break;
1236
+
1237
+ case 'stream/variables':
1238
+ flow.variables = msg.data;
1239
+ for (var key in flow.meta.flow) {
1240
+ var instance = flow.meta.flow[key];
1241
+ instance.variables && instance.variables(flow.variables);
1242
+ }
1243
+ flow.proxy.online && flow.proxy.send({ TYPE: 'flow/variables', data: msg.data });
1244
+ flow.save();
1245
+ break;
1246
+
1247
+ case 'stream/variables2':
1248
+ flow.variables2 = msg.data;
1249
+ for (var key in flow.meta.flow) {
1250
+ var instance = flow.meta.flow[key];
1251
+ instance.variables2 && instance.variables2(flow.variables2);
1252
+ }
1253
+ flow.save();
1254
+ break;
1255
+
1256
+ case 'ui/newclient':
1257
+ flow.proxy.online = true;
1258
+ flow.proxy.newclient(msg.clientid);
1259
+ break;
1260
+
1261
+ case 'ui/online':
1262
+ flow.proxy.online = msg.online;
1263
+ break;
1264
+
1265
+ case 'ui/message':
1266
+ if (msg.callbackid) {
1267
+ flow.proxy.message(msg.data, msg.clientid, function(data) {
1268
+ msg.TYPE = 'stream/eval';
1269
+ msg.data = data;
1270
+ Parent.postMessage(msg);
1271
+ });
1272
+ } else
1273
+ flow.proxy.message(msg.data, msg.clientid);
1274
+ break;
1275
+ }
1276
+ });
1277
+
1278
+ flow.proxy.remove = function() {
1279
+ Parent.postMessage({ TYPE: 'stream/remove' });
1280
+ };
1281
+
1282
+ flow.proxy.kill = function() {
1283
+ Parent.postMessage({ TYPE: 'stream/kill' });
1284
+ };
1285
+
1286
+ flow.proxy.send = function(msg, type, clientid) {
1287
+ Parent.postMessage({ TYPE: 'ui/send', data: msg, type: type, clientid: clientid });
1288
+ };
1289
+
1290
+ flow.proxy.save = function(data) {
1291
+ if (!flow.$schema || !flow.$schema.readonly)
1292
+ Parent.postMessage({ TYPE: 'stream/save', data: data });
1293
+ };
1294
+
1295
+ flow.proxy.httproute = function(url, callback, instance) {
1296
+ if (!flow.$schema || !flow.$schema.readonly) {
1297
+ if (callback)
1298
+ flow.httproutes[url] = { id: instance.id, component: instance.component, callback: callback };
1299
+ else
1300
+ delete flow.httproutes[url];
1301
+ Parent.postMessage({ TYPE: 'stream/httproute', data: { url: url, remove: callback == null }});
1302
+ }
1303
+ };
1304
+
1305
+ flow.proxy.done = function(err) {
1306
+ Parent.postMessage({ TYPE: 'stream/done', error: err });
1307
+ };
1308
+
1309
+ flow.proxy.error = function(err, source, instance) {
1310
+
1311
+ var instanceid = '';
1312
+ var componentid = '';
1313
+
1314
+ if (instance) {
1315
+ if (typeof(instance) === 'string') {
1316
+ if (source === 'add') {
1317
+ componentid = instance;
1318
+ F.error(err, 'FlowStream | register component | ' + instance);
1319
+ } else if (meta.design[instance]) {
1320
+ instanceid = instance;
1321
+ componentid = meta.design[instance].component;
1322
+ }
1323
+ } else if (source === 'instance_message') {
1324
+ if (instance.instance) {
1325
+ instanceid = instance.instance.id;
1326
+ componentid = instance.instance.component;
1327
+ } else
1328
+ F.error(err, 'FlowStream | message | ' + instance);
1329
+ } else if (source === 'instance_close') {
1330
+ instanceid = instance.id;
1331
+ componentid = instance.component;
1332
+ } else if (source === 'instance_make') {
1333
+ instanceid = instance.id;
1334
+ componentid = instance.component;
1335
+ } else if (source === 'register') {
1336
+ instanceid = '';
1337
+ componentid = instance;
1338
+ } else {
1339
+ instanceid = instance.id;
1340
+ componentid = instance.module.id;
1341
+ }
1342
+ }
1343
+
1344
+ Parent.postMessage({ TYPE: 'stream/error', error: err.toString(), stack: err.stack, source: source, id: instanceid, component: componentid });
1345
+ };
1346
+
1347
+ flow.proxy.refresh = function(type) {
1348
+ Parent.postMessage({ TYPE: 'stream/refresh', type: type });
1349
+ };
1350
+
1351
+ flow.proxy.output = function(id, data, flowstreamid, instanceid, reference) {
1352
+ Parent.postMessage({ TYPE: 'stream/output', id: id, data: data, flowstreamid: flowstreamid, instanceid: instanceid, reference: reference });
1353
+ };
1354
+
1355
+ flow.proxy.input = function(fromid, tfsid, toid, data, reference) {
1356
+ Parent.postMessage({ TYPE: 'stream/toinput', fromflowstreamid: flow.id, fromid: fromid, toflowstreamid: tfsid, toid: toid, data: data, reference: reference });
1357
+ };
1358
+
1359
+ flow.proxy.restart = function() {
1360
+ Parent.postMessage({ TYPE: 'stream/restart' });
1361
+ };
1362
+
1363
+ flow.proxy.io = function(flowstreamid, id, callback) {
1364
+
1365
+ if (typeof(flowstreamid) === 'function') {
1366
+ callback = flowstreamid;
1367
+ id = null;
1368
+ flowstreamid = null;
1369
+ } else if (typeof(id) === 'function') {
1370
+ callback = id;
1371
+ id = null;
1372
+ }
1373
+
1374
+ var callbackid = callback ? (CALLBACKID++) : -1;
1375
+ if (callback)
1376
+ CALLBACKS[callbackid] = { id: flow.id, callback: callback };
1377
+
1378
+ Parent.postMessage({ TYPE: 'stream/io2', flowstreamid: flowstreamid, id: id, callbackid: callbackid });
1379
+ };
1380
+
1381
+ } else {
1382
+
1383
+ flow.proxy.io = function(flowstreamid, id, callback) {
1384
+ exports.io(flowstreamid, id, callback);
1385
+ };
1386
+
1387
+ flow.proxy.restart = function() {
1388
+ // nothing
1389
+ };
1390
+
1391
+ flow.proxy.remove = function() {
1392
+ flow.$instance.remove();
1393
+ };
1394
+
1395
+ flow.proxy.kill = function() {
1396
+ flow.$instance.kill();
1397
+ };
1398
+
1399
+ flow.proxy.send = NOOP;
1400
+ flow.proxy.save = function(data) {
1401
+ if (!flow.$schema || !flow.$schema.readonly)
1402
+ flow.$instance.onsave && flow.$instance.onsave(data);
1403
+ };
1404
+
1405
+ flow.proxy.httproute = function(url, callback, instanceid) {
1406
+ if (!flow.$schema || !flow.$schema.readonly) {
1407
+ if (callback)
1408
+ flow.httproutes[url] = { id: instanceid, callback: callback };
1409
+ else
1410
+ delete flow.httproutes[url];
1411
+ flow.$instance.onhttproute && flow.$instance.onhttproute(url, callback == null);
1412
+ }
1413
+ };
1414
+
1415
+ flow.proxy.refresh = function(type) {
1416
+ exports.refresh(flow.id, type);
1417
+ };
1418
+
1419
+ flow.proxy.done = function(err) {
1420
+ flow.$instance.ondone && setImmediate(flow.$instance.ondone, err);
1421
+ };
1422
+
1423
+ flow.proxy.input = function(fromid, tfsid, toid, data, reference) {
1424
+ exports.input(flow.id, fromid, tfsid, toid, data, reference);
1425
+ };
1426
+
1427
+ flow.proxy.error = function(err, source, instance) {
1428
+
1429
+ let instanceid = '';
1430
+ let componentid = '';
1431
+
1432
+ if (instance) {
1433
+ if (source === 'instance_message') {
1434
+ instanceid = instance.instance.id;
1435
+ componentid = instance.instance.component;
1436
+ } else if (source === 'instance_close') {
1437
+ instanceid = instance.id;
1438
+ componentid = instance.component;
1439
+ } else if (source === 'instance_make') {
1440
+ instanceid = instance.id;
1441
+ componentid = instance.component;
1442
+ } else if (source === 'register') {
1443
+ instanceid = '';
1444
+ componentid = instance;
1445
+ } else {
1446
+ instanceid = instance.id;
1447
+ componentid = instance.module.id;
1448
+ }
1449
+ }
1450
+
1451
+ let tmp = { TYPE: 'flow/error', error: err.toString(), source: source, id: instanceid, component: componentid, ts: new Date() };
1452
+ flow.$socket && flow.$socket.send(tmp);
1453
+ flow.$client && flow.$client.send(tmp);
1454
+ flow.$instance.onerror && flow.$instance.onerror(err, source, instanceid, componentid);
1455
+ };
1456
+
1457
+ flow.proxy.output = function(id, data, flowstreamid, instanceid, reference) {
1458
+ flow.$instance.output && flow.$instance.output(id, data, flowstreamid, instanceid, reference);
1459
+ };
1460
+ }
1461
+
1462
+ callback && callback(null, flow.$instance);
1463
+ return flow.$instance;
1464
+ }
1465
+
1466
+ function init_worker(meta, type, callback) {
1467
+
1468
+ var forkargs = [F.directory, '--fork'];
1469
+
1470
+ if (meta.memory)
1471
+ forkargs.push('--max-old-space-size=' + meta.memory);
1472
+
1473
+ var worker = type === 'worker' ? (new W.Worker(__filename, { workerData: meta })) : Fork(__filename, forkargs, { serialization: 'json', detached: false });
1474
+ var ischild = false;
1475
+
1476
+ meta.unixsocket = makeunixsocket(meta.id);
1477
+
1478
+ if (PROXIES[meta.id]) {
1479
+ PROXIES[meta.id].remove();
1480
+ delete PROXIES[meta.id];
1481
+ }
1482
+
1483
+ if (meta.proxypath)
1484
+ PROXIES[meta.id] = F.proxy(meta.proxypath, meta.unixsocket);
1485
+
1486
+ if (!worker.postMessage) {
1487
+ worker.postMessage = worker.send;
1488
+ ischild = true;
1489
+ }
1490
+
1491
+ worker.postMessage2 = function(a, b) {
1492
+ if (!worker.$terminated)
1493
+ worker.postMessage(a, b);
1494
+ };
1495
+
1496
+ worker.$instance = new Instance(worker, meta.id);
1497
+ worker.$instance.isworkerthread = true;
1498
+ worker.isworkerthread = true;
1499
+ worker.$schema = meta;
1500
+
1501
+ worker.$instance.output = function(id, data, flowstreamid, instanceid, reference) {
1502
+ exports.input(meta.id, id, flowstreamid, instanceid, data, reference);
1503
+ };
1504
+
1505
+ FLOWS[meta.id] = worker;
1506
+
1507
+ var restart = function(code) {
1508
+ worker.$terminated = true;
1509
+ setTimeout(function(worker, code) {
1510
+ worker.$socket && setTimeout(socket => socket && socket.destroy(), 2000, worker.$socket);
1511
+ worker.$client && setTimeout(client => client && client.destroy(), 2000, worker.$client);
1512
+ if (!worker.$destroyed) {
1513
+ console.log('FlowStream auto-restart: ' + worker.$schema.name + ' (exit code: ' + ((code || '0') + '') + ')');
1514
+ init_worker(worker.$schema, type, callback);
1515
+ worker.$instance = null;
1516
+ worker.$schema = null;
1517
+ worker.$destroyed = true;
1518
+ }
1519
+ }, 1000, worker, code);
1520
+ };
1521
+
1522
+ worker.on('exit', restart);
1523
+
1524
+ worker.on('message', function(msg) {
1525
+
1526
+ var tmp;
1527
+
1528
+ Flow.$events.message && Flow.emit('message', worker.$instance.id, msg);
1529
+
1530
+ switch (msg.TYPE) {
1531
+
1532
+ case 'stream/stats':
1533
+ worker.stats = msg.data;
1534
+ if (Flow.$events.stats)
1535
+ Flow.emit('stats', meta.id, msg.data);
1536
+ break;
1537
+
1538
+ case 'stream/restart':
1539
+ if (worker.terminate)
1540
+ worker.terminate();
1541
+ else
1542
+ worker.kill(9);
1543
+ break;
1544
+
1545
+ case 'stream/kill':
1546
+ if (!worker.$terminated)
1547
+ worker.$instance.destroy(msg.code || 9);
1548
+ break;
1549
+
1550
+ case 'stream/remove':
1551
+ if (!worker.$terminated)
1552
+ worker.$instance.remove();
1553
+ break;
1554
+
1555
+ case 'stream/send':
1556
+ tmp = CALLBACKS[msg.callbackid];
1557
+ if (tmp) {
1558
+ delete CALLBACKS[msg.callbackid];
1559
+ tmp.callback(msg.data.error, msg.data);
1560
+ }
1561
+ break;
1562
+ case 'stream/exec':
1563
+ tmp = CALLBACKS[msg.callbackid];
1564
+ if (tmp) {
1565
+ delete CALLBACKS[msg.callbackid];
1566
+ tmp.callback(msg.data.error, msg.data, msg.meta);
1567
+ }
1568
+ break;
1569
+
1570
+ case 'stream/httpresponse':
1571
+ tmp = CALLBACKS[msg.callbackid];
1572
+ if (tmp) {
1573
+ delete CALLBACKS[msg.callbackid];
1574
+ tmp.callback(msg.data, msg.meta);
1575
+ }
1576
+ break;
1577
+
1578
+ case 'stream/rpc':
1579
+ rpc(msg.name, msg.data, (err, response) => worker.postMessage2({ TYPE: 'stream/rpcresponse', error: err, data: response, callbackid: msg.callbackid }));
1580
+ break;
1581
+
1582
+ case 'stream/export':
1583
+ case 'stream/components':
1584
+ var cb = CALLBACKS[msg.callbackid];
1585
+ if (cb) {
1586
+ delete CALLBACKS[msg.callbackid];
1587
+ cb.callback(null, msg.data);
1588
+ }
1589
+ break;
1590
+
1591
+ case 'stream/toinput':
1592
+ exports.input(msg.fromflowstreamid, msg.fromid, msg.toflowstreamid, msg.toid, msg.data, msg.reference);
1593
+ break;
1594
+
1595
+ case 'stream/refresh':
1596
+ exports.refresh(meta.id, msg.type);
1597
+ break;
1598
+
1599
+ case 'stream/error':
1600
+ tmp = { TYPE: 'flow/error', error: msg.error, stack: msg.stack, source: msg.source, id: msg.id, component: msg.component, ts: new Date() };
1601
+ worker.$socket && worker.$socket.send(tmp);
1602
+ worker.$client && worker.$client.send(tmp);
1603
+ worker.$instance.onerror && worker.$instance.onerror(msg.error, msg.source, msg.id, msg.component, msg.stack);
1604
+ break;
1605
+
1606
+ case 'stream/save':
1607
+ worker.$schema.components = msg.data.components;
1608
+ worker.$schema.design = msg.data.design;
1609
+ worker.$schema.variables = msg.data.variables;
1610
+ worker.$schema.origin = msg.data.origin;
1611
+ worker.$schema.sources = msg.data.sources;
1612
+ worker.$instance.onsave && worker.$instance.onsave(msg.data);
1613
+ break;
1614
+
1615
+ case 'stream/httproute':
1616
+ worker.$instance.onhttproute && worker.$instance.onhttproute(msg.data.url, msg.data.remove);
1617
+ break;
1618
+
1619
+ case 'stream/done':
1620
+ worker.$instance.ondone && worker.$instance.ondone(msg.error);
1621
+ break;
1622
+
1623
+ case 'stream/io2':
1624
+ exports.io(msg.flowstreamid, msg.id, function(err, data) {
1625
+ msg.data = data;
1626
+ msg.error = err;
1627
+ worker.postMessage(msg);
1628
+ });
1629
+ break;
1630
+
1631
+ case 'stream/output':
1632
+ if (worker.$instance.onoutput) {
1633
+ tmp = meta.design[msg.id];
1634
+ tmp && worker.$instance.onoutput({ id: msg.id, name: tmp.config.name, data: msg.data, reference: msg.reference });
1635
+ }
1636
+ worker.$instance.output && worker.$instance.output(msg.id, msg.data, msg.flowstreamid, msg.instanceid, msg.reference);
1637
+ break;
1638
+
1639
+ case 'stream/add':
1640
+ case 'stream/rem':
1641
+ tmp = CALLBACKS[msg.callbackid];
1642
+ if (tmp) {
1643
+ delete CALLBACKS[msg.callbackid];
1644
+ tmp.callback(msg.error);
1645
+ }
1646
+ break;
1647
+
1648
+ case 'stream/io':
1649
+ case 'stream/eval':
1650
+ tmp = CALLBACKS[msg.callbackid];
1651
+ if (tmp) {
1652
+ delete CALLBACKS[msg.callbackid];
1653
+ tmp.callback(msg.error, msg.data);
1654
+ }
1655
+ break;
1656
+
1657
+ case 'ui/send':
1658
+
1659
+ worker.$client && worker.$client.send(msg.data);
1660
+
1661
+ switch (msg.type) {
1662
+ case 1:
1663
+ worker.$socket && worker.$socket.send(msg.data, client => client.id === msg.clientid);
1664
+ break;
1665
+ case 2:
1666
+ worker.$socket && worker.$socket.send(msg.data, client => client.id !== msg.clientid);
1667
+ break;
1668
+ default:
1669
+ worker.$socket && worker.$socket.send(msg.data);
1670
+ break;
1671
+ }
1672
+ break;
1673
+ }
1674
+
1675
+ });
1676
+
1677
+ ischild && worker.send({ TYPE: 'init', data: meta });
1678
+ callback && callback(null, worker.$instance);
1679
+ return worker.$instance;
1680
+ }
1681
+
1682
+ exports.io = function(flowstreamid, id, callback) {
1683
+
1684
+ if (typeof(flowstreamid) === 'function') {
1685
+ callback = flowstreamid;
1686
+ id = null;
1687
+ flowstreamid = null;
1688
+ } else if (typeof(id) === 'function') {
1689
+ callback = id;
1690
+ id = null;
1691
+ }
1692
+
1693
+ var flow;
1694
+
1695
+ if (id) {
1696
+ flow = FLOWS[flowstreamid];
1697
+
1698
+ if (flow) {
1699
+ flow.$instance.io(id, function(err, data) {
1700
+ if (data) {
1701
+ var tmp = readmeta(flow.$schema);
1702
+ tmp.item = data;
1703
+ data = tmp;
1704
+ }
1705
+ callback(err, data);
1706
+ });
1707
+ } else
1708
+ callback();
1709
+
1710
+ return;
1711
+ }
1712
+
1713
+ if (flowstreamid) {
1714
+ flow = FLOWS[flowstreamid];
1715
+ if (flow) {
1716
+ flow.$instance.io(null, function(err, data) {
1717
+ var f = flow.$schema || EMPTYOBJECT;
1718
+ var meta = readmeta(f);
1719
+ meta.items = data;
1720
+ callback(null, meta);
1721
+ });
1722
+ } else
1723
+ callback();
1724
+ return;
1725
+ }
1726
+
1727
+ var arr = [];
1728
+
1729
+ Object.keys(FLOWS).wait(function(key, next) {
1730
+ var flow = FLOWS[key];
1731
+ if (flow) {
1732
+ flow.$instance.io(null, function(err, data) {
1733
+ var f = flow.$schema || EMPTYOBJECT;
1734
+ var meta = readmeta(f);
1735
+ meta.items = data;
1736
+ arr.push(meta);
1737
+ next();
1738
+ });
1739
+ } else
1740
+ next();
1741
+ }, function() {
1742
+ callback(null, arr);
1743
+ });
1744
+ };
1745
+
1746
+ exports.socket = function(flow, socket, check) {
1747
+
1748
+ if (typeof(flow) === 'string')
1749
+ flow = FLOWS[flow];
1750
+
1751
+ if (!flow) {
1752
+ setTimeout(() => socket.destroy(), 100);
1753
+ return;
1754
+ }
1755
+
1756
+ flow.$socket = socket;
1757
+
1758
+ var newclient = function(client) {
1759
+
1760
+ client.isflowstreamready = true;
1761
+
1762
+ if (flow.isworkerthread) {
1763
+ flow.postMessage2({ TYPE: 'ui/newclient', clientid: client.id });
1764
+ } else {
1765
+ flow.proxy.online = true;
1766
+ flow.proxy.newclient(client.id);
1767
+ }
1768
+
1769
+ };
1770
+
1771
+ socket.on('open', function(client) {
1772
+ if (check)
1773
+ check(client, () => newclient(client));
1774
+ else
1775
+ newclient(client);
1776
+ });
1777
+
1778
+ socket.autodestroy(function() {
1779
+
1780
+ delete flow.$socket;
1781
+
1782
+ if (flow.isworkerthread)
1783
+ flow.postMessage2({ TYPE: 'ui/online', online: false });
1784
+ else
1785
+ flow.proxy.online = false;
1786
+ });
1787
+
1788
+ socket.on('close', function(client) {
1789
+ if (client.isflowstreamready) {
1790
+ var is = socket.online > 0;
1791
+ if (flow.isworkerthread)
1792
+ flow.postMessage2({ TYPE: 'ui/online', online: is });
1793
+ else
1794
+ flow.proxy.online = is;
1795
+ }
1796
+ });
1797
+
1798
+ socket.on('message', function(client, msg) {
1799
+ if (client.isflowstreamready) {
1800
+ if (flow.isworkerthread)
1801
+ flow.postMessage2({ TYPE: 'ui/message', clientid: client.id, data: msg });
1802
+ else
1803
+ flow.proxy.message(msg, client.id);
1804
+ }
1805
+ });
1806
+
1807
+ if (flow.isworkerthread)
1808
+ return;
1809
+
1810
+ flow.proxy.send = function(msg, type, clientid) {
1811
+
1812
+ // 0: all
1813
+ // 1: client
1814
+ // 2: with except client
1815
+
1816
+ switch (type) {
1817
+ case 1:
1818
+ clientid && socket.send(msg, conn => conn.id === clientid);
1819
+ break;
1820
+ case 2:
1821
+ socket.send(msg, conn => conn.id !== clientid);
1822
+ break;
1823
+ default:
1824
+ socket.send(msg);
1825
+ break;
1826
+ }
1827
+ };
1828
+ };
1829
+
1830
+ exports.client = function(flow, socket) {
1831
+
1832
+ if (typeof(flow) === 'string')
1833
+ flow = FLOWS[flow];
1834
+
1835
+ var clientid = flow.id;
1836
+
1837
+ flow.$client = socket;
1838
+
1839
+ socket.on('close', function() {
1840
+ if (flow.isworkerthread)
1841
+ flow.postMessage2({ TYPE: 'ui/online', online: false });
1842
+ else
1843
+ flow.proxy.online = false;
1844
+ });
1845
+
1846
+ socket.on('message', function(msg) {
1847
+ if (msg.TYPE === 'flow') {
1848
+ if (flow.isworkerthread) {
1849
+ flow.postMessage2({ TYPE: 'ui/newclient', clientid: clientid });
1850
+ } else {
1851
+ flow.proxy.online = true;
1852
+ flow.proxy.newclient(clientid);
1853
+ }
1854
+ } else {
1855
+ if (flow.isworkerthread)
1856
+ flow.postMessage2({ TYPE: 'ui/message', clientid: clientid, data: msg });
1857
+ else
1858
+ flow.proxy.message(msg, clientid);
1859
+ }
1860
+ });
1861
+
1862
+ if (flow.isworkerthread)
1863
+ return;
1864
+
1865
+ flow.proxy.send = msg => socket.send(msg);
1866
+ };
1867
+
1868
+ function MAKEFLOWSTREAM(meta) {
1869
+
1870
+ var flow = F.TFlowStream.create(meta.id, function(err, type, instance) {
1871
+ this.proxy.error(err, type, instance);
1872
+ });
1873
+
1874
+ var saveid = null;
1875
+
1876
+ flow.cloning = meta.cloning != false;
1877
+ flow.export_instance2 = function(id) {
1878
+
1879
+ var com = flow.meta.flow[id];
1880
+ if (com) {
1881
+
1882
+ if (id === 'paused' || id === 'groups' || id === 'tabs')
1883
+ return CLONE(com);
1884
+
1885
+ var tmp = {};
1886
+ tmp.id = id;
1887
+ tmp.config = CLONE(com.config);
1888
+ tmp.x = com.x;
1889
+ tmp.y = com.y;
1890
+ tmp.offset = com.offset;
1891
+ tmp.size = com.size;
1892
+ tmp.meta = com.meta;
1893
+ tmp.schemaid = com.schemaid;
1894
+ tmp.note = com.note;
1895
+ tmp.schema = com.schema;
1896
+ tmp.component = com.component;
1897
+ tmp.connections = CLONE(com.connections);
1898
+ tmp.tab = com.tab;
1899
+
1900
+ if (com.outputs)
1901
+ tmp.outputs = com.outputs;
1902
+
1903
+ if (com.inputs)
1904
+ tmp.inputs = com.inputs;
1905
+
1906
+ var c = flow.meta.components[com.component];
1907
+ if (c) {
1908
+ tmp.template = { type: c.type, icon: c.icon, color: c.color, group: c.group, name: c.name, inputs: c.inputs, outputs: c.outputs };
1909
+ return tmp;
1910
+ }
1911
+ }
1912
+ };
1913
+
1914
+ function stringifyskip(key, value) {
1915
+ return key === '$$ID' || key === '$$REQUIRED' ? undefined : value;
1916
+ }
1917
+
1918
+ flow.export2 = function() {
1919
+
1920
+ var variables = flow.variables;
1921
+ var design = {};
1922
+ var components = {};
1923
+ var sources = flow.sources ? JSON.parse(JSON.stringify(flow.sources, stringifyskip)) : {};
1924
+
1925
+ for (let key in flow.meta.components) {
1926
+ let com = flow.meta.components[key];
1927
+ components[key] = com.ui.raw;
1928
+ }
1929
+
1930
+ for (let key in flow.meta.flow) {
1931
+ design[key] = flow.export_instance2(key);
1932
+ delete design[key].template;
1933
+ }
1934
+
1935
+ var data = {};
1936
+ var blacklist = { unixsocket: 1, components: 1, variables2: 1, sources: 1, design: 1, size: 1, directory: 1 };
1937
+
1938
+ for (let key in flow.$schema) {
1939
+ if (!blacklist[key])
1940
+ data[key] = flow.$schema[key];
1941
+ }
1942
+
1943
+ data.paused = flow.paused;
1944
+ data.components = components;
1945
+ data.design = design;
1946
+ data.variables = variables;
1947
+ data.sources = sources;
1948
+ return data;
1949
+ };
1950
+
1951
+ var saveforce = function() {
1952
+ saveid && clearTimeout(saveid);
1953
+ saveid = null;
1954
+ flow.proxy.save(flow.export2());
1955
+ };
1956
+
1957
+ var save = function() {
1958
+
1959
+ // reloads TMS
1960
+ for (var key in flow.sockets)
1961
+ flow.sockets[key].synchronize();
1962
+
1963
+ if (flow.$schema && flow.$schema.readonly)
1964
+ return;
1965
+
1966
+ clearTimeout(saveid);
1967
+ saveid = setTimeout(saveforce, 5000);
1968
+ };
1969
+
1970
+ flow.save = function() {
1971
+ save();
1972
+ };
1973
+
1974
+ flow.remove = function() {
1975
+ flow.proxy.remove();
1976
+ };
1977
+
1978
+ flow.kill = function(code) {
1979
+ flow.proxy.kill(code);
1980
+ };
1981
+
1982
+ flow.restart = function() {
1983
+ flow.proxy.restart();
1984
+ };
1985
+
1986
+ var timeoutrefresh = null;
1987
+
1988
+ var refresh_components_force = function() {
1989
+ timeoutrefresh = null;
1990
+ if (flow.proxy.online) {
1991
+ flow.proxy.send({ TYPE: 'flow/components', data: flow.components(true) });
1992
+ var instances = flow.export();
1993
+ flow.proxy.send({ TYPE: 'flow/design', data: instances });
1994
+ }
1995
+ };
1996
+
1997
+ var refresh_components = function() {
1998
+ timeoutrefresh && clearTimeout(timeoutrefresh);
1999
+ timeoutrefresh = setTimeout(refresh_components_force, 700);
2000
+ };
2001
+
2002
+ flow.rpc = function(name, data, callback) {
2003
+ if (Parent) {
2004
+ var callbackid = callback ? (CALLBACKID++) : -1;
2005
+ if (callbackid !== -1)
2006
+ CALLBACKS[callbackid] = { id: flow.id, callback: callback };
2007
+ Parent.postMessage({ TYPE: 'stream/rpc', name: name, data: data, callbackid: callbackid });
2008
+ } else
2009
+ rpc(name, data, callback);
2010
+ };
2011
+
2012
+ flow.redraw = refresh_components;
2013
+ flow.sources = meta.sources || {};
2014
+ flow.proxy = {};
2015
+
2016
+ flow.proxy.variables = function(data) {
2017
+ flow.variables = data;
2018
+ for (var key in flow.meta.flow) {
2019
+ var instance = flow.meta.flow[key];
2020
+ instance.variables && instance.variables(flow.variables);
2021
+ }
2022
+ var msg = {};
2023
+ msg.TYPE = 'flow/variables';
2024
+ msg.data = data;
2025
+ flow.proxy.online && flow.proxy.send(msg);
2026
+ save();
2027
+ };
2028
+
2029
+ flow.proxy.message = function(msg, clientid, callback) {
2030
+
2031
+ var tmp;
2032
+
2033
+ switch (msg.TYPE) {
2034
+
2035
+ case 'call':
2036
+
2037
+ var instance;
2038
+
2039
+ // Executes "exports.call"
2040
+ if (msg.id[0] === '@') {
2041
+ instance = flow.meta.components[msg.id.substring(1)];
2042
+ if (instance && instance.call) {
2043
+ msg.id = msg.callbackid;
2044
+ msg.TYPE = 'flow/call';
2045
+ instance.call.call(flow, msg.data, function(data) {
2046
+ msg.data = data;
2047
+ flow.proxy.online && flow.proxy.send(msg, 1, clientid);
2048
+ callback && callback(msg);
2049
+ });
2050
+ }
2051
+ return;
2052
+ }
2053
+
2054
+ instance = flow.meta.flow[msg.id];
2055
+ if (instance && instance.call) {
2056
+ msg.id = msg.callbackid;
2057
+ msg.TYPE = 'flow/call';
2058
+ instance.call(msg.data, function(data) {
2059
+ msg.data = data;
2060
+ flow.proxy.online && flow.proxy.send(msg, 1, clientid);
2061
+ callback && callback(msg);
2062
+ });
2063
+ }
2064
+ break;
2065
+
2066
+ case 'note':
2067
+ case 'meta':
2068
+ var instance = flow.meta.flow[msg.id];
2069
+ if (instance) {
2070
+ instance[msg.TYPE] = msg.data;
2071
+ msg.TYPE = 'flow/' + msg.TYPE;
2072
+ flow.proxy.online && flow.proxy.send(msg, 0, clientid);
2073
+ callback && callback(msg);
2074
+ save();
2075
+ }
2076
+ break;
2077
+
2078
+ case 'status':
2079
+ flow.instances().wait(function(com, next) {
2080
+ com[msg.TYPE] && com[msg.TYPE](msg, 0, clientid);
2081
+ setImmediate(next);
2082
+ }, 3);
2083
+ break;
2084
+
2085
+ case 'refresh':
2086
+ // Sends last statuses
2087
+ flow.instances().wait(function(com, next) {
2088
+ com.status();
2089
+ setImmediate(next);
2090
+ }, 3);
2091
+ break;
2092
+
2093
+ case 'reset':
2094
+ flow.errors.length = 0;
2095
+ msg.TYPE = 'flow/reset';
2096
+ flow.proxy.online && flow.proxy.send(msg, 0, clientid);
2097
+ callback && callback(msg);
2098
+ break;
2099
+
2100
+ case 'trigger':
2101
+ var instance = flow.meta.flow[msg.id];
2102
+ instance && instance.trigger && instance.trigger(msg);
2103
+ break;
2104
+
2105
+ case 'config':
2106
+ var instance = flow.meta.flow[msg.id];
2107
+ if (instance) {
2108
+ msg.TYPE = 'flow/configuration';
2109
+ msg.data = instance.config;
2110
+ flow.proxy.send(msg, 1, clientid);
2111
+ }
2112
+ break;
2113
+
2114
+ case 'reconfigure':
2115
+ flow.reconfigure(msg.id, msg.data);
2116
+ break;
2117
+
2118
+ case 'move':
2119
+ var com = flow.meta.flow[msg.id];
2120
+ if (com) {
2121
+ com.x = msg.data.x;
2122
+ com.y = msg.data.y;
2123
+ msg.TYPE = 'flow/move';
2124
+ flow.proxy.online && flow.proxy.send(msg, 2, clientid);
2125
+ callback && callback(msg);
2126
+ save();
2127
+ }
2128
+ break;
2129
+
2130
+ case 'groups':
2131
+ flow.meta.flow.groups = msg.data;
2132
+ msg.TYPE = 'flow/groups';
2133
+ flow.proxy.online && flow.proxy.send(msg, 2, clientid);
2134
+ callback && callback(msg);
2135
+ save();
2136
+ break;
2137
+
2138
+ case 'export':
2139
+ msg.TYPE = 'flow/export';
2140
+ if (flow.proxy.online) {
2141
+ msg.data = flow.export2();
2142
+ if (isFLOWSTREAMWORKER)
2143
+ msg.data.worker = process.argv.indexOf('--fork') === -1 ? 'worker' : 'fork';
2144
+ flow.proxy.send(msg, 1, clientid);
2145
+ callback && callback(msg);
2146
+ }
2147
+ break;
2148
+
2149
+ case 'origin':
2150
+ var origin = msg.body || '';
2151
+ if (flow.$schema.origin !== origin) {
2152
+ flow.origin = flow.$schema.origin = origin;
2153
+ flow.proxy.refreshmeta();
2154
+ save();
2155
+ }
2156
+ break;
2157
+
2158
+ case 'restart':
2159
+ flow.proxy.restart();
2160
+ break;
2161
+
2162
+ case 'reset_stats':
2163
+ flow.stats.messages = 0;
2164
+ flow.stats.pending = 0;
2165
+ break;
2166
+
2167
+ case 'save':
2168
+ flow.use(CLONE(msg.data), function(err) {
2169
+ msg.error = err ? err.toString() : null;
2170
+ msg.TYPE = 'flow/design';
2171
+ flow.proxy.online && flow.proxy.send(msg);
2172
+ callback && callback(msg);
2173
+ save();
2174
+ });
2175
+ break;
2176
+
2177
+ case 'insert':
2178
+ flow.insert(CLONE(msg.data), function(err) {
2179
+ for (var key in msg.data)
2180
+ msg.data[key] = flow.export_instance2(key);
2181
+ msg.TYPE = 'flow/design_insert';
2182
+ msg.error = err ? err.toString() : null;
2183
+ flow.proxy.online && flow.proxy.send(msg);
2184
+ callback && callback(msg);
2185
+ save();
2186
+ });
2187
+ break;
2188
+
2189
+ case 'remove':
2190
+ flow.remove(msg.data, function(err) {
2191
+ msg.TYPE = 'flow/design_remove';
2192
+ msg.error = err ? err.toString() : null;
2193
+ flow.proxy.online && flow.proxy.send(msg);
2194
+ callback && callback(msg);
2195
+ save();
2196
+ });
2197
+ break;
2198
+
2199
+ case 'variables':
2200
+ flow.variables = msg.data;
2201
+ for (var key in flow.meta.flow) {
2202
+ var instance = flow.meta.flow[key];
2203
+ instance.variables && instance.variables(flow.variables);
2204
+ }
2205
+ msg.TYPE = 'flow/variables';
2206
+ flow.proxy.online && flow.proxy.send(msg);
2207
+ callback && callback(msg);
2208
+ save();
2209
+ break;
2210
+
2211
+ case 'sources':
2212
+ msg.TYPE = 'flow/sources';
2213
+ msg.data = flow.sources;
2214
+ flow.proxy.online && flow.proxy.send(msg, 1, clientid);
2215
+ callback && callback(msg);
2216
+ break;
2217
+
2218
+ case 'pause':
2219
+
2220
+ if (msg.id == null) {
2221
+ // entire flow
2222
+ flow.pause(msg.is);
2223
+ save();
2224
+ return;
2225
+ }
2226
+
2227
+ if (msg.is) {
2228
+ if (!flow.meta.flow.paused)
2229
+ flow.meta.flow.paused = {};
2230
+ flow.meta.flow.paused[msg.id] = 1;
2231
+ save();
2232
+ } else {
2233
+ if (flow.meta.flow.paused) {
2234
+ delete flow.meta.flow.paused[msg.id];
2235
+ save();
2236
+ }
2237
+ }
2238
+ msg.TYPE = 'flow/pause';
2239
+ flow.proxy.online && flow.proxy.send(msg, 2, clientid);
2240
+ callback && callback(msg);
2241
+ break;
2242
+
2243
+ case 'source_read':
2244
+ msg.TYPE = 'flow/source_read';
2245
+ msg.data = flow.sources[msg.id];
2246
+ msg.error = msg.data ? null : 'Not found';
2247
+ flow.proxy.online && flow.proxy.send(msg, 1, clientid);
2248
+ callback && callback(msg);
2249
+ break;
2250
+
2251
+ case 'source_save':
2252
+
2253
+ TMS.check(msg.data, function(err, meta) {
2254
+
2255
+ if (err) {
2256
+ delete msg.data;
2257
+ msg.TYPE = 'flow/source_save';
2258
+ msg.error = err.toString();
2259
+ flow.proxy.online && flow.proxy.send(msg, 1, clientid);
2260
+ callback && callback(msg);
2261
+ return;
2262
+ }
2263
+
2264
+ var source = flow.sources[msg.data.id];
2265
+ if (source) {
2266
+ source.name = msg.data.name;
2267
+ source.url = msg.data.url;
2268
+ source.token = msg.data.token;
2269
+ source.dtupdated = NOW;
2270
+ source.meta = meta;
2271
+ source.checksum = HASH(JSON.stringify(meta)).toString(36) + '';
2272
+ } else {
2273
+ flow.sources[msg.data.id] = msg.data;
2274
+ msg.data.meta = meta;
2275
+ msg.data.checksum = HASH(JSON.stringify(meta)).toString(36) + '';
2276
+ }
2277
+
2278
+ TMS.refresh(flow);
2279
+ save();
2280
+ flow.proxy.online && flow.proxy.send({ TYPE: 'flow/source_save', callbackid: msg.callbackid, error: null }, 1, clientid);
2281
+ callback && callback(msg);
2282
+ });
2283
+
2284
+ break;
2285
+
2286
+ case 'source_remove':
2287
+
2288
+ msg.TYPE = 'flow/remove';
2289
+ var source = flow.sources[msg.id];
2290
+ if (source) {
2291
+ delete flow.sources[msg.id];
2292
+ flow.sockets[msg.id] && flow.sockets[msg.id].destroy();
2293
+ var remove = [];
2294
+ for (var key in flow.meta.components) {
2295
+ var com = flow.meta.components[key];
2296
+ if (com.schemaid && com.schemaid[0] === msg.id)
2297
+ remove.push(key);
2298
+ }
2299
+
2300
+ remove.wait(function(key, next) {
2301
+ flow.unregister(key, next);
2302
+ }, function() {
2303
+ refresh_components();
2304
+ save();
2305
+ });
2306
+ }
2307
+
2308
+ msg.error = source == null ? 'Not found' : null;
2309
+ flow.proxy.online && flow.proxy.send(msg, 1, clientid);
2310
+ callback && callback(msg);
2311
+ break;
2312
+
2313
+ case 'component_read':
2314
+
2315
+ tmp = flow.meta.components[msg.id];
2316
+ if (tmp && tmp.meta) {
2317
+ if (tmp.meta.readonly) {
2318
+ msg.TYPE = 'flow/component_read';
2319
+ msg.data = null;
2320
+ msg.error = 'The component cannot be edited';
2321
+ flow.proxy.online && flow.proxy.send(msg, 1, clientid);
2322
+ callback && callback(msg);
2323
+ return;
2324
+ }
2325
+ }
2326
+
2327
+ msg.TYPE = 'flow/component_read';
2328
+ msg.data = flow.meta.components[msg.id] ? flow.meta.components[msg.id].ui.raw : null;
2329
+ msg.error = msg.data == null ? 'Not found' : null;
2330
+ flow.proxy.online && flow.proxy.send(msg, 1, clientid);
2331
+ callback && callback(msg);
2332
+ break;
2333
+
2334
+ case 'component_save':
2335
+
2336
+ // check prev functionality
2337
+ tmp = flow.meta.components[msg.id];
2338
+ if (tmp && tmp.meta) {
2339
+ if (tmp.meta.readonly) {
2340
+ msg.TYPE = 'flow/component_save';
2341
+ msg.error = 'The component cannot be edited';
2342
+ flow.proxy.online && flow.proxy.send(msg, 1, clientid);
2343
+ callback && callback(msg);
2344
+ return;
2345
+ }
2346
+ }
2347
+
2348
+ flow.add(msg.id, msg.data, function(err) {
2349
+ delete msg.data;
2350
+ msg.TYPE = 'flow/component_save';
2351
+ msg.error = err ? err.toString() : null;
2352
+ flow.proxy.online && flow.proxy.send(msg, 1, clientid);
2353
+ callback && callback(msg);
2354
+ refresh_components();
2355
+ save();
2356
+ }, ASFILES);
2357
+ break;
2358
+
2359
+ case 'component_remove':
2360
+
2361
+ // check prev functionality
2362
+ tmp = flow.meta.components[msg.id];
2363
+ if (tmp && tmp.meta) {
2364
+ if (tmp.meta.protected) {
2365
+ msg.TYPE = 'flow/component_remove';
2366
+ msg.error = 'The component cannot be removed';
2367
+ flow.proxy.online && flow.proxy.send(msg, 1, clientid);
2368
+ callback && callback(msg);
2369
+ return;
2370
+ }
2371
+ }
2372
+
2373
+ flow.unregister(msg.id, function() {
2374
+ refresh_components();
2375
+ save();
2376
+ });
2377
+
2378
+ break;
2379
+ }
2380
+ };
2381
+
2382
+ flow.errors = [];
2383
+ flow.variables = meta.variables;
2384
+ flow.variables2 = meta.variables2;
2385
+ flow.sockets = {};
2386
+ flow.$schema = meta;
2387
+ flow.httproutes = {};
2388
+ flow.secrets = {};
2389
+
2390
+ if (meta.paused)
2391
+ flow.pause(true);
2392
+
2393
+ flow.load(meta, function() {
2394
+
2395
+ if (flow.sources) {
2396
+ Object.keys(flow.sources).wait(function(key, next) {
2397
+ TMS.connect(flow, key, next);
2398
+ });
2399
+ }
2400
+
2401
+ flow.ready = true;
2402
+ setImmediate(() => flow.proxy.done());
2403
+ }, ASFILES);
2404
+
2405
+ flow.components = function(prepare_export) {
2406
+
2407
+ var self = this;
2408
+ var arr = [];
2409
+
2410
+ for (var key in self.meta.components) {
2411
+ var com = self.meta.components[key];
2412
+ if (prepare_export) {
2413
+ var obj = {};
2414
+ obj.id = com.id;
2415
+ obj.meta = com.meta;
2416
+ obj.name = com.name;
2417
+ obj.type = com.type;
2418
+ obj.css = com.ui.css;
2419
+ obj.js = com.ui.js;
2420
+ obj.icon = com.icon;
2421
+ obj.color = com.color;
2422
+ obj.config = com.config;
2423
+ obj.html = com.ui.html;
2424
+ obj.schema = com.schema ? com.schema.id : null;
2425
+ obj.readme = com.ui.readme;
2426
+ obj.template = com.ui.template;
2427
+ obj.settings = com.ui.settings;
2428
+ obj.inputs = com.inputs;
2429
+ obj.outputs = com.outputs;
2430
+ obj.group = com.group;
2431
+ obj.version = com.version;
2432
+ obj.author = com.author;
2433
+ obj.permissions = com.permissions;
2434
+ arr.push(obj);
2435
+ } else
2436
+ arr.push(com);
2437
+ }
2438
+
2439
+ return arr;
2440
+ };
2441
+
2442
+ var minutes = -1;
2443
+ var memory = 0;
2444
+ var notifier = 0;
2445
+
2446
+ // Captures stats from the Flow
2447
+ flow.onstats = function(stats) {
2448
+
2449
+ if (stats.minutes !== minutes) {
2450
+ minutes = stats.minutes;
2451
+ if (isFLOWSTREAMWORKER)
2452
+ memory = process.memoryUsage().heapUsed;
2453
+ }
2454
+
2455
+ flow.stats.memory = memory;
2456
+ flow.stats.errors = flow.errors.length;
2457
+
2458
+ // Each 9 seconds
2459
+ if (notifier % 3 === 0) {
2460
+ notifier = 0;
2461
+ if (Parent || Flow.$events.stats) {
2462
+ let pstats = { paused: flow.paused, messages: flow.stats.messages, pending: flow.stats.pending, memory: flow.stats.memory, minutes: flow.stats.minutes, errors: flow.stats.errors, mm: flow.stats.mm, pid: process.pid };
2463
+ if (Parent)
2464
+ Parent.postMessage({ TYPE: 'stream/stats', data: pstats });
2465
+ else if (Flow.$events.stats)
2466
+ Flow.emit(flow.$schema.id, pstats);
2467
+ }
2468
+ }
2469
+
2470
+ notifier++;
2471
+ stats.paused = flow.paused;
2472
+
2473
+ flow.stats.TYPE = 'flow/stats';
2474
+ flow.proxy.online && flow.proxy.send(stats);
2475
+ };
2476
+
2477
+ var cleanerid;
2478
+ var problematic = [];
2479
+ var cleaner = function() {
2480
+
2481
+ cleanerid = null;
2482
+
2483
+ for (var key of problematic) {
2484
+ delete meta.components[key];
2485
+ flow.unregister(key);
2486
+ }
2487
+
2488
+ if (flow.proxy.online)
2489
+ refresh_components();
2490
+
2491
+ save();
2492
+ };
2493
+
2494
+ var cleanerservice = function() {
2495
+ cleanerid && clearTimeout(cleanerid);
2496
+ cleanerid = setTimeout(cleaner, 500);
2497
+ };
2498
+
2499
+ flow.onunregister = function(component) {
2500
+ for (var key in flow.httproutes) {
2501
+ var route = flow.httproutes[key];
2502
+ if (route && route.component === component.id)
2503
+ flow.proxy.httproute(key, null);
2504
+ }
2505
+ };
2506
+
2507
+ flow.onregister = function(component) {
2508
+ if (!component.schema && component.schemaid && (component.type === 'pub' || component.type === 'sub' || component.type === 'call')) {
2509
+ var tmp = flow.sources[component.schemaid[0]];
2510
+ if (tmp && tmp.meta) {
2511
+ var arr = component.type === 'pub' ? tmp.meta.publish : component.type === 'call' ? tmp.meta.call : tmp.meta.subscribe;
2512
+ component.schema = arr.findItem('id', component.schemaid[1]);
2513
+ component.itemid = component.schemaid[0];
2514
+ } else {
2515
+ problematic.push(component.id);
2516
+ cleanerservice();
2517
+ }
2518
+ }
2519
+ };
2520
+
2521
+ flow.httproute = function(url, callback) {
2522
+ flow.proxy.httproute(url, callback);
2523
+ };
2524
+
2525
+ flow.ondisconnect = function(instance) {
2526
+
2527
+ if (instance.$statusdelay) {
2528
+ clearTimeout(instance.$statusdelay);
2529
+ instance.$statusdelay = null;
2530
+ }
2531
+
2532
+ for (var key in flow.httproutes) {
2533
+ var route = flow.httproutes[key];
2534
+ if (route && route.id === instance.id)
2535
+ flow.proxy.httproute(key, null);
2536
+ }
2537
+ };
2538
+
2539
+ flow.onconnect = function(instance) {
2540
+
2541
+ instance.env = instance.main.env;
2542
+
2543
+ instance.httproute = function(url, callback) {
2544
+ flow.proxy.httproute(url, callback, instance);
2545
+ };
2546
+
2547
+ instance.href = function(url) {
2548
+ var hostname = (flow.$schema.origin || '') + (flow.$schema.proxypath || '');
2549
+
2550
+ if (url && hostname[hostname.length - 1] === '/')
2551
+ hostname = hostname.substring(0, hostname.length - 1);
2552
+
2553
+ return url ? (hostname + url) : hostname;
2554
+ };
2555
+
2556
+ instance.save = function() {
2557
+ var item = {};
2558
+ item.x = instance.x;
2559
+ item.y = instance.y;
2560
+ item.size = instance.size;
2561
+ item.offset = instance.offset;
2562
+ item.meta = instance.meta;
2563
+ item.note = instance.note;
2564
+ item.config = instance.config;
2565
+ item.outputs = instance.outputs;
2566
+ item.inputs = instance.inputs;
2567
+ item.tab = instance.tab;
2568
+
2569
+ if (!flow.loading) {
2570
+ flow.cleanforce();
2571
+ save();
2572
+ }
2573
+
2574
+ flow.proxy.online && flow.proxy.send({ TYPE: 'flow/redraw', id: instance.id, data: item });
2575
+ };
2576
+
2577
+ instance.newvariables = function(data) {
2578
+ flow.proxy.variables(data || {});
2579
+ };
2580
+
2581
+ instance.newsecrets = function(data) {
2582
+
2583
+ for (var key in data)
2584
+ flow.secrets[key] = data[key];
2585
+
2586
+ for (var key in flow.meta.flow) {
2587
+ var m = flow.meta.flow[key];
2588
+ m.secrets && m.secrets(flow.secrets);
2589
+ m.vary && m.vary('secrets');
2590
+ }
2591
+
2592
+ };
2593
+
2594
+ instance.newflowstream = function(meta, isworker, callback) {
2595
+ return exports.init(meta, isworker, callback, true);
2596
+ };
2597
+
2598
+ instance.io = function(flowstreamid, id, callback) {
2599
+ flow.proxy.io(flowstreamid, id, callback);
2600
+ };
2601
+
2602
+ instance.toinput = function(data, flowstreamid, id, reference) {
2603
+ flow.proxy.input(instance.id, flowstreamid, id, data, reference);
2604
+ };
2605
+
2606
+ instance.output = function(data, flowstreamid, id, reference) {
2607
+ flow.proxy.output(instance.id, data, flowstreamid, id, reference);
2608
+ };
2609
+
2610
+ instance.reconfigure = function(config) {
2611
+ instance.main.reconfigure(instance.id, config);
2612
+ };
2613
+ };
2614
+
2615
+ flow.io = function(flowstreamid, id, callback) {
2616
+ flow.proxy.io(flowstreamid, id, callback);
2617
+ };
2618
+
2619
+ flow.onreconfigure = function(instance, init) {
2620
+ if (!init) {
2621
+ flow.proxy.online && flow.proxy.send({ TYPE: 'flow/config', id: instance.id, data: instance.config });
2622
+ flow.proxy.refresh('configure');
2623
+ save();
2624
+ }
2625
+ };
2626
+
2627
+ flow.onerror = function(err, source) {
2628
+
2629
+ err += '';
2630
+
2631
+ var obj = {};
2632
+ obj.error = err;
2633
+ obj.id = this.id;
2634
+ obj.ts = new Date();
2635
+ obj.source = source;
2636
+
2637
+ flow.errors.unshift(obj);
2638
+
2639
+ if (flow.errors.length > 10)
2640
+ flow.errors.pop();
2641
+
2642
+ flow.proxy.error(err, source, this);
2643
+ flow.proxy.online && flow.proxy.send({ TYPE: 'flow/error', error: err, id: obj.id, ts: obj.ts, source: source });
2644
+ };
2645
+
2646
+ var sendstatusforce = function(instance) {
2647
+ instance.$statusdelay = null;
2648
+ if (instance.$status != null && flow.proxy.online)
2649
+ flow.proxy.online && flow.proxy.send({ TYPE: 'flow/status', id: instance.id, data: instance.$status });
2650
+ };
2651
+
2652
+ // component.status() will execute this method
2653
+ flow.onstatus = function(status, delay) {
2654
+
2655
+ var instance = this;
2656
+
2657
+ if (status != undefined)
2658
+ instance.$status = status;
2659
+
2660
+ if (delay) {
2661
+ if (!instance.$statusdelay)
2662
+ instance.$statusdelay = setTimeout(sendstatusforce, delay || 1000, instance);
2663
+ } else if (instance.$status != null && flow.proxy.online)
2664
+ flow.proxy.online && flow.proxy.send({ TYPE: 'flow/status', id: instance.id, data: instance.$status });
2665
+ };
2666
+
2667
+ // component.dashboard() will execute this method
2668
+ flow.ondashboard = function(status) {
2669
+
2670
+ var instance = this;
2671
+
2672
+ if (status == null)
2673
+ status = instance.$dashboard;
2674
+ else
2675
+ instance.$dashboard = status;
2676
+
2677
+ if (status != null && flow.proxy.online)
2678
+ flow.proxy.online && flow.proxy.send({ TYPE: 'flow/dashboard', id: instance.id, data: status });
2679
+
2680
+ };
2681
+
2682
+ var loaded = false;
2683
+
2684
+ flow.on('schema', function() {
2685
+ if (flow.ready) {
2686
+
2687
+ for (var key in flow.sockets)
2688
+ flow.sockets[key].synchronize();
2689
+
2690
+ if (loaded)
2691
+ flow.proxy.refresh('schema');
2692
+
2693
+ loaded = true;
2694
+ }
2695
+ });
2696
+
2697
+ var makemeta = function() {
2698
+ return { TYPE: 'flow/flowstream', id: flow.$schema.id, version: VERSION, paused: flow.paused, node: F.version_node, total: F.version, name: flow.$schema.name, version2: flow.$schema.version, icon: flow.$schema.icon, reference: flow.$schema.reference, author: flow.$schema.author, color: flow.$schema.color, origin: flow.$schema.origin, notify: flow.$schema.origin + NOTIFYPATH + flow.$schema.id + '-/', readme: flow.$schema.readme, url: flow.$schema.url, proxypath: isFLOWSTREAMWORKER ? flow.$schema.proxypath : '/', env: flow.$schema.env, worker: isFLOWSTREAMWORKER ? (W.workerData ? 'Worker Thread' : 'Child Process') : false, cloning: flow.cloning };
2699
+ };
2700
+
2701
+ flow.proxy.refreshmeta = function() {
2702
+
2703
+ flow.origin = flow.$schema.origin;
2704
+ flow.proxypath = flow.$schema.proxypath || '';
2705
+
2706
+ if (isFLOWSTREAMWORKER) {
2707
+ if (flow.proxypath) {
2708
+ if (!F.server)
2709
+ F.httpload({ unixsocket: flow.$schema.unixsocket });
2710
+ } else if (F.server)
2711
+ F.server.close();
2712
+ }
2713
+
2714
+ flow.cloning = flow.$schema.cloning != false;
2715
+ flow.proxy.send(makemeta(), 0);
2716
+ };
2717
+
2718
+ flow.proxy.newclient = function(clientid, metaonly) {
2719
+ if (flow.proxy.online) {
2720
+ flow.proxy.send(makemeta(), 1, clientid);
2721
+ if (!metaonly) {
2722
+ flow.proxy.send({ TYPE: 'flow/variables', data: flow.variables }, 1, clientid);
2723
+ flow.proxy.send({ TYPE: 'flow/variables2', data: flow.variables2 }, 1, clientid);
2724
+ flow.proxy.send({ TYPE: 'flow/components', data: flow.components(true) }, 1, clientid);
2725
+ flow.proxy.send({ TYPE: 'flow/design', data: flow.export() }, 1, clientid);
2726
+ flow.proxy.send({ TYPE: 'flow/errors', data: flow.errors }, 1, clientid);
2727
+ setTimeout(function() {
2728
+ flow.instances().wait(function(com, next) {
2729
+ com.status();
2730
+ setImmediate(next);
2731
+ }, 3);
2732
+ }, 1500);
2733
+ }
2734
+ }
2735
+ };
2736
+
2737
+ return flow;
2738
+ }
2739
+
2740
+ // TMS implementation:
2741
+ TMS.check = function(item, callback) {
2742
+
2743
+ var client = F.websocketclient();
2744
+
2745
+ if (item.token)
2746
+ client.headers['x-token'] = item.token;
2747
+
2748
+ client.options.reconnect = 0;
2749
+
2750
+ client.on('open', function() {
2751
+ client.tmsready = true;
2752
+ });
2753
+
2754
+ client.on('error', function(err) {
2755
+ client.tmsready = false;
2756
+ callback(err);
2757
+ clearTimeout(client.timeout);
2758
+ });
2759
+
2760
+ client.on('close', function() {
2761
+ client.tmsready = false;
2762
+ callback('401: Unauthorized');
2763
+ });
2764
+
2765
+ client.on('message', function(msg) {
2766
+ switch (msg.type) {
2767
+ case 'ping':
2768
+ msg.type = 'pong';
2769
+ client.send(msg);
2770
+ break;
2771
+ case 'meta':
2772
+ callback(null, msg);
2773
+ clearTimeout(client.timeout);
2774
+ client.close();
2775
+ break;
2776
+ }
2777
+ });
2778
+
2779
+ client.timeout = setTimeout(function() {
2780
+ if (client.tmsready) {
2781
+ client.close();
2782
+ callback('408: Timeout');
2783
+ }
2784
+ }, 2500);
2785
+
2786
+ client.connect(item.url.replace(/^http/g, 'ws'));
2787
+ };
2788
+
2789
+ function makemodel(item) {
2790
+ return { url: item.url, token: item.token, error: item.error };
2791
+ }
2792
+
2793
+ TMS.connect = function(fs, sourceid, callback) {
2794
+
2795
+ if (fs.sockets[sourceid]) {
2796
+ fs.sockets[sourceid].close();
2797
+ delete fs.sockets[sourceid];
2798
+ }
2799
+
2800
+ var client = F.websocketclient();
2801
+
2802
+ var item = fs.sources[sourceid];
2803
+ var prev;
2804
+
2805
+ item.restart = false;
2806
+ client.options.reconnectserver = true;
2807
+ client.callbacks = {};
2808
+ client.callbackindexer = 0;
2809
+ client.callbacktimeout = function(callbackid) {
2810
+ var cb = client.callbacks[callbackid];
2811
+ if (cb) {
2812
+ delete client.callbacks[callbackid];
2813
+ cb(new ErrorBuilder().push(408).output());
2814
+ }
2815
+ };
2816
+
2817
+ if (item.token)
2818
+ client.headers['x-token'] = item.token;
2819
+
2820
+ var syncforce = function() {
2821
+ client.synchronize();
2822
+ };
2823
+
2824
+ client.on('open', function() {
2825
+ prev = null;
2826
+ fs.sockets[item.id] = client;
2827
+ item.error = 0;
2828
+ item.init = true;
2829
+ item.online = true;
2830
+ client.subscribers = {};
2831
+ client.tmsready = true;
2832
+ client.model = makemodel(item);
2833
+ setTimeout(syncforce, 10);
2834
+ });
2835
+
2836
+ client.synchronize = function() {
2837
+
2838
+ if (!client.tmsready)
2839
+ return;
2840
+
2841
+ var publishers = {};
2842
+
2843
+ for (var key in fs.meta.flow) {
2844
+ var instance = fs.meta.flow[key];
2845
+ var com = fs.meta.components[instance.component];
2846
+ if (com && com.itemid === item.id && com.outputs && com.outputs.length) {
2847
+ if (Object.keys(instance.connections).length)
2848
+ publishers[com.schema.id] = 1;
2849
+ }
2850
+ }
2851
+
2852
+ var keys = Object.keys(publishers);
2853
+ var cache = keys.join(',');
2854
+
2855
+ if (!prev || prev !== cache) {
2856
+ prev = cache;
2857
+ client.send({ type: 'subscribers', subscribers: keys });
2858
+ }
2859
+
2860
+ };
2861
+
2862
+ client.on('close', function(code) {
2863
+
2864
+ if (code === 4001)
2865
+ client.destroy();
2866
+
2867
+ item.error = code;
2868
+ item.online = false;
2869
+
2870
+ client.model = makemodel(item);
2871
+ // AUDIT(client, 'close');
2872
+
2873
+ delete fs.sockets[item.id];
2874
+ client.tmsready = false;
2875
+ });
2876
+
2877
+ client.on('message', function(msg) {
2878
+
2879
+ var type = msg.type || msg.TYPE;
2880
+ var tmp;
2881
+
2882
+ switch (type) {
2883
+ case 'meta':
2884
+
2885
+ item.meta = msg;
2886
+
2887
+ tmp = HASH(JSON.stringify(msg)).toString(36);
2888
+ client.subscribers = {};
2889
+ client.publishers = {};
2890
+ client.calls = {};
2891
+
2892
+ for (let pub of msg.publish)
2893
+ client.publishers[pub.id] = pub.schema;
2894
+
2895
+ for (let sub of msg.subscribe)
2896
+ client.subscribers[sub.id] = 1;
2897
+
2898
+ if (msg.call) {
2899
+ for (let call of msg.call)
2900
+ client.calls[call.id] = 1;
2901
+ }
2902
+
2903
+ if (item.checksum !== tmp) {
2904
+ item.checksum = tmp;
2905
+ item.init = false;
2906
+ TMS.refresh2(fs);
2907
+ }
2908
+
2909
+ client.synchronize();
2910
+ break;
2911
+
2912
+ case 'call':
2913
+
2914
+ tmp = client.callbacks[msg.callbackid];
2915
+ if (tmp) {
2916
+ tmp.id && clearTimeout(tmp.id);
2917
+ tmp.callback(msg.error ? msg.data : null, msg.error ? null : msg.data);
2918
+ delete client.callbacks[msg.callbackid];
2919
+ }
2920
+
2921
+ break;
2922
+
2923
+ case 'subscribers':
2924
+ client.subscribers = {};
2925
+ if (msg.subscribers instanceof Array) {
2926
+ for (let key of msg.subscribers)
2927
+ client.subscribers[key] = 1;
2928
+ }
2929
+ break;
2930
+
2931
+ case 'publish':
2932
+
2933
+ if (fs.paused)
2934
+ return;
2935
+
2936
+ tmp = client.publishers[msg.id];
2937
+ if (tmp) {
2938
+ // HACK: very fast validation
2939
+ var err = new F.TBuilders.ErrorBuilder();
2940
+ var data = F.TJSONSchema.transform(tmp, err, msg.data, true);
2941
+ if (data) {
2942
+ var id = 'pub' + item.id + 'X' + msg.id;
2943
+ for (let key in fs.meta.flow) {
2944
+ var flow = fs.meta.flow[key];
2945
+ if (flow.component === id)
2946
+ flow.process(data, client);
2947
+ }
2948
+ }
2949
+ }
2950
+
2951
+ break;
2952
+ }
2953
+ });
2954
+
2955
+ client.connect(item.url.replace(/^http/g, 'ws'));
2956
+ callback && setImmediate(callback);
2957
+ };
2958
+
2959
+ // In the Flow will be the "Publish" mentioned in the "Subscribe" group
2960
+ const TEMPLATE_PUBLISH = `<script total>
2961
+
2962
+ exports.name = '{0}';
2963
+ exports.icon = '{3}';
2964
+ exports.config = {};
2965
+ exports.outputs = [{ id: 'publish', name: 'Output' }];
2966
+ exports.group = 'Subscribe';
2967
+ exports.type = 'pub';
2968
+ exports.schemaid = ['{7}', '{1}'];
2969
+
2970
+ exports.make = function(instance) {
2971
+ instance.process = function(msg, client) {
2972
+ instance.send('publish', msg, client);
2973
+ };
2974
+ };
2975
+
2976
+ </script>
2977
+
2978
+ <style>
2979
+ .f-{5} .url { font-size: 11px; }
2980
+ </style>
2981
+
2982
+ <readme>
2983
+ {2}
2984
+ </readme>
2985
+
2986
+ <body>
2987
+ <header>
2988
+ <div><i class="{3} mr5"></i><span>{6} / <b>{1}</b></span></div>
2989
+ <div class="url">{4}</div>
2990
+ </header>
2991
+ </body>`;
2992
+
2993
+ // In the Flow will be the "Subscribe" mentioned in the "Publish" group
2994
+ const TEMPLATE_SUBSCRIBE = `<script total>
2995
+
2996
+ exports.name = '{0}';
2997
+ exports.icon = '{3}';
2998
+ exports.group = 'Publish';
2999
+ exports.config = {};
3000
+ exports.inputs = [{ id: 'subscribe', name: 'Input' }];
3001
+ exports.type = 'sub';
3002
+ exports.schemaid = ['{7}', '{1}'];
3003
+
3004
+ exports.make = function(instance) {
3005
+ instance.message = function($) {
3006
+ var socket = instance.main.sockets['{7}'];
3007
+ if (socket && socket.subscribers && socket.subscribers['{1}']) {
3008
+
3009
+ var data = $.data;
3010
+
3011
+ /*
3012
+ var err = new F.ErrorBuilder();
3013
+ data = F.TJSONSchema.transform(schema, err, data, true);
3014
+
3015
+ if (err.is) {
3016
+ $.destroy();
3017
+ return;
3018
+ }
3019
+
3020
+ */
3021
+
3022
+ socket.send({ type: 'subscribe', id: '{1}', data: data });
3023
+ }
3024
+ $.destroy();
3025
+ };
3026
+ };
3027
+
3028
+ </script>
3029
+
3030
+ <style>
3031
+ .f-{5} .url { font-size: 11px; }
3032
+ </style>
3033
+
3034
+ <readme>
3035
+ {2}
3036
+ </readme>
3037
+
3038
+ <body>
3039
+ <header>
3040
+ <div><i class="{3} mr5"></i><span>{6} / <b>{1}</b></span></div>
3041
+ <div class="url">{4}</div>
3042
+ </header>
3043
+ </body>`;
3044
+
3045
+ const TEMPLATE_CALL = `<script total>
3046
+
3047
+ exports.name = '{0}';
3048
+ exports.icon = '{3}';
3049
+ exports.config = { timeout: 60000 };
3050
+ exports.inputs = [{ id: 'input', name: 'Input' }];
3051
+ exports.outputs = [{ id: 'output', name: 'Output' }, { id: 'error', name: 'Error' }];
3052
+ exports.group = 'Calls';
3053
+ exports.type = 'call';
3054
+ exports.schemaid = ['{7}', '{1}'];
3055
+
3056
+ exports.make = function(instance, config) {
3057
+
3058
+ instance.message = function($, client) {
3059
+ var socket = instance.main.sockets['{7}'];
3060
+ if (socket && socket.calls && socket.calls['{1}']) {
3061
+
3062
+ var data = $.data;
3063
+
3064
+ /*
3065
+ var err = new F.ErrorBuilder();
3066
+ data = F.TJSONSchema.transform(schema, err, data, true);
3067
+
3068
+ if (err.is) {
3069
+ $.send('error', err.toString());
3070
+ return;
3071
+ }
3072
+
3073
+ */
3074
+
3075
+ var callback = function(err, response) {
3076
+ if (err)
3077
+ $.send('error', err);
3078
+ else
3079
+ $.send('output', response);
3080
+ };
3081
+
3082
+ var callbackid = (socket.callbackindexer++) + '';
3083
+
3084
+ if (socket.callbackindexer > 999999999)
3085
+ socket.callbackindexer = 0;
3086
+
3087
+ socket.callbacks[callbackid] = { callback: callback, id: setTimeout(socket.callbacktimeout, config.timeout, callbackid) };
3088
+ socket.send({ type: 'call', id: '{1}', data: data, callbackid: callbackid });
3089
+
3090
+ } else
3091
+ $.destroy();
3092
+ };
3093
+ };
3094
+
3095
+ </script>
3096
+
3097
+ <style>
3098
+ .f-{5} .url { font-size: 11px; }
3099
+ </style>
3100
+
3101
+ <readme>
3102
+ {2}
3103
+ </readme>
3104
+
3105
+ <body>
3106
+ <header>
3107
+ <div><i class="{3} mr5"></i><span>{6} / <b>{1}</b></span></div>
3108
+ <div class="url">{4}</div>
3109
+ </header>
3110
+ </body>`;
3111
+
3112
+ function beautifyjsonschema(schema) {
3113
+
3114
+ var builder = ['__Data__:\n```json'];
3115
+
3116
+ for (var key in schema.properties) {
3117
+ var prop = schema.properties[key];
3118
+ var val = prop.type;
3119
+ var required = schema.required ? schema.required.includes(key) : false;
3120
+ if (prop.enum)
3121
+ val = prop.enum.join('|');
3122
+ builder.push((required ? '*' : '') + key + ' {' + val + '}');
3123
+ }
3124
+ builder.join('```');
3125
+ return builder.join('\n');
3126
+ }
3127
+
3128
+ TMS.refresh = function(fs, callback) {
3129
+
3130
+ Object.keys(fs.sources).wait(function(key, next) {
3131
+
3132
+ var item = fs.sources[key];
3133
+ if (item.init) {
3134
+ if (item.restart || !fs.sources[key])
3135
+ TMS.connect(fs, item.id, next);
3136
+ else
3137
+ next();
3138
+
3139
+ } else {
3140
+
3141
+ var index = item.url.indexOf('/', 10);
3142
+ var url = item.url.substring(0, index);
3143
+
3144
+ if (item.meta.publish instanceof Array) {
3145
+ for (var i = 0; i < item.meta.publish.length; i++) {
3146
+ var m = item.meta.publish[i];
3147
+ var readme = [];
3148
+
3149
+ readme.push('# ' + m.id);
3150
+ readme.push('- URL address: <' + url + '>');
3151
+ readme.push('- Channel: __publish__');
3152
+ readme.push('- JSON schema `' + m.id + '.json`');
3153
+ readme.push('');
3154
+ readme.push(beautifyjsonschema(m.schema));
3155
+
3156
+ var id = 'pub' + item.id + 'X' + m.id;
3157
+ var template = TEMPLATE_PUBLISH.format(item.meta.name, m.id, readme.join('\n'), m.icon || 'fas fa-broadcast-tower', m.url, id, item.meta.name.max(15), item.id);
3158
+ var com = fs.add(id, template);
3159
+ m.url = url;
3160
+ com.type = 'pub';
3161
+ com.itemid = item.id;
3162
+ com.schema = m;
3163
+ }
3164
+ }
3165
+
3166
+ if (item.meta.subscribe instanceof Array) {
3167
+ for (var i = 0; i < item.meta.subscribe.length; i++) {
3168
+ var m = item.meta.subscribe[i];
3169
+ var readme = [];
3170
+
3171
+ readme.push('# ' + m.id);
3172
+ readme.push('- URL address: <' + url + '>');
3173
+ readme.push('- Channel: __subscribe__');
3174
+ readme.push('- JSON schema `' + m.id + '.json`');
3175
+ readme.push('');
3176
+ readme.push(beautifyjsonschema(m));
3177
+
3178
+ var id = 'sub' + item.id + 'X' + m.id;
3179
+ var template = TEMPLATE_SUBSCRIBE.format(item.meta.name, m.id, readme.join('\n'), m.icon || 'fas fa-satellite-dish', m.url, id, item.meta.name.max(15), item.id);
3180
+ var com = fs.add(id, template);
3181
+ m.url = url;
3182
+ com.type = 'sub';
3183
+ com.itemid = item.id;
3184
+ com.schema = m;
3185
+ }
3186
+ }
3187
+
3188
+ if (item.meta.call instanceof Array) {
3189
+ for (var i = 0; i < item.meta.call.length; i++) {
3190
+ var m = item.meta.call[i];
3191
+ var readme = [];
3192
+
3193
+ readme.push('# ' + m.id);
3194
+ readme.push('- URL address: <' + url + '>');
3195
+ readme.push('- Channel: __call__');
3196
+ readme.push('- JSON schema `' + m.id + '.json`');
3197
+ readme.push('');
3198
+ readme.push(beautifyjsonschema(m.schema));
3199
+
3200
+ var id = 'cal' + item.id + 'X' + m.id;
3201
+ var template = TEMPLATE_CALL.format(item.meta.name, m.id, readme.join('\n'), m.icon || 'fa fa-plug', m.url, id, item.meta.name.max(15), item.id);
3202
+ var com = fs.add(id, template);
3203
+ m.url = url;
3204
+ com.type = 'call';
3205
+ com.itemid = item.id;
3206
+ com.schema = m;
3207
+ }
3208
+ }
3209
+
3210
+ if (item.socket)
3211
+ next();
3212
+ else
3213
+ TMS.connect(fs, item.id, next);
3214
+ }
3215
+
3216
+ }, function() {
3217
+
3218
+ var components = fs.meta.components;
3219
+ var unregister = [];
3220
+
3221
+ for (var key in components) {
3222
+ var com = components[key];
3223
+ var type = com.type;
3224
+ if (type === 'pub' || type === 'sub' || type === 'call') {
3225
+ var index = key.indexOf('X');
3226
+ if (index !== -1) {
3227
+
3228
+ var sourceid = key.substring(3, index);
3229
+ var subid = key.substring(index + 1);
3230
+ var source = fs.sources[sourceid];
3231
+
3232
+ if (source) {
3233
+ if (type === 'call') {
3234
+ if (source.meta.call instanceof Array) {
3235
+ if (source.meta.call.findItem('id', subid))
3236
+ continue;
3237
+ }
3238
+ } else if (type === 'pub') {
3239
+ if (source.meta.publish instanceof Array) {
3240
+ if (source.meta.publish.findItem('id', subid))
3241
+ continue;
3242
+ }
3243
+ } else {
3244
+ if (source.meta.subscribe instanceof Array) {
3245
+ if (source.meta.subscribe.findItem('id', subid))
3246
+ continue;
3247
+ }
3248
+ }
3249
+ }
3250
+
3251
+ unregister.push(key);
3252
+ }
3253
+ }
3254
+ }
3255
+
3256
+ unregister.wait(function(key, next) {
3257
+ fs.unregister(key, next);
3258
+ }, function() {
3259
+
3260
+ if (fs.proxy.online) {
3261
+ fs.proxy.send({ TYPE: 'flow/components', data: fs.components(true) });
3262
+ fs.proxy.send({ TYPE: 'flow/design', data: fs.export() });
3263
+ }
3264
+
3265
+ fs.save();
3266
+ callback && callback();
3267
+ });
3268
+
3269
+ });
3270
+
3271
+ };
3272
+
3273
+ TMS.synchronize = function(fs) {
3274
+
3275
+ var sync = {};
3276
+
3277
+ for (var key in fs.meta.components) {
3278
+ var com = fs.meta.components[key];
3279
+ if (com.itemid)
3280
+ sync[com.itemid] = fs.sources.findItem('id', com.itemid);
3281
+ }
3282
+
3283
+ for (var key in sync) {
3284
+ var source = sync[key];
3285
+ if (source && source.socket)
3286
+ source.socket.synchronize();
3287
+ }
3288
+ };
3289
+
3290
+ TMS.refresh2 = function(fs) {
3291
+ setTimeout2('tms_refresh_' + fs.name, fs => TMS.refresh(fs), 500, null, fs);
3292
+ };
3293
+
3294
+ function initrunning() {
3295
+
3296
+ if (isrunning)
3297
+ return;
3298
+
3299
+ isrunning = true;
3300
+
3301
+ F.on('service', function() {
3302
+ if (CALLBACKID > 999999999)
3303
+ CALLBACKID = 1;
3304
+ });
3305
+
3306
+ F.on('exit', function() {
3307
+ for (var key in FLOWS) {
3308
+ var flow = FLOWS[key];
3309
+ if (flow.terminate || flow.kill) {
3310
+ if (flow.terminate)
3311
+ flow.terminate();
3312
+ else
3313
+ flow.kill(9);
3314
+ }
3315
+ }
3316
+ });
3317
+
3318
+ }
3319
+
3320
+ function makeunixsocket(id) {
3321
+ return F.isWindows ? ('\\\\?\\pipe\\flowstream' + F.directory.makeid() + id + Date.now().toString(36)) : (F.Path.join(F.Os.tmpdir(), 'flowstream_' + F.directory.makeid() + '_' + id + '_' + Date.now().toString(36) + '.socket'));
3322
+ }
3323
+
3324
+ if (process.argv[1].endsWith('flow-flowstream.js')) {
3325
+
3326
+ isFLOWSTREAMWORKER = W.workerData || process.argv.indexOf('--fork') !== -1;
3327
+
3328
+ // Runs the worker
3329
+ if (W.workerData) {
3330
+ F.dir(F.path.join(__dirname, '../'));
3331
+ exports.init(W.workerData);
3332
+ }
3333
+
3334
+ if (process.argv.includes('--fork')) {
3335
+
3336
+ process.once('message', function(msg) {
3337
+ if (msg.TYPE === 'init') {
3338
+ Parent = process;
3339
+ if (!Parent.postMessage)
3340
+ Parent.postMessage = process.send;
3341
+ F.dir(process.argv[2]);
3342
+ exports.init(msg.data);
3343
+ }
3344
+ });
3345
+
3346
+ F.on('error', function(obj) {
3347
+ if (obj.error.indexOf('ERR_IPC_CHANNEL_CLOSED') !== -1)
3348
+ process.exit(1);
3349
+ });
3350
+ }
3351
+
3352
+ }