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
@@ -0,0 +1,341 @@
1
+ let cached_sitemap;
2
+
3
+ /**
4
+ * Sitemap class
5
+ *
6
+ * @constructor
7
+ *
8
+ * @author Jelle De Loecker <jelle@elevenways.be>
9
+ * @since 1.3.0
10
+ * @version 1.3.0
11
+ *
12
+ */
13
+ const Sitemap = Function.inherits('Alchemy.Base', function Sitemap() {
14
+ this.categories = {};
15
+ });
16
+
17
+ /**
18
+ * Get the sitemap
19
+ *
20
+ * @author Jelle De Loecker <jelle@elevenways.be>
21
+ * @since 1.3.0
22
+ * @version 1.3.0
23
+ *
24
+ * @return {Pledge}
25
+ */
26
+ Sitemap.setStatic(function get() {
27
+
28
+ if (cached_sitemap) {
29
+ return cached_sitemap;
30
+ }
31
+
32
+ let pledge = new Pledge();
33
+ let sitemap = new Sitemap();
34
+ cached_sitemap = pledge;
35
+
36
+ let loading = sitemap.load();
37
+
38
+ Pledge.done(loading, err => {
39
+
40
+ if (err) {
41
+ pledge.reject(err);
42
+ } else {
43
+ pledge.resolve(sitemap);
44
+ }
45
+ });
46
+
47
+ return cached_sitemap;
48
+ });
49
+
50
+ /**
51
+ * Get all the categories
52
+ *
53
+ * @author Jelle De Loecker <jelle@elevenways.be>
54
+ * @since 1.3.0
55
+ * @version 1.3.0
56
+ *
57
+ * @return {Object}
58
+ */
59
+ Sitemap.setMethod(function getCategories(prefix) {
60
+
61
+ let result = [],
62
+ category,
63
+ new_category,
64
+ entry,
65
+ name;
66
+
67
+ for (name in this.categories) {
68
+ category = this.categories[name];
69
+ new_category = [];
70
+
71
+ for (entry of category) {
72
+
73
+ if (entry.prefix && prefix && entry.prefix != prefix) {
74
+ continue;
75
+ }
76
+
77
+ new_category.push(entry);
78
+ }
79
+
80
+ if (new_category.length) {
81
+ result.push({
82
+ name : name,
83
+ pages : new_category,
84
+ });
85
+ }
86
+ }
87
+
88
+ return result;
89
+ });
90
+
91
+ /**
92
+ * Start loading the contents
93
+ *
94
+ * @author Jelle De Loecker <jelle@elevenways.be>
95
+ * @since 1.3.0
96
+ * @version 1.3.0
97
+ *
98
+ * @return {Pledge}
99
+ */
100
+ Sitemap.setMethod(function load() {
101
+
102
+ let sections = Router.getFullRoutes(),
103
+ promises = [],
104
+ key;
105
+
106
+ for (key in sections) {
107
+ let promise = this.processSection(sections[key]);
108
+ promises.push(promise);
109
+ }
110
+
111
+ return Function.parallel(promises);
112
+ });
113
+
114
+ /**
115
+ * Add a URL to the sitemap
116
+ *
117
+ * @author Jelle De Loecker <jelle@elevenways.be>
118
+ * @since 1.3.0
119
+ * @version 1.3.0
120
+ */
121
+ Sitemap.setMethod(function addUrl(category, url, config = {}, prefix = '') {
122
+
123
+ if (!this.categories[category]) {
124
+ this.categories[category] = [];
125
+ }
126
+
127
+ this.categories[category].push({
128
+ url : url,
129
+ title : config.title,
130
+ changefreq : config.changefreq,
131
+ priority : config.priority,
132
+ lastmod : config.lastmod,
133
+ prefix : prefix,
134
+ });
135
+ });
136
+
137
+ /**
138
+ * Process a route section
139
+ *
140
+ * @author Jelle De Loecker <jelle@elevenways.be>
141
+ * @since 1.3.0
142
+ * @version 1.3.0
143
+ *
144
+ * @return {Promise}
145
+ */
146
+ Sitemap.setMethod(function processSection(section) {
147
+
148
+ let promise,
149
+ tasks = [],
150
+ route,
151
+ key;
152
+
153
+ for (key in section) {
154
+ route = section[key];
155
+
156
+ if (!route.sitemap) {
157
+ continue;
158
+ }
159
+
160
+ promise = this.processRoute(route, route.sitemap);
161
+
162
+ if (promise) {
163
+ tasks.push(promise);
164
+ }
165
+ }
166
+
167
+ return Function.parallel(tasks);
168
+ });
169
+
170
+ /**
171
+ * Process a route & its sitemap config
172
+ *
173
+ * @author Jelle De Loecker <jelle@elevenways.be>
174
+ * @since 1.3.0
175
+ * @version 1.3.0
176
+ *
177
+ * @return {Promise}
178
+ */
179
+ Sitemap.setTypedMethod([Types.Alchemy.Route], function processRoute(route) {
180
+
181
+ if (!route.sitemap) {
182
+ return;
183
+ }
184
+
185
+ return this.processRoute(route, route.sitemap);
186
+ });
187
+
188
+ /**
189
+ * Process a route & its sitemap config
190
+ *
191
+ * @author Jelle De Loecker <jelle@elevenways.be>
192
+ * @since 1.3.0
193
+ * @version 1.3.0
194
+ *
195
+ * @return {Promise}
196
+ */
197
+ Sitemap.setTypedMethod([Types.Alchemy.Route, Types.Boolean], function processRoute(route, value) {
198
+
199
+ if (!value) {
200
+ return;
201
+ }
202
+
203
+ return this.processRoute(route, {category: 'general'});
204
+ });
205
+
206
+ /**
207
+ * Process a route & its sitemap config
208
+ *
209
+ * @author Jelle De Loecker <jelle@elevenways.be>
210
+ * @since 1.3.0
211
+ * @version 1.3.0
212
+ *
213
+ * @return {Promise}
214
+ */
215
+ Sitemap.setTypedMethod([Types.Alchemy.Route, Types.Object], function processRoute(route, config) {
216
+
217
+ const category = config.category;
218
+
219
+ if (!category) {
220
+ return;
221
+ }
222
+
223
+ let criteria = config.criteria;
224
+
225
+ if (criteria) {
226
+
227
+ if (typeof criteria == 'function') {
228
+
229
+ let prefixes = [];
230
+
231
+ for (let prefix in route.paths) {
232
+ if (prefix) {
233
+ prefixes.push(prefix);
234
+ }
235
+ }
236
+
237
+ if (prefixes.length) {
238
+ let tasks = [];
239
+
240
+ for (let prefix of prefixes) {
241
+ let prefixed_criteria = criteria(prefix);
242
+ let task = this.processRouteCriteria(route, config, prefixed_criteria);
243
+
244
+ if (task) {
245
+ tasks.push(task);
246
+ }
247
+ }
248
+
249
+ return Function.parallel(tasks);
250
+ }
251
+
252
+ criteria = criteria();
253
+ }
254
+
255
+ return this.processRouteCriteria(route, config, criteria);
256
+ }
257
+
258
+ return this.addRouteWithParameters(route, config);
259
+ });
260
+
261
+ /**
262
+ * Process a route & its sitemap config
263
+ *
264
+ * @author Jelle De Loecker <jelle@elevenways.be>
265
+ * @since 1.3.0
266
+ * @version 1.3.0
267
+ *
268
+ * @return {Promise}
269
+ */
270
+ Sitemap.setTypedMethod([Types.Alchemy.Route, Types.Object, Types.Alchemy.Criteria], async function processRouteCriteria(route, config, criteria) {
271
+
272
+ let prefix = criteria.options.locale;
273
+
274
+ for await (const record of criteria) {
275
+ this.addRouteWithParameters(route, config, record, prefix);
276
+ }
277
+ });
278
+
279
+ /**
280
+ * Add a route with the given parameters
281
+ *
282
+ * @author Jelle De Loecker <jelle@elevenways.be>
283
+ * @since 1.3.0
284
+ * @version 1.3.0
285
+ *
286
+ * @return {Promise}
287
+ */
288
+ Sitemap.setMethod(function addRouteWithParameters(route, config, parameters, forced_prefix) {
289
+
290
+ if (!parameters) {
291
+ parameters = {};
292
+ } else {
293
+ parameters = Object.create(parameters);
294
+ }
295
+
296
+ let route_parameters = Object.assign(parameters, config.parameters),
297
+ prefixes = [];
298
+
299
+ if (forced_prefix) {
300
+ prefixes.push(forced_prefix);
301
+ } else {
302
+ for (let prefix in route.paths) {
303
+ prefixes.push(prefix);
304
+ }
305
+ }
306
+
307
+ for (let prefix of prefixes) {
308
+
309
+ if (!route.paths[prefix]) {
310
+ continue;
311
+ }
312
+
313
+ let url = Router.getUrl(route.name, route_parameters, {
314
+ absolute : true,
315
+ locale : prefix,
316
+ extra_get_parameters : false,
317
+ });
318
+
319
+ let route_config = Object.assign({}, config);
320
+
321
+ if (parameters && parameters.getDisplayFieldValue) {
322
+ route_config.title = parameters.getDisplayFieldValue({prefix});
323
+ }
324
+
325
+ if (!route_config.title) {
326
+ if (route.title) {
327
+ if (typeof route.title == 'object' && !(route.title instanceof Classes.Alchemy.Microcopy)) {
328
+ route_config.title = route.title[prefix] || route.title[''];
329
+ } else {
330
+ route_config.title = route.title;
331
+ }
332
+ }
333
+ }
334
+
335
+ if (!route_config.title) {
336
+ route_config.title = route.name;
337
+ }
338
+
339
+ this.addUrl(config.category, url, route_config, prefix);
340
+ }
341
+ });
package/lib/core/base.js CHANGED
@@ -233,9 +233,9 @@ Base.setStatic(function getAllChildren() {
233
233
  /**
234
234
  * Find a member of this group
235
235
  *
236
- * @author Jelle De Loecker <jelle@develry.be>
236
+ * @author Jelle De Loecker <jelle@elevenways.be>
237
237
  * @since 0.3.0
238
- * @version 1.1.4
238
+ * @version 1.3.0
239
239
  */
240
240
  Base.setStatic(function getMember(name) {
241
241
 
@@ -243,6 +243,10 @@ Base.setStatic(function getMember(name) {
243
243
  throw new Error('Unable to get class member of ' + this.name + ', given name is empty');
244
244
  }
245
245
 
246
+ if (name.includes('.')) {
247
+ return this.getDescendant(name);
248
+ }
249
+
246
250
  if (this.group == null) {
247
251
  return null;
248
252
  }
@@ -713,4 +717,10 @@ Base.setMethod(function debugMark(message) {
713
717
  */
714
718
  alchemy.getClassGroup = __Protoblast.getGroup;
715
719
 
716
- // PROTOBLAST END CUT
720
+ // PROTOBLAST END CUT
721
+
722
+ // @TODO: put this somewhere else
723
+ if (Blast.isBrowser) {
724
+ window.Types = Blast.Types;
725
+ window.Classes = Blast.Classes;
726
+ }
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * @author Jelle De Loecker <jelle@develry.be>
7
7
  * @since 0.1.0
8
- * @version 1.0.5
8
+ * @version 1.3.0
9
9
  */
10
10
  var Alchemy = Function.inherits('Alchemy.Client.Base', function Alchemy() {
11
11
 
@@ -22,6 +22,8 @@ var Alchemy = Function.inherits('Alchemy.Client.Base', function Alchemy() {
22
22
 
23
23
  // Sent subscriptions
24
24
  this.sent_subscriptions = [];
25
+
26
+ this.distinct_problems = new Map();
25
27
  });
26
28
 
27
29
  /**
@@ -125,6 +127,75 @@ Alchemy.setMethod(function initScene(scene, options) {
125
127
  });
126
128
  });
127
129
 
130
+
131
+ /**
132
+ * Log a distinct warning
133
+ *
134
+ * @author Jelle De Loecker <jelle@elevenways.be>
135
+ * @since 1.3.0
136
+ * @version 1.3.1
137
+ *
138
+ * @param {String} id
139
+ * @param {String} message
140
+ * @param {Object} options
141
+ *
142
+ * @return {Object}
143
+ */
144
+ Alchemy.setMethod(function distinctProblem(id, message, options = {}) {
145
+
146
+ if (!id) {
147
+ return;
148
+ }
149
+
150
+ let do_log = true,
151
+ entry = this.distinct_problems.get(id),
152
+ now = Date.now();
153
+
154
+ if (!entry) {
155
+ entry = {
156
+ id,
157
+ last_seen : 0,
158
+ last_logged : 0,
159
+ counter : 0,
160
+ repeat_after : options.repeat_after || 0,
161
+ };
162
+
163
+ this.distinct_problems.set(id, entry);
164
+ } else {
165
+ if (entry.repeat_after) {
166
+ let wait_until = entry.last_logged + entry.repeat_after;
167
+
168
+ if (now >= wait_until) {
169
+ do_log = true;
170
+ } else {
171
+ do_log = false;
172
+ }
173
+ } else {
174
+ do_log = false;
175
+ }
176
+ }
177
+
178
+ if (do_log) {
179
+ let args = ['[Distinct]', id];
180
+
181
+ if (message) {
182
+ args.push(message);
183
+ }
184
+
185
+ if (options.error) {
186
+ args.push(options.error);
187
+ }
188
+
189
+ this.printLog(options.level || 'error', args, options);
190
+ entry.last_logged = now;
191
+ }
192
+
193
+ entry.counter++;
194
+ entry.last_seen = now;
195
+
196
+ return entry;
197
+ });
198
+
128
199
  /**
129
200
  * Actually print a log message
130
201
  *
@@ -511,7 +582,7 @@ Alchemy.setMethod(function linkup(type, data, cb) {
511
582
  *
512
583
  * @author Jelle De Loecker <jelle@develry.be>
513
584
  * @since 0.3.0
514
- * @version 1.2.5
585
+ * @version 1.3.0
515
586
  *
516
587
  * @param {Object} variables
517
588
  * @param {Object} render_data
@@ -542,7 +613,7 @@ Alchemy.setMethod(function markLinks(variables, render_data) {
542
613
  elements = document.querySelectorAll('[data-alchemy-language-switch]');
543
614
 
544
615
  for (i = 0; i < elements.length; i++) {
545
- hawkejs.scene.general_renderer.helpers.Router.updateLanguageSwitcher(elements[i]);
616
+ hawkejs.scene.general_renderer.helpers.Router.updateLanguageSwitcher(elements[i], variables);
546
617
  }
547
618
 
548
619
  // Get all the elements with a breadcrumb set
@@ -835,17 +906,17 @@ Alchemy.decorateMethod(Blast.Decorators.memoize({max_age: 500, ignore_callbacks:
835
906
  /**
836
907
  * Get a route
837
908
  *
838
- * @author Jelle De Loecker <jelle@develry.be>
909
+ * @author Jelle De Loecker <jelle@elevenways.be>
839
910
  * @since 1.1.0
840
- * @version 1.1.0
911
+ * @version 1.3.0
841
912
  *
842
913
  * @param {String} href The href or route name
843
914
  * @param {Object} parameters Route parameters
844
915
  *
845
916
  * @return {String}
846
917
  */
847
- Alchemy.setMethod(function routeUrl(href, parameters) {
848
- let temp = hawkejs.scene.helpers.Router.routeUrl(href, parameters);
918
+ Alchemy.setMethod(function routeUrl(href, parameters, options) {
919
+ let temp = hawkejs.scene.helpers.Router.routeUrl(href, parameters, options);
849
920
 
850
921
  if (temp) {
851
922
  temp = String(temp);
@@ -294,19 +294,22 @@ ClientBase.setMethod(function getModel(name, init, options) {
294
294
  *
295
295
  * @author Jelle De Loecker <jelle@elevenways.be>
296
296
  * @since 1.1.1
297
- * @version 1.1.1
297
+ * @version 1.3.0
298
298
  *
299
299
  * @param {String} name The name of the event to emit
300
300
  * @param {Array} args The parameters to pass to the event
301
301
  * @param {Function} next The optional callback
302
+ *
303
+ * @return {Pledge}
302
304
  */
303
305
  ClientBase.setMethod(function issueEvent(name, args, next) {
304
306
 
305
307
  let that = this,
306
308
  method_name,
307
- promise;
309
+ promise,
310
+ pledge = new Pledge();
308
311
 
309
- if (typeof args == 'function') {
312
+ if (typeof args == 'function' || !Array.isArray(args)) {
310
313
  next = args;
311
314
  args = [];
312
315
  }
@@ -326,20 +329,23 @@ ClientBase.setMethod(function issueEvent(name, args, next) {
326
329
  Pledge.done(promise, function doneEventPromise(err, result) {
327
330
 
328
331
  if (err) {
332
+ pledge.reject(err);
329
333
  return next(err);
330
334
  }
331
335
 
332
- if (result === false) {
333
- return;
334
- }
336
+ if (result !== false) {
337
+ let event_args = args.slice(0);
335
338
 
336
- let event_args = args.slice(0);
339
+ event_args.unshift(name);
340
+ event_args.push(next);
337
341
 
338
- event_args.unshift(name);
339
- event_args.push(next);
342
+ that.emit.apply(that, event_args);
343
+ }
340
344
 
341
- that.emit.apply(that, event_args);
345
+ pledge.resolve(result);
342
346
  });
347
+
348
+ return pledge;
343
349
  });
344
350
 
345
351
  if (!Blast.isBrowser) {