@nymphjs/nymph 1.0.0-beta.55 → 1.0.0-beta.56
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.
- package/CHANGELOG.md +8 -0
- package/dist/Entity.d.ts +191 -0
- package/dist/Entity.js +179 -0
- package/dist/Entity.js.map +1 -1
- package/dist/Entity.test.js +6 -0
- package/dist/Entity.test.js.map +1 -1
- package/dist/Entity.types.d.ts +258 -0
- package/dist/Nymph.d.ts +389 -0
- package/dist/Nymph.js +501 -179
- package/dist/Nymph.js.map +1 -1
- package/dist/Nymph.types.d.ts +12 -1
- package/dist/Nymph.types.js +1 -0
- package/dist/Nymph.types.js.map +1 -1
- package/dist/conf/d.d.ts +26 -0
- package/dist/conf/defaults.js +22 -0
- package/dist/conf/defaults.js.map +1 -1
- package/dist/driver/NymphDriver.d.ts +61 -1
- package/dist/driver/NymphDriver.js +220 -123
- package/dist/driver/NymphDriver.js.map +1 -1
- package/dist/driver/testArtifacts.js +142 -0
- package/dist/driver/testArtifacts.js.map +1 -1
- package/dist/errors/EntityInvalidDataError.d.ts +6 -0
- package/dist/errors/EntityInvalidDataError.js +6 -0
- package/dist/errors/EntityInvalidDataError.js.map +1 -1
- package/dist/testArtifacts.d.ts +9 -0
- package/dist/testArtifacts.js +9 -0
- package/dist/testArtifacts.js.map +1 -1
- package/dist/testMocks.js +2 -0
- package/dist/testMocks.js.map +1 -1
- package/dist/utils.js +10 -0
- package/dist/utils.js.map +1 -1
- package/package.json +5 -3
- package/src/Nymph.ts +281 -177
- package/src/Nymph.types.ts +1 -1
- package/src/conf/d.ts +8 -0
- package/src/conf/defaults.ts +24 -0
- package/src/driver/NymphDriver.ts +198 -183
- package/tsconfig.json +1 -1
package/dist/Nymph.js
CHANGED
|
@@ -6,9 +6,32 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
const conf_1 = require("./conf");
|
|
7
7
|
const Entity_1 = __importDefault(require("./Entity"));
|
|
8
8
|
const errors_1 = require("./errors");
|
|
9
|
+
/**
|
|
10
|
+
* An object relational mapper for Node.js.
|
|
11
|
+
*
|
|
12
|
+
* Written by Hunter Perrin for SciActive.
|
|
13
|
+
*
|
|
14
|
+
* @author Hunter Perrin <hperrin@gmail.com>
|
|
15
|
+
* @copyright SciActive Inc
|
|
16
|
+
* @see http://nymph.io/
|
|
17
|
+
*/
|
|
9
18
|
class Nymph {
|
|
19
|
+
/**
|
|
20
|
+
* Initialize Nymph.
|
|
21
|
+
*
|
|
22
|
+
* @param config The Nymph configuration.
|
|
23
|
+
* @param driver The Nymph database driver.
|
|
24
|
+
* @param tilmeld The Tilmeld user/group manager instance, if you want to use it.
|
|
25
|
+
* @param parent Used internally by Nymph. Don't set this.
|
|
26
|
+
*/
|
|
10
27
|
constructor(config, driver, tilmeld, parent) {
|
|
28
|
+
/**
|
|
29
|
+
* An optional Tilmeld user/group manager instance.
|
|
30
|
+
*/
|
|
11
31
|
this.tilmeld = undefined;
|
|
32
|
+
/**
|
|
33
|
+
* A simple map of names to Entity classes.
|
|
34
|
+
*/
|
|
12
35
|
this.entityClasses = {};
|
|
13
36
|
this.connectCallbacks = [];
|
|
14
37
|
this.disconnectCallbacks = [];
|
|
@@ -90,6 +113,7 @@ class Nymph {
|
|
|
90
113
|
];
|
|
91
114
|
for (let event of events) {
|
|
92
115
|
const prop = event + 'Callbacks';
|
|
116
|
+
// @ts-ignore: The callback should be the right type here.
|
|
93
117
|
const callbacks = parent[prop];
|
|
94
118
|
for (let callback of callbacks) {
|
|
95
119
|
if (callback.skipOnClone) {
|
|
@@ -99,14 +123,27 @@ class Nymph {
|
|
|
99
123
|
}
|
|
100
124
|
}
|
|
101
125
|
}
|
|
126
|
+
this.config.debugLog('nymph', 'Nymph loaded.');
|
|
102
127
|
if (tilmeld != null) {
|
|
103
128
|
this.tilmeld = tilmeld;
|
|
104
129
|
}
|
|
105
130
|
this.driver.init(this);
|
|
106
131
|
if (this.tilmeld) {
|
|
132
|
+
this.config.debugLog('nymph', 'Tilmeld initializing.');
|
|
107
133
|
this.tilmeld.init(this);
|
|
134
|
+
this.config.debugLog('nymph', 'Tilmeld initialized.');
|
|
108
135
|
}
|
|
109
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* Add your class to this instance.
|
|
139
|
+
*
|
|
140
|
+
* This will create a class that extends your class within this instance of
|
|
141
|
+
* Nymph and return it. You can then use this class's constructor and methods,
|
|
142
|
+
* which will use this instance of Nymph.
|
|
143
|
+
*
|
|
144
|
+
* Because this creates a subclass, don't use the class
|
|
145
|
+
* returned from `getEntityClass` to check with `instanceof`.
|
|
146
|
+
*/
|
|
110
147
|
addEntityClass(entityClass) {
|
|
111
148
|
const nymph = this;
|
|
112
149
|
class NymphEntity extends entityClass {
|
|
@@ -116,6 +153,7 @@ class Nymph {
|
|
|
116
153
|
}
|
|
117
154
|
NymphEntity.nymph = nymph;
|
|
118
155
|
this.entityClasses[entityClass.class] = NymphEntity;
|
|
156
|
+
this.config.debugLog('nymph', `Added entity class "${entityClass.class}".`);
|
|
119
157
|
return NymphEntity;
|
|
120
158
|
}
|
|
121
159
|
getEntityClass(className) {
|
|
@@ -129,29 +167,62 @@ class Nymph {
|
|
|
129
167
|
if (key in this.entityClasses) {
|
|
130
168
|
return this.entityClasses[key];
|
|
131
169
|
}
|
|
132
|
-
|
|
170
|
+
this.config.debugError('nymph', `Tried to use missing class "${key}".`);
|
|
171
|
+
throw new errors_1.ClassNotAvailableError(`Tried to use missing class "${key}".`);
|
|
133
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Get a clone of this instance with cloned classes and event listeners.
|
|
175
|
+
*
|
|
176
|
+
* @returns A clone of this instance.
|
|
177
|
+
*/
|
|
134
178
|
clone() {
|
|
135
179
|
return new Nymph(this.config, this.driver.clone(), this.tilmeld?.clone(), this);
|
|
136
180
|
}
|
|
181
|
+
/**
|
|
182
|
+
* Connect to the database.
|
|
183
|
+
*
|
|
184
|
+
* @returns Whether the instance is connected to the database.
|
|
185
|
+
*/
|
|
137
186
|
async connect() {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
187
|
+
try {
|
|
188
|
+
const result = this.driver.connect();
|
|
189
|
+
for (let callback of this.connectCallbacks) {
|
|
190
|
+
if (callback) {
|
|
191
|
+
await callback(this, result);
|
|
192
|
+
}
|
|
142
193
|
}
|
|
194
|
+
this.config.debugLog('nymph', 'Driver connected.');
|
|
195
|
+
return await result;
|
|
196
|
+
}
|
|
197
|
+
catch (e) {
|
|
198
|
+
this.config.debugError('nymph', `Failed to connect: ${e}`);
|
|
199
|
+
throw e;
|
|
143
200
|
}
|
|
144
|
-
return await result;
|
|
145
201
|
}
|
|
202
|
+
/**
|
|
203
|
+
* Disconnect from the database.
|
|
204
|
+
*
|
|
205
|
+
* @returns Whether the instance is connected to the database.
|
|
206
|
+
*/
|
|
146
207
|
async disconnect() {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
208
|
+
try {
|
|
209
|
+
const result = this.driver.disconnect();
|
|
210
|
+
for (let callback of this.disconnectCallbacks) {
|
|
211
|
+
if (callback) {
|
|
212
|
+
await callback(this, result);
|
|
213
|
+
}
|
|
151
214
|
}
|
|
215
|
+
this.config.debugLog('nymph', 'Driver disconnected.');
|
|
216
|
+
return await result;
|
|
217
|
+
}
|
|
218
|
+
catch (e) {
|
|
219
|
+
this.config.debugError('nymph', `Failed to disconnect: ${e}`);
|
|
220
|
+
throw e;
|
|
152
221
|
}
|
|
153
|
-
return await result;
|
|
154
222
|
}
|
|
223
|
+
/**
|
|
224
|
+
* Run all the query callbacks on a query.
|
|
225
|
+
*/
|
|
155
226
|
runQueryCallbacks(options, selectors) {
|
|
156
227
|
for (let callback of this.queryCallbacks) {
|
|
157
228
|
if (callback) {
|
|
@@ -159,270 +230,519 @@ class Nymph {
|
|
|
159
230
|
}
|
|
160
231
|
}
|
|
161
232
|
}
|
|
233
|
+
/**
|
|
234
|
+
* Start an atomic transaction and returns a new instance of Nymph.
|
|
235
|
+
*
|
|
236
|
+
* All proceeding changes using this new instance will wait to be written to
|
|
237
|
+
* the database's permanent storage until commit() is called. You can also
|
|
238
|
+
* undo all the changes since this function ran with rollback().
|
|
239
|
+
*
|
|
240
|
+
* Transactions will nest as long as every name is unique. Internally, Nymph
|
|
241
|
+
* uses names prefixed with "nymph-".
|
|
242
|
+
*
|
|
243
|
+
* @returns A new instance of Nymph that should be used for the transaction.
|
|
244
|
+
*/
|
|
162
245
|
async startTransaction(name) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
246
|
+
try {
|
|
247
|
+
for (let callback of this.beforeStartTransactionCallbacks) {
|
|
248
|
+
if (callback) {
|
|
249
|
+
await callback(this, name);
|
|
250
|
+
}
|
|
166
251
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
252
|
+
this.config.debugLog('nymph', `Starting transaction "${name}".`);
|
|
253
|
+
const result = await this.driver.startTransaction(name);
|
|
254
|
+
for (let callback of this.afterStartTransactionCallbacks) {
|
|
255
|
+
if (callback) {
|
|
256
|
+
await callback(this, name, result);
|
|
257
|
+
}
|
|
172
258
|
}
|
|
259
|
+
return result;
|
|
260
|
+
}
|
|
261
|
+
catch (e) {
|
|
262
|
+
this.config.debugError('nymph', `Failed to start transaction: ${e}`);
|
|
263
|
+
throw e;
|
|
173
264
|
}
|
|
174
|
-
return result;
|
|
175
265
|
}
|
|
266
|
+
/**
|
|
267
|
+
* Commit the named transaction.
|
|
268
|
+
*
|
|
269
|
+
* After this is called, the transaction instance should be discarded.
|
|
270
|
+
*
|
|
271
|
+
* @returns True on success, false on failure.
|
|
272
|
+
*/
|
|
176
273
|
async commit(name) {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
274
|
+
try {
|
|
275
|
+
for (let callback of this.beforeCommitTransactionCallbacks) {
|
|
276
|
+
if (callback) {
|
|
277
|
+
await callback(this, name);
|
|
278
|
+
}
|
|
180
279
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
280
|
+
this.config.debugLog('nymph', `Committing transaction "${name}".`);
|
|
281
|
+
const result = await this.driver.commit(name);
|
|
282
|
+
for (let callback of this.afterCommitTransactionCallbacks) {
|
|
283
|
+
if (callback) {
|
|
284
|
+
await callback(this, name, result);
|
|
285
|
+
}
|
|
186
286
|
}
|
|
287
|
+
return result;
|
|
288
|
+
}
|
|
289
|
+
catch (e) {
|
|
290
|
+
this.config.debugError('nymph', `Failed to commit transaction: ${e}`);
|
|
291
|
+
throw e;
|
|
187
292
|
}
|
|
188
|
-
return result;
|
|
189
293
|
}
|
|
294
|
+
/**
|
|
295
|
+
* Rollback the named transaction.
|
|
296
|
+
*
|
|
297
|
+
* After this is called, the transaction instance should be discarded.
|
|
298
|
+
*
|
|
299
|
+
* @returns True on success, false on failure.
|
|
300
|
+
*/
|
|
190
301
|
async rollback(name) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
302
|
+
try {
|
|
303
|
+
for (let callback of this.beforeRollbackTransactionCallbacks) {
|
|
304
|
+
if (callback) {
|
|
305
|
+
await callback(this, name);
|
|
306
|
+
}
|
|
194
307
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
308
|
+
this.config.debugLog('nymph', `Rolling back transaction "${name}".`);
|
|
309
|
+
const result = await this.driver.rollback(name);
|
|
310
|
+
for (let callback of this.afterRollbackTransactionCallbacks) {
|
|
311
|
+
if (callback) {
|
|
312
|
+
await callback(this, name, result);
|
|
313
|
+
}
|
|
200
314
|
}
|
|
315
|
+
return result;
|
|
316
|
+
}
|
|
317
|
+
catch (e) {
|
|
318
|
+
this.config.debugError('nymph', `Failed to roll back transaction: ${e}`);
|
|
319
|
+
throw e;
|
|
201
320
|
}
|
|
202
|
-
return result;
|
|
203
321
|
}
|
|
322
|
+
/**
|
|
323
|
+
* Check if there is any open transaction.
|
|
324
|
+
*
|
|
325
|
+
* @returns True if there is a transaction.
|
|
326
|
+
*/
|
|
204
327
|
async inTransaction() {
|
|
205
328
|
return await this.driver.inTransaction();
|
|
206
329
|
}
|
|
330
|
+
/**
|
|
331
|
+
* Increment or create a unique ID and return the new value.
|
|
332
|
+
*
|
|
333
|
+
* Unique IDs, or UIDs are similar to GUIDs, but numeric and sequential.
|
|
334
|
+
*
|
|
335
|
+
* A UID can be used to identify an object when the GUID doesn't suffice. On
|
|
336
|
+
* a system where a new entity is created many times per second, referring
|
|
337
|
+
* to something by its GUID may be unintuitive. However, the component
|
|
338
|
+
* designer is responsible for assigning UIDs to the component's entities.
|
|
339
|
+
* Beware that if a UID is incremented for an entity, and the entity cannot
|
|
340
|
+
* be saved, there is no safe, and therefore, no recommended way to
|
|
341
|
+
* decrement the UID back to its previous value.
|
|
342
|
+
*
|
|
343
|
+
* If newUID() is passed the name of a UID which does not exist yet, one
|
|
344
|
+
* will be created with that name, and assigned the value 1. If the UID
|
|
345
|
+
* already exists, its value will be incremented. The new value will be
|
|
346
|
+
* returned.
|
|
347
|
+
*
|
|
348
|
+
* @param name The UID's name.
|
|
349
|
+
* @returns The UID's new value, or null on failure.
|
|
350
|
+
*/
|
|
207
351
|
async newUID(name) {
|
|
208
|
-
for (let callback of this.beforeNewUIDCallbacks) {
|
|
209
|
-
if (callback) {
|
|
210
|
-
await callback(this, name);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
let result;
|
|
214
352
|
try {
|
|
215
|
-
|
|
216
|
-
}
|
|
217
|
-
catch (e) {
|
|
218
|
-
for (let callback of this.failedNewUIDCallbacks) {
|
|
353
|
+
for (let callback of this.beforeNewUIDCallbacks) {
|
|
219
354
|
if (callback) {
|
|
220
|
-
await callback(this,
|
|
355
|
+
await callback(this, name);
|
|
221
356
|
}
|
|
222
357
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
358
|
+
let result;
|
|
359
|
+
try {
|
|
360
|
+
result = this.driver.newUID(name);
|
|
361
|
+
}
|
|
362
|
+
catch (e) {
|
|
363
|
+
for (let callback of this.failedNewUIDCallbacks) {
|
|
364
|
+
if (callback) {
|
|
365
|
+
await callback(this, e);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
throw e;
|
|
228
369
|
}
|
|
370
|
+
for (let callback of this.afterNewUIDCallbacks) {
|
|
371
|
+
if (callback) {
|
|
372
|
+
await callback(this, result);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return await result;
|
|
376
|
+
}
|
|
377
|
+
catch (e) {
|
|
378
|
+
this.config.debugError('nymph', `Failed to create UID "${name}": ${e}`);
|
|
379
|
+
throw e;
|
|
229
380
|
}
|
|
230
|
-
return await result;
|
|
231
381
|
}
|
|
382
|
+
/**
|
|
383
|
+
* Get the current value of a unique ID.
|
|
384
|
+
* @param name The UID's name.
|
|
385
|
+
* @returns The UID's value, or null on failure and if it doesn't exist.
|
|
386
|
+
*/
|
|
232
387
|
async getUID(name) {
|
|
233
|
-
return await this.driver.getUID(name);
|
|
234
|
-
}
|
|
235
|
-
async setUID(name, value) {
|
|
236
|
-
for (let callback of this.beforeSetUIDCallbacks) {
|
|
237
|
-
if (callback) {
|
|
238
|
-
await callback(this, name, value);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
let result;
|
|
242
388
|
try {
|
|
243
|
-
|
|
389
|
+
return await this.driver.getUID(name);
|
|
244
390
|
}
|
|
245
391
|
catch (e) {
|
|
246
|
-
|
|
392
|
+
this.config.debugError('nymph', `Failed to get UID "${name}": ${e}`);
|
|
393
|
+
throw e;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Set the value of a UID.
|
|
398
|
+
*
|
|
399
|
+
* @param name The UID's name.
|
|
400
|
+
* @param value The value.
|
|
401
|
+
* @returns True on success, false on failure.
|
|
402
|
+
*/
|
|
403
|
+
async setUID(name, value) {
|
|
404
|
+
try {
|
|
405
|
+
for (let callback of this.beforeSetUIDCallbacks) {
|
|
247
406
|
if (callback) {
|
|
248
|
-
await callback(this,
|
|
407
|
+
await callback(this, name, value);
|
|
249
408
|
}
|
|
250
409
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
if (callback) {
|
|
255
|
-
await callback(this, result);
|
|
410
|
+
let result;
|
|
411
|
+
try {
|
|
412
|
+
result = this.driver.setUID(name, value);
|
|
256
413
|
}
|
|
414
|
+
catch (e) {
|
|
415
|
+
for (let callback of this.failedSetUIDCallbacks) {
|
|
416
|
+
if (callback) {
|
|
417
|
+
await callback(this, e);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
throw e;
|
|
421
|
+
}
|
|
422
|
+
for (let callback of this.afterSetUIDCallbacks) {
|
|
423
|
+
if (callback) {
|
|
424
|
+
await callback(this, result);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return await result;
|
|
428
|
+
}
|
|
429
|
+
catch (e) {
|
|
430
|
+
this.config.debugError('nymph', `Failed to set UID "${name}": ${e}`);
|
|
431
|
+
throw e;
|
|
257
432
|
}
|
|
258
|
-
return await result;
|
|
259
433
|
}
|
|
434
|
+
/**
|
|
435
|
+
* Delete a unique ID.
|
|
436
|
+
*
|
|
437
|
+
* @param name The UID's name.
|
|
438
|
+
* @returns True on success, false on failure.
|
|
439
|
+
*/
|
|
260
440
|
async deleteUID(name) {
|
|
261
|
-
for (let callback of this.beforeDeleteUIDCallbacks) {
|
|
262
|
-
if (callback) {
|
|
263
|
-
await callback(this, name);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
let result;
|
|
267
441
|
try {
|
|
268
|
-
|
|
269
|
-
}
|
|
270
|
-
catch (e) {
|
|
271
|
-
for (let callback of this.failedDeleteUIDCallbacks) {
|
|
442
|
+
for (let callback of this.beforeDeleteUIDCallbacks) {
|
|
272
443
|
if (callback) {
|
|
273
|
-
await callback(this,
|
|
444
|
+
await callback(this, name);
|
|
274
445
|
}
|
|
275
446
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
447
|
+
let result;
|
|
448
|
+
try {
|
|
449
|
+
result = this.driver.deleteUID(name);
|
|
450
|
+
}
|
|
451
|
+
catch (e) {
|
|
452
|
+
for (let callback of this.failedDeleteUIDCallbacks) {
|
|
453
|
+
if (callback) {
|
|
454
|
+
await callback(this, e);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
throw e;
|
|
458
|
+
}
|
|
459
|
+
for (let callback of this.afterDeleteUIDCallbacks) {
|
|
460
|
+
if (callback) {
|
|
461
|
+
await callback(this, result);
|
|
462
|
+
}
|
|
281
463
|
}
|
|
464
|
+
return await result;
|
|
465
|
+
}
|
|
466
|
+
catch (e) {
|
|
467
|
+
this.config.debugError('nymph', `Failed to delete UID "${name}": ${e}`);
|
|
468
|
+
throw e;
|
|
282
469
|
}
|
|
283
|
-
return await result;
|
|
284
470
|
}
|
|
471
|
+
/**
|
|
472
|
+
* Rename a unique ID.
|
|
473
|
+
*
|
|
474
|
+
* @param oldName The old name.
|
|
475
|
+
* @param newName The new name.
|
|
476
|
+
* @returns True on success, false on failure.
|
|
477
|
+
*/
|
|
285
478
|
async renameUID(oldName, newName) {
|
|
286
|
-
for (let callback of this.beforeRenameUIDCallbacks) {
|
|
287
|
-
if (callback) {
|
|
288
|
-
await callback(this, oldName, newName);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
let result;
|
|
292
479
|
try {
|
|
293
|
-
|
|
294
|
-
}
|
|
295
|
-
catch (e) {
|
|
296
|
-
for (let callback of this.failedRenameUIDCallbacks) {
|
|
480
|
+
for (let callback of this.beforeRenameUIDCallbacks) {
|
|
297
481
|
if (callback) {
|
|
298
|
-
await callback(this,
|
|
482
|
+
await callback(this, oldName, newName);
|
|
299
483
|
}
|
|
300
484
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
485
|
+
let result;
|
|
486
|
+
try {
|
|
487
|
+
result = this.driver.renameUID(oldName, newName);
|
|
488
|
+
}
|
|
489
|
+
catch (e) {
|
|
490
|
+
for (let callback of this.failedRenameUIDCallbacks) {
|
|
491
|
+
if (callback) {
|
|
492
|
+
await callback(this, e);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
throw e;
|
|
496
|
+
}
|
|
497
|
+
for (let callback of this.afterRenameUIDCallbacks) {
|
|
498
|
+
if (callback) {
|
|
499
|
+
await callback(this, result);
|
|
500
|
+
}
|
|
306
501
|
}
|
|
502
|
+
return await result;
|
|
503
|
+
}
|
|
504
|
+
catch (e) {
|
|
505
|
+
this.config.debugError('nymph', `Failed to rename UID "${oldName}" to "${newName}": ${e}`);
|
|
506
|
+
throw e;
|
|
307
507
|
}
|
|
308
|
-
return await result;
|
|
309
508
|
}
|
|
509
|
+
/**
|
|
510
|
+
* Save an entity to the database.
|
|
511
|
+
*
|
|
512
|
+
* If the entity has never been saved (has no GUID), a variable "cdate"
|
|
513
|
+
* is set on it with the current Unix timestamp.
|
|
514
|
+
*
|
|
515
|
+
* The variable "mdate" is set to the current Unix timestamp.
|
|
516
|
+
*
|
|
517
|
+
* @param entity The entity.
|
|
518
|
+
* @returns True on success, false on failure.
|
|
519
|
+
*/
|
|
310
520
|
async saveEntity(entity) {
|
|
311
|
-
for (let callback of this.beforeSaveEntityCallbacks) {
|
|
312
|
-
if (callback) {
|
|
313
|
-
await callback(this, entity);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
let result;
|
|
317
521
|
try {
|
|
318
|
-
|
|
319
|
-
}
|
|
320
|
-
catch (e) {
|
|
321
|
-
for (let callback of this.failedSaveEntityCallbacks) {
|
|
522
|
+
for (let callback of this.beforeSaveEntityCallbacks) {
|
|
322
523
|
if (callback) {
|
|
323
|
-
await callback(this,
|
|
524
|
+
await callback(this, entity);
|
|
324
525
|
}
|
|
325
526
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
if (callback) {
|
|
330
|
-
await callback(this, result);
|
|
527
|
+
let result;
|
|
528
|
+
try {
|
|
529
|
+
result = this.driver.saveEntity(entity);
|
|
331
530
|
}
|
|
531
|
+
catch (e) {
|
|
532
|
+
for (let callback of this.failedSaveEntityCallbacks) {
|
|
533
|
+
if (callback) {
|
|
534
|
+
await callback(this, e);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
throw e;
|
|
538
|
+
}
|
|
539
|
+
for (let callback of this.afterSaveEntityCallbacks) {
|
|
540
|
+
if (callback) {
|
|
541
|
+
await callback(this, result);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
return await result;
|
|
545
|
+
}
|
|
546
|
+
catch (e) {
|
|
547
|
+
this.config.debugError('nymph', `Failed to save entity: ${e}`);
|
|
548
|
+
throw e;
|
|
332
549
|
}
|
|
333
|
-
return await result;
|
|
334
550
|
}
|
|
335
551
|
async getEntities(options = {}, ...selectors) {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
552
|
+
try {
|
|
553
|
+
for (let callback of this.beforeGetEntitiesCallbacks) {
|
|
554
|
+
if (callback) {
|
|
555
|
+
await callback(this, options, selectors);
|
|
556
|
+
}
|
|
339
557
|
}
|
|
558
|
+
return await this.driver.getEntities(options, ...selectors);
|
|
559
|
+
}
|
|
560
|
+
catch (e) {
|
|
561
|
+
this.config.debugError('nymph', `Failed to get entities: ${e}`);
|
|
562
|
+
throw e;
|
|
340
563
|
}
|
|
341
|
-
return await this.driver.getEntities(options, ...selectors);
|
|
342
564
|
}
|
|
343
565
|
async getEntity(options = {}, ...selectors) {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
for (let callback of this.beforeGetEntityCallbacks) {
|
|
349
|
-
if (callback) {
|
|
350
|
-
await callback(this, options, selectors);
|
|
566
|
+
try {
|
|
567
|
+
// Set up options and selectors.
|
|
568
|
+
if (typeof selectors[0] === 'string') {
|
|
569
|
+
selectors = [{ type: '&', guid: selectors[0] }];
|
|
351
570
|
}
|
|
571
|
+
options.limit = 1;
|
|
572
|
+
for (let callback of this.beforeGetEntityCallbacks) {
|
|
573
|
+
if (callback) {
|
|
574
|
+
await callback(this, options, selectors);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
const entities = await this.driver.getEntities(options, ...selectors);
|
|
578
|
+
if (options.return === 'count') {
|
|
579
|
+
return entities;
|
|
580
|
+
}
|
|
581
|
+
if (!entities || !entities.length) {
|
|
582
|
+
return null;
|
|
583
|
+
}
|
|
584
|
+
return entities[0];
|
|
352
585
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
}
|
|
357
|
-
if (!entities || !entities.length) {
|
|
358
|
-
return null;
|
|
586
|
+
catch (e) {
|
|
587
|
+
this.config.debugError('nymph', `Failed to get entity: ${e}`);
|
|
588
|
+
throw e;
|
|
359
589
|
}
|
|
360
|
-
return entities[0];
|
|
361
590
|
}
|
|
591
|
+
/**
|
|
592
|
+
* Delete an entity from the database.
|
|
593
|
+
*
|
|
594
|
+
* @param entity The entity.
|
|
595
|
+
* @returns True on success, false on failure.
|
|
596
|
+
*/
|
|
362
597
|
async deleteEntity(entity) {
|
|
363
|
-
for (let callback of this.beforeDeleteEntityCallbacks) {
|
|
364
|
-
if (callback) {
|
|
365
|
-
await callback(this, entity);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
let result;
|
|
369
598
|
try {
|
|
370
|
-
|
|
371
|
-
}
|
|
372
|
-
catch (e) {
|
|
373
|
-
for (let callback of this.failedDeleteEntityCallbacks) {
|
|
599
|
+
for (let callback of this.beforeDeleteEntityCallbacks) {
|
|
374
600
|
if (callback) {
|
|
375
|
-
await callback(this,
|
|
601
|
+
await callback(this, entity);
|
|
376
602
|
}
|
|
377
603
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
604
|
+
let result;
|
|
605
|
+
try {
|
|
606
|
+
result = this.driver.deleteEntity(entity);
|
|
607
|
+
}
|
|
608
|
+
catch (e) {
|
|
609
|
+
for (let callback of this.failedDeleteEntityCallbacks) {
|
|
610
|
+
if (callback) {
|
|
611
|
+
await callback(this, e);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
throw e;
|
|
383
615
|
}
|
|
616
|
+
for (let callback of this.afterDeleteEntityCallbacks) {
|
|
617
|
+
if (callback) {
|
|
618
|
+
await callback(this, result);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
return await result;
|
|
622
|
+
}
|
|
623
|
+
catch (e) {
|
|
624
|
+
this.config.debugError('nymph', `Failed to delete entity: ${e}`);
|
|
625
|
+
throw e;
|
|
384
626
|
}
|
|
385
|
-
return await result;
|
|
386
627
|
}
|
|
628
|
+
/**
|
|
629
|
+
* Delete an entity from the database by its GUID.
|
|
630
|
+
*
|
|
631
|
+
* @param guid The entity's GUID.
|
|
632
|
+
* @param className The entity's class name.
|
|
633
|
+
* @returns True on success, false on failure.
|
|
634
|
+
*/
|
|
387
635
|
async deleteEntityByID(guid, className) {
|
|
388
|
-
for (let callback of this.beforeDeleteEntityByIDCallbacks) {
|
|
389
|
-
if (callback) {
|
|
390
|
-
await callback(this, guid, className);
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
let result;
|
|
394
636
|
try {
|
|
395
|
-
|
|
396
|
-
}
|
|
397
|
-
catch (e) {
|
|
398
|
-
for (let callback of this.failedDeleteEntityByIDCallbacks) {
|
|
637
|
+
for (let callback of this.beforeDeleteEntityByIDCallbacks) {
|
|
399
638
|
if (callback) {
|
|
400
|
-
await callback(this,
|
|
639
|
+
await callback(this, guid, className);
|
|
401
640
|
}
|
|
402
641
|
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
642
|
+
let result;
|
|
643
|
+
try {
|
|
644
|
+
result = this.driver.deleteEntityByID(guid, className);
|
|
645
|
+
}
|
|
646
|
+
catch (e) {
|
|
647
|
+
for (let callback of this.failedDeleteEntityByIDCallbacks) {
|
|
648
|
+
if (callback) {
|
|
649
|
+
await callback(this, e);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
throw e;
|
|
653
|
+
}
|
|
654
|
+
for (let callback of this.afterDeleteEntityByIDCallbacks) {
|
|
655
|
+
if (callback) {
|
|
656
|
+
await callback(this, result);
|
|
657
|
+
}
|
|
408
658
|
}
|
|
659
|
+
return await result;
|
|
660
|
+
}
|
|
661
|
+
catch (e) {
|
|
662
|
+
this.config.debugError('nymph', `Failed to delete entity by ID: ${e}`);
|
|
663
|
+
throw e;
|
|
409
664
|
}
|
|
410
|
-
return await result;
|
|
411
665
|
}
|
|
666
|
+
/**
|
|
667
|
+
* Export entities to a local file.
|
|
668
|
+
*
|
|
669
|
+
* This is the file format:
|
|
670
|
+
*
|
|
671
|
+
* ```
|
|
672
|
+
* #nex2
|
|
673
|
+
* # The above line must be the first thing in the file.
|
|
674
|
+
* # Comments begin with #
|
|
675
|
+
* # And can have white space before them.
|
|
676
|
+
* # This defines a UID.
|
|
677
|
+
* <name/of/uid>[5]
|
|
678
|
+
* <another uid>[8000]
|
|
679
|
+
* # For UIDs, the name is in angle brackets (<>) and the value follows
|
|
680
|
+
* # in square brackets ([]).
|
|
681
|
+
* # This starts a new entity.
|
|
682
|
+
* {1234abcd}<etype>[tag,list,with,commas]
|
|
683
|
+
* # For entities, the GUID is in curly brackets ({}), then the etype in
|
|
684
|
+
* # angle brackets, then the comma separated tag list follows in square
|
|
685
|
+
* # brackets ([]).
|
|
686
|
+
* # Properties are stored like this:
|
|
687
|
+
* # propname=JSON.stringify(value)
|
|
688
|
+
* abilities=["system/admin"]
|
|
689
|
+
* groups=[]
|
|
690
|
+
* inheritAbilities=false
|
|
691
|
+
* name="admin"
|
|
692
|
+
* # White space before/after "=" and at beginning/end of line is ignored.
|
|
693
|
+
* username = "admin"
|
|
694
|
+
* {2}<etype>[tag,list]
|
|
695
|
+
* another="This is another entity."
|
|
696
|
+
* newline="\n"
|
|
697
|
+
* ```
|
|
698
|
+
*
|
|
699
|
+
* @param filename The file to export to.
|
|
700
|
+
* @returns True on success, false on failure.
|
|
701
|
+
*/
|
|
412
702
|
async export(filename) {
|
|
413
|
-
|
|
703
|
+
try {
|
|
704
|
+
return await this.driver.export(filename);
|
|
705
|
+
}
|
|
706
|
+
catch (e) {
|
|
707
|
+
this.config.debugError('nymph', `Failed to export: ${e}`);
|
|
708
|
+
throw e;
|
|
709
|
+
}
|
|
414
710
|
}
|
|
711
|
+
/**
|
|
712
|
+
* Export entities to the console.
|
|
713
|
+
*
|
|
714
|
+
* @returns True on success, false on failure.
|
|
715
|
+
*/
|
|
415
716
|
async exportPrint() {
|
|
416
|
-
|
|
717
|
+
try {
|
|
718
|
+
return await this.driver.exportPrint();
|
|
719
|
+
}
|
|
720
|
+
catch (e) {
|
|
721
|
+
this.config.debugError('nymph', `Failed to export: ${e}`);
|
|
722
|
+
throw e;
|
|
723
|
+
}
|
|
417
724
|
}
|
|
725
|
+
/**
|
|
726
|
+
* Import entities from a file.
|
|
727
|
+
*
|
|
728
|
+
* @param filename The file to import from.
|
|
729
|
+
* @returns True on success, false on failure.
|
|
730
|
+
*/
|
|
418
731
|
async import(filename) {
|
|
419
|
-
|
|
732
|
+
try {
|
|
733
|
+
return await this.driver.import(filename);
|
|
734
|
+
}
|
|
735
|
+
catch (e) {
|
|
736
|
+
this.config.debugError('nymph', `Failed to import: ${e}`);
|
|
737
|
+
throw e;
|
|
738
|
+
}
|
|
420
739
|
}
|
|
421
740
|
on(event, callback) {
|
|
422
741
|
const prop = (event + 'Callbacks');
|
|
423
742
|
if (!(prop in this)) {
|
|
424
743
|
throw new Error('Invalid event type.');
|
|
425
744
|
}
|
|
745
|
+
// @ts-ignore: The callback should be the right type here.
|
|
426
746
|
this[prop].push(callback);
|
|
427
747
|
return () => this.off(event, callback);
|
|
428
748
|
}
|
|
@@ -431,8 +751,10 @@ class Nymph {
|
|
|
431
751
|
if (!(prop in this)) {
|
|
432
752
|
return false;
|
|
433
753
|
}
|
|
754
|
+
// @ts-ignore: The callback should always be the right type here.
|
|
434
755
|
const i = this[prop].indexOf(callback);
|
|
435
756
|
if (i > -1) {
|
|
757
|
+
// @ts-ignore: The callback should always be the right type here.
|
|
436
758
|
this[prop].splice(i, 1);
|
|
437
759
|
}
|
|
438
760
|
return true;
|