@nymphjs/nymph 1.0.0-beta.1 → 1.0.0-beta.100

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 (96) hide show
  1. package/CHANGELOG.md +535 -0
  2. package/README.md +14 -22
  3. package/dist/Entity.d.ts +293 -10
  4. package/dist/Entity.js +571 -149
  5. package/dist/Entity.js.map +1 -1
  6. package/dist/Entity.test.js +67 -26
  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 +497 -13
  11. package/dist/Nymph.js +761 -188
  12. package/dist/Nymph.js.map +1 -1
  13. package/dist/Nymph.types.d.ts +95 -26
  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 +181 -43
  25. package/dist/driver/NymphDriver.js +532 -285
  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 +939 -219
  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/InvalidParametersError.js +1 -5
  46. package/dist/errors/InvalidParametersError.js.map +1 -1
  47. package/dist/errors/InvalidStateError.d.ts +3 -0
  48. package/dist/errors/InvalidStateError.js +7 -0
  49. package/dist/errors/InvalidStateError.js.map +1 -0
  50. package/dist/errors/NotConfiguredError.js +1 -5
  51. package/dist/errors/NotConfiguredError.js.map +1 -1
  52. package/dist/errors/QueryFailedError.js +2 -5
  53. package/dist/errors/QueryFailedError.js.map +1 -1
  54. package/dist/errors/UnableToConnectError.js +1 -5
  55. package/dist/errors/UnableToConnectError.js.map +1 -1
  56. package/dist/errors/index.d.ts +10 -8
  57. package/dist/errors/index.js +10 -24
  58. package/dist/errors/index.js.map +1 -1
  59. package/dist/index.d.ts +11 -11
  60. package/dist/index.js +14 -34
  61. package/dist/index.js.map +1 -1
  62. package/dist/nymphJoiProps.js +10 -15
  63. package/dist/nymphJoiProps.js.map +1 -1
  64. package/dist/testArtifacts.d.ts +19 -6
  65. package/dist/testArtifacts.js +32 -37
  66. package/dist/testArtifacts.js.map +1 -1
  67. package/dist/testMocks.d.ts +28 -11
  68. package/dist/testMocks.js +44 -28
  69. package/dist/testMocks.js.map +1 -1
  70. package/dist/utils.d.ts +4 -4
  71. package/dist/utils.js +40 -29
  72. package/dist/utils.js.map +1 -1
  73. package/jest.config.js +11 -2
  74. package/package.json +24 -20
  75. package/src/Entity.test.ts +63 -13
  76. package/src/Entity.ts +498 -156
  77. package/src/Entity.types.ts +72 -74
  78. package/src/Nymph.ts +904 -336
  79. package/src/Nymph.types.ts +179 -44
  80. package/src/conf/d.ts +25 -0
  81. package/src/conf/defaults.ts +34 -1
  82. package/src/conf/index.ts +2 -2
  83. package/src/driver/NymphDriver.ts +650 -460
  84. package/src/driver/index.ts +2 -2
  85. package/src/driver/testArtifacts.ts +966 -196
  86. package/src/errors/EntityUniqueConstraintError.ts +6 -0
  87. package/src/errors/InvalidStateError.ts +6 -0
  88. package/src/errors/index.ts +12 -8
  89. package/src/index.ts +11 -11
  90. package/src/nymphJoiProps.ts +2 -2
  91. package/src/testArtifacts.ts +20 -20
  92. package/src/testMocks.ts +85 -37
  93. package/src/type/locutus.d.ts +3 -0
  94. package/src/utils.ts +39 -18
  95. package/tsconfig.json +5 -3
  96. 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, EntityData, 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
  */
@@ -63,7 +92,7 @@ export default class Nymph {
63
92
  /**
64
93
  * The entity class for this instance of Nymph.
65
94
  */
66
- public Entity: typeof Entity = Entity;
95
+ public Entity: typeof Entity;
67
96
 
68
97
  private connectCallbacks: NymphConnectCallback[] = [];
69
98
  private disconnectCallbacks: NymphDisconnectCallback[] = [];
@@ -72,34 +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[] = [];
89
-
90
- public addEntityClass(entityClass: EntityConstructor) {
91
- this.entityClasses[entityClass.class] = entityClass;
92
- entityClass.nymph = this;
93
- }
94
-
95
- public getEntityClass(className: string): EntityConstructor {
96
- if (className in this.entityClasses) {
97
- const EntityClass = this.entityClasses[className];
98
- EntityClass.nymph = this;
99
- return EntityClass;
100
- }
101
- throw new ClassNotAvailableError('Tried to use class: ' + className);
102
- }
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
+ [];
103
138
 
104
139
  /**
105
140
  * Initialize Nymph.
@@ -107,78 +142,190 @@ export default class Nymph {
107
142
  * @param config The Nymph configuration.
108
143
  * @param driver The Nymph database driver.
109
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.
110
146
  */
111
147
  public constructor(
112
148
  config: Partial<Config>,
113
149
  driver: NymphDriver,
114
- tilmeld?: TilmeldInterface
150
+ tilmeld?: TilmeldInterface,
151
+ parent?: Nymph,
115
152
  ) {
116
153
  this.config = { ...defaults, ...config };
117
154
  this.driver = driver;
118
- if (typeof tilmeld !== 'undefined') {
119
- this.tilmeld = tilmeld;
155
+ this.parent = parent ?? null;
156
+
157
+ this.Entity = this.addEntityClass(Entity);
158
+
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
+ }
120
216
  }
121
217
 
122
- class NymphEntity<T extends EntityData = EntityData> extends Entity<T> {}
123
- NymphEntity.nymph = this;
124
- this.Entity = NymphEntity;
125
- this.addEntityClass(NymphEntity);
218
+ this.config.debugInfo('nymph', 'Nymph loaded.');
219
+
220
+ if (tilmeld != null) {
221
+ this.tilmeld = tilmeld;
222
+ }
126
223
 
127
224
  this.driver.init(this);
225
+ this.config.debugInfo('nymph', 'Nymph driver loaded.');
128
226
  if (this.tilmeld) {
129
227
  this.tilmeld.init(this);
228
+ this.config.debugInfo('nymph', 'Tilmeld loaded.');
130
229
  }
131
230
  }
132
231
 
133
232
  /**
134
- * Get a clone of this instance with duplicate classes and event listeners.
233
+ * Add your class to this instance.
135
234
  *
136
- * @returns A clone of this instance.
235
+ * This will create a class that extends your class within this instance of
236
+ * Nymph and return it. You can then use this class's constructor and methods,
237
+ * which will use this instance of Nymph.
238
+ *
239
+ * Because this creates a subclass, don't use the class
240
+ * returned from `getEntityClass` to check with `instanceof`.
137
241
  */
