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