@nymphjs/nymph 1.0.0-beta.40 → 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 CHANGED
@@ -3,6 +3,13 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [1.0.0-beta.41](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.40...v1.0.0-beta.41) (2023-07-12)
7
+
8
+ ### Features
9
+
10
+ - remove synchronous database queries ([b579fb2](https://github.com/sciactive/nymphjs/commit/b579fb2eacd96cdd1b386a62c5c00cdbb2438f6e))
11
+ - rewrite server side async api to match client side api ([9c537a8](https://github.com/sciactive/nymphjs/commit/9c537a8be49e9b989af0822a4c2236e8c2d20f87))
12
+
6
13
  # [1.0.0-beta.40](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.39...v1.0.0-beta.40) (2023-07-10)
7
14
 
8
15
  **Note:** Version bump only for package @nymphjs/nymph
package/README.md CHANGED
@@ -76,19 +76,17 @@ export default class Todo extends Entity<TodoData> {
76
76
  return (await super.factory(guid)) as Todo & TodoData;
77
77
  }
78
78
 
79
- static factorySync(guid?: string): Todo & TodoData {
80
- return super.factorySync(guid) as Todo & TodoData;
79
+ static factorySync(): Todo & TodoData {
80
+ return super.factorySync() as Todo & TodoData;
81
81
  }
82
82
 
83
- constructor(guid?: string) {
84
- super(guid);
83
+ constructor() {
84
+ super();
85
85
 
86
- if (this.guid == null) {
87
- // Within the methods of an entity, you will use `this.$data` to access
88
- // its data. Outside, you don't need the $data part.
89
- this.$data.text = '';
90
- this.$data.done = false;
91
- }
86
+ // Within the methods of an entity, you will use `this.$data` to access
87
+ // its data. Outside, you don't need the $data part.
88
+ this.$data.text = '';
89
+ this.$data.done = false;
92
90
  }
93
91
 
94
92
  async $getOtherTodos() {
package/dist/Entity.d.ts CHANGED
@@ -15,6 +15,7 @@ export default class Entity<T extends EntityData = EntityData> implements Entity
15
15
  protected $data: T;
16
16
  protected $isASleepingReference: boolean;
17
17
  protected $sleepingReference: EntityReference | null;
18
+ protected $wakePromise: Promise<Entity<T>> | null;
18
19
  protected $privateData: string[];
19
20
  static pubSubEnabled: boolean;
20
21
  static restEnabled: boolean;
@@ -27,9 +28,9 @@ export default class Entity<T extends EntityData = EntityData> implements Entity
27
28
  static clientEnabledStaticMethods: string[];
28
29
  private $skipAc;
29
30
  private $originalAcValues;
30
- constructor(guid?: string);
31
+ constructor();
31
32
  static factory(guid?: string): Promise<EntityInterface | Entity<EntityData>>;
32
- static factorySync(guid?: string): Entity<EntityData>;
33
+ static factorySync(): Entity<EntityData>;
33
34
  static factoryReference(reference: EntityReference): Entity<EntityData>;
34
35
  toJSON(): EntityReference | EntityJson | null;
35
36
  $addTag(...tags: string[]): void;
@@ -41,6 +42,21 @@ export default class Entity<T extends EntityData = EntityData> implements Entity
41
42
  $getData(includeSData?: boolean): any;
42
43
  $getSData(): SerializedEntityData;
43
44
  $getOriginalAcValues(): ACProperties;
45
+ $getCurrentAcValues(): {
46
+ user: string | null;
47
+ group: string | null;
48
+ acUser: any;
49
+ acGroup: any;
50
+ acOther: any;
51
+ acRead: string[] | null;
52
+ acWrite: string[] | null;
53
+ acFull: string[] | null;
54
+ };
55
+ $getAcUid(): string | null;
56
+ $getAcGid(): string | null;
57
+ $getAcReadIds(): string[] | null;
58
+ $getAcWriteIds(): string[] | null;
59
+ $getAcFullIds(): string[] | null;
44
60
  $getValidatable(): any;
45
61
  $getTags(): string[];
46
62
  $hasTag(...tags: string[]): boolean;
@@ -50,7 +66,10 @@ export default class Entity<T extends EntityData = EntityData> implements Entity
50
66
  $jsonAcceptPatch(patch: EntityPatch, allowConflict?: boolean): void;
51
67
  $putData(data: EntityData, sdata?: SerializedEntityData): void;
52
68
  protected $referenceSleep(reference: EntityReference): void;
53
- protected $referenceWake(): boolean;
69
+ protected $check(): void;
70
+ $asleep(): boolean;
71
+ $wake(): Promise<Entity<T>>;
72
+ $wakeAll(level?: number): Promise<Entity<T>>;
54
73
  $refresh(): Promise<boolean | 0>;
55
74
  $removeTag(...tags: string[]): void;
56
75
  $save(): Promise<boolean>;
package/dist/Entity.js CHANGED
@@ -4,13 +4,14 @@ const lodash_1 = require("lodash");
4
4
  const errors_1 = require("./errors");
5
5
  const utils_1 = require("./utils");
6
6
  class Entity {
7
- constructor(guid) {
7
+ constructor() {
8
8
  this.guid = null;
9
9
  this.cdate = null;
10
10
  this.mdate = null;
11
11
  this.tags = [];
12
12
  this.$isASleepingReference = false;
13
13
  this.$sleepingReference = null;
14
+ this.$wakePromise = null;
14
15
  this.$privateData = [];
15
16
  this.$protectedData = [];
16
17
  this.$protectedTags = [];
@@ -20,11 +21,11 @@ class Entity {
20
21
  this.$nymph = this.constructor.nymph;
21
22
  this.$dataHandler = {
22
23
  has: (data, name) => {
23
- this.$referenceWake();
24
+ this.$check();
24
25
  return name in data || name in this.$sdata;
25
26
  },
26
27
  get: (data, name) => {
27
- this.$referenceWake();
28
+ this.$check();
28
29
  if (this.$sdata.hasOwnProperty(name)) {
29
30
  data[name] = (0, utils_1.referencesToEntities)(JSON.parse(this.$sdata[name]), this.$nymph, this.$skipAc);
30
31
  delete this.$sdata[name];
@@ -35,7 +36,7 @@ class Entity {
35
36
  return undefined;
36
37
  },
37
38
  set: (data, name, value) => {
38
- this.$referenceWake();
39
+ this.$check();
39
40
  if (this.$sdata.hasOwnProperty(name)) {
40
41
  delete this.$sdata[name];
41
42
  }
@@ -43,7 +44,7 @@ class Entity {
43
44
  return true;
44
45
  },
45
46
  deleteProperty: (data, name) => {
46
- this.$referenceWake();
47
+ this.$check();
47
48
  if (this.$sdata.hasOwnProperty(name)) {
48
49
  return delete this.$sdata[name];
49
50
  }
@@ -53,7 +54,7 @@ class Entity {
53
54
  return true;
54
55
  },
55
56
  defineProperty: (data, name, descriptor) => {
56
- this.$referenceWake();
57
+ this.$check();
57
58
  if (this.$sdata.hasOwnProperty(name)) {
58
59
  delete this.$sdata[name];
59
60
  }
@@ -61,7 +62,7 @@ class Entity {
61
62
  return true;
62
63
  },
63
64
  getOwnPropertyDescriptor: (data, name) => {
64
- this.$referenceWake();
65
+ this.$check();
65
66
  if (this.$sdata.hasOwnProperty(name)) {
66
67
  data[name] = (0, utils_1.referencesToEntities)(JSON.parse(this.$sdata[name]), this.$nymph, this.$skipAc);
67
68
  delete this.$sdata[name];
@@ -69,25 +70,13 @@ class Entity {
69
70
  return Object.getOwnPropertyDescriptor(data, name);
70
71
  },
71
72
  ownKeys: (data) => {
72
- this.$referenceWake();
73
+ this.$check();
73
74
  return Object.getOwnPropertyNames(data).concat(Object.getOwnPropertyNames(this.$sdata));
74
75
  },
75
76
  };
76
77
  this.$dataStore = {};
77
78
  this.$sdata = {};
78
79
  this.$data = new Proxy(this.$dataStore, this.$dataHandler);
79
- if (guid != null) {
80
- const entity = this.$nymph.driver.getEntitySync({
81
- class: this.$nymph.getEntityClass(this.constructor),
82
- }, { type: '&', guid });
83
- if (entity) {
84
- this.guid = entity.guid;
85
- this.tags = entity.tags;
86
- this.cdate = entity.cdate;
87
- this.mdate = entity.mdate;
88
- this.$putData(entity.$getData(), entity.$getSData());
89
- }
90
- }
91
80
  return new Proxy(this, {
92
81
  has: (entity, name) => {
93
82
  if (typeof name !== 'string' ||
@@ -95,7 +84,7 @@ class Entity {
95
84
  name.substring(0, 1) === '$') {
96
85
  return name in entity;
97
86
  }
98
- this.$referenceWake();
87
+ this.$check();
99
88
  return name in entity.$data;
100
89
  },
101
90
  get: (entity, name) => {
@@ -103,11 +92,11 @@ class Entity {
103
92
  name in entity ||
104
93
  name.substring(0, 1) === '$') {
105
94
  if (name === 'tags' || name === 'cdate' || name === 'mdate') {
106
- this.$referenceWake();
95
+ this.$check();
107
96
  }
108
97
  return entity[name];
109
98
  }
110
- this.$referenceWake();
99
+ this.$check();
111
100
  if (name in entity.$data) {
112
101
  return entity.$data[name];
113
102
  }
@@ -118,12 +107,12 @@ class Entity {
118
107
  name in entity ||
119
108
  name.substring(0, 1) === '$') {
120
109
  if (name === 'tags' || name === 'cdate' || name === 'mdate') {
121
- this.$referenceWake();
110
+ this.$check();
122
111
  }
123
112
  entity[name] = value;
124
113
  }
125
114
  else {
126
- this.$referenceWake();
115
+ this.$check();
127
116
  entity.$data[name] = value;
128
117
  }
129
118
  return true;
@@ -133,7 +122,7 @@ class Entity {
133
122
  return delete entity[name];
134
123
  }
135
124
  else if (name in entity.$data) {
136
- this.$referenceWake();
125
+ this.$check();
137
126
  return delete entity.$data[name];
138
127
  }
139
128
  return true;
@@ -146,12 +135,12 @@ class Entity {
146
135
  name in entity ||
147
136
  name.substring(0, 1) === '$') {
148
137
  if (name === 'tags' || name === 'cdate' || name === 'mdate') {
149
- this.$referenceWake();
138
+ this.$check();
150
139
  }
151
140
  Object.defineProperty(entity, name, descriptor);
152
141
  }
153
142
  else {
154
- this.$referenceWake();
143
+ this.$check();
155
144
  Object.defineProperty(entity.$data, name, descriptor);
156
145
  }
157
146
  return true;
@@ -161,17 +150,17 @@ class Entity {
161
150
  name in entity ||
162
151
  name.substring(0, 1) === '$') {
163
152
  if (name === 'tags' || name === 'cdate' || name === 'mdate') {
164
- this.$referenceWake();
153
+ this.$check();
165
154
  }
166
155
  return Object.getOwnPropertyDescriptor(entity, name);
167
156
  }
168
157
  else {
169
- this.$referenceWake();
158
+ this.$check();
170
159
  return Object.getOwnPropertyDescriptor(entity.$data, name);
171
160
  }
172
161
  },
173
162
  ownKeys: (entity) => {
174
- this.$referenceWake();
163
+ this.$check();
175
164
  return Object.getOwnPropertyNames(entity).concat(Object.getOwnPropertyNames(entity.$data));
176
165
  },
177
166
  });
@@ -188,8 +177,8 @@ class Entity {
188
177
  }
189
178
  return entity;
190
179
  }
191
- static factorySync(guid) {
192
- return new this(guid);
180
+ static factorySync() {
181
+ return new this();
193
182
  }
194
183
  static factoryReference(reference) {
195
184
  const entity = new this();
@@ -217,14 +206,14 @@ class Entity {
217
206
  return obj;
218
207
  }
219
208
  $addTag(...tags) {
220
- this.$referenceWake();
209
+ this.$check();
221
210
  if (tags.length < 1) {
222
211
  return;
223
212
  }
224
213
  this.tags = (0, utils_1.uniqueStrings)([...this.tags, ...tags]);
225
214
  }
226
215
  $arraySearch(array, strict = false) {
227
- this.$referenceWake();
216
+ this.$check();
228
217
  if (!Array.isArray(array)) {
229
218
  return -1;
230
219
  }
@@ -237,18 +226,18 @@ class Entity {
237
226
  return -1;
238
227
  }
239
228
  $clearCache() {
240
- this.$referenceWake();
229
+ this.$check();
241
230
  this.$putData(this.$getData(), this.$getSData());
242
231
  }
243
232
  $getClientEnabledMethods() {
244
233
  return this.$clientEnabledMethods;
245
234
  }
246
235
  async $delete() {
247
- this.$referenceWake();
236
+ this.$check();
248
237
  return await this.$nymph.deleteEntity(this);
249
238
  }
250
239
  $equals(object) {
251
- this.$referenceWake();
240
+ this.$check();
252
241
  if (!(object instanceof Entity)) {
253
242
  return false;
254
243
  }
@@ -273,7 +262,7 @@ class Entity {
273
262
  return (0, lodash_1.isEqual)(obData, myData);
274
263
  }
275
264
  $getData(includeSData = false) {
276
- this.$referenceWake();
265
+ this.$check();
277
266
  if (includeSData) {
278
267
  for (const key in this.$sdata) {
279
268
  const _unused = this[key];
@@ -282,24 +271,43 @@ class Entity {
282
271
  return (0, utils_1.entitiesToReferences)({ ...this.$dataStore });
283
272
  }
284
273
  $getSData() {
285
- this.$referenceWake();
274
+ this.$check();
286
275
  return this.$sdata;
287
276
  }
288
277
  $getOriginalAcValues() {
289
- this.$referenceWake();
290
- return (this.$originalAcValues ?? {
291
- user: this.$data.user,
292
- group: this.$data.group,
278
+ this.$check();
279
+ return this.$originalAcValues ?? this.$getCurrentAcValues();
280
+ }
281
+ $getCurrentAcValues() {
282
+ this.$check();
283
+ return {
284
+ user: this.$getAcUid(),
285
+ group: this.$getAcGid(),
293
286
  acUser: this.$data.acUser,
294
287
  acGroup: this.$data.acGroup,
295
288
  acOther: this.$data.acOther,
296
- acRead: this.$data.acRead,
297
- acWrite: this.$data.acWrite,
298
- acFull: this.$data.acFull,
299
- });
289
+ acRead: this.$getAcReadIds(),
290
+ acWrite: this.$getAcWriteIds(),
291
+ acFull: this.$getAcFullIds(),
292
+ };
293
+ }
294
+ $getAcUid() {
295
+ return this.$data.user?.guid;
296
+ }
297
+ $getAcGid() {
298
+ return this.$data.group?.guid;
299
+ }
300
+ $getAcReadIds() {
301
+ return this.$data.acRead?.map((entity) => entity.guid);
302
+ }
303
+ $getAcWriteIds() {
304
+ return this.$data.acWrite?.map((entity) => entity.guid);
305
+ }
306
+ $getAcFullIds() {
307
+ return this.$data.acFull?.map((entity) => entity.guid);
300
308
  }
301
309
  $getValidatable() {
302
- this.$referenceWake();
310
+ this.$check();
303
311
  for (const key in this.$sdata) {
304
312
  const _unused = this[key];
305
313
  }
@@ -312,11 +320,11 @@ class Entity {
312
320
  };
313
321
  }
314
322
  $getTags() {
315
- this.$referenceWake();
323
+ this.$check();
316
324
  return this.tags;
317
325
  }
318
326
  $hasTag(...tags) {
319
- this.$referenceWake();
327
+ this.$check();
320
328
  if (!tags.length) {
321
329
  return false;
322
330
  }
@@ -337,7 +345,7 @@ class Entity {
337
345
  if (this.guid || object.guid) {
338
346
  return this.guid === object.guid;
339
347
  }
340
- this.$referenceWake();
348
+ this.$check();
341
349
  if (typeof object.$getData !== 'function') {
342
350
  return false;
343
351
  }
@@ -353,7 +361,7 @@ class Entity {
353
361
  }
354
362
  }
355
363
  $jsonAcceptData(input, allowConflict = false) {
356
- this.$referenceWake();
364
+ this.$check();
357
365
  if (this.guid != input.guid) {
358
366
  throw new errors_1.EntityConflictError('Tried to accept JSON input for the wrong entity.');
359
367
  }
@@ -392,7 +400,9 @@ class Entity {
392
400
  protectedProps.push('acFull');
393
401
  if ((this.constructor.class !== 'User' &&
394
402
  this.constructor.class !== 'Group') ||
395
- !this.$nymph.tilmeld.gatekeeper('tilmeld/admin')) {
403
+ !this.$nymph.tilmeld.currentUser ||
404
+ (!this.$nymph.tilmeld.currentUser.abilities?.includes('tilmeld/admin') &&
405
+ !this.$nymph.tilmeld.currentUser.abilities?.includes('system/admin'))) {
396
406
  protectedProps.push('user');
397
407
  protectedProps.push('group');
398
408
  }
@@ -425,7 +435,7 @@ class Entity {
425
435
  });
426
436
  }
427
437
  $jsonAcceptPatch(patch, allowConflict = false) {
428
- this.$referenceWake();
438
+ this.$check();
429
439
  if (this.guid != patch.guid) {
430
440
  throw new errors_1.EntityConflictError('Tried to accept JSON patch for the wrong entity.');
431
441
  }
@@ -445,7 +455,9 @@ class Entity {
445
455
  protectedProps.push('acFull');
446
456
  if ((this.constructor.class !== 'User' &&
447
457
  this.constructor.class !== 'Group') ||
448
- !this.$nymph.tilmeld.gatekeeper('tilmeld/admin')) {
458
+ !this.$nymph.tilmeld.currentUser ||
459
+ (!this.$nymph.tilmeld.currentUser.abilities?.includes('tilmeld/admin') &&
460
+ !this.$nymph.tilmeld.currentUser.abilities?.includes('system/admin'))) {
449
461
  protectedProps.push('user');
450
462
  protectedProps.push('group');
451
463
  }
@@ -486,7 +498,7 @@ class Entity {
486
498
  }
487
499
  }
488
500
  $putData(data, sdata) {
489
- this.$referenceWake();
501
+ this.$check();
490
502
  const mySdata = sdata ?? this.$getSData();
491
503
  for (const name in data) {
492
504
  delete mySdata[name];
@@ -499,7 +511,7 @@ class Entity {
499
511
  }
500
512
  this.$sdata = mySdata;
501
513
  if (this.$originalAcValues == null) {
502
- this.$originalAcValues = this.$getOriginalAcValues();
514
+ this.$originalAcValues = this.$getCurrentAcValues();
503
515
  }
504
516
  }
505
517
  $referenceSleep(reference) {
@@ -519,29 +531,90 @@ class Entity {
519
531
  this.guid = reference[1];
520
532
  this.$sleepingReference = reference;
521
533
  }
522
- $referenceWake() {
523
- if (!this.$isASleepingReference || this.$sleepingReference == null) {
524
- return true;
534
+ $check() {
535
+ if (this.$isASleepingReference || this.$sleepingReference != null) {
536
+ throw new errors_1.EntityIsSleepingReferenceError('This entity is in a sleeping reference state. You must use .$wake() to wake it.');
525
537
  }
526
- const EntityClass = this.$nymph.getEntityClass(this.$sleepingReference[2]);
527
- const entity = this.$nymph.driver.getEntitySync({
528
- class: EntityClass,
529
- skipAc: this.$skipAc,
530
- }, { type: '&', guid: this.$sleepingReference[1] });
531
- if (entity == null) {
532
- return false;
538
+ }
539
+ $asleep() {
540
+ return this.$isASleepingReference || this.$sleepingReference != null;
541
+ }
542
+ $wake() {
543
+ if (!this.$isASleepingReference || this.$sleepingReference == null) {
544
+ this.$wakePromise = null;
545
+ return Promise.resolve(this);
546
+ }
547
+ if (this.$sleepingReference?.[1] == null) {
548
+ throw new errors_1.InvalidStateError('Tried to wake a sleeping reference with no GUID.');
549
+ }
550
+ if (!this.$wakePromise) {
551
+ const EntityClass = this.$nymph.getEntityClass(this.$sleepingReference[2]);
552
+ this.$wakePromise = this.$nymph
553
+ .getEntity({
554
+ class: EntityClass,
555
+ skipAc: this.$skipAc,
556
+ }, { type: '&', guid: this.$sleepingReference[1] })
557
+ .then((entity) => {
558
+ if (entity == null || entity.guid == null) {
559
+ return Promise.reject(new errors_1.InvalidStateError('The sleeping reference could not be retrieved.'));
560
+ }
561
+ this.$isASleepingReference = false;
562
+ this.$sleepingReference = null;
563
+ this.guid = entity.guid;
564
+ this.tags = entity.tags;
565
+ this.cdate = entity.cdate;
566
+ this.mdate = entity.mdate;
567
+ this.$putData(entity.$getData(), entity.$getSData());
568
+ return this;
569
+ })
570
+ .finally(() => {
571
+ this.$wakePromise = null;
572
+ });
533
573
  }
534
- this.$isASleepingReference = false;
535
- this.$sleepingReference = null;
536
- this.guid = entity.guid;
537
- this.tags = entity.tags;
538
- this.cdate = entity.cdate;
539
- this.mdate = entity.mdate;
540
- this.$putData(entity.$getData(), entity.$getSData());
541
- return true;
574
+ return this.$wakePromise;
575
+ }
576
+ $wakeAll(level) {
577
+ return new Promise((resolve, reject) => {
578
+ const wakeProps = () => {
579
+ let newLevel;
580
+ if (level !== undefined) {
581
+ newLevel = level - 1;
582
+ }
583
+ if (newLevel !== undefined && newLevel < 0) {
584
+ resolve(this);
585
+ return;
586
+ }
587
+ const promises = [];
588
+ for (let [key, value] of Object.entries(this.$data)) {
589
+ if (value instanceof Entity && value.$isASleepingReference) {
590
+ promises.push(value.$wakeAll(newLevel));
591
+ }
592
+ else if (Array.isArray(value)) {
593
+ for (let i = 0; i < value.length; i++) {
594
+ if (value[i] instanceof Entity &&
595
+ value[i].$isASleepingReference) {
596
+ promises.push(value[i].$wakeAll(newLevel));
597
+ }
598
+ }
599
+ }
600
+ }
601
+ if (promises.length) {
602
+ Promise.all(promises).then(() => resolve(this), (errObj) => reject(errObj));
603
+ }
604
+ else {
605
+ resolve(this);
606
+ }
607
+ };
608
+ if (this.$isASleepingReference) {
609
+ this.$wake().then(wakeProps, (errObj) => reject(errObj));
610
+ }
611
+ else {
612
+ wakeProps();
613
+ }
614
+ });
542
615
  }
543
616
  async $refresh() {
544
- this.$referenceWake();
617
+ await this.$wake();
545
618
  if (this.guid == null) {
546
619
  return false;
547
620
  }
@@ -560,11 +633,11 @@ class Entity {
560
633
  return true;
561
634
  }
562
635
  $removeTag(...tags) {
563
- this.$referenceWake();
636
+ this.$check();
564
637
  this.tags = (0, lodash_1.difference)(this.tags, tags);
565
638
  }
566
639
  async $save() {
567
- this.$referenceWake();
640
+ await this.$wake();
568
641
  return await this.$nymph.saveEntity(this);
569
642
  }
570
643
  $toReference() {