@onehat/data 1.19.40 → 1.20.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onehat/data",
3
- "version": "1.19.40",
3
+ "version": "1.20.0",
4
4
  "description": "JS data modeling package with adapters for many storage mediums.",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -47,6 +47,7 @@
47
47
  "moment": "^2.29.4",
48
48
  "natsort": "^2.0.3",
49
49
  "numeral": "^2.0.6",
50
+ "object-hash": "^3.0.0",
50
51
  "qs": "^6.11.2",
51
52
  "relative-time-parser": "^1.0.15",
52
53
  "uuid": "^9.0.1"
@@ -3,6 +3,7 @@
3
3
  import EventEmitter from '@onehat/events';
4
4
  import PropertyTypes from '../Property/index.js';
5
5
  import moment from 'moment';
6
+ import hash from 'object-hash';
6
7
  import _ from 'lodash';
7
8
 
8
9
  /**
@@ -104,6 +105,11 @@ class Entity extends EventEmitter {
104
105
  */
105
106
  this.properties = [];
106
107
 
108
+ /**
109
+ * @member {string} hash - A hash of this.submitValues, so we can detect changes
110
+ */
111
+ this.hash = null;
112
+
107
113
  /**
108
114
  * @member {boolean} isTree - Whether this Entity is a TreeNode
109
115
  */
@@ -254,11 +260,22 @@ class Entity extends EventEmitter {
254
260
  return this._proxy; // Return the Proxy, not 'this'
255
261
  }
256
262
 
263
+ /**
264
+ * Decorator for parent emit() method so we can rehash
265
+ */
266
+ emit(name) { // NOTE: Purposefully do not use an arrow-function, so we have access to arguments
267
+
268
+ this.rehash();
269
+
270
+ return super.emit(...arguments);
271
+ }
272
+
257
273
  initialize() {
258
274
  this.properties = this._createProperties();
259
275
  this._createMethods();
260
276
  this._createStatics();
261
277
  this.reset();
278
+ this.rehash();
262
279
  this.isInitialized = true;
263
280
  }
264
281
 
@@ -1022,47 +1039,6 @@ class Entity extends EventEmitter {
1022
1039
  return !_.isEqualWith(this._originalDataParsed, this.getParsedValues());
1023
1040
  }
1024
1041
 
1025
- /**
1026
- * Gets the a hash of the current submitValues.
1027
- * This allows easy detection of changes in data.
1028
- * @return {integer} hash
1029
- */
1030
- getHash = () => {
1031
- if (this.isDestroyed) {
1032
- throw Error('this.getHash is no longer valid. Entity has been destroyed.');
1033
- }
1034
-
1035
- const str = JSON.stringify(_.merge({}, this.submitValues, {
1036
- // include Entity state in hash
1037
- isDestroyed: this.isDestroyed,
1038
- isPhantom: this.isPhantom,
1039
- isDirty: this.isDirty,
1040
- isTempId: this.isTempId,
1041
- }));
1042
-
1043
- // from https://github.com/bryc/code/blob/master/jshash/experimental/cyrb53.js
1044
- const seed = 0;
1045
- let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
1046
- for (let i = 0, ch; i < str.length; i++) {
1047
- ch = str.charCodeAt(i);
1048
- h1 = Math.imul(h1 ^ ch, 2654435761);
1049
- h2 = Math.imul(h2 ^ ch, 1597334677);
1050
- }
1051
- h1 = Math.imul(h1 ^ (h1>>>16), 2246822507) ^ Math.imul(h2 ^ (h2>>>13), 3266489909);
1052
- h2 = Math.imul(h2 ^ (h2>>>16), 2246822507) ^ Math.imul(h1 ^ (h1>>>13), 3266489909);
1053
- const hash = 4294967296 * (2097151 & h2) + (h1>>>0);
1054
-
1055
- return hash;
1056
- }
1057
-
1058
- /**
1059
- * Getter of the hash for this Entity.
1060
- * @return {integer} hash
1061
- */
1062
- get hash() {
1063
- return this.getHash();
1064
- }
1065
-
1066
1042
  /**
1067
1043
  * Gets the original data object for this Entity.
1068
1044
  * This is either what was persisted to storage medium, or what was
@@ -1817,6 +1793,22 @@ class Entity extends EventEmitter {
1817
1793
  return true;
1818
1794
  }
1819
1795
 
1796
+ /**
1797
+ * Sets the hash of the current submitValues and state.
1798
+ * This allows easy detection of changes in data.
1799
+ * @return {integer} hash
1800
+ */
1801
+ rehash = () => {
1802
+ const toHash = JSON.stringify(_.merge({}, this.submitValues, {
1803
+ // include Entity state in hash
1804
+ isDestroyed: this.isDestroyed,
1805
+ isPhantom: this.isPhantom,
1806
+ isDirty: this.isDirty,
1807
+ isTempId: this.isTempId,
1808
+ }));
1809
+ this.hash = hash(toHash);
1810
+ }
1811
+
1820
1812
 
