alchemymvc 1.3.22 → 1.4.0-alpha.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.
Files changed (155) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +3 -3
  3. package/lib/app/behaviour/publishable_behaviour.js +5 -5
  4. package/lib/app/behaviour/revision_behaviour.js +96 -59
  5. package/lib/app/behaviour/sluggable_behaviour.js +14 -14
  6. package/lib/app/conduit/electron_conduit.js +9 -9
  7. package/lib/app/conduit/http_conduit.js +13 -13
  8. package/lib/app/conduit/loopback_conduit.js +15 -15
  9. package/lib/app/conduit/socket_conduit.js +43 -43
  10. package/lib/app/config/routes.js +26 -0
  11. package/lib/app/controller/00-default_app_controller.js +21 -0
  12. package/lib/app/controller/alchemy_info_controller.js +12 -12
  13. package/lib/app/datasource/mongo_datasource.js +27 -18
  14. package/lib/app/element/00-default_app_element.js +19 -0
  15. package/lib/app/element/time_ago.js +5 -5
  16. package/lib/app/helper/00-default_app_helper.js +11 -0
  17. package/lib/app/helper/alchemy_helper.js +22 -22
  18. package/lib/app/helper/backed_map.js +1 -1
  19. package/lib/app/helper/breadcrumb.js +10 -10
  20. package/lib/app/helper/client_collection.js +3 -3
  21. package/lib/app/helper/cron.js +29 -29
  22. package/lib/app/helper/enum_values.js +6 -6
  23. package/lib/app/helper/pagination_helper.js +36 -36
  24. package/lib/app/helper/router_helper.js +35 -35
  25. package/lib/app/helper/socket_helper.js +57 -57
  26. package/lib/app/helper/syncable.js +62 -62
  27. package/lib/app/helper_component/paginate_component.js +9 -9
  28. package/lib/app/helper_controller/component.js +1 -1
  29. package/lib/app/helper_controller/conduit.js +31 -31
  30. package/lib/app/helper_controller/controller.js +54 -39
  31. package/lib/app/helper_datasource/00-nosql_datasource.js +624 -70
  32. package/lib/app/helper_datasource/05-fallback_datasource.js +10 -10
  33. package/lib/app/helper_datasource/idb_datasource.js +6 -6
  34. package/lib/app/helper_datasource/indexed_db.js +22 -22
  35. package/lib/app/helper_datasource/remote_datasource.js +5 -5
  36. package/lib/app/helper_error/http_error.js +4 -4
  37. package/lib/app/helper_error/model_error.js +2 -2
  38. package/lib/app/helper_error/validation_error.js +12 -12
  39. package/lib/app/helper_field/00-objectid_field.js +7 -7
  40. package/lib/app/helper_field/05-string_field.js +16 -12
  41. package/lib/app/helper_field/06-text_field.js +2 -4
  42. package/lib/app/helper_field/10-number_field.js +9 -12
  43. package/lib/app/helper_field/11-date_field.js +15 -15
  44. package/lib/app/helper_field/15-local_temporal_field.js +10 -10
  45. package/lib/app/helper_field/20-decimal_field.js +8 -9
  46. package/lib/app/helper_field/belongsto_field.js +1 -1
  47. package/lib/app/helper_field/big_int_field.js +8 -8
  48. package/lib/app/helper_field/boolean_field.js +9 -11
  49. package/lib/app/helper_field/datetime_field.js +3 -3
  50. package/lib/app/helper_field/enum_field.js +13 -8
  51. package/lib/app/helper_field/fixed_decimal_field.js +6 -7
  52. package/lib/app/helper_field/geopoint_field.js +9 -10
  53. package/lib/app/helper_field/habtm_field.js +3 -3
  54. package/lib/app/helper_field/hasoneparent_field.js +1 -1
  55. package/lib/app/helper_field/html_field.js +2 -4
  56. package/lib/app/helper_field/integer_field.js +8 -11
  57. package/lib/app/helper_field/local_date_field.js +5 -5
  58. package/lib/app/helper_field/local_date_time_field.js +5 -5
  59. package/lib/app/helper_field/local_time_field.js +5 -5
  60. package/lib/app/helper_field/mixed_field.js +5 -5
  61. package/lib/app/helper_field/object_field.js +6 -6
  62. package/lib/app/helper_field/password_field.js +3 -3
  63. package/lib/app/helper_field/regexp_field.js +7 -9
  64. package/lib/app/helper_field/schema_field.js +91 -88
  65. package/lib/app/helper_field/settings_field.js +92 -0
  66. package/lib/app/helper_field/time_field.js +6 -6
  67. package/lib/app/helper_field/url_field.js +2 -4
  68. package/lib/app/helper_model/00-base_criteria.js +662 -0
  69. package/lib/app/helper_model/05-criteria_expressions.js +605 -0
  70. package/lib/app/helper_model/10-model_criteria.js +1182 -0
  71. package/lib/app/helper_model/data_provider.js +2 -2
  72. package/lib/app/helper_model/document.js +103 -92
  73. package/lib/app/helper_model/document_list.js +14 -14
  74. package/lib/app/helper_model/field_config.js +11 -11
  75. package/lib/app/helper_model/field_set.js +17 -17
  76. package/lib/app/helper_model/model.js +201 -123
  77. package/lib/app/helper_model/remote_data_provider.js +2 -2
  78. package/lib/app/helper_validator/00_validator.js +16 -16
  79. package/lib/app/helper_validator/not_empty_validator.js +9 -9
  80. package/lib/app/model/00-default_app_model.js +18 -0
  81. package/lib/app/model/05-system_model.js +27 -0
  82. package/lib/app/model/{alchemy_migration_model.js → system_migration_model.js} +4 -4
  83. package/lib/app/model/system_setting_model.js +154 -0
  84. package/lib/app/model/{alchemy_task_history_model.js → system_task_history_model.js} +7 -7
  85. package/lib/app/model/{alchemy_task_model.js → system_task_model.js} +11 -11
  86. package/lib/bootstrap.js +22 -312
  87. package/lib/class/accumulator.js +5 -5
  88. package/lib/class/behaviour.js +5 -5
  89. package/lib/class/component.js +3 -3
  90. package/lib/class/conduit.js +152 -150
  91. package/lib/class/controller.js +42 -42
  92. package/lib/class/datasource.js +74 -79
  93. package/lib/class/document.js +74 -95
  94. package/lib/class/document_list.js +5 -5
  95. package/lib/class/element.js +17 -17
  96. package/lib/class/error.js +3 -3
  97. package/lib/class/field.js +169 -91
  98. package/lib/class/field_value.js +6 -6
  99. package/lib/class/helper.js +3 -3
  100. package/lib/class/inode.js +17 -17
  101. package/lib/class/inode_dir.js +12 -12
  102. package/lib/class/inode_file.js +26 -26
  103. package/lib/class/inode_list.js +4 -4
  104. package/lib/class/migration.js +4 -4
  105. package/lib/class/model.js +146 -165
  106. package/lib/class/path_definition.js +22 -22
  107. package/lib/class/path_evaluator.js +5 -5
  108. package/lib/class/path_param_definition.js +7 -7
  109. package/lib/class/plugin.js +312 -0
  110. package/lib/class/postponement.js +29 -29
  111. package/lib/class/reciprocal.js +8 -8
  112. package/lib/class/route.js +33 -33
  113. package/lib/class/router.js +73 -73
  114. package/lib/class/schema.js +21 -21
  115. package/lib/class/schema_client.js +105 -82
  116. package/lib/class/session.js +63 -29
  117. package/lib/class/session_scene.js +4 -4
  118. package/lib/class/sitemap.js +16 -16
  119. package/lib/class/task.js +39 -39
  120. package/lib/class/task_service.js +43 -47
  121. package/lib/{init → core}/alchemy.js +411 -373
  122. package/lib/{init/functions.js → core/alchemy_functions.js} +113 -91
  123. package/lib/core/alchemy_load_functions.js +715 -0
  124. package/lib/core/base.js +50 -62
  125. package/lib/core/client_alchemy.js +144 -152
  126. package/lib/core/client_base.js +27 -50
  127. package/lib/core/discovery.js +16 -18
  128. package/lib/core/middleware.js +54 -43
  129. package/lib/core/{routing.js → prefix.js} +14 -16
  130. package/lib/core/setting.js +1684 -0
  131. package/lib/core/stage.js +758 -0
  132. package/lib/scripts/create_constants.js +119 -0
  133. package/lib/{init/languages.js → scripts/create_languages.js} +5 -5
  134. package/lib/scripts/create_settings.js +449 -0
  135. package/lib/scripts/create_shared_constants.js +95 -0
  136. package/lib/scripts/create_stages.js +55 -0
  137. package/lib/scripts/init_alchemy.js +51 -0
  138. package/lib/{init/requirements.js → scripts/preload_modules.js} +15 -2
  139. package/lib/scripts/setup_devwatch.js +238 -0
  140. package/lib/stages/00-load_core.js +342 -0
  141. package/lib/stages/05-load_app.js +57 -0
  142. package/lib/stages/10-datasource.js +61 -0
  143. package/lib/stages/15-tasks.js +27 -0
  144. package/lib/stages/20-settings.js +68 -0
  145. package/lib/stages/50-routes.js +218 -0
  146. package/lib/stages/90-server.js +347 -0
  147. package/package.json +5 -7
  148. package/lib/app/helper_model/criteria.js +0 -2294
  149. package/lib/app/helper_model/db_query.js +0 -1488
  150. package/lib/app/routes.js +0 -11
  151. package/lib/core/socket.js +0 -171
  152. package/lib/init/constants.js +0 -158
  153. package/lib/init/devwatch.js +0 -238
  154. package/lib/init/load_functions.js +0 -979
  155. package/lib/stages.js +0 -515
