@nymphjs/nymph 1.0.0-beta.11 → 1.0.0-beta.110
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/CHANGELOG.md +536 -0
- package/README.md +23 -21
- package/dist/Entity.d.ts +293 -10
- package/dist/Entity.js +560 -148
- package/dist/Entity.js.map +1 -1
- package/dist/Entity.test.js +52 -13
- package/dist/Entity.test.js.map +1 -1
- package/dist/Entity.types.d.ts +256 -23
- package/dist/Entity.types.js +1 -2
- package/dist/Nymph.d.ts +586 -12
- package/dist/Nymph.js +844 -179
- package/dist/Nymph.js.map +1 -1
- package/dist/Nymph.types.d.ts +81 -8
- package/dist/Nymph.types.js +3 -5
- package/dist/Nymph.types.js.map +1 -1
- package/dist/conf/d.d.ts +43 -0
- package/dist/conf/d.js +1 -2
- package/dist/conf/defaults.d.ts +1 -1
- package/dist/conf/defaults.js +26 -3
- package/dist/conf/defaults.js.map +1 -1
- package/dist/conf/index.d.ts +2 -2
- package/dist/conf/index.js +2 -8
- package/dist/conf/index.js.map +1 -1
- package/dist/driver/NymphDriver.d.ts +201 -40
- package/dist/driver/NymphDriver.js +685 -239
- package/dist/driver/NymphDriver.js.map +1 -1
- package/dist/driver/index.d.ts +2 -2
- package/dist/driver/index.js +3 -23
- package/dist/driver/index.js.map +1 -1
- package/dist/driver/testArtifacts.d.ts +2 -2
- package/dist/driver/testArtifacts.js +909 -93
- package/dist/driver/testArtifacts.js.map +1 -1
- package/dist/errors/ClassNotAvailableError.js +1 -5
- package/dist/errors/ClassNotAvailableError.js.map +1 -1
- package/dist/errors/EntityConflictError.js +1 -5
- package/dist/errors/EntityConflictError.js.map +1 -1
- package/dist/errors/EntityInvalidDataError.d.ts +6 -0
- package/dist/errors/EntityInvalidDataError.js +8 -6
- package/dist/errors/EntityInvalidDataError.js.map +1 -1
- package/dist/errors/EntityIsSleepingReferenceError.js +1 -5
- package/dist/errors/EntityIsSleepingReferenceError.js.map +1 -1
- package/dist/errors/EntityUniqueConstraintError.d.ts +3 -0
- package/dist/errors/EntityUniqueConstraintError.js +7 -0
- package/dist/errors/EntityUniqueConstraintError.js.map +1 -0
- package/dist/errors/HttpError.d.ts +5 -0
- package/dist/errors/HttpError.js +11 -0
- package/dist/errors/HttpError.js.map +1 -0
- package/dist/errors/InvalidParametersError.js +1 -5
- package/dist/errors/InvalidParametersError.js.map +1 -1
- package/dist/errors/InvalidStateError.d.ts +3 -0
- package/dist/errors/InvalidStateError.js +7 -0
- package/dist/errors/InvalidStateError.js.map +1 -0
- package/dist/errors/NotConfiguredError.js +1 -5
- package/dist/errors/NotConfiguredError.js.map +1 -1
- package/dist/errors/QueryFailedError.d.ts +2 -1
- package/dist/errors/QueryFailedError.js +5 -6
- package/dist/errors/QueryFailedError.js.map +1 -1
- package/dist/errors/UnableToConnectError.js +1 -5
- package/dist/errors/UnableToConnectError.js.map +1 -1
- package/dist/errors/index.d.ts +11 -8
- package/dist/errors/index.js +11 -24
- package/dist/errors/index.js.map +1 -1
- package/dist/index.d.ts +11 -11
- package/dist/index.js +14 -34
- package/dist/index.js.map +1 -1
- package/dist/nymphJoiProps.d.ts +1 -1
- package/dist/nymphJoiProps.js +10 -15
- package/dist/nymphJoiProps.js.map +1 -1
- package/dist/testArtifacts.d.ts +19 -6
- package/dist/testArtifacts.js +32 -37
- package/dist/testArtifacts.js.map +1 -1
- package/dist/testMocks.d.ts +27 -12
- package/dist/testMocks.js +40 -29
- package/dist/testMocks.js.map +1 -1
- package/dist/utils.d.ts +4 -4
- package/dist/utils.js +40 -29
- package/dist/utils.js.map +1 -1
- package/jest.config.js +11 -2
- package/package.json +24 -20
- package/src/Entity.test.ts +50 -7
- package/src/Entity.ts +489 -150
- package/src/Entity.types.ts +72 -74
- package/src/Nymph.ts +985 -322
- package/src/Nymph.types.ts +150 -27
- package/src/conf/d.ts +25 -0
- package/src/conf/defaults.ts +34 -1
- package/src/conf/index.ts +2 -2
- package/src/driver/NymphDriver.ts +859 -412
- package/src/driver/index.ts +2 -2
- package/src/driver/testArtifacts.ts +1090 -186
- package/src/errors/EntityUniqueConstraintError.ts +6 -0
- package/src/errors/HttpError.ts +12 -0
- package/src/errors/InvalidStateError.ts +6 -0
- package/src/errors/QueryFailedError.ts +3 -1
- package/src/errors/index.ts +14 -8
- package/src/index.ts +11 -11
- package/src/nymphJoiProps.ts +2 -2
- package/src/testArtifacts.ts +20 -20
- package/src/testMocks.ts +76 -35
- package/src/type/locutus.d.ts +3 -0
- package/src/utils.ts +39 -18
- package/tsconfig.json +5 -3
- package/typedoc.json +4 -0
package/dist/Entity.js
CHANGED
|
@@ -1,32 +1,228 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
1
|
+
import { difference, intersection, isEqual } from 'lodash-es';
|
|
2
|
+
import { guid } from '@nymphjs/guid';
|
|
3
|
+
import { EntityConflictError, EntityIsSleepingReferenceError, InvalidParametersError, InvalidStateError, } from './errors/index.js';
|
|
4
|
+
import { entitiesToReferences, referencesToEntities, uniqueStrings, } from './utils.js';
|
|
5
|
+
/**
|
|
6
|
+
* Database abstraction object.
|
|
7
|
+
*
|
|
8
|
+
* Provides a way to access, manipulate, and store data in Nymph.
|
|
9
|
+
*
|
|
10
|
+
* The GUID is not set until the entity is saved. GUIDs must be unique forever,
|
|
11
|
+
* even after deletion. It's the job of the Nymph DB driver to make sure no two
|
|
12
|
+
* entities ever have the same GUID. This is generally done by using a large
|
|
13
|
+
* randomly generated ID.
|
|
14
|
+
*
|
|
15
|
+
* Each entity class has an etype that determines which table(s) in the database
|
|
16
|
+
* it belongs to. If two entity classes have the same etype, their data will be
|
|
17
|
+
* stored in the same table(s). This isn't a good idea, however, because
|
|
18
|
+
* references to an entity store a class name, not an etype.
|
|
19
|
+
*
|
|
20
|
+
* Tags are used to classify entities. Where an etype is used to separate data
|
|
21
|
+
* by tables, tags can be used to separate entities within a table. You can
|
|
22
|
+
* define specific tags to be protected, meaning they cannot be added/removed
|
|
23
|
+
* from the client. It can be useful to allow user defined tags, such as for
|
|
24
|
+
* blog posts.
|
|
25
|
+
*
|
|
26
|
+
* Simply calling $delete() will not unset the entity. It will still take up
|
|
27
|
+
* memory. Likewise, simply calling unset will not delete the entity from the
|
|
28
|
+
* DB.
|
|
29
|
+
*
|
|
30
|
+
* Some notes about $equals() and $is(), the replacements for "==":
|
|
31
|
+
*
|
|
32
|
+
* The == operator will likely not give you the result you want, since two
|
|
33
|
+
* instances of the same entity will fail that check, even though they represent
|
|
34
|
+
* the same data in the database.
|
|
35
|
+
*
|
|
36
|
+
* $equals() performs a more strict comparison of the entity to another. Use
|
|
37
|
+
* $equals() instead of the == operator when you want to check both the entities
|
|
38
|
+
* they represent, and the data inside them. In order to return true for
|
|
39
|
+
* $equals(), the entity and object must meet the following criteria:
|
|
40
|
+
*
|
|
41
|
+
* - They must be entities.
|
|
42
|
+
* - They must have equal GUIDs, or both must have no GUID.
|
|
43
|
+
* - Their data and tags must be equal.
|
|
44
|
+
*
|
|
45
|
+
* $is() performs a less strict comparison of the entity to another. Use $is()
|
|
46
|
+
* instead of the == operator when the entity's data may have been changed, but
|
|
47
|
+
* you only care if they represent the same entity. In order to return true, the
|
|
48
|
+
* entity and object must meet the following criteria:
|
|
49
|
+
*
|
|
50
|
+
* - They must be entities.
|
|
51
|
+
* - They must have equal GUIDs, or both must have no GUID.
|
|
52
|
+
* - If they have no GUIDs, their data and tags must be equal.
|
|
53
|
+
*
|
|
54
|
+
* Some notes about saving entities in other entity's properties:
|
|
55
|
+
*
|
|
56
|
+
* Entities use references in the DB to store an entity in their properties. The
|
|
57
|
+
* reference is stored as an array with the values:
|
|
58
|
+
*
|
|
59
|
+
* - 0 => The string 'nymph_entity_reference'
|
|
60
|
+
* - 1 => The referenced entity's GUID.
|
|
61
|
+
* - 2 => The referenced entity's class name.
|
|
62
|
+
*
|
|
63
|
+
* Since the referenced entity's class name (meaning the `class` static
|
|
64
|
+
* property, not the name of the class itself) is stored in the reference on the
|
|
65
|
+
* parent entity, if you change the class name in an update, you need to
|
|
66
|
+
* reassign all referenced entities of that class and resave.
|
|
67
|
+
*
|
|
68
|
+
* When an entity is loaded, it does not request its referenced entities from
|
|
69
|
+
* Nymph. Instead, it creates instances without data called sleeping references.
|
|
70
|
+
* When you first access an entity's data, if it is a sleeping reference, it
|
|
71
|
+
* will fill its data from the DB. You can call clearCache() to turn all the
|
|
72
|
+
* entities back into sleeping references.
|
|
73
|
+
*/
|
|
74
|
+
export default class Entity {
|
|
75
|
+
/**
|
|
76
|
+
* The instance of Nymph to use for queries.
|
|
77
|
+
*/
|
|
78
|
+
static nymph = {};
|
|
79
|
+
/**
|
|
80
|
+
* A unique name for this type of entity used to separate its data from other
|
|
81
|
+
* types of entities in the database.
|
|
82
|
+
*/
|
|
83
|
+
static ETYPE = 'entity';
|
|
84
|
+
/**
|
|
85
|
+
* The lookup name for this entity.
|
|
86
|
+
*
|
|
87
|
+
* This is used for reference arrays (and sleeping references) and client
|
|
88
|
+
* requests.
|
|
89
|
+
*/
|
|
90
|
+
static class = 'Entity';
|
|
91
|
+
$nymph;
|
|
92
|
+
guid = null;
|
|
93
|
+
cdate = null;
|
|
94
|
+
mdate = null;
|
|
95
|
+
tags = [];
|
|
96
|
+
/**
|
|
97
|
+
* The data proxy handler.
|
|
98
|
+
*/
|
|
99
|
+
$dataHandler;
|
|
100
|
+
/**
|
|
101
|
+
* The actual data store.
|
|
102
|
+
*/
|
|
103
|
+
$dataStore;
|
|
104
|
+
/**
|
|
105
|
+
* The actual sdata store.
|
|
106
|
+
*/
|
|
107
|
+
$sdata;
|
|
108
|
+
/**
|
|
109
|
+
* The data proxy object.
|
|
110
|
+
*/
|
|
111
|
+
$data;
|
|
112
|
+
/**
|
|
113
|
+
* Whether this instance is a sleeping reference.
|
|
114
|
+
*/
|
|
115
|
+
$isASleepingReference = false;
|
|
116
|
+
/**
|
|
117
|
+
* The reference to use to wake.
|
|
118
|
+
*/
|
|
119
|
+
$sleepingReference = null;
|
|
120
|
+
/**
|
|
121
|
+
* A promise that resolved when the entity's data is wake.
|
|
122
|
+
*/
|
|
123
|
+
$wakePromise = null;
|
|
124
|
+
/**
|
|
125
|
+
* Properties that will not be serialized into JSON with toJSON(). This
|
|
126
|
+
* can be considered a denylist, because these properties will not be set
|
|
127
|
+
* with incoming JSON.
|
|
128
|
+
*
|
|
129
|
+
* Clients CAN still determine what is in these properties, unless they are
|
|
130
|
+
* also listed in searchRestrictedData.
|
|
131
|
+
*/
|
|
132
|
+
$privateData = [];
|
|
133
|
+
/**
|
|
134
|
+
* Whether this entity should publish changes to PubSub servers.
|
|
135
|
+
*/
|
|
136
|
+
static pubSubEnabled = true;
|
|
137
|
+
/**
|
|
138
|
+
* Whether this entity should be accessible on the frontend through the REST
|
|
139
|
+
* server.
|
|
140
|
+
*
|
|
141
|
+
* If this is false, any request from the client that attempts to use this
|
|
142
|
+
* entity will fail.
|
|
143
|
+
*/
|
|
144
|
+
static restEnabled = true;
|
|
145
|
+
/**
|
|
146
|
+
* Properties that will not be searchable from the frontend. If the frontend
|
|
147
|
+
* includes any of these properties in any of their clauses, they will be
|
|
148
|
+
* filtered out before the search is executed.
|
|
149
|
+
*/
|
|
150
|
+
static searchRestrictedData = [];
|
|
151
|
+
/**
|
|
152
|
+
* Properties that can only be modified by server side code. They will still
|
|
153
|
+
* be visible on the frontend, unlike $privateData, but any changes to them
|
|
154
|
+
* that come from the frontend will be ignored.
|
|
155
|
+
*
|
|
156
|
+
* In addition to what's listed here, all of the access control properties
|
|
157
|
+
* will be included when Tilmeld is being used. These are:
|
|
158
|
+
*
|
|
159
|
+
* - acUser
|
|
160
|
+
* - acGroup
|
|
161
|
+
* - acOther
|
|
162
|
+
* - acRead
|
|
163
|
+
* - acWrite
|
|
164
|
+
* - acFull
|
|
165
|
+
* - user
|
|
166
|
+
* - group
|
|
167
|
+
*
|
|
168
|
+
* You should modify these through client enabled methods or the $save method
|
|
169
|
+
* instead, for safety.
|
|
170
|
+
*/
|
|
171
|
+
$protectedData = [];
|
|
172
|
+
/**
|
|
173
|
+
* If this is defined, then it lists the only properties that will be
|
|
174
|
+
* accepted from incoming JSON. Any other properties will be ignored.
|
|
175
|
+
*
|
|
176
|
+
* If you use an allowlist, you don't need to use protectedData, since you
|
|
177
|
+
* can simply leave those entries out of allowlistData.
|
|
178
|
+
*/
|
|
179
|
+
$allowlistData;
|
|
180
|
+
/**
|
|
181
|
+
* Tags that can only be added/removed by server side code. They will still be
|
|
182
|
+
* visible on the frontend, but any changes to them that come from the
|
|
183
|
+
* frontend will be ignored.
|
|
184
|
+
*/
|
|
185
|
+
$protectedTags = [];
|
|
186
|
+
/**
|
|
187
|
+
* If this is defined, then it lists the only tags that will be accepted from
|
|
188
|
+
* incoming JSON. Any other tags will be ignored.
|
|
189
|
+
*/
|
|
190
|
+
$allowlistTags;
|
|
191
|
+
/**
|
|
192
|
+
* The names of methods allowed to be called by the frontend with serverCall.
|
|
193
|
+
*/
|
|
194
|
+
$clientEnabledMethods = [];
|
|
195
|
+
/**
|
|
196
|
+
* The names of static methods allowed to be called by the frontend with
|
|
197
|
+
* serverCallStatic.
|
|
198
|
+
*/
|
|
199
|
+
static clientEnabledStaticMethods = [];
|
|
200
|
+
/**
|
|
201
|
+
* Whether to use "skipAc" when accessing entity references.
|
|
202
|
+
*/
|
|
203
|
+
$skipAc = false;
|
|
204
|
+
/**
|
|
205
|
+
* The AC properties' values when the entity was loaded.
|
|
206
|
+
*/
|
|
207
|
+
$originalAcValues = null;
|
|
208
|
+
/**
|
|
209
|
+
* This is used to hold a generated GUID for a new entity.
|
|
210
|
+
*/
|
|
211
|
+
$guaranteedGUID = null;
|
|
212
|
+
/**
|
|
213
|
+
* Initialize an entity.
|
|
214
|
+
*/
|
|
215
|
+
constructor(..._rest) {
|
|
20
216
|
this.$nymph = this.constructor.nymph;
|
|
21
217
|
this.$dataHandler = {
|
|
22
218
|
has: (data, name) => {
|
|
23
|
-
this.$
|
|
219
|
+
this.$check();
|
|
24
220
|
return name in data || name in this.$sdata;
|
|
25
221
|
},
|
|
26
222
|
get: (data, name) => {
|
|
27
|
-
this.$
|
|
223
|
+
this.$check();
|
|
28
224
|
if (this.$sdata.hasOwnProperty(name)) {
|
|
29
|
-
data[name] =
|
|
225
|
+
data[name] = referencesToEntities(JSON.parse(this.$sdata[name]), this.$nymph, this.$skipAc);
|
|
30
226
|
delete this.$sdata[name];
|
|
31
227
|
}
|
|
32
228
|
if (data.hasOwnProperty(name)) {
|
|
@@ -35,7 +231,7 @@ class Entity {
|
|
|
35
231
|
return undefined;
|
|
36
232
|
},
|
|
37
233
|
set: (data, name, value) => {
|
|
38
|
-
this.$
|
|
234
|
+
this.$check();
|
|
39
235
|
if (this.$sdata.hasOwnProperty(name)) {
|
|
40
236
|
delete this.$sdata[name];
|
|
41
237
|
}
|
|
@@ -43,7 +239,7 @@ class Entity {
|
|
|
43
239
|
return true;
|
|
44
240
|
},
|
|
45
241
|
deleteProperty: (data, name) => {
|
|
46
|
-
this.$
|
|
242
|
+
this.$check();
|
|
47
243
|
if (this.$sdata.hasOwnProperty(name)) {
|
|
48
244
|
return delete this.$sdata[name];
|
|
49
245
|
}
|
|
@@ -53,7 +249,7 @@ class Entity {
|
|
|
53
249
|
return true;
|
|
54
250
|
},
|
|
55
251
|
defineProperty: (data, name, descriptor) => {
|
|
56
|
-
this.$
|
|
252
|
+
this.$check();
|
|
57
253
|
if (this.$sdata.hasOwnProperty(name)) {
|
|
58
254
|
delete this.$sdata[name];
|
|
59
255
|
}
|
|
@@ -61,31 +257,21 @@ class Entity {
|
|
|
61
257
|
return true;
|
|
62
258
|
},
|
|
63
259
|
getOwnPropertyDescriptor: (data, name) => {
|
|
64
|
-
this.$
|
|
260
|
+
this.$check();
|
|
65
261
|
if (this.$sdata.hasOwnProperty(name)) {
|
|
66
|
-
data[name] =
|
|
262
|
+
data[name] = referencesToEntities(JSON.parse(this.$sdata[name]), this.$nymph, this.$skipAc);
|
|
67
263
|
delete this.$sdata[name];
|
|
68
264
|
}
|
|
69
265
|
return Object.getOwnPropertyDescriptor(data, name);
|
|
70
266
|
},
|
|
71
267
|
ownKeys: (data) => {
|
|
72
|
-
this.$
|
|
268
|
+
this.$check();
|
|
73
269
|
return Object.getOwnPropertyNames(data).concat(Object.getOwnPropertyNames(this.$sdata));
|
|
74
270
|
},
|
|
75
271
|
};
|
|
76
272
|
this.$dataStore = {};
|
|
77
273
|
this.$sdata = {};
|
|
78
274
|
this.$data = new Proxy(this.$dataStore, this.$dataHandler);
|
|
79
|
-
if (guid != null) {
|
|
80
|
-
const entity = this.$nymph.driver.getEntitySync({ class: this.constructor }, { type: '&', guid });
|
|
81
|
-
if (entity) {
|
|
82
|
-
this.guid = entity.guid;
|
|
83
|
-
this.tags = entity.tags;
|
|
84
|
-
this.cdate = entity.cdate;
|
|
85
|
-
this.mdate = entity.mdate;
|
|
86
|
-
this.$putData(entity.$getData(), entity.$getSData());
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
275
|
return new Proxy(this, {
|
|
90
276
|
has: (entity, name) => {
|
|
91
277
|
if (typeof name !== 'string' ||
|
|
@@ -93,7 +279,7 @@ class Entity {
|
|
|
93
279
|
name.substring(0, 1) === '$') {
|
|
94
280
|
return name in entity;
|
|
95
281
|
}
|
|
96
|
-
this.$
|
|
282
|
+
this.$check();
|
|
97
283
|
return name in entity.$data;
|
|
98
284
|
},
|
|
99
285
|
get: (entity, name) => {
|
|
@@ -101,11 +287,11 @@ class Entity {
|
|
|
101
287
|
name in entity ||
|
|
102
288
|
name.substring(0, 1) === '$') {
|
|
103
289
|
if (name === 'tags' || name === 'cdate' || name === 'mdate') {
|
|
104
|
-
this.$
|
|
290
|
+
this.$check();
|
|
105
291
|
}
|
|
106
292
|
return entity[name];
|
|
107
293
|
}
|
|
108
|
-
this.$
|
|
294
|
+
this.$check();
|
|
109
295
|
if (name in entity.$data) {
|
|
110
296
|
return entity.$data[name];
|
|
111
297
|
}
|
|
@@ -116,12 +302,12 @@ class Entity {
|
|
|
116
302
|
name in entity ||
|
|
117
303
|
name.substring(0, 1) === '$') {
|
|
118
304
|
if (name === 'tags' || name === 'cdate' || name === 'mdate') {
|
|
119
|
-
this.$
|
|
305
|
+
this.$check();
|
|
120
306
|
}
|
|
121
307
|
entity[name] = value;
|
|
122
308
|
}
|
|
123
309
|
else {
|
|
124
|
-
this.$
|
|
310
|
+
this.$check();
|
|
125
311
|
entity.$data[name] = value;
|
|
126
312
|
}
|
|
127
313
|
return true;
|
|
@@ -131,7 +317,7 @@ class Entity {
|
|
|
131
317
|
return delete entity[name];
|
|
132
318
|
}
|
|
133
319
|
else if (name in entity.$data) {
|
|
134
|
-
this.$
|
|
320
|
+
this.$check();
|
|
135
321
|
return delete entity.$data[name];
|
|
136
322
|
}
|
|
137
323
|
return true;
|
|
@@ -144,12 +330,12 @@ class Entity {
|
|
|
144
330
|
name in entity ||
|
|
145
331
|
name.substring(0, 1) === '$') {
|
|
146
332
|
if (name === 'tags' || name === 'cdate' || name === 'mdate') {
|
|
147
|
-
this.$
|
|
333
|
+
this.$check();
|
|
148
334
|
}
|
|
149
335
|
Object.defineProperty(entity, name, descriptor);
|
|
150
336
|
}
|
|
151
337
|
else {
|
|
152
|
-
this.$
|
|
338
|
+
this.$check();
|
|
153
339
|
Object.defineProperty(entity.$data, name, descriptor);
|
|
154
340
|
}
|
|
155
341
|
return true;
|
|
@@ -159,21 +345,28 @@ class Entity {
|
|
|
159
345
|
name in entity ||
|
|
160
346
|
name.substring(0, 1) === '$') {
|
|
161
347
|
if (name === 'tags' || name === 'cdate' || name === 'mdate') {
|
|
162
|
-
this.$
|
|
348
|
+
this.$check();
|
|
163
349
|
}
|
|
164
350
|
return Object.getOwnPropertyDescriptor(entity, name);
|
|
165
351
|
}
|
|
166
352
|
else {
|
|
167
|
-
this.$
|
|
353
|
+
this.$check();
|
|
168
354
|
return Object.getOwnPropertyDescriptor(entity.$data, name);
|
|
169
355
|
}
|
|
170
356
|
},
|
|
171
357
|
ownKeys: (entity) => {
|
|
172
|
-
this.$
|
|
358
|
+
this.$check();
|
|
173
359
|
return Object.getOwnPropertyNames(entity).concat(Object.getOwnPropertyNames(entity.$data));
|
|
174
360
|
},
|
|
175
361
|
});
|
|
176
362
|
}
|
|
363
|
+
/**
|
|
364
|
+
* Create or retrieve a new entity instance.
|
|
365
|
+
*
|
|
366
|
+
* Note that this will always return an entity, even if the GUID is not found.
|
|
367
|
+
*
|
|
368
|
+
* @param guid An optional GUID to retrieve.
|
|
369
|
+
*/
|
|
177
370
|
static async factory(guid) {
|
|
178
371
|
const entity = new this();
|
|
179
372
|
if (guid != null) {
|
|
@@ -186,14 +379,76 @@ class Entity {
|
|
|
186
379
|
}
|
|
187
380
|
return entity;
|
|
188
381
|
}
|
|
189
|
-
|
|
190
|
-
|
|
382
|
+
/**
|
|
383
|
+
* Create a new entity instance.
|
|
384
|
+
*/
|
|
385
|
+
static factorySync() {
|
|
386
|
+
return new this();
|
|
191
387
|
}
|
|
388
|
+
/**
|
|
389
|
+
* Create a new sleeping reference instance.
|
|
390
|
+
*
|
|
391
|
+
* Sleeping references won't retrieve their data from the database until they
|
|
392
|
+
* are readied with `$wake()` or a parent's `$wakeAll()`.
|
|
393
|
+
*
|
|
394
|
+
* @param reference The Nymph Entity Reference to use to wake.
|
|
395
|
+
* @returns The new instance.
|
|
396
|
+
*/
|
|
192
397
|
static factoryReference(reference) {
|
|
193
398
|
const entity = new this();
|
|
194
399
|
entity.$referenceSleep(reference);
|
|
195
400
|
return entity;
|
|
196
401
|
}
|
|
402
|
+
/**
|
|
403
|
+
* Get an array of strings that **must** be unique across the current etype.
|
|
404
|
+
*
|
|
405
|
+
* When you try to save another entity with any of the same unique strings,
|
|
406
|
+
* Nymph will throw an error.
|
|
407
|
+
*
|
|
408
|
+
* The default implementation of this static method instantiates the entity,
|
|
409
|
+
* assigns all of the given data, then calls `$getUniques` and returns its
|
|
410
|
+
* output. This can have a performance impact if a lot of extra processing
|
|
411
|
+
* happens during any of these steps. You can override this method to
|
|
412
|
+
* calculate the unique strings faster, but you must return the same strings
|
|
413
|
+
* that would be returned by `$getUniques`.
|
|
414
|
+
*
|
|
415
|
+
* @returns Resolves to an array of entity's unique constraint strings.
|
|
416
|
+
*/
|
|
417
|
+
static async getUniques({ guid, cdate, mdate, tags, data, sdata, }) {
|
|
418
|
+
const entity = new this();
|
|
419
|
+
if (guid != null) {
|
|
420
|
+
entity.guid = guid;
|
|
421
|
+
}
|
|
422
|
+
if (cdate != null) {
|
|
423
|
+
entity.cdate = cdate;
|
|
424
|
+
}
|
|
425
|
+
if (mdate != null) {
|
|
426
|
+
entity.mdate = mdate;
|
|
427
|
+
}
|
|
428
|
+
entity.tags = tags;
|
|
429
|
+
entity.$putData(data, sdata);
|
|
430
|
+
return await entity.$getUniques();
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Get a string for full text search for one of an entity's properties.
|
|
434
|
+
*
|
|
435
|
+
* The result will be tokenized and stored as the full text search index for
|
|
436
|
+
* use with "search" clauses.
|
|
437
|
+
*
|
|
438
|
+
* Return null to not include any tokens in the full text search storage.
|
|
439
|
+
*
|
|
440
|
+
* You shouldn't use any other entity data to transform the text, as it won't
|
|
441
|
+
* always be available. This function is meant for things like stripping HTML
|
|
442
|
+
* tags.
|
|
443
|
+
*
|
|
444
|
+
* @returns By default, returns the value if it is a string.
|
|
445
|
+
*/
|
|
446
|
+
static getFTSText(_name, value) {
|
|
447
|
+
if (typeof value === 'string') {
|
|
448
|
+
return value;
|
|
449
|
+
}
|
|
450
|
+
return null;
|
|
451
|
+
}
|
|
197
452
|
toJSON() {
|
|
198
453
|
if (this.$isASleepingReference) {
|
|
199
454
|
return this.$sleepingReference;
|
|
@@ -206,23 +461,34 @@ class Entity {
|
|
|
206
461
|
tags: [...this.tags],
|
|
207
462
|
data: {},
|
|
208
463
|
};
|
|
209
|
-
const
|
|
210
|
-
|
|
464
|
+
for (const key of [
|
|
465
|
+
...Object.keys(this.$dataStore),
|
|
466
|
+
...Object.keys(this.$sdata),
|
|
467
|
+
]) {
|
|
211
468
|
if (this.$privateData.indexOf(key) === -1) {
|
|
212
|
-
obj.data[key] =
|
|
469
|
+
obj.data[key] = entitiesToReferences(this.$data[key], true);
|
|
213
470
|
}
|
|
214
471
|
}
|
|
215
472
|
return obj;
|
|
216
473
|
}
|
|
474
|
+
$getGuaranteedGUID() {
|
|
475
|
+
if (this.guid !== null) {
|
|
476
|
+
return this.guid;
|
|
477
|
+
}
|
|
478
|
+
if (this.$guaranteedGUID == null) {
|
|
479
|
+
this.$guaranteedGUID = guid();
|
|
480
|
+
}
|
|
481
|
+
return this.$guaranteedGUID;
|
|
482
|
+
}
|
|
217
483
|
$addTag(...tags) {
|
|
218
|
-
this.$
|
|
484
|
+
this.$check();
|
|
219
485
|
if (tags.length < 1) {
|
|
220
486
|
return;
|
|
221
487
|
}
|
|
222
|
-
this.tags =
|
|
488
|
+
this.tags = uniqueStrings([...this.tags, ...tags]);
|
|
223
489
|
}
|
|
224
490
|
$arraySearch(array, strict = false) {
|
|
225
|
-
this.$
|
|
491
|
+
this.$check();
|
|
226
492
|
if (!Array.isArray(array)) {
|
|
227
493
|
return -1;
|
|
228
494
|
}
|
|
@@ -235,18 +501,18 @@ class Entity {
|
|
|
235
501
|
return -1;
|
|
236
502
|
}
|
|
237
503
|
$clearCache() {
|
|
238
|
-
this.$
|
|
239
|
-
this.$putData(this.$getData(), this.$getSData());
|
|
504
|
+
this.$check();
|
|
505
|
+
this.$putData(this.$getData(false, true), this.$getSData());
|
|
240
506
|
}
|
|
241
507
|
$getClientEnabledMethods() {
|
|
242
508
|
return this.$clientEnabledMethods;
|
|
243
509
|
}
|
|
244
510
|
async $delete() {
|
|
245
|
-
this.$
|
|
511
|
+
this.$check();
|
|
246
512
|
return await this.$nymph.deleteEntity(this);
|
|
247
513
|
}
|
|
248
514
|
$equals(object) {
|
|
249
|
-
this.$
|
|
515
|
+
this.$check();
|
|
250
516
|
if (!(object instanceof Entity)) {
|
|
251
517
|
return false;
|
|
252
518
|
}
|
|
@@ -263,41 +529,79 @@ class Entity {
|
|
|
263
529
|
}
|
|
264
530
|
const obTags = [...object.tags].sort();
|
|
265
531
|
const myTags = [...this.tags].sort();
|
|
266
|
-
if (!
|
|
532
|
+
if (!isEqual(obTags, myTags)) {
|
|
267
533
|
return false;
|
|
268
534
|
}
|
|
269
|
-
const obData = object.$getData(true);
|
|
270
|
-
const myData = this.$getData(true);
|
|
271
|
-
return
|
|
535
|
+
const obData = object.$getData(true, true);
|
|
536
|
+
const myData = this.$getData(true, true);
|
|
537
|
+
return isEqual(obData, myData);
|
|
272
538
|
}
|
|
273
|
-
$getData(includeSData = false) {
|
|
274
|
-
this.$
|
|
539
|
+
$getData(includeSData = false, referenceOnlyExisting) {
|
|
540
|
+
this.$check();
|
|
275
541
|
if (includeSData) {
|
|
542
|
+
// Access all the serialized properties to initialize them.
|
|
276
543
|
for (const key in this.$sdata) {
|
|
277
544
|
const _unused = this[key];
|
|
278
545
|
}
|
|
279
546
|
}
|
|
280
|
-
return
|
|
547
|
+
return entitiesToReferences({ ...this.$dataStore }, referenceOnlyExisting);
|
|
281
548
|
}
|
|
282
549
|
$getSData() {
|
|
283
|
-
this.$
|
|
550
|
+
this.$check();
|
|
284
551
|
return this.$sdata;
|
|
285
552
|
}
|
|
553
|
+
async $getUniques() {
|
|
554
|
+
return [];
|
|
555
|
+
}
|
|
286
556
|
$getOriginalAcValues() {
|
|
287
|
-
this.$
|
|
288
|
-
return
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
557
|
+
this.$check();
|
|
558
|
+
return this.$originalAcValues ?? this.$getCurrentAcValues();
|
|
559
|
+
}
|
|
560
|
+
$getCurrentAcValues() {
|
|
561
|
+
this.$check();
|
|
562
|
+
return {
|
|
563
|
+
user: this.$getAcUid(),
|
|
564
|
+
group: this.$getAcGid(),
|
|
565
|
+
acUser: this.$data.acUser ?? null,
|
|
566
|
+
acGroup: this.$data.acGroup ?? null,
|
|
567
|
+
acOther: this.$data.acOther ?? null,
|
|
568
|
+
acRead: this.$getAcReadIds(),
|
|
569
|
+
acWrite: this.$getAcWriteIds(),
|
|
570
|
+
acFull: this.$getAcFullIds(),
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
$getAcUid() {
|
|
574
|
+
if ('user' in this.$sdata) {
|
|
575
|
+
const userValue = JSON.parse(this.$sdata.user);
|
|
576
|
+
if (Array.isArray(userValue) &&
|
|
577
|
+
userValue[0] === 'nymph_entity_reference') {
|
|
578
|
+
return userValue[1];
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
return (this.$data.user?.guid ?? null);
|
|
582
|
+
}
|
|
583
|
+
$getAcGid() {
|
|
584
|
+
if ('group' in this.$sdata) {
|
|
585
|
+
const groupValue = JSON.parse(this.$sdata.group);
|
|
586
|
+
if (Array.isArray(groupValue) &&
|
|
587
|
+
groupValue[0] === 'nymph_entity_reference') {
|
|
588
|
+
return groupValue[1];
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
return (this.$data.group?.guid ?? null);
|
|
592
|
+
}
|
|
593
|
+
$getAcReadIds() {
|
|
594
|
+
return (this.$data.acRead?.map((entity) => typeof entity === 'string' ? entity : entity.guid) ?? null);
|
|
595
|
+
}
|
|
596
|
+
$getAcWriteIds() {
|
|
597
|
+
return (this.$data.acWrite?.map((entity) => typeof entity === 'string' ? entity : entity.guid) ?? null);
|
|
598
|
+
}
|
|
599
|
+
$getAcFullIds() {
|
|
600
|
+
return (this.$data.acFull?.map((entity) => typeof entity === 'string' ? entity : entity.guid) ?? null);
|
|
298
601
|
}
|
|
299
602
|
$getValidatable() {
|
|
300
|
-
this.$
|
|
603
|
+
this.$check();
|
|
604
|
+
// Access all the serialized properties to initialize them.
|
|
301
605
|
for (const key in this.$sdata) {
|
|
302
606
|
const _unused = this[key];
|
|
303
607
|
}
|
|
@@ -306,15 +610,15 @@ class Entity {
|
|
|
306
610
|
cdate: this.cdate,
|
|
307
611
|
mdate: this.mdate,
|
|
308
612
|
tags: this.tags,
|
|
309
|
-
...
|
|
613
|
+
...referencesToEntities(this.$dataStore, this.$nymph, this.$skipAc),
|
|
310
614
|
};
|
|
311
615
|
}
|
|
312
616
|
$getTags() {
|
|
313
|
-
this.$
|
|
617
|
+
this.$check();
|
|
314
618
|
return this.tags;
|
|
315
619
|
}
|
|
316
620
|
$hasTag(...tags) {
|
|
317
|
-
this.$
|
|
621
|
+
this.$check();
|
|
318
622
|
if (!tags.length) {
|
|
319
623
|
return false;
|
|
320
624
|
}
|
|
@@ -332,44 +636,50 @@ class Entity {
|
|
|
332
636
|
if (!(object instanceof Entity)) {
|
|
333
637
|
return false;
|
|
334
638
|
}
|
|
335
|
-
if (this
|
|
639
|
+
if (this === object) {
|
|
640
|
+
return true;
|
|
641
|
+
}
|
|
642
|
+
if (this.guid != null || object.guid != null) {
|
|
336
643
|
return this.guid === object.guid;
|
|
337
644
|
}
|
|
338
|
-
this.$
|
|
645
|
+
this.$check();
|
|
339
646
|
if (typeof object.$getData !== 'function') {
|
|
340
647
|
return false;
|
|
341
648
|
}
|
|
342
649
|
else {
|
|
343
650
|
const obTags = [...object.tags].sort();
|
|
344
651
|
const myTags = [...this.tags].sort();
|
|
345
|
-
if (!
|
|
652
|
+
if (!isEqual(obTags, myTags)) {
|
|
346
653
|
return false;
|
|
347
654
|
}
|
|
348
|
-
const obData = object.$getData(true);
|
|
349
|
-
const myData = this.$getData(true);
|
|
350
|
-
return
|
|
655
|
+
const obData = object.$getData(true, true);
|
|
656
|
+
const myData = this.$getData(true, true);
|
|
657
|
+
return isEqual(obData, myData);
|
|
351
658
|
}
|
|
352
659
|
}
|
|
353
660
|
$jsonAcceptData(input, allowConflict = false) {
|
|
354
|
-
this.$
|
|
661
|
+
this.$check();
|
|
355
662
|
if (this.guid != input.guid) {
|
|
356
|
-
throw new
|
|
663
|
+
throw new EntityConflictError('Tried to accept JSON input for the wrong entity.');
|
|
357
664
|
}
|
|
665
|
+
// Accept the modified date.
|
|
358
666
|
const mdate = input.mdate ?? 0;
|
|
359
667
|
const thismdate = this.mdate ?? 0;
|
|
360
668
|
if (mdate < thismdate && !allowConflict) {
|
|
361
|
-
throw new
|
|
669
|
+
throw new EntityConflictError('This entity is newer than JSON input.');
|
|
362
670
|
}
|
|
363
671
|
this.mdate = input.mdate;
|
|
672
|
+
// Accept the tags.
|
|
364
673
|
const currentTags = this.$getTags();
|
|
365
|
-
const protectedTags =
|
|
366
|
-
let tags =
|
|
674
|
+
const protectedTags = intersection(this.$protectedTags, currentTags);
|
|
675
|
+
let tags = difference(input.tags, this.$protectedTags);
|
|
367
676
|
if (this.$allowlistTags != null) {
|
|
368
|
-
tags =
|
|
677
|
+
tags = intersection(tags, this.$allowlistTags);
|
|
369
678
|
}
|
|
370
679
|
this.$removeTag(...currentTags);
|
|
371
680
|
this.$addTag(...protectedTags, ...tags);
|
|
372
|
-
|
|
681
|
+
// Accept the data.
|
|
682
|
+
const data = { ...input.data };
|
|
373
683
|
const privateData = {};
|
|
374
684
|
for (const name of this.$privateData) {
|
|
375
685
|
if (name in this.$data) {
|
|
@@ -381,10 +691,21 @@ class Entity {
|
|
|
381
691
|
}
|
|
382
692
|
const protectedData = {};
|
|
383
693
|
const protectedProps = [...this.$protectedData];
|
|
384
|
-
if (this.$nymph.tilmeld
|
|
385
|
-
|
|
386
|
-
protectedProps.push('
|
|
387
|
-
protectedProps.push('
|
|
694
|
+
if (this.$nymph.tilmeld) {
|
|
695
|
+
protectedProps.push('acUser');
|
|
696
|
+
protectedProps.push('acGroup');
|
|
697
|
+
protectedProps.push('acOther');
|
|
698
|
+
protectedProps.push('acRead');
|
|
699
|
+
protectedProps.push('acWrite');
|
|
700
|
+
protectedProps.push('acFull');
|
|
701
|
+
if ((this.constructor.class !== 'User' &&
|
|
702
|
+
this.constructor.class !== 'Group') ||
|
|
703
|
+
!this.$nymph.tilmeld.currentUser ||
|
|
704
|
+
(!this.$nymph.tilmeld.currentUser.abilities?.includes('tilmeld/admin') &&
|
|
705
|
+
!this.$nymph.tilmeld.currentUser.abilities?.includes('system/admin'))) {
|
|
706
|
+
protectedProps.push('user');
|
|
707
|
+
protectedProps.push('group');
|
|
708
|
+
}
|
|
388
709
|
}
|
|
389
710
|
for (const name of protectedProps) {
|
|
390
711
|
if (name in this.$data) {
|
|
@@ -396,7 +717,7 @@ class Entity {
|
|
|
396
717
|
}
|
|
397
718
|
let nonAllowlistData = {};
|
|
398
719
|
if (this.$allowlistData != null) {
|
|
399
|
-
nonAllowlistData = { ...this.$getData(true) };
|
|
720
|
+
nonAllowlistData = { ...this.$getData(true, true) };
|
|
400
721
|
for (const name of this.$allowlistData) {
|
|
401
722
|
delete nonAllowlistData[name];
|
|
402
723
|
}
|
|
@@ -414,29 +735,47 @@ class Entity {
|
|
|
414
735
|
});
|
|
415
736
|
}
|
|
416
737
|
$jsonAcceptPatch(patch, allowConflict = false) {
|
|
417
|
-
this.$
|
|
738
|
+
this.$check();
|
|
418
739
|
if (this.guid != patch.guid) {
|
|
419
|
-
throw new
|
|
740
|
+
throw new EntityConflictError('Tried to accept JSON patch for the wrong entity.');
|
|
420
741
|
}
|
|
742
|
+
// Accept the modified date.
|
|
421
743
|
const mdate = patch.mdate ?? 0;
|
|
422
744
|
const thismdate = this.mdate ?? 0;
|
|
423
745
|
if (mdate < thismdate && !allowConflict) {
|
|
424
|
-
throw new
|
|
746
|
+
throw new EntityConflictError('This entity is newer than JSON patch.');
|
|
425
747
|
}
|
|
426
748
|
this.mdate = patch.mdate;
|
|
749
|
+
const protectedProps = [...this.$protectedData];
|
|
750
|
+
if (this.$nymph.tilmeld) {
|
|
751
|
+
protectedProps.push('acUser');
|
|
752
|
+
protectedProps.push('acGroup');
|
|
753
|
+
protectedProps.push('acOther');
|
|
754
|
+
protectedProps.push('acRead');
|
|
755
|
+
protectedProps.push('acWrite');
|
|
756
|
+
protectedProps.push('acFull');
|
|
757
|
+
if ((this.constructor.class !== 'User' &&
|
|
758
|
+
this.constructor.class !== 'Group') ||
|
|
759
|
+
!this.$nymph.tilmeld.currentUser ||
|
|
760
|
+
(!this.$nymph.tilmeld.currentUser.abilities?.includes('tilmeld/admin') &&
|
|
761
|
+
!this.$nymph.tilmeld.currentUser.abilities?.includes('system/admin'))) {
|
|
762
|
+
protectedProps.push('user');
|
|
763
|
+
protectedProps.push('group');
|
|
764
|
+
}
|
|
765
|
+
}
|
|
427
766
|
for (const name in patch.set) {
|
|
428
767
|
if ((this.$allowlistData != null &&
|
|
429
768
|
this.$allowlistData.indexOf(name) === -1) ||
|
|
430
|
-
|
|
769
|
+
protectedProps.indexOf(name) !== -1 ||
|
|
431
770
|
this.$privateData.indexOf(name) !== -1) {
|
|
432
771
|
continue;
|
|
433
772
|
}
|
|
434
|
-
this.$data[name] =
|
|
773
|
+
this.$data[name] = referencesToEntities(patch.set[name], this.$nymph, this.$skipAc);
|
|
435
774
|
}
|
|
436
775
|
for (const name of patch.unset) {
|
|
437
776
|
if ((this.$allowlistData != null &&
|
|
438
777
|
this.$allowlistData.indexOf(name) === -1) ||
|
|
439
|
-
|
|
778
|
+
protectedProps.indexOf(name) !== -1 ||
|
|
440
779
|
this.$privateData.indexOf(name) !== -1) {
|
|
441
780
|
continue;
|
|
442
781
|
}
|
|
@@ -459,8 +798,8 @@ class Entity {
|
|
|
459
798
|
this.$removeTag(tag);
|
|
460
799
|
}
|
|
461
800
|
}
|
|
462
|
-
$putData(data, sdata) {
|
|
463
|
-
this.$
|
|
801
|
+
$putData(data, sdata, _source) {
|
|
802
|
+
this.$check();
|
|
464
803
|
const mySdata = sdata ?? this.$getSData();
|
|
465
804
|
for (const name in data) {
|
|
466
805
|
delete mySdata[name];
|
|
@@ -469,58 +808,137 @@ class Entity {
|
|
|
469
808
|
delete this.$dataStore[name];
|
|
470
809
|
}
|
|
471
810
|
for (const name in data) {
|
|
472
|
-
this.$dataStore[name] =
|
|
811
|
+
this.$dataStore[name] = referencesToEntities(data[name], this.$nymph, this.$skipAc);
|
|
473
812
|
}
|
|
474
813
|
this.$sdata = mySdata;
|
|
814
|
+
// Set original AC values if not set..
|
|
475
815
|
if (this.$originalAcValues == null) {
|
|
476
|
-
this.$originalAcValues = this.$
|
|
816
|
+
this.$originalAcValues = this.$getCurrentAcValues();
|
|
477
817
|
}
|
|
478
818
|
}
|
|
819
|
+
/**
|
|
820
|
+
* Set up a sleeping reference.
|
|
821
|
+
* @param array $reference The reference to use to wake.
|
|
822
|
+
*/
|
|
479
823
|
$referenceSleep(reference) {
|
|
480
824
|
if (reference.length !== 3 ||
|
|
481
825
|
reference[0] !== 'nymph_entity_reference' ||
|
|
482
826
|
typeof reference[1] !== 'string' ||
|
|
483
827
|
typeof reference[2] !== 'string') {
|
|
484
|
-
throw new
|
|
828
|
+
throw new InvalidParametersError('referenceSleep expects parameter 1 to be a valid Nymph entity ' +
|
|
485
829
|
'reference.');
|
|
486
830
|
}
|
|
487
831
|
const thisClass = this.constructor.class;
|
|
488
832
|
if (reference[2] !== thisClass) {
|
|
489
|
-
throw new
|
|
833
|
+
throw new InvalidParametersError('referenceSleep can only be called with an entity reference of the ' +
|
|
490
834
|
`same class. Given class: ${reference[2]}; this class: ${thisClass}.`);
|
|
491
835
|
}
|
|
492
836
|
this.$isASleepingReference = true;
|
|
493
837
|
this.guid = reference[1];
|
|
494
838
|
this.$sleepingReference = reference;
|
|
495
839
|
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
class: EntityClass,
|
|
503
|
-
skipAc: this.$skipAc,
|
|
504
|
-
}, { type: '&', guid: this.$sleepingReference[1] });
|
|
505
|
-
if (entity == null) {
|
|
506
|
-
return false;
|
|
840
|
+
/**
|
|
841
|
+
* Check if this is a sleeping reference and throw an error if so.
|
|
842
|
+
*/
|
|
843
|
+
$check() {
|
|
844
|
+
if (this.$isASleepingReference || this.$sleepingReference != null) {
|
|
845
|
+
throw new EntityIsSleepingReferenceError('This entity is in a sleeping reference state. You must use .$wake() to wake it.');
|
|
507
846
|
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
this
|
|
514
|
-
|
|
515
|
-
|
|
847
|
+
}
|
|
848
|
+
/**
|
|
849
|
+
* Check if this is a sleeping reference.
|
|
850
|
+
*/
|
|
851
|
+
$asleep() {
|
|
852
|
+
return this.$isASleepingReference || this.$sleepingReference != null;
|
|
853
|
+
}
|
|
854
|
+
/**
|
|
855
|
+
* Wake from a sleeping reference.
|
|
856
|
+
*/
|
|
857
|
+
$wake() {
|
|
858
|
+
if (!this.$isASleepingReference || this.$sleepingReference == null) {
|
|
859
|
+
this.$wakePromise = null;
|
|
860
|
+
return Promise.resolve(this);
|
|
861
|
+
}
|
|
862
|
+
if (this.$sleepingReference?.[1] == null) {
|
|
863
|
+
throw new InvalidStateError('Tried to wake a sleeping reference with no GUID.');
|
|
864
|
+
}
|
|
865
|
+
if (!this.$wakePromise) {
|
|
866
|
+
const EntityClass = this.$nymph.getEntityClass(this.$sleepingReference[2]);
|
|
867
|
+
this.$wakePromise = this.$nymph
|
|
868
|
+
.getEntity({
|
|
869
|
+
class: EntityClass,
|
|
870
|
+
skipAc: this.$skipAc,
|
|
871
|
+
}, { type: '&', guid: this.$sleepingReference[1] })
|
|
872
|
+
.then((entity) => {
|
|
873
|
+
if (entity == null || entity.guid == null) {
|
|
874
|
+
return Promise.reject(new InvalidStateError('The sleeping reference could not be retrieved.'));
|
|
875
|
+
}
|
|
876
|
+
this.$isASleepingReference = false;
|
|
877
|
+
this.$sleepingReference = null;
|
|
878
|
+
this.guid = entity.guid;
|
|
879
|
+
this.tags = entity.tags;
|
|
880
|
+
this.cdate = entity.cdate;
|
|
881
|
+
this.mdate = entity.mdate;
|
|
882
|
+
this.$putData(entity.$getData(false, true), entity.$getSData(), 'server');
|
|
883
|
+
return this;
|
|
884
|
+
})
|
|
885
|
+
.finally(() => {
|
|
886
|
+
this.$wakePromise = null;
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
return this.$wakePromise;
|
|
890
|
+
}
|
|
891
|
+
$wakeAll(level) {
|
|
892
|
+
return new Promise((resolve, reject) => {
|
|
893
|
+
// Run this once this entity is wake.
|
|
894
|
+
const wakeProps = () => {
|
|
895
|
+
let newLevel;
|
|
896
|
+
// If level is undefined, keep going forever, otherwise, stop once we've
|
|
897
|
+
// gone deep enough.
|
|
898
|
+
if (level !== undefined) {
|
|
899
|
+
newLevel = level - 1;
|
|
900
|
+
}
|
|
901
|
+
if (newLevel !== undefined && newLevel < 0) {
|
|
902
|
+
resolve(this);
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
905
|
+
const promises = [];
|
|
906
|
+
// Go through data looking for entities to wake.
|
|
907
|
+
for (let [key, value] of Object.entries(this.$data)) {
|
|
908
|
+
if (value instanceof Entity && value.$isASleepingReference) {
|
|
909
|
+
promises.push(value.$wakeAll(newLevel));
|
|
910
|
+
}
|
|
911
|
+
else if (Array.isArray(value)) {
|
|
912
|
+
for (let i = 0; i < value.length; i++) {
|
|
913
|
+
if (value[i] instanceof Entity &&
|
|
914
|
+
value[i].$isASleepingReference) {
|
|
915
|
+
promises.push(value[i].$wakeAll(newLevel));
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
if (promises.length) {
|
|
921
|
+
Promise.all(promises).then(() => resolve(this), (errObj) => reject(errObj));
|
|
922
|
+
}
|
|
923
|
+
else {
|
|
924
|
+
resolve(this);
|
|
925
|
+
}
|
|
926
|
+
};
|
|
927
|
+
if (this.$isASleepingReference) {
|
|
928
|
+
this.$wake().then(wakeProps, (errObj) => reject(errObj));
|
|
929
|
+
}
|
|
930
|
+
else {
|
|
931
|
+
wakeProps();
|
|
932
|
+
}
|
|
933
|
+
});
|
|
516
934
|
}
|
|
517
935
|
async $refresh() {
|
|
518
|
-
this.$
|
|
936
|
+
await this.$wake();
|
|
519
937
|
if (this.guid == null) {
|
|
520
938
|
return false;
|
|
521
939
|
}
|
|
522
940
|
const refresh = await this.$nymph.getEntity({
|
|
523
|
-
class: this.constructor,
|
|
941
|
+
class: this.$nymph.getEntityClass(this.constructor),
|
|
524
942
|
skipCache: true,
|
|
525
943
|
skipAc: this.$skipAc,
|
|
526
944
|
}, { type: '&', guid: this.guid });
|
|
@@ -530,27 +948,27 @@ class Entity {
|
|
|
530
948
|
this.tags = refresh.tags;
|
|
531
949
|
this.cdate = refresh.cdate;
|
|
532
950
|
this.mdate = refresh.mdate;
|
|
533
|
-
this.$putData(refresh.$getData(), refresh.$getSData());
|
|
951
|
+
this.$putData(refresh.$getData(false, true), refresh.$getSData(), 'server');
|
|
534
952
|
return true;
|
|
535
953
|
}
|
|
536
954
|
$removeTag(...tags) {
|
|
537
|
-
this.$
|
|
538
|
-
this.tags =
|
|
955
|
+
this.$check();
|
|
956
|
+
this.tags = difference(this.tags, tags);
|
|
539
957
|
}
|
|
540
958
|
async $save() {
|
|
541
|
-
this.$
|
|
959
|
+
await this.$wake();
|
|
542
960
|
return await this.$nymph.saveEntity(this);
|
|
543
961
|
}
|
|
544
|
-
$toReference() {
|
|
962
|
+
$toReference(existingOnly) {
|
|
545
963
|
if (this.$isASleepingReference && this.$sleepingReference != null) {
|
|
546
964
|
return this.$sleepingReference;
|
|
547
965
|
}
|
|
548
|
-
if (this.guid == null) {
|
|
966
|
+
if (existingOnly && this.guid == null) {
|
|
549
967
|
return this;
|
|
550
968
|
}
|
|
551
969
|
return [
|
|
552
970
|
'nymph_entity_reference',
|
|
553
|
-
this
|
|
971
|
+
this.$getGuaranteedGUID(),
|
|
554
972
|
this.constructor.class,
|
|
555
973
|
];
|
|
556
974
|
}
|
|
@@ -558,10 +976,4 @@ class Entity {
|
|
|
558
976
|
this.$skipAc = !!skipAc;
|
|
559
977
|
}
|
|
560
978
|
}
|
|
561
|
-
exports.default = Entity;
|
|
562
|
-
Entity.nymph = {};
|
|
563
|
-
Entity.ETYPE = 'entity';
|
|
564
|
-
Entity.class = 'Entity';
|
|
565
|
-
Entity.searchRestrictedData = [];
|
|
566
|
-
Entity.clientEnabledStaticMethods = [];
|
|
567
979
|
//# sourceMappingURL=Entity.js.map
|