@nymphjs/nymph 1.0.0-beta.8 → 1.0.0-beta.81

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.
Files changed (65) hide show
  1. package/CHANGELOG.md +382 -0
  2. package/README.md +9 -19
  3. package/dist/Entity.d.ts +269 -7
  4. package/dist/Entity.js +491 -105
  5. package/dist/Entity.js.map +1 -1
  6. package/dist/Entity.test.js +25 -3
  7. package/dist/Entity.test.js.map +1 -1
  8. package/dist/Entity.types.d.ts +225 -19
  9. package/dist/Nymph.d.ts +430 -11
  10. package/dist/Nymph.js +686 -168
  11. package/dist/Nymph.js.map +1 -1
  12. package/dist/Nymph.types.d.ts +65 -7
  13. package/dist/Nymph.types.js +2 -1
  14. package/dist/Nymph.types.js.map +1 -1
  15. package/dist/conf/d.d.ts +30 -0
  16. package/dist/conf/defaults.js +27 -0
  17. package/dist/conf/defaults.js.map +1 -1
  18. package/dist/driver/NymphDriver.d.ts +136 -38
  19. package/dist/driver/NymphDriver.js +264 -131
  20. package/dist/driver/NymphDriver.js.map +1 -1
  21. package/dist/driver/testArtifacts.d.ts +1 -1
  22. package/dist/driver/testArtifacts.js +499 -51
  23. package/dist/driver/testArtifacts.js.map +1 -1
  24. package/dist/errors/EntityInvalidDataError.d.ts +6 -0
  25. package/dist/errors/EntityInvalidDataError.js +7 -1
  26. package/dist/errors/EntityInvalidDataError.js.map +1 -1
  27. package/dist/errors/EntityUniqueConstraintError.d.ts +3 -0
  28. package/dist/errors/EntityUniqueConstraintError.js +11 -0
  29. package/dist/errors/EntityUniqueConstraintError.js.map +1 -0
  30. package/dist/errors/InvalidStateError.d.ts +3 -0
  31. package/dist/errors/InvalidStateError.js +11 -0
  32. package/dist/errors/InvalidStateError.js.map +1 -0
  33. package/dist/errors/QueryFailedError.js +1 -0
  34. package/dist/errors/QueryFailedError.js.map +1 -1
  35. package/dist/errors/index.d.ts +2 -0
  36. package/dist/errors/index.js +2 -0
  37. package/dist/errors/index.js.map +1 -1
  38. package/dist/testArtifacts.d.ts +17 -5
  39. package/dist/testArtifacts.js +31 -27
  40. package/dist/testArtifacts.js.map +1 -1
  41. package/dist/testMocks.d.ts +13 -10
  42. package/dist/testMocks.js +32 -13
  43. package/dist/testMocks.js.map +1 -1
  44. package/dist/utils.d.ts +1 -1
  45. package/dist/utils.js +21 -5
  46. package/dist/utils.js.map +1 -1
  47. package/package.json +16 -14
  48. package/src/Entity.test.ts +22 -5
  49. package/src/Entity.ts +423 -134
  50. package/src/Entity.types.ts +32 -66
  51. package/src/Nymph.ts +609 -176
  52. package/src/Nymph.types.ts +131 -26
  53. package/src/conf/d.ts +12 -0
  54. package/src/conf/defaults.ts +31 -0
  55. package/src/driver/NymphDriver.ts +289 -311
  56. package/src/driver/testArtifacts.ts +572 -181
  57. package/src/errors/EntityUniqueConstraintError.ts +6 -0
  58. package/src/errors/InvalidStateError.ts +6 -0
  59. package/src/errors/index.ts +4 -0
  60. package/src/nymphJoiProps.ts +2 -2
  61. package/src/testArtifacts.ts +18 -19
  62. package/src/testMocks.ts +55 -30
  63. package/src/utils.ts +22 -8
  64. package/tsconfig.json +3 -3
  65. package/typedoc.json +4 -0
package/src/Nymph.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Config, ConfigDefaults as defaults } from './conf';
2
- import { NymphDriver } from './driver';
3
- import Entity from './Entity';
4
- import { EntityConstructor, EntityInterface } from './Entity.types';
2
+ import type { NymphDriver } from './driver';
3
+ import Entity, { type EntityInstanceType } from './Entity';
4
+ import type { EntityConstructor, EntityInterface } from './Entity.types';
5
5
  import { ClassNotAvailableError } from './errors';
