alchemymvc 1.2.8 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/lib/app/behaviour/sluggable_behaviour.js +4 -2
  2. package/lib/app/conduit/http_conduit.js +7 -2
  3. package/lib/app/conduit/loopback_conduit.js +2 -2
  4. package/lib/app/conduit/socket_conduit.js +20 -5
  5. package/lib/app/controller/alchemy_info_controller.js +4 -8
  6. package/lib/app/helper/backed_map.js +2 -2
  7. package/lib/app/helper/router_helper.js +98 -24
  8. package/lib/app/helper_controller/controller.js +45 -30
  9. package/lib/app/helper_datasource/00-nosql_datasource.js +44 -10
  10. package/lib/app/helper_field/enum_field.js +4 -4
  11. package/lib/app/helper_field/schema_field.js +50 -36
  12. package/lib/app/helper_model/document.js +81 -46
  13. package/lib/app/helper_model/field_set.js +11 -0
  14. package/lib/app/helper_model/model.js +107 -53
  15. package/lib/app/helper_validator/00_validator.js +38 -6
  16. package/lib/app/helper_validator/not_empty_validator.js +1 -3
  17. package/lib/app/routes.js +7 -1
  18. package/lib/bootstrap.js +1 -0
  19. package/lib/class/conduit.js +438 -290
  20. package/lib/class/controller.js +18 -15
  21. package/lib/class/datasource.js +19 -8
  22. package/lib/class/document.js +3 -3
  23. package/lib/class/field.js +34 -3
  24. package/lib/class/inode.js +27 -0
  25. package/lib/class/inode_file.js +204 -4
  26. package/lib/class/migration.js +2 -1
  27. package/lib/class/model.js +16 -5
  28. package/lib/class/path_definition.js +76 -120
  29. package/lib/class/path_param_definition.js +202 -0
  30. package/lib/class/postponement.js +573 -0
  31. package/lib/class/route.js +193 -33
  32. package/lib/class/router.js +22 -4
  33. package/lib/class/schema.js +47 -11
  34. package/lib/class/schema_client.js +65 -35
  35. package/lib/class/session.js +138 -12
  36. package/lib/class/sitemap.js +341 -0
  37. package/lib/core/base.js +13 -3
  38. package/lib/core/client_alchemy.js +78 -7
  39. package/lib/core/client_base.js +16 -10
  40. package/lib/core/middleware.js +56 -45
  41. package/lib/init/alchemy.js +124 -11
  42. package/lib/init/constants.js +11 -0
  43. package/lib/init/functions.js +163 -86
  44. package/lib/stages.js +18 -3
  45. package/package.json +6 -6
@@ -7,7 +7,7 @@ var publicDirs = alchemy.shared('public.directories', new Deck()),
7
7
  imageDirs = alchemy.shared('images.directories', new Deck()),
8
8
  rootDirs = alchemy.shared('root.directories', new Deck()),
9
9
  fontDirs = alchemy.shared('font.directories', new Deck()),
10
- assetMap = alchemy.shared('files.assetMap'),
10
+ asset_cache= alchemy.getCache('files.assets'),
11
11
  fileCache = alchemy.shared('files.fileCache'),
12
12
  minifyMap = alchemy.shared('files.minifyMap'),
13
13
  Nodent = alchemy.use('nodent-compiler'),
