@nymphjs/nymph 1.0.0-beta.11 → 1.0.0-beta.111
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 +540 -0
- package/README.md +23 -21
- package/dist/Entity.d.ts +293 -10
- package/dist/Entity.js +560 -148
- package/dist/Entity.js.map +1 -1
- package/dist/Entity.test.js +52 -13
- package/dist/Entity.test.js.map +1 -1
- package/dist/Entity.types.d.ts +256 -23
- package/dist/Entity.types.js +1 -2
- package/dist/Nymph.d.ts +586 -12
- package/dist/Nymph.js +844 -179
- package/dist/Nymph.js.map +1 -1
- package/dist/Nymph.types.d.ts +81 -8
- package/dist/Nymph.types.js +3 -5
- package/dist/Nymph.types.js.map +1 -1
- package/dist/conf/d.d.ts +43 -0
- package/dist/conf/d.js +1 -2
- package/dist/conf/defaults.d.ts +1 -1
- package/dist/conf/defaults.js +26 -3
- package/dist/conf/defaults.js.map +1 -1
- package/dist/conf/index.d.ts +2 -2
- package/dist/conf/index.js +2 -8
- package/dist/conf/index.js.map +1 -1
- package/dist/driver/NymphDriver.d.ts +201 -40
- package/dist/driver/NymphDriver.js +685 -239
- package/dist/driver/NymphDriver.js.map +1 -1
- package/dist/driver/index.d.ts +2 -2
- package/dist/driver/index.js +3 -23
- package/dist/driver/index.js.map +1 -1
- package/dist/driver/testArtifacts.d.ts +2 -2
- package/dist/driver/testArtifacts.js +909 -93
- package/dist/driver/testArtifacts.js.map +1 -1
- package/dist/errors/ClassNotAvailableError.js +1 -5
- package/dist/errors/ClassNotAvailableError.js.map +1 -1
- package/dist/errors/EntityConflictError.js +1 -5
- package/dist/errors/EntityConflictError.js.map +1 -1
- package/dist/errors/EntityInvalidDataError.d.ts +6 -0
- package/dist/errors/EntityInvalidDataError.js +8 -6
- package/dist/errors/EntityInvalidDataError.js.map +1 -1
- package/dist/errors/EntityIsSleepingReferenceError.js +1 -5
- package/dist/errors/EntityIsSleepingReferenceError.js.map +1 -1
- package/dist/errors/EntityUniqueConstraintError.d.ts +3 -0
- package/dist/errors/EntityUniqueConstraintError.js +7 -0
- package/dist/errors/EntityUniqueConstraintError.js.map +1 -0
- package/dist/errors/HttpError.d.ts +5 -0
- package/dist/errors/HttpError.js +11 -0
- package/dist/errors/HttpError.js.map +1 -0
- package/dist/errors/InvalidParametersError.js +1 -5
- package/dist/errors/InvalidParametersError.js.map +1 -1
- package/dist/errors/InvalidStateError.d.ts +3 -0
- package/dist/errors/InvalidStateError.js +7 -0
- package/dist/errors/InvalidStateError.js.map +1 -0
- package/dist/errors/NotConfiguredError.js +1 -5
- package/dist/errors/NotConfiguredError.js.map +1 -1
- package/dist/errors/QueryFailedError.d.ts +2 -1
- package/dist/errors/QueryFailedError.js +5 -6
- package/dist/errors/QueryFailedError.js.map +1 -1
- package/dist/errors/UnableToConnectError.js +1 -5
- package/dist/errors/UnableToConnectError.js.map +1 -1
- package/dist/errors/index.d.ts +11 -8
- package/dist/errors/index.js +11 -24
- package/dist/errors/index.js.map +1 -1
- package/dist/index.d.ts +11 -11
- package/dist/index.js +14 -34
- package/dist/index.js.map +1 -1
- package/dist/nymphJoiProps.d.ts +1 -1
- package/dist/nymphJoiProps.js +10 -15
- package/dist/nymphJoiProps.js.map +1 -1
- package/dist/testArtifacts.d.ts +19 -6
- package/dist/testArtifacts.js +32 -37
- package/dist/testArtifacts.js.map +1 -1
- package/dist/testMocks.d.ts +27 -12
- package/dist/testMocks.js +40 -29
- package/dist/testMocks.js.map +1 -1
- package/dist/utils.d.ts +4 -4
- package/dist/utils.js +40 -29
- package/dist/utils.js.map +1 -1
- package/jest.config.js +11 -2
- package/package.json +24 -20
- package/src/Entity.test.ts +50 -7
- package/src/Entity.ts +489 -150
- package/src/Entity.types.ts +72 -74
- package/src/Nymph.ts +985 -322
- package/src/Nymph.types.ts +150 -27
- package/src/conf/d.ts +25 -0
- package/src/conf/defaults.ts +34 -1
- package/src/conf/index.ts +2 -2
- package/src/driver/NymphDriver.ts +859 -412
- package/src/driver/index.ts +2 -2
- package/src/driver/testArtifacts.ts +1090 -186
- package/src/errors/EntityUniqueConstraintError.ts +6 -0
- package/src/errors/HttpError.ts +12 -0
- package/src/errors/InvalidStateError.ts +6 -0
- package/src/errors/QueryFailedError.ts +3 -1
- package/src/errors/index.ts +14 -8
- package/src/index.ts +11 -11
- package/src/nymphJoiProps.ts +2 -2
- package/src/testArtifacts.ts +20 -20
- package/src/testMocks.ts +76 -35
- package/src/type/locutus.d.ts +3 -0
- package/src/utils.ts +39 -18
- package/tsconfig.json +5 -3
- package/typedoc.json +4 -0
package/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.
|
|
@@ -87,9 +104,9 @@ import {
|
|
|
87
104
|
* will fill its data from the DB. You can call clearCache() to turn all the
|
|
88
105
|
* entities back into sleeping references.
|
|
89
106
|
*/
|
|
90
|
-
export default class Entity<
|
|
91
|
-
|
|
92
|
-
{
|
|
107
|
+
export default class Entity<
|
|
108
|
+
T extends EntityData = EntityData,
|
|
109
|
+
> implements EntityInterface {
|
|
93
110
|
/**
|
|
94
111
|
* The instance of Nymph to use for queries.
|
|
95
112
|
*/
|
|
@@ -136,6 +153,10 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
136
153
|
* The reference to use to wake.
|
|
137
154
|
*/
|
|
138
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;
|
|
139
160
|
/**
|
|
140
161
|
* Properties that will not be serialized into JSON with toJSON(). This
|
|
141
162
|
* can be considered a denylist, because these properties will not be set
|
|
@@ -145,6 +166,18 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
145
166
|
* also listed in searchRestrictedData.
|
|
146
167
|
*/
|
|
147
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;
|
|
148
181
|
/**
|
|
149
182
|
* Properties that will not be searchable from the frontend. If the frontend
|
|
150
183
|
* includes any of these properties in any of their clauses, they will be
|
|
@@ -156,8 +189,20 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
156
189
|
* be visible on the frontend, unlike $privateData, but any changes to them
|
|
157
190
|
* that come from the frontend will be ignored.
|
|
158
191
|
*
|
|
159
|
-
* In addition to what's listed here, the
|
|
160
|
-
* 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.
|
|
161
206
|
*/
|
|
162
207
|
protected $protectedData: string[] = [];
|
|
163
208
|
/**
|
|
@@ -196,28 +241,39 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
196
241
|
* The AC properties' values when the entity was loaded.
|
|
197
242
|
*/
|
|
198
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;
|
|
199
248
|
|
|
200
249
|
/**
|
|
201
|
-
*
|
|
202
|
-
*
|
|
250
|
+
* Alter the options for a query for this entity.
|
|
251
|
+
*
|
|
252
|
+
* @param options The current options.
|
|
253
|
+
* @returns The altered options.
|
|
203
254
|
*/
|
|
204
|
-
|
|
255
|
+
static alterOptions?<T extends Options>(options: T): T;
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Initialize an entity.
|
|
259
|
+
*/
|
|
260
|
+
public constructor(..._rest: any[]) {
|
|
205
261
|
this.$nymph = (this.constructor as EntityConstructor).nymph;
|
|
206
262
|
this.$dataHandler = {
|
|
207
263
|
has: (data: EntityData, name: string) => {
|
|
208
|
-
this.$
|
|
264
|
+
this.$check();
|
|
209
265
|
|
|
210
266
|
return name in data || name in this.$sdata;
|
|
211
267
|
},
|
|
212
268
|
|
|
213
269
|
get: (data: EntityData, name: string) => {
|
|
214
|
-
this.$
|
|
270
|
+
this.$check();
|
|
215
271
|
|
|
216
272
|
if (this.$sdata.hasOwnProperty(name)) {
|
|
217
273
|
data[name] = referencesToEntities(
|
|
218
274
|
JSON.parse(this.$sdata[name]),
|
|
219
275
|
this.$nymph,
|
|
220
|
-
this.$skipAc
|
|
276
|
+
this.$skipAc,
|
|
221
277
|
);
|
|
222
278
|
delete this.$sdata[name];
|
|
223
279
|
}
|
|
@@ -228,7 +284,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
228
284
|
},
|
|
229
285
|
|
|
230
286
|
set: (data: EntityData, name: string, value: any) => {
|
|
231
|
-
this.$
|
|
287
|
+
this.$check();
|
|
232
288
|
|
|
233
289
|
if (this.$sdata.hasOwnProperty(name)) {
|
|
234
290
|
delete this.$sdata[name];
|
|
@@ -238,7 +294,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
238
294
|
},
|
|
239
295
|
|
|
240
296
|
deleteProperty: (data: EntityData, name: string) => {
|
|
241
|
-
this.$
|
|
297
|
+
this.$check();
|
|
242
298
|
|
|
243
299
|
if (this.$sdata.hasOwnProperty(name)) {
|
|
244
300
|
return delete this.$sdata[name];
|
|
@@ -252,9 +308,9 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
252
308
|
defineProperty: (
|
|
253
309
|
data: EntityData,
|
|
254
310
|
name: string,
|
|
255
|
-
descriptor: PropertyDescriptor
|
|
311
|
+
descriptor: PropertyDescriptor,
|
|
256
312
|
) => {
|
|
257
|
-
this.$
|
|
313
|
+
this.$check();
|
|
258
314
|
|
|
259
315
|
if (this.$sdata.hasOwnProperty(name)) {
|
|
260
316
|
delete this.$sdata[name];
|
|
@@ -264,13 +320,13 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
264
320
|
},
|
|
265
321
|
|
|
266
322
|
getOwnPropertyDescriptor: (data: EntityData, name: string) => {
|
|
267
|
-
this.$
|
|
323
|
+
this.$check();
|
|
268
324
|
|
|
269
325
|
if (this.$sdata.hasOwnProperty(name)) {
|
|
270
326
|
data[name] = referencesToEntities(
|
|
271
327
|
JSON.parse(this.$sdata[name]),
|
|
272
328
|
this.$nymph,
|
|
273
|
-
this.$skipAc
|
|
329
|
+
this.$skipAc,
|
|
274
330
|
);
|
|
275
331
|
delete this.$sdata[name];
|
|
276
332
|
}
|
|
@@ -278,10 +334,10 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
278
334
|
},
|
|
279
335
|
|
|
280
336
|
ownKeys: (data: EntityData) => {
|
|
281
|
-
this.$
|
|
337
|
+
this.$check();
|
|
282
338
|
|
|
283
339
|
return Object.getOwnPropertyNames(data).concat(
|
|
284
|
-
Object.getOwnPropertyNames(this.$sdata)
|
|
340
|
+
Object.getOwnPropertyNames(this.$sdata),
|
|
285
341
|
);
|
|
286
342
|
},
|
|
287
343
|
};
|
|
@@ -290,20 +346,6 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
290
346
|
|
|
291
347
|
this.$data = new Proxy(this.$dataStore, this.$dataHandler);
|
|
292
348
|
|
|
293
|
-
if (guid != null) {
|
|
294
|
-
const entity = this.$nymph.driver.getEntitySync(
|
|
295
|
-
{ class: this.constructor as EntityConstructor },
|
|
296
|
-
{ type: '&', guid }
|
|
297
|
-
);
|
|
298
|
-
if (entity) {
|
|
299
|
-
this.guid = entity.guid;
|
|
300
|
-
this.tags = entity.tags;
|
|
301
|
-
this.cdate = entity.cdate;
|
|
302
|
-
this.mdate = entity.mdate;
|
|
303
|
-
this.$putData(entity.$getData(), entity.$getSData());
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
349
|
return new Proxy(this, {
|
|
308
350
|
has: (entity: Entity, name: string) => {
|
|
309
351
|
if (
|
|
@@ -314,7 +356,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
314
356
|
return name in entity;
|
|
315
357
|
}
|
|
316
358
|
|
|
317
|
-
this.$
|
|
359
|
+
this.$check();
|
|
318
360
|
return name in entity.$data;
|
|
319
361
|
},
|
|
320
362
|
|
|
@@ -325,12 +367,12 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
325
367
|
name.substring(0, 1) === '$'
|
|
326
368
|
) {
|
|
327
369
|
if (name === 'tags' || name === 'cdate' || name === 'mdate') {
|
|
328
|
-
this.$
|
|
370
|
+
this.$check();
|
|
329
371
|
}
|
|
330
372
|
return (entity as any)[name];
|
|
331
373
|
}
|
|
332
374
|
|
|
333
|
-
this.$
|
|
375
|
+
this.$check();
|
|
334
376
|
if (name in entity.$data) {
|
|
335
377
|
return entity.$data[name];
|
|
336
378
|
}
|
|
@@ -344,11 +386,11 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
344
386
|
name.substring(0, 1) === '$'
|
|
345
387
|
) {
|
|
346
388
|
if (name === 'tags' || name === 'cdate' || name === 'mdate') {
|
|
347
|
-
this.$
|
|
389
|
+
this.$check();
|
|
348
390
|
}
|
|
349
391
|
(entity as any)[name] = value;
|
|
350
392
|
} else {
|
|
351
|
-
this.$
|
|
393
|
+
this.$check();
|
|
352
394
|
(entity.$data as any)[name] = value;
|
|
353
395
|
}
|
|
354
396
|
return true;
|
|
@@ -358,7 +400,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
358
400
|
if (name in entity) {
|
|
359
401
|
return delete (entity as any)[name];
|
|
360
402
|
} else if (name in entity.$data) {
|
|
361
|
-
this.$
|
|
403
|
+
this.$check();
|
|
362
404
|
return delete entity.$data[name];
|
|
363
405
|
}
|
|
364
406
|
return true;
|
|
@@ -371,7 +413,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
371
413
|
defineProperty: (
|
|
372
414
|
entity: Entity,
|
|
373
415
|
name: string,
|
|
374
|
-
descriptor: PropertyDescriptor
|
|
416
|
+
descriptor: PropertyDescriptor,
|
|
375
417
|
) => {
|
|
376
418
|
if (
|
|
377
419
|
typeof name !== 'string' ||
|
|
@@ -379,11 +421,11 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
379
421
|
name.substring(0, 1) === '$'
|
|
380
422
|
) {
|
|
381
423
|
if (name === 'tags' || name === 'cdate' || name === 'mdate') {
|
|
382
|
-
this.$
|
|
424
|
+
this.$check();
|
|
383
425
|
}
|
|
384
426
|
Object.defineProperty(entity, name, descriptor);
|
|
385
427
|
} else {
|
|
386
|
-
this.$
|
|
428
|
+
this.$check();
|
|
387
429
|
Object.defineProperty(entity.$data, name, descriptor);
|
|
388
430
|
}
|
|
389
431
|
return true;
|
|
@@ -396,48 +438,147 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
396
438
|
name.substring(0, 1) === '$'
|
|
397
439
|
) {
|
|
398
440
|
if (name === 'tags' || name === 'cdate' || name === 'mdate') {
|
|
399
|
-
this.$
|
|
441
|
+
this.$check();
|
|
400
442
|
}
|
|
401
443
|
return Object.getOwnPropertyDescriptor(entity, name);
|
|
402
444
|
} else {
|
|
403
|
-
this.$
|
|
445
|
+
this.$check();
|
|
404
446
|
return Object.getOwnPropertyDescriptor(entity.$data, name);
|
|
405
447
|
}
|
|
406
448
|
},
|
|
407
449
|
|
|
408
450
|
ownKeys: (entity: Entity) => {
|
|
409
|
-
this.$
|
|
451
|
+
this.$check();
|
|
410
452
|
return Object.getOwnPropertyNames(entity).concat(
|
|
411
|
-
Object.getOwnPropertyNames(entity.$data)
|
|
453
|
+
Object.getOwnPropertyNames(entity.$data),
|
|
412
454
|
);
|
|
413
455
|
},
|
|
414
456
|
}) as Entity<T>;
|
|
415
457
|
}
|
|
416
458
|
|
|
417
|
-
|
|
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>> {
|
|
418
472
|
const entity = new this();
|
|
419
473
|
if (guid != null) {
|
|
420
|
-
const entity = await
|
|
474
|
+
const entity = await (
|
|
475
|
+
this as unknown as EntityConstructor
|
|
476
|
+
).nymph.getEntity(
|
|
421
477
|
{
|
|
422
|
-
class: this as
|
|
478
|
+
class: this as unknown as EntityConstructor,
|
|
423
479
|
},
|
|
424
|
-
{ type: '&', guid }
|
|
480
|
+
{ type: '&', guid },
|
|
425
481
|
);
|
|
426
482
|
if (entity != null) {
|
|
427
|
-
return entity
|
|
483
|
+
return entity as E & EntityDataType<E>;
|
|
428
484
|
}
|
|
429
485
|
}
|
|
430
|
-
return entity
|
|
486
|
+
return entity as E & EntityDataType<E>;
|
|
431
487
|
}
|
|
432
488
|
|
|
433
|
-
|
|
434
|
-
|
|
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>;
|
|
435
496
|
}
|
|
436
497
|
|
|
437
|
-
|
|
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> {
|
|
438
513
|
const entity = new this();
|
|
439
514
|
entity.$referenceSleep(reference);
|
|
440
|
-
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;
|
|
441
582
|
}
|
|
442
583
|
|
|
443
584
|
public toJSON() {
|
|
@@ -452,17 +593,29 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
452
593
|
tags: [...this.tags],
|
|
453
594
|
data: {},
|
|
454
595
|
};
|
|
455
|
-
const
|
|
456
|
-
|
|
596
|
+
for (const key of [
|
|
597
|
+
...Object.keys(this.$dataStore),
|
|
598
|
+
...Object.keys(this.$sdata),
|
|
599
|
+
]) {
|
|
457
600
|
if (this.$privateData.indexOf(key) === -1) {
|
|
458
|
-
obj.data[key] = entitiesToReferences(data[key]);
|
|
601
|
+
obj.data[key] = entitiesToReferences(this.$data[key], true);
|
|
459
602
|
}
|
|
460
603
|
}
|
|
461
604
|
return obj;
|
|
462
605
|
}
|
|
463
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
|
+
|
|
464
617
|
public $addTag(...tags: string[]) {
|
|
465
|
-
this.$
|
|
618
|
+
this.$check();
|
|
466
619
|
|
|
467
620
|
if (tags.length < 1) {
|
|
468
621
|
return;
|
|
@@ -471,7 +624,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
471
624
|
}
|
|
472
625
|
|
|
473
626
|
public $arraySearch(array: any[], strict = false) {
|
|
474
|
-
this.$
|
|
627
|
+
this.$check();
|
|
475
628
|
|
|
476
629
|
if (!Array.isArray(array)) {
|
|
477
630
|
return -1;
|
|
@@ -486,9 +639,9 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
486
639
|
}
|
|
487
640
|
|
|
488
641
|
public $clearCache() {
|
|
489
|
-
this.$
|
|
642
|
+
this.$check();
|
|
490
643
|
|
|
491
|
-
this.$putData(this.$getData(), this.$getSData());
|
|
644
|
+
this.$putData(this.$getData(false, true), this.$getSData());
|
|
492
645
|
}
|
|
493
646
|
|
|
494
647
|
public $getClientEnabledMethods() {
|
|
@@ -496,13 +649,13 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
496
649
|
}
|
|
497
650
|
|
|
498
651
|
public async $delete(): Promise<boolean> {
|
|
499
|
-
this.$
|
|
652
|
+
this.$check();
|
|
500
653
|
|
|
501
654
|
return await this.$nymph.deleteEntity(this);
|
|
502
655
|
}
|
|
503
656
|
|
|
504
657
|
public $equals(object: any) {
|
|
505
|
-
this.$
|
|
658
|
+
this.$check();
|
|
506
659
|
|
|
507
660
|
if (!(object instanceof Entity)) {
|
|
508
661
|
return false;
|
|
@@ -523,13 +676,13 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
523
676
|
if (!isEqual(obTags, myTags)) {
|
|
524
677
|
return false;
|
|
525
678
|
}
|
|
526
|
-
const obData = object.$getData(true);
|
|
527
|
-
const myData = this.$getData(true);
|
|
679
|
+
const obData = object.$getData(true, true);
|
|
680
|
+
const myData = this.$getData(true, true);
|
|
528
681
|
return isEqual(obData, myData);
|
|
529
682
|
}
|
|
530
683
|
|
|
531
|
-
public $getData(includeSData = false) {
|
|
532
|
-
this.$
|
|
684
|
+
public $getData(includeSData = false, referenceOnlyExisting?: boolean) {
|
|
685
|
+
this.$check();
|
|
533
686
|
|
|
534
687
|
if (includeSData) {
|
|
535
688
|
// Access all the serialized properties to initialize them.
|
|
@@ -537,34 +690,82 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
537
690
|
const _unused: any = (this as any)[key];
|
|
538
691
|
}
|
|
539
692
|
}
|
|
540
|
-
return entitiesToReferences({ ...this.$dataStore });
|
|
693
|
+
return entitiesToReferences({ ...this.$dataStore }, referenceOnlyExisting);
|
|
541
694
|
}
|
|
542
695
|
|
|
543
696
|
public $getSData() {
|
|
544
|
-
this.$
|
|
697
|
+
this.$check();
|
|
545
698
|
|
|
546
699
|
return this.$sdata;
|
|
547
700
|
}
|
|
548
701
|
|
|
549
|
-
public $
|
|
550
|
-
|
|
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();
|
|
714
|
+
|
|
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
|
+
}
|
|
551
726
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
acWrite: this.$data.acWrite,
|
|
561
|
-
acFull: this.$data.acFull,
|
|
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];
|
|
562
735
|
}
|
|
563
|
-
|
|
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 | string) =>
|
|
753
|
+
typeof entity === 'string' ? entity : entity.guid,
|
|
754
|
+
) ?? null) as (string | null)[] | null;
|
|
755
|
+
}
|
|
756
|
+
public $getAcWriteIds() {
|
|
757
|
+
return (this.$data.acWrite?.map((entity: EntityInterface | string) =>
|
|
758
|
+
typeof entity === 'string' ? entity : entity.guid,
|
|
759
|
+
) ?? null) as (string | null)[] | null;
|
|
760
|
+
}
|
|
761
|
+
public $getAcFullIds() {
|
|
762
|
+
return (this.$data.acFull?.map((entity: EntityInterface | string) =>
|
|
763
|
+
typeof entity === 'string' ? entity : entity.guid,
|
|
764
|
+
) ?? null) as (string | null)[] | null;
|
|
564
765
|
}
|
|
565
766
|
|
|
566
767
|
public $getValidatable() {
|
|
567
|
-
this.$
|
|
768
|
+
this.$check();
|
|
568
769
|
|
|
569
770
|
// Access all the serialized properties to initialize them.
|
|
570
771
|
for (const key in this.$sdata) {
|
|
@@ -580,13 +781,13 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
580
781
|
}
|
|
581
782
|
|
|
582
783
|
public $getTags() {
|
|
583
|
-
this.$
|
|
784
|
+
this.$check();
|
|
584
785
|
|
|
585
786
|
return this.tags;
|
|
586
787
|
}
|
|
587
788
|
|
|
588
789
|
public $hasTag(...tags: string[]) {
|
|
589
|
-
this.$
|
|
790
|
+
this.$check();
|
|
590
791
|
|
|
591
792
|
if (!tags.length) {
|
|
592
793
|
return false;
|
|
@@ -608,11 +809,15 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
608
809
|
return false;
|
|
609
810
|
}
|
|
610
811
|
|
|
611
|
-
if (this
|
|
812
|
+
if (this === object) {
|
|
813
|
+
return true;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
if (this.guid != null || object.guid != null) {
|
|
612
817
|
return this.guid === object.guid;
|
|
613
818
|
}
|
|
614
819
|
|
|
615
|
-
this.$
|
|
820
|
+
this.$check();
|
|
616
821
|
if (typeof object.$getData !== 'function') {
|
|
617
822
|
return false;
|
|
618
823
|
} else {
|
|
@@ -621,19 +826,18 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
621
826
|
if (!isEqual(obTags, myTags)) {
|
|
622
827
|
return false;
|
|
623
828
|
}
|
|
624
|
-
const obData = object.$getData(true);
|
|
625
|
-
const myData = this.$getData(true);
|
|
829
|
+
const obData = object.$getData(true, true);
|
|
830
|
+
const myData = this.$getData(true, true);
|
|
626
831
|
return isEqual(obData, myData);
|
|
627
832
|
}
|
|
628
833
|
}
|
|
629
834
|
|
|
630
835
|
public $jsonAcceptData(input: EntityJson, allowConflict = false) {
|
|
631
|
-
|
|
632
|
-
this.$referenceWake();
|
|
836
|
+
this.$check();
|
|
633
837
|
|
|
634
838
|
if (this.guid != input.guid) {
|
|
635
839
|
throw new EntityConflictError(
|
|
636
|
-
'Tried to accept JSON input for the wrong entity.'
|
|
840
|
+
'Tried to accept JSON input for the wrong entity.',
|
|
637
841
|
);
|
|
638
842
|
}
|
|
639
843
|
|
|
@@ -658,7 +862,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
658
862
|
this.$addTag(...protectedTags, ...tags);
|
|
659
863
|
|
|
660
864
|
// Accept the data.
|
|
661
|
-
const data = input.data;
|
|
865
|
+
const data = { ...input.data };
|
|
662
866
|
const privateData: EntityData = {};
|
|
663
867
|
for (const name of this.$privateData) {
|
|
664
868
|
if (name in this.$data) {
|
|
@@ -671,12 +875,27 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
671
875
|
|
|
672
876
|
const protectedData: EntityData = {};
|
|
673
877
|
const protectedProps = [...this.$protectedData];
|
|
674
|
-
if (
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
protectedProps.push('
|
|
679
|
-
protectedProps.push('
|
|
878
|
+
if (this.$nymph.tilmeld) {
|
|
879
|
+
protectedProps.push('acUser');
|
|
880
|
+
protectedProps.push('acGroup');
|
|
881
|
+
protectedProps.push('acOther');
|
|
882
|
+
protectedProps.push('acRead');
|
|
883
|
+
protectedProps.push('acWrite');
|
|
884
|
+
protectedProps.push('acFull');
|
|
885
|
+
if (
|
|
886
|
+
((this.constructor as EntityConstructor).class !== 'User' &&
|
|
887
|
+
(this.constructor as EntityConstructor).class !== 'Group') ||
|
|
888
|
+
!(this.$nymph.tilmeld as any).currentUser ||
|
|
889
|
+
(!(this.$nymph.tilmeld as any).currentUser.abilities?.includes(
|
|
890
|
+
'tilmeld/admin',
|
|
891
|
+
) &&
|
|
892
|
+
!(this.$nymph.tilmeld as any).currentUser.abilities?.includes(
|
|
893
|
+
'system/admin',
|
|
894
|
+
))
|
|
895
|
+
) {
|
|
896
|
+
protectedProps.push('user');
|
|
897
|
+
protectedProps.push('group');
|
|
898
|
+
}
|
|
680
899
|
}
|
|
681
900
|
for (const name of protectedProps) {
|
|
682
901
|
if (name in this.$data) {
|
|
@@ -689,7 +908,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
689
908
|
|
|
690
909
|
let nonAllowlistData: EntityData = {};
|
|
691
910
|
if (this.$allowlistData != null) {
|
|
692
|
-
nonAllowlistData = { ...this.$getData(true) };
|
|
911
|
+
nonAllowlistData = { ...this.$getData(true, true) };
|
|
693
912
|
for (const name of this.$allowlistData) {
|
|
694
913
|
delete nonAllowlistData[name];
|
|
695
914
|
}
|
|
@@ -709,11 +928,11 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
709
928
|
}
|
|
710
929
|
|
|
711
930
|
public $jsonAcceptPatch(patch: EntityPatch, allowConflict = false) {
|
|
712
|
-
this.$
|
|
931
|
+
this.$check();
|
|
713
932
|
|
|
714
933
|
if (this.guid != patch.guid) {
|
|
715
934
|
throw new EntityConflictError(
|
|
716
|
-
'Tried to accept JSON patch for the wrong entity.'
|
|
935
|
+
'Tried to accept JSON patch for the wrong entity.',
|
|
717
936
|
);
|
|
718
937
|
}
|
|
719
938
|
|
|
@@ -725,11 +944,35 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
725
944
|
}
|
|
726
945
|
this.mdate = patch.mdate;
|
|
727
946
|
|
|
947
|
+
const protectedProps = [...this.$protectedData];
|
|
948
|
+
if (this.$nymph.tilmeld) {
|
|
949
|
+
protectedProps.push('acUser');
|
|
950
|
+
protectedProps.push('acGroup');
|
|
951
|
+
protectedProps.push('acOther');
|
|
952
|
+
protectedProps.push('acRead');
|
|
953
|
+
protectedProps.push('acWrite');
|
|
954
|
+
protectedProps.push('acFull');
|
|
955
|
+
if (
|
|
956
|
+
((this.constructor as EntityConstructor).class !== 'User' &&
|
|
957
|
+
(this.constructor as EntityConstructor).class !== 'Group') ||
|
|
958
|
+
!(this.$nymph.tilmeld as any).currentUser ||
|
|
959
|
+
(!(this.$nymph.tilmeld as any).currentUser.abilities?.includes(
|
|
960
|
+
'tilmeld/admin',
|
|
961
|
+
) &&
|
|
962
|
+
!(this.$nymph.tilmeld as any).currentUser.abilities?.includes(
|
|
963
|
+
'system/admin',
|
|
964
|
+
))
|
|
965
|
+
) {
|
|
966
|
+
protectedProps.push('user');
|
|
967
|
+
protectedProps.push('group');
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
|
|
728
971
|
for (const name in patch.set) {
|
|
729
972
|
if (
|
|
730
973
|
(this.$allowlistData != null &&
|
|
731
974
|
this.$allowlistData.indexOf(name) === -1) ||
|
|
732
|
-
|
|
975
|
+
protectedProps.indexOf(name) !== -1 ||
|
|
733
976
|
this.$privateData.indexOf(name) !== -1
|
|
734
977
|
) {
|
|
735
978
|
continue;
|
|
@@ -737,7 +980,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
737
980
|
(this.$data as any)[name] = referencesToEntities(
|
|
738
981
|
patch.set[name],
|
|
739
982
|
this.$nymph,
|
|
740
|
-
this.$skipAc
|
|
983
|
+
this.$skipAc,
|
|
741
984
|
);
|
|
742
985
|
}
|
|
743
986
|
|
|
@@ -745,7 +988,7 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
745
988
|
if (
|
|
746
989
|
(this.$allowlistData != null &&
|
|
747
990
|
this.$allowlistData.indexOf(name) === -1) ||
|
|
748
|
-
|
|
991
|
+
protectedProps.indexOf(name) !== -1 ||
|
|
749
992
|
this.$privateData.indexOf(name) !== -1
|
|
750
993
|
) {
|
|
751
994
|
continue;
|
|
@@ -776,8 +1019,12 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
776
1019
|
}
|
|
777
1020
|
}
|
|
778
1021
|
|
|
779
|
-
public $putData(
|
|
780
|
-
|
|
1022
|
+
public $putData(
|
|
1023
|
+
data: EntityData,
|
|
1024
|
+
sdata?: SerializedEntityData,
|
|
1025
|
+
_source?: 'server',
|
|
1026
|
+
) {
|
|
1027
|
+
this.$check();
|
|
781
1028
|
|
|
782
1029
|
const mySdata = sdata ?? this.$getSData();
|
|
783
1030
|
for (const name in data) {
|
|
@@ -790,13 +1037,13 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
790
1037
|
(this.$dataStore as any)[name] = referencesToEntities(
|
|
791
1038
|
data[name],
|
|
792
1039
|
this.$nymph,
|
|
793
|
-
this.$skipAc
|
|
1040
|
+
this.$skipAc,
|
|
794
1041
|
);
|
|
795
1042
|
}
|
|
796
1043
|
this.$sdata = mySdata;
|
|
797
1044
|
// Set original AC values if not set..
|
|
798
1045
|
if (this.$originalAcValues == null) {
|
|
799
|
-
this.$originalAcValues = this.$
|
|
1046
|
+
this.$originalAcValues = this.$getCurrentAcValues();
|
|
800
1047
|
}
|
|
801
1048
|
}
|
|
802
1049
|
|
|
@@ -813,14 +1060,14 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
813
1060
|
) {
|
|
814
1061
|
throw new InvalidParametersError(
|
|
815
1062
|
'referenceSleep expects parameter 1 to be a valid Nymph entity ' +
|
|
816
|
-
'reference.'
|
|
1063
|
+
'reference.',
|
|
817
1064
|
);
|
|
818
1065
|
}
|
|
819
1066
|
const thisClass: string = (this.constructor as any).class;
|
|
820
1067
|
if (reference[2] !== thisClass) {
|
|
821
1068
|
throw new InvalidParametersError(
|
|
822
1069
|
'referenceSleep can only be called with an entity reference of the ' +
|
|
823
|
-
`same class. Given class: ${reference[2]}; this class: ${thisClass}
|
|
1070
|
+
`same class. Given class: ${reference[2]}; this class: ${thisClass}.`,
|
|
824
1071
|
);
|
|
825
1072
|
}
|
|
826
1073
|
this.$isASleepingReference = true;
|
|
@@ -828,49 +1075,141 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
828
1075
|
this.$sleepingReference = reference;
|
|
829
1076
|
}
|
|
830
1077
|
|
|
1078
|
+
/**
|
|
1079
|
+
* Check if this is a sleeping reference and throw an error if so.
|
|
1080
|
+
*/
|
|
1081
|
+
protected $check() {
|
|
1082
|
+
if (this.$isASleepingReference || this.$sleepingReference != null) {
|
|
1083
|
+
throw new EntityIsSleepingReferenceError(
|
|
1084
|
+
'This entity is in a sleeping reference state. You must use .$wake() to wake it.',
|
|
1085
|
+
);
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
/**
|
|
1090
|
+
* Check if this is a sleeping reference.
|
|
1091
|
+
*/
|
|
1092
|
+
public $asleep() {
|
|
1093
|
+
return this.$isASleepingReference || this.$sleepingReference != null;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
831
1096
|
/**
|
|
832
1097
|
* Wake from a sleeping reference.
|
|
833
|
-
*
|
|
834
|
-
* @returns True on success, false on failure.
|
|
835
1098
|
*/
|
|
836
|
-
|
|
1099
|
+
public $wake() {
|
|
837
1100
|
if (!this.$isASleepingReference || this.$sleepingReference == null) {
|
|
838
|
-
|
|
1101
|
+
this.$wakePromise = null;
|
|
1102
|
+
return Promise.resolve(this);
|
|
839
1103
|
}
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
skipAc: this.$skipAc,
|
|
845
|
-
},
|
|
846
|
-
{ type: '&', guid: this.$sleepingReference[1] }
|
|
847
|
-
);
|
|
848
|
-
if (entity == null) {
|
|
849
|
-
return false;
|
|
1104
|
+
if (this.$sleepingReference?.[1] == null) {
|
|
1105
|
+
throw new InvalidStateError(
|
|
1106
|
+
'Tried to wake a sleeping reference with no GUID.',
|
|
1107
|
+
);
|
|
850
1108
|
}
|
|
851
|
-
this.$
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
1109
|
+
if (!this.$wakePromise) {
|
|
1110
|
+
const EntityClass = this.$nymph.getEntityClass(
|
|
1111
|
+
this.$sleepingReference[2],
|
|
1112
|
+
);
|
|
1113
|
+
this.$wakePromise = this.$nymph
|
|
1114
|
+
.getEntity(
|
|
1115
|
+
{
|
|
1116
|
+
class: EntityClass,
|
|
1117
|
+
skipAc: this.$skipAc,
|
|
1118
|
+
},
|
|
1119
|
+
{ type: '&', guid: this.$sleepingReference[1] },
|
|
1120
|
+
)
|
|
1121
|
+
.then((entity) => {
|
|
1122
|
+
if (entity == null || entity.guid == null) {
|
|
1123
|
+
return Promise.reject(
|
|
1124
|
+
new InvalidStateError(
|
|
1125
|
+
'The sleeping reference could not be retrieved.',
|
|
1126
|
+
),
|
|
1127
|
+
);
|
|
1128
|
+
}
|
|
1129
|
+
this.$isASleepingReference = false;
|
|
1130
|
+
this.$sleepingReference = null;
|
|
1131
|
+
this.guid = entity.guid;
|
|
1132
|
+
this.tags = entity.tags;
|
|
1133
|
+
this.cdate = entity.cdate;
|
|
1134
|
+
this.mdate = entity.mdate;
|
|
1135
|
+
this.$putData(
|
|
1136
|
+
entity.$getData(false, true),
|
|
1137
|
+
entity.$getSData(),
|
|
1138
|
+
'server',
|
|
1139
|
+
);
|
|
1140
|
+
|
|
1141
|
+
return this;
|
|
1142
|
+
})
|
|
1143
|
+
.finally(() => {
|
|
1144
|
+
this.$wakePromise = null;
|
|
1145
|
+
});
|
|
1146
|
+
}
|
|
1147
|
+
return this.$wakePromise;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
public $wakeAll(level?: number) {
|
|
1151
|
+
return new Promise((resolve, reject) => {
|
|
1152
|
+
// Run this once this entity is wake.
|
|
1153
|
+
const wakeProps = () => {
|
|
1154
|
+
let newLevel;
|
|
1155
|
+
// If level is undefined, keep going forever, otherwise, stop once we've
|
|
1156
|
+
// gone deep enough.
|
|
1157
|
+
if (level !== undefined) {
|
|
1158
|
+
newLevel = level - 1;
|
|
1159
|
+
}
|
|
1160
|
+
if (newLevel !== undefined && newLevel < 0) {
|
|
1161
|
+
resolve(this);
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
const promises = [];
|
|
1165
|
+
// Go through data looking for entities to wake.
|
|
1166
|
+
for (let [key, value] of Object.entries(this.$data)) {
|
|
1167
|
+
if (value instanceof Entity && value.$isASleepingReference) {
|
|
1168
|
+
promises.push(value.$wakeAll(newLevel));
|
|
1169
|
+
} else if (Array.isArray(value)) {
|
|
1170
|
+
for (let i = 0; i < value.length; i++) {
|
|
1171
|
+
if (
|
|
1172
|
+
value[i] instanceof Entity &&
|
|
1173
|
+
value[i].$isASleepingReference
|
|
1174
|
+
) {
|
|
1175
|
+
promises.push(value[i].$wakeAll(newLevel));
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
if (promises.length) {
|
|
1181
|
+
Promise.all(promises).then(
|
|
1182
|
+
() => resolve(this),
|
|
1183
|
+
(errObj) => reject(errObj),
|
|
1184
|
+
);
|
|
1185
|
+
} else {
|
|
1186
|
+
resolve(this);
|
|
1187
|
+
}
|
|
1188
|
+
};
|
|
1189
|
+
|
|
1190
|
+
if (this.$isASleepingReference) {
|
|
1191
|
+
this.$wake().then(wakeProps, (errObj) => reject(errObj));
|
|
1192
|
+
} else {
|
|
1193
|
+
wakeProps();
|
|
1194
|
+
}
|
|
1195
|
+
}) as Promise<Entity<T>>;
|
|
859
1196
|
}
|
|
860
1197
|
|
|
861
1198
|
public async $refresh() {
|
|
862
|
-
this.$
|
|
1199
|
+
await this.$wake();
|
|
863
1200
|
|
|
864
1201
|
if (this.guid == null) {
|
|
865
1202
|
return false;
|
|
866
1203
|
}
|
|
867
1204
|
const refresh = await this.$nymph.getEntity(
|
|
868
1205
|
{
|
|
869
|
-
class: this.
|
|
1206
|
+
class: this.$nymph.getEntityClass(
|
|
1207
|
+
this.constructor as EntityConstructor,
|
|
1208
|
+
),
|
|
870
1209
|
skipCache: true,
|
|
871
1210
|
skipAc: this.$skipAc,
|
|
872
1211
|
},
|
|
873
|
-
{ type: '&', guid: this.guid }
|
|
1212
|
+
{ type: '&', guid: this.guid },
|
|
874
1213
|
);
|
|
875
1214
|
if (refresh == null) {
|
|
876
1215
|
return 0;
|
|
@@ -878,32 +1217,32 @@ export default class Entity<T extends EntityData = EntityData>
|
|
|
878
1217
|
this.tags = refresh.tags;
|
|
879
1218
|
this.cdate = refresh.cdate;
|
|
880
1219
|
this.mdate = refresh.mdate;
|
|
881
|
-
this.$putData(refresh.$getData(), refresh.$getSData());
|
|
1220
|
+
this.$putData(refresh.$getData(false, true), refresh.$getSData(), 'server');
|
|
882
1221
|
return true;
|
|
883
1222
|
}
|
|
884
1223
|
|
|
885
1224
|
public $removeTag(...tags: string[]) {
|
|
886
|
-
this.$
|
|
1225
|
+
this.$check();
|
|
887
1226
|
|
|
888
1227
|
this.tags = difference(this.tags, tags);
|
|
889
1228
|
}
|
|
890
1229
|
|
|
891
1230
|
public async $save(): Promise<boolean> {
|
|
892
|
-
this.$
|
|
1231
|
+
await this.$wake();
|
|
893
1232
|
|
|
894
1233
|
return await this.$nymph.saveEntity(this);
|
|
895
1234
|
}
|
|
896
1235
|
|
|
897
|
-
public $toReference() {
|
|
1236
|
+
public $toReference(existingOnly?: boolean) {
|
|
898
1237
|
if (this.$isASleepingReference && this.$sleepingReference != null) {
|
|
899
1238
|
return this.$sleepingReference;
|
|
900
1239
|
}
|
|
901
|
-
if (this.guid == null) {
|
|
1240
|
+
if (existingOnly && this.guid == null) {
|
|
902
1241
|
return this;
|
|
903
1242
|
}
|
|
904
1243
|
return [
|
|
905
1244
|
'nymph_entity_reference',
|
|
906
|
-
this
|
|
1245
|
+
this.$getGuaranteedGUID(),
|
|
907
1246
|
(this.constructor as any).class as string,
|
|
908
1247
|
] as EntityReference;
|
|
909
1248
|
}
|