@nymphjs/nymph 1.0.0-beta.11 → 1.0.0-beta.111

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/CHANGELOG.md +540 -0
  2. package/README.md +23 -21
  3. package/dist/Entity.d.ts +293 -10
  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 +586 -12
  11. package/dist/Nymph.js +844 -179
  12. package/dist/Nymph.js.map +1 -1
  13. package/dist/Nymph.types.d.ts +81 -8
  14. package/dist/Nymph.types.js +3 -5
  15. package/dist/Nymph.types.js.map +1 -1
  16. package/dist/conf/d.d.ts +43 -0
  17. package/dist/conf/d.js +1 -2
  18. package/dist/conf/defaults.d.ts +1 -1
  19. package/dist/conf/defaults.js +26 -3
  20. package/dist/conf/defaults.js.map +1 -1
  21. package/dist/conf/index.d.ts +2 -2
  22. package/dist/conf/index.js +2 -8
  23. package/dist/conf/index.js.map +1 -1
  24. package/dist/driver/NymphDriver.d.ts +201 -40
  25. package/dist/driver/NymphDriver.js +685 -239
  26. package/dist/driver/NymphDriver.js.map +1 -1
  27. package/dist/driver/index.d.ts +2 -2
  28. package/dist/driver/index.js +3 -23
  29. package/dist/driver/index.js.map +1 -1
  30. package/dist/driver/testArtifacts.d.ts +2 -2
  31. package/dist/driver/testArtifacts.js +909 -93
  32. package/dist/driver/testArtifacts.js.map +1 -1
  33. package/dist/errors/ClassNotAvailableError.js +1 -5
  34. package/dist/errors/ClassNotAvailableError.js.map +1 -1
  35. package/dist/errors/EntityConflictError.js +1 -5
  36. package/dist/errors/EntityConflictError.js.map +1 -1
  37. package/dist/errors/EntityInvalidDataError.d.ts +6 -0
  38. package/dist/errors/EntityInvalidDataError.js +8 -6
  39. package/dist/errors/EntityInvalidDataError.js.map +1 -1
  40. package/dist/errors/EntityIsSleepingReferenceError.js +1 -5
  41. package/dist/errors/EntityIsSleepingReferenceError.js.map +1 -1
  42. package/dist/errors/EntityUniqueConstraintError.d.ts +3 -0
  43. package/dist/errors/EntityUniqueConstraintError.js +7 -0
  44. package/dist/errors/EntityUniqueConstraintError.js.map +1 -0
  45. package/dist/errors/HttpError.d.ts +5 -0
  46. package/dist/errors/HttpError.js +11 -0
  47. package/dist/errors/HttpError.js.map +1 -0
  48. package/dist/errors/InvalidParametersError.js +1 -5
  49. package/dist/errors/InvalidParametersError.js.map +1 -1
  50. package/dist/errors/InvalidStateError.d.ts +3 -0
  51. package/dist/errors/InvalidStateError.js +7 -0
  52. package/dist/errors/InvalidStateError.js.map +1 -0
  53. package/dist/errors/NotConfiguredError.js +1 -5
  54. package/dist/errors/NotConfiguredError.js.map +1 -1
  55. package/dist/errors/QueryFailedError.d.ts +2 -1
  56. package/dist/errors/QueryFailedError.js +5 -6
  57. package/dist/errors/QueryFailedError.js.map +1 -1
  58. package/dist/errors/UnableToConnectError.js +1 -5
  59. package/dist/errors/UnableToConnectError.js.map +1 -1
  60. package/dist/errors/index.d.ts +11 -8
  61. package/dist/errors/index.js +11 -24
  62. package/dist/errors/index.js.map +1 -1
  63. package/dist/index.d.ts +11 -11
  64. package/dist/index.js +14 -34
  65. package/dist/index.js.map +1 -1
  66. package/dist/nymphJoiProps.d.ts +1 -1
  67. package/dist/nymphJoiProps.js +10 -15
  68. package/dist/nymphJoiProps.js.map +1 -1
  69. package/dist/testArtifacts.d.ts +19 -6
  70. package/dist/testArtifacts.js +32 -37
  71. package/dist/testArtifacts.js.map +1 -1
  72. package/dist/testMocks.d.ts +27 -12
  73. package/dist/testMocks.js +40 -29
  74. package/dist/testMocks.js.map +1 -1
  75. package/dist/utils.d.ts +4 -4
  76. package/dist/utils.js +40 -29
  77. package/dist/utils.js.map +1 -1
  78. package/jest.config.js +11 -2
  79. package/package.json +24 -20
  80. package/src/Entity.test.ts +50 -7
  81. package/src/Entity.ts +489 -150
  82. package/src/Entity.types.ts +72 -74
  83. package/src/Nymph.ts +985 -322
  84. package/src/Nymph.types.ts +150 -27
  85. package/src/conf/d.ts +25 -0
  86. package/src/conf/defaults.ts +34 -1
  87. package/src/conf/index.ts +2 -2
  88. package/src/driver/NymphDriver.ts +859 -412
  89. package/src/driver/index.ts +2 -2
  90. package/src/driver/testArtifacts.ts +1090 -186
  91. package/src/errors/EntityUniqueConstraintError.ts +6 -0
  92. package/src/errors/HttpError.ts +12 -0
  93. package/src/errors/InvalidStateError.ts +6 -0
  94. package/src/errors/QueryFailedError.ts +3 -1
  95. package/src/errors/index.ts +14 -8
  96. package/src/index.ts +11 -11
  97. package/src/nymphJoiProps.ts +2 -2
  98. package/src/testArtifacts.ts +20 -20
  99. package/src/testMocks.ts +76 -35
  100. package/src/type/locutus.d.ts +3 -0
  101. package/src/utils.ts +39 -18
  102. package/tsconfig.json +5 -3
  103. package/typedoc.json +4 -0
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,23 +22,36 @@ 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.
@@ -41,10 +64,16 @@ import type {
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;
355
571
  }
