@nymphjs/nymph 1.0.0-beta.12 → 1.0.0-beta.121

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 (103) hide show
  1. package/CHANGELOG.md +588 -0
  2. package/README.md +23 -21
  3. package/dist/Entity.d.ts +295 -12
  4. package/dist/Entity.js +560 -148
  5. package/dist/Entity.js.map +1 -1
  6. package/dist/Entity.test.js +52 -13
  7. package/dist/Entity.test.js.map +1 -1
  8. package/dist/Entity.types.d.ts +256 -23
  9. package/dist/Entity.types.js +1 -2
  10. package/dist/Nymph.d.ts +590 -13
  11. package/dist/Nymph.js +846 -180
  12. package/dist/Nymph.js.map +1 -1
  13. package/dist/Nymph.types.d.ts +131 -8
  14. package/dist/Nymph.types.js +47 -5
  15. package/dist/Nymph.types.js.map +1 -1
  16. package/dist/conf/defaults.d.ts +1 -1
  17. package/dist/conf/defaults.js +26 -3
  18. package/dist/conf/defaults.js.map +1 -1
  19. package/dist/conf/index.d.ts +50 -2
  20. package/dist/conf/index.js +2 -8
  21. package/dist/conf/index.js.map +1 -1
  22. package/dist/driver/NymphDriver.d.ts +205 -40
  23. package/dist/driver/NymphDriver.js +712 -238
  24. package/dist/driver/NymphDriver.js.map +1 -1
  25. package/dist/driver/index.d.ts +1 -2
  26. package/dist/driver/index.js +2 -23
  27. package/dist/driver/index.js.map +1 -1
  28. package/dist/driver/testArtifacts.d.ts +4 -4
  29. package/dist/driver/testArtifacts.js +1920 -1086
  30. package/dist/driver/testArtifacts.js.map +1 -1
  31. package/dist/errors/ClassNotAvailableError.js +1 -5
  32. package/dist/errors/ClassNotAvailableError.js.map +1 -1
  33. package/dist/errors/EntityConflictError.js +1 -5
  34. package/dist/errors/EntityConflictError.js.map +1 -1
  35. package/dist/errors/EntityInvalidDataError.d.ts +6 -0
  36. package/dist/errors/EntityInvalidDataError.js +8 -6
  37. package/dist/errors/EntityInvalidDataError.js.map +1 -1
  38. package/dist/errors/EntityIsSleepingReferenceError.js +1 -5
  39. package/dist/errors/EntityIsSleepingReferenceError.js.map +1 -1
  40. package/dist/errors/EntityUniqueConstraintError.d.ts +3 -0
  41. package/dist/errors/EntityUniqueConstraintError.js +7 -0
  42. package/dist/errors/EntityUniqueConstraintError.js.map +1 -0
  43. package/dist/errors/HttpError.d.ts +5 -0
  44. package/dist/errors/HttpError.js +11 -0
  45. package/dist/errors/HttpError.js.map +1 -0
  46. package/dist/errors/InvalidParametersError.js +1 -5
  47. package/dist/errors/InvalidParametersError.js.map +1 -1
  48. package/dist/errors/InvalidStateError.d.ts +3 -0
  49. package/dist/errors/InvalidStateError.js +7 -0
  50. package/dist/errors/InvalidStateError.js.map +1 -0
  51. package/dist/errors/NotConfiguredError.js +1 -5
  52. package/dist/errors/NotConfiguredError.js.map +1 -1
  53. package/dist/errors/QueryFailedError.d.ts +2 -1
  54. package/dist/errors/QueryFailedError.js +5 -6
  55. package/dist/errors/QueryFailedError.js.map +1 -1
  56. package/dist/errors/UnableToConnectError.js +1 -5
  57. package/dist/errors/UnableToConnectError.js.map +1 -1
  58. package/dist/errors/index.d.ts +11 -8
  59. package/dist/errors/index.js +11 -24
  60. package/dist/errors/index.js.map +1 -1
  61. package/dist/index.d.ts +11 -11
  62. package/dist/index.js +14 -34
  63. package/dist/index.js.map +1 -1
  64. package/dist/nymphJoiProps.d.ts +1 -1
  65. package/dist/nymphJoiProps.js +10 -15
  66. package/dist/nymphJoiProps.js.map +1 -1
  67. package/dist/testArtifacts.d.ts +19 -6
  68. package/dist/testArtifacts.js +32 -37
  69. package/dist/testArtifacts.js.map +1 -1
  70. package/dist/testMocks.d.ts +27 -12
  71. package/dist/testMocks.js +40 -29
  72. package/dist/testMocks.js.map +1 -1
  73. package/dist/utils.d.ts +4 -4
  74. package/dist/utils.js +40 -29
  75. package/dist/utils.js.map +1 -1
  76. package/jest.config.js +31 -5
  77. package/package.json +33 -23
  78. package/src/Entity.test.ts +50 -7
  79. package/src/Entity.ts +489 -150
  80. package/src/Entity.types.ts +72 -74
  81. package/src/Nymph.ts +991 -324
  82. package/src/Nymph.types.ts +201 -27
  83. package/src/conf/defaults.ts +34 -1
  84. package/src/conf/index.ts +50 -2
  85. package/src/driver/NymphDriver.ts +898 -413
  86. package/src/driver/index.ts +1 -3
  87. package/src/driver/testArtifacts.ts +2557 -1629
  88. package/src/errors/EntityUniqueConstraintError.ts +6 -0
  89. package/src/errors/HttpError.ts +12 -0
  90. package/src/errors/InvalidStateError.ts +6 -0
  91. package/src/errors/QueryFailedError.ts +3 -1
  92. package/src/errors/index.ts +14 -8
  93. package/src/index.ts +11 -11
  94. package/src/nymphJoiProps.ts +2 -2
  95. package/src/testArtifacts.ts +20 -20
  96. package/src/testMocks.ts +76 -35
  97. package/src/utils.ts +39 -18
  98. package/tsconfig.json +6 -3
  99. package/typedoc.json +4 -0
  100. package/dist/conf/d.d.ts +0 -6
  101. package/dist/conf/d.js +0 -3
  102. package/dist/conf/d.js.map +0 -1
  103. package/src/conf/d.ts +0 -24
package/src/Nymph.ts CHANGED
@@ -1,8 +1,18 @@
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';
5
- import { ClassNotAvailableError } from './errors';
1
+ import { Config, ConfigDefaults as defaults } from './conf/index.js';
2
+ import type { NymphDriver } from './driver/index.js';
3
+ import Entity, {
4
+ type EntityInstanceType,
5
+ type EntityObjectType,
6
+ } from './Entity.js';
7
+ import type {
8
+ EntityConstructor,
9
+ EntityInterface,
10
+ SerializedEntityData,
11
+ } from './Entity.types.js';
12
+ import {
13
+ ClassNotAvailableError,
14
+ InvalidParametersError,
15
+ } from './errors/index.js';
6
16
  import type {
7
17
  Selector,
8
18
  Options,
@@ -12,39 +22,58 @@ import type {
12
22
  NymphBeforeGetEntitiesCallback,
13
23
  NymphBeforeSaveEntityCallback,
14
24
  NymphAfterSaveEntityCallback,
25
+ NymphFailedSaveEntityCallback,
15
26
  NymphBeforeDeleteEntityCallback,
16
27
  NymphAfterDeleteEntityCallback,
28
+ NymphFailedDeleteEntityCallback,
17
29
  NymphBeforeDeleteEntityByIDCallback,
18
30
  NymphAfterDeleteEntityByIDCallback,
31
+ NymphFailedDeleteEntityByIDCallback,
19
32
  NymphBeforeNewUIDCallback,
20
33
  NymphAfterNewUIDCallback,
34
+ NymphFailedNewUIDCallback,
21
35
  NymphBeforeSetUIDCallback,
22
36
  NymphAfterSetUIDCallback,
37
+ NymphFailedSetUIDCallback,
23
38
  NymphBeforeRenameUIDCallback,
24
39
  NymphAfterRenameUIDCallback,
40
+ NymphFailedRenameUIDCallback,
25
41
  NymphBeforeDeleteUIDCallback,
26
42
  NymphAfterDeleteUIDCallback,
43
+ NymphFailedDeleteUIDCallback,
44
+ NymphBeforeStartTransactionCallback,
45
+ NymphAfterStartTransactionCallback,
46
+ NymphBeforeCommitTransactionCallback,
47
+ NymphAfterCommitTransactionCallback,
48
+ NymphBeforeRollbackTransactionCallback,
49
+ NymphAfterRollbackTransactionCallback,
27
50
  NymphEventType,
28
51
  NymphQueryCallback,
29
52
  FormattedSelector,
30
53
  TilmeldInterface,
31
- } from './Nymph.types';
54
+ } from './Nymph.types.js';
32
55
 
33
56
  /**
34
57
  * An object relational mapper for Node.js.
35
58
  *
36
59
  * Written by Hunter Perrin for SciActive.
37
60
  *
38
- * @author Hunter Perrin <hperrin@gmail.com>
61
+ * @author Hunter Perrin <hperrin@port87.com>
39
62
  * @copyright SciActive Inc
40
63
  * @see http://nymph.io/
41
64
  */