138
- public clone() {
139
- const nymph = new Nymph(this.config, this.driver, this.tilmeld?.clone());
140
- for (const name in this.entityClasses) {
141
- if (name === 'Entity' || (this.entityClasses[name] as any).skipOnClone) {
142
- continue;
143
- }
144
- nymph.addEntityClass(this.entityClasses[name]);
145
- }
146
-
147
- const events = [
148
- 'connect',
149
- 'disconnect',
150
- 'query',
151
- 'beforeGetEntity',
152
- 'beforeGetEntities',
153
- 'beforeSaveEntity',
154
- 'afterSaveEntity',
155
- 'beforeDeleteEntity',
156
- 'afterDeleteEntity',
157
- 'beforeDeleteEntityByID',
158
- 'afterDeleteEntityByID',
159
- 'beforeNewUID',
160
- 'afterNewUID',
161
- 'beforeSetUID',
162
- 'afterSetUID',
163
- 'beforeRenameUID',
164
- 'afterRenameUID',
165
- 'beforeDeleteUID',
166
- 'afterDeleteUID',
167
- ];
168
-
169
- for (let event of events) {
170
- const prop = event + 'Callbacks';
171
- // @ts-ignore: The callback should be the right type here.
172
- const callbacks = this[prop];
173
- for (let callback of callbacks) {
174
- if (callback.skipOnClone) {
175
- continue;
176
- }
177
- nymph.on(event as NymphEventType, callback);
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
+ );
178
260
  }
179
261
  }
180
262
 
181
- return nymph;
263
+ const nymph = this;
264
+ this.entityClasses[EntityClass.class] = class extends EntityClass {
265
+ static nymph: Nymph = nymph;
266
+
267
+ constructor(...args: any[]) {
268
+ super(...args);
269
+ }
270
+ };
271
+ return this.entityClasses[EntityClass.class] as T;
272
+ }
273
+
274
+ /**
275
+ * Get the class that uses the specified class name.
276
+ */
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;
287
+ }
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}".`);
293
+ }
294
+
295
+ /**
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.
319
+ *
320
+ * @returns A clone of this instance.
321
+ */
322
+ public clone() {
323
+ return new Nymph(
324
+ this.config,
325
+ this.driver.clone(),
326
+ this.tilmeld?.clone(),
327
+ this,
328
+ );
182
329
  }
183
330
 
184
331
  /**
@@ -187,13 +334,19 @@ export default class Nymph {
187
334
  * @returns Whether the instance is connected to the database.
188
335
  */
189
336
  public async connect(): Promise<boolean> {
190
- const result = this.driver.connect();
191
- for (let callback of this.connectCallbacks) {
192
- if (callback) {
193
- 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
+ }
194
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;
195
349
  }
196
- return await result;
197
350
  }
198
351
 
199
352
  /**
@@ -202,22 +355,25 @@ export default class Nymph {
202
355
  * @returns Whether the instance is connected to the database.
203
356
  */
204
357
  public async disconnect(): Promise<boolean> {
205
- const result = this.driver.disconnect();
206
- for (let callback of this.disconnectCallbacks) {
207
- if (callback) {
208
- 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
+ }
209
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;
210
370
  }
211
- return await result;
212
371
  }
213
372
 
214
373
  /**
215
374
  * Run all the query callbacks on a query.
216
375
  */