6
6
  import type {
7
7
  Selector,
@@ -12,18 +12,31 @@ import type {
12
12
  NymphBeforeGetEntitiesCallback,
13
13
  NymphBeforeSaveEntityCallback,
14
14
  NymphAfterSaveEntityCallback,
15
+ NymphFailedSaveEntityCallback,
15
16
  NymphBeforeDeleteEntityCallback,
16
17
  NymphAfterDeleteEntityCallback,
18
+ NymphFailedDeleteEntityCallback,
17
19
  NymphBeforeDeleteEntityByIDCallback,
18
20
  NymphAfterDeleteEntityByIDCallback,
21
+ NymphFailedDeleteEntityByIDCallback,
19
22
  NymphBeforeNewUIDCallback,
20
23
  NymphAfterNewUIDCallback,
24
+ NymphFailedNewUIDCallback,
21
25
  NymphBeforeSetUIDCallback,
22
26
  NymphAfterSetUIDCallback,
27
+ NymphFailedSetUIDCallback,
23
28
  NymphBeforeRenameUIDCallback,
24
29
  NymphAfterRenameUIDCallback,
30
+ NymphFailedRenameUIDCallback,
25
31
  NymphBeforeDeleteUIDCallback,
26
32
  NymphAfterDeleteUIDCallback,
33
+ NymphFailedDeleteUIDCallback,
34
+ NymphBeforeStartTransactionCallback,
35
+ NymphAfterStartTransactionCallback,
36
+ NymphBeforeCommitTransactionCallback,
37
+ NymphAfterCommitTransactionCallback,
38
+ NymphBeforeRollbackTransactionCallback,
39
+ NymphAfterRollbackTransactionCallback,
27
40
  NymphEventType,
28
41
  NymphQueryCallback,
29
42
  FormattedSelector,
@@ -41,10 +54,16 @@ import type {
41
54
  */
42
55
  export default class Nymph {
43
56
  /**
44
- * The PubSub config.
57
+ * The Nymph config.
45
58
  */
46
59
  public config: Config;
47
60
 
61
+ /**
62
+ * The Nymph instance that this one was cloned from, or null if it's not a
63
+ * clone.
64
+ */
65
+ public parent: Nymph | null;
66
+
48
67
  /**
49
68
  * The Nymph database driver.
50
69
  */
@@ -72,20 +91,40 @@ export default class Nymph {
72
91
  private beforeGetEntitiesCallbacks: NymphBeforeGetEntitiesCallback[] = [];
73
92
  private beforeSaveEntityCallbacks: NymphBeforeSaveEntityCallback[] = [];
74
93
  private afterSaveEntityCallbacks: NymphAfterSaveEntityCallback[] = [];
94
+ private failedSaveEntityCallbacks: NymphFailedSaveEntityCallback[] = [];
75
95
  private beforeDeleteEntityCallbacks: NymphBeforeDeleteEntityCallback[] = [];
76
96
  private afterDeleteEntityCallbacks: NymphAfterDeleteEntityCallback[] = [];
97
+ private failedDeleteEntityCallbacks: NymphFailedDeleteEntityCallback[] = [];
77
98
  private beforeDeleteEntityByIDCallbacks: NymphBeforeDeleteEntityByIDCallback[] =
78
99
  [];
79
100
  private afterDeleteEntityByIDCallbacks: NymphAfterDeleteEntityByIDCallback[] =
80
101
  [];
102
+ private failedDeleteEntityByIDCallbacks: NymphFailedDeleteEntityByIDCallback[] =
103
+ [];
81
104
  private beforeNewUIDCallbacks: NymphBeforeNewUIDCallback[] = [];
82
105
  private afterNewUIDCallbacks: NymphAfterNewUIDCallback[] = [];
106
+ private failedNewUIDCallbacks: NymphFailedNewUIDCallback[] = [];
83
107
  private beforeSetUIDCallbacks: NymphBeforeSetUIDCallback[] = [];
84
108
  private afterSetUIDCallbacks: NymphAfterSetUIDCallback[] = [];
109
+ private failedSetUIDCallbacks: NymphFailedSetUIDCallback[] = [];
85
110
  private beforeRenameUIDCallbacks: NymphBeforeRenameUIDCallback[] = [];
86
111
  private afterRenameUIDCallbacks: NymphAfterRenameUIDCallback[] = [];
112
+ private failedRenameUIDCallbacks: NymphFailedRenameUIDCallback[] = [];
87
113
  private beforeDeleteUIDCallbacks: NymphBeforeDeleteUIDCallback[] = [];
88
114
  private afterDeleteUIDCallbacks: NymphAfterDeleteUIDCallback[] = [];
115
+ private failedDeleteUIDCallbacks: NymphFailedDeleteUIDCallback[] = [];
116
+ private beforeStartTransactionCallbacks: NymphBeforeStartTransactionCallback[] =
117
+ [];
118
+ private afterStartTransactionCallbacks: NymphAfterStartTransactionCallback[] =
119
+ [];
120
+ private beforeCommitTransactionCallbacks: NymphBeforeCommitTransactionCallback[] =
121
+ [];
122
+ private afterCommitTransactionCallbacks: NymphAfterCommitTransactionCallback[] =
123
+ [];
124
+ private beforeRollbackTransactionCallbacks: NymphBeforeRollbackTransactionCallback[] =
125
+ [];
126
+ private afterRollbackTransactionCallbacks: NymphAfterRollbackTransactionCallback[] =
127
+ [];
89
128
 
90
129
  /**
91
130
  * Initialize Nymph.
@@ -93,24 +132,90 @@ export default class Nymph {
93
132
  * @param config The Nymph configuration.
94
133
  * @param driver The Nymph database driver.
95
134
  * @param tilmeld The Tilmeld user/group manager instance, if you want to use it.
135
+ * @param parent Used internally by Nymph. Don't set this.
96
136
  */
97
137
  public constructor(
98
138
  config: Partial<Config>,
99
139
  driver: NymphDriver,
100
- tilmeld?: TilmeldInterface
140
+ tilmeld?: TilmeldInterface,
141
+ parent?: Nymph,
101
142
  ) {
102
143
  this.config = { ...defaults, ...config };
103
144
  this.driver = driver;
145
+ this.parent = parent ?? null;
104
146
 
105
147
  this.Entity = this.addEntityClass(Entity);
106
148
 
107
- if (typeof tilmeld !== 'undefined') {
149
+ if (parent) {
150
+ for (const name in parent.entityClasses) {
151
+ if (
152
+ name === 'Entity' ||
153
+ (parent.entityClasses[name] as any).skipOnClone
154
+ ) {
155
+ continue;
156
+ }
157
+ this.addEntityClass(parent.entityClasses[name]);
158
+ }
159
+
160
+ const events = [
161
+ 'connect',
162
+ 'disconnect',
163
+ 'query',
164
+ 'beforeGetEntity',
165
+ 'beforeGetEntities',
166
+ 'beforeSaveEntity',
167
+ 'afterSaveEntity',
168
+ 'failedSaveEntity',
169
+ 'beforeDeleteEntity',
170
+ 'afterDeleteEntity',
171
+ 'failedDeleteEntity',
172
+ 'beforeDeleteEntityByID',
173
+ 'afterDeleteEntityByID',
174
+ 'failedDeleteEntityByID',
175
+ 'beforeNewUID',
176
+ 'afterNewUID',
177
+ 'failedNewUID',
178
+ 'beforeSetUID',
179
+ 'afterSetUID',
180
+ 'failedSetUID',
181
+ 'beforeRenameUID',
182
+ 'afterRenameUID',
183
+ 'failedRenameUID',
184
+ 'beforeDeleteUID',
185
+ 'afterDeleteUID',
186
+ 'failedDeleteUID',
187
+ 'beforeStartTransaction',
188
+ 'afterStartTransaction',
189
+ 'beforeCommitTransaction',
190
+ 'afterCommitTransaction',
191
+ 'beforeRollbackTransaction',
192
+ 'afterRollbackTransaction',
193
+ ];
194
+
195
+ for (let event of events) {
196
+ const prop = event + 'Callbacks';
197
+ // @ts-ignore: The callback should be the right type here.
198
+ const callbacks = parent[prop];
199
+ for (let callback of callbacks) {
200
+ if (callback.skipOnClone) {
201
+ continue;
202
+ }
203
+ this.on(event as NymphEventType, callback);
204
+ }
205
+ }
206
+ }
207
+
208
+ this.config.debugInfo('nymph', 'Nymph loaded.');
209
+
210
+ if (tilmeld != null) {
108
211
  this.tilmeld = tilmeld;
109
212
  }
110
213
 
111
214
  this.driver.init(this);
215
+ this.config.debugInfo('nymph', 'Nymph driver loaded.');
112
216
  if (this.tilmeld) {
113
217
  this.tilmeld.init(this);
218
+ this.config.debugInfo('nymph', 'Tilmeld loaded.');
114
219
  }
115
220
  }
116
221
 
@@ -124,82 +229,93 @@ export default class Nymph {
124
229
  * Because this creates a subclass, don't use the class
125
230
  * returned from `getEntityClass` to check with `instanceof`.
126
231
  */
127
- public addEntityClass<T extends EntityConstructor>(entityClass: T): T {
232
+ public addEntityClass<T extends EntityConstructor>(EntityClass: T): T {
233
+ if (EntityClass.class in this.entityClasses) {
234
+ this.config.debugLog(
235
+ 'nymph',
236
+ `Adding duplicate class "${EntityClass.class}". This may be a mistake.`,
237
+ );
238
+ }
239
+
240
+ // Check that it doesn't have the same etype as any other class.
241
+ for (const ExistingClass of Object.values(this.entityClasses)) {
242
+ if (
243
+ EntityClass.ETYPE === ExistingClass.ETYPE &&
244
+ EntityClass.class !== EntityClass.class
245
+ ) {
246
+ this.config.debugLog(
247
+ 'nymph',
248
+ `Adding class "${EntityClass.class}" with same etype as existing class "${ExistingClass.class}". This may be a mistake.`,
249
+ );
250
+ }
251
+ }
252
+
128
253
  const nymph = this;
129
- class NymphEntity extends entityClass {
254
+ this.entityClasses[EntityClass.class] = class extends EntityClass {
130
255
  static nymph: Nymph = nymph;
131
256
 
132
257
  constructor(...args: any[]) {
133
258
  super(...args);
134
259
  }
135
- }
136
- this.entityClasses[entityClass.class] = NymphEntity;
137
- return NymphEntity;
260
+ };
261
+ return this.entityClasses[EntityClass.class] as T;
138
262
  }
139
263
 
140
264
  /**
141
265
  * Get the class that uses the specified class name.
142
266
  */
143
- public getEntityClass(className: string): EntityConstructor {
144
- if (className in this.entityClasses) {
145
- return this.entityClasses[className];
267
+ public getEntityClass(className: string): EntityConstructor;
268
+ public getEntityClass<T extends EntityConstructor>(className: T): T;
269
+ public getEntityClass<T extends EntityConstructor = EntityConstructor>(
270
+ className: T | string,
271
+ ): T | EntityConstructor {
272
+ let key: string | null = null;
273
+ if (typeof className === 'string') {
274
+ key = className;
275
+ } else {
276
+ key = className.class;
146
277
  }
147
- throw new ClassNotAvailableError('Tried to use class: ' + className);
278
+ if (key in this.entityClasses) {
279
+ return this.entityClasses[key];
280
+ }
281
+ this.config.debugError('nymph', `Tried to use missing class "${key}".`);
282
+ throw new ClassNotAvailableError(`Tried to use missing class "${key}".`);
148
283
  }
149
284
 
150
285
  /**
151
- * Get a clone of this instance with duplicate classes and event listeners.
286
+ * Get the class that uses the specified etype.
287
+ *
288
+ * Note that it is fine, though unusual, for two classes to use the same
289
+ * etype. However, this can lead to very hard to diagnose bugs, so is
290
+ * generally discouraged.
291
+ */
292
+ public getEntityClassByEtype(etype: string): EntityConstructor {
293
+ for (const EntityClass of Object.values(this.entityClasses)) {
294
+ if (EntityClass.ETYPE === etype) {
295
+ return EntityClass;
296
+ }
297
+ }
298
+ this.config.debugError(
299
+ 'nymph',
300
+ `Tried to use missing class by etype "${etype}".`,
301
+ );
302
+ throw new ClassNotAvailableError(
303
+ `Tried to use missing class by etype "${etype}".`,
304
+ );
305
+ }
306
+
307
+ /**
308
+ * Get a clone of this instance with cloned classes and event listeners.
152
309
  *
153
310
  * @returns A clone of this instance.
154
311
  */
155
312
  public clone() {
156
- const nymph = new Nymph(
313
+ return new Nymph(
157
314
  this.config,
158
315
  this.driver.clone(),
159
- this.tilmeld?.clone()
316
+ this.tilmeld?.clone(),
317
+ this,
160
318
  );
161
- for (const name in this.entityClasses) {
162
- if (name === 'Entity' || (this.entityClasses[name] as any).skipOnClone) {
163
- continue;
164
- }
165
- nymph.addEntityClass(this.entityClasses[name]);
166
- }
167
-
168
- const events = [
169
- 'connect',
170
- 'disconnect',
171
- 'query',
172
- 'beforeGetEntity',
173
- 'beforeGetEntities',
174
- 'beforeSaveEntity',
175
- 'afterSaveEntity',
176
- 'beforeDeleteEntity',
177
- 'afterDeleteEntity',
178
- 'beforeDeleteEntityByID',
179
- 'afterDeleteEntityByID',
180
- 'beforeNewUID',
181
- 'afterNewUID',
182
- 'beforeSetUID',
183
- 'afterSetUID',
184
- 'beforeRenameUID',
185
- 'afterRenameUID',
186
- 'beforeDeleteUID',
187
- 'afterDeleteUID',
188
- ];
189
-
190
- for (let event of events) {
191
- const prop = event + 'Callbacks';
192
- // @ts-ignore: The callback should be the right type here.
193
- const callbacks = this[prop];
194
- for (let callback of callbacks) {
195
- if (callback.skipOnClone) {
196
- continue;
197
- }
198
- nymph.on(event as NymphEventType, callback);
199
- }
200
- }
201
-
202
- return nymph;
203
319
  }
204
320
 
205
321
  /**
@@ -208,13 +324,19 @@ export default class Nymph {
208
324
  * @returns Whether the instance is connected to the database.
209
325
  */
210
326
  public async connect(): Promise<boolean> {
211
- const result = this.driver.connect();
212
- for (let callback of this.connectCallbacks) {
213
- if (callback) {
214
- await callback(this, result);
327
+ try {
328
+ const result = this.driver.connect();
329
+ for (let callback of this.connectCallbacks) {
330
+ if (callback) {
331
+ await callback(this, result);
332
+ }
215
333
  }
334
+ this.config.debugInfo('nymph', 'Driver connected.');
335
+ return await result;
336
+ } catch (e: any) {
337
+ this.config.debugError('nymph', `Failed to connect: ${e}`);
338
+ throw e;
216
339
  }
217
- return await result;
218
340
  }
219
341
 
220
342
  /**
@@ -223,13 +345,19 @@ export default class Nymph {
223
345
  * @returns Whether the instance is connected to the database.
224
346
  */
225
347
  public async disconnect(): Promise<boolean> {
226
- const result = this.driver.disconnect();
227
- for (let callback of this.disconnectCallbacks) {
228
- if (callback) {
229
- await callback(this, result);
348
+ try {
349
+ const result = this.driver.disconnect();
350
+ for (let callback of this.disconnectCallbacks) {
351
+ if (callback) {
352
+ await callback(this, result);
353
+ }
230
354
  }
355
+ this.config.debugInfo('nymph', 'Driver disconnected.');
356
+ return await result;
357
+ } catch (e: any) {
358
+ this.config.debugError('nymph', `Failed to disconnect: ${e}`);
359
+ throw e;
231
360
  }
232
- return await result;
233
361
  }
234
362
 
235
363
  /**
@@ -256,7 +384,24 @@ export default class Nymph {
256
384
  * @returns A new instance of Nymph that should be used for the transaction.
257
385
  */
258
386
  public async startTransaction(name: string): Promise<Nymph> {
259
- return await this.driver.startTransaction(name);
387
+ try {
388
+ for (let callback of this.beforeStartTransactionCallbacks) {
389
+ if (callback) {
390
+ await callback(this, name);
391
+ }
392
+ }
393
+ this.config.debugLog('nymph', `Starting transaction "${name}".`);
394
+ const result = await this.driver.startTransaction(name);
395
+ for (let callback of this.afterStartTransactionCallbacks) {
396
+ if (callback) {
397
+ await callback(this, name, result);
398
+ }
399
+ }
400
+ return result;
401
+ } catch (e: any) {
402
+ this.config.debugError('nymph', `Failed to start transaction: ${e}`);
403
+ throw e;
404
+ }
260
405
  }
261
406
 
262
407
  /**
@@ -267,7 +412,24 @@ export default class Nymph {
267
412
  * @returns True on success, false on failure.
268
413
  */
269
414
  public async commit(name: string): Promise<boolean> {
270
- return await this.driver.commit(name);
415
+ try {
416
+ for (let callback of this.beforeCommitTransactionCallbacks) {
417
+ if (callback) {
418
+ await callback(this, name);
419
+ }
420
+ }
421
+ this.config.debugLog('nymph', `Committing transaction "${name}".`);
422
+ const result = await this.driver.commit(name);
423
+ for (let callback of this.afterCommitTransactionCallbacks) {
424
+ if (callback) {
425
+ await callback(this, name, result);
426
+ }
427
+ }
428
+ return result;
429
+ } catch (e: any) {
430
+ this.config.debugError('nymph', `Failed to commit transaction: ${e}`);
431
+ throw e;
432
+ }
271
433
  }
272
434
 
273
435
  /**
@@ -278,7 +440,24 @@ export default class Nymph {
278
440
  * @returns True on success, false on failure.
279
441
  */
280
442
  public async rollback(name: string): Promise<boolean> {
281
- return await this.driver.rollback(name);
443
+ try {
444
+ for (let callback of this.beforeRollbackTransactionCallbacks) {
445
+ if (callback) {
446
+ await callback(this, name);
447
+ }
448
+ }
449
+ this.config.debugLog('nymph', `Rolling back transaction "${name}".`);
450
+ const result = await this.driver.rollback(name);
451
+ for (let callback of this.afterRollbackTransactionCallbacks) {
452
+ if (callback) {
453
+ await callback(this, name, result);
454
+ }
455
+ }
456
+ return result;
457
+ } catch (e: any) {
458
+ this.config.debugError('nymph', `Failed to roll back transaction: ${e}`);
459
+ throw e;
460
+ }
282
461
  }
283
462
 
284
463
  /**
@@ -312,18 +491,33 @@ export default class Nymph {
312
491
  * @returns The UID's new value, or null on failure.
313
492
  */
314
493
  public async newUID(name: string): Promise<number | null> {
315
- for (let callback of this.beforeNewUIDCallbacks) {
316
- if (callback) {
317
- await callback(this, name);
494
+ try {
495
+ for (let callback of this.beforeNewUIDCallbacks) {
496
+ if (callback) {
497
+ await callback(this, name);
498
+ }
318
499
  }
319
- }
320
- const result = this.driver.newUID(name);
321
- for (let callback of this.afterNewUIDCallbacks) {
322
- if (callback) {
323
- await callback(this, result);
500
+ let result: Promise<number | null>;
501
+ try {
502
+ result = this.driver.newUID(name);
503
+ } catch (e: any) {
504
+ for (let callback of this.failedNewUIDCallbacks) {
505
+ if (callback) {
506
+ await callback(this, e);
507
+ }
508
+ }
509
+ throw e;
510
+ }
511
+ for (let callback of this.afterNewUIDCallbacks) {
512
+ if (callback) {
513
+ await callback(this, result);
514
+ }
324
515
  }
516
+ return await result;
517
+ } catch (e: any) {
518
+ this.config.debugError('nymph', `Failed to create UID "${name}": ${e}`);
519
+ throw e;
325
520
  }
326
- return await result;
327
521
  }
328
522
 
329
523
  /**
@@ -332,7 +526,12 @@ export default class Nymph {
332
526
  * @returns The UID's value, or null on failure and if it doesn't exist.
333
527
  */
334
528
  public async getUID(name: string): Promise<number | null> {
335
- return await this.driver.getUID(name);
529
+ try {
530
+ return await this.driver.getUID(name);
531
+ } catch (e: any) {
532
+ this.config.debugError('nymph', `Failed to get UID "${name}": ${e}`);
533
+ throw e;
534
+ }
336
535
  }
337
536
 
338
537
  /**
@@ -343,18 +542,33 @@ export default class Nymph {
343
542
  * @returns True on success, false on failure.
344
543
  */
345
544
  public async setUID(name: string, value: number): Promise<boolean> {
346
- for (let callback of this.beforeSetUIDCallbacks) {
347
- if (callback) {
348
- await callback(this, name, value);
545
+ try {
546
+ for (let callback of this.beforeSetUIDCallbacks) {
547
+ if (callback) {
548
+ await callback(this, name, value);
549
+ }
349
550
  }
350
- }
351
- const result = this.driver.setUID(name, value);
352
- for (let callback of this.afterSetUIDCallbacks) {
353
- if (callback) {
354
- await callback(this, result);
551
+ let result: Promise<boolean>;
552
+ try {
553
+ result = this.driver.setUID(name, value);
554
+ } catch (e: any) {
555
+ for (let callback of this.failedSetUIDCallbacks) {
556
+ if (callback) {
557
+ await callback(this, e);
558
+ }
559
+ }
560
+ throw e;
355
561
  }
562
+ for (let callback of this.afterSetUIDCallbacks) {
563
+ if (callback) {
564
+ await callback(this, result);
565
+ }
566
+ }
567
+ return await result;
568
+ } catch (e: any) {
569
+ this.config.debugError('nymph', `Failed to set UID "${name}": ${e}`);
570
+ throw e;
356
571
  }
357
- return await result;
358
572
  }
359
573
 
360
574
  /**
@@ -364,18 +578,33 @@ export default class Nymph {
364
578
  * @returns True on success, false on failure.
365
579
  */
366
580
  public async deleteUID(name: string): Promise<boolean> {
367
- for (let callback of this.beforeDeleteUIDCallbacks) {
368
- if (callback) {
369
- await callback(this, name);
581
+ try {
582
+ for (let callback of this.beforeDeleteUIDCallbacks) {
583
+ if (callback) {
584
+ await callback(this, name);
585
+ }
370
586
  }
371
- }
372
- const result = this.driver.deleteUID(name);
373
- for (let callback of this.afterDeleteUIDCallbacks) {
374
- if (callback) {
375
- await callback(this, result);
587
+ let result: Promise<boolean>;
588
+ try {
589
+ result = this.driver.deleteUID(name);
590
+ } catch (e: any) {
591
+ for (let callback of this.failedDeleteUIDCallbacks) {
592
+ if (callback) {
593
+ await callback(this, e);
594
+ }
595
+ }
596
+ throw e;
597
+ }
598
+ for (let callback of this.afterDeleteUIDCallbacks) {
599
+ if (callback) {
600
+ await callback(this, result);
601
+ }
376
602
  }
603
+ return await result;
604
+ } catch (e: any) {
605
+ this.config.debugError('nymph', `Failed to delete UID "${name}": ${e}`);
606
+ throw e;
377
607
  }
378
- return await result;
379
608
  }
380
609
 
381
610
  /**
@@ -386,18 +615,36 @@ export default class Nymph {
386
615
  * @returns True on success, false on failure.
387
616
  */
388
617
  public async renameUID(oldName: string, newName: string): Promise<boolean> {
389
- for (let callback of this.beforeRenameUIDCallbacks) {
390
- if (callback) {
391
- await callback(this, oldName, newName);
618
+ try {
619
+ for (let callback of this.beforeRenameUIDCallbacks) {
620
+ if (callback) {
621
+ await callback(this, oldName, newName);
622
+ }
392
623
  }
393
- }
394
- const result = this.driver.renameUID(oldName, newName);
395
- for (let callback of this.afterRenameUIDCallbacks) {
396
- if (callback) {
397
- await callback(this, result);
624
+ let result: Promise<boolean>;
625
+ try {
626
+ result = this.driver.renameUID(oldName, newName);
627
+ } catch (e: any) {
628
+ for (let callback of this.failedRenameUIDCallbacks) {
629
+ if (callback) {
630
+ await callback(this, e);
631
+ }
632
+ }
633
+ throw e;
634
+ }
635
+ for (let callback of this.afterRenameUIDCallbacks) {
636
+ if (callback) {
637
+ await callback(this, result);
638
+ }
398
639
  }
640
+ return await result;
641
+ } catch (e: any) {
642
+ this.config.debugError(
643
+ 'nymph',
644
+ `Failed to rename UID "${oldName}" to "${newName}": ${e}`,
645
+ );
646
+ throw e;
399
647
  }
400
- return await result;
401
648
  }
402
649
 
403
650
  /**
@@ -412,18 +659,33 @@ export default class Nymph {
412
659
  * @returns True on success, false on failure.
413
660
  */
414
661
  public async saveEntity(entity: EntityInterface): Promise<boolean> {
415
- for (let callback of this.beforeSaveEntityCallbacks) {
416
- if (callback) {
417
- await callback(this, entity);
662
+ try {
663
+ for (let callback of this.beforeSaveEntityCallbacks) {
664
+ if (callback) {
665
+ await callback(this, entity);
666
+ }
418
667
  }
419
- }
420
- const result = this.driver.saveEntity(entity);
421
- for (let callback of this.afterSaveEntityCallbacks) {
422
- if (callback) {
423
- await callback(this, result);
668
+ let result: Promise<boolean>;
669
+ try {
670
+ result = this.driver.saveEntity(entity);
671
+ } catch (e: any) {
672
+ for (let callback of this.failedSaveEntityCallbacks) {
673
+ if (callback) {
674
+ await callback(this, e);
675
+ }
676
+ }
677
+ throw e;
424
678
  }
679
+ for (let callback of this.afterSaveEntityCallbacks) {
680
+ if (callback) {
681
+ await callback(this, result);
682
+ }
683
+ }
684
+ return await result;
685
+ } catch (e: any) {
686
+ this.config.debugError('nymph', `Failed to save entity: ${e}`);
687
+ throw e;
425
688
  }
426
- return await result;
427
689
  }
428
690
 
429
691
  /**
@@ -436,8 +698,10 @@ export default class Nymph {
436
698
  * - offset - The offset from the oldest matching entity to start retrieving.
437
699
  * - reverse - If true, entities will be retrieved from newest to oldest.
438
700
  * Therefore, offset will be from the newest entity.
439
- * - sort - How to sort the entities. Accepts "cdate" or "mdate". Defaults to
440
- * "cdate".
701
+ * - sort - How to sort the entities. Accepts "cdate", "mdate", or the name of
702
+ * a top-level property. The method of sorting properties other than cdate
703
+ * and mdate is driver dependent. The only hard rule is that numbers should
704
+ * be sorted numerically (2 before 10). Defaults to "cdate".
441
705
  * - return - What to return. "entity", "guid", or "count". Defaults to
442
706
  * "entity".
443
707
  * - source - Will be 'client' if the query came from a REST call.
@@ -596,17 +860,22 @@ export default class Nymph {
596
860
  public async getEntities<T extends EntityConstructor = EntityConstructor>(
597
861
  options?: Options<T>,
598
862
  ...selectors: Selector[]
599
- ): Promise<ReturnType<T['factorySync']>[]>;
863
+ ): Promise<EntityInstanceType<T>[]>;
600
864
  public async getEntities<T extends EntityConstructor = EntityConstructor>(
601
865
  options: Options<T> = {},
602
866
  ...selectors: Selector[]
603
- ): Promise<ReturnType<T['factorySync']>[] | string[] | number> {
604
- for (let callback of this.beforeGetEntitiesCallbacks) {
605
- if (callback) {
606
- await callback(this, options, selectors);
867
+ ): Promise<EntityInstanceType<T>[] | string[] | number> {
868
+ try {
869
+ for (let callback of this.beforeGetEntitiesCallbacks) {
870
+ if (callback) {
871
+ await callback(this, options, selectors);
872
+ }
607
873
  }
874
+ return await this.driver.getEntities(options, ...selectors);
875
+ } catch (e: any) {
876
+ this.config.debugError('nymph', `Failed to get entities: ${e}`);
877
+ throw e;
608
878
  }
609
- return await this.driver.getEntities(options, ...selectors);
610
879
  }
611
880
 
612
881
  /**
@@ -633,40 +902,45 @@ export default class Nymph {
633
902
  public async getEntity<T extends EntityConstructor = EntityConstructor>(
634
903
  options: Options<T>,
635
904
  ...selectors: Selector[]
636
- ): Promise<ReturnType<T['factorySync']> | null>;
905
+ ): Promise<EntityInstanceType<T> | null>;
637
906
  public async getEntity<T extends EntityConstructor = EntityConstructor>(
638
907
  options: Options<T> & { return: 'guid' },
639
- guid: string
908
+ guid: string,
640
909
  ): Promise<string | null>;
641
910
  public async getEntity<T extends EntityConstructor = EntityConstructor>(
642
911
  options: Options<T>,
643
- guid: string
644
- ): Promise<ReturnType<T['factorySync']> | null>;
912
+ guid: string,
913
+ ): Promise<EntityInstanceType<T> | null>;
645
914
  public async getEntity<T extends EntityConstructor = EntityConstructor>(
646
915
  options: Options<T> = {},
647
916
  ...selectors: Selector[] | string[]
648
- ): Promise<ReturnType<T['factorySync']> | string | number | null> {
649
- // Set up options and selectors.
650
- if (typeof selectors[0] === 'string') {
651
- selectors = [{ type: '&', guid: selectors[0] }];
652
- }
653
- options.limit = 1;
654
- for (let callback of this.beforeGetEntityCallbacks) {
655
- if (callback) {
656
- await callback(this, options, selectors as Selector[]);
917
+ ): Promise<EntityInstanceType<T> | string | number | null> {
918
+ try {
919
+ // Set up options and selectors.
920
+ if (typeof selectors[0] === 'string') {
921
+ selectors = [{ type: '&', guid: selectors[0] }];
657
922
  }
923
+ options.limit = 1;
924
+ for (let callback of this.beforeGetEntityCallbacks) {
925
+ if (callback) {
926
+ await callback(this, options, selectors as Selector[]);
927
+ }
928
+ }
929
+ const entities = await this.driver.getEntities(
930
+ options,
931
+ ...(selectors as Selector[]),
932
+ );
933
+ if (options.return === 'count') {
934
+ return entities as unknown as number;
935
+ }
936
+ if (!entities || !entities.length) {
937
+ return null;
938
+ }
939
+ return entities[0];
940
+ } catch (e: any) {
941
+ this.config.debugError('nymph', `Failed to get entity: ${e}`);
942
+ throw e;
658
943
  }
659
- const entities = await this.driver.getEntities(
660
- options,
661
- ...(selectors as Selector[])
662
- );
663
- if (options.return === 'count') {
664
- return entities as unknown as number;
665
- }
666
- if (!entities || !entities.length) {
667
- return null;
668
- }
669
- return entities[0];
670
944
  }
671
945
 
672
946
  /**
@@ -676,18 +950,33 @@ export default class Nymph {
676
950
  * @returns True on success, false on failure.
677
951
  */
678
952
  public async deleteEntity(entity: EntityInterface): Promise<boolean> {
679
- for (let callback of this.beforeDeleteEntityCallbacks) {
680
- if (callback) {
681
- await callback(this, entity);
953
+ try {
954
+ for (let callback of this.beforeDeleteEntityCallbacks) {
955
+ if (callback) {
956
+ await callback(this, entity);
957
+ }
682
958
  }
683
- }
684
- const result = this.driver.deleteEntity(entity);
685
- for (let callback of this.afterDeleteEntityCallbacks) {
686
- if (callback) {
687
- await callback(this, result);
959
+ let result: Promise<boolean>;
960
+ try {
961
+ result = this.driver.deleteEntity(entity);
962
+ } catch (e: any) {
963
+ for (let callback of this.failedDeleteEntityCallbacks) {
964
+ if (callback) {
965
+ await callback(this, e);
966
+ }
967
+ }
968
+ throw e;
688
969
  }
970
+ for (let callback of this.afterDeleteEntityCallbacks) {
971
+ if (callback) {
972
+ await callback(this, result);
973
+ }
974
+ }
975
+ return await result;
976
+ } catch (e: any) {
977
+ this.config.debugError('nymph', `Failed to delete entity: ${e}`);
978
+ throw e;
689
979
  }
690
- return await result;
691
980
  }
692
981
 
693
982
  /**
@@ -699,20 +988,35 @@ export default class Nymph {
699
988
  */
700
989
  public async deleteEntityByID(
701
990
  guid: string,
702
- className?: string
991
+ className?: string,
703
992
  ): Promise<boolean> {
704
- for (let callback of this.beforeDeleteEntityByIDCallbacks) {
705
- if (callback) {
706
- await callback(this, guid, className);
993
+ try {
994
+ for (let callback of this.beforeDeleteEntityByIDCallbacks) {
995
+ if (callback) {
996
+ await callback(this, guid, className);
997
+ }
707
998
  }
708
- }
709
- const result = this.driver.deleteEntityByID(guid, className);
710
- for (let callback of this.afterDeleteEntityByIDCallbacks) {
711
- if (callback) {
712
- await callback(this, result);
999
+ let result: Promise<boolean>;
1000
+ try {
1001
+ result = this.driver.deleteEntityByID(guid, className);
1002
+ } catch (e: any) {
1003
+ for (let callback of this.failedDeleteEntityByIDCallbacks) {
1004
+ if (callback) {
1005
+ await callback(this, e);
1006
+ }
1007
+ }
1008
+ throw e;
1009
+ }
1010
+ for (let callback of this.afterDeleteEntityByIDCallbacks) {
1011
+ if (callback) {
1012
+ await callback(this, result);
1013
+ }
713
1014
  }
1015
+ return await result;
1016
+ } catch (e: any) {
1017
+ this.config.debugError('nymph', `Failed to delete entity by ID: ${e}`);
1018
+ throw e;
714
1019
  }
715
- return await result;
716
1020
  }
717
1021
 
718
1022
  /**
@@ -737,7 +1041,7 @@ export default class Nymph {
737
1041
  * # brackets ([]).
738
1042
  * # Properties are stored like this:
739
1043
  * # propname=JSON.stringify(value)
740
- * abilities=["system/all"]
1044
+ * abilities=["system/admin"]
741
1045
  * groups=[]
742
1046
  * inheritAbilities=false
743
1047
  * name="admin"
@@ -752,7 +1056,12 @@ export default class Nymph {
752
1056
  * @returns True on success, false on failure.
753
1057
  */
754
1058
  public async export(filename: string): Promise<boolean> {
755
- return await this.driver.export(filename);
1059
+ try {
1060
+ return await this.driver.export(filename);
1061
+ } catch (e: any) {
1062
+ this.config.debugError('nymph', `Failed to export: ${e}`);
1063
+ throw e;
1064
+ }
756
1065
  }
757
1066
 
758
1067
  /**
@@ -761,7 +1070,12 @@ export default class Nymph {
761
1070
  * @returns True on success, false on failure.
762
1071
  */
763
1072
  public async exportPrint(): Promise<boolean> {
764
- return await this.driver.exportPrint();
1073
+ try {
1074
+ return await this.driver.exportPrint();
1075
+ } catch (e: any) {
1076
+ this.config.debugError('nymph', `Failed to export: ${e}`);
1077
+ throw e;
1078
+ }
765
1079
  }
766
1080
 
767
1081
  /**
@@ -771,7 +1085,22 @@ export default class Nymph {
771
1085
  * @returns True on success, false on failure.
772
1086
  */
773
1087
  public async import(filename: string): Promise<boolean> {
774
- return await this.driver.import(filename);
1088
+ try {
1089
+ return await this.driver.import(filename);
1090
+ } catch (e: any) {
1091
+ this.config.debugError('nymph', `Failed to import: ${e}`);
1092
+ throw e;
1093
+ }
1094
+ }
1095
+
1096
+ /**
1097
+ * Detect whether the database needs to be migrated.
1098
+ *
1099
+ * If true, the database should be exported with an old version of Nymph, then
1100
+ * imported into a fresh database with this version.
1101
+ */
1102
+ public async needsMigration(): Promise<boolean> {
1103
+ return await this.driver.needsMigration();
775
1104
  }
776
1105
 
777
1106
  public on<T extends NymphEventType>(
@@ -790,31 +1119,57 @@ export default class Nymph {
790
1119
  ? NymphBeforeSaveEntityCallback
791
1120
  : T extends 'afterSaveEntity'
792
1121
  ? NymphAfterSaveEntityCallback
1122
+ : T extends 'failedSaveEntity'
1123
+ ? NymphFailedSaveEntityCallback
793
1124
  : T extends 'beforeDeleteEntity'
794
1125
  ? NymphBeforeDeleteEntityCallback
795
1126
  : T extends 'afterDeleteEntity'
796
1127
  ? NymphAfterDeleteEntityCallback
1128
+ : T extends 'failedDeleteEntity'
1129
+ ? NymphFailedDeleteEntityCallback
797
1130
  : T extends 'beforeDeleteEntityByID'
798
1131
  ? NymphBeforeDeleteEntityByIDCallback
799
1132
  : T extends 'afterDeleteEntityByID'
800
1133
  ? NymphAfterDeleteEntityByIDCallback
1134
+ : T extends 'failedDeleteEntityByID'
1135
+ ? NymphFailedDeleteEntityByIDCallback
801
1136
  : T extends 'beforeNewUID'
802
1137
  ? NymphBeforeNewUIDCallback
803
1138
  : T extends 'afterNewUID'
804
1139
  ? NymphAfterNewUIDCallback
1140
+ : T extends 'failedNewUID'
1141
+ ? NymphFailedNewUIDCallback
805
1142
  : T extends 'beforeSetUID'
806
1143
  ? NymphBeforeSetUIDCallback
807
1144
  : T extends 'afterSetUID'
808
1145
  ? NymphAfterSetUIDCallback
1146
+ : T extends 'failedSetUID'
1147
+ ? NymphFailedSetUIDCallback
809
1148
  : T extends 'beforeRenameUID'
810
1149
  ? NymphBeforeRenameUIDCallback
811
1150
  : T extends 'afterRenameUID'
812
1151
  ? NymphAfterRenameUIDCallback
1152
+ : T extends 'failedRenameUID'
1153
+ ? NymphFailedRenameUIDCallback
813
1154
  : T extends 'beforeDeleteUID'
814
1155
  ? NymphBeforeDeleteUIDCallback
815
1156
  : T extends 'afterDeleteUID'
816
1157
  ? NymphAfterDeleteUIDCallback
817
- : never
1158
+ : T extends 'failedDeleteUID'
1159
+ ? NymphFailedDeleteUIDCallback
1160
+ : T extends 'beforeStartTransaction'
1161
+ ? NymphBeforeStartTransactionCallback
1162
+ : T extends 'afterStartTransaction'
1163
+ ? NymphAfterStartTransactionCallback
1164
+ : T extends 'beforeCommitTransaction'
1165
+ ? NymphBeforeCommitTransactionCallback
1166
+ : T extends 'afterCommitTransaction'
1167
+ ? NymphAfterCommitTransactionCallback
1168
+ : T extends 'beforeRollbackTransaction'
1169
+ ? NymphBeforeRollbackTransactionCallback
1170
+ : T extends 'afterRollbackTransaction'
1171
+ ? NymphAfterRollbackTransactionCallback
1172
+ : never,
818
1173
  ) {
819
1174
  const prop = (event + 'Callbacks') as T extends 'connect'
820
1175
  ? 'connectCallbacks'
@@ -830,30 +1185,56 @@ export default class Nymph {
830
1185
  ? 'beforeSaveEntityCallbacks'
831
1186
  : T extends 'afterSaveEntity'
832
1187
  ? 'afterSaveEntityCallbacks'
1188
+ : T extends 'failedSaveEntity'
1189
+ ? 'failedSaveEntityCallbacks'
833
1190
  : T extends 'beforeDeleteEntity'
834
1191
  ? 'beforeDeleteEntityCallbacks'
835
1192
  : T extends 'afterDeleteEntity'
836
1193
  ? 'afterDeleteEntityCallbacks'
1194
+ : T extends 'failedDeleteEntity'
1195
+ ? 'failedDeleteEntityCallbacks'
837
1196
  : T extends 'beforeDeleteEntityByID'
838
1197
  ? 'beforeDeleteEntityByIDCallbacks'
839
1198
  : T extends 'afterDeleteEntityByID'
840
1199
  ? 'afterDeleteEntityByIDCallbacks'
1200
+ : T extends 'failedDeleteEntityByID'
1201
+ ? 'failedDeleteEntityByIDCallbacks'
841
1202
  : T extends 'beforeNewUID'
842
1203
  ? 'beforeNewUIDCallbacks'
843
1204
  : T extends 'afterNewUID'
844
1205
  ? 'afterNewUIDCallbacks'
1206
+ : T extends 'failedNewUID'
1207
+ ? 'failedNewUIDCallbacks'
845
1208
  : T extends 'beforeSetUID'
846
1209
  ? 'beforeSetUIDCallbacks'
847
1210
  : T extends 'afterSetUID'
848
1211
  ? 'afterSetUIDCallbacks'
1212
+ : T extends 'failedSetUID'
1213
+ ? 'failedSetUIDCallbacks'
849
1214
  : T extends 'beforeRenameUID'
850
1215
  ? 'beforeRenameUIDCallbacks'
851
1216
  : T extends 'afterRenameUID'
852
1217
  ? 'afterRenameUIDCallbacks'
1218
+ : T extends 'failedRenameUID'
1219
+ ? 'failedRenameUIDCallbacks'
853
1220
  : T extends 'beforeDeleteUID'
854
1221
  ? 'beforeDeleteUIDCallbacks'
855
1222
  : T extends 'afterDeleteUID'
856
1223
  ? 'afterDeleteUIDCallbacks'
1224
+ : T extends 'failedDeleteUID'
1225
+ ? 'failedDeleteUIDCallbacks'
1226
+ : T extends 'beforeStartTransaction'
1227
+ ? 'beforeStartTransactionCallbacks'
1228
+ : T extends 'afterStartTransaction'
1229
+ ? 'afterStartTransactionCallbacks'
1230
+ : T extends 'beforeCommitTransaction'
1231
+ ? 'beforeCommitTransactionCallbacks'
1232
+ : T extends 'afterCommitTransaction'
1233
+ ? 'afterCommitTransactionCallbacks'
1234
+ : T extends 'beforeRollbackTransaction'
1235
+ ? 'beforeRollbackTransactionCallbacks'
1236
+ : T extends 'afterRollbackTransaction'
1237
+ ? 'afterRollbackTransactionCallbacks'
857
1238
  : never;
858
1239
  if (!(prop in this)) {
859
1240
  throw new Error('Invalid event type.');
@@ -879,31 +1260,57 @@ export default class Nymph {
879
1260
  ? NymphBeforeSaveEntityCallback
880
1261
  : T extends 'afterSaveEntity'
881
1262
  ? NymphAfterSaveEntityCallback
1263
+ : T extends 'failedSaveEntity'
1264
+ ? NymphFailedSaveEntityCallback
882
1265
  : T extends 'beforeDeleteEntity'
883
1266
  ? NymphBeforeDeleteEntityCallback
884
1267
  : T extends 'afterDeleteEntity'
885
1268
  ? NymphAfterDeleteEntityCallback
1269
+ : T extends 'failedDeleteEntity'
1270
+ ? NymphFailedDeleteEntityCallback
886
1271
  : T extends 'beforeDeleteEntityByID'
887
1272
  ? NymphBeforeDeleteEntityByIDCallback
888
1273
  : T extends 'afterDeleteEntityByID'
889
1274
  ? NymphAfterDeleteEntityByIDCallback
1275
+ : T extends 'failedDeleteEntityByID'
1276
+ ? NymphFailedDeleteEntityByIDCallback
890
1277
  : T extends 'beforeNewUID'
891
1278
  ? NymphBeforeNewUIDCallback
892
1279
  : T extends 'afterNewUID'
893
1280
  ? NymphAfterNewUIDCallback
1281
+ : T extends 'failedNewUID'
1282
+ ? NymphFailedNewUIDCallback
894
1283
  : T extends 'beforeSetUID'
895
1284
  ? NymphBeforeSetUIDCallback
896
1285
  : T extends 'afterSetUID'
897
1286
  ? NymphAfterSetUIDCallback
1287
+ : T extends 'failedSetUID'
1288
+ ? NymphFailedSetUIDCallback
898
1289
  : T extends 'beforeRenameUID'
899
1290
  ? NymphBeforeRenameUIDCallback
900
1291
  : T extends 'afterRenameUID'
901
1292
  ? NymphAfterRenameUIDCallback
1293
+ : T extends 'failedRenameUID'
1294
+ ? NymphFailedRenameUIDCallback
902
1295
  : T extends 'beforeDeleteUID'
903
1296
  ? NymphBeforeDeleteUIDCallback
904
1297
  : T extends 'afterDeleteUID'
905
1298
  ? NymphAfterDeleteUIDCallback
906
- : never
1299
+ : T extends 'failedDeleteUID'
1300
+ ? NymphFailedDeleteUIDCallback
1301
+ : T extends 'beforeStartTransaction'
1302
+ ? NymphBeforeStartTransactionCallback
1303
+ : T extends 'afterStartTransaction'
1304
+ ? NymphAfterStartTransactionCallback
1305
+ : T extends 'beforeCommitTransaction'
1306
+ ? NymphBeforeCommitTransactionCallback
1307
+ : T extends 'afterCommitTransaction'
1308
+ ? NymphAfterCommitTransactionCallback
1309
+ : T extends 'beforeRollbackTransaction'
1310
+ ? NymphBeforeRollbackTransactionCallback
1311
+ : T extends 'afterRollbackTransaction'
1312
+ ? NymphAfterRollbackTransactionCallback
1313
+ : never,
907
1314
  ) {
908
1315
  const prop = (event + 'Callbacks') as T extends 'connect'
909
1316
  ? 'connectCallbacks'
@@ -919,30 +1326,56 @@ export default class Nymph {
919
1326
  ? 'beforeGetEntitiesCallbacks'
920
1327
  : T extends 'afterSaveEntity'
921
1328
  ? 'afterSaveEntityCallbacks'
1329
+ : T extends 'failedSaveEntity'
1330
+ ? 'failedSaveEntityCallbacks'
922
1331
  : T extends 'beforeDeleteEntity'
923
1332
  ? 'beforeDeleteEntityCallbacks'
924
1333
  : T extends 'afterDeleteEntity'
925
1334
  ? 'afterDeleteEntityCallbacks'
1335
+ : T extends 'failedDeleteEntity'
1336
+ ? 'failedDeleteEntityCallbacks'
926
1337
  : T extends 'beforeDeleteEntityByID'
927
1338
  ? 'beforeDeleteEntityByIDCallbacks'
928
1339
  : T extends 'afterDeleteEntityByID'
929
1340
  ? 'afterDeleteEntityByIDCallbacks'
1341
+ : T extends 'failedDeleteEntityByID'
1342
+ ? 'failedDeleteEntityByIDCallbacks'
930
1343
  : T extends 'beforeNewUID'
931
1344
  ? 'beforeNewUIDCallbacks'
932
1345
  : T extends 'afterNewUID'
933
1346
  ? 'afterNewUIDCallbacks'
1347
+ : T extends 'failedNewUID'
1348
+ ? 'failedNewUIDCallbacks'
934
1349
  : T extends 'beforeSetUID'
935
1350
  ? 'beforeSetUIDCallbacks'
936
1351
  : T extends 'afterSetUID'
937
1352
  ? 'afterSetUIDCallbacks'
1353
+ : T extends 'failedSetUID'
1354
+ ? 'failedSetUIDCallbacks'
938
1355
  : T extends 'beforeRenameUID'
939
1356
  ? 'beforeRenameUIDCallbacks'
940
1357
  : T extends 'afterRenameUID'
941
1358
  ? 'afterRenameUIDCallbacks'
1359
+ : T extends 'failedRenameUID'
1360
+ ? 'failedRenameUIDCallbacks'
942
1361
  : T extends 'beforeDeleteUID'
943
1362
  ? 'beforeDeleteUIDCallbacks'
944
1363
  : T extends 'afterDeleteUID'
945
1364
  ? 'afterDeleteUIDCallbacks'
1365
+ : T extends 'failedDeleteUID'
1366
+ ? 'failedDeleteUIDCallbacks'
1367
+ : T extends 'beforeStartTransaction'
1368
+ ? 'beforeStartTransactionCallbacks'
1369
+ : T extends 'afterStartTransaction'
1370
+ ? 'afterStartTransactionCallbacks'
1371
+ : T extends 'beforeCommitTransaction'
1372
+ ? 'beforeCommitTransactionCallbacks'
1373
+ : T extends 'afterCommitTransaction'
1374
+ ? 'afterCommitTransactionCallbacks'
1375
+ : T extends 'beforeRollbackTransaction'
1376
+ ? 'beforeRollbackTransactionCallbacks'
1377
+ : T extends 'afterRollbackTransaction'
1378
+ ? 'afterRollbackTransactionCallbacks'
946
1379
  : never;
947
1380
  if (!(prop in this)) {
948
1381
  return false;