42
65
  export default class Nymph {
43
66
  /**
44
- * The PubSub config.
67
+ * The Nymph config.
45
68
  */
46
69
  public config: Config;
47
70
 
71
+ /**
72
+ * The Nymph instance that this one was cloned from, or null if it's not a
73
+ * clone.
74
+ */
75
+ public parent: Nymph | null;
76
+
48
77
  /**
49
78
  * The Nymph database driver.
50
79
  */
@@ -72,20 +101,40 @@ export default class Nymph {
72
101
  private beforeGetEntitiesCallbacks: NymphBeforeGetEntitiesCallback[] = [];
73
102
  private beforeSaveEntityCallbacks: NymphBeforeSaveEntityCallback[] = [];
74
103
  private afterSaveEntityCallbacks: NymphAfterSaveEntityCallback[] = [];
104
+ private failedSaveEntityCallbacks: NymphFailedSaveEntityCallback[] = [];
75
105
  private beforeDeleteEntityCallbacks: NymphBeforeDeleteEntityCallback[] = [];
76
106
  private afterDeleteEntityCallbacks: NymphAfterDeleteEntityCallback[] = [];
107
+ private failedDeleteEntityCallbacks: NymphFailedDeleteEntityCallback[] = [];
77
108
  private beforeDeleteEntityByIDCallbacks: NymphBeforeDeleteEntityByIDCallback[] =
78
109
  [];
79
110
  private afterDeleteEntityByIDCallbacks: NymphAfterDeleteEntityByIDCallback[] =
80
111
  [];
112
+ private failedDeleteEntityByIDCallbacks: NymphFailedDeleteEntityByIDCallback[] =
113
+ [];
81
114
  private beforeNewUIDCallbacks: NymphBeforeNewUIDCallback[] = [];
82
115
  private afterNewUIDCallbacks: NymphAfterNewUIDCallback[] = [];
116
+ private failedNewUIDCallbacks: NymphFailedNewUIDCallback[] = [];
83
117
  private beforeSetUIDCallbacks: NymphBeforeSetUIDCallback[] = [];
84
118
  private afterSetUIDCallbacks: NymphAfterSetUIDCallback[] = [];
119
+ private failedSetUIDCallbacks: NymphFailedSetUIDCallback[] = [];
85
120
  private beforeRenameUIDCallbacks: NymphBeforeRenameUIDCallback[] = [];
86
121
  private afterRenameUIDCallbacks: NymphAfterRenameUIDCallback[] = [];
122
+ private failedRenameUIDCallbacks: NymphFailedRenameUIDCallback[] = [];
87
123
  private beforeDeleteUIDCallbacks: NymphBeforeDeleteUIDCallback[] = [];
88
124
  private afterDeleteUIDCallbacks: NymphAfterDeleteUIDCallback[] = [];
125
+ private failedDeleteUIDCallbacks: NymphFailedDeleteUIDCallback[] = [];
126
+ private beforeStartTransactionCallbacks: NymphBeforeStartTransactionCallback[] =
127
+ [];
128
+ private afterStartTransactionCallbacks: NymphAfterStartTransactionCallback[] =
129
+ [];
130
+ private beforeCommitTransactionCallbacks: NymphBeforeCommitTransactionCallback[] =
131
+ [];
132
+ private afterCommitTransactionCallbacks: NymphAfterCommitTransactionCallback[] =
133
+ [];
134
+ private beforeRollbackTransactionCallbacks: NymphBeforeRollbackTransactionCallback[] =
135
+ [];
136
+ private afterRollbackTransactionCallbacks: NymphAfterRollbackTransactionCallback[] =
137
+ [];
89
138
 
90
139
  /**
91
140
  * Initialize Nymph.
@@ -93,24 +142,90 @@ export default class Nymph {
93
142
  * @param config The Nymph configuration.
94
143
  * @param driver The Nymph database driver.
95
144
  * @param tilmeld The Tilmeld user/group manager instance, if you want to use it.
145
+ * @param parent Used internally by Nymph. Don't set this.
96
146
  */
97
147
  public constructor(
98
148
  config: Partial<Config>,
99
149
  driver: NymphDriver,
100
- tilmeld?: TilmeldInterface
150
+ tilmeld?: TilmeldInterface,
151
+ parent?: Nymph,
101
152
  ) {
102
153
  this.config = { ...defaults, ...config };
103
154
  this.driver = driver;
155
+ this.parent = parent ?? null;
104
156
 
105
157
  this.Entity = this.addEntityClass(Entity);
106
158
 
107
- if (typeof tilmeld !== 'undefined') {
159
+ if (parent) {
160
+ for (const name in parent.entityClasses) {
161
+ if (
162
+ name === 'Entity' ||
163
+ (parent.entityClasses[name] as any).skipOnClone
164
+ ) {
165
+ continue;
166
+ }
167
+ this.addEntityClass(parent.entityClasses[name]);
168
+ }
169
+
170
+ const events = [
171
+ 'connect',
172
+ 'disconnect',
173
+ 'query',
174
+ 'beforeGetEntity',
175
+ 'beforeGetEntities',
176
+ 'beforeSaveEntity',
177
+ 'afterSaveEntity',
178
+ 'failedSaveEntity',
179
+ 'beforeDeleteEntity',
180
+ 'afterDeleteEntity',
181
+ 'failedDeleteEntity',
182
+ 'beforeDeleteEntityByID',
183
+ 'afterDeleteEntityByID',
184
+ 'failedDeleteEntityByID',
185
+ 'beforeNewUID',
186
+ 'afterNewUID',
187
+ 'failedNewUID',
188
+ 'beforeSetUID',
189
+ 'afterSetUID',
190
+ 'failedSetUID',
191
+ 'beforeRenameUID',
192
+ 'afterRenameUID',
193
+ 'failedRenameUID',
194
+ 'beforeDeleteUID',
195
+ 'afterDeleteUID',
196
+ 'failedDeleteUID',
197
+ 'beforeStartTransaction',
198
+ 'afterStartTransaction',
199
+ 'beforeCommitTransaction',
200
+ 'afterCommitTransaction',
201
+ 'beforeRollbackTransaction',
202
+ 'afterRollbackTransaction',
203
+ ];
204
+
205
+ for (let event of events) {
206
+ const prop = event + 'Callbacks';
207
+ // @ts-ignore: The callback should be the right type here.
208
+ const callbacks = parent[prop];
209
+ for (let callback of callbacks) {
210
+ if (callback.skipOnClone) {
211
+ continue;
212
+ }
213
+ this.on(event as NymphEventType, callback);
214
+ }
215
+ }
216
+ }
217
+
218
+ this.config.debugInfo('nymph', 'Nymph loaded.');
219
+
220
+ if (tilmeld != null) {
108
221
  this.tilmeld = tilmeld;
109
222
  }
110
223
 
111
224
  this.driver.init(this);
225
+ this.config.debugInfo('nymph', 'Nymph driver loaded.');
112
226
  if (this.tilmeld) {
113
227
  this.tilmeld.init(this);
228
+ this.config.debugInfo('nymph', 'Tilmeld loaded.');
114
229
  }
115
230
  }
116
231
 
@@ -124,82 +239,93 @@ export default class Nymph {
124
239
  * Because this creates a subclass, don't use the class
125
240
  * returned from `getEntityClass` to check with `instanceof`.
126
241
  */
127
- public addEntityClass<T extends EntityConstructor>(entityClass: T): T {
242
+ public addEntityClass<T extends EntityConstructor>(EntityClass: T): T {
243
+ if (EntityClass.class in this.entityClasses) {
244
+ this.config.debugLog(
245
+ 'nymph',
246
+ `Adding duplicate class "${EntityClass.class}". This may be a mistake.`,
247
+ );
248
+ }
249
+
250
+ // Check that it doesn't have the same etype as any other class.
251
+ for (const ExistingClass of Object.values(this.entityClasses)) {
252
+ if (
253
+ EntityClass.ETYPE === ExistingClass.ETYPE &&
254
+ EntityClass.class !== EntityClass.class
255
+ ) {
256
+ this.config.debugLog(
257
+ 'nymph',
258
+ `Adding class "${EntityClass.class}" with same etype as existing class "${ExistingClass.class}". This may be a mistake.`,
259
+ );
260
+ }
261
+ }
262
+
128
263
  const nymph = this;
129
- class NymphEntity extends entityClass {
264
+ this.entityClasses[EntityClass.class] = class extends EntityClass {
130
265
  static nymph: Nymph = nymph;
131
266
 
132
267
  constructor(...args: any[]) {
133
268
  super(...args);
134
269
  }
135
- }
136
- this.entityClasses[entityClass.class] = NymphEntity;
137
- return NymphEntity;
270
+ };
271
+ return this.entityClasses[EntityClass.class] as T;
138
272
  }
139
273
 
140
274
  /**
141
275
  * Get the class that uses the specified class name.
142
276
  */
143
- public getEntityClass(className: string): EntityConstructor {
144
- if (className in this.entityClasses) {
145
- return this.entityClasses[className];
277
+ public getEntityClass(className: string): EntityConstructor;
278
+ public getEntityClass<T extends EntityConstructor>(className: T): T;
279
+ public getEntityClass<T extends EntityConstructor = EntityConstructor>(
280
+ className: T | string,
281
+ ): T | EntityConstructor {
282
+ let key: string | null = null;
283
+ if (typeof className === 'string') {
284
+ key = className;
285
+ } else {
286
+ key = className.class;
146
287
  }
147
- throw new ClassNotAvailableError('Tried to use class: ' + className);
288
+ if (key in this.entityClasses) {
289
+ return this.entityClasses[key];
290
+ }
291
+ this.config.debugError('nymph', `Tried to use missing class "${key}".`);
292
+ throw new ClassNotAvailableError(`Tried to use missing class "${key}".`);
148
293
  }
149
294
 
150
295
  /**
151
- * Get a clone of this instance with duplicate classes and event listeners.
296
+ * Get the class that uses the specified etype.
297
+ *
298
+ * Note that it is fine, though unusual, for two classes to use the same
299
+ * etype. However, this can lead to very hard to diagnose bugs, so is
300
+ * generally discouraged.
301
+ */
302
+ public getEntityClassByEtype(etype: string): EntityConstructor {
303
+ for (const EntityClass of Object.values(this.entityClasses)) {
304
+ if (EntityClass.ETYPE === etype) {
305
+ return EntityClass;
306
+ }
307
+ }
308
+ this.config.debugError(
309
+ 'nymph',
310
+ `Tried to use missing class by etype "${etype}".`,
311
+ );
312
+ throw new ClassNotAvailableError(
313
+ `Tried to use missing class by etype "${etype}".`,
314
+ );
315
+ }
316
+
317
+ /**
318
+ * Get a clone of this instance with cloned classes and event listeners.
152
319
  *
153
320
  * @returns A clone of this instance.
154
321
  */
155
322
  public clone() {
156
- const nymph = new Nymph(
323
+ return new Nymph(
157
324
  this.config,
158
325
  this.driver.clone(),
159
- this.tilmeld?.clone()
326
+ this.tilmeld?.clone(),
327
+ this,
160
328
  );
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
329
  }
204
330
 
205
331
  /**
@@ -208,13 +334,19 @@ export default class Nymph {
208
334
  * @returns Whether the instance is connected to the database.
209
335
  */
210
336
  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);
337
+ try {
338
+ const result = this.driver.connect();
339
+ for (let callback of this.connectCallbacks) {
340
+ if (callback) {
341
+ await callback(this, result);
342
+ }
215
343
  }
344
+ this.config.debugInfo('nymph', 'Driver connected.');
345
+ return await result;
346
+ } catch (e: any) {
347
+ this.config.debugError('nymph', `Failed to connect: ${e}`);
348
+ throw e;
216
349
  }
217
- return await result;
218
350
  }
219
351
 
220
352
  /**
@@ -223,13 +355,19 @@ export default class Nymph {
223
355
  * @returns Whether the instance is connected to the database.
224
356
  */
225
357
  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);
358
+ try {
359
+ const result = this.driver.disconnect();
360
+ for (let callback of this.disconnectCallbacks) {
361
+ if (callback) {
362
+ await callback(this, result);
363
+ }
230
364
  }
365
+ this.config.debugInfo('nymph', 'Driver disconnected.');
366
+ return await result;
367
+ } catch (e: any) {
368
+ this.config.debugError('nymph', `Failed to disconnect: ${e}`);
369
+ throw e;
231
370
  }
232
- return await result;
233
371
  }
234
372
 
235
373
  /**
@@ -256,7 +394,24 @@ export default class Nymph {
256
394
  * @returns A new instance of Nymph that should be used for the transaction.
257
395
  */
258
396
  public async startTransaction(name: string): Promise<Nymph> {
259
- return await this.driver.startTransaction(name);
397
+ try {
398
+ for (let callback of this.beforeStartTransactionCallbacks) {
399
+ if (callback) {
400
+ await callback(this, name);
401
+ }
402
+ }
403
+ this.config.debugLog('nymph', `Starting transaction "${name}".`);
404
+ const result = await this.driver.startTransaction(name);
405
+ for (let callback of this.afterStartTransactionCallbacks) {
406
+ if (callback) {
407
+ await callback(this, name, result);
408
+ }
409
+ }
410
+ return result;
411
+ } catch (e: any) {
412
+ this.config.debugError('nymph', `Failed to start transaction: ${e}`);
413
+ throw e;
414
+ }
260
415
  }
261
416
 
262
417
  /**
@@ -267,7 +422,24 @@ export default class Nymph {
267
422
  * @returns True on success, false on failure.
268
423
  */
269
424
  public async commit(name: string): Promise<boolean> {
270
- return await this.driver.commit(name);
425
+ try {
426
+ for (let callback of this.beforeCommitTransactionCallbacks) {
427
+ if (callback) {
428
+ await callback(this, name);
429
+ }
430
+ }
431
+ this.config.debugLog('nymph', `Committing transaction "${name}".`);
432
+ const result = await this.driver.commit(name);
433
+ for (let callback of this.afterCommitTransactionCallbacks) {
434
+ if (callback) {
435
+ await callback(this, name, result);
436
+ }
437
+ }
438
+ return result;
439
+ } catch (e: any) {
440
+ this.config.debugError('nymph', `Failed to commit transaction: ${e}`);
441
+ throw e;
442
+ }
271
443
  }
272
444
 
273
445
  /**
@@ -278,7 +450,24 @@ export default class Nymph {
278
450
  * @returns True on success, false on failure.
279
451
  */
280
452
  public async rollback(name: string): Promise<boolean> {
281
- return await this.driver.rollback(name);
453
+ try {
454
+ for (let callback of this.beforeRollbackTransactionCallbacks) {
455
+ if (callback) {
456
+ await callback(this, name);
457
+ }
458
+ }
459
+ this.config.debugLog('nymph', `Rolling back transaction "${name}".`);
460
+ const result = await this.driver.rollback(name);
461
+ for (let callback of this.afterRollbackTransactionCallbacks) {
462
+ if (callback) {
463
+ await callback(this, name, result);
464
+ }
465
+ }
466
+ return result;
467
+ } catch (e: any) {
468
+ this.config.debugError('nymph', `Failed to roll back transaction: ${e}`);
469
+ throw e;
470
+ }
282
471
  }
283
472
 
284
473
  /**
@@ -312,18 +501,33 @@ export default class Nymph {
312
501
  * @returns The UID's new value, or null on failure.
313
502
  */
314
503
  public async newUID(name: string): Promise<number | null> {
315
- for (let callback of this.beforeNewUIDCallbacks) {
316
- if (callback) {
317
- await callback(this, name);
504
+ try {
505
+ for (let callback of this.beforeNewUIDCallbacks) {
506
+ if (callback) {
507
+ await callback(this, name);
508
+ }
318
509
  }
319
- }
320
- const result = this.driver.newUID(name);
321
- for (let callback of this.afterNewUIDCallbacks) {
322
- if (callback) {
323
- await callback(this, result);
510
+ let result: Promise<number | null>;
511
+ try {
512
+ result = this.driver.newUID(name);
513
+ } catch (e: any) {
514
+ for (let callback of this.failedNewUIDCallbacks) {
515
+ if (callback) {
516
+ await callback(this, e);
517
+ }
518
+ }
519
+ throw e;
324
520
  }
521
+ for (let callback of this.afterNewUIDCallbacks) {
522
+ if (callback) {
523
+ await callback(this, result);
524
+ }
525
+ }
526
+ return await result;
527
+ } catch (e: any) {
528
+ this.config.debugError('nymph', `Failed to create UID "${name}": ${e}`);
529
+ throw e;
325
530
  }
326
- return await result;
327
531
  }
328
532
 
329
533
  /**
@@ -332,7 +536,12 @@ export default class Nymph {
332
536
  * @returns The UID's value, or null on failure and if it doesn't exist.
333
537
  */
334
538
  public async getUID(name: string): Promise<number | null> {
335
- return await this.driver.getUID(name);
539
+ try {
540
+ return await this.driver.getUID(name);
541
+ } catch (e: any) {
542
+ this.config.debugError('nymph', `Failed to get UID "${name}": ${e}`);
543
+ throw e;
544
+ }
336
545
  }
337
546
 
338
547
  /**
@@ -343,18 +552,33 @@ export default class Nymph {
343
552
  * @returns True on success, false on failure.
344
553
  */
345
554
  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);
555
+ try {
556
+ for (let callback of this.beforeSetUIDCallbacks) {
557
+ if (callback) {
558
+ await callback(this, name, value);
559
+ }
349
560
  }
350
- }
351
- const result = this.driver.setUID(name, value);
352
- for (let callback of this.afterSetUIDCallbacks) {
353
- if (callback) {
354
- await callback(this, result);
561
+ let result: Promise<boolean>;
562
+ try {
563
+ result = this.driver.setUID(name, value);
564
+ } catch (e: any) {
565
+ for (let callback of this.failedSetUIDCallbacks) {
566
+ if (callback) {
567
+ await callback(this, e);
568
+ }
569
+ }
570
+ throw e;
571
+ }
572
+ for (let callback of this.afterSetUIDCallbacks) {
573
+ if (callback) {
574
+ await callback(this, result);
575
+ }
355
576
  }
577
+ return await result;
578
+ } catch (e: any) {
579
+ this.config.debugError('nymph', `Failed to set UID "${name}": ${e}`);
580
+ throw e;
356
581
  }
357
- return await result;
358
582
  }
359
583
 
360
584
  /**
@@ -364,18 +588,33 @@ export default class Nymph {
364
588
  * @returns True on success, false on failure.
365
589
  */
366
590
  public async deleteUID(name: string): Promise<boolean> {
367
- for (let callback of this.beforeDeleteUIDCallbacks) {
368
- if (callback) {
369
- await callback(this, name);
591
+ try {
592
+ for (let callback of this.beforeDeleteUIDCallbacks) {
593
+ if (callback) {
594
+ await callback(this, name);
595
+ }
370
596
  }
371
- }
372
- const result = this.driver.deleteUID(name);
373
- for (let callback of this.afterDeleteUIDCallbacks) {
374
- if (callback) {
375
- await callback(this, result);
597
+ let result: Promise<boolean>;
598
+ try {
599
+ result = this.driver.deleteUID(name);
600
+ } catch (e: any) {
601
+ for (let callback of this.failedDeleteUIDCallbacks) {
602
+ if (callback) {
603
+ await callback(this, e);
604
+ }
605
+ }
606
+ throw e;
376
607
  }
608
+ for (let callback of this.afterDeleteUIDCallbacks) {
609
+ if (callback) {
610
+ await callback(this, result);
611
+ }
612
+ }
613
+ return await result;
614
+ } catch (e: any) {
615
+ this.config.debugError('nymph', `Failed to delete UID "${name}": ${e}`);
616
+ throw e;
377
617
  }
378
- return await result;
379
618
  }
380
619
 
381
620
  /**
@@ -386,18 +625,36 @@ export default class Nymph {
386
625
  * @returns True on success, false on failure.
387
626
  */
388
627
  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);
628
+ try {
629
+ for (let callback of this.beforeRenameUIDCallbacks) {
630
+ if (callback) {
631
+ await callback(this, oldName, newName);
632
+ }
392
633
  }
393
- }
394
- const result = this.driver.renameUID(oldName, newName);
395
- for (let callback of this.afterRenameUIDCallbacks) {
396
- if (callback) {
397
- await callback(this, result);
634
+ let result: Promise<boolean>;
635
+ try {
636
+ result = this.driver.renameUID(oldName, newName);
637
+ } catch (e: any) {
638
+ for (let callback of this.failedRenameUIDCallbacks) {
639
+ if (callback) {
640
+ await callback(this, e);
641
+ }
642
+ }
643
+ throw e;
398
644
  }
645
+ for (let callback of this.afterRenameUIDCallbacks) {
646
+ if (callback) {
647
+ await callback(this, result);
648
+ }
649
+ }
650
+ return await result;
651
+ } catch (e: any) {
652
+ this.config.debugError(
653
+ 'nymph',
654
+ `Failed to rename UID "${oldName}" to "${newName}": ${e}`,
655
+ );
656
+ throw e;
399
657
  }
400
- return await result;
401
658
  }
402
659
 
403
660
  /**
@@ -412,18 +669,33 @@ export default class Nymph {
412
669
  * @returns True on success, false on failure.
413
670
  */
414
671
  public async saveEntity(entity: EntityInterface): Promise<boolean> {
415
- for (let callback of this.beforeSaveEntityCallbacks) {
416
- if (callback) {
417
- await callback(this, entity);
672
+ try {
673
+ for (let callback of this.beforeSaveEntityCallbacks) {
674
+ if (callback) {
675
+ await callback(this, entity);
676
+ }
418
677
  }
419
- }
420
- const result = this.driver.saveEntity(entity);
421
- for (let callback of this.afterSaveEntityCallbacks) {
422
- if (callback) {
423
- await callback(this, result);
678
+ let result: Promise<boolean>;
679
+ try {
680
+ result = this.driver.saveEntity(entity);
681
+ } catch (e: any) {
682
+ for (let callback of this.failedSaveEntityCallbacks) {
683
+ if (callback) {
684
+ await callback(this, e);
685
+ }
686
+ }
687
+ throw e;
688
+ }
689
+ for (let callback of this.afterSaveEntityCallbacks) {
690
+ if (callback) {
691
+ await callback(this, result);
692
+ }
424
693
  }
694
+ return await result;
695
+ } catch (e: any) {
696
+ this.config.debugError('nymph', `Failed to save entity: ${e}`);
697
+ throw e;
425
698
  }
426
- return await result;
427
699
  }
428
700
 
429
701
  /**
@@ -436,10 +708,13 @@ export default class Nymph {
436
708
  * - offset - The offset from the oldest matching entity to start retrieving.
437
709
  * - reverse - If true, entities will be retrieved from newest to oldest.
438
710
  * Therefore, offset will be from the newest entity.
439
- * - sort - How to sort the entities. Accepts "cdate" or "mdate". Defaults to
440
- * "cdate".
441
- * - return - What to return. "entity", "guid", or "count". Defaults to
442
- * "entity".
711
+ * - sort - How to sort the entities. Accepts "cdate", "mdate", the name of a
712
+ * top-level property, or null for no particular order. The method of
713
+ * sorting properties other than cdate and mdate is driver dependent. The
714
+ * only hard rule is that numbers should be sorted numerically (2 before
715
+ * 10). Defaults to "cdate".
716
+ * - return - What to return. "entity", "object", "guid", or "count". Defaults
717
+ * to "entity".
443
718
  * - source - Will be 'client' if the query came from a REST call.
444
719
  * - skipCache - If true, Nymph will skip the cache and retrieve the entity
445
720
  * from the DB.
@@ -472,6 +747,11 @@ export default class Nymph {
472
747
  * - contain - An array with a name, then value. True if the named property
473
748
  * contains the value (its JSON string is found within the property's JSON
474
749
  * string).
750
+ * - search - An array with a name, then search query. True if the named
751
+ * property matches the query. Use single quotes to find sequential terms.
752
+ * Use double quotes to find exact terms. Use "or" as the or operator. Use
753
+ * "-" before a term as the negation operator. Stop words and punctuation
754
+ * are stripped. Case insensitive.
475
755
  * - match - An array with a name, then regular expression. True if the
476
756
  * named property matches. Uses POSIX RegExp. Case sensitive. Must *not* be
477
757
  * surrounded by any delimiters.
@@ -593,20 +873,37 @@ export default class Nymph {
593
873
  options: Options<T> & { return: 'guid' },
594
874
  ...selectors: Selector[]
595
875
  ): Promise<string[]>;
876
+ public async getEntities<T extends EntityConstructor = EntityConstructor>(
877
+ options: Options<T> & { return: 'object' },
878
+ ...selectors: Selector[]
879
+ ): Promise<EntityObjectType<T>[]>;
596
880
  public async getEntities<T extends EntityConstructor = EntityConstructor>(
597
881
  options?: Options<T>,
598
882
  ...selectors: Selector[]
599
- ): Promise<ReturnType<T['factorySync']>[]>;
883
+ ): Promise<EntityInstanceType<T>[]>;
600
884
  public async getEntities<T extends EntityConstructor = EntityConstructor>(
601
885
  options: Options<T> = {},
602
886
  ...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);
887
+ ): Promise<
888
+ EntityInstanceType<T>[] | EntityObjectType<T>[] | string[] | number
889
+ > {
890
+ if (options.source === 'client' && options.return === 'object') {
891
+ throw new InvalidParametersError(
892
+ 'Object return type not allowed from client.',
893
+ );
894
+ }
895
+
896
+ try {
897
+ for (let callback of this.beforeGetEntitiesCallbacks) {
898
+ if (callback) {
899
+ await callback(this, options, selectors);
900
+ }
607
901
  }
902
+ return await this.driver.getEntities(options, ...selectors);
903
+ } catch (e: any) {
904
+ this.config.debugError('nymph', `Failed to get entities: ${e}`);
905
+ throw e;
608
906
  }
609
- return await this.driver.getEntities(options, ...selectors);
610
907
  }
611
908
 
612
909
  /**
@@ -630,43 +927,64 @@ export default class Nymph {
630
927
  options: Options<T> & { return: 'guid' },
631
928
  ...selectors: Selector[]
632
929
  ): Promise<string | null>;
930
+ public async getEntity<T extends EntityConstructor = EntityConstructor>(
931
+ options: Options<T> & { return: 'object' },
932
+ ...selectors: Selector[]
933
+ ): Promise<EntityObjectType<T> | null>;
633
934
  public async getEntity<T extends EntityConstructor = EntityConstructor>(
634
935
  options: Options<T>,
635
936
  ...selectors: Selector[]
636
- ): Promise<ReturnType<T['factorySync']> | null>;
937
+ ): Promise<EntityInstanceType<T> | null>;
637
938
  public async getEntity<T extends EntityConstructor = EntityConstructor>(
638
939
  options: Options<T> & { return: 'guid' },
639
- guid: string
940
+ guid: string,
640
941
  ): Promise<string | null>;
942
+ public async getEntity<T extends EntityConstructor = EntityConstructor>(
943
+ options: Options<T> & { return: 'object' },
944
+ guid: string,
945
+ ): Promise<EntityObjectType<T> | null>;
641
946
  public async getEntity<T extends EntityConstructor = EntityConstructor>(
642
947
  options: Options<T>,
643
- guid: string
644
- ): Promise<ReturnType<T['factorySync']> | null>;
948
+ guid: string,
949
+ ): Promise<EntityInstanceType<T> | null>;
645
950
  public async getEntity<T extends EntityConstructor = EntityConstructor>(
646
951
  options: Options<T> = {},
647
952
  ...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] }];
953
+ ): Promise<
954
+ EntityInstanceType<T> | EntityObjectType<T> | string | number | null
955
+ > {
956
+ if (options.source === 'client' && options.return === 'object') {
957
+ throw new InvalidParametersError(
958
+ 'Object return type not allowed from client.',
959
+ );
652
960
  }
653
- options.limit = 1;
654
- for (let callback of this.beforeGetEntityCallbacks) {
655
- if (callback) {
656
- await callback(this, options, selectors as Selector[]);
961
+
962
+ try {
963
+ // Set up options and selectors.
964
+ if (typeof selectors[0] === 'string') {
965
+ selectors = [{ type: '&', guid: selectors[0] }];
657
966
  }
967
+ options.limit = 1;
968
+ for (let callback of this.beforeGetEntityCallbacks) {
969
+ if (callback) {
970
+ await callback(this, options, selectors as Selector[]);
971
+ }
972
+ }
973
+ const entities = await this.driver.getEntities(
974
+ options,
975
+ ...(selectors as Selector[]),
976
+ );
977
+ if (options.return === 'count') {
978
+ return entities as unknown as number;
979
+ }
980
+ if (!entities || !entities.length) {
981
+ return null;
982
+ }
983
+ return entities[0];
984
+ } catch (e: any) {
985
+ this.config.debugError('nymph', `Failed to get entity: ${e}`);
986
+ throw e;
658
987
  }
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
988
  }
671
989
 
672
990
  /**
@@ -676,18 +994,33 @@ export default class Nymph {
676
994
  * @returns True on success, false on failure.
677
995
  */
678
996
  public async deleteEntity(entity: EntityInterface): Promise<boolean> {
679
- for (let callback of this.beforeDeleteEntityCallbacks) {
680
- if (callback) {
681
- await callback(this, entity);
997
+ try {
998
+ for (let callback of this.beforeDeleteEntityCallbacks) {
999
+ if (callback) {
1000
+ await callback(this, entity);
1001
+ }
682
1002
  }
683
- }
684
- const result = this.driver.deleteEntity(entity);
685
- for (let callback of this.afterDeleteEntityCallbacks) {
686
- if (callback) {
687
- await callback(this, result);
1003
+ let result: Promise<boolean>;
1004
+ try {
1005
+ result = this.driver.deleteEntity(entity);
1006
+ } catch (e: any) {
1007
+ for (let callback of this.failedDeleteEntityCallbacks) {
1008
+ if (callback) {
1009
+ await callback(this, e);
1010
+ }
1011
+ }
1012
+ throw e;
1013
+ }
1014
+ for (let callback of this.afterDeleteEntityCallbacks) {
1015
+ if (callback) {
1016
+ await callback(this, result);
1017
+ }
688
1018
  }
1019
+ return await result;
1020
+ } catch (e: any) {
1021
+ this.config.debugError('nymph', `Failed to delete entity: ${e}`);
1022
+ throw e;
689
1023
  }
690
- return await result;
691
1024
  }
692
1025
 
693
1026
  /**
@@ -699,20 +1032,35 @@ export default class Nymph {
699
1032
  */
700
1033
  public async deleteEntityByID(
701
1034
  guid: string,
702
- className?: string
1035
+ className?: string,
703
1036
  ): Promise<boolean> {
704
- for (let callback of this.beforeDeleteEntityByIDCallbacks) {
705
- if (callback) {
706
- await callback(this, guid, className);
1037
+ try {
1038
+ for (let callback of this.beforeDeleteEntityByIDCallbacks) {
1039
+ if (callback) {
1040
+ await callback(this, guid, className);
1041
+ }
707
1042
  }
708
- }
709
- const result = this.driver.deleteEntityByID(guid, className);
710
- for (let callback of this.afterDeleteEntityByIDCallbacks) {
711
- if (callback) {
712
- await callback(this, result);
1043
+ let result: Promise<boolean>;
1044
+ try {
1045
+ result = this.driver.deleteEntityByID(guid, className);
1046
+ } catch (e: any) {
1047
+ for (let callback of this.failedDeleteEntityByIDCallbacks) {
1048
+ if (callback) {
1049
+ await callback(this, e);
1050
+ }
1051
+ }
1052
+ throw e;
1053
+ }
1054
+ for (let callback of this.afterDeleteEntityByIDCallbacks) {
1055
+ if (callback) {
1056
+ await callback(this, result);
1057
+ }
713
1058
  }
1059
+ return await result;
1060
+ } catch (e: any) {
1061
+ this.config.debugError('nymph', `Failed to delete entity by ID: ${e}`);
1062
+ throw e;
714
1063
  }
715
- return await result;
716
1064
  }
717
1065
 
718
1066
  /**
@@ -737,7 +1085,7 @@ export default class Nymph {
737
1085
  * # brackets ([]).
738
1086
  * # Properties are stored like this:
739
1087
  * # propname=JSON.stringify(value)
740
- * abilities=["system/all"]
1088
+ * abilities=["system/admin"]
741
1089
  * groups=[]
742
1090
  * inheritAbilities=false
743
1091
  * name="admin"
@@ -752,7 +1100,12 @@ export default class Nymph {
752
1100
  * @returns True on success, false on failure.
753
1101
  */
754
1102
  public async export(filename: string): Promise<boolean> {
755
- return await this.driver.export(filename);
1103
+ try {
1104
+ return await this.driver.export(filename);
1105
+ } catch (e: any) {
1106
+ this.config.debugError('nymph', `Failed to export: ${e}`);
1107
+ throw e;
1108
+ }
756
1109
  }
757
1110
 
758
1111
  /**
@@ -761,17 +1114,227 @@ export default class Nymph {
761
1114
  * @returns True on success, false on failure.
762
1115
  */
763
1116
  public async exportPrint(): Promise<boolean> {
764
- return await this.driver.exportPrint();
1117
+ try {
1118
+ return await this.driver.exportPrint();
1119
+ } catch (e: any) {
1120
+ this.config.debugError('nymph', `Failed to export: ${e}`);
1121
+ throw e;
1122
+ }
765
1123
  }
766
1124
 
767
1125
  /**
768
1126
  * Import entities from a file.
769
1127
  *
770
1128
  * @param filename The file to import from.
1129
+ * @param options The options for the import.
771
1130
  * @returns True on success, false on failure.
772
1131
  */
773
- public async import(filename: string): Promise<boolean> {
774
- return await this.driver.import(filename);
1132
+ public async import(
1133
+ filename: string,
1134
+ options?: { ignoreUnknownETypes?: boolean },
1135
+ ): Promise<boolean> {
1136
+ try {
1137
+ return await this.driver.import(filename, options);
1138
+ } catch (e: any) {
1139
+ this.config.debugError('nymph', `Failed to import: ${e}`);
1140
+ throw e;
1141
+ }
1142
+ }
1143
+
1144
+ /**
1145
+ * Import a single entity.
1146
+ *
1147
+ * @param entity The entity data to import.
1148
+ */
1149
+ public async importEntity(entity: {
1150
+ guid: string;
1151
+ cdate: number;
1152
+ mdate: number;
1153
+ tags: string[];
1154
+ sdata: SerializedEntityData;
1155
+ etype: string;
1156
+ }) {
1157
+ try {
1158
+ return await this.driver.importEntity(entity);
1159
+ } catch (e: any) {
1160
+ this.config.debugError('nymph', `Failed to import: ${e}`);
1161
+ throw e;
1162
+ }
1163
+ }
1164
+
1165
+ /**
1166
+ * Import a single entity's tokens.
1167
+ *
1168
+ * This does not update any of the actual data, only the tokens used for full
1169
+ * text search.
1170
+ *
1171
+ * Be careful with this function, since you can overwrite existing tokens with
1172
+ * data that is out of sync. The DB _should_ prevent you from inserting tokens
1173
+ * for an entity that doesn't exist, but it _won't_ prevent you from inserting
1174
+ * tokens for properties that don't exist on an entity.
1175
+ *
1176
+ * @param entity The entity data to import.
1177
+ */
1178
+ public async importEntityTokens(entity: {
1179
+ guid: string;
1180
+ cdate: number;
1181
+ mdate: number;
1182
+ tags: string[];
1183
+ sdata: SerializedEntityData;
1184
+ etype: string;
1185
+ }) {
1186
+ try {
1187
+ return await this.driver.importEntityTokens(entity);
1188
+ } catch (e: any) {
1189
+ this.config.debugError('nymph', `Failed to import: ${e}`);
1190
+ throw e;
1191
+ }
1192
+ }
1193
+
1194
+ /**
1195
+ * Import a single entity's Tilmeld access control properties.
1196
+ *
1197
+ * This does not update any of the other data, only the access control
1198
+ * columns.
1199
+ *
1200
+ * @param entity The entity data to import.
1201
+ */
1202
+ public async importEntityTilmeldAC(entity: {
1203
+ guid: string;
1204
+ cdate: number;
1205
+ mdate: number;
1206
+ tags: string[];
1207
+ sdata: SerializedEntityData;
1208
+ etype: string;
1209
+ }) {
1210
+ try {
1211
+ return await this.driver.importEntityTilmeldAC(entity);
1212
+ } catch (e: any) {
1213
+ this.config.debugError('nymph', `Failed to import: ${e}`);
1214
+ throw e;
1215
+ }
1216
+ }
1217
+
1218
+ /**
1219
+ * Detect whether the database needs to be migrated.
1220
+ *
1221
+ * If not false (so, one of the string values), the database should be
1222
+ * exported with an old version of Nymph, then imported into a fresh database
1223
+ * with this version. Sometimes, a live migration can be done instead, which
1224
+ * will require no downtime. You should still export the database beforehand,
1225
+ * in case anything goes wrong.
1226
+ *
1227
+ * 'json' means the data tables are missing the JSON field, and the DB
1228
+ * **must** be exported and imported to fix it.
1229
+ *
1230
+ * 'tokens' means the FTS token tables are missing, and you can get away with
1231
+ * a live migration with `liveMigration('tokenTables')` and doing
1232
+ * `importEntityTokens(entity)` for each entity.
1233
+ *
1234
+ * 'tilmeldColumns' means the entity columns for "user", "group", and access
1235
+ * controls are missing, and you can get away with a live migration with
1236
+ * `liveMigration('tilmeldColumns')` and doing
1237
+ * `importEntityTilmeldAC(entity)` for each entity. DO NOT update to any
1238
+ * version later than 1.0.0-beta.109 before this is done, or access controls
1239
+ * WILL NOT work, and everyone will have access to every entity!
1240
+ */
1241
+ public async needsMigration() {
1242
+ return await this.driver.needsMigration();
1243
+ }
1244
+
1245
+ /**
1246
+ * Perform a live migration on the DB.
1247
+ *
1248
+ * A 'tokenTables' migration will simply add the missing token tables. It will
1249
+ * **not** fill them. You must use `importEntityTokens` on each entity after
1250
+ * running this to fill the token tables and enable full text search matching
1251
+ * on existing entities.
1252
+ *
1253
+ * A 'tilmeldColumns' migration will simply add the missing Tilmeld columns
1254
+ * and indexes to each entity table. It will **not** fill them, and you must
1255
+ * fill them before updating to the latest version of Nymph, or everyone will
1256
+ * be able to access every entity! You must use `importEntityTilmeldAC` on
1257
+ * each entity after running this.
1258
+ *
1259
+ * A 'tilmeldRemoveOldRows' migration will remove all of the old access
1260
+ * control rows in the data tables that are no longer used as of
1261
+ * 1.0.0-beta.110. MAKE SURE YOU HAVE RUN `importEntityTilmeldAC` ON ALL
1262
+ * ENTITIES BEFORE RUNNING THIS, OR THE DATA WILL BE IRRETRIEVABLY LOST!
1263
+ */
1264
+ public async liveMigration(
1265
+ migrationType: 'tokenTables' | 'tilmeldColumns' | 'tilmeldRemoveOldRows',
1266
+ ) {
1267
+ return await this.driver.liveMigration(migrationType);
1268
+ }
1269
+
1270
+ /**
1271
+ * Returns a list of the etypes stored in the DB.
1272
+ */
1273
+ public async getEtypes() {
1274
+ return await this.driver.getEtypes();
1275
+ }
1276
+
1277
+ /**
1278
+ * Get all the indexes defined for a specific etype.
1279
+ *
1280
+ * @param etype The etype to get indexes for.
1281
+ */
1282
+ public async getIndexes(etype: string) {
1283
+ return await this.driver.getIndexes(etype);
1284
+ }
1285
+
1286
+ /**
1287
+ * Add an index on a specific property to an etype.
1288
+ *
1289
+ * Nymph adds general indexes on etypes, but they span across all properties,
1290
+ * so they can only speed up a query so much. You can add indexes that are
1291
+ * specific to a property with this function, which will speed up queries for
1292
+ * that property. If an etype only has one or two properties, you may not need
1293
+ * a specific index, but if your etypes include many properties or properties
1294
+ * that generally contain large amounts of data, adding a specific index can
1295
+ * drastically improve the performance of your queries.
1296
+ *
1297
+ * The scope of the index will affect which queries it will speed up:
1298
+ *
1299
+ * - data: This will speed up comparisons and equality checks in queries.
1300
+ * - references: This will speed up ref and qref clauses in queries.
1301
+ * - tokens: This will speed up search clauses in queries.
1302
+ *
1303
+ * The name must be unique within the etype and scope or the existing index
1304
+ * will be overwritten.
1305
+ *
1306
+ * You do not need to add reference indexes for the "user" and "group"
1307
+ * properties, because these are already specifically indexed by Nymph.
1308
+ *
1309
+ * @param etype The etype to add the index on.
1310
+ * @param definition The definition of the index.
1311
+ */
1312
+ public async addIndex(
1313
+ etype: string,
1314
+ definition: {
1315
+ scope: 'data' | 'references' | 'tokens';
1316
+ name: string;
1317
+ property: string;
1318
+ },
1319
+ ) {
1320
+ this.driver.checkIndexName(definition.name);
1321
+ return await this.driver.addIndex(etype, definition);
1322
+ }
1323
+
1324
+ /**
1325
+ * Delete an index from an etype.
1326
+ *
1327
+ * @param etype The etype to delete the index on.
1328
+ * @param scope The scope of the index.
1329
+ * @param name The name of the index to delete.
1330
+ */
1331
+ public async deleteIndex(
1332
+ etype: string,
1333
+ scope: 'data' | 'references' | 'tokens',
1334
+ name: string,
1335
+ ) {
1336
+ this.driver.checkIndexName(name);
1337
+ return await this.driver.deleteIndex(etype, scope, name);
775
1338
  }
776
1339
 
777
1340
  public on<T extends NymphEventType>(
@@ -779,82 +1342,134 @@ export default class Nymph {
779
1342
  callback: T extends 'connect'
780
1343
  ? NymphConnectCallback
781
1344
  : T extends 'disconnect'
782
- ? NymphDisconnectCallback
783
- : T extends 'query'
784
- ? NymphQueryCallback
785
- : T extends 'beforeGetEntity'
786
- ? NymphBeforeGetEntityCallback
787
- : T extends 'beforeGetEntities'
788
- ? NymphBeforeGetEntitiesCallback
789
- : T extends 'beforeSaveEntity'
790
- ? NymphBeforeSaveEntityCallback
791
- : T extends 'afterSaveEntity'
792
- ? NymphAfterSaveEntityCallback
793
- : T extends 'beforeDeleteEntity'
794
- ? NymphBeforeDeleteEntityCallback
795
- : T extends 'afterDeleteEntity'
796
- ? NymphAfterDeleteEntityCallback
797
- : T extends 'beforeDeleteEntityByID'
798
- ? NymphBeforeDeleteEntityByIDCallback
799
- : T extends 'afterDeleteEntityByID'
800
- ? NymphAfterDeleteEntityByIDCallback
801
- : T extends 'beforeNewUID'
802
- ? NymphBeforeNewUIDCallback
803
- : T extends 'afterNewUID'
804
- ? NymphAfterNewUIDCallback
805
- : T extends 'beforeSetUID'
806
- ? NymphBeforeSetUIDCallback
807
- : T extends 'afterSetUID'
808
- ? NymphAfterSetUIDCallback
809
- : T extends 'beforeRenameUID'
810
- ? NymphBeforeRenameUIDCallback
811
- : T extends 'afterRenameUID'
812
- ? NymphAfterRenameUIDCallback
813
- : T extends 'beforeDeleteUID'
814
- ? NymphBeforeDeleteUIDCallback
815
- : T extends 'afterDeleteUID'
816
- ? NymphAfterDeleteUIDCallback
817
- : never
1345
+ ? NymphDisconnectCallback
1346
+ : T extends 'query'
1347
+ ? NymphQueryCallback
1348
+ : T extends 'beforeGetEntity'
1349
+ ? NymphBeforeGetEntityCallback
1350
+ : T extends 'beforeGetEntities'
1351
+ ? NymphBeforeGetEntitiesCallback
1352
+ : T extends 'beforeSaveEntity'
1353
+ ? NymphBeforeSaveEntityCallback
1354
+ : T extends 'afterSaveEntity'
1355
+ ? NymphAfterSaveEntityCallback
1356
+ : T extends 'failedSaveEntity'
1357
+ ? NymphFailedSaveEntityCallback
1358
+ : T extends 'beforeDeleteEntity'
1359
+ ? NymphBeforeDeleteEntityCallback
1360
+ : T extends 'afterDeleteEntity'
1361
+ ? NymphAfterDeleteEntityCallback
1362
+ : T extends 'failedDeleteEntity'
1363
+ ? NymphFailedDeleteEntityCallback
1364
+ : T extends 'beforeDeleteEntityByID'
1365
+ ? NymphBeforeDeleteEntityByIDCallback
1366
+ : T extends 'afterDeleteEntityByID'
1367
+ ? NymphAfterDeleteEntityByIDCallback
1368
+ : T extends 'failedDeleteEntityByID'
1369
+ ? NymphFailedDeleteEntityByIDCallback
1370
+ : T extends 'beforeNewUID'
1371
+ ? NymphBeforeNewUIDCallback
1372
+ : T extends 'afterNewUID'
1373
+ ? NymphAfterNewUIDCallback
1374
+ : T extends 'failedNewUID'
1375
+ ? NymphFailedNewUIDCallback
1376
+ : T extends 'beforeSetUID'
1377
+ ? NymphBeforeSetUIDCallback
1378
+ : T extends 'afterSetUID'
1379
+ ? NymphAfterSetUIDCallback
1380
+ : T extends 'failedSetUID'
1381
+ ? NymphFailedSetUIDCallback
1382
+ : T extends 'beforeRenameUID'
1383
+ ? NymphBeforeRenameUIDCallback
1384
+ : T extends 'afterRenameUID'
1385
+ ? NymphAfterRenameUIDCallback
1386
+ : T extends 'failedRenameUID'
1387
+ ? NymphFailedRenameUIDCallback
1388
+ : T extends 'beforeDeleteUID'
1389
+ ? NymphBeforeDeleteUIDCallback
1390
+ : T extends 'afterDeleteUID'
1391
+ ? NymphAfterDeleteUIDCallback
1392
+ : T extends 'failedDeleteUID'
1393
+ ? NymphFailedDeleteUIDCallback
1394
+ : T extends 'beforeStartTransaction'
1395
+ ? NymphBeforeStartTransactionCallback
1396
+ : T extends 'afterStartTransaction'
1397
+ ? NymphAfterStartTransactionCallback
1398
+ : T extends 'beforeCommitTransaction'
1399
+ ? NymphBeforeCommitTransactionCallback
1400
+ : T extends 'afterCommitTransaction'
1401
+ ? NymphAfterCommitTransactionCallback
1402
+ : T extends 'beforeRollbackTransaction'
1403
+ ? NymphBeforeRollbackTransactionCallback
1404
+ : T extends 'afterRollbackTransaction'
1405
+ ? NymphAfterRollbackTransactionCallback
1406
+ : never,
818
1407
  ) {
819
1408
  const prop = (event + 'Callbacks') as T extends 'connect'
820
1409
  ? 'connectCallbacks'
821
1410
  : T extends 'disconnect'
822
- ? 'disconnectCallbacks'
823
- : T extends 'query'
824
- ? 'queryCallbacks'
825
- : T extends 'beforeGetEntity'
826
- ? 'beforeGetEntityCallbacks'
827
- : T extends 'beforeGetEntities'
828
- ? 'beforeGetEntitiesCallbacks'
829
- : T extends 'beforeSaveEntity'
830
- ? 'beforeSaveEntityCallbacks'
831
- : T extends 'afterSaveEntity'
832
- ? 'afterSaveEntityCallbacks'
833
- : T extends 'beforeDeleteEntity'
834
- ? 'beforeDeleteEntityCallbacks'
835
- : T extends 'afterDeleteEntity'
836
- ? 'afterDeleteEntityCallbacks'
837
- : T extends 'beforeDeleteEntityByID'
838
- ? 'beforeDeleteEntityByIDCallbacks'
839
- : T extends 'afterDeleteEntityByID'
840
- ? 'afterDeleteEntityByIDCallbacks'
841
- : T extends 'beforeNewUID'
842
- ? 'beforeNewUIDCallbacks'
843
- : T extends 'afterNewUID'
844
- ? 'afterNewUIDCallbacks'
845
- : T extends 'beforeSetUID'
846
- ? 'beforeSetUIDCallbacks'
847
- : T extends 'afterSetUID'
848
- ? 'afterSetUIDCallbacks'
849
- : T extends 'beforeRenameUID'
850
- ? 'beforeRenameUIDCallbacks'
851
- : T extends 'afterRenameUID'
852
- ? 'afterRenameUIDCallbacks'
853
- : T extends 'beforeDeleteUID'
854
- ? 'beforeDeleteUIDCallbacks'
855
- : T extends 'afterDeleteUID'
856
- ? 'afterDeleteUIDCallbacks'
857
- : never;
1411
+ ? 'disconnectCallbacks'
1412
+ : T extends 'query'
1413
+ ? 'queryCallbacks'
1414
+ : T extends 'beforeGetEntity'
1415
+ ? 'beforeGetEntityCallbacks'
1416
+ : T extends 'beforeGetEntities'
1417
+ ? 'beforeGetEntitiesCallbacks'
1418
+ : T extends 'beforeSaveEntity'
1419
+ ? 'beforeSaveEntityCallbacks'
1420
+ : T extends 'afterSaveEntity'
1421
+ ? 'afterSaveEntityCallbacks'
1422
+ : T extends 'failedSaveEntity'
1423
+ ? 'failedSaveEntityCallbacks'
1424
+ : T extends 'beforeDeleteEntity'
1425
+ ? 'beforeDeleteEntityCallbacks'
1426
+ : T extends 'afterDeleteEntity'
1427
+ ? 'afterDeleteEntityCallbacks'
1428
+ : T extends 'failedDeleteEntity'
1429
+ ? 'failedDeleteEntityCallbacks'
1430
+ : T extends 'beforeDeleteEntityByID'
1431
+ ? 'beforeDeleteEntityByIDCallbacks'
1432
+ : T extends 'afterDeleteEntityByID'
1433
+ ? 'afterDeleteEntityByIDCallbacks'
1434
+ : T extends 'failedDeleteEntityByID'
1435
+ ? 'failedDeleteEntityByIDCallbacks'
1436
+ : T extends 'beforeNewUID'
1437
+ ? 'beforeNewUIDCallbacks'
1438
+ : T extends 'afterNewUID'
1439
+ ? 'afterNewUIDCallbacks'
1440
+ : T extends 'failedNewUID'
1441
+ ? 'failedNewUIDCallbacks'
1442
+ : T extends 'beforeSetUID'
1443
+ ? 'beforeSetUIDCallbacks'
1444
+ : T extends 'afterSetUID'
1445
+ ? 'afterSetUIDCallbacks'
1446
+ : T extends 'failedSetUID'
1447
+ ? 'failedSetUIDCallbacks'
1448
+ : T extends 'beforeRenameUID'
1449
+ ? 'beforeRenameUIDCallbacks'
1450
+ : T extends 'afterRenameUID'
1451
+ ? 'afterRenameUIDCallbacks'
1452
+ : T extends 'failedRenameUID'
1453
+ ? 'failedRenameUIDCallbacks'
1454
+ : T extends 'beforeDeleteUID'
1455
+ ? 'beforeDeleteUIDCallbacks'
1456
+ : T extends 'afterDeleteUID'
1457
+ ? 'afterDeleteUIDCallbacks'
1458
+ : T extends 'failedDeleteUID'
1459
+ ? 'failedDeleteUIDCallbacks'
1460
+ : T extends 'beforeStartTransaction'
1461
+ ? 'beforeStartTransactionCallbacks'
1462
+ : T extends 'afterStartTransaction'
1463
+ ? 'afterStartTransactionCallbacks'
1464
+ : T extends 'beforeCommitTransaction'
1465
+ ? 'beforeCommitTransactionCallbacks'
1466
+ : T extends 'afterCommitTransaction'
1467
+ ? 'afterCommitTransactionCallbacks'
1468
+ : T extends 'beforeRollbackTransaction'
1469
+ ? 'beforeRollbackTransactionCallbacks'
1470
+ : T extends 'afterRollbackTransaction'
1471
+ ? 'afterRollbackTransactionCallbacks'
1472
+ : never;
858
1473
  if (!(prop in this)) {
859
1474
  throw new Error('Invalid event type.');
860
1475
  }
@@ -868,82 +1483,134 @@ export default class Nymph {
868
1483
  callback: T extends 'connect'
869
1484
  ? NymphConnectCallback
870
1485
  : T extends 'disconnect'
871
- ? NymphDisconnectCallback
872
- : T extends 'query'
873
- ? NymphQueryCallback
874
- : T extends 'beforeGetEntity'
875
- ? NymphBeforeGetEntityCallback
876
- : T extends 'beforeGetEntities'
877
- ? NymphBeforeGetEntitiesCallback
878
- : T extends 'beforeSaveEntity'
879
- ? NymphBeforeSaveEntityCallback
880
- : T extends 'afterSaveEntity'
881
- ? NymphAfterSaveEntityCallback
882
- : T extends 'beforeDeleteEntity'
883
- ? NymphBeforeDeleteEntityCallback
884
- : T extends 'afterDeleteEntity'
885
- ? NymphAfterDeleteEntityCallback
886
- : T extends 'beforeDeleteEntityByID'
887
- ? NymphBeforeDeleteEntityByIDCallback
888
- : T extends 'afterDeleteEntityByID'
889
- ? NymphAfterDeleteEntityByIDCallback
890
- : T extends 'beforeNewUID'
891
- ? NymphBeforeNewUIDCallback
892
- : T extends 'afterNewUID'
893
- ? NymphAfterNewUIDCallback
894
- : T extends 'beforeSetUID'
895
- ? NymphBeforeSetUIDCallback
896
- : T extends 'afterSetUID'
897
- ? NymphAfterSetUIDCallback
898
- : T extends 'beforeRenameUID'
899
- ? NymphBeforeRenameUIDCallback
900
- : T extends 'afterRenameUID'
901
- ? NymphAfterRenameUIDCallback
902
- : T extends 'beforeDeleteUID'
903
- ? NymphBeforeDeleteUIDCallback
904
- : T extends 'afterDeleteUID'
905
- ? NymphAfterDeleteUIDCallback
906
- : never
1486
+ ? NymphDisconnectCallback
1487
+ : T extends 'query'
1488
+ ? NymphQueryCallback
1489
+ : T extends 'beforeGetEntity'
1490
+ ? NymphBeforeGetEntityCallback
1491
+ : T extends 'beforeGetEntities'
1492
+ ? NymphBeforeGetEntitiesCallback
1493
+ : T extends 'beforeSaveEntity'
1494
+ ? NymphBeforeSaveEntityCallback
1495
+ : T extends 'afterSaveEntity'
1496
+ ? NymphAfterSaveEntityCallback
1497
+ : T extends 'failedSaveEntity'
1498
+ ? NymphFailedSaveEntityCallback
1499
+ : T extends 'beforeDeleteEntity'
1500
+ ? NymphBeforeDeleteEntityCallback
1501
+ : T extends 'afterDeleteEntity'
1502
+ ? NymphAfterDeleteEntityCallback
1503
+ : T extends 'failedDeleteEntity'
1504
+ ? NymphFailedDeleteEntityCallback
1505
+ : T extends 'beforeDeleteEntityByID'
1506
+ ? NymphBeforeDeleteEntityByIDCallback
1507
+ : T extends 'afterDeleteEntityByID'
1508
+ ? NymphAfterDeleteEntityByIDCallback
1509
+ : T extends 'failedDeleteEntityByID'
1510
+ ? NymphFailedDeleteEntityByIDCallback
1511
+ : T extends 'beforeNewUID'
1512
+ ? NymphBeforeNewUIDCallback
1513
+ : T extends 'afterNewUID'
1514
+ ? NymphAfterNewUIDCallback
1515
+ : T extends 'failedNewUID'
1516
+ ? NymphFailedNewUIDCallback
1517
+ : T extends 'beforeSetUID'
1518
+ ? NymphBeforeSetUIDCallback
1519
+ : T extends 'afterSetUID'
1520
+ ? NymphAfterSetUIDCallback
1521
+ : T extends 'failedSetUID'
1522
+ ? NymphFailedSetUIDCallback
1523
+ : T extends 'beforeRenameUID'
1524
+ ? NymphBeforeRenameUIDCallback
1525
+ : T extends 'afterRenameUID'
1526
+ ? NymphAfterRenameUIDCallback
1527
+ : T extends 'failedRenameUID'
1528
+ ? NymphFailedRenameUIDCallback
1529
+ : T extends 'beforeDeleteUID'
1530
+ ? NymphBeforeDeleteUIDCallback
1531
+ : T extends 'afterDeleteUID'
1532
+ ? NymphAfterDeleteUIDCallback
1533
+ : T extends 'failedDeleteUID'
1534
+ ? NymphFailedDeleteUIDCallback
1535
+ : T extends 'beforeStartTransaction'
1536
+ ? NymphBeforeStartTransactionCallback
1537
+ : T extends 'afterStartTransaction'
1538
+ ? NymphAfterStartTransactionCallback
1539
+ : T extends 'beforeCommitTransaction'
1540
+ ? NymphBeforeCommitTransactionCallback
1541
+ : T extends 'afterCommitTransaction'
1542
+ ? NymphAfterCommitTransactionCallback
1543
+ : T extends 'beforeRollbackTransaction'
1544
+ ? NymphBeforeRollbackTransactionCallback
1545
+ : T extends 'afterRollbackTransaction'
1546
+ ? NymphAfterRollbackTransactionCallback
1547
+ : never,
907
1548
  ) {
908
1549
  const prop = (event + 'Callbacks') as T extends 'connect'
909
1550
  ? 'connectCallbacks'
910
1551
  : T extends 'disconnect'
911
- ? 'disconnectCallbacks'
912
- : T extends 'query'
913
- ? 'queryCallbacks'
914
- : T extends 'beforeSaveEntity'
915
- ? 'beforeSaveEntityCallbacks'
916
- : T extends 'beforeGetEntity'
917
- ? 'beforeGetEntityCallbacks'
918
- : T extends 'beforeGetEntities'
919
- ? 'beforeGetEntitiesCallbacks'
920
- : T extends 'afterSaveEntity'
921
- ? 'afterSaveEntityCallbacks'
922
- : T extends 'beforeDeleteEntity'
923
- ? 'beforeDeleteEntityCallbacks'
924
- : T extends 'afterDeleteEntity'
925
- ? 'afterDeleteEntityCallbacks'
926
- : T extends 'beforeDeleteEntityByID'
927
- ? 'beforeDeleteEntityByIDCallbacks'
928
- : T extends 'afterDeleteEntityByID'
929
- ? 'afterDeleteEntityByIDCallbacks'
930
- : T extends 'beforeNewUID'
931
- ? 'beforeNewUIDCallbacks'
932
- : T extends 'afterNewUID'
933
- ? 'afterNewUIDCallbacks'
934
- : T extends 'beforeSetUID'
935
- ? 'beforeSetUIDCallbacks'
936
- : T extends 'afterSetUID'
937
- ? 'afterSetUIDCallbacks'
938
- : T extends 'beforeRenameUID'
939
- ? 'beforeRenameUIDCallbacks'
940
- : T extends 'afterRenameUID'
941
- ? 'afterRenameUIDCallbacks'
942
- : T extends 'beforeDeleteUID'
943
- ? 'beforeDeleteUIDCallbacks'
944
- : T extends 'afterDeleteUID'
945
- ? 'afterDeleteUIDCallbacks'
946
- : never;
1552
+ ? 'disconnectCallbacks'
1553
+ : T extends 'query'
1554
+ ? 'queryCallbacks'
1555
+ : T extends 'beforeSaveEntity'
1556
+ ? 'beforeSaveEntityCallbacks'
1557
+ : T extends 'beforeGetEntity'
1558
+ ? 'beforeGetEntityCallbacks'
1559
+ : T extends 'beforeGetEntities'
1560
+ ? 'beforeGetEntitiesCallbacks'
1561
+ : T extends 'afterSaveEntity'
1562
+ ? 'afterSaveEntityCallbacks'
1563
+ : T extends 'failedSaveEntity'
1564
+ ? 'failedSaveEntityCallbacks'
1565
+ : T extends 'beforeDeleteEntity'
1566
+ ? 'beforeDeleteEntityCallbacks'
1567
+ : T extends 'afterDeleteEntity'
1568
+ ? 'afterDeleteEntityCallbacks'
1569
+ : T extends 'failedDeleteEntity'
1570
+ ? 'failedDeleteEntityCallbacks'
1571
+ : T extends 'beforeDeleteEntityByID'
1572
+ ? 'beforeDeleteEntityByIDCallbacks'
1573
+ : T extends 'afterDeleteEntityByID'
1574
+ ? 'afterDeleteEntityByIDCallbacks'
1575
+ : T extends 'failedDeleteEntityByID'
1576
+ ? 'failedDeleteEntityByIDCallbacks'
1577
+ : T extends 'beforeNewUID'
1578
+ ? 'beforeNewUIDCallbacks'
1579
+ : T extends 'afterNewUID'
1580
+ ? 'afterNewUIDCallbacks'
1581
+ : T extends 'failedNewUID'
1582
+ ? 'failedNewUIDCallbacks'
1583
+ : T extends 'beforeSetUID'
1584
+ ? 'beforeSetUIDCallbacks'
1585
+ : T extends 'afterSetUID'
1586
+ ? 'afterSetUIDCallbacks'
1587
+ : T extends 'failedSetUID'
1588
+ ? 'failedSetUIDCallbacks'
1589
+ : T extends 'beforeRenameUID'
1590
+ ? 'beforeRenameUIDCallbacks'
1591
+ : T extends 'afterRenameUID'
1592
+ ? 'afterRenameUIDCallbacks'
1593
+ : T extends 'failedRenameUID'
1594
+ ? 'failedRenameUIDCallbacks'
1595
+ : T extends 'beforeDeleteUID'
1596
+ ? 'beforeDeleteUIDCallbacks'
1597
+ : T extends 'afterDeleteUID'
1598
+ ? 'afterDeleteUIDCallbacks'
1599
+ : T extends 'failedDeleteUID'
1600
+ ? 'failedDeleteUIDCallbacks'
1601
+ : T extends 'beforeStartTransaction'
1602
+ ? 'beforeStartTransactionCallbacks'
1603
+ : T extends 'afterStartTransaction'
1604
+ ? 'afterStartTransactionCallbacks'
1605
+ : T extends 'beforeCommitTransaction'
1606
+ ? 'beforeCommitTransactionCallbacks'
1607
+ : T extends 'afterCommitTransaction'
1608
+ ? 'afterCommitTransactionCallbacks'
1609
+ : T extends 'beforeRollbackTransaction'
1610
+ ? 'beforeRollbackTransactionCallbacks'
1611
+ : T extends 'afterRollbackTransaction'
1612
+ ? 'afterRollbackTransactionCallbacks'
1613
+ : never;
947
1614
  if (!(prop in this)) {
948
1615
  return false;
949
1616
  }