@nymphjs/nymph 1.0.0-beta.4 → 1.0.0-beta.41
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 +195 -0
- package/README.md +12 -12
- package/dist/Entity.d.ts +24 -3
- package/dist/Entity.js +194 -86
- package/dist/Entity.js.map +1 -1
- package/dist/Entity.test.js +34 -17
- package/dist/Entity.test.js.map +1 -1
- package/dist/Entity.types.d.ts +3 -1
- package/dist/Nymph.d.ts +22 -7
- package/dist/Nymph.js +214 -68
- package/dist/Nymph.js.map +1 -1
- package/dist/Nymph.types.d.ts +18 -5
- package/dist/Nymph.types.js +1 -1
- package/dist/Nymph.types.js.map +1 -1
- package/dist/driver/NymphDriver.d.ts +1 -22
- package/dist/driver/NymphDriver.js +2 -16
- package/dist/driver/NymphDriver.js.map +1 -1
- package/dist/driver/testArtifacts.js +208 -159
- package/dist/driver/testArtifacts.js.map +1 -1
- 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/index.d.ts +1 -0
- package/dist/errors/index.js +1 -0
- package/dist/errors/index.js.map +1 -1
- package/dist/testArtifacts.d.ts +10 -3
- package/dist/testArtifacts.js +20 -11
- package/dist/testArtifacts.js.map +1 -1
- package/dist/testMocks.d.ts +10 -7
- package/dist/testMocks.js +22 -10
- package/dist/testMocks.js.map +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +11 -5
- package/dist/utils.js.map +1 -1
- package/package.json +13 -13
- package/src/Entity.test.ts +31 -9
- package/src/Entity.ts +289 -113
- package/src/Entity.types.ts +13 -1
- package/src/Nymph.ts +386 -82
- package/src/Nymph.types.ts +76 -5
- package/src/driver/NymphDriver.ts +19 -85
- package/src/driver/testArtifacts.ts +125 -44
- package/src/errors/InvalidStateError.ts +6 -0
- package/src/errors/index.ts +2 -0
- package/src/testArtifacts.ts +28 -10
- package/src/testMocks.ts +38 -21
- package/src/utils.ts +21 -7
- package/typedoc.json +4 -0
package/src/Entity.ts
CHANGED
|
@@ -11,7 +11,12 @@ import {
|
|
|
11
11
|
EntityReference,
|
|
12
12
|
SerializedEntityData,
|
|
13
13
|
} from './Entity.types';
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
EntityConflictError,
|
|
16
|
+
EntityIsSleepingReferenceError,
|
|
17
|
+
InvalidParametersError,
|
|
18
|
+
InvalidStateError,
|
|
19
|
+
} from './errors';
|
|
15
20
|
import {
|
|
16
21
|
entitiesToReferences,
|
|
17
22
|
referencesToEntities,
|
|
@@ -45,9 +50,9 @@ import {
|
|
|
45
50
|
*
|
|
46
51
|
* Some notes about $equals() and $is(), the replacements for "==":
|
|
47
52
|
*
|
|
48
|
-
* The == operator will likely not give you the result you want, since
|
|
49
|
-
*
|
|
50
|
-
*
|
|
53
|
+
* The == operator will likely not give you the result you want, since two
|
|
54
|
+
* instances of the same entity will fail that check, even though they represent
|
|
55
|
+
* the same data in the database.
|
|
51
56
|
*
|
|
52
57
|
* $equals() performs a more strict comparison of the entity to another. Use
|
|
53
58
|
* $equals() instead of the == operator when you want to check both the entities
|
|
@@ -56,8 +61,7 @@ import {
|
|
|
56
61
|
*
|
|
57
62
|
* - They must be entities.
|
|
58
63
|
* - They must have equal GUIDs, or both must have no GUID.
|
|
59
|
-
* -
|
|
60
|
-
* - Their data must be equal.
|
|
64
|
+
* - Their data and tags must be equal.
|
|
61
65
|
*
|
|
62
66
|
* $is() performs a less strict comparison of the entity to another. Use $is()
|
|
63
67
|
* instead of the == operator when the entity's data may have been changed, but
|
|
@@ -66,7 +70,7 @@ import {
|
|
|
66
70
|
*
|
|
67
71
|
* - They must be entities.
|
|
68
72
|
* - They must have equal GUIDs, or both must have no GUID.
|
|
69
|
-
* - If they have no GUIDs, their data must be equal.
|
|
73
|
+
* - If they have no GUIDs, their data and tags must be equal.
|
|
70
74
|
*
|
|
71
75
|
* Some notes about saving entities in other entity's properties:
|
|
72
76
|
*
|
|
@@ -137,6 +141,10 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
137
141
|
* The reference to use to wake.
|
|
138
142
|
*/
|
|
139
143
|
protected $sleepingReference: EntityReference | null = null;
|
|
144
|
+
/**
|
|
145
|
+
* A promise that resolved when the entity's data is wake.
|
|
146
|
+
*/
|
|
147
|
+
protected $wakePromise: Promise<Entity<T>> | null = null;
|
|
140
148
|
/**
|
|
141
149
|
* Properties that will not be serialized into JSON with toJSON(). This
|
|
142
150
|
* can be considered a denylist, because these properties will not be set
|
|
@@ -146,6 +154,18 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
146
154
|
* also listed in searchRestrictedData.
|
|
147
155
|
*/
|
|
148
156
|
protected $privateData: string[] = [];
|
|
157
|
+
/**
|
|
158
|
+
* Whether this entity should publish changes to PubSub servers.
|
|
159
|
+
*/
|
|
160
|
+
public static pubSubEnabled = true;
|
|
161
|
+
/**
|
|
162
|
+
* Whether this entity should be accessible on the frontend through the REST
|
|
163
|
+
* server.
|
|
164
|
+
*
|
|
165
|
+
* If this is false, any request from the client that attempts to use this
|
|
166
|
+
* entity will fail.
|
|
167
|
+
*/
|
|
168
|
+
public static restEnabled = true;
|
|
149
169
|
/**
|
|
150
170
|
* Properties that will not be searchable from the frontend. If the frontend
|
|
151
171
|
* includes any of these properties in any of their clauses, they will be
|
|
@@ -157,8 +177,20 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
157
177
|
* be visible on the frontend, unlike $privateData, but any changes to them
|
|
158
178
|
* that come from the frontend will be ignored.
|
|
159
179
|
*
|
|
160
|
-
* In addition to what's listed here, the
|
|
161
|
-
* be
|
|
180
|
+
* In addition to what's listed here, all of the access control properties
|
|
181
|
+
* will be included when Tilmeld is being used. These are:
|
|
182
|
+
*
|
|
183
|
+
* - acUser
|
|
184
|
+
* - acGroup
|
|
185
|
+
* - acOther
|
|
186
|
+
* - acRead
|
|
187
|
+
* - acWrite
|
|
188
|
+
* - acFull
|
|
189
|
+
* - user
|
|
190
|
+
* - group
|
|
191
|
+
*
|
|
192
|
+
* You should modify these through client enabled methods or the $save method
|
|
193
|
+
* instead, for safety.
|
|
162
194
|
*/
|
|
163
195
|
protected $protectedData: string[] = [];
|
|
164
196
|
/**
|
|
@@ -199,20 +231,19 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
199
231
|
private $originalAcValues: ACProperties | null = null;
|
|
200
232
|
|
|
201
233
|
/**
|
|
202
|
-
*
|
|
203
|
-
* @param guid The ID of the entity to load, undefined for a new entity.
|
|
234
|
+
* Initialize an entity.
|
|
204
235
|
*/
|
|
205
|
-
public constructor(
|
|
236
|
+
public constructor() {
|
|
206
237
|
this.$nymph = (this.constructor as EntityConstructor).nymph;
|
|
207
238
|
this.$dataHandler = {
|
|
208
239
|
has: (data: EntityData, name: string) => {
|
|
209
|
-
this.$
|
|
240
|
+
this.$check();
|
|
210
241
|
|
|
211
242
|
return name in data || name in this.$sdata;
|
|
212
243
|
},
|
|
213
244
|
|
|
214
245
|
get: (data: EntityData, name: string) => {
|
|
215
|
-
this.$
|
|
246
|
+
this.$check();
|
|
216
247
|
|
|
217
248
|
if (this.$sdata.hasOwnProperty(name)) {
|
|
218
249
|
data[name] = referencesToEntities(
|
|
@@ -229,7 +260,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
229
260
|
},
|
|
230
261
|
|
|
231
262
|
set: (data: EntityData, name: string, value: any) => {
|
|
232
|
-
this.$
|
|
263
|
+
this.$check();
|
|
233
264
|
|
|
234
265
|
if (this.$sdata.hasOwnProperty(name)) {
|
|
235
266
|
delete this.$sdata[name];
|
|
@@ -239,7 +270,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
239
270
|
},
|
|
240
271
|
|
|
241
272
|
deleteProperty: (data: EntityData, name: string) => {
|
|
242
|
-
this.$
|
|
273
|
+
this.$check();
|
|
243
274
|
|
|
244
275
|
if (this.$sdata.hasOwnProperty(name)) {
|
|
245
276
|
return delete this.$sdata[name];
|
|
@@ -255,7 +286,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
255
286
|
name: string,
|
|
256
287
|
descriptor: PropertyDescriptor
|
|
257
288
|
) => {
|
|
258
|
-
this.$
|
|
289
|
+
this.$check();
|
|
259
290
|
|
|
260
291
|
if (this.$sdata.hasOwnProperty(name)) {
|
|
261
292
|
delete this.$sdata[name];
|
|
@@ -265,7 +296,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
265
296
|
},
|
|
266
297
|
|
|
267
298
|
getOwnPropertyDescriptor: (data: EntityData, name: string) => {
|
|
268
|
-
this.$
|
|
299
|
+
this.$check();
|
|
269
300
|
|
|
270
301
|
if (this.$sdata.hasOwnProperty(name)) {
|
|
271
302
|
data[name] = referencesToEntities(
|
|
@@ -279,7 +310,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
279
310
|
},
|
|
280
311
|
|
|
281
312
|
ownKeys: (data: EntityData) => {
|
|
282
|
-
this.$
|
|
313
|
+
this.$check();
|
|
283
314
|
|
|
284
315
|
return Object.getOwnPropertyNames(data).concat(
|
|
285
316
|
Object.getOwnPropertyNames(this.$sdata)
|
|
@@ -291,20 +322,6 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
291
322
|
|
|
292
323
|
this.$data = new Proxy(this.$dataStore, this.$dataHandler);
|
|
293
324
|
|
|
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
325
|
return new Proxy(this, {
|
|
309
326
|
has: (entity: Entity, name: string) => {
|
|
310
327
|
if (
|
|
@@ -315,7 +332,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
315
332
|
return name in entity;
|
|
316
333
|
}
|
|
317
334
|
|
|
318
|
-
this.$
|
|
335
|
+
this.$check();
|
|
319
336
|
return name in entity.$data;
|
|
320
337
|
},
|
|
321
338
|
|
|
@@ -326,12 +343,12 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
326
343
|
name.substring(0, 1) === '$'
|
|
327
344
|
) {
|
|
328
345
|
if (name === 'tags' || name === 'cdate' || name === 'mdate') {
|
|
329
|
-
this.$
|
|
346
|
+
this.$check();
|
|
330
347
|
}
|
|
331
348
|
return (entity as any)[name];
|
|
332
349
|
}
|
|
333
350
|
|
|
334
|
-
this.$
|
|
351
|
+
this.$check();
|
|
335
352
|
if (name in entity.$data) {
|
|
336
353
|
return entity.$data[name];
|
|
337
354
|
}
|
|
@@ -345,11 +362,11 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
345
362
|
name.substring(0, 1) === '$'
|
|
346
363
|
) {
|
|
347
364
|
if (name === 'tags' || name === 'cdate' || name === 'mdate') {
|
|
348
|
-
this.$
|
|
365
|
+
this.$check();
|
|
349
366
|
}
|
|
350
367
|
(entity as any)[name] = value;
|
|
351
368
|
} else {
|
|
352
|
-
this.$
|
|
369
|
+
this.$check();
|
|
353
370
|
(entity.$data as any)[name] = value;
|
|
354
371
|
}
|
|
355
372
|
return true;
|
|
@@ -359,7 +376,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
359
376
|
if (name in entity) {
|
|
360
377
|
return delete (entity as any)[name];
|
|
361
378
|
} else if (name in entity.$data) {
|
|
362
|
-
this.$
|
|
379
|
+
this.$check();
|
|
363
380
|
return delete entity.$data[name];
|
|
364
381
|
}
|
|
365
382
|
return true;
|
|
@@ -380,11 +397,11 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
380
397
|
name.substring(0, 1) === '$'
|
|
381
398
|
) {
|
|
382
399
|
if (name === 'tags' || name === 'cdate' || name === 'mdate') {
|
|
383
|
-
this.$
|
|
400
|
+
this.$check();
|
|
384
401
|
}
|
|
385
402
|
Object.defineProperty(entity, name, descriptor);
|
|
386
403
|
} else {
|
|
387
|
-
this.$
|
|
404
|
+
this.$check();
|
|
388
405
|
Object.defineProperty(entity.$data, name, descriptor);
|
|
389
406
|
}
|
|
390
407
|
return true;
|
|
@@ -397,17 +414,17 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
397
414
|
name.substring(0, 1) === '$'
|
|
398
415
|
) {
|
|
399
416
|
if (name === 'tags' || name === 'cdate' || name === 'mdate') {
|
|
400
|
-
this.$
|
|
417
|
+
this.$check();
|
|
401
418
|
}
|
|
402
419
|
return Object.getOwnPropertyDescriptor(entity, name);
|
|
403
420
|
} else {
|
|
404
|
-
this.$
|
|
421
|
+
this.$check();
|
|
405
422
|
return Object.getOwnPropertyDescriptor(entity.$data, name);
|
|
406
423
|
}
|
|
407
424
|
},
|
|
408
425
|
|
|
409
426
|
ownKeys: (entity: Entity) => {
|
|
410
|
-
this.$
|
|
427
|
+
this.$check();
|
|
411
428
|
return Object.getOwnPropertyNames(entity).concat(
|
|
412
429
|
Object.getOwnPropertyNames(entity.$data)
|
|
413
430
|
);
|
|
@@ -431,8 +448,8 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
431
448
|
return entity;
|
|
432
449
|
}
|
|
433
450
|
|
|
434
|
-
public static factorySync(
|
|
435
|
-
return new this(
|
|
451
|
+
public static factorySync() {
|
|
452
|
+
return new this();
|
|
436
453
|
}
|
|
437
454
|
|
|
438
455
|
public static factoryReference(reference: EntityReference) {
|
|
@@ -463,7 +480,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
463
480
|
}
|
|
464
481
|
|
|
465
482
|
public $addTag(...tags: string[]) {
|
|
466
|
-
this.$
|
|
483
|
+
this.$check();
|
|
467
484
|
|
|
468
485
|
if (tags.length < 1) {
|
|
469
486
|
return;
|
|
@@ -472,7 +489,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
472
489
|
}
|
|
473
490
|
|
|
474
491
|
public $arraySearch(array: any[], strict = false) {
|
|
475
|
-
this.$
|
|
492
|
+
this.$check();
|
|
476
493
|
|
|
477
494
|
if (!Array.isArray(array)) {
|
|
478
495
|
return -1;
|
|
@@ -487,7 +504,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
487
504
|
}
|
|
488
505
|
|
|
489
506
|
public $clearCache() {
|
|
490
|
-
this.$
|
|
507
|
+
this.$check();
|
|
491
508
|
|
|
492
509
|
this.$putData(this.$getData(), this.$getSData());
|
|
493
510
|
}
|
|
@@ -497,13 +514,13 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
497
514
|
}
|
|
498
515
|
|
|
499
516
|
public async $delete(): Promise<boolean> {
|
|
500
|
-
this.$
|
|
517
|
+
this.$check();
|
|
501
518
|
|
|
502
519
|
return await this.$nymph.deleteEntity(this);
|
|
503
520
|
}
|
|
504
521
|
|
|
505
522
|
public $equals(object: any) {
|
|
506
|
-
this.$
|
|
523
|
+
this.$check();
|
|
507
524
|
|
|
508
525
|
if (!(object instanceof Entity)) {
|
|
509
526
|
return false;
|
|
@@ -513,22 +530,24 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
513
530
|
return false;
|
|
514
531
|
}
|
|
515
532
|
}
|
|
516
|
-
if (object.constructor !== this.constructor) {
|
|
517
|
-
return false;
|
|
518
|
-
}
|
|
519
533
|
if (object.cdate !== this.cdate) {
|
|
520
534
|
return false;
|
|
521
535
|
}
|
|
522
536
|
if (object.mdate !== this.mdate) {
|
|
523
537
|
return false;
|
|
524
538
|
}
|
|
539
|
+
const obTags = [...object.tags].sort();
|
|
540
|
+
const myTags = [...this.tags].sort();
|
|
541
|
+
if (!isEqual(obTags, myTags)) {
|
|
542
|
+
return false;
|
|
543
|
+
}
|
|
525
544
|
const obData = object.$getData(true);
|
|
526
545
|
const myData = this.$getData(true);
|
|
527
546
|
return isEqual(obData, myData);
|
|
528
547
|
}
|
|
529
548
|
|
|
530
549
|
public $getData(includeSData = false) {
|
|
531
|
-
this.$
|
|
550
|
+
this.$check();
|
|
532
551
|
|
|
533
552
|
if (includeSData) {
|
|
534
553
|
// Access all the serialized properties to initialize them.
|
|
@@ -540,30 +559,56 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
540
559
|
}
|
|
541
560
|
|
|
542
561
|
public $getSData() {
|
|
543
|
-
this.$
|
|
562
|
+
this.$check();
|
|
544
563
|
|
|
545
564
|
return this.$sdata;
|
|
546
565
|
}
|
|
547
566
|
|
|
548
567
|
public $getOriginalAcValues() {
|
|
549
|
-
this.$
|
|
550
|
-
|
|
551
|
-
return (
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
568
|
+
this.$check();
|
|
569
|
+
|
|
570
|
+
return this.$originalAcValues ?? this.$getCurrentAcValues();
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
public $getCurrentAcValues() {
|
|
574
|
+
this.$check();
|
|
575
|
+
|
|
576
|
+
return {
|
|
577
|
+
user: this.$getAcUid(),
|
|
578
|
+
group: this.$getAcGid(),
|
|
579
|
+
acUser: this.$data.acUser,
|
|
580
|
+
acGroup: this.$data.acGroup,
|
|
581
|
+
acOther: this.$data.acOther,
|
|
582
|
+
acRead: this.$getAcReadIds(),
|
|
583
|
+
acWrite: this.$getAcWriteIds(),
|
|
584
|
+
acFull: this.$getAcFullIds(),
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
public $getAcUid() {
|
|
589
|
+
return this.$data.user?.guid as string | null;
|
|
590
|
+
}
|
|
591
|
+
public $getAcGid() {
|
|
592
|
+
return this.$data.group?.guid as string | null;
|
|
593
|
+
}
|
|
594
|
+
public $getAcReadIds() {
|
|
595
|
+
return this.$data.acRead?.map((entity: EntityInterface) => entity.guid) as
|
|
596
|
+
| string[]
|
|
597
|
+
| null;
|
|
598
|
+
}
|
|
599
|
+
public $getAcWriteIds() {
|
|
600
|
+
return this.$data.acWrite?.map((entity: EntityInterface) => entity.guid) as
|
|
601
|
+
| string[]
|
|
602
|
+
| null;
|
|
603
|
+
}
|
|
604
|
+
public $getAcFullIds() {
|
|
605
|
+
return this.$data.acFull?.map((entity: EntityInterface) => entity.guid) as
|
|
606
|
+
| string[]
|
|
607
|
+
| null;
|
|
563
608
|
}
|
|
564
609
|
|
|
565
610
|
public $getValidatable() {
|
|
566
|
-
this.$
|
|
611
|
+
this.$check();
|
|
567
612
|
|
|
568
613
|
// Access all the serialized properties to initialize them.
|
|
569
614
|
for (const key in this.$sdata) {
|
|
@@ -579,13 +624,13 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
579
624
|
}
|
|
580
625
|
|
|
581
626
|
public $getTags() {
|
|
582
|
-
this.$
|
|
627
|
+
this.$check();
|
|
583
628
|
|
|
584
629
|
return this.tags;
|
|
585
630
|
}
|
|
586
631
|
|
|
587
632
|
public $hasTag(...tags: string[]) {
|
|
588
|
-
this.$
|
|
633
|
+
this.$check();
|
|
589
634
|
|
|
590
635
|
if (!tags.length) {
|
|
591
636
|
return false;
|
|
@@ -611,10 +656,15 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
611
656
|
return this.guid === object.guid;
|
|
612
657
|
}
|
|
613
658
|
|
|
614
|
-
this.$
|
|
659
|
+
this.$check();
|
|
615
660
|
if (typeof object.$getData !== 'function') {
|
|
616
661
|
return false;
|
|
617
662
|
} else {
|
|
663
|
+
const obTags = [...object.tags].sort();
|
|
664
|
+
const myTags = [...this.tags].sort();
|
|
665
|
+
if (!isEqual(obTags, myTags)) {
|
|
666
|
+
return false;
|
|
667
|
+
}
|
|
618
668
|
const obData = object.$getData(true);
|
|
619
669
|
const myData = this.$getData(true);
|
|
620
670
|
return isEqual(obData, myData);
|
|
@@ -622,8 +672,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
622
672
|
}
|
|
623
673
|
|
|
624
674
|
public $jsonAcceptData(input: EntityJson, allowConflict = false) {
|
|
625
|
-
|
|
626
|
-
this.$referenceWake();
|
|
675
|
+
this.$check();
|
|
627
676
|
|
|
628
677
|
if (this.guid != input.guid) {
|
|
629
678
|
throw new EntityConflictError(
|
|
@@ -665,12 +714,27 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
665
714
|
|
|
666
715
|
const protectedData: EntityData = {};
|
|
667
716
|
const protectedProps = [...this.$protectedData];
|
|
668
|
-
if (
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
protectedProps.push('
|
|
673
|
-
protectedProps.push('
|
|
717
|
+
if (this.$nymph.tilmeld) {
|
|
718
|
+
protectedProps.push('acUser');
|
|
719
|
+
protectedProps.push('acGroup');
|
|
720
|
+
protectedProps.push('acOther');
|
|
721
|
+
protectedProps.push('acRead');
|
|
722
|
+
protectedProps.push('acWrite');
|
|
723
|
+
protectedProps.push('acFull');
|
|
724
|
+
if (
|
|
725
|
+
((this.constructor as typeof Entity).class !== 'User' &&
|
|
726
|
+
(this.constructor as typeof Entity).class !== 'Group') ||
|
|
727
|
+
!(this.$nymph.tilmeld as any).currentUser ||
|
|
728
|
+
(!(this.$nymph.tilmeld as any).currentUser.abilities?.includes(
|
|
729
|
+
'tilmeld/admin'
|
|
730
|
+
) &&
|
|
731
|
+
!(this.$nymph.tilmeld as any).currentUser.abilities?.includes(
|
|
732
|
+
'system/admin'
|
|
733
|
+
))
|
|
734
|
+
) {
|
|
735
|
+
protectedProps.push('user');
|
|
736
|
+
protectedProps.push('group');
|
|
737
|
+
}
|
|
674
738
|
}
|
|
675
739
|
for (const name of protectedProps) {
|
|
676
740
|
if (name in this.$data) {
|
|
@@ -703,7 +767,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
703
767
|
}
|
|
704
768
|
|
|
705
769
|
public $jsonAcceptPatch(patch: EntityPatch, allowConflict = false) {
|
|
706
|
-
this.$
|
|
770
|
+
this.$check();
|
|
707
771
|
|
|
708
772
|
if (this.guid != patch.guid) {
|
|
709
773
|
throw new EntityConflictError(
|
|
@@ -719,11 +783,35 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
719
783
|
}
|
|
720
784
|
this.mdate = patch.mdate;
|
|
721
785
|
|
|
786
|
+
const protectedProps = [...this.$protectedData];
|
|
787
|
+
if (this.$nymph.tilmeld) {
|
|
788
|
+
protectedProps.push('acUser');
|
|
789
|
+
protectedProps.push('acGroup');
|
|
790
|
+
protectedProps.push('acOther');
|
|
791
|
+
protectedProps.push('acRead');
|
|
792
|
+
protectedProps.push('acWrite');
|
|
793
|
+
protectedProps.push('acFull');
|
|
794
|
+
if (
|
|
795
|
+
((this.constructor as typeof Entity).class !== 'User' &&
|
|
796
|
+
(this.constructor as typeof Entity).class !== 'Group') ||
|
|
797
|
+
!(this.$nymph.tilmeld as any).currentUser ||
|
|
798
|
+
(!(this.$nymph.tilmeld as any).currentUser.abilities?.includes(
|
|
799
|
+
'tilmeld/admin'
|
|
800
|
+
) &&
|
|
801
|
+
!(this.$nymph.tilmeld as any).currentUser.abilities?.includes(
|
|
802
|
+
'system/admin'
|
|
803
|
+
))
|
|
804
|
+
) {
|
|
805
|
+
protectedProps.push('user');
|
|
806
|
+
protectedProps.push('group');
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
722
810
|
for (const name in patch.set) {
|
|
723
811
|
if (
|
|
724
812
|
(this.$allowlistData != null &&
|
|
725
813
|
this.$allowlistData.indexOf(name) === -1) ||
|
|
726
|
-
|
|
814
|
+
protectedProps.indexOf(name) !== -1 ||
|
|
727
815
|
this.$privateData.indexOf(name) !== -1
|
|
728
816
|
) {
|
|
729
817
|
continue;
|
|
@@ -739,7 +827,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
739
827
|
if (
|
|
740
828
|
(this.$allowlistData != null &&
|
|
741
829
|
this.$allowlistData.indexOf(name) === -1) ||
|
|
742
|
-
|
|
830
|
+
protectedProps.indexOf(name) !== -1 ||
|
|
743
831
|
this.$privateData.indexOf(name) !== -1
|
|
744
832
|
) {
|
|
745
833
|
continue;
|
|
@@ -771,7 +859,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
771
859
|
}
|
|
772
860
|
|
|
773
861
|
public $putData(data: EntityData, sdata?: SerializedEntityData) {
|
|
774
|
-
this.$
|
|
862
|
+
this.$check();
|
|
775
863
|
|
|
776
864
|
const mySdata = sdata ?? this.$getSData();
|
|
777
865
|
for (const name in data) {
|
|
@@ -790,7 +878,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
790
878
|
this.$sdata = mySdata;
|
|
791
879
|
// Set original AC values if not set..
|
|
792
880
|
if (this.$originalAcValues == null) {
|
|
793
|
-
this.$originalAcValues = this.$
|
|
881
|
+
this.$originalAcValues = this.$getCurrentAcValues();
|
|
794
882
|
}
|
|
795
883
|
}
|
|
796
884
|
|
|
@@ -822,45 +910,133 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
822
910
|
this.$sleepingReference = reference;
|
|
823
911
|
}
|
|
824
912
|
|
|
913
|
+
/**
|
|
914
|
+
* Check if this is a sleeping reference and throw an error if so.
|
|
915
|
+
*/
|
|
916
|
+
protected $check() {
|
|
917
|
+
if (this.$isASleepingReference || this.$sleepingReference != null) {
|
|
918
|
+
throw new EntityIsSleepingReferenceError(
|
|
919
|
+
'This entity is in a sleeping reference state. You must use .$wake() to wake it.'
|
|
920
|
+
);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
/**
|
|
925
|
+
* Check if this is a sleeping reference.
|
|
926
|
+
*/
|
|
927
|
+
public $asleep() {
|
|
928
|
+
return this.$isASleepingReference || this.$sleepingReference != null;
|
|
929
|
+
}
|
|
930
|
+
|
|
825
931
|
/**
|
|
826
932
|
* Wake from a sleeping reference.
|
|
827
|
-
*
|
|
828
|
-
* @returns True on success, false on failure.
|
|
829
933
|
*/
|
|
830
|
-
|
|
934
|
+
public $wake() {
|
|
831
935
|
if (!this.$isASleepingReference || this.$sleepingReference == null) {
|
|
832
|
-
|
|
936
|
+
this.$wakePromise = null;
|
|
937
|
+
return Promise.resolve(this);
|
|
833
938
|
}
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
skipAc: this.$skipAc,
|
|
839
|
-
},
|
|
840
|
-
{ type: '&', guid: this.$sleepingReference[1] }
|
|
841
|
-
);
|
|
842
|
-
if (entity == null) {
|
|
843
|
-
return false;
|
|
939
|
+
if (this.$sleepingReference?.[1] == null) {
|
|
940
|
+
throw new InvalidStateError(
|
|
941
|
+
'Tried to wake a sleeping reference with no GUID.'
|
|
942
|
+
);
|
|
844
943
|
}
|
|
845
|
-
this.$
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
944
|
+
if (!this.$wakePromise) {
|
|
945
|
+
const EntityClass = this.$nymph.getEntityClass(
|
|
946
|
+
this.$sleepingReference[2]
|
|
947
|
+
);
|
|
948
|
+
this.$wakePromise = this.$nymph
|
|
949
|
+
.getEntity(
|
|
950
|
+
{
|
|
951
|
+
class: EntityClass,
|
|
952
|
+
skipAc: this.$skipAc,
|
|
953
|
+
},
|
|
954
|
+
{ type: '&', guid: this.$sleepingReference[1] }
|
|
955
|
+
)
|
|
956
|
+
.then((entity) => {
|
|
957
|
+
if (entity == null || entity.guid == null) {
|
|
958
|
+
return Promise.reject(
|
|
959
|
+
new InvalidStateError(
|
|
960
|
+
'The sleeping reference could not be retrieved.'
|
|
961
|
+
)
|
|
962
|
+
);
|
|
963
|
+
}
|
|
964
|
+
this.$isASleepingReference = false;
|
|
965
|
+
this.$sleepingReference = null;
|
|
966
|
+
this.guid = entity.guid;
|
|
967
|
+
this.tags = entity.tags;
|
|
968
|
+
this.cdate = entity.cdate;
|
|
969
|
+
this.mdate = entity.mdate;
|
|
970
|
+
this.$putData(entity.$getData(), entity.$getSData());
|
|
971
|
+
|
|
972
|
+
return this;
|
|
973
|
+
})
|
|
974
|
+
.finally(() => {
|
|
975
|
+
this.$wakePromise = null;
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
return this.$wakePromise;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
public $wakeAll(level?: number) {
|
|
982
|
+
return new Promise((resolve, reject) => {
|
|
983
|
+
// Run this once this entity is wake.
|
|
984
|
+
const wakeProps = () => {
|
|
985
|
+
let newLevel;
|
|
986
|
+
// If level is undefined, keep going forever, otherwise, stop once we've
|
|
987
|
+
// gone deep enough.
|
|
988
|
+
if (level !== undefined) {
|
|
989
|
+
newLevel = level - 1;
|
|
990
|
+
}
|
|
991
|
+
if (newLevel !== undefined && newLevel < 0) {
|
|
992
|
+
resolve(this);
|
|
993
|
+
return;
|
|
994
|
+
}
|
|
995
|
+
const promises = [];
|
|
996
|
+
// Go through data looking for entities to wake.
|
|
997
|
+
for (let [key, value] of Object.entries(this.$data)) {
|
|
998
|
+
if (value instanceof Entity && value.$isASleepingReference) {
|
|
999
|
+
promises.push(value.$wakeAll(newLevel));
|
|
1000
|
+
} else if (Array.isArray(value)) {
|
|
1001
|
+
for (let i = 0; i < value.length; i++) {
|
|
1002
|
+
if (
|
|
1003
|
+
value[i] instanceof Entity &&
|
|
1004
|
+
value[i].$isASleepingReference
|
|
1005
|
+
) {
|
|
1006
|
+
promises.push(value[i].$wakeAll(newLevel));
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
if (promises.length) {
|
|
1012
|
+
Promise.all(promises).then(
|
|
1013
|
+
() => resolve(this),
|
|
1014
|
+
(errObj) => reject(errObj)
|
|
1015
|
+
);
|
|
1016
|
+
} else {
|
|
1017
|
+
resolve(this);
|
|
1018
|
+
}
|
|
1019
|
+
};
|
|
1020
|
+
|
|
1021
|
+
if (this.$isASleepingReference) {
|
|
1022
|
+
this.$wake().then(wakeProps, (errObj) => reject(errObj));
|
|
1023
|
+
} else {
|
|
1024
|
+
wakeProps();
|
|
1025
|
+
}
|
|
1026
|
+
}) as Promise<Entity<T>>;
|
|
853
1027
|
}
|
|
854
1028
|
|
|
855
1029
|
public async $refresh() {
|
|
856
|
-
this.$
|
|
1030
|
+
await this.$wake();
|
|
857
1031
|
|
|
858
1032
|
if (this.guid == null) {
|
|
859
1033
|
return false;
|
|
860
1034
|
}
|
|
861
1035
|
const refresh = await this.$nymph.getEntity(
|
|
862
1036
|
{
|
|
863
|
-
class: this.
|
|
1037
|
+
class: this.$nymph.getEntityClass(
|
|
1038
|
+
this.constructor as EntityConstructor
|
|
1039
|
+
),
|
|
864
1040
|
skipCache: true,
|
|
865
1041
|
skipAc: this.$skipAc,
|
|
866
1042
|
},
|
|
@@ -877,13 +1053,13 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
877
1053
|
}
|
|
878
1054
|
|
|
879
1055
|
public $removeTag(...tags: string[]) {
|
|
880
|
-
this.$
|
|
1056
|
+
this.$check();
|
|
881
1057
|
|
|
882
1058
|
this.tags = difference(this.tags, tags);
|
|
883
1059
|
}
|
|
884
1060
|
|
|
885
1061
|
public async $save(): Promise<boolean> {
|
|
886
|
-
this.$
|
|
1062
|
+
await this.$wake();
|
|
887
1063
|
|
|
888
1064
|
return await this.$nymph.saveEntity(this);
|
|
889
1065
|
}
|