217
- public async runQueryCallbacks(
218
- options: Options,
219
- selectors: FormattedSelector[]
220
- ) {
376
+ public runQueryCallbacks(options: Options, selectors: FormattedSelector[]) {
221
377
  for (let callback of this.queryCallbacks) {
222
378
  if (callback) {
223
379
  callback(this, options, selectors);
@@ -235,28 +391,83 @@ export default class Nymph {
235
391
  * Transactions will nest as long as every name is unique. Internally, Nymph
236
392
  * uses names prefixed with "nymph-".
237
393
  *
238
- * @returns True on success, false on failure.
394
+ * @returns A new instance of Nymph that should be used for the transaction.
239
395
  */
240
396
  public async startTransaction(name: string): Promise<Nymph> {
241
- 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
+ }
242
415
  }
243
416
 
244
417
  /**
245
418
  * Commit the named transaction.
246
419
  *
420
+ * After this is called, the transaction instance should be discarded.
421
+ *
247
422
  * @returns True on success, false on failure.
248
423
  */
249
424
  public async commit(name: string): Promise<boolean> {
250
- 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
+ }
251
443
  }
252
444
 
253
445
  /**
254
446
  * Rollback the named transaction.
255
447
  *
448
+ * After this is called, the transaction instance should be discarded.
449
+ *
256
450
  * @returns True on success, false on failure.
257
451
  */
258
452
  public async rollback(name: string): Promise<boolean> {
259
- 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
+ }
260
471
  }
261
472
 
262
473
  /**
@@ -290,18 +501,33 @@ export default class Nymph {
290
501
  * @returns The UID's new value, or null on failure.
291
502
  */
292
503
  public async newUID(name: string): Promise<number | null> {
293
- for (let callback of this.beforeNewUIDCallbacks) {
294
- if (callback) {
295
- callback(this, name);
504
+ try {
505
+ for (let callback of this.beforeNewUIDCallbacks) {
506
+ if (callback) {
507
+ await callback(this, name);
508
+ }
296
509
  }
297
- }
298
- const result = this.driver.newUID(name);
299
- for (let callback of this.afterNewUIDCallbacks) {
300
- if (callback) {
301
- 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;
520
+ }
521
+ for (let callback of this.afterNewUIDCallbacks) {
522
+ if (callback) {
523
+ await callback(this, result);
524
+ }
302
525
  }
526
+ return await result;
527
+ } catch (e: any) {
528
+ this.config.debugError('nymph', `Failed to create UID "${name}": ${e}`);
529
+ throw e;
303
530
  }
304
- return await result;
305
531
  }
306
532
 
307
533
  /**
@@ -310,7 +536,12 @@ export default class Nymph {
310
536
  * @returns The UID's value, or null on failure and if it doesn't exist.
311
537
  */
312
538
  public async getUID(name: string): Promise<number | null> {
313
- 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
+ }
314
545
  }
315
546
 
316
547
  /**
@@ -321,18 +552,33 @@ export default class Nymph {
321
552
  * @returns True on success, false on failure.
322
553
  */
323
554
  public async setUID(name: string, value: number): Promise<boolean> {
324
- for (let callback of this.beforeSetUIDCallbacks) {
325
- if (callback) {
326
- callback(this, name, value);
555
+ try {
556
+ for (let callback of this.beforeSetUIDCallbacks) {
557
+ if (callback) {
558
+ await callback(this, name, value);
559
+ }
327
560
  }
328
- }
329
- const result = this.driver.setUID(name, value);
330
- for (let callback of this.afterSetUIDCallbacks) {
331
- if (callback) {
332
- 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
+ }
333
576
  }
577
+ return await result;
578
+ } catch (e: any) {
579
+ this.config.debugError('nymph', `Failed to set UID "${name}": ${e}`);
580
+ throw e;
334
581
  }
335
- return await result;
336
582
  }
337
583
 
338
584
  /**
@@ -342,18 +588,33 @@ export default class Nymph {
342
588
  * @returns True on success, false on failure.
343
589
  */
344
590
  public async deleteUID(name: string): Promise<boolean> {
345
- for (let callback of this.beforeDeleteUIDCallbacks) {
346
- if (callback) {
347
- callback(this, name);
591
+ try {
592
+ for (let callback of this.beforeDeleteUIDCallbacks) {
593
+ if (callback) {
594
+ await callback(this, name);
595
+ }
348
596
  }
349
- }
350
- const result = this.driver.deleteUID(name);
351
- for (let callback of this.afterDeleteUIDCallbacks) {
352
- if (callback) {
353
- 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;
354
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;
355
617
  }
356
- return await result;
357
618
  }
358
619
 
359
620
  /**
@@ -364,18 +625,36 @@ export default class Nymph {
364
625
  * @returns True on success, false on failure.
365
626
  */
366
627
  public async renameUID(oldName: string, newName: string): Promise<boolean> {
367
- for (let callback of this.beforeRenameUIDCallbacks) {
368
- if (callback) {
369
- callback(this, oldName, newName);
628
+ try {
629
+ for (let callback of this.beforeRenameUIDCallbacks) {
630
+ if (callback) {
631
+ await callback(this, oldName, newName);
632
+ }
370
633
  }
371
- }
372
- const result = this.driver.renameUID(oldName, newName);
373
- for (let callback of this.afterRenameUIDCallbacks) {
374
- if (callback) {
375
- 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;
376
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;
377
657
  }
378
- return await result;
379
658
  }
380
659
 
381
660
  /**
@@ -390,18 +669,33 @@ export default class Nymph {
390
669
  * @returns True on success, false on failure.
391
670
  */
392
671
  public async saveEntity(entity: EntityInterface): Promise<boolean> {
393
- for (let callback of this.beforeSaveEntityCallbacks) {
394
- if (callback) {
395
- callback(this, entity);
672
+ try {
673
+ for (let callback of this.beforeSaveEntityCallbacks) {
674
+ if (callback) {
675
+ await callback(this, entity);
676
+ }
396
677
  }
397
- }
398
- const result = this.driver.saveEntity(entity);
399
- for (let callback of this.afterSaveEntityCallbacks) {
400
- if (callback) {
401
- 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
+ }
402
693
  }
694
+ return await result;
695
+ } catch (e: any) {
696
+ this.config.debugError('nymph', `Failed to save entity: ${e}`);
697
+ throw e;
403
698
  }
404
- return await result;
405
699
  }
406
700
 
407
701
  /**
@@ -414,10 +708,12 @@ export default class Nymph {
414
708
  * - offset - The offset from the oldest matching entity to start retrieving.
415
709
  * - reverse - If true, entities will be retrieved from newest to oldest.
416
710
  * Therefore, offset will be from the newest entity.
417
- * - sort - How to sort the entities. Accepts "cdate" or "mdate". Defaults to
418
- * "cdate".
419
- * - return - What to return. "entity", "guid", or "count". Defaults to
420
- * "entity".
711
+ * - sort - How to sort the entities. Accepts "cdate", "mdate", or the name of
712
+ * a top-level property. The method of sorting properties other than cdate
713
+ * and mdate is driver dependent. The only hard rule is that numbers should
714
+ * be sorted numerically (2 before 10). Defaults to "cdate".
715
+ * - return - What to return. "entity", "object", "guid", or "count". Defaults
716
+ * to "entity".
421
717
  * - source - Will be 'client' if the query came from a REST call.
422
718
  * - skipCache - If true, Nymph will skip the cache and retrieve the entity
423
719
  * from the DB.
@@ -450,6 +746,11 @@ export default class Nymph {
450
746
  * - contain - An array with a name, then value. True if the named property
451
747
  * contains the value (its JSON string is found within the property's JSON
452
748
  * string).
749
+ * - search - An array with a name, then search query. True if the named
750
+ * property matches the query. Use single quotes to find sequential terms.
751
+ * Use double quotes to find exact terms. Use "or" as the or operator. Use
752
+ * "-" before a term as the negation operator. Stop words and punctuation
753
+ * are stripped. Case insensitive.
453
754
  * - match - An array with a name, then regular expression. True if the
454
755
  * named property matches. Uses POSIX RegExp. Case sensitive. Must *not* be
455
756
  * surrounded by any delimiters.
@@ -571,20 +872,37 @@ export default class Nymph {
571
872
  options: Options<T> & { return: 'guid' },
572
873
  ...selectors: Selector[]
573
874
  ): Promise<string[]>;
875
+ public async getEntities<T extends EntityConstructor = EntityConstructor>(
876
+ options: Options<T> & { return: 'object' },
877
+ ...selectors: Selector[]
878
+ ): Promise<EntityObjectType<T>[]>;
574
879
  public async getEntities<T extends EntityConstructor = EntityConstructor>(
575
880
  options?: Options<T>,
576
881
  ...selectors: Selector[]
577
- ): Promise<ReturnType<T['factorySync']>[]>;
882
+ ): Promise<EntityInstanceType<T>[]>;
578
883
  public async getEntities<T extends EntityConstructor = EntityConstructor>(
579
884
  options: Options<T> = {},
580
885
  ...selectors: Selector[]
581
- ): Promise<ReturnType<T['factorySync']>[] | string[] | number> {
582
- for (let callback of this.beforeGetEntitiesCallbacks) {
583
- if (callback) {
584
- callback(this, options, selectors);
886
+ ): Promise<
887
+ EntityInstanceType<T>[] | EntityObjectType<T>[] | string[] | number
888
+ > {
889
+ if (options.source === 'client' && options.return === 'object') {
890
+ throw new InvalidParametersError(
891
+ 'Object return type not allowed from client.',
892
+ );
893
+ }
894
+
895
+ try {
896
+ for (let callback of this.beforeGetEntitiesCallbacks) {
897
+ if (callback) {
898
+ await callback(this, options, selectors);
899
+ }
585
900
  }
901
+ return await this.driver.getEntities(options, ...selectors);
902
+ } catch (e: any) {
903
+ this.config.debugError('nymph', `Failed to get entities: ${e}`);
904
+ throw e;
586
905
  }
587
- return await this.driver.getEntities(options, ...selectors);
588
906
  }
589
907
 
590
908
  /**
@@ -608,43 +926,64 @@ export default class Nymph {
608
926
  options: Options<T> & { return: 'guid' },
609
927
  ...selectors: Selector[]
610
928
  ): Promise<string | null>;
929
+ public async getEntity<T extends EntityConstructor = EntityConstructor>(
930
+ options: Options<T> & { return: 'object' },
931
+ ...selectors: Selector[]
932
+ ): Promise<EntityObjectType<T> | null>;
611
933
  public async getEntity<T extends EntityConstructor = EntityConstructor>(
612
934
  options: Options<T>,
613
935
  ...selectors: Selector[]
614
- ): Promise<ReturnType<T['factorySync']> | null>;
936
+ ): Promise<EntityInstanceType<T> | null>;
615
937
  public async getEntity<T extends EntityConstructor = EntityConstructor>(
616
938
  options: Options<T> & { return: 'guid' },
617
- guid: string
939
+ guid: string,
618
940
  ): Promise<string | null>;
941
+ public async getEntity<T extends EntityConstructor = EntityConstructor>(
942
+ options: Options<T> & { return: 'object' },
943
+ guid: string,
944
+ ): Promise<EntityObjectType<T> | null>;
619
945
  public async getEntity<T extends EntityConstructor = EntityConstructor>(
620
946
  options: Options<T>,
621
- guid: string
622
- ): Promise<ReturnType<T['factorySync']> | null>;
947
+ guid: string,
948
+ ): Promise<EntityInstanceType<T> | null>;
623
949
  public async getEntity<T extends EntityConstructor = EntityConstructor>(
624
950
  options: Options<T> = {},
625
951
  ...selectors: Selector[] | string[]
626
- ): Promise<ReturnType<T['factorySync']> | string | number | null> {
627
- // Set up options and selectors.
628
- if (typeof selectors[0] === 'string') {
629
- selectors = [{ type: '&', guid: selectors[0] }];
952
+ ): Promise<
953
+ EntityInstanceType<T> | EntityObjectType<T> | string | number | null
954
+ > {
955
+ if (options.source === 'client' && options.return === 'object') {
956
+ throw new InvalidParametersError(
957
+ 'Object return type not allowed from client.',
958
+ );
630
959
  }
631
- options.limit = 1;
632
- for (let callback of this.beforeGetEntityCallbacks) {
633
- if (callback) {
634
- callback(this, options, selectors as Selector[]);
960
+
961
+ try {
962
+ // Set up options and selectors.
963
+ if (typeof selectors[0] === 'string') {
964
+ selectors = [{ type: '&', guid: selectors[0] }];
635
965
  }
966
+ options.limit = 1;
967
+ for (let callback of this.beforeGetEntityCallbacks) {
968
+ if (callback) {
969
+ await callback(this, options, selectors as Selector[]);
970
+ }
971
+ }
972
+ const entities = await this.driver.getEntities(
973
+ options,
974
+ ...(selectors as Selector[]),
975
+ );
976
+ if (options.return === 'count') {
977
+ return entities as unknown as number;
978
+ }
979
+ if (!entities || !entities.length) {
980
+ return null;
981
+ }
982
+ return entities[0];
983
+ } catch (e: any) {
984
+ this.config.debugError('nymph', `Failed to get entity: ${e}`);
985
+ throw e;
636
986
  }
637
- const entities = await this.driver.getEntities(
638
- options,
639
- ...(selectors as Selector[])
640
- );
641
- if (options.return === 'count') {
642
- return entities as unknown as number;
643
- }
644
- if (!entities || !entities.length) {
645
- return null;
646
- }
647
- return entities[0];
648
987
  }
649
988
 
650
989
  /**
@@ -654,18 +993,33 @@ export default class Nymph {
654
993
  * @returns True on success, false on failure.
655
994
  */
656
995
  public async deleteEntity(entity: EntityInterface): Promise<boolean> {
657
- for (let callback of this.beforeDeleteEntityCallbacks) {
658
- if (callback) {
659
- callback(this, entity);
996
+ try {
997
+ for (let callback of this.beforeDeleteEntityCallbacks) {
998
+ if (callback) {
999
+ await callback(this, entity);
1000
+ }
660
1001
  }
661
- }
662
- const result = this.driver.deleteEntity(entity);
663
- for (let callback of this.afterDeleteEntityCallbacks) {
664
- if (callback) {
665
- callback(this, result);
1002
+ let result: Promise<boolean>;
1003
+ try {
1004
+ result = this.driver.deleteEntity(entity);
1005
+ } catch (e: any) {
1006
+ for (let callback of this.failedDeleteEntityCallbacks) {
1007
+ if (callback) {
1008
+ await callback(this, e);
1009
+ }
1010
+ }
1011
+ throw e;
1012
+ }
1013
+ for (let callback of this.afterDeleteEntityCallbacks) {
1014
+ if (callback) {
1015
+ await callback(this, result);
1016
+ }
666
1017
  }
1018
+ return await result;
1019
+ } catch (e: any) {
1020
+ this.config.debugError('nymph', `Failed to delete entity: ${e}`);
1021
+ throw e;
667
1022
  }
668
- return await result;
669
1023
  }
670
1024
 
671
1025
  /**
@@ -677,20 +1031,35 @@ export default class Nymph {
677
1031
  */
678
1032
  public async deleteEntityByID(
679
1033
  guid: string,
680
- className?: string
1034
+ className?: string,
681
1035
  ): Promise<boolean> {
682
- for (let callback of this.beforeDeleteEntityByIDCallbacks) {
683
- if (callback) {
684
- callback(this, guid, className);
1036
+ try {
1037
+ for (let callback of this.beforeDeleteEntityByIDCallbacks) {
1038
+ if (callback) {
1039
+ await callback(this, guid, className);
1040
+ }
685
1041
  }
686
- }
687
- const result = this.driver.deleteEntityByID(guid, className);
688
- for (let callback of this.afterDeleteEntityByIDCallbacks) {
689
- if (callback) {
690
- callback(this, result);
1042
+ let result: Promise<boolean>;
1043
+ try {
1044
+ result = this.driver.deleteEntityByID(guid, className);
1045
+ } catch (e: any) {
1046
+ for (let callback of this.failedDeleteEntityByIDCallbacks) {
1047
+ if (callback) {
1048
+ await callback(this, e);
1049
+ }
1050
+ }
1051
+ throw e;
691
1052
  }
1053
+ for (let callback of this.afterDeleteEntityByIDCallbacks) {
1054
+ if (callback) {
1055
+ await callback(this, result);
1056
+ }
1057
+ }
1058
+ return await result;
1059
+ } catch (e: any) {
1060
+ this.config.debugError('nymph', `Failed to delete entity by ID: ${e}`);
1061
+ throw e;
692
1062
  }
693
- return await result;
694
1063
  }
695
1064
 
696
1065
  /**
@@ -715,7 +1084,7 @@ export default class Nymph {
715
1084
  * # brackets ([]).
716
1085
  * # Properties are stored like this:
717
1086
  * # propname=JSON.stringify(value)
718
- * abilities=["system/all"]
1087
+ * abilities=["system/admin"]
719
1088
  * groups=[]
720
1089
  * inheritAbilities=false
721
1090
  * name="admin"
@@ -730,7 +1099,12 @@ export default class Nymph {
730
1099
  * @returns True on success, false on failure.
731
1100
  */
732
1101
  public async export(filename: string): Promise<boolean> {
733
- return await this.driver.export(filename);
1102
+ try {
1103
+ return await this.driver.export(filename);
1104
+ } catch (e: any) {
1105
+ this.config.debugError('nymph', `Failed to export: ${e}`);
1106
+ throw e;
1107
+ }
734
1108
  }
735
1109
 
736
1110
  /**
@@ -739,7 +1113,12 @@ export default class Nymph {
739
1113
  * @returns True on success, false on failure.
740
1114
  */
741
1115
  public async exportPrint(): Promise<boolean> {
742
- return await this.driver.exportPrint();
1116
+ try {
1117
+ return await this.driver.exportPrint();
1118
+ } catch (e: any) {
1119
+ this.config.debugError('nymph', `Failed to export: ${e}`);
1120
+ throw e;
1121
+ }
743
1122
  }
744
1123
 
745
1124
  /**
@@ -749,7 +1128,92 @@ export default class Nymph {
749
1128
  * @returns True on success, false on failure.
750
1129
  */
751
1130
  public async import(filename: string): Promise<boolean> {
752
- return await this.driver.import(filename);
1131
+ try {
1132
+ return await this.driver.import(filename);
1133
+ } catch (e: any) {
1134
+ this.config.debugError('nymph', `Failed to import: ${e}`);
1135
+ throw e;
1136
+ }
1137
+ }
1138
+
1139
+ /**
1140
+ * Import a single entity.
1141
+ *
1142
+ * @param entity The entity data to import.
1143
+ */
1144
+ public async importEntity(entity: {
1145
+ guid: string;
1146
+ cdate: number;
1147
+ mdate: number;
1148
+ tags: string[];
1149
+ sdata: SerializedEntityData;
1150
+ etype: string;
1151
+ }) {
1152
+ try {
1153
+ return await this.driver.importEntity(entity);
1154
+ } catch (e: any) {
1155
+ this.config.debugError('nymph', `Failed to import: ${e}`);
1156
+ throw e;
1157
+ }
1158
+ }
1159
+
1160
+ /**
1161
+ * Import a single entity's tokens.
1162
+ *
1163
+ * This does not update any of the actual data, only the tokens used for full
1164
+ * text search.
1165
+ *
1166
+ * Be careful with this function, since you can overwrite existing tokens with
1167
+ * data that is out of sync. The DB _should_ prevent you from inserting tokens
1168
+ * for an entity that doesn't exist, but it _won't_ prevent you from inserting
1169
+ * tokens for properties that don't exist on an entity.
1170
+ *
1171
+ * @param entity The entity data to import.
1172
+ */
1173
+ public async importEntityTokens(entity: {
1174
+ guid: string;
1175
+ cdate: number;
1176
+ mdate: number;
1177
+ tags: string[];
1178
+ sdata: SerializedEntityData;
1179
+ etype: string;
1180
+ }) {
1181
+ try {
1182
+ return await this.driver.importEntityTokens(entity);
1183
+ } catch (e: any) {
1184
+ this.config.debugError('nymph', `Failed to import: ${e}`);
1185
+ throw e;
1186
+ }
1187
+ }
1188
+
1189
+ /**
1190
+ * Detect whether the database needs to be migrated.
1191
+ *
1192
+ * If not false (so, one of the string values), the database should be
1193
+ * exported with an old version of Nymph, then imported into a fresh database
1194
+ * with this version.
1195
+ *
1196
+ * 'json' means the data tables are missing the JSON field, and the DB
1197
+ * **must** be exported and imported to fix it.
1198
+ *
1199
+ * 'tokens' means the FTS token tables are missing, and you can get away with
1200
+ * a live migration with `liveMigration('tokenTables')` and doing
1201
+ * `importEntityTokens(entity)` for each entity.
1202
+ */
1203
+ public async needsMigration() {
1204
+ return await this.driver.needsMigration();
1205
+ }
1206
+
1207
+ /**
1208
+ * Perform a live migration on the DB.
1209
+ *
1210
+ * A 'tokenTables' migration will simply add the missing token tables. It will
1211
+ * **not** fill them. You must use `importEntityTokens` on each entity after
1212
+ * running this to fill the token tables and enable full text search matching
1213
+ * on existing entities.
1214
+ */
1215
+ public async liveMigration(migrationType: 'tokenTables') {
1216
+ return await this.driver.liveMigration(migrationType);
753
1217
  }
754
1218
 
755
1219
  public on<T extends NymphEventType>(
@@ -757,82 +1221,134 @@ export default class Nymph {
757
1221
  callback: T extends 'connect'
758
1222
  ? NymphConnectCallback
759
1223
  : T extends 'disconnect'
760
- ? NymphDisconnectCallback
761
- : T extends 'query'
762
- ? NymphQueryCallback
763
- : T extends 'beforeGetEntity'
764
- ? NymphBeforeGetEntityCallback
765
- : T extends 'beforeGetEntities'
766
- ? NymphBeforeGetEntitiesCallback
767
- : T extends 'beforeSaveEntity'
768
- ? NymphBeforeSaveEntityCallback
769
- : T extends 'afterSaveEntity'
770
- ? NymphAfterSaveEntityCallback
771
- : T extends 'beforeDeleteEntity'
772
- ? NymphBeforeDeleteEntityCallback
773
- : T extends 'afterDeleteEntity'
774
- ? NymphAfterDeleteEntityCallback
775
- : T extends 'beforeDeleteEntityByID'
776
- ? NymphBeforeDeleteEntityByIDCallback
777
- : T extends 'afterDeleteEntityByID'
778
- ? NymphAfterDeleteEntityByIDCallback
779
- : T extends 'beforeNewUID'
780
- ? NymphBeforeNewUIDCallback
781
- : T extends 'afterNewUID'
782
- ? NymphAfterNewUIDCallback
783
- : T extends 'beforeSetUID'
784
- ? NymphBeforeSetUIDCallback
785
- : T extends 'afterSetUID'
786
- ? NymphAfterSetUIDCallback
787
- : T extends 'beforeRenameUID'
788
- ? NymphBeforeRenameUIDCallback
789
- : T extends 'afterRenameUID'
790
- ? NymphAfterRenameUIDCallback
791
- : T extends 'beforeDeleteUID'
792
- ? NymphBeforeDeleteUIDCallback
793
- : T extends 'afterDeleteUID'
794
- ? NymphAfterDeleteUIDCallback
795
- : never
1224
+ ? NymphDisconnectCallback
1225
+ : T extends 'query'
1226
+ ? NymphQueryCallback
1227
+ : T extends 'beforeGetEntity'
1228
+ ? NymphBeforeGetEntityCallback
1229
+ : T extends 'beforeGetEntities'
1230
+ ? NymphBeforeGetEntitiesCallback
1231
+ : T extends 'beforeSaveEntity'
1232
+ ? NymphBeforeSaveEntityCallback
1233
+ : T extends 'afterSaveEntity'
1234
+ ? NymphAfterSaveEntityCallback
1235
+ : T extends 'failedSaveEntity'
1236
+ ? NymphFailedSaveEntityCallback
1237
+ : T extends 'beforeDeleteEntity'
1238
+ ? NymphBeforeDeleteEntityCallback
1239
+ : T extends 'afterDeleteEntity'
1240
+ ? NymphAfterDeleteEntityCallback
1241
+ : T extends 'failedDeleteEntity'
1242
+ ? NymphFailedDeleteEntityCallback
1243
+ : T extends 'beforeDeleteEntityByID'
1244
+ ? NymphBeforeDeleteEntityByIDCallback
1245
+ : T extends 'afterDeleteEntityByID'
1246
+ ? NymphAfterDeleteEntityByIDCallback
1247
+ : T extends 'failedDeleteEntityByID'
1248
+ ? NymphFailedDeleteEntityByIDCallback
1249
+ : T extends 'beforeNewUID'
1250
+ ? NymphBeforeNewUIDCallback
1251
+ : T extends 'afterNewUID'
1252
+ ? NymphAfterNewUIDCallback
1253
+ : T extends 'failedNewUID'
1254
+ ? NymphFailedNewUIDCallback
1255
+ : T extends 'beforeSetUID'
1256
+ ? NymphBeforeSetUIDCallback
1257
+ : T extends 'afterSetUID'
1258
+ ? NymphAfterSetUIDCallback
1259
+ : T extends 'failedSetUID'
1260
+ ? NymphFailedSetUIDCallback
1261
+ : T extends 'beforeRenameUID'
1262
+ ? NymphBeforeRenameUIDCallback
1263
+ : T extends 'afterRenameUID'
1264
+ ? NymphAfterRenameUIDCallback
1265
+ : T extends 'failedRenameUID'
1266
+ ? NymphFailedRenameUIDCallback
1267
+ : T extends 'beforeDeleteUID'
1268
+ ? NymphBeforeDeleteUIDCallback
1269
+ : T extends 'afterDeleteUID'
1270
+ ? NymphAfterDeleteUIDCallback
1271
+ : T extends 'failedDeleteUID'
1272
+ ? NymphFailedDeleteUIDCallback
1273
+ : T extends 'beforeStartTransaction'
1274
+ ? NymphBeforeStartTransactionCallback
1275
+ : T extends 'afterStartTransaction'
1276
+ ? NymphAfterStartTransactionCallback
1277
+ : T extends 'beforeCommitTransaction'
1278
+ ? NymphBeforeCommitTransactionCallback
1279
+ : T extends 'afterCommitTransaction'
1280
+ ? NymphAfterCommitTransactionCallback
1281
+ : T extends 'beforeRollbackTransaction'
1282
+ ? NymphBeforeRollbackTransactionCallback
1283
+ : T extends 'afterRollbackTransaction'
1284
+ ? NymphAfterRollbackTransactionCallback
1285
+ : never,
796
1286
  ) {
797
1287
  const prop = (event + 'Callbacks') as T extends 'connect'
798
1288
  ? 'connectCallbacks'
799
1289
  : T extends 'disconnect'
800
- ? 'disconnectCallbacks'
801
- : T extends 'query'
802
- ? 'queryCallbacks'
803
- : T extends 'beforeGetEntity'
804
- ? 'beforeGetEntityCallbacks'
805
- : T extends 'beforeGetEntities'
806
- ? 'beforeGetEntitiesCallbacks'
807
- : T extends 'beforeSaveEntity'
808
- ? 'beforeSaveEntityCallbacks'
809
- : T extends 'afterSaveEntity'
810
- ? 'afterSaveEntityCallbacks'
811
- : T extends 'beforeDeleteEntity'
812
- ? 'beforeDeleteEntityCallbacks'
813
- : T extends 'afterDeleteEntity'
814
- ? 'afterDeleteEntityCallbacks'
815
- : T extends 'beforeDeleteEntityByID'
816
- ? 'beforeDeleteEntityByIDCallbacks'
817
- : T extends 'afterDeleteEntityByID'
818
- ? 'afterDeleteEntityByIDCallbacks'
819
- : T extends 'beforeNewUID'
820
- ? 'beforeNewUIDCallbacks'
821
- : T extends 'afterNewUID'
822
- ? 'afterNewUIDCallbacks'
823
- : T extends 'beforeSetUID'
824
- ? 'beforeSetUIDCallbacks'
825
- : T extends 'afterSetUID'
826
- ? 'afterSetUIDCallbacks'
827
- : T extends 'beforeRenameUID'
828
- ? 'beforeRenameUIDCallbacks'
829
- : T extends 'afterRenameUID'
830
- ? 'afterRenameUIDCallbacks'
831
- : T extends 'beforeDeleteUID'
832
- ? 'beforeDeleteUIDCallbacks'
833
- : T extends 'afterDeleteUID'
834
- ? 'afterDeleteUIDCallbacks'
835
- : never;
1290
+ ? 'disconnectCallbacks'
1291
+ : T extends 'query'
1292
+ ? 'queryCallbacks'
1293
+ : T extends 'beforeGetEntity'
1294
+ ? 'beforeGetEntityCallbacks'
1295
+ : T extends 'beforeGetEntities'
1296
+ ? 'beforeGetEntitiesCallbacks'
1297
+ : T extends 'beforeSaveEntity'
1298
+ ? 'beforeSaveEntityCallbacks'
1299
+ : T extends 'afterSaveEntity'
1300
+ ? 'afterSaveEntityCallbacks'
1301
+ : T extends 'failedSaveEntity'
1302
+ ? 'failedSaveEntityCallbacks'
1303
+ : T extends 'beforeDeleteEntity'
1304
+ ? 'beforeDeleteEntityCallbacks'
1305
+ : T extends 'afterDeleteEntity'
1306
+ ? 'afterDeleteEntityCallbacks'
1307
+ : T extends 'failedDeleteEntity'
1308
+ ? 'failedDeleteEntityCallbacks'
1309
+ : T extends 'beforeDeleteEntityByID'
1310
+ ? 'beforeDeleteEntityByIDCallbacks'
1311
+ : T extends 'afterDeleteEntityByID'
1312
+ ? 'afterDeleteEntityByIDCallbacks'
1313
+ : T extends 'failedDeleteEntityByID'
1314
+ ? 'failedDeleteEntityByIDCallbacks'
1315
+ : T extends 'beforeNewUID'
1316
+ ? 'beforeNewUIDCallbacks'
1317
+ : T extends 'afterNewUID'
1318
+ ? 'afterNewUIDCallbacks'
1319
+ : T extends 'failedNewUID'
1320
+ ? 'failedNewUIDCallbacks'
1321
+ : T extends 'beforeSetUID'
1322
+ ? 'beforeSetUIDCallbacks'
1323
+ : T extends 'afterSetUID'
1324
+ ? 'afterSetUIDCallbacks'
1325
+ : T extends 'failedSetUID'
1326
+ ? 'failedSetUIDCallbacks'
1327
+ : T extends 'beforeRenameUID'
1328
+ ? 'beforeRenameUIDCallbacks'
1329
+ : T extends 'afterRenameUID'
1330
+ ? 'afterRenameUIDCallbacks'
1331
+ : T extends 'failedRenameUID'
1332
+ ? 'failedRenameUIDCallbacks'
1333
+ : T extends 'beforeDeleteUID'
1334
+ ? 'beforeDeleteUIDCallbacks'
1335
+ : T extends 'afterDeleteUID'
1336
+ ? 'afterDeleteUIDCallbacks'
1337
+ : T extends 'failedDeleteUID'
1338
+ ? 'failedDeleteUIDCallbacks'
1339
+ : T extends 'beforeStartTransaction'
1340
+ ? 'beforeStartTransactionCallbacks'
1341
+ : T extends 'afterStartTransaction'
1342
+ ? 'afterStartTransactionCallbacks'
1343
+ : T extends 'beforeCommitTransaction'
1344
+ ? 'beforeCommitTransactionCallbacks'
1345
+ : T extends 'afterCommitTransaction'
1346
+ ? 'afterCommitTransactionCallbacks'
1347
+ : T extends 'beforeRollbackTransaction'
1348
+ ? 'beforeRollbackTransactionCallbacks'
1349
+ : T extends 'afterRollbackTransaction'
1350
+ ? 'afterRollbackTransactionCallbacks'
1351
+ : never;
836
1352
  if (!(prop in this)) {
837
1353
  throw new Error('Invalid event type.');
838
1354
  }
@@ -846,82 +1362,134 @@ export default class Nymph {
846
1362
  callback: T extends 'connect'
847
1363
  ? NymphConnectCallback
848
1364
  : T extends 'disconnect'
849
- ? NymphDisconnectCallback
850
- : T extends 'query'
851
- ? NymphQueryCallback
852
- : T extends 'beforeGetEntity'
853
- ? NymphBeforeGetEntityCallback
854
- : T extends 'beforeGetEntities'
855
- ? NymphBeforeGetEntitiesCallback
856
- : T extends 'beforeSaveEntity'
857
- ? NymphBeforeSaveEntityCallback
858
- : T extends 'afterSaveEntity'
859
- ? NymphAfterSaveEntityCallback
860
- : T extends 'beforeDeleteEntity'
861
- ? NymphBeforeDeleteEntityCallback
862
- : T extends 'afterDeleteEntity'
863
- ? NymphAfterDeleteEntityCallback
864
- : T extends 'beforeDeleteEntityByID'
865
- ? NymphBeforeDeleteEntityByIDCallback
866
- : T extends 'afterDeleteEntityByID'
867
- ? NymphAfterDeleteEntityByIDCallback
868
- : T extends 'beforeNewUID'
869
- ? NymphBeforeNewUIDCallback
870
- : T extends 'afterNewUID'
871
- ? NymphAfterNewUIDCallback
872
- : T extends 'beforeSetUID'
873
- ? NymphBeforeSetUIDCallback
874
- : T extends 'afterSetUID'
875
- ? NymphAfterSetUIDCallback
876
- : T extends 'beforeRenameUID'
877
- ? NymphBeforeRenameUIDCallback
878
- : T extends 'afterRenameUID'
879
- ? NymphAfterRenameUIDCallback
880
- : T extends 'beforeDeleteUID'
881
- ? NymphBeforeDeleteUIDCallback
882
- : T extends 'afterDeleteUID'
883
- ? NymphAfterDeleteUIDCallback
884
- : never
1365
+ ? NymphDisconnectCallback
1366
+ : T extends 'query'
1367
+ ? NymphQueryCallback
1368
+ : T extends 'beforeGetEntity'
1369
+ ? NymphBeforeGetEntityCallback
1370
+ : T extends 'beforeGetEntities'
1371
+ ? NymphBeforeGetEntitiesCallback
1372
+ : T extends 'beforeSaveEntity'
1373
+ ? NymphBeforeSaveEntityCallback
1374
+ : T extends 'afterSaveEntity'
1375
+ ? NymphAfterSaveEntityCallback
1376
+ : T extends 'failedSaveEntity'
1377
+ ? NymphFailedSaveEntityCallback
1378
+ : T extends 'beforeDeleteEntity'
1379
+ ? NymphBeforeDeleteEntityCallback
1380
+ : T extends 'afterDeleteEntity'
1381
+ ? NymphAfterDeleteEntityCallback
1382
+ : T extends 'failedDeleteEntity'
1383
+ ? NymphFailedDeleteEntityCallback
1384
+ : T extends 'beforeDeleteEntityByID'
1385
+ ? NymphBeforeDeleteEntityByIDCallback
1386
+ : T extends 'afterDeleteEntityByID'
1387
+ ? NymphAfterDeleteEntityByIDCallback
1388
+ : T extends 'failedDeleteEntityByID'
1389
+ ? NymphFailedDeleteEntityByIDCallback
1390
+ : T extends 'beforeNewUID'
1391
+ ? NymphBeforeNewUIDCallback
1392
+ : T extends 'afterNewUID'
1393
+ ? NymphAfterNewUIDCallback
1394
+ : T extends 'failedNewUID'
1395
+ ? NymphFailedNewUIDCallback
1396
+ : T extends 'beforeSetUID'
1397
+ ? NymphBeforeSetUIDCallback
1398
+ : T extends 'afterSetUID'
1399
+ ? NymphAfterSetUIDCallback
1400
+ : T extends 'failedSetUID'
1401
+ ? NymphFailedSetUIDCallback
1402
+ : T extends 'beforeRenameUID'
1403
+ ? NymphBeforeRenameUIDCallback
1404
+ : T extends 'afterRenameUID'
1405
+ ? NymphAfterRenameUIDCallback
1406
+ : T extends 'failedRenameUID'
1407
+ ? NymphFailedRenameUIDCallback
1408
+ : T extends 'beforeDeleteUID'
1409
+ ? NymphBeforeDeleteUIDCallback
1410
+ : T extends 'afterDeleteUID'
1411
+ ? NymphAfterDeleteUIDCallback
1412
+ : T extends 'failedDeleteUID'
1413
+ ? NymphFailedDeleteUIDCallback
1414
+ : T extends 'beforeStartTransaction'
1415
+ ? NymphBeforeStartTransactionCallback
1416
+ : T extends 'afterStartTransaction'
1417
+ ? NymphAfterStartTransactionCallback
1418
+ : T extends 'beforeCommitTransaction'
1419
+ ? NymphBeforeCommitTransactionCallback
1420
+ : T extends 'afterCommitTransaction'
1421
+ ? NymphAfterCommitTransactionCallback
1422
+ : T extends 'beforeRollbackTransaction'
1423
+ ? NymphBeforeRollbackTransactionCallback
1424
+ : T extends 'afterRollbackTransaction'
1425
+ ? NymphAfterRollbackTransactionCallback
1426
+ : never,
885
1427
  ) {
886
1428
  const prop = (event + 'Callbacks') as T extends 'connect'
887
1429
  ? 'connectCallbacks'
888
1430
  : T extends 'disconnect'
889
- ? 'disconnectCallbacks'
890
- : T extends 'query'
891
- ? 'queryCallbacks'
892
- : T extends 'beforeSaveEntity'
893
- ? 'beforeSaveEntityCallbacks'
894
- : T extends 'beforeGetEntity'
895
- ? 'beforeGetEntityCallbacks'
896
- : T extends 'beforeGetEntities'
897
- ? 'beforeGetEntitiesCallbacks'
898
- : T extends 'afterSaveEntity'
899
- ? 'afterSaveEntityCallbacks'
900
- : T extends 'beforeDeleteEntity'
901
- ? 'beforeDeleteEntityCallbacks'
902
- : T extends 'afterDeleteEntity'
903
- ? 'afterDeleteEntityCallbacks'
904
- : T extends 'beforeDeleteEntityByID'
905
- ? 'beforeDeleteEntityByIDCallbacks'
906
- : T extends 'afterDeleteEntityByID'
907
- ? 'afterDeleteEntityByIDCallbacks'
908
- : T extends 'beforeNewUID'
909
- ? 'beforeNewUIDCallbacks'
910
- : T extends 'afterNewUID'
911
- ? 'afterNewUIDCallbacks'
912
- : T extends 'beforeSetUID'
913
- ? 'beforeSetUIDCallbacks'
914
- : T extends 'afterSetUID'
915
- ? 'afterSetUIDCallbacks'
916
- : T extends 'beforeRenameUID'
917
- ? 'beforeRenameUIDCallbacks'
918
- : T extends 'afterRenameUID'
919
- ? 'afterRenameUIDCallbacks'
920
- : T extends 'beforeDeleteUID'
921
- ? 'beforeDeleteUIDCallbacks'
922
- : T extends 'afterDeleteUID'
923
- ? 'afterDeleteUIDCallbacks'
924
- : never;
1431
+ ? 'disconnectCallbacks'
1432
+ : T extends 'query'
1433
+ ? 'queryCallbacks'
1434
+ : T extends 'beforeSaveEntity'
1435
+ ? 'beforeSaveEntityCallbacks'
1436
+ : T extends 'beforeGetEntity'
1437
+ ? 'beforeGetEntityCallbacks'
1438
+ : T extends 'beforeGetEntities'
1439
+ ? 'beforeGetEntitiesCallbacks'
1440
+ : T extends 'afterSaveEntity'
1441
+ ? 'afterSaveEntityCallbacks'
1442
+ : T extends 'failedSaveEntity'
1443
+ ? 'failedSaveEntityCallbacks'
1444
+ : T extends 'beforeDeleteEntity'
1445
+ ? 'beforeDeleteEntityCallbacks'
1446
+ : T extends 'afterDeleteEntity'
1447
+ ? 'afterDeleteEntityCallbacks'
1448
+ : T extends 'failedDeleteEntity'
1449
+ ? 'failedDeleteEntityCallbacks'
1450
+ : T extends 'beforeDeleteEntityByID'
1451
+ ? 'beforeDeleteEntityByIDCallbacks'
1452
+ : T extends 'afterDeleteEntityByID'
1453
+ ? 'afterDeleteEntityByIDCallbacks'
1454
+ : T extends 'failedDeleteEntityByID'
1455
+ ? 'failedDeleteEntityByIDCallbacks'
1456
+ : T extends 'beforeNewUID'
1457
+ ? 'beforeNewUIDCallbacks'
1458
+ : T extends 'afterNewUID'
1459
+ ? 'afterNewUIDCallbacks'
1460
+ : T extends 'failedNewUID'
1461
+ ? 'failedNewUIDCallbacks'
1462
+ : T extends 'beforeSetUID'
1463
+ ? 'beforeSetUIDCallbacks'
1464
+ : T extends 'afterSetUID'
1465
+ ? 'afterSetUIDCallbacks'
1466
+ : T extends 'failedSetUID'
1467
+ ? 'failedSetUIDCallbacks'
1468
+ : T extends 'beforeRenameUID'
1469
+ ? 'beforeRenameUIDCallbacks'
1470
+ : T extends 'afterRenameUID'
1471
+ ? 'afterRenameUIDCallbacks'
1472
+ : T extends 'failedRenameUID'
1473
+ ? 'failedRenameUIDCallbacks'
1474
+ : T extends 'beforeDeleteUID'
1475
+ ? 'beforeDeleteUIDCallbacks'
1476
+ : T extends 'afterDeleteUID'
1477
+ ? 'afterDeleteUIDCallbacks'
1478
+ : T extends 'failedDeleteUID'
1479
+ ? 'failedDeleteUIDCallbacks'
1480
+ : T extends 'beforeStartTransaction'
1481
+ ? 'beforeStartTransactionCallbacks'
1482
+ : T extends 'afterStartTransaction'
1483
+ ? 'afterStartTransactionCallbacks'
1484
+ : T extends 'beforeCommitTransaction'
1485
+ ? 'beforeCommitTransactionCallbacks'
1486
+ : T extends 'afterCommitTransaction'
1487
+ ? 'afterCommitTransactionCallbacks'
1488
+ : T extends 'beforeRollbackTransaction'
1489
+ ? 'beforeRollbackTransactionCallbacks'
1490
+ : T extends 'afterRollbackTransaction'
1491
+ ? 'afterRollbackTransactionCallbacks'
1492
+ : never;
925
1493
  if (!(prop in this)) {
926
1494
  return false;
927
1495
  }