1821
1813
  /**
1822
1814
  * Destroy this object.
@@ -53,6 +53,13 @@ export default class BooleanProperty extends Property {
53
53
  return Parsers.ParseBool(this.parsedValue); // Use a Parser instead of a Formatter to make sure we submit it as an actual boolean primitive value
54
54
  }
55
55
 
56
+ toggle = () => {
57
+ if (this.isDestroyed) {
58
+ throw Error('this.toggle is no longer valid. Property has been destroyed.');
59
+ }
60
+ this.parsedValue = !this.parsedValue;
61
+ }
62
+
56
63
  };
57
64
 
58
65
  BooleanProperty.className = 'Boolean';
@@ -611,6 +611,7 @@ class OneBuildRepository extends AjaxRepository {
611
611
 
612
612
 
613
613
  // Don't emit events for root nodes...
614
+ this.rehash();
614
615
  this.emit('load', this);
615
616
  // this.emit('changeData', this.entities);
616
617
 
@@ -693,6 +694,7 @@ class OneBuildRepository extends AjaxRepository {
693
694
 
694
695
  this._setPaginationVars();
695
696
 
697
+ this.rehash();
696
698
  // this.emit('changeData', this.entities);
697
699
  this.emit('load', this);
698
700
 
@@ -773,6 +775,7 @@ class OneBuildRepository extends AjaxRepository {
773
775
 
774
776
  this._setPaginationVars();
775
777
 
778
+ this.rehash();
776
779
  // this.emit('changeData', this.entities);
777
780
  this.emit('load', this);
778
781
 
@@ -8,6 +8,7 @@ import {
8
8
  } from 'uuid';
9
9
  import moment from 'moment';
10
10
  import { waitUntil } from 'async-wait-until';
11
+ import hash from 'object-hash';
11
12
  import _ from 'lodash';
12
13
 
13
14
  /**
@@ -113,6 +114,11 @@ export default class Repository extends EventEmitter {
113
114
  */
114
115
  isShowingMore: false,
115
116
 
117
+ /**
118
+ * @member {string} hash - A hash of this.entities, so we can detect changes
119
+ */
120
+ hash: null,
121
+
116
122
  /**
117
123
  * @member {number} pageSize - Max number of entities per page
118
124
  * Example: For "Showing 21-30 of 45" This would be 10
@@ -284,6 +290,19 @@ export default class Repository extends EventEmitter {
284
290
  ]);
285
291
  }
286
292
 
293
+ /**
294
+ * Decorator for parent emit() method
295
+ * so we can rehash on changeData events
296
+ */
297
+ emit(name) { // NOTE: Purposefully do not use an arrow-function, so we have access to arguments
298
+
299
+ if (name === 'changeData') {
300
+ this.rehash();
301
+ }
302
+
303
+ return super.emit(...arguments);
304
+ }
305
+
287
306
  /**
288
307
  * Initializes the Repository.
289
308
  * - Applies default sorters
@@ -318,6 +337,7 @@ export default class Repository extends EventEmitter {
318
337
  if (init) {
319
338
  await init.call(this);
320
339
  }
340
+ this.rehash();
321
341
 
322
342
  this.isInitialized = true;
323
343
  this.emit('initialize');
@@ -2123,6 +2143,11 @@ export default class Repository extends EventEmitter {
2123
2143
  _.merge(this, options);
2124
2144
  }
2125
2145
 
2146
+ rehash = () => {
2147
+ const hashes = _.map(this.entities, (entity) => entity.hash);
2148
+ this.hash = hash(hashes);
2149
+ }
2150
+
2126
2151
  unmapData = (mappedData) => {
2127
2152
  const propertiesDef = this.schema?.model?.properties;
2128
2153
  if (!propertiesDef) {