572
+ for (let callback of this.afterSetUIDCallbacks) {
573
+ if (callback) {
574
+ await callback(this, result);
575
+ }
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;
644
+ }
645
+ for (let callback of this.afterRenameUIDCallbacks) {
646
+ if (callback) {
647
+ await callback(this, result);
648
+ }
398
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;
713
1053
  }
1054
+ for (let callback of this.afterDeleteEntityByIDCallbacks) {
1055
+ if (callback) {
1056
+ await callback(this, result);
1057
+ }
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,7 +1114,12 @@ 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
  /**
@@ -771,7 +1129,208 @@ export default class Nymph {
771
1129
  * @returns True on success, false on failure.
772
1130
  */
773
1131
  public async import(filename: string): Promise<boolean> {
774
- return await this.driver.import(filename);
1132
+ try {
1133
+ return await this.driver.import(filename);
1134
+ } catch (e: any) {
1135
+ this.config.debugError('nymph', `Failed to import: ${e}`);
1136
+ throw e;
1137
+ }
1138
+ }
1139
+
1140
+ /**
1141
+ * Import a single entity.
1142
+ *
1143
+ * @param entity The entity data to import.
1144
+ */
1145
+ public async importEntity(entity: {
1146
+ guid: string;
1147
+ cdate: number;
1148
+ mdate: number;
1149
+ tags: string[];
1150
+ sdata: SerializedEntityData;
1151
+ etype: string;
1152
+ }) {
1153
+ try {
1154
+ return await this.driver.importEntity(entity);
1155
+ } catch (e: any) {
1156
+ this.config.debugError('nymph', `Failed to import: ${e}`);
1157
+ throw e;
1158
+ }
1159
+ }
1160
+
1161
+ /**
1162
+ * Import a single entity's tokens.
1163
+ *
1164
+ * This does not update any of the actual data, only the tokens used for full
1165
+ * text search.
1166
+ *
1167
+ * Be careful with this function, since you can overwrite existing tokens with
1168
+ * data that is out of sync. The DB _should_ prevent you from inserting tokens
1169
+ * for an entity that doesn't exist, but it _won't_ prevent you from inserting
1170
+ * tokens for properties that don't exist on an entity.
1171
+ *
1172
+ * @param entity The entity data to import.
1173
+ */
1174
+ public async importEntityTokens(entity: {
1175
+ guid: string;
1176
+ cdate: number;
1177
+ mdate: number;
1178
+ tags: string[];
1179
+ sdata: SerializedEntityData;
1180
+ etype: string;
1181
+ }) {
1182
+ try {
1183
+ return await this.driver.importEntityTokens(entity);
1184
+ } catch (e: any) {
1185
+ this.config.debugError('nymph', `Failed to import: ${e}`);
1186
+ throw e;
1187
+ }
1188
+ }
1189
+
1190
+ /**
1191
+ * Import a single entity's Tilmeld access control properties.
1192
+ *
1193
+ * This does not update any of the other data, only the access control
1194
+ * columns.
1195
+ *
1196
+ * @param entity The entity data to import.
1197
+ */
1198
+ public async importEntityTilmeldAC(entity: {
1199
+ guid: string;
1200
+ cdate: number;
1201
+ mdate: number;
1202
+ tags: string[];
1203
+ sdata: SerializedEntityData;
1204
+ etype: string;
1205
+ }) {
1206
+ try {
1207
+ return await this.driver.importEntityTilmeldAC(entity);
1208
+ } catch (e: any) {
1209
+ this.config.debugError('nymph', `Failed to import: ${e}`);
1210
+ throw e;
1211
+ }
1212
+ }
1213
+
1214
+ /**
1215
+ * Detect whether the database needs to be migrated.
1216
+ *
1217
+ * If not false (so, one of the string values), the database should be
1218
+ * exported with an old version of Nymph, then imported into a fresh database
1219
+ * with this version. Sometimes, a live migration can be done instead, which
1220
+ * will require no downtime. You should still export the database beforehand,
1221
+ * in case anything goes wrong.
1222
+ *
1223
+ * 'json' means the data tables are missing the JSON field, and the DB
1224
+ * **must** be exported and imported to fix it.
1225
+ *
1226
+ * 'tokens' means the FTS token tables are missing, and you can get away with
1227
+ * a live migration with `liveMigration('tokenTables')` and doing
1228
+ * `importEntityTokens(entity)` for each entity.
1229
+ *
1230
+ * 'tilmeldColumns' means the entity columns for "user", "group", and access
1231
+ * controls are missing, and you can get away with a live migration with
1232
+ * `liveMigration('tilmeldColumns')` and doing
1233
+ * `importEntityTilmeldAC(entity)` for each entity. DO NOT update to any
1234
+ * version later than 1.0.0-beta.109 before this is done, or access controls
1235
+ * WILL NOT work, and everyone will have access to every entity!
1236
+ */
1237
+ public async needsMigration() {
1238
+ return await this.driver.needsMigration();
1239
+ }
1240
+
1241
+ /**
1242
+ * Perform a live migration on the DB.
1243
+ *
1244
+ * A 'tokenTables' migration will simply add the missing token tables. It will
1245
+ * **not** fill them. You must use `importEntityTokens` on each entity after
1246
+ * running this to fill the token tables and enable full text search matching
1247
+ * on existing entities.
1248
+ *
1249
+ * A 'tilmeldColumns' migration will simply add the missing Tilmeld columns
1250
+ * and indexes to each entity table. It will **not** fill them, and you must
1251
+ * fill them before updating to the latest version of Nymph, or everyone will
1252
+ * be able to access every entity! You must use `importEntityTilmeldAC` on
1253
+ * each entity after running this.
1254
+ *
1255
+ * A 'tilmeldRemoveOldRows' migration will remove all of the old access
1256
+ * control rows in the data tables that are no longer used as of
1257
+ * 1.0.0-beta.110. MAKE SURE YOU HAVE RUN `importEntityTilmeldAC` ON ALL
1258
+ * ENTITIES BEFORE RUNNING THIS, OR THE DATA WILL BE IRRETRIEVABLY LOST!
1259
+ */
1260
+ public async liveMigration(
1261
+ migrationType: 'tokenTables' | 'tilmeldColumns' | 'tilmeldRemoveOldRows',
1262
+ ) {
1263
+ return await this.driver.liveMigration(migrationType);
1264
+ }
1265
+
1266
+ /**
1267
+ * Returns a list of the etypes stored in the DB.
1268
+ */
1269
+ public async getEtypes() {
1270
+ return await this.driver.getEtypes();
1271
+ }
1272
+
1273
+ /**
1274
+ * Get all the indexes defined for a specific etype.
1275
+ *
1276
+ * @param etype The etype to get indexes for.
1277
+ */
1278
+ public async getIndexes(etype: string) {
1279
+ return await this.driver.getIndexes(etype);
1280
+ }
1281
+
1282
+ /**
1283
+ * Add an index on a specific property to an etype.
1284
+ *
1285
+ * Nymph adds general indexes on etypes, but they span across all properties,
1286
+ * so they can only speed up a query so much. You can add indexes that are
1287
+ * specific to a property with this function, which will speed up queries for
1288
+ * that property. If an etype only has one or two properties, you may not need
1289
+ * a specific index, but if your etypes include many properties or properties
1290
+ * that generally contain large amounts of data, adding a specific index can
1291
+ * drastically improve the performance of your queries.
1292
+ *
1293
+ * The scope of the index will affect which queries it will speed up:
1294
+ *
1295
+ * - data: This will speed up comparisons and equality checks in queries.
1296
+ * - references: This will speed up ref and qref clauses in queries.
1297
+ * - tokens: This will speed up search clauses in queries.
1298
+ *
1299
+ * The name must be unique within the etype and scope or the existing index
1300
+ * will be overwritten.
1301
+ *
1302
+ * You do not need to add reference indexes for the "user" and "group"
1303
+ * properties, because these are already specifically indexed by Nymph.
1304
+ *
1305
+ * @param etype The etype to add the index on.
1306
+ * @param definition The definition of the index.
1307
+ */
1308
+ public async addIndex(
1309
+ etype: string,
1310
+ definition: {
1311
+ scope: 'data' | 'references' | 'tokens';
1312
+ name: string;
1313
+ property: string;
1314
+ },
1315
+ ) {
1316
+ this.driver.checkIndexName(definition.name);
1317
+ return await this.driver.addIndex(etype, definition);
1318
+ }
1319
+
1320
+ /**
1321
+ * Delete an index from an etype.
1322
+ *
1323
+ * @param etype The etype to delete the index on.
1324
+ * @param scope The scope of the index.
1325
+ * @param name The name of the index to delete.
1326
+ */
1327
+ public async deleteIndex(
1328
+ etype: string,
1329
+ scope: 'data' | 'references' | 'tokens',
1330
+ name: string,
1331
+ ) {
1332
+ this.driver.checkIndexName(name);
1333
+ return await this.driver.deleteIndex(etype, scope, name);
775
1334
  }
776
1335
 
777
1336
  public on<T extends NymphEventType>(
@@ -779,82 +1338,134 @@ export default class Nymph {
779
1338
  callback: T extends 'connect'
780
1339
  ? NymphConnectCallback
781
1340
  : 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
1341
+ ? NymphDisconnectCallback
1342
+ : T extends 'query'
1343
+ ? NymphQueryCallback
1344
+ : T extends 'beforeGetEntity'
1345
+ ? NymphBeforeGetEntityCallback
1346
+ : T extends 'beforeGetEntities'
1347
+ ? NymphBeforeGetEntitiesCallback
1348
+ : T extends 'beforeSaveEntity'
1349
+ ? NymphBeforeSaveEntityCallback
1350
+ : T extends 'afterSaveEntity'
1351
+ ? NymphAfterSaveEntityCallback
1352
+ : T extends 'failedSaveEntity'
1353
+ ? NymphFailedSaveEntityCallback
1354
+ : T extends 'beforeDeleteEntity'
1355
+ ? NymphBeforeDeleteEntityCallback
1356
+ : T extends 'afterDeleteEntity'
1357
+ ? NymphAfterDeleteEntityCallback
1358
+ : T extends 'failedDeleteEntity'
1359
+ ? NymphFailedDeleteEntityCallback
1360
+ : T extends 'beforeDeleteEntityByID'
1361
+ ? NymphBeforeDeleteEntityByIDCallback
1362
+ : T extends 'afterDeleteEntityByID'
1363
+ ? NymphAfterDeleteEntityByIDCallback
1364
+ : T extends 'failedDeleteEntityByID'
1365
+ ? NymphFailedDeleteEntityByIDCallback
1366
+ : T extends 'beforeNewUID'
1367
+ ? NymphBeforeNewUIDCallback
1368
+ : T extends 'afterNewUID'
1369
+ ? NymphAfterNewUIDCallback
1370
+ : T extends 'failedNewUID'
1371
+ ? NymphFailedNewUIDCallback
1372
+ : T extends 'beforeSetUID'
1373
+ ? NymphBeforeSetUIDCallback
1374
+ : T extends 'afterSetUID'
1375
+ ? NymphAfterSetUIDCallback
1376
+ : T extends 'failedSetUID'
1377
+ ? NymphFailedSetUIDCallback
1378
+ : T extends 'beforeRenameUID'
1379
+ ? NymphBeforeRenameUIDCallback
1380
+ : T extends 'afterRenameUID'
1381
+ ? NymphAfterRenameUIDCallback
1382
+ : T extends 'failedRenameUID'
1383
+ ? NymphFailedRenameUIDCallback
1384
+ : T extends 'beforeDeleteUID'
1385
+ ? NymphBeforeDeleteUIDCallback
1386
+ : T extends 'afterDeleteUID'
1387
+ ? NymphAfterDeleteUIDCallback
1388
+ : T extends 'failedDeleteUID'
1389
+ ? NymphFailedDeleteUIDCallback
1390
+ : T extends 'beforeStartTransaction'
1391
+ ? NymphBeforeStartTransactionCallback
1392
+ : T extends 'afterStartTransaction'
1393
+ ? NymphAfterStartTransactionCallback
1394
+ : T extends 'beforeCommitTransaction'
1395
+ ? NymphBeforeCommitTransactionCallback
1396
+ : T extends 'afterCommitTransaction'
1397
+ ? NymphAfterCommitTransactionCallback
1398
+ : T extends 'beforeRollbackTransaction'
1399
+ ? NymphBeforeRollbackTransactionCallback
1400
+ : T extends 'afterRollbackTransaction'
1401
+ ? NymphAfterRollbackTransactionCallback
1402
+ : never,
818
1403
  ) {
819
1404
  const prop = (event + 'Callbacks') as T extends 'connect'
820
1405
  ? 'connectCallbacks'
821
1406
  : 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;
1407
+ ? 'disconnectCallbacks'
1408
+ : T extends 'query'
1409
+ ? 'queryCallbacks'
1410
+ : T extends 'beforeGetEntity'
1411
+ ? 'beforeGetEntityCallbacks'
1412
+ : T extends 'beforeGetEntities'
1413
+ ? 'beforeGetEntitiesCallbacks'
1414
+ : T extends 'beforeSaveEntity'
1415
+ ? 'beforeSaveEntityCallbacks'
1416
+ : T extends 'afterSaveEntity'
1417
+ ? 'afterSaveEntityCallbacks'
1418
+ : T extends 'failedSaveEntity'
1419
+ ? 'failedSaveEntityCallbacks'
1420
+ : T extends 'beforeDeleteEntity'
1421
+ ? 'beforeDeleteEntityCallbacks'
1422
+ : T extends 'afterDeleteEntity'
1423
+ ? 'afterDeleteEntityCallbacks'
1424
+ : T extends 'failedDeleteEntity'
1425
+ ? 'failedDeleteEntityCallbacks'
1426
+ : T extends 'beforeDeleteEntityByID'
1427
+ ? 'beforeDeleteEntityByIDCallbacks'
1428
+ : T extends 'afterDeleteEntityByID'
1429
+ ? 'afterDeleteEntityByIDCallbacks'
1430
+ : T extends 'failedDeleteEntityByID'
1431
+ ? 'failedDeleteEntityByIDCallbacks'
1432
+ : T extends 'beforeNewUID'
1433
+ ? 'beforeNewUIDCallbacks'
1434
+ : T extends 'afterNewUID'
1435
+ ? 'afterNewUIDCallbacks'
1436
+ : T extends 'failedNewUID'
1437
+ ? 'failedNewUIDCallbacks'
1438
+ : T extends 'beforeSetUID'
1439
+ ? 'beforeSetUIDCallbacks'
1440
+ : T extends 'afterSetUID'
1441
+ ? 'afterSetUIDCallbacks'
1442
+ : T extends 'failedSetUID'
1443
+ ? 'failedSetUIDCallbacks'
1444
+ : T extends 'beforeRenameUID'
1445
+ ? 'beforeRenameUIDCallbacks'
1446
+ : T extends 'afterRenameUID'
1447
+ ? 'afterRenameUIDCallbacks'
1448
+ : T extends 'failedRenameUID'
1449
+ ? 'failedRenameUIDCallbacks'
1450
+ : T extends 'beforeDeleteUID'
1451
+ ? 'beforeDeleteUIDCallbacks'
1452
+ : T extends 'afterDeleteUID'
1453
+ ? 'afterDeleteUIDCallbacks'
1454
+ : T extends 'failedDeleteUID'
1455
+ ? 'failedDeleteUIDCallbacks'
1456
+ : T extends 'beforeStartTransaction'
1457
+ ? 'beforeStartTransactionCallbacks'
1458
+ : T extends 'afterStartTransaction'
1459
+ ? 'afterStartTransactionCallbacks'
1460
+ : T extends 'beforeCommitTransaction'
1461
+ ? 'beforeCommitTransactionCallbacks'
1462
+ : T extends 'afterCommitTransaction'
1463
+ ? 'afterCommitTransactionCallbacks'
1464
+ : T extends 'beforeRollbackTransaction'
1465
+ ? 'beforeRollbackTransactionCallbacks'
1466
+ : T extends 'afterRollbackTransaction'
1467
+ ? 'afterRollbackTransactionCallbacks'
1468
+ : never;
858
1469
  if (!(prop in this)) {
859
1470
  throw new Error('Invalid event type.');
860
1471
  }
@@ -868,82 +1479,134 @@ export default class Nymph {
868
1479
  callback: T extends 'connect'
869
1480
  ? NymphConnectCallback
870
1481
  : 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
1482
+ ? NymphDisconnectCallback
1483
+ : T extends 'query'
1484
+ ? NymphQueryCallback
1485
+ : T extends 'beforeGetEntity'
1486
+ ? NymphBeforeGetEntityCallback
1487
+ : T extends 'beforeGetEntities'
1488
+ ? NymphBeforeGetEntitiesCallback
1489
+ : T extends 'beforeSaveEntity'
1490
+ ? NymphBeforeSaveEntityCallback
1491
+ : T extends 'afterSaveEntity'
1492
+ ? NymphAfterSaveEntityCallback
1493
+ : T extends 'failedSaveEntity'
1494
+ ? NymphFailedSaveEntityCallback
1495
+ : T extends 'beforeDeleteEntity'
1496
+ ? NymphBeforeDeleteEntityCallback
1497
+ : T extends 'afterDeleteEntity'
1498
+ ? NymphAfterDeleteEntityCallback
1499
+ : T extends 'failedDeleteEntity'
1500
+ ? NymphFailedDeleteEntityCallback
1501
+ : T extends 'beforeDeleteEntityByID'
1502
+ ? NymphBeforeDeleteEntityByIDCallback
1503
+ : T extends 'afterDeleteEntityByID'
1504
+ ? NymphAfterDeleteEntityByIDCallback
1505
+ : T extends 'failedDeleteEntityByID'
1506
+ ? NymphFailedDeleteEntityByIDCallback
1507
+ : T extends 'beforeNewUID'
1508
+ ? NymphBeforeNewUIDCallback
1509
+ : T extends 'afterNewUID'
1510
+ ? NymphAfterNewUIDCallback
1511
+ : T extends 'failedNewUID'
1512
+ ? NymphFailedNewUIDCallback
1513
+ : T extends 'beforeSetUID'
1514
+ ? NymphBeforeSetUIDCallback
1515
+ : T extends 'afterSetUID'
1516
+ ? NymphAfterSetUIDCallback
1517
+ : T extends 'failedSetUID'
1518
+ ? NymphFailedSetUIDCallback
1519
+ : T extends 'beforeRenameUID'
1520
+ ? NymphBeforeRenameUIDCallback
1521
+ : T extends 'afterRenameUID'
1522
+ ? NymphAfterRenameUIDCallback
1523
+ : T extends 'failedRenameUID'
1524
+ ? NymphFailedRenameUIDCallback
1525
+ : T extends 'beforeDeleteUID'
1526
+ ? NymphBeforeDeleteUIDCallback
1527
+ : T extends 'afterDeleteUID'
1528
+ ? NymphAfterDeleteUIDCallback
1529
+ : T extends 'failedDeleteUID'
1530
+ ? NymphFailedDeleteUIDCallback
1531
+ : T extends 'beforeStartTransaction'
1532
+ ? NymphBeforeStartTransactionCallback
1533
+ : T extends 'afterStartTransaction'
1534
+ ? NymphAfterStartTransactionCallback
1535
+ : T extends 'beforeCommitTransaction'
1536
+ ? NymphBeforeCommitTransactionCallback
1537
+ : T extends 'afterCommitTransaction'
1538
+ ? NymphAfterCommitTransactionCallback
1539
+ : T extends 'beforeRollbackTransaction'
1540
+ ? NymphBeforeRollbackTransactionCallback
1541
+ : T extends 'afterRollbackTransaction'
1542
+ ? NymphAfterRollbackTransactionCallback
1543
+ : never,
907
1544
  ) {
908
1545
  const prop = (event + 'Callbacks') as T extends 'connect'
909
1546
  ? 'connectCallbacks'
910
1547
  : 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;
1548
+ ? 'disconnectCallbacks'
1549
+ : T extends 'query'
1550
+ ? 'queryCallbacks'
1551
+ : T extends 'beforeSaveEntity'
1552
+ ? 'beforeSaveEntityCallbacks'
1553
+ : T extends 'beforeGetEntity'
1554
+ ? 'beforeGetEntityCallbacks'
1555
+ : T extends 'beforeGetEntities'
1556
+ ? 'beforeGetEntitiesCallbacks'
1557
+ : T extends 'afterSaveEntity'
1558
+ ? 'afterSaveEntityCallbacks'
1559
+ : T extends 'failedSaveEntity'
1560
+ ? 'failedSaveEntityCallbacks'
1561
+ : T extends 'beforeDeleteEntity'
1562
+ ? 'beforeDeleteEntityCallbacks'
1563
+ : T extends 'afterDeleteEntity'
1564
+ ? 'afterDeleteEntityCallbacks'
1565
+ : T extends 'failedDeleteEntity'
1566
+ ? 'failedDeleteEntityCallbacks'
1567
+ : T extends 'beforeDeleteEntityByID'
1568
+ ? 'beforeDeleteEntityByIDCallbacks'
1569
+ : T extends 'afterDeleteEntityByID'
1570
+ ? 'afterDeleteEntityByIDCallbacks'
1571
+ : T extends 'failedDeleteEntityByID'
1572
+ ? 'failedDeleteEntityByIDCallbacks'
1573
+ : T extends 'beforeNewUID'
1574
+ ? 'beforeNewUIDCallbacks'
1575
+ : T extends 'afterNewUID'
1576
+ ? 'afterNewUIDCallbacks'
1577
+ : T extends 'failedNewUID'
1578
+ ? 'failedNewUIDCallbacks'
1579
+ : T extends 'beforeSetUID'
1580
+ ? 'beforeSetUIDCallbacks'
1581
+ : T extends 'afterSetUID'
1582
+ ? 'afterSetUIDCallbacks'
1583
+ : T extends 'failedSetUID'
1584
+ ? 'failedSetUIDCallbacks'
1585
+ : T extends 'beforeRenameUID'
1586
+ ? 'beforeRenameUIDCallbacks'
1587
+ : T extends 'afterRenameUID'
1588
+ ? 'afterRenameUIDCallbacks'
1589
+ : T extends 'failedRenameUID'
1590
+ ? 'failedRenameUIDCallbacks'
1591
+ : T extends 'beforeDeleteUID'
1592
+ ? 'beforeDeleteUIDCallbacks'
1593
+ : T extends 'afterDeleteUID'
1594
+ ? 'afterDeleteUIDCallbacks'
1595
+ : T extends 'failedDeleteUID'
1596
+ ? 'failedDeleteUIDCallbacks'
1597
+ : T extends 'beforeStartTransaction'
1598
+ ? 'beforeStartTransactionCallbacks'
1599
+ : T extends 'afterStartTransaction'
1600
+ ? 'afterStartTransactionCallbacks'
1601
+ : T extends 'beforeCommitTransaction'
1602
+ ? 'beforeCommitTransactionCallbacks'
1603
+ : T extends 'afterCommitTransaction'
1604
+ ? 'afterCommitTransactionCallbacks'
1605
+ : T extends 'beforeRollbackTransaction'
1606
+ ? 'beforeRollbackTransactionCallbacks'
1607
+ : T extends 'afterRollbackTransaction'
1608
+ ? 'afterRollbackTransactionCallbacks'
1609
+ : never;
947
1610
  if (!(prop in this)) {
948
1611
  return false;
949
1612
  }