@nymphjs/nymph 1.0.0-beta.13 → 1.0.0-beta.130

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 (123) hide show
  1. package/CHANGELOG.md +644 -0
  2. package/README.md +34 -24
  3. package/dist/Entity.d.ts +304 -15
  4. package/dist/Entity.js +609 -154
  5. package/dist/Entity.js.map +1 -1
  6. package/dist/Entity.test.js +75 -29
  7. package/dist/Entity.test.js.map +1 -1
  8. package/dist/Entity.types.d.ts +275 -28
  9. package/dist/Entity.types.js +1 -2
  10. package/dist/Nymph.d.ts +575 -11
  11. package/dist/Nymph.js +825 -288
  12. package/dist/Nymph.js.map +1 -1
  13. package/dist/Nymph.types.d.ts +117 -7
  14. package/dist/Nymph.types.js +47 -5
  15. package/dist/Nymph.types.js.map +1 -1
  16. package/dist/conf/defaults.d.ts +1 -1
  17. package/dist/conf/defaults.js +26 -3
  18. package/dist/conf/defaults.js.map +1 -1
  19. package/dist/conf/index.d.ts +50 -2
  20. package/dist/conf/index.js +2 -8
  21. package/dist/conf/index.js.map +1 -1
  22. package/dist/decorators/index.d.ts +1 -0
  23. package/dist/decorators/index.js +2 -0
  24. package/dist/decorators/index.js.map +1 -0
  25. package/dist/decorators/transactional.d.ts +12 -0
  26. package/dist/decorators/transactional.js +45 -0
  27. package/dist/decorators/transactional.js.map +1 -0
  28. package/dist/driver/NymphDriver.d.ts +209 -41
  29. package/dist/driver/NymphDriver.js +737 -242
  30. package/dist/driver/NymphDriver.js.map +1 -1
  31. package/dist/driver/index.d.ts +1 -2
  32. package/dist/driver/index.js +2 -23
  33. package/dist/driver/index.js.map +1 -1
  34. package/dist/driver/testArtifacts.d.ts +4 -4
  35. package/dist/driver/testArtifacts.js +2016 -1086
  36. package/dist/driver/testArtifacts.js.map +1 -1
  37. package/dist/errors/ClassNotAvailableError.js +1 -5
  38. package/dist/errors/ClassNotAvailableError.js.map +1 -1
  39. package/dist/errors/EntityConflictError.js +1 -5
  40. package/dist/errors/EntityConflictError.js.map +1 -1
  41. package/dist/errors/EntityInvalidDataError.d.ts +6 -0
  42. package/dist/errors/EntityInvalidDataError.js +8 -6
  43. package/dist/errors/EntityInvalidDataError.js.map +1 -1
  44. package/dist/errors/EntityIsSleepingReferenceError.js +1 -5
  45. package/dist/errors/EntityIsSleepingReferenceError.js.map +1 -1
  46. package/dist/errors/EntityUniqueConstraintError.d.ts +3 -0
  47. package/dist/errors/EntityUniqueConstraintError.js +7 -0
  48. package/dist/errors/EntityUniqueConstraintError.js.map +1 -0
  49. package/dist/errors/HttpError.d.ts +5 -0
  50. package/dist/errors/HttpError.js +11 -0
  51. package/dist/errors/HttpError.js.map +1 -0
  52. package/dist/errors/InvalidParametersError.js +1 -5
  53. package/dist/errors/InvalidParametersError.js.map +1 -1
  54. package/dist/errors/InvalidStateError.d.ts +3 -0
  55. package/dist/errors/InvalidStateError.js +7 -0
  56. package/dist/errors/InvalidStateError.js.map +1 -0
  57. package/dist/errors/MethodFailedError.d.ts +3 -0
  58. package/dist/errors/MethodFailedError.js +7 -0
  59. package/dist/errors/MethodFailedError.js.map +1 -0
  60. package/dist/errors/NotConfiguredError.js +1 -5
  61. package/dist/errors/NotConfiguredError.js.map +1 -1
  62. package/dist/errors/QueryFailedError.d.ts +2 -1
  63. package/dist/errors/QueryFailedError.js +5 -6
  64. package/dist/errors/QueryFailedError.js.map +1 -1
  65. package/dist/errors/UnableToConnectError.js +1 -5
  66. package/dist/errors/UnableToConnectError.js.map +1 -1
  67. package/dist/errors/index.d.ts +12 -8
  68. package/dist/errors/index.js +12 -24
  69. package/dist/errors/index.js.map +1 -1
  70. package/dist/helpers/index.d.ts +1 -0
  71. package/dist/helpers/index.js +2 -0
  72. package/dist/helpers/index.js.map +1 -0
  73. package/dist/helpers/transaction.d.ts +15 -0
  74. package/dist/helpers/transaction.js +47 -0
  75. package/dist/helpers/transaction.js.map +1 -0
  76. package/dist/index.d.ts +13 -11
  77. package/dist/index.js +16 -34
  78. package/dist/index.js.map +1 -1
  79. package/dist/nymphJoiProps.d.ts +1 -1
  80. package/dist/nymphJoiProps.js +10 -15
  81. package/dist/nymphJoiProps.js.map +1 -1
  82. package/dist/testArtifacts.d.ts +21 -6
  83. package/dist/testArtifacts.js +124 -50
  84. package/dist/testArtifacts.js.map +1 -1
  85. package/dist/testMocks.d.ts +27 -12
  86. package/dist/testMocks.js +44 -33
  87. package/dist/testMocks.js.map +1 -1
  88. package/dist/utils.d.ts +5 -5
  89. package/dist/utils.js +106 -68
  90. package/dist/utils.js.map +1 -1
  91. package/jest.config.js +31 -5
  92. package/package.json +43 -24
  93. package/src/Entity.test.ts +73 -23
  94. package/src/Entity.ts +555 -169
  95. package/src/Entity.types.ts +102 -90
  96. package/src/Nymph.ts +930 -517
  97. package/src/Nymph.types.ts +145 -39
  98. package/src/conf/defaults.ts +34 -1
  99. package/src/conf/index.ts +50 -2
  100. package/src/decorators/index.ts +1 -0
  101. package/src/decorators/transactional.ts +63 -0
  102. package/src/driver/NymphDriver.ts +987 -417
  103. package/src/driver/index.ts +1 -3
  104. package/src/driver/testArtifacts.ts +2699 -1628
  105. package/src/errors/EntityUniqueConstraintError.ts +6 -0
  106. package/src/errors/HttpError.ts +12 -0
  107. package/src/errors/InvalidStateError.ts +6 -0
  108. package/src/errors/MethodFailedError.ts +6 -0
  109. package/src/errors/QueryFailedError.ts +3 -1
  110. package/src/errors/index.ts +16 -8
  111. package/src/helpers/index.ts +1 -0
  112. package/src/helpers/transaction.ts +65 -0
  113. package/src/index.ts +13 -11
  114. package/src/nymphJoiProps.ts +2 -2
  115. package/src/testArtifacts.ts +69 -20
  116. package/src/testMocks.ts +107 -41
  117. package/src/utils.ts +102 -67
  118. package/tsconfig.json +6 -3
  119. package/typedoc.json +4 -0
  120. package/dist/conf/d.d.ts +0 -6
  121. package/dist/conf/d.js +0 -3
  122. package/dist/conf/d.js.map +0 -1
  123. package/src/conf/d.ts +0 -24
