alchemymvc 1.3.21 → 1.4.0-alpha.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 (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 +10 -10
  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 +16 -16
  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 +84 -59
  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 +8 -8
  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 +203 -124
  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 +203 -163
  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 +50 -25
  103. package/lib/class/inode_list.js +4 -4
  104. package/lib/class/migration.js +4 -4
  105. package/lib/class/model.js +182 -168
  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 +73 -67
  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 +413 -374
  122. package/lib/{init/functions.js → core/alchemy_functions.js} +171 -108
  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 +39 -52
  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 -973
  155. package/lib/stages.js +0 -513
@@ -0,0 +1,57 @@
1
+ const libpath = require('path');
2
+
3
+ /**
4
+ * The "load_app" stage
5
+ *
6
+ * @author Jelle De Loecker <jelle@elevenways.be>
7
+ * @since 1.4.0
8
+ * @version 1.4.0
9
+ *
10
+ * @type {Alchemy.Stages.Stage}
11
+ */
12
+ const load_app = STAGES.createStage('load_app');
13
+
14
+ /**
15
+ * The "load_app.core_app" stage
16
+ *
17
+ * @author Jelle De Loecker <jelle@elevenways.be>
18
+ * @since 1.4.0
19
+ * @version 1.4.0
20
+ *
21
+ * @type {Alchemy.Stages.Stage}
22
+ */
23
+ const core_app = load_app.createStage('core_app', () => {
24
+ alchemy.useAppPath(libpath.resolve(PATH_CORE, 'app'), {weight: 1});
25
+ });
26
+
27
+ /**
28
+ * The "load_app.plugins" stage
29
+ *
30
+ * @author Jelle De Loecker <jelle@elevenways.be>
31
+ * @since 1.4.0
32
+ * @version 1.4.0
33
+ *
34
+ * @type {Alchemy.Stages.Stage}
35
+ */
36
+ const plugins = load_app.createStage('plugins', async () => {
37
+
38
+ // Make sure all the constitutions of the core classes are loaded
39
+ Blast.doLoaded();
40
+
41
+ // Now all the plugins will load
42
+ // (they registered themselves as a `post` task)
43
+ });
44
+
45
+ /**
46
+ * The "load_app.main_app" stage
47
+ *
48
+ * @author Jelle De Loecker <jelle@elevenways.be>
49
+ * @since 1.4.0
50
+ * @version 1.4.0
51
+ *
52
+ * @type {Alchemy.Stages.Stage}
53
+ */
54
+ const main_app = load_app.createStage('main_app', () => {
55
+ // Load in the app
56
+ alchemy.useAppPath(PATH_APP, {weight: 20, skip: ['routes']});
57
+ });
@@ -0,0 +1,61 @@
1
+ const libpath = require('path');
2
+
3
+ /**
4
+ * The "datasource" stage
5
+ *
6
+ * @author Jelle De Loecker <jelle@elevenways.be>
7
+ * @since 1.4.0
8
+ * @version 1.4.0
9
+ *
10
+ * @type {Alchemy.Stages.Stage}
11
+ */
12
+ const datasource = STAGES.createStage('datasource');
13
+
14
+ /**
15
+ * The "datasource.connect" stage:
16
+ * Make a connection to all the datasources.
17
+ *
18
+ * @author Jelle De Loecker <jelle@elevenways.be>
19
+ * @since 1.4.0
20
+ * @version 1.4.0
21
+ *
22
+ * @type {Alchemy.Stages.Stage}
23
+ */
24
+ const connect = datasource.createStage('connect', () => {
25
+
26
+ // Force Blast to load
27
+ try {
28
+ Blast.doLoaded();
29
+ } catch (err) {
30
+ alchemy.printLog('error', ['Failed to load application:', err.message], {err: err, level: 1});
31
+ return
32
+ }
33
+
34
+ let environment = alchemy.getSetting('environment');
35
+
36
+ // Require the environment datasources configuration
37
+ try {
38
+ require(libpath.resolve(PATH_ROOT, 'app', 'config', environment, 'database'));
39
+ } catch (err) {
40
+
41
+ if (err.code == 'MODULE_NOT_FOUND') {
42
+ if (!alchemy.getSetting('client_mode')) {
43
+ // Only output a warning when not in client mode
44
+ log.warn('Could not find ' + environment + ' database settings');
45
+ }
46
+ } else {
47
+ log.warn('Could not load ' + environment + ' database settings:', err);
48
+ }
49
+
50
+ return;
51
+ }
52
+
53
+ let tasks = [];
54
+
55
+ // Get all available datasources
56
+ Object.each(Datasource.get(), function eachDatasource(datasource, key) {
57
+ tasks.push(datasource.setup());
58
+ });
59
+
60
+ return Function.parallel(tasks);
61
+ });
@@ -0,0 +1,27 @@
1
+ /**
2
+ * The "tasks" stage
3
+ *
4
+ * @author Jelle De Loecker <jelle@elevenways.be>
5
+ * @since 1.4.0
6
+ * @version 1.4.0
7
+ *
8
+ * @type {Alchemy.Stages.Stage}
9
+ */
10
+ const tasks = STAGES.createStage('tasks');
11
+
12
+ // Do not start any task stage before the datasources are connected
13
+ tasks.dependsOn('datasource.connect');
14
+
15
+ /**
16
+ * "tasks.start_service"
17
+ * Start the task service
18
+ *
19
+ * @author Jelle De Loecker <jelle@elevenways.be>
20
+ * @since 1.4.0
21
+ * @version 1.4.0
22
+ *
23
+ * @type {Alchemy.Stages.Stage}
24
+ */
25
+ const start_service = tasks.createStage('start_service', () => {
26
+ alchemy.task_service = new Classes.Alchemy.Task.TaskService();
27
+ });
@@ -0,0 +1,68 @@
1
+ /**
2
+ * The "settings" stage.
3
+ * The settings definitions should already be loaded.
4
+ * The hard-coded settings too.
5
+ * In this stage, we load the settings from the database.
6
+ *
7
+ * @author Jelle De Loecker <jelle@elevenways.be>
8
+ * @since 1.4.0
9
+ * @version 1.4.0
10
+ *
11
+ * @type {Alchemy.Stages.Stage}
12
+ */
13
+ const settings = STAGES.createStage('settings');
14
+
15
+ // Do not start this stage before the datasources are connected
16
+ settings.dependsOn('datasource.connect');
17
+
18
+ /**
19
+ * "settings.load"
20
+ * Load the settings from the database
21
+ *
22
+ * @author Jelle De Loecker <jelle@elevenways.be>
23
+ * @since 1.4.0
24
+ * @version 1.4.0
25
+ *
26
+ * @type {Alchemy.Stages.Stage}
27
+ */
28
+ const load = settings.createStage('load', async () => {
29
+
30
+ let records = await Model.get('System.Setting').find('all');
31
+
32
+ if (!records.length) {
33
+ return;
34
+ }
35
+
36
+ for (let record of records) {
37
+ await record.applySetting(false);
38
+ }
39
+ });
40
+
41
+ /**
42
+ * "settings.perform_actions"
43
+ * Do all the setting-associated actions.
44
+ *
45
+ * @author Jelle De Loecker <jelle@elevenways.be>
46
+ * @since 1.4.0
47
+ * @version 1.4.0
48
+ *
49
+ * @type {Alchemy.Stages.Stage}
50
+ */
51
+ const perform_actions = settings.createStage('perform_actions', async () => {
52
+ await alchemy.system_settings.performAllActions();
53
+ });
54
+
55
+ /**
56
+ * "settings.to_object"
57
+ * Convert the `alchemy.settings` property to a simple object
58
+ *
59
+ * @author Jelle De Loecker <jelle@elevenways.be>
60
+ * @since 1.4.0
61
+ * @version 1.4.0
62
+ *
63
+ * @type {Alchemy.Stages.Stage}
64
+ */
65
+ const to_object = settings.createStage('to_object', async () => {
66
+ // Create the settings object
67
+ alchemy.settings = alchemy.system_settings.toObject();
68
+ });
@@ -0,0 +1,218 @@
1
+ const libpath = require('path');
2
+
3
+ /**
4
+ * The "routes" stage
5
+ *
6
+ * @author Jelle De Loecker <jelle@elevenways.be>
7
+ * @since 1.4.0
8
+ * @version 1.4.0
9
+ *
10
+ * @type {Alchemy.Stages.Stage}
11
+ */
12
+ const routes = STAGES.createStage('routes');
13
+
14
+ /**
15
+ * The "routes.hawkejs" stage:
16
+ * Setup the Hawkejs routes
17
+ *
18
+ * @author Jelle De Loecker <jelle@elevenways.be>
19
+ * @since 1.4.0
20
+ * @version 1.4.0
21
+ *
22
+ * @type {Alchemy.Stages.Stage}
23
+ */
24
+ const hawkejs = routes.createStage('hawkejs', () => {
25
+
26
+ // Set the correct asset paths
27
+ alchemy.hawkejs.style_path = 'stylesheets/';
28
+ alchemy.hawkejs.script_path = 'scripts/';
29
+
30
+ // Serve the hawkejs file
31
+ Router.use('/hawkejs/hawkejs-client.js', function getHawkejs(req, res, next) {
32
+
33
+ var retries = 0;
34
+
35
+ Blast.getClientPath({
36
+ modify_prototypes : true,
37
+ ua : req.conduit.headers.useragent,
38
+ create_source_map : alchemy.getSetting('debugging.create_source_map'),
39
+ enable_coverage : !!global.__coverage__,
40
+ debug : alchemy.getSetting('debugging.debug'),
41
+ }).done(gotClientFile);
42
+
43
+ function gotClientFile(err, path) {
44
+
45
+ if (err) {
46
+ console.log(err)
47
+ return retryFnc(err);
48
+ }
49
+
50
+ let options = {};
51
+
52
+ if (req.conduit && req.conduit.supports('async') === false) {
53
+ options.add_async_support = true;
54
+ }
55
+
56
+ alchemy.minifyScript(path, options, function gotMinifiedPath(err, mpath) {
57
+
58
+ var options;
59
+
60
+ if (!retries) {
61
+ options = {
62
+ onError: retryFnc
63
+ }
64
+ }
65
+
66
+ req.conduit.serveFile(mpath || path, options);
67
+ });
68
+ }
69
+
70
+ function retryFnc(err) {
71
+
72
+ if (retries > 0) {
73
+ return req.conduit.error(new Error('Failed to serve client file'));
74
+ }
75
+
76
+ retries++;
77
+
78
+ Blast.getClientPath({
79
+ refresh : true,
80
+ modify_prototypes : true,
81
+ ua : req.conduit.headers.useragent,
82
+ create_source_map : alchemy.getSetting('debugging.create_source_map'),
83
+ debug : alchemy.getSetting('debugging.debug'),
84
+ }).done(gotClientFile);
85
+ }
86
+ });
87
+
88
+ // Serve the static file with exposed variables
89
+ Router.use('/hawkejs/static.js', function getHawkejs(req, res, next) {
90
+ alchemy.hawkejs.getStaticExposedPath((err, path) => {
91
+
92
+ if (err) {
93
+ return req.conduit.error(err);
94
+ }
95
+
96
+ req.conduit.serveFile(path);
97
+ });
98
+ });
99
+
100
+ // Serve multiple template files
101
+ Router.use('/hawkejs/templates', function onGetTemplates(req, res) {
102
+
103
+ var names = req.conduit.param('name');
104
+
105
+ if (!names) {
106
+ return req.conduit.error(new Error('No template names have been given'));
107
+ }
108
+
109
+ alchemy.hawkejs.getFirstAvailableSource(names, function gotResult(err, result) {
110
+
111
+ if (err) {
112
+ return req.conduit.error(err);
113
+ }
114
+
115
+ if (!result || !result.name) {
116
+ return req.conduit.notFound('Could not find any of the given templates');
117
+ }
118
+
119
+ req.conduit.setHeader('cache-control', 'public, max-age=3600, must-revalidate');
120
+
121
+ // Don't use json dry, hawkejs expects regular json
122
+ req.conduit.json_dry = false;
123
+
124
+ req.conduit.end(result);
125
+ });
126
+ }, {methods: ['get'], weight: 19});
127
+
128
+ // Serve single template files
129
+ Router.use('/hawkejs/template', function onGetTemplate(req, res) {
130
+
131
+ var name = req.conduit.param('name');
132
+
133
+ if (!name) {
134
+ return req.conduit.error(new Error('No template name has been given'));
135
+ }
136
+
137
+ alchemy.hawkejs.getTemplatePath(name, function gotTemplate(err, path) {
138
+
139
+ if (err) {
140
+ return req.conduit.error(err);
141
+ }
142
+
143
+ if (!path) {
144
+ req.conduit.notFound('Could not find ' + name);
145
+ } else {
146
+ req.conduit.serveFile(path);
147
+ }
148
+ });
149
+ }, {methods: ['get'], weight: 19});
150
+ });
151
+
152
+ /**
153
+ * The "routes.middleware" stage:
154
+ * Setup all the middleware routes
155
+ *
156
+ * @author Jelle De Loecker <jelle@elevenways.be>
157
+ * @since 1.4.0
158
+ * @version 1.4.0
159
+ *
160
+ * @type {Alchemy.Stages.Stage}
161
+ */
162
+ const middleware = routes.createStage('middleware', () => {
163
+
164
+ // Serve public files
165
+ Router.use('/public/', alchemy.publicMiddleware, 50);
166
+
167
+ // Serve stylesheets
168
+ Router.use('/stylesheets/', alchemy.styleMiddleware, 50);
169
+
170
+ // Serve scripts
171
+ Router.use('/scripts/', alchemy.scriptMiddleware, 50);
172
+
173
+ // Serve fonts
174
+ Router.use('/fonts/', alchemy.fontMiddleware, 50);
175
+
176
+ // Serve root files
177
+ Router.use('/', alchemy.rootMiddleware, 49);
178
+
179
+ if (alchemy.getSetting('debugging.debug')) {
180
+ // Serve sourcemap files
181
+ Router.use('/_sourcemaps/', alchemy.sourcemapMiddleware, 50);
182
+ }
183
+
184
+ // Parse body (form-data & json, no multipart)
185
+ // @todo: not all routes require body parsing
186
+ Router.use(function parseBody(req, res, next) {
187
+
188
+ // Don't re-check internal redirects, they always should have a body set
189
+ if (req.original.body != null || (req.conduit && req.conduit instanceof Classes.Alchemy.Conduit.Loopback)) {
190
+ return next();
191
+ }
192
+
193
+ alchemy.parseRequestBody(req, res, next);
194
+
195
+ }, {methods: ['post'], weight: 99999});
196
+
197
+ });
198
+
199
+ /**
200
+ * The "routes.app_routes" stage:
201
+ * Setup the routes of the main app.
202
+ *
203
+ * @author Jelle De Loecker <jelle@elevenways.be>
204
+ * @since 1.4.0
205
+ * @version 1.4.0
206
+ *
207
+ * @type {Alchemy.Stages.Stage}
208
+ */
209
+ const app_routes = routes.createStage('app_routes', () => {
210
+ try {
211
+ alchemy.useOnce(libpath.resolve(PATH_APP, 'config', 'routes.js'));
212
+ } catch (err) {
213
+ // Only output warning when not in client mode
214
+ if (!alchemy.getSetting('client_mode')) {
215
+ log.warn('No app routes were found:', err);
216
+ }
217
+ }
218
+ });