alchemymvc 1.2.3 → 1.2.4

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.
@@ -1,18 +1,20 @@
1
- var bcrypt = alchemy.use('bcrypt'),
2
- regex = /^\$2[ayb]\$/;
3
-
4
1
  /**
5
2
  * The Password Field class
6
3
  *
7
4
  * @constructor
8
5
  *
9
- * @author Jelle De Loecker <jelle@develry.be>
6
+ * @author Jelle De Loecker <jelle@elevenways.be>
10
7
  * @since 0.2.0
11
- * @version 1.1.0
8
+ * @version 1.2.3
12
9
  */
13
- var Password = Function.inherits('Alchemy.Field', function Password(schema, name, options) {
14
- Password.super.call(this, schema, name, options);
15
- });
10
+ const Password = Function.inherits('Alchemy.Field', 'Password');
11
+
12
+ if (Blast.isBrowser) {
13
+ return true;
14
+ }
15
+
16
+ const bcrypt = alchemy.use('bcrypt'),
17
+ regex = /^\$2[ayb]\$/;
16
18
 
17
19
  /**
18
20
  * Make sure the password is hashed before storing it
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * @author Jelle De Loecker <jelle@develry.be>
7
7
  * @since 0.2.0
8
- * @version 1.1.3
8
+ * @version 1.2.4
9
9
  */
