total5 0.0.2 → 0.0.3-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.
package/bin/total5 CHANGED
@@ -93,10 +93,13 @@ FUNC.translate = function(filename) {
93
93
  var dictionary = {};
94
94
 
95
95
  for (let filename of files) {
96
- let body = F.Fs.readFileSync(filename, 'utf8');
97
- let dict = translate(body);
98
- for (let key in dict)
99
- dictionary[key] = dict[key];
96
+ let ext = U.getExtension(filename);
97
+ if (ext === 'js' || ext === 'html') {
98
+ let body = F.Fs.readFileSync(filename, 'utf8');
99
+ let dict = translate(body);
100
+ for (let key in dict)
101
+ dictionary[key] = dict[key];
102
+ }
100
103
  }
101
104
 
102
105
  let builder = [];
package/changelog.txt CHANGED
@@ -1,3 +1,24 @@
1
+ ========================
2
+ 0.0.3
3
+ ========================
4
+
5
+ - extended icon validator by adding support for `tic` keyword
6
+ - added new events `ON('request')`, `ON('controller')` and `ON('websocket')`
7
+ - fixed applying the `$insecure` option in the FlowStream
8
+ - fixed routing for unexpected content-type
9
+ - added `controller.authorize([callback])` method
10
+ - improved `translate` command in `total5` command-line tool
11
+ - fixed link generator for email and phone numbers in `Markdown`
12
+ - fixed auto-restarting app in the `debug` mode with bundles
13
+ - fixed global settings for JS/CSS/HTML minificator
14
+ - improved merging files
15
+ - fixed middleware with `_` underscore character in routes
16
+ - fixed `CORS()` method
17
+ - improved `CORS` checker
18
+ - added support for inheriting inline schemas in the form e.g. `id:String,@address,@products`
19
+ - added `ctrl.image(opt)` method
20
+ - extended `ctrl.filefs()` method by returing `opt` object
21
+
1
22
  ========================
2
23
  0.0.2
3
24
  ========================
package/controller.js CHANGED
@@ -188,6 +188,10 @@ Controller.prototype.callback = function(err, value) {
188
188
 
189
189
  };
190
190
 
191
+ Controller.prototype.cancel = function() {
192
+ this.iscanceled = true;
193
+ };
194
+
191
195
  Controller.prototype.csrf = function() {
192
196
  return F.def.onCSRFcreate(this);
193
197
  };
@@ -570,6 +574,67 @@ Controller.prototype.filefs = function(name, id, download, checkmeta) {
570
574
  opt.check = checkmeta;
571
575
 
572
576
  F.filestorage(name).http(ctrl, opt);
577
+ return opt;
578
+ };
579
+
580
+ Controller.prototype.image = function(opt) {
581
+
582
+ // opt.ext {String} required, image extension
583
+ // opt.date {Date} optional, for HTTP cache
584
+ // opt.cache {String} optional, a cache key
585
+ // opt.load {Function(next(stream), opt)};
586
+ // opt.image {Function(img, opt)}
587
+
588
+ var ctrl = this;
589
+ var date = opt.date ? opt.date instanceof Date ? opt.date.toUTCString() : opt.date : null;
590
+
591
+ // HTTP Cache
592
+ if (ctrl.response.cache && date && ctrl.notmodified(date))
593
+ return;
594
+
595
+ if (opt.cache) {
596
+ var tmp = F.path.tmp('img_' + opt.cache + '.' + opt.ext);
597
+
598
+ if (!DEBUG && date)
599
+ ctrl.httpcache(date);
600
+
601
+ F.Fs.lstat(tmp, function(err) {
602
+ if (err) {
603
+ opt.load.call(ctrl, function(stream) {
604
+ var img = F.TImages.load(stream);
605
+ opt.image.call(ctrl, img, opt);
606
+ img.save(tmp, function(err) {
607
+ if (err)
608
+ ctrl.fallback(404, err);
609
+ else
610
+ ctrl.file(tmp);
611
+ });
612
+ }, opt);
613
+ } else
614
+ ctrl.file(tmp);
615
+ });
616
+ } else {
617
+
618
+ opt.load.call(ctrl, function(stream) {
619
+
620
+ var img = F.TImages.load(stream);
621
+ var response = ctrl.response;
622
+
623
+ opt.image.call(ctrl, img, opt);
624
+
625
+ if (ctrl.response.headers.expires)
626
+ delete response.headers.expires;
627
+
628
+ if (!DEBUG && date)
629
+ ctrl.httpcache(date);
630
+
631
+ response.headers.etag = '858' + F.config.$httpetag;
632
+ response.headers['content-type'] = F.TUtils.getContentType(opt.ext);
633
+ ctrl.res.writeHead(response.status, response.headers);
634
+ F.stats.response.stream++;
635
+ img.pipe(ctrl.res);
636
+ }, opt);
637
+ }
573
638
  };
574
639
 
575
640
  Controller.prototype.binary = function(buffer, type, download) {
@@ -881,6 +946,7 @@ Controller.prototype.hostname = function(path) {
881
946
  Controller.prototype.$route = function() {
882
947
 
883
948
  var ctrl = this;
949
+
884
950
  if (ctrl.isfile) {
885
951
 
886
952
  if (F.routes.files.length) {
@@ -943,7 +1009,6 @@ Controller.prototype.$route = function() {
943
1009
 
944
1010
  ctrl.payload = Buffer.concat(ctrl.payload);
945
1011
  F.stats.performance.download += ctrl.payload.length / 1024 / 1024;
946
-
947
1012
  switch (ctrl.datatype) {
948
1013
  case 'json':
949
1014
  ctrl.body = F.def.parsers.json(ctrl.payload.toString('utf8'));
@@ -987,6 +1052,29 @@ function readfile(filename, callback) {
987
1052
  });
988
1053
  }
989
1054
 
1055
+ /*
1056
+ @Path: Controller
1057
+ @Method: instance.authorize([callback]); #callback {Function(err, session)} optional;
1058
+ The method performs "manual" authorization. If the user is logged in, then `session {Object}` is not null, otherwise `null`. If you don't use the `callback` argument, then the method returns `Promise`.
1059
+ */
1060
+ Controller.prototype.authorize = function(callback) {
1061
+
1062
+ var ctrl = this;
1063
+
1064
+ if (!callback)
1065
+ return new Promise((resolve, reject) => ctrl.authorize((err, response) => resolve(response)));
1066
+
1067
+ if (F.def.onAuthorize) {
1068
+ var opt = new F.TBuilders.Options(ctrl);
1069
+ opt.TYPE = 'auth';
1070
+ opt.query = ctrl.query;
1071
+ opt.next = opt.callback;
1072
+ opt.$callback = (err, response) => callback(null, response);
1073
+ F.def.onAuthorize(opt);
1074
+ } else
1075
+ callback();
1076
+ }
1077
+
990
1078
  Controller.prototype.notmodified = function(date) {
991
1079
  var ctrl = this;
992
1080
  if (ctrl.headers['if-modified-since'] === date) {
@@ -1137,6 +1225,7 @@ function execute(ctrl, skipmiddleware) {
1137
1225
 
1138
1226
  let schema = body.schema.split('/');
1139
1227
  let endpoint = ctrl.route.api[schema[0]];
1228
+
1140
1229
  if (endpoint) {
1141
1230
 
1142
1231
  if ((endpoint.auth === 1 && ctrl.user == null) || (endpoint.auth === 2 && ctrl.user)) {
@@ -1170,29 +1259,56 @@ function execute(ctrl, skipmiddleware) {
1170
1259
 
1171
1260
  ctrl.params = params;
1172
1261
  ctrl.query = query ? query.parseEncoded() : {};
1262
+
1263
+ if (body)
1264
+ ctrl.body = body;
1265
+
1266
+ if (F.$events.controller) {
1267
+ F.emit('controller', ctrl);
1268
+ if (ctrl.iscanceled)
1269
+ return;
1270
+ }
1271
+
1173
1272
  let action = endpoint.action;
1174
- if (action) {
1175
- ctrl.body = body || {};
1273
+ if (action)
1176
1274
  action(ctrl);
1177
- } else
1178
- F.action(endpoint.actions, body || {}, ctrl).autorespond();
1275
+ else
1276
+ F.action(endpoint.actions, ctrl.body, ctrl).autorespond();
1277
+
1179
1278
  return;
1180
1279
  }
1181
1280
  }
1182
1281
 
1183
- ctrl.fallback(400, 'Invalid data');
1184
1282
  }
1283
+
1284
+ ctrl.fallback(400, 'Invalid data');
1285
+
1185
1286
  } else {
1287
+
1288
+ if (F.$events.controller) {
1289
+ /*
1290
+ @Path: Framework
1291
+ @Event: ON('controller', function(ctrl) { ... }); #ctrl {Controller};
1292
+ The event captures all processed controllers on HTTP requests. The next processing can be canceled via the `ctrl.cancel()` method.
1293
+ */
1294
+ F.emit('controller', ctrl);
1295
+ if (ctrl.iscanceled)
1296
+ return;
1297
+ }
1298
+
1186
1299
  if (ctrl.route.actions) {
1187
1300
  F.action(ctrl.route.actions, ctrl.body, ctrl).autorespond();
1188
1301
  } else {
1302
+
1189
1303
  if (ctrl.route.view) {
1190
1304
  ctrl.view(ctrl.route.view);
1191
1305
  return;
1192
1306
  }
1307
+
1193
1308
  let action = ctrl.route.action;
1194
1309
  if (!action)
1195
1310
  action = auto_view;
1311
+
1196
1312
  action(ctrl);
1197
1313
  }
1198
1314
  }
package/debug.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // Debug module (Watcher)
2
2
  // The MIT License
3
- // Copyright 2012-2023 (c) Peter Širka <petersirka@gmail.com>
3
+ // Copyright 2012-2024 (c) Peter Širka <petersirka@gmail.com>
4
4
 
5
5
  'use strict';
6
6
 
@@ -76,7 +76,7 @@ function runwatching() {
76
76
  const REG_PUBLIC = /\/public\//i;
77
77
  const REG_INDEX = new RegExp(FILENAME.replace(/\.js$/, '') + '_.*?\\.js$');
78
78
  const REG_EXTENSION = /\.(js|ts|resource|package|bundle|build|flow|url)$/i;
79
- const REG_RELOAD = /\.(js|ts|css|html|htm|jpg|png|gif|ico|svg|resource)$/i;
79
+ const REG_RELOAD = /\.(js|ts|css|html|htm|jpg|png|gif|ico|svg|webp|resource)$/i;
80
80
  const isRELOAD = !!options.livereload;
81
81
  const SPEED = isRELOAD ? 1000 : 1500;
82
82
  const ARGV = F.TUtils.clone(process.argv);
@@ -124,8 +124,8 @@ function runwatching() {
124
124
  F.Path.join(directory, 'middleware'),
125
125
  F.Path.join(directory, 'bundles'),
126
126
  F.Path.join(directory, 'flowstreams'),
127
- F.Path.join(directory, '/startup/'),
128
- F.Path.join(directory, '/plugins/')
127
+ F.Path.join(directory, 'startup'),
128
+ F.Path.join(directory, 'plugins')
129
129
  ];
130
130
 
131
131
  const SRC = F.Path.join(directory, '.src');
@@ -186,7 +186,7 @@ function runwatching() {
186
186
  }
187
187
  }
188
188
 
189
- if (skipbundle) {
189
+ if (!skipbundle) {
190
190
  try {
191
191
  F.Fs.statSync(F.path.root('bundles'));
192
192
  isbundle = true;
@@ -200,14 +200,19 @@ function runwatching() {
200
200
 
201
201
  function onFilter(path, isdir) {
202
202
  var p = path.substring(directory.length);
203
+
203
204
  if (isbundle)
204
205
  return isdir ? SRC !== path : !ignore[p];
206
+
205
207
  if (isRELOAD)
206
208
  return isdir ? true : REG_RELOAD.test(path);
209
+
207
210
  if (isdir)
208
211
  return true;
212
+
209
213
  if (!REG_PUBLIC.test(path) && REG_EXTENSION.test(path))
210
214
  return true;
215
+
211
216
  return false;
212
217
  }
213
218
 
package/filestorage.js CHANGED
@@ -1018,14 +1018,13 @@ FP.http = function(ctrl, opt) {
1018
1018
 
1019
1019
  response.headers['x-size'] = obj.size;
1020
1020
 
1021
- if (opt.image) {
1022
- /*
1023
- res.opt.stream = { filename: filename, start: HEADERSIZE, custom: true };
1024
- res.opt.make = opt.make;
1025
- res.opt.cache = opt.cache !== false;
1026
- res.opt.persistent = false;
1027
- res.$image();
1028
- */
1021
+ if (opt.image && IMAGES[obj.ext]) {
1022
+ var img = {};
1023
+ img.ext = obj.ext;
1024
+ img.cache = opt.cache;
1025
+ img.load = next => next(F.Fs.createReadStream(filename, { start: HEADERSIZE }));
1026
+ img.image = opt.image;
1027
+ ctrl.image(img);
1029
1028
  } else {
1030
1029
 
1031
1030
  var range = ctrl.headers.range;
@@ -1077,10 +1076,8 @@ FP.http = function(ctrl, opt) {
1077
1076
 
1078
1077
  } else {
1079
1078
  var stream = F.Fs.createReadStream(filename, { start: HEADERSIZE });
1080
-
1081
1079
  if (!opt.download && !DEBUG && date)
1082
1080
  ctrl.httpcache(date);
1083
-
1084
1081
  ctrl.stream(obj.type, stream);
1085
1082
  }
1086
1083
  }
@@ -1467,6 +1467,9 @@ function init_worker(meta, type, callback) {
1467
1467
 
1468
1468
  var forkargs = [F.directory, '--fork'];
1469
1469
 
1470
+ if (F.config.$insecure)
1471
+ forkargs.push('--insecure');
1472
+
1470
1473
  if (meta.memory)
1471
1474
  forkargs.push('--max-old-space-size=' + meta.memory);
1472
1475
 
@@ -3323,7 +3326,12 @@ function makeunixsocket(id) {
3323
3326
 
3324
3327
  if (process.argv[1].endsWith('flow-flowstream.js')) {
3325
3328
 
3326
- isFLOWSTREAMWORKER = W.workerData || process.argv.indexOf('--fork') !== -1;
3329
+ isFLOWSTREAMWORKER = W.workerData || process.argv.includes('--fork');
3330
+
3331
+ if (process.argv.includes('--insecure')) {
3332
+ process.env.NODE_TLS_REJECT_UNAUTHORIZED = '1';
3333
+ F.config.$insecure = true;
3334
+ }
3327
3335
 
3328
3336
  // Runs the worker
3329
3337
  if (W.workerData) {
package/global.js CHANGED
@@ -11,6 +11,7 @@ global.OFF = (name, fn) => F.off(name, fn);
11
11
  global.ROUTE = F.TRouting.route;
12
12
  global.PROXY = F.TRouting.proxy;
13
13
  global.print = console.log;
14
+ global.Utils = F.TUtils;
14
15
  global.LOAD = F.load;
15
16
  global.LOADCONFIG = F.loadconfig;
16
17
  global.LOADRESOURCE = F.loadresource;
@@ -25,6 +26,7 @@ global.CLEANUP = F.cleanup;
25
26
  global.NEWDB = F.newdb;
26
27
  global.IMPORT = F.import;
27
28
  global.REQUIRE = F.require;
29
+ global.RESTORE = F.restore;
28
30
  global.CRON = F.cron;
29
31
  global.UID = F.uid;
30
32
  global.SUCCESS = value => DEF.onSuccess(value);
@@ -48,6 +50,7 @@ global.MODS = F.modules;
48
50
  global.PLUGINS = F.plugins;
49
51
  global.DECRYPT = F.decrypt;
50
52
  global.ENCRYPT = F.encrypt;
53
+ global.BACKUP = F.backup;
51
54
  global.DECRYPTREQ = F.decryptreq;
52
55
  global.ENCRYPTREQ = F.encryptreq;
53
56
  global.PATH = F.path;
@@ -105,7 +108,7 @@ global.LDAP = function(opt, callback) {
105
108
  global.CORS = function(origin) {
106
109
  if (origin && origin[0] === '+') {
107
110
  if (F.config.$cors !== '*')
108
- F.config.$cors = (F.config.$cors ? ',' : '') + origin.substring(1);
111
+ F.config.$cors += (F.config.$cors ? ',' : '') + origin.substring(1);
109
112
  } else
110
113
  F.config.$cors = origin || '*';
111
114
  F.emit('$cors');
package/http.js CHANGED
@@ -65,6 +65,19 @@ exports.listen = function(req, res) {
65
65
  // Pending requests
66
66
  F.temporary.pending.push(ctrl);
67
67
 
68
+ if (F.$events.request) {
69
+
70
+ /*
71
+ @Path: Framework
72
+ @Event: ON('request', function(ctrl) { ... }); #ctrl {Controller};
73
+ The event captures all incoming requests. The next processing can be canceled via the `ctrl.cancel()` method.
74
+ */
75
+ F.emit('request', ctrl);
76
+
77
+ if (ctrl.iscanceled)
78
+ return;
79
+ }
80
+
68
81
  if (ctrl.headers.origin && (F.def.onCORS || F.config.$cors)) {
69
82
  if (F.TRouting.lookupcors(ctrl))
70
83
  ctrl.$route();
package/images.js CHANGED
@@ -222,11 +222,6 @@ ImageProto.measure = function(callback) {
222
222
  return self;
223
223
  };
224
224
 
225
- ImageProto.$$measure = function() {
226
- var self = this;
227
- return callback => self.measure(callback);
228
- };
229
-
230
225
  ImageProto.save = function(filename, callback, writer) {
231
226
 
232
227
  var self = this;
@@ -284,13 +279,6 @@ ImageProto.save = function(filename, callback, writer) {
284
279
  return self;
285
280
  };
286
281
 
287
- ImageProto.$$save = function(filename, writer) {
288
- var self = this;
289
- return function(callback) {
290
- self.save(filename, callback, writer);
291
- };
292
- };
293
-
294
282
  ImageProto.pipe = function(stream, type, options) {
295
283
 
296
284
  var self = this;
@@ -327,7 +315,7 @@ ImageProto.pipe = function(stream, type, options) {
327
315
  return self;
328
316
  };
329
317
 
330
- ImageProto.stream = function(type, writer) {
318
+ ImageProto.writer = function(type, writer) {
331
319
 
332
320
  var self = this;
333
321
 
@@ -428,13 +416,6 @@ ImageProto.identify = function(callback) {
428
416
  return self;
429
417
  };
430
418
 
431
- ImageProto.$$identify = function() {
432
- var self = this;
433
- return function(callback) {
434
- self.identify(callback);
435
- };
436
- };
437
-
438
419
  ImageProto.push = function(key, value, priority, encode) {
439
420
  var self = this;
440
421
  var cmd = key;
package/index.js CHANGED
@@ -36,7 +36,7 @@ global.DEF = {};
36
36
 
37
37
  F.id = '';
38
38
  F.clusterid = '';
39
- F.is5 = F.version = 5000;
39
+ F.is5 = F.version = 5003;
40
40
  F.isBundle = false;
41
41
  F.isLoaded = false;
42
42
  F.version_header = '5';
@@ -1452,11 +1452,15 @@ F.merge = function(url) {
1452
1452
  }
1453
1453
 
1454
1454
  if (link[0] !== '~' && link[0] !== '_') {
1455
- let ext = F.TUtils.getExtension(link);
1456
- if (ext === 'js')
1457
- link = F.path.public('/js/' + link);
1458
- else
1459
- link = F.path.public('/css/' + link);
1455
+ if (link[0] === '/') {
1456
+ link = F.path.public(link);
1457
+ } else {
1458
+ let ext = F.TUtils.getExtension(link);
1459
+ if (ext === 'js')
1460
+ link = F.path.public('/js/' + link);
1461
+ else
1462
+ link = F.path.public('/css/' + link);
1463
+ }
1460
1464
  arr.push(link);
1461
1465
  } else
1462
1466
  arr.push(F.path.route(link, 'public'));
@@ -2831,9 +2835,9 @@ F.dir();
2831
2835
  // Init CORS
2832
2836
  F.on('$cors', function() {
2833
2837
 
2834
- var arr = (F.config.$cors || '').toLowerCase().split(',').trim();
2838
+ var arr = (F.config.$cors || '').toLowerCase().split(/,|;|\|/).trim();
2835
2839
  var wildcard = [];
2836
- var strict = [];
2840
+ var strict = {};
2837
2841
 
2838
2842
  for (let i = 0; i < arr.length; i++) {
2839
2843
  let val = arr[i];
@@ -2841,7 +2845,7 @@ F.on('$cors', function() {
2841
2845
  if (val[0] === '*' && val.length > 1)
2842
2846
  wildcard.push(val.substring(1));
2843
2847
  else
2844
- strict.push(val);
2848
+ strict[val] = 1;
2845
2849
  }
2846
2850
  }
2847
2851
 
package/jsonschema.js CHANGED
@@ -8,7 +8,7 @@ require('./index');
8
8
 
9
9
  const REG_NUMBER = /[\d,.]+/;
10
10
  const REG_COLOR = /^#([A-F0-9]{3}|[A-F0-9]{6}|[A-F0-9]{8})$/i;
11
- const REG_ICON = /^(ti|far|fab|fad|fal|fas|fa)?\s(fa|ti)-[a-z0-9-]+$/;
11
+ const REG_ICON = /^(ti|tic|far|fab|fad|fal|fas|fa)?\s(fa|ti|tic)-[a-z0-9-]+$/;
12
12
  const REG_WSPACE = /\u00A0/g;
13
13
 
14
14
  function Value() {}
package/markdown.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // Total.js Markdown
2
2
  // The MIT License
3
- // Copyright 2018-2023 (c) Peter Širka <petersirka@gmail.com>
3
+ // Copyright 2018-2024 (c) Peter Širka <petersirka@gmail.com>
4
4
 
5
5
  const REG_DASH = /-{2,}/g;
6
6
  const REG_TAGS = /<[^>]*>/g;
@@ -64,9 +64,14 @@ function markdown_links(value) {
64
64
  var link = value.substring(end + 2, value.length - 1);
65
65
 
66
66
  // footnotes
67
- if ((/^#\d+$/).test(link)) {
67
+ if ((/^#\d+$/).test(link))
68
68
  return (/^\d+$/).test(text) ? '<sup data-id="{0}" class="markdown-footnote">{1}</sup>'.format(link.substring(1), text) : '<span data-id="{0}" class="markdown-footnote">{1}</span>'.format(link.substring(1), text);
69
- }
69
+
70
+ if (link.isEmail())
71
+ return '<a href="mailto:' + link + '" rel="nofollow">' + text + '</a>';
72
+
73
+ if (link.isPhone())
74
+ return '<a href="tel:' + link + '" rel="nofollow">' + text + '</a>';
70
75
 
71
76
  if (link.substring(0, 4) === 'www.')
72
77
  link = 'https://' + link;
package/minificators.js CHANGED
@@ -301,7 +301,7 @@ exports.htmlremovecomments = function(value) {
301
301
  return value;
302
302
  };
303
303
 
304
- exports.merge = async function(filename, filenames, minify = true) {
304
+ exports.merge = async function(filename, filenames, minify) {
305
305
 
306
306
  // @filename {String/Boolean}
307
307
 
@@ -423,6 +423,7 @@ async function mergedownload(url, minify = true) {
423
423
  }
424
424
 
425
425
  async function mergefile(filename, minify) {
426
+
426
427
  return new Promise(function(resolve) {
427
428
 
428
429
  F.Fs.readFile(filename, 'utf8', function(err, response) {
@@ -445,6 +446,9 @@ async function mergefile(filename, minify) {
445
446
  ext = ext.toLowerCase();
446
447
  }
447
448
 
449
+ if (minify === undefined)
450
+ minify = !!F.config['$minify' + ext];
451
+
448
452
  var output = { ext: ext };
449
453
 
450
454
  response = response.ROOT();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "total5",
3
- "version": "0.0.2",
3
+ "version": "0.0.3-2",
4
4
  "description": "Total.js framework v5",
5
5
  "main": "index.js",
6
6
  "directories": {
package/routing.js CHANGED
@@ -188,7 +188,7 @@ function Route(url, action, size) {
188
188
  }
189
189
 
190
190
  // Parse flags
191
- url = url.replace(/(@|#)+[a-z0-9]+/gi, function(text) {
191
+ url = url.replace(/(@|#)+[a-z0-9_]+/gi, function(text) {
192
192
  let tmp = text.trim();
193
193
  if (tmp[0] === '#') {
194
194
  t.middleware.push(tmp.substring(1));
@@ -618,7 +618,7 @@ exports.lookupcors = function(ctrl) {
618
618
  let cors = F.temporary.cors;
619
619
 
620
620
  if (cors) {
621
- if (cors.strict.length && cors.strict.includes(origin)) {
621
+ if (cors.strict[origin]) {
622
622
  resume = true;
623
623
  } else if (cors.wildcard.length) {
624
624
  for (let m of cors.wildcard) {
package/tms.js CHANGED
@@ -101,7 +101,7 @@ function tmscontroller($) {
101
101
  }
102
102
  } else {
103
103
  msg.error = true;
104
- msg.data = new ErrorBuilder.push(404).output();
104
+ msg.data = new ErrorBuilder().push(404).output();
105
105
  client.send(msg);
106
106
  }
107
107
  }
@@ -377,4 +377,4 @@ F.on('$tms', function() {
377
377
  Cache.socket && Cache.socket.close(1000, 'Changed TMS secret');
378
378
  }
379
379
 
380
- });
380
+ });
package/utils.js CHANGED
@@ -5968,6 +5968,17 @@ SP.toJSONSchema = SP.parseSchema = function(name, url) {
5968
5968
  var arr = prop[i].split(':').trim();
5969
5969
  var tmp = null;
5970
5970
 
5971
+ if (arr[0][0] === '@') {
5972
+ let ext = F.jsonschemas[arr[0].substring(1)];
5973
+ if (ext) {
5974
+ for (let m of ext.required)
5975
+ required.push(m);
5976
+ for (let m in ext.properties)
5977
+ obj.properties[m] = ext.properties[m];
5978
+ }
5979
+ continue;
5980
+ }
5981
+
5971
5982
  if (arr[0][0] === '!' || arr[0][0] === '*') {
5972
5983
  // required
5973
5984
  arr[0] = arr[0].substring(1);
package/websocket.js CHANGED
@@ -110,7 +110,19 @@ Controller.prototype.upgrade = function(websocket) {
110
110
  ctrl.socket.on('end', websocket_close);
111
111
  ctrl.parent.add(ctrl);
112
112
  websocket.online++;
113
- F.$events.websocket_begin && F.emit('websocket_begin', ctrl.parent, ctrl);
113
+
114
+ // Deprecated:
115
+ // F.$events.websocket_begin && F.emit('websocket_begin', ctrl.parent, ctrl);
116
+
117
+ if (F.$events.websocket) {
118
+ /*
119
+ @Path: Framework
120
+ @Event: ON('websocket', function(ctrl) { ... }); #ctrl {Controller};
121
+ The event captures all incoming WebSocket connections. The next processing __can't be__ canceled via the `ctrl.cancel()` method.
122
+ */
123
+ F.emit('websocket', ctrl);
124
+ }
125
+
114
126
  ctrl.parent.$events.open && ctrl.parent.emit('open', ctrl);
115
127
  };
116
128