alchemymvc 1.3.19 → 1.3.20

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 (33) hide show
  1. package/lib/app/behaviour/sluggable_behaviour.js +0 -0
  2. package/lib/app/datasource/mongo_datasource.js +94 -1
  3. package/lib/app/element/time_ago.js +0 -0
  4. package/lib/app/helper/alchemy_helper.js +0 -0
  5. package/lib/app/helper_datasource/00-nosql_datasource.js +0 -0
  6. package/lib/app/helper_field/{date_field.js → 11-date_field.js} +1 -3
  7. package/lib/app/helper_field/15-local_temporal_field.js +100 -0
  8. package/lib/app/helper_field/20-decimal_field.js +105 -0
  9. package/lib/app/helper_field/big_int_field.js +7 -13
  10. package/lib/app/helper_field/fixed_decimal_field.js +44 -0
  11. package/lib/app/helper_field/local_date_field.js +39 -0
  12. package/lib/app/helper_field/local_date_time_field.js +39 -0
  13. package/lib/app/helper_field/local_time_field.js +39 -0
  14. package/lib/app/helper_field/schema_field.js +11 -0
  15. package/lib/app/helper_model/field_set.js +0 -0
  16. package/lib/app/helper_model/model.js +31 -17
  17. package/lib/app/model/alchemy_task_history_model.js +0 -0
  18. package/lib/bootstrap.js +0 -0
  19. package/lib/class/datasource.js +80 -0
  20. package/lib/class/inode_file.js +0 -0
  21. package/lib/class/model.js +3 -16
  22. package/lib/class/path_evaluator.js +16 -9
  23. package/lib/class/router.js +0 -0
  24. package/lib/class/schema_client.js +0 -0
  25. package/lib/class/session.js +0 -0
  26. package/lib/class/task.js +0 -0
  27. package/lib/class/task_service.js +32 -7
  28. package/lib/core/base.js +19 -4
  29. package/lib/core/client_alchemy.js +7 -0
  30. package/lib/core/middleware.js +0 -0
  31. package/lib/init/constants.js +23 -0
  32. package/lib/init/requirements.js +0 -0
  33. package/package.json +2 -2
File without changes
@@ -1,4 +1,5 @@
1
- var MongoClient = alchemy.use('mongodb').MongoClient;
1
+ const mongo = alchemy.use('mongodb'),
2
+ MongoClient = mongo.MongoClient;
2
3
 
3
4
  /**
4
5
  * MongoDb Datasource
@@ -102,6 +103,98 @@ Mongo.setProperty('allowed_find_options', [
102
103
  'session'
103
104
  ]);
104
105
 
106
+ /**
107
+ * Convert the given value to a BigInt
108
+ *
109
+ * @author Jelle De Loecker <jelle@elevenways.be>
110
+ * @since 1.3.20
111
+ * @version 1.3.20
112
+ *
113
+ * @param {*} value
114
+ *
115
+ * @return {BigInt}
116
+ */
117
+ Mongo.setMethod(function castToBigInt(value) {
118
+
119
+ if (value == null) {
120
+ return value;
121
+ }
122
+
123
+ if (typeof value == 'object') {
124
+ return value.toBigInt();
125
+ }
126
+
127
+ return BigInt(value);
128
+ });
129
+
130
+ /**
131
+ * Convert the given value from a BigInt (for use in DB)
132
+ *
133
+ * @author Jelle De Loecker <jelle@elevenways.be>
134
+ * @since 1.3.20
135
+ * @version 1.3.20
136
+ *
137
+ * @param {BigInt} value
138
+ *
139
+ * @return {Mongo.Long}
140
+ */
141
+ Mongo.setMethod(function convertBigIntForDatasource(value) {
142
+
143
+ if (value == null) {
144
+ return value;
145
+ }
146
+
147
+ return mongo.Long.fromBigInt(value);
148
+ });
149
+
150
+ /**
151
+ * Convert the given value to a Decimal (for use in JS)
152
+ *
153
+ * @author Jelle De Loecker <jelle@elevenways.be>
154
+ * @since 1.3.20
155
+ * @version 1.3.20
156
+ *
157
+ * @param {*} value
158
+ *
159
+ * @return {BigInt}
160
+ */
161
+ Mongo.setMethod(function castToDecimal(value) {
162
+
163
+ if (value == null) {
164
+ return value;
165
+ }
166
+
167
+ if (typeof value == 'object') {
168
+ value = value.toString();
169
+ }
170
+
171
+ return new Blast.Classes.Develry.Decimal(value);
172
+ });
173
+
174
+ /**
175
+ * Convert the given decimal for use in DB
176
+ *
177
+ * @author Jelle De Loecker <jelle@elevenways.be>
178
+ * @since 1.3.20
179
+ * @version 1.3.20
180
+ *
181
+ * @param {Decimal|String} value
182
+ *
183
+ * @return {Mongo.Decimal128}
184
+ */
185
+ Mongo.setMethod(function convertDecimalForDatasource(value) {
186
+
187
+ if (value == null) {
188
+ return value;
189
+ }
190
+
191
+ if (!(value instanceof Blast.Classes.Develry.Decimal)) {
192
+ value = new Blast.Classes.Develry.Decimal(value);
193
+ }
194
+
195
+ return mongo.Decimal128.fromString(value.toString());
196
+ });
197
+
105
198
  /**
106
199
  * Get find options for the MongoDB server
107
200
  *
File without changes
File without changes
File without changes
@@ -7,9 +7,7 @@
7
7
  * @since 0.2.0
8
8
  * @version 1.1.0
9
9
  */