package/dist/Nymph.js CHANGED
@@ -1,149 +1,264 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const conf_1 = require("./conf");
7
- const Entity_1 = __importDefault(require("./Entity"));
8
- const errors_1 = require("./errors");
9
- class Nymph {
10
- constructor(config, driver, tilmeld) {
11
- this.tilmeld = undefined;
12
- this.entityClasses = {};
13
- this.connectCallbacks = [];
14
- this.disconnectCallbacks = [];
15
- this.queryCallbacks = [];
16
- this.beforeGetEntityCallbacks = [];
17
- this.beforeGetEntitiesCallbacks = [];
18
- this.beforeSaveEntityCallbacks = [];
19
- this.afterSaveEntityCallbacks = [];
20
- this.failedSaveEntityCallbacks = [];
21
- this.beforeDeleteEntityCallbacks = [];
22
- this.afterDeleteEntityCallbacks = [];
23
- this.failedDeleteEntityCallbacks = [];
24
- this.beforeDeleteEntityByIDCallbacks = [];
25
- this.afterDeleteEntityByIDCallbacks = [];
26
- this.failedDeleteEntityByIDCallbacks = [];
27
- this.beforeNewUIDCallbacks = [];
28
- this.afterNewUIDCallbacks = [];
29
- this.failedNewUIDCallbacks = [];
30
- this.beforeSetUIDCallbacks = [];
31
- this.afterSetUIDCallbacks = [];
32
- this.failedSetUIDCallbacks = [];
33
- this.beforeRenameUIDCallbacks = [];
34
- this.afterRenameUIDCallbacks = [];
35
- this.failedRenameUIDCallbacks = [];
36
- this.beforeDeleteUIDCallbacks = [];
37
- this.afterDeleteUIDCallbacks = [];
38
- this.failedDeleteUIDCallbacks = [];
39
- this.beforeStartTransactionCallbacks = [];
40
- this.afterStartTransactionCallbacks = [];
41
- this.beforeCommitTransactionCallbacks = [];
42
- this.afterCommitTransactionCallbacks = [];
43
- this.beforeRollbackTransactionCallbacks = [];
44
- this.afterRollbackTransactionCallbacks = [];
45
- this.config = { ...conf_1.ConfigDefaults, ...config };
1
+ import { ConfigDefaults as defaults } from './conf/index.js';
2
+ import Entity from './Entity.js';
3
+ import { ClassNotAvailableError, InvalidParametersError, } from './errors/index.js';
4
+ /**
5
+ * An object relational mapper for Node.js.
6
+ *
7
+ * Written by Hunter Perrin for SciActive.
8
+ *
9
+ * @author Hunter Perrin <hperrin@port87.com>
10
+ * @copyright SciActive Inc
11
+ * @see http://nymph.io/
12
+ */
13
+ export default class Nymph {
14
+ /**
15
+ * The Nymph config.
16
+ */
17
+ config;
18
+ /**
19
+ * The Nymph instance that this one was cloned from, or null if it's not a
20
+ * clone.
21
+ */
22
+ parent;
23
+ /**
24
+ * The Nymph database driver.
25
+ */
26
+ driver;
27
+ /**
28
+ * An optional Tilmeld user/group manager instance.
29
+ */
30
+ tilmeld = undefined;
31
+ /**
32
+ * A simple map of names to Entity classes.
33
+ */
34
+ entityClasses = {};
35
+ /**
36
+ * The entity class for this instance of Nymph.
37
+ */
38
+ Entity;
39
+ connectCallbacks = [];
40
+ disconnectCallbacks = [];
41
+ queryCallbacks = [];
42
+ beforeGetEntityCallbacks = [];
43
+ beforeGetEntitiesCallbacks = [];
44
+ beforeSaveEntityCallbacks = [];
45
+ afterSaveEntityCallbacks = [];
46
+ failedSaveEntityCallbacks = [];
47
+ beforeDeleteEntityCallbacks = [];
48
+ afterDeleteEntityCallbacks = [];
49
+ failedDeleteEntityCallbacks = [];
50
+ beforeDeleteEntityByIDCallbacks = [];
51
+ afterDeleteEntityByIDCallbacks = [];
52
+ failedDeleteEntityByIDCallbacks = [];
53
+ beforeNewUIDCallbacks = [];
54
+ afterNewUIDCallbacks = [];
55
+ failedNewUIDCallbacks = [];
56
+ beforeSetUIDCallbacks = [];
57
+ afterSetUIDCallbacks = [];
58
+ failedSetUIDCallbacks = [];
59
+ beforeRenameUIDCallbacks = [];
60
+ afterRenameUIDCallbacks = [];
61
+ failedRenameUIDCallbacks = [];
62
+ beforeDeleteUIDCallbacks = [];
63
+ afterDeleteUIDCallbacks = [];
64
+ failedDeleteUIDCallbacks = [];
65
+ beforeStartTransactionCallbacks = [];
66
+ afterStartTransactionCallbacks = [];
67
+ beforeCommitTransactionCallbacks = [];
68
+ afterCommitTransactionCallbacks = [];
69
+ beforeRollbackTransactionCallbacks = [];
70
+ afterRollbackTransactionCallbacks = [];
71
+ /**
72
+ * Initialize Nymph.
73
+ *
74
+ * @param config The Nymph configuration.
75
+ * @param driver The Nymph database driver.
76
+ * @param tilmeld The Tilmeld user/group manager instance, if you want to use it.
77
+ * @param parent Used internally by Nymph. Don't set this.
78
+ */
79
+ constructor(config, driver, tilmeld, parent) {
80
+ this.config = { ...defaults, ...config };
46
81
  this.driver = driver;
47
- this.parent = null;
48
- this.Entity = this.addEntityClass(Entity_1.default);
49
- if (typeof tilmeld !== 'undefined') {
82
+ this.parent = parent ?? null;
83
+ this.Entity = this.addEntityClass(Entity);
84
+ if (parent) {
85
+ for (const name in parent.entityClasses) {
86
+ if (name === 'Entity' ||
87
+ parent.entityClasses[name].skipOnClone) {
88
+ continue;
89
+ }
90
+ this.addEntityClass(parent.entityClasses[name]);
91
+ }
92
+ const events = [
93
+ 'connect',
94
+ 'disconnect',
95
+ 'query',
96
+ 'beforeGetEntity',
97
+ 'beforeGetEntities',
98
+ 'beforeSaveEntity',
99
+ 'afterSaveEntity',
100
+ 'failedSaveEntity',
101
+ 'beforeDeleteEntity',
102
+ 'afterDeleteEntity',
103
+ 'failedDeleteEntity',
104
+ 'beforeDeleteEntityByID',
105
+ 'afterDeleteEntityByID',
106
+ 'failedDeleteEntityByID',
107
+ 'beforeNewUID',
108
+ 'afterNewUID',
109
+ 'failedNewUID',
110
+ 'beforeSetUID',
111
+ 'afterSetUID',
112
+ 'failedSetUID',
113
+ 'beforeRenameUID',
114
+ 'afterRenameUID',
115
+ 'failedRenameUID',
116
+ 'beforeDeleteUID',
117
+ 'afterDeleteUID',
118
+ 'failedDeleteUID',
119
+ 'beforeStartTransaction',
120
+ 'afterStartTransaction',
121
+ 'beforeCommitTransaction',
122
+ 'afterCommitTransaction',
123
+ 'beforeRollbackTransaction',
124
+ 'afterRollbackTransaction',
125
+ ];
126
+ for (let event of events) {
127
+ const prop = event + 'Callbacks';
128
+ // @ts-ignore: The callback should be the right type here.
129
+ const callbacks = parent[prop];
130
+ for (let callback of callbacks) {
131
+ if (callback.skipOnClone) {
132
+ continue;
133
+ }
134
+ this.on(event, callback);
135
+ }
136
+ }
137
+ }
138
+ this.config.debugInfo('nymph', 'Nymph loaded.');
139
+ if (tilmeld != null) {
50
140
  this.tilmeld = tilmeld;
51
141
  }
52
142
  this.driver.init(this);
143
+ this.config.debugInfo('nymph', 'Nymph driver loaded.');
53
144
  if (this.tilmeld) {
54
145
  this.tilmeld.init(this);
146
+ this.config.debugInfo('nymph', 'Tilmeld loaded.');
55
147
  }
56
148
  }
57
- addEntityClass(entityClass) {
149
+ /**
150
+ * Add your class to this instance.
151
+ *
152
+ * This will create a class that extends your class within this instance of
153
+ * Nymph and return it. You can then use this class's constructor and methods,
154
+ * which will use this instance of Nymph.
155
+ *
156
+ * Because this creates a subclass, don't use the class
157
+ * returned from `getEntityClass` to check with `instanceof`.
158
+ */
159
+ addEntityClass(EntityClass) {
160
+ if (EntityClass.class in this.entityClasses) {
161
+ this.config.debugLog('nymph', `Adding duplicate class "${EntityClass.class}". This may be a mistake.`);
162
+ }
163
+ // Check that it doesn't have the same etype as any other class.
164
+ for (const ExistingClass of Object.values(this.entityClasses)) {
165
+ if (EntityClass.ETYPE === ExistingClass.ETYPE &&
166
+ EntityClass.class !== EntityClass.class) {
167
+ this.config.debugLog('nymph', `Adding class "${EntityClass.class}" with same etype as existing class "${ExistingClass.class}". This may be a mistake.`);
168
+ }
169
+ }
58
170
  const nymph = this;
59
- class NymphEntity extends entityClass {
171
+ this.entityClasses[EntityClass.class] = class extends EntityClass {
172
+ static nymph = nymph;
60
173
  constructor(...args) {
61
174
  super(...args);
62
175
  }
63
- }
64
- NymphEntity.nymph = nymph;
65
- this.entityClasses[entityClass.class] = NymphEntity;
66
- return NymphEntity;
176
+ };
177
+ return this.entityClasses[EntityClass.class];
67
178
  }
68
179
  getEntityClass(className) {
69
- if (className in this.entityClasses) {
70
- return this.entityClasses[className];
180
+ let key = null;
181
+ if (typeof className === 'string') {
182
+ key = className;
71
183
  }
72
- throw new errors_1.ClassNotAvailableError('Tried to use class: ' + className);
184
+ else {
185
+ key = className.class;
186
+ }
187
+ if (key in this.entityClasses) {
188
+ return this.entityClasses[key];
189
+ }
190
+ this.config.debugError('nymph', `Tried to use missing class "${key}".`);
191
+ throw new ClassNotAvailableError(`Tried to use missing class "${key}".`);
73
192
  }
193
+ /**
194
+ * Get the class that uses the specified etype.
195
+ *
196
+ * Note that it is fine, though unusual, for two classes to use the same
197
+ * etype. However, this can lead to very hard to diagnose bugs, so is
198
+ * generally discouraged.
199
+ */
200
+ getEntityClassByEtype(etype) {
201
+ for (const EntityClass of Object.values(this.entityClasses)) {
202
+ if (EntityClass.ETYPE === etype) {
203
+ return EntityClass;
204
+ }
205
+ }
206
+ this.config.debugError('nymph', `Tried to use missing class by etype "${etype}".`);
207
+ throw new ClassNotAvailableError(`Tried to use missing class by etype "${etype}".`);
208
+ }
209
+ /**
210
+ * Get a clone of this instance with cloned classes and event listeners.
211
+ *
212
+ * @returns A clone of this instance.
213
+ */
74
214
  clone() {
75
- const nymph = new Nymph(this.config, this.driver.clone(), this.tilmeld?.clone());
76
- nymph.parent = this;
77
- for (const name in this.entityClasses) {
78
- if (name === 'Entity' || this.entityClasses[name].skipOnClone) {
79
- continue;
80
- }
81
- nymph.addEntityClass(this.entityClasses[name]);
82
- }
83
- const events = [
84
- 'connect',
85
- 'disconnect',
86
- 'query',
87
- 'beforeGetEntity',
88
- 'beforeGetEntities',
89
- 'beforeSaveEntity',
90
- 'afterSaveEntity',
91
- 'failedSaveEntity',
92
- 'beforeDeleteEntity',
93
- 'afterDeleteEntity',
94
- 'failedDeleteEntity',
95
- 'beforeDeleteEntityByID',
96
- 'afterDeleteEntityByID',
97
- 'failedDeleteEntityByID',
98
- 'beforeNewUID',
99
- 'afterNewUID',
100
- 'failedNewUID',
101
- 'beforeSetUID',
102
- 'afterSetUID',
103
- 'failedSetUID',
104
- 'beforeRenameUID',
105
- 'afterRenameUID',
106
- 'failedRenameUID',
107
- 'beforeDeleteUID',
108
- 'afterDeleteUID',
109
- 'failedDeleteUID',
110
- 'beforeStartTransaction',
111
- 'afterStartTransaction',
112
- 'beforeCommitTransaction',
113
- 'afterCommitTransaction',
114
- 'beforeRollbackTransaction',
115
- 'afterRollbackTransaction',
116
- ];
117
- for (let event of events) {
118
- const prop = event + 'Callbacks';
119
- const callbacks = this[prop];
120
- for (let callback of callbacks) {
121
- if (callback.skipOnClone) {
122
- continue;
123
- }
124
- nymph.on(event, callback);
125
- }
126
- }
127
- return nymph;
215
+ return new Nymph(this.config, this.driver.clone(), this.tilmeld?.clone(), this);
128
216
  }
217
+ /**
218
+ * Connect to the database.
219
+ *
220
+ * @returns Whether the instance is connected to the database.
221
+ */
129
222
  async connect() {
130
- const result = this.driver.connect();
131
- for (let callback of this.connectCallbacks) {
132
- if (callback) {
133
- await callback(this, result);
223
+ try {
224
+ const result = this.driver.connect();
225
+ for (let callback of this.connectCallbacks) {
226
+ if (callback) {
227
+ await callback(this, result);
228
+ }
134
229
  }
230
+ this.config.debugInfo('nymph', 'Driver connected.');
231
+ return await result;
232
+ }
233
+ catch (e) {
234
+ this.config.debugError('nymph', `Failed to connect: ${e}`);
235
+ throw e;
135
236
  }
136
- return await result;
137
237
  }
238
+ /**
239
+ * Disconnect from the database.
240
+ *
241
+ * @returns Whether the instance is connected to the database.
242
+ */
138
243
  async disconnect() {
139
- const result = this.driver.disconnect();
140
- for (let callback of this.disconnectCallbacks) {
141
- if (callback) {
142
- await callback(this, result);
244
+ try {
245
+ const result = this.driver.disconnect();
246
+ for (let callback of this.disconnectCallbacks) {
247
+ if (callback) {
248
+ await callback(this, result);
249
+ }
143
250
  }
251
+ this.config.debugInfo('nymph', 'Driver disconnected.');
252
+ return await result;
253
+ }
254
+ catch (e) {
255
+ this.config.debugError('nymph', `Failed to disconnect: ${e}`);
256
+ throw e;
144
257
  }
145
- return await result;
146
258
  }
259
+ /**
260
+ * Run all the query callbacks on a query.
261
+ */
147
262
  runQueryCallbacks(options, selectors) {
148
263
  for (let callback of this.queryCallbacks) {
149
264
  if (callback) {
@@ -151,270 +266,691 @@ class Nymph {
151
266
  }
152
267
  }
153
268
  }
269
+ /**
270
+ * Start an atomic transaction and returns a new instance of Nymph.
271
+ *
272
+ * All proceeding changes using this new instance will wait to be written to
273
+ * the database's permanent storage until commit() is called. You can also
274
+ * undo all the changes since this function ran with rollback().
275
+ *
276
+ * Transactions will nest as long as every name is unique. Internally, Nymph
277
+ * uses names prefixed with "nymph-".
278
+ *
279
+ * @returns A new instance of Nymph that should be used for the transaction.
280
+ */
154
281
  async startTransaction(name) {
155
- for (let callback of this.beforeStartTransactionCallbacks) {
156
- if (callback) {
157
- await callback(this, name);
282
+ try {
283
+ for (let callback of this.beforeStartTransactionCallbacks) {
284
+ if (callback) {
285
+ await callback(this, name);
286
+ }
158
287
  }
159
- }
160
- const result = await this.driver.startTransaction(name);
161
- for (let callback of this.afterStartTransactionCallbacks) {
162
- if (callback) {
163
- await callback(this, name, result);
288
+ this.config.debugLog('nymph', `Starting transaction "${name}".`);
289
+ const result = await this.driver.startTransaction(name);
290
+ for (let callback of this.afterStartTransactionCallbacks) {
291
+ if (callback) {
292
+ await callback(this, name, result);
293
+ }
164
294
  }
295
+ return result;
296
+ }
297
+ catch (e) {
298
+ this.config.debugError('nymph', `Failed to start transaction: ${e}`);
299
+ throw e;
165
300
  }
166
- return result;
167
301
  }
302
+ /**
303
+ * Commit the named transaction.
304
+ *
305
+ * After this is called, the transaction instance should be discarded.
306
+ *
307
+ * @returns True on success, false on failure.
308
+ */
168
309
  async commit(name) {
169
- for (let callback of this.beforeCommitTransactionCallbacks) {
170
- if (callback) {
171
- await callback(this, name);
310
+ try {
311
+ for (let callback of this.beforeCommitTransactionCallbacks) {
312
+ if (callback) {
313
+ await callback(this, name);
314
+ }
172
315
  }
173
- }
174
- const result = await this.driver.commit(name);
175
- for (let callback of this.afterCommitTransactionCallbacks) {
176
- if (callback) {
177
- await callback(this, name, result);
316
+ this.config.debugLog('nymph', `Committing transaction "${name}".`);
317
+ const result = await this.driver.commit(name);
318
+ for (let callback of this.afterCommitTransactionCallbacks) {
319
+ if (callback) {
320
+ await callback(this, name, result);
321
+ }
178
322
  }
323
+ return result;
324
+ }
325
+ catch (e) {
326
+ this.config.debugError('nymph', `Failed to commit transaction: ${e}`);
327
+ throw e;
179
328
  }
180
- return result;
181
329
  }
330
+ /**
331
+ * Rollback the named transaction.
332
+ *
333
+ * After this is called, the transaction instance should be discarded.
334
+ *
335
+ * @returns True on success, false on failure.
336
+ */
182
337
  async rollback(name) {
183
- for (let callback of this.beforeRollbackTransactionCallbacks) {
184
- if (callback) {
185
- await callback(this, name);
338
+ try {
339
+ for (let callback of this.beforeRollbackTransactionCallbacks) {
340
+ if (callback) {
341
+ await callback(this, name);
342
+ }
186
343
  }
187
- }
188
- const result = await this.driver.rollback(name);
189
- for (let callback of this.afterRollbackTransactionCallbacks) {
190
- if (callback) {
191
- await callback(this, name, result);
344
+ this.config.debugLog('nymph', `Rolling back transaction "${name}".`);
345
+ const result = await this.driver.rollback(name);
346
+ for (let callback of this.afterRollbackTransactionCallbacks) {
347
+ if (callback) {
348
+ await callback(this, name, result);
349
+ }
192
350
  }
351
+ return result;
352
+ }
353
+ catch (e) {
354
+ this.config.debugError('nymph', `Failed to roll back transaction: ${e}`);
355
+ throw e;
193
356
  }
194
- return result;
195
357
  }
358
+ /**
359
+ * Check if there is any open transaction.
360
+ *
361
+ * @returns True if there is a transaction.
362
+ */
196
363
  async inTransaction() {
197
364
  return await this.driver.inTransaction();
198
365
  }
366
+ /**
367
+ * Increment or create a unique ID and return the new value.
368
+ *
369
+ * Unique IDs, or UIDs are similar to GUIDs, but numeric and sequential.
370
+ *
371
+ * A UID can be used to identify an object when the GUID doesn't suffice. On
372
+ * a system where a new entity is created many times per second, referring
373
+ * to something by its GUID may be unintuitive. However, the component
374
+ * designer is responsible for assigning UIDs to the component's entities.
375
+ * Beware that if a UID is incremented for an entity, and the entity cannot
376
+ * be saved, there is no safe, and therefore, no recommended way to
377
+ * decrement the UID back to its previous value.
378
+ *
379
+ * If newUID() is passed the name of a UID which does not exist yet, one
380
+ * will be created with that name, and assigned the value 1. If the UID
381
+ * already exists, its value will be incremented. The new value will be
382
+ * returned.
383
+ *
384
+ * @param name The UID's name.
385
+ * @returns The UID's new value, or null on failure.
386
+ */
199
387
  async newUID(name) {
200
- for (let callback of this.beforeNewUIDCallbacks) {
201
- if (callback) {
202
- await callback(this, name);
203
- }
204
- }
205
- let result;
206
388
  try {
207
- result = this.driver.newUID(name);
208
- }
209
- catch (e) {
210
- for (let callback of this.failedNewUIDCallbacks) {
389
+ for (let callback of this.beforeNewUIDCallbacks) {
211
390
  if (callback) {
212
- await callback(this, e);
391
+ await callback(this, name);
213
392
  }
214
393
  }
215
- throw e;
216
- }
217
- for (let callback of this.afterNewUIDCallbacks) {
218
- if (callback) {
219
- await callback(this, result);
394
+ let result;
395
+ try {
396
+ result = this.driver.newUID(name);
397
+ }
398
+ catch (e) {
399
+ for (let callback of this.failedNewUIDCallbacks) {
400
+ if (callback) {
401
+ await callback(this, e);
402
+ }
403
+ }
404
+ throw e;
405
+ }
406
+ for (let callback of this.afterNewUIDCallbacks) {
407
+ if (callback) {
408
+ await callback(this, result);
409
+ }
220
410
  }
411
+ return await result;
412
+ }
413
+ catch (e) {
414
+ this.config.debugError('nymph', `Failed to create UID "${name}": ${e}`);
415
+ throw e;
221
416
  }
222
- return await result;
223
417
  }
418
+ /**
419
+ * Get the current value of a unique ID.
420
+ * @param name The UID's name.
421
+ * @returns The UID's value, or null on failure and if it doesn't exist.
422
+ */
224
423
  async getUID(name) {
225
- return await this.driver.getUID(name);
226
- }
227
- async setUID(name, value) {
228
- for (let callback of this.beforeSetUIDCallbacks) {
229
- if (callback) {
230
- await callback(this, name, value);
231
- }
232
- }
233
- let result;
234
424
  try {
235
- result = this.driver.setUID(name, value);
425
+ return await this.driver.getUID(name);
236
426
  }
237
427
  catch (e) {
238
- for (let callback of this.failedSetUIDCallbacks) {
428
+ this.config.debugError('nymph', `Failed to get UID "${name}": ${e}`);
429
+ throw e;
430
+ }
431
+ }
432
+ /**
433
+ * Set the value of a UID.
434
+ *
435
+ * @param name The UID's name.
436
+ * @param value The value.
437
+ * @returns True on success, false on failure.
438
+ */
439
+ async setUID(name, value) {
440
+ try {
441
+ for (let callback of this.beforeSetUIDCallbacks) {
239
442
  if (callback) {
240
- await callback(this, e);
443
+ await callback(this, name, value);
241
444
  }
242
445
  }
243
- throw e;
244
- }
245
- for (let callback of this.afterSetUIDCallbacks) {
246
- if (callback) {
247
- await callback(this, result);
446
+ let result;
447
+ try {
448
+ result = this.driver.setUID(name, value);
449
+ }
450
+ catch (e) {
451
+ for (let callback of this.failedSetUIDCallbacks) {
452
+ if (callback) {
453
+ await callback(this, e);
454
+ }
455
+ }
456
+ throw e;
457
+ }
458
+ for (let callback of this.afterSetUIDCallbacks) {
459
+ if (callback) {
460
+ await callback(this, result);
461
+ }
248
462
  }
463
+ return await result;
464
+ }
465
+ catch (e) {
466
+ this.config.debugError('nymph', `Failed to set UID "${name}": ${e}`);
467
+ throw e;
249
468
  }
250
- return await result;
251
469
  }
470
+ /**
471
+ * Delete a unique ID.
472
+ *
473
+ * @param name The UID's name.
474
+ * @returns True on success, false on failure.
475
+ */
252
476
  async deleteUID(name) {
253
- for (let callback of this.beforeDeleteUIDCallbacks) {
254
- if (callback) {
255
- await callback(this, name);
256
- }
257
- }
258
- let result;
259
477
  try {
260
- result = this.driver.deleteUID(name);
261
- }
262
- catch (e) {
263
- for (let callback of this.failedDeleteUIDCallbacks) {
478
+ for (let callback of this.beforeDeleteUIDCallbacks) {
264
479
  if (callback) {
265
- await callback(this, e);
480
+ await callback(this, name);
266
481
  }
267
482
  }
268
- throw e;
269
- }
270
- for (let callback of this.afterDeleteUIDCallbacks) {
271
- if (callback) {
272
- await callback(this, result);
483
+ let result;
484
+ try {
485
+ result = this.driver.deleteUID(name);
486
+ }
487
+ catch (e) {
488
+ for (let callback of this.failedDeleteUIDCallbacks) {
489
+ if (callback) {
490
+ await callback(this, e);
491
+ }
492
+ }
493
+ throw e;
273
494
  }
495
+ for (let callback of this.afterDeleteUIDCallbacks) {
496
+ if (callback) {
497
+ await callback(this, result);
498
+ }
499
+ }
500
+ return await result;
501
+ }
502
+ catch (e) {
503
+ this.config.debugError('nymph', `Failed to delete UID "${name}": ${e}`);
504
+ throw e;
274
505
  }
275
- return await result;
276
506
  }
507
+ /**
508
+ * Rename a unique ID.
509
+ *
510
+ * @param oldName The old name.
511
+ * @param newName The new name.
512
+ * @returns True on success, false on failure.
513
+ */
277
514
  async renameUID(oldName, newName) {
278
- for (let callback of this.beforeRenameUIDCallbacks) {
279
- if (callback) {
280
- await callback(this, oldName, newName);
281
- }
282
- }
283
- let result;
284
515
  try {
285
- result = this.driver.renameUID(oldName, newName);
286
- }
287
- catch (e) {
288
- for (let callback of this.failedRenameUIDCallbacks) {
516
+ for (let callback of this.beforeRenameUIDCallbacks) {
289
517
  if (callback) {
290
- await callback(this, e);
518
+ await callback(this, oldName, newName);
291
519
  }
292
520
  }
293
- throw e;
294
- }
295
- for (let callback of this.afterRenameUIDCallbacks) {
296
- if (callback) {
297
- await callback(this, result);
521
+ let result;
522
+ try {
523
+ result = this.driver.renameUID(oldName, newName);
524
+ }
525
+ catch (e) {
526
+ for (let callback of this.failedRenameUIDCallbacks) {
527
+ if (callback) {
528
+ await callback(this, e);
529
+ }
530
+ }
531
+ throw e;
532
+ }
533
+ for (let callback of this.afterRenameUIDCallbacks) {
534
+ if (callback) {
535
+ await callback(this, result);
536
+ }
298
537
  }
538
+ return await result;
539
+ }
540
+ catch (e) {
541
+ this.config.debugError('nymph', `Failed to rename UID "${oldName}" to "${newName}": ${e}`);
542
+ throw e;
299
543
  }
300
- return await result;
301
544
  }
545
+ /**
546
+ * Save an entity to the database.
547
+ *
548
+ * If the entity has never been saved (has no GUID), a variable "cdate"
549
+ * is set on it with the current Unix timestamp.
550
+ *
551
+ * The variable "mdate" is set to the current Unix timestamp.
552
+ *
553
+ * @param entity The entity.
554
+ * @returns True on success, false on failure.
555
+ */
302
556
  async saveEntity(entity) {
303
- for (let callback of this.beforeSaveEntityCallbacks) {
304
- if (callback) {
305
- await callback(this, entity);
306
- }
307
- }
308
- let result;
309
557
  try {
310
- result = this.driver.saveEntity(entity);
311
- }
312
- catch (e) {
313
- for (let callback of this.failedSaveEntityCallbacks) {
558
+ for (let callback of this.beforeSaveEntityCallbacks) {
314
559
  if (callback) {
315
- await callback(this, e);
560
+ await callback(this, entity);
316
561
  }
317
562
  }
318
- throw e;
319
- }
320
- for (let callback of this.afterSaveEntityCallbacks) {
321
- if (callback) {
322
- await callback(this, result);
563
+ let result;
564
+ try {
565
+ result = this.driver.saveEntity(entity);
323
566
  }
567
+ catch (e) {
568
+ for (let callback of this.failedSaveEntityCallbacks) {
569
+ if (callback) {
570
+ await callback(this, e);
571
+ }
572
+ }
573
+ throw e;
574
+ }
575
+ for (let callback of this.afterSaveEntityCallbacks) {
576
+ if (callback) {
577
+ await callback(this, result);
578
+ }
579
+ }
580
+ return await result;
581
+ }
582
+ catch (e) {
583
+ this.config.debugError('nymph', `Failed to save entity: ${e}`);
584
+ throw e;
324
585
  }
325
- return await result;
326
586
  }
327
587
  async getEntities(options = {}, ...selectors) {
328
- for (let callback of this.beforeGetEntitiesCallbacks) {
329
- if (callback) {
330
- await callback(this, options, selectors);
588
+ if (options.source === 'client' && options.return === 'object') {
589
+ throw new InvalidParametersError('Object return type not allowed from client.');
590
+ }
591
+ try {
592
+ for (let callback of this.beforeGetEntitiesCallbacks) {
593
+ if (callback) {
594
+ await callback(this, options, selectors);
595
+ }
331
596
  }
597
+ return await this.driver.getEntities(options, ...selectors);
598
+ }
599
+ catch (e) {
600
+ this.config.debugError('nymph', `Failed to get entities: ${e}`);
601
+ throw e;
332
602
  }
333
- return await this.driver.getEntities(options, ...selectors);
334
603
  }
335
604
  async getEntity(options = {}, ...selectors) {
336
- if (typeof selectors[0] === 'string') {
337
- selectors = [{ type: '&', guid: selectors[0] }];
605
+ if (options.source === 'client' && options.return === 'object') {
606
+ throw new InvalidParametersError('Object return type not allowed from client.');
338
607
  }
339
- options.limit = 1;
340
- for (let callback of this.beforeGetEntityCallbacks) {
341
- if (callback) {
342
- await callback(this, options, selectors);
608
+ try {
609
+ // Set up options and selectors.
610
+ if (typeof selectors[0] === 'string') {
611
+ selectors = [{ type: '&', guid: selectors[0] }];
343
612
  }
613
+ options.limit = 1;
614
+ for (let callback of this.beforeGetEntityCallbacks) {
615
+ if (callback) {
616
+ await callback(this, options, selectors);
617
+ }
618
+ }
619
+ const entities = await this.driver.getEntities(options, ...selectors);
620
+ if (options.return === 'count') {
621
+ return entities;
622
+ }
623
+ if (!entities || !entities.length) {
624
+ return null;
625
+ }
626
+ return entities[0];
344
627
  }
345
- const entities = await this.driver.getEntities(options, ...selectors);
346
- if (options.return === 'count') {
347
- return entities;
348
- }
349
- if (!entities || !entities.length) {
350
- return null;
628
+ catch (e) {
629
+ this.config.debugError('nymph', `Failed to get entity: ${e}`);
630
+ throw e;
351
631
  }
352
- return entities[0];
353
632
  }
633
+ /**
634
+ * Delete an entity from the database.
635
+ *
636
+ * @param entity The entity.
637
+ * @returns True on success, false on failure.
638
+ */
354
639
  async deleteEntity(entity) {
355
- for (let callback of this.beforeDeleteEntityCallbacks) {
356
- if (callback) {
357
- await callback(this, entity);
358
- }
359
- }
360
- let result;
361
640
  try {
362
- result = this.driver.deleteEntity(entity);
641
+ for (let callback of this.beforeDeleteEntityCallbacks) {
642
+ if (callback) {
643
+ await callback(this, entity);
644
+ }
645
+ }
646
+ let result;
647
+ try {
648
+ result = this.driver.deleteEntity(entity);
649
+ }
650
+ catch (e) {
651
+ for (let callback of this.failedDeleteEntityCallbacks) {
652
+ if (callback) {
653
+ await callback(this, e);
654
+ }
655
+ }
656
+ throw e;
657
+ }
658
+ for (let callback of this.afterDeleteEntityCallbacks) {
659
+ if (callback) {
660
+ await callback(this, result);
661
+ }
662
+ }
663
+ return await result;
363
664
  }
364
665
  catch (e) {
365
- for (let callback of this.failedDeleteEntityCallbacks) {
666
+ this.config.debugError('nymph', `Failed to delete entity: ${e}`);
667
+ throw e;
668
+ }
669
+ }
670
+ /**
671
+ * Delete an entity from the database by its GUID.
672
+ *
673
+ * @param guid The entity's GUID.
674
+ * @param className The entity's class name.
675
+ * @returns True on success, false on failure.
676
+ */
677
+ async deleteEntityByID(guid, className) {
678
+ try {
679
+ for (let callback of this.beforeDeleteEntityByIDCallbacks) {
680
+ if (callback) {
681
+ await callback(this, guid, className);
682
+ }
683
+ }
684
+ let result;
685
+ try {
686
+ result = this.driver.deleteEntityByID(guid, className);
687
+ }
688
+ catch (e) {
689
+ for (let callback of this.failedDeleteEntityByIDCallbacks) {
690
+ if (callback) {
691
+ await callback(this, e);
692
+ }
693
+ }
694
+ throw e;
695
+ }
696
+ for (let callback of this.afterDeleteEntityByIDCallbacks) {
366
697
  if (callback) {
367
- await callback(this, e);
698
+ await callback(this, result);
368
699
  }
369
700
  }
701
+ return await result;
702
+ }
703
+ catch (e) {
704
+ this.config.debugError('nymph', `Failed to delete entity by ID: ${e}`);
370
705
  throw e;
371
706
  }
372
- for (let callback of this.afterDeleteEntityCallbacks) {
373
- if (callback) {
374
- await callback(this, result);
375
- }
707
+ }
708
+ /**
709
+ * Export entities to a local file.
710
+ *
711
+ * This is the file format:
712
+ *
713
+ * ```
714
+ * #nex2
715
+ * # The above line must be the first thing in the file.
716
+ * # Comments begin with #
717
+ * # And can have white space before them.
718
+ * # This defines a UID.
719
+ * <name/of/uid>[5]
720
+ * <another uid>[8000]
721
+ * # For UIDs, the name is in angle brackets (<>) and the value follows
722
+ * # in square brackets ([]).
723
+ * # This starts a new entity.
724
+ * {1234abcd}<etype>[tag,list,with,commas]
725
+ * # For entities, the GUID is in curly brackets ({}), then the etype in
726
+ * # angle brackets, then the comma separated tag list follows in square
727
+ * # brackets ([]).
728
+ * # Properties are stored like this:
729
+ * # propname=JSON.stringify(value)
730
+ * abilities=["system/admin"]
731
+ * groups=[]
732
+ * inheritAbilities=false
733
+ * name="admin"
734
+ * # White space before/after "=" and at beginning/end of line is ignored.
735
+ * username = "admin"
736
+ * {2}<etype>[tag,list]
737
+ * another="This is another entity."
738
+ * newline="\n"
739
+ * ```
740
+ *
741
+ * @param filename The file to export to.
742
+ * @returns True on success, false on failure.
743
+ */
744
+ async export(filename) {
745
+ try {
746
+ return await this.driver.export(filename);
747
+ }
748
+ catch (e) {
749
+ this.config.debugError('nymph', `Failed to export: ${e}`);
750
+ throw e;
376
751
  }
377
- return await result;
378
752
  }
379
- async deleteEntityByID(guid, className) {
380
- for (let callback of this.beforeDeleteEntityByIDCallbacks) {
381
- if (callback) {
382
- await callback(this, guid, className);
383
- }
753
+ /**
754
+ * Export entities to the console.
755
+ *
756
+ * @returns True on success, false on failure.
757
+ */
758
+ async exportPrint() {
759
+ try {
760
+ return await this.driver.exportPrint();
384
761
  }
385
- let result;
762
+ catch (e) {
763
+ this.config.debugError('nymph', `Failed to export: ${e}`);
764
+ throw e;
765
+ }
766
+ }
767
+ async exportEntity(etype, guid) {
386
768
  try {
387
- result = this.driver.deleteEntityByID(guid, className);
769
+ return await this.driver.exportEntity(etype, guid);
388
770
  }
389
771
  catch (e) {
390
- for (let callback of this.failedDeleteEntityByIDCallbacks) {
391
- if (callback) {
392
- await callback(this, e);
393
- }
394
- }
772
+ this.config.debugError('nymph', `Failed to export: ${e}`);
395
773
  throw e;
396
774
  }
397
- for (let callback of this.afterDeleteEntityByIDCallbacks) {
398
- if (callback) {
399
- await callback(this, result);
400
- }
775
+ }
776
+ /**
777
+ * Import entities from a file.
778
+ *
779
+ * @param filename The file to import from.
780
+ * @param options The options for the import.
781
+ * @returns True on success, false on failure.
782
+ */
783
+ async import(filename, options) {
784
+ try {
785
+ return await this.driver.import(filename, options);
786
+ }
787
+ catch (e) {
788
+ this.config.debugError('nymph', `Failed to import: ${e}`);
789
+ throw e;
401
790
  }
402
- return await result;
403
791
  }
404
- async export(filename) {
405
- return await this.driver.export(filename);
792
+ /**
793
+ * Import a single entity.
794
+ *
795
+ * @param entity The entity data to import.
796
+ */
797
+ async importEntity(entity) {
798
+ try {
799
+ return await this.driver.importEntity(entity);
800
+ }
801
+ catch (e) {
802
+ this.config.debugError('nymph', `Failed to import: ${e}`);
803
+ throw e;
804
+ }
406
805
  }
407
- async exportPrint() {
408
- return await this.driver.exportPrint();
806
+ /**
807
+ * Import a single entity's tokens.
808
+ *
809
+ * This does not update any of the actual data, only the tokens used for full
810
+ * text search.
811
+ *
812
+ * Be careful with this function, since you can overwrite existing tokens with
813
+ * data that is out of sync. The DB _should_ prevent you from inserting tokens
814
+ * for an entity that doesn't exist, but it _won't_ prevent you from inserting
815
+ * tokens for properties that don't exist on an entity.
816
+ *
817
+ * @param entity The entity data to import.
818
+ */
819
+ async importEntityTokens(entity) {
820
+ try {
821
+ return await this.driver.importEntityTokens(entity);
822
+ }
823
+ catch (e) {
824
+ this.config.debugError('nymph', `Failed to import: ${e}`);
825
+ throw e;
826
+ }
827
+ }
828
+ /**
829
+ * Import a single entity's Tilmeld access control properties.
830
+ *
831
+ * This does not update any of the other data, only the access control
832
+ * columns.
833
+ *
834
+ * @param entity The entity data to import.
835
+ */
836
+ async importEntityTilmeldAC(entity) {
837
+ try {
838
+ return await this.driver.importEntityTilmeldAC(entity);
839
+ }
840
+ catch (e) {
841
+ this.config.debugError('nymph', `Failed to import: ${e}`);
842
+ throw e;
843
+ }
844
+ }
845
+ /**
846
+ * Detect whether the database needs to be migrated.
847
+ *
848
+ * If not false (so, one of the string values), the database should be
849
+ * exported with an old version of Nymph, then imported into a fresh database
850
+ * with this version. Sometimes, a live migration can be done instead, which
851
+ * will require no downtime. You should still export the database beforehand,
852
+ * in case anything goes wrong.
853
+ *
854
+ * 'json' means the data tables are missing the JSON field, and the DB
855
+ * **must** be exported and imported to fix it.
856
+ *
857
+ * 'tokens' means the FTS token tables are missing, and you can get away with
858
+ * a live migration with `liveMigration('tokenTables')` and doing
859
+ * `importEntityTokens(entity)` for each entity.
860
+ *
861
+ * 'tilmeldColumns' means the entity columns for "user", "group", and access
862
+ * controls are missing, and you can get away with a live migration with
863
+ * `liveMigration('tilmeldColumns')` and doing
864
+ * `importEntityTilmeldAC(entity)` for each entity. DO NOT update to any
865
+ * version later than 1.0.0-beta.109 before this is done, or access controls
866
+ * WILL NOT work, and everyone will have access to every entity!
867
+ */
868
+ async needsMigration() {
869
+ return await this.driver.needsMigration();
870
+ }
871
+ /**
872
+ * Perform a live migration on the DB.
873
+ *
874
+ * A 'tokenTables' migration will simply add the missing token tables. It will
875
+ * **not** fill them. You must use `importEntityTokens` on each entity after
876
+ * running this to fill the token tables and enable full text search matching
877
+ * on existing entities.
878
+ *
879
+ * A 'tilmeldColumns' migration will simply add the missing Tilmeld columns
880
+ * and indexes to each entity table. It will **not** fill them, and you must
881
+ * fill them before updating to the latest version of Nymph, or everyone will
882
+ * be able to access every entity! You must use `importEntityTilmeldAC` on
883
+ * each entity after running this.
884
+ *
885
+ * A 'tilmeldRemoveOldRows' migration will remove all of the old access
886
+ * control rows in the data tables that are no longer used as of
887
+ * 1.0.0-beta.110. MAKE SURE YOU HAVE RUN `importEntityTilmeldAC` ON ALL
888
+ * ENTITIES BEFORE RUNNING THIS, OR THE DATA WILL BE IRRETRIEVABLY LOST!
889
+ */
890
+ async liveMigration(migrationType) {
891
+ return await this.driver.liveMigration(migrationType);
892
+ }
893
+ /**
894
+ * Returns a list of the etypes stored in the DB.
895
+ */
896
+ async getEtypes() {
897
+ return await this.driver.getEtypes();
898
+ }
899
+ /**
900
+ * Get all the indexes defined for a specific etype.
901
+ *
902
+ * @param etype The etype to get indexes for.
903
+ */
904
+ async getIndexes(etype) {
905
+ return await this.driver.getIndexes(etype);
906
+ }
907
+ /**
908
+ * Add an index on a specific property to an etype.
909
+ *
910
+ * Nymph adds general indexes on etypes, but they span across all properties,
911
+ * so they can only speed up a query so much. You can add indexes that are
912
+ * specific to a property with this function, which will speed up queries for
913
+ * that property. If an etype only has one or two properties, you may not need
914
+ * a specific index, but if your etypes include many properties or properties
915
+ * that generally contain large amounts of data, adding a specific index can
916
+ * drastically improve the performance of your queries.
917
+ *
918
+ * The scope of the index will affect which queries it will speed up:
919
+ *
920
+ * - data: This will speed up comparisons and equality checks in queries.
921
+ * - references: This will speed up ref and qref clauses in queries.
922
+ * - tokens: This will speed up search clauses in queries.
923
+ *
924
+ * The name must be unique within the etype and scope or the existing index
925
+ * will be overwritten.
926
+ *
927
+ * You do not need to add reference indexes for the "user" and "group"
928
+ * properties, because these are already specifically indexed by Nymph.
929
+ *
930
+ * @param etype The etype to add the index on.
931
+ * @param definition The definition of the index.
932
+ */
933
+ async addIndex(etype, definition) {
934
+ this.driver.checkIndexName(definition.name);
935
+ return await this.driver.addIndex(etype, definition);
409
936
  }
410
- async import(filename) {
411
- return await this.driver.import(filename);
937
+ /**
938
+ * Delete an index from an etype.
939
+ *
940
+ * @param etype The etype to delete the index on.
941
+ * @param scope The scope of the index.
942
+ * @param name The name of the index to delete.
943
+ */
944
+ async deleteIndex(etype, scope, name) {
945
+ this.driver.checkIndexName(name);
946
+ return await this.driver.deleteIndex(etype, scope, name);
412
947
  }
413
948
  on(event, callback) {
414
949
  const prop = (event + 'Callbacks');
415
950
  if (!(prop in this)) {
416
951
  throw new Error('Invalid event type.');
417
952
  }
953
+ // @ts-ignore: The callback should be the right type here.
418
954
  this[prop].push(callback);
419
955
  return () => this.off(event, callback);
420
956
  }
@@ -423,12 +959,13 @@ class Nymph {
423
959
  if (!(prop in this)) {
424
960
  return false;
425
961
  }
962
+ // @ts-ignore: The callback should always be the right type here.
426
963
  const i = this[prop].indexOf(callback);
427
964
  if (i > -1) {
965
+ // @ts-ignore: The callback should always be the right type here.
428
966
  this[prop].splice(i, 1);
429
967
  }
430
968
  return true;
431
969
  }
432
970
  }
433
- exports.default = Nymph;
434
971
  //# sourceMappingURL=Nymph.js.map