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.
- package/lib/app/behaviour/sluggable_behaviour.js +4 -2
- package/lib/app/conduit/http_conduit.js +7 -2
- package/lib/app/conduit/loopback_conduit.js +2 -2
- package/lib/app/conduit/socket_conduit.js +20 -5
- package/lib/app/controller/alchemy_info_controller.js +4 -8
- package/lib/app/helper/backed_map.js +2 -2
- package/lib/app/helper/router_helper.js +98 -24
- package/lib/app/helper_controller/controller.js +45 -30
- package/lib/app/helper_datasource/00-nosql_datasource.js +44 -10
- package/lib/app/helper_field/enum_field.js +4 -4
- package/lib/app/helper_field/schema_field.js +50 -36
- package/lib/app/helper_model/document.js +81 -46
- package/lib/app/helper_model/field_set.js +11 -0
- package/lib/app/helper_model/model.js +107 -53
- package/lib/app/helper_validator/00_validator.js +38 -6
- package/lib/app/helper_validator/not_empty_validator.js +1 -3
- package/lib/app/routes.js +7 -1
- package/lib/bootstrap.js +1 -0
- package/lib/class/conduit.js +438 -290
- package/lib/class/controller.js +18 -15
- package/lib/class/datasource.js +19 -8
- package/lib/class/document.js +3 -3
- package/lib/class/field.js +34 -3
- package/lib/class/inode.js +27 -0
- package/lib/class/inode_file.js +204 -4
- package/lib/class/migration.js +2 -1
- package/lib/class/model.js +16 -5
- package/lib/class/path_definition.js +76 -120
- package/lib/class/path_param_definition.js +202 -0
- package/lib/class/postponement.js +573 -0
- package/lib/class/route.js +193 -33
- package/lib/class/router.js +22 -4
- package/lib/class/schema.js +47 -11
- package/lib/class/schema_client.js +65 -35
- package/lib/class/session.js +138 -12
- package/lib/class/sitemap.js +341 -0
- package/lib/core/base.js +13 -3
- package/lib/core/client_alchemy.js +78 -7
- package/lib/core/client_base.js +16 -10
- package/lib/core/middleware.js +56 -45
- package/lib/init/alchemy.js +124 -11
- package/lib/init/constants.js +11 -0
- package/lib/init/functions.js +163 -86
- package/lib/stages.js +18 -3
- 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.
|
|
6
|
+
* @version 1.3.0
|
|
7
7
|
*/
|
|
8
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
393
|
-
|
|
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
|
-
|
|
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
|
-
|
|
419
|
-
|
|
425
|
+
let value = RURL.decodeUriSegment(values[index]);
|
|
426
|
+
let param_def = this.param_definitions?.[index];
|
|
420
427
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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
|
-
|
|
427
|
-
|
|
432
|
+
result[index] = {
|
|
433
|
+
name : token.name,
|
|
434
|
+
value : value,
|
|
435
|
+
original_value : value
|
|
436
|
+
};
|
|
428
437
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
type_check.then(function gotValue(value) {
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
432
440
|
|
|
433
|
-
|
|
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
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
+
// Return early when one of the checks fail
|
|
444
|
+
if (!entry) {
|
|
445
|
+
return null;
|
|
446
|
+
}
|
|
443
447
|
|
|
444
|
-
|
|
445
|
-
|
|
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
|
-
|
|
461
|
-
|
|
462
|
-
|
|
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
|
-
|
|
469
|
-
|
|
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
|
+
});
|