10
- var DateField = Function.inherits('Alchemy.Field', function Date(schema, name, options) {
11
- Date.super.call(this, schema, name, options);
12
- });
10
+ const DateField = Function.inherits('Alchemy.Field', 'Date');
13
11
 
14
12
  /**
15
13
  * Set the datatype name
@@ -0,0 +1,100 @@
1
+ /**
2
+ * The abstract LocalTemporal Field class
3
+ *
4
+ * @constructor
5
+ *
6
+ * @author Jelle De Loecker <jelle@elevenways.be>
7
+ * @since 1.3.20
8
+ * @version 1.3.20
9
+ */
10
+ const LocalTemporal = Function.inherits('Alchemy.Field.Date', 'LocalTemporal');
11
+
12
+ /**
13
+ * Mark this class as being abstract
14
+ *
15
+ * @author Jelle De Loecker <jelle@elevenways.be>
16
+ * @since 1.3.20
17
+ * @version 1.3.20
18
+ */
19
+ LocalTemporal.makeAbstractClass();
20
+
21
+ /**
22
+ * Cast the given value to this field's type for search in a db
23
+ *
24
+ * @author Jelle De Loecker <jelle@elevenways.be>
25
+ * @since 1.3.20
26
+ * @version 1.3.20
27
+ *
28
+ * @param {Mixed} value
29
+ * @param {Array} field_paths The path to the field
30
+ *
31
+ * @return {Mixed}
32
+ */
33
+ LocalTemporal.setMethod(function _castCondition(value, field_paths) {
34
+
35
+ if (value == null) {
36
+ return value;
37
+ }
38
+
39
+ value = this.cast(value);
40
+ value = value.toNumericRepresentation();
41
+
42
+ if (typeof value == 'bigint') {
43
+ value = this.datasource.convertBigIntForDatasource(value);
44
+ }
45
+
46
+ return value;
47
+ });
48
+
49
+ /**
50
+ * Prepare the value to be stored in the database
51
+ *
52
+ * @author Jelle De Loecker <jelle@elevenways.be>
53
+ * @since 1.3.20
54
+ * @version 1.3.20
55
+ *
56
+ * @param {Mixed} value The field's own value
57
+ * @param {Object} data The main record
58
+ * @param {Datasource} datasource The datasource instance
59
+ *
60
+ * @return {Mixed}
61
+ */
62
+ LocalTemporal.setMethod(function _toDatasource(value, data, datasource, callback) {
63
+
64
+ value = this.cast(value);
65
+
66
+ if (value) {
67
+ value = value.toNumericRepresentation();
68
+ }
69
+
70
+ if (typeof value == 'bigint') {
71
+ value = this.datasource.convertBigIntForDatasource(value);
72
+ }
73
+
74
+ Blast.nextTick(() => callback(null, value));
75
+ });
76
+
77
+ /**
78
+ * Convert from database to app
79
+ *
80
+ * @author Jelle De Loecker <jelle@elevenways.be>
81
+ * @since 1.3.20
82
+ * @version 1.3.20
83
+ *
84
+ * @param {Object} query The original query
85
+ * @param {Object} options The original query options
86
+ * @param {Mixed} value The field value, as stored in the DB
87
+ * @param {Function} callback
88
+ */
89
+ LocalTemporal.setMethod(function _toApp(query, options, value, callback) {
90
+
91
+ if (value && typeof value == 'object') {
92
+ if (typeof value.getUTCDate != 'function') {
93
+ value = this.datasource.castToBigInt(value);
94
+ }
95
+ }
96
+
97
+ value = this.cast(value);
98
+
99
+ callback(null, value);
100
+ });
@@ -0,0 +1,105 @@
1
+ /**
2
+ * The Decimal Field class
3
+ *
4
+ * @constructor
5
+ *
6
+ * @author Jelle De Loecker <jelle@elevenways.be>
7
+ * @since 1.3.20
8
+ * @version 1.3.20
9
+ */
10
+ const DecimalField = Function.inherits('Alchemy.Field.Number', 'Decimal');
11
+
12
+ /**
13
+ * Cast the given value to this field's type
14
+ *
15
+ * @author Jelle De Loecker <jelle@elevenways.be>
16
+ * @since 1.3.20
17
+ * @version 1.3.20
18
+ *
19
+ * @param {Mixed} value
20
+ *
21
+ * @return {Decimal}
22
+ */
23
+ DecimalField.setMethod(function cast(value) {
24
+
25
+ // Allow null
26
+ if (value == null) {
27
+ return value;
28
+ }
29
+
30
+ if (value instanceof Classes.Develry.Decimal) {
31
+ value = value.toImmutable();
32
+ } else {
33
+ value = new Classes.Develry.Decimal(value);
34
+ }
35
+
36
+ return value;
37
+ });
38
+
39
+ /**
40
+ * Cast the given value to this field's type for search in a db
41
+ *
42
+ * @author Jelle De Loecker <jelle@elevenways.be>
43
+ * @since 1.3.20
44
+ * @version 1.3.20
45
+ *
46
+ * @param {Mixed} value
47
+ * @param {Array} field_paths The path to the field
48
+ *
49
+ * @return {Mixed}
50
+ */
51
+ DecimalField.setMethod(function _castCondition(value, field_paths) {
52
+
53
+ if (value == null) {
54
+ return value;
55
+ }
56
+
57
+ value = this.cast(value);
58
+ value = this.datasource.convertDecimalForDatasource(value);
59
+
60
+ return value;
61
+ });
62
+
63
+ /**
64
+ * Prepare the value to be stored in the database
65
+ *
66
+ * @author Jelle De Loecker <jelle@elevenways.be>
67
+ * @since 1.3.20
68
+ * @version 1.3.20
69
+ *
70
+ * @param {Mixed} value The field's own value
71
+ * @param {Object} data The main record
72
+ * @param {Datasource} datasource The datasource instance
73
+ *
74
+ * @return {Mixed}
75
+ */
76
+ DecimalField.setMethod(function _toDatasource(value, data, datasource, callback) {
77
+
78
+ value = this.cast(value);
79
+ value = this.datasource.convertDecimalForDatasource(value);
80
+
81
+ Blast.nextTick(() => callback(null, value));
82
+ });
83
+
84
+ /**
85
+ * Convert from database to app
86
+ *
87
+ * @author Jelle De Loecker <jelle@elevenways.be>
88
+ * @since 1.3.20
89
+ * @version 1.3.20
90
+ *
91
+ * @param {Object} query The original query
92
+ * @param {Object} options The original query options
93
+ * @param {Mixed} value The field value, as stored in the DB
94
+ * @param {Function} callback
95
+ */
96
+ DecimalField.setMethod(function _toApp(query, options, value, callback) {
97
+
98
+ if (value && typeof value == 'object') {
99
+ value = value.toString();
100
+ }
101
+
102
+ value = this.cast(value);
103
+
104
+ callback(null, value);
105
+ });
@@ -50,7 +50,7 @@ BigIntField.setMethod(function cast(value) {
50
50
  *
51
51
  * @author Jelle De Loecker <jelle@elevenways.be>
52
52
  * @since 1.3.6
53
- * @version 1.3.6
53
+ * @version 1.3.20
54
54
  *
55
55
  * @param {Mixed} value
56
56
  * @param {Array} field_paths The path to the field
@@ -60,10 +60,7 @@ BigIntField.setMethod(function cast(value) {
60
60
  BigIntField.setMethod(function _castCondition(value, field_paths) {
61
61
 
62
62
  value = this.cast(value);
63
-
64
- if (mongo) {
65
- value = mongo.Long.fromBigInt(value);
66
- }
63
+ value = this.convertBigIntForDatasource(value);
67
64
 
68
65
  return value;
69
66
  });
@@ -73,7 +70,7 @@ BigIntField.setMethod(function _castCondition(value, field_paths) {
73
70
  *
74
71
  * @author Jelle De Loecker <jelle@elevenways.be>
75
72
  * @since 1.3.6
76
- * @version 1.3.6
73
+ * @version 1.3.20
77
74
  *
78
75
  * @param {Mixed} value The field's own value
79
76
  * @param {Object} data The main record
@@ -84,10 +81,7 @@ BigIntField.setMethod(function _castCondition(value, field_paths) {
84
81
  BigIntField.setMethod(function _toDatasource(value, data, datasource, callback) {
85
82
 
86
83
  value = this.cast(value);
87
-
88
- if (mongo) {
89
- value = mongo.Long.fromBigInt(value);
90
- }
84
+ value = this.datasource.convertBigIntForDatasource(value);
91
85
 
92
86
  callback(null, value);
93
87
  });
@@ -97,7 +91,7 @@ BigIntField.setMethod(function _toDatasource(value, data, datasource, callback)
97
91
  *
98
92
  * @author Jelle De Loecker <jelle@elevenways.be>
99
93
  * @since 1.3.6
100
- * @version 1.3.6
94
+ * @version 1.3.20
101
95
  *
102
96
  * @param {Object} query The original query
103
97
  * @param {Object} options The original query options
@@ -106,8 +100,8 @@ BigIntField.setMethod(function _toDatasource(value, data, datasource, callback)
106
100
  */
107
101
  BigIntField.setMethod(function _toApp(query, options, value, callback) {
108
102
 
109
- if (mongo && value && value.toBigInt) {
110
- value = value.toBigInt();
103
+ if (value) {
104
+ value = this.datasource.castToBigInt(value);
111
105
  }
112
106
 
113
107
  value = this.cast(value);
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The FixedDecimal Field class
3
+ *
4
+ * @constructor
5
+ *
6
+ * @author Jelle De Loecker <jelle@elevenways.be>
7
+ * @since 1.3.20
8
+ * @version 1.3.20
9
+ */
10
+ const FixedDecimalField = Function.inherits('Alchemy.Field.Decimal', function FixedDecimal(schema, name, options) {
11
+
12
+ if (options?.scale == null) {
13
+ throw new Error('FixedDecimal fields require a scale option');
14
+ }
15
+
16
+ FixedDecimal.super.call(this, schema, name, options);
17
+ });
18
+
19
+ /**
20
+ * Cast the given value to this field's type
21
+ *
22
+ * @author Jelle De Loecker <jelle@elevenways.be>
23
+ * @since 1.3.20
24
+ * @version 1.3.20
25
+ *
26
+ * @param {Mixed} value
27
+ *
28
+ * @return {Decimal}
29
+ */
30
+ FixedDecimalField.setMethod(function cast(value) {
31
+
32
+ // Allow null
33
+ if (value == null) {
34
+ return value;
35
+ }
36
+
37
+ if (value instanceof Classes.Develry.Decimal) {
38
+ value = value.toImmutable().toScale(this.options.scale);
39
+ } else {
40
+ value = new Classes.Develry.FixedDecimal(value, this.options.scale);
41
+ }
42
+
43
+ return value;
44
+ });
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The LocalDate Field class
3
+ *
4
+ * @constructor
5
+ *
6
+ * @author Jelle De Loecker <jelle@elevenways.be>
7
+ * @since 1.3.20
8
+ * @version 1.3.20
9
+ */
10
+ const LocalDate = Function.inherits('Alchemy.Field.LocalTemporal', 'LocalDate');
11
+
12
+ /**
13
+ * Set the datatype name
14
+ *
15
+ * @author Jelle De Loecker <jelle@elevenways.be>
16
+ * @since 1.3.20
17
+ * @version 1.3.20
18
+ */
19
+ LocalDate.setDatatype('date');
20
+
21
+ /**
22
+ * Cast the given value to this field's type
23
+ *
24
+ * @author Jelle De Loecker <jelle@elevenways.be>
25
+ * @since 1.3.20
26
+ * @version 1.3.20
27
+ *
28
+ * @param {Mixed} value
29
+ *
30
+ * @return {Develry.LocalDate}
31
+ */
32
+ LocalDate.setMethod(function cast(value) {
33
+
34
+ if (value == null || value === '') {
35
+ return null;
36
+ }
37
+
38
+ return Classes.Develry.LocalDate.create(value);
39
+ });
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The LocalDateTime Field class
3
+ *
4
+ * @constructor
5
+ *
6
+ * @author Jelle De Loecker <jelle@elevenways.be>
7
+ * @since 1.3.20
8
+ * @version 1.3.20
9
+ */
10
+ const LocalDateTime = Function.inherits('Alchemy.Field.LocalTemporal', 'LocalDateTime');
11
+
12
+ /**
13
+ * Set the datatype name
14
+ *
15
+ * @author Jelle De Loecker <jelle@elevenways.be>
16
+ * @since 1.3.20
17
+ * @version 1.3.20
18
+ */
19
+ LocalDateTime.setDatatype('datetime');
20
+
21
+ /**
22
+ * Cast the given value to this field's type
23
+ *
24
+ * @author Jelle De Loecker <jelle@elevenways.be>
25
+ * @since 1.3.20
26
+ * @version 1.3.20
27
+ *
28
+ * @param {Mixed} value
29
+ *
30
+ * @return {Develry.LocalDateTime}
31
+ */
32
+ LocalDateTime.setMethod(function cast(value) {
33
+
34
+ if (value == null || value === '') {
35
+ return null;
36
+ }
37
+
38
+ return Classes.Develry.LocalDateTime.create(value);
39
+ });
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The LocalTime Field class
3
+ *
4
+ * @constructor
5
+ *
6
+ * @author Jelle De Loecker <jelle@elevenways.be>
7
+ * @since 1.3.20
8
+ * @version 1.3.20
9
+ */
10
+ const LocalTime = Function.inherits('Alchemy.Field.LocalTemporal', 'LocalTime');
11
+
12
+ /**
13
+ * Set the datatype name
14
+ *
15
+ * @author Jelle De Loecker <jelle@elevenways.be>
16
+ * @since 1.3.20
17
+ * @version 1.3.20
18
+ */
19
+ LocalTime.setDatatype('time');
20
+
21
+ /**
22
+ * Cast the given value to this field's type
23
+ *
24
+ * @author Jelle De Loecker <jelle@elevenways.be>
25
+ * @since 1.3.20
26
+ * @version 1.3.20
27
+ *
28
+ * @param {Mixed} value
29
+ *
30
+ * @return {Develry.LocalTime}
31
+ */
32
+ LocalTime.setMethod(function cast(value) {
33
+
34
+ if (value == null || value === '') {
35
+ return null;
36
+ }
37
+
38
+ return Classes.Develry.LocalTime.create(value);
39
+ });
@@ -414,6 +414,17 @@ SchemaField.setMethod(function _toAppFromValue(query, options, value, callback)
414
414
  record = {
415
415
  [this.schema.name] : options.parent_value
416
416
  };
417
+ } else if (options._root_data) {
418
+
419
+ let root_data = options._root_data;
420
+
421
+ if (root_data[this.schema.name]) {
422
+ root_data = root_data[this.schema.name];
423
+ }
424
+
425
+ record = {
426
+ [this.schema.name] : root_data
427
+ };
417
428
  } else {
418
429
  record = {
419
430
  [this.schema.name] : {
File without changes
@@ -1626,7 +1626,7 @@ Hawkejs.Model = Model;
1626
1626
  *
1627
1627
  * @author Jelle De Loecker <jelle@develry.be>
1628
1628
  * @since 0.0.1
1629
- * @version 1.1.1
1629
+ * @version 1.3.20
1630
1630
  *
1631
1631
  * @param {Document} document
1632
1632
  * @param {Object} options
@@ -1675,9 +1675,9 @@ Model.setMethod(function saveRecord(document, options, callback) {
1675
1675
  creating = options.create || doc.$pk == null;
1676
1676
  next();
1677
1677
  });
1678
- }, function doBeforeSave(next) {
1678
+ }, function doBeforeNormalize(next) {
1679
1679
  // @TODO: make "beforeSave" only use promises
1680
- that.callOrNext('beforeSave', [document, options], next);
1680
+ that.callOrNext('beforeNormalize', [document, options], next);
1681
1681
  }, function emitSavingEvent(next) {
1682
1682
  // @TODO: Should this be able to stop the saving without throwing an error?
1683
1683
  that.issueEvent('saving', [document, options, creating], next);
@@ -1771,28 +1771,35 @@ Model.setMethod(function compose(data, options) {
1771
1771
  /**
1772
1772
  * Create a record in the database
1773
1773
  *
1774
- * @author Jelle De Loecker <jelle@develry.be>
1774
+ * @author Jelle De Loecker <jelle@elevenways.be>
1775
1775
  * @since 0.2.0
1776
- * @version 1.3.1
1776
+ * @version 1.3.20
1777
1777
  *
1778
- * @param {Object} data The record data to check
1778
+ * @param {Document} document
1779
1779
  * @param {Object} options
1780
1780
  * @param {Function} callback
1781
1781
  */
1782
- Model.setMethod(function createRecord(data, options, callback) {
1782
+ Model.setMethod(function createRecord(document, options, callback) {
1783
1783
 
1784
1784
  const that = this;
1785
1785
 
1786
1786
  // Normalize & clone the data, set default values, ...
1787
- data = this.compose(data, options);
1787
+ let data = this.compose(document, options);
1788
1788
 
1789
- Function.series(function validate(next) {
1789
+ // Turn it into a new document
1790
+ document = this.createDocument(data);
1791
+
1792
+ Function.series(function doBeforeValidate(next) {
1793
+ that.callOrNext('beforeValidate', [document, options], next);
1794
+ }, function validate(next) {
1790
1795
 
1791
1796
  if (options.validate === false || !that.schema) {
1792
1797
  return next();
1793
1798
  }
1794
1799
 
1795
- Pledge.done(that.schema.validate(that.createDocument(data)), next);
1800
+ Pledge.done(that.schema.validate(document), next);
1801
+ }, function doBeforeSave(next) {
1802
+ that.callOrNext('beforeSave', [document, options], next);
1796
1803
  }, function done(err) {
1797
1804
 
1798
1805
  if (err) {
@@ -1819,29 +1826,36 @@ Model.setMethod(function createRecord(data, options, callback) {
1819
1826
  /**
1820
1827
  * Update a record in the database
1821
1828
  *
1822
- * @author Jelle De Loecker <jelle@develry.be>
1829
+ * @author Jelle De Loecker <jelle@elevenways.be>
1823
1830
  * @since 0.2.0
1824
- * @version 1.3.1
1831
+ * @version 1.3.20
1825
1832
  *
1826
- * @param {Object} data The record data to check
1833
+ * @param {Document} document
1827
1834
  * @param {Object} options
1828
1835
  * @param {Function} callback
1829
1836
  */
1830
- Model.setMethod(function updateRecord(data, options, callback) {
1837
+ Model.setMethod(function updateRecord(document, options, callback) {
1831
1838
 
1832
1839
  const that = this;
1833
1840
 
1834
1841
  // Normalize the data, but no default values should be set (skip non present items)
1835
- data = this.compose(data, Object.assign({update: true}, options));
1842
+ let data = this.compose(document, Object.assign({update: true}, options));
1843
+
1844
+ // Turn it into a new document
1845
+ document = this.createDocument(data);
1836
1846
 
1837
- Function.series(function validate(next) {
1847
+ Function.series(function doBeforeValidate(next) {
1848
+ that.callOrNext('beforeValidate', [document, options], next);
1849
+ }, function validate(next) {
1838
1850
 
1839
1851
  if (options.validate === false || !that.schema) {
1840
1852
  return next();
1841
1853
  }
1842
1854
 
1843
- Pledge.done(that.schema.validate(that.createDocument(data)), next);
1855
+ Pledge.done(that.schema.validate(document), next);
1844
1856
 
1857
+ }, function doBeforeSave(next) {
1858
+ that.callOrNext('beforeSave', [document, options], next);
1845
1859
  }, function done(err) {
1846
1860
 
1847
1861
  if (err) {
File without changes
package/lib/bootstrap.js CHANGED
File without changes
@@ -116,6 +116,86 @@ Datasource.setMethod(function allows(name) {
116
116
  return true;
117
117
  });
118
118
 
119
+ /**
120
+ * Convert the given value to a BigInt (for use in JS)
121
+ *
122
+ * @author Jelle De Loecker <jelle@elevenways.be>
123
+ * @since 1.3.20
124
+ * @version 1.3.20
125
+ *
126
+ * @param {*} value
127
+ *
128
+ * @return {BigInt}
129
+ */
130
+ Datasource.setMethod(function castToBigInt(value) {
131
+
132
+ if (value == null) {
133
+ return value;
134
+ }
135
+
136
+ return BigInt(value);
137
+ });
138
+
139
+ /**
140
+ * Convert the given value from a BigInt (for use in DB)
141
+ *
142
+ * @author Jelle De Loecker <jelle@elevenways.be>
143
+ * @since 1.3.20
144
+ * @version 1.3.20
145
+ *
146
+ * @param {BigInt} value
147
+ *
148
+ * @return {BigInt}
149
+ */
150
+ Datasource.setMethod(function convertBigIntForDatasource(value) {
151
+
152
+ if (value == null) {
153
+ return value;
154
+ }
155
+
156
+ return BigInt(value);
157
+ });
158
+
159
+ /**
160
+ * Convert the given value to a Decimal (for use in JS)
161
+ *
162
+ * @author Jelle De Loecker <jelle@elevenways.be>
163
+ * @since 1.3.20
164
+ * @version 1.3.20
165
+ *
166
+ * @param {*} value
167
+ *
168
+ * @return {BigInt}
169
+ */
170
+ Datasource.setMethod(function castToDecimal(value) {
171
+
172
+ if (value == null) {
173
+ return value;
174
+ }
175
+
176
+ return new Blast.Classes.Develry.Decimal(value);
177
+ });
178
+
179
+ /**
180
+ * Convert the given decimal for use in DB
181
+ *
182
+ * @author Jelle De Loecker <jelle@elevenways.be>
183
+ * @since 1.3.20
184
+ * @version 1.3.20
185
+ *
186
+ * @param {Decimal|String} value
187
+ *
188
+ * @return {Decimal}
189
+ */
190
+ Datasource.setMethod(function convertDecimalForDatasource(value) {
191
+
192
+ if (value == null) {
193
+ return value;
194
+ }
195
+
196
+ return new Blast.Classes.Develry.Decimal(value);
197
+ });
198
+
119
199
  /**
120
200
  * Hash a string synchronously
121
201
  *
File without changes
@@ -845,7 +845,7 @@ Model.setMethod(function ensureIds(list, callback) {
845
845
  *
846
846
  * @author Jelle De Loecker <jelle@develry.be>
847
847
  * @since 0.0.1
848
- * @version 1.3.16
848
+ * @version 1.3.20
849
849
  *
850
850
  * @param {Document} document
851
851
  * @param {Object} options
@@ -895,21 +895,8 @@ Model.setMethod(function saveRecord(document, options, callback) {
895
895
  creating = options.create || doc[that.primary_key] == null;
896
896
  next();
897
897
  });
898
- }, function doBeforeSave(next) {
899
-
900
- if (typeof that.beforeSave == 'function') {
901
- let promise = that.beforeSave(document, options, next);
902
-
903
- if (promise) {
904
- Pledge.done(promise, next);
905
- } else if (that.beforeSave.length < 3) {
906
- // If the method accepts no `next` callback, call it now
907
- next();
908
- }
909
- } else {
910
- next();
911
- }
912
-
898
+ }, function doBeforeNormalize(next) {
899
+ that.callOrNext('beforeNormalize', [document, options], next);
913
900
  }, function emitSavingEvent(next) {
914
901
  that.emit('saving', document, options, creating, function afterSavingEvent(err, stopped) {
915
902
  return next(err);
@@ -4,14 +4,18 @@
4
4
  *
5
5
  * @constructor
6
6
  *
7
- * @author Jelle De Loecker <jelle@develry.be>
7
+ * @author Jelle De Loecker <jelle@elevenways.be>
8
8
  * @since 1.1.0
9
- * @version 1.1.0
9
+ * @version 1.3.20
10
10
  *
11
11
  * @param {String} path
12
12
  */
13
13
  const PathEvaluator = Function.inherits(null, 'Alchemy', function PathEvaluator(path) {
14
14
 
15
+ if (!(this instanceof PathEvaluator)) {
16
+ return new PathEvaluator(path);
17
+ }
18
+
15
19
  if (typeof path == 'string') {
16
20
  path = path.split('.');
17
21
  }
@@ -22,7 +26,7 @@ const PathEvaluator = Function.inherits(null, 'Alchemy', function PathEvaluator(
22
26
  /**
23
27
  * Undry the value
24
28
  *
25
- * @author Jelle De Loecker <jelle@develry.be>
29
+ * @author Jelle De Loecker <jelle@elevenways.be>
26
30
  * @since 1.1.0
27
31
  * @version 1.1.0
28
32
  */
@@ -33,7 +37,7 @@ PathEvaluator.setStatic(function unDry(value) {
33
37
  /**
34
38
  * Create a dry object
35
39
  *
36
- * @author Jelle De Loecker <jelle@develry.be>
40
+ * @author Jelle De Loecker <jelle@elevenways.be>
37
41
  * @since 1.1.0
38
42
  * @version 1.1.0
39
43
  */
@@ -48,14 +52,17 @@ PathEvaluator.setMethod(function toDry() {
48
52
  /**
49
53
  * Get the actual value
50
54
  *
51
- * @author Jelle De Loecker <jelle@develry.be>
55
+ * @author Jelle De Loecker <jelle@elevenways.be>
52
56
  * @since 1.1.0
53
- * @version 1.1.0
57
+ * @version 1.3.20
54
58
  */
55
- PathEvaluator.setMethod(function getValue() {
59
+ PathEvaluator.setMethod(function getValue(context) {
60
+
61
+ if (arguments.length == 0) {
62
+ context = Blast.Globals;
63
+ }
56
64
 
57
- var context = Blast.Globals,
58
- result = Object.path(context, this.path);
65
+ let result = Object.path(context, this.path);
59
66
 
60
67
  if (typeof result == 'function') {
61
68
  result = result();
File without changes
File without changes
File without changes
package/lib/class/task.js CHANGED
File without changes
@@ -105,7 +105,7 @@ Service.setMethod(function checksumSystemSchedule(type_path, cron, settings) {
105
105
  *
106
106
  * @author Jelle De Loecker <jelle@elevenways.be>
107
107
  * @since 1.3.17
108
- * @version 1.3.17
108
+ * @version 1.3.20
109
109
  */
110
110
  Service.setMethod(async function initSchedules() {
111
111
 
@@ -179,6 +179,7 @@ Service.setMethod(async function initSchedules() {
179
179
  // If this checksum has already been seen, also remove it
180
180
  if (existing_system_records.has(checksum)) {
181
181
  await record.remove();
182
+ continue;
182
183
  }
183
184
 
184
185
  // Update the record
@@ -188,6 +189,8 @@ Service.setMethod(async function initSchedules() {
188
189
 
189
190
  await record.save();
190
191
 
192
+ existing_system_records.set(checksum, record);
193
+
191
194
  } catch (err) {
192
195
  alchemy.registerError(err);
193
196
  }
@@ -526,12 +529,15 @@ class TaskSchedules {
526
529
  *
527
530
  * @author Jelle De Loecker <jelle@elevenways.be>
528
531
  * @since 1.3.17
529
- * @version 1.3.17
532
+ * @version 1.3.20
530
533
  */
531
- #startFromMenu(task_record) {
534
+ async #startFromMenu(task_record) {
532
535
  console.log('Manually starting', task_record.type, 'task:', task_record);
533
536
  let schedule = new TaskSchedule(this, null, task_record.settings, task_record);
534
- schedule.startManually();
537
+ let task_instance = await schedule.startManually();
538
+ console.log(' -- Started:', task_instance);
539
+
540
+ return task_instance;
535
541
  }
536
542
 
537
543
  /**
@@ -579,6 +585,7 @@ class TaskSchedule {
579
585
  janeway_menu_item = null;
580
586
 
581
587
  #change_counter = 0;
588
+ #instance_pledge = null;
582
589
 
583
590
  /**
584
591
  * Initialize the instance
@@ -845,10 +852,22 @@ class TaskSchedule {
845
852
  *
846
853
  * @author Jelle De Loecker <jelle@elevenways.be>
847
854
  * @since 1.3.17
848
- * @version 1.3.17
855
+ * @version 1.3.20
856
+ *
857
+ * @return {Task}
849
858
  */
850
859
  async startManually() {
851
- return this.#run(true);
860
+
861
+ if (this.is_running) {
862
+ return false;
863
+ }
864
+
865
+ let pledge = new Pledge();
866
+ this.#instance_pledge = pledge;
867
+
868
+ this.#run(true);
869
+
870
+ return pledge;
852
871
  }
853
872
 
854
873
  /**
@@ -856,7 +875,7 @@ class TaskSchedule {
856
875
  *
857
876
  * @author Jelle De Loecker <jelle@elevenways.be>
858
877
  * @since 1.3.17
859
- * @version 1.3.17
878
+ * @version 1.3.20
860
879
  */
861
880
  async #run(started_manually = false) {
862
881
 
@@ -889,6 +908,12 @@ class TaskSchedule {
889
908
  if (started_manually || await this.ownsScheduledTask()) {
890
909
  let instance = this.createInstance();
891
910
  this.task_instance = instance;
911
+
912
+ if (this.#instance_pledge) {
913
+ this.#instance_pledge.resolve(instance);
914
+ this.#instance_pledge = null;
915
+ }
916
+
892
917
  await instance.start();
893
918
  }
894
919
  } catch (err) {
package/lib/core/base.js CHANGED
@@ -471,7 +471,11 @@ Base.setMethod(function getClassPathAfter(after) {
471
471
  *
472
472
  * @author Jelle De Loecker <jelle@elevenways.be>
473
473
  * @since 1.0.0
474
- * @version 1.2.2
474
+ * @version 1.3.20
475
+ *
476
+ * @param {String} name The name of the method to call
477
+ * @param {Array} args The arguments to pass
478
+ * @param {Function} next The callback to call afterwards
475
479
  */
476
480
  Base.setMethod(function callOrNext(name, args, next) {
477
481
 
@@ -480,14 +484,25 @@ Base.setMethod(function callOrNext(name, args, next) {
480
484
  args = [];
481
485
  }
482
486
 
483
- if (typeof this[name] == 'function') {
487
+ const fnc = this[name];
488
+
489
+ if (typeof fnc == 'function') {
490
+
491
+ let has_callback = false,
492
+ promise;
484
493
 
485
- if (next) {
494
+ if (next && fnc.length > args.length) {
495
+ has_callback = true;
486
496
  args.push(next);
487
497
  }
488
498
 
489
499
  try {
490
- this[name].apply(this, args);
500
+ promise = this[name].apply(this, args);
501
+
502
+ if (!has_callback) {
503
+ Pledge.done(promise, next);
504
+ }
505
+
491
506
  } catch (err) {
492
507
  next(err);
493
508
  }
@@ -635,6 +635,13 @@ function getRoutesDict() {
635
635
 
636
636
  // From here on, only client-side code is added
637
637
  if (Blast.isBrowser) {
638
+ window.LocalDateTime = Blast.Classes.Develry.LocalDateTime;
639
+ window.LocalDate = Blast.Classes.Develry.LocalDate;
640
+ window.LocalTime = Blast.Classes.Develry.LocalTime;
641
+ window.Decimal = Blast.Classes.Develry.Decimal;
642
+ window.MutableDecimal = Blast.Classes.Develry.MutableDecimal;
643
+ window.FixedDecimal = Blast.Classes.Develry.FixedDecimal;
644
+ window.MutableFixedDecimal = Blast.Classes.Develry.MutableFixedDecimal;
638
645
  window.alchemy = new Alchemy();
639
646
  } else {
640
647
  return;
File without changes
@@ -59,6 +59,29 @@ DEFINE('Classes', Blast.Classes);
59
59
  */
60
60
  DEFINE('Types', Blast.Types);
61
61
 
62
+ /**
63
+ * The new Local Date/Time classes
64
+ *
65
+ * @author Jelle De Loecker <jelle@elevenways.be>
66
+ * @since 1.3.20
67
+ * @version 1.3.20
68
+ */
69
+ DEFINE('LocalDateTime', Blast.Classes.Develry.LocalDateTime);
70
+ DEFINE('LocalDate', Blast.Classes.Develry.LocalDate);
71
+ DEFINE('LocalTime', Blast.Classes.Develry.LocalTime);
72
+
73
+ /**
74
+ * The new Decimal classes
75
+ *
76
+ * @author Jelle De Loecker <jelle@elevenways.be>
77
+ * @since 1.3.20
78
+ * @version 1.3.20
79
+ */
80
+ DEFINE('Decimal', Blast.Classes.Develry.Decimal);
81
+ DEFINE('MutableDecimal', Blast.Classes.Develry.MutableDecimal);
82
+ DEFINE('FixedDecimal', Blast.Classes.Develry.FixedDecimal);
83
+ DEFINE('MutableFixedDecimal', Blast.Classes.Develry.MutableFixedDecimal);
84
+
62
85
  /**
63
86
  * Path to the directory of the server.js file
64
87
  *
File without changes
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "alchemymvc",
3
3
  "description": "MVC framework for Node.js",
4
- "version": "1.3.19",
4
+ "version": "1.3.20",
5
5
  "author": "Jelle De Loecker <jelle@elevenways.be>",
6
6
  "keywords": [
7
7
  "alchemy",
@@ -31,7 +31,7 @@
31
31
  "mongodb" : "~6.1.0",
32
32
  "ncp" : "~2.0.0",
33
33
  "postcss" : "~8.4.31",
34
- "protoblast" : "~0.8.12",
34
+ "protoblast" : "~0.8.14",
35
35
  "semver" : "~7.5.4",
36
36
  "socket.io" : "~4.7.2",
37
37
  "@11ways/socket.io-stream" : "~0.9.2",