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
@@ -3,12 +3,9 @@
3
3
  *
4
4
  * @author Jelle De Loecker <jelle@develry.be>
5
5
  * @since 1.0.0
6
- * @version 1.0.0
6
+ * @version 1.3.0
7
7
  */
8
- var PathDefinition = Function.inherits('Alchemy.Base', function PathDefinition(path, options) {
9
-
10
- var tokens,
11
- i;
8
+ const PathDefinition = Function.inherits('Alchemy.Base', function PathDefinition(path, options) {
12
9
 
13
10
  // Store the original path
14
11
  this.path = path;
@@ -17,22 +14,32 @@ var PathDefinition = Function.inherits('Alchemy.Base', function PathDefinition(p
17
14
  // Store the key tokens
18
15
  this.key_tokens = [];
19
16
 
17
+ // Store the param definitions
18
+ this.param_definitions = null;
19
+
20
20
  // Store the key names
21
21
  this.keys = [];
22
22
 
23
23
  // Path options
24
24
  this.options = options || {};
25
25
 
26
+ // The prefix of this path
27
+ this.prefix = this.options.prefix;
28
+
26
29
  // Get the tokens
27
- tokens = this.parse(path);
30
+ let tokens = this.parse(path);
28
31
 
29
32
  if (!this.regex) {
33
+ let i;
34
+
30
35
  // Compile the regex
31
36
  this.regex = this.tokensToRegexp(tokens, this.key_tokens);
32
37
 
33
38
  for (i = 0; i < this.key_tokens.length; i++) {
34
39
  this.keys[i] = this.key_tokens[i].name;
35
40
  }
41
+
42
+ this.param_definitions = Classes.Alchemy.PathParamDefinition.from(this.key_tokens);
36
43
  }
37
44
  });
38
45
 
@@ -103,6 +110,34 @@ PathDefinition.setStatic(function escapeString(str) {
103
110
  return str.replace(/([.+*?=^!:${}()[\]|/\\])/g, '\\$1')
104
111
  });
105
112
 
113
+
114
+ /**
115
+ * Does this path use any type class checks?
116
+ *
117
+ * @author Jelle De Loecker <jelle@elevenways.be>
118
+ * @since 1.3.0
119
+ * @version 1.3.0
120
+ */
121
+ PathDefinition.enforceProperty(function uses_type_class_checks(new_value) {
122
+
123
+ if (new_value == null) {
124
+ new_value = false;
125
+
126
+ if (this.param_definitions?.length) {
127
+ for (let def of this.param_definitions) {
128
+ def.parseTypeDefinition();
129
+
130
+ if (def.type_class_constructor) {
131
+ new_value = true;
132
+ break;
133
+ }
134
+ }
135
+ }
136
+ }
137
+
138
+ return new_value;
139
+ });
140
+
106
141
  /**
107
142
  * Get the complete section identifier
108
143
  *
@@ -364,7 +399,7 @@ PathDefinition.setMethod(function getParametersObject(values, info_type) {
364
399
  *
365
400
  * @author Jelle De Loecker <jelle@develry.be>
366
401
  * @since 1.0.0
367
- * @version 1.1.0
402
+ * @version 1.3.0
368
403
  *
369
404
  * @param {String} path
370
405
  *
@@ -372,15 +407,7 @@ PathDefinition.setMethod(function getParametersObject(values, info_type) {
372
407
  */
373
408
  PathDefinition.setMethod(function test(path, conduit) {
374
409
 
375
- var that = this,
376
- reject,
377
- values,
378
- result,
379
- pledge,
380
- tasks;
381
-
382
- // See if it matches & get the values
383
- values = this.regex.exec(path);
410
+ const values = this.regex.exec(path);
384
411
 
385
412
  if (!values) {
386
413
  return null;
@@ -389,133 +416,62 @@ PathDefinition.setMethod(function test(path, conduit) {
389
416
  // Remove the first part of the value, it's just the match
390
417
  values.shift();
391
418
 
392
- // Create a new array
393
- result = new Array(values.length);
394
- tasks = [];
419
+ let result = new Array(values.length),
420
+ do_await = false;
395
421
 
396
422
  // Iterate over the rest of the found values
397
- values.forEach(function eachValue(value, index) {
398
-
399
- var type_check,
400
- TypeChecker,
401
- token,
402
- entry;
403
-
404
- if (reject) {
405
- return;
406
- }
407
-
408
- token = that.key_tokens[index] || {};
409
-
410
- value = RURL.decodeUriSegment(value);
411
-
412
- entry = {
413
- name : token.name,
414
- value : value,
415
- original_value : value
416
- };
423
+ for (let index = 0; index < values.length; index++) {
417
424
 
418
- if (token.typedef) {
419
- type_check = that.checkType(value, token.typedef, token, conduit);
425
+ let value = RURL.decodeUriSegment(values[index]);
426
+ let param_def = this.param_definitions?.[index];
420
427
 
421
- if (typeof type_check === 'undefined') {
422
- reject = true;
423
- return;
424
- }
428
+ // There will be no param definition for regex-style matching
429
+ if (!param_def) {
430
+ let token = this.key_tokens[index] || {};
425
431
 
426
- entry.original_value = value;
427
- entry.value = type_check;
432
+ result[index] = {
433
+ name : token.name,
434
+ value : value,
435
+ original_value : value
436
+ };
428
437
 
429
- if (type_check && type_check.then) {
430
- tasks.push(function waitForCheck(next) {
431
- type_check.then(function gotValue(value) {
438
+ continue;
439
+ }
432
440
 
433
- if (value == null) {
434
- // Returning with an error still fucks shit up
435
- //return next(new Error('Path definition type check returned null'));
436
- reject = true;
437
- return next();
438
- }
441
+ let entry = param_def.parsePathValue(value, conduit);
439
442
 
440
- entry.value = value;
441
- next();
442
- });
443
+ // Return early when one of the checks fail
444
+ if (!entry) {
445
+ return null;
446
+ }
443
447
 
444
- type_check.catch(function onError(err) {
445
- next(err);
446
- });
447
- });
448
- }
448
+ if (Pledge.isThenable(entry)) {
449
+ do_await = true;
450
+ } else if (entry.rejected) {
451
+ return null;
449
452
  }
450
453
 
451
454
  result[index] = entry;
452
- });
453
-
454
- // Something made reject truthy,
455
- // so return null
456
- if (reject) {
457
- return null;
458
455
  }
459
456
 
460
- // If there are tasks, return the pledge
461
- if (tasks.length) {
462
- pledge = Function.parallel(tasks, function done(err) {
457
+ if (do_await) {
458
+
459
+ return Function.parallel(result, (err, result) => {
463
460
 
464
461
  if (err) {
465
462
  return null;
466
463
  }
467
464
 
468
- if (reject) {
469
- return null;
465
+ let i;
466
+
467
+ for (i = 0; i < result.length; i++) {
468
+ if (result[i]?.rejected) {
469
+ return null;
470
+ }
470
471
  }
471
472
 
472
473
  return result;
473
474
  });
474
-
475
- return pledge;
476
- }
477
-
478
- return result;
479
- });
480
-
481
- /**
482
- * Get the type checker
483
- *
484
- * @author Jelle De Loecker <jelle@develry.be>
485
- * @since 1.0.0
486
- * @version 1.1.0
487
- *
488
- * @param {String|Array} typedef
489
- *
490
- * @return {Function}
491
- */
492
- PathDefinition.setMethod(function checkType(value, typedef, token, conduit) {
493
-
494
- var class_name,
495
- field_name,
496
- TypeClass,
497
- is_string = typeof typedef === 'string',
498
- result,
499
- Model;
500
-
501
- if (is_string && PathDefinition.typedefs[typedef]) {
502
- result = PathDefinition.typedefs[typedef](value, token.name, conduit);
503
- } else {
504
-
505
- Model = Blast.Classes.Alchemy.Model || Blast.Classes.Alchemy.Client.Model;
506
-
507
- if (is_string) {
508
- class_name = typedef;
509
- } else {
510
- class_name = typedef[0];
511
- field_name = typedef[1];
512
- }
513
-
514
- TypeClass = Object.path(Model, class_name) || Object.path(Blast.Classes.Alchemy, class_name) || Object.path(Blast.Classes, class_name);
515
-
516
- if (TypeClass && TypeClass.checkPathValue) {
517
- result = TypeClass.checkPathValue(value, token.name, field_name, conduit);
518
- }
519
475
  }
520
476
 
521
477
  return result;
@@ -0,0 +1,202 @@
1
+ const PathDefinition = Classes.Alchemy.PathDefinition;
2
+
3
+ /**
4
+ * Path param definition
5
+ *
6
+ * @author Jelle De Loecker <jelle@elevenways.be>
7
+ * @since 1.3.0
8
+ * @version 1.3.0
9
+ */
10
+ const PathParamDefinition = Function.inherits('Alchemy.Base', function PathParamDefinition(config) {
11
+
12
+ // The delimiter, probably "/"
13
+ this.delimiter = config.delimiter;
14
+
15
+ // The name of this parameter
16
+ this.name = config.name;
17
+
18
+ // Is this parameter optional?
19
+ this.optional = config.optional || false;
20
+
21
+ this.partial = config.partial;
22
+ this.pattern = config.pattern;
23
+ this.prefix = config.prefix;
24
+ this.repeat = config.repeat;
25
+
26
+ // Set the raw type definition
27
+ this.typedef = config.typedef;
28
+
29
+ // Does this have a type definition?
30
+ this.has_type_definition = !!this.typedef;
31
+
32
+ // Does this use a simple typedefinition?
33
+ this.uses_simple_typedef = false;
34
+
35
+ // The optional type class name
36
+ this.type_class_name = null;
37
+
38
+ // The optional field inside the class
39
+ this.type_field_name = null;
40
+
41
+ // The optional type class constructor
42
+ this.type_class_constructor = null;
43
+
44
+ // Has all the config been parsed?
45
+ this.is_parsed = false;
46
+ });
47
+
48
+ /**
49
+ * Create path param definitions
50
+ *
51
+ * @author Jelle De Loecker <jelle@elevenways.be>
52
+ * @since 1.3.0
53
+ * @version 1.3.0
54
+ *
55
+ * @param {Array|Object}
56
+ */
57
+ PathParamDefinition.setStatic(function from(input) {
58
+
59
+ if (!input) {
60
+ return;
61
+ }
62
+
63
+ if (Array.isArray(input)) {
64
+ let result = [],
65
+ entry,
66
+ temp;
67
+
68
+ for (entry of input) {
69
+ temp = PathParamDefinition.from(entry);
70
+
71
+ if (temp) {
72
+ result.push(temp);
73
+ }
74
+ }
75
+
76
+ return result;
77
+ }
78
+
79
+ return new this(input);
80
+ });
81
+
82
+ /**
83
+ * Parse the type definition
84
+ *
85
+ * @author Jelle De Loecker <jelle@elevenways.be>
86
+ * @since 1.3.0
87
+ * @version 1.3.0
88
+ */
89
+ PathParamDefinition.setMethod(function parseTypeDefinition() {
90
+
91
+ if (this.is_parsed) {
92
+ return;
93
+ }
94
+
95
+ this.is_parsed = true;
96
+
97
+ if (!this.has_type_definition) {
98
+ return;
99
+ }
100
+
101
+ let class_name,
102
+ field_name;
103
+
104
+ if (typeof this.typedef == 'string') {
105
+ this.uses_simple_typedef = !!PathDefinition.typedefs[this.typedef];
106
+
107
+ // If it's not a simple type, the type is a class name
108
+ if (!this.uses_simple_typedef) {
109
+ class_name = this.typedef;
110
+ }
111
+ } else {
112
+ class_name = this.typedef[0];
113
+ field_name = this.typedef[1];
114
+ }
115
+
116
+ if (class_name) {
117
+ this.type_class_name = class_name;
118
+ this.type_field_name = field_name;
119
+
120
+ const Model = Classes.Alchemy.Model || Classes.Alchemy.Client.Model;
121
+ this.type_class_constructor = Object.path(Model, class_name) || Object.path(Classes.Alchemy, class_name) || Object.path(Classes, class_name);
122
+ }
123
+ });
124
+
125
+ /**
126
+ * Check the value
127
+ *
128
+ * @author Jelle De Loecker <jelle@elevenways.be>
129
+ * @since 1.3.0
130
+ * @version 1.3.0
131
+ *
132
+ * @param {Boolean|Pledge<Boolean>}
133
+ */
134
+ PathParamDefinition.setMethod(function castValueToType(value, conduit) {
135
+
136
+ if (!this.is_parsed) {
137
+ this.parseTypeDefinition();
138
+ }
139
+
140
+ let result;
141
+
142
+ if (this.uses_simple_typedef) {
143
+ result = PathDefinition.typedefs[this.typedef](value, this.name, conduit);
144
+ } else if (this.type_class_constructor?.checkPathValue) {
145
+ result = this.type_class_constructor.checkPathValue(value, this.name, this.type_field_name, conduit);
146
+ }
147
+
148
+ return result;
149
+ });
150
+
151
+
152
+ /**
153
+ * Parse an original path value
154
+ *
155
+ * @author Jelle De Loecker <jelle@elevenways.be>
156
+ * @since 1.3.0
157
+ * @version 1.3.0
158
+ *
159
+ * @param {Object|Pledge<Object>}
160
+ */
161
+ PathParamDefinition.setMethod(function parsePathValue(original_value, conduit) {
162
+
163
+ let result = {
164
+ name : this.name,
165
+ value : original_value,
166
+ rejected : false,
167
+ original_value,
168
+ };
169
+
170
+ if (this.has_type_definition) {
171
+ let new_value = this.castValueToType(original_value, conduit);
172
+
173
+ if (typeof new_value === 'undefined') {
174
+ return false;
175
+ }
176
+
177
+ if (Pledge.isThenable(new_value)) {
178
+ let pledge = new Pledge();
179
+
180
+ Pledge.done(new_value, (err, new_value) => {
181
+
182
+ if (err) {
183
+ pledge.reject(err);
184
+ } else {
185
+ result.value = new_value;
186
+
187
+ if (new_value == null) {
188
+ result.rejected = true;
189
+ }
190
+
191
+ pledge.resolve(result);
192
+ }
193
+ });
194
+
195
+ return pledge;
196
+ }
197
+
198
+ result.value = new_value;
199
+ }
200
+
201
+ return result;
202
+ });