@@ -1,6 +1,4 @@
1
- 'use strict';
2
-
3
- var shared_objects = {},
1
+ let shared_objects = {},
4
2
  plugModules = null,
5
3
  usedModules = {},
6
4
  useErrors = {},
@@ -17,20 +15,20 @@ var shared_objects = {},
17
15
  *
18
16
  * @constructor
19
17
  *
20
- * @author Jelle De Loecker <jelle@elevenways.be>
18
+ * @author Jelle De Loecker <jelle@elevenways.be>
21
19
  * @since 0.0.1
22
20
  * @version 1.3.21
23
21
  */
24
- global.Alchemy = Function.inherits('Informer', 'Alchemy', function Alchemy() {
25
-
26
- var that = this,
27
- package_json;
22
+ global.Alchemy = Function.inherits('Alchemy.Base', function Alchemy() {
28
23
 
29
24
  // Only allow a single instance of the Alchemy class
30
25
  if (global.alchemy) {
31
26
  return global.alchemy;
32
27
  }
33
28
 
29
+ let that = this,
30
+ package_json;
31
+
34
32
  // Timestamp when alchemy started
35
33
  this.start_time = Date.now();
36
34
 
@@ -144,16 +142,22 @@ global.Alchemy = Function.inherits('Informer', 'Alchemy', function Alchemy() {
144
142
  console.warn('Failed to start Janeway:', err);
145
143
  }
146
144
  }
145
+
146
+ this.any_body = this.use('body/any');
147
+ this.text_body = this.use('body');
148
+ this.formidable = this.use('formidable');
149
+ this.body_parser = this.use('body-parser');
150
+ this.url_form_body = this.body_parser.urlencoded({extended: true});
147
151
  });
148
152
 
149
153
  /**
150
154
  * See if running janeway is allowed
151
155
  *
152
- * @author Jelle De Loecker <jelle@develry.be>
156
+ * @author Jelle De Loecker <jelle@elevenways.be>
153
157
  * @since 0.5.0
154
158
  * @version 0.5.0
155
159
  *
156
- * @type {Boolean}
160
+ * @type {boolean}
157
161
  */
158
162
  Alchemy.prepareProperty(function allow_janeway() {
159
163
 
@@ -163,7 +167,7 @@ Alchemy.prepareProperty(function allow_janeway() {
163
167
  }
164
168
 
165
169
  // You can also disable janeway in the settings
166
- if (this.settings.janeway === false) {
170
+ if (this.settings.debugging.enable_janeway === false) {
167
171
  return false;
168
172
  }
169
173
 
@@ -177,16 +181,16 @@ Alchemy.prepareProperty(function allow_janeway() {
177
181
  /**
178
182
  * Expirable object where sessions are stored
179
183
  *
180
- * @author Jelle De Loecker <jelle@develry.be>
184
+ * @author Jelle De Loecker <jelle@elevenways.be>
181
185
  * @since 0.5.0
182
186
  * @version 1.0.4
183
187
  *
184
- * @type {Develry.Cache}
188
+ * @type {Develry.Cache}
185
189
  */
186
190
  Alchemy.prepareProperty(function sessions() {
187
191
 
188
192
  var cache = this.getCache('sessions', {
189
- max_idle : alchemy.settings.session_length,
193
+ max_idle : this.settings.sessions.duration,
190
194
  max_length : Infinity
191
195
  });
192
196
 
@@ -202,11 +206,11 @@ Alchemy.prepareProperty(function sessions() {
202
206
  * Expirable object where sessions are temporarily stored
203
207
  * based on the browser fingerprints
204
208
  *
205
- * @author Jelle De Loecker <jelle@develry.be>
209
+ * @author Jelle De Loecker <jelle@elevenways.be>
206
210
  * @since 1.1.0
207
211
  * @version 1.1.0
208
212
  *
209
- * @type {Develry.Cache}
213
+ * @type {Develry.Cache}
210
214
  */
211
215
  Alchemy.prepareProperty(function fingerprints() {
212
216
 
@@ -221,27 +225,40 @@ Alchemy.prepareProperty(function fingerprints() {
221
225
  /**
222
226
  * Get or set the environment
223
227
  *
224
- * @author Jelle De Loecker <jelle@develry.be>
228
+ * @author Jelle De Loecker <jelle@elevenways.be>
225
229
  * @since 0.4.0
226
230
  * @version 0.4.0
227
231
  *
228
- * @type {String}
232
+ * @type {string}
229
233
  */
230
234
  Alchemy.setProperty(function environment() {
231
- return alchemy.settings.environment;
235
+ return this.settings.environment;
232
236
  }, function set_environment(value) {
233
- alchemy.settings.environment = String(value);
234
- return alchemy.settings.environment;
237
+ this.setSetting('environment', String(value));
238
+ return this.settings.environment;
239
+ });
240
+
241
+ /**
242
+ * Add a getter for the total amount of http requests
243
+ *
244
+ * @author Jelle De Loecker <jelle@elevenways.be>
245
+ * @since 1.3.1
246
+ * @version 1.3.1
247
+ *
248
+ * @type {number}
249
+ */
250
+ Alchemy.setProperty(function http_request_counter() {
251
+ return 0;
235
252
  });
236
253
 
237
254
  /**
238
255
  * Get the current lag in ms
239
256
  *
240
- * @author Jelle De Loecker <jelle@elevenways.be>
257
+ * @author Jelle De Loecker <jelle@elevenways.be>
241
258
  * @since 1.3.1
242
259
  * @version 1.3.1
243
260
  *
244
- * @return {Number}
261
+ * @return {number}
245
262
  */
246
263
  Alchemy.setMethod(function lagInMs() {
247
264
  return this.toobusy.lag();
@@ -250,11 +267,11 @@ Alchemy.setMethod(function lagInMs() {
250
267
  /**
251
268
  * Get the system load as percentage points
252
269
  *
253
- * @author Jelle De Loecker <jelle@elevenways.be>
270
+ * @author Jelle De Loecker <jelle@elevenways.be>
254
271
  * @since 1.3.1
255
272
  * @version 1.3.1
256
273
  *
257
- * @return {Number}
274
+ * @return {number}
258
275
  */
259
276
  Alchemy.setMethod(function systemLoad() {
260
277
 
@@ -269,7 +286,7 @@ Alchemy.setMethod(function systemLoad() {
269
286
  * Register an extra toobusy check method.
270
287
  * These will be checked after event loop lag & system load are deemed OK.
271
288
  *
272
- * @author Jelle De Loecker <jelle@elevenways.be>
289
+ * @author Jelle De Loecker <jelle@elevenways.be>
273
290
  * @since 1.3.2
274
291
  * @version 1.3.2
275
292
  */
@@ -280,11 +297,11 @@ Alchemy.setMethod(function addToobusyCheck(fnc) {
280
297
  /**
281
298
  * Is the server currently too busy, overloaded in some way?
282
299
  *
283
- * @author Jelle De Loecker <jelle@elevenways.be>
300
+ * @author Jelle De Loecker <jelle@elevenways.be>
284
301
  * @since 1.3.1
285
302
  * @version 1.3.2
286
303
  *
287
- * @return {Boolean}
304
+ * @return {boolean}
288
305
  */
289
306
  Alchemy.setMethod(function isTooBusy() {
290
307
 
@@ -293,9 +310,11 @@ Alchemy.setMethod(function isTooBusy() {
293
310
  return true;
294
311
  }
295
312
 
296
- if (this.settings.max_system_load > 0) {
313
+ const max_system_load = this.settings.performance.max_system_load;
314
+
315
+ if (max_system_load > 0) {
297
316
  // Then check the load average
298
- if (this.systemLoad() > this.settings.max_system_load) {
317
+ if (this.systemLoad() > max_system_load) {
299
318
  return true;
300
319
  }
301
320
  }
@@ -308,7 +327,7 @@ Alchemy.setMethod(function isTooBusy() {
308
327
  return true;
309
328
  }
310
329
  } catch (err) {
311
- alchemy.distinctProblem('error-extra-toobusy-check', 'Extra toobusy check failed', {error: err});
330
+ this.distinctProblem('error-extra-toobusy-check', 'Extra toobusy check failed', {error: err});
312
331
  }
313
332
  }
314
333
  }
@@ -319,11 +338,11 @@ Alchemy.setMethod(function isTooBusy() {
319
338
  /**
320
339
  * Is the server too busy for requests?
321
340
  *
322
- * @author Jelle De Loecker <jelle@elevenways.be>
341
+ * @author Jelle De Loecker <jelle@elevenways.be>
323
342
  * @since 1.3.1
324
343
  * @version 1.3.1
325
344
  *
326
- * @return {Boolean}
345
+ * @return {boolean}
327
346
  */
328
347
  Alchemy.setMethod(function isTooBusyForRequests() {
329
348
 
@@ -339,11 +358,11 @@ Alchemy.setMethod(function isTooBusyForRequests() {
339
358
  /**
340
359
  * Is the server too busy for AJAX too?
341
360
  *
342
- * @author Jelle De Loecker <jelle@elevenways.be>
361
+ * @author Jelle De Loecker <jelle@elevenways.be>
343
362
  * @since 1.3.1
344
363
  * @version 1.3.18
345
364
  *
346
- * @return {Boolean}
365
+ * @return {boolean}
347
366
  */
348
367
  Alchemy.setMethod(function isTooBusyForAjax() {
349
368
 
@@ -351,7 +370,7 @@ Alchemy.setMethod(function isTooBusyForAjax() {
351
370
  return false;
352
371
  }
353
372
 
354
- let max_event_loop_lag = alchemy.settings.max_event_loop_lag ?? alchemy.settings.toobusy;
373
+ let max_event_loop_lag = this.settings.performance.max_event_loop_lag ?? this.settings.performance.toobusy;
355
374
 
356
375
  if (max_event_loop_lag) {
357
376
  let lag = this.lagInMs();
@@ -367,11 +386,11 @@ Alchemy.setMethod(function isTooBusyForAjax() {
367
386
  /**
368
387
  * Set the maximum event loop lag before the server is considered overloaded
369
388
  *
370
- * @author Jelle De Loecker <jelle@elevenways.be>
389
+ * @author Jelle De Loecker <jelle@elevenways.be>
371
390
  * @since 1.3.18
372
391
  * @version 1.3.18
373
392
  *
374
- * @param {Number} max_lag
393
+ * @param {number} max_lag
375
394
  */
376
395
  Alchemy.setMethod(function setMaxEventLoopLag(max_lag) {
377
396
 
@@ -383,50 +402,30 @@ Alchemy.setMethod(function setMaxEventLoopLag(max_lag) {
383
402
  max_lag = 70;
384
403
  }
385
404
 
386
- this.settings.max_event_loop_lag = max_lag;
405
+ this.setSetting('performance.max_event_loop_lag', max_lag);
387
406
  });
388
407
 
389
408
  /**
390
409
  * Called after the server has started
391
410
  *
392
- * @author Jelle De Loecker <jelle@elevenways.be>
411
+ * @author Jelle De Loecker <jelle@elevenways.be>
393
412
  * @since 1.3.17
394
- * @version 1.3.17
413
+ * @version 1.4.0
395
414
  */
396
415
  Alchemy.setMethod(function afterStart() {
397
- this.startTaskService();
398
- });
399
-
400
- let task_service_has_started = false;
401
-
402
- /**
403
- * Start the task service
404
- *
405
- * @author Jelle De Loecker <jelle@elevenways.be>
406
- * @since 1.3.17
407
- * @version 1.3.17
408
- */
409
- Alchemy.setMethod(function startTaskService() {
410
-
411
- if (task_service_has_started) {
412
- return;
413
- }
414
-
415
- task_service_has_started = true;
416
-
417
- this.task_service = new Classes.Alchemy.Task.TaskService();
416
+
418
417
  });
419
418
 
420
419
  /**
421
420
  * Is the given PID running?
422
421
  *
423
- * @author Jelle De Loecker <jelle@elevenways.be>
422
+ * @author Jelle De Loecker <jelle@elevenways.be>
424
423
  * @since 1.3.17
425
424
  * @version 1.3.17
426
425
  *
427
- * @param {Number} pid
426
+ * @param {number} pid
428
427
  *
429
- * @return {Boolean}
428
+ * @return {boolean}
430
429
  */
431
430
  Alchemy.setMethod(function isProcessRunning(pid) {
432
431
 
@@ -444,9 +443,9 @@ Alchemy.setMethod(function isProcessRunning(pid) {
444
443
  /**
445
444
  * Start janeway
446
445
  *
447
- * @author Jelle De Loecker <jelle@develry.be>
446
+ * @author Jelle De Loecker <jelle@elevenways.be>
448
447
  * @since 0.5.0
449
- * @version 1.3.1
448
+ * @version 1.4.0
450
449
  *
451
450
  * @param {Object} options
452
451
  */
@@ -506,67 +505,27 @@ Alchemy.setMethod(function startJaneway(options) {
506
505
 
507
506
  this.Janeway.started = true;
508
507
  this.Janeway.start(options);
509
-
510
- if (this.settings.title) {
511
- let title = this.settings.title;
512
-
513
- if (this.settings.titleized) {
514
- title = 'Alchemy: ' + title;
515
- }
516
-
517
- this.Janeway.setTitle(title);
518
- }
519
-
520
- if (this.settings.session_menu) {
521
-
522
- let session_menu = this.Janeway.addIndicator('⌨ ');
523
-
524
- if (!session_menu.addItem) {
525
- return session_menu.remove();
526
- }
527
-
528
- session_menu.addItem({
529
- title : 'Current active browser sessions:',
530
- weight : Infinity,
531
- }, () => {
532
- console.log('All sessions:', alchemy.sessions);
533
- });
534
-
535
- this.Janeway.session_menu = session_menu;
536
- }
537
-
538
- if (this.settings.lag_menu) {
539
- let lag_menu = this.Janeway.addIndicator('0 ms');
540
-
541
- setInterval(() => {
542
- lag_menu.setIcon(this.lagInMs() + ' ms');
543
- this.Janeway.redraw();
544
- }, 900).unref();
545
-
546
- this.Janeway.lag_menu = lag_menu;
547
- }
548
-
549
508
  });
550
509
 
551
510
  /**
552
511
  * Log messages of level 5 (info)
553
512
  *
554
- * @author Jelle De Loecker <jelle@develry.be>
513
+ * @author Jelle De Loecker <jelle@elevenways.be>
555
514
  * @since 0.0.1
556
515
  * @version 0.4.0
557
516
  */
558
517
  Alchemy.setMethod(function log(...args) {
559
- return alchemy.printLog(5, args, {level: 3});
518
+ return this.printLog(5, args, {level: 3});
560
519
  });
561
520
 
562
521
  /**
563
522
  * Actually print a log message
564
523
  *
565
- * @author Jelle De Loecker <jelle@develry.be>
524
+ * @author Jelle De Loecker <jelle@elevenways.be>
566
525
  * @since 0.4.0
567
526
  * @version 1.1.0
568
527
  *
569
- * @param {Number} level
528
+ * @param {number} level
570
529
  * @param {Array} args
571
530
  * @param {Object} options
572
531
  */
@@ -575,7 +534,7 @@ Alchemy.setMethod(function printLog(level, args, options) {
575
534
  var type,
576
535
  line;
577
536
 
578
- if (this.settings.silent) {
537
+ if (this.settings.debugging.silent) {
579
538
  return;
580
539
  }
581
540
 
@@ -609,49 +568,112 @@ Alchemy.setMethod(function printLog(level, args, options) {
609
568
  });
610
569
 
611
570
  /**
612
- * Load the settings
571
+ * Set a setting value (without triggering actions)
572
+ *
573
+ * @author Jelle De Loecker <jelle@elevenways.be>
574
+ * @since 1.4.0
575
+ * @version 1.4.0
576
+ *
577
+ * @param {string} path The path of the setting (without system prefix)
578
+ * @param {Mixed} value
579
+ */
580
+ Alchemy.setMethod(function setSetting(path, value) {
581
+ this.system_settings.setPathSilently(path, value);
582
+
583
+ if (this.started) {
584
+ this.settings = this.system_settings.toObject();
585
+ }
586
+ });
587
+
588
+ /**
589
+ * Get a setting value
590
+ *
591
+ * @author Jelle De Loecker <jelle@elevenways.be>
592
+ * @since 1.4.0
593
+ * @version 1.4.0
594
+ *
595
+ * @param {string} path The path of the setting (without system prefix)
596
+ */
597
+ Alchemy.setMethod(function getSetting(path) {
598
+
599
+ let value = this.getSettingValueInstance(path);
600
+
601
+ if (value) {
602
+ return value.get();
603
+ }
604
+ });
605
+
606
+ /**
607
+ * Get a setting value instance
608
+ *
609
+ * @author Jelle De Loecker <jelle@elevenways.be>
610
+ * @since 1.4.0
611
+ * @version 1.4.0
612
+ *
613
+ * @param {string} path The path of the setting (without system prefix)
614
+ */
615
+ Alchemy.setMethod(function getSettingValueInstance(path) {
616
+ return this.system_settings.getPath(path);
617
+ });
618
+
619
+ /**
620
+ * Execute the action linked to a setting
621
+ *
622
+ * @author Jelle De Loecker <jelle@elevenways.be>
623
+ * @since 1.4.0
624
+ * @version 1.4.0
625
+ *
626
+ * @param {string} path The path of the setting (without system prefix)
627
+ */
628
+ Alchemy.setMethod(function executeSetting(path) {
629
+
630
+ let value = this.getSettingValueInstance(path);
631
+
632
+ if (value) {
633
+ return value.executeAction();
634
+ }
635
+ });
636
+
637
+ /**
638
+ * Load the initial hard-coded settings
613
639
  *
614
- * @author Jelle De Loecker <jelle@develry.be>
640
+ * @author Jelle De Loecker <jelle@elevenways.be>
615
641
  * @since 0.4.0
616
- * @version 1.3.18
642
+ * @version 1.4.0
617
643
  */
618
644
  Alchemy.setMethod(function loadSettings() {
619
645
 
620
- var default_path,
621
- port_error,
622
- local_path,
623
- env_config,
624
- env_path,
625
- settings,
626
- local,
627
- env;
628
-
629
646
  if (this.settings) {
630
647
  return;
631
648
  }
632
649
 
633
- // Create the settings object
634
- this.settings = settings = {};
650
+ // Create the settings scope
651
+ const system = Classes.Alchemy.Setting.SYSTEM.generateValue();
652
+
653
+ this.system_settings = system;
654
+ let settings = this.settings = system.toProxyObject();
635
655
 
636
656
  // Generate the path to the default settings file
637
- default_path = libpath.resolve(PATH_ROOT, 'app', 'config', 'default');
657
+ let default_path = libpath.resolve(PATH_ROOT, 'app', 'config', 'default');
638
658
 
639
659
  // Get default settings
640
660
  try {
641
- Object.assign(settings, require(default_path));
661
+ let value = require(default_path);
662
+ system.setDefaultValue(value);
642
663
  } catch (err) {
643
- settings.no_default_file = default_path;
664
+ this.setSetting('no_default_file', default_path);
644
665
  }
645
666
 
646
667
  // Generate the path to the local settings file
647
- local_path = libpath.resolve(PATH_ROOT, 'app', 'config', 'local');
668
+ let local_path = libpath.resolve(PATH_ROOT, 'app', 'config', 'local'),
669
+ local;
648
670
 
649
671
  // Get the local settings
650
672
  try {
651
673
  local = require(local_path);
652
674
  } catch(err) {
653
675
  local = {};
654
- settings.no_local_file = local_path;
676
+ this.setSetting('no_local_file', local_path);
655
677
  }
656
678
 
657
679
  // Default to the 'dev' environment
@@ -664,7 +686,7 @@ Alchemy.setMethod(function loadSettings() {
664
686
  }
665
687
  }
666
688
 
667
- env = this.argv.env || this.argv.environment;
689
+ let env = this.argv.env || this.argv.environment;
668
690
 
669
691
  if (env) {
670
692
  local.environment = env;
@@ -672,30 +694,30 @@ Alchemy.setMethod(function loadSettings() {
672
694
  }
673
695
 
674
696
  // Generate the path to the environment settings file
675
- env_path = libpath.resolve(PATH_APP, 'config', local.environment, 'config');
697
+ let env_path = libpath.resolve(PATH_APP, 'config', local.environment, 'config');
676
698
 
677
699
  // Get the config
678
700
  try {
679
- env_config = require(env_path);
701
+ let value = require(env_path);
702
+ system.setValueSilently(value);
680
703
  } catch(err) {
681
- env_config = {};
682
- settings.no_env_file = env_path;
704
+ this.setSetting('no_env_file', env_path);
683
705
  }
684
706
 
685
- // Merge all the settings in order: default - environment - local
686
- Object.merge(settings, env_config, local);
707
+ // And now overlay the final local settings
708
+ system.setValueSilently(local);
687
709
 
688
710
  if (!settings.name) {
689
- settings.name = this.package.name;
711
+ this.setSetting('name', this.package.name);
690
712
  }
691
713
 
692
- if (settings.title == null) {
714
+ if (this.getSetting('frontend.title') == null) {
693
715
  if (this.package.title) {
694
716
  // Allow users to set the title in their package file
695
- settings.title = this.package.title;
717
+ this.setSetting('frontend.title', this.package.title);
696
718
  } else if (settings.name) {
697
- settings.title = settings.name.replace(/-/g, ' ').titleize();
698
- settings.titleized = true;
719
+ let title = settings.name.replace(/-/g, ' ').titleize();
720
+ this.setSetting('frontend.title', title);
699
721
  }
700
722
  }
701
723
 
@@ -704,41 +726,44 @@ Alchemy.setMethod(function loadSettings() {
704
726
 
705
727
  if (port) {
706
728
  this.printLog(this.INFO, ['Using port setting from argument:', port]);
707
- settings.port = port;
729
+ this.setSetting('network.port', port);
708
730
  } else {
709
- this.argv.socket = this.argv.port;
710
- this.argv.port = null;
731
+ this.setSetting('network.socket', this.argv.port);
732
+ this.setSetting('network.port', null);
711
733
  }
712
734
  }
713
735
 
714
736
  if (this.argv.socket) {
715
- settings.port = false;
716
- settings.socket = this.argv.socket;
737
+ this.setSetting('network.port', false);
738
+ this.setSetting('network.socket', this.argv.socket);
717
739
 
718
740
  let stat;
719
741
 
720
742
  try {
721
- stat = fs.statSync(settings.socket);
743
+ stat = fs.statSync(this.argv.socket);
722
744
  } catch (err) {
723
745
  // Ignore if it doesn't exist yet
724
746
  }
725
747
 
726
748
  if (stat && stat.isDirectory()) {
727
- settings.socket = libpath.resolve(settings.socket, settings.name + '.alchemy.sock');
749
+ let socket = libpath.resolve(this.argv.socket, settings.name + '.alchemy.sock');
750
+ this.setSetting('network.socket', socket);
728
751
  }
729
752
 
730
- this.printLog(this.INFO, ['Using socket setting from argument:', settings.socket]);
753
+ this.printLog(this.INFO, ['Using socket setting from argument:', settings.network.socket]);
731
754
  }
732
755
 
733
- if (!settings.port && settings.port !== false) {
734
- settings.port = 3000;
756
+ if (!settings.network.port && settings.network.port !== false) {
757
+ this.setSetting('network.port', 3000);
735
758
  }
736
759
 
737
- if (settings.port > 49151) {
760
+ let port = this.getSetting('network.port');
761
+
762
+ if (port > 49151) {
738
763
  port_error = 'Could not use port number ' + String(port).bold.red + ' because ';
739
764
 
740
765
  // Make sure the port is valid
741
- if (settings.port > 65535) {
766
+ if (port > 65535) {
742
767
  this.printLog(this.FATAL, [port_error + 'there is no port higher than 65535. Please use ports below 49151.']);
743
768
  } else {
744
769
  this.printLog(this.FATAL, [port_error + 'it\'s an ephemeral port. Please use ports below 49151.']);
@@ -748,27 +773,23 @@ Alchemy.setMethod(function loadSettings() {
748
773
  }
749
774
 
750
775
  if (this.argv.url) {
751
- settings.url = this.argv.url;
776
+ this.setUrl(this.argv.url);
777
+ } else {
778
+ this.setUrl(settings.network.main_url);
752
779
  }
753
780
 
754
- this.setUrl(settings.url);
755
-
756
- if (settings.cookie_domain) {
757
- this.setCookieDomain(settings.cookie_domain);
781
+ if (settings.frontend.cookies.domain) {
782
+ this.setCookieDomain(settings.frontend.cookies.domain);
758
783
  }
759
784
 
760
785
  if (this.argv.preload) {
761
786
  settings.preload = this.argv.preload;
762
787
 
763
- if (Array.isArray(settings.preload)) {
788
+ if (Array.isArray(settings.performance.preload)) {
764
789
  settings.preload = settings.preload.last();
765
790
  }
766
791
  }
767
792
 
768
- if (settings.preload == 'false') {
769
- settings.preload = false;
770
- }
771
-
772
793
  let key,
773
794
  val;
774
795
 
@@ -781,46 +802,39 @@ Alchemy.setMethod(function loadSettings() {
781
802
  val = this.argv[key];
782
803
  key = key.after('override-');
783
804
 
784
- if (!settings[key] || typeof settings[key] != 'object') {
785
- settings[key] = val;
786
- } else {
787
- Object.merge(settings[key], val);
788
- }
789
- }
805
+ this.setSetting(key, val);
790
806
 
791
- if (settings.debug) {
807
+ // if (!settings[key] || typeof settings[key] != 'object') {
808
+ // settings[key] = val;
809
+ // } else {
810
+ // Object.merge(settings[key], val);
811
+ // }
812
+ }
792
813
 
793
- if (settings.source_map === 'false') {
794
- settings.source_map = false;
814
+ if (settings.debugging.debug) {
815
+ if (settings.debugging.create_source_map == null) {
816
+ this.setSetting('debugging.create_source_map', true);
795
817
  }
796
-
797
- if (settings.source_map == null || settings.source_map) {
798
- settings.source_map = true;
799
- } else {
800
- settings.source_map = false;
801
- }
802
- } else {
803
- settings.source_map = false;
804
818
  }
805
819
 
806
- if (settings.preload) {
820
+ if (settings.performance.preload) {
807
821
  this.doPreload();
808
822
  }
809
823
 
810
- this.setMaxEventLoopLag(settings.max_event_loop_lag);
824
+ this.setMaxEventLoopLag(settings.performance.max_event_loop_lag);
811
825
 
812
826
  // Set the debug value
813
- global.DEBUG = settings.debug;
827
+ global.DEBUG = settings.debugging.debug;
814
828
  });
815
829
 
816
830
  /**
817
831
  * Set the main url
818
832
  *
819
- * @author Jelle De Loecker <jelle@elevenways.be>
833
+ * @author Jelle De Loecker <jelle@elevenways.be>
820
834
  * @since 1.3.18
821
835
  * @version 1.3.18
822
836
  *
823
- * @param {RURL|String} value
837
+ * @param {RURL|string} value
824
838
  */
825
839
  Alchemy.setMethod(function setUrl(value) {
826
840
 
@@ -832,9 +846,9 @@ Alchemy.setMethod(function setUrl(value) {
832
846
  }
833
847
  }
834
848
 
835
- this.settings.url = value;
849
+ this.setSetting('network.main_url', value);
836
850
 
837
- if (this.settings.cookie_domain === true || this.settings.cookie_domain == null) {
851
+ if (this.settings.frontend.cookies.domain === true || this.settings.frontend.cookies.domain == null) {
838
852
  this.setCookieDomain(value);
839
853
  }
840
854
  });
@@ -842,21 +856,21 @@ Alchemy.setMethod(function setUrl(value) {
842
856
  /**
843
857
  * Set the cookie domain
844
858
  *
845
- * @author Jelle De Loecker <jelle@elevenways.be>
859
+ * @author Jelle De Loecker <jelle@elevenways.be>
846
860
  * @since 1.3.18
847
861
  * @version 1.3.18
848
862
  *
849
- * @param {RURL|String} value
863
+ * @param {RURL|string} value
850
864
  */
851
865
  Alchemy.setMethod(function setCookieDomain(value) {
852
866
 
853
867
  if (value === false) {
854
- this.settings.cookie_domain = false;
868
+ this.setSetting('frontend.cookies.domain', false);
855
869
  return;
856
870
  }
857
871
 
858
872
  if (!value) {
859
- value = this.settings.url;
873
+ value = this.settings.network.main_url;
860
874
  }
861
875
 
862
876
  if (!value) {
@@ -874,13 +888,13 @@ Alchemy.setMethod(function setCookieDomain(value) {
874
888
  value = url.hostname;
875
889
  }
876
890
 
877
- this.settings.cookie_domain = value;
891
+ this.setSetting('frontend.cookies.domain', value);
878
892
  });
879
893
 
880
894
  /**
881
895
  * Set status
882
896
  *
883
- * @author Jelle De Loecker <jelle@develry.be>
897
+ * @author Jelle De Loecker <jelle@elevenways.be>
884
898
  * @since 0.4.1
885
899
  * @version 0.4.1
886
900
  */
@@ -891,9 +905,9 @@ Alchemy.setMethod(function setStatus(name, value) {
891
905
  /**
892
906
  * Execute the function when alchemy is ready
893
907
  *
894
- * @author Jelle De Loecker <jelle@develry.be>
908
+ * @author Jelle De Loecker <jelle@elevenways.be>
895
909
  * @since 0.0.1
896
- * @version 1.1.2
910
+ * @version 1.4.0
897
911
  *
898
912
  * @param {Function} callback The function to execute
899
913
  *
@@ -901,20 +915,17 @@ Alchemy.setMethod(function setStatus(name, value) {
901
915
  */
902
916
  Alchemy.setMethod(function ready(callback) {
903
917
 
904
- var that = this,
905
- pledge = new Pledge();
918
+ let pledge = new Pledge();
906
919
 
907
920
  pledge.done(callback);
908
921
 
909
- if (!this.sputnik) {
910
- Blast.loaded(function hasLoaded() {
911
- pledge.resolve(that.ready());
912
- });
913
- } else {
914
- this.sputnik.after(['start_server', 'datasources', 'listening'], function afterReady() {
915
- pledge.resolve();
916
- });
917
- }
922
+ STAGES.afterStages([
923
+ 'server.start',
924
+ 'datasource',
925
+ 'server.listening',
926
+ ], function hasLoaded() {
927
+ pledge.resolve();
928
+ });
918
929
 
919
930
  return pledge;
920
931
  });
@@ -922,7 +933,7 @@ Alchemy.setMethod(function ready(callback) {
922
933
  /**
923
934
  * Preload the client-side stuff
924
935
  *
925
- * @author Jelle De Loecker <jelle@elevenways.be>
936
+ * @author Jelle De Loecker <jelle@elevenways.be>
926
937
  * @since 1.1.2
927
938
  * @version 1.3.22
928
939
  */
@@ -932,10 +943,10 @@ Alchemy.setMethod(async function doPreload() {
932
943
 
933
944
  let url;
934
945
 
935
- if (this.settings.url) {
936
- url = this.settings.url;
937
- } else if (this.settings.port) {
938
- url = 'http://localhost:' + this.settings.port;
946
+ if (this.settings.network.main_url) {
947
+ url = this.settings.network.main_url;
948
+ } else if (this.settings.network.port) {
949
+ url = 'http://localhost:' + this.settings.network.port;
939
950
  }
940
951
 
941
952
  if (url) {
@@ -951,9 +962,9 @@ Alchemy.setMethod(async function doPreload() {
951
962
 
952
963
  Blast.getClientPath({
953
964
  modify_prototypes : true,
954
- create_source_map : alchemy.settings.source_map,
965
+ create_source_map : this.settings.debugging.create_source_map,
955
966
  enable_coverage : !!global.__coverage__,
956
- debug : alchemy.settings.debug,
967
+ debug : this.settings.debugging.debug,
957
968
  });
958
969
  }
959
970
  });
@@ -962,11 +973,11 @@ Alchemy.setMethod(async function doPreload() {
962
973
  * Resolve the provided arguments to a useable path string.
963
974
  * Only used strings, discards objects.
964
975
  *
965
- * @author Jelle De Loecker <jelle@develry.be>
976
+ * @author Jelle De Loecker <jelle@elevenways.be>
966
977
  * @since 0.0.1
967
978
  * @version 0.4.0
968
979
  *
969
- * @param {String} path_to_dirs The path containing the dirs to load
980
+ * @param {string} path_to_dirs The path containing the dirs to load
970
981
  */
971
982
  Alchemy.setMethod(function pathResolve(...path_to_dirs) {
972
983
 
@@ -995,16 +1006,16 @@ Alchemy.setMethod(function pathResolve(...path_to_dirs) {
995
1006
  /**
996
1007
  * A wrapper function for requiring modules
997
1008
  *
998
- * @author Jelle De Loecker <jelle@develry.be>
1009
+ * @author Jelle De Loecker <jelle@elevenways.be>
999
1010
  * @since 0.0.1
1000
1011
  * @version 1.0.0
1001
1012
  *
1002
- * @param {String} module_name The name/path of the module to load
1003
- * @param {String} register_as Cache the module under this name
1004
- * @param {Object} options Extra options
1005
- * @param {Boolean} options.force Force a new requirement and do not cache
1013
+ * @param {string} module_name The name/path of the module to load
1014
+ * @param {string} register_as Cache the module under this name
1015
+ * @param {Object} options Extra options
1016
+ * @param {boolean} options.force Force a new requirement and do not cache
1006
1017
  *
1007
- * @return {Object} The required module
1018
+ * @return {Object} The required module
1008
1019
  */
1009
1020
  Alchemy.setMethod(function use(module_name, register_as, options) {
1010
1021
 
@@ -1088,15 +1099,15 @@ Alchemy.setMethod(function use(module_name, register_as, options) {
1088
1099
  /**
1089
1100
  * Look for a module by traversing the filesystem
1090
1101
  *
1091
- * @author Jelle De Loecker <jelle@develry.be>
1102
+ * @author Jelle De Loecker <jelle@elevenways.be>
1092
1103
  * @since 0.0.1
1093
- * @version 0.4.0
1104
+ * @version 1.4.0
1094
1105
  *
1095
- * @param {String} startPath The path to originate the search from
1096
- * @param {String} moduleName
1097
- * @param {Number} recurse
1106
+ * @param {string} startPath The path to originate the search from
1107
+ * @param {string} moduleName
1108
+ * @param {number} recurse
1098
1109
  *
1099
- * @return {String}
1110
+ * @return {string}
1100
1111
  */
1101
1112
  Alchemy.setMethod(function searchModule(startPath, moduleName, recurse) {
1102
1113
 
@@ -1109,54 +1120,41 @@ Alchemy.setMethod(function searchModule(startPath, moduleName, recurse) {
1109
1120
  key,
1110
1121
  i;
1111
1122
 
1112
- // Don't do this search if it hasn't been enabled
1113
- // The new npm flat structure makes this an expensive thing to do
1114
- if (!this.settings.search_for_modules) {
1123
+ // Set recurse to 3, so this is the first and last call
1124
+ recurse = 3;
1115
1125
 
1116
- // Set recurse to 3, so this is the first and last call
1117
- recurse = 3;
1126
+ // Only add 2 folder to look through,
1127
+ // the alchemymvc node_modules folder
1128
+ // and the base node_modules folder
1129
+ moduledirs = ['..', libpath.resolve(startPath, 'node_modules', 'alchemymvc')];
1118
1130
 
1119
- // Only add 2 folder to look through,
1120
- // the alchemymvc node_modules folder
1121
- // and the base node_modules folder
1122
- moduledirs = ['..', libpath.resolve(startPath, 'node_modules', 'alchemymvc')];
1131
+ // Add plugin folders
1132
+ if (!plugModules) {
1133
+ path = libpath.resolve(PATH_ROOT, 'node_modules');
1123
1134
 
1124
- // Add plugin folders
1125
- if (!plugModules) {
1126
- path = libpath.resolve(PATH_ROOT, 'node_modules');
1135
+ if (fs.existsSync(path)) {
1127
1136
 
1128
- if (fs.existsSync(path)) {
1137
+ // Get all the entries in the main modules folder
1138
+ entries = fs.readdirSync(libpath.resolve(PATH_ROOT, 'node_modules'));
1129
1139
 
1130
- // Get all the entries in the main modules folder
1131
- entries = fs.readdirSync(libpath.resolve(PATH_ROOT, 'node_modules'));
1140
+ // Initiate the plugin modules variables
1141
+ plugModules = [];
1132
1142
 
1133
- // Initiate the plugin modules variables
1134
- plugModules = [];
1135
1143
 
1144
+ for (i = 0; i < entries.length; i++) {
1145
+ temp = entries[i];
1136
1146
 
1137
- for (i = 0; i < entries.length; i++) {
1138
- temp = entries[i];
1139
-
1140
- if (temp.startsWith('alchemy-')) {
1141
- plugModules.push(libpath.resolve(PATH_ROOT, 'node_modules', temp));
1142
- }
1147
+ if (temp.startsWith('alchemy-')) {
1148
+ plugModules.push(libpath.resolve(PATH_ROOT, 'node_modules', temp));
1143
1149
  }
1144
- } else {
1145
- plugModules = [];
1146
1150
  }
1151
+ } else {
1152
+ plugModules = [];
1147
1153
  }
1148
-
1149
- for (i = 0; i < plugModules.length; i++) {
1150
- moduledirs.push(plugModules[i]);
1151
- }
1152
-
1153
- } else if (!searchModule.have_warned) {
1154
- searchModule.have_warned = true;
1155
- log.warn('The "search_for_modules" config has been enabled!');
1156
1154
  }
1157
1155
 
1158
- if (!recurse) {
1159
- recurse = 1;
1156
+ for (i = 0; i < plugModules.length; i++) {
1157
+ moduledirs.push(plugModules[i]);
1160
1158
  }
1161
1159
 
1162
1160
  nmPath = libpath.resolve(startPath, 'node_modules');
@@ -1209,11 +1207,11 @@ Alchemy.setMethod(function searchModule(startPath, moduleName, recurse) {
1209
1207
  /**
1210
1208
  * Find a module in our customized file structure
1211
1209
  *
1212
- * @author Jelle De Loecker <jelle@develry.be>
1210
+ * @author Jelle De Loecker <jelle@elevenways.be>
1213
1211
  * @since 0.0.1
1214
1212
  * @version 1.3.17
1215
1213
  *
1216
- * @param {String} moduleName
1214
+ * @param {string} moduleName
1217
1215
  * @param {Object} options
1218
1216
  *
1219
1217
  * @return {Object}
@@ -1363,7 +1361,7 @@ Alchemy.setMethod(function findModule(moduleName, options) {
1363
1361
  }
1364
1362
 
1365
1363
  if (!supports_cjs) {
1366
- module = doImport(module_path)
1364
+ module = import(module_path)
1367
1365
  } else {
1368
1366
  module = require(module_path);
1369
1367
  }
@@ -1399,14 +1397,14 @@ Alchemy.setMethod(function findModule(moduleName, options) {
1399
1397
  /**
1400
1398
  * Create a shared object
1401
1399
  *
1402
- * @author Jelle De Loecker <jelle@develry.be>
1400
+ * @author Jelle De Loecker <jelle@elevenways.be>
1403
1401
  * @since 0.0.1
1404
1402
  * @version 0.4.0
1405
1403
  *
1406
- * @param {String} name The name of the object to get
1407
- * @param {String} type The type to create (array or object)
1404
+ * @param {string} name The name of the object to get
1405
+ * @param {string} type The type to create (array or object)
1408
1406
  *
1409
- * @return {Object|Array}
1407
+ * @return {Object|Array}
1410
1408
  */
1411
1409
  Alchemy.setMethod(function shared(name, type, value) {
1412
1410
 
@@ -1431,11 +1429,11 @@ Alchemy.setMethod(function shared(name, type, value) {
1431
1429
  * Get an object id,
1432
1430
  * return undefined if no valid data was given (instead of throwing an error)
1433
1431
  *
1434
- * @author Jelle De Loecker <jelle@develry.be>
1432
+ * @author Jelle De Loecker <jelle@elevenways.be>
1435
1433
  * @since 0.0.1
1436
1434
  * @version 1.3.16
1437
1435
  *
1438
- * @param {String|ObjectId} obj
1436
+ * @param {string|ObjectId} obj
1439
1437
  *
1440
1438
  * @return {ObjectId|undefined}
1441
1439
  */
@@ -1444,7 +1442,7 @@ Alchemy.setMethod(function castObjectId(obj) {
1444
1442
  let type = typeof obj;
1445
1443
 
1446
1444
  if (type === 'string' && obj.isObjectId()) {
1447
- return alchemy.ObjectId(obj);
1445
+ return this.ObjectId(obj);
1448
1446
  }
1449
1447
 
1450
1448
  if (obj && type === 'object') {
@@ -1461,11 +1459,11 @@ Alchemy.setMethod(function castObjectId(obj) {
1461
1459
  /**
1462
1460
  * See if the given object is a stream
1463
1461
  *
1464
- * @author Jelle De Loecker <jelle@develry.be>
1462
+ * @author Jelle De Loecker <jelle@elevenways.be>
1465
1463
  * @since 0.2.0
1466
1464
  * @version 1.0.5
1467
1465
  *
1468
- * @return {Boolean}
1466
+ * @return {boolean}
1469
1467
  */
1470
1468
  Alchemy.setMethod(function isStream(obj) {
1471
1469
  return obj && (typeof obj._read == 'function' || typeof obj._write == 'function') && typeof obj.on === 'function';
@@ -1474,12 +1472,12 @@ Alchemy.setMethod(function isStream(obj) {
1474
1472
  /**
1475
1473
  * Get or create a new cache instance
1476
1474
  *
1477
- * @author Jelle De Loecker <jelle@develry.be>
1475
+ * @author Jelle De Loecker <jelle@elevenways.be>
1478
1476
  * @since 1.0.0
1479
1477
  * @version 1.3.0
1480
1478
  *
1481
- * @param {String} name
1482
- * @param {Number|Object} options
1479
+ * @param {string} name
1480
+ * @param {number|Object} options
1483
1481
  *
1484
1482
  * @return {Develry.Cache}
1485
1483
  */
@@ -1521,15 +1519,15 @@ Alchemy.setMethod(function getCache(name, options) {
1521
1519
  /**
1522
1520
  * Get a route
1523
1521
  *
1524
- * @author Jelle De Loecker <jelle@elevenways.be>
1525
- * @since 1.1.3
1526
- * @version 1.3.0
1522
+ * @author Jelle De Loecker <jelle@elevenways.be>
1523
+ * @since 1.1.3
1524
+ * @version 1.3.0
1527
1525
  *
1528
- * @param {String} href The href or route name
1529
- * @param {Object} parameters Route parameters
1530
- * @param {Object} options
1526
+ * @param {string} href The href or route name
1527
+ * @param {Object} parameters Route parameters
1528
+ * @param {Object} options
1531
1529
  *
1532
- * @return {String}
1530
+ * @return {string}
1533
1531
  */
1534
1532
  Alchemy.setMethod(function routeUrl(href, parameters, options) {
1535
1533
 
@@ -1549,7 +1547,7 @@ Alchemy.setMethod(function routeUrl(href, parameters, options) {
1549
1547
  /**
1550
1548
  * Get paths that should be cached by the client
1551
1549
  *
1552
- * @author Jelle De Loecker <jelle@develry.be>
1550
+ * @author Jelle De Loecker <jelle@elevenways.be>
1553
1551
  * @since 1.0.7
1554
1552
  * @version 1.0.7
1555
1553
  *
@@ -1661,7 +1659,7 @@ Alchemy.decorateMethod(Blast.Decorators.memoize(), function getAppcachePaths() {
1661
1659
  /**
1662
1660
  * Get the appcache manifest text
1663
1661
  *
1664
- * @author Jelle De Loecker <jelle@develry.be>
1662
+ * @author Jelle De Loecker <jelle@elevenways.be>
1665
1663
  * @since 1.0.7
1666
1664
  * @version 1.0.7
1667
1665
  *
@@ -1713,11 +1711,11 @@ Alchemy.decorateMethod(Blast.Decorators.memoize(), function getAppcacheManifest(
1713
1711
  /**
1714
1712
  * Add an appcache entry
1715
1713
  *
1716
- * @author Jelle De Loecker <jelle@develry.be>
1714
+ * @author Jelle De Loecker <jelle@elevenways.be>
1717
1715
  * @since 1.0.7
1718
1716
  * @version 1.0.7
1719
1717
  *
1720
- * @param {String|Object}
1718
+ * @param {string|Object}
1721
1719
  */
1722
1720
  Alchemy.setMethod(function addAppcacheEntry(entry) {
1723
1721
 
@@ -1743,7 +1741,7 @@ Alchemy.setMethod(function addAppcacheEntry(entry) {
1743
1741
  /**
1744
1742
  * Get the body of an IncomingMessage
1745
1743
  *
1746
- * @author Jelle De Loecker <jelle@develry.be>
1744
+ * @author Jelle De Loecker <jelle@elevenways.be>
1747
1745
  * @since 1.1.0
1748
1746
  * @version 1.3.18
1749
1747
  *
@@ -1768,14 +1766,16 @@ Alchemy.setMethod(function parseRequestBody(req, res, callback) {
1768
1766
  return callback(null, req.body);
1769
1767
  }
1770
1768
 
1769
+ const that = this;
1770
+
1771
1771
  let content_type = req.headers['content-type'];
1772
1772
 
1773
1773
  // Multipart data is handled by "formidable"
1774
1774
  if (content_type && content_type.startsWith('multipart/form-data')) {
1775
1775
 
1776
- let form = new formidable.IncomingForm({
1776
+ let form = new this.formidable.IncomingForm({
1777
1777
  multiples : true,
1778
- hashAlgorithm : alchemy.settings.file_hash_algorithm || 'sha1',
1778
+ hashAlgorithm : this.settings.data_management.file_hash_algorithm || 'sha1',
1779
1779
  });
1780
1780
 
1781
1781
  form.parse(req, function parsedMultipart(err, form_fields, form_files) {
@@ -1827,7 +1827,7 @@ Alchemy.setMethod(function parseRequestBody(req, res, callback) {
1827
1827
  // Regular form-encoded data
1828
1828
  if (content_type && content_type.indexOf('form-urlencoded') > -1) {
1829
1829
 
1830
- urlFormBody(req, res, function parsedBody(err) {
1830
+ this.url_form_body(req, res, function parsedBody(err) {
1831
1831
 
1832
1832
  if (err && req.conduit && req.conduit.aborted) {
1833
1833
  return callback(null);
@@ -1854,7 +1854,7 @@ Alchemy.setMethod(function parseRequestBody(req, res, callback) {
1854
1854
  }
1855
1855
 
1856
1856
  // Any other encoded data (like JSON)
1857
- anyBody(req, function parsedBody(err, body) {
1857
+ this.any_body(req, function parsedBody(err, body) {
1858
1858
 
1859
1859
  function handleResponse(err, body) {
1860
1860
  if (err && req.conduit && req.conduit.aborted) {
@@ -1880,7 +1880,7 @@ Alchemy.setMethod(function parseRequestBody(req, res, callback) {
1880
1880
 
1881
1881
  if (err?.type == 'invalid.content.type') {
1882
1882
  if (!content_type || content_type.startsWith('text/')) {
1883
- textBody(req, handleResponse);
1883
+ that.text_body(req, handleResponse);
1884
1884
  return;
1885
1885
  }
1886
1886
  }
@@ -1892,7 +1892,7 @@ Alchemy.setMethod(function parseRequestBody(req, res, callback) {
1892
1892
  /**
1893
1893
  * Undo formidable's new array handling
1894
1894
  *
1895
- * @author Jelle De Loecker <jelle@elevenways.be>
1895
+ * @author Jelle De Loecker <jelle@elevenways.be>
1896
1896
  * @since 1.3.16
1897
1897
  * @version 1.3.16
1898
1898
  *
@@ -1928,7 +1928,7 @@ function undoFormidableArray(data) {
1928
1928
  /**
1929
1929
  * Export all data
1930
1930
  *
1931
- * @author Jelle De Loecker <jelle@develry.be>
1931
+ * @author Jelle De Loecker <jelle@elevenways.be>
1932
1932
  * @since 1.0.5
1933
1933
  * @version 1.0.5
1934
1934
  *
@@ -1946,7 +1946,7 @@ Alchemy.setMethod(function createExportStream(options) {
1946
1946
  /**
1947
1947
  * Export all data to stream
1948
1948
  *
1949
- * @author Jelle De Loecker <jelle@develry.be>
1949
+ * @author Jelle De Loecker <jelle@elevenways.be>
1950
1950
  * @since 1.0.5
1951
1951
  * @version 1.0.5
1952
1952
  *
@@ -1957,7 +1957,7 @@ Alchemy.setMethod(function createExportStream(options) {
1957
1957
  */
1958
1958
  Alchemy.setMethod(function exportToStream(output, options) {
1959
1959
 
1960
- if (!alchemy.isStream(output)) {
1960
+ if (!this.isStream(output)) {
1961
1961
  if (!options) {
1962
1962
  options = output;
1963
1963
  output = null;
@@ -1999,7 +1999,7 @@ Alchemy.setMethod(function exportToStream(output, options) {
1999
1999
  /**
2000
2000
  * Import from a stream
2001
2001
  *
2002
- * @author Jelle De Loecker <jelle@develry.be>
2002
+ * @author Jelle De Loecker <jelle@elevenways.be>
2003
2003
  * @since 1.0.5
2004
2004
  * @version 1.0.5
2005
2005
  *
@@ -2010,7 +2010,7 @@ Alchemy.setMethod(function exportToStream(output, options) {
2010
2010
  */
2011
2011
  Alchemy.setMethod(function importFromStream(input, options) {
2012
2012
 
2013
- if (!alchemy.isStream(input)) {
2013
+ if (!this.isStream(input)) {
2014
2014
  if (!options) {
2015
2015
  options = input;
2016
2016
  input = null;
@@ -2181,7 +2181,7 @@ Alchemy.setMethod(function importFromStream(input, options) {
2181
2181
  /**
2182
2182
  * Create a new schema
2183
2183
  *
2184
- * @author Jelle De Loecker <jelle@elevenways.be>
2184
+ * @author Jelle De Loecker <jelle@elevenways.be>
2185
2185
  * @since 1.2.1
2186
2186
  * @version 1.2.1
2187
2187
  *
@@ -2195,14 +2195,14 @@ Alchemy.setMethod(function createSchema(parent) {
2195
2195
  /**
2196
2196
  * Get a client-side model
2197
2197
  *
2198
- * @author Jelle De Loecker <jelle@elevenways.be>
2198
+ * @author Jelle De Loecker <jelle@elevenways.be>
2199
2199
  * @since 1.2.4
2200
2200
  * @version 1.2.4
2201
2201
  *
2202
- * @param {String} name
2203
- * @param {Object} options
2202
+ * @param {string} name
2203
+ * @param {Object} options
2204
2204
  *
2205
- * @return {Model}
2205
+ * @return {Model}
2206
2206
  */
2207
2207
  Alchemy.setMethod(function getClientModel(name, init, options) {
2208
2208
  return Classes.Alchemy.Client.Base.prototype.getModel.call(this, name, init, options);
@@ -2213,12 +2213,12 @@ let error_handler_count = 0;
2213
2213
  /**
2214
2214
  * Register an error handler
2215
2215
  *
2216
- * @author Jelle De Loecker <jelle@elevenways.be>
2216
+ * @author Jelle De Loecker <jelle@elevenways.be>
2217
2217
  * @since 1.3.11
2218
2218
  * @version 1.3.11
2219
2219
  *
2220
- * @param {Function} callback
2221
- * @param {Number} weight
2220
+ * @param {Function} callback
2221
+ * @param {number} weight
2222
2222
  */
2223
2223
  Alchemy.setMethod(function registerErrorHandler(callback, weight) {
2224
2224
 
@@ -2246,12 +2246,12 @@ Alchemy.setMethod(function registerErrorHandler(callback, weight) {
2246
2246
  /**
2247
2247
  * Process the given error
2248
2248
  *
2249
- * @author Jelle De Loecker <jelle@elevenways.be>
2249
+ * @author Jelle De Loecker <jelle@elevenways.be>
2250
2250
  * @since 1.3.11
2251
2251
  * @version 1.3.11
2252
2252
  *
2253
- * @param {Error} error
2254
- * @param {Object} info
2253
+ * @param {Error} error
2254
+ * @param {Object} info
2255
2255
  */
2256
2256
  Alchemy.setMethod(async function registerError(error, info = {}) {
2257
2257
 
@@ -2284,88 +2284,126 @@ Alchemy.setMethod(async function registerError(error, info = {}) {
2284
2284
  return handle_count;
2285
2285
  });
2286
2286
 
2287
+ let starting;
2288
+
2287
2289
  /**
2288
- * Initialize the error handler (if it's enabled)
2290
+ * Only start the server after this function has been called
2289
2291
  *
2290
- * @author Jelle De Loecker <jelle@elevenways.be>
2291
- * @since 1.3.11
2292
- * @version 1.3.11
2292
+ * @author Jelle De Loecker <jelle@elevenways.be>
2293
+ * @since 0.0.1
2294
+ * @version 1.1.5
2295
+ *
2296
+ * @param {Function} callback The optional callback after alchemy is ready
2297
+ *
2298
+ * @return {Pledge}
2293
2299
  */
2294
- function initializeErrorHandler() {
2300
+ Alchemy.setMethod(function start(options, callback) {
2301
+
2302
+ if (typeof options == 'function') {
2303
+ callback = options;
2304
+ options = {};
2305
+ }
2306
+
2307
+ if (!options) {
2308
+ options = {};
2309
+ }
2295
2310
 
2296
- const config = this.settings.errors ?? {};
2311
+ // If silent is true, don't output anything
2312
+ if (options.silent == true) {
2313
+ this.setSetting('debugging.silent', true);
2314
+ }
2297
2315
 
2298
- if (config.handle_uncaught) {
2299
- this.registerErrorHandler((error, info) => {
2316
+ if (starting) {
2317
+ return this.ready(callback);
2318
+ }
2300
2319
 
2301
- let message;
2320
+ if (Object.size(Blast.loaded_versions) > 1) {
2321
+ log.warn(Object.size(Blast.loaded_versions), 'versions of Protoblast have been loaded, this can cause problems!');
2322
+ }
2302
2323
 
2303
- if (info?.promise) {
2304
- message = 'Unhandled promise rejection';
2305
- } else {
2306
- message = 'Uncaught exception';
2307
- }
2324
+ if (options.client_mode) {
2325
+ this.setSetting('client_mode', true);
2326
+ } else {
2327
+ if (alchemy.settings.no_default_file) {
2328
+ log.warn('Could not find default settings file at "' + alchemy.settings.no_default_file + '.js"');
2329
+ }
2308
2330
 
2309
- alchemy.printLog('error', [message, String(error), error], {err: error, level: -2});
2310
- }, 1);
2331
+ if (alchemy.settings.no_local_file) {
2332
+ log.warn('Could not find local settings file');
2333
+ }
2334
+
2335
+ if (alchemy.settings.no_env_file) {
2336
+ log.warn('Could not find environment settings file at "' + alchemy.settings.no_env_file + '.js"');
2337
+ }
2311
2338
  }
2312
- }
2313
2339
 
2314
- /**
2315
- * The alchemy global, where everything will be stored
2316
- *
2317
- * @author Jelle De Loecker <jelle@develry.be>
2318
- * @since 0.0.1
2319
- * @version 0.4.0
2320
- *
2321
- * @type {Alchemy}
2322
- */
2323
- DEFINE('alchemy', new Alchemy());
2340
+ // Indicate the server is starting
2341
+ starting = true;
2342
+
2343
+ // Start the stages
2344
+ STAGES.launch([
2345
+ 'load_app',
2346
+ 'datasource',
2347
+ 'tasks',
2348
+ 'settings',
2349
+ 'routes',
2350
+ 'server',
2351
+ ]);
2352
+
2353
+ // Make sure Blast has executed everything that's still waiting
2354
+ Blast.doLoaded();
2355
+
2356
+ // Call the `afterStart` method
2357
+ this.ready(() => this.afterStart());
2358
+
2359
+ // Schedule the callback
2360
+ return this.ready(callback);
2361
+ });
2324
2362
 
2325
2363
  /**
2326
- * Define the log function
2364
+ * Stop the server
2327
2365
  *
2328
- * @author Jelle De Loecker <jelle@develry.be>
2329
- * @since 0.0.1
2330
- * @version 0.4.0
2366
+ * @author Jelle De Loecker <jelle@elevenways.be>
2367
+ * @since 1.0.3
2368
+ * @version 1.0.3
2331
2369
  *
2332
- * @type {Function}
2370
+ * @param {Function} callback The optional callback after alchemy has stopped
2333
2371
  */
2334
- DEFINE('log', alchemy.log);
2372
+ Alchemy.setMethod(function stop(callback) {
2335
2373
 
2336
- for (let key in alchemy.Janeway.LEVELS) {
2337
- let name = key.toLowerCase();
2338
- let val = alchemy.Janeway.LEVELS[key];
2374
+ this.server.close(function closed() {
2339
2375
 
2340
- log[name] = function(...args) {
2341
- return alchemy.printLog(val, args, {level: 2});
2342
- };
2343
- }
2376
+ if (callback) {
2377
+ callback();
2378
+ }
2344
2379
 
2345
- log.warn = log.warning;
2380
+ });
2381
+
2382
+ });
2346
2383
 
2347
2384
  /**
2348
- * Define the todo log function
2349
- *
2350
- * @author Jelle De Loecker <jelle@develry.be>
2351
- * @since 0.2.0
2352
- * @version 0.4.0
2385
+ * Initialize the error handler (if it's enabled)
2353
2386
  *
2354
- * @type {Function}
2387
+ * @author Jelle De Loecker <jelle@elevenways.be>
2388
+ * @since 1.3.11
2389
+ * @version 1.4.0
2355
2390
  */
2356
- log.todo = function todo(...args) {
2391
+ function initializeErrorHandler() {
2357
2392
 
2358
- var options = {
2359
- gutter: alchemy.Janeway.esc(91) + '\u2620 Todo:' + alchemy.Janeway.esc(39),
2360
- level: 2
2361
- };
2393
+ if (!this.settings.errors.handle_uncaught) {
2394
+ return;
2395
+ }
2396
+
2397
+ this.registerErrorHandler((error, info) => {
2398
+
2399
+ let message;
2362
2400
 
2363
- return alchemy.printLog(alchemy.TODO, args, options);
2364
- };
2401
+ if (info?.promise) {
2402
+ message = 'Unhandled promise rejection';
2403
+ } else {
2404
+ message = 'Uncaught exception';
2405
+ }
2365
2406
 
2366
- const anyBody = alchemy.use('body/any'),
2367
- formBody = alchemy.use('body/form'),
2368
- textBody = alchemy.use('body'),
2369
- formidable = alchemy.use('formidable'),
2370
- bodyParser = alchemy.use('body-parser'),
2371
- urlFormBody = bodyParser.urlencoded({extended: true});
2407
+ this.printLog('error', [message, String(error), error], {err: error, level: -2});
2408
+ }, 1);
2409
+ }