10
10
  var SchemaField = Function.inherits('Alchemy.Field', function Schema(schema, name, options) {
11
11
  Schema.super.call(this, schema, name, options);
@@ -21,18 +21,20 @@ var SchemaField = Function.inherits('Alchemy.Field', function Schema(schema, nam
21
21
  this.field_schema.setName(name);
22
22
  }
23
23
 
24
- if (!this.field_schema.parent || schema.root_schema == this.field_schema.parent) {
25
- this.field_schema.setParent(schema);
26
- } else {
27
- // It's possible the given schema already had the correct parent
28
- // But if that's not the case, throw an error
29
- if (this.field_schema.parent != schema) {
30
- throw new Error('Unable to re-use a schema for field "' + name + '", use `schema.clone()`!');
24
+ if (schema) {
25
+ if (!this.field_schema.parent || schema.root_schema == this.field_schema.parent) {
26
+ this.field_schema.setParent(schema);
27
+ } else {
28
+ // It's possible the given schema already had the correct parent
29
+ // But if that's not the case, throw an error
30
+ if (this.field_schema.parent != schema) {
31
+ throw new Error('Unable to re-use a schema for field "' + name + '", use `schema.clone()`!');
32
+ }
31
33
  }
32
- }
33
34
 
34
- if (!this.field_schema.model_name) {
35
- this.field_schema.setModel(schema.model_name);
35
+ if (!this.field_schema.model_name) {
36
+ this.field_schema.setModel(schema.model_name);
37
+ }
36
38
  }
37
39
  });
38
40
 
@@ -242,7 +244,7 @@ SchemaField.setMethod(function _toDatasource(value, data, datasource, callback)
242
244
  *
243
245
  * @author Jelle De Loecker <jelle@develry.be>
244
246
  * @since 0.2.0
245
- * @version 1.1.0
247
+ * @version 1.2.4
246
248
  *
247
249
  * @param {Mixed} value
248
250
  * @param {Function} callback
@@ -262,6 +264,12 @@ SchemaField.setMethod(function _toApp(query, options, value, callback) {
262
264
  recursive = 1;
263
265
  }
264
266
 
267
+ if (recursive && Blast.isBrowser) {
268
+ // @TODO: this will mostly fail on the browser, so disable it for now.
269
+ // Maybe make it configurable later
270
+ recursive = 0;
271
+ }
272
+
265
273
  // Get associated records if the subschema has associations defined
266
274
  if (recursive && this.field_schema && !Object.isEmpty(this.field_schema.associations)) {
267
275
 
@@ -305,7 +305,7 @@ Document.setStatic(function getDocumentClass(model) {
305
305
  *
306
306
  * @author Jelle De Loecker <jelle@develry.be>
307
307
  * @since 1.0.0
308
- * @version 1.1.0
308
+ * @version 1.2.4
309
309
  *
310
310
  * @param {Object} obj
311
311
  * @param {Boolean|String} cloned
@@ -349,6 +349,25 @@ Document.setStatic(function unDry(obj, cloned) {
349
349
  obj.$options.model = null;
350
350
  }
351
351
 
352
+ if (Blast.isBrowser && obj.$options?.private_fields) {
353
+ let model = alchemy.getModel(obj.$model_name),
354
+ field;
355
+
356
+ for (field of obj.$options.private_fields) {
357
+
358
+ // @TODO: don't let documents undry before schema is ready?
359
+ if (!model.schema) {
360
+ continue;
361
+ }
362
+
363
+ if (model.schema.has(field.name)) {
364
+ continue;
365
+ }
366
+
367
+ model.schema.addField(field.name, field.constructor.type_name, field.options);
368
+ }
369
+ }
370
+
352
371
  DocClass.call(result, obj.$record, obj.$options);
353
372
 
354
373
  if (cloned || Blast.isNode || obj[Blast.Classes.IndexedDb.from_cache_symbol]) {
@@ -621,7 +640,7 @@ Document.setMethod(function toDry() {
621
640
  *
622
641
  * @author Jelle De Loecker <jelle@develry.be>
623
642
  * @since 1.0.4
624
- * @version 1.1.0
643
+ * @version 1.2.4
625
644
  *
626
645
  * @param {Object} record
627
646
  * @param {Object} options
@@ -662,13 +681,32 @@ Document.setMethod(function setDataRecord(record, options) {
662
681
  this.$record = record;
663
682
 
664
683
  if (Blast.isNode && this.constructor.namespace.indexOf('Alchemy.Document') == -1) {
665
- let field,
684
+ let delete_field = false,
685
+ added_private_field_info = false,
686
+ field,
666
687
  key;
667
688
 
668
689
  for (key in this.$main) {
669
690
  field = this.$model.schema.get(key);
670
691
 
671
- if (!field || field.is_private) {
692
+ if (!field) {
693
+ delete_field = true;
694
+ } else if (field.is_private) {
695
+ delete_field = true;
696
+
697
+ if (options.keep_private_fields) {
698
+ delete_field = false;
699
+
700
+ if (!added_private_field_info) {
701
+ added_private_field_info = true;
702
+
703
+ let fields = this.$model.schema.getPrivateFields();
704
+ options.private_fields = JSON.clone(fields, 'toHawkejs');
705
+ }
706
+ }
707
+ }
708
+
709
+ if (delete_field) {
672
710
  delete this.$main[key];
673
711
  }
674
712
  }
@@ -143,7 +143,7 @@ DocumentList.setMethod(function createIterator() {
143
143
  *
144
144
  * @author Jelle De Loecker <jelle@develry.be>
145
145
  * @since 1.0.0
146
- * @version 1.2.3
146
+ * @version 1.2.4
147
147
  *
148
148
  * @return {Object}
149
149
  */
@@ -152,16 +152,22 @@ DocumentList.setMethod(function toDry() {
152
152
  let options;
153
153
 
154
154
  if (this.options) {
155
- let key;
155
+ let val,
156
+ key;
156
157
 
157
158
  options = {};
158
159
 
159
160
  for (key in this.options) {
160
- if (key == 'options' || key == 'assoc_cache') {
161
+ val = this.options[key];
162
+
163
+ if (key == 'options') {
164
+ val = Object.assign({}, val);
165
+ val.assoc_cache = undefined;
166
+ } else if (key == 'assoc_cache') {
161
167
  continue;
162
168
  }
163
169
 
164
- options[key] = this.options[key];
170
+ options[key] = val;
165
171
  }
166
172
  }
167
173
 
@@ -386,7 +386,7 @@ Document.setMethod(function toDry() {
386
386
  *
387
387
  * @author Jelle De Loecker <jelle@develry.be>
388
388
  * @since 0.2.0
389
- * @version 1.1.0
389
+ * @version 1.2.4
390
390
  *
391
391
  * @param {WeakMap} wm
392
392
  *
@@ -412,7 +412,10 @@ Document.setMethod(function toHawkejs(wm) {
412
412
  // Sometimes we get an EMPTY $record value,
413
413
  // this is probably because it's already in the process of being clones
414
414
  if (!Object.isEmpty(record)) {
415
- result.setDataRecord(record);
415
+ // Get clean options
416
+ let options = JSON.clone(this.getCleanOptions(), 'toHawkejs', wm);
417
+
418
+ result.setDataRecord(record, options);
416
419
  } else {
417
420
  record.$record = record;
418
421
  }
@@ -425,6 +428,24 @@ Document.setMethod(function toHawkejs(wm) {
425
428
  return result;
426
429
  });
427
430
 
431
+ /**
432
+ * Keep private fields when sending to browser
433
+ *
434
+ * @author Jelle De Loecker <jelle@elevenways.be>
435
+ * @since 1.2.4
436
+ * @version 1.2.4
437
+ */
438
+ Document.setMethod(function keepPrivateFields(value) {
439
+
440
+ if (arguments.length == 0) {
441
+ value = true;
442
+ } else {
443
+ value = !!value;
444
+ }
445
+
446
+ this.$options.keep_private_fields = value;
447
+ });
448
+
428
449
  /**
429
450
  * Return the basic record for JSON
430
451
  *
@@ -115,4 +115,26 @@ DocumentList.setMethod(function findNextBatch(callback) {
115
115
  }
116
116
 
117
117
  this.options.model.find('all', options, callback);
118
+ });
119
+
120
+ /**
121
+ * Keep private fields when sending to browser
122
+ *
123
+ * @author Jelle De Loecker <jelle@elevenways.be>
124
+ * @since 1.2.4
125
+ * @version 1.2.4
126
+ */
127
+ DocumentList.setMethod(function keepPrivateFields(value) {
128
+
129
+ if (arguments.length == 0) {
130
+ value = true;
131
+ } else {
132
+ value = !!value;
133
+ }
134
+
135
+ let record;
136
+
137
+ for (record of this) {
138
+ record.keepPrivateFields(value);
139
+ }
118
140
  });
@@ -5,9 +5,7 @@
5
5
  * @since 1.0.0
6
6
  * @version 1.0.0
7
7
  */
8
- var Element = Function.inherits('Hawkejs.Element', 'Alchemy.Element', function Element() {
9
- return Element.super.call(this);
10
- });
8
+ const Element = Function.inherits('Hawkejs.Element', 'Alchemy.Element', 'Element');
11
9
 
12
10
  /**
13
11
  * The default element prefix (when element contains no hyphen) is "al"
@@ -111,4 +109,20 @@ Element.setMethod(function getModel(model_name, options) {
111
109
  this._model_instances[model_name] = instance;
112
110
 
113
111
  return instance;
112
+ });
113
+
114
+ /**
115
+ * Default translation implementation
116
+ *
117
+ * @author Jelle De Loecker <jelle@elevenways.be>
118
+ * @since 1.2.4
119
+ * @version 1.2.4
120
+ *
121
+ * @param {String} key
122
+ * @param {Object} parameters
123
+ *
124
+ * @return {String}
125
+ */
126
+ Element.setMethod(function __(key, parameters) {
127
+ return key;
114
128
  });
@@ -105,12 +105,17 @@ Field.setStatic(function createPathEvaluator(path) {
105
105
  *
106
106
  * @author Jelle De Loecker <jelle@develry.be>
107
107
  * @since 1.0.0
108
- * @version 1.0.0
108
+ * @version 1.2.4
109
109
  *
110
110
  * @type {Boolean}
111
111
  */
112
112
  Field.setProperty(function is_private() {
113
- return !!this.options.is_private;
113
+
114
+ if (this.options.is_private || this.options.private) {
115
+ return true;
116
+ }
117
+
118
+ return false;
114
119
  });
115
120
 
116
121
  /**
@@ -362,7 +362,7 @@ Model.setStatic(function checkPathValue(value, name, field_name, conduit) {
362
362
  *
363
363
  * @author Jelle De Loecker <jelle@develry.be>
364
364
  * @since 0.2.0
365
- * @version 1.0.7
365
+ * @version 1.2.4
366
366
  *
367
367
  * @return {Alchemy.Field}
368
368
  */
@@ -380,12 +380,10 @@ Model.setStatic(function addField(name, type, options) {
380
380
  // Add it to the Document class
381
381
  this.Document.setFieldGetter(name);
382
382
 
383
- // Add the field to the client document too, if it's not private
384
- if (!field.is_private) {
385
- // False means it should not be set on the server implementation
386
- // (because that's where it's coming from)
387
- this.ClientDocument.setFieldGetter(name, null, null, false);
388
- }
383
+ // False means it should not be set on the server implementation
384
+ // (because that's where it's coming from)
385
+ // Yes, this also sets private fields on the server-side client document.
386
+ this.ClientDocument.setFieldGetter(name, null, null, false);
389
387
  }
390
388
 
391
389
  return field;
@@ -409,18 +407,16 @@ Model.setStatic(function addBehaviour(behaviour_name, options) {
409
407
  *
410
408
  * @author Jelle De Loecker <jelle@develry.be>
411
409
  * @since 0.2.0
412
- * @version 0.2.0
410
+ * @version 1.2.4
413
411
  */
414
412
  Model.setStatic(function addAssociation(type, alias, model_name, options) {
415
413
  var data = this.schema.addAssociation(type, alias, model_name, options);
416
414
  this.Document.setAliasGetter(data.alias);
417
415
 
418
- // Add the alias to the client document too, if it's not private
419
- if (!options || !options.is_private) {
420
- // False means it should not be set on the server implementation
421
- // (because that's where it's coming from)
422
- this.ClientDocument.setAliasGetter(name);
423
- }
416
+ // False means it should not be set on the server implementation
417
+ // (because that's where it's coming from)
418
+ // Yes, this also sets private fields on the server-side client document.
419
+ this.ClientDocument.setAliasGetter(name);
424
420
  });
425
421
 
426
422
  /**
@@ -287,7 +287,7 @@ Schema.setMethod(function getAssociationArguments(locality, alias, modelName, op
287
287
  *
288
288
  * @author Jelle De Loecker <jelle@develry.be>
289
289
  * @since 0.2.0
290
- * @version 1.2.2
290
+ * @version 1.2.4
291
291
  *
292
292
  * @param {String} alias
293
293
  * @param {String} modelname
@@ -382,7 +382,8 @@ Schema.setMethod(function addAssociation(type, alias, modelName, options) {
382
382
  doc_class.setFieldGetter(options.localKey);
383
383
  }
384
384
 
385
- if (client_doc && !options.is_private) {
385
+ // Also set is_private fields on the server-side client_doc!
386
+ if (client_doc) {
386
387
  client_doc.setFieldGetter(options.localKey, null, null, false);
387
388
  }
388
389
  }
@@ -396,7 +397,8 @@ Schema.setMethod(function addAssociation(type, alias, modelName, options) {
396
397
  if (constructor) {
397
398
  doc_class.setAliasGetter(alias);
398
399
 
399
- if (client_doc && !options.is_private) {
400
+ // Also set is_private fields on the server-side client_doc!
401
+ if (client_doc) {
400
402
  client_doc.setAliasGetter(alias);
401
403
  }
402
404
  }
@@ -414,6 +414,37 @@ Schema.setMethod(function toHawkejs(wm) {
414
414
  return result;
415
415
  });
416
416
 
417
+ /**
418
+ * Get all the private fields
419
+ *
420
+ * @author Jelle De Loecker <jelle@elevenways.be>
421
+ * @since 1.2.4
422
+ * @version 1.2.4
423
+ *
424
+ * @return {Array}
425
+ */
426
+ Schema.setMethod(function getPrivateFields() {
427
+
428
+ let result = [],
429
+ field,
430
+ i;
431
+
432
+ // Get the sorted fields
433
+ let fields = this.getSorted(false);
434
+
435
+ for (i = 0; i < fields.length; i++) {
436
+ field = fields[i];
437
+
438
+ if (field.is_private) {
439
+ field = Object.create(field);
440
+ field.schema = null;
441
+ result.push(field);
442
+ }
443
+ }
444
+
445
+ return result;
446
+ });
447
+
417
448
  /**
418
449
  * Add a field to this schema
419
450
  *
package/lib/core/base.js CHANGED
@@ -345,7 +345,7 @@ Base.setStatic('starts_new_group', false);
345
345
  *
346
346
  * @author Jelle De Loecker <jelle@elevenways.be>
347
347
  * @since 1.1.8
348
- * @version 1.1.8
348
+ * @version 1.2.4
349
349
  *
350
350
  * @type {Conduit}
351
351
  */
@@ -355,16 +355,20 @@ Base.setProperty(function conduit() {
355
355
  return this[CONDUIT];
356
356
  }
357
357
 
358
- let renderer = this.renderer || this.view || this.hawkejs_view,
358
+ let renderer,
359
359
  result;
360
+
361
+ if (Blast.isBrowser || !(this instanceof Classes.Alchemy.Controller)) {
362
+ renderer = this.renderer || this.view || this.hawkejs_view;
363
+ }
360
364
 
361
- if (renderer && renderer.conduit) {
365
+ if (renderer?.conduit) {
362
366
  result = renderer.conduit;
363
- } else if (renderer && renderer.root_renderer && renderer.root_renderer.conduit) {
367
+ } else if (renderer?.root_renderer?.conduit) {
364
368
  result = renderer.root_renderer.conduit;
365
369
  }
366
370
 
367
- if (!conduit && renderer && renderer.server_var) {
371
+ if (!conduit && renderer?.server_var) {
368
372
  result = renderer.server_var('conduit');
369
373
  }
370
374
 
@@ -448,6 +452,39 @@ Base.setMethod(function callOrNext(name, args, next) {
448
452
 
449
453
  // PROTOBLAST START CUT
450
454
 
455
+ /**
456
+ * Get the client implementation of this class
457
+ *
458
+ * @author Jelle De Loecker <jelle@elevenways.be>
459
+ * @since 1.2.4
460
+ * @version 1.2.4
461
+ *
462
+ * @return {Function}
463
+ */
464
+ Base.setStatic(function getClientClass() {
465
+
466
+ if (!this.namespace) {
467
+ return;
468
+ }
469
+
470
+ // It's already a client-side class!
471
+ if (this.namespace.indexOf('.Client.')) {
472
+ return this;
473
+ }
474
+
475
+ // Add the Client part of the namespace
476
+ let namespace = this.namespace.replace('.Alchemy.', '.Alchemy.Client.');
477
+
478
+ // Get the actual namespace object
479
+ namespace = Function.getNamespace(namespace);
480
+
481
+ if (!namespace) {
482
+ return;
483
+ }
484
+
485
+ return namespace[this.name];
486
+ });
487
+
451
488
  /**
452
489
  * Attach a conduit to a certain instance
453
490
  *
@@ -1091,4 +1091,20 @@ Alchemy.setMethod(function syncDataWithServer() {
1091
1091
  Alchemy.setMethod(function createSchema(parent) {
1092
1092
  let schema = new Classes.Alchemy.Client.Schema(parent);
1093
1093
  return schema;
1094
+ });
1095
+
1096
+ /**
1097
+ * Get a client-side model
1098
+ *
1099
+ * @author Jelle De Loecker <jelle@elevenways.be>
1100
+ * @since 1.2.4
1101
+ * @version 1.2.4
1102
+ *
1103
+ * @param {String} name
1104
+ * @param {Object} options
1105
+ *
1106
+ * @return {Model}
1107
+ */
1108
+ Alchemy.setMethod(function getClientModel(name, init, options) {
1109
+ return Classes.Alchemy.Client.Base.prototype.getModel.call(this, name, init, options);
1094
1110
  });
@@ -233,7 +233,7 @@ ClientBase.setStatic(function mapEventToMethod(event_name, method_name) {
233
233
  *
234
234
  * @author Jelle De Loecker <jelle@develry.be>
235
235
  * @since 1.0.0
236
- * @version 1.1.8
236
+ * @version 1.2.4
237
237
  *
238
238
  * @param {String} name
239
239
  * @param {Object} options
@@ -255,9 +255,19 @@ ClientBase.setMethod(function getModel(name, init, options) {
255
255
  }
256
256
 
257
257
  if (Blast.isBrowser) {
258
- constructor = Hawkejs.Model.getClass(name, false);
258
+
259
+ if (name == '') {
260
+ constructor = Hawkejs.Model;
261
+ } else {
262
+ constructor = Hawkejs.Model.getClass(name, false);
263
+ }
259
264
  } else {
260
- constructor = Blast.Classes.Hawkejs.Model.getClass(name);
265
+
266
+ if (name == '') {
267
+ constructor = Blast.Classes.Hawkejs.Model;
268
+ } else {
269
+ constructor = Blast.Classes.Hawkejs.Model.getClass(name);
270
+ }
261
271
  }
262
272
 
263
273
  if (!constructor) {
@@ -1749,6 +1749,22 @@ Alchemy.setMethod(function createSchema(parent) {
1749
1749
  return schema;
1750
1750
  });
1751
1751
 
1752
+ /**
1753
+ * Get a client-side model
1754
+ *
1755
+ * @author Jelle De Loecker <jelle@elevenways.be>
1756
+ * @since 1.2.4
1757
+ * @version 1.2.4
1758
+ *
1759
+ * @param {String} name
1760
+ * @param {Object} options
1761
+ *
1762
+ * @return {Model}
1763
+ */
1764
+ Alchemy.setMethod(function getClientModel(name, init, options) {
1765
+ return Classes.Alchemy.Client.Base.prototype.getModel.call(this, name, init, options);
1766
+ });
1767
+
1752
1768
  /**
1753
1769
  * The alchemy global, where everything will be stored
1754
1770
  *
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.2.3",
4
+ "version": "1.2.4",
5
5
  "author": "Jelle De Loecker <jelle@elevenways.be>",
6
6
  "keywords": [
7
7
  "alchemy",
@@ -22,7 +22,7 @@
22
22
  "chokidar" : "~3.5.3",
23
23
  "formidable" : "~1.2.2",
24
24
  "graceful-fs" : "~4.2.9",
25
- "hawkejs" : "~2.2.9",
25
+ "hawkejs" : "~2.2.10",
26
26
  "jsondiffpatch" : "~0.4.1",
27
27
  "mime" : "~3.0.0",
28
28
  "minimist" : "~1.2.5",
@@ -31,7 +31,7 @@
31
31
  "mongodb" : "~3.6.6",
32
32
  "ncp" : "~2.0.0",
33
33
  "postcss" : "~8.4.6",
34
- "protoblast" : "~0.7.20",
34
+ "protoblast" : "~0.7.21",
35
35
  "semver" : "~7.3.5",
36
36
  "socket.io" : "~2.4.0",
37
37
  "@11ways/socket.io-stream" : "~0.9.2",