alchemymvc 1.4.0-alpha.9 → 1.4.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/revision_behaviour.js +1 -1
- package/lib/app/behaviour/sluggable_behaviour.js +74 -3
- package/lib/app/conduit/electron_conduit.js +1 -1
- package/lib/app/conduit/socket_conduit.js +62 -8
- package/lib/app/controller/alchemy_info_controller.js +2 -2
- package/lib/app/datasource/mongo_datasource.js +103 -107
- package/lib/app/element/al_time.js +126 -0
- package/lib/app/helper/cron.js +2 -2
- package/lib/app/helper/enum_values.js +9 -0
- package/lib/app/helper/router_helper.js +37 -5
- package/lib/app/helper/socket_helper.js +71 -14
- package/lib/app/helper/syncable.js +253 -30
- package/lib/app/helper_datasource/00-nosql_datasource.js +410 -54
- package/lib/app/helper_datasource/05-fallback_datasource.js +7 -2
- package/lib/app/helper_datasource/idb_datasource.js +82 -64
- package/lib/app/helper_datasource/indexed_db.js +41 -33
- package/lib/app/helper_datasource/read_operational_context.js +0 -17
- package/lib/app/helper_datasource/remote_datasource.js +1 -1
- package/lib/app/helper_field/00-objectid_field.js +16 -0
- package/lib/app/helper_field/11-date_field.js +13 -0
- package/lib/app/helper_field/datetime_field.js +13 -0
- package/lib/app/helper_field/enum_field.js +27 -0
- package/lib/app/helper_field/geopoint_field.js +61 -0
- package/lib/app/helper_field/html_field.js +14 -1
- package/lib/app/helper_field/integer_field.js +14 -0
- package/lib/app/helper_field/local_date_field.js +13 -0
- package/lib/app/helper_field/local_date_time_field.js +13 -0
- package/lib/app/helper_field/local_time_field.js +13 -0
- package/lib/app/helper_field/password_field.js +28 -2
- package/lib/app/helper_field/schema_field.js +88 -4
- package/lib/app/helper_field/url_field.js +22 -0
- package/lib/app/helper_model/00-base_criteria.js +81 -6
- package/lib/app/helper_model/05-criteria_expressions.js +53 -11
- package/lib/app/helper_model/10-model_criteria.js +82 -22
- package/lib/app/helper_model/document.js +23 -9
- package/lib/app/helper_model/field_config.js +9 -2
- package/lib/app/helper_model/model.js +15 -11
- package/lib/app/model/system_task_history_model.js +11 -1
- package/lib/class/conduit.js +116 -13
- package/lib/class/controller.js +1 -0
- package/lib/class/datasource.js +30 -2
- package/lib/class/document.js +40 -12
- package/lib/class/field.js +170 -7
- package/lib/class/import_stream_parser.js +299 -0
- package/lib/class/inode_file.js +2 -2
- package/lib/class/migration.js +5 -2
- package/lib/class/model.js +22 -151
- package/lib/class/operational_context.js +37 -0
- package/lib/class/plugin.js +32 -3
- package/lib/class/route.js +34 -3
- package/lib/class/router.js +38 -34
- package/lib/class/schema.js +1 -1
- package/lib/class/schema_client.js +172 -14
- package/lib/class/session.js +1 -1
- package/lib/class/sitemap.js +2 -2
- package/lib/class/task.js +30 -1
- package/lib/class/task_service.js +83 -14
- package/lib/core/alchemy.js +188 -166
- package/lib/core/alchemy_functions.js +7 -11
- package/lib/core/alchemy_load_functions.js +101 -10
- package/lib/core/base.js +2 -2
- package/lib/core/client_alchemy.js +9 -1
- package/lib/core/middleware.js +83 -25
- package/lib/core/setting.js +86 -14
- package/lib/scripts/create_constants.js +5 -1
- package/lib/scripts/create_settings.js +44 -0
- package/lib/scripts/setup_ai_devmode.js +324 -0
- package/lib/scripts/setup_devwatch.js +0 -0
- package/lib/stages/00-load_core.js +9 -3
- package/lib/stages/50-routes.js +6 -1
- package/lib/stages/90-server.js +0 -1
- package/lib/testing/browser.js +1164 -0
- package/lib/testing/harness.js +840 -0
- package/package.json +26 -19
- package/testing/browser.js +27 -0
- package/testing.js +37 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
const DATETIME = Symbol('datetime');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The custom al-time element
|
|
5
|
+
*
|
|
6
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
7
|
+
* @since 1.4.0
|
|
8
|
+
* @version 1.4.0
|
|
9
|
+
*/
|
|
10
|
+
const AlTime = Function.inherits('Alchemy.Element', 'AlTime');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The preferred format
|
|
14
|
+
*
|
|
15
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
16
|
+
* @since 0.3.0
|
|
17
|
+
* @version 0.3.0
|
|
18
|
+
*/
|
|
19
|
+
AlTime.setAttribute('format', null, function setFormat(format) {
|
|
20
|
+
this._populate({value: this.datetime, format: format});
|
|
21
|
+
return format;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Get/set the datetime value
|
|
26
|
+
*
|
|
27
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
28
|
+
* @since 0.3.0
|
|
29
|
+
* @version 0.3.0
|
|
30
|
+
*/
|
|
31
|
+
AlTime.setAttribute('datetime', null, function setDatetime(value) {
|
|
32
|
+
this._populate({value: value});
|
|
33
|
+
return this[DATETIME];
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Set the value with a function call
|
|
38
|
+
*
|
|
39
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
40
|
+
* @since 0.3.0
|
|
41
|
+
* @version 0.3.0
|
|
42
|
+
*/
|
|
43
|
+
AlTime.setMethod(function setDatetime(value) {
|
|
44
|
+
this.datetime = value;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Populate the element
|
|
49
|
+
*
|
|
50
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
51
|
+
* @since 0.3.0
|
|
52
|
+
* @version 0.3.0
|
|
53
|
+
*
|
|
54
|
+
* @return {Object[]}
|
|
55
|
+
*/
|
|
56
|
+
AlTime.setMethod(function populate() {
|
|
57
|
+
let iso_string = this._populate({value: this.value, format: this.format});
|
|
58
|
+
this[DATETIME] = iso_string;
|
|
59
|
+
return iso_string;
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Populate the element
|
|
64
|
+
*
|
|
65
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
66
|
+
* @since 0.3.0
|
|
67
|
+
* @version 0.3.0
|
|
68
|
+
*/
|
|
69
|
+
AlTime.setMethod(function _populate(input) {
|
|
70
|
+
|
|
71
|
+
if (!input) {
|
|
72
|
+
input = {};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
let {value, format} = input;
|
|
76
|
+
|
|
77
|
+
if (value == null) {
|
|
78
|
+
value = this[DATETIME];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (format == null) {
|
|
82
|
+
format = this.format;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let iso_date,
|
|
86
|
+
date;
|
|
87
|
+
|
|
88
|
+
if (value) {
|
|
89
|
+
date = Date.create(value);
|
|
90
|
+
iso_date = date.toISOString();
|
|
91
|
+
} else {
|
|
92
|
+
iso_date = '';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
value = iso_date;
|
|
96
|
+
|
|
97
|
+
if (Blast.isServer) {
|
|
98
|
+
return value;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
let target_element = this.children?.[0];
|
|
102
|
+
|
|
103
|
+
if (!target_element) {
|
|
104
|
+
target_element = this.createElement('time');
|
|
105
|
+
this.append(target_element);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let tag_name = target_element.tagName;
|
|
109
|
+
let formatted;
|
|
110
|
+
|
|
111
|
+
if (format) {
|
|
112
|
+
formatted = date.format(format);
|
|
113
|
+
} else {
|
|
114
|
+
formatted = date + '';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (tag_name == 'INPUT') {
|
|
118
|
+
target_element.value = formatted;
|
|
119
|
+
} else {
|
|
120
|
+
if (tag_name == 'TIME') {
|
|
121
|
+
target_element.setAttribute('datetime', iso_date);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
target_element.textContent = formatted;
|
|
125
|
+
}
|
|
126
|
+
});
|
package/lib/app/helper/cron.js
CHANGED
|
@@ -284,7 +284,7 @@ function splitAndCleanup(input, separator) {
|
|
|
284
284
|
* @author Santhosh Kumar <brsanthu@gmail.com>
|
|
285
285
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
286
286
|
* @since 1.3.17
|
|
287
|
-
* @version 1.
|
|
287
|
+
* @version 1.4.1
|
|
288
288
|
*/
|
|
289
289
|
CronExpression.setMethod(function parse() {
|
|
290
290
|
|
|
@@ -296,7 +296,7 @@ CronExpression.setMethod(function parse() {
|
|
|
296
296
|
let has_seconds = this.has_seconds;
|
|
297
297
|
|
|
298
298
|
if (Cron.PREDEFINED_EXPRESSIONS[internal_expression]) {
|
|
299
|
-
internal_expression = Cron.PREDEFINED_EXPRESSIONS[
|
|
299
|
+
internal_expression = Cron.PREDEFINED_EXPRESSIONS[internal_expression];
|
|
300
300
|
has_seconds = false;
|
|
301
301
|
}
|
|
302
302
|
|
|
@@ -70,17 +70,23 @@ EnumMap.setMethod(function set(name, value) {
|
|
|
70
70
|
result = {
|
|
71
71
|
name : value.name,
|
|
72
72
|
title : value.title,
|
|
73
|
+
short_title : value.short_title,
|
|
73
74
|
};
|
|
74
75
|
} else {
|
|
75
76
|
result = {
|
|
76
77
|
name : value.name,
|
|
77
78
|
title : value.title || value.name,
|
|
79
|
+
short_title : value.short_title,
|
|
78
80
|
};
|
|
79
81
|
|
|
80
82
|
if (value.icon) {
|
|
81
83
|
result.icon = value.icon;
|
|
82
84
|
}
|
|
83
85
|
|
|
86
|
+
if (value.icon_style) {
|
|
87
|
+
result.icon_style = value.icon_style;
|
|
88
|
+
}
|
|
89
|
+
|
|
84
90
|
if (value.color) {
|
|
85
91
|
result.color = value.color;
|
|
86
92
|
}
|
|
@@ -96,6 +102,9 @@ EnumMap.setMethod(function set(name, value) {
|
|
|
96
102
|
}
|
|
97
103
|
|
|
98
104
|
result.number = this.local.size + 1;
|
|
105
|
+
|
|
106
|
+
// "value" does not mean the value that is stored in the database
|
|
107
|
+
// it can be the function that houses info
|
|
99
108
|
result.value = value;
|
|
100
109
|
result.is_enumified = true;
|
|
101
110
|
|
|
@@ -29,14 +29,26 @@ var Route = Function.inherits('Alchemy.Helper.Router', function Route(renderer)
|
|
|
29
29
|
*
|
|
30
30
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
31
31
|
* @since 1.1.0
|
|
32
|
-
* @version 1.
|
|
32
|
+
* @version 1.4.0
|
|
33
33
|
*
|
|
34
34
|
* @type {RURL}
|
|
35
35
|
*/
|
|
36
36
|
Router.setProperty(function current_url() {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
|
|
38
|
+
let renderer = this.view_render;
|
|
39
|
+
|
|
40
|
+
if (renderer?.variables) {
|
|
41
|
+
try {
|
|
42
|
+
let url = renderer.variables.get('__url');
|
|
43
|
+
if (url) {
|
|
44
|
+
return url.clone();
|
|
45
|
+
}
|
|
46
|
+
} catch (err) {
|
|
47
|
+
// Ignore
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (Blast.isBrowser) {
|
|
40
52
|
|
|
41
53
|
if (hawkejs?.scene?.opening_url?.url) {
|
|
42
54
|
return Blast.Classes.RURL.parse(hawkejs.scene.opening_url.url);
|
|
@@ -270,7 +282,27 @@ Router.setMethod(function applyDirective(element, name, options) {
|
|
|
270
282
|
}
|
|
271
283
|
|
|
272
284
|
if (method_attribute && config.methods?.[0]) {
|
|
273
|
-
element.
|
|
285
|
+
let existing_method = element.getAttribute(method_attribute);
|
|
286
|
+
|
|
287
|
+
// Assume the user knows what they're doing when there is an existing method
|
|
288
|
+
if (!existing_method) {
|
|
289
|
+
let found_method = false;
|
|
290
|
+
|
|
291
|
+
// There is no method, look for the best one (we prefer a post)
|
|
292
|
+
for (let method of config.methods) {
|
|
293
|
+
method = method.toLowerCase();
|
|
294
|
+
|
|
295
|
+
if (method == 'post' || method == 'put') {
|
|
296
|
+
element.setAttribute(method_attribute, method);
|
|
297
|
+
found_method = true;
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (!found_method) {
|
|
303
|
+
element.setAttribute(method_attribute, config.methods[0]);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
274
306
|
}
|
|
275
307
|
|
|
276
308
|
if (disable_ajax) {
|
|
@@ -16,7 +16,7 @@ function isStream(obj) {
|
|
|
16
16
|
*
|
|
17
17
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
18
18
|
* @since 0.2.0
|
|
19
|
-
* @version 1.
|
|
19
|
+
* @version 1.4.0
|
|
20
20
|
*
|
|
21
21
|
* @param {string} type
|
|
22
22
|
*/
|
|
@@ -43,12 +43,21 @@ var Linkup = Blast.Collection.Function.inherits('Informer', function ClientLinku
|
|
|
43
43
|
// The initial submitted data
|
|
44
44
|
this.initialData = data;
|
|
45
45
|
|
|
46
|
+
// Has this linkup been destroyed?
|
|
47
|
+
this.destroyed = false;
|
|
48
|
+
|
|
46
49
|
// Make the linkup store itself
|
|
47
50
|
client.linkups[this.id] = this;
|
|
48
51
|
|
|
49
52
|
// The parent server
|
|
50
53
|
this.client = client;
|
|
51
54
|
|
|
55
|
+
// Listen for socket disconnection
|
|
56
|
+
this._onClientClose = () => {
|
|
57
|
+
this._markDisconnected();
|
|
58
|
+
};
|
|
59
|
+
client.on('close', this._onClientClose);
|
|
60
|
+
|
|
52
61
|
if (server_object) {
|
|
53
62
|
this.submit('ready');
|
|
54
63
|
} else {
|
|
@@ -146,23 +155,51 @@ Linkup.setMethod(function createStream() {
|
|
|
146
155
|
*
|
|
147
156
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
148
157
|
* @since 0.2.0
|
|
149
|
-
* @version 1.
|
|
158
|
+
* @version 1.4.0
|
|
150
159
|
*/
|
|
151
160
|
Linkup.setMethod(function destroy() {
|
|
161
|
+
if (this.destroyed) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
152
165
|
this.submit('__destroy__');
|
|
153
166
|
this._destroy();
|
|
154
167
|
this.removeAllListeners();
|
|
155
168
|
});
|
|
156
169
|
|
|
170
|
+
/**
|
|
171
|
+
* Mark the linkup as disconnected due to socket close
|
|
172
|
+
*
|
|
173
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
174
|
+
* @since 1.4.0
|
|
175
|
+
* @version 1.4.0
|
|
176
|
+
*/
|
|
177
|
+
Linkup.setMethod(function _markDisconnected() {
|
|
178
|
+
if (this.destroyed) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Emit the close event so listeners can handle disconnect
|
|
183
|
+
this.emit('close');
|
|
184
|
+
});
|
|
185
|
+
|
|
157
186
|
/**
|
|
158
187
|
* Make sure the linkup is removed
|
|
159
188
|
*
|
|
160
189
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
161
190
|
* @since 1.3.10
|
|
162
|
-
* @version 1.
|
|
191
|
+
* @version 1.4.0
|
|
163
192
|
*/
|
|
164
193
|
Linkup.setMethod(function _destroy() {
|
|
194
|
+
this.destroyed = true;
|
|
165
195
|
delete this.client.linkups[this.id];
|
|
196
|
+
|
|
197
|
+
// Remove the close listener from the client
|
|
198
|
+
if (this._onClientClose) {
|
|
199
|
+
this.client.removeListener('close', this._onClientClose);
|
|
200
|
+
this._onClientClose = null;
|
|
201
|
+
}
|
|
202
|
+
|
|
166
203
|
this.emit('destroyed');
|
|
167
204
|
});
|
|
168
205
|
|
|
@@ -352,7 +389,7 @@ Client.setMethod(function createStream() {
|
|
|
352
389
|
*
|
|
353
390
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
354
391
|
* @since 0.2.0
|
|
355
|
-
* @version 1.
|
|
392
|
+
* @version 1.4.0
|
|
356
393
|
*
|
|
357
394
|
* @param {Function} callback
|
|
358
395
|
*/
|
|
@@ -487,7 +524,8 @@ Client.setMethod(function connect(address, data, callback) {
|
|
|
487
524
|
});
|
|
488
525
|
|
|
489
526
|
server.on('error', function(err) {
|
|
490
|
-
|
|
527
|
+
log.error('Socket error:', err);
|
|
528
|
+
that.emit('error', err);
|
|
491
529
|
});
|
|
492
530
|
|
|
493
531
|
// Listen for cookies
|
|
@@ -547,16 +585,22 @@ Client.setMethod(function connect(address, data, callback) {
|
|
|
547
585
|
}
|
|
548
586
|
}
|
|
549
587
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
}
|
|
554
|
-
} catch (err) {
|
|
555
|
-
console.log('ERROR UNDRYING PACKET:', err, packet);
|
|
556
|
-
return;
|
|
588
|
+
try {
|
|
589
|
+
if (packet.data && typeof packet.data == 'object') {
|
|
590
|
+
packet.data = JSON.undry(packet.data);
|
|
557
591
|
}
|
|
592
|
+
} catch (err) {
|
|
593
|
+
log.error('Error undrying response data:', err);
|
|
558
594
|
|
|
559
|
-
|
|
595
|
+
// Still call the callback with an error instead of silently dropping
|
|
596
|
+
let undry_error = new Error('Failed to deserialize response data: ' + err.message);
|
|
597
|
+
undry_error.cause = err;
|
|
598
|
+
that.callbacks[packet.respond_to](undry_error, null);
|
|
599
|
+
delete that.callbacks[packet.respond_to];
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
if (packet.noData) {
|
|
560
604
|
that.callbacks[packet.respond_to](packet.err, packet.stream);
|
|
561
605
|
} else if (packet.stream) {
|
|
562
606
|
that.callbacks[packet.respond_to](packet.err, packet.data, packet.stream);
|
|
@@ -578,7 +622,20 @@ Client.setMethod(function connect(address, data, callback) {
|
|
|
578
622
|
packet.data = JSON.undry(packet.data);
|
|
579
623
|
}
|
|
580
624
|
} catch (err) {
|
|
581
|
-
|
|
625
|
+
log.error('Error undrying packet data:', err);
|
|
626
|
+
|
|
627
|
+
// If the sender expects a response, send back an error
|
|
628
|
+
if (packet.respond) {
|
|
629
|
+
let response_packet = {
|
|
630
|
+
err: new Error('Failed to deserialize packet data: ' + err.message),
|
|
631
|
+
respond_to: packet.id,
|
|
632
|
+
data: null
|
|
633
|
+
};
|
|
634
|
+
server.emit('response', response_packet);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// Emit error event so callers can be aware
|
|
638
|
+
that.emit('packet_error', err, packet);
|
|
582
639
|
return;
|
|
583
640
|
}
|
|
584
641
|
|