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