@nymphjs/nymph 1.0.0-beta.1 → 1.0.0-beta.100
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 +535 -0
- package/README.md +14 -22
- package/dist/Entity.d.ts +293 -10
- package/dist/Entity.js +571 -149
- package/dist/Entity.js.map +1 -1
- package/dist/Entity.test.js +67 -26
- 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 +497 -13
- package/dist/Nymph.js +761 -188
- package/dist/Nymph.js.map +1 -1
- package/dist/Nymph.types.d.ts +95 -26
- 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 +181 -43
- package/dist/driver/NymphDriver.js +532 -285
- 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 +939 -219
- 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/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.js +2 -5
- 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 +10 -8
- package/dist/errors/index.js +10 -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.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 +28 -11
- package/dist/testMocks.js +44 -28
- 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 +63 -13
- package/src/Entity.ts +498 -156
- package/src/Entity.types.ts +72 -74
- package/src/Nymph.ts +904 -336
- package/src/Nymph.types.ts +179 -44
- 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 +650 -460
- package/src/driver/index.ts +2 -2
- package/src/driver/testArtifacts.ts +966 -196
- package/src/errors/EntityUniqueConstraintError.ts +6 -0
- package/src/errors/InvalidStateError.ts +6 -0
- package/src/errors/index.ts +12 -8
- package/src/index.ts +11 -11
- package/src/nymphJoiProps.ts +2 -2
- package/src/testArtifacts.ts +20 -20
- package/src/testMocks.ts +85 -37
- 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/src/Entity.ts
CHANGED
|
@@ -1,22 +1,39 @@
|
|
|
1
|
-
import { difference, intersection, isEqual } from 'lodash';
|
|
1
|
+
import { difference, intersection, isEqual } from 'lodash-es';
|
|
2
|
+
import { guid } from '@nymphjs/guid';
|
|
2
3
|
|
|
3
|
-
import type Nymph from './Nymph';
|
|
4
|
-
import {
|
|
4
|
+
import type Nymph from './Nymph.js';
|
|
5
|
+
import type { Options } from './Nymph.types.js';
|
|
6
|
+
import type {
|
|
5
7
|
ACProperties,
|
|
6
8
|
EntityConstructor,
|
|
7
9
|
EntityData,
|
|
8
10
|
EntityInterface,
|
|
9
11
|
EntityJson,
|
|
12
|
+
EntityObject,
|
|
10
13
|
EntityPatch,
|
|
11
14
|
EntityReference,
|
|
12
15
|
SerializedEntityData,
|
|
13
|
-
} from './Entity.types';
|
|
14
|
-
import {
|
|
16
|
+
} from './Entity.types.js';
|
|
17
|
+
import {
|
|
18
|
+
EntityConflictError,
|
|
19
|
+
EntityIsSleepingReferenceError,
|
|
20
|
+
InvalidParametersError,
|
|
21
|
+
InvalidStateError,
|
|
22
|
+
} from './errors/index.js';
|
|
15
23
|
import {
|
|
16
24
|
entitiesToReferences,
|
|
17
25
|
referencesToEntities,
|
|
18
26
|
uniqueStrings,
|
|
19
|
-
} from './utils';
|
|
27
|
+
} from './utils.js';
|
|
28
|
+
|
|
29
|
+
export type EntityDataType<T> =
|
|
30
|
+
T extends Entity<infer DataType> ? DataType : never;
|
|
31
|
+
|
|
32
|
+
export type EntityInstanceType<T extends EntityConstructor> =
|
|
33
|
+
T extends new () => infer E ? E & EntityDataType<E> : never;
|
|
34
|
+
|
|
35
|
+
export type EntityObjectType<T extends EntityConstructor> =
|
|
36
|
+
T extends new () => infer E ? EntityObject & EntityDataType<E> : never;
|
|
20
37
|
|
|
21
38
|
/**
|
|
22
39
|
* Database abstraction object.
|
|
@@ -45,9 +62,9 @@ import {
|
|
|
45
62
|
*
|
|
46
63
|
* Some notes about $equals() and $is(), the replacements for "==":
|
|
47
64
|
*
|
|
48
|
-
* The == operator will likely not give you the result you want, since
|
|
49
|
-
*
|
|
50
|
-
*
|
|
65
|
+
* The == operator will likely not give you the result you want, since two
|
|
66
|
+
* instances of the same entity will fail that check, even though they represent
|
|
67
|
+
* the same data in the database.
|
|
51
68
|
*
|
|
52
69
|
* $equals() performs a more strict comparison of the entity to another. Use
|
|
53
70
|
* $equals() instead of the == operator when you want to check both the entities
|
|
@@ -56,8 +73,7 @@ import {
|
|
|
56
73
|
*
|
|
57
74
|
* - They must be entities.
|
|
58
75
|
* - They must have equal GUIDs, or both must have no GUID.
|
|
59
|
-
* -
|
|
60
|
-
* - Their data must be equal.
|
|
76
|
+
* - Their data and tags must be equal.
|
|
61
77
|
*
|
|
62
78
|
* $is() performs a less strict comparison of the entity to another. Use $is()
|
|
63
79
|
* instead of the == operator when the entity's data may have been changed, but
|
|
@@ -66,7 +82,7 @@ import {
|
|
|
66
82
|
*
|
|
67
83
|
* - They must be entities.
|
|
68
84
|
* - They must have equal GUIDs, or both must have no GUID.
|
|
69
|
-
* - If they have no GUIDs, their data must be equal.
|
|
85
|
+
* - If they have no GUIDs, their data and tags must be equal.
|
|
70
86
|
*
|
|
71
87
|
* Some notes about saving entities in other entity's properties:
|
|
72
88
|
*
|
|
@@ -137,6 +153,10 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
137
153
|
* The reference to use to wake.
|
|
138
154
|
*/
|
|
139
155
|
protected $sleepingReference: EntityReference | null = null;
|
|
156
|
+
/**
|
|
157
|
+
* A promise that resolved when the entity's data is wake.
|
|
158
|
+
*/
|
|
159
|
+
protected $wakePromise: Promise<Entity<T>> | null = null;
|
|
140
160
|
/**
|
|
141
161
|
* Properties that will not be serialized into JSON with toJSON(). This
|
|
142
162
|
* can be considered a denylist, because these properties will not be set
|
|
@@ -146,6 +166,18 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
146
166
|
* also listed in searchRestrictedData.
|
|
147
167
|
*/
|
|
148
168
|
protected $privateData: string[] = [];
|
|
169
|
+
/**
|
|
170
|
+
* Whether this entity should publish changes to PubSub servers.
|
|
171
|
+
*/
|
|
172
|
+
public static pubSubEnabled = true;
|
|
173
|
+
/**
|
|
174
|
+
* Whether this entity should be accessible on the frontend through the REST
|
|
175
|
+
* server.
|
|
176
|
+
*
|
|
177
|
+
* If this is false, any request from the client that attempts to use this
|
|
178
|
+
* entity will fail.
|
|
179
|
+
*/
|
|
180
|
+
public static restEnabled = true;
|
|
149
181
|
/**
|
|
150
182
|
* Properties that will not be searchable from the frontend. If the frontend
|
|
151
183
|
* includes any of these properties in any of their clauses, they will be
|
|
@@ -157,8 +189,20 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
157
189
|
* be visible on the frontend, unlike $privateData, but any changes to them
|
|
158
190
|
* that come from the frontend will be ignored.
|
|
159
191
|
*
|
|
160
|
-
* In addition to what's listed here, the
|
|
161
|
-
* be
|
|
192
|
+
* In addition to what's listed here, all of the access control properties
|
|
193
|
+
* will be included when Tilmeld is being used. These are:
|
|
194
|
+
*
|
|
195
|
+
* - acUser
|
|
196
|
+
* - acGroup
|
|
197
|
+
* - acOther
|
|
198
|
+
* - acRead
|
|
199
|
+
* - acWrite
|
|
200
|
+
* - acFull
|
|
201
|
+
* - user
|
|
202
|
+
* - group
|
|
203
|
+
*
|
|
204
|
+
* You should modify these through client enabled methods or the $save method
|
|
205
|
+
* instead, for safety.
|
|
162
206
|
*/
|
|
163
207
|
protected $protectedData: string[] = [];
|
|
164
208
|
/**
|
|
@@ -197,28 +241,39 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
197
241
|
* The AC properties' values when the entity was loaded.
|
|
198
242
|
*/
|
|
199
243
|
private $originalAcValues: ACProperties | null = null;
|
|
244
|
+
/**
|
|
245
|
+
* This is used to hold a generated GUID for a new entity.
|
|
246
|
+
*/
|
|
247
|
+
private $guaranteedGUID: string | null = null;
|
|
200
248
|
|
|
201
249
|
/**
|
|
202
|
-
*
|
|
203
|
-
*
|
|
250
|
+
* Alter the options for a query for this entity.
|
|
251
|
+
*
|
|
252
|
+
* @param options The current options.
|
|
253
|
+
* @returns The altered options.
|
|
204
254
|
*/
|
|
205
|
-
|
|
255
|
+
static alterOptions?<T extends Options>(options: T): T;
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Initialize an entity.
|
|
259
|
+
*/
|
|
260
|
+
public constructor(..._rest: any[]) {
|
|
206
261
|
this.$nymph = (this.constructor as EntityConstructor).nymph;
|
|
207
262
|
this.$dataHandler = {
|
|
208
263
|
has: (data: EntityData, name: string) => {
|
|
209
|
-
this.$
|
|
264
|
+
this.$check();
|
|
210
265
|
|
|
211
266
|
return name in data || name in this.$sdata;
|
|
212
267
|
},
|
|
213
268
|
|
|
214
269
|
get: (data: EntityData, name: string) => {
|
|
215
|
-
this.$
|
|
270
|
+
this.$check();
|
|
216
271
|
|
|
217
272
|
if (this.$sdata.hasOwnProperty(name)) {
|
|
218
273
|
data[name] = referencesToEntities(
|
|
219
274
|
JSON.parse(this.$sdata[name]),
|
|
220
275
|
this.$nymph,
|
|
221
|
-
this.$skipAc
|
|
276
|
+
this.$skipAc,
|
|
222
277
|
);
|
|
223
278
|
delete this.$sdata[name];
|
|
224
279
|
}
|
|
@@ -229,7 +284,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
229
284
|
},
|
|
230
285
|
|
|
231
286
|
set: (data: EntityData, name: string, value: any) => {
|
|
232
|
-
this.$
|
|
287
|
+
this.$check();
|
|
233
288
|
|
|
234
289
|
if (this.$sdata.hasOwnProperty(name)) {
|
|
235
290
|
delete this.$sdata[name];
|
|
@@ -239,7 +294,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
239
294
|
},
|
|
240
295
|
|
|
241
296
|
deleteProperty: (data: EntityData, name: string) => {
|
|
242
|
-
this.$
|
|
297
|
+
this.$check();
|
|
243
298
|
|
|
244
299
|
if (this.$sdata.hasOwnProperty(name)) {
|
|
245
300
|
return delete this.$sdata[name];
|
|
@@ -253,9 +308,9 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
253
308
|
defineProperty: (
|
|
254
309
|
data: EntityData,
|
|
255
310
|
name: string,
|
|
256
|
-
descriptor: PropertyDescriptor
|
|
311
|
+
descriptor: PropertyDescriptor,
|
|
257
312
|
) => {
|
|
258
|
-
this.$
|
|
313
|
+
this.$check();
|
|
259
314
|
|
|
260
315
|
if (this.$sdata.hasOwnProperty(name)) {
|
|
261
316
|
delete this.$sdata[name];
|
|
@@ -265,13 +320,13 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
265
320
|
},
|
|
266
321
|
|
|
267
322
|
getOwnPropertyDescriptor: (data: EntityData, name: string) => {
|
|
268
|
-
this.$
|
|
323
|
+
this.$check();
|
|
269
324
|
|
|
270
325
|
if (this.$sdata.hasOwnProperty(name)) {
|
|
271
326
|
data[name] = referencesToEntities(
|
|
272
327
|
JSON.parse(this.$sdata[name]),
|
|
273
328
|
this.$nymph,
|
|
274
|
-
this.$skipAc
|
|
329
|
+
this.$skipAc,
|
|
275
330
|
);
|
|
276
331
|
delete this.$sdata[name];
|
|
277
332
|
}
|
|
@@ -279,10 +334,10 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
279
334
|
},
|
|
280
335
|
|
|
281
336
|
ownKeys: (data: EntityData) => {
|
|
282
|
-
this.$
|
|
337
|
+
this.$check();
|
|
283
338
|
|
|
284
339
|
return Object.getOwnPropertyNames(data).concat(
|
|
285
|
-
Object.getOwnPropertyNames(this.$sdata)
|
|
340
|
+
Object.getOwnPropertyNames(this.$sdata),
|
|
286
341
|
);
|
|
287
342
|
},
|
|
288
343
|
};
|
|
@@ -291,20 +346,6 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
291
346
|
|
|
292
347
|
this.$data = new Proxy(this.$dataStore, this.$dataHandler);
|
|
293
348
|
|
|
294
|
-
if (guid != null) {
|
|
295
|
-
const entity = this.$nymph.driver.getEntitySync(
|
|
296
|
-
{ class: this.constructor as EntityConstructor },
|
|
297
|
-
{ type: '&', guid }
|
|
298
|
-
);
|
|
299
|
-
if (entity) {
|
|
300
|
-
this.guid = entity.guid;
|
|
301
|
-
this.tags = entity.tags;
|
|
302
|
-
this.cdate = entity.cdate;
|
|
303
|
-
this.mdate = entity.mdate;
|
|
304
|
-
this.$putData(entity.$getData(), entity.$getSData());
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
|
|
308
349
|
return new Proxy(this, {
|
|
309
350
|
has: (entity: Entity, name: string) => {
|
|
310
351
|
if (
|
|
@@ -315,7 +356,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
315
356
|
return name in entity;
|
|
316
357
|
}
|
|
317
358
|
|
|
318
|
-
this.$
|
|
359
|
+
this.$check();
|
|
319
360
|
return name in entity.$data;
|
|
320
361
|
},
|
|
321
362
|
|
|
@@ -326,12 +367,12 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
326
367
|
name.substring(0, 1) === '$'
|
|
327
368
|
) {
|
|
328
369
|
if (name === 'tags' || name === 'cdate' || name === 'mdate') {
|
|
329
|
-
this.$
|
|
370
|
+
this.$check();
|
|
330
371
|
}
|
|
331
372
|
return (entity as any)[name];
|
|
332
373
|
}
|
|
333
374
|
|
|
334
|
-
this.$
|
|
375
|
+
this.$check();
|
|
335
376
|
if (name in entity.$data) {
|
|
336
377
|
return entity.$data[name];
|
|
337
378
|
}
|
|
@@ -345,11 +386,11 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
345
386
|
name.substring(0, 1) === '$'
|
|
346
387
|
) {
|
|
347
388
|
if (name === 'tags' || name === 'cdate' || name === 'mdate') {
|
|
348
|
-
this.$
|
|
389
|
+
this.$check();
|
|
349
390
|
}
|
|
350
391
|
(entity as any)[name] = value;
|
|
351
392
|
} else {
|
|
352
|
-
this.$
|
|
393
|
+
this.$check();
|
|
353
394
|
(entity.$data as any)[name] = value;
|
|
354
395
|
}
|
|
355
396
|
return true;
|
|
@@ -359,7 +400,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
359
400
|
if (name in entity) {
|
|
360
401
|
return delete (entity as any)[name];
|
|
361
402
|
} else if (name in entity.$data) {
|
|
362
|
-
this.$
|
|
403
|
+
this.$check();
|
|
363
404
|
return delete entity.$data[name];
|
|
364
405
|
}
|
|
365
406
|
return true;
|
|
@@ -372,7 +413,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
372
413
|
defineProperty: (
|
|
373
414
|
entity: Entity,
|
|
374
415
|
name: string,
|
|
375
|
-
descriptor: PropertyDescriptor
|
|
416
|
+
descriptor: PropertyDescriptor,
|
|
376
417
|
) => {
|
|
377
418
|
if (
|
|
378
419
|
typeof name !== 'string' ||
|
|
@@ -380,11 +421,11 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
380
421
|
name.substring(0, 1) === '$'
|
|
381
422
|
) {
|
|
382
423
|
if (name === 'tags' || name === 'cdate' || name === 'mdate') {
|
|
383
|
-
this.$
|
|
424
|
+
this.$check();
|
|
384
425
|
}
|
|
385
426
|
Object.defineProperty(entity, name, descriptor);
|
|
386
427
|
} else {
|
|
387
|
-
this.$
|
|
428
|
+
this.$check();
|
|
388
429
|
Object.defineProperty(entity.$data, name, descriptor);
|
|
389
430
|
}
|
|
390
431
|
return true;
|
|
@@ -397,48 +438,147 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
397
438
|
name.substring(0, 1) === '$'
|
|
398
439
|
) {
|
|
399
440
|
if (name === 'tags' || name === 'cdate' || name === 'mdate') {
|
|
400
|
-
this.$
|
|
441
|
+
this.$check();
|
|
401
442
|
}
|
|
402
443
|
return Object.getOwnPropertyDescriptor(entity, name);
|
|
403
444
|
} else {
|
|
404
|
-
this.$
|
|
445
|
+
this.$check();
|
|
405
446
|
return Object.getOwnPropertyDescriptor(entity.$data, name);
|
|
406
447
|
}
|
|
407
448
|
},
|
|
408
449
|
|
|
409
450
|
ownKeys: (entity: Entity) => {
|
|
410
|
-
this.$
|
|
451
|
+
this.$check();
|
|
411
452
|
return Object.getOwnPropertyNames(entity).concat(
|
|
412
|
-
Object.getOwnPropertyNames(entity.$data)
|
|
453
|
+
Object.getOwnPropertyNames(entity.$data),
|
|
413
454
|
);
|
|
414
455
|
},
|
|
415
456
|
}) as Entity<T>;
|
|
416
457
|
}
|
|
417
458
|
|
|
418
|
-
|
|
459
|
+
/**
|
|
460
|
+
* Create or retrieve a new entity instance.
|
|
461
|
+
*
|
|
462
|
+
* Note that this will always return an entity, even if the GUID is not found.
|
|
463
|
+
*
|
|
464
|
+
* @param guid An optional GUID to retrieve.
|
|
465
|
+
*/
|
|
466
|
+
public static async factory<E extends Entity>(
|
|
467
|
+
this: {
|
|
468
|
+
new (): E;
|
|
469
|
+
},
|
|
470
|
+
guid?: string,
|
|
471
|
+
): Promise<E & EntityDataType<E>> {
|
|
419
472
|
const entity = new this();
|
|
420
473
|
if (guid != null) {
|
|
421
|
-
const entity = await
|
|
474
|
+
const entity = await (
|
|
475
|
+
this as unknown as EntityConstructor
|
|
476
|
+
).nymph.getEntity(
|
|
422
477
|
{
|
|
423
|
-
class: this as
|
|
478
|
+
class: this as unknown as EntityConstructor,
|
|
424
479
|
},
|
|
425
|
-
{ type: '&', guid }
|
|
480
|
+
{ type: '&', guid },
|
|
426
481
|
);
|
|
427
482
|
if (entity != null) {
|
|
428
|
-
return entity
|
|
483
|
+
return entity as E & EntityDataType<E>;
|
|
429
484
|
}
|
|
430
485
|
}
|
|
431
|
-
return entity
|
|
486
|
+
return entity as E & EntityDataType<E>;
|
|
432
487
|
}
|
|
433
488
|
|
|
434
|
-
|
|
435
|
-
|
|
489
|
+
/**
|
|
490
|
+
* Create a new entity instance.
|
|
491
|
+
*/
|
|
492
|
+
public static factorySync<E extends Entity>(this: {
|
|
493
|
+
new (): E;
|
|
494
|
+
}): E & EntityDataType<E> {
|
|
495
|
+
return new this() as E & EntityDataType<E>;
|
|
436
496
|
}
|
|
437
497
|
|
|
438
|
-
|
|
498
|
+
/**
|
|
499
|
+
* Create a new sleeping reference instance.
|
|
500
|
+
*
|
|
501
|
+
* Sleeping references won't retrieve their data from the database until they
|
|
502
|
+
* are readied with `$wake()` or a parent's `$wakeAll()`.
|
|
503
|
+
*
|
|
504
|
+
* @param reference The Nymph Entity Reference to use to wake.
|
|
505
|
+
* @returns The new instance.
|
|
506
|
+
*/
|
|
507
|
+
public static factoryReference<E extends Entity>(
|
|
508
|
+
this: {
|
|
509
|
+
new (): E;
|
|
510
|
+
},
|
|
511
|
+
reference: EntityReference,
|
|
512
|
+
): E & EntityDataType<E> {
|
|
439
513
|
const entity = new this();
|
|
440
514
|
entity.$referenceSleep(reference);
|
|
441
|
-
return entity
|
|
515
|
+
return entity as E & EntityDataType<E>;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Get an array of strings that **must** be unique across the current etype.
|
|
520
|
+
*
|
|
521
|
+
* When you try to save another entity with any of the same unique strings,
|
|
522
|
+
* Nymph will throw an error.
|
|
523
|
+
*
|
|
524
|
+
* The default implementation of this static method instantiates the entity,
|
|
525
|
+
* assigns all of the given data, then calls `$getUniques` and returns its
|
|
526
|
+
* output. This can have a performance impact if a lot of extra processing
|
|
527
|
+
* happens during any of these steps. You can override this method to
|
|
528
|
+
* calculate the unique strings faster, but you must return the same strings
|
|
529
|
+
* that would be returned by `$getUniques`.
|
|
530
|
+
*
|
|
531
|
+
* @returns Resolves to an array of entity's unique constraint strings.
|
|
532
|
+
*/
|
|
533
|
+
public static async getUniques({
|
|
534
|
+
guid,
|
|
535
|
+
cdate,
|
|
536
|
+
mdate,
|
|
537
|
+
tags,
|
|
538
|
+
data,
|
|
539
|
+
sdata,
|
|
540
|
+
}: {
|
|
541
|
+
guid?: string;
|
|
542
|
+
cdate?: number;
|
|
543
|
+
mdate?: number;
|
|
544
|
+
tags: string[];
|
|
545
|
+
data: EntityData;
|
|
546
|
+
sdata?: SerializedEntityData;
|
|
547
|
+
}) {
|
|
548
|
+
const entity = new this();
|
|
549
|
+
if (guid != null) {
|
|
550
|
+
entity.guid = guid;
|
|
551
|
+
}
|
|
552
|
+
if (cdate != null) {
|
|
553
|
+
entity.cdate = cdate;
|
|
554
|
+
}
|
|
555
|
+
if (mdate != null) {
|
|
556
|
+
entity.mdate = mdate;
|
|
557
|
+
}
|
|
558
|
+
entity.tags = tags;
|
|
559
|
+
entity.$putData(data, sdata);
|
|
560
|
+
return await entity.$getUniques();
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Get a string for full text search for one of an entity's properties.
|
|
565
|
+
*
|
|
566
|
+
* The result will be tokenized and stored as the full text search index for
|
|
567
|
+
* use with "search" clauses.
|
|
568
|
+
*
|
|
569
|
+
* Return null to not include any tokens in the full text search storage.
|
|
570
|
+
*
|
|
571
|
+
* You shouldn't use any other entity data to transform the text, as it won't
|
|
572
|
+
* always be available. This function is meant for things like stripping HTML
|
|
573
|
+
* tags.
|
|
574
|
+
*
|
|
575
|
+
* @returns By default, returns the value if it is a string.
|
|
576
|
+
*/
|
|
577
|
+
public static getFTSText(_name: string, value: any) {
|
|
578
|
+
if (typeof value === 'string') {
|
|
579
|
+
return value;
|
|
580
|
+
}
|
|
581
|
+
return null;
|
|
442
582
|
}
|
|
443
583
|
|
|
444
584
|
public toJSON() {
|
|
@@ -453,17 +593,29 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
453
593
|
tags: [...this.tags],
|
|
454
594
|
data: {},
|
|
455
595
|
};
|
|
456
|
-
const
|
|
457
|
-
|
|
596
|
+
for (const key of [
|
|
597
|
+
...Object.keys(this.$dataStore),
|
|
598
|
+
...Object.keys(this.$sdata),
|
|
599
|
+
]) {
|
|
458
600
|
if (this.$privateData.indexOf(key) === -1) {
|
|
459
|
-
obj.data[key] = entitiesToReferences(data[key]);
|
|
601
|
+
obj.data[key] = entitiesToReferences(this.$data[key], true);
|
|
460
602
|
}
|
|
461
603
|
}
|
|
462
604
|
return obj;
|
|
463
605
|
}
|
|
464
606
|
|
|
607
|
+
public $getGuaranteedGUID() {
|
|
608
|
+
if (this.guid !== null) {
|
|
609
|
+
return this.guid;
|
|
610
|
+
}
|
|
611
|
+
if (this.$guaranteedGUID == null) {
|
|
612
|
+
this.$guaranteedGUID = guid();
|
|
613
|
+
}
|
|
614
|
+
return this.$guaranteedGUID;
|
|
615
|
+
}
|
|
616
|
+
|
|
465
617
|
public $addTag(...tags: string[]) {
|
|
466
|
-
this.$
|
|
618
|
+
this.$check();
|
|
467
619
|
|
|
468
620
|
if (tags.length < 1) {
|
|
469
621
|
return;
|
|
@@ -472,7 +624,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
472
624
|
}
|
|
473
625
|
|
|
474
626
|
public $arraySearch(array: any[], strict = false) {
|
|
475
|
-
this.$
|
|
627
|
+
this.$check();
|
|
476
628
|
|
|
477
629
|
if (!Array.isArray(array)) {
|
|
478
630
|
return -1;
|
|
@@ -487,9 +639,9 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
487
639
|
}
|
|
488
640
|
|
|
489
641
|
public $clearCache() {
|
|
490
|
-
this.$
|
|
642
|
+
this.$check();
|
|
491
643
|
|
|
492
|
-
this.$putData(this.$getData(), this.$getSData());
|
|
644
|
+
this.$putData(this.$getData(false, true), this.$getSData());
|
|
493
645
|
}
|
|
494
646
|
|
|
495
647
|
public $getClientEnabledMethods() {
|
|
@@ -497,13 +649,13 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
497
649
|
}
|
|
498
650
|
|
|
499
651
|
public async $delete(): Promise<boolean> {
|
|
500
|
-
this.$
|
|
652
|
+
this.$check();
|
|
501
653
|
|
|
502
654
|
return await this.$nymph.deleteEntity(this);
|
|
503
655
|
}
|
|
504
656
|
|
|
505
657
|
public $equals(object: any) {
|
|
506
|
-
this.$
|
|
658
|
+
this.$check();
|
|
507
659
|
|
|
508
660
|
if (!(object instanceof Entity)) {
|
|
509
661
|
return false;
|
|
@@ -513,22 +665,24 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
513
665
|
return false;
|
|
514
666
|
}
|
|
515
667
|
}
|
|
516
|
-
if (object.constructor !== this.constructor) {
|
|
517
|
-
return false;
|
|
518
|
-
}
|
|
519
668
|
if (object.cdate !== this.cdate) {
|
|
520
669
|
return false;
|
|
521
670
|
}
|
|
522
671
|
if (object.mdate !== this.mdate) {
|
|
523
672
|
return false;
|
|
524
673
|
}
|
|
525
|
-
const
|
|
526
|
-
const
|
|
674
|
+
const obTags = [...object.tags].sort();
|
|
675
|
+
const myTags = [...this.tags].sort();
|
|
676
|
+
if (!isEqual(obTags, myTags)) {
|
|
677
|
+
return false;
|
|
678
|
+
}
|
|
679
|
+
const obData = object.$getData(true, true);
|
|
680
|
+
const myData = this.$getData(true, true);
|
|
527
681
|
return isEqual(obData, myData);
|
|
528
682
|
}
|
|
529
683
|
|
|
530
|
-
public $getData(includeSData = false) {
|
|
531
|
-
this.$
|
|
684
|
+
public $getData(includeSData = false, referenceOnlyExisting?: boolean) {
|
|
685
|
+
this.$check();
|
|
532
686
|
|
|
533
687
|
if (includeSData) {
|
|
534
688
|
// Access all the serialized properties to initialize them.
|
|
@@ -536,34 +690,79 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
536
690
|
const _unused: any = (this as any)[key];
|
|
537
691
|
}
|
|
538
692
|
}
|
|
539
|
-
return entitiesToReferences({ ...this.$dataStore });
|
|
693
|
+
return entitiesToReferences({ ...this.$dataStore }, referenceOnlyExisting);
|
|
540
694
|
}
|
|
541
695
|
|
|
542
696
|
public $getSData() {
|
|
543
|
-
this.$
|
|
697
|
+
this.$check();
|
|
544
698
|
|
|
545
699
|
return this.$sdata;
|
|
546
700
|
}
|
|
547
701
|
|
|
548
|
-
public $
|
|
549
|
-
|
|
702
|
+
public async $getUniques(): Promise<string[]> {
|
|
703
|
+
return [];
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
public $getOriginalAcValues(): ACProperties {
|
|
707
|
+
this.$check();
|
|
708
|
+
|
|
709
|
+
return this.$originalAcValues ?? this.$getCurrentAcValues();
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
public $getCurrentAcValues(): ACProperties {
|
|
713
|
+
this.$check();
|
|
550
714
|
|
|
551
|
-
return
|
|
552
|
-
this.$
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
715
|
+
return {
|
|
716
|
+
user: this.$getAcUid(),
|
|
717
|
+
group: this.$getAcGid(),
|
|
718
|
+
acUser: this.$data.acUser ?? null,
|
|
719
|
+
acGroup: this.$data.acGroup ?? null,
|
|
720
|
+
acOther: this.$data.acOther ?? null,
|
|
721
|
+
acRead: this.$getAcReadIds(),
|
|
722
|
+
acWrite: this.$getAcWriteIds(),
|
|
723
|
+
acFull: this.$getAcFullIds(),
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
public $getAcUid() {
|
|
728
|
+
if ('user' in this.$sdata) {
|
|
729
|
+
const userValue = JSON.parse(this.$sdata.user);
|
|
730
|
+
if (
|
|
731
|
+
Array.isArray(userValue) &&
|
|
732
|
+
userValue[0] === 'nymph_entity_reference'
|
|
733
|
+
) {
|
|
734
|
+
return userValue[1];
|
|
561
735
|
}
|
|
562
|
-
|
|
736
|
+
}
|
|
737
|
+
return (this.$data.user?.guid ?? null) as string | null;
|
|
738
|
+
}
|
|
739
|
+
public $getAcGid() {
|
|
740
|
+
if ('group' in this.$sdata) {
|
|
741
|
+
const groupValue = JSON.parse(this.$sdata.group);
|
|
742
|
+
if (
|
|
743
|
+
Array.isArray(groupValue) &&
|
|
744
|
+
groupValue[0] === 'nymph_entity_reference'
|
|
745
|
+
) {
|
|
746
|
+
return groupValue[1];
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
return (this.$data.group?.guid ?? null) as string | null;
|
|
750
|
+
}
|
|
751
|
+
public $getAcReadIds() {
|
|
752
|
+
return (this.$data.acRead?.map((entity: EntityInterface) => entity.guid) ??
|
|
753
|
+
null) as (string | null)[] | null;
|
|
754
|
+
}
|
|
755
|
+
public $getAcWriteIds() {
|
|
756
|
+
return (this.$data.acWrite?.map((entity: EntityInterface) => entity.guid) ??
|
|
757
|
+
null) as (string | null)[] | null;
|
|
758
|
+
}
|
|
759
|
+
public $getAcFullIds() {
|
|
760
|
+
return (this.$data.acFull?.map((entity: EntityInterface) => entity.guid) ??
|
|
761
|
+
null) as (string | null)[] | null;
|
|
563
762
|
}
|
|
564
763
|
|
|
565
764
|
public $getValidatable() {
|
|
566
|
-
this.$
|
|
765
|
+
this.$check();
|
|
567
766
|
|
|
568
767
|
// Access all the serialized properties to initialize them.
|
|
569
768
|
for (const key in this.$sdata) {
|
|
@@ -579,13 +778,13 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
579
778
|
}
|
|
580
779
|
|
|
581
780
|
public $getTags() {
|
|
582
|
-
this.$
|
|
781
|
+
this.$check();
|
|
583
782
|
|
|
584
783
|
return this.tags;
|
|
585
784
|
}
|
|
586
785
|
|
|
587
786
|
public $hasTag(...tags: string[]) {
|
|
588
|
-
this.$
|
|
787
|
+
this.$check();
|
|
589
788
|
|
|
590
789
|
if (!tags.length) {
|
|
591
790
|
return false;
|
|
@@ -607,27 +806,35 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
607
806
|
return false;
|
|
608
807
|
}
|
|
609
808
|
|
|
610
|
-
if (this
|
|
809
|
+
if (this === object) {
|
|
810
|
+
return true;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
if (this.guid != null || object.guid != null) {
|
|
611
814
|
return this.guid === object.guid;
|
|
612
815
|
}
|
|
613
816
|
|
|
614
|
-
this.$
|
|
817
|
+
this.$check();
|
|
615
818
|
if (typeof object.$getData !== 'function') {
|
|
616
819
|
return false;
|
|
617
820
|
} else {
|
|
618
|
-
const
|
|
619
|
-
const
|
|
821
|
+
const obTags = [...object.tags].sort();
|
|
822
|
+
const myTags = [...this.tags].sort();
|
|
823
|
+
if (!isEqual(obTags, myTags)) {
|
|
824
|
+
return false;
|
|
825
|
+
}
|
|
826
|
+
const obData = object.$getData(true, true);
|
|
827
|
+
const myData = this.$getData(true, true);
|
|
620
828
|
return isEqual(obData, myData);
|
|
621
829
|
}
|
|
622
830
|
}
|
|
623
831
|
|
|
624
832
|
public $jsonAcceptData(input: EntityJson, allowConflict = false) {
|
|
625
|
-
|
|
626
|
-
this.$referenceWake();
|
|
833
|
+
this.$check();
|
|
627
834
|
|
|
628
835
|
if (this.guid != input.guid) {
|
|
629
836
|
throw new EntityConflictError(
|
|
630
|
-
'Tried to accept JSON input for the wrong entity.'
|
|
837
|
+
'Tried to accept JSON input for the wrong entity.',
|
|
631
838
|
);
|
|
632
839
|
}
|
|
633
840
|
|
|
@@ -652,7 +859,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
652
859
|
this.$addTag(...protectedTags, ...tags);
|
|
653
860
|
|
|
654
861
|
// Accept the data.
|
|
655
|
-
const data = input.data;
|
|
862
|
+
const data = { ...input.data };
|
|
656
863
|
const privateData: EntityData = {};
|
|
657
864
|
for (const name of this.$privateData) {
|
|
658
865
|
if (name in this.$data) {
|
|
@@ -665,12 +872,27 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
665
872
|
|
|
666
873
|
const protectedData: EntityData = {};
|
|
667
874
|
const protectedProps = [...this.$protectedData];
|
|
668
|
-
if (
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
protectedProps.push('
|
|
673
|
-
protectedProps.push('
|
|
875
|
+
if (this.$nymph.tilmeld) {
|
|
876
|
+
protectedProps.push('acUser');
|
|
877
|
+
protectedProps.push('acGroup');
|
|
878
|
+
protectedProps.push('acOther');
|
|
879
|
+
protectedProps.push('acRead');
|
|
880
|
+
protectedProps.push('acWrite');
|
|
881
|
+
protectedProps.push('acFull');
|
|
882
|
+
if (
|
|
883
|
+
((this.constructor as EntityConstructor).class !== 'User' &&
|
|
884
|
+
(this.constructor as EntityConstructor).class !== 'Group') ||
|
|
885
|
+
!(this.$nymph.tilmeld as any).currentUser ||
|
|
886
|
+
(!(this.$nymph.tilmeld as any).currentUser.abilities?.includes(
|
|
887
|
+
'tilmeld/admin',
|
|
888
|
+
) &&
|
|
889
|
+
!(this.$nymph.tilmeld as any).currentUser.abilities?.includes(
|
|
890
|
+
'system/admin',
|
|
891
|
+
))
|
|
892
|
+
) {
|
|
893
|
+
protectedProps.push('user');
|
|
894
|
+
protectedProps.push('group');
|
|
895
|
+
}
|
|
674
896
|
}
|
|
675
897
|
for (const name of protectedProps) {
|
|
676
898
|
if (name in this.$data) {
|
|
@@ -683,7 +905,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
683
905
|
|
|
684
906
|
let nonAllowlistData: EntityData = {};
|
|
685
907
|
if (this.$allowlistData != null) {
|
|
686
|
-
nonAllowlistData = { ...this.$getData(true) };
|
|
908
|
+
nonAllowlistData = { ...this.$getData(true, true) };
|
|
687
909
|
for (const name of this.$allowlistData) {
|
|
688
910
|
delete nonAllowlistData[name];
|
|
689
911
|
}
|
|
@@ -703,11 +925,11 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
703
925
|
}
|
|
704
926
|
|
|
705
927
|
public $jsonAcceptPatch(patch: EntityPatch, allowConflict = false) {
|
|
706
|
-
this.$
|
|
928
|
+
this.$check();
|
|
707
929
|
|
|
708
930
|
if (this.guid != patch.guid) {
|
|
709
931
|
throw new EntityConflictError(
|
|
710
|
-
'Tried to accept JSON patch for the wrong entity.'
|
|
932
|
+
'Tried to accept JSON patch for the wrong entity.',
|
|
711
933
|
);
|
|
712
934
|
}
|
|
713
935
|
|
|
@@ -719,11 +941,35 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
719
941
|
}
|
|
720
942
|
this.mdate = patch.mdate;
|
|
721
943
|
|
|
944
|
+
const protectedProps = [...this.$protectedData];
|
|
945
|
+
if (this.$nymph.tilmeld) {
|
|
946
|
+
protectedProps.push('acUser');
|
|
947
|
+
protectedProps.push('acGroup');
|
|
948
|
+
protectedProps.push('acOther');
|
|
949
|
+
protectedProps.push('acRead');
|
|
950
|
+
protectedProps.push('acWrite');
|
|
951
|
+
protectedProps.push('acFull');
|
|
952
|
+
if (
|
|
953
|
+
((this.constructor as EntityConstructor).class !== 'User' &&
|
|
954
|
+
(this.constructor as EntityConstructor).class !== 'Group') ||
|
|
955
|
+
!(this.$nymph.tilmeld as any).currentUser ||
|
|
956
|
+
(!(this.$nymph.tilmeld as any).currentUser.abilities?.includes(
|
|
957
|
+
'tilmeld/admin',
|
|
958
|
+
) &&
|
|
959
|
+
!(this.$nymph.tilmeld as any).currentUser.abilities?.includes(
|
|
960
|
+
'system/admin',
|
|
961
|
+
))
|
|
962
|
+
) {
|
|
963
|
+
protectedProps.push('user');
|
|
964
|
+
protectedProps.push('group');
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
722
968
|
for (const name in patch.set) {
|
|
723
969
|
if (
|
|
724
970
|
(this.$allowlistData != null &&
|
|
725
971
|
this.$allowlistData.indexOf(name) === -1) ||
|
|
726
|
-
|
|
972
|
+
protectedProps.indexOf(name) !== -1 ||
|
|
727
973
|
this.$privateData.indexOf(name) !== -1
|
|
728
974
|
) {
|
|
729
975
|
continue;
|
|
@@ -731,7 +977,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
731
977
|
(this.$data as any)[name] = referencesToEntities(
|
|
732
978
|
patch.set[name],
|
|
733
979
|
this.$nymph,
|
|
734
|
-
this.$skipAc
|
|
980
|
+
this.$skipAc,
|
|
735
981
|
);
|
|
736
982
|
}
|
|
737
983
|
|
|
@@ -739,7 +985,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
739
985
|
if (
|
|
740
986
|
(this.$allowlistData != null &&
|
|
741
987
|
this.$allowlistData.indexOf(name) === -1) ||
|
|
742
|
-
|
|
988
|
+
protectedProps.indexOf(name) !== -1 ||
|
|
743
989
|
this.$privateData.indexOf(name) !== -1
|
|
744
990
|
) {
|
|
745
991
|
continue;
|
|
@@ -770,8 +1016,12 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
770
1016
|
}
|
|
771
1017
|
}
|
|
772
1018
|
|
|
773
|
-
public $putData(
|
|
774
|
-
|
|
1019
|
+
public $putData(
|
|
1020
|
+
data: EntityData,
|
|
1021
|
+
sdata?: SerializedEntityData,
|
|
1022
|
+
_source?: 'server',
|
|
1023
|
+
) {
|
|
1024
|
+
this.$check();
|
|
775
1025
|
|
|
776
1026
|
const mySdata = sdata ?? this.$getSData();
|
|
777
1027
|
for (const name in data) {
|
|
@@ -784,13 +1034,13 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
784
1034
|
(this.$dataStore as any)[name] = referencesToEntities(
|
|
785
1035
|
data[name],
|
|
786
1036
|
this.$nymph,
|
|
787
|
-
this.$skipAc
|
|
1037
|
+
this.$skipAc,
|
|
788
1038
|
);
|
|
789
1039
|
}
|
|
790
1040
|
this.$sdata = mySdata;
|
|
791
1041
|
// Set original AC values if not set..
|
|
792
1042
|
if (this.$originalAcValues == null) {
|
|
793
|
-
this.$originalAcValues = this.$
|
|
1043
|
+
this.$originalAcValues = this.$getCurrentAcValues();
|
|
794
1044
|
}
|
|
795
1045
|
}
|
|
796
1046
|
|
|
@@ -807,14 +1057,14 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
807
1057
|
) {
|
|
808
1058
|
throw new InvalidParametersError(
|
|
809
1059
|
'referenceSleep expects parameter 1 to be a valid Nymph entity ' +
|
|
810
|
-
'reference.'
|
|
1060
|
+
'reference.',
|
|
811
1061
|
);
|
|
812
1062
|
}
|
|
813
1063
|
const thisClass: string = (this.constructor as any).class;
|
|
814
1064
|
if (reference[2] !== thisClass) {
|
|
815
1065
|
throw new InvalidParametersError(
|
|
816
1066
|
'referenceSleep can only be called with an entity reference of the ' +
|
|
817
|
-
`same class. Given class: ${reference[2]}; this class: ${thisClass}
|
|
1067
|
+
`same class. Given class: ${reference[2]}; this class: ${thisClass}.`,
|
|
818
1068
|
);
|
|
819
1069
|
}
|
|
820
1070
|
this.$isASleepingReference = true;
|
|
@@ -822,49 +1072,141 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
822
1072
|
this.$sleepingReference = reference;
|
|
823
1073
|
}
|
|
824
1074
|
|
|
1075
|
+
/**
|
|
1076
|
+
* Check if this is a sleeping reference and throw an error if so.
|
|
1077
|
+
*/
|
|
1078
|
+
protected $check() {
|
|
1079
|
+
if (this.$isASleepingReference || this.$sleepingReference != null) {
|
|
1080
|
+
throw new EntityIsSleepingReferenceError(
|
|
1081
|
+
'This entity is in a sleeping reference state. You must use .$wake() to wake it.',
|
|
1082
|
+
);
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
/**
|
|
1087
|
+
* Check if this is a sleeping reference.
|
|
1088
|
+
*/
|
|
1089
|
+
public $asleep() {
|
|
1090
|
+
return this.$isASleepingReference || this.$sleepingReference != null;
|
|
1091
|
+
}
|
|
1092
|
+
|
|
825
1093
|
/**
|
|
826
1094
|
* Wake from a sleeping reference.
|
|
827
|
-
*
|
|
828
|
-
* @returns True on success, false on failure.
|
|
829
1095
|
*/
|
|
830
|
-
|
|
1096
|
+
public $wake() {
|
|
831
1097
|
if (!this.$isASleepingReference || this.$sleepingReference == null) {
|
|
832
|
-
|
|
1098
|
+
this.$wakePromise = null;
|
|
1099
|
+
return Promise.resolve(this);
|
|
833
1100
|
}
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
skipAc: this.$skipAc,
|
|
839
|
-
},
|
|
840
|
-
{ type: '&', guid: this.$sleepingReference[1] }
|
|
841
|
-
);
|
|
842
|
-
if (entity == null) {
|
|
843
|
-
return false;
|
|
1101
|
+
if (this.$sleepingReference?.[1] == null) {
|
|
1102
|
+
throw new InvalidStateError(
|
|
1103
|
+
'Tried to wake a sleeping reference with no GUID.',
|
|
1104
|
+
);
|
|
844
1105
|
}
|
|
845
|
-
this.$
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
1106
|
+
if (!this.$wakePromise) {
|
|
1107
|
+
const EntityClass = this.$nymph.getEntityClass(
|
|
1108
|
+
this.$sleepingReference[2],
|
|
1109
|
+
);
|
|
1110
|
+
this.$wakePromise = this.$nymph
|
|
1111
|
+
.getEntity(
|
|
1112
|
+
{
|
|
1113
|
+
class: EntityClass,
|
|
1114
|
+
skipAc: this.$skipAc,
|
|
1115
|
+
},
|
|
1116
|
+
{ type: '&', guid: this.$sleepingReference[1] },
|
|
1117
|
+
)
|
|
1118
|
+
.then((entity) => {
|
|
1119
|
+
if (entity == null || entity.guid == null) {
|
|
1120
|
+
return Promise.reject(
|
|
1121
|
+
new InvalidStateError(
|
|
1122
|
+
'The sleeping reference could not be retrieved.',
|
|
1123
|
+
),
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1126
|
+
this.$isASleepingReference = false;
|
|
1127
|
+
this.$sleepingReference = null;
|
|
1128
|
+
this.guid = entity.guid;
|
|
1129
|
+
this.tags = entity.tags;
|
|
1130
|
+
this.cdate = entity.cdate;
|
|
1131
|
+
this.mdate = entity.mdate;
|
|
1132
|
+
this.$putData(
|
|
1133
|
+
entity.$getData(false, true),
|
|
1134
|
+
entity.$getSData(),
|
|
1135
|
+
'server',
|
|
1136
|
+
);
|
|
1137
|
+
|
|
1138
|
+
return this;
|
|
1139
|
+
})
|
|
1140
|
+
.finally(() => {
|
|
1141
|
+
this.$wakePromise = null;
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1144
|
+
return this.$wakePromise;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
public $wakeAll(level?: number) {
|
|
1148
|
+
return new Promise((resolve, reject) => {
|
|
1149
|
+
// Run this once this entity is wake.
|
|
1150
|
+
const wakeProps = () => {
|
|
1151
|
+
let newLevel;
|
|
1152
|
+
// If level is undefined, keep going forever, otherwise, stop once we've
|
|
1153
|
+
// gone deep enough.
|
|
1154
|
+
if (level !== undefined) {
|
|
1155
|
+
newLevel = level - 1;
|
|
1156
|
+
}
|
|
1157
|
+
if (newLevel !== undefined && newLevel < 0) {
|
|
1158
|
+
resolve(this);
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
const promises = [];
|
|
1162
|
+
// Go through data looking for entities to wake.
|
|
1163
|
+
for (let [key, value] of Object.entries(this.$data)) {
|
|
1164
|
+
if (value instanceof Entity && value.$isASleepingReference) {
|
|
1165
|
+
promises.push(value.$wakeAll(newLevel));
|
|
1166
|
+
} else if (Array.isArray(value)) {
|
|
1167
|
+
for (let i = 0; i < value.length; i++) {
|
|
1168
|
+
if (
|
|
1169
|
+
value[i] instanceof Entity &&
|
|
1170
|
+
value[i].$isASleepingReference
|
|
1171
|
+
) {
|
|
1172
|
+
promises.push(value[i].$wakeAll(newLevel));
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
if (promises.length) {
|
|
1178
|
+
Promise.all(promises).then(
|
|
1179
|
+
() => resolve(this),
|
|
1180
|
+
(errObj) => reject(errObj),
|
|
1181
|
+
);
|
|
1182
|
+
} else {
|
|
1183
|
+
resolve(this);
|
|
1184
|
+
}
|
|
1185
|
+
};
|
|
1186
|
+
|
|
1187
|
+
if (this.$isASleepingReference) {
|
|
1188
|
+
this.$wake().then(wakeProps, (errObj) => reject(errObj));
|
|
1189
|
+
} else {
|
|
1190
|
+
wakeProps();
|
|
1191
|
+
}
|
|
1192
|
+
}) as Promise<Entity<T>>;
|
|
853
1193
|
}
|
|
854
1194
|
|
|
855
1195
|
public async $refresh() {
|
|
856
|
-
this.$
|
|
1196
|
+
await this.$wake();
|
|
857
1197
|
|
|
858
1198
|
if (this.guid == null) {
|
|
859
1199
|
return false;
|
|
860
1200
|
}
|
|
861
1201
|
const refresh = await this.$nymph.getEntity(
|
|
862
1202
|
{
|
|
863
|
-
class: this.
|
|
1203
|
+
class: this.$nymph.getEntityClass(
|
|
1204
|
+
this.constructor as EntityConstructor,
|
|
1205
|
+
),
|
|
864
1206
|
skipCache: true,
|
|
865
1207
|
skipAc: this.$skipAc,
|
|
866
1208
|
},
|
|
867
|
-
{ type: '&', guid: this.guid }
|
|
1209
|
+
{ type: '&', guid: this.guid },
|
|
868
1210
|
);
|
|
869
1211
|
if (refresh == null) {
|
|
870
1212
|
return 0;
|
|
@@ -872,32 +1214,32 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
872
1214
|
this.tags = refresh.tags;
|
|
873
1215
|
this.cdate = refresh.cdate;
|
|
874
1216
|
this.mdate = refresh.mdate;
|
|
875
|
-
this.$putData(refresh.$getData(), refresh.$getSData());
|
|
1217
|
+
this.$putData(refresh.$getData(false, true), refresh.$getSData(), 'server');
|
|
876
1218
|
return true;
|
|
877
1219
|
}
|
|
878
1220
|
|
|
879
1221
|
public $removeTag(...tags: string[]) {
|
|
880
|
-
this.$
|
|
1222
|
+
this.$check();
|
|
881
1223
|
|
|
882
1224
|
this.tags = difference(this.tags, tags);
|
|
883
1225
|
}
|
|
884
1226
|
|
|
885
1227
|
public async $save(): Promise<boolean> {
|
|
886
|
-
this.$
|
|
1228
|
+
await this.$wake();
|
|
887
1229
|
|
|
888
1230
|
return await this.$nymph.saveEntity(this);
|
|
889
1231
|
}
|
|
890
1232
|
|
|
891
|
-
public $toReference() {
|
|
1233
|
+
public $toReference(existingOnly?: boolean) {
|
|
892
1234
|
if (this.$isASleepingReference && this.$sleepingReference != null) {
|
|
893
1235
|
return this.$sleepingReference;
|
|
894
1236
|
}
|
|
895
|
-
if (this.guid == null) {
|
|
1237
|
+
if (existingOnly && this.guid == null) {
|
|
896
1238
|
return this;
|
|
897
1239
|
}
|
|
898
1240
|
return [
|
|
899
1241
|
'nymph_entity_reference',
|
|
900
|
-
this
|
|
1242
|
+
this.$getGuaranteedGUID(),
|
|
901
1243
|
(this.constructor as any).class as string,
|
|
902
1244
|
] as EntityReference;
|
|
903
1245
|
}
|