@@ -128,7 +128,7 @@ function getMiddlePaths(paths, ext, new_ext) {
128
128
  *
129
129
  * @author Jelle De Loecker <jelle@develry.be>
130
130
  * @since 0.2.0
131
- * @version 1.2.7
131
+ * @version 1.3.0
132
132
  */
133
133
  Alchemy.setMethod(function styleMiddleware(req, res, nextMiddleware) {
134
134
 
@@ -145,10 +145,10 @@ Alchemy.setMethod(function styleMiddleware(req, res, nextMiddleware) {
145
145
  path = url.path;
146
146
 
147
147
  // If this file has already been found & compiled, serve it to the user
148
- if (assetMap[path]) {
149
- return req.conduit.serveFile(assetMap[path].path, {onError: function onError() {
148
+ if (asset_cache.has(path)) {
149
+ return req.conduit.serveFile(asset_cache.get(path).path, {onError: function onError() {
150
150
  // Unset asset
151
- assetMap[path] = null;
151
+ asset_cache.remove(path);
152
152
  alchemy.styleMiddleware(req, res, nextMiddleware);
153
153
  }});
154
154
  }
@@ -246,17 +246,20 @@ Alchemy.setMethod(function styleMiddleware(req, res, nextMiddleware) {
246
246
  return nextMiddleware();
247
247
  }
248
248
 
249
- return req.conduit.serveFile(compiled.path, {onError: function onError(err) {
250
- if (fileCache[compiled.path]) {
251
- fileCache[compiled.path] = null;
249
+ return req.conduit.serveFile(compiled.path, {
250
+ mimetype: 'text/css',
251
+ onError: function onError(err) {
252
+ if (fileCache[compiled.path]) {
253
+ fileCache[compiled.path] = null;
252
254
 
253
- if (o_stats && fileCache[o_stats.path]) {
254
- fileCache[o_stats.path] = null;
255
- }
255
+ if (o_stats && fileCache[o_stats.path]) {
256
+ fileCache[o_stats.path] = null;
257
+ }
256
258
 
257
- alchemy.styleMiddleware(req, res, nextMiddleware);
259
+ alchemy.styleMiddleware(req, res, nextMiddleware);
260
+ }
258
261
  }
259
- }});
262
+ });
260
263
  });
261
264
  });
262
265
 
@@ -266,7 +269,7 @@ Alchemy.setMethod(function styleMiddleware(req, res, nextMiddleware) {
266
269
  *
267
270
  * @author Jelle De Loecker <jelle@elevenways.be>
268
271
  * @since 1.1.0
269
- * @version 1.1.0
272
+ * @version 1.3.0
270
273
  */
271
274
  Alchemy.setMethod(function sourcemapMiddleware(req, res, nextMiddleware) {
272
275
 
@@ -297,7 +300,9 @@ Alchemy.setMethod(function sourcemapMiddleware(req, res, nextMiddleware) {
297
300
  return nextMiddleware();
298
301
  }
299
302
 
300
- req.conduit.serveFile(stats.path);
303
+ req.conduit.serveFile(stats.path, {
304
+ mimetype: 'application/json',
305
+ });
301
306
  });
302
307
  });
303
308
 
@@ -503,7 +508,7 @@ Alchemy.setMethod(function minifyScript(path, options, callback) {
503
508
  *
504
509
  * @author Jelle De Loecker <jelle@develry.be>
505
510
  * @since 0.2.0
506
- * @version 1.0.4
511
+ * @version 1.3.0
507
512
  */
508
513
  Alchemy.setMethod(function scriptMiddleware(req, res, nextMiddleware) {
509
514
 
@@ -517,15 +522,15 @@ Alchemy.setMethod(function scriptMiddleware(req, res, nextMiddleware) {
517
522
  return next();
518
523
  }
519
524
 
520
- if (assetMap[req.url]) {
521
- source = assetMap[req.url];
525
+ if (asset_cache.has(req.url)) {
526
+ source = asset_cache.get(req.url);
522
527
  return next();
523
528
  }
524
529
 
525
530
  alchemy.findAssetPath(req.middlePath, scriptDirs.getSorted(), function gotAssetPath(err, stats) {
526
531
 
527
532
  // @todo: error stuff, 404
528
- assetMap[req.url] = stats;
533
+ asset_cache.set(req.url, stats);
529
534
  source = stats;
530
535
 
531
536
  next();
@@ -566,22 +571,25 @@ Alchemy.setMethod(function scriptMiddleware(req, res, nextMiddleware) {
566
571
  return nextMiddleware();
567
572
  }
568
573
 
569
- req.conduit.serveFile(source.path, {onError: function onError() {
570
- // Unset asset
571
- if (fileCache[req.url]) {
572
- fileCache[req.url] = null;
573
- }
574
+ req.conduit.serveFile(source.path, {
575
+ mimetype: 'text/javascript',
576
+ onError: function onError() {
577
+ // Unset asset
578
+ if (fileCache[req.url]) {
579
+ fileCache[req.url] = null;
580
+ }
574
581
 
575
- if (assetMap[req.url]) {
576
- assetMap[req.url] = null;
577
- }
582
+ if (asset_cache.has(req.url)) {
583
+ asset_cache.remove(req.url);
584
+ }
578
585
 
579
- if (source && minifyMap[source.path]) {
580
- minifyMap[source.path] = null;
581
- }
586
+ if (source && minifyMap[source.path]) {
587
+ minifyMap[source.path] = null;
588
+ }
582
589
 
583
- alchemy.scriptMiddleware(req, res, nextMiddleware);
584
- }});
590
+ alchemy.scriptMiddleware(req, res, nextMiddleware);
591
+ }
592
+ });
585
593
  });
586
594
  });
587
595
 
@@ -622,15 +630,15 @@ Alchemy.setMethod(function assetInDirsMiddleware(req, res, directories, nextMidd
622
630
 
623
631
  Function.series(function getPath(next) {
624
632
 
625
- if (assetMap[req.url]) {
626
- source = assetMap[req.url];
633
+ if (asset_cache.has(req.url)) {
634
+ source = asset_cache.get(req.url);
627
635
  return next();
628
636
  }
629
637
 
630
638
  alchemy.findAssetPath(req.middlePath, directories, function gotAssetPath(err, stats) {
631
639
 
632
640
  // @todo: error stuff, 404
633
- assetMap[req.url] = stats;
641
+ asset_cache.set(req.url, stats);
634
642
  source = stats;
635
643
 
636
644
  next();
@@ -654,7 +662,7 @@ Alchemy.setMethod(function assetInDirsMiddleware(req, res, directories, nextMidd
654
662
  *
655
663
  * @author Jelle De Loecker <jelle@develry.be>
656
664
  * @since 0.2.0
657
- * @version 1.1.0
665
+ * @version 1.3.0
658
666
  */
659
667
  Alchemy.setMethod(function rootMiddleware(req, res, nextMiddleware) {
660
668
 
@@ -663,8 +671,8 @@ Alchemy.setMethod(function rootMiddleware(req, res, nextMiddleware) {
663
671
 
664
672
  Function.series(function getPath(next) {
665
673
 
666
- if (assetMap[req.url]) {
667
- source = assetMap[req.url];
674
+ if (asset_cache.has(req.url)) {
675
+ source = asset_cache.get(req.url);
668
676
  cached = true;
669
677
  return next();
670
678
  }
@@ -672,7 +680,10 @@ Alchemy.setMethod(function rootMiddleware(req, res, nextMiddleware) {
672
680
  alchemy.findAssetPath(req.middlePath, rootDirs.getSorted(), function gotAssetPath(err, stats) {
673
681
 
674
682
  // @todo: error stuff, 404
675
- assetMap[req.url] = stats;
683
+ if (stats?.path) {
684
+ asset_cache.set(req.url, stats);
685
+ }
686
+
676
687
  source = stats;
677
688
 
678
689
  next();
@@ -689,7 +700,7 @@ Alchemy.setMethod(function rootMiddleware(req, res, nextMiddleware) {
689
700
  onError: function onError() {
690
701
 
691
702
  if (cached) {
692
- assetMap[req.url] = null;
703
+ asset_cache.remove(req.url);
693
704
  alchemy.rootMiddleware(req, res, nextMiddleware);
694
705
  return;
695
706
  }
@@ -704,7 +715,7 @@ Alchemy.setMethod(function rootMiddleware(req, res, nextMiddleware) {
704
715
  *
705
716
  * @author Jelle De Loecker <jelle@develry.be>
706
717
  * @since 0.2.0
707
- * @version 1.1.4
718
+ * @version 1.3.0
708
719
  *
709
720
  * @param {String} image_path
710
721
  * @param {Function} callback
@@ -723,8 +734,8 @@ Alchemy.setMethod(function findImagePath(image_path, callback) {
723
734
 
724
735
  let id = 'image-' + image_path;
725
736
 
726
- if (alchemy.settings.cache && assetMap[id]) {
727
- pledge.resolve(assetMap[id].path);
737
+ if (alchemy.settings.cache && asset_cache.has(id)) {
738
+ pledge.resolve(asset_cache.get(id).path);
728
739
  } else {
729
740
 
730
741
  alchemy.findAssetPath(image_path, imageDirs.getSorted(), function gotImagePath(err, stats) {
@@ -732,7 +743,7 @@ Alchemy.setMethod(function findImagePath(image_path, callback) {
732
743
  if (err) {
733
744
  pledge.reject(err);
734
745
  } else {
735
- assetMap[id] = stats;
746
+ asset_cache.set(id, stats);
736
747
  pledge.resolve(stats.path);
737
748
  }
738
749
  });
@@ -869,7 +880,7 @@ Alchemy.setMethod(function findAssetPath(assetFile, directories, callback) {
869
880
  }, function lastly() {
870
881
 
871
882
  // Return an empty object when nothing was found,
872
- // so we'll cache that in the assetMap
883
+ // so we'll cache that in the asset_cache
873
884
  // Otherwise it'll keep checking for every request
874
885
  if (found == null) {
875
886
  found = {};
@@ -9,7 +9,8 @@ var shared_objects = {},
9
9
  parseArgs = require('minimist'),
10
10
  libpath = require('path'),
11
11
  colors = require('ansi-256-colors'),
12
- fs = require('fs');
12
+ fs = require('fs'),
13
+ os = require('os');
13
14
 
14
15
  /**
15
16
  * The Alchemy class
@@ -60,6 +61,9 @@ global.Alchemy = Function.inherits('Informer', 'Alchemy', function Alchemy() {
60
61
  // Link to failed modules
61
62
  this.modules_error = useErrors;
62
63
 
64
+ // How many CPUs are available?
65
+ this.cpu_core_count = os.cpus().length;
66
+
63
67
  // Try getting the app package.json file
64
68
  try {
65
69
  package_json = require(libpath.resolve(PATH_ROOT, 'package.json'));
@@ -92,6 +96,9 @@ global.Alchemy = Function.inherits('Informer', 'Alchemy', function Alchemy() {
92
96
  // Also store the version of the app
93
97
  process.versions.alchemy_app = this.package.version;
94
98
 
99
+ // Distinct problems
100
+ this.distinct_problems = new Map();
101
+
95
102
  // Load the settings
96
103
  this.loadSettings();
97
104
 
@@ -211,12 +218,107 @@ Alchemy.setProperty(function environment() {
211
218
  return alchemy.settings.environment;
212
219
  });
213
220
 
221
+ /**
222
+ * Get the current lag in ms
223
+ *
224
+ * @author Jelle De Loecker <jelle@elevenways.be>
225
+ * @since 1.3.1
226
+ * @version 1.3.1
227
+ *
228
+ * @return {Number}
229
+ */
230
+ Alchemy.setMethod(function lagInMs() {
231
+ return this.toobusy.lag();
232
+ });
233
+
234
+ /**
235
+ * Get the system load as percentage points
236
+ *
237
+ * @author Jelle De Loecker <jelle@elevenways.be>
238
+ * @since 1.3.1
239
+ * @version 1.3.1
240
+ *
241
+ * @return {Number}
242
+ */
243
+ Alchemy.setMethod(function systemLoad() {
244
+
245
+ let load_average = os.loadavg();
246
+
247
+ const percentage = ~~((load_average[0] / this.cpu_core_count) * 100);
248
+
249
+ return percentage;
250
+ });
251
+
252
+ /**
253
+ * Is the server currently too busy, overloaded in some way?
254
+ *
255
+ * @author Jelle De Loecker <jelle@elevenways.be>
256
+ * @since 1.3.1
257
+ * @version 1.3.1
258
+ *
259
+ * @return {Boolean}
260
+ */
261
+ Alchemy.setMethod(function isTooBusy() {
262
+
263
+ // First check if it's too busy because of lag
264
+ if (this.toobusy()) {
265
+ return true;
266
+ }
267
+
268
+ // Then check the load average
269
+ return this.systemLoad() > 90;
270
+ });
271
+
272
+ /**
273
+ * Is the server too busy for requests?
274
+ *
275
+ * @author Jelle De Loecker <jelle@elevenways.be>
276
+ * @since 1.3.1
277
+ * @version 1.3.1
278
+ *
279
+ * @return {Boolean}
280
+ */
281
+ Alchemy.setMethod(function isTooBusyForRequests() {
282
+
283
+ // If a queue already exists, the server's lag might be quite good
284
+ // BECAUSE requests are being queued.
285
+ if (Classes.Alchemy.Conduit.Postponement.queue_length > 5) {
286
+ return true;
287
+ }
288
+
289
+ return this.isTooBusy();
290
+ });
291
+
292
+ /**
293
+ * Is the server too busy for AJAX too?
294
+ *
295
+ * @author Jelle De Loecker <jelle@elevenways.be>
296
+ * @since 1.3.1
297
+ * @version 1.3.1
298
+ *
299
+ * @return {Boolean}
300
+ */
301
+ Alchemy.setMethod(function isTooBusyForAjax() {
302
+
303
+ if (!this.isTooBusyForRequests()) {
304
+ return false;
305
+ }
306
+
307
+ let lag = this.lagInMs();
308
+
309
+ if (lag > (alchemy.settings.toobusy * 3)) {
310
+ return true;
311
+ }
312
+
313
+ return false;
314
+ });
315
+
214
316
  /**
215
317
  * Start janeway
216
318
  *
217
319
  * @author Jelle De Loecker <jelle@develry.be>
218
320
  * @since 0.5.0
219
- * @version 1.2.7
321
+ * @version 1.3.1
220
322
  *
221
323
  * @param {Object} options
222
324
  */
@@ -298,6 +400,17 @@ Alchemy.setMethod(function startJaneway(options) {
298
400
  this.Janeway.session_menu = session_menu;
299
401
  }
300
402
 
403
+ if (this.settings.lag_menu) {
404
+ let lag_menu = this.Janeway.addIndicator('0 ms');
405
+
406
+ setInterval(() => {
407
+ lag_menu.setIcon(this.lagInMs() + ' ms');
408
+ this.Janeway.redraw();
409
+ }, 900).unref();
410
+
411
+ this.Janeway.lag_menu = lag_menu;
412
+ }
413
+
301
414
  });
302
415
 
303
416
  /**
@@ -1113,7 +1226,7 @@ Alchemy.setMethod(function isStream(obj) {
1113
1226
  *
1114
1227
  * @author Jelle De Loecker <jelle@develry.be>
1115
1228
  * @since 1.0.0
1116
- * @version 1.0.4
1229
+ * @version 1.3.0
1117
1230
  *
1118
1231
  * @param {String} name
1119
1232
  * @param {Number|Object} options
@@ -1144,12 +1257,11 @@ Alchemy.setMethod(function getCache(name, options) {
1144
1257
  }
1145
1258
 
1146
1259
  config = Object.assign({
1260
+ name,
1147
1261
  max_length : 5000,
1148
1262
  }, options);
1149
1263
 
1150
- // @TODO: Fixed in 0.6.1
1151
- instance = new Blast.Classes.Develry.Cache();
1152
- Object.assign(instance, config);
1264
+ instance = new Blast.Classes.Develry.Cache(config);
1153
1265
 
1154
1266
  this.caches[name] = instance;
1155
1267
 
@@ -1161,16 +1273,17 @@ Alchemy.setMethod(function getCache(name, options) {
1161
1273
  *
1162
1274
  * @author Jelle De Loecker <jelle@elevenways.be>
1163
1275
  * @since 1.1.3
1164
- * @version 1.1.3
1276
+ * @version 1.3.0
1165
1277
  *
1166
1278
  * @param {String} href The href or route name
1167
1279
  * @param {Object} parameters Route parameters
1280
+ * @param {Object} options
1168
1281
  *
1169
1282
  * @return {String}
1170
1283
  */
1171
- Alchemy.setMethod(function routeUrl(href, parameters) {
1284
+ Alchemy.setMethod(function routeUrl(href, parameters, options) {
1172
1285
 
1173
- let temp = Router.getUrl(href, parameters);
1286
+ let temp = Router.getUrl(href, parameters, options);
1174
1287
 
1175
1288
  if (temp && temp.href) {
1176
1289
  temp = String(temp);
@@ -1382,7 +1495,7 @@ Alchemy.setMethod(function addAppcacheEntry(entry) {
1382
1495
  *
1383
1496
  * @author Jelle De Loecker <jelle@develry.be>
1384
1497
  * @since 1.1.0
1385
- * @version 1.2.7
1498
+ * @version 1.3.0
1386
1499
  *
1387
1500
  * @param {IncomingMessage} req
1388
1501
  * @param {OutgoingMessage} res Optional
@@ -1410,7 +1523,7 @@ Alchemy.setMethod(function parseRequestBody(req, res, callback) {
1410
1523
 
1411
1524
  let form = new formidable.IncomingForm({
1412
1525
  multiples : true,
1413
- hashAlgorithm : 'md5',
1526
+ hashAlgorithm : alchemy.settings.file_hash_algorithm || 'sha1',
1414
1527
  });
1415
1528
 
1416
1529
  form.parse(req, function parsedMultipart(err, form_fields, form_files) {
@@ -48,6 +48,17 @@ DEFINE('Blast', __Protoblast);
48
48
  */
49
49
  DEFINE('Classes', Blast.Classes);
50
50
 
51
+ /**
52
+ * Available types
53
+ *
54
+ * @author Jelle De Loecker <jelle@elevenways.be>
55
+ * @since 1.3.0
56
+ * @version 1.3.0
57
+ *
58
+ * @type {Object}
59
+ */
60
+ DEFINE('Types', Blast.Types);
61
+
51
62
  /**
52
63
  * Path to the directory of the server.js